shyster.h

/* This is the header file for the Shyster module.  It is included by all
   twelve modules. */

/* version and copyright information */

#define Shyster_Version "SHYSTER version 1.0"
#define Copyright_Message "Copyright James Popple 1993"

/* a string which is written to stderr if SHYSTER is invoked without
   arguments */

#define usage_string\
   "usage:\t" \
   "shyster [ -a ] [ -c filename ] [ -d filename ] [ -D filename ] [ -e ]\n" \
   "\t\t[ -h number number ] [ -i ] [ -l filename ] [ -p filename ]\n" \
   "\t\t[ -q ] [ -r filename ] [ -w filename ]\n"

/* the versions of LaTeX and TeX for which SHYSTER has been developed */

#define LaTeX_Version "LaTeX version 2.09 <25 March 1992>"
#define TeX_Version "TeX version 3.141"

/* maxima */

#define Max_Filename_Length 256
#define Max_Error_Message_Length 256
#define Max_LaTeX_Line_Width 64

/* other constants */

#define Empty_String ""
#define Null_Character '\0'
#define Space_Character ' '
#define Carriage_Return_Character '\n'
#define Top_Level 0
#define No_Hang 0
#define Hang 1

/* functions whose returned values are always ignored */

#define fprintf (void) fprintf
#define free (void) free
#define gets (void) gets
#define sprintf (void) sprintf

/* simple types */

typedef unsigned int cardinal;
typedef float floating_point;
typedef char *string;
typedef FILE *file;

/* enumerated type */

typedef enum {
   FALSE,
   TRUE
} boolean;

/* external functions */

extern void
Indent(
      file stream,
      cardinal level);

extern void
Write(
      file stream,
      string write_string,
      const string suffix,
      const cardinal level,
      const boolean hanging_indent);

extern void
Write_Error_Message_And_Exit(
      file stream,
      const string module_name,
      string message);

extern void
Write_Warning_Message(
      file stream,
      const string module_name,
      string message,
      const cardinal level);

extern void
Write_LaTeX_Header(
      file stream,
      boolean inputable_latex);

extern void
Write_LaTeX_Trailer(
      file stream,
      boolean inputable_latex);

extern boolean
Is_Digit(
      int ch);

shyster.c

/* This is the implementation file for the Shyster module. */

#include <stdio.h>
#include <stdlib.h>
#include "shyster.h"
#include "cases.h"
#include "statutes.h"

extern void
Indent(
      file stream,
      cardinal level)

/* Writes the equivalent of 4 x level spaces (1 tab = 8 spaces). */

{
   while (level > 1) {
      fprintf(stream, "\t");
      level -= 2;
   }
   if (level == 1)
      fprintf(stream, "    ");
}

static void
write_line(
      file stream,
      string *write_string,
      const cardinal level,
      const boolean hanging_indent,
      cardinal count)

/* Writes a line of characters from *write_string.  Writes characters up to
   the last space in the next count characters, then breaks the line and
   indents the next line by 4 x level spaces (plus an extra two spaces if
   hanging_indent is TRUE). Leaves *write_string pointing to the character
   after the space at which the line was broken. */

{
   /* find the last space that will fit on the line */

   while ((*(*write_string + count) != Space_Character) && (count != 0))
      count--;

   if (count == 0) {

      /* there is no convenient place to break this line, so write as much as
         will fit, with a % character at the end of the line, and don't
         indent the next line */

      for (count = Max_LaTeX_Line_Width - level * 4 - 1; count != 0; count--)
         fprintf(stream, "%c", *((*write_string)++));
      fprintf(stream, "%%\n");

   } else {

      /* there is (at least) one space in this line, so write each of the
         characters up to the last space, break the line, and indent the next
         line */

      while (count != 0) {
         fprintf(stream, "%c", *((*write_string)++));
         count--;
      }
      (*write_string)++;
      fprintf(stream, "\n");
      Indent(stream, level);
      if (hanging_indent)
         fprintf(stream, "  ");
   }
}

extern void
Write(
      file stream,
      string write_string,
      const string suffix,
      const cardinal level,
      const boolean hanging_indent)

