From: "David" Newsgroups: comp.os.msdos.djgpp References: <44An5.13355$Gs DOT 66133 AT news4 DOT atl> <399EDDEB DOT C16E9D1F AT phekda DOT freeserve DOT co DOT uk> Subject: Re: DJGPP port of WHICH Lines: 697 X-Newsreader: Microsoft Outlook Express 4.72.3110.5 X-MimeOLE: Produced By Microsoft MimeOLE V4.72.3110.3 Message-ID: Date: Mon, 21 Aug 2000 06:58:19 -0400 NNTP-Posting-Host: 209.214.202.78 X-Trace: news2.atl 966856019 209.214.202.78 (Mon, 21 Aug 2000 07:06:59 EDT) NNTP-Posting-Date: Mon, 21 Aug 2000 07:06:59 EDT To: djgpp AT delorie DOT com DJ-Gateway: from newsgroup comp.os.msdos.djgpp Reply-To: djgpp AT delorie DOT com Richard Dawe wrote in message <399EDDEB DOT C16E9D1F AT phekda DOT freeserve DOT co DOT uk>... >It would be nice to have a DJGPP port of 'which'. I often miss not having >that command. Where can I get the sources of the port? Oops. I attached those files instead of including them as raw text. Here's the WHICH.C file. I kept all my changes inside WHICH.C. I used the GNU sources for WHICH v2.11. The main feature of this patch (and the reason I created it) is that "which blah" will find "blah.exe", "blah.com", and "blah.bat". This is really useful for finding out which version of grep you are using if you have both Borland C++ and DJGPP installed (or DJGPP and Cygwin). === WHICH.C === /* * which v2.x -- print full path of executables * Copyright (C) 1999, 2000 Carlo Wood * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "sys.h" #include #include "getopt.h" #include "tilde/tilde.h" #include "bash.h" #ifdef DJGPP #include #include #endif void fixpath(const char *in_path, char *out_path); static const char *progname; static void print_usage(FILE *out) { fprintf(out, "Usage: %s [options] [--] programname [...]\n", progname); fprintf(out, "Options: --version, -[vV] Print version and exit successfully.\n"); fprintf(out, " --help, Print this help and exit successfully.\n"); fprintf(out, " --skip-dot Skip directories in PATH that start with a dot.\n"); fprintf(out, " --skip-tilde Skip directories in PATH that start with a tilde.\n"); fprintf(out, " --show-dot Don't expand a dot to current directory in output.\n"); fprintf(out, " --show-tilde Output a tilde for HOME directory for non-root.\n"); fprintf(out, " --tty-only Stop processing options on the right if not on tty.\n"); fprintf(out, " --all, -a Print all matches in PATH, not just the first\n"); fprintf(out, " --read-alias, -i Read list of aliases from stdin.\n"); fprintf(out, " --skip-alias Ignore option --read-alias; don't read stdin.\n"); } static void print_version(void) { fprintf(stdout, "GNU which v" VERSION ", Copyright (C) 1999, 2000 Carlo Wood.\n"); fprintf(stdout, "GNU which comes with ABSOLUTELY NO WARRANTY;\n"); fprintf(stdout, "This program is free software; your freedom to use, change\n"); fprintf(stdout, "and distribute this program is protected by the GPL.\n"); } static void print_fail(const char *name, const char *path_list) { fprintf(stderr, "%s: no %s in (%s)\n", progname, name, path_list); } static char home[256]; static size_t homelen = 0; static int absolute_path_given; static int found_path_starts_with_dot; static char *abs_path; static int skip_dot = 0, skip_tilde = 0, skip_alias = 0, read_alias = 0; static int show_dot = 0, show_tilde = 0, show_all = 0, tty_only = 0; static char *find_command_in_path(const char *name, const char *path_list, int *path_index) { char *found = NULL, *full_path; int status, name_len; name_len = strlen(name); if (!absolute_program(name)) absolute_path_given = 0; else { char *p; absolute_path_given = 1; if (abs_path) free(abs_path); if (*name != '.' && *name != '/' && *name != '~') { abs_path = (char *)xmalloc(3 + name_len); strcpy(abs_path, "./"); strcat(abs_path, name); } else { abs_path = (char *)xmalloc(1 + name_len); strcpy(abs_path, name); } path_list = abs_path; p = strrchr(abs_path, '/'); *p++ = 0; name = p; } while (path_list && path_list[*path_index]) { char *path; if (absolute_path_given) { path = savestring(path_list); *path_index = strlen(path); } else path = get_next_path_element(path_list, path_index); if (!path) break; if (*path == '~') { char *t = tilde_expand(path); free(path); path = t; if (skip_tilde) { free(path); continue; } } if (skip_dot && *path != '/') { free(path); continue; } found_path_starts_with_dot = (*path == '.'); full_path = make_full_pathname(path, name, name_len); free(path); status = file_status(full_path); if ((status & FS_EXISTS) && (status & FS_EXECABLE)) { found = full_path; break; } free(full_path); } return (found); } static char cwd[256]; #ifdef DJGPP static char oldcwd[PATH_MAX]; // Storage for the DOS version of cwd #endif static size_t cwdlen; static void get_current_working_directory(void) { if (cwdlen) return; if (!getcwd(cwd, sizeof(cwd))) { const char *pwd = getenv("PWD"); if (pwd && strlen(pwd) < sizeof(cwd)) strcpy(cwd, pwd); } #ifdef DJGPP strcpy(oldcwd, cwd); // Save the cwd fixpath(oldcwd, cwd); // Fix the cwd #endif if (*cwd != '/') { fprintf(stderr, "Can't get current working directory\n"); exit(-1); } cwdlen = strlen(cwd); if (cwd[cwdlen - 1] != '/') { cwd[cwdlen++] = '/'; cwd[cwdlen] = 0; } } static char *path_clean_up(const char *path) { static char result[256]; const char *p1 = path; char *p2 = result; int saw_slash = 0, saw_slash_dot = 0, saw_slash_dot_dot = 0; if (*p1 != '/') { get_current_working_directory(); strcpy(result, cwd); saw_slash = 1; p2 = &result[cwdlen]; } do { if (!saw_slash || *p1 != '/') *p2++ = *p1; if (saw_slash_dot && (*p1 == '/')) p2 -= 2; if (saw_slash_dot_dot && (*p1 == '/')) { int cnt = 0; do { if (--p2 < result) { strcpy(result, path); return result; } if (*p2 == '/') ++cnt; } while (cnt != 3); ++p2; } saw_slash_dot_dot = saw_slash_dot && (*p1 == '.'); saw_slash_dot = saw_slash && (*p1 == '.'); saw_slash = (*p1 == '/'); } while (*p1++); return result; } #ifdef DJGPP /***************************************************************** * * find_alt_command_in_path * * This is used to find alternate versions of the cmd. In DOS, * the command could have ".exe", ".com", or ".bat" extensions. * ****************************************************************** / static char *find_alt_command_in_path(const char *cmd, const char *ext, const char *path_list, int old_index, int *path_index) { char altcmd[PATH_MAX]; // Storage for the alternate version of cmd char *result = NULL; *path_index = old_index; strcpy(altcmd, cmd); strcat(altcmd, ext); result = find_command_in_path(altcmd, path_list, path_index); return(result); } // This the list of alternative extensions. // To add an extension, just stick in this list // (the code automatically calculates the number // of extensions from the size of the list). const char *exts[] = { ".exe", ".com", ".bat" }; #endif int path_search(int indent, const char *cmd, const char *path_list) { char *result = NULL; int found_something = 0; if (path_list && *path_list != '\0') { int next; int path_index = 0; do { #ifdef DJGPP int indx; // Extension index int old_index; // Storage for the old path_index old_index = path_index; // Save path_index #endif next = show_all; result = find_command_in_path(cmd, path_list, &path_index); #ifdef DJGPP // If we didn't find the original command, try looking for a // version with ".exe", ".com", or ".bat" tacked on the end. if (!result) { for (indx = 0; indx < sizeof(exts) / sizeof(const char *); indx++) { result = find_alt_command_in_path(cmd, exts[indx], path_list, old_index, &path_index); if (result) break; } } #endif if (result) { const char *full_path = path_clean_up(result); int in_home = (show_tilde || skip_tilde) && !strncmp(full_path, home, homelen); if (indent) fprintf(stdout, "\t"); if (!(skip_tilde && in_home) && show_dot && found_path_starts_with_dot && !strncmp(full_path, cwd, cwdlen)) { full_path += cwdlen; fprintf(stdout, "./"); } else if (in_home) { if (skip_tilde) { next = 1; continue; } if (show_tilde) { full_path += homelen; fprintf(stdout, "~/"); } } fprintf(stdout, "%s\n", full_path); free(result); found_something = 1; } else break; } while (next); } return found_something; } void process_alias(const char *str, int argc, char *argv[], const char *path_list) { const char *p = str; int len = 0; while(*p == ' ' || *p == '\t') ++p; if (!strncmp("alias", p, 5)) p += 5; while(*p == ' ' || *p == '\t') ++p; while(*p && *p != ' ' && *p != '\t' && *p != '=') ++p, ++len; for (; argc > 0; --argc, ++argv) { char q = 0; char *cmd; if (!*argv || len != strlen(*argv) || strncmp(*argv, &p[-len], len)) continue; fputs(str, stdout); if (!show_all) *argv = NULL; while(*p == ' ' || *p == '\t') ++p; if (*p == '=') ++p; while(*p == ' ' || *p == '\t') ++p; if (*p == '"' || *p == '\'') q = *p, ++p; for(;;) { while(*p == ' ' || *p == '\t') ++p; len = 0; while(*p && *p != ' ' && *p != '\t' && *p != q && *p != '|' && *p != '&') ++p, ++len; cmd = (char *)xmalloc(len + 1); strncpy(cmd, &p[-len], len); cmd[len] = 0; if (*argv && !strcmp(cmd, *argv)) *argv = NULL; path_search(2, cmd, path_list); free(cmd); while(*p && (*p != '|' || p[1] == '|') && (*p != '&' || p[1] == '&')) ++p; if (!*p) break; ++p; } break; } } enum opts { opt_version, opt_skip_dot, opt_skip_tilde, opt_skip_alias, opt_show_dot, opt_show_tilde, opt_tty_only, opt_help }; #ifdef DJGPP /***************************************************************** * * fixpath * * This is an improved version of the DJGPP _fixpath routine. * It converts a DOS style path to a UNIX style path. * It changes "X:" (drive letter and colon combinations) to * "/dev/x" (where x is the drive letter). It changes "\" * (backslash) to "/" (forward slash). It changes ";" * (semi-colon path separator) to ":" (colon path separator). * * I have not hammered on it very hard, so bizarre DOS path * constructs may break it. * ****************************************************************** / void fixpath(const char *in_path, char *out_path) { char *s, *d, *p, *cat; s = (char *) in_path; d = out_path; while (s[0]) { switch (s[0]) { case '\\': d[0] = '/'; // Convert '\' to '/' break; case ';' : d[0] = ':'; // Convert ';' to ':' break; case ':' : d--; // Backup to overwrite drive letter cat = "/dev/"; // Cat /dev/ strcpy(d, cat); d += strlen(cat); p = s - 1; // Get the drive letter d[0] = tolower(*p); // Cat the drive letter break; default : d[0] = s[0]; // Copy all other characters break; } s++; d++; } d[0] = '\0'; // Add NULL terminator } #endif int main(int argc, char *argv[]) { #ifdef DJGPP const char *dos_path_list = getenv("PATH"); char *path_list[PATH_MAX]; #else const char *path_list = getenv("PATH"); #endif int short_option, fail_count = 0; static int long_option; struct option longopts[] = { {"help", 0, &long_option, opt_help}, {"version", 0, &long_option, opt_version}, {"skip-dot", 0, &long_option, opt_skip_dot}, {"skip-tilde", 0, &long_option, opt_skip_tilde}, {"show-dot", 0, &long_option, opt_show_dot}, {"show-tilde", 0, &long_option, opt_show_tilde}, {"tty-only", 0, &long_option, opt_tty_only}, {"all", 0, NULL, 'a'}, {"read-alias", 0, NULL, 'i'}, {"skip-alias", 0, &long_option, opt_skip_alias}, {NULL, 0, NULL, 0} }; progname = argv[0]; fixpath(dos_path_list, path_list); while ((short_option = getopt_long(argc, argv, "aivV", longopts, NULL)) != -1) { switch (short_option) { case 0: switch (long_option) { case opt_help: print_usage(stdout); return 0; case opt_version: print_version(); return 0; case opt_skip_dot: skip_dot = !tty_only; break; case opt_skip_tilde: skip_tilde = !tty_only; break; case opt_skip_alias: skip_alias = 1; break; case opt_show_dot: show_dot = !tty_only; break; case opt_show_tilde: show_tilde = (!tty_only && geteuid() != 0); break; case opt_tty_only: tty_only = !isatty(1); break; } break; case 'a': show_all = 1; break; case 'i': read_alias = 1; break; case 'v': case 'V': print_version(); return 0; } } if (show_dot) get_current_working_directory(); if (show_tilde || skip_tilde) { const char *h; if (!(h = getenv("HOME"))) { fprintf(stderr, "%s: ", progname); if (show_tilde) fprintf(stderr, "--show-tilde"); else fprintf(stderr, "--skip-tilde"); fprintf(stderr, ": Environment variable HOME not set\n"); show_tilde = skip_tilde = 0; } else { strncpy(home, h, sizeof(home)); home[sizeof(home) - 1] = 0; homelen = strlen(home); if (home[homelen - 1] != '/' && homelen < sizeof(home) - 1) { strcat(home, "/"); ++homelen; } } } if (skip_alias) read_alias = 0; argv += optind; argc -= optind; if (argc == 0) { print_usage(stderr); return -1; } if (read_alias) { char buf[1024]; if (isatty(0)) fprintf(stderr, "%s: --read-alias, -i: Warning: stdin is a tty.\n", progname); while (fgets(buf, sizeof(buf), stdin)) process_alias(buf, argc, argv, path_list); } for (; argc > 0; --argc, ++argv) { if (!*argv) continue; if (!path_search(0, *argv, path_list)) { print_fail(absolute_path_given ? strrchr(*argv, '/') + 1 : *argv, absolute_path_given ? abs_path : path_list); ++fail_count; } } return fail_count; } #ifdef NEED_XMALLOC void *xmalloc(size_t size) { void *ptr = malloc(size); if (ptr == NULL) { fprintf(stderr, "%s: Out of memory", progname); exit(-1); } return ptr; } void *xrealloc(void *ptr, size_t size) { if (!ptr) return xmalloc(size); ptr = realloc(ptr, size); if (size > 0 && ptr == NULL) { fprintf(stderr, "%s: Out of memory\n", progname); exit(-1); } return ptr; } #endif /* NEED_XMALLOC */ === WHICH.C ===