xref: /plan9/sys/src/ape/cmd/pax/namelist.c (revision 9a747e4fd48b9f4522c70c07e8f882a15030f964)
1 /* $Source: /u/mark/src/pax/RCS/namelist.c,v $
2  *
3  * $Revision: 1.6 $
4  *
5  * namelist.c - track filenames given as arguments to tar/cpio/pax
6  *
7  * DESCRIPTION
8  *
9  *	Arguments may be regular expressions, therefore all agurments will
10  *	be treated as if they were regular expressions, even if they are
11  *	not.
12  *
13  * AUTHOR
14  *
15  *	Mark H. Colburn, NAPS International (mark@jhereg.mn.org)
16  *
17  * Sponsored by The USENIX Association for public distribution.
18  *
19  * Copyright (c) 1989 Mark H. Colburn.
20  * All rights reserved.
21  *
22  * Redistribution and use in source and binary forms are permitted
23  * provided that the above copyright notice is duplicated in all such
24  * forms and that any documentation, advertising materials, and other
25  * materials related to such distribution and use acknowledge that the
26  * software was developed by Mark H. Colburn and sponsored by The
27  * USENIX Association.
28  *
29  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
30  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
31  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
32  *
33  * $Log:	namelist.c,v $
34  * Revision 1.6  89/02/13  09:14:48  mark
35  * Fixed problem with directory errors
36  *
37  * Revision 1.5  89/02/12  12:14:00  mark
38  * Fixed misspellings
39  *
40  * Revision 1.4  89/02/12  11:25:19  mark
41  * Modifications to compile and link cleanly under USG
42  *
43  * Revision 1.3  89/02/12  10:40:23  mark
44  * Fixed casting problems
45  *
46  * Revision 1.2  89/02/12  10:04:57  mark
47  * 1.2 release fixes
48  *
49  * Revision 1.1  88/12/23  18:02:17  mark
50  * Initial revision
51  *
52  */
53 
54 #ifndef lint
55 static char *ident = "$Id: namelist.c,v 1.6 89/02/13 09:14:48 mark Exp $";
56 static char *copyright = "Copyright (c) 1989 Mark H. Colburn.\nAll rights reserved.\n";
57 #endif /* ! lint */
58 
59 
60 /* Headers */
61 
62 #include "pax.h"
63 
64 
65 /* Type Definitions */
66 
67 /*
68  * Structure for keeping track of filenames and lists thereof.
69  */
70 struct nm_list {
71     struct nm_list *next;
72     short           length;	/* cached strlen(name) */
73     char            found;	/* A matching file has been found */
74     char            firstch;	/* First char is literally matched */
75     char            re;		/* regexp pattern for item */
76     char            name[1];	/* name of file or rexexp */
77 };
78 
79 struct dirinfo {
80     char            dirname[PATH_MAX + 1];	/* name of directory */
81     OFFSET	    where;	/* current location in directory */
82     struct dirinfo *next;
83 };
84 
85 
86 /* Static Variables */
87 
88 static struct dirinfo *stack_head = (struct dirinfo *)NULL;
89 
90 
91 /* Function Prototypes */
92 
93 #ifndef __STDC__
94 
95 static void pushdir();
96 static struct dirinfo *popdir();
97 
98 #else
99 
100 static void pushdir(struct dirinfo *info);
101 static struct dirinfo *popdir(void);
102 
103 #endif
104 
105 
106 /* Internal Identifiers */
107 
108 static struct nm_list *namelast;	/* Points to last name in list */
109 static struct nm_list *namelist;	/* Points to first name in list */
110 
111 
112 /* addname -  add a name to the namelist.
113  *
114  * DESCRIPTION
115  *
116  *	Addname adds the name given to the name list.  Memory for the
117  *	namelist structure is dynamically allocated.  If the space for
118  *	the structure cannot be allocated, then the program will exit
119  *	the an out of memory error message and a non-zero return code
120  *	will be returned to the caller.
121  *
122  * PARAMETERS
123  *
124  *	char *name	- A pointer to the name to add to the list
125  */
126 
127 #ifdef __STDC__
128 
add_name(char * name)129 void add_name(char *name)
130 
131 #else
132 
133 void add_name(name)
134 char           *name;		/* pointer to name */
135 
136 #endif
137 {
138     int             i;		/* Length of string */
139     struct nm_list *p;		/* Current struct pointer */
140 
141     i = strlen(name);
142     p = (struct nm_list *) malloc((unsigned) (i + sizeof(struct nm_list)));
143     if (!p) {
144 	fatal("cannot allocate memory for namelist entry\n");
145     }
146     p->next = (struct nm_list *)NULL;
147     p->length = i;
148     strncpy(p->name, name, i);
149     p->name[i] = '\0';		/* Null term */
150     p->found = 0;
151     p->firstch = isalpha(name[0]);
152     if (strchr(name, '*') || strchr(name, '[') || strchr(name, '?')) {
153         p->re = 1;
154     }
155     if (namelast) {
156 	namelast->next = p;
157     }
158     namelast = p;
159     if (!namelist) {
160 	namelist = p;
161     }
162 }
163 
164 
165 /* name_match - match a name from an archive with a name from the namelist
166  *
167  * DESCRIPTION
168  *
169  *	Name_match attempts to find a name pointed at by p in the namelist.
170  *	If no namelist is available, then all filenames passed in are
171  *	assumed to match the filename criteria.  Name_match knows how to
172  *	match names with regular expressions, etc.
173  *
174  * PARAMETERS
175  *
176  *	char	*p	- the name to match
177  *
178  * RETURNS
179  *
180  *	Returns 1 if the name is in the namelist, or no name list is
181  *	available, otherwise returns 0
182  *
183  */
184 
185 #ifdef __STDC__
186 
name_match(char * p)187 int name_match(char *p)
188 
189 #else
190 
191 int name_match(p)
192 char           *p;
193 
194 #endif
195 {
196     struct nm_list *nlp;
197     int             len;
198 
199     if ((nlp = namelist) == 0) {/* Empty namelist is easy */
200 	return (1);
201     }
202     len = strlen(p);
203     for (; nlp != 0; nlp = nlp->next) {
204 	/* If first chars don't match, quick skip */
205 	if (nlp->firstch && nlp->name[0] != p[0]) {
206 	    continue;
207 	}
208 	/* Regular expressions */
209 	if (nlp->re) {
210 	    if (wildmat(nlp->name, p)) {
211 		nlp->found = 1;	/* Remember it matched */
212 		return (1);	/* We got a match */
213 	    }
214 	    continue;
215 	}
216 	/* Plain Old Strings */
217 	if (nlp->length <= len	/* Archive len >= specified */
218 	    && (p[nlp->length] == '\0' || p[nlp->length] == '/')
219 	    && strncmp(p, nlp->name, nlp->length) == 0) {
220 	    /* Name compare */
221 	    nlp->found = 1;	/* Remember it matched */
222 	    return (1);		/* We got a match */
223 	}
224     }
225     return (0);
226 }
227 
228 
229 /* names_notfound - print names of files in namelist that were not found
230  *
231  * DESCRIPTION
232  *
233  *	Names_notfound scans through the namelist for any files which were
234  *	named, but for which a matching file was not processed by the
235  *	archive.  Each of the files is listed on the standard error.
236  *
237  */
238 
239 #ifdef __STDC__
240 
names_notfound(void)241 void names_notfound(void)
242 
243 #else
244 
245 void names_notfound()
246 
247 #endif
248 {
249     struct nm_list *nlp;
250 
251     for (nlp = namelist; nlp != 0; nlp = nlp->next) {
252 	if (!nlp->found) {
253 	    fprintf(stderr, "%s: %s not found in archive\n",
254 	            myname, nlp->name);
255 	}
256 	free(nlp);
257     }
258     namelist = (struct nm_list *)NULL;
259     namelast = (struct nm_list *)NULL;
260 }
261 
262 
263 /* name_init - set up to gather file names
264  *
265  * DESCRIPTION
266  *
267  *	Name_init sets up the namelist pointers so that we may access the
268  *	command line arguments.  At least the first item of the command
269  *	line (argv[0]) is assumed to be stripped off, prior to the
270  *	name_init call.
271  *
272  * PARAMETERS
273  *
274  *	int	argc	- number of items in argc
275  *	char	**argv	- pointer to the command line arguments
276  */
277 
278 #ifdef __STDC__
279 
name_init(int argc,char ** argv)280 void name_init(int argc, char **argv)
281 
282 #else
283 
284 void name_init(argc, argv)
285 int             argc;
286 char          **argv;
287 
288 #endif
289 {
290     /* Get file names from argv, after options. */
291     n_argc = argc;
292     n_argv = argv;
293 }
294 
295 
296 /* name_next - get the next name from argv or the name file.
297  *
298  * DESCRIPTION
299  *
300  *	Name next finds the next name which is to be processed in the
301  *	archive.  If the named file is a directory, then the directory
302  *	is recursively traversed for additional file names.  Directory
303  *	names and locations within the directory are kept track of by
304  *	using a directory stack.  See the pushdir/popdir function for
305  *	more details.
306  *
307  * 	The names come from argv, after options or from the standard input.
308  *
309  * PARAMETERS
310  *
311  *	name - a pointer to a buffer of at least MAX_PATH + 1 bytes long;
312  *	statbuf - a pointer to a stat structure
313  *
314  * RETURNS
315  *
316  *	Returns -1 if there are no names left, (e.g. EOF), otherwise returns
317  *	0
318  */
319 
320 #ifdef __STDC__
321 
name_next(char * name,Stat * statbuf)322 int name_next(char *name, Stat *statbuf)
323 
324 #else
325 
326 int name_next(name, statbuf)
327 char           *name;
328 Stat           *statbuf;
329 
330 #endif
331 {
332     int             err = -1;
333     static int      in_subdir = 0;
334     static DIR     *dirp;
335     struct dirent  *d;
336     static struct dirinfo *curr_dir;
337     int			len;
338 
339     do {
340 	if (names_from_stdin) {
341 	    if (lineget(stdin, name) < 0) {
342 		return (-1);
343 	    }
344 	    if (nameopt(name) < 0) {
345 		continue;
346 	    }
347 	} else {
348 	    if (in_subdir) {
349 		if ((d = readdir(dirp)) != (struct dirent *)NULL) {
350 		    /* Skip . and .. */
351 		    if (strcmp(d->d_name, ".") == 0 ||
352 		        strcmp(d->d_name, "..") == 0) {
353 			    continue;
354 		    }
355 		    if (strlen(d->d_name) +
356 			strlen(curr_dir->dirname) >= PATH_MAX) {
357 			warn("name too long", d->d_name);
358 			continue;
359 		    }
360 		    strcpy(name, curr_dir->dirname);
361 		    strcat(name, d->d_name);
362 		} else {
363 		    closedir(dirp);
364 		    in_subdir--;
365 		    curr_dir = popdir();
366 		    if (in_subdir) {
367 			errno = 0;
368 			if ((dirp=opendir(curr_dir->dirname)) == (DIR *)NULL) {
369 			    warn(curr_dir->dirname, "error opening directory (1)");
370 			    in_subdir--;
371 			}
372 			seekdir(dirp, curr_dir->where);
373 		    }
374 		    continue;
375 		}
376 	    } else if (optind >= n_argc) {
377 		return (-1);
378 	    } else {
379 		strcpy(name, n_argv[optind++]);
380 	    }
381 	}
382 	if ((err = LSTAT(name, statbuf)) < 0) {
383 	    warn(name, strerror());
384 	    continue;
385 	}
386 	if (!names_from_stdin && (statbuf->sb_mode & S_IFMT) == S_IFDIR) {
387 	    if (in_subdir) {
388 		curr_dir->where = telldir(dirp);
389 		pushdir(curr_dir);
390 		closedir(dirp);
391 	    }
392 	    in_subdir++;
393 
394 	    /* Build new prototype name */
395 	    if ((curr_dir = (struct dirinfo *) mem_get(sizeof(struct dirinfo)))
396 			  == (struct dirinfo *)NULL) {
397 		exit(2);
398 	    }
399 	    strcpy(curr_dir->dirname, name);
400 	    len = strlen(curr_dir->dirname);
401 	    while (len >= 1 && curr_dir->dirname[len - 1] == '/') {
402 		len--;		/* Delete trailing slashes */
403 	    }
404 	    curr_dir->dirname[len++] = '/';	/* Now add exactly one back */
405 	    curr_dir->dirname[len] = '\0';/* Make sure null-terminated */
406             curr_dir->where = 0;
407 
408             errno = 0;
409             do {
410                 if ((dirp = opendir(curr_dir->dirname)) == (DIR *)NULL) {
411                      warn(curr_dir->dirname, "error opening directory (2)");
412                      if (in_subdir > 1) {
413                           curr_dir = popdir();
414                      }
415                      in_subdir--;
416                      err = -1;
417                      continue;
418                 } else {
419                      seekdir(dirp, curr_dir->where);
420 		}
421 	    } while (in_subdir && (! dirp));
422 	}
423     } while (err < 0);
424     return (0);
425 }
426 
427 
428 /* name_gather - gather names in a list for scanning.
429  *
430  * DESCRIPTION
431  *
432  *	Name_gather takes names from the command line and adds them to
433  *	the name list.
434  *
435  * FIXME
436  *
437  * 	We could hash the names if we really care about speed here.
438  */
439 
440 #ifdef __STDC__
441 
name_gather(void)442 void name_gather(void)
443 
444 #else
445 
446 void name_gather()
447 
448 #endif
449 {
450      while (optind < n_argc) {
451 	 add_name(n_argv[optind++]);
452      }
453 }
454 
455 
456 /* pushdir - pushes a directory name on the directory stack
457  *
458  * DESCRIPTION
459  *
460  *	The pushdir function puses the directory structure which is pointed
461  *	to by "info" onto a stack for later processing.  The information
462  *	may be retrieved later with a call to popdir().
463  *
464  * PARAMETERS
465  *
466  *	dirinfo	*info	- pointer to directory structure to save
467  */
468 
469 #ifdef __STDC__
470 
pushdir(struct dirinfo * info)471 static void pushdir(struct dirinfo *info)
472 
473 #else
474 
475 static void pushdir(info)
476 struct dirinfo	*info;
477 
478 #endif
479 {
480     if  (stack_head == (struct dirinfo *)NULL) {
481 	stack_head = info;
482 	stack_head->next = (struct dirinfo *)NULL;
483     } else {
484 	info->next = stack_head;
485 	stack_head = info;
486     }
487 }
488 
489 
490 /* popdir - pop a directory structure off the directory stack.
491  *
492  * DESCRIPTION
493  *
494  *	The popdir function pops the most recently pushed directory
495  *	structure off of the directory stack and returns it to the calling
496  *	function.
497  *
498  * RETURNS
499  *
500  *	Returns a pointer to the most recently pushed directory structure
501  *	or NULL if the stack is empty.
502  */
503 
504 #ifdef __STDC__
505 
popdir(void)506 static struct dirinfo *popdir(void)
507 
508 #else
509 
510 static struct dirinfo *popdir()
511 
512 #endif
513 {
514     struct dirinfo	*tmp;
515 
516     if (stack_head == (struct dirinfo *)NULL) {
517 	return((struct dirinfo *)NULL);
518     } else {
519 	tmp = stack_head;
520 	stack_head = stack_head->next;
521     }
522     return(tmp);
523 }
524