home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / unix / volume18 / btoa < prev    next >
Text File  |  1989-04-19  |  41KB  |  1,597 lines

  1. Subject:  v18i108:  Btoa v5.2 -- binary/ascii packer with repair features
  2. Newsgroups: comp.sources.unix
  3. Sender: sources
  4. Approved: rsalz@uunet.UU.NET
  5.  
  6. Submitted-by: d84sp@efd.lth.se
  7. Posting-number: Volume 18, Issue 108
  8. Archive-name: btoa
  9.  
  10. Btoa was originally part of the Compress4.0 distribution; it's now
  11. broken out into a separate package.  It converts between binary
  12. and ASCII (printable) files.
  13.  
  14. This is version 5.2 of btoa for distribution to comp.sources.unix.
  15.  
  16. Besides expanding binary files by only 25% rather than
  17. uuencode's 33%, it now also has got the ability to
  18. REPAIR damaged files, without having to retransmit the
  19. entire file.
  20.  
  21. It has worked successfully when transferring files between
  22. a Sun-3 and an Amiga 1000.
  23.  
  24. Below is the compressed and uuencoded shar archive.
  25. --
  26. Stefan Parmark, Lund Institute of Technology, Sweden
  27. #    This is a shell archive.
  28. #    Remove everything above and including the cut line.
  29. #    Then run the rest of the file through sh.
  30. #----cut here-----cut here-----cut here-----cut here----#
  31. #!/bin/sh
  32. # shar:    Shell Archiver
  33. #    Run the following text with /bin/sh to create:
  34. #    README
  35. #    btoa.1
  36. #    btoa.doc
  37. #    Makefile.amiga_lattice
  38. #    Makefile.sun
  39. #    atob.c
  40. #    btoa.c
  41. #    btoa.h
  42. #    chksum.h
  43. #    repair.c
  44. # This archive created: Tue Feb 21 17:45:16 1989
  45. cat << \SHAR_EOF > README
  46.              BTOA version 5.2
  47.             ------------------
  48. Written by Paul Rutter, Joe Orost & Stefan Parmark.
  49.  
  50.  
  51. Btoa was created from atob/btoa which were submitted to Usenet
  52. by Paul E. Rutter. Atob and btoa has now been merged into one
  53. program, now refered to as btoa.
  54.  
  55. Btoa converts 4 binary characters to 5 ascii ones, causing a 25%
  56. expansion. Spaces will not be used, which should make it safe
  57. to send files over Usenet without risking that blanks become
  58. tabs. 
  59.  
  60. Decoding, which previously was done with atob, is now an option
  61. of btoa. See the manual for details.
  62.  
  63. One of the drawbacks with the previous version of btoa was that,
  64. if there was an error in the file, atob only stated so, but gave
  65. no clue to its location. It used a checksum covering the
  66. total file, making it impossible to detect where the error was.
  67. I added a single-byte checksum for each row.
  68.  
  69. Further, the file contained no information about the name of the
  70. output file. Rather, stdout was used. Version 5.0 has the feature
  71. to name the file contents. It can be turned off by reading data
  72. from stdin.
  73.  
  74. A totally new feature is the ability to mend corrupted archives.
  75. This can be done as long as the header and last line are OK.
  76. It detects bad lines, informs the remote computer about this,
  77. which retransmits these lines in a special file, which btoa uses
  78. to repair the archive. See the manual for more details.
  79.  
  80. Btoa uses characters between '!' and 'u'. Special characters are
  81. 'z' meaning 4 consecutive zeros, 'y' meaning 4 spaces, and
  82. 'x' as an end-of-archive mark. The 'y' was added be me, and will
  83. not be recognized by the old version.
  84.  
  85. Old btoa encoding is still possible, as an extra option (see the
  86. manual). When decoding, btoa can tell if it's the old or new btoa
  87. format by looking at the header.
  88.  
  89. I removed the feature to exit with no output if there was en error
  90. in the archive. This was done by using a temporary file for
  91. storage. This is not a good idea for micro computer folks like
  92. me, with limited storage possibilities. I hope all realize that
  93. you shouldn't run a file that was created from a corrupted archive.
  94.  
  95. Most of the code was rewritten in order to make it execute faster.
  96. Special efforts have been made to optimize the atob part.
  97. Measured speeds are 30 kbyte/s on a Sun-3 and 4 kbyte/s on an
  98. Amiga. On the Amiga I used the VD0: recoverable RAM disk without
  99. FFS.
  100.  
  101. I have tested btoa on an Amiga 1000 and a Sun-3, and it has
  102. worked very well. If you find it doesn't work on your favourite
  103. computer, drop me a note to one of the addresses below, preferably
  104. the top one, and I'll see what I can do. Also, if you port and/or
  105. improve it, please send me the diffs, and I'll include them in the
  106. next release.
  107.  
  108. ---------------------- DISCLAIMER -------------------------------
  109.  
  110. I assume no responsibility for the use of btoa. It should work OK,
  111. and I have included lots of tests to make sure that files open,
  112. end-of-file is detected, etc.
  113.  
  114. Btoa is in the public domain. You may use it, give it away, and
  115. make improvements, as long as the names of the developers are
  116. mentioned and you don't use it to earn money. It may NOT be used
  117. commercially without my permission.
  118.  
  119. /Stefan Parmark
  120.         d84sp@efd.lth.se
  121.         d84spa@rigel.sunet.se
  122. SHAR_EOF
  123. cat << \SHAR_EOF > btoa.1
  124. .TH BTOA 1 "21 February 1989"
  125. .| btoa version 5.2
  126. .SH NAME
  127. btoa - encode/decode binary to printable ASCII
  128. .SH SYNOPSIS
  129. btoa [-adhor] [input filename] [output filename]
  130. .SH DESCRIPTION
  131. .I Btoa
  132. is a filter which reads binary bytes from the input file and
  133. generates printable ASCII characters on the output file. It attaches
  134. a header and a checksum to the archive. It can also reverse this,
  135. creating a binary file from the archive.
  136. .LP
  137. Since last version of
  138. .I btoa/atob,
  139. several new features have been
  140. added. The most obvious one is that
  141. .I atob
  142. has been integrated
  143. with
  144. .I btoa.
  145. They are now the same program which is called with
  146. different arguments. Another is the ability to repair damaged
  147. archives.
  148. .LP
  149. The new version is compatible with the old version, that is,
  150. it can still encode and decode old btoa files.
  151. .LP
  152. .I Btoa
  153. has an option to decode the archive, restoring the binary bytes.
  154. It strips the input file until it finds a valid header, and continues
  155. decoding until the end mark is found. It recognices both old- and
  156. new-style headers, and can decode both. It is possible to leave out
  157. the destination name when decoding new-style archives, because the
  158. name is stored in the header. Entering a name will override the
  159. autonaming function.
  160. .LP
  161. It is possible to leave out the file names and redirect stdin and
  162. stdout with '<' and '>' to the desired files. This is to maintain
  163. compatibility with earlier versions of
  164. .I btoa.
  165. .LP
  166. .I Btoa
  167. now adds a single byte checksum to each row in the archive.
  168. When an error is found, diagnosis automatically starts and produces
  169. a diagnosis file which can be used to extract the damaged part from
  170. an errorfree archive. The extracted part can then be used to correct
  171. the damaged archive.
  172. .I Btoa
  173. has options to perform the reparation
  174. automatically. This is especially useful when downloading data
  175. converted to text files, and occasionally finding that an archive
  176. file of considerable size turns is corrupted.
  177. .SH FEATURES
  178. .I Btoa
  179. encodes 4 binary bytes into 5 characters, expanding the file by
  180. 25%. As a special case 4 zeroes will be encoded as 'z' and 4
  181. spaces as 'y'. This makes it possible to compress the archive a bit.
  182. .SH OPTIONS
  183. .IP -h
  184. Shows help on 
  185. .I btoa.
  186. .LP
  187. .IP-a
  188. Switches to
  189. .I atob
  190. (decoding) mode.
  191. .LP
  192. .IP -o
  193. Switches to old version of
  194. .I btoa.
  195. .LP
  196. .IP -d
  197. Extracts repair file from diagnosis file. This assumes that
  198. an undamaged version of the archive and a file called
  199. 'btoa.dia' is present.
  200. .LP
  201. .IP-r
  202. Repairs the damaged archive. A file named 'btoa.rep' must
  203. be present for this to work.
  204. .SH EXAMPLES
  205. Below follows a description of a normal repair session. Lines
  206. beginning with 'Local>' were typed on the computer to which the
  207. file was downloaded. Accordingly, lines typed on the connected
  208. computer will begin with 'Remote>'. Sending a file to the other
  209. computer will be noted as 'transmit file'.
  210. .LP
  211. A normal repairing procedure is as follows:
  212. Local> btoa -a file.btoa
  213. btoa: Bad checksum on line 2648.
  214. btoa: Starting diagnosis.
  215. btoa: Diagnosis output to 'btoa.dia'.
  216. Local> transmit btoa.dia
  217. .LP
  218. Remote> btoa -d file.btoa
  219. btoa: Repair output to 'btoa.rep'.
  220. Remote> transmit btoa.rep
  221. .LP
  222. Local> btoa -a btoa.rep
  223. btoa: Repaired archive written to 'btoa.rdy'.
  224. .LP
  225. You can now erase file.btoa and decode btoa.rdy using 'btoa -a btoa.rdy'.
  226. .SH AUTHORS
  227. Paul Rutter  Joe Orost  Stefan Parmark
  228. .SH KNOWN BUGS
  229. .I Btoa
  230. will not work properly unless the input is a true file or a
  231. redirected one. This is because file positions are collected during
  232. diagnosis for later reference when producing the diagnosis file.
  233. The bug is actually in fseek() which only can reposition 'real' files.
  234. .LP
  235. Send bug reports to d84sp@efd.lth.se (Stefan Parmark).
  236. SHAR_EOF
  237. cat << \SHAR_EOF > btoa.doc
  238.  
  239.  
  240.  
  241. BTOA(1)                  USER COMMANDS                    BTOA(1)
  242.  
  243.  
  244.  
  245. NAME
  246.      btoa - encode/decode binary to printable ASCII
  247.  
  248. SYNOPSIS
  249.      btoa -adhor input filename output filename
  250.  
  251. DESCRIPTION
  252.      Btoa is a filter which reads binary  bytes  from  the  input
  253.      file  and generates printable ASCII characters on the output
  254.      file. It attaches a header and a checksum to the archive. It
  255.      can  also  reverse  this,  creating  a  binary file from the
  256.      archive.
  257.  
  258.      Since last version of btoa/atob, several new  features  have
  259.      been  added.  The  most  obvious  one  is that atob has been
  260.      integrated with btoa. They are now the same program which is
  261.      called  with  different arguments. Another is the ability to
  262.      repair damaged archives.
  263.  
  264.      The new version is compatible with the old version, that is,
  265.      it can still encode and decode old btoa files.
  266.  
  267.      Btoa has an option to  decode  the  archive,  restoring  the
  268.      binary  bytes.   It  strips  the input file until it finds a
  269.      valid header, and continues decoding until the end  mark  is
  270.      found.  It  recognices  both old- and new-style headers, and
  271.      can decode both. It is possible to leave out the destination
  272.      name  when  decoding new-style archives, because the name is
  273.      stored in the header. Entering  a  name  will  override  the
  274.      autonaming function.
  275.  
  276.      It is possible to leave out  the  file  names  and  redirect
  277.      stdin and stdout with '<' and '>' to the desired files. This
  278.      is to maintain compatibility with earlier versions of btoa.
  279.  
  280.      Btoa now adds a single byte checksum  to  each  row  in  the
  281.      archive.   When  an  error is found, diagnosis automatically
  282.      starts and produces a diagnosis file which can  be  used  to
  283.      extract  the  damaged  part  from  an errorfree archive. The
  284.      extracted part can then  be  used  to  correct  the  damaged
  285.      archive.    Btoa  has  options  to  perform  the  reparation
  286.      automatically. This is especially  useful  when  downloading
  287.      data  converted to text files, and occasionally finding that
  288.      an archive file of considerable size turns is corrupted.
  289.  
  290. FEATURES
  291.      Btoa encodes 4 binary bytes into 5 characters, expanding the
  292.      file  by  25%. As a special case 4 zeroes will be encoded as
  293.      'z' and 4 spaces as 'y'. This makes it possible to  compress
  294.      the archive a bit.
  295.  
  296.  
  297.  
  298.  
  299.  
  300.                   Last change: 21 February 1989                 1
  301.  
  302.  
  303.  
  304.  
  305.  
  306.  
  307. BTOA(1)                  USER COMMANDS                    BTOA(1)
  308.  
  309.  
  310.  
  311. OPTIONS
  312.      -h   Shows help on btoa.
  313.  
  314.      -a   Switches to atob (decoding) mode.
  315.  
  316.      -o   Switches to old version of btoa.
  317.  
  318.      -d   Extracts repair file from diagnosis file. This  assumes
  319.           that  an  undamaged  version  of the archive and a file
  320.           called
  321.  
  322.      -r   Repairs the damaged archive. A  file  named  'btoa.rep'
  323.           must be present for this to work.
  324.  
  325. EXAMPLES
  326.      Below follows a description  of  a  normal  repair  session.
  327.      Lines  beginning with 'Local>' were typed on the computer to
  328.      which the file was downloaded. Accordingly, lines  typed  on
  329.      the  connected computer will begin with 'Remote>'. Sending a
  330.      file to the other computer will be noted as 'transmit file'.
  331.  
  332.      A normal repairing procedure is as follows: Local>  btoa  -a
  333.      file.btoa  btoa:  Bad checksum on line 2648.  btoa: Starting
  334.      diagnosis.  btoa: Diagnosis output  to  'btoa.dia'.   Local>
  335.      transmit btoa.dia
  336.  
  337.      Remote> btoa -d file.btoa btoa: Repair output to 'btoa.rep'.
  338.      Remote> transmit btoa.rep
  339.  
  340.      Local> btoa -a btoa.rep btoa: Repaired  archive  written  to
  341.      'btoa.rdy'.
  342.  
  343.      You can now erase file.btoa and decode btoa.rdy using  'btoa
  344.      -a btoa.rdy'.
  345.  
  346. AUTHORS
  347.      Paul Rutter  Joe Orost  Stefan Parmark
  348.  
  349. KNOWN BUGS
  350.      Btoa will not work properly unless the input is a true  file
  351.      or a redirected one. This is because file positions are col-
  352.      lected during diagnosis for later reference  when  producing
  353.      the  diagnosis  file.   The bug is actually in fseek() which
  354.      only can reposition 'real' files.
  355.  
  356.      Send bug reports to d84sp@efd.lth.se (Stefan Parmark).
  357.  
  358.  
  359.  
  360.  
  361.  
  362.  
  363.  
  364.  
  365.  
  366.                   Last change: 21 February 1989                 2
  367.  
  368.  
  369.  
  370. SHAR_EOF
  371. cat << \SHAR_EOF > Makefile.amiga_lattice
  372. OBJS     = btoa.o atob.o repair.o
  373. CFLAGS   = -cef -dLATTICE -v -w
  374.  
  375. btoa     : $(OBJS)
  376.            blink lib:c.o $(OBJS) TO btoa LIB lib:lcs.lib SC SD ND
  377.  
  378. atob.o   : atob.c btoa.h chksum.h
  379. btoa.o   : btoa.c btoa.h chksum.h
  380. repair.o : repair.c btoa.h
  381. SHAR_EOF
  382. cat << \SHAR_EOF > Makefile.sun
  383. OBJS     = btoa.o atob.o repair.o
  384.  
  385. btoa     : $(OBJS)
  386.            cc -O $(OBJS) -o btoa
  387.  
  388. btoa.o   : btoa.c btoa.h chksum.h
  389.            cc -c -O btoa.c
  390. atob.o   : atob.c btoa.h chksum.h
  391.            cc -c -O atob.c
  392. repair.o : repair.c btoa.h
  393.            cc -c -O repair.c
  394. SHAR_EOF
  395. cat << \SHAR_EOF > atob.c
  396. /* atob.c */
  397.  
  398. /* Written by Paul Rutter, Joe Orost & Stefan Parmark. */
  399.  
  400. #include <stdio.h>
  401. #ifdef AMIGA
  402. #include <stdlib.h>
  403. #include <string.h>
  404. #endif AMIGA
  405.  
  406. #include "btoa.h"
  407. #if USE_MACROS
  408. #include "chksum.h"
  409. #endif USE_MACROS
  410.  
  411.  
  412. BYTE atob(infile)
  413. register FILE *infile;
  414. {
  415.   register BYTE error;
  416.   register LONG filepos;
  417.   int maxperline;
  418.   LONG n1, n2, oeor, osum, orot, lastline;
  419.   static BYTE outfilename[BUFSIZE];
  420.   extern LONG Ceor, Csum, Crot;
  421.   extern FILE *outfile;
  422.   extern BYTE new_version, openoutput, buffer[BUFSIZE];
  423.  
  424.   error = FALSE;
  425.  
  426.   /* Search for archive header. */
  427.   do
  428.   {
  429.     filepos = ftell(infile);
  430.  
  431.     if (readbuffer(buffer, "archive", infile))
  432.       error = TRUE;
  433.   }
  434.   while (!(error || strncmp(buffer, "xbtoa", 5) == 0));
  435.  
  436.   if (!error)
  437.     if (strcmp(buffer, "xbtoa Begin\n") == 0)
  438.     {
  439.       new_version = FALSE;
  440.       fprintf(stderr, "btoa: Old btoa format.\n");
  441.     }
  442.     else if (sscanf(buffer, "xbtoa5 %d %s Begin\n", &maxperline, outfilename) == 2)
  443.     {
  444.       new_version = TRUE;
  445.       /* Naming a file overrides the read-name-from-file function. */
  446.       if (!openoutput && strcmp(outfilename, "-") != 0)
  447.         if ((outfile = fopen_write(outfilename)) == NULL)
  448.           error = TRUE;
  449.         else
  450.           openoutput = TRUE;
  451.     }
  452.     else
  453.     {
  454.       fprintf(stderr, "btoa: Illegal archive header.\n");
  455.       error = TRUE;
  456.     }
  457.  
  458.   if (!error)
  459.   {
  460.     Ceor = Csum = Crot = 0;
  461.  
  462.     if (new_version)
  463.       error = new_decodefile(infile, &lastline, filepos, maxperline);
  464.     else
  465.       error = old_decodefile(infile, &lastline);
  466.   }
  467.  
  468.   if (!error)
  469.   {
  470.     if (sscanf(buffer, "xbtoa End N %ld %lx E %lx S %lx R %lx\n",
  471.         &n1, &n2, &oeor, &osum, &orot) != 5)
  472.     {
  473.       fprintf(stderr, "btoa: Bad format on line %ld. Can't repair file.\n",
  474.           lastline);
  475.       error = TRUE;
  476.     }
  477.     else if ((n1 != n2) || (oeor != Ceor) || (osum != Csum) || (orot != Crot))
  478.     {
  479.       fprintf(stderr, "btoa: Bad file checksum. Can't repair file.\n");
  480.       error = TRUE;
  481.     }
  482.     else
  483.       /* Flush last characters. */
  484.       decode_line(NULL, (int) ((n1 - 1) & 0x03));
  485.   }
  486.  
  487.   return(error);
  488. }
  489.  
  490.  
  491. /* Peek at the next byte without moving the file pointer. */
  492. int nextbyte(infile)
  493. register FILE *infile;
  494. {
  495.   register int ch;
  496.   register LONG filepos;
  497.  
  498.   filepos = ftell(infile);
  499.   ch = fgetc(infile);
  500.   fseek(infile, filepos, 0);
  501.  
  502.   return(ch);
  503. }
  504.  
  505.  
  506. BYTE new_decodefile(infile, lastline, filepos, maxperline)
  507. register FILE *infile;
  508. LONG *lastline, filepos;
  509. int maxperline;
  510. {
  511.   register int length;
  512.   int ch;
  513.   register BYTE stop, error, newerror, errorstart;
  514.   register LONG line, prevfilepos, startpos;
  515.   struct Diagnosis diagnosislist;
  516.   extern LONG Csum;
  517.   extern BYTE buffer[BUFSIZE];
  518.  
  519.   error = FALSE;
  520.  
  521.   line = 1;  /* Current line number. */
  522.  
  523.   /* A sequence of errors is indicated by errorstart. When it */
  524.   /* changes from TRUE to FALSE a diagnosis record will be    */
  525.   /* generated.                                               */
  526.   stop = errorstart = FALSE;
  527.  
  528.   /* File position of the line before the error sequence.     */
  529.   /* That is the last correct line.                           */
  530.   startpos = 0;
  531.  
  532.   while (!stop)
  533.   {
  534.     prevfilepos = filepos;
  535.     filepos = ftell(infile);
  536.  
  537.     /* Newerror indicates an error on the current line. */
  538.     newerror = FALSE;
  539.  
  540.     line++;
  541.     if (readbuffer(buffer, "archive", infile))
  542.       newerror = stop = TRUE;
  543.     else if (buffer[0] == 'x')  /* End of archive found. */
  544.       stop = TRUE;
  545.     else if ((length = strlen(buffer) - 1) != maxperline ||
  546.              buffer[length] != '\n')
  547.     {
  548.       /* If last character wasn't end-of-line, then we */
  549.       /* have to read until it is found.               */
  550.       if (buffer[length] != '\n')
  551.       {
  552.         newerror = TRUE;
  553.         while ((ch = fgetc(infile)) != EOF && (BYTE)ch != '\n')
  554.           ;
  555.         if (ch == EOF)
  556.           stop = TRUE;
  557.       }
  558.       else if (length > maxperline || nextbyte(infile) != 'x')
  559.       {
  560.         newerror = TRUE;
  561.         Csum = DECODE(buffer[length - 1]);  /* Make Csum correct (modulo 85). */
  562.       }
  563.  
  564.       if (newerror)
  565.         fprintf(stderr, "btoa: Bad line length on line %ld.\n", line);
  566.     }
  567.  
  568.     if (!(newerror || stop))
  569.     {
  570.       if (decode_line(buffer, length - 1))
  571.       {
  572.         if (!error)
  573.           fprintf(stderr, "btoa: Bad character on line %ld.\n", line);
  574.         newerror = TRUE;
  575.       }
  576.  
  577.       /* Examine checksum. */
  578.       if ((ch = buffer[length - 1]) == ENCODE(Csum % 85))
  579.       {
  580.         if (errorstart)
  581.         {
  582.           intodiagnosislist(&diagnosislist, startpos, filepos);
  583.           errorstart = FALSE;
  584.         }
  585.       }
  586.       else
  587.       {
  588.         newerror = TRUE;
  589.         fprintf(stderr, "btoa: Bad checksum on line %ld.\n", line);
  590.         Csum = DECODE(ch);  /* Make Csum correct (modulo 85). */
  591.       }
  592.     }
  593.  
  594.     if (newerror)
  595.     {
  596.       if (!error)
  597.       {
  598.         fprintf(stderr, "btoa: Starting diagnosis.\n");
  599.         diagnosislist.next = diagnosislist.last = NULL;
  600.       }
  601.  
  602.       error = TRUE;
  603.       if (!errorstart)
  604.       {
  605.         errorstart = TRUE;
  606.         startpos = prevfilepos;
  607.       }
  608.     }
  609.   }
  610.  
  611.   if (error)
  612.   {
  613.     if (errorstart)
  614.       intodiagnosislist(&diagnosislist, startpos, filepos);
  615.     producediagnosis(&diagnosislist, infile);
  616.   }
  617.  
  618.   *lastline = line;
  619.  
  620.   return(error);
  621. }
  622.  
  623.  
  624.  
  625. BYTE old_decodefile(infile, lastline)
  626. register FILE *infile;
  627. LONG *lastline;
  628. {
  629.   register int length;
  630.   register BYTE stop, error;
  631.   register LONG line;
  632.   extern BYTE buffer[BUFSIZE];
  633.  
  634.   error = FALSE;
  635.  
  636.   line = 1;
  637.   stop = FALSE;
  638.   while (!stop)
  639.   {
  640.     line ++;
  641.  
  642.     if (readbuffer(buffer, "archive", infile))
  643.       error = stop = TRUE;
  644.     else if (buffer[0] == 'x')  /* End of archive found. */
  645.       stop = TRUE;
  646.     else
  647.     {
  648.       length = strlen(buffer) - 1;
  649.       if (buffer[length] != '\n')
  650.         error = stop = TRUE;  /* The line was longer than the buffer. */
  651.     }
  652.  
  653.     if (!stop)
  654.     {
  655.       if (decode_line(buffer, length))
  656.       {
  657.         fprintf(stderr, "btoa: Bad character on line %ld.\n", line);
  658.         error = stop = TRUE;
  659.       }
  660.     }
  661.   }
  662.  
  663.   *lastline = line;
  664.  
  665.   return(error);
  666. }
  667.  
  668.  
  669. BYTE decode_line(buffer, length)
  670. register BYTE *buffer;
  671. register int length;
  672. {
  673.   register int ch;
  674.   register BYTE error;
  675.   register LONG tmp_codeword;
  676.   extern BYTE new_version;
  677.   extern FILE *outfile;
  678.   static LONG codeword;
  679.   static int ch1, ch2, ch3, ch4;
  680.   static BYTE bytecount = 0;
  681.  
  682.   error = FALSE;
  683.  
  684.   if (buffer == NULL)  /* Flush last characters. */
  685.   {
  686.     if (bytecount > 0)
  687.     {
  688.       fputc(ch1, outfile);
  689.       if (length > 0)
  690.         fputc(ch2, outfile);
  691.       if (length > 1)
  692.         fputc(ch3, outfile);
  693.       if (length > 2)
  694.         fputc(ch4, outfile);
  695.     }
  696.   }
  697.   else
  698.   {
  699.     while (length > 0)
  700.     {
  701.       length--;
  702.       ch = *buffer++;
  703.  
  704.       /* Delayed output. This is to make sure that files with lengths */
  705.       /* that are not multiples of 4 won't become too long.           */
  706.       if (bytecount == 5)
  707.       {
  708.         fputc(ch1, outfile);
  709.         fputc(ch2, outfile);
  710.         fputc(ch3, outfile);
  711.         fputc(ch4, outfile);
  712.  
  713.         bytecount = 0;
  714.       }
  715.  
  716.       if (new_version)
  717.         calcchecksum(ch);
  718.  
  719.       if (((BYTE)ch >= '!') && ((BYTE)ch < ('!' + 85)))  /* Valid characters. */
  720.       {
  721.         /* See if we can take all 5 bytes and decode them right away. */
  722.         /* That is, if all remaining bytes are on the current line.   */
  723.         if (length >= 4 - bytecount)
  724.         {
  725.           length -= 4 - bytecount;
  726.  
  727.           if (bytecount == 0)
  728.             codeword = DECODE(ch);
  729.           else
  730.             codeword = codeword * 85 + DECODE(ch);
  731.  
  732.           for (bytecount++; bytecount < 5; bytecount++)
  733.           {
  734.             ch = *buffer++;
  735.             if (new_version)
  736.               calcchecksum(ch);
  737.             codeword = codeword * 85 + DECODE(ch);
  738.           }
  739.         }
  740.         else
  741.         {
  742.           /* Shift codeword and insert character. */
  743.  
  744.           if (bytecount == 0)
  745.           {
  746.             codeword = DECODE(ch);
  747.             bytecount = 1;
  748.           } 
  749.           else /* bytecount < 5 */
  750.           {
  751.             codeword = codeword * 85 + DECODE(ch);
  752.             bytecount ++;
  753.           }
  754.         }
  755.  
  756.         if (bytecount == 5)
  757.         {
  758.           tmp_codeword = codeword;
  759.  
  760.           ch4 = (int)tmp_codeword & 0xFF;
  761.           ch3 = (int)(tmp_codeword >>= 8) & 0xFF;
  762.           ch2 = (int)(tmp_codeword >>= 8) & 0xFF;
  763.           ch1 = (int)(tmp_codeword >> 8) & 0xFF;
  764.  
  765.           if (!new_version)
  766.           {
  767.             calcchecksum(ch1);
  768.             calcchecksum(ch2);
  769.             calcchecksum(ch3);
  770.             calcchecksum(ch4);
  771.           }
  772.         }
  773.       }
  774.       else if ((BYTE)ch == 'z' || (new_version && (BYTE)ch == 'y'))
  775.       {
  776.         if (bytecount != 0)
  777.           error = TRUE;
  778.         else
  779.         {
  780.           ch1 = ch2 = ch3 = ch4 = (ch == 'z') ? 0 : ' ';
  781.           if (!new_version)
  782.           {
  783.             calcchecksum(ch1);
  784.             calcchecksum(ch1);
  785.             calcchecksum(ch1);
  786.             calcchecksum(ch1);
  787.           }
  788.           bytecount = 5;
  789.         }
  790.       }
  791.       else
  792.         error = TRUE;
  793.     }
  794.   }
  795.  
  796.   return(error);
  797. }
  798. SHAR_EOF
  799. cat << \SHAR_EOF > btoa.c
  800. /* btoa.c */
  801.  
  802. /* Written by Paul Rutter, Joe Orost & Stefan Parmark. */
  803.  
  804. #include <stdio.h>
  805. #ifdef AMIGA
  806. #include <stdlib.h>
  807. #include <string.h>
  808. #endif AMIGA
  809.  
  810. #include "btoa.h"
  811. #if USE_MACROS
  812. #include "chksum.h"
  813. #endif USE_MACROS
  814.  
  815. #define VERSION  "5.2"
  816.  
  817. LONG Ceor, Csum, Crot;  /* Checksums to verify archive validity. */
  818. BYTE new_version, openoutput, buffer[BUFSIZE];
  819. FILE *outfile;
  820.  
  821.  
  822. void main(argc, argv)
  823. int argc;
  824. BYTE **argv;
  825. {
  826.   register BYTE openinput, error, ch, a_to_b, diagnosis, repair;
  827.   register BYTE *infilename, *text;
  828.   register FILE *infile;
  829.   extern BYTE new_version, openoutput;
  830.   extern FILE *outfile;
  831. #ifdef AMIGA
  832.   extern int _bufsiz;
  833.  
  834.   /* Change file buffer size. */
  835.   _bufsiz = 10240;
  836. #endif AMIGA
  837.  
  838.   error = openinput = openoutput = a_to_b = diagnosis = repair = FALSE;
  839.   new_version = TRUE;
  840.   infilename = NULL;
  841.  
  842.   /* Scan for '-' options. The rest must be file names. */
  843.   while (!error && argc > 1 && *argv[1] == '-')
  844.   {
  845.     text = &argv[1][1];
  846.     while (!error && (ch = *text++) != 0)
  847.     {
  848.       switch(ch)
  849.       {
  850.         case 'a' : /* Activate atob. */
  851.                    a_to_b = TRUE;
  852.                    break;
  853.         case 'd' : /* Extract missing part from undamaged archive. */
  854.                    diagnosis = TRUE;
  855.                    break;
  856.         case 'h' : /* Print help and abort execution. */
  857.                    error = TRUE;
  858.                    break;
  859.         case 'o' : /* Use old btoa format. */
  860.                    new_version = FALSE;
  861.                    break;
  862.         case 'r' : /* Repair damaged archive. */
  863.                    repair = TRUE;
  864.                    break;
  865.         default  : error = TRUE;
  866.       }
  867.     }
  868.     argv++;
  869.     argc--;
  870.   }
  871.  
  872.   if (argc > 3)
  873.     error = TRUE;
  874.  
  875.   if (error)
  876.     printhelp();
  877.   else
  878.   {
  879.     /* If file name was given, try to open file. Otherwise use stdin. */
  880.     if (argc > 1)
  881.     {
  882.       infilename = argv[1];
  883.       if ((infile = fopen_read(infilename)) == NULL)
  884.         error = TRUE;
  885.       else
  886.         openinput = TRUE;
  887.     }
  888.     else
  889.       infile = stdin;
  890.   }
  891.  
  892.   if (!error)
  893.   {
  894.     /* If file name was given, try to open file. Otherwise use stdout. */
  895.     if (argc > 2 && !diagnosis && !repair)
  896.     {
  897.       if ((outfile = fopen_write(argv[2])) == NULL)
  898.         error = TRUE;
  899.       else
  900.         openoutput = TRUE;
  901.     }
  902.     else
  903.       outfile = stdout;
  904.   }
  905.  
  906.   if (!error)
  907.   {
  908.     if (diagnosis)
  909.       error = producerepair(infile);
  910.     else if (repair)
  911.       error = performrepair(infile);
  912.     else if (a_to_b)
  913.       error = atob(infile);
  914.     else
  915.       error = btoa(infile, infilename);
  916.   }
  917.  
  918.   /* Close all opened files. */
  919.   if (openinput)
  920.     fclose(infile);
  921.   if (openoutput)
  922.     fclose(outfile);
  923.  
  924.   if (error)
  925.     exit(1);
  926. }
  927.  
  928.  
  929. BYTE btoa(infile, infilename)
  930. register FILE *infile;
  931. register BYTE *infilename;
  932. {
  933.   register LONG codeword, filesize;
  934.   register int ch1, ch2, ch3, ch4, readbytes;
  935.   extern FILE *outfile;
  936.   extern BYTE new_version, buffer[BUFSIZE];
  937.   extern LONG Ceor, Csum, Crot;
  938.  
  939.   Ceor = Csum = Crot = 0;
  940.  
  941.   /* Write archive header. */
  942.   if (new_version)
  943.   {
  944.     fprintf(outfile, "xbtoa5 %d %s Begin\n", MAXPERLINE,
  945.         (infilename == NULL) ? "-" : truncname(infilename));
  946.   }
  947.   else
  948.     fprintf(outfile, "xbtoa Begin\n");
  949.  
  950.   /* Encode entire input file. */
  951.   filesize = 0;
  952.   do
  953.   {
  954.     readbytes = fread(buffer, 1, 4, infile);
  955.  
  956.     if (readbytes < 4)
  957.     {
  958.       ch1 = (readbytes > 0) ? ((int)buffer[0] & 0xFF) : 0;
  959.       ch2 = (readbytes > 1) ? ((int)buffer[1] & 0xFF) : 0;
  960.       ch3 = (readbytes > 2) ? ((int)buffer[2] & 0xFF) : 0;
  961.       ch4 = 0;
  962.     }
  963.     else
  964.     {
  965.       ch1 = (int)buffer[0] & 0xFF;
  966.       ch2 = (int)buffer[1] & 0xFF;
  967.       ch3 = (int)buffer[2] & 0xFF;
  968.       ch4 = (int)buffer[3] & 0xFF;
  969.     }
  970.  
  971.     if (readbytes > 0)
  972.     {
  973.       if (!new_version)
  974.       {
  975.         calcchecksum(ch1);
  976.         calcchecksum(ch2);
  977.         calcchecksum(ch3);
  978.         calcchecksum(ch4);
  979.       }
  980.  
  981.       codeword = (ch1 << 8) | ch2;
  982.       codeword = (((codeword << 8) | ch3) << 8) | ch4;
  983.       wordout(codeword);
  984.  
  985.       filesize += readbytes;
  986.     }
  987.   }
  988.   while (readbytes == 4);
  989.  
  990.   asciiout(EOF);  /* Flush buffer. */
  991.  
  992.   /* Filesize is written twice as crude cross check. */
  993.   fprintf(outfile, "xbtoa End N %ld %lx E %lx S %lx R %lx\n",
  994.         filesize, filesize, Ceor, Csum, Crot);
  995.  
  996.   return(FALSE);  /* No errors discovered. */
  997. }
  998.  
  999.  
  1000. /* Print help on how to use btoa. */
  1001. void printhelp()
  1002. {
  1003.   fprintf(stderr, "              Btoa version %s\n", VERSION);
  1004.   fprintf(stderr, "Written by Paul Rutter, Joe Orost & Stefan Parmark.\n");
  1005.  
  1006.   fprintf(stderr, "\nUsage: btoa [-{adhor}] [input file] [output file]\n");
  1007.  
  1008.   fprintf(stderr, "\nOptions:\n");
  1009.   fprintf(stderr, "-h  Shows this help list.\n");
  1010.   fprintf(stderr, "-a  Use atob rather than btoa.\n");
  1011.   fprintf(stderr, "-o  Use old version of btoa.\n");
  1012.   fprintf(stderr, "-d  Extract repair file from diagnosis file.\n");
  1013.   fprintf(stderr, "-r  Repair archive from repair file.\n");
  1014.  
  1015.   fprintf(stderr, "\nExamples:\n");
  1016.   fprintf(stderr, "  btoa -h\n");
  1017.   fprintf(stderr, "  btoa [input binary file] [output archive file]\n");
  1018.   fprintf(stderr, "  btoa -o [input binary file] [output archive file]\n");
  1019.   fprintf(stderr, "  btoa -a [input archive file] [output binary file]\n");
  1020.   fprintf(stderr, "  btoa -d [undamaged archive file]\n");
  1021.   fprintf(stderr, "  btoa -r [damaged archive file]\n");
  1022. }
  1023.  
  1024.  
  1025. #if !USE_MACROS
  1026. /* Update file checksums. */
  1027. void calcchecksum(ch)
  1028. register int ch;
  1029. {
  1030.   extern LONG Ceor, Csum, Crot;
  1031.  
  1032.   Ceor ^= ch;
  1033.   Csum += ch + 1;
  1034.  
  1035.   if (Crot & 0x80000000L)
  1036.     ch ++;
  1037.   Crot <<= 1;
  1038.   Crot += ch;
  1039. }
  1040. #endif !USE_MACROS
  1041.  
  1042.  
  1043. /* Encode 4 binary bytes to 5 ascii bytes. */
  1044. void wordout(codeword)
  1045. register LONG codeword;
  1046. {
  1047.   register int tmp, quote;
  1048.   extern BYTE new_version;
  1049.  
  1050.   if (codeword == 0)
  1051.     /* Encode 4 zeros as a 'z'. */
  1052.     asciiout('z');
  1053.   else if (new_version && codeword == 0x20202020)
  1054.     /* Encode 4 spaces as a 'y'. */
  1055.     asciiout('y');
  1056.   else
  1057.   {
  1058.     tmp = 0;
  1059.  
  1060.     /* Extra calculations because some machines don't support */
  1061.     /* unsigned longwords.                                    */
  1062.     if (codeword < 0)
  1063.     {
  1064.       tmp = 32;
  1065.       codeword -= (LONG)(85L * 85 * 85 * 85 * 32);
  1066.     }
  1067.     if (codeword < 0)
  1068.     {
  1069.       tmp = 64;
  1070.       codeword -= (LONG)(85L * 85 * 85 * 85 * 32);
  1071.     }
  1072.  
  1073.     /* Write 5 ascii bytes representing 4 binary bytes. */
  1074.  
  1075.     quote = codeword / (LONG)(85L * 85 * 85 * 85);
  1076.     codeword -= quote * (LONG)(85L * 85 * 85 * 85);
  1077.     asciiout(ENCODE(quote + tmp));
  1078.  
  1079.     quote = codeword / (LONG)(85L * 85 * 85);
  1080.     codeword -= quote * (LONG)(85L * 85 * 85);
  1081.     asciiout(ENCODE(quote));
  1082.  
  1083.     quote = codeword / (LONG)(85L * 85);
  1084.     codeword -= quote * (LONG)(85L * 85);
  1085.     asciiout(ENCODE(quote));
  1086.  
  1087.     quote = (int)codeword / 85;
  1088.     codeword -= quote * 85;
  1089.     asciiout(ENCODE(quote));
  1090.  
  1091.     asciiout(ENCODE((int)codeword));
  1092.   }
  1093. }
  1094.  
  1095.  
  1096. /* Write ch to outfile. Write '\n' for every line. */
  1097. void asciiout(ch)
  1098. register int ch;
  1099. {
  1100.   static WORD linepos = 0;
  1101.   extern FILE *outfile;
  1102.   extern LONG Csum;
  1103.   extern BYTE new_version;
  1104.  
  1105.   if (ch == EOF)  /* Signal to flush buffer. */
  1106.   {
  1107.     /* Linepos == 0 means '\n' just written in asciiout(). This */
  1108.     /* avoids bug in BITNET, which changes blank line to spaces. */
  1109.     if (linepos != 0)
  1110.     {
  1111.       if (new_version)
  1112.         fputc(ENCODE(Csum % 85), outfile); /* Checksum for every line. */
  1113.       fputc('\n', outfile);
  1114.     }
  1115.   }
  1116.   else
  1117.   {
  1118.     fputc(ch, outfile);
  1119.     linepos ++;
  1120.  
  1121.     if (new_version)
  1122.     {
  1123.       calcchecksum(ch);
  1124.       if (linepos >= (MAXPERLINE-1))
  1125.       {
  1126.         fputc(ENCODE(Csum % 85), outfile); /* Checksum for every line. */
  1127.         fputc('\n', outfile);
  1128.         linepos = 0;
  1129.       }
  1130.     }
  1131.     else  /* Old version */
  1132.       if (linepos >= MAXPERLINE)
  1133.       {
  1134.         fputc('\n', outfile);
  1135.         linepos = 0;
  1136.       }
  1137.  
  1138.   }
  1139. }
  1140.  
  1141.  
  1142. /* Remove paths from a file name. */
  1143. BYTE *truncname(name)
  1144. register BYTE *name;
  1145. {
  1146.   register BYTE ch, *newname;
  1147.  
  1148.   newname = name;
  1149.   while ((ch = *name++) != 0)
  1150.     if (ch == '/' || ch == ':')
  1151.       newname = name;
  1152.  
  1153.   return(newname);
  1154. }
  1155. SHAR_EOF
  1156. cat << \SHAR_EOF > btoa.h
  1157. /* btoa.h */
  1158.  
  1159. #define MAXPERLINE      78
  1160. #define BUFSIZE         100
  1161. #define TRUE            1
  1162. #define FALSE           0
  1163. #define USE_MACROS      TRUE
  1164.  
  1165. #define BYTE            char
  1166. #define WORD            short
  1167. #define LONG            long
  1168.  
  1169. #define ENCODE(ch)      ( (int) ((ch) + '!') )
  1170. #define DECODE(ch)      ( (int) ((ch) - '!') )
  1171.  
  1172. struct Diagnosis
  1173. {
  1174.   LONG startpos, endpos;  /* Line before and after erroneous area */
  1175.   struct Diagnosis *next, *last;
  1176. };
  1177.  
  1178.  
  1179. /*
  1180. Following functions have been converted to macros:
  1181.   calcchecksum()
  1182. */
  1183.  
  1184.  
  1185. #if LATTICE  /* Prototypes for Lattice C */
  1186.  
  1187. void asciiout(int), exit(int),
  1188.      intodiagnosislist(struct Diagnosis *, LONG, LONG),
  1189.      outdiagnosislist(struct Diagnosis *, LONG *, LONG *), printhelp(void),
  1190.      producediagnosis(struct Diagnosis *, FILE *), wordout(LONG);
  1191.  
  1192. BYTE atob(FILE *), btoa(FILE *, BYTE *), copyfile(FILE *, FILE *, BYTE *),
  1193.      decode_line(BYTE *, int), new_decodefile(FILE *, LONG *, LONG, int),
  1194.      old_decodefile(FILE *, LONG *), performrepair(FILE *),
  1195.      producerepair(FILE *), readbuffer(BYTE *, BYTE *, FILE *),
  1196.      *truncname(BYTE *);
  1197.  
  1198. int  nextbyte(FILE *);
  1199.  
  1200. FILE *fopen_read(BYTE *), *fopen_write(BYTE *);
  1201.  
  1202. #if USE_MACROS
  1203. void calcchecksum(int);
  1204. #else
  1205. #include "chksum.h"
  1206. #endif USE_MACROS
  1207.  
  1208. #else !LATTICE  /* For compilers which don't know about prototypes. */
  1209.  
  1210. void asciiout(), exit(), intodiagnosislist(), outdiagnosislist(),
  1211.      printhelp(), producediagnosis(), wordout();
  1212.  
  1213. BYTE atob(), btoa(), copyfile(), decode_line(), new_decodefile(),
  1214.      old_decodefile(), performrepair(), producerepair(), readbuffer(),
  1215.      *truncname();
  1216.  
  1217. int  nextbyte();
  1218.  
  1219. FILE *fopen_read(), *fopen_write();
  1220.  
  1221. #if USE_MACROS
  1222. void calcchecksum();
  1223. #else
  1224. #include "chksum.h"
  1225. #endif USE_MACROS
  1226.  
  1227. #endif LATTICE
  1228. SHAR_EOF
  1229. cat << \SHAR_EOF > chksum.h
  1230. /* chksum.h */
  1231. /* calcchecksum() was converted to a macro for effectivity reasons. */
  1232. /* Don't (!!) give it an argument that has to be evaluated. This    */
  1233. /* is guaranteed to slow it down.                                   */
  1234.  
  1235. /* Update file checksums. */
  1236.  
  1237. #define calcchecksum(ch)        \
  1238. {                               \
  1239.   extern LONG Ceor, Csum, Crot; \
  1240.                                 \
  1241.   Ceor ^= ch;                   \
  1242.   Csum += ch + 1;               \
  1243.                                 \
  1244.   if (Crot & 0x80000000L)       \
  1245.   {                             \
  1246.     Crot <<= 1;                 \
  1247.     Crot ++;                    \
  1248.   }                             \
  1249.   else                          \
  1250.     Crot <<= 1;                 \
  1251.                                 \
  1252.   Crot += ch;                   \
  1253. }
  1254. SHAR_EOF
  1255. cat << \SHAR_EOF > repair.c
  1256. /* repair.c */
  1257.  
  1258. /* Written by Stefan Parmark. */
  1259.  
  1260. #include <stdio.h>
  1261. #ifdef AMIGA
  1262. #include <stdlib.h>
  1263. #include <string.h>
  1264. #endif AMIGA
  1265.  
  1266. #include "btoa.h"
  1267.  
  1268. /* File names. */
  1269. BYTE *diagnosisname   = "btoa.dia";
  1270. BYTE *repairname      = "btoa.rep";
  1271. BYTE *repairedname    = "btoa.rdy";
  1272.  
  1273. /* File headers. */
  1274. BYTE *diagnosisheader = "xdiagnosis\n";
  1275. BYTE *repairheader    = "xrepair\n";
  1276.  
  1277.  
  1278. /* Produce diagnosis file from diagnoses records created by atob(). */
  1279. /* It contains the lines immediately before and after the error     */
  1280. /* sequence.                                                        */
  1281. void producediagnosis(diagnosislist, infile)
  1282. register struct Diagnosis *diagnosislist;
  1283. register FILE *infile;
  1284. {
  1285.   register FILE *diagnosisfile;
  1286.   LONG startpos, endpos;
  1287.   register LONG currentpos;
  1288.   extern BYTE *diagnosisname, *diagnosisheader, buffer[BUFSIZE];
  1289.  
  1290.   currentpos = ftell(infile);
  1291.  
  1292.   if ((diagnosisfile = fopen_write(diagnosisname)) != NULL)
  1293.   {
  1294.     fprintf(stderr, "btoa: Diagnosis output to '%s'.\n", diagnosisname);
  1295.  
  1296.     fputs(diagnosisheader, diagnosisfile);
  1297.     do
  1298.     {
  1299.       /* Extract startpos & endpos from diagnosislist. */
  1300.       outdiagnosislist(diagnosislist, &startpos, &endpos);
  1301.  
  1302.       if (startpos != -1)
  1303.       {
  1304.         /* Print line before error. */
  1305.         fseek(infile, startpos, 0);
  1306.         fgets(buffer, BUFSIZE, infile);
  1307.         fputs(buffer, diagnosisfile);
  1308.  
  1309.         /* Print line after error. */
  1310.         fseek(infile, endpos, 0);
  1311.         fgets(buffer, BUFSIZE, infile);
  1312.         fputs(buffer, diagnosisfile);
  1313.       }
  1314.     }
  1315.     while (startpos != -1);
  1316.     fputs(diagnosisheader, diagnosisfile);
  1317.  
  1318.     fclose(diagnosisfile);
  1319.   }
  1320.  
  1321.   /* Move file pointer to where it was when we entered. */
  1322.   fseek(infile, currentpos, 0);
  1323. }
  1324.  
  1325.  
  1326. /* Insert two file positions into diagnosislist. */
  1327. void intodiagnosislist(diagnosislist, startpos, endpos)
  1328. register struct Diagnosis *diagnosislist;
  1329. register LONG startpos, endpos;
  1330. {
  1331.   register struct Diagnosis *diagnosisitem, *lastitem;
  1332.  
  1333.   diagnosisitem = (struct Diagnosis *)malloc(sizeof(struct Diagnosis));
  1334.   diagnosisitem->startpos = startpos;
  1335.   diagnosisitem->endpos = endpos;
  1336.   diagnosisitem->next = NULL;
  1337.  
  1338.   if ((lastitem = diagnosislist->last) == NULL)  /* List is empty */
  1339.     diagnosislist->next = diagnosislist->last = diagnosisitem;
  1340.   else
  1341.   {
  1342.     if (lastitem->endpos >= startpos)
  1343.     {
  1344.       lastitem->endpos = endpos;
  1345.       free((BYTE *) diagnosisitem);
  1346.     }
  1347.     else
  1348.     {
  1349.       lastitem->next = diagnosisitem;
  1350.       diagnosislist->last = diagnosisitem;
  1351.     }
  1352.   }
  1353. }
  1354.  
  1355.  
  1356. /* Extract two file positions from diagnosislist. */
  1357. void outdiagnosislist(diagnosislist, startpos, endpos)
  1358. register struct Diagnosis *diagnosislist;
  1359. LONG *startpos, *endpos;
  1360. {
  1361.   register struct Diagnosis *diagnosisitem;
  1362.  
  1363.   if ((diagnosisitem = diagnosislist->next) == NULL)  /* List is empty */
  1364.     *startpos = *endpos = -1;
  1365.   else
  1366.   {
  1367.     *startpos = diagnosisitem->startpos;
  1368.     *endpos = diagnosisitem->endpos;
  1369.  
  1370.     diagnosislist->next = diagnosisitem->next;
  1371.     free((BYTE *)diagnosisitem);
  1372.     if (diagnosislist->next == NULL)
  1373.       diagnosislist->last = NULL;
  1374.   }
  1375. }
  1376.  
  1377.  
  1378. /* Copy infile to outfile until searchstring is found. If outfile */
  1379. /* is NULL nothing will be written.                               */
  1380. BYTE copyfile(infile, outfile, searchstring)
  1381. register FILE *infile, *outfile;
  1382. register BYTE *searchstring;
  1383. {
  1384.   register BYTE stop, error;
  1385.   static BYTE copybuffer[BUFSIZE];
  1386.  
  1387.   stop = error = FALSE;
  1388.   while (!(stop || error))
  1389.     if (readbuffer(copybuffer, "archive", infile))
  1390.       error = TRUE;
  1391.     else
  1392.     {
  1393.       if (outfile != NULL)
  1394.         fputs(copybuffer, outfile);
  1395.       if (strcmp(copybuffer, searchstring) == 0)
  1396.         stop = TRUE;
  1397.     }
  1398.  
  1399.   return(error);
  1400. }
  1401.  
  1402.  
  1403. /* Read a line from infile into buffer. Returns TRUE if */
  1404. /* end-of-file has been reached.                        */
  1405. BYTE readbuffer(buffer, errormsg, infile)
  1406. register BYTE *buffer, *errormsg;
  1407. register FILE *infile;
  1408. {
  1409.   register BYTE error;
  1410.  
  1411.   error = FALSE;
  1412.   if (fgets(buffer, BUFSIZE, infile) == NULL)
  1413.   {
  1414.     fprintf(stderr, "btoa: Unexpected end of %s file.\n", errormsg);
  1415.     error = TRUE;
  1416.   }
  1417.  
  1418.   return(error);
  1419. }
  1420.  
  1421.  
  1422. FILE *fopen_read(filename)
  1423. register BYTE *filename;
  1424. {
  1425.   register FILE *infile;
  1426.  
  1427.   if ((infile = fopen(filename, "r")) == NULL)
  1428.     fprintf(stderr, "btoa: Can't open '%s' for input.\n", filename);
  1429.  
  1430.   return(infile);
  1431. }
  1432.  
  1433.  
  1434. FILE *fopen_write(filename)
  1435. register BYTE *filename;
  1436. {
  1437.   register FILE *outfile;
  1438.  
  1439.   if ((outfile = fopen(filename, "w")) == NULL)
  1440.     fprintf(stderr, "btoa: Can't open '%s' for output.\n", filename);
  1441.  
  1442.   return(outfile);
  1443. }
  1444.  
  1445.  
  1446. /* Extract lines from original archive to fix the damaged one. */
  1447. BYTE producerepair(infile)
  1448. register FILE *infile;
  1449. {
  1450.   register FILE *repairfile, *diagnosisfile;
  1451.   register BYTE error, stop;
  1452.   static BYTE *errormsg = "diagnosis";
  1453.   extern BYTE *diagnosisname, *diagnosisheader, *repairname, *repairheader,
  1454.               buffer[BUFSIZE];
  1455.  
  1456.   error = FALSE;
  1457.   diagnosisfile = repairfile = NULL;
  1458.  
  1459.   fprintf(stderr, "btoa: Repair output to '%s'.\n", repairname);
  1460.   if ((diagnosisfile = fopen_read(diagnosisname)) == NULL)
  1461.     error = TRUE;
  1462.   else if ((repairfile = fopen_write(repairname)) == NULL)
  1463.   {
  1464.     fclose(diagnosisfile);
  1465.     diagnosisfile = NULL;
  1466.     error = TRUE;
  1467.   }
  1468.   else
  1469.   {
  1470.     /* Read until header is found. This makes it possible to   */
  1471.     /* have junk before the header, such as an article header. */
  1472.     do
  1473.     {
  1474.       if (readbuffer(buffer, errormsg, diagnosisfile))
  1475.         error = TRUE;
  1476.     }
  1477.     while (!error && strcmp(buffer, diagnosisheader) != 0);
  1478.     fputs(repairheader, repairfile);
  1479.   }
  1480.  
  1481.   stop = FALSE;
  1482.   while (!(error || stop))
  1483.   {
  1484.     /* Loop until header is found again. */
  1485.  
  1486.     if (readbuffer(buffer, errormsg, diagnosisfile))
  1487.       error = TRUE;
  1488.     else if (strcmp(buffer, diagnosisheader) == 0)
  1489.       stop = TRUE;
  1490.     else
  1491.     {
  1492.       /* Read until line before error is found. */
  1493.       error = copyfile(infile, NULL, buffer);
  1494.       if (!error)
  1495.       {
  1496.         /* Print line before error. */
  1497.         fputs(buffer, repairfile);
  1498.  
  1499.         if (readbuffer(buffer, errormsg, diagnosisfile))
  1500.           error = TRUE;
  1501.         else
  1502.         {
  1503.           /* Print line after error */
  1504.           fputs(buffer, repairfile);
  1505.           /* Copy infile to repairfile until line after error */
  1506.           error = copyfile(infile, repairfile, buffer);
  1507.         }
  1508.       }
  1509.     }
  1510.   }
  1511.  
  1512.   if (!error)
  1513.     fputs(repairheader, repairfile);
  1514.  
  1515.   if (repairfile != NULL)
  1516.     fclose(repairfile);
  1517.   if (diagnosisfile != NULL)
  1518.     fclose(diagnosisfile);
  1519.  
  1520.   return(error);
  1521. }
  1522.  
  1523.  
  1524. /* Repair damaged archive from repair file. */
  1525. BYTE performrepair(infile)
  1526. register FILE *infile;
  1527. {
  1528.   register FILE *repairfile, *outfile;
  1529.   register BYTE error, stop;
  1530.   static BYTE *errormsg = "repair";
  1531.   extern BYTE *repairname, *repairedname, *repairheader, buffer[BUFSIZE];
  1532.  
  1533.   error = FALSE;
  1534.   repairfile = outfile = NULL;
  1535.  
  1536.   if ((repairfile = fopen_read(repairname)) == NULL)
  1537.     error = TRUE;
  1538.   else if ((outfile = fopen_write(repairedname)) == NULL)
  1539.   {
  1540.     fclose(repairfile);
  1541.     repairfile = NULL;
  1542.     error = TRUE;
  1543.   }
  1544.   else
  1545.   {
  1546.     fprintf(stderr, "btoa: Repaired archive written to '%s'.\n", repairedname);
  1547.  
  1548.     /* Read until header is found. */
  1549.     do
  1550.     {
  1551.       if (readbuffer(buffer, errormsg, repairfile))
  1552.         error = TRUE;
  1553.     }
  1554.     while (!error && strcmp(buffer, repairheader) != 0);
  1555.   }
  1556.  
  1557.   stop = FALSE;
  1558.   while (!(error || stop))
  1559.   {
  1560.     /* Loop until header is found. */
  1561.  
  1562.     if (readbuffer(buffer, errormsg, repairfile))
  1563.       error = TRUE;
  1564.     else if (strcmp(buffer, repairheader) == 0)
  1565.       stop = TRUE;
  1566.     else
  1567.     {
  1568.       /* Read and write until line before error. */
  1569.       error = copyfile(infile, outfile, buffer);
  1570.       if (!error)
  1571.         if (readbuffer(buffer, errormsg, repairfile))
  1572.           error = TRUE;
  1573.         else
  1574.         {
  1575.           /* Read and write until line after error. */
  1576.           error = copyfile(repairfile, outfile, buffer);
  1577.           /* Skip until line after error */
  1578.           copyfile(infile, NULL, buffer);
  1579.         }
  1580.     }
  1581.   }
  1582.  
  1583.   if (!error)  /* Write rest of archive. */
  1584.     while (fgets(buffer, BUFSIZE, infile) != NULL)
  1585.       fputs(buffer, outfile);
  1586.  
  1587.   if (outfile != NULL)
  1588.     fclose(outfile);
  1589.   if (repairfile != NULL)
  1590.     fclose(repairfile);
  1591.  
  1592.   return(error);
  1593. }
  1594. SHAR_EOF
  1595. #    End of shell archive
  1596. exit 0
  1597.