/*
  These are the files for the business report WITH SUB-TOTALS
  ALL of these files are different from the corresponding files
  with "3" in their titles previusly sent.

  these have NOTHING to do with assignment 3

  They are provided for class discussion/review this week

  as it stands there are two known bugs which we could discuss in
  class

        1.      if the last line printed on a page is the last detail
                line for a group
        2.      if the last line printed on a page is one of the total
                lines
*/


################awkfilt4.c 


/* awkfilt4.c - awk-like filter program */
/*
        This filter program requires three functions to make it into a
        complete program. The three functions should be in ONE source
        file, sharing static global data between them if needed.  In each
        case they should set the return value to zero to tell the main
        program to output whatever is in dest, and non-zero to signal that
        nothing is to be output.  The main program is responsible for
        reading, writing and routing and should be absolutely standard.

        It is intended that the contents of dest will not be arbitrarily
        cleared ... that is to say that it MAY be used to build output
        incrementally during several function calls ... the contents only
        get written to stdout when a zero is returned to main by one of the
        three functions

        int begin(char* dest, char* src);

        This function is called ONCE prior to reading stdin. When called,
        the contents of dest are irrelevant, it is provided to enable
        data to be passed back to main. If this function returns zero (normal)
        then main() writes the contents of dest to standard output. If
        this function returns non-zero then nothing is written to stdout.
        The variable src is initialised to '\0', and it probably not useful
        but it is provided now to facilitate possible future use.

        int process(char* dest, char* source);

        This function is called once for every input record.  The input
        record is pointed to by source. Default processing is to move
        the contents of source to dest and return zero to cause the record
        to be written to stdout.  However, there is no need for the
        contents of dest to bear any relationship to source so that any output
        may be written.  To suppress the writing of output return non-zero.
        The contents of dest are NOT cleared by the standard main line program
        so that dest may be used to build output incrementally, appending data
        during several calls, then writing it based on some
        application-specific condition or trigger.


        int finish(char* dest, char* src);

        This function is called ONCE prior after eof on stdin.
        The contents of dest are irrelevant, it is provided to enable
        data to be passed back to main. If this function returns zero
        then main() writes the contents of dest to standard output. If
        this function returns non-zero then nothing is written to stdout.
        The variable src is initialised to '\0', and it probably not useful
        but it is provided now to facilitate possible future use.

*/

#define MAXLEN 256

#include <stdio.h>
#include <string.h>

int begin(char* dest, char* source);    /* user-supplied function */
int process(char *dest, char *source);  /* user-supplied function */
int finish(char* dest, char* source);   /* user-supplied function */
void do_print(char* line);              /* print with page-breaks */
int level_break(char* source);     /* detect, prcess level change */

int main(void)
{
        char input[MAXLEN] = "\0";
        char output[MAXLEN] = "\0";

        int err = 0;                   /* error flag  0 = OK */

        /*--- awk-like BEGIN processing */
        err = begin(output, input);     /* pre-file processing */
        if (!err) do_print(output);         /* write output */

        /*--- main processing */
        gets(input);                    /* get first input */
        while (!feof(stdin))
        {
    level_break(input);           /* check, process level break */
                err = process(output, input); /* process */
                if (!err) do_print(output);   /* write output */
                gets(input);                  /* get next input */
        }

        /*--- awk-like END processing */

  /* force totals after last record has been processed */
  strcpy(input, "~~~~~");
  level_break(input);

        err = finish(output, input);     /* post-processing */
        if (!err) do_print(output);       /* write output */

 return 0;
}

########################busrep4.c

/* busrep4.c - functions for use with awkfilt4444.c to:

   PRODUCE A TYPICAL BUSINESS REPORT

      this is a typical "list and total" report

      Input is text records containing five fields as follows, a simplified
      "cash register" example:

      customer id
      invoice number
      inventory item (code or description - developer's choice)
      quantity sold
      unit price of inventory item

      a static global two dimensional array (of float)
      is used to hold the totals

      FUNCTION begin
         intializes the array used to total the items
         formats the header line for the report
      FUNCTION process
         totals the items
         formats each detail line for the report
      FUNCTION finish
         formats the total line for the report

      ***NOTE***
         all the actual output is done by the main() function
 */

