/* Copyright 2001, 2007 Alexandre Oliva <aoliva@redhat.com>
 *
 * This file 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 3 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, see <http://www.gnu.org/licenses/>.
 *
 * This program is, erhm, useful to convert files encoded in base64
 * to/from a far more compressible format, namely, DNA.  The idea is
 * that each base64 character represents exactly 6 bits, whereas, in
 * DNA sequences, each base represents 2 bits, but each triple of
 * bases represents an amino acid in a protein.  But 3 * 2 == 6.  I
 * couldn't let such a coincidence go by.
 *
 * Besides converting base64 to/from DNA code, this program can also
 * convert DNA or base64 directly from/to plain-text or binary
 * octet-streams.  I.e., it's useful as a base64 encoder/decoder.
 * It's also useful as a dos2unix/unix2dos converter: `dnacode -tO' or
 * `dnacode -oT' (that's a consequence of base64 requiring
 * end-of-lines to be represented as CR+LF).
 *
 * This program is also very useful in case `cat' is buggy or missing:
 * `dnacode -oO' will behave exactly like any `cat'.  Moreover, if
 * `cat' is present, the command `dnacode -oD < /bin/cat' will get you
 * the DNA sequence of Felis catus.
 *
 * To see how this program works, run the following DNA sequence
 * through `dnacode -dT'.  The original DNA sequence comes from
 * http://www.netfunny.com/rhf/jokes/91q3/genome.html.

AAGGAATCAGTTAGGGAGAACAGACCCCCATCCAACCATGCCTTCACACATGCAACAGTGCAGAAAGGAATCAGA
AAGGGAAGGAATCAGAAAGGGAGAACAGACTCCCGTCCGACCGTGAGAACACTCGCCCGTGCGTTCGTCCGCCAA
GGAATCAGAAAGGGAGAACCCGCGCCCTAGCTATCGGCCGTTCGTGAGAAATAGAGTGATACAAGGAATCAGAAA
GGGAAGGAATCAGAAAGGGAGAAAGGACAATAGGCAGAACACTCGTTCGCAAAGGAATCAGAAAGGGAGTTAAGG
AATCAGAAAAGGAATCAGTTAGGGAGAACCAGCGCCCTCGCGGCCTATCGGCCGTTCGTGAGAACGGACGGCCTA
TCTCACGTTCTAGCTGCATGGAAGGAATCAGAAAGGGAAGGAATCAGAAAGGGAGAAATAAATAAATAAATAAAG
TCATAAATAAAGTCATAAATACAGAAATAAATAAATGGATAAATAAAGAAAGAAATACAGTGATAAAGAAAGAAC
AACCGCACGACCGTCAGTGAAGGAATCAGAAAGGGAGAAATAAATAAATAAATAAAGTCATAAATAAAGTCATAA
ATAGAGAAATACATAAATGGATAAATAAAGAAAGAAATACAGTGATACAGAAAGAACACCCTCGCGCCAGTGAAG
GAATCAGAAAGGGAGAAATAAATAAATAAATAAAGTCATAAATAAAGTCATAAATATAGAAATAAATAGATGGAT
ACATACAGAAAGAAATACAGTGATAGAGAAAGAACAACCGCACGCACGCCCGCAAGAACTAACGCCCGTGCGGCC
TATAGAACGATCGTTCGCACGCCAGAACTCACGTTAGAACGTCCGACCGTACGCCAGAACTCGCGCCCTAGCTAT
CGGCCGTTCGTGAGTGAGAACAACAGAACGAGCGGCCTCAAGAACGTCCGCCCTATCTATCTGCAGAAAGTCAGT
CAAGGAATCAGAAAGGGAGAAAGAAAGAAAGAAAGAAAGAAAGAAAGAAAGAAAGAAAGAAAGAAAGAAAGAAAG
AAAGAAAGAAAGAAAGAAAGAAAGAAAGAAAGAAAGAACTCTCGGCCGTACGTAAGAACTAGCGCCCTACCTCCC
GGCCTAGCGCCAGAACGACAGAACTAGCGCCCTCTCTAGCGGCCTCACGCCAGAACGTACGACCTCACGCCCTAG
AGAACGTTCGTGAGAACTCACGTTAGAACGTCCGACCGGTCGCCAGAACGGCCTCAAGAACGTGCGCCCGACCTC
ACGCCCTAGAGTGAAGGAATCAGAAAGGGAGAAATAAATAAATACATCTAGTCATAAATATAGTCATACATAGAG
AAATAAATATATGGATACATCAAGAAAGAAATACAGTGATATAGAAAGAACAACCGCACGCACGCCCGCAAGAAC
GCCCTGACTCACTAGCGACAGAACTATCGCCCTGAAGAACGCACTAGCGGCCTCGCGCCAGAACTCACGTTAGAA
CGTCCGACCGTACGCCAGTGCGGAATGTAGAACTCACGTTCGTTCGGTAGAACGATCGTTCGCACGCCAGAACGC
GCTAGCGTTCGTCAAGGAATCAGAAAGGGAGAAAGAAAGAAAGAAAGAAAGAAAGAAAGAAAGAAAGAAAGAAAG
AAAGAAAGAAAGAAAGAAAGAAAGAAAGAAAGAAAGAAAGAAAGAAAGAACGCCCGTACGCCCTAACGGACGACC
GTGCTCAAGTCCGCACGTGCGACAGTGCGATAAGGAATCAGAAAGGGAGAAATAAATACATCAATCCAGTCATAC
ATAAAGTCATAAATATAGAAATACATCGATGGATATATATAGAAAGAAATACAGTGATCAAGAAAGAACCAGCGC
CCGTCCGTTCTCGCGCCCGCAAGAACTCACGACCGGCCGTAAGTGAAGGAATCAGAAAGGGAGAAATACATACAT
ACATCCAGTCATAAATAAAGTCATATATACAGAAATACATCTATGGATAGATAAAGAAAGAAATACAGTGATCCA
GAAAGAACCATCGGACGTTCTAGCTCACGCCCGTGCGCCCGCAAGAACGCGCGTTCTAGCGCCCGACCTAGCGTC
CTATAGTAAGAACGCCCTGACTAACGACCGTGCGCACGCCCGCAAGAACGAGCTAGCGACCGGCCGTGAGAACGA
TCGACCTATCGCCAGTGAAGGAATCAGAAAGGGAGAAATAGATAAATGCATACAGTCATAAATGAAGTCATAGAT
AAAGAAATACATATATGGATCCATCGAGAAAGAAATACAGTGATCGAGAAAGAACATTCTAACTAACGTTCTATC
GACCGAGCGTACGCCAGAACTCACGGACTCCCGTCCGAGCTATAGAACGACCGCACGCACGCCCGCAAGAACTCA
CGTTAGAACGGACGACCGTGCGCAAGGAAGGCAGAACTAGCGTTCTCCCTCACGGCCGTGCGCCAGTGAAGGAAT
CAGAAAGGGAGAAATAGATCCATAAATACAGTCATAAATCAAGTCATAAATGCAGAAATACATCAATGGATAAAT
CAAGAAAGAAATACAGTGATCTAGAAAGAACATCCGGCCGTGCGTTCTAGAGAACGATCGTTCTATCGTCCGCCC
TCACGGCCGATAGAACGGCCGTCCTAACTAGCGTTCTCGCGCCCGTCCGCCCGTGCTCACTATAGAAAGTCAGTC
AGAACTATCGGTCGGCCGTGAGAACGATCGTTCGTACGTTCTCCCTAGAGAACGTCCGACCGCACGCCAAGGAAT
CAGAAAGGGAGAAAGAAAGAAAGAAAGAAAGAAAGAAAGAAAGAAAGAAAGAAAGAAAGAAAGAAAGAAAGAAAG
AAAGAAAGAAAGAAAGAAAGAAAGAAAGAACGCACGACCTAGCGGTCGCCCTAGAGAACTCACGTTAGAACGTCC
GACCTCACGATCGGAAGAACGTCCTGCAGAACGTTCTCTCGTGAGAACGGCCGTCCGACCGCTCGCCAGTGAAGG
AATCAGAAAGGGAGAAATAGATGCATAAATGCAGTCATAAATCTAGTCATACATAGAGAAATAAATAGATGGATA
GATACAGAAAGAAATACAGTGATGAAGAAAGAACACACGCCCGTGCTCACGGCCTCACGGCCGTTCGTGAGAACG
GCCGTGCGACCGCACGCCCTACCTCCCGACCTCACGCCATGTAGAACGACCGCACGCACGCCCGCAAGAACGCCC
TGACTCACTAGCGACAGAAAGCTCTCTCGGCCTATCGCACGTTCGTCAGCTAGAACTCACGCCCGCCCTCACGGA
AGTGAAGGAATCAGAAAGGGAGAAAGAAAGAAAGAAAGAAAGAAAGAAAGAAAGAAAGAAAGAAAGAAAGAAAGA
AAGAAAGAAAGAAAGAAAGAAAGAAAGAAAGAAAGAAAGAACATCCTCCCTATCTCAAGAACTAGCGCCCGTCCG
CCCGTCCGAGCGCCCTAGAGAACTCACGTTAGAACGTCCGACCGGTCGCCAGAACGTCCGTTCTCCCTCACGGAA
GAACGAGCGGCCGCTCGCTCGCCCTAGAGAACTCACGTTAGAACGATCGTTCGTCCTAACGCCCGTGCTATCGAC
CTCACGCCAGTGAAGGAATCAGAAAGGGAGAAATCAATCCATAAATACAGTCATACATAGAGTCATATATACAGA
AATACATCAATGGATACATGAAGAAAGAAATACAGTGATGCAGAAAGAACAGCCGTGCGATCTAGCGCCCGACCT
ATCGCCAGAACGACCTCGCGCCCTAGCGACCGCTCGCCAGAACGGACGCCCGGCCGCTCGGACTCAAGTGAAGGA
ATCAGAAAGGGAGAAATCCATCCATATATATAGTCATAAATAGAGTCATACATAGAGAAATACATCTATGGATAA
ATGCAGAAAGAAATAGAGTGATAAAGAAAGAACAACCGCACGCACGCCCGCAAGAACGCTCGACCTGCAGAACGT
TCTAACTCACGGCCGTTCGTGAGTAAGAACTCACTAGCGGCCGCTCGCTCGCCCTAGCGCCCGCAAGAACGAGCT
GCAGAACGGACGGCCGCTCGGAAGAACTAACGTTCTAACTCCCGTACGACCTCACGGCCGTTCGTGAAGGAATCA
GAAAGGGAGAAAGAAAGAAAGAAAGAAAGAAAGAAAGAAAGAAAGAAAGAAAGAAAGAAAGAAAGAAAGAAAGAA
AGAAAGAAAGAAAGAAAGAAAGAAAGAACGCACGCCCGTGCTATCGGCCTCACTGCAGTAAGAACTCACGTTAGA
ACTCACTAGCTGCAGAACGACCGTGCGCAAGAACTATCGTACGTTCTCTAGAACTCACGGACGCCAGAACGTTCT
CGCGCCCTAGCTAACGTTCTAACTCCCGTACGACCTCACGGCCGTTCGTGAGAACTAACTAGCGTTCGAGCGTAC
GCCCGTCAGTGAAGGAATCAGAAAGGGAGAAATCGATAAATAAATCAAGTCATACATACAGTCATAAATCAAGAA
ATACATCGATGGATACATACAGAAAGAAATAGAGTGATACAGAAAGAACATCCGACCGCACGCCAGAACGCGCGT
TCTAGCGCCCGCGCGGCCGTGCGCTCGCCCTAGAGAACGTGCGACCTAGCTAGCGTTCTCTCGCCCTAGAGAACT
CACGTTAGAACGCGCGGCCTCAAGAACGGACGTTCGTACGCCAGAACGGCCGTGAGAACGATCGCCCGTGCTCAC
TAGCGCCAGAACGTTCGCGAAGGAATCAGAAAGGGAGAAAGAAAGAAAGAAAGAAAGAAAGAAAGAAAGAAAGAA
AGAAAGAAAGAAAGAAAGAAAGAAAGAAAGAAAGAAAGAAAGAAAGAAAGAAAGAACAATCACAAGTGAAGGAAT
CAGAAAGGGAGTTAAGGAATCAGAAAAGGAATCAGTTAGGGAGAACCATCTCACGACCGTGCGCACGACCTAGCG
CAAGAACGCACGCCCGCGCGGCCGTGCGGCCTCACGGCCGTTCGTGCTATAAGGAATCAGAAAGGGAGTTAAGGA
ATCAGAAAAGGAATCAGATCGCACGCCCGCGCGGCCGTGCGCCAGAACCATCACCCCGAAGAACGTCCGACCGTA
CGCCAAGGAATCAGATCGCACGCCCGCGCGGCCGTGCGCCAGAACAGACACCCAGCCACTCAGACCCAAGAAATA
CAGTGATGAATCAAAGGAATCAGATCGCACGCCCGCGCGGCCGTGCGCCAGAACATCCAACCCATCCATAGAAAT
CGATGAAAGGAATCAGATCGCACGCCCGCGCGGCCGTGCGCCAGAACCAGCAACCAATCACCAGAACGATCGACC
TCCCGATCGACCTATCGGCCGACCGTGAAGGAATCAGAAAAGGAATCAGTTAGGGAGAACAGCCGTGCGATCGTA
CTCCCGCACGCCAGAACGGCCGTGCGGACGCCCTAGCGGCCTCACGCCCGCAAGAACTCACTAGCGACCGGCCTC
ACTATAGAACGCGCTAGCGTTCGTCAGAACTAACGACCTAGCGCCCGTGCTCAAGAACACACATGCAACAGAACG
CGCGGCCGTACGCCCTATAGTGAAGGAATCAGAAAGGGAAGGAATCAGAAAGGGAGAACACGCGGCCGTACGCCC
TATAGAACGTCCTCCCTATCTCAAGAACGAGCGCCAGAACTAACTAGCGCCAGTCCTAACTAGCGTTCGATCGCC
CTATCTATCGCCCGCAAGAACTCTCGGCCTCACGGAAGAACATCCACCCATGCACACACCCATAAGAACTAACTA
GCGTTCGCTCTAGCGACCGTCAGAACTCACGTTAGAACTAACTAGCGTTCTCGCGGCCGCACGCCAGAACTAACT
AGCGTTCTAACGCCCTAGAAGGAATCAGAAAGGGAGAACGGCCGTGCGGACGCCCTAGCGGCCTCACGACCGTGC
GATCGCCAGAACGCGCGCCCGACCTCACTCCCTAGCGCCCTATAGTGAAGGAATCAGAAAGGGAGTTAAGGAATC
AGAAAAGGAATCAGATCGGCCGTGCGATCGTACTCCCGCACGCCAGAAAGAGCGTCCGTTCTCACGGACGCCCTA
GAGTGCGGAAGAGAAGGAATCAGATCGGCCGTGCGATCGTACTCCCGCACGCCAGAAAGAGCGCGCGACCTCACG
GACGCCCTAGAGTGCGGAAGAGAAGGAATCAGAAAAGGAATCAGATCGGCCGTGCGCGCGTGCGCACGCCCGCGA
GAACACGCAACCCCACAGACACCCCAGAAGGAATCAGATCTCTCGACCTAGCGTGAGGAAGAGCACGCGACCTCA
CGGACGCCCTAGAGAACTCCCGTGCGGTCGTGCGTTCTCTCGTGAGAAAGTCAGTCAGAACGCTCTCCCGCCCTA
TCTATCGGCCGTGCGCTCCTACGTGAGAGAGGCAAGGAATCAGATCGGCCGTGCGATCGTACTCCCGCACGCCAG
AAAGAGCGAGCGACCTATCTCACGACCTAGCGCAAGTGCGGAAGAGAAGGAATCAGATCGCCCGTGCGCACGGCC
GCGAAGGAATCAGAAAAGGAATCAGTTAGGGAGAACCATCGCCCTCAAGAACTCCCTAAAGAACTATCGCCCTGA
AGTCCTATCTAACGCCCGATCGGCCGCGCGGCCGATAGAACGCGCTCCCGTGCGATCTCACGGCCGTTCGTGCTA
TAGAACGACCGTGCGCAAGAACTCGCGACCTAGCGGCCGACCGAGCGTACGCCCTATAAGGAATCAGAAAGGGAG
TTAAGGAATCAGATCGGCCGTGCGATCGTACTCCCGCACGCCAGAAATTACTATCGCCCTGAAGTGCGGAATTGA
AGGAATCAGAAAAGGAATCAGTTAGGGAGAACAGTCGTACTCCCGCACGCTCGCCCGCAAGAACGATCGTTCGCA
CGCCAGAAAGTCAGTCAGAACAGCAGCTCGTACGTAAGAACTAGCGCCAGTCCGCACGCCCTATCGGCCGCTCGT
GAGAACTCACGGACGGCCTATAGAACGTACGTTCTCAAGAACGACCGTGCGCAAGAACTAGCGCCAGTCCTCTCT
AGCGGCCTCACGCCAGAACGGCCTCAAGAACGACCTATAGAACGACAGAACTAACTAGCGTTCTAACGCCCTAGA
AGGAATCAGAAAGGGAGAACGTACGGCCGAGCTAGCGACCTAGCTGCAGAACTATCGTTCGTCCGCCCTCACGGC
CGTCCGCCAGAACTATCGTTCGTTCGTGAGTGAAGGAATCAGAAAGGGAGTTAAGGAATCCTATCTCACTAGCTC
CCGATCTCAAGAACGCTCGCCCGTGCGGCCTCACGACCGTACTATAAGGAATCAGAAAGAAAGAACTGTAAGGAA
TCAGATCGGCCGCGCGCACGCCCGCGAGAACATCCAACCATACACCAAGGAATCAGAAAGAAAGAACCAACGCCC
GTGCGGCCTATAGAAAGGGCGGGCTCAATGTAAGGAATCAGATCGCCCGTGCGCACGGCCGCGAAGGAATCAGAA
AGAAAGAAAGTTAGGGAGAACACTCCTTCTATCTAACGTTCTCAAGAAAGGGCGCTATGTAGAAAGAAAGAACCA
GCGCCCGTCCGTTCTCGCGCCCGCAAGAACGCGCGTTCTAGAGAACGCACGCCCGAGCTCCCGCTCGCTCGGCCG
TGCGCTAGAACTAACTCCCTAGCTAACGTTCTATCGCCCTATAGAAAGGGAGTTAAGGAATCAGATCGGCCGCGC
GCACGCCCGCGAGAACACGCACCCATCCAACCATACACCAAGGAATCAGAAAGAAAGAACCCGCGACCGCTCGGC
CGTGCGACAGAAAGGGCTAAATGTAAGGAATCAGATCGCCCGTGCGCACGGCCGCGAAGGAATCAGAAAGAAAGA
ACTTCAAGGAATCAGAAAAGGAATCAGTTAGGGAGAACAGCCGTGCGGCCTCACGGCCGACCGTACGGCCTGGCG
ACCTCACGGCCGTTCGTGAGAACGAGCGTTCGTTCTCACTATCTCACTAGCGACCTAAAGAACTAGCGTTCTCCC
TCACGGCCGTGCGCCAGAAAGTCAGTCAGAACGATCGACCGTACGTACGCCCGCAAGAACGAGCGCCCGCGCGTT
CTAGCGCCAGAACACACATGCAACAGAACGCACTCCCTAACGTACGGCCGATCGACCTCACGGCCGTTCGTGAGT
GAAGGAATCAGAAAGGGAGAACAACCGTACGTACGTTCGATCGACCTCACGCCCTATAGAACGAGCTCCCGCGCG
CGCGCCCTAGCTATAGAACGACCGTGCGCAAGAACTATCGCCCTCACTATAGAACTCCCTAAAGAACTAACTAGC
GTTCTCACGCCCGGCCGTGAGAACGCGCGGCCGTACGCCAGAACTAACGTTCGGCCGTGCTCACGCCCTAGCTAT
AAGGAATCAGAAAGGGAGTTAAGGAATCCACACATGCAACAGAAAGGGCTGGCTGCCGCTCGTTCTCACGCCCCT
TCGGCCGTGCGGCCTCACGGCCGACCGTACGGCCTGGCGCCAGGACCATCTAACGCCCTAGCGTCAGAAAGGGAG
TAAGAACATTCTCGCTCCCGTCAGAAAGGGAGGCATGTAAGGAATCAGAAAAGGAATCAGTTAGGGAGAACATCC
AACCAGCCATGAGAACAGCCATGCAGCCCCACAGCCAACCATACAGCCCGGCAACCCCACAGCCATTCATGAGAA
CAATCATTCACACACCAAGGAATCAGAAAGGGAAGGAATCAGAAAGGGAGAACCAGCGCCCTCACTCCCTAGCGT
GCTATAGAACTATCTCACTAGCTCCCGATCTCACTCCCTAGCGCCCTATAGAACGATCGTTCGTGCTCACGACCG
GCCGTGCGGCCGTGCGCTAGAACTAACTAGCGCCAGTCCTAACTAGCGTTCGATCGCCCTATCTATCGCCCGCAA
GAACTAACGGACGCCCGTGCGTTCTCACTGCCTAACGCCCTATAGAACGCGCGTTCTAGAGAACTCACGGACGCC
AGAACGTTCTAGCGCTCGACCGTGCGGCCTATCGTCAAGGAATCAGAAAGGGAGAACTCACGTTAGAACGCACGG
CCTATCTAACGTACGACCTGCAGAACGACCTCAAGAACGAGCGGCCTAGCTCACGGAAGTGAAGGAATCAGAAAG
GGAAGGAATCAGAAAGGGAGAACCCTCGGCCGTACGTAAGAACGAGCGCCAGAACGGCCGTCCTAACTAGCGTTC
TCGCGCCCGCAAGAACGTACGACCTCACGCCCTAGAGAACTCACGTTAGAACGTCCGACCGGTCGCCAGAACGTT
CTCCCTCACTAACTCCCTCAAGAACGTACGCCCTATCTATAGAACTCCCGCTCGTACTGCAGTGAAGGAATCAGA
AAGGGAGTTAAGGAATCCAATCGGACGACCTAGCGACCGATCTCACGCCCTAGCGGCCTATCTCACGGCCGATAG
AAAGGGCGTACGTTCGTTCGGTCTCCCTAACCTTCTAACGGACGCCCGTGCGTTCTCACTGCCTAACGCCAGGAC
AGCCGCACGCCCGTGCTCACGGCCGCGCGGCCGCCCTAGAGAAAGGGCGGCAGGCATGTAAGGAATCAAGGAATC

 *
 * There might be a newer version of this program at
 * http://www.ic.unicamp.br/~oliva/snapshots/dnacode
 *
*/

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

