/**
 * dump.c HEXDUMP utility
 *
 * Copyright 2001,2002,2005,2009 by Charles Wilson
 * All rights reserved.
 *
 * 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 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/>.
 *
 * See the COPYING file for full license information.
 */

#if HAVE_CONFIG_H
# include "config.h"
#endif

#include "common.h"

#if defined(__WIN32__) && !defined(__CYGWIN__)
# include <io.h>
# include <conio.h>
#endif

static const char versionID[] = PACKAGE_VERSION;
static const char revID[] =
  "$Id$";
static const char copyrightID[] =
  "Copyright (c) 2009\nCharles S. Wilson. All rights reserved.\nLicensed under GPLv3+\n";

static void printTopDescription (FILE * f, char *name);
static void printBottomDescription (FILE * f, char *name);
static const char *getVersion (void);
static void usage (FILE * f, char *name);
static void help (FILE * f, char *name);
static void version (FILE * f, char *name);
static void license (FILE * f, char *name);
static void puthex (long n, int digits, int pos);
static void dumpfile (FILE * f);

static char *program_name;
static poptContext optCon;
static char line[80];
static long address;

int
main (int argc, const char **argv)
{
  const char **rest;
  int rc;
  int ec = 0;

  struct poptOption helpOptionsTable[] = {
    {"help", '?', POPT_ARG_NONE, NULL, '?',
     "Show this help message", NULL},
    {"usage", '\0', POPT_ARG_NONE, NULL, 'u',
     "Display brief usage message", NULL},
    {"version", '\0', POPT_ARG_NONE, NULL, 'v',
     "Display version information", NULL},
    {"license", '\0', POPT_ARG_NONE, NULL, 'l',
     "Display licensing information", NULL},
    {NULL, '\0', 0, NULL, 0, NULL, NULL}
  };

  struct poptOption opt[] = {
    {NULL, '\0', POPT_ARG_INCLUDE_TABLE, helpOptionsTable, 0,
     "Help options", NULL},
    {NULL, '\0', 0, NULL, 0, NULL, NULL}
  };

  if ((program_name = strdup (argv[0])) == NULL)
    {
      fprintf (stderr, "%s: memory allocation error\n", argv[0]);
      exit (1);
    }
  optCon = poptGetContext (NULL, argc, argv, opt, 0);
  poptSetOtherOptionHelp (optCon, "[OPTION...] [files...]");
  while ((rc = poptGetNextOpt (optCon)) > 0)
    {
      switch (rc)
        {
        case '?':
          help (stdout, program_name);
          goto exit;
        case 'u':
          usage (stdout, program_name);
          goto exit;
        case 'v':
          version (stdout, program_name);
          goto exit;
        case 'l':
          license (stdout, program_name);
          goto exit;
        }
    }
  if (rc < -1)
    {
      fprintf (stderr, "%s: bad argument %s: %s\n",
               program_name, poptBadOption (optCon, POPT_BADOPTION_NOALIAS),
               poptStrerror (rc));
      ec = 2;
      goto exit;
    }
  rest = poptGetArgs (optCon);

  if (rest == NULL)
    dumpfile (stdin);
  else
    {
      while (*rest)
        {
          FILE *f = fopen (*rest, "rb");
          printf ("%s:\n", *rest);
          if (f)
            {
              dumpfile (f);
              fclose (f);
            }
          else
            printf ("*** Can't open %s!!\n", *rest);
          rest++;
        }
    }
/*
  if (argc < 2) dumpfile(stdin);
  else {
    while (--argc > 0) {
      FILE *f = fopen(*++argv, "rb");
      printf("%s:\n",*argv);
      if (f) {
        dumpfile(f);
        fclose(f);
      }
      else printf("*** Can't open %s!!\n", *argv);
    }
  }
*/
exit:
  poptFreeContext (optCon);
  free (program_name);
  return (ec);
}

static const char *
getVersion ()
{
  return versionID;
}

static void
printTopDescription (FILE * f, char *name)
{
  fprintf (f, "%s is part of cygutils version %s\n", name, getVersion ());
  fprintf (f, "  Prints a hexdump of stdin or specified files to stdout\n\n");
}

static void
printBottomDescription (FILE * f, char *name)
{
  fprintf (f, "\n");
  fprintf (f, "Other arguments\n");
  fprintf (f,
           "  [files...]      dump each file specified; if none, use stdin\n");
  fprintf (f,
           "\nSimilar in behavior to 'od -Ax -tx2z [a file]'\n");
}

static void
printLicense (FILE * f, char *name)
{
  fprintf (f,
           "This program is free software: you can redistribute it and/or modify\n"
           "it under the terms of the GNU General Public License as published by\n"
           "the Free Software Foundation, either version 3 of the License, or\n"
           "(at your option) any later version.\n\n"
           "This program is distributed in the hope that it will be useful,\n"
           "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
           "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n"
           "GNU General Public License for more details.\n\n"
           "You should have received a copy of the GNU General Public License\n"
           "along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n"
           "See the COPYING file for full license information.\n");
}

static void
usage (FILE * f, char *name)
{
  poptPrintUsage (optCon, f, 0);
}

static void
help (FILE * f, char *name)
{
  printTopDescription (f, name);
  poptPrintHelp (optCon, f, 0);
  printBottomDescription (f, name);
}

static void
version (FILE * f, char *name)
{
  printTopDescription (f, name);
}

static void
license (FILE * f, char *name)
{
  printTopDescription (f, name);
  printLicense (f, name);
}

static void
puthex (long n, int digits, int pos)
{
  if (digits > 1)
    puthex (n / 16, digits - 1, pos);
  line[pos + digits - 1] = "0123456789abcdef"[n % 16];
}

static void
dumpfile (FILE * f)
{
  int c, i;
  address = 0;
  c = getc (f);
  while (1)
    {
      for (i = 0; i < 50; i++)
        line[i] = ' ';
      for (; i < 80; i++)
        line[i] = 0;
      puthex (address, 8, 0);
      if (c == EOF)
        return;
      for (i = 0; i < 16; i++)
        {
          puthex (c & 0xff, 2, 10 + i * 2 + i / 2);
          line[50 + i] = '.';
          if (isprint (c & 0x7f))
            line[50 + i] = c & 0x7f;
          if ((c = getc (f)) == EOF)
            break;
        }
      if (address && ((address % 256) == 0))
        {
#if defined(__WIN32__) && !defined(__CYGWIN__)
          if (isatty (STDOUT_FILENO))
            {
              while (kbhit ())
                getch ();
              getch ();
            }
#endif
          puts ("");
          puts
            ("  Addr     0 1  2 3  4 5  6 7  8 9  A B  C D  E F 0 2 4 6 8 A C E ");
          puts
            ("--------  ---- ---- ---- ---- ---- ---- ---- ---- ----------------");
        }
      puts (line);
      address += 16;
    }
}
