#include <stdio.h>

#define TRUE 1
#define FALSE 0

void decodefile(FILE *, FILE *);

int main(int argc, char *argv[]){
  FILE *fin, *fout;

  if (argc > 3){
    printf("Usage: %s <infile> <outfile>\n", argv[0]);
    exit(1);
  }

  switch(argc){
    case 3:
      fin = fopen(argv[1], "r");
      fout = fopen(argv[2], "w");
      if (!fin || !fout) {
        perror("fopen");
        exit(1);
      }
      break;
    case 2:
      fin = fopen(argv[1], "r");
      fout = stdout;
      if (!fin) {
        perror("fopen");
        exit(1);
      }
      break;
    case 1:
      fin = stdin;
      fout = stdout;
      break;
  }

  decodefile(fin, fout);
  close(fin);
  close(fout);
  exit(0);
}

void decodefile(FILE *fin, FILE *fout) {

  short charctr;
  int breakout;
  unsigned char ch;
  unsigned char inbuf[3], outbuf[4];
  short bufctr = 0, ignore, eot = 0;

  while ((ch = fgetc(fin))) {
    if (feof(fin)){
      close(fin);
      break;
    }

    ignore = FALSE;

    if ((ch >= 'A') && (ch <= 'Z'))
      ch = ch - 'A';
    else if ((ch >= 'a') && (ch <= 'z'))
      ch = ch - 'a' + 26;
    else if ((ch >= '0') && (ch <= '9'))
      ch = ch - '0' + 52;
    else if (ch == '+')
      ch = 62;
    else if (ch == '=')
      eot = TRUE;
    else if (ch == '/')
      ch = 63;
    else
      ignore = TRUE;

    if (!ignore) {
      charctr = 3;
      breakout = FALSE;

      if (eot) {
        if (bufctr == 0)
          break;

        if ((bufctr == 1) || (bufctr == 2))
          charctr = 1;
        else
          charctr = 2;

        bufctr = 3;
        breakout = TRUE;
      }

      inbuf[bufctr++] = ch;

      if (bufctr == 4) {
        bufctr = 0;

        outbuf[0] = (inbuf[0] << 2) | ((inbuf[1] & 0x30) >> 4);
        outbuf[1] = ((inbuf[1] & 0x0F) << 4) | ((inbuf[2] & 0x3C) >> 2);
        outbuf[2] = ((inbuf[2] & 0x03) << 6) | (inbuf[3] & 0x3F);

        fprintf(fout, "%c%c%c", outbuf[0], outbuf[1], outbuf[2]);
      }

      if (breakout)
        break;
    }
  }

}
