/* Ptype.c - type to the printer       Author: Robert C. Alston
*            version 1.1               Date:   12-12-85
*
*Format:
* Ptype <file> [[TO] <file2>] [W <columns>] [L <lines>] [T <tabs>] [NN] [SH]
*
*Specification:
*
* <file>       "from" file name
*
* TO           optional keyword
* <file2>      "to" file name
*              (if "to" file is omitted, "par:" is assumed)
*
* W <columns>  maximum width of lines for output (default = 92)
*              (note "W 0" eliminates these additional newlines)
* L <lines>    maximum lines before formfeed with heading (default = 58)
*              (note "L 0" eliminates this formfeed and suppresses headings)
* T <tabs>     tab spacing (default = 3)
*              (note "T 0" eliminates expansion of tabs to spaces)
* NN           no line numbers (default is with line numbers)
* SH           suppress heading at top of each page (default is not supp.)
*
*Special processing:
*
* This program is intended for use only with text files.  The following
* special processing is performed on control characters.  Other characters
* in the range from 0x00 thru 0x1F are changed to blanks.
*
*     \n       end of a line
*     \r       deleted (not output)
*     \t       expanded to spaces (see "T <tabs>" above)
*     \f       passed thru unchanged and line count is reset
*     \v       passed thru unchanged
*
*
* Line numbers are added at the front of each each input source line
* unless the no-numbers option, "NN", is specified. 
*
* Extra newlines ('\n') may be inserted based on the line width option
* (see the "W <columns>" option above).  Note that the page heading,
* which is output in columns 1 thru 75, is not affected by this option.
*
* Because the Amiga editor (ED) does not allow typing the form feed
* character into the text file, a form feed character ('\f') is
* inserted in the output file, after the page eject sequence at
* the bottom of this page, to allow page ejects.
*
* Also formfeeds are inserted into the output file if the lines-per-page
* option is not specified as 0 (see "L <lines>" above).  After every
* formfeed in the output file (and at the start of the file) a page
* heading line is output if "L 0" is not specified.
*
*/ /*eject*/
#include <stdio.h>                  /* include headers */
#include <ctype.h>

#define TRUE      1                 /* define TRUE & FALSE */
#define FALSE     0

/* file parameters */
FILE *file1, *file2;                /* file pointers */
#define NAMELEN   256               /* max length of file name strings */
char name1[NAMELEN];                /* file 1 name */
char name2[NAMELEN] = "par:";       /* file 2 name */

/* print parameters */

int width = 92;                     /* line width */
int lines = 58;                     /* lines per page */
int tabs = 3;                       /* tab spacing */
unsigned char numflag = TRUE;       /* print line numbers flag */
unsigned char sheadflag = FALSE;    /* suppress heading flag */

#define LINENLEN  5                 /* line number length */
#define PAGENLEN  3                 /* page number length */
#define HEADLLEN  75                /* heading line len w/o \n or NULL */
#define HEADPNUM  (HEADLLEN-PAGENLEN) /* offset for heading page number */
#define HEADPAGE  (HEADPNUM-4)      /* offset for heading "PAGE" */
#define HEADTIME  (HEADPAGE-12)     /* offset for heading time */
#define HEADDATE  (HEADTIME-10)     /* offset for heading date */
#define HEADDAY   (HEADDATE-10)     /* offset for heading day */
char heading[HEADLLEN+3];           /* heading line */
char pagelit[] = "PAGE";            /* page literal */

#define OPTW      15                /* offset for width */
#define OPTL      25                /* offset for lines */
#define OPTT      34                /* offset for tabs */
#define OPTN      39                /* offset for no numbers */
#define OPTS      41                /* offset for suppress heading */
char optmsg[] = "Options:  Width     Lines     Tabs    N   H\n";

/* misc */

#define CTRLCHAR  0x20              /* largest control char + 1 */
int linecnt = -1;                   /* lines left count per page */
int lineno = 0;                     /* current line number */
int linewidth;                      /* calculated line width */
int pageno = 0;                     /* page number */
#define BUFLEN    256               /* length of line i/o buffers */
char buf1[BUFLEN+2];                /* input buffer */
char buf2[BUFLEN+2];                /* output buffer */
int  buf2i;                         /* index for buf2 */

