DIRECTORY Ascii USING [CR, SP, TAB], BasicTime USING [minutesPerHour, nullGMT, Pack, 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, card, char, EndOfStream, Error, GetChar, GetLineRope, GetToken, GetUnpackedTime, PeekChar, PutChar, PutF, PutFR, PutRope, rope, STREAM, TokenProc], RefText USING [Equal, Fetch, Length, ObtainScratch, ReleaseScratch, TrustTextAsRope], Rope USING [Cat, Compare, Concat, Fetch, Find, FromRefText, Index, Length, ROPE, Substr]; DFUtilitiesImpl: CEDAR PROGRAM IMPORTS BasicTime, IO, RefText, Rope EXPORTS DFUtilities = BEGIN OPEN Utils: DFUtilities; ROPE: TYPE = Rope.ROPE; ParseFromStream: PUBLIC PROC [ in: IO.STREAM, proc: Utils.ProcessItemProc, filter: Utils.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]; }; 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[Utils.WhiteSpaceItem _ [lines: blankLineCount]]] THEN ERROR Abort; CancelWhiteSpace[]; }; }; CancelWhiteSpace: PROC = {blankLineCount _ 0}; CancelDirectory: PROC = {underDirectory _ FALSE}; GetDate: PROC RETURNS [Utils.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 Utils.DirectoryItem] = { directoryFilterB: Utils.FilterB = IF exported THEN $public ELSE $private; directoryFilterC: Utils.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 ERROR SyntaxError[ Rope.Cat["Missing directory path following '", RefText.TrustTextAsRope[x], "'."]]; CheckEndOfLine[]; }; underDirectory _ TRUE; RETURN[ IF (passFileItems _ ConsiderDefiningInstance[]) THEN NEW[Utils.DirectoryItem _ [ path1: path1, path2: path2, path2IsCameFrom: path2IsCameFrom, exported: exported, readOnly: readOnly ]] ELSE NIL ] }; ParseFileItem: PROC [verifyRoot: BOOL _ FALSE, name: ROPE _ NIL] RETURNS [REF Utils.FileItem] = { PassesNameFilter: PROC [file: ROPE] RETURNS [BOOL] = { IF ~(filter.filterA = $all OR ClassifyFileExtension[file] = filter.filterA) THEN RETURN[FALSE]; RETURN[SearchUsingList[file, filter.list].found] }; date: Utils.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[Utils.FileItem _ [ name: name, date: date, verifyRoot: verifyRoot ]] ELSE NIL ] }; ParseImportsItem: PROC [exported: BOOL] RETURNS [REF Utils.ImportsItem] = { x: REF TEXT; path1, path2: ROPE _ NIL; date: Utils.Date; form: Utils.UsingForm _ $exports; list: REF Utils.UsingList _ NIL; ConsiderImports: PROC RETURNS [BOOL] = { IF filter.filterC = $defining THEN RETURN[FALSE]; 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 ERROR SyntaxError[ Rope.Cat["Missing directory path following '", RefText.TrustTextAsRope[x], "'."]]; CheckEndOfLine[]; x _ GetTokenAsRefText[SimpleToken]; }; IF RefText.Equal[x, "Using", FALSE] THEN { x _ GetTokenAsRefText[IO.TokenProc]; 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[IO.TokenProc]; IF RefText.Length[x] = 1 THEN SELECT RefText.Fetch[x, 0] FROM '] => EXIT; '+ => IF ~verifyRoot THEN {verifyRoot _ TRUE; LOOP} ELSE ERROR SyntaxError["Illegal 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[Utils.UsingList[length]]; list.nEntries _ 0; }; list.nEntries = list.length => { newList: REF Utils.UsingList _ NEW[Utils.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[Utils.ImportsItem _ [ path1: path1, date: date, path2: path2, exported: exported, form: form, list: list ]] ELSE NIL ] }; ParseIncludeItem: PROC RETURNS [REF Utils.IncludeItem] = { item: REF Utils.IncludeItem = NEW[Utils.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 ERROR SyntaxError[ Rope.Cat["Missing directory path following '", RefText.TrustTextAsRope[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[Utils.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[Utils.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: IO.STREAM, proc: Utils.SupplyItemProc] = { haveDirectory: BOOL _ FALSE; DO item: REF ANY = proc[]; IF item = NIL THEN EXIT; WITH item SELECT FROM directory: REF Utils.DirectoryItem => haveDirectory _ TRUE; file: REF Utils.FileItem => IF ~haveDirectory THEN ERROR SyntaxError["File item without preceding Directory item."]; imports: REF Utils.ImportsItem => haveDirectory _ FALSE; include: REF Utils.IncludeItem => haveDirectory _ FALSE; ENDCASE; WriteItemToStream[out, item]; ENDLOOP; }; WriteItemToStream: PUBLIC PROC [out: IO.STREAM, item: REF ANY] = { maxUsingLineLength: INT = 90; maxReasonableFileNameLength: INT = 45; WITH item SELECT FROM directory: REF Utils.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]; IF directory.path2.Length[] ~= 0 THEN { out.PutRope[" "]; out.PutRope[IF directory.path2IsCameFrom THEN "CameFrom " ELSE "ReleaseAs "]; out.PutRope[directory.path2]; }; }; file: REF Utils.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 Utils.ImportsItem => { IF imports.exported THEN out.PutRope["Exports "]; out.PutRope["Imports "]; out.PutRope[imports.path1]; out.PutRope[" Of "]; DateToStream[out, imports.date]; IF imports.path2.Length[] ~= 0 THEN { out.PutRope["\N CameFrom "]; out.PutRope[imports.path2]; }; 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: Utils.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 Utils.IncludeItem => { out.PutRope["Include "]; out.PutRope[include.path1]; out.PutRope[" Of "]; DateToStream[out, include.date]; IF include.path2.Length[] ~= 0 THEN { out.PutRope["\N "]; out.PutRope[IF include.path2IsCameFrom THEN "CameFrom " ELSE "ReleaseAs "]; out.PutRope[include.path2]; }; }; comment: REF Utils.CommentItem => out.PutRope[comment.text]; whiteSpace: REF Utils.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 Utils.UsingList, nearlySorted: BOOL _ FALSE] = { IF usingList = NIL THEN RETURN; IF nearlySorted THEN { FOR i: NAT IN [1..usingList.nEntries) DO item: Utils.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: Utils.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 Utils.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 Utils.UsingList] RETURNS [diff: REF Utils.UsingList] = { aI, bI: NAT _ 0; aL: NAT = a.nEntries; bL: NAT = b.nEntries; diff _ NEW[Utils.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: Utils.Date] RETURNS [ROPE _ NIL] = { SELECT date.format FROM $explicit => { months: ROPE = "JanFebMarAprMayJunJulAugSepOctNovDec"; up: BasicTime.Unpacked = BasicTime.Unpack[date.gmt]; 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", IO.char[IF up.zone < 0 THEN '- ELSE '+], IO.card[up.zone.ABS/BasicTime.minutesPerHour], IO.card[up.zone.ABS MOD BasicTime.minutesPerHour] ] ] }; RETURN[ IO.PutFR[ "%02d-%g-%02d %g", IO.card[up.day], IO.rope[months.Substr[start: up.month.ORD*3, len: 3]], IO.card[up.year MOD 100], IO.rope[IO.PutFR[ "%02d:%02d:%02d %g", IO.card[up.hour], IO.card[up.minute], IO.card[up.second], IO.rope[ConvertZone[]] ]] ] ] }; $notEqual => RETURN["~="]; $greaterThan => RETURN[">"]; ENDCASE; }; DateToStream: PUBLIC PROC [s: IO.STREAM, date: Utils.Date] = { SELECT date.format FROM $explicit => { months: ROPE = "JanFebMarAprMayJunJulAugSepOctNovDec"; up: BasicTime.Unpacked = BasicTime.Unpack[date.gmt]; 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", IO.char[IF up.zone < 0 THEN '- ELSE '+], IO.card[up.zone.ABS/BasicTime.minutesPerHour], IO.card[up.zone.ABS MOD BasicTime.minutesPerHour] ] }; s.PutF["%02d-%g-%02d ", IO.card[up.day], IO.rope[months.Substr[start: up.month.ORD*3, len: 3]], IO.card[up.year MOD 100] ]; s.PutF["%02d:%02d:%02d ", IO.card[up.hour], IO.card[up.minute], IO.card[up.second]]; ConvertZone[]; }; $notEqual => s.PutRope["~="]; $greaterThan => s.PutChar['>]; ENDCASE; }; ClassifyFileExtension: PUBLIC PROC [file: ROPE] RETURNS [Utils.FilterA] = { exts: ARRAY [0..4) OF ROPE = [".bcd", ".boot", ".press", ".signals"]; FOR i: NAT IN [0..exts.LENGTH) DO IF file.Length[] >= exts[i].Length[] AND file.Find[s2: exts[i], pos1: file.Length[]-exts[i].Length[], case: FALSE] >= 0 THEN RETURN[$derived]; ENDLOOP; RETURN[$source] }; GetVersionNumber: PUBLIC PROC [r: ROPE] RETURNS [ROPE] = { RETURN[r.Substr[start: r.Index[s2: "!"]]] }; RemoveVersionNumber: PUBLIC PROC [r: ROPE] RETURNS [ROPE] = { RETURN[r.Substr[len: r.Index[s2: "!"]]] }; END. `DFUtilitiesImpl.mesa last edited by Levin on November 7, 1983 12:59 pm 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. 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". For now, efficiency concerns prevent us from using FS.ExpandName for this. For now, efficiency concerns prevent us from using FS.ExpandName for this. For now, efficiency concerns prevent us from using FS.ExpandName for this. ʘJšœ™Jšœ1™1J˜šÏk ˜ Jš œœœœœ˜Jšœ œD˜Sšœ œ˜Jšœ¼˜¼—šœœ˜ Jšœ“œ ˜¦—JšœœH˜UJšœœAœ ˜Y—J˜šœœ˜Jšœ œ˜$Jšœ˜—J˜Jš˜J˜Jšœ˜J˜Jšœœœ˜J˜Jšœ™J˜šÏnœœœ˜Jšœœœ=˜JJšœœœ˜šž œœ˜J•StartOfExpansion+ -- [char: CHAR] RETURNS [IO.CharClass] -- šœœœ˜Jšœœœ˜Jšœœ˜Jšœ¦œœ‰œ™ºJšœ œœ˜.Jšœ œœ˜/šœ œ Ðck*œ˜Hšœ˜Jšœœœœ˜%Jšœœœ ˜Jšœœ ˜—J˜—šžœœ œ ˜1Jšœ œœ ˜$Jšœ)œœ˜KJ˜—š žœœ œ œœ˜;Jšœœœ˜Jšœ)œœ˜KJšœ˜J˜—šžœœœœ˜š œœ œœ˜2Jšœ˜Jšœ˜—J˜—š ž œœœœœœ˜4Jšœœœ˜@Jšœ˜—š ž œœœœœ˜0Jšœœœ˜0Jšœ˜—šžœœ˜šœ0˜6JšœD˜I—Jšœ˜—šžœœ˜šœœœ˜1Jšœœ2œœ˜NJšœ˜J˜—J˜—Jšžœœ˜.Jšžœœœ˜1šžœœœ˜&š˜Jš œœœœœ ˜<šœ˜JšœœœœÏc˜4Jšœœœœ ˜-Jšœœ!˜-šœ˜Jšœœœ ˜@Jšœœœ˜—Jšœœ$˜0šœ˜ Jšœ˜Jšœ˜Jšœœ œœ˜4Jšœ‘™‘šœ œ˜!Jš œ$œœœœ˜M—Jšœ+˜1J˜——Jšœ˜—š˜Jšœ œ ˜1Jšœ œ,˜:—J˜—š žœœ œ œ œœ˜LJšœœ˜&Jšœ"œ œ œ ˜IJšœ"œ œ œ ˜Lšžœœœœ˜1šœ˜Jšœœ$˜@Jšœœ#˜JšœœA˜Q—šœ.˜4šœ ˜JšœR˜R——J˜J˜—Jšœœ˜šœ˜šœ.˜4šœ˜Jšœ ˜ Jšœ ˜ Jšœ!˜!Jšœ˜Jšœ˜Jšœ˜——Jšœ˜J˜—J˜—š ž œœœœœœ˜@Jšœœ˜ š žœœœœœ˜6šœœ/˜PJšœœ˜—Jšœ*˜0Jšœ˜—Jšœ˜Jšœœœ,˜Išœœœ-˜BJšœ#˜(—Jšœ˜J˜šœ˜šœœ-˜Ešœ˜Jšœ ˜ Jšœ ˜ Jšœ˜Jšœ˜——Jšœ˜Jšœ˜—J˜—š žœœ œœœ˜KJšœœ˜ Jšœœœ˜Jšœ˜J˜!Jšœœœ˜ šžœœœœ˜(Jšœœœœ˜1šœ˜Jš œ œ œœœœ˜>Jš œ œ œœœ˜+Jšœ˜—Jšœœœ˜&J˜—šœ.˜4Jšœ#˜(—šœ3˜7Jšœœ˜#Jšœ/˜4—Jšœ˜J˜Jšœ#˜#šœœœ˜-šœ.˜4šœ ˜JšœR˜R——J˜Jšœ#˜#J˜—šœœœ˜*Jšœœ ˜$šœœ˜Jšœœ˜.Jšœœ œœ˜?šœœ˜!Jšœ œœ˜Jšœ ˜ š˜Jšœœ˜ Jšœœ˜ Jšœœ ˜$šœ˜šœ˜Jšœœ˜ ˜Jšœ œœœ˜-Jšœœ4˜>—Jšœ˜——JšœK˜Kšœœ˜Jšœ™šœœ˜šœœ˜Jš œœœœœœ˜EJšœœ˜$Jšœ˜J˜—šœ ˜ Jšœ.™.Jšœ œœ%˜GJšœ!˜!šœœœ˜#Jšœ˜Jšœ˜—Jšœ˜J˜—Jšœ˜—šœ˜Jšœ˜šœ˜Jšœœœ˜-Jšœ˜—Jšœ˜—Jšœœ˜#J˜—Jšœ œ˜Jšœ˜—J˜—Jšœœ:˜J—J˜J˜—šœ˜J˜ Jš œ˜J˜—Jšœ˜šœ˜šœ˜šœ˜Jšœ ˜ Jšœ ˜ Jšœ ˜ Jšœ˜J˜ Jšœ ˜ J˜——Jšœ˜J˜—J˜—šžœœœœ˜:šœœœ˜7Jšœ˜Jšœ˜Jšœœ˜ Jšœ˜Jšœ˜—Jšœœ˜ Jšœœœ#˜Jšœ3˜7Jšœœ˜#Jšœ/˜4—Jšœ˜J˜Jšœ#˜#šœœ˜Jšœœœ˜EJšœœœ˜CJšœœ˜&—šœ3˜9šœ ˜JšœR˜R——J˜Jšœ˜Jšœ˜ J˜—Jšœ™š˜Jšœœœœ˜Jšœœœœ˜3šœ˜Jšœœœœ ˜<šœœ˜ Jšœ ™ Jšœœ!œ˜=Jš˜J˜—šœ˜J˜Jšœ!œ˜'Jšœ˜—šœ˜J˜šœœ˜Jšœ ™ Jšœ œ&˜3Jšœœœ&˜HJ˜—Jšœ+˜/Jšœ˜—šœ˜JšœA™AJ˜šœœ˜Jšœ œœ˜Jšœœœ˜8Jšœ™šœ˜Jšœœ9˜C—J˜—Jšœœ7˜AJ˜—šœ˜ Jšœœœ˜ Jšœ˜J˜#šœœ˜šœœ˜'Jšœ$œ œ˜<—šœœ˜&Jšœ$œ œ˜;—Jšœœ'œ˜OJšœœ˜#Jšœœ˜Ašœœ˜'šœ3˜9Jšœ<˜A—šœœ˜šœœ˜'Jšœ$œ œ˜;—šœœ˜&Jšœ$œ œ˜:—šœœ˜%Jšœ"œ˜(—šœ˜ šœ˜Jšœ œ œ˜=———J˜—Jšœ4˜;—J˜——šœ œœ˜J˜Jšœ œœ˜J˜—Jšœ˜Jšœ˜—J˜ J˜ J˜—Jšœ4œ˜:˜ Jšœ œ˜Jšœœ&˜=Jšœ œ œ˜#J˜—J˜J˜—š œ œœ œœ˜0J˜—š ž œœœœœ!˜KJšœœœ˜š˜Jšœœœ ˜Jšœœœœ˜šœœ˜Jšœ œ(œ˜;šœœ˜šœ˜Jšœ<˜A——Jšœ œ&œ˜8Jšœ œ&œ˜8Jšœ˜—Jšœ˜Jšœ˜—J˜—J˜šžœœœœœœœ˜BJšœœ˜Jšœœ˜&šœœ˜šœ œ˜'Jšœœ˜3Jšœœ˜4Jšœœœ˜NJ˜šœœ˜'J˜Jšœ œœ œ˜MJ˜J˜—J˜—šœœ˜J˜Jš œ œœœœ˜6J˜šœœ5˜FJšœœ˜Jšœ˜—Jšœ˜J˜—šœ œ˜#Jšœœ˜1Jšœ˜Jšœ˜Jšœ˜Jšœ ˜ šœœ˜%Jšœ˜Jšœ˜J˜—šœ˜šœG™GJšœ ™ —Jšœ œ˜Jšœ$˜$˜ Jšœœ˜Jšœ œœ˜'Jšœ˜šœœœ˜šœœœ˜+Jšœ_™_Jšœ,˜,šœ8œ˜@Jšœœ ˜Jšœœ˜Jšœ˜Jšœœ˜#J˜—š˜Jšœœ4˜B—Jšœœ,œ˜KJšœ˜Jšœ0˜0Jšœ˜—J˜—J˜J˜—Jšœ˜—J˜—šœ œ˜#Jšœ˜Jšœ˜Jšœ˜Jšœ ˜ šœœ˜%Jšœ˜Jšœ œœ œ˜KJšœ˜J˜—J˜—šœ œ˜!J˜—šœ œ˜'Jšœœœœ˜B—Jšœœ%˜5—Jšœœ˜J˜J˜—š ž œœœ œ œœ˜[Jšœ œœœ˜šœœ˜Jšœ™šœœœ˜(Jšœ(˜(Jšœœ J˜Wšœ0œ ˜EJšœ"˜"Jšœœœœ˜Jšœ˜—Jšœœ .˜UJšœ˜—J˜—šœ˜Jšœ ™ šžœœ œ˜!Jšœœ˜ š˜Jšœœ˜Jšœœ˜Jšœ œœ˜šœ˜JšœCœ ˜VJšœ ˜ —Jšœ>œ œœ˜XJ˜J˜Jšœ˜—J˜—šžœœœ˜Jšœ(˜(J˜ J˜J˜—š œœ œœ˜5Jšœ˜Jšœ˜—š œœ œœ˜3Jšœ˜Jšœ ˜ Jšœ˜—J˜—J˜—J˜š žœœœœœ˜DJšœ œœ œ˜-Jšœ(™(Jš œœœ œ *˜Kšœ˜Jšœœ˜Jšœœœ˜#šœ ˜Jšœœ˜šœ"œ˜3Jšœ œœ ˜šœ˜Jš œ œœœœ˜.—šœ ˜ Jš œœœœœ œ˜>—Jšœ˜—Jšœ˜—Jšœœ˜J˜—J˜—J˜šžœœœœ˜?Jšœœ˜'Jšœœ˜Jšœœ˜Jšœœ˜Jšœœ˜(Jšœ˜šœ œ ˜šœ$œ˜5Jšœœ œ˜&˜ Jšœ ˜ Jšœœ˜#Jšœœ˜ Jšœ˜—Jšœœ˜Jšœ˜—Jšœ˜—šœ ˜Jšœ ˜ Jšœœ˜#Jšœœ˜ Jšœ˜—J˜—J˜š ž œœœœœœ˜Cšœ ˜šœ˜Jšœœ*˜6J˜4šž œœœœ˜$Jšœœ˜šœ ˜Jšœœœœ˜ Jš œ œœœœ˜HJš œ œœœœ˜HJš œ œœœœ˜HJš œ œœœœ˜HJšœ˜—šœ˜šœ˜Jšœœ œœ˜(Jšœœ˜.Jšœœœ˜1J˜—J˜—J˜—šœ˜šœ˜ J˜Jšœ˜Jšœ$œ ˜6Jšœœ˜šœœ˜J˜Jšœœœ˜9Jšœ˜J˜—J˜—J˜—J˜—Jšœ œ˜Jšœœ˜Jšœ˜—J˜—J˜š ž œœœœœ˜>šœ ˜šœ˜Jšœœ*˜6J˜4šž œœ˜Jšœœ˜šœ ˜Jšœœœ˜#Jšœ*œœœ˜KJšœ*œœœ˜KJšœ*œœœ˜KJšœ*œœœ˜Kšœ˜ šœ˜Jšœœ œœ˜(Jšœœ˜.Jšœœœ˜1J˜———J˜—šœ˜Jšœ˜Jšœ$œ ˜6Jšœœ˜J˜—Jšœœœœ˜TJšœ˜J˜—Jšœ˜Jšœ˜Jšœ˜—J˜—J˜š žœœœœœ˜KJšœJ™JJšœœœœ+˜Eš œœœ œ˜!šœ#˜(šœCœ˜SJšœ ˜——Jšœ˜—Jšœ ˜J˜J™—š žœœœœœœ˜:JšœJ™JJšœ#˜)J˜—J˜š žœœœœœœ˜=JšœJ™JJšœ!˜'J˜—J™Jšœ˜J˜J˜—…—Kºm8