<> <> <> <> DIRECTORY BasicTime, Commander, CommanderOps, Convert, DFCachingUtilities, DFUtilities, FileDWIM, IO, MorePfsNames, PFS, PFSNames, RefTab, RefText, Rope, RopeList, RopeParts, SimpleFeedback, SymTab, TEditOps, TEditProfile, UserProfile, VersionMap, VersionMapDefaults, ViewerClasses; FileDWIMImpl: CEDAR PROGRAM IMPORTS BasicTime, Commander, CommanderOps, Convert, DFCachingUtilities, IO, MorePfsNames, PFS, PFSNames, RefText, Rope, RopeList, RopeParts, SimpleFeedback, SymTab, TEditOps, TEditProfile, UserProfile, VersionMap, VersionMapDefaults EXPORTS FileDWIM = BEGIN OPEN FileDWIM, MPfsN:MorePfsNames, RP:RopeParts; RopePart: TYPE = RP.RopePart; ROPEList: TYPE = LIST OF ROPE; Viewer: TYPE = ViewerClasses.Viewer; <> <> ResolveHint: PUBLIC PROC [hint: ROPE, contextFileName: ROPE ¬ NIL, searchHack: BOOL ¬ FALSE, tryAll: BOOL ¬ TRUE] RETURNS [ans: Answer] = { ans ¬ ProtectedResolveHint[hint, contextFileName, searchHack, tryAll !PFS.Error => GOTO Bombed]; EXITS Bombed => ans ¬ [NIL]}; ProtectedResolveHint: PROC [hint, contextFileName: ROPE, searchHack, tryAll: BOOL] RETURNS [ans: Answer] = { hintPath: PFS.PATH ~ PFS.PathFromRope[hint]; contextPath: PFS.PATH ~ PFS.PathFromRope[contextFileName]; contextShortName: ROPE ~ IF contextPath.ComponentCount[] > 0 THEN contextPath.ShortName[].ComponentRope[--exclude version--] ELSE NIL; isADfFileName: BOOL ~ Rope.Match["*.df", contextShortName, FALSE]; wDir: PFS.PATH ~ IF contextFileName#NIL THEN contextPath.Directory[] ELSE PFS.GetWDir[]; InWDir: PROC ~ { wDirName: ROPE ~ PFS.RopeFromPath[PFS.GetWDir[]]; fullHint: PFS.PATH ~ PFS.AbsoluteName[hintPath]; fullHintName: ROPE ~ PFS.RopeFromPath[fullHint]; IF OK[ans ¬ TryFull[fullHint]] THEN RETURN; {hadDir: BOOL = hintPath.ComponentCount[] > 1; hintShort: PFSNames.Component ~ fullHint.ShortName[]; hintExtParsed: PFS.PATH ~ ParseForExtension[hintShort]; hasExt: BOOL = hintExtParsed.ComponentCount[] > 1; hasVersion: BOOL = hintShort.version.versionKind # none; base: PFS.PATH = hintExtParsed.SubName[count: 1]; extComp: PFSNames.Component = FetchOrNil[hintExtParsed, 1]; ext: ROPE ~ extComp.ComponentRope[]; hintDir: PFS.PATH ~ fullHint.Directory[]; extless: PFS.PATH = hintDir.Cat[base]; extlessName: ROPE ~ PFS.RopeFromPath[extless]; dwimAns: PFS.PATH; standardExtension: BOOL ¬ FALSE; IF hasExt OR hasVersion OR NOT tryAll THEN NULL ELSE { IF verbose THEN SimpleFeedback.Append[$FileDWIM, oneLiner, $FYI, Rope.Concat["Trying source extensions to ", extlessName]]; IF OK[ans ¬ TryExtensions[fullHint, TEditProfile.sourceExtensions]] THEN RETURN; }; [dwimAns, ans.searchFor, ans.line, ans.char] ¬ DWIMit[hintPath, NIL, contextPath, searchHack, isADfFileName]; IF dwimAns#NIL THEN {ans.fullFileName ¬ PFS.RopeFromPath[dwimAns]; RETURN}; IF hasVersion OR NOT tryAll THEN RETURN; IF searchHack AND hasExt THEN { standardExtension ¬ RopeList.Memb[TEditProfile.sourceExtensions, ext, FALSE]; IF NOT standardExtension THEN { IF verbose THEN SimpleFeedback.Append[$FileDWIM, oneLiner, $FYI, Rope.Cat["Looking for prog=", extlessName, ", search=", ext]]; IF OK[ans ¬ TryExtensions[extless, TEditProfile.implExtensions]] THEN {ans.searchFor ¬ ext; RETURN}; }; }; IF (NOT hadDir) AND TEditProfile.tryVersionMap THEN { baseName: ROPE ~ PFS.RopeFromPath[base]; IF verbose THEN SimpleFeedback.Append[$FileDWIM, begin, $FYI, Rope.Concat["Trying version maps for ", baseName]]; SELECT TRUE FROM NOT hasExt => ans ¬ TryVersionMapExtensions[baseName, TEditProfile.sourceExtensions]; ENDCASE => { asked: PFSNames.Component ~ StripComponentVersion[hintShort]; ans ¬ TryVersionMapExtensions[baseName, CONS[ext, IF NOT searchHack THEN NIL ELSE IF standardExtension THEN TEditProfile.sourceExtensions ELSE TEditProfile.implExtensions]]; IF OK[ans] AND searchHack AND NOT PFSNames.EqualComponents[StripComponentVersion[PFS.PathFromRope[ans.fullFileName].ShortName[]], asked, FALSE] THEN ans.searchFor ¬ ext}; IF verbose THEN SimpleFeedback.Append[$FileDWIM, end, $FYI, " ."]; }; }}; ans ¬ [NIL]; PFS.DoInWDir[wDir, InWDir]; RETURN}; OK: PROC [ans: Answer] RETURNS [ok: BOOL] = {ok ¬ ans.fullFileName.Length[] # 0}; TryFull: PROC [fullFileName: PFS.PATH] RETURNS [ans: Answer] = { RETURN [IF Exists[fullFileName] THEN [PFS.RopeFromPath[fullFileName]] ELSE [NIL]]}; TryExtensions: PROC [extless: PFS.PATH, extensions: LIST OF ROPE] RETURNS [ans: Answer] = { ans ¬ [NIL]; FOR list: ROPEList ¬ extensions, list.rest UNTIL list=NIL DO extended: PFS.PATH ~ AddExtension[extless, list.first]; IF Exists[extended] THEN { ans.fullFileName ¬ PFS.RopeFromPath[extended]; RETURN}; ENDLOOP; RETURN}; TryVersionMap: PROC [shortName: ROPE] RETURNS [ans: Answer] = { mapList: VersionMap.MapList ~ VersionMapDefaults.GetMapList[$Source]; ranges: VersionMap.RangeList ~ VersionMap.ShortNameToRanges[mapList, shortName]; bestName: ROPE ¬ NIL; bestDate: BasicTime.GMT ¬ BasicTime.nullGMT; FOR list: VersionMap.RangeList ¬ ranges, list.rest UNTIL list=NIL DO range: VersionMap.Range ¬ list.first; WHILE range.len # 0 DO fullName: ROPE; created: BasicTime.GMT; [name: fullName, created: created, next: range] ¬ VersionMap.RangeToEntry[range]; IF bestDate = BasicTime.nullGMT OR BasicTime.Period[from: bestDate, to: created] > 0 THEN {bestDate ¬ created; bestName ¬ fullName}; ENDLOOP; ENDLOOP; ans ¬ [bestName]; }; TryVersionMapExtensions: PROC [name: ROPE, extensions: ROPEList] RETURNS [ans: Answer] = { mapList: VersionMap.MapList ~ VersionMapDefaults.GetMapList[$Source]; ans ¬ [VersionMap.LookupByExtension[mapList, name, extensions], NIL]; RETURN}; <> processDF, assocExts, followAttachments: BOOLEAN ¬ FALSE; BaseMatchTester: TYPE ~ PROC [dir: PFS.PATH, file: PFSNames.Component, ext: ROPE] RETURNS [stop: BOOL]; FullMatchTester: TYPE ~ PROC [dir: PFS.PATH, nonDir: PFSNames.Component] RETURNS [stop: BOOL]; DWIMit: PROC [orgName, wDir, contextFileName: PFS.PATH, searchHack, isADfFileName: BOOL] RETURNS [fileName: PFS.PATH, search: ROPE ¬ NIL, line, char: INT ¬ -1] = { Try: PROC [dir, short: PFS.PATH, BaseMatch: BaseMatchTester, FullMatch: FullMatchTester] RETURNS [stop: BOOL] = { testFull: PFS.PATH ~ PFS.AbsoluteName[short, dir]; testDir: PFS.PATH ~ testFull.Directory[]; testShort: PFSNames.Component ~ testFull.ShortName[]; stop ¬ FALSE; IF goalVersioned THEN { IF testShort.EqualComponents[c2: goalNonDir, case: FALSE] THEN stop ¬ FullMatch[testDir, goalNonDir]; } ELSE { extParsed: PFS.PATH ~ ParseForExtension[testShort]; testBase: PFSNames.Component ~ extParsed.Fetch[0]; testExt: ROPE ~ FetchOrNil[extParsed, 1].ComponentRope[]; IF testBase.EqualComponents[goalBase, FALSE] THEN { stop ¬ IF testExt.Equal[goalExt, FALSE] THEN FullMatch[testDir, goalNonDir] ELSE BaseMatch[testDir, testBase, testExt] }; }; stop ¬ stop; }; FullMatch: PROC [dir: PFS.PATH, nonDir: PFSNames.Component] RETURNS [stop: BOOL] = { IF Exists[fileName ¬ ExtendPath[dir, nonDir]] THEN { search ¬ NIL; RETURN [found ¬ TRUE]}; stop ¬ FALSE; }; BaseMatch: PROC [dir: PFS.PATH, file: PFSNames.Component, ext: ROPE] RETURNS [stop: BOOL] = { allButExt: PFS.PATH ~ ExtendPath[dir, file]; stop ¬ FALSE; IF goalExtended THEN { IF assocExts AND Exists[fileName ¬ AddExtension[allButExt, goalExt]] THEN { search ¬ NIL; RETURN [found ¬ TRUE]}; IF searchHack AND RopeList.Memb[TEditProfile.implExtensions, ext, FALSE] AND Exists[fileName ¬ AddExtension[allButExt, ext]] THEN { search ¬ goalExt; RETURN [found ¬ TRUE]}; IF searchHack AND assocExts AND NOT allButExt.Equal[lastImplAssoced, FALSE] THEN { lastImplAssoced ¬ allButExt; FOR ifel: ROPEList ¬ TEditProfile.implExtensions, ifel.rest WHILE ifel # NIL DO IF Exists[fileName ¬ AddExtension[allButExt, ifel.first]] THEN { search ¬ goalExt; RETURN [found ¬ TRUE]}; ENDLOOP; }; }; IF NOT goalExtended THEN { IF RopeList.Memb[TEditProfile.sourceExtensions, ext, FALSE] AND Exists[fileName ¬ AddExtension[allButExt, ext]] THEN { search ¬ NIL; RETURN [found ¬ TRUE]}; IF assocExts AND NOT allButExt.Equal[lastSourceAssoced, FALSE] THEN { lastSourceAssoced ¬ allButExt; FOR ifel: ROPEList ¬ TEditProfile.sourceExtensions, ifel.rest WHILE ifel # NIL DO IF NOT ifel.first.Equal[ext] AND Exists[fileName ¬ AddExtension[allButExt, ifel.first]] THEN { search ¬ NIL; RETURN [found ¬ TRUE]}; ENDLOOP; }; }; stop ¬ stop; }; SeekTags: PROC [short, item: ROPE] RETURNS [BOOL] ~ { FOR tfs: Tagfiles ¬ tagfiles, tfs.rest WHILE tfs#NIL DO tf: Tagfile ~ tfs.first; f: TaggedFile ¬ NARROW[tf.files.Fetch[short].val]; IF f#NIL THEN { t: Tags ¬ IF f.items#NIL THEN NARROW[f.items.Fetch[item].val] ELSE NIL; fileName ¬ f.name; IF item=NIL THEN search ¬ NIL ELSE IF t=NIL THEN LOOP ELSE {search ¬ item; line ¬ t.line; char ¬ t.char}; RETURN [TRUE]}; ENDLOOP; RETURN [FALSE]}; SeekExtTags: PROC [short, item: ROPE] RETURNS [BOOL] ~ { FOR ifel: ROPEList ¬ TEditProfile.sourceExtensions, ifel.rest WHILE ifel # NIL DO IF SeekTags[short.Cat[".", ifel.first], item] THEN RETURN[TRUE]; ENDLOOP; RETURN[FALSE]}; found, resolved: BOOL ¬ FALSE; goalExpanded, goalExtParsed: PFS.PATH; goalShort, goalBase, goalNonDir: PFSNames.Component; goalExt, goalBaseRope, goalShortRope: ROPE; goalExtended, goalVersioned: BOOL; lastSourceAssoced, lastImplAssoced: PFS.PATH ¬ NIL; goalExpanded ¬ PFS.AbsoluteName[orgName, wDir]; goalShort ¬ goalExpanded.ShortName[]; goalExtParsed ¬ ParseForExtension[goalShort]; goalBase ¬ goalExtParsed.Fetch[0]; goalExt ¬ FetchOrNil[goalExtParsed, 1].ComponentRope[]; goalNonDir ¬ StripComponentVersion[goalShort]; goalExtended ¬ goalExtParsed.ComponentCount[] > 1; goalVersioned ¬ goalShort.version.versionKind # none; <> { Consume: PROC [fullFName, attachedTo: PFS.PATH, uniqueID: PFS.UniqueID, bytes: INT, mutability: PFS.Mutability, fileType: PFS.FileType] RETURNS [continue: BOOLEAN] --FS.InfoProc-- = { IF Try[NIL, fullFName, BaseMatch, FullMatch] THEN RETURN [FALSE]; IF followAttachments AND attachedTo#NIL AND attachedTo#fullFName AND Try[NIL, GuessView[attachedTo], BaseMatch, FullMatch] THEN RETURN [FALSE]; continue ¬ TRUE; }; IF resolved THEN PFS.EnumerateForInfo[pattern: goalExpanded, proc: Consume !PFS.Error => CONTINUE]; IF found THEN RETURN; IF assocExts AND followAttachments THEN { goalOhneExt: PFS.PATH ~ goalExpanded.ReplaceShortName[goalBase]; IF verbose THEN SimpleFeedback.Append[$FileDWIM, oneLiner, $FYI, Rope.Concat["Looking for attachments to follow from ", PFS.RopeFromPath[goalOhneExt]]]; PFS.EnumerateForInfo[pattern: goalOhneExt.SetVersionNumber[[highest]], proc: Consume !PFS.Error => CONTINUE]; IF found THEN RETURN; PFS.EnumerateForInfo[pattern: goalExpanded.ReplaceShortName[[name: [goalBase.ComponentRope[--exclude version--].Concat[".*"], 0, NAT.LAST], version: [highest]]], proc: Consume !PFS.Error => CONTINUE]; IF found THEN RETURN; } <> <> <> <<};>> }; <> IF contextFileName.ComponentCount[] # 0 THEN { IF processDF AND isADfFileName THEN { Find[contextFileName, Try, BaseMatch, FullMatch]; IF found THEN RETURN; }; }; <> goalShortRope ¬ goalShort.ComponentRope[]; IF SeekTags[goalShortRope, NIL] THEN RETURN; IF goalExtended THEN { goalBaseRope ¬ goalBase.ComponentRope[]; IF SeekTags[goalBaseRope, goalExt] THEN RETURN; IF SeekExtTags[goalBaseRope, goalExt] THEN RETURN}; IF SeekExtTags[goalShortRope, NIL] THEN RETURN; <> fileName ¬ NIL; search ¬ NIL; line ¬ char ¬ -1; RETURN}; uxC: PFSNames.Component ~ MPfsN.ConsComponent[RP.Make["-ux"]]; vuxC: PFSNames.Component ~ MPfsN.ConsComponent[RP.Make["-vux"]]; GuessView: PROC [path: PFS.PATH] RETURNS [PFS.PATH] ~ { lastC: PFSNames.Component ~ path.ShortName[]; IF path.ComponentCount[]<2 OR lastC.version#[none] OR NOT uxC.EqualComponents[path.Fetch[0]] THEN RETURN [path]; {short: RopePart ~ MPfsN.ComponentName[lastC]; dotPos: INT ~ short.FindBackward[RP.Make["."]]; len: INT ~ short.Length[]; IF dotPos>0 AND dotPos+3 dir ¬ PFS.PathFromRope[di.path1]; fi: REF DFUtilities.FileItem => IF Try[dir, PFS.PathFromRope[fi.name], BaseMatch, FullMatch] THEN stop ¬ TRUE; ni: REF DFCachingUtilities.NestItem => SELECT ni.bracket FROM begin => { dfPath: PFS.PATH ~ PFS.PathFromRope[ni.df.name]; IF pacifyDFEnumeration THEN { dfShort: PFSNames.Component ~ dfPath.ShortName[]; dfExtParsed: PFS.PATH ~ ParseForExtension[dfShort]; base: ROPE ~ dfExtParsed.Fetch[0].ComponentRope[]; this: ROPE ~ Rope.Concat[" ", base]; stack ¬ CONS[Rope.Concat[stack.first, this], stack]; SimpleFeedback.Append[$FileDWIM, middle, $FYI, this]; }; IF Try[NIL, dfPath, BaseMatch, FullMatch] THEN stop ¬ TRUE ELSE { tryThis: BOOL ¬ ni.filter.file.files=NIL; RightOn: PROC [dir: PFS.PATH, nonDir: PFSNames.Component] RETURNS [stop: BOOL] = { tryThis ¬ stop ¬ TRUE; }; Almost: PROC [dir: PFS.PATH, file: PFSNames.Component, ext: ROPE] RETURNS [stop: BOOL] = { IF stop ¬ assocExts THEN tryThis ¬ TRUE; }; TryPair: PROC [key: ROPE, val: REF ANY] RETURNS [quit: BOOL] ~ { quit ¬ Try[NIL, PFS.PathFromRope[key], Almost, RightOn]; RETURN}; IF NOT tryThis THEN [] ¬ ni.filter.file.files.Pairs[TryPair]; clip ¬ NOT tryThis; }; }; end => { IF pacifyDFEnumeration THEN { stack ¬ stack.rest; SimpleFeedback.Append[$FileDWIM, begin, $FYI, stack.first]; }; }; ENDCASE => ERROR; ii: REF DFUtilities.ImportsItem => NULL; ii: REF DFUtilities.IncludeItem => NULL; c: REF DFUtilities.CommentItem => NULL; w: REF DFUtilities.WhiteSpaceItem => NULL; ENDCASE => ERROR; RETURN; END; cache: DFCachingUtilities.EnumerationCache ~ DFCachingUtilities.CreateEnumerationCache[]; dir: PFS.PATH ¬ NIL; IF verbose OR pacifyDFEnumeration THEN SimpleFeedback.Append[$FileDWIM, begin, $FYI, "Trying DF smarts"]; [] ¬ DFCachingUtilities.EnumerateDFContents[ df: [PFS.RopeFromPath[dfFileName]], Consumer: PerItem, cache: cache ! DFCachingUtilities.FSErrorOnDF => { IF verbose THEN SimpleFeedback.Append[$FileDWIM, oneLiner, $Error, IO.PutFR["FS.Error[%g, %g]", [atom[error.code]], [rope[error.explanation]] ]]; RESUME}; DFCachingUtilities.Miss => RESUME; DFCachingUtilities.SyntaxError => { IF verbose THEN SimpleFeedback.Append[$FileDWIM, oneLiner, $Error, IO.PutFR["Syntax error (%g) at %g in %g", [rope[reason]], [integer[position]], [rope[dfFileName]] ]]; RESUME} ]; IF verbose OR pacifyDFEnumeration THEN SimpleFeedback.Append[$FileDWIM, end, $FYI, " ."]; END; <> Tagfiles: TYPE ~ LIST OF Tagfile; Tagfile: TYPE ~ REF TagfilePrivate; TagfilePrivate: TYPE ~ RECORD [ name: PFS.PATH, uid: PFS.UniqueID, files: SymTab.Ref--short name -> TaggedFile-- ]; TaggedFile: TYPE ~ REF TaggedFilePrivate; TaggedFilePrivate: TYPE ~ RECORD [ name: PFS.PATH, items: SymTab.Ref--name -> tags-- ]; Tags: TYPE ~ REF TagsPrivate; TagsPrivate: TYPE ~ RECORD [line, char: INT]; tagAnalyses: SymTab.Ref--tag file name -> Tagfile-- ~ SymTab.Create[]; tagfiles: Tagfiles ¬ NIL; GetTagfiles: PROC [filenames: ROPEList] RETURNS [Tagfiles] ~ { IF filenames=NIL THEN RETURN [NIL]; {tf1: Tagfile ~ GetTagfile[filenames.first]; RETURN [CONS[tf1, GetTagfiles[filenames.rest]]]}}; GetTagfile: PROC [name: ROPE] RETURNS [tf: Tagfile] ~ { path: PFS.PATH ¬ NIL; in: IO.STREAM ¬ NIL; uid: PFS.UniqueID ¬ PFS.nullUniqueID; version: ROPE; path ¬ PFS.PathFromRope[name !PFS.Error => { SimpleFeedback.PutFL[$FileDWIM, oneLiner, $Error, "PFS.PathFromRope[\"%q\"] => PFS.Error[%g, %g, %g]", LIST[ [rope[name]], [atom[PFS.AtomFromErrorGroup[error.group]]], [atom[error.code]], [rope[error.explanation]] ]]; CONTINUE}]; IF path=NIL THEN RETURN [NIL]; tf ¬ NARROW[tagAnalyses.Fetch[name].val]; uid ¬ PFS.FileInfo[path !PFS.Error => { SimpleFeedback.PutFL[$FileDWIM, oneLiner, $Error, "PFS.FileInfo[\"%q\"] => PFS.Error[%g, %g, %g]", LIST[ [rope[name]], [atom[PFS.AtomFromErrorGroup[error.group]]], [atom[error.code]], [rope[error.explanation]] ]]; CONTINUE}].uniqueID; IF tf#NIL AND tf.uid=uid THEN RETURN; tf ¬ NEW[TagfilePrivate ¬ [ name: path, uid: uid, files: SymTab.Create[] ]]; [] ¬ tagAnalyses.Store[name, tf]; in ¬ PFS.StreamOpen[path !PFS.Error => { SimpleFeedback.PutFL[$FileDWIM, oneLiner, $Error, "PFS.StreamOpen[\"%q\"] => PFS.Error[%g, %g, %g]", LIST[ [rope[name]], [atom[PFS.AtomFromErrorGroup[error.group]]], [atom[error.code]], [rope[error.explanation]] ]]; CONTINUE}]; IF in=NIL THEN RETURN; {ENABLE UNWIND => in.Close[abort: TRUE]; version ¬ in.GetTokenRope[FtagsToken].token; SELECT TRUE FROM version.Equal["<>"] => ParseFtags1[PFSNames.Directory[path], name, tf, in]; ENDCASE => SimpleFeedback.PutFL[$FileDWIM, oneLiner, $Error, "Unknown tags format %g in file %g", LIST[ [rope[version]], [rope[name]] ]]; }; in.Close[]; RETURN}; ParseFtags1: PROC [wDir: PFS.PATH, name: ROPE, tf: Tagfile, in: IO.STREAM] ~ { taggedName: ROPE; taggedPath: PFS.PATH; taggedShort: ROPE; db: INT; f: TaggedFile; buff: REF TEXT ¬ RefText.ObtainScratch[100]; NextNot: PROC [e: CHAR] RETURNS [BOOL] ~ { c: CHAR ~ in.GetChar[]; IF c=e THEN RETURN [FALSE]; RefText.ReleaseScratch[buff]; SimpleFeedback.PutFL[$FileDWIM, oneLiner, $Error, "Syntax error at %g in %g: '\\%03b expected, '\\%03b found", LIST[ [integer[in.GetIndex[]-1]], [rope[name]], [cardinal[e.ORD]], [cardinal[c.ORD]] ]]; RETURN [TRUE]}; IF NOT debugParseFtags THEN SimpleFeedback.PutF[$FileDWIM, oneLiner, $FYI, "Reading %g.", [rope[name]] ]; {WHILE NOT in.EndOf[] DO IF in.PeekChar[]='< THEN { idx: INT ~ in.GetIndex[]; v2: ROPE ~ in.GetTokenRope[FtagsToken].token; IF NOT v2.Equal["<>"] THEN { SimpleFeedback.PutFL[$FileDWIM, oneLiner, $Error, "Syntax error at %g in %g: got unrecognized (%q) version, not <>", LIST[ [integer[idx]], [rope[name]], [rope[v2]] ]]; RefText.ReleaseScratch[buff]; RETURN}; }; IF NextNot['\014] THEN RETURN; taggedName ¬ in.GetTokenRope[IO.IDProc].token; IF NextNot[',] THEN RETURN; db ¬ in.GetInt[]; IF NextNot['\l] THEN RETURN; taggedPath ¬ PFS.AbsoluteName[PFS.PathFromRope[taggedName], wDir]; taggedShort ¬ taggedPath.ShortNameRope[]; f ¬ NEW[TaggedFilePrivate ¬ [ name: taggedPath, items: NIL ]]; [] ¬ tf.files.Store[taggedShort, f]; IF debugParseFtags THEN SimpleFeedback.PutF[$FileDWIM, oneLiner, $Debug, "Noting %g", [rope[taggedShort]] ]; IF readFtagsItems THEN { f.items ¬ SymTab.Create[]; DO item: ROPE; tags: Tags; IF in.EndOf[] THEN GOTO Done; SELECT in.PeekChar[] FROM '\014, '< => EXIT; ENDCASE => NULL; item ¬ in.GetTokenRope[FtagsToken].token; tags ¬ NEW [TagsPrivate]; IF NextNot['\177] THEN RETURN; [] ¬ in.GetToken[FtagsToken, buff]; IF NextNot['\177] THEN RETURN; tags.line ¬ in.GetInt[]; IF NextNot[',] THEN RETURN; tags.char ¬ in.GetInt[]; IF NextNot['\012] THEN RETURN; [] ¬ f.items.Store[item, tags]; ENDLOOP; } ELSE { wuz: INT ~ in.GetIndex[]; in.SetIndex[wuz+db]; }; ENDLOOP; EXITS Done => NULL}; RefText.ReleaseScratch[buff]; IF NOT debugParseFtags THEN SimpleFeedback.PutF[$FileDWIM, oneLiner, $FYI, "Done reading %g.", [rope[name]] ]; RETURN}; debugParseFtags: BOOL ¬ FALSE; readFtagsItems: BOOL ¬ FALSE; FtagsToken: PROC [char: CHAR] RETURNS [IO.CharClass] --IO.BreakProc-- ~ {SELECT char FROM '\177, '\012, '\014 => RETURN [break]; ENDCASE => RETURN [other]}; <> inPlace, doSearchHack: BOOL ¬ FALSE; OldFNP: PROC [ROPE, Viewer] RETURNS [fileName: ROPE, search: ROPE] ¬ NIL; GetDWIM: PROC [orgName: ROPE, viewer: Viewer] RETURNS [fileName: ROPE ¬ NIL, search: ROPE]--TEditOps.FileNameProc-- = { IF orgName.Length[]=0 THEN RETURN [NIL, NIL]; {orgPath: PFS.PATH ~ PFS.PathFromRope[orgName !PFS.Error => GOTO Bad]; IF orgPath.IsAbsolute[] THEN RETURN [NIL, NIL]; {contextFileName: ROPE ~ IF viewer # NIL THEN IF viewer.file = NIL THEN viewer.name ELSE viewer.file ELSE NIL; contextPath: PFS.PATH ~ PFS.PathFromRope[contextFileName]; isADfFileName: BOOL ¬ FALSE; Doit: PROC ~ { dwimAns: PFS.PATH; [dwimAns, search,,] ¬ DWIMit[orgPath, NIL, contextPath, doSearchHack, isADfFileName]; IF dwimAns#NIL THEN fileName ¬ PFS.RopeFromPath[dwimAns]; RETURN}; IF contextFileName.Length[]#0 THEN { wDir: PFS.PATH ~ contextPath.Directory[]; contextShort: ROPE ~ contextPath.ShortName[].ComponentRope[--exclude version--]; isADfFileName ¬ Rope.Match["*.df", contextShort, FALSE]; PFS.DoInWDir[wDir, Doit]; } ELSE Doit[]; IF fileName=NIL AND OldFNP#NIL THEN [fileName, search] ¬ OldFNP[orgName, viewer]; }; EXITS Bad => search ¬ NIL}; RETURN}; <> wDirKey: ATOM ~ $WorkingDirectory; verbose, pacifyDFEnumeration: BOOL ¬ TRUE; Exists: PROC [fileName: PFS.PATH] RETURNS [exists: BOOLEAN] = { exists ¬ FALSE; exists ¬ PFS.FileInfo[ name: fileName ! PFS.Error => CONTINUE].bytes # -1; RETURN}; ParseForExtension: PROC [c: PFSNames.Component] RETURNS [PFS.PATH] ~ { pos: INT ~ Rope.FindBackward[c.name.base, ".", c.name.start+c.name.len-1]; nameLen: INT ~ c.name.start+c.name.len; len: INT ~ MIN[c.name.base.Length[], nameLen]; IF pos >= c.name.start THEN RETURN [PFSNames.ConstructName[LIST[ [[c.name.base, c.name.start, pos-c.name.start]], [[c.name.base, pos+1, len-pos-1]] ]]] ELSE RETURN [PFSNames.ConstructName[LIST[c]]]}; ExtendPath: PROC [path: PFS.PATH, component: PFSNames.Component] RETURNS [PFS.PATH] ~ { RETURN path.Cat[PFSNames.ConstructName[LIST[component]]]}; AddExtension: PROC [path: PFS.PATH, ext: ROPE] RETURNS [PFS.PATH] ~ { RETURN path.ReplaceShortName[[name: [path.ShortName[].ComponentRope[].Cat[".", ext], 0, NAT.LAST]]]}; FetchOrNil: PROC [path: PFS.PATH, index: INT] RETURNS [PFSNames.Component] ~ { IF index=path.ComponentCount[] THEN RETURN [[]]; RETURN path.Fetch[index]}; StripComponentVersion: PROC [c: PFSNames.Component] RETURNS [PFSNames.Component] ~ {RETURN [[c.name, [none]]]}; TestCmd: PROC [cmd: Commander.Handle] RETURNS [result: ATOM ¬ NIL, msg: ROPE ¬ NIL] --Commander.CommandProc-- ~ { argv: CommanderOps.ArgumentVector ~ CommanderOps.Parse[cmd]; hint, contextFileName: ROPE ¬ NIL; searchHack, errd: BOOL ¬ FALSE; tryAll: BOOL ¬ TRUE; ans: Answer; IF argv.argc<=1 THEN RETURN [$Failure, "Usage: FileDWIM (-s | +s | -a | +a)* || ([contextFileName] hint)"]; FOR i: NAT IN [1..argv.argc) DO SELECT TRUE FROM argv[i].Equal["-s"] => searchHack ¬ FALSE; argv[i].Equal["+s"] => searchHack ¬ TRUE; argv[i].Equal["-a"] => tryAll ¬ FALSE; argv[i].Equal["+a"] => tryAll ¬ TRUE; hint=NIL => hint ¬ argv[i]; ENDCASE => contextFileName ¬ argv[i]; ENDLOOP; ans ¬ ResolveHint[hint, contextFileName, searchHack, tryAll !PFS.Error => { errd ¬ TRUE; msg ¬ IO.PutFR["PFS.Error[%g, %g]", [atom[error.code]], [rope[error.explanation]] ]; CONTINUE}]; IF errd THEN RETURN [$Failure, msg]; msg ¬ IO.PutFR["[fullFileName: %g, searchFor: %g]", [rope[ans.fullFileName]], [rope[ans.searchFor]] ]; RETURN}; NoticeProfileChanges: PROC [reason: UserProfile.ProfileChangeReason] --UserProfile.ProfileChangedProc-- = { tagfilenames: ROPEList ¬ UserProfile.ListOfTokens["FileDWIM.Tagfiles", NIL]; doSearchHack ¬ UserProfile.Boolean["FileDWIM.DoSearchHack", FALSE]; processDF ¬ UserProfile.Boolean["FileDWIM.ProcessDF", TRUE]; assocExts ¬ UserProfile.Boolean["FileDWIM.AssociateExtensions", TRUE]; followAttachments ¬ UserProfile.Boolean["FileDWIM.FollowAttachments", TRUE]; verbose ¬ UserProfile.Boolean["FileDWIM.Verbose", FALSE]; pacifyDFEnumeration ¬ UserProfile.Boolean["FileDWIM.PacifyDFEnumeration", FALSE]; tagfiles ¬ GetTagfiles[tagfilenames]; IF (processDF OR assocExts) AND NOT inPlace THEN BEGIN inPlace ¬ TRUE; OldFNP ¬ TEditOps.ReplaceFileNameProc[GetDWIM]; END; }; UserProfile.CallWhenProfileChanges[NoticeProfileChanges]; Commander.Register["FileDWIM", TestCmd, "FileDWIM (-s | +s | -a | +a)* || ([contextFileName] hint) tests FileDWIM"]; END.