/* -------------------------------------------------------------------------- **
 * Program: AuDecode
 *
 * UUdecoding for the Amiga.
 *
 *  Written by Christopher R. Hertel
 *  Email: crh@bubble.mooses.affinity.mn.org
 * -------------------------------------------------------------------------- **
 *  Copyright (C) 1994  Christopher R. Hertel
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 * -------------------------------------------------------------------------- **
 *
 * $Log:	AuDecode.c,v $
 * Revision 1.2  94/02/15  21:42:29  CRH
 * Quick change to the construction of the version string.
 * 
 * Revision 1.1  94/02/15  21:39:24  CRH
 * Small fixes to the help messages.
 * ,
 *
 * Revision 1.0  94/02/06  16:26:16  CRH
 * Initial revision
 *
 * -------------------------------------------------------------------------- **
 * My understanding of uuencoding/decoding is based on the knowledge that I
 * gained by reading the uuencode/decode source provided with AmigaUUCP
 * V1.16. Many thanks to the authors of that software!  The following are
 * comments from their code, which are included as my way of giving credit
 * where credit is due.
 *
 * /begin/
 * Written by Mark Horton
 * Modified by ajr (Alan J Rosenthatl,flaps@utcsri.UUCP) to use checksums
 * Modified by fnf (Fred Fish,well!fnf) to use Keith Pyle's suggestion for
 * compatibility
 * Modified by bcn (Bryce Nesbitt,ucbvax!hoser!bryce) to enable CTRL-C for
 * Amiga Lattice C.  Added a transparent file size trailer for later check.
 * Changed fopen from "r" to "rb" for Messy-dos machines (thanks to Andrew
 * Wylie)
 * [uudecode.c:]
 * Modified by bcn (Bryce Nesbitt,ucbvax!cogsci!bryce) to fix a misleading
 * error message on the Amiga port, to fix a bug that prevented decoding
 * certain files, to work even if trailing spaces have been removed from a
 * file, to check the filesize (if present), to add some error checking, to
 * loop for multiple decodes from a single file, and to handle common
 * BITNET mangling.  Kludged around a missing string function in Aztec
 * C. Changed "r" to "rb" and "w" to "wb" for Messy-dos machines
 * (Thanks to Andrew Wylie).
 * /end/
 *
 * -------------------------------------------------------------------------- **
 * This program uses the AuCode module, which contains most of the uudecoding
 * functionality.  90% of *this* file is dedicated to command line parsing,
 * file I/O, and error checking.  This file was written to compile under SAS
 * Amiga C 5.10 or later.  I use the following command line:
 *
 *    lc -L AuDecode.c AuCode.c
 *
 * CRH
 * -------------------------------------------------------------------------- **
 */

#include <stdio.h>      /* Standard I/O */
#include <string.h>     /* Standard string functions */
#include <stdlib.h>     /* Standard C functions.  */
#include <ctype.h>      /* C iswhatsit() macros.  */
#include <time.h>       /* C time functions.      */
#include <sys/types.h>  /* Unix types.            */
#include <sys/stat.h>   /* File status functions. */
#include "AuCode.h"     /* Amiga-Usenet coding module. */

/* -------------------------------------------------------------------------- **
 * Defines...
 *
 *  FALSE       - Zero, the standard C value for not true.
 *  TRUE        - If it's not false, it must be true.  Mustn't it?
 *
 * MAXCS_WARN   - Maximum number of checksum warning messages to report
 *                before we get annoying.
 */
#define FALSE 0
#define TRUE (~0)
#define MAXCS_WARN 10

/* -------------------------------------------------------------------------- **
 * Macros...
 *          Max - This is your basic max() macro.  I implement it here in
 *                order to avoid the inclusion of extra header files.
 */
#define Max(A,B) (((A)>(B))?(A):(B))

/* -------------------------------------------------------------------------- **
 * Typedefs...
 *
 *        boolean - Your basic True/False options.
 */

typedef char boolean;

/* -------------------------------------------------------------------------- **
 * Global constants...
 *  vers    - Program version string.
 *  HelpTxt - An array of help strings.
 */

const char *vers = "\0$VER$Id: AuDecode.c,v 1.2 94/02/15 21:42:29 CRH Exp $";

