/* Compile with HiTech C for CP/M-80 v3.09(will also compile with BC4.5)
/* #include <fcntl.h> */
#include <cpm.h>
#include <stddef.h>
#include <ctype.h>
#include <stat.h>
#include <stdio.h>
#include <stdarg.h>
/* #include <io.h> */
/* #include <unixio.h> */
/* #include <dos.h> */
/* #include <bios.h> */
#include <stdlib.h>
#include <string.h>
#include <conio.h> 

/* extern int _fmode; */
FILE * infile;
unsigned long data=0;
/* struct text_info ti; */

struct stat FileStat;

int optDispChar=0;
int optDispAscii=0;
int optDispText=0;
int optSigned=0;
int optAddressBase=16;
int optDataSize=1;
int optDataBase=16;
int optDataEndian=0;
int optExtAscii=0;
int optVerbose=0;
int optDebug=0;
int optPage=0;
int LineCount=16;
int ScreenCount=-1;
int LinesPerScreen=24;
int ScreenHeight=24;
int OverRideLC=0;
int UsedDos=0;
int UsedPosix=0;
unsigned long Count=0;
unsigned long Offset=0;
unsigned long NB=0;
int optInvalid=0;
char * ptrInvoked;
char * ptrIF;
char S[80]="";
int SI=0;
int ret;

static char * CntlNames[33]=
	{"nul" , "soh","stx","etx","eot","enq","ack","bel",
	 "bs"  ,"ht"  ,"lf" ,"vt" ,"ff" ,"cr" ,"so" ,"si",
	 "dle" ,"dc1" ,"dc2","dc3","dc4","nak","syn","etb",
	 "can" ,"em"  ,"sub","esc","fs" ,"gs" ,"rs" ,"us",
	 "spc" 
	};

int strnicmp(char *s1, char *s2,int maxlen)
{
	char ts1;
	char ts2;

	int i = 0;
	int result = 0;

	while ( (i < maxlen) && (result==0) )
	{
		if ( strlen(s1) > i ) ts1 = s1[i]; else ts1 = 0;
		if ( strlen(s2) > i ) ts2 = s2[i]; else ts2 = 0;

		if ( islower(ts1) ) ts1 = ts1 - 32;
		if ( islower(ts2) ) ts2 = ts2 - 32;

		result = ts1 - ts2;

		i++;
	}
	return (result);
}
void Exit(int code, char * message1, char * message2)
{
	if (message1 !=0) fprintf (stderr,"\n   %s\n", message1);
	if (message2 !=0) fprintf (stderr,"\n   %s\n", message2);
	if ( code ==1 ) 
		{
		fprintf (stderr," Usage :\n %s [<options>] [FileName>] [DPL=][<DataPerLine>]\n",ptrInvoked);
		fprintf (stderr,"   Address Base   Z:<base> = O - Octal,  D - Decimal, X - Hex, N - none\n");
		fprintf (stderr,"   Data Base      D:<base> = O - octal,  D - decimal, X - hex\n");
		fprintf (stderr,"   Data size      S:<size> = B - 8bits,  W - 16bits,  L - 32bits\n");
		fprintf (stderr,"   Endianess      E:<dir> =L = Little, B = Big\n");
		fprintf (stderr,"   Display Ascii  A = on,  -A = off\n");
		fprintf (stderr,"   Display Text   T = on,  -T = off\n");
		fprintf (stderr,"   Signed Data    N = on,  -N = off\n");
		fprintf (stderr,"   Display Chars  C = on,  -C = off\n");
		fprintf (stderr,"   Extended Ascii X = on,  -X = off\n");
		fprintf (stderr,"   Pager          P = on,  -P = off\n");
		fprintf (stderr,"   Help           H or ?\n");
		fprintf (stderr,"    Environment Variable is HEXDUMPCMD=\n" );
		fprintf (stderr,"    Defaults are /Z:X /D:X /S:B /E:L /-A /-T /-N /-C /-X /-P and DPL is 16\n");
		};
	exit (code);
}

