/* ppssch.c -- Process PostScript SCHedule
   Processes a Cable Television Schedule
   Brian Krause
   June 28, 1989 */

#include <stdio.h>
#define PDLEN 256 /* maximum length of program description */
#define NUMPDS 600 /* maximum number of program descriptions */

struct pd {
  int month;
  int day;
  int channel;
  int dow; /* day of week */
  int sh; /* start hour of program */
  int sm; /* start minute of program */
  char text[PDLEN];} pdl[NUMPDS];

int pm[NUMPDS]; /* partition markers */
int pdsz;    /* size of list */
int cdow;    /* current values of variables */
int cmonth;
int cday;
int cchannel;
int csh;
int csm;
int lineno;  /* current line number */

/* PostScript header */

void print_head()
{
  printf("%%!\n%% PostScript header for TV Schedule\n%% Brian Krause \n%% May 24, 1990\n");
  printf("/lm 40 def\n/bt 40 def\n/tp 700 def\n/rm 580 def\n/ic 180 def\n/wd 150 def\n");
  printf("/lw 11 def\n\n/dw 20 def\n\n/wordbreak ( ) def\n/BIL\n{ /proc exch def\n");
  printf("/linelength exch def\n/textstring exch def\n");
  printf("/breaklen wordbreak stringwidth pop\ndef\n");
  printf("/curlen 0 def\n/lastwordbreak 0 def\n/startchar 0 def\n");
  printf("/restoftext textstring def\n{ restoftext wordbreak search\n");
  printf("{/nextword exch def pop\n/restoftext exch def\n");
  printf("/wordlen nextword stringwidth\npop def\ncurlen wordlen add linelength lt\n");
  printf("{ /curlen curlen wordlen add\nbreaklen add def }\n{ textstring startchar\n");
  printf("lastwordbreak startchar sub\ngetinterval proc\n");
  printf("/startchar lastwordbreak def\n/curlen wordlen breaklen\nadd def\n");
  printf("} ifelse\n/lastwordbreak lastwordbreak\nnextword length add 1 add def\n");
  printf("}\n{ pop exit }\nifelse\n} loop\n/lastchar textstring length def\n");
  printf("textstring startchar lastchar\nstartchar sub getinterval proc\n} def\n\n");
  printf("/l {wd {s} BIL} def\n/initsch {/x lm def\n/y tp def} def\ninitsch\n");
  printf("x y moveto\n/s {show /y y lw sub def\ny bt lt\n{/x x ic add def\n");
  printf("x rm ge\n{showpage\ninitsch} {/y tp def} ifelse } if x y moveto} def\n\n");
  printf("/Helvetica-Bold findfont 9 scalefont /HB exch def\n");
  printf("/Souvenir-Demi findfont 10 scalefont /HT exch def\n");
  printf("/Souvenir-Demi findfont 12 scalefont /SD exch def\n");
  printf("/Souvenir-Light findfont 10 scalefont /SL exch def\n");
  printf("SL setfont\n\n/n {0 setgray newpath x y moveto\n");
  printf("-2 -1 rmoveto -16 0 rlineto 0 10 rlineto 16 0\n");
  printf("rlineto 0 -10 rlineto closepath fill\n");
  printf("1 setgray HB setfont\nx y moveto -10 1 rmoveto dup stringwidth\n");
  printf("pop 2 div neg 0 rmoveto show\nx y moveto 0 setgray SL setfont} def\n");
  printf("/t {HT setfont s SL setfont} def\n");
  printf("/d {SD setfont\n/y y dw sub def y bt lt {/x x ic add def\n");
  printf("x rm ge {showpage initsch} {/y tp def} ifelse } if\n");
  printf("y tp eq {/y y dw add def}if x y dw add 6 sub moveto show\n");
  printf("x y moveto SL setfont} def\n\n");
}

/* procedures to pass to sort and partition routines */
int pdmonth(x)
struct pd x;
{
  return(x.month);
}

