jik@pit-manager.MIT.EDU (Jonathan I. Kamens) (02/26/91)
Submitted-by: Jonathan I. Kamens <jik@pit-manager.MIT.EDU> Posting-number: Volume 17, Issue 26 Archive-name: delete/part04 #! /bin/sh # This is a shell archive. Remove anything before this line, then unpack # it by saving it into a file and typing "sh file". To overwrite existing # files, type "sh file -c". You can also feed this as standard input via # unshar, or by typing "sh <file", e.g.. If this archive is complete, you # will see the following message at the end: # "End of archive 4 (of 4)." # Contents: pattern.c # Wrapped by jik@pit-manager on Fri Feb 22 08:14:28 1991 PATH=/bin:/usr/bin:/usr/ucb ; export PATH if test -f 'pattern.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'pattern.c'\" else echo shar: Extracting \"'pattern.c'\" \(25764 characters\) sed "s/^X//" >'pattern.c' <<'END_OF_FILE' X/* X * $Source: /afs/athena.mit.edu/astaff/project/delete/src/RCS/pattern.c,v $ X * $Author: jik $ X * X * This program is part of a package including delete, undelete, X * lsdel, expunge and purge. The software suite is meant as a X * replacement for rm which allows for file recovery. X * X * Copyright (c) 1989 by the Massachusetts Institute of Technology. X * For copying and distribution information, see the file "mit-copyright.h." X */ X X#if (!defined(lint) && !defined(SABER)) X static char rcsid_pattern_c[] = "$Header: /afs/athena.mit.edu/astaff/project/delete/src/RCS/pattern.c,v 1.21 91/02/20 17:27:03 jik Exp $"; X#endif X X#include <stdio.h> X#include <sys/types.h> X#include <sys/dir.h> X#include <sys/param.h> X#ifdef SYSV X#include <string.h> X#define index strchr X#define rindex strrchr X#else X#include <strings.h> X#endif /* SYSV */ X#include <errno.h> X#include <com_err.h> X#include "pattern.h" X#include "util.h" X#include "directories.h" X#include "undelete.h" X#include "shell_regexp.h" X#include "mit-copyright.h" X#include "delete_errs.h" X#include "errors.h" X#include "stack.h" X Xextern char *realloc(); Xextern int errno; Xextern char *whoami; X Xvoid free_list(); X X X/* X * add_arrays() takes pointers to two arrays of char **'s and their X * lengths, merges the two into the first by realloc'ing the first and X * then free's the second's memory usage. X */ Xint add_arrays(array1, num1, array2, num2) Xchar ***array1, ***array2; Xint *num1, *num2; X{ X int counter; X X *array1 = (char **) realloc((char *) *array1, (unsigned) X (sizeof(char *) * (*num1 + *num2))); X if (! *array1) { X set_error(errno); X error("realloc"); X return error_code; X } X for (counter = *num1; counter < *num1 + *num2; counter++) X *(*array1 + counter) = *(*array2 + counter - *num1); X free ((char *) *array2); X *num1 += *num2; X return 0; X} X X X X X X X/* X * Add a string to a list of strings. X */ Xint add_str(strs, num, str) Xchar ***strs; Xint num; Xchar *str; X{ X char **ary; X X ary = *strs = (char **) realloc((char *) *strs, (unsigned) X (sizeof(char *) * (num + 1))); X if (! *strs) { X set_error(errno); X error("realloc"); X return error_code; X } X ary[num] = Malloc((unsigned) (strlen(str) + 1)); X if (! ary[num]) { X set_error(errno); X error("Malloc"); X return error_code; X } X (void) strcpy(ary[num], str); X return 0; X} X X X X X X/* X * Find_matches will behave unpredictably if you try to use it to find X * very strange combinations of file types, for example only searching X * for undeleted files in the top-level directory, while searching X * recursively for deleted files. Basically, there are some conflicts X * between options that I don't list here because I don't think I'll X * ever need to use those combinations. X */ X/* X * Function: find_matches(char *name, int *num_found, char ***found, X * int options) X * X * Requires: name points to a NULL-terminated string, representing a X * filename pattern with regular filename characters, path X * separators and shell wildcard characters []*?; num_found points X * to a valid int memory storage location; found points to a valid X * char ** memory storage location. X * X * Effects: Returns a list of all the files in the file hierarchy that X * match the options specified in options and that match name. X * Options are: X * X * FIND_UNDELETED search for and return undeleted files X * X * FIND_DELETED search for and return deleted files X * X * FIND_CONTENTS means that if matches are directories (or links to X * directories), the contents of the directory should be matched X * in addition to the directory itself X * X * RECURS_FIND_DELETED to search all undeleted subdirectories X * recursively of matched directories looking for deleted files X * X * RECURS_FIND_UNDELETED to search all undeleted subdirectories X * recursively of matched directories looking for undeleted files X * X * RECURS_DELETED to recursively return all contents of deleted X * directories in addition to the directories themselves X * X * FOLLW_LINKS to pursue symlinks to directories and continue down X * the referenced directories when searching recursively (if the X * initial string is an undeleted symlink it is always traversed; X * deleted symlinks are never traversed) X * X * FOLLW_MOUNTPOINTS to traverse mount points when searching X * recursively (if the initial string is a mountpoint it is always X * traversed) X * X * FIND_DOTFILES forces the system to recognize dot files instead of X * discarding them when looking for files X * X * If the first character of name is '/', the search is conducted X * absolutely from the root of the hierarchy; else, it is conducted X * relative to the current working directory. The number of X * matching files is returned in *num_found, and a list of file X * names is returned in *found. If there are no errors, the return X * value is 0; else the return value represents the error code of X * the error which occurred. No matter how many file names are X * returned, the memory location addressed in *found is a valid X * pointer according to Malloc() and can be released using free() X * safely. However, if an error value is returned, the caller X * should not attempt to use the values stored in *num_found or X * *found. X * X * Modifies: *num_found, *found. X */ Xint find_matches(name, num_found, found, options) Xchar *name; Xint *num_found; Xchar ***found; Xint options; X{ X char **matched_files, **return_files, **recurs_files; X int num_matched_files = 0, num_return_files = 0, X num_recurs_files = 0; X int retval; X int i; X#ifdef DEBUG X int j; X#endif X int match_options = 0; X X#ifdef DEBUG X fprintf(stderr, "Entering find_matches, name = %s, options = %d.\n", X name, options); X#endif X X match_options = options & (FIND_DELETED | FIND_UNDELETED); X if (options & (RECURS_FIND_DELETED | RECURS_FIND_UNDELETED | X FIND_CONTENTS)) X match_options |= FIND_UNDELETED; X X if (! match_options) { X set_error(PAT_NO_FILES_REQUESTED); X error("find_matches"); X return error_code; X } X X retval = do_match(name, &num_matched_files, &matched_files, X match_options & FIND_UNDELETED, X match_options & FIND_DELETED); X if (retval) { X error(name); X return retval; X } X if (num_matched_files == 0) { X *num_found = num_matched_files; X *found = matched_files; X#ifdef DEBUG X fprintf(stderr, "No matches found, returning.\n"); X#endif X return 0; X } X X#ifdef DEBUG X fprintf(stderr, "The following matches were found:\n"); X for (i = 0; i < num_matched_files; i++) X fprintf(stderr, " %s\n", matched_files[i]); X#endif X X if (options & RECURS) { X return_files = (char **) Malloc(0); X if (! return_files) { X set_error(errno); X error("Malloc"); X return error_code; X } X num_return_files = 0; X X for (i = 0; i < num_matched_files; i++) { X X retval = do_recurs(matched_files[i], &num_recurs_files, X &recurs_files, options); X if (retval) { X error(matched_files[i]); X return retval; X } X X if (num_recurs_files) { X retval = add_arrays(&return_files, &num_return_files, X &recurs_files, &num_recurs_files); X if (retval) { X error("add_arrays"); X return retval; X } X#ifdef DEBUG X fprintf(stderr, X "Just added the following to return_files:\n"); X for (j = num_return_files - num_recurs_files; X j < num_return_files; j++) X fprintf(stderr, " %s\n", return_files[j]); X#endif X } X X if (is_deleted(lastpart(matched_files[i]))) { X if (options & FIND_DELETED) { X retval = add_str(&return_files, num_return_files, X matched_files[i]); X if (retval) { X error("add_str"); X return retval; X } X num_return_files++; X#ifdef DEBUG X fprintf(stderr, "Just added %s to return_files.\n", X return_files[num_return_files-1]); X#endif X } X } X else if (options & FIND_UNDELETED) { X retval = add_str(&return_files, num_return_files, X matched_files[i]); X if (retval) { X error("add_str"); X return retval; X } X num_return_files++; X#ifdef DEBUG X fprintf(stderr, "Just added %s to return_files.\n", X return_files[num_return_files-1]); X#endif X } X } X free_list(matched_files, num_matched_files); X *num_found = num_return_files; X *found = return_files; X } X else { X *num_found = num_matched_files; X *found = matched_files; X } X X return 0; X} X X X X X X X X X#define string_push(str)\ X strsize = strlen(str);\ X retval = push(str, strsize);\ X if (! retval)\ X retval |= push(&strsize, sizeof(int));\ X if (retval) {\ X error("push");\ X (void) popall();\ X return retval;\ X } X#define string_pop(str)\ X retval = pop(&strsize, sizeof(int));\ X if (! retval)\ X retval = pop(str, strsize);\ X if (! retval)\ X str[strsize] = '\0' X X X X X X X/* X * Function: do_match(char *name, int *num_found, char ***found, X * Boolean match_undeleted, Boolean match_deleted) X * X * Requires: name points to a NULL-terminated string, representing a X * filename pattern with regular filename characters, path X * separators and shell wildcard characters []*?; num_found points X * to a valid int memory storage location; found points to a valid X * char ** memory storage location. X * X * Effects: Returns a list of all the files in the file hierarchy that X * match name. If match_undeleted is true, will return undeleted X * files that match; if match_deleted is true, will return X * deleted_files that match. If the first character of name is '/', X * the search is conducted absolutely from the root of the X * hierarchy; else, it is conducted relative to the current working X * directory. The number of matching files is returned in X * *num_found, and a list of file names is returned in *found. If X * there are no errors, the return value is 0; else the return value X * represents the error code of the error which occurred. No matter X * how many file names are returned, the memory location addressed X * in *found is a valid pointer according to Malloc() and can be X * released using free() safely. However, if an error value is X * returned, the caller should not attempt to use the values stored X * in *num_found or *found. X * X * Modifies: *num_found, *found. X * X * Algorithm: X * X * start: X * base = "" or "/", X * name = name or name + 1 X * initialze found and num_found X * dirp = Opendir(base) X * first = firstpart(name, rest) (assigns rest as side-effect) X * if (! *first) { X * add string to list if appropriate X * return X * X * loop: X * dp = readdir(dirp) X * if (! dp) goto updir X * compare dp->d_name to first -- match? X * yes - goto downdir X * no - are we looking for deleted and is dp->d_name deleted? X * yes - compare undeleted dp->d_name to first -- match? X * yes - goto downdir X * no - goto loop X * no - goto loop X * X * downdir: X * save dirp, rest, first and base on stack X * first = firstpart(rest, rest) X * base = dp->d_name appended to base X * is first an empty string? X * yes - put back dirp, rest, first, base X * goto loop X * try to open dir base - opens? X * yes - goto loop X * no - is the error ENOTDIR? X * yes - don't worry about it X * no - report the error X * restore dirp, rest, first, base from stack X * goto loop X * X * updir: X * close dirp X * restore base, rest, first from stack X * STACK_EMPTY? X * yes - return from procedure with results X * restore dirp from stack X * goto loop X */ Xint do_match(name, num_found, found, match_undeleted, match_deleted) Xchar *name; Xint *num_found; Xchar ***found; XBoolean match_undeleted, match_deleted; X{ X char base[MAXPATHLEN]; X struct direct *dp; X DIR *dirp; X char first[MAXNAMLEN], rest[MAXPATHLEN]; X int retval; X int strsize; X struct stat statbuf; X#ifdef PATTERN_DEBUG X int j; X#endif X X#ifdef DEBUG X printf("do_match: looking for %s\n", name); X#endif X X /* start: */ X X if (*name == '/') { X *base = '/'; X *(base + 1) = '\0'; X name++; X } X else X *base = '\0'; X X *found = (char **) Malloc(0); X if (! *found) { X set_error(errno); X error("Malloc"); X#ifdef PATTERN_DEBUG X fprintf(stderr, "do_match: return 1.\n"); X#endif X return error_code; X } X *num_found = 0; X X dirp = Opendir(base); X if (! dirp) { X set_error(errno); X error(base); X#ifdef PATTERN_DEBUG X fprintf(stderr, "do_match: return 2.\n"); X#endif X return error_code; X } X (void) strcpy(first, firstpart(name, rest)); X if ((! *first) && (match_undeleted)) { X retval = add_str(found, *num_found, base); X if (retval) { X error("add_str"); X (void) popall(); X#ifdef PATTERN_DEBUG X fprintf(stderr, "do_match: return 3.\n"); X#endif X return retval; X } X (*num_found)++; X#ifdef PATTERN_DEBUG X fprintf(stderr, "do_match: return 4.\n"); X#endif X return 0; X } X X while (1) { X dp = readdir(dirp); X if (! dp) goto updir; X X retval = reg_cmp(first, dp->d_name); X#ifdef PATTERN_DEBUG X fprintf(stderr, "do_match: comparing %s to %s returns %d.\n", X first, dp->d_name, retval); X#endif X if (retval < 0) { X error("reg_cmp"); X goto updir; X } X X if (retval == REGEXP_MATCH) goto downdir; X X if (is_deleted(dp->d_name) && match_deleted) { X retval = reg_cmp(first, &dp->d_name[2]); X#ifdef PATTERN_DEBUG X fprintf(stderr, X "do_match: deleted compare of %s to %s returns %d.\n", X first, &dp->d_name[2], retval); X#endif X if (retval < 0) { X error("reg_cmp"); X goto updir; X } X X if (retval == REGEXP_MATCH) X goto downdir; X else X continue; X } X else X continue; X X downdir: X#ifdef PATTERN_DEBUG X fprintf(stderr, "do_match: downdir\n"); X#endif X retval = push(&dirp, sizeof(DIR *)); X if (retval) { X error("push"); X (void) popall(); X#ifdef PATTERN_DEBUG X fprintf(stderr, "do_match: return 5.\n"); X#endif X return retval; X } X#ifdef PATTERN_DEBUG X fprintf(stderr, "do_match: pushing %s, %s, %s\n", first, rest, base); X#endif X string_push(first); X string_push(rest); X string_push(base); X (void) strcpy(base, append(base, dp->d_name)); X (void) strcpy(first, firstpart(rest, rest)); X if (! *first) { X if (is_deleted(lastpart(base))) { X if (match_deleted) { X retval = add_str(found, *num_found, base); X if (retval) { X error("add_str"); X (void) popall(); X#ifdef PATTERN_DEBUG X fprintf(stderr, "do_match: return 6.\n"); X#endif X return retval; X } X (*num_found)++; X } X } X else if (match_undeleted) { X retval = add_str(found, *num_found, base); X if (retval) { X error("add_str"); X (void) popall(); X#ifdef PATTERN_DEBUG X fprintf(stderr, "do_match: return 7.\n"); X#endif X return retval; X } X (*num_found)++; X } X string_pop(base); X string_pop(rest); X string_pop(first); X#ifdef PATTERN_DEBUG X fprintf(stderr, "do_match: popped %s, %s, %s\n", first, X rest, base); X#endif X (void) pop(&dirp, sizeof(DIR *)); X continue; X } X X if (! stat(base, &statbuf)) { X if ((statbuf.st_mode & S_IFMT) == S_IFDIR) X dirp = Opendir(base); X } X else { X dirp = NULL; X } X if (! dirp) { X set_error(errno); X error(base); X string_pop(base); X string_pop(rest); X string_pop(first); X#ifdef PATTERN_DEBUG X fprintf(stderr, "do_match: popped %s, %s, %s\n", first, X rest, base); X#endif X (void) pop(&dirp, sizeof(DIR *)); X continue; X } X else X continue; X X updir: X#ifdef PATTERN_DEBUG X fprintf(stderr, "do_match: updir\n"); X#endif X closedir(dirp); X string_pop(base); X#ifdef PATTERN_DEBUG X fprintf(stderr, "do_match: popped %s\n", base); X#endif X if (retval) { X if (retval != STACK_EMPTY) { X error("pop"); X (void) popall(); X#ifdef PATTERN_DEBUG X fprintf(stderr, "do_match: return 8.\n"); X#endif X return retval; X } X#ifdef PATTERN_DEBUG X fprintf(stderr, "Returning %d word%s from do_match:\n", X *num_found, X *num_found == 1 ? "" : "s"); X for (j = 0; j < *num_found; j++) X fprintf(stderr, "\t%s\n", (*found)[j]); X fprintf(stderr, "do_match: return 9.\n"); X#endif X return 0; X } X string_pop(rest); X string_pop(first); X#ifdef PATTERN_DEBUG X fprintf(stderr, "do_match: popped %s, %s\n", rest, first); X#endif X retval = pop(&dirp, sizeof(DIR *)); X if (retval) { X error("pop"); X (void) popall(); X#ifdef PATTERN_DEBUG X fprintf(stderr, "do_match: return 10.\n"); X#endif X return retval; X } X continue; X } X} X X X X X X X/* X * Function: do_recurs(char *name, int *num_found, char ***found, X * int options) X * X * Requires: name points to a NULL-terminated string, representing a X * filename; points to a valid int memory storage location; found X * points to a valid char ** memory storage location. X * X * Effects: Returns a list of all the files in the file hierarchy that X * are underneath the specified file, governed by the options set in X * options. Options are as described in the find_matches() description. X * RECURS_FIND_DELETED and RECURS_DELETED imply FIND_DELETED. X * RECURS_FIND_UNDELETED implies FIND_UNDELETED. X * X * Modifies: *num_found, *found. X * X * Algorithm: X * X * start: X * initialze found and num_found X * strcopy(base, name) X * check if we just opened a deleted symlink and return if we did X * dirp = Opendir(base) X * check RECURS options and set FIND options as appropriate X * X * loop: X * dp = readdir(dirp) X * if (! dp) goto updir X * is dp deleted? X * yes - is FIND_DELETED set? X * yes - add to list X * is RECURS_DELETED set? X * yes - goto downdir X * no - goto loop X * no - goto loop X * no - is FIND_UNDELETED set? X * yes - is the file a dotfile? X * yes - is FIND_DOTFILES set? X * yes - add to list X * goto loop X * no - add to list X * are RECURS_FIND_DELETED and FIND_DELETED set? X * yes - goto downdir X * is RECURS_FIND_UNDELETED set? X * yes - goto downdir X * no - goto loop X * no - goto loop X * X * downdir: X * save dirp, base on stack X * base = dp->d_name appended to base X * try to open base -- opens? X * yes - is FOLLW_LINKS set? X * yes - is it deleted? X * yes - is it a link? X * yes - close the directory X * restore base and dirp X * goto loop X * no - is it a link? X * yes - close the directory X * restore base and dirp X * goto loop X * is FOLLW_MOUNTPOINTS set? X * no - is it a mountpoint? X * yes - close the directory X * restore base and dirp X * goto loop X * no - is the error ENOTDIR? X * yes - don't worry about it X * no - report the error X * restore base and dirp X * goto loop X * X * updir: X * close dirp X * restore base from stack X * STACK_EMPTY? X * yes - return from procedure with results X * restore dirp from stack X * goto loop X */ Xint do_recurs(name, num_found, found, options) Xchar *name; Xint *num_found; Xchar ***found; Xint options; X{ X char base[MAXPATHLEN]; X struct direct *dp; X DIR *dirp; X int retval; X int strsize; X struct stat statbuf; X int use_stat; X X#ifdef DEBUG X fprintf(stderr, "do_recurs: opening %s\n", name); X#endif X X /* start: */ X X *found = (char **) Malloc(0); X if (! *found) { X set_error(errno); X error("Malloc"); X return error_code; X } X *num_found = 0; X strcpy(base, name); X X if (lstat(base, &statbuf)) { X set_error(errno); X error(base); X return error_code; X } X X if (is_link(base, &statbuf)) { X /* Never follow deleted symlinks */ X if (is_deleted(lastpart(base))) { X return 0; X } X if (stat(base, &statbuf)) { X if (errno == ENOENT) { X extern int readlink(); X char pathbuf[MAXPATHLEN]; X int cc; X X /* What the link is pointing to does not exist; */ X /* this is a warning, not an error. */ X set_warning(errno); X cc = readlink(base, pathbuf, MAXPATHLEN); X if (cc > 0) { X char error_buf[2*MAXPATHLEN+20]; X X pathbuf[(cc == MAXPATHLEN) ? (cc - 1) : cc] = '\0'; X sprintf(error_buf, "%s (pointed to by %s)", pathbuf, X base); X error(error_buf); X } X else { X error(base); X } X X return 0; X } X else { X set_error(errno); X error(base); X return error_code; X } X } X } X X if ((statbuf.st_mode & S_IFMT) != S_IFDIR) X return 0; X X dirp = Opendir(base); X if (! dirp) { X#ifdef DEBUG X fprintf(stderr, "Couldn't open %s.\n", base); X#endif X set_error(errno); X error(base); X return error_code; X } X X if (options & (RECURS_FIND_DELETED | RECURS_DELETED)) X options |= FIND_DELETED; X if (options & RECURS_FIND_UNDELETED) X options |= FIND_UNDELETED; X X while (1) { X dp = readdir(dirp); X if (! dp) goto updir; X X if (is_deleted(dp->d_name)) { X if (options & FIND_DELETED) { X retval = add_str(found, *num_found, X append(base, dp->d_name)); X if (retval) { X error("add_str"); X (void) popall(); X return retval; X } X (*num_found)++; X if (options & RECURS_DELETED) X goto downdir; X else X continue; X } X else X continue; X } X X if (options & FIND_UNDELETED) { X if (is_dotfile(dp->d_name)) { X if (options & FIND_DOTFILES) { X retval = add_str(found, *num_found, X append(base, dp->d_name)); X if (retval) { X error("add_str"); X (void) popall(); X return retval; X } X } X continue; X } X else { X retval = add_str(found, *num_found, X append(base, dp->d_name)); X if (retval) { X error("add_str"); X (void) popall(); X return retval; X } X (*num_found)++; X } X } X X if (! is_dotfile(dp->d_name)) { X if (options & RECURS_FIND_DELETED) X goto downdir; X if (options & RECURS_FIND_UNDELETED) X goto downdir; X } X X continue; X X X downdir: X retval = push(&dirp, sizeof(DIR *)); X if (retval) { X error("push"); X (void) popall(); X return retval; X } X string_push(base); X (void) strcpy(base, append(base, dp->d_name)); X X /* X * Originally, I did an Opendir() right at the start and X * then only checked things if the Opendir resulted in an X * error. However, this is inefficient, because the X * Opendir() procedure works by first calling open() on the X * file, and *then* calling fstat on the file descriptor X * that is returned. since most of the time we will be X * trying to open things that are not directory, it is much X * more effecient to do the stat first here and to do the X * Opendir only if the stat results are satisfactory. X */ X use_stat = (options & FOLLW_LINKS) && (! is_deleted(lastpart(base))); X if (use_stat) X retval = stat(base, &statbuf); X else X retval = lstat(base, &statbuf); X if (retval == -1) { X set_error(errno); X error(base); X string_pop(base); X (void) pop(&dirp, sizeof(DIR *)); X continue; X } X /* It's not a directory, so punt it and continue. */ X if ((statbuf.st_mode & S_IFMT) != S_IFDIR) { X string_pop(base); X (void) pop(&dirp, sizeof(DIR *)); X continue; X } X X /* Actually try to open it. */ X dirp = Opendir(base); X if (! dirp) { X set_error(errno); X error(base); X string_pop(base); X (void) pop(&dirp, sizeof(DIR *)); X continue; X } X X if (! (options & FOLLW_MOUNTPOINTS)) { X if (is_mountpoint(base, use_stat ? (struct stat *) NULL : X &statbuf)) { X closedir(dirp); X set_warning(PAT_IS_MOUNT); X error(base); X string_pop(base); X (void) pop(&dirp, sizeof(DIR *)); X continue; X } X#ifdef DEBUG X else { X fprintf(stderr, X "do_recurs: %s isn't a mountpoint, following.\n", X base); X } X#endif X } X#ifdef DEBUG X printf("do_recurs: opening %s\n", base); X#endif X continue; X X updir: X closedir(dirp); X string_pop(base); X if (retval) { X if (retval != STACK_EMPTY) { X error("pop"); X (void) popall(); X return retval; X } X return 0; X } X retval = pop(&dirp, sizeof(DIR *)); X if (retval) { X error("pop"); X (void) popall(); X return retval; X } X continue; X } X} X X Xvoid free_list(list, num) Xchar **list; Xint num; X{ X int i; X X for (i = 0; i < num; i++) X free(list[i]); X X free((char *) list); X} X X X X X X X/* X * returns true if the filename has no globbing wildcards in it. That X * means no non-quoted question marks, asterisks, or open square X * braces. Assumes a null-terminated string, and a valid globbing X */ Xint no_wildcards(name) Xchar *name; X{ X do { X switch (*name) { X case '\\': X name++; X break; X case '?': X return(0); X case '*': X return(0); X case '[': X return(0); X } X } while (*++name); X return(1); X} END_OF_FILE if test 25764 -ne `wc -c <'pattern.c'`; then echo shar: \"'pattern.c'\" unpacked with wrong size! fi # end of 'pattern.c' fi echo shar: End of archive 4 \(of 4\). cp /dev/null ark4isdone MISSING="" for I in 1 2 3 4 ; do if test ! -f ark${I}isdone ; then MISSING="${MISSING} ${I}" fi done if test "${MISSING}" = "" ; then echo You have unpacked all 4 archives. rm -f ark[1-9]isdone else echo You still need to unpack the following archives: echo " " ${MISSING} fi ## End of shell archive. exit 0 exit 0 # Just in case... -- Kent Landfield INTERNET: kent@sparky.IMD.Sterling.COM Sterling Software, IMD UUCP: uunet!sparky!kent Phone: (402) 291-8300 FAX: (402) 291-4362 Please send comp.sources.misc-related mail to kent@uunet.uu.net.