/*EVERYTHING IN XYPLORER — Rev. 0.95 / 2014/07/23.################################################# The code is divided into logical sections, each one identified by a line in the form "/*TITLE.###…" . Below that line a comment section like this one is found, which summarizes what happens below. -------------------------------------------------------------------------------------------------*/ /*ES.EXE SYNTAX.################################################################################### -r Search the database using a basic POSIX regular expression. -i Does a case sensitive search. -w Does a whole word search. -p Does a full path search. -h --help Display this help. -n Limit the amount of results shown to . -s Sort by full path. Note: a query containing a minus preceded by a blank (or in first position) and not followed by any of the letters above always produces the help, and no search results. -------------------------------------------------------------------------------------------------*/ /*CUSTOMIZATIONS.################################################################################## Here three parameters can be customized: $path: the path of the folder containing the es.exe executable, with a final slash. The default assumes - reasonably - that XYplorer and Everything, being both portable, reside in the same root folder; $paper: the name of the paper folder containing the search results; $mode: controls how the minus sign, which introduces switches in es.exe, is treated/escaped. Three modes are available. Total: Everything in Xyplorer will behave as if it was a window of Everything. The minus sign will ALWAYS be escaped with double quotes, thus the functionality provided by es.exe switches won't be available, however the search syntax will absolutely be the same of that of the graphical user interface of Everything; Smart: Everything in Xyplorer will behave as an "advanced" interface to es.exe. The minus sign will be escaped with double quotes AS LONG AS it is not introducing a switch, EXCEPT for -h, --help and -n when NOT followed by a blank and one or more digits. This mode allows the use of switches, keeping at the same time the syntax of search queries involving actual hypens identical to the one used by Everything. In order for it to work properly, non-ambiguous syntax must be entered, i.e. if you really want to look for an item named "-p" (without quotes) then you must quote it. The odds of such kind of queries are low so the attention required for such corner cases is a more than acceptable tradeoff for the smartness provided by this mode. Also known as "The best of the two worlds…™"; None: the minus sign will NEVER be escaped, giving full control on how each minus is passed to the command line. Remember: proper escaping consists of double quotes for a "text" minus, while a "switch" minus shall not be modified. Whatever mode is entered that doesn't match "Total" or "Smart" is treated as "None". $ipc: if set to TRUE the availability of Everything via IPC is tested, i.e. the script checks whether es.exe can successfully communicate with Everything and obtain search results, and starts Everything service and client if not. $diacritics: if set to TRUE it matches diacritical marks, therefore a ≠ à. This behaviour can be temporarily overridden with the custom switch "-d". $loc_aware: if set to TRUE it limits the scope of search to the current location and all its subfolders. This behaviour can be temporarily overridden with the custom switch "-l". Note: the custom switches above must be placed at the very start of the query in order to be recognized, and the last letter must be followed by a blank space. They can be compounded and order doesn't matter, so "-dl " = "-ld ". However each letter must not be repeated, so "-ll " is not valid syntax. $unicode: if set to TRUE it tells es.exe to always output the search results to an output file. If set to FALSE instead (default), the script will try to read the search results from memory first, but if it finds question marks (the sign of the presence of unrecognized Unicode characters) it will trigger the search a second time, effectively falling back to a TRUE status of the parameter and roughly doubling the execution time. Useful for people who rarely deal with CJK characters and can thus gain some speed, especially on low-end machines. -------------------------------------------------------------------------------------------------*/ $path = "\..\Everything\"; //"\..\Everything\" $paper = "Everything in XYplorer.txt"; //"Everything in XYplorer.txt" $mode = "Smart"; //"Smart" $ipc = FALSE; //FALSE $loc_aware = TRUE; //TRUE $diacritics = TRUE; //TRUE $unicode = FALSE; //FALSE /*GO!############################################################################################## First of all a test is made to make sure that es.exe can obtain results from Everything. A simple call is made: cmd /c es * -n 1 This asks for just one file, with no conditions at all on its name. This is easier than matching against the four possible error messages provided by es.exe, i.e. (according to its source code) Everything IPC service not running. Everything IPC window not found, IPC unavailable. failed to register IPCTEST window class failed to create IPCTEST window Them the output obtained via runret() is matched against a regular expression that represents a valid local path and filename on NTFS. To be honest this is not entirely true because the presence of question marks is tolerated, but it doesn't matter and the reason will be clear later on. If nothing can be matched it means either Everything database is empty or there is some problem with the IPC interface. Both can be usually solved by simply starting the Everything service and the Everything client, and so the script will take care of both, repeating the steps if necessary until it gets one result. After that, the $query variable is declared global: doing so a search query can be fed from an external source, for example a call via an alias. A special boolean variable is declared as well, that will help during the validation of the search query. The user is asked to enter the search query if not done before, then custom switches are searched for. The approach consists in isolating whatever matches the regular expression "^-[dl]* " first, and then counting the occurences of each letter in it. If a letter is found more than once the whole match is dismissed, i.e. it is not considered a switch or a composition of switches. Otherwise it is kept in memory and removed from the query. If what remains of the query is not an empty, then the query itself is valid and the script can go on (remember that an empty query tells Everything to list its full database, which is usually huge and causes a system slowdown). While an approach with foreach() loops might be deemed as overkill for just two letters, it can be expanded very easily in the future (linear complexity), should more switches be needed. The custom switches can now perform their action. Their overriding action against the parameters defined on top is calculated with a XOR operation. To confine a search to the current path, this is appended to the query as: "Shall we dance" AND path:"G:\Movies" The current path is retrived via and processed to resolve all the junctions in it (because Everything works with the real paths). In case no standard path can be obtained, i.e. in the form "X:\…", location-awareness is discarded and the search will be system-wide. Diacritics can be matched simply by enclosing the query like this: diacritics:(háček façade Pokémon) Parentheses are not mandatory, but they can never hurt so there they are. Then the minus sign is escaped according to the mode set above. Susbsequently, other special characters are escaped, namely the ones that can be used as search modifiers in Everything or in a normal regular expression and that are known to be "problematic" in a command line. Further info on http://forum.voidtools.com/viewtopic.php?f=5&t=1970&sid=2590f752481d94429e91191e55b6a261 and http://www.robvanderwoude.com/escapechars.php Once a "command line-friendly" query is obtained, the proper command can be run. There are two possibilities: the search results will either contain or not Unicode characters. The first case is the cleanest one, since search results can be retrieved directly from memory via runret(). This mode is enabled when $unicode is set to FALSE (as per user choice) AND there are no Unicode characters in the query (the reason is pretty obvious). This is the best-guess scenario possible for a "disk-less" execution, however there could still be Unicode characters in the results. If this is the case, the codepage of the console must be changed to 65001 (UTF-8) with the chcp command. This change is temporary, so this is a portable/stealth solution that doesn't alter the system. The call to es.exe can simply be concatenated ( && ) and the results will be written on disk to an output file ( >> ). Cases for which this type of execution is necessary are: - questions mark, which are the sign of the presence of Unicode characters, were found with the "disk-less" approach; - the $results variable is empty, either because the user set $unicode to TRUE, effectively skipping the faster execution above, or because Unicode characters were found in the query, again effectively skipping the faster execution. The console will write to disk a list of matches encoded in UTF-8 without BOM. However the absence of a BOM causes the generation of gibberish in place of CJK characters. Therefore a little trick is required: the BOM of the future paper folder file is written beforehand. In order to be immune to variations in system encoding/locale/codepage, the BOM is provided to the writefile() function as a conversion from hex values (0xEF 0xBB 0xBF). Due to the different way in which the results are given to XYplorer, the tab showing the paper folder will be opened in slightly different way. The general rationale behind it is however the same: open the results in a new tab unless an Everything in XYplorer tab is focused, in which case it is overwritten. If the "disk-less" execution was successfully performed, then a new tab is opened and relocated to the paper folder: the two different steps are executed with different functions and might produce some flickering. If the longer execution was performed instead, the same two steps are performed altogether by the tab() function. Finally, the status bar will show the search query as Everything would have seen it, i.e. with no character special escapings. And that's all, folks. -------------------------------------------------------------------------------------------------*/ if ($ipc) { while (regexmatches(runret("cmd /c es * -n 1", "$path"), '^[A-Z]:\\[^/:*"<>|]*?$') == "") { if (confirm("Everything in XYplorer can't work properly.Do you want it to start Everything service and client?")) { //translatable status "Everything in XYplorer is starting Everything service…", , "progress"; //translatable run "Everything -svc", "$path", 0, 0; wait 1500; status "Everything in XYplorer is starting Everything client…", , "progress"; //translatable run "Everything -startup", "$path", 0, 0; wait 1500; } else { status "Everything in XYplorer aborted.", "FF0000", "alert"; //translatable end TRUE; }; }; }; global $query; set $valid; while (NOT $valid) { while ("$query" == "") { $query = trim(input("Everything in XYplorer", "Type your search query as you would in Everything", "Everything is awesome… [it's a movie quote ツ]", "s")); //translatable }; $start = now("yyyy-mm-dd hh:nn:ss.fffffff"); $switches = formatlist(regexmatches("$query", "^-[ld]* "), "p", ""); foreach ($switch, "l|d") { if (gettokenindex("$switch", "$switches", "", "c") > 1) { $switches = ""; break; }; }; $query = trim(substr("$query", strlen(replace("$switches", "")))); $valid = ("$query" == "") ? FALSE : TRUE ; }; $current_path = property(#ResolveJunctionsAll, ""); $current_path = quote((regexmatches("$current_path", '^[A-Z]:\\[^/:*?"<>|]*?$') != "") ? "$current_path" : "*"); $query = ($loc_aware XOR ($switches Like "*l*")) ? ("$query AND path:$current_path") : ("$query"); $query = ($diacritics XOR ($switches Like "*d*")) ? ("diacritics:(" . "$query" . ")") : ("$query"); if ("$mode" == "Smart") { $cl = regexreplace("$query", '\B-(?!([riwps]|n (?!\D))\b)', '"-"'); } elseif ("$mode" == "Total") { $cl = replace("$query", '-', '"-"'); }; $cl = replacelist("$cl", '< > & | ^ "', '^< ^> ^& ^| ^^ \"', " "); if (NOT $unicode AND NOT isunicode("$cl")) { $results = runret("cmd /c es $cl", "$path"); }; if ($unicode OR ($results Like "*[?]*") OR NOT isset($results)) { writefile("\$paper", hexdump("EFBBBF", , "ri"), , "b"); run "cmd /c chcp 65001 && es $cl >> ""\$paper""", "$path", 2, 0; $finish = now("yyyy-mm-dd hh:nn:ss.fffffff"); tab((tab("get", "path") == "paper:\$paper") ? "relocate" : "new", "paper:\$paper"); status "Everything in XYplorer [".datediff("$start", "$finish", "ns")/1000000." ms]: $query"; end TRUE; }; $finish = now("yyyy-mm-dd hh:nn:ss.fffffff"); if (tab("get", "path") != "paper:\$paper") { tab("new"); }; paperfolder("paper:\$paper", "$results"); status "Everything in XYplorer [".datediff("$start", "$finish", "ns")/1000000." ms]: $query";