const char *HelpTxt[] =
  {
  "Copyright (C) 1994 by  Christopher R. Hertel\n",
  "This program decodes uuencoded files.\n",
  "Usage:",
  "\tAuDecode [options] [infile]\n",
  "where  [infile]  = an input file.  This is the source files that is",
  "                   to be decoded.  If no file is specified, then ",
  "                   standard input will be read.",
  "       [options] = command line switches, as follows:\n",
  "      -? or -h     = display this help message.",
  "      -a           = if the output file already exists, open it in",
  "                     append mode.  The default is to overwrite it.",
  "      -d<n>        = decode specific occurance.  If the input stream",
  "                     contains several encoded files, this switch will",
  "                     allow you to select a single encoded file to",
  "                     decode.  The default is to decode all files.",
  "      -i           = display index only.  This switch causes AuDecode",
  "                     to scan the input for encoded files.  The files",
  "                     *will not be decoded*, but a sequentially numbered",
  "                     list of names will be written to standard output.",
  "      -m<mode>     = output file mode (Unix protection scheme).",
  "                     (Not fully implemented yet.)",
  "      -n<filename> = specify the output filename.  Uuencoded files",
  "                     contain a default target filename, which is",
  "                     generally the same as the input file name received",
  "                     by the encoder.  The decoding program will use this",
  "                     name as the default output filename.  The -n option",
  "                     allows you to override the default target filename.",
  "               Note: If the -d option is not specified, -n assumes -d1.",
  "      -o           = send output to the standard output device (stdout).",
  "                     This option is similar to -n, except that it",
  "                     instructs AuDecode to redirect output to standard",
  "                     output, rather than to a file.  This is useful if",
  "                     you wish to use command line redirection.  This",
  "                     option will override -n.",
  "               Note: Unlike -n, -o does not assume -d1.  If you do not",
  "                     specify an occurance, all encoded files will be",
  "                     decoded to stdout.",
  "      -s           = status display.  The encoder will display the",
  "                     converted byte count (via standard error) as",
  "                     decoding progresses.",
  NULL
  }; /* HelpTxt[] */

/* -------------------------------------------------------------------------- **
 * Globals Variables...
 */

        /* Process parameters      Default actions                            */
boolean AppendFlg = FALSE;      /* Open output in overwrite mode.             */
boolean DecodeFlg = TRUE;       /* Yes we decode (no, we don't just index).   */
boolean StatusFlg = FALSE;      /* No, we don't display statistics.           */
boolean StdOutFlg = FALSE;      /* Don't send output to stdout.               */
int     OutFlMode = 0;          /* Output file mode.  0=default.              */
int     SelectOcc = 0;          /* Zero means "select all occurances".        */
int     badcsums  = 0;          /* Number of bad checksums encountered.       */

        /* File and path specifications.                                      */
char   *OutFlName = NULL;       /* Output file name (default=encoded name).   */
char   *SrcFlName = NULL;       /* Name of input file (default=stdin).        */

        /* Processing buffers. */
char    linebufr[au_cdMAX_LINE];    /* Input buffer.    */
char    tmpbufr[au_cdMAX_LINE];     /* Scratch buffer.  */


/* -------------------------------------------------------------------------- **
 * Functions...
 */

static void HelpMsg( void )
  /* ------------------------------------------------------------------------ **
   * This function sends the help message text to stderr.
   *
   *  Notes:  For the Amiga, this code could easily be replaced with calls to
   *          locale.library under OS version 2.1 or greater.
   * ------------------------------------------------------------------------ **
   */
  {
  int i;

  printf( "%s\n", &vers[2] );       /* Print version info.  */
  for( i = 0; HelpTxt[i]; i++ )     /* Display each line of help text.  */
    printf( "%s\n", HelpTxt[i] );
  } /* HelpMsg */