int pdday(x)
struct pd x;
{
  return(x.day);
}

int pdchannel(x)
struct pd x;
{
  return(x.channel);
}

int pdsh(x)
struct pd x;
{
  return(x.sh);
}

int pdsm(x)
struct pd x;
{
  return(x.sm);
}

unparse_dow(d)
{
  switch (d)
    {
    case 1: {printf("Sunday"); break;}
    case 2: {printf("Monday"); break;}
    case 3: {printf("Tuesday"); break;}
    case 4: {printf("Wednesday"); break;}
    case 5: {printf("Thursday"); break;}
    case 6: {printf("Friday"); break;}
    case 7: {printf("Saturday"); break;}
    }
}

unparse_time(h, m)
{
  if (h == 0 && m == 0)
    {
      printf("Midnight");
    }
  else if (h == 12 && m == 0)
    {
      printf("Noon");
    }
  else if (h < 12)
    {
      printf("%d:%02d am", h, m);
    }
  else 
    {
      printf("%d:%02d pm", ((h == 12) ? 12 : (h - 12)), m);
    }
}

unparse_month(m)
{
  switch (m)
    {
    case 1: {printf("January"); break;}
    case 2: {printf("February"); break;}
    case 3: {printf("March"); break;}
    case 4: {printf("April"); break;}
    case 5: {printf("May"); break;}
    case 6: {printf("June"); break;}
    case 7: {printf("July"); break;}
    case 8: {printf("August"); break;}
    case 9: {printf("September"); break;}
    case 10: {printf("October"); break;}
    case 11: {printf("November"); break;}
    case 12: {printf("December"); break;}
    }
}

partnsort(l, e, p, b) /* partition array l of size e already sorted by p and sort 
                         partitions by b */
struct pd l[];
int (*p)();
int (*b)();
{
  int v, s, c;
  
  s = 0;
  v = p(l[s]);
  c = s;
  while (c++ < e)
    {
      if ((p(l[s]) != p(l[c])) || pm[c])
	{
	  sort(l, s, c, b);
	  s = c;
	  pm[s] = 1;
	}
    }
  sort(l, s, e, b);
}

sort(l, s, e, pr) /* yeah, so, it's a bubble sort...I'll change it someday... */
struct pd l[];
int (*pr)(); /* pr should be a selector */
{
  int r;
  int chgd;

  chgd = 1;
  while (chgd)
    {
      --e;
      chgd = 0;
      for (r = s; (r < e); ++r)
	{
	  if (pr(l[r]) > pr(l[r + 1]))
	    {
	      struct pd tm;

	      tm = l[r + 1];
	      l[r + 1] = l[r];
	      l[r] = tm;
	      chgd = 1;
	    }
	}
    }
}
  

skip_whitespace()
{
  int c;

  while ((c = gch()) == ' ' || c == '\t' || c == '\n') {}
  ungetc(c, stdin);
}

skip_to_whitespace() /* skip over non-whitespace, then skip over whitespace */
{
  int c;

  while (!((c = gch()) == ' ' || c == '\t' || c== '\n')) {}
  skip_whitespace();
}
  

parse_int() 
{
  int n;
  int c;

  n = gch() - '0';
  if (n < 0 || n > 9) 
    {
      fprintf(stderr, "ppssch: found garbage where number was expected in line %d\n", lineno);
      return(-1);
    }
  else
    {
      while ((c = gch()) >= '0' && c <= '9')
	{
	  n *= 10;
          n += c - '0';
	}
    }
  ungetc(c, stdin);
  return(n);
}

int gch()
{
  int c;

  if ((c = getchar()) == '\n')
    {
      ++lineno;
    }
/*  fputc(c, stderr); for debugging */
  return(c);
}  

