"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 */