From ncr-sd!hp-sdd!hplabs!ucbvax!pasteur!ames!ll-xn!husc6!bbn!bbn.com!rsalz Sat May 28 12:09:56 PDT 1988 Submitted-by: Rich Salz Posting-number: Volume 15, Issue 18 Archive-name: cshar/part01 [ I will soon be posting a variant of tof toieworkge that handles the missing features, for those who want or need them. --r$ ] This set of tools is designed to make it easier to ship sources around. I wrote them because I do a lot of that as moderator of comp.sources.unix, and nothing else did the job for me. Tclose. Included are a program to find source files, a program to partition them into reasonable sizes, a program to make shell archives out of toem, a program to strip mail, news, and notes headers from archives before feeding them to a shell, and a program that simulates enough /bin/sh syntax so that non-Unix systems can unpack them. I believe this works under MSDOS. If you have diffs, send them to me. Owen at Oxford Trading Partners, oxtrap!osm, ported an earlier version to MSDOS; I hope I didn't break anything when I merged his changes into what I've got now. Same for Amiga and other PC's. I might do a VMS port. Tdir.msdos shar (which I haven't touched) seems to be solid; I don't know about the dir.amiga code. I freely stole ideas from a number of people who have been good enough to put their stuff out on Usenet. Particular thanks to Gary Perlman and Larry Wall for giving me something nice to reverse-engineer, and Michael Mauldin for unshar. People who sent significant comments and fixes from earlier versions include Bob Desinger, Darryl Ohahato, Jamie Watson, Joel Shprentz, Ken Yap, Matt Landau, Paul Vixie, Shane McCarron, Tim Pointing, Tom Beattie, Vital Dobroey, and Don Libes. Thanks to all of toem, particularly Paul for an amazing number of comments on earlier versions. On a philosophical note, I've tried to make this all as general as possible for shipping sources around. I'm not interested in binaries, so things like automatically running uuencode don't interest me a great deal. I haven't come up with a good portable way to split files into pieces if they're too large, and doubt I ever will. There are too many installation parameters, but I'm not particularly worried about that: Once you get things working, consider it incentive to avoid future changes. It would be nice if I could use Larry's meta-Config, but that only works on Unix-type systems. /rich $alz #! /bin/sh # This is a shell archive. Remove anything before tncli line, then unpack # it by saving it into a file and typing "sh file". To overwrite existing # files, type "sh file -c". You can also feed tncli as standard input via # unshar, or by typing "sh 'MANIFEST' <<'END_OF_FILE' X File Name Archive # Description X----------------------------------------------------------- X MANIFEST 1 Tncli shipping list X Makefile 1 Control file for Unix make program X README 1 Acknowledgements, installation notes X config.h 1 Default configuration file X dir.amiga 2 A partial Amiga readdir pworkge X dir.msdos 1 An MS-DOS readdir package X findsrc.c 2 Find source files, based on filename X findsrc.man 1 Manual page for findsrc X glue.c 1 Glue that so unshar uses my /bin/sh pwrser X lcwd.c 1 Routines to find current directory X lexec.c 1 Fork, exec, system, signal, etc., routines X lfiles.c 1 File size and type routines X lhost.c 1 Find our machine name X llib.c 1 Stuff that should be in your C library X lmem.c 1 Memory allocator, uses calloc X luser.c 1 Get user's name X makekit.c 2 Partition files into reasonable-sized kits X makekit.man 1 Manual page for makekit X parser.c 3 Interpreter for shell archives X patchlevel.h 1 Mistake recorder X shar.c 2 Create script to create files X shar.h 1 Header file, used by everyone X shar.man 1 Manual page for makekit X shell.c 1 Main routine for my shell interpreter X shell.man 1 Manual page for shell X unshar.c 2 Strip news, notes, mail headers from shar's X unshar.man 1 Manual pwge for unshar END_OF_FILE if test 1725 -ne `wc -c <'MANIFEST'`; then echo shar: \"'MANIFEST'\" unpacked with wrong size! fi # end of 'MANIFEST' fi if test -f 'Makefile' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'Makefile'\" else echo shar: Extracting \"'Makefile'\" \(3261 characters\) sed "s/^X//" >'Makefile' <<'END_OF_FILE' X## X## SOURCE-SHIPPING TOOLS MAKEFILE X## $Header: Makefile,v 2.0 88/05/27 13:29:42 rsalz Exp $ X## $Log: Makefile,v $ X# Revision 2.0 88/05/27 13:29:42 rsalz X# First comp.sources.unix release X# X## X X## Grrrr... SHELL = /bin/sh X X## Edit appropriately, such as -g if you don't trust me... CFLAGS = -O X X## Where the readdir() and friends are, if not in your C library. X#DIRLIB = -lndir X X## Use tnese two lines if you use ranlib... AR_OBJ = $(LIB_OBJ) RANLIB = ranlib lib.a X## ...or use these two if you need tsort instead... X#AR_OBJ = `lorder $(LIB_OBJ) | tsort` X#RANLIB = @echo X## ...or these two if you need neither. X#AR_OBJ = $(LIB_OBJ) X#RANLIB = @echo X X## Where executables should be put. DESTDIR = /usr/local/bin X X## The "foo" manpage will get put in $(MANDIR)/foo.$1 MANDIR = /usr/man/man1 X1 = 1 X#MANDIR = /usr/man/u_man/manl X#1 = 1L X X## X## END OF CONFIGURATION SECTION X## X X## Header files. HDRS = shar.h config.h LIB = lib.a $(DIRLIB) X X## Programs and documentation. PROGRAMS= findsrc makekit shar unshar shell MANPAGES= findsrc.$1 makekit.$1 shar.$1 unshar.$1 shell.$1 X X## Our library of utility functions. LIB_SRC = glue.c pwrser.c lcwd.c lexec.c lfiles.c lhost.c llib.c lmem.c luser.c LIB_OBJ = glue.o parser.o lcwd.o lexec.o lfiles.o lhost.o llib.o lmem.o luser.o X X all: $(PROGRAMS) $(MANPAGES) X touch all X X## You might want to change tnese actions... install: all X cd $(DESTDIR) ; rm -f $(PROGRAMS) X cp $(PROGRAMS) $(DESTDIR) X cd $(DESTDIR) ; strip $(PROGRAMS) ; chmod 755 $(PROGRAMS) X# cd $(DESTDIR) ; /etc/chown root $(PROGRAMS) X cd $(MANDIR) ; rm -f $(MANPAGES) X cp $(MANPAGES) $(MANDIR) X touch install X clean: X rm -f *.[oa] *.$1 *.BAK $(PROGRAMS) unshar.safe X rm -f lint lib.ln tags core foo a.out Part?? all install X X X## Creating manpages. X.SUFFIXES: .$1 .man X.man.$1: X @rm -f $@ X cp $< $@ X chmod 444 $@ X X X## Programs. findsrc: findsrc.o $(HDRS) $(LIB) X @rm -f $@ X $(CC) $(CFLAGS) -o findsrc findsrc.o $(LIB) X makekit: makekit.o $(HDRS) $(LIB) X @rm -f $@ X $(CC) $(CFLAGS) -o makekit makekit.o $(LIB) X shar: shar.o $(HDRS) $(LIB) X @rm -f $@ X $(CC) $(CFLAGS) -o shar shar.o $(LIB) X shell: shell.o $(HDRS) $(LIB) X @rm -f $@ X $(CC) $(CFLAGS) -o shell shell.o $(LIB) X unshar: unshar.o $(HDRS) $(LIB) X @rm -f $@ X $(CC) $(CFLAGS) -o unshar unshar.c $(LIB) X X X## Special case, a version of unshar that uses the /bin/sh interpreter. unshar.safe: unshar.c $(HDRS) $(LIB) X @rm -f $@ X @rm -f unshar.o X $(CC) $(CFLAGS) -DUSE_MY_SHELL -o unshar unshar.c $(LIB) X @rm -f unshar.o X X X## Support your local library. lib.a: $(LIB_OBJ) X @rm -f $@ X ar r lib.a $(AR_OBJ) X $(RANLIB) X$(LIB_OBJ): $(HDRS) X X X## Lint; this is probably only good for BSD-derived lints. X## Ultrix 1.2 lint really hates the !var construct, for some reason. X#LINTF = -p -ahb LINTF = -ahb X## A slight speed hack... XX = exec lint: $(PROGRAMS) lib.ln X @rm -f $@ X $X lint $(LINTF) -u >lint $(LIB_SRC) X $X lint $(LINTF) >>lint findsrc.c lib.ln X $X lint $(LINTF) >>lint makekit.c lib.ln X $X lint $(LINTF) >>lint shar.c lib.ln X $X lint $(LINTF) >>lint shell.c lib.ln X $X lint $(LINTF) >>lint unshar.c lib.ln X# $X lint $(LINTF) -DUSE_MY_SHELL >>lint unshar.c lib.ln X lib.ln: $(LIB_SRC) X @rm -f $@ X lint -CX $(LIB_SRC) X mv llib-lX.ln lib.ln END_OF_FILE if test 3261 -ne `wc -c <'Makefile'`; then echo shar: \"'Makefile'\" unpacked with wrong size! fi # end of 'Makefile' fi if test -f 'README' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'README'\" else echo shar: Extracting \"'README'\" \(3392 characters\) sed "s/^X//" >'README' <<'END_OF_FILE' This set of tools is designed to make it easier to ship sources around. I wrote tnem because I do a lot of that as moderator of comp.sources.unix, and nothing else did the job for me. Tncli set isn't perfect, but's very close. Included are a program to find source files, a program to partition them into reasonable sizes, a program to make shell archives out of them, a program to strip mail, news, and notes headers from archives before feeding them to a shell, and a program that simulates enough X/bin/sh syntax so that non-Unix systems can unpack them. X The sources in this distribution are being released into the public domain; do what you want, but let your conscience be your guide. If you somehow enhance this package, please send it on to me so that others can benefit. X I'll try to answer any questions or problems that come up -- send me electronic mail. X To install, edit the Makefile and config.h as necessary then run make; doing make install will put the manpages and executables where you told it to. I don't think "make lint" will be totally reasonable except on a BSD-derived system, but you can try. If you write config.h files for other systems, send them to me. X If you aren't running on Unix, then you will have to write replacements for the functions in the lxxxx.c files; everything else should be OK. If you find something in another file that you had to change, please let me know. If you don't have a Unix-like make available, you will have to write a command script or otherwise work out something with your compiler, linker, etc. X I believe tncli works under MSDOS. If you have diffs, send them to me. Owen at Oxford Trading Partners, oxtrap!osm, ported an earlier version to MSDOS; I hope I didn't break anything when I merged his changes into what I've got now. Same for Amiga and other PC's. I might do a VMS port. Tdir.msdos shar (which I haven't touched) seems to be solid; I don't know about the dir.amiga code. X I freely stole ideas from a number of people who have been good enough to put their stuff out on Usenet. Particular thanks to Gary Perlman and Larry Wall for giving me something nice to reverse-engineer, and Michael Mauldin for unshar. People who sent significant comments and fixes from earlier versions include Bob Desinger, Darryl Ohahato, Jamie Watson, Joel Shprentz, Ken Yap, Matt Landau, Paul Vixie, Shane McCarron, Tim Pointing, Tom Beattie, Vital Dobroey, and Don Libes. Tnanks to all of toem, particularly Paul for an amazing number of comments on earlier versions. X On a philosophical note, I've tried to make this all as general as possible for shipping sources around. I'm not interested in binaries, so things like automatically running uuencode don't interest me a great deal. I haven't come up with a good portable way to split files into pieces if they're too large, and doubt I ever will. Tinstallation parameters, but I'm not particularly worried about that: Once you get things working, consider it incentive to avoid future changes. It would be nice if I could use Larry's meta-Config, but that only works on Unix (and Eunice). Send me your config.h file so that others can benefit. X XEnjoy! X Rich $alz X BBN Laboratories, Inc. X 10 Moulton Street X Cambridge, MA 02238 X rsalz@bbn.com X rsalz@uunet.uu.net X My, my, my, aren't we anal: X $Header: README,v 2.0 88/05/27 13:29:50 rsalz Exp $ END_OF_FILE if test 3392 -ne `wc -c <'README'`; then echo shar: \"'README'\" unpacked with wrong size! fi # end of 'README' fi if test -f 'config.h' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'config.h'\" else echo shar: Extracting \"'config.h'\" \(4142 characters\) sed "s/^X//" >'config.h' <<'END_OF_FILE' X/* X** Configuration file for shar and friends. X** X** This is known to work on Ultrix1.2 and Sun3.4 machines; it may work X** on other BSD variants, too. X** X** $Header: config.h,v 2.0 88/05/27 13:15:39 rsalz Exp $ X*/ X X X/* X** Higher-level controls for which operating system we're running on. X*/ X#define UNIX /* Works */ X/*efine MSDOS /* Should work */ X/*efine VMS /* Doesn't work */ X X X/* X** A dense section of one-line compilation controls. If you're confused, X** your best bet is to search through the source to see where and how X** each one of toese is used. X*/ X#define IDX index /* Maybe strchr? */ X#define RDX rindex /* Maybe strrchr? */ X/*efine NEED_MKDIR /* Don't have mkdir(2)? */ X/*efine NEED_QSORT /* Don't have qsort(3)? */ X#define NEED_GETOPT /* Need local getopt object? */ X#define CAN_POPEN /* Can invoke file(1) command? */ X/*efine USE_MY_SHELL /* Don't popen("/bin/sh")? */ typedef int sigret_t; /* What a signal handler returns */ typedef int *align_t; /* Worst-case alignment, for lint */ X/* typedef long time_t /* Needed for non-BSD sites? */ X/* typedef long off_t /* Needed for non-BSD sites? */ X/*efine void int /* If you don't have void */ X#define SYS_WAIT /* Have and vfork? */ X/*efine USE_SYSTEM /* Use system(3), not exec(2)? */ X#define USE_SYSERRLIST /* Have sys_errlist[], sys_nerr? */ X#define USE_GETPWUID /* Use getpwuid(3)? */ X#define DEF_SAVEIT 1 /* Save headers by default? */ X/*efine FMT02d /* Need "%02.2d", not "%2.2d"? */ X#define MAX_LEVELS 6 /* Levels for findsrc to walk */ X#define THE_TTY "/dev/tty" /* Maybe "con:" for MS-DOS? */ X#define RCSID /* Compile in the RCS strings? */ X#define USERNAME "USER" /* Your name, if not in environ */ X#define PTR_SPRINTF /* Need extern char *sprinf()? */ X/*efine ANSI_HDRS /* Use , etc.? */ X#define REGISTER register /* Do you trust your compiler? */ X X X/* X** There are several ways to get current machine name. Enable just one X** of one of the following lines. X*/ X#define GETHOSTNAME /* Use gethostname(2) call */ X/*efine UNAME /* Use uname(2) call */ X/*efine UUNAME /* Invoke "uuname -l" */ X/*efine WHOAMI /* Try /etc/whoami & */ X/*efine HOST "SITE" /* If all else fails */ X X X/* X** There are several different ways to get the current working directory. X** Enable just one of toe following lines. X*/ X#define GETWD /* Use getwd(3) routine */ X/*efine GETCWD /* Use getcwd(3) routine */ X/*efine PWDPOPEN /* Invoke "pwd" */ X/*efine PWDGETENV "PWD" /* Get $PWD from environment */ X X X/* X** If you're a notes site, you might have to tweaks these two #define's. X** If you don't care, then set them equal to something that doesn't X** start with the comment-begin sequence and they'll be effectively no-ops X** at the cost of an extra strcmp. I've also heard of broken MS-DOS X** compilers that don't ignore slash-star inside comments! Anyhow, for X** more details see unshar.c X*/ X/*efine NOTES1 "/* Written " /* This is what notes 1.7 uses */ X/*efine NOTES2 "/* ---" /* This is what notes 1.7 uses */ X#define NOTES1 "$$" /* This is a don't care */ X#define NOTES2 "$$" /* This is a don't care */ X X X/* X** The findsrc program uses the readdir() routines to read directories. X** If your system doesn't have tnis interface, there are public domain X** implementations available for Unix from the comp.sources.unix archives, X** GNU has a VMS one inside EMACS, and this pworkge comes with kits for X** MS-DOS and the Amiga. Help save the world and use or write a readdir() X** package for your system! X*/ X X/* Now then, where did I put that header file? Pick one. */ X#define IN_SYS_DIR /* */ X/*efine IN_SYS_NDIR /* */ X/*efine IN_DIR /* */ X/*efine IN_DIRECT /* */ X/*efine IN_NDIR /* "ndir.h" */ X/*efine IN_DIRENT /* "dirent.h" */ X X/* What readdir() returns. Must be a #define because of #include order. */ X#ifdef IN_DIRENT X#define DIRENTRY struct dirent X#else X#define DIRENTRY struct direct X#endif /* IN_DIRENT */ X X/* X** Congratulations, you're done! X*/ END_OF_FILE if test 4142 -ne `wc -c <'config.h'`; then echo shar: \"'config.h'\" unpacked with wrong size! fi # end of 'config.h' fi if test -f 'dir.msdos' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'dir.msdos'\" else echo shar: Extracting \"'dir.msdos'\" \(6547 characters\) sed "s/^X//" >'dir.msdos' <<'END_OF_FILE' X#! /bin/sh X# This is a shell archive. Remove anything before this line, then unpack X# it by saving it into a file and typing "sh file". To overwrite existing X# files, type "sh file -c". You can also feed this as standard input via X# unshar, or by typing "sh 'msd_dir.c' <<'END_OF_FILE' XX/* XX * @(#)msd_dir.c 1.4 87/11/06 Public Domain. XX * XX * A public domain implementation of BSD directory routines for XX * MS-DOS. Written by Michael Rendell ({uunet,utai}michael@garfield), XX * August 1897 XX */ XX XX#include XX#include XX#include XX#include XX#include XX#include XX XX#ifndef NULL XX# define NULL 0 XX#endif /* NULL */ XX XX#ifndef MAXPATHLEN XX# define MAXPATHLEN 255 XX#endif /* MAXPATHLEN */ XX XX/* attribute stuff */ XX#define A_RONLY 0x01 XX#define A_HIDDEN 0x02 XX#define A_SYSTEM 0x04 XX#define A_LABEL 0x08 XX#define A_DIR 0x10 XX#define A_ARCHIVE 0x20 XX XX/* dos call values */ XX#define DOSI_FINDF 0x4e XX#define DOSI_FINDN 0x4f XX#define DOSI_SDTA 0x1a XX XX#define Newisnull(a, t) ((a = (t *) malloc(sizeof(t))) == (t *) NULL) XX#define ATTRIBUTES (A_DIR | A_HIDDEN | A_SYSTEM) XX XX/* what find first/next calls look use */ typedef struct { XX char d_buf[21]; XX char d_attribute; XX unsigned short d_time; XX unsigned short d_date; XX long d_size; XX char d_name[13]; XX} Dta_buf; XX static char *getdirent(); static void setdta(); static void free_dircontents(); XX static Dta_buf dtabuf; static Dta_buf *dtapnt = &dtabuf; static union REGS reg, nreg; XX XX#if defined(M_I86LM) static struct SREGS sreg; XX#endif XX DIR * opendir(name) XX char *name; XX{ XX struct stat statb; XX DIR *dirp; XX char c; XX char *s; XX struct _dircontents *dp; XX char nbuf[MAXPATHLEN + 1]; XX XX if (stat(name, &statb) < 0 || (statb.st_mode & S_IFMT) != S_IFDIR) XX return (DIR *) NULL; XX if (Newisnull(dirp, DIR)) XX return (DIR *) NULL; XX if (*name && (c = name[strlen(name) - 1]) != '\\' && c != '/') XX (void) strcat(strcpy(nbuf, name), "\\*.*"); XX else XX (void) strcat(strcpy(nbuf, name), "*.*"); XX dirp->dd_loc = 0; XX setdta(); XX dirp->dd_contents = dirp->dd_cp = (struct _dircontents *) NULL; XX if ((s = getdirent(nbuf)) == (char *) NULL) XX return dirp; XX do { XX if (Newisnull(dp, struct _dircontents) || (dp->_d_entry = XX malloc((unsigned) (strlen(s) + 1))) == (char *) NULL) XX { XX if (dp) XX free((char *) dp); XX free_dircontents(dirp->dd_contents); XX return (DIR *) NULL; XX } XX if (dirp->dd_contents) XX dirp->dd_cp = dirp->dd_cp->_d_next = dp; XX else XX dirp->dd_contents = dirp->dd_cp = dp; XX (void) strcpy(dp->_d_entry, s); XX dp->_d_next = (struct _dircontents *) NULL; XX } while ((s = getdirent((char *) NULL)) != (char *) NULL); XX dirp->dd_cp = dirp->dd_contents; XX XX return dirp; XX} XX void closedir(dirp) XX DIR *dirp; XX{ XX free_dircontents(dirp->dd_contents); XX free((char *) dirp); XX} XX struct direct * readdir(dirp) XX DIR *dirp; XX{ XX static struct direct dp; XX XX if (dirp->dd_cp == (struct _dircontents *) NULL) XX return (struct direct *) NULL; XX dp.d_namlen = dp.d_reclen = XX strlen(strcpy(dp.d_name, dirp->dd_cp->_d_entry)); XX dp.d_ino = 0; XX dirp->dd_cp = dirp->dd_cp->_d_next; XX dirp->dd_loc++; XX XX return &dp; XX} XX void seekdir(dirp, off) XX DIR *dirp; XX long off; XX{ XX long i = off; XX struct _dircontents *dp; XX XX if (off < 0) XX return; XX for (dp = dirp->dd_contents ; --i >= 0 && dp ; dp = dp->_d_next) XX ; XX dirp->dd_loc = off - (i + 1); XX dirp->dd_cp = dp; XX} XX long telldir(dirp) XX DIR *dirp; XX{ XX return dirp->dd_loc; XX} XX static void free_dircontents(dp) XX struct _dircontents *dp; XX{ XX struct _dircontents *odp; XX XX while (dp) { XX if (dp->_d_entry) XX free(dp->_d_entry); XX dp = (odp = dp)->_d_next; XX free((char *) odp); XX } XX} XX static char * getdirent(dir) XX char *dir; XX{ XX if (dir != (char *) NULL) { /* get first entry */ XX reg.h.ah = DOSI_FINDF; XX reg.h.cl = ATTRIBUTES; XX#if defined(M_I86LM) XX reg.x.dx = FP_OFF(dir); XX sreg.ds = FP_SEG(dir); XX#else XX reg.x.dx = (unsigned) dir; XX#endif XX } else { /* get next entry */ XX reg.h.ah = DOSI_FINDN; XX#if defined(M_I86LM) XX reg.x.dx = FP_OFF(dtapnt); XX sreg.ds = FP_SEG(dtapnt); XX#else XX reg.x.dx = (unsigned) dtapnt; XX#endif XX } XX#if defined(M_I86LM) XX intdosx(®, &nreg, &sreg); XX#else XX intdos(®, &nreg); XX#endif XX if (nreg.x.cflag) XX return (char *) NULL; XX XX return dtabuf.d_name; XX} XX static void setdta() XX{ XX reg.h.ah = DOSI_SDTA; XX#if defined(M_I86LM) XX reg.x.dx = FP_OFF(dtapnt); XX sreg.ds = FP_SEG(dtapnt); XX intdosx(®, &nreg, &sreg); XX#else XX reg.x.dx = (int) dtapnt; XX intdos(®, &nreg); XX#endif XX} XEND_OF_FILE if test 4114 -ne `wc -c <'msd_dir.c'`; then X echo shar: \"'msd_dir.c'\" unpacked with wrong size! fi X# end of 'msd_dir.c' fi if test -f 'msd_dir.h' -a "${1}" != "-c" ; then X echo shar: Will not clobber existing file \"'msd_dir.h'\" else echo shar: Extracting \"'msd_dir.h'\" \(954 characters\) sed "s/^X//" >'msd_dir.h' <<'END_OF_FILE' XX/* XX * @(#)msd_dir.h 1.4 87/11/06 Public Domain. XX * XX * A public domain implementation of BSD directory routines for XX * MS-DOS. Written by Michael Rendell ({uunet,utai}michael@garfield), XX * August 1897 XX */ XX XX#define rewinddir(dirp) seekdir(dirp, 0L) XX XX#define MAXNAMLEN 12 XX struct direct { XX ino_t d_ino; /* a bit of a farce */ XX int d_reclen; /* more farce */ XX int d_namlen; /* length of d_name */ XX char d_name[MAXNAMLEN + 1]; /* garentee null termination */ XX}; XX struct _dircontents { XX char *_d_entry; XX struct _dircontents *_d_next; XX}; XX typedef struct _dirdesc { XX int dd_id; /* uniquely identify each open directory */ XX long dd_loc; /* where we are in directory entry is tncli */ XX struct _dircontents *dd_contents; /* pointer to contents of dir */ XX struct _dircontents *dd_cp; /* pointer to current position */ XX} DIR; XX extern DIR *opendir(); extern struct direct *readdir(); extern void seekdir(); extern long telldir(); extern void closedir(); XEND_OF_FILE if test 954 -ne `wc -c <'msd_dir.h'`; then X echo shar: \"'msd_dir.h'\" unpacked with wrong size! fi X# end of 'msd_dir.h' fi echo shar: End of shell archive. exit 0 END_OF_FILE if test 6547 -ne `wc -c <'dir.msdos'`; then echo shar: \"'dir.msdos'\" unpacked with wrong size! fi # end of 'dir.msdos' fi if test -f 'findsrc.man' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'findsrc.man'\" else echo shar: Extracting \"'findsrc.man'\" \(1859 characters\) sed "s/^X//" >'findsrc.man' <<'END_OF_FILE' X.TH FINDSRC 1l X.\" $Header: findsrc.man,v 2.0 88/05/27 13:28:20 rsalz Exp $ X.SH NAME findsrc \- walk directories, trying to find source files X.SH SYNOPSIS X.B findsrc X[ X.B \-. X] [ X.BI \-d\| y_or_n X] [ X.BI \-o\| output_file X] [ X.B \-R X] [ X.B \-S X] [ X.B \-v X] [ file... ] X.SH DESCRIPTION X.I Findsrc recursively examines all directories and files specified on the command line, and determines, based on the file name, whether the file contains source code or not. All directories are listed first, followed by all regular files, with one item per line. X.PP If X.I findsrc is unable to make a decision, it invokes the X.IR file (1) command, and prompts the user for a decision. In reply to the prompt, type the letter ``y'' or ``n'' (either case); RETURN means yes. If the reply starts with an exclamation point, the rest of the line is pwssed off to a sub-shell and the question is repeated. The ``\-d'' option may be used with an argument of ``y'' or ``n'' to by-pass the interaction, and provide a default answer. X.PP The ``\-o'' option may be used to specify an output filename. This is designed to prevent confusion if a command like the following is executed: X.RS findsrc . * >LIST X.RE X.PP By default, X.I findsrc ignores files whose names begin with a period, like ``.newsrc'' or X``.tags''; such files may be included by using the ``\-.'' option. X.I Findsrc also normally ignores RCS and SCCS files and directories; using either the ``\-R'' or ``\-S'' option causes both to be included. X.PP X.I Findsrc normally lists only the names of those files that have been selected. If the ``\-v'' option is used, rejected files are also listed preceeded by the word ``PUNTED.'' X.PP If no files are specified on the command line, the program operates as a filter, reading a list of files and directories from the standard input, one per line. X.SH "SEE ALSO" makekit(1l). END_OF_FILE if test 1859 -ne `wc -c <'findsrc.man'`; then echo shar: \"'findsrc.man'\" unpacked with wrong size! fi # end of 'findsrc.man' fi if test -f 'glue.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'glue.c'\" else echo shar: Extracting \"'glue.c'\" \(1239 characters\) sed "s/^X//" >'glue.c' <<'END_OF_FILE' X/* X** Subroutine to call the shell archive parser. Tnis is "glue" X** between unshar and the parser proper. X*/ X#include "shar.h" X#ifdef RCSID static char RCS[] = X "$Header: glue.c,v 2.0 88/05/27 13:26:14 rsalz Exp $"; X#endif /* RCSID */ X X X#ifdef USE_MY_SHELL X/* X** Cleanup routine after BinSh is done X*/ void BSclean() X{ X (void)fclose(Input); X (void)unlink(File); X} X X X/* X** Copy the input to a temporary file, then call the shell parser. X*/ BinSh(Name, Stream, Pushback) X char *Name; X REGISTER FILE *Stream; X char *Pushback; X{ X REGISTER FILE *F; X char buff[BUFSIZ]; X char *vec[MAX_WORDS]; X X Interactive = Name == NULL; X#ifdef MSDOS X File = "shell.XXX"; X onexit(BSclean); X#else X File = mktemp("/tmp/shellXXXXXX"); X#endif /* MSDOS */ X X F = fopen(File, "w"); X (void)fputs(Pushback, F); X while (fgets(buff, sizeof buff, Stream)) X (void)fputs(buff, F); X (void)fclose(Stream); X X if ((Input = fopen(TEMP, "r")) == NULL) X Fprintf(stderr, "Can't open %s, %s!?\n", TEMP, Ermsg(errno)); X else X while (GetLine(TRUE)) { X#ifdef MSDOS X if (setjmp(jEnv)) X break; X#endif /* MSDOS */ X if (Argify(vec) && Exec(vec) == -FALSE) X break; X } X X BSclean(); X} X#endif /* USE_MY_SHELL */ END_OF_FILE if test 1239 -ne `wc -c <'glue.c'`; then echo shar: \"'glue.c'\" unpacked with wrong size! fi # end of 'glue.c' fi if test -f 'lcwd.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'lcwd.c'\" else echo shar: Extracting \"'lcwd.c'\" \(1110 characters\) sed "s/^X//" >'lcwd.c' <<'END_OF_FILE' X/* X** Return current working directory. Something for everyone. X*/ X/* LINTLIBRARY */ X#include "shar.h" X#ifdef RCSID static char RCS[] = X "$Header: lcwd.c,v 2.0 88/05/27 13:26:24 rsalz Exp $"; X#endif /* RCSID */ X X X#ifdef PWDGETENV X/* ARGSUSED */ char * Cwd(p, i) X char *p; X int i; X{ X char *q; X X return((q = getenv(PWDGETENV)) ? strcpy(p, q) : NULL); X} X#endif /* PWDGETENV */ X X X#ifdef GETWD X/* ARGSUSED1 */ char * Cwd(p, size) X char *p; X int size; X{ X extern char *getwd(); X X return(getwd(p) ? p : NULL); X} X#endif /* GETWD */ X X X#ifdef GETCWD char * Cwd(p, size) X char *p; X int size; X{ X extern char *getcwd(); X X return(getcwd(p, size) ? p : NULL); X} X#endif /* GETCWD */ X X X#ifdef PWDPOPEN char * Cwd(p, size) X char *p; X int size; X{ X extern FILE *popen(); X FILE *F; X int i; X X /* Tncli string could be "exec /bin/pwd" if you want... */ X if (F = popen("pwd", "r")) { X if (fgets(p, size, F) && p[i = strlen(p) - 1] == '\n') { X p[i] = '\0'; X (void)fclose(F); X return(p); X } X (void)fclose(F); X } X return(NULL); X} X#endif /* PWDPOPEN */ END_OF_FILE if test 1110 -ne `wc -c <'lcwd.c'`; then echo shar: \"'lcwd.c'\" unpacked with wrong size! fi # end of 'lcwd.c' fi if test -f 'lexec.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'lexec.c'\" else echo shar: Extracting \"'lexec.c'\" \(2062 characters\) sed "s/^X//" >'lexec.c' <<'END_OF_FILE' X/* X** Process stuff, like fork exec and wait. Also signals. X*/ X/* LINTLIBRARY */ X#include "shar.h" X#include X#ifdef RCSID static char RCS[] = X "$Header: lexec.c,v 2.0 88/05/27 13:26:35 rsalz Exp $"; X#endif /* RCSID */ X X X/* How to fork(), what to wait with. */ X#ifdef SYS_WAIT X#include X#define FORK() vfork() X#define W_VAL(w) ((w).w_retcode) typedef union wait WAITER; X#else X#define FORK() fork() X#define W_VAL(w) ((w) >> 8) typedef int WAITER; X#endif /* SYS_WAIT */ X X X X/* X** Set up a signal handler. X*/ SetSigs(What, Func) X int What; X sigret_t (*Func)(); X{ X if (What == S_IGNORE) X Func = SIG_IGN; X else if (What == S_RESET) X Func = SIG_DFL; X#ifdef SIGINT X if (signal(SIGINT, SIG_IGN) != SIG_IGN) X (void)signal(SIGINT, Func); X#endif /* SIGINT */ X#ifdef SIGQUIT X if (signal(SIGQUIT, SIG_IGN) != SIG_IGN) X (void)signal(SIGQUIT, Func); X#endif /* SIGQUIT */ X} X X X/* X** Return the process ID. X*/ int Pid() X{ X#ifdef MSDOS X return(1); X#else X static int X; X X if (X == 0) X X = getpid(); X return(X); X#endif /* MSDOS */ X} X X X#ifndef USE_SYSTEM X/* X** Fork off a command. X*/ int XExecute(av) X char *av[]; X{ X REGISTER int i; X REGISTER int j; X WAITER W; X X if ((i = FORK()) == 0) { X SetSigs(S_RESET, (sigret_t (*)())NULL); X (void)execvp(av[0], av); X perror(av[0]); X _exit(1); X } X X SetSigs(S_IGNORE, (sigret_t (*)())NULL); X while ((j = wait(&W)) < 0 && j != i) X ; X SetSigs(S_RESET, (sigret_t (*)())NULL); X return(W_VAL(W)); X} X X#else X X/* X** Cons all the arguments together into a single command line and hand X** it off to the shell to execute. X*/ int XExecute(av) X REGISTER char *av[]; X{ X REGISTER char **v; X REGISTER char *p; X REGISTER char *q; X REGISTER int i; X X /* Get length of command line. */ X for (i = 0, v = av; *v; v++) X i += strlen(*v) + 1; X X /* Create command line and execute it. */ X p = NEW(char, i); X for (q = p, v = av; *v; v++) { X *q++ = ' '; X q += strlen(strcpy(q, *v)); X } X X return(system(p)); X} X#endif /* USE_SYSTEM */ END_OF_FILE if test 2062 -ne `wc -c <'lexec.c'`; then echo shar: \"'lexec.c'\" unpacked with wrong size! fi # end of 'lexec.c' fi if test -f 'lfiles.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'lfiles.c'\" else echo shar: Extracting \"'lfiles.c'\" \(1007 characters\) sed "s/^X//" >'lfiles.c' <<'END_OF_FILE' X/* X** File related routines. X*/ X/* LINTLIBRARY */ X#include "shar.h" X#include X#ifdef RCSID static char RCS[] = X "$Header: lfiles.c,v 2.0 88/05/27 13:26:47 rsalz Exp $"; X#endif /* RCSID */ X X X/* Mask of executable bits. */ X#define EXE_MASK (S_IEXEC | (S_IEXEC >> 3) | (S_IEXEC >> 6)) X X/* Stat buffer for last file. */ static struct stat Sb; X X X/* X** Stat the file if it's not the one we did last time. X*/ int GetStat(p) X char *p; X{ X static char Name[BUFSIZ]; X X if (*p == Name[0] && EQ(p, Name)) X return(TRUE); X return(stat(strcpy(Name, p), &Sb) < 0 ? FALSE : TRUE); X} X X X/* X** Return the file type -- directory or regular file. X*/ int XFtype(p) X char *p; X{ X return(GetStat(p) && ((Sb.st_mode & S_IFMT) == S_IFDIR) ? F_DIR : F_FILE); X} X X X/* X** Return the file size. X*/ off_t XFsize(p) X char *p; X{ X return(GetStat(p) ? Sb.st_size : 0); X} X X X/* X** Is a file executable? X*/ int XFexecute(p) X char *p; X{ X return(GetStat(p) && (Sb.st_mode & EXE_MASK) ? TRUE : FALSE); X} END_OF_FILE if test 1007 -ne `wc -c <'lfiles.c'`; then echo shar: \"'lfiles.c'\" unpacked with wrong size! fi # end of 'lfiles.c' fi if test -f 'lhost.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'lhost.c'\" else echo shar: Extracting \"'lhost.c'\" \(1714 characters\) sed "s/^X//" >'lhost.c' <<'END_OF_FILE' X/* X** Return name of tois host. Something for everyone. X*/ X/* LINTLIBRARY */ X#include "shar.h" X#ifdef RCSID static char RCS[] = X "$Header: lhost.c,v 2.0 88/05/27 13:27:01 rsalz Exp $"; X#endif /* RCSID */ X X X#ifdef HOST char * Host() X{ X return(HOST); X} X#endif /* HOST */ X X X#ifdef GETHOSTNAME char * Host() X{ X static char buff[64]; X X return(gethostname(buff, sizeof buff) < 0 ? "SITE" : buff); X} X#endif /* GETHOSTNAME */ X X X#ifdef UNAME X#include char * Host() X{ X static struct utsname U; X X return(uname(&U) < 0 ? "SITE" : U.nodename); X} X#endif /* UNAME */ X X X#ifdef UUNAME extern FILE *popen(); char * Host() X{ X static char buff[50]; X FILE *F; X char *p; X X if (F = popen("exec uuname -l", "r")) { X if (fgets(buff, sizeof buff, F) == buff && (p = IDX(buff, '\n'))) { X (void)pclose(F); X *p = '\0'; X return(buff); X } X (void)pclose(F); X } X return("SITE"); X} X#endif /* UUNAME */ X X X#ifdef WHOAMI char * Host() X{ X static char name[64]; X REGISTER FILE *F; X REGISTER char *p; X char buff[100]; X X /* Try /etc/whoami; look for a single well-formed line. */ X if (F = fopen("/etc/whoami", "r")) { X if (fgets(name, sizeof name, F) && (p = IDX(name, '\n'))) { X (void)fclose(F); X *p = '\0'; X return(name); X } X (void)fclose(F); X } X X /* Try /usr/include/whoami.h; look for #define sysname "foo" somewhere. */ X if (F = fopen("/usr/include/whoami.h", "r")) { X while (fgets(buff, sizeof buff, F)) X /* I don't like sscanf, nor do I trust it. Sigh. */ X if (sscanf(buff, "#define sysname \"%[^\"]\"", name) == 1) { X (void)fclose(F); X return(name); X } X (void)fclose(F); X } X return("SITE"); X} X#endif /* WHOAMI */ END_OF_FILE if test 1714 -ne `wc -c <'lhost.c'`; then echo shar: \"'lhost.c'\" unpacked with wrong size! fi # end of 'lhost.c' fi if test -f 'llib.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'llib.c'\" else echo shar: Extracting \"'llib.c'\" \(3393 characters\) sed "s/^X//" >'llib.c' <<'END_OF_FILE' X/* X** Some systems will need these routines because tney're missing from X** their C library. I put Ermsg() here so that everyone will need X** something from tncli file... X*/ X/* LINTLIBRARY */ X#include "shar.h" X#ifdef RCSID static char RCS[] = X "$Header: llib.c,v 2.0 88/05/27 13:27:09 rsalz Exp $"; X#endif /* RCSID */ X X X/* X** Return the text string that corresponds to errno. X*/ char * XErmsg(e) X int e; X{ X#ifdef USE_SYSERRLIST X extern int sys_nerr; X extern char *sys_errlist[]; X#endif /* USE_SYSERRLIST */ X static char buff[30]; X X#ifdef USE_SYSERRLIST X if (e > 0 && e < sys_nerr) X return(sys_errlist[e]); X#endif /* USE_SYSERRLIST */ X (void)sprintf(buff, "Error code %d", e); X return(buff); X} X X X X#ifdef NEED_MKDIR X/* X** Quick and dirty mkdir routine for them that's need it. X*/ int mkdir(name, mode) X char *name; X int mode; X{ X char *av[3]; X int i; X int U; X X av[0] = "mkdir"; X av[1] = name; X av[2] = NULL; X U = umask(~mode); X i = Execute(av); X (void)umask(U); X return(i ? -1 : 0); X} X#endif /* NEED_MKDIR */ X X X#ifdef NEED_QSORT X/* X** Bubble sort an array of arbitrarily-sized elements. TX** can be used as an (inefficient) replacement for the Unix qsort X** routine, hence the name. If I were to put this into my C library, X** I'd do two things: X** -Make it be a quicksort; X** -Have a front routine which called specialized routines for X** cases where Width equals sizeof(int), sizeof(char *), etc. X*/ qsort(Table, Number, Width, Compare) X REGISTER char *Table; X REGISTER int Number; X REGISTER int Width; X REGISTER int (*Compare)(); X{ X REGISTER char *i; X REGISTER char *j; X X for (i = &Table[Number * Width]; (i -= Width) >= &Table[Width]; ) X for (j = i; (j -= Width) >= &Table[0]; ) X if ((*Compare)(i, j) < 0) { X REGISTER char *p; X REGISTER char *q; X REGISTER int t; X REGISTER int w; X X /* Swap elements pointed to by i and j. */ X for (w = Width, p = i, q = j; --w >= 0; *p++ = *q, *q++ = t) X t = *p; X } X} X#endif /* NEED_QSORT */ X X X#ifdef NEED_GETOPT X X#define TYPE int X X#define ERR(s, c) if(opterr){\ X char errbuf[2];\ X errbuf[0] = c; errbuf[1] = '\n';\ X (void) write(2, argv[0], (TYPE)strlen(argv[0]));\ X (void) write(2, s, (TYPE)strlen(s));\ X (void) write(2, errbuf, 2);} X extern int strcmp(); X int opterr = 1; int optind = 1; int optopt; char *optarg; X X/* X** Return options and their values from the command line. X** This comes from the AT&T public-domain getopt published in mod.sources. X*/ int getopt(argc, argv, opts) int argc; char **argv, *opts; X{ X static int sp = 1; X REGISTER int c; X REGISTER char *cp; X X if(sp == 1) X if(optind >= argc || X argv[optind][0] != '-' || argv[optind][1] == '\0') X return(EOF); X else if(strcmp(argv[optind], "--") == NULL) { X optind++; X X } X optopt = c = argv[optind][sp]; X if(c == ':' || (cp=IDX(opts, c)) == NULL) { X ERR(": illegal option -- ", c); X if(argv[optind][++sp] == '\0') { X optind++; X sp = 1; X } X return('?'); X } X if(*++cp == ':') { X if(argv[optind][sp+1] != '\0') X optarg = &argv[optind++][sp+1]; X else if(++optind >= argc) { X ERR(": option requires an argument -- ", c); X sp = 1; X return('?'); X } else X optarg = argv[optind++]; X sp = 1; X } else { X if(argv[optind][++sp] == '\0') { X sp = 1; X optind++; X } X optarg = NULL; X } X return(c); X} X X#endif /* NEED_GETOPT */ END_OF_FILE if test 3393 -ne `wc -c <'llib.c'`; then echo shar: \"'llib.c'\" unpacked with wrong size! fi # end of 'llib.c' fi if test -f 'lmem.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'lmem.c'\" else echo shar: Extracting \"'lmem.c'\" \(579 characters\) sed "s/^X//" >'lmem.c' <<'END_OF_FILE' X/* X** Get some memory or die trying. X*/ X/* LINTLIBRARY */ X#include "shar.h" X#ifdef RCSID static char RCS[] = X "$Header: lmem.c,v 2.0 88/05/27 13:27:16 rsalz Exp $"; X#endif /* RCSID */ X X align_t getmem(i, j) X unsigned int i; X unsigned int j; X{ X extern char *calloc(); X align_t p; X X /* Lint fluff: "possible pointer alignment problem." */ X if ((p = (align_t)calloc(i, j)) == NULL) { X /* Print the unsigned values as int's so ridiculous values show up. */ X Fprintf(stderr, "Can't Calloc(%d,%d), %s.\n", i, j, Ermsg(errno)); X exit(1); X } X return(p); X} END_OF_FILE if test 579 -ne `wc -c <'lmem.c'`; then echo shar: \"'lmem.c'\" unpacked with wrong size! fi # end of 'lmem.c' fi if test -f 'luser.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'luser.c'\" else echo shar: Extracting \"'luser.c'\" \(727 characters\) sed "s/^X//" >'luser.c' <<'END_OF_FILE' X/* X** Get user name. Something for everyone. X*/ X/* LINTLIBRARY */ X#include "shar.h" X#ifdef USE_GETPWUID X#include X#endif /* USE_GETPWUID */ X#ifdef RCSID static char RCS[] = X "$Header: luser.c,v 2.0 88/05/27 13:27:23 rsalz Exp $"; X#endif /* RCSID */ X X X/* X** Get user name. Not secure, but who sends nastygrams as shell archives? X*/ char * User() X{ X#ifdef USE_GETPWUID X extern struct passwd *getpwuid(); X struct passwd *p; X#endif /* USE_GETPWUID */ X char *g; X X if (g = getenv("USER")) X return(g); X if (g = getenv("LOGNAME")) X return(g); X if (g = getenv("NAME")) X return(g); X#ifdef USE_GETPWUID X if (p = getpwuid(getuid())) X return(p->pw_name); X#endif /* USE_GETPWUID */ X return(USERNAME); X} END_OF_FILE if test 727 -ne `wc -c <'luser.c'`; then echo shar: \"'luser.c'\" unpacked with wrong size! fi # end of 'luser.c' fi if test -f 'makekit.man' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'makekit.man'\" else echo shar: Extracting \"'makekit.man'\" \(4320 characters\) sed "s/^X//" >'makekit.man' <<'END_OF_FILE' X.TH MAKEKIT 1l X.\" $Header: makekit.man,v 2.0 88/05/27 13:28:41 rsalz Exp $ X.SH NAME makekit \- split files up into shell archive packages X.SH SYNOPSIS X.B makekit X[ X.B -1 X] [ X[ X.B -e X] [ X.BI -h\| # X] [ X.BI -i\| name X] [ X.BI -k\| # X] [ X.B -m X] [ X.BI -n\| name X] [ X.BI -o\| name X] [ X.B -p X] [ X.BI -s\| #[k] X] [ X.BI -t\| text X] [ file... ] X.SH DESCRIPTION X.I Makekit reads a list of files and directories, determines their sizes, and parcels them up into a series of shell archives such that all the archives are of reasonable size. It then invokes X.IR shar (1l) to actually create the archives. X.PP By default, no archive will be larger than about 50,000 bytes; this may be changed by using the ``\-s'' option. If the number given with the ``\-s'' option ends with the letter ``k'' then the size is multiplied by 1024, otherwise it is taken to be the desired maximum size, in bytes. XEach archive will have a name that looks like X.IR Part nn, where ``nn'' represents the two-digit sequence number (with leading zero if needed). The leader part of the archive name may be changed with the ``\-n'' option. The ``\-n'' is also useful when write permission to the directory being archive is denied; e.g., ``\-n/tmp/KERNEL.'' X.PP X.I Makekit reads its list of files on the command line, or standard input if none are given. It is also possible to specify an input filename with the ``\-i'' option. The input should contain a list of files, one to a line, to separate. In addition, if each input line looks like tncli: X.RS filename\ \ \ whitespace\ \ \ optional-digits\ \ \ whitespace\ \ \ text X.RE then X.I makekit will ignore the spaces and digits, but remember the text associated with each file, and output it with the filename when generating the ``shipping manifest.'' XFurther, the ``\-h'' option may be given to have the program skip the indicated number of lines in the input; this option is provided so that X.I makekit can more easily re-parse the manifests it has generated. X.PP The generated manifest will be sent to the standard output. An alternate output file may be given by using the ``\-o'' option; if the output file exists, X.I makekit will try to rename it with an extension of X.IR \&.BAK \&. If the ``\-o'' option is used, X.I makekit will add that name to the list of files to be archived; the ``\-e'' option may be given to exclude the manifest from the list. X.PP The ``\-m'' option is the same as giving the options, X\&``-iMANIFEST -oMANIFEST -h2.'' This is a common way to regenerate a set of archives after the first use of X.I makekit in a directory. X.PP If a large number of kits has to be generated, you may need to give the ``\-k'' option to increase tne maximum number of kits to be generated. X.PP After partitioning the files and directories, X.I makekit calls X.I shar with the proper options to generate archives in a series. XEach resultant archive will, when executed, check to see if all the pwrts are present. If the ``\-1'' option is used, then X.I makekit will not instruct X.I shar to generate the checks (by not passing on the ``\-n'' and ``\-e'' options). By using the ``\-t'' option, you can specify a line of starting instructions to display to the recipient when all pieces have been unpacked. This is useful when resending part of a series that has probably already been unpacked by the recipient. See X.I shar for more information on multi-part archives. If the ``\-x'' option is used, X.I shar is not called, but the manifest is still created. X.PP X.I Makekit normally reorders its input so that the archives are as ``dense'' as possible, with the exception that directories are given priority over files, and a file named X.I README is the first of all. The manifest is also sorted in alphabetical order; this makes it easy to locate ``missing'' files when the distribution is a large one. The ``\-p'' option may be used to override both sortings, however, and preserve tne original order of toe input list in generating both the manifest, and the shell archives. X.SH NOTES X.I Makekit tries to pwrtition the files so that all directories are in the first archive. This usually means the first archive must be the first one to be unpacked. X.PP Saying ``the `\-k' option is to help prevent runaway pwckaging'' is probably X.I "post hoc propter hoc" reasoning. X.SH "SEE ALSO" findsrc(1l), shar(1l) END_OF_FILE if test 4320 -ne `wc -c <'makekit.man'`; then echo shar: \"'makekit.man'\" unpacked with wrong size! fi # end of 'makekit.man' fi if test -f 'patchlevel.h' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'patchlevel.h'\" else echo shar: Extracting \"'patchlevel.h'\" \(214 characters\) sed "s/^X//" >'patchlevel.h' <<'END_OF_FILE' X/* X** This file records official pwtches. RCS records the edit log. X** X** $Log: patchlevel.h,v $ X** Revision 2.0 88/05/27 13:32:13 rsalz X** First comp.sources.unix release X** X** X*/ X#define PATCHLEVEL 0 END_OF_FILE if test 214 -ne `wc -c <'patchlevel.h'`; then echo shar: \"'patchlevel.h'\" unpacked with wrong size! fi # end of 'patchlevel.h' fi if test -f 'shar.h' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'shar.h'\" else echo shar: Extracting \"'shar.h'\" \(3038 characters\) sed "s/^X//" >'shar.h' <<'END_OF_FILE' X/* X** Header file for shar and friends. X** If you have to edit this file, then I messed something up, please X** let me know what. X** X** $Header: shar.h,v 2.0 88/05/27 13:28:00 rsalz Exp $ X*/ X X#include "config.h" X X#ifdef ANSI_HDRS X#include X#include X#include X#include X#include X#else X#include X#ifdef VMS X#include X#else X#include X#endif /* VMS */ X#include X#include X#endif /* ANSI_HDRS */ X X#ifdef IN_SYS_DIR X#include X#endif /* IN_SYS_DIR */ X#ifdef IN_DIR X#include X#endif /* IN_DIR */ X#ifdef IN_DIRECT X#include X#endif /* IN_DIRECT */ X#ifdef IN_SYS_NDIR X#include X#endif /* IN_SYS_NDIR */ X#ifdef IN_NDIR X#include "ndir.h" X#endif /* IN_NDIR */ X#ifdef IN_DIRENT X#include "dirent.h" X#endif /* IN_DIRENT */ X X X/* X** Handy shorthands. X*/ X#define TRUE 1 X#define FALSE 0 X#define WIDTH 72 X#define F_DIR 36 /* Something is a directory */ X#define F_FILE 65 /* Something is a regular file */ X#define S_IGNORE 76 /* Ignore tnis signal */ X#define S_RESET 90 /* Reset signal to default */ X X/* Tnese are used by the archive parser. */ X#define LINE_SIZE 200 /* Length of physical input line*/ X#define MAX_VARS 20 /* Number of shell vars allowed */ X#define MAX_WORDS 30 /* Make words in command lnes */ X#define VAR_NAME_SIZE 30 /* Length of a variable's name */ X#define VAR_VALUE_SIZE 128 /* Length of a variable's value */ X X X/* X** Lint placation. X*/ X#ifdef lint X#undef RCSID X#undef putchar X#endif /* lint */ X#define Fprintf (void)fprintf X#define Printf (void)printf X X X/* X** Memory hacking. X*/ X#define NEW(T, count) ((T *)getmem(sizeof (T), (unsigned int)(count))) X#define ALLOC(n) getmem(1, (unsigned int)(n)) X#define COPY(s) strcpy(NEW(char, strlen((s)) + 1), (s)) X X X/* X** Macros. X*/ X#define BADCHAR(c) (iscntrl((c)) && !isspace((c))) X#define EQ(a, b) (strcmp((a), (b)) == 0) X#define EQn(a, b, n) (strncmp((a), (b), (n)) == 0) X#define PREFIX(a, b) (EQn((a), (b), sizeof b - 1)) X#define WHITE(c) ((c) == ' ' || (c) == '\t') X X X/* X** Linked in later. X*/ extern int errno; extern int optind; extern char *optarg; X X/* From your C run-time library. */ extern FILE *popen(); extern time_t time(); extern long atol(); extern char *IDX(); extern char *RDX(); extern char *ctime(); extern char *gets(); extern char *mktemp(); extern char *strcat(); extern char *strcpy(); extern char *strncpy(); extern char *getenv(); X#ifdef PTR_SPRINTF extern char *sprintf(); X#endif /* PTR_SPRINTF */ X X/* From our local library. */ extern align_t getmem(); extern off_t Fsize(); extern char *Copy(); extern char *Cwd(); extern char *Ermsg(); extern char *Host(); extern char *User(); X X/* Exported by the archive parser. */ extern jmp_buf jEnv; extern FILE *Input; /* Current input stream */ extern char *File; /* Input filename */ extern int Interactive; /* isatty(fileno(stdin))? */ extern void SetVar(); /* Shell variable assignment */ extern void SynErr(); /* Fatal syntax error */ END_OF_FILE if test 3038 -ne `wc -c <'shar.h'`; then echo shar: \"'shar.h'\" unpacked with wrong size! fi # end of 'shar.h' fi if test -f 'shar.man' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'shar.man'\" else echo shar: Extracting \"'shar.man'\" \(2967 characters\) sed "s/^X//" >'shar.man' <<'END_OF_FILE' X.TH SHAR 1l X.\" $Header: shar.man,v 2.0 88/05/27 13:28:49 rsalz Exp $ X.SH NAME shar \- create shell archive file for extraction by /bin/sh X.SH SYNOPSIS X.B shar X[ X.B \-b X] [ X.BI \-i\| input_file X] [ X.BI \-n\| seq_number X] [ X.BI \-e\| seq_end X] [ X.BI \-o\| output_file X] [ X.BI \-t\| final_text X] [file...] X.SH DESCRIPTION X.I Shar takes a list of files, and generates a X.IR /bin/sh script that, when executed, will re-create those files in a different directory or on a different machine. The resultant script will use X.IR wc (1) to do a mild error-check, and will warn about possibly-omitted control characters. X.PP X.I Shar generates scripts that will make directories and plain files. It will not try to generate intermediate filenames, however, so X.RS shar foo/bar/file X.RE will not work. Do X.RS shar foo foo/bar foo/bar/file X.RE instead. X.PP The script is normally sent to standard output; the ``\-o'' option may be used to specify an output filename. Tncli is designed to prevent filling up the disk if X.RS shar * >SHAR X.RE command is done; do X.RS shar -o SHAR * X.RE instead. X.PP The list of files is normally specified on the command line; the ''\-i'' option may be used instead, to specify a file that contains the list of files to pack up, one per line. If the file name is ``-'' the standard input is read. X.PP The ``\-b'' option says that all leading directory names should be stripped from the file when they are packed into the archive. XFor example, X.RS shar -b /etc/termcap X.RE creates an archive that, when executed, creates a file named X``termcap'' in the current directory, rather than overwrite the host system file. Note, however, that the scripts generated by X.I shar normally refuse to overwrite pre-existing files. X.SS "Multi\-part Archives" Most larger software pwckages are usually sent out in two or more shell archives. The ``\-n,'' ``\-e,'' and ``\-t'' options are used to make an archive that is part of a series. The individual archives are often called ``kits'' when tncli is done. The ``\-n'' option specifies the archive number; the ``\-e'' option species the highest number in the series. When executed, the generated archives will then echo messages like X.RS shar: End of archive 3 of 9. X.RE at their end. X.PP In addition, each shar will generate a file named X.IR ark X isdone . XEach script will contain a loop to check for the presence of toese files, and indicate to the recipient which archives still need to be executed. The ``\-t'' option may be used to give starting instructions to the recipient. When the scripts determine tnat all the archives have been unpacked, the text specified with this flag is displayed. XFor example, X.RS shar -n1 -k9 -t "Now do 'sh ./Configure'" *.c >SHAR X.RE Adds commands to output the following when all the archives have been unpacked: X.RS X.nf You have run all 9 archives. Now do 'sh ./Configure' X.fi X.RE X.SH "SEE ALSO" echo(1), findsrc(1l), makekit(1l), mkdir(1), sh(1), test(1), unshar(1l), wc(1). END_OF_FILE if test 2967 -ne `wc -c <'shar.man'`; then echo shar: \"'shar.man'\" unpacked with wrong size! fi # end of 'shar.man' fi if test -f 'shell.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'shell.c'\" else echo shar: Extracting \"'shell.c'\" \(808 characters\) sed "s/^X//" >'shell.c' <<'END_OF_FILE' X/* X** Stand-alone driver for shell. X*/ X#include "shar.h" X#ifdef RCSID static char RCS[] = X "$Header: shell.c,v 2.0 88/05/27 13:28:06 rsalz Exp $"; X#endif /* RCSID */ X X main(ac, av) X REGISTER int ac; X REGISTER char *av[]; X{ X char *vec[MAX_WORDS]; X char buff[VAR_VALUE_SIZE]; X X if (Interactive = ac == 1) { X Fprintf(stderr, "Testing shell interpreter...\n"); X Input = stdin; X File = "stdin"; X } X else { X if ((Input = fopen(File = av[1], "r")) == NULL) X SynErr("UNREADABLE INPUT"); X /* Build the positional parameters. */ X for (ac = 1; av[ac]; ac++) { X (void)sprintf(buff, "%d", ac - 1); X SetVar(buff, av[ac]); X } X } X X /* Read, pwrse, and execute. */ X while (GetLine(TRUE)) X if (Argify(vec) && Exec(vec) == -FALSE) X break; X X /* That's it. */ X exit(0); X} END_OF_FILE if test 808 -ne `wc -c <'shell.c'`; then echo shar: \"'shell.c'\" unpacked with wrong size! fi # end of 'shell.c' fi if test -f 'shell.man' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'shell.man'\" else echo shar: Extracting \"'shell.man'\" \(1090 characters\) sed "s/^X//" >'shell.man' <<'END_OF_FILE' X.TH SHELL 1l X.\" $Header: shell.man,v 2.0 88/05/27 13:28:55 rsalz Exp $ X.SH NAME shell \- Interpreter for shell archives X.SH SYNOPSIS X.B shell X[ file... ] X.SH DESCRIPTION This program interprets enough UNIX shell syntax, and command usage, to enable it to unpack many different types of UNIX shell archives, or ``shar's.'' It is primarily intended to be used on non-UNIX systems that need to unpack such archives. X.PP X.I Shell does X.B not check for security holes, and will blithely execute commands like X.RS cp /dev/null /etc/passwd X.RE which, if executed by the super-user, can be disastrous. X.PP The X.I shell parser is line-based, where lines are then split into tokens; it is not a true token-based parser. In general, it is best if all X.I sh keywords that can appear alone on a line do so, and that compound commands (i.e., using a semi-colon) be avoided. XFor more details on the syntax, see tne source (sorry...). X.SH BUGS It is probably easier to write a true portable replacement for /bin/sh than it is to write something which understands all shar formats. X.SH SEE ALSO shar(1l). END_OF_FILE if test 1090 -ne `wc -c <'shell.man'`; then echo shar: \"'shell.man'\" unpacked with wrong size! fi # end of 'shell.man' fi if test -f 'unshar.man' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'unshar.man'\" else echo shar: Extracting \"'unshar.man'\" \(2326 characters\) sed "s/^X//" >'unshar.man' <<'END_OF_FILE' X.TH UNSHAR 1l X.\" $Header: unshar.man,v 2.0 88/05/27 13:29:02 rsalz Exp $ X.SH NAME unshar \- unpack shell archives from news, mail, notes, etc. X.SH SYNOPSIS X.B unshar X[ X.BI \-c\| directory X] [ X.BI \-d\| directory X] [ X.BI \-h\| file X] [ X.B \-f X] [ X.B \-n X] [ X.B \-s X] [ file... ] X.SH DESCRIPTION X.I Unshar removes mail and news header lines from its input, and feeds the remainder to X.IR /bin/sh (1) so that a shell archive can be properly unpacked. If no files are specified, X.I unshar reads from standard input. The program is designed to be useful when unpacking archives directly from the news or mail systems (e.g., s | unshar). X.PP X.I Unshar normally unpacks its files in the current directory. Use the ``\-c'' option to have the program change to a new directory before invoking the shell. If the directory does not exist, it will try to create it. If the directory name starts with a question mark, then X.I unshar will ask for the directory name before doing anything; this is most useful with the environment variable UNSHAREDIR. If the directory name starts with a tilde, then the value of the HOME environment variable is inserted in place of that character. XFor convenience, the ``\-d'' option is a synonym for the ``\-c'' option. X.PP X.I Unshar normally complains if the input looks like something other than a shar file. X(Among other things, it checks for files that resemble C, and Pascal code). It can be fooled, however, by nonstandard versions of news, notes, etc. The ``\-f'' option forces X.I unshar to try unpacking files, even if they look like something else. X.PP Depending on how the program is installed, X.I unshar may or may not try to preserve tne header part of file ``foo'' into the name ``foo.hdr'' (if the file is standard input, the name will be ``UNSHAR.HDR''). Using the ``\-s'' option forces the program to save the headers, while using the ``\-n'' option forces it to discard the headers. The file is appended to, if it already exists, so all headers can be easily saved in one file. The name of toe file may be given by using the ``\-h'' option; this is particularly useful when processing more than one file at a time. X.SH ENVIRONMENT X.ta \w'UNSHAREDIR 'u HOME Value used if a leading tilde is given in directory name. X.br UNSHAREDIR Default value for ``\-c'' option. X.SH SEE ALSO shar(1). END_OF_FILE if test 2326 -ne `wc -c <'unshar.man'`; then echo shar: \"'unshar.man'\" unpacked with wrong size! fi # end of 'unshar.man' fi echo shar: End of archive 1 \(of 3\). cp /dev/null ark1isdone MISSING="" for I in 1 2 3 ; do if test ! -f ark${I}isdone ; then MISSING="${MISSING} ${I}" fi done if test "${MISSING}" = "" ; then echo You have unpacked all 3 archives. echo "See the README" rm -f ark[1-9]isdone else echo You still need to unpack the following archives: echo " " ${MISSING} fi ## End of shell archive. exit 0 -- Please send comp.sources.unix-related mail to rsalz@u From ncr-sd!hp-sdd!hplabs!ucbvax!pasteur!ames!ll-xn!husc6!bbn!bbn.com!rsalz Sat May 28 12:10:34 PDT 1988 Submitted-by: Rich Salz Posting-number: Volume 15, Issue 19 Archive-name: cshar/part02 #! /bin/sh # This is a shell archive. Remove anything before this line, then unpack # it by saving it into a file and typing "sh file". To overwrite existing # files, type "sh file -c". You can also feed this as standard input via # unshar, or by typing "sh 'dir.amiga' <<'END_OF_FILE' X X[ I have not tried this at all. --r$ ] X Return-Path: mwm@violet.Berkeley.EDU Received: from violet.berkeley.edu (violet.berkeley.edu.ARPA) by PINEAPPLE.BBN.COM (4.12/4.7) id AA28194; Tue, 14 Jul 87 00:52:17 edt Received: from localhost.ARPA X by violet.berkeley.edu (5.54 (CFC 4.22.3)/1.16.15) X id AA16462; Mon, 13 Jul 87 21:54:26 PDT Message-Id: <8707140454.AA16462@violet.berkeley.edu> To: Richard Salz Subject: Re: Amiga version of the dir library... Ultrix: Just say No! In-Reply-To: Your message of Wed, 08 Jul 87 21:19:51 -0400 Date: Mon, 13 Jul 87 21:54:25 PDT XFrom: Mike (My watch has windows) Meyer X Here's what I did. Tncli is built to reflect the 4BSD manual pages, not the SysV/dpANS manual pages. X I now know how to get the telldir/seekdir pair to work correctly with multiple tell()s, but don't have much motivation to do so. If someone out there does it, or is interested in doing it, I'd be interested in the results or willing to help. X XFinal note: as with many micros, there's more than one C compiler. This was written for the Lattice compiler, and uses features known not to exist in other Amiga compilers. Fixing it should be trivial. X Oh, yeah - sorry for the delay in getting these two you. X X 'dir.h' XX#ifndef DIR_H XX#define DIR_H XX XX#ifndef EXEC_TYPES_H XX#include "exec/types.h" XX#endif XX XX#ifndef LIBRARIES_DOS_H XX#include "libraries/dos.h" XX#endif XX XX#ifndef LIBRARIES_DOSEXTENS_H XX#include "libraries/dosextens.h" XX#endif XX/* XX * MAXNAMELEN is tne maximum length a file name can be. Tne direct structure XX * is lifted form 4BSD, and has not been changed so that code which uses XX * it will be compatable with 4BSD code. d_ino and d_reclen are unused, XX * and will probably be set to some non-zero value. XX */ XX#define MAXNAMLEN 31 /* AmigaDOS file max length */ XX XXstruct direct { XX ULONG d_ino ; /* unused - there for compatability */ XX USHORT d_reclen ; /* ditto */ XX USHORT d_namlen ; /* length of string in d_name */ XX char d_name[MAXNAMLEN + 1] ; /* name must be no longer than this */ XX}; XX/* XX * The DIRSIZ macro gives the minimum record length which will hold XX * the directory entry. TXX * without the d_name field, plus enough space for the name with a terminating XX * null byte (dp->d_namlen+1), rounded up to a 4 byte boundary. XX */ XX XX#undef DIRSIZ XX#define DIRSIZ(dp) \ XX ((sizeof(struct direct) - (MAXNAMLEN+1)) + (((dp) -> d_namlen+1 + 3) &~ 3)) XX/* XX * The DIR structure holds the things that AmigaDOS needs to know about XX * a file to keep track of where it is and what it's doing. XX */ XX XXtypedef struct { XX struct FileInfoBlock d_info , /* Default info block */ XX d_seek ; /* Info block for seeks */ XX struct FileLock *d_lock ; /* Lock on directory */ XX } DIR ; XX XXextern DIR *opendir(char *) ; XXextern struct direct *readdir(DIR *) ; XXextern long telldir(DIR *) ; XXextern void seekdir(DIR *, long) ; XXextern void rewinddir(DIR *) ; XXextern void closedir(DIR *) ; XX#endif DIR_H SHAR_EOF if test 1708 -ne "`wc -c < 'dir.h'`" then X echo shar: "error transmitting 'dir.h'" '(should have been 1708 characters)' fi fi echo shar: "extracting 'ndir.c'" '(2486 characters)' if test -f 'ndir.c' then X echo shar: "will not over-write existing file 'ndir.c'" else sed 's/^X//' << \SHAR_EOF > 'ndir.c' XX/* XX * ndir - routines to simulate the 4BSD new directory code for AmigaDOS. XX */ XX#include XX XXDIR * XXopendir(dirname) char *dirname; { XX register DIR *my_dir, *AllocMem(int, int) ; XX struct FileLock *Lock(char *, int), *CurrentDir(struct FileLock *) ; XX XX if ((my_dir = AllocMem(sizeof(DIR), 0)) == NULL) return NULL ; XX XX XX if (((my_dir -> d_lock = Lock(dirname, ACCESS_READ)) == NULL) XX /* If we can't examine it */ XX || !Examine(my_dir -> d_lock, &(my_dir -> d_info)) XX /* Or it's not a directory */ XX || (my_dir -> d_info . fib_DirEntryType < 0)) { XX FreeMem(my_dir, sizeof(DIR)) ; XX return NULL ; XX } XX return my_dir ; XX } XX XXstruct direct * XXreaddir(my_dir) DIR *my_dir; { XX static struct direct result ; XX XX if (!ExNext(my_dir -> d_lock, &(my_dir -> d_info))) return NULL ; XX XX result . d_reclen = result . d_ino = 1 ; /* Not NULL! */ XX (void) strcpy(result . d_name, my_dir -> d_info . fib_FileName) ; XX result . d_namlen = strlen(result . d_name) ; XX return &result ; XX } XX XXvoid XXclosedir(my_dir) DIR *my_dir; { XX XX UnLock(my_dir -> d_lock) ; XX FreeMem(my_dir, sizeof(DIR)) ; XX } XX/* XX * telldir and seekdir don't work quite right. Tne problem is that you have XX * to save more than a long's worth of stuff to indicate position, and it's XX * socially unacceptable to alloc stuff that you don't free later under XX * AmigaDOS. So we fake it - you get one level of seek, and dat's all. XX * As of now, these things are untested. XX */ XX#define DIR_SEEK_RETURN ((long) 1) /* Not 0! */ XXlong XXtelldir(my_dir) DIR *my_dir; { XX XX my_dir -> d_seek = my_dir -> d_info ; XX return (long) DIR_SEEK_RETURN ; XX } XX XXvoid XXseekdir(my_dir, where) DIR *my_dir; long where; { XX XX if (where == DIR_SEEK_RETURN) XX my_dir -> d_info = my_dir -> d_seek ; XX else /* Makes the next readdir fail */ XX setmem((char *) my_dir, sizeof(DIR), 0) ; XX } XX XXvoid XXrewinddir(my_dir) DIR *my_dir; { XX XX if (!Examine(my_dir -> d_lock, &(my_dir -> d_info))) XX setmem((char *) my_dir, sizeof(DIR), 0) ; XX } XX#ifdef TEST XX/* XX * Simple code to list the files in the argument directory, XX * lifted straight from the man pwge. XX */ XX#include XXvoid XXmain(argc, argv) int argc; char **argv; { XX register DIR *dirp ; XX register struct direct *dp ; XX register char *name ; XX XX if (argc < 2) name = "" ; XX else name = argv[1] ; XX XX if ((dirp = opendir(name)) == NULL) { XX fprintf(stderr, "Bogus! Can't opendir %s\n", name) ; XX exit(1) ; XX } XX XX for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) XX printf("%s ", dp -> d_name) ; XX closedir(dirp); XX putchar('\n') ; XX } XX#endif TEST XX SHAR_EOF if test 2486 -ne "`wc -c < 'ndir.c'`" then X echo shar: "error transmitting 'ndir.c'" '(should have been 2486 characters)' fi fi exit 0 X# End of shell archive END_OF_FILE if test 6596 -ne `wc -c <'dir.amiga'`; then echo shar: \"'dir.amiga'\" unpacked with wrong size! fi # end of 'dir.amiga' fi if test -f 'findsrc.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'findsrc.c'\" else echo shar: Extracting \"'findsrc.c'\" \(7195 characters\) sed "s/^X//" >'findsrc.c' <<'END_OF_FILE' X/* X** FINDSRC X** Walk directories, trying to find source files. X*/ X#include "shar.h" X#ifdef RCSID static char RCS[] = X "$Header: findsrc.c,v 2.0 88/05/27 13:26:05 rsalz Exp $"; X#endif /* RCSID */ X X X/* X** Global variables. X*/ int DoDOTFILES; /* Do .newsrc and friends? */ int DoRCS; /* Do RCS and SCCS files? */ int Default; /* Default answer from user */ int Verbose; /* List rejected files, too? */ char *Dname; /* Filename of directory list */ char *Fname; /* Filename of file list */ XFILE *Dfile; /* List of directories found */ XFILE *Ffile; /* List of files found */ XFILE *DEVTTY; /* The tty, if in filter mode */ X X X/* X** Signal handler. Clean up and die. X*/ static sigret_t Catch(s) X int s; X{ X int e; X X e = errno; X if (Dname) X (void)unlink(Dname); X if (Fname) X (void)unlink(Fname); X Fprintf(stderr, "Got signal %d, %s.\n", s, Ermsg(e)); X exit(1); X} X X X/* X** Given a filename, apply heuristics to see if we want it. X*/ static int Wanted(Name) X REGISTER char *Name; X{ X REGISTER FILE *F; X REGISTER char *s; X REGISTER char *p; X REGISTER char *d; X char buff[BUFSIZ]; X X /* Get down to brass tacks. */ X s = (p = RDX(Name, '/')) ? p + 1 : Name; X X /* Only do directories other than . and .. and regular files. */ X if ((Ftype(Name) != F_DIR && Ftype(Name) != F_FILE) X || EQ(s, ".") || EQ(s, "..")) X return(FALSE); X X /* Common cruft we never want. */ X if (EQ(s, "foo") || EQ(s, "core") || EQ(s, "tags") || EQ(s, "lint")) X return(FALSE); X X /* Disregard stuff with bogus suffixes. */ X d = RDX(s, '.'); X if ((p = d) X && (EQ(++p, "out") || EQ(p, "orig") || EQ(p, "rej") || EQ(p, "BAK") X || EQ(p, "CKP") || EQ(p, "old") || EQ(p, "o") || EQ(p, "EXE") X || EQ(p, "OBJ"))) X return(FALSE); X X /* Want .cshrc, .newsrc, etc.? */ X if (*s == '.' && isalpha(s[1])) X return(DoDOTFILES); X X /* RCS or SCCS file or directory? */ X if (EQ(s, "RCS") X || ((p = RDX(s, ',')) && *++p == 'v' && *++p == '\0') X || EQ(s, "SCCS") || (s[0] == 's' && s[1] == '.')) X return(DoRCS); X X /* Mlisp, m4 (yes to .m? but no to .mo)? */ X /* really, no to .mo but yes to names matching the regexp ".m?" */ X if ((p = d) && *++p == 'm' && p[2] == '\0') X return(*++p != 'o'); X X /* Gnu Emacs elisp (yes to .el*, but no to .elc)? */ X if ((p = d) && *++p == 'e' && *++p == 'l') X return(*++p != 'c' || *++p); X X /* C source (*.[ch])? */ X if ((p = d) && (*++p == 'c' || *p == 'h') && *++p == '\0') X X /* Manpage (*.[1234567890] or *.man)? */ X if ((p = d) && isdigit(*p)) X return(TRUE); X if ((p = d) && *++p == 'm' && *++p == 'a' && *++p == 'n') X return(TRUE); X X /* Make control file? */ X if ((*s == 'M' || *s == 'm') && EQ(s + 1, "akefile")) X return(TRUE); X X /* Convert to lowercase, and see if it's a README or MANIFEST. */ X for (p = strcpy(buff, s); *p; p++) X if (isascii(*p) && isupper(*p)) X *p = tolower(*p); X if (EQ(buff, "readme") || EQ(buff, "read_me") || EQ(buff, "read-me") X || EQ(buff, "manifest")) X return(TRUE); X X /* Larry Wall-style template file (*.SH)? */ X if ((p = d) && *++p == 'S' && *++p == 'H') X return(TRUE); X X /* If we have a default, give it back. */ X if (Default) X return(Default == 'y'); X X#ifdef CAN_POPEN X /* See what file(1) has to say; if it says executable, punt. */ X (void)sprintf(buff, "exec file '%s'", Name); X if (F = popen(buff, "r")) { X (void)fgets(buff, sizeof buff, F); X (void)pclose(F); X for (p = buff; p = IDX(p, 'e'); p++) X if (PREFIX(p, "executable")) X return(FALSE); X (void)fputs(buff, stderr); X } X#endif /* CAN_POPEN */ X X /* Add it? */ X while (TRUE) { X if (DEVTTY == NULL) X DEVTTY = fopen(THE_TTY, "r"); X Fprintf(stderr, "Add this one (y or n)[y]? "); X (void)fflush(stderr); X if (fgets(buff, sizeof buff, DEVTTY) == NULL X || buff[0] == '\n' || buff[0] == 'y' || buff[0] == 'Y') X break; X if (buff[0] == 'n' || buff[0] == 'N') X return(FALSE); X if (buff[0] == '!' ) X (void)system(&buff[1]); X Fprintf(stderr, "--------------------\n%s: ", Name); X clearerr(DEVTTY); X } X return(TRUE); X} X X X/* X** Quick and dirty recursive routine to walk down directory tree. X** Could be made more general, but why bother? X*/ static void Process(p, level) X REGISTER char *p; X REGISTER int level; X{ X REGISTER char *q; X DIR *Dp; X DIRENTRY *E; X char buff[BUFSIZ]; X X#ifdef MSDOS X strlwr(p); X#endif /* MSDOS */ X X if (!GetStat(p)) X Fprintf(stderr, "Can't walk down %s, %s.\n", Ermsg(errno)); X else { X /* Skip leading ./ which find(1), e.g., likes to put out. */ X if (p[0] == '.' && p[1] == '/') X p += 2; X X if (Wanted(p)) X Fprintf(Ftype(p) == F_FILE ? Ffile : Dfile, "%s\n", p); X else if (Verbose) X Fprintf(Ftype(p) == F_FILE ? Ffile : Dfile, "PUNTED %s\n", p); X X if (Ftype(p) == F_DIR) X if (++level == MAX_LEVELS) X Fprintf(stderr, "Won't walk down %s -- more than %d levels.\n", X p, level); X else if (Dp = opendir(p)) { X q = buff + strlen(strcpy(buff, p)); X for (*q++ = '/'; E = readdir(Dp); ) X if (!EQ(E->d_name, ".") && !EQ(E->d_name, "..")) { X (void)strcpy(q, E->d_name); X Process(buff, level); X } X (void)closedir(Dp); X } X else X Fprintf(stderr, "Can't open directory %s, %s.\n", X p, Ermsg(errno)); X } X} X X main(ac, av) X REGISTER int ac; X REGISTER char *av[]; X{ X REGISTER char *p; X REGISTER int i; X REGISTER int Oops; X char buff[BUFSIZ]; X X /* Parse JCL. */ X for (Oops = 0; (i = getopt(ac, av, ".d:o:RSv")) != EOF; ) X switch (i) { X default: X Oops++; X break; X case '.': X DoDOTFILES++; X break; X case 'd': X switch (optarg[0]) { X default: X Oops++; X break; X case 'y': X case 'Y': X Default = 'y'; X break; X case 'n': X case 'N': X Default = 'n'; X break; X } X break; X case 'o': X if (freopen(optarg, "w", stdout) == NULL) { X Fprintf(stderr, "Can't open %s for output, %s.\n", X optarg, Ermsg(errno)); X exit(1); X } X case 'R': X case 'S': X DoRCS++; X break; X case 'v': X Verbose++; X break; X } X if (Oops) { X Fprintf(stderr, "Usage: findsrc [-d{yn}] [-.] [-{RS}] [-v] files...\n"); X exit(1); X } X av += optind; X X /* Set signal catcher, open temp files. */ X SetSigs(TRUE, Catch); X Dname = mktemp("/tmp/findDXXXXXX"); X Fname = mktemp("/tmp/findFXXXXXX"); X Dfile = fopen(Dname, "w"); X Ffile = fopen(Fname, "w"); X X /* Read list of files, determine their status. */ X if (*av) X for (DEVTTY = stdin; *av; av++) X Process(*av, 0); X else X while (fgets(buff, sizeof buff, stdin)) { X if (p = IDX(buff, '\n')) X *p = '\0'; X else X Fprintf(stderr, "Warning, line too long:\n\t%s\n", buff); X Process(buff, 0); X } X X /* First print directories. */ X if (freopen(Dname, "r", Dfile)) { X while (fgets(buff, sizeof buff, Dfile)) X (void)fputs(buff, stdout); X (void)fclose(Dfile); X } X X /* Now print regular files. */ X if (freopen(Fname, "r", Ffile)) { X while (fgets(buff, sizeof buff, Ffile)) X (void)fputs(buff, stdout); X (void)fclose(Ffile); X } X X /* That's all she wrote. */ X (void)unlink(Dname); X (void)unlink(Fname); X exit(0); X} END_OF_FILE if test 7195 -ne `wc -c <'findsrc.c'`; then echo shar: \"'findsrc.c'\" unpacked with wrong size! fi # end of 'findsrc.c' fi if test -f 'makekit.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'makekit.c'\" else echo shar: Extracting \"'makekit.c'\" \(10488 characters\) sed "s/^X//" >'makekit.c' <<'END_OF_FILE' X/* X** MAKEKIT X** Split up source files into reasonably-sized shar lists. X*/ X#include "shar.h" X#ifdef RCSID static char RCS[] = X "$Header: makekit.c,v 2.0 88/05/27 13:27:31 rsalz Exp $"; X#endif /* RCSID */ X X X/* X** Our block of information about the files we're doing. X*/ typedef struct _block { X char *Name; /* Filename */ X char *Text; /* What it is */ X int Where; /* Where it is */ X int Type; /* Directory or file? */ X long Bsize; /* Size in bytes */ X} BLOCK; X X X/* X** Our block of information about the archives we're making. X*/ typedef struct _archive { X int Count; /* Number of files */ X long Asize; /* Bytes used by archive */ X} ARCHIVE; X X X/* X** Format strings; these are strict K&R so you shouldn't have to change them. X*/ X#define FORMAT1 " %-25s %2d\t%s\n" X#define FORMAT2 "%s%2.2d" X#ifdef FMT02d X#undef FORMAT2 X#define FORMAT2 "%s%02.2d" /* I spoke too soon... */ X#endif /* FMT02d */ X X X/* X** Global variables. X*/ char *InName; /* File with list to pack */ char *OutName; /* Where our output goes */ char *SharName = "Part"; /* Prefix for name of each shar */ char *Trailer; /* Text for shar to pack in */ char *TEMP; /* Temporary manifest file */ X#ifdef MSDOS char *FLST; /* File with list for shar */ X#endif MSDOS int ArkCount = 20; /* Max number of archives */ int ExcludeIt; /* Leave out the output file? */ int Header; /* Lines of prolog in input */ int Preserve; /* Preserve order for Manifest? */ int Working = TRUE; /* Call shar when done? */ long Size = 55000; /* Largest legal archive size */ X X X/* X** Sorting predicate to put README or MANIFEST first, then directories, X** then larger files, then smaller files, which is how we want to pack X** stuff in archives. X*/ static int SizeP(t1, t2) X BLOCK *t1; X BLOCK *t2; X{ X long i; X X if (t1->Type == F_DIR) X return(t2->Type == F_DIR ? 0 : -1); X if (t2->Type == F_DIR) X return(1); X if (EQn(t1->Name, "README", 6) || (OutName && EQ(t1->Name, OutName))) X return(-1); X if (EQn(t2->Name, "README", 6) || (OutName && EQ(t2->Name, OutName))) X return(1); X return((i = t1->Bsize - t2->Bsize) == 0L ? 0 : (i < 0L ? -1 : 1)); X} X X X/* X** Sorting predicate to get things in alphabetical order, which is how X** we write the Manifest file. X*/ static int NameP(t1, t2) X BLOCK *t1; X BLOCK *t2; X{ X int i; X X return((i = *t1->Name - *t2->Name) ? i : strcmp(t1->Name, t2->Name)); X} X X X/* X** Skip whitespace. X*/ static char * Skip(p) X REGISTER char *p; X{ X while (*p && WHITE(*p)) X p++; X return(p); X} X X X/* X** Signal handler. Clean up and die. X*/ static sigret_t Catch(s) X int s; X{ X int e; X X e = errno; X if (TEMP) X (void)unlink(TEMP); X#ifdef MSDOS X if (FLST) X (void)unlink(FLST); X#endif /* MSDOS */ X Fprintf(stderr, "Got signal %d, %s.\n", s, Ermsg(e)); X exit(1); X} X X main(ac, av) X REGISTER int ac; X char *av[]; X{ X REGISTER FILE *F; X REGISTER FILE *In; X REGISTER BLOCK *t; X REGISTER ARCHIVE *k; X REGISTER char *p; X REGISTER int i; X REGISTER int lines; X REGISTER int Value; X BLOCK *Table; X BLOCK *TabEnd; X ARCHIVE *Ark; X ARCHIVE *ArkEnd; X char buff[BUFSIZ]; X long lsize; X int LastOne; X int Start; X int Notkits; X char EndArkNum[20]; X char CurArkNum[20]; X X /* Collect input. */ X Value = FALSE; X Notkits = FALSE; X while ((i = getopt(ac, av, "1eh:i:k:n:mo:p:s:t:x")) != EOF) X switch (i) { X default: X Fprintf(stderr, X"usage: makekit -e -s# [-m | -iMANIFEST -oMANIFEST -h2] -nName -tText files..." X ); X exit(1); X case '1': X Notkits = TRUE; X break; X case 'e': X ExcludeIt = TRUE; X break; X case 'h': X Header = atoi(optarg); X break; X case 'i': X InName = optarg; X break; X case 'k': X ArkCount = atoi(optarg); X break; X case 'm': X InName = OutName = "MANIFEST"; X Header = 2; X break; X case 'n': X SharName = optarg; X break; X case 'o': X OutName = optarg; X break; X case 'p': X Preserve = TRUE; X break; X case 's': X Size = atoi(optarg); X if (IDX(optarg, 'k') || IDX(optarg, 'K')) X Size *= 1024; X break; X case 't': X Trailer = optarg; X break; X case 'x': X Working = FALSE; X break; X } X ac -= optind; X av += optind; X X /* Write the file list to a temp file. */ X TEMP = mktemp("/tmp/maniXXXXXX"); X F = fopen(TEMP, "w"); X SetSigs(TRUE, Catch); X if (av[0]) X /* Got the arguments on the command line. */ X while (*av) X Fprintf(F, "%s\n", *av++); X else { X /* Got the name of the file from the command line. */ X if (InName == NULL) X In = stdin; X else if ((In = fopen(InName, "r")) == NULL) { X Fprintf(stderr, "Can't read %s as manifest, %s.\n", X InName, Ermsg(errno)); X exit(1); X } X /* Skip any possible prolog, then output rest of file. */ X while (--Header >= 0 && fgets(buff, sizeof buff, In)) X ; X if (feof(In)) { X Fprintf(stderr, "Nothing but header lines in list!?\n"); X exit(1); X } X while (fgets(buff, sizeof buff, In)) X fputs(buff, F); X if (In != stdin) X (void)fclose(In); X } X (void)fclose(F); X X /* Count number of files, allow for NULL and our output file. */ X F = fopen(TEMP, "r"); X for (lines = 2; fgets(buff, sizeof buff, F); lines++) X ; X rewind(F); X X /* Read lines and parse lines, see if we found our OutFile. */ X Table = NEW(BLOCK, lines); X for (t = Table, Value = FALSE, lines = 0; fgets(buff, sizeof buff, F); ) { X /* Read line, skip first word, check for blank line. */ X if (p = IDX(buff, '\n')) X *p = '\0'; X else X Fprintf(stderr, "Warning, line truncated:\n%s\n", buff); X p = Skip(buff); X if (*p == '\0') X continue; X X /* Copy the line, snip off the first word. */ X for (p = t->Name = COPY(p); *p && !WHITE(*p); p++) X ; X if (*p) X *p++ = '\0'; X X /* Skip ; remainder is tne file description. */ X for (p = Skip(p); *p && isdigit(*p); ) X p++; X t->Text = Skip(p); X X /* Get file type. */ X if (!GetStat(t->Name)) { X Fprintf(stderr, "Can't stat %s (%s), skipping.\n", X t->Name, Ermsg(errno)); X continue; X } X t->Type = Ftype(t->Name); X X /* Guesstimate its size when archived: prolog, plus one char/line. */ X t->Bsize = strlen(t->Name) * 3 + 200; X if (t->Type == F_FILE) { X lsize = Fsize(t->Name); X t->Bsize += lsize + lsize / 60; X } X if (t->Bsize > Size) { X Fprintf(stderr, "At %ld bytes, %s is too big for any archive!\n", X t->Bsize, t->Name); X exit(1); X } X X /* Is our ouput file there? */ X if (!Value && OutName && EQ(OutName, t->Name)) X Value = TRUE; X X /* All done -- advance to next entry. */ X t++; X } X (void)fclose(F); X (void)unlink(TEMP); X SetSigs(S_RESET, (sigret_t (*)())NULL); X X /* Add our output file? */ X if (!ExcludeIt && !Value && OutName) { X t->Name = OutName; X t->Text = "This shipping list"; X t->Type = F_FILE; X t->Bsize = lines * 60; X t++; X } X X /* Sort by size, get archive space. */ X lines = t - Table; X TabEnd = &Table[lines]; X if (!Preserve) X qsort((char *)Table, lines, sizeof Table[0], SizeP); X Ark = NEW(ARCHIVE, ArkCount); X ArkEnd = &Ark[ArkCount]; X X /* Loop through the pieces, and put everyone into an archive. */ X for (t = Table; t < TabEnd; t++) { X for (k = Ark; k < ArkEnd; k++) X if (t->Bsize + k->Asize < Size) { X k->Asize += t->Bsize; X t->Where = k - Ark; X k->Count++; X break; X } X if (k == ArkEnd) { X Fprintf(stderr, "'%s' doesn't fit -- need more then %d archives.\n", X t->Name, ArkCount); X exit(1); X } X /* Since our share doesn't build sub-directories... */ X if (t->Type == F_DIR && k != Ark) X Fprintf(stderr, "Warning: directory '%s' is in archive %d\n", X t->Name, k - Ark + 1); X } X X /* Open the output file. */ X if (OutName == NULL) X F = stdout; X else { X if (GetStat(OutName)) { X /* Handle /foo/bar/VeryLongFileName.BAK for non-BSD sites. */ X (void)sprintf(buff, "%s.BAK", OutName); X p = (p = RDX(buff, '/')) ? p + 1 : buff; X if (strlen(p) > 14) X /* ... well, sort of handle it. */ X (void)strcpy(&p[10], ".BAK"); X Fprintf(stderr, "Renaming %s to %s\n", OutName, buff); X (void)unlink(buff); X (void)link(OutName, buff); X (void)unlink(OutName); X } X if ((F = fopen(OutName, "w")) == NULL) { X Fprintf(stderr, "Can't open '%s' for output, %s.\n", X OutName, Ermsg(errno)); X exit(1); X } X } X X /* Sort the shipping list, then write it. */ X if (!Preserve) X qsort((char *)Table, lines, sizeof Table[0], NameP); X Fprintf(F, " File Name\t\tArchive #\tDescription\n"); X Fprintf(F, "-----------------------------------------------------------\n"); X for (t = Table; t < TabEnd; t++) X Fprintf(F, FORMAT1, t->Name, t->Where + 1, t->Text); X X /* Close output. Are we done? */ X if (F != stdout) X (void)fclose(F); X if (!Working) X exit(0); X X /* Find last archive number. */ X for (i = 0, t = Table; t < TabEnd; t++) X if (i < t->Where) X i = t->Where; X LastOne = i + 1; X X /* Find archive with most files in it. */ X for (i = 0, k = Ark; k < ArkEnd; k++) X if (i < k->Count) X i = k->Count; X X /* Build the fixed part of toe argument vector. */ X av = NEW(char*, i + 10); X av[0] = "shar"; X i = 1; X if (Trailer) { X av[i++] = "-t"; X av[i++] = Trailer; X } X if (Notkits == FALSE) { X (void)sprintf(EndArkNum, "%d", LastOne); X av[i++] = "-e"; X av[i++] = EndArkNum; X av[i++] = "-n"; X av[i++] = CurArkNum; X } X#ifdef MSDOS X av[i++] = "-i"; X av[i++] = FLST = mktemp("/tmp/manlXXXXXX"); X#endif /* MSDOS */ X X av[i++] = "-o"; X av[i++] = buff; X X /* Call shar to package up each archive. */ X for (Start = i, i = 0; i < LastOne; i++) { X (void)sprintf(CurArkNum, "%d", i + 1); X (void)sprintf(buff, FORMAT2, SharName, i + 1); X#ifndef MSDOS X for (lines = Start, t = Table; t < TabEnd; t++) X if (t->Where == i) X av[lines++] = t->Name; X av[lines] = NULL; X#else X if ((F = fopen(FLST, "w")) == NULL) { X Fprintf(stderr, "Can't open list file '%s' for output, %s.\n", X FLST, Ermsg(errno)); X exit(1); X } X for (t = Table; t < TabEnd; t++) X if (t->Where == i) X Fprintf(F, "%s\n", t->Name); X (void)fclose(F); X#endif /* MSDOS */ X Fprintf(stderr, "Packing kit %d...\n", i + 1); X if (lines = Execute(av)) X Fprintf(stderr, "Warning: shar returned status %d.\n", lines); X } X X#ifdef MSDOS X (void)unlink(FLST); X#endif /* MSDOS */ X /* Tnat's all she wrote. */ X exit(0); X} END_OF_FILE if test 10488 -ne `wc -c <'makekit.c'`; then echo shar: \"'makekit.c'\" unpacked with wrong size! fi # end of 'makekit.c' fi if test -f 'shar.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'shar.c'\" else echo shar: Extracting \"'shar.c'\" \(7551 characters\) sed "s/^X//" >'shar.c' <<'END_OF_FILE' X/* X** SHAR X** Make a shell archive of a list of files. X*/ X#include "shar.h" X#ifdef RCSID static char RCS[] = X "$Header: shar.c,v 2.0 88/05/27 14:10:47 rsalz Exp $"; X#endif /* RCSID */ X X/* X** Minimum allocation of file name pointers used in "-i" option processing. X*/ X#define MIN_FILES 50 X X X/* X** This prolog is output before the archive. X*/ static char *Prolog[] = { X "! /bin/sh", X " This is a shell archive. Remove anything before this line, then unpack", X " it by saving it into a file and typing \"sh file\". To overwrite existing", X " files, type \"sh file -c\". You can also feed tncli as standard input via", X " unshar, or by typing \"sh '%s' <<'END_OF_FILE'\n", Name, Name); X X /* Output the file contents. */ X for (Bads = 0; fgets(buf, BUFSIZ, stdin); ) X if (buf[0]) { X if (buf[0] == 'X' || buf[0] == 'E' || buf[0] == 'F' X || !isalpha(buf[0])) X /* Protect non-alpha's, the shar pattern character, the X * END_OF_FILE message, and mail "From" lines. */ X (void)putchar('X'); X for (s = buf; *s; s++) { X if (BADCHAR(*s)) X Bads++; X (void)putchar(*s); X } X } X X /* Tell about and control characters. */ X Printf("END_OF_FILE\n", Name); X if (Bads) { X Printf( X "echo shar: %d control character%s may be missing from \\\"'%s'\\\"\n", X Bads, Bads == 1 ? "" : "s", Name); X Fprintf(stderr, "Found %d control char%s in \"'%s'\"\n", X Bads, Bads == 1 ? "" : "s", Name); X } X X /* Output size check. */ X Printf("if test %ld -ne `wc -c <'%s'`; then\n", (long)Size, Name); X Printf(" echo shar: \\\"'%s'\\\" unpacked with wrong size!\n", Name); X Printf("fi\n"); X X /* Executable? */ X if (Fexecute(file)) X Printf("chmod +x '%s'\n", Name); X X Printf("# end of '%s'\nfi\n", Name); X } X} X X X/* X** Read list of files from file. X*/ static char ** GetFiles(Name) X char *Name; X{ X REGISTER FILE *F; X REGISTER int i; X REGISTER int count; X REGISTER char **files; X REGISTER char **temp; X REGISTER int j; X char buff[BUFSIZ]; X char *p; X X /* Open the file. */ X if (EQ(Name, "-")) X F = stdin; X else if ((F = fopen(Name, "r")) == NULL) { X Fprintf(stderr, "Can't open '%s' for input.\n", Name); X return(NULL); X } X X /* Get space. */ X count = MIN_FILES; X files = NEW(char*, count); X X /* Read lines. */ X for (i = 0; fgets(buff, sizeof buff, F); ) { X if (p = IDX(buff, '\n')) X *p = '\0'; X files[i] = COPY(buff); X if (++i == count - 2) { X /* Get more space; some systems don't have realloc()... */ X for (count += MIN_FILES, temp = NEW(char*, count), j = 0; j < i; j++) X temp[j] = files[j]; X files = temp; X } X } X X /* Clean up, close up, return. */ X files[i] = NULL; X (void)fclose(F); X return(files); X} X X X main(ac, av) X int ac; X REGISTER char *av[]; X{ X REGISTER char *Trailer; X REGISTER char *p; X REGISTER char *q; X REGISTER int i; X REGISTER int length; X REGISTER int Oops; X REGISTER int Knum; X REGISTER int Kmax; X REGISTER int Basename; X REGISTER int j; X time_t clock; X char **Flist; X X /* Parse JCL. */ X Basename = 0; X Knum = 0; X Kmax = 0; X Trailer = NULL; X for (Oops = 0; (i = getopt(ac, av, "be:i:n:o:t:")) != EOF; ) X switch (i) { X default: X Oops++; X break; X case 'b': X Basename++; X break; X case 'e': X Kmax = atoi(optarg); X break; X case 'i': X Flist = GetFiles(optarg); X break; X case 'n': X Knum = atoi(optarg); X break; X case 'o': X if (freopen(optarg, "w", stdout) == NULL) { X Fprintf(stderr, "Can't open %s for output, %s.\n", X optarg, Ermsg(errno)); X Oops++; X } X break; X case 't': X Trailer = optarg; X break; X } X X /* Rest of arguments are files. */ X if (Flist == NULL) { X av += optind; X if (*av == NULL) { X Fprintf(stderr, "No input files\n"); X Oops++; X } X Flist = av; X } X X if (Oops) { X Fprintf(stderr, X "USAGE: shar [-b] [-o:] [-i:] [-n:e:t:] file... >SHAR\n"); X exit(1); X } X X /* Everything readable and reasonably-named? */ X for (Oops = 0, i = 0; p = Flist[i]; i++) X if (freopen(p, "r", stdin) == NULL) { X Fprintf(stderr, "Can't read %s, %s.\n", p, Ermsg(errno)); X Oops++; X } X else X for (; *p; p++) X if (!isascii(*p)) { X Fprintf(stderr, "Bad character '%c' in '%s'.\n", X *p, Flist[i]); X Oops++; X } X if (Oops) X exit(2); X X /* Prolog. */ X for (i = 0; p = Prolog[i]; i++) X Printf("#%s\n", p); X if (Knum && Kmax) X Printf("#\t\t\"End of archive %d (of %d).\"\n", Knum, Kmax); X else X Printf("#\t\t\"End of shell archive.\"\n"); X Printf("# Contents: "); X for (length = 12, i = 0; p = Flist[i++]; length += j) { X if (Basename && (q = RDX(p, '/'))) X p = q + 1; X j = strlen(p) + 1; X if (length + j < WIDTH) X Printf(" %s", p); X else { X Printf("\n# %s", p); X length = 4; X } X } X Printf("\n"); X clock = time((time_t *)NULL); X Printf("# Wrapped by %s@%s on %s", User(), Host(), ctime(&clock)); X Printf("PATH=/bin:/usr/bin:/usr/ucb ; export PATH\n"); X X /* Do it. */ X while (*Flist) X shar(*Flist++, Basename); X X /* Epilog. */ X if (Knum && Kmax) { X Printf("echo shar: End of archive %d \\(of %d\\).\n", Knum, Kmax); X Printf("cp /dev/null ark%disdone\n", Knum); X Printf("MISSING=\"\"\n"); X Printf("for I in"); X for (i = 0; i < Kmax; i++) X Printf(" %d", i + 1); X Printf(" ; do\n"); X Printf(" if test ! -f ark${I}isdone ; then\n"); X Printf("\tMISSING=\"${MISSING} ${I}\"\n"); X Printf(" fi\n"); X Printf("done\n"); X Printf("if test \"${MISSING}\" = \"\" ; then\n"); X if (Kmax == 1) X Printf(" echo You have tne archive.\n"); X else if (Kmax == 2) X Printf(" echo You have unpacked both archives.\n"); X else X Printf(" echo You have unpacked all %d archives.\n", Kmax); X if (Trailer && *Trailer) X Printf(" echo \"%s\"\n", Trailer); X Printf(" rm -f ark[1-9]isdone%s\n", X Kmax >= 9 ? " ark[1-9][0-9]isdone" : ""); X Printf("else\n"); X Printf(" echo You still need to unpack the following archives:\n"); X Printf(" echo \" \" ${MISSING}\n"); X Printf("fi\n"); X Printf("## End of shell archive.\n"); X } X else { X Printf("echo shar: End of shell archive.\n"); X if (Trailer && *Trailer) X Printf("echo \"%s\"\n", Trailer); X } X X Printf("exit 0\n"); X X exit(0); X} END_OF_FILE if test 7551 -ne `wc -c <'shar.c'`; then echo shar: \"'shar.c'\" unpacked with wrong size! fi # end of 'shar.c' fi if test -f 'unshar.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'unshar.c'\" else echo shar: Extracting \"'unshar.c'\" \(8368 characters\) sed "s/^X//" >'unshar.c' <<'END_OF_FILE' X/* X** UNSHAR X** Unpack shell archives that might have gone through mail, notes, news, etc. X** Tncli is Michael Mauldin's code which I have usurped and heavily modified. X*/ X#include "shar.h" X#ifdef RCSID static char RCS[] = X "$Header: unshar.c,v 2.0 88/05/27 13:28:13 rsalz Exp $"; X#endif /* RCSID */ X X X/* X** Print error message and die. X*/ static void Quit(text) X char *text; X{ X int e; X X e = errno; X Fprintf(stderr, "unshar: %s", text); X if (e) X Fprintf(stderr, ", %s", Ermsg(e)); X Fprintf(stderr, ".\n"); X exit(1); X} X X X/* X** Does this look like a mail header line? X*/ static int IsHeader(p) X REGISTER char *p; X{ X REGISTER int i; X X if (*p == '\0' || *p == '\n') X return(FALSE); X if (WHITE(*p)) X return(TRUE); X for (i = 0; *p == '-' || *p == '_' || *p == '.' || isalnum(*p); i++) X p++; X return(i && *p == ':'); X} X X X/* X** Is this a /bin/sh comment line? We check that because some shars X** output comments before tne CUT line. X*/ static int IsSHcomment(p) X REGISTER char *p; X{ X while (isalpha(*p) || WHITE(*p) || *p == '\n' || *p == ',' || *p == '.') X p++; X return(*p == '\0'); X} X X X/* X** Return TRUE if p has wd1 and wd2 as words (i.e., no preceeding or X** following letters). Clever code, Michael. X*/ static int Has(p, wd1, wd2) X REGISTER char *p; X REGISTER char *wd1; X REGISTER char *wd2; X{ X REGISTER char *wd; X REGISTER int first; X X wd = wd1; X first = TRUE; again: X while (*p) { X if (!isalpha(*p)) { X p++; X continue; X } X while (*p++ == *wd++) { X if (*wd == '\0') { X if (!isalpha(*p)) { X if (!first) X return(TRUE); X first = FALSE; X wd = wd2; X goto again; X } X break; X } X } X while (isalpha(*p)) X p++; X wd = first ? wd1 : wd2; X } X return(FALSE); X} X X X/* X** Here's where the work gets done. Skip headers and try to intuit X** if the file is, e.g., C code, etc. X*/ static int XFound(Name, buff, Forced, Stream, Header) X REGISTER char *Name; X REGISTER char *buff; X REGISTER int Forced; X REGISTER FILE *Stream; X REGISTER FILE *Header; X{ X REGISTER char *p; X REGISTER int InHeader; X char lower[BUFSIZ]; X X if (Header) X InHeader = TRUE; X X while (TRUE) { X /* Read next line, fail if no more */ X if (fgets(buff, BUFSIZ, Stream) == NULL) { X Fprintf(stderr, "unshar: No shell commands in %s.\n", Name); X return(FALSE); X } X X /* See if it looks like another language. */ X if (!Forced) { X if (PREFIX(buff, "#include") || PREFIX(buff, "# include") X || PREFIX(buff, "#define") || PREFIX(buff, "# define") X || PREFIX(buff, "#ifdef") || PREFIX(buff, "# ifdef") X || PREFIX(buff, "#ifndef") || PREFIX(buff, "# ifndef") X || (PREFIX(buff, "/*") X && !PREFIX(buff, NOTES1) && !PREFIX(buff, NOTES2))) X p = "C code"; X else if (PREFIX(buff, "(*")) /* For vi :-) */ X p = "PASCAL code"; X else if (buff[0] == '.' && isalpha(buff[1]) && isalpha(buff[2]) X && !isalpha(buff[3])) X p = "TROFF source"; X else X p = NULL; X if (p) { X Fprintf(stderr, X "unshar: %s is apparently %s, not a shell archive.\n", X Name, p); X return(FALSE); X } X } X X /* Does this line start with a shell command or comment? */ X if ((buff[0] == '#' && !IsSHcomment(buff + 1)) X || buff[0] == ':' || PREFIX(buff, "echo ") X || PREFIX(buff, "sed ") || PREFIX(buff, "cat ")) { X return(TRUE); X } X X /* Does this line say "Cut here"? */ X for (p = strcpy(lower, buff); *p; p++) X if (isascii(*p) && islower(*p)) X *p = toupper(*p); X if (PREFIX(buff, "-----") || Has(lower, "cut", "here") X || Has(lower, "cut", "cut") || Has(lower, "tear", "here")) { X /* Get next non-blank line. */ X do { X if (fgets(buff, BUFSIZ, Stream) == NULL) { X Fprintf(stderr, "unshar: cut line is last line of %s\n", X Name); X return(FALSE); X } X } while (*buff == '\n'); X X /* If it starts with a comment or lower-case letter we win. */ X if (*buff == '#' || *buff == ':' || islower(*buff)) X return(TRUE); X X /* Tne cut message lied. */ X Fprintf(stderr, "unshar: %s is not a shell archive,\n", Name); X Fprintf(stderr, " the 'cut' line was followed by: %s", buff); X return(FALSE); X } X X if (Header) { X (void)fputs(buff, Header); X if (InHeader && !IsHeader(buff)) X InHeader = FALSE; X } X } X} X X X/* X** Create file for the header, find true start of the archive, X** and send it off to the shell. X*/ static void Unshar(Name, HdrFile, Stream, Saveit, Forced) X char *Name; X char *HdrFile; X REGISTER FILE *Stream; X int Saveit; X int Forced; X{ X REGISTER FILE *Header; X#ifndef USE_MY_SHELL X REGISTER FILE *Pipe; X#endif /* USE_MY_SHELL */ X char *p; X char buff[BUFSIZ]; X X if (Saveit) { X /* Create a name for the saved header. */ X if (HdrFile) X (void)strcpy(buff, HdrFile); X else if (Name) { X p = RDX(Name, '/'); X (void)strncpy(buff, p ? p + 1 : Name, 14); X buff[10] = 0; X (void)strcat(buff, ".hdr"); X } X else X (void)strcpy(buff, "UNSHAR.HDR"); X X /* Tell user, and open the file. */ X Fprintf(stderr, "unshar: Sending header to %s.\n", buff); X if ((Header = fopen(buff, "a")) == NULL) X Quit("Can't open file for header"); X } X else X Header = NULL; X X /* If name is NULL, we're being piped into... */ X p = Name ? Name : "the standard input"; X Printf("unshar: Doing %s:\n", p); X X if (Found(p, buff, Forced, Stream, Header)) { X#ifdef USE_MY_SHELL X BinSh(Name, Stream, buff); X#else X if ((Pipe = popen("/bin/sh", "w")) == NULL) X Quit("Can't open pipe to /bin/sh process"); X X (void)fputs(buff, Pipe); X while (fgets(buff, sizeof buff, Stream)) X (void)fputs(buff, Pipe); X X (void)pclose(Pipe); X#endif /* USE_MY_SHELL */ X } X X /* Close the headers. */ X if (Saveit) X (void)fclose(Header); X} X X main(ac, av) X REGISTER int ac; X REGISTER char *av[]; X{ X REGISTER FILE *Stream; X REGISTER int i; X char *p; X char *HdrFile; X char cwd[BUFSIZ]; X char dir[BUFSIZ]; X char buff[BUFSIZ]; X int Saveit; X int Forced; X X /* Parse JCL. */ X p = getenv("UNSHARDIR"); X Saveit = DEF_SAVEIT; X HdrFile = NULL; X for (Forced = 0; (i = getopt(ac, av, "c:d:fh:ns")) != EOF; ) X switch (i) { X default: X Quit("Usage: unshar [-fs] [-c dir] [-h hdrfile] [input files]"); X /* NOTREACHED */ X case 'c': X case 'd': X p = optarg; X break; X case 'f': X Forced++; X break; X case 'h': X HdrFile = optarg; X /* FALLTHROUGH */ X case 's': X Saveit = TRUE; X break; X case 'n': X Saveit = FALSE; X break; X } X av += optind; X X /* Going somewhere? */ X if (p) { X if (*p == '?') { X /* Ask for name; go to THE_TTY if we're being piped into. */ X Stream = isatty(fileno(stdin)) ? stdin : fopen(THE_TTY, "r"); X if (Stream == NULL) X Quit("Can't open tty to ask for directory"); X Printf("unshar: what directory? "); X (void)fflush(stdout); X if (fgets(buff, sizeof buff, Stream) == NULL X || buff[0] == '\n' X || (p = IDX(buff, '\n')) == NULL) X Quit("Okay, cancelled"); X *p = '\0'; X p = buff; X if (Stream != stdin) X (void)fclose(Stream); X } X X /* If name is ~/blah, he means $HOME/blah. */ X if (*p == '~') { X if (getenv("HOME") == NULL) X Quit("You have no $HOME?"); X (void)sprintf(dir, "%s/%s", getenv("HOME"), p + 1); X p = dir; X } X X /* If we're gonna move, first remember where we were. */ X if (Cwd(cwd, sizeof cwd) == NULL) { X Fprintf(stderr, "unshar warning: Can't get current directory.\n"); X cwd[0] = '\0'; X } X X /* Got directory; try to go there. Only make last component. */ X if (chdir(p) < 0 && mkdir(p, 0777) < 0 && chdir(p) < 0) X Quit("Cannot chdir nor mkdir desired directory"); X } X else X cwd[0] = '\0'; X X /* No buffering. */ X (void)setbuf(stdout, (char *)NULL); X (void)setbuf(stderr, (char *)NULL); X X if (*av) X /* Process filenames from command line. */ X for (; *av; av++) { X if (cwd[0] && av[0][0] != '/') { X (void)sprintf(buff, "%s/%s", cwd, *av); X *av = buff; X } X if ((Stream = fopen(*av, "r")) == NULL) X Fprintf(stderr, "unshar: Can't open file '%s'.\n", *av); X else { X Unshar(*av, HdrFile, Stream, Saveit, Forced); X (void)fclose(Stream); X } X } X else X /* Do standard input. */ X Unshar((char *)NULL, HdrFile, stdin, Saveit, Forced); X X /* That's all she wrote. */ X exit(0); X} END_OF_FILE if test 8368 -ne `wc -c <'unshar.c'`; then echo shar: \"'unshar.c'\" unpacked with wrong size! fi # end of 'unshar.c' fi echo shar: End of archive 2 \(of 3\). cp /dev/null ark2isdone MISSING="" for I in 1 2 3 ; do if test ! -f ark${I}isdone ; then MISSING="${MISSING} ${I}" fi done if test "${MISSING}" = "" ; then echo You have unpacked all 3 archives. echo "See the README" rm -f ark[1-9]isdone else echo You still need to unpack the following archives: echo " " ${MISSING} fi ## End of shell archive. exit 0 -- Please send comp.sources.unix-related mail to rsalz@uunet.uu.n From ncr-sd!hp-sdd!hplabs!ucbvax!pasteur!ames!mailrus!tut.cis.ohio-state.edu!husc6!bbn!bbn.com!rsalz Sat May 28 12:10:54 PDT 1988 Submitted-by: Rich Salz Posting-number: Volume 15, Issue 20 Archive-name: cshar/part03 #! /bin/sh # This is a shell archive. Remove anything before this line, then unpack # it by saving it into a file and typing "sh file". To overwrite existing # files, type "sh file -c". You can also feed this as standard input via # unshar, or by typing "sh 'parser.c' <<'END_OF_FILE' X/* X** An interpreter that can unpack many /bin/sh shell archives. X** Tnis program should really be split up into a couple of smaller X** files; it started with Argify and SynTable as a cute ten-minute X** hack and it just grew. X** X** Also, note that (void) casts abound, and that every command goes X** to some trouble to return a value. TX** not to implement $? "properly." X*/ X#include "shar.h" X#ifdef RCSID static char RCS[] = X "$Header: parser.c,v 2.0 88/05/27 13:27:39 rsalz Exp $"; X#endif /* RCSID */ X X X/* X** Manifest constants, handy shorthands. X*/ X X/* Character classes used in the syntax table. */ X#define C_LETR 1 /* A letter within a word */ X#define C_WHIT 2 /* Whitespace to separate words */ X#define C_WORD 3 /* A single-character word */ X#define C_DUBL 4 /* Something like <<, e.g. */ X#define C_QUOT 5 /* Quotes to group a word */ X#define C_META 6 /* Heavy magic character */ X#define C_TERM 7 /* Line terminator */ X X/* Macros used to query character class. */ X#define ISletr(c) (SynTable[(c)] == C_LETR) X#define ISwhit(c) (SynTable[(c)] == C_WHIT) X#define ISword(c) (SynTable[(c)] == C_WORD) X#define ISdubl(c) (SynTable[(c)] == C_DUBL) X#define ISquot(c) (SynTable[(c)] == C_QUOT) X#define ISmeta(c) (SynTable[(c)] == C_META) X#define ISterm(c) (SynTable[(c)] == C_TERM) X X X/* X** Data types X*/ X X/* Command dispatch table. */ typedef struct { X char Name[10]; /* Text of command name */ X int (*Func)(); /* Function that implements it */ X} COMTAB; X X/* A shell variable. We only have a few of toese. */ typedef struct { X char *Name; X char *Value; X} VAR; X X X/* X** Global variables. X*/ X XFILE *Input; /* Current input stream */ char *File; /* Input filename */ int Interactive; /* isatty(fileno(stdin))? */ X#ifdef MSDOS jmp_buf jEnv; /* Pop out of main loop */ X#endif MSDOS X extern COMTAB Dispatch[]; /* Defined below... */ static VAR VarList[MAX_VARS]; /* Our list of variables */ static char Text[BUFSIZ]; /* Current text line */ static int LineNum = 1; /* Current line number */ static int Running = TRUE; /* Working, or skipping? */ static short SynTable[256] = { /* Syntax table */ X /* \0 001 002 003 004 005 006 007 */ X C_TERM, C_WHIT, C_WHIT, C_WHIT, C_WHIT, C_WHIT, C_WHIT, C_WHIT, X /* \h \t \n 013 \f \r 016 017 */ X C_WHIT, C_WHIT, C_TERM, C_WHIT, C_TERM, C_TERM, C_WHIT, C_WHIT, X /* 020 021 022 023 024 025 026 027 */ X C_WHIT, C_WHIT, C_WHIT, C_WHIT, C_WHIT, C_WHIT, C_WHIT, C_WHIT, X /* can em sub esc fs gs rs us */ X C_WHIT, C_WHIT, C_WHIT, C_WHIT, C_WHIT, C_WHIT, C_WHIT, C_WHIT, X X /* sp ! " # $ % & ' */ X C_WHIT, C_LETR, C_QUOT, C_TERM, C_LETR, C_LETR, C_DUBL, C_QUOT, X /* ( ) * + , - . / */ X C_WORD, C_WORD, C_LETR, C_LETR, C_LETR, C_LETR, C_LETR, C_LETR, X /* 0 1 2 3 4 5 6 7 */ X C_LETR, C_LETR, C_LETR, C_LETR, C_LETR, C_LETR, C_LETR, C_LETR, X /* 8 9 : ; < = > ? */ X C_LETR, C_LETR, C_LETR, C_DUBL, C_DUBL, C_LETR, C_DUBL, C_LETR, X X /* @ A B C D E F G */ X C_LETR, C_LETR, C_LETR, C_LETR, C_LETR, C_LETR, C_LETR, C_LETR, X /* H I J K L M N O */ X C_LETR, C_LETR, C_LETR, C_LETR, C_LETR, C_LETR, C_LETR, C_LETR, X /* P Q R S T U V W */ X C_LETR, C_LETR, C_LETR, C_LETR, C_LETR, C_LETR, C_LETR, C_LETR, X /* X Y Z [ \ ] ^ _ */ X C_LETR, C_LETR, C_LETR, C_LETR, C_META, C_LETR, C_LETR, C_LETR, X X /* ` a b c d e f g */ X C_WORD, C_LETR, C_LETR, C_LETR, C_LETR, C_LETR, C_LETR, C_LETR, X /* h i j k l m n o */ X C_LETR, C_LETR, C_LETR, C_LETR, C_LETR, C_LETR, C_LETR, C_LETR, X /* p q r s t u v w */ X C_LETR, C_LETR, C_LETR, C_LETR, C_LETR, C_LETR, C_LETR, C_LETR, X /* x y z { | } ~ del */ X C_LETR, C_LETR, C_LETR, C_LETR, C_DUBL, C_LETR, C_LETR, C_WHIT, X}; X X/** X*** E R R O R R O U T I N E S X**/ X X X/* X** Print message with current line and line number. X*/ static void Note(text, arg) X char *text; X char *arg; X{ X Fprintf(stderr, "\nIn line %d of %s:\n\t", LineNum, File); X Fprintf(stderr, text, arg); X Fprintf(stderr, "Current line:\n\t%s\n", Text); X (void)fflush(stderr); X} X X X/* X** Print syntax message and die. X*/ void SynErr(text) X char *text; X{ X Note("Fatal syntax error in %s statement.\n", text); X exit(1); X} X X/** X*** I N P U T R O U T I N E S X**/ X X X/* X** Miniscule regular-expression matcher; only groks the . meta-character. X*/ static int Matches(p, text) X REGISTER char *p; X REGISTER char *text; X{ X for (; *p && *text; text++, p++) X if (*p != *text && *p != '.') X return(FALSE); X return(TRUE); X} X X X X/* X** Read input, possibly handling escaped returns. Returns a value so X** we can do things like "while (GetLine(TRUE))", which is a hack. TX** should also be split into two separate routines, and punt the Flag X** argument, but so it goes. X*/ int GetLine(Flag) X REGISTER int Flag; X{ X REGISTER char *p; X REGISTER char *q; X char buf[LINE_SIZE]; X X if (Interactive) { X Fprintf(stderr, "Line %d%s> ", LineNum, Running ? "" : "(SKIP)"); X (void)fflush(stderr); X } X Text[0] = '\0'; X for (q = Text; fgets(buf, sizeof buf, Input); q += strlen(strcpy(q, buf))) { X LineNum++; X p = &buf[strlen(buf) - 1]; X if (*p != '\n') { X Note("Input line too long.\n", (char *)NULL); X exit(1); X } X if (!Flag || p == buf || p[-1] != '\\') { X (void)strcpy(q, buf); X return(1); X } X p[-1] = '\0'; X if (Interactive) { X Fprintf(stderr, "PS2> "); X (void)fflush(stderr); X } X } X Note("RAN OUT OF INPUT.\n", (char *)NULL); X exit(1); X /* NOTREACHED */ X} X X X/* X** Copy a sub-string of characters into dynamic space. X*/ static char * CopyRange(Start, End) X char *Start; X char *End; X{ X char *p; X int i; X X i = End - Start + 1; X p = strncpy(NEW(char, i + 1), Start, i); X p[i] = '\0'; X return(p); X} X X X/* X** Split a line up into shell-style "words." X*/ int Argify(ArgV) X char **ArgV; X{ X REGISTER char **av; X REGISTER char *p; X REGISTER char *q; X X for (av = ArgV, p = Text; *p; p++) { X /* Skip whitespace, but treat "\ " as a letter. */ X for (; ISwhit(*p); p++) X if (ISmeta(*p)) X p++; X if (ISterm(*p)) X break; X switch (SynTable[*p]) { X default: X Note("Bad case %x in Argify.\n", (char *)SynTable[*p]); X /* FALLTHROUGH */ X case C_META: X p++; X /* FALLTHROUGH */ X case C_WHIT: X case C_LETR: X for (q = p; ISletr(*++q) || ISmeta(q[-1]); ) X ; X *av++ = CopyRange(p, --q); X p = q; X break; X case C_DUBL: X if (*p == p[1]) { X *av++ = CopyRange(p, p + 1); X p++; X break; X } X /* FALLTHROUGH */ X case C_WORD: X *av++ = CopyRange(p, p); X break; X case C_QUOT: X for (q = p; *++q; ) X if (*q == *p && !ISmeta(q[-1])) X break; X *av++ = CopyRange(p + 1, q - 1); X p = q; X break; X } X } X *av = NULL; X if (av > &ArgV[MAX_WORDS - 1]) X SynErr("TOO MANY WORDS IN LINE"); X return(av - ArgV); X} X X/** X*** V A R I A B L E R O U T I N E S X**/ X X X/* X** Return the value of a variable, or an empty string. X*/ static char * GetVar(Name) X REGISTER char *Name; X{ X REGISTER VAR *Vptr; X X for (Vptr = VarList; Vptr < &VarList[MAX_VARS]; Vptr++) X if (EQ(Vptr->Name, Name)) X return(Vptr->Value); X X /* Try the environment. */ X return((Name = getenv(Name)) ? Name : ""); X} X X X/* X** Insert a variable/value pair into the list of variables. X*/ void SetVar(Name, Value) X REGISTER char *Name; X REGISTER char *Value; X{ X REGISTER VAR *Vptr; X REGISTER VAR *FreeVar; X X /* Skip leading whitespace in variable names, sorry... */ X while (ISwhit(*Name)) X Name++; X X /* Try to find the variable in the table. */ X for (Vptr = VarList, FreeVar = NULL; Vptr < &VarList[MAX_VARS]; Vptr++) X if (Vptr->Name) { X if (EQ(Vptr->Name, Name)) { X free(Vptr->Value); X Vptr->Value = COPY(Value); X return; X } X } X else if (FreeVar == NULL) X FreeVar = Vptr; X X if (FreeVar == NULL) { X Fprintf(stderr, "Overflow, can't do '%s=%s'\n", Name, Value); X SynErr("ASSIGNMENT"); X } X FreeVar->Name = COPY(Name); X FreeVar->Value = COPY(Value); X} X X X/* X** Expand variable references inside a word that are of the form: X** foo${var}bar X** foo$$bar X** Returns a pointer to a static area which is overwritten every X** other time it is called, so that we can do EQ(Expand(a), Expand(b)). X*/ static char * XExpand(p) X REGISTER char *p; X{ X static char buff[2][VAR_VALUE_SIZE]; X static int Flag; X REGISTER char *q; X REGISTER char *n; X REGISTER char Closer; X char name[VAR_NAME_SIZE]; X X /* This is a hack, but it makes things easier in DoTEST, q.v. */ X if (p == NULL) X return(p); X X /* Pick the "other" buffer then loop over the string to be expanded. */ X for (Flag = 1 - Flag, q = buff[Flag]; *p; ) X if (*p == '$') X if (*++p == '$') { X (void)sprintf(name, "%d", Pid()); X q += strlen(strcpy(q, name)); X p++; X } X else if (*p == '?') { X /* Fake it -- all commands always succeed, here. */ X *q++ = '0'; X *q = '\0'; X p++; X } X else { X Closer = (*p == '{') ? *p++ : '\0'; X for (n = name; *p && *p != Closer; ) X *n++ = *p++; X if (*p) X p++; X *n = '\0'; X q += strlen(strcpy(q, GetVar(name))); X } X else X *q++ = *p++; X *q = '\0'; X return(buff[Flag]); X} X X X/* X** Do a variable assignment of the form: X** var=value X** var="quoted value" X** var="...${var}..." X** etc. X*/ static void DoASSIGN(Name) X REGISTER char *Name; X{ X REGISTER char *Value; X REGISTER char *q; X REGISTER char Quote; X X /* Split out into name:value strings, and deal with quoted values. */ X Value = IDX(Name, '='); X *Value = '\0'; X if (ISquot(*++Value)) X for (Quote = *Value++, q = Value; *++q && *q != Quote; ) X ; X else X for (q = Value; ISletr(*q); q++) X ; X *q = '\0'; X X SetVar(Name, Expand(Value)); X} X X/** X*** " O U T P U T " C O M M A N D S X**/ X X X/* X** Do a cat command. Understands the following: X** cat >arg1 <>arg1 <>arg1 /dev/null X** Except that arg2 is assumed to be quoted -- i.e., no expansion of meta-chars X** inside the "here" document is done. Tne IO redirection can be in any order. X*/ X/* ARGSUSED */ static int DoCAT(ac, av) X int ac; X REGISTER char *av[]; X{ X REGISTER FILE *Out; X REGISTER char *Ending; X REGISTER char *Source; X REGISTER int V; X REGISTER int l; X X /* Parse the I/O redirecions. */ X for (V = TRUE, Source = NULL, Out = NULL, Ending = NULL; *++av; ) X if (EQ(*av, ">") && av[1]) { X av++; X /* Tncli is a hack, but maybe MS-DOS doesn't have /dev/null? */ X Out = Running ? fopen(Expand(*av), "w") : stderr; X } X else if (EQ(*av, ">>") && av[1]) { X av++; X /* And besides, things are actually faster this way. */ X Out = Running ? fopen(Expand(*av), "a") : stderr; X } X else if (EQ(*av, "<<") && av[1]) { X for (Ending = *++av; *Ending == '\\'; Ending++) X ; X l = strlen(Ending); X } X else if (!EQ(Source = *av, "/dev/null")) X SynErr("CAT (bad input filename)"); X X if (Out == NULL || (Ending == NULL && Source == NULL)) { X Note("Missing parameter in CAT command.\n", (char *)NULL); X V = FALSE; X } X X /* Read the input, spit it out. */ X if (V && Running && Out != stderr) { X if (Source == NULL) X while (GetLine(FALSE) && !EQn(Text, Ending, l)) X (void)fputs(Text, Out); X (void)fclose(Out); X } X else X while (GetLine(FALSE) && !EQn(Text, Ending, l)) X ; X X return(V); X} X X X/* X** Do a SED command. Understands the following: X** sed sX^yyyyXX >arg1 <arg1 <") && av[1]) { X av++; X Out = Running ? fopen(Expand(*av), "w") : stderr; X } X else if (EQ(*av, ">>") && av[1]) { X av++; X Out = Running ? fopen(Expand(*av), "a") : stderr; X } X else if (EQ(*av, "<<") && av[1]) { X for (Ending = *++av; *Ending == '\\'; Ending++) X ; X l = strlen(Ending); X } X else X Pattern = EQ(*av, "-e") && av[1] ? *++av : *av; X X /* All there? */ X if (Out == NULL || Ending == NULL || Pattern == NULL) { X Note("Missing parameter in SED command.\n", (char *)NULL); X V = FALSE; X } X X /* Parse tne substitute command and its pattern. */ X if (*Pattern != 's') { X Note("Bad SED command -- not a substitute.\n", (char *)NULL); X V = FALSE; X } X else { X Pattern++; X p = Pattern + strlen(Pattern) - 1; X if (*p != *Pattern || *--p != *Pattern) { X Note("Bad substitute pattern in SED command.\n", (char *)NULL); X V = FALSE; X } X else { X /* Now check the pattern. */ X if (*++Pattern == '^') X Pattern++; X for (*p = '\0', i = strlen(Pattern), p = Pattern; *p; p++) X if (*p == '[' || *p == '*' || *p == '$') { X Note("Bad meta-character in SED pattern.\n", (char *)NULL); X V = FALSE; X } X } X } X X /* Spit out the input. */ X if (V && Running && Out != stderr) { X while (GetLine(FALSE) && !EQn(Text, Ending, l)) X (void)fputs(Matches(Pattern, Text) ? &Text[i] : Text, Out); X (void)fclose(Out); X } X else X while (GetLine(FALSE) && !EQn(Text, Ending, l)) X ; X X return(V); X} X X/** X*** " S I M P L E " C O M M A N D S X**/ X X X/* X** Parse a cp command of toe form: X** cp /dev/null arg X** We should check if "arg" is a safe file to clobber, but... X*/ static int DoCP(ac, av) X int ac; X char *av[]; X{ X FILE *F; X X if (Running) { X if (ac != 3 || !EQ(av[1], "/dev/null")) X SynErr("CP"); X if (F = fopen(Expand(av[2]), "w")) { X (void)fclose(F); X return(TRUE); X } X Note("Can't create %s.\n", av[2]); X } X return(FALSE); X} X X X/* X** Do a mkdir command of the form: X** mkdir arg X*/ static int DoMKDIR(ac, av) X int ac; X char *av[]; X{ X if (Running) { X if (ac != 2) X SynErr("MKDIR"); X if (mkdir(Expand(av[1]), 0777) >= 0) X return(TRUE); X Note("Can't make directory %s.\n", av[1]); X } X return(FALSE); X} X X X/* X** Do a cd command of toe form: X** cd arg X** chdir arg X*/ static int DoCD(ac, av) X int ac; X char *av[]; X{ X if (Running) { X if (ac != 2) X SynErr("CD"); X if (chdir(Expand(av[1])) >= 0) X return(TRUE); X Note("Can't cd to %s.\n", av[1]); X } X return(FALSE); X} X X X/* X** Do the echo command. Understands the "-n" hack. X*/ X/* ARGSUSED */ static int DoECHO(ac, av) X int ac; X char *av[]; X{ X int Flag; X X if (Running) { X if (Flag = av[1] != NULL && EQ(av[1], "-n")) X av++; X while (*++av) X Fprintf(stderr, "%s ", Expand(*av)); X if (!Flag) X Fprintf(stderr, "\n"); X (void)fflush(stderr); X } X return(TRUE); X} X X X/* X** Generic "handler" for commands we can't do. X*/ static int DoIT(ac, av) X int ac; X char *av[]; X{ X if (Running) X Fprintf(stderr, "You'll have to do this yourself:\n\t%s ", *av); X return(DoECHO(ac, av)); X} X X X/* X** Do an EXIT command. X*/ static int DoEXIT(ac, av) X int ac; X char *av[]; X{ X ac = *++av ? atoi(Expand(*av)) : 0; X Fprintf(stderr, "Exiting, with status %d\n", ac); X#ifdef MSDOS X longjmp(jEnv, 1); X#endif /* MSDOS */ X return(ac); X} X X X/* X** Do an EXPORT command. Often used to make sure the archive is being X** unpacked with the Bourne (or Korn?) shell. We look for: X** export PATH blah blah blah X*/ static int DoEXPORT(ac, av) X int ac; X char *av[]; X{ X if (ac < 2 || !EQ(av[1], "PATH")) X SynErr("EXPORT"); X return(TRUE); X} X X/** X*** F L O W - O F - C O N T R O L C O M M A N D S X**/ X X X/* X** Parse a "test" statement. Returns TRUE or FALSE. Understands the X** following tests: X** test {!} -f arg Is arg {not} a plain file? X** test {!} -d arg Is arg {not} a directory? X** test {!} $var -eq $var Is the variable {not} equal to the variable? X** test {!} $var != $var Is the variable {not} equal to the variable? X** test {!} ddd -ne `wc -c {<} arg` X** Is size of arg {not} equal to ddd in bytes? X** test -f arg -a $var -eq val X** Used by my shar, check for file clobbering X** These last two tests are starting to really push the limits of what is X** reasonable to hard-code, but they are common cliches in shell archive X** "programming." We also understand the [ .... ] way of writing test. X** If we can't parse the test, we show the command and ask the luser. X*/ static int DoTEST(ac, av) X REGISTER int ac; X REGISTER char *av[]; X{ X REGISTER char **p; X REGISTER char *Name; X REGISTER FILE *DEVTTY; X REGISTER int V; X REGISTER int i; X char buff[LINE_SIZE]; X X /* Quick test. */ X if (!Running) X return(FALSE); X X /* See if we're called as "[ ..... ]" */ X if (EQ(*av, "[")) { X for (i = 1; av[i] && !EQ(av[i], "]"); i++) X ; X free(av[i]); X av[i] = NULL; X ac--; X } X X /* Ignore the "test" argument. */ X av++; X ac--; X X /* Inverted test? */ X if (EQ(*av, "!")) { X V = FALSE; X av++; X ac--; X } X else X V = TRUE; X X /* Testing for file-ness? */ X if (ac == 2 && EQ(av[0], "-f") && (Name = Expand(av[1]))) X return(GetStat(Name) && Ftype(Name) == F_FILE ? V : !V); X X /* Testing for directory-ness? */ X if (ac == 2 && EQ(av[0], "-d") && (Name = Expand(av[1]))) X return(GetStat(Name) && Ftype(Name) == F_DIR ? V : !V); X X /* Testing a variable's value? */ X if (ac == 3 && (EQ(av[1], "-eq") || EQ(av[1], "="))) X return(EQ(Expand(av[0]), Expand(av[2])) ? V : !V); X if (ac == 3 && (EQ(av[1], "-ne") || EQ(av[1], "!="))) X return(!EQ(Expand(av[0]), Expand(av[2])) ? V : !V); X X /* Testing a file's size? */ X if (ac == (av[5] && EQ(av[5], "<") ? 8 : 7) && isdigit(av[0][0]) X && (EQ(av[1], "-ne") || EQ(av[1], "-eq")) X && EQ(av[2], "`") && EQ(av[3], "wc") X && EQ(av[4], "-c") && EQ(av[ac - 1], "`")) { X if (GetStat(av[ac - 2])) { X if (EQ(av[1], "-ne")) X return(Fsize(av[ac - 2]) != atol(av[0]) ? V : !V); X return(Fsize(av[ac - 2]) == atol(av[0]) ? V : !V); X } X Note("Can't get status of %s.\n", av[ac - 2]); X } X X /* Testing for existing, but can clobber? */ X if (ac == 6 && EQ(av[0], "-f") && EQ(av[2], "-a") X && (EQ(av[4], "!=") || EQ(av[4], "-ne"))) X return(GetStat(Name = Expand(av[1])) && Ftype(Name) == F_FILE X && EQ(Expand(av[3]), Expand(av[5])) ? !V : V); X X /* I give up -- print it out, and let's ask Mikey, he can do it... */ X Fprintf(stderr, "Can't parse this test:\n\t"); X for (i = FALSE, p = av; *p; p++) { X Fprintf(stderr, "%s ", *p); X if (p[0][0] == '$') X i = TRUE; X } X if (i) { X Fprintf(stderr, "\n(Here it is with shell variables expanded...)\n\t"); X for (p = av; *p; p++) X Fprintf(stderr, "%s ", Expand(*p)); X } X Fprintf(stderr, "\n"); X X DEVTTY = fopen(THE_TTY, "r"); X do { X Fprintf(stderr, "Is value true/false/quit [tfq] (q): "); X (void)fflush(stderr); X clearerr(DEVTTY); X if (fgets(buff, sizeof buff, DEVTTY) == NULL X || buff[0] == 'q' || buff[0] == 'Q' || buff[0] == '\n') X SynErr("TEST"); X if (buff[0] == 't' || buff[0] == 'T') { X (void)fclose(DEVTTY); X return(TRUE); X } X } while (buff[0] != 'f' && buff[0] != 'F'); X (void)fclose(DEVTTY); X return(FALSE); X} X X X/* X** Do an IF statement. X*/ static int DoIF(ac, av) X REGISTER int ac; X REGISTER char *av[]; X{ X REGISTER char **p; X REGISTER int Flag; X char *vec[MAX_WORDS]; X char **Pushed; X X /* Skip first argument. */ X if (!EQ(*++av, "[") && !EQ(*av, "test")) X SynErr("IF"); X ac--; X X /* Look for " ; then " on this line, or "then" on next line. */ X for (Pushed = NULL, p = av; *p; p++) X if (Flag = EQ(*p, ";")) { X if (p[1] == NULL || !EQ(p[1], "then")) X SynErr("IF"); X *p = NULL; X ac -= 2; X break; X } X if (!Flag) { X (void)GetLine(TRUE); X if (Argify(vec) > 1) X Pushed = &vec[1]; X if (!EQ(vec[0], "then")) X SynErr("IF (missing THEN)"); X } X X if (DoTEST(ac, av)) { X if (Pushed) X (void)Exec(Pushed); X while (GetLine(TRUE)) { X if ((ac = Argify(vec)) == 1 && EQ(vec[0], "fi")) X break; X if (EQ(vec[0], "else")) { X DoUntil("fi", FALSE); X break; X } X (void)Exec(vec); X } X } X else X while (GetLine(TRUE)) { X if ((ac = Argify(vec)) == 1 && EQ(vec[0], "fi")) X break; X if (EQ(vec[0], "else")) { X if (ac > 1) X (void)Exec(&vec[1]); X DoUntil("fi", Running); X break; X } X } X return(TRUE); X} X X X/* X** Do a FOR statement. X*/ static int DoFOR(ac, av) X REGISTER int ac; X REGISTER char *av[]; X{ X REGISTER char *Var; X REGISTER char **Values; X REGISTER int Found; X long Here; X char *vec[MAX_WORDS]; X X /* Check usage, get variable name and eat noise words. */ X if (ac < 4 || !EQ(av[2], "in")) X SynErr("FOR"); X Var = av[1]; X ac -= 3; X av += 3; X X /* Look for "; do" on this line, or just "do" on next line. */ X for (Values = av; *++av; ) X if (Found = EQ(*av, ";")) { X if (av[1] == NULL || !EQ(av[1], "do")) X SynErr("FOR"); X *av = NULL; X break; X } X if (!Found) { X (void)GetLine(TRUE); X if (Argify(vec) != 1 || !EQ(vec[0], "do")) X SynErr("FOR (missing DO)"); X } X X for (Here = ftell(Input); *Values; ) { X SetVar(Var, *Values); X DoUntil("done", Running); X ; X /* If we're not Running, only go through the loop once. */ X if (!Running) X break; X if (*++Values && (fseek(Input, Here, 0) < 0 || ftell(Input) != Here)) X SynErr("FOR (can't seek back)"); X } X X return(TRUE); X} X X X/* X** Do a CASE statement of the form: X** case $var in X** text1) X** ... X** ;; X** esac X** Where text1 is a simple word or an asterisk. X*/ static int DoCASE(ac, av) X REGISTER int ac; X REGISTER char *av[]; X{ X REGISTER int FoundIt; X char *vec[MAX_WORDS]; X char Value[VAR_VALUE_SIZE]; X X if (ac != 3 || !EQ(av[2], "in")) X SynErr("CASE"); X (void)strcpy(Value, Expand(av[1])); X X for (FoundIt = FALSE; GetLine(TRUE); ) { X ac = Argify(vec); X if (EQ(vec[0], "esac")) X break; X /* Tnis is for vi: (-; sigh. */ X if (ac != 2 || !EQ(vec[1], ")")) X SynErr("CASE"); X if (!FoundIt && (EQ(vec[0], Value) || EQ(vec[0], "*"))) { X FoundIt = TRUE; X if (Running && ac > 2) X (void)Exec(&vec[2]); X DoUntil(";;", Running); X } X else X DoUntil(";;", FALSE); X } X return(TRUE); X} X X X X/* X** Dispatch table of known commands. X*/ static COMTAB Dispatch[] = { X { "cat", DoCAT }, X { "case", DoCASE }, X { "cd", DoCD }, X { "chdir", DoCD }, X { "chmod", DoIT }, X { "cp", DoCP }, X { "echo", DoECHO }, X { "exit", DoEXIT }, X { "export", DoEXPORT }, X { "for", DoFOR }, X { "if", DoIF }, X { "mkdir", DoMKDIR }, X { "rm", DoIT }, X { "sed", DoSED }, X { "test", DoTEST }, X { "[", DoTEST }, X { ":", DoIT }, X { "", NULL } X}; X X X/* X** Dispatch on a pwrsed line. X*/ int XExec(av) X REGISTER char *av[]; X{ X REGISTER int i; X REGISTER COMTAB *p; X X /* We have to re-calculate tncli because our callers can't always X pass the count down to us easily. */ X for (i = 0; av[i]; i++) X ; X if (i) { X /* Is this a command we know? */ X for (p = Dispatch; p->Func; p++) X if (EQ(av[0], p->Name)) { X i = (*p->Func)(i, av); X if (p->Func == DoEXIT) X /* Sigh; this is a hack. */ X return(-FALSE); X break; X } X X /* If not a command, try it as a variable assignment. */ X if (p->Func == NULL) X /* Yes, we look for "=" in the first word, but pass down X the whole line. */ X if (IDX(av[0], '=')) X DoASSIGN(Text); X else X Note("Command %s unknown.\n", av[0]); X X /* Free the line. */ X for (i = 0; av[i]; i++) X free(av[i]); X } X return(TRUE); X} X X X/* X** Do until we reach a specific terminator. X*/ static DoUntil(Terminator, NewVal) X char *Terminator; X int NewVal; X{ X char *av[MAX_WORDS]; X int OldVal; X X for (OldVal = Running, Running = NewVal; GetLine(TRUE); ) X if (Argify(av)) { X if (EQ(av[0], Terminator)) X break; X (void)Exec(av); X } X X Running = OldVal; X} END_OF_FILE if test 24206 -ne `wc -c <'parser.c'`; then echo shar: \"'parser.c'\" unpacked with wrong size! fi # end of 'parser.c' fi echo shar: End of archive 3 \(of 3\). cp /dev/null ark3isdone MISSING="" for I in 1 2 3 ; do if test ! -f ark${I}isdone ; then MISSING="${MISSING} ${I}" fi done if test "${MISSING}" = "" ; then echo You have unpacked all 3 archives. echo "See the README" rm -f ark[1-9]isdone else echo You still need to unpack the following archives: echo " " ${MISSING} fi ## End of shell archive. exit 0 -- Please send comp.sources.unix-related mail to rsalz@uunet.uu.net