/*
 *				o d . c
 */

/*)BUILD	$(TKBOPTIONS) = {
			TASK	= ...ODX
		}
*/

#ifdef	DOCUMENTATION

title	od	Octal Dump
index	od	Octal Dump

synopsis

	od [-options] [-f blocknumber] [-l blocknumber] file

description

	od dumps binary files in human-readable format.  The following
	options are defined:
	.lm +8
	.s.i -8;-a	Dump ASCII characters
	.s.i -8;-b	Dump octal bytes
	.s.i -8;-f _#	Set first record, default is 0
	.s.i -8;-l _#	Set last record, default is EOF
	.s.i -8;-u	Dump by block, even if record-oriented
	.s.i -8;-w	Dump octal words
	.s.i -8;-x	Dump hexadecimal bytes
	.s.i -8;-5	Dump radix 50 words.
	.s.lm -8
	If no format options are selected, od dumps records
	by words "-w".
	.s
	On RSX, od defaults to record mode dumps.  Use the -u
	switch to force a block mode dump.

diagnostics

	.lm +8
	.s.i -8;"File name": Cannot open
	.s.i -8;Usage ...
	.lm -8

bugs

	od is heavily dependent on Decus C libraries.  It
	will not run correctly on vax-11 C.  If compiled with
	the RT11 library, it cannot read "attributed" files on
	RSTS/E.

author

	David Conroy

#endif

#include <stdio.h>
#ifdef vms
#include		<ssdef.h>
#include		<stsdef.h>
#define	IO_SUCCESS	(SS$_NORMAL | STS$M_INHIB_MSG)
#define	IO_ERROR	SS$_ABORT
#endif
/*
 * Note: IO_SUCCESS and IO_ERROR are defined in the Decus C stdio.h file
 */
#ifndef	IO_SUCCESS
#define	IO_SUCCESS	0
#endif
#ifndef	IO_ERROR
#define	IO_ERROR	1
#endif
#define	EOS	0
#define	FALSE	0
#define	TRUE	1

#ifdef	rt11
#define	RECORD	unsigned int
#define	ZERO	0
#define	HUGE	32767
#define BSIZ	512
#endif

#ifdef	rsx
#define	RECORD	long
#define	ZERO	0L
#define	HUGE	04000000L		/* Max block on ODS-1		*/
#define	BSIZ	512
#endif

#ifdef	vms
#define	RECORD	long
#define	ZERO	0l
#define	HUGE	04000000L
#define	BSIZ	512
#endif

int	aflag	= FALSE;
int	bflag	= FALSE;
int	fflag	= FALSE;
int	uflag	= FALSE;
int	wflag	= FALSE;
int	xflag	= FALSE;

int	max_size;
char	*filename;
char	*buf = NULL;
FILE	*fd;
RECORD	fbn	= ZERO;
RECORD	lbn	= HUGE;

#ifdef	rsx
/*
 * The following are offsets (F_xxx) and values (R_xxx) in the FDB
 */

extern	char	*F_RTYP;		/* record type			*/
extern	char	*F_RATT;		/* record attributes		*/
extern	char	*F_RSIZ;		/* record size word		*/
extern	char	*F_SEQN;		/* printfile fixed header word	*/

extern	char	*R_FIX;			/* in F.RTYP for fixed-length	*/
extern	char	*R_SEQ;			/* in F.RTYP for printfile	*/

int		seqflag = FALSE;	/* Set to test for sequence	*/
#endif

main(argc, argv)
char *argv[];
{
	register int i, c;
	register char *p;
	char *bp;

#ifdef vms
	/*
	 * Do VAX C redirection - although this program doesn't work on VAX C
	 * anyway.
	 */
	argc = getredirection(argc,argv);
	fprintf(stderr,"od doesn't work under VAX C - sorry\n");
	abort();
#endif

	filename = NULL;
	for(i=1; i<argc; ++i) {
		p = argv[i];
		if(*p == '-') {
			++p;
			while(c = *p++) {
				switch(tolower(c)) {

				case '5':
					++fflag;
					break;

				case 'a':
					++aflag;
					break;

				case 'b':
					++bflag;
					break;

				case 'u':
					++uflag;
					break;

				case 'w':
					++wflag;
					break;

				case 'x':
					++xflag;
					break;

				case 'f':
					if(++i >= argc)
						usage();
					fbn = atoi(argv[i]);
					break;

				case 'l':
					if(++i >= argc)
						usage();
					lbn = atoi(argv[i]);
					break;

				default:
					usage();
				}
			}
		} else if(filename != NULL)
			usage();		/* Two files to dump	*/
		else
			filename = p;
	}
	if(filename == NULL)
		usage();
	if(lbn < fbn)
		usage();
	if(!aflag && !bflag && !fflag && !wflag && !xflag)
		++wflag;
	if((fd = fopen(filename, "ru")) == NULL)
		cant(filename);
#ifdef	rsx
	max_size = fd->io_rbsz;
	bp = "Record";
	/*
	 * If the file is record oriented, and the user set the -u
	 * (block mode) switch, the attributes in the FDB are fudged to
	 * make the FCS routines think it is an R.FIX file with R.SIZE=512
	 * and no carriage control attributes.  This makes it dump a record
	 * file in block format, so you can see all the RCW's, etc.
	 *
	 * If a record-oriented dump is being done, set the sequence-format
	 * flag to identify printfile format.
	 */
	if (uflag && !frec(fd)) {
		bp = "Block";
		max_size = BSIZ;
		/*
		 * Set to fixed blocked, no attributes, 512 byte records
		 */
		fd->io_fdb[(int) &F_RTYP] = (int) &R_FIX;
		fd->io_fdb[(int) &F_RATT] = 0;
		*((int *) &fd->io_fdb[(int) &F_RSIZ]) = BSIZ;
		seqflag = FALSE;	/* Never do sequencing		*/
	}
	else seqflag = ((fd->io_fdb[(int) &F_RTYP] & 0377) == (int) &R_SEQ);
	if ((buf = malloc(max_size)) == NULL)
		error("Can't allocate record buffer of %d bytes\n", max_size);
#endif

#ifdef	rt11
	/*
	 * Rt11 is always a block mode dump
	 */
	max_size = BSIZ;
	if ((buf = malloc(BSIZ)) == NULL)
		error("Can't allocate block buffer of %d bytes\n", BSIZ);
	bp = "Block";
#endif
	dump(bp);
}

