<> <> <> <> <> DIRECTORY Ascii USING [CR, SP, TAB], BasicTime USING [minutesPerHour, nullGMT, OutOfRange, Pack, TimeParametersNotKnown, Unpacked, Unpack, unspecifiedZone], DFUtilities USING [CommentItem, Date, DirectoryItem, FileItem, Filter, FilterA, FilterB, FilterC, ImportsItem, IncludeItem, ProcessItemProc, SupplyItemProc, UsingEntry, UsingForm, UsingList, WhiteSpaceItem], IO USING [Backup, BreakProc, EndOfStream, Error, GetChar, GetLineRope, GetToken, GetUnpackedTime, PeekChar, PutChar, PutF, PutFR, PutRope, STREAM, TokenProc], RefText USING [Equal, Fetch, Length, ObtainScratch, ReleaseScratch, TrustTextAsRope], Rope USING [Compare, Concat, Fetch, FromRefText, InlineFetch, Length, ROPE, Run, Substr]; DFUtilitiesImpl: CEDAR PROGRAM IMPORTS BasicTime, IO, RefText, Rope EXPORTS DFUtilities = BEGIN ROPE: TYPE = Rope.ROPE; STREAM: TYPE = IO.STREAM; <> ParseFromStream: PUBLIC PROC [in: STREAM, proc: DFUtilities.ProcessItemProc, filter: DFUtilities.Filter _ []] = { Abort: ERROR = CODE; ParseInner: PROC = { passFileItems: BOOL _ TRUE; underDirectory: BOOL _ FALSE; blankLineCount: NAT _ 0; <> bufferT: REF TEXT = RefText.ObtainScratch[20]; bufferN: REF TEXT = RefText.ObtainScratch[100]; SimpleToken: IO.BreakProc -- [char: CHAR] RETURNS [IO.CharClass] -- = { SELECT char FROM Ascii.SP, Ascii.TAB => RETURN[$sepr]; Ascii.CR => RETURN[$break]; ENDCASE => RETURN[$other]; }; UsingToken: IO.BreakProc -- [char: CHAR] RETURNS [IO.CharClass] -- = { SELECT char FROM '- => RETURN[$other]; ENDCASE => RETURN[IO.TokenProc[char]]; }; GetTokenAsRefText: PROC [breakProc: IO.BreakProc] RETURNS [token: REF TEXT _ "\N"] = { token _ in.GetToken[breakProc, bufferT ! IO.EndOfStream => CONTINUE].token; }; GetToken: PROC [breakProc: IO.BreakProc] RETURNS [ROPE] = { token: REF TEXT _ "\N"; token _ in.GetToken[breakProc, bufferN ! IO.EndOfStream => CONTINUE].token; RETURN[Rope.FromRefText[token]] }; PutBack: PROC [x: REF TEXT] = { FOR i: INT DECREASING IN [0..RefText.Length[x]) DO in.Backup[RefText.Fetch[x, i]]; ENDLOOP; }; EndOfLineText: PROC [t: REF TEXT] RETURNS [BOOL] = { RETURN[RefText.Length[t] = 1 AND RefText.Fetch[t, 0] = Ascii.CR] }; EndOfLineRope: PROC [t: ROPE] RETURNS [BOOL] = { RETURN[t.Length[] = 1 AND t.Fetch[0] = Ascii.CR] }; CheckEndOfLine: PROC = { IF ~EndOfLineText[GetTokenAsRefText[SimpleToken]] THEN ERROR SyntaxError["Unrecognizable text where end-of-line was expected."]; }; FlushWhiteSpace: PROC = { IF blankLineCount ~= 0 AND filter.comments THEN { IF proc[NEW[DFUtilities.WhiteSpaceItem _ [lines: blankLineCount]]] THEN ERROR Abort; CancelWhiteSpace[]; }; }; CancelWhiteSpace: PROC = {blankLineCount _ 0}; CancelDirectory: PROC = {underDirectory _ FALSE}; GetDate: PROC RETURNS [DFUtilities.Date] = { DO char: CHAR = in.GetChar[ ! IO.EndOfStream => GO TO omitted]; SELECT char FROM Ascii.SP, Ascii.TAB => NULL; -- leading white space Ascii.CR => {in.Backup[char]; GO TO omitted}; '# => RETURN[[$notEqual, BasicTime.nullGMT]]; '~ => IF in.GetChar[] = '= THEN RETURN[[$notEqual, BasicTime.nullGMT]] ELSE GO TO bogus; '> => RETURN[[$greaterThan, BasicTime.nullGMT]]; ENDCASE => { up: BasicTime.Unpacked; in.Backup[char]; up _ in.GetUnpackedTime[ ! IO.Error => GO TO bogus]; <> IF up.year = 0 OR up.hour = 24 OR up.zone = BasicTime.unspecifiedZone OR up.dst = unspecified THEN GO TO bogus; RETURN[[$explicit, BasicTime.Pack[unpacked: up]]] }; ENDLOOP; EXITS omitted => RETURN[[$omitted, BasicTime.nullGMT]]; bogus => ERROR SyntaxError["Illegal date specification."]; }; ParseDirectoryItem: PROC [exported: BOOL, readOnly: BOOL, path1: ROPE _ NIL] RETURNS [REF DFUtilities.DirectoryItem] = { directoryFilterB: DFUtilities.FilterB = IF exported THEN $public ELSE $private; directoryFilterC: DFUtilities.FilterC = IF readOnly THEN $imported ELSE $defining; ConsiderDefiningInstance: PROC RETURNS [BOOL] = { RETURN[ (filter.filterB = $all OR filter.filterB = directoryFilterB) AND (filter.filterC = $all OR filter.filterC = directoryFilterC) ]; }; path2: ROPE _ NIL; path2IsCameFrom: BOOL _ FALSE; x: REF TEXT; IF path1 = NIL AND EndOfLineRope[path1 _ GetToken[SimpleToken]] THEN ERROR SyntaxError["Missing directory path."]; IF ~EndOfLineText[x _ GetTokenAsRefText[SimpleToken]] THEN { SELECT TRUE FROM RefText.Equal[x, "ReleaseAs", FALSE] => path2IsCameFrom _ FALSE; RefText.Equal[x, "CameFrom", FALSE] => path2IsCameFrom _ TRUE; ENDCASE => ERROR SyntaxError["Unrecognized construct following directory path."]; IF EndOfLineRope[path2 _ GetToken[SimpleToken]] THEN RaiseSyntaxError["Missing directory path following", x]; CheckEndOfLine[]; }; underDirectory _ TRUE; RETURN[ IF (passFileItems _ ConsiderDefiningInstance[]) THEN NEW[DFUtilities.DirectoryItem _ [ path1: path1, path2: path2, path2IsCameFrom: path2IsCameFrom, exported: exported, readOnly: readOnly ]] ELSE NIL ] }; ParseFileItem: PROC [verifyRoot: BOOL _ FALSE, name: ROPE _ NIL] RETURNS [REF DFUtilities.FileItem] = { PassesNameFilter: PROC [file: ROPE] RETURNS [BOOL] = { SELECT TRUE FROM filter.list # NIL => {}; filter.filterA = $all => {}; ClassifyFileExtension[file] = filter.filterA => {}; ENDCASE => RETURN[FALSE]; RETURN[SearchUsingList[file, filter.list].found] }; date: DFUtilities.Date; IF ~underDirectory THEN ERROR SyntaxError["Missing directory statement"]; IF name = NIL AND EndOfLineRope[name _ GetToken[SimpleToken]] THEN ERROR SyntaxError["Missing file name."]; date _ GetDate[]; CheckEndOfLine[]; RETURN[ IF passFileItems AND PassesNameFilter[RemoveVersionNumber[name]] THEN NEW[DFUtilities.FileItem _ [ name: name, date: date, verifyRoot: verifyRoot ]] ELSE NIL ] }; ParseImportsItem: PROC [exported: BOOL] RETURNS [REF DFUtilities.ImportsItem] = { x: REF TEXT; path1, path2: ROPE _ NIL; date: DFUtilities.Date; form: DFUtilities.UsingForm _ $exports; list: REF DFUtilities.UsingList _ NIL; ConsiderImports: PROC RETURNS [BOOL] = { IF filter.filterC = $defining THEN RETURN[FALSE]; IF exported AND filter.list # NIL THEN { <> RETURN [form = $all OR form = $exports OR list # NIL]; }; SELECT filter.filterB FROM $private => IF exported OR form = $exports THEN RETURN[FALSE]; $public => IF ~exported THEN RETURN[FALSE]; ENDCASE; RETURN[~(form = $list AND list = NIL)] }; IF EndOfLineRope[path1 _ GetToken[SimpleToken]] THEN ERROR SyntaxError["Missing file name."]; IF EndOfLineText[x _ GetTokenAsRefText[SimpleToken]] OR ~RefText.Equal[x, "Of", FALSE] THEN ERROR SyntaxError["Missing 'Of' following DF name"]; date _ GetDate[]; CheckEndOfLine[]; x _ GetTokenAsRefText[SimpleToken]; IF RefText.Equal[x, "CameFrom", FALSE] THEN { IF EndOfLineRope[path2 _ GetToken[SimpleToken]] THEN RaiseSyntaxError["Missing directory path following", x]; CheckEndOfLine[]; x _ GetTokenAsRefText[SimpleToken]; }; IF RefText.Equal[x, "Using", FALSE] THEN { x _ GetTokenAsRefText[UsingToken]; SELECT TRUE FROM RefText.Equal[x, "All", FALSE] => form _ $all; RefText.Equal[x, "Exports", FALSE] => --form _ $exports-- NULL; RefText.Equal[x, "[", FALSE] => { verifyRoot: BOOL _ FALSE; form _ $list; DO index: NAT; inList: BOOL; x _ GetTokenAsRefText[UsingToken]; IF RefText.Length[x] = 1 THEN SELECT RefText.Fetch[x, 0] FROM '] => EXIT; '+ => IF ~verifyRoot THEN {verifyRoot _ TRUE; LOOP} ELSE ERROR SyntaxError["Illegally placed '+' in 'Using' list."]; ENDCASE; [inList, index] _ SearchUsingList[RefText.TrustTextAsRope[x], filter.list]; IF inList THEN { <> SELECT TRUE FROM list = NIL => { length: NAT = IF filter.list = NIL THEN 20 ELSE filter.list.nEntries; list _ NEW[DFUtilities.UsingList[length]]; list.nEntries _ 0; }; list.nEntries = list.length => { <> newList: REF DFUtilities.UsingList _ NEW[DFUtilities.UsingList[(list.length*3)/2]]; newList.nEntries _ list.nEntries; FOR i: NAT IN [0..list.nEntries) DO newList.u[i] _ list.u[i]; ENDLOOP; list _ newList; }; ENDCASE; list.u[list.nEntries] _ [ verifyRoot: verifyRoot, name: IF filter.list = NIL THEN Rope.FromRefText[x] ELSE filter.list.u[index].name ]; list.nEntries _ list.nEntries.SUCC; }; verifyRoot _ FALSE; ENDLOOP; }; ENDCASE => ERROR SyntaxError["Unrecognized construct following 'Using'."]; CheckEndOfLine[]; } ELSE { PutBack[x]; --form _ $exports-- }; CancelDirectory[]; RETURN [ IF ConsiderImports[] THEN NEW[DFUtilities.ImportsItem _ [ path1: path1, date: date, path2: path2, exported: exported, form: form, list: list ]] ELSE NIL ] }; ParseIncludeItem: PROC RETURNS [REF DFUtilities.IncludeItem] = { item: REF DFUtilities.IncludeItem = NEW[DFUtilities.IncludeItem _ [ path1: GetToken[SimpleToken], date: , path2: NIL, path2IsCameFrom: ]]; x: REF TEXT; IF EndOfLineRope[item.path1] THEN ERROR SyntaxError["Missing file name."]; IF EndOfLineText[x _ GetTokenAsRefText[SimpleToken]] OR ~RefText.Equal[x, "Of", FALSE] THEN ERROR SyntaxError["Missing 'Of' following DF name"]; item.date _ GetDate[]; CheckEndOfLine[]; x _ GetTokenAsRefText[SimpleToken]; SELECT TRUE FROM RefText.Equal[x, "ReleaseAs", FALSE] => item.path2IsCameFrom _ FALSE; RefText.Equal[x, "CameFrom", FALSE] => item.path2IsCameFrom _ TRUE; ENDCASE => {PutBack[x]; RETURN[item]}; IF EndOfLineRope[item.path2 _ GetToken[SimpleToken]] THEN RaiseSyntaxError["Missing directory path following", x]; CheckEndOfLine[]; CancelDirectory[]; RETURN[item] }; <
> DO item: REF ANY _ NIL; char: CHAR = in.GetChar[ ! IO.EndOfStream => EXIT]; SELECT char FROM Ascii.SP, Ascii.TAB => LOOP; -- leading white space on line Ascii.CR => { <> IF filter.comments THEN blankLineCount _ blankLineCount.SUCC; LOOP }; '+ => { FlushWhiteSpace[]; item _ ParseFileItem[verifyRoot: TRUE]; }; '- => { FlushWhiteSpace[]; IF in.PeekChar[] = '- THEN { <> comment: ROPE = Rope.Concat["-", in.GetLineRope[]]; IF filter.comments THEN item _ NEW[DFUtilities.CommentItem _ [text: comment]]; } ELSE {in.Backup[char]; item _ ParseFileItem[]}; }; '/ => { <> FlushWhiteSpace[]; IF in.GetChar[] = '/ THEN { comment: ROPE _ NIL; comment _ in.GetLineRope[ ! IO.EndOfStream => CONTINUE]; <> IF filter.comments THEN item _ NEW[DFUtilities.CommentItem _ [text: Rope.Concat["--", comment]]]; } ELSE ERROR SyntaxError["'/' is illegal at the start of a line."]; }; ENDCASE => { x: REF TEXT; in.Backup[char]; x _ GetTokenAsRefText[SimpleToken]; SELECT TRUE FROM RefText.Equal[x, "Directory", FALSE] => item _ ParseDirectoryItem[exported: FALSE, readOnly: FALSE]; RefText.Equal[x, "ReadOnly", FALSE] => item _ ParseDirectoryItem[exported: FALSE, readOnly: TRUE]; RefText.Equal[x, "Imports", FALSE] => item _ ParseImportsItem[exported: FALSE]; RefText.Equal[x, "Include", FALSE], RefText.Equal[x, "Includes", FALSE] => item _ ParseIncludeItem[]; RefText.Equal[x, "Exports", FALSE] => { IF EndOfLineText[x _ GetTokenAsRefText[SimpleToken]] THEN ERROR SyntaxError["Missing directory path following 'Exports'."]; SELECT TRUE FROM RefText.Equal[x, "Directory", FALSE] => item _ ParseDirectoryItem[exported: TRUE, readOnly: FALSE]; RefText.Equal[x, "ReadOnly", FALSE] => item _ ParseDirectoryItem[exported: TRUE, readOnly: TRUE]; RefText.Equal[x, "Imports", FALSE] => item _ ParseImportsItem[exported: TRUE]; ENDCASE => item _ ParseDirectoryItem[ exported: TRUE, readOnly: FALSE, path1: Rope.FromRefText[x]]; }; ENDCASE => item _ ParseFileItem[name: Rope.FromRefText[x]]; }; IF item ~= NIL THEN { FlushWhiteSpace[]; IF proc[item] THEN ERROR Abort; } ELSE CancelWhiteSpace[]; ENDLOOP; RefText.ReleaseScratch[bufferN]; RefText.ReleaseScratch[bufferT]; }; SortUsingList[usingList: filter.list, nearlySorted: TRUE]; ParseInner[ ! Abort => CONTINUE; IO.EndOfStream => ERROR SyntaxError["Unexpected end of DF."]; IO.Error => ERROR SyntaxError[NIL]; ] }; SyntaxError: PUBLIC ERROR [reason: ROPE] = CODE; WriteToStream: PUBLIC PROC [out: STREAM, proc: DFUtilities.SupplyItemProc] = { haveDirectory: BOOL _ FALSE; DO item: REF ANY = proc[]; IF item = NIL THEN EXIT; WITH item SELECT FROM directory: REF DFUtilities.DirectoryItem => haveDirectory _ TRUE; file: REF DFUtilities.FileItem => IF ~haveDirectory THEN ERROR SyntaxError["File item without preceding Directory item."]; imports: REF DFUtilities.ImportsItem => haveDirectory _ FALSE; include: REF DFUtilities.IncludeItem => haveDirectory _ FALSE; ENDCASE; WriteItemToStream[out, item]; ENDLOOP; }; WriteItemToStream: PUBLIC PROC [out: STREAM, item: REF ANY] = { maxUsingLineLength: INT = 90; maxReasonableFileNameLength: INT = 45; WITH item SELECT FROM directory: REF DFUtilities.DirectoryItem => { IF directory.exported THEN out.PutRope["Exports "]; IF directory.readOnly THEN out.PutRope["ReadOnly "]; IF ~(directory.exported OR directory.readOnly) THEN out.PutRope["Directory "]; out.PutRope[directory.path1]; }; file: REF DFUtilities.FileItem => { out.PutRope[" "]; out.PutChar[IF file.verifyRoot THEN '+ ELSE Ascii.SP]; out.PutRope[file.name]; THROUGH [0..MAX[maxReasonableFileNameLength-file.name.Length[], 1]) DO out.PutChar[Ascii.SP]; ENDLOOP; DateToStream[out, file.date]; }; imports: REF DFUtilities.ImportsItem => { IF imports.exported THEN out.PutRope["Exports "]; out.PutRope["Imports "]; out.PutRope[imports.path1]; out.PutRope[" Of "]; DateToStream[out, imports.date]; SELECT imports.form FROM <> <> exports => NULL; all => out.PutRope["\N Using All"]; list => { intro: ROPE = "\N Using ["; charsOnLine: INT _ intro.Length[].PRED; out.PutRope[intro]; IF imports.list ~= NIL THEN { FOR i: NAT IN [0..imports.list.nEntries) DO <> entry: DFUtilities.UsingEntry = imports.list.u[i]; IF charsOnLine + entry.name.Length[] > maxUsingLineLength THEN { indent: ROPE = "\N "; IF i ~= 0 THEN out.PutChar[',]; out.PutRope[indent]; charsOnLine _ indent.Length[].PRED; } ELSE IF i ~= 0 THEN {out.PutRope[", "]; charsOnLine _ charsOnLine + 2}; IF entry.verifyRoot THEN {out.PutChar['+]; charsOnLine _ charsOnLine.SUCC}; out.PutRope[entry.name]; charsOnLine _ charsOnLine + entry.name.Length[]; ENDLOOP; }; out.PutChar[']]; }; ENDCASE; }; include: REF DFUtilities.IncludeItem => { out.PutRope["Include "]; out.PutRope[include.path1]; out.PutRope[" Of "]; DateToStream[out, include.date]; }; comment: REF DFUtilities.CommentItem => out.PutRope[comment.text]; whiteSpace: REF DFUtilities.WhiteSpaceItem => THROUGH [0..whiteSpace.lines-1) DO out.PutChar[Ascii.CR]; ENDLOOP; ENDCASE => ERROR SyntaxError["Unrecognizable item."]; out.PutChar[Ascii.CR]; }; SortUsingList: PUBLIC PROC [usingList: REF DFUtilities.UsingList, nearlySorted: BOOL _ FALSE] = { IF usingList = NIL THEN RETURN; IF nearlySorted THEN { <> FOR i: NAT IN [1..usingList.nEntries) DO item: DFUtilities.UsingEntry = usingList.u[i]; j: NAT _ i; -- 'j' can't be a loop control variable; we want its value on termination. WHILE Rope.Compare[item.name, usingList.u[j-1].name, FALSE] = less DO usingList.u[j] _ usingList.u[j-1]; IF (j _ j.PRED) = 0 THEN EXIT; ENDLOOP; IF j ~= i THEN usingList.u[j] _ item; -- test avoids unnecessary reference counting. ENDLOOP; } ELSE { <> SiftUp: PROC [low, high: NAT] = { k: NAT _ low; DO twoK: NAT = k*2; son: NAT _ twoK; IF twoK > high THEN EXIT; IF twoK+1 <= high AND Rope.Compare[usingList.u[twoK+1-1].name, usingList.u[twoK-1].name, FALSE] ~= less THEN son _ twoK+1; IF Rope.Compare[usingList.u[son-1].name, usingList.u[k-1].name, FALSE] = less THEN EXIT; Exchange[son-1, k-1]; k _ son; ENDLOOP; }; Exchange: PROC [a, b: NAT] = { temp: DFUtilities.UsingEntry = usingList.u[a]; usingList.u[a] _ usingList.u[b]; usingList.u[b] _ temp; }; FOR i: NAT DECREASING IN [1..usingList.nEntries/2] DO SiftUp[i, usingList.nEntries]; ENDLOOP; FOR i: NAT DECREASING IN [1..usingList.nEntries) DO Exchange[0, i]; SiftUp[1, i]; ENDLOOP; }; }; SearchUsingList: PUBLIC PROC [file: ROPE, list: REF DFUtilities.UsingList] RETURNS [found: BOOL _ FALSE, index: NAT] = { <> IF list = NIL THEN found _ TRUE -- NIL list is interpreted as "everything" ELSE { low: INTEGER _ 0; high: INTEGER _ list.nEntries.PRED; UNTIL low > high DO probe: NAT _ (low + high) / 2; SELECT file.Compare[list.u[probe].name, FALSE] FROM equal => RETURN[TRUE, probe]; less => IF probe = 0 THEN EXIT ELSE high _ probe.PRED; greater => IF probe = list.nEntries.PRED THEN EXIT ELSE low _ probe.SUCC; ENDCASE; ENDLOOP; found _ FALSE; }; }; DifferenceOfUsingLists: PUBLIC PROC [a, b: REF DFUtilities.UsingList] RETURNS [diff: REF DFUtilities.UsingList] = { aI, bI: NAT _ 0; aL: NAT = a.nEntries; bL: NAT = b.nEntries; diff _ NEW[DFUtilities.UsingList[a.nEntries]]; diff.nEntries _ 0; UNTIL aI = aL OR bI = bL DO SELECT a.u[aI].name.Compare[b.u[bI].name, FALSE] FROM equal => {aI _ aI.SUCC; bI _ bI.SUCC}; less => { diff.u[diff.nEntries] _ a.u[aI]; diff.nEntries _ diff.nEntries.SUCC; aI _ aI.SUCC; }; greater => bI _ bI.SUCC; ENDCASE; ENDLOOP; UNTIL aI = aL DO diff.u[diff.nEntries] _ a.u[aI]; diff.nEntries _ diff.nEntries.SUCC; aI _ aI.SUCC; ENDLOOP; }; DateToRope: PUBLIC PROC [date: DFUtilities.Date] RETURNS [ROPE _ NIL] = { SELECT date.format FROM $explicit => { months: ROPE = "JanFebMarAprMayJunJulAugSepOctNovDec"; up: BasicTime.Unpacked = BasicTime.Unpack[date.gmt ! BasicTime.OutOfRange, BasicTime.TimeParametersNotKnown => GO TO noDate]; ConvertZone: PROC RETURNS [ROPE] = { dst: BOOL = up.dst = yes; SELECT up.zone FROM 0 => IF ~dst THEN RETURN["GMT"]; NAT[5*BasicTime.minutesPerHour] => RETURN[IF dst THEN "EDT" ELSE "EST"]; NAT[6*BasicTime.minutesPerHour] => RETURN[IF dst THEN "CDT" ELSE "CST"]; NAT[7*BasicTime.minutesPerHour] => RETURN[IF dst THEN "MDT" ELSE "MST"]; NAT[8*BasicTime.minutesPerHour] => RETURN[IF dst THEN "PDT" ELSE "PST"]; ENDCASE; RETURN[ IO.PutFR["%g%02d%02d", [character[IF up.zone < 0 THEN '- ELSE '+]], [cardinal[up.zone.ABS/BasicTime.minutesPerHour]], [cardinal[up.zone.ABS MOD BasicTime.minutesPerHour]] ] ] }; RETURN[ IO.PutFR[ "%02d-%g-%02d %g", [cardinal[up.day]], [rope[months.Substr[start: up.month.ORD*3, len: 3]]], [cardinal[up.year MOD 100]], [rope[IO.PutFR[ "%02d:%02d:%02d %g", [cardinal[up.hour]], [cardinal[up.minute]], [cardinal[up.second]], [rope[ConvertZone[]]] ]]] ] ] }; $notEqual => RETURN["~="]; $greaterThan => RETURN[">"]; ENDCASE; EXITS noDate => NULL; }; DateToStream: PUBLIC PROC [s: STREAM, date: DFUtilities.Date] = { SELECT date.format FROM $explicit => { months: ROPE = "JanFebMarAprMayJunJulAugSepOctNovDec"; up: BasicTime.Unpacked = BasicTime.Unpack[date.gmt ! BasicTime.OutOfRange, BasicTime.TimeParametersNotKnown => GO TO noDate]; ConvertZone: PROC = { dst: BOOL = up.dst = yes; SELECT up.zone FROM 0 => IF ~dst THEN s.PutRope["GMT"]; NAT[5*BasicTime.minutesPerHour] => s.PutRope[IF dst THEN "EDT" ELSE "EST"]; NAT[6*BasicTime.minutesPerHour] => s.PutRope[IF dst THEN "CDT" ELSE "CST"]; NAT[7*BasicTime.minutesPerHour] => s.PutRope[IF dst THEN "MDT" ELSE "MST"]; NAT[8*BasicTime.minutesPerHour] => s.PutRope[IF dst THEN "PDT" ELSE "PST"]; ENDCASE => s.PutF["%g%02d%02d", [character[IF up.zone < 0 THEN '- ELSE '+]], [cardinal[up.zone.ABS/BasicTime.minutesPerHour]], [cardinal[up.zone.ABS MOD BasicTime.minutesPerHour]] ] }; s.PutF["%02d-%g-%02d ", [cardinal[up.day]], [rope[months.Substr[start: up.month.ORD*3, len: 3]]], [cardinal[up.year MOD 100]] ]; s.PutF["%02d:%02d:%02d ", [cardinal[up.hour]], [cardinal[up.minute]], [cardinal[up.second]]]; ConvertZone[]; }; $notEqual => s.PutRope["~="]; $greaterThan => s.PutChar['>]; ENDCASE; EXITS noDate => NULL; }; derivedList: LIST OF ROPE _ LIST[".bcd", ".boot", ".press", ".signals"]; ClassifyFileExtension: PUBLIC PROC [file: ROPE] RETURNS [DFUtilities.FilterA] = { dot, bang, len: INT; [dot, bang, len] _ FindPlaces[file]; IF dot >= bang THEN RETURN [$source]; len _ bang-dot; FOR each: LIST OF ROPE _ derivedList, each.rest WHILE each # NIL DO IF Rope.Run[each.first, 0, file, dot, FALSE] = len THEN RETURN [$derived]; ENDLOOP; RETURN[$source] }; GetVersionNumber: PUBLIC PROC [r: ROPE] RETURNS [ROPE] = { RETURN[r.Substr[start: FindVersionNumber[r]]] }; RemoveVersionNumber: PUBLIC PROC [r: ROPE] RETURNS [ROPE] = { RETURN[r.Substr[len: FindVersionNumber[r]]] }; FindVersionNumber: PROC [r: ROPE] RETURNS [INT] = { len: INT _ Rope.Length[r]; pos: INT _ len; WHILE (pos _ pos-1) >= 0 DO SELECT Rope.InlineFetch[r, pos] FROM '! => RETURN [pos]; '., '>, '] => RETURN [len]; ENDCASE; ENDLOOP; RETURN [len]; }; <<>> FindPlaces: PROC [r: ROPE] RETURNS [dot, bang, len: INT] = { bang _ dot _ len _ Rope.Length[r]; WHILE dot # 0 DO SELECT Rope.InlineFetch[r, dot _ dot-1] FROM '! => bang _ dot; '. => RETURN; '>, '] => EXIT; ENDCASE; ENDLOOP; dot _ len; }; <<>> RaiseSyntaxError: PROC [prefix: ROPE, text: REF TEXT] = { ERROR SyntaxError[IO.PutFR["%g '%g'.", [rope[prefix]], [text[text]] ] ]; }; <<>> END.