/* Writes write_string plus suffix.  Breaks lines (at spaces) so that they
   are no longer than Max_LaTeX_Line_Width characters.  Indents lines by 4 x
   level spaces.  Indents lines after the first by a further two spaces, if
   hanging_indent is TRUE.  (Assumes that suffix is less than
   Max_LaTeX_Line_Width - level x 4 characters long.) */

{
   cardinal count = 0,
      suffix_length = 0,
      line_length = Max_LaTeX_Line_Width - level * 4;
   boolean hanged = FALSE;

   if (write_string == NULL)

      /* there is no string to write */

      fprintf(stream, "%s", Null_String);

   else {

      /* there is a string to write */

      if (suffix != Null_String)

         /* there is a suffix, so set suffix_length to the number of
            characters in the suffix, up to (but not including) the first
            carriage return (if there is one) */

         while ((*(suffix + suffix_length) != Null_Character) &&
               (*(suffix + suffix_length) != Carriage_Return_Character))
            suffix_length++;

      Indent(stream, level);

      while (*(write_string + count) != Null_Character) {

         if (count == line_length) {

            /* there are more characters left in the string than will fit on
               this line, so write as much as will fit */

            write_line(stream, &write_string, level, hanging_indent, count);
            if (!hanged && hanging_indent) {
               line_length -= 2;
               hanged = TRUE;
            }
            count = 0;
         }
         count++;
      }

      /* the rest of the string will fit on a line */

      if (count + suffix_length > line_length) {

         /* ... but the rest of the string plus the suffix will be too long
            to fit on a line, so write as much as will fit */

         write_line(stream, &write_string, level, hanging_indent, count);

         /* the rest of the string plus the suffix will fit on the new line;
            set count to be the number of characters still to be written */

         count = 0;
         while (*(write_string + count) != Null_Character)
            count++;
      }

      /* write the rest of the string */

      while (count != 0) {
         fprintf(stream, "%c", *write_string++);
         count--;
      }
   }

   /* write the suffix */

   fprintf(stream, "%s\n", suffix);
}

extern void
Write_Error_Message_And_Exit(
      file stream,
      const string module_name,
      string message)

/* Writes "ERROR (module_name): message." to stderr and to stream. Exits with
   a value of EXIT_FAILURE (defined in stdlib.h). */

{
   static char full_message[Max_Error_Message_Length];

   sprintf(full_message, "ERROR (%s): %s", module_name, message);

   Write(stderr, full_message, ".\n", Top_Level, Hang);

   /* write to stream only if stream is not stdout (the error message has
      already been written to stderr) */

   if ((stream != NULL) && (stream != stdout))
      Write(stream, full_message, ".\n", Top_Level, Hang);

   exit(EXIT_FAILURE);
}

extern void
Write_Warning_Message(
      file stream,
      const string module_name,
      string message,
      const cardinal level)

/* Writes "WARNING (module_name): message." to stderr and to stream. */

{
   static char full_message[Max_Error_Message_Length];

   if (stream != NULL) {

      sprintf(full_message, "WARNING (%s): %s", module_name, message);

      Write(stderr, full_message, ".\n", Top_Level, Hang);

      /* write to stream only if stream is not stdout (the warning message
         has already been written to stderr) */

      if (stream != stdout)
         Write(stream, full_message, ".\n", level, Hang);
   }
}

extern void
Write_LaTeX_Header(
      file stream,
      boolean inputable_latex)

/* Writes LaTeX code to go at the start of a LaTeX document.  Writes code
   that can be included in another LaTeX document (i.e. not stand-alone
   code), if inputable_latex is TRUE. */

{
   fprintf(stream, "%% Produced by %s\n\n"
         "%% %s\n\n", Shyster_Version, Copyright_Message);
   if (!inputable_latex)
      fprintf(stream, "%% This is a stand-alone LaTeX file.\n");
   else
      fprintf(stream, "%% This is not a stand-alone LaTeX file.\n"
            "%% Include it in a LaTeX document using the \\input command.\n");
   fprintf(stream, "%% Use %s and %s.\n\n", LaTeX_Version, TeX_Version);
   if (!inputable_latex)
      fprintf(stream, "\\documentstyle[12pt]{article}\n"
            "\\oddsidemargin=-5.4mm\n"
            "\\evensidemargin=-5.4mm\n"
            "\\topmargin=-5.4mm\n"
            "\\headheight=0mm\n"
            "\\headsep=0mm\n"
            "\\textheight=247mm\n"
            "\\textwidth=170mm\n"
            "\\footskip=15mm\n"
            "\\pagestyle{plain}\n\n"
            "\\begin{document}\n\n");
}