#if defined __GNUC__ || defined __STDC__
#define PARAMS(PARMS) PARMS
#else
#define PARAMS(PARMS) ()
#endif

typedef void (*encode_function_t) PARAMS ((unsigned long val, int bits));

static void
b64_encode (val, bits)
     unsigned long val;
     int bits;
{
  static char base[64] = {
    'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
    'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
    'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
    'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
    'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
    'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
    'w', 'x', 'y', 'z', '0', '1', '2', '3',
    '4', '5', '6', '7', '8', '9', '+', '/',
  };
  static int count = 0;
  int padding;

  if (bits == 0)
    {
      if (count)
	putchar ('\n');
      return;
    }

  if (bits % 8)
    {
      val <<= 8 - bits % 8;
      bits += 8 - bits % 8;
    }

  if (bits % 6)
    {
      val <<= 6 - bits % 6;
      bits += 6 - bits % 6;
    }

  padding = 4 - bits / 6;

  while (bits > 5)
    {
      if (count == 76)
	{
	  putchar ('\n');
	  count = 0;
	}

      count++;
      bits -= 6;
      putchar (base[(int)(val >> bits) & 63]);
    }

  while (padding > 0)
    {
      padding--;
      count++;
      putchar ('=');
    }
}

static void
dna_encode (val, bits)
     unsigned long val;
     int bits;
{
  static char base[4] = { 'A', 'C', 'G', 'T' };
  static int count = 0;

  if (bits == 0)
    {
      if (count)
	putchar ('\n');
      return;
    }

  if (bits % 8)
    {
      val <<= 8 - bits % 8;
      bits += 8 - bits % 8;
    }

  while (bits > 1)
    {
      if (count == 75)
	{
	  putchar ('\n');
	  count = 0;
	}

      count++;
      bits -= 2;
      putchar (base[(int)(val >> bits) & 3]);
    }
}

