home *** CD-ROM | disk | FTP | other *** search
- /*-------------------------------------------------------------------------*/
- /* Program: WHICH .C */
- /* Purpose: Scans path for executable programs. */
- /* Notes: Compiles under TURBO C, v2.0. Should work on any machine */
- /* running MS-DOS, v2.xx or higher. */
- /* Status: Released into the public domain. Enjoy! If you use it, */
- /* let me know what you think. You don't have to send */
- /* any money, just comments and suggestions. */
- /* Updates: 28-Jan-89, v1.0a, GAT */
- /* - initial version. */
- /* 05-Feb-89, v1.0b, GAT */
- /* - Cleaned up code a bit and added -a option. */
- /* 25-Feb-89, v1.1, GAT */
- /* - Fixed bug in ScanPath() that didn't delete extensions */
- /* if a match had previously been found. */
- /* - Added GetDirs() to determine directory names. */
- /* - Avoided appending '\' to directory if at root. */
- /* - Avoided scanning same directory twice. */
- /* 03-Dec-89, v1.2, GAT */
- /* - Improved efficiency of help message display. */
- /* - Removed code that converted argvs to uppercase. */
- /* - Renamed ErrMsg() to write_ErrMsg(). */
- /* - Now write_ErrMsg adds a period and CR/LF combination. */
- /* 02-Jan-90, v2.0, GAT */
- /* - Added verbose option. */
- /* - Rewrote main(), get_DirNames(), and scan_Path(). */
- /* - Added function to clean up directory names a bit. */
- /* - Replaced #defines with enumerations. */
- /* - Used AT&T's getopt() for program args. */
- /* - Separated usage message into its own function. */
- /* - Tweaked variable storage. */
- /* - Removed malloc() calls for directory names. */
- /* - Dirs[] do *not* end with a backslash. */
- /*-------------------------------------------------------------------------*/
-
- /*-------------------------------------------------------------------------*/
- /* Author: George A. Theall */
- /* Phone: (215) 662-0558 */
- /* SnailMail: TifaWARE */
- /* 506 South 41st St., #3M */
- /* Philadelphia, PA. 19104 USA */
- /* E-Mail: GTHEALL@PENNDRLS.UPENN.EDU (ARPA Internet) */
- /*-------------------------------------------------------------------------*/
-
- /* Useful type definitions. */
- typedef int BOOLEAN;
-
- typedef enum { /* error classes */
- err_dirs, /* can't find dirs */
- err_nhit, /* no matches */
- err_name /* invalid filename */
- } ERR_TYPE;
-
- typedef enum { /* return codes */
- rc_ok = 0, /* no problem */
- rc_help = 1, /* help given */
- rc_dirs = 5, /* can't find dir names */
- rc_nhit = 10, /* no matches */
- rc_name = 20 /* invalid filename */
- } RC_TYPE;
-
- /* Symbolic constants. */
- #define FALSE 0
- #define TRUE 1
- #define VERS "2.0"
- #define MAX_DIRS 25 /* should be enough */
-
- /* Required include files. */
- #include <stdio.h>
- #include <dir.h> /* for fnsplit(), MAXFILE, etc */
- #include <stdarg.h> /* for va_arg, etc.. */
- #include <stdlib.h> /* for exit(), getenv(), etc */
- #include <string.h> /* for strlwr() */
- #include "getopt.h"
-
- /* Variables with file scope. */
- static char ProgName[MAXFILE]; /* space for filename */
- static char *Dirs[MAX_DIRS]; /* space for dir names */
- static BOOLEAN /* option flags */
- AFlag = FALSE, /* search all dirs? */
- HFlag = FALSE, /* needs help? */
- VFlag = FALSE; /* verbose reporting? */
-
- /* Define the program's error messages. */
- /*
- * NB: getopt() itself is responsible for generating the following
- * error messages, which do not appear in the structure below:
- * ": illegal option -- %c"
- * ": option requires an argument -- %c"
- */
- const static struct {
- ERR_TYPE Type;
- char *Msg;
- } Error[] = {
- err_dirs, ": can't determine directories to search",
- err_nhit, ": no matches for %s",
- err_name, ": invalid filename -- %s"
- };
-
-
- /*--- main --------------------------------------------------------------+
- | Purpose: Main body of program. |
- | Notes: Future versions might add a '--' option to allow program |
- | to work with filenames which have a leading dash. |
- | Entry: argc = ARGument Count, |
- | argv = array of ARGument Variables. |
- | Exit: Return code as enumerated by RC_TYPE. |
- +-------------------------------------------------------------------------*/
- int main(int argc, char *argv[])
- {
- char ch;
- RC_TYPE rc = rc_ok; /* program return code */
- void write_Usage(void);
- void write_ErrMsg(const char *, ...);
- unsigned int scan_Path(const char *, const char **);
- unsigned int get_DirNames(char **);
-
- /*
- * Isolate program name to keep error messages neat. This kludge
- * is necessary for DOS v2.xx when argv[0] is NULL.
- */
- fnsplit((*argv == NULL) ? __FILE__ : *argv, /* TURBO C extension! */
- NULL, NULL, ProgName, NULL);
- *argv = strlwr(ProgName); /* TURBO C extension! */
-
- /* All options must appear before any filenames. */
- while ((ch = getopt(argc, argv, "av?")) != EOF)
- switch (ch) {
- case 'a': AFlag = TRUE; break; /* match all possible files */
- case 'v': VFlag = TRUE; break; /* verbose reporting */
- case '?': HFlag = TRUE; break; /* help needed or requested */
- default: /* VOID */ ; /* UNREACHABLE */
- }
- do {
- --argc;
- ++argv;
- } while (--optind); /* nb: optind >= 1 in getopt() */
-
- if (HFlag == TRUE || argc == 0) {
- write_Usage();
- exit(rc_help);
- }
-
- /* Loop for each program named on the commandline. */
- if (get_DirNames(Dirs) == 0) {
- write_ErrMsg(Error[err_dirs].Msg); /* can't find dirs */
- exit(rc_dirs);
- }
- do {
-
- if (strpbrk(*argv, ":\\\.*?") != NULL) {
- write_ErrMsg(Error[err_name].Msg, *argv); /* invalid filename */
- rc = rc_name;
- continue;
- }
-
- if (scan_Path(*argv, Dirs) == 0) {
- write_ErrMsg(Error[err_nhit].Msg, *argv); /* no matches */
- rc = rc_nhit;
- continue;
- }
- } while (*++argv != NULL);
-
- exit(rc);
- }
-
-
- /*--- write_ErrMsg ------------------------------------------------------+
- | Purpose: Writes an error message to stderr. |
- | Notes: Refer to TURBO C _REFERENCE GUIDE_ for information about |
- | functions with a variable number of args. |
- | Entry: MsgFmt = string containing message format, |
- | ... = optional arguments in same format as printf(). |
- | Exit: n/a |
- +-------------------------------------------------------------------------*/
- void write_ErrMsg(const char *MsgFmt, ...)
- {
- va_list ArgPtr;
-
- fputs(ProgName, stderr);
- va_start(ArgPtr, MsgFmt);
- vfprintf(stderr, MsgFmt, ArgPtr);
- va_end(ArgPtr);
- fputs(".\n", stderr);
- }
-
-
- /*--- write_Usage -------------------------------------------------------+
- | Purpose: Provides a brief message about program usage. |
- | Notes: none |
- | Entry: n/a |
- | Exit: n/a |
- +-------------------------------------------------------------------------*/
- void write_Usage(void)
- {
- fprintf(stderr, "\n"
- "TifaWARE WHICH, v" VERS ", " __DATE__
- " - scans path for executable programs.\n"
- "Usage: %s [-options] prog(s)\n"
- "\n"
- "Options:\n"
- " -a = find all possible matches\n"
- " -v = verbose reporting\n"
- " -? = provide this help message\n"
- "\n"
- "Prog(s) must not contain wildcards or drive/path/extension info.\n",
- ProgName);
- }
-
-
- /*--- scan_Path ---------------------------------------------------------+
- | Purpose: Scans path for an executable program. |
- | Notes: none |
- | Entry: Name = filename of program, |
- | Dirs[] = array of pointers to directory names. |
- | Exit: Number of matches found. |
- +-------------------------------------------------------------------------*/
- unsigned int scan_Path(const char *Name, const char *Dirs[])
- {
- static char *Exts[] = /* DON'T CHANGE ORDER OF */
- {".COM", ".EXE", ".BAT", NULL}; /* EXTENSIONS HERE!!! */
- char File_Name[MAXPATH], /* full name of a program */
- *pDot, /* pointer to dot in extension */
- **pExt; /* pointer to ptr to extension */
- register unsigned int Hits = 0; /* number of matches */
- FILE *fp;
-
- /* If verbose printing, announce start of search. */
- if (VFlag)
- fprintf(stdout, "\nSearching for %s\n", Name);
-
- /*
- * For each program name, add path info and extension, then try
- * to open the resultant file. If open succeeds, a match has
- * been found so report it. Otherwise, just keep going.
- */
- do { /* for each name in Dirs[] */
-
- /* More stuff if verbose reporting. */
- if (VFlag)
- fprintf(stdout, " in directory %s\\\n", *Dirs);
-
- /*
- * Names are constructed manually rather than with TURBO C's
- * fnmerge() because:
- * 1. *Dirs contains both drive and path info,
- * 2. *Dirs does not end with a backslash,
- * 3. The current method is more easily understood.
- */
- strcpy(File_Name, *Dirs);
- strcat(File_Name, "\\");
- strcat(File_Name, Name);
- strupr(File_Name); /* TURBO C extension! */
- pDot = File_Name + strlen(File_Name);
- pExt = Exts;
-
- do { /* for each name in Exts[] */
- strcat(File_Name, *pExt);
- if ((fp = fopen(File_Name, "rb")) != NULL) {
- fclose(fp);
- fprintf(stdout, "%s\n", File_Name);
- ++Hits;
- if (AFlag == FALSE)
- return 1;
- }
- *pDot = NULL;
- } while (*++pExt != NULL);
-
- } while (*++Dirs != NULL);
-
- return Hits;
- }
-
-
- /*--- get_DirNames -----------------------------------------------------+
- | Purpose: Determines directories to search based on cwd and PATH. |
- | Notes: none |
- | Entry: Dirs[] = array of pointers to directory names. |
- | Exit: Number of directories to search. |
- +-------------------------------------------------------------------------*/
- unsigned int get_DirNames(char *Dirs[])
- {
- register char *ptr; /* pointer to string */
- register unsigned int cnt, /* count of directories */
- i;
- char *clean_DirName(char *);
-
- /* Current working directory is always searched first in DOS. */
- if ((ptr = getcwd(NULL, MAXPATH)) == NULL) /* TURBO C extension! */
- return 0;
- *Dirs = clean_DirName(ptr);
-
- /* Get names of other directories. */
- cnt = 1;
- ptr = strtok(getenv("PATH"), ";");
- while (ptr != NULL && cnt < MAX_DIRS) {
- clean_DirName(ptr);
- for (i = 1; i < cnt && strcmp(*(Dirs - i), ptr) != 0; ++i)
- ;
- if (i == cnt) {
- *++Dirs = ptr; /* keep it - it's unique */
- ++cnt;
- }
- ptr = strtok(NULL, ";");
- }
-
- return cnt;
- }
-
-
- /*--- clean_DirName ----------------------------------------------------+
- | Purpose: Cleans up a directory name by converting to uppercase and |
- | removing any trailing backslashes. |
- | Notes: none |
- | Entry: name = pointer to a directory name. |
- | Exit: pointer to name after cleaning. |
- +-------------------------------------------------------------------------*/
- char *clean_DirName(char *name)
- {
- char *eos; /* end of string */
-
- /*
- * Remove any trailing backslash and convert to uppercase.
- */
- eos = name + strlen(name) - 1;
- if (*eos == '\\')
- *eos = '\0';
- return strupr(name); /* TURBO C extension! */
- }
-