summaryrefslogtreecommitdiffstats
path: root/contrib/cpio/tcexparg.c
blob: c5d88f069bf5ad39258079b455bc8272aeebe5f5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
/* tcexparg.c - Unix-style command line wildcards for Turbo C 2.0

   This file is in the public domain.

   Compile your main program with -Dmain=_main and link with this file.

   After that, it is just as if the operating system had expanded the
   arguments, except that they are not sorted.  The program name and all
   arguments that are expanded from wildcards are lowercased.

   Syntax for wildcards:
   *		Matches zero or more of any character (except a '.' at
		the beginning of a name).
   ?		Matches any single character.
   [r3z]	Matches 'r', '3', or 'z'.
   [a-d]	Matches a single character in the range 'a' through 'd'.
   [!a-d]	Matches any single character except a character in the
		range 'a' through 'd'.

   The period between the filename root and its extension need not be
   given explicitly.  Thus, the pattern `a*e' will match 'abacus.exe'
   and 'axyz.e' as well as 'apple'.  Comparisons are not case sensitive.

   Authors:
   The expargs code is a modification of wildcard expansion code
   written for Turbo C 1.0 by
   Richard Hargrove
   Texas Instruments, Inc.
   P.O. Box 869305, m/s 8473
   Plano, Texas 75086
   214/575-4128
   and posted to USENET in September, 1987.

   The wild_match code was written by Rich Salz, rsalz@bbn.com,
   posted to net.sources in November, 1986.

   The code connecting the two is by Mike Slomin, bellcore!lcuxa!mike2,
   posted to comp.sys.ibm.pc in November, 1988.

   Major performance enhancements and bug fixes, and source cleanup,
   by David MacKenzie, djm@gnu.ai.mit.edu.  */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <dos.h>
#include <dir.h>

/* Number of new arguments to allocate space for at a time.  */
#define ARGS_INCREMENT 10

/* The name this program was run with, for error messages.  */
static char *program_name;

static char **grow_argv (char **new_argv, int new_argc);
static void fatal_error (const char *message);

int wild_match (char *string, char *pattern);
char *basename (char *path);

char **expargs (int *, char **);

#ifdef main
#undef main
#endif

int
main (int argc, char **argv, char **envp)
{
  argv = expargs (&argc, argv);
  return _main (argc, argv, envp);
}

char **
expargs (int *pargc, char **argv)
{
  char path[MAXPATH + 1];
  char **new_argv;
  struct ffblk block;
  char *path_base;
  char *arg_base;
  int argind;
  int new_argc;
  int path_length;
  int matched;

  program_name = argv[0];
  if (program_name && *program_name)
    strlwr (program_name);
  new_argv = grow_argv (NULL, 0);
  new_argv[0] = argv[0];
  new_argc = 1;

  for (argind = 1; argind < *pargc; ++argind)
    {
      matched = 0;
      if (strpbrk (argv[argind], "?*[") != NULL)
	{
	  strncpy (path, argv[argind], MAXPATH - 3);
	  path_base = basename (path);
	  strcpy (path_base, "*.*");
	  arg_base = argv[argind] + (path_base - path);

	  if (!findfirst (path, &block, FA_DIREC))
	    {
	      strlwr (path);
	      do
		{
		  /* Only match "." and ".." explicitly.  */
		  if (*block.ff_name == '.' && *arg_base != '.')
		    continue;
		  path_length = stpcpy (path_base, block.ff_name) - path + 1;
		  strlwr (path_base);
		  if (wild_match (path, argv[argind]))
		    {
		      matched = 1;
		      new_argv[new_argc] = (char *) malloc (path_length);
		      if (new_argv[new_argc] == NULL)
			fatal_error ("memory exhausted");
		      strcpy (new_argv[new_argc++], path);
		      new_argv = grow_argv (new_argv, new_argc);
		    }
	      } while (!findnext (&block));
	    }
	}
      if (matched == 0)
	new_argv[new_argc++] = argv[argind];
      new_argv = grow_argv (new_argv, new_argc);
    }

  *pargc = new_argc;
  new_argv[new_argc] = NULL;
  return &new_argv[0];
}

/* Return a pointer to the last element of PATH.  */

char *
basename (char *path)
{
  char *tail;

  for (tail = path; *path; ++path)
    if (*path == ':' || *path == '\\')
      tail = path + 1;
  return tail;
}

static char **
grow_argv (char **new_argv, int new_argc)
{
  if (new_argc % ARGS_INCREMENT == 0)
    {
      new_argv = (char **) realloc
	(new_argv, sizeof (char *) * (new_argc + ARGS_INCREMENT));
      if (new_argv == NULL)
	fatal_error ("memory exhausted");
    }
  return new_argv;
}

static void
fatal_error (const char *message)
{
  putc ('\n', stderr);
  if (program_name && *program_name)
    {
      fputs (program_name, stderr);
      fputs (": ", stderr);
    }
  fputs (message, stderr);
  putc ('\n', stderr);
  exit (1);
}

/* Shell-style pattern matching for ?, \, [], and * characters.
   I'm putting this replacement in the public domain.

   Written by Rich $alz, mirror!rs, Wed Nov 26 19:03:17 EST 1986.  */

/* The character that inverts a character class; '!' or '^'.  */
#define INVERT '!'

static int star (char *string, char *pattern);

/* Return nonzero if `string' matches Unix-style wildcard pattern
   `pattern'; zero if not.  */

int
wild_match (char *string, char *pattern)
{
  int prev;		/* Previous character in character class.  */
  int matched;		/* If 1, character class has been matched.  */
  int reverse;		/* If 1, character class is inverted.  */

  for (; *pattern; string++, pattern++)
    switch (*pattern)
      {
      case '\\':
	/* Literal match with following character; fall through.  */
	pattern++;
      default:
	if (*string != *pattern)
	  return 0;
	continue;
      case '?':
	/* Match anything.  */
	if (*string == '\0')
	  return 0;
	continue;
      case '*':
	/* Trailing star matches everything.  */
	return *++pattern ? star (string, pattern) : 1;
      case '[':
	/* Check for inverse character class.  */
	reverse = pattern[1] == INVERT;
	if (reverse)
	  pattern++;
	for (prev = 256, matched = 0; *++pattern && *pattern != ']';
	     prev = *pattern)
	  if (*pattern == '-'
	      ? *string <= *++pattern && *string >= prev
	      : *string == *pattern)
	    matched = 1;
	if (matched == reverse)
	  return 0;
	continue;
      }

  return *string == '\0';
}

static int
star (char *string, char *pattern)
{
  while (wild_match (string, pattern) == 0)
    if (*++string == '\0')
      return 0;
  return 1;
}
OpenPOWER on IntegriCloud