home *** CD-ROM | disk | FTP | other *** search
/ ProfitPress Mega CDROM2 …eeware (MSDOS)(1992)(Eng) / ProfitPress-MegaCDROM2.B6I / UTILITY / DIR / WHICH20.ZIP / WHICH.C < prev    next >
Encoding:
C/C++ Source or Header  |  1990-01-02  |  14.4 KB  |  330 lines

  1. /*-------------------------------------------------------------------------*/
  2. /* Program:    WHICH   .C                                                  */
  3. /* Purpose:    Scans path for executable programs.                         */
  4. /* Notes:      Compiles under TURBO C, v2.0. Should work on any machine    */
  5. /*                running MS-DOS, v2.xx or higher.                         */
  6. /* Status:     Released into the public domain. Enjoy! If you use it,      */
  7. /*                let me know what you think. You don't have to send       */
  8. /*                any money, just comments and suggestions.                */
  9. /* Updates:    28-Jan-89, v1.0a, GAT                                       */
  10. /*                - initial version.                                       */
  11. /*             05-Feb-89, v1.0b, GAT                                       */
  12. /*                - Cleaned up code a bit and added -a option.             */
  13. /*             25-Feb-89, v1.1, GAT                                        */
  14. /*                - Fixed bug in ScanPath() that didn't delete extensions  */
  15. /*                   if a match had previously been found.                 */
  16. /*                - Added GetDirs() to determine directory names.          */
  17. /*                - Avoided appending '\' to directory if at root.         */
  18. /*                - Avoided scanning same directory twice.                 */
  19. /*             03-Dec-89, v1.2, GAT                                        */
  20. /*                - Improved efficiency of help message display.           */
  21. /*                - Removed code that converted argvs to uppercase.        */
  22. /*                - Renamed ErrMsg() to write_ErrMsg().                    */
  23. /*                - Now write_ErrMsg adds a period and CR/LF combination.  */
  24. /*             02-Jan-90, v2.0, GAT                                        */
  25. /*                - Added verbose option.                                  */
  26. /*                - Rewrote main(), get_DirNames(), and scan_Path().       */
  27. /*                - Added function to clean up directory names a bit.      */
  28. /*                - Replaced #defines with enumerations.                   */
  29. /*                - Used AT&T's getopt() for program args.                 */
  30. /*                - Separated usage message into its own function.         */
  31. /*                - Tweaked variable storage.                              */
  32. /*                - Removed malloc() calls for directory names.            */
  33. /*                - Dirs[] do *not* end with a backslash.                  */
  34. /*-------------------------------------------------------------------------*/
  35.  
  36. /*-------------------------------------------------------------------------*/
  37. /* Author:     George A. Theall                                            */
  38. /* Phone:      (215) 662-0558                                              */
  39. /* SnailMail:  TifaWARE                                                    */
  40. /*             506 South 41st St., #3M                                     */
  41. /*             Philadelphia, PA.  19104   USA                              */
  42. /* E-Mail:     GTHEALL@PENNDRLS.UPENN.EDU (ARPA Internet)                  */
  43. /*-------------------------------------------------------------------------*/
  44.  
  45. /* Useful type definitions. */
  46. typedef int BOOLEAN;
  47.  
  48. typedef enum {                               /* error classes       */
  49.    err_dirs,                                 /*    can't find dirs  */
  50.    err_nhit,                                 /*    no matches       */
  51.    err_name                                  /*    invalid filename */
  52. } ERR_TYPE;
  53.  
  54. typedef enum {                               /* return codes            */
  55.    rc_ok   = 0,                              /*    no problem           */
  56.    rc_help = 1,                              /*    help given           */
  57.    rc_dirs = 5,                              /*    can't find dir names */
  58.    rc_nhit = 10,                             /*    no matches           */
  59.    rc_name = 20                              /*    invalid filename     */
  60. } RC_TYPE;
  61.  
  62. /* Symbolic constants. */
  63. #define FALSE        0
  64. #define TRUE         1
  65. #define VERS         "2.0"
  66. #define MAX_DIRS     25                      /* should be enough */
  67.  
  68. /* Required include files. */
  69. #include <stdio.h>
  70. #include <dir.h>                             /* for fnsplit(), MAXFILE, etc */
  71. #include <stdarg.h>                          /* for va_arg, etc.. */
  72. #include <stdlib.h>                          /* for exit(), getenv(), etc */
  73. #include <string.h>                          /* for strlwr() */
  74. #include "getopt.h"
  75.  
  76. /* Variables with file scope. */
  77. static char ProgName[MAXFILE];               /* space for filename */
  78. static char *Dirs[MAX_DIRS];                 /* space for dir names */
  79. static BOOLEAN                               /* option flags         */
  80.    AFlag = FALSE,                            /*   search all dirs?   */
  81.    HFlag = FALSE,                            /*   needs help?        */
  82.    VFlag = FALSE;                            /*   verbose reporting? */
  83.  
  84. /* Define the program's error messages. */
  85. /*
  86.  * NB: getopt() itself is responsible for generating the following
  87.  * error messages, which do not appear in the structure below:
  88.  *    ": illegal option -- %c"
  89.  *    ": option requires an argument -- %c"
  90.  */
  91. const static struct {
  92.    ERR_TYPE Type;
  93.    char *Msg;
  94. } Error[] = {
  95.    err_dirs, ": can't determine directories to search",
  96.    err_nhit, ": no matches for %s",
  97.    err_name, ": invalid filename -- %s"
  98. };
  99.  
  100.  
  101. /*---  main  --------------------------------------------------------------+
  102. |  Purpose:    Main body of program.                                       |
  103. |  Notes:      Future versions might add a '--' option to allow program    |
  104. |                 to work with filenames which have a leading dash.        |
  105. |  Entry:      argc = ARGument Count,                                      |
  106. |              argv = array of ARGument Variables.                         |
  107. |  Exit:       Return code as enumerated by RC_TYPE.                       |
  108. +-------------------------------------------------------------------------*/
  109. int main(int argc, char *argv[])
  110. {
  111.    char ch;
  112.    RC_TYPE rc = rc_ok;                       /* program return code */
  113.    void write_Usage(void);
  114.    void write_ErrMsg(const char *, ...);
  115.    unsigned int scan_Path(const char *, const char **);
  116.    unsigned int get_DirNames(char **);
  117.  
  118.    /*
  119.     * Isolate program name to keep error messages neat. This kludge
  120.     * is necessary for DOS v2.xx when argv[0] is NULL.
  121.     */
  122.    fnsplit((*argv == NULL) ? __FILE__ : *argv,  /* TURBO C extension! */        
  123.       NULL, NULL, ProgName, NULL);
  124.    *argv = strlwr(ProgName);                    /* TURBO C extension! */
  125.  
  126.    /* All options must appear before any filenames. */
  127.    while ((ch = getopt(argc, argv, "av?")) != EOF)
  128.       switch (ch) {
  129.          case 'a': AFlag = TRUE; break;      /* match all possible files */ 
  130.          case 'v': VFlag = TRUE; break;      /* verbose reporting */  
  131.          case '?': HFlag = TRUE; break;      /* help needed or requested */
  132.          default:  /* VOID */  ;             /* UNREACHABLE */
  133.       }
  134.    do {
  135.       --argc;
  136.       ++argv;
  137.    } while (--optind);                       /* nb: optind >= 1 in getopt() */
  138.  
  139.    if (HFlag == TRUE || argc == 0) {
  140.       write_Usage();
  141.       exit(rc_help);
  142.    }
  143.  
  144.    /* Loop for each program named on the commandline. */
  145.    if (get_DirNames(Dirs) == 0) {
  146.       write_ErrMsg(Error[err_dirs].Msg);              /* can't find dirs */
  147.       exit(rc_dirs);
  148.    }
  149.    do {
  150.  
  151.       if (strpbrk(*argv, ":\\\.*?") != NULL) {
  152.          write_ErrMsg(Error[err_name].Msg, *argv);    /* invalid filename */
  153.          rc = rc_name;
  154.          continue;
  155.       }
  156.  
  157.       if (scan_Path(*argv, Dirs) == 0) {
  158.          write_ErrMsg(Error[err_nhit].Msg, *argv);    /* no matches */
  159.          rc = rc_nhit;
  160.          continue;
  161.       }
  162.    } while (*++argv != NULL);
  163.  
  164.    exit(rc);
  165. }
  166.  
  167.  
  168. /*---  write_ErrMsg  ------------------------------------------------------+
  169. |  Purpose:    Writes an error message to stderr.                          |
  170. |  Notes:      Refer to TURBO C _REFERENCE GUIDE_ for information about    |
  171. |                 functions with a variable number of args.                |
  172. |  Entry:      MsgFmt = string containing message format,                  |
  173. |              ... = optional arguments in same format as printf().        |
  174. |  Exit:       n/a                                                         |
  175. +-------------------------------------------------------------------------*/
  176. void write_ErrMsg(const char *MsgFmt, ...)
  177. {
  178.    va_list ArgPtr;
  179.  
  180.    fputs(ProgName, stderr);
  181.    va_start(ArgPtr, MsgFmt);
  182.    vfprintf(stderr, MsgFmt, ArgPtr);
  183.    va_end(ArgPtr);
  184.    fputs(".\n", stderr);
  185. }
  186.  
  187.  
  188. /*---  write_Usage  -------------------------------------------------------+
  189. |  Purpose:    Provides a brief message about program usage.               |
  190. |  Notes:      none                                                        |
  191. |  Entry:      n/a                                                         |
  192. |  Exit:       n/a                                                         |
  193. +-------------------------------------------------------------------------*/
  194. void write_Usage(void)
  195. {
  196.    fprintf(stderr, "\n"
  197.       "TifaWARE WHICH, v" VERS ", " __DATE__
  198.          " - scans path for executable programs.\n"
  199.       "Usage:  %s [-options] prog(s)\n"
  200.       "\n"
  201.       "Options:\n"
  202.       "  -a = find all possible matches\n"
  203.       "  -v = verbose reporting\n"
  204.       "  -? = provide this help message\n"
  205.       "\n"
  206.       "Prog(s) must not contain wildcards or drive/path/extension info.\n",
  207.       ProgName);
  208. }
  209.  
  210.  
  211. /*---  scan_Path  ---------------------------------------------------------+
  212. |  Purpose:    Scans path for an executable program.                       |
  213. |  Notes:      none                                                        |
  214. |  Entry:      Name = filename of program,                                 |
  215. |              Dirs[] = array of pointers to directory names.              |
  216. |  Exit:       Number of matches found.                                    |
  217. +-------------------------------------------------------------------------*/
  218. unsigned int scan_Path(const char *Name, const char *Dirs[])
  219. {
  220.    static char *Exts[] =                     /* DON'T CHANGE ORDER OF */
  221.       {".COM", ".EXE", ".BAT", NULL};        /*    EXTENSIONS HERE!!! */
  222.    char File_Name[MAXPATH],                  /* full name of a program */
  223.       *pDot,                                 /* pointer to dot in extension */
  224.       **pExt;                                /* pointer to ptr to extension */
  225.    register unsigned int Hits = 0;           /* number of matches */
  226.    FILE *fp;
  227.  
  228.    /* If verbose printing, announce start of search. */
  229.    if (VFlag)
  230.       fprintf(stdout, "\nSearching for %s\n", Name);
  231.  
  232.    /*
  233.     * For each program name, add path info and extension, then try 
  234.     * to open the resultant file. If open succeeds, a match has
  235.     * been found so report it. Otherwise, just keep going.
  236.     */
  237.    do {                                      /* for each name in Dirs[] */
  238.  
  239.       /* More stuff if verbose reporting. */
  240.       if (VFlag)
  241.          fprintf(stdout, "   in directory %s\\\n", *Dirs);
  242.  
  243.       /*
  244.        * Names are constructed manually rather than with TURBO C's
  245.        * fnmerge() because:
  246.        *    1. *Dirs contains both drive and path info,
  247.        *    2. *Dirs does not end with a backslash,
  248.        *    3. The current method is more easily understood.
  249.        */
  250.       strcpy(File_Name, *Dirs);
  251.       strcat(File_Name, "\\");
  252.       strcat(File_Name, Name);
  253.       strupr(File_Name);                     /* TURBO C extension! */
  254.       pDot = File_Name + strlen(File_Name);
  255.       pExt = Exts;
  256.  
  257.       do {                                   /* for each name in Exts[] */
  258.          strcat(File_Name, *pExt);
  259.          if ((fp = fopen(File_Name, "rb")) != NULL) {
  260.             fclose(fp);
  261.             fprintf(stdout, "%s\n", File_Name);
  262.             ++Hits;
  263.             if (AFlag == FALSE)
  264.                return 1;
  265.          }
  266.          *pDot = NULL;
  267.       } while (*++pExt != NULL);
  268.  
  269.    } while (*++Dirs != NULL);
  270.  
  271.    return Hits;
  272. }
  273.  
  274.  
  275. /*---  get_DirNames   -----------------------------------------------------+
  276. |  Purpose:    Determines directories to search based on cwd and PATH.     |
  277. |  Notes:      none                                                        |
  278. |  Entry:      Dirs[] = array of pointers to directory names.              |
  279. |  Exit:       Number of directories to search.                            |
  280. +-------------------------------------------------------------------------*/
  281. unsigned int get_DirNames(char *Dirs[])
  282. {
  283.    register char *ptr;                       /* pointer to string */
  284.    register unsigned int cnt,                /* count of directories */
  285.       i;
  286.    char *clean_DirName(char *);
  287.  
  288.    /* Current working directory is always searched first in DOS. */   
  289.    if ((ptr = getcwd(NULL, MAXPATH)) == NULL)   /* TURBO C extension! */
  290.       return 0;
  291.    *Dirs = clean_DirName(ptr);
  292.  
  293.    /* Get names of other directories. */
  294.    cnt = 1;
  295.    ptr = strtok(getenv("PATH"), ";");
  296.    while (ptr != NULL && cnt < MAX_DIRS) {
  297.       clean_DirName(ptr);
  298.       for (i = 1; i < cnt && strcmp(*(Dirs - i), ptr) != 0; ++i)
  299.          ;
  300.       if (i == cnt) {
  301.          *++Dirs = ptr;                      /* keep it - it's unique */
  302.          ++cnt;
  303.       }
  304.       ptr = strtok(NULL, ";");
  305.    }
  306.  
  307.    return cnt;
  308. }
  309.  
  310.  
  311. /*---  clean_DirName   ----------------------------------------------------+
  312. |  Purpose:    Cleans up a directory name by converting to uppercase and   |
  313. |                 removing any trailing backslashes.                       |
  314. |  Notes:      none                                                        |
  315. |  Entry:      name = pointer to a directory name.                         |
  316. |  Exit:       pointer to name after cleaning.                             |
  317. +-------------------------------------------------------------------------*/
  318. char *clean_DirName(char *name)
  319. {
  320.    char *eos;                                /* end of string */
  321.  
  322.    /*
  323.     * Remove any trailing backslash and convert to uppercase.
  324.     */
  325.    eos = name + strlen(name) - 1;
  326.    if (*eos == '\\')
  327.       *eos = '\0';
  328.    return strupr(name);                      /* TURBO C extension! */
  329. }
  330.