├── .gitattributes ├── CI ├── ALSysIO64.sys ├── CoreTemp.exe ├── CoreTemp.ini └── winsqlite3.dll ├── README.md ├── appveyor-script.bat ├── appveyor.yml ├── doc ├── Makefile ├── README.md ├── envtool.Doxyfile ├── envtool.chm ├── envtool.css └── main_page.dox ├── envtool-help.png ├── envtool-release.zip ├── envtool.cfg └── src ├── Everything.c ├── Everything.h ├── Everything_ETP.c ├── Everything_ETP.h ├── Everything_IPC.h ├── Makefile.VC ├── Makefile.Windows ├── auth.c ├── auth.h ├── cache.c ├── cache.h ├── cfg_file.c ├── cfg_file.h ├── cmake.c ├── cmake.h ├── color.c ├── color.h ├── compiler.c ├── compiler.h ├── description.c ├── description.h ├── dirlist.c ├── dirlist.h ├── du.c ├── envtool.c ├── envtool.h ├── envtool.rc ├── envtool.sln ├── envtool.vcxproj ├── envtool_py.c ├── envtool_py.h ├── find_vstudio.c ├── foo └── win_glob.c ├── get_file_assoc.c ├── get_file_assoc.h ├── getopt_long.c ├── getopt_long.h ├── ignore.c ├── ignore.h ├── json.c ├── json.h ├── lua.c ├── lua.h ├── misc.c ├── pkg-config.c ├── pkg-config.h ├── regex.c ├── regex.h ├── report.c ├── report.h ├── searchpath.c ├── show_ver.c ├── smartlist.c ├── smartlist.h ├── sort.c ├── sort.h ├── tests.c ├── tests.h ├── vcpkg.c ├── vcpkg.h ├── win_glob.c ├── win_glob.h ├── win_sqlite3.c ├── win_trust.c └── win_ver.c /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Batch files must be run with CRLF line endings. 5 | *.bat text eol=crlf 6 | -------------------------------------------------------------------------------- /CI/ALSysIO64.sys: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gvanem/EnvTool/68027f3a5784650874b4fb4803e63de64a1ccd37/CI/ALSysIO64.sys -------------------------------------------------------------------------------- /CI/CoreTemp.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gvanem/EnvTool/68027f3a5784650874b4fb4803e63de64a1ccd37/CI/CoreTemp.exe -------------------------------------------------------------------------------- /CI/CoreTemp.ini: -------------------------------------------------------------------------------- 1 | [General] 2 | ReadInt=1000; 3 | LogInt=10; 4 | Language=; 5 | Plugins=1; 6 | EnLog=0; 7 | SingleInstance=1; 8 | AutoUpdateCheck=1; 9 | 10 | [Display] 11 | Fahr=0; 12 | Minimized=0; 13 | CloseToSystray=0; 14 | HideTaskbarButton=0; 15 | TextColor=FF000000; 16 | StatusColor=0000C0FF,000000FF; 17 | LabelColor=FF000000; 18 | 19 | [System tray] 20 | SystrayOption=0; 21 | SystrayTransparentBack=1; 22 | SystrayColorAllBack=; 23 | SystrayColorAllText=; 24 | SystrayColorHighCpuBack=; 25 | SystrayColorHighCpuText=; 26 | SystrayColorHighBack=; 27 | SystrayColorHighText=; 28 | SystrayColorClockBack=; 29 | SystrayColorClockText=; 30 | SystrayColorLoadBack=; 31 | SystrayColorLoadText=; 32 | SystrayColorRamBack=; 33 | SystrayColorRamText=; 34 | SystrayColorPowerBack=; 35 | SystrayColorPowerText=; 36 | SystrayDisplayFrequency=0; 37 | SystrayDisplayLoad=0; 38 | SystrayDisplayRam=0; 39 | SystrayDisplayPower=0; 40 | SystrayFontName=Tahoma; 41 | SystrayFontSize=8; 42 | 43 | [Windows 7 Taskbar button settings] 44 | W7TBEnable=1; 45 | W7TBOption=0; 46 | W7TBCycleDelay=10; 47 | W7TBFrequencyColor=2; 48 | W7TBDisableMinimizeToTray=0; 49 | 50 | [G15 LCD settings] 51 | G15BuiltInFont=1; 52 | G15Time=1; 53 | G1524HTime=0; 54 | G15FontName=Tahoma; 55 | G15FontSize=8; 56 | 57 | [Advanced] 58 | ShowDTJ=0; 59 | BusClk=0; 60 | SnmpSharedMemory=0; 61 | 62 | [Overheat protection settings] 63 | EnableOHP=0; 64 | NotifyHot=0; 65 | Balloon=1; 66 | Flash=0; 67 | Execute=; 68 | EnableShutDown=0; 69 | ProtectionType=0; 70 | ActivateAt=0; 71 | Seconds=30; 72 | ExecuteOnce=1; 73 | Degrees=90; 74 | 75 | [Misc] 76 | Version=0; 77 | TjMaxOffset=0; 78 | AlwaysOnTop=0; 79 | MiniMode=0; 80 | AltFreq=0; 81 | 82 | [UI] 83 | SPX=0; 84 | SPY=0; 85 | CoreFrequencySelector=-1; 86 | -------------------------------------------------------------------------------- /CI/winsqlite3.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gvanem/EnvTool/68027f3a5784650874b4fb4803e63de64a1ccd37/CI/winsqlite3.dll -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # EnvTool v1.4: 2 | 3 | [![Build Status](https://ci.appveyor.com/api/projects/status/github/gvanem/envtool?branch=master&svg=true&pendingText=MSVC%20build%20-%20Pending&passingText=MSVC%20build%20-%20OK)](https://ci.appveyor.com/project/gvanem/envtool) 4 | 5 | A tool to search along various environment variables for files (or a wildcard). The following modes 6 | handles these environment variables: 7 | [![screenshot](envtool-help.png?raw=true)](envtool-help.png?raw=true) 8 | 9 | 23 | 24 | It also checks for missing directories along the above env-variables. 25 | 26 | The `--path` option also checks these registry keys: 27 | `HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths` and 28 | `HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths` 29 | 30 | and enumerates all keys for possible programs. E.g. if registry contains this: 31 | `HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\makensis.exe` = 32 | `f:\MinGW32\bin\MingW-studio\makensis.exe`, 33 | 34 | `envtool --path maken*` will include `f:\MinGW32\bin\MingW-studio\makensis.exe` 35 | in the result. 36 | 37 | Problem with old programs pestering your `PATH` and _Registry_ entries can be tricky 38 | to diagnose. Here I had an problem with an old version of the _FoxitReader PDF reader_: 39 | Checking with `envtool --path foxit*.exe`, resulted in: 40 | 41 | ``` 42 | Matches in HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths: 43 | (2) 27 Nov 2014 - 10:24:04: f:\ProgramFiler\FoxitReader\FoxitReader.exe 44 | Matches in %PATH: 45 | 21 Apr 2006 - 17:43:10: f:\util\FoxitReader.exe 46 | (2): found in "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths". 47 | ``` 48 | 49 | Hence if you write `FoxitReader` in the Window Run-box (_Winkey-R_), you'll get the 50 | newer (27 Nov 2014) `FoxitReader` launched. But if you say `FoxitReader` in your shell 51 | (cmd etc.), you'll get the old version (21 Apr 2006). 52 | 53 | Other examples: 54 | 55 | **E.g. 1**: `envtool --path notepad*.exe` first checks the `%PATH%` env-var 56 | for consistency (reports missing directories in `%PATH%`) and prints 57 | all the locations of `notepad*.exe`. On my box the result is: 58 | 59 | ``` 60 | Thu Jul 21 16:02:20 2011: f:\windows\system32\notepad-orig.exe 61 | Mon Nov 18 19:26:40 2002: f:\windows\system32\notepad.exe 62 | Thu Jul 21 16:13:11 2011: f:\windows\system32\notepad2.exe 63 | Mon Nov 18 19:26:40 2002: f:\windows\notepad.exe 64 | ``` 65 | 66 | **E.g. 2**: `envtool --inc afxwin*` first checks the `%INCLUDE%` env-var 67 | for consistency (reports missing directories in `%INCLUDE`) and prints 68 | all the locations of `afxwin*`. On my box the result is: 69 | 70 | ``` 71 | Thu Apr 14 18:54:46 2005: g:\vc_2010\VC\AtlMfc\include\AFXWIN.H 72 | Thu Apr 14 18:54:46 2005: g:\vc_2010\VC\AtlMfc\include\AFXWIN1.INL 73 | Thu Apr 14 18:54:46 2005: g:\vc_2010\VC\AtlMfc\include\AFXWIN2.INL 74 | Thu Apr 14 18:54:46 2005: g:\vc_2010\VC\AtlMfc\include\AFXWIN.H 75 | Thu Apr 14 18:54:46 2005: g:\vc_2010\VC\AtlMfc\include\AFXWIN1.INL 76 | Thu Apr 14 18:54:46 2005: g:\vc_2010\VC\AtlMfc\include\AFXWIN2.INL 77 | ``` 78 | 79 | **E.g. 3**: If an _App Paths_ registry key has an alias for a command, the target 80 | program is printed. E.g. if: 81 | `HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\winzip.exe` 82 | points to `c:\PROGRA~1\WINZIP\winzip32.exe` 83 | 84 | (here `winzip.exe` is an alias for the real program `winzip32.exe`). Hence 85 | `envtool --path winzip*` reports: 86 | 87 | ``` 88 | Fri Oct 11 09:10:00 2002: f:\PROGRA~1\WINZIP\winzip.exe ! 89 | Fri Oct 11 09:10:00 2002: f:\PROGRA~1\WINZIP\winzip32.exe ! 90 | (!) - found in registry. 91 | ``` 92 | 93 | **E.g. 4**: It's pretty amazing what the _FindFile()_ API in Windows can do. E.g.: 94 | `envtool --path *-?++.exe`: 95 | 96 | ``` 97 | Tue Nov 19 12:01:38 2002: f:\Mingw32\bin\mingw32-c++.exe 98 | Tue Nov 19 12:01:38 2002: f:\Mingw32\bin\mingw32-g++.exe 99 | Wed Mar 09 14:39:05 2011: f:\CygWin\bin\i686-pc-cygwin-c++.exe 100 | Wed Mar 09 14:39:05 2011: f:\CygWin\bin\i686-pc-cygwin-g++.exe 101 | ``` 102 | 103 | Although not as powerful as "POSIX-style file matching" which is also built-in 104 | via the `fnmatch()` function. 105 | 106 | **E.g. 5**: If you have Python installed, the `--python` option will search in 107 | `%PYTHONPATH` and `sys.path[]` for a match. E.g.: 108 | `envtool.exe --python ss*.py`: 109 | 110 | ``` 111 | 24 Jun 2011 - 11:38:10: f:\ProgramFiler\Python27\lib\ssl.py 112 | 16 Feb 2011 - 12:14:28: f:\ProgramFiler\Python27\lib\site-packages\win32\lib\sspi.py 113 | 16 Feb 2011 - 12:14:28: f:\ProgramFiler\Python27\lib\site-packages\win32\lib\sspicon.py 114 | ``` 115 | 116 | **E.g. 6**: The `--python` option will also look inside Python *EGGs* (plain ZIP-files) found 117 | in `sys.path[]`. E.g.: 118 | `envtool.exe --python socket.py`: 119 | 120 | ``` 121 | 27 Mar 2013 - 16:41:58: stem\socket.py (%PYTHONHOME\lib\site-packages\stem-1.0.1-py2.7.egg) 122 | 30 Apr 2014 - 09:54:04: f:\Programfiler\Python27\lib\socket.py 123 | ``` 124 | 125 | **E.g. 7**: If you have Lua installed, the `--lua` option will search in 126 | `%LUA_PATH` and `%LUA_CPATH` for a match. E.g.: 127 | `envtool.exe --lua [gs]*.dll`: 128 | 129 | ``` 130 | 12 Aug 2021 - 13:11:04: f:\MingW32\src\inet\Lua\luasocket\install\gem.dll 131 | 12 Aug 2021 - 15:25:09: f:\MingW32\src\inet\Lua\LuaSec\src\ssl.dll 132 | ``` 133 | 134 | **E.g. 8**: The `--evry` option combined with the `--regex` (or `-r`) is quite powerful. To find 135 | all directories with Unix man-pages, you can do this: 136 | `envtool.exe --evry -r "man[1-9]$"`: 137 | 138 | ``` 139 | 03 Jun 2014 - 17:56:08: f:\CygWin\usr\share\man\man1\ 140 | 03 Jun 2014 - 17:56:08: f:\CygWin\usr\share\man\man3\ 141 | 03 Jun 2014 - 17:56:08: f:\CygWin\usr\share\man\man5\ 142 | 03 Jun 2014 - 17:56:08: f:\CygWin\usr\share\man\man7\ 143 | 03 Jun 2014 - 17:56:08: f:\CygWin\usr\share\man\man8\ 144 | ``` 145 | (assuming you have only 1 set of man-pages from a CygWin install) 146 | 147 | Or to find only `foo*.bar` files under directory-branch(es) `misc`, you can do: 148 | ``` 149 | envtool.exe --evry -r "misc\\.*\\foo.*\.bar" 150 | ``` 151 | 152 | Or how much space is *wasted* in your Python cache-files. This would tell you quickly: 153 | ``` 154 | envtool.exe --evry -sD __pycache__* 155 | ... 156 | 592 matches found for "__pycache__*". Totalling 125 MB (131,391,488 bytes). 157 | ``` 158 | 159 | Or report all >= 500 MByte files on the `c:` partition: 160 | ``` 161 | envtool --evry -s c:\* "size:>500MB" 162 | 01 Feb 2022 - 07:10:28 - 3 GB: c:\hiberfil.sys 163 | 27 Jan 2022 - 10:58:54 - 8 GB: c:\pagefile.sys 164 | ... 165 | 6 matches found for "c:\* size:>500MB". Totalling 14 GB (14,585,290,752 bytes). 166 | ``` 167 | 168 | **E.g. 9**: More than one option-mode can combined. For example: 169 | `envtool.exe --man --evry awk*.[1-9]*`: 170 | 171 | ``` 172 | Matches in %MANPATH: 173 | 13 Mar 2011 - 19:47:43: f:\CygWin\usr\share\man\man1\awk.1.gz (f:\CygWin\usr\share\man\man1\gawk.1) 174 | 09 Dec 1997 - 17:55:20: e:\djgpp\share\man\man1\awk.1 175 | 15 Dec 2004 - 02:06:32: e:\djgpp\share\man\cat1\awk.1 176 | Matches from EveryThing: 177 | 24 May 2014 - 12:48:16: e:\DJGPP\gnu\awk-1.3-3\ 178 | 09 Dec 1997 - 17:55:20: e:\DJGPP\share\man\man1\awk.1 179 | 01 Sep 2014 - 17:49:32: f:\MingW32\msys32\var\lib\pacman\local\awk-1.6-1\ 180 | ``` 181 | 182 | **E.g. 10**: All modes support showing the file-owner. Or only 183 | specific owners.
184 | For example: `envtool.exe --path --owner=Admin* --owner=S-1-5* add*` could return: 185 | ``` 186 | Matches in %PATH: 187 | 25 Jun 2015 - 17:46:39: Administratorer f:\MingW32\TDM-gcc\bin\addr2line.exe 188 | 01 Jun 2013 - 08:05:42: S-1-5-21...1001 f:\MingW32\bin\addr2line.exe 189 | 17 Aug 2001 - 22:04:02: Administratorer f:\ProgramFiler\Support-Tools\addiag.exe 190 | 04 Nov 1999 - 09:45:44: Administratorer f:\ProgramFiler\RKsupport\addiag.exe 191 | 19 Jan 2018 - 00:12:23: Administratorer f:\gv\dx-radio\UHD\host\bin\addr_test.pdb 192 | ``` 193 | The inverse `envtool.exe --path --owner=!Admin*` is also possible; showing files on the
194 | `%PATH` who's owner does *not* match `Admin*`. 195 | 196 | **E.g. 11**: `envtool --check -v` does a shadow check for file-spec in these 197 | environment variables:
198 | `PATH`, `LIB`, `LIBRARY_PATH`,`INCLUDE`, `C_INCLUDE_PATH`, 199 | `CPLUS_INCLUDE_PATH`, `MANPATH`,
200 | `PKG_CONFIG_PATH`, `PYTHONPATH`,`CMAKE_MODULE_PATH`, `CLASSPATH` and `GOPATH`. 201 | 202 | For all directories in an env-var, build lists of files matching a `file_spec` and do a shadow check of files 203 | in all directories after each directory. This is to show possibly newer files (in "later" directories) that 204 | should be used instead. 205 | 206 | E.g. with a `PATH=c:\ProgramFiler\Python27;c:\CygWin32\bin` 207 | and these files: 208 | ``` 209 | c:\ProgramFiler\Python27\python.exe 24.06.2011 12:38 (oldest) 210 | c:\CygWin32\bin\python.exe 20.03.2019 18:32 211 | ``` 212 | 213 | then the oldest `python.exe` shadows the newest `python.exe`. 214 | Situation such as thise can be tricky to diagnose,
215 | but won't always hurt. If you get too many *shadow reports*, edit `%APPDATA%\envtool.cfg` like this: 216 | ``` 217 | [Shadow] 218 | ignore = f:\ProgramFiler\RKsupport\*.exe # ignore all .EXEs here 219 | ignore = unins00*.exe # Ignore uninstall programs everywhere. 220 | ``` 221 | 222 | **E.g. 12**: `envtool --evry --signed chrome.dll` does a signature check of all 223 | `chrome.dll` files: 224 | 225 | ``` 226 | 18 May 2022 - 06:45:16: c:\Users\Gisle\AppData\Local\Google\Chrome\Application\102.0.5005.63\chrome.dll 227 | ver 102.0.5005.63, 64-bit, Chksum OK (Verified, Google LLC). 228 | 09 Jul 2004 - 12:16:08: f:\gv\dx-radio\WiNRADiO\DRMplayer\MMPlayerSR\components\chrome.dll 229 | ver 0.0.0.0, 32-bit, Chksum OK (Not signed). 230 | ``` 231 | 232 | Here the `--pe` option is implicitly turned on. 233 | 234 | --- 235 | 236 | C-source included in `./src`. `Makefile.Windows` (GNU-make required) is for MSVC, clang-cl, Intel-ICX and Zig. 237 | `Makefile.VC` is for MSVC only. Enjoy! 238 | 239 | `#include `:
240 | *"I do not accept responsibility for any effects, adverse or otherwise, 241 | that this code may have on you, your computer, your sanity, your dog, 242 | and anything else that you can think of. Use it at your own risk."* 243 | 244 | 245 | Gisle Vanem [gvanem@yahoo.no](mailto:gvanem@yahoo.no). 246 | 247 | 248 | ### Changes: 249 | 250 | ``` 251 | 0.1: Initial version. 252 | 253 | 0.2: Added file date/time stamp. Check for suffix or trailing 254 | wildcard in file-specification. If not found add a trailing "*". 255 | 256 | 0.3: Handled the case where an env-var contains the current directory. 257 | E.g. when "PATH=./;c:\util", turn the "./" into CWD (using 'getcwd()') 258 | for 'stat("/.")' to work. Turn off command-line globbing in MinGW 259 | ('_CRT_glob = 0'). 260 | 261 | 0.4: Rudimentary check for Python 'sys.path' searching. 262 | 263 | 0.5: Add a directory search spec mode ("--dir foo*.bar" searches for 264 | directories only). Better handling of file-specs with a sub-directory 265 | part. E.g. "envtool --python win32\Demos\Net*". 266 | 267 | 0.6: Add support for POSIX-style file matching (using fnmatch() from djgpp). 268 | E.g. a file-spec can contain things like "foo/[a-d]bar". 269 | Note: it doesn't handle ranges on directories. Only ranges on directories 270 | withing Pyhon EGG-files are handled. 271 | 272 | Improved Python 'sys.path' searching. Uses zipinfo.exe and looks inside 273 | Python EGG-files (zip files) for a match. 274 | 275 | 0.7: Improved Python 'sys.path' searching. Look inside Python EGGs and ZIP-files 276 | for a match (this uses the zipinfo external program). 277 | Add colour-output option '-C'. 278 | 279 | 0.8: New option '--pe' outputs version info from resource-section. E.g.: 280 | envtool --path --pe vcbuild.* 281 | Matches in %PATH: 282 | 19 Mar 2010 - 15:02:22: g:\vc_2010\VC\vcpackages\vcbuild.dll 283 | ver 10.0.30319.1 284 | 285 | 0.9: Cosmetic changes in debug-output ('-d') and command-line parsing. 286 | 287 | 0.92: Added option "--evry" to check matches in Everything's database. 288 | This option queries the database via IPC. 289 | Ref. http://www.voidtools.com/support/everything/ 290 | 291 | 0.93: The "--python" option now loads Python dynamically (pythonXX.dll) 292 | and calls 'PyRun_SimpleString()' to execute Python programs. 293 | 294 | 0.94: Fixes for '--evry' (EveryThing database) searches. 295 | Drop '-i' option. 296 | Add '-r' option. 297 | 298 | 0.95: Tweaks for better 'fnmatch()' matches. 299 | Improved '--evry' Regular Expression (--regex) searches. 300 | Build the MSVC-version using '-MT' (drop the dependency on MSVC1*.DLL) 301 | 302 | 0.96: Better Python embedding; lookup python.exe in env-var %PYTHON, then on 303 | %PATH. Test for correct Python DLL. Report full name of python.exe in 304 | "envtool -V". 305 | 306 | 0.97: Lots of improvements. Print more details on "envtool -VV" (compiler and 307 | linker flags). 308 | 309 | 0.98: Added option "--man" to search for matches in all *subdir* of %MANPATH%. 310 | E.g. subdirs "man[1-9]" and "cat[1-9]". 311 | 312 | Added option "--cmake" to search for matches along the built-in Cmake 313 | module path and '%CMAKE_MODULE_PATH%'. 314 | 315 | 0.99: Option "--pe" now calls WinTrust functions (in win_trust.c) to check if 316 | a PE-file is "Verified", "Not trusted" etc. 317 | 318 | 1.0: Added option "--pkg" to search in pkg-config's searcch-path specified by 319 | %PKG_CONFIG_PATH%. 320 | 321 | 1.1: Added option "--check" to check for missing directories in all supported 322 | environment variables. 323 | Added reading of an config-file; "%APPDATA%\envtool.cfg" is read at startup 324 | to support files to ignore. Copy the included "envtool.cfg" to your "%APPDATA" 325 | folder if needed. 326 | 327 | 1.2: Modifiers for "--evry" option is now possible. E.g. 328 | envtool --evry *.exe rc:today - find today's changes of all *.exe files. 329 | envtool --evry Makefile.am content:pod2man - find Makefile.am with pod2man commands. 330 | envtool --evry M*.mp3 artist:Madonna "year:<2002" - find all Madonna M*.mp3 titles issued prior to 2002. 331 | 332 | 1.3: Enhanced "envtool --check -v" to look for shadowed files in important environment variables 333 | like PATH, INCLUDE and LIB. See E.g. 10. above. 334 | 335 | 1.4: Added a "--lua" option. 336 | 337 | ``` 338 | 339 | PS. This file is written with the aid of the **[Atom](https://atom.io/)** 340 | editor and it's **[Markdown-Preview](https://atom.io/packages/markdown-preview)**. 341 | A real time-saver. 342 | -------------------------------------------------------------------------------- /appveyor-script.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | :: 3 | :: AppVeyor .bat-file to run "build" or "test". 4 | :: 5 | :: Do not confuse 'appveyor.yml' by the 'cd' below. 6 | :: 7 | setlocal 8 | set APPVEYOR_BUILD=0 9 | 10 | :: 11 | :: One could also run this .BAT-file locally before testing with AppVeyor. 12 | :: Then the env-var '%APPVEYOR_BUILD_FOLDER%' will not exist. 13 | :: 14 | :: Otherwise when running on AppVeyor: 15 | :: Since only Python2.7 is in AppVeyor's PATH by default, add this 16 | :: Python 3.4 (x86) to the PATH. 17 | :: 18 | if not "%APPVEYOR_BUILD_FOLDER%" == "" set APPVEYOR_BUILD=1 19 | if "%APPVEYOR_BUILD_FOLDER%" == "" set APPVEYOR_BUILD_FOLDER=%~dp0 20 | set PATH=%PATH%;c:\Python34;c:\msys64\MinGW64\bin 21 | set PROMPT=$P$G 22 | 23 | if %1. == build. goto build 24 | if %1. == test. goto test 25 | 26 | echo Usage: %~dp0appveyor-script.bat "build / test" 27 | exit /b 1 28 | 29 | :build 30 | cd %APPVEYOR_BUILD_FOLDER%\src 31 | set WK_VER=8.1 32 | call nmake -nologo -f Makefile.VC all 33 | exit /b 34 | 35 | :: 36 | :: Run some "envtool" tests. 37 | :: 38 | :test 39 | :: 40 | :: For 'test_searchpath()' and 'NeedCurrentDirectoryForExePathA()' 41 | :: 42 | set NoDefaultCurrentDirectoryInExePath=1 43 | set APPDATA=%APPVEYOR_BUILD_FOLDER% 44 | set INCLUDE=%INCLUDE%;c:\Python34\include 45 | set VCPKG_ROOT=c:\Tools\vcpkg 46 | set COLUMNS=120 47 | 48 | cd %APPDATA% 49 | call :create_auth_files 50 | 51 | cd %APPDATA%\src 52 | 53 | :: 54 | :: Delete the quite limited Msys based Python; doesn't even have a 'pip' module 55 | :: 56 | del /q c:\msys64\MinGW64\bin\python.exe 2> NUL 57 | 58 | :: 59 | :: If running on Github Actions', delete this Python since it shadows for 60 | :: 'c:\ProgramData\Chocolatey\bin\python.exe' 61 | :: 62 | del /q c:\hostedtoolcache\windows\Python\3.7.9\x64\python.exe 2> NUL 63 | 64 | copy ..\CI\winsqlite3.dll > NUL 65 | 66 | echo on 67 | 68 | @call :green_msg Testing version output: 69 | .\envtool -VVV 70 | 71 | @call :green_msg Testing grep search and --inc mode: 72 | .\envtool --inc --grep PyOS_ pys*.h 73 | 74 | @call :green_msg Testing test output (show owner in test_PE_wintrust()): 75 | .\envtool --test --owner 76 | 77 | @call :green_msg Testing Python2 test output: 78 | .\envtool --test --python=py2 --verbose 79 | 80 | @call :green_msg Testing Python3 test output: 81 | .\envtool --test --python=py3 --verbose 82 | 83 | @call :green_msg Testing VCPKG output: 84 | .\envtool --vcpkg=all azure-u* 85 | 86 | @call :green_msg Testing ETP-searches (should fail): 87 | .\envtool -d --test --evry:ftp.github.com:21 88 | 89 | @call :green_msg Testing verbose check output: 90 | .\envtool --check --verbose 91 | 92 | :: @call :green_msg Testing win_glob: 93 | :: .\win_glob -fr "c:\Program Files (x86)\CMake" 94 | 95 | @call :green_msg Showing last 20 lines of cache-file:\n 96 | @"c:\Program Files\Git\usr\bin\tail" --lines=20 %TEMP%\envtool.cache 97 | 98 | @echo off 99 | del /q %APPDATA%\.netrc %APPDATA%\.authinfo winsqlite3.dll 100 | exit /b 101 | 102 | :: 103 | :: Create a '\.netrc' and '\.authinfo' files for testing of 'src/auth.c' functions 104 | :: 105 | :create_auth_files 106 | @call :green_msg Creating '%APPDATA%/.netrc'. 107 | echo # > .netrc 108 | echo # This .netrc file was generated from "appveyor-script.bat". >> .netrc 109 | echo # >> .netrc 110 | echo machine host1 login user1 password password1 >> .netrc 111 | echo machine host2 login user2 password password2 >> .netrc 112 | echo default login anonymous password your@email.address >> .netrc 113 | 114 | @call :green_msg Creating '%APPDATA%/.authinfo'. 115 | echo # > .authinfo 116 | echo # This .authinfo file was generated from "appveyor-script.bat". >> .authinfo 117 | echo # >> .authinfo 118 | echo machine host1 port 8080 login user1 password password1 >> .authinfo 119 | echo machine host2 port 7070 login user2 password password2 >> .authinfo 120 | echo default port 21 login anonymous password your@email.address >> .authinfo 121 | goto :EOF 122 | 123 | :: 124 | :: Use an 'echo.exe' with colour support in this sub-routine. 125 | :: 126 | :green_msg 127 | @c:\msys64\usr\bin\echo.exe -e -n "\n\e[1;32m%*\e[0m" 128 | @goto :EOF 129 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | environment: 2 | global: 3 | CPU: x86 4 | matrix: 5 | - REL_DBG: Release 6 | - REL_DBG: Debug 7 | 8 | install: 9 | # 10 | # Set compiler environment for MSVC first. 11 | # And start the CoreTemp program. Does not work. 12 | # 13 | - cmd: call "c:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" %CPU% 14 | 15 | build_script: 16 | - cmd: appveyor-script.bat build 17 | 18 | test_script: 19 | - cmd: appveyor-script.bat test 20 | 21 | -------------------------------------------------------------------------------- /doc/Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # This is a GNU-make Makefile for Envtool's './doc' 3 | # 4 | DOC_GENERATED = envtool.chm envtool.chw doxygen.log 5 | OUTPUT_GENERATED = $(addprefix output/, \ 6 | *.css *.html *.js *.png *.map *.md5 index.{hhc,hhk,hhp} ) 7 | 8 | # DOXY_ARGS = -d commentscan 9 | 10 | all: envtool.chm check_msc 11 | 12 | envtool.chm: Makefile envtool.Doxyfile main_page.dox envtool.css ../src/*.[ch] 13 | rm -f output/msc*.png ; \ 14 | doxygen $(DOXY_ARGS) envtool.Doxyfile > doxygen.log ; \ 15 | cd output ; \ 16 | hhc index.hhp ; \ 17 | mv -f envtool.chm .. 18 | @echo 'Doxygen done. Look in "$(realpath doxygen.log)" for messages.' 19 | 20 | check_msc: 21 | ifeq ($(wildcard output/msc*.png),) 22 | @echo 'MSCGEN failed.' 23 | else 24 | @echo 'MSCGEN run seemed okay.' 25 | endif 26 | 27 | clean: 28 | rm -fr output 29 | rm -f $(DOC_GENERATED) 30 | @echo '"clean" done.' 31 | -------------------------------------------------------------------------------- /doc/README.md: -------------------------------------------------------------------------------- 1 | ## Generating documentation 2 | 3 | The documentation is generated by **[Doxygen](http://www.stack.nl/~dimitri/doxygen/index.html)** 4 | via the `envtool.Doxyfile` config-file. 5 | 6 | Running the command `doxygen envtool.Doxyfile`, requires `hhc.exe`. 7 | This is part of the **[Microsoft HTML Workshop](https://www.microsoft.com/en-us/download/details.aspx?id=21138)**. 8 | This will generate the compressed HTML-file `envtool.chm` of the generated documentation. 9 | 10 | `envtool.chm` can be view by the command `start envtool.chm` (which is normally 11 | pointing to `"%WINDIR%\hh.exe"`). Or loaded in the excellent 12 | **[KchmViewer](http://www.ulduzsoft.com/linux/kchmviewer/getting-kchmviewer/)**. 13 | 14 | But it's much easier using the GNU `Makefile` directly. Enter this directory 15 | and issue `make` or `mingw32-make` with one if these targets: 16 | ``` 17 | make all - should produce envtool.chm in this directory. 18 | make clean - should delete all generated files. 19 | ``` 20 | 21 | *PS*. When using the `Index` menu in `"%WINDIR%\hh.exe"`, it will crash! 22 | Some issue with how I or Doxygen creates the `output/index.hhk` file? 23 | **[KchmViewer](http://www.ulduzsoft.com/linux/kchmviewer/getting-kchmviewer/)** does 24 | not exhibit this bug. 25 | 26 | -------------------------------------------------------------------------------- /doc/envtool.chm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gvanem/EnvTool/68027f3a5784650874b4fb4803e63de64a1ccd37/doc/envtool.chm -------------------------------------------------------------------------------- /doc/envtool.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin-top:1ex; margin-bottom:1ex; margin-left:4ex; color:black; 3 | background:white; 4 | font-family:"Trebuchet MS",Tahoma,Arial,Verdana,Helvetica,sans-serif; 5 | } 6 | 7 | table ul { margin-bottom:0pt } 8 | table { border="0" cellspacing="0" cellpadding="0" } 9 | 10 | h1, h2, h3, h4, h5, h6, th { color = darkblue } 11 | 12 | h1 { border-top:solid 5pt; font-size:300% } 13 | h2 { margin-top:20pt; margin-left:0ex; font-style:italic; border-top:solid 1pt } 14 | h3 { margin-top:20pt; font-style:italic; font-size:140% } 15 | h4 { margin-top:20pt; font-style:italic; font-size:180% } 16 | code { color: #4665A2; } 17 | 18 | td { background:#fffff3 } 19 | th { text-align:right; font-weight:bold; font-style:italic; 20 | vertical-align:top; white-space:nowrap; border-top: solid 1pt 21 | } 22 | 23 | a:hover { color:black; background:yellow } 24 | 25 | .name { text-align: left } 26 | .pfooter { margin-top:4ex; border-top:thin solid; margin-left=-3ex } 27 | .pheader { font-style:normal; margin-top:0; margin-left:-3ex; margin-bottom:4ex } 28 | .stdhead { background:#804080; color:#FFFFBF } 29 | .sbemu th { border-left:solid 1pt; border-right:solid 1pt; text-align:left } 30 | 31 | pre.fragment { 32 | border: 1px solid #C4CFE5; 33 | background-color: #FFFF; 34 | padding: 4px 6px; 35 | margin: 4px 8px 4px 2px; 36 | overflow: auto; 37 | word-wrap: break-word; 38 | font-size: 9pt; 39 | line-height: 125%; 40 | font-family: monospace, fixed; 41 | font-size: 105%; 42 | } 43 | -------------------------------------------------------------------------------- /doc/main_page.dox: -------------------------------------------------------------------------------- 1 | /* Define all the groups here: */ 2 | 3 | /**\addtogroup Authentication User and password retrieval 4 | * @{ 5 | * Functions for parsing and lookup of host/user records in: 6 | * \li \c %APPDATA%/.netrc and 7 | * \li \c %APPDATA%/.authinfo 8 | * 9 | * @} 10 | */ 11 | 12 | /**\addtogroup Color printf()-style printing with colours 13 | * @{ 14 | * 15 | * @} 16 | */ 17 | 18 | /**\addtogroup Compiler compiler include and library search 19 | * @{ 20 | * 21 | * @} 22 | */ 23 | 24 | /**\addtogroup Envtool Main functions 25 | * @{ 26 | * 27 | * @} 28 | */ 29 | 30 | /**\addtogroup Envtool_PY Python functions 31 | * @{ 32 | * This consists of the files: 33 | * \li envtool_py.c 34 | * \li envtool_py.h 35 | * 36 | * @} 37 | */ 38 | 39 | /**\addtogroup EveryThing_SDK EveryThing SDK 40 | * @{ 41 | * This SDK consists of the files: 42 | * \li Everything.c 43 | * \li Everything.h 44 | * \li Everything_IPC.h 45 | * @} 46 | */ 47 | 48 | /**\addtogroup EveryThing_ETP Remote FTP queries 49 | * @{ 50 | * This consists of the files: 51 | * \li Everything_ETP.c 52 | * \li Everything_ETP.h 53 | * @} 54 | */ 55 | 56 | /**\addtogroup Lua Lua-mode search functions. 57 | * @{ 58 | * This consists of the files: 59 | * \li lua.c 60 | * \li lua.h 61 | * @} 62 | */ 63 | 64 | /**\addtogroup Misc Misc - Various support functions 65 | * @{ 66 | * This consists of the files: 67 | * \li dirlist.c, dirlist.h 68 | * \li getopt_long.c, getopt_long.h 69 | * \li ignore.c, ignore.h 70 | * \li misc.c, misc.h 71 | * \li pkg-config.c, vcpkg.h 72 | * \li regex.c, regex.h 73 | * \li report.c, report.h 74 | * \li searchpath.c 75 | * \li show_ver.c 76 | * \li smartlist.c, smartlist.h 77 | * \li vcpkg.c, vcpkg.h 78 | * \li win_trust.c, win_trust.h 79 | * \li win_sqlite3.c 80 | * \li win_ver.c 81 | * @} 82 | */ 83 | 84 | /** 85 | 86 | \mainpage Envtool 87 | \ingroup Envtool 88 | 89 |

