home *** CD-ROM | disk | FTP | other *** search
- /* _cwild.c - (reasonable, i.e. **IX style) wildcard expansion and
- passing of long commandlines for MSC
- Copyright (C) 1990 by Thorsten Ohl, td12@ddagsi3.bitnet
-
- 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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
-
- $Header: e:/gnu/lib/RCS/_cwild.c 1.0 90/09/11 01:55:11 tho Exp $
- */
-
-
- /* One of the major shortcomings of MS-DOS is the inability to pass long
- commandlines. Commandlines are restricted to 126 characters in length
- and may not contain newlines, etc.
-
- Both of these restrictions make it impossible to put reasonably sized
- `awk', `sed', `perl', etc. scripts into shell scripts. Also it's
- impossible to put reasonably sized shell script fragments into
- makefiles.
-
- One way to overcome *some* of these problems is to let the user
- commands perform their own globbing. The second (more satisfactory)
- way is to invent a new scheme for argument passing.
-
- One approach that presents itself, is to pass the arguments via the
- environment. This is the approach adopted here. If this code is
- compiled with `-DWANT_LONG_COMMANDLINES' (which is implied by
- `-DSMART_SHELL_ONLY' and `-DSMART_SHELL'), the startup code looks for
- "_argc" in the environment. If it is found, it is used as a argument
- count and the commandline is taken from "_argv0", "_argv1", ... The
- MS-DOS commandline is *ignored* in this case! This is of course only
- useful if your Shell or Make supports this feature. (GNU make will do
- in the next version.)
-
- The other part of the code uses the GNU globbing library to perform
- globbing of the MS-DOS commandline in a **IX compatible way.
-
- This code has been developped to be used with the startup code of
- Microsoft C. Compile with one of SMART_SHELL_ONLY, SMART_SHELL, or
- DUMB_SHELL_ONLY. Link with the `setargv.obj' that came with the
- compiler to ensure that _cwild () is called during startup.
-
- IMPORTANT: Since we want this appraoch to become compiler independent,
- feedback in form of patches for other compilers (Turbo C, Zortech,
- etc.) is *greatly* appreciated. */
-
-
- /* NOTE: Even though this approach is almost evident, I do *not* put it
- in the public domain. I CLAIM A COPYRIGHT BOTH ON THIS CODE AND THE
- UNDERLYING IDEAS. But you are welcome to redistribute, use and
- improve it under the terms of the GNU GENERAL PUBLIC LICENSE.
-
- This seems appropriate in the days of aggressive copyright lawsuits
- to prevent someone from preventing YOU to use it. */
-
-
- /* Configuration Section: */
-
- /* Define this if you are exclusively using Bash, GNU make,
- or other winning utilities which take the load of expanding
- the commandline from your program and know how to pass
- *long* commandlines. */
-
- #ifdef SMART_SHELL_ONLY
-
- #ifndef WANT_LONG_COMMANDLINES
- #define WANT_LONG_COMMANDLINES
- #endif
-
- #ifdef WANT_GLOBBING
- #undef WANT_GLOBBING
- #endif
-
- #endif /* SMART_SHELL_ONLY */
-
-
- /* Define this if you are always use loosing Shells and Makes, that don't
- know how to pass long commandlines. And if your program has to expand
- the commandline itself. */
-
- #ifdef DUMB_SHELL_ONLY
-
- #ifdef WANT_LONG_COMMANDLINES
- #undef WANT_LONG_COMMANDLINES
- #endif
-
- #ifndef WANT_GLOBBING
- #define WANT_GLOBBING
- #endif
-
- #endif /* not DUMB_SHELL_ONLY */
-
-
- #if !defined(SMART_SHELL) && !defined(SMART_SHELL_ONLY) \
- && !defined(DUMB_SHELL_ONLY)
- #define SMART_SHELL
- #endif
-
- /* Define this if your program can expect long commandlines, but should
- also be able to do some globbing. */
-
- #ifdef SMART_SHELL
-
- #ifndef WANT_LONG_COMMANDLINES
- #define WANT_LONG_COMMANDLINES
- #endif
-
- #ifndef WANT_GLOBBING
- #define WANT_GLOBBING
- #endif
-
- #endif /* SMART_SHELL */
-
-
- /* This code relies on the standard GNU globbing library, which does
- all the hard work. */
-
- /* This wildcard expansion module is tailored for use with the Microsoft
- C startup code and uses their conventions. It will probably not work
- with the libraries of other compilers. */
-
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <ctype.h>
-
-
- #ifdef WANT_LONG_COMMANDLINES
- static int _cenv (void);
- #define LONG_ARGS_PRESENT (getenv ("_argc") != NULL)
- #else
- #define _cenv() 1
- #define LONG_ARGS_PRESENT 0
- #endif
-
-
- #ifdef WANT_GLOBBING
- #include <gnulib.h>
- static int compare (char **s1, char **s2);
- static int _cglob (void);
- #else
- #define _cglob() 1
- #endif
-
- int _cwild (void);
-
-
-
- /* The external argument vector: */
-
- extern int __argc;
- extern char ** __argv;
-
-
- /* This is the main entry point, decide whether we have a smart shell
- or have to do the globbing ourselves. */
-
- int
- _cwild (void)
- {
- return LONG_ARGS_PRESENT ? _cenv () : _cglob ();
- }
-
-
- #ifdef WANT_LONG_COMMANDLINES
-
- /* We have been called by a smart shell, get arguments from the
- environment entries "_argc", "_argv0", "_argv1", ... */
-
- int
- _cenv (void)
- {
- int ind = 0;
- char **argv;
-
- /* We know that "_argc" is there! */
-
- __argc = atoi (getenv ("_argc"));
- if (__argc == 0)
- return -1;
-
- argv = (char **) malloc ((__argc + 1) * sizeof (char *));
- if (argv == NULL)
- return -1;
-
- while (ind < __argc)
- {
- char entry[10];
- char *p;
-
- sprintf (entry, "_argv%d", ind);
- p = getenv (entry);
- if (p != 0)
- argv[ind] = strcpy (malloc (strlen (p) + 1), p);
- else
- argv[ind] = strcpy (malloc (1), "");
-
- ind++;
- }
-
- /* Terminate */
- argv[__argc] = NULL;
-
- /* Install */
- __argv = argv;
-
- return 0;
- }
-
- #endif /* WANT_LONG_COMMANDLINES */
-
-
- #ifdef WANT_GLOBBING
-
- /* During startup, perform filename globbing of __ARGV. By Microsoft
- convention, the first letter of each member of __ARGV marks wether
- globbing is to be performed for this specific name. If it is '\"',
- the string is left alone. One has to be careful, not to include
- this character in the result. */
-
- int
- _cglob (void)
- {
- int argc = __argc;
- char **argv = __argv;
- int optind;
-
-
- /* Vector of vectors of globbed filenames, terminated by NULL.
- If GLOB_ARGV[N] == -1, or if *(GLOB_ARGV[N]) == NULL, no
- globbing has been performed and the original __ARGV[N] will
- be used. (Strictly speaking, GLOB_ARGVV[0] is never used,
- but we don't care.) */
-
- char ***glob_argvv = (char ***) malloc (argc * sizeof (char **));
-
- if (glob_argvv == NULL)
- return -1;
-
- __argc = 2; /* program name and terminating NULL */
-
-
- /* Pass 1: Find out how much storage we need and allocate it. */
-
- for (optind = 1; optind < argc; optind++)
- {
- /* there's at least one (the original argv[optind]). */
-
- __argc++;
-
- if (*(argv[optind])++ == '\"')
- {
- /* Don't glob ARGV[OPTIND], but strip the leading quote marker */
-
- glob_argvv[optind] = (char **) -1;
- }
- else
- {
- char **ptr = glob_argvv[optind] = glob_filename (argv[optind]);
- size_t cnt = 0;
-
- /* we will ignore errors from glob_filename () and pass the
- unexpanded argument */
-
- if (ptr != (char **) -1 && *ptr++ != NULL)
- while (*ptr++ != NULL)
- cnt++;
-
- /* Sort the globbed filenames */
-
- if (cnt > 0)
- qsort (glob_argvv[optind], cnt + 1, sizeof (char *), compare);
-
- __argc += cnt;
- }
- }
-
- __argv = (char **) malloc ((__argc + 1) * sizeof (char *));
-
- if (__argv == NULL)
- {
- __argv = argv; /* failed */
- return -1;
- }
-
-
- /* Pass 2: Build the new commandline. */
-
- __argc = 1;
- __argv[0] = argv[0];
-
- if (*(__argv[0])++ != '\"') /* Reformat the program name. */
- msdos_format_filename (__argv[0]);
-
- for (optind = 1; optind < argc; optind++)
- {
- char **ptr = glob_argvv[optind];
-
- /* Did we perform globbing? */
-
- if (ptr == (char **) -1 || *ptr == NULL)
- __argv[__argc++] = argv[optind];
- else
- while (*ptr)
- __argv[__argc++] = *ptr++;
- }
-
-
- __argv[__argc] = NULL; /* terminate */
- free (glob_argvv); /* cleanup */
-
- return 0;
- }
-
-
- /* This is for passing strcmp () to qsort (). */
- int
- compare (char **s1, char **s2)
- {
- return strcmp (*s1, *s2);
- }
-
- #endif /* WANT_GLOBBING */
-
-
- /* Filenames returned by MS-DOS system calls are formatted very ugly:
- all uppercase and backslashes. Perform some cosmetics. */
-
- char *
- msdos_format_filename (char *name)
- {
- char *p = name;
- while (*p = (*p == '\\') ? '/' : tolower (*p))
- p++;
- return name;
- }
-
- #ifdef TEST
-
- void
- main (int argc, char **argv)
- {
- if (*++argv)
- {
- printf ("%s", *argv);
- while (*++argv)
- printf (" %s", *argv);
- }
- exit (0);
- }
-
- #endif /* TEST */
-
- /*
- * Local Variables:
- * mode:C
- * ChangeLog:ChangeLog
- * compile-command:cl -DSMART_SHELL -DTEST _cwild.c glob msd_dir d:\ms\lib\setgarv -link /noe
- * End:
- */
-