/* Main driver for impost program to pretty print files.
   Prints comments in italics, and reserved words in bold for
   C and clu programs.  */

/* Andrew Heybey (atheybey@ptt.lcs.mit.edu)
   15-JAN-1987

   Modified by Neeraj Gupta (goop@athena.mit.edu)
   10-MAR-1993
   * -2 option allows 2 column output

   Modified by Jay Ongg (ongg@mit.edu)
   27-SEP-1994
   * Scheme file support

   Modified by Erik Nygren (nygren@mit.edu)
   13-FEB-1998
   * Java and PolyJ support
*/


#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <time.h>
#include <ctype.h>

int max_lines;
int page_lines;
int starting;
int type;
char name[256], date[50];

/* Variables from lex.yy.c */
extern int linenum;
extern int pagenum, masterpagenum;
extern FILE *yyin;

/* Defaults go here--
   Also check out how it prints multiple line comments */
#define HFONT "Times-BoldItalic"	/* Header font */
#define HFONTSIZE 12
#define BFONT "Courier"			/* Body font */
#define BFONTSIZE 10
#define CFONT "Courier-Oblique"		/* Comment font */
#define CFONTSIZE 10
#define XFONT "Courier-BoldOblique"	/* Comment reserved word font */
#define XFONTSIZE 10
#define RFONT "Courier-Bold"		/* Reserved word font */
#define RFONTSIZE 10
#define COL_SIZE (int)7		        /* Size to use in 2col format */
#define COL_LMARGIN (int)18

/* Default margins, in points */
#define LEFTMARGIN 72
#define TOPMARGIN 36
#define BOTTOMMARGIN 36

/* Different types of files that I know how to process */
#define C_FILE_TYPE 1		/* C source (.c & .h files) */
#define CLU_FILE_TYPE 2		/* Clu source (.clu files) */
#define PLAIN_FILE_TYPE 3	/* Plain text files */
#define ARGUS_FILE_TYPE 4	/* Argus source files (.arg files) */
#define SCHEME_FILE_TYPE 5      /* Scheme source files (.scm files) */
#define JAVA_FILE_TYPE 6        /* Java source files (.java & .pj files) */

char *days[] = {
  "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
};

char *months[] = {
  "Jan", "Feb", "Mar", "Apr", "May", "Jun",
  "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
};


char *str_rchr();

char *
strcpy_until_num(s1, s2)
     char *s1, *s2;
{
  for (; *s2; s2++)  {
    if (isdigit(*s2))
      break;
    *s1++ = *s2;
  }
  *s1 = '\0';
  return(s2);
}
	

char *
str_rchr(s1, c)
     char *s1;
     char c;
{
  char *cp, *retval;

  retval = NULL;
  for (cp = s1; *cp; cp++)
    if (*cp == c) retval = cp;
  return retval;
}

