Trailing-Edge
-
PDP-10 Archives
-
decuslib20-10
-
decus/20-189/par.c
There are no other files named par.c in the archive.
/*
*
*
* 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 <stdio.h>
#ifndef EOS
# define EOS '\000'
#endif
#ifndef FALSE
# define FALSE (0)
#endif
#ifndef TRUE
# define TRUE (1)
#endif
/*
* The following changes are needed to compile and link portar.c under
* Berkeley UNIX 4.2bsd. Add the following lines at the beginning of
* the program:
*
* Then compile and link as follows:
*
* cc -O -o portar -DBSD portar.c
*
* Now you should be able to archive file on the vax, upload them to the
* amiga, and the de-archive them on the amiga.
*
* Marco Papa
* USC - Computer Science Dept.
*
* UUCP: ...!{decvax,ucbvax,hplabs}!sdcsvax!sdcrdcf!uscvax!papa
* ...!{allegra,trwrb,trwspp,ism780,cithep,oberon}!uscvax!papa
* CSNET: [email protected]
* ARPA: papa%[email protected]
*/
#ifdef BSD
#include <ctype.h>
#undef tolower
#define tolower(c) (isupper(c)?((c)+('a'-'A')):(c))
#include <strings.h>
#define strrchr=rindex
#endif
/* Hack for PCC-20 */
#ifdef TOPS20
#define void int
#include <ctype.h>
#define delete unlink
#undef tolower
#define tolower(c) (isupper(c)?((c)+('a'-'A')):(c))
/* Need to fake these because pcc doesn't provide them */
char *strchr(s,c)
char *s,c;
{
register char *t;
t = s;
while (*t)
if (*(t++) == c)
return t - 1;
return NULL;
}
char *strrchr(s,c)
char *s, c;
{
register char *t;
t = s + strlen(s) - 1; /* point at end of string */
while (t >= s)
if (*(t--) == 'c')
return t+1;
return NULL; /* failure */
}
#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,
* Ditto with PCC-20 (MPK)
*/
#if unix || AMIGA || TOPS20
# 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 <local/dbug.h>
#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) {
for (;;) { /* Search for first header MPK 16-Jan-86 */
if (fgets (text, (int) sizeof (text), fd) == NULL)
DBUG_RETURN (FALSE);
if ((text[0] == '-') && (text[1] == 'h') && (text[2] == '-'))
break; /* found a header */
}
}
else /* check current line for header */
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 <cr>,<lf>'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, ": <unknown error>");
}
#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 <libraries/dosextens.h>
# include <libraries/dos.h>
#endif
#ifdef TOPS20
#include <jsys.h>
#define left(n) ((n) >> 18)
#define right(n) ((n) & 0777777)
char *days[7] = { "Mon","Tue","Wed","Thu","Fri","Sat","Sun" };
char *months[12] = { "Jan","Feb","Mar","Apr","May","Jun",
"Jul","Aug","Sep","Oct","Nov","Dec" };
int align; /* Encourage pcc-20 to align next instruction (?) */
#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
#ifdef TOPS20
/* MPK 16-Jan-86 */
int ACin[5], ACout[5];
int curtime, year, day, month, weekday, hour, minute, second;
static char buffer[70]; /* nice and big */
/* Get date and time */
jsys (JSgtad,ACin,ACout);
ACin[2] = ACout[1];
ACin[4] = 0; /* Apply DST as appropriate */
jsys (JSodcnv,ACin,ACout); /* Convert the time */
/* Parse all the fields */
year = left(ACout[2]);
month = right(ACout[2]); /* Month (0-11) in right */
day = left(ACout[3]);
weekday = right(ACout[3]); /* 0 = monday */
ACout[4] = right(ACout[4]); /* Mask of left half */
hour = (ACout[4] / 3600) % 3600;
minute = (ACout[4] / 60) % 60;
second = ACout[4] % 60;
sprintf (buffer,"%3s %3s %2d %2d:%02d:%02d %4d",
days[weekday], months[month],day,hour,minute,second,year);
return (buffer);
#endif
#else
return ("<time currently unavailable>");
#endif /* AMIGA */
#endif /* unix */
}