home *** CD-ROM | disk | FTP | other *** search
/ BCI NET / BCI NET Dec 94.iso / archives / telecomm / archivers / aucode.lha / AuCode.c next >
Encoding:
C/C++ Source or Header  |  1994-02-06  |  18.1 KB  |  414 lines

  1. /* -------------------------------------------------------------------------- **
  2.  * AuCode.c - Amiga uuencode/uudecode toolkit.
  3.  *
  4.  *  Written by Christopher R. Hertel; December, 1992
  5.  *  email: crh@bubble.mooses.affinity.mn.org
  6.  * -------------------------------------------------------------------------- **
  7.  *  Copyright (C) 1993 Christopher R. Hertel
  8.  *
  9.  *  This library is free software; you can redistribute it and/or
  10.  *  modify it under the terms of the GNU Library General Public
  11.  *  License as published by the Free Software Foundation; either
  12.  *  version 2 of the License, or (at your option) any later version.
  13.  *
  14.  *  This library is distributed in the hope that it will be useful,
  15.  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  16.  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  17.  *  Library General Public License for more details.
  18.  *
  19.  *  You should have received a copy of the GNU Library General Public
  20.  *  License along with this library; if not, write to the Free
  21.  *  Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  22.  *
  23.  * -------------------------------------------------------------------------- **
  24.  *
  25.  * $Log:    AuCode.c,v $
  26.  * Revision 1.3  94/02/06  14:06:47  CRH
  27.  * With this revision I rewrote the comments for functions Cleanln() and 
  28.  * Decodeln().  I fixed some bugs with the checksum calculations (I was
  29.  * getting negative values), and I added an internal function to calculate
  30.  * checksums on bytes triples (au_cdChecksum3()).  CRH
  31.  * 
  32.  * Revision 1.2  93/09/15  21:27:52  CRH
  33.  * Fixed a small bug in the decoding functions.  Plus some general
  34.  * cleanup.
  35.  *
  36.  * Revision 1.1  93/03/25  11:33:21  CRH
  37.  * Cleaned up comments.  Clarified License and Copyright.
  38.  *
  39.  * Revision 1.0  93/03/04  00:29:07  CRH
  40.  * Initial revision
  41.  *
  42.  * -------------------------------------------------------------------------- **
  43.  * My understanding of uuencoding/decoding is based on the knowledge that I
  44.  * gained by reading the uuencode source provided with AmigaUUCP V1.16.
  45.  * Many thanks to the authors of that software!  The following are comments
  46.  * from their code, which are included as my way of giving credit where
  47.  * credit is due.
  48.  *
  49.  * /begin/
  50.  * [uuencode.c:]
  51.  * Written by Mark Horton
  52.  * Modified by ajr (Alan J Rosenthatl,flaps@utcsri.UUCP) to use checksums
  53.  * Modified by fnf (Fred Fish,well!fnf) to use Keith Pyle's suggestion for
  54.  * compatibility
  55.  * Modified by bcn (Bryce Nesbitt,ucbvax!hoser!bryce) to enable CTRL-C for
  56.  * Amiga Lattice C.  Added a transparent file size trailer for later check.
  57.  * Changed fopen from "r" to "rb" for Messy-dos machines (thanks to Andrew
  58.  * Wylie)
  59.  *
  60.  * [uudecode.c:]
  61.  * Modified by bcn (Bryce Nesbitt,ucbvax!cogsci!bryce) to fix a misleading
  62.  * error message on the Amiga port, to fix a bug that prevented decoding
  63.  * certain files, to work even if trailing spaces have been removed from a
  64.  * file, to check the filesize (if present), to add some error checking, to
  65.  * loop for multiple decodes from a single file, and to handle common
  66.  * BITNET mangling.  Kludged around a missing string function in Aztec
  67.  * C. Changed "r" to "rb" and "w" to "wb" for Messy-dos machines
  68.  * (Thanks to Andrew Wylie).
  69.  * /end/
  70.  *
  71.  * -------------------------------------------------------------------------- **
  72.  *  Initial comments:  This module implements the basic uuencode/decode
  73.  *  functionality.  The functions are designed to be reentrant, so you may
  74.  *  use them as part of either a link library or a run-time library.
  75.  *  Also, no attempt is made to support any hardware platform other than the
  76.  *  Commodore Amiga.  In particular, this module is written to compile under
  77.  *  SAS C 5.10 or above on an Amiga running OS 2.04 or above.  The code is,
  78.  *  however, written in a fairly generic style, and should be highly porable.
  79.  *
  80.  *  Notes regarding style:  I am a Pascal programmer at heart, and most of
  81.  *  my style choices are based on my preference for that language and its
  82.  *  derivatives.  The result is a hybrid format with a few odd quirks of
  83.  *  its own.  In any case, I intend to produce several modules, so my
  84.  *  function, macro, constant, variable, and type naming conventions serve
  85.  *  to associate identifiers with modules, and to prevent duplicate names
  86.  *  (after all, who else would use such awkward identifiers?).
  87.  *
  88.  *  As mentioned above, these modules are designed to work within the
  89.  *  multitasking environment of the Commodore Amiga.  The code is (should
  90.  *  be) reentrant and self contained (I've even avoided ANSI standard
  91.  *  functions, because the available libraries are not necessarily
  92.  *  reentrant).  I have, however, been careful to avoid Amiga-specific
  93.  *  functions.  It should, therefore, be very easy to port the code to other
  94.  *  platforms.  (I'm hoping for a microVAX for Christmas. ;-) )
  95.  *
  96.  * Christopher R. Hertel; December, 1992
  97.  * -------------------------------------------------------------------------- **
  98.  */
  99.  
  100. #include "AuCode.h"     /* Header for THIS module.  */
  101.  
  102. /* -------------------------------------------------------------------------- **
  103.  * Macros...
  104.  *
  105.  *  Min     - This is the classic min() macro.  We implement it here to avoid
  106.  *            including a header file for some module that we won't ever use.
  107.  */
  108.  
  109. #define Min(a,b) ((a)<(b))?(a):(b)
  110.  
  111. /* -------------------------------------------------------------------------- **
  112.  * Functions...
  113.  */
  114.  
  115. static unsigned int au_cdChecksum3( char *C )
  116.   /* ------------------------------------------------------------------------ **
  117.    * Calculate a checsum over three bytes using au_cdCHKSUMsize as a modulus.
  118.    *
  119.    *  Input:  C - A pointer to an array of at least three bytes.
  120.    *  Output: The checksum of C[0]..C[2], mod au_cdCHKSUMsize.
  121.    * ------------------------------------------------------------------------ **
  122.    */
  123.   {
  124.   unsigned int tmp;
  125.  
  126.   tmp  = (unsigned int)C[0];
  127.   tmp += (unsigned int)C[1];
  128.   tmp += (unsigned int)C[2];
  129.   return( tmp % au_cdCHKSUMsize );
  130.   } /* au_cdChecksum3 */
  131.  
  132. int au_cdCvt3to4( char *S, char *T )
  133.   /* ------------------------------------------------------------------------ **
  134.    * Uuencode a three-byte value.  The result is a four-byte value.  The
  135.    * function returns the checksum of the three source bytes.
  136.    *
  137.    *  Input:
  138.    *    S - A pointer to an array of three bytes.  These are the source data.
  139.    *    T - A pointer to the four byte target array.
  140.    *
  141.    *  Output: a checksum value calculated from the three source bytes.
  142.    * ------------------------------------------------------------------------ **
  143.    */
  144.   {
  145.   T[0] = S[0] >> 2;
  146.   T[1] = ((S[0] << 4) & 0x30) | ((S[1] >> 4) & 0x0f);
  147.   T[2] = ((S[1] << 2) & 0x3c) | ((S[2] >> 6) & 0x03);
  148.   T[3] = S[2] & 077;
  149.   T[0] = au_cdENCD( T[0] );
  150.   T[1] = au_cdENCD( T[1] );
  151.   T[2] = au_cdENCD( T[2] );
  152.   T[3] = au_cdENCD( T[3] );
  153.   return( (int)au_cdChecksum3( S ) );
  154.   } /* au_cdCvt3to4 */
  155.  
  156. int au_cdCvt4to3( char *S, char *T )
  157.   /* ------------------------------------------------------------------------ **
  158.    * Uudecode a four-byte value.  The result is a three-byte value.  The
  159.    * function returns the checksum of the three target bytes.
  160.    *
  161.    *  Input:
  162.    *    S - A pointer to an array of four bytes.  These are the source data.
  163.    *    T - A pointer to the three byte target array.
  164.    *
  165.    *  Output: a checksum value calculated from the three target bytes.
  166.    * ------------------------------------------------------------------------ **
  167.    */
  168.   {
  169.   T[0] = (au_cdDECD(S[0]) << 2) | (0x03 & (au_cdDECD(S[1]) >> 4));
  170.   T[1] = (au_cdDECD(S[1]) << 4) | (0x0f & (au_cdDECD(S[2]) >> 2));
  171.   T[2] = (au_cdDECD(S[2]) << 6) | ( 077 & (au_cdDECD(S[3])) );
  172.   return( (int)au_cdChecksum3( T ) );
  173.   } /* au_cdCvt4to3 */
  174.  
  175. int au_cdEncode( char *sBufr, int sbSize, char *tBufr, int tbSize )
  176.   /* ------------------------------------------------------------------------ **
  177.    * Encode a block of characters using the UUENCODE coding scheme.
  178.    *
  179.    *  Input:
  180.    *    sBufr   - A pointer to a buffer containing the bytes to be encoded.
  181.    *    sbSize  - The number of bytes in sBufr to be encoded.
  182.    *    tBufr   - A pointer to the target buffer.
  183.    *    tbSize  - The number of bytes available in tBufr.
  184.    *
  185.    *  Output:   The number of source bytes that were actually converted.  If
  186.    *            this number is less than sbSize, an error occured during the
  187.    *            encoding process (ran out of room in tBufr).  The best way to
  188.    *            recover is to write out the contents of tBufr (which, if they
  189.    *            exist, are valid) and call this function again, passing in a
  190.    *            pointer to the remainder of the source bytes.
  191.    *         -> However, if zero bytes were processed (i.e., this function
  192.    *            returns zero), then tBufr is simply not large enough.
  193.    *
  194.    *  Note:     This function will not write the terminating line.
  195.    * ------------------------------------------------------------------------ **
  196.    */
  197.   {
  198.   int           CharCount = 0;
  199.   int           TargPos   = 0;
  200.   int           PhraseLen;
  201.   int           TriplLen;
  202.   int           PhrasePos;
  203.   char         *sp;
  204.   int           CheckSum;
  205.  
  206.   tBufr[0] = '\0';              /* Mark the end of the empty target string. */
  207.   while( CharCount < sbSize )
  208.     {
  209.     CheckSum = 0;
  210.     /* Calculate length of next input phrase.
  211.      * We need output space to store the translated phrase plus a nul byte.
  212.      * If there's not enough room, return now.
  213.      */
  214.     PhraseLen = (sbSize - CharCount);                  /* Bytes left in sBufr */
  215.     PhraseLen = Min( au_cdMAXPhrase, PhraseLen );
  216.     if( (au_cdExpPhrLen(PhraseLen)) >= (tbSize - TargPos) )
  217.       return( CharCount );
  218.  
  219.     /* First convert as many whole triples as possible.  This is fairly
  220.      * quick.  TriplLen is the greatest multiple of three that is
  221.      * <= PhraseLen.  sp is the "starting point", it points to the
  222.      * first char of the phrase.
  223.      */
  224.     TriplLen = 3 *(PhraseLen / 3);
  225.     sp = &sBufr[CharCount];
  226.     tBufr[TargPos++] = au_cdENCD( (unsigned char)PhraseLen );/* phrase length */
  227.     for( PhrasePos = 0; (PhrasePos < TriplLen); PhrasePos += 3, TargPos += 4 )
  228.       CheckSum = ( (CheckSum + au_cdCvt3to4(&sp[PhrasePos], &tBufr[TargPos]))
  229.                  % au_cdCHKSUMsize );
  230.     /* Now get any odd bytes at the end of the phrase.  */
  231.     TriplLen = (PhraseLen - TriplLen);
  232.     if( TriplLen )
  233.       {
  234.       char tmp[] = {0, 0, 0};
  235.       int  i;   /* Position within tmp. */
  236.  
  237.       for( i = 0; (i < TriplLen); i++, PhrasePos++ )
  238.         tmp[i] = sp[PhrasePos];
  239.       CheckSum = ( (CheckSum + au_cdCvt3to4( tmp, &tBufr[TargPos] ))
  240.                  % au_cdCHKSUMsize );
  241.       TargPos += 4;
  242.       }
  243.     tBufr[TargPos++] = au_cdENCD(CheckSum);   /* Write the checksum.  */
  244.     tBufr[TargPos++] = '\n';                  /* Write a newline.     */
  245.     tBufr[TargPos]   = '\0';                  /* Terminate the buffer.*/
  246.     CharCount += PhraseLen;   /* *Now* advance the character count.   */
  247.     }
  248.   return( CharCount );  /* Succesfully completed the entire block.  */
  249.   } /* au_Encode */
  250.  
  251. int au_cdCleanln( char *sBufr, int sbSize, char *tBufr, int tbSize )
  252.   /* ------------------------------------------------------------------------ **
  253.    * Clean a line of input to ensure that it can be decoded properly.
  254.    *
  255.    *  Input:  sBufr   - A pointer to an array of encoded bytes.  The
  256.    *                    contents of sBufr[] (the "source") are assumed to
  257.    *                    be a single line of uuencoded text.  The line is
  258.    *                    considered to be terminated by ascii value less
  259.    *                    than 32.
  260.    *
  261.    *          sbSize  - The number of significant bytes in sBufr[].  That
  262.    *                    is, the maximum number of bytes that you wish to
  263.    *                    have cleaned.  Cleanln() will process no more than
  264.    *                    min( tbSize, sbSize ) bytes.
  265.    *
  266.    *          tBufr   - A pointer to the target buffer.  The target buffer
  267.    *                    will receive the "cleaned" copy of the source line.
  268.    *                    Note that, in general, tBufr should be at least
  269.    *                    sbSize bytes long, and possibly longer.  It should
  270.    *                    always be safe to create a tBufr that is
  271.    *                    au_cdMAX_LINE bytes long (unless you are using in
  272.    *                    this module in some new and interesting way that is
  273.    *                    beyond my ability to extrapolate).
  274.    *
  275.    *          tbSize  - The number of bytes available in tBufr.  Cleanln()
  276.    *                    will process no more than min( tbSize, sbSize )
  277.    *                    bytes.
  278.    *
  279.    *  Output: The number of characters in sBufr[] that were copied to
  280.    *          tBufr[].  This value does not include any spaces that were
  281.    *          added to the end of tBufr[] as padding.
  282.    *
  283.    *  Notes:  The input line is "cleaned" as follows:
  284.    *
  285.    *          This function copies the input line to a new buffer.  During
  286.    *          the copy the function repairs damage that *may* have occurred
  287.    *          during transmission.
  288.    *
  289.    *          In particular, some UUENCODErs encode a nul sixel as a space
  290.    *          (this module encodes a nul as a "`" character, both decode
  291.    *          to the same thing).  Some editors (and other text utilites)
  292.    *          will trim trailing spaces from the ends of lines.  This
  293.    *          function pads the end of the target buffer with spaces to
  294.    *          avoid the problem of lost sixles.
  295.    *
  296.    *          Another potential error exists because BITNET converts the
  297.    *          "^" to a "~".  Technically, the "~" can't be part of the code,
  298.    *          so it's safe to convert it back using the au_cdCleanSixel()
  299.    *          macro.
  300.    *
  301.    *          This function always places a nul byte in the last position
  302.    *          in tBufr[].  That is:
  303.    *              tBufr[tbSize-1] = '\0';
  304.    *          be certain that your tBufr[] is big enough to handle all the
  305.    *          valid characters in sBufr[] plus the nul!
  306.    * ------------------------------------------------------------------------ **
  307.    */
  308.   {
  309.   register int  i;
  310.   register int  maxMove;
  311.   int tmp;
  312.  
  313.   maxMove = Min( tbSize, sbSize );    /* Max # of bytes to *copy* to tBufr[]. */
  314.   for( i = 0; (i < maxMove) && (32 <= sBufr[i]); i++ )
  315.     {
  316.     tBufr[i] = au_cdCleanSixel(sBufr[i]); /* Clean and copy chars to target.  */
  317.     }
  318.   tmp = i+1;
  319.  
  320.   for( ; (i < tbSize); i++ )          /* Pad the end of the line with spaces. */
  321.     {
  322.     tBufr[i] = ' ';                   /* Pad remainder of tBufr with spaces.  */
  323.     }
  324.   tBufr[tbSize-1] = '\0';             /* Terminate tBufr. */
  325.   return( tmp );
  326.   } /* au_cdCleanln */
  327.  
  328. int au_cdDecodeln( char *sBufr, char *tBufr, int tbSize, int *ErrCD )
  329.   /* ------------------------------------------------------------------------ **
  330.    * This function will decode one "line" of encoded input.
  331.    *
  332.    *  Input:  sBufr   - A pointer to a buffer containing the encoded data
  333.    *                    that is to be decoded.  The data should be in the
  334.    *                    form of a "line" of uuencoded text.  The line is
  335.    *                    assumed to be "clean" (see au_cdCleanln()).
  336.    *          tBufr   - A pointer to the target buffer.  tBufr[] will
  337.    *                    receive the decoded data.
  338.    *          tbSize  - Number of bytes available in tBufr[].
  339.    *          ErrCD   - A pointer to an integer that will receive an
  340.    *                    error code.  Check *ErrCD for one of the following
  341.    *                    values:
  342.    *
  343.    *                    au_cdErr_NoError  - No error.  If the function
  344.    *                      return value is zero *and* the error code is
  345.    *                      _NoError, then an empty (i.e., terminating) line
  346.    *                      has been found, indicating end of the (encoded)
  347.    *                      file.
  348.    *
  349.    *                    au_cdErr_Cramped  - tBufr[] does not contain enough
  350.    *                      space to receive the decoded information.  A
  351.    *                      single encoded line may contain, at most, 63
  352.    *                      encoded (source) bytes.
  353.    *
  354.    *                    au_cdErr_Checksum - the checksum comparison failed.
  355.    *                      Either the source line is mangled, or the
  356.    *                      checksum was not written properly by the encoding
  357.    *                      program.
  358.    *
  359.    *  Output: The number of bytes that were generated as a result of
  360.    *          decoding the input line.
  361.    *
  362.    *  Notes: sBufr[] is assumed to be "clean".  See the notes associated
  363.    *  with function au_cdCleanln() for an explanation of the term "clean",
  364.    *  as used in this context.
  365.    * ------------------------------------------------------------------------ **
  366.    */
  367.   {
  368.   register int i;
  369.   int   decodeLen = au_cdDECD(*sBufr);
  370.   int   Quads,
  371.         QuadSize;
  372.   int   sPos, tPos;
  373.   int   CheckSum = 0;
  374.  
  375.   *ErrCD = au_cdErr_NoError;    /* initialize the error return value */
  376.  
  377.   /* The original data is stored in four-byte chunks.  The variable <Quads>
  378.    * represents the number of four-byte chunks required to store
  379.    * <decodeLen> original bytes.  <QuadSize> is the number of bytes that
  380.    * are required in order to decode all of the four-byte chunks.  Note
  381.    * that QuadSize may be greater than decodeLen by 1 or 2 bytes.  This is
  382.    * because we decode the line one chunk at a time, generating three bytes
  383.    * at a time.  One or two of those bytes may be padding.
  384.    */
  385.   Quads    = ( ( decodeLen + 2 ) / 3 );
  386.   QuadSize = ( Quads * 3 );
  387.  
  388.   /* If tBufr doesn't have room for the decoded bytes, return zero now.
  389.    */
  390.   if( tbSize < QuadSize )
  391.     {
  392.     *ErrCD = au_cdErr_Cramped;
  393.     return( 0 );
  394.     }
  395.  
  396.   /* Now decode all of the quads.  Note that sPos is set to 1.  This is
  397.    * because the first character in sBufr (i.e., sBufr[0]) is the length
  398.    * byte, and not actually part of the encoded data.
  399.    */
  400.   for( i = 0, sPos = 1, tPos = 0; (i < Quads); i++, sPos += 4, tPos += 3 )
  401.     {
  402.     CheckSum = ( (CheckSum + au_cdCvt4to3( &sBufr[sPos], &tBufr[tPos] ))
  403.                % au_cdCHKSUMsize );
  404.     }
  405.  
  406.   /* Check the checksum. */
  407.   if( CheckSum != (int)(au_cdDECD(sBufr[sPos])) )
  408.     *ErrCD = au_cdErr_Checksum;             /* If checksum bad, set errorcode,*/
  409.  
  410.   return( decodeLen );
  411.   } /* au_cdDecodeln */
  412.  
  413. /* ========================================================================== */
  414.