static void
txt_encode (val, bits)
     unsigned long val;
     int bits;
{
  if (bits % 8)
    {
      val <<= 8 - bits % 8;
      bits += 8 - bits % 8;
    }

  while (bits > 7)
    {
      bits -= 8;

      if (((val >> bits) & 255) == '\r')
	continue;

      putchar ((val >> bits) & 255);
    }
}

static void
bin_encode (val, bits)
     unsigned long val;
     int bits;
{
  if (bits % 8)
    {
      val <<= 8 - bits % 8;
      bits += 8 - bits % 8;
    }

  while (bits > 7)
    {
      bits -= 8;
      putchar ((val >> bits) & 255);
    }
}

static int
b64_decode (encode_function)
     encode_function_t encode_function;
{
  int end = 0;
  int bits = 0;
  unsigned long val = 0;
  int c;

  do
    {
      switch (c = getchar())
	{
	default:
	  if (isspace (c))
	    continue;
	  /* Fall through.  */

	case -1:
	case '=':
	  end = 1;
	  switch (bits)
	    {
	    case 0:
	      if (c == -1)
		break;
	      /* Fall through.  */

	    default:
	      (*encode_function) (val, bits);
	      val = 0;
	      bits = 0;
	      fflush (stdout);

	      end = -1;
	      fprintf (stderr, "\nInvalid base64 encoding.\n");
	      break;
	      
	    case 12:
	      val >>= 4;
	      bits -= 4;
	      (*encode_function) (val, bits);
	      val = 0;
	      bits = 0;
	      fflush (stdout);

	      if (c != '=' ||
		  (c != EOF && getchar () != '='))
		{
		  fprintf (stderr, "\nMissing padding '=' at the end.\n");
		  end = -1;
		}
	      break;

	    case 18:
	      val >>= 2;
	      bits -= 2;
	      (*encode_function) (val, bits);
	      val = 0;
	      bits = 0;

	      if (c != '=')
		{
		  fprintf (stderr, "\nMissing padding at the end.\n");
		  end = -1;
		}
	      break;
	    }
	  break;

	case 'A':
	case 'B':
	case 'C':
	case 'D':
	case 'E':
	case 'F':
	case 'G':
	case 'H':
	case 'I':
	case 'J':
	case 'K':
	case 'L':
	case 'M':
	case 'N':
	case 'O':
	case 'P':
	case 'Q':
	case 'R':
	case 'S':
	case 'T':
	case 'U':
	case 'V':
	case 'W':
	case 'X':
	case 'Y':
	case 'Z':
	  val <<= 6;
	  val |= c - 'A';
	  bits += 6;
	  break;

	case 'a':
	case 'b':
	case 'c':
	case 'd':
	case 'e':
	case 'f':
	case 'g':
	case 'h':
	case 'i':
	case 'j':
	case 'k':
	case 'l':
	case 'm':
	case 'n':
	case 'o':
	case 'p':
	case 'q':
	case 'r':
	case 's':
	case 't':
	case 'u':
	case 'v':
	case 'w':
	case 'x':
	case 'y':
	case 'z':
	  val <<= 6;
	  val |= c - 'a' + 26;
	  bits += 6;
	  break;

	case '0':
	case '1':
	case '2':
	case '3':
	case '4':
	case '5':
	case '6':
	case '7':
	case '8':
	case '9':
	  val <<= 6;
	  val |= c - '0' + 52;
	  bits += 6;
	  break;

	case '+':
	  val <<= 6;
	  val |= 62;
	  bits += 6;
	  break;
	  
	case '/':
	  val <<= 6;
	  val |= 63;
	  bits += 6;
	  break;
	}

      if (bits == 24)
	{
	  (*encode_function) (val, bits);
	  val = 0;
	  bits = 0;
	}
    }
  while (! end);

  (*encode_function) (val, 0);

  return end == 1;
}

