From:	MERC::"uunet!CRVAX.SRI.COM!RELAY-INFO-VAX" 26-APR-1992 16:05:26.99
To:	info-vax@kl.sri.com
CC:	
Subj:	Expand wild card files name - ver 2

I found several problems in my first version of expand_files(),
fixed them, and am reposting the entire source.  

Problem 1:  The test to determine whether or not to strip the
            disk from an expanded file name did not handle all cases,
            particularly when the user's current working directory
            was set via a logical.

Problem 2:  Some programs ported from Unix expect argv[] to be terminated
            with a NULL pointer and version 1 of expand_files() didn't
            do this in all cases.

Problem 3:  expand_files() no longer expands arguments prefixed with '-',
            since these are usually taken to be options from a simulated
            Unix command line.

------------------------- Cut Here --------------------------------
/*
 * FILENAME
 *    expfiles.c
 *
 * PURPOSE
 *    Simple routine to take argc and argv from the unix-emulated command
 *    line provided by the VAX C runtime environment and:
 *
 *    - expand all wild card filenames,
 *
 *    - lowercase the expanded filenames,
 *
 *    - strip the working directory from each expanded file name if 
 *      it is the same as the invoking process's working directory, and
 *
 *    - strip the version# from the expanded filename if requested by the
 *      client.
 *
 * FEATURES
 *    - Handles normal Unix command line file globbing (using VMS wildcard
 *      characters only).  Example:
 *
 *      <program_name> *.h *.c some_logical:*.com
 *
 *    - Handles VMS-style comma-delimited file globbing so long as the
 *      file spec containing the comma (or commas) is a single command
 *      line token.  Example:
 *    
 *      <program_name> some_logical:*.com,*.opt        !Okay - 1 token
 *
 *      <program_name> "*.h ,*.opt"                    !Okay - 1 token
 *
 *      <program_name> *.h ,*.opt                      !Wrong - 2 tokens
 *
 *    - Handles all wildcard tokens supported by lib$find_file(),
 *      including "*", "%", and "[...]".
 *
 *    - Ignores any argv[] element prefixed by '-'.
 *
 * ERROR RECOVERY:
 *    If the command line arguments cannot be completely expanded (i.e., no
 *    errors encountered), the original argv and argc are returned unchanged.
 *
 * EXAMPLE USAGE
 *   The code fragment shown below was taken from the VMS version of the
 *   Unix "wc" utility:
 *
 *     main(int argc, char **argv)
 *     {
 *        ... 
 *        expand_files(&argc, &argv, 0);
 *        while (--argc)
 *        {
 *           ...
 *        }
 *     }
 *
 * AUTHOR
 *    Clark O. Morgan
 */
 
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <fab.h>
#include <nam.h>
#include <descrip.h>
#include <rmsdef.h>

#define BUFLEN             255
#define INCR                64
#define MULTIPLE    (0x1 << 1)

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

