[alt.sources] ASCIIFY and BINAFY... programs for exchanging binary files...

jtn@potomac.ads.com (John T. Nelson) (06/08/90)

Asciify and binafy are a pair of programs for converting arbitrary
format files into ASCII representation for later transmission through
network mailers.  These programs are superior to Binhex, Mcvert, xbin,
et al in that no assumptions are made concerning the type of file being
encoded.  Also, Asciify and Binafy were written with ease of protability
in mind.  A Macintosh port is now underway.

I welcome bug reports and suggestions for future improvement of this
program.  I may be reached at kzin!speaker@mimsy.umd.edu or
jtn@potomac.ads.com.

unzip here [O  ]-8-8-8-8-8-8-8-8-8-8-8-8-8-8-8-8-8-8-8-8-8-8-8-8-8-8-8-8-8-

echo x - asciify
mkdir asciify
echo x - asciify/asciify.1
sed 's/^x//' >asciify/asciify.1 <<'!Funky!Stuff!'
x.\" @(#)asciify.1 1.0 90/06/1 SMI;
x.TH ASCIIFY 1 "01 June 1990"
x.SH NAME
x.LP
xasciify \- convert file contents into asciified format
x.LP
xbinafy \- convert asciified file back to original format
x.SH SYNOPSIS
x.LP
x.B asciify
x[
x.IR -c
x]
x.IR file
x[ 
x.IR asciified_file
x]
x.LP
x.B binafy
x.IR asciified_file
x[
x.IR file
x]
x
x.SH DESCRIPTION
x.LP
x.B Asciify
xand
x.B binafy
xare a pair of programs for converting arbitrary format files into
xASCII representation for later transmission through network mailers.
xThis program is superior to Binhex, Mcvert, xbin, et al in that
xno assumptions have been made concerning the type of file being encoded.
xAlso, asciify and binafy were written for ease of porting
xbetween different machine architectures and thus facilitating
xa common interchange standard for files of any format.
x
xI believe this calls for some diabolical laughter...  MWWAHHA HAH HAW
xHA HA HA HA!!!
x
x.nf
x.LP
xHere's what an asciified file looks like:
x
x(This file must be decoded with Binafy)
x!Asciify Version 1.00!File t!Chk!
x7)GI;9*N*'3[;X7T*(+W>H[K=H>B:I+K;XCV$BK+=D#V<(6B;H_T;9/V*(_H*)3J!4327!
x;4#P<8?J?"J,7XCC?$#K=8WQ>I3C=$#J:8[F*(_T*(7[;2J,2X_W=(2B;I+C=86B!4161!
x .
x .
x .
x?(C[*(;G:9+H?8RB>YGO=87V>IFa$d!2125!
x!!
x.fi
x
xAnything outside the parenthetical line and the final bangs are ignored.
xAnything following the bang on each line is a checksum, used for error
xdetection.  Unlike Binhex, the binafy program will tell you which line
xhad a checksum error if one occurs, although there isn't much that the
xprogram can do to correct the error.
x
x.SH USING ASCIIFY
x.LP
xThe command line...
x.IP
x.B
xexample% asciify binary_file asciified_file
x.LP
xconverts binary_file into the
x.B
xasciify
xformat and puts it into file
x.B
xasciified_file.
xIf no target file is specified then the output is placed onto
xstandard output.
x
xThe command line...
x.IP
x.B
xexample% binafy asciified_file file
x.LP
xconverts the asciified format file into its original format
xand places it in
x.B
xfile.
x
xIf no target file is specified then the output is placed onto
xstandard output so in the case of binafy you'll want to redirect output
xsomeplace.  For example:
x.IP
x.B
xexample% binafy asciified_file > file
x
x.SH "SEE ALSO"
x.BR binafy (1),
x
x.SH HOW IT WORKS
x.LP
xThree bytes of the original file are read and converted into 4
xcharacters representing 6 bits each of the original 3 bytes.  Note that
x6 divides into 12 ( 3 bytes times 8 bits per byte) quite evenly.  The 64
xcharacters of the ASCII character set starting with the double quote
x(hex 22) character are used.  The exclamation point character is
xreserved as a special delimeter.
x
xNote that we have used only 64 of the 94 some printable characters.
x
xIn case the number of bytes in a file is not evenly divisible by 3, a
xspecial encoding is used to represent the remaining piece of the byte.
xThe remaining pieces can be either 2 bits or 4 bits and thus consume 20
xmore ASCII printable characters.  Because these characters are unique in
xthe encoding we will know when the end of a file has arrived and, more
ximportantly, we will know what these characters represent because they
xare outside of the main encoding scheme.
x
xThis encoding scheme yields a 4 for 3 decompression which I consider to
xbe fairly effecient.
x
x.SH INSTALLATION
x.LP
xJust type
x.B make asciify
xto build
x.B asciify
xand
x.B make binafy
xto build
x.B binafy
x
xBoth should compile and generate binaries called
x.B asciify
xand
x.B binafy
xrespectively.
x
x.SH FUTURE ENHANCEMENTS
x.LP
xSupport binafying of multipart files.  It's simple enough
xto split files apart with the Unix "split" program.  It would
xbe nice if asciify and binafy split and assembled multiple files
xautomagically.  Note that the above example has a "part of" spec in the
xheader.  That's how binafy will know what order to assemble the files
xin.
x
xThe file format is incompatible with Binhex and uuencode/uudecode
xformats.  I should probably provide compatibility and cross-conversion
xfor these too, but since I can't find their source code you'll just
xhave to use
x.B asciify
xformat for now.
x
xMagic strings.
x
x.SH BUGS
x.LP
xThe ANSI C standard actually makes no assumptions concerning
xthe size of a character or byte, therefore this program will
xprobably do the wrong thing on machines with 13-bit bytes.
xThis should not be an immediate concern as most machines use 8 bit
xbytes.
x
xAlso note that I have avoided the use of the fscanf/sscanf whenever
xpossible.  This is because the behavior of these library routines
xvaries from implementation to implementation.
x
x.SH AUTHOR
x.LP
x.nf
x=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
xORGANIZATION:  Kzinti Embassy              GEOGRAPHIC: McLean, VA
xUUCP:          kzin!speaker@mimsy.umd.edu  INTERNET:   jtn@potomac.ads.com
xSPOKEN:        Hey Stupid Cat!             PHONE:      (703) 356-6514
xPROJECT:       The Conrail Locomotive/Harpsichord Fusion Program
x=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
x.fi
!Funky!Stuff!
echo x - asciify/DOC
cat >asciify/DOC <<'!Funky!Stuff!'



ASCIIFY(1)               USER COMMANDS                 ASCIIFY(1)



NAME
     asciify - convert file contents into asciified format

     binafy - convert asciified file back to original format

SYNOPSIS
     asciify [ -_c ] _f_i_l_e [ _a_s_c_i_i_f_i_e_d__f_i_l_e ]

     binafy _a_s_c_i_i_f_i_e_d__f_i_l_e [ _f_i_l_e ]


DESCRIPTION
     Asciify and binafy are a pair  of  programs  for  converting
     arbitrary  format  files into ASCII representation for later
     transmission through network mailers.  This program is supe-
     rior  to  Binhex, Mcvert, xbin, et al in that no assumptions
     have been made concerning the type of  file  being  encoded.
     Also,  asciify  and  binafy were written for ease of porting
     between different machine architectures and thus  facilitat-
     ing a common interchange standard for files of any format.

     I  believe  this  calls  for  some  diabolical   laughter...
     MWWAHHA HAH HAW HA HA HA HA!!!


     Here's what an asciified file looks like:

     (This file must be decoded with Binafy)
     !Asciify Version 1.00!File t!Chk!
     7)GI;9*N*'3[;X7T*(+W>H[K=H>B:I+K;XCV$BK+=D#V<(6B;H_T;9/V*(_H*)3J!4327!
     ;4#P<8?J?"J,7XCC?$#K=8WQ>I3C=$#J:8[F*(_T*(7[;2J,2X_W=(2B;I+C=86B!4161!
      .
      .
      .
     ?(C[*(;G:9+H?8RB>YGO=87V>IFa$d!2125!
     !!

     Anything outside the parenthetical line and the final  bangs
     are  ignored.  Anything following the bang on each line is a
     checksum, used for  error  detection.   Unlike  Binhex,  the
     binafy program will tell you which line had a checksum error
     if one occurs, although there isn't much  that  the  program
     can do to correct the error.


USING ASCIIFY
     The command line...

          example% asciify binary_file asciified_file

     converts binary_file into the asciify  format  and  puts  it
     into  file  asciified_file.   If no target file is specified



Sun Release 4.0     Last change: 01 June 1990                   1






ASCIIFY(1)               USER COMMANDS                 ASCIIFY(1)



     then the output is placed onto standard output.

     The command line...

          example% binafy asciified_file file

     converts the asciified format file into its original  format
     and places it in file.

     If no target file is specified then  the  output  is  placed
     onto standard output so in the case of binafy you'll want to
     redirect output someplace.  For example:

          example% binafy asciified_file > file


SEE ALSO
     binafy(1),


HOW IT WORKS
     Three bytes of the original file are read and converted into
     4  characters  representing  6  bits  each of the original 3
     bytes.  Note that 6 divides into 12 ( 3 bytes times  8  bits
     per  byte)  quite  evenly.   The  64 characters of the ASCII
     character set starting with the double quote (hex 22)  char-
     acter are used.  The exclamation point character is reserved
     as a special delimeter.

     Note that we have used only 64  of  the  94  some  printable
     characters.

     In case the number of bytes in a file is not evenly  divisi-
     ble  by  3,  a  special  encoding  is  used to represent the
     remaining piece of the byte.  The remaining  pieces  can  be
     either  2  bits  or  4  bits  and thus consume 20 more ASCII
     printable characters.  Because these characters  are  unique
     in  the  encoding  we  will  know when the end of a file has
     arrived and, more importantly, we will know what these char-
     acters represent because they are outside of the main encod-
     ing scheme.

     This encoding scheme yields a 4 for 3 decompression which  I
     consider to be fairly effecient.


INSTALLATION
     Just type make asciify to build asciify and make  binafy  to
     build binafy

     Both should compile and generate binaries called asciify and
     binafy respectively.



Sun Release 4.0     Last change: 01 June 1990                   2






ASCIIFY(1)               USER COMMANDS                 ASCIIFY(1)



FUTURE ENHANCEMENTS
     Support binafying of multipart files.  It's simple enough to
     split  files  apart with the Unix "split" program.  It would
     be nice if asciify could assemble multiple  files  automagi-
     cally.   Note that the above example has a "part of" spec in
     the header.  That's how  binafy  will  know  what  order  to
     assemble the files in.

     The  file   format   is   incompatible   with   Binhex   and
     uuencode/uudecode formats.  I should probably provide compa-
     tibility and cross-conversion for these  too,  but  since  I
     can't find their source code you'll just have to use asciify
     format for now.

     Magic strings.


BUGS
     The ANSI C standard actually makes no assumptions concerning
     the size of a character or byte, therefore this program will
     probably do the wrong thing on machines with  13-bit  bytes.
     This should not be an immediate concern as most machines use
     8 bit bytes.

     Also note that I have avoided the use of  the  fscanf/sscanf
     whenever  possible.   This  is because the behavior of these
     library routines varies from implementation  to  implementa-
     tion.


AUTHOR
     =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
     ORGANIZATION:  Kzinti Embassy              GEOGRAPHIC: McLean, VA
     UUCP:          kzin!speaker@mimsy.umd.edu  INTERNET:   jtn@potomac.ads.com
     SPOKEN:        Hey Stupid Cat!             PHONE:      (703) 356-6514
     PROJECT:       The Conrail Locomotive/Harpsichord Fusion Program
     =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=


















Sun Release 4.0     Last change: 01 June 1990                   3



!Funky!Stuff!
echo x - asciify/Makefile
cat >asciify/Makefile <<'!Funky!Stuff!'
all: asciify binafy

asciify: asciify.c globals.c asciifyCore.c definitions.h globals.h Makefile
	cc asciify.c globals.c asciifyCore.c -o asciify

binafy: binafy.c globals.c binafyCore.c definitions.h globals.h Makefile
	cc binafy.c globals.c binafyCore.c -o binafy

clean:
	rm -f *.o asciify binafy core #*
!Funky!Stuff!
echo x - asciify/README
cat >asciify/README <<'!Funky!Stuff!'
Asciify and binafy are a pair of programs for converting arbitrary
format files into ASCII representation for later transmission through
network mailers.  These programs are superior to Binhex, Mcvert, xbin,
et al in that no assumptions are made concerning the type of file being
encoded.  Also, Asciify and Binafy were written with ease of protability
in mind.  A Macintosh port is now underway.

I welcome bug reports and suggestions for future improvement of this
program.  I may be reached at kzin!speaker@mimsy.umd.edu or
jtn@potomac.ads.com.


=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
ORGANIZATION:  Advanced Decision Systems   GEOGRAPHIC: Arlington, VA
UUCP:          kzin!speaker@mimsy.umd.edu  INTERNET:   jtn@potomac.ads.com
SPOKEN:        Yo... John!                 PHONE:      (703) 243-1611
PROJECT:       The Conrail Locomotive/Harpsichord Fusion Program
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
!Funky!Stuff!
echo x - asciify/asciify.c
cat >asciify/asciify.c <<'!Funky!Stuff!'
#include <stdio.h>
#include <fcntl.h>
#include "definitions.h"
#include "globals.h"

/*
	This is "asciify", a program for converting files of arbitrary
	format into an ASCII encoding for later transmission through network
	mailers.  The program "binafy" should be used to convert the ASCII
	encoding back into the original file.

	Usage: asciify [ -c ] <original file> [ <asciified file> ]

	-- John T. Nelson (a.k.a. Speaker-To-Animals) mimsy!kzin!speaker
*/

BOOLEAN parseFlags(argv)
char	*argv[];
{
char			*s;
unsigned int	k;

if ( *argv[0] == '-' ) {
	k = 1;
	header.useCheck = FALSE;
	for ( s = argv[0] + 1; *s != '\0'; s++ ) {
		switch ( *s ) {
			case 'c':
				header.useCheck = TRUE;
				break;
			}
		k++;
		}
	return TRUE;
	}
else
	return FALSE;
}

main(argc, argv)
int		argc;
char *argv[];
{
int			fi;
FILE		*fo;

unsigned int	counter;
int				status;

if ( argc == 1 ) {
	printf("Usage: asciify [ -c ] <original file> <target file>\n");
	return;
	}
argv++;
if ( argc == 2 ) {
	if ( *argv[0] == '-' ) {
		printf("Usage: asciify [ -c ] <original file> <target file>\n");
		return;
		}
	else {
		fi = open( *argv, O_RDONLY);
		fo = stdout;
		strcpy(header.fileName, "stdout");
		}
	}

if ( argc == 3 )
	if ( parseFlags(argv) ) {
		argv++;
		fi = open( *argv, O_RDONLY);
		fo = stdout;
		strcpy(header.fileName, "stdout");
		}
	else {
		fi = open( *argv, O_RDONLY);
		argv++;
		fo = fopen(*argv, "w");
		strcpy(header.fileName, *argv);
		}

if ( argc == 4 )
	if ( parseFlags(argv) ) {
		argv++;
		fi = open( *argv, O_RDONLY);
		argv++;
		fo = fopen(*argv, "w");
	strcpy(header.fileName, *argv);
		}
	else {
		printf("More than 2 file names - I'm quiting\n");
		return;
		}

if ( argc > 4 ) {
	printf("More than 2 file names - I'm quiting\n");
	return;
	}

asciify(fi, fo);
}
!Funky!Stuff!
echo x - asciify/asciifyCore.c
cat >asciify/asciifyCore.c <<'!Funky!Stuff!'
#include <stdio.h>
#include <fcntl.h>
#include "definitions.h"
#include "globals.h"

asciify(fi, fo)
int	fi;
FILE	*fo;
{
unsigned int	counter;
int				status;

num = 0;
cur = buffer;

putHerald(fo);

counter = 0;
status = 0;

while ( status != EOF ) {
	status = convertThree(fi, fo, header.useCheck);
	fflush(fo);
	}

fputc(DELIMETER, fo);
fputc(DELIMETER, fo);
fprintf(fo, "\n");
fflush(fo);

close(fi);
fclose(fo);
}

errorCleanup(fi, fo)
int		fi;
FILE	*fo;
{
close(fi);
fclose(fo);
}

/* No... don't look!  It's EVIL! */

convertThree(fi, fo, check)
int		fi;
FILE	*fo;
BOOLEAN	check;
{
int				status;
unsigned int	i;
char			byte1, byte2, byte3;
char			c;
unsigned int	checksum;

i = 1;
checksum = 0;
status = SUCCESS;

while (i < LINE_LENGTH && status != EOF) {
	status = getAbyte(fi, &byte1);
	if ( status == EOF ) {
		break;
		}
	checksum = checksum + (unsigned char) byte1;
	convert1(&byte1, &c);
	fputc(c, fo);
	i++;

	status = getAbyte(fi, &byte2);
	if ( status == EOF ) {
		putShortTwo(fo, byte1);
		break;
		}
	checksum = checksum + (unsigned char) byte2;
	convert2(&byte1, &byte2, &c);
	fputc(c, fo);
	i++;

	status = getAbyte(fi, &byte3);
	if ( status == EOF ) {
		putShortFour(fo, byte2);
		break;
		}
	checksum = checksum + (unsigned char) byte3;
	convert3(&byte2, &byte3, &c);
	fputc(c, fo);
	i++;

	convert4(&byte3, &c);
	fputc(c, fo);

	i++;
	byte1 = 0;
	byte2 = 0;
	byte3 = 0;
	}

if ( i == 1 )
	return EOF;

putCheckSum(fo, checksum, check);

return status;
}

putCheckSum(fo, checksum, check)
FILE			*fo;
unsigned int	checksum;
BOOLEAN			check;
{
if ( checksum < 0 )
	check = check;
if ( check == YES )
	fprintf(fo,"%c%u%c\n", DELIMETER, checksum, DELIMETER);
else {
	fputc(DELIMETER, fo);
	fputc('\n', fo);
	}
}

convert1(b1, c)
char	*c;
char	*b1;
{
char	byte;

byte = *b1;

byte &= UPPER_SIX_BITS;
byte = byte >> 2;
byte &= LOWER_SIX_BITS;
byte += CODING_START;

*c = byte;
return SUCCESS;
}

convert2(b1, b2, c)
char	*c;
char	*b1, *b2;
{
char	byte1;
char	byte2;

byte1 = *b1;
byte2 = *b2;

byte1 &= LOWER_TWO_BITS;
byte2 &= UPPER_FOUR_BITS;

byte1 = byte1 << 4;
byte2 = byte2 >> 4;

byte1 &= 0x30;
byte2 &= LOWER_FOUR_BITS;

byte1 |= byte2;
byte1 &= LOWER_SIX_BITS;
byte1 += CODING_START;

*c = byte1;
return SUCCESS;
}

convert3(b2, b3, c)
char	*c;
char	*b2, *b3;
{
char	byte2;
char	byte3;

byte2 = *b2;
byte3 = *b3;

byte2 &= LOWER_FOUR_BITS;
byte3 &= UPPER_TWO_BITS;

byte2 = byte2 << 2;
byte3 = byte3 >> 6;

byte2 &= 0x3C;
byte3 &= LOWER_TWO_BITS;

byte2 |= byte3;
byte2 &= LOWER_SIX_BITS;
byte2 += CODING_START;

*c = byte2;
return SUCCESS;
}

convert4(b3, c)
char	*c;
char	*b3;
{
char	byte3;

byte3 = *b3;
byte3 &= LOWER_SIX_BITS;
byte3 += CODING_START;

*c = byte3;
return SUCCESS;
}

processChecksum()
{
}

getAbyte(fi, c)
int		fi;
char	*c;
{
if ( num == 0 ) {
	num = read(fi, buffer, BUFFER_SIZE);
	if ( num == 0 || num == EOF )
		return EOF;
	cur = buffer;
	}
*c = *cur;
cur += 1;
num -= 1;

return SUCCESS;
}

putShortTwo(fo, b)
FILE	*fo;
char	b;
{
char	byte;

byte = b;
byte &= LOWER_TWO_BITS;
byte += START_TWO;
fputc(byte, fo);
}

putShortFour(fo, b)
FILE	*fo;
char	b;
{
char	byte;

byte = b;
byte &= LOWER_FOUR_BITS;
byte += START_FOUR;
fputc(byte, fo);
}

putHerald(fo)
FILE	*fo;
{
fprintf(fo, "\n%s", HERALD);

fputc(DELIMETER, fo);
fprintf(fo, VERSION_STRING, VERSION_NUM);

fputc(DELIMETER, fo);
fprintf(fo, FILE_STRING,  header.fileName);

if ( header.useCheck == YES ) {
	fputc(DELIMETER, fo);
	fprintf(fo, CHECKSUM_STRING);
	}
fputc(DELIMETER, fo);
fprintf(fo, "\n");
}
!Funky!Stuff!
echo x - asciify/binafy.c
cat >asciify/binafy.c <<'!Funky!Stuff!'
#include <stdio.h>
#include <fcntl.h>
#include "definitions.h"
#include "globals.h"

/*
	This is "binafy", a program for converting "asciified" files
	back into their original file formats.

	Usage: binafy <asciified file> <original file>

	-- John T. Nelson (a.k.a. Speaker-To-Animals) mimsy!kzin!speaker
*/

main(argc, argv)
int	argc;
char	*argv[];
{
FILE	*fi;
int		fo;

if ( argc == 1 ) {
	printf("Usage: binafy <asciify'd file> <original file>\n");
	return;
	}
if ( argc == 2 ) {
	fo = STANDARD_OUTPUT_FD;			/* Assume the shmuck knows what he wants */
	}
if ( argc == 2  || argc == 3) {
	argv++;
	fi = fopen( *argv, "r");
	}
if ( argc == 3 ) {
	argv++;
#ifndef THINK_C
	fo = open(*argv, O_WRONLY | O_CREAT, 0640);
#else
	fo = open(*argv, O_WRONLY | O_CREAT);
#endif
	}
if ( argc > 3 ) {
	printf("More than 2 arguments - I'm quiting\n");
	return;
	}

binafy(fi, fo);
}
!Funky!Stuff!
echo x - asciify/binafyCore.c
cat >asciify/binafyCore.c <<'!Funky!Stuff!'
#include <stdio.h>
#include <fcntl.h>
#include "definitions.h"
#include "globals.h"

binafy(fi, fo)
FILE	*fi;
int	fo;
{

num = -1;
header.line = 1;

if ( flushHerald(fi) == FAILURE ) {
	fclose(fi);
	close(fo);
	exit();
	}

if ( readHeader(fi, header.header) == FAILURE ) {
	fclose(fi);
	close(fo);
	exit();
	}

parseHeader(&header);

if ( header.useCheck == YES )
	processWithChecksums(fi, fo);
else
	processWithoutChecksums(fi, fo);

fclose(fi);
flushBuffer(fo);
close(fo);
}

processWithChecksums(fi, fo)
int		fi;
FILE	*fo;
{
int				status;

status = SUCCESS;

while ( status != EOF ) {
	status = processLine(fi, fo, &header);
	if ( status == FAILURE )
		return;
	if ( checkChecksum(header) == FAILURE ) {
		printf("Checksum error detected on line %d!\n", header.line);
		return;
		}
	header.line += 1;
	}
}

processWithoutChecksums(fi, fo)
int		fi;
FILE	*fo;
{
int				status;

status = SUCCESS;

while ( status != EOF ) {
	status = processLine(fi, fo, &header);
	if ( status == FAILURE )
		return;
	header.line += 1;
	}
}

processLine(fi, fo, header)
FILE			*fi;
int				fo;
struct HEADER_STRUCT	*header;
{
char			string[NUM_DATA_CHARS];
char			errorChars[NUM_CHECKSUM_CHARS];
unsigned int	i;
unsigned int	lastChar;
unsigned int	checksum;

char		byte1;
char		byte2;
char		byte3;
int			status;

switch ( collectStrings(fi, string, errorChars) ) {
	case SUCCESS:
		break;
	case FAILURE:
		return FAILURE;
	case DOUBLE_DELIMS:
		return EOF;
	default:
		return FAILURE;
	};

i = 0;
checksum = 0;

lastChar = strlen(string) - 1;
convertToNumber(errorChars, &(header -> checksumRead));

while( i <= lastChar ) {
	if ( string[i] == DELIMETER ) {
		header -> checksum = checksum;
		return SUCCESS;
		}

	status = SUCCESS;
	status = convert1(string[i], &byte1);
	if ( status == TRAILING_CHARS ) {
		printf("Byte 1 started with a short byte!\n");
		return FAILURE;
		}
	i++;
	status = convert2(string[i], &byte1, &byte2);
	if ( status == TRAILING_FOUR ) {
		printf("Byte 1 decoded 4 bits not 2!\n");
		return FAILURE;
		}
	checksum += (unsigned char) byte1;
	if ( status == TRAILING_TWO ) {
		putAchar(byte1, fo);
		status = EOF;
		break;
		}
	putAchar(byte1, fo);
	i++;
	status = convert3(string[i], &byte2, &byte3);
	if ( status == TRAILING_TWO ) {
		printf("Byte 2 decoded 2 bits not 4!\n");
		return FAILURE;
		}
	checksum += (unsigned char) byte2;
	if ( status == TRAILING_FOUR ) {
		putAchar(byte2, fo);
		status = EOF;
		break;
		}
	putAchar(byte2, fo);
	i++;
	status = convert4(string[i], &byte3);
	if ( status == TRAILING_CHARS ) {
		printf("Byte 3 had a short byte!\n");
		return FAILURE;
		}
	putAchar(byte3, fo);
	checksum += (unsigned char) byte3;
	i++;
	byte1 = 0;
	byte2 = 0;
	byte3 = 0;
	}

header -> checksum = checksum;
return SUCCESS;
}

convert1(c, b1)
char	c;
char	*b1;
{
char	byte;

if ( c >= SHORT_START )
	return TRAILING_CHARS;

byte = c - CODING_START;
byte = byte << 2;
byte &= UPPER_SIX_BITS;

*b1 = byte;
return SUCCESS;
}

convert2(c, b1, b2)
char	c;
char	*b1, *b2;
{
char	byte1;
char	byte2;
char	byte;
char	cc;

if ( c >= START_FOUR )
	return TRAILING_FOUR;

byte1 = *b1;
if ( c >= START_TWO && c < START_FOUR ) {
	byte = c - SHORT_START;
	byte1 |= byte;
	*b1 = byte1;
	return TRAILING_TWO;
	}

cc = c - CODING_START;
byte = cc >> 4;				/* Extract the upper 2 bits */
byte &= LOWER_TWO_BITS;
byte1 |= byte;

byte = cc << 4;				/* The lower 4 bits */
byte2 = byte & UPPER_FOUR_BITS;

*b1 = byte1;
*b2 = byte2;
return SUCCESS;
}

convert3(c, b2, b3)
char	c;
char	*b2, *b3;
{
char	byte2;
char	byte3;
char	byte;
char	cc;

if ( c < START_FOUR  && c >= SHORT_START )
	return TRAILING_TWO;

byte2 = *b2;
if ( c >= START_FOUR && c <= SHORT_STOP ) {
	byte = c - (SHORT_START + MASK_TWO);
	byte2 |= byte;
	*b2 = byte2;
	return TRAILING_FOUR;
	}

cc = c - CODING_START;
byte = cc >> 2;				/* Extract the upper 4 bits */
byte &= LOWER_FOUR_BITS;
byte2 |= byte;

byte = cc << 6;				/* The lower 2 bits */
byte3 = byte & UPPER_TWO_BITS;

*b2 = byte2;
*b3 = byte3;
return SUCCESS;
}

convert4(c, b3)
char	c;
char	*b3;
{
char	byte3;
char	byte;

byte3 = SHORT_START;

if ( c >= SHORT_START )
	return TRAILING_CHARS;

byte3 = *b3;
byte = c - CODING_START;
byte &= LOWER_SIX_BITS;
byte3 |= byte;

*b3 = byte3;
return SUCCESS;
}

flushHerald(fi)
FILE *fi;
{
char		string[MAX_LINE_LENGTH];

while ( fgets(string, MAX_LINE_LENGTH, fi) != NULL ) {
	header.line++;
	if ( strcmp(string, HERALD) == 0 )
		return SUCCESS;
	}
printf("Geeze... I didn't even get to the herald!\n");
return FAILURE;
}

readHeader(fi, h)
FILE 	*fi;
char	*h;
{

while ( fgets(h, MAX_LINE_LENGTH, fi) != NULL ) {
	header.line++;
	if ( h[0] == DELIMETER )
		return SUCCESS;
	}
printf("Couldn't find the header!\n");
return FAILURE;
}

putAchar(byte, fo)
char	byte;
int		fo;
{

num++;
if ( num > BUFFER_SIZE - 1 ) {
	write(fo, buffer, BUFFER_SIZE);
	num = 0;
	}
buffer[num] = byte;
}

flushBuffer(fo)
int	fo;
{
int	n;

if ( num > BUFFER_SIZE - 1 )
	num = BUFFER_SIZE;
else
	num++;

while ( num > 0 ) {
	n = write(fo, buffer, num);
	num -= n;
	}
}

parseHeader(header)
struct HEADER_STRUCT	*header;
{
char	*s;
char	h[MAX_LINE_LENGTH];

s = header -> header;
header -> useCheck = FALSE;

if ( *s != DELIMETER ) {
	printf("Bad header format!\n");
	return FAILURE;
	}
s++;
while ( *s != '\n' && *s != '\0' ) {
	if ( headerExtract(&s, h) == FAILURE )
		return FAILURE;
	if ( strcmp(h, CHECKSUM_STRING) == 0 )
		header -> useCheck = TRUE;
	}
}

headerExtract(s, h)
char	**s;
char	*h;
{

while ( **s == DELIMETER || **s == '\n' ) (*s)++;

while ( **s != DELIMETER ) {
	if ( **s == '\0' )
		return FAILURE;

	*h = **s;
	h++;
	(*s)++;
	}
*h = '\0';
return SUCCESS;
}

collectStrings(fi, s, e)
FILE		*fi;
char		*s;
char		*e;
{
char		c;

if ( getAchar(fi, &c) == EOF )
	return FAILURE;

while ( c != DELIMETER ) {
	*s = c;
	s++;
	if ( getAchar(fi, &c) == EOF )
		return FAILURE;
	}
*s = '\0';
if ( getAchar(fi, &c) == EOF )
	return FAILURE;
if ( c == DELIMETER && *s == '\0' )
	return DOUBLE_DELIMS;
if ( header.useCheck == NO ) {
	while ( c != '\n' ) {
		if ( getAchar(fi, &c) == EOF )
			return FAILURE;
		}
	}
else {
	if ( c == DELIMETER || c == '\n' ) {
		printf("So where's the checksum?\n");
		return FAILURE;
		}
	while ( c != DELIMETER ) {
		*e = c;
		if ( getAchar(fi, &c) == EOF )
			return FAILURE;
		e++;
		}
	if ( getAchar(fi, &c) == EOF )
		return FAILURE;
	if ( c != '\n' ) {
		printf("Expected a new-line here\n");
		return FAILURE;
		}
	}
*e = '\0';
return SUCCESS;
}

checkChecksum(header)
struct HEADER_STRUCT		header;
{
if ( header.checksum != header.checksumRead )
	return FAILURE;
else
	return SUCCESS;
}

getAchar(fi, c)
FILE	*fi;
char	*c;
{
if ( (*c = fgetc(fi)) == EOF ) {
	printf("Premature EOF encountered\n");
	return EOF;
	}
}

convertToNumber(errorChars, number)
char	*errorChars;
unsigned int	*number;
{
int		s;
int sum, n;
unsigned int	i;
char	c;

s = strlen(errorChars) - 1;
sum = 0;
i = 0;

while ( s >= 0 ) {
	c = errorChars[s];
	n = atoi(&c);
	if ( i == 0 )
		sum += n;
	else
		sum += (n * power(10, i));
	s--;
	i++;
	}
*number = (unsigned int) sum;
}

power(base, n)
int base, n;
{
int i, p;

p = 1;
for ( i = 1; i <= n; ++i )
	p = p * base;
return p;
}
!Funky!Stuff!
echo x - asciify/definitions.h
cat >asciify/definitions.h <<'!Funky!Stuff!'
#ifndef DEFINITIONS_DEFINED
#define DEFINITIONS_DEFINED

#define			TRUE			1
#define			FALSE			0
#define			YES				TRUE
#define			NO				FALSE

#define			VERSION_NUM		1.0
#define			VERSION_STRING	"Asciify Version %1.2f"
#define			FILE_STRING		"File %s"
#define			CHECKSUM_STRING	"Chk"

#define			DELIMETER		0x21
#define			CODING_START	0x22
#define			BIT_SPAN		0x3F	/* 6 bits worth */
#define			SHORT_START		(CODING_START + BIT_SPAN + 1)
#define			MASK_TWO		0x0003
#define			MASK_FOUR		0x000F
#define			START_TWO		SHORT_START
#define			START_FOUR		(SHORT_START + MASK_TWO + 1)
#define			SHORT_STOP		0x7E
#define			LOWER_CHAR		0x00FF
#define			UPPER_CHAR		0xFF00

#define			UPPER_TWO_BITS	0xC0
#define			UPPER_FOUR_BITS	0xF0
#define			UPPER_SIX_BITS	0xFC
#define			LOWER_TWO_BITS	0x03
#define			LOWER_FOUR_BITS	0x0F;
#define			LOWER_SIX_BITS	0x3F

#define			FAILURE			0
#define			SUCCESS			1
#define			NO_TRAILING		1
#define			TRAILING_CHARS	2
#define			TRAILING_TWO	3
#define			TRAILING_FOUR	4
#define			DOUBLE_DELIMS	5

#define			CHECKSUM		10
#define			NO_CHECKSUM		11

#define			BUFFER_SIZE				1024
#define			MAX_LINE_LENGTH			80
#define			LINE_LENGTH				64
#define			NUM_CHECKSUM_CHARS		10
#define			NUM_DATA_CHARS			80

#define			HERALD		"(This file must be decoded with Binafy)\n"

typedef		short		BOOLEAN;

typedef struct HEADER_STRUCT {
	char			fileName[MAX_LINE_LENGTH];
	char			header[MAX_LINE_LENGTH];
	BOOLEAN			useCheck;
	unsigned int	checksum;
	unsigned int	checksumRead;
	unsigned int	line;
	};

#define		STANDARD_OUTPUT_FD		1

#endif
!Funky!Stuff!
echo x - asciify/globals.c
cat >asciify/globals.c <<'!Funky!Stuff!'
#include "definitions.h"

char			buffer[BUFFER_SIZE];
int				num = 0;
char			*cur;

struct HEADER_STRUCT	header = {
		"",	/* The file we are processing */
			"",			/* The line read */
			NO,			/* Perform checksums? */
			0,			/* CHecksum calculated */
			0,			/* Checksum read */
			0			/* Line */
			};
!Funky!Stuff!
echo x - asciify/globals.h
cat >asciify/globals.h <<'!Funky!Stuff!'
#include "definitions.h"

extern char			buffer[BUFFER_SIZE];
extern int			num;
extern char			*cur;

extern struct HEADER_STRUCT	header;
!Funky!Stuff!
echo x - asciify/tyger
cat >asciify/tyger <<'!Funky!Stuff!'
Tyger, Tyger burning bright

In the forest of the night

What immortal hand or eye

Could frame thy fearful symmetry?
!Funky!Stuff!