/* simp-pb.c */
/* Andrew Davison, July 1996 (ad@cs.mu.oz.au)  */
/* Return a GIF containing 
   the user's three biorhythm curves for a specified month/year.
   The user supplies their birthday and month/year of interest
   via a form.
   The number of days since the user's birthday is
   calculated by converting the birthday and month/year of interest
   into their number of Julian days. The difference is the
   start day for the biorhythm equation.
   The gnuplot commands are in PLOT_CMDS, and this is modified
   and copied into TEMP_CMDS before being displayed. The two
   changes insert the start day and the month/year details.
   gnuplot saves its drawing into <PIC>.ppm, and this is
   converted into GIF format with ppmtogif.
   URL_PIC is the URL of PIC. 
   Tools used: gnuplot, ppmtogif
   This is a simplified version of pb.c which generates a GIF
   rather than a Web page containing a link to a GIF. 
   Only the functions that are different from those in pb.c 
   are listed here. The full version of simp-pb.c is available in:
      http://www.cs.mu.oz.au/~ad/code/biorhythm/simp-pb.c
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>     /* for read(), lseek(), write(), close(), lockf() */
#include <fcntl.h>      /* for open() */

#define BKGROUND "http://www.cs.mu.oz.au/~ad/chalk.jpg"
#define MAX_ENTRIES 2           /* number of input fields */
#define MAXLEN 500              /* max length of a string */
#define NOVAL "$$no-value$$"    /* when no value is found */
#define PLOT_CMDS "/home/staff/ad/www_public/code/biorhythm/bio.plt"
#define TEMP_CMDS "/home/staff/ad/www_public/code/biorhythm/tmp.plt"
#define PIC       "/home/staff/ad/www_public/code/biorhythm/pbio"
#define LOCKFNM   "/home/staff/ad/www_public/code/biorhythm/lock##"

typedef struct {  /* structure for HTML name/value pair */
  char *name;
  char *val;
} entry; 
typedef struct date {
  int day;
  int month;    /* 1 to 12 */
  int year;     /* e.g. 1996 */
} Date;

Date start_date(entry entries[], int etnum, long int *start);
char *get_val(entry entries[], int etnum, char *field);
Date check_date(Date d);
long int toJulian(Date d);
void make_bpic(long int start, Date sd);
void modify_plot(char *plotnm, char *tempnm, long int start, Date d);
/* Locking Utilities */
int my_lock(void);
void my_unlock(int fd);
/* Standard Form Functions */
void start_reply(char *title);
void cgi_errs();
int build_entries(entry entries[]);
char *find_value(entry entries[], int etnum, char *name);
/* Debugging Utilities */
int input_entries(entry entries[]);
void show_entries(entry entries[], int etnum);
/* CGI utilities written by Rob McCool */
char *makeword(char *line, char stop);
char *fmakeword(FILE *f, char stop, int *len);
void unescape_url(char *url);
char x2c(char *what);
void plustospace(char *str);

int main()
{
  entry entries[MAX_ENTRIES];
  int etnum = 0;
  Date sd;
  long int start;
  cgi_errs();
  etnum = build_entries(entries);
/*
  etnum = input_entries(entries);
  show_entries(entries, etnum);
*/
  sd = start_date(entries, etnum, &start);
  make_bpic(start, sd);
  return 1;
}

Date start_date(entry entries[], int etnum, long int *start)
/* Get the user's birthday (bd) and month/year of interest (sd)
   from the form details. Work out the number of days between
   bd and sd, and store it in start. sd is also returned.
*/
{
  char *val;
  Date bd, sd;
  long int bdjul, sdjul;
  val = get_val(entries, etnum, "birth");
  sscanf(val, "%d/%d/%d", &bd.day, &bd.month, &bd.year);
  bd = check_date(bd);
  bdjul = toJulian(bd);
  val =  get_val(entries, etnum, "bio");
  sscanf(val, "%d/%d", &sd.month, &sd.year);
  sd.day = 1;
  sd = check_date(sd);
  sdjul = toJulian(sd);
  if (sdjul >= bdjul)
    *start = sdjul - bdjul;
  else {
    *start = 0;
    return bd;
  }
  return sd;
}

char *get_val(entry entries[], int etnum, char *field)
/* Terminate if no value has been entered */
{
  char *val;
  val = find_value(entries, etnum, field);
  if (strcmp(val, NOVAL) == 0) {
    start_reply("Biorhythm Error");
    printf("<P>Error: No value found in \"%s\" field</P>\n", field);
    printf("</body></html>\n");
    exit(1);
  }
  return val;
}

