[comp.sources.amiga] v89i116: unshar - unpack shell archives

page%rishathra@Sun.COM (Bob Page) (05/03/89)

Submitted-by: ecarroll@vax1.tcd.ie (Eddy Carroll)
Posting-number: Volume 89, Issue 116
Archive-name: unix/unshar.1

This is yet another unshar utility.  In brief: It's small, fast,
handles all cat & sed format's I've seen (including multi-char sed
strips), sub-directories, "./" type filenames and more.

[uuencode executable included.  ..bob]

# This is a shell archive.
# Remove anything above and including the cut line.
# Then run the rest of the file through 'sh'.
# Unpacked files will be owned by you and have default permissions.
#----cut here-----cut here-----cut here-----cut here----#
#!/bin/sh
# shar: SHell ARchive
# Run the following text through 'sh' to create:
#	makefile
#	tiny.a
#	unshar.c
#	unshar.doc
#	unshar.n
#	unshar.uu
# This is archive 1 of a 1-part kit.
# This archive created: Wed May  3 00:57:54 1989
echo "extracting makefile"
sed 's/^X//' << \SHAR_EOF > makefile
X#
X# Aztec Make makefile, for Lattice C V4.0 :-)
X#
X# Unshar by Eddy Carroll, April 1989
X#
X
XZOOEXE = unshar unshar.doc
XZOOSRC = unshar.c tiny.a tiny.o unshar.n makefile
XOBJS   = tiny.o unshar.o
X#
X#
X#
X.c.o:
X	lc -s -v $*.c
X.a.o:
X	sys:lattice/c/asm -isys:include/ -u $*.a
X.n.doc:
X	nro >$*.doc -ms:an $*.n
X#
X#
X#
Xall: unshar unshar.doc
X
Xunshar: $(OBJS)
X    blink from $(OBJS) to unshar sc sd nd map ram:map lib lib:lc.lib
X
Xtiny.o:	tiny.a
Xunshar.o: unshar.c
Xunshar.doc: unshar.n
X
Xzoo:	unshar.zoo
Xzoosrc: unsharsrc.zoo
X
Xunshar.zoo: $(ZOOEXE)
X	-delete unshar.zoo
X	zoo a unshar.zoo $(ZOOEXE)
X
Xunsharsrc.zoo: $(ZOOSRC)
X	-delete unsharsrc.zoo
X	zoo a unsharsrc.zoo $(ZOOSRC)
X
Xclean:
X	-delete "#?.bak"
SHAR_EOF
echo "extracting tiny.a"
sed 's/^X//' << \SHAR_EOF > tiny.a
X*:ts=8
X****************************************************************************
X*                                                                          *
X* TINY.A                                  (C) Copyright Eddy Carroll 1989  *
X* ~~~~~~                                  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  *
X*                                                                          *
X* This is a rewrite of the startup code provided with Lattice C V4.0.      *
X* It is a "bare bones" version, which is substantially smaller, and also   *
X* allows programs to be linked without needing to access lc.lib (unless    *
X* of course they are using functions in lc.lib). Only CLI programs are     *
X* supported, and no default i/o channels or other lattice-specific stuff   *
X* are initialised. exit() doesn't automatically clean up any more, so      *
X* don't use things like malloc() and fopen() unless you call free() and    *
X* fclose() yourself. This replacement is designed for programs that go     *
X* directly to AmigaDOS and Exec, rather than through the Unix-like Lattice *
X* functions.                                                               *
X*                                                                          *
X* All in all, this version shaves around 1500 bytes off the size of a C    *
X* program. No guarantees are supplied as regards its suitability for any   *
X* particular use, other than it works for me.                              *
X*                                                                          *
X* Using it is very simple: Simply modify the command line for BLINK from   *
X* BLINK FROM LIB:C.O+.... to BLINK FROM TINY.O+... and leave everything    *
X* else the same.                                                           *
X*                                                                          *
X* Note that when assembling this, make sure to specify the -u switch on    *
X* the Lattice assembler's command line. This adds a '_' to all symbols     *
X* defined. If you are using a different assembler, you need to add a '_'   *
X* manually yourself.                                                       *
X*                                                                          *
X****************************************************************************
X
X        INCLUDE "exec/types.i"
X        INCLUDE "exec/alerts.i"
X        INCLUDE "exec/nodes.i"
X        INCLUDE "exec/lists.i"
X        INCLUDE "exec/ports.i"
X        INCLUDE "exec/libraries.i"
X        INCLUDE "exec/tasks.i"
X        INCLUDE "libraries/dos.i"
X        INCLUDE "libraries/dosextens.i"
X        INCLUDE "workbench/startup.i"
X        INCLUDE "exec/funcdef.i"
X        INCLUDE "exec/exec_lib.i"
X        INCLUDE "libraries/dos_lib.i"
X
XMAXARGS	    EQU 32	; Maximum number of command line arguments from CLI
XAbsExecBase EQU 4	; Welcome to the only fixed point in the universe
X
X* A useful macro to let us call library routines
Xcallsys macro
X        CALLLIB _LVO\1
X        endm
X        
X        xdef    XCEXIT			* exit(code) is standard way to exit C.
X	xdef	exit                    *
X
X        xref    LinkerDB        	* linker defined base value
X        xref    _BSSBAS			* linker defined base of BSS
X        xref    _BSSLEN			* linker defined length of BSS
X
X*       library references
X
X        csect   text,0,0,1,2		* xref's after this are 16-bit reloc
X
X        xref    main			* Name of C program to start with.
X
Xstart:
X        movem.l d1-d6/a0-a6,-(a7)
XREGSIZE EQU     (6+7)*4
X        lea     REGSIZE(a7),A5         * Determine old stack pointer
X        move.l  a0,a2                  * Save command pointer
X        move.l  d0,d2                  * and command length
X        lea     LinkerDB,a4            * Load base register
X
X        move.l  AbsExecBase.W,a6
X        move.l  a6,SysBase(A4)
X        move.l  a7,_StackPtr(A4)       * Save stack ptr
X
X	suba.l	a1,a1
X	callsys	FindTask	       * Find out our task ID
X	move.l	d0,a3
X
X*=======================================================================
X*====== CLI Startup Code ===============================================
X*=======================================================================
X*
X* Entry: D2 = command length
X*        A2 = Command pointer
XfromCLI:
X        move.l  a5,D0           * get top of stack
X        sub.l   4(a5),D0        * compute bottom 
X        add.l   #128,D0         * allow for parms overflow
X        move.l  D0,_base(A4)    * save for stack checking
X*-----------------------------------------------------------------------
X*  Open the DOS library:
X
XopenDOS
X	lea     DOSName(A4),A1
X	moveq.l #0,D0
X	callsys OpenLibrary
X	move.l  D0,DOSBase(A4)
X	bne	getcom
XnoDOS:
X	moveq.l #100,d0
X	bra     exit2
X
X*------ find command name:
Xgetcom:
X	move.l  pr_CLI(a3),a0
X        add.l   a0,a0           * bcpl pointer conversion
X        add.l   a0,a0
X        move.l  cli_CommandName(a0),a1
X        add.l   a1,a1           * bcpl pointer conversion
X        add.l   a1,a1
X
X*------ collect parameters:
X        move.l  d2,d0                   * get command line length
X        moveq.l #0,d1
X        move.b  (a1)+,d1
X        move.l  a1,_ProgramName(A4)
X        add.l   d1,d0                   * add length of command name
X        addq.l  #1,d0                   * allow for space after command 
X
X        clr.w   -(A7)                   * set null terminator for command line
X        addq.l  #1,D0                   * force to even number of bytes
X        andi.w  #$fffe,D0               * (round up)
X        sub.l   D0,A7                   * make room on stack for command line
X        subq.l  #2,D0
X        clr.w   0(A7,D0)
X
X*------ copy command line onto stack
X        move.l  d2,d0                   * get command line length
X        subq.l  #1,d0
X        add.l   d1,d2
X
Xcopy_line:
X        move.b  0(A2,D0.W),0(A7,D2.W)   * copy command line to stack
X        subq.l  #1,d2
X        dbf     d0,copy_line
X        move.b  #' ',0(a7,d2.w)         * add space between command and parms
X        subq.l  #1,d2
X
Xcopy_cmd:
X        move.b  0(a1,d2.w),0(a7,d2.w)	* copy command name to stack
X        dbf     d2,copy_cmd
X	move.l	a7,a1			* Get pointer to new command line
X
X	sub.l	#(MAXARGS*4),a7		* Reserve space for argv[]
X	move.l	a7,a2			* Initialise base into array
X	move.l	a2,a3			* Save base of argv
X	moveq	#0,d2			* Initialise argc
X
X*
X* From here on down, A1 is pointer into command line
X*
Xbuild_argv:
X	bsr.s	getnext			* Read next character from line
X	bcs.s	doquote			* If quote, handle
X	beq.s	build_argv		* If white space, skip over it
X
X	lea	-1(a1),a0		* Get address of this parameter
X	bsr.s	bumpargv		* Store it to argv[] array
Xbuild_2:
X	bsr.s	getnext			* Get next character
X	bne.s	build_2			* If not white space, keep looking
X	clr.b	-1(a1)			* Zero-terminate current argument
X	bra.s	build_argv		* And go back to get next argument
X
Xdoquote:
X	move.l	a1,a0			* Get pointer to this argument
X	bsr.s	bumpargv		* Output it to argv[]
Xquote_2:
X	bsr.s	getnext			* Get next character
X	bcc.s	quote_2			* If not quote, keep looking
X	clr.b	-1(a1)			* Zero-terminate current argument
Xquote_3:
X	bsr.s	getnext			* Get next character
X	bne.s	quote_3			* Skip until space reached
X	beq.s	build_argv		* Go back and read next argument
X
Xbumpargv:
X	move.l	a0,(a2)+		* Output ptr to current argument
X	addq	#1,d2			* Increment argc
X	cmpi	#MAXARGS,d2		* Used up all our arguments yet?
X	bls.s	qrts			* If not, then return
X	moveq	#110,d0			* Else set return code
X	bra.s	exit2			* And exit
X
X*
X* Reads next character from command line. If zero, never returns, but
X* drops into call to main. Else, returns, with C=1 if character is quote,
X* Z=1 if character is white space.
X*
Xgetnext:
X	move.b	(a1)+,d0		* Get character from command line
X	beq.s	get_2			* Exit if end of line
X	cmp.b	#34,d0			* Check if quote
X	beq.s	isquote			* 
X	cmp.b	#32,d0			* Check if space
X	beq.s	isspace			*
X	cmp.b	#9,d0			* Or tab
X	beq.s	isspace			*
X	cmp.b	#10,d0			* Or end of line
Xisspace:
X	andi	#$1E,ccr		* Clear carry flag, retaining Z
Xqrts	rts
X
Xisquote:
X	ori	#1,ccr			* Set carry flag
X	andi	#$FB,ccr		* Clear zero flag
X	rts				* And return
X
Xget_2:
X	move.l	a3,-(a7)		* Push argv onto stack
X	move.l	d2,-(a7)		* Push argc onto stack
X
X        lea     _BSSBAS,a3      	* get base of BSS
X        moveq   #0,d1
X        move.l  #_BSSLEN,d0     	* get length of BSS in longwords
X        bra.s   clr_lp          	* and clear for length given
Xclr_bss move.l  d1,(a3)+
Xclr_lp  dbf     d0,clr_bss
X
Xdomain:
X	jsr	main(PC)		* Call main(argc,argv)
X        moveq.l #0,d0           	* Set successful status
X        bra.s   exit2
X
Xexit:
X_exit:
XXCEXIT:
X        move.l  4(SP),d0		* Extract return code
Xexit2:
X        move.l  d0,-(a7)
X        move.l  AbsExecBase.W,a6
X        move.l  DOSBase(A4),a1
X        callsys CloseLibrary            * Close Dos library
X
X*------ this rts sends us back to DOS:
XexitToDOS:
X        MOVE.L  (A7)+,D0
X        movea.l _StackPtr(a4),SP        * Restore stack ptr
X        movem.l (a7)+,d1-d6/a0-a6
X        rts
X
X*-----------------------------------------------------------------------
X* Global definitions
X*
X        csect   __MERGED,1,,2,2
X*
X        xdef    NULL,SysBase,LoadAddress,DOSBase
X        xdef    _oserr,_OSERR,_ONBREAK
X        xdef    _ProgramName,_StackPtr,_base
X*
XNULL           dc.l    0
X_base          dc.l    0
X_oserr         equ     *
X_OSERR         dc.l    0
X_ONBREAK       dc.l    0
XSysBase        dc.l    0
XLoadAddress    dc.l    0
X_StackPtr      dc.l    0
XDOSBase        dc.l    0
X_ProgramName   dc.l    0
XDOSName        dc.b    'dos.library',0
X
X        END
SHAR_EOF
echo "extracting unshar.c"
sed 's/^X//' << \SHAR_EOF > unshar.c
X/*
X *                     Unshar (C) Copyright Eddy Carroll 1988
X *                     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
X * Usage: Unshar {-o} <filename> ...
X *
X * Extracts files from a SHAR'd archive.
X *
X * This utility has a few advantages over the version of SH on Fish Disk 92.
X * For a start, it doesn't crash if it gets a slightly unusual format! It
X * also has a (limited) capability for extracting files from shar archives
X * which use 'SED' rather than 'CAT' (typically, this is done so that
X * each line in the file may be prefixed with an 'X' or similar, so that
X * indentation is preserved). Unshar will spot 'SED' lines, and treat them
X * the same as 'CAT' (allowing for different parameters of course) with
X * the exception that any leading characters matching the string specified
X * in the SED command are discarded.
X *
X * Unshar checks files being extracted to see if they are to be stored
X * within a sub-directory. If they are, and the sub-directory does not
X * already exist, it is created.
X *
X * One other small addition is that any filenames which are prefixed with
X * the characters "./" have these characters removed. Some shar files
X * use this prefix to ensure that the files are stored in the current
X * directory.
X *
X * Files are extracted into the current directory. As each file is extracted,
X * an appropriate message is printed on the screen. If the file already
X * exists, the user is warned and given the chance to avoid overwriting it
X * "Overwrite file (Yes/No/All)? ". The default is Yes. If All is selected,
X * then this prompt is supressed for the rest of the current file. It may
X * be disabled for all the files by specifying the -o switch on the
X * command line.
X * 
X * DISTRIBUTION
X * I retain copyright rights to this source code, though it may be _freely_
X * distributed. The executable file created from this source code is in
X * the Public Domain and may be distributed without any restrictions.
X *
X */
X
X/* Assumes Lattice V4.0 - should work with Aztec using 32 bit ints */
X
X#include <exec/types.h>
X#include <libraries/dos.h>
X#include <proto/dos.h>
X
X#define YES			1
X#define NO			0
X#define CR			'\015'
X#define EOL			'\012'
X#define SINGLEQUOTE '\''
X#define DOUBLEQUOTE '\042'
X
Xchar HelpMsg[] = "\
XUnshar by Eddy Carroll 1988 Public Domain, extracts files from shar archives.\
X\n\
XUsage: unshar {-overwrite} <filename> ...\n";
X
Xchar DiskMsg[]  = "Unshar aborted - Disk write error (disk full?)\n";
Xchar ErrorMsg[] = "Unshar: Invalid CAT or SED command at line ";
X
Xint linenum;
Xint CtrlC = NO;
X
X
X/* 
X * Note - if you are using any 'C'-type memory allocators, or any C library
X * functions which might conceivably allocate memory for you, delete the
X * following line. If you are sticking to pure AmigaDOS calls, leave it
X * in, and save yourself 1K.
X *
X */
X
Xvoid MemCleanup(){}
X
X/*
X * --------------------------------------------------------------------------
X * The following block may be removed `as-is' and used in other programs.
X * It provides basic buffered i/o on two files, an input file and an output
X * file. It also provides output to the current standard output via
X * print. Buffering is done using buffers of size MAXBUF.
X *
X * The following routines are provided:
X *
X * getc() returns an integer corresponding to the next character read from
X * infile, or EOF if the end of file has been reached.
X *
X * putc(c) outputs a character to outfile. putc(EOF) flushes the current
X * output buffer to outfile, and should be called just before closing the
X * file.
X *
X * getline() returns a pointer to a string containing the next line
X * read in from infile. getline() also checks for CTRL-C via chkabort()
X * 
X * putline(s) outputs a string to outfile, returning non-zero if an
X * error occurred during the write.
X *
X * flushin() resets getc() and getline() for input from a new file
X *
X * input() returns a pointer to a string containing a line from stdin.
X *
X * print(s) prints a message on standard output.
X *
X * print3(s1,s2,s3) outputs three strings on standard output.
X *
X * numtostr(n) returns a pointer to the ascii representation of n.
X *
X * Special Notes
X * ~~~~~~~~~~~~~
X * You should ensure that you use the filenames 'infile' and 'outfile'
X * when you are opening the input and output files in main(). Also,
X * do not #define EOF or MAXBUF elsewhere in your program.
X *
X */
X
X#define EOF		-1
X#define MAXBUF	4096
X
XLONG infile, outfile, Open(), Input(), Output(), Read();
XLONG maxin = MAXBUF, maxout = MAXBUF, inbuf = MAXBUF, outbuf = 0;
Xunsigned char inbuffer[MAXBUF], outbuffer[MAXBUF];
Xint diskerror = NO;
X
X/*
X * int getc()
X * Returns next character from infile, or EOF if end of file.
X *
X */
X
Xint getc()
X{
X	if (!maxin)
X		return (EOF);
X
X	if (inbuf >= maxin) {
X		maxin = Read(infile, inbuffer, MAXBUF);
X		inbuf = 0;
X		if (!maxin)
X			return (EOF);
X	}
X	return (inbuffer[inbuf++]);
X}
X
X/* 
X * Prepares getc() for input from a new file
X *
X */
X
X#define flushin() maxin = MAXBUF, inbuf = MAXBUF;
X
X/*
X * putc(c)
X * Outputs character C to disk. If C is an EOF, then the output is flushed.
X * If error detected on Write, then all future writes are ignored until
X * an EOF is output, and diskerror is set to YES.
X *
X */
X
Xvoid putc(c)
Xint c;
X{
X	if (c == EOF)
X		maxout = outbuf;
X	else
X		outbuffer[outbuf++] = c;
X
X	if (outbuf >= maxout) {
X		if (!diskerror && Write(outfile, outbuffer, maxout) == -1) {
X			if (c == EOF)
X				diskerror = NO;
X			else
X				diskerror = YES;
X		}
X		outbuf = 0;
X		maxout = MAXBUF;
X	}
X}
X
X/*
X * print(s)
X * Outputs a message to std output
X *
X */
X
Xvoid print(s)
Xchar *s;
X{
X	Write(Output(),s,strlen(s));
X}
X
X/*
X * Outputs three strings to std output.
X * Useful for sequences like print3("string", variable, "string");
X *
X */
X
Xvoid print3(s1,s2,s3)
Xchar *s1,*s2,*s3;
X{
X	print(s1);
X	print(s2);
X	print(s3);
X}
X
X/*
X * Reads in a line from current infile into string, and returns a pointer
X * to that string. Returns NULL if EOF encountered.
X *
X */
X
Xchar *getline()
X{
X	register int ch, i = 0;
X	static char line[90];
X
X	ch = getc();
X	if (ch == EOF)
X		return (NULL);
X
X	while (i < 80 && ch != EOF && ch != EOL) {
X		line[i++] = ch;
X		ch = getc();
X	}
X
X	line[i] = '\0';
X	linenum++;
X	chkabort();
X	return (line);
X}
X
X
X/*
X * Outputs a string to the current output file (terminating it with LF).
X * Returns 0 for success, non-zero for disk error
X *
X */
X
Xint putline(s)
Xchar *s;
X{
X	while (*s)
X		putc(*s++);
X	putc(EOL);
X	return (diskerror);
X}
X
X
X/*
X * Reads a line from keyboard and returns pointer to it
X *
X */
X
Xchar *input()
X{
X	static char s[80];
X
X	s[Read(Input(),s,75)] = '\0';
X	return(s);
X}
X
X/*
X * Converts integer to string and returns pointer to it.
X *
X */
Xchar *numtostr(n)
Xint n;
X{
X	static char s[20];
X	int i = 19;
X
X	s[19] = '\0';
X	if (n)
X		while (n)
X			s[--i] = '0' + (n % 10), n /= 10;
X	else
X		s[--i] = '0';
X	return(&s[i]);
X}
X
X/*
X * -----------------------* End of Buffered IO routines *--------------------
X */
X
X
X/*
X * index(s,c,skip)
X *
X * Like standard Unix index(), but skips over quotes if skip == true.
X * Also skips over chars prefixed by a \.
X * Returns pointer to first occurance of char c inside string s, or NULL.
X *
X */
X
Xchar *index(s,c,skip)
Xchar *s,c;
Xint skip;
X{
X	register char *p = s;
X	register int noquotes = YES, literal = NO;
X
X	while (*p) {
X		if (literal) {
X			literal = NO;
X			p++;
X		} else {
X			if (skip && ((*p == SINGLEQUOTE) || (*p == DOUBLEQUOTE)))
X				noquotes = !noquotes;
X			if (noquotes && (*p == c))
X				return(p);
X			literal = (*p == '\\');
X			p++;
X		}
X	}
X	return (NULL);
X}
X
X
X/*
X * getname(s1,s2,mode)
X *
X * Extracts a string from start of string s1 and stores it in s2. Leading
X * spaces are discarded, and quotes, if present, are used to indicate the
X * start and end of the filename. If mode is MODE_FILE, then if the name
X * starts with either './' or '/', this prefix is stripped. This doesn't
X * happen if the mode is MODE_TEXT. A pointer to the first character after
X * the string in s1 is returned. In addition, any characters prefixed with
X * are passed through without checking.
X *
X */
X
X#define MODE_FILE 1
X#define MODE_TEXT 2
X
Xchar *getname(s1,s2,mode)
Xchar *s1,*s2;
X{
X	char endchar = ' ';
X
X	while (*s1 == ' ')
X		s1++;
X
X	if (*s1 == SINGLEQUOTE || *s1 == DOUBLEQUOTE)
X		endchar = *s1++;
X
X	if (mode == MODE_FILE) {
X		if (s1[0] == '.' && s1[1] == '/')
X			s1 += 2;
X		while (*s1 == '/')
X			s1++;
X	}
X
X	while (*s1 && *s1 != endchar) {
X		if (*s1 == '\\' && *(s1+1))
X			s1++;
X		*s2++ = *s1++;
X	}
X	*s2 = '\0';
X
X	if (*s1 == endchar)
X		return(++s1);
X	else
X		return(s1);
X}
X
X
X/*
X * checkfordir(filename)
X *
X * Checks filename to see if it is inside a subdirectory. If it is, then
X * checks if subdirectory exists, and creates it if it doesn't.
X *
X */
X
Xvoid checkfordir(filename)
Xchar *filename;
X{
X	char dir[80], *p;
X	int i, x;
X	long dirlock, Lock(), CreateDir();
X
X	p = filename;
X
X	while (p = index(p, '/', 1)) {
X
X		/* Dir exists, so copy dir part of filename into dir name area */
X
X		x = p - filename;
X		for (i = 0; i < x; i++)
X			dir[i] = filename[i];
X		dir[i] = '\0';
X
X		/* Now, see if directory exists, if not then create */
X		if ((dirlock = Lock(dir,ACCESS_READ)) == NULL) {
X			dirlock = CreateDir(dir);
X			if (dirlock) {
X				print3("Creating directory ", dir, "\n");
X			}
X		}
X		if (dirlock)
X			UnLock(dirlock);
X
X		p++;
X	}
X}
X
X
X/*
X * Extracts all stored files from a shar file. Returns 0 for success,
X * non-zero if disk error occurred. The echofilename parameter, if non-zero,
X * causes the name of each shar file to be output before unsharing.
X * If overwrite is non-zero, then existing files are overwritten without
X * any warning.
X *
X */
X
Xint unshar(sharfile, echofilename, overwrite)
Xchar *sharfile;
Xint echofilename, overwrite;
X{
X	register char *s, *p;
X	char endmarker[100], filename[100],sedstring[100];
X	int endlen, stripfirst, startfile, found = NO, err = NO, skip, sedlen;
X	long filelock, Lock();
X
X	if ((infile = Open(sharfile, MODE_OLDFILE)) == NULL) {
X		print3("Can't open file ", sharfile, " for input\n");
X		return(0);
X	}
X
X	linenum = 0;
X	if (echofilename)
X		print3("\033[7m Shar file: ", sharfile, " \033[0m\n");
X
X	while (!err && !CtrlC && (s = getline()) != NULL) {
X		startfile = NO;
X		if (strncmp(s,"cat ",4) == 0) {
X			startfile  = YES;
X			stripfirst = NO;
X		}
X		if (strncmp(s,"sed ",4) == 0) {
X			startfile  = YES;
X			stripfirst = YES;
X			sedlen = 0;
X			if ((p = index(s,'^',0)) != NULL) {
X				while (*(++p) && *p != '/')
X					sedstring[sedlen++] = *p;
X			} 
X		}
X
X		if (startfile) {	
X			if (found == NO) {
X				found = YES;
X			}
X			if ((p = index(s,'>',1)) == NULL) {
X				print3(ErrorMsg, numtostr(linenum), "\n");
X			} else {
X				getname(++p,filename,MODE_FILE);
X				p = s;
X				while ((p = index(p,'<',1)) && (p[1] != '<'))
X					;
X				if (p)
X					getname(p+2,endmarker,MODE_TEXT);
X
X				endlen = strlen(endmarker);
X
X				if (strlen(filename) && endlen) {
X
X					checkfordir(filename);
X
X					/* Found a valid line so perform extract */
X
X					/* Check if file exists */
X
X					skip = NO;
X					if (!overwrite) {
X						filelock = Lock(filename, ACCESS_READ);
X						if (filelock) {
X							UnLock(filelock);
X							print3("Overwrite file ", filename,
X								" (Yes, [No], All)? ");
X
X							switch (tolower(*input())) {
X
X							case 'a':
X								overwrite = YES;
X								break;
X							case 'y':
X								skip = NO;
X								break;
X							default:
X								skip = YES;
X								break;
X							}
X						}
X					}
X
X					if ((outfile = Open(filename,MODE_NEWFILE)) == NULL)
X						print3("Couldn't open file ",filename," for output\n");
X					else {
X						if (!skip)
X							print3("Unsharing file ", filename, "\n");
X						s = getline();
X						err = NO;
X						while (s && strncmp(s,endmarker,endlen) && !CtrlC) {
X							if (stripfirst && !strncmp(sedstring,s,sedlen))
X								s += sedlen;
X							if (!skip && (err = putline(s)))
X								break;
X							s = getline();
X						}
X						putc(EOF);
X						if (err)
X							print(DiskMsg);
X						Close(outfile);
X					}
X				} else
X					print(ErrorMsg, numtostr(linenum), "\n");
X			}
X		}
X	}
X	if (!err && !CtrlC)
X		if (found)
X			print("Unshar done\n");
X		else
X			print("No files to unshar\n");
X	Close(infile);
X	flushin();
X	return(err);
X}
X
X/*
X * New handler for Ctrl-C. Simply sets global variable to true and exits.
X *
X */
X
Xint brk()
X{
X	CtrlC = YES;
X	return (0);
X}
X
X/*
X * Start of mainline
X *
X */
X
Xint main(argc,argv)
Xint argc;
Xchar *argv[];
X{
X
X	int i, ok, overwrite = NO;
X
X	if ((argc == 1) || (*argv[1] == '?')) {
X		print(HelpMsg);
X		return (10);
X	}
X	if (onbreak(brk))
X		exit(10);
X
X	for (i = 1, ok = YES; ok && i < argc && !CtrlC; i++) {
X		if (*argv[i] == '-' && (argv[i][1] == 'o' || argv[i][1] == 'O'))
X			overwrite = YES;
X		else
X			ok = !unshar(argv[i], argc > 2, overwrite);
X	}
X
X	if (CtrlC)
X		print("^C\n");
X}
SHAR_EOF
echo "extracting unshar.doc"
sed 's/^X//' << \SHAR_EOF > unshar.doc
X
X     UNSHAR(1)           AMIGA Programmer's Manual            UNSHAR(1)
X
X
X
X     NAME 
X          unshar - extracts files from shar archives 
X
X     SYNOPSIS 
X          unshar {-overwrite} file1 file2 ...  
X
X     DESCRIPTION 
X          Unshar is a utility which extracts files from the ubiquitous
X          Unix shar  archives.    "But  I  have umpteen zillion unshar
X          utilities already", I hear you shout.  Well, maybe  so,  but
X          this one has the following advantages: 
X
X               - Small and fast 
X               - Handles many cat and sed formats 
X               - Allows extraction of subdirectories 
X               - Understands ./file type filenames 
X               - Exits cleanly with CTRL-C 
X
X          The total code size is only slightly over 4K, for those with
X          packed C  directories.  It treats quotes and imbedded escape
X          sequences intelligently and handles  all  the  cat  and  sed
X          formats  I've  ever seen, including sed commands which strip
X          off more than one letter.  There are probably a few  obscure
X          shar's  out  there  it  won't  handle,  but I've yet to find
X          them.  
X
X          Invoke unshar with as many filenames as you like.    The  -o
X          flag  controls  how  unshar  handles the extraction of files
X          which already exist.  It can appear anywhere on the  command
X          line,  and  only  affects those files which appear after it.
X          Normally, if extracting a file would overwrite  an  existing
X          file,  unshar  will  ask  you  how  it  should  handle this.
X          Entering 'Y' will overwrite the existing file, 'N' will skip
X          past this file without extracting it, and 'A' will overwrite
X          this file, and  also  any  other  existing  files  from  the
X          current shar file without prompting you again.  If you don't
X          like prompts, specifying -o on the command line makes unshar
X          overwrite all existing files.  
X
X     OPERATION 
X          Unshar  scans  through each archive specified on the command
X          line, looking for lines beginning with 'cat' or  'sed'.  All
X          other lines  are  ignored.   In particular, 'echo' lines are
X          not echoed.  This way, you don't  get  a  load  of  messages
X          which  are  in  any  case  fairly  meaningless,  because the
X          operations they  are  describing  are  unsupported  by  most
X          unshars.  
X
X          When  a  shar  archive  contains  a  file  for  which a full
X          pathname is given (as in source/file.c for  example)  unshar
X          will create  whatever  directories  are  necessary.  It also
X          strips  off  leading  /'s  and  ./'s,  to   make   filenames
X          understandable by AmigaDOS.  
X
X     AUTHOR 
X          Eddy Carroll 
X          Internet: ecarrollcs.tcd.ie 
X
X
X     Copyright Eddy Carroll 1989      -1-
SHAR_EOF
echo "extracting unshar.n"
sed 's/^X//' << \SHAR_EOF > unshar.n
X.TH BACKDROP 1 "AMIGA Programmer's Manual" "Copyright Eddy Carroll 1989"
X.SH NAME
Xunshar - extracts files from shar archives
X.SH SYNOPSIS
Xunshar {-overwrite} file1 file2 ...
X.SH DESCRIPTION
XUnshar is a utility which extracts files from the ubiquitous Unix shar
Xarchives. "But I have umpteen zillion unshar utilities already",
XI hear you shout. Well, maybe so, but this one has the following advantages:
X
X.in +5
X- Small and fast
X.br
X- Handles many cat and sed formats
X.br
X- Allows extraction of subdirectories
X.br
X- Understands ./file type filenames
X.br
X- Exits cleanly with CTRL-C
X.in -5
X
XThe total code size is only slightly over 4K, for those with packed C
Xdirectories. It treats quotes and imbedded escape sequences intelligently
Xand handles all the cat and sed formats I've ever seen, including sed
Xcommands which strip off more than one letter. There are probably a few
Xobscure shar's out there it won't handle, but I've yet to find them.
X
XInvoke unshar with as many filenames as you like. The -o flag controls
Xhow unshar handles the extraction of files which already exist. It can
Xappear anywhere on the command line, and only affects those files which
Xappear after it. Normally, if extracting a file would overwrite an
Xexisting file, unshar will ask you how it should handle this. Entering
X'Y' will overwrite the existing file, 'N' will skip past this file without
Xextracting it, and 'A' will overwrite this file, and also any other existing
Xfiles from the current shar file without prompting you again. If you don't
Xlike prompts, specifying -o on the command line makes unshar overwrite all
Xexisting files.
X.SH OPERATION
XUnshar scans through each archive specified on the command line, looking
Xfor lines beginning with 'cat' or 'sed'. All other lines are ignored. In
Xparticular, 'echo' lines are not echoed. This way, you don't get a load
Xof messages which are in any case fairly meaningless, because the operations
Xthey are describing are unsupported by most unshars.
X
XWhen a shar archive contains a file for which a full pathname is given
X(as in source/file.c for example) unshar will create whatever directories
Xare necessary. It also strips off leading /'s and ./'s, to make filenames
Xunderstandable by AmigaDOS.
X.SH AUTHOR
XEddy Carroll
X.br
XInternet: ecarroll@cs.tcd.ie
SHAR_EOF
echo "extracting unshar.uu"
sed 's/^X//' << \SHAR_EOF > unshar.uu
X
Xbegin 644 unshar
XM```#\P`````````"``````````$```-2```)*````^D```-22.=^_DOO`#0D]
XM2"0`2?D`````+'@`!"E.`!`I3P`8D\E.KO[:)D`@#9"M``0&@````(`I0``$D
XM0^P`)'``3J[]V"E``!QF```(<&1@``#H(&L`K-'(T<@B:``0T\G3R2`"<@`2$
XM&2E)`"#0@5*`0F=2@`)`__Z?P%6`0G<(`"`"4X#4@1^R```@`%."4<C_]A^\V
XM`"`@`%."'[$@`"``4<K_^")/G_P```"`)$\F2G0`831E$F?Z0>G__V$<82AF;
XM_$(I__]@ZB!)80YA&F3\0BG__V$29OQGV"3(4D(,0@`@8R)P;F!0$!EG)@P`B
XM`")G%@P``"!G"@P```EG!`P```H"/``>3G4`/``!`CP`^TYU+PLO`D?Y```"7
XME'(`(#P```B#8`(FP5'(__Q.N@AB<`!@!"`O``0O`"QX``0B;``<3J[^8B`?_
XM+FP`&$S??WY.=0``3G5(YS`"2JP!"F8$</]@1B(L`1*RK`$*;2PB+`*80>P"[
XMH"0()CP``!``+&P`'$ZN_]8I0`$*<``I0`$22JP!"F8$</]@$$'L`J#1[`$2,
XM4JP!$G``$!!,WT`,3G5.50``2.<P`B(M``@,@?____]F"B`L`18I0`$.8!!![
XM[!*@(DC3[`$64JP!%A*!(BP!#B0L`1:T@6U$2JP!&F8R(BP"G$'L$J`D""8LB
XM`0XL;``<3J[_T%*`9A@,K?____\`"&8(<``I0`$:8`9P`2E``1I"K`$6*7P`"
XM`!```0Y,WT`,3EU.=4Y5__A(YS`"+&P`'$ZN_\0O0``,+RT`"$ZZ"9Q83R]`4
XM`!`B+P`,)"T`""8O`!`L;``<3J[_T$S?0`Q.74YU3E4``"\M``AAN%A/+RT`R
XM#&&P6$\O+0`08:A83TY=3G5.5?_X2.<#`'P`80#^MBX`#(?_____9@1P`&!"D
XM#(8```!0;"0,A_____]G'`R'````"F<40>PBH-'&4H8@!Q"`80#^@"X`8-1!S
XM["*@T<9"$%*L`I1.N@F"0>PBH"`(3-\`P$Y=3G5.50``(&T`"$H09Q00$$B`C
XM2,!2K0`(+P!A`/Z<6$]@Y$AX``IA`/Z06$\@+`$:3EU.=4Y5__Q(YS`"+&P`P
XM'$ZN_\HO0``,(B\`#$'L(OHD"'9++&P`'$ZN_]9!["+Z(DC3P$(1(`A,WT`,L
XM3EU.=4Y5__AP$RM`__Q"+"-=2JT`"&<\2JT`"&=(4ZW__$'L(TK1[?_\("T`*
XM"'(*+T@``$ZZ!XX&@0```#`@;P``$($@+0`(<@I.N@=X*T``"&#$4ZW__$'L&
XM(THB2-/M__P2O``P0>PC2M'M__P@"$Y=3G5.5?_T2.<C("1M``A^`7P`2A)G8
XM3DJ&9P9\`%**8/)*K0`09QH2$@P!`"=G!@P!`")F#$J'5\!$`$B`2,`N`$J'>
XM9PX2$A0M``^T`68$(`I@%`P2`%Q7P$0`2(!(P"P`4HI@KG``3-\$Q$Y=3G5.`
XM5?_^2.<@`!M\`"#__R!M``@,$``@9@92K0`(8/`@;0`($A`,`0`G9P8,`0`BQ
XM9@A2K0`(&T'__PRM`````0`09B8@;0`(#!``+F8,#"@`+P`!9@14K0`((&T`:
XM"`P0`"]F!E*M``A@\"!M``A*$&<N$A`4+?__M`%G)`P!`%QF"DHH``%G!%*MQ
XM``@@;0`((FT`#!*04JT`"%*M``Q@RB!M``Q"$"!M``@2$+(M__]F#%*M``@@N
XM;0`((`A@!"`(3G%,WP`$3EU.=4Y5_Z!(YR`"*VT`"/^L2'@``4AX`"\O+?^L:
XM80#^L$_O``PK0/^L2H!G``"0D*T`"$*M_Z@K0/^D(BW_J+*M_Z1L$"!M``@;]
XML!@`&+!2K?^H8.8@+?^H0C4(L$'M_[`B"'3^+&P`'$ZN_ZPK0/^@2JW_H&8LE
XM0>W_L"((+&P`'$ZN_X@K0/^@2JW_H&<42&P!,DAM_[!(;`$>80#\SD_O``Q*V
XMK?^@9PPB+?^@+&P`'$ZN_Z92K?^L8`#_5DS?0`1.74YU3E7^K$CG(")P`"M`7
XM_K@K0/Z\(BT`""0\```#[2QL`!Q.KO_B*4`"F$JL`IAF&DAL`48O+0`(2&P!Z
XM-&$`_&I/[P`,<`!@``-J0JP"E$JM``QG%$AL`60O+0`(2&P!4F$`_$9/[P`,U
XM2JW^N&8``OY*K`$&9@`"]F$`_$XD0+3\``!G``+H0JW^P$AX``1(;`%L+PI.7
XMN@1.3^\`#$J`9@IP`2M`_L!"K?[$2'@`!$AL`7(O"DZZ!"Y/[P`,2H!F3'`!P
XM<@`O`4AX`%XO"BM`_L0K0/[`*T'^L&$`_3)/[P`,*T#_^$J`9R12K?_X(&W_\
XM^$H09Q@2$`P!`"]G$$/M_LS3[?ZP4JW^L!*!8-Q*K?[`9P#_5DJM_KQF!G`!)
XM*T#^O$AX``%(>``^+PIA`/S@3^\`#"M`__A*@&8@+RP"E&$`_%Q83TAL`7@O#
XM`$AL`-EA`/M83^\`#&``_Q!2K?_X(&W_^$AX``%(;?\P+PAA`/T*3^\`#"M*W
XM__A(>``!2'@`/"\M__AA`/R&3^\`#"M`__A*@&<*($`,*``\``%FVDJM__AGW
XM&"!M__A4B$AX``)(;?^4+PAA`/S"3^\`#$AM_Y1.N@1>6$](;?\P*T#^R$ZZ8
XM!%!83TJ`9P`!=$JM_LAG``%L2&W_,&$`_5A83T*M_K1*K0`09G)![?\P(@ATN
XM_BQL`!Q.KO^L*T#^K$JM_JQG6"(M_JPL;``<3J[_IDAL`8I(;?\P2&P!>F$`/
XM^GY/[P`,80#[+B!`$A!(@4C!+P%.N@,$6$\,@````'EG$`R`````868.<`$K_
XM0``08`Q"K?ZT8`9P`2M`_K1![?\P(@@D/````^XL;``<3J[_XBE``IQ*K`*<5
XM9AA(;`&R2&W_,$AL`9YA`/H43^\`#&``_<Q*K?ZT9A1(;`'02&W_,$AL`<!A@
XM`/GV3^\`#&$`^@XD0$*M_KBT_```9UHO+?[(2&W_E"\*3KH"$$_O``Q*@&=$8
XM2JP!!F8^2JW^Q&<:+RW^L"\*2&W^S$ZZ`>Y/[P`,2H!F!-7M_K!*K?ZT9A`O0
XM"F$`^B!83RM`_KA*@&8(80#YK"1`8*!(>/__80#XO%A/2JW^N&<*2&P`J6$`%
XM^3)83R(L`IPL;``<3J[_W&``_1XO+`*480#Z2EA/2&P!TB\`2&P`V6$`^0A/U
XM[P`,8`#\_DJM_KAF(DJL`09F'$JM_KQG#$AL`=1A`/CF6$]@"DAL`>)A`/C:0
XM6$\B+`*8+&P`'$ZN_]P@/```$``I0`$**4`!$B`M_KA,WT0$3EU.=7`!*4`!U
XM!G``3G5.5?_T2.<X`$*M__0,K0````$`"&<0(FT`#"!I``00$`P``#]F$$AL.
XM`#!A`/AZ6$]P"F```+!(>O^^3KH"B%A/2H!G"DAX``I.NO=:6$]P`2M`__@KO
XM0/_\2JW_^&=V(BT`""0M__RT@6QJ2JP!!F9D(`+E@")M``P@<0@`%A`,`P`M8
XM9AA2B!`0#```;V<&#```3V8(<`$K0/_T8#(@`N6`#($````"7L%$`4B!2,$O%
XM+?_T+P$O,0@`80#[A$_O``Q*@%?!1`%(@4C!*T'_^%*M__Q@A$JL`09G"DAL^
XM`?9A`/?&6$],WP`<3EU.=4CG,#(L>0```I`@;P`8(F\`'"1O`"`F;P`D("\`(
XM*"(O`"PD+P`P)B\`-$ZN_J1,WTP,3G5.5?_\2JT`$&<P(&T`"$H09R@B;0`,J
XM2A%G('``$!!2K0`(<@`2$5*M``R0@2M`__Q*@&8F4ZT`$&#*2JT`$&<8(&T`S
XM"$H09P1P`6`.(&T`#$H09P1P_V`"<`!.74YU```@+P`$#```06T*#```6FX$H
XM!@``($YU``!*@&H``!Y$@$J!:@``#$2!80``($2!3G5A```81(!$@4YU2H%J(
XM```,1(%A```&1(!.=2\"2$$T`68``")(0$A!2$(T`&<```:$P3`"2$`T`(3!J
XM,`)(0C(")!].=2\#=A`,00"`9```!N&944,,00@`9```!NF964,,02``9```A
XM!N6954-*06L```;CF5-#-`#FJ$A"0D+FJDA#@,$V`#`"-`-(0<3!D()D```(!
XM4T/0@63^<@`R`TA#Y[A(0,-`)A\D'TYU(&\`!$H89OQ3B)'O``0@"$YU``!.?
XM5?_X2.<!('X`1>PC8+ZL`?QL'DJ29Q0(*@`"``-G`F`*+RH`!$ZZ`/Q83U*'!
XM4(I@W"\M``PO+0`(3KKT^%!/3-\$@$Y=3G5P`6`.(&T`#$H09P0@+P`$9@8@N
XM/```#,0CP`````QP`$YU``!.5?_\<``B/```,``L>``$3J[^S@*````P`"M`7
XM__Q*@&8$<`!@)$JL``QG&B!L``Q.D$J`9@1P`&`00J=(>``43KK_4E!/("W_Q
XM_$Y=3G5AL$YU``````````!P84JL`I!F$D/L`GQP`"QX``1.KOW8*4`"D"ELU
XM`"`"/$AX`#Q(>`#Z<``O`"\`2&P":$AL`DY(;`(P+P!.NOUX3^\`(%.`9P1PB
XM_V`"<`!.=0``3G4`````````````3E4``$JL``QG!$ZZ_SPB+0`(+&P`'$ZNQ
XM_]QP`$Y=3G4`(%.`9P1P80```^P````!````````#%@````$`````0``#%X`K
XM``I^```!%`````X````````#\@```^H```"E````````````````````````R
XM````````````````````````9&]S+FQI8G)A<GD`56YS:&%R(&)Y($5D9'D@;
XM0V%R<F]L;"`Q.3@X(%!U8FQI8R!$;VUA:6XL(&5X=')A8W1S(&9I;&5S(&9RE
XM;VT@<VAA<B!A<F-H:79E<RX*57-A9V4Z('5N<VAA<B![+6]V97)W<FET97T@#
XM/&9I;&5N86UE/B`N+BX*`%5N<VAA<B!A8F]R=&5D("T@1&ES:R!W<FET92!EI
XM<G)O<B`H9&ES:R!F=6QL/RD*`%5N<VAA<CH@26YV86QI9"!#050@;W(@4T5$$
XM(&-O;6UA;F0@870@;&EN92```````````!`````0````$````````````$-R!
XM96%T:6YG(&1I<F5C=&]R>2``"@!#86XG="!O<&5N(&9I;&4@```@9F]R(&EN4
XM<'5T"@`;6S=M(%-H87(@9FEL93H@```@&ULP;0H``&-A="```'-E9"````H``
XM3W9E<G=R:71E(&9I;&4@`"`H665S+"!;3F]=+"!!;&PI/R``0V]U;&1N)W0@N
XM;W!E;B!F:6QE(``@9F]R(&]U='!U=`H``%5N<VAA<FEN9R!F:6QE(``*``H`W
XM56YS:&%R(&1O;F4*``!.;R!F:6QE<R!T;R!U;G-H87(*`%Y#"@```````"@JL
XM*B!5<V5R($%B;W)T(%)E<75E<W1E9"`J*@``__\````.``X````````"````C
XM``#__P````0`!``````````````"'$-/3E1)3E5%``#__P````0`!```````/
XM``)$`````$%"3U)4`/__````!``$`````````F(`````:6YT=6ET:6]N+FQI.
XM8G)A<GD````````````#[`````0````!```"=````EH```)````"*```````2
X#``/RU
X``
Xend
Xsize 4188
SHAR_EOF
echo "End of archive 1 (of 1)"
# if you want to concatenate archives, remove anything after this line
exit