/*---------------------------------------------------------------------------

    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 wildcard commands?

    Copyright (c) 1993,1995,2003 by Greg Roelofs.  4OS2/4DOS spawnl() code
    by Michael D. Lawler.  DosQuerySysInfo() code by Kenneth Porter.  You
    may use this code for any purpose 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   "v3.01 of 27 December 2003"

/* #define DEBUG */

#if (defined(unix) && !defined(UNIX))
#  define UNIX
#endif

#if (!defined(UNIX) || defined(__STDC__))
#  define OF(x)  x
#else
#  define OF(x)  ()
#endif

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#ifndef UNIX
#  include <process.h>
#endif
#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

#if defined(__IBMC__) || defined(__EMX__) || defined(__WATCOMC__)
#  ifndef __32BIT__
#    define __32BIT__
#  endif
#  define INCL_DOSMISC       /* for IBM Toolkit headers */
#  define INCL_DOSERRORS     /* for IBM Toolkit headers */
#  define INCL_NOPMAPI       /* for IBM Toolkit headers */
#  include <os2.h>
#endif

#ifdef UNIX
#  define stricmp       strcmp
#  define strnicmp      strncmp
#  define ToLower
#  define PATHSEP       ':'
#  define DIRSEP        '/'
#  define SHELLVAR      "SHELL"
#  define DEFSHELL      _SH
#  define DEFSHELLNAME  "/bin/sh"
#else
#  define ToLower       tolower   /* assumes "smart" tolower() */
#  define PATHSEP       ';'
#  define DIRSEP        '\\'
#  define SHELLVAR      "COMSPEC"
#  define DEFSHELL      _CMD
#  define DEFSHELLNAME  "cmd.exe"
#endif

#ifdef DEBUG
#  define Trace(x)   fprintf x
#else
#  define Trace(x)
#endif


int get_libpath OF((char **path));      /* our sole prototype... */


/* 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_unix[] = {        /* Unix extensions (i.e., none) */
    ""
};

static char *ext_manpath[] = {     /* MANPATH extension(s) */
    ".1", ".1.gz", ".1.z", ".1.Z", ".1x", ".1x.gz", ".1x.z", ".1x.Z",
    ".2", ".2.gz", ".2.z", ".2.Z", ".2x", ".2x.gz", ".2x.z", ".2x.Z",
    ".3", ".3.gz", ".3.z", ".3.Z", ".3x", ".3x.gz", ".3x.z", ".3x.Z",
    ".4", ".4.gz", ".4.z", ".4.Z", ".4x", ".4x.gz", ".4x.z", ".4x.Z",
    ".5", ".5.gz", ".5.z", ".5.Z", ".5x", ".5x.gz", ".5x.z", ".5x.Z",
    ".6", ".6.gz", ".6.z", ".6.Z", ".6x", ".6x.gz", ".6x.z", ".6x.Z",
    ".7", ".7.gz", ".7.z", ".7.Z", ".7x", ".7x.gz", ".7x.z", ".7x.Z",
    ".8", ".8.gz", ".8.z", ".8.Z", ".8x", ".8x.gz", ".8x.z", ".8x.Z",
    ".l", ".l.gz", ".l.z", ".l.Z", ".lx", ".lx.gz", ".lx.z", ".lx.Z",
    ".n", ".n.gz", ".n.z", ".n.Z", ".nx", ".nx.gz", ".nx.z", ".nx.Z"
};


#ifndef UNIX

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"
};

#endif /* !UNIX */


/* 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 spawnl() and check the errors,
 * but that would be slow.)
 */

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"
};

/* Unix internal commands */

static char *int_csh[] = {
    ":", "@", "alias", "bg", "break", "breaksw", "case", "cd", "chdir",
    "continue", "default", "dirs", "echo", "else", "end", "endif", "endsw",
    "eval", "exec", "exit", "fg", "foreach", "glob", "goto", "hashstat",
    "history", "if", "jobs", "kill", "limit", "login", "logout", "newgrp",
    "nice", "nohup", "notify", "onintr", "popd", "printenv", "pushd",
    "rehash", "repeat", "set", "setenv", "shift", "source", "stop", "suspend",
    "switch", "time", "umask", "unalias", "unhash", "unlimit", "unset",
    "unsetenv", "wait", "which", "while"
};

