Path: seismo!harvard!husc6!talcott!panda!genrad!decvax!decwrl!dec-rhea!dec-pauper!minow (Martin Minow) From: sources-request@panda.UUCP Newsgroups: mod.sources Subject: archx: suggested replacement for shar Message-ID: <1769@panda.UUCP> Date: 1 May 86 12:39:44 GMT Sender: jpn@panda.UUCP Lines: 769 Approved: jpn@panda.UUCP Mod.sources: Volume 4, Issue 80 Submitted by: decvax!decwrl!dec-rhea!dec-pauper!minow (Martin Minow) [ This is one of two submissions of programs that provide alternatives to "shar" as a source archive format. This does not imply my support of one format over another. Oh, I am distributing these in "shar" format to avoid the chicken-or-egg problem. - John Nelson - moderator, mod.sources ] #! /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 the files: # readme.txt # archc.c # archx.c # This archive created: Thu May 1 08:33:31 1986 export PATH; PATH=/bin:$PATH echo shar: extracting "'readme.txt'" '(1420 characters)' if test -f 'readme.txt' then echo shar: will not over-write existing file "'readme.txt'" else cat << \SHAR_EOF > 'readme.txt' This is a suggested replacement for shar. It is based on the archive program in Kernighan and Plauger's Software Tools, but has been heavily simplified. It has the following advantages over shar: 1. it is not tied to Unix -- thus VMS users can unpack files without excessive effort. Archc and archx should run without change on all Unix and Unix lookalike systems, as well as on VMS (VaxC) and all PDP-11 Decus C systems. It has been in use for over 6 years. 2. it does not execute the distributed image, but interprets it. This means that trojan horses cannot be concealed in distributions. 3 The distribution file can be edited without damaging the archive. (Also, embedded archives can be handled). It has the following disadvantages: 1. It is not as flexible as shar -- it cannot create directories or access any other Unix system services. 2. There is no checksum capability (it appears impossible to implement checksumming in a system-independent manner). To use, save this message. Then, use your favorite editor to extract archx.c (delimited by lines beginning with "-h-" in column 1). Then compile archx and run it using the command: archx It should produce readme.txt, archx.c, and archc.c. Manual pages can be produced by extracting the text delimited by #ifdef DOCUMENTATION ... #endif Please report problems to the author: Martin Minow decvax!minow SHAR_EOF if test 1420 -ne "`wc -c < 'readme.txt'`" then echo shar: error transmitting "'readme.txt'" '(should have been 1420 characters)' fi fi echo shar: extracting "'archc.c'" '(7759 characters)' if test -f 'archc.c' then echo shar: will not over-write existing file "'archc.c'" else cat << \SHAR_EOF > 'archc.c' /* * A R C H I V E * * Create an archive * */ /*)BUILD $(TKBOPTIONS) = { TASK = ...ARC } */ #ifdef DOCUMENTATION title archc text file archive creation index text file archive creation synopsis archc file[s] >archive description Archc manages archives (libraries) of source files, allowing a large number of small files to be stored without using excessive system resources. It copies the set of named files to standard output in archive format. The archx program will recreate the files from an archive. Note: there are no checks against the same file appearing twice in an archive. archive file format Archive files are standard text files. Each archive element is preceeded by a line of the format: .s.nf -h- file.name date true_path_name .s.f 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. Certain bytes at the beginning of a record are also prefixed by '-' to prevent mailers from treating them as commands. diagnostics Diagnostic messages should be self-explanatory author Martin Minow #endif #include #include #ifdef vms #include #include #define IO_SUCCESS (SS$_NORMAL | STS$M_INHIB_MSG) #define IO_ERROR SS$_ABORT #endif /* * Note: IO_SUCCESS and IO_ERROR are defined in the Decus C stdio.h file */ #ifndef IO_SUCCESS #define IO_SUCCESS 0 #endif #ifndef IO_ERROR #define IO_ERROR 1 #endif #define EOS 0 #define FALSE 0 #define TRUE 1 char text[513]; /* Working text */ char name[81]; /* Current archive member name */ char pathname[81]; /* Output for argetname() */ char *timetext; /* Time of day text */ int verbose = TRUE; /* TRUE for verbosity */ FILE *infd; /* Input file */ main(argc, argv) int argc; /* Arg count */ char *argv[]; /* Arg vector */ { register int i; /* Random counter */ register char *fn; /* File name pointer */ register char *argp; /* Arg pointer */ int nfiles; extern char *ctime(); extern long time(); long timval; time(&timval); timetext = ctime(&timval); timetext[24] = EOS; #ifdef vms argc = getredirection(argc, argv); #endif if (argc <= 1) fprintf(stderr, "No files to archive?\n"); #ifdef unix for (i = 1; i < argc; i++) { if ((infd = fopen(argv[i], "r")) == NULL) perror(argv[i]); else { strcpy(pathname, argv[i]); import(); fclose(infd); } } #else /* * Decus C supports fwild/fnext for explicit processing * of wild-card filenames. */ for (i = 1; i < argc; i++) { if ((infd = fwild(argv[i], "r")) == NULL) perror(argv[i]); else { for (nfiles = 0; fnext(infd) != NULL; nfiles++) { fgetname(infd, pathname); import(); } fclose(infd); if (nfiles == 0) fprintf(stderr, "No files match \"%s\"\n", argv[i]); } } #endif } import() /* * Add the file open on infd (with file name in pathname) to * the archive. */ { unsigned int nrecords; fixname(); nrecords = 0; printf("-h- %s\t%s\t%s\n", name, timetext, pathname); while (fgets(text, sizeof text, infd) != NULL) { switch (text[0]) { case '-': case '.': case '~': putchar('-'); /* Quote */ } fputs(text, stdout); nrecords++; } if (ferror(infd)) { perror(name); fprintf(stderr, "Error when importing a file\n"); } if (verbose) { fprintf(stderr, "%u records read from %s\n", nrecords, pathname); } } fixname() /* * Get file name (in pathname), stripping off device:[directory] * and ;version. The archive name ("file.ext") is written to name[]. * On a dec operating system, name is forced to lowercase. */ { register char *tp; register char *ip; char bracket; extern char *strrchr(); #ifdef unix /* * name is after all directory information */ if ((tp = strrchr(pathname, '/')) != NULL) tp++; else tp = pathname; strcpy(name, tp); #else strcpy(name, pathname); if ((tp = strrchr(name, ';')) != NULL) *tp = EOS; while ((tp = strchr(name, ':')) != NULL) strcpy(name, tp + 1); switch (name[0]) { case '[': bracket = ']'; break; case '<': bracket = '>'; break; case '(': bracket = ')'; break; default: bracket = EOS; break; } if (bracket != EOS) { if ((tp = strchr(name, bracket)) == NULL) { fprintf(stderr, "? Illegal file name \"%s\"\n", pathname); } else { strcpy(name, tp + 1); } } for (tp = name; *tp != EOS; tp++) { if (isupper(*tp)) *tp = tolower(*tp); } #endif } #ifdef unix char * strrchr(stng, chr) register char *stng; register char chr; /* * Return rightmost instance of chr in stng. * This has the wrong name on some Unix systems. */ { register char *result; result = NULL; do { if (*stng == chr) result = stng; } while (*stng++ != EOS); return (result); } #endif /* * getredirection() is intended to aid in porting C programs * to VMS (Vax-11 C) which does not support '>' and '<' * I/O redirection. With suitable modification, it may * useful for other portability problems as well. */ static int getredirection(argc, argv) int argc; char **argv; /* * Process vms redirection arg's. Exit if any error is seen. * If getredirection() processes an argument, it is erased * from the vector. getredirection() returns a new argc value. * * Warning: do not try to simplify the code for vms. The code * presupposes that getredirection() is called before any data is * read from stdin or written to stdout. * * Normal usage is as follows: * * main(argc, argv) * int argc; * char *argv[]; * { * argc = getredirection(argc, argv); * } */ { #ifdef vms register char *ap; /* Argument pointer */ int i; /* argv[] index */ int j; /* Output index */ int file; /* File_descriptor */ for (j = i = 1; i < argc; i++) { /* Do all arguments */ switch (*(ap = argv[i])) { case '<': /* ': /* >file or >>file */ if (*++ap == '>') { /* >>file */ /* * If the file exists, and is writable by us, * call freopen to append to the file (using the * file's current attributes). Otherwise, create * a new file with "vanilla" attributes as if * the argument was given as ">filename". * access(name, 2) is TRUE if we can write on * the specified file. */ if (access(++ap, 2) == 0) { if (freopen(ap, "a", stdout) != NULL) break; /* Exit case statement */ perror(ap); /* Error, can't append */ exit(IO_ERROR); /* After access test */ } /* If file accessable */ } /* * On vms, we want to create the file using "standard" * record attributes. create(...) creates the file * using the caller's default protection mask and * "variable length, implied carriage return" * attributes. dup2() associates the file with stdout. */ if ((file = creat(ap, 0, "rat=cr", "rfm=var")) == -1 || dup2(file, fileno(stdout)) == -1) { perror(ap); /* Can't create file */ exit(IO_ERROR); /* is a fatal error */ } /* If '>' creation */ break; /* Exit case test */ default: argv[j++] = ap; /* Not a redirector */ break; /* Exit case test */ } } /* For all arguments */ return (j); #else /* * Note: argv[] is referenced to fool the Decus C * syntax analyser, supressing an unneeded warning * message. */ return (argv[0], argc); /* Just return as seen */ #endif } SHAR_EOF if test 7759 -ne "`wc -c < 'archc.c'`" then echo shar: error transmitting "'archc.c'" '(should have been 7759 characters)' fi fi echo shar: extracting "'archx.c'" '(7837 characters)' if test -f 'archx.c' then echo shar: will not over-write existing file "'archx.c'" else cat << \SHAR_EOF > 'archx.c' /* * A R C H X * * Archive extraction * */ /* * Note: the )BUILD comment is extracted by a Decus C tool to construct * system-dependent compiler command lines. * * Text inside #ifdef DOCUMENTATION is converted to runoff by a * Decus C tool. */ /*)BUILD $(TKBOPTIONS) = { TASK = ...ARX } */ #ifdef DOCUMENTATION title archx text file archiver extraction index text file archiver extraction synopsis archx archive_files description Archx manages archives (libraries) of source files, allowing a large number of small files to be stored without using excessive system resources. Archx extracts all files from an archive. If no archive_name file is given, the standard input is read. Archive header records are echoed to the standard output. archive file format Archive files are standard text files. Each archive element is preceeded by a line of the format: .s.nf -h- file.name date true_name .s.f 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 some operating systems, file.name is forced to lowercase. The archive builder (archc) may prefix other characters by '-'. If the first non-blank line of an input file does not begin with "-h", the text will be appended to "archx.tmp" This is needed if archives are distributed by mail and arrive with initial routing and subject information. diagnostics Diagnostic messages should be self-explanatory author Martin Minow bugs #endif #include #include #ifdef vms #include #include #define IO_SUCCESS (SS$_NORMAL | STS$M_INHIB_MSG) #define IO_ERROR SS$_ABORT #endif /* * Note: IO_SUCCESS and IO_ERROR are defined in the Decus C stdio.h file */ #ifndef IO_SUCCESS #define IO_SUCCESS 0 #endif #ifndef IO_ERROR #define IO_ERROR 1 #endif #define EOS 0 #define FALSE 0 #define TRUE 1 /* * The following status codes are returned by gethdr() */ #define DONE 0 #define GOTCHA 1 #define NOGOOD 2 char text[513]; /* Working text line */ char name[81]; /* Current archive member name */ char filename[81]; /* Working file name */ char arfilename[81]; /* Archive file name */ char fullname[81]; /* Output for argetname() */ int verbose = TRUE; /* TRUE for verbosity */ int first_archive; /* For mail header skipping */ main(argc, argv) int argc; /* Arg count */ char *argv[]; /* Arg vector */ { register int i; /* Random counter */ int status; /* Exit status */ #ifdef vms argc = getredirection(argc, argv); #endif status = IO_SUCCESS; if (argc == 1) process(); else { for (i = 1; i < argc; i++) { if (freopen(argv[i], "r", stdin) != NULL) process(); else { perror(argv[i]); status = IO_ERROR; } } } exit(status); } process() /* * Process archive open on stdin */ { register char *fn; /* File name pointer */ register FILE *outfd; register int i; text[0] = EOS; while ((i = gethdr()) != DONE) { switch (i) { case GOTCHA: if ((outfd = fopen(name, "w")) == NULL) { perror(name); fprintf(stderr, "Can't create \"%s\"\n", name); arskip(); continue; } break; case NOGOOD: fprintf(stderr, "Missing -h-, writing to archx.tmp\n"); fprintf(stderr, "Current text line: %s", text); strcpy(name, "archx.tmp"); if ((outfd = fopen(name, "a")) == NULL) { perror(name); fprintf(stderr, "Cannot append to %s\n", name); arskip(); continue; } break; } arexport(outfd); fclose(outfd); } } int gethdr() /* * If text is null, read a record, returning to signal input state: * DONE Eof read * NOGOOD -h- wasn't first non-blank line. Line is in text[] * GOTCHA -h- found, parsed into name. */ { register char *tp; register char *np; again: if (text[0] == EOS && fgets(text, sizeof text, stdin) == NULL) return (DONE); if (text[0] == '\n' && text[1] == EOS) { text[0] = EOS; goto again; } if (text[0] != '-' || text[1] != 'h' || text[2] != '-') return (NOGOOD); for (tp = &text[3]; isspace(*tp); tp++) ; for (np = name; !isspace(*tp); *np++ = *tp++) ; *np = EOS; return (GOTCHA); } arskip() /* * Skip to next header */ { while (fgets(text, sizeof text, stdin) != NULL) { if (text[0] == '-' && text[1] == 'h' && text[2] == '-') return; } text[0] = EOS; /* EOF signal */ } arexport(outfd) register FILE *outfd; /* * Read secret archive format, writing archived data to outfd. * Clean out extraneous ,'s */ { register char *tp; unsigned int nrecords; printf("Creating \"%s\", ", name); nrecords = 0; while (fgets(text, sizeof text, stdin) != NULL) { tp = &text[strlen(text)]; if (tp > &text[1] && *--tp == '\n' && *--tp == '\r') { *tp++ = '\n'; *tp = EOS; } if (text[0] == '-') { if (text[1] == 'h') goto gotcha; fputs(text+1, outfd); } else { fputs(text, outfd); } nrecords++; } text[0] = EOS; gotcha: printf("%u records\n", nrecords); if (ferror(stdin) || ferror(outfd)) printf("Creation of \"%s\" completed with error\n", name); } /* * getredirection() is intended to aid in porting C programs * to VMS (Vax-11 C) which does not support '>' and '<' * I/O redirection. With suitable modification, it may * useful for other portability problems as well. */ #ifdef vms static int getredirection(argc, argv) int argc; char **argv; /* * Process vms redirection arg's. Exit if any error is seen. * If getredirection() processes an argument, it is erased * from the vector. getredirection() returns a new argc value. * * Warning: do not try to simplify the code for vms. The code * presupposes that getredirection() is called before any data is * read from stdin or written to stdout. * * Normal usage is as follows: * * main(argc, argv) * int argc; * char *argv[]; * { * argc = getredirection(argc, argv); * } */ { register char *ap; /* Argument pointer */ int i; /* argv[] index */ int j; /* Output index */ int file; /* File_descriptor */ for (j = i = 1; i < argc; i++) { /* Do all arguments */ switch (*(ap = argv[i])) { case '<': /* ': /* >file or >>file */ if (*++ap == '>') { /* >>file */ /* * If the file exists, and is writable by us, * call freopen to append to the file (using the * file's current attributes). Otherwise, create * a new file with "vanilla" attributes as if * the argument was given as ">filename". * access(name, 2) is TRUE if we can write on * the specified file. */ if (access(++ap, 2) == 0) { if (freopen(ap, "a", stdout) != NULL) break; /* Exit case statement */ perror(ap); /* Error, can't append */ exit(IO_ERROR); /* After access test */ } /* If file accessable */ } /* * On vms, we want to create the file using "standard" * record attributes. create(...) creates the file * using the caller's default protection mask and * "variable length, implied carriage return" * attributes. dup2() associates the file with stdout. */ if ((file = creat(ap, 0, "rat=cr", "rfm=var")) == -1 || dup2(file, fileno(stdout)) == -1) { perror(ap); /* Can't create file */ exit(IO_ERROR); /* is a fatal error */ } /* If '>' creation */ break; /* Exit case test */ default: argv[j++] = ap; /* Not a redirector */ break; /* Exit case test */ } } /* For all arguments */ return (j); } #endif SHAR_EOF if test 7837 -ne "`wc -c < 'archx.c'`" then echo shar: error transmitting "'archx.c'" '(should have been 7837 characters)' fi fi exit 0 # End of shell archive