Remove trailing white space.
[debian/tar] / src / tcexparg.c
1 /* tcexparg.c - Unix-style command line wildcards for Turbo C 2.0
2
3    This file is in the public domain.
4
5    Compile your main program with -Dmain=_main and link with this file.
6
7    After that, it is just as if the operating system had expanded the
8    arguments, except that they are not sorted.  The program name and all
9    arguments that are expanded from wildcards are lowercased.
10
11    Syntax for wildcards:
12    *            Matches zero or more of any character (except a '.' at
13                 the beginning of a name).
14    ?            Matches any single character.
15    [r3z]        Matches 'r', '3', or 'z'.
16    [a-d]        Matches a single character in the range 'a' through 'd'.
17    [!a-d]       Matches any single character except a character in the
18                 range 'a' through 'd'.
19
20    The period between the filename root and its extension need not be
21    given explicitly.  Thus, the pattern `a*e' will match 'abacus.exe'
22    and 'axyz.e' as well as 'apple'.  Comparisons are not case sensitive.
23
24    Authors:
25    The expargs code is a modification of wildcard expansion code
26    written for Turbo C 1.0 by
27    Richard Hargrove
28    Texas Instruments, Inc.
29    P.O. Box 869305, m/s 8473
30    Plano, Texas 75086
31    214/575-4128
32    and posted to USENET in September, 1987.
33
34    The wild_match code was written by Rich Salz, rsalz@bbn.com,
35    posted to net.sources in November, 1986.
36
37    The code connecting the two is by Mike Slomin, bellcore!lcuxa!mike2,
38    posted to comp.sys.ibm.pc in November, 1988.
39
40    Major performance enhancements and bug fixes, and source cleanup,
41    by David MacKenzie, djm@gnu.ai.mit.edu.  */
42
43 #include <stdio.h>
44 #include <string.h>
45 #include <stdlib.h>
46 #include <dos.h>
47 #include <dir.h>
48
49 /* Number of new arguments to allocate space for at a time.  */
50 #define ARGS_INCREMENT 10
51
52 /* The name this program was run with, for error messages.  */
53 static char *program_name;
54
55 static char **grow_argv (char **new_argv, int new_argc);
56 static void fatal_error (const char *message);
57
58 int wild_match (char *string, char *pattern);
59 char *basename (char *path);
60
61 char **expargs (int *, char **);
62
63 #ifdef main
64 #undef main
65 #endif
66
67 int
68 main (int argc, char **argv, char **envp)
69 {
70   argv = expargs (&argc, argv);
71   return _main (argc, argv, envp);
72 }
73
74 char **
75 expargs (int *pargc, char **argv)
76 {
77   char path[MAXPATH + 1];
78   char **new_argv;
79   struct ffblk block;
80   char *path_base;
81   char *arg_base;
82   int argind;
83   int new_argc;
84   int path_length;
85   int matched;
86
87   program_name = argv[0];
88   if (program_name && *program_name)
89     strlwr (program_name);
90   new_argv = grow_argv (NULL, 0);
91   new_argv[0] = argv[0];
92   new_argc = 1;
93
94   for (argind = 1; argind < *pargc; ++argind)
95     {
96       matched = 0;
97       if (strpbrk (argv[argind], "?*[") != NULL)
98         {
99           strncpy (path, argv[argind], MAXPATH - 3);
100           path_base = basename (path);
101           strcpy (path_base, "*.*");
102           arg_base = argv[argind] + (path_base - path);
103
104           if (!findfirst (path, &block, FA_DIREC))
105             {
106               strlwr (path);
107               do
108                 {
109                   /* Only match "." and ".." explicitly.  */
110                   if (*block.ff_name == '.' && *arg_base != '.')
111                     continue;
112                   path_length = stpcpy (path_base, block.ff_name) - path + 1;
113                   strlwr (path_base);
114                   if (wild_match (path, argv[argind]))
115                     {
116                       matched = 1;
117                       new_argv[new_argc] = (char *) malloc (path_length);
118                       if (new_argv[new_argc] == NULL)
119                         fatal_error ("memory exhausted");
120                       strcpy (new_argv[new_argc++], path);
121                       new_argv = grow_argv (new_argv, new_argc);
122                     }
123               } while (!findnext (&block));
124             }
125         }
126       if (matched == 0)
127         new_argv[new_argc++] = argv[argind];
128       new_argv = grow_argv (new_argv, new_argc);
129     }
130
131   *pargc = new_argc;
132   new_argv[new_argc] = NULL;
133   return &new_argv[0];
134 }
135
136 /* Return a pointer to the last element of PATH.  */
137
138 char *
139 basename (char *path)
140 {
141   char *tail;
142
143   for (tail = path; *path; ++path)
144     if (*path == ':' || *path == '\\')
145       tail = path + 1;
146   return tail;
147 }
148
149 static char **
150 grow_argv (char **new_argv, int new_argc)
151 {
152   if (new_argc % ARGS_INCREMENT == 0)
153     {
154       new_argv = (char **) realloc
155         (new_argv, sizeof (char *) * (new_argc + ARGS_INCREMENT));
156       if (new_argv == NULL)
157         fatal_error ("memory exhausted");
158     }
159   return new_argv;
160 }
161
162 static void
163 fatal_error (const char *message)
164 {
165   putc ('\n', stderr);
166   if (program_name && *program_name)
167     {
168       fputs (program_name, stderr);
169       fputs (": ", stderr);
170     }
171   fputs (message, stderr);
172   putc ('\n', stderr);
173   exit (1);
174 }
175
176 /* Shell-style pattern matching for ?, \, [], and * characters.
177    I'm putting this replacement in the public domain.
178
179    Written by Rich $alz, mirror!rs, Wed Nov 26 19:03:17 EST 1986.  */
180
181 /* The character that inverts a character class; '!' or '^'.  */
182 #define INVERT '!'
183
184 static int star (char *string, char *pattern);
185
186 /* Return nonzero if `string' matches Unix-style wildcard pattern
187    `pattern'; zero if not.  */
188
189 int
190 wild_match (char *string, char *pattern)
191 {
192   int prev;             /* Previous character in character class.  */
193   int matched;          /* If 1, character class has been matched.  */
194   int reverse;          /* If 1, character class is inverted.  */
195
196   for (; *pattern; string++, pattern++)
197     switch (*pattern)
198       {
199       case '\\':
200         /* Literal match with following character; fall through.  */
201         pattern++;
202       default:
203         if (*string != *pattern)
204           return 0;
205         continue;
206       case '?':
207         /* Match anything.  */
208         if (*string == '\0')
209           return 0;
210         continue;
211       case '*':
212         /* Trailing star matches everything.  */
213         return *++pattern ? star (string, pattern) : 1;
214       case '[':
215         /* Check for inverse character class.  */
216         reverse = pattern[1] == INVERT;
217         if (reverse)
218           pattern++;
219         for (prev = 256, matched = 0; *++pattern && *pattern != ']';
220              prev = *pattern)
221           if (*pattern == '-'
222               ? *string <= *++pattern && *string >= prev
223               : *string == *pattern)
224             matched = 1;
225         if (matched == reverse)
226           return 0;
227         continue;
228       }
229
230   return *string == '\0';
231 }
232
233 static int
234 star (char *string, char *pattern)
235 {
236   while (wild_match (string, pattern) == 0)
237     if (*++string == '\0')
238       return 0;
239   return 1;
240 }