char peject[] = {'/','*','e','j','e','c','t','*','/','\0'};
int pejectlen;                      /* calculated length of eject seq */
/*eject*/
void main(argc,argv)
int   argc;                         /* argument count */
char  *argv[];                      /* array of pointers to args */
{
   int error;                       /* error return flag */
   int d;                           /* dummy */
   void inithead(), putstring(), itoa();

   if (argc == 0)                   /* if called by icon */
      return;                       /* error - not handled */
   error = getparms(argc,argv);     /* process command line parameters */
   itoa(width,3,'Z',optmsg+OPTW);
   itoa(lines,3,'Z',optmsg+OPTL);   /* put numbers in message */
   itoa(tabs,2,'Z',optmsg+OPTT);
   if (!numflag)                    /* if no numbers */
      *(optmsg+OPTN) = 'N';
   if (sheadflag)                   /* if suppress heading */
      *(optmsg+OPTS) = 'S';
   putstring(optmsg);               /* display options message */
   if (error)
      exit(20);                     /* bad parms - exit */
   inithead();                      /* init heading etc. */
   file1 = fopen(name1,"r");        /* open input file */
   if (file1 == NULL) {             /* if error */
      putstring("Can't open file \"");
      putstring(name1);
      putstring("\"\n");
      exit(20);                     /* set return error code */
   }                             
   file2 = fopen(name2,"w");        /* open output file */
   if (file2 == NULL) {             /* if error */
      putstring("Can't open file \"");
      putstring(name2);
      putstring("\"\n");
      d = fclose(file1);            /* close input file */
      exit(20);                     /* set return error code */
   }
   putstring("Processing \"");      /* display messsage */
   putstring(name1);
   putstring("\" to \"");
   putstring(name2);
   putstring("\"\n");
   while (getline() && !error)      /* get a line, until EOF */
      error = convline();           /* convert and output lines */
   if (lines && !error)             /* if auto pageing active */
      d = putc('\f',file2);         /* eject the last page */
   d = fclose(file1);               /* close input file */
   d = fclose(file2);               /* close output file */
   if (error) {
      putstring("Error writing file \"");
      putstring(name2);
      putstring("\"\n");
      exit(20);                     /* if error - exit */
   }
   return;
}
/*eject*/
/* get command line parameters */
getparms(argc,argv)
int   argc;                         /* argument count */
char  *argv[];                      /* array of pointers to args */
{
   int error;                       /* error flag */
   int fileflag;                    /* <outfile> processed flag */
   int argn;                        /* arg index */
   char *cp;                        /* pointer to curr string */
   char *cpu;                       /* pointer to upper case string */
   char *strncpy(), *supper();      /* functions */
   void putstring();
   char *cpd;                       /* dummy */

   error = FALSE;                   /* init to no error */
   fileflag = FALSE;                /* init to <outfile> not processed */
   while (1) {                      /* provide exit point by break */
      if (argc < 2) {               /* if no <file> specified */
         error = TRUE;              /* set error */
         break;                     /* exit the loop */
      }
      cp = argv[1];                 /* point to <file> */
      if (*cp == '?') {             /* if format inquiry */
         error = TRUE;              /* set error */
         break;                     /* exit */
      }
                                    /* copy parm1 <file> */
      cpd = strncpy(name1,cp,NAMELEN);
      argn = 2;                     /* start at parm2 */
      while (argn < argc) {         /* while arguments */
         cp = argv[argn++];         /* get string pointer */
         cpu = supper(cp);          /* convert 3 chars to upper */
         if (strcmp(cpu,"W") == 0) {
            width = atoi(argv[argn++]);   /* width */
            if (width > BUFLEN-1)         /* if too large */
               width = BUFLEN - 1;        /* set it */
         }
         else if (strcmp(cpu,"L") == 0)
            lines = atoi(argv[argn++]);   /* lines */
         else if (strcmp(cpu,"T") == 0)
            tabs = atoi(argv[argn++]);    /* tab spacing */
         else if (strcmp(cpu,"NN") == 0)
            numflag = FALSE;              /* no numbers */
         else if (strcmp(cpu,"SH") == 0)
            sheadflag = TRUE;             /* supress heading */
         else {
            if (fileflag)           /* if "to" file processed */
               error = TRUE;        /* unknown param */
            else {                  /* else check for "to" file */
               if (strcmp(cpu,"TO") == 0)
                  cp = argv[argn++]; /* skip "TO" */
               cpd = strncpy(name2,cp,NAMELEN);
               fileflag = TRUE;     /* set processed flag */
            }
         }
      }
      break;
   }
   if (argn != argc)                /* if args not processed ok */
      error = TRUE;                 /* set error */
   if (error) {                     /* if error */
      putstring("Format:\n");       /* display format message */
      putstring(" Ptype <file> [[TO] <outfile>] [W <columns>] ");
      putstring("[L <lines>] [T <tabs>] [NN] [SH]\n");
   }
   return(error);
}
/*eject*/
/* get a line
*
*  Get a line of text into buf1 for a maximum length of BUFLEN+2 including
*  the trailing NULL.  The ending \n is followed by the NULL.  Any \r
*  characters are discarded.  Characters \t, \f, and \v are passed thur
*  unchanged.  All other control chars (0x00 thru 0x1F) are changed to
*  blanks.  Returns FALSE if EOF reached.
*/
getline()
{
   int ich;                         /* int of input char */
   int i;                           /* char count */

   i = 0;                           /* init output count */
   while (i < BUFLEN) {             /* while room in buffer */
      ich = getc(file1);            /* get input char */
      if (ich == EOF || ich == '\n')
         break;                     /* exit on newline or EOF */
      if (ich != '\r') {            /* ignore carriage return */
         if (ich >= 0 && ich < CTRLCHAR)
            if (ich != '\f' && ich != '\t' && ich != '\v')
               ich = ' ';           /* convert ctrl chars to blank */
         buf1[i++] = ich;
      }
   }
   if (ich == EOF)                  /* if EOF */
      ich = FALSE;                  /* set return FALSE */
   else                             /* else TRUE */
      ich = TRUE;
   buf1[i++] = '\n';                /* put newline and NULL */
   buf1[i] = 0;
   return(ich);                     /* return eof status */
}
/*eject*/
/* convert input line to output line
*
*  The line number is placed at the front of the line if numflag
*  is TRUE.  If tabs is not 0, then tabs are expanded to spaces.
*  If width is not 0, then the output line will be no longer than
*  width plus \n, [\f], and \0.  error = TRUE will be returned if
*  an error is detected writing to the output file (see putline()).
*  If the peject sequence is detected, a \f is inserted after
*  it.  If the output line ends in \f\n, the two chars are
*  reversed to \n\f.
*/
convline()
{
   int error;                       /* output error returned */
   char *buf1p;                     /* pointer to input buffer */
   int tcnt;                        /* count for tab expansion */
   char ch;                         /* char from input buffer */
   void itoa();                     /* declare function */

   error = FALSE;                   /* assume no output error */
   buf1p = buf1;                    /* init in pointer */
   buf2i = 0;                       /* init out count */
   tcnt = 0;                        /* init tab column count */
   if (numflag) {                   /* if line numbers */
      itoa(++lineno,LINENLEN,'Z',buf2);  /* get line number */
      buf2i = LINENLEN;
      buf2[buf2i++] = ' ';          /* add 2 spaces */
      buf2[buf2i++] = ' ';
   }
   while ((ch = *buf1p++) && !error) /* until NULL char */
      if (ch == '\t' && tabs)       /* if a tab to expand */
         do
            error = charbuf2(' ');  /* put blanks */
         while (++tcnt % tabs != 0 && !error);
      else {
         error = charbuf2(ch);      /* put char in buffer */
         if (ch < 0 || ch >= CTRLCHAR)
            ++tcnt;                 /* bump tab count for non ctrl chars */
         if (ch == '\n' && !error) { /* if end of line */
            buf2[buf2i] = 0;        /* put NULL on end */
            error = putline();      /* output line to file */
         }
      }
   return(error);                   /* return error code */
}
/*eject*/
/* put a char in buf2
*     Put a character in buf2 and do not allow a line too long.
*/
charbuf2(ch)
char ch;
{
   int error;                       /* error flag */

   error = FALSE;                   /* assume no error */
   buf2[buf2i++] = ch;              /* put char in buffer */
   if (buf2i >= linewidth && ch != '\n') { /* if max chars */
      buf2[buf2i++] = '\n';         /* put newline char */
      buf2[buf2i] = 0;              /* end with a null */
      error = putline();            /* output the line */
      buf2i = 0;                    /* start at front of buffer */
   }
   return(error);
}
/*eject*/
/* write line to output file
*        (see convline() above) 
*/
putline()
{
   int error;                       /* error flag */
   int endf;                        /* end of file flag */
   char *p1, *p2;                   /* pointers */
                                    
   error = FALSE;                   /* init to no error */
   if (lines) {                     /* if page heading is to be done */
      if (linecnt < 1) {            /* if time to insert a page eject */
         if (linecnt == 0) {        /* if not first time */
            endf = putc('\f',file2); /* eject page */
            if (endf == EOF)
               error = TRUE;        /* set error on EOF */
         }
         if (!error)
            error = doheading();    /* do page heading */
      }
      p1 = buf2;                    /* start at beginning of buffer */
      while (*p1 != '\n')           /* search for page eject sequence */
                                    /* if match is found */
         if (strncmp(p1++,peject,pejectlen) == 0) {
            p1 += pejectlen - 1;    /* point to 1st char after */
            p2 = p1;
            while (*p2)             /* find the ending null */
               ++p2;
            for (; p2 >= p1; --p2)  /* move them over 1 char */
               *(p2+1) = *p2;
            *p1 = '\f';             /* insert a formfeed */
            break;                  /* exit the while loop */
         }
      for (p1=buf2; *p1 != '\n'; p1++) /* find end of line */
         ;
      if (*--p1 == '\f') {          /* if end is \f\n */
         *p1++ = '\n';              /* make it \n\f */
         *p1 = '\f';
      }
   }
   for (p1=buf2; *p1 && !error; p1++) { /* for the whole buffer */
      endf = putc(*p1, file2);      /* output the chars */
      if (endf == EOF)
         error = TRUE;              /* set error on EOF */
      if (!error && *p1 == '\f')    /* if page ejected */
         if (lines)                 /* if heading not supressed */
            error = doheading();    /* do page heading */
   }
   --linecnt;                       /* decr lines left */
   return(error);                   /* return error status */
}
/*eject*/
/* output the heading line
*     Reset the lines left count.  Return is TRUE if an output
*     error occurs.
*/
doheading()
{
   char *cp;                        /* pointer */
   int error;                       /* error flag */
   int endf;                        /* end of file flag */

   error = FALSE;                   /* init to no error */
   if (sheadflag)                   /* if supressing heading */
      linecnt = lines;              /* reset line count */
   else {
                                    /* put page number in heading */
      itoa(++pageno,PAGENLEN,'Z',heading+HEADPNUM);
      cp = heading;                 /* init pointer */
      while (*cp && !error) {
         endf = putc(*cp,file2);    /* write heading to output */
         cp++;
         if (endf == EOF)
            error = TRUE;           /* set error on end of file */
      }
      linecnt = lines -2;           /* reset line count */
   }
   return(error);
}


