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. DFUtilitiesImpl.mesa Copyright c 1984, 1985 by Xerox Corporation. All rights reserved. Levin on December 7, 1983 11:00 am Russ Atkinson, March 19, 1985 1:21:01 pm PST Doug Wyatt, December 28, 1984 3:57:57 pm PST (add UsingToken in ParseFromStream) Exported Procedures bufferT is used for terminals, like "Directory". bufferN is used for non-terminals, like file names, dates, and the like. Separate buffers are used to simplify the REF TEXT handling by clients of GetTokenAsRefText. They are not released if ParseInner exits with an uncaught signal, but this is claimed to be OK. Since GetUnpackedTime didn't raise Error, we must have either a complete date or a complete time (with or without zone) or both. We want to complain unless both were specified. Therefore, it suffices to test one field each of the date and time for "out-of-bounds" values. RRA sez: we should consider the imports of any imports item being exported when there is an explicit filter list. Of course, if there is a null intersection (list = NIL), we don't have to consider it unless the form dictates it. enter name in list This case can only happen if filter.list = NIL Main parsing loop blank line Comment line Remove this code when old-style comments are no longer supported. convert to new style When compatibility is no longer needed, replace the following NULL with out.PutRope["\N Using Exports"] The following is an approximate check; the line may get a bit longer than `maxUsingLineLength'. Insertion sort Heap sort Do a binary search for "file" in "list". Κ7˜codešœ™Kšœ Οmœ7™BKšœ"™"K™,K™P—K˜šΟk ˜ Kš œžœžœžœžœ˜Kšœ žœh˜wKšœ žœΎ˜ΟKšžœžœƒžœ ˜žKšœžœH˜UKšœžœ<žœ˜Y—K˜šœžœž˜Kšžœ žœ˜$Kšžœž˜—˜Kšžœžœžœ˜Kšžœžœžœžœ˜—K˜Kšœ™K˜šΟnœžœžœžœI˜qKšœžœžœ˜šŸ œžœ˜K•StartOfExpansion+ -- [char: CHAR] RETURNS [IO.CharClass] -- šœžœžœ˜Kšœžœžœ˜Kšœžœ˜Kšœ¦žœžœ‰žœ™ΊKšœ žœžœ˜.Kšœ žœžœ˜/šœ žœ Πck*œ˜Hšžœž˜Kšœžœžœžœ˜%Kšœžœžœ ˜Kšžœžœ ˜—K˜—šœ žœ  *œ˜Gšžœž˜Kšœžœ ˜Kšžœžœžœ˜&—K˜—šŸœžœ žœ ˜1Kšžœ žœžœ ˜$Kšœ)žœžœ˜KK˜—š Ÿœžœ žœ žœžœ˜;Kšœžœžœ˜Kšœ)žœžœ˜KKšžœ˜K˜—šŸœžœžœžœ˜š žœžœž œžœž˜2Kšœ˜Kšžœ˜—K˜—š Ÿ œžœžœžœžœžœ˜4Kšžœžœžœ˜@Kšœ˜—š Ÿ œžœžœžœžœ˜0Kšžœžœžœ˜0Kšœ˜—šŸœžœ˜šžœ0ž˜6KšžœD˜I—Kšœ˜—šŸœžœ˜šžœžœžœ˜1Kšžœžœ8žœžœ˜TKšœ˜K˜—K˜—KšŸœžœ˜.KšŸœžœžœ˜1šŸœžœžœ˜,šž˜Kš œžœžœžœžœ ˜<šžœž˜KšœžœžœžœΟc˜4Kšœžœžœžœ ˜-Kšœžœ!˜-šœ˜Kšžœžœžœ ˜@Kšžœžœžœ˜—Kšœžœ$˜0šžœ˜ Kšœ˜Kšœ˜Kšœžœ žœžœ˜4Kšœ‘™‘šžœ žœž˜!Kš œ$žœžœžœžœ˜M—Kšžœ+˜1K˜——Kšžœ˜—šž˜Kšœ žœ ˜1Kšœ žœ,˜:—K˜—šŸœžœ žœ žœ žœžœžœžœ ˜yKšœ(žœ žœ žœ ˜OKšœ(žœ žœ žœ ˜RšŸœžœžœžœ˜1šžœ˜Kšœžœ$ž˜@Kšœžœ#˜KšžœžœA˜Q—šžœ.ž˜4Kšœ8˜8—K˜K˜—Kšœžœ˜šžœ˜šžœ.ž˜4šžœ˜!Kšœ ˜ Kšœ ˜ Kšœ!˜!Kšœ˜Kšœ˜Kšœ˜——Kšžœž˜K˜—K˜—šŸ œžœžœžœžœžœžœžœ˜gš Ÿœžœžœžœžœ˜6šžœžœž˜Kšœžœ˜Kšœ˜Kšœ3˜3Kšžœžœžœ˜—Kšžœ*˜0Kšœ˜—Kšœ˜Kšžœžœžœ,˜Išžœžœžœ-ž˜BKšžœ#˜(—Kšœ˜K˜šžœ˜šžœžœ-ž˜Ešžœ˜Kšœ ˜ Kšœ ˜ Kšœ˜Kšœ˜——Kšžœž˜Kšœ˜—K˜—š Ÿœžœ žœžœžœ˜QKšœžœ˜ Kšœžœžœ˜Kšœ˜Kšœ'˜'Kšœžœžœ˜&šŸœžœžœžœ˜(Kšžœžœžœžœ˜1šžœ žœžœžœ˜(Kšžœ£žœ<™εKšžœžœžœžœ˜6K˜—šžœž˜Kš œ žœ žœžœžœžœ˜>Kš œ žœ žœžœžœ˜+Kšžœ˜—Kšžœžœžœ˜&K˜—šžœ.ž˜4Kšžœ#˜(—šžœ3ž˜7Kšœžœž˜#Kšžœ/˜4—Kšœ˜K˜Kšœ#˜#šžœžœžœ˜-šžœ.ž˜4Kšœ8˜8—K˜Kšœ#˜#K˜—šžœžœžœ˜*Kšœ"˜"šžœžœž˜Kšœžœ˜.Kšœžœ‘œžœ˜?šœžœ˜!Kšœ žœžœ˜Kšœ ˜ šž˜Kšœžœ˜ Kšœžœ˜ Kšœ"˜"šžœž˜šžœž˜Kšœžœ˜ ˜Kšžœ žœžœžœ˜-Kšžœžœ6˜@—Kšžœ˜——KšœK˜Kšžœžœ˜Kšœ™šžœžœž˜šœžœ˜Kš œžœžœžœžœžœ˜EKšœžœ ˜*Kšœ˜K˜—šœ ˜ Kšœ.™.Kšœ žœžœ+˜SKšœ!˜!šžœžœžœž˜#Kšœ˜Kšžœ˜—Kšœ˜K˜—Kšžœ˜—šœ˜Kšœ˜šœ˜Kšžœžœžœ˜-Kšžœ˜—Kšœ˜—Kšœžœ˜#K˜—Kšœ žœ˜Kšžœ˜—K˜—Kšžœžœ:˜J—K˜K˜—šžœ˜K˜ Kš‘œ˜K˜—Kšœ˜šžœ˜šžœž˜šžœ˜Kšœ ˜ Kšœ ˜ Kšœ ˜ Kšœ˜K˜ Kšœ ˜ K˜——Kšžœž˜K˜—K˜—šŸœžœžœžœ˜@šœžœžœ˜CKšœ˜Kšœ˜Kšœžœ˜ Kšœ˜Kšœ˜—Kšœžœ˜ Kšžœžœžœ#˜Jšžœ3ž˜7Kšœžœž˜#Kšžœ/˜4—Kšœ˜K˜Kšœ#˜#šžœžœž˜Kšœžœžœ˜EKšœžœžœ˜CKšžœžœ˜&—šžœ3ž˜9Kšœ8˜8—K˜Kšœ˜Kšžœ˜ K˜—Kšœ™šž˜Kšœžœžœžœ˜Kšœžœžœžœ˜3šžœž˜Kšœžœžœžœ‘˜<šœžœ˜ Kšœ ™ Kšžœžœ!žœ˜=Kšž˜K˜—šœ˜K˜Kšœ!žœ˜'Kšœ˜—šœ˜K˜šžœžœ˜Kšœ ™ Kšœ žœ&˜3Kšžœžœžœ,˜NK˜—Kšžœ+˜/Kšœ˜—šœ˜KšœA™AK˜šžœžœ˜Kšœ žœžœ˜Kšœžœžœ˜8Kšœ™šžœž˜Kšœžœ?˜I—K˜—Kšžœžœ7˜AK˜—šžœ˜ Kšœžœžœ˜ Kšœ˜K˜#šžœžœž˜šœžœ˜'Kšœ$žœ žœ˜<—šœžœ˜&Kšœ$žœ žœ˜;—Kšœžœ'žœ˜OKšœžœ˜#Kšœžœ˜Ašœžœ˜'šžœ3ž˜9Kšžœ<˜A—šžœžœž˜šœžœ˜'Kšœ$žœ žœ˜;—šœžœ˜&Kšœ$žœ žœ˜:—šœžœ˜%Kšœ"žœ˜(—šžœ˜ šœ˜Kšœ žœ žœ˜=———K˜—Kšžœ4˜;—K˜——šžœ žœžœ˜K˜Kšžœ žœžœ˜K˜—Kšžœ˜Kšžœ˜—K˜ K˜ K˜—Kšœ4žœ˜:˜ Kšœ žœ˜Kšžœžœ&˜=Kšžœ žœ žœ˜#K˜—K˜K˜—š œ žœžœ žœžœ˜0K˜—šŸ œžœžœžœ'˜NKšœžœžœ˜šž˜Kšœžœžœ ˜Kšžœžœžœžœ˜šžœžœž˜Kšœ žœ.žœ˜Ašœžœ˜!šžœž˜Kšžœ<˜A——Kšœ žœ,žœ˜>Kšœ žœ,žœ˜>Kšžœ˜—Kšœ˜Kšžœ˜—K˜—K˜š Ÿœžœžœžœžœžœ˜?Kšœžœ˜Kšœžœ˜&šžœžœž˜šœ žœ˜-Kšžœžœ˜3Kšžœžœ˜4Kšžœžœžœ˜NK˜K˜—šœžœ˜#K˜Kš œ žœžœžœžœ˜6K˜šžœžœ5ž˜FKšœžœ˜Kšžœ˜—Kšœ˜K˜—šœ žœ˜)Kšžœžœ˜1Kšœ˜Kšœ˜Kšœ˜Kšœ ˜ šžœž˜šœG™GKšœ ™ —Kšœ žœ˜Kšœ$˜$˜ Kšœžœ˜Kšœ žœžœ˜'Kšœ˜šžœžœžœ˜šžœžœžœž˜+Kšœ_™_Kšœ2˜2šžœ8žœ˜@Kšœžœ ˜Kšžœžœ˜Kšœ˜Kšœžœ˜#K˜—šž˜Kšžœžœ4˜B—Kšžœžœ,žœ˜KKšœ˜Kšœ0˜0Kšžœ˜—K˜—K˜K˜—Kšžœ˜—K˜—šœ žœ˜)Kšœ˜Kšœ˜Kšœ˜Kšœ ˜ K˜—šœ žœ˜'K˜—šœ žœ˜-Kšžœžœžœžœ˜B—Kšžœžœ%˜5—Kšœžœ˜K˜K˜—š Ÿ œžœžœ žœ&žœžœ˜aKšžœ žœžœžœ˜šžœ ˜šžœ˜Kšœ™šžœžœžœž˜(Kšœ.˜.Kšœžœ‘J˜Wšžœ0žœ ž˜EKšœ"˜"Kšžœžœžœžœ˜Kšžœ˜—Kšžœžœ‘.˜UKšžœ˜—K˜—šžœ˜Kšœ ™ šŸœžœ žœ˜!Kšœžœ˜ šž˜Kšœžœ˜Kšœžœ˜Kšžœ žœžœ˜KšžœžœDžœ žœ˜zKšžœ>žœ žœžœ˜XK˜K˜Kšžœ˜—K˜—šŸœžœžœ˜Kšœ.˜.K˜ K˜K˜—š žœžœž œžœž˜5Kšœ˜Kšžœ˜—š žœžœž œžœž˜3Kšœ˜Kšœ ˜ Kšžœ˜—K˜——K˜—K˜šŸœžœžœžœžœžœ žœžœ žœ˜xKšœ(™(Kš žœžœžœ žœ‘*˜Kšžœ˜Kšœžœ˜Kšœžœžœ˜#šžœ ž˜Kšœžœ˜šžœ"žœž˜3Kšœ žœžœ ˜Kš œžœ žœžœžœžœ˜6Kš œ žœžœžœžœžœ žœ˜IKšžœ˜—Kšžœ˜—Kšœžœ˜K˜—K˜—K˜š Ÿœžœžœžœžœžœ˜sKšœžœ˜Kšœžœ˜Kšœžœ˜Kšœžœ$˜.Kšœ˜šžœ žœ ž˜šžœ$žœž˜5Kšœžœ žœ˜&˜ Kšœ ˜ Kšœžœ˜#Kšœžœ˜ Kšœ˜—Kšœžœ˜Kšžœ˜—Kšžœ˜—šžœ ž˜Kšœ ˜ Kšœžœ˜#Kšœžœ˜ Kšžœ˜—K˜—K˜š Ÿ œžœžœžœžœžœ˜Išžœ ž˜šœ˜Kšœžœ*˜6˜2Kšœ<žœžœ ˜J—šŸ œžœžœžœ˜$Kšœžœ˜šžœ ž˜Kšœžœžœžœ˜ Kš žœ žœžœžœžœ˜HKš žœ žœžœžœžœ˜HKš žœ žœžœžœžœ˜HKš žœ žœžœžœžœ˜HKšžœ˜—šžœ˜šžœ˜Kšœ žœ žœžœ˜,Kšœžœ˜1Kšœžœžœ˜4K˜—K˜—K˜—šžœ˜šžœ˜ K˜Kšœ˜Kšœ$žœ˜5Kšœžœ˜šœžœ˜K˜KšœB˜BKšœ˜K˜—K˜—K˜—K˜—Kšœ žœ˜Kšœžœ˜Kšžœ˜—šž˜Kšœ žœ˜—K˜—K˜šŸ œžœžœžœ˜Ašžœ ž˜šœ˜Kšœžœ*˜6˜2Kšœ<žœžœ ˜J—šŸ œžœ˜Kšœžœ˜šžœ ž˜Kšœžœžœ˜#Kšžœ*žœžœžœ˜KKšžœ*žœžœžœ˜KKšžœ*žœžœžœ˜KKšžœ*žœžœžœ˜Kšžœ˜ šœ˜Kšœ žœ žœžœ˜,Kšœžœ˜1Kšœžœžœ˜4K˜———K˜—šœ˜Kšœ˜Kšœ$žœ˜5Kšœžœ˜K˜—šœ˜KšœC˜C—Kšœ˜K˜—Kšœ˜Kšœ˜Kšžœ˜—šž˜Kšœ žœ˜—K˜K˜—Kš œ žœžœžœžœ(˜HK˜š Ÿœžœžœžœžœ˜QKšœžœ˜Kšœ$˜$Kšžœ žœžœ ˜%Kšœ˜š žœžœžœžœžœžœž˜CKšžœ$žœžœžœ ˜JKšžœ˜—Kšžœ ˜K˜K˜—š Ÿœžœžœžœžœžœ˜:Kšžœ'˜-K˜—K˜š Ÿœžœžœžœžœžœ˜=Kšžœ%˜+K˜K˜—š Ÿœžœžœžœžœ˜3Kšœžœ˜Kšœžœ˜šžœž˜šžœž˜$Kšœžœ˜Kšœžœ˜Kšžœ˜—Kšžœ˜—Kšžœ˜ K˜K™—š Ÿ œžœžœžœžœ˜