void SetLineCount (int pLineCount)
{
	if (!OverRideLC) LineCount = pLineCount; 
}
int GetSlashOptions(char * ptrStr)
{
	int j=0; int offOpt=0;
	if (ptrStr[0] != '/' )return (0);
	UsedDos=1;
	for (j = 1; j < strlen(ptrStr) ; j++ )
	{
	switch (ptrStr[j])
		{
		case '/' : break;
		case '+' : offOpt=0;  break;
		case '-' : offOpt=1;  break;
		case 'A' :
			 	if (offOpt==1)
					{optDispAscii = 0; offOpt=0;}
				else
					{optDispAscii = 1; optDispText = 0;};
				break;
		case 'T' :
			  	if (offOpt==1)
					{optDispText = 0;  offOpt=0;}
				else
					{optDispText = 1; optDispAscii = 0; SetLineCount ( 0 ) ;};
				break;
		case 'C' :
		  		if (offOpt==1)
					{optDispChar = 0;  offOpt=0;}
				else
					{optDispChar = 1;};
				break;
		case 'X' :
				if (offOpt==1)
					{optExtAscii = 0;  offOpt=0;}
				else
					{optExtAscii = 1;};
				break;
		case 'V' :
				if (offOpt==1)
					{optVerbose = 0;  offOpt=0;}
				else
					{optVerbose = 1;};
				break;
		case 'G' :
				if (offOpt==1)
					{optDebug = 0;  offOpt=0;}
				else
					{optDebug = 1;};
				break;
		case 'N' :
				if (offOpt==1)
					{optSigned = 0;  offOpt=0;}
				else
					{optSigned = 1;};
				break;
		case 'P' :
				if (offOpt==1)
					{optPage = 0;  offOpt=0;}
				else
					{optPage = 1;};
				break;
		case 'E' :
				if ( (j+2) <= strlen(ptrStr) && (ptrStr[j+1]==':') )
				{
				switch (ptrStr[j+2])
				 	{
				 	case 'L':
						optDataEndian = 0; break;
				 	case 'B':
						optDataEndian = 1; break;
				 	default: 
						fprintf (stderr," Invalid Endianess %c \n" ,(char)ptrStr[j+2]);
						optInvalid=1;
				 	}
				j+=2;
				}
				break;
		case 'D' :
				if ( (j+2) <= strlen(ptrStr) && (ptrStr[j+1]==':') )
				{
				switch (ptrStr[j+2])
					 {
					 case 'O':
						optDataBase = 8; break;
					 case 'D':
						optDataBase = 10; break;
					 case 'X':
						optDataBase = 16; break;
					 default: 
						fprintf (stderr," Invalid Data Base %c \n" ,(char)ptrStr[j+2]);
						optInvalid=1;
					 }
				j+=2;
				}
				break;
		case 'Z' :
				if ( (j+2) <= strlen(ptrStr) && (ptrStr[j+1]==':') )
				{
				switch (ptrStr[j+2])
				 	{
				 	case 'N':
						optAddressBase = 0;  break;
				 	case 'O':
						optAddressBase = 8;   break;
				 	case 'D':
						optAddressBase = 10;  break;
				 	case 'X':
						optAddressBase = 16;   break;
				 	default: 
						fprintf (stderr," Invalid Address Base %c \n" ,(char)ptrStr[j+2]);
					 	optInvalid=1;
				 	}
				j+=2;
				}
				break;
		case 'S' :
				if ( (j+2) <= strlen(ptrStr) && (ptrStr[j+1]==':') )
				{
				switch (ptrStr[j+2])
					 {
					 case 'B':
						optDataSize = 1; break;
					 case 'W':
						optDataSize = 2; SetLineCount ( 8 );break;
					 case 'L':
						optDataSize = 4; SetLineCount ( 4 );break;
					 default: 
						fprintf (stderr," Invalid Data size %c \n" ,(char)ptrStr[j+2]);
						optInvalid=1;
					 }
				j+=2;
				}
				break;
		case '?' :
		case 'H' :
		 	Exit (1,0,0);
		default: fprintf (stderr," Invalid Option %c \n" ,(char)ptrStr[j]);
			optInvalid=1;
		}
	}
	return(1);
}
int GetNamedStringOptions(char *ptrStr)
{
	if (strnicmp("if=",ptrStr,3)==0 ) ptrIF=ptrStr+3;
	else return(0);
 return(1);
}
int GetNamedValueOptions(char * ptrStr)
{
	int ret=0;
		if (strnicmp("DPL=",ptrStr,4)==0 ) {ret = sscanf(ptrStr+4,"%d",&LineCount);  OverRideLC=1;}
		else return(0);

		if (ret!=1)
		{
		 fprintf (stderr," Invalid Value %s \n" ,ptrStr);
		 optInvalid=1;
		}

return (1);
}
void GetOptions(int argc, char * argv[], char * env[])
{
int i ;  int ret;
char * ptrStr ;
ptrInvoked=argv[0];
for (i = 0; i < argc ; i++)
{
  if ( i == 0 ) ptrStr = getenv("HEXDUMPCMD"); else ptrStr = argv[i];
  if ( strlen(ptrStr) != 0 )
  if ( GetSlashOptions(ptrStr) );
  else if (GetNamedValueOptions(ptrStr));
  else if (GetNamedStringOptions(ptrStr));
  else
  {
	ret = sscanf(ptrStr,"%d",&LineCount);
	if (optDebug) fprintf (stderr," %s is %d and ret is %d\n" ,ptrStr,LineCount,ret);
	if ( ret ==0 )
		ptrIF=ptrStr;
	else
		OverRideLC=1;
  }

}
 if ( (optDispAscii==1) || (optDispText==1) ) optDataSize=1;
 if ( (LineCount==0) || (LineCount>79) || (optDispText==1) ) optDispChar=0;

 if (optDataSize == 2) LineCount*=2;
 if (optDataSize == 4) LineCount*=4;

 if (optDebug) fprintf (stderr,"LineCount is %d\n",LineCount);
 if (optDebug) fprintf (stderr,"FileName is %s\n",ptrIF);
}
void PrintCmdLine(int argc, char * argv[], char * env[])
{
int i;
printf("argc is %d \n" , argc);
for (i = 0 ; i < argc ; i++) fprintf(stderr," Argument %d is %s \n",i,argv[i]);
/*
i=0;
while (env[i] != 0) {fprintf(stderr," Environment %d is %s \n",i,env[i]); i++;} 
*/
/*
fprintf(stderr,"optDataSize is %d \n" , optDataSize);
fprintf(stderr,"optDataBase is %d \n" , optDataBase);
fprintf(stderr,"optDataEndian is %d \n" , optDataEndian);
fprintf(stderr,"optAddressBase is %d \n" , optAddressBase);
fprintf(stderr,"optDispChar is %d \n" , optDispChar);
fprintf(stderr,"optDispText is %d \n" , optDispText);
fprintf(stderr,"optDispAscii is %d \n" , optDispAscii);
fprintf(stderr,"optDebug is %d \n" , optDebug);
fprintf(stderr,"optVerbose is %d \n" , optVerbose);
fprintf(stderr,"optSigned is %d \n" , optSigned);
fprintf(stderr,"optPage is %d \n" , optPage);
*/
/*
fprintf(stderr,"window left      %2d\r\n",ti.winleft);
fprintf(stderr,"window top       %2d\r\n",ti.wintop);
fprintf(stderr,"window right     %2d\r\n",ti.winright);
fprintf(stderr,"window bottom    %2d\r\n",ti.winbottom);
fprintf(stderr,"attribute        %2d\r\n",ti.attribute);
fprintf(stderr,"normal attribute %2d\r\n",ti.normattr);
fprintf(stderr,"current mode     %2d\r\n",ti.currmode);
fprintf(stderr,"screen height    %2d\r\n",ti.screenheight);
fprintf(stderr,"screen width     %2d\r\n",ti.screenwidth);
fprintf(stderr,"current x        %2d\r\n",ti.curx);
fprintf(stderr,"current y        %2d\r\n",ti.cury);
 */
}
unsigned char FixDisp(unsigned char data)
{
if (data < 32) return('.');
if (data == 127 ) return('.');
if (optExtAscii==0 &&  data > 126 ) return('.');
return (data);
}
unsigned long GetData( int n )
{

unsigned int nread;
unsigned char data[4]; /* = {0,0,0,0}; */

data[0]=0;data[1]=0;data[2]=0;data[3]=0;

	if (optDataSize == 1 )  
	{  
		if ((nread=fread(data+0,1,1,infile))!=0) 
		{ 
			NB+=nread; 
			if (optDispChar==1) S[SI++]=FixDisp(data[0]) ;
		};
		return ((unsigned long)data[0]);
	};
	if (optDataSize == 2 ) 
	{   
		if ((nread=fread(data+0,1,2,infile))!=0) 
		{ 	NB+=nread; 
			if (optDispChar==1) 
			{
				S[SI++]=FixDisp(data[0]) ; 
				S[SI++]=FixDisp(data[1]) ;
			}
		};
		if (optDataEndian== 0)  /* little */
			return ((unsigned long)data[0] + ((unsigned long)data[1] * 256)) ;
		else  /* big */
			return ((unsigned long)data[1] + ((unsigned long)data[0] * 256)) ;
	};
	if (optDataSize == 4 ) 
	{   
		if ((nread=fread(data+0,1,4,infile))!=0) 
		{ 	NB+=nread; 
			if (optDispChar==1) 
			{
				S[SI++]=FixDisp(data[0]) ; 
				S[SI++]=FixDisp(data[1]) ;
				S[SI++]=FixDisp(data[2]) ;
				S[SI++]=FixDisp(data[3]) ;
			}
		};
		if (optDataEndian == 0)  /* little */
			return ((unsigned long)data[0] + ((unsigned long)data[1] * 256) + ((unsigned long)data[2] * 65536) + ((unsigned long)data[3] * 16777216));
		else  /* big */
			return ((unsigned long)data[3] + ((unsigned long)data[2] * 256) + ((unsigned long)data[1] * 65536) + ((unsigned long)data[0] * 16777216));
	};
return (0) ;
}
void PrintAddress( unsigned long address)
{
		if ( optAddressBase == 0 )
		    printf ("\n"); 
		if ( optAddressBase == 8 )
		    printf ("\n%010lo  ", address);  
		if ( optAddressBase == 10 )
		   printf ("\n%7ld  ", address);   
		if ( optAddressBase == 16 )
		   printf ("\n%08lX  ", address); 
}
void PrintChar(unsigned long data)
{
if (  data <= 32  )
	{
	if (optDispText == 1)
		printf("<%s>",CntlNames[(unsigned char)data]);
	else
		{
		if (strlen(CntlNames[(unsigned char)data])==2)  printf(" %s ",CntlNames[(unsigned char)data]);
		if (strlen(CntlNames[(unsigned char)data])==3)  printf("%s ",CntlNames[(unsigned char)data]);
		};
	}
else
	{
	if ( (optExtAscii == 1) || ( data < 127) )
		{
			if (optDispText == 1)
				printf ("%c",(unsigned char)data);
			else
				printf (" %c  ",(unsigned char)data);
		}
	else
		{
			if( optDataBase == 8 )
				if (optDispText == 1)
					printf ("<%03o>",(unsigned char)data);
				else
					printf ("%03o ",(unsigned char)data);
			if( optDataBase == 10 )
				if (optDispText == 1)
					printf ("<%3d>",(unsigned char)data);
				else
					printf ("%3d ",(unsigned char)data);
			if( optDataBase == 16 )
				if (optDispText == 1)
					printf ("<%2x>",(unsigned char)data);
				else
					printf (" %2x ",(unsigned char)data);
		};
	}
if ( (LineCount==0) && (optDispText == 1) && (data == 10) ) printf("\n");
}
void PrintData( unsigned long data)
{
	if( optDataBase == 8 )
		{
		if(optDataSize == 1 )   printf ("%03o ", data);
		if(optDataSize == 2 )   printf ("%06o ", data);
		if(optDataSize == 4 )   printf ("%011lo ", data);
		};
	if( optDataBase ==10 )
		{
		if(optDataSize == 1 )if (optSigned) printf ("%3d ", (char)data); else printf ("%3u ", (unsigned char)data);
		if(optDataSize == 2 )if (optSigned) printf ("%6d ", (int)data); else printf ("%6u ", (unsigned int)data);
		if(optDataSize == 4 )if (optSigned) printf ("%11ld ", (long)data); else printf ("%11lu ", (unsigned long)data);
		};
	if( optDataBase ==16 )
		{
		if(optDataSize == 1 )printf ("%02X ", data);
		if(optDataSize == 2 )printf ("%04X ", data);
		if(optDataSize == 4 )printf ("%08lX ", data);
		};
}
void DisplayCharS ()
{
	if (optDispChar==1)
	{
		S[SI]=0;
		printf("%s",S);
		SI=0;
	};
}
int GetKeyCh(void)
{
int key=0;

/* Wait until a key is pressed */
/* while (kbhit() == 0); */

/* Fetch the key that is waiting */
key = getch();
switch (key)
	{
	case 3:     /* cntrl-c */
	case 4 :    /* cntrl-d */
	case 26:  Exit(0,0,0);  break;   /* cntrl-z */
	case '0': ScreenCount=0; optPage=0; break;
	case ' ': ScreenCount=0; LinesPerScreen=1; break;
	case '1': ScreenCount=0; LinesPerScreen=1; break;
	case '2': ScreenCount=0; LinesPerScreen=2; break;
	case '3': ScreenCount=0; LinesPerScreen=3; break;
	case '4': ScreenCount=0; LinesPerScreen=4; break;
	case '5': ScreenCount=0; LinesPerScreen=5; break;
	case '6': ScreenCount=0; LinesPerScreen=6; break;
	case '7': ScreenCount=0; LinesPerScreen=7; break;
	case '8': ScreenCount=0; LinesPerScreen=8; break;
	case '9': ScreenCount=0; LinesPerScreen=9; break;
	case 13 : ScreenCount=0; LinesPerScreen=ScreenHeight; break;
	default : ScreenCount=0; LinesPerScreen=ScreenHeight/2;
	}
return (key & 0xff);
}
int main(int argc, char * argv[], char * env[])
{
/* this to fake out Hi-Tech Compiler into correctly linking to _fgets */
	if(ScreenHeight==-1) fgets(S,1,infile);

 	ScreenHeight=24;/* ti.screenheight */
	LinesPerScreen = ScreenHeight;
	GetOptions(argc,argv,env);
	if (optVerbose) fprintf(stderr,"\nHex Dump Version 1.0 for CP/M-80\n");
	if (optDebug)PrintCmdLine (argc,argv,env);
	if (optInvalid == 1) Exit(1,0,0);

	if (ptrIF != 0 )
		{
		if ( (infile = fopen( ptrIF, "rb" ))==NULL ) fprintf(stderr,"\nCan not open %s",ptrIF);
		}
	else
		Exit(1,0,0);

	stat(ptrIF, &FileStat);

	while ( (!feof(infile)) && (NB < FileStat.st_size) )
		{
		if ( (LineCount != 0 )&&((NB % LineCount) == 0) )
			{
			DisplayCharS();
 			if ((optPage==1) && (++ScreenCount >= LinesPerScreen) )
				{  
				int ch=0;
				ch = GetKeyCh();
				}
			PrintAddress(NB);
			}
		data=GetData(optDataSize);
		if ( (optDispAscii==1) || (optDispText==1) )  
			PrintChar (data);
		else
			PrintData(data);
		}
	DisplayCharS ();
	puts("\n");
	if (optVerbose) fprintf(stderr,"\n %9ld - 0o%lo - 0x%08lX bytes processed\n",NB ,NB, NB);
	return 0;
}