/* init heading */
void inithead()
{
   char *strncpy();
   void datetime();
   char *cp1, *cp2;                 /* char pointers */
   int i;                           /* counter */

   cp1 = heading;                   /* init pointer for clear */
   for (i=0; i<HEADLLEN; i++)       /* clear heading to blanks */
      *cp1++ = ' ';
   *cp1++ = '\n';                   /* put two newlines */
   *cp1++ = '\n';
   *cp1 = 0;                        /* put NULL on end */
   cp1 = name1;                     /* init pointers for move */
   cp2 = heading;
   for (i=0; i<HEADLLEN && *cp1; i++)
      *cp2++ = *cp1++;              /* move file name to heading */
                                    /* move date and time */
   datetime(heading+HEADDAY,heading+HEADDATE,heading+HEADTIME);
   cp1 = pagelit;
   cp2 = heading+HEADPAGE;
   while(*cp1)                      /* move "PAGE" to heading */
      *cp2++ = *cp1++;
   linewidth = width ? width : BUFLEN-1; /* set maximum index */
   pejectlen = strlen(peject);      /* get search length */
   return;
}
/*eject*/
/* put a string to stdout */
void putstring(cp)
char *cp;                           /* pointer to the string */
{
   for(; *cp; cp++) {               /* while a char */
      putchar(*cp);                 /* put out chars */
      if (*cp == '\n')              /* if end of line */
         fflush(stdout);            /* flush buffer (display it) */
   }
   return;
}


