www.delorie.com/archives/browse.cgi   search  
Mail Archives: djgpp/2000/08/21/07:16:02

From: "David" <nobody AT bogus DOT org>
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: <n38o5.1215$Mw.8065@news2.atl>
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 <carlo AT gnu DOT org>
 *
 * 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 <stdio.h>
#include "getopt.h"
#include "tilde/tilde.h"
#include "bash.h"
#ifdef DJGPP
#include <string.h>
#include <limits.h>
#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 ===




- Raw text -


  webmaster     delorie software   privacy  
  Copyright © 2019   by DJ Delorie     Updated Jul 2019