perlman@wivax.UUCP (12/16/84)
Here we go again. I've gotten enough sugestions for my shar.c that I am posting a new version. The minor changes are that some potential bugs were removed along with unused features. The major change is that shar now knows how to march through a directory hierarchy. This posting should supersede the versions posted recently. The traversal is done with a function for marching through directories. I hope people find it useful for other tasks. I am pretty sure about its behaviour on pre-Berkeley 4.2 UNIX, but not positive about it on Berkeley 4.2. If there are any bugs in it, please send fixes to me. Gary Perlman/Wang Institute/Tyngsboro, MA/01879/(617) 649-9731 shar -v shar.1 shar.c makefile traverse.3 traverse.c -----cut here-----cut here-----cut here-----cut here----- #!/bin/sh # shar: Shell Archiver # Run the following text with /bin/sh to create: # shar.1 # shar.c # makefile # traverse.3 # traverse.c echo shar: extracting shar.1 cat - << \SHAR_EOF > shar.1 .TH SHAR 1net "December 16, 1984" .SH NAME shar \- create storage archive for extraction by /bin/sh .SH SYNOPSIS .B shar [-v] files .SH DESCRIPTION shar prints its input files with special lines around them to be used by the shell (/bin/sh) to extract them later. The output can be filtered through the shell to recreate copies of the original files. The -v (verbose) option causes feedback messages about what shar is doing to be printed during extraction. .PP shar allows directories to be named, and shar prints the necessary commands to create new directories and fill them. .SH AUTHOR Gary Perlman (based on a shell version by James Gosling) .SH BUGS shar does not know anything about links between files, about binary files, or about overwriting existing files. SHAR_EOF echo shar: extracting shar.c cat - << \SHAR_EOF > shar.c #include <stdio.h> /* Shar puts readable text files together in a package from which they are easy to extract. The original version was a shell script posted to the net, shown below: #Date: Mon Oct 18 11:08:34 1982 #From: decvax!microsof!uw-beave!jim (James Gosling at CMU) AR=$1 shift for i do echo a - $i echo "echo x - $i" >>$AR echo "cat >$i <<'!Funky!Stuff!'" >>$AR cat $i >>$AR echo "!Funky!Stuff!" >>$AR done I rewrote this version in C to provide better diagnostics and to run faster. The major difference is that my version does not affect any files because it prints to the standard output. Mine also has a -v (verbose) option. Gary Perlman/Wang Institute/Tyngsboro, MA/01879/(617) 649-9731 Some enhancements motivated by Michael Thompson. I did not put in his feature of using sed to strip a prefix character because: (1) I did not want the shar scripts to depend on sed because some systems do not have it. (2) The convention arose so that people would not have to worry about the end of input delimiter being part of the line. This is unlikely and better handled by having a programmable delimiter. I don't think even that is necessary. Directory archiving motivated by Derek Zahn @ wisconsin His version had some problems, so I wrote a general routine for traversing a directory hierarchy. It allows marching through a directory on old and new UNIX systems. */ #define DELIM "SHAR_EOF" /* put after each file */ #define SHAR "shar" /* the name of this program */ #define READ_PERMISSION 4 /* access permission */ int Verbose = 0; /* option to provide append/extract feedback */ main (argc, argv) char **argv; { int status = 0; if (!strcmp (argv[1], "-v")) { Verbose = 1; argc--; argv++; } if (argc == 1) { fprintf (stderr, "%s: No input files\n", SHAR); fprintf (stderr, "USAGE: %s [-v] files > archive\n", SHAR); exit (1); } if (header (argc, argv)) exit (2); while (--argc) status += shar (*++argv); footer (); exit (status); } header (argc, argv) char **argv; { int i; int problems = 0; for (i = 1; i < argc; i++) if (access (argv[i], READ_PERMISSION)) { fprintf (stderr, "%s: Can't read %s\n", SHAR, argv[i]); problems++; } if (problems) return (problems); puts ("-----cut here-----cut here-----cut here-----cut here-----"); puts ("#!/bin/sh"); printf ("# %s: Shell Archiver\n", SHAR); puts ("#\tRun the following text with /bin/sh to create:"); for (i = 1; i < argc; i++) printf ("#\t%s\n", argv[i]); return (0); } footer () { puts ("#That's all folks!"); puts ("exit 0"); } archive (file) char *file; { char line[BUFSIZ]; FILE *ioptr; if (ioptr = fopen (file, "r")) { printf ("cat - << \\%s > %s\n", DELIM, file); while (fgets (line, BUFSIZ, ioptr)) fputs (line, stdout); (void) fclose (ioptr); puts (DELIM); return (0); } else { fprintf (stderr, "%s: Can't open %s\n", SHAR, file); return (1); } } shar (file) char *file; { int rshar (); traverse (file, rshar); } rshar (file, type, place) { if (!strcmp (file, ".")) return; if (place == 0) { if (type == 'd') { if (Verbose) printf ("echo %s: creating directory %s\n", SHAR, file); printf ("mkdir %s\n", file); printf ("chdir %s\n", file); } else /* type == 'f' */ { if (Verbose) printf ("echo %s: extracting %s\n", SHAR, file); archive (file); } } else /* place == 1 */ { if (type == 'd') { if (Verbose) printf ("echo %s: done with directory %s\n", SHAR, file); printf ("chdir ..\n"); } } } SHAR_EOF echo shar: extracting makefile cat - << \SHAR_EOF > makefile # The SYSTEM variable should be set to BSD4_2 to use the new routines # specific to that release. Otherwise, the value doesn't matter. SYSTEM=BSD4_1 CFLAGS=-O shar: shar.o traverse.o cc -o shar shar.o traverse.o traverse.o: traverse.c cc -D$(SYSTEM) -c $(CFLAGS) traverse.c SHAR_EOF echo shar: extracting traverse.3 cat - << \SHAR_EOF > traverse.3 .TH TRAVERSE 3WI "December 16, 1984" .SH NAME traverse \- recursively traverse a directory .SH SYNOPSIS .nf traverse (path, func) char *path; int (*func) (); func (path, filetype, position) char *path; .fi .SH DESCRIPTION traverse applies its argument function func to its argument file pathname path. If path is a directory, then traverse applies func to all its entries. .PP The argument func should take three parameters: a file name, a file type, and a position. The call looks like this for directories: .ce (*func) (path, 'd', position); and like this for other files: .ce (*func) (path, 'f', position); The position is 0 when path is first encountered and 1 when traverse is done. This is used to allow processing before and after a directory is processed. .SH EXAMPLE .nf list (name, type, pos) char *name; { if (type == 'd') printf ("%s %s\en", pos ? "Leaving" : "Entering", name); else /* type == 'f' */ printf (" %s\en", name); } .fi .SH AUTHOR Gary Perlman .SH BUGS There are no diagnostics when directories cannot be searched. SHAR_EOF echo shar: extracting traverse.c cat - << \SHAR_EOF > traverse.c /*LINTLIBRARY*/ #include <stdio.h> #include <sys/types.h> #include <sys/dir.h> #ifdef BSD4_2 #define namedir(entry) (entry->d_name) #define MAXNAME 256 #else #define DIR FILE #define MAXNAME (DIRSIZ+2) #define opendir(path) fopen (path, "r") #define closedir(dirp) fclose (dirp) struct direct * readdir (dirp) DIR *dirp; { static struct direct entry; if (dirp == NULL) return (NULL); for (;;) { if (fread (&entry, sizeof (struct direct), 1, dirp) == 0) return (NULL); if (entry.d_ino) return (&entry); } } char *strncpy (); char * namedir (entry) struct direct *entry; { static char name[MAXNAME]; return (strncpy (name, entry->d_name, DIRSIZ)); } #endif #include <sys/stat.h> #define isdir(path) (stat(path, &buf) ? 0 : (buf.st_mode&S_IFMT)==S_IFDIR) traverse (path, func) char *path; int (*func) (); { DIR *dirp; struct direct *entry; struct stat buf; int filetype = isdir (path) ? 'd' : 'f'; (*func) (path, filetype, 0); if (filetype == 'd') { if (chdir (path) == 0) { if (dirp = opendir (".")) { while (entry = readdir (dirp)) { char name[MAXNAME]; (void) strcpy (name, namedir (entry)); if (strcmp(name, ".") && strcmp(name, "..")) traverse (name, func); } (void) closedir(dirp); } (void) chdir (".."); } } (*func) (path, filetype, 1); } SHAR_EOF #That's all folks! exit 0