Date check_date(Date d)
/* Check the date for obvious mistakes */
{
  if (d.year < 0)
    d.year = 0;
  else if (d.year > 9000)
    d.year = 9000;
  if (d.month < 1)
    d.month = 1;
  else if (d.month > 12)
    d.month = 12;
  if (d.day < 1) 
    d.day = 1;
  if ((d.month == 1) || (d.month == 3) || (d.month == 5) ||
      (d.month == 7) || (d.month == 8) || (d.month == 10) ||
      (d.month == 12)) {
    if (d.day > 31)
      d.day = 31;
  }
  else if ((d.month == 4) || (d.month == 6) || (d.month == 9) ||
           (d.month == 11)) {
     if (d.day > 30) 
       d.day = 30;
  }
  else if (d.month == 2) {
     /* february: don't bother with leap year checking */
     if (d.day > 29)
       d.day = 29;
  }
  return d;
}

long int toJulian(Date d)
{ /* see pb.c for definition */ }

void make_bpic(long int start, Date sd)
/* Housekeeping: remove an old biorhythm GIF.
   Creating a new biorhythm GIF involves making a new 
   version of the gnuplot commands in PLOT_CMDS by changing 
   start and the month/year of interest (sd), to create TEMP_CMDS. 
   TEMP_CMDS is evaluated by gnuplot to create PIC.
   PIC is in ".ppm" format, and so is converted into 
   a GIF sent to stdout. This means that it will be returned
   to the user. Prior to this a suitable "Content-type"
   header must be output.
   Since TEMP_CMDS and PIC are not unique, use locking
   to stop any other invocations of pb from working on them
   at the same time: an 'exclusion' zone.
*/
{
  char cmd[MAXLEN];
  int lock_fd;
  lock_fd = my_lock();    /* start of 'exclusion' zone */
  modify_plot(PLOT_CMDS, TEMP_CMDS, start, sd);
  /* Plot the biorhythm */
  sprintf(cmd, "/usr/local/bin/gnuplot %s", TEMP_CMDS);
  system(cmd);
  printf("Content-type: image/gif\n\n");
  fflush(NULL);   /* force output to occur now */
  /* Change the biorhythm into GIF form */
  sprintf(cmd, "/home/staff/ad/pbm/pbm-mundook/ppmtogif %s.ppm 2> /dev/null", 
                                    PIC);
  system(cmd);
  my_unlock(lock_fd);     /* end of 'exclusion' zone */
}

void modify_plot(char *plotnm, char *tempnm, long int start, Date d)
/* Open the template gnuplot commands file (plotnm) and modify it,
   saving the new version in tempnm. The changes are to replace '?'
   by start and '@' by d.
*/
{
  FILE *fp, *tfp;
  int c;
  if ((fp = fopen(plotnm, "r")) == NULL) {
    start_reply("Biorhythm Error");
    printf("<P><b>Error</b>: Cannot open %s for reading</P>\n", plotnm);
    printf("</body></html>\n");
    exit(1);
  }
  else if ((tfp = fopen(tempnm, "w")) == NULL) {
    start_reply("Biorhythm Error");
    printf("<P><b>Error</b>: Cannot open %s for writing</P>\n", tempnm);
    printf("</body></html>\n");
    exit(1);
  }
  else {
    while ((c = fgetc(fp)) != EOF) 
      if (c == '?')         /* marker for start digit */
        fprintf(tfp, "%ld", start);
      else if (c == '@')    /* marker for start date */
        fprintf(tfp, "%d/%d", d.month, d.year);
      else 
        fputc(c, tfp);
    fclose(fp);
    fclose(tfp);
  }
}

/* Locking Utilities */
int my_lock(void)
{ /* see pb.c for definition */ }

void my_unlock(int fd)
{ /* see pb.c for definition */ }

/* Standard Form Functions */
void start_reply(char *title)
{ /* see pb.c for definition */ }

void cgi_errs()
/* Check for errors in the REQUEST_METHOD and CONTENT_TYPE
   environment variables.
*/
{
  if(strcmp(getenv("REQUEST_METHOD"),"POST")) {
    start_reply("Biorhythm Error");
    printf("<P>This script should be referenced with a METHOD of POST.\n");
    printf("If you don't understand this, read ");
    printf("<A HREF=\"http://www.ncsa.uiuc.edu/SDG/Software/Mosaic/Docs
/fill-out-forms/overview.html\">forms overview</A>.</P>\n");
    printf("</body></html>");
    exit(1);
  }
  if(strcmp(getenv("CONTENT_TYPE"), "application/x-www-form-urlencoded")) {
    start_reply("Biorhythm Error");
    printf("<P>This script can only be used to decode form results.</P>\n");
    printf("</body></html>");
    exit(1);
  }
}

/* All the other Standard Form Functions are unchanged from
   pb.c. See the function prototypes at the top of this listing
   for their names.
*/