void
expand_files(
   int  *cmdline_argc,    /* From client's main(), by reference. */
   char ***cmdline_argv,  /* Ditto.                              */
   int  strip_version     /* Boolean, T -> strip version#        */
   )    
{
   int  argc, allocd, argv_len;
   char **argv, *cwd, cwd_buf[BUFLEN], inbuf[BUFLEN], outbuf[BUFLEN], **pc,
        *delim, *hb, holdbuf[BUFLEN];
   long context = NULL, find_status, flags = MULTIPLE;
   
   $DESCRIPTOR(input_desc, inbuf);
   $DESCRIPTOR(output_desc, outbuf);
   
   argc = *cmdline_argc;
   if (argc == 1)
      return;     /* simple optimization, nothing to expand. */
   if (! (argv = calloc(sizeof(argv), INCR)))
      return;     /* Boy oh boy, this process doesn't have much memory. */  
   argv_len = INCR;
   cwd      = NULL;
   pc       = *cmdline_argv;
   argv[0]  = *pc++;    /* Copy program name. */
   allocd   = 1;
   while (--argc)
   {
      if (**pc == '-')
      {
         /* Copy argument as is. */
         
         if (allocd >= argv_len)
         {
            int i;
            
            if (! (argv = realloc(argv, (argv_len + INCR) * sizeof(argv))))
            {
               /* Out of memory. */
               
               lib$find_file_end(&context);
               return;
            }
            for (i = argv_len; i < argv_len + INCR; i++)
               argv[i] = NULL;
            argv_len += INCR;
         }
         argv[allocd++] = *pc;
      }
      else
      {
         strcpy(holdbuf, *pc);
         hb = holdbuf;
         while (hb)
         {
            delim = strchr(hb, ',');   /* handle comma-delmited files. */
            if (delim)
               *delim++ = '\0';
            strcpy(inbuf, hb);
            input_desc.dsc$w_length = strlen(inbuf);
            while (1)
            {
               char *beginning;
               int  status = lib$find_file(&input_desc,
                                           &output_desc,
                                           &context,
                                           NULL,
                                           NULL,
                                           &find_status,
                                           &flags);  
               if (status == RMS$_NMF || status == RMS$_CHN)
                  break;  /* No more translations for this file spec. */
               else
               {
                  /*
                   * Successful lookup one way or another, the client will
                   * need to deal with the error cases (e.g., permission
                   * denied, invalid device, etc.).
                   */
                  
                  char *cp;
                  
                  if (allocd >= argv_len)
                  {
                     int i;
                     
                     if (! (argv = realloc(argv,
                                           (argv_len + INCR) * sizeof(argv))))
                     {
                        /* Out of memory. */
                        
                        lib$find_file_end(&context);
                        return;
                     }
                     for (i = argv_len; i < argv_len + INCR; i++)
                        argv[i] = NULL;
                     argv_len += INCR;
                  }
                  cp = strchr(outbuf, ' ');  /* Strip trailing white space */
                  if (cp)                    /* from returned file name.   */
                     *cp = '\0';
                  else
                     outbuf[BUFLEN - 1] = '\0';  
                     
                  /*
                   * Strip working directory and working disk if appropriate...
                   * lib$find_file() always returns a working directory and
                   * working disk with the expanded file name.  This tends to 
                   * foul up a lot of Unix-ported utilities, like RCS.
                   */
                  if (! cwd)
                  {
                     struct FAB fab = cc$rms_fab;
                     struct NAM nam = cc$rms_nam;
                     int        parse_status;
                     
                     cwd = getcwd(cwd_buf, sizeof(cwd_buf));
                     
                     /* Expand logical name, if any, within CWD. */
                     fab.fab$l_fna = cwd;
                     fab.fab$b_fns = strlen(cwd);
                     fab.fab$l_nam = &nam;
                     nam.nam$l_esa = cwd_buf;
                     nam.nam$b_ess = sizeof(cwd_buf);
                     if ((parse_status = sys$parse(&fab)) == RMS$_NORMAL &&
                                                              nam.nam$b_esl > 0)
                     {
                        cwd_buf[nam.nam$b_esl] = '\0';
                        if ((cp = strchr(cwd_buf, ']')) != NULL)
                           *++cp = '\0';  /* Clip trailing ".;" */
                        cwd = cwd_buf;
                     }
                  }
                  if ((cp = strrchr(outbuf, ']')) != NULL)
                  {
                     char save;
                     
                     save = *++cp;
                     *cp  = '\0';
                     if (strcmp(outbuf, cwd) == 0)
                     {
                        *cp       = save;
                        beginning = cp;
                     }
                     else
                     {
                        *cp = save;
                        cp  = beginning = outbuf;
                     }
                  }
                  else
                     cp = beginning = outbuf;
                  if (strip_version)
                  {
                     char *ver = strrchr(cp, ';');
                     
                     if (ver)
                        *ver = '\0';
                  }
                  while (*cp)
                  {
                     if (isupper(*cp))        /* Lowercase file name. */
                        *cp = tolower(*cp);
                     cp++;
                  }
                  if (! (argv[allocd] = malloc(cp - beginning + 1)))
                  {
                     lib$find_file_end(&context);
                     return;
                  }
                  strcpy(argv[allocd++], beginning);
               }
            }
            hb = (delim) ? delim : NULL;
         }
         lib$find_file_end(&context);
      }
      pc++;
   }
   *cmdline_argc = allocd;
   *cmdline_argv = argv;
}

