/*
 * 5799-WZQ (C) COPYRIGHT IBM CORPORATION 1986,1987,1988
 * LICENSED MATERIALS - PROPERTY OF IBM
 * REFER TO COPYRIGHT INSTRUCTIONS FORM NUMBER G120-2083
 */
/* $Header:dump.c 12.0$ */
/* $ACIS:dump.c 12.0$ */
/* $Source: /ibm/acis/usr/sys/standca/RCS/dump.c,v $ */

#ifndef lint
static char *rcsid = "$Header:dump.c 12.0$";
#endif

/*
 * program to dump a file (or device) in a convenient
 * format.
 * it can either be run in standalone mode or under the system.
 */

#include "err.h"

#if ! defined(STANDALONE)
#define hfetch(x) (* (unsigned short *) (x))
#define bad_addr()
#endif STANDALONE

char *buff;

/* int screen_lines = 24; */
extern int screen_lines;
#define SCREEN_SIZE (screen_lines-4)

#define NWORDS 8

typedef short word;
int count = 1;
char *calloc();
#define ATOX aton
#define SHORT_LINE	80
int err_flag;
int wflg;

main(argc, argv)
	char **argv;
{
	register char *argp;
	register int b = 0, n;
	register int i;
	register int l;
	register int how = 0;
	char *addr;
	char line[256];			       /* input command line */
	char file[100];

	if (argc < 2) {
		printf("File ");
		gets(file);
		argp = file;
	} else
		argp = argv[1];
	if (argc > 2)
		count = atoi(argv[2]);
	else
		count = 1;
	if (argc == 0 || argc > 3) {
		++wflg;
		how = 2;
	}
	if ((buff = calloc(count, 512)) == 0)
		err("count not allocate %d buffers", count);
	if ((n = open(argp, how)) < 0 && (how == 0 || (n = open(argp, how = 0)) < 0)) {
		printf("cannot open %s\n", argp);
		exit(1);
	} else if (wflg && how == 0)
		printf("sorry, %s could only be opened read-only");
	while (printf("Block "), gets(line) && strcmp(line, "end") != 0) {
		switch (line[0]) {
		case 0:
			break;	       /* use next block */
		case '/':
			addr = buff + atox(line + 2);
			switch (line[1]) {
			case 'w':
				modify(addr, 512 / 4, 4, buff);
				break;
			case 'h':
				modify(addr, 512 / 2, 2, buff);
				break;
			case 'b':
				modify(addr, 512, 1, buff);
				break;
			default:
				printf("usage: /[whb]hex-offset\n");
				continue;
			}
			printf("\n");

			bdisplay(buff, 512 / 16);
			continue;
		case 'w':
			if (how == 0) {
				printf("not open for writing! usage: dump file 1 -w\n");
				continue;
			}
			if (line[1])
				b = aton(line + 1);
			else
				b -= count;
			printf("\nwrite block %d (0x%x) of %s offset %d (0x%x)\n\n", b, b, argp, b * 512, b * 512);
			if (lseek(n, b * 512L, 0) < 0)
				printf("lseek failed on block %d (%x)\n", b, b);
			if ((l = write(n, buff, count * 512)) != count * 512)
				printf("write error (%d)\n", l);
			continue;
		default:
			b = aton(line);
			break;
		}
		if (lseek(n, b * 512L, 0) < 0)
			printf("lseek failed on block %d (%x)\n", b, b);
		else {
			printf("\nblock %d (0x%x) of %s offset %d (0x%x)\n\n", b, b, argp, b * 512, b * 512);
			if ((l = read(n, buff, 512 * count)) < 0)
				printf("read error (got %d)\n", l);
			else {
				if (l != 512 * count)
					printf("Short read: only %d bytes received of %d\n", l, count * 512);
				for (i = 0; i < (l >> 9); ++i, ++b) {
					if (i)
						printf("\nblock %d (0x%x) of %s offset %d (0x%x)\n\n", b, b, argp, b * 512, b * 512);
					bdisplay(buff + i * 512, 512 / 16);
				}
			}
		}
	}
	close(n);
}