Introduction

90 | 91 | Envtool is a tool to search along various environment variables for files (or a wildcard). 92 | The following modes handles these environment variables: 93 | 94 | 95 |
\c --cmake Check and search in \c \%CMAKE_MODULE_PATH\% 96 | and it's built-in module-path. 97 |
\c --cmake Check and search in \c \%CMAKE_MODULE_PATH\% and it's built-in module-path. 98 |
\c --evry[=\em host] Check and search in the [EveryThing](https://www.voidtools.com/) database. 99 |
\c --inc Check and search in \c \%INCLUDE\%. 100 |
\c --lib Check and search in \c \%LIB\% and \c \%LIBRARY_PATH\%. 101 |
\c --lua Check and search in \c \%LUA_PATH\% and \c \%LUA_CPATH\%. 102 |
\c --man Check and search in \c \%MANPATH\%. 103 |
\c --path Check and search in \c \%PATH\%. 104 |
\c --pkg Check and search in \c \%PKG_CONFIG_PATH\%. 105 |
\c --python[=\em X] Check and search in \c \%PYTHONPATH\% and \c sys.path[].
106 | The \em X selects a specific Python (if found). Can be one of these:
107 | 108 |
\c all Use all Python programs found on \c \%PATH\%. 109 | This is the default. 110 |
\c py2 Use a Python2 program only. 111 |
\c py3 Use a Python3 program only. 112 |
\c pypy Use a PyPy program only. 113 |
114 | Otherwise use only first Python found on PATH (i.e. the default). 115 |
\c --vcpkg[=\em all] Check and search for VCPKG packages. 116 |
\c --check Check for missing directories in all supported environment variables 117 | and missing files in these Registry keys: 118 | \verbatim HKCU\Microsoft\Windows\CurrentVersion\App Paths \endverbatim 119 | \verbatim HKLM\Microsoft\Windows\CurrentVersion\App Paths \endverbatim 120 |
121 | 122 |

Options

123 | 124 | Envtool contains a lot of options. These are: 125 | 126 |
127 | \c --no-gcc Don't spawn \c prefix -gcc prior to checking. [2]. 128 |
\c --no-g++ Don't spawn \c prefix -g++ prior to checking. [2] 129 |
\c --no-prefix Don't check any \c prefix -ed gcc/g++ programs [2]. 130 |
\c --no-sys Don't scan \c HKLM\\SYSTEM\\CurrentControlSet\\Control\\Session \c Manager\\Environment. 131 |
\c --no-usr Don't scan \c HKCU\\Environment. 132 |
\c --no-app Don't scan \code 133 | HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths 134 | HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths 135 | \endcode 136 |
\c --no-colour Don't print using colours. 137 |
\c --no-watcom Don't check for Watcom in \c --include or \c --lib mode 138 |
\c --owner Shown owner of the file. 139 |
\c --owner=\em [X] Shown only owners matching \c X. 140 |
\c --owner=\em [!X] Shown only owners \b not matching \c X. 141 |
\c --pe Print checksum, version-info and signing status for PE-files. 142 |
\c --32 Tell \c prefix -gcc to return only 32-bit libs in \c --lib mode.
143 | Report only 32-bit PE-files with \c --pe option. 144 |
\c --64 Tell \c prefix -gcc to return only 64-bit libs in \c --lib mode.
145 | Report only 64-bit PE-files with \c --pe option. 146 |
\c -c Don't add current directory to search-lists. 147 |
\c -C Be case-sensitive. 148 |
\c -d, \c --debug Set debug level (\c -dd sets \c PYTHONVERBOSE=1 in \c --python mode). 149 |
\c -D, \c --dir Looks only for directories matching \c \. 150 |
\c -H, \c --host Hostname/IPv4-address for remote FTP \c --evry searches. 151 | Can be used multiple times.
152 | Alternative syntax is \c --evry:host\[:port]. 153 | 154 |
\c --evry remote FTP options: 155 |
\c --nonblock-io connects using non-blocking I/O. 156 |
\c --buffered-io use buffering to receive the data. 157 | 158 |
\c -r, \c --regex Enable Regular Expressions in all \c --mode searches. 159 |
\c -s, \c --size Show size of file(s) found. With \c --dir option, recursively show
160 | the size of all files under directories matching \c \. 161 |
\c -q, \c --quiet Disable warnings. 162 |
\c -t Do some internal tests. 163 |
\c -T Show file times in sortable decimal format. E.g. \c "20121107.180658". 164 |
\c -u Show all paths on Unix format. E.g. \c c:/ProgramFiles/. 165 |
\c -v Increase verbose level (currently used in \c --pe and \c --check). 166 |
\c -V Show basic program version information.
167 | \c -VV and \c -VVV prints much more information. 168 |
\c -h, \c -? Show this help. 169 |
170 | 171 | The `prefix`es are: 172 | + `x86_64-w64-mingw32` 173 | + `i386-mingw32` 174 | + `i686-w64-mingw32` 175 | + `avr` for Arduino gcc development on Atmel processors 176 | 177 | To avoid specifying an option every time you run Envtool, that can also be specified in an 178 | environment variable `ENVTOOL_OPTIONS`.
179 | For example: 180 | ``` 181 | set ENVTOOL_OPTIONS=--no-app 182 | ``` 183 | 184 | will use the option `--no-app` every time Envtool is invoked. 185 | 186 |

Other Features

187 | 188 | 189 | */ 190 | -------------------------------------------------------------------------------- /envtool-help.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gvanem/EnvTool/68027f3a5784650874b4fb4803e63de64a1ccd37/envtool-help.png -------------------------------------------------------------------------------- /envtool-release.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gvanem/EnvTool/68027f3a5784650874b4fb4803e63de64a1ccd37/envtool-release.zip -------------------------------------------------------------------------------- /envtool.cfg: -------------------------------------------------------------------------------- 1 | # 2 | # %APPDATA%/envtool.cfg 3 | # 4 | # Names for cache-files 5 | # 6 | cache.filename = %TEMP%\envtool.cache 7 | cache.filename_prev = %TEMP%\envtool.cache-prev 8 | cache.enable = 1 9 | 10 | # 11 | # TODO: to enable searching in "Windows Subsystem for Linux" files, define the 12 | # Windows root for WSL here (only one): 13 | # 14 | # wsl.root = %LOCALAPPDATA%\Packages\CanonicalGroupLimited.Ubuntu22.04LTS_79rhkp1fndgsc\LocalState\rootfs 15 | 16 | # 17 | # TODO: to search for 'winget' packages, load this SQlite file: 18 | # 19 | # winget.db = %LOCALAPPDATA%\Packages\Microsoft.DesktopAppInstaller_8wekyb3d8bbwe\LocalState\Microsoft.Winget.Source_8wekyb3d8bbwe\installed.db 20 | # winget.log = %LOCALAPPDATA%\Packages\Microsoft.DesktopAppInstaller_8wekyb3d8bbwe\LocalState\DiagOutputDir\*.log 21 | 22 | # 23 | # Values for 'opt.beep' settings (struct beep_info). 24 | # 25 | beep.enable = 1 # Enable a beep after a long search. 26 | beep.limit = 1000 # How many msec a search should take before beeping. 27 | beep.freq = 2000 # The frequency of a beep. 28 | beep.msec = 20 # And it's duraction in msec. 29 | 30 | # 31 | # Settings for remote EveryThing searches (via the ETP protocol): 32 | # 33 | ETP.buffered_io = 1 # Use a read-ahead buffer for 'recv()' 34 | ETP.nonblock_io = 1 # Use 'select()' while waiting for a connect. 35 | 36 | # 37 | # Setting for "--grep" option. 38 | # 39 | grep.max_matches = 5 # Max number of matches to print per found file. 40 | 41 | # 42 | # You can use wildcards in all 'ignore =' values below. 43 | # Not just for files. I.e. in the below '[PE-resources]' section, you can specify: 44 | # ignore = File* 45 | # 46 | # Be careful not to "wildcard" too much. 47 | # 48 | 49 | # 50 | # Paths or path-specs to ignore if found on %PATH%: 51 | # 52 | [Path] 53 | # ignore = c:\Windows\Sysnative\OpenSSH 54 | 55 | # 56 | # Paths or path-specs to ignore if found in %LUA_PATH% or %LUA_CPATH%: 57 | # 58 | [Lua] 59 | # 60 | # If preferring "luaJIT.exe" over "lua.exe", set this to 1. 61 | # 62 | luajit.enable = 0 63 | 64 | # 65 | # Ignorables for 'LUA_PATH' and 'LUA_CPATH' 66 | # 67 | ignore = .git* 68 | 69 | # 70 | # Files to ignore warning about if *not* found in Registry. 71 | # Under "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths" 72 | # or "HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths" 73 | # 74 | [Registry] 75 | ignore = c:\windows\System32\what-ever-you-want.exe 76 | ignore = c:\windows\system32\what-is-missing-but-in-Registry.exe 77 | 78 | # 79 | # Compilers to ignore if found on %PATH%: 80 | # 81 | [Compiler] 82 | # ignore = 83 | 84 | # 85 | # Ignore these files as duplicates when found in an env-var (like '%PATH%' and '%INCLUDE%') 86 | # during 'envtool --check -v'. 87 | # 88 | # Each 'ignore' keyword can only have one file or file-spec value. 89 | # An 'ignore = foo.*' means 'envtool --check -v' will ignore any matching 'foo.*' file 90 | # in any supported environment variable. 91 | # 92 | [Shadow] 93 | dtime = 100000 ; seconds of file-time difference to ignore in 'envtool --check -v' 94 | 95 | ignore = %WinDir%\System32\*.exe 96 | ignore = %WinDir%\SysWOW64\*.exe 97 | ignore = %WinDir%\Sysnative\*.exe 98 | 99 | ignore = unins00*.exe # Uninstall programs are probably safe to ignore. 100 | ignore = link.exe # Cygwin usually has this program. So does MSVC. 101 | 102 | # 103 | # Pythons to ignore if found on %PATH%: 104 | # 105 | [Python] 106 | ignore = "c:\Program Files (x86)\some-slow-to-start-IronPython\ipy.exe" 107 | ignore = c:\some-other-slow-to-start-IronPython-files\ipy64.exe 108 | 109 | # 110 | # Things to ignore if found by EveryThing: 111 | # 112 | [EveryThing] 113 | # 114 | # 'Windows Defender' scan results: 115 | # 116 | ignore = "c:\ProgramData\Microsoft\Windows Defender\Scans\History\Results\*" 117 | 118 | # 119 | # 'Win-10 Cortana' files: 120 | # 121 | ignore = c:\Windows\SystemApps\Microsoft.Windows.Cortana_cw5n1h2txyewy\* 122 | 123 | busy_wait = 2 # If EveryThing is busy indexing itself, wait maximum 2 sec. 124 | 125 | # 126 | # Things to ignore if found in PE-resources (with options '--pe -v '): 127 | # 128 | [PE-resources] 129 | ignore = Signature 130 | ignore = StrucVersion 131 | # ignore = FileVersion 132 | # ignore = ProductVersion 133 | # ignore = FileFlagsMask 134 | # ignore = FileFlags 135 | # ignore = FileType 136 | # ignore = FileSubType 137 | # ignore = FileDate 138 | ignore = LangID 139 | ignore = Translation 140 | 141 | # 142 | # Login information used by remote EveryThing searches: 143 | # 144 | [Login] 145 | host1 = username1 / password1 / port 2121 146 | host2 = username2 / password2 / port 8080 147 | -------------------------------------------------------------------------------- /src/Everything_ETP.h: -------------------------------------------------------------------------------- 1 | /** \file Everything_ETP.h 2 | * \ingroup EveryThing_ETP 3 | */ 4 | #pragma once 5 | 6 | extern DWORD ETP_total_rcv; 7 | extern DWORD ETP_num_evry_dups; 8 | 9 | extern int do_check_evry_ept (const char *host); 10 | -------------------------------------------------------------------------------- /src/Makefile.VC: -------------------------------------------------------------------------------- 1 | # 2 | # EnvTool MSVC Nmake makefile. 3 | # 4 | DEBUG = 0 5 | STATIC = 0 6 | OBJ_DIR = objects\MSVC 7 | 8 | CC = cl 9 | CFLAGS = -nologo -W3 -Zi -Zo -Gy -Gs \ 10 | -D_CRT_SECURE_NO_WARNINGS -D_CRT_NONSTDC_NO_DEPRECATE \ 11 | -DWIN32_LEAN_AND_MEAN -DEVERYTHINGUSERAPI= -D_WIN32_IE=0x500 \ 12 | -DEVERYTHINGAPI=__cdecl -D_WIN32_WINNT=0x0602 -DUSE_SQLITE3 \ 13 | -DHAVE_WINSQLITE3_H -I./objects/MSVC 14 | 15 | LDFLAGS = -nologo -incremental:no -debug -map 16 | 17 | # 18 | # Enable a x86 .EXEs to run on Win-XP SP3 (5.1) or 19 | # a x64 .EXEs to run on Win-200 SP1 (5.2) 20 | # 21 | # Ref: https://learn.microsoft.com/en-us/cpp/build/reference/subsystem-specify-subsystem?view=msvc-160#remarks 22 | # 23 | MIN_VER = 5.02 24 | 25 | !if "$(PLATFORM)" == "x86" 26 | MIN_VER = 5.01 27 | !endif 28 | 29 | !if "$(CPU)" == "x86" 30 | MIN_VER = 5.01 31 | !endif 32 | 33 | LDFLAGS = $(LDFLAGS) -subsystem:console,$(MIN_VER) 34 | CFLAGS = $(CFLAGS) -wd4244 -wd4267 -wd4477 35 | 36 | RCFLAGS = -nologo -D_MSC_VER -r 37 | MSBUILD = msbuild -nologo -p:Platform="Win32" 38 | OS_LIBS = advapi32.lib crypt32.lib imagehlp.lib psapi.lib shell32.lib shlwapi.lib user32.lib version.lib wintrust.lib ws2_32.lib 39 | 40 | !if "$(DEBUG)" == "1" 41 | D = d 42 | CFLAGS = $(CFLAGS) -Os -RTCc -RTCs -RTCu 43 | RCFLAGS = $(RCFLAGS) -D_DEBUG 44 | MSBUILD = $(MSBUILD) -p:Configuration=Debug 45 | !else 46 | D = 47 | CFLAGS = $(CFLAGS) -Ot 48 | RCFLAGS = $(RCFLAGS) -D_RELEASE 49 | MSBUILD = $(MSBUILD) -p:Configuration=Release 50 | !endif 51 | 52 | !if "$(STATIC)" == "1" 53 | CFLAGS = -MT$(D) $(CFLAGS) 54 | !else 55 | CFLAGS = -MD$(D) $(CFLAGS) 56 | !endif 57 | 58 | !if "$(PLATFORM)" == "x64" 59 | RCFLAGS = $(RCFLAGS) -DWIN64 60 | !message "Building for x64" 61 | 62 | !else if "$(CPU)" == "X64" 63 | RCFLAGS = $(RCFLAGS) -DWIN64 64 | !message "Building for x64" 65 | 66 | !else 67 | !message "Building for x86" 68 | !endif 69 | 70 | OBJECTS = $(OBJ_DIR)\auth.obj \ 71 | $(OBJ_DIR)\envtool.obj \ 72 | $(OBJ_DIR)\envtool_py.obj \ 73 | $(OBJ_DIR)\cache.obj \ 74 | $(OBJ_DIR)\cfg_file.obj \ 75 | $(OBJ_DIR)\cmake.obj \ 76 | $(OBJ_DIR)\compiler.obj \ 77 | $(OBJ_DIR)\color.obj \ 78 | $(OBJ_DIR)\description.obj \ 79 | $(OBJ_DIR)\dirlist.obj \ 80 | $(OBJ_DIR)\Everything.obj \ 81 | $(OBJ_DIR)\Everything_ETP.obj \ 82 | $(OBJ_DIR)\find_vstudio.obj \ 83 | $(OBJ_DIR)\get_file_assoc.obj \ 84 | $(OBJ_DIR)\getopt_long.obj \ 85 | $(OBJ_DIR)\ignore.obj \ 86 | $(OBJ_DIR)\json.obj \ 87 | $(OBJ_DIR)\lua.obj \ 88 | $(OBJ_DIR)\misc.obj \ 89 | $(OBJ_DIR)\pkg-config.obj \ 90 | $(OBJ_DIR)\report.obj \ 91 | $(OBJ_DIR)\searchpath.obj \ 92 | $(OBJ_DIR)\show_ver.obj \ 93 | $(OBJ_DIR)\smartlist.obj \ 94 | $(OBJ_DIR)\sort.obj \ 95 | $(OBJ_DIR)\regex.obj \ 96 | $(OBJ_DIR)\tests.obj \ 97 | $(OBJ_DIR)\vcpkg.obj \ 98 | $(OBJ_DIR)\win_trust.obj \ 99 | $(OBJ_DIR)\win_sqlite3.obj \ 100 | $(OBJ_DIR)\win_ver.obj 101 | 102 | PROGRAMS = envtool.exe description.exe dirlist.exe du.exe get_file_assoc.exe win_glob.exe win_trust.exe win_ver.exe 103 | 104 | all: $(OBJ_DIR) $(OBJ_DIR)\cflags_cl.h $(OBJ_DIR)\ldflags_cl.h $(PROGRAMS) 105 | @echo '"$(PROGRAMS)" successfully built.' 106 | 107 | $(OBJ_DIR): 108 | mkdir objects 2> NUL 109 | mkdir objects\MSVC 2> NUL 110 | 111 | envtool.exe: $(OBJECTS) $(OBJ_DIR)\envtool.res 112 | link $(LDFLAGS) -verbose -out:$@ $** $(OS_LIBS) ole32.lib oleaut32.lib > link.tmp 113 | type link.tmp >> envtool.map 114 | -del /q link.tmp 115 | 116 | $(OBJ_DIR)\envtool.res: envtool.rc 117 | rc $(RCFLAGS) -fo $@ envtool.rc 118 | @echo 119 | 120 | description.exe: description.c dirlist.c misc.c color.c searchpath.c smartlist.c 121 | $(CC) -c $(CFLAGS) -DDESCRIPTION_TEST -Fo./$(OBJ_DIR)\\ $** 122 | link $(LDFLAGS) -out:$@ @<< 123 | $(OBJ_DIR)\description.obj 124 | $(OBJ_DIR)\dirlist.obj 125 | $(OBJ_DIR)\misc.obj 126 | $(OBJ_DIR)\color.obj 127 | $(OBJ_DIR)\searchpath.obj 128 | $(OBJ_DIR)\smartlist.obj 129 | $(OS_LIBS) 130 | << 131 | del /q $(OBJ_DIR)\description.obj 132 | @echo 133 | 134 | dirlist.exe: dirlist.c misc.c color.c getopt_long.c searchpath.c smartlist.c 135 | $(CC) -c $(CFLAGS) -DDIRLIST_TEST -Fo./$(OBJ_DIR)\\ $** 136 | link $(LDFLAGS) -out:$@ @<< 137 | $(OBJ_DIR)\dirlist.obj 138 | $(OBJ_DIR)\misc.obj 139 | $(OBJ_DIR)\color.obj 140 | $(OBJ_DIR)\getopt_long.obj 141 | $(OBJ_DIR)\searchpath.obj 142 | $(OBJ_DIR)\smartlist.obj 143 | $(OS_LIBS) 144 | << 145 | del /q $(OBJ_DIR)\dirlist.obj 146 | @echo 147 | 148 | du.exe: $(OBJ_DIR)\du.obj 149 | link $(LDFLAGS) -out:$@ $** 150 | @echo 151 | 152 | get_file_assoc.exe: get_file_assoc.c color.c dirlist.c misc.c smartlist.c 153 | $(CC) -c $(CFLAGS) -DGET_FILE_ASSOC_TEST -Fo./$(OBJ_DIR)\\ $** 154 | link $(LDFLAGS) -out:$@ @<< 155 | $(OBJ_DIR)\get_file_assoc.obj 156 | $(OBJ_DIR)\color.obj 157 | $(OBJ_DIR)\dirlist.obj 158 | $(OBJ_DIR)\misc.obj 159 | $(OBJ_DIR)\smartlist.obj 160 | $(OS_LIBS) 161 | << 162 | del /q $(OBJ_DIR)\get_file_assoc.obj 163 | @echo 164 | 165 | win_glob.exe: win_glob.c dirlist.c misc.c color.c getopt_long.c searchpath.c smartlist.c 166 | $(CC) -c $(CFLAGS) -DWIN_GLOB_TEST -Fo./$(OBJ_DIR)\\ $** 167 | link $(LDFLAGS) -out:$@ @<< 168 | $(OBJ_DIR)\win_glob.obj 169 | $(OBJ_DIR)\dirlist.obj 170 | $(OBJ_DIR)\misc.obj 171 | $(OBJ_DIR)\color.obj 172 | $(OBJ_DIR)\getopt_long.obj 173 | $(OBJ_DIR)\searchpath.obj 174 | $(OBJ_DIR)\smartlist.obj 175 | $(OS_LIBS) 176 | << 177 | del /q $(OBJ_DIR)\win_glob.obj 178 | @echo 179 | 180 | win_ver.exe: win_ver.c dirlist.c misc.c color.c searchpath.c smartlist.c 181 | $(CC) -c $(CFLAGS) -DWIN_VER_TEST -Fo./$(OBJ_DIR)\\ $** 182 | link $(LDFLAGS) -out:$@ @<< 183 | $(OBJ_DIR)\win_ver.obj 184 | $(OBJ_DIR)\dirlist.obj 185 | $(OBJ_DIR)\misc.obj 186 | $(OBJ_DIR)\color.obj 187 | $(OBJ_DIR)\searchpath.obj 188 | $(OBJ_DIR)\smartlist.obj 189 | $(OS_LIBS) 190 | << 191 | del /q $(OBJ_DIR)\win_ver.obj 192 | @echo 193 | 194 | win_trust.exe: win_trust.c dirlist.c getopt_long.c misc.c color.c searchpath.c smartlist.c 195 | $(CC) -c $(CFLAGS) -DWIN_TRUST_TEST -Fo./$(OBJ_DIR)\\ $** 196 | link $(LDFLAGS) -out:$@ @<< 197 | $(OBJ_DIR)\win_trust.obj 198 | $(OBJ_DIR)\dirlist.obj 199 | $(OBJ_DIR)\getopt_long.obj 200 | $(OBJ_DIR)\misc.obj 201 | $(OBJ_DIR)\color.obj 202 | $(OBJ_DIR)\searchpath.obj 203 | $(OBJ_DIR)\smartlist.obj 204 | $(OS_LIBS) 205 | << 206 | del /q $(OBJ_DIR)\win_trust.obj 207 | @echo 208 | 209 | .c{$(OBJ_DIR)}.obj: 210 | $(CC) -c $(CFLAGS) -Fo.\$*.obj $< 211 | @echo 212 | 213 | $(OBJ_DIR)\cflags_cl.h: Makefile.VC 214 | @echo const char *cflags = "$(CC) $(CFLAGS)"; > $@ 215 | 216 | $(OBJ_DIR)\ldflags_cl.h: Makefile.VC 217 | @echo const char *ldflags = "link $(LDFLAGS) -verbose -out:envtool.exe $(OBJECTS:\=/) $(OBJ_DIR)/envtool.res $(OS_LIBS) ole32.lib oleaut32.lib"; > $@ 218 | 219 | clean vclean: 220 | -rd /Q /S objects 221 | -del /q *.sbr vc1*.idb vc*.pdb link.tmp \ 222 | envtool.map description.exe dirlist.exe du.exe win_glob.exe win_trust.exe win_ver.exe \ 223 | envtool.exe description.map dirlist.map du.map win_glob.map win_trust.map win_ver.map \ 224 | envtool.pdb description.pdb dirlist.pdb du.pdb win_glob.pdb win_trust.pdb win_ver.pdb 225 | 226 | msbuild: 227 | $(MSBUILD) envtool.sln 228 | @echo envtool.exe successfully built. 229 | 230 | msclean: clean 231 | $(MSBUILD) -target:clean envtool.sln 232 | -del /q envtool.sdf 233 | -rd /q Debug Release 234 | 235 | $(OBJ_DIR)\auth.obj: auth.c color.h envtool.h getopt_long.h sort.h smartlist.h auth.h 236 | $(OBJ_DIR)\cache.obj: cache.c envtool.h getopt_long.h sort.h smartlist.h color.h cache.h 237 | $(OBJ_DIR)\cfg_file.obj: cfg_file.c color.h cfg_file.h envtool.h getopt_long.h sort.h smartlist.h 238 | $(OBJ_DIR)\envtool.res: envtool.h 239 | $(OBJ_DIR)\envtool.obj: envtool.c getopt_long.h Everything.h Everything_IPC.h Everything_ETP.h \ 240 | envtool.h envtool_py.h dirlist.h auth.h color.h smartlist.h \ 241 | sort.h $(OBJ_DIR)\cflags_cl.h $(OBJ_DIR)\ldflags_cl.h lua.h 242 | $(OBJ_DIR)\envtool_py.obj: envtool_py.c envtool.h envtool_py.h color.h dirlist.h smartlist.h 243 | $(OBJ_DIR)\Everything.obj: Everything.c Everything.h Everything_IPC.h 244 | $(OBJ_DIR)\Everything_ETP.obj: Everything_ETP.c color.h envtool.h auth.h Everything_ETP.h 245 | $(OBJ_DIR)\getopt_long.obj: getopt_long.c getopt_long.h 246 | $(OBJ_DIR)\color.obj: color.c color.h 247 | $(OBJ_DIR)\dirlist.obj: dirlist.c envtool.h color.h dirlist.h getopt_long.h 248 | $(OBJ_DIR)\json.obj: json.c envtool.h getopt_long.h sort.h smartlist.h report.h color.h json.h 249 | $(OBJ_DIR)\lua.obj: lua.c envtool.h getopt_long.h sort.h smartlist.h report.h color.h lua.h 250 | $(OBJ_DIR)\misc.obj: misc.c envtool.h color.h 251 | $(OBJ_DIR)\pkg-config.obj: pkg-config.c envtool.h getopt_long.h sort.h smartlist.h color.h report.h cache.h pkg-config.h 252 | $(OBJ_DIR)\regex.obj: regex.c regex.h envtool.h 253 | $(OBJ_DIR)\report.obj: report.c envtool.h getopt_long.h sort.h smartlist.h color.h pkg-config.h Everything_ETP.h ignore.h description.h report.h 254 | $(OBJ_DIR)\searchpath.obj: searchpath.c envtool.h 255 | $(OBJ_DIR)\show_ver.obj: show_ver.c envtool.h 256 | $(OBJ_DIR)\smartlist.obj: smartlist.c smartlist.h envtool.h 257 | $(OBJ_DIR)\vcpkg.obj: vcpkg.c envtool.h smartlist.h color.h dirlist.h vcpkg.h 258 | $(OBJ_DIR)\win_glob.obj: win_glob.c envtool.h win_glob.h 259 | $(OBJ_DIR)\win_trust.obj: win_trust.c getopt_long.h envtool.h 260 | $(OBJ_DIR)\win_ver.obj: win_ver.c envtool.h 261 | 262 | -------------------------------------------------------------------------------- /src/auth.c: -------------------------------------------------------------------------------- 1 | /**\file auth.c 2 | * \ingroup Authentication 3 | * 4 | * Used to login to a remote EveryThing FTP-server before doing queries. 5 | * 6 | * The syntax `"~/xx"` (meaning file `"xx"` in the user's home directory) really 7 | * means `"%APPDATA%\\xx"`. 8 | * 9 | * This file is part of envtool. 10 | * 11 | * By Gisle Vanem August 2017. 12 | */ 13 | #include "color.h" 14 | #include "envtool.h" 15 | #include "auth.h" 16 | 17 | /**\typedef login_source 18 | * The source of the login information_: 19 | * either `~/.netrc`, `~/.authinfo` or `~/envtool.cfg`. 20 | */ 21 | typedef enum login_source { 22 | LOGIN_NETRC, 23 | LOGIN_AUTHINFO, 24 | LOGIN_ENVTOOL_CFG, 25 | } login_source; 26 | 27 | /**\typedef login_info 28 | * Data for each parsed entry from one of the files in `enum login_source`. 29 | */ 30 | typedef struct login_info { 31 | bool is_default; /**< This is the `default` user/password entry for non-matching lookups */ 32 | login_source src; /**< Which file this entry came from */ 33 | char *host; /**< The hostname of the entry */ 34 | char *user; /**< The username of the entry */ 35 | char *passw; /**< The password of the entry */ 36 | int port; /**< The network port of the entry. Only if from `~/.authinfo` or `~/envtool.cfg` */ 37 | } login_info; 38 | 39 | /** The smartlists of "login_info" entries. 40 | */ 41 | static smartlist_t *login_list [LOGIN_ENVTOOL_CFG+1]; 42 | 43 | /** 44 | * \def GET_LOGIN_LIST(src) 45 | * Returns the `login_list[]` smartlist for `src`. 46 | */ 47 | #define GET_LOGIN_LIST(src) ( ASSERT (src >= LOGIN_NETRC && src < LOGIN_ENVTOOL_CFG), \ 48 | login_list [(src))-1] \ 49 | ) 50 | 51 | /** 52 | * Return the name of the login source. 53 | */ 54 | static const char *login_src_name (enum login_source src) 55 | { 56 | return ((src == LOGIN_NETRC) ? "NETRC" : 57 | (src == LOGIN_AUTHINFO) ? "AUTHINFO" : 58 | (src == LOGIN_ENVTOOL_CFG) ? "ENVTOOL_CFG" : "?"); 59 | } 60 | 61 | /** 62 | * Common to both `netrc_init()` and `authinfo_init()`. 63 | * The `login_list [src]` is returned from `smartlist_read_file()`. 64 | */ 65 | static int common_init (const char *fname, smartlist_parse_func parser, login_source src) 66 | { 67 | char *file = getenv_expand (fname); 68 | smartlist_t *sl; 69 | 70 | ASSERT (login_list[src] == NULL); 71 | 72 | sl = file ? smartlist_read_file (parser, file) : NULL; 73 | FREE (file); 74 | 75 | if (!sl) 76 | return (0); 77 | 78 | login_list [src] = sl; 79 | TRACE (2, "smartlist_len (0x%p): %d.\n", sl, smartlist_len(sl)); 80 | return (1); 81 | } 82 | 83 | /** 84 | * Free the memory allocated in the `login_list [src]` smartlist.
85 | */ 86 | static void common_exit (enum login_source src) 87 | { 88 | int i, max = login_list [src] ? smartlist_len (login_list[src]) : 0; 89 | 90 | for (i = 0; i < max; i++) 91 | { 92 | login_info *li = smartlist_get (login_list[src], i); 93 | 94 | TRACE (2, "i: %2d, %s.\n", i, login_src_name(li->src)); 95 | 96 | FREE (li->host); 97 | FREE (li->user); 98 | FREE (li->passw); 99 | FREE (li); 100 | } 101 | smartlist_free (login_list[src]); 102 | login_list[src] = NULL; 103 | } 104 | 105 | /** 106 | * Search the `login_list [src]` smartlist for `host`.
107 | */ 108 | static const login_info *common_lookup (const char *host, enum login_source src) 109 | { 110 | const login_info *def_li = NULL; 111 | int i, save, max = login_list[src] ? smartlist_len (login_list[src]) : 0; 112 | 113 | for (i = 0; i < max; i++) 114 | { 115 | const login_info *li = smartlist_get (login_list[src], i); 116 | char buf [300]; 117 | 118 | if (li->is_default) 119 | def_li = li; 120 | 121 | snprintf (buf, sizeof(buf), "%-12s host: '%s', user: '%s', passw: '%s', port: %d\n", 122 | login_src_name(li->src), li->is_default ? "*default*" : li->host, 123 | li->user, li->passw, li->port); 124 | 125 | if (opt.do_tests) 126 | { 127 | save = C_setraw (1); 128 | C_printf (" %s", buf); 129 | C_setraw (save); 130 | } 131 | else 132 | TRACE (3, buf); 133 | 134 | if (li->host && host && !stricmp(host, li->host)) 135 | return (li); 136 | } 137 | if (def_li) 138 | return (def_li); 139 | return (NULL); 140 | } 141 | 142 | /** 143 | * Parse a line from `"~/.netrc"`. Match lines like: 144 | * \code machine login password \endcode 145 | * Or 146 | * \code default login password \endcode 147 | */ 148 | static void netrc_parse (smartlist_t *sl, const char *line) 149 | { 150 | login_info *li = NULL; 151 | char host [256]; 152 | char user [50]; 153 | char passw [50]; 154 | const char *fmt1 = "machine %256s login %50s password %50s"; 155 | const char *fmt2 = "default login %50s password %50s"; 156 | 157 | if (sscanf(line, fmt1, host, user, passw) == 3) 158 | { 159 | li = CALLOC (1, sizeof(*li)); 160 | li->host = STRDUP (host); 161 | li->user = STRDUP (user); 162 | li->passw = STRDUP (passw); 163 | li->src = LOGIN_NETRC; 164 | } 165 | else if (sscanf(line, fmt2, user, passw) == 2) 166 | { 167 | li = CALLOC (1, sizeof(*li)); 168 | li->user = STRDUP (user); 169 | li->passw = STRDUP (passw); 170 | li->is_default = true; 171 | li->src = LOGIN_NETRC; 172 | } 173 | if (li) 174 | smartlist_add (sl, li); 175 | } 176 | 177 | /** 178 | * Parse a line from `"~/.authinfo"`. Match lines like:
179 | * `machine port login password ` 180 | * Or
181 | * `default port login password ` 182 | */ 183 | static void authinfo_parse (smartlist_t *sl, const char *line) 184 | { 185 | login_info *li = NULL; 186 | char host [256]; 187 | char user [50]; 188 | char passw [50]; 189 | int port = 0; 190 | const char *fmt1 = "machine %256s port %d login %50s password %50s"; 191 | const char *fmt2 = "default port %d login %50s password %50s"; 192 | 193 | if (sscanf(line, fmt1, host, &port, user, passw) == 4 && port > 0 && port < USHRT_MAX) 194 | { 195 | li = CALLOC (1, sizeof(*li)); 196 | li->host = STRDUP (host); 197 | li->user = STRDUP (user); 198 | li->passw = STRDUP (passw); 199 | li->port = port; 200 | li->src = LOGIN_AUTHINFO; 201 | } 202 | else if (sscanf(line, fmt2, &port, user, passw) == 3 && port > 0 && port < USHRT_MAX) 203 | { 204 | li = CALLOC (1, sizeof(*li)); 205 | li->user = STRDUP (user); 206 | li->passw = STRDUP (passw); 207 | li->port = port; 208 | li->is_default = true; 209 | li->src = LOGIN_AUTHINFO; 210 | } 211 | if (li) 212 | smartlist_add (sl, li); 213 | } 214 | 215 | /** 216 | * Parse a line from `"~/envtool.cfg"`. Match lines like: 217 | * ``` 218 | * [Login] 219 | * = / 220 | * = / / port 221 | * ``` 222 | * And add to the `login_list [LOGIN_ENVTOOL_CFG]` smartlist. 223 | */ 224 | bool auth_envtool_handler (const char *section, const char *key, const char *value) 225 | { 226 | char user [256]; 227 | char passw [256]; 228 | int port = 0; 229 | int num = sscanf (value, "%255[^ /] / %255[^ /] / port %d", user, passw, &port); 230 | 231 | if (num >= 2) 232 | { 233 | login_info *li = CALLOC (1, sizeof(*li)); 234 | 235 | if (!login_list[LOGIN_ENVTOOL_CFG]) 236 | login_list [LOGIN_ENVTOOL_CFG] = smartlist_new(); 237 | 238 | li->host = STRDUP (key); 239 | li->user = STRDUP (user); 240 | li->passw = STRDUP (passw); 241 | li->port = port; 242 | li->src = LOGIN_ENVTOOL_CFG; 243 | TRACE (2, "num: %d, host: '%s', user: '%s', passwd: '%s', port: %d.\n", num, li->host, li->user, li->passw, li->port); 244 | smartlist_add (login_list[LOGIN_ENVTOOL_CFG], li); 245 | } 246 | ARGSUSED (section); 247 | return (true); 248 | } 249 | 250 | /** 251 | * Open and parse the `"%APPDATA%\\.netrc"` file only once. 252 | */ 253 | static int netrc_init (void) 254 | { 255 | static int last_rc = -1; 256 | 257 | if (last_rc == -1) 258 | last_rc = common_init ("%APPDATA%\\.netrc", netrc_parse, LOGIN_NETRC); 259 | return (last_rc); 260 | } 261 | 262 | /** 263 | * Open and parse the `"%APPDATA%\\.authinfo"` file only once. 264 | */ 265 | static int authinfo_init (void) 266 | { 267 | static int last_rc = -1; 268 | 269 | if (last_rc == -1) 270 | last_rc = common_init ("%APPDATA%\\.authinfo", authinfo_parse, LOGIN_AUTHINFO); 271 | return (last_rc); 272 | } 273 | 274 | /** 275 | * Free the `login_list [LOGIN_NETRC]` entries assosiated with the `"%APPDATA%\\.netrc"` file. 276 | */ 277 | void netrc_exit (void) 278 | { 279 | common_exit (LOGIN_NETRC); 280 | } 281 | 282 | /** 283 | * Free the `login_list [LOGIN_AUTHINFO]` entries assosiated with the `"%APPDATA%\\.authinfo"` file. 284 | */ 285 | void authinfo_exit (void) 286 | { 287 | common_exit (LOGIN_AUTHINFO); 288 | } 289 | 290 | /** 291 | * Free the `login_list [LOGIN_ENVTOOL_CFG]` entries assosiated with the `"%APPDATA%\\envtool.cfg"` file. 292 | */ 293 | void envtool_cfg_exit (void) 294 | { 295 | common_exit (LOGIN_ENVTOOL_CFG); 296 | } 297 | 298 | static int return_login (const login_info *li, const char **user, const char **passw, int *port) 299 | { 300 | if (!li) 301 | return (0); 302 | 303 | if (user) 304 | *user = li->user; 305 | if (passw) 306 | *passw = li->passw; 307 | if (port) 308 | *port = li->port; 309 | return (1); 310 | } 311 | 312 | /** 313 | * Use this externally like: 314 | * `netrc_lookup (NULL, NULL, NULL)` can be used for test/debug. 315 | */ 316 | int netrc_lookup (const char *host, const char **user, const char **passw) 317 | { 318 | const login_info *li; 319 | 320 | if (!netrc_init()) 321 | return (0); 322 | 323 | li = common_lookup (host, LOGIN_NETRC); 324 | return return_login (li, user, passw, NULL); 325 | } 326 | 327 | /** 328 | * Use this externally like: 329 | * `authinfo_lookup (NULL, NULL, NULL, NULL)` can be used for test/debug. 330 | */ 331 | int authinfo_lookup (const char *host, const char **user, const char **passw, int *port) 332 | { 333 | const login_info *li; 334 | 335 | if (!authinfo_init()) 336 | return (0); 337 | 338 | li = common_lookup (host, LOGIN_AUTHINFO); 339 | return return_login (li, user, passw, port); 340 | } 341 | 342 | /** 343 | * Use this externally like: 344 | * `envtool_cfg_lookup (NULL, NULL, NULL, NULL)` can be used for test/debug. 345 | */ 346 | int envtool_cfg_lookup (const char *host, const char **user, const char **passw, int *port) 347 | { 348 | const login_info *li = common_lookup (host, LOGIN_ENVTOOL_CFG); 349 | 350 | /* Since a `~/envtool.cfg` does not have a default login entry, 351 | */ 352 | if (!li && !host) 353 | return (1); 354 | return return_login (li, user, passw, port); 355 | } 356 | 357 | -------------------------------------------------------------------------------- /src/auth.h: -------------------------------------------------------------------------------- 1 | /** \file auth.h 2 | * \ingroup Authentication 3 | */ 4 | #pragma once 5 | 6 | #include 7 | 8 | extern int netrc_lookup (const char *host, const char **user, const char **passw); 9 | extern int authinfo_lookup (const char *host, const char **user, const char **passw, int *port); 10 | extern int envtool_cfg_lookup (const char *host, const char **user, const char **passw, int *port); 11 | 12 | extern void netrc_exit (void); 13 | extern void authinfo_exit (void); 14 | extern void envtool_cfg_exit (void); 15 | extern bool auth_envtool_handler (const char *section, const char *key, const char *value); 16 | 17 | -------------------------------------------------------------------------------- /src/cache.h: -------------------------------------------------------------------------------- 1 | /** \file cache.h 2 | * \ingroup Misc 3 | */ 4 | #pragma once 5 | 6 | /** 7 | * \typedef CacheSections 8 | * 9 | * The cache sections we handle. 10 | */ 11 | typedef enum CacheSections { 12 | SECTION_FIRST = 0, /* Do not use this */ 13 | SECTION_CMAKE, 14 | SECTION_COMPILER, 15 | 16 | /** 17 | * For `CLASSPATH`, `PATH, `INCLUDE`, `MANPATH`, 18 | * `LIB`, `PERLLIBDIR`, `PKG_CONFIG_PATH` and 19 | * `PYTHONPATH` environment variables. 20 | */ 21 | SECTION_ENV_DIR, 22 | SECTION_LUA, 23 | SECTION_PKGCONFIG, 24 | SECTION_PYTHON, 25 | SECTION_VCPKG, 26 | SECTION_TEST, 27 | SECTION_LAST /* Do not use this */ 28 | } CacheSections; 29 | 30 | extern void cache_init (void); 31 | extern void cache_exit (void); 32 | extern void cache_test (void); 33 | extern bool cache_config (const char *key, const char *value); 34 | extern void cache_put (CacheSections section, const char *key, const char *value); 35 | extern void cache_putf (CacheSections section, _Printf_format_string_ const char *fmt, ...) ATTR_PRINTF(2,3); 36 | extern const char *cache_get (CacheSections section, const char *key); 37 | extern int cache_getf (CacheSections section, const char *fmt, ...); 38 | extern void cache_del (CacheSections section, const char *key); 39 | extern void cache_delf (CacheSections section, _Printf_format_string_ const char *fmt, ...) ATTR_PRINTF(2,3); 40 | 41 | -------------------------------------------------------------------------------- /src/cfg_file.c: -------------------------------------------------------------------------------- 1 | /**\file cfg_file.c 2 | * \ingroup Misc 3 | * \brief Functions for parsing a config-file. 4 | */ 5 | #include 6 | #include 7 | #include 8 | 9 | #include "color.h" 10 | #include "cfg_file.h" 11 | #include "envtool.h" 12 | 13 | /** 14 | * \def CFG_MAX_SECTIONS Maximum number of sections in a config-file. 15 | * \def CFG_SECTION_LEN Maximum length of a section name. 16 | * \def CFG_KEYWORD_LEN Maximum length of a keyword. 17 | * \def CFG_VALUE_LEN Maximum length of a value. 18 | * \def FGETS_BUF_LEN Length of the `fgets()` buffer 19 | * \def SSCANF_BUF_LEN Length of the `sscanf()` buffer 20 | */ 21 | #define CFG_MAX_SECTIONS 12 22 | #define CFG_SECTION_LEN 40 23 | #define CFG_KEYWORD_LEN 40 24 | #define CFG_VALUE_LEN 512 25 | #define FGETS_BUF_LEN (CFG_VALUE_LEN + CFG_KEYWORD_LEN + 2) 26 | #define SSCANF_BUF_LEN (CFG_KEYWORD_LEN + CFG_VALUE_LEN + 2*6 + 7) /* 2 * strlen("%[^= ]") + strlen(" = [^\r\n]") */ 27 | 28 | typedef struct CFG_FILE { 29 | FILE *file; 30 | char *fname; /**< The name of this config-file. */ 31 | unsigned line; /**< The line number in `config_get_line()`. */ 32 | int num_sections; /**< Number of sections / handlers set in `cfg_init()`. */ 33 | const char *sections [CFG_MAX_SECTIONS]; /**< The sections this structure handles. */ 34 | cfg_handler handlers [CFG_MAX_SECTIONS]; /**< The config-handlers for this config-file. */ 35 | smartlist_t *list; /**< A `smartlist_t` of `struct cfg_node *` */ 36 | 37 | /** The work-buffers used by `config_get_line()`. 38 | */ 39 | char section [CFG_SECTION_LEN+1]; 40 | char keyword [CFG_KEYWORD_LEN+1]; 41 | char value [CFG_VALUE_LEN+1]; 42 | } CFG_FILE; 43 | 44 | /** 45 | * \struct cfg_node 46 | * The structure for each key/value pair in a config-file. 47 | */ 48 | struct cfg_node { 49 | char *section; /**< The name of the section (allocated) */ 50 | char *key; /**< The key-name (allocated) */ 51 | char *value; /**< The key value (allocated) */ 52 | }; 53 | 54 | /** 55 | * Return the next line from the config-file with key, value and 56 | * section. 57 | * 58 | * \param[in] cf the config-file structure of the current config-file (`cf->fname`). 59 | * \retval 0 when we have reached end-of-file. 60 | * \retval >0 the line-number of the current line. 61 | */ 62 | static unsigned config_get_line (CFG_FILE *cf) 63 | { 64 | char *p, *q, *l_quote, *r_quote; 65 | 66 | cf->keyword[0] = cf->value[0] = '\0'; 67 | 68 | while (1) 69 | { 70 | char buf [FGETS_BUF_LEN]; 71 | char fmt [SSCANF_BUF_LEN]; 72 | 73 | if (!fgets(buf, sizeof(buf), cf->file)) /* EOF */ 74 | return (0); 75 | 76 | /* Remove leading spaces 77 | */ 78 | p = str_ltrim (buf); 79 | 80 | /* Ignore newlines or comment lines 81 | */ 82 | if (strchr("\r\n#;", *p)) 83 | { 84 | cf->line++; 85 | continue; 86 | } 87 | 88 | /* Got a `[section]` line. Find a `key = val`' on next `fgets()`. 89 | */ 90 | snprintf (fmt, sizeof(fmt), "[%%%d[^]\r\n]", CFG_SECTION_LEN); 91 | if (sscanf(p, fmt, cf->section) == 1) 92 | { 93 | cf->line++; 94 | continue; 95 | } 96 | 97 | snprintf (fmt, sizeof(fmt), "%%%d[^= ] = %%%d[^\r\n]", CFG_KEYWORD_LEN, CFG_VALUE_LEN); 98 | if (sscanf(p, fmt, cf->keyword, cf->value) != 2) 99 | { 100 | cf->line++; 101 | TRACE (3, "line %u: keyword: '%s', value: '%s'\n", cf->line, cf->keyword, cf->value); 102 | continue; 103 | } 104 | 105 | r_quote = strrchr (cf->value, '\"'); 106 | l_quote = strchr (cf->value, '\"'); 107 | 108 | /* Remove trailing `;` or `#` comment characters. 109 | * First check for a correctly quoted string value. 110 | */ 111 | if (l_quote && r_quote && r_quote > l_quote) 112 | q = r_quote; 113 | else q = cf->value; 114 | 115 | p = strchr (q, ';'); 116 | if (p) 117 | *p = '\0'; 118 | p = strchr (q, '#'); 119 | if (p) 120 | *p = '\0'; 121 | str_rtrim (cf->value); 122 | break; 123 | } 124 | return (++cf->line); 125 | } 126 | 127 | /** 128 | * Given a section, return the `cfg_handler` for it. 129 | * 130 | * \param[in] cf the config-file structure for the config-file. 131 | * \param[in] section the section string. 132 | */ 133 | static cfg_handler lookup_section_handler (CFG_FILE *cf, const char *section) 134 | { 135 | int i; 136 | 137 | for (i = 0; i < cf->num_sections; i++) 138 | { 139 | if (section && !stricmp(section, cf->sections[i])) 140 | { 141 | TRACE (3, "Matched section '%s' at index %d.\n", cf->sections[i], i); 142 | return (cf->handlers[i]); 143 | } 144 | } 145 | return (NULL); 146 | } 147 | 148 | /* 149 | * warn_clang_style(). Print a warning for a cfg-file warning similar to how clang does it: 150 | * c:\Users\Gisle\AppData\Roaming\envtool.cfg(97): Section [Shadow], Unhandled setting: 'xdtime=100000'. 151 | * ^~~~~~~~~~~~~ 152 | */ 153 | static void warn_clang_style (const CFG_FILE *cf, const char *section, const char *key, const char *value) 154 | { 155 | size_t kv_len = strlen (key) + strlen (value); 156 | int save, len; 157 | char slash = (opt.show_unix_paths ? '/' : '\\'); 158 | char cfg_name [_MAX_PATH]; 159 | 160 | slashify2 (cfg_name, cf->fname, slash); 161 | if (kv_len > 50) 162 | { 163 | C_printf ("~6%s(%u):\n", cfg_name, cf->line); 164 | len = C_printf ("~5 Section %s, Unhandled setting: '%s=%s'\n~2", section, key, value); 165 | } 166 | else 167 | len = C_printf ("~6%s(%u): ~5Section %s, Unhandled setting: '%s=%s'\n~2", cfg_name, cf->line, section, key, value); 168 | 169 | save = C_setraw (1); 170 | C_printf ("%-*s^%s\n", (int)(len - kv_len - 3), "", str_repeat('~', kv_len)); 171 | C_setraw (save); 172 | C_puts ("~0"); 173 | } 174 | 175 | /** 176 | * Parse the config-file given in `cf->file`. 177 | * Build the `cf->list` smartlist as it is parsed. 178 | * 179 | * \param[in] cf the config-file structure. 180 | */ 181 | static void parse_config_file (CFG_FILE *cf) 182 | { 183 | TRACE (3, "file: %s.\n", cf->fname); 184 | 185 | while (1) 186 | { 187 | struct cfg_node *cfg; 188 | char *p; 189 | size_t p_size = CFG_SECTION_LEN + CFG_KEYWORD_LEN + 16; 190 | cfg_handler handler; 191 | 192 | if (!config_get_line(cf)) 193 | break; 194 | 195 | if (!cf->section[0]) 196 | strcpy (cf->section, ""); 197 | 198 | TRACE (3, "line %2u: [%s]: %s = %s\n", 199 | cf->line, cf->section, cf->keyword, cf->value); 200 | 201 | /* Ignore "foo = " 202 | */ 203 | if (!*cf->value) 204 | continue; 205 | 206 | cfg = MALLOC (sizeof(*cfg) + p_size); 207 | p = (char*) (cfg + 1); 208 | cfg->section = p; 209 | 210 | snprintf (cfg->section, p_size - sizeof(*cfg), "[%s] %s", cf->section, cf->keyword); 211 | p = strchr (cfg->section, ']'); 212 | p[1] = '\0'; 213 | cfg->key = p + 2; 214 | cfg->value = getenv_expand2 (cf->value); /* Allocates memory */ 215 | smartlist_add (cf->list, cfg); 216 | 217 | handler = lookup_section_handler (cf, cfg->section); 218 | if (handler) 219 | { 220 | if (!(*handler) (cfg->section, cfg->key, cfg->value)) 221 | warn_clang_style (cf, cfg->section, cfg->key, cfg->value); 222 | } 223 | else 224 | { 225 | if (!cfg->section || cfg->section[0] == '\0') 226 | TRACE (3, "%s(%u): Keyword '%s' = '%s' in the CFG_GLOBAL section.\n", 227 | cf->fname, cf->line, cfg->key, cfg->value); 228 | else 229 | TRACE (3, "%s(%u): Keyword '%s' = '%s' in unknown section '%s'.\n", 230 | cf->fname, cf->line, cfg->key, cfg->value, cfg->section); 231 | } 232 | } 233 | } 234 | 235 | /** 236 | * Open a config-file with a number of `[section]` and `key = value` pairs. 237 | * Build up the `cf->list` as we go along and calling the parsers 238 | * in the var-arg list. 239 | * 240 | * \param[in] fname The config-file to parse. 241 | * \param[in] section The first section to handle. 242 | * The next `va_arg` is the callback function for this section. 243 | */ 244 | CFG_FILE *cfg_init (const char *fname, const char *section, ...) 245 | { 246 | CFG_FILE *cf; 247 | va_list args; 248 | int i; 249 | 250 | cf = CALLOC (sizeof(*cf), 1); 251 | cf->list = smartlist_new(); 252 | cf->fname = getenv_expand2 (fname); /* Allocates memory */ 253 | cf->file = fopen (cf->fname, "rt"); 254 | if (!cf->file) 255 | { 256 | WARN ("Failed to open \"%s\" (%s).\n", cf->fname, strerror(errno)); 257 | cfg_exit (cf); 258 | return (NULL); 259 | } 260 | 261 | va_start (args, section); 262 | for (i = 0; section && i < CFG_MAX_SECTIONS; section = va_arg(args, const char*), i++) 263 | { 264 | if (!*section) 265 | section = "[]"; 266 | cf->sections [i] = section; 267 | cf->handlers [i] = va_arg (args, cfg_handler); 268 | cf->num_sections++; 269 | } 270 | if (i == CFG_MAX_SECTIONS && va_arg(args, const char*)) 271 | WARN ("Too many sections. Max %d.\n", CFG_MAX_SECTIONS); 272 | 273 | va_end (args); 274 | parse_config_file (cf); 275 | fclose (cf->file); 276 | cf->file = NULL; 277 | return (cf); 278 | } 279 | 280 | /** 281 | * Clean-up after `cfg_init()`. 282 | * 283 | * \param[in] cf the config-file structure. 284 | */ 285 | void cfg_exit (CFG_FILE *cf) 286 | { 287 | int i, max; 288 | 289 | if (!cf) 290 | return; 291 | 292 | max = cf->list ? smartlist_len (cf->list) : 0; 293 | for (i = 0; i < max; i++) 294 | { 295 | struct cfg_node *cfg = smartlist_get (cf->list, i); 296 | 297 | FREE (cfg->value); 298 | FREE (cfg); 299 | } 300 | 301 | smartlist_free (cf->list); 302 | FREE (cf->fname); 303 | FREE (cf); 304 | } 305 | -------------------------------------------------------------------------------- /src/cfg_file.h: -------------------------------------------------------------------------------- 1 | /** \file cfg_file.h 2 | * \ingroup Misc 3 | */ 4 | #ifndef _CFG_FILE_H 5 | #define _CFG_FILE_H 6 | 7 | #include 8 | #include 9 | 10 | /** 11 | * \typedef struct CFG_FILE 12 | * The opaque structure returned by `cfg_init()`. 13 | */ 14 | typedef struct CFG_FILE CFG_FILE; 15 | 16 | /** 17 | * \typedef cfg_handler 18 | * A config-file handler should match this prototype. 19 | */ 20 | typedef bool (*cfg_handler) (const char *section, 21 | const char *key, 22 | const char *value); 23 | 24 | extern CFG_FILE *cfg_init (const char *fname, 25 | const char *section, 26 | /* cfg_handler parser */ ...); 27 | 28 | extern void cfg_exit (CFG_FILE *cf); 29 | 30 | #endif 31 | 32 | -------------------------------------------------------------------------------- /src/cmake.c: -------------------------------------------------------------------------------- 1 | /**\file cmake.c 2 | * \ingroup Misc 3 | * \brief Functions for CMake. 4 | */ 5 | #include "envtool.h" 6 | #include "cache.h" 7 | #include "color.h" 8 | #include "cmake.h" 9 | 10 | /** 11 | * The global data for 'cmake.exe' found on `PATH`. 12 | */ 13 | static struct ver_info cmake_ver; 14 | static char *cmake_exe = NULL; 15 | 16 | /** 17 | * Get the value and data for a Kitware sub-key.
18 | * Like: 19 | * `reg.exe query HKCU\Software\Kitware\CMake\Packages\gflags` 20 | * does: 21 | * ``` 22 | * HKEY_CURRENT_USER\Software\Kitware\CMake\Packages\gflags 23 | * 6dceedd62edc8337ea153c73497e3d9e REG_SZ f:/ProgramFiler-x86/gflags/lib/cmake/gflags 24 | * ^ ^ 25 | * |__ ret_uuid |___ ret_path 26 | * ``` 27 | */ 28 | static bool cmake_get_value_path (HKEY top_key, const char *key_name, char **ret_uuid, char **ret_path) 29 | { 30 | HKEY key = NULL; 31 | DWORD rc; 32 | u_long num = 0; 33 | REGSAM acc = reg_read_access(); 34 | static char _ret_uuid [100]; 35 | static char _ret_path [512]; 36 | 37 | *ret_uuid = *ret_path = NULL; 38 | 39 | rc = RegOpenKeyEx (top_key, key_name, 0, acc, &key); 40 | while (rc == ERROR_SUCCESS) 41 | { 42 | char uuid [200] = "\0"; 43 | char path [512] = "\0"; 44 | DWORD uuid_size = sizeof(uuid); 45 | DWORD path_size = sizeof(path); 46 | DWORD type = REG_NONE; 47 | 48 | rc = RegEnumValue (key, num++, uuid, &uuid_size, NULL, &type, (BYTE*)&path, &path_size); 49 | if (rc == ERROR_NO_MORE_ITEMS) 50 | break; 51 | 52 | if (type != REG_SZ) 53 | continue; 54 | 55 | if (uuid[0]) 56 | *ret_uuid = _strlcpy (_ret_uuid, uuid, sizeof(_ret_uuid)); 57 | if (path[0]) 58 | *ret_path = _strlcpy (_ret_path, path, sizeof(_ret_path)); 59 | } 60 | if (key) 61 | RegCloseKey (key); 62 | return (*ret_uuid && *ret_path); 63 | } 64 | 65 | /** 66 | * Iterate over Registry keys to find location of `.cmake` files.
67 | * Does what the command: 68 | * reg.exe query HKCU\Software\Kitware\CMake\Packages /s 69 | * reg.exe query HKLM\Software\Kitware\CMake\Packages /s 70 | * does. 71 | */ 72 | int cmake_get_info_registry (smartlist_t *sl, int *index, HKEY top_key) 73 | { 74 | HKEY key = NULL; 75 | int num; 76 | REGSAM acc = reg_read_access(); 77 | DWORD rc = RegOpenKeyEx (top_key, KITWARE_REG_NAME, 0, acc, &key); 78 | 79 | for (num = 0; rc == ERROR_SUCCESS; num++) 80 | { 81 | char package_key [512]; 82 | char package [100]; 83 | char *uuid = "?"; 84 | char *path = "?"; 85 | bool exist; 86 | DWORD size = sizeof(package); 87 | struct stat st; 88 | 89 | rc = RegEnumKeyEx (key, num, package, &size, NULL, NULL, NULL, NULL); 90 | if (rc == ERROR_NO_MORE_ITEMS) 91 | break; 92 | 93 | snprintf (package_key, sizeof(package_key), "%s\\%s", KITWARE_REG_NAME, package); 94 | cmake_get_value_path (top_key, package_key, &uuid, &path); 95 | uuid = _fix_uuid (uuid, NULL); 96 | path = _fix_path (path, NULL); 97 | exist = (safe_stat(path, &st, NULL) == 0); 98 | cache_putf (SECTION_CMAKE, "cmake_key%d = %s\\%s,%s,%s,%d", *index, reg_top_key_name(top_key), package_key, uuid, path, exist); 99 | 100 | if (sl && exist) 101 | smartlist_add_strdup (sl, path); 102 | if (opt.do_check) 103 | { 104 | if (opt.show_unix_paths) 105 | slashify2 (path, path, '/'); 106 | C_printf (" [%2d]: ~6%-15s~0 -> ~6%s%s~0\n", num, package, path, exist ? "": " ~5(Missing)"); 107 | } 108 | 109 | FREE (uuid); 110 | FREE (path); 111 | (*index)++; 112 | } 113 | if (key) 114 | RegCloseKey (key); 115 | return (num); 116 | } 117 | 118 | /** 119 | * Get CMake Registry entries from the cache. 120 | * Return it as a smartlist. 121 | */ 122 | static smartlist_t *cmake_cache_info_registry (void) 123 | { 124 | char format [50], *key, *uuid, *path; 125 | int i = 0, found = 0, exist; 126 | smartlist_t *sl = smartlist_new(); 127 | 128 | while (1) 129 | { 130 | snprintf (format, sizeof(format), "cmake_key%d = %%s,%%s,%%s,%%d", i); 131 | if (cache_getf(SECTION_CMAKE, format, &key, &uuid, &path, &exist) != 4) 132 | break; 133 | if (exist) 134 | smartlist_add_strdup (sl, path); 135 | TRACE (1, "%s: %s, %s, %d\n", key, uuid, path, exist); 136 | found++; 137 | i++; 138 | } 139 | TRACE (1, "Found %d cached entries for Cmake.\n", found); 140 | return (sl); 141 | } 142 | 143 | /** 144 | * Before checking the `CMAKE_MODULE_PATH`, we need to find the version and location 145 | * of `cmake.exe` (on `PATH`). Then assume it's built-in Module-path is relative to this. 146 | * E.g: 147 | * \code 148 | * cmake.exe -> f:\MinGW32\bin\CMake\bin\cmake.exe. 149 | * built-in path -> f:\MinGW32\bin\CMake\share\cmake-{major_ver}.{minor_ver}\Modules 150 | * \endcode 151 | */ 152 | static int cmake_version_cb (char *buf, int index) 153 | { 154 | static char prefix[] = "cmake version "; 155 | struct ver_info ver = { 0,0,0,0 }; 156 | char *p = buf + sizeof(prefix) - 1; 157 | 158 | if (str_startswith(buf, prefix) && 159 | sscanf(p, "%d.%d.%d", &ver.val_1, &ver.val_2, &ver.val_3) >= 2) 160 | { 161 | memcpy (&cmake_ver, &ver, sizeof(cmake_ver)); 162 | return (1); 163 | } 164 | ARGSUSED (index); 165 | return (0); 166 | } 167 | 168 | /** 169 | * Return the full path and version-information of `cmake.exe`. 170 | */ 171 | bool cmake_get_info (char **exe, struct ver_info *ver) 172 | { 173 | *ver = cmake_ver; 174 | *exe = NULL; 175 | 176 | /* We have already done this 177 | */ 178 | if (cmake_exe && VALID_VER(cmake_ver)) 179 | { 180 | *exe = STRDUP (cmake_exe); 181 | return (true); 182 | } 183 | 184 | TRACE (2, "ver: %d.%d.%d.\n", ver->val_1, ver->val_2, ver->val_3); 185 | 186 | cache_getf (SECTION_CMAKE, "cmake_exe = %s", &cmake_exe); 187 | cache_getf (SECTION_CMAKE, "cmake_version = %d,%d,%d", &cmake_ver.val_1, &cmake_ver.val_2, &cmake_ver.val_3); 188 | 189 | if (cmake_exe && !FILE_EXISTS(cmake_exe)) 190 | { 191 | cache_del (SECTION_CMAKE, "cmake_exe"); 192 | cache_del (SECTION_CMAKE, "cmake_version"); 193 | memset (&cmake_ver, '\0', sizeof(cmake_ver)); 194 | cmake_exe = NULL; 195 | return cmake_get_info (exe, ver); 196 | } 197 | 198 | if (!cmake_exe) 199 | cmake_exe = searchpath ("cmake.exe", "PATH"); 200 | 201 | if (!cmake_exe) 202 | return (false); 203 | 204 | cache_putf (SECTION_CMAKE, "cmake_exe = %s", cmake_exe); 205 | *exe = STRDUP (cmake_exe); 206 | 207 | if (!VALID_VER(cmake_ver) && popen_run(cmake_version_cb, cmake_exe, "-version") > 0) 208 | cache_putf (SECTION_CMAKE, "cmake_version = %d,%d,%d", cmake_ver.val_1, cmake_ver.val_2, cmake_ver.val_3); 209 | 210 | *ver = cmake_ver; 211 | TRACE (2, "ver: %d.%d.%d.\n", ver->val_1, ver->val_2, ver->val_3); 212 | 213 | return (cmake_exe && VALID_VER(cmake_ver)); 214 | } 215 | 216 | /** 217 | * Search for CMake modules matching the `opt.file_spec`. 218 | * 219 | * Search along these directories: 220 | * \li the builtin `modules_dir` 221 | * \li the environment variable `CMAKE_MODULE_PATH`. 222 | * \li the directories from the Registry. 223 | */ 224 | int cmake_search (void) 225 | { 226 | struct ver_info ver; 227 | char modules_dir [_MAX_PATH]; 228 | char *bin, *root, *dir; 229 | const char *env_name = "CMAKE_MODULE_PATH"; 230 | int found, i, max; 231 | smartlist_t *sl; 232 | 233 | if (!cmake_get_info(&bin, &ver)) 234 | { 235 | WARN ("cmake.exe not found on PATH.\n"); 236 | return (0); 237 | } 238 | 239 | sl = cmake_cache_info_registry(); 240 | if (smartlist_len(sl) == 0) 241 | { 242 | int index = 0; 243 | 244 | cmake_get_info_registry (sl, &index, HKEY_CURRENT_USER); 245 | cmake_get_info_registry (sl, &index, HKEY_LOCAL_MACHINE); 246 | } 247 | 248 | root = dirname (bin); 249 | snprintf (modules_dir, sizeof(modules_dir), "%s\\..\\share\\cmake-%d.%d\\Modules", root, ver.val_1, ver.val_2); 250 | _fix_path (modules_dir, modules_dir); 251 | 252 | TRACE (1, "found Cmake version %d.%d.%d. Module-dir -> '%s'\n", 253 | ver.val_1, ver.val_2, ver.val_3, modules_dir); 254 | 255 | report_header_set ("Matches in built-in Cmake modules:\n"); 256 | found = process_dir (modules_dir, 0, true, true, 1, true, env_name, HKEY_CMAKE_FILE); 257 | FREE (bin); 258 | FREE (root); 259 | 260 | report_header_set ("Matches in %%%s:\n", env_name); 261 | found += do_check_env (env_name); 262 | report_header_set (NULL); 263 | 264 | max = smartlist_len (sl); 265 | for (i = 0; i < max; i++) 266 | { 267 | if (i == 0) 268 | report_header_set ("Matches in Cmake Registry directories:\n"); 269 | dir = smartlist_get (sl, i); 270 | found += process_dir (dir, 0, true, true, 1, true, NULL, HKEY_CMAKE_FILE); 271 | } 272 | smartlist_free_all (sl); 273 | report_header_set (NULL); 274 | return (found); 275 | } 276 | 277 | -------------------------------------------------------------------------------- /src/cmake.h: -------------------------------------------------------------------------------- 1 | /** \file cmake.h 2 | * \ingroup Misc 3 | */ 4 | #pragma once 5 | 6 | /** 7 | * \def KITWARE_REG_NAME 8 | * The Kitware (Cmake) Registry key name under HKCU or HKLM. 9 | */ 10 | #define KITWARE_REG_NAME "Software\\Kitware\\CMake\\Packages" 11 | 12 | bool cmake_get_info (char **exe, struct ver_info *ver); 13 | int cmake_get_info_registry (smartlist_t *sl, int *index, HKEY top_key); 14 | int cmake_search (void); 15 | 16 | -------------------------------------------------------------------------------- /src/color.h: -------------------------------------------------------------------------------- 1 | /**\file color.h 2 | * \ingroup Color 3 | */ 4 | #ifndef _COLOR_H 5 | #define _COLOR_H 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #ifdef __cplusplus 13 | extern "C" { 14 | #endif 15 | 16 | /** 17 | * \brief 18 | * These are the default colour that `C_init_colour_map()` is initialised with. 19 | * Can be used like: 20 | * \code 21 | * C_printf (" " C_BR_RED "" C_DEFAULT); 22 | * \endcode 23 | * 24 | * But not after `C_init_colour_map()` has been called to alter these 25 | * colours with something else. 26 | */ 27 | #define C_BR_CYAN "~1" /**< bright cyan */ 28 | #define C_BR_GREEN "~2" /**< bright green */ 29 | #define C_BR_YELLOW "~3" /**< bright yellow */ 30 | #define C_BR_MEGENTA "~4" /**< bright magenta */ 31 | #define C_BR_RED "~5" /**< bright red */ 32 | #define C_BR_WHITE "~6" /**< bright white */ 33 | #define C_DK_CYAN "~7" /**< dark cyan */ 34 | #define C_BG_RED "~8" /**< white on red background */ 35 | #define C_BG_BLACK "~9" /**< white on black background (not yet) */ 36 | #define C_DEFAULT "~0" /**< restore default colour */ 37 | 38 | extern int C_use_colours; 39 | extern int C_use_ansi_colours; 40 | extern int C_no_ansi; 41 | extern int C_use_fwrite; 42 | 43 | /* 44 | * Defined in newer for MSVC. 45 | */ 46 | #ifndef _Printf_format_string_ 47 | #define _Printf_format_string_ 48 | #endif 49 | 50 | extern int C_printf (_Printf_format_string_ const char *fmt, ...) 51 | #if defined(__GNUC__) || defined(__clang__) 52 | __attribute__ ((format(printf,1,2))) 53 | #endif 54 | ; 55 | 56 | extern int C_vprintf (const char *fmt, va_list args); 57 | extern int C_puts (const char *str); 58 | extern int C_putsn (const char *str, size_t len); 59 | extern int C_putc (int ch); 60 | extern int C_putc_raw (int ch); 61 | extern int C_setraw (int raw); 62 | extern int C_setbin (int bin); 63 | extern size_t C_flush (void); 64 | extern void C_reset (void); 65 | extern void C_init (void); 66 | extern void C_exit (void); 67 | extern void C_set_colour (unsigned short col); 68 | extern void C_set_ansi (unsigned short col); 69 | extern void C_puts_long_line (const char *start, size_t indent); 70 | extern int C_init_colour_map (unsigned short col1, ...); 71 | extern size_t C_screen_width (void); 72 | extern int C_trace_level (void); 73 | extern bool C_conemu_detected (void); 74 | extern bool C_winterm_detected(void); 75 | extern bool C_VT_detected (int cmd_only); 76 | 77 | #ifdef __cplusplus 78 | } 79 | #endif 80 | 81 | #endif /* _COLOR_H */ 82 | -------------------------------------------------------------------------------- /src/compiler.h: -------------------------------------------------------------------------------- 1 | /**\file compiler.h 2 | * \ingroup Compiler 3 | */ 4 | #ifndef _COMPILER_H 5 | #define _COMPILER_H 6 | 7 | #ifdef __cplusplus 8 | extern "C" { 9 | #endif 10 | 11 | /** 12 | * \enum compiler_type 13 | * The compiler types supported 14 | */ 15 | typedef enum compiler_type { 16 | CC_UNKNOWN = 0, /**< Unknown compiler (not initialised value) */ 17 | CC_GNU_GCC, /**< == 1: Some sort of (prefixed) `*gcc.exe`. */ 18 | CC_GNU_GXX, /**< == 2: Some sort of (prefixed) `*g++.exe`. */ 19 | CC_MSVC, /**< == 3: A MSVC compiler */ 20 | CC_CLANG, /**< == 4: A clang/clang-cl compiler */ 21 | CC_INTEL, /**< == 5: A Intel oneAPI compiler */ 22 | CC_BORLAND, /**< == 6: A Borland / Embarcadero compiler */ 23 | CC_WATCOM /**< == 7: A Watcom/OpenWatcom compiler */ 24 | } compiler_type; 25 | 26 | struct compiler_info; /* Opaque structure */ 27 | 28 | struct compiler_info *compiler_lookup (compiler_type type); 29 | const char *compiler_GCC_prefix_first (void); 30 | const char *compiler_GCC_prefix_next (void); 31 | 32 | void compiler_init (bool print_info, bool print_lib_path); 33 | void compiler_exit (void); 34 | 35 | int compiler_check_includes (compiler_type type); 36 | int compiler_check_libraries (compiler_type type); 37 | bool compiler_cfg_handler (const char *section, const char *key, const char *value); 38 | 39 | /** 40 | * Print compiler version and CFLAGS + LDFLAGS the 41 | * program was built with. 42 | */ 43 | const char *compiler_version (void); 44 | const char *compiler_clang_version (void); 45 | void compiler_print_build_cflags (void); 46 | void compiler_print_build_ldflags (void); 47 | 48 | #ifdef __cplusplus 49 | } 50 | #endif 51 | 52 | #endif 53 | -------------------------------------------------------------------------------- /src/description.c: -------------------------------------------------------------------------------- 1 | /**\file description.c 2 | * \ingroup Misc 3 | * 4 | * \brief Reading and parsing of 4NT/TCC-style file descriptions. 5 | */ 6 | #include 7 | #include 8 | 9 | #include "color.h" 10 | #include "smartlist.h" 11 | #include "envtool.h" 12 | #include "description.h" 13 | 14 | /** 15 | * \def MAX_DESCR 16 | * The maximum length of a file/directory description line we accept. 17 | * 4NT/TCC only accepts 20 - 511 bytes (can be changed by 18 | * the `4NT.INI` directive `DescriptionMax`). 19 | */ 20 | #define MAX_DESCR 1000 21 | 22 | /** 23 | * \struct descr_node 24 | * Elements for a file/directory with a description 25 | */ 26 | struct descr_node { 27 | char file_dir [_MAX_PATH]; /**< the file or directory name */ 28 | char file_descr [MAX_DESCR]; /**< the description of the above `file_name` (a file or a directory) */ 29 | bool is_dir; /**< true if `file_dir` is a directory */ 30 | }; 31 | 32 | /** 33 | * \struct descr_dir 34 | * Elements for an already checked directory with or without a `DESCRIPT.ION` file. 35 | */ 36 | struct descr_dir { 37 | /** 38 | * The directory this `DESCRIPT.ION` file is in. 39 | */ 40 | char dir [_MAX_PATH]; 41 | 42 | /** 43 | * The list of file/directory descriptions in this `dir`. 44 | * If NULL, this `dir` has no `DESCRIPT.ION` file. 45 | */ 46 | smartlist_t *descr; 47 | }; 48 | 49 | /** 50 | * All directories already checked. 51 | * A smartlist of `struct descr_dir` 52 | */ 53 | static smartlist_t *all_descr = NULL; 54 | 55 | /** 56 | * A simple hit-counter for `file_descr_get()`. 57 | */ 58 | static DWORD cache_hits = 0; 59 | 60 | /** 61 | * The default name of a description-file. 62 | */ 63 | static char descr_name [200] = "DESCRIPT.ION"; 64 | 65 | /** 66 | * The directory we're currently parsing for 67 | * a description-file. 68 | */ 69 | static const char *curr_dir; 70 | 71 | /** 72 | * Initialise this module. 73 | */ 74 | bool file_descr_init (void) 75 | { 76 | const char *env, *line, *shell; 77 | 78 | if (all_descr) 79 | return (false); 80 | 81 | all_descr = smartlist_new(); 82 | env = getenv ("COMSPEC"); 83 | if (!env) 84 | return (false); 85 | 86 | shell = strlwr (basename(env)); 87 | if (!strcmp(shell, "4nt.exe") || !strcmp(env, "tcc.exe")) 88 | { 89 | /* `%_DNAME` is an internal 4NT/TCC variable 90 | */ 91 | popen_run (NULL, env, "/C echo %%_dname"); 92 | line = popen_last_line(); 93 | TRACE (2, "line: '%s'.\n", line); 94 | if (*line && strchr(line, '.')) 95 | _strlcpy (descr_name, line, sizeof(descr_name)); 96 | } 97 | return (true); 98 | } 99 | 100 | /** 101 | * Report a summary for `all_descr`. 102 | */ 103 | static void file_descr_dump (void) 104 | { 105 | const struct descr_dir *dd; 106 | const struct descr_node *dn; 107 | int i, j, j_max, i_max = smartlist_len (all_descr); 108 | int save = C_setraw (1); 109 | 110 | C_printf ("file_descr_dump(): cache_hits: %lu\n" 111 | " i j is_dir Directory / 'File': Description\n" 112 | " ------------------------------------------------------------------------------------\n", 113 | cache_hits); 114 | 115 | for (i = 0; i < i_max; i++) 116 | { 117 | dd = smartlist_get (all_descr, i); 118 | j_max = dd->descr ? smartlist_len (dd->descr) : 0; 119 | 120 | C_printf (" %2d '%s':\n", i, dd->dir); 121 | for (j = 0; j < j_max; j++) 122 | { 123 | dn = smartlist_get (dd->descr, j); 124 | C_printf (" %2d %d '%-20.20s': %.80s\n", 125 | j, dn->is_dir, dn->file_dir, dn->file_descr); 126 | } 127 | C_putc ('\n'); 128 | } 129 | C_setraw (save); 130 | } 131 | 132 | /** 133 | * 'smartlist_wipe()' helper. 134 | * Free an item in the 'all_descr' smartlist. 135 | */ 136 | static void file_descr_free_all (void *_d) 137 | { 138 | struct descr_dir *dd = (struct descr_dir*) _d; 139 | 140 | smartlist_free_all (dd->descr); 141 | FREE (dd); 142 | } 143 | 144 | /** 145 | * Free memory allocated by this module. 146 | */ 147 | void file_descr_exit (void) 148 | { 149 | if (!all_descr) 150 | return; 151 | 152 | if (opt.debug >= 2) 153 | file_descr_dump(); 154 | 155 | smartlist_wipe (all_descr, file_descr_free_all); 156 | smartlist_free (all_descr); 157 | all_descr = NULL; 158 | } 159 | 160 | /** 161 | * Parser for a single `DESCRIPT.ION` file for a specific directory. 162 | * Add `struct descr_node` elements to this smartlist as we parse the file. 163 | */ 164 | static void file_descr_parse (smartlist_t *sl, const char *buf) 165 | { 166 | char file [_MAX_PATH] = "?"; 167 | char descr [MAX_DESCR] = "?"; 168 | const char *p = buf; 169 | int i; 170 | 171 | if (*p == '"') 172 | { 173 | p++; 174 | for (i = 0; i < sizeof(file)-2 && *p && *p != '"'; p++) 175 | file[i++] = *p; 176 | p++; 177 | } 178 | else 179 | { 180 | for (i = 0 ; i < sizeof(file)-1 && *p && !isspace((int)*p); p++) 181 | file[i++] = *p; 182 | } 183 | file[i] = '\0'; 184 | 185 | while (*p && isspace((int)*p)) 186 | p++; 187 | 188 | for (i = 0 ; i < sizeof(descr)-1; p++) 189 | { 190 | if (*p == '\0' || *p == '\r' || *p == '\n') 191 | break; 192 | descr[i++] = *p; 193 | } 194 | descr[i] = '\0'; 195 | 196 | /* Do not add an entry for a `DESCRIPT.ION` file. 197 | */ 198 | if (file[0] != '?' && descr[0] != '?' && stricmp(file, descr_name)) 199 | { 200 | struct descr_node *dn = CALLOC (1, sizeof(*dn)); 201 | char fqfn_name [_MAX_PATH+2]; 202 | 203 | _strlcpy (dn->file_dir, file, sizeof(dn->file_dir)); 204 | _strlcpy (dn->file_descr, descr, sizeof(dn->file_descr)); 205 | snprintf (fqfn_name, sizeof(fqfn_name), "%s\\%s", curr_dir, file); 206 | dn->is_dir = is_directory (fqfn_name); 207 | smartlist_add (sl, dn); 208 | } 209 | TRACE (2, "file: '%s', descr: '%s'.\n", file, descr); 210 | } 211 | 212 | /** 213 | * Lookup a description for a `file_dir` in `smartlist sl` (i.e. an `all_descr::descr` element). 214 | * This list is for a single directory. 215 | * 216 | * \retval !NULL The file/dir description found. 217 | * \retval NULL The file/dir have no description. 218 | */ 219 | static const char *file_descr_lookup (const smartlist_t *sl, const char *file_dir) 220 | { 221 | int i, max = smartlist_len (sl); 222 | 223 | TRACE (2, "Looking for file_dir: '%s'.\n", file_dir); 224 | 225 | for (i = 0; i < max; i++) 226 | { 227 | const struct descr_node *dn = smartlist_get (sl, i); 228 | 229 | TRACE (2, "i: %d, file_dir: %s.\n", i, dn->file_dir); 230 | if (!stricmp(file_dir, dn->file_dir)) 231 | return (dn->file_descr); 232 | } 233 | return (NULL); 234 | } 235 | 236 | #if defined(__GNUC__) && (__GNUC__ >= 7) 237 | // #pragma GCC diagnostic ignored "-Wformat-truncation" 238 | #endif 239 | 240 | /** 241 | * Allocate a new smartlist `struct descr_dir` element for this directory. 242 | * 243 | * The `smartlist_read_file()` builds up another smartlist for the `struct descr_node` 244 | * entries in this directory. 245 | * 246 | * We add this `dir` to the `all_descr` smartlist (a cache) to avoid calling 247 | * `smartlist_read_file()` every time. 248 | * 249 | * \retval !NULL The file/dir description found. 250 | * \retval NULL The file/dir have no description. 251 | */ 252 | static const char *file_descr_new (const char *dir, const char *file_dir) 253 | { 254 | struct descr_dir *dd; 255 | 256 | curr_dir = dir; 257 | dd = CALLOC (1, sizeof(*dd)); 258 | dd->descr = smartlist_read_file (file_descr_parse, "%s\\%s", dir, descr_name); 259 | _strlcpy (dd->dir, dir, sizeof(dd->dir)); 260 | 261 | if (dd->descr) 262 | TRACE (2, "Parser found %d descriptions for '%s'.\n", smartlist_len(dd->descr), dir); 263 | else TRACE (2, "Parser found no descriptions for files in '%s\\'.\n", dir); 264 | 265 | smartlist_add (all_descr, dd); 266 | 267 | /* Is it found now? 268 | */ 269 | if (dd->descr) 270 | return file_descr_lookup (dd->descr, file_dir); 271 | return (NULL); 272 | } 273 | 274 | /** 275 | * Lookup a file/directory description for a `file_dir` in the directory `dir`. 276 | * 277 | * if `dd->descr == NULL`, it means the `dir` was already 278 | * tried and we found no `descr_name` in it. Hence no point to look further. 279 | * 280 | * \retval !NULL The file/dir description was found. 281 | * \retval NULL The file/dir have no description. 282 | */ 283 | static const char *file_descr_lookup_all (const char *dir, const char *file_dir, bool *empty) 284 | { 285 | const struct descr_dir *dd; 286 | int i, max = smartlist_len (all_descr); 287 | 288 | *empty = false; 289 | TRACE (2, "%s(): max: %d, looking for dir: '%s'\n", __FUNCTION__, max, dir); 290 | 291 | for (i = 0; i < max; i++) 292 | { 293 | dd = smartlist_get (all_descr, i); 294 | TRACE (2, " i=%d: empty: %d, dir: '%s'\n", i, dd->descr ? 0 : 1, dd->dir); 295 | 296 | if (!stricmp(dir, dd->dir)) 297 | { 298 | if (dd->descr) 299 | return file_descr_lookup (dd->descr, file_dir); 300 | *empty = true; 301 | } 302 | } 303 | return (NULL); 304 | } 305 | 306 | /** 307 | * Lookup a file/dir description for a `file_dir` in the directory `dir`. 308 | * 309 | * Use already cached information or create a new node in the `all_descr` list. 310 | * 311 | * Should handle a relative path since `fix_path()` is used first: 312 | * \code 313 | * file_descr_get ("../envtool.cfg") -> 314 | * dir -> CWD of parent-dir 315 | * fname -> envtool.cfg 316 | * \endcode 317 | * 318 | * \retval !NULL The file/dir description was found. 319 | * \retval NULL The file/dir have no description. 320 | */ 321 | const char *file_descr_get (const char *file_dir) 322 | { 323 | const char *descr; 324 | char *fname, *dir, _file[_MAX_PATH]; 325 | bool empty; 326 | 327 | /* `file_descr_init()` was not called. 328 | * Or this function was called after `file_descr_exit()` 329 | */ 330 | if (!all_descr) 331 | return (NULL); 332 | 333 | _fix_path (file_dir, _file); 334 | dir = _file; 335 | fname = basename (_file); 336 | fname [-1] = '\0'; /* terminate 'dir' */ 337 | 338 | if (opt.debug > 0) 339 | C_putc ('\n'); 340 | TRACE (2, "file_dir: '%s', fname: '%s'\n", file_dir, fname); 341 | 342 | descr = file_descr_lookup_all (dir, fname, &empty); 343 | if (empty) 344 | return (NULL); 345 | 346 | if (descr) 347 | cache_hits++; 348 | else descr = file_descr_new (dir, fname); 349 | 350 | return (descr); 351 | } 352 | 353 | #if defined(DESCRIPTION_TEST) || defined(__DOXYGEN__) 354 | prog_options opt; 355 | 356 | static void test_init (int argc, char **argv) 357 | { 358 | if (argc >= 2) 359 | { 360 | if (!strcmp(argv[1], "-d")) 361 | opt.debug = 2; 362 | else if (!strcmp(argv[1], "-dd")) 363 | opt.debug = 3; 364 | } 365 | C_init(); 366 | crtdbug_init(); 367 | file_descr_init(); 368 | } 369 | 370 | static void test_exit (void) 371 | { 372 | file_descr_exit(); 373 | mem_report(); 374 | C_exit(); 375 | crtdbug_exit(); 376 | } 377 | 378 | /** 379 | * Create a `DESCRIPT.ION` file such that a `dir /mk /z ..\envtool.cfg` in 4NT/TCC would print: 380 | * ``` 381 | * envtool.cfg 2426 28.08.19 10.38 EnvTool config-file 382 | * ``` 383 | * 384 | * Create a long description for `..\envtool.exe` to compare our output with the output of 4NT/TCC: 385 | * ``` 386 | * envtool.exe 708096 3.10.19 12.18 EnvTool program. Just some long lines of text to test the parser. Lorem ipsum dolor sit amet, consectetur adipiscing elit-> 387 | * ``` 388 | */ 389 | static void test_create (void) 390 | { 391 | char fbuf [210]; 392 | FILE *fil; 393 | 394 | snprintf (fbuf, sizeof(fbuf), "../%s", descr_name); 395 | fil = fopen (fbuf, "w+t"); 396 | fputs ("\"envtool.exe\" EnvTool program. Just some long lines of text to test the parser. " 397 | "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras non nulla ac " 398 | "nibh venenatis ullamcorper. In ut dui lorem. Mauris molestie dolor quis erat " 399 | "interdum, vitae dignissim sapien cursus. Vestibulum pulvinar neque nec fringilla " 400 | "viverra. Nam feugiat condimentum nibh, sed cursus risus tempor eget. Vestibulum " 401 | "porttitor augue ut tellus vestibulum porta id nec erat. Proin pulvinar justo ut " 402 | "orci pharetra, ut rhoncus lorem tincidunt.\n" 403 | "envtool.cfg EnvTool config-file\n" 404 | "src EnvTool source directory\n", fil); 405 | fclose (fil); 406 | } 407 | 408 | int main (int argc, char **argv) 409 | { 410 | static const char *files[] = { 411 | "envtool.exe", /* calls 'file_descr_new()' */ 412 | "envtool.exe", /* Should be a negative cache-hit */ 413 | "../envtool.cfg", /* calls 'file_descr_new()' */ 414 | "../envtool.exe", /* Should be a positive cache-hit */ 415 | "../envtool.exe", /* Should be a positive cache-hit */ 416 | "../src" /* Test directory description. Should be a positive cache-hit */ 417 | }; 418 | int i; 419 | 420 | test_init (argc, argv); 421 | test_create(); 422 | 423 | for (i = 0; i < DIM(files); i++) 424 | { 425 | const char *f = files[i]; 426 | const char *descr = file_descr_get (f); 427 | 428 | printf ("%s -> descr: %s\n\n", f, descr); 429 | } 430 | 431 | if (cache_hits == 3) 432 | printf ("Cached logic seems to work.\n"); 433 | else printf ("Cached logic failed!? cache_hits: %lu.\n", cache_hits); 434 | 435 | test_exit(); 436 | return (0); 437 | } 438 | #endif /* DESCRIPTION_TEST || __DOXYGEN__ */ 439 | 440 | -------------------------------------------------------------------------------- /src/description.h: -------------------------------------------------------------------------------- 1 | /** \file description.h 2 | * \ingroup Misc 3 | */ 4 | #pragma once 5 | 6 | #include 7 | 8 | extern bool file_descr_init (void); 9 | extern void file_descr_exit (void); 10 | extern const char *file_descr_get (const char *file_dir); 11 | 12 | -------------------------------------------------------------------------------- /src/dirlist.h: -------------------------------------------------------------------------------- 1 | /** \file dirlist.h 2 | * \ingroup Misc 3 | */ 4 | #ifndef _DIRLIST_H 5 | #define _DIRLIST_H 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | enum od2x_sorting { 12 | OD2X_UNSORTED, 13 | OD2X_ON_NAME, 14 | OD2X_FILES_FIRST, 15 | OD2X_DIRECTORIES_FIRST, 16 | OD2X_SORT_EXACT = 0x400, 17 | OD2X_SORT_REVERSE = 0x800 18 | }; 19 | 20 | enum od2x_attribs { 21 | OD2X_ATTR_READONLY, 22 | OD2X_ATTR_HIDDEN, 23 | OD2X_ATTR_SYSTEM, 24 | OD2X_ATTR_OFFLINE, 25 | OD2X_ATTR_TEMPORARY, 26 | OD2X_ATTR_ENCRYPTED, 27 | OD2X_ATTR_COMPRESSED, 28 | OD2X_ATTR_REVERSE = 0x800 /* Reverse the meaning of >=1 of the above */ 29 | }; 30 | 31 | struct od2x_options { 32 | const char *pattern; 33 | enum od2x_sorting sort; 34 | enum od2x_attribs attribs; /* todo */ 35 | int recursive; 36 | int unixy_paths; 37 | }; 38 | 39 | struct dirent2 { 40 | ino_t d_ino; /* a bit of a farce */ 41 | size_t d_reclen; /* more farce */ 42 | size_t d_namlen; /* length of d_name */ 43 | char *d_name; /* MALLOC()'ed fully qualified file-name */ 44 | char *d_link; /* MALLOC()'ed name of Repare-Point (Junction target) */ 45 | int d_special_link; /* the above d_link is a "Windows System for Linux" or an AppX link */ 46 | DWORD d_attrib; /* FILE_ATTRIBUTE_xx. Ref MSDN. */ 47 | FILETIME d_time_create; 48 | FILETIME d_time_access; /* always midnight local time */ 49 | FILETIME d_time_write; 50 | DWORD64 d_fsize; 51 | }; 52 | 53 | typedef struct dirdesc2 { 54 | size_t dd_loc; /* index into below dd_contents[] */ 55 | size_t dd_num; /* max # of entries in dd_contents[] */ 56 | struct dirent2 *dd_contents; /* pointer to contents of dir */ 57 | } DIR2; 58 | 59 | extern DIR2 *opendir2 (const char *dir); 60 | extern DIR2 *opendir2x (const char *dir, const struct od2x_options *_opts); 61 | extern struct dirent2 *readdir2 (DIR2 *dp); 62 | extern void seekdir2 (DIR2 *dp, long ofs); 63 | extern long telldir2 (DIR2 *dp); 64 | extern void rewinddir2 (DIR2 *dp); 65 | extern void closedir2 (DIR2 *dp); 66 | 67 | /* 68 | * Comparison routines for scandir2() and opendir2x(): 69 | * If 'od2x_options::sort == OD2X_ON_NAME' -> use 'sd_compare_alphasort()'. 70 | * If 'od2x_options::sort == OD2X_FILES_FIRST' -> use 'sd_compare_files_first()'. 71 | * If 'od2x_options::sort == OD2X_DIRECTORIES_FIRST' -> use 'sd_compare_dirs_first()'. 72 | */ 73 | extern int MS_CDECL sd_compare_alphasort (const void **a, const void **b); 74 | extern int MS_CDECL sd_compare_files_first (const void **a, const void **b); 75 | extern int MS_CDECL sd_compare_dirs_first (const void **a, const void **b); 76 | 77 | typedef int (MS_CDECL *QsortCmpFunc) (const void *, const void *); 78 | typedef int (MS_CDECL *ScandirCmpFunc) (const void **, const void **); 79 | typedef int (MS_CDECL *ScandirSelectFunc) (const struct dirent2 *); 80 | 81 | /* 82 | * arg1 = directory name 83 | * arg2 = unallocated array of pointers to dirent(direct) strctures 84 | * arg3 = specifier for which objects to pick (pointer to function) 85 | * arg4 = sorting function pointer to pass to qsort 86 | * 87 | * Returns number of files added to namelist[]. 88 | * Or -1 on error. 89 | */ 90 | extern int scandir2 (const char *dirname, 91 | struct dirent2 ***namelist, 92 | ScandirSelectFunc Select, 93 | ScandirCmpFunc compare); 94 | 95 | #endif /* _DIRLIST_H */ 96 | -------------------------------------------------------------------------------- /src/du.c: -------------------------------------------------------------------------------- 1 | /* 2 | * du - A simple "Disk Usage" program that 3 | * spawns `dirlist.exe` and calls `scandir2()`. 4 | */ 5 | #include 6 | #include "envtool.h" 7 | 8 | /** 9 | * A long path must be passed quoted to the 10 | * `dirlist.exe` sub-program. 11 | */ 12 | static char *check_long_name (const char *arg) 13 | { 14 | if (strchr(arg, ' ')) 15 | { 16 | char dir [_MAX_PATH]; 17 | 18 | snprintf (dir, sizeof(dir), "\"%s\"", arg); 19 | return strdup (dir); 20 | } 21 | return strdup (arg); 22 | } 23 | 24 | static void free_args (char **args) 25 | { 26 | int i; 27 | for (i = 0; args[i]; i++) 28 | free (args[i]); 29 | } 30 | 31 | int main (int argc, char **argv) 32 | { 33 | char my_dir [_MAX_PATH] = "?"; 34 | char dirlist [_MAX_PATH + 100]; 35 | char *end, *args [5]; 36 | int i, j, rc; 37 | int debug = (getenv("DU_DEBUG") != NULL); 38 | 39 | GetModuleFileName (NULL, my_dir, sizeof(my_dir)); 40 | end = strrchr (my_dir, '\\'); 41 | if (end) 42 | *end = '\0'; 43 | 44 | snprintf (dirlist, sizeof(dirlist), "%s\\dirlist.exe", my_dir); 45 | if (access(dirlist, 0)) 46 | { 47 | fprintf (stderr, "The program `%s` was not found.\n", dirlist); 48 | return (1); 49 | } 50 | 51 | memset (&args, '\0', sizeof(args)); 52 | args [0] = "--disk-usage"; 53 | 54 | i = 1; 55 | for (j = 0; j < argc; i++, j++) 56 | { 57 | if (i == DIM(args)-1) 58 | { 59 | fprintf (stderr, "Too many args. Max: %u.\n", DIM(args)-1); 60 | break; 61 | } 62 | args[i] = check_long_name (argv[j]); 63 | if (debug) 64 | fprintf (stderr, "args[i]: '%s'.\n", args[i]); 65 | } 66 | rc = _spawnvp (_P_WAIT, dirlist, (char const *const *) args); 67 | free_args (args + 1); 68 | return (rc); 69 | } 70 | 71 | -------------------------------------------------------------------------------- /src/envtool.rc: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "envtool.h" 4 | 5 | LANGUAGE 0x09,0x01 6 | 7 | #define RC_VERSION MAJOR_VER, MINOR_VER, 0, 0 8 | 9 | #if defined(DEBUG) || defined(_DEBUG) 10 | #define RC_FILEFLAGS 1 11 | #else 12 | #define RC_FILEFLAGS 0 13 | #endif 14 | 15 | VS_VERSION_INFO VERSIONINFO 16 | FILEVERSION RC_VERSION 17 | PRODUCTVERSION RC_VERSION 18 | FILEFLAGSMASK 0x3fL 19 | FILEFLAGS RC_FILEFLAGS 20 | FILEOS VOS_NT_WINDOWS32 21 | FILETYPE VFT_APP 22 | FILESUBTYPE 0x0L 23 | 24 | BEGIN 25 | BLOCK "StringFileInfo" 26 | BEGIN 27 | BLOCK "040904b0" 28 | BEGIN 29 | VALUE "CompanyName", "Gisle Vanem." 30 | VALUE "FileDescription", "EnvTool utility." 31 | VALUE "FileVersion", VER_STRING 32 | VALUE "InternalName", "envtool.exe (" BUILDER ", " WIN_VERSTR ")" 33 | VALUE "OriginalFilename", "envtool.exe (" BUILDER ", " WIN_VERSTR ")" 34 | VALUE "ProductName", "Environment variables and Python 'sys.path[]' search tool." 35 | VALUE "ProductVersion", VER_STRING "\0" 36 | VALUE "LegalCopyright", "A freeware program by " AUTHOR_STR ", 2012-2025." 37 | END 38 | END 39 | 40 | BLOCK "VarFileInfo" 41 | BEGIN 42 | VALUE "Translation", 0x409, 1200 43 | END 44 | END 45 | -------------------------------------------------------------------------------- /src/envtool.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.2.32630.192 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "envtool", "envtool.vcxproj", "{47A4EE11-5847-41F7-B00C-1627C75FAC12}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Mixed Platforms = Debug|Mixed Platforms 11 | Debug|Win32 = Debug|Win32 12 | Debug|x86 = Debug|x86 13 | Release|Mixed Platforms = Release|Mixed Platforms 14 | Release|Win32 = Release|Win32 15 | Release|x86 = Release|x86 16 | EndGlobalSection 17 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 18 | {47A4EE11-5847-41F7-B00C-1627C75FAC12}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32 19 | {47A4EE11-5847-41F7-B00C-1627C75FAC12}.Debug|Mixed Platforms.Build.0 = Debug|Win32 20 | {47A4EE11-5847-41F7-B00C-1627C75FAC12}.Debug|Win32.ActiveCfg = Debug|Win32 21 | {47A4EE11-5847-41F7-B00C-1627C75FAC12}.Debug|Win32.Build.0 = Debug|Win32 22 | {47A4EE11-5847-41F7-B00C-1627C75FAC12}.Debug|x86.ActiveCfg = Debug|Win32 23 | {47A4EE11-5847-41F7-B00C-1627C75FAC12}.Release|Mixed Platforms.ActiveCfg = Release|Win32 24 | {47A4EE11-5847-41F7-B00C-1627C75FAC12}.Release|Mixed Platforms.Build.0 = Release|Win32 25 | {47A4EE11-5847-41F7-B00C-1627C75FAC12}.Release|Win32.ActiveCfg = Release|Win32 26 | {47A4EE11-5847-41F7-B00C-1627C75FAC12}.Release|Win32.Build.0 = Release|Win32 27 | {47A4EE11-5847-41F7-B00C-1627C75FAC12}.Release|x86.ActiveCfg = Release|Win32 28 | EndGlobalSection 29 | GlobalSection(SolutionProperties) = preSolution 30 | HideSolutionNode = FALSE 31 | EndGlobalSection 32 | GlobalSection(ExtensibilityGlobals) = postSolution 33 | SolutionGuid = {C802B0CE-FE7D-4E05-BD0E-DA5BDE6C2935} 34 | EndGlobalSection 35 | EndGlobal 36 | -------------------------------------------------------------------------------- /src/envtool.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | ClCompile 5 | 6 | 7 | 8 | 9 | echo const char *cflags = "cl -nologo -c -MT -Zi -Zo -W3 -WX- -O2 -Oi -Oy- -GL -DEVERYTHINGUSERAPI= -DEVERYTHINGAPI=__cdecl -DUSE_SQLITE3 -D_WIN32_WINNT=0x0602 -D_CRT_SECURE_NO_WARNINGS -D_CRT_NONSTDC_NO_DEPRECATE -DWIN32_LEAN_AND_MEAN -D_WIN32_IE=0x500 -Gm- -EHsc -GS -Gy -fp:precise -Zc:wchar_t -Zc:forScope"; > cflags_cl.h 10 | echo const char *ldflags = "link -nologo -errorreport:none -out:envtool.exe -incremental:no -subsystem:console -opt:ref -opt:icf -ltcg -tlbid:1 -release -dynamicbase -nxcompat -manifest:embed -debug -map:envtool.map -machine:x86 -safeseh version.lib advapi32.lib imagehlp.lib wintrust.lib psapi.lib crypt32.lib shlwapi.lib kernel32.lib user32.lib winspool.lib shell32.lib ole32.lib oleaut32.lib ws2_32.lib Release/auth.obj Release/envtool.obj Release/envtool_py.obj Release/description.obj Release/find_vstudio.obj Release/cache.obj Release/cfg_file.obj Release/cmake.obj Release/color.obj Release/compiler.obj Release/Everything.obj Release/Everything_ETP.obj Release/dirlist.obj Release/get_file_assoc.obj Release/getopt_long.obj Release/ignore.obj Release/json.obj Release/lua.obj Release/misc.obj Release/pkg-config.obj Release/report.obj Release/searchpath.obj Release/show_ver.obj Release/smartlist.obj Release/sort.obj Release/tests.obj Release/vcpkg.obj Release/win_sqlite3.obj Release/win_trust.obj Release/win_ver.obj Release/envtool.res"; > ldflags_cl.h 11 | 12 | envtool.exe 13 | 14 | 15 | 16 | 17 | Debug 18 | Win32 19 | 20 | 21 | Release 22 | Win32 23 | 24 | 25 | 26 | {47A4EE11-5847-41F7-B00C-1627C75FAC12} 27 | Win32Proj 28 | envtool 29 | $(WK_VER) 30 | 31 | 32 | 33 | Application 34 | true 35 | MultiByte 36 | $(PlatformToolset) 37 | 38 | 39 | Application 40 | false 41 | true 42 | MultiByte 43 | $(PlatformToolset) 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | false 57 | .\ 58 | envtool 59 | .exe 60 | 61 | 62 | false 63 | .\ 64 | envtool 65 | .exe 66 | 67 | 68 | 69 | NotUsing 70 | Level3 71 | Disabled 72 | EVERYTHINGUSERAPI=;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 73 | 74 | 75 | /I. /D_LIBC /D_CRT_SECURE_NO_WARNINGS /D_CRT_NONSTDC_NO_DEPRECATE /DWIN32_LEAN_AND_MEAN /D_WIN32_WINNT=0x0602 /D_WIN32_IE=0x500 /DUSE_SQLITE3 %(AdditionalOptions) 76 | MultiThreadedDebug 77 | 78 | 79 | Console 80 | true 81 | envtool.exe 82 | NotSet 83 | 84 | 85 | -libpath:"$(WindowsSdkDir)/lib/$(WindowsSdkVer)/um/x86" -libpath:"$(WindowsSdkDir)/lib/$(WindowsSdkVer)/ucrt/x86" -libpath:"$(VCToolkitInstallDir)/lib/x86" 86 | version.lib;advapi32.lib;imagehlp.lib;wintrust.lib;psapi.lib;crypt32.lib;shlwapi.lib;ws2_32.lib;ole32.lib;oleaut32.lib;%(AdditionalDependencies) 87 | true 88 | NoErrorReport 89 | true 90 | $(TargetName).map 91 | 92 | 93 | true 94 | 95 | 96 | 97 | 98 | Level3 99 | NotUsing 100 | MaxSpeed 101 | true 102 | true 103 | EVERYTHINGUSERAPI=;_CONSOLE;%(PreprocessorDefinitions) 104 | 105 | 106 | /I. /D_LIBC /D_CRT_SECURE_NO_WARNINGS /D_CRT_NONSTDC_NO_DEPRECATE /DWIN32_LEAN_AND_MEAN /D_WIN32_WINNT=0x0602 /D_WIN32_IE=0x500 /DUSE_SQLITE3 %(AdditionalOptions) 107 | MultiThreaded 108 | 109 | 110 | Console 111 | true 112 | true 113 | true 114 | envtool.exe 115 | NotSet 116 | 117 | 118 | -libpath:"$(WindowsSdkDir)/lib/$(WindowsSdkVer)/um/x86" -libpath:"$(WindowsSdkDir)/lib/$(WindowsSdkVer)/ucrt/x86" -libpath:"$(VCToolkitInstallDir)/lib/x86" 119 | version.lib;advapi32.lib;imagehlp.lib;wintrust.lib;psapi.lib;crypt32.lib;shlwapi.lib;ws2_32.lib;ole32.lib;oleaut32.lib;%(AdditionalDependencies) 120 | true 121 | NoErrorReport 122 | true 123 | $(TargetName).map 124 | 125 | 126 | true 127 | 128 | 129 | 130 | 131 | -D_MSC_VER -D_RELEASE %(AdditionalOptions) 132 | -D_MSC_VER -D_DEBUG %(AdditionalOptions) 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | -------------------------------------------------------------------------------- /src/envtool_py.h: -------------------------------------------------------------------------------- 1 | /** \file envtool_py.h 2 | * \ingroup Envtool_PY 3 | */ 4 | #pragma once 5 | 6 | /** 7 | * \typedef enum python_variants 8 | * The types of Python we support. 9 | */ 10 | typedef enum python_variants { 11 | UNKNOWN_PYTHON, /**< we have not found any suitable Python yet. */ 12 | DEFAULT_PYTHON, /**< a suitable Python found first on `PATH`. */ 13 | PY2_PYTHON, /**< a version 2.x Python was found and selected. */ 14 | PY3_PYTHON, /**< a version 3.x Python was found and selected. */ 15 | PYPY_PYTHON, /**< a PyPy was found and selected (any version). */ 16 | ALL_PYTHONS /**< any Python found. */ 17 | } python_variants; 18 | 19 | extern enum python_variants py_which; 20 | 21 | struct ver_info; /* Forward; in envtool.h */ 22 | 23 | extern void py_init (void); 24 | extern void py_exit (void); 25 | extern int py_search (void); 26 | extern void py_searchpaths (void); 27 | extern int py_test (void); 28 | extern bool py_get_info (char **exe, struct ver_info *ver); 29 | extern const char **py_get_variants (void); 30 | extern const char *py_variant_name (enum python_variants v); 31 | extern int py_variant_value (const char *short_name, const char *full_name); 32 | extern char *py_execfile (const char **py_argv, bool capture, bool as_import); 33 | extern bool py_cfg_handler (const char *section, const char *key, const char *value); 34 | 35 | -------------------------------------------------------------------------------- /src/foo/win_glob.c: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2001 DJ Delorie, see COPYING.DJ for details */ 2 | /* Copyright (C) 1999 DJ Delorie, see COPYING.DJ for details */ 3 | /* Copyright (C) 1998 DJ Delorie, see COPYING.DJ for details */ 4 | /* Copyright (C) 1997 DJ Delorie, see COPYING.DJ for details */ 5 | /* Copyright (C) 1996 DJ Delorie, see COPYING.DJ for details */ 6 | /* Copyright (C) 1995 DJ Delorie, see COPYING.DJ for details */ 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include "envtool.h" 19 | #include "win_glob.h" 20 | 21 | #define PATHBUF_LEN 2000 22 | #define EOS '\0' 23 | 24 | struct ffblk { 25 | HANDLE ff_handle; 26 | DWORD ff_attrib; /* Attributes, see constants above. */ 27 | FILETIME ff_time_create; 28 | FILETIME ff_time_access; /* always midnight local time */ 29 | FILETIME ff_time_write; 30 | uint64_t ff_fsize; 31 | char ff_name [_MAX_PATH]; 32 | }; 33 | 34 | static int findfirst (const char *file_spec, struct ffblk *ffblk, int attrib) 35 | { 36 | WIN32_FIND_DATA ff_data; 37 | HANDLE handle = FindFirstFile (file_spec, &ff_data); 38 | 39 | if (handle == INVALID_HANDLE_VALUE) 40 | { 41 | DEBUGF (("\"%s\" not found.\n", file_spec)); 42 | return (-1); 43 | } 44 | 45 | ffblk->ff_handle = handle; 46 | ffblk->ff_attrib = ff_data.dwFileAttributes; 47 | ffblk->ff_time_create = ff_data.ftCreationTime; 48 | ffblk->ff_time_access = ff_data.ftLastAccessTime; 49 | ffblk->ff_time_write = ff_data.ftLastWriteTime; 50 | ffblk->ff_fsize = ((uint64_t)ff_data.nFileSizeHigh << 32) + ff_data.nFileSizeLow; 51 | 52 | strncpy (ffblk->ff_name, ff_data.cFileName, sizeof(ffblk->ff_name)); 53 | ARGSUSED (attrib); 54 | return (0); 55 | } 56 | 57 | static int findnext (struct ffblk *ffblk) 58 | { 59 | WIN32_FIND_DATA ff_data; 60 | DWORD rc = FindNextFile (ffblk->ff_handle, &ff_data); 61 | 62 | if (!rc) 63 | { 64 | FindClose (ffblk->ff_handle); 65 | ffblk->ff_handle = NULL; 66 | } 67 | else 68 | { 69 | ffblk->ff_attrib = ff_data.dwFileAttributes; 70 | strncpy (ffblk->ff_name, ff_data.cFileName, sizeof(ffblk->ff_name)); 71 | } 72 | return (rc ? 0 : 1); 73 | } 74 | 75 | typedef struct Save { 76 | struct Save *prev; 77 | char *entry; 78 | } Save; 79 | 80 | static Save *save_list; 81 | static int save_count; 82 | static int flags; 83 | static int (*errfunc)(const char *epath, int eerno); 84 | static char *pathbuf, *pathbuf_end; 85 | static int wildcard_nesting; 86 | static char use_lfn = 1; 87 | static char slash; 88 | 89 | static int glob2 (const char *pattern, char *epathbuf); 90 | static int add (const char *path, unsigned line); 91 | static int str_compare (const void *va, const void *vb); 92 | 93 | /* `tolower' might depend on the locale. We don't want to. 94 | */ 95 | static int msdos_tolower_fname (int c) 96 | { 97 | return (c >= 'A' && c <= 'Z') ? c + 'a' - 'A' : c; 98 | } 99 | 100 | static int add (const char *path, unsigned line) 101 | { 102 | Save *sp; 103 | 104 | for (sp = save_list; sp; sp = sp->prev) 105 | if (!stricmp(sp->entry, path)) 106 | return (0); 107 | 108 | sp = CALLOC (sizeof(*sp), 1); 109 | if (!sp) 110 | return (1); 111 | 112 | sp->entry = STRDUP (path); 113 | if (!sp->entry) 114 | { 115 | FREE (sp); 116 | return (1); 117 | } 118 | DEBUGF (("add: `%s' (from line %u)\n", sp->entry, line)); 119 | 120 | sp->prev = save_list; 121 | save_list = sp; 122 | save_count++; 123 | return (0); 124 | } 125 | 126 | static int glob_dirs (const char *rest, char *epathbuf, 127 | int first) /* rest is ptr to null or ptr after slash, bp after slash */ 128 | { 129 | struct ffblk ff; 130 | int done; 131 | 132 | DEBUGF (("glob_dirs[%d]: rest=`%s' %c epathbuf=`%s' %c pathbuf=`%s'\n", 133 | wildcard_nesting, rest, *rest, epathbuf, *epathbuf, pathbuf)); 134 | 135 | if (first) 136 | { 137 | if (*rest) 138 | { 139 | if (glob2(rest, epathbuf) == GLOB_NOSPACE) 140 | return (GLOB_NOSPACE); 141 | } 142 | else 143 | { 144 | char sl = epathbuf[-1]; 145 | 146 | *epathbuf = '\0'; 147 | DEBUGF (("end, checking `%s'\n", pathbuf)); 148 | if (epathbuf == pathbuf) 149 | { 150 | epathbuf[0] = '.'; 151 | epathbuf[1] = '\0'; 152 | } 153 | else 154 | epathbuf[-1] = '\0'; 155 | 156 | if (FILE_EXISTS(pathbuf)) 157 | if (add(pathbuf,__LINE__)) 158 | return (GLOB_NOSPACE); 159 | epathbuf[-1] = sl; 160 | } 161 | } 162 | 163 | strcpy (epathbuf, "*.*"); 164 | done = findfirst (pathbuf, &ff, FILE_ATTRIBUTE_DIRECTORY); 165 | 166 | while (!done) 167 | { 168 | if ((ff.ff_attrib & FILE_ATTRIBUTE_DIRECTORY) && 169 | (strcmp(ff.ff_name, ".") && strcmp(ff.ff_name, ".."))) 170 | { 171 | char *tp; 172 | 173 | DEBUGF (("found `%s' `%s'\n", pathbuf, ff.ff_name)); 174 | 175 | strcpy (epathbuf, ff.ff_name); 176 | tp = strchr (epathbuf, '\0'); 177 | *tp++ = slash; 178 | *tp = '\0'; 179 | 180 | wildcard_nesting++; 181 | if (*rest) 182 | { 183 | if (glob2(rest, tp) == GLOB_NOSPACE) 184 | return (GLOB_NOSPACE); 185 | } 186 | else 187 | { 188 | if (!(flags & GLOB_MARK)) 189 | tp[-1] = 0; 190 | if (add(pathbuf,__LINE__)) 191 | return (GLOB_NOSPACE); 192 | tp[-1] = slash; 193 | } 194 | *tp = 0; 195 | if (glob_dirs(rest, tp, 0) == GLOB_NOSPACE) 196 | return (GLOB_NOSPACE); 197 | wildcard_nesting--; 198 | } 199 | done = findnext(&ff); 200 | } 201 | return (0); 202 | } 203 | 204 | static int glob2 (const char *pattern, char *epathbuf) /* both point *after* the slash */ 205 | { 206 | struct ffblk ff; 207 | const char *pp, *pslash; 208 | char *bp, *my_pattern; 209 | int done, attr; 210 | 211 | if (strcmp(pattern, "...") == 0) 212 | return glob_dirs (pattern+3, epathbuf, 1); 213 | 214 | if (strncmp(pattern, "...", 3) == 0 && (pattern[3] == '\\' || pattern[3] == '/')) 215 | { 216 | slash = pattern[3]; 217 | return glob_dirs (pattern+4, epathbuf, 1); 218 | } 219 | 220 | *epathbuf = '\0'; 221 | 222 | /* copy as many non-wildcard segments as possible */ 223 | pp = pattern; 224 | bp = epathbuf; 225 | pslash = bp - 1; 226 | 227 | while (bp < pathbuf_end) 228 | { 229 | if (*pp == ':' || *pp == '\\' || *pp == '/') 230 | { 231 | pslash = bp; 232 | if (strcmp(pp+1, "...") == 0 || 233 | (strncmp(pp+1, "...", 3) == 0 && (pp[4] == '/' || pp[4] == '\\'))) 234 | { 235 | if (*pp != ':') 236 | slash = *pp; 237 | DEBUGF (("glob2: dots at `%s'\n", pp)); 238 | *bp++ = *pp++; 239 | break; 240 | } 241 | } 242 | else if (*pp == '*' || *pp == '?' || *pp == '[') 243 | { 244 | if (pslash > pathbuf) 245 | strncpy (epathbuf, pattern, pslash - pathbuf); 246 | pp = pattern + (pslash - epathbuf) + 1; 247 | bp = epathbuf + (pslash - epathbuf) + 1; 248 | break; 249 | } 250 | else if (*pp == 0) 251 | break; 252 | 253 | *bp++ = *pp++; 254 | } 255 | *bp = 0; 256 | 257 | /* A pattern this big won't match any file. */ 258 | if (bp >= pathbuf_end && *pp) 259 | return (0); 260 | 261 | DEBUGF (("glob2: pp: `%s'\n", pp)); 262 | 263 | if (*pp == 0) /* end of pattern? */ 264 | { 265 | if (FILE_EXISTS(pathbuf)) 266 | { 267 | if (flags & GLOB_MARK) 268 | { 269 | struct ffblk _ff; 270 | 271 | attr = FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_SYSTEM | 272 | FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_ARCHIVE; 273 | 274 | findfirst (pathbuf, &_ff, attr); 275 | if (_ff.ff_attrib & FILE_ATTRIBUTE_DIRECTORY) 276 | { 277 | char *_pathbuf = pathbuf + strlen(pathbuf); 278 | *_pathbuf++ = '/'; 279 | *_pathbuf = 0; 280 | } 281 | } 282 | if (add(pathbuf,__LINE__)) 283 | return (GLOB_NOSPACE); 284 | } 285 | return (0); 286 | } 287 | 288 | DEBUGF (("glob2(): initial segment is `%s', wildcard_nesting: %d\n", 289 | pathbuf, wildcard_nesting)); 290 | 291 | if (wildcard_nesting) 292 | { 293 | char s = bp[-1]; 294 | 295 | bp[-1] = 0; 296 | if (!FILE_EXISTS(pathbuf)) 297 | return (0); 298 | bp[-1] = s; 299 | } 300 | 301 | for (pslash = pp; *pslash && *pslash != '\\' && *pslash != '/'; pslash++) 302 | ; 303 | 304 | if (*pslash) 305 | slash = *pslash; 306 | 307 | my_pattern = alloca (pslash - pp + 1); 308 | strncpy (my_pattern, pp, pslash - pp); 309 | my_pattern [pslash-pp] = '\0'; 310 | 311 | DEBUGF (("glob2: `%s' `%s'\n", pathbuf, my_pattern)); 312 | 313 | if (strcmp(my_pattern, "...") == 0) 314 | { 315 | if (glob_dirs(*pslash ? pslash+1 : pslash, bp, 1) == GLOB_NOSPACE) 316 | return (GLOB_NOSPACE); 317 | return (0); 318 | } 319 | 320 | strcpy (bp, "*.*"); 321 | 322 | attr = FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_SYSTEM | 323 | FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_ARCHIVE; 324 | done = findfirst (pathbuf, &ff, attr); 325 | 326 | while (!done) 327 | { 328 | if ((ff.ff_attrib & FILE_ATTRIBUTE_DIRECTORY) == 0 || 329 | (strcmp(ff.ff_name, ".") && strcmp(ff.ff_name, ".."))) 330 | { 331 | if (fnmatch(my_pattern, ff.ff_name, 332 | FNM_FLAG_NOESCAPE | FNM_FLAG_PATHNAME | 333 | FNM_FLAG_NOCASE) == FNM_MATCH) 334 | { 335 | strcpy (bp, ff.ff_name); 336 | if (*pslash) 337 | { 338 | char *tp = bp + strlen(bp); 339 | 340 | *tp++ = *pslash; 341 | *tp = 0; 342 | DEBUGF (("nest: `%s' `%s'\n", pslash+1, pathbuf)); 343 | wildcard_nesting++; 344 | if (glob2(pslash+1, tp) == GLOB_NOSPACE) 345 | return (GLOB_NOSPACE); 346 | wildcard_nesting--; 347 | } 348 | else 349 | { 350 | DEBUGF (("ffmatch: `%s' matching `%s', add `%s'\n", 351 | ff.ff_name, my_pattern, pathbuf)); 352 | if ((ff.ff_attrib & FILE_ATTRIBUTE_DIRECTORY) && (flags & GLOB_MARK)) 353 | { 354 | size_t len = strlen (bp); 355 | bp [len+1] = '\0'; 356 | bp [len] = slash; 357 | } 358 | if (add(pathbuf,__LINE__)) 359 | return (GLOB_NOSPACE); 360 | } 361 | } 362 | } 363 | done = findnext (&ff); 364 | } 365 | return (0); 366 | } 367 | 368 | static int str_compare (const void *va, const void *vb) 369 | { 370 | return stricmp (*(const char * const *)va, *(const char * const *)vb); 371 | } 372 | 373 | int glob (const char *_pattern, int _flags, 374 | int (*_errfunc)(const char *_epath, int _eerrno), 375 | glob_t *_pglob) 376 | { 377 | char path_buffer[PATHBUF_LEN + 1]; 378 | int l_ofs, l_ptr; 379 | 380 | pathbuf = path_buffer + 1; 381 | pathbuf_end = path_buffer + PATHBUF_LEN; 382 | flags = _flags; 383 | errfunc = _errfunc; 384 | wildcard_nesting = 0; 385 | save_count = 0; 386 | save_list = 0; 387 | use_lfn = 1; 388 | slash = '/'; 389 | 390 | if (!(_flags & GLOB_APPEND)) 391 | { 392 | _pglob->gl_pathc = 0; 393 | _pglob->gl_pathv = NULL; 394 | if (!(flags & GLOB_DOOFFS)) 395 | _pglob->gl_offs = 0; 396 | } 397 | 398 | if (glob2(_pattern, pathbuf) == GLOB_NOSPACE) 399 | return (GLOB_NOSPACE); 400 | 401 | if (save_count == 0) 402 | { 403 | if (flags & GLOB_NOCHECK) 404 | { 405 | if (add(_pattern,__LINE__)) 406 | return (GLOB_NOSPACE); 407 | } 408 | else 409 | return (GLOB_NOMATCH); 410 | } 411 | 412 | if (flags & GLOB_DOOFFS) 413 | l_ofs = _pglob->gl_offs; 414 | else l_ofs = 0; 415 | 416 | if (flags & GLOB_APPEND) 417 | { 418 | _pglob->gl_pathv = REALLOC (_pglob->gl_pathv, (l_ofs + _pglob->gl_pathc + save_count + 1) * sizeof(char *)); 419 | if (!_pglob->gl_pathv) 420 | return (GLOB_NOSPACE); 421 | l_ptr = l_ofs + _pglob->gl_pathc; 422 | } 423 | else 424 | { 425 | _pglob->gl_pathv = CALLOC ((l_ofs + save_count + 1), sizeof(char *)); 426 | if (!_pglob->gl_pathv) 427 | return (GLOB_NOSPACE); 428 | l_ptr = l_ofs; 429 | } 430 | 431 | l_ptr += save_count; 432 | _pglob->gl_pathv[l_ptr] = NULL; 433 | 434 | while (save_list) 435 | { 436 | Save *s = save_list; 437 | 438 | l_ptr --; 439 | _pglob->gl_pathv[l_ptr] = save_list->entry; 440 | save_list = save_list->prev; 441 | FREE (s); 442 | } 443 | 444 | if (!(flags & GLOB_NOSORT)) 445 | qsort (_pglob->gl_pathv + l_ptr, save_count, sizeof(char *), str_compare); 446 | 447 | _pglob->gl_pathc = l_ptr + save_count; 448 | 449 | return (0); 450 | } 451 | 452 | void globfree (glob_t *_pglob) 453 | { 454 | size_t i; 455 | 456 | if (!_pglob->gl_pathv) 457 | return; 458 | 459 | for (i = 0; i < _pglob->gl_pathc; i++) 460 | FREE (_pglob->gl_pathv[i]); 461 | FREE (_pglob->gl_pathv); 462 | } 463 | 464 | #if defined(TEST) 465 | 466 | int debug = 0; 467 | int show_unix_paths = 0; 468 | int color = 0; 469 | 470 | /* Tell MingW's CRT to turn off command line globbing by default. 471 | */ 472 | int _CRT_glob = 0; 473 | 474 | /* MinGW-64's CRT seems to NOT glob the cmd-line by default. 475 | * Hence this doesn't change that behaviour. 476 | */ 477 | int _dowildcard = 0; 478 | 479 | char *getenv_expand (const char *var) 480 | { 481 | char *e = getenv (var); 482 | 483 | if (e) 484 | return STRDUP (e); 485 | return (NULL); 486 | } 487 | 488 | void usage (void) 489 | { 490 | printf ("Usage: win_glob [-d] \n"); 491 | exit (-1); 492 | } 493 | 494 | int main (int argc, char **argv) 495 | { 496 | glob_t res; 497 | size_t cnt; 498 | int rc; 499 | char **p; 500 | 501 | if (argc < 2) 502 | usage(); 503 | 504 | if (!strcmp(argv[1],"-d")) 505 | debug++, argv++; 506 | 507 | rc = glob (argv[1], GLOB_NOSORT | GLOB_MARK, NULL, &res); 508 | if (rc != 0) 509 | printf ("glob() failed: %d\n", rc); 510 | else 511 | for (p = res.gl_pathv, cnt = 1; cnt <= res.gl_pathc; p++, cnt++) 512 | printf ("%d: %s\n", cnt, *p); 513 | 514 | globfree (&res); 515 | return (0); 516 | } 517 | #endif 518 | -------------------------------------------------------------------------------- /src/get_file_assoc.c: -------------------------------------------------------------------------------- 1 | /**\file get_file_assoc.c 2 | * \ingroup Misc 3 | * 4 | * \brief 5 | * Gets the **File Associations** (`ASSOCSTR_EXECUTABLE`) for a file extension 6 | * using `AssocQueryStringA()`:
7 | * https://docs.microsoft.com/en-us/windows/desktop/api/shlwapi/nf-shlwapi-assocquerystringa 8 | */ 9 | #if !defined(_WIN32_WINNT) 10 | #undef _WIN32_WINNT 11 | #define _WIN32_WINNT 0x0A00 /* Windows-10 */ 12 | #endif 13 | 14 | #if !defined(_WIN32_IE) 15 | #undef _WIN32_IE 16 | #define _WIN32_IE 0x800 /* _WIN32_IE_IE80 */ 17 | #endif 18 | 19 | #include 20 | #include 21 | 22 | #include "envtool.h" 23 | #include "color.h" 24 | #include "get_file_assoc.h" 25 | 26 | #define ADD_VALUE(v) { (unsigned)(v), #v } 27 | 28 | static const search_list assoc_values[] = { 29 | ADD_VALUE (ASSOCSTR_COMMAND), 30 | ADD_VALUE (ASSOCSTR_EXECUTABLE), 31 | ADD_VALUE (ASSOCSTR_FRIENDLYDOCNAME), 32 | ADD_VALUE (ASSOCSTR_FRIENDLYAPPNAME), 33 | ADD_VALUE (ASSOCSTR_NOOPEN), 34 | ADD_VALUE (ASSOCSTR_SHELLNEWVALUE), 35 | ADD_VALUE (ASSOCSTR_DDECOMMAND), 36 | ADD_VALUE (ASSOCSTR_DDEIFEXEC), 37 | ADD_VALUE (ASSOCSTR_DDEAPPLICATION), 38 | ADD_VALUE (ASSOCSTR_DDETOPIC), 39 | ADD_VALUE (ASSOCSTR_INFOTIP), 40 | #if defined(_WIN32_IE) && (_WIN32_IE >= _WIN32_IE_IE60) 41 | ADD_VALUE (ASSOCSTR_QUICKTIP), 42 | ADD_VALUE (ASSOCSTR_TILEINFO), 43 | ADD_VALUE (ASSOCSTR_CONTENTTYPE), 44 | ADD_VALUE (ASSOCSTR_DEFAULTICON), 45 | ADD_VALUE (ASSOCSTR_SHELLEXTENSION), 46 | #endif 47 | #if defined(_WIN32_IE) && (_WIN32_IE >= _WIN32_IE_IE80) 48 | ADD_VALUE (ASSOCSTR_DROPTARGET), 49 | ADD_VALUE (ASSOCSTR_DELEGATEEXECUTE), 50 | #endif 51 | #if defined(_MSC_VER) && (_WIN32_WINNT >= 0x0A00) 52 | ADD_VALUE (ASSOCSTR_PROGID), 53 | ADD_VALUE (ASSOCSTR_APPID), 54 | ADD_VALUE (ASSOCSTR_APPPUBLISHER), 55 | ADD_VALUE (ASSOCSTR_APPICONREFERENCE), 56 | #endif 57 | }; 58 | 59 | static char last_err [300]; 60 | 61 | /** 62 | * Return the error-string from the last failed function. 63 | */ 64 | const char *get_file_assoc_last_err (void) 65 | { 66 | return (last_err); 67 | } 68 | 69 | /** 70 | * Print all registered associations for an file-extension. 71 | */ 72 | bool get_file_assoc_all (const char *extension) 73 | { 74 | HRESULT hr; 75 | char buf [1024]; 76 | DWORD buf_len; 77 | int i, failed = 0; 78 | 79 | last_err[0] = '\0'; 80 | 81 | for (i = 0; i < DIM(assoc_values); i++) 82 | { 83 | printf (" %2d: %-26s: ", i, assoc_values[i].name); 84 | buf_len = sizeof(buf); 85 | 86 | hr = AssocQueryStringA (0, assoc_values[i].value, 87 | extension, NULL, buf, &buf_len); 88 | if (hr == S_OK) 89 | printf ("%s\n", buf); 90 | else 91 | { 92 | _strlcpy (last_err, win_strerror(hr), sizeof(last_err)); 93 | printf ("Failed: %s\n", last_err); 94 | failed++; 95 | } 96 | } 97 | if (failed == 0) 98 | last_err[0] = '\0'; 99 | return (failed > 0 ? false : true); 100 | } 101 | 102 | /** 103 | * Retrieves the registered friendly-name (`*program_descr`) and 104 | * executable program (`*program_exe`) associated for an file-extension. 105 | */ 106 | bool get_file_assoc (const char *extension, char **program_descr, char **program_exe) 107 | { 108 | char buf [1024]; 109 | DWORD buf_len = sizeof(buf); 110 | HRESULT hr; 111 | 112 | *program_descr = *program_exe = NULL; 113 | 114 | hr = AssocQueryStringA (0, ASSOCSTR_FRIENDLYDOCNAME, 115 | extension, NULL, buf, &buf_len); 116 | if (hr != S_OK) 117 | { 118 | _strlcpy (last_err, win_strerror(hr), sizeof(last_err)); 119 | TRACE (1, "Failed: %s\n", last_err); 120 | return (false); 121 | } 122 | *program_descr = STRDUP (buf); 123 | 124 | buf_len = sizeof(buf); 125 | hr = AssocQueryStringA (ASSOCF_INIT_IGNOREUNKNOWN, ASSOCSTR_EXECUTABLE, 126 | extension, NULL, buf, &buf_len); 127 | if (hr != S_OK) 128 | { 129 | _strlcpy (last_err, win_strerror(hr), sizeof(last_err)); 130 | TRACE (1, "Failed: %s\n", last_err); 131 | return (false); 132 | } 133 | 134 | last_err[0] = '\0'; 135 | *program_exe = STRDUP (buf); 136 | return (true); 137 | } 138 | 139 | /** 140 | * Get the actual casing for a full file-name by getting the short-name 141 | * and then the long-name. This function therefore first checks if the 142 | * `*file_p` is already on a short-name form (`is_sfn`). If true, a call 143 | * to `GetShortPathNameA()` is not done. 144 | * 145 | * Internally, these functions seems to be using `SHGetFileInfo()`:
146 | * https://docs.microsoft.com/en-us/windows/desktop/api/shellapi/nf-shellapi-shgetfileinfoa 147 | * 148 | * \param[in,out] file_p A pointer to the file to be converted.
149 | * The new proper file-name is returned at the same location.
150 | * 151 | * \param[in] allocated If true, the `*file_p` was allocated by STRDUP() and will 152 | * be FREE() before `*file_p` is set to the new value. 153 | * 154 | * \retval true if the `*file_p` was converted successfully. 155 | * \retval false if the convertion failed, `*file_p` is unchanged. 156 | * 157 | * \note 158 | * The drive-letter is down-cased using `_fix_drive()` since 159 | * `GetLongPathName()` does not touch that. 160 | * 161 | * \note 162 | * Written with the inspiration from:
163 | * http://stackoverflow.com/questions/74451/getting-actual-file-name-with-proper-casing-on-windows 164 | */ 165 | bool get_actual_filename (char **file_p, bool allocated) 166 | { 167 | char buf [_MAX_PATH], *_new, *file; 168 | bool is_sfn; 169 | 170 | is_sfn = (strchr(*file_p,'~') != NULL); 171 | 172 | if (!is_sfn && GetShortPathNameA(*file_p, buf, sizeof(buf)) == 0) 173 | { 174 | _strlcpy (last_err, win_strerror(GetLastError()), sizeof(last_err)); 175 | TRACE (1, "*file_p: '%s' failed: %s\n", *file_p, last_err); 176 | return (false); 177 | } 178 | 179 | _new = MALLOC (_MAX_PATH); 180 | if (!_new) 181 | return (false); 182 | 183 | file = is_sfn ? *file_p : buf; 184 | 185 | if (GetLongPathNameA(file, _new, _MAX_PATH) == 0) 186 | { 187 | _strlcpy (last_err, win_strerror(GetLastError()), sizeof(last_err)); 188 | TRACE (1, "file: '%s' failed: %s\n", file, last_err); 189 | FREE (_new); 190 | return (false); 191 | } 192 | 193 | if (allocated) 194 | FREE (*file_p); 195 | 196 | slashify2 (_new, _new, opt.show_unix_paths ? '/' : '\\'); 197 | *file_p = _new; 198 | last_err[0] = '\0'; 199 | 200 | TRACE (3, "\n short: '%s' ->\n long: '%s'\n", buf, *file_p); 201 | return (true); 202 | } 203 | 204 | 205 | #if defined(GET_FILE_ASSOC_TEST) 206 | prog_options opt; 207 | 208 | char *searchpath (const char *file, const char *env_var) 209 | { 210 | ARGSUSED (file); 211 | ARGSUSED (env_var); 212 | return (NULL); 213 | } 214 | 215 | static void usage (const char *I_am) 216 | { 217 | printf ("Usage: %s [-d] [.file-extension | *]\n", I_am); 218 | exit (1); 219 | } 220 | 221 | int MS_CDECL main (int argc, char **argv) 222 | { 223 | const char *argv0 = argv[0]; 224 | const char *extension_to_test = "?"; 225 | char *program_descr, *program_exe; 226 | 227 | opt.debug = 0; 228 | C_use_colours = 1; 229 | C_init(); 230 | crtdbug_init(); 231 | 232 | if (argc >= 2 && !strcmp(argv[1], "-d")) 233 | { 234 | opt.debug = 1; 235 | argc--; 236 | argv++; 237 | } 238 | 239 | if (argc == 2 && (argv[1][0] == '.' || argv[1][0] == '*')) 240 | extension_to_test = argv[1]; 241 | else usage (argv0); 242 | 243 | C_printf ("File Associations (ASSOCSTR_EXECUTABLE) for ~3%s~0:\n", extension_to_test); 244 | 245 | if (!get_file_assoc(extension_to_test, &program_descr, &program_exe)) 246 | C_printf ("Failed: %s\n", get_file_assoc_last_err()); 247 | else 248 | { 249 | /* In case '*program_exe' is something like "F:\PROGRA~1\WINZIP\winzip32.exe", 250 | * convert to a long-name which should not contain any '~' SFN character. 251 | */ 252 | if (*program_exe != '\0' && FILE_EXISTS(program_exe)) 253 | get_actual_filename (&program_exe, true); 254 | 255 | C_printf (" ~3%s~0 -> ~6%s~0\n", program_descr, program_exe); 256 | 257 | C_printf ("\nAll associations for ~3%s~0:\n", extension_to_test); 258 | get_file_assoc_all (extension_to_test); 259 | } 260 | 261 | FREE (program_descr); 262 | FREE (program_exe); 263 | 264 | crtdbug_exit(); 265 | return (0); 266 | } 267 | #endif /* GET_FILE_ASSOC_TEST */ 268 | 269 | 270 | -------------------------------------------------------------------------------- /src/get_file_assoc.h: -------------------------------------------------------------------------------- 1 | /** \file get_file_assoc.h 2 | * \ingroup Misc 3 | */ 4 | #pragma once 5 | 6 | #include 7 | 8 | extern bool get_file_assoc_all (const char *extension); 9 | extern bool get_file_assoc (const char *extension, char **program, char **exe); 10 | extern bool get_actual_filename (char **file_p, bool allocated); 11 | 12 | -------------------------------------------------------------------------------- /src/getopt_long.h: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright (c) 2000 The NetBSD Foundation, Inc. 3 | * All rights reserved. 4 | * 5 | * This code is derived from software contributed to The NetBSD Foundation 6 | * by Dieter Baron and Thomas Klausner. 7 | * 8 | * Redistribution and use in source and binary forms, with or without 9 | * modification, are permitted provided that the following conditions 10 | * are met: 11 | * 1. Redistributions of source code must retain the above copyright 12 | * notice, this list of conditions and the following disclaimer. 13 | * 2. Redistributions in binary form must reproduce the above copyright 14 | * notice, this list of conditions and the following disclaimer in the 15 | * documentation and/or other materials provided with the distribution. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 18 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 19 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 20 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 21 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #ifndef _GETOPT_LONG_H_ 31 | #define _GETOPT_LONG_H_ 32 | 33 | /** \file getopt_long.h 34 | * \ingroup Misc 35 | * 36 | * GNU-like getopt_long() / getopt_long_only() with 4.4BSD optreset extension. 37 | * getopt() is declared here too for GNU programs. 38 | */ 39 | #define no_argument 0 /**< \def no_argument */ 40 | #define required_argument 1 /**< \def required_argument */ 41 | #define optional_argument 2 /**< \def optional_argument */ 42 | 43 | /**\struct option 44 | */ 45 | struct option { 46 | const char *name; /**< name of long option */ 47 | 48 | /** 49 | * one of `no_argument`, `required_argument` or `optional_argument`:
50 | * whether option takes an argument. 51 | */ 52 | int has_arg; 53 | int *flag; /**< if not NULL, set *flag to val when option found */ 54 | int val; /**< if flag not NULL, value to set `*flag` to; else return value */ 55 | }; 56 | 57 | int getopt_long (int, char * const *, const char *, 58 | const struct option *, int *); 59 | 60 | int getopt_long_only (int, char * const *, const char *, 61 | const struct option *, int *); 62 | 63 | int getopt (int nargc, char * const *nargv, const char *options); 64 | 65 | /* getopt(3) external variables 66 | */ 67 | extern char *optarg; /**< the argument to an option in `optsstring`. */ 68 | extern int optind; /**< the index of the next element to be processed in `argv`. */ 69 | extern int opterr; /**< if caller set this to zero, an error-message will never be printed. */ 70 | extern int optopt; /**< on errors, an unrecognised option character is stored in `optopt`. */ 71 | 72 | /**\typedef void (*set_option) (int o, const char *arg); 73 | * The function-type to set a short or long option. 74 | */ 75 | typedef void (*set_option) (int o, const char *arg); 76 | 77 | /**\typedef struct command_line 78 | * The structure used by getopt_parse() 79 | */ 80 | typedef struct command_line { 81 | /* 82 | * Set on input by the caller. 83 | */ 84 | const char *env_opt; /**< Take options from an environment variable? */ 85 | const char *short_opt; /**< Short options for `getopt_long()` */ 86 | const struct option *long_opt; /**< Long options for `getopt_long()` */ 87 | set_option set_short_opt; /**< Callback for setting a short option */ 88 | set_option set_long_opt; /**< Callback for setting a long option */ 89 | 90 | /* 91 | * Set on output by getopt_parse(). 92 | */ 93 | int argc; /**< Number of arguments in `argv[]` below */ 94 | char **argv; /**< All arguments from all sources are stored here */ 95 | int argc0; /**< The index of the first non-option in `argv[]` */ 96 | char *file_buf; /**< Scratch-buffer for reading a response-file */ 97 | char **file_array; /**< arg-vector of response-file values */ 98 | char *env_buf; /**< Scratch-buffer for `env_opt` */ 99 | char **env_array; /**< arg-vector of `env_opt` value */ 100 | } command_line; 101 | 102 | void getopt_parse (struct command_line *cmd_line); 103 | void getopt_free (struct command_line *cmd_line); 104 | 105 | #endif /* !_GETOPT_LONG_H_ */ 106 | -------------------------------------------------------------------------------- /src/ignore.c: -------------------------------------------------------------------------------- 1 | /** 2 | * \file ignore.c 3 | * \ingroup Misc 4 | * \brief 5 | * Support for reading a config-file with things to 6 | * ignore at run-time. 7 | * 8 | * This file will probably be extended to handle configuration of 9 | * other settings later. 10 | */ 11 | #include "envtool.h" 12 | #include "color.h" 13 | #include "smartlist.h" 14 | #include "ignore.h" 15 | 16 | /** 17 | * The list of sections we handle here. 18 | */ 19 | static const search_list sections[] = { 20 | { 0, "[Compiler]" }, 21 | { 1, "[Registry]" }, 22 | { 2, "[Path]" }, 23 | { 3, "[Python]" }, 24 | { 4, "[PE-resources]" }, 25 | { 5, "[EveryThing]" }, 26 | { 6, "[LUA]" }, /* Only used in lua.c */ 27 | { 7, "[Login]" }, /* Only used in auth.c */ 28 | { 8, "[Shadow]" } /* Use in 'envtool ---check -v' */ 29 | }; 30 | 31 | /**\struct ignore_node 32 | */ 33 | struct ignore_node { 34 | const char *section; /** The section; one of the ones in `sections[]` */ 35 | const char *value; /** The value to ignore (allocated by 'cfg_file.c') */ 36 | }; 37 | 38 | /** A dynamic array of ignore_node. 39 | * \anchor ignore_list 40 | */ 41 | static smartlist_t *ignore_list = NULL; 42 | 43 | /** 44 | * Help indices for `cfg_ignore_first()` and `cfg_ignore_next()`. 45 | */ 46 | static int next_idx = -1; 47 | static UINT curr_sec = UINT_MAX; 48 | 49 | /** 50 | * Parser for `parse_config_file()` in `cfg_file.c`: 51 | * 52 | * Accepts only strings like `"ignore = xx"` from the config-file. 53 | * Add to `ignore_list` in the correct `sections[]` slot. 54 | * 55 | * \param[in] section the section from the file opened in `parse_config_file()`. 56 | * \param[in] key the key from the file opened in `parse_config_file()`. 57 | * \param[in] value the malloced value from the file opened in `parse_config_file()`. 58 | */ 59 | bool cfg_ignore_handler (const char *section, const char *key, const char *value) 60 | { 61 | if (section && !stricmp(key, "ignore")) 62 | { 63 | struct ignore_node *node; 64 | unsigned idx; 65 | 66 | if (!ignore_list) 67 | ignore_list = smartlist_new(); 68 | 69 | idx = list_lookup_value (section, sections, DIM(sections)); 70 | if (idx == UINT_MAX) 71 | { 72 | WARN ("Ignoring unknown section: %s.\n", section); 73 | return (true); 74 | } 75 | 76 | node = MALLOC (sizeof(*node)); 77 | node->section = sections[idx].name; 78 | node->value = value; 79 | smartlist_add (ignore_list, node); 80 | TRACE (3, "%s: ignore = '%s'\n", node->section, node->value); 81 | return (true); 82 | } 83 | return (false); 84 | } 85 | 86 | /** 87 | * Lookup a `value` to test for ignore. Compare the `section` too. 88 | * 89 | * \param[in] section Look for the `value` in this `section`. 90 | * \param[in] value The string-value to check. 91 | * 92 | * \retval false the `section` and `value` was not found in the `ignore_list`. 93 | * \retval true the `section` and `value` was found in the `ignore_list`. 94 | */ 95 | bool cfg_ignore_lookup (const char *section, const char *value) 96 | { 97 | int i, max; 98 | 99 | if (section[0] != '[' || !ignore_list) 100 | return (false); 101 | 102 | max = smartlist_len (ignore_list); 103 | for (i = 0; i < max; i++) 104 | { 105 | const struct ignore_node *node = smartlist_get (ignore_list, i); 106 | 107 | /* Not this section, try the next 108 | */ 109 | if (stricmp(section, node->section)) 110 | continue; 111 | 112 | /* An exact match. 113 | * This is case-sensitive if option '-c' was used. 114 | */ 115 | if (str_equal(value, node->value)) 116 | { 117 | TRACE (3, "Found '%s' in %s.\n", value, section); 118 | return (true); 119 | } 120 | 121 | /* A wildcard match. 122 | * This is case-sensitive if option '-c' was used. 123 | */ 124 | if (fnmatch(node->value, value, fnmatch_case(FNM_FLAG_NOESCAPE | FNM_FLAG_PATHNAME)) == FNM_MATCH) 125 | { 126 | TRACE (3, "Wildcard match for '%s' in %s.\n", value, section); 127 | return (true); 128 | } 129 | } 130 | return (false); 131 | } 132 | 133 | /** 134 | * Lookup the first ignored `value` in a `section`. 135 | * 136 | * \param[in] section the `section` to start in. 137 | */ 138 | const char *cfg_ignore_first (const char *section) 139 | { 140 | const struct ignore_node *node; 141 | int i, max; 142 | UINT idx = list_lookup_value (section, sections, DIM(sections)); 143 | 144 | if (idx == UINT_MAX) 145 | { 146 | TRACE (3, "No such section: %s.\n", section); 147 | goto not_found; 148 | } 149 | 150 | max = ignore_list ? smartlist_len (ignore_list) : 0; 151 | for (i = 0; i < max; i++) 152 | { 153 | node = smartlist_get (ignore_list, i); 154 | if (!stricmp(section, node->section)) 155 | { 156 | next_idx = i + 1; 157 | curr_sec = idx; 158 | return (node->value); 159 | } 160 | } 161 | 162 | not_found: 163 | next_idx = -1; 164 | curr_sec = UINT_MAX; 165 | return (NULL); 166 | } 167 | 168 | /** 169 | * Lookup the next ignored `value` in the same `section`. 170 | * 171 | * \param[in] section the `section` to search in. 172 | */ 173 | const char *cfg_ignore_next (const char *section) 174 | { 175 | const struct ignore_node *node; 176 | int i, max; 177 | 178 | /* cfg_ignore_first() not called or nothing to ignore was 179 | * found by cfg_ignore_first(). 180 | */ 181 | if (next_idx == -1 || curr_sec == UINT_MAX) 182 | return (NULL); 183 | 184 | max = ignore_list ? smartlist_len (ignore_list) : 0; 185 | for (i = next_idx; i < max; i++) 186 | { 187 | node = smartlist_get (ignore_list, i); 188 | if (!stricmp(section, sections[curr_sec].name) && !stricmp(section, node->section)) 189 | { 190 | next_idx = i + 1; 191 | return (node->value); 192 | } 193 | } 194 | next_idx = -1; 195 | return (NULL); 196 | } 197 | 198 | /** 199 | * Dump number of ignored values in all sections. 200 | */ 201 | void cfg_ignore_dump (void) 202 | { 203 | int i, j; 204 | 205 | for (i = 0; i < DIM(sections); i++) 206 | { 207 | const char *section = sections[i].name; 208 | const char *ignored; 209 | 210 | j = 0; 211 | for (ignored = cfg_ignore_first(section); 212 | ignored; 213 | ignored = cfg_ignore_next(section), j++) 214 | ; 215 | TRACE (3, "section: %-15s: num: %d.\n", section, j); 216 | } 217 | } 218 | 219 | /** 220 | * Free the memory allocated in the ignore_list smartlist. 221 | * Called from cleanup() in envtool.c. 222 | */ 223 | void cfg_ignore_exit (void) 224 | { 225 | int i, max; 226 | 227 | if (!ignore_list) 228 | return; 229 | 230 | max = smartlist_len (ignore_list); 231 | for (i = 0; i < max; i++) 232 | { 233 | struct ignore_node *node = smartlist_get (ignore_list, i); 234 | 235 | FREE (node); 236 | } 237 | smartlist_free (ignore_list); 238 | ignore_list = NULL; 239 | } 240 | -------------------------------------------------------------------------------- /src/ignore.h: -------------------------------------------------------------------------------- 1 | /** \file ignore.h 2 | * \ingroup Misc 3 | */ 4 | #pragma once 5 | 6 | #include 7 | 8 | extern void cfg_ignore_exit (void); 9 | extern bool cfg_ignore_lookup (const char *section, const char *value); 10 | extern const char *cfg_ignore_first (const char *section); 11 | extern const char *cfg_ignore_next (const char *section); 12 | extern void cfg_ignore_dump (void); 13 | extern bool cfg_ignore_handler (const char *section, 14 | const char *key, 15 | const char *value); 16 | -------------------------------------------------------------------------------- /src/json.c: -------------------------------------------------------------------------------- 1 | #include "envtool.h" 2 | #include "color.h" 3 | #include "json.h" 4 | 5 | /** 6 | * Returns a string naming token type `t`. 7 | */ 8 | const char *JSON_typestr (JSON_type_t t) 9 | { 10 | return (t == JSON_UNDEFINED ? "UNDEFINED" : 11 | t == JSON_OBJECT ? "OBJECT" : 12 | t == JSON_ARRAY ? "ARRAY" : 13 | t == JSON_STRING ? "STRING" : 14 | t == JSON_PRIMITIVE ? "PRIMITIVE" : "?"); 15 | } 16 | 17 | /** 18 | * Returns an error-string for error `e`. 19 | */ 20 | const char *JSON_strerror (JSON_err e) 21 | { 22 | return (e == JSON_ERROR_NO_TOK ? "JSON_ERROR_NO_TOK" : 23 | e == JSON_ERROR_INVAL ? "JSON_ERROR_INVAL" : 24 | e == JSON_ERROR_PART ? "JSON_ERROR_PART" : "?"); 25 | } 26 | 27 | /** 28 | * Allocates a fresh unused token from the token pool. 29 | */ 30 | static JSON_tok_t *JSON_alloc_token (JSON_parser *parser, JSON_tok_t *tokens, size_t num_tokens) 31 | { 32 | JSON_tok_t *tok; 33 | 34 | if (parser->tok_next >= num_tokens) 35 | return (NULL); 36 | 37 | tok = &tokens [parser->tok_next++]; 38 | tok->start = tok->end = -1; 39 | tok->size = 0; 40 | tok->is_key = 0; 41 | return (tok); 42 | } 43 | 44 | /** 45 | * Fills token type and set boundaries. 46 | */ 47 | static void JSON_fill_token (JSON_tok_t *token, JSON_type_t type, int start, int end) 48 | { 49 | token->type = type; 50 | token->start = start; 51 | token->end = end; 52 | token->size = 0; 53 | } 54 | 55 | /** 56 | * Fills next available token with JSON primitive. 57 | */ 58 | int JSON_parse_primitive (JSON_parser *parser, const char *js, size_t len, JSON_tok_t *tokens, size_t num_tokens) 59 | { 60 | JSON_tok_t *token; 61 | int start = parser->pos; 62 | 63 | for ( ; parser->pos < len && js[parser->pos]; parser->pos++) 64 | { 65 | switch (js[parser->pos]) 66 | { 67 | case ':': 68 | case '\t': 69 | case '\r': 70 | case '\n': 71 | case ' ': 72 | case ',': 73 | case ']': 74 | case '}': 75 | goto found; 76 | } 77 | if (js[parser->pos] < 32 || js[parser->pos] >= 127) 78 | { 79 | parser->pos = start; 80 | return (JSON_ERROR_INVAL); 81 | } 82 | } 83 | 84 | found: 85 | if (!tokens) 86 | { 87 | parser->pos--; 88 | return (0); 89 | } 90 | token = JSON_alloc_token (parser, tokens, num_tokens); 91 | if (!token) 92 | { 93 | parser->pos = start; 94 | TRACE (2, "No more tokens\n"); 95 | return (JSON_ERROR_NO_TOK); 96 | } 97 | JSON_fill_token (token, JSON_PRIMITIVE, start, parser->pos); 98 | parser->pos--; 99 | return (0); 100 | } 101 | 102 | /** 103 | * Fills next token with JSON string. 104 | */ 105 | static int JSON_parse_string (JSON_parser *parser, const char *js, size_t len, JSON_tok_t *tokens, size_t num_tokens) 106 | { 107 | JSON_tok_t *token; 108 | int i, start = parser->pos; 109 | char c; 110 | 111 | /* Skip starting quote 112 | */ 113 | parser->pos++; 114 | 115 | for ( ; parser->pos < len && js[parser->pos]; parser->pos++) 116 | { 117 | c = js [parser->pos]; 118 | 119 | if (c == '\"') /* Quote: end of string */ 120 | { 121 | if (!tokens) 122 | return (0); 123 | 124 | token = JSON_alloc_token (parser, tokens, num_tokens); 125 | if (!token) 126 | { 127 | parser->pos = start; 128 | TRACE (2, "No more tokens\n"); 129 | return (JSON_ERROR_NO_TOK); 130 | } 131 | JSON_fill_token (token, JSON_STRING, start+1, parser->pos); 132 | return (0); 133 | } 134 | 135 | if (c == '\\' && parser->pos + 1 < len) /* Backslash: Quoted symbol expected */ 136 | { 137 | parser->pos++; 138 | switch (js[parser->pos]) 139 | { 140 | /* Allowed escaped symbols */ 141 | case '\"': 142 | case '/': 143 | case '\\': 144 | case 'b': 145 | case 'f': 146 | case 'r': 147 | case 'n': 148 | case 't': 149 | break; 150 | 151 | /* Allows escaped symbol '\uXXXX' */ 152 | case 'u': 153 | parser->pos++; 154 | for (i = 0; i < 4 && parser->pos < len && js[parser->pos]; i++) 155 | { 156 | /* If it isn't a hex character we have an error */ 157 | if (!((js[parser->pos] >= 48 && js[parser->pos] <= 57) || /* 0-9 */ 158 | (js[parser->pos] >= 65 && js[parser->pos] <= 70) || /* A-F */ 159 | (js[parser->pos] >= 97 && js[parser->pos] <= 102))) /* a-f */ 160 | { 161 | parser->pos = start; 162 | return (JSON_ERROR_INVAL); 163 | } 164 | parser->pos++; 165 | } 166 | parser->pos--; 167 | break; 168 | 169 | /* Unexpected symbol */ 170 | default: 171 | parser->pos = start; 172 | return (JSON_ERROR_INVAL); 173 | } 174 | } 175 | } 176 | parser->pos = start; 177 | return (JSON_ERROR_PART); 178 | } 179 | 180 | /** 181 | * Run JSON parser. It parses a JSON data string into and array of tokens, each describing 182 | * a single JSON object. 183 | */ 184 | int JSON_parse (JSON_parser *parser, const char *js, size_t len, JSON_tok_t *tokens, size_t num_tokens) 185 | { 186 | JSON_tok_t *token; 187 | int r, i, count = parser->tok_next; 188 | 189 | for ( ; parser->pos < len && js[parser->pos]; parser->pos++) 190 | { 191 | JSON_type_t type; 192 | char c = js [parser->pos]; 193 | 194 | switch (c) 195 | { 196 | case '{': 197 | case '[': 198 | count++; 199 | if (!tokens) 200 | break; 201 | 202 | token = JSON_alloc_token (parser, tokens, num_tokens); 203 | if (!token) 204 | { 205 | TRACE (2, "No more tokens\n"); 206 | return (JSON_ERROR_NO_TOK); 207 | } 208 | if (parser->tok_super != -1) 209 | tokens[parser->tok_super].size++; 210 | 211 | token->type = (c == '{' ? JSON_OBJECT : JSON_ARRAY); 212 | token->start = parser->pos; 213 | parser->tok_super = parser->tok_next - 1; 214 | break; 215 | 216 | case '}': 217 | case ']': 218 | if (!tokens) 219 | break; 220 | type = (c == '}' ? JSON_OBJECT : JSON_ARRAY); 221 | for (i = parser->tok_next - 1; i >= 0; i--) 222 | { 223 | token = &tokens[i]; 224 | if (token->start != -1 && token->end == -1) 225 | { 226 | if (token->type != type) 227 | return (JSON_ERROR_INVAL); 228 | 229 | parser->tok_super = -1; 230 | token->end = parser->pos + 1; 231 | break; 232 | } 233 | } 234 | /* Error if unmatched closing bracket */ 235 | if (i == -1) 236 | return (JSON_ERROR_INVAL); 237 | for (; i >= 0; i--) 238 | { 239 | token = &tokens[i]; 240 | if (token->start != -1 && token->end == -1) 241 | { 242 | parser->tok_super = i; 243 | break; 244 | } 245 | } 246 | break; 247 | 248 | case '\"': 249 | r = JSON_parse_string (parser, js, len, tokens, num_tokens); 250 | if (r < 0) 251 | return (r); 252 | count++; 253 | if (parser->tok_super != -1 && tokens) 254 | tokens[parser->tok_super].size++; 255 | break; 256 | 257 | case '\n': 258 | case '\t': 259 | case '\r': 260 | case ' ': 261 | break; 262 | 263 | case ':': 264 | if (tokens && parser->tok_next >= 1) 265 | { 266 | JSON_tok_t *key = &tokens [parser->tok_next-1]; 267 | 268 | if (key && key->type == JSON_STRING) 269 | key->is_key = 1; 270 | } 271 | parser->tok_super = parser->tok_next - 1; 272 | break; 273 | 274 | case ',': 275 | if (tokens && parser->tok_super != -1 && 276 | tokens[parser->tok_super].type != JSON_ARRAY && 277 | tokens[parser->tok_super].type != JSON_OBJECT) 278 | { 279 | for (i = parser->tok_next - 1; i >= 0; i--) 280 | { 281 | if (tokens[i].type == JSON_ARRAY || tokens[i].type == JSON_OBJECT) 282 | { 283 | if (tokens[i].start != -1 && tokens[i].end == -1) 284 | { 285 | parser->tok_super = i; 286 | break; 287 | } 288 | } 289 | } 290 | } 291 | break; 292 | 293 | /* In non-strict mode every unquoted value is a primitive */ 294 | default: 295 | r = JSON_parse_primitive (parser, js, len, tokens, num_tokens); 296 | if (r < 0) 297 | return (r); 298 | count++; 299 | if (tokens && parser->tok_super != -1) 300 | tokens[parser->tok_super].size++; 301 | break; 302 | } 303 | } 304 | 305 | if (tokens) 306 | { 307 | for (i = parser->tok_next - 1; i >= 0; i--) 308 | { 309 | /* Unmatched opened object or array 310 | */ 311 | if (tokens[i].start != -1 && tokens[i].end == -1) 312 | return (JSON_ERROR_PART); 313 | } 314 | } 315 | return (count); 316 | } 317 | 318 | /** 319 | * Creates a new parser based on a given buffer with an array of tokens. 320 | */ 321 | void JSON_init (JSON_parser *parser) 322 | { 323 | memset (parser, '\0', sizeof(*parser)); 324 | parser->tok_super = -1; 325 | } 326 | 327 | /** 328 | * Return 1 if Json token matches `str`. 329 | */ 330 | int JSON_str_eq (const JSON_tok_t *tok, const char *buf, const char *str) 331 | { 332 | size_t len = tok->end - tok->start; 333 | 334 | if (tok->type == JSON_STRING && (int)strlen(str) == len && !strnicmp(buf + tok->start, str, len)) 335 | return (1); 336 | return (0); 337 | } 338 | 339 | /** 340 | * Scraped from https://github.com/zserge/jsmn/pull/166/files 341 | * By Maxim Menshikov 342 | * 343 | * Get the size (in tokens) consumed by token and its children. 344 | */ 345 | size_t JSON_get_total_size (const JSON_tok_t *token) 346 | { 347 | size_t rc = 0; 348 | size_t i, j; 349 | const JSON_tok_t *key; 350 | 351 | if (token->type == JSON_PRIMITIVE || token->type == JSON_STRING) 352 | { 353 | rc = 1; 354 | } 355 | else if (token->type == JSON_OBJECT) 356 | { 357 | for (i = j = 0; i < (size_t)token->size; i++) 358 | { 359 | key = token + 1 + j; 360 | j += JSON_get_total_size (key); 361 | if (key->size > 0) 362 | j += JSON_get_total_size (token + 1 + j); 363 | } 364 | rc = j + 1; 365 | } 366 | else if (token->type == JSON_ARRAY) 367 | { 368 | for (i = j = 0; i < (size_t)token->size; i++) 369 | j += JSON_get_total_size (token + 1 + j); 370 | rc = j + 1; 371 | } 372 | return (rc); 373 | } 374 | 375 | /** 376 | * Get token with a given index inside the JSON_ARRAY or JSON_OBJECT defined by 'token' parameter. 377 | */ 378 | const JSON_tok_t *JSON_get_token_by_index (const JSON_tok_t *token, JSON_type_t type, int index) 379 | { 380 | const JSON_tok_t *token2; 381 | size_t i, total_size; 382 | 383 | if (token->type != type) 384 | return (NULL); 385 | 386 | total_size = JSON_get_total_size (token); 387 | for (i = 1; i < total_size; i++) 388 | { 389 | token2 = token + i; 390 | if (index == 0) 391 | return (token2); 392 | i += JSON_get_total_size (token2) - 1; 393 | --index; 394 | } 395 | return (NULL); 396 | } 397 | 398 | -------------------------------------------------------------------------------- /src/json.h: -------------------------------------------------------------------------------- 1 | /** \file json.h 2 | * \ingroup Misc 3 | */ 4 | #ifndef _JSON_H 5 | #define _JSON_H 6 | 7 | /** 8 | * This JSON parser is based on these files: 9 | * https://github.com/zserge/jsmn/blob/master/jsmn.h 10 | * https://github.com/zserge/jsmn/blob/master/example/simple.c 11 | * 12 | * \typedef JSON_type_t 13 | * 14 | * JSON type identifier. Basic types are: 15 | * o Object 16 | * o Array 17 | * o String 18 | * o Other primitive: number, boolean (true/false) or null 19 | */ 20 | typedef enum JSON_type_t { 21 | JSON_UNDEFINED = 0, 22 | JSON_OBJECT = 1, 23 | JSON_ARRAY = 2, 24 | JSON_STRING = 3, 25 | JSON_PRIMITIVE = 4 26 | } JSON_type_t; 27 | 28 | /** 29 | * \typedef JSON_err 30 | * 31 | * Error status from `JSON_parse()` 32 | */ 33 | typedef enum JSON_err { 34 | JSON_ERROR_NO_TOK = -1, /**< Not enough tokens were provided in `JSON_parse()` */ 35 | JSON_ERROR_INVAL = -2, /**< Invalid character inside JSON string */ 36 | JSON_ERROR_PART = -3 /**< The string is not a full JSON packet, more bytes expected */ 37 | } JSON_err; 38 | 39 | /** 40 | * \typedef JSON_tok_t 41 | * 42 | * JSON token description. 43 | * type type (object, array, string etc.) 44 | * start start position in JSON data string 45 | * end end position in JSON data string 46 | */ 47 | typedef struct JSON_tok_t { 48 | JSON_type_t type; 49 | int start; 50 | int end; 51 | int size; 52 | int is_key; 53 | } JSON_tok_t; 54 | 55 | /** 56 | * \typedef JSON_parser 57 | * 58 | * Contains an array of token blocks available. Also stores 59 | * the string being parsed now and current position in that string. 60 | */ 61 | typedef struct JSON_parser { 62 | uint32_t pos; /**< Offset in the JSON string */ 63 | uint32_t tok_next; /**< Next token to allocate */ 64 | int tok_super; /**< Superior token node, e.g parent object or array */ 65 | // uint32_t line; /**< Current parser line of input (unreliable, does not work) */ 66 | // uint32_t column; /**< Current parser column of input (unreliable, does not work) */ 67 | } JSON_parser; 68 | 69 | void JSON_init (JSON_parser *parser); 70 | int JSON_parse (JSON_parser *parser, const char *js, size_t len, JSON_tok_t *tokens, size_t num_tokens); 71 | int JSON_parse_primitive (JSON_parser *parser, const char *js, size_t len, JSON_tok_t *tokens, size_t num_tokens); 72 | size_t JSON_get_total_size (const JSON_tok_t *token); 73 | int JSON_str_eq (const JSON_tok_t *tok, const char *buf, const char *str); 74 | const JSON_tok_t *JSON_get_token_by_index (const JSON_tok_t *token, JSON_type_t type, int index); 75 | const char *JSON_typestr (JSON_type_t t); 76 | const char *JSON_strerror (JSON_err e); 77 | 78 | #endif 79 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /src/lua.h: -------------------------------------------------------------------------------- 1 | /** \file lua.h 2 | * \ingroup Lua 3 | */ 4 | #pragma once 5 | 6 | #include 7 | 8 | extern void lua_init (void); 9 | extern void lua_exit (void); 10 | extern int lua_search (const char *search_spec); 11 | extern void lua_check_env (const char *env, int *num, char *status, size_t status_sz); 12 | extern bool lua_get_info (char **exe, struct ver_info *ver); 13 | extern const char *lua_get_exe (void); 14 | extern bool lua_cfg_handler (const char *section, const char *key, const char *value); 15 | -------------------------------------------------------------------------------- /src/misc.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gvanem/EnvTool/68027f3a5784650874b4fb4803e63de64a1ccd37/src/misc.c -------------------------------------------------------------------------------- /src/pkg-config.c: -------------------------------------------------------------------------------- 1 | /** \file pkg-config.c 2 | * \ingroup pkg_config 3 | * 4 | * \brief The PKG-CONFIG functions for the envtool program. 5 | */ 6 | 7 | #include "envtool.h" 8 | #include "color.h" 9 | #include "report.h" 10 | #include "cache.h" 11 | #include "pkg-config.h" 12 | 13 | static struct ver_info pkgconfig_ver; 14 | static char *pkgconfig_exe = NULL; 15 | 16 | /** 17 | * 18 | */ 19 | typedef struct pkgconfig_dir { 20 | char path [_MAX_PATH]; 21 | HKEY top_key; 22 | bool exist; 23 | bool is_dir; 24 | bool exp_ok; 25 | int num_dup; 26 | } pkgconfig_dir; 27 | 28 | typedef struct pkgconfig_node { 29 | char name [30]; 30 | char description [200]; 31 | } pkgconfig_node; 32 | 33 | static smartlist_t *pkgconfig_dirs = NULL; /* A smartlist_t of 'pkgconfig_dir' */ 34 | static smartlist_t *pkgconfig_pkg = NULL; /* A smartlist_t of 'pkgconfig_node' */ 35 | 36 | /** 37 | * \def ENV_NAME 38 | * The environment variable for search-directories. 39 | */ 40 | #define ENV_NAME "PKG_CONFIG_PATH" 41 | 42 | /** 43 | * \def REG_KEY 44 | * The Registry sub-branch variable for seach-directories. 45 | * 46 | * The full key is either `HKEY_CURRENT_USER\Software\pkgconfig\PKG_CONFIG_PATH` or
47 | * `HKEY_LOCAL_MACHINE\Software\pkgconfig\PKG_CONFIG_PATH`. 48 | */ 49 | #define REG_KEY "Software\\pkgconfig" 50 | 51 | static void add_package (const char *name, char *description) 52 | { 53 | pkgconfig_node *pkg; 54 | 55 | if (!name) 56 | return; 57 | 58 | pkg = MALLOC (sizeof(*pkg)); 59 | _strlcpy (pkg->name, name, sizeof(pkg->name)); 60 | _strlcpy (pkg->description, description ? str_unquote(description) : "", sizeof(pkg->description)); 61 | smartlist_add (pkgconfig_pkg, pkg); 62 | } 63 | 64 | static int pkg_config_version_cb (char *buf, int index) 65 | { 66 | struct ver_info ver = { 0,0,0,0 }; 67 | 68 | ARGSUSED (index); 69 | if (sscanf(buf, "%d.%d", &ver.val_1, &ver.val_2) == 2) 70 | { 71 | memcpy (&pkgconfig_ver, &ver, sizeof(pkgconfig_ver)); 72 | return (1); 73 | } 74 | return (0); 75 | } 76 | 77 | static int pkg_config_list_all_cb (char *buf, int index) 78 | { 79 | char *name, *descr, *end; 80 | 81 | name = _strtok_r (buf, " ", &end); 82 | descr = (*end != '\0') ? end : ""; 83 | descr = str_ltrim (descr); 84 | 85 | TRACE (2, "%3d: %-30s -> %s.\n", index, name, descr); 86 | add_package (name, descr); 87 | return (1); 88 | } 89 | 90 | /** 91 | * Build the list of pkg_config packages found. 92 | * First try the cache. Otherwise spawn `pkg-config.exe --list-all`. 93 | * 94 | * This has no relation to the number of `*.pc` files found on `PKG_CONFIG_PATH`. 95 | * But rather via the output of `pkg-config --list-all`. 96 | */ 97 | static void pkg_config_build_pkg (void) 98 | { 99 | int max, i = 0; 100 | 101 | if (!pkgconfig_exe) 102 | return; 103 | 104 | while (1) 105 | { 106 | char format [100]; 107 | char *name = NULL; 108 | char *descr = NULL; 109 | 110 | snprintf (format, sizeof(format), "pkgconfig_node_%d = %%s,%%s", i++); 111 | if (cache_getf (SECTION_PKGCONFIG, format, &name, &descr) != 2) 112 | break; 113 | add_package (name, descr); 114 | } 115 | 116 | max = smartlist_len (pkgconfig_pkg); 117 | if (max > 0) 118 | TRACE (1, "Found %d cached pkg-config packages.\n", max); 119 | else 120 | { 121 | /* None found from cache. Try for real 122 | */ 123 | popen_run (pkg_config_list_all_cb, pkgconfig_exe, "--list-all 2> %s", DEV_NULL); 124 | } 125 | 126 | max = smartlist_len (pkgconfig_pkg); 127 | for (i = 0; i < max; i++) 128 | { 129 | const pkgconfig_node *pkg = smartlist_get (pkgconfig_pkg, i); 130 | 131 | TRACE (2, "%3d: %-30s descr: %s.\n", i, pkg->name, pkg->description); 132 | cache_putf (SECTION_PKGCONFIG, "pkgconfig_node_%d = %s,\"%s\"", i, pkg->name, pkg->description); 133 | } 134 | } 135 | 136 | /** 137 | * Get the number of pkg_config packages found. 138 | */ 139 | unsigned pkg_config_get_num_installed (void) 140 | { 141 | int num; 142 | 143 | pkg_config_init(); 144 | num = pkgconfig_pkg ? smartlist_len (pkgconfig_pkg) : 0; 145 | return (num); 146 | } 147 | 148 | /** 149 | * Print the pkg_config packages found. 150 | */ 151 | unsigned pkg_config_list_installed (void) 152 | { 153 | int i, max = pkgconfig_pkg ? smartlist_len (pkgconfig_pkg) : 0; 154 | int num_dirs = pkgconfig_dirs ? smartlist_len (pkgconfig_dirs) : 0; 155 | int indent; 156 | 157 | C_printf ("\n Found %u ~3pkg-config~0 packages in ~3%d~0 directories:\n", max, num_dirs); 158 | for (i = 0; i < max; i++) 159 | { 160 | const pkgconfig_node *pkg = smartlist_get (pkgconfig_pkg, i); 161 | 162 | indent = C_printf (" %-25s", pkg->name); 163 | C_puts_long_line (pkg->description, indent); 164 | } 165 | return (max); 166 | } 167 | 168 | /** 169 | * Find the version and location `pkg-config.exe` (on `PATH`). 170 | * First from the cache, then do it for real. 171 | * 172 | * In case Cygwin is installed and a `/bin/pkg-config` is on PATH, check 173 | * if it's symlinked to a `/bin/pkgconf.exe` program. 174 | */ 175 | static bool pkg_config_get_info_internal (void) 176 | { 177 | static char exe_copy [_MAX_PATH]; 178 | 179 | cache_getf (SECTION_PKGCONFIG, "pkgconfig_exe = %s", &pkgconfig_exe); 180 | cache_getf (SECTION_PKGCONFIG, "pkgconfig_version = %d,%d", &pkgconfig_ver.val_1, &pkgconfig_ver.val_2); 181 | 182 | if (pkgconfig_exe && !FILE_EXISTS(pkgconfig_exe)) 183 | { 184 | cache_del (SECTION_PKGCONFIG, "pkgconfig_exe"); 185 | cache_del (SECTION_PKGCONFIG, "pkgconfig_version"); 186 | memset (&pkgconfig_ver, '\0', sizeof(pkgconfig_ver)); 187 | pkgconfig_exe = NULL; 188 | return pkg_config_get_info_internal(); 189 | } 190 | 191 | /* The above recursive call will get here (having done nothing prior to that) 192 | */ 193 | if (!pkgconfig_exe) 194 | { 195 | const char *cyg_exe = searchpath ("pkg-config", "PATH"); 196 | 197 | if (cyg_exe) 198 | pkgconfig_exe = (char*) get_sym_link (cyg_exe); 199 | else pkgconfig_exe = searchpath ("pkg-config.exe", "PATH"); 200 | } 201 | 202 | if (!pkgconfig_exe) 203 | return (false); 204 | 205 | pkgconfig_exe = slashify2 (exe_copy, pkgconfig_exe, '\\'); 206 | cache_putf (SECTION_PKGCONFIG, "pkgconfig_exe = %s", pkgconfig_exe); 207 | 208 | /* A valid version from the cache? 209 | */ 210 | if (!VALID_VER(pkgconfig_ver) && 211 | popen_run(pkg_config_version_cb, pkgconfig_exe, "--version") > 0) 212 | cache_putf (SECTION_PKGCONFIG, "pkgconfig_version = %d,%d", pkgconfig_ver.val_1, pkgconfig_ver.val_2); 213 | 214 | TRACE (2, "ver: %d.%d.\n", pkgconfig_ver.val_1, pkgconfig_ver.val_2); 215 | return (VALID_VER(pkgconfig_ver)); 216 | } 217 | 218 | /** 219 | * Return pkg-config information to caller. 220 | */ 221 | bool pkg_config_get_info (char **exe, struct ver_info *ver) 222 | { 223 | pkg_config_init(); 224 | 225 | if (pkgconfig_exe && VALID_VER(pkgconfig_ver)) 226 | { 227 | *exe = STRDUP (pkgconfig_exe); 228 | *ver = pkgconfig_ver; 229 | return (true); 230 | } 231 | return (false); 232 | } 233 | 234 | /** 235 | * Get the `PKG_CONFIG_PATH` from Registry. 236 | */ 237 | static smartlist_t *pkg_config_reg_keys (HKEY top_key) 238 | { 239 | char buf [16*1024] = ""; 240 | char *tok, *end; 241 | int i; 242 | DWORD type = 0; 243 | DWORD buf_size = sizeof(buf); 244 | DWORD rc = RegGetValue (top_key, REG_KEY, ENV_NAME, RRF_RT_REG_SZ, 245 | &type, (void*)&buf, &buf_size); 246 | 247 | TRACE (1, " RegGetValue (%s\\%s, %s), type: %s:\n -> %s\n", 248 | reg_top_key_name(top_key), REG_KEY, win_strerror(rc), reg_type_name(type), buf); 249 | 250 | if (rc != ERROR_SUCCESS || type != REG_SZ) 251 | return (NULL); 252 | 253 | i = 0; 254 | for (tok = _strtok_r(buf, ";", &end); tok; 255 | tok = _strtok_r(NULL, ";", &end)) 256 | { 257 | TRACE (1, "tok[%d]: '%s'\n", i++, tok); 258 | reg_array_add (top_key, tok, tok); 259 | } 260 | return reg_array_head(); 261 | } 262 | 263 | /** 264 | * Get and print more verbose details in a pkg-config `.pc` file. 265 | * Look for lines like: 266 | * ``` 267 | * Description: Python bindings for cairo 268 | * Version: 1.8.10 269 | * ``` 270 | * 271 | * and print this like `Python bindings for cairo (v1.8.10) ` 272 | */ 273 | int pkg_config_get_details (const char *pc_file, const char *filler) 274 | { 275 | FILE *f = fopen (pc_file, "rt"); 276 | char buf [1000], *p; 277 | char descr [1000] = { "" }; 278 | char version [50] = { "" }; 279 | 280 | if (!f) 281 | return (0); 282 | 283 | while (fgets(buf, sizeof(buf), f)) 284 | { 285 | p = str_ltrim (buf); 286 | sscanf (p, "Description: %999[^\r\n]", descr); 287 | sscanf (p, "Version: %49s", version); 288 | } 289 | if (descr[0] && version[0]) 290 | C_printf ("\n%s%s (v%s)", filler, str_ltrim(descr), str_ltrim(version)); 291 | fclose (f); 292 | return (1); 293 | } 294 | 295 | int pkg_config_get_details2 (report *r) 296 | { 297 | return pkg_config_get_details (r->file, r->filler); 298 | } 299 | 300 | /** 301 | * Create a single smartlist with unique paths from 2 smartlists. 302 | * 303 | * \param[in] dir The smartlist of directories from env-var `PKG_CONFIG_PATH`. 304 | * \param[in] reg The smartlist of directories from Registry. 305 | * 306 | * This function assumes the directories in `dir` are unique. It will for each 307 | * element in `dir` check if it's present in `reg`. If not found in `dir`, 308 | * it will append it to `pkgconfig_dirs`. 309 | */ 310 | static void merge_directories (smartlist_t *dir, smartlist_t *reg) 311 | { 312 | pkgconfig_dir *pkdir; 313 | int i, j; 314 | int dir_max = dir ? smartlist_len(dir) : 0; 315 | int reg_max = reg ? smartlist_len(reg) : 0; 316 | 317 | for (i = 0; i < dir_max; i++) 318 | { 319 | const directory_array *arr = smartlist_get (dir, i); 320 | 321 | pkdir = CALLOC (sizeof(*pkdir), 1); 322 | _strlcpy (pkdir->path, arr->dir, sizeof(pkdir->path)); 323 | pkdir->top_key = NULL; 324 | pkdir->exist = arr->exist; 325 | pkdir->is_dir = arr->is_dir; 326 | pkdir->exp_ok = arr->exp_ok; 327 | pkdir->num_dup = arr->num_dup; 328 | smartlist_add (pkgconfig_dirs, pkdir); 329 | } 330 | 331 | /* Loop over the `reg` list and add only if not already in `pkgconfig_dirs`. 332 | */ 333 | for (i = 0; i < reg_max; i++) 334 | { 335 | const registry_array *arr1 = smartlist_get (reg, i); 336 | const pkgconfig_dir *arr2; 337 | bool add_it = true; 338 | 339 | for (j = 0; j < dir_max; j++) 340 | { 341 | arr2 = smartlist_get (pkgconfig_dirs, j); 342 | if (!stricmp(arr1->fname, arr2->path)) 343 | { 344 | add_it = false; 345 | break; 346 | } 347 | } 348 | if (!add_it) 349 | continue; 350 | 351 | pkdir = CALLOC (sizeof(*pkdir), 1); 352 | _strlcpy (pkdir->path, arr1->fname, sizeof(pkdir->path)); 353 | pkdir->top_key = arr1->key; 354 | pkdir->exist = arr1->exist; 355 | pkdir->is_dir = arr1->exist; 356 | pkdir->exp_ok = true; 357 | smartlist_add (pkgconfig_dirs, pkdir); 358 | } 359 | } 360 | 361 | /** 362 | * Initialise this module. Only once. 363 | */ 364 | void pkg_config_init (void) 365 | { 366 | smartlist_t *list_env, *list_reg; 367 | char *orig_e; 368 | 369 | if (pkgconfig_pkg) 370 | return; 371 | 372 | pkgconfig_pkg = smartlist_new(); 373 | pkgconfig_dirs = smartlist_new(); 374 | 375 | orig_e = getenv_expand (ENV_NAME); 376 | list_env = orig_e ? split_env_var (ENV_NAME, orig_e) : NULL; 377 | 378 | list_reg = pkg_config_reg_keys (HKEY_CURRENT_USER); 379 | if (!list_reg) 380 | list_reg = pkg_config_reg_keys (HKEY_LOCAL_MACHINE); 381 | 382 | merge_directories (list_env, list_reg); 383 | dir_array_free(); 384 | reg_array_free(); 385 | FREE (orig_e); 386 | 387 | pkg_config_get_info_internal(); 388 | pkg_config_build_pkg(); 389 | } 390 | 391 | /** 392 | * Called from `cleanup()` to free memory allocated here. 393 | */ 394 | void pkg_config_exit (void) 395 | { 396 | smartlist_free_all (pkgconfig_dirs); 397 | smartlist_free_all (pkgconfig_pkg); 398 | pkgconfig_pkg = pkgconfig_dirs = NULL; 399 | } 400 | 401 | /** 402 | * Search and check along `pkg_config_dirs` for a 403 | * matching `.pc` file. 404 | */ 405 | int pkg_config_search (const char *search_spec) 406 | { 407 | int i, max, num, prev_num = 0, found = 0; 408 | bool do_warn = false; 409 | 410 | pkg_config_init(); 411 | 412 | if (smartlist_len(pkgconfig_dirs) == 0) 413 | { 414 | WARN ("%s not defined in environment nor in the Registry\n", ENV_NAME); 415 | return (0); 416 | } 417 | 418 | report_header_set ("Matches in %%%s:\n", ENV_NAME); 419 | 420 | max = smartlist_len (pkgconfig_dirs); 421 | for (i = 0; i < max; i++) 422 | { 423 | pkgconfig_dir *dir = smartlist_get (pkgconfig_dirs, i); 424 | char prefix [30]; 425 | 426 | TRACE (2, "Checking in %s dir '%s'\n", dir->top_key ? "Registry" : "environment", dir->path); 427 | 428 | if (dir->top_key == HKEY_CURRENT_USER) 429 | snprintf (prefix, sizeof(prefix), "[HKCU\\%s]", REG_KEY); 430 | else if (dir->top_key == HKEY_LOCAL_MACHINE) 431 | snprintf (prefix, sizeof(prefix), "[HKLM\\%s]", REG_KEY); 432 | else if (dir->top_key == NULL) 433 | _strlcpy (prefix, ENV_NAME, sizeof(prefix)); 434 | else 435 | _strlcpy (prefix, "PkgConfig?", sizeof(prefix)); 436 | 437 | num = process_dir (dir->path, 0, dir->exist, true, dir->is_dir, dir->exp_ok, 438 | prefix, HKEY_PKG_CONFIG_FILE); 439 | if (dir->num_dup == 0 && prev_num > 0 && num > 0) 440 | do_warn = true; 441 | found += num; 442 | } 443 | 444 | if (do_warn && !opt.quiet) 445 | { 446 | WARN ("Note: "); 447 | C_printf ("~6There seems to be several '%s' files in different %%%s directories.\n" 448 | " \"pkg-config\" will only select the first.~0\n", search_spec, ENV_NAME); 449 | } 450 | return (found); 451 | } 452 | 453 | void pkg_config_extras (const struct ver_data *v, int pad_len) 454 | { 455 | unsigned num = pkg_config_get_num_installed(); 456 | 457 | C_printf ("%-*s -> ~6%s~0", pad_len, v->found, slashify(v->exe, v->slash)); 458 | if (num >= 1) 459 | C_printf (" (%u .pc files installed).", num); 460 | C_putc ('\n'); 461 | } 462 | -------------------------------------------------------------------------------- /src/pkg-config.h: -------------------------------------------------------------------------------- 1 | /** \file pkg-config.h 2 | * \ingroup pkg_config 3 | */ 4 | #pragma once 5 | 6 | #include 7 | 8 | extern void pkg_config_init (void); 9 | extern void pkg_config_exit (void); 10 | extern int pkg_config_search (const char *search_spec); 11 | extern bool pkg_config_get_info (char **exe, struct ver_info *ver); 12 | extern int pkg_config_get_details (const char *pc_file, const char *filler); 13 | extern int pkg_config_get_details2 (report *r); 14 | extern unsigned pkg_config_get_num_installed (void); 15 | extern unsigned pkg_config_list_installed (void); 16 | extern void pkg_config_extras (const struct ver_data *v, int pad_len); 17 | -------------------------------------------------------------------------------- /src/report.h: -------------------------------------------------------------------------------- 1 | /** \file report.h 2 | * \ingroup Misc 3 | */ 4 | #pragma once 5 | 6 | typedef struct report { 7 | const char *file; 8 | time_t mtime; 9 | UINT64 fsize; 10 | BOOL is_dir; 11 | BOOL is_junction; 12 | BOOL is_cwd; 13 | HKEY key; 14 | DWORD last_err; 15 | const char *filler; 16 | const char *content; 17 | int (*pre_action) (struct report *r); 18 | int (*post_action) (struct report *r); 19 | } report; 20 | 21 | extern DWORD num_version_ok; 22 | extern DWORD num_verified; 23 | extern DWORD num_evry_dups; 24 | extern DWORD num_evry_ignored; 25 | 26 | extern int report_file (report *r); 27 | extern int report_file2 (report *r); 28 | extern void report_header_print (void); 29 | extern void report_header_set (const char *fmt, ...); 30 | extern void report_final (int found); 31 | 32 | -------------------------------------------------------------------------------- /src/searchpath.c: -------------------------------------------------------------------------------- 1 | /** \file searchpath.c 2 | * \ingroup Misc 3 | * \brief 4 | * Find a file along an envirnment variable. (Usually the `%PATH%`). 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include "envtool.h" 16 | 17 | static int last_pos = -1; 18 | 19 | /** 20 | * Return the last position in `env_var` for last successfull 21 | * `searchpath()` call. 22 | */ 23 | int searchpath_pos (void) 24 | { 25 | return (last_pos); 26 | } 27 | 28 | /** 29 | * Search `%env_var` for the first `file` (not a `file_spec`). 30 | * 31 | * If successful, store the full pathname in static buffer and return a 32 | * pointer to it. If not successful, return `NULL`. 33 | * 34 | * This is what the Borland `searchpath()` library function does. 35 | * 36 | * \note 37 | * if `env_var` is just a directory name, the `file` is just tested for 38 | * presence in that directory name. 39 | * \eg 40 | * \code 41 | * searchpath ("SWAPFILE.SYS", "c:\\") 42 | * \endcode 43 | * would simply return `C:\\SWAPFILE.SYS`. 44 | */ 45 | static char *_searchpath (const char *file, const char *env_var, char *found) 46 | { 47 | char *env, *path, *_path, *end, *tok; 48 | const char *env_dir = NULL; 49 | char *_file = alloca (strlen(file)+1); 50 | 51 | last_pos = -1; 52 | 53 | if (!file || !*file) 54 | { 55 | TRACE (1, "given a bogus 'file': '%s'\n", file); 56 | errno = EINVAL; 57 | return (NULL); 58 | } 59 | 60 | if (!strncmp(file, "\\\\.\\", 4)) 61 | { 62 | TRACE (1, "Not handling UNC-names: '%s'\n", file); 63 | errno = EINVAL; 64 | return (NULL); 65 | } 66 | 67 | if (!env_var || !*env_var) 68 | { 69 | TRACE (1, "given a bogus 'env_var'\n"); 70 | errno = EINVAL; 71 | return (NULL); 72 | } 73 | 74 | init_misc(); 75 | 76 | strcpy (_file, file); 77 | str_unquote (_file); /* remove quotes around a long filename */ 78 | 79 | found[0] = '\0'; 80 | 81 | env = getenv_expand (env_var); 82 | if (!env) 83 | { 84 | path = MALLOC (strlen(env_var) + 3); /* Room for `.;env_var` */ 85 | if (is_directory(env_var)) /* Given env_var is "c:\\" */ 86 | env_dir = env_var; 87 | } 88 | else 89 | path = MALLOC (strlen(env) + 3); /* Room for `.;%env_var%` */ 90 | 91 | if (!path) 92 | { 93 | TRACE (1, "calloc() failed"); 94 | FREE (env); 95 | errno = ENOMEM; 96 | return (NULL); 97 | } 98 | 99 | if ((env && !(env[0] == '.' && env[1] == ';')) || !env) 100 | { 101 | path[0] = '.'; 102 | path[1] = ';'; 103 | path[2] = '\0'; 104 | _path = path + 2; 105 | } 106 | else 107 | _path = path; 108 | 109 | if (env) 110 | strcpy (_path, env); 111 | else if (env_dir) 112 | strcpy (_path, env_dir); 113 | 114 | TRACE (2, "Looking for _file: '%s' in path: '%s'\n", _file, path); 115 | 116 | tok = _strtok_r (path, ";", &end); 117 | while (tok) 118 | { 119 | last_pos++; 120 | 121 | snprintf (found, _MAX_PATH, "%s\\%s", str_unquote(tok), _file); 122 | if (FILE_EXISTS(found)) 123 | goto was_found; 124 | 125 | tok = _strtok_r (NULL, ";", &end); 126 | } 127 | 128 | FREE (env); 129 | FREE (path); 130 | 131 | last_pos = -1; 132 | errno = ENOENT; 133 | return (NULL); 134 | 135 | was_found: 136 | FREE (env); 137 | FREE (path); 138 | return _fix_path (found, found); 139 | } 140 | 141 | /** 142 | * The public interface to this module. 143 | * \param[in] file the file to search for in an environment variable. 144 | * \param[in] env_var the name of the environment variable (e.g. `PATH`). 145 | * 146 | * \note If `file` is found, this function returns a static buffer. 147 | */ 148 | char *searchpath (const char *file, const char *env_var) 149 | { 150 | static char found [_MAX_PATH]; 151 | char *s = _searchpath (file, env_var, found); 152 | 153 | if (s) 154 | return slashify2 (found, found, opt.show_unix_paths ? '/' : '\\'); 155 | return (NULL); 156 | } 157 | 158 | /* 159 | * Not used any more. 160 | */ 161 | int _is_DOS83 (const char *fname) 162 | { 163 | const char *s = fname; 164 | const char *e; 165 | char c, period_seen = 0; 166 | 167 | if (*s == '.') 168 | { 169 | if (s[1] == 0) 170 | return (1); /* "." is valid */ 171 | if (s[1] == '.' && s[2] == 0) 172 | return (1); /* ".." is valid */ 173 | return (0); /* starting period invalid */ 174 | } 175 | 176 | e = s + 8; /* end */ 177 | 178 | while ((c = *s++) != 0) 179 | if (c == '.') 180 | { 181 | if (period_seen) 182 | return (0); /* multiple periods invalid */ 183 | period_seen = 1; 184 | e = s + 3; /* already one past period */ 185 | } 186 | else if (s > e) 187 | return (0); /* name component too long */ 188 | 189 | if (c >= 'a' && c <= 'z') 190 | return (0); /* lower case character */ 191 | 192 | if (strchr("+,; =[]", c)) 193 | return (0); /* special non-DOS characters */ 194 | return (1); /* all chars OK */ 195 | } 196 | -------------------------------------------------------------------------------- /src/smartlist.h: -------------------------------------------------------------------------------- 1 | /** \file smartlist.h 2 | * \ingroup Misc 3 | */ 4 | #pragma once 5 | 6 | #include 7 | 8 | #define SMARTLIST_FOREACH(sl) for (i = 0; i < smartlist_len(sl); i++) 9 | #define SMARTLIST_EMPTY(sl) (smartlist_len(sl) == 0) 10 | 11 | typedef struct smartlist_t smartlist_t; /* Opaque struct; defined in smartlist.c */ 12 | 13 | typedef int (*smartlist_sort_func) (const void **a, const void **b); 14 | typedef int (*smartlist_compare_func) (const void *key, const void **member); 15 | typedef void (*smartlist_parse_func) (smartlist_t *sl, const char *line); 16 | typedef void (*smartlist_parse_reg_func) (smartlist_t *sl, const char *key, const char *value); 17 | typedef void (*smartlist_free_func) (void *a); 18 | 19 | smartlist_t *smartlist_new (void); 20 | int smartlist_len (const smartlist_t *sl); 21 | void *smartlist_get (const smartlist_t *sl, int idx); 22 | unsigned smartlist_getu (const smartlist_t *sl, int idx); 23 | void smartlist_set (smartlist_t *sl, int idx, void *val); 24 | 25 | void smartlist_free (smartlist_t *sl); 26 | void smartlist_free_all (smartlist_t *sl); 27 | void **smartlist_ensure_capacity (smartlist_t *sl, size_t num); 28 | void *smartlist_add (smartlist_t *sl, void *element); 29 | unsigned smartlist_addu (smartlist_t *sl, unsigned element); 30 | char *smartlist_add_strdup (smartlist_t *sl, const char *str); 31 | void smartlist_del (smartlist_t *sl, int idx); 32 | void smartlist_del_keeporder (smartlist_t *sl, int idx); 33 | size_t smartlist_append (smartlist_t *sl1, const smartlist_t *sl2); 34 | void smartlist_insert (smartlist_t *sl, int idx, void *val); 35 | void smartlist_swap (smartlist_t *sl, int idx1, int idx2); 36 | int smartlist_pos (const smartlist_t *sl, const void *element); 37 | 38 | void smartlist_clear (smartlist_t *sl); 39 | void smartlist_wipe (smartlist_t *sl, smartlist_free_func free_fn); 40 | 41 | int smartlist_duplicates (smartlist_t *sl, smartlist_sort_func compare); 42 | int smartlist_make_uniq (smartlist_t *sl, smartlist_sort_func compare, smartlist_free_func free_fn); 43 | 44 | void smartlist_sort (smartlist_t *sl, smartlist_sort_func compare); 45 | 46 | int smartlist_bsearch_idx (const smartlist_t *sl, const void *key, 47 | smartlist_compare_func compare, bool *found_out); 48 | 49 | void *smartlist_bsearch (const smartlist_t *sl, const void *key, 50 | smartlist_compare_func compare); 51 | 52 | smartlist_t *smartlist_read_file (smartlist_parse_func parse, const char *file_fmt, ...); 53 | int smartlist_write_file (smartlist_t *sl, const char *file_fmt, ...); 54 | smartlist_t *smartlist_read_registry (smartlist_parse_reg_func parse, const char *reg_fmt, ...); 55 | 56 | smartlist_t *smartlist_split_str (const char *val, const char *sep); 57 | char *smartlist_join_str (smartlist_t *sl, const char *sep); 58 | 59 | #ifdef _DEBUG 60 | /* 61 | * Some helpful bug-hunter versions and macros. 62 | */ 63 | int smartlist_len_dbg (const smartlist_t *sl, const char *sl_name, const char *file, unsigned line); 64 | void *smartlist_get_dbg (const smartlist_t *sl, int idx, const char *sl_name, const char *file, unsigned line); 65 | unsigned smartlist_getu_dbg (const smartlist_t *sl, int idx, const char *sl_name, const char *file, unsigned line); 66 | void *smartlist_add_dbg (smartlist_t *sl, void *element, const char *sl_name, const char *file, unsigned line); 67 | unsigned smartlist_addu_dbg (smartlist_t *sl, unsigned element, const char *sl_name, const char *file, unsigned line); 68 | char *smartlist_add_strdup_dbg (smartlist_t *sl, const char *string, const char *sl_name, const char *file, unsigned line); 69 | 70 | #define smartlist_len(sl) smartlist_len_dbg (sl, #sl, __FILE(), __LINE__) 71 | #define smartlist_get(sl, idx) smartlist_get_dbg (sl, idx, #sl, __FILE(), __LINE__) 72 | #define smartlist_getu(sl, idx) smartlist_getu_dbg (sl, idx, #sl, __FILE(), __LINE__) 73 | #define smartlist_add(sl, elem) smartlist_add_dbg (sl, elem, #sl, __FILE(), __LINE__) 74 | #define smartlist_addu(sl, elem) smartlist_addu_dbg (sl, elem, #sl, __FILE(), __LINE__) 75 | #define smartlist_add_strdup(sl, str) smartlist_add_strdup_dbg (sl, str, #sl, __FILE(), __LINE__) 76 | #endif 77 | 78 | -------------------------------------------------------------------------------- /src/sort.c: -------------------------------------------------------------------------------- 1 | /** 2 | * \file sort.c 3 | * \ingroup Misc 4 | * \brief 5 | * Handling of command-line options `-S` and `--sort`. 6 | */ 7 | #include "envtool.h" 8 | #include "sort.h" 9 | 10 | #define ADD_VALUE(v) { v, #v } 11 | 12 | static const search_list method_names[] = { 13 | ADD_VALUE (SORT_FILE_NAME), 14 | ADD_VALUE (SORT_FILE_EXTENSION), 15 | ADD_VALUE (SORT_FILE_DATE), 16 | ADD_VALUE (SORT_FILE_TIME), 17 | ADD_VALUE (SORT_FILE_SIZE), 18 | ADD_VALUE (SORT_PE_VERSION) 19 | }; 20 | 21 | static const search_list short_methods[] = { 22 | { SORT_FILE_NAME, "n" }, 23 | { SORT_FILE_EXTENSION, "e" }, 24 | { SORT_FILE_DATE, "d" }, 25 | { SORT_FILE_TIME, "t" }, 26 | { SORT_FILE_SIZE, "s" }, 27 | { SORT_PE_VERSION, "v" } 28 | }; 29 | 30 | static const search_list long_methods[] = { 31 | { SORT_FILE_NAME, "name" }, 32 | { SORT_FILE_EXTENSION, "ext" }, 33 | { SORT_FILE_DATE, "date" }, 34 | { SORT_FILE_TIME, "time" }, 35 | { SORT_FILE_SIZE, "size" }, 36 | { SORT_PE_VERSION, "version" } 37 | }; 38 | 39 | /** 40 | * Internal: ensure the local `search_list` are large enough 41 | * and of the same sized (use since a `_STATIC_ASSERT()` did not work). 42 | */ 43 | static void check_sizes (void) 44 | { 45 | if (DIM(method_names) != DIM(short_methods)) 46 | FATAL ("'method_names[]' and 'short_methods[]' must have the same number of elements.\n"); 47 | 48 | if (DIM(method_names) != DIM(long_methods)) 49 | FATAL ("'method_names[]' and 'long_methods[]' must have the same number of elements.\n"); 50 | 51 | if (DIM(opt.sort_methods) < DIM(long_methods)) 52 | FATAL ("'DIM(opt.sort_methods[])' must be >= %d\n.", DIM(long_methods)); 53 | } 54 | 55 | /** 56 | * Internal: build a comma separated list of sort methods. 57 | * 58 | * \retval the length of `buf` that was filled. 59 | */ 60 | static size_t get_methods (char *buf, size_t left, const search_list *l, size_t num) 61 | { 62 | size_t i, sz, sz_total = 0; 63 | 64 | for (i = 0; i < num && left > 2; i++, l++) 65 | { 66 | sz = strlen (l->name); 67 | _strlcpy (buf, l->name, left); 68 | sz_total += sz; 69 | buf += sz; 70 | left -= sz; 71 | if (i < num-1) 72 | { 73 | *buf++ = ','; 74 | sz_total++; 75 | } 76 | } 77 | *buf = '\0'; 78 | return (sz_total); 79 | } 80 | 81 | /** 82 | * Return a comma separated list of the accepted short and long sort methods. 83 | * 84 | * \retval currently `"n,e,d,t,s,v,name,ext,date,time,size,version"`. 85 | */ 86 | const char *get_sort_methods (void) 87 | { 88 | static char methods [200]; 89 | size_t sz; 90 | 91 | check_sizes(); 92 | sz = get_methods (methods, sizeof(methods), short_methods, DIM(short_methods)); 93 | methods [sz++] = ','; 94 | get_methods (methods + sz, sizeof(methods) - sz, long_methods, DIM(long_methods)); 95 | return (methods); 96 | } 97 | 98 | /** 99 | * Called from the `getopt_long()` handlers (`set_short_option()` or `set_long_option()` 100 | * in envtool.c) to set `opt.sort_methods[]` based on `opts`. 101 | * 102 | * \param[in] opts match each `enum SortMethod`bit-value in this against 103 | * `short_methods[]` and `long_methods[]`. 104 | * 105 | * \param[in,out] err_opt a char-pointer which can be set to the illegal sort method. 106 | * 107 | * \retval true if a matching method(s) was found. 108 | */ 109 | bool set_sort_method (const char *opts, char **err_opt) 110 | { 111 | static char err_buf[20]; 112 | bool rc; 113 | char *opts2, *end, *tok; 114 | int i, num = 0; 115 | 116 | *err_opt = NULL; 117 | check_sizes(); 118 | ASSERT (opts); 119 | 120 | TRACE (1, "got sort opts: '%s'.\n", opts); 121 | opts2 = alloca (strlen(opts)+1); 122 | strcpy (opts2, opts); 123 | 124 | rc = true; /* Assume 'opts2' is okay */ 125 | tok = _strtok_r (opts2, ", ", &end); 126 | while (tok && num < DIM(opt.sort_methods)-1) 127 | { 128 | unsigned m1 = list_lookup_value (tok, short_methods, DIM(short_methods)); 129 | unsigned m2 = list_lookup_value (tok, long_methods, DIM(long_methods)); 130 | 131 | if (m1 < UINT_MAX) 132 | opt.sort_methods [num++] = m1; 133 | else if (m2 < UINT_MAX) 134 | opt.sort_methods [num++] = m2; 135 | else 136 | { 137 | *err_opt = _strlcpy (err_buf, tok, sizeof(err_buf)); 138 | rc = false; 139 | break; 140 | } 141 | tok = _strtok_r (NULL, ", ", &end); 142 | } 143 | for (i = 0; opt.sort_methods[i]; i++) 144 | TRACE (1, "opt.sort_methods[%d]: '%s'.\n", 145 | i, list_lookup_name(opt.sort_methods[i], method_names, DIM(method_names))); 146 | TRACE (1, "opt.sort_methods[%d]: 0.\n", i); 147 | return (rc); 148 | } 149 | 150 | 151 | -------------------------------------------------------------------------------- /src/sort.h: -------------------------------------------------------------------------------- 1 | /** \file sort.h 2 | */ 3 | #pragma once 4 | 5 | #include 6 | 7 | /** \enum SortMethod 8 | * 9 | * Used with the `-S` or `--sort` cmd-line options to sort 10 | * matches on file-name, file-extension, date/time, size or version. 11 | * The latter only applies to *PE-files* with version-information in it's 12 | * *Resource* section. 13 | * 14 | * Always sort from low to high. 15 | * 16 | * Several of these values can be set in `opt.sort_methods[]` to form a first-level and 17 | * second-level sort. 18 | * \eg a `opt.sort_method[0] = SORT_FILE_NAME`, 19 | * `opt.sort_method[1] = SORT_FILE_EXTENSION` and 20 | * `opt.sort_method[2] = 0` applied to this list of files: 21 | * ``` 22 | * file-b.txt 23 | * file-a.txt 24 | * file-a.a 25 | * file-b.a 26 | * ``` 27 | * 28 | * will return this list of files: 29 | * ``` 30 | * file-a.a 31 | * file-a.txt 32 | * file-b.a 33 | * file-b.txt 34 | * ``` 35 | */ 36 | typedef enum SortMethod { 37 | SORT_FILE_UNSORTED = 0x00, 38 | SORT_FILE_NAME = 0x01, 39 | SORT_FILE_EXTENSION = 0x02, 40 | SORT_FILE_DATE = 0x04, 41 | SORT_FILE_TIME = 0x08, 42 | SORT_FILE_SIZE = 0x10, 43 | SORT_PE_VERSION = 0x20 44 | } SortMethod; 45 | 46 | extern bool set_sort_method (const char *opt, char **err_opt); 47 | extern const char *get_sort_methods (void); 48 | 49 | -------------------------------------------------------------------------------- /src/tests.h: -------------------------------------------------------------------------------- 1 | /** \file tests.h 2 | * \ingroup Misc 3 | */ 4 | #ifndef _TESTS_H 5 | #define _TESTS_H 6 | 7 | extern int do_tests (void); 8 | 9 | #if defined(USE_SQLITE3) 10 | extern void test_sqlite3 (void); /* In win_sqlite3.c */ 11 | #endif 12 | 13 | #endif /* _TESTS_H */ 14 | -------------------------------------------------------------------------------- /src/vcpkg.h: -------------------------------------------------------------------------------- 1 | /** \file vcpkg.h 2 | * \ingroup Misc 3 | */ 4 | #pragma once 5 | 6 | #include 7 | 8 | struct ver_info; 9 | struct ver_data; 10 | 11 | extern void vcpkg_init (void); 12 | extern void vcpkg_exit (void); 13 | extern bool vcpkg_get_info (char **exe_p, struct ver_info *ver); 14 | extern unsigned vcpkg_get_num_CONTROLS (void); 15 | extern unsigned vcpkg_get_num_JSON (void); 16 | extern unsigned vcpkg_get_num_portfile (void); 17 | extern unsigned vcpkg_get_num_installed (void); 18 | extern unsigned vcpkg_list_installed (bool detailed); 19 | extern unsigned vcpkg_find (const char *package_spec); 20 | extern bool vcpkg_get_only_installed (void); 21 | extern bool vcpkg_set_only_installed (bool True); 22 | extern const char *vcpkg_last_error (void); 23 | extern void vcpkg_clear_error (void); 24 | extern void vcpkg_extras (const struct ver_data *v, int pad_len); 25 | extern int vcpkg_json_parser_test (void); 26 | 27 | -------------------------------------------------------------------------------- /src/win_glob.h: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 1995-2003 DJ Delorie, see COPYING.DJ for details */ 2 | 3 | #ifndef __dj_include_glob_h_ 4 | #define __dj_include_glob_h_ 5 | 6 | #ifdef __cplusplus 7 | extern "C" { 8 | #endif 9 | 10 | #include 11 | 12 | #define GLOB_MARK 0x01 13 | #define GLOB_NOSORT 0x02 14 | #define GLOB_RECURSIVE 0x04 15 | #define GLOB_USE_EX 0x08 16 | 17 | #define GLOB_NOMATCH 1 18 | #define GLOB_NOSPACE 2 19 | 20 | typedef struct { 21 | size_t gl_pathc; 22 | char **gl_pathv; 23 | size_t gl_offs; 24 | } glob_t; 25 | 26 | int glob (const char *_pattern, int _flags, 27 | int (*_errfunc)(const char *_epath, int _eerrno), 28 | glob_t *_pglob); 29 | 30 | void globfree (glob_t *_pglob); 31 | 32 | /***********************************************************************************/ 33 | 34 | struct glob_new_entry; 35 | 36 | typedef struct { 37 | size_t gl_pathc; 38 | struct glob_new_entry *gl_pathv; 39 | } glob_new_t; 40 | 41 | int glob_new (const char *_dir, int _flags, 42 | int (*callback)(const char *path), 43 | glob_new_t *_pglob); 44 | 45 | void globfree_new (glob_new_t *_pglob); 46 | 47 | #ifdef __cplusplus 48 | } 49 | #endif 50 | 51 | #endif /* !__dj_include_glob_h_ */ 52 | -------------------------------------------------------------------------------- /src/win_sqlite3.c: -------------------------------------------------------------------------------- 1 | /** 2 | * \file win_sqlite3.c 3 | * \ingroup Misc 4 | * \brief Function for read/write to envtool Sqlite3 cache 5 | * 6 | * Currently called only from 'tests.c' (calls 'test_sqlite3()'). 7 | * It is meant to replace or enhance functions in 'cache.c' later. 8 | */ 9 | #include "envtool.h" 10 | #include "color.h" 11 | 12 | #if defined(USE_SQLITE3) /* rest of file */ 13 | 14 | #define SQLITE_API __declspec(dllimport) 15 | #define SQLITE_APICALL __stdcall 16 | #define SQLITE_CALLBACK __stdcall 17 | 18 | #if defined(HAVE_WINSQLITE3_H) 19 | #if defined(_MSC_VER) 20 | #include 21 | #else 22 | #include 23 | #endif 24 | #undef sqlite3_open 25 | #endif 26 | 27 | #if !defined(HAVE_WINSQLITE3_H) || (_WIN32_WINNT < 0x0A00) 28 | typedef struct sqlite3 sqlite3; 29 | typedef struct sqlite3_vfs sqlite3_vfs; 30 | typedef int (SQLITE_CALLBACK* sqlite3_callback) (void *cb_arg, int argc, char **argv, char **col_name); 31 | #endif 32 | 33 | #if defined(HAVE_WINSQLITE3_H) && (_WIN32_WINNT >= 0x0A00) 34 | #define HAVE_SQLITE3_VFS 35 | #endif 36 | 37 | #ifndef SQLITE_OPEN_READWRITE 38 | #define SQLITE_OPEN_READWRITE 2 39 | #endif 40 | 41 | #ifndef SQLITE_OPEN_CREATE 42 | #define SQLITE_OPEN_CREATE 4 43 | #endif 44 | 45 | #ifndef SQLITE_OK 46 | #define SQLITE_OK 0 47 | #endif 48 | 49 | #define SQLITE_DLL_NAME "WinSqlite3.dll" 50 | 51 | static const char *db_name = "test.db"; 52 | 53 | static const char *exec_stmt[] = { 54 | "CREATE TABLE IF NOT EXISTS tbl (column INTEGER, message TEXT, value INTEGER);", 55 | "INSERT OR REPLACE INTO tbl VALUES (1, 'hello!', 10);", 56 | "INSERT OR REPLACE INTO tbl VALUES (2, 'another string', 20);", 57 | "INSERT OR REPLACE INTO tbl VALUES (3, 'goodbye', 30);", 58 | "SELECT * FROM tbl WHERE column >= 2;", 59 | "SELECT message FROM tbl WHERE message = 'hello!';" 60 | }; 61 | 62 | static HANDLE dll_hnd = NULL; 63 | 64 | /* 65 | * Similar to 'envtool_py.c' 66 | */ 67 | #define DEF_FUNC(ret, f, args) \ 68 | typedef ret (SQLITE_APICALL *func_##f) args ; \ 69 | static func_##f p_##f = NULL 70 | 71 | #define LOAD_FUNC(is_opt, f) \ 72 | do { \ 73 | p_##f = GETPROCADDRESS (func_##f, dll_hnd, #f); \ 74 | if (!p_##f && !is_opt) \ 75 | { \ 76 | WARN (" Failed to find '%s()' in %s.\n", \ 77 | #f, SQLITE_DLL_NAME); \ 78 | return (false); \ 79 | } \ 80 | TRACE (2, "Function %s(): %*s 0x%p.\n", \ 81 | #f, 23-(int)strlen(#f), "", p_##f); \ 82 | } while (0) 83 | 84 | 85 | DEF_FUNC (int, sqlite3_open, (const char *filename, sqlite3 **p_db)); 86 | DEF_FUNC (int, sqlite3_open_v2, (const char *filename, sqlite3 **p_db, 87 | int flags, const char *vfs)); 88 | 89 | DEF_FUNC (int, sqlite3_free, (void *data)); 90 | DEF_FUNC (int, sqlite3_close, (sqlite3 *db)); 91 | DEF_FUNC (const char*, sqlite3_errmsg, (sqlite3 *db)); 92 | DEF_FUNC (int, sqlite3_extended_result_codes, (sqlite3*, int onoff)); 93 | DEF_FUNC (const char*, sqlite3_libversion, (void)); 94 | DEF_FUNC (const char*, sqlite3_sourceid, (void)); 95 | DEF_FUNC (sqlite3_vfs*, sqlite3_vfs_find, (const char *vfs_name)); 96 | DEF_FUNC (int, sqlite3_exec, (sqlite3 *db, const char *statement, 97 | sqlite3_callback cb, void *cb_arg, 98 | char **p_err_msg)); 99 | 100 | static bool sql3_load (void) 101 | { 102 | dll_hnd = LoadLibrary (SQLITE_DLL_NAME); 103 | if (!dll_hnd) 104 | { 105 | WARN (" Failed to load %s; %s\n", SQLITE_DLL_NAME, win_strerror(GetLastError())); 106 | return (false); 107 | } 108 | 109 | LOAD_FUNC (0, sqlite3_open); 110 | LOAD_FUNC (1, sqlite3_open_v2); 111 | LOAD_FUNC (0, sqlite3_exec); 112 | LOAD_FUNC (0, sqlite3_free); 113 | LOAD_FUNC (0, sqlite3_close); 114 | LOAD_FUNC (0, sqlite3_errmsg); 115 | LOAD_FUNC (1, sqlite3_extended_result_codes); 116 | LOAD_FUNC (0, sqlite3_libversion); 117 | LOAD_FUNC (0, sqlite3_sourceid); 118 | LOAD_FUNC (0, sqlite3_vfs_find); 119 | 120 | return (true); 121 | } 122 | 123 | static bool sql3_unload (void) 124 | { 125 | if (dll_hnd) 126 | FreeLibrary (dll_hnd); 127 | dll_hnd = NULL; 128 | return (true); 129 | } 130 | 131 | static int SQLITE_CALLBACK sql3_callback (void *cb_arg, int argc, char **argv, char **col_name) 132 | { 133 | int i; 134 | 135 | C_printf (" ~3%s(): exec-number: %d~0 (argc: %d)\n", 136 | __FUNCTION__, (int)(intptr_t)cb_arg, argc); 137 | 138 | for (i = 0; i < argc; i++) 139 | C_printf (" ~6%-20s ~2%s.~0\n", 140 | col_name[i], argv[i] ? argv[i] : "NULL"); 141 | 142 | C_putc ('\n'); 143 | return (0); 144 | } 145 | 146 | static void test_sqlite3_vfs (void) 147 | { 148 | #ifdef HAVE_SQLITE3_VFS 149 | const struct sqlite3_vfs *vfs; 150 | int i; 151 | 152 | C_printf (" ~3%s():~0\n ", __FUNCTION__); 153 | 154 | vfs = (*p_sqlite3_vfs_find) (NULL); 155 | for (i = 0; vfs; i++, vfs = vfs->pNext) 156 | C_printf ("~6zName: '%s'~0, ", vfs->zName); 157 | 158 | if (i == 0) 159 | C_puts ("No Virtual File Systems."); 160 | C_puts ("\n\n"); 161 | 162 | #else 163 | C_printf (" ~3%s():~0\n%s\n\n", __FUNCTION__, "Virtual File Systems not available."); 164 | #endif 165 | } 166 | 167 | /* 168 | * Called from tests.c / 'do_tests()'. 169 | */ 170 | void test_sqlite3 (void) 171 | { 172 | struct sqlite3 *db = NULL; 173 | char *err_msg = NULL; 174 | int i, flags, rc; 175 | 176 | C_printf ("\n~3%s():~0\n", __FUNCTION__); 177 | 178 | /* Load the needed functions from the SQlite3 API. I.e. "WinSqlite3.dll". 179 | */ 180 | if (!sql3_load()) 181 | return; 182 | 183 | C_printf ("~2 sqlite3_libversion(): ~6%s~0\n" 184 | "~2 sqlite3_sourceid(): ~6%s~0.\n", 185 | (*p_sqlite3_libversion)(), (*p_sqlite3_sourceid)()); 186 | 187 | test_sqlite3_vfs(); 188 | 189 | /* Create a handle to the SQlite3 API 190 | */ 191 | if (p_sqlite3_open_v2) 192 | { 193 | flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE /* | SQLITE_OPEN_MEMORY */; 194 | rc = (*p_sqlite3_open_v2) (db_name, &db, flags, NULL); 195 | TRACE (1, "sqlite3_open_v2(): rc: %d\n", rc); 196 | } 197 | else 198 | { 199 | rc = (*p_sqlite3_open) (db_name, &db); 200 | TRACE (1, "sqlite3_open(): rc: %d\n", rc); 201 | } 202 | 203 | if (db && p_sqlite3_extended_result_codes) 204 | (*p_sqlite3_extended_result_codes) (db, 1); 205 | 206 | if (rc != SQLITE_OK) 207 | { 208 | WARN (" Can't open database: rc: %d, %s\n", rc, (*p_sqlite3_errmsg)(db)); 209 | (*p_sqlite3_close) (db); 210 | return; 211 | } 212 | 213 | for (i = 0; i < DIM(exec_stmt); i++) 214 | { 215 | rc = (*p_sqlite3_exec) (db, exec_stmt[i], 216 | sql3_callback, (void*)(intptr_t)i, &err_msg); 217 | if (rc != SQLITE_OK) 218 | { 219 | C_printf (" ~6%d: ~5SQL error:~0 rc: %d, %s\n", i, rc, err_msg); 220 | (*p_sqlite3_free) (err_msg); 221 | break; 222 | } 223 | } 224 | 225 | if (rc == SQLITE_OK && i == DIM(exec_stmt)) 226 | C_printf (" Successfully created ~6%s~0 and executed all SQL statements.\n", db_name); 227 | 228 | (*p_sqlite3_close) (db); 229 | sql3_unload(); 230 | } 231 | #endif /* USE_SQLITE3 */ 232 | 233 | --------------------------------------------------------------------------------