static int
dna_decode (encode_function)
     encode_function_t encode_function;
{
  int end = 0;
  int bits = 0;
  unsigned long val = 0;
  int c;

  do
    {
      switch (c = getchar())
	{
	default:
	  if (isspace (c))
	    continue;
	  /* Fall through.  */

	case -1:
	  end = 1;
	  switch (bits)
	    {
	    case 0:
	    case 8:
	    case 16:
	      break;

	    default:
	      if (bits % 8)
		{
		  val <<= 8 - bits % 8;
		  bits += 8 - bits % 8;
		}
	      (*encode_function) (val, bits);
	      val = 0;
	      bits = 0;
	      fflush (stdout);

	      fprintf (stderr, "\nInvalid DNA encoding.\n");
	      end = -1;
	      break;
	    }
	  break;

	case 'A':
	case 'a':
	  val <<= 2;
	  bits += 2;
	  break;

	case 'C':
	case 'c':
	  val <<= 2;
	  val |= 1;
	  bits += 2;
	  break;

	case 'G':
	case 'g':
	  val <<= 2;
	  val |= 2;
	  bits += 2;
	  break;

	case 'T':
	case 't':
	  val <<= 2;
	  val |= 3;
	  bits += 2;
	  break;
	}

      if ((end && bits) || bits == 24)
	{
	  (*encode_function) (val, bits);
	  val = 0;
	  bits = 0;
	}
    }
  while (! end);

  (*encode_function) (val, 0);

  return end == 1;
}

