home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Monster Media 1993 #2
/
Image.iso
/
os2
/
whch20gr.zip
/
WHICH.C
< prev
next >
Wrap
C/C++ Source or Header
|
1993-04-28
|
16KB
|
475 lines
/*---------------------------------------------------------------------------
which.c
This program by default finds the path of an executable command under
either OS/2 or MS-DOS. It can also be used to find data files in the
DPATH or DLLs in the LIBPATH (at least under OS/2). It does not have
a whole lot of error-checking, but the array sizes are reasonably gen-
erous.
To do: add support for ksh, bash, etc.; allow wildcards?
Copyright (c) 1993 by Greg Roelofs. You may use this code for any pur-
pose whatsoever, as long as you don't sell it and don't claim to have
written it. Not that you'd want to.
---------------------------------------------------------------------------*/
#define VERSION "v2.02 of 28 April 1993"
/* #define DEBUG */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#ifndef TRUE
# define TRUE 1
# define FALSE 0
#endif
/* not used:
#if defined(__OS2__) && !defined(OS2)
# define OS2
#endif
*/
#if defined(__IBMC__)
# define S_IFMT 0xF000 /* or (S_IFREG | S_IFDIR | S_IFCHR) */
#endif
#ifndef S_ISDIR
# define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
#endif
#ifdef DEBUG
# define Trace(x) fprintf x
#else
# define Trace(x)
#endif
struct stat statbuf;
/* extensions must be in order of operating-system precedence! */
static char *ext_command[] = { /* COMMAND extensions */
".com",
".exe",
".bat"
};
static char *ext_cmd[] = { /* CMD extensions */
".com",
".exe",
".cmd",
".bat"
};
static char *ext_4dos[] = { /* 4DOS extensions */
".com",
".exe",
".btm",
".bat"
};
static char *ext_4os2[] = { /* 4OS2 extensions */
".com",
".exe",
".btm",
".cmd",
".bat"
};
static char *ext_libpath[] = { /* LIBPATH extension(s) */
".dll"
};
static char *ext_dpath[] = { /* DPATH extension(s) */
".boo",
".dat",
".inf",
".ini", /* does this belong here? */
".hlp",
".msg",
".ndx"
};
/* OS/2 internal commands: some of these are rarely used outside of
* command files, but all of them can be called from the command line.
* (I don't know if some of the OS/2-only commands might be in later
* versions of MS-DOS. One could use system() and check the errors,
* but that would be slow and probably difficult.)
*/
static char *int_command[] = {
"call", "cd", "chdir", "cls", "copy", "date", "del", "dir", "echo",
"erase", "exit", "for", "goto", "if", "md", "mkdir", "path", "pause",
"prompt", "rd", "rem", "ren", "rename", "rmdir", "set", "shift",
"time", "type", "ver", "verify", "vol"
};
static char *int_cmd[] = {
/* note that extproc cannot be called from command line in 4OS2 */
"chcp", "detach", "dpath", "endlocal", "extproc", "keys", "move",
"setlocal", "start",
/* all the rest are identical to int_command[] */
"call", "cd", "chdir", "cls", "copy", "date", "del", "dir", "echo",
"erase", "exit", "for", "goto", "if", "md", "mkdir", "path", "pause",
"prompt", "rd", "rem", "ren", "rename", "rmdir", "set", "shift",
"time", "type", "ver", "verify", "vol"
};
static char *int_4dos[] = {
/* these are actually 4OS2 commands; different from 4DOS? */
"?", "alias", "attrib", "beep", "cancel", "cdd", "color", "delay",
"describe", "dirs", "drawbox", "drawhline", "drawvline", "echos",
"eset", "except", "free", "global", "gosub", "help", "history", "iff",
"inkey", "input", "list", "loadbtm", "log", "memory", "popd", "pushd",
"quit", "reboot", "return", "screen", "scrput", "select", "setdos",
"tee", "text", "timer", "unalias", "unset", "vscrput", "window", "y",
/* all the rest are identical to int_command[] */
"call", "cd", "chdir", "cls", "copy", "date", "del", "dir", "echo",
"erase", "exit", "for", "goto", "if", "md", "mkdir", "path", "pause",
"prompt", "rd", "rem", "ren", "rename", "rmdir", "set", "shift",
"time", "type", "ver", "verify", "vol"
};
static char *int_4os2[] = {
"?", "alias", "attrib", "beep", "cancel", "cdd", "color", "delay",
"describe", "dirs", "drawbox", "drawhline", "drawvline", "echos",
"eset", "except", "free", "global", "gosub", "help", "history", "iff",
"inkey", "input", "list", "loadbtm", "log", "memory", "popd", "pushd",
"quit", "reboot", "return", "screen", "scrput", "select", "setdos",
"tee", "text", "timer", "unalias", "unset", "vscrput", "window", "y",
/* these are identical to int_cmd[], aside from missing extproc */
"chcp", "detach", "dpath", "endlocal", "keys", "move", "setlocal",
"start",
/* all the rest are identical to int_command[] */
"call", "cd", "chdir", "cls", "copy", "date", "del", "dir", "echo",
"erase", "exit", "for", "goto", "if", "md", "mkdir", "path", "pause",
"prompt", "rd", "rem", "ren", "rename", "rmdir", "set", "shift",
"time", "type", "ver", "verify", "vol"
};
typedef struct shellinfo {
char *name;
char **extension;
int num_ext;
char **internal;
int num_int;
} SHELLINFO;
/* these guys must match the order of shells[] */
#define _COMMAND 0
#define _CMD 1
#define _4DOS 2
#define _4OS2 3
SHELLINFO shells[] = {
{"COMMAND.COM", ext_command, sizeof(ext_command)/sizeof(char *),
int_command, sizeof(int_command)/sizeof(char *) },
{"CMD.EXE", ext_cmd, sizeof(ext_cmd)/sizeof(char *),
int_cmd, sizeof(int_cmd)/sizeof(char *) },
{"4DOS.COM", ext_4dos, sizeof(ext_4dos)/sizeof(char *),
int_4dos, sizeof(int_4dos)/sizeof(char *) },
{"4OS2.EXE", ext_4os2, sizeof(ext_4os2)/sizeof(char *),
int_4os2, sizeof(int_4os2)/sizeof(char *) },
{"4OS2-16.EXE", ext_4os2, sizeof(ext_4os2)/sizeof(char *),
int_4os2, sizeof(int_4os2)/sizeof(char *) },
{"4OS2-32.EXE", ext_4os2, sizeof(ext_4os2)/sizeof(char *),
int_4os2, sizeof(int_4os2)/sizeof(char *) }
};
#define REGPATH 0 /* for pathtype */
#define LIBPATH 1
#define DPATH 2
char *prognam;
/****************/
/* Main program */
/****************/
int main(int argc, char *argv[])
{
char *p, *q, *comspec, *path, *pathvar;
char *dir[1000], tempname[300];
int c, n, error=0;
int i, j, k, sh, all=FALSE, pathtype=REGPATH, regpath;
int numdirs=0, numshells=sizeof(shells)/sizeof(SHELLINFO);
/*---------------------------------------------------------------------------
Parse the command line...
---------------------------------------------------------------------------*/
prognam = argv[0];
p = prognam - 1;
while (*++p)
*p = tolower(*p); /* assumes "smart" tolower() */
if (argc <= 1)
--argc;
else
while (--argc > 0 && (*++argv)[0] == '-')
while (c = *++(argv[0])) /* argv[0] not program name any more */
switch (c) {
case 'a': /* list all matches */
all = TRUE;
break;
case 'l': /* search for DLLs */
pathtype = LIBPATH;
break;
case 'd': /* search for data files */
pathtype = DPATH;
break;
default:
++error;
break;
} /* end switch, while, while, if */
/*---------------------------------------------------------------------------
Print usage if any errors or if no arguments.
---------------------------------------------------------------------------*/
if (error || (argc <= 0)) {
#ifdef TRADITIONAL
fprintf(stderr, "%s: too few arguments\n", prognam);
#else
fprintf(stderr, "\n\
which for OS/2 and MS-DOS (%s), from Newtware\n\n\
usage: %s [ -a ] [ -l | -d ] cmd [ cmd ... ]\n\
(default behavior is to find location of program executed as \"cmd\")\n\
-a list all matches, not just first\n\
-l search directories in LIBPATH for DLLs (OS/2)\n\
-d search directories in DPATH for data files (OS/2)\n", VERSION, prognam);
#endif
exit(1);
}
/*---------------------------------------------------------------------------
Try to figure out what shell we're in, based on COMSPEC.
---------------------------------------------------------------------------*/
if ((comspec = getenv("COMSPEC")) == (char *)NULL || *comspec == '\0') {
Trace((stderr, "COMSPEC is empty...assuming COMSPEC = cmd.exe\n"));
sh = _CMD;
} else {
Trace((stderr, "COMSPEC = %s\n", comspec));
p = strrchr(comspec, '\\');
if (p == NULL)
p = comspec;
else
++p;
for (i = 0; i < numshells; ++i) {
if (stricmp(p, shells[i].name) == 0) {
sh = i;
break;
}
}
if (i == numshells) {
sh = _CMD;
fprintf(stderr,
"%s: unknown command shell \"%s\"\n%s: assuming %s\n",
prognam, comspec, prognam, shells[sh].name);
}
}
Trace((stderr, "shell is %s\n\n", shells[sh].name));
/*---------------------------------------------------------------------------
Get the PATH, DPATH or LIBPATH, depending on the user's wishes.
---------------------------------------------------------------------------*/
/* for regular path, current directory is always implied; not for others */
switch (pathtype) {
case REGPATH:
pathvar = "PATH";
regpath = TRUE;
dir[numdirs++] = ".";
path = getenv(pathvar);
break;
case DPATH:
pathvar = "DPATH";
regpath = FALSE;
shells[sh].extension = ext_dpath;
shells[sh].num_ext = sizeof(ext_dpath)/sizeof(char *);
path = getenv(pathvar);
break;
case LIBPATH:
pathvar = "LIBPATH";
regpath = FALSE;
shells[sh].extension = ext_libpath;
shells[sh].num_ext = sizeof(ext_libpath)/sizeof(char *);
get_libpath(&path);
break;
}
/* terminate path elements and store pointers to each directory */
if (path == (char *)NULL || *path == '\0') {
Trace((stderr, "\n%s is empty\n\n", pathvar));
if (!regpath) {
fprintf(stderr, "%s: %s is empty\n", prognam, pathvar);
exit(2);
}
} else {
Trace((stderr, "\n%s = %s\n\n", pathvar, path));
if (*path != ';')
dir[numdirs++] = path;
p = path - 1;
while (*++p)
if (*p == ';') {
*p = '\0';
if ((p[1] != '\0') && (p[1] != ';'))
dir[numdirs++] = p + 1;
} else
*p = tolower(*p); /* should probably make this an option... */
}
/*---------------------------------------------------------------------------
For each command or file given as an argument, check all of the direc-
tories in the appropriate path. For commands, first see if it's an in-
ternal command; if not, see if the OS will consider it a command as is
(it has a dot in its name), and if so whether it exists; then try append-
ing each extension (in order of precedence) and again check for existence
in each path directory. For data files (DLLs), just check directories in
the path for the filename or the filename with ".dll" appended.
---------------------------------------------------------------------------*/
for (j = 0; j < argc; ++j) {
int hasdot, found=0;
/* don't bother with internal commands if argument has a dot */
hasdot = (strchr(argv[j], (int)'.') != (char *)NULL);
if (regpath && !hasdot) {
Trace((stderr, "checking %s internals\n", shells[sh].name));
/* quit as soon as found: only one internal match allowed */
for (i = 0; (i < shells[sh].num_int) && !found; ++i) {
Trace((stderr, "checking %s\n", shells[sh].internal[i]));
if (stricmp(argv[j], shells[sh].internal[i]) == 0) {
++found;
printf("%s: %s internal command", argv[j],
shells[sh].name);
break;
}
}
}
for (i = 0; (i < numdirs) && (!found || (found && all)); ++i) {
p = tempname;
q = dir[i];
while (*p++ = *q++); /* p now points to char *after* '\0' */
p[-1] = '\\'; /* replace null */
q = argv[j];
while (*p++ = *q++); /* copy program name */
--p; /* point at null */
if (!regpath || hasdot) {
Trace((stderr, "checking %s\n", tempname));
if (!stat(tempname, &statbuf) && !S_ISDIR(statbuf.st_mode)) {
if (!found || all) {
if (found == 1)
printf(" (also");
printf("%s%s", found? " " : "", tempname);
}
++found;
if (!all)
break; /* quit right now unless finding all */
}
}
for (k = 0; (k < shells[sh].num_ext) && (!found || (found && all));
++k)
{
strcpy(p, shells[sh].extension[k]);
Trace((stderr, "checking %s\n", tempname));
if (!stat(tempname, &statbuf) && !S_ISDIR(statbuf.st_mode)) {
if (!found || all) {
if (found == 1)
printf(" (also");
printf("%s%s", found? " " : "", tempname);
}
++found;
if (!all)
break; /* quit right now unless finding all */
}
}
} /* end i-loop */
if (!found) {
printf("no %s in", argv[j]);
for (i = 0; i < numdirs; ++i)
printf(" %s", dir[i]);
}
printf("%s\n", (found > 1)? ")" : "");
}
return 0;
}
/**************************/
/* Function get_libpath() */
/**************************/
int get_libpath(char **path)
{
char *line, *p;
int foundcfg=FALSE;
FILE *cfg;
if ((line = (char *)malloc(4096)) == NULL) {
fprintf(stderr, "%s: not enough memory\n", prognam);
exit(8);
}
if (!stat("\\config.sys", &statbuf) && !S_ISDIR(statbuf.st_mode) &&
(cfg = fopen("\\config.sys", "r")) != NULL)
foundcfg = TRUE;
else if (!stat("c:\\config.sys", &statbuf) && !S_ISDIR(statbuf.st_mode) &&
(cfg = fopen("c:\\config.sys", "r")) != NULL)
foundcfg = TRUE;
else {
fprintf(stderr, "%s: can't find config.sys\n", prognam);
*path = (char *)NULL;
return -1;
}
/* assume cfg is open file pointer to config.sys */
while (fgets(line, 4096, cfg)) {
if (strnicmp(line, "LIBPATH", 7)) /* assumes no leading spaces: ? */
continue;
/* get rid of trailing newline, if any */
if ((p=strrchr(line, '\n')) != NULL)
*p = '\0';
Trace((stderr, "found LIBPATH line:\n%s\n", line));
p = strchr(line+7, '=');
if (p == NULL) {
*path = (char *)NULL;
return -1;
}
while (*++p == ' ');
*path = p;
return 0;
}
/* LIBPATH not found in config.sys */
*path = (char *)NULL;
return -1;
}