From CMORGAN@ALOHA2.intel.com Fri Apr 24 09:00:41 1992
Received: from hermes.intel.com by cse.ogi.edu with SMTP
	(5.61+eap+OGI_1.1.named/IDA-1.2.8+OGI_1.12) id AA23962; Fri, 24 Apr 92 09:00:32 -0700
Received: from ALOHA2.intel.com by hermes.intel.com (5.65/10.0i); Fri, 24 Apr 92 09:00:06 -0700
Date: Fri, 24 Apr 92 09:00:29 PDT
From: "Clark O. Morgan, PTD, 642-8066" <CMORGAN@ALOHA2.intel.com>
Message-Id: <9204241600.utk14724@ALOHA2.intel.com>
X-To: HERMES::"morgan@cse.ogi.edu"
Subject: expfiles.c
To: morgan@cse.ogi.edu
Status: OR

/*
 * FILENAME
 *    expfiles.c
 *
 * PURPOSE
 *    Simple routine to take argc and argv from the unix-emulated command
 *    line provided by the VAX C runtime environment and:
 *
 *    - expand all wild card filenames,
 *
 *    - lowercase the expanded filenames,
 *
 *    - strip the working directory from each expanded file name if 
 *      it is the same as the invoking process's working directory, and
 *
 *    - strip the version# from the expanded filename if requested by the
 *      client.
 *
 * FEATURES
 *    - Handles normal Unix command line file globbing (using VMS wildcard
 *      characters only).  Example:
 *
 *      <program_name> *.h *.c some_logical:*.com
 *
 *    - Handles VMS-style comma-delimited file globbing so long as the
 *      file spec containing the comma (or commas) is a single command
 *      line token.  Example:
 *    
 *      <program_name> some_logical:*.com,*.opt        !Okay - 1 token
 *
 *      <program_name> "*.h ,*.opt"                    !Okay - 1 token
 *
 *      <program_name> *.h ,*.opt                      !Wrong - 2 tokens
 *
 *    - Handles all wildcard tokens supported by lib$find_file(),
 *      including "*", "%", and "[...]".
 *
 *    - Ignores any argv[] element prefixed by '-'.
 *
 *    - Ensures that expanded argument list is terminated with at least one
 *      NULL ptr (for Unix command line compatibility).
 *
 * ERROR RECOVERY:
 *    If the command line arguments cannot be completely expanded (i.e., no
 *    errors encountered), the original argv and argc are returned unchanged.
 *
 * EXAMPLE USAGE
 *   The code fragment shown below was taken from the VMS version of the
 *   Unix "wc" utility:
 *
 *     main(int argc, char **argv)
 *     {
 *        ... 
 *        expand_files(&argc, &argv, 0);
 *        while (--argc)
 *        {
 *           ...
 *        }
 *     }
 *
 * AUTHOR
 *    Clark O. Morgan
 */
 
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <fab.h>
#include <nam.h>
#include <descrip.h>
#include <rmsdef.h>

#define BUFLEN             255
#define INCR                64
#define MULTIPLE    (0x1 << 1)

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