static boolean ParseCmdLine( int argc, char *argv[] )
  /* ------------------------------------------------------------------------ **
   * This function parses and responds to command line input.
   *
   *  Input:  argc  - Count of arguments in argv.
   *          argv  - An array of pointers to character strings.
   *                  Basically, the previous two are the same standard C
   *                  input parameters that are fed into main().
   *
   *  Output: A boolean.  TRUE if we parsed successfully and can continue
   *          processing, else FALSE.
   *
   *  Notes:  If there *was* no command line input, then we were run from the
   *          workbench.  We don't know how to do that yet, so we error out.
   *          If only one argument, we display a brief message to stderr, and
   *          just keep on going (assume redirected input).
   *          If the first argument is a '?', or if a -h or -? switch is read
   *          from the command line, we will dump the help text and exit.
   * ------------------------------------------------------------------------ **
   */
  {
  int i;
  char *Source;

  if( 0 == argc )   /* No arguments = Amiga Workbench */
    return( FALSE );

  if( 1 == argc )   /* A signle argument may indicate redirected I/O. */
    {               /* Output to stderr is still safe.  */
    fprintf( stderr, "[%s] Type '%s ?' for help.\n", &vers[2], argv[0] );
    fprintf( stderr, "Copyright (C) 1994 Christopher R. Hertel\n" );
    fprintf( stderr, "Ctrl-C <return> to cancel.\n" );
    return( TRUE );
    }

  if( '?' == argv[1][0] )   /* If first character of argv[1] is a '?'...*/
    {                       /* ...then send help (and exit).            */
    HelpMsg();
    return( FALSE );
    }

  /* Now we can attempt to parse the command line. */

  for( i = 1; i < argc; i++ )
    /* For each command line parameter... */
    {
    Source = argv[i];
    if( '-' == *Source )
      switch( Source[1] )
        {
        case '?':                   /* Dump help text & return */
        case 'H':
        case 'h':
          HelpMsg();
          return( FALSE );
        case 'A':                   /* Open output files in append mode.    */
        case 'a':
          AppendFlg = TRUE;
          break;
        case 'D':                   /* Select a specific occurance.         */
        case 'd':
          sscanf( &(Source[2]), "%d", &(SelectOcc) );
          break;
        case 'I':                   /* Produce index only.                  */
        case 'i':
          DecodeFlg = FALSE;
          break;
        case 'S':                   /* Display status information.          */
        case 's':
          StatusFlg = TRUE;
          break;
        case 'M':                   /* Read mode from cmd line.             */
        case 'm':
          if( OutFlMode )
            fprintf( stderr, "Warning: Multiple -m values; last one wins.\n" );
          sscanf( &Source[2], "%o", &OutFlMode );
          break;
        case 'N':                   /* Set output file name.                */
        case 'n':
          if( StdOutFlg )
            fprintf( stderr, "Warning: -o and -n conflict; -o wins.\n" );
          else
            {
            if( OutFlName )
              {
              fprintf( stderr, "Error: Output filename specified twice.\n" );
              return( FALSE );
              }
            else
              OutFlName = &Source[2];
            }
          break;
        case 'O':                   /* Output to stdout.                    */
        case 'o':
          if( OutFlName )
            {
            fprintf( stderr, "Warning: -o and -n conflict; -o wins.\n" );
            OutFlName = NULL;
            }
          StdOutFlg = TRUE;
          break;
        default:
          fprintf( stderr, "Warning: Unknown option: %s\n", Source );
        } /* switch */
    else                            /* Get input file name.                 */
      {
      if( SrcFlName )
        {
        fprintf( stderr, "Error: Multiple input files specified.\n" );
        fprintf( stderr, "[%s], [%s]\n", SrcFlName, Source );
        fprintf( stderr, "This program handles only one source at a time.  " );
        fprintf( stderr, "Sorry.\n" );
        return( FALSE );
        }
      SrcFlName = Source;
      }
    } /* for */

  return( TRUE );
  } /* ParseCmdLine */

