home *** CD-ROM | disk | FTP | other *** search
/ BCI NET / BCI NET Dec 94.iso / archives / telecomm / archivers / aucode.lha / AuEncode.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-01-29  |  17.9 KB  |  418 lines

  1. /* -------------------------------------------------------------------------- **
  2.  * Program: AuEncode - UUencoding for the Amiga.
  3.  *
  4.  *  Written by Christopher R. Hertel; January, 1993
  5.  *  crh@bubble.mooses.affinity.mn.org
  6.  * -------------------------------------------------------------------------- **
  7.  *  Copyright (C) 1993  Christopher R. Hertel
  8.  *
  9.  *  This program is free software; you can redistribute it and/or modify
  10.  *  it under the terms of the GNU General Public License as published by
  11.  *  the Free Software Foundation; either version 2 of the License, or
  12.  *  (at your option) any later version.
  13.  *
  14.  *  This program 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
  17.  *  GNU General Public License for more details.
  18.  *
  19.  *  You should have received a copy of the GNU General Public License
  20.  *  along with this program; if not, write to the Free Software
  21.  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  22.  * -------------------------------------------------------------------------- **
  23.  *
  24.  * $Log:    AuEncode.c,v $
  25.  * Revision 1.2  93/09/15  20:43:07  CRH
  26.  * Updated the comments a bit.  Nothing significant.
  27.  * 
  28.  * Revision 1.1  93/09/15  20:29:43  CRH
  29.  * Rewritten to match format of UUENCODE.  This meant that several
  30.  * large chunks were removed.  (They were, in fact, unnecessary fluf).
  31.  * CRH.
  32.  *
  33.  * Revision 1.0  93/03/04  00:37:38  CRH
  34.  * Initial revision
  35.  *
  36.  * -------------------------------------------------------------------------- **
  37.  * My understanding of uuencoding/decoding is based on the knowledge that I
  38.  * gained by reading the uuencode source provided with AmigaUUCP V1.16.
  39.  * Many thanks to the authors of that software!  The following are comments
  40.  * from their code, which are included as my way of giving credit where
  41.  * credit is due.
  42.  *
  43.  * /begin/
  44.  * [uuencode.c:]
  45.  * Written by Mark Horton
  46.  * Modified by ajr (Alan J Rosenthatl,flaps@utcsri.UUCP) to use checksums
  47.  * Modified by fnf (Fred Fish,well!fnf) to use Keith Pyle's suggestion for
  48.  * compatibility
  49.  * Modified by bcn (Bryce Nesbitt,ucbvax!hoser!bryce) to enable CTRL-C for
  50.  * Amiga Lattice C.  Added a transparent file size trailer for later check.
  51.  * Changed fopen from "r" to "rb" for Messy-dos machines (thanks to Andrew
  52.  * Wylie)
  53.  *
  54.  * [uudecode.c:]
  55.  * Modified by bcn (Bryce Nesbitt,ucbvax!cogsci!bryce) to fix a misleading
  56.  * error message on the Amiga port, to fix a bug that prevented decoding
  57.  * certain files, to work even if trailing spaces have been removed from a
  58.  * file, to check the filesize (if present), to add some error checking, to
  59.  * loop for multiple decodes from a single file, and to handle common
  60.  * BITNET mangling.  Kludged around a missing string function in Aztec
  61.  * C. Changed "r" to "rb" and "w" to "wb" for Messy-dos machines
  62.  * (Thanks to Andrew Wylie).
  63.  * /end/
  64.  *
  65.  * Also, many thanks to Michael B. Smith, current keeper of AmigaUUCP.
  66.  * -------------------------------------------------------------------------- **
  67.  * This program uses the AuCode module, which contains most of the uuencoding
  68.  * functionality.  90% of *this* file is dedicated to command line parsing,
  69.  * file I/O, and error checking.  This file was written to compile under SAS
  70.  * Amiga C 5.10 or later.  I use the following command line:
  71.  *
  72.  * 1> lc -L AuEncode.c AuCode.c
  73.  *
  74.  * The primary purpose of this program is to demonstrate the utility of the
  75.  * AuCode module.
  76.  *
  77.  * CRH
  78.  * -------------------------------------------------------------------------- **
  79.  */
  80.  
  81. #include <stdio.h>  /* Standard I/O */
  82. #include <string.h> /* Standard string functions */
  83. #include <stdlib.h> /* Standard C functions.  */
  84. #include <time.h>   /* C time functions.  */
  85. #include "AuCode.h" /* Amiga-Unix uuencoding module. */
  86.  
  87. /* -------------------------------------------------------------------------- **
  88.  * Defines...
  89.  *
  90.  *  DEFMODE     - Default file mode.  This value is used on Unix systems.
  91.  *                It represents Unix file protections.  The default value
  92.  *                will work in most cases.
  93.  *  InBUFRSIZE  - Default input buffer size.  As with buffer sizes provided
  94.  *                via the command line, this buffer size will be adjusted
  95.  *                to the greatest whole multiple of au_cdMAXPhrase that is
  96.  *                less than or equal to the original value.
  97.  *  FALSE       - Zero, the standard C value for not true.
  98.  *  TRUE        - If it's not false, it must be true.
  99.  *  DEF_DefName - The "default" default output file name.  This is the
  100.  *                target filename associated with the encoded file.  If no
  101.  *                name is specified, the value assigned to this constant
  102.  *                will be used.
  103.  */
  104. #define DEFMODE 0644
  105. #define InBUFRSIZE 1024
  106. #define FALSE 0
  107. #define TRUE (~0)
  108. #define DEF_DefName "AuEncode.out"
  109.  
  110. /* -------------------------------------------------------------------------- **
  111.  * Macros...
  112.  *  AdjIBufrSize - The input parameter is the requested (or default) input
  113.  *                buffer size.  Our goal is a buffer size that is a whole
  114.  *                multiple of au_cdMAXPhrase (see AuCode.h), so we integer
  115.  *                divide then multiply.  The result is a value that is less
  116.  *                than or equal to the original (4096, in this case), and a
  117.  *                whole multiple of au_cdMAXPhrase.
  118.  *  AdjOBufrSize - The input parameter is the adjusted Input Buffer Size.
  119.  *                Roughly, we calculate the number of Phrases (see
  120.  *                AuCode.h) per input buffer, multiply that by 3 (the
  121.  *                number of overhead characters generated per Phrase), and
  122.  *                then add that number to the number of regular encoded
  123.  *                bytes that will be generated.  Then add two for good
  124.  *                luck.
  125.  *          Max - This is your basic max() macro.  I implement it here in
  126.  *                order to avoid the inclusion of otherwise unnecessary
  127.  *                header files.
  128.  */
  129. #define AdjIBufrSize(B) (((B)/au_cdMAXPhrase)*au_cdMAXPhrase)
  130. #define AdjOBufrSize(B) ((((B)/au_cdMAXPhrase)*3)+((((B)+2)/3)*4)+2)
  131. #define Max(A,B) (((A)>(B))?(A):(B))
  132.  
  133. /* -------------------------------------------------------------------------- **
  134.  * Typedefs...
  135.  *
  136.  *        boolean - Your basic True/False options.
  137.  */
  138.  
  139. typedef unsigned char boolean;
  140.  
  141. /* -------------------------------------------------------------------------- **
  142.  * Globals...
  143.  *  vers    - Program version string.
  144.  *  HelpTxt - An array of help strings.  Should be replaced by a localization
  145.  *            file.
  146.  */
  147.  
  148. const char *vers =
  149.     "\0$VER: AuEncode v1.2.";
  150.  
  151. const char *HelpTxt[] =
  152.   {
  153.   "This program encodes files in uuencode format.  The encoded format consists",
  154.   "of printable characters, which may be transmitted via electronic mail.\n",
  155.   "Usage:\tAuEncode [<infile] [>outfile] [filename] [options]\n",
  156.   "where  [<infile] = redirected input.  AuEncode reads from standard",
  157.   "                   input (stdin).",
  158.   "      [>outfile] = redirected output.  AuEncode writes encoded output to",
  159.   "                   standard output (stdout).",
  160.   "      [filename] = specify the encoded filename.  Uuencoded files",
  161.   "                   contain a default target filename, which is generally",
  162.   "                   the same as the name of the original input file.  The",
  163.   "                   uuDEcoding program will use this name as the default",
  164.   "                   output filename.  If you do not specify a filename, a",
  165.   "                   default filename will be used.",
  166.   "       [options] = command line switches, as follows:\n",
  167.   "        -? or -h     = display this message.",
  168.   "        -b<bufrsize> = set the size of the input buffer.  The output",
  169.   "                       buffer size will be adjusted accordingly.  Note",
  170.   "                       that your specified buffer size will be adjusted",
  171.   "                       for greatest efficiency.",
  172.   "        -m<mode>     = file mode.  Unix systems (and possibly some",
  173.   "                       others) use this to indicate read/write/etc.",
  174.   "                       protections that are to be applied to newly",
  175.   "                       created files.  This program provides a suitable",
  176.   "                       default.  Enter mode as a C style octal numeric",
  177.   "                       value (i.e., with a leading zero).",
  178.   "                       Example: -m0644",
  179.   "        -s           = status display.  The encoder will display the",
  180.   "                       converted byte count (via standard error) as",
  181.   "                       encoding progresses.\n",
  182.   NULL
  183.   }; /* HelpTxt[] */
  184.  
  185. struct {
  186.   char    *defFname;    /* Default file name, stored with encoded file.       */
  187.   boolean  foundName;   /* TRUE if defFname was read from command line.       */
  188.   boolean  statusFlg;   /* Flag: if TRUE, display status info.                */
  189.   long     bSize;       /* Input buffer size.                                 */
  190.   int      mode;        /* Unix file mode to be stored with encoded file.     */
  191.   } Args
  192.          = { DEF_DefName,    /* Initialize to default filename.         */
  193.              FALSE,          /* Filename not yet read from cmd line.    */
  194.              FALSE,          /* No status (do not display status).      */
  195.              InBUFRSIZE,     /* Initialize input buffer size to default.*/
  196.              DEFMODE };      /* File mode (set to default value).       */
  197.  
  198. /* -------------------------------------------------------------------------- **
  199.  * Functions...
  200.  */
  201.  
  202. static void HelpMsg(void)
  203.   /* ------------------------------------------------------------------------ **
  204.    * This function sends the help message text to stdout.  I use stdout for
  205.    * two reasons:
  206.    *                1)  stdout is safe because the user *asked* for help.
  207.    *                2)  The number of displayable characters varies depending
  208.    *                    on font, window size, etc.  Via stdout, the help info
  209.    *                    can be redirected to a file and viewed with a utility
  210.    *                    such as <more>.
  211.    *
  212.    *  Notes:  For the Amiga, this code could be replaced with calls to
  213.    *          locale.library under OS version 2.1 or greater.
  214.    */
  215.   {
  216.   int i;
  217.  
  218.   printf( "[%s]\n\n", &vers[2] );
  219.   for( i = 0; HelpTxt[i]; i++ )
  220.     printf( "%s\n", HelpTxt[i] );
  221.   } /* HelpMsg */
  222.  
  223. static boolean ParseCmdLine( int argc, char *argv[] )
  224.   /* ------------------------------------------------------------------------ **
  225.    * This function parses and responds to command line input.
  226.    *
  227.    *  Input:  argc  - Count of arguments in argv.
  228.    *          argv  - An array of pointers to character strings.
  229.    *                  Basically, the previous two are the same standard C
  230.    *                  input parameters that are fed into main().
  231.    *        ArgsPtr - A pointer to a CLI_Args structure.  This record will
  232.    *                  store the results of the parse.
  233.    *
  234.    *  Output: A boolean.  TRUE if we parsed successfully and can continue
  235.    *          processing, else FALSE.
  236.    *
  237.    *  Notes:  If there was *no* command line input, then we were run from the
  238.    *          workbench.  We don't know how to do that yet, so we error out.
  239.    *          If only one argument, we display a brief message to stderr, and
  240.    *          just keep on going (in case the user used redirection).
  241.    *          If the first argument is a '?', or if a -h or -? switch is read
  242.    *          from the command line, we will dump the error text and exit.
  243.    */
  244.   {
  245.   int i;
  246.  
  247.   if( 0 == argc )   /* No arguments = Amiga Workbench */
  248.     return( FALSE );
  249.  
  250.   if( 1 == argc )   /* One argument may be redirected I/O.  */
  251.     {               /* Output to stderr still safe.         */
  252.     fprintf( stderr, "[%s]\t Type '%s ?' for help.\n", &vers[2], argv[0] );
  253.     fprintf( stderr, "Copyright (C) 1993 Christopher R. Hertel\n" );
  254.     fprintf( stderr, "Please see the Gnu General Public License, " );
  255.     fprintf( stderr, "version 2,\nfor terms and conditions.\n" );
  256.     fprintf( stderr, "Ctrl-C <return> to cancel.\n" );
  257.     return( TRUE ); /* Assume redirected I/O. */
  258.     }
  259.  
  260.   if( '?' == argv[1][0] )   /* If first character of arg #1 is a '?'... */
  261.     {                       /* ...then send help (and exit).            */
  262.     HelpMsg();
  263.     return( FALSE );
  264.     }
  265.  
  266.   for( i = 1; i < argc; i++ )       /* For each command line parameter */
  267.     {
  268.     char *Source = argv[i];
  269.  
  270.     if( '-' == *Source )
  271.       switch( Source[1] )
  272.         {
  273.         case '?':   /* Dump help & return.    */
  274.         case 'H':
  275.         case 'h':
  276.           HelpMsg();
  277.           return( FALSE );
  278.         case 'B':   /* Read new buffer size.  */
  279.         case 'b':
  280.           sscanf( &(Source[2]), "%ld", &(Args.bSize) );
  281.           break;
  282.         case 'M':   /* Read new mode setting. */
  283.         case 'm':
  284.           sscanf( &(Source[2]), "%o", &(Args.mode) );
  285.           break;
  286.         case 'S':   /* Turn on status flag.   */
  287.         case 's':
  288.           Args.statusFlg = TRUE;
  289.           break;
  290.         default:    /* error */
  291.           fprintf( stderr, "? Invalid option: %s\n", Source );
  292.         } /* switch */
  293.     else
  294.       /* Assume that it's a filename. */
  295.       {
  296.       if( Args.foundName )
  297.         fprintf( stderr, "? Extra filename? [%s]\n", Source );
  298.       else
  299.         {
  300.         Args.defFname  = Source;
  301.         Args.foundName = TRUE;
  302.         } /* else */
  303.       } /* else */
  304.     } /* for */
  305.  
  306.   return( TRUE );
  307.   } /* ParseCmdLine */
  308.  
  309. static long Encode( long inBsize, boolean Stats )
  310.   /* ------------------------------------------------------------------------ **
  311.    * The actual encoding of the source file is performed here.
  312.    *
  313.    *  Input:  inBsize - Size of the input buffer.  This value will be modifed
  314.    *                    so that it is a multiple of the Phrase size.  The
  315.    *                    output buffer size will be calculated based on the
  316.    *                    resultant size of the input buffer.  The calculations
  317.    *                    result in optimized input/output buffer sizes.
  318.    *          Stats   - Boolean.  If TRUE, statistics will be displayed via
  319.    *                    stderr.
  320.    *
  321.    *  Output: The total number of converted (input) bytes.
  322.    */
  323.   {
  324.   char *p;            /* Position within sBufr pointer.                       */
  325.   int   readcnt,      /* Number of bytes received (input) per fread() call.   */
  326.         codecnt;      /* Used to count encoded bytes per call to au_cdEncode()*/
  327.   long  total = 0;    /* Running total number of input bytes.                 */
  328.   long  outcount = 0; /* Running total number of generated (output) bytes.    */
  329.   long  t;            /* Time value, used when displaying stats.              */
  330.   long  outBsize;     /* Calculated size of the output buffer.                */
  331.   char *sBufr;        /* Pointer to the source (input) buffer.                */
  332.   char *tBufr;        /* Pointer to the target (output) buffer.               */
  333.  
  334.   inBsize  = AdjIBufrSize(inBsize);       /* Calc correct input buffer size.*/
  335.   inBsize  = Max( inBsize, au_cdMAXPhrase );  /* Make sure size > 0.        */
  336.   outBsize = AdjOBufrSize(inBsize);       /* Calc correct output buffer size*/
  337.   sBufr = (char *)malloc( inBsize );      /* Allocate input buffer.         */
  338.   tBufr = (char *)malloc( outBsize );     /* Allocate output buffer.        */
  339.   if( !sBufr || !tBufr )                  /* If either allocation failed... */
  340.     {
  341.     fprintf( stderr, "? Buffer allocation failure.\n" );
  342.     if( sBufr ) free( sBufr );
  343.     if( tBufr ) free( tBufr );
  344.     return( 0L );
  345.     }
  346.  
  347.   if( Stats )   /* If Stats requested, display buffer sizes and get time.   */
  348.     {
  349.     fprintf( stderr,
  350.              "Input buffer: [%ld] bytes.  Output buffer: [%ld] bytes.\n",
  351.              inBsize, outBsize  );
  352.     t = time( NULL );
  353.     }
  354.  
  355.   while( readcnt = fread( sBufr, 1, inBsize, stdin ) )    /* Read a chunk.    */
  356.     {
  357.     codecnt = 0;                /* We've encoded 0 bytes of the input buffer. */
  358.     p = sBufr;                  /* Point to start of input buffer.            */
  359.     while( readcnt = (readcnt-codecnt) )  /* Encode as much as possible.      */
  360.       {                                   /* If any are left over we'll loop. */
  361.       codecnt = au_cdEncode( p, readcnt, tBufr, outBsize );
  362.       p = &p[codecnt];                    /* Point to first non-encoded byte  */
  363.                                           /* within sBufr.                    */
  364.       total += codecnt;                   /* Add encoded byte count to total. */
  365.       fputs( tBufr, stdout );             /* Write 'em. */
  366.  
  367.       if( Stats )                         /* Running statistics. */
  368.         {
  369.         outcount += strlen( tBufr );
  370.         fprintf( stderr, "Read: [%ld], Written: [%ld].\r", total, outcount );
  371.         }
  372.       }
  373.     }
  374.  
  375.   if( Stats )                             /* Finished: concluding statistics. */
  376.     {
  377.     t = time(NULL)-t;
  378.     fprintf( stderr, "Total: %ld bytes read & encoded, ", total );
  379.     fprintf( stderr, "%ld bytes written ", outcount );
  380.     if( t )
  381.       fprintf( stderr, "in %ld second%s.\n", t, (1==t)?(""):("s") );
  382.     else
  383.       fprintf( stderr, "in less than one second.\n" );
  384.     }
  385.   return( total );
  386.   } /* Encode */
  387.  
  388. int main( int argc, char **argv )
  389.   /* ------------------------------------------------------------------------ **
  390.    * Mainline.
  391.    *
  392.    * The following steps are performed:
  393.    *    1)  Parse the command line.
  394.    *    2)  Write the encoding header.
  395.    *    3)  Encode the file (stdin to stdout).
  396.    *    4)  Write the encoding trailer.
  397.    *
  398.    * ------------------------------------------------------------------------ **
  399.    */
  400.   {
  401.   long ByteCount;                   /* Total bytes read from input. */
  402.  
  403.   if( ParseCmdLine( argc, argv ) )  /* Parse the command line.      */
  404.     {
  405.     /* Write code header to output. */
  406.     printf( "\nbegin %o %s\n", Args.mode, Args.defFname );
  407.  
  408.     /* Encode the input file to the output file.  */
  409.     ByteCount = Encode( Args.bSize, Args.statusFlg );
  410.  
  411.     /* Write code trailer to output.  */
  412.     printf( "``\nend\nsize %ld\n", ByteCount );
  413.     }
  414.   return( 0 );
  415.   } /* main */
  416.  
  417. /* ========================================================================== */
  418.