static char *int_tcsh[] = {
    ":", "@", "alias", "alloc", "bg", "bindkey", "break", "breaksw",
    "builtins", "case", "cd", "chdir", "complete", "continue", "default",
    "dirs", "echo", "echotc", "else", "end", "endif", "endsw", "eval",
    "exec", "exit", "fg", "filetest", "foreach", "glob", "goto", "hashstat",
    "history", "hup", "if", "jobs", "kill", "limit", "log", "login", "logout",
    "ls-F", "newgrp", "nice", "nohup", "notify", "onintr", "popd", "printenv",
    "pushd", "rehash", "repeat", "sched", "set", "setenv", "settc", "setty",
    "shift", "source", "stop", "suspend", "switch", "telltc", "time", "umask",
    "unalias", "uncomplete", "unhash", "unlimit", "unset", "unsetenv", "wait",
    "where", "which", "while"
};


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
#define _4OS2_16   4
#define _4OS2_32   5
#define _SH        6
#define _CSH       7
#define _TCSH      8
#define _KSH       9
#define _BASH      10
#define _ZSH       11
#define _SASH      12

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 *),
                    (char **)NULL, 0 },
    {"4OS2.EXE",    ext_4os2,      sizeof(ext_4os2)/sizeof(char *),
                    (char **)NULL, 0 },
    {"4OS2-16.EXE", ext_4os2,      sizeof(ext_4os2)/sizeof(char *),
                    (char **)NULL, 0 },
    {"4OS2-32.EXE", ext_4os2,      sizeof(ext_4os2)/sizeof(char *),
                    (char **)NULL, 0 },
    {"sh",          ext_unix,      sizeof(ext_unix)/sizeof(char *),
                    (char **)NULL, 0 },
    {"csh",         ext_unix,      sizeof(ext_unix)/sizeof(char *),
                    int_csh,       sizeof(int_csh)/sizeof(char *) },
    {"tcsh",        ext_unix,      sizeof(ext_unix)/sizeof(char *),
                    int_tcsh,      sizeof(int_tcsh)/sizeof(char *) },
    {"ksh",         ext_unix,      sizeof(ext_unix)/sizeof(char *),
                    (char **)NULL, 0 },
    {"bash",        ext_unix,      sizeof(ext_unix)/sizeof(char *),
                    (char **)NULL, 0 },
    {"zsh",         ext_unix,      sizeof(ext_unix)/sizeof(char *),
                    (char **)NULL, 0 },
    {"sash",        ext_unix,      sizeof(ext_unix)/sizeof(char *),
                    (char **)NULL, 0 }
};

#define REGPATH    0   /* for pathtype */
#define LIBPATH    1
#define DPATH      2
#define MANPATH    3

struct stat statbuf;
char *prognam;


/****************/
/* Main program */
/****************/

int main(argc, argv)
    int argc;
    char *argv[];
{
    static char tempname[1024];
    char *p, *q, *comspec, *path=NULL, *pathvar=NULL, *dir[1000];
    int i, j, k, c, error=0, startdir=0, sh=DEFSHELL;
    int all=FALSE, pathtype=REGPATH, regpath=FALSE, fourshell=FALSE;
    int numdirs=0, numshells=sizeof(shells)/sizeof(SHELLINFO);


/*---------------------------------------------------------------------------
    Parse the command line...
  ---------------------------------------------------------------------------*/

    prognam = argv[0];
    p = prognam - 1;
    while (*++p)
        *p = ToLower(*p);

    if (argc <= 1)
        --argc;
    else
        while (--argc > 0  &&  (*++argv)[0] == '-')
            while ((c = *++(argv[0])) != '\0')
                switch (c) {
                    case 'a':             /* list all matches */
                        all = TRUE;
                        break;
                    case 'm':             /* search for man pages */
                        pathtype = MANPATH;
                        break;
#ifndef UNIX
                    case 'l':             /* search for DLLs */
                        pathtype = LIBPATH;
                        break;
                    case 'd':             /* search for data files */
                        pathtype = DPATH;
                        break;
#endif
                    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
#ifdef UNIX
        fprintf(stderr, "\n\
which (%s) for Unix, from Newtware\n\n\
usage:  %s [ -a ] [ -m ] cmd [ cmd ... ]\n\
(default behavior is to find location of \"cmd\")\n\
  -a  list all matches, not just first\n\
  -m  search directories in MANPATH for man pages\n", VERSION, prognam);
#else /* !UNIX */
        fprintf(stderr, "\n\
which (%s) for OS/2 %s, from Newtware\n\n\
usage:  %s [ -a ] [ -m | -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\
  -m  search directories in MANPATH for man pages\n\
  -l  search directories in LIBPATH for DLLs%s\n\
  -d  search directories in DPATH for data files%s\n", VERSION,
#ifdef __32BIT__
          "2.x", prognam, "", "");
#else
          "and MS-DOS", prognam, " (OS/2)", " (OS/2)");
#endif /* ?__32BIT__ */
#endif /* ?UNIX */
#endif /* ?TRADITIONAL */
        exit(1);
    }