char *ParseBegin( char *Source, int *modeptr )
  /* ------------------------------------------------------------------------ **
   * This function identifies the file name and file mode that are typically
   * stored in the uuencoded 'begin' line.
   *
   *  Input:  Source  - a pointer to the string buffer that contains the
   *                   'begin' line.
   *          modeptr - a pointer to an integer.  The mode will be read from
   *                    the begin line, and stored in the location indicated
   *                    by modeptr.
   *
   *  Output: A pointer to the file name substring of the begin line.  That
   *          is, a pointer to the position within Source[] at which the file
   *          name is assumed to start.
   *
   *  Notes:  This function will remove any trailing control characters from
   *          the Source string by placing a nul byte ('\0') after the last
   *          non-control character.
   *
   *          This function should probably be moved into the AuCode module.
   * ------------------------------------------------------------------------ **
   */
  {
  int   i;
  char *p;

  sscanf( &Source[5], " %o", modeptr );     /* Read Mode */
  for( i = 5; isspace( Source[i] ); i++ );  /* Skip pre-mode spaces.    */
  for(      ; isdigit( Source[i] ); i++ );  /* Skip mode.               */
  for(      ; isspace( Source[i] ); i++ );  /* Skip post-mode spaces.   */
  p = &Source[i];                           /* Should be start of name. */
  for( i = strlen(p); iscntrl(p[i]); --i ); /* Find last non-controlchar*/
  p[i+1] = '\0';                            /* Trim off control chars   */
  return( p );
  } /* ParseBegin */

int ParseSize( void )
  /* ------------------------------------------------------------------------ **
   * This function parses the 'size' line that is often included folloing a
   * uuencoded block.
   *
   *  Input:  None.
   *
   *  Ouptut: The size, in bytes, of the original file as reported by the
   *          'size' line, or zero (0) if there was no size line.
   *
   *          This function should probably be moved into the AuCode module.
   * ------------------------------------------------------------------------ **
   */
  {
  int tmp = 0;

  if( 0 == strncmp( "size", linebufr, 4 ) )   /* If line starts w/"size"  */
    sscanf( &linebufr[4], " %ld", &tmp );     /* Read size from string.   */
  return( tmp );
  } /* ParseSize */

boolean Index( FILE *inF )
  /* ------------------------------------------------------------------------ **
   * This function works its way through the occurance of an encoded output
   * file within the input file.
   *
   *  Input:
   *    inF   - A pointer to the input file (so that we can read from it).
   *
   *  Output:   TRUE if the end of the occurance was found.  FALSE if the
   *            end of the input file was found before the end of the
   *            encoded occurance.
   * ------------------------------------------------------------------------ **
   */
  {
  char *name;
  int   namelen;
  int   mode   = 0;
  int   size   = 0;
  long  bytcnt = 0;

  name = ParseBegin( linebufr, &mode );
  namelen = strlen( name );

  if( namelen > 35 )                        /* Print file name.         */
    printf( "%-34.34s+  ", name );
  else
    printf( "%-35.*s  ", namelen, name );

  if( mode > 0 )
    printf( "%4o  ", mode );                /* Print file mode.         */
  else
    printf( "????  " );                     /* ...or ?s if no mode.     */

  if( StatusFlg )
    /* Status "ON" version. */
    {
    int lncnt  = 0;

    for(;;)
      {
      if( !(fgets( linebufr, au_cdMAX_LINE, inF )) )
        {                                     /* EOF before end/n = error.*/
        fprintf( stderr,
                 "\nError: Found End-of-File before end of encoded data.\n" );
        return( FALSE );
        }
      if( strncmp( "end\n", linebufr, 4 ) )
        {
        lncnt++;
        bytcnt += au_cdDECD( linebufr[0] );
        if( 0 == (lncnt % 10) )
          printf( "%9ld\b\b\b\b\b\b\b\b\b", bytcnt );
        }
      else
        break;
      }
    }
  else
    /* Status "OFF" version (identical to above, but without status output). */
    {
    for(;;)
      {
      if( !(fgets( linebufr, au_cdMAX_LINE, inF )) )
        {                                     /* EOF before end/n = error.*/
        fprintf( stderr,
                 "\nError: Found End-of-File before end of encoded data.\n" );
        return( FALSE );
        }
      if( strncmp( "end\n", linebufr, 4 ) )
        bytcnt += au_cdDECD( linebufr[0] );
      else
        break;
      }
    }
  printf( "%9ld  ", bytcnt );                   /* Print totaled byte count.*/

  if( fgets( linebufr, au_cdMAX_LINE, inF ) )   /* Try to read past "end".  */
    size = ParseSize();                         /* ...and read "size" line. */

  if( size )                                    /* Print recorded size.     */
    printf( "%12ld%s\n", size, (size==bytcnt)?"":" *" );
  else
    printf( "-Not provided-\n" );               /* No size value found.     */

  return( TRUE );
  } /* Index */