#include <string.h>
#include <stdio.h>
#include <stdlib.h>

#include "busrep4.h"    /* function prototypes */



/*--- global data */

/* arrays totals[][] are used to accumulate then print various totals    */

#define LEVELS 2
#define ACCUMS 3
static float totals[LEVELS][ACCUMS];

/* elements of the array totals are accessed using the        */
/* following defines for the possible index values of ACCUMS  */
#define QTY 0
#define VALUE 1
#define COUNT 2

/* variables used to detect and process level change */
static char current[81]  = "\0";
static char previous[81] = "\0";
static char continued[81] = "\0";  /* continued message */

/* vaiables used to control page-breaks */
static int page_number = 0;
static int lines_per_page = 55;
static int lines_printed = 55;



/*--- function prototypes  - all in #include files */

/*==================================================================*/
/*==================== awk-like BEGIN processing ===================*/
/*==================================================================*/
int begin(char* dest, char* source)     /* user supplied function */
{
  int j = 0;        /* loop control - LEVELS*/
  int k = 0;        /* loop control - ACCUMS*/

  /* initialize totals to zeros */
  for (j = 0; j < LEVELS; j++)
  {
    for (k = 0; k < ACCUMS; k++)
    {
      totals[j][k] = 0.0;
    }
        }
  return 1;          /* by default write nothing to stdout at start */
}


/*==================================================================*/
/*================== awk-like END processing =======================*/
/*==================================================================*/
int finish(char* dest, char* source)     /* user supplied function */
{
  char temp[256];

  /*--- format final total  line */
  sprintf(temp,
         "Total QTY and VALUE          %9.2f       %9.2f\nITEMS=%1.0f\n\n",
                totals[1][QTY], totals[1][VALUE], totals[1][COUNT]);

  /*--- arrange to display/print */
  strcpy(dest, "\n======================================================\n");
  strcat(dest, temp);
        return 0;
}


/*==================================================================*/
/*====================== per-record processing =======================*/
/*==================================================================*/
int process(char* dest, char* source)  /* user supplied function */
{
  int word_count = 0;
  char* words[6];   /* words from input (6 is big enough ... expecting 5) */
  char temp[81];     /* used in formatting */

  /* split up input line into array*/
  word_count = split_tokens(source, " \t", 5, words);
  /* word_count should be 5 ... check and flag error if necessary */
  if (word_count != 5)
  {

          strcpy(dest, source);   /*--- by default copy source -> dest        */
    return 99;
  }

  /*--- accumulate totals into DETAIL-level [0] ofarray */
  totals[0][QTY] = totals[0][QTY] + atof(words[3]);
  totals[0][VALUE] = totals[0][VALUE] + (atof(words[3]) * atof(words[4]) );
  totals[0][COUNT]++;

  /*--- format detail line for output */
  sprintf(temp, "%-5s %-5s %-20s %5.2f %7.2f %7.2f",
            words[0], words[1], words[2], atof(words[3]),
            atof(words[4]), atof(words[3]) * atof(words[4]) );
        strcpy(dest, temp);   /*--- by default copy source -> dest        */
        return 0;               /*--- by default cause dest to go to stdout */
}

/*==================================================================*/
/* ================ format header lines =================== */
/*==================================================================*/
int do_header(char* heading)
{
  /*--- format heading line */
  sprintf(heading, "\f\n\n%-5s %-5s %-20s %5s %7s %7s %s %d\n",
            "Cust#", "Inv#", "Inventory Item", "Qty",
             "Price", "Value","Page -", page_number);

  /*--- arrange to display/print */
  strcat(heading, "======================================================\n");
  return 0;
}

/* do_print.c - print with page control */

/*==================================================================*/
/* ============= does printing - handles page-breaks ======= */
/*==================================================================*/
void do_print (char* text)
{
  int err = 0;              /* holds return value of called funcs */
  char output[256] = "\0";  /* holds output text */

  /* ---- check if headings are needed ... do them if needed */
  if (lines_printed >= lines_per_page)
  {
    page_number++;
    err = do_header(output);  /* filled by function called */
    if (!err) puts(output);   /* output header */
    lines_printed = 1 + count_lines(output);

    /* and do level break headings */
    do_level_header(output);
    puts(output);
    lines_printed = 1 + count_lines(output);
  }

  /* ---- in any case (with or without headings */
  puts(text);
  lines_printed = lines_printed + 1 + count_lines(text);
}


