"Fossies" - the Fresh Open Source Software Archive 
Member "which-2.21/which.c" (19 Mar 2015, 16684 Bytes) of package /linux/privat/which-2.21.tar.gz:
As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style:
standard) with prefixed line numbers and
code folding option.
Alternatively you can here
view or
download the uninterpreted source code file.
For more information about "which.c" see the
Fossies "Dox" file reference documentation and the latest
Fossies "Diffs" side-by-side code changes report:
2.20_vs_2.21.
1 /*
2 * which v2.x -- print full path of executables
3 * Copyright (C) 1999, 2003, 2007, 2008 Carlo Wood <carlo@gnu.org>
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 #include "sys.h"
20 #include <stdio.h>
21 #include <ctype.h>
22 #include "getopt.h"
23 #include "tilde/tilde.h"
24 #include "bash.h"
25
26 static const char *progname;
27
28 static void print_usage(FILE *out)
29 {
30 fprintf(out, "Usage: %s [options] [--] COMMAND [...]\n", progname);
31 fprintf(out, "Write the full path of COMMAND(s) to standard output.\n\n");
32 fprintf(out, " --version, -[vV] Print version and exit successfully.\n");
33 fprintf(out, " --help, Print this help and exit successfully.\n");
34 fprintf(out, " --skip-dot Skip directories in PATH that start with a dot.\n");
35 fprintf(out, " --skip-tilde Skip directories in PATH that start with a tilde.\n");
36 fprintf(out, " --show-dot Don't expand a dot to current directory in output.\n");
37 fprintf(out, " --show-tilde Output a tilde for HOME directory for non-root.\n");
38 fprintf(out, " --tty-only Stop processing options on the right if not on tty.\n");
39 fprintf(out, " --all, -a Print all matches in PATH, not just the first\n");
40 fprintf(out, " --read-alias, -i Read list of aliases from stdin.\n");
41 fprintf(out, " --skip-alias Ignore option --read-alias; don't read stdin.\n");
42 fprintf(out, " --read-functions Read shell functions from stdin.\n");
43 fprintf(out, " --skip-functions Ignore option --read-functions; don't read stdin.\n\n");
44 fprintf(out, "Recommended use is to write the output of (alias; declare -f) to standard\n");
45 fprintf(out, "input, so that which can show aliases and shell functions. See which(1) for\n");
46 fprintf(out, "examples.\n\n");
47 fprintf(out, "If the options --read-alias and/or --read-functions are specified then the\n");
48 fprintf(out, "output can be a full alias or function definition, optionally followed by\n");
49 fprintf(out, "the full path of each command used inside of those.\n\n");
50 fprintf(out, "Report bugs to <which-bugs@gnu.org>.\n");
51 }
52
53 static void print_version(void)
54 {
55 fprintf(stdout, "GNU which v" VERSION ", Copyright (C) 1999 - 2015 Carlo Wood.\n");
56 fprintf(stdout, "GNU which comes with ABSOLUTELY NO WARRANTY;\n");
57 fprintf(stdout, "This program is free software; your freedom to use, change\n");
58 fprintf(stdout, "and distribute this program is protected by the GPL.\n");
59 }
60
61 static void print_fail(const char *name, const char *path_list)
62 {
63 fprintf(stderr, "%s: no %s in (%s)\n", progname, name, path_list);
64 }
65
66 static char home[256];
67 static size_t homelen = 0;
68
69 static int absolute_path_given;
70 static int found_path_starts_with_dot;
71 static char *abs_path;
72
73 static int skip_dot = 0, skip_tilde = 0, skip_alias = 0, read_alias = 0;
74 static int show_dot = 0, show_tilde = 0, show_all = 0, tty_only = 0;
75 static int skip_functions = 0, read_functions = 0;
76
77 static char *find_command_in_path(const char *name, const char *path_list, int *path_index)
78 {
79 char *found = NULL, *full_path;
80 int status, name_len;
81
82 name_len = strlen(name);
83
84 if (!absolute_program(name))
85 absolute_path_given = 0;
86 else
87 {
88 char *p;
89 absolute_path_given = 1;
90
91 if (abs_path)
92 free(abs_path);
93
94 if (*name != '.' && *name != '/' && *name != '~')
95 {
96 abs_path = (char *)xmalloc(3 + name_len);
97 strcpy(abs_path, "./");
98 strcat(abs_path, name);
99 }
100 else
101 {
102 abs_path = (char *)xmalloc(1 + name_len);
103 strcpy(abs_path, name);
104 }
105
106 path_list = abs_path;
107 p = strrchr(abs_path, '/');
108 *p++ = 0;
109 name = p;
110 }
111
112 while (path_list && path_list[*path_index])
113 {
114 char *path;
115
116 if (absolute_path_given)
117 {
118 path = savestring(path_list);
119 *path_index = strlen(path);
120 }
121 else
122 path = get_next_path_element(path_list, path_index);
123
124 if (!path)
125 break;
126
127 if (*path == '~')
128 {
129 char *t = tilde_expand(path);
130 free(path);
131 path = t;
132
133 if (skip_tilde)
134 {
135 free(path);
136 continue;
137 }
138 }
139
140 if (skip_dot && *path != '/')
141 {
142 free(path);
143 continue;
144 }
145
146 found_path_starts_with_dot = (*path == '.');
147
148 full_path = make_full_pathname(path, name, name_len);
149 free(path);
150
151 status = file_status(full_path);
152
153 if ((status & FS_EXISTS) && (status & FS_EXECABLE))
154 {
155 found = full_path;
156 break;
157 }
158
159 free(full_path);
160 }
161
162 return (found);
163 }
164
165 static char cwd[256];
166 static size_t cwdlen;
167
168 static void get_current_working_directory(void)
169 {
170 if (cwdlen)
171 return;
172
173 if (!getcwd(cwd, sizeof(cwd)))
174 {
175 const char *pwd = getenv("PWD");
176 if (pwd && strlen(pwd) < sizeof(cwd))
177 strcpy(cwd, pwd);
178 }
179
180 if (*cwd != '/')
181 {
182 fprintf(stderr, "Can't get current working directory\n");
183 exit(-1);
184 }
185
186 cwdlen = strlen(cwd);
187
188 if (cwd[cwdlen - 1] != '/')
189 {
190 cwd[cwdlen++] = '/';
191 cwd[cwdlen] = 0;
192 }
193 }
194
195 static char *path_clean_up(const char *path)
196 {
197 static char result[256];
198
199 const char *p1 = path;
200 char *p2 = result;
201
202 int saw_slash = 0, saw_slash_dot = 0, saw_slash_dot_dot = 0;
203
204 if (*p1 != '/')
205 {
206 get_current_working_directory();
207 strcpy(result, cwd);
208 saw_slash = 1;
209 p2 = &result[cwdlen];
210 }
211
212 do
213 {
214 /*
215 * Two leading slashes are allowed, having an OS implementation-defined meaning.
216 * See http://www.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap04.html#tag_04_11
217 */
218 if (!saw_slash || *p1 != '/' || (p1 == path + 1 && p1[1] != '/'))
219 *p2++ = *p1;
220 if (saw_slash_dot && (*p1 == '/'))
221 p2 -= 2;
222 if (saw_slash_dot_dot && (*p1 == '/'))
223 {
224 int cnt = 0;
225 do
226 {
227 if (--p2 < result)
228 {
229 strcpy(result, path);
230 return result;
231 }
232 if (*p2 == '/')
233 ++cnt;
234 }
235 while (cnt != 3);
236 ++p2;
237 }
238 saw_slash_dot_dot = saw_slash_dot && (*p1 == '.');
239 saw_slash_dot = saw_slash && (*p1 == '.');
240 saw_slash = (*p1 == '/');
241 }
242 while (*p1++);
243
244 return result;
245 }
246
247 struct function_st {
248 char *name;
249 size_t len;
250 char **lines;
251 int line_count;
252 };
253
254 static struct function_st *functions;
255 static int func_count;
256 static int max_func_count;
257
258 static char **aliases;
259 static int alias_count;
260 static int max_alias_count;
261
262 int func_search(int indent, const char *cmd, struct function_st *func_list, int function_start_type)
263 {
264 int i;
265 for (i = 0; i < func_count; ++i)
266 {
267 if (!strcmp(functions[i].name, cmd))
268 {
269 int j;
270 if (indent)
271 fputc('\t', stdout);
272 if (function_start_type == 1)
273 fprintf(stdout, "%s () {\n", cmd);
274 else
275 fprintf(stdout, "%s ()\n", cmd);
276 for (j = 0; j < functions[i].line_count; ++j)
277 {
278 if (indent)
279 fputc('\t', stdout);
280 fputs(functions[i].lines[j], stdout);
281 }
282 return 1;
283 }
284 }
285 return 0;
286 }
287
288 int path_search(int indent, const char *cmd, const char *path_list)
289 {
290 char *result = NULL;
291 int found_something = 0;
292
293 if (path_list && *path_list != '\0')
294 {
295 int next;
296 int path_index = 0;
297 do
298 {
299 next = show_all;
300 result = find_command_in_path(cmd, path_list, &path_index);
301 if (result)
302 {
303 const char *full_path = path_clean_up(result);
304 int in_home = (show_tilde || skip_tilde) && !strncmp(full_path, home, homelen);
305 if (indent)
306 fprintf(stdout, "\t");
307 if (!(skip_tilde && in_home) && show_dot && found_path_starts_with_dot && !strncmp(full_path, cwd, cwdlen))
308 {
309 full_path += cwdlen;
310 fprintf(stdout, "./");
311 }
312 else if (in_home)
313 {
314 if (skip_tilde)
315 {
316 next = 1;
317 free(result);
318 continue;
319 }
320 if (show_tilde)
321 {
322 full_path += homelen;
323 fprintf(stdout, "~/");
324 }
325 }
326 fprintf(stdout, "%s\n", full_path);
327 free(result);
328 found_something = 1;
329 }
330 else
331 break;
332 }
333 while (next);
334 }
335
336 return found_something;
337 }
338
339 void process_alias(const char *str, int argc, char *argv[], const char *path_list, int function_start_type)
340 {
341 const char *p = str;
342 int len = 0;
343
344 while(*p == ' ' || *p == '\t')
345 ++p;
346 if (!strncmp("alias", p, 5))
347 p += 5;
348 while(*p == ' ' || *p == '\t')
349 ++p;
350 while(*p && *p != ' ' && *p != '\t' && *p != '=')
351 ++p, ++len;
352
353 for (; argc > 0; --argc, ++argv)
354 {
355 char q = 0;
356 char *cmd;
357
358 if (!*argv || len != strlen(*argv) || strncmp(*argv, &p[-len], len))
359 continue;
360
361 fputs(str, stdout);
362
363 if (!show_all)
364 *argv = NULL;
365
366 while(*p == ' ' || *p == '\t')
367 ++p;
368 if (*p == '=')
369 ++p;
370 while(*p == ' ' || *p == '\t')
371 ++p;
372 if (*p == '"' || *p == '\'')
373 q = *p, ++p;
374
375 for(;;)
376 {
377 int found = 0;
378
379 while(*p == ' ' || *p == '\t')
380 ++p;
381 len = 0;
382 while(*p && *p != ' ' && *p != '\t' && *p != q && *p != '|' && *p != '&')
383 ++p, ++len;
384
385 cmd = (char *)xmalloc(len + 1);
386 strncpy(cmd, &p[-len], len);
387 cmd[len] = 0;
388 if (*argv && !strcmp(cmd, *argv))
389 *argv = NULL;
390 if (read_functions && !strchr(cmd, '/'))
391 found = func_search(1, cmd, functions, function_start_type);
392 if (show_all || !found)
393 path_search(1, cmd, path_list);
394 free(cmd);
395
396 while(*p && (*p != '|' || p[1] == '|') && (*p != '&' || p[1] == '&'))
397 ++p;
398
399 if (!*p)
400 break;
401
402 ++p;
403 }
404
405 break;
406 }
407 }
408
409 enum opts {
410 opt_version,
411 opt_skip_dot,
412 opt_skip_tilde,
413 opt_skip_alias,
414 opt_read_functions,
415 opt_skip_functions,
416 opt_show_dot,
417 opt_show_tilde,
418 opt_tty_only,
419 opt_help
420 };
421
422 #ifdef __TANDEM
423 /* According to Tom Bates, <tom.bates@hp.com> */
424 static uid_t const superuser = 65535;
425 #else
426 static uid_t const superuser = 0;
427 #endif
428
429 int main(int argc, char *argv[])
430 {
431 const char *path_list = getenv("PATH");
432 int short_option, fail_count = 0;
433 static int long_option;
434 struct option longopts[] = {
435 {"help", 0, &long_option, opt_help},
436 {"version", 0, &long_option, opt_version},
437 {"skip-dot", 0, &long_option, opt_skip_dot},
438 {"skip-tilde", 0, &long_option, opt_skip_tilde},
439 {"show-dot", 0, &long_option, opt_show_dot},
440 {"show-tilde", 0, &long_option, opt_show_tilde},
441 {"tty-only", 0, &long_option, opt_tty_only},
442 {"all", 0, NULL, 'a'},
443 {"read-alias", 0, NULL, 'i'},
444 {"skip-alias", 0, &long_option, opt_skip_alias},
445 {"read-functions", 0, &long_option, opt_read_functions},
446 {"skip-functions", 0, &long_option, opt_skip_functions},
447 {NULL, 0, NULL, 0}
448 };
449
450 progname = argv[0];
451 while ((short_option = getopt_long(argc, argv, "aivV", longopts, NULL)) != -1)
452 {
453 switch (short_option)
454 {
455 case 0:
456 switch (long_option)
457 {
458 case opt_help:
459 print_usage(stdout);
460 return 0;
461 case opt_version:
462 print_version();
463 return 0;
464 case opt_skip_dot:
465 skip_dot = !tty_only;
466 break;
467 case opt_skip_tilde:
468 skip_tilde = !tty_only;
469 break;
470 case opt_skip_alias:
471 skip_alias = 1;
472 break;
473 case opt_show_dot:
474 show_dot = !tty_only;
475 break;
476 case opt_show_tilde:
477 show_tilde = (!tty_only && geteuid() != superuser);
478 break;
479 case opt_tty_only:
480 tty_only = !isatty(1);
481 break;
482 case opt_read_functions:
483 read_functions = 1;
484 break;
485 case opt_skip_functions:
486 skip_functions = 1;
487 break;
488 }
489 break;
490 case 'a':
491 show_all = 1;
492 break;
493 case 'i':
494 read_alias = 1;
495 break;
496 case 'v':
497 case 'V':
498 print_version();
499 return 0;
500 }
501 }
502
503 uidget();
504
505 if (show_dot)
506 get_current_working_directory();
507
508 if (show_tilde || skip_tilde)
509 {
510 const char *h;
511
512 if (!(h = getenv("HOME")))
513 h = sh_get_home_dir();
514
515 strncpy(home, h, sizeof(home));
516 home[sizeof(home) - 1] = 0;
517 homelen = strlen(home);
518 if (home[homelen - 1] != '/' && homelen < sizeof(home) - 1)
519 {
520 strcat(home, "/");
521 ++homelen;
522 }
523 }
524
525 if (skip_alias)
526 read_alias = 0;
527
528 if (skip_functions)
529 read_functions = 0;
530
531 argv += optind;
532 argc -= optind;
533
534 if (argc == 0)
535 {
536 print_usage(stderr);
537 return -1;
538 }
539
540 int function_start_type = 0;
541 if (read_alias || read_functions)
542 {
543 char buf[1024];
544 int processing_aliases = read_alias;
545
546 if (isatty(0))
547 {
548 fprintf(stderr, "%s: %s: Warning: stdin is a tty.\n", progname,
549 (read_functions ? read_alias ? "--read-functions, --read-alias, -i" : "--read-functions" : "--read-alias, -i"));
550 }
551
552 while (fgets(buf, sizeof(buf), stdin))
553 {
554 int looks_like_function_start = 0;
555 int function_start_has_declare;
556 if (read_functions)
557 {
558 // bash version 2.0.5a and older output a pattern for `str' like
559 // declare -fx FUNCTION_NAME ()
560 // {
561 // body
562 // }
563 //
564 // bash version 2.0.5b and later output a pattern for `str' like
565 // FUNCTION_NAME ()
566 // {
567 // body
568 // }
569 char *p = buf + strlen(buf) - 1;
570 while (isspace(*p) && p > buf + 2)
571 --p;
572 if (*p == ')' && p[-1] == '(' && p[-2] == ' ')
573 {
574 looks_like_function_start = 1;
575 function_start_has_declare = (strncmp("declare -", buf, 9) == 0);
576 }
577 // Add some zsh support here.
578 // zsh does output a pattern for `str' like
579 // FUNCTION () {
580 // body
581 // }
582 if (p > buf + 4 && *p == '{' && p[-1] == ' ' &&
583 p[-2] == ')' && p[-3] == '(' && p[-4] == ' ')
584 {
585 looks_like_function_start = 1;
586 function_start_type = 1;
587 function_start_has_declare = 0;
588 }
589 }
590 if (processing_aliases && !looks_like_function_start)
591 {
592 // bash version 2.0.5b can throw in lines like "declare -fx FUNCTION_NAME", eat them.
593 if (!strncmp("declare -", buf, 9))
594 continue;
595 if (alias_count == max_alias_count)
596 {
597 max_alias_count += 32;
598 aliases = (char **)xrealloc(aliases, max_alias_count * sizeof(char *));
599 }
600 aliases[alias_count++] = strcpy((char *)xmalloc(strlen(buf) + 1), buf);
601 }
602 else if (read_functions && looks_like_function_start)
603 {
604 struct function_st *function;
605 int max_line_count;
606
607 const char *p = buf;
608 int len = 0;
609
610 processing_aliases = 0;
611
612 // Eat "declare -fx " at start of bash version 2.0.5a and older, if present.
613 if (function_start_has_declare)
614 {
615 p += 9;
616 while(*p && *p++ != ' ');
617 }
618
619 while(*p && *p != ' ')
620 ++p, ++len;
621
622 if (func_count == max_func_count)
623 {
624 max_func_count += 16;
625 functions = (struct function_st *)xrealloc(functions, max_func_count * sizeof(struct function_st));
626 }
627 function = &functions[func_count++];
628 function->name = (char *)xmalloc(len + 1);
629 strncpy(function->name, &p[-len], len);
630 function->name[len] = 0;
631 function->len = len;
632 max_line_count = 32;
633 function->lines = (char **)xmalloc(max_line_count * sizeof(char *));
634 function->line_count = 0;
635 while (fgets(buf, sizeof(buf), stdin))
636 {
637 size_t blen = strlen(buf);
638 function->lines[function->line_count++] = strcpy((char *)xmalloc(blen + 1), buf);
639 if (!strcmp(buf, "}\n"))
640 break;
641 if (function->line_count == max_line_count)
642 {
643 max_line_count += 32;
644 function->lines = (char **)xrealloc(function->lines, max_line_count * sizeof(char *));
645 }
646 }
647 }
648 }
649 if (read_alias)
650 {
651 int i;
652 for (i = 0; i < alias_count; ++i)
653 process_alias(aliases[i], argc, argv, path_list, function_start_type);
654 }
655 }
656
657 for (; argc > 0; --argc, ++argv)
658 {
659 int found_something = 0;
660
661 if (!*argv)
662 continue;
663
664 if (read_functions && !strchr(*argv, '/'))
665 found_something = func_search(0, *argv, functions, function_start_type);
666
667 if ((show_all || !found_something) && !path_search(0, *argv, path_list) && !found_something)
668 {
669 print_fail(absolute_path_given ? strrchr(*argv, '/') + 1 : *argv, absolute_path_given ? abs_path : path_list);
670 ++fail_count;
671 }
672 }
673
674 return fail_count;
675 }
676
677 #ifdef NEED_XMALLOC
678 void *xmalloc(size_t size)
679 {
680 void *ptr = malloc(size);
681 if (ptr == NULL)
682 {
683 fprintf(stderr, "%s: Out of memory", progname);
684 exit(-1);
685 }
686 return ptr;
687 }
688
689 void *xrealloc(void *ptr, size_t size)
690 {
691 if (!ptr)
692 return xmalloc(size);
693 ptr = realloc(ptr, size);
694 if (size > 0 && ptr == NULL)
695 {
696 fprintf(stderr, "%s: Out of memory\n", progname);
697 exit(-1);
698 }
699 return ptr;
700 }
701 #endif /* NEED_XMALLOC */