DIRECTORY Ascii USING [Lower], BasicTime USING [earliestGMT, GMT, UnpackedPeriod, UnpackPeriod, Update], Convert USING [AppendCard, AppendDCard, AppendDReal, AppendE, AppendF, AppendG, AppendInt, AppendTime, AppendTimeRFC822, Base, RopeFromAtom, RopeFromBool], DReal USING [Round], IO USING [Close, Error, ErrorCode, Flush, int, InlinePutChar, PutChar, PutRope, PutText, RopeFromROS, ROS, STREAM, TextFromTOS, TOS, Value], IOUtils USING [Format, LookupData, PFCodeProc, PFErrorProc, PrintRefAnyProc, StoreData], Real USING [MaxSinglePrecision, RealException, Round], RefText USING [AppendChar, AppendTextRope, ObtainScratch, ReleaseScratch, TrustTextAsRope, TrustTextRopeAsText], Rope USING [Fetch, Map, Size, SkipTo, ROPE], RuntimeError USING [BoundsFault]; IOPrintImpl: CEDAR MONITOR IMPORTS Ascii, BasicTime, Convert, DReal, IO, IOUtils, RefText, Real, Rope, RuntimeError EXPORTS IO, IOUtils = BEGIN ROPE: TYPE = Rope.ROPE; STREAM: TYPE = IO.STREAM; Value: TYPE = IO.Value; Format: TYPE = IOUtils.Format; PFCodeProc: TYPE = IOUtils.PFCodeProc; PFErrorProc: TYPE = IOUtils.PFErrorProc; defaultTextSize: NAT ฌ 64; dint: PUBLIC PROC [v: DINT] RETURNS [Value] = {RETURN[[dint[NEW[DINTฌv]]]]}; dcard: PUBLIC PROC [v: DCARD] RETURNS [Value] = {RETURN[[dcard[NEW[DCARDฌv]]]]}; dreal: PUBLIC PROC [v: DREAL] RETURNS [Value] = {RETURN[[dreal[NEW[DREALฌv]]]]}; Put: PUBLIC PROC [stream: STREAM, v1, v2, v3: Value] = { scratch: REF TEXT = RefText.ObtainScratch[defaultTextSize]; InnerPut1[stream, v1, scratch]; InnerPut1[stream, v2, scratch]; InnerPut1[stream, v3, scratch]; RefText.ReleaseScratch[scratch]; }; Put1: PUBLIC PROC [stream: STREAM, value: Value] = { scratch: REF TEXT = RefText.ObtainScratch[defaultTextSize]; InnerPut1[stream, value, scratch]; RefText.ReleaseScratch[scratch]; }; PutR1: PUBLIC PROC [value: Value] RETURNS [ROPE] = { stream: STREAM = IO.ROS[]; scratch: REF TEXT = RefText.ObtainScratch[defaultTextSize]; InnerPut1[stream, value, scratch]; RefText.ReleaseScratch[scratch]; RETURN[IO.RopeFromROS[stream]]; }; PutR: PUBLIC PROC [v1, v2, v3: Value] RETURNS [ROPE] = { stream: STREAM = IO.ROS[]; scratch: REF TEXT = RefText.ObtainScratch[defaultTextSize]; InnerPut1[stream, v1, scratch]; InnerPut1[stream, v2, scratch]; InnerPut1[stream, v3, scratch]; RefText.ReleaseScratch[scratch]; RETURN[IO.RopeFromROS[stream]]; }; PutL: PUBLIC PROC [stream: STREAM, list: LIST OF Value] = { scratch: REF TEXT = RefText.ObtainScratch[defaultTextSize]; FOR l: LIST OF READONLY Value ฌ list, l.rest UNTIL l = NIL DO InnerPut1[stream, l.first, scratch]; ENDLOOP; RefText.ReleaseScratch[scratch]; }; PutLR: PUBLIC PROC [list: LIST OF Value] RETURNS [ROPE] = { stream: STREAM = IO.ROS[]; scratch: REF TEXT = RefText.ObtainScratch[defaultTextSize]; FOR l: LIST OF READONLY Value ฌ list, l.rest UNTIL l = NIL DO InnerPut1[stream, l.first, scratch]; ENDLOOP; RefText.ReleaseScratch[scratch]; RETURN[IO.RopeFromROS[stream]]; }; InnerPut1: PROC [stream: STREAM, v: Value, scratch: REF TEXT] = { scratch.length ฌ 0; { WITH v SELECT FROM null: Value.null => NULL; atom: Value.atom => IO.PutRope[stream, Convert.RopeFromAtom[from: atom.value, quote: FALSE]]; bool: Value.boolean => IO.PutRope[stream, Convert.RopeFromBool[bool.value]]; char: Value.character => IO.PutChar[stream, char.value]; card: Value.cardinal => { scratch ฌ Convert.AppendCard[scratch, card.value]; GO TO putBlock}; int: Value.integer => { scratch ฌ Convert.AppendInt[scratch, int.value]; GO TO putBlock}; dint: Value.dint => { IF dint.valueญ >= 0 THEN scratch ฌ Convert.AppendDCard[scratch, dint.valueญ] ELSE{ ENABLE RuntimeError.BoundsFault => IO.Error[PFUnprintableValue, stream]; dc: DCARD ~ NegateDIntToDCard[dint.valueญ]; scratch ฌ RefText.AppendChar[scratch, '-]; scratch ฌ Convert.AppendDCard[scratch, dc]; }; GO TO putBlock }; dcard: Value.dcard => { scratch ฌ Convert.AppendDCard[scratch, dcard.valueญ]; GO TO putBlock}; real: Value.real => { scratch ฌ Convert.AppendG[scratch, real.value]; GO TO putBlock}; refAny: Value.refAny => PrintRef[stream, scratch, refAny.value]; rope: Value.rope => IO.PutRope[stream, rope.value]; text: Value.text => IO.PutText[stream, text.value]; time: Value.time => { scratch ฌ Convert.AppendTime[to: scratch, from: time.value, start: $years, end: $seconds]; GO TO putBlock; }; ENDCASE => ERROR; EXITS putBlock => stream.streamProcs.putBlock[stream, scratch, 0, scratch.length]; }; }; NegateDIntToDCard: PROC [i: DINT--that's negative--] RETURNS [DCARD] ~ { <> i2: DINT ~ (-1) - i; c3: DCARD ~ i2; RETURN [c3+1]}; globalPrintRefAny: IOUtils.PrintRefAnyProc ฌ NIL; RegisterPrintRefAny: PUBLIC ENTRY PROC [printRefAnyProc: IOUtils.PrintRefAnyProc] = { globalPrintRefAny ฌ printRefAnyProc; }; GetPrintRefAny: ENTRY PROC [] RETURNS [IOUtils.PrintRefAnyProc] = { RETURN [globalPrintRefAny] }; PrintRef: PROC [stream: STREAM, scratch: REF TEXT, ref: REF READONLY ANY] = { printRefAnyProc: IOUtils.PrintRefAnyProc = GetPrintRefAny[]; IF printRefAnyProc = NIL THEN { scratch ฌ RefText.AppendTextRope[scratch, "[REF: "]; scratch ฌ Convert.AppendCard[ to: scratch, from: LOOPHOLE[ref, LONG CARDINAL], base: 8, showRadix: TRUE]; scratch ฌ RefText.AppendChar[scratch, ']]; stream.streamProcs.putBlock[stream, scratch, 0, scratch.length]; } ELSE { printRefAnyProc[stream, ref, 4, 32, FALSE]; } }; PFProcs: TYPE = REF PFProcsRecord; PFProcsRecord: PUBLIC TYPE = RECORD [ procArray: ARRAY CHAR ['a..'z] OF PFCodeProc ฌ ALL[NIL], errorProc: PFErrorProc ฌ NIL ]; globalPFProcs: PFProcs ฌ NIL; GetDefaultPFProcs: ENTRY PROC [] RETURNS [PFProcs] = INLINE { RETURN [globalPFProcs]; }; GetPFProcs: PROC [stream: STREAM] RETURNS [pfProcs: PFProcs] = INLINE { pfProcs ฌ NARROW[IOUtils.LookupData[stream, $SetPFCode]]; IF pfProcs = NIL THEN pfProcs ฌ GetDefaultPFProcs[]; }; SetDefaultPFCodeProc: PUBLIC ENTRY PROC [char: CHAR, codeProc: PFCodeProc] RETURNS [previous: PFCodeProc] = { char ฌ Ascii.Lower[char]; IF char NOT IN ['a..'z] THEN RETURN WITH ERROR IO.Error[PFInvalidCode, NIL]; previous ฌ globalPFProcs.procArray[char]; globalPFProcs.procArray[char] ฌ codeProc; }; SetDefaultPFErrorProc: PUBLIC ENTRY PROC [errorProc: PFErrorProc] RETURNS [previous: PFErrorProc] = { previous ฌ globalPFProcs.errorProc; globalPFProcs.errorProc ฌ errorProc; }; CopyPFProcs: PUBLIC PROC [stream: STREAM] RETURNS [new: PFProcs] = { pfProcs: PFProcs ฌ IF stream = NIL THEN NIL ELSE NARROW[IOUtils.LookupData[stream, $SetPFCode]]; IF pfProcs = NIL THEN pfProcs ฌ GetDefaultPFProcs[]; RETURN [NEW[PFProcsRecord ฌ pfProcsญ]]; }; SetPFProcs: PUBLIC PROC [stream: STREAM, pfProcs: PFProcs] RETURNS [previous: PFProcs] = { previous ฌ NARROW[IOUtils.LookupData[stream, $SetPFCode]]; IOUtils.StoreData[stream, $SetPFCode, pfProcs]; }; SetPFCodeProc: PUBLIC PROC [pfProcs: PFProcs, char: CHAR, codeProc: PFCodeProc] RETURNS [previous: PFCodeProc] = { IF pfProcs = NIL THEN ERROR IO.Error[PFInvalidPFProcs, NIL]; char ฌ Ascii.Lower[char]; IF char NOT IN ['a..'z] THEN ERROR IO.Error[PFInvalidCode, NIL]; previous ฌ pfProcs.procArray[char]; pfProcs.procArray[char] ฌ codeProc; }; SetPFErrorProc: PUBLIC PROC [pfProcs: PFProcs, errorProc: PFErrorProc] RETURNS [previous: PFErrorProc] = { IF pfProcs = NIL THEN ERROR IO.Error[PFInvalidPFProcs, NIL]; previous ฌ pfProcs.errorProc; pfProcs.errorProc ฌ errorProc; }; PutF: PUBLIC PROC [stream: STREAM, format: ROPE, v1, v2, v3: Value] = { pfProcs: PFProcs = GetPFProcs[stream]; fp: INT ฌ 0; IF v1.type#null THEN fp ฌ PFInternal[stream, format, fp, v1, pfProcs]; IF v2.type#null THEN fp ฌ PFInternal[stream, format, fp, v2, pfProcs]; IF v3.type#null THEN fp ฌ PFInternal[stream, format, fp, v3, pfProcs]; [] ฌ PFInternal[stream, format, fp, [null[]], pfProcs]; }; PutF1: PUBLIC PROC [stream: STREAM, format: ROPE, value: Value] = { pfProcs: PFProcs = GetPFProcs[stream]; fp: INT ฌ 0; IF value.type#null THEN fp ฌ PFInternal[stream, format, fp, value, pfProcs]; [] ฌ PFInternal[stream, format, fp, [null[]], pfProcs]; }; PutFR: PUBLIC PROC [format: ROPE, v1, v2, v3: Value] RETURNS [ROPE] = { stream: STREAM = IO.ROS[]; PutF[stream, format, v1, v2, v3]; RETURN [IO.RopeFromROS[stream]]; }; PutFR1: PUBLIC PROC [format: ROPE, value: Value] RETURNS [ROPE] = { stream: STREAM = IO.ROS[]; pfProcs: PFProcs = GetPFProcs[stream]; fp: INT ฌ 0; IF value.type#null THEN fp ฌ PFInternal[stream, format, fp, value, pfProcs]; [] ฌ PFInternal[stream, format, fp, [null[]], pfProcs]; RETURN [IO.RopeFromROS[stream]]; }; PutFL: PUBLIC PROC [stream: STREAM, format: ROPE, list: LIST OF Value] = { pfProcs: PFProcs = GetPFProcs[stream]; fp: INT ฌ 0; FOR l: LIST OF READONLY Value ฌ list, l.rest UNTIL l = NIL DO fp ฌ PFInternal[stream, format, fp, l.first, pfProcs]; ENDLOOP; [] ฌ PFInternal[stream, format, fp, [null[]], pfProcs]; }; PutFLR: PUBLIC PROC [format: ROPE, list: LIST OF Value] RETURNS [ROPE] = { stream: STREAM = IO.ROS[]; PutFL[stream, format, list]; RETURN [IO.RopeFromROS[stream]]; }; PFInternal: PROC [stream: STREAM, format: ROPE, formatPtr: INT, arg: Value, pfProcs: PFProcs] RETURNS [--advancedFormatPtr--INT] = { error: IO.ErrorCode ฌ $Null; c: CHAR ฌ 0C; GetNextFromFormat: PROC[] = INLINE { c ฌ format.Fetch[formatPtr]; formatPtr ฌ formatPtr.SUCC; }; formatSize: INT ฌ format.Size[]; IF formatSize = 0 THEN { IF arg.type = null THEN GOTO Done ELSE { format ฌ "%g"; formatSize ฌ 2; formatPtr ฌ 0 } }; DO { -- EXITS PutC, HandleError IF formatPtr < formatSize THEN { end: INT ~ Rope.SkipTo[format, formatPtr, "%"]; IF end > formatPtr THEN { IO.PutRope[stream, format, formatPtr, end-formatPtr]; formatPtr ฌ end; }; }; -- end accelerator IF formatPtr >= formatSize THEN { IF arg.type = null THEN GOTO Done ELSE { --ran out of format while looking for '% error ฌ PFFormatSyntaxError; GOTO HandleError } }; GetNextFromFormat[]; IF c # '% THEN GOTO PutC; { savedFormatPointer: INT = formatPtr; DO { IF formatPtr >= formatSize THEN { --ran out of format while looking for code error ฌ PFFormatSyntaxError; GOTO HandleError }; GetNextFromFormat[]; IF c IN ['a..'z] OR c IN ['A..'Z] THEN GOTO FoundCode; IF c = '% THEN GOTO FoundPercent; EXITS FoundCode => EXIT; FoundPercent => { --found '% while looking for code IF formatPtr = savedFormatPointer.SUCC THEN GOTO PutC; error ฌ PFFormatSyntaxError; GOTO HandleError }; } ENDLOOP; IF arg.type = null THEN { --no Value corresponding to '% in format error ฌ PFFormatSyntaxError; GOTO HandleError }; c ฌ Ascii.Lower[c]; TRUSTED { val: Value = WITH arg SELECT FROM ref: Value.refAny => WITH LOOPHOLE[ref.value, REF] SELECT FROM refBool: REF BOOL => [boolean[refBoolญ]], refChar: REF CHAR => [character[refCharญ]], refCard: REF CARD => [cardinal[refCardญ]], refInt: REF INT => [integer[refIntญ]], refNat: REF NAT => [integer[refNatญ]], refReal: REF REAL => [real[refRealญ]], refDReal: REF DREAL => [dreal[refDReal]], refDInt: REF DINT => [dint[refDInt]], refDCard: REF DCARD => [dcard[refDCard]], refText: REF TEXT => [text[refText]], atom: ATOM => [atom[atom]], rope: ROPE => [rope[rope]], ENDCASE => arg ENDCASE => arg; p: PFCodeProc ฌ pfProcs.procArray[c]; IF p = NIL THEN { error ฌ PFCantBindConversionProc; GOTO HandleError }; p[stream, val, [format, savedFormatPointer], c ! IO.Error => IF ec IN [PFCantBindConversionProc .. PFUnprintableValue] THEN { error ฌ ec; GOTO HandleError }]; GOTO Done; } } EXITS PutC => stream.streamProcs.putChar[stream, c]; HandleError => { ep: PFErrorProc ฌ pfProcs.errorProc; IF ep = NIL THEN ep ฌ PFErrorPrintPounds; ep[error, stream]; GOTO Done; }; } ENDLOOP; EXITS Done => RETURN [formatPtr]; }; ParseFormat: PROC [form: ROPE, start: INT] RETURNS [leadingMinus: BOOL ฌ FALSE, leadingZero: BOOL ฌ FALSE, fieldWidth: INTEGER ฌ -1, numDigits: INTEGER ฌ -1] = { len: INT ฌ Rope.Size[form]; IF start < len THEN SELECT Rope.Fetch[form, start] FROM '0 => {leadingZero ฌ TRUE; start ฌ start + 1}; '- => {leadingMinus ฌ TRUE; start ฌ start + 1}; ENDCASE; WHILE start < len DO c: CHAR ฌ Rope.Fetch[form, start]; SELECT c FROM IN ['0..'9] => { digit: [0..9] ฌ (c-'0); IF fieldWidth < 0 THEN fieldWidth ฌ 0; IF fieldWidth >= (LAST[INTEGER] - digit)/10 THEN { fieldWidth ฌ LAST[INTEGER]; RETURN}; fieldWidth ฌ fieldWidth*10 + digit; }; ENDCASE => EXIT; start ฌ start + 1; ENDLOOP; IF start < len AND Rope.Fetch[form, start] = '. THEN { start ฌ start + 1; WHILE start < len DO c: CHAR ฌ Rope.Fetch[form, start]; SELECT c FROM IN ['0..'9] => { digit: [0..9] ฌ (c-'0); IF numDigits < 0 THEN numDigits ฌ 0; IF numDigits >= (LAST[INTEGER] - digit)/10 THEN { numDigits ฌ LAST[INTEGER]; RETURN}; numDigits ฌ numDigits*10 + digit; }; ENDCASE => RETURN; start ฌ start + 1; ENDLOOP; }; }; PFErrorPrintPounds: PUBLIC PROC [error: IO.ErrorCode, stream: STREAM] = { IO.PutRope[stream, "#####"]; }; PFErrorNoop: PUBLIC PROC [error: IO.ErrorCode, stream: STREAM] = { }; PFErrorError: PUBLIC PROC [error: IO.ErrorCode, stream: STREAM] = { ERROR IO.Error[error, stream]; }; StringRoutine: PROC [stream: STREAM, val: Value, format: Format, char: CHAR] = TRUSTED { r: REF TEXT ฌ NIL; rp: ROPE ฌ NIL; WITH v: val SELECT FROM atom => rp ฌ Convert.RopeFromAtom[from: v.value, quote: FALSE]; boolean => rp ฌ Convert.RopeFromBool[v.value]; character => r ฌ RefText.AppendChar[RefText.ObtainScratch[defaultTextSize], v.value]; cardinal => r ฌ Convert.AppendCard[RefText.ObtainScratch[defaultTextSize], v.value]; dcard => r ฌ Convert.AppendDCard[RefText.ObtainScratch[defaultTextSize], v.valueญ]; integer => r ฌ Convert.AppendInt[RefText.ObtainScratch[defaultTextSize], v.value]; dint => { r ฌ RefText.ObtainScratch[defaultTextSize]; IF v.valueญ >= 0 THEN r ฌ Convert.AppendDCard[r, v.valueญ] ELSE { ENABLE RuntimeError.BoundsFault => IO.Error[PFUnprintableValue, stream]; dc: DCARD ~ NegateDIntToDCard[v.valueญ]; r ฌ RefText.AppendChar[r, '-]; r ฌ Convert.AppendDCard[r, dc]; }; }; real => r ฌ Convert.AppendG[RefText.ObtainScratch[defaultTextSize], v.value]; dreal => r ฌ Convert.AppendDReal[RefText.ObtainScratch[defaultTextSize], v.valueญ]; refAny => { strm: STREAM = IO.ROS[]; Put1[strm, val]; rp ฌ IO.RopeFromROS[strm] }; rope => rp ฌ v.value; text => rp ฌ RefText.TrustTextAsRope[v.value]; time => r ฌ Convert.AppendTime[to: RefText.ObtainScratch[defaultTextSize], from: v.value, start: $years, end: $seconds]; ENDCASE => ERROR; PrintText[r, rp, format, stream, IF char = 'h THEN TRUE ELSE FALSE]; }; FixnumRoutine: PROC [stream: STREAM, val: Value, format: Format, char: CHAR] = TRUSTED { base: Convert.Base ฌ SELECT char FROM 'b => 8, 'x => 16, ENDCASE => 10; r: REF TEXT; WITH v: val SELECT FROM boolean => r ฌ Convert.AppendCard[RefText.ObtainScratch[defaultTextSize], IF v.value THEN 1 ELSE 0, base, FALSE]; cardinal => r ฌ Convert.AppendCard[RefText.ObtainScratch[defaultTextSize], v.value, base, FALSE]; character => r ฌ Convert.AppendCard[RefText.ObtainScratch[defaultTextSize], v.value.ORD, base, FALSE]; dcard => r ฌ Convert.AppendDCard[RefText.ObtainScratch[defaultTextSize], v.valueญ, base, FALSE]; dint => { r ฌ RefText.ObtainScratch[defaultTextSize]; IF v.valueญ >= 0 THEN r ฌ Convert.AppendDCard[r, v.valueญ, base, FALSE] ELSE { ENABLE RuntimeError.BoundsFault => IO.Error[PFUnprintableValue, stream]; dc: DCARD ~ NegateDIntToDCard[v.valueญ]; r ฌ RefText.AppendChar[r, '-]; r ฌ Convert.AppendDCard[r, dc, base, FALSE]; }; }; dreal => r ฌ Convert.AppendDCard[RefText.ObtainScratch[defaultTextSize], LOOPHOLE[DReal.Round[v.valueญ ! Real.RealException => ERROR IO.Error[PFUnprintableValue, stream]], DCARD], base, FALSE]; integer => r ฌ Convert.AppendInt[RefText.ObtainScratch[defaultTextSize], v.value, base, FALSE]; real => r ฌ Convert.AppendInt[RefText.ObtainScratch[defaultTextSize], DReal.Round[v.value ! Real.RealException => ERROR IO.Error[PFUnprintableValue, stream]], base, FALSE]; ENDCASE => ERROR IO.Error[PFTypeMismatch, stream]; PrintText[r, NIL, format, stream, FALSE]; }; LooksRoutine: PROC [stream: STREAM, val: Value, format: Format, char: CHAR] = { WITH val SELECT FROM ropeVal: Value.rope => { back: STREAM ฌ stream.backingStream; WHILE back # NIL DO WITH IOUtils.LookupData[back, $SetPFCode] SELECT FROM pfProcs: PFProcs => { pp: PFCodeProc = pfProcs.procArray[char]; IF pp # NIL THEN { IO.Flush[stream]; pp[back, val, format, char]; RETURN; }; }; ENDCASE; back ฌ back.backingStream; ENDLOOP; }; ENDCASE => ERROR IO.Error[PFTypeMismatch, stream]; }; RopeLiteralRoutine: PROC [stream: STREAM, val: Value, format: Format, char: CHAR] = { PutEscape: PROC [c: CHAR] RETURNS [BOOL] = { IF c IN [40C .. 177C] THEN { IF c = '\\ OR c = '\" THEN stream.streamProcs.putChar[stream, '\\]; stream.streamProcs.putChar[stream, c]; } ELSE { stream.streamProcs.putChar[stream, '\\]; SELECT c FROM '\n => stream.streamProcs.putChar[stream, 'n]; '\t => stream.streamProcs.putChar[stream, 't]; '\b => stream.streamProcs.putChar[stream, 'b]; '\f => stream.streamProcs.putChar[stream, 'f]; '\l => stream.streamProcs.putChar[stream, 'l]; ENDCASE => { stream.streamProcs.putChar[stream, '0 + (c - 0C) / 64]; stream.streamProcs.putChar[stream, '0 + (c - 0C) MOD 64 / 8]; stream.streamProcs.putChar[stream, '0 + (c - 0C) MOD 8] } ; }; RETURN [FALSE]; }; WITH val SELECT FROM ropeVal: Value.rope => [] ฌ Rope.Map[base: ropeVal.value, action: PutEscape]; charVal: Value.character => [] ฌ PutEscape[charVal.value]; ENDCASE => ERROR IO.Error[PFTypeMismatch, stream]; }; TimeIntervalRoutine: PROC [stream: STREAM, val: Value, format: Format, char: CHAR] = { seconds: INT; period: BasicTime.UnpackedPeriod; r: REF TEXT; han: STREAM; WITH val SELECT FROM v: Value.cardinal => { IF v.value>CARD[INT.LAST] THEN ERROR IO.Error[PFUnprintableValue, stream]; seconds ฌ v.value; }; v: Value.integer => seconds ฌ v.value; v: Value.real => seconds ฌ Real.Round[v.value ! Real.RealException => ERROR IO.Error[PFUnprintableValue, stream] ]; ENDCASE => ERROR IO.Error[PFTypeMismatch, stream]; period ฌ BasicTime.UnpackPeriod[seconds]; r ฌ RefText.ObtainScratch[defaultTextSize]; han ฌ IO.TOS[r]; IF period.negative THEN IO.PutChar[han, '-]; PutF[han, "%02g:%02g:%02g", IO.int[period.hours], IO.int[period.minutes], IO.int[period.seconds]]; r ฌ han.TextFromTOS[]; han.Close[]; PrintText[r, NIL, format, stream, FALSE]; }; TimeRoutine: PROC [stream: STREAM, val: Value, format: Format, char: CHAR] = { t: BasicTime.GMT; text: REF TEXT ฌ NIL; WITH val SELECT FROM v: Value.integer => t ฌ BasicTime.Update[base: BasicTime.earliestGMT, period: v.value]; v: Value.cardinal => t ฌ BasicTime.Update[base: BasicTime.earliestGMT, period: v.value]; v: Value.real => { li: INT ฌ Real.Round[v.value ! Real.RealException => ERROR IO.Error[PFUnprintableValue, stream] ]; IF li < 0 THEN ERROR IO.Error[PFUnprintableValue, stream]; t ฌ BasicTime.Update[base: BasicTime.earliestGMT, period: li]; }; v: Value.time => t ฌ v.value; ENDCASE => ERROR IO.Error[PFTypeMismatch, stream]; text ฌ RefText.ObtainScratch[defaultTextSize]; SELECT char FROM 'u => text ฌ Convert.AppendTimeRFC822[to: text, from: t, includeSeconds: TRUE]; ENDCASE => text ฌ Convert.AppendTime[to: text, from: t, start: $years, end: $seconds]; PrintText[text, NIL, format, stream, FALSE]; }; FlonumRoutine: IOUtils.PFCodeProc = { leadingMinus: BOOL ฌ FALSE; leadingZero: BOOL ฌ FALSE; fieldWidth: INT ฌ -1; numDigits: INT ฌ -1; [leadingMinus, leadingZero, fieldWidth, numDigits] ฌ ParseFormat[format.form, format.first]; SELECT numDigits FROM < 0 => numDigits ฌ 2; > Real.MaxSinglePrecision => numDigits ฌ Real.MaxSinglePrecision; ENDCASE; WITH val SELECT FROM r: real IO.Value => { proc: PROC [REF TEXT, REAL, [0..Real.MaxSinglePrecision]] RETURNS [REF TEXT] ฌ SELECT char FROM 'e => Convert.AppendE, 'f => Convert.AppendF, ENDCASE => ERROR; scratch: REF TEXT ฌ RefText.ObtainScratch[defaultTextSize]; buffer: REF TEXT ฌ proc[scratch, r.value, numDigits]; rLen: NAT ฌ buffer.length; diff: INTEGER ฌ fieldWidth - rLen; IF diff > 0 AND NOT leadingMinus THEN PutChars[stream, diff, ' ]; IO.PutText[stream, buffer]; IF diff > 0 AND leadingMinus THEN PutChars[stream, diff, ' ]; RefText.ReleaseScratch[scratch]; }; ENDCASE => PutChars[stream, fieldWidth, '!]; }; PutChars: PROC [st: STREAM, chars: NAT, char: CHAR] = { THROUGH [0..chars) DO IO.PutChar[st, char]; ENDLOOP; }; AppendChars: PROC [text: REF TEXT, chars: NAT, char: CHAR] RETURNS [REF TEXT] = { THROUGH [0..chars) DO text ฌ RefText.AppendChar[text, char]; ENDLOOP; RETURN [text]; }; PrintText: PROC [text: REF TEXT, rp: ROPE, format: Format, stream: STREAM, visiblecc: BOOL] = { MyPut: PROC [c: CHAR] = { IO.InlinePutChar[stream, c] }; fillChar: CHAR ฌ ' ; ladj: BOOL ฌ FALSE; w, k, tlength: CARDINAL; -- w is field width, k is # chars to fill c: CHAR ฌ 0C; formatPtr: INT ฌ format.first; GetNextFromFormat: PROC[] = INLINE { c ฌ format.form.Fetch[formatPtr]; formatPtr ฌ formatPtr.SUCC; }; IF text#NIL THEN tlength ฌ text.length ELSE tlength ฌ rp.Size[]; GetNextFromFormat[]; SELECT Ascii.Lower[c] FROM '0 => { fillChar ฌ '0; GetNextFromFormat[] }; '- => { ladj ฌ TRUE; GetNextFromFormat[] }; ENDCASE; w ฌ 0; DO SELECT c FROM IN ['0 .. '9] => { w ฌ w*10 + (c - '0); GetNextFromFormat[] }; ENDCASE => EXIT; ENDLOOP; SELECT Ascii.Lower[c] FROM '., IN ['a .. 'z] => NULL; ENDCASE => IO.Error[PFFormatSyntaxError, stream]; IF w < tlength THEN { w ฌ tlength; k ฌ 0} ELSE { k ฌ w - tlength }; IF NOT ladj THEN THROUGH [1..k] DO MyPut[fillChar]; ENDLOOP; IF visiblecc THEN { FOR j: NAT IN [0..tlength) DO c ฌ IF text#NIL THEN text[j] ELSE rp.Fetch[j]; IF c < 40C THEN { MyPut['^]; MyPut[c + LOOPHOLE['@, CARDINAL]] } ELSE MyPut[c]; ENDLOOP } ELSE { IF text#NIL THEN IO.PutText[stream, text] ELSE IO.PutRope[stream, rp]; }; IF ladj THEN THROUGH [1..k] DO MyPut[' ] ENDLOOP; IF text#NIL THEN RefText.ReleaseScratch[text]; }; Create: ENTRY PROC [] = { globalPFProcs ฌ NEW[PFProcsRecord ฌ []]; globalPFProcs.errorProc ฌ PFErrorPrintPounds; globalPFProcs.procArray['a] ฌ StringRoutine; globalPFProcs.procArray['b] ฌ FixnumRoutine; globalPFProcs.procArray['c] ฌ StringRoutine; globalPFProcs.procArray['d] ฌ FixnumRoutine; globalPFProcs.procArray['e] ฌ FlonumRoutine; globalPFProcs.procArray['f] ฌ FlonumRoutine; globalPFProcs.procArray['g] ฌ StringRoutine; globalPFProcs.procArray['h] ฌ StringRoutine; globalPFProcs.procArray['l] ฌ LooksRoutine; globalPFProcs.procArray['q] ฌ RopeLiteralRoutine; globalPFProcs.procArray['r] ฌ TimeIntervalRoutine; globalPFProcs.procArray['s] ฌ StringRoutine; globalPFProcs.procArray['t] ฌ TimeRoutine; globalPFProcs.procArray['u] ฌ TimeRoutine; globalPFProcs.procArray['x] ฌ FixnumRoutine; }; Create[]; END. ๖ IOPrintImpl.mesa Copyright ำ 1984, 1985, 1986, 1987, 1991 by Xerox Corporation. All rights reserved. Schmidt on 31-Aug-81 21:39:19 Stewart on 20-Jan-82 19:15:39 Teitelman on January 21, 1983 5:31 pm Andrew Birrell on June 22, 1983 9:44 am Paul Rovner on May 26, 1983 3:56 pm Levin on August 8, 1983 6:09 pm MBrown on January 16, 1984 3:06 pm PST Swinehart, June 25, 1984 1:34:33 pm PDT Russ Atkinson (RRA) February 19, 1985 7:13:57 pm PST Carl Hauser, May 11, 1988 3:49:46 pm PDT Michael Plass, January 23, 1992 4:11 pm PST Doug Wyatt, May 20, 1992 3:55 pm PDT Willie-s, September 2, 1992 6:19 pm PDT Last tweaked by Mike Spreitzer September 17, 1992 9:57 pm PDT RRA: It is better to quickly acquire and release buffers of constant size, even when the need is not constant, since this reduces fragmentation. We believe that this size should be OK for nearly all applications. It is a variable to encourage experimentation. Value constructors Put PrintRefAny PFProcs PutF, PutFR, PutFL, PutFLR PFInternal (scan format and print one Value) Scans format, starting at formatPtr, until reaching end or reaching a conversion specification (e.g., "%05d"). If format is a null rope, treats it as if it were the rope "%g", and as if formatPtr = 0. If conversion specification is found and arg is not null, prints it; if conversion specification is not found and arg is null, ok; otherwise error. Returns index of first byte of format not scanned. accelerator - output consecutive vanilla characters all at once c is a conversion code; conversion string is the stuff between % and c. Now either print arg or generate error. If arg has type refAny, narrow it to a more specific type if possible. Can't update of arg in checked code, even though arg is local! This block is trusted because of the interface says REF READONLY ANY, and we need to loophole this into a REF ANY for the discrimination to work (for ROPE and ATOM, in particular).August 6, 1991 12:02:10 pm PDT pi: REF READONLY BasicTime.GMT => time[pi^], Not possible now because GMT is opaque ... parses a standard format specification. leadingMinus = TRUE iff there is a '- at form[start] (and start < Size[form]) leadingZero = TRUE iff there is a '0 at form[start] (and start < Size[form]) fieldWidth is an optional leading number in the format specification IN [0..LAST[INTEGER]) if there was a number = -1 if there was no number present = LAST[INTEGER] if the number overflows numDigits is an optional number following a '. in the specification IN [0..LAST[INTEGER]) if there was such a number = -1 if there was no such number = LAST[INTEGER] if said number overflows PFCodeProcs TRUSTED because of WITH v: val SELECT ... compiler problem Value: any Format: standard char: 'h: causes ^ for ctrl-char. Converts fixnums to decimal, flonums to F-format decimal. Converts bools to "TRUE" | "FALSE". Converts date to standard format. Prints resulting string. ABS[DINT] does not do the right thing - no ABS is applied TRUSTED because of WITH v: val SELECT ... compiler problem Value: bool, char, card, int, real Format: standard char: 'b: forces base 8; 'x: forces base 16 Converts numbers to decimal unless other base specified by char. Converts bools to 1 | 0. Converts char to numeric code. Rounds real to nearest INT. Prints resulting string. Value: rope Format: ignored char: ignored Noop here; for Viewer streams, performs font changes RRA: I changed this to pass through the info to the backing stream. This is to allow layered streams to pass along formatting commands without having to have cloned PFProcs, provided that the layered streams stuff the under layer in the backingStream field. We stop whenever we get to a proper PFProc for the backing stream, under the assumption that it is no longer our problem after that. Must flush stream to synch with setting looks Value: rope Format: ignored char: ignored Prints rope with \-notation for control codes, \, and " Print c to stream with \-notation Value: card, int, real Format: standard char: ignored Rounds real to nearest INT. Prints number as hh:mm:ss (hh may be more than two digits, but not fewer). Value: int, card, real, time Format: standard char: ignored Prints a date using AppendTime. Interprets int, card, and real as seconds since "beginning of time" Real Formatting [stream: STREAM, val: IO.Value, format: IOUtils.Format, char: CHAR] Utility routines Field justification and filling Prints text or rp to stream, while enforcing simple format specifications: field width, left or right justification, zero-filling. Format has syntax [0|-|][num.num]. Used by conversion routines that first buffer full output in text, then format it here. Our policy is to print all of text, even if the format specifies a smaller width. If text is non-NIL, releases to scratch pool after call. Initialization ส•NewlineDelimiter –(cedarcode) style™codešœ™Kšœ ฯeœI™TKšœ™Kšœ™Kšœ&™&Kšœ'™'Kšœ$™$Kšœ™Kšœ&™&K™'K™4K™(K™+K™$K™'K™=K˜K™—šฯk ˜ Kšœžœ ˜Kšœ žœžœ(˜IKšœžœŽ˜›Kšœžœ ˜Kš žœžœ^žœžœžœ ˜ŒKšœžœK˜XKšœžœ,˜6Kšœžœc˜pKšœžœžœ˜,Kšœ žœ˜!—K˜šฯn œžœž˜Kšžœ#žœ,˜XKšžœžœ ˜Kšœž˜K˜Kšžœžœžœ˜Kšžœžœžœžœ˜Kšœžœžœ˜Kšœžœ˜Kšœ žœ˜&Kšœ žœ˜(K˜šœžœ˜Kšœ…™…——head™Kšœžœžœžœžœ žœžœžœ˜LKšœžœžœžœžœ žœžœžœ˜PKšœžœžœžœžœ žœžœžœ˜P—™šŸœžœžœ žœ˜8Kšœ žœžœ*˜;Kšœ˜Kšœ˜K˜Kšœ ˜ Kšœ˜—K˜šŸœžœžœ žœ˜4Kšœ žœžœ*˜;Kšœ"˜"Kšœ ˜ Kšœ˜K˜—š Ÿœžœžœžœžœ˜4Kšœžœžœžœ˜Kšœ žœžœ*˜;Kšœ"˜"Kšœ ˜ Kšžœžœ˜Kšœ˜K˜—š Ÿœžœžœžœžœ˜8Kšœžœžœžœ˜Kšœ žœžœ*˜;Kšœ˜K˜K˜Kšœ ˜ Kšžœžœ˜Kšœ˜K˜—š Ÿœžœžœ žœžœžœ ˜;Kšœ žœžœ*˜;š žœžœžœžœžœžœž˜=Kšœ$˜$Kšžœ˜—Kšœ ˜ Kšœ˜—K˜šŸœžœžœžœžœžœžœ˜;Kšœžœžœžœ˜Kšœ žœžœ*˜;š žœžœžœžœžœžœž˜=Kšœ$˜$Kšžœ˜—Kšœ ˜ Kšžœžœ˜Kšœ˜—K˜š Ÿ œžœ žœžœžœ˜AKšœ˜˜šžœžœž˜Kšœžœ˜šœ˜Kšžœ?žœ˜I—šœ˜Kšžœ3˜5—šœ˜Kšžœ˜—šœ˜Kšœ3žœžœ ˜C—šœ˜Kšœ1žœžœ ˜A—šœ˜Kšžœžœ4˜Lšžœ˜Kšžœžœ#˜HKšœžœ"˜+K˜*K˜+K˜—Kšžœžœ ˜K˜—šœ˜Kšœ6žœžœ ˜F—šœ˜Kšœ0žœžœ ˜A—šœ˜Kšœ(˜(—šœ˜Kšžœ˜—šœ˜Kšžœ˜—šœ˜KšœZ˜ZKšžœžœ ˜K˜—Kšžœžœ˜—KšžœM˜RKšœ˜—Kšœ˜—K˜š Ÿœžœžฯcœžœžœ˜HK˜bKšœžœ ˜Kšœžœ˜Kšžœ ˜——™ Kšœ-žœ˜1K˜šŸœžœžœžœ/˜UKšœ$˜$K˜—K˜šŸœžœžœžœ˜CKšžœ˜K˜—K˜šŸœžœ žœ žœžœžœžœžœ˜NKšœ<˜<šžœžœžœ˜Kšœ4˜4šœ˜Kš œžœžœžœžœ˜K—Kšœ*˜*Kšœ@˜@K˜—šžœ˜Kšœ$žœ˜+K˜—K˜——™Kšœ žœžœ˜"šœžœžœžœ˜%Kš œ žœžœ žœžœžœ˜8Kšœž˜K˜K˜—Kšœžœ˜K˜š Ÿœžœžœžœ žœ˜=Kšžœ˜K˜K˜—š Ÿ œžœ žœžœžœ˜GKšœ žœ)˜9Kšžœ žœžœ˜4Kšœ˜—K˜š Ÿœžœžœžœžœžœ˜mKšœ˜Kšžœžœžœ žœžœžœžœžœžœ˜LKšœ)˜)Kšœ)˜)K˜—K˜š Ÿœžœžœžœžœ˜eKšœ#˜#Kšœ$˜$K˜K˜—š Ÿ œžœžœ žœžœ˜Dšœ˜Kš žœ žœžœžœžœžœ)˜M—Kšžœ žœžœ˜4Kšžœžœ˜'K˜—K˜š Ÿ œžœžœ žœžœ˜ZKšœ žœ)˜:Kšœ/˜/K˜—K˜š Ÿ œžœžœžœžœ˜rKš žœ žœžœžœžœžœ˜™>K™าšœ žœžœž˜!šœ˜š žœžœ žœžœž˜)Kšœ žœžœ˜)Kšœ žœžœ˜+Kšœ žœžœ˜*Kšœžœžœ˜&Kšœžœžœ˜&Kšœ žœžœ˜&Kšœ žœžœ˜)Kšœ žœžœ˜%Kšœ žœžœ˜)Kšœ žœžœ˜%Kšœžœ˜Kšœžœ˜šœ,™,Kšœ&™&—Kšžœ˜——Kšžœ˜—Kšœ%˜%šžœžœžœ˜Kšœ#žœ ˜3Kšœ˜—šœ1žœ ˜<šžœžœ2žœ˜@Kšœ žœ˜ ——Kšžœ˜ K˜—K˜—šž˜Kšœ.˜.šœ˜Kšœ$˜$Kšžœžœžœ˜)K˜Kšžœ˜ K˜——Kšœžœ˜ —šž˜Kšœžœ ˜—Kšœ˜K˜—šŸ œžœžœ žœžœžœžœžœžœžœžœ ˜กšœ+™+Kšœžœ:™MKšœžœ:™LšœD™DKšžœžœžœ™+K™#Kšœžœžœ™'—šœC™CKšžœžœžœ™0K™ Kšœžœžœ™(——Kšœžœ˜šžœ žœ˜šžœž˜#Kšœžœ˜.Kšœžœ˜/Kšžœ˜——šžœ ž˜Kšœžœ˜"šžœž˜ šžœ˜Kšœ˜Kšžœžœ˜&šžœžœžœžœ˜2Kšœ žœžœžœ˜$—Kšœ#˜#Kšœ˜—Kšžœžœ˜—K˜Kšžœ˜—šžœ žœžœ˜6K˜šžœ ž˜Kšœžœ˜"šžœž˜ šžœ˜Kšœ˜Kšžœžœ˜$šžœžœžœžœ˜1Kšœ žœžœžœ˜#—Kšœ!˜!Kšœ˜—Kšžœžœ˜—K˜Kšžœ˜—K˜—K˜K˜—š Ÿœžœžœ žœžœ˜IKšžœ˜Kšœ˜K˜—š Ÿ œžœžœ žœžœ˜BKšœ˜K˜—š Ÿ œžœžœ žœžœ˜CKšžœžœ˜Kšœ˜——™ š Ÿ œžœ žœ$žœžœ˜XKšžœ žœž™:K™ K™K™'KšœNžœžœ?™›Kšœžœžœžœ˜Kšœžœžœ˜šžœžœž˜Kšœ8žœ˜?Kšœ.˜.KšœU˜UKšœT˜TK˜SKšœR˜R˜ K˜+Kšžœžœ%˜:šžœ˜K™9Kšžœžœ#˜HKšœžœ˜(K˜K˜K˜—K˜—KšœM˜MKšœS˜SKš œžœžœžœžœ˜RK˜K˜.Kšœx˜xKšžœžœ˜—Kš œ!žœ žœžœžœžœ˜DK˜K˜—š Ÿ œžœ žœ$žœžœ˜XKšžœ žœž™:K™"K™K™+KšœTžœžœ:žœ™ฑKšœžœžœžœ˜GKšœžœžœ˜ šžœžœž˜Kš œJžœ žœžœ žœ˜qKšœZžœ˜aKšœTžœžœ˜fKšœYžœ˜`˜ K˜+Kšžœžœ,žœ˜Gšžœ˜Kšžœžœ#˜HKšœžœ˜(K˜Kšœ%žœ˜,K˜—K˜—šœIžœ˜hKš œžœžœ%žœ žœ˜X—KšœXžœ˜_˜[Kšœžœžœ+žœ˜P—Kšžœžœžœ˜2—Kšœ žœžœ˜)K˜K˜—šŸ œžœ žœ$žœ˜PK™ K™K™ K™4šžœžœž˜šœ˜Kšœˆ™ˆKšœžœ˜$šžœžœž˜šžœ&žœž˜5šœ˜Kšœ)˜)šžœžœžœ˜šžœ˜Kšœ-™-—Kšœ˜Kšžœ˜K˜—K˜—Kšžœ˜—Kšœ˜Kšžœ˜—Kšœ˜—Kšžœžœžœ˜2—˜K˜——šŸœžœ žœ$žœ˜UK™ K™K™ Kšœ7™7š Ÿ œžœžœžœžœ˜,Kšœ!™!šžœžœžœ˜Kšžœ žœ žœ*˜DKšœ&˜&K˜—šžœ˜Kšœ(˜(šžœž˜ Kšœ.˜.Kšœ.˜.Kšœ.˜.Kšœ.˜.Kšœ.˜.šžœ˜ Kšœ8˜8Kšœ1žœ ˜>Kšœ1žœ˜7Kšœ˜——K˜—Kšžœžœ˜K˜—šžœžœž˜KšœM˜MK˜:Kšžœžœžœ˜2—˜K˜——šŸœžœ žœ$žœ˜VK™K™K™ KšœžœM™gKšœ žœ˜ K˜!Kšœžœžœ˜ Kšœžœ˜ šžœžœž˜˜Kšžœ žœžœžœžœžœžœ#˜JKšœ˜K˜—Kšœ&˜&šœ/˜/Kšœžœžœ#˜AKšœ˜—Kšžœžœžœ˜2—Kšœ)˜)Kšœ+˜+Kšœžœžœ˜Kšžœžœžœ˜,Kšœžœžœžœ˜bK˜K˜ Kšœ žœžœ˜)K˜K˜—šŸ œžœ žœ$žœ˜NK™K™K™ Kšœd™dKšœ žœ˜Kšœžœžœžœ˜šžœžœž˜KšœW˜WKšœX˜X˜šœžœ-˜4Kšžœžœ%˜-—Kšžœžœžœžœ#˜:Kšœ>˜>K˜—K˜Kšžœžœžœ˜2—Kšœ.˜.šžœž˜KšœIžœ˜OKšžœO˜V—Kšœžœžœ˜,K˜K˜——™•StartOfExpansionG -- [stream: STREAM, val: IO.Value, format: IOUtils.Format, char: CHAR]šŸ œ˜%KšะckC™CKšœžœžœ˜Kšœ žœžœ˜Kšœ žœ˜Kšœ žœ˜Kšœ\˜\šžœ ž˜Kšœ˜KšœA˜AKšžœ˜—šžœžœž˜šœžœ ˜šœžœžœžœžœ žœžœžœ˜Nšžœž˜Kšœ-˜-Kšžœžœ˜——Kšœ žœžœ*˜;Kšœžœžœ%˜5Kšœžœ˜Kšœžœ˜"Kšžœ žœžœžœ˜AKšžœ˜Kšžœ žœžœ˜=Kšœ ˜ K˜—Kšžœ%˜,—K˜K˜—šœ™K˜š Ÿœžœžœ žœžœ˜7Kšžœ žœžœžœ˜4K˜K˜—šŸ œžœžœžœ žœžœžœžœžœ˜QKšžœ žœ(žœ˜EKšžœ˜K˜K˜———™šŸ œžœžœžœžœžœ žœ˜_Kšœา™าK™8KšŸœžœžœžœ˜8Kšœ žœ˜Kšœžœžœ˜Kšœžœ )˜CKšœžœ˜ Kšœ žœ˜šŸœžœžœ˜$K˜!Kšœžœ˜K˜—Kšžœžœžœžœ˜@K˜šžœž˜K˜-Kšœžœ˜+Kšžœ˜—K˜šž˜šžœž˜ Kšžœ=˜?Kšžœžœ˜—Kšžœ˜—šžœž˜Kšœžœžœ˜Kšžœžœ$˜1—Kšžœ žœžœ˜Cš žœžœžœžœž˜"K˜Kšžœ˜—šžœ ˜ šžœ˜šžœžœžœž˜Kš œžœžœžœ žœ ˜.šžœ˜ šžœ˜K˜ Kšœ žœžœ˜!K˜—Kšžœ ˜—Kšž˜—K˜—šžœ˜šžœž˜ Kšžœžœ˜Kšžœžœ˜—K˜——Kš žœžœžœžœ žœ˜1Kšžœžœžœ˜.Kšœ˜——™šŸœžœžœ˜Kšœžœ˜(Kšœ-˜-Kšœ,˜,Kšœ,˜,Kšœ,˜,Kšœ,˜,Kšœ,˜,Kšœ,˜,Kšœ,˜,Kšœ,˜,Kšœ+˜+Kšœ1˜1Kšœ2˜2Kšœ,˜,Kšœ*˜*Kšœ*˜*Kšœ,˜,Kšœ˜——˜˜ K˜—Kšžœ˜—K˜—…—XŠ