home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Professional / OS2PRO194.ISO / os2 / prgramer / rcs / sources / cwild.c < prev    next >
C/C++ Source or Header  |  1990-12-04  |  10KB  |  389 lines

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