IMPLEMENTATION MODULE StrConv; (* * 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/strconv.mov 1.7 10 Mar 1991 15:33:12 coleb $ * *) IMPORT ErrorManager; IMPORT Key; IMPORT LowLevel; IMPORT M2Strings; IMPORT Numbers; IMPORT NumTypes; IMPORT PosUtils; IMPORT StrEdit; IMPORT StringIO; IMPORT SYSTEM; VAR Initialized : BOOLEAN; CONST blank = ' '; VAR ten, zero, one : NumTypes.Real8; PROCEDURE StrToReal(TheStr : ARRAY OF CHAR; StartingSpot : CARDINAL; VAR TheReal : NumTypes.Real8): BOOLEAN; CONST ten = 10.0; one = 1.0; zero = 0.0; digitsize = 16; OrdZero = 48; VAR digit : ARRAY [1..digitsize] OF INTEGER; expstr : ARRAY [0..24] OF CHAR; temp, WholeNum, r, fac : NumTypes.Real8; scalesign, negative : BOOLEAN; scale, DigitsRightOfDec, exp : INTEGER; NumOfBlanks, Zeroes, DecPt, EPos : INTEGER; lngth, limit, spot, DigitArrayIndex, j : INTEGER; BEGIN TheReal := 0.0; exp := 0; DigitsRightOfDec := 0; lngth := M2Strings.Length(TheStr); spot := StartingSpot; negative := FALSE; WHILE (spot'9')) DO IF TheStr[spot]='-' THEN negative := NOT negative; ELSIF TheStr[spot]='+' THEN negative := FALSE; END; INC(spot); END; IF (spot>=lngth) THEN RETURN FALSE; END; DigitArrayIndex := 1; WHILE (spot307 THEN RETURN FALSE; END; IF TheReal # zero THEN FOR spot := 0 TO 8 DO IF CARDINAL(spot) IN BITSET(scale) THEN IF scalesign THEN TheReal := TheReal / NumTypes.R8TenPowers[spot]; ELSE TheReal := TheReal * NumTypes.R8TenPowers[spot]; END; END; END; END; END; RETURN TRUE; END StrToReal; PROCEDURE AppendDecoded(VAR TheStr : ARRAY OF CHAR; TheCodeChar : CHAR; StrConst : ARRAY OF CHAR); VAR CharNum, spot1, spot2 : CARDINAL; TmpStr : ARRAY [0..255] OF CHAR; tmpchar : CHAR; BEGIN spot2 := M2Strings.Pos(TheCodeChar,StrConst); spot1 := 0; WHILE spot2<=HIGH(StrConst) DO IF spot2>spot1 THEN M2Strings.Copy(StrConst, spot1, spot2-spot1, TmpStr); StrEdit.Append(TheStr, TmpStr); (* Copies everything from the start of StrConst, or from the end of the last CharCode, into TheStr. *) END; IF NOT StrToCardinal(StrConst,spot2+1,CharNum) THEN ErrorManager.WARN('Invalid string in AppendDecoded'); END; tmpchar := CHR(CharNum); StrEdit.Append(TheStr, tmpchar); spot1 := spot2+4; spot2 := PosUtils.Positn(TheCodeChar,StrConst,spot2+1); END; IF spot1< M2Strings.Length(StrConst) THEN M2Strings.Copy(StrConst, spot1, M2Strings.Length(StrConst)-spot1, TmpStr); StrEdit.Append(TheStr, TmpStr); END; END AppendDecoded; PROCEDURE CardinalToStr(TheNum : CARDINAL; width : CARDINAL; VAR TheStr : ARRAY OF CHAR); VAR spot : INTEGER; tmp : ARRAY [0..80] OF CHAR; BEGIN LowLevel.Fill(SYSTEM.ADR(TheStr), 1+HIGH(TheStr), blank); (*Fill TheStr with spaces.*) spot := HIGH(TheStr); REPEAT TheStr[spot] := CHR((TheNum MOD 10)+48); TheNum := TheNum DIV 10; DEC(spot); UNTIL (TheNum<10) OR (spot<=0); IF TheNum#0 THEN TheStr[spot] := CHR(TheNum+48); END; IF M2Strings.Length(TheStr)>width THEN StrEdit.CutLeadingChars(' ', TheStr); END; IF M2Strings.Length(TheStr)0 THEN (* We do this to preserve the correct width, if possible. *) TheStr[spot-1] := '-'; ELSE M2Strings.Insert('-', TheStr, spot); END; END; END IntegerToStr; TYPE LongAndShort = RECORD CASE : CHAR OF 'A' : longi : LONGINT; | 'B' : locard, hicard : CARDINAL; | 'C' : loint, hiint : INTEGER; END; END; VAR TenPowers : ARRAY [0..9] OF LongAndShort; PROCEDURE LongIntegerToStr(TheNum : LONGINT; width : CARDINAL; VAR TheStr : ARRAY OF CHAR); VAR LongConv : LongAndShort; positive : BOOLEAN; charstr : ARRAY [0..80] OF CHAR; i : CARDINAL; tmpchar: CHAR; BEGIN LongConv.longi := TheNum; IF LongConv.hiint < 0 THEN TheNum := -TheNum; positive := FALSE; ELSE positive := TRUE; END; (* Fill(ADR(charstr), 10, '0'); replaced below *) charstr[10] := 0C; FOR i := 0 TO 9 DO tmpchar := '0'; WHILE NOT (TheNum < TenPowers[i].longi) DO INC( tmpchar); TheNum := TheNum - TenPowers[i].longi; END; charstr[i] := tmpchar; END; StrEdit.CutLeadingChars('0', charstr); IF NOT positive THEN M2Strings.Insert('-', charstr, 0); END; IF charstr[0]=0C THEN charstr[0] := '0'; charstr[1] := 0C; END; M2Strings.Assign(charstr, TheStr); IF M2Strings.Length(TheStr)'9')) DO IF TheStr[StartingSpot]='-' THEN positive := FALSE; END; INC(StartingSpot); END; IF (StartingSpot>=leng) THEN RETURN FALSE; END; WHILE (StartingSpot='0' ) AND (TheStr[StartingSpot]<='9')) DO TheLong := TheLong * ten; dumlong.hicard := 0; dumlong.locard := ORD(TheStr[StartingSpot])-48; TheLong := TheLong + dumlong.longi; INC(StartingSpot); END; IF NOT positive THEN TheLong := -TheLong; END; RETURN TRUE; END StrToLongInteger; (*$O=*) PROCEDURE StrToCardinal(TheStr : ARRAY OF CHAR; StartingSpot : CARDINAL; VAR TheCard : CARDINAL) : BOOLEAN; VAR spot, StrLen : CARDINAL; BEGIN spot := StartingSpot; TheCard := 0; StrLen := M2Strings.Length(TheStr); WHILE ((spot'9'))) DO INC(spot); (*Skips all leading non-numeric stuff.*) END; IF spot>=StrLen THEN RETURN FALSE; END; WHILE (spot='0' ) AND (TheStr[spot]<='9') DO TheCard := TheCard*10; INC(TheCard, ORD(TheStr[spot])-48); INC(spot); END; RETURN TRUE; END StrToCardinal; PROCEDURE StrToInteger(TheStr : ARRAY OF CHAR; StartingSpot : CARDINAL; VAR TheInt : INTEGER) : BOOLEAN; VAR spot, StrLen : CARDINAL; negative : BOOLEAN; BEGIN spot := StartingSpot; TheInt := 0; StrLen := M2Strings.Length(TheStr); negative := FALSE; WHILE ((spot'9'))) DO IF TheStr[spot]='-' THEN negative := NOT negative; (* Means negative will go back to FALSE if there are two leading minus signs; may not be good for much, but why not? *) END; INC(spot); (*Skips all leading non-numeric stuff.*) END; IF spot>=StrLen THEN RETURN FALSE; END; WHILE (spot='0' ) AND (TheStr[spot]<='9') DO TheInt := TheInt*10; INC(TheInt, ORD(TheStr[spot])-48); INC(spot); END; IF negative THEN TheInt := -TheInt; END; RETURN TRUE; END StrToInteger; PROCEDURE DateToStr(month, day, year : CARDINAL; VAR TheStr : ARRAY OF CHAR); VAR DayStr, MnthStr, YrStr : ARRAY [0..15] OF CHAR; BEGIN CardinalToStr(day, 2, DayStr); IF DayStr[0]=' ' THEN DayStr[0] := '0'; END; CardinalToStr(month, 2, MnthStr); CardinalToStr(year, 2, YrStr); IF M2Strings.Length(YrStr)=4 THEN (*Delete the '19' if it is present.*) M2Strings.Delete(YrStr, 0, 2); END; M2Strings.Assign(MnthStr, TheStr); StrEdit.Append(TheStr, '-'); StrEdit.Append(TheStr, DayStr); StrEdit.Append(TheStr, '-'); StrEdit.Append(TheStr, YrStr); END DateToStr; PROCEDURE TimeToStr(hours, minutes, seconds, hundredths : CARDINAL; VAR TheStr : ARRAY OF CHAR); VAR HundrStr, SecStr, MinStr, HourStr : ARRAY [0..20] OF CHAR; pm : BOOLEAN; BEGIN CardinalToStr(hundredths, 2, HundrStr); IF HundrStr[0]=' ' THEN HundrStr[0] := '0'; END; CardinalToStr(seconds, 2, SecStr); IF SecStr[0]=' ' THEN SecStr[0] := '0'; END; CardinalToStr(minutes, 2, MinStr); IF MinStr[0]=' ' THEN MinStr[0] := '0'; END; pm := hours>=12; (* 10 July 87: changed from hours > 12 at suggestion of H. A. Hofmans to correct problem with the first noon hour being called am. *) IF hours>12 THEN DEC(hours, 12); END; CardinalToStr(hours, 2, HourStr); M2Strings.Assign(HourStr, TheStr); StrEdit.Append(TheStr, ':'); StrEdit.Append(TheStr, MinStr); StrEdit.Append(TheStr, ':'); StrEdit.Append(TheStr, SecStr); StrEdit.Append(TheStr, ':'); StrEdit.Append(TheStr, HundrStr); IF pm THEN StrEdit.Append(TheStr, 'p'); ELSE StrEdit.Append(TheStr, 'a'); END; END TimeToStr; PROCEDURE RealToStr( TheNum: NumTypes.Real8; DigitsRightOfDec, width: INTEGER; VAR TheStr: ARRAY OF CHAR); CONST ten = 10.0; one = 1.0; zero = 0.0; VAR spot : CARDINAL; i, exp : INTEGER; expstr : ARRAY [0..6] OF CHAR; tmpstr : ARRAY [0..31] OF CHAR; PROCEDURE PutDigit(VAR r : NumTypes.Real8); VAR dig : LONGINT; d : CARDINAL; WholePart : NumTypes.Real8; BEGIN dig := Numbers.R8ToL(r); d := Numbers.C(dig); tmpstr[spot] := CHR(d+ORD('0')); INC(spot); WholePart := Numbers.LToR8(dig); r := r - WholePart; END PutDigit; BEGIN LowLevel.Fill(SYSTEM.ADR(tmpstr), 1+HIGH(tmpstr), 0C); (*Fill tmpstr with nulls.*) spot := 0; IF TheNum=ten THEN TheNum := TheNum/ten; INC(exp); ELSE EXIT; END; END; END; PutDigit(TheNum); tmpstr[spot] := '.'; INC(spot); FOR i := 1 TO 15 DO TheNum := TheNum*ten; PutDigit(TheNum); END; tmpstr[spot] := 'E'; IntegerToStr(exp, 0, expstr); StrEdit.Append(tmpstr, expstr); IF DigitsRightOfDec < 0 THEN RETURN; END; IF NOT ScientificToPlain( tmpstr, DigitsRightOfDec, width, TheStr ) THEN ErrorManager.WARN('Invalid real'); END; END RealToStr; PROCEDURE ReturnedCard( TheStr: ARRAY OF CHAR ): CARDINAL; VAR tmpc: CARDINAL; BEGIN IF NOT StrToCardinal( TheStr, 0, tmpc ) THEN StrEdit.Append( TheStr, ' # number' ); ErrorManager.WARN( TheStr ); END; RETURN tmpc; END ReturnedCard; PROCEDURE ReturnedInt( TheStr: ARRAY OF CHAR ): INTEGER; VAR tmpi: INTEGER; BEGIN IF NOT StrToInteger( TheStr, 0, tmpi ) THEN StrEdit.Append( TheStr, ' # number' ); ErrorManager.WARN( TheStr ); END; RETURN tmpi; END ReturnedInt; PROCEDURE ScientificToPlain(scientific : ARRAY OF CHAR; DigitsToRight, width : CARDINAL; VAR plain : ARRAY OF CHAR) : BOOLEAN; VAR RoundSpot, lngth, FirstSpot, eSpot, DotSpot, ProperNumOfDigits : CARDINAL; cnt, DecimalShift : INTEGER; tmpstr : ARRAY [0..79] OF CHAR; carrying : BOOLEAN; e, dot, space, zero : ARRAY [0..0] OF CHAR; (* We use these instead of CHAR constants because this procedure was actually written for the ITC version. See CharStuf module chapter for explanation. *) BEGIN e[0] := 'E'; dot[0] := '.'; space[0] := ' '; zero[0] := '0'; eSpot := M2Strings.Pos(e,scientific); IF NOT StrToInteger(scientific, eSpot, DecimalShift) THEN RETURN FALSE; END; M2Strings.Assign(scientific, tmpstr); M2Strings.Delete(tmpstr, eSpot, (M2Strings.Length(tmpstr) - eSpot) + 1); DotSpot := M2Strings.Pos(dot,tmpstr); IF DotSpot>HIGH(tmpstr) THEN RETURN FALSE; ELSE M2Strings.Delete(tmpstr, DotSpot, 1); END; IF tmpstr[0]='-' THEN FirstSpot := 1; ELSE FirstSpot := 0; END; IF DecimalShift>=0 THEN DotSpot := DotSpot + CARDINAL(DecimalShift); ELSE DecimalShift := -DecimalShift; FOR cnt := 1 TO DecimalShift DO M2Strings.Insert(zero, tmpstr, FirstSpot); END; DotSpot := FirstSpot + 1; END; ProperNumOfDigits := (DotSpot + DigitsToRight); lngth := M2Strings.Length(tmpstr) + 1; IF lngth > ProperNumOfDigits THEN IF LowLevel.ScanNE( ProperNumOfDigits, '9', SYSTEM.ADR(tmpstr) ) < INTEGER(ProperNumOfDigits) THEN (*We can round up without extending length of string.*) RoundSpot := ProperNumOfDigits; carrying := FALSE; WHILE RoundSpot > 0 DO IF (tmpstr[RoundSpot] >= '5') OR carrying THEN INC( tmpstr[RoundSpot - 1] ); carrying := FALSE; END; DEC(RoundSpot); IF tmpstr[RoundSpot] = ':' THEN (*We tried to round up a 9.*) carrying := TRUE; tmpstr[RoundSpot] := '0'; ELSIF tmpstr[RoundSpot] = '/' THEN carrying := TRUE; (*We meant to round up the first digit to the left of the decimal point, but actually rounded up the decimal point itself. The carrying flag will correct it next time through.*) tmpstr[RoundSpot] := '.'; ELSE RoundSpot := 0; (*Forces us out of the loop.*) END; END; END; M2Strings.Delete(tmpstr, ProperNumOfDigits, lngth-ProperNumOfDigits); lngth := ProperNumOfDigits + 1; ELSE WHILE (ProperNumOfDigits + 1) > lngth DO StrEdit.Append(tmpstr, zero); INC(lngth); END; END; M2Strings.Insert(dot, tmpstr, DotSpot); (*We postpone insertion of the dot so that we can scan for non-9's above.*) WHILE (lngth last) OR ( (NOT InQuote) AND PosUtils.Present(s, ';.') ); INC( cnt ); StrEdit.CrunchBlanks( TheSymbol ); END GetSymbol; BEGIN last := M2Strings.Length( OldFormStr ); IF last > 0 THEN DEC( last ); ELSE StrEdit.AssignStr( "{0..400}", NewFormStr ); RETURN; END; StrEdit.AssignStr( "{", NewFormStr ); cnt := 0; REPEAT GetSymbol( tmpstr ); IF CAP(tmpstr[0]) = 'E' THEN IF NOT StrToCardinal( tmpstr, 1, tmpcard ) THEN ErrorManager.WARN('Bad set string.'); END; INC( tmpcard, Key.Extended ); CardinalToStr( tmpcard, 0, tmpstr2 ); StrEdit.Append( NewFormStr, tmpstr2 ); ELSE (*These should always be numbers or ellipses.*) IF (tmpstr[0] = '.') THEN M2Strings.Delete( NewFormStr, M2Strings.Length(NewFormStr) - 1, 1 ); (*delete the comma appended in the last loop*) END; StrEdit.Append( NewFormStr, tmpstr ); END; IF (cnt <= last) AND (tmpstr[0] # '.') THEN StrEdit.Append( NewFormStr, ',' ); END; UNTIL cnt > last; StrEdit.Append( NewFormStr, "}" ); END OldSetFormToNew; PROCEDURE Init(); VAR i : CARDINAL; BEGIN IF Initialized THEN RETURN; ELSE Initialized := TRUE; END; ErrorManager.Init(); Key.Init(); LowLevel.Init(); M2Strings.Init(); Numbers.Init(); NumTypes.Init(); PosUtils.Init(); StrEdit.Init(); (*EntryDiag: StringIO.WriteEol( 1, 'Entering StrConv' ); :EntryDiag*) FOR i := 5 TO 9 DO TenPowers[i].hicard := 0; END; TenPowers[9].locard := 1; TenPowers[8].locard := 10; TenPowers[7].locard := 100; TenPowers[6].locard := 1000; TenPowers[5].locard := 10000; TenPowers[4].locard := 34464; (* 100,000 *) TenPowers[3].locard := 16960; (* 1,000,000 *) TenPowers[2].locard := 38528; (* 10,000,000 *) TenPowers[1].locard := 57600; (* 100,000,000 *) TenPowers[0].locard := 51712; (* 1,000,000,000 *) TenPowers[4].hicard := 1; (* 100,000 *) TenPowers[3].hicard := 15; (* 1,000,000 *) TenPowers[2].hicard := 152; (* 10,000,000 *) TenPowers[1].hicard := 1525; (* 100,000,000 *) TenPowers[0].hicard := 15258; (* 1,000,000,000 *) (*EntryDiag: StringIO.WriteEol( 1, 'Exiting StrConv' ); :EntryDiag*) END Init; BEGIN Initialized := FALSE; Init(); END StrConv.