/* convert 3 chars of string to upper case */
char *supper(cp)
char *cp;                           /* pointer to the string */
{
   static char s[4];                /* returned string */
   int i;                           /* counter */

   for (i=0; *cp && i<3; cp++)      /* while a char and not too long */
      s[i++] = toupper(*cp);        /* convert it */
   s[i] = 0;                        /* put trailing NULL */
   return(s);
}
/*eject*/
/* get current date and time        Author:  Robert C. Alston
 *                                  Date:    11-21-85
 *
 *    Day name, date (mm-dd-yy), and time (hh:mm:ss) are moved to
 *    locations pointed to by the three pointers.  No trailing NULLs
 *    are moved.  Any pointer may be NULL to supress the move.
 */
void datetime(dayp,datep,timep)
char *dayp;                         /* place to store 9 char day name */
char *datep;                        /* place to store 8 char date */
char *timep;                        /* place to store 8 char time */
{
   long dt[3];                      /* 3 longs to receive DateStamp */
   int dow, mo, dy, yr, hr, mn, sc;
   static int days[] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };
   static char *dayname[] = {
      "Monday   ","Tuesday  ","Wednesday","Thursday ",
      "Friday   ","Saturday ","Sunday   "
   };
   static char dateplate[] = "mm-dd-yy";
   static char timeplate[] = "hh:mm:ss";
   int d;                           /* misc use */
   char *cp;                        /* misc pointer */
   void itoa();                     /* declare the function */

   DateStamp(dt);                   /* get dt[0] = days since 1/1/78 */
                                    /*     dt[1] = minutes today */
                                    /*     dt[2] = seconds * 50 */
   hr = dt[1] / 60;
   mn = dt[1] - hr * 60;            /* calc hours, minutes, & seconds */
   sc = dt[2] / 50;
   dy = dt[0];                      /* init day to pseudo julian date - 1 */
   dow = (dy - 1) % 7;              /* calc day of week (0 = monday) */
   dy++;                            /* make it a julian date */
   yr = 78;                         /* start at 1/1/78 */
   while (1) {
      d = 365;                      /* get days this year */
      if (yr % 4 == 0)
         d = 366;                   /* it's leap year */
      if (dy <= d)
         break;                     /* found the year exit loop */
      dy -= d;                      /* subtract this years days */
      ++yr;                         /* bump year */
   }
   mo = 1;                          /* init to Jan */
   while (dy > days[mo]) {          /* while more days than curr month */
      if (mo == 2 && yr % 4 == 0) {
         if (dy == 29)              /* if Feb 29 - done */
            break;
         dy--;                      /* decr for Feb 29 */
      }
      dy -= days[mo++];             /* subtract days this mo, bump mo */
   }
   itoa(mo,2,'Z',dateplate);        /* convert date to ascii */
   itoa(dy,2,'9',dateplate+3);
   itoa(yr,2,'9',dateplate+6);
   itoa(hr,2,'Z',timeplate);        /* convert time to ascii */
   itoa(mn,2,'9',timeplate+3);
   itoa(sc,2,'9',timeplate+6);
   cp = dayname[dow];               /* get pointer to day name */
   if (dayp != NULL)                /* if caller wants day name */
      while (*cp)                   /* move day name to caller area */
         *dayp++ = *cp++;
   cp = dateplate;                  /* get pointer to date */
   if (datep != NULL)               /* if caller wants date */
      while (*cp)                   /* move date */
         *datep++ = *cp++;
   cp = timeplate;                  /* get pointer to time */
   if (timep != NULL)               /* if caller wants time */
      while (*cp)                   /* move time */
         *timep++ = *cp++;
   return;
}
/*eject*/
/* integer to ascii decimal         Author:  Robert C. Alston
 *                                  Date:    11-21-85
 *
 *    The low w digits of the input number n are placed in the
 *    field.  Pointer cp points to the start of the field.
 *    Zero supression is optional. Upper digits of the number
 *    which will not fit are ignored.  No trailing NULL is output.
 */
void itoa(n,w,c,cp)
int  n;                             /* number to convert */
int  w;                             /* width of field */
char c;                             /* 'Z' for zero supress */
char *cp;                           /* start of field */
{
   int t;                           /* temp number */
   int i;                           /* index for field */

   for (i = w-1; i >= 0; i--) {
      t = n / 10;                   /* calculate decimal digits */
      cp[i] = n - t * 10 + '0';     /* store digit */
      n = t;
   }
   if (c == 'Z' && w > 1)           /* if supression should be done */
      for (i=1; *cp == '0' && i < w; i++)
         *cp++ = ' ';               /* supress leading zeroes */
   return;
}


