-- DataInput.mesa Edited by Sweet, 15-Jan-81 16:11:51 DIRECTORY IODefs: FROM "iodefs" USING [ControlZ, CR, SP, WriteString, TAB], LedgerDefs: FROM "ledgerdefs", StreamDefs: FROM "streamdefs", StringDefs: FROM "stringdefs", SystemDefs: FROM "systemdefs"; DataInput: PROGRAM IMPORTS IODefs, LedgerDefs, StreamDefs, StringDefs, SystemDefs EXPORTS LedgerDefs = BEGIN OPEN IODefs, StringDefs, LedgerDefs; SkipBlanks: PUBLIC PROCEDURE [ss: SubString, notTab: BOOLEAN ← FALSE] = BEGIN DO IF Exhausted[ss] THEN RETURN; SELECT CurrentChar[ss] FROM TAB => IF notTab THEN RETURN ELSE Bump[ss]; SP => Bump[ss]; CR => WarnSS["Unexpected end of line", ss]; ENDCASE => RETURN; ENDLOOP; END; GetNumber: PUBLIC PROCEDURE [ss: SubString, defaultOK: BOOLEAN ← FALSE, default: INTEGER ← 0] RETURNS [val: INTEGER] = BEGIN neg: BOOLEAN ← FALSE; c: CHARACTER; SkipBlanks[ss]; DO IF Exhausted[ss] THEN GO TO noNum; SELECT (c ← CurrentChar[ss]) FROM '- => neg ← TRUE; IN ['0..'9] => EXIT; ENDCASE => GO TO noNum; Bump[ss]; REPEAT noNum => BEGIN IF ~defaultOK THEN WarnSS["Missing number", ss]; RETURN [default]; END; ENDLOOP; val ← 0; WHILE c IN ['0..'9] DO val ← 10*val+(c-'0); Bump[ss]; IF Exhausted[ss] THEN EXIT; c ← CurrentChar[ss]; ENDLOOP; IF neg THEN val ← -val; RETURN END; GetLongNumber: PROCEDURE [ss: SubString, defaultOK: BOOLEAN ← FALSE, default: LONG INTEGER ← 0] RETURNS [val: LONG INTEGER] = BEGIN neg: BOOLEAN ← FALSE; c: CHARACTER; SkipBlanks[ss]; DO IF Exhausted[ss] THEN GO TO noNum; SELECT (c ← CurrentChar[ss]) FROM '- => neg ← TRUE; IN ['0..'9] => EXIT; ENDCASE => GO TO noNum; Bump[ss]; REPEAT noNum => BEGIN IF ~defaultOK THEN WarnSS["Missing number", ss]; RETURN [default]; END; ENDLOOP; val ← 0; WHILE c IN ['0..'9] DO val ← 10*val+(c-'0); Bump[ss]; IF Exhausted[ss] THEN EXIT; c ← CurrentChar[ss]; ENDLOOP; IF neg THEN val ← -val; RETURN END; GetMoney: PUBLIC PROCEDURE [ss: SubString] RETURNS [val: Money, deductable: BOOLEAN] = BEGIN neg, brkt: BOOLEAN ← FALSE; SkipBlanks[ss]; IF CurrentChar[ss] = '< THEN BEGIN neg ← brkt ← TRUE; Bump[ss] END; IF CurrentChar[ss] = '- THEN BEGIN neg ← TRUE; Bump[ss]; END; val ← IF CurrentChar[ss] = '. THEN 0 ELSE GetLongNumber[ss]*100; IF CurrentChar[ss] = '. THEN BEGIN Bump[ss]; val ← val + GetLongNumber[ss, TRUE, 0]; END; IF brkt THEN IF CurrentChar[ss] # '> THEN WarnSS["Missing <",ss] ELSE Bump[ss]; IF neg THEN val ← -val; SELECT CurrentChar[ss] FROM 'T, 't => BEGIN Bump[ss]; deductable ← TRUE; END; ENDCASE => deductable ← FALSE; RETURN END; defaultYear: [60..99] ← 60; yearDefaultable: BOOLEAN ← FALSE; lastDate: PUBLIC SmallDate ← NullDate; GetDate: PROCEDURE [ss: SubString] RETURNS [dd: SmallDate] = BEGIN m: CARDINAL; SkipBlanks[ss, TRUE]; IF CurrentChar[ss] = TAB THEN IF lastDate = NullDate THEN WarnSS["Invalid date",ss] ELSE RETURN [lastDate]; dd.cv ← 0; m ← GetNumber[ss]; IF CurrentChar[ss] # '/ THEN IF lastDate = NullDate THEN WarnSS["Invalid date",ss] ELSE {lastDate.day ← m; dd ← lastDate; RETURN}; dd.month ← m; Bump[ss]; dd.day ← GetNumber[ss]; IF CurrentChar[ss] = '/ THEN BEGIN Bump[ss]; dd.year ← GetNumber[ss, yearDefaultable, defaultYear]; END ELSE IF yearDefaultable THEN dd.year ← defaultYear ELSE WarnSS["Invalid date",ss]; lastDate ← dd; RETURN END; lastCheck: PUBLIC CARDINAL ← NullCheck; GetCheck: PROC [ss: SubString] RETURNS [CARDINAL] = BEGIN SkipBlanks[ss, TRUE]; IF CurrentChar[ss] = TAB THEN IF lastCheck = NullCheck THEN WarnSS["Missing Check number",ss] ELSE RETURN [lastCheck ← lastCheck + 1]; RETURN[lastCheck ← GetNumber[ss]]; END; GetUpTo: PUBLIC PROCEDURE [ss: SubString, stop: CHARACTER] RETURNS [ans: STRING] = BEGIN adesc: SubStringDescriptor ← ss↑; DO IF Exhausted[ss] THEN BEGIN ws: STRING ← [15]; AppendString[ws, "Missing "]; IF stop = IODefs.TAB THEN AppendString[ws, "<TAB>"] ELSE AppendChar[ws, stop]; WarnSS[ws, ss]; END; IF CurrentChar[ss] = stop THEN EXIT; Bump[ss]; ENDLOOP; adesc.length ← ss.offset - adesc.offset; ans ← SystemDefs.AllocateHeapString[adesc.length]; AppendSubString[ans, @adesc]; END; GetCategory: PUBLIC PROCEDURE [ss: SubString] RETURNS [col: Column, note: STRING] = BEGIN key: STRING ← [10]; c: CHARACTER; SkipBlanks[ss]; IF Exhausted[ss] THEN WarnSS["Missing Category", ss]; DO IF Exhausted[ss] THEN EXIT; SELECT (c ← CurrentChar[ss]) FROM IN ['a..'z], IN ['A..'Z] => StringDefs.AppendChar[key, c]; ENDCASE => EXIT; Bump[ss]; ENDLOOP; FOR col IN Column DO IF StringDefs.EquivalentString[categoryKey[col], key] THEN EXIT; REPEAT FINISHED => WarnSS["Invalid category", ss]; ENDLOOP; SkipBlanks[ss]; IF ~Exhausted[ss] AND CurrentChar[ss] = '( THEN BEGIN Bump[ss]; note ← GetUpTo[ss, ')]; Bump[ss]; END ELSE note ← NIL; END; dateFirst: PUBLIC BOOLEAN; instring: STRING; InputLine: PUBLIC PROCEDURE [p: POINTER] RETURNS [CARDINAL] = BEGIN sip: POINTER TO SortItem = p; adesc: StringDefs.SubStringDescriptor ← [base: instring, offset: NULL, length: 1]; ss: StringDefs.SubString = @adesc; DO ENABLE BadData => LOOP; GetLine[instring]; IF EndOfFile AND instring.length = 0 THEN BEGIN IODefs.WriteString["Sorting..."]; RETURN[0]; END; ss.offset ← 0; SkipBlanks[ss, TRUE]; IF Exhausted[ss] THEN LOOP; SELECT CurrentChar[ss] FROM TAB, IN ['0..'9] => BEGIN IF dateFirst THEN BEGIN sip.date ← GetDate[ss]; SkipBlanks[ss, TRUE]; IF CurrentChar[ss] # TAB THEN WarnSS["missing TAB", ss]; Bump[ss]; sip.check ← GetCheck[ss]; END ELSE BEGIN sip.check ← GetCheck[ss]; SkipBlanks[ss, TRUE]; IF CurrentChar[ss] # TAB THEN WarnSS["missing TAB", ss]; Bump[ss]; sip.date ← GetDate[ss]; END; SkipBlanks[ss]; ss.length ← instring.length - ss.offset; sip.string ← [length: 0, maxlength: ss.length, text:]; StringDefs.AppendSubString[@sip.string, ss]; RETURN [SIZE[SortItem]+(ss.length+1)/2] END; 'y, 'Y => BEGIN SkipToken[ss]; yearDefaultable ← TRUE; defaultYear ← GetNumber[ss]; END; 'd, 'D => dateFirst ← TRUE; 'c, 'C => dateFirst ← FALSE; 't, 'T => BEGIN col: Column; val: Money; SkipToken[ss]; DO val ← GetMoney[ss].val; col ← GetCategory[ss].col; ytdTotal[col] ← val; SkipBlanks[ss]; IF Exhausted[ss] THEN EXIT; IF CurrentChar[ss] # '/ THEN WarnSS["Expected /", ss]; Bump[ss]; ENDLOOP; END; IODefs.ControlZ => NULL; -- ignore lines that start with ↑Z ENDCASE => WarnSS["Bad input data",ss]; ENDLOOP; END; SkipToken: PROCEDURE [ss: StringDefs.SubString] = BEGIN DO Bump[ss]; SELECT CurrentChar[ss] FROM SP, TAB => EXIT; CR => WarnSS["Unexpected end of line",ss]; ENDCASE => NULL; ENDLOOP; END; EndOfFile: PUBLIC BOOLEAN; instream: PUBLIC StreamDefs.StreamHandle ← NIL; GetLine: PROCEDURE [instring: STRING] = BEGIN ch: CHARACTER; instring.length ← 0; IF EndOfFile THEN RETURN; DO ch ← instream.get[instream !StreamDefs.StreamError => IF error = StreamAccess THEN BEGIN EndOfFile ← TRUE; EXIT END]; IF ch = CR THEN EXIT; AppendChar[instring, ch]; ENDLOOP; RETURN END; instring ← SystemDefs.AllocateResidentPages[2]; instring↑ ← [length: 0, maxlength: (512-2)*2, text: NULL]; END.