/****************************************************************************
 *	scan.c  Version 1.0						
 *
 *	29 December 1990
 *	Compiled with SAS/C 5.10  
 *
 *	Copyright © 1990 By Dan Fish
 *	All rights reserved.
 *
 *	Permission is granted to freely redistribute this program provided 
 *	the source code is included in the distribution and this copyright 
 *	notice is unchanged.
 *
 ****************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <fcntl.h>
#include <exec/types.h>
#include <intuition/intuition.h>

#include "ansicodes.h"

#define BUFFSIZE 2048L       /* length of I/O buffer */
#define MAXFILELEN 80
#define SPACEBAR 32

char *key[] = {
        "NULLS     ","^A (SOH)  ","^B (STX)  ","^C (ETX)  ","^D (EOT)  ",
        "^E (ENQ)  ","^F (ACK)  ","^G (BELL) ","Backspaces","Tabs      ",
        "Line Feeds","Vert Tabs ","Formfeeds ","Returns   ","^N (SO)   ",
        "^O (SI)   ","^P (DLE)  ","^Q (DC1)  ","^R (DC2)  ","^S (DC3)  ",
        "^T (DC4)  ","^U (NAK)  ","^V (SYN)  ","^W (ETB)  ","^X (CAN)  ",
        "^Y (EM)   ","^Z (SUB)  ","ESC       ","^\\ (FS)   ","^] (GS)   ",
        "^^ (RS)   ","^- (US)   ","Spaces    " };


char WindowTitle[] = "  ASCII     HEX    Count  Character   Percent";
char *fname;

struct numlist {
		 long ascii;
		 long count;
		};
struct numlist num[256];
struct numlist *numptr;

long temp[256];

long totalchars=0;

int diffchars=0, pages, page=1;

main (argc,argv)
int argc;
char *argv[];
{

  void DisplayData();
  void WriteData();
  void GetData();
  void BooBoo();
  void Usage();
  extern struct Window *mywindow;  /* Console window	*/
  extern short pagelen;  
  char oname[MAXFILELEN+1];
  
  int in;
  FILE *out;
  int outfile = FALSE;
  int sort = FALSE;
  int loop;
  
  numptr=num;

/* parse the command line  */
 
 if (( argc<2) || (*argv[1] =='?') || argv[1][1] == '?')
    {
      Usage();
      exit(10);
    }   
 
 for (loop = argc-1; loop > 1; --loop)
   if (!(argv[loop][0] == '-' ))  /* then it better be an output filename! */  
    {
     if(outfile)
       BooBoo(3,(char *)NULL);                  /* already have an outfile! */
     else { 
       outfile = TRUE;
       stccpy(oname,argv[loop],MAXFILELEN); }
    }

 for (loop = argc-1; loop > 0; --loop)
   if(argv[loop][0] == '-')  /* options */  
    {
     if(strpbrk(argv[loop],"sS") != (char *)NULL)
       sort = TRUE;
    }


 if ((in = open(argv[1], O_RDONLY, NULL)) < 0)
    BooBoo(1,argv[1]);


 if(outfile)
   if ((out = fopen(oname, "w")) == NULL)
    {
      close(in);
      BooBoo(2,oname);
    }

  fname=argv[1];

 GetData(in,sort);

 if(outfile)
   WriteData(out);
 else 
   DisplayData();
 
 exit(0);
}

/************************************************************************
* Function : WriteData							*
* Arguments: fp - pointer to output file structure			*
* Returns  : nothing							*
* Purpose  : Writes the data to an output file instead of the screen	*
*************************************************************************/

void WriteData(fp)
FILE *fp;
{
 char *Line();
 int error=0;
 register j;
 fprintf(fp,"   File: %s     Total: %-6d     Different: %-3d\n\n",
              fname,totalchars,diffchars);

 for(j = 0, numptr = num; j < diffchars; j++, numptr++)
 {
 error = fputs(Line(),fp);
 if(error)
   fprintf(stderr,"Scan: Error during write! Iteration = %3d, Ascii = %3d\n",
           j,numptr->ascii);
 error = fputs("\n",fp);

 }
 
fclose(fp); 
}

