/* $Source: /u/mark/src/pax/RCS/namelist.c,v $ * * $Revision: 1.6 $ * * namelist.c - track filenames given as arguments to tar/cpio/pax * * DESCRIPTION * * Arguments may be regular expressions, therefore all agurments will * be treated as if they were regular expressions, even if they are * not. * * AUTHOR * * Mark H. Colburn, NAPS International (mark@jhereg.mn.org) * * Sponsored by The USENIX Association for public distribution. * * Copyright (c) 1989 Mark H. Colburn. * All rights reserved. * * Redistribution and use in source and binary forms are permitted * provided that the above copyright notice is duplicated in all such * forms and that any documentation, advertising materials, and other * materials related to such distribution and use acknowledge that the * software was developed by Mark H. Colburn and sponsored by The * USENIX Association. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. * * $Log: namelist.c,v $ * Revision 1.6 89/02/13 09:14:48 mark * Fixed problem with directory errors * * Revision 1.5 89/02/12 12:14:00 mark * Fixed misspellings * * Revision 1.4 89/02/12 11:25:19 mark * Modifications to compile and link cleanly under USG * * Revision 1.3 89/02/12 10:40:23 mark * Fixed casting problems * * Revision 1.2 89/02/12 10:04:57 mark * 1.2 release fixes * * Revision 1.1 88/12/23 18:02:17 mark * Initial revision * */ #ifndef lint static char *ident = "$Id: namelist.c,v 1.6 89/02/13 09:14:48 mark Exp $"; static char *copyright = "Copyright (c) 1989 Mark H. Colburn.\nAll rights reserved.\n"; #endif /* ! lint */ /* Headers */ #include "pax.h" /* Type Definitions */ /* * Structure for keeping track of filenames and lists thereof. */ struct nm_list { struct nm_list *next; short length; /* cached strlen(name) */ char found; /* A matching file has been found */ char firstch; /* First char is literally matched */ char re; /* regexp pattern for item */ char name[1]; /* name of file or rexexp */ }; struct dirinfo { char dirname[PATH_MAX + 1]; /* name of directory */ OFFSET where; /* current location in directory */ struct dirinfo *next; }; /* Static Variables */ static struct dirinfo *stack_head = (struct dirinfo *)NULL; /* Function Prototypes */ #ifndef __STDC__ static void pushdir(); static struct dirinfo *popdir(); #else static void pushdir(struct dirinfo *info); static struct dirinfo *popdir(void); #endif /* Internal Identifiers */ static struct nm_list *namelast; /* Points to last name in list */ static struct nm_list *namelist; /* Points to first name in list */ /* addname - add a name to the namelist. * * DESCRIPTION * * Addname adds the name given to the name list. Memory for the * namelist structure is dynamically allocated. If the space for * the structure cannot be allocated, then the program will exit * the an out of memory error message and a non-zero return code * will be returned to the caller. * * PARAMETERS * * char *name - A pointer to the name to add to the list */ #ifdef __STDC__ void add_name(char *name) #else void add_name(name) char *name; /* pointer to name */ #endif { int i; /* Length of string */ struct nm_list *p; /* Current struct pointer */ i = strlen(name); p = (struct nm_list *) malloc((unsigned) (i + sizeof(struct nm_list))); if (!p) { fatal("cannot allocate memory for namelist entry\n"); } p->next = (struct nm_list *)NULL; p->length = i; strncpy(p->name, name, i); p->name[i] = '\0'; /* Null term */ p->found = 0; p->firstch = isalpha(name[0]); if (strchr(name, '*') || strchr(name, '[') || strchr(name, '?')) { p->re = 1; } if (namelast) { namelast->next = p; } namelast = p; if (!namelist) { namelist = p; } } /* name_match - match a name from an archive with a name from the namelist * * DESCRIPTION * * Name_match attempts to find a name pointed at by p in the namelist. * If no namelist is available, then all filenames passed in are * assumed to match the filename criteria. Name_match knows how to * match names with regular expressions, etc. * * PARAMETERS * * char *p - the name to match * * RETURNS * * Returns 1 if the name is in the namelist, or no name list is * available, otherwise returns 0 * */ #ifdef __STDC__ int name_match(char *p) #else int name_match(p) char *p; #endif { struct nm_list *nlp; int len; if ((nlp = namelist) == 0) {/* Empty namelist is easy */ return (1); } len = strlen(p); for (; nlp != 0; nlp = nlp->next) { /* If first chars don't match, quick skip */ if (nlp->firstch && nlp->name[0] != p[0]) { continue; } /* Regular expressions */ if (nlp->re) { if (wildmat(nlp->name, p)) { nlp->found = 1; /* Remember it matched */ return (1); /* We got a match */ } continue; } /* Plain Old Strings */ if (nlp->length <= len /* Archive len >= specified */ && (p[nlp->length] == '\0' || p[nlp->length] == '/') && strncmp(p, nlp->name, nlp->length) == 0) { /* Name compare */ nlp->found = 1; /* Remember it matched */ return (1); /* We got a match */ } } return (0); } /* names_notfound - print names of files in namelist that were not found * * DESCRIPTION * * Names_notfound scans through the namelist for any files which were * named, but for which a matching file was not processed by the * archive. Each of the files is listed on the standard error. * */ #ifdef __STDC__ void names_notfound(void) #else void names_notfound() #endif { struct nm_list *nlp; for (nlp = namelist; nlp != 0; nlp = nlp->next) { if (!nlp->found) { fprintf(stderr, "%s: %s not found in archive\n", myname, nlp->name); } free(nlp); } namelist = (struct nm_list *)NULL; namelast = (struct nm_list *)NULL; } /* name_init - set up to gather file names * * DESCRIPTION * * Name_init sets up the namelist pointers so that we may access the * command line arguments. At least the first item of the command * line (argv[0]) is assumed to be stripped off, prior to the * name_init call. * * PARAMETERS * * int argc - number of items in argc * char **argv - pointer to the command line arguments */ #ifdef __STDC__ void name_init(int argc, char **argv) #else void name_init(argc, argv) int argc; char **argv; #endif { /* Get file names from argv, after options. */ n_argc = argc; n_argv = argv; } /* name_next - get the next name from argv or the name file. * * DESCRIPTION * * Name next finds the next name which is to be processed in the * archive. If the named file is a directory, then the directory * is recursively traversed for additional file names. Directory * names and locations within the directory are kept track of by * using a directory stack. See the pushdir/popdir function for * more details. * * The names come from argv, after options or from the standard input. * * PARAMETERS * * name - a pointer to a buffer of at least MAX_PATH + 1 bytes long; * statbuf - a pointer to a stat structure * * RETURNS * * Returns -1 if there are no names left, (e.g. EOF), otherwise returns * 0 */ #ifdef __STDC__ int name_next(char *name, Stat *statbuf) #else int name_next(name, statbuf) char *name; Stat *statbuf; #endif { int err = -1; static int in_subdir = 0; static DIR *dirp; struct dirent *d; static struct dirinfo *curr_dir; int len; do { if (names_from_stdin) { if (lineget(stdin, name) < 0) { return (-1); } if (nameopt(name) < 0) { continue; } } else { if (in_subdir) { if ((d = readdir(dirp)) != (struct dirent *)NULL) { /* Skip . and .. */ if (strcmp(d->d_name, ".") == 0 || strcmp(d->d_name, "..") == 0) { continue; } if (strlen(d->d_name) + strlen(curr_dir->dirname) >= PATH_MAX) { warn("name too long", d->d_name); continue; } strcpy(name, curr_dir->dirname); strcat(name, d->d_name); } else { closedir(dirp); in_subdir--; curr_dir = popdir(); if (in_subdir) { errno = 0; if ((dirp=opendir(curr_dir->dirname)) == (DIR *)NULL) { warn(curr_dir->dirname, "error opening directory (1)"); in_subdir--; } seekdir(dirp, curr_dir->where); } continue; } } else if (optind >= n_argc) { return (-1); } else { strcpy(name, n_argv[optind++]); } } if ((err = LSTAT(name, statbuf)) < 0) { warn(name, strerror()); continue; } if (!names_from_stdin && (statbuf->sb_mode & S_IFMT) == S_IFDIR) { if (in_subdir) { curr_dir->where = telldir(dirp); pushdir(curr_dir); closedir(dirp); } in_subdir++; /* Build new prototype name */ if ((curr_dir = (struct dirinfo *) mem_get(sizeof(struct dirinfo))) == (struct dirinfo *)NULL) { exit(2); } strcpy(curr_dir->dirname, name); len = strlen(curr_dir->dirname); while (len >= 1 && curr_dir->dirname[len - 1] == '/') { len--; /* Delete trailing slashes */ } curr_dir->dirname[len++] = '/'; /* Now add exactly one back */ curr_dir->dirname[len] = '\0';/* Make sure null-terminated */ curr_dir->where = 0; errno = 0; do { if ((dirp = opendir(curr_dir->dirname)) == (DIR *)NULL) { warn(curr_dir->dirname, "error opening directory (2)"); if (in_subdir > 1) { curr_dir = popdir(); } in_subdir--; err = -1; continue; } else { seekdir(dirp, curr_dir->where); } } while (in_subdir && (! dirp)); } } while (err < 0); return (0); } /* name_gather - gather names in a list for scanning. * * DESCRIPTION * * Name_gather takes names from the command line and adds them to * the name list. * * FIXME * * We could hash the names if we really care about speed here. */ #ifdef __STDC__ void name_gather(void) #else void name_gather() #endif { while (optind < n_argc) { add_name(n_argv[optind++]); } } /* pushdir - pushes a directory name on the directory stack * * DESCRIPTION * * The pushdir function puses the directory structure which is pointed * to by "info" onto a stack for later processing. The information * may be retrieved later with a call to popdir(). * * PARAMETERS * * dirinfo *info - pointer to directory structure to save */ #ifdef __STDC__ static void pushdir(struct dirinfo *info) #else static void pushdir(info) struct dirinfo *info; #endif { if (stack_head == (struct dirinfo *)NULL) { stack_head = info; stack_head->next = (struct dirinfo *)NULL; } else { info->next = stack_head; stack_head = info; } } /* popdir - pop a directory structure off the directory stack. * * DESCRIPTION * * The popdir function pops the most recently pushed directory * structure off of the directory stack and returns it to the calling * function. * * RETURNS * * Returns a pointer to the most recently pushed directory structure * or NULL if the stack is empty. */ #ifdef __STDC__ static struct dirinfo *popdir(void) #else static struct dirinfo *popdir() #endif { struct dirinfo *tmp; if (stack_head == (struct dirinfo *)NULL) { return((struct dirinfo *)NULL); } else { tmp = stack_head; stack_head = stack_head->next; } return(tmp); }