Found this old sample program written in Turbo Pascal 6. My recollection is that it would process a file received from a factory in Italy and print a report. Notice the first line of code for declaring the amount of memory? Umm…those are BYTES in the first line, as in minimum of 8K, maximum of 384K
{$M 8192, 0, 384000}
{************************************************************
Program: ITALYSUM
Purpose: Print a list of styles with a summary by style
Author : John H. Watson III
Compile: Borland Turbo Pascal 6.0
Changed: 11/09/92 John Watson
Use extended syntax on WriteLn to format numeric
output right-adjusted. (e.g. "PairsNum:5"
instead of just "PairsNum").
Changed: 11/15/92 John Watson
Added style description to detail & summary print.
Removed hardcoding of field lengths in ProcessData
and replaced with constants.
Replaced hardcoded page titles with constants.
Change test of result of collection from "> 0"
to "<> -1". Zero is a valid location in the
collection and IndexOf will return a -1 if the
search doesn't find anything.
************************************************************}
PROGRAM ITALYSUM;
USES Dos, Printer, Objects, StdPrint;
CONST
CopyrightText = 'Copyright (c) 1992 by John H. Watson III';
DetailTitle : String = 'Italy Data File Listing - Detail';
SummaryTitle : String = 'Italy Data File Listing - Summary';
VAR
CopyrightStr : String[40];
CONST
MaxLines = 60;
Pagesize = 66;
{********************************************************************
Define Objects
********************************************************************}
TYPE
pSummary = ^TSummary;
TSummary = OBJECT(TObject)
Pairs : Integer;
Style : String[10];
SDesc : String[30];
CONSTRUCTOR Init(iPairs: Integer; iStyle: String; iSDesc: String);
PROCEDURE Print;
PROCEDURE Accumulate(iPairs: Integer);
END;
PStyleSummary = ^TStyleSummary;
TStyleSummary = OBJECT(TSortedCollection)
FUNCTION KeyOf(Item: Pointer): Pointer; VIRTUAL;
FUNCTION Compare(Key1, Key2: Pointer): Integer; VIRTUAL;
END;
{********************************************************************
Global Variables
********************************************************************}
VAR
ErrorCode : Integer;
F : File;
GrandTotal : Longint;
Path : PathStr;
SummaryList : pStyleSummary; { define global instance }
VAR
FTime : Longint; { file time }
FDT : DateTime; { DOS' date/time record structure }
{********************************************************************
TSummary Object Methods
********************************************************************}
CONSTRUCTOR TSummary.Init(iPairs: Integer; iStyle: String; iSDesc: String);
BEGIN
TObject.Init; { call ancestor's init first }
Pairs := iPairs;
Style := iStyle;
SDesc := iSDesc;
END;
PROCEDURE TSummary.Accumulate(iPairs: Integer);
BEGIN
Pairs := Pairs + iPairs;
END;
PROCEDURE TSummary.Print;
BEGIN
WRITELN(LST,Pairs:5, ' ', Style, ' ', SDesc);
END;
{********************************************************************
TStyleSummary Methods
********************************************************************}
{ Define the key field for the collection. Since KeyOf expects
a pointer and Style is a simple string we must use the "@"
(address of) operator to generate the address (pointer). If
Style was a pString, this would not be necessary. }
FUNCTION TStyleSummary.KeyOf(Item: Pointer): Pointer;
BEGIN
KeyOf := @pSummary(Item)^.Style;
END;
FUNCTION TStyleSummary.Compare(Key1, Key2: Pointer): Integer;
BEGIN
IF pString(Key1)^ = pString(Key2)^ THEN
Compare := 0
ELSE IF pString(Key1)^ < pString(Key2)^ THEN
Compare := -1
ELSE
Compare := +1;
END;
{********************************************************************
Miscellaneous Procedures and Functions
********************************************************************}
FUNCTION OpenFile(VAR F:File; iName:PathStr): BOOLEAN;
BEGIN
ASSIGN(F, iName);
{$I-} RESET(F, 1); {$I+}
OpenFile := IOResult = 0;
END {Open};
PROCEDURE Initialize;
VAR
Dir : DirStr;
Name : NameStr;
Extn : ExtStr;
BEGIN
CopyrightStr := CopyrightText;
IF ParamCount = 0
THEN Path := 'ITALYDTA'
ELSE Path := ParamStr(1);
Path := FExpand(Path); { expand into full path string }
FSplit(Path, Dir, Name, Extn); { split into components }
IF (Name = '') OR NOT OpenFile(F, Path) THEN BEGIN
WRITELN('ERROR: File not found (', Name, ').');
HALT(1);
END;
GetFTime(F, FTime);
UnpackTime(FTime, FDT);
GrandTotal := 0;
LineCount := PageSize;
SummaryList := New(pStyleSummary, Init(200, 50)); { create collection }
END {Initialize};
PROCEDURE Terminate;
BEGIN
Close(F);
Close(LST);
Dispose(SummaryList, Done);
END;
PROCEDURE PrintHeading(iTitle: String);
BEGIN
IF NOT FirstPrint THEN EjectPage;
VerticalTab(1);
WriteLn(LST, iTitle);
VerticalTab(1);
IF FirstPrint THEN BEGIN
WriteLn(LST, 'File: ', Path);
WriteLn(LST, 'Date: ',FDT.Month,'/',FDT.Day,'/',FDT.Year,
' Time: ',FDT.Hour,':',FDT.Min);
VerticalTab(1);
END;
IF FirstPrint
THEN LineCount := 6
ELSE LineCount := LineCount + 3;
FirstPrint := False;
END;
{ Use ForEach iterator to display Summary information }
PROCEDURE PrintSummary;
PROCEDURE PrintSummaryObject(p : pSummary); FAR;
BEGIN
IF LineCount >= MaxLines THEN BEGIN
PrintHeading(SummaryTitle);
WriteLn(LST, 'Pairs Style Description');
WriteLn(LST, '----- ---------- -----------');
VerticalTab(1);
LineCount := LineCount + 3;
END;
p^.Print; { Call object's print method }
GrandTotal := GrandTotal + p^.Pairs;
LineCount := LineCount + 1;
END;
BEGIN
LineCount := MaxLines; { force initial headings to print }
SummaryList^.ForEach(@PrintSummaryObject);
VerticalTab(1);
WriteLn(LST, GrandTotal:5, ' Grand Total');
EjectPage;
END;
{ This procedure will read enough bytes to fill a record buffer.
Since the record length exceeds 255 bytes, we cannot use ReadLn to
read a record. To overcome this, the file is processed as an untyped
file and BlockRead is used to fill the buffer. }
PROCEDURE ProcessData;
CONST
PairsPos : Integer = 578; PairsLen : Integer = 5;
OrdNoPos : Integer = 2; OrdNoLen : Integer = 9;
RegNoPos : Integer = 131; RegNoLen : Integer = 6;
SDescPos : Integer = 588; SDescLen : Integer = 30;
StylePos : Integer = 313; StyleLen : Integer = 10;
VAR
Buffer : ARRAY[1..905] OF Char; { includes two extra bytes for CR/LF }
BytesRead : Word;
Location : Integer; { location in collection of existing summary object }
NewObj, OldObj : pSummary;
PairsNum : Integer;
PairsStr : String[5];
OrdNo : String[9];
RegNo : String[6];
Style : String[10];
SDesc : String[30];
BEGIN
BlockRead(F, Buffer, SizeOf(Buffer), BytesRead);
WHILE (NOT EOF(F)) DO BEGIN
{ Extract individual fields from record buffer }
Move(Buffer[StylePos], Style[1], StyleLen);
Style[0] := Char(StyleLen); { force the proper string size }
Move(Buffer[SDescPos], SDesc[1], SDescLen);
SDesc[0] := Char(SDescLen);
Move(Buffer[OrdNoPos], OrdNo[1], OrdNoLen);
OrdNo[0] := Char(OrdNoLen);
Move(Buffer[RegNoPos], RegNo[1], RegNoLen);
RegNo[0] := Char(RegNoLen);
Move(Buffer[PairsPos], PairsStr[1], PairsLen);
PairsStr[0] := Char(PairsLen);
Val(PairsStr, PairsNum, ErrorCode); { convert string to numeric }
{ Print detail line }
IF LineCount >= MaxLines THEN BEGIN
PrintHeading(DetailTitle);
WriteLn(LST, 'Order No Reg No Pairs Style Description');
WriteLn(LST, '--------- ------ ----- ---------- -----------');
VerticalTab(1);
LineCount := LineCount + 3;
END;
WRITELN(LST, OrdNo, ' ', RegNo, ' ', PairsNum:5, ' ', Style, ' ', SDesc);
LineCount := LineCount + 1;
{ Now create a new instance of a summary object with these values }
NewObj := New(pSummary, Init(PairsNum, Style, SDesc));
{ Search the collection to see if it already exists }
Location := SummaryList^.IndexOf(NewObj);
IF Location <> -1 THEN BEGIN { If found, accumulate pairs }
OldObj := SummaryList^.AT(Location); { get ptr to obj @ location }
OldObj^.Accumulate(PairsNum); { call that obj's accumulate method }
END
ELSE SummaryList^.Insert(NewObj); { else add to collection }
BlockRead(F, Buffer, SizeOf(Buffer), BytesRead);
END;
END {ProcessData};
BEGIN
Initialize;
ProcessData;
PrintSummary;
Terminate;
END.