<> <> <> <> <<>> DIRECTORY DFUtilities, DFUtilitiesExtras, FS, IO, Rope, RopeHash; DFUtilitiesExtrasImpl: CEDAR PROGRAM IMPORTS DFUtilities, FS, IO, Rope, RopeHash EXPORTS DFUtilitiesExtras ~ BEGIN OPEN DFUtilities, DFUtilitiesExtras, Rope; CacheArray: TYPE = ARRAY [0 .. cacheSize) OF ParsedDF; cacheSize: CARDINAL = 01019; cache: REF CacheArray _ NEW [CacheArray _ ALL [NIL]]; ParsedDF: TYPE = REF ParsedDFRep; ParsedDFRep: TYPE = RECORD [ dfFullName: ROPE, items: SEQUENCE size: NAT OF REF ANY ]; FileSyntaxError: PUBLIC ERROR [reason: ROPE, position: INT] = CODE; ParseDFFile: PROC [df: ROPE] RETURNS [parsedDF: ParsedDF] = { cp: FS.ComponentPositions; dfFullName: ROPE; hash: CARDINAL; [fullFName: dfFullName, cp: cp] _ FS.ExpandName[df]; IF cp.server.length=0 OR cp.ver.length=0 OR NOT Rope.Equal[df, dfFullName] OR Rope.Fetch[df, Rope.Length[df]-1]='h OR Rope.Fetch[df, Rope.Length[df]-1]='l OR Rope.Fetch[df, Rope.Length[df]-1]='H OR Rope.Fetch[df, Rope.Length[df]-1]='L THEN dfFullName _ FS.FileInfo[df].fullFName; -- we try to avoid an expensive FS.FileInfo hash _ RopeHash.FromRope[dfFullName, FALSE] MOD cacheSize; parsedDF _ cache[hash]; IF parsedDF#NIL AND Rope.Equal[parsedDF.dfFullName, dfFullName, FALSE] THEN RETURN; <> BEGIN dfStream: IO.STREAM _ FS.StreamOpen[fileName: dfFullName]; count: NAT _ 0; items: LIST OF REF ANY _ NIL; ProcessItem: ProcessItemProc = {items _ CONS [item, items]; count _ count + 1}; ParseFromStream[dfStream, ProcessItem, [comments: TRUE] ! SyntaxError => { position: INT = IO.GetIndex[dfStream]; IO.Close[dfStream]; ERROR FileSyntaxError[reason, position]; }]; IO.Close[dfStream]; parsedDF _ NEW [ParsedDFRep[count]]; parsedDF.dfFullName _ dfFullName; FOR i: NAT DECREASING IN [0 .. count) DO parsedDF[i] _ items.first; items _ items.rest; ENDLOOP; cache[hash] _ parsedDF; END; }; ParseFromFile: PUBLIC PROC [df: ROPE, proc: ProcessItemProc, filter: Filter _ []] = { previousWhiteSpace: REF ANY _ NIL; previousDirectoryPassed: BOOL _ FALSE; parsedDF: ParsedDF _ ParseDFFile[df]; SortUsingList[filter.list]; FOR i: NAT IN [0 .. parsedDF.size) DO item: REF ANY _ parsedDF[i]; WITH item SELECT FROM directory: REF DirectoryItem => { directoryFilterB: DFUtilities.FilterB = IF directory.exported THEN $public ELSE $private; directoryFilterC: DFUtilities.FilterC = IF directory.readOnly THEN $imported ELSE $defining; previousDirectoryPassed _ (filter.filterB = $all OR filter.filterB = directoryFilterB) AND (filter.filterC = $all OR filter.filterC = directoryFilterC); IF NOT previousDirectoryPassed THEN LOOP; }; file: REF 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] }; IF NOT previousDirectoryPassed OR NOT PassesNameFilter[RemoveVersionNumber[file.name]] THEN LOOP; }; import: REF ImportsItem => { list: REF UsingList _ import.list; ConsiderImports: PROC RETURNS [BOOL] = { IF filter.filterC = $defining THEN RETURN [FALSE]; IF import.exported AND filter.list # NIL THEN { <> RETURN [import.form = $all OR import.form = $exports OR list # NIL]; }; SELECT filter.filterB FROM $private => IF import.exported OR import.form = $exports THEN RETURN [FALSE]; $public => IF ~import.exported THEN RETURN [FALSE]; ENDCASE; RETURN[~(import.form = $list AND list = NIL)] }; IF filter.list=NIL OR import.list=NIL THEN IF ConsiderImports[] THEN {} ELSE LOOP ELSE { last: NAT _ 0; list _ NEW [UsingList[import.list.nEntries]]; FOR i: NAT IN [0 .. import.list.nEntries) DO IF NOT SearchUsingList[import.list[i].name, filter.list].found THEN LOOP; list[last] _ import.list[i]; last _ last + 1; ENDLOOP; list.nEntries _ last; SortUsingList[list]; IF ConsiderImports[] THEN item _ NEW [ImportsItem _ [path1: import.path1, date: import.date, path2: import.path2, exported: import.exported, form: import.form, list: list]] ELSE LOOP; }; }; include: REF IncludeItem => {}; comment: REF CommentItem => IF NOT filter.comments THEN LOOP; whiteSpace: REF WhiteSpaceItem => {IF filter.comments THEN previousWhiteSpace _ item; LOOP}; ENDCASE => ERROR; IF previousWhiteSpace#NIL THEN {IF proc[previousWhiteSpace] THEN RETURN; previousWhiteSpace _ NIL}; IF proc[item] THEN RETURN; ENDLOOP; }; END.