void ChecksumWarn( int lineno )
  /* ------------------------------------------------------------------------ **
   * This function prints a checksum warning message.  Note that each file
   * is allowed a maximum of MAXCS_WARN warnings.
   *
   *  Input:  lineno  - The line number.  This will be printed as part of the
   *                    error message.
   *
   *  Output: None.
   * ------------------------------------------------------------------------ **
   */
  {
  badcsums++;
  if( badcsums <= MAXCS_WARN )
    {
    fprintf( stderr, "Warning: Bad checksum at encoded line #%d.\n", lineno );
    if( badcsums == MAXCS_WARN )
      fprintf( stderr, "         Maximum checksum warnings reached.\n" );
    }
  } /* ChecksumWarn */

boolean Decode( FILE *inF )
  /* ------------------------------------------------------------------------ **
   * This function decodes a single encoded file, from 'begin' line to 'end'
   * and (if present) 'size'.
   *
   *  Input:  inF - Input file handle.
   *
   *  Output: TRUE if decoding was successful, else FALSE.
   * ------------------------------------------------------------------------ **
   */
  {
  FILE *fOut = stdout;
  char *name;
  int   mode   = 0;
  int   size   = 0;
  int   lncnt  = 0;
  int   ErrCD;
  long  cnt;
  long  totcnt = 0;

  badcsums = 0;                             /* Init checksum warning count.   */
  name = ParseBegin( linebufr, &mode );     /* Get filename & mode from input */
  if( OutFlMode )                           /* Override mode? */
    mode = OutFlMode;

  if( StdOutFlg )                           /* Override filename with stdout? */
    name = "Standard output";
  else
    {
    if( OutFlName )                         /* Override filename with -n?     */
      name = OutFlName;
    else
      name = strdup( name );
    fOut = fopen( name, (AppendFlg)?"ab":"wb" );
    if( !fOut )
      {
      fprintf( stderr, "Error: Unable to open output file: [%s].\n", name );
      return( FALSE );
      }
    }

  if( StatusFlg )
    /* Status "ON" version. */
    {
    for(;;)
      {
      if( !(fgets( linebufr, au_cdMAX_LINE, inF )) )
        {                                     /* EOF before end/n = error.*/
        fprintf( stderr,
                 "Error: Found End-of-File before end of encoded data.\n" );
        return( FALSE );
        }
      if( strncmp( "end\n", linebufr, 4 ) )
        {
        lncnt++;
        (void)au_cdCleanln( linebufr, au_cdMAX_LINE, tmpbufr, au_cdMAX_LINE );
        cnt = au_cdDecodeln( tmpbufr, linebufr, au_cdMAX_LINE, &ErrCD );
        if( au_cdErr_Checksum == ErrCD )
          ChecksumWarn( lncnt );
        if( 1 != fwrite( linebufr, cnt, 1, fOut ) )
          {
          fprintf( stderr, "Error: Failure writing to output file.\n" );
          perror( "Error" );
          return( FALSE );
          }
        totcnt += cnt;
        if( 0 == (lncnt % 10) )
          fprintf( stderr, "%-30.30s: %9ld\r", name, totcnt );
        }
      else
        break;
      }
    fprintf( stderr, "%-30.30s: %9ld bytes.\n", name, totcnt );
    }
  else
    /* Status "OFF" version (identical to above, but without status output). */
    {
    for(;;)
      {
      if( !(fgets( linebufr, au_cdMAX_LINE, inF )) )
        {                                     /* EOF before end/n = error.*/
        fprintf( stderr,
                 "Error: Found End-of-File before end of encoded data.\n" );
        return( FALSE );
        }
      if( strncmp( "end\n", linebufr, 4 ) )
        {
        lncnt++;
        (void)au_cdCleanln( linebufr, au_cdMAX_LINE, tmpbufr, au_cdMAX_LINE );
        cnt = au_cdDecodeln( tmpbufr, linebufr, au_cdMAX_LINE, &ErrCD );
        if( au_cdErr_Checksum == ErrCD )
          ChecksumWarn( lncnt );
        if( 1 != fwrite( linebufr, cnt, 1, fOut ) )
          {
          fprintf( stderr, "Error: Failure writing to output file.\n" );
          perror( "system" );
          return( FALSE );
          }
        totcnt += cnt;
        }
      else
        break;
      }
    }

  if( !StdOutFlg )
    {
    fclose( fOut );
    /* mode handling goes here. */
    /* chmod( name, mode );     */
    }

  if( fgets( linebufr, au_cdMAX_LINE, inF ) )   /* Try to read past "end".  */
    size = ParseSize();                         /* ...and read "size" line. */

  if( size && (size != totcnt) )
    {
    fprintf( stderr, "Warning: Decoded size [%d] ", totcnt );
    fprintf( stderr, "does not match original size [%d].\n", size );
    }

  return( TRUE );
  } /* Decode */

