/* * PROGRAM: JRD Command Oriented Query Language * MODULE: report.c * DESCRIPTION: Report writer runtime control * * The contents of this file are subject to the Interbase Public * License Version 1.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy * of the License at http://www.Inprise.com/IPL.html * * Software distributed under the License is distributed on an * "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express * or implied. See the License for the specific language governing * rights and limitations under the License. * * The Original Code was created by Inprise Corporation * and its predecessors. Portions created by Inprise Corporation are * Copyright (C) Inprise Corporation. * * All Rights Reserved. * Contributor(s): ______________________________________. */ #include "firebird.h" #include "../jrd/ib_stdio.h" #include "../qli/dtr.h" #include "../qli/exe.h" #include "../qli/compile.h" #include "../qli/report.h" #include "../qli/all_proto.h" #include "../qli/eval_proto.h" #include "../qli/exe_proto.h" #include "../qli/forma_proto.h" #include "../qli/mov_proto.h" #include "../qli/repor_proto.h" static void bottom_break(BRK, PRT); static void increment_break(BRK); static void initialize_break(BRK); static int test_break(BRK, RPT, QLI_MSG); static void top_break(BRK, PRT); static void top_of_page(PRT, BOOLEAN); #define SWAP(a,b) {temp = a; a = b; b = temp;} void RPT_report( QLI_NOD loop) { /************************************** * * R P T _ r e p o r t * ************************************** * * Functional description * Execute a FOR loop. This may require that a request get * started, a message sent, and a message received for each * record. At the other end of the spectrum, there may be * absolutely nothing to do. * **************************************/ RPT report; QLI_NOD node; QLI_REQ request; QLI_MSG message; DSC *desc; BRK control; STR string; PRT print; UCHAR *temp; /* Get to actual report node */ node = loop->nod_arg[e_for_statement]; report = (RPT) node->nod_arg[e_prt_list]; print = (PRT) node->nod_arg[e_prt_output]; print->prt_new_page = (int(*)()) top_of_page; print->prt_page_number = 0; /* Get to actual report node */ /* If there is a request associated with the loop, start it and possibly send a message slong with it. */ if (request = (QLI_REQ) loop->nod_arg[e_for_request]) EXEC_start_request(request, (QLI_MSG) loop->nod_arg[e_for_send]); else if (message = (QLI_MSG) loop->nod_arg[e_for_send]) EXEC_send(message); /* Receive messages in a loop until the end of file field comes up true. */ message = (QLI_MSG) loop->nod_arg[e_for_receive]; /* Get the first record of the record. If there isn't anything, don't worry about anything. */ desc = EXEC_receive(message, (PAR) loop->nod_arg[e_for_eof]); if (*(USHORT *) desc->dsc_address) return; if (!report->rpt_buffer) { string = (STR) ALLOCDV(type_str, message->msg_length); report->rpt_buffer = (UCHAR *) string->str_data; } MOVQ_fast((SCHAR*) message->msg_buffer, (SCHAR*) report->rpt_buffer, (SLONG) message->msg_length); if (control = report->rpt_top_rpt) FMT_print((QLI_NOD) control->brk_line, print); top_of_page(print, TRUE); initialize_break(report->rpt_bottom_breaks); initialize_break(report->rpt_bottom_page); initialize_break(report->rpt_bottom_rpt); /* Force TOP breaks for all fields */ for (control = report->rpt_top_breaks; control; control = control->brk_next) FMT_print((QLI_NOD) control->brk_line, print); for (;;) { /* Check for bottom breaks. If we find one, force all lower breaks. */ for (control = report->rpt_bottom_breaks; control; control = control->brk_next) if (test_break(control, report, message)) { SWAP(message->msg_buffer, report->rpt_buffer); bottom_break(control, print); SWAP(message->msg_buffer, report->rpt_buffer); initialize_break(control); break; } if (print->prt_lines_remaining <= 0) top_of_page(print, FALSE); /* Now check for top breaks. */ for (control = report->rpt_top_breaks; control; control = control->brk_next) if (test_break(control, report, message)) { top_break(control, print); break; } /* Increment statisticals and print detail line, if any */ increment_break(report->rpt_bottom_breaks); increment_break(report->rpt_bottom_page); increment_break(report->rpt_bottom_rpt); if (node = report->rpt_detail_line) FMT_print(node, print); /* Get the next record. If we're at end, we're almost done. */ SWAP(message->msg_buffer, report->rpt_buffer); desc = EXEC_receive(message, (PAR) loop->nod_arg[e_for_eof]); if (*(USHORT *) desc->dsc_address) break; } /* Force BOTTOM breaks for all fields */ SWAP(message->msg_buffer, report->rpt_buffer); bottom_break(report->rpt_bottom_breaks, print); bottom_break(report->rpt_bottom_rpt, print); if (control = report->rpt_bottom_page) FMT_print((QLI_NOD) control->brk_line, print); } static void bottom_break( BRK control, PRT print) { /************************************** * * b o t t o m _ b r e a k * ************************************** * * Functional description * Force all lower breaks then take break. * **************************************/ LLS stack; if (!control) return; if (control->brk_next) bottom_break(control->brk_next, print); for (stack = control->brk_statisticals; stack; stack = stack->lls_next) EVAL_break_compute((QLI_NOD) stack->lls_object); FMT_print((QLI_NOD) control->brk_line, print); } static void increment_break( BRK control) { /************************************** * * i n c r e m e n t _ b r e a k * ************************************** * * Functional description * Toss another record into running computations. * **************************************/ LLS stack; for (; control; control = control->brk_next) for (stack = control->brk_statisticals; stack; stack = stack->lls_next) EVAL_break_increment((QLI_NOD) stack->lls_object); } static void initialize_break( BRK control) { /************************************** * * i n i t i a l i z e _ b r e a k * ************************************** * * Functional description * Execute a control break. * **************************************/ LLS stack; for (; control; control = control->brk_next) for (stack = control->brk_statisticals; stack; stack = stack->lls_next) EVAL_break_init((QLI_NOD) stack->lls_object); } static int test_break( BRK control, RPT report, QLI_MSG message) { /************************************** * * t e s t _ b r e a k * ************************************** * * Functional description * Check to see if there has been a control break for an expression. * **************************************/ DSC desc1, desc2, *ptr1, *ptr2; UCHAR *p1, *p2; USHORT l; /* Evaluate the two versions of the expression */ if (ptr1 = EVAL_value((QLI_NOD) control->brk_field)) desc1 = *ptr1; p1 = message->msg_buffer; message->msg_buffer = report->rpt_buffer; if (ptr2 = EVAL_value((QLI_NOD) control->brk_field)) desc2 = *ptr2; message->msg_buffer = p1; /* Check for consistently missing */ if (!ptr1 || !ptr2) return (ptr1 != ptr2); /* Both fields are present. Check values. Luckily, there's no need to worry about datatypes. */ p1 = desc1.dsc_address; p2 = desc2.dsc_address; l = desc1.dsc_length; if (desc1.dsc_dtype == dtype_varying) l = 2 + *(USHORT *) p1; if (l) do if (*p1++ != *p2++) return TRUE; while (--l); return FALSE; } static void top_break( BRK control, PRT print) { /************************************** * * t o p _ b r e a k * ************************************** * * Functional description * Execute a control break. * **************************************/ LLS stack; for (; control; control = control->brk_next) { for (stack = control->brk_statisticals; stack; stack = stack->lls_next) EVAL_break_compute((QLI_NOD) stack->lls_object); FMT_print((QLI_NOD) control->brk_line, print); } } static void top_of_page( PRT print, BOOLEAN first_flag) { /************************************** * * t o p _ o f _ p a g e * ************************************** * * Functional description * Handle top of page condition. * **************************************/ RPT report; BRK control; ++print->prt_page_number; report = print->prt_report; if (!first_flag) { if (control = report->rpt_bottom_page) FMT_print((QLI_NOD) control->brk_line, print); FMT_put("\f", print); } print->prt_lines_remaining = print->prt_lines_per_page; if (control = report->rpt_top_page) FMT_print((QLI_NOD) control->brk_line, print); else if (report->rpt_column_header) { if (report->rpt_header) FMT_put(report->rpt_header, print); if (report->rpt_column_header) FMT_put(report->rpt_column_header, print); } if (report->rpt_bottom_page) initialize_break(report->rpt_bottom_page); }