void
expand_files(
   int  *cmdline_argc,    /* From client's main(), by reference. */
   char ***cmdline_argv,  /* Ditto.                              */
   int  strip_version     /* Boolean, T -> strip version#        */
   )    
{
   int  argc, allocd, argv_len;
   char **argv, *cwd, cwd_buf[BUFLEN], inbuf[BUFLEN], outbuf[BUFLEN], **pc,
        *delim, *hb, holdbuf[BUFLEN];
   long context = NULL, find_status, flags = MULTIPLE;
   
   $DESCRIPTOR(input_desc, inbuf);
   $DESCRIPTOR(output_desc, outbuf);
   
   argc = *cmdline_argc;
   if (argc == 1)
      return;     /* simple optimization, nothing to expand. */
   if (! (argv = calloc(sizeof(argv), INCR + 1)))
      return;     /* Boy oh boy, this process doesn't have much memory. */  
   argv_len = INCR;
   cwd      = NULL;
   pc       = *cmdline_argv;
   argv[0]  = *pc++;    /* Copy program name. */
   allocd   = 1;
   while (--argc)
   {
      if (**pc == '-')
      {
         /* Copy argument as is. */
         
         if (allocd >= argv_len)
         {
            int i;
            
            if (! (argv = realloc(argv, (argv_len + INCR + 1) * sizeof(argv))))
            {
               /* Out of memory. */
               
               lib$find_file_end(&context);
               return;
            }
            for (i = argv_len; i < argv_len + INCR + 1; i++)
               argv[i] = NULL;
            argv_len += INCR;
         }
         argv[allocd++] = *pc;
      }
      else
      {
         strcpy(holdbuf, *pc);
         hb = holdbuf;
         while (hb)
         {
            delim = strchr(hb, ',');   /* handle comma-delmited files. */
            if (delim)
               *delim++ = '\0';
            strcpy(inbuf, hb);
            input_desc.dsc$w_length = strlen(inbuf);
            while (1)
            {
               char *beginning;
               int  status = lib$find_file(&input_desc,
                                           &output_desc,
                                           &context,
                                           NULL,
                                           NULL,
                                           &find_status,
                                           &flags);  
               if (status == RMS$_NMF || status == RMS$_CHN)
                  break;  /* No more translations for this file spec. */
               else
               {
                  /*
                   * Successful lookup one way or another, the client will
                   * need to deal with the error cases (e.g., permission
                   * denied, invalid device, etc.).
                   */
                  
                  char *cp;
                  
                  if (allocd >= argv_len)
                  {
                     int i;
                     
                     if (! (argv = realloc(
                                      argv,
                                      (argv_len + INCR + 1) * sizeof(argv)))
                                          )
                     {
                        /* Out of memory. */
                        
                        lib$find_file_end(&context);
                        return;
                     }
                     for (i = argv_len; i < argv_len + INCR + 1; i++)
                        argv[i] = NULL;
                     argv_len += INCR;
                  }
                  cp = strchr(outbuf, ' ');  /* Strip trailing white space */
                  if (cp)                    /* from returned file name.   */
                     *cp = '\0';
                  else
                     outbuf[BUFLEN - 1] = '\0';  
                     
                  /*
                   * Strip working directory and working disk if appropriate...
                   * lib$find_file() always returns a working directory and
                   * working disk with the expanded file name.  This tends to 
                   * foul up a lot of Unix-ported utilities, like RCS.
                   */
                  if (! cwd)
                  {
                     struct FAB fab = cc$rms_fab;
                     struct NAM nam = cc$rms_nam;
                     int        parse_status;
                     
                     cwd = getcwd(cwd_buf, sizeof(cwd_buf));
                     
                     /* Expand logical name, if any, within CWD. */
                     fab.fab$l_fna = cwd;
                     fab.fab$b_fns = strlen(cwd);
                     fab.fab$l_nam = &nam;
                     nam.nam$l_esa = cwd_buf;
                     nam.nam$b_ess = sizeof(cwd_buf);
                     if ((parse_status = sys$parse(&fab)) == RMS$_NORMAL &&
                                                              nam.nam$b_esl > 0)
                     {
                        cwd_buf[nam.nam$b_esl] = '\0';
                        if ((cp = strchr(cwd_buf, ']')) != NULL)
                           *++cp = '\0';  /* Clip trailing ".;" */
                        cwd = cwd_buf;
                     }
                  }
                  if ((cp = strrchr(outbuf, ']')) != NULL)
                  {
                     char save;
                     
                     save = *++cp;
                     *cp  = '\0';
                     if (strcmp(outbuf, cwd) == 0)
                     {
                        *cp       = save;
                        beginning = cp;
                     }
                     else
                     {
                        *cp = save;
                        cp  = beginning = outbuf;
                     }
                  }
                  else
                     cp = beginning = outbuf;
                  if (strip_version)
                  {
                     char *ver = strrchr(cp, ';');
                     
                     if (ver)
                        *ver = '\0';
                  }
                  while (*cp)
                  {
                     if (isupper(*cp))        /* Lowercase file name. */
                        *cp = tolower(*cp);
                     cp++;
                  }
                  if (! (argv[allocd] = malloc(cp - beginning + 1)))
                  {
                     lib$find_file_end(&context);
                     return;
                  }
                  strcpy(argv[allocd++], beginning);
               }
            }
            hb = (delim) ? delim : NULL;
         }
         lib$find_file_end(&context);
      }
      pc++;
   }
   *cmdline_argc = allocd;
   *cmdline_argv = argv;
}

