home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Programming Languages Suite
/
ProgramD2.iso
/
Database
/
CLIPR502.DOS
/
SOURCE
/
SYS
/
FRMRUN.PRG
< prev
next >
Wrap
Text File
|
1993-02-15
|
26KB
|
814 lines
/***
*
* Frmrun.prg
*
* Clipper REPORT FORM runtime system
*
* Copyright (c) 1990-1993, Computer Associates International, Inc.
* All rights reserved.
*
* Compile: /m /n /w
*
*/
#include "frmdef.ch"
#include "error.ch"
#define S87_COMPAT // preserve Summer '87 compatability
STATIC aReportData, nPageNumber, nLinesLeft, aReportTotals
STATIC aGroupTotals, lFirstPass, lFormFeeds, nMaxLinesAvail
/***
*
* __ReportForm( <cFRMName>, [<lPrinter>], <cAltFile>,
* [<lNoConsole>], <bFor>, <bWhile>, <nNext>, <nRecord>,
* <lRest>, <lPlain>, [<cHeading>], [<lBEject>],
* [<lSummary>] )
*
*/
PROCEDURE __ReportForm( cFRMName, lPrinter, cAltFile, lNoConsole, bFor, ;
bWhile, nNext, nRecord, lRest, lPlain, cHeading, ;
lBEject, lSummary )
LOCAL lPrintOn, lConsoleOn // Status of PRINTER and CONSOLE
LOCAL cExtraFile, lExtraState // Status of EXTRA
LOCAL nCol, nGroup
LOCAL xBreakVal, lBroke := .F.
LOCAL err
LOCAL lAnyTotals
LOCAL lAnySubTotals
// Resolve parameters
IF cFRMName == NIL
err := ErrorNew()
err:severity := ES_ERROR
err:genCode := EG_ARG
err:subSystem := "FRMLBL"
Eval(ErrorBlock(), err)
ELSE
IF AT( ".", cFRMName ) == 0
cFRMName := TRIM( cFRMName ) + ".FRM"
ENDIF
ENDIF
IF lPrinter == NIL
lPrinter := .F.
ENDIF
IF cHeading == NIL
cHeading := ""
ENDIF
// Set output devices
lPrintOn := SET( _SET_PRINTER, lPrinter )
lConsoleOn := SET( _SET_CONSOLE, .F. )
SET( _SET_CONSOLE, ! ( lNoConsole .OR. !lConsoleOn ) )
IF lPrinter // To the printer
lFormFeeds := .T.
ELSE
lFormFeeds := .F.
ENDIF
IF (!Empty(cAltFile)) // To file
cExtraFile := SET( _SET_EXTRAFILE, cAltFile )
lExtraState := SET( _SET_EXTRA, .T. )
ENDIF
BEGIN SEQUENCE
aReportData := __FrmLoad( cFRMName ) // Load the frm into an array
nMaxLinesAvail := aReportData[RP_LINES]
// Modify aReportData based on the report parameters
IF lSummary != NIL // Set the summary only flag
aReportData[ RP_SUMMARY ] := lSummary
ENDIF
IF lBEject != NIL .AND. lBEject
aReportData[ RP_BEJECT ] := .F.
ENDIF
IF lPlain // Set plain report flag
aReportData[ RP_PLAIN ] := .T.
cHeading := ""
lFormFeeds := .F.
ENDIF
aReportData[ RP_HEADING ] := cHeading
// Add to the left margin if a SET MARGIN has been defined
// NOTE: uncommenting this line will cause REPORT FORM to respect
// SET MARGIN to screen/to file, but double the margin TO PRINT
// aReportData[ RP_LMARGIN ] += SET( _SET_MARGIN )
nPageNumber := 1 // Set the initial page number
lFirstPass := .T. // Set the first pass flag
nLinesLeft := aReportData[ RP_LINES ]
#ifdef S87_COMPAT
QOUT() // output additional line on first page
#endif
// Check to see if a "before report" eject, or TO FILE has been specified
IF aReportData[ RP_BEJECT ]
EjectPage()
ENDIF
// Generate the initial report header manually (in case there are no
// records that match the report scope)
ReportHeader()
// Initialize aReportTotals to track both group and report totals, then
// set the column total elements to 0 if they are to be totaled, otherwise
// leave them NIL
aReportTotals := ARRAY( LEN(aReportData[RP_GROUPS]) + 1, ;
LEN(aReportData[RP_COLUMNS]) )
// Column total elements
FOR nCol := 1 TO LEN(aReportData[RP_COLUMNS])
IF aReportData[RP_COLUMNS,nCol,RC_TOTAL]
FOR nGroup := 1 TO LEN(aReportTotals)
aReportTotals[nGroup,nCol] := 0
NEXT
ENDIF
NEXT
// Initialize aGroupTotals as an array
aGroupTotals := ARRAY( LEN(aReportData[RP_GROUPS]) )
// Execute the actual report based on matching records
DBEval( { || ExecuteReport() }, bFor, bWhile, nNext, nRecord, lRest )
// Generate any totals that may have been identified
// Make a pass through all the groups
FOR nGroup := LEN(aReportData[RP_GROUPS]) TO 1 STEP -1
// make sure group has subtotals
lAnySubTotals := .F.
FOR nCol := 1 TO LEN(aReportData[RP_COLUMNS])
IF aReportData[RP_COLUMNS,nCol,RC_TOTAL]
lAnySubTotals := .T.
EXIT // NOTE
ENDIF
NEXT
IF !lAnySubTotals
LOOP // NOTE
ENDIF
// Check to see if we need to eject the page
IF nLinesLeft < 2
EjectPage()
IF aReportData[ RP_PLAIN ]
nLinesLeft := 1000
ELSE
ReportHeader()
ENDIF
ENDIF
// Print the first line
PrintIt( SPACE(aReportData[RP_LMARGIN]) + ;
IF(nGroup==1,"** Subtotal **","* Subsubtotal *") )
// Print the second line
QQOUT( SPACE(aReportData[RP_LMARGIN]) )
FOR nCol := 1 TO LEN(aReportData[RP_COLUMNS])
IF nCol > 1
QQOUT( " " )
ENDIF
IF aReportData[RP_COLUMNS,nCol,RC_TOTAL]
QQOUT( TRANSFORM(aReportTotals[nGroup+1,nCol], ;
aReportData[RP_COLUMNS,nCol,RC_PICT]) )
ELSE
QQOUT( SPACE(aReportData[RP_COLUMNS,nCol,RC_WIDTH]) )
ENDIF
NEXT
// Send a cr/lf for the last line
QOUT()
NEXT
// Generate the "Grand totals"
// Check to see if we need to eject the page
IF nLinesLeft < 2
EjectPage()
IF aReportData[ RP_PLAIN ]
nLinesLeft := 1000
ELSE
ReportHeader()
ENDIF
ENDIF
// Any report totals?
lAnyTotals := .F.
FOR nCol := 1 TO LEN(aReportData[RP_COLUMNS])
IF aReportData[RP_COLUMNS,nCol,RC_TOTAL]
lAnyTotals := .T.
EXIT
ENDIF
NEXT nCol
IF lAnyTotals
// Print the first line
PrintIt( SPACE(aReportData[RP_LMARGIN]) + "*** Total ***" )
// Print the second line
QQOUT( SPACE(aReportData[RP_LMARGIN]) )
FOR nCol := 1 TO LEN(aReportData[RP_COLUMNS])
IF nCol > 1
QQOUT( " " )
ENDIF
IF aReportData[RP_COLUMNS,nCol,RC_TOTAL]
QQOUT( TRANSFORM(aReportTotals[1,nCol], ;
aReportData[RP_COLUMNS,nCol,RC_PICT]) )
ELSE
QQOUT( SPACE(aReportData[RP_COLUMNS,nCol,RC_WIDTH]) )
ENDIF
NEXT nCol
// Send a cr/lf for the last line
QOUT()
ENDIF
// Check to see if an "after report" eject, or TO FILE has been specified
IF aReportData[ RP_AEJECT ]
EjectPage()
ENDIF
RECOVER USING xBreakVal
lBroke := .T.
END SEQUENCE
// Clean up and leave
aReportData := NIL // Recover the space
aReportTotals := NIL
aGroupTotals := NIL
nPageNumber := NIL
lFirstPass := NIL
nLinesLeft := NIL
lFormFeeds := NIL
nMaxLinesAvail := NIL
// clean up
SET( _SET_PRINTER, lPrintOn ) // Set the printer back to prior state
SET( _SET_CONSOLE, lConsoleOn ) // Set the console back to prior state
IF (!Empty(cAltFile)) // Set extrafile back
SET( _SET_EXTRAFILE, cExtraFile )
SET( _SET_EXTRA, lExtraState )
ENDIF
IF lBroke
// keep the break value going
BREAK xBreakVal
END
RETURN
/***
* ExecuteReport()
* Executed by DBEVAL() for each record that matches record scope
*/
STATIC PROCEDURE ExecuteReport
LOCAL aRecordHeader := {} // Header for the current record
LOCAL aRecordToPrint := {} // Current record to print
LOCAL nCol // Counter for the column work
LOCAL nGroup // Counter for the group work
LOCAL lGroupChanged := .F. // Has any group changed?
LOCAL lEjectGrp := .F. // Group eject indicator
LOCAL nMaxLines // Number of lines needed by record
LOCAL nLine // Counter for each record line
LOCAL cLine // Current line of text for parsing
LOCAL nLastElement // Last element pointer if record is
LOCAL lAnySubTotals
// Add to the main column totals
FOR nCol := 1 TO LEN(aReportData[RP_COLUMNS])
IF aReportData[RP_COLUMNS,nCol,RC_TOTAL]
// If this column should be totaled, do it
aReportTotals[ 1 ,nCol] += ;
EVAL( aReportData[RP_COLUMNS,nCol,RC_EXP] )
ENDIF
NEXT
// Determine if any of the groups have changed. If so, add the appropriate
// line to aRecordHeader for totaling out the previous records
IF !lFirstPass // Don't bother first time through
// Make a pass through all the groups
FOR nGroup := LEN(aReportData[RP_GROUPS]) TO 1 STEP -1
// make sure group has subtotals
lAnySubTotals := .F.
FOR nCol := 1 TO LEN(aReportData[RP_COLUMNS])
IF aReportData[RP_COLUMNS,nCol,RC_TOTAL]
lAnySubTotals := .T.
EXIT // NOTE
ENDIF
NEXT
IF !lAnySubTotals
LOOP // NOTE
ENDIF
// If this group has changed since the last record
IF MakeAStr(EVAL(aReportData[RP_GROUPS,nGroup,RG_EXP]),;
aReportData[RP_GROUPS,nGroup,RG_TYPE]) != aGroupTotals[nGroup]
AADD( aRecordHeader, IF(nGroup==1,"** Subtotal **","* Subsubtotal *") )
AADD( aRecordHeader, "" )
// retrieve group eject state from report form
IF ( nGroup == 1 )
lEjectGrp := aReportData[ RP_GROUPS, nGroup, RG_AEJECT ]
ENDIF
// Cycle through the columns, adding either the group
// amount from aReportTotals or spaces wide enough for
// the non-totaled columns
FOR nCol := 1 TO LEN(aReportData[RP_COLUMNS])
IF aReportData[RP_COLUMNS,nCol,RC_TOTAL]
aRecordHeader[ LEN(aRecordHeader) ] += ;
TRANSFORM(aReportTotals[nGroup+1,nCol], ;
aReportData[RP_COLUMNS,nCol,RC_PICT])
// Zero out the group totals column from aReportTotals
aReportTotals[nGroup+1,nCol] := 0
ELSE
aRecordHeader[ LEN(aRecordHeader) ] += ;
SPACE(aReportData[RP_COLUMNS,nCol,RC_WIDTH])
ENDIF
aRecordHeader[ LEN(aRecordHeader) ] += " "
NEXT
// Get rid of the extra space from the last column
aRecordHeader[LEN(aRecordHeader)] := ;
LEFT( aRecordHeader[LEN(aRecordHeader)], ;
LEN(aRecordHeader[LEN(aRecordHeader)]) - 1 )
ENDIF
NEXT
ENDIF
lFirstPass = .F.
IF ( LEN( aRecordHeader ) > 0 ) .AND. lEjectGrp
IF LEN( aRecordHeader ) > nLinesLeft
EjectPage()
IF ( aReportData[ RP_PLAIN ] )
nLinesLeft := 1000
ELSE
ReportHeader()
ENDIF
ENDIF
AEVAL( aRecordHeader, { | HeaderLine | ;
PrintIt( SPACE( aReportData[ RP_LMARGIN ] ) + HeaderLine ) } )
aRecordHeader := {}
EjectPage()
IF ( aReportData[ RP_PLAIN ] )
nLinesLeft := 1000
ELSE
ReportHeader()
ENDIF
ENDIF
// Add to aRecordHeader in the event that the group has changed and
// new group headers need to be generated
// Cycle through the groups
FOR nGroup := 1 TO LEN(aReportData[RP_GROUPS])
// If the group has changed
IF MakeAStr(EVAL(aReportData[RP_GROUPS,nGroup,RG_EXP]),;
aReportData[RP_GROUPS,nGroup,RG_TYPE]) == aGroupTotals[nGroup]
ELSE
AADD( aRecordHeader, "" ) // The blank line
AADD( aRecordHeader, IF(nGroup==1,"** ","* ") +;
aReportData[RP_GROUPS,nGroup,RG_HEADER] + " " +;
MakeAStr(EVAL(aReportData[RP_GROUPS,nGroup,RG_EXP]), ;
aReportData[RP_GROUPS,nGroup,RG_TYPE]) )
ENDIF
NEXT
// Is there anything in the record header?
IF LEN( aRecordHeader ) > 0
// Determine if aRecordHeader will fit on the current page. If not,
// start a new header
IF LEN( aRecordHeader ) > nLinesLeft
EjectPage()
IF aReportData[ RP_PLAIN ]
nLinesLeft := 1000
ELSE
ReportHeader()
ENDIF
ENDIF
// Send aRecordHeader to the output device, resetting nLinesLeft
AEVAL( aRecordHeader, { | HeaderLine | ;
PrintIt( SPACE(aReportData[RP_LMARGIN])+ HeaderLine ) } )
nLinesLeft -= LEN( aRecordHeader )
// Make sure it didn't hit the bottom margin
IF nLinesLeft == 0
EjectPage()
IF aReportData[ RP_PLAIN ]
nLinesLeft := 1000
ELSE
ReportHeader()
ENDIF
ENDIF
ENDIF
// Add to the group totals
FOR nCol := 1 TO LEN(aReportData[RP_COLUMNS])
// If this column should be totaled, do it
IF aReportData[RP_COLUMNS,nCol,RC_TOTAL]
// Cycle through the groups
FOR nGroup := 1 TO LEN( aReportTotals ) - 1
aReportTotals[nGroup+1,nCol] += ;
EVAL( aReportData[RP_COLUMNS,nCol,RC_EXP] )
NEXT
ENDIF
NEXT
// Reset the group expressions in aGroupTotals
FOR nGroup := 1 TO LEN(aReportData[RP_GROUPS])
aGroupTotals[nGroup] := MakeAStr(EVAL(aReportData[RP_GROUPS,nGroup,RG_EXP]),;
aReportData[RP_GROUPS,nGroup,RG_TYPE])
NEXT
// Only run through the record detail if this is NOT a summary report
IF !aReportData[ RP_SUMMARY ]
// Determine the max number of lines needed by each expression
nMaxLines := 1
FOR nCol := 1 TO LEN(aReportData[RP_COLUMNS])
IF aReportData[RP_COLUMNS,nCol,RC_TYPE] $ "CM"
nMaxLines := MAX(XMLCOUNT(EVAL(aReportData[RP_COLUMNS,nCol,RC_EXP]),;
aReportData[RP_COLUMNS,nCol,RC_WIDTH]), nMaxLines)
ENDIF
NEXT
// Size aRecordToPrint to the maximum number of lines it will need, then
// fill it with nulls
ASIZE( aRecordToPrint, nMaxLines )
AFILL( aRecordToPrint, "" )
// Load the current record into aRecordToPrint
FOR nCol := 1 TO LEN(aReportData[RP_COLUMNS])
FOR nLine := 1 TO nMaxLines
// Check to see if it's a memo or character
IF aReportData[RP_COLUMNS,nCol,RC_TYPE] $ "CM"
// Load the current line of the current column into cLine
cLine := XMEMOLINE(TRIM(EVAL(aReportData[RP_COLUMNS,nCol,RC_EXP])),;
aReportData[RP_COLUMNS,nCol,RC_WIDTH], nLine )
cLine := PADR( cLine, aReportData[RP_COLUMNS,nCol,RC_WIDTH] )
ELSE
IF nLine == 1
cLine := TRANSFORM(EVAL(aReportData[RP_COLUMNS,nCol,RC_EXP]),;
aReportData[RP_COLUMNS,nCol,RC_PICT])
cLine := PADR( cLine, aReportData[RP_COLUMNS,nCol,RC_WIDTH] )
ELSE
cLine := SPACE( aReportData[RP_COLUMNS,nCol,RC_WIDTH])
ENDIF
ENDIF
// Add it to the existing report line
IF nCol > 1
aRecordToPrint[ nLine ] += " "
ENDIF
aRecordToPrint[ nLine ] += cLine
NEXT
NEXT
// Determine if aRecordToPrint will fit on the current page
IF LEN( aRecordToPrint ) > nLinesLeft
// The record will not fit on the current page - will it fit on
// a full page? If not, break it up and print it.
IF LEN( aRecordToPrint ) > nMaxLinesAvail
// This record is HUGE! Break it up...
nLine := 1
DO WHILE nLine < LEN( aRecordToPrint )
PrintIt( SPACE(aReportData[RP_LMARGIN]) + aRecordToPrint[nLine] )
nLine++
nLinesLeft--
IF nLinesLeft == 0
EjectPage()
IF aReportData[ RP_PLAIN ]
nLinesLeft := 1000
ELSE
ReportHeader()
ENDIF
ENDIF
ENDDO
ELSE
EjectPage()
IF aReportData[ RP_PLAIN ]
nLinesLeft := 1000
ELSE
ReportHeader()
ENDIF
AEVAL( aRecordToPrint, ;
{ | RecordLine | ;
PrintIt( SPACE(aReportData[RP_LMARGIN])+ RecordLine ) ;
} ;
)
nLinesLeft -= LEN( aRecordToPrint )
ENDIF
ELSE
// Send aRecordToPrint to the output device, resetting nLinesLeft
AEVAL( aRecordToPrint, ;
{ | RecordLine | ;
PrintIt( SPACE(aReportData[RP_LMARGIN])+ RecordLine ) ;
} ;
)
nLinesLeft -= LEN( aRecordToPrint )
ENDIF
// Make sure it didn't hit the bottom margin
IF nLinesLeft == 0
EjectPage()
IF aReportData[ RP_PLAIN ]
nLinesLeft := 1000
ELSE
ReportHeader()
ENDIF
ENDIF
// Tack on the spacing for double/triple/etc.
IF aReportData[ RP_SPACING ] > 1
IF nLinesLeft > aReportData[ RP_SPACING ] - 1
FOR nLine := 2 TO aReportData[ RP_SPACING ]
PrintIt()
nLinesLeft--
NEXT
ENDIF
ENDIF
ENDIF // Was this a summary report?
RETURN
/***
*
* ReportHeader()
*
*/
STATIC PROCEDURE ReportHeader
LOCAL nLinesInHeader := 0
LOCAL aPageHeader := {}
LOCAL nHeadingLength := aReportData[RP_WIDTH] - aReportData[RP_LMARGIN] - 30
LOCAL nCol, nLine, nMaxColLength, nGroup, cHeader
LOCAL nHeadLine // lines in a single heading
LOCAL nRPageSize // width of report after subtracting right margin
LOCAL aTempPgHeader // temporary page header array
nRPageSize := aReportData[RP_WIDTH] - aReportData[RP_RMARGIN]
// Create the header and drop it into aPageHeader
// Start with the heading
IF !aReportData[ RP_PLAIN ] // If not a plain paper report, build
IF aReportData[RP_HEADING] == "" // the heading
AADD( aPageHeader, "Page No." + STR(nPageNumber,6) )
ELSE
aTempPgHeader := ParseHeader( aReportData[ RP_HEADING ], ;
Occurs( ";", aReportData[ RP_HEADING ] ) + 1 )
FOR nLine := 1 TO LEN( aTempPgHeader )
// determine number of lines in header given current report dimensions
nLinesInHeader := MAX( XMLCOUNT( LTRIM( aTempPgHeader[ nLine ] ), ;
nHeadingLength ), 1 )
// extract lines and add to array
FOR nHeadLine := 1 TO nLinesInHeader
AADD( aPageHeader, SPACE( 15 ) + ;
PADC( TRIM( XMEMOLINE( LTRIM( aTempPgHeader[ nLine ] ),;
nHeadingLength, nHeadLine ) ), nHeadingLength ) )
NEXT nHeadLine
NEXT nLine
aPageHeader[ 1 ] := STUFF( aPageHeader[ 1 ], 1, 14, ;
"Page No." + STR(nPageNumber,6) )
ENDIF
AADD( aPageHeader, DTOC(DATE()) )
ENDIF
// Tack on the actual header from the FRM
FOR nLine := 1 TO LEN( aReportData[RP_HEADER] )
// determine number of lines in header given current report dimensions
nLinesInHeader := MAX( XMLCOUNT( LTRIM( aReportData[RP_HEADER, ;
nLine ] ), nRPageSize ), 1 )
// extract lines and add to array
FOR nHeadLine := 1 TO nLinesInHeader
cHeader := TRIM( XMEMOLINE( LTRIM( aReportData[ RP_HEADER, nLine ] ),;
nRPageSize, nHeadLine) )
AADD( aPageHeader, SPACE( ( nRPageSize - aReportData[ RP_LMARGIN ] - ;
LEN( cHeader ) ) / 2 ) + cHeader )
NEXT nHeadLine
NEXT nLine
#ifdef S87_COMPAT
// S87 compat.
AADD( aPageHeader, "" )
#endif
// Now add the column headings
nLinesInHeader := LEN( aPageHeader )
// Determine the longest column header
nMaxColLength := 0
FOR nCol := 1 TO LEN( aReportData[ RP_COLUMNS ] )
nMaxColLength := MAX( LEN(aReportData[RP_COLUMNS,nCol,RC_HEADER]), ;
nMaxColLength )
NEXT
FOR nCol := 1 TO LEN( aReportData[ RP_COLUMNS ] )
ASIZE( aReportData[RP_COLUMNS,nCol,RC_HEADER], nMaxColLength )
NEXT
FOR nLine := 1 TO nMaxColLength
AADD( aPageHeader, "" )
NEXT
FOR nCol := 1 TO LEN(aReportData[RP_COLUMNS]) // Cycle through the columns
FOR nLine := 1 TO nMaxColLength
IF nCol > 1
aPageHeader[ nLinesInHeader + nLine ] += " "
ENDIF
IF aReportData[RP_COLUMNS,nCol,RC_HEADER,nLine] == NIL
aPageHeader[ nLinesInHeader + nLine ] += ;
SPACE( aReportData[RP_COLUMNS,nCol,RC_WIDTH] )
ELSE
IF aReportData[RP_COLUMNS,nCol,RC_TYPE] == "N"
aPageHeader[ nLinesInHeader + nLine ] += ;
PADL(aReportData[RP_COLUMNS,nCol,RC_HEADER,nLine],;
aReportData[RP_COLUMNS,nCol,RC_WIDTH])
ELSE
aPageHeader[ nLinesInHeader + nLine ] += ;
PADR(aReportData[RP_COLUMNS,nCol,RC_HEADER,nLine],;
aReportData[RP_COLUMNS,nCol,RC_WIDTH])
ENDIF
ENDIF
NEXT
NEXT
// Insert the two blank lines between the heading and the actual data
AADD( aPageHeader, "" )
AADD( aPageHeader, "" )
AEVAL( aPageHeader, { | HeaderLine | ;
PrintIt( SPACE(aReportData[RP_LMARGIN])+ HeaderLine ) } )
// Set the page number and number of available lines
nPageNumber++
// adjust the line count to account for Summer '87 behavior
nLinesLeft := aReportData[RP_LINES] - LEN( aPageHeader )
nMaxLinesAvail := aReportData[RP_LINES] - LEN( aPageHeader )
RETURN
/***
* Occurs( <cSearch>, <cTarget> ) --> nCount
* Determine the number of times <cSearch> is found in <cTarget>
*
*/
STATIC FUNCTION Occurs( cSearch, cTarget )
LOCAL nPos, nCount := 0
DO WHILE !EMPTY( cTarget )
IF (nPos := AT( cSearch, cTarget )) != 0
nCount++
cTarget := SUBSTR( cTarget, nPos + 1 )
ELSE
// End of string
cTarget := ""
ENDIF
ENDDO
RETURN nCount
/***
* MakeStr( <exp>, <cType> ) --> value
* Convert a value of any data type into string to add to the group header
*/
STATIC FUNCTION MakeAStr( uVar, cType )
LOCAL cString
DO CASE
CASE UPPER(cType) == "D"
cString := DTOC( uVar )
CASE UPPER(cType) == "L"
cString := IF( uVar, "T", "F" )
CASE UPPER(cType) == "N"
cString := STR( uVar )
CASE UPPER(cType) == "C"
cString := uVar
OTHERWISE
cString := "INVALID EXPRESSION"
ENDCASE
RETURN( cString )
/***
* PrintIt( <cString> )
* Print a string, THEN send a CRLF
*/
STATIC PROCEDURE PrintIt( cString )
IF cString == NIL
cString := ""
ELSE
#ifdef S87_COMPAT
// prevents output of trailing space, also prevents wrapping of some
// lines when sent to screen or 80-column printer. Comment out this
// line for complete Summer 87 compatibility.
//cString := Trim( cString )
#endif
ENDIF
QQOUT( cString )
QOUT()
RETURN
/***
*
* EjectPage()
* Eject a page if the form feed option is set
*
*/
STATIC PROCEDURE EjectPage
IF lFormFeeds
EJECT
ENDIF
RETURN
/***
*
* XMLCOUNT( <cString>, [<nLineLength>], [<nTabSize>],
* [<lWrap>] ) --> nLineCount
*
*/
STATIC FUNCTION XMLCOUNT( cString, nLineLength, nTabSize, lWrap )
// Set defaults if none specified
nLineLength := IF( nLineLength == NIL, 79, nLineLength )
nTabSize := IF( nTabSize == NIL, 4, nTabSize )
lWrap := IF( lWrap == NIL, .T., .F. )
IF nTabSize >= nLineLength
nTabSize := nLineLength - 1
ENDIF
RETURN( MLCOUNT( TRIM(cString), nLineLength, nTabSize, lWrap ) )
/***
*
* XMEMOLINE( <cString>, [<nLineLength>], [<nLineNumber>],
* [<nTabSize>], [<lWrap>] ) --> cLine
*
*/
STATIC FUNCTION XMEMOLINE( cString, nLineLength, nLineNumber, nTabSize, lWrap )
// Set defaults if none specified
nLineLength := IF( nLineLength == NIL, 79, nLineLength )
nLineNumber := IF( nLineNumber == NIL, 1, nLineNumber )
nTabSize := IF( nTabSize == NIL, 4, nTabSize )
lWrap := IF( lWrap == NIL, .T., lWrap )
IF nTabSize >= nLineLength
nTabSize := nLineLength - 1
ENDIF
RETURN( MEMOLINE( cString, nLineLength, nLineNumber, nTabSize, lWrap ) )