/*	dumper

Usage:

    DUMper [<screen width>] [<line group length>]

Description:

File contents dumping in hex and ASCII format.
Currently this program works on direct binary access files only
(files created by fput in C, or direct access files in FORTRAN).

The user specifies:

  File <filename> - Name of the file to open.
    [Def: The currently open file (if any) is shown]
  Seek <position> - Position (byte count from 0) to seek.
    [Def: Show the value of the current file record pointer]
  Dump [<number>] [<records>] - Dump to the screen the number of bytes
    from the next records.
    [Def: Up to 20 lines (screen full)]
    [* == Up to MAX_BUFFER]
  Group [<number>] - Number of record bytes grouped on a dump line.
    [Def: Show the current grouping value]
  Continue [<records>] - Continue the dump (for number of records).
    [Def: 1]
    [* == until EOF]

The user is notified of:

    File access errors.
    Dump contents by byte: hex and ASCII equivalents
    	(or '_' if not printable).
    
*/

#include	<std.h>
#include	<stdio.h>
#include	<qioret.h>

#define	DEF_GROUPING	16
static int	groups = DEF_GROUPING;
#define	DEF_WIDTH	80
#define	MIN_WIDTH	13

static FILE	*file = NULL;
static long	position = 0L;
static int	rec_size;

static char	command[80];
#define	MAX_BUFFER	4096
static char	buffer[MAX_BUFFER];


main (count, arg)

int	count;
STRING	arg[];
{
STRING	string;
int	amount, records, group;
long	atol(), ftell();

if (count >= 2 && isdigit (*arg[1]))
    {
    if ((group = atoi (arg[1])) < MIN_WIDTH)
	group = MIN_WIDTH;
    }
else
    group = DEF_WIDTH;

if (count >= 3 && isdigit (*arg[2]))
    {
    groups = atoi (arg[2]);
    groups = (((group - 10) / (4 * groups)) > 0) ?
	groups : ((group - 10) / 4);
    }
else
    groups = (((group - 10) / (4 * groups)) / groups) * groups;
if (groups <= 0) groups = DEF_GROUPING;
amount = 20 * groups;


FOREVER
    {
    printf ("Dumper> ");
    if (! gets (command)) exit ();

    string = command;
    switch (toupper (*string))
	{
	case 'F':
/*
  File <filename>
*/
	    if (*(string = nxtword (string)))
		{
		if (file) fclose (file);
		if (! (file = fopen (string, "ru")))
		    {
		    printf ("Unable to open the file\n%s (%d)\n",
			ioerrmsg (IO_ERROR), IO_ERROR);
		    fclose (file);
		    }
		}
	    else
		{
		if (file)
		    printf ("%s\n", fgetname (file, string));
		else
		    printf ("No open file.\n");
		}
	    break;

	case 'S':
/*
  Seek <position>
*/
	    if (file)
		{
		if (isdigit (*(string = nxtword (string))))
		    {
		    position = atol (string);
		    fseek (file, position, 0);
		    if (IO_ERROR)
			printf ("%s (%d)\nwhile positioning to %ld\n",
			    ioerrmsg (IO_ERROR), IO_ERROR, position);
		    else
			break;
		    }
		else
		    {
		    position = ftell (file);
		    printf ("Record pointer = %ld\n", position);
		    }
		}
	    else
		{
no_file:
		printf ("File?\n");
		}
	    break;


	case 'D':
/*
  Dump [<amount>]
*/
	    if (file)
		{
/*
		Amount to dump from each record
*/
		if (*(string = nxtword (string)) == '*')
		    amount = MAX_BUFFER;
		else if (isdigit (*string))
		    {
		    amount = atoi (string);
		    if (amount <= 0 || amount > MAX_BUFFER)
			{
			printf ("Invalid value.\n");
			break;
			}
		    }
		else
		    amount = 20 * groups;
		}
	    else
		goto no_file;


	case 'C':
/*
  Continue [<records>]
*/
	    if (file)
		{
		if (*(string = nxtword (string)) == '*')
		    records = -1;
		else if (isdigit (*string))
		    {
		    records = atoi (string);
		    if (records <= 0)
			{
			printf ("Invalid value.\n");
			break;
			}
		    }
		else
		    records = 1;

/*
		Continue listing records
*/
		keyin ();	/* Set keyboard trap */
		while (records--)
		    {
/*
		    Get the next amount from the next record into the buffer
*/
		    position = ftell (file);
		    rec_size = fget (buffer, amount, file);

		    if (IO_ERROR)
			{
			printf ("\
%s (%d)\n\
while attempting to read %d bytes\n\
from position %ld\n\
%d bytes read\n",
ioerrmsg (IO_ERROR), IO_ERROR,
amount,
position,
rec_size);
			break;
			}
		    else
			{
			printf ("\n%d bytes read:\n", rec_size);
			list (((amount > rec_size) ? rec_size : amount));
			}
		    if (keyin ())
			break;
		    }
		keyoff ();
		}
	    else
		goto no_file;
	    break;

	case 'G':
/*
  Group <amount>
*/
	    if (isdigit (*(string = nxtword (string))))
		groups = atoi (string);
	    else
		printf ("Listing grouped at %d bytes per line.\n", groups);
	    break;

	default:
	    printf ("\n\
Commands:\n\
  File <filename> - Name of the file to open.\n\
    [Def: The currently open file (if any) is shown]\n\
  Seek <position> - Position (byte count from 0) to seek.\n\
    [Def: Show the value of the current file record pointer]\n\
  Dump [<number>] [<records>] - Dump to the screen the number of bytes\n\
    from the next records.\n\
    [Def: Up to 20 lines (screen full)]\n\
    [* == Up to MAX_BUFFER]\n\
  Group [<number>] - Number of record bytes grouped on a dump line.\n\
    [Def: Show the current grouping value]\n\
  Continue [<records>] - Continue the dump (for number of records).\n\
    [Def: 1]\n\
    [* == until EOF]\n\
  ^Z (exit)\n");
	    break;
	}
    }
}


/******************************************************************************

Produce a screen listing of the current buffer.

*/

#define	BYTE_MASK	0377


list (amount)

int	amount;
{
int	count, number, group;


count = 0;
while (amount)
    {
/*
    Hex format
*/
    printf ("\n%6ld:", count + position);
    for (
	group = 0;
	group < groups && amount;
	group++, count++, amount--
	)
	printf (" %02X", (buffer[count] & BYTE_MASK));
    count -= group;
    number = group;
/*
    Space forward to the ASCII format area
*/
    while (group++ < groups)
	printf ("   ");
/*
    ASCII format
*/
    printf ("  ");
    for
	(
	group = 0;
	group < number;
	group++, count++)
	printf ("%c",
	    ((buffer[count] >= ' ' && buffer[count] <= '~') ?
	    buffer[count] : '_'));
    }
printf ("\n");
}