/************************************************************************
* Function : GetData							*
* Arguments: fh - file handle of input file				*
*	     sort - TRUE if output is to be sorted by frequency		*
* Returns  : nothing							*
* Purpose  : Creates an array containing the number of each character 	*
*	     found in the input file.  Sorts the array if applicable	*
*************************************************************************/
void GetData(fh,sort)
int fh,sort; 				/*input file handle */
{
  int ch;
  register j=0;
  int frequency_sort();

  while ((ch = Fetch(fh)) != EOF)
   {
     temp[ch]++;	/* Increment the "temp[]" value that	*/
			/* corresponds to the ASCII value 	*/
			/* of the character just read		*/

     totalchars++;	/* Running tab on total # of char's	*/
   }

/* Create an array of only characters contained in the file by filtering */
/* out all "temps[]" with a value of zero				 */

  while (j < 256)
   {
    if (temp[j] != 0)
     {
      num[diffchars].ascii = j;
      num[diffchars].count = temp[j];
      diffchars++;
     }   
    j++;
}

 if (sort)
  qsort((char *)num,diffchars+1,sizeof(struct numlist),frequency_sort);

}

/************************************************************************
* Function : DisplayData						*
* Arguments: none							*
* Returns  : nothing							*
* Purpose  : Handles (or call functions to handle) all outputing of	*
*	     data to the screen.					*
*************************************************************************/
void DisplayData()
{
  void mysetup();
  extern short pagelen;
  void DisplayLine();
  void DisplayPage();
  int getkey;

  char quit[] = {'q','Q','\x1b','\003','\0'}; /* Null terminated string of */
					      /* characters that will end  */
          				      /* the program               */
	
  mysetup();	/* Initialize the output console */
  numptr = num;
  cursor_off();

  if ((diffchars % pagelen)==0)  /* the number of different characters will  */
   pages = diffchars/pagelen;    /* exactly fill a given number of pages     */
  else
   pages = (diffchars/pagelen)+1;/* gonna need an extra page 	*/


  DisplayPage();    		 /* Display the first "pagelen" lines 	*/

  while(strchr(quit, getkey = getchar()) == (char *)NULL)
     {
      if (getkey == SPACEBAR && pages > 1)
       {
        if(numptr < &num[diffchars])  
          DisplayPage();              /* display next page		*/
        else
        {
         numptr = num;	/* go back to first page */
         DisplayPage();
        }
       }
     }
    return;
   }

/************************************************************************
* Function : DisplayLine						*
* Arguments: yval-- position on the screen where line is displayed	*
* Returns  : nothing							*
* Purpose  : Output a line to the screen positioned at "yval". If 	*
*	     "numptr->ascii" is non-printable, the line is highlighted	*       
*************************************************************************/
void DisplayLine(yval)
int yval;
{
 char print;
 char *Line();
 put_cursor(1,yval);
 if(!(print = isprint(numptr->ascii)))
   setforecolor3(); 
 printf("%s",Line());
 if(!print)
   set_normal();
}

/************************************************************************
* Function : *Line							*
* Arguments: None							*
* Returns  : pointer to buffer containing the formatted line		*
* Purpose  : Format a line of information about character 		*
*	     "numptr->ascii".  This information includes its 		*
* 	     ascii value, hex value, common reference, number of  	*
*	     occurences and % of total					*  
*************************************************************************/
char *Line()
{
 static char linebuff[80];
 char tempbuff[20];
 if (numptr->ascii < 33)
   sprintf(tempbuff,"%10s ",key[numptr->ascii]);  /* a more familiar form */
 else if(numptr->ascii > 127 && numptr->ascii < 160)
    sprintf(tempbuff,"           ");
 else
    sprintf(tempbuff,"%-10c ",numptr->ascii);	/* the actual character */

 sprintf(linebuff,"  [%3d]    [%3x] %7d  ",numptr->ascii,numptr->ascii,
         numptr->count);

 strcat(linebuff,tempbuff);

	 sprintf(tempbuff,"%6.2f %%",
        (((float)numptr->count)/((float)totalchars))*100.0); 
 strcat(linebuff,tempbuff);
 return(linebuff);
} 
 