extern void
Write_LaTeX_Trailer(
      file stream,
      boolean inputable_latex)

/* Writes LaTeX code to go at the end of a LaTeX document.  Writes code that
   can be included in another LaTeX document (i.e. not stand-alone code), if
   inputable_latex is TRUE. */

{
   if (!inputable_latex)
      fprintf(stream, "\\end{document}\n");
}

extern boolean
Is_Digit(
      int ch)

/* Returns TRUE, iff ch is a digit (0 ... 9). */

{
   return ((ch >= Zero_Character) && (ch <= Nine_Character));
}

static void
error_exit(
      const string message)
{
   Write_Error_Message_And_Exit(NULL, "Shyster", message);
}

static void
parse_arguments(
      int argc,
      string *argv,
      boolean *adjust,
      boolean *echo,
      cardinal *hypothetical_reports,
      cardinal *hypothetical_changes,
      boolean *inputable_latex,
      boolean *verbose,
      string *specification_filename,
      string *distances_filename,
      string *log_filename,
      string *probabilities_filename,
      string *report_filename,
      string *dump_filename,
      string *weights_filename)

/* Parses the UNIX command line arguments (argv [1] ... argv [argc - 1]) and
   stores the information in the variables pointed to by the 13 other
   parameters. */

{
   string argument;
   char message[Max_Error_Message_Length];

   if (argc < 2) {

      /* no argument was provided, so write usage_string to stderr and exit
         with a value of EXIT_FAILURE (defined in stdlib.h) */

      fprintf(stderr, usage_string);
      exit(EXIT_FAILURE);
   }

   /* skip over the first argument (the name by which SHYSTER was invoked) */

   argc--;
   argv++;

   /* while there are still arguments to parse ... */

   while (argc > 0) {

      argument = *argv;

      if (*argument++ != '-')

         /* this argument is not a switch */

         break;

      switch (*argument++) {

         case 'a':

            /* -a: enable weight adjustment */

            *adjust = TRUE;
            break;

         case 'c':

            /* -c specification: read the case law specification from
               "specification.cls" */

            if (argc > 1) {
               argc--;
               argv++;
               *specification_filename = *argv;
            } else
               error_exit("must supply a filename with -c");
            break;

         case 'd':

            /* -d distances: write distances to "distances-area.tex" */

            if (argc > 1) {
               argc--;
               argv++;
               *distances_filename = *argv;
            } else
               error_exit("must supply a filename with -d");
            break;

         case 'D':

            /* -D dump: write dump to "dump.tex" */

            if (argc > 1) {
               argc--;
               argv++;
               *dump_filename = *argv;
            } else
               error_exit("must supply a filename with -D");
            break;

         case 'e':

            /* -e: enable echo mode */

            *echo = TRUE;
            break;

         case 'h':

            /* -h r c: hypothesize, reporting on r hypotheticals per result
               with a limit of c changes */

            if (argc > 2) {
               argc--;
               argv++;
               while (**argv != Null_Character) {
                  if (!Is_Digit(**argv))
                     error_exit("argument to -h must be two numbers");
                  *hypothetical_reports = (10 * *hypothetical_reports) +
                        (cardinal) **argv - (cardinal) Zero_Character;
                  (*argv)++;
               }
               argc--;
               argv++;
               *hypothetical_changes = 0;
               while (**argv != Null_Character) {
                  if (!Is_Digit(**argv))
                     error_exit("argument to -h must be two numbers");
                  *hypothetical_changes = (10 * *hypothetical_changes) +
                        (cardinal) **argv - (cardinal) Zero_Character;
                  (*argv)++;
               }
            } else
               error_exit("must supply two numbers with -h");
            break;

         case 'i':

            /* -i: write LaTeX code that can be included in another LaTeX
               document (i.e. not stand-alone code) */

            *inputable_latex = TRUE;
            break;

         case 'l':

            /* -l log: write log to "log.log" */

            if (argc > 1) {
               argc--;
               argv++;
               *log_filename = *argv;
            } else
               error_exit("must supply a filename with -l");
            break;

         case 'p':

            /* -p probabilities: write probabilities to "probabilities.tex" */

            if (argc > 1) {
               argc--;
               argv++;
               *probabilities_filename = *argv;
            } else
               error_exit("must supply a filename with -p");
            break;

         case 'q':

            /* -q: enable quiet mode (don't summarize cases, etc.) */

            *verbose = FALSE;
            break;

         case 'r':

            /* -r report: write report to "report-area.tex" */

            if (argc > 1) {
               argc--;
               argv++;
               *report_filename = *argv;
            } else
               error_exit("must supply a filename with -r");
            break;

         case 'w':

            /* -w weights: write weights to "weights.tex" */

            if (argc > 1) {
               argc--;
               argv++;
               *weights_filename = *argv;
            } else
               error_exit("must supply a filename with -w");
            break;

         default:
            sprintf(message, "unrecognized option -%s", argument - 1);
            error_exit(message);
            break;
      }
      argc--;
      argv++;
   }
}