/*---------------------------------------------------------------------------
    Try to figure out what shell we're in, based on COMSPEC.
  ---------------------------------------------------------------------------*/

    if ((comspec = getenv(SHELLVAR)) == (char *)NULL || *comspec == '\0') {
        Trace((stderr, "%s is empty...assuming %s = cmd.exe\n", SHELLVAR,
          SHELLVAR, DEFSHELLNAME));
        sh = DEFSHELL;
    } else {
        Trace((stderr, "%s = %s\n", SHELLVAR, comspec));
        p = strrchr(comspec, DIRSEP);
        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 = DEFSHELL;
            fprintf(stderr,
              "%s: unknown command shell \"%s\"\n%s: assuming %s\n",
              prognam, comspec, prognam, shells[sh].name);
        }
#ifndef UNIX
        if (sh >= 2 && sh <= 5)   /* 4DOS, 4OS2, 4OS2-16, 4OS2-32 */
            fourshell = TRUE;
#endif
    }

    Trace((stderr, "shell is %s\n\n", shells[sh].name));

/*---------------------------------------------------------------------------
    Get the PATH, MANPATH, DPATH or LIBPATH, depending on the user's wishes.
  ---------------------------------------------------------------------------*/

    /* for regular path under OS/2 and MS-DOS, current directory is always
     * implied; not for others */
    switch (pathtype) {
        case REGPATH:
            pathvar = "PATH";
            regpath = TRUE;
#ifndef UNIX
            dir[numdirs++] = ".";
#endif
            path = getenv(pathvar);
            break;
        case MANPATH:
            pathvar = "MANPATH";
            regpath = FALSE;
            shells[sh].extension = ext_manpath;
            shells[sh].num_ext = sizeof(ext_manpath)/sizeof(char *);
            path = getenv(pathvar);
            break;
#ifndef UNIX
        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;
#endif /* !UNIX */
    }
    Trace((stderr, "%s now = %s\n", SHELLVAR, comspec));

/*---------------------------------------------------------------------------
    Terminate the path elements and store pointers to each one.
  ---------------------------------------------------------------------------*/

    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 != PATHSEP)
            dir[numdirs++] = path;
        p = path - 1;
        while (*++p)
            if (*p == PATHSEP) {
                *p = '\0';
                if ((p[1] != '\0') && (p[1] != PATHSEP))
                    dir[numdirs++] = p + 1;
            } else
                *p = ToLower(*p);  /* should probably make this an option... */
    }

/*---------------------------------------------------------------------------
    If we're doing a normal PATH search under 4OS2 or 4DOS, check the path
    for a "." entry; if find one, ignore the extra "." entry which was previ-
    ously inserted at the beginning of the dir[] array.  (Entries of the form
    "d:." don't count, and CMD and COMMAND always insert the "." first.)
  ---------------------------------------------------------------------------*/

    if (fourshell && pathtype == REGPATH) {
        for (i = 1;  i < numdirs;  ++i)
            if (dir[i][0] == '.' && dir[i][1] == '\0') {    /* "." */
                startdir = 1;
                break;
            }
    }

