#! /bin/sh # This is a shell archive. Type 'sh ' to unpack. echo x - README.1st cat >README.1st <<'MKSHAR_EOF' Multiple Report Interleaved Output Processing This presents a sample of 4GL code which writes interleaved sections of multiple reports to a single output stream. In the case of the sample it is a detail report and a summary report which would be difficult to implement as a single 4GL report module. The user requirement is that the summary page for a client appear immediately following the detail page. The solution presented is to write both reports to a pipe and have the pipe filter handle interleaving the reports properly using UNIX file locking functions. The indicator to the sample filter, append.c, that a logical report page is complete is the FormFeed (^L or \f) character at the end of each physical page accomplished using the 4GL END OF PAGE option in the OUTPUT section or the page length passed on the command line. The method requires that a separate report be started and finished for each grouping that causes an alternation in the output from one report type to the other (ie from summary to detail). The order in which the reports are FINISHed is the order in which their pages will be flushed as 4GL itself buffers the report lines in a temp table until the report is FINISHed. To allow for logical report pages or sections that span physical pages a semaphore character or string can be printed from the REPORT HEADER section as appropriate and the filter modified to recognize that semaphore string as the point at which to flush buffered output, optionally not printing the semaphore. The current version uses a fixed array of 100 strings 1024 characters each to buffer the report. Modifiing this to use a dynamically growing array is trivial and left to the user as an exercise. Files included: README.1st - This file. append.c - The sample output filter program. sample.4gl - The sample 4GL module. sample.sql - Table structure for the sample program. sample.dat - Sample data for the sample program. MKSHAR_EOF echo x - append.c cat >append.c <<'MKSHAR_EOF' /* append.c - buffer one page at a time (up to 100 lines) and flush locking the output file to permit multiple writers to the file without interference. This could be implemented as a Perl script if "C" is not available. Author: Art S. Kagel */ #include #include #include #define ARRSIZE 100 int main( int argc, char **argv ) { FILE *out; char line[ARRSIZE][1024]; long ln=0, i, fd, pgln; /* Arg 1 is output file name */ out = fopen( argv[1], "a" ); fd = fileno( out ); /* Arg 2 is output page length */ pgln = atol( argv[2] ); if (!pgln) pgln = ARRSIZE; /* Buffer one page of data and then output. */ for (;;) { if (fgets( line[ln], 1024, stdin ) == (char *)NULL) break; if (strstr( line[ln], "\f" ) || (ln + 1) >= pgln) { lockf( fd, F_LOCK, 0 ); for (i=0; i<=ln; i++) fputs( line[i], out ); fflush( out ); lockf( fd, F_ULOCK, 0 ); ln = 0; } else ln++; } if (ln) { lockf( fd, F_LOCK, 0 ); for (i=0; isample.4gl <<'MKSHAR_EOF' # # Sample of a 4GL program that produces multiple interleaved reports using # append.c to filter and flush the output # database flintstones main define cust, ocust char(6), rn_no char(10), type char(2), amt decimal(9,2), first integer declare summ cursor for select * from tm_rental order by cust, type let ocust = "" let first = TRUE run "rm inv.all" -- delete any old output file -ASK -- Start the report the first time -ASK -- The append program buffers one report page and locks the output file -- before flushing to avoid clashes between the two reports. See source. start report summary to pipe "append inv.all 59" -ASK start report detail to pipe "append inv.all 59" -ASK foreach summ into cust, rn_no, type, amt if (first) then -- So the first row does not trigger an empty report -ASK let ocust = cust let first = FALSE end if if (ocust != cust) then -- If customer changes, close summary and detail reports and restart finish report summary finish report detail start report summary to pipe "append inv.all 59" start report detail to pipe "append inv.all 59" end if output to report summary (cust, rn_no, type, amt) output to report detail (cust, rn_no, type, amt) let ocust = cust -- Detects change in customer end foreach -- Don't forget to finish for last customer -ASK finish report summary finish report detail run "lp inv.all" without waiting end main # layout for summary invoice report summary (cust, rn_no, type, amt) define cust char(6), rn_no char(10), type char(2), amt decimal(9,2) OUTPUT -- I added this section, if nothing else it documents -ASK PAGE LENGTH 59 -- Set the page length -ASK LEFT MARGIN 5 -- This is default anyway -ASK TOP OF PAGE "^L" -- This uses form feed char instead of blank lines -ASK ORDER EXTERNAL BY cust, type -- Need this so report knows data is sorted - ASK format page header print "Summary Invoice" print "Bill To : ", cust print print "Type Amt (RM)" print "-----------------" before group of cust skip to top of page after group of type print type, column 8, group sum(amt) using "##,##&.&&" after group of cust print "-----------------" print "TOTAL", column 8, group sum(amt) using "##,##&.&&" print "-----------------" end report # layout for detail invoice report detail (cust, rn_no, type, amt) define cust char(6), rn_no char(10), type char(2), amt decimal(9,2) OUTPUT -- I added this section, if nothing else it documents -ASK PAGE LENGTH 59 -- Set the page length -ASK LEFT MARGIN 5 -- This is default anyway -ASK TOP OF PAGE "^L" -- This uses form feed char instead of blank lines -ASK ORDER EXTERNAL BY cust, type -- Need this so report knows data is sorted -ASK format page header print "Detail Invoice" print "Bill To : ", cust print print "Rental No Type Amt (RM)" print "----------------------------" on every row print rn_no, column 12, type, column 19, amt using "##,##&.&&" before group of cust skip to top of page after group of cust print "----------------------------" print "TOTAL", column 19, group sum(amt) using "##,##&.&&" print "----------------------------" end report MKSHAR_EOF echo x - sample.sql cat >sample.sql <<'MKSHAR_EOF' create table TM_RENTAL ( CUST CHAR(6) , RN_NO CHAR(10) , TYPE CHAR(2) , AMT DECIMAL(9,2) ); MKSHAR_EOF echo x - sample.dat cat >sample.dat <<'MKSHAR_EOF' ABAA02|98000680|TT|343.9| ACAA09|98000014|DT|216.15| ACAA09|98000421|RT|67.08| ACAA09|98000548|RT|67.76| AHAA06|97000159|RT|901.0| AHAA06|98000392|RT|77.64| AHAA06|98000395|RT|70.08| ACAA09|98000004|DT|201.9| ACAA09|98000012|DT|509.18| ACAA09|98000376|TT|288.0| ABAA02|98000539|RT|175.6| ABAA02|98000659|RT|171.0| ABAA02|98000393|TT|96.03| AFAA08|98000665|RT|176.0| AFAA08|98000365|TT|117.71| AFAA08|98000402|TT|781.28| ABAA02|98000440|RT|125.7| ABAA02|98000511|TT|184.69| ABAA02|98000512|TT|196.44| AFAA08|98000721|TT|100.08| AFAA08|98000741|TT|120.47| AHAA06|98000400|RT|60.54| ABAA02|98000694|TT|522.95| ACAA09|98000002|DT|249.35| AHAA06|98000013|DT|450.45| ACAA09|98000017|DT|218.84| AHAA06|98000436|RT|55.34| AHAA06|98000437|RT|70.55| AHAA06|98000444|RT|79.0| MKSHAR_EOF