home *** CD-ROM | disk | FTP | other *** search
/ The C Users' Group Library 1994 August / wc-cdrom-cusersgrouplibrary-1994-08.iso / vol_300 / 368_01 / gnulibas.zoo / _cwild.c next >
C/C++ Source or Header  |  1990-09-19  |  10KB  |  372 lines

  1. /*  _cwild.c - (reasonable, i.e. **IX style) wildcard expansion and
  2.     passing of long commandlines for MSC
  3.     Copyright (C) 1990 by Thorsten Ohl, td12@ddagsi3.bitnet
  4.  
  5.     This program is free software; you can redistribute it and/or modify
  6.     it under the terms of the GNU General Public License as published by
  7.     the Free Software Foundation; either version 1, or (at your option)
  8.     any later version.
  9.  
  10.     This program is distributed in the hope that it will be useful,
  11.     but WITHOUT ANY WARRANTY; without even the implied warranty of
  12.     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13.     GNU General Public License for more details.
  14.  
  15.     You should have received a copy of the GNU General Public License
  16.     along with this program; if not, write to the Free Software
  17.     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  18.  
  19.     $Header: e:/gnu/lib/RCS/_cwild.c 1.0 90/09/11 01:55:11 tho Exp $
  20.  */
  21.  
  22.  
  23. /* One of the major shortcomings of MS-DOS is the inability to pass long
  24.    commandlines.  Commandlines are restricted to 126 characters in length
  25.    and may not contain newlines, etc.
  26.  
  27.    Both of these restrictions make it impossible to put reasonably sized
  28.    `awk', `sed', `perl', etc. scripts into shell scripts.  Also it's
  29.    impossible to put reasonably sized shell script fragments into
  30.    makefiles.
  31.  
  32.    One way to overcome *some* of these problems is to let the user
  33.    commands perform their own globbing.  The second (more satisfactory)
  34.    way is to invent a new scheme for argument passing.
  35.  
  36.    One approach that presents itself, is to pass the arguments via the
  37.    environment.  This is the approach adopted here.  If this code is
  38.    compiled with `-DWANT_LONG_COMMANDLINES' (which is implied by
  39.    `-DSMART_SHELL_ONLY' and `-DSMART_SHELL'), the startup code looks for
  40.    "_argc" in the environment.  If it is found, it is used as a argument
  41.    count and the commandline is taken from "_argv0", "_argv1", ...  The
  42.    MS-DOS commandline is *ignored* in this case!  This is of course only
  43.    useful if your Shell or Make supports this feature.  (GNU make will do
  44.    in the next version.)
  45.  
  46.    The other part of the code uses the GNU globbing library to perform
  47.    globbing of the MS-DOS commandline in a **IX compatible way.
  48.  
  49.    This code has been developped to be used with the startup code of
  50.    Microsoft C.  Compile with one of SMART_SHELL_ONLY, SMART_SHELL, or
  51.    DUMB_SHELL_ONLY.  Link with the `setargv.obj' that came with the
  52.    compiler to ensure that _cwild () is called during startup.
  53.  
  54.    IMPORTANT:  Since we want this appraoch to become compiler independent,
  55.    feedback in form of patches for other compilers (Turbo C, Zortech,
  56.    etc.) is *greatly* appreciated.  */
  57.  
  58.  
  59. /* NOTE:  Even though this approach is almost evident, I do *not* put it
  60.    in the public domain.  I CLAIM A COPYRIGHT BOTH ON THIS CODE AND THE
  61.    UNDERLYING IDEAS.  But you are welcome to redistribute, use and
  62.    improve it under the terms of the GNU GENERAL PUBLIC LICENSE.
  63.  
  64.    This seems appropriate in the days of aggressive copyright lawsuits
  65.    to prevent someone from preventing YOU to use it.  */
  66.  
  67.  
  68. /* Configuration Section:  */
  69.  
  70. /* Define this if you are exclusively using Bash, GNU make,
  71.    or other winning utilities which take the load of expanding
  72.    the commandline from your program and know how to pass
  73.    *long* commandlines.  */
  74.  
  75. #ifdef SMART_SHELL_ONLY
  76.  
  77. #ifndef WANT_LONG_COMMANDLINES
  78. #define WANT_LONG_COMMANDLINES
  79. #endif
  80.  
  81. #ifdef WANT_GLOBBING
  82. #undef WANT_GLOBBING
  83. #endif
  84.  
  85. #endif /* SMART_SHELL_ONLY */
  86.  
  87.  
  88. /* Define this if you are always use loosing Shells and Makes, that don't
  89.    know how to pass long commandlines.   And if your program has to expand
  90.    the commandline itself.  */
  91.  
  92. #ifdef DUMB_SHELL_ONLY
  93.  
  94. #ifdef WANT_LONG_COMMANDLINES
  95. #undef WANT_LONG_COMMANDLINES
  96. #endif
  97.  
  98. #ifndef WANT_GLOBBING
  99. #define WANT_GLOBBING
  100. #endif
  101.  
  102. #endif /* not DUMB_SHELL_ONLY */
  103.  
  104.  
  105. #if !defined(SMART_SHELL) && !defined(SMART_SHELL_ONLY) \
  106.   && !defined(DUMB_SHELL_ONLY)
  107. #define SMART_SHELL
  108. #endif
  109.  
  110. /* Define this if your program can expect long commandlines, but should
  111.    also be able to do some globbing. */
  112.  
  113. #ifdef SMART_SHELL
  114.  
  115. #ifndef WANT_LONG_COMMANDLINES
  116. #define WANT_LONG_COMMANDLINES
  117. #endif
  118.  
  119. #ifndef WANT_GLOBBING
  120. #define WANT_GLOBBING
  121. #endif
  122.  
  123. #endif /* SMART_SHELL */
  124.  
  125.  
  126. /* This code relies on the standard GNU globbing library, which does
  127.    all the hard work.  */
  128.  
  129. /* This wildcard expansion module is tailored for use with the Microsoft
  130.    C startup code and uses their conventions.  It will probably not work
  131.    with the libraries of other compilers.  */
  132.  
  133. #include <stdio.h>
  134. #include <stdlib.h>
  135. #include <string.h>
  136. #include <ctype.h>
  137.  
  138.  
  139. #ifdef WANT_LONG_COMMANDLINES
  140. static int _cenv (void);
  141. #define LONG_ARGS_PRESENT    (getenv ("_argc") != NULL)
  142. #else
  143. #define _cenv()            1
  144. #define LONG_ARGS_PRESENT    0
  145. #endif
  146.  
  147.  
  148. #ifdef WANT_GLOBBING
  149. #include <gnulib.h>
  150. static int compare (char **s1, char **s2);
  151. static int _cglob (void);
  152. #else
  153. #define _cglob()        1
  154. #endif
  155.  
  156. int _cwild (void);
  157.  
  158.  
  159.  
  160. /* The external argument vector:  */
  161.  
  162. extern int __argc;
  163. extern char ** __argv;
  164.  
  165.  
  166. /* This is the main entry point, decide whether we have a smart shell
  167.    or have to do the globbing ourselves.  */
  168.  
  169. int
  170. _cwild (void)
  171. {
  172.   return LONG_ARGS_PRESENT ? _cenv () : _cglob ();
  173. }
  174.  
  175.  
  176. #ifdef WANT_LONG_COMMANDLINES
  177.  
  178. /* We have been called by a smart shell, get arguments from the
  179.    environment entries "_argc", "_argv0", "_argv1", ...  */
  180.  
  181. int
  182. _cenv (void)
  183. {
  184.   int ind = 0;
  185.   char **argv;
  186.  
  187.   /* We know that "_argc" is there! */
  188.  
  189.   __argc = atoi (getenv ("_argc"));
  190.   if (__argc == 0)
  191.     return -1;
  192.  
  193.   argv = (char **) malloc ((__argc + 1) * sizeof (char *));
  194.   if (argv == NULL)
  195.     return -1;
  196.  
  197.   while (ind < __argc)
  198.     {
  199.       char entry[10];
  200.       char *p;
  201.  
  202.       sprintf (entry, "_argv%d", ind);
  203.       p = getenv (entry);
  204.       if (p != 0)
  205.     argv[ind] = strcpy (malloc (strlen (p) + 1), p);
  206.       else
  207.     argv[ind] = strcpy (malloc (1), "");
  208.  
  209.       ind++;
  210.     }
  211.  
  212.   /* Terminate  */
  213.   argv[__argc] = NULL;
  214.  
  215.   /* Install  */
  216.   __argv = argv;
  217.  
  218.   return 0;
  219. }
  220.  
  221. #endif /* WANT_LONG_COMMANDLINES */
  222.  
  223.  
  224. #ifdef WANT_GLOBBING
  225.  
  226. /* During startup, perform filename globbing of __ARGV.  By Microsoft
  227.    convention, the first letter of each member of __ARGV marks wether
  228.    globbing is to be performed for this specific name.  If it is '\"',
  229.    the string is left alone.  One has to be careful, not to include
  230.    this character in the result.  */
  231.  
  232. int
  233. _cglob (void)
  234. {
  235.   int argc = __argc;
  236.   char **argv = __argv;
  237.   int optind;
  238.  
  239.  
  240.   /* Vector of vectors of globbed filenames, terminated by NULL.
  241.      If GLOB_ARGV[N] == -1, or if *(GLOB_ARGV[N]) == NULL, no
  242.      globbing has been performed and the original __ARGV[N] will
  243.      be used. (Strictly speaking, GLOB_ARGVV[0] is never used,
  244.      but we don't care.) */
  245.  
  246.   char ***glob_argvv = (char ***) malloc (argc * sizeof (char **));
  247.  
  248.   if (glob_argvv == NULL)
  249.     return -1;
  250.  
  251.   __argc = 2;            /* program name and terminating NULL */
  252.  
  253.  
  254.   /* Pass 1: Find out how much storage we need and allocate it.  */
  255.  
  256.   for (optind = 1; optind < argc; optind++)
  257.     {
  258.       /* there's at least one (the original argv[optind]).  */
  259.  
  260.       __argc++;
  261.  
  262.       if (*(argv[optind])++ == '\"')
  263.     {
  264.       /* Don't glob ARGV[OPTIND], but strip the leading quote marker  */
  265.  
  266.       glob_argvv[optind] = (char **) -1;
  267.     }
  268.       else
  269.     {
  270.       char **ptr = glob_argvv[optind]  = glob_filename (argv[optind]);
  271.       size_t cnt = 0;
  272.  
  273.       /* we will ignore errors from glob_filename () and pass the
  274.          unexpanded argument  */
  275.  
  276.       if (ptr != (char **) -1 && *ptr++ != NULL)
  277.         while (*ptr++ != NULL)
  278.           cnt++;
  279.  
  280.       /* Sort the globbed filenames */
  281.  
  282.       if (cnt > 0)
  283.         qsort (glob_argvv[optind], cnt + 1, sizeof (char *), compare);
  284.  
  285.       __argc += cnt;
  286.     }
  287.     }
  288.  
  289.   __argv = (char **) malloc ((__argc + 1) * sizeof (char *));
  290.  
  291.   if (__argv == NULL)
  292.     {
  293.       __argv = argv;        /* failed */
  294.       return -1;
  295.     }
  296.  
  297.  
  298.   /* Pass 2: Build the new commandline.  */
  299.  
  300.   __argc = 1;
  301.   __argv[0] = argv[0];
  302.  
  303.   if (*(__argv[0])++ != '\"')          /* Reformat the program name.  */
  304.     msdos_format_filename (__argv[0]);
  305.  
  306.   for (optind = 1; optind < argc; optind++)
  307.     {
  308.       char **ptr = glob_argvv[optind];
  309.  
  310.       /* Did we perform globbing?  */
  311.  
  312.       if (ptr == (char **) -1 || *ptr == NULL)
  313.     __argv[__argc++] = argv[optind];
  314.       else
  315.     while (*ptr)
  316.       __argv[__argc++] = *ptr++;
  317.     }
  318.  
  319.  
  320.   __argv[__argc] = NULL;        /* terminate */
  321.   free (glob_argvv);            /* cleanup */
  322.  
  323.   return 0;
  324. }
  325.  
  326.  
  327. /* This is for passing strcmp () to qsort ().  */
  328. int
  329. compare (char **s1, char **s2)
  330. {
  331.   return strcmp (*s1, *s2);
  332. }
  333.  
  334. #endif /* WANT_GLOBBING */
  335.  
  336.  
  337. /* Filenames returned by MS-DOS system calls are formatted very ugly:
  338.    all uppercase and backslashes.  Perform some cosmetics.  */
  339.  
  340. char *
  341. msdos_format_filename (char *name)
  342. {
  343.   char *p = name;
  344.   while (*p = (*p == '\\') ? '/' : tolower (*p))
  345.     p++;
  346.   return name;
  347. }
  348.  
  349. #ifdef TEST
  350.  
  351. void
  352. main (int argc, char **argv)
  353. {
  354.   if (*++argv)
  355.     {
  356.       printf ("%s", *argv);
  357.       while (*++argv)
  358.     printf (" %s", *argv);
  359.     }
  360.   exit (0);
  361. }
  362.  
  363. #endif /* TEST */
  364.  
  365. /* 
  366.  * Local Variables:
  367.  * mode:C
  368.  * ChangeLog:ChangeLog
  369.  * compile-command:cl -DSMART_SHELL -DTEST _cwild.c glob msd_dir d:\ms\lib\setgarv -link /noe
  370.  * End:
  371.  */
  372.