/*---------------------------------------------------------------------------
    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 or (in the case of 4DOS/4OS2) an alias; if not, search
    the path for it.  For each directory in the path, see if the OS will con-
    sider it a command as is (it has a dot in its name), and if so whether
    it exists; then try appending each extension (in order of precedence)
    and again check for existence.  For data files or DLLs, just check direc-
    tories in the path for the filename or the filename with an appropriate
    extension (".dll", ".inf", etc.) 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));

#ifndef UNIX
            /* 4DOS/4OS2 have tests for internals/aliases, so check smartly */
            if (fourshell) {
                char *tempenv[2] = {tempname, NULL};
                int rc;

#ifdef COMMANDSEP_WORKS   /* This form always fails (get correct response only
                           * if first condition is true, i.e., both alias and
                           * internal command).  It appears to be another bug
                           * in 4OS2 (rc always = 0).  The line is too long for
                           * 4DOS in any case (257 bytes for 5-char alias name).
                           */
                sprintf(tempname, "4WHICH=iff isalias %s .and. isinternal %s "
                  "then %%+ echos %s:  aliased to \"%%@alias[%s]\" %%+ exit 43 %%+"
                  " elseiff isalias %s then %%+ echos %s:  aliased to \"%%@alias"
                  "[%s]\" %%+ exit 42 %%+ elseiff isinternal %s then %%+ exit 41 "
                  "%%+ else %%+ exit 40 %%+ endiff", argv[j], argv[j],
                  argv[j], argv[j], argv[j], argv[j], argv[j], argv[j]);
                Trace((stderr, "4WHICH length = %d bytes\n\n%s\n\n",
                  strlen(tempname), tempname));
                rc = spawnle(P_WAIT, comspec, comspec, "/c", "%4which%", NULL,
                  tempenv);
                Trace((stderr, "4WHICH return code = %d\n", rc));
#else /* !COMMANDSEP_WORKS */
                /* quotes around alias necessary due to 4DOS/4OS2 bug */
                if (sh != _4DOS) {   /* 4OS2:  can do in one long command */
                    sprintf(tempname, "4WHICH=iff isalias %s .and. isinternal "
                      "%s then & echos %s:  aliased to \"%%@alias[%s]\" & exit "
                      "43 & elseiff isalias %s then & echos %s:  aliased to "
                      "\"%%@alias[%s]\" & exit 42 & elseiff isinternal %s then "
                      "& exit 41 & else & exit 40 & endiff", argv[j], argv[j],
                      argv[j], argv[j], argv[j], argv[j], argv[j], argv[j]);
                    Trace((stderr, "4WHICH length = %d bytes\n\n%s\n\n",
                      strlen(tempname), tempname));
                    /* "/c", "4which" arguments must be separate for emx+gcc */
                    rc = spawnle(P_WAIT, comspec, comspec, "/c", "%4which%",
                      NULL, tempenv);
                    Trace((stderr, "4WHICH return code = %d\n", rc));
                    if (rc == 42 || rc == 43)  /* alias (and internal if 43) */
                        ++found;
                } else {
                    sprintf(tempname, "4WHICH=iff isalias %s .and. isinternal "
                      "%s then ^ exit 43 ^ elseiff isalias %s then ^ exit 42 ^ "
                      "elseiff isinternal %s then ^ exit 41 ^ else ^ exit 40 ^ "
                      "endiff", argv[j], argv[j], argv[j], argv[j]);
                    Trace((stderr, "4WHICH length = %d bytes\n\n%s\n\n",
                      strlen(tempname), tempname));
                    rc = spawnle(P_WAIT, comspec, comspec, "/c", "%4which%", NULL,
                      tempenv);
                    Trace((stderr, "4WHICH return code = %d\n", rc));
                    if (rc == 42 || rc == 43) {   /* alias */
                        ++found;
                        sprintf(tempname, "echos %s:  aliased to \"%%@alias"
                          "[%s]\"", argv[j], argv[j]);
                        Trace((stderr, "2nd cmd length = %d bytes\n\n%s\n\n",
                          strlen(tempname), tempname));
                        spawnl(P_WAIT, comspec, comspec, "/c", tempname, NULL);
                    }
                }
