/*****************************************************************************\
*                                                                             *
*  Name   : uucat                                                             *
*  Author : Chris Koeritz                                                     *
*                                                                             *
*  Purpose:                                                                   *
*                                                                             *
*    Takes a uuencoded file, decodes it and sends it to standard out.         *
*                                                                             *
*******************************************************************************
* Copyright (c) 1991-$now By Author.  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 is online at:      *
*     http://www.fsf.org/copyleft/gpl.html                                    *
* Please send any updates to: fred@gruntose.com                               *
\*****************************************************************************/

// i cannot verify absolutely that i wrote this; it was so long ago, but i
// think the formatting does match with my early version of C formatting,
// which was influenced by my painful years of experience with pascal and
// modula-2.  if you have details to the contrary, such as an ancient version
// of uucat whose age is documented, i would change the attribution.
//   --chris koeritz.

#include <data_struct/static_memory_gremlin.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

HOOPLE_STARTUP_CODE;

#define LENGTH 150

int uuread(FILE *infile)
{
  char *s2, *s1, *s0, *tmp_s;
  int length;
  static char s[3 * (LENGTH + 1)];
  static int echo_on = false, started = false, just_finished = false;
  static int line_length = 0, lines_to_go = 0;

  s0 = s;
  s1 = s0 + (LENGTH + 1);
  s2 = s1 + (LENGTH + 1);

  s0[0] = s1[0] = s2[0] = '\0';  /* Clear strings */

  while (fgets(s0, LENGTH, infile) != NULL)
  {
    s0[LENGTH] = '\0';  /* Make sure string is terminated */

    if (just_finished)
    {
      if (strncmp(s0, "size ", 5) == 0)
      {
        fputs(s0, stdout);
        s0[0] = '\0';
      }
      just_finished = false;
    }

    if (!started)
    {
      if (strncmp(s0, "begin ", 6) == 0)
      {
        started = echo_on = true;
        line_length = 0;
        lines_to_go = 0;
      }
    }
    else  /* started */
    {
      length = int(strlen(s0));
      if (line_length == 0)
        line_length = length;

      if (echo_on)
      {
        lines_to_go = 0;
        if (s0[0] != 'M' || length != line_length)
        {
          echo_on = false;
          lines_to_go = 2;  /* Lines to go before 'end' is expected */
          if (s0[0] == ' ' || s0[0] == '`')
            lines_to_go = 1;
        }
      }
      else  /* !echo_on */
      {
        if (s0[0] == 'M' && length == line_length)
          echo_on = true;
        else if (lines_to_go > 0)
        {
          if (lines_to_go == 2)
          {
            if (s0[0] == ' ' || s0[0] == '`')
              lines_to_go = 1;
            else
              lines_to_go = 0;  /* Unexpected line, so break off */
          }
          else if (lines_to_go == 1)
          {
            if (strcmp(s0, "end\n") == 0)
            {
              fputs(s2, stdout);
              fputs(s1, stdout);
              fputs(s0, stdout);
              lines_to_go = 0;  /* Done. Break off */
              just_finished = true;
              started = false;
            }
            else
              lines_to_go = 0;  /* Unexpected line, so break off */
          }
        }
      }
    }

    if (echo_on)
    {
      fputs(s0, stdout);
      s0[0] = '\0';
    }

    tmp_s = s2;
    s2 = s1;
    s1 = s0;
    s0 = tmp_s;
  }
  return 0;
}

int main(int argc, char *argv[])
{
  int error, argno;
  FILE *infile;

  if (argc < 2)
    return uuread(stdin);
  else
  {
    error = false;
    for (argno = 1; !error && argno < argc; argno++)
    {
      if ((infile = fopen(argv[argno], "r")) == NULL)
      {
        error = true;
        fprintf(stderr, "uucat: Can't open '%s' for input.\n", argv[argno]);
      }
      else
      {
        return uuread(infile);
        fclose(infile);
      }
    }
  }

  return 0;
}

#ifdef __BUILD_STATIC_APPLICATION__
  // static dependencies found by buildor_gen_deps.sh:
  #include <basis/array.cpp>
  #include <basis/byte_array.cpp>
  #include <basis/callstack_tracker.cpp>
  #include <basis/chaos.cpp>
  #include <basis/convert_utf.cpp>
  #include <basis/definitions.cpp>
  #include <basis/earth_time.cpp>
  #include <basis/guards.cpp>
  #include <basis/istring.cpp>
  #include <basis/log_base.cpp>
  #include <basis/memory_checker.cpp>
  #include <basis/mutex.cpp>
  #include <basis/object_base.cpp>
  #include <basis/outcome.cpp>
  #include <basis/packable.cpp>
  #include <basis/portable.cpp>
  #include <basis/sequence.cpp>
  #include <basis/set.cpp>
  #include <basis/utility.cpp>
  #include <basis/version_record.cpp>
  #include <data_struct/amorph.cpp>
  #include <data_struct/bit_vector.cpp>
  #include <data_struct/byte_hasher.cpp>
  #include <data_struct/configurator.cpp>
  #include <data_struct/hash_table.cpp>
  #include <data_struct/pointer_hash.cpp>
  #include <data_struct/stack.cpp>
  #include <data_struct/static_memory_gremlin.cpp>
  #include <data_struct/string_hash.cpp>
  #include <data_struct/string_hasher.cpp>
  #include <data_struct/string_table.cpp>
  #include <data_struct/symbol_table.cpp>
  #include <data_struct/table_configurator.cpp>
  #include <loggers/console_logger.cpp>
  #include <loggers/file_logger.cpp>
  #include <loggers/locked_logger.cpp>
  #include <loggers/null_logger.cpp>
  #include <loggers/program_wide_logger.cpp>
  #include <opsystem/byte_filer.cpp>
  #include <opsystem/command_line.cpp>
  #include <opsystem/critical_events.cpp>
  #include <opsystem/directory.cpp>
  #include <opsystem/filename.cpp>
  #include <opsystem/ini_config.cpp>
  #include <opsystem/ini_parser.cpp>
  #include <opsystem/path_configuration.cpp>
  #include <opsystem/rendezvous.cpp>
  #include <textual/byte_format.cpp>
  #include <textual/parser_bits.cpp>
  #include <textual/string_manipulation.cpp>
  #include <textual/tokenizer.cpp>
#endif // __BUILD_STATIC_APPLICATION__

