home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / fchek284.zip / shell_mung.c < prev    next >
C/C++ Source or Header  |  1994-07-10  |  39KB  |  924 lines

  1. /******************************************************************/
  2.  
  3. #ifdef VAXC            /* v1.2-004 */
  4. #module SHELL_MUNG "Jym V1.2-005"       /* Only VAX C understands this. */
  5. #else                /* v1.2-004 */
  6. #define    TRUE        1    /* GNU C stdio.h doesn't define TRUE and FALSE*/
  7. #define    FALSE        0
  8. #endif
  9.  
  10. /* SHELL_MUNG.C
  11. **=============================================================================
  12. ** Copyright (C) 1989 Jym Dyer (jym@wheaties.ai.mit.edu)
  13. **
  14. ** This program is free software; you can redistribute it and/or modify
  15. ** it under the terms of the GNU General Public License as published by
  16. ** the Free Software Foundation; either version 1, or (at your option)
  17. ** any later version.
  18. **
  19. ** This program is distributed in the hope that it will be useful,
  20. ** but WITHOUT ANY WARRANTY; without even the implied warranty of
  21. ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  22. ** GNU General Public License for more details.
  23. **
  24. ** You should have received a copy of the GNU General Public License
  25. ** along with this program; if not, write to the Free Software
  26. ** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  27. **-----------------------------------------------------------------------------
  28. ** Version:     V1.2-005
  29. **-----------------------------------------------------------------------------
  30. ** Facility:    GNU
  31. **-----------------------------------------------------------------------------
  32. ** Prefix:      SHELL_
  33. **-----------------------------------------------------------------------------
  34. ** Abstract
  35. ** ~~~~~~~~
  36. **  If you think "shell mung" is a step in the preparation of a good mung dahl,
  37. ** you're right, but in this context "mung" is the verb.  ("Mung" historically
  38. ** means "mung until no good," but one could easily interpret it to mean "mung
  39. ** until nearly gnu".  Or something to that effect.)
  40. **  This file provides the shell_glob() and shell_mung() functions for the
  41. ** VMS/DCL/VAX C user.  The shell_glob() function "globs" (parses filespecs
  42. ** with wildcards and matches them with existing files).  The shell_mung()
  43. ** function provides GNU-shell-like IO redirection and globbing:  it changes
  44. ** `stderr', `stdin', and/or `stdout' to do the IO redirection; it mungs a
  45. ** VAX C program's main() function's `argc' and `argv' variables (using shell_
  46. ** glob()) to do the globbing; and it truncates `argv[0]' down to a filename
  47. ** and filetype or, if the file is a .EXE file, to just the filename.
  48. **  This makes it easy to port many GNU utilities to VMS, as these utilities
  49. ** often expect the shell to have done the work that shell_mung() does.  It can
  50. ** also be used to port Un*x utilities as well, if anybody cares.
  51. **-----------------------------------------------------------------------------
  52. ** Functions
  53. ** ~~~~~~~~~
  54. ** shell_glob()
  55. ** shell_mung()
  56. ** (static) truncate_argv_0()
  57. **-----------------------------------------------------------------------------
  58. ** Environment
  59. ** ~~~~~~~~~~~
  60. **  Must be linked with the VAX C RTL.  Intended for use with the DCL CLI on
  61. ** VMS; it would be pointless to use shell_mung() with the SHELL CLI, though
  62. ** shell_glob() might possibly come in handy.
  63. **  Must be linked with xmalloc() and xrealloc() functions, which are readily
  64. ** available from GNU source code.  These are simply malloc() and realloc()
  65. ** functions that exit if there is an error getting memory.
  66. **-----------------------------------------------------------------------------
  67. ** Author:      Jym Dyer (jym@wheaties.ai.mit) 8-Apr-1988
  68. **-----------------------------------------------------------------------------
  69. ** Modifications
  70. ** ~~~~~~~~~~~~~
  71. ** 1.0-001 - Original version.  Inspired by the DECUS C getredirection()
  72. **           function written by Martin Minow, Jerry Leichter, and Jym Dyer.
  73. **           {Jym 8-Apr-1988}
  74. ** 1.1-002 - Added `shell_default' and use of same by shell_glob(), which
  75. **           allows default filespecs to be used in globbing.
  76. **           {Jym 9-Jan-1989}
  77. ** 1.2-003 - Made shell_glob() accept unglobbable filespecs.
  78. **         - In addition to '-', '+' is now accepted as the start of an option.
  79. **           {Jym 3-Jun-1989}
  80. ** 1.2-004 - Made changes to allow compilation by GNU C.
  81. **         {Hunter Goatley, goathunter@WKUVX1.BITNET, 12-SEP-1991}
  82. ** 1.2-005 - Added Andrew Greer's patch to fix problem parsing non-filespec
  83. **         arguments to options
  84. **         {Hunter Goatley, goathunter@WKUVX1.BITNET, 22-SEP-1991}
  85. **         {Andrew Greer, <Andrew.Greer@vuw.ac.nz>}
  86. **=============================================================================
  87. */
  88.  
  89. /* -=- FILE INCLUSIONS -=- */
  90.  
  91. #include <ctype.h>
  92. #include <errno.h>
  93. #include <stdio.h>
  94. #include <string.h>
  95.  
  96. #include <fab.h>
  97. #include <nam.h>
  98. #include <rmsdef.h>
  99. #include <stsdef.h>
  100.  
  101. #ifndef VAXC                /* v1.2-004 */
  102. #define _tolower(c)     ((c) >= 'A' && (c) <= 'Z' ? (c) | 0x20:(c))
  103. #endif            /* Also changed tolower() calls to _tolower() */
  104.  
  105. /* -=- FORWARD DECLARATIONS -=- */
  106.  
  107. extern void *  xmalloc();
  108. extern void *  xrealloc();
  109.  
  110. extern void  sys$exit();
  111. extern int  sys$parse();
  112. extern int  sys$search();
  113.  
  114. extern char **  shell_glob();
  115. static char *  truncate_argv_0();
  116.  
  117. /* -=- GLOBAL VARIABLE DEFINITION -=- */
  118.  
  119. #ifdef VAXC                /* v1.2-004 */
  120. globaldef char *  shell_default = NULL; /* Default filespec for shell_glob().*/
  121. #else
  122. char *  shell_default = NULL;        /* Default filespec for shell_glob().*/
  123. #endif
  124.                                         /* Initialized here to NULL, which
  125.                                         ** means that no default filespec is
  126.                                         ** used by shell_glob().
  127.                                         */
  128.  
  129. /* -=- FUNCTIONS -=- */
  130.  
  131. /* shell_glob()
  132. **-----------------------------------------------------------------------------
  133. ** Functional Description
  134. ** ~~~~~~~~~~~~~~~~~~~~~~
  135. **  Given a filespec, this function parses that filespec out and searches for
  136. ** the file---or, if the filespec has wildcard characters in it, the files---
  137. ** associated with the filespec.  It returns a newly-allocated vector of file-
  138. ** specs that are also newly-allocated.  This function can be used to find only
  139. ** the first file that matches the filespec or all the files that match it.  In
  140. ** the latter case, this function will also provide a count of the files that
  141. ** were found.
  142. **  Filespecs returned by this function are in lowercase, to approximate the
  143. ** "look and feel" of GNU.
  144. **  This function is used by shell_mung(), and is also handy for general use.
  145. **-----------------------------------------------------------------------------
  146. ** Usage
  147. ** ~~~~~
  148. **  This function returns a vector---that is, a pointer to pointers.  Thus you
  149. ** will need to declare this function and a vector like this:
  150. **
  151. **      extern char **  shell_glob();
  152. **
  153. **      char **  filespec_v;
  154. **
  155. **  To find only the first match of a wildcarded filespec, or just to get the
  156. ** fully-parsed filespec of a given filespec, call shell_glob() with NULL as
  157. ** its second parameter.  For example, if you call it like this:
  158. **
  159. **      static int const  filespec = "*.c";
  160. **      char **  filespec_v;
  161. **              ...
  162. **      filespec_v = shell_glob(filespec,NULL);
  163. **
  164. ** The value in `filespec_v[0]' will be (for example) "dev:[dir]bar.c;".  Note
  165. ** that the character string pointer is the content of the vector, not the
  166. ** vector itself.
  167. **  With this usage, `filespec_v' points to a pointer (actually an array of one
  168. ** pointer, which is the same thing).  When used to get a number of filespecs,
  169. ** it points to an array of pointers.
  170. **  To get all the filespecs that match a filespec, use an address of an
  171. ** integer as shell_glob()'s second parameter, as in this example:
  172. **
  173. **      static int const  filespec = "*.c";
  174. **      int  filespec_count;
  175. **      char **  filespec_v;
  176. **              ...
  177. **      filespec_v = shell_glob(filespec,&filespec_count);
  178. **
  179. ** The result of this would be (for example),
  180. **
  181. **      filespec_count = 3,
  182. **      filespec_v[0] = "dev:[dir]bar.c",
  183. **      filespec_v[1] = "dev:[dir]baz.c", and
  184. **      filespec_v[2] = "dev:[dir]foo.c".
  185. **
  186. **  In either type of usage, both the vector and the character strings are in
  187. ** newly-allocated memory.
  188. **  VMS globbing entails a feature that allows one to fill in parts of a file-
  189. ** spec with default values.  This feature can be used by shell_glob() using
  190. ** `shell_default', a global character string.  Use of this character string is
  191. ** optional, and it is initially set to NULL, meaning no default filespec is
  192. ** used.  To use it, you need to declare it like this:
  193. **
  194. **      globalref char *  shell_default;
  195. **
  196. **  The most frequent use of a default filespec in VMS is to provide a default
  197. ** filetype, as shown in this example:
  198. **
  199. **      static int const  filespec = "*.c";
  200. **      int  filespec_count;
  201. **      char **  filespec_v;
  202. **              ...
  203. **      shell_default = ".c";
  204. **      filespec_v = shell_glob(filespec,&filespec_count);
  205. **
  206. ** If `user_string' (which is, of course, a character string) is (for example)
  207. ** "foo*", a filespec without a filetype, shell_glob() will look for all files
  208. ** whose filespecs match "foo*.c".  If `user_string' were "foo*.h", a filespec
  209. ** that has a filetype, the default filespec ".c" is ignored and shell_glob()
  210. ** will look for all files whose filespecs match "foo*.h".
  211. **  Default filespecs can include devices, directories, filenames, filetypes,
  212. ** and version numbers.  Wildcards can be used in filenames, filetypes, and
  213. ** version numbers.  To turn default filespecs off, just set `shell_default'
  214. ** back to NULL.
  215. **  If the given filespec cannot be globbed, shell_glob() will simply return
  216. ** a filespec vector with an array of one pointer, pointing to the unglobbable
  217. ** given filespec.  The given filespec, therefore, should be something that
  218. ** will stick around for as long as you expect to use the filespec vector.  In
  219. ** particular, don't call shell_glob() from a function using an automatic
  220. ** character string variable of that function and expect the string to still be
  221. ** available when you've exited that function.
  222. **-----------------------------------------------------------------------------
  223. ** Calling Sequence
  224. ** ~~~~~~~~~~~~~~~~
  225. ** filespec_v = shell_glob(given_filespec,filespec_count_p);
  226. **-----------------------------------------------------------------------------
  227. ** Formal Arguments     Description
  228. ** ~~~~~~~~~~~~~~~~     ~~~~~~~~~~~
  229. ** given_filespec       Filespec "as given"---that is, as typed on the command
  230. **                      line.  This is what we parse and search for.
  231. ** filespec_count_p     Pointer to an integer that is to receive a count of
  232. **                      filespecs found that match `given_filespec'.  If NULL,
  233. **                      indicates that we're only to search for the first file
  234. **                      that matches `given_filespec'.
  235. **-----------------------------------------------------------------------------
  236. ** Implicit Input       Description
  237. ** ~~~~~~~~~~~~~~       ~~~~~~~~~~~
  238. ** shell_default        This global variable is a character string of a default
  239. **                      filespec.  If NULL, no default filespec is used.
  240. **-----------------------------------------------------------------------------
  241. ** Implicit Outputs:    None
  242. **-----------------------------------------------------------------------------
  243. ** Return Value
  244. ** ~~~~~~~~~~~~
  245. **  Returns a vector of pointers to newly-parsed filespecs.
  246. **-----------------------------------------------------------------------------
  247. ** Side Effects
  248. ** ~~~~~~~~~~~~
  249. **  Memory is allocated for the filespec vector and for the filespecs that it
  250. ** points to.
  251. **-----------------------------------------------------------------------------
  252. */
  253. char **
  254. shell_glob(given_filespec,filespec_count_p)
  255.  char *  given_filespec;
  256.  int *  filespec_count_p;
  257. {
  258.   /* --- LOCAL VARIABLES --- */
  259.  
  260.   register char *  character_p;         /* General-purpose character pointer,
  261.                                         ** used to convert filespecs to
  262.                                         ** lowercase.
  263.                                         */
  264.   register char  * expanded_filespec;    /* v1.2-004 */
  265.                                         /* Buffer to hold the expanded filespec
  266.                                         ** from a call to sys$parse().
  267.                                         */
  268.   register int  filespec_count = 0;     /* Count of filespecs that sys$search()
  269.                                         ** finds.
  270.                                         */
  271.   register char **  filespec_v = NULL;  /* Vector of pointers to filespecs that
  272.                                         ** sys$search() finds.  Allocated and
  273.                                         ** reallocated as filespecs are found.
  274.                                         */
  275.   static char *our_path;                /* The default device and directory
  276.                                         ** path.  Initialized the first time
  277.                                         ** this function is called.
  278.                                         */
  279.   register char * resultant_filespec;    /* v1.2-004 */
  280.                                         /* Buffer to hold a resultant filespec
  281.                                         ** from a call to sys$search().
  282.                                         */
  283.   register int  status;                 /* Status codes returned from calls to
  284.                                         ** sys$parse() and sys$search().
  285.                                         */
  286.   struct FAB  fab = cc$rms_fab;         /* RMS file access block.
  287.                                         */
  288.   struct NAM  nam = cc$rms_nam;         /* RMS name block.
  289.                                         */
  290.  
  291.   /* --- INITIALIZE PATH --- */
  292.  
  293.   resultant_filespec = xmalloc(NAM$C_MAXRSS + 1);    /* v1.2-004 */
  294.   expanded_filespec = xmalloc(NAM$C_MAXRSS + 1);    /* v1.2-004 */
  295.   if (our_path == NULL)
  296.    (char *) our_path = getenv("PATH");            /* v1.2-004 */
  297.    /* our_path = getenv("PATH"); */
  298.  
  299.   /* --- INITIALIZE FAB AND NAM --- */
  300.  
  301.   if (shell_default != NULL)
  302.   {
  303.     fab.fab$l_dna = shell_default;
  304.     fab.fab$b_dns = strlen(shell_default);
  305.   }
  306.   fab.fab$l_fna = given_filespec;
  307.   fab.fab$b_fns = strlen(given_filespec);
  308.   fab.fab$l_nam = &nam;
  309.   nam.nam$l_esa = expanded_filespec;        /* v1.2-004 */
  310.   nam.nam$b_ess = NAM$C_MAXRSS;
  311.   nam.nam$l_rsa = resultant_filespec;        /* v1.2-004 */
  312.   nam.nam$b_rss = NAM$C_MAXRSS;
  313.  
  314.   /* --- PARSE OUT THE GIVEN FILESPEC --- */
  315.  
  316.   if ((status = sys$parse(&fab)) == RMS$_NORMAL)
  317.   {
  318.     /* --- SEARCH FOR FILE OR FILES --- */
  319.  
  320.     while ((status = sys$search(&fab)) == RMS$_NORMAL)
  321.     {
  322.       filespec_v = ((filespec_v == NULL) ? xmalloc(sizeof (char *)) :
  323.        xrealloc(filespec_v,(filespec_count + 1) * sizeof (char *)));
  324.  
  325.       nam.nam$l_rsa[nam.nam$b_rsl] = '\0';
  326.       for (character_p = nam.nam$l_rsa; *character_p; ++character_p)
  327.        *character_p = _tolower(*character_p);        /* v1.2-004 */
  328.  
  329.       if (strncmp(nam.nam$l_rsa,our_path,nam.nam$b_dev + nam.nam$b_dir) == 0)
  330.       {
  331.         filespec_v[filespec_count] = xmalloc(
  332.           (int) (nam.nam$b_rsl - nam.nam$b_dev - nam.nam$b_dir) + 1
  333.         );
  334.         (void)strcpy(filespec_v[filespec_count],nam.nam$l_name);
  335.       }
  336.       else if (strncmp(nam.nam$l_rsa,our_path,nam.nam$b_dev) == 0)
  337.       {
  338.         filespec_v[filespec_count] = xmalloc(
  339.           (int) (nam.nam$b_rsl - nam.nam$b_dev) + 1
  340.         );
  341.         (void)strcpy(filespec_v[filespec_count],nam.nam$l_dir);
  342.       }
  343.       else
  344.       {
  345.         filespec_v[filespec_count] = xmalloc((int) nam.nam$b_rsl + 1);
  346.         (void)strcpy(filespec_v[filespec_count],nam.nam$l_rsa);
  347.       }
  348.  
  349.       if (filespec_count_p == NULL)
  350.        break;
  351.       ++filespec_count;
  352.  
  353.     } /* sys$search() while */
  354.  
  355.     /* --- UPDATE FILESPEC COUNT AND RETURN FILESPEC VECTOR --- */
  356.  
  357.     if ((status == RMS$_NORMAL) || (status == RMS$_NMF))
  358.     {
  359.       if (filespec_count_p)
  360.        *filespec_count_p = filespec_count;
  361.       return filespec_v;
  362.     }
  363.  
  364.   } /* sys$parse() if */
  365.  
  366.   /* --- RETURN VECTOR REFERRING TO GIVEN FILESPEC --- */
  367.  
  368.   /* If we get here, we either could not parse the given filespec or could
  369.   ** not find it.  In such cases we simply return a vector that refers to
  370.   ** the given filespec.
  371.   */
  372.   filespec_v = xmalloc(sizeof (char *));
  373.   filespec_v[0] = given_filespec;
  374.   if (filespec_count_p)
  375.    *filespec_count_p = 1;
  376.   return filespec_v;
  377.  
  378. } /* shell_glob() */
  379.  
  380. /* shell_mung()
  381. **-----------------------------------------------------------------------------
  382. ** Functional Description
  383. ** ~~~~~~~~~~~~~~~~~~~~~~
  384. **  Given a description of the command line that invoked a program from the
  385. ** main() function's `argc' and `argv' variables, this function changes those
  386. ** variables to reflect wildcard matching of filespecs in that command line
  387. ** and changes standard IO file pointers `stderr', `stdin', and `stdout' to
  388. ** reflect any IO redirection requested in that command line.
  389. **-----------------------------------------------------------------------------
  390. ** Usage
  391. ** ~~~~~
  392. **  Since shell_mung() is written for programs running VAX C programs under the
  393. ** DCL CLI on VMS, a call to shell_mung() should be predicated on these things:
  394. **
  395. **      #if defined(VAXC) && defined(VMS)
  396. **        if (!shell$is_shell())
  397. **         shell_mung(&argc,&argv,...,...);
  398. **      #endif
  399. **
  400. ** One should call shell_mung() before doing anything else---in particular,
  401. ** before anything is done with `argc', `argv', and parts of the standard IO
  402. ** library.
  403. **  The first and second arguments to shell_mung() should always be addresses
  404. ** of `argc' and `argv', respectively.  The third and fourth arguments require
  405. ** a bit more thought.  Before they are explained, though, we have to define
  406. ** some terms.
  407. **  A "parameter" is a position-dependent command line argument that isn't an
  408. ** IO redirection (those start with '<' or '>'), an option or an option value.
  409. ** An "option" is similar to a DCL qualifier, except that it starts with '-' or
  410. ** '+' instead of '/'.  A "value" is a string that follows certain options.
  411. ** Which options have values is something that varies from program to program.
  412. ** A value in DCL is indicated with a equal sign (e.g. /qualifier=value), but
  413. ** here it can either be concatenated to an option (e.g. -ovalue) or appear as
  414. ** an argument following the option (e.g. -o value).  "Globbing," as mentioned
  415. ** above, is to parse wildcarded filespecs and match them with existing files.
  416. ** A "mung" is a legume used in Indian cooking, often after being shelled.
  417. **  The third argument to shell_mung() is the number of the parameter where we
  418. ** expect filespecs to appear---in other words, the parameters we expect to
  419. ** need globbing.  A GNU-shell-like command language interpreter will glob any
  420. ** argument that has a wildcard character in it, unless that argument is put in
  421. ** single quotes.  DCL doesn't glob arguments, and though you can put arguments
  422. ** in double quotes to pass them through exactly as typed, by the time VAX C
  423. ** has processed them into `argv' strings, the double quotes have been stripped
  424. ** off.  This is why we have to tell shell_mung() where to start globbing.  The
  425. ** assumption is made that unglobbable arguments (such as strings) will appear
  426. ** as parameters before globbable arguments (such as filespecs), an assumption
  427. ** that holds for most GNU utilities, probably because it's the only rational
  428. ** way to code utilities that accept multiple filespecs on the command line.
  429. **  As an example, consider the following command line, where "fgrep" is a DCL
  430. ** command to run the GNU "fgrep" program:
  431. **
  432. **      $ fgrep -n xyzzy *.c *.h
  433. **
  434. ** "xyzzy", "*.c", and "*.h" are the parameters.  For fgrep, the string to be
  435. ** searched for is always the first parameter ("xyzzy" in this case); the files
  436. ** to be searched always begin as a second parameter.  The "-n" is an option,
  437. ** which could be placed anywhere.  The following commands mean the same thing
  438. ** as the previous one:
  439. **
  440. **      $ fgrep xyzzy -n *.c *.h
  441. **      $ fgrep xyzzy *.c -n *.h
  442. **      $ fgrep xyzzy *.c *.h -n
  443. **
  444. ** (It is, however, usually considered better form to type the options first.
  445. ** Some programs actually require that certain options appear before certain
  446. ** parameters.)  To indicate that we expect input filespecs to begin as the
  447. ** second parameter, we call shell_mung() with 2 as its third argument:
  448. **
  449. **      shell_mung(&argc,&argv,2,...);
  450. **
  451. ** This changes `argc' and `argv' so that if the user types this:
  452. **
  453. **      $ fgrep -n xyzzy *.c
  454. **
  455. ** It's as if the user had typed this (for example):
  456. **
  457. **      $ fgrep -n xyzzy dev:[dir]bar.c dev:[dir]baz.c dev:[dir]foo.c
  458. **
  459. ** If all of the parameters are to be considered globbable filespecs, the third
  460. ** argument to shell_mung() should be 1 (indicating, of course, that globbing
  461. ** can start with the first parameter).  If no parameters are to be considered
  462. ** globbable, shell_mung()'s third argument should be 0.  If the user uses a
  463. ** negative number as the third argument, it is treated as 0.
  464. **  The fourth argument is a list of options which are followed by values, with
  465. ** indications as to which values are to be globbed, and which option/value
  466. ** combinations are to be considered as replacements for parameters (more on
  467. ** this later).  The list is passed as a character string.
  468. **  As an example, the "make" program has a "-f" option that expects a filespec
  469. ** as its value:
  470. **
  471. **      $ make [-f makefile] [target] [macros]...
  472. **
  473. ** To indicate that we have a "-f" option followed by a globbable value, we put
  474. ** 'f' in shell_mung()'s fourth argument:
  475. **
  476. **      shell_mung(&argc,&argv,0,"f");
  477. **
  478. ** When an option's value is globbed, only the first matching filespec is used
  479. ** to replace that value.  If, for example, "makefile.vms" and "makefile.xenix"
  480. ** are the only two files in your directory, and the user types one of these:
  481. **
  482. **      $ make -fmakefile.*
  483. **      $ make -f makefile.*
  484. **
  485. ** It's as if the user had typed one of these (respectively):
  486. **
  487. **      $ make -fdev:[dir]makefile.vms
  488. **      $ make -f dev:[dir]makefile.vms
  489. **
  490. **  Options with unglobbable values must also be listed, with indications that
  491. ** they are unglobbable.  The "from" program is an example of this:
  492. **
  493. **      $ from [-s sender] [user]
  494. **
  495. ** The "-s" option is followed by a username, not an input filespec.  Thus we
  496. ** put a '-' after the 's' in shell_mung()'s fourth argument:
  497. **
  498. **      shell_mung(&argc,&argv,0,"s-");
  499. **
  500. **  There are times when an option/value combination can replace a parameter.
  501. ** Using fgrep as an example again, its first parameter---the string to be
  502. ** searched for---can be replaced by the "-e" and "-f" options and their
  503. ** values.  The "-e" option is used when the string to be searched for starts
  504. ** with a '-' character and could be confused with an option, and the "-f"
  505. ** option is used when the string or strings to be searched for is in a file.
  506. ** We indicate such options by appending a '+' character to them in the string
  507. ** that is shell_mungs()'s fourth argument:
  508. **
  509. **      shell_mung(&argc,&argv,2,"e+-f+");
  510. **
  511. ** (Note that the "-e" option also has a '-' character following it, since we
  512. ** don't want its value to be globbed.  When both '+' and '-' are used, the '+'
  513. ** should always be first.)  It should be noted that such option/value pairs
  514. ** should be typed early in the command line, in positions where the parameters
  515. ** they're replacing would have been.  For example, you would do this:
  516. **
  517. **      $ fgrep -f stringfile inputfile
  518. **
  519. ** But this would be an invitation to disaster:
  520. **
  521. **      $ fgrep inputfile -f stringfile
  522. **
  523. **  Programs that use the getopt() library function to process options will
  524. ** help you decide which options to include in shell_mung()'s fourth argument.
  525. ** The third argument to getopt() is a list of options, and options that have
  526. ** values are followed by a ':' character.  Here's an example from the source
  527. ** to the GNU "grep" program:
  528. **
  529. **      while ((c = getopt(argc,argv,"0123456789A:B:CVbce:f:hilnsvwx")) != EOF)
  530. **
  531. ** This tells us that the "-A", "-B", "-e", and "-f" options have values.  Of
  532. ** these, "-A", "-B", and "-e" have unglobbable values; and "-e" and "-f" can
  533. ** replace a parameter.  Thus, our call to shell_mung() for the GNU grep
  534. ** program looks like this:
  535. **
  536. **      shell_mung(&argc,&argv,2,"A-B-e+-f+");
  537. **
  538. **  The IO redirection capabilities provided are as follows:
  539. **
  540. **      <infile         Input comes from "infile".
  541. **      >outfile        Output goes to "outfile".
  542. **      >&outfile       Output and errors go to "outfile".
  543. **      >>outfile       Output appended to "outfile".
  544. **      >>&outfile      Output and errors appended to "outfile".
  545. **
  546. ** They can be used anywhere on the command line, except between an option and
  547. ** its value.  Here are some examples:
  548. **
  549. **      $ fgrep xyzzy *.txt >&lines_with_xyzzy.lis
  550. **      $ from -s flintstone >>accumulated_flintstone_missives.dat
  551. **      $ my_mailer -d <mail_commands.com
  552. **
  553. ** Some notes:  (1) The filespecs must be next to the redirection characters;
  554. ** they can't be separate arguments.  (2) For input redirection, the "infile"
  555. ** is globbed to the first matching filespec, just like an option's value.  (3)
  556. ** The "<<" redirection characters are not implemented.  Unlike shell scripts,
  557. ** VMS DCL command procedures define standard input as records that don't start
  558. ** with a '$' character, or aren't the record defined by the DECK command.
  559. ** Thus, "<<" redirection is irrelevant.  (4) The "!" redirection character as
  560. ** used in ">!", ">&!", ">>!", and ">>&!" is also not implemented.  VMS uses
  561. ** file versions, which makes it irrelevant.  (5) If a globbable argument turns
  562. ** out to be unglobbable, the new vector will simply point to the original
  563. ** argument.  (6) Pipes (as in the "|" and "|&" redirection characters) are not
  564. ** implemented.
  565. **-----------------------------------------------------------------------------
  566. ** Calling Sequence
  567. ** ~~~~~~~~~~~~~~~~
  568. ** delta-arg-count = shell_mung(argc_p,argv_p,parameter_number,option_string);
  569. **-----------------------------------------------------------------------------
  570. ** Formal Arguments     Description
  571. ** ~~~~~~~~~~~~~~~~     ~~~~~~~~~~~
  572. ** argc_p               Pointer to argument count (main()'s `argc').
  573. ** argv_p               Pointer to argument vector (main()'s `argv').
  574. ** parameter_number     Position among parameters where filespecs are expected.
  575. ** option_string        List of options that may be followed by filespecs.
  576. **-----------------------------------------------------------------------------
  577. ** Implicit Inputs:     None
  578. **-----------------------------------------------------------------------------
  579. ** Implicit Outputs:    None
  580. **-----------------------------------------------------------------------------
  581. ** Return Value
  582. ** ~~~~~~~~~~~~
  583. **  Returns the number of new arguments that have been added to the argument
  584. ** vector.  In other words, the difference between the new value of `argc_p'
  585. ** and its old value.
  586. **-----------------------------------------------------------------------------
  587. ** Side Effects
  588. ** ~~~~~~~~~~~~
  589. **  Function may cause the program to exit if it can't open files for error,
  590. ** input, and output redirection.  Uses global variable `vaxc$errno' for exit
  591. ** status.
  592. **-----------------------------------------------------------------------------
  593. */
  594. int
  595. shell_mung(argc_p,argv_p,parameter_number,option_string)
  596.  int *  argc_p;
  597.  char ***  argv_p;
  598.  int  parameter_number;
  599.  char *  option_string;
  600. {
  601.   /* --- LOCAL VARIABLES --- */
  602.  
  603.   register char *  arg_p;               /* General-purpose argument pointer.
  604.                                         */
  605.   int  filespec_count;                  /* Number of filespecs in `filespec_v',
  606.                                         ** returned from shell_glob().
  607.                                         */
  608.   char **  filespec_v;                  /* Vector of filespecs returned from
  609.                                         ** shell_glob().
  610.                                         */
  611.   char  from_err_too = FALSE;           /* Flag set to indicate that error text
  612.                                         ** will be redirected to a file along
  613.                                         ** with the output text.
  614.                                         */
  615.   register int  i_new;                  /* Index to new pointers in `new_argv'.
  616.                                         */
  617.   register int  i_old;                  /* Index to old pointers in `argv_p'.
  618.                                         */
  619.   char **  new_argv;                    /* New argument vector, which `argv_p'
  620.                                         ** will end up being replaced with.
  621.                                         */
  622.   int  new_argv_count;                  /* Count of pointers in `new_argv'.
  623.                                         */
  624.   int  output_fd;                       /* File descriptor for file being
  625.                                         ** created as redirected output, for
  626.                                         ** use with creat() and dup2().
  627.                                         */
  628.   int  parameter_count = 0;             /* Counts parameters---arguments that
  629.                                         ** don't have '<' or '>' or '-' in
  630.                                         ** front of them.  When and if the
  631.                                         ** count reaches `parameter_number',
  632.                                         ** we start attempting to parse the
  633.                                         ** parameters as filespecs.
  634.                                         */
  635.   register char *  place_p;             /* Location of a character, as returned
  636.                                         ** from strchr() or strrchr().
  637.                                         */
  638.  
  639.   /* --- INITIALIZE THINGS --- */
  640.  
  641.   /* Allocate the new argument vector.
  642.   */
  643.   new_argv_count = *argc_p;
  644.   new_argv = xmalloc(new_argv_count * sizeof (char *));
  645.  
  646.   /* Replace main()'s `argv[0]' with a GNU-like truncated version.
  647.   */
  648.   new_argv[0] = truncate_argv_0((*argv_p)[0]);
  649.  
  650.   /* --- TRAVERSE THE ARGUMENT VECTOR --- */
  651.  
  652.   for (i_new = i_old = 1; i_old < *argc_p; ++i_old)
  653.    switch (*(arg_p = (*argv_p)[i_old]))
  654.   {
  655.     /* If it begins with '<', we're redirecting input.
  656.     */
  657.     case '<':
  658.     filespec_v = shell_glob(++arg_p,NULL);
  659.     if (freopen(*filespec_v,"r",stdin) == NULL)
  660.     {
  661.       perror(*filespec_v);
  662.       sys$exit(vaxc$errno | STS$M_INHIB_MSG);
  663.     }
  664.     free(*filespec_v);
  665.     free(filespec_v);
  666.     --new_argv_count;
  667.     break;
  668.  
  669.     /* If it begins with '>', we're redirecting output.  This comes in four
  670.     ** varieties, as one can append output to an existing file and/or send
  671.     ** error text out along with the output.  If we're told to append it (by
  672.     ** two '>' characters), we try to access the filespec and append to it.
  673.     ** If we can't access it, we proceed as if we were told to create one.
  674.     ** We create an output file (which is also what we're told to do when we
  675.     ** only get one '>' character) with "standard" VMS attributes.  If we're
  676.     ** told to redirect error text as well (by the '&' character), we set
  677.     ** `stderr' to refer to the same file.
  678.     */
  679.     case '>':
  680.     if (*++arg_p == '>')
  681.     {
  682.       if (*++arg_p == '&')
  683.       {
  684.         from_err_too = TRUE;
  685.         ++arg_p;
  686.       }
  687.       if (access(arg_p,2) == 0)
  688.       {
  689.         if (freopen(arg_p,"a",stdout) != NULL)
  690.         {
  691.           if (from_err_too == TRUE)
  692.            stderr = stdout;
  693.           --new_argv_count;
  694.           break;
  695.         }
  696.         perror(arg_p);
  697.         sys$exit(vaxc$errno | STS$M_INHIB_MSG);
  698.       }
  699.     }
  700.     /* We get to this point if we didn't want to append output text to an
  701.     ** existing file, or if an attempt to establish append access to an
  702.     ** existing file has failed.
  703.     */
  704.     if ((from_err_too == FALSE) && (*arg_p == '&'))
  705.     {
  706.       from_err_too = TRUE;
  707.       ++arg_p;
  708.     }
  709.     if (
  710.       ((output_fd = creat(arg_p,0,"rat=cr","rfm=var","mrs=512")) != 1)
  711.       && (dup2(output_fd,fileno(stdout)) != -1)
  712.       && ((from_err_too == FALSE) || (dup2(output_fd,fileno(stderr)) != -1))
  713.     )
  714.     {
  715.       --new_argv_count;
  716.       break;
  717.     }
  718.     perror(arg_p);
  719.     sys$exit(vaxc$errno | STS$M_INHIB_MSG);
  720.  
  721.     /* If it begins with '-' or '+', it's an option.  We check it against
  722.     ** `option_string' to see if it's an option followed by an argument.
  723.     */
  724.     case '-':
  725.     case '+':
  726.     if (
  727.       (option_string == NULL)
  728.       || (((char *) place_p = strchr(option_string,(*(arg_p + 1)))) == 0)
  729.     )
  730.     {
  731.       /* Option is not in `option_string'.  We copy it into `new_argv'.
  732.       */
  733.       new_argv[i_new++] = arg_p;
  734.     }
  735.     else
  736.     {
  737.       /* Option is in `option_string'.  First we check to see if its existence
  738.       ** is to be thought of as introducing a parameter.  This is indicated by
  739.       ** a '+' character following the option in `option_string'.
  740.       */
  741.       if (*++place_p == '+')
  742.        ++parameter_count;
  743.       else
  744.        --place_p;
  745.  
  746.       /* Next we check to see if the string following it is to be considered an
  747.       ** input filespec.  If a '-' character follows the option in `option_
  748.       ** string' (or a '+' character following that option), that indicates
  749.       ** that it's not to be considered as such.
  750.       */
  751.       if (*++place_p == '-')
  752.       {
  753.         new_argv[i_new++] = arg_p;
  754.         if ((arg_p = (*argv_p)[++i_old]) == NULL)
  755.             goto BREAKOUT;
  756.         else                    /* V1.2-005 */
  757.             new_argv[i_new++] = arg_p;        /* V1.2-005 */
  758.       }
  759.       else
  760.       {
  761.         /* The string after the option is an input filespec, so we glob it.
  762.         ** But we glob it to return only the first match (if it's a wildcard),
  763.         ** not all possible matches.
  764.         */
  765.         if (*(arg_p + 2) == '\0')
  766.         {
  767.           new_argv = xrealloc(new_argv,(++new_argv_count * sizeof (char *)));
  768.           new_argv[i_new++] = arg_p;
  769.           if ((arg_p = (*argv_p)[++i_old]) == NULL)
  770.            goto BREAKOUT;
  771.  
  772.           filespec_v = shell_glob(arg_p,NULL);
  773.           new_argv[i_new++] = *filespec_v;
  774.           free(filespec_v);
  775.         }
  776.         else
  777.         {
  778.           filespec_v = shell_glob((arg_p + 2),NULL);
  779.           new_argv[i_new] = xmalloc(2 + strlen(*filespec_v) + 1);
  780.           memcpy(new_argv[i_new],arg_p,2);
  781.           (void)strcpy((new_argv[i_new++] + 2),*filespec_v);
  782.           free(*filespec_v);
  783.           free(filespec_v);
  784.         }
  785.       }
  786.     }
  787.     break;
  788.  
  789.     /* If we get here, the argument is a parameter.  If we're not concerned
  790.     ** about parameters (that is, if `parameter_number' is zero or less), or
  791.     ** if the parameter count is less than `parameter_number', we simply copy
  792.     ** the parameter into the new argument vector.  Otherwise, the parameter
  793.     ** and those following it are expected to be filespecs, and an attempt is
  794.     ** made to parse them as such and put all resulting filespecs into the
  795.     ** argument vector.
  796.     */
  797.     default:
  798.     if ((parameter_number <= 0) || (++parameter_count < parameter_number))
  799.      new_argv[i_new++] = arg_p;
  800.     else
  801.     {
  802.       filespec_v = shell_glob(arg_p,&filespec_count);
  803.       if (filespec_count == 1)
  804.        new_argv[i_new++] = *filespec_v;
  805.       else
  806.       {
  807.         new_argv_count += filespec_count - 1;
  808.         new_argv = xrealloc(new_argv,(new_argv_count * sizeof (char *)));
  809.         memcpy(&new_argv[i_new],filespec_v,(filespec_count * sizeof (char *)));
  810.         i_new += filespec_count;
  811.       }
  812.       free(filespec_v);
  813.     }
  814.     break;
  815.  
  816.   } /* for/switch */
  817.  
  818. BREAKOUT:
  819.  
  820.   new_argv[i_new] = NULL;
  821.  
  822.   /* --- UPDATE ARGUMENT COUNT AND ARGUMENT VECTOR --- */
  823.  
  824.   *argc_p = i_new;
  825.   *argv_p = new_argv;
  826.  
  827.   return i_new - i_old;
  828.  
  829. } /* shell_mung() */
  830.  
  831. /* -=- STATIC FUNCTION -=- */
  832.  
  833. /* truncate_argv_0
  834. **-----------------------------------------------------------------------------
  835. ** Functional Description
  836. ** ~~~~~~~~~~~~~~~~~~~~~~
  837. **  Programs written in VAX C, when run under VMS DCL, will have their entire
  838. ** filespec (i.e., the filespec of the running image) in the main() function's
  839. ** `argv[0]' variable.  While this makes sense from a VMS standpoint, it does
  840. ** not look very good in programs ported from GNU, which often use `argv[0]' as
  841. ** an error message prefix.
  842. **  This function, which is called from shell_mung(), truncates the filespec in
  843. ** `argv[0]' to just the filename or, if the filetype is not ".EXE", to the
  844. ** filename and filetype.
  845. **-----------------------------------------------------------------------------
  846. ** Calling Sequence
  847. ** ~~~~~~~~~~~~~~~~
  848. ** new_argv[0] = truncate_argv_0(given_filespec);
  849. **-----------------------------------------------------------------------------
  850. ** Formal Argument      Description
  851. ** ~~~~~~~~~~~~~~~      ~~~~~~~~~~~
  852. ** given_filespec       The filespec to be truncated.  Assumed to be `argv[0]'
  853. **                      or a pointer to the same thing `argv[0]' points to.
  854. **-----------------------------------------------------------------------------
  855. ** Implicit Inputs:     None
  856. **-----------------------------------------------------------------------------
  857. ** Implicit Outputs:    None
  858. **-----------------------------------------------------------------------------
  859. ** Return Value
  860. ** ~~~~~~~~~~~~
  861. **  Returns the truncated filespec, which is in newly-allocated memory.
  862. **-----------------------------------------------------------------------------
  863. ** Side Effects
  864. ** ~~~~~~~~~~~~
  865. **  Memory is allocated for the truncated filespec.
  866. **-----------------------------------------------------------------------------
  867. */
  868. static char *
  869. truncate_argv_0(given_filespec)
  870.  char *  given_filespec;
  871. {
  872.   /* --- LOCAL VARIABLES --- */
  873.  
  874.   register char *  character_p;         /* General-purpose character pointer,
  875.                                         ** used to lowercase filespec.
  876.                                         */
  877.   char  * expanded_filespec;        /* v1.2-004 */
  878.                                         /* Buffer to hold the expanded filespec
  879.                                         ** from a call to sys$parse().
  880.                                         */
  881.   register char *  filespec_p;          /* Pointer to truncated filespec to be
  882.                                         ** returned.
  883.                                         */
  884.   struct FAB  fab = cc$rms_fab;         /* RMS file access block for file.
  885.                                         */
  886.   struct NAM  nam = cc$rms_nam;         /* RMS name block for file.
  887.                                         */
  888.  
  889.   /* --- INITIALIZE FABS AND NAMS --- */
  890.  
  891.   expanded_filespec = xmalloc(NAM$C_MAXRSS + 1);    /* v1.2-004 */
  892.   fab.fab$l_fna = given_filespec;
  893.   fab.fab$b_fns = strlen(given_filespec);
  894.   fab.fab$l_nam = &nam;
  895.   nam.nam$l_esa = expanded_filespec;            /* v1.2-004 */
  896.   nam.nam$b_ess = NAM$C_MAXRSS;
  897.   nam.nam$b_nop = NAM$M_SYNCHK;
  898.  
  899.   /* --- PARSE OUT THE GIVEN FILESPEC --- */
  900.  
  901.   sys$parse(&fab);
  902.  
  903.   /* --- NUL-TERMINATE AND LOWERCASE FILESPEC --- */
  904.  
  905.   *nam.nam$l_ver = '\0';
  906.  
  907.   for (character_p = nam.nam$l_esa; *character_p; ++character_p)
  908.    *character_p = _tolower(*character_p);    /* v1.2-004 */
  909.  
  910.   /* --- RETURN TRUNCATED FILESPEC --- */
  911.  
  912.   if (strcmp(nam.nam$l_type,".exe"))
  913.    filespec_p = xmalloc(nam.nam$b_name + nam.nam$b_type + 1);
  914.   else
  915.   {
  916.     *nam.nam$l_type = '\0';
  917.     filespec_p = xmalloc(nam.nam$b_name + 1);
  918.   }
  919.  
  920.   (void)strcpy(filespec_p,nam.nam$l_name);
  921.   return filespec_p;
  922.  
  923. } /* truncate_argv_0() */
  924.