#endif /* ?COMMANDSEP_WORKS */
                if (rc == 41 || (rc == 43 && all)) {   /* internal */
                    ++found;
                    sprintf(tempname, "%s internal command", shells[sh].name);
                    if (rc == 41)
                        printf("%s:  %s", argv[j], tempname);
                    else
                        printf(" (also %s", tempname);
                }
            } else
#endif /* !UNIX */
            {
                /* 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 = startdir;  (i < numdirs) && (!found || all);  ++i) {
            char *mp=NULL, *pp;

            p = tempname;
            q = dir[i];
            while ((*p++ = *q++) != '\0');  /* p now points to char *after* 0 */
            if (p[-2] == DIRSEP)            /* could be root dir (e.g., c:\) */
                --p;
            else                            /* replace null with dir sep. */
                p[-1] = DIRSEP;
            if (pathtype == MANPATH) {
                mp = p;
                p += 4;                     /* room for "man1" or "cat1" */
                *p++ = DIRSEP;
            }
            q = argv[j];
            while ((*p++ = *q++) != '\0');  /* copy program name */
            pp = p - 1;                     /* point at null */
#ifndef UNIX
            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 %s", tempname);
                        else
                            printf("%s%s", found? ", " : "", tempname);
                    }
                    ++found;
                    if (!all)
                        break;   /* quit right now unless finding all */
                }
            }
#endif
            for (k = 0;  (k < shells[sh].num_ext) && (!found || all); ++k) {
                strcpy(pp, shells[sh].extension[k]);
                if (pathtype == MANPATH) {
                    q = "man";
                    p = mp;
                    while ((*p++ = *q++) != '\0');
                    p[-1] = shells[sh].extension[k][1];   /* overwrite null */
                    Trace((stderr, "checking %s\n", tempname));
                    if (!stat(tempname, &statbuf) && !S_ISDIR(statbuf.st_mode))
                    {
                        if (!found || all) {
                            if (found == 1)
                                printf(" (also %s", tempname);
                            else
                                printf("%s%s", found? ", " : "", tempname);
                        }
                        ++found;
                        if (!all)
                            break;   /* quit right now unless finding all */
                    }
                    q = "cat";
                    p = mp;
                    while ((*p++ = *q++) != '\0');
                    p[-1] = shells[sh].extension[k][1];   /* overwrite null */
                }
                Trace((stderr, "checking %s\n", tempname));
                if (!stat(tempname, &statbuf) && !S_ISDIR(statbuf.st_mode)) {
                    if (!found || all) {
                        if (found == 1)
                            printf(" (also %s", tempname);
                        else
                            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(path)
    char **path;
{
    char *line, *p, config_sys[]="c:\\config.sys";
    int foundcfg=FALSE;
#ifdef __32BIT__
    int drive;
    APIRET rc;
#endif
    FILE *cfg=NULL;


    if ((line = (char *)malloc(4096)) == NULL) {
        fprintf(stderr, "%s: not enough memory\n", prognam);
        exit(8);
    }

#ifdef __32BIT__
    rc = DosQuerySysInfo(QSV_BOOT_DRIVE, QSV_BOOT_DRIVE, &drive, sizeof(drive));
    if (rc == NO_ERROR) {
        config_sys[0] = drive - 1 + 'a';
        if (!stat(config_sys, &statbuf) && !S_ISDIR(statbuf.st_mode) &&
            (cfg = fopen(config_sys, "r")) != NULL)
                foundcfg = TRUE;
    } else {
        fprintf(stderr, "%s: internal error (%d)\n", prognam, rc);
#endif
        if (!stat(config_sys+2, &statbuf) && !S_ISDIR(statbuf.st_mode) &&
            (cfg = fopen(config_sys+2, "r")) != NULL)
                foundcfg = TRUE;
        else if (!stat(config_sys, &statbuf) && !S_ISDIR(statbuf.st_mode) &&
            (cfg = fopen(config_sys, "r")) != NULL)
                foundcfg = TRUE;
#ifdef __32BIT__
    }
#endif

    if (!foundcfg) {
        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;
}
