<<>> <> <> <> <> <> <> <> <> <> <> <> <> <> DIRECTORY Ascii USING [CR, LF, SP, TAB], BasicTime USING [daysPerMonth, hoursPerDay, minutesPerHour, MonthOfYear, OutOfRange, Pack, secondsPerMinute, TimeParametersNotKnown, Unpack, Unpacked, unspecifiedZone, Zone], 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, PeekChar, PutChar, PutFLR, PutFR, PutRope, STREAM, TokenProc], RefText USING [InlineAppendChar, Equal, Fetch, Length, ObtainScratch, ReleaseScratch, TrustTextAsRope], Rope USING [Compare, Concat, Equal, Fetch, Find, FindBackward, FromRefText, Length, ROPE, Substr]; DFUtilitiesImpl: CEDAR PROGRAM IMPORTS BasicTime, IO, RefText, Rope EXPORTS DFUtilities = BEGIN ROPE: TYPE = Rope.ROPE; STREAM: TYPE = IO.STREAM; LineSep: CHAR ~ '\n; LineSepText: REF TEXT ~ "\r"; IsLineSep: PROC [c: CHAR] RETURNS [BOOL] ~ INLINE { RETURN [(c = Ascii.LF) OR (c = Ascii.CR)]; }; <> 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; readFakeNewline: BOOL ¬ FALSE; <> 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, Ascii.LF => RETURN [$break]; ENDCASE => RETURN [$other]; }; PreUsingToken: IO.BreakProc -- [char: CHAR] RETURNS [IO.CharClass] -- = { SELECT char FROM Ascii.SP, Ascii.TAB => RETURN [$sepr]; Ascii.CR, Ascii.LF, '[ => 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] = INLINE {RETURN FullGetToken[breakProc, bufferT]}; GetToken: PROC [breakProc: IO.BreakProc] RETURNS [ROPE] = INLINE {RETURN [Rope.FromRefText[FullGetToken[breakProc, bufferN]]]}; FullGetToken: PROC [breakProc: IO.BreakProc, buffer: REF TEXT] RETURNS [token: REF TEXT] ~ { IF breakProc = SimpleToken THEN {char: CHAR; DO char ¬ in.GetChar[!IO.EndOfStream => GOTO EOF]; SELECT char FROM Ascii.SP, Ascii.TAB => NULL; ENDCASE => EXIT; ENDLOOP; IF char = '[ THEN { state: {server, startDir, finishDir, tail} ¬ server; buffer.length ¬ 1; buffer[0] ¬ char; DO char ¬ in.GetChar[!IO.EndOfStream => GOTO EOF]; IF IsLineSep[char] THEN EXIT; SELECT state FROM server => IF char='] THEN state ¬ startDir; startDir => SELECT char FROM '< => state ¬ finishDir; Ascii.SP, Ascii.TAB => EXIT; ENDCASE => state ¬ tail; finishDir => IF char='> THEN state ¬ tail; tail => SELECT char FROM Ascii.SP, Ascii.TAB => EXIT; ENDCASE => NULL; ENDCASE => ERROR; buffer ¬ RefText.InlineAppendChar[buffer, char]; ENDLOOP; in.Backup[char]; RETURN [buffer]; } ELSE in.Backup[char]; }; token ¬ in.GetToken[breakProc, buffer !IO.EndOfStream => GOTO EOF].token; RETURN; EXITS EOF => {readFakeNewline ¬ TRUE; token ¬ LineSepText}}; PutBack: PROC [x: REF TEXT] = { IF readFakeNewline THEN { IF NOT RefText.Equal[x, LineSepText] THEN ERROR SyntaxError["Internal bug."]; } ELSE 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 IsLineSep[RefText.Fetch[t, 0]]] }; EndOfLineRope: PROC [t: ROPE] RETURNS [BOOL] = { RETURN [t.Length[] = 1 AND IsLineSep[t.Fetch[0]]] }; 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}; ParseDirectoryItem: PROC [exported: BOOL, readOnly: BOOL, path1: ROPE ¬ NIL] RETURNS [REF DFUtilities.DirectoryItem ¬ NIL] = { 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; IF path1.Find["/"]#-1 OR path2.Find["/"]#-1 THEN ERROR SyntaxError["Slash found in directory; use bracket instead."]; 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 ¬ GetDateAndLineSep[in, bufferN]; IF name.Find["/"]#-1 THEN ERROR SyntaxError["Slash found in file name; use bracket instead."]; 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 ¬ GetDateAndLineSep[in, bufferN]; x ¬ GetTokenAsRefText[PreUsingToken]; IF RefText.Equal[x, "CameFrom", FALSE] THEN { IF EndOfLineRope[path2 ¬ GetToken[SimpleToken]] THEN RaiseSyntaxError["Missing directory path following", x]; CheckEndOfLine[]; x ¬ GetTokenAsRefText[PreUsingToken]; }; 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; IF RefText.TrustTextAsRope[x].Find["/"]#-1 THEN ERROR SyntaxError["Slash found in file name; use bracket instead."]; [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]; <
> }; CancelDirectory[]; IF path1.Find["/"]#-1 THEN ERROR SyntaxError["Slash found in DF name; use bracket instead."]; IF path2.Find["/"]#-1 THEN ERROR SyntaxError["Slash found in directory; use bracket instead."]; 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 ¬ GetDateAndLineSep[in, bufferN]; 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[]; IF item.path1.Find["/"]#-1 THEN ERROR SyntaxError["Slash found in DF name; use bracket instead."]; IF item.path2.Find["/"]#-1 THEN ERROR SyntaxError["Slash found in directory; use bracket instead."]; 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 '* => LOOP; -- a flag used in XDE DF files, similar to '+, we ignore it Ascii.CR, Ascii.LF => { <> 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]; ] }; ParseDigits: PROC [text: REF TEXT, start, stop: NAT] RETURNS [n: INT ¬ 0] ~ { FOR i: NAT IN[start..stop) DO char: CHAR ~ text[i]; IF char IN['0..'9] THEN n ¬ n*10+(char-'0) ELSE EXIT; ENDLOOP; }; ParseMonth: PROC [text: REF TEXT, start, stop: NAT] RETURNS [BasicTime.MonthOfYear] ~ { State: TYPE ~ {null, A, Ap, Apr, Au, Aug, D, De, Dec, F, Fe, Feb, J, Ja, Jan, Ju, Jul, Jun, M, Ma, Mar, May, N, No, Nov, O, Oc, Oct, S, Se, Sep}; state: State ¬ null; FOR i: NAT IN[start..stop) DO char: CHAR ~ text[i]; SELECT state FROM null => SELECT char FROM 'A => state ¬ A; 'D => state ¬ D; 'F => state ¬ F; 'J => state ¬ J; 'M => state ¬ M; 'N => state ¬ N; 'O => state ¬ O; 'S => state ¬ S; ENDCASE => GOTO bogus; A => SELECT char FROM 'p => state ¬ Ap; 'u => state ¬ Au; ENDCASE => GOTO bogus; Ap => SELECT char FROM 'r => state ¬ Apr; ENDCASE => GOTO bogus; Au => SELECT char FROM 'g => state ¬ Aug; ENDCASE => GOTO bogus; D => SELECT char FROM 'e => state ¬ De; ENDCASE => GOTO bogus; De => SELECT char FROM 'c => state ¬ Dec; ENDCASE => GOTO bogus; F => SELECT char FROM 'e => state ¬ Fe; ENDCASE => GOTO bogus; Fe => SELECT char FROM 'b => state ¬ Feb; ENDCASE => GOTO bogus; J => SELECT char FROM 'a => state ¬ Ja; 'u => state ¬ Ju; ENDCASE => GOTO bogus; Ja => SELECT char FROM 'n => state ¬ Jan; ENDCASE => GOTO bogus; Ju => SELECT char FROM 'l => state ¬ Jul; 'n => state ¬ Jun; ENDCASE => GOTO bogus; M => SELECT char FROM 'a => state ¬ Ma; ENDCASE => GOTO bogus; Ma => SELECT char FROM 'r => state ¬ Mar; 'y => state ¬ May; ENDCASE => GOTO bogus; N => SELECT char FROM 'o => state ¬ No; ENDCASE => GOTO bogus; No => SELECT char FROM 'v => state ¬ Nov; ENDCASE => GOTO bogus; O => SELECT char FROM 'c => state ¬ Oc; ENDCASE => GOTO bogus; Oc => SELECT char FROM 't => state ¬ Oct; ENDCASE => GOTO bogus; S => SELECT char FROM 'e => state ¬ Se; ENDCASE => GOTO bogus; Se => SELECT char FROM 'p => state ¬ Sep; ENDCASE => GOTO bogus; ENDCASE => GOTO bogus; ENDLOOP; SELECT state FROM Jan => RETURN[January]; Feb => RETURN[February]; Mar => RETURN[March]; Apr => RETURN[April]; May => RETURN[May]; Jun => RETURN[June]; Jul => RETURN[July]; Aug => RETURN[August]; Sep => RETURN[September]; Oct => RETURN[October]; Nov => RETURN[November]; Dec => RETURN[December]; ENDCASE => GOTO bogus; EXITS bogus => RETURN [unspecified]; }; ZoneInfo: TYPE ~ RECORD [zone: BasicTime.Zone, dst: BOOL]; nullZoneInfo: ZoneInfo ~ [zone: BasicTime.unspecifiedZone, dst: FALSE]; ParseZone: PROC [text: REF TEXT, start, stop: NAT] RETURNS [info: ZoneInfo ¬ nullZoneInfo] ~ { minus, dst: BOOL ¬ FALSE; hrs, mins: INT ¬ 0; SELECT stop-start FROM 3 => { zoneRope: Rope.ROPE ¬ Rope.FromRefText[text, start, 3]; SELECT TRUE FROM -- GMT needs more general parser - MGL Rope.Equal[zoneRope, "GMT"] => { hrs ¬ 0; dst ¬ FALSE }; Rope.Equal[zoneRope, "BST"] => { hrs ¬ 0; dst ¬ TRUE }; Rope.Equal[zoneRope, "PST"] => { hrs ¬ 8; dst ¬ FALSE }; Rope.Equal[zoneRope, "MST"] => { hrs ¬ 7; dst ¬ FALSE }; Rope.Equal[zoneRope, "CST"] => { hrs ¬ 6; dst ¬ FALSE }; Rope.Equal[zoneRope, "EST"] => { hrs ¬ 5; dst ¬ FALSE }; Rope.Equal[zoneRope, "PDT"] => { hrs ¬ 8; dst ¬ TRUE }; Rope.Equal[zoneRope, "MDT"] => { hrs ¬ 7; dst ¬ TRUE }; Rope.Equal[zoneRope, "CDT"] => { hrs ¬ 6; dst ¬ TRUE }; Rope.Equal[zoneRope, "EDT"] => { hrs ¬ 5; dst ¬ TRUE }; Rope.Equal[zoneRope, "JST"] => { hrs ¬ 9; minus ¬ TRUE; dst ¬ FALSE }; ENDCASE => GOTO bogus; < dst _ FALSE; 'D => dst _ TRUE;>> < GOTO bogus;>> <