michal@kuhub.cc.ukans.edu (Merlin The Magician) (04/27/89)
Dear Unix Community, I need to create a "shell archive". problem is I don't have the "shar" prorgam to do it. Any idea where I can get it ? I have a book on Unix that states that there is a shar command. Altough this indicates that it should be a more or less common tool on all Unix systems, mine does not have it. -Merlin michal@kuhub.cc.ukans.edu chmielew@cs.ukans.edu
bickel@nprdc.arpa (Steven Bickel) (04/28/89)
In article <5992@kuhub.cc.ukans.edu> michal@kuhub.cc.ukans.edu (Merlin The Magician) writes: >Dear Unix Community, > I need to create a "shell archive". problem is I don't have the >"shar" prorgam to do it. Any idea where I can get it ? > I have a book on Unix that states that there is a shar command. Altough >this indicates that it should be a more or less common tool on all >Unix systems, mine does not have it. >-Merlin >michal@kuhub.cc.ukans.edu >chmielew@cs.ukans.edu #! /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: # shar # This archive created: Thu Apr 27 12:17:42 1989 export PATH; PATH=/bin:/usr/bin:$PATH if test ! -d 'shar' then mkdir 'shar' fi cd 'shar' if test -f 'MANIFEST' then echo shar: "will not over-write existing file 'MANIFEST'" else cat << \SHAR_EOF > 'MANIFEST' File Name Archive # Description ----------------------------------------------------------- MANIFEST 1 This shipping list getopt.3 1 getopt.c 1 makefile 1 shar.1 1 shar.c 1 traverse.3 1 traverse.c 1 SHAR_EOF fi if test -f 'getopt.3' then echo shar: "will not over-write existing file 'getopt.3'" else cat << \SHAR_EOF > 'getopt.3' .TH GETOPT 3 local .DA 25 March 1982 .SH NAME getopt \- get option letter from argv .SH SYNOPSIS .ft B int getopt(argc, argv, optstring) .br int argc; .br char **argv; .br char *optstring; .sp extern char *optarg; .br extern int optind; .ft .SH DESCRIPTION .I Getopt returns the next option letter in .I argv that matches a letter in .IR optstring . .I Optstring is a string of recognized option letters; if a letter is followed by a colon, the option is expected to have an argument that may or may not be separated from it by white space. .I Optarg is set to point to the start of the option argument on return from .IR getopt . .PP .I Getopt places in .I optind the .I argv index of the next argument to be processed. Because .I optind is external, it is normally initialized to zero automatically before the first call to .IR getopt . .PP When all options have been processed (i.e., up to the first non-option argument), .I getopt returns .BR EOF . The special option .B \-\- may be used to delimit the end of the options; .B EOF will be returned, and .B \-\- will be skipped. .SH SEE ALSO getopt(1) .SH DIAGNOSTICS .I Getopt prints an error message on .I stderr and returns a question mark .RB ( ? ) when it encounters an option letter not included in .IR optstring . .SH EXAMPLE The following code fragment shows how one might process the arguments for a command that can take the mutually exclusive options .B a and .BR b , and the options .B f and .BR o , both of which require arguments: .PP .RS .nf main(argc, argv) int argc; char **argv; { int c; extern int optind; extern char *optarg; \&. \&. \&. while ((c = getopt(argc, argv, "abf:o:")) != EOF) switch (c) { case 'a': if (bflg) errflg++; else aflg++; break; case 'b': if (aflg) errflg++; else bproc(); break; case 'f': ifile = optarg; break; case 'o': ofile = optarg; break; case '?': default: errflg++; break; } if (errflg) { fprintf(stderr, "Usage: ..."); exit(2); } for (; optind < argc; optind++) { \&. \&. \&. } \&. \&. \&. } .RE .PP .SH HISTORY Written by Henry Spencer, working from a Bell Labs manual page. Behavior believed identical to the Bell version. .SH BUGS It is not obvious how `\-' standing alone should be treated; this version treats it as a non-option argument, which is not always right. .PP Option arguments are allowed to begin with `\-'; this is reasonable but reduces the amount of error checking possible. .PP .I Getopt is quite flexible but the obvious price must be paid: there is much it could do that it doesn't, like checking mutually exclusive options, checking type of option arguments, etc. SHAR_EOF fi if test -f 'getopt.c' then echo shar: "will not over-write existing file 'getopt.c'" else cat << \SHAR_EOF > 'getopt.c' /* I got this off net.sources from Henry Spencer. It is a public domain getopt(3) like in System V. I have made the following modifications: index(s,c) was added because too many people could not compile getopt without it. A test main program was added, ifdeffed by GETOPT. This main program is a public domain implementation of the getopt(1) program like in System V. The getopt program can be used to standardize shell option handling. e.g. cc -DGETOPT getopt.c -o getopt */ #include <stdio.h> #ifndef lint static char sccsfid[] = "@(#) getopt.c 5.0 (UTZoo) 1985"; #endif #define ARGCH (int)':' #define BADCH (int)'?' #define EMSG "" #define ENDARGS "--" /* this is included because index is not on some UNIX systems */ static char * index (s, c) register char *s; register int c; { while (*s) if (c == *s) return (s); else s++; return (NULL); } /* * get option letter from argument vector */ int opterr = 1, /* useless, never set or used */ optind = 1, /* index into parent argv vector */ optopt; /* character checked for validity */ char *optarg; /* argument associated with option */ #define tell(s) fputs(*nargv,stderr);fputs(s,stderr); \ fputc(optopt,stderr);fputc('\n',stderr);return(BADCH); getopt(nargc,nargv,ostr) int nargc; char **nargv, *ostr; { static char *place = EMSG; /* option letter processing */ register char *oli; /* option letter list index */ char *index(); if(!*place) { /* update scanning pointer */ if(optind >= nargc || *(place = nargv[optind]) != '-' || !*++place) return(EOF); if (*place == '-') { /* found "--" */ ++optind; return(EOF); } } /* option letter okay? */ if ((optopt = (int)*place++) == ARGCH || !(oli = index(ostr,optopt))) { if(!*place) ++optind; tell(": illegal option -- "); } if (*++oli != ARGCH) { /* don't need argument */ optarg = NULL; if (!*place) ++optind; } else { /* need an argument */ if (*place) optarg = place; /* no white space */ else if (nargc <= ++optind) { /* no arg */ place = EMSG; tell(": option requires an argument -- "); } else optarg = nargv[optind]; /* white space */ place = EMSG; ++optind; } return(optopt); /* dump back option letter */ } #ifdef GETOPT #ifndef lint static char sccspid[] = "@(#) getopt.c 5.1 (WangInst) 6/15/85"; #endif main (argc, argv) char **argv; { char *optstring = argv[1]; char *argv0 = argv[0]; extern int optind; extern char *optarg; int opterr = 0; int C; char *opi; if (argc == 1) { fprintf (stderr, "Usage: %s optstring args\n", argv0); exit (1); } argv++; argc--; argv[0] = argv0; while ((C = getopt (argc, argv, optstring)) != EOF) { if (C == BADCH) opterr++; printf ("-%c ", C); opi = index (optstring, C); if (opi && opi[1] == ARGCH) if (optarg) printf ("\"%s\" ", optarg); else opterr++; } printf ("%s", ENDARGS); while (optind < argc) printf (" \"%s\"", argv[optind++]); putchar ('\n'); exit (opterr); } #endif SHAR_EOF fi if test -f 'makefile' then echo shar: "will not over-write existing file 'makefile'" else cat << \SHAR_EOF > 'makefile' CFLAGS=-O OBJS=shar.o traverse.o getopt.o SRCS=shar.c traverse.c getopt.c DOCS=shar.1 traverse.3 getopt.3 PR=/usr/ucb/print LINT=lint -h shar: $(OBJS) makefile @cc $(CFLAGS) -o shar $(OBJS) traverse: @cc -o traverse -DSTANDALONE traverse.c archive: makefile $(SRCS) $(DOCS) @shar -a makefile $(SRCS) $(DOCS) > archive print: @$(PR) makefile shar.c lint: @$(LINT) $(SRCS) clean: @-rm -f core a.out *.o SHAR_EOF fi if test -f 'shar.1' then echo shar: "will not over-write existing file 'shar.1'" else cat << \SHAR_EOF > 'shar.1' .TH SHAR 1net "March 4, 1986" "UNIX User's Manual" "Wang Institute" .SH NAME shar \- create file storage archive for extraction by /bin/sh .SH SYNOPSIS .B shar [-abcmsuv] [-p prefix] [-d delim] files > archive .SH DESCRIPTION .I shar prints its input files with special command lines around them to be used by the shell, .I /bin/sh , to extract the files later. The output can be filtered through the shell to recreate copies of the original files. .PP .I shar allows directories to be named, and .I shar prints the necessary commands .ul (mkdir & cd) to create new directories and fill them. .I shar will not allow existing files to be over-written; such files must be removed by the user extracting the files. .SH OPTIONS .de OP .TP .B -\\$1 .. .OP a All the options. The options: .B "-v -c -b -p <tab>X" are implied. .OP b Extract files into basenames so that files with absolute path names are put into the current directory. This option has strange effects when directories are archived. .OP c Check file size on extraction by counting characters. An error message is reported to the person doing the extraction if the sizes don't match. One reason why the sizes may not match is that .I shar will append a newline to complete incomplete last lines; .I shar prints a message that mentions added newlines. Another reason why the sizes may not match is that some network mail programs remove non-whitespace control characters. .I shar prints a message that mentions control characters to the extractor. .OP d delim Use this as the ``end of file'' delimiter instead of the default. The only reason to change it is if you suspect a file contains the default delimiter: .B SHAR_EOF. .OP m Reset the exact protection modes of files when they are extracted (using the .I chmod program). By default, the extractor's default file modes are used, and executable files (e.g., shell scripts) are made executable. .OP p prefix Use this as the prefix to each line of the archived files. This is to make sure that special characters at the start of lines are not eaten up by programs like mailers. If this option is used, the files will be extracted with the stream editor .B sed rather than .B cat so it is more efficient and portable to avoid setting the prefix, though perhaps less safe if you don't know what is in the files. .OP s Silent running. All checking and extra output is inhibited. .OP u Archive the input files with the .I uuencode format for later extraction with .I uudecode. This will allow you to send files with control characters in them, but will slow down the extracting. You must be sure that the receiving party has access to uudecode. .OP v Print verbose feedback messages about what .I shar is doing to be printed during extraction. Sizes of plain files are echoed to allow a simple validity check. .SH "SEE ALSO sh(1), tar(1), cpio(1), tp(1), uuencode(1), uudecode(1) .br fpack(1) is a plain-file packer useful for UNIX and MSDOS .SH AUTHOR Gary Perlman (based on a shell version by James Gosling, with additions motivated by many people on the UNIX network: Derek Zahn, Michael Thompson, H. Morrow Long, Fred Avolio, Gran Uddeborg, Chuck Wegrzyn, nucleus!randy@TORONTO, & Bill McKeeman) .SH LIMITATIONS .I shar does not know anything about links between files. SHAR_EOF fi if test -f 'shar.c' then echo shar: "will not over-write existing file 'shar.c'" else cat << \SHAR_EOF > 'shar.c' /* Shar puts readable text files together in a package from which they are extracted with /bin/sh and friends. */ #include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <ctype.h> #ifndef lint static char sccsid[] = "@(#)shar.c 2.0 (Usenet) 3/11/86"; #endif typedef int Boole; #define TRUE ((Boole) 1) #define FALSE ((Boole) 0) typedef int Status; #define SUCCESS 0 #define FAILURE 1 /* GLOBALS */ int Lastchar; /* the last character printed */ int Ctrlcount; /* how many bad control characters are in file */ /* COMMANDS */ #define EXTRACT "#! /bin/sh" /* magic exec string at shar file start */ #define PATH "/bin:/usr/bin:$PATH" /* search path for programs */ #define CAT "cat"; /* /bin/cat */ #define SED "sed 's\/^%s\/\/'" /* /bin/sed removes Prefix from lines */ #define MKDIR "mkdir" /* make a new dirctory */ #define CHMOD "chmod" /* change file mode */ #define CHDIR "cd" /* change current directory */ #define TEST "test" /* /bin/test files */ #define WC_C "wc -c <" /* counts chars in file */ #define ECHO "echo shar:" /* echo a message to extractor */ #define DECODE "uudecode" /* used to decode uuencoded files */ /*FUNCTION main: traverse files to make archive to standard output */ main (argc, argv) char **argv; { int shar (); int optind; if ((optind = initial (argc, argv)) < 0) exit (FAILURE); if (header (argc, argv, optind)) exit (FAILURE); while (optind < argc) traverse (argv[optind++], shar); footer (); exit (SUCCESS); } /* OPTIONS */ Boole Verbose = FALSE; /* provide append/extract feedback */ Boole Basename = FALSE; /* extract into basenames */ Boole Count = FALSE; /* count characters to check transfer */ Boole Overcheck = TRUE; /* check overwriting */ Boole Uucode = FALSE; /* uuencode the file */ char *Delim = "SHAR_EOF"; /* put after each file */ char Filter[100] = CAT; /* used to extract archived files */ char *Prefix = NULL; /* line prefix to avoid funny chars */ Boole Modeset = FALSE; /* set exact modes on extraction */ /*FUNCTION: initial: do option parsing and any setup */ int /* returns the index of the first operand file */ initial (argc, argv) char **argv; { int errflg = 0; extern int optind; extern char *optarg; int C; char *optstring = "abcmsuvp:d:"; char *usage = "[-abcmsuv] [-p prefix] [-d delim] files > archive"; while ((C = getopt (argc, argv, optstring)) != EOF) switch (C) { case 'u': Uucode = TRUE; break; case 'b': Basename = TRUE; break; case 'c': Count = TRUE; break; case 'd': Delim = optarg; break; case 'm': Modeset = TRUE; break; case 's': /* silent running */ Overcheck = FALSE; Verbose = FALSE; Count = FALSE; Prefix = NULL; break; case 'a': /* all the options */ Verbose = TRUE; Count = TRUE; Basename = TRUE; /* fall through to set prefix */ optarg = " X"; /* FALLTHROUGH */ case 'p': (void) sprintf (Filter, SED, Prefix = optarg); break; case 'v': Verbose = TRUE; break; default: errflg++; } if (errflg || optind == argc) { if (optind == argc) fprintf (stderr, "shar: No input files\n"); fprintf (stderr, "Usage: shar %s\n", usage); return (-1); } return (optind); } /*FUNCTION header: print header for archive */ header (argc, argv, optind) char **argv; { int i; Boole problems = FALSE; long clock; char *ctime (); char *getenv (); char *NAME = getenv ("NAME"); char *ORG = getenv ("ORGANIZATION"); for (i = optind; i < argc; i++) if (access (argv[i], 4)) /* check read permission */ { fprintf (stderr, "shar: Can't read '%s'\n", argv[i]); problems++; } if (problems) return (FAILURE); printf ("%s\n", EXTRACT); printf ("# This is a shell archive, meaning:\n"); printf ("# 1. Remove everything above the %s line.\n", EXTRACT); printf ("# 2. Save the resulting text in a file.\n"); printf ("# 3. Execute the file with /bin/sh (not csh) to create:\n"); for (i = optind; i < argc; i++) printf ("#\t%s\n", argv[i]); (void) time (&clock); printf ("# This archive created: %s", ctime (&clock)); if (NAME) printf ("# By:\t%s (%s)\n", NAME, ORG ? ORG : ""); printf ("export PATH; PATH=%s\n", PATH); return (SUCCESS); } /*FUNCTION footer: print end of shell archive */ footer () { printf ("exit 0\n"); printf ("#\tEnd of shell archive\n"); } /* uuencode options available to send cntrl and non-ascii chars */ /* really, this is getting to be too much like cpio or tar */ /* ENC is the basic 1 character encoding function to make a char printing */ #define ENC(c) (((c) & 077) + ' ') /*FUNCTION uuarchive: simulate uuencode to send files */ Status uuarchive (input, protection, output) char *input; int protection; char *output; { FILE *in; if (in = fopen (input, "r")) { printf ("%s << \\%s\n", DECODE, Delim); printf ("begin %o %s\n", protection, output); uuencode (in, stdout); printf ("end\n"); fclose (in); return (SUCCESS); } return (FAILURE); } uuencode (in, out) FILE *in, *out; { char buf[80]; int i, n; for (;;) { n = fread (buf, 1, 45, in); putc (ENC(n), out); for (i = 0; i < n; i += 3) outdec (&buf[i], out); putc ('\n', out); if (n <= 0) break; } } /* output one group of 3 bytes, pointed at by p, on file ioptr */ outdec (p, ioptr) char *p; FILE *ioptr; { int c1, c2, c3, c4; c1 = *p >> 2; c2 = (*p << 4) & 060 | (p[1] >> 4) & 017; c3 = (p[1] << 2) & 074 | (p[2] >> 6) & 03; c4 = p[2] & 077; putc (ENC(c1), ioptr); putc (ENC(c2), ioptr); putc (ENC(c3), ioptr); putc (ENC(c4), ioptr); } /*FUNCTION archive: make archive of input file to be extracted to output */ archive (input, output) char *input, *output; { char buf[BUFSIZ]; FILE *ioptr; if (ioptr = fopen (input, "r")) { Ctrlcount = 0; /* no bad control characters so far */ Lastchar = '\n'; /* simulate line start */ printf ("%s << \\%s > '%s'\n", Filter, Delim, output); if (Prefix) { while (fgets (buf, BUFSIZ, ioptr)) { if (Prefix) outline (Prefix); outline (buf); } } else copyout (ioptr); if (Lastchar != '\n') /* incomplete last line */ putchar ('\n'); /* Delim MUST begin new line! */ if (Count == TRUE && Lastchar != '\n') printf ("%s \"a missing newline was added to '%s'\"\n", ECHO, input); if (Count == TRUE && Ctrlcount) printf ("%s \"%d control character%s may be missing from '%s'\"\n", ECHO, Ctrlcount, Ctrlcount > 1 ? "s" : "", input); (void) fclose (ioptr); return (SUCCESS); } else { fprintf (stderr, "shar: Can't open '%s'\n", input); return (FAILURE); } } /*FUNCTION copyout: copy ioptr to stdout */ /* Copyout copies its ioptr almost as fast as possible except that it has to keep track of the last character printed. If the last character is not a newline, then shar has to add one so that the end of file delimiter is recognized by the shell. This checking costs about a 10% difference in user time. Otherwise, it is about as fast as cat. It also might count control characters. */ #define badctrl(c) (iscntrl (c) && !isspace (c)) copyout (ioptr) register FILE *ioptr; { register int C; register int L; register count; count = Count; while ((C = getc (ioptr)) != EOF) { if (count == TRUE && badctrl (C)) Ctrlcount++; L = putchar (C); } Lastchar = L; } /*FUNCTION outline: output a line, recoring last character */ outline (s) register char *s; { if (*s) { while (*s) { if (Count == TRUE && badctrl (*s)) Ctrlcount++; putchar (*s++); } Lastchar = *(s-1); } } /*FUNCTION shar: main archiving routine passed to directory traverser */ shar (file, type, pos) char *file; /* file or directory to be processed */ int type; /* either 'f' for file or 'd' for directory */ int pos; /* 0 going in to a file or dir, 1 going out */ { struct stat statbuf; char *basefile = file; int protection; if (!strcmp (file, ".")) return; if (stat (file, &statbuf)) statbuf.st_size = 0; else protection = statbuf.st_mode & 07777; if (Basename == TRUE) { while (*basefile) basefile++; /* go to end of name */ while (basefile > file && *(basefile-1) != '/') basefile--; } if (pos == 0) /* before the file starts */ { beginfile (basefile, type, statbuf.st_size, protection); if (type == 'f') { if (Uucode) { if (uuarchive (file, protection, basefile) != SUCCESS) exit (FAILURE); } else if (archive (file, basefile) != SUCCESS) exit (FAILURE); } } else /* pos == 1, after the file is archived */ endfile (basefile, type, statbuf.st_size, protection); } /*FUNCTION beginfile: do activities before packing up a file */ beginfile (basefile, type, size, protection) char *basefile; /* name of the target file */ int type; /* either 'd' for directory, or 'f' for file */ int size; /* size of the file */ int protection; /* chmod protection bits */ { if (type == 'd') { printf ("if %s ! -d '%s'\n", TEST, basefile); printf ("then\n"); if (Verbose == TRUE) printf (" %s \"creating directory '%s'\"\n", ECHO, basefile); printf (" %s '%s'\n", MKDIR, basefile); printf ("fi\n"); if (Verbose == TRUE) printf ("%s \"entering directory '%s'\"\n", ECHO, basefile); printf ("%s '%s'\n", CHDIR, basefile); } else /* type == 'f' */ { if (Verbose == TRUE) printf ("%s \"extracting '%s'\" '(%d character%s)'\n", ECHO, basefile, size, size > 1 ? "s" : ""); if (Overcheck == TRUE) { printf ("if %s -f '%s'\n", TEST, basefile); printf ("then\n"); printf (" %s \"will not over-write existing file '%s'\"\n", ECHO, basefile); printf ("else\n"); } } } /*FUNCTION endfile: do activities after packing up a file */ endfile (basefile, type, size, protection) char *basefile; /* name of the target file */ int type; /* either 'd' for directory, or 'f' for file */ int size; /* size of the file */ int protection; /* chmod protection bits */ { if (type == 'd') { if (Modeset == TRUE) printf ("%s %o .\n", CHMOD, protection); if (Verbose == TRUE) printf ("%s \"done with directory '%s'\"\n", ECHO, basefile); printf ("%s ..\n", CHDIR); } else /* type == 'f' (plain file) */ { printf ("%s\n", Delim); if (Count == TRUE) { printf ("if %s %d -ne \"`%s '%s'`\"\n", TEST, size, WC_C, basefile); printf ("then\n"); printf (" %s \"error transmitting '%s'\" ", ECHO, basefile); printf ("'(should have been %d character%s)'\n", size, size == 1 ? "" : "s"); printf ("fi\n"); } if (Uucode == FALSE) /* might have to chmod by hand */ { if (Modeset == TRUE) /* set all protection bits (W McKeeman) */ printf ("%s %o '%s'\n", CHMOD, protection, basefile); else if (protection & 0111) /* executable -> chmod +x */ printf ("%s +x '%s'\n", CHMOD, basefile); } if (Overcheck == TRUE) printf ("fi\n"); } } SHAR_EOF fi if test -f 'traverse.3' then echo shar: "will not over-write existing file 'traverse.3'" else 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. This traversal is in depth first order so that files are processed in the order that they are stored in the directory. .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 fi if test -f 'traverse.c' then echo shar: "will not over-write existing file 'traverse.c'" else cat << \SHAR_EOF > 'traverse.c' /*LINTLIBRARY*/ #ifndef lint static char sccsid[] = "@(#)traverse.c 1.0 (WangInst) 12/23/84"; #endif #include <stdio.h> #include <sys/types.h> #include <sys/dir.h> #ifdef MAXNAMLEN #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); } #ifdef STANDALONE static Indent = 0; tryverse (file, type, pos) char *file; { int in; if (pos == 0) { for (in = 0; in < Indent; in++) putchar ('\t'); if (type == 'd') { printf ("%s/\n", file); Indent++; } else puts (file); } else if (type == 'd') Indent--; } main (argc, argv) char **argv; { int tryverse (); char *root = argc > 1 ? argv[1] : "."; traverse (root, tryverse); } #endif SHAR_EOF fi cd .. exit 0 # End of shell archive