/************************************************************************
* Function : DisplayPage						*
* Arguments: none							*
* Returns  : nothing							*
* Purpose  : Handles the screen formatting and display of an entire	*
*            page of lines formatted by *Line().  Also displays 	*
* 	     filename and totalizer information at the bottom		*
*************************************************************************/
void DisplayPage()
{
  extern short pagelen;
  void DisplayLine();
  int j;
  char filnam[29];

  for (j=0; j < pagelen; j++)		/* 22 lines per page */
   {
    if (numptr > &num[diffchars-1])	/* if this is the last character */
      erase_eos();			/* get rid of any old junk left  */
    else 				/* from the previous page        */
     {
      DisplayLine(j+1);		/* display the next line */
      printf("\n");
      numptr++;  /* increment pointer to next non-empty charval */
     }
   }
 
    stccpy(filnam,fname,27); 		/* Ensure filename will fit on 	*/
					/* the last line!		*/
    cursor_bot();			
    set_hilite();
    printf(" Page %d of %d ",page,pages);
    printf(" Total: %-6d ",totalchars);
    printf(" Different: %-3d ",diffchars);
    printf(" File: %s ",filnam);
    set_normal();
    page++;
    if (page > pages)
      page = 1;
}

/************************************************************************
* Function : Fetch							*
* Arguments: fh - file handle of file to read				*
* Returns  : a character (int) or EOF					*
* Purpose  : Performs  buffered low-level file reading, refilling	* 
*	     buffer when last character has been "fetched"		*
*	     Returns EOF if no more characters in file			*
*************************************************************************/
Fetch(fh)
int fh; 				/*input file handle */
{
  static unsigned char buffer[BUFFSIZE];	/* input buffer */
  static short nextch = BUFFSIZE-1;
  static short lastch = BUFFSIZE-1;

  if(++nextch >= lastch)		/* If buffer used up,  */
   {					/* refill it from file */
     lastch = read(fh,buffer,BUFFSIZE);
     nextch = 0;
   }

  if (lastch == 0)			/* EOF, close up shop */
   {
     close(fh);
     return(EOF);
   }

  return((int)buffer[nextch]);		/* return next character */
}

/************************************************************************
* Function : Usage							*
* Arguments: none							*
* Returns  : nothing							*
* Purpose  : Displays the proper usage format				*
*************************************************************************/
void Usage()
{
  fprintf(stderr,"\x1b[5m");
  fprintf(stderr,"\n\x1b[1;33m  Usage: \x1b[0m");
  fprintf(stderr,"Scan <infile> [outfile] [-s] \x1b[0m\n\n");
}

/************************************************************************
* Function : BooBoo							*
* Arguments: cause - integer for determining the nature of the problem  *
*            string - pointer to a character string for screen output	*
* Returns  : nothing							*
* Purpose  : Displays an error message in the event of problems parsing *
*	     command line arguments or accessing files			*
*************************************************************************/
void BooBoo(cause,string)
int cause;
char *string;
{
  void Usage();

  fprintf(stderr,"\n\x1b[33m   Scan: \x1b[32m");
  if (cause == 1)
    fprintf(stderr,"Can't open \"%s\" for input! \n\n", string);
  else if(cause == 2)
    fprintf(stderr,"Can't open \"%s\" for output! \n\n", string);
  else if(cause == 3)
   {
    fprintf(stderr,"Too many file names!\n");
    Usage();
   } 
 fprintf(stderr,"\x1b[0m");
 exit(-cause);
}

/************************************************************************
* Function : frequency_sort						*
* Arguments: two pointers to the items being compared			*
* Purpose  : provides the sorting algorithm for qsort()			*
*************************************************************************/
int frequency_sort(num1,num2)
struct numlist *num1,*num2;
{
 if (num1->count < num2->count)
     return(1);
 else if (num1->count == num2->count)
     return(0);
 else 
     return(-1);
}
