% -*- mode:SLang; mode:fold; -*- % % file: filefinder.sl v1.0 % Author: Marko Mahnič, % % Used for listing directories and subdirectories % !if (is_defined("_FF_List_Type")) { %{{{ typedef struct { Name, Next } _FF_List_Type; } %}}} % \struct{FileSearch_Type} % % \var{Dirs} _FF_List_Type. List of directories to search % \var{Files} Array. Array of files in current directory. % \var{Current} Integer. Next file in Files that will be returned by FileFindNext. % \var{FileMask} Array. Array of filemasks, regular expressions. A file that is % returned by FileFindNext must match at least one filemask. The filemask matching % is not performed on directories. % \var{Recurse} Integer. Nonzero if subdirectories should be visited % \var{ReturnDirs} Integer. Nonzero if directory names should be returned % \description % Defines the search criteria and holds the current state % of the file search. % !if (is_defined("FileSearch_Type")) { %{{{ typedef struct { Dirs, Files, Current, FileMask, Recurse, ReturnDirs } FileSearch_Type; } %}}} static define _push_dir (fsrec, name) %{{{ { if (fsrec.Dirs == NULL) { fsrec.Dirs = @_FF_List_Type; fsrec.Dirs.Next = NULL; fsrec.Dirs.Name = name; } else { variable tmp = fsrec.Dirs; while (tmp.Next != NULL) tmp = tmp.Next; tmp.Next = @_FF_List_Type; tmp = tmp.Next; tmp.Next = NULL; tmp.Name = name; } } %}}} static define _pop_dir (fsrec) %{{{ { if (fsrec.Dirs == NULL) return; fsrec.Dirs = fsrec.Dirs.Next; } %}}} static define _read_directory (fsrec) %{{{ { fsrec.Current = 0; if (fsrec.Dirs == NULL) { fsrec.Files = String_Type[0]; return; } else if (file_status (fsrec.Dirs.Name) != 2) { fsrec.Files = String_Type[0]; return; } fsrec.Files = listdir (fsrec.Dirs.Name); variable i, fullname; for (i = length(fsrec.Files)-1; i >= 0; i--) { fullname = dircat (fsrec.Dirs.Name, fsrec.Files[i]); % fullname = dircat (fsrec.Dirs.Name, "ab"); if (file_status (fullname) == 2) { if (fsrec.Recurse) { _push_dir (fsrec, fullname); } fsrec.Files[i] = ">" + fsrec.Files[i]; % mark directory name } } } %}}} static define _file_matches (file, mask_list) %{{{ { # ifdef MSDOS WIN32 file = strup (file); #endif variable mask; foreach (mask_list) { mask = (); if (string_match (file, mask, 1)) return (1); } return (0); } %}}} % \function {fixup_filemasks} % \usage{String fixup_filemasks (String masks)} % \param{masks} String. A space separated list of filemasks to be converted % \return % A semicolon separated list of regular expressions that can be used % by file_find_start. % \description % changes filemasks into regexp that will be used by file_find_start define fixup_filemasks (masks) { variable fixed = Null_String; variable len = strlen (masks); variable i; for (i = 0; i < len; i++) { switch (masks[i]) { case '*': fixed = fixed + ".*"; } { case '.': fixed = fixed + "\\."; } { case '?': fixed = fixed + "."; } { case ' ': fixed = fixed + "$;"; } { case ';': fixed = fixed + "$;"; } { fixed += sprintf ("%c", masks[i]); } } fixed = fixed + "$;"; while (str_replace (fixed, "$;$;", "$;")) fixed = (); while (str_replace (fixed, "$$", "$")) fixed = (); fixed = strtrim (fixed, ";"); return (fixed); } % \function{file_find_start} % \usage{FileSearch_Type file_find_start (String RootDir, String FileMask, Int Recurse)} % \param{RootDir} String. Directory in which to search % \param{FileMask} String. A semicolon separated list of regular expressions % \param{Recurse} Integer. Nonzero if subdirectories should be visited % \description % Prepares the search structure and returns it. % define file_find_start (RootDir, FileMask, Recurse) { variable fsrec = @FileSearch_Type; fsrec.Dirs = NULL; _push_dir (fsrec, RootDir); # ifdef MSDOS WIN32 FileMask = strup (FileMask); #endif fsrec.FileMask = strtok (FileMask, ";"); fsrec.Recurse = Recurse; fsrec.ReturnDirs = 0; % Do not return directory names _read_directory (fsrec); return (fsrec); } % \function{file_find_next} % \usage{String file_find_next (FileSearch_Type FSRec)} % \param{FSRec} FileSearch_Type. The structure created with file_find_start. % \description % Returns the next file that matches the search criteria. % define file_find_next (FSRec) { variable file, fullname = Null_String; while (fullname == Null_String and FSRec.Dirs != NULL) { if (FSRec.Current >= length(FSRec.Files)) { _pop_dir (FSRec); if (FSRec.Recurse and FSRec.Dirs != NULL) _read_directory (FSRec); } else { file = FSRec.Files[FSRec.Current]; if (1 == is_substr (file, ">")) { if (FSRec.ReturnDirs) { fullname = dircat (FSRec.Dirs.Name, file[[1:]]); } } else { variable fn = dircat (FSRec.Dirs.Name, file); if (_file_matches (fn, FSRec.FileMask)) fullname = fn; } FSRec.Current++; } } return (fullname); } %% Testing and debugging %{{{ #iffalse define DumpDirs (fsrec) %{{{ { variable item; insert ("\n--------------\nDirs:\n"); if (fsrec.Dirs != NULL) { foreach (fsrec.Dirs) using ("Next") { item = (); if (item == NULL) insert ("NULL\n"); else insert (item.Name + "\n"); } } insert ("\nFiles:\n"); if (fsrec.Files != NULL) { foreach (fsrec.Files) { item = (); if (item == NULL) insert ("NULL\n"); else insert (item + "\n"); } } insert ("\nMasks:"); if (fsrec.FileMask != NULL) { foreach (fsrec.FileMask) { item = (); if (item == NULL) insert ("NULL\n"); else insert (item + "; "); } } vinsert ("\nCount: %d, Current: %d, Recurse: %d, RetDir: %d\n", length(fsrec.Files), fsrec.Current, fsrec.Recurse, fsrec.ReturnDirs); } %}}} define TestFF () { variable Test = file_find_start ("s:\\Src", "\\\\Debug\\\\.*$;\\\\Release\\\\.*$", 1); variable count = 0; variable file = file_find_next (Test); while ( file != Null_String ) { count++; file = file_find_next (Test); flush ("Found: " + file); } flush (sprintf ("%ld files Found.", count)); Test = file_find_start ("s:\\Src", "\\\\Debug\\\\.*$;\\\\Release\\\\.*$", 1); Test.ReturnDirs = 1; count = 0; while ( file != Null_String ) { count++; file = file_find_next (Test); flush ("Found: " + file); } flush (sprintf ("%ld files Found.", count)); } % TestFF(); #endif %}}}