home *** CD-ROM | disk | FTP | other *** search
/ Monster Media 1993 #2 / Image.iso / os2 / whch20gr.zip / WHICH.C < prev    next >
C/C++ Source or Header  |  1993-04-28  |  16KB  |  475 lines

  1. /*---------------------------------------------------------------------------
  2.  
  3.     which.c
  4.  
  5.     This program by default finds the path of an executable command under
  6.     either OS/2 or MS-DOS.  It can also be used to find data files in the
  7.     DPATH or DLLs in the LIBPATH (at least under OS/2).  It does not have
  8.     a whole lot of error-checking, but the array sizes are reasonably gen-
  9.     erous.
  10.  
  11.     To do:  add support for ksh, bash, etc.; allow wildcards?
  12.  
  13.     Copyright (c) 1993 by Greg Roelofs.  You may use this code for any pur-
  14.     pose whatsoever, as long as you don't sell it and don't claim to have
  15.     written it.  Not that you'd want to.
  16.  
  17.   ---------------------------------------------------------------------------*/
  18.  
  19. #define VERSION   "v2.02 of 28 April 1993"
  20.  
  21. /* #define DEBUG */
  22.  
  23. #include <stdio.h>
  24. #include <stdlib.h>
  25. #include <string.h>
  26. #include <sys/types.h>
  27. #include <sys/stat.h>
  28.  
  29. #ifndef TRUE
  30. #  define TRUE   1
  31. #  define FALSE  0
  32. #endif
  33.  
  34. /* not used:
  35.     #if defined(__OS2__) && !defined(OS2)
  36.     #  define OS2
  37.     #endif
  38.  */
  39.  
  40. #if defined(__IBMC__)
  41. #  define S_IFMT   0xF000   /* or (S_IFREG | S_IFDIR | S_IFCHR) */
  42. #endif
  43.  
  44. #ifndef S_ISDIR
  45. #  define S_ISDIR(m)   (((m) & S_IFMT) == S_IFDIR)
  46. #endif
  47.  
  48. #ifdef DEBUG
  49. #  define Trace(x)   fprintf x
  50. #else
  51. #  define Trace(x)
  52. #endif
  53.  
  54.  
  55. struct stat statbuf;
  56.  
  57.  
  58. /* extensions must be in order of operating-system precedence! */
  59.  
  60. static char *ext_command[] = {     /* COMMAND extensions */
  61.     ".com",
  62.     ".exe",
  63.     ".bat"
  64. };
  65.  
  66. static char *ext_cmd[] = {         /* CMD extensions */
  67.     ".com",
  68.     ".exe",
  69.     ".cmd",
  70.     ".bat"
  71. };
  72.  
  73. static char *ext_4dos[] = {        /* 4DOS extensions */
  74.     ".com",
  75.     ".exe",
  76.     ".btm",
  77.     ".bat"
  78. };
  79.  
  80. static char *ext_4os2[] = {        /* 4OS2 extensions */
  81.     ".com",
  82.     ".exe",
  83.     ".btm",
  84.     ".cmd",
  85.     ".bat"
  86. };
  87.  
  88. static char *ext_libpath[] = {     /* LIBPATH extension(s) */
  89.     ".dll"
  90. };
  91.  
  92. static char *ext_dpath[] = {       /* DPATH extension(s) */
  93.     ".boo",
  94.     ".dat",
  95.     ".inf",
  96.     ".ini",   /* does this belong here? */
  97.     ".hlp",
  98.     ".msg",
  99.     ".ndx"
  100. };
  101.  
  102.  
  103. /* OS/2 internal commands:  some of these are rarely used outside of
  104.  * command files, but all of them can be called from the command line.
  105.  * (I don't know if some of the OS/2-only commands might be in later
  106.  * versions of MS-DOS.  One could use system() and check the errors,
  107.  * but that would be slow and probably difficult.)
  108.  */
  109.  
  110. static char *int_command[] = {
  111.     "call", "cd", "chdir", "cls", "copy", "date", "del", "dir", "echo",
  112.     "erase", "exit", "for", "goto", "if", "md", "mkdir", "path", "pause",
  113.     "prompt", "rd", "rem", "ren", "rename", "rmdir", "set", "shift",
  114.     "time", "type", "ver", "verify", "vol"
  115. };
  116.  
  117. static char *int_cmd[] = {
  118.  
  119.     /* note that extproc cannot be called from command line in 4OS2 */
  120.     "chcp", "detach", "dpath", "endlocal", "extproc", "keys", "move",
  121.     "setlocal", "start",
  122.  
  123.     /* all the rest are identical to int_command[] */
  124.     "call", "cd", "chdir", "cls", "copy", "date", "del", "dir", "echo",
  125.     "erase", "exit", "for", "goto", "if", "md", "mkdir", "path", "pause",
  126.     "prompt", "rd", "rem", "ren", "rename", "rmdir", "set", "shift",
  127.     "time", "type", "ver", "verify", "vol"
  128. };
  129.  
  130. static char *int_4dos[] = {
  131.  
  132.     /* these are actually 4OS2 commands; different from 4DOS? */
  133.     "?", "alias", "attrib", "beep", "cancel", "cdd", "color", "delay",
  134.     "describe", "dirs", "drawbox", "drawhline", "drawvline", "echos",
  135.     "eset", "except", "free", "global", "gosub", "help", "history", "iff",
  136.     "inkey", "input", "list", "loadbtm", "log", "memory", "popd", "pushd",
  137.     "quit", "reboot", "return", "screen", "scrput", "select", "setdos",
  138.     "tee", "text", "timer", "unalias", "unset", "vscrput", "window", "y",
  139.  
  140.     /* all the rest are identical to int_command[] */
  141.     "call", "cd", "chdir", "cls", "copy", "date", "del", "dir", "echo",
  142.     "erase", "exit", "for", "goto", "if", "md", "mkdir", "path", "pause",
  143.     "prompt", "rd", "rem", "ren", "rename", "rmdir", "set", "shift",
  144.     "time", "type", "ver", "verify", "vol"
  145. };
  146.  
  147. static char *int_4os2[] = {
  148.     "?", "alias", "attrib", "beep", "cancel", "cdd", "color", "delay",
  149.     "describe", "dirs", "drawbox", "drawhline", "drawvline", "echos",
  150.     "eset", "except", "free", "global", "gosub", "help", "history", "iff",
  151.     "inkey", "input", "list", "loadbtm", "log", "memory", "popd", "pushd",
  152.     "quit", "reboot", "return", "screen", "scrput", "select", "setdos",
  153.     "tee", "text", "timer", "unalias", "unset", "vscrput", "window", "y",
  154.  
  155.     /* these are identical to int_cmd[], aside from missing extproc */
  156.     "chcp", "detach", "dpath", "endlocal", "keys", "move", "setlocal",
  157.     "start",
  158.  
  159.     /* all the rest are identical to int_command[] */
  160.     "call", "cd", "chdir", "cls", "copy", "date", "del", "dir", "echo",
  161.     "erase", "exit", "for", "goto", "if", "md", "mkdir", "path", "pause",
  162.     "prompt", "rd", "rem", "ren", "rename", "rmdir", "set", "shift",
  163.     "time", "type", "ver", "verify", "vol"
  164. };
  165.  
  166.  
  167. typedef struct shellinfo {
  168.     char *name;
  169.     char **extension;
  170.     int num_ext;
  171.     char **internal;
  172.     int num_int;
  173. } SHELLINFO;
  174.  
  175. /* these guys must match the order of shells[] */
  176. #define _COMMAND   0
  177. #define _CMD       1
  178. #define _4DOS      2
  179. #define _4OS2      3
  180.  
  181. SHELLINFO shells[] = {
  182.     {"COMMAND.COM", ext_command, sizeof(ext_command)/sizeof(char *),
  183.                     int_command, sizeof(int_command)/sizeof(char *) },
  184.     {"CMD.EXE",     ext_cmd,     sizeof(ext_cmd)/sizeof(char *),
  185.                     int_cmd,     sizeof(int_cmd)/sizeof(char *) },
  186.     {"4DOS.COM",    ext_4dos,    sizeof(ext_4dos)/sizeof(char *),
  187.                     int_4dos,    sizeof(int_4dos)/sizeof(char *) },
  188.     {"4OS2.EXE",    ext_4os2,    sizeof(ext_4os2)/sizeof(char *),
  189.                     int_4os2,    sizeof(int_4os2)/sizeof(char *) },
  190.     {"4OS2-16.EXE", ext_4os2,    sizeof(ext_4os2)/sizeof(char *),
  191.                     int_4os2,    sizeof(int_4os2)/sizeof(char *) },
  192.     {"4OS2-32.EXE", ext_4os2,    sizeof(ext_4os2)/sizeof(char *),
  193.                     int_4os2,    sizeof(int_4os2)/sizeof(char *) }
  194. };
  195.  
  196. #define REGPATH    0   /* for pathtype */
  197. #define LIBPATH    1
  198. #define DPATH      2
  199.  
  200. char *prognam;
  201.  
  202.  
  203. /****************/
  204. /* Main program */
  205. /****************/
  206.  
  207. int main(int argc, char *argv[])
  208. {
  209.     char *p, *q, *comspec, *path, *pathvar;
  210.     char *dir[1000], tempname[300];
  211.     int c, n, error=0;
  212.     int i, j, k, sh, all=FALSE, pathtype=REGPATH, regpath;
  213.     int numdirs=0, numshells=sizeof(shells)/sizeof(SHELLINFO);
  214.  
  215.  
  216. /*---------------------------------------------------------------------------
  217.     Parse the command line...
  218.   ---------------------------------------------------------------------------*/
  219.  
  220.     prognam = argv[0];
  221.     p = prognam - 1;
  222.     while (*++p)
  223.         *p = tolower(*p);   /* assumes "smart" tolower() */
  224.  
  225.     if (argc <= 1)
  226.         --argc;
  227.     else
  228.         while (--argc > 0  &&  (*++argv)[0] == '-')
  229.             while (c = *++(argv[0]))   /* argv[0] not program name any more */
  230.                 switch (c) {
  231.                     case 'a':             /* list all matches */
  232.                         all = TRUE;
  233.                         break;
  234.                     case 'l':             /* search for DLLs */
  235.                         pathtype = LIBPATH;
  236.                         break;
  237.                     case 'd':             /* search for data files */
  238.                         pathtype = DPATH;
  239.                         break;
  240.                     default:
  241.                         ++error;
  242.                         break;
  243.                 } /* end switch, while, while, if */
  244.  
  245. /*---------------------------------------------------------------------------
  246.     Print usage if any errors or if no arguments.
  247.   ---------------------------------------------------------------------------*/
  248.  
  249.     if (error || (argc <= 0)) {
  250. #ifdef TRADITIONAL
  251.         fprintf(stderr, "%s: too few arguments\n", prognam);
  252. #else
  253.         fprintf(stderr, "\n\
  254. which for OS/2 and MS-DOS (%s), from Newtware\n\n\
  255. usage:  %s [ -a ] [ -l | -d ] cmd [ cmd ... ]\n\
  256. (default behavior is to find location of program executed as \"cmd\")\n\
  257.   -a  list all matches, not just first\n\
  258.   -l  search directories in LIBPATH for DLLs (OS/2)\n\
  259.   -d  search directories in DPATH for data files (OS/2)\n", VERSION, prognam);
  260. #endif
  261.         exit(1);
  262.     }
  263.  
  264. /*---------------------------------------------------------------------------
  265.     Try to figure out what shell we're in, based on COMSPEC.
  266.   ---------------------------------------------------------------------------*/
  267.  
  268.     if ((comspec = getenv("COMSPEC")) == (char *)NULL || *comspec == '\0') {
  269.         Trace((stderr, "COMSPEC is empty...assuming COMSPEC = cmd.exe\n"));
  270.         sh = _CMD;
  271.     } else {
  272.         Trace((stderr, "COMSPEC = %s\n", comspec));
  273.         p = strrchr(comspec, '\\');
  274.         if (p == NULL)
  275.             p = comspec;
  276.         else
  277.             ++p;
  278.         for (i = 0;  i < numshells;  ++i) {
  279.             if (stricmp(p, shells[i].name) == 0) {
  280.                 sh = i;
  281.                 break;
  282.             }
  283.         }
  284.         if (i == numshells) {
  285.             sh = _CMD;
  286.             fprintf(stderr,
  287.               "%s: unknown command shell \"%s\"\n%s: assuming %s\n",
  288.               prognam, comspec, prognam, shells[sh].name);
  289.         }
  290.     }
  291.     Trace((stderr, "shell is %s\n\n", shells[sh].name));
  292.  
  293. /*---------------------------------------------------------------------------
  294.     Get the PATH, DPATH or LIBPATH, depending on the user's wishes.
  295.   ---------------------------------------------------------------------------*/
  296.  
  297.     /* for regular path, current directory is always implied; not for others */
  298.     switch (pathtype) {
  299.         case REGPATH:
  300.             pathvar = "PATH";
  301.             regpath = TRUE;
  302.             dir[numdirs++] = ".";
  303.             path = getenv(pathvar);
  304.             break;
  305.         case DPATH:
  306.             pathvar = "DPATH";
  307.             regpath = FALSE;
  308.             shells[sh].extension = ext_dpath;
  309.             shells[sh].num_ext = sizeof(ext_dpath)/sizeof(char *);
  310.             path = getenv(pathvar);
  311.             break;
  312.         case LIBPATH:
  313.             pathvar = "LIBPATH";
  314.             regpath = FALSE;
  315.             shells[sh].extension = ext_libpath;
  316.             shells[sh].num_ext = sizeof(ext_libpath)/sizeof(char *);
  317.             get_libpath(&path);
  318.             break;
  319.     }
  320.  
  321.     /* terminate path elements and store pointers to each directory */
  322.     if (path == (char *)NULL || *path == '\0') {
  323.         Trace((stderr, "\n%s is empty\n\n", pathvar));
  324.         if (!regpath) {
  325.             fprintf(stderr, "%s: %s is empty\n", prognam, pathvar);
  326.             exit(2);
  327.         }
  328.     } else {
  329.         Trace((stderr, "\n%s = %s\n\n", pathvar, path));
  330.         if (*path != ';')
  331.             dir[numdirs++] = path;
  332.         p = path - 1;
  333.         while (*++p)
  334.             if (*p == ';') {
  335.                 *p = '\0';
  336.                 if ((p[1] != '\0') && (p[1] != ';'))
  337.                     dir[numdirs++] = p + 1;
  338.             } else
  339.                 *p = tolower(*p);  /* should probably make this an option... */
  340.     }
  341.  
  342. /*---------------------------------------------------------------------------
  343.     For each command or file given as an argument, check all of the direc-
  344.     tories in the appropriate path.  For commands, first see if it's an in-
  345.     ternal command; if not, see if the OS will consider it a command as is
  346.     (it has a dot in its name), and if so whether it exists; then try append-
  347.     ing each extension (in order of precedence) and again check for existence
  348.     in each path directory.  For data files (DLLs), just check directories in
  349.     the path for the filename or the filename with ".dll" appended.
  350.   ---------------------------------------------------------------------------*/
  351.  
  352.     for (j = 0;  j < argc;  ++j) {
  353.         int hasdot, found=0;
  354.  
  355.         /* don't bother with internal commands if argument has a dot */
  356.         hasdot = (strchr(argv[j], (int)'.') != (char *)NULL);
  357.  
  358.         if (regpath && !hasdot) {
  359.             Trace((stderr, "checking %s internals\n", shells[sh].name));
  360.  
  361.             /* quit as soon as found:  only one internal match allowed */
  362.             for (i = 0;  (i < shells[sh].num_int) && !found;  ++i) {
  363.                 Trace((stderr, "checking %s\n", shells[sh].internal[i]));
  364.                 if (stricmp(argv[j], shells[sh].internal[i]) == 0) {
  365.                     ++found;
  366.                     printf("%s:  %s internal command", argv[j],
  367.                       shells[sh].name);
  368.                     break;
  369.                 }
  370.             }
  371.         }
  372.         for (i = 0;  (i < numdirs) && (!found || (found && all));  ++i) {
  373.             p = tempname;
  374.             q = dir[i];
  375.             while (*p++ = *q++);  /* p now points to char *after* '\0' */
  376.             p[-1] = '\\';         /* replace null */
  377.             q = argv[j];
  378.             while (*p++ = *q++);  /* copy program name */
  379.             --p;                  /* point at null */
  380.             if (!regpath || hasdot) {
  381.                 Trace((stderr, "checking %s\n", tempname));
  382.                 if (!stat(tempname, &statbuf) && !S_ISDIR(statbuf.st_mode)) {
  383.                     if (!found || all) {
  384.                         if (found == 1)
  385.                             printf(" (also");
  386.                         printf("%s%s", found? " " : "", tempname);
  387.                     }
  388.                     ++found;
  389.                     if (!all)
  390.                         break;   /* quit right now unless finding all */
  391.                 }
  392.             }
  393.             for (k = 0;  (k < shells[sh].num_ext) && (!found || (found && all));
  394.                 ++k)
  395.             {
  396.                 strcpy(p, shells[sh].extension[k]);
  397.                 Trace((stderr, "checking %s\n", tempname));
  398.                 if (!stat(tempname, &statbuf) && !S_ISDIR(statbuf.st_mode)) {
  399.                     if (!found || all) {
  400.                         if (found == 1)
  401.                             printf(" (also");
  402.                         printf("%s%s", found? " " : "", tempname);
  403.                     }
  404.                     ++found;
  405.                     if (!all)
  406.                         break;   /* quit right now unless finding all */
  407.                 }
  408.             }
  409.         } /* end i-loop */
  410.  
  411.         if (!found) {
  412.             printf("no %s in", argv[j]);
  413.             for (i = 0;  i < numdirs;  ++i)
  414.                 printf(" %s", dir[i]);
  415.         }
  416.         printf("%s\n", (found > 1)? ")" : "");
  417.     }
  418.  
  419.     return 0;
  420. }
  421.  
  422.  
  423.  
  424. /**************************/
  425. /* Function get_libpath() */
  426. /**************************/
  427.  
  428. int get_libpath(char **path)
  429. {
  430.     char *line, *p;
  431.     int foundcfg=FALSE;
  432.     FILE *cfg;
  433.  
  434.  
  435.     if ((line = (char *)malloc(4096)) == NULL) {
  436.         fprintf(stderr, "%s: not enough memory\n", prognam);
  437.         exit(8);
  438.     }
  439.  
  440.     if (!stat("\\config.sys", &statbuf) && !S_ISDIR(statbuf.st_mode) &&
  441.         (cfg = fopen("\\config.sys", "r")) != NULL)
  442.             foundcfg = TRUE;
  443.     else if (!stat("c:\\config.sys", &statbuf) && !S_ISDIR(statbuf.st_mode) &&
  444.         (cfg = fopen("c:\\config.sys", "r")) != NULL)
  445.             foundcfg = TRUE;
  446.     else {
  447.         fprintf(stderr, "%s: can't find config.sys\n", prognam);
  448.         *path = (char *)NULL;
  449.         return -1;
  450.     }
  451.  
  452.     /* assume cfg is open file pointer to config.sys */
  453.     while (fgets(line, 4096, cfg)) {
  454.         if (strnicmp(line, "LIBPATH", 7))  /* assumes no leading spaces: ? */
  455.             continue;
  456.  
  457.         /* get rid of trailing newline, if any */
  458.         if ((p=strrchr(line, '\n')) != NULL)
  459.             *p = '\0';
  460.         Trace((stderr, "found LIBPATH line:\n%s\n", line));
  461.         p = strchr(line+7, '=');
  462.         if (p == NULL) {
  463.             *path = (char *)NULL;
  464.             return -1;
  465.         }
  466.         while (*++p == ' ');
  467.         *path = p;
  468.         return 0;
  469.     }
  470.  
  471.     /* LIBPATH not found in config.sys */
  472.     *path = (char *)NULL;
  473.     return -1;
  474. }
  475.