long ScanFile( FILE *inF )
  /* ------------------------------------------------------------------------ **
   * This function scans the input file (inF) for 'begin' lines (which
   * indicate the start of a uuencoded file).  For each 'begin' line, this
   * function calls either Decode() or Index() to process the encoded file.
   *
   *  Input:  inF - Input file handle.
   *
   *  Output: The number of encoded files found and processed.
   * ------------------------------------------------------------------------ **
   */
  {
  boolean tmp = TRUE;         /* Did Decode() or Index() succeed/fail?    */
  long  count = 0;            /* Number of files processed.               */

  while( fgets( linebufr, au_cdMAX_LINE, inF ) )        /* While not EOF  */
    {
    while( 0 == strncmp( "begin", linebufr, 5 ) )
      {
      /* While current line is "begin". */
      count++;
      if( (0 != SelectOcc) && (SelectOcc != count) )
        {
        /* If we've selected a specific occurance, and this isn't it, then
         * skip this one.
         */
        if( !fgets( linebufr, au_cdMAX_LINE, inF ) )
          return( count );     /* return if there are no more to be done. */
        }
      else
        {
        /* These functions will each read one entire encoded block. */
        if( DecodeFlg )
          {
          Decode( inF );
          if( badcsums > 0 )
            fprintf( stderr, "Warning: There were %d checksum errors.\n",
                     badcsums );
          }
        else
          {
          printf( "%5d  ", count );               /* Print index number.      */
          Index( inF );
          }
        /* Exit on error or if we've found & processed the selected occurance.*/
        if( !tmp || (SelectOcc == count) )
          return( count );
        } /* else */
      } /* while */
    } /* while */

  return( count );
  } /* ScanFile */

int main( int argc, char *argv[] )
  /* ------------------------------------------------------------------------ **
   * Mainline.
   * ------------------------------------------------------------------------ **
   */
  {
  FILE *inF = stdin;
  long  count;
  long  t   = time( NULL );

  if( !ParseCmdLine( argc, argv ) )         /* Parse the command line. */
    return( 0 );                            /* If parse failed, exit.  */

  if( (OutFlName) && (0 == SelectOcc) )     /* Default action.  If the user   */
    SelectOcc = 1;                          /* specified an output file name, */
                                            /* but didn't specify a specific  */
                                            /* file occurance, we assume file */
                                            /* occurance 1. */

  if( SrcFlName )                           /* Open user-specified source file*/
    {
    inF = fopen( SrcFlName, "rb" );
    if( !(inF) )
      {
      fprintf( stderr, "Error: Unable to open file \"%s\".\n", SrcFlName );
      return( 0 );
      }
    }

  if( !(DecodeFlg) )  /* Display index header. */
    {
    printf( "\nSource File: %s\n", (SrcFlName)?(SrcFlName):("Standard Input") );
    printf( "Index  Name                                 Mode" );
    printf( "    Bytes    Reported Bytes\n" );
    printf( "-----  -----------------------------------  ----" );
    printf( "  ---------  --------------\n" );
    }

  count = ScanFile( inF );          /* Scan the input file for encoded files. */

  if( SrcFlName )                   /* Close the input file. */
    fclose( inF );

  if( StatusFlg )
    {
    t = time( NULL ) - t;
    fprintf( stderr, "\nTotal: %d encoded files processed ", count );
    switch( t )
      {
      case 0:  fprintf( stderr, "in less than one second.\n" );
               break;
      case 1:  fprintf( stderr, "in 1 second.\n" );
               break;
      default: fprintf( stderr, "in %ld seconds.\n", t );
      }
    }

  return( 0 );
  } /* main */

/* ========================================================================== */
