[mod.amiga.sources] SHAR 4 of 4

doc@pucc-j.UUCP (09/02/86)

Reply-To: dillon%cory.Berkeley.EDU@BERKELEY.EDU (Matt Dillon)

	This shar file contains the backup and restore utility programs, and
an instructional text file ('cause they have a huge number of options).


#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create:
#	backup.c
#	backup.txt
#	restore.c
# This archive created: Wed Aug 27 21:32:44 1986
export PATH; PATH=/bin:/usr/bin:$PATH
echo shar: "extracting 'backup.c'" '(12075 characters)'
if test -f 'backup.c'
then
	echo shar: "will not over-write existing file 'backup.c'"
else
cat << \!Funky!Stuff! > 'backup.c'

/*
 * BACKUP.C
 *
 *
 * Compilation:
 *       -Use a 32 bit integers compiler
 *       -Use Astartup.obj (tiny startup)
 *       -Use the following library order:  MY.LIB AMIGA.LIB LC.LIB
 *
 *       LC.LIB is only in there because Lattice uses a function in its
 *       library for long integer multiplies.
 *
 *       MY.LIB is my own library.  If you don't have it, I'll mail it
 *       to you via UUCP or ARPAnet.
 *
 *       Executable size should be around 6K
 *
 * This program will backup a filesystem or directory, creating a single
 * output file which can later be RESTORE'd from.  It is a quick way to
 * backup your work to a 'backup' disk.
 *
 * backup [-nnn] [-atsSv] [-pPAT] [-dPAT] source [dest_file]
 *
 *    -nnn     a number.  Specify the output filebuffer size in Kbytes.
 *             the default is 32K
 *
 *    -a       append to the destination file.
 *
 *    -t       with this option, no destination file is specified, and the
 *             files in the source are simply listed.
 *
 *    -s       Silent.  Don't display files or directories.
 *
 *    -S       Only display directories as we go along.
 *
 *    -v       Verbose... Additionaly, display those files which will NOT
 *             be backed up.
 *
 *    -pPAT    only file-paths matching this pattern are backed up.  You
 *             may specify more than one '-p' option.
 *
 *    -dPAT    file-paths matching this pattern are NOT backed up.  you
 *             may specify more than one '-d' option.
 *
 *    source   either a file, directory, or filesystem to be backed up.
 *
 *    destfile the name of the backup FILE... it will be created, of course.
 *
 *
 *
 *    NOTE:    do not specify a destination file if you use the '-t' option.
 *
 *    the PAT for the -p and -d options uses standard '*' and '?'.  You are
 *    matching against the entire file path from the base directory you
 *    specified in 'source'.  It is a good idea to use actual volume names
 *    when backing up entire diskettes rather than 'df0:' or 'df1:', since
 *    they will be used as a default when you RESTORE.
 *
 *    For example, to backup all the files on your src: diskette except
 *    the .o files in the 'object' directory of that disk, you would use:
 *
 *               backup "-dsrc:object/*.o" src: backup:xx
 *
 *                     the following are almost equivalent
 *
 *               backup "-d*.o" src: backup:xx
 *               backup "-d*object/*.o" src: backup:xx
 *
 *    Example II: To backup only the '.c' and '.h' files, you might:
 *
 *               backup "-p*.c" "-p*.h" src: backup:xx
 *
 *    Example III: A straight backup of cc: to backup:xx
 *
 *                backup cc: backup:xx
 *
 *
 *    NOTE: the options containing '*' or '?' must be enclosed in quotes
 *    if you are using my SHELL program, or the SHELL will attempt to
 *    expand them.
 *
 *    This program does not allow you to backup to several disks, the
 *    destination must have enough room on it to store the entire backup
 *    file.
 *
 *
 * ---------------------------------------------------------------------
 *
 * destination file format:
 *
 * first four bytes: <0><'b'><'u'><ver>     this ver is 0.
 *
 *
 * FDS = <FDS><total_size  ><name_size><name><protection><the file>
 * DDS = <DDS><name_size   ><name>
 * CDS = <CDS><comment_size><comment>
 * END = <END><0>
 *
 *
 */

#include <exec/types.h>
#include <libraries/dos.h>

#define XCDS  3
#define XFDS  2
#define XDDS  1
#define XEND  0

#define MAXPAT 32

typedef struct FileInfoBlock FIB;

extern char *AllocMem();
extern char *get_name();

static char List, Silent, Verbose;
static long Size = 32 << 10;
static long Outfi;
static char Buf[512];
static char *Wild_accept[MAXPAT];
static char *Wild_naccept[MAXPAT];

main(ac, av)
register char *av[];
{
   register int i, l;
   char *mode       = "w";
   int index_accept = 0;
   int index_naccept= 0;

   check32();
   if (ac == 1) {
      puts("V2.00 by Matthew Dillon");
      puts("backup [-nnn] [-atsSv] [-pPAT] [-dPAT] source [dest_file]");
   }
   for (i = 1; i < ac && av[i][0] == '-'; ++i) {
      register char *str;
      register char notdone = 1;

      str = av[i];
      while (notdone && *++str) {
         if (*str >= '0' && *str <= '9') {
            Size = atoi(str) << 10;
            break;
         }
         switch (*str) {
         case 'a':
            mode = "w+";
            break;
         case 't':
            List = 1;
            break;
         case 's':
            Silent = 2;
            break;
         case 'S':
            Silent = 1;
            break;
         case 'p':
            Wild_accept[index_accept++] = str + 1;
            notdone = 0;
            break;
         case 'd':
            Wild_naccept[index_naccept++] = str + 1;
            notdone = 0;
            break;
         case 'v':
            Verbose = 1;
            break;
         default:
            break;
         }
      }
   }

   if (!index_accept)                           /* default accept pattern             */
      Wild_accept[0] = "*";

   if (!List && i < --ac) {                     /* if not a listing & have src & dest */
      static char c[4] = { 0, 'b', 'u', 0 };    /* last element indicates version     */

      Outfi = xopen(av[ac], mode, Size);        /* output file      */
      if (Outfi == NULL)
         die("Could not open output file or out of memory");
      xwrite(Outfi, c, 4);                      /* write ID         */
   }

   /*
    * This is slightly sticky.  The user can specify a path containing
    * several directories here, so I must manually write the DDS and END
    * structures for all levels except the last.
    */

   for (; i < ac; ++i) {                        /* process sources  */
      register int j, k;
      register char *str = av[i];
      char c;
      long v;

      c = XDDS;
      for (j = k = l = 0; str[j]; ++j) {
         if (str[j+1] && (str[j] == ':' || str[j] == '/')) {
            v = j - k + 1;
            ++l;
            add_name(str + k, v, 1);
            xwrite(Outfi, &c, 1);
            xwrite(Outfi, &v, 4);
            xwrite(Outfi, str + k, v);
            k = j + 1;
         }
      }
      doname(str + k);
      c = XEND;
      v = 0;
      while (l--) {
         xwrite(Outfi, &c, 1);
         xwrite(Outfi, &v, 4);
      }
   }
   die(NULL);
}


die(str)
{
   xclose(Outfi);
   if (str) {
      puts(str);
      exit (10);
   }
   exit (0);
}

/*
 * DONAME()
 *
 * process a file or directory.  This is called recursively.
 *
 */

doname(name)
char *name;
{
   register char *str;
   register FIB *fib;
   register long lock;

   str  = "Could Not find";
   fib  = (FIB *)AllocMem(sizeof(FIB), 0);
   if (lock = Lock(name, ACCESS_READ)) {
      str = "Could Not examine";
      if (Examine(lock, fib)) {
         str = NULL;
         if (fib->fib_DirEntryType > 0) {
            add_name(name, strlen(name), 1);       /* directory   */
            process_dir(name, lock, fib);
         } else {
            add_name(name, strlen(name), 0);       /* normal file */
            if (acceptable()) {
               process_file(name, lock, fib);
            } else {
               if (!Silent && !List && Verbose)
                  printf ("%-40s (NOT ACCEPTED)\n", get_name());
            }
         }
      }
   }
   if (str)                                        /* error       */
      printf ("%s    (%s)\n", name, str);
   if (lock)                                       /* cleanup     */
      UnLock(lock);
   sub_name();
   FreeMem(fib, sizeof(FIB));
}

/*
 * PROCESS_DIR()
 *
 * process a directory.  Make it the current directory, then process
 * all files in that directory.  The XDDS id, indicating a new directory,
 * has only a name, no data, and thus the total size of the segment is
 * simply the size of the name.  Note that the name is NOT zero terminated.
 *
 */


process_dir(str, dir_lock, dir_fib)
char *str;
long dir_lock;
FIB  *dir_fib;
{
   register long cd_lock;
   char c;
   long v;

   if (Silent < 2)
      printf ("%-40s (DIR)\n", get_name());

   cd_lock = CurrentDir(dir_lock);
   c = XDDS;
   v = strlen(str);

   if (List == 0) {
      xwrite(Outfi, &c, 1);                  /* DDS                     */
      xwrite(Outfi, &v, 4);                  /* total size = name_size  */
      xwrite(Outfi, str, strlen(str));
   }

   while (ExNext(dir_lock, dir_fib))         /* process every file      */
      doname(dir_fib->fib_FileName);

   c = XEND;                                 /* END (of directory)      */
   v = 0;
   if (List == 0) {
      xwrite(Outfi, &c, 1);
      xwrite(Outfi, &v, 4);
   }
   CurrentDir(cd_lock);                      /* go back to prev. dir.   */
}

/*
 * PROCESS_FILE()
 *
 * Process a file.  The XFDS id (A file), does have a data section.  The
 * format is:
 *                            |------   total_size   -----|
 *             1       4           4      name_size
 *          <XFDS><total_size><name_size>  <name>    <data>
 *
 */

process_file(str, lock, fib)
char *str;
long lock;
FIB *fib;
{
   register long fh;
   register int n;
   char c;
   long v, w;

   if (List == 0)
      fh = Open(str, MODE_OLDFILE);

   if (Silent < 1)
      printf("%-40s size:%-8ld blocks:%-4ld\n", get_name(), fib->fib_Size, fib->fib_NumBlocks);

   if (List == 0) {
      if (fib->fib_Comment[0]) {             /* CDS before FDS only if   */
         c = XCDS;                           /* comment is non-null      */
         v = strlen(fib->fib_Comment);
         xwrite(Outfi, &c, 1);
         xwrite(Outfi, &v, 4);
         xwrite(Outfi, fib->fib_Comment, v); /* note: \0 is NOT included */
      }
      c = XFDS;
      w = strlen(str);
      v = w + fib->fib_Size + 8;
      xwrite(Outfi, &c, 1);                  /* FDS                   */
      xwrite(Outfi, &v, 4);                  /* total bytes from here */
      xwrite(Outfi, &w, 4);                  /* # bytes in name       */
      xwrite(Outfi, str, strlen(str));       /* name itself           */
      xwrite(Outfi, &fib->fib_Protection, 4);/* protection            */
      while ((n = Read(fh, Buf, 512)) > 0)
         xwrite(Outfi, Buf, n);              /* file itself           */
      Close(fh);
   }
}


/*
 * PATH routines.  These routines upkeep a string which is the 'path'.  This
 * string can be added to or subtracted from as you search through
 * directories.  Additionaly, it has the added ability to 'overide' a
 * directory.  specifically, if the string already contains a prefix,
 * and somebody attempts to append a XXX: to it, a 0-length string will
 * be appended (yielding no change in the overall string).
 *
 * add_name() returns 0 when the name you add does NOT get appended.
 * sub_name() returns 0 when the thing it pops off the end was the
 *  name which did NOT get appended (e.g. it still keeps track that
 *  you appened a NULL name at a given position)
 *
 *
 */

static char  master[512];
static short level, lar[128];

char *
get_name()
{
   return (master);
}

add_name(str, len, isdir)
char *str;
register int len;
{
   register int start = (level) ? lar[level-1] : 0;

   if (str[len-1] == ':' && level)
      len = 0;
   if (len) {
      bmov(str, master + start, len);
      if (isdir && str[len-1] != ':' && str[len-1] != '/')
         master[start + len++] = '/';
   }
   master[start + len] = 0;
   lar[level++] = start + len;
   return (len);
}

sub_name()
{
   register int start;

   if (level) {
      --level;
      start = (level) ? lar[level-1] : 0;
      master[start] = 0;
      return (lar[level] - start);
   }
   return (0);
}

/*
 * ACCEPTABLE()
 *
 * determines if the current path is an acceptable name according to
 * the -pPAT and -dPAT options the user specified.
 */


acceptable()
{
   register char *name = master;
   register char **p;

   for (p = Wild_accept; *p; ++p) {
      if (wildcmp(*p, name))
         goto ok;
   }
   return (0);
ok:
   for (p = Wild_naccept; *p; ++p) {
      if (wildcmp(*p, name))
         return(0);
   }
   return (1);
}


!Funky!Stuff!
fi  # end of overwriting check
echo shar: "extracting 'backup.txt'" '(3368 characters)'
if test -f 'backup.txt'
then
	echo shar: "will not over-write existing file 'backup.txt'"
else
cat << \!Funky!Stuff! > 'backup.txt'

BACKUP AND RESTORE FORMATS

GENERAL:

   Backup creates a single output file.  Restore deciphers that output file
   to recreate files and sub-directories, including the comment and
   protection fields for the files.


   The first four bytes of the backup file is a special 'magic' sequence
   identifying it as a backup file and which backup-file-format version
   it is.

   The backup file is then made up of records.  Each record has a 1 byte ID
   which identifies the record type, and a 4 byte size.  The size refers
   to any bytes after this 5-byte header.  Currently, there are four record
   types:

   CDS      A Comment Record
   FDS      A File Record
   DDS      A Directory Record
   END      An END Directory Record

   Each DDS encountered effectively CD's into that directory, and each END
   encounted effectively CD's back.  The CDS (Comment Record) will optionaly
   occur immediately before an FDS, indicating that particular FDS has a
   comment attached to it (e.g. FileNote).  A typical backup file:

         DF0: (DIR)  a
                     b
                     c
                     src(DIR)  d.c  -comment "hello"
                               e.c
                               f.c

                     obj(DIR)  x.o
                               y.o

      DDS   DF0:
      FDS   a
      FDS   b
      FDS   c
      DDS   src
      CDS   hello          -comment for file d.c
      FDS   d.c
      FDS   e.c
      FDS   f.c
      END                  -end of src
      DDS   obj
      FDS   x.o
      FDS   y.o
      END                  -end of obj
      END                  -end of DF0:


   Note that for every DDS there will eventually be an END.  In This way,
   the RESTORE program can recreate the entire directory structure.
   There need not be any files in a directory, in which case you get
   something like

      DDS emptydir
      END


   Backup files may be appended (JOIN)'d to each other.


FORMAT:

   The first four bytes are as follows:

   0           NUL
   'b'         lower case b
   'u'         lower case u
   version     0 (NUL) for this version, since it's the first release.


   the numbers above indicate the number of bytes each field takes.  Note
   that the header is always the same:  a one byte type and a 4 byte total
   size.  Note that END contains no information in it, thus the total size
   is 0.  A file has a comment only if there is a CDS immediately preceding
   it.

   Name Conventions:  Since the name size is known, no trailing 0 exists in
   the string.  Additionaly, the first DDS encountered may have a ':' as
   the last character of a name (normal conventions).  Note that ANY DDS
   may have a '/' as the last character of a name, but it ISN'T REQUIRED.


           1        4          n
CDS      <CDS><comment_size><comment>

           1      4 (ttl)      4       n        4      ttl - n - 8
FDS      <FDS><total_size><name_size><name><protection><the file>

           1        4      n
DDS      <DDS><name_size><name>

           1        4
END      <END>     <0>



CDS = 3
FDS = 2
DDS = 1
END = 0



---------------------------------------------------------------
THE FUTURE:

   I hope to add a 'compressed file' type in the future, as soon as
   I figure out the best compression scheme.  If you've got any ideas,
   mail me!

                        -Matt


!Funky!Stuff!
fi  # end of overwriting check
echo shar: "extracting 'restore.c'" '(9547 characters)'
if test -f 'restore.c'
then
	echo shar: "will not over-write existing file 'restore.c'"
else
cat << \!Funky!Stuff! > 'restore.c'

/*
 * RESTORE.C
 *
 * Compilation:
 *       -Use a 32 bit integers compiler
 *       -Use Astartup.obj (tiny startup)
 *       -Use the following library order:  MY.LIB AMIGA.LIB LC.LIB
 *
 *       LC.LIB is only in there because Lattice uses a function in its
 *       library for long integer multiplies.
 *
 *       MY.LIB is my own library.  If you don't have it, I'll mail it
 *       to you via UUCP or ARPAnet.
 *
 *       Executable size should be around 7K
 *
 * Restore from a BACKUP file.  This program will unpack a backup file
 * and restore it into a directory or filesystem.  Normally, only
 * directories containing files are restored.
 *
 * restore [-nnn] [-tsSrv -pPAT -dPAT] backupfile [destdir]
 *
 *    -nnn        a number.  Specify the input buffer size in Kbytes.  The
 *                default is 32K
 *
 *    -t          display files in backupfile only, do NOT restore
 *
 *    -s          silent.  Do not display files or directories during restore
 *
 *    -S          only display directories as you restore
 *
 *    -r          Recover entire backed up directory structure, even if
 *                the directories contain no files after the pattern
 *                matching.
 *
 *    -v          verbose.. list files we are ignoring as well as those we
 *                are accepting.
 *
 *    -pPAT       only restore files which match this pattern (ala '*' & '?')
 *
 *    -dPAT       do NOT restore files which match this pattern
 *
 *    backupfile  the backup file (created using BACKUP) to restore.
 *
 *    destdir     the destination directory to restore to.  If none
 *                 specified, the original root directory will be used.
 *
 *
 *    you may specify -pPAT and -dPAT as many times as you wish.  Note that
 *    if you are using my SHELL, you must enclose them in quotes or the
 *    shell will attempt to expand the patterns.
 *
 *    Your pattern is checked against the entire RESTORE file-path.  That
 *    is, if you are restoring some junk into the directory 'ram:q', the path
 *    for some file in the restore will look like:
 *                      ram:q/xxx/xxx/xxx/file
 *
 *    The pattern '-p*.o' would restore only .o files.  Likewise,
 *    '-d*.o' restores everything EXCEPT the .o files.
 *
 */


#include <exec/types.h>
#include <libraries/dos.h>

#define XCDS  3
#define XFDS  2
#define XDDS  1
#define XEND  0

#define MAXPAT 32

extern char *get_name();

static char List, Silent, Verbose, Empty = 1;
static int Size = 32 << 10;
static char Buf[512];
static char Name[128];
static char Comment[128];
static char *Wild_accept[MAXPAT];
static char *Wild_naccept[MAXPAT];

main(ac, av)
register char *av[];
{
   register int i;
   register char *str;
   register long fi;
   short index_accept, index_naccept;
   char c[4];

   check32();
   if (ac == 1) {
      puts ("V2.00 by Matthew Dillon");
      puts ("restore [-nnn] [-tsSrv -pPAT -dPAT] backupfile [destdir]");
   }
   index_accept = index_naccept = 0;
   for (i = 1; i < ac; ++i) {
      if (*(str = av[i]) == '-') {
         while (*++str) {
            if (!List && *str >= '0' && *str <= '9') {
               Size = atoi(str) << 10;
               break;
            }
            switch (*str) {
            case 'r':
               Empty = 0;
               break;
            case 't':
               List = 1;
               Size = 32;
               break;
            case 's':
               Silent = 2;
               break;
            case 'S':
               Silent = 1;
               break;
            case 'p':
               Wild_accept[index_accept++] = str + 1;
               goto br2;
            case 'd':
               Wild_naccept[index_naccept++] = str + 1;
               goto br2;
            case 'v':
               Verbose = 1;
               break;
            default:
               break;
            }
         }
br2:
         continue;
      }
      break;
   }
   if (index_accept == 0)
      Wild_accept[0] = "*";
   if (i >= ac)
      die("?backup file");
   fi = xopen(av[i++], "r", Size);
   if (fi == NULL)
      die("no memory or file not found");
   if (!List && i < ac)
      add_name(av[i], strlen(av[i]), 1);
   xread(fi, c, 4);
   if (c[0] == 0 && c[1] == 'b' && c[2] == 'u') {
      switch (c[3]) {
      case 0:                          /* version supported */
         restore(fi);
         break;
      default:
         puts ("wrong version file");
         break;
      }
   } else {
      puts ("Not a backup file");
   }
   xclose(fi);
   die(NULL);
}


die(str)
char *str;
{
   if (str)
      puts(str);
   exit(0);
}


restore(fi)
long fi;
{
   register long lock, dir_lock;
   int nfiles;

   lock = Lock(get_name(), ACCESS_READ);
   if (lock == NULL) {
      printf ("%s does not exist\n", get_name());
      return (-1);
   }
   dir_lock = CurrentDir(lock);
   dostuff(fi, &nfiles);
   CurrentDir(dir_lock);
   UnLock(lock);
}


dostuff(fi, nfiles)
unsigned long fi;
int *nfiles;
{
   unsigned long v, w, x;
   char c;

   *nfiles = 0;
   while (xread(fi, &c, 1) && xread(fi, &v, 4) == 4) {
      switch (c) {
      case XCDS:                          /* possible Comment */
         xread(fi, Comment, v);
         Comment[v] = 0;
         break;
      case XFDS:
         {
            register int i, fh;

            xread(fi, &w, 4);                /* Name length */
            xread(fi, Name, w);              /* Name NOT \0 */
            xread(fi, &x, 4);                /* protection  */
            i = v - w - 8;                   /* file length */
            add_name(Name, w, 0);
            if (acceptable()) {
               if (Silent < 1)
                  display_name(i);
               if (List == 0) {
                  Name[w] = 0;
                  if (fh = Open(Name, MODE_NEWFILE)) {
                     *++nfiles;
                     while (i > 512) {
                        xread(fi, Buf, 512);
                        Write(fh, Buf, 512);
                        i -= 512;
                     }
                     xread(fi, Buf, i);
                     Write(fh, Buf, i);
                     i = 0;
                     Close(fh);
                     if (Comment[0]) {
                        if (SetComment(Name, Comment) == 0)
                           puts (" ---- warning, could not set comment");
                     }
                     if (x != 0x0F)
                        SetProtection(Name, x);
                  }
               }
            } else {
               if (Verbose)
                  display_name(-3);
            }
            xseek(fi, i, 0);
            sub_name();
         }
         Comment[0] = 0;
         break;
      case XDDS:
         {
            register long lock, dir_lock;
            register int i;
            char created;
            char dirname[128];

            created = 0;
            xread(fi, dirname, v);
            if (add_name(dirname, v, 1) == 0)
               break;
            if (List) {
               display_name(-1);
            } else {
               dirname[v] = 0;
               if (lock = Lock(dirname, ACCESS_READ)) {
                  if (Silent < 2)
                     display_name(-1);
               } else {
                  if (lock = CreateDir(dirname)) {
                     created = 1;
                     if (Silent < 2)
                        display_name(-2);
                  } else {
                     printf ("could not create: %s\n", get_name());
                     return (0);
                  }
               }
               {
                  int sfiles;

                  dir_lock = CurrentDir(lock);           /* cd down */
                  i = dostuff(fi, &sfiles);
                  CurrentDir(dir_lock);                  /* cd up   */
                  UnLock(lock);
                  if (sfiles == 0 && Empty && created)
                     DeleteFile(dirname);
                  *nfiles += sfiles;
                  if (i == 0)
                     return (i);
               }
            }
         }
         break;
      case XEND:
         if (sub_name() && !List)
            return (1);
         break;
      default:
         break;
      }
   }
   return (0);
}


display_name(fsize)
long fsize;
{
   printf ("%-40s ", get_name());
   switch (fsize) {
   case -1:
      printf ("(DIR)\n");
      break;
   case -2:
      printf ("(DIR)(CREATED)\n");
      break;
   case -3:
      printf ("(NOT ACCEPTED)\n");
      break;
   default:
      printf ("size: %ld\n", fsize);
      break;
   }
}

static char  master[512];
static short level, lar[128];

char *
get_name()
{
   return (master);
}

add_name(str, len, isdir)
char *str;
register int len;
{
   register int start = (level) ? lar[level-1] : 0;

   if (str[len-1] == ':' && level)
      len = 0;
   if (len) {
      bmov(str, master + start, len);
      if (isdir && str[len-1] != ':' && str[len-1] != '/')
         master[start + len++] = '/';
   }
   master[start + len] = 0;
   lar[level++] = start + len;
   return (len);
}

sub_name()
{
   register int start;

   if (level) {
      --level;
      start = (level) ? lar[level-1] : 0;
      master[start] = 0;
      return (lar[level] - start);
   }
   return (0);
}


acceptable()
{
   register char *name = master;
   register char **p;

   for (p = Wild_accept; *p; ++p) {
      if (wildcmp(*p, name))
         goto ok;
   }
   return (0);
ok:
   for (p = Wild_naccept; *p; ++p) {
      if (wildcmp(*p, name))
         return(0);
   }
   return (1);
}


!Funky!Stuff!
fi  # end of overwriting check
exit 0
#	End of shell archive