static int
txt_decode (encode_function)
     encode_function_t encode_function;
{
  int end = 0;
  int bits = 0;
  unsigned long val = 0;
  int c, last;

  do
    {
      c = getchar ();

      if (last == '\n' && c != '\r')
	{
	  val <<= 8;
	  val |= '\r' & 255;
	  bits += 8;

	  if (bits == 24)
	    {
	      (*encode_function) (val, bits);
	      val = 0;
	      bits = 0;
	    }

	}	  

    repeat:
      switch (c)
	{
	case -1:
	  (*encode_function) (val, bits);
	  val = 0;
	  bits = 0;
	  end = 1;
	  break;

	default:
	  val <<= 8;
	  val |= c & 255;
	  bits += 8;
	  break;
	}

      if (bits == 24)
	{
	  (*encode_function) (val, bits);
	  val = 0;
	  bits = 0;
	}

      last = c;
    }
  while (! end);

  (*encode_function) (val, 0);

  return 0;
}  

static int
bin_decode (encode_function)
     encode_function_t encode_function;
{
  int end = 0;
  int bits = 0;
  unsigned long val = 0;
  int c;

  do
    {
      switch (c = getchar ())
	{
	case -1:
	  (*encode_function) (val, bits);
	  val = 0;
	  bits = 0;
	  end = 1;
	  break;

	default:
	  val <<= 8;
	  val |= c & 255;
	  bits += 8;
	  break;
	}

      if (bits == 24)
	{
	  (*encode_function) (val, bits);
	  val = 0;
	  bits = 0;
	}
    }
  while (! end);

  (*encode_function) (val, 0);

  return 0;
}  