/*==================================================================*/
/* ================ counts lines in text =================== */
/*==================================================================*/
int count_lines(char* text)
{
  int k = 0;        /* loop control */
  int count = 0;    /* holds count of '\n' characters */

  for (k = 0; text[k] != '\0'; k++)
  {
    if (text[k] == '\n')
    {
      count++;
    }
  }
  return count;
}


/*==================================================================*/
/* =========== detects and processes level breaks ============ */
/*==================================================================*/
int level_break(char* record)
{
  /* variables which hold current and previous level-change data */

  char temp[256];

  int k = 0;          /* loop control */
  int word_count = 0; /* holds count of words found by split_tokens()       */
  char* words[6];     /* words from input (6 is big enough ... expecting 5) */

  /* ------------OVERVIEW--------------------------------------  */
  /*  extract the data from the record that governs the level    */
  /*  level change                                               */
  /*  check "current" against "previous" to see if level change  */
  /*  happened                                                   */
  /*  do the following in this sequemce                          */
  /*  - print totals for level change                            */
  /*  - roll up the totals and zero the detail level             */
  /*  - do the headings for the level change                     */
  /*--- extract the data from the record that governs the level   */
  /*    level change                                              */

  word_count = split_tokens(record, " \t", 5, words);
  /*  words[0] now contains data which controls level-change      */
  strcpy(current, words[0]);

  /*--- check "current" against "previous" to see if level change */
  /*  happened                                                    */
  if ( ! strcmp(current, previous))
  {
    /* no level change */

    strcpy(continued, "continued");      /* set continued message text */
    return 0;
  }

  /*--- level change has been detected therefore                  */
  /*--- do the following in this sequemce                         */
  /*--- print totals for level change                             */

  /* do not do totals at start of job */
  if (previous[0] != '\0')
  {
    do_level_totals(temp);
    do_print(temp);
    /*  - roll up the totals and zero the detail level              */
    for (k = 0; k < ACCUMS; k++)
    {
      totals[1][k] = totals[1][k] + totals[0][k];  /* roll totals   */
      totals[0][k] = 0.0;                          /* zero totals   */
    }
    /* set continued message text */
    strcpy(continued, "");
  }

  /*--- do the headings for the level change                      */
  /*    do not do headings after EOF or until after first record read  */
  if ( (previous[0] != '\0') && (current[0] != '~'))
  {
    do_level_header(temp);
    do_print(temp);
  }

  /*--- store new value of previous      */
  strcpy(previous, current);

  return 1;
}


/*==================================================================*/
/* =========== formats level break totals ============ */
/*==================================================================*/
void do_level_totals(char* dest)
{
  char temp[256] = "\0";

  /*--- format total line */
  sprintf(temp,
         "Total for %s               %9.2f       %9.2f\n\n",
                previous, totals[0][QTY], totals[0][VALUE]);
  strcpy(dest, "\n======================================================\n");
  strcat(dest, temp);
}


/*==================================================================*/
/* =========== format level break headings ============ */
/*==================================================================*/
void do_level_header(char* dest)
{

  /*--- format heading line */
  sprintf(dest, "\n-------Customer %s---------%s\n", current, continued);
}

########################busrep4.h   (... included by busrep4.c)

/* busrep4.h - prototypes for busrep4 */

int   begin(char* dest, char* source);    /* user supplied function */
int   count_lines(char* line);            /* used by page control   */
int   do_header(char* header);            /* page headings          */
void  do_level_header(char* dest);        /* level change headings  */
void  do_level_totals(char* dest);        /* sub totals             */
void  do_print(char* line);               /* do the actual printing */
int   finish(char* dest, char* source);   /* user supplied function */
int   process(char* dest, char* source);  /* user supplied function */

int   split_tokens(char* str, char* delims, int expect, char* tokens[]);


