IMPLEMENTATION MODULE ListUtils; (* * REPERTOIRE * Release 1.6 * By Charles Bradford and Cole Brecheen * (c) Copyright 1985-1992 PMI * Green Bay, Wisconsin * All rights reserved * (414) 468-6040 * * $Header: D:/logfiles/mods/listutil.mov 1.6 10 Mar 1991 15:35:08 coleb $ * * * For GenList manipulation routines that don't need access to * the internal structure of a GenList. *) IMPORT ByteFiddler; IMPORT EnvironUtils; IMPORT ErrorManager; IMPORT HandleIO; IMPORT GenLists; IMPORT LowLevel; IMPORT M2Strings; IMPORT Numbers; IMPORT NumTypes; IMPORT PosUtils; IMPORT StrConv; IMPORT StrEdit; IMPORT StringIO; IMPORT SYSTEM; IMPORT VStorage; VAR Initialized : BOOLEAN; PROCEDURE Init(); BEGIN IF Initialized THEN RETURN; ELSE Initialized := TRUE; END; ByteFiddler.Init(); EnvironUtils.Init(); ErrorManager.Init(); HandleIO.Init(); GenLists.Init(); LowLevel.Init(); M2Strings.Init(); Numbers.Init(); NumTypes.Init(); PosUtils.Init(); StrConv.Init(); StrEdit.Init(); StringIO.Init(); VStorage.Init(); END Init; PROCEDURE InitResponse(msg : ARRAY OF CHAR); (*All calls to InitResponse are diagnostic. Remove them from finished programs.*) VAR TmpStr : ARRAY [0..79] OF CHAR; BEGIN StrEdit.AssignStr('Uninit. list passed to ', TmpStr); StrEdit.Append(TmpStr, msg); ErrorManager.WARN(TmpStr); END InitResponse; (*$O-*) PROCEDURE CharCount( TheList : GenLists.GenList; StartLine, EndLine: CARDINAL ): LONGINT; VAR tmp1, tmp2: LONGINT; lngth, cnt: CARDINAL; BEGIN tmp2 := NumTypes.L0; lngth := GenLists.ListLength( TheList ); IF EndLine > lngth THEN EndLine := lngth; END; FOR cnt := StartLine TO EndLine DO tmp1 := Numbers.Lc( ElmtSize(TheList, cnt) ); (*Workaround for Logitech LONGINT bug.*) tmp2 := tmp2 + tmp1; END; RETURN tmp2; END CharCount; (*$O=*) PROCEDURE CheckLength(VAR TheList : GenLists.GenList; LeftMargin, LngthLimit : CARDINAL); VAR delimiters : ARRAY [0..15] OF CHAR; tmpsize, cnt, DumType, BreakSpot : CARDINAL; tmpadr: SYSTEM.ADDRESS; BEGIN delimiters := ' -,)]?+'; cnt := 1; WHILE cnt <= GenLists.ListLength( TheList) DO tmpsize := ElmtSize( TheList, cnt ); (* we assign this out here to make it easier to see in the RTD *) IF tmpsize > LngthLimit THEN GenLists.GetElmtAdr( TheList, cnt, tmpadr, tmpsize, DumType ); tmpsize := LowLevel.ScanEQ( tmpsize, 0C, tmpadr ); (*reduce size to real string length*) BreakSpot := PosUtils.BreakPtAdr( tmpadr, tmpsize, LngthLimit, delimiters) + 1; GenLists.ListInsertAdr( LowLevel.AddAddr(tmpadr, BreakSpot), (tmpsize -BreakSpot) + LeftMargin + 1, 0, TheList, cnt + 1 ); (* We can't use StrCode as the type in this ListInsertAdr because we need to reserve room in the list element beyond the null. *) GenLists.ChangeTypeCode( TheList, cnt + 1, GenLists.StrCode ); LowLevel.Fill( LowLevel.AddAddr(tmpadr, BreakSpot), 1, 0C ); (*Make sure there's a null terminator at end of old string.*) IF LeftMargin > 0 THEN GenLists.GetElmtAdr( TheList, cnt + 1, tmpadr, tmpsize, DumType ); LowLevel.ShiftArrayRight( tmpadr, tmpsize, LeftMargin ); LowLevel.Fill( tmpadr, LeftMargin, ' ' ); END; END; INC( cnt ); END; END CheckLength; PROCEDURE IndentationAt( TheList : GenLists.GenList; Line : CARDINAL ): CARDINAL; VAR tmpsize, indent, DumType: CARDINAL; tmpadr: SYSTEM.ADDRESS; BEGIN GenLists.GetElmtAdr( TheList, Line, tmpadr, tmpsize, DumType ); tmpsize := LowLevel.ScanEQ( tmpsize, 0C, tmpadr ); IF tmpsize = 0 THEN RETURN 65535; END; indent := LowLevel.ScanNE(tmpsize, ' ', tmpadr); RETURN indent; END IndentationAt; PROCEDURE ReformPara( VAR TheList : GenLists.GenList; LeftMargin, RightMargin : CARDINAL; VAR StartLine, CursorLine, CursorCol: CARDINAL; VAR LinesAdded, BytesAdded: INTEGER; VAR CursorBytePos: LONGINT ); VAR blank : ARRAY [0..0] OF CHAR; delimiters : ARRAY [0..15] OF CHAR; tmpsize, cnt, indent, DumType, BreakSpot : CARDINAL; tmpadr: SYSTEM.ADDRESS; done : BOOLEAN; BEGIN blank[0] := ' '; delimiters := ' -,)]?+'; cnt := StartLine; LinesAdded := 0; BytesAdded := 0; done := FALSE; WHILE (NOT done) AND (cnt <= GenLists.ListLength( TheList)) DO IF cnt = GenLists.ListLength( TheList ) THEN StartLine := cnt; done := TRUE; ELSE indent := IndentationAt( TheList, cnt + 1 ); END; IF (NOT done) AND (indent = LeftMargin) THEN (* We bring the next line up and append it to this one. Means the reformat stops when we reach a line where indentation <> LeftMargin. *) GenLists.GetElmtAdr( TheList, cnt, tmpadr, tmpsize, DumType ); tmpsize := LowLevel.ScanEQ( tmpsize, 0C, tmpadr ); (*reduce size to real string length*) IF (tmpsize > 0) AND (ByteFiddler.PeekChar( LowLevel.AddAddr( tmpadr, tmpsize - 1 )) # ' ') THEN (* If this line doesn't end with a blank. *) InsertIntoElmt( TheList, cnt, SYSTEM.ADR(blank), 1, 65535 ); (*Append a blank to this line because we're not going to insert leading blanks from the line below.*) INC( BytesAdded ); END; IF CursorLine > (cnt + 1) THEN DEC( CursorLine ); ELSIF (CursorLine = (cnt + 1)) THEN DEC( CursorLine ); CursorCol := CursorCol + ElmtSize(TheList, cnt); END; GenLists.GetElmtAdr( TheList, cnt + 1, tmpadr, tmpsize, DumType ); InsertIntoElmt( TheList, cnt, LowLevel.AddAddr(tmpadr, indent), ElmtSize(TheList, cnt + 1) - indent, 65535 ); BytesAdded := BytesAdded - INTEGER(indent) - 2; (* We've deleted leading spaces and separating CR/LF. *) GenLists.ListDelete( TheList, cnt + 1, 1 ); DEC( LinesAdded ); ELSE done := TRUE; END; GenLists.GetElmtAdr( TheList, cnt, tmpadr, tmpsize, DumType ); tmpsize := LowLevel.ScanEQ( tmpsize, 0C, tmpadr ); (*reduce size to real string length*) IF (tmpsize > RightMargin) THEN done := FALSE; BreakSpot := PosUtils.BreakPtAdr( tmpadr, tmpsize, RightMargin, delimiters) + 1; IF CursorLine > cnt THEN INC( CursorLine ); ELSIF (CursorLine = cnt) AND (CursorCol >= BreakSpot) THEN INC( CursorLine ); CursorCol := CursorCol - BreakSpot; END; GenLists.ListInsertAdr( LowLevel.AddAddr(tmpadr, BreakSpot), (tmpsize -BreakSpot) + LeftMargin + 1, 0, TheList, cnt + 1 ); (* We can't use StrCode as the type in this ListInsertAdr because we need to reserve room in the list element beyond the null. *) INC( LinesAdded ); GenLists.ChangeTypeCode( TheList, cnt + 1, GenLists.StrCode ); LowLevel.Fill( LowLevel.AddAddr(tmpadr, BreakSpot), 1, 0C ); (*Make sure there's a null terminator at end of old string.*) IF LeftMargin > 0 THEN GenLists.GetElmtAdr( TheList, cnt + 1, tmpadr, tmpsize, DumType ); LowLevel.ShiftArrayRight( tmpadr, tmpsize, LeftMargin ); LowLevel.Fill( tmpadr, LeftMargin, ' ' ); IF CursorLine = (cnt + 1) THEN CursorCol := CursorCol + LeftMargin; END; BytesAdded := BytesAdded + INTEGER(LeftMargin) + 2 ; (* We've added leading spaces on new line plus CR/LF on this one. *) ELSE BytesAdded := BytesAdded + 2; (* We've added only the CR/LF. *) END; END; INC( cnt ); END; IF (CursorBytePos > NumTypes.L0) THEN (* They want us to compute new byte position. *) IF CursorLine > 1 THEN CursorBytePos := CharCount( TheList, 1, CursorLine - 1 ) + Numbers.Lc( CursorCol ); ELSE CursorBytePos := Numbers.Lc( CursorCol ); END; END; StartLine := cnt; END ReformPara; PROCEDURE DeTabList( VAR TheList: GenLists.GenList; VAR ListChanged: BOOLEAN ); VAR TmpStr: ARRAY [0..255] OF CHAR; ListEnd, StartingSpot, StrSpot: CARDINAL; TabStr: ARRAY [0..0] OF CHAR; BEGIN TabStr[0] := 11C; ListEnd := GenLists.ListLength( TheList ); StartingSpot := 1; ListChanged := FALSE; WHILE (StartingSpot <= ListEnd) AND (StartingSpot # 0) DO StartingSpot := GenLists.ScanList( TabStr, TheList, StartingSpot, ListEnd, StrSpot ); IF StartingSpot > 0 THEN ListChanged := TRUE; GetStr( TheList, StartingSpot, TmpStr ); StrEdit.CutTrailingChars( ' ', TmpStr ); StrEdit.ReplaceTabs( TmpStr, 8 ); GenLists.ListReplace( TmpStr, GenLists.StrCode, TheList, StartingSpot ); END; END; END DeTabList; PROCEDURE EnTabList( VAR TheList: GenLists.GenList; LeadingOnly: BOOLEAN; VAR ListChanged: BOOLEAN ); VAR TmpStr: ARRAY [0..255] OF CHAR; ListEnd, StartingSpot, StrSpot: CARDINAL; BlankStr: ARRAY [0..8] OF CHAR; BEGIN BlankStr := ' '; ListEnd := GenLists.ListLength( TheList ); StartingSpot := 1; ListChanged := FALSE; WHILE (StartingSpot <= ListEnd) AND (StartingSpot # 0) DO StartingSpot := GenLists.ScanList( BlankStr, TheList, StartingSpot, ListEnd, StrSpot ); IF StartingSpot > 0 THEN ListChanged := TRUE; GetStr( TheList, StartingSpot, TmpStr ); StrEdit.CutTrailingChars( ' ', TmpStr ); StrEdit.InsertTabs( TmpStr, 8, LeadingOnly ); GenLists.ListReplace( TmpStr, GenLists.StrCode, TheList, StartingSpot ); END; END; END EnTabList; PROCEDURE ElmtSize( TheList: GenLists.GenList; spot: CARDINAL ): CARDINAL; VAR TheAddr: SYSTEM.ADDRESS; TheSize, TypeCode: CARDINAL; BEGIN GenLists.GetElmtAdr( TheList, spot, TheAddr, TheSize, TypeCode ); IF TypeCode = GenLists.StrCode THEN TheSize := LowLevel.ScanEQ( TheSize, 0C, TheAddr ); END; RETURN TheSize; END ElmtSize; PROCEDURE GetStr( TheList: GenLists.GenList; spot: CARDINAL; VAR TheStr: ARRAY OF CHAR ); VAR TypeCode: CARDINAL; BEGIN GenLists.GetElmt( TheList, spot, TheStr, TypeCode ); IF TypeCode # GenLists.StrCode THEN ErrorManager.WARN('Not a string'); END; END GetStr; PROCEDURE HandleToList( TheHandle : VStorage.MemHandle; BlockSize : CARDINAL; delimiter1, delimiter2 : ARRAY OF CHAR; RecSize, TypeCode : CARDINAL; VAR TheList : GenLists.GenList); VAR BytesDone, Delim1Len, Delim2Len : CARDINAL; TwoDelimiters, FixedLengthRecs : BOOLEAN; PROCEDURE AnElmtStartsHere( OffSet: CARDINAL ): BOOLEAN; VAR TmpAdr: SYSTEM.ADDRESS; BEGIN IF FixedLengthRecs OR (NOT TwoDelimiters) THEN RETURN FALSE; END; TmpAdr := VStorage.LockMem( TheHandle ); VStorage.UnLockMem( TheHandle ); LowLevel.IncAddr( TmpAdr, OffSet ); IF (BytesDone >= BlockSize) OR (0 = PosUtils.PosAdr( delimiter1, TmpAdr, BlockSize - BytesDone)) THEN RETURN TRUE; END; RETURN FALSE; END AnElmtStartsHere; PROCEDURE AnElmtEndsHere( OffSet: CARDINAL ): BOOLEAN; VAR TmpAdr: SYSTEM.ADDRESS; BEGIN IF FixedLengthRecs OR (NOT TwoDelimiters) THEN RETURN FALSE; END; TmpAdr := VStorage.LockMem( TheHandle ); VStorage.UnLockMem( TheHandle ); LowLevel.IncAddr( TmpAdr, OffSet ); IF (BytesDone >= BlockSize) OR (0 = PosUtils.PosAdr( delimiter2, TmpAdr, BlockSize - BytesDone)) THEN RETURN TRUE; END; RETURN FALSE; END AnElmtEndsHere; PROCEDURE SizeOfElement( OldAdr: SYSTEM.ADDRESS ): CARDINAL; (*Returns the address of the first byte following delimiter2, but dist includes only the bytes preceding delimiter2.*) BEGIN IF FixedLengthRecs THEN RETURN RecSize; ELSE RETURN PosUtils.PosAdr( delimiter2, OldAdr, BlockSize-BytesDone); END; END SizeOfElement; PROCEDURE MakeOneList( TheList : GenLists.GenList ); VAR TmpTypeCode, TmpSize: CARDINAL; TmpAdr: SYSTEM.ADDRESS; SubList : GenLists.GenList; BEGIN LOOP IF TwoDelimiters THEN LOOP (*Skip the leading delim1, if there is one, and move at least to the byte just beyond the next delim1.*) IF AnElmtStartsHere(BytesDone) THEN INC( BytesDone, Delim1Len ); EXIT; ELSE INC( BytesDone, Delim1Len ); END; END; END; IF (BytesDone >= BlockSize) THEN EXIT; END; IF (TypeCode < 65000) AND (NOT FixedLengthRecs) AND TwoDelimiters THEN TmpAdr := VStorage.LockMem( TheHandle ); LowLevel.IncAddr( TmpAdr, BytesDone ); LowLevel.Move( TmpAdr, SYSTEM.ADR(TmpTypeCode), 2); VStorage.UnLockMem( TheHandle ); INC( BytesDone, 2 ); ELSE TmpTypeCode := TypeCode; END; IF (TmpTypeCode = GenLists.ListCode) OR AnElmtStartsHere( BytesDone ) THEN GenLists.NewList( SubList ); IF AnElmtEndsHere( BytesDone ) THEN (*Null list.*) INC( BytesDone, Delim2Len ); ELSE MakeOneList( SubList ); END; GenLists.ListInsert( SubList, GenLists.ListCode, TheList, 65535 ); ELSE TmpAdr := VStorage.LockMem( TheHandle ); VStorage.UnLockMem( TheHandle ); LowLevel.IncAddr( TmpAdr, BytesDone ); TmpSize := SizeOfElement( TmpAdr ); GenLists.ListInsertAdr( TmpAdr, TmpSize, TmpTypeCode, TheList, 65535 ); INC( BytesDone, TmpSize + Delim2Len ); END; IF (BytesDone >= BlockSize) OR (AnElmtEndsHere(BytesDone)) THEN (*Increment past the delimiter2 that ends the whole list.*) INC( BytesDone, Delim2Len ); EXIT; END; END; END MakeOneList; BEGIN (*HandleToList*) IF NOT GenLists.Initialized( TheList ) THEN InitResponse( 'HandleToList' ); END; Delim1Len := M2Strings.Length(delimiter1); Delim2Len := M2Strings.Length(delimiter2); TwoDelimiters := FALSE; IF ((Delim1Len>0) OR (Delim2Len>0)) AND (RecSize>0) THEN ErrorManager.WARN("Delim-RecSize conflict"); RETURN; END; FixedLengthRecs := RecSize>0; IF FixedLengthRecs THEN IF ((BlockSize MOD RecSize) # 0) THEN ErrorManager.WARN('Bad Recsize'); RETURN; END; ELSE IF NOT PosUtils.Equal(delimiter1,delimiter2) THEN TwoDelimiters := TRUE; END; END; BytesDone := 0; MakeOneList( TheList ); VStorage.DeallocMem( TheHandle, BlockSize ); END HandleToList; (*$O-*) (*We turn off optimization for this procedure because of a bug in Logitech's v3.0.*) PROCEDURE ListToFileHandle( TheList: GenLists.GenList; TheHandle: CARDINAL; delim1, delim2: ARRAY OF CHAR; InitialNestingLevel: CARDINAL; VAR BytesWritten: LONGINT ): CARDINAL; VAR TmpAdr: SYSTEM.ADDRESS; LstCount, LstLngth, TypeCode, TmpSize, DumRecSz, Delim1Lngth, Delim2Lngth, tmpc: CARDINAL; SubList: GenLists.GenList; NilList, TwoDelimiters: BOOLEAN; SavedMessage: CARDINAL; TmpL, TmpBytesWritten: LONGINT; BEGIN BytesWritten := NumTypes.L0; Delim1Lngth := M2Strings.Length( delim1 ); Delim2Lngth := M2Strings.Length( delim2 ); TwoDelimiters := (Delim1Lngth > 0) AND (Delim2Lngth > 0); (*We begin and end the procedure by bracketing the entire block with a StartSep and EndSep, if the parameter list indicates that they're in use. That's the convention NdxFiles uses to indicate that the block as a whole represents a list.*) IF (Delim1Lngth > 0) AND (InitialNestingLevel = 0) THEN SavedMessage := HandleIO.BlockWrite( TheHandle, SYSTEM.ADR(delim1), Delim1Lngth ); TmpL := Numbers.Lc(Delim1Lngth); (*Necessary because of a bug in Logitech's LONGINT addition.*) BytesWritten := BytesWritten + TmpL; END; IF TwoDelimiters AND (InitialNestingLevel = 0) THEN tmpc := GenLists.ListCode; (*We have to do this because some implementations won't give us the address of a constant.*) SavedMessage := HandleIO.BlockWrite( TheHandle, SYSTEM.ADR(tmpc), SYSTEM.TSIZE(CARDINAL) ); BytesWritten := BytesWritten + Numbers.Lc(SYSTEM.TSIZE(CARDINAL)); END; IF GenLists.Initialized( TheList ) THEN NilList := FALSE; ELSE GenLists.NewList( TheList ); NilList := TRUE; END; GenLists.ListToBlock( TheList, delim1, delim2, TmpAdr, TmpSize, DumRecSz); IF (GenLists.ErrorFlag = GenLists.NoListError) THEN SavedMessage := HandleIO.BlockWrite( TheHandle, TmpAdr, TmpSize ); (*Write the Ndx as a single block.*) VStorage.DosDealloc( TmpAdr, TmpSize); (*Get rid of temporary buffer space used.*) BytesWritten := BytesWritten + Numbers.Lc(TmpSize); IF (Delim2Lngth > 0) AND (InitialNestingLevel = 0) THEN SavedMessage := HandleIO.BlockWrite( TheHandle, SYSTEM.ADR(delim2), Delim2Lngth ); BytesWritten := BytesWritten + Numbers.Lc(Delim2Lngth); END; IF NilList THEN GenLists.DisposeList( TheList ); END; RETURN SavedMessage; END; (*Write the NdxElements one by one if there isn't enough memory to write them as a block.*) LstLngth := GenLists.ListLength( TheList ); LstCount := 1; WHILE LstCount <= LstLngth DO GenLists.GetElmtAdr( TheList, LstCount, TmpAdr, TmpSize, TypeCode ); IF Delim1Lngth > 0 THEN SavedMessage := HandleIO.BlockWrite( TheHandle, SYSTEM.ADR(delim1), Delim1Lngth ); BytesWritten := BytesWritten + Numbers.Lc(Delim1Lngth); END; IF TwoDelimiters THEN SavedMessage := HandleIO.BlockWrite( TheHandle, SYSTEM.ADR(TypeCode), SYSTEM.TSIZE(CARDINAL) ); BytesWritten := BytesWritten + Numbers.Lc(SYSTEM.TSIZE(CARDINAL)); END; IF TypeCode = GenLists.ListCode THEN GenLists.GetChildList( TheList, LstCount, SubList ); SavedMessage := ListToFileHandle( SubList, TheHandle, delim1, delim2, InitialNestingLevel + 1, TmpBytesWritten ); BytesWritten := BytesWritten + TmpBytesWritten; IF SavedMessage # StringIO.NoError THEN IF NilList THEN GenLists.DisposeList( TheList ); END; RETURN SavedMessage; END; ELSE SavedMessage := HandleIO.BlockWrite( TheHandle, TmpAdr, TmpSize ); BytesWritten := BytesWritten + Numbers.Lc( TmpSize ); END; IF Delim2Lngth > 0 THEN SavedMessage := HandleIO.BlockWrite( TheHandle, SYSTEM.ADR(delim2), Delim2Lngth ); BytesWritten := BytesWritten + Numbers.Lc(Delim2Lngth); END; INC(LstCount); END; IF (Delim2Lngth > 0) AND (InitialNestingLevel = 0) THEN SavedMessage := HandleIO.BlockWrite( TheHandle, SYSTEM.ADR(delim2), Delim2Lngth ); BytesWritten := BytesWritten + Numbers.Lc(Delim2Lngth); END; IF NilList THEN GenLists.DisposeList( TheList ); END; RETURN SavedMessage; END ListToFileHandle; (*$O=*) PROCEDURE InsertElmtsOf( List1: GenLists.GenList; StartingAt, NumberOfLines: CARDINAL; VAR List2: GenLists.GenList; InsertionPoint: CARDINAL ); VAR cnt, tmpsize, TypeCode, List2Lngth : CARDINAL; tmpadr: SYSTEM.ADDRESS; BEGIN NumberOfLines := Numbers.Min( NumberOfLines, GenLists.ListLength( List1 ) ); List2Lngth := GenLists.ListLength( List2 ); IF Numbers.Lc(InsertionPoint) + Numbers.Lc( NumberOfLines ) > NumTypes.L65535 THEN InsertionPoint := List2Lngth + 1; END; FOR cnt := 1 TO NumberOfLines DO GenLists.GetElmtAdr( List1, StartingAt + cnt - 1, tmpadr, tmpsize, TypeCode ); GenLists.ListInsertAdr( tmpadr, tmpsize, TypeCode, List2, InsertionPoint + (cnt - 1) ); END; END InsertElmtsOf; PROCEDURE InsertIntoElmt( VAR TheList: GenLists.GenList; ListSpot: CARDINAL; StrAdr: SYSTEM.ADDRESS; StrSize: CARDINAL; ElmtSpot: CARDINAL ); VAR tmpadr1, tmpadr2: SYSTEM.ADDRESS; TypeCode, tmpsize: CARDINAL; BEGIN GenLists.GetElmtAdr( TheList, ListSpot, tmpadr1, tmpsize, TypeCode ); (* Make sure we're inserting into a string. *) IF TypeCode # GenLists.StrCode THEN ErrorManager.WARN('Not a string'); END; tmpsize := LowLevel.ScanEQ( tmpsize, 0C, tmpadr1 ); (* This means you can insert your string at ElmtSpot 65535 and have it neatly appended. *) IF ElmtSpot > tmpsize THEN ElmtSpot := tmpsize; END; VStorage.DosAlloc( tmpadr2, tmpsize + StrSize ); LowLevel.Move( tmpadr1, tmpadr2, ElmtSpot ); LowLevel.Move( StrAdr, LowLevel.AddAddr(tmpadr2, ElmtSpot), StrSize ); LowLevel.Move( LowLevel.AddAddr(tmpadr1, ElmtSpot), LowLevel.AddAddr(tmpadr2, ElmtSpot + StrSize), tmpsize - ElmtSpot ); GenLists.ListReplaceAdr( tmpadr2, tmpsize + StrSize, GenLists.StrCode, TheList, ListSpot ); VStorage.DosDealloc( tmpadr2, tmpsize + StrSize ); END InsertIntoElmt; PROCEDURE IsBlankList( TheList: GenLists.GenList ): BOOLEAN; (*We use this procedure to test Editor fields that have been marked "required."*) VAR SomethingFound: BOOLEAN; lngth, cnt, TypeCode: CARDINAL; LocalStr: ARRAY [0..127] OF CHAR; BEGIN IF NOT GenLists.Initialized(TheList) THEN RETURN TRUE; END; SomethingFound := FALSE; lngth := GenLists.ListLength( TheList ); cnt := 1; WHILE (cnt <= lngth) AND (NOT SomethingFound) DO GenLists.GetElmt( TheList, cnt, LocalStr, TypeCode ); SomethingFound := (TypeCode # GenLists.StrCode) OR (NOT PosUtils.IsBlank( LocalStr )); INC(cnt); END; RETURN NOT SomethingFound; END IsBlankList; PROCEDURE ListToString( TheList: GenLists.GenList; VAR TheStr: ARRAY OF CHAR); VAR tmpstr: ARRAY [0..255] OF CHAR; lngth, TypeCode, cnt: CARDINAL; BEGIN lngth := GenLists.ListLength( TheList ); StrEdit.SetLength( TheStr, 0 ); FOR cnt := 1 TO lngth DO GenLists.GetElmt( TheList, cnt, tmpstr, TypeCode ); IF TypeCode = GenLists.StrCode THEN StrEdit.Append( TheStr, tmpstr ); END; END; END ListToString; PROCEDURE LongestLine( TheList: GenLists.GenList ): CARDINAL; VAR LstLngth, lngth, cnt, longest: CARDINAL; BEGIN LstLngth := GenLists.ListLength( TheList ); longest := 0; FOR cnt := 1 TO LstLngth DO lngth := ElmtSize( TheList, cnt ); IF lngth > longest THEN longest := lngth; END; END; RETURN longest; END LongestLine; PROCEDURE OverwriteElmt( VAR TheList: GenLists.GenList; ListSpot: CARDINAL; StrAdr: SYSTEM.ADDRESS; StrSize: CARDINAL; ElmtSpot: CARDINAL ); VAR tmpadr1, tmpadr2: SYSTEM.ADDRESS; excess, TypeCode, tmpsize: CARDINAL; BEGIN GenLists.GetElmtAdr( TheList, ListSpot, tmpadr1, tmpsize, TypeCode ); IF TypeCode # GenLists.StrCode THEN ErrorManager.WARN('Not a string'); END; IF tmpsize >= (ElmtSpot + StrSize) THEN LowLevel.Move( StrAdr, LowLevel.AddAddr(tmpadr1, ElmtSpot), StrSize ); ELSE excess := (ElmtSpot + StrSize) - tmpsize; VStorage.DosAlloc( tmpadr2, tmpsize + excess ); LowLevel.Move( tmpadr1, tmpadr2, ElmtSpot ); LowLevel.Move( StrAdr, LowLevel.AddAddr(tmpadr2, ElmtSpot), StrSize ); GenLists.ListReplaceAdr( tmpadr2, tmpsize + excess, GenLists.StrCode, TheList, ListSpot ); VStorage.DosDealloc( tmpadr2, tmpsize + excess ); END; END OverwriteElmt; PROCEDURE ObjectSpot( BinaryObject : ARRAY OF SYSTEM.BYTE; TheList : GenLists.GenList; StartingAt, EndingAt : CARDINAL) : CARDINAL; VAR cnt, ListEnd, ObjSize, TmpSize, TypeCode : CARDINAL; TmpAdr : SYSTEM.ADDRESS; BEGIN (*ObjectSpot*) ListEnd := GenLists.ListLength( TheList ); IF ListEnd < EndingAt THEN EndingAt := ListEnd; END; IF StartingAt > EndingAt THEN RETURN 0; END; ObjSize := HIGH( BinaryObject ) + 1; FOR cnt := StartingAt TO EndingAt DO GenLists.GetElmtAdr( TheList, cnt, TmpAdr, TmpSize, TypeCode ); IF TmpSize = ObjSize THEN IF PosUtils.PatternScan( SYSTEM.ADR(BinaryObject), ObjSize, TmpAdr, TmpSize ) = 0 THEN RETURN cnt; END; END; END; RETURN 0; (*BinaryObject not found between StartingAt and EndingAt.*) END ObjectSpot; PROCEDURE PrintList( fhandle: CARDINAL; TheList : GenLists.GenList; LeftMargin: CARDINAL; Separator: ARRAY OF CHAR ); PROCEDURE PrintRecursive( fhandle: CARDINAL; TheList : GenLists.GenList; Separator: ARRAY OF CHAR; InSubList: BOOLEAN ); VAR SubLeader, TmpStr : ARRAY [0..80] OF CHAR; SubList : GenLists.GenList; TmpSiz, cnt, ListLen, TypeCode, spot : CARDINAL; FirstTime: BOOLEAN; TmpAdr: SYSTEM.ADDRESS; BEGIN IF InSubList THEN StringIO.PrintMessage( HandleIO.FillFile( fhandle, LeftMargin, ' ' ) ); StringIO.WriteStr( fhandle, '{' ); END; FirstTime := TRUE; spot := 1; ListLen := GenLists.ListLength(TheList); IF GenLists.ErrorFlag # GenLists.NoListError THEN ErrorManager.WARN('Uninitialized list?'); END; REPEAT IF spot<=ListLen THEN GenLists.GetElmtAdr( TheList, spot, TmpAdr, TmpSiz, TypeCode ); IF TypeCode=GenLists.ListCode THEN GenLists.GetChildList(TheList, spot, SubList); PrintRecursive( fhandle, SubList, Separator, TRUE); ELSE IF FirstTime THEN FirstTime := FALSE; ELSE StringIO.WriteStr( fhandle, Separator ); END; IF TypeCode = GenLists.StrCode THEN GetStr( TheList, spot, TmpStr ); IF PosUtils.Present( Separator, TmpStr ) THEN StringIO.WriteStr( fhandle, '"' ); StringIO.WriteStr( fhandle, TmpStr ); StringIO.WriteStr( fhandle, '"' ); ELSE StringIO.PrintMessage( HandleIO.FillFile( fhandle, LeftMargin, ' ' ) ); StringIO.WriteStr( fhandle, TmpStr ); END; ELSE StrConv.CardinalToStr( spot, 2, TmpStr ); StringIO.PrintMessage( HandleIO.FillFile( fhandle, LeftMargin, ' ' ) ); StringIO.WriteStr( fhandle, TmpStr ); StringIO.WriteStr( fhandle, ': ' ); StringIO.WriteStr( fhandle, 'Size = ' ); StrConv.CardinalToStr( TmpSiz, 4, TmpStr ); StringIO.WriteStr( fhandle, TmpStr ); StringIO.WriteStr( fhandle, '; Type = ' ); StrConv.CardinalToStr( TypeCode, 0, TmpStr ); StringIO.WriteStr( fhandle, TmpStr ); END; END; END; INC(spot); UNTIL spot>ListLen; IF InSubList THEN StringIO.WriteStr( fhandle, '}' ); END; END PrintRecursive; BEGIN (* PrintList *) PrintRecursive( fhandle, TheList, Separator, FALSE ); (*We don't call PrintList itself recursively because we don't want the CrLf from the WriteEol below to follow embedded lists.*) StringIO.WriteEol( fhandle, '' ); END PrintList; PROCEDURE TextFileToList( VAR TheFile: ARRAY OF CHAR; VAR TheList: GenLists.GenList): CARDINAL; (* finds the file, reads it into the list, closes the file *) VAR SizeLeft, SizeOK, DoSizeL : LONGINT; ResultErr: CARDINAL; NextList: GenLists.GenList; DoSizeC, TheHandl : CARDINAL; GoBack : INTEGER; buffer: SYSTEM.ADDRESS; BEGIN IF NOT GenLists.Initialized( TheList ) THEN InitResponse( 'TextFileToList' ); END; ResultErr := HandleIO.FindFile( TheHandl, TheFile, 'PATH'); IF StringIO.NoError # ResultErr THEN RETURN ResultErr; END; SizeLeft := HandleIO.FileLength( TheHandl); WHILE SizeLeft # Numbers.Lc( 0) DO (* first, get a mem block <= 65000 bytes *) DoSizeC := 65000; IF NOT VStorage.DosAvail( DoSizeC) THEN (* If there's not a 64K chunk available, get as much as there is.*) DoSizeC := EnvironUtils.MemAvail( 100); END; SizeOK := Numbers.Lc( DoSizeC); IF ( SizeLeft > SizeOK) AND ( Numbers.Lc( 64000) > SizeLeft) THEN GenLists.DisposeList( TheList ); ResultErr := HandleIO.CloseHandle( TheHandl); RETURN StringIO.TooLittleMemory; END; IF SizeLeft > SizeOK THEN DoSizeL := SizeOK; ELSE DoSizeL := SizeLeft; END; SizeLeft := SizeLeft - DoSizeL; DoSizeC := Numbers.C( DoSizeL ); IF NOT VStorage.DosAvail( DoSizeC ) THEN ResultErr := HandleIO.CloseHandle( TheHandl); RETURN StringIO.TooLittleMemory; END; VStorage.DosAlloc( buffer, DoSizeC ); ResultErr := HandleIO.BlockRead( TheHandl, buffer, DoSizeC); IF StringIO.NoError # ResultErr THEN ResultErr := HandleIO.CloseHandle( TheHandl); RETURN ResultErr; END; IF SizeLeft # Numbers.Lc( 0) THEN GoBack := LowLevel.ScanEQ( -32000, 12C, LowLevel.AddAddr(buffer, DoSizeC - 1) ); (* to locate a cut-ff line, look for the last line-feed character before the end of the block *) IF GoBack = -32000 THEN (* No LF found in last 32000 bytes of block *) ResultErr := HandleIO.CloseHandle( TheHandl); RETURN StringIO.BadData; END; DoSizeL := Numbers.Lc( -GoBack ); SizeLeft := SizeLeft + DoSizeL; DoSizeL := -DoSizeL; HandleIO.SetFilePtr( TheHandl, HandleIO.FromCurrent, DoSizeL); (* change file position and counters to start from beginning of the line that was cut off *) END; GenLists.NewList( NextList ); GenLists.BlockToList( buffer, DoSizeC, StringIO.CrLf, StringIO.CrLf, 0, GenLists.StrCode, NextList); GenLists.JoinLists( NextList, TheList, 65535); END; ResultErr := HandleIO.CloseHandle( TheHandl); RETURN ResultErr; END TextFileToList; PROCEDURE TextListToFile( VAR TheList: GenLists.GenList; TheFile: ARRAY OF CHAR): CARDINAL; (* creates the file; writes the list to it and closes file *) VAR StrAdr1, StrAdr2: SYSTEM.ADDRESS; StrSize, DumType, SizeL, TheHandl, cnt: CARDINAL; ResultErr: CARDINAL; TmpCrLf: ARRAY [0..2] OF CHAR; BEGIN StrEdit.AssignStr( StringIO.CrLf, TmpCrLf ); ResultErr := HandleIO.CreateFile( TheHandl, TheFile); IF StringIO.NoError # ResultErr THEN RETURN ResultErr; END; SizeL := GenLists.ListLength( TheList); FOR cnt := 1 TO SizeL DO GenLists.GetElmtAdr( TheList, cnt, StrAdr1, StrSize, DumType); StrSize := LowLevel.ScanEQ( StrSize, 0C, StrAdr1 ); (* Reduce StrSize to number of bytes before the null. *) VStorage.DosAlloc( StrAdr2, StrSize + 2 ); LowLevel.Move( StrAdr1, StrAdr2, StrSize ); LowLevel.Move( SYSTEM.ADR(TmpCrLf), LowLevel.AddAddr(StrAdr2, StrSize), 2 ); ResultErr := HandleIO.BlockWrite( TheHandl, StrAdr2, StrSize + 2 ); VStorage.DosDealloc( StrAdr2, StrSize + 2 ); IF StringIO.NoError # ResultErr THEN RETURN ResultErr; END; END; ResultErr := HandleIO.CloseHandle( TheHandl); RETURN ResultErr; END TextListToFile; PROCEDURE TypeCheck( TheList: GenLists.GenList; spot: CARDINAL ): CARDINAL; VAR DumAddr: SYSTEM.ADDRESS; TheSize, TypeCode: CARDINAL; BEGIN GenLists.GetElmtAdr( TheList, spot, DumAddr, TheSize, TypeCode ); RETURN TypeCode; END TypeCheck; BEGIN Initialized := FALSE; Init(); END ListUtils.