/*
 * Do the dump.
 * `bp' is the banner string
 * (either "block" or "record").
 */
dump(bp)
char *bp;
{
	register int n;
	RECORD		rn;
	long		place;
	long		ftell();

	rn = ZERO;
	while(rn < fbn) {
#ifdef	rsx
		fget(buf, max_size, fd);
#endif
#ifdef	rt11
		fread(buf, 1, max_size, fd);
#endif
		if(feof(fd)) {
			perror(filename);
			return;
		}
		rn++;
	}
	while(rn <= lbn) {
		place = ftell(fd);
#ifdef	rsx
		n = fget(buf, max_size, fd);
		if(feof(fd)) {
			perror(filename);
			return;
		}
#endif
#ifdef	rt11
		n = fread(buf, 1, max_size, fd);
		if(feof(fd)) {
			perror(filename);
			return;
		}
		printf("\n** %s %d at %ld. [%06o %06o] length = %d. bytes\n",
			bp, rn++, place, place, n);
#endif
#ifdef	rsx
		printf(
	"\n** %s %ld: length = %d bytes, VBN = %07lo, block offset = %03lo\n",
			bp, rn++, n, place/512L, place%512L);
		if (seqflag) {
			printf("** record sequence number = %u. [%04x hex]\n",
				*((int *) &fd->io_fdb[(int) &F_SEQN]),
				*((int *) &fd->io_fdb[(int) &F_SEQN]));
		}
#endif
		printf("\n");
		format(buf, n);
	}
}

/*
 * Dump out an `n' byte
 * item according to the flags
 * set by the user.
 */
format(ap, n)
char *ap;
{
	register int  *wp;
	register char *bp;
	register int  i;
	int o, of, nb, c;
	char ab[3], *fmt;

	if(!bflag && !wflag && xflag)
		fmt = "%04x  ";
	else
		fmt = "%06o";
	o = 0;
	while(n) {
		nb = min(16, n);
		of = 1;
		if(wflag) {
			printf(fmt, o);
			of = 0;
			printf(" w");
			wp = ap;
			i = (nb & 017776)/2;	/* hack odd size rec	*/
			while(i--)
				printf("  %06o", *wp++);
			putchar('\n');
		}
		if(aflag) {
			if(of) {
				printf(fmt, o);
				of = 0;
			} else
				printf("      ");
			printf(" a");
			bp = ap;
			i = nb;
			while(i--) {
				c = *bp++ & 0377;
				if(c<' ' || c>'~')
					printf(" %03o", c);
				else
					printf("   %c", c);
			}
			putchar('\n');
		}
		if(bflag) {
			if(of) {
				printf(fmt, o);
				of = 0;
			} else
				printf("      ");
			printf(" b");
			bp = ap;
			i = nb;
			while(i--)
				printf(" %03o", *bp++ & 0377);
			putchar('\n');
		}
		if(fflag) {
			if(of) {
				printf(fmt, o);
				of = 0;
			} else
				printf("      ");
			printf(" 5");
			wp = ap;
			i = nb/2;
			while(i--) {
				r50toa(ab, wp++, 1);
				printf("     %.3s", ab);
			}
			putchar('\n');
		}
		if(xflag) {
			if(of)
				printf(fmt, o);
			else
				printf("      ");
			printf(" x");
			bp = ap;
			i = nb;
			while(i--)
				printf("  %02x", *bp++ & 0377);
			putchar('\n');
		}
		o  += 16;
		ap += 16;
		n  -= nb;
	}
}

/*
 * Compute the minimum of
 * two numbers.
 * Should be in the library.
 */
min(a, b)
{
	if(a < b)
		return(a);
	return(b);
}

/*
 * Cannot open diagnostic.
 * Just exit.
 */
cant(p)
char *p;
{
	fprintf(stderr, "%s: cannot open\n", p);
	exit(IO_ERROR);
}

/*
 * Usage diagnostic.
 * Just exit.
 */
usage()
{
	fprintf(stderr, "Usage: dump [-5abrwx] [-f bn] [-l bn] file\n");
	exit(IO_ERROR);
}
