/* 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 #include 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 #include #include #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[]);