home *** CD-ROM | disk | FTP | other *** search
- Path: wugate!wucs1!uunet!bbn.com!rsalz
- From: rsalz@uunet.uu.net (Rich Salz)
- Newsgroups: comp.sources.unix
- Subject: v18i108: Btoa v5.2 -- binary/ascii packer with repair features
- Message-ID: <1682@papaya.bbn.com>
- Date: 19 Apr 89 19:24:06 GMT
- Lines: 1593
- Approved: rsalz@uunet.UU.NET
-
- Submitted-by: d84sp@efd.lth.se
- Posting-number: Volume 18, Issue 108
- Archive-name: btoa
-
- Btoa was originally part of the Compress4.0 distribution; it's now
- broken out into a separate package. It converts between binary
- and ASCII (printable) files.
-
- This is version 5.2 of btoa for distribution to comp.sources.unix.
-
- Besides expanding binary files by only 25% rather than
- uuencode's 33%, it now also has got the ability to
- REPAIR damaged files, without having to retransmit the
- entire file.
-
- It has worked successfully when transferring files between
- a Sun-3 and an Amiga 1000.
-
- Below is the compressed and uuencoded shar archive.
- --
- Stefan Parmark, Lund Institute of Technology, Sweden
- # This is a shell archive.
- # Remove everything above and including the cut line.
- # Then run the rest of the file through sh.
- #----cut here-----cut here-----cut here-----cut here----#
- #!/bin/sh
- # shar: Shell Archiver
- # Run the following text with /bin/sh to create:
- # README
- # btoa.1
- # btoa.doc
- # Makefile.amiga_lattice
- # Makefile.sun
- # atob.c
- # btoa.c
- # btoa.h
- # chksum.h
- # repair.c
- # This archive created: Tue Feb 21 17:45:16 1989
- cat << \SHAR_EOF > README
- BTOA version 5.2
- ------------------
- Written by Paul Rutter, Joe Orost & Stefan Parmark.
-
-
- Btoa was created from atob/btoa which were submitted to Usenet
- by Paul E. Rutter. Atob and btoa has now been merged into one
- program, now refered to as btoa.
-
- Btoa converts 4 binary characters to 5 ascii ones, causing a 25%
- expansion. Spaces will not be used, which should make it safe
- to send files over Usenet without risking that blanks become
- tabs.
-
- Decoding, which previously was done with atob, is now an option
- of btoa. See the manual for details.
-
- One of the drawbacks with the previous version of btoa was that,
- if there was an error in the file, atob only stated so, but gave
- no clue to its location. It used a checksum covering the
- total file, making it impossible to detect where the error was.
- I added a single-byte checksum for each row.
-
- Further, the file contained no information about the name of the
- output file. Rather, stdout was used. Version 5.0 has the feature
- to name the file contents. It can be turned off by reading data
- from stdin.
-
- A totally new feature is the ability to mend corrupted archives.
- This can be done as long as the header and last line are OK.
- It detects bad lines, informs the remote computer about this,
- which retransmits these lines in a special file, which btoa uses
- to repair the archive. See the manual for more details.
-
- Btoa uses characters between '!' and 'u'. Special characters are
- 'z' meaning 4 consecutive zeros, 'y' meaning 4 spaces, and
- 'x' as an end-of-archive mark. The 'y' was added be me, and will
- not be recognized by the old version.
-
- Old btoa encoding is still possible, as an extra option (see the
- manual). When decoding, btoa can tell if it's the old or new btoa
- format by looking at the header.
-
- I removed the feature to exit with no output if there was en error
- in the archive. This was done by using a temporary file for
- storage. This is not a good idea for micro computer folks like
- me, with limited storage possibilities. I hope all realize that
- you shouldn't run a file that was created from a corrupted archive.
-
- Most of the code was rewritten in order to make it execute faster.
- Special efforts have been made to optimize the atob part.
- Measured speeds are 30 kbyte/s on a Sun-3 and 4 kbyte/s on an
- Amiga. On the Amiga I used the VD0: recoverable RAM disk without
- FFS.
-
- I have tested btoa on an Amiga 1000 and a Sun-3, and it has
- worked very well. If you find it doesn't work on your favourite
- computer, drop me a note to one of the addresses below, preferably
- the top one, and I'll see what I can do. Also, if you port and/or
- improve it, please send me the diffs, and I'll include them in the
- next release.
-
- ---------------------- DISCLAIMER -------------------------------
-
- I assume no responsibility for the use of btoa. It should work OK,
- and I have included lots of tests to make sure that files open,
- end-of-file is detected, etc.
-
- Btoa is in the public domain. You may use it, give it away, and
- make improvements, as long as the names of the developers are
- mentioned and you don't use it to earn money. It may NOT be used
- commercially without my permission.
-
- /Stefan Parmark
- d84sp@efd.lth.se
- d84spa@rigel.sunet.se
- SHAR_EOF
- cat << \SHAR_EOF > btoa.1
- .TH BTOA 1 "21 February 1989"
- .| btoa version 5.2
- .SH NAME
- btoa - encode/decode binary to printable ASCII
- .SH SYNOPSIS
- btoa [-adhor] [input filename] [output filename]
- .SH DESCRIPTION
- .I Btoa
- is a filter which reads binary bytes from the input file and
- generates printable ASCII characters on the output file. It attaches
- a header and a checksum to the archive. It can also reverse this,
- creating a binary file from the archive.
- .LP
- Since last version of
- .I btoa/atob,
- several new features have been
- added. The most obvious one is that
- .I atob
- has been integrated
- with
- .I btoa.
- They are now the same program which is called with
- different arguments. Another is the ability to repair damaged
- archives.
- .LP
- The new version is compatible with the old version, that is,
- it can still encode and decode old btoa files.
- .LP
- .I Btoa
- has an option to decode the archive, restoring the binary bytes.
- It strips the input file until it finds a valid header, and continues
- decoding until the end mark is found. It recognices both old- and
- new-style headers, and can decode both. It is possible to leave out
- the destination name when decoding new-style archives, because the
- name is stored in the header. Entering a name will override the
- autonaming function.
- .LP
- It is possible to leave out the file names and redirect stdin and
- stdout with '<' and '>' to the desired files. This is to maintain
- compatibility with earlier versions of
- .I btoa.
- .LP
- .I Btoa
- now adds a single byte checksum to each row in the archive.
- When an error is found, diagnosis automatically starts and produces
- a diagnosis file which can be used to extract the damaged part from
- an errorfree archive. The extracted part can then be used to correct
- the damaged archive.
- .I Btoa
- has options to perform the reparation
- automatically. This is especially useful when downloading data
- converted to text files, and occasionally finding that an archive
- file of considerable size turns is corrupted.
- .SH FEATURES
- .I Btoa
- encodes 4 binary bytes into 5 characters, expanding the file by
- 25%. As a special case 4 zeroes will be encoded as 'z' and 4
- spaces as 'y'. This makes it possible to compress the archive a bit.
- .SH OPTIONS
- .IP -h
- Shows help on
- .I btoa.
- .LP
- .IP-a
- Switches to
- .I atob
- (decoding) mode.
- .LP
- .IP -o
- Switches to old version of
- .I btoa.
- .LP
- .IP -d
- Extracts repair file from diagnosis file. This assumes that
- an undamaged version of the archive and a file called
- 'btoa.dia' is present.
- .LP
- .IP-r
- Repairs the damaged archive. A file named 'btoa.rep' must
- be present for this to work.
- .SH EXAMPLES
- Below follows a description of a normal repair session. Lines
- beginning with 'Local>' were typed on the computer to which the
- file was downloaded. Accordingly, lines typed on the connected
- computer will begin with 'Remote>'. Sending a file to the other
- computer will be noted as 'transmit file'.
- .LP
- A normal repairing procedure is as follows:
- Local> btoa -a file.btoa
- btoa: Bad checksum on line 2648.
- btoa: Starting diagnosis.
- btoa: Diagnosis output to 'btoa.dia'.
- Local> transmit btoa.dia
- .LP
- Remote> btoa -d file.btoa
- btoa: Repair output to 'btoa.rep'.
- Remote> transmit btoa.rep
- .LP
- Local> btoa -a btoa.rep
- btoa: Repaired archive written to 'btoa.rdy'.
- .LP
- You can now erase file.btoa and decode btoa.rdy using 'btoa -a btoa.rdy'.
- .SH AUTHORS
- Paul Rutter Joe Orost Stefan Parmark
- .SH KNOWN BUGS
- .I Btoa
- will not work properly unless the input is a true file or a
- redirected one. This is because file positions are collected during
- diagnosis for later reference when producing the diagnosis file.
- The bug is actually in fseek() which only can reposition 'real' files.
- .LP
- Send bug reports to d84sp@efd.lth.se (Stefan Parmark).
- SHAR_EOF
- cat << \SHAR_EOF > btoa.doc
-
-
-
- BTOA(1) USER COMMANDS BTOA(1)
-
-
-
- NAME
- btoa - encode/decode binary to printable ASCII
-
- SYNOPSIS
- btoa -adhor input filename output filename
-
- DESCRIPTION
- Btoa is a filter which reads binary bytes from the input
- file and generates printable ASCII characters on the output
- file. It attaches a header and a checksum to the archive. It
- can also reverse this, creating a binary file from the
- archive.
-
- Since last version of btoa/atob, several new features have
- been added. The most obvious one is that atob has been
- integrated with btoa. They are now the same program which is
- called with different arguments. Another is the ability to
- repair damaged archives.
-
- The new version is compatible with the old version, that is,
- it can still encode and decode old btoa files.
-
- Btoa has an option to decode the archive, restoring the
- binary bytes. It strips the input file until it finds a
- valid header, and continues decoding until the end mark is
- found. It recognices both old- and new-style headers, and
- can decode both. It is possible to leave out the destination
- name when decoding new-style archives, because the name is
- stored in the header. Entering a name will override the
- autonaming function.
-
- It is possible to leave out the file names and redirect
- stdin and stdout with '<' and '>' to the desired files. This
- is to maintain compatibility with earlier versions of btoa.
-
- Btoa now adds a single byte checksum to each row in the
- archive. When an error is found, diagnosis automatically
- starts and produces a diagnosis file which can be used to
- extract the damaged part from an errorfree archive. The
- extracted part can then be used to correct the damaged
- archive. Btoa has options to perform the reparation
- automatically. This is especially useful when downloading
- data converted to text files, and occasionally finding that
- an archive file of considerable size turns is corrupted.
-
- FEATURES
- Btoa encodes 4 binary bytes into 5 characters, expanding the
- file by 25%. As a special case 4 zeroes will be encoded as
- 'z' and 4 spaces as 'y'. This makes it possible to compress
- the archive a bit.
-
-
-
-
-
- Last change: 21 February 1989 1
-
-
-
-
-
-
- BTOA(1) USER COMMANDS BTOA(1)
-
-
-
- OPTIONS
- -h Shows help on btoa.
-
- -a Switches to atob (decoding) mode.
-
- -o Switches to old version of btoa.
-
- -d Extracts repair file from diagnosis file. This assumes
- that an undamaged version of the archive and a file
- called
-
- -r Repairs the damaged archive. A file named 'btoa.rep'
- must be present for this to work.
-
- EXAMPLES
- Below follows a description of a normal repair session.
- Lines beginning with 'Local>' were typed on the computer to
- which the file was downloaded. Accordingly, lines typed on
- the connected computer will begin with 'Remote>'. Sending a
- file to the other computer will be noted as 'transmit file'.
-
- A normal repairing procedure is as follows: Local> btoa -a
- file.btoa btoa: Bad checksum on line 2648. btoa: Starting
- diagnosis. btoa: Diagnosis output to 'btoa.dia'. Local>
- transmit btoa.dia
-
- Remote> btoa -d file.btoa btoa: Repair output to 'btoa.rep'.
- Remote> transmit btoa.rep
-
- Local> btoa -a btoa.rep btoa: Repaired archive written to
- 'btoa.rdy'.
-
- You can now erase file.btoa and decode btoa.rdy using 'btoa
- -a btoa.rdy'.
-
- AUTHORS
- Paul Rutter Joe Orost Stefan Parmark
-
- KNOWN BUGS
- Btoa will not work properly unless the input is a true file
- or a redirected one. This is because file positions are col-
- lected during diagnosis for later reference when producing
- the diagnosis file. The bug is actually in fseek() which
- only can reposition 'real' files.
-
- Send bug reports to d84sp@efd.lth.se (Stefan Parmark).
-
-
-
-
-
-
-
-
-
- Last change: 21 February 1989 2
-
-
-
- SHAR_EOF
- cat << \SHAR_EOF > Makefile.amiga_lattice
- OBJS = btoa.o atob.o repair.o
- CFLAGS = -cef -dLATTICE -v -w
-
- btoa : $(OBJS)
- blink lib:c.o $(OBJS) TO btoa LIB lib:lcs.lib SC SD ND
-
- atob.o : atob.c btoa.h chksum.h
- btoa.o : btoa.c btoa.h chksum.h
- repair.o : repair.c btoa.h
- SHAR_EOF
- cat << \SHAR_EOF > Makefile.sun
- OBJS = btoa.o atob.o repair.o
-
- btoa : $(OBJS)
- cc -O $(OBJS) -o btoa
-
- btoa.o : btoa.c btoa.h chksum.h
- cc -c -O btoa.c
- atob.o : atob.c btoa.h chksum.h
- cc -c -O atob.c
- repair.o : repair.c btoa.h
- cc -c -O repair.c
- SHAR_EOF
- cat << \SHAR_EOF > atob.c
- /* atob.c */
-
- /* Written by Paul Rutter, Joe Orost & Stefan Parmark. */
-
- #include <stdio.h>
- #ifdef AMIGA
- #include <stdlib.h>
- #include <string.h>
- #endif AMIGA
-
- #include "btoa.h"
- #if USE_MACROS
- #include "chksum.h"
- #endif USE_MACROS
-
-
- BYTE atob(infile)
- register FILE *infile;
- {
- register BYTE error;
- register LONG filepos;
- int maxperline;
- LONG n1, n2, oeor, osum, orot, lastline;
- static BYTE outfilename[BUFSIZE];
- extern LONG Ceor, Csum, Crot;
- extern FILE *outfile;
- extern BYTE new_version, openoutput, buffer[BUFSIZE];
-
- error = FALSE;
-
- /* Search for archive header. */
- do
- {
- filepos = ftell(infile);
-
- if (readbuffer(buffer, "archive", infile))
- error = TRUE;
- }
- while (!(error || strncmp(buffer, "xbtoa", 5) == 0));
-
- if (!error)
- if (strcmp(buffer, "xbtoa Begin\n") == 0)
- {
- new_version = FALSE;
- fprintf(stderr, "btoa: Old btoa format.\n");
- }
- else if (sscanf(buffer, "xbtoa5 %d %s Begin\n", &maxperline, outfilename) == 2)
- {
- new_version = TRUE;
- /* Naming a file overrides the read-name-from-file function. */
- if (!openoutput && strcmp(outfilename, "-") != 0)
- if ((outfile = fopen_write(outfilename)) == NULL)
- error = TRUE;
- else
- openoutput = TRUE;
- }
- else
- {
- fprintf(stderr, "btoa: Illegal archive header.\n");
- error = TRUE;
- }
-
- if (!error)
- {
- Ceor = Csum = Crot = 0;
-
- if (new_version)
- error = new_decodefile(infile, &lastline, filepos, maxperline);
- else
- error = old_decodefile(infile, &lastline);
- }
-
- if (!error)
- {
- if (sscanf(buffer, "xbtoa End N %ld %lx E %lx S %lx R %lx\n",
- &n1, &n2, &oeor, &osum, &orot) != 5)
- {
- fprintf(stderr, "btoa: Bad format on line %ld. Can't repair file.\n",
- lastline);
- error = TRUE;
- }
- else if ((n1 != n2) || (oeor != Ceor) || (osum != Csum) || (orot != Crot))
- {
- fprintf(stderr, "btoa: Bad file checksum. Can't repair file.\n");
- error = TRUE;
- }
- else
- /* Flush last characters. */
- decode_line(NULL, (int) ((n1 - 1) & 0x03));
- }
-
- return(error);
- }
-
-
- /* Peek at the next byte without moving the file pointer. */
- int nextbyte(infile)
- register FILE *infile;
- {
- register int ch;
- register LONG filepos;
-
- filepos = ftell(infile);
- ch = fgetc(infile);
- fseek(infile, filepos, 0);
-
- return(ch);
- }
-
-
- BYTE new_decodefile(infile, lastline, filepos, maxperline)
- register FILE *infile;
- LONG *lastline, filepos;
- int maxperline;
- {
- register int length;
- int ch;
- register BYTE stop, error, newerror, errorstart;
- register LONG line, prevfilepos, startpos;
- struct Diagnosis diagnosislist;
- extern LONG Csum;
- extern BYTE buffer[BUFSIZE];
-
- error = FALSE;
-
- line = 1; /* Current line number. */
-
- /* A sequence of errors is indicated by errorstart. When it */
- /* changes from TRUE to FALSE a diagnosis record will be */
- /* generated. */
- stop = errorstart = FALSE;
-
- /* File position of the line before the error sequence. */
- /* That is the last correct line. */
- startpos = 0;
-
- while (!stop)
- {
- prevfilepos = filepos;
- filepos = ftell(infile);
-
- /* Newerror indicates an error on the current line. */
- newerror = FALSE;
-
- line++;
- if (readbuffer(buffer, "archive", infile))
- newerror = stop = TRUE;
- else if (buffer[0] == 'x') /* End of archive found. */
- stop = TRUE;
- else if ((length = strlen(buffer) - 1) != maxperline ||
- buffer[length] != '\n')
- {
- /* If last character wasn't end-of-line, then we */
- /* have to read until it is found. */
- if (buffer[length] != '\n')
- {
- newerror = TRUE;
- while ((ch = fgetc(infile)) != EOF && (BYTE)ch != '\n')
- ;
- if (ch == EOF)
- stop = TRUE;
- }
- else if (length > maxperline || nextbyte(infile) != 'x')
- {
- newerror = TRUE;
- Csum = DECODE(buffer[length - 1]); /* Make Csum correct (modulo 85). */
- }
-
- if (newerror)
- fprintf(stderr, "btoa: Bad line length on line %ld.\n", line);
- }
-
- if (!(newerror || stop))
- {
- if (decode_line(buffer, length - 1))
- {
- if (!error)
- fprintf(stderr, "btoa: Bad character on line %ld.\n", line);
- newerror = TRUE;
- }
-
- /* Examine checksum. */
- if ((ch = buffer[length - 1]) == ENCODE(Csum % 85))
- {
- if (errorstart)
- {
- intodiagnosislist(&diagnosislist, startpos, filepos);
- errorstart = FALSE;
- }
- }
- else
- {
- newerror = TRUE;
- fprintf(stderr, "btoa: Bad checksum on line %ld.\n", line);
- Csum = DECODE(ch); /* Make Csum correct (modulo 85). */
- }
- }
-
- if (newerror)
- {
- if (!error)
- {
- fprintf(stderr, "btoa: Starting diagnosis.\n");
- diagnosislist.next = diagnosislist.last = NULL;
- }
-
- error = TRUE;
- if (!errorstart)
- {
- errorstart = TRUE;
- startpos = prevfilepos;
- }
- }
- }
-
- if (error)
- {
- if (errorstart)
- intodiagnosislist(&diagnosislist, startpos, filepos);
- producediagnosis(&diagnosislist, infile);
- }
-
- *lastline = line;
-
- return(error);
- }
-
-
-
- BYTE old_decodefile(infile, lastline)
- register FILE *infile;
- LONG *lastline;
- {
- register int length;
- register BYTE stop, error;
- register LONG line;
- extern BYTE buffer[BUFSIZE];
-
- error = FALSE;
-
- line = 1;
- stop = FALSE;
- while (!stop)
- {
- line ++;
-
- if (readbuffer(buffer, "archive", infile))
- error = stop = TRUE;
- else if (buffer[0] == 'x') /* End of archive found. */
- stop = TRUE;
- else
- {
- length = strlen(buffer) - 1;
- if (buffer[length] != '\n')
- error = stop = TRUE; /* The line was longer than the buffer. */
- }
-
- if (!stop)
- {
- if (decode_line(buffer, length))
- {
- fprintf(stderr, "btoa: Bad character on line %ld.\n", line);
- error = stop = TRUE;
- }
- }
- }
-
- *lastline = line;
-
- return(error);
- }
-
-
- BYTE decode_line(buffer, length)
- register BYTE *buffer;
- register int length;
- {
- register int ch;
- register BYTE error;
- register LONG tmp_codeword;
- extern BYTE new_version;
- extern FILE *outfile;
- static LONG codeword;
- static int ch1, ch2, ch3, ch4;
- static BYTE bytecount = 0;
-
- error = FALSE;
-
- if (buffer == NULL) /* Flush last characters. */
- {
- if (bytecount > 0)
- {
- fputc(ch1, outfile);
- if (length > 0)
- fputc(ch2, outfile);
- if (length > 1)
- fputc(ch3, outfile);
- if (length > 2)
- fputc(ch4, outfile);
- }
- }
- else
- {
- while (length > 0)
- {
- length--;
- ch = *buffer++;
-
- /* Delayed output. This is to make sure that files with lengths */
- /* that are not multiples of 4 won't become too long. */
- if (bytecount == 5)
- {
- fputc(ch1, outfile);
- fputc(ch2, outfile);
- fputc(ch3, outfile);
- fputc(ch4, outfile);
-
- bytecount = 0;
- }
-
- if (new_version)
- calcchecksum(ch);
-
- if (((BYTE)ch >= '!') && ((BYTE)ch < ('!' + 85))) /* Valid characters. */
- {
- /* See if we can take all 5 bytes and decode them right away. */
- /* That is, if all remaining bytes are on the current line. */
- if (length >= 4 - bytecount)
- {
- length -= 4 - bytecount;
-
- if (bytecount == 0)
- codeword = DECODE(ch);
- else
- codeword = codeword * 85 + DECODE(ch);
-
- for (bytecount++; bytecount < 5; bytecount++)
- {
- ch = *buffer++;
- if (new_version)
- calcchecksum(ch);
- codeword = codeword * 85 + DECODE(ch);
- }
- }
- else
- {
- /* Shift codeword and insert character. */
-
- if (bytecount == 0)
- {
- codeword = DECODE(ch);
- bytecount = 1;
- }
- else /* bytecount < 5 */
- {
- codeword = codeword * 85 + DECODE(ch);
- bytecount ++;
- }
- }
-
- if (bytecount == 5)
- {
- tmp_codeword = codeword;
-
- ch4 = (int)tmp_codeword & 0xFF;
- ch3 = (int)(tmp_codeword >>= 8) & 0xFF;
- ch2 = (int)(tmp_codeword >>= 8) & 0xFF;
- ch1 = (int)(tmp_codeword >> 8) & 0xFF;
-
- if (!new_version)
- {
- calcchecksum(ch1);
- calcchecksum(ch2);
- calcchecksum(ch3);
- calcchecksum(ch4);
- }
- }
- }
- else if ((BYTE)ch == 'z' || (new_version && (BYTE)ch == 'y'))
- {
- if (bytecount != 0)
- error = TRUE;
- else
- {
- ch1 = ch2 = ch3 = ch4 = (ch == 'z') ? 0 : ' ';
- if (!new_version)
- {
- calcchecksum(ch1);
- calcchecksum(ch1);
- calcchecksum(ch1);
- calcchecksum(ch1);
- }
- bytecount = 5;
- }
- }
- else
- error = TRUE;
- }
- }
-
- return(error);
- }
- SHAR_EOF
- cat << \SHAR_EOF > btoa.c
- /* btoa.c */
-
- /* Written by Paul Rutter, Joe Orost & Stefan Parmark. */
-
- #include <stdio.h>
- #ifdef AMIGA
- #include <stdlib.h>
- #include <string.h>
- #endif AMIGA
-
- #include "btoa.h"
- #if USE_MACROS
- #include "chksum.h"
- #endif USE_MACROS
-
- #define VERSION "5.2"
-
- LONG Ceor, Csum, Crot; /* Checksums to verify archive validity. */
- BYTE new_version, openoutput, buffer[BUFSIZE];
- FILE *outfile;
-
-
- void main(argc, argv)
- int argc;
- BYTE **argv;
- {
- register BYTE openinput, error, ch, a_to_b, diagnosis, repair;
- register BYTE *infilename, *text;
- register FILE *infile;
- extern BYTE new_version, openoutput;
- extern FILE *outfile;
- #ifdef AMIGA
- extern int _bufsiz;
-
- /* Change file buffer size. */
- _bufsiz = 10240;
- #endif AMIGA
-
- error = openinput = openoutput = a_to_b = diagnosis = repair = FALSE;
- new_version = TRUE;
- infilename = NULL;
-
- /* Scan for '-' options. The rest must be file names. */
- while (!error && argc > 1 && *argv[1] == '-')
- {
- text = &argv[1][1];
- while (!error && (ch = *text++) != 0)
- {
- switch(ch)
- {
- case 'a' : /* Activate atob. */
- a_to_b = TRUE;
- break;
- case 'd' : /* Extract missing part from undamaged archive. */
- diagnosis = TRUE;
- break;
- case 'h' : /* Print help and abort execution. */
- error = TRUE;
- break;
- case 'o' : /* Use old btoa format. */
- new_version = FALSE;
- break;
- case 'r' : /* Repair damaged archive. */
- repair = TRUE;
- break;
- default : error = TRUE;
- }
- }
- argv++;
- argc--;
- }
-
- if (argc > 3)
- error = TRUE;
-
- if (error)
- printhelp();
- else
- {
- /* If file name was given, try to open file. Otherwise use stdin. */
- if (argc > 1)
- {
- infilename = argv[1];
- if ((infile = fopen_read(infilename)) == NULL)
- error = TRUE;
- else
- openinput = TRUE;
- }
- else
- infile = stdin;
- }
-
- if (!error)
- {
- /* If file name was given, try to open file. Otherwise use stdout. */
- if (argc > 2 && !diagnosis && !repair)
- {
- if ((outfile = fopen_write(argv[2])) == NULL)
- error = TRUE;
- else
- openoutput = TRUE;
- }
- else
- outfile = stdout;
- }
-
- if (!error)
- {
- if (diagnosis)
- error = producerepair(infile);
- else if (repair)
- error = performrepair(infile);
- else if (a_to_b)
- error = atob(infile);
- else
- error = btoa(infile, infilename);
- }
-
- /* Close all opened files. */
- if (openinput)
- fclose(infile);
- if (openoutput)
- fclose(outfile);
-
- if (error)
- exit(1);
- }
-
-
- BYTE btoa(infile, infilename)
- register FILE *infile;
- register BYTE *infilename;
- {
- register LONG codeword, filesize;
- register int ch1, ch2, ch3, ch4, readbytes;
- extern FILE *outfile;
- extern BYTE new_version, buffer[BUFSIZE];
- extern LONG Ceor, Csum, Crot;
-
- Ceor = Csum = Crot = 0;
-
- /* Write archive header. */
- if (new_version)
- {
- fprintf(outfile, "xbtoa5 %d %s Begin\n", MAXPERLINE,
- (infilename == NULL) ? "-" : truncname(infilename));
- }
- else
- fprintf(outfile, "xbtoa Begin\n");
-
- /* Encode entire input file. */
- filesize = 0;
- do
- {
- readbytes = fread(buffer, 1, 4, infile);
-
- if (readbytes < 4)
- {
- ch1 = (readbytes > 0) ? ((int)buffer[0] & 0xFF) : 0;
- ch2 = (readbytes > 1) ? ((int)buffer[1] & 0xFF) : 0;
- ch3 = (readbytes > 2) ? ((int)buffer[2] & 0xFF) : 0;
- ch4 = 0;
- }
- else
- {
- ch1 = (int)buffer[0] & 0xFF;
- ch2 = (int)buffer[1] & 0xFF;
- ch3 = (int)buffer[2] & 0xFF;
- ch4 = (int)buffer[3] & 0xFF;
- }
-
- if (readbytes > 0)
- {
- if (!new_version)
- {
- calcchecksum(ch1);
- calcchecksum(ch2);
- calcchecksum(ch3);
- calcchecksum(ch4);
- }
-
- codeword = (ch1 << 8) | ch2;
- codeword = (((codeword << 8) | ch3) << 8) | ch4;
- wordout(codeword);
-
- filesize += readbytes;
- }
- }
- while (readbytes == 4);
-
- asciiout(EOF); /* Flush buffer. */
-
- /* Filesize is written twice as crude cross check. */
- fprintf(outfile, "xbtoa End N %ld %lx E %lx S %lx R %lx\n",
- filesize, filesize, Ceor, Csum, Crot);
-
- return(FALSE); /* No errors discovered. */
- }
-
-
- /* Print help on how to use btoa. */
- void printhelp()
- {
- fprintf(stderr, " Btoa version %s\n", VERSION);
- fprintf(stderr, "Written by Paul Rutter, Joe Orost & Stefan Parmark.\n");
-
- fprintf(stderr, "\nUsage: btoa [-{adhor}] [input file] [output file]\n");
-
- fprintf(stderr, "\nOptions:\n");
- fprintf(stderr, "-h Shows this help list.\n");
- fprintf(stderr, "-a Use atob rather than btoa.\n");
- fprintf(stderr, "-o Use old version of btoa.\n");
- fprintf(stderr, "-d Extract repair file from diagnosis file.\n");
- fprintf(stderr, "-r Repair archive from repair file.\n");
-
- fprintf(stderr, "\nExamples:\n");
- fprintf(stderr, " btoa -h\n");
- fprintf(stderr, " btoa [input binary file] [output archive file]\n");
- fprintf(stderr, " btoa -o [input binary file] [output archive file]\n");
- fprintf(stderr, " btoa -a [input archive file] [output binary file]\n");
- fprintf(stderr, " btoa -d [undamaged archive file]\n");
- fprintf(stderr, " btoa -r [damaged archive file]\n");
- }
-
-
- #if !USE_MACROS
- /* Update file checksums. */
- void calcchecksum(ch)
- register int ch;
- {
- extern LONG Ceor, Csum, Crot;
-
- Ceor ^= ch;
- Csum += ch + 1;
-
- if (Crot & 0x80000000L)
- ch ++;
- Crot <<= 1;
- Crot += ch;
- }
- #endif !USE_MACROS
-
-
- /* Encode 4 binary bytes to 5 ascii bytes. */
- void wordout(codeword)
- register LONG codeword;
- {
- register int tmp, quote;
- extern BYTE new_version;
-
- if (codeword == 0)
- /* Encode 4 zeros as a 'z'. */
- asciiout('z');
- else if (new_version && codeword == 0x20202020)
- /* Encode 4 spaces as a 'y'. */
- asciiout('y');
- else
- {
- tmp = 0;
-
- /* Extra calculations because some machines don't support */
- /* unsigned longwords. */
- if (codeword < 0)
- {
- tmp = 32;
- codeword -= (LONG)(85L * 85 * 85 * 85 * 32);
- }
- if (codeword < 0)
- {
- tmp = 64;
- codeword -= (LONG)(85L * 85 * 85 * 85 * 32);
- }
-
- /* Write 5 ascii bytes representing 4 binary bytes. */
-
- quote = codeword / (LONG)(85L * 85 * 85 * 85);
- codeword -= quote * (LONG)(85L * 85 * 85 * 85);
- asciiout(ENCODE(quote + tmp));
-
- quote = codeword / (LONG)(85L * 85 * 85);
- codeword -= quote * (LONG)(85L * 85 * 85);
- asciiout(ENCODE(quote));
-
- quote = codeword / (LONG)(85L * 85);
- codeword -= quote * (LONG)(85L * 85);
- asciiout(ENCODE(quote));
-
- quote = (int)codeword / 85;
- codeword -= quote * 85;
- asciiout(ENCODE(quote));
-
- asciiout(ENCODE((int)codeword));
- }
- }
-
-
- /* Write ch to outfile. Write '\n' for every line. */
- void asciiout(ch)
- register int ch;
- {
- static WORD linepos = 0;
- extern FILE *outfile;
- extern LONG Csum;
- extern BYTE new_version;
-
- if (ch == EOF) /* Signal to flush buffer. */
- {
- /* Linepos == 0 means '\n' just written in asciiout(). This */
- /* avoids bug in BITNET, which changes blank line to spaces. */
- if (linepos != 0)
- {
- if (new_version)
- fputc(ENCODE(Csum % 85), outfile); /* Checksum for every line. */
- fputc('\n', outfile);
- }
- }
- else
- {
- fputc(ch, outfile);
- linepos ++;
-
- if (new_version)
- {
- calcchecksum(ch);
- if (linepos >= (MAXPERLINE-1))
- {
- fputc(ENCODE(Csum % 85), outfile); /* Checksum for every line. */
- fputc('\n', outfile);
- linepos = 0;
- }
- }
- else /* Old version */
- if (linepos >= MAXPERLINE)
- {
- fputc('\n', outfile);
- linepos = 0;
- }
-
- }
- }
-
-
- /* Remove paths from a file name. */
- BYTE *truncname(name)
- register BYTE *name;
- {
- register BYTE ch, *newname;
-
- newname = name;
- while ((ch = *name++) != 0)
- if (ch == '/' || ch == ':')
- newname = name;
-
- return(newname);
- }
- SHAR_EOF
- cat << \SHAR_EOF > btoa.h
- /* btoa.h */
-
- #define MAXPERLINE 78
- #define BUFSIZE 100
- #define TRUE 1
- #define FALSE 0
- #define USE_MACROS TRUE
-
- #define BYTE char
- #define WORD short
- #define LONG long
-
- #define ENCODE(ch) ( (int) ((ch) + '!') )
- #define DECODE(ch) ( (int) ((ch) - '!') )
-
- struct Diagnosis
- {
- LONG startpos, endpos; /* Line before and after erroneous area */
- struct Diagnosis *next, *last;
- };
-
-
- /*
- Following functions have been converted to macros:
- calcchecksum()
- */
-
-
- #if LATTICE /* Prototypes for Lattice C */
-
- void asciiout(int), exit(int),
- intodiagnosislist(struct Diagnosis *, LONG, LONG),
- outdiagnosislist(struct Diagnosis *, LONG *, LONG *), printhelp(void),
- producediagnosis(struct Diagnosis *, FILE *), wordout(LONG);
-
- BYTE atob(FILE *), btoa(FILE *, BYTE *), copyfile(FILE *, FILE *, BYTE *),
- decode_line(BYTE *, int), new_decodefile(FILE *, LONG *, LONG, int),
- old_decodefile(FILE *, LONG *), performrepair(FILE *),
- producerepair(FILE *), readbuffer(BYTE *, BYTE *, FILE *),
- *truncname(BYTE *);
-
- int nextbyte(FILE *);
-
- FILE *fopen_read(BYTE *), *fopen_write(BYTE *);
-
- #if USE_MACROS
- void calcchecksum(int);
- #else
- #include "chksum.h"
- #endif USE_MACROS
-
- #else !LATTICE /* For compilers which don't know about prototypes. */
-
- void asciiout(), exit(), intodiagnosislist(), outdiagnosislist(),
- printhelp(), producediagnosis(), wordout();
-
- BYTE atob(), btoa(), copyfile(), decode_line(), new_decodefile(),
- old_decodefile(), performrepair(), producerepair(), readbuffer(),
- *truncname();
-
- int nextbyte();
-
- FILE *fopen_read(), *fopen_write();
-
- #if USE_MACROS
- void calcchecksum();
- #else
- #include "chksum.h"
- #endif USE_MACROS
-
- #endif LATTICE
- SHAR_EOF
- cat << \SHAR_EOF > chksum.h
- /* chksum.h */
- /* calcchecksum() was converted to a macro for effectivity reasons. */
- /* Don't (!!) give it an argument that has to be evaluated. This */
- /* is guaranteed to slow it down. */
-
- /* Update file checksums. */
-
- #define calcchecksum(ch) \
- { \
- extern LONG Ceor, Csum, Crot; \
- \
- Ceor ^= ch; \
- Csum += ch + 1; \
- \
- if (Crot & 0x80000000L) \
- { \
- Crot <<= 1; \
- Crot ++; \
- } \
- else \
- Crot <<= 1; \
- \
- Crot += ch; \
- }
- SHAR_EOF
- cat << \SHAR_EOF > repair.c
- /* repair.c */
-
- /* Written by Stefan Parmark. */
-
- #include <stdio.h>
- #ifdef AMIGA
- #include <stdlib.h>
- #include <string.h>
- #endif AMIGA
-
- #include "btoa.h"
-
- /* File names. */
- BYTE *diagnosisname = "btoa.dia";
- BYTE *repairname = "btoa.rep";
- BYTE *repairedname = "btoa.rdy";
-
- /* File headers. */
- BYTE *diagnosisheader = "xdiagnosis\n";
- BYTE *repairheader = "xrepair\n";
-
-
- /* Produce diagnosis file from diagnoses records created by atob(). */
- /* It contains the lines immediately before and after the error */
- /* sequence. */
- void producediagnosis(diagnosislist, infile)
- register struct Diagnosis *diagnosislist;
- register FILE *infile;
- {
- register FILE *diagnosisfile;
- LONG startpos, endpos;
- register LONG currentpos;
- extern BYTE *diagnosisname, *diagnosisheader, buffer[BUFSIZE];
-
- currentpos = ftell(infile);
-
- if ((diagnosisfile = fopen_write(diagnosisname)) != NULL)
- {
- fprintf(stderr, "btoa: Diagnosis output to '%s'.\n", diagnosisname);
-
- fputs(diagnosisheader, diagnosisfile);
- do
- {
- /* Extract startpos & endpos from diagnosislist. */
- outdiagnosislist(diagnosislist, &startpos, &endpos);
-
- if (startpos != -1)
- {
- /* Print line before error. */
- fseek(infile, startpos, 0);
- fgets(buffer, BUFSIZE, infile);
- fputs(buffer, diagnosisfile);
-
- /* Print line after error. */
- fseek(infile, endpos, 0);
- fgets(buffer, BUFSIZE, infile);
- fputs(buffer, diagnosisfile);
- }
- }
- while (startpos != -1);
- fputs(diagnosisheader, diagnosisfile);
-
- fclose(diagnosisfile);
- }
-
- /* Move file pointer to where it was when we entered. */
- fseek(infile, currentpos, 0);
- }
-
-
- /* Insert two file positions into diagnosislist. */
- void intodiagnosislist(diagnosislist, startpos, endpos)
- register struct Diagnosis *diagnosislist;
- register LONG startpos, endpos;
- {
- register struct Diagnosis *diagnosisitem, *lastitem;
-
- diagnosisitem = (struct Diagnosis *)malloc(sizeof(struct Diagnosis));
- diagnosisitem->startpos = startpos;
- diagnosisitem->endpos = endpos;
- diagnosisitem->next = NULL;
-
- if ((lastitem = diagnosislist->last) == NULL) /* List is empty */
- diagnosislist->next = diagnosislist->last = diagnosisitem;
- else
- {
- if (lastitem->endpos >= startpos)
- {
- lastitem->endpos = endpos;
- free((BYTE *) diagnosisitem);
- }
- else
- {
- lastitem->next = diagnosisitem;
- diagnosislist->last = diagnosisitem;
- }
- }
- }
-
-
- /* Extract two file positions from diagnosislist. */
- void outdiagnosislist(diagnosislist, startpos, endpos)
- register struct Diagnosis *diagnosislist;
- LONG *startpos, *endpos;
- {
- register struct Diagnosis *diagnosisitem;
-
- if ((diagnosisitem = diagnosislist->next) == NULL) /* List is empty */
- *startpos = *endpos = -1;
- else
- {
- *startpos = diagnosisitem->startpos;
- *endpos = diagnosisitem->endpos;
-
- diagnosislist->next = diagnosisitem->next;
- free((BYTE *)diagnosisitem);
- if (diagnosislist->next == NULL)
- diagnosislist->last = NULL;
- }
- }
-
-
- /* Copy infile to outfile until searchstring is found. If outfile */
- /* is NULL nothing will be written. */
- BYTE copyfile(infile, outfile, searchstring)
- register FILE *infile, *outfile;
- register BYTE *searchstring;
- {
- register BYTE stop, error;
- static BYTE copybuffer[BUFSIZE];
-
- stop = error = FALSE;
- while (!(stop || error))
- if (readbuffer(copybuffer, "archive", infile))
- error = TRUE;
- else
- {
- if (outfile != NULL)
- fputs(copybuffer, outfile);
- if (strcmp(copybuffer, searchstring) == 0)
- stop = TRUE;
- }
-
- return(error);
- }
-
-
- /* Read a line from infile into buffer. Returns TRUE if */
- /* end-of-file has been reached. */
- BYTE readbuffer(buffer, errormsg, infile)
- register BYTE *buffer, *errormsg;
- register FILE *infile;
- {
- register BYTE error;
-
- error = FALSE;
- if (fgets(buffer, BUFSIZE, infile) == NULL)
- {
- fprintf(stderr, "btoa: Unexpected end of %s file.\n", errormsg);
- error = TRUE;
- }
-
- return(error);
- }
-
-
- FILE *fopen_read(filename)
- register BYTE *filename;
- {
- register FILE *infile;
-
- if ((infile = fopen(filename, "r")) == NULL)
- fprintf(stderr, "btoa: Can't open '%s' for input.\n", filename);
-
- return(infile);
- }
-
-
- FILE *fopen_write(filename)
- register BYTE *filename;
- {
- register FILE *outfile;
-
- if ((outfile = fopen(filename, "w")) == NULL)
- fprintf(stderr, "btoa: Can't open '%s' for output.\n", filename);
-
- return(outfile);
- }
-
-
- /* Extract lines from original archive to fix the damaged one. */
- BYTE producerepair(infile)
- register FILE *infile;
- {
- register FILE *repairfile, *diagnosisfile;
- register BYTE error, stop;
- static BYTE *errormsg = "diagnosis";
- extern BYTE *diagnosisname, *diagnosisheader, *repairname, *repairheader,
- buffer[BUFSIZE];
-
- error = FALSE;
- diagnosisfile = repairfile = NULL;
-
- fprintf(stderr, "btoa: Repair output to '%s'.\n", repairname);
- if ((diagnosisfile = fopen_read(diagnosisname)) == NULL)
- error = TRUE;
- else if ((repairfile = fopen_write(repairname)) == NULL)
- {
- fclose(diagnosisfile);
- diagnosisfile = NULL;
- error = TRUE;
- }
- else
- {
- /* Read until header is found. This makes it possible to */
- /* have junk before the header, such as an article header. */
- do
- {
- if (readbuffer(buffer, errormsg, diagnosisfile))
- error = TRUE;
- }
- while (!error && strcmp(buffer, diagnosisheader) != 0);
- fputs(repairheader, repairfile);
- }
-
- stop = FALSE;
- while (!(error || stop))
- {
- /* Loop until header is found again. */
-
- if (readbuffer(buffer, errormsg, diagnosisfile))
- error = TRUE;
- else if (strcmp(buffer, diagnosisheader) == 0)
- stop = TRUE;
- else
- {
- /* Read until line before error is found. */
- error = copyfile(infile, NULL, buffer);
- if (!error)
- {
- /* Print line before error. */
- fputs(buffer, repairfile);
-
- if (readbuffer(buffer, errormsg, diagnosisfile))
- error = TRUE;
- else
- {
- /* Print line after error */
- fputs(buffer, repairfile);
- /* Copy infile to repairfile until line after error */
- error = copyfile(infile, repairfile, buffer);
- }
- }
- }
- }
-
- if (!error)
- fputs(repairheader, repairfile);
-
- if (repairfile != NULL)
- fclose(repairfile);
- if (diagnosisfile != NULL)
- fclose(diagnosisfile);
-
- return(error);
- }
-
-
- /* Repair damaged archive from repair file. */
- BYTE performrepair(infile)
- register FILE *infile;
- {
- register FILE *repairfile, *outfile;
- register BYTE error, stop;
- static BYTE *errormsg = "repair";
- extern BYTE *repairname, *repairedname, *repairheader, buffer[BUFSIZE];
-
- error = FALSE;
- repairfile = outfile = NULL;
-
- if ((repairfile = fopen_read(repairname)) == NULL)
- error = TRUE;
- else if ((outfile = fopen_write(repairedname)) == NULL)
- {
- fclose(repairfile);
- repairfile = NULL;
- error = TRUE;
- }
- else
- {
- fprintf(stderr, "btoa: Repaired archive written to '%s'.\n", repairedname);
-
- /* Read until header is found. */
- do
- {
- if (readbuffer(buffer, errormsg, repairfile))
- error = TRUE;
- }
- while (!error && strcmp(buffer, repairheader) != 0);
- }
-
- stop = FALSE;
- while (!(error || stop))
- {
- /* Loop until header is found. */
-
- if (readbuffer(buffer, errormsg, repairfile))
- error = TRUE;
- else if (strcmp(buffer, repairheader) == 0)
- stop = TRUE;
- else
- {
- /* Read and write until line before error. */
- error = copyfile(infile, outfile, buffer);
- if (!error)
- if (readbuffer(buffer, errormsg, repairfile))
- error = TRUE;
- else
- {
- /* Read and write until line after error. */
- error = copyfile(repairfile, outfile, buffer);
- /* Skip until line after error */
- copyfile(infile, NULL, buffer);
- }
- }
- }
-
- if (!error) /* Write rest of archive. */
- while (fgets(buffer, BUFSIZE, infile) != NULL)
- fputs(buffer, outfile);
-
- if (outfile != NULL)
- fclose(outfile);
- if (repairfile != NULL)
- fclose(repairfile);
-
- return(error);
- }
- SHAR_EOF
- # End of shell archive
- exit 0
- --
- Please send comp.sources.unix-related mail to rsalz@uunet.uu.net.
-