/* * * * The information in this document is subject to change * without notice and should not be construed as a commitment * by Digital Equipment Corporation or by DECUS. * * Neither Digital Equipment Corporation, DECUS, nor the authors * assume any responsibility for the use or reliability of this * document or the described software. * * Copyright (C) 1980, DECUS * * * General permission to copy or modify, but not for profit, is * hereby granted, provided that the above copyright notice is * included and reference made to the fact that reproduction * privileges were granted by DECUS. * */ /* * A R C H I V E * * Archiver, roughly from software tools. * */ /* * title ar text file archiver * index text file archiver * * synopsis * ar [-options] [-z logfile] archive_name file[s] * * description * * Ar manages archives (libraries) of source files, allowing * a large number of small files to be stored without using * excessive system resources. The following options may * be specified: * * c Force creation of new archive * d Delete file from archive. * i Insert, same as update * p Print files on standard output * r Replace, same as update * l List archive contents (directory) * u Update, same as replace * x Extract named files * v Verbose * z Write verbose log to indicated file * * The file name arguments may contain '*' and '?' wildcards, where * '*' matches any string of characters, and '?' matches one character. * ('%' may be used as a synonym for '?'.) There is a slight, but * suble difference in the way wild cards are processed for the * various commands: * * directory, delete, and extract * * Match ('*' and '?') against the files in the * archive, performing the operation on all files that * match. Except for delete, "no argument" matches * all files in the archive. * * insert, replace, and update * * Expand the wild-card arguments against the files * stored on the operating system -- eliminating all * wild cards. Then, match against the archived * files. Those that match are replaced by the * file on the operating system. After replacement, * any additional files are appended to the archive. * Files in the archive that are not in the directory * are unchanged. * * Currently, insert, replace, and update work the same. * If it seems reasonable, the program may be extended * as follows: * * insert Add files new only * * Adds new files (not present in the archive) * but does not modify files currently in * the archive. It would be an error to try * modifying a currently archived file. * * replace Modify existing files only * * Modify files present in the archive, but do * not add new files to the archive. * * update Modify existing, add new * * This is simple to do, but would seem to be a rich * source of user error. * * archive file format * * Archive files are standard text files. Each archive element is * preceeded by a line of the format: * * -h- file.name date true_name * * Note that there is no line or byte count. To prevent problems, * a '-' at the beginning of a record within a user file or embedded * archive will be "quoted" by doubling it. The date and true filename * fields are ignored. On Dec operating systems, file.name is * forced to lowercase. * * diagnostics * * Diagnostic messages should be self-explanatory * * author * * Martin Minow * * Extensively reworked by Fred Fish, 1-Dec-85 * - Reformatted using the "indent" program. * - Added support for macro based debugging package. * - Delinted and questionable C usages removed. * - Ported to Commodore AMIGA using Lattice C. * */ #include #ifdef BSD #include #undef tolower #define tolower(c) (isupper(c)?((c)+('a'-'A')):(c)) #include #define strrchr=rindex #endif #ifndef EOS # define EOS '\000' #endif #ifndef FALSE # define FALSE (0) #endif #ifndef TRUE # define TRUE (1) #endif /* * When calling Error(), the following flag bits define * optional processing. */ #define WARN (000001) /* This is a warning message */ #define ERR (000002) /* This is an error */ #define FATAL (000004) /* Fatal, die after message */ #define SYS (000010) /* System error number available */ /* * The two routines fwild() and fnext() are faked on unix. * Also faked on AMIGA for now, may do equivalent later. */ #if unix || AMIGA # define FAKEFWILD #endif #ifdef FAKEFWILD static FILE *fwild (); static FILE *fnext (); static void fgetname (); #else extern FILE *fwild (); /* Wild card file lookup */ extern FILE *fnext (); /* Open next wild card file */ extern void fgetname (); #endif #define TEMPNAME "ar.tmp" /* * arfile chains together strings of text. */ typedef struct arfile { struct arfile *l_next; /* -> next list element */ int l_flag; /* mark if found in archive */ char *l_arname; /* archive name argument */ char *l_filename; /* directory file name */ } ARFILE; /* * Global storage */ FILE *arfd = NULL; /* Archive */ FILE *newfd = NULL; /* New archive */ FILE *logfd; /* Log output */ char *logname; /* Name of log file */ int newarchive = FALSE; /* True if create from scratch */ int logging = FALSE; /* True if log file enabled */ char text[513]; /* Working text */ char arname[81]; /* Current archive member name */ char filename[81]; /* Working file name */ char arfilename[81]; /* Archive file name */ char *timetext; /* Time of day text */ int verbose = FALSE; /* TRUE for verbosity */ int delflag = 0; /* Delete files */ int directory = 0; /* Table of contents if lit */ int update = 0; /* Update files if lit */ int extract = 0; /* Get files from archive */ int print = 0; /* Write files to stdout */ int errorflag = 0; /* Set on fatal error */ ARFILE *list = NULL; /* String list header */ #ifdef unix # define delete unlink #endif /* * The following allow use on systems that don't have my macro based * debugging package. The default, for now, is to assume it is not * available. Fred Fish, 1-Dec-85 */ #ifdef DBUG # include #else /* !DBUG */ # define DBUG_ENTER(a) # define DBUG_RETURN(a) return(a) # define DBUG_VOID_RETURN return # define DBUG_2(a,b) # define DBUG_3(a,b,c) # define DBUG_4(a,b,c,d) # define DBUG_5(a,b,c,d,e) # define DBUG_PUSH(a) #endif /* DBUG */ /* * Declare internal functions that are used before definition seen. */ static char *GetTime (); /* Get current time as printable ascii */ static void Error (); /* Process an error or warning */ static void doupdate (); static void dodirectory (); static void dodelete (); static void doextract (); static int replace (); static int expandargs (); static int findfiles (); static int savestring (); static void dumplist (); static void usage (); static void notfound (); static int gethdr (); static int findarg (); static int compare (); static int addfile (); static void argetname (); static void filemove (); static void arcopy (); static void arimport (); static void arexport (); static int match (); static int breakout (); static int match1 (); /* * Library functions. */ extern char *malloc (); extern char *strcpy (); extern char *strchr (); extern int delete (); extern void free (); extern void exit (); #ifndef fflush /* Sometimes is a macro */ extern void fflush (); #endif /* * Main entry point. Note declaration is 'int' and a meaningful * value is actually returned. Generally this becomes the exit * status for the parent process. */ int main (argc, argv) int argc; /* Arg count */ char *argv[]; /* Arg vector */ { register int i; /* Random counter */ register char *argp; /* Arg pointer */ DBUG_ENTER ("main"); logfd = stderr; logname = "stderr"; timetext = GetTime (); for (i = 1; i < argc; i++) { if ((argp = argv[i]) == NULL) { continue; /* From log file writer */ } if (*argp == '-') { /* * Process options */ argv[i] = NULL; /* Erase it from file stuff */ while (*++argp != EOS) { switch (tolower (*argp)) { case '#': /* Can not bundle with other args! */ DBUG_PUSH (argp); argp = " "; /* Trickery to terminate arg */ break; case 'c': newarchive = TRUE; break; case 'd': /* Delete from archive */ delflag = 1; break; case 'p': /* Print on stdout */ print = 1; break; case 'l': /* List directory */ directory = 1; break; case 'h': /* Explicit usage requested */ usage (); exit (0); break; case 'i': /* Insert */ case 'r': /* Replace */ case 'u': /* Update modified */ update = 1; break; case 'v': /* Verbose */ verbose = 1; break; case 'x': /* Extract */ extract = 1; break; case 'z': /* Log file */ logname = argv[i + 1]; if ((logfd = fopen (logname, "w")) == NULL) { Error (ERR|SYS|FATAL, "can't create logfile '%s'", logname); } if (verbose) { fprintf (stderr, "writing log to %s\n", logname); } logging = TRUE; argv[i + 1] = NULL; break; default: Error (WARN|FATAL, "illegal option '%c', use -h for help", *argp); } } argv[i] = NULL; /* Erase argument */ } else if (arfd == NULL && newfd == NULL) { /* Not option */ /* * First file is the archive name */ if (newarchive || (arfd = fopen (argp, "r")) == NULL) { DBUG_3 ("new", "opening '%s' as new archive", argp); if ((newfd = fopen (argp, "w")) == NULL) { Error (ERR|FATAL|SYS, "can't create archive '%s'", argp); } else { newarchive = TRUE; if (verbose) { fprintf (logfd, "Creating new archive '%s'\n", argp); } } } argv[i] = NULL; /* Erase argument */ (void) strcpy (arfilename, argp); } } if (errorflag) { Error (ERR|FATAL, "previous error prevents continuation"); } if (!newarchive && arfd == NULL) { Error (ERR|FATAL, "no archive file specified, use -h for help"); } /* * Got all arguments. */ if ((i = delflag + directory + extract + print + update) > 1) { Error (ERR|FATAL, "illogical option combination"); } else if (i == 0) { if (verbose) { fprintf (logfd, "Update selected by default\n"); } update = 1; } if (!newarchive && (delflag || update)) { if ((newfd = fopen (TEMPNAME, "w")) == NULL) { Error (ERR|FATAL|SYS, "can't create work file '%s'", TEMPNAME); } } /* * Debugging verbosity. */ if (verbose) { fprintf (logfd, "You have selected:"); if (directory) fprintf (logfd, " directory"); if (delflag) fprintf (logfd, " delete"); if (extract) fprintf (logfd, " extract"); if (print) fprintf (logfd, " print"); if (update) fprintf (logfd, " update"); if (verbose) fprintf (logfd, " and verbosity"); fprintf (logfd, ".\nArchive file is \"%s\".\n", arfilename); } if (expandargs (argc, argv, update)) { Error (WARN, "errors found in arg expansion"); } if (newarchive && !update) { fprintf (logfd, "Dummy archive created\n"); (void) fclose (newfd); } else if (directory) { dodirectory (); } else if (delflag) { dodelete (); } else if (extract || print) { doextract (print); } else if (update) { doupdate (); } else { Error (FATAL|WARN, "no command was provided, use -h for help"); } DBUG_RETURN (0); } /* * Write a table of contents */ static void dodirectory () { DBUG_ENTER ("dodirectory"); text[0] = EOS; while (gethdr (arfd)) { if (findarg (arname, (char *) NULL)) { printf (text); } arcopy (arfd, (FILE *) NULL); /* Skip file */ } DBUG_VOID_RETURN; } /* * Delete named files -- gotta have a name list */ static void dodelete () { register int ecount; DBUG_ENTER ("dodelete"); if (list == NULL) { Error (ERR|FATAL, "delete by name only"); } ecount = replace (arfd, newfd, FALSE, 0); notfound (); (void) fclose (arfd); (void) fclose (newfd); if (ecount == 0) { filemove (TEMPNAME, arfilename); } else { Error (WARN, "errors prevent deletion of archive"); if (logging) { fprintf (logfd, "Errors prevent deletion of archive\n"); } if (delete (TEMPNAME) == -1) { Error (WARN|SYS, "can't delete '%s'", TEMPNAME); } } DBUG_VOID_RETURN; } /* * Extract or print named files */ static void doextract (printflag) int printflag; /* TRUE to print, FALSE to extract */ { register FILE *outfd; DBUG_ENTER ("doextract"); outfd = (printflag) ? stdout : NULL; text[0] = EOS; while (gethdr (arfd)) { if (!findarg (arname, (char *) NULL)) { if (verbose) { fprintf (logfd, "Skipping \"%s\"\n", arname); } arcopy (arfd, (FILE *) NULL); /* Skip */ } else { if (outfd != stdout) { if ((outfd = fopen (arname, "w")) == NULL) { Error (ERR|SYS, "can't create '%s'", arname); if (logging) { fprintf (logfd, "Can't create \"%s\"\n", arname); } arcopy (arfd, (FILE *) NULL); continue; } } if (verbose) { fprintf (logfd, "Creating \"%s\"\n", arname); } arexport (arfd, outfd); (void) fclose (outfd); outfd = NULL; } } DBUG_VOID_RETURN; } /* * Update existing files, add argv[1]..argv[argc-1] at end */ static void doupdate () { register int ecount; register ARFILE *lp; DBUG_ENTER ("doupdate"); ecount = 0; if (!newarchive) { DBUG_2 ("old", "update using existing archive"); ecount = replace (arfd, newfd, TRUE, 0); } for (lp = list; lp != NULL; lp = lp -> l_next) { if (!lp -> l_flag) { ecount += addfile (lp -> l_arname, lp -> l_filename, newfd, ecount, "Added"); lp -> l_flag = TRUE; } } if (newarchive) { DBUG_2 ("new", "new archive, no need to copy temp archive"); (void) fclose (newfd); if (ecount) { Error (WARN, "completed with %d errors", ecount); if (logging) { fprintf (stderr, "completed with %d errors\n", ecount); } } } else { DBUG_2 ("new", "copy temp archive to new archive"); (void) fclose (arfd); (void) fclose (newfd); if (ecount == 0) { filemove (TEMPNAME, arfilename); } else { Error (WARN|SYS, "move of '%s' to '%s' supressed because of errors", TEMPNAME, arfilename); if (logging) { fprintf (logfd, "Move of %s to %s supressed because of errors\n", TEMPNAME, arfilename); } } } DBUG_VOID_RETURN; } /* * Replace or delete files from the archive. The updated archive * is written to outfd. */ static int replace (infd, outfd, updateflag, ecount) FILE *infd; /* Reading files from here */ FILE *outfd; /* Writing files here */ int updateflag; /* TRUE to update, FALSE to remove */ int ecount; { DBUG_ENTER ("replace"); text[0] = EOS; /* Signal gethdr initialization */ while (gethdr (infd)) { /* * We have a file, is it selected? */ if (findarg (arname, filename)) { if (updateflag) { ecount += addfile (arname, filename, outfd, ecount, "Replaced"); } arcopy (infd, (FILE *) NULL); } else { /* * Not selected for update, copy to the new archive */ (void) fputs (text, outfd); arcopy (infd, outfd); } } DBUG_RETURN (ecount); } /* * Process the argv[] vector, building the argument list. * Note: argv[1] is the first argument -- argv[0] is untouched and * NULL entries in argv[] are ignored. * * If updateflag is TRUE, arguments are expanded against the file * directory (using fwild/fnext). If FALSE, they are used as is. * * Return TRUE if errors occurred. */ static int expandargs (argc, argv, updateflag) int argc; /* Number of arguments */ char *argv[]; /* Arg vector */ int updateflag; /* TRUE to trigger file search */ { register int in; register int eflag; DBUG_ENTER ("expandargs"); eflag = 0; for (in = 1; in < argc; in++) { if (argv[in] != NULL) { if (updateflag) { eflag += findfiles (argv[in]); } else { eflag += savestring (argv[in], (char *) NULL); } } } DBUG_RETURN (eflag != 0); } /* * Archive element names, do fwild lookup to expand wildcards where possible. */ static int findfiles (fname) char *fname; { register int i; register FILE *fd; DBUG_ENTER ("findfiles"); if ((fd = fwild (fname, "r")) == NULL) { Error (WARN|SYS, "can't open directory or file '%s'", fname); if (logging) { fprintf (stderr, "Can't open directory or wildcard file \"%s\"\n", fname); } DBUG_RETURN (1); } /* * Locate each file, then save archive and file names */ for (i = 0; fnext (fd) != NULL; i++) { argetname (fd, arname, filename); savestring (arname, filename); } if (i == 0) { Error (WARN, "no match for '%s'", fname); if (logging) { fprintf (stderr, "Warning, no match for \"%s\"\n", fname); } DBUG_RETURN (1); } else if (verbose) { fprintf (logfd, "%d file%s in your directory match%s \"%s\"\n", i, (i > 1) ? "s" : "", (i == 1) ? "es" : "", fname); DBUG_RETURN (0); } DBUG_RETURN (0); } /* * Insert text into the list in sorted order (on datum). * Warn (and fail on) duplicates. */ static int savestring (datum, file) char *datum; /* Archive element name */ char *file; /* May be NULL if not necessary */ { register ARFILE *next; register ARFILE **prev; register ARFILE *new; char *ardatum; char *arfile; int comp; DBUG_ENTER ("savestring"); arfile = NULL; if (file != NULL) { arfile = (char *) malloc ((unsigned) (strlen (file) + 1)); if (arfile == NULL) { Error (ERR|FATAL|SYS, "can't allocate any more memory"); } (void) strcpy (arfile, file); } if ((ardatum = (char *) malloc ((unsigned) (strlen (datum) + 1))) == NULL || (new = (ARFILE *) malloc (sizeof (ARFILE))) == NULL) { Error (ERR|FATAL|SYS, "can't allocate any more memory"); } (void) strcpy (ardatum, datum); new -> l_flag = FALSE; new -> l_arname = ardatum; new -> l_filename = arfile; prev = &list; next = list; while (next != NULL && (comp = compare (datum, next -> l_arname)) > 0) { if (comp == 0) { Error (WARN, "duplicate argument '%s'", datum); if (arfile) { free (arfile); } free (ardatum); free ((char *) new); DBUG_RETURN (TRUE); } prev = &next -> l_next; next = *prev; } *prev = new; new -> l_next = next; DBUG_RETURN (FALSE); } #ifdef DEADCODE /* Not used, leftover from what? (fnf) */ /* * Dump archive name list -- used for debugging only */ static void dumplist () { register ARFILE *lp; DBUG_ENTER ("dumplist"); if ((lp = list) == NULL) { Error (WARN, "list is empty"); } else { while (lp != NULL) { fprintf (stderr, "%s, \"%s\"", (lp -> l_flag) ? " found" : "not found", lp -> l_arname); if (lp -> l_filename == NULL) { fprintf (stderr, "\n"); } else { fprintf (stderr, "%s\n", lp -> l_filename); } lp = lp -> l_next; } } DBUG_VOID_RETURN; } #endif /* DEADCODE */ static char *documentation[] = { "Usage: portar -cdhilpruvx archive files", "", " c Create a new archive", " d Delete named files from archive", " h Print this help info", " i Insert named files into archive", " l List archive directory", " p Print named files on standard output", " r Replace named files", " u Update -- replace named files", " v Verbose -- give running commentary", " x Extract -- copy named files to current directory", " z Put logfile in file named in next argument", "", "i, r, and u, are identical", "", NULL }; static void usage () { register char **dp; DBUG_ENTER ("usage"); for (dp = documentation; *dp != NULL; dp++) { printf ("%s\n", *dp); } DBUG_VOID_RETURN; } static void Error (flags, fmt, arg1, arg2, arg3) int flags; char *fmt; char *arg1; char *arg2; char *arg3; { fprintf (stderr, "portar: "); if (flags & WARN) { fprintf (stderr, "warning -- "); } else if (flags & ERR) { fprintf (stderr, "error -- "); } fprintf (stderr, fmt, arg1, arg2, arg3); if (flags & SYS) { perror (""); } else { fprintf (stderr, "\n"); } (void) fflush (stderr); if (flags & FATAL) { exit (1); } } /* * Called from dodelete() to warn the user about files that were * to be deleted, but which were not in the archive. */ static void notfound () { register ARFILE *lp; DBUG_ENTER ("notfound"); for (lp = list; lp != NULL; lp = lp -> l_next) { if (!lp -> l_flag) { Error (WARN|SYS, "can't delete '%s'", lp -> l_arname); if (logging) { fprintf (stderr, "Can't delete \"%s\" -- not found\n", lp -> l_arname); } } } DBUG_VOID_RETURN; } /* * If text is null, read a record, returning TRUE if text contains a header. * Parse the header into arname. */ static int gethdr (fd) FILE *fd; { register char *tp; register char *np; DBUG_ENTER ("gethdr"); if (text[0] == EOS && fgets (text, (int) sizeof (text), fd) == NULL) { DBUG_RETURN (FALSE); } if (text[0] != '-' || text[1] != 'h' || text[2] != '-') { DBUG_RETURN (FALSE); } for (tp = &text[3]; *tp && *tp <= ' '; tp++); for (np = &arname[0]; *tp > ' '; *np++ = *tp++); *np = EOS; DBUG_RETURN (TRUE); } /* * If name is in the list, mark it as "found" and return TRUE. * If true, and fname is not NULL, fname will have the file argument. */ static int findarg (name, fname) char *name; char *fname; /* Gets full file name */ { register ARFILE *lp; DBUG_ENTER ("findarg"); if ((lp = list) == NULL) { if (fname != NULL) { fname[0] = '\000'; } DBUG_RETURN (TRUE); } while (lp != NULL) { if (match (name, lp -> l_arname)) { lp -> l_flag = TRUE; if (fname != NULL) { if (lp -> l_filename == NULL) { fname[0] = EOS; } else { (void) strcpy (fname, lp -> l_filename); } } DBUG_RETURN (TRUE); } lp = lp -> l_next; } DBUG_RETURN (FALSE); } /* * Compare strings (note: case insensitive) */ static int compare (string1, string2) register char *string1; register char *string2; { DBUG_ENTER ("compare"); while (tolower (*string1) == tolower (*string2)) { if (*string1 == NULL) { DBUG_RETURN (0); } string1++; string2++; } DBUG_RETURN ((tolower (*string1) > tolower (*string2)) ? 1 : -1); } /* * Add file "fname" (archive element "name") to the archive */ static int addfile (name, fname, outfd, ecount, why) char *name; /* Archive element name */ char *fname; /* Archive file name */ FILE *outfd; /* Output file, already open */ int ecount; /* Current error count (updated */ char *why; /* Why are we here -- for verbosity */ { register FILE *infd; DBUG_ENTER ("addfile"); if ((infd = fopen (fname, "r")) == NULL) { Error (WARN|SYS, "'%s' archive member '%s' not found", why, (fname == NULL) ? "{Null}" : fname); if (logging) { fprintf (stderr, "%s archive member \"%s\" not found\n", why, (fname == NULL) ? "{Null}" : fname); } ecount++; } else { #ifdef DECUS fgetname (infd, filename); #else (void) strcpy (filename, fname); #endif if (verbose) { fprintf (logfd, "%s archive member \"%s\" (%s)\n", why, name, filename); } fprintf (outfd, "-h- %s\t%s\t%s\n", name, timetext, filename); arimport (infd, outfd); (void) fclose (infd); } DBUG_RETURN (ecount); } /* * Get file name, stripping off device:[directory] and ;version. * The archive name ("FILE.EXT" is written to outname, while the * full file name is written to outfilename. On a dec operating system, * outname is forced to lowercase. */ static void argetname (fd, outname, outfilename) FILE *fd; char *outname; /* Archive name */ char *outfilename; /* Full file name */ { register char *tp; #ifndef unix char bracket; #endif extern char *strrchr (); DBUG_ENTER ("argetname"); fgetname (fd, outfilename); (void) strcpy (outname, outfilename); #ifdef unix /* * outname is after all directory information */ if ((tp = strrchr (outname, '/')) != NULL) { (void) strcpy (outname, tp + 1); } #else if ((tp = strrchr (outname, ';')) != NULL) { *tp = EOS; } while ((tp = strchr (outname, ':')) != NULL) { (void) strcpy (outname, tp + 1); } switch (outname[0]) { case '[': bracket = ']'; break; case '<': bracket = '>'; break; case '(': bracket = ')'; break; default: bracket = EOS; break; } if (bracket != EOS) { if ((tp = strchr (outname, bracket)) == NULL) { Error (WARN, "? Illegal file name '%s'", outfilename); } else { (void) strcpy (outname, tp + 1); } } for (tp = outname; *tp != EOS; tp++) { *tp = tolower (*tp); } #endif DBUG_VOID_RETURN; } /* * "Rename" inname to outname the hard way. */ static void filemove (inname, outname) char *inname; char *outname; { register FILE *infd; register FILE *outfd; long int nrecords; DBUG_ENTER ("filemove"); if (verbose) { fprintf (logfd, "Copying %s to %s\n", inname, outname); } if ((infd = fopen (inname, "r")) == NULL) { Error (ERR|FATAL|SYS, "can't open '%s' for input", inname); } if ((outfd = fopen (outname, "w")) == NULL) { Error (ERR|FATAL|SYS, "can't open '%s' for write", outname); } for (nrecords = 0; fgets (text, (int) sizeof (text), infd) != NULL; nrecords++) { (void) fputs (text, outfd); } #ifdef DECUS fgetname (infd, text); #else (void) strcpy (text, inname); #endif (void) fclose (infd); (void) fclose (outfd); if (delete (text) == -1) { Error (WARN|SYS, "can't delete '%s'", text); } if (verbose) { fprintf (logfd, "Archive %s contains %ld records.\n", outname, nrecords); } DBUG_VOID_RETURN; } /* * Copy (or skip if outfd == NULL) to next header */ static void arcopy (infd, outfd) register FILE *infd; register FILE *outfd; { DBUG_ENTER ("arcopy"); while (fgets (text, (int) sizeof (text), infd) != NULL) { if (text[0] == '-' && text[1] != '-') { DBUG_VOID_RETURN; } if (outfd != NULL) { (void) fputs (text, outfd); } } text[0] = EOS; /* EOF signal */ DBUG_VOID_RETURN; } /* * Import text, writing it in the secret ar format. */ static void arimport (infd, outfd) register FILE *infd; register FILE *outfd; { DBUG_ENTER ("arimport"); while (fgets (text, (int) sizeof (text), infd) != NULL) { if (text[0] == '-') { (void) putc ('-', outfd); /* Quote */ } (void) fputs (text, outfd); } DBUG_VOID_RETURN; } /* * Read secret archive format, writing archived data to outfd. * Clean out extraneous ,'s */ static void arexport (infd, outfd) register FILE *infd; register FILE *outfd; { register char *tp; DBUG_ENTER ("arexport"); while (fgets (text, (int) sizeof (text), infd) != NULL) { tp = &text[strlen (text)]; if (tp > &text[1] && *--tp == '\n' && *--tp == '\r') { *tp++ = '\n'; *tp = EOS; } if (text[0] == '-') { if (text[1] != '-') { DBUG_VOID_RETURN; } (void) fputs (text + 1, outfd); } else { (void) fputs (text, outfd); } } text[0] = EOS; DBUG_VOID_RETURN; } /* * Pattern match between * name string argument (FILE.EXT format) * pattern which may contain wildcards. * * Note: '*' matches all but '.' separator between file and ext. * '?' matches one character, but not '.' * */ typedef struct filename { char namepart[10]; char typepart[4]; } FILENAME; static int match (name, pattern) register char *name; register char *pattern; { FILENAME namebuff; FILENAME patternbuff; int result; DBUG_ENTER ("match"); if (breakout (name, &namebuff) || breakout (pattern, &patternbuff)) { result = FALSE; } else { result = (match1 (namebuff.namepart, patternbuff.namepart) && match1 (namebuff.typepart, patternbuff.typepart)); } DBUG_RETURN (result); } /* * Parse arg ("foo.bar") into "foo" and "bar" * Return TRUE if trouble. */ static int breakout (arg, buff) char *arg; FILENAME * buff; { register char *ap; register char *bp; register int dotseen; int size; DBUG_ENTER ("breakout"); dotseen = FALSE; ap = arg; bp = buff -> namepart; buff -> typepart[0] = EOS; size = (sizeof buff -> namepart) - 1; while (*ap != EOS) { if (*ap == '.') { if (dotseen++) { /* 2 dots */ DBUG_RETURN (TRUE); } else { ap++; *bp = EOS; bp = buff -> typepart; size = (sizeof buff -> typepart) - 1; continue; } } if (size-- <= 0) { /* 2 big */ DBUG_RETURN (TRUE); } *bp++ = *ap++; } *bp = EOS; DBUG_RETURN (FALSE); } /* * Recursive routine to match "name" against "pattern". * Returns TRUE if successful. */ static int match1 (name, pattern) register char *name; /* What to look for */ register char *pattern; /* May have wildcard */ { register char pattbyte; char namebyte; DBUG_ENTER ("match1"); for (;;) { /* * First check for pattern ending in '*' -- this has to succeed */ if ((pattbyte = *pattern++) == '*' && *pattern == EOS) { DBUG_RETURN (TRUE); } /* * If not, then if both strings finish equally, it succeeds. */ if ((namebyte = *name) == EOS && pattbyte == EOS) { DBUG_RETURN (TRUE); } /* * Not at end of the name string. */ switch (pattbyte) { case EOS: /* End of pattern -> failure */ DBUG_RETURN (FALSE); case '*': /* Wild card means "advance" */ do { if (match1 (name, pattern)) { DBUG_RETURN (TRUE); } } while (*name++ != EOS); DBUG_RETURN (FALSE); /* Did our best */ default: if (tolower (namebyte) != tolower (pattbyte)) { DBUG_RETURN (FALSE); } case '?': /* One byte joker */ case '%': /* RT11 one byte joker */ name++; /* Matched this one */ } } } #ifdef FAKEFWILD /* Set if a file is open */ /* 0 nothing open */ /* +1 open, fnext not called */ /* +2 fnext called once */ static int fake_flag = 0; static char fake_name[81]; /* Name of file */ /* * "setup" to open a wildcard file name */ static FILE *fwild (fname, mode) char *fname; char *mode; { register FILE *fd; DBUG_ENTER ("fwild"); if (fake_flag != 0) { Error (WARN, "fwild/fnext out of sync"); } fake_flag = 0; if ((fd = fopen (fname, mode)) != NULL) { fake_flag++; (void) strcpy (fake_name, fname); } DBUG_RETURN (fd); } static FILE *fnext (fd) FILE *fd; { DBUG_ENTER ("fnext"); switch (fake_flag) { case 1: fake_flag++; /* First call after fwild */ DBUG_RETURN (fd); /* File is "open" */ case 2: fake_flag = 0; /* Second call of fnext */ (void) fclose (fd); /* Close existing file */ fake_name[0] = EOS; /* Zap file name */ DBUG_RETURN ((FILE *)NULL); /* No more files left */ default: Error (WARN, "fnext called without calling fwild"); DBUG_RETURN ((FILE *) NULL); } } /* * Note, this only works for files opened via fwild/fnext. (fnf) */ static void fgetname (fd, name) FILE *fd; char *name; { if (fd != NULL) { (void) strcpy (name, fake_name); } } #endif /* FAKEFWILD */ #ifndef unix perror (sp) char *sp; { if (sp != NULL) { fprintf (stderr, "%s", sp); } fprintf (stderr, ": "); } #endif #ifdef AMIGA int delete (name) char *name; { int status; extern int DeleteFile (); DBUG_ENTER ("delete"); if (DeleteFile (name)) { /* Returns 0 or 1 */ status = 0; /* Success */ } else { status = -1; /* Failure */ } DBUG_RETURN (status); } #endif /* AMIGA */ /* * Setup the time of day, erasing trailing '\n' */ #ifdef AMIGA # undef TRUE /* TRUE, FALSE, and NULL are all redefined in types.h, */ # undef FALSE /* which is ultimately pulled in. They have the same */ # undef NULL /* numeric values but are not enclosed in parens. */ # include # include #endif static char *GetTime () { #ifdef unix register char *cp; register char *now; long timval; extern long time (); extern char *ctime (); (void) time (&timval); now = ctime (&timval); cp = now + strlen (now); while (cp > now && *--cp <= ' '); cp[1] = EOS; return (now); #else #ifdef AMIGA auto struct DateStamp now; static char buffer[64]; extern struct DateStamp *DateStamp (); if (DateStamp (&now) == NULL) { Error (WARN|SYS, "can't get current date"); now.ds_Days = 0; now.ds_Minute = 0; now.ds_Tick = 0; } /* Kinda ugly for now, just dump structure as ascii. */ sprintf (buffer, "%u:%u:%u", now.ds_Days, now.ds_Minute, now.ds_Tick); return (buffer); #else return ("