555 lines
13 KiB
C
555 lines
13 KiB
C
/* $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"
|
|
extern OFFSET telldir(DIR *);
|
|
extern void seekdir(DIR *, OFFSET);
|
|
|
|
/* 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 regexp; /* 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 = NULL; /* Points to last name in list */ /* Xn */
|
|
static struct nm_list *namelist = NULL; /* Points to first name in list */ /* Xn */
|
|
|
|
|
|
/* 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 */
|
|
|
|
#ifdef DF_TRACE_DEBUG
|
|
printf("DF_TRACE_DEBUG: void add_name() in namelist.c\n");
|
|
#endif
|
|
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 = (short)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->regexp = 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;
|
|
|
|
#ifdef DF_TRACE_DEBUG
|
|
printf("DF_TRACE_DEBUG: int name_match() in namelist.c\n");
|
|
#endif
|
|
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->regexp) {
|
|
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;
|
|
|
|
#ifdef DF_TRACE_DEBUG
|
|
printf("DF_TRACE_DEBUG: void names_notfound() in namelist.c\n");
|
|
#endif
|
|
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
|
|
*
|
|
#ifdef DF_TRACE_DEBUG
|
|
printf("DF_TRACE_DEBUG: void name_init() in namelist.c\n");
|
|
#endif
|
|
* 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; /* Xn */
|
|
static int in_subdir = 0; /* Xn */
|
|
static DIR *dirp = NULL; /* Xn */
|
|
struct dirent *d; /* Xn */
|
|
static struct dirinfo *curr_dir = NULL; /* Xn */
|
|
int len; /* Xn */
|
|
|
|
do {
|
|
#ifdef DF_TRACE_DEBUG
|
|
printf("DF_TRACE_DEBUG: int name_next() in namelist.c\n");
|
|
#endif
|
|
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;
|
|
}
|
|
#ifdef DF_TRACE_DEBUG
|
|
printf("DF_TRACE_DEBUG: strcmp() in namelist.c\n");
|
|
#endif
|
|
if (strlen(d->d_name) +
|
|
strlen(curr_dir->dirname) >= PATH_MAX) {
|
|
#ifdef DF_TRACE_DEBUG
|
|
printf("DF_TRACE_DEBUG: strlen() in namelist.c\n");
|
|
#endif
|
|
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(errno)); /* Xn */
|
|
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
|
|
{
|
|
#ifdef DF_TRACE_DEBUG
|
|
printf("DF_TRACE_DEBUG: void name_gather() in namelist.c\n");
|
|
#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
|
|
{
|
|
#ifdef DF_TRACE_DEBUG
|
|
printf("DF_TRACE_DEBUG: static void pushdir() in namelist.c\n");
|
|
#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;
|
|
|
|
#ifdef DF_TRACE_DEBUG
|
|
printf("DF_TRACE_DEBUG: static struct dirinfo *popdir() in namelist.c\n");
|
|
#endif
|
|
if (stack_head == (struct dirinfo *)NULL) {
|
|
return((struct dirinfo *)NULL);
|
|
} else {
|
|
tmp = stack_head;
|
|
stack_head = stack_head->next;
|
|
}
|
|
return(tmp);
|
|
}
|