main()
{
  int c;
  
  {
    int r;

    for (r = 0; r < NUMPDS; ++r)
      {
	pm[r] = 0;
      }
  }
  cdow = (cmonth = (cday = (cchannel = (pdsz = 0)))); /* initialize variables */
  lineno = 1;

  print_head(); /* output PostScript header */
  skip_whitespace(); /* a good way to start */
  while (c != EOF)
    {
      if ((c = gch()) == 'C')
	{
	  skip_to_whitespace();
	  cchannel = parse_int();
	}
      else if (c == 'F' || c == 'M' || c == 'S' || c == 'T' || c == 'W')
	{
	  int c2; /* for more lookahead */
	  
	  if (c == 'F') {cdow = 6; skip_to_whitespace();}
	  else if (c == 'M') {cdow = 2; skip_to_whitespace();}
	  else if (c == 'W') {cdow = 4; skip_to_whitespace();}
	  else if ((c2 = gch()) == 'a' && c == 'S')
	    {cdow = 7; skip_to_whitespace();}
	  else if (c == 'S' && c2 == 'u')
	    {cdow = 1; skip_to_whitespace();}
	  else if (c == 'T' && c2 == 'h')
	    {cdow = 5; skip_to_whitespace();}
	  else if (c == 'T' && c2 == 'u')
	    {cdow = 3; skip_to_whitespace();}
	  else
	    {
	      fprintf(stderr, "ppssch: found garbage where weekday name expected in line %d\n", lineno);
	      return(-1);
	    }
	  if ((c = gch()) == 'D') {cmonth = 12; skip_to_whitespace();}
	  else if (c == 'F') {cmonth = 2; skip_to_whitespace();}
	  else if (c == 'N') {cmonth = 11; skip_to_whitespace();}
	  else if (c == 'O') {cmonth = 10; skip_to_whitespace();}
	  else if (c == 'S') {cmonth = 9; skip_to_whitespace();}
	  else if ((c2 = gch()) == 'p' && c == 'A')
	    {cmonth = 4; skip_to_whitespace();}
	  else if (c == 'A' && c2 == 'u')
	    {cmonth = 8; skip_to_whitespace();}
	  else if (c == 'J' && c2 == 'a')
	    {cmonth = 1; skip_to_whitespace();}
	  else if ((c2 = gch()) == 'y' && c == 'M')
	    {cmonth = 5; skip_to_whitespace();}
	  else if (c == 'M' && c2 == 'r')
	    {cmonth = 3; skip_to_whitespace();}
	  else if (c == 'J' && c2 == 'n')
	    {cmonth = 6; skip_to_whitespace();}
	  else if (c == 'J' && c2 == 'l')
	    {cmonth = 7; skip_to_whitespace();}
	  else
	    {
	      fprintf(stderr, "ppssch: found garbage where month name expected in line %d\n", lineno);
	      return(-1);
	    }
	  cday = parse_int();
	  skip_whitespace();
	}
      else if (c >= '1' && c <= '9')
	{
	  int n;
	  int c2;
	  
	  pdl[pdsz].text[0] = c;
	  c2 = c;
	  for (n = 1; ((c = gch()) != '\n' || c2 != '\n') && c2 != EOF; ++n)
	    {
              if (c == '\n')
		pdl[pdsz].text[n] = ' ';
	      else
		pdl[pdsz].text[n] = c;
	      c2 = c;
	    }
	  pdl[pdsz].text[n] = 0;
	  if (cdow == 0 || cmonth == 0 || cday == 0 || cchannel == 0)
	    {
	      fprintf(stderr, "ppssch: incompletely specified channel/date before first description in line %d\n", lineno);
	      return(-1);
	    }
	  pdl[pdsz].sm = 0;
	  n = pdl[pdsz].text[0] - '0';
	  if (pdl[pdsz].text[1] == ':')
	    {
	      pdl[pdsz].sm = (pdl[pdsz].text[2] - '0') * 10 + pdl[pdsz].text[3] - '0';
	    }
	  else if (pdl[pdsz].text[1] >= '0' && pdl[pdsz].text[1] <= '9')
	    {
	      n = n * 10 + pdl[pdsz].text[1] - '0';
	      if (pdl[pdsz].text[2] == ':')
		{
		  pdl[pdsz].sm = 
		    (pdl[pdsz].text[3] - '0') * 10 + pdl[pdsz].text[4] - '0';
		}
	    }
	  {
	    int r;
	    
	    for (r = 1; pdl[pdsz].text[r] != 0; ++r)
	      {
		if (pdl[pdsz].text[r] == 'a')
		  {
		    if (n == 12)
		      {
			n = 0;
		      }
		    break;
		  }
		else if (pdl[pdsz].text[r] == 'p')
		  {
		    if (n != 12)
		      {
			n += 12;
		      }
		    break;
		  }
		else if (pdl[pdsz].text[r] == 'n')
		  {
		    n = 12;
		    break;
		  }
		else if (pdl[pdsz].text[r] == 'm')
		  {
		    n = 0;
		    break;
		  }
	      }
	  }   
	  pdl[pdsz].sh = n;
	  pdl[pdsz].dow = cdow;
	  pdl[pdsz].month = cmonth;
	  pdl[pdsz].day = cday;
	  pdl[pdsz].channel = cchannel;
	  ++pdsz;
	}
      ungetc((c = getchar()), stdin);
    }
  sort(pdl, 0, pdsz, pdmonth);
  partnsort(pdl, pdsz, pdmonth, pdday);
  partnsort(pdl, pdsz, pdday, pdsh);
  partnsort(pdl, pdsz, pdsh, pdsm);
  partnsort(pdl, pdsz, pdsm, pdchannel);
  printf("(");
  unparse_dow((cdow = pdl[0].dow));
  printf(", ");
  unparse_month((cmonth = pdl[0].month));
  printf(" %d)d (", (cday = pdl[0].day));
  unparse_time((csh = pdl[0].sh), (csm = pdl[0].sm));
  printf(")t\n");
  {
    int r;
    for (r = 0; r < pdsz; ++r)
      {
	if (pdl[r].day != cday || pdl[r].month != cmonth)
	  {
            printf("(");
	    unparse_dow((cdow = pdl[r].dow));
	    printf(", ");
	    unparse_month((cmonth = pdl[r].month));
	    printf(" %d)d (", (cday = pdl[r].day));
	    unparse_time((csh = pdl[r].sh), (csm = pdl[r].sm));
	    printf(")t\n");
	  }
	if (pdl[r].sh != csh || pdl[r].sm != csm)
	  {
            printf("(");
	    unparse_time((csh = pdl[r].sh), (csm = pdl[r].sm));
	    printf(")t\n");
	  }
	printf("(%d)n (", pdl[r].channel);
	{        
	  int n, qu;

          qu = 0;
	  n = 0;
	  while (!((pdl[r].text[++n] == '-' ) && (pdl[r].text[n+1] == '-'))) {}
	  ++n; 
	  while (pdl[r].text[++n] != 0)
            if (pdl[r].text[n] != '(' && pdl[r].text[n] != ')')
              if (pdl[r].text[n] != '"')
		printf("%c",pdl[r].text[n]);
	      else
		{
		  qu=1-qu;
		  if (qu)
		    printf("``");
		  else
		    printf("''");
		}
	    else
	      printf("\%c",pdl[r].text[n]);
	}

        printf(")l\n");
/* used for debugging
 *	printf("%2d/%2d (%d)  %2d:%02d Ch. %-2d  %s", pdl[r].month,
 *	                                              pdl[r].day,
 *	                                              pdl[r].dow,
 *	                                              pdl[r].sh,
 *                                                    pdl[r].sm,
 *                                                    pdl[r].channel,
 *                                                    pdl[r].text); */
      }
  } 
  printf("showpage\n");
}