main(argc, argv)
     int argc;
     char *argv[];
{
  FILE *infp, *fp;
  char *dot;
  struct tm *tbuf;
  time_t timeval;
  struct stat statbuf;
  char hfont[50], cfont[50], bfont[50], rfont[50], xfont[50];
  char *s;
  int hfontsize, cfontsize, xfontsize, bfontsize, rfontsize;
  int lmargin, mmargin, tmargin, bmargin, linespace;
  int do_header, from_stdin, cols, duplex;
  int i;
  int forced_type = 0, size, new_name = 0;

  /* Always use stdout for the moment */
  fp = stdout;

  /* Initialize defaults */
  strcpy(hfont, HFONT);  hfontsize = HFONTSIZE;
  strcpy(bfont, BFONT);  bfontsize = BFONTSIZE;
  strcpy(cfont, CFONT);  cfontsize = CFONTSIZE;
  strcpy(xfont, XFONT);  xfontsize = XFONTSIZE;
  strcpy(rfont, RFONT);  rfontsize = RFONTSIZE;
  lmargin = LEFTMARGIN;
  tmargin = TOPMARGIN;
  bmargin = BOTTOMMARGIN;

  *name = '\0';
  do_header = 1;
  cols = 0;
  from_stdin = 0;
  mmargin = 400;
  duplex = 0;

  /* Process options */
  for (i = 1; i < argc && *argv[i] == '-'; i++)  {
    if (strncmp(argv[i], "-fb", 3) == 0)  {
      s = strcpy_until_num(bfont, argv[i] + 3);
      if (*s)
	bfontsize = atoi(s);
      continue;
    }
    if (strncmp(argv[i], "-fh", 3) == 0)  {
      s = strcpy_until_num(hfont, argv[i] + 3);
      if (*s)
	hfontsize = atoi(s);
      continue;
    }
    if (strncmp(argv[i], "-fr", 3) == 0)  {
      s = strcpy_until_num(rfont, argv[i] + 3);
      if (*s)
	rfontsize = atoi(s);
      continue;
    }
    if (strncmp(argv[i], "-fc", 3) == 0)  {
      s = strcpy_until_num(cfont, argv[i] + 3);
      if (*s)
	cfontsize = atoi(s);
      continue;
    }
    if (strncmp(argv[i], "-fx", 3) == 0)  {
      s = strcpy_until_num(xfont, argv[i] + 3);
      if (*s)
	xfontsize = atoi(s);
      continue;
    }
    if (strncmp(argv[i], "-fa", 3) == 0)  {
      s = strcpy_until_num(bfont, argv[i] + 3);
      bfontsize = atoi(s);
      strcpy(cfont, bfont);
      cfontsize = bfontsize;
      strcpy(xfont, bfont);
      xfontsize = bfontsize;
      strcpy(rfont, bfont);
      rfontsize = bfontsize;
      continue;
    }
    if (strncmp(argv[i], "-sz", 3) == 0)  {
      size = atoi(argv[i] + 3);
      if (size > 0)
	bfontsize = xfontsize = rfontsize = cfontsize = size;
      continue;
    }
    if (strncmp(argv[i], "-lm", 3) == 0)  {
      lmargin = atoi(argv[i] + 3);
      continue;
    }
    if (strncmp(argv[i], "-tm", 3) == 0)  {
      tmargin = atoi(argv[i] + 3);
      continue;
    }
    if (strncmp(argv[i], "-bm", 3) == 0)  {
      bmargin = atoi(argv[i] + 3);
      continue;
    }
    if (strcmp(argv[i], "-clu") == 0)  {
      forced_type = CLU_FILE_TYPE;
      continue;
    }
    if (strcmp(argv[i], "-c") == 0)  {
      forced_type = C_FILE_TYPE;
      continue;
    }
    if (strcmp(argv[i], "-arg") == 0)  {
      forced_type = ARGUS_FILE_TYPE;
      continue;
    }
    if (strcmp(argv[i], "-scheme") == 0)  {
      forced_type = SCHEME_FILE_TYPE;
      continue;
    }
    if (strcmp(argv[i], "-java") == 0)  {
      forced_type = JAVA_FILE_TYPE;
      continue;
    }
    if (strcmp(argv[i], "-polyj") == 0)  {
      forced_type = JAVA_FILE_TYPE;
      continue;
    }
    if (strcmp(argv[i], "-text") == 0)  {
      forced_type = PLAIN_FILE_TYPE;
      continue;
    }
    if (strncmp(argv[i], "-h", 2) == 0)  {
      if (*(argv[i] + 2))  {
	new_name = 1;
	strcpy(name, argv[i]+2);
      }
      else
	do_header = 0;
      continue;
    }
    if (strcmp(argv[i], "-2") == 0) {
      cols = 1;
      continue;
    }
    if (strcmp(argv[i], "-d") == 0) {
      duplex = 1;
      continue;
    }
    if (strlen(argv[i]) == 1)	/* Allow "-" option so can have file */
      break;			/* names that start with '-' */

    fprintf(stderr, "%s: unknown option: %s; aborting\n", argv[0], argv[i]);
    exit(1);
  }      

  /* Calculate stuff */
  if (cols) {
    bfontsize = rfontsize = cfontsize = xfontsize = COL_SIZE;
    lmargin = COL_LMARGIN;
    linespace = bfontsize + 1;
    if (do_header)
      max_lines = (8.5 * 72 - (tmargin + bmargin) - 11) / linespace;
    else
      max_lines = (8.5 * 72 - (tmargin + bmargin)) / linespace;
    page_lines = 2*max_lines;
  }
  else {
    linespace = bfontsize + 1;
    if (do_header)
      max_lines = (11 * 72 - (tmargin + bmargin) - 11) / linespace;
    else
      max_lines = (11 * 72 - (tmargin + bmargin)) / linespace;
    page_lines = max_lines;
  }

  /* Write out prologue to PS file */
  fprintf(fp, "%%!PS-Adobe-\n");
  fprintf(fp, "%%%%DocumentFonts: %s %s %s %s %s\n", hfont, bfont, cfont, rfont, xfont);
  fprintf(fp, "%%%%Creator: psify\n");
  fprintf(fp, "%%%%Pages: (atend)\n");
  fprintf(fp, "%%%%EndComments\n");
  fprintf(fp, "\n");
  
  if (duplex) {
    fprintf(fp, "%%%% duplex mode\n");
    fprintf(fp, "<< /Duplex true >> setpagedevice\n\n");
  }
  if (cols) {
    fprintf(fp, "%%%% landscape mode\n");
    fprintf(fp, "792 0 translate\n90 rotate\n\n");
  }

  fprintf(fp, "/HF /%s findfont %d scalefont def\n", hfont, hfontsize);
  fprintf(fp, "/BF /%s findfont %d scalefont def\n", bfont, bfontsize);
  fprintf(fp, "/CF /%s findfont %d scalefont def\n", cfont, cfontsize);
  fprintf(fp, "/XF /%s findfont %d scalefont def\n", xfont, xfontsize);
  fprintf(fp, "/RF /%s findfont %d scalefont def\n", rfont, rfontsize);
  fprintf(fp, "/headfont {HF setfont} def\n");
  fprintf(fp, "/normfont {BF setfont} def\n");
  fprintf(fp, "/commentfont {CF setfont} def\n");
  fprintf(fp, "/reservefont {RF setfont} def\n");
  fprintf(fp, "/commentreservefont {XF setfont} def\n");
  
  fprintf(fp, "/inch {72 mul} def\n");
  fprintf(fp, "/toppos %d def\n", 11*72 - tmargin);
  fprintf(fp, "/leftmar %d def\n", lmargin);
  fprintf(fp, "/linespace %d def\n", linespace);
  fprintf(fp, "normfont\n");
  fprintf(fp, "/tabsize (        ) stringwidth pop def\n");
  fprintf(fp, "/newline {currentpoint linespace sub exch pop");
  fprintf(fp, " usemar exch moveto} def\n");
  fprintf(fp, "/dotab {currentpoint exch usemar sub round cvi\n");
  fprintf(fp, "  tabsize round cvi idiv\n");
  fprintf(fp, "  2 index add tabsize mul usemar add exch moveto pop} def\n");

  fprintf(fp, "/topcol1 {\n");
  fprintf(fp, "  /usemar {leftmar} def\n");
  fprintf(fp, "  usemar toppos 11 sub linespace sub moveto\n");
  fprintf(fp, "} def\n");

  if (cols) {
    fprintf(fp, "/midmar %d def\n", mmargin);
    fprintf(fp, "/topcol2 {\n");
    fprintf(fp, "  /usemar {midmar} def\n");
    fprintf(fp, "  usemar toppos 11 sub linespace sub moveto\n");
    fprintf(fp, "} def\n");
    
    if (do_header)  {
      fprintf(fp, "/makeheader {\n");
      fprintf(fp, "  gsave headfont 0.25 inch toppos moveto show\n");
      fprintf(fp, "  dup stringwidth pop 11 inch exch sub 2 div\n");
      fprintf(fp, "  toppos moveto show\n");
      fprintf(fp, "  3 string cvs\n");
      fprintf(fp, "  dup stringwidth pop 10.75 inch exch sub dup toppos\n");
      fprintf(fp, "  moveto exch show\n");
      fprintf(fp, "  (Page ) dup stringwidth pop\n");
      fprintf(fp, "  2 index exch sub\n");
      fprintf(fp, "  toppos moveto show pop\n");
      fprintf(fp, "  newpath 0.25 inch toppos 7 sub moveto\n");
      fprintf(fp, "  10.5 inch 0 rlineto stroke\n");
      fprintf(fp, "  newpath 5.5 inch toppos 7 sub moveto\n");
      fprintf(fp, "  5.5 inch .25 lineto stroke\n");
      fprintf(fp, "  grestore topcol1\n");
      fprintf(fp, "  } def\n");
    }
    else {
      fprintf(fp, "/makeheader {\n");
      fprintf(fp, "  pop pop pop topcol1\n");
      fprintf(fp, "  newpath 5.5 inch toppos 7 sub moveto\n");
      fprintf(fp, "  5.5 inch .25 lineto stroke\n");
      fprintf(fp, "  } def\n");
    }
  }
  else {
    fprintf(fp, "/toppos %d def\n", 11*72 - tmargin);

    if (do_header)  {
      fprintf(fp, "/makeheader {\n");
      fprintf(fp, "  gsave headfont 0.5 inch toppos moveto show\n");
      fprintf(fp, "  dup stringwidth pop 8.5 inch exch sub 2 div\n");
      fprintf(fp, "  toppos moveto show\n");
      fprintf(fp, "  3 string cvs\n");
      fprintf(fp, "  dup stringwidth pop 8 inch exch sub dup toppos\n");
      fprintf(fp, "  moveto exch show\n");
      fprintf(fp, "  (Page ) dup stringwidth pop\n");
      fprintf(fp, "  2 index exch sub\n");
      fprintf(fp, "  toppos moveto show pop\n");
      fprintf(fp, "  newpath 0.5 inch toppos 7 sub moveto\n");
      fprintf(fp, "  7.5 inch 0 rlineto stroke\n");
      fprintf(fp, "  grestore topcol1\n");
      fprintf(fp, "  } def\n");
    }
    else {
      fprintf(fp, "/makeheader {\n");
      fprintf(fp, "  pop pop pop topcol1\n");
      fprintf(fp, "  } def\n");
    }
  }

  fprintf(fp, "%%%%EndProlog\n\n");

  if (i == argc)
    from_stdin = 1;

  masterpagenum = 0;

  /* Print all of the files. */
  for (; i < argc || from_stdin; i++)  {
    if (from_stdin)  {
      /* Input coming from stdin */
      infp = stdin;
      timeval = time(0);
      if (!new_name)
	strcpy(name, "stdin");
    }
    else  {
      if (!(infp = fopen(argv[i], "r")))  {
	fprintf(stderr, "%s: File open failed: %s\n", argv[0], argv[i]);
	continue;
      }
      if (!new_name)  {
	s = str_rchr(argv[i], '/');
	if (s)
	  strcpy(name, s+1);
	else
	  strcpy(name, argv[i]);
      }
      stat(argv[i], &statbuf);
      timeval = statbuf.st_mtime;
    }

    tbuf = localtime(&timeval);
    sprintf(date, "%s, %s %d, %d %02d:%02d:%02d", days[tbuf->tm_wday],
	    months[tbuf->tm_mon], tbuf->tm_mday, tbuf->tm_year + 1900,
	    tbuf->tm_hour, tbuf->tm_min, tbuf->tm_sec);
    yyin = infp;

    /** Decide which lex routine to use */
    if (forced_type)
      type = forced_type;
    else  {
      if (from_stdin)
	type = PLAIN_FILE_TYPE;
      else  {
	/* Look at the file name to get the type */
	dot = str_rchr(argv[i], '.');
	type = PLAIN_FILE_TYPE;
	if (dot)  {
	  if (strcmp(dot, ".clu") == 0)
	    type = CLU_FILE_TYPE;
	  else if (strcmp(dot, ".c") == 0 || strcmp(dot, ".h") == 0)
	    type = C_FILE_TYPE;
	  else if (strcmp(dot, ".arg") == 0)
	    type = ARGUS_FILE_TYPE;
	  else if (strcmp(dot, ".scm") == 0)
	    type = SCHEME_FILE_TYPE;
	  else if (strcmp(dot, ".java") == 0 || strcmp(dot, ".pj") == 0)
	    type = JAVA_FILE_TYPE;
	}
      }
    }

    /* lex that baby.
       The lex file uses the type variable in order to do the right
       thing for each type of file. */
    starting = 1;
    yylex();

    if (infp != stdin)
      fclose(infp);

    if (from_stdin)
      break;
  }

  fprintf(fp, "\n%%%%Trailer\n");
  fprintf(fp, "%%%%Pages: %d\n", masterpagenum);

  if (fp != stdout)
    fclose(fp);
}