[comp.sources.misc] v07i002: ABE bullet-proof ascii encoder, part 2 of 2

allbery@uunet.UU.NET (Brandon S. Allbery - comp.sources.misc) (06/04/89)

Posting-number: Volume 7, Issue 2
Submitted-by: brad@looking.on.ca (Brad Templeton)
Archive-name: abe/part02

echo Extracting dabe.1
sed 's/^X//' > dabe.1 << 'E-O-F'
X.TH DABE 1
X.SH NAME
Xdabe - Decode Ascii-Binary Encodings
X.SH SYNOPSIS
X.B dabe
X[ options ] [filename ...]
X.SH DESCRIPTION
XThe
X.I dabe
Xprogram program reads ABE Ascii-Binary encodings produced by the
Xabe(1) program.  ABE files consist only of printable characters and are
Xdesigned for the safe transmission of binary data over communications
Xchannels which may alter non-printable characters.  ABE files are meant
Xto be fairly bullet-proof.
X.PP
XThe
X.I
Xdabe
Xcommand can either read the standard input, or be given a list of files
Xcontaining ABE data.  If files are provided, they are concatenated together.
X(ABE encodings of large files are often split up into several files so that
Xno file is too large for the communications channel.  Simply pass all the
Xfiles to the decoder.)
X.PP
X.I
XDabe
Xcan handle files with randomly inserted lines and extraneous headers and
Xcomments.  There is no need to edit a mail message with an ABE file before
Xpassing it to
X.I dabe.
X.PP
XSome ABE file sets are encoded in such a way that redundant information
Xis included with each part of a split-up encoding.  In this case, you can
Xpass such sets of files to
X.I dabe
Xin any order, and duplicates may also exist.
X.PP
XIf redundant information is not included, you can always put an ABE encoding
Xin order with the sort(1) program.  Use "sort -u" if your set of files
Xmight contain duplicates of parts of the file.  If all lines of an encoding
Xare present and intact, then sort can always make the encoding right, no
Xmatter how much mangling has been done to it.
X.PP
XSay that a binary encoding has arrived on USENET, and that the
Xencoding was done with redundant information in each part.  Say that
Xthe parts arrived at your site in a random order, with other articles
Xmixed in, and that some of the parts were reposted because they were lost
Xin some sections of the net.   If those articles ran from article 340
Xto article 347, you could simply say:
X.ce
Xdabe newsgroupdir/34[0-7]
Xand your file would be decoded.  ("newsgroupdir" represents the name of
Xthe directory where the newsgroup is stored.)
X.PP
XThe only thing
X.I dabe
Xare certain missing header lines and the presence of a different, independently
Xmade ABE encoding in the middle of the group of files passed to the decoder.
XAn error message is always given if there is something wrong with the file.
X.PP
XIt is even possible for
X.I dabe
Xto decode an encoding that has missing or damaged blocks.  Those sections
Xof the resulting file will be blank or in error, but other sections will
Xbe correct and in place.  With some types of files -- namely certain
Xarchives, this is still useful so long as the first section is not one of
Xthose missing or damaged.
X.PP
X.I Dabe
Xwrites to files as named in the encoding.  If the encoding was done on
Xthe same operating system as you are using, the files will appear with
Xtheir valid names, and often with permissions and modification times
Xproperly set.  If the file comes from another operating system, it will
Xbe stored in the current directory according to a "universal name"
Xdefined at the time of encoding.
X.PP
XIt is possible for several files to be combined in the same encoding.
XThis allows the ABE system to be used as a primitive archiver.  It is
X.I not
Xpossible for blocks from different files to be presented to the decoder
Xin any order.  Multiple file encodings must either be given in order, or
Xsorted.
X.PP
XSome ABE files come with the source code to a tiny decoder.  Do not
Xuse this if you have the full
X.I dabe
Xdecoder.
X.SH OPTIONS
X(Note that while option names are displayed here in full, only the first
Xletter is actually required.  For +/\- options, using + turns the option on,
Xand using \- turns the option off.)
X.TP
X.B +stdout
XNormally output is placed in files named in the encoding.  You can have
X.I dabe
Xdo all output to the standard output with this option.  If there are
Xtwo or more files in the ABE file you are decoding, they will be
Xconcatenated together on the standard output, which is not very useful.
X.TP
X.B +ignore_errors
XCertain errors, such as missing lines, missing blocks or duplicated blocks,
Xnormally terminate a decoding.  It is possible to have the decoding continue
Xto produce a partial file.  Use this option to have the decoder continue after
Xwhat it calls a "serious error."
X.TP
X.B -verbose
X.I
XDabe
Xis usually quite talkative about what's going on as it decodes a file.  You
Xcan turn this off by turning off verbose mode with this option.
X.PP
X.SH ENCODING FORMAT
XWith ABE1, the 256 bytes are broken up into 3 sets, with 86, 86 and 84 bytes,
Xrespectively.  The most common 86 bytes in the file go into set 0, and so
Xon.  86 of the printable ASCII characters are used to encode the members
Xof each set.  Special printable escape characters switch from set to set.
XIn ABE2, we have 4 sets of 64 bytes each.
X.PP
XIn an ABE encoding, printable characters always map to themselves, if possible.
XThis means that printable character strings found in binary files are still
Xreadable in an ABE encoding.  You can often look at a raw ABE file and see
Xwhat it is, which is quite useful.  In addition, the byte 0 maps to the
XASCII digit "0," and several other similar useful mappings are made.
X.PP
XABE files also have header information that defines information about the
Xencoded files, block headings, sizes and checksums.  For full details on
Xthe encoding format, see the special file on that in the ABE kit.
X.PP
XThe
X.I dabe
Xdecoder is actually very general, and it has facilities to handle arbitrary
XABE-style encodings as well as ABE encased uuencode(1) encodings.  It
Xunderstands many header lines which are not yet used by the encoder, but
Xwhich may be implemented in future.
X.SH AUTHOR
XThe ABE system was written by Brad Templeton, who is brad@looking.UUCP.
X(Mail regarding abe should go to abe@looking.UUCP.)
XThe tiny ABE decoder is released to the public domain.  All other files
Xare Copyright 1989 by Brad Templeton.  A licence for unlimited non-commercial
Xuse of these encoders is granted.  See the source code in the ABE kit
Xfor full details on the licence.
X
XNo fee is requested or required for the use of these programs.
XIf you feel the need
Xto show appreciation, You might order copies of the REC.HUMOR.FUNNY
XComputer Network Humour Annual(s) (a USENET jokebook) for 9.95 USD+S/H.
XMail to jokebook@looking.UUCP or call 519/884-7473.  There is no requirement to
Xbuy the jokebook in order to use these programs. 
X.SH "SEE ALSO"
X.IR dabe (1),
X.IR sort (1),
X.IR uudecode (1),
XABE File Format
X.SH VERSION
XVersion 1.0
E-O-F
echo Extracting dabe.c
sed 's/^X//' > dabe.c << 'E-O-F'
X
X
X/* ABE 'Ascii-Binary Encoding' Decoder 
X   This is the full decoder which detects all errors in input files,
X   and can handle groups of input blocks in any order, without
X   sorting.  This program was written with Unix(TM-Bell Labs) in mind, but it
X   can be easily ported to other systems, notably MS-DOS using the
X   Microsoft C compiler conventions for text/binary files. 
X
X   This program was written by Brad Templeton, and is a companion to
X   the ABE encoder and the simple ABE decoder.  While the simple
X   ABE decoder was released to the public domain, this decoder is not.
X
X   This decoder is quite general.  It includes the ability to decode
X   ABE files of just about any type, including 3-set ascii ones, 4-set
X   EBCDIC ones and general ABE style files of up to MAX_SETS sets.  It
X   can also handle UUENCODE format placed inside an ABE file.
X
X   It is Copyright 1989 by Brad Templeton.  I hereby grant a licence
X   for unlimited use of binaries generated by compiling this source
X   code.  The source code may be distributed and modified for non-commercial
X   purposes, but all copyright messages must be retained.  Changes which
X   alter the file format of ABE files are not permitted without
X   the explicit consent of Brad Templeton.  I mean it.
X
X   No fee is required for the use of the program.
X   See the man page for a potential way to contribute.
X
X   Changes which move this program to other machines and operating
X   systems are encouraged.  Please send any port to a new machine to
X   me at abe@looking.UUCP.
X
X */
X
X#include <stdio.h>
X#include "abe.h"
X
X
X
X
X
X	/* the big array that maps all the printables in each of the
X	   three character sets to actual bytes */
X
Xint whatbyte[MAX_SETS][MAX_PRINTS];
X
Xchar linbuf[MAX_LLEN];		/* input line */
Xchar lbcopy[MAX_LLEN];		/* copy of input line for error msgs */
X
Xint current_block;		/* block we are decoding */
Xbool in_file;			/* are we in a ABE file */
Xbool in_block;			/* are we in a ABE block */
Xbool system_noted;		/* did we note the OS? */
Xbool has_blocking;		/* does this file have blocks at all */
Xint got_coding;			/* which lines of the encoding have we got */
Xbool got_checksum;		/* did we get the file's checksum */
Xbool same_os;			/* file from same OS as ours? */
Xbool got_main_head;		/* got the main header */
Xbool got_main_end;		/* got the EOF header */
X
Xint total_blocks;		/* number of blocks to expect */
Xlong this_line;			/* line number for this line */
Xlong expected_line;		/* what line number we expect next */
Xlong block_checksum;		/* running checksum for the block */
Xlong total_checksum;		/* running checksum for the file */
Xlong block_bytecount;		/* running count of bytes in block */
Xlong total_bytecount;		/* running count of bytes in file */
Xlong expected_checksum;		/* total checksum file should have */
Xlong expected_bytecount;	/* size file should have */
Xlong save_total_bytecount;	/* in case a block gets ignored */
Xlong save_total_checksum;
Xbool skipping_block = FALSE;	/* are we skipping this block */
X
Xtcrc crc;			/* total file crc */
Xtcrc block_crc;			/* block crc */
Xtcrc crctab [TABSIZE];		/* big mother CRC-32 lookup table */
X
Xlong file_date;			/* modification date for file */
Xint file_perm;			/* file permission bits.  Only the low 3
X				   are significant across OSs */
X
Xchar *cur_filename;		/* current universal filename */
Xchar *real_filename;		/* real name of the file */
Xchar *cur_sourcefile;		/* name of current source file */
Xbool block_gotmap[MAX_BLOCKS];	/* have we seen this block */
X
XFILE *out_desc;			/* descriptor for current output file */
Xlong remember_seek;		/* address to seek to after opening file */
X
Xint error_stat = 0;		/* exit status */
Xbool had_files = FALSE;		/* did we process files */
X
X/* options */
Xbool only_stdout = FALSE;	/* output to stdout, ignore files */
Xbool error_ignore = FALSE;	/* ignore semi-fatal errors and continue */
Xbool is_verbose = TRUE;		/* give verbose output */
Xbool no_numbers = FALSE;	/* don't require line numbers */
Xint print_map[TOTAL_PRINTS];	/* map printable chars to actions */
X				/* -1 means undefined.  0 to 128 means that
X				   the printable maps to a character of the
X				   given index in one of the sets.  Numbers
X				   above 256 are shifting chars */
X
Xstruct fseen_list *seen_list = 0;	/* list of file names seen */
Xint current_style = UNDEF;		/* decoding style */
Xint sets_per_char;			/* how many sets are packed into
X					   an index byte in the code map */
X
X/* The map of ABE2 characters and line number characters */
X
Xchar safe_prints[] =
X"./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
X
X
Xint num_sets = A1NSETS;		/* number of sets of characters */
Xint num_prints = A1NPRINTS;	/* number of characters per set */
X
X/* characters that define the headers (normally never changed) */
X
Xint main_head_char = MAIN_HEAD;
Xint code_head_char = CODE_HEAD;
Xint sub_head_char = SUB_HEAD;
X
Xmain(argc,argv)
Xint argc;
Xchar **argv;
X{
X	int argnum;
X	char *strchr();
X
X
X
X	/* scan the option arguments */
X	for( argnum = 1; argnum < argc; argnum++ ) {
X		char *argline, *argstr;
X		int isplus;		/* boolean tells +arg vs -arg */
X		argline = argv[argnum];
X
X		if (argstr = strchr(argline, '=')) {
X			int len;
X			argstr++;
X			switch( argline[0] ) {
X				case 'h':	/* header chars */
X					/* undocumented option allows an
X					   encoding to use different header
X					   characters.  For future encoders
X					   and unforseen problems */
X					len = strlen( argstr );
X					if( len > 2 )
X						code_head_char = argstr[2];
X					if( len > 1 )
X						sub_head_char = argstr[1];
X					if( len > 0 )
X						main_head_char = argstr[0];
X					break;
X				default:
X					do_options();
X					fatal( "Bad Option %s", argline );
X				}
X			}
X		else if( (isplus = argline[0] == '+') || argline[0] == '-' ) {
X			switch( argline[1] ) {
X				case 's': /* output to stdout */
X					only_stdout = isplus;
X					break;
X				case 'i': 
X					error_ignore = isplus;
X					break;
X				case 'v':
X					is_verbose = isplus;
X					break;
X				default:
X					do_options();
X					fatal( "Bad Option %s", argline );
X				}
X			}
X		}
X
X	mkcrctab();		/* init crc-32 table */
X
X	/* Don't even think of removing or changing the copyright */
X	verbose( 1, "ABE decoder v%d Copyright 1989 by Brad Templeton",
X			OUR_VERSION );
X
X	/* now scan the non-tagged arguments */
X	for( argnum = 1; argnum < argc; argnum++ ) {
X		FILE *indesc;
X		char *argline;
X
X		argline = argv[argnum];
X		if( strchr( "+-", argline[0] ) == NULL && strchr(argline,'=')
X								== NULL ) {
X
X			indesc = fopen( argline, "r" );
X			if( !indesc )
X				fatal( "Could not open file '%s'", argline );
X			cur_sourcefile = argline;
X			givemsg( dofile(indesc), argline );
X			had_files = TRUE;
X			fclose( indesc );
X			}
X		}
X	cur_sourcefile = "(STDIN)";
X	if( !had_files )
X		givemsg( dofile( stdin ), "STDIN" );
X	if( in_file )
X		end_current_file();
X
X	return error_stat;
X}
X
Xdofile(desc)
XFILE *desc;
X{
X	bool try_uu;			/* flag to try a uudecode */
X	bool got_abe;			/* got some ABE data */
X	char *ldata;			/* data on the line */
X	char command;
X	int i, c, len;
X	extern bool seen_file( );
X
X	try_uu = got_abe = FALSE;
X	lbcopy[0] = 0;
X
X	while( fgets( linbuf, MAX_LLEN, desc ) ){
X		int dummyo;		/* dummies for scanf */
X		char dummystr[8];
X		int sum;		/* checksum for this line */
X
X		len = strlen( linbuf );
X		if( !in_file && !try_uu && sscanf(linbuf, "begin %o %7s",
X				&dummyo, dummystr ) == 2 )
X			try_uu = TRUE;
X
X		if( no_numbers ) {
X			ldata = linbuf;
X			}
X		 else {
X			/* ignore any line not starting with T or later, or
X		   	with fewer than 5 characters */
X			if( len < 5 || linbuf[0] < safe_prints[SFBYTE] )
X				continue;
X			ldata = linbuf+4;
X			len -= 4;
X			}
X		/* extract the command character, 0 if none */
X
X		command = ldata[0] == ldata[1] ? ldata[0] : 0;
X		/* eliminate the newline from the official line length,
X		   and if it isn't there, the line's too long, so ignore it. */
X		if( ldata[len-1] == '\n' )
X			ldata[--len] = '\0';
X		 else
X			continue;	/* line too long */
X		/* deal with files from ms-dos */
X		if( ldata[len-1] == '\r' )
X			ldata[--len] = 0;
X		strcpy( lbcopy, linbuf );
X		/* checksum this line */
X		sum = 0;
X		for( i = 0; i < len; i++ )
X			sum += ldata[i];
X
X		if( !no_numbers ) {
X			/* see if the checksum matches */
X			if( (sum % NUM_SAFE) != lnconv(linbuf[3]) )
X					continue;
X			/* get a real line number from first 3 characters */
X		
X			this_line = ( (long)(lnconv(linbuf[0])-SFBYTE)*NUM_SAFE+
X					lnconv(linbuf[1]))*NUM_SAFE +
X					lnconv(linbuf[2]);
X
X			if( in_file && in_block && !skipping_block &&
X						this_line != expected_line ) {
X				semifatal("Out of sequence.  Should have got line '%s'",
X						lineconv( expected_line ) );
X				expected_line = this_line + 1;
X				}
X			else
X				expected_line++;
X			}
X
X		if( command == main_head_char ) {
X			int file_version;
X			int early_ver;		/* earlist decoder */
X			char style[MAX_STYLE+1];
X			/* check start or end of file */
X			if( ldata[2] == 'S' ) {
X				sscanf(ldata+3, "%*d,%u,%u,%8s",
X					&file_version,
X					&early_ver, style);
X
X				if( !in_file )
X					begin_file(file_version,early_ver);
X				init_style( style );
X
X				got_main_head = TRUE;
X				}
X			 else {
X				got_main_end = TRUE;
X				expected_checksum = atol(ldata+3);
X				got_checksum = TRUE;
X				if( in_block && has_blocking )
X					fatal( "End of ABE while still inside block" );
X				consider_closing();
X				}
X			}
X		else if( command == sub_head_char ) /* general sub-header */
X			sub_header( ldata + 2 );
X		else if( command == code_head_char ) /* mapping entry */
X			code_head( ldata + 2, len-2 );
X		else if( command && (print_map[command] >> 8) != MCHAR ) {
X			/* unknown header entry */
X			nonfatal( "Unknown header type %c", command );
X			}
X		else if( in_file && in_block && !skipping_block ) {
X			/* a line of data */
X			if( got_coding != FULL_MAP ) {
X				if( !no_numbers )
X					verbose( -1,"You may wish to sort the input files" );
X				fatal( "Unable to decode line without full character map." );
X				}
X			/* add to the checksum for this line */
X			total_checksum = (total_checksum + sum)
X						% CSUM_MOD;
X			got_abe = TRUE;	/* we got something */
X			if( out_desc == (FILE *)0 )
X				init_output();
X			/* decode the line */
X			decode( ldata, len );
X			}
X		/* these checksums are added after, as the encoder calculates
X		   a checksum before sending out a line, and it thus does
X		   not include the checksum of the checksum line itself */
X		block_checksum = (block_checksum + sum) % CSUM_MOD;
X		}
X
X	consider_closing();
X	
X
X	/* indicate how this file went */
X	if( got_abe )
X		return GOOD_FILE;
X	else
X		return try_uu ? UUDECODE : NO_DATA;
X
X}
X
X/* Decode a line of data */
X
Xdecode( line, len )
Xchar *line;		/* pointer to start of data */
Xint len;		/* length of data region */
X{
X	register int c;		/* lower byte of action word */
X	int i;
X	int cset;		/* current decoding set - (always 0 currently)*/
X	int lastc;
X	register int wmap;
X
X	if( current_style == UUENCODE ) {
X		uudecode( line, len );
X		return;
X		}
X
X	cset = 0;
X	wmap = 0;
X
X	for( i = 0; i < len; i++ ) {
X		c = print_map[line[i]] & 0xff;
X		switch(print_map[line[i]] >> 8 ){
X		    case SHIFTX:
X			if( (wmap = print_map[line[++i]]) >= 0 )
X				outbyte(lastc = whatbyte[1+c][wmap]);
X			break;
X		    case SHIFTXX:
X			outbyte(whatbyte[1 + c/(num_sets-1)]
X						[print_map[line[++i]]]);
X			outbyte(whatbyte[1 + c%(num_sets-1)]
X						[print_map[line[++i]]]);
X			break;
X		    case SHIFTXcX:
X			outbyte(whatbyte[1+c/(num_sets-1)]
X						[print_map[line[++i]]]);
X			outbyte(whatbyte[cset][print_map[line[++i]]]);
X			outbyte(whatbyte[1+c%(num_sets-1)]
X						[print_map[line[++i]]]);
X			break;
X		    case MCHAR:	/* plain old data character */
X			outbyte(lastc = whatbyte[cset][c]);
X			break;
X		    case CHANGE_SET:		/* currently unused */
X			/* change current set for rest of line */
X			/* some more efficient future decoder may decide to
X			   use this some day */
X			cset = c;
X			break;
X		    case RUNLENGTH: {	/* not currently used */
X			int mapc;
X			int rlen;
X
X			rlen = 1+print_map[line[i++]];
X			for( mapc = 0; mapc < rlen; mapc++ )
X				outbyte( lastc );
X			}
X		    default:	/* probably a -1 */
X			semifatal( "Unknown character %c\n", print_map[i] );
X			}
X	    }
X	/* do the newline in TEXT mode if not escaped */
X	if( current_style == TEXT && wmap != -1 )
X		outbyte( '\n' );
X}
X	/* process a sub header item */
X
Xsub_header( shcom )
Xchar *shcom;
X{
X	char *arg, *strchr();
X	char *thecom;
X
X	thecom = shcom;
X	if( arg = strchr( shcom, '=' ) ) {
X		*arg++ = 0;		/* point at the argument */
X		}
X
X	if( comeq( thecom, "os" ) ) {
X		same_os = arg && comeq( arg, OUROS );
X		if( !system_noted ) {
X			verbose(2,"File was encoded on a %s system (%slike ours)",
X					arg, same_os ? "" : "un" );
X			system_noted = TRUE;
X			}
X		}
X	else if( comeq( thecom, "blocking" ) ) {
X		has_blocking = comeq( arg, "true" );
X		in_block = !has_blocking;
X		expected_line = this_line+1;
X		}
X	else if( comeq( thecom, "fname" ) ) {
X		init_outfile( NULL, arg );
X		}
X	else if( comeq( thecom, "linenumbers" ) ) {
X		no_numbers = comeq( arg, "false" );
X		}
X	else if( comeq( thecom, "startblock" ) ) {
X		startblock( arg );
X		}
X	else if( comeq( thecom, "closeblock" ) ) {
X		closeblock( arg );
X		}
X	else if( comeq( thecom, "uname" ) ) {
X		init_outfile( arg, NULL );
X		}
X	else if( comeq( thecom, "owner" ) ) {
X		/* do nothing, for now */;
X		}
X	else if( comeq( thecom, "filecrc32" ) ) {
X		if( !has_blocking && ~crc != (tcrc)atol(arg) )
X			semifatal( "Bad CRC on file %s", cur_filename );
X		}
X	else if( comeq( thecom, "filecount" ) ) {
X		/* do nothing, for now */;
X		}
X	else if( comeq( thecom, "xshifts" ) ) {
X		set_shifts( SHIFTX, arg );
X		}
X	else if( comeq( thecom, "xxshifts" ) ) {
X		set_shifts( SHIFTXX, arg );
X		}
X	else if( comeq( thecom, "xcxshifts" ) ) {
X		set_shifts( SHIFTXcX, arg );
X		}
X	else if( comeq( thecom, "runlength" ) ) {
X		set_shifts( RUNLENGTH, arg );
X		}
X	else if( comeq( thecom, "changeset" ) ) {
X		set_shifts( CHANGE_SET, arg );
X		}
X	else if( comeq( thecom, "style" ) ) {
X		init_style( arg );
X		}
X	else if( comeq( thecom, "prints1" ) ) {
X		set_prints( 0, arg );
X		}
X	else if( comeq( thecom, "prints48" ) ) {
X		set_prints( 48, arg );
X		}
X	else if( comeq( thecom, "numsets" ) ) {
X		num_sets = atoi(arg);
X		if( num_sets > MAX_SETS )
X			fatal( "Too many character sets" );
X		}
X	else if( comeq( thecom, "setgroup" ) ) {
X		/* we should insist that this divide 32 */
X		sets_per_char = atoi(arg);
X		if( 32 % sets_per_char != 0 )
X			fatal( "Set Grouping must divide into 32" );
X		}
X	else if( comeq( thecom, "total-blocks" ) ) {
X		total_blocks = atoi( arg );
X		}
X	else if( comeq( thecom, "end_file" ) ) {
X		if( !cur_filename || strcmp( arg, cur_filename ) != 0 )
X			semifatal( "Termination of wrong file" );
X		}
X	else if( comeq( thecom, "date" ) ) {
X		file_date = atol(arg);
X		}
X	else if( comeq( thecom, "perm" ) ) {
X		file_perm = (int)atol(arg);
X		}
X	else if( comeq( thecom, "size" ) ) {
X		expected_bytecount = atol(arg);
X		}
X	else
X		nonfatal( "Unknown header command %s", thecom );
X}
X
X	/* begin a new file */
X
Xbegin_file(vnum,early_ver)
Xint vnum;		/* version number of file */
Xint early_ver;		/* earliest decoder that can read this file */
X{
X	int i;
X
X	if( OUR_VERSION < early_ver )
X		fatal( "File version %d needs a more recent ABE decoder",
X					vnum );
X
X	if( in_file ) {	
X		nonfatal( "New file begins while still in file '%s' -- terminating that file", cur_filename ? cur_filename : "**Unknown**" );
X		end_current_file();
X		}
X	in_file = TRUE;
X	in_block = FALSE;
X	got_coding = 0;
X	/* clear out printable character action map */
X	for( i = 0; i < TOTAL_PRINTS; i++ )
X		print_map[i] = -1;
X	current_style = UNDEF;
X	got_checksum = FALSE;
X	system_noted = FALSE;
X	no_numbers = FALSE;
X	got_main_head = got_main_end = TRUE;
X	remember_seek = 0;
X	file_date = 0;
X	file_perm = -1;
X	cur_filename = NULL;
X	/* this store may throw away a few bytes of memory, but it
X	   isn't important */
X	real_filename = NULL;
X
X	total_checksum = 0;
X	total_bytecount = 0;
X	block_crc = crc = 0xffffffffL;
X
X
X	/* mark no blocks as having been seen */
X	for( i = 0; i < MAX_BLOCKS; i++ )
X		block_gotmap[i] = FALSE;
X}
X
X	/* terminate processing of the current file and close it */
X
Xend_current_file()
X{
X	int i;
X	int success;
X
X	success = TRUE;
X	if( !cur_filename )
X		fatal( "No name ever found for file in this encoding" );
X
X	if( !got_checksum ) {
X		warning( "No checksum found for '%s'", cur_filename );
X		success = FALSE;
X		}
X
X	if( has_blocking )
X		if( !total_blocks )
X			fatal( "No count of total blocks found for '%s'",
X							cur_filename );
X		 else {
X			for( i = 0; i < total_blocks; i++ ) {
X				if( !block_gotmap[i] ) {
X					semifatal( "Missing block %d from '%s'",
X							i, cur_filename );
X					success = FALSE;
X					}
X				}
X			}
X	if( expected_bytecount != total_bytecount ) {
X		semifatal( "Expected %ld bytes, got %ld bytes on file '%s'",
X			expected_bytecount, total_bytecount, cur_filename );
X		success = FALSE;
X		}
X	else if( expected_checksum != (total_checksum % CSUM_MOD) ) {
X		semifatal( "Whole file checksum error on '%s' we had %ld", cur_filename, total_checksum );
X		success = FALSE;
X		}
X#ifndef msdos
X	close_output();	/* under dos, file must be open to change date */
X#endif
X	if( !only_stdout ) {
X		if( file_date != 0 )
X			setfiledate( file_date );
X		if( file_perm != -1 )
X			setfileperm( file_perm );
X		}
X#ifdef msdos
X	close_output();
X#endif
X	if( success )
X		verbose( 0, "File '%s' decoded successfully, %ld bytes",
X				cur_filename, total_bytecount );
X	 else
X		warning( "Error decoding file '%s'", cur_filename );
X	cur_filename = NULL;
X	in_file = FALSE;
X	no_numbers = FALSE;
X
X}
X
X
X	/* set of OS dependent routines to set file dates and permissions */
X#ifdef unix
X
X#include <sys/types.h>
X
Xsetfiledate(arg)
Xlong arg;
X{
X	struct utimbuf {
X		time_t actime;
X		time_t modtime;
X		} otime;
X
X	otime.actime = otime.modtime = arg;
X	if( utime( real_filename, &otime ) )
X		warning( "Could not set date/time on '%s'", real_filename );
X}
X
X#include <sys/stat.h>
Xsetfileperm(arg)
Xint arg;
X{
X	struct stat sbuf;
X	int failure;
X
X
X	if( same_os )
X		failure = chmod( real_filename, arg );
X	 else 
X		/* only the lower 3 bits are portable */
X		failure = stat( real_filename, &sbuf ) ||
X			chmod( real_filename, (arg & 7) | (sbuf.st_mode & ~7) );
X	if( failure )
X		warning( "Could not set permissions on '%s'", real_filename );
X}
X
X#else
X
X# include <sys/types.h>
X
X# ifdef msdos
X	/* using unix compat library */
Xsetfiledate(arg)
Xlong arg;
X{
X	struct utimbuf {
X		time_t actime;
X		time_t modtime;
X		} otime;
X
X	otime.actime = otime.modtime = arg;
X	if( utime( real_filename, &otime ) )
X		warning( "Could not set date/time on '%s'", real_filename );
X}
X	/* setting file permissions in DOS is not a great idea, anyway.
X	   If is currently commented out, but people can add it if
X	   desired */
X
Xsetfileperm(arg)
Xint arg;
X{
X	int failure;
X
X
X	/*if( chmod( real_filename, same_os ? arg : (arg & 7) << 6 ) )
X		warning( "Could not set permissions on '%s'", real_filename );*/
X}
X
X
X# else
X
Xsetfiledate(arg)
Xlong arg;
X{
X}
X
Xsetfileperm(arg)
Xint arg;
X{
X}
X
X# endif /* else */
X
X#endif
X
X	/* initialize the output file */
X
Xinit_outfile( uname, fullname )
Xchar *uname;		/* name to init, or null */
Xchar *fullname;		/* full name of the file */
X{
X	if( fullname ) {
X		if( !real_filename ) {
X			if( same_os ) { 
X				if( out_desc != (FILE *) 0 ) /* dead code ?*/
X					verbose( 2, "File's true name '%s' ignored due to block re-ordering", fullname );
X				real_filename = allocstring(fullname);
X				}
X		 	else
X				verbose( 2, "Real name '%s' ignored due to operating system differences.", fullname );
X			}
X		}
X	 else {
X		if( !cur_filename ) {
X			struct fseen_list *ourseen;
X			cur_filename = real_filename = allocstring(uname);
X			/* add this to the chain of filenames seen */
X			ourseen = (struct fseen_list *)malloc(sizeof(
X							struct fseen_list));
X			if( !ourseen )
X				fatal( "Out of memory" );
X			ourseen->next_fs = seen_list;
X			ourseen->name_seen = cur_filename;
X			/* put on the front of the chain */
X			seen_list = ourseen;
X			}
X		}
X}
X	
X	/* open the output descriptor.  Return stdout if requested */
X
XFILE *
Xopenout( fname )
Xchar *fname;
X{
X	FILE *mydesc;
X
X	if( only_stdout ) {
X#ifdef msdos
X		setmode( fileno(stdout), O_BINARY );
X#endif
X		return stdout;
X		}
X
X	mydesc = fopen( fname, WRITEMODE );
X
X	if( !mydesc )
X		fatal( "Unable to open file '%s' for writing", fname );
X	return mydesc;
X}
X
Xoutbyte( byte )
Xint byte;
X{
X	putc( byte, out_desc );
X	total_bytecount++;
X	block_bytecount++;
X	crc = UPDC32( byte, crc );
X	block_crc = UPDC32( byte, block_crc );
X}
X
Xclose_output()
X{
X	fclose( out_desc );
X	out_desc = (FILE *)0;
X}
X
X
Xseekto( adr )
Xlong adr;
X{
X	if( out_desc ) {
X		/* don't try to seek on stdout unless necessary */
X		if( !only_stdout || adr != total_bytecount )
X			if( fseek( out_desc, adr, 0 ) )
X				semifatal( "Seek to address %ld failed", adr );
X		}
X	 else
X		remember_seek = adr;
X		
X}
X
Xstatic char lstr[] = " - line: ";
X
X
X/* error message routines */
X
X/* DANGER, NON-PORTABLE FUNCTION */
X/*VARARGS*/
Xfatal(form,a,b,c,d,e,f,g)
Xchar *form;
X{
X	fprintf( stderr, "Fatal Error%s%s\n", lbcopy[0] ? lstr : "", lbcopy );
X	fprintf( stderr, form, a,b,c,d,e,f,g );
X	fprintf( stderr, "\n" );
X	exit(1);
X}
X
X/* DANGER, NON-PORTABLE FUNCTION */
X/*VARARGS*/
Xnonfatal(form,a,b,c,d,e,f,g)
Xchar *form;
X{
X	fprintf( stderr, "Error%s%s\n", lbcopy[0] ? lstr : "", lbcopy );
X	fprintf( stderr, form, a,b,c,d,e,f,g );
X	fprintf( stderr, "\n" );
X}
X/* DANGER, NON-PORTABLE FUNCTION */
X/*VARARGS*/
Xsemifatal(form,a,b,c,d,e,f,g)
Xchar *form;
X{
X	fprintf( stderr, "Serious Error%s%s\n", lbcopy[0] ? lstr : "", lbcopy );
X	fprintf( stderr, form, a,b,c,d,e,f,g );
X	fprintf( stderr, "\n" );
X	if( !error_ignore ) {
X		fprintf(stderr, "Use +i option to DABE to ignore this error\n" );
X		exit(1);
X		}
X	 else
X		error_stat = 2;
X}
X/* DANGER, NON-PORTABLE FUNCTION */
X/*VARARGS*/
Xwarning(form,a,b,c,d,e,f,g)
Xchar *form;
X{
X	fprintf( stderr, "Warning%s%s\n", lbcopy[0] ? lstr : "", lbcopy );
X	fprintf( stderr, form, a,b,c,d,e,f,g );
X	fprintf( stderr, "\n" );
X}
X/*VARARGS*/
Xverbose(level, form,a,b,c,d,e,f,g)
Xint level;
Xchar *form;
X{
X	int i;
X	if( is_verbose ) {
X		for( i = 0; i < level; i++ )
X			putc( '\t', stderr );
X		fprintf( stderr, form, a,b,c,d,e,f,g );
X		fprintf( stderr, "\n" );
X		}
X}
X
X/* routine to alloc and copy a string */
X
Xchar *
Xallocstring( str )
Xchar *str;
X{
X	char *p, *malloc();
X
X	p = malloc( strlen(str) + 1 );
X	if( !p )
X		fatal( "Out of memory!" );
X	(void)strcpy( p, str );
X	return p;
X}
X
X/* comeq - an alphanumeric string equality tester that ignores case */
X#define CASEBIT 0x20
Xcomeq( s1, s2 )
Xregister char *s1;
Xregister char *s2;
X{
X	while( *s1 )
X		if( ( *s1++ | CASEBIT) != ( *s2++ | CASEBIT ) )
X			return FALSE;
X	/* zero in s1, if also end of s2 strings were equal */
X	return *s2 == 0;
X}
X
X	/* convert a line number/checksum character to an index number */
X
Xlnconv( n )
Xint n;
X{
X	return n - (n > '9' ? (n > 'Z' ? 'a'-38 : 'A'-12) : '.');
X}
X
X	/* give a line number in character form, in static buffer */
X
Xstatic char lcbuf[4];
X
Xchar *
Xlineconv( num )
Xlong num;
X{
X	lcbuf[0] = safe_prints[ SFBYTE + num / (NUM_SAFE*NUM_SAFE) ];
X	lcbuf[1] = safe_prints[ (num / NUM_SAFE) % NUM_SAFE ];
X	lcbuf[2] = safe_prints[ num % NUM_SAFE ];
X	lcbuf[3] = 0;
X	return lcbuf;
X}
X
Xgivemsg( mstat, fname )
Xint mstat;
Xchar *fname;
X{
X	lbcopy[0] = 0;
X	if( mstat == NO_DATA )
X		verbose( 2, "No valid data found in file '%s'", fname );
X	else if ( mstat == UUDECODE )
X		verbose( 1, "No data in file '%s' -- but try uudecode on it",
X				fname );
X	/* some day I should build in a uudecoder and just do it */
X}
X
X	/* has the given file been seen before? */
Xbool
Xseen_file( name )
Xchar *name;
X{
X	struct fseen_list *osptr;
X
X	for( osptr = seen_list; osptr; osptr = osptr->next_fs )
X		if( strcmp( name, osptr->name_seen ) == 0 )
X			return TRUE;
X		
X	return FALSE;
X}
X
X	/* check to see if we have all this file, and if so, close it off */
X
Xconsider_closing()
X{
X	int i;
X
X	if( !in_file || !(got_main_head && got_main_end && got_checksum) )
X		return;			/* not enough header */
X	if( has_blocking ) {
X		if( total_blocks == 0 ) 
X			return;		/* no block count yet */
X		for( i = 0; i < total_blocks; i++ )
X			if( !block_gotmap[i] )
X				return;	/* missing block */
X		}
X	/* we seem to have a finished file */
X	end_current_file();				
X}
X
Xinit_output()
X{
X	if( real_filename && same_os ) {
X		out_desc = openout( real_filename );
X		verbose( 1, "File's True Name: '%s'", real_filename );
X		}
X	 else {
X		if( cur_filename ) {
X			out_desc = openout( cur_filename );
X			verbose( 1, "File Universal Name: '%s'", cur_filename );
X			}
X		 else
X			fatal( "No name for output file found" );
X		}
X	if( remember_seek != 0 ) {
X		seekto( remember_seek );
X		remember_seek = 0;
X		}
X}
X
X/* Handle the start of a block, possibly initializing the file */
X
Xstartblock(args)
Xchar *args;
X{
X
X	char tuname[MAX_UNAME+1];	/* universal name of block */
X	long seekadr; 			/* seek address to start block */
X	int early_ver;			/* earliest decoder that understands */
X	extern bool seen_file();
X
X	if( in_block )
X		fatal( "Already in block %d", current_block );
X	sscanf( args, "%d,%ld,%u,%14s", &current_block, &seekadr, 
X					&early_ver, tuname );
X
X	if( current_block >= MAX_BLOCKS )
X		fatal( "Illegal block number %d", current_block );
X
X	/* if not in a file, or in a different file,
X	   start a new file for this block */
X
X	if( !in_file || (cur_filename && strcmp(tuname,cur_filename)!=0)){
X		if( seen_file( tuname ) ) {
X			semifatal("Got block %d from previously encountered file '%s'",
X				current_block,tuname );
X			verbose( -1,"Skipping over it");
X			skipping_block = TRUE;
X			return;
X			}
X		 else
X			begin_file(0,early_ver);
X		}
X
X	init_outfile( tuname, NULL );
X	has_blocking = TRUE;
X	/* set line we next expect to see */
X	expected_line = this_line + 1;
X
X	if( block_gotmap[current_block] ) {
X		semifatal( "Duplicate of block %d detected",
X			current_block );
X		verbose( -1, "Skipping over it" );
X		skipping_block = TRUE;
X		return;
X		}
X	 else
X		verbose( 1, "Decoding block %d of file '%s' from input '%s'",
X			current_block, tuname,
X			cur_sourcefile );
X	in_block = TRUE;
X	block_gotmap[current_block] = TRUE;
X
X	seekto( seekadr );
X	block_checksum = 0;
X	block_bytecount = 0;
X	block_crc = 0xffffffffL;
X	save_total_checksum = total_checksum;
X	save_total_bytecount = total_bytecount;
X}
X
X/* Close off a block */
X
Xcloseblock( arg )
Xchar *arg;
X{
X	int ended_block;	/* what block we're ending */
X	long checksum;		/* expected checksum for block */
X	long bytecount;		/* expected bytecount for block */
X	bool failed;		/* did the block fail */
X	unsigned long blcrc;	/* block crc */
X
X
X	failed = FALSE;
X	sscanf( arg, "%d,%lu,%lu,%lu", &ended_block, &checksum, &bytecount,
X					&blcrc);
X
X	/* if we were skipping this block, just go on */
X	no_numbers = FALSE;
X	if( skipping_block ) {
X		skipping_block = FALSE;
X		return;
X		}
X		
X	if( !in_block ) {
X		semifatal( "Attempt to close unopened block %d", ended_block );
X		return;
X		}
X	if( ended_block != current_block )
X		fatal( "Attempt to close block %d while in block %d",
X			ended_block, current_block );
X	in_block = FALSE;
X	if( bytecount != block_bytecount ) {
X		semifatal( "Expected %ld bytes, got %ld in block %d",
X				bytecount, block_bytecount, current_block );
X		failed = TRUE;
X		}
X	else if( (block_checksum % CSUM_MOD) != checksum ) {
X		semifatal( "Checksum mismatch on block %d %d",
X				current_block, block_checksum % CSUM_MOD );
X		failed = TRUE;
X		}
X	else if( blcrc != ~block_crc ) {
X		semifatal( "Block CRC mismatch on block %d", current_block );
X		failed = TRUE;
X		}
X	if( failed ) {
X		block_gotmap[ended_block] = FALSE;
X		total_checksum = save_total_checksum;
X		total_bytecount = save_total_bytecount;
X		}
X}
X
Xcode_head( codeline, len )
Xchar *codeline;		/* the code header line */
Xint len;		/* length of code header line from codeline */
X{
X	int cline;	/* which line of code */
X	int i;
X	int setby, by;
X	extern int sets_per_char;	/* number of chars in a set grouping,
X					   and also the number of sets grouped
X					   into the trailing 'sets' byte */
X	int wprint;			/* which printable char */
X	int grp;			/* size of set groupings */
X
X
X	cline = print_map[ codeline[0] ];
X	grp = sets_per_char+1;
X
X	/* ABE1 code header lines have 41 bytes */
X	if( (len != 33 + 32/sets_per_char) || cline < 0 || cline > 7)
X		fatal( "Invalid code map line" );
X	for( i = 0; i < 32/sets_per_char; i++ ) {
X		setby = print_map[codeline[1+sets_per_char+i*grp]];
X		for( by = sets_per_char-1; by >= 0; by-- ) {
X			wprint = print_map[codeline[1+i*grp+by]];
X			if( wprint < 0 || wprint > 128 )
X				fatal( "Invalid char in print map" );
X			whatbyte[setby%num_sets][wprint] =
X					cline*32+i*sets_per_char+by;
X			setby /= num_sets;
X			}
X		}
X	/* mark we got this row of the coding */
X	got_coding |= 1 << cline;
X}
X
X/* initialize the encoding style */
X
Xchar *styles[] = {
X"ABE1",
X"ABE2",
X"UUENCODE",
X"TEXT",
X0
X};
X
X/* special predefined mappings for 'TEXT' secondary set */
X
Xchar tmap1[] = { 'G', 'H', 'r', 'n', 'E', '@', 0 };
Xchar tmap2[] = { 7,    8, '\r', '\n', 27, '#', 0 };
X
Xinit_style( style )
Xchar *style;
X{
X	int i;
X	int newstyle;
X
X	for( i = 0; styles[i]; i++ )
X		if( comeq( styles[i], style ) )
X			break;
X	if( styles[i] == NULL )
X		fatal( "Unknown encoding style %s", style );
X	/* if same style as before, just return */
X	if( i == current_style )
X		return;
X
X	/* A new style.  Initialize the tables for it */
X	/* This means setting up printable character map tables, including
X	   shift characters, and the number of sets and mappable chars, plus
X	   the number of sets per character in a code header line */
X
X	current_style = i;
X	switch( current_style ) {
X		case ABE1:
X			/* mark results of printables */
X			for( i = 0; i < A1NPRINTS; i++ )
X				print_map[ A1FPRINT+i ] = i;
X			num_sets = A1NSETS;
X			num_prints = A1NPRINTS;
X			set_shifts( SHIFTX, "{|" );
X			set_shifts( SHIFTXX, "!\"#$" );
X			set_shifts( SHIFTXcX, "}~00" );
X			sets_per_char = 4;
X			break;
X		case ABE2:
X			num_sets = 4;
X			set_shifts( SHIFTX, "+,-" );
X			set_shifts( SHIFTXX, "\"#$%&'()*" );
X			set_shifts( SHIFTXcX, ":;<=>?@_0" );
X			set_prints( 0, safe_prints );
X			sets_per_char = 2;
X			break;
X		case UUENCODE:
X			got_coding = FULL_MAP;	/* no coding needed */
X			for( i = 0; i < 64; i++ )
X				print_map[ ' '+i ] = i;
X			break;
X		case TEXT:
X			{
X			/* special text mapping */
X			num_sets = 2;
X			sets_per_char = 4;
X			got_coding = FULL_MAP;	/* no map needed */
X			for( i = 0; i < 128; i++ ) {
X				print_map[ i ] = i;
X				whatbyte[0][i] = whatbyte[1][i] = i;
X				}
X			print_map[0] = -1;
X			set_shifts( SHIFTX, "#" );
X			/* define proper maps in set 1 */
X			for( i = 0; tmap1[i]; i++ )
X				whatbyte[1][tmap1[i]] = tmap2[i];
X			break;
X			}
X		}
X			
X}
X
X/* set the mappings of the general printable characters */
X
Xset_prints( base, str )
Xint base;
Xchar *str;
X{
X	int i;
X	for( i = 0; i < 128 && str[i]; i++ )
X		print_map[str[i]] = i+base;
X	num_prints = i+base;
X}
X
X/*
X * This routine stores the shifting characters into the printable
X * character map.  Shifting characters all start with a base which is
X * multiplied by 256, and get an index added to them which tells which
X * sort of shifting character it is.
X * The character '0' indicates that this shift is not defined.  Thus
X * '0' can never be a shifting character.
X */
X
Xset_shifts( mainshift, shchars )
Xint mainshift;		
Xchar *shchars;
X{
X	int i;
X	char c;
X
X	for( i = 0; c = shchars[i]; i++ )
X		if( c != '0' )
X			print_map[c] = (mainshift << 8) + i;
X}
X
X#define UUD(x) ((x-' ')&0x3f)
X
X/* do uudecode on a line */
X
X/* This is far from the best uudecoder in the world, it's just here to
X * support files with the old format in them.
X */
X
Xuudecode( line, len )
Xchar *line;
Xint len;
X{
X	int blen;			/* number of bytes to put out */
X	char *p;			/* pointer into line */
X	int i;
X	
X	if( *line >= 'a' )
X		return;		/* control line of some sort */
X	blen = UUD( *line );
X	if( blen <= 0 || (blen+2)/3 != (len-1)/4 )  /* null or invalid line */
X		return;
X	line++;			/* point to start of decode area */
X
X	/* no need to space pad, as we use grave instead of space */
X
X	/* fix the possible bitnet munge */
X	for( i = 0; i < len; i++ )
X		if( line[i] == '~' )
X			line[i] = '^';
X
X	p = line;
X
X        while( blen > 0 ) {
X		outbyte( UUD(p[0]) << 2 | UUD(p[1]) >> 4 );
X		if (blen >= 2)
X			outbyte( UUD(p[1]) << 4 | UUD(p[2]) >> 2 );
X		if (blen >= 3)
X			outbyte( UUD(p[2]) << 6 | UUD(p[3]) );
X		p += 4;
X		blen -= 3;
X		}
X}
X
Xdo_options() {
X	fprintf( stderr, "Usage:\n" );
X	fprintf( stderr, "\tdabe [options] files\n" );
X	fprintf( stderr, "or to read from stdin:\n" );
X	fprintf( stderr, "\tdabe [options]\n" );
X	fprintf( stderr, "\nDabe options:\n" );
X	fprintf( stderr, "\t-v\tTurn off verbose mode -- errors only\n" );
X	fprintf( stderr, "\t+i\tIgnore errors and keep going\n" );
X	fprintf( stderr, "\t+s\tOutput file to standard output (use of -v advised)\n" );
X}
X
X/* Crc-32 builder, thanks to Rahul Dhesi */
X#define CRC_32          0xedb88320L    /* CRC-32 polynomial */
X
X/* calculates CRC of one item */
Xtcrc
Xonecrc (item)
Xint item;
X{
X   int i;
X   tcrc accum = 0;
X   item <<= 1;
X   for (i = 8;  i > 0;  i--) {
X      item >>= 1;
X      if ((item ^ accum) & 0x0001)
X         accum = (accum >> 1) ^ CRC_32;
X      else
X         accum >>= 1;
X   }
X   return (accum);
X}
X
X/* generates CRC table, calling onecrc() to make each term */
Xmkcrctab()
X{
X   int i;
X   for (i = 0;  i < TABSIZE;  i++)
X      crctab[i] = onecrc (i);
X}
E-O-F
echo Extracting tdABE1
sed 's/^X//' > tdABE1 << 'E-O-F'
X
X/* Tiny ABE 'Ascii-Binary Encoding' Decoder for ABE1 (3 sets, 94 printables)
X   'sort' ALL THE INPUT FILES TOGETHER IF THEY'RE NOT IN ORDER.
X   Try to get the full version of this program from various archives.
X   This program is mainly meant as a temporary version for Unix users.
X   DEFINE THE SYMBOL "msdos" on DOS MACHINES.
X   By Brad Templeton.  This code is released to the public domain.  No
X   warranties are provided or implied. */
X
X
X#include <stdio.h>
X#include <fcntl.h>		/* to get the MSDOS O_BINARY symbol only */
X
X#define OUR_VERSION 1000
X#define MAX_LLEN 80		/* max output line len */
X#define NSETS 3			/* number of character sets */
X#define NPRINTS 86		/* number of printable characters */
X#define FPRINT '%'		/* first printable character used */
X#define LPRINT 'z'		/* last printable */
X#define SETXX '!'
X#define NEWSET1 '{'
X#define SET10X '}'
X#define SFBYTE 31
X#define NUM_SAFE 64
X#define CSUM_MOD 65536
X
X/* header characters */
X#define CODE_HEAD '"'
X#define MAIN_HEAD '#'
X
Xlong line_num;
Xlong expected_sum = 0;
Xextern long atol();
Xint whatbyte[NSETS][NPRINTS];	/* array maps printables to bytes */
Xchar linbuf[MAX_LLEN];		/* input line */
X
Xmain()
X{
X	int i, c, len;
X	int sum;
X	int setby, by;
X	char ltype;
X
X	line_num = 1;
X#ifdef msdos
X	setmode( 1, O_BINARY );		/* make stdout binary on DOS */
X#endif
X
X	while( fgets( linbuf, MAX_LLEN, stdin ) ){
X		if( linbuf[0] >= 'T' && linbuf[0] <= 'Z' ) {
X			ltype = linbuf[4] == linbuf[5] ? linbuf[4] : 0;
X			len = strlen(linbuf);
X			if( linbuf[len-1] == '\n' )
X				linbuf[--len] = 0;
X			 else
X				continue;	/* line too long */
X			sum = 0;
X			for( i = 4; i < len; i++ )
X				sum += linbuf[i];
X			/* bad checksum, not a line for us */
X			if( (sum % NUM_SAFE) != lnconv(linbuf[3]) )
X				continue;
X			if( ( (long)(lnconv(linbuf[0])-SFBYTE)*NUM_SAFE+
X					lnconv(linbuf[1]))*NUM_SAFE +
X					lnconv(linbuf[2]) != line_num++ )
X				bomb_out("Sequence");
X			if( ltype == MAIN_HEAD ) {
X				if( linbuf[6] == 'S' ) {
X					int ver;
X					ver = atoi(linbuf+7);
X					if( ver > OUR_VERSION || ver < 1000 )
X						bomb_out( "Version number" );
X					if(strcmp(linbuf+len-5,",ABE1") != 0 )
X						bomb_out("Bad Encoding Style");
X					}
X				 else
X					if( expected_sum != atol(linbuf+7) )
X						bomb_out( "Checksum" );
X				}
X			else if( ltype == CODE_HEAD ) { /* encoding map */
X				reduce( linbuf+6 );
X				for( i = 0; i < 8; i++ ) {
X					setby = linbuf[7+4+i*5];
X					for( by = 3; by >= 0; by-- ) {
X						whatbyte[setby%3][linbuf[7+i*5	
X							+by]] = linbuf[6]*32+i*4
X							+by;
X						setby /= NSETS;
X						}
X					}
X				}
X			else if( !ltype || (ltype>=FPRINT && ltype<=LPRINT)) {
X				/* decode a line */
X				reduce( linbuf+4 );
X				expected_sum = (expected_sum + sum) % CSUM_MOD;
X				for( i = 4; i < len; i++ )
X				  switch(c = linbuf[i] + FPRINT){
X				    case NEWSET1:
X				    case NEWSET1+1:
X					putchar(whatbyte[1+c-NEWSET1]
X							[linbuf[++i]]);
X					break;
X				    case SETXX:
X				    case SETXX+1:
X				    case SETXX+2:
X				    case SETXX+3:
X					putchar(whatbyte[1+(c-SETXX)/2]
X							[linbuf[++i]]);
X					putchar(whatbyte[1+(c-SETXX)%2]
X							[linbuf[++i]]);
X					break;
X				    case SET10X:
X				    case SET10X+1:
X					putchar(whatbyte[1][linbuf[++i]]);
X					putchar(whatbyte[0][linbuf[++i]]);
X					putchar(whatbyte[1+c-SET10X]
X							[linbuf[++i]]);
X					break;
X				    default:
X					putchar(whatbyte[0][c-FPRINT]);
X					break;
X					}
X				}
X			}
X		}
X
X}
X/* reduce elements of string to numbers from 0 to NPRINTS-1 */
Xreduce(str)
Xchar *str;
X{
X	while( *str )
X		*str++ -= FPRINT;
X}
Xbomb_out(err)
Xchar *err;
X{
X	fprintf( stderr, "Decode aborted - %s error, Line: %3.3s\n",err,linbuf);
X	fprintf(stderr, "Try to sort file or get the advanced DABE decoder.\n");
X	exit(1);
X}
Xlnconv( n )		/* convert line number and checksum chars to ints */
Xint n;
X{
X	return n - (n > '9' ? (n > 'Z' ? 'a'-38 : 'A'-12) : '.');
X}
E-O-F
echo Extracting tdABE2
sed 's/^X//' > tdABE2 << 'E-O-F'
X
X/* Tiny ABE 'Ascii-Binary Encoding' Decoder for ABE2 (4 sets/64, 85 printables)
X   'sort' ALL THE INPUT FILES TOGETHER IF THEY'RE NOT IN ORDER.
X   Try to get the full version of this program from various archives.
X   This program is mainly meant as a temporary version for Unix users.
X   DEFINE THE SYMBOL "msdos" on DOS MACHINES.
X   By Brad Templeton.  This code is released to the public domain.  No
X   warranties are provided or implied. */
X
X
X#include <stdio.h>
X#include <fcntl.h>		/* to get the MSDOS O_BINARY symbol only */
X
X#define OUR_VERSION 1000
X#define MAX_LLEN 80		/* max output line len */
X#define NSETS 4			/* number of character sets */
X#define SFBYTE 31
X#define NUM_SAFE 64
X#define CSUM_MOD 65536
X
X/* header characters */
X#define CODE_HEAD '"'
X#define MAIN_HEAD '#'
X#define ismapchar(x) ((x>='.'&&x<='9')||(x>='A'&&x<='Z')||(x>='a'&&x<='z'))
X
Xlong line_num;
Xlong expected_sum = 0;
Xextern long atol();
Xint whatbyte[NSETS][NUM_SAFE];	/* array maps printables to bytes */
Xchar linbuf[MAX_LLEN];		/* input line */
X
Xmain()
X{
X	int i, c, len;
X	int sum;
X	int setby, by;
X	char ltype;
X
X	line_num = 1;
X#ifdef msdos
X	setmode( 1, O_BINARY );		/* make stdout binary on DOS */
X#endif
X
X	while( fgets( linbuf, MAX_LLEN, stdin ) ){
X		if( linbuf[0] >= 'T' && linbuf[0] <= 'Z' ) {
X			ltype = linbuf[4] == linbuf[5] ? linbuf[4] : 0;
X			len = strlen(linbuf);
X			if( linbuf[len-1] == '\n' )
X				linbuf[--len] = 0;
X			 else
X				continue;	/* line too long */
X			sum = 0;
X			for( i = 4; i < len; i++ )
X				sum += linbuf[i];
X			/* bad checksum, not a line for us */
X			if( (sum % NUM_SAFE) != bconv(linbuf[3]) )
X				continue;
X			if( ( (long)(bconv(linbuf[0])-SFBYTE)*NUM_SAFE+
X					bconv(linbuf[1]))*NUM_SAFE +
X					bconv(linbuf[2]) != line_num++ )
X				bomb_out("Sequence");
X			if( ltype == MAIN_HEAD ) {
X				if( linbuf[6] == 'S' ) {
X					int ver;
X					ver = atoi(linbuf+7);
X					if( ver > OUR_VERSION || ver < 1000 )
X						bomb_out( "Version number" );
X					if(strcmp(linbuf+len-5,",ABE2") != 0 )
X						bomb_out("Bad Encoding Style");
X					}
X				 else
X					if( expected_sum != atol(linbuf+7) )
X						bomb_out( "Checksum" );
X				}
X			else if( ltype == CODE_HEAD ) { /* encoding map */
X				int line;
X				line = bconv(linbuf[6])*32;
X				for( i = 0; i < 16; i++ ) {
X					setby = bconv(linbuf[7+2+i*3]);
X					for( by = 1; by >= 0; by-- ) {
X						whatbyte[setby%4][bconv(linbuf[
X							7+i*3+by])] = line+i*2
X							+by;
X						setby /= NSETS;
X						}
X					}
X				}
X			else if( !ltype || ismapchar(ltype)) {
X				/* decode a line */
X				expected_sum = (expected_sum + sum) % CSUM_MOD;
X				for( i = 4; i < len; i++ )
X				  switch(c = linbuf[i]){
X				    case '+':
X				    case ',':
X				    case '-':
X					putchar(whatbyte[1+c-'+']
X							[bconv(linbuf[++i])]);
X					break;
X				    case '"':
X				    case '#':
X				    case '$':
X				    case '%':
X				    case '&':
X				    case  39: /* single quote ' */
X				    case '(':
X				    case ')':
X				    case '*':
X					putchar(whatbyte[1+(c-'"')/3]
X							[bconv(linbuf[++i])]);
X					putchar(whatbyte[1+(c-'"')%3]
X							[bconv(linbuf[++i])]);
X					break;
X				    case '_':
X					/* act like this is the 7th code */
X					c = ':'+7;
X					/* fall through */
X				    case ':':
X				    case ';':
X				    case '<':
X				    case '=':
X				    case '>':
X				    case '?':
X				    case '@':
X					putchar(whatbyte[1+(c-':')/3]
X							[bconv(linbuf[++i])]);
X					putchar(whatbyte[0][
X						bconv(linbuf[++i])]);
X					putchar(whatbyte[1+(c-':')%3]
X							[bconv(linbuf[++i])]);
X
X					break;
X				    default:
X					putchar(whatbyte[0][bconv(c)]);
X					break;
X					}
X				}
X			}
X		}
X
X}
Xbomb_out(err)
Xchar *err;
X{
X	fprintf( stderr, "Decode aborted - %s error, Line: %3.3s\n",err,linbuf);
X	fprintf(stderr, "Try to sort file or get the advanced DABE decoder.\n");
X	exit(1);
X}
Xbconv( n )		/* convert character from 64 set into int */
Xint n;
X{
X	return n - (n > '9' ? (n > 'Z' ? 'a'-38 : 'A'-12) : '.');
X}
E-O-F
echo Extracting tdUUENCODE
sed 's/^X//' > tdUUENCODE << 'E-O-F'
X: This is a shell script
X: Remove or modify the sed script if line numbers are not present in
X: the archive
X: Beware of data the sort sorts into the encoding.
X: Some dumb uudecode programs will use this data.
Xsort -u $* | sed -e '/^......closeblock=/d' -e '/^......startblock=/d' -e 's/^....//' | uudecode
E-O-F
exit 0