static int
endswith (string, what)
     char *string, *what;
{
  int lens = strlen (string);
  int lenw = strlen (what);

  if (lenw > lens)
    return 0;

  return strcmp (string + lens - lenw, what) == 0;
}

int
main (argc, argv)
     int argc;
     char *argv[];
{
  int count;
  encode_function_t encode_function = 0;
  int (*decode_function) PARAMS ((encode_function_t)) = 0;

  if (argc < 2 || ! argv || ! argv[0])
    goto usage;


  for (count = 1; count < argc; count++)
    {
      char *arg;

      if (argv[count][0] != '-')
	goto usage;
	  
      for (arg = &argv[count][1]; *arg; arg++)
	switch (*arg)
	  {
	  case 'b':
	    decode_function = b64_decode;
	    break;

	  case 'B':
	    encode_function = b64_encode;
	    break;

	  case 'd':
	    decode_function = dna_decode;
	    break;

	  case 'D':
	    encode_function = dna_encode;
	    break;

	  case 'o':
	    decode_function = bin_decode;
	    break;

	  case 'O':
	    encode_function = bin_encode;
	    break;

	  case 't':
	    decode_function = txt_decode;
	    break;

	  case 'T':
	    encode_function = txt_encode;
	    break;

	  default:
	  usage:
	  case 'h':
	  case '?':
	    printf ("Usage: dnacode -[bdotBDOT]\nConverts plain text to/from base64 and DNA.\n\nUse b for base64, d for DNA, t for text and o for octet-stream.\nLower case for input format, upper case for output format.\n\n");
	    /* Fall through.  */

	  case 'v':
	  case 'V':
	    printf ("dnacode version -1\nCopyright 2001 Alexandre Oliva <aoliva@redhat.com>\n");
	    return 1;
	  }
    }

  if (! decode_function || ! encode_function)
    {
      fprintf (stderr, "Missing input or output format specification\n");
      goto usage;
    }

  return (*decode_function) (encode_function);
}
