home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1992 March / Source_Code_CD-ROM_Walnut_Creek_March_1992.iso / usenet / compsrcs / unix / volume18 / btoa < prev    next >
Encoding:
Internet Message Format  |  1989-04-20  |  40.7 KB

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