home *** CD-ROM | disk | FTP | other *** search
/ Columbia Kermit / kermit.zip / mskermit / msbpct.c < prev    next >
C/C++ Source or Header  |  2020-01-01  |  15KB  |  567 lines

  1. /* File MSBPCT.C
  2.  
  3. Author: Robert Weiner, Programming Plus, rweiner@watsun.cc.columbia.edu
  4.  
  5. Synopsis: Translates a BOO-encoded file (produced by MSBMKB) back into its
  6. original form.
  7.  
  8. Modification History:
  9.   29-APR-92    Initial Beta Release
  10.         Ideas taken from old msbpct.c (versions before
  11.         01-may-92) and new msbmkb.c
  12.   01-MAY-92        Added files="-", Added -q
  13.   05-MAY-92    Release after outside testing
  14.             Added void usage() proto
  15.             Thanks to Christian Hemsing for OS-9 testing & defs.
  16.             Thanks to Steve Walton for Amiga testing & defs.
  17.   08-MAY-92    Prepare for general release
  18.             Modified _CDECL define, Added uchar defs,
  19.             Fixed up for MSDOS GNU CC
  20.             Use gcc -DMSDOS to compile.
  21.             This MSDOS GCC defines "unix" which doesn't
  22.                 help us at all!
  23.   17-MAY-92        Add AtariST defs & Improved __STDC__ check
  24.                 from Bruce Moore
  25.             I think I'm going to leave off the old program
  26.             "does output file exist? overwrite y/n?" check.
  27.             Please let me know if you think its really reqd.
  28.             Actually, I would like to force the user to
  29.             specify the output file name always since
  30.             embedded output names can be used maliciously.
  31.             Removed string fns so don't need string.h.
  32.             Added Check for ~0 removing non-nulls.
  33.         Next general release now ready... Thanks to those
  34.             listed in the directory below:
  35.   12-JUL-92    Near Final release...??
  36.             Added portability items, cmd line overrides
  37.             ifdef UCHAR, VOID, NOANSI
  38.             Shortened lines to 79 max (got them all?)
  39.             Only thing not done is checking #ifdef NOUCHAR
  40.             and adding any anding off bits which signed
  41.             chars may intruduce in unboo().
  42.  
  43. Beta Testing Informaton, Supported Systems Directory:
  44. =====================================================================
  45. ( Testor / Operating System / O.S. Version / Compiler )
  46.  
  47. Rob Weiner, rweiner@watsun.cc.columbia.edu:
  48.     MSDOS        5.0        MSC 5.1
  49.     MSDOS        5.0        GCC (DJGPP DOS 386/G++ 1.05)
  50.     VAX/VMS        5.4-2        VAXC 3.2
  51.     SUNOS        4.1
  52.     UNIXPC        3.51
  53. Christian Hemsing, chris@v750.lfm.rwth-aachen.de:
  54.     OS-9
  55. Stephen Walton, swalton@solaria.csun.edu:
  56.     AMIGA                MANX C (defines MCH_AMIGA)
  57. Bruce J. Moore, moorebj@icd.ab.com:
  58.     AtariST TOS/GEMDOS        MWC 3.7
  59.  
  60. Fun stuff such as my favorite testing shell command is now possible:
  61.     $ for i in *
  62.     do
  63.         echo $i:
  64.         cat $i | msbmkb -q - - | msbpct -q - - | cmp -l - $i
  65.     done
  66.  
  67. This properly implements the Lasner ~0 fixes.
  68.  
  69. Synopsys: The en-booer writes out printable text from binary text via a 3
  70. input char to 4 output char conversion (called "triple to quad" conversion).
  71. Since the input text can run out before the last triple can be formed, all
  72. en-booers (msbmkb) would add 1 or 2 nulls to the input stream to complete
  73. the triple such that a valid quad can be output.  Thus the problem where
  74. often a de-booer (msbpct) will create an output file from a boo encoded
  75. file, but the output file is larger than the input file by 1 or 2 nulls.
  76. Charles Lasner documented this problem and offered a fix... For each 1 or 2
  77. extra null pad chars added to the input stream, the en-booer should add a
  78. trailing ~0 to the created boo file.  ~X (where X-'0' is a repeat value
  79. which indicates a number of "repeated nulls" does not have a value for the
  80. sequence "~0" which would imply: ``decode into a series of 0 nulls,'' a noop
  81. for "old" debooers.  Hence ~0 can be used as a flag that the input text had
  82. a "padding null" added to it and then the de-booer can know NOT to add these
  83. padding chars to the output stream.  This allows the en-boo/de-boo programs
  84. to finally always guarantee that you get what you started with after passing
  85. through the en-boo then de-boo process.
  86.  
  87. Some bugs/facts with the MSBPCT/MSBMKB programs which popped up
  88. or were discovered recently (January through March 1992):
  89.  -    CURRENT msbpct will NOT make a correct output file from
  90.     the boo file THIS msbmkb creates.  It loses or adds a char.
  91.         Comes from improper implementation of Lasner changes.
  92.         Note: CURRENT enbooer with CURRENT unbooer make the
  93.         same mistakes encoding/uncoding hense files come out
  94.         more or less ok.
  95.  -    OLD msbpct will create a proper output file from a boo
  96.     file created from THIS en-booer.
  97.  -    Current msbpct also screws up output column checking and can
  98.     override the max (usually ~0~0 at eof) and undercut the
  99.     standard value.
  100.  -    Current msbpct doesn't correctly implement lasner fixes.
  101.  -    Current msbpct tells of "using an old booer" at times
  102.     it can determine that that statement is meaningless.
  103.  -    Addtl improper implementation of Lasner change yields
  104.     (quite often) an additional 2 nulls in the output file which
  105.     are removed by an additional 2 ~0 sequence... to break even.
  106.     ie. where old & this enbooer at eof writes "~A", the
  107.     current (bad) booer writes "~C~0~0".
  108. (other items not listed).
  109.  
  110. This new msbpct replaces the old one (msbpct's dated before Mar1992).
  111. Credit should be given to the maintainers of the old msbpct:
  112.     Original by Howie Kaye -- Columbia University 3/11/86
  113.     Robert Weiner of Programming Plus,
  114.     Frank da Cruz of Columbia University,
  115.     Davide P. Cervone of University of Rochester,
  116.     Martin Knoblauch of TH-Darmstadt, Germany,
  117.     John Matthews of U of Delaware,
  118.     L. John Junod of DTNSRDC,
  119.     Christian Hemsing, RWTH Aachen, Germany.
  120.  
  121. Sorry, this seems a bit slower than previous msbpct.  Not sure why yet.
  122. */
  123.  
  124.  
  125. #include <stdio.h>            /* only header we need */
  126.  
  127. /*
  128.     Version Dependencies... Give each new special case its own defs:
  129. */
  130.  
  131. #ifdef VAX11C                /* VAXC032 */
  132. #define SYSTEM        "VAX/VMS"
  133. #define EXIT_GOOD    1
  134. #define EXIT_INFO    3
  135. #define EXIT_BAD    5
  136. #define FOPEN_ROPTS    "r"
  137.             /* open it VMS/RMS Fixed 512 - VMS Executable Format */
  138. #define FOPEN_WOPTS    "wb","ctx=rec","mrs=512","rfm=fix"
  139. #define YES_PROTOS
  140. #endif
  141.  
  142. #ifdef MSDOS                /* MSC 5.1 */
  143. #define SYSTEM        "MSDOS"
  144. #define EXIT_GOOD    0
  145. #define EXIT_INFO    1
  146. #define EXIT_BAD    2
  147. #define FOPEN_ROPTS    "r"
  148. #define FOPEN_WOPTS    "wb"
  149. #define YES_PROTOS
  150. #endif
  151.  
  152. #ifdef GEMDOS                           /* AtariST - TOS - MWC v3.7 */
  153. #define SYSTEM          "AtariST/TOS"
  154. #define EXIT_GOOD       0
  155. #define EXIT_INFO       1
  156. #define EXIT_BAD        2
  157. #define FOPEN_ROPTS     "rb"
  158. #define FOPEN_WOPTS     "w"
  159. #define CASE_CHANGE     CHANGE_LOWER    /* lowercase boo file name */
  160. #define YES_PROTOS
  161. #endif
  162.  
  163. #ifdef OSK
  164. #define SYSTEM          "OS-9"
  165. #define EXIT_GOOD       0
  166. #define EXIT_INFO       1
  167. #define EXIT_BAD        1
  168. #define FOPEN_ROPTS     "r"
  169. #define FOPEN_WOPTS     "w"
  170. #define CASE_CHANGE     CHANGE_NONE     /* leave filename case sensitive */
  171. /*
  172. #undef  YES_PROTOS                      * default OS9 to noprotos *
  173. */
  174. #endif
  175.  
  176. #ifndef FOPEN_ROPTS            /* No system found, use unix defaults */
  177. #define SYSTEM        "UNIX/Amiga/Generic"
  178. #define EXIT_GOOD    0
  179. #define EXIT_INFO    1
  180. #define EXIT_BAD    2
  181. #define FOPEN_ROPTS    "r"
  182. #define FOPEN_WOPTS    "w"
  183. /*
  184. #undef  YES_PROTOS            * default UNIX/generic to noprotos *
  185. */
  186. #endif
  187.  
  188. #ifndef NOANSI                /* allow cmd line override to STDC */
  189. #ifdef __STDC__                /* Ansi likes prototypes */
  190. #if __STDC__                /* MWC sets this defined but 0 valued */
  191. #define YES_PROTOS
  192. #endif
  193. #endif /* __STDC__ */
  194. #endif /* NOANSI */
  195.  
  196. #ifndef VOID                /* allow cmd line override to VOID */
  197. #define VOID void            /* assume system likes void */
  198. #endif
  199.  
  200. #ifndef _CDECL
  201. #define _CDECL
  202. #endif
  203.  
  204. #ifndef __DATE__
  205. #define __DATE__ "01-MAY-1992"
  206. #endif
  207.  
  208. #ifndef __TIME__
  209. #define __TIME__ "00:00:00"
  210. #endif
  211.  
  212. /*
  213.     Typedefs
  214. */
  215. #ifndef UCHAR                /* allow cmd line override */
  216. typedef unsigned char uchar;        /* possible portability concern */
  217. #define UCHAR    uchar
  218. #else
  219. #define NOUCHAR        1        /* flag saying cmd line changed uchar */
  220. #endif
  221.  
  222. /*
  223.     BOO Decoder Defs:
  224. */
  225. #define unchar(c)    ( (c) - '0' )
  226.  
  227. /*
  228.     Here are the function prototypes...
  229.     If your 'C' don't like prototypes, don't declare YES_PROTOS.
  230. */
  231. #ifdef YES_PROTOS
  232. VOID _CDECL convert    (FILE *, FILE *);
  233. int  _CDECL get4       (FILE *, UCHAR *);
  234. VOID _CDECL output     (FILE *, UCHAR *, int);
  235. VOID _CDECL unboo      (UCHAR *, UCHAR *);
  236. VOID usage           (VOID);
  237. #else
  238. VOID convert    ();
  239. int  get4       ();
  240. VOID output     ();
  241. VOID unboo      ();
  242. VOID usage    ();
  243. #endif
  244.  
  245. long count_in=0, count_out=0;        /* character counts */
  246. int quiet=0;
  247.  
  248. main(argc,argv)
  249. int argc;
  250. char **argv;
  251. {
  252.     FILE *fpin, *fpout;
  253.     char outfile[BUFSIZ], *outfilptr;
  254.  
  255.     while( argc > 1 && *argv[1]=='-' )
  256.         {
  257.         if( argv[1][1] == '\0' )
  258.             break;
  259.         switch( argv[1][1] )
  260.             {
  261.             case 'v':        /* version */
  262.                 fprintf(stderr,
  263.                 "MSBPCT.C, Date=\"%s, %s\", System=\"%s\"\n",
  264.                     __DATE__,__TIME__,SYSTEM);
  265.                 fprintf(stderr, "\
  266. Email comments to \"rweiner@kermit.columbia.edu\" \
  267. (Rob Weiner/Programming Plus)\
  268. \n");
  269.                 fprintf(stderr,"\n");
  270.                 break;
  271.             case 'q':        /* quiet */
  272.                 quiet=1;
  273.                 break;
  274.             default:
  275.                 usage();
  276.             }
  277.         argc--;
  278.         argv++;
  279.         }
  280.  
  281.     if( argc < 2 || argc > 3 )
  282.         usage();
  283.         
  284.     if( argv[1][0]=='-' && argv[1][1]=='\0' )
  285.         {
  286.         fpin = stdin ;
  287.         }
  288.     else if( (fpin = fopen( argv[1] , FOPEN_ROPTS )) == NULL )
  289.         {
  290.         fprintf(stderr,"Error, cannot open input file \"%s\"\n",
  291.             argv[1]);
  292.         exit(EXIT_BAD);
  293.         }
  294.  
  295.     if( fgets(outfile, BUFSIZ, fpin) == NULL )
  296.         {
  297.         fprintf(stderr,"Error, cannot read boo filename line\n");
  298.         exit(EXIT_BAD);
  299.         }
  300.  
  301. /*    outfile[ strlen(outfile) - 1 ] = '\0' ;        * wack \n */
  302.                     /* redone w/o strlen... */
  303.     outfilptr = outfile ;
  304.     while( *outfilptr && (*outfilptr != '\n') && (*outfilptr != '\r') )
  305.         outfilptr++;
  306.     *outfilptr = '\0' ;
  307.     outfilptr = outfile ;
  308.  
  309.     if( argc == 3 )        /* override on internally stored filename */
  310.         {
  311.         outfilptr = argv[2];
  312.         if( !quiet )
  313.             {
  314.             fprintf(stderr,
  315.             "BOO Internally stored output filename = \"%s\"\n",
  316.                 outfile);
  317.             fprintf(stderr,
  318.             "Command line output filename override = \"%s\"\n",
  319.                 outfilptr);
  320.             }
  321.         }
  322.  
  323.     if( !quiet )
  324.         fprintf(stderr,
  325.                "Creating Binary File \"%s\" from BOO File \"%s\"...\n",
  326.                 outfilptr,argv[1]);
  327.  
  328.     if( outfilptr[0]=='-' && outfilptr[1]=='\0' )
  329.         {
  330.         fpout = stdout ;
  331.         }
  332.     else if( (fpout = fopen( outfilptr , FOPEN_WOPTS )) == NULL )
  333.         {
  334.         fprintf(stderr,"Error, cannot open output file \"%s\"\n",
  335.             outfilptr);
  336.         exit(EXIT_BAD);
  337.         }
  338.  
  339.  
  340.     convert(fpin,fpout);
  341.  
  342.     output(fpout,(UCHAR *)"",0);        /* flush output buffering */
  343.  
  344.     fclose(fpin);
  345.     fclose(fpout);
  346.  
  347.     if( !quiet )
  348.         {
  349.         fprintf(stderr,"Data bytes in: %ld,  ",  count_in);
  350.         fprintf(stderr,"Data bytes out: %ld,  ", count_out);
  351.         fprintf(stderr,
  352.             "Difference: %ld bytes\n", count_in - count_out);
  353.         }
  354.     exit(EXIT_GOOD);
  355. }
  356.  
  357. VOID usage()
  358. {
  359.     fprintf(stderr,
  360.         "MSBPCT = Decode Ascii BOO Encoded File into Binary File\n");
  361.     fprintf(stderr, "\
  362. Usage: MSBPCT [-v(version) -q(quiet)] input_boo_file [output_file_override]\n"
  363.             );
  364.     fprintf(stderr,
  365. "              Note: Filenames of '-' are supported for stdin & stdout\n");
  366.     exit(EXIT_INFO);
  367. }
  368.  
  369. VOID convert(fpin,fpout)        /* convert every 4 chars to 3 */
  370. FILE *fpin, *fpout;
  371. {
  372.     int n;
  373.     int fill_nulls = 0;
  374.     UCHAR inbuf[10], outbuf[10];
  375.     int must_output=0;
  376.  
  377.     while( (n = get4(fpin,inbuf)) != 0 )
  378.         {
  379.         if( n < 0 )        /* -n is 1 more than # repeated nulls */
  380.             {
  381.             if( n == -1 )        /* ~0 found */
  382.                 {
  383.                 fill_nulls++;    /* count #nulls to back up */
  384.                 }
  385.             else    {        /* ~X null compression found */
  386.                 if( must_output )  /* output last triple */
  387.                     {
  388.                     output(fpout,outbuf,must_output);
  389.                     must_output = 0;
  390.                     }
  391.                 while( ++n < 0 )
  392.                     output(fpout,(UCHAR *)"",1);
  393.                 /* ~0 must be after all data */
  394.                 fill_nulls = 0 ;
  395.                 }
  396.             }
  397.         else    {
  398.             if( must_output )    /* output last triple */
  399.                 output(fpout,outbuf,must_output);
  400.  
  401.             unboo( inbuf , outbuf );
  402.  
  403.             /* output these chars the next time around */
  404.  
  405.             fill_nulls = 0 ;    /* ~0 must be after all data */
  406.             must_output = 3 ;    /* must output last triple */
  407.             }
  408.         }
  409.  
  410.     if( fill_nulls > 0 )
  411.         {
  412.         if( !quiet )
  413.             fprintf(stderr,"Fill Nulls = %d\n",fill_nulls);
  414.  
  415.         /* by definition, if there are ~0, there must be a triple */
  416.         if( must_output < 3 )    /* we expect a triple when see ~0s */
  417.             {
  418.             fprintf(stderr,
  419. "WARNING: Detected Invalid Boo Format (~0 after non-triple)\n");
  420.             fprintf(stderr,
  421. "WARNING: Output File is probably %d nulls greater than original input file\n",
  422.                 fill_nulls);
  423.             }
  424.         else    {
  425.             must_output -= fill_nulls ;
  426.  
  427.             if( ((fill_nulls>0) && (outbuf[2]!='\0')) ||
  428.                 ((fill_nulls>1) && (outbuf[1]!='\0')) )
  429.                 {
  430.                 fprintf(stderr,
  431. "WARNING: Detected Invalid Boo Format (Non-Null Chars Removed by ~0)\n");
  432.                 }
  433.             }
  434.         }
  435.     if( must_output > 0 )  /* output last, possibly ~0 reduced, triple */
  436.         output(fpout, outbuf, must_output);
  437. }
  438.  
  439. int get4( fp , buf )    /* return: pos=# read, neg=# nulls + 1 found */
  440. FILE *fp;
  441. UCHAR *buf;
  442. {
  443.     int i=0;        /* amt last read */
  444.     int nulls=0;        /* amt nulls found */
  445.     int c;
  446.  
  447.     do    {
  448.         if( (c = getc(fp)) == EOF )        /* hit eof */
  449.             {
  450.             if( ferror(fp) )        /* quick check */
  451.                 {
  452.                 fprintf(stderr,
  453.                     "get4(): fread error on input file\n");
  454.                 exit(EXIT_BAD);
  455.                 }
  456.             break;                /* stop */
  457.             }
  458.         count_in++;
  459.  
  460.         if( c == '\n' )                /* \n means nothing */
  461.             continue;
  462.  
  463.         if( i == 0 )            /* not in quad yet */
  464.             {
  465.             if( nulls == 1 )    /* this char IS #nulls now */
  466.                 {        /* add 1 as a ~0 flag */
  467.                 nulls = unchar( c ) + 1 ;
  468.                 return( -nulls ); /* got it, return */
  469.                 }
  470.             else if( c == '~' ) /* null repeat prefix */
  471.                 {
  472.                 nulls=1;
  473.                 continue;
  474.                 }
  475.             }
  476.  
  477.         i++;                /* count till 4 */
  478.         *buf++ = c ;            /* save chars */
  479.         } while( i <= 3 );
  480.     return(i);
  481. }
  482.  
  483. VOID output(fp,s,n)        /* output chars, n==0 = flush buffer */
  484. FILE *fp;
  485. UCHAR *s;
  486. int n;
  487. {
  488.     static char buf[BUFSIZ];
  489.     static char *p=buf;
  490.     int flush = (n==0) ;
  491.     unsigned count;
  492.  
  493.     if( n < 0 )            /* ~0 backup */
  494.         {
  495.         if( p < buf-n )        /* ensure there is stuff to delete */
  496.             {
  497.             fprintf(stderr,
  498.     "output(): Error, no chars in buffer to backup output stream\n");
  499.             exit(EXIT_BAD);
  500.             }
  501.         p += n ;        /* backup ptr */
  502.         }
  503.     else    {            /* n==0 = flush buffer */
  504.         if( (n != 0) && ((p+n) <= (buf+sizeof(buf))) )
  505.             {        /* will fit in current buffer */
  506.             while( n-- > 0 )
  507.                 *p++ = *s++ ;
  508.             }
  509.         else    {        /* won't fit in current buffer */
  510.             /* take what we can, write current, load next */
  511.  
  512.             while( (n > 0) && (p < (buf+sizeof(buf))) )
  513.                 {
  514.                 *p++ = *s++ ;
  515.                 n-- ;
  516.                 }
  517.  
  518.             /* this must be "p-buf,1" ordered here for VMS
  519.                varying recs to come out right, probably helps
  520.                fixed 512 too */
  521.  
  522.             count = p - buf ;
  523.             if( (count>0) &&
  524.                (fwrite( buf , count , 1 , fp ) != 1) )
  525.                 {
  526.                 fprintf(stderr,
  527.                 "output(): fwrite error on output file\n");
  528.                 exit(EXIT_BAD);
  529.                 }
  530.  
  531.             count_out += count ;
  532.             p = buf ;
  533.             while( n-- > 0 )    /* don't forget leftovers */
  534.                 *p++ = *s++ ;
  535.             }
  536.         }
  537. }
  538.  
  539. VOID unboo( inbuf , outbuf )    /* here is where we unboo 4 into 3 chars */
  540. UCHAR *inbuf, *outbuf;
  541. {
  542.     UCHAR x,y,z,a,b,c,d;
  543.  
  544.     /* get a,b,c,d the 4 booed bytes */
  545.  
  546.     a = unchar( *inbuf++ );
  547.     b = unchar( *inbuf++ );
  548.     c = unchar( *inbuf++ );
  549.     d = unchar( *inbuf   );
  550.  
  551.     /* calc x,y,z the 3 unbooed bytes */
  552.     /* we shouldn't need some of these &ands below,
  553.        except to make sure input data is still 6 bit */
  554.  
  555.     x = (a << 2) | ((b >> 4) & 003) ;
  556.     y = (b << 4) | ((c >> 2) & 017) ;
  557.     z = (c << 6) | (d        & 077) ;
  558.  
  559.     *outbuf++ = x;
  560.     *outbuf++ = y;
  561.     *outbuf   = z;
  562. }
  563.  
  564. /*
  565.     [EOF]
  566. */
  567.