extern int
main(
      int argc,
      string *argv)

/* Extracts the options and arguments from the UNIX command line, initializes
   the rule-based system and the case-based system, then invokes the
   rule-based system. */

{
   char filename[Max_Filename_Length],
      message[Max_Error_Message_Length];
   statute_law_specification statute_law;
   case_law_specification case_law;
   file log_stream;
   boolean adjust = FALSE,
      echo = FALSE,
      inputable_latex = FALSE,
      verbose = TRUE;
   cardinal hypothetical_reports = 0,
      hypothetical_changes;
   string specification_filename = NULL,
      distances_filename = NULL,
      log_filename = NULL,
      probabilities_filename = NULL,
      report_filename = NULL,
      dump_filename = NULL,
      weights_filename = NULL;

   /* extract the options and arguments from the UNIX command line */

   parse_arguments(argc, argv, &adjust, &echo, &hypothetical_reports, &hypothetical_changes,
         &inputable_latex, &verbose, &specification_filename, &distances_filename,
         &log_filename, &probabilities_filename, &report_filename, &dump_filename,
         &weights_filename);

   /* write version and copyright information to stdout */

   fprintf(stdout, "%s\n\n%s\n\n", Shyster_Version, Copyright_Message);

   if (log_filename == NULL)

      /* no log filename was specified, so log information will be written to
         stdout */

      log_stream = stdout;

   else {

      /* open the log file */

      sprintf(filename, "%s%s", log_filename, Log_File_Extension);
      if ((log_stream = fopen(filename, "w")) == NULL) {
         sprintf(message, "can't open log file \"%s\"", filename);
         error_exit(message);
      }

      /* write version and copyright information to the log file */

      fprintf(log_stream, "%s\n\n"
            "%s\n\n", Shyster_Version, Copyright_Message);
   }

   /* initialize the rule-based system */

   statute_law = Initialize_Statutes();

   /* initialize the case-based system */

   case_law = Initialize_Cases(log_stream, inputable_latex, verbose, specification_filename,
         dump_filename, probabilities_filename, weights_filename);

   /* invoke the rule-based system */

   Statute_Law(log_stream, statute_law, case_law, adjust, echo, inputable_latex, verbose,
         hypothetical_reports, hypothetical_changes, distances_filename, weights_filename,
         report_filename);

   /* write "Finished." to the log file (if there is one) and to stdout */

   if (log_filename != NULL)
      fprintf(log_stream, "Finished.\n");
   fprintf(stdout, "Finished.\n");

   /* close the log file */

   if (fclose(log_stream) == EOF) {
      sprintf(message, "can't close log file \"%s\"", filename);
      error_exit(message);
   }

   /* everything worked, so exit with a value of EXIT_SUCCESS (defined in
      stdlib.h) */

   return EXIT_SUCCESS;
}

Other SHYSTER modules: Statutes, Cases, Tokenizer, Parser, Dumper, Checker, Scales, Adjuster, Consultant, Odometer and Reporter.