/******************************************************************************
*
*   Copyright P.J.Ruczynski 1990
*   This software is free for redistribution and re-use as long as this
*   copyright is included in all source files. This software is supplied
*   as is and no responsibilty is taken by the author for any problems
*   arising from this code.
*
* File name		-  hd.c
*
* Module name		-  HD
*
* Author		-  P.J.Ruczynski	<pjr@pyra.co.uk>
*
* First Release		-  16 Feb 1990
*
* Version number	-  1.4
*
* Description		-  A hexdump formatting tool, this can take input from
*			   stdin or a file and print it to stdout in a neat
*			   hexdump format.
*			   Basically, a better/different(*) version of od.
*
*				(*) delete as you think appropriate :-)
*
*			Revision List
*
* pjr   08.05.89        Added offset numbering. 
*
* pjr   11.05.89        Added compressed output format. 
*
* pjr	30.05.89	Changed to allow any block size to be used, default
*			is still 1024 though.
*
* pjr	31.05.89	Added version printing option.
*
* pjr	02.06.89	Added start and end block specification.
*
******************************************************************************/

#define VERSION		"1.4 (14.09.89)"
#define PRINTUSAGE	fprintf(stderr,"Usage: %s [-cnov] [-b n] [-sp|-sn n] [-sb n] [-eb n] [filename]\n", progname);

/*
 * This file contains the following routines:
 *
 * main()
 * get_options()
 * openfile()
 * closefile()
 */

#include <stdio.h>
#include <sys/types.h>
#include <sys/file.h>
#include <string.h>
#include <fcntl.h>
#include "hexprint.h"

#define	MAXLINE		256	/* Length of character strings. */
#define	MAX_BUFSIZ	1024	/* default read bufer size */

/*
 * these are control vars for the hexprint routine
 */
extern int offset_print;	/* only print offsets on request */
extern long offset;		/* default start offset for printing */
extern int hex_compression;	/* compress identical lines */

/*
 * option variables
 */
char progname[MAXLINE];		/* Name of this program. */
int b_start = 1;		/* start block no to be printed */
int b_end = 0;			/* end block no to be printed */
int b_size = MAX_BUFSIZ;	/* block size */
int blocking_on = FALSE;	/* only block up on request */
char fname[MAXLINE];		/* name of input file (if any) */
int block_numbering = FALSE;	/* output number of each block ? */
int print_start_block = FALSE;	/* print the very first diff size block ? */
int sb_size = 0;		/* staring block size */
long n_blocks = 0;		/* no blocks to print, 0 => all */

/*
 * get_options
 *
 * Read and process command line options.
 */
/*****************************************************************************/
void get_options(argc,argv)
/*****************************************************************************/
int argc;
char *argv[];
{
int i;

    fname[0] = 0;	/* indicate no filename input */
    argc-- ; argv++ ;	/* skip the program name */

    while (argc > 0)	/* now get the rest */
    {
      if (argv[0][0] == '-')
	switch (argv[0][1])
	{
	case 'b' :	/* block size to output */
		argv++;
		b_size = (int)strtol(&argv[0][0], (char **)NULL, 0);
		printf("block size = %d (0x%x) bytes\n",
				b_size,(unsigned)b_size);
		blocking_on = TRUE;
		argc--;
		break;
	case 'c' :
	case 'n' :
	case 'o' :
	case 'v' :
		i = 1;
		while (argv[0][i])
		{
			switch (argv[0][i])
			{
			case 'c' :	/* compress identical lines */
				hex_compression = TRUE;
				break;
			case 'n' :	/* switch on block numbering */
				block_numbering = TRUE;
				blocking_on = TRUE;
				break;
			case 'o' :	/* switch on offset printing */
				offset_print = TRUE;
				break;
			case 'v' :	/* print the version of this hd */
				printf("%s version %s Author P.Ruczynski\n",
						progname,VERSION);
				break;
			default : PRINTUSAGE
				exit(1);
			} /* end of switch */
			i++;
		} /* end of while */
		break;
	case 'e' :	/* end block options */
		switch (argv[0][2])
		{
		case 'b' :	/* do not print starting block */
			argv++;
			b_end = strtol(&argv[0][0], (char **)NULL, 0);
			printf("last block printed = %d (0x%x) bytes\n",
					b_end,(unsigned)b_end);
			argc--;
			break;
		default: PRINTUSAGE
			exit(1);
		}
		break;
	case 's' :	/* starting block options */
		switch (argv[0][2])
		{
		case 'p' :	/* print the starting block */
			print_start_block = TRUE;
			/* FALL THROUGH */
		case 'n' :	/* do not print starting block */
			argv++;
			sb_size = strtol(&argv[0][0], (char **)NULL, 0);
			printf("starting block size = %d (0x%x) bytes\n",
					sb_size,(unsigned)sb_size);
			argc--;
			break;
		case 'b' :	/* get the starting block number */
			argv++;
			b_start = strtol(&argv[0][0], (char **)NULL, 0);
			printf("first block printed = %d (0x%x) bytes\n",
					b_start,(unsigned)b_start);
			argc--;
			break;
		default: PRINTUSAGE
			exit(1);
		}
		break;
	case '?' :
	default  : PRINTUSAGE
		exit(1);
	} /* end of case */
	else	/* not an option so must be file name */
	{
		strcpy(&fname[0], &argv[0][0]);
		printf("input file = %s\n",fname);
	}

	argc-- ;
	argv++ ;

    } /* end of while loop */

} /* end of get_options */
 

/*
 * openfile
 *
 * open the requested file or assign stdin for reading
 */
/*****************************************************************************/
int openfile(fname)
/*****************************************************************************/
char *fname;
{
int fd;	/* file descriptor to read from */

	if (fname[0] != 0)
		if ((fd = open(fname, O_RDONLY)) < 0)
		{
			perror(progname);
			exit(1);
		}
		else
			return(fd);
	else
		return(0);	/* if no file then stdin */
} /* end of openfile */


/*
 * closefile
 *
 * close the given file descriptor
 */
/*****************************************************************************/
void closefile(fd)
/*****************************************************************************/
int fd;
{
	if (fd != 0)
		close(fd);
} /* end of closefile */


/*
 * printfile
 *
 * print the given file descriptor
 */
/*****************************************************************************/
void printfile(fd)
/*****************************************************************************/
int fd;
{
char *buf;	/*[MAX_BUFSIZ]; */
int n, x=0, y=0, i;
long bn = 1;

	if ((buf = (char *)malloc(b_size)) == (char *)NULL)
	{
		printf("%s: error in block size memory allocation\n",progname);
		exit(1);
	}

	/*
	 * this first 'if' handles the start block case (if there is one)
	 */
	if (sb_size != 0)
	{
		x = sb_size % MAX_BUFSIZ;	/* the very last bit */
		y = sb_size / MAX_BUFSIZ;	/* the no of full bufs */
		sb_size = MAX_BUFSIZ;		/* optimum read size. 1K */

		if ((block_numbering) && (print_start_block))
			printf("start block\n");

		for (i=0; i<=y; i++)
		{
			if (i==y)
				sb_size = x;

			if (((n=read(fd, &buf[0], sb_size)) > 0) &&
					(print_start_block))
				hexprint(&buf[0], n);
		
			offset += n;
		}

		if (((blocking_on) || (hex_compression))
		    && (print_start_block))
			printf("\n");
	}
		
	while ((n = read(fd, &buf[0], b_size)) > 0)
	{
		if ((block_numbering) && (bn >= b_start) &&
		    ((bn <= b_end) || (b_end == 0)))
		{
			printf("block number %d (0x%x) ", bn, bn);
			if (n < b_size)
				printf("size = %d",n);
			printf("\n");
		}

		if (blocking_on)
		{
			if (bn >= b_start)
			{
				if (b_end == 0)
				{
					hexprint(&buf[0], n);
					offset += n;
				}
				else if (bn <= b_end)
				{
					hexprint(&buf[0], n);
					offset += n;
				}
			}
			bn++;
		}
		else
		{
			hexprint(&buf[0], n);
			offset += n;
		}

		if (((blocking_on) || (hex_compression)) &&
		     (bn >= b_start) && ((bn <= b_end) || (b_end == 0)))
			printf("\n");
	}

	free(buf);

} /* end of printfile */

/*
 * main
 */
/*****************************************************************************/
main(argc,argv)
/*****************************************************************************/
int argc;
char **argv;
{
int fd;	/* file descriptor to read from */

	strcpy(progname, argv[0]);  /* Save this program name. */
	get_options(argc,argv);     /* Read and process command line options. */

	fd = openfile(fname);	    /* do this cos input maybe stdin */
	printfile(fd);
	closefile(fd);

	exit(0);

} /* end of main */