bdisplay(start, count)
	register word * start;
	register int count;
{
	register int i;
	register char *p;
	register int c;
	int base = (int)start;
	int lines = 0;
	int sum;
	int same = 0;

	do {
		for (i = 0; i < NWORDS; ++i)
			if (sum = hfetch(start + i))
				break;
		if (sum == 0) {
			if (same++) {
				if (same == 1)
					printf("...\n");
				start += NWORDS;
				continue;
			}
		} else
			same = 0;
		printf("%08x: ", ((int)start) - base);
		p = (char *)start;
		for (i = 0; i < NWORDS; ++i) {
			if (hfetch(start) == -1) {
				bad_addr();
				break;
			}
			printf("%04x ", *start++ & 0xffff);
		}
		printf("  |");
		for (i = 0; i < NWORDS * sizeof(word); ++i) {
			c = *p++;
			if (c < 040 || c >= 0177)
				c = '.';
			putchar(c);
		}
		printf("|");
		printf("\n");
		if (++lines >= SCREEN_SIZE) {
			if (pause() <= 04)
				break;
			else
				lines = 0;
		}
	} while (--count > 0);
	return ((int)start);
}


#ifndef STANDALONE

pause()
{
	register int c;

	printf("<HOLDING>");
	c = getchar();
	printf("\r         \r");
	return (c);
}


#endif

#ifndef STANDALONE
aton(ptr)
	register char *ptr;
{
	if (*ptr == '+' || *ptr == '-')
		return (atoi(ptr));
	if (*ptr == '0' && ptr[1] == 'x')
		return (atox(ptr + 2));
	return (atox(ptr));
}
#endif


#include <ctype.h>


/*
 * convert string in hex to binary
 */
int atox(ptr)
	register char *ptr;
{
	register int n = 0;
	register int c;

	for (; c = *ptr++;) {
		if (isdigit(c))
			c -= '0';
		else if ('a' <= c && c <= 'f')
			c -= 'a' - 10;
		else if ('A' <= c && c <= 'F')
			c -= 'A' - 10;
		else
			break;
		n = (n << 4) + c;
	}
	return (n);
}


#ifndef SAUTIL
#include "err.c"
#endif SAUTIL


#define prsym(addr,offset,fmt) printf(fmt,addr)
 /*
  * change value at "start" (for "count" lines) of length "length"
  * length == 1, 2 , or 4
  * if count <=0 then don't check address validity as this can cause
  * problems when accessing the I/O map.
  * if count < 0 then don't display the old value as this can also
  * cause problems when addess the I/O map.
  */
static modify(start, count, length, offset)
	register start, length, count;
{
	char buff[SHORT_LINE];
	register int n;
	register oldcount = count;     /* special case for I/O bus */

	if (count <= 0)
		count = 1;	       /* always do at least one */
	for (; --count >= 0;) {
		prsym(start - offset, 0x10000, "%08x ");
		putchar(':');
		if (oldcount > 0) {
			if (length == 1)
				n = hfetch(start & ~1);	/* get the half word */
			else if (length == 2)
				n = hfetch(start); /* get the half word */
			else
				n = hfetch(start) | hfetch(start + 2);
			if (n == -1) {
				bad_addr();
				return (start);
			}
		}
		if (oldcount >= 0) {
			if (length == 1)
				printf("%02x ", *(char *)start & 0xff);
			else if (length == 2)
				printf("%04x ", *(short *)start & 0xffff);
			else
				printf("%08x ", *(int *)start);
		}
		gets(buff);
		if (*buff == '^') {
			start -= length;
			continue;
		}
		if (strcmp(buff, "end") == 0 || (*buff && *buff < 012))
			break;
		if (*buff && !isspace(*buff)) {
			err_flag = 0;
			n = ATOX(buff);
			if (err_flag)
				continue;
			if (length == 1)
				*(char *)start = n;
			else if (length == 2)
				*(short *)start = n;
			else
				*(int *)start = n;
		}
		start += length;
	}
	return (start);
}
