]> git.gag.com Git - debian/tar/blob - src/tcexparg.c
f319dd9debc11a6fbc624f862dcff623308775b0
[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@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                 }
124               while (!findnext (&block));
125             }
126         }
127       if (matched == 0)
128         new_argv[new_argc++] = argv[argind];
129       new_argv = grow_argv (new_argv, new_argc);
130     }
131
132   *pargc = new_argc;
133   new_argv[new_argc] = NULL;
134   return &new_argv[0];
135 }
136
137 /* Return a pointer to the last element of PATH.  */
138
139 char *
140 basename (char *path)
141 {
142   char *tail;
143
144   for (tail = path; *path; ++path)
145     if (*path == ':' || *path == '\\')
146       tail = path + 1;
147   return tail;
148 }
149
150 static char **
151 grow_argv (char **new_argv, int new_argc)
152 {
153   if (new_argc % ARGS_INCREMENT == 0)
154     {
155       new_argv = (char **) realloc
156       (new_argv, sizeof (char *) * (new_argc + ARGS_INCREMENT));
157       if (new_argv == NULL)
158         fatal_error ("memory exhausted");
159     }
160   return new_argv;
161 }
162
163 static void
164 fatal_error (const char *message)
165 {
166   putc ('\n', stderr);
167   if (program_name && *program_name)
168     {
169       fputs (program_name, stderr);
170       fputs (": ", stderr);
171     }
172   fputs (message, stderr);
173   putc ('\n', stderr);
174   exit (1);
175 }
176
177 /* Shell-style pattern matching for ?, \, [], and * characters.
178    I'm putting this replacement in the public domain.
179
180    Written by Rich $alz, mirror!rs, Wed Nov 26 19:03:17 EST 1986. */
181
182 /* The character that inverts a character class; '!' or '^'. */
183 #define INVERT '!'
184
185 static int star (char *string, char *pattern);
186
187 /* Return nonzero if `string' matches Unix-style wildcard pattern
188    `pattern'; zero if not. */
189
190 int
191 wild_match (char *string, char *pattern)
192 {
193   int prev;                     /* Previous character in character class. */
194   int matched;                  /* If 1, character class has been matched. */
195   int reverse;                  /* If 1, character class is inverted. */
196
197   for (; *pattern; string++, pattern++)
198     switch (*pattern)
199       {
200       case '\\':
201         /* Literal match with following character; fall through. */
202         pattern++;
203       default:
204         if (*string != *pattern)
205           return 0;
206         continue;
207       case '?':
208         /* Match anything. */
209         if (*string == '\0')
210           return 0;
211         continue;
212       case '*':
213         /* Trailing star matches everything. */
214         return *++pattern ? star (string, pattern) : 1;
215       case '[':
216         /* Check for inverse character class. */
217         reverse = pattern[1] == INVERT;
218         if (reverse)
219           pattern++;
220         for (prev = 256, matched = 0; *++pattern && *pattern != ']';
221              prev = *pattern)
222           if (*pattern == '-'
223               ? *string <= *++pattern && *string >= prev
224               : *string == *pattern)
225             matched = 1;
226         if (matched == reverse)
227           return 0;
228         continue;
229       }
230
231   return *string == '\0';
232 }
233
234 static int
235 star (char *string, char *pattern)
236 {
237   while (wild_match (string, pattern) == 0)
238     if (*++string == '\0')
239       return 0;
240   return 1;
241 }