/*
 *  print_dump
 *
 *  COPYRIGHT (c) 1994 by Division Incorporated
 *
 *  The license and distribution terms for this file may be
 *  found in the file LICENSE in this distribution or at
 *  http://www.OARcorp.com/rtems/license.html.
 *
 *  $Id$
 */

#define GETOPTARGS "v"

char *USAGE = "\
usage:  print_dump  [ -v ] \n\
            -v          -- verbose\n\
 Reads HP simulator 'memdump' output of 'print_buffer' structure
 on stdin.  Dumps it out in vanilla ASCII.
";

#include <stdio.h>
#include <fcntl.h>
#include <ctype.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <memory.h>
#include <stdarg.h>

#include "../../../../../../build-tools/src/config.h"

#ifndef VMS
#ifndef HAVE_STRERROR
#if defined(__linux__) && (__GLIBC__ < 2)
extern char *sys_errlist[];
#endif

#define strerror( _err ) \
  ((_err) < sys_nerr) ? sys_errlist [(_err)] : "unknown error"

#else   /* HAVE_STRERROR */
char *strerror ();
#endif
#else   /* VMS */
char *strerror (int,...);
#endif

#define Failed(x)       (((int) (x)) == -1)
#define TRUE    1
#define FALSE   0
#define STREQ(a,b)      (strcmp(a,b) == 0)
#define NUMELEMS(arr)   (sizeof(arr) / sizeof(arr[0]))

/*
 * Definitions for unsigned "ints"; especially for use in data structures
 *  that will be shared among (potentially) different cpu's (we punt on
 *  byte ordering problems tho)
 */

typedef unsigned char   u8;
typedef unsigned short  u16;
typedef unsigned int    u32;

typedef union uval {
    u8      uv_chars[4];
    u16     uv_words[2];
    u32     uv_long;
    void    *uv_ptr[sizeof(long) / sizeof(void *)];
} uval_t;


/*
 * vars controlled by command line options
 */

int verbose = FALSE;                    /* be verbose */

extern char *optarg;                    /* getopt(3) control vars */
extern int optind, opterr;
extern int errno;

char *progname;                         /* for error() */

void error(int errn, ...);

#define ERR_ERRNO  (1<<((sizeof(int) * 8) - 2)) /* hi bit; use 'errno' */
#define ERR_FATAL  (ERR_ERRNO / 2)              /* error is fatal; no return */
#define ERR_ABORT  (ERR_ERRNO / 4)              /* error is fatal; abort */
#define ERR_MASK   (ERR_ERRNO | ERR_FATAL | ERR_ABORT) /* all */

int  process(void);
void prchar(unsigned int ch);


int
main(int argc, char **argv, char **env)
{
    register int c;
    int showusage = FALSE;                      /* usage error? */

    /*
     * figure out invocation leaf-name
     */

    if ((progname = strrchr(argv[0], '/')) == (char *) NULL)
        progname = argv[0];
    else
        progname++;

    argv[0] = progname;                         /* for getopt err reporting */

    /*
     *  Check options and arguments.
     */

    opterr = 0;                                 /* we'll report all errors */
    while ((c = getopt(argc, argv, GETOPTARGS)) != EOF)
        switch (c)
        {
            case 'v':                           /* toggle verbose */
                verbose = ! verbose;
                break;

            case '?':
                showusage = TRUE;
        }

    if (showusage)
    {
        (void) fprintf(stderr, "%s", USAGE);
        exit(1);
    }

    return process();
}


/*
 * process(arg)
 *
 * Input looks like this
 *
 *   Starting address: 00000001.480035a0
 *   -----------------------------------

+0000 / 0d0a0d0a 2a2a2a20 53454d20 54455354 202d2d20 4e4f4445 2032202a 2a2a0d0a
+0020 / 73703a20 30783433 30303030 31300d0a 30783438 30613161 38383a20 676f7420
  ....
+0b40 / xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx

 *
 * The 'xxxxxxxxx' shows up if the page has not been allocated.
 */

int
process(void)
{
    uval_t b[8];
    u32   ignore;
    char *p;
    int  i;
    int  failed_once;

    char line[1024];

#define PRINT_BUFFER_SIZE (16 * 1024)
    struct {
        int  index;
        int  size;
        u8   buffer[PRINT_BUFFER_SIZE];
    } print_buffer;

    /* we stuff the data into print_buffer using memcpy() */
    p = (char *) &print_buffer;

    failed_once = 0;

    while (gets(line))
    {
        char *cp;

        /* hack; deal with the 'xxxxxxxx' problem noted above */
        for (cp=line; *cp; cp++)
            if (*cp == 'x')
                *cp = '0';

        if (*line != '+')
            continue;
        if (sscanf(line, "+%x / %x %x %x %x %x %x %x %x\n",
                   &ignore,
                   &b[0].uv_long,
                   &b[1].uv_long,
                   &b[2].uv_long,
                   &b[3].uv_long,
                   &b[4].uv_long,
                   &b[5].uv_long,
                   &b[6].uv_long,
                   &b[7].uv_long) != 9)
        {
            if (failed_once)
                error(ERR_FATAL, "2nd format problem; giving up");
            error(0, "format problem in line: `%s`", line);
            failed_once = 1;
        }

        memcpy((void *) p, (void *) b, sizeof(b));
        p += sizeof(b);
    }

    if (verbose)
        printf("buffer size: %d\n", print_buffer.size);

    if (print_buffer.size < 0)
        error(ERR_FATAL, "size is too small");

    if (print_buffer.size != sizeof(print_buffer.buffer))
    {
        error(ERR_FATAL, "buffer size mismatch, expected %d",
              sizeof(print_buffer.buffer));
        /* XXX we really should just dynamically allocate the buffer */
    }

    i = print_buffer.index + 1;
    while (i != print_buffer.index)
    {
        unsigned int c;
        c = print_buffer.buffer[i++];
        if (c && (c != '\r'))
            prchar(c);
        i %= print_buffer.size;
    }
    printf("\n");
    return 0;
}

/* de-controlify */
char *de_control[] = {
    "^@", "^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", ":", ";",
    "<", "=", ">",  "?", "@",  "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", "{", "|", "}", "~", "^?",
    "M-^@", "M-^A", "M-^B", "M-^C", "M-^D", "M-^E", "M-^F", "M-^G",
    "M-^H", "M-^I", "M-^J", "M-^K", "M-^L", "M-^M", "M-^N", "M-^O",
    "M-^P", "M-^Q", "M-^R", "M-^S", "M-^T", "M-^U", "M-^V", "M-^W",
    "M-^X", "M-^Y", "M-^Z", "M-^[", "M-^\\", "M-^]", "M-^~", "M-^_",
    "M- ", "M-!", "M-\"", "M-#", "M-$", "M-%", "M-&", "M-'",
    "M-(", "M-)", "M-*", "M-+", "M-,", "M--", "M-.", "M-/",
    "M-0", "M-1", "M-2", "M-3", "M-4", "M-5", "M-6", "M-7",
    "M-8", "M-9", "M-:", "M-;", "M-<", "M-=", "M->", "M-?",
    "M-@", "M-A", "M-B", "M-C", "M-D", "M-E", "M-F", "M-G",
    "M-H", "M-I", "M-J", "M-K", "M-L", "M-M", "M-N", "M-O",
    "M-P", "M-Q", "M-R", "M-S", "M-T", "M-U", "M-V", "M-W",
    "M-X", "M-Y", "M-Z", "M-[", "M-\\", "M-]", "M-^", "M-_",
    "M-`", "M-a", "M-b", "M-c", "M-d", "M-e", "M-f", "M-g",
    "M-h", "M-i", "M-j", "M-k", "M-l", "M-m", "M-n", "M-o",
    "M-p", "M-q", "M-r", "M-s", "M-t", "M-u", "M-v", "M-w",
    "M-x", "M-y", "M-z", "M-{", "M-|", "M-}", "M-~", "M-^?"
};

/*
 * prchar(ch); print ch in a readable format, ie ^X or X or ~^X or DEL, etc.
 */

void
prchar(unsigned int ch)
{
    if (isprint(ch) || isspace(ch))
        putchar(ch);
    else
        printf("%s", de_control[ch]);
}


/*
 * error(errn, arglist)
 *      report an error to stderr using printf(3) conventions.
 *      Any output is preceded by '<progname>: '
 *
 * Uses ERR_EXIT  bit to request exit(errn)
 *      ERR_ABORT to request abort()
 *      ERR_ERRNO to indicate use of errno instead of argument.
 *
 * If resulting 'errn' is non-zero, it is assumed to be an 'errno' and its
 *      associated error message is appended to the output.
 */

/*VARARGS*/

void
error(int error_flag, ...)
{
    va_list arglist;
    register char *format;
    int local_errno;

    extern int errno;

    (void) fflush(stdout);          /* in case stdout/stderr same */

    local_errno = error_flag & ~ERR_MASK;
    if (error_flag & ERR_ERRNO)     /* use errno? */
        local_errno = errno;

    va_start(arglist, error_flag);
    format = va_arg(arglist, char *);
    (void) fprintf(stderr, "%s: ", progname);
    (void) vfprintf(stderr, format, arglist);
    va_end(arglist);

    if (local_errno)
      (void) fprintf(stderr, " (%s)\n", strerror(local_errno));
    else
      (void) fprintf(stderr, "\n");

    (void) fflush(stderr);

    if (error_flag & (ERR_FATAL | ERR_ABORT))
    {
        if (error_flag & ERR_FATAL)
        {
            error(0, local_errno ? "fatal error, exiting" : "exiting");
            exit(local_errno);
        }
        else
        {
            error(0, "fatal error, aborting");
            abort();
        }
    }
}

