├── Changes.txt ├── LICENSE ├── README.md ├── res ├── cli.rc ├── es.Manifest └── resource.h ├── src ├── Everything3.h ├── array.c ├── array.h ├── column.c ├── column.h ├── column_color.c ├── column_color.h ├── column_width.c ├── column_width.h ├── config.c ├── config.h ├── debug.c ├── debug.h ├── es.c ├── es.h ├── everything_ipc.h ├── filelist.c ├── filelist.h ├── ipc3.c ├── ipc3.h ├── mem.c ├── mem.h ├── os.c ├── os.h ├── pool.c ├── pool.h ├── property.c ├── property.h ├── property_name_to_id_macro.h ├── property_old_column_macro.h ├── property_unknown.c ├── property_unknown.h ├── safe_int.c ├── safe_int.h ├── safe_size.c ├── safe_size.h ├── secondary_sort.c ├── secondary_sort.h ├── unicode.c ├── unicode.h ├── utf8_buf.c ├── utf8_buf.h ├── utf8_string.c ├── utf8_string.h ├── version.h ├── wchar_buf.c ├── wchar_buf.h ├── wchar_string.c └── wchar_string.h ├── vs2005 ├── cli.sln └── cli.vcproj └── vs2019 ├── cli.sln └── cli.vcxproj /Changes.txt: -------------------------------------------------------------------------------- 1 | Saturday 11 October 2025: Version 1.1.0.34 2 | fixed an issue with output being truncated when highlighting and redirecting output to a file. 3 | improved connecting to the ipc3 pipe server when it is busy. 4 | added aspect ratio formatting like Everything. -added -aspect-ratio-format 5 | added a debug mode. -debug 6 | reverted show help when there's no arguments. 7 | 8 | Friday 26 September 2025: Version 1.1.0.33 9 | cleaned up /a dir style commands, had P for sparse, which no longer exists. 10 | show help when there's no arguments, no export type and output to console. -added -no-help-on-no-args option. 11 | run count 0xffffffff is a valid value and should be displayed. 12 | fixed an issue with DIR /ah style attributes. 13 | fixed a long timeout delay when using Everything 1.4. 14 | fixed an issue with -timeout not waiting for the db to load. 15 | 16 | Wednesday 10 September 2025: Version 1.1.0.32a 17 | fixed an issue with a lowercase [es] section not working in es.ini 18 | fixed an issue with background colors carrying over to the next line. 19 | fixed an issue with --width not working. 20 | fixed an issue with -pause when there is no results. 21 | improved -date-format and removed -export-date-format 22 | added -no-new-line switch 23 | 24 | Friday 5 September 2025: Version 1.1.0.31a 25 | added Everything 1.5 support 26 | added Journal support 27 | added ARM/ARM64 support 28 | 29 | Monday 2 June 2025: Version 1.1.0.30 30 | added new code signing certificate: voidtools PTY LTD 31 | 32 | Saturday 30 March 2024: Version 1.1.0.29 33 | added CSV/TSV -double-quote 34 | added -search 35 | added -search* 36 | 37 | Saturday 16 March 2024: Version 1.1.0.28 38 | treat single - and double -- as literal (not as a switch) 39 | 40 | Wednesday 10 January 2024: Version 1.1.0.27 41 | improved WriteFile performance. (added es_buf_t) -added /cp 65001 42 | 43 | Monday 26 September 2022: Version 1.1.0.26 44 | fixed an issue with exporting UTF-16 surrogate pairs. 45 | 46 | Monday 26 September 2022: Version 1.1.0.25 47 | added -exit 48 | added -reindex 49 | added -utf8-bom 50 | 51 | Thursday 11 August 2022: Version 1.1.0.24 52 | fixed an issue with converting ini utf-8 to wchar text. 53 | 54 | Wednesday 12 January 2022: Version 1.1.0.23 55 | fixed an issue with instance name not being initialized. 56 | 57 | Wednesday 12 January 2022: Version 1.1.0.22 58 | fixed an issue with WideCharToMultiByte using the wrong buffer size. 59 | 60 | Tueday 30 November 2021: Version 1.1.0.21 61 | added TSV support. 62 | 63 | Tueday 12 October 2021: Version 1.1.0.20 64 | fix ALT in pause mode causing page down. 65 | 66 | Monday 4 May 2020: Version 1.1.0.19 67 | added -inc-run-count 68 | added -set-run-count 69 | added -instance 70 | 71 | Wednesday 22 January 2020: Version 1.1.0.18 72 | added -get-total-size 73 | 74 | Wednesday 22 January 2020: Version 1.1.0.17 75 | added -no-result-error 76 | 77 | Wednesday 22 January 2020: Version 1.1.0.16 78 | added -no-header 79 | added -double-quote 80 | added -version 81 | added -get-everything-version 82 | 83 | Wednesday 22 January 2020: Version 1.1.0.15 84 | updated help (thanks to NotNull) 85 | 86 | Wednesday 22 January 2020: Version 1.1.0.14 87 | updated help (thanks to NotNull) 88 | fixed -? -h -help errors 89 | 90 | Wednesday 22 January 2020: Version 1.1.0.13 91 | added -ipc1 -ipc2 92 | added output errors to std_error 93 | 94 | Friday 21 June 2019: Version 1.1.0.12 95 | added -get-result-count 96 | 97 | Version 1.1.0.11 98 | added -date-format 99 | 100 | Version 1.1.0.10 101 | -r now takes a parameter 102 | Everything handles escaping quotes. 103 | 104 | Version 1.1.0.9 105 | fixed an issue with checking if the database is loaded 106 | exporting as efu only exports indexed information now 107 | use -size, -date-modified, -date-created or -attributes to override 108 | folder size is also exported now. 109 | 110 | Version 1.1.0.8 111 | removed -highligh-name, -highligh-path and -highligh-full-path-name. 112 | fixed date widths, switched to ini settings. 113 | 114 | Version 1.1.0.7 115 | Everything 1.4 release. 116 | 117 | Version 1.1.0.6a 118 | added -csv, -efu, -txt, -m3u and -m3u8 to set the display format (no exporting) 119 | added -export-m3u 120 | anded -export-m3u8 121 | 122 | Version 1.1.0.5a 123 | /a[RASH] attributes added 124 | added -more/-pause ignored for no results 125 | added -sort-size 126 | added -sort-size-ascending 127 | added -sort-size-descending 128 | 129 | Version 1.1.0.4a 130 | -pause improvements 131 | added -set-run-count, -inc-run-count, -get-run-count 132 | allow -name -size -date to re-order columns even when they are specified in settings 133 | added -more ENTER=show one line, Q=Quit, ESC=Escape., column alignment improvements. 134 | 135 | Version 1.1.0.3a 136 | added error levels 137 | added -timeout 138 | 139 | Version 1.1.0.2a 140 | added -pause 141 | added -more 142 | added -hide-empty-search-results 143 | added -empty-search-help 144 | added -save-settings 145 | added -clear-settings 146 | added -path 147 | added -parent-path 148 | added -parent 149 | added -no-*switches* 150 | added localized dates 151 | 152 | Version 1.1.0.1a 153 | Everything 1.4.1 support for size, dates etc. 154 | 155 | Version 1.0.0.4 156 | added ChangeWindowMessageFilterEx (if available) for admin/user support. 157 | 158 | Version 1.0.0.3 159 | removed es_write console because it has no support for piping. 160 | 161 | Version 1.0.0.2 162 | convert unicode to same code page as console. 163 | 164 | Version 1.0.0.1 165 | fixed command line interpreting '-' as a switch inside text. 166 | 167 | Version 1.0.0.0 168 | initial release 169 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 voidtools 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ES 2 | The Command Line Interface for Everything. 3 | 4 | [Download](#download)
5 | [Install Guide](#Install-Guide)
6 | [Usage](#Usage)
7 | [Search Options](#Search-Options)
8 | [Sort Options](#Sort-Options)
9 | [Display Options](#Display-Options)
10 | [Export Options](#Export-Options)
11 | [General Options](#General-Options)
12 | [Search Syntax](#Search-Syntax)
13 | [Notes](#Notes)
14 | [Error Levels](#Error-Levels)
15 | [See Also](#See-Also)
16 |


17 | 18 | 19 | 20 | Download 21 | -------- 22 | 23 | https://github.com/voidtools/ES/releases 24 | 25 | https://www.voidtools.com/downloads#cli 26 |


27 | 28 | 29 | 30 | ![image](https://github.com/user-attachments/assets/0fcbe74a-c24c-4065-8a14-85757d525212) 31 |


32 | 33 | 34 | 35 | Install Guide 36 | ------------- 37 | 38 | To install ES: 39 | * [Download](#download) ES and extract es.exe to:
40 | %LOCALAPPDATA%\Microsoft\WindowsApps 41 | 42 | This location is set in the PATH environment variable and allows es to be called from any directory in a command prompt or powershell. 43 |


44 | 45 | 46 | 47 | Usage 48 | ----- 49 | 50 | es.exe [options] search text 51 | 52 | For example: 53 | 54 | es everything ext:exe;ini 55 |


56 | 57 | 58 | 59 | Search Options 60 | -------------- 61 | 62 |
63 |
-r, -regex
64 |
Search using regular expressions.
65 |
-i, -case
66 |
Match case.
67 |
-w, -ww, -whole-word, -whole-words
68 |
Match whole words.
69 |
-p, -match-path
70 |
Match full path and file name.
71 |
-a, -diacritics
72 |
Match diacritical marks.
73 |
74 |
-o <offset>, -offset <offset>
75 |
Show results starting from offset.
76 |
-n <num>, -max-results <num>
77 |
Limit the number of results shown to <num>.
78 |
79 |
-path <path>
80 |
Search for subfolders and files in path.
81 |
-parent-path <path>
82 |
Search for subfolders and files in the parent of path.
83 |
-parent <path>
84 |
Search for files with the specified parent path.
85 |
86 |
/ad
87 |
Folders only.
88 |
/a-d
89 |
Files only.
90 |
/a[RHSDAVNTPLCOIE]
91 |
DIR style attributes search.
92 | R = Read only.
93 | H = Hidden.
94 | S = System.
95 | D = Directory.
96 | A = Archive.
97 | V = Device.
98 | N = Normal.
99 | T = Temporary.
100 | P = Sparse file.
101 | L = Reparse point.
102 | C = Compressed.
103 | O = Offline.
104 | I = Not content indexed.
105 | E = Encrypted.
106 | - = Prefix a flag with - to exclude.
107 |
108 |


109 | 110 | 111 | 112 | Sort Options 113 | ------------ 114 | 115 |
116 |
-s
117 |
sort by full path.
118 |
-sort <name[-ascending|-descending]>, -sort-<name>[-ascending|-descending]
119 |
Set sort
120 | name=name|path|size|extension|date-created|date-modified|date-accessed|
121 | attributes|file-list-file-name|run-count|date-recently-changed|date-run
122 |
-sort-ascending, -sort-descending
123 |
Set sort order
124 |
125 |
/on, /o-n, /os, /o-s, /oe, /o-e, /od, /o-d
126 |
DIR style sorts.
127 | N = Name.
128 | S = Size.
129 | E = Extension.
130 | D = Date modified.
131 | - = Sort in descending order.
132 |
133 |


134 | 135 | 136 | 137 | Display Options 138 | --------------- 139 | 140 |
141 |
-name
142 | -path-column
143 | -full-path-and-name, -filename-column
144 | -extension, -ext
145 | -size
146 | -date-created, -dc
147 | -date-modified, -dm
148 | -date-accessed, -da
149 | -attributes, -attribs, -attrib
150 | -file-list-file-name
151 | -run-count
152 | -date-run
153 | -date-recently-changed, -rc
154 |
Show the specified column.
155 |
156 |
-highlight
157 |
Highlight results.
158 |
-highlight-color <color>
159 |
Highlight color 0x00-0xFF:
160 |
161 |
162 |
-csv
163 | -efu
164 | -txt
165 | -m3u
166 | -m3u8
167 | -tsv
168 |
Change display format.
169 |
170 |
-size-format <format>
171 |
0=auto, 1=Bytes, 2=KB, 3=MB.
172 |
-date-format <format>
173 |
0=auto, 1=ISO-8601, 2=FILETIME, 3=ISO-8601(UTC)
174 |
175 |
-filename-color <color>
176 | -name-color <color>
177 | -path-color <color>
178 | -extension-color <color>
179 | -size-color <color>
180 | -date-created-color <color>, -dc-color <color>
181 | -date-modified-color <color>, -dm-color <color>
182 | -date-accessed-color <color>, -da-color <color>
183 | -attributes-color <color>
184 | -file-list-filename-color <color>
185 | -run-count-color <color>
186 | -date-run-color <color>
187 | -date-recently-changed-color <color>, -rc-color <color>
188 |
Set the column color 0x00-0xFF:
189 |
190 |
191 |
-filename-width <width>
192 | -name-width <width>
193 | -path-width <width>
194 | -extension-width <width>
195 | -size-width <width>
196 | -date-created-width <width>, -dc-width <width>
197 | -date-modified-width <width>, -dm-width <width>
198 | -date-accessed-width <width>, -da-width <width>
199 | -attributes-width <width>
200 | -file-list-filename-width <width>
201 | -run-count-width <width>
202 | -date-run-width <width>
203 | -date-recently-changed-width <width>, -rc-width <width>
204 |
Set the column width 0-200.
205 |
206 |
-no-digit-grouping
207 |
Don't group numbers with commas.
208 |
-size-leading-zero
209 | -run-count-leading-zero
210 |
Format the number with leading zeros, use with -no-digit-grouping.
211 |
-double-quote
212 |
Wrap paths and filenames with double quotes.
213 |
214 |


215 | 216 | 217 | 218 | Export Options 219 | -------------- 220 | 221 |
222 |
-export-csv <out.csv>
223 | -export-efu <out.efu>
224 | -export-txt <out.txt>
225 | -export-m3u <out.m3u>
226 | -export-m3u8 <out.m3u8>
227 | -export-tsv <out.txt>
228 |
Export to a file using the specified layout.
229 |
-no-header
230 |
Do not output a column header for CSV, EFU and TSV files.
231 |
-utf8-bom
232 |
Store a UTF-8 byte order mark at the start of the exported file.
233 |
234 |


235 | 236 | 237 | 238 | General Options 239 | --------------- 240 | 241 |
242 |
-h, -help
243 |
Display this help.
244 |
245 |
-instance <name>
246 |
Connect to the unique Everything instance name.
247 |
-ipc1, -ipc2
248 |
Use IPC version 1 or 2.
249 |
-pause, -more
250 |
Pause after each page of output.
251 |
-hide-empty-search-results
252 |
Don't show any results when there is no search.
253 |
-empty-search-help
254 |
Show help when no search is specified.
255 |
-timeout <milliseconds>
256 |
Timeout after the specified number of milliseconds to wait for
257 | the Everything database to load before sending a query.
258 |
259 |
-set-run-count <filename> <count>
260 |
Set the run count for the specified filename.
261 |
-inc-run-count <filename>
262 |
Increment the run count for the specified filename by one.
263 |
-get-run-count <filename>
264 |
Display the run count for the specified filename.
265 |
-get-result-count
266 |
Display the result count for the specified search.
267 |
-get-total-size
268 |
Display the total result size for the specified search.
269 |
-save-settings, -clear-settings
270 |
Save or clear settings.
271 |
-version
272 |
Display ES major.minor.revision.build version and exit.
273 |
-get-everything-version
274 |
Display Everything major.minor.revision.build version and exit.
275 |
-exit
276 |
Exit Everything.
277 | Returns after Everything process closes.
278 |
-save-db
279 |
Save the Everything database to disk.
280 | Returns after saving completes.
281 |
-reindex
282 |
Force Everything to reindex.
283 | Returns after indexing completes.
284 |
-no-result-error
285 |
Set the error level if no results are found.
286 |
287 |


288 | 289 | 290 | 291 | Search Syntax 292 | ------------- 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 |
spaceAND
|OR
!NOT
< >Grouping
" "Escape operator characters.
301 |


302 | 303 | 304 | 305 | Notes 306 | ----- 307 | 308 | Internal -'s in options can be omitted, eg: -nodigitgrouping 309 | 310 | Switches can also start with a / 311 | 312 | Use double quotes to escape spaces and switches. 313 | 314 | Switches can be disabled by prefixing them with no-, eg: -no-size 315 | 316 | Use a ^ prefix or wrap with double quotes (") to escape \ & | > < ^ in a command prompt. 317 |


318 | 319 | 320 | 321 | Error Levels 322 | ------------ 323 | 324 | Return codes from ES: 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 |
0No known error, search successful.
1Failed to register window class
2Failed to create listening window.
3Out of memory
4Expected an additional command line option with the specified switch
5Failed to create export output file
6Unknown switch.
7Failed to send Everything IPC a query.
8No Everything IPC window - make sure the Everything search client is running.
9No results found when used with -no-result-error
337 |


338 | 339 | 340 | 341 | See Also 342 | -------- 343 | 344 | * https://www.voidtools.com/support/everything/command_line_interface/ 345 | * https://www.voidtools.com/downloads#cli 346 | * https://www.voidtools.com/forum/viewtopic.php?t=5762 347 | -------------------------------------------------------------------------------- /res/cli.rc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voidtools/ES/7992057aa883a67585afe8b6dc34f3ef18ba5f92/res/cli.rc -------------------------------------------------------------------------------- /res/es.Manifest: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /res/resource.h: -------------------------------------------------------------------------------- 1 | //{{NO_DEPENDENCIES}} 2 | // Microsoft Visual C++ generated include file. 3 | // Used by cli.rc 4 | 5 | // Next default values for new objects 6 | // 7 | #ifdef APSTUDIO_INVOKED 8 | #ifndef APSTUDIO_READONLY_SYMBOLS 9 | #define _APS_NEXT_RESOURCE_VALUE 101 10 | #define _APS_NEXT_COMMAND_VALUE 40001 11 | #define _APS_NEXT_CONTROL_VALUE 1001 12 | #define _APS_NEXT_SYMED_VALUE 101 13 | #endif 14 | #endif 15 | -------------------------------------------------------------------------------- /src/array.c: -------------------------------------------------------------------------------- 1 | 2 | // 3 | // Copyright (C) 2025 voidtools / David Carpenter 4 | // 5 | // Permission is hereby granted, free of charge, 6 | // to any person obtaining a copy of this software 7 | // and associated documentation files (the "Software"), 8 | // to deal in the Software without restriction, 9 | // including without limitation the rights to use, 10 | // copy, modify, merge, publish, distribute, sublicense, 11 | // and/or sell copies of the Software, and to permit 12 | // persons to whom the Software is furnished to do so, 13 | // subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be 16 | // included in all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 20 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 21 | // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 22 | // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 23 | // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 24 | // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | // 26 | // simple arrays 27 | 28 | #include "es.h" 29 | 30 | // initialize an array. 31 | // The initialized array will be empty. 32 | void array_init(array_t *a) 33 | { 34 | a->indexes = (void **)a->stack_buf; 35 | a->count = 0; 36 | a->allocated = ARRAY_STACK_SIZE / sizeof(void *); 37 | } 38 | 39 | // kill an initialized array 40 | // Any allocated memory is returned to the system. 41 | void array_kill(array_t *a) 42 | { 43 | if (a->indexes != (void **)a->stack_buf) 44 | { 45 | mem_free(a->indexes); 46 | } 47 | } 48 | 49 | // empty an array. 50 | // Any allocated memory is returned to the system. 51 | void array_empty(array_t *a) 52 | { 53 | array_kill(a); 54 | array_init(a); 55 | } 56 | 57 | // binary search for an item in a sorted array. 58 | // returns a pointer to the found item. 59 | // returns NULL if the item is not found and stores the insertion_index for a new item in out_insertion_index. 60 | // call array_insert with this index to add the new item. 61 | void *array_find_or_get_insertion_index(const array_t *a,int (*compare_proc)(const void *a,const void *b),const void *compare_data,SIZE_T *out_insertion_index) 62 | { 63 | SIZE_T blo; 64 | SIZE_T bhi; 65 | SIZE_T best_insertion_index; 66 | void **array_base; 67 | 68 | // search for name insertion point. 69 | blo = 0; 70 | bhi = a->count; 71 | best_insertion_index = 0; 72 | array_base = a->indexes; 73 | 74 | while(blo < bhi) 75 | { 76 | SIZE_T bpos; 77 | int i; 78 | 79 | bpos = blo + ((bhi - blo) / 2); 80 | 81 | i = compare_proc(array_base[bpos],compare_data); 82 | 83 | if (i > 0) 84 | { 85 | bhi = bpos; 86 | } 87 | else 88 | if (!i) 89 | { 90 | // already in the list! 91 | return array_base[bpos]; 92 | } 93 | else 94 | { 95 | best_insertion_index = bpos + 1; 96 | blo = bpos + 1; 97 | } 98 | } 99 | 100 | *out_insertion_index = best_insertion_index; 101 | 102 | return NULL; 103 | } 104 | 105 | // returns a pointer to the found item. 106 | // returns NULL if not found. 107 | void *array_find(const array_t *a,int (*compare_proc)(const void *a,const void *b),const void *compare_data) 108 | { 109 | SIZE_T blo; 110 | SIZE_T bhi; 111 | void **array_base; 112 | 113 | // search for insertion point. 114 | blo = 0; 115 | bhi = a->count; 116 | array_base = a->indexes; 117 | 118 | while(blo < bhi) 119 | { 120 | SIZE_T bpos; 121 | int i; 122 | 123 | bpos = blo + ((bhi - blo) / 2); 124 | 125 | i = compare_proc(array_base[bpos],compare_data); 126 | 127 | if (i > 0) 128 | { 129 | bhi = bpos; 130 | } 131 | else 132 | if (!i) 133 | { 134 | // already in the list! 135 | return array_base[bpos]; 136 | } 137 | else 138 | { 139 | blo = bpos + 1; 140 | } 141 | } 142 | 143 | return NULL; 144 | } 145 | 146 | // insert the specified item at the specified index. 147 | // call array_find_or_get_insertion_index to get the insertion position. 148 | // insertion_index can be SIZE_MAX to add to the end. 149 | void array_insert(array_t *a,SIZE_T insertion_index,void *item) 150 | { 151 | SIZE_T new_count; 152 | SIZE_T validated_insertion_index; 153 | 154 | validated_insertion_index = insertion_index; 155 | if (validated_insertion_index > a->count) 156 | { 157 | validated_insertion_index = a->count; 158 | } 159 | 160 | new_count = safe_size_add(a->count,1); 161 | 162 | if (new_count <= a->allocated) 163 | { 164 | // make a hole in the array. 165 | os_move_memory(a->indexes + validated_insertion_index + 1,a->indexes + validated_insertion_index,(a->count - validated_insertion_index) * sizeof(void *)); 166 | 167 | a->indexes[validated_insertion_index] = item; 168 | } 169 | else 170 | { 171 | SIZE_T new_allocated; 172 | void **new_indexes; 173 | 174 | new_allocated = safe_size_add(a->allocated,a->allocated); 175 | 176 | new_indexes = mem_alloc(safe_size_mul_sizeof_pointer(new_allocated)); 177 | 178 | os_copy_memory(new_indexes,a->indexes,validated_insertion_index * sizeof(void *)); 179 | 180 | new_indexes[validated_insertion_index] = item; 181 | 182 | os_copy_memory(new_indexes + validated_insertion_index + 1,a->indexes + validated_insertion_index,(a->count - validated_insertion_index) * sizeof(void *)); 183 | 184 | // free old 185 | if (a->indexes != (void **)a->stack_buf) 186 | { 187 | mem_free(a->indexes); 188 | } 189 | 190 | a->indexes = new_indexes; 191 | a->allocated = new_allocated; 192 | } 193 | 194 | a->count++; 195 | } 196 | 197 | // returns a pointer to the item if found and removes it from the array. 198 | // returns NULL if the item is not found. 199 | // caller should free the returned item. 200 | void *array_remove(array_t *a,int (*compare_proc)(const void *a,const void *b),const void *compare_data) 201 | { 202 | SIZE_T blo; 203 | SIZE_T bhi; 204 | void **array_base; 205 | 206 | // search for name insertion point. 207 | blo = 0; 208 | bhi = a->count; 209 | array_base = a->indexes; 210 | 211 | while(blo < bhi) 212 | { 213 | SIZE_T bpos; 214 | int i; 215 | 216 | bpos = blo + ((bhi - blo) / 2); 217 | 218 | i = compare_proc(array_base[bpos],compare_data); 219 | 220 | if (i > 0) 221 | { 222 | bhi = bpos; 223 | } 224 | else 225 | if (!i) 226 | { 227 | void *item; 228 | 229 | item = a->indexes[bpos]; 230 | 231 | os_move_memory(array_base + bpos,array_base + bpos + 1,(a->count - (bpos + 1)) * sizeof(void *)); 232 | a->count--; 233 | 234 | return item; 235 | } 236 | else 237 | { 238 | blo = bpos + 1; 239 | } 240 | } 241 | 242 | return NULL; 243 | } 244 | -------------------------------------------------------------------------------- /src/array.h: -------------------------------------------------------------------------------- 1 | 2 | // 3 | // Copyright (C) 2025 voidtools / David Carpenter 4 | // 5 | // Permission is hereby granted, free of charge, 6 | // to any person obtaining a copy of this software 7 | // and associated documentation files (the "Software"), 8 | // to deal in the Software without restriction, 9 | // including without limitation the rights to use, 10 | // copy, modify, merge, publish, distribute, sublicense, 11 | // and/or sell copies of the Software, and to permit 12 | // persons to whom the Software is furnished to do so, 13 | // subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be 16 | // included in all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 20 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 21 | // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 22 | // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 23 | // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 24 | // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | // 26 | 27 | #define ARRAY_STACK_SIZE 256 28 | 29 | // a simple array 30 | typedef struct array_s 31 | { 32 | // the base indexes. 33 | // sorted by compare_proc 34 | void **indexes; 35 | 36 | // the number of items in indexes. 37 | SIZE_T count; 38 | 39 | // the number of allocated items in indexes. 40 | // this will double in size each time the array grows. 41 | SIZE_T allocated; 42 | 43 | // ensure stack_buf is 16byte aligned. 44 | SIZE_T padding1; 45 | 46 | // small stack space for initial buffer to avoid allocation. 47 | BYTE stack_buf[ARRAY_STACK_SIZE]; 48 | 49 | }array_t; 50 | 51 | void array_init(array_t *a); 52 | void array_kill(array_t *a); 53 | void array_empty(array_t *a); 54 | void *array_find_or_get_insertion_index(const array_t *a,int (*compare_proc)(const void *a,const void *b),const void *compare_data,SIZE_T *out_insertion_index); 55 | void *array_find(const array_t *a,int (*compare_proc)(const void *a,const void *b),const void *compare_data); 56 | void array_insert(array_t *a,SIZE_T insertion_index,void *item); 57 | void *array_remove(array_t *a,int (*compare_proc)(const void *a,const void *b),const void *compare_data); 58 | -------------------------------------------------------------------------------- /src/column.c: -------------------------------------------------------------------------------- 1 | 2 | // 3 | // Copyright (C) 2025 voidtools / David Carpenter 4 | // 5 | // Permission is hereby granted, free of charge, 6 | // to any person obtaining a copy of this software 7 | // and associated documentation files (the "Software"), 8 | // to deal in the Software without restriction, 9 | // including without limitation the rights to use, 10 | // copy, modify, merge, publish, distribute, sublicense, 11 | // and/or sell copies of the Software, and to permit 12 | // persons to whom the Software is furnished to do so, 13 | // subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be 16 | // included in all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 20 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 21 | // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 22 | // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 23 | // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 24 | // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | // 26 | // Columns 27 | 28 | #include "es.h" 29 | 30 | // these must be set in main() 31 | pool_t *column_pool = NULL; // pool of column_t 32 | array_t *column_array = NULL; // array of column_t sorted by property ID. 33 | column_t *column_order_start = NULL; // column order 34 | column_t *column_order_last = NULL; 35 | 36 | // compare two columns, sorting by property ID. 37 | int column_compare(const column_t *a,const void *property_id) 38 | { 39 | if (a->property_id < (DWORD)(uintptr_t)property_id) 40 | { 41 | return -1; 42 | } 43 | 44 | if (a->property_id > (DWORD)(uintptr_t)property_id) 45 | { 46 | return 1; 47 | } 48 | 49 | return 0; 50 | } 51 | 52 | // returns the column from the specified property id. 53 | // returns NULL if not found. 54 | column_t *column_find(DWORD property_id) 55 | { 56 | return array_find(column_array,column_compare,(const void *)(uintptr_t)property_id); 57 | } 58 | 59 | // add the specified column to the end of the order list. 60 | // the column MUST NOT be in the order list. 61 | void column_insert_order_at_end(column_t *column) 62 | { 63 | if (column_order_start) 64 | { 65 | column_order_last->order_next = column; 66 | column->order_prev = column_order_last; 67 | } 68 | else 69 | { 70 | column_order_start = column; 71 | column->order_prev = NULL; 72 | } 73 | 74 | column->order_next = NULL; 75 | column_order_last = column; 76 | } 77 | 78 | // add the specified column to the start of the order list. 79 | // the column MUST NOT be in the order list. 80 | void column_insert_order_at_start(column_t *column) 81 | { 82 | column->order_prev = NULL; 83 | column->order_next = column_order_start; 84 | column_order_start = column; 85 | 86 | if (column->order_next) 87 | { 88 | column->order_next->order_prev = column; 89 | } 90 | else 91 | { 92 | column_order_last = column; 93 | } 94 | } 95 | 96 | // remove the specified column from the order list. 97 | // the column MUTS BE in the order list. 98 | void column_remove_order(column_t *column) 99 | { 100 | if (column_order_start == column) 101 | { 102 | column_order_start = column->order_next; 103 | } 104 | else 105 | { 106 | column->order_prev->order_next = column->order_next; 107 | } 108 | 109 | if (column_order_last == column) 110 | { 111 | column_order_last = column->order_prev; 112 | } 113 | else 114 | { 115 | column->order_next->order_prev = column->order_prev; 116 | } 117 | } 118 | 119 | // add a new column. 120 | // Does nothing if the column was already added. 121 | // returns a pointer to the added column or existing column. 122 | // The new column is added to the end of the order list. 123 | // or, the existing column is moved to the end of the order list. 124 | column_t *column_add(DWORD property_id) 125 | { 126 | column_t *column; 127 | SIZE_T insert_index; 128 | 129 | column = array_find_or_get_insertion_index(column_array,column_compare,(const void *)(uintptr_t)property_id,&insert_index); 130 | if (column) 131 | { 132 | column_remove_order(column); 133 | } 134 | else 135 | { 136 | column = pool_alloc(column_pool,sizeof(column_t)); 137 | 138 | column->property_id = property_id; 139 | 140 | array_insert(column_array,insert_index,column); 141 | } 142 | 143 | column_insert_order_at_end(column); 144 | 145 | return column; 146 | } 147 | 148 | // removes the specified column if it exists. 149 | void column_remove(DWORD property_id) 150 | { 151 | column_t *column; 152 | 153 | column = array_remove(column_array,column_compare,(const void *)(uintptr_t)property_id); 154 | if (column) 155 | { 156 | column_remove_order(column); 157 | } 158 | } 159 | 160 | // reset columns. 161 | void column_clear_all(void) 162 | { 163 | array_empty(column_array); 164 | pool_empty(column_pool); 165 | 166 | column_order_start = NULL; 167 | column_order_last = NULL; 168 | } 169 | 170 | // insert_after_column can be NULL to insert at the start. 171 | // if insert_after_column is non-NULL it MUST be in the column order list. 172 | // column MUST not be in the column order list. 173 | void column_insert_after(column_t *column,column_t *insert_after_column) 174 | { 175 | if (insert_after_column) 176 | { 177 | column->order_next = insert_after_column->order_next; 178 | column->order_prev = insert_after_column; 179 | insert_after_column->order_next = column; 180 | 181 | if (column->order_next) 182 | { 183 | column->order_next->order_prev = column; 184 | } 185 | else 186 | { 187 | column_order_last = column; 188 | } 189 | } 190 | else 191 | { 192 | column_insert_order_at_start(column); 193 | } 194 | } 195 | 196 | // move the specified column by property id to the start of the order list (if the column exists) 197 | void column_move_order_to_start(DWORD property_id) 198 | { 199 | column_t *column; 200 | 201 | column = column_find(property_id); 202 | if (column) 203 | { 204 | column_remove_order(column); 205 | column_insert_order_at_start(column); 206 | } 207 | } -------------------------------------------------------------------------------- /src/column.h: -------------------------------------------------------------------------------- 1 | 2 | // 3 | // Copyright (C) 2025 voidtools / David Carpenter 4 | // 5 | // Permission is hereby granted, free of charge, 6 | // to any person obtaining a copy of this software 7 | // and associated documentation files (the "Software"), 8 | // to deal in the Software without restriction, 9 | // including without limitation the rights to use, 10 | // copy, modify, merge, publish, distribute, sublicense, 11 | // and/or sell copies of the Software, and to permit 12 | // persons to whom the Software is furnished to do so, 13 | // subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be 16 | // included in all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 20 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 21 | // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 22 | // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 23 | // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 24 | // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | // 26 | 27 | typedef struct column_s 28 | { 29 | // order list. 30 | struct column_s *order_next; 31 | struct column_s *order_prev; 32 | 33 | // EVERYTHING3_PROPERTY_ID_* 34 | DWORD property_id; 35 | 36 | }column_t; 37 | 38 | int column_compare(const column_t *a,const void *property_id); 39 | column_t *column_find(DWORD property_id); 40 | void column_insert_order_at_end(column_t *column); 41 | void column_insert_order_at_start(column_t *column); 42 | void column_remove_order(column_t *column); 43 | column_t *column_add(DWORD property_id); 44 | void column_remove(DWORD property_id); 45 | void column_clear_all(void); 46 | void column_insert_after(column_t *column,column_t *insert_after_column); 47 | void column_move_order_to_start(DWORD property_id); 48 | 49 | extern pool_t *column_pool; // pool of column_t 50 | extern array_t *column_array; // array of column_t 51 | extern column_t *column_order_start; // column order 52 | extern column_t *column_order_last; 53 | -------------------------------------------------------------------------------- /src/column_color.c: -------------------------------------------------------------------------------- 1 | 2 | // 3 | // Copyright (C) 2025 voidtools / David Carpenter 4 | // 5 | // Permission is hereby granted, free of charge, 6 | // to any person obtaining a copy of this software 7 | // and associated documentation files (the "Software"), 8 | // to deal in the Software without restriction, 9 | // including without limitation the rights to use, 10 | // copy, modify, merge, publish, distribute, sublicense, 11 | // and/or sell copies of the Software, and to permit 12 | // persons to whom the Software is furnished to do so, 13 | // subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be 16 | // included in all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 20 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 21 | // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 22 | // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 23 | // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 24 | // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | // 26 | // column colors 27 | // we can set column colors for columns that are not shown. -useful for loading colors from the es.ini 28 | 29 | #include "es.h" 30 | 31 | // these must be set in main() 32 | pool_t *column_color_pool = NULL; // pool of column_color_t 33 | array_t *column_color_array = NULL; // array of column_color_t sorted by property id. 34 | 35 | // compare two column colors by property ID. 36 | int column_color_compare(const column_color_t *a,const void *property_id) 37 | { 38 | if (a->property_id < (DWORD)(uintptr_t)property_id) 39 | { 40 | return -1; 41 | } 42 | 43 | if (a->property_id > (DWORD)(uintptr_t)property_id) 44 | { 45 | return 1; 46 | } 47 | 48 | return 0; 49 | } 50 | 51 | // set a column color 52 | // adds a new column color or replaces the existing column color. 53 | void column_color_set(DWORD property_id,WORD color) 54 | { 55 | column_color_t *column_color; 56 | SIZE_T insert_index; 57 | 58 | column_color = array_find_or_get_insertion_index(column_color_array,column_color_compare,(const void *)(uintptr_t)property_id,&insert_index); 59 | if (column_color) 60 | { 61 | column_color->color = color; 62 | } 63 | else 64 | { 65 | column_color = pool_alloc(column_color_pool,sizeof(column_color_t)); 66 | 67 | column_color->property_id = property_id; 68 | column_color->color = color; 69 | 70 | array_insert(column_color_array,insert_index,column_color); 71 | } 72 | } 73 | 74 | // remove an column color (if it exists) 75 | // returns a pointer to the removed column color. 76 | // returns NULL if not found. 77 | column_color_t *column_color_remove(DWORD property_id) 78 | { 79 | return array_remove(column_color_array,column_color_compare,(const void *)(uintptr_t)property_id); 80 | } 81 | 82 | // returns a pointer to the found column color. 83 | // returns NULL if not found. 84 | column_color_t *column_color_find(DWORD property_id) 85 | { 86 | return array_find(column_color_array,column_color_compare,(const void *)(uintptr_t)property_id); 87 | } 88 | 89 | // reset column colors. 90 | void column_color_clear_all(void) 91 | { 92 | array_empty(column_color_array); 93 | pool_empty(column_color_pool); 94 | } -------------------------------------------------------------------------------- /src/column_color.h: -------------------------------------------------------------------------------- 1 | 2 | // 3 | // Copyright (C) 2025 voidtools / David Carpenter 4 | // 5 | // Permission is hereby granted, free of charge, 6 | // to any person obtaining a copy of this software 7 | // and associated documentation files (the "Software"), 8 | // to deal in the Software without restriction, 9 | // including without limitation the rights to use, 10 | // copy, modify, merge, publish, distribute, sublicense, 11 | // and/or sell copies of the Software, and to permit 12 | // persons to whom the Software is furnished to do so, 13 | // subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be 16 | // included in all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 20 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 21 | // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 22 | // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 23 | // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 24 | // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | // 26 | 27 | typedef struct es_column_color_s 28 | { 29 | // EVERYTHING3_PROPERTY_ID_* 30 | DWORD property_id; 31 | 32 | // the SetConsoleTextAttribute color 33 | WORD color; 34 | 35 | }column_color_t; 36 | 37 | int column_color_compare(const column_color_t *a,const void *property_id); 38 | void column_color_set(DWORD property_id,WORD color); 39 | column_color_t *column_color_remove(DWORD property_id); 40 | column_color_t *column_color_find(DWORD property_id); 41 | void column_color_clear_all(void); 42 | 43 | extern pool_t *column_color_pool; // pool of column_color_t 44 | extern array_t *column_color_array; // array of column_color_t 45 | -------------------------------------------------------------------------------- /src/column_width.c: -------------------------------------------------------------------------------- 1 | 2 | // 3 | // Copyright (C) 2025 voidtools / David Carpenter 4 | // 5 | // Permission is hereby granted, free of charge, 6 | // to any person obtaining a copy of this software 7 | // and associated documentation files (the "Software"), 8 | // to deal in the Software without restriction, 9 | // including without limitation the rights to use, 10 | // copy, modify, merge, publish, distribute, sublicense, 11 | // and/or sell copies of the Software, and to permit 12 | // persons to whom the Software is furnished to do so, 13 | // subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be 16 | // included in all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 20 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 21 | // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 22 | // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 23 | // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 24 | // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | // 26 | // column widths 27 | // we can set column widths for columns that are not shown. -useful for loading widths from the es.ini 28 | 29 | #include "es.h" 30 | 31 | static int _column_width_compare(const column_width_t *a,const void *property_id); 32 | 33 | // these must be set in main() 34 | pool_t *column_width_pool = NULL; // pool of column_width_t 35 | array_t *column_width_array = NULL; // array of column_width_t sorted by property ID. 36 | 37 | // compare two column widths by property ID. 38 | static int _column_width_compare(const column_width_t *a,const void *property_id) 39 | { 40 | if (a->property_id < (DWORD)(uintptr_t)property_id) 41 | { 42 | return -1; 43 | } 44 | 45 | if (a->property_id > (DWORD)(uintptr_t)property_id) 46 | { 47 | return 1; 48 | } 49 | 50 | return 0; 51 | } 52 | 53 | // set a column width 54 | // adds a new column widths or replaces the existing column width. 55 | void column_width_set(DWORD property_id,int width) 56 | { 57 | column_width_t *column_width; 58 | SIZE_T insert_index; 59 | int sane_width; 60 | 61 | sane_width = width; 62 | 63 | // sanity. 64 | if (sane_width < 0) 65 | { 66 | sane_width = 0; 67 | } 68 | 69 | if (sane_width > 65535) 70 | { 71 | sane_width = 65535; 72 | } 73 | 74 | column_width = array_find_or_get_insertion_index(column_width_array,_column_width_compare,(const void *)(uintptr_t)property_id,&insert_index); 75 | if (column_width) 76 | { 77 | column_width->width = sane_width; 78 | } 79 | else 80 | { 81 | column_width = pool_alloc(column_width_pool,sizeof(column_width_t)); 82 | 83 | column_width->property_id = property_id; 84 | column_width->width = sane_width; 85 | 86 | array_insert(column_width_array,insert_index,column_width); 87 | } 88 | } 89 | 90 | // remove an column width (if it exists) 91 | // returns a pointer to the removed column width. 92 | // returns NULL if not found. 93 | column_width_t *column_width_find(DWORD property_id) 94 | { 95 | return array_find(column_width_array,_column_width_compare,(const void *)(uintptr_t)property_id); 96 | } 97 | 98 | // returns a pointer to the found column width. 99 | // returns NULL if not found. 100 | column_width_t *column_width_remove(DWORD property_id) 101 | { 102 | return array_remove(column_width_array,_column_width_compare,(const void *)(uintptr_t)property_id); 103 | } 104 | 105 | // get the width of a column. 106 | // returns any defined width. 107 | // if no width is defined, returns the default width. 108 | int column_width_get(DWORD property_id) 109 | { 110 | column_width_t *column_width; 111 | 112 | column_width = column_width_find(property_id); 113 | 114 | if (column_width) 115 | { 116 | return column_width->width; 117 | } 118 | 119 | return property_get_default_width(property_id); 120 | } 121 | 122 | // reset column widths. 123 | void column_width_clear_all(void) 124 | { 125 | array_empty(column_width_array); 126 | pool_empty(column_width_pool); 127 | } 128 | 129 | -------------------------------------------------------------------------------- /src/column_width.h: -------------------------------------------------------------------------------- 1 | 2 | // 3 | // Copyright (C) 2025 voidtools / David Carpenter 4 | // 5 | // Permission is hereby granted, free of charge, 6 | // to any person obtaining a copy of this software 7 | // and associated documentation files (the "Software"), 8 | // to deal in the Software without restriction, 9 | // including without limitation the rights to use, 10 | // copy, modify, merge, publish, distribute, sublicense, 11 | // and/or sell copies of the Software, and to permit 12 | // persons to whom the Software is furnished to do so, 13 | // subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be 16 | // included in all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 20 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 21 | // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 22 | // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 23 | // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 24 | // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | // 26 | 27 | typedef struct es_column_width_s 28 | { 29 | // EVERYTHING3_PROPERTY_ID_* 30 | DWORD property_id; 31 | 32 | // the SetConsoleTextAttribute color 33 | int width; 34 | 35 | }column_width_t; 36 | 37 | void column_width_set(DWORD property_id,int width); 38 | column_width_t *column_width_find(DWORD property_id); 39 | column_width_t *column_width_remove(DWORD property_id); 40 | int column_width_get(DWORD property_id); 41 | void column_width_clear_all(void); 42 | 43 | extern pool_t *column_width_pool; // pool of column_width_t 44 | extern array_t *column_width_array; // array of column_width_t 45 | -------------------------------------------------------------------------------- /src/config.c: -------------------------------------------------------------------------------- 1 | 2 | // 3 | // Copyright (C) 2025 voidtools / David Carpenter 4 | // 5 | // Permission is hereby granted, free of charge, 6 | // to any person obtaining a copy of this software 7 | // and associated documentation files (the "Software"), 8 | // to deal in the Software without restriction, 9 | // including without limitation the rights to use, 10 | // copy, modify, merge, publish, distribute, sublicense, 11 | // and/or sell copies of the Software, and to permit 12 | // persons to whom the Software is furnished to do so, 13 | // subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be 16 | // included in all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 20 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 21 | // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 22 | // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 23 | // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 24 | // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | // 26 | // Configuration es.ini 27 | 28 | #include "es.h" 29 | 30 | // a keyvalue pair. 31 | typedef struct _config_keyvalue_s 32 | { 33 | // the next keyvalue pair 34 | // in reverse order (start == last line in es.ini) 35 | struct _config_keyvalue_s *next; 36 | 37 | // the key text 38 | const ES_UTF8 *key; 39 | 40 | // the value text. 41 | const ES_UTF8 *value; 42 | 43 | }_config_keyvalue_t; 44 | 45 | static BOOL _config_ini_get_line(config_ini_t *ini); 46 | static BOOL _config_ini_find_next_keyvalue(config_ini_t *ini); 47 | static _config_keyvalue_t *_config_keyvalue_find(config_ini_t *ini,const ES_UTF8 *key); 48 | 49 | // get the es.ini filename 50 | BOOL config_get_filename(int is_appdata,int is_temp,wchar_buf_t *wcbuf) 51 | { 52 | if (is_appdata) 53 | { 54 | // "%APPDATA%\\voidtools\\es" 55 | if (os_get_appdata_path(wcbuf)) 56 | { 57 | wchar_buf_cat_path_separator(wcbuf); 58 | 59 | wchar_buf_cat_utf8_string(wcbuf,"voidtools"); 60 | 61 | wchar_buf_cat_path_separator(wcbuf); 62 | 63 | wchar_buf_cat_utf8_string(wcbuf,"es"); 64 | 65 | wchar_buf_cat_path_separator(wcbuf); 66 | 67 | wchar_buf_cat_utf8_string(wcbuf,"es.ini"); 68 | 69 | if (is_temp) 70 | { 71 | wchar_buf_cat_utf8_string(wcbuf,".tmp"); 72 | } 73 | 74 | return TRUE; 75 | } 76 | } 77 | else 78 | { 79 | // same path as es.exe 80 | if (os_get_module_file_name(NULL,wcbuf)) 81 | { 82 | wchar_buf_remove_file_spec(wcbuf); 83 | 84 | wchar_buf_cat_path_separator(wcbuf); 85 | 86 | wchar_buf_cat_utf8_string(wcbuf,"es.ini"); 87 | 88 | if (is_temp) 89 | { 90 | wchar_buf_cat_utf8_string(wcbuf,".tmp"); 91 | } 92 | 93 | return TRUE; 94 | } 95 | } 96 | 97 | return FALSE; 98 | } 99 | 100 | // write out a key=int-value pair to the opened es.ini 101 | void config_write_int(HANDLE file_handle,const ES_UTF8 *name,int value) 102 | { 103 | utf8_buf_t cbuf; 104 | 105 | utf8_buf_init(&cbuf); 106 | 107 | utf8_buf_printf(&cbuf,"%s=%d\r\n",name,value); 108 | 109 | os_write_file_utf8_string_n(file_handle,cbuf.buf,cbuf.length_in_bytes); 110 | 111 | utf8_buf_kill(&cbuf); 112 | } 113 | 114 | // write out a key=int-value pair to the opened es.ini 115 | void config_write_empty(HANDLE file_handle,const ES_UTF8 *name) 116 | { 117 | utf8_buf_t cbuf; 118 | 119 | utf8_buf_init(&cbuf); 120 | 121 | utf8_buf_printf(&cbuf,"%s=\r\n",name); 122 | 123 | os_write_file_utf8_string_n(file_handle,cbuf.buf,cbuf.length_in_bytes); 124 | 125 | utf8_buf_kill(&cbuf); 126 | } 127 | 128 | // write out a key=dword-value pair to the opened es.ini 129 | void config_write_dword(HANDLE file_handle,const ES_UTF8 *name,DWORD value) 130 | { 131 | utf8_buf_t cbuf; 132 | 133 | utf8_buf_init(&cbuf); 134 | 135 | utf8_buf_printf(&cbuf,"%s=%u\r\n",name,value); 136 | 137 | os_write_file_utf8_string_n(file_handle,cbuf.buf,cbuf.length_in_bytes); 138 | 139 | utf8_buf_kill(&cbuf); 140 | } 141 | 142 | // write out a key=dword-value pair to the opened es.ini 143 | void config_write_uint64(HANDLE file_handle,const ES_UTF8 *name,ES_UINT64 value) 144 | { 145 | utf8_buf_t cbuf; 146 | 147 | utf8_buf_init(&cbuf); 148 | 149 | utf8_buf_printf(&cbuf,"%s=%I64u\r\n",name,value); 150 | 151 | os_write_file_utf8_string_n(file_handle,cbuf.buf,cbuf.length_in_bytes); 152 | 153 | utf8_buf_kill(&cbuf); 154 | } 155 | 156 | // write out a key=string-value pair to the opened es.ini 157 | void config_write_string(HANDLE file_handle,const ES_UTF8 *name,const wchar_t *value) 158 | { 159 | utf8_buf_t cbuf; 160 | 161 | utf8_buf_init(&cbuf); 162 | 163 | utf8_buf_copy_wchar_string(&cbuf,value); 164 | 165 | os_write_file_utf8_string(file_handle,name); 166 | os_write_file_utf8_string(file_handle,"="); 167 | os_write_file_utf8_string_n(file_handle,cbuf.buf,cbuf.length_in_bytes); 168 | os_write_file_utf8_string(file_handle,"\r\n"); 169 | 170 | utf8_buf_kill(&cbuf); 171 | } 172 | 173 | // read a string value with the specified name. 174 | // returns TRUE if found and stores the value in wcbuf. 175 | // Otherwise, returns FALSE if not found. 176 | BOOL config_read_string(config_ini_t *ini,const ES_UTF8 *name,wchar_buf_t *wcbuf) 177 | { 178 | _config_keyvalue_t *keyvalue; 179 | 180 | keyvalue = _config_keyvalue_find(ini,name); 181 | 182 | if (keyvalue) 183 | { 184 | wchar_buf_copy_utf8_string(wcbuf,keyvalue->value); 185 | 186 | return TRUE; 187 | } 188 | 189 | return FALSE; 190 | } 191 | 192 | // read an int-value with the specified name. 193 | // returns the int-value if found. 194 | // Otherwise, returns default_value 195 | int config_read_int(config_ini_t *ini,const ES_UTF8 *name,int default_value) 196 | { 197 | int ret; 198 | _config_keyvalue_t *keyvalue; 199 | 200 | ret = default_value; 201 | keyvalue = _config_keyvalue_find(ini,name); 202 | 203 | if (keyvalue) 204 | { 205 | if (*keyvalue->value) 206 | { 207 | wchar_buf_t wcbuf; 208 | 209 | wchar_buf_init(&wcbuf); 210 | 211 | wchar_buf_copy_utf8_string(&wcbuf,keyvalue->value); 212 | 213 | ret = wchar_string_to_int(wcbuf.buf); 214 | 215 | wchar_buf_kill(&wcbuf); 216 | } 217 | } 218 | 219 | return ret; 220 | } 221 | 222 | // read an dword-value with the specified name. 223 | // returns the dword-value if found. 224 | // Otherwise, returns default_value 225 | DWORD config_read_dword(config_ini_t *ini,const ES_UTF8 *name,DWORD default_value) 226 | { 227 | DWORD ret; 228 | _config_keyvalue_t *keyvalue; 229 | 230 | ret = default_value; 231 | keyvalue = _config_keyvalue_find(ini,name); 232 | 233 | if (keyvalue) 234 | { 235 | if (*keyvalue->value) 236 | { 237 | wchar_buf_t wcbuf; 238 | 239 | wchar_buf_init(&wcbuf); 240 | 241 | wchar_buf_copy_utf8_string(&wcbuf,keyvalue->value); 242 | 243 | ret = wchar_string_to_dword(wcbuf.buf); 244 | 245 | wchar_buf_kill(&wcbuf); 246 | } 247 | } 248 | 249 | return ret; 250 | } 251 | 252 | // read an uint64-value with the specified name. 253 | // returns the uint64-value if found. 254 | // Otherwise, returns default_value 255 | ES_UINT64 config_read_uint64(config_ini_t *ini,const ES_UTF8 *name,ES_UINT64 default_value) 256 | { 257 | ES_UINT64 ret; 258 | _config_keyvalue_t *keyvalue; 259 | 260 | ret = default_value; 261 | keyvalue = _config_keyvalue_find(ini,name); 262 | 263 | if (keyvalue) 264 | { 265 | if (*keyvalue->value) 266 | { 267 | wchar_buf_t wcbuf; 268 | 269 | wchar_buf_init(&wcbuf); 270 | 271 | wchar_buf_copy_utf8_string(&wcbuf,keyvalue->value); 272 | 273 | ret = wchar_string_to_uint64(wcbuf.buf); 274 | 275 | wchar_buf_kill(&wcbuf); 276 | } 277 | } 278 | 279 | return ret; 280 | } 281 | 282 | // read a line from the ini file. 283 | // stores the found key/value pair in ini. 284 | // ini->value will be set to NULL for comments, sections and if there's no = 285 | // returns TRUE if data is available. 286 | // returns FALSE if there's no more data. 287 | static BOOL _config_ini_get_line(config_ini_t *ini) 288 | { 289 | ES_UTF8 *p; 290 | 291 | if (!ini->p) 292 | { 293 | return FALSE; 294 | } 295 | 296 | p = ini->p; 297 | ini->key = p; 298 | ini->value = NULL; 299 | 300 | if (!*p) 301 | { 302 | return FALSE; 303 | } 304 | 305 | while(*p) 306 | { 307 | if ((*p == '\r') && (p[1] == '\n')) 308 | { 309 | *p = 0; 310 | 311 | p += 2; 312 | 313 | break; 314 | } 315 | 316 | if (*p == '\n') 317 | { 318 | *p = 0; 319 | 320 | p++; 321 | 322 | break; 323 | } 324 | 325 | if (*p == '=') 326 | { 327 | if (!ini->value) 328 | { 329 | *p = 0; 330 | 331 | p++; 332 | 333 | ini->value = p; 334 | 335 | continue; 336 | } 337 | } 338 | 339 | p++; 340 | } 341 | 342 | ini->p = p; 343 | 344 | return TRUE; 345 | } 346 | 347 | // open an ini file, find the specified section and read all the keyvalue pairs. 348 | // the keyvalue pairs are stored in ini. 349 | BOOL config_ini_open(config_ini_t *ini,const wchar_t *filename,const char *lowercase_ascii_section) 350 | { 351 | BOOL ret; 352 | HANDLE file_handle; 353 | 354 | ret = FALSE; 355 | file_handle = os_open_file(filename); 356 | 357 | if (file_handle != INVALID_HANDLE_VALUE) 358 | { 359 | DWORD size_lo; 360 | DWORD size_hi; 361 | 362 | size_lo = GetFileSize(file_handle,&size_hi); 363 | 364 | if ((size_lo != INVALID_FILE_SIZE) && (!size_hi)) 365 | { 366 | DWORD numread; 367 | 368 | utf8_buf_init(&ini->file_cbuf); 369 | pool_init(&ini->pool); 370 | ini->keyvalue_start = NULL; 371 | ini->keyvalue_last = NULL; 372 | 373 | utf8_buf_grow_length(&ini->file_cbuf,size_lo); 374 | 375 | if (ReadFile(file_handle,ini->file_cbuf.buf,size_lo,&numread,NULL)) 376 | { 377 | if (numread == size_lo) 378 | { 379 | ini->file_cbuf.buf[size_lo] = 0; 380 | ini->p = ini->file_cbuf.buf; 381 | 382 | // find section 383 | for(;;) 384 | { 385 | if (!_config_ini_get_line(ini)) 386 | { 387 | break; 388 | } 389 | 390 | if (*ini->key == '[') 391 | { 392 | const ES_UTF8 *match_p; 393 | 394 | match_p = utf8_string_parse_ascii_string_nocase(ini->key + 1,lowercase_ascii_section); 395 | if (match_p) 396 | { 397 | if (*match_p == ']') 398 | { 399 | ret = TRUE; 400 | 401 | // save values. 402 | while(_config_ini_find_next_keyvalue(ini)) 403 | { 404 | _config_keyvalue_t *keyvalue; 405 | 406 | // alloc 407 | keyvalue = pool_alloc(&ini->pool,sizeof(_config_keyvalue_t)); 408 | 409 | // init 410 | keyvalue->key = ini->key; 411 | keyvalue->value = ini->value; 412 | 413 | // insert at start, as we search from the start 414 | // this way the last added value will be returned first. 415 | keyvalue->next = ini->keyvalue_start; 416 | ini->keyvalue_start = keyvalue; 417 | 418 | if (!ini->keyvalue_last) 419 | { 420 | ini->keyvalue_last = keyvalue; 421 | } 422 | } 423 | 424 | break; 425 | } 426 | } 427 | } 428 | } 429 | } 430 | } 431 | 432 | if (!ret) 433 | { 434 | config_ini_close(ini); 435 | } 436 | } 437 | 438 | CloseHandle(file_handle); 439 | } 440 | 441 | return ret; 442 | } 443 | 444 | // find the next keyvalue pair. 445 | // skips any ; comments 446 | // returns TRUE if another keyvalue pair is found. 447 | // returns FALSE if the section changes or if there's no more keyvalue pairs. 448 | static BOOL _config_ini_find_next_keyvalue(config_ini_t *ini) 449 | { 450 | for(;;) 451 | { 452 | if (!_config_ini_get_line(ini)) 453 | { 454 | break; 455 | } 456 | 457 | // comment. 458 | if (*ini->key == ';') 459 | { 460 | continue; 461 | } 462 | 463 | // section 464 | if (*ini->key == '[') 465 | { 466 | ini->p = NULL; 467 | break; 468 | } 469 | 470 | // no value 471 | if (!ini->value) 472 | { 473 | continue; 474 | } 475 | 476 | return TRUE; 477 | } 478 | 479 | return FALSE; 480 | } 481 | 482 | // find a keyvalue pair by key name. 483 | // returns a pointer to the keyvalue pair. 484 | // returns NULL if not found. 485 | static _config_keyvalue_t *_config_keyvalue_find(config_ini_t *ini,const ES_UTF8 *key) 486 | { 487 | _config_keyvalue_t *keyvalue; 488 | 489 | keyvalue = ini->keyvalue_start; 490 | while(keyvalue) 491 | { 492 | const ES_UTF8 *match_p; 493 | 494 | match_p = utf8_string_parse_utf8_string(keyvalue->key,key); 495 | if (match_p) 496 | { 497 | if (!*match_p) 498 | { 499 | return keyvalue; 500 | } 501 | } 502 | 503 | keyvalue = keyvalue->next; 504 | } 505 | 506 | return NULL; 507 | } 508 | 509 | // close an ini file. 510 | // returns any allocated memory to the system. 511 | void config_ini_close(config_ini_t *ini) 512 | { 513 | pool_kill(&ini->pool); 514 | utf8_buf_kill(&ini->file_cbuf); 515 | } 516 | 517 | -------------------------------------------------------------------------------- /src/config.h: -------------------------------------------------------------------------------- 1 | 2 | // 3 | // Copyright (C) 2025 voidtools / David Carpenter 4 | // 5 | // Permission is hereby granted, free of charge, 6 | // to any person obtaining a copy of this software 7 | // and associated documentation files (the "Software"), 8 | // to deal in the Software without restriction, 9 | // including without limitation the rights to use, 10 | // copy, modify, merge, publish, distribute, sublicense, 11 | // and/or sell copies of the Software, and to permit 12 | // persons to whom the Software is furnished to do so, 13 | // subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be 16 | // included in all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 20 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 21 | // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 22 | // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 23 | // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 24 | // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | // 26 | 27 | typedef struct config_ini_s 28 | { 29 | utf8_buf_t file_cbuf; 30 | ES_UTF8 *p; 31 | const ES_UTF8 *key; 32 | const ES_UTF8 *value; 33 | pool_t pool; 34 | struct _config_keyvalue_s *keyvalue_start; 35 | struct _config_keyvalue_s *keyvalue_last; 36 | 37 | }config_ini_t; 38 | 39 | BOOL config_get_filename(int is_appdata,int is_temp,wchar_buf_t *wcbuf); 40 | void config_write_int(HANDLE file_handle,const ES_UTF8 *name,int value); 41 | void config_write_empty(HANDLE file_handle,const ES_UTF8 *name); 42 | void config_write_dword(HANDLE file_handle,const ES_UTF8 *name,DWORD value); 43 | void config_write_uint64(HANDLE file_handle,const ES_UTF8 *name,ES_UINT64 value); 44 | void config_write_string(HANDLE file_handle,const ES_UTF8 *name,const wchar_t *value); 45 | BOOL config_read_string(config_ini_t *ini,const ES_UTF8 *name,wchar_buf_t *wcbuf); 46 | int config_read_int(config_ini_t *ini,const ES_UTF8 *name,int default_value); 47 | DWORD config_read_dword(config_ini_t *ini,const ES_UTF8 *name,DWORD default_value); 48 | ES_UINT64 config_read_uint64(config_ini_t *ini,const ES_UTF8 *name,ES_UINT64 default_value); 49 | 50 | BOOL config_ini_open(config_ini_t *ini,const wchar_t *filename,const char *lowercase_ascii_section); 51 | void config_ini_close(config_ini_t *ini); 52 | -------------------------------------------------------------------------------- /src/debug.c: -------------------------------------------------------------------------------- 1 | 2 | // 3 | // Copyright (C) 2025 voidtools / David Carpenter 4 | // 5 | // Permission is hereby granted, free of charge, 6 | // to any person obtaining a copy of this software 7 | // and associated documentation files (the "Software"), 8 | // to deal in the Software without restriction, 9 | // including without limitation the rights to use, 10 | // copy, modify, merge, publish, distribute, sublicense, 11 | // and/or sell copies of the Software, and to permit 12 | // persons to whom the Software is furnished to do so, 13 | // subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be 16 | // included in all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 20 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 21 | // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 22 | // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 23 | // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 24 | // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | // 26 | // debugging 27 | 28 | #include "es.h" 29 | 30 | // write some debug information to the console. 31 | void debug_printf(ES_UTF8 *format,...) 32 | { 33 | if (es_debug) 34 | { 35 | va_list argptr; 36 | wchar_buf_t wcbuf; 37 | DWORD num_written; 38 | 39 | va_start(argptr,format); 40 | wchar_buf_init(&wcbuf); 41 | 42 | wchar_buf_vprintf(&wcbuf,format,argptr); 43 | 44 | if (wcbuf.length_in_wchars <= ES_DWORD_MAX) 45 | { 46 | WriteConsole(GetStdHandle(STD_ERROR_HANDLE),wcbuf.buf,(DWORD)wcbuf.length_in_wchars,&num_written,NULL); 47 | } 48 | 49 | wchar_buf_kill(&wcbuf); 50 | va_end(argptr); 51 | } 52 | } 53 | 54 | // write a debug error message to the console in Red text. 55 | void debug_error_printf(const ES_UTF8 *format,...) 56 | { 57 | if (es_debug) 58 | { 59 | va_list argptr; 60 | 61 | va_start(argptr,format); 62 | 63 | os_error_vprintf(format,argptr); 64 | 65 | va_end(argptr); 66 | } 67 | } 68 | 69 | // show a message and terminate the program. 70 | void DECLSPEC_NORETURN debug_fatal2(const ES_UTF8 *filename,int line,const ES_UTF8 *format,...) 71 | { 72 | va_list argptr; 73 | utf8_buf_t msg_cbuf; 74 | 75 | va_start(argptr,format); 76 | utf8_buf_init(&msg_cbuf); 77 | 78 | utf8_buf_vprintf(&msg_cbuf,format,argptr); 79 | 80 | os_error_printf("FATAL ERROR %s(%d): %s",filename,line,msg_cbuf.buf); 81 | 82 | ExitProcess(0); 83 | 84 | utf8_buf_kill(&msg_cbuf); 85 | va_end(argptr); 86 | } -------------------------------------------------------------------------------- /src/debug.h: -------------------------------------------------------------------------------- 1 | 2 | // 3 | // Copyright (C) 2025 voidtools / David Carpenter 4 | // 5 | // Permission is hereby granted, free of charge, 6 | // to any person obtaining a copy of this software 7 | // and associated documentation files (the "Software"), 8 | // to deal in the Software without restriction, 9 | // including without limitation the rights to use, 10 | // copy, modify, merge, publish, distribute, sublicense, 11 | // and/or sell copies of the Software, and to permit 12 | // persons to whom the Software is furnished to do so, 13 | // subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be 16 | // included in all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 20 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 21 | // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 22 | // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 23 | // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 24 | // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | // 26 | 27 | #define DEBUG_FATAL(format,...) debug_fatal2(__FILE__,__LINE__,format,__VA_ARGS__) 28 | 29 | #ifdef _DEBUG 30 | #include // assert() 31 | #define DEBUG_ASSERT(exp) assert(exp) 32 | #else 33 | #define DEBUG_ASSERT(exp) 34 | #endif 35 | 36 | void debug_printf(ES_UTF8 *format,...); 37 | void debug_error_printf(const ES_UTF8 *format,...); 38 | 39 | void DECLSPEC_NORETURN debug_fatal2(const ES_UTF8 *format,...); 40 | -------------------------------------------------------------------------------- /src/es.h: -------------------------------------------------------------------------------- 1 | 2 | // 3 | // Copyright (C) 2025 voidtools / David Carpenter 4 | // 5 | // Permission is hereby granted, free of charge, 6 | // to any person obtaining a copy of this software 7 | // and associated documentation files (the "Software"), 8 | // to deal in the Software without restriction, 9 | // including without limitation the rights to use, 10 | // copy, modify, merge, publish, distribute, sublicense, 11 | // and/or sell copies of the Software, and to permit 12 | // persons to whom the Software is furnished to do so, 13 | // subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be 16 | // included in all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 20 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 21 | // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 22 | // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 23 | // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 24 | // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | // 26 | 27 | // errorlevel returned from ES. 28 | #define ES_ERROR_SUCCESS 0 // no known error, search successful. 29 | #define ES_ERROR_REGISTER_WINDOW_CLASS 1 // failed to register window class 30 | #define ES_ERROR_CREATE_WINDOW 2 // failed to create listening window. 31 | #define ES_ERROR_OUT_OF_MEMORY 3 // out of memory 32 | #define ES_ERROR_EXPECTED_SWITCH_PARAMETER 4 // expected an additional command line option with the specified switch or bad switch param 33 | #define ES_ERROR_CREATE_FILE 5 // failed to create export output file 34 | #define ES_ERROR_UNKNOWN_SWITCH 6 // unknown switch. 35 | #define ES_ERROR_IPC_ERROR 7 // failed to send Everything IPC a query or bad IPC reply. 36 | #define ES_ERROR_NO_IPC 8 // NO Everything IPC window or pipe. 37 | #define ES_ERROR_NO_RESULTS 9 // No results found. Only set if -no-result-error is used 38 | 39 | #define ES_UINT64_MAX 0xffffffffffffffffUI64 40 | #define ES_DWORD_MAX 0xffffffff 41 | #define ES_WORD_MAX 0xffff 42 | #define ES_BYTE_MAX 0xff 43 | //#define INT64_MIN (-9223372036854775807i64 - 1) 44 | //#define INT64_MAX 0x7fffffffffffffffi64 45 | //#define INT32_MIN (-2147483647 - 1) 46 | //#define INT32_MAX 2147483647 47 | 48 | #define ES_IPC_VERSION_FLAG_IPC1 0x00000001 // Everything 1.3 49 | #define ES_IPC_VERSION_FLAG_IPC2 0x00000002 // Everything 1.4 50 | #define ES_IPC_VERSION_FLAG_IPC3 0x00000004 // Everything 1.5 51 | 52 | #define WIN32_LEAN_AND_MEAN 53 | 54 | #include 55 | #include // SIZE_MAX 56 | 57 | typedef unsigned __int64 ES_UINT64; 58 | typedef BYTE ES_UTF8; 59 | 60 | #include "everything_ipc.h" 61 | #include "everything3.h" 62 | #include "version.h" 63 | #include "safe_size.h" 64 | #include "safe_int.h" 65 | #include "mem.h" 66 | #include "pool.h" 67 | #include "array.h" 68 | #include "unicode.h" 69 | #include "wchar_string.h" 70 | #include "wchar_buf.h" 71 | #include "utf8_string.h" 72 | #include "utf8_buf.h" 73 | #include "config.h" 74 | #include "property.h" 75 | #include "property_unknown.h" 76 | #include "column.h" 77 | #include "column_color.h" 78 | #include "column_width.h" 79 | #include "secondary_sort.h" 80 | #include "os.h" 81 | #include "debug.h" 82 | #include "ipc3.h" 83 | 84 | void DECLSPEC_NORETURN es_fatal(int error_code); 85 | 86 | extern wchar_buf_t *es_instance_name_wcbuf; 87 | extern DWORD es_timeout; 88 | extern DWORD es_ipc_version; // allow all ipc versions 89 | extern BYTE es_debug; 90 | extern int es_pixels_to_characters_mul; 91 | extern int es_pixels_to_characters_div; 92 | -------------------------------------------------------------------------------- /src/filelist.c: -------------------------------------------------------------------------------- 1 | 2 | // TODO 3 | /* 4 | #include "es.h" 5 | 6 | #define _FILELIST_ITEM_FILENAME(filelist_item) ((ES_UTF8 *)(((_filelist_item_t *)(filelist_item)) + 1)) 7 | 8 | #define _FILELIST_MAX_DEPTH 64 9 | 10 | #pragma pack (push,1) 11 | 12 | // an editor item. 13 | typedef struct _filelist_item_s 14 | { 15 | struct _filelist_item_s *next; 16 | 17 | ES_UINT64 size; 18 | ES_UINT64 date_created; 19 | ES_UINT64 date_modified; 20 | DWORD attributes; 21 | BYTE flags; 22 | 23 | // NULL terminated filename follows. 24 | 25 | }_filelist_item_t; 26 | 27 | #pragma pack (pop) 28 | 29 | // a list of filenames. 30 | typedef struct _filelist_s 31 | { 32 | pool_t data_pool; 33 | _filelist_item_t *item_start; 34 | _filelist_item_t *item_last; 35 | // filename_filter_t include_only_folders_filename_filter; 36 | // filename_filter_t exclude_folders_filename_filter; 37 | // filename_filter_t include_only_files_filename_filter; 38 | // filename_filter_t exclude_files_filename_filter; 39 | int no_folders; 40 | int no_files; 41 | int no_subfolders; 42 | 43 | }_filelist_t; 44 | 45 | static void _filelist_add_folder_from_path_recurse(_filelist_t *e,const wchar_t *path,uintptr_t path_len,uintptr_t depth); 46 | static void _filelist_add_item(_filelist_t *e,const ES_UTF8 *filename,ES_UINT64 size,ES_UINT64 date_created,ES_UINT64 date_modified); 47 | static int _filelist_startwith(const wchar_t *s,const wchar_t *substring); 48 | 49 | int filelist_create(const wchar_t *filename,const wchar_t *path,int folder_append_path_separator,int no_folders,int no_files,int no_subfolders,const wchar_t *include_only_folders,const wchar_t *exclude_folders,const wchar_t *include_only_files,const wchar_t *exclude_files) 50 | { 51 | int ret; 52 | _filelist_t filelist; 53 | 54 | ret = 0; 55 | os_zero_memory(&filelist,sizeof(_filelist_t)); 56 | 57 | pool_init(&filelist.data_pool); 58 | 59 | // filename_filter_init(&filelist.include_only_folders_filename_filter); 60 | // filename_filter_init(&filelist.exclude_folders_filename_filter); 61 | // filename_filter_init(&filelist.include_only_files_filename_filter); 62 | // filename_filter_init(&filelist.exclude_files_filename_filter); 63 | 64 | // filename_filter_compile(&filelist.include_only_folders_filename_filter,include_only_folders ? include_only_folders : "",1); 65 | // filename_filter_compile(&filelist.exclude_folders_filename_filter,exclude_folders ? exclude_folders : "",1); 66 | // filename_filter_compile(&filelist.include_only_files_filename_filter,include_only_files ? include_only_files : "",0); 67 | // filename_filter_compile(&filelist.exclude_files_filename_filter,exclude_files ? exclude_files : "",0); 68 | 69 | filelist.no_folders = no_folders; 70 | filelist.no_files = no_files; 71 | filelist.no_subfolders = no_subfolders; 72 | 73 | 74 | // recurse path 75 | 76 | { 77 | utf8_buf_t item_cbuf; 78 | utf8_buf_t path_cbuf; 79 | const wchar_t *path_p; 80 | 81 | utf8_buf_init(&item_cbuf); 82 | utf8_buf_init(&path_cbuf); 83 | 84 | path_p = path; 85 | 86 | while(*path_p) 87 | { 88 | path_p = utf8_string_parse_c_item(path_p,&item_cbuf); 89 | if (!path_p) 90 | { 91 | break; 92 | } 93 | 94 | // uppercase drive letter 95 | if ((item_cbuf.buf[0] >= 'a') && (item_cbuf.buf[0] <= 'z') && (item_cbuf.buf[1] == ':')) 96 | { 97 | item_cbuf.buf[0] = item_cbuf.buf[0] - 'a' + 'A'; 98 | } 99 | 100 | utf8_buf_copy_no_trailing_path_separator(&path_cbuf,item_cbuf.buf); 101 | 102 | utf8_buf_fix_path_separators(&path_cbuf); 103 | 104 | // add this path. 105 | // this makes it easy to update roots. 106 | if (!no_folders) 107 | { 108 | fileinfo_fd_t fd; 109 | 110 | os_get_fd(path_cbuf.buf,&fd); 111 | 112 | _filelist_add_item(&filelist,path_cbuf.buf,&fd); 113 | } 114 | 115 | _filelist_add_folder_from_path_recurse(&filelist,path_cbuf.buf,path_cbuf.len,0); 116 | } 117 | 118 | utf8_buf_kill(&path_cbuf); 119 | utf8_buf_kill(&item_cbuf); 120 | } 121 | 122 | // save 123 | { 124 | output_stream_t f; 125 | 126 | // create the file 127 | if (output_stream_create_file(&f,filename)) 128 | { 129 | utf8_buf_t path_cbuf; 130 | uintptr_t path_len; 131 | 132 | output_stream_write_utf8_bom(&f); 133 | 134 | utf8_buf_init(&path_cbuf); 135 | path_len = 0; 136 | 137 | utf8_buf_copy_utf8_string(&path_cbuf,filename); 138 | 139 | // check filename prefix. 140 | { 141 | wchar_t *p; 142 | wchar_t *last; 143 | 144 | p = path_cbuf.buf; 145 | last = 0; 146 | 147 | while(*p) 148 | { 149 | if (*p == '\\') 150 | { 151 | // include the backslash. 152 | path_len = (p - path_cbuf.buf + 1); 153 | } 154 | else 155 | if (*p == '/') 156 | { 157 | // include the backslash. 158 | path_len = (p - path_cbuf.buf + 1); 159 | } 160 | 161 | p++; 162 | } 163 | 164 | path_cbuf.buf[path_len] = 0; 165 | } 166 | 167 | // make all files relative to filename. 168 | if (config_get_int_value(CONFIG_FILE_LIST_RELATIVE_PATHS)) 169 | { 170 | _filelist_item_t *item; 171 | 172 | item = filelist.item_start; 173 | 174 | while(item) 175 | { 176 | // free item 177 | if (!_filelist_startwith(_FILELIST_ITEM_FILENAME(item),path_cbuf.buf)) 178 | { 179 | path_len = 0; 180 | break; 181 | } 182 | 183 | item = item->next; 184 | } 185 | } 186 | else 187 | { 188 | path_len = 0; 189 | } 190 | 191 | // write header. 192 | output_stream_write_printf(&f,(const utf8_t *)"Filename,Size,Date Modified,Date Created,Attributes\r\n"); 193 | 194 | { 195 | _filelist_item_t *item; 196 | 197 | item = filelist.item_start; 198 | 199 | while(item) 200 | { 201 | // filename 202 | if ((folder_append_path_separator) && (item->fd.attributes & FILE_ATTRIBUTE_DIRECTORY)) 203 | { 204 | output_stream_write_csv_path_with_trailing_path_separator(&f,_FILELIST_ITEM_FILENAME(item) + path_len); 205 | } 206 | else 207 | { 208 | output_stream_write_csv_string(&f,_FILELIST_ITEM_FILENAME(item) + path_len); 209 | } 210 | 211 | output_stream_write_byte(&f,','); 212 | 213 | // size 214 | if (item->fd.size != QWORD_MAX) 215 | { 216 | output_stream_write_printf(&f,(const utf8_t *)"%I64u",item->fd.size); 217 | } 218 | output_stream_write_byte(&f,','); 219 | 220 | // date modified 221 | if (item->fd.date_modified & QWORD_MAX) 222 | { 223 | output_stream_write_printf(&f,(const utf8_t *)"%I64u",item->fd.date_modified); 224 | } 225 | output_stream_write_byte(&f,','); 226 | 227 | // date created. 228 | if (item->fd.date_created & QWORD_MAX) 229 | { 230 | output_stream_write_printf(&f,(const utf8_t *)"%I64u",item->fd.date_created); 231 | } 232 | 233 | // attributes 234 | output_stream_write_printf(&f,(const utf8_t *)",%u\r\n",item->fd.attributes); 235 | 236 | item = item->next; 237 | } 238 | } 239 | 240 | // close file. 241 | if (output_stream_close(&f)) 242 | { 243 | ret = 1; 244 | } 245 | else 246 | { 247 | // failed to export 248 | ui_task_dialog_show(0,MB_OK|MB_ICONERROR,localization_get_string(LOCALIZATION_EVERYTHING),NULL,localization_get_string(LOCALIZATION_EXPORT_WRITE_FAILED),filename); 249 | } 250 | 251 | utf8_buf_kill(&path_cbuf); 252 | } 253 | else 254 | { 255 | // Message 256 | ui_task_dialog_show(0,MB_OK|MB_ICONERROR,localization_get_string(LOCALIZATION_EVERYTHING),NULL,localization_get_string(LOCALIZATION_EXPORT_CREATEFILE_FAILED),filename); 257 | } 258 | } 259 | 260 | filename_filter_kill(&filelist.include_only_folders_filename_filter); 261 | filename_filter_kill(&filelist.exclude_folders_filename_filter); 262 | filename_filter_kill(&filelist.include_only_files_filename_filter); 263 | filename_filter_kill(&filelist.exclude_files_filename_filter); 264 | 265 | // free items. 266 | pool_kill(&filelist.data_pool); 267 | 268 | return ret; 269 | } 270 | 271 | static int _filelist_startwith(const utf8_t *s,const utf8_t *substring) 272 | { 273 | const utf8_t *sp; 274 | const utf8_t *ssp; 275 | 276 | sp = s; 277 | ssp = substring; 278 | 279 | while(*ssp) 280 | { 281 | if (*sp != *ssp) return 0; 282 | 283 | sp++; 284 | ssp++; 285 | } 286 | 287 | return 1; 288 | } 289 | 290 | static void _filelist_add_item(_filelist_t *filelist,const utf8_t *filename,fileinfo_fd_t *fd) 291 | { 292 | _filelist_item_t *item; 293 | uintptr_t len; 294 | uintptr_t item_size; 295 | 296 | len = utf8_string_get_length_in_bytes(filename); 297 | 298 | // alloc 299 | item_size = sizeof(_filelist_item_t); 300 | item_size = safe_uintptr_add(item_size,len); 301 | item_size = safe_uintptr_add_one(item_size); 302 | 303 | item = pool_alloc(&filelist->data_pool,item_size); 304 | 305 | // init 306 | os_copy_memory(&item->fd,fd,sizeof(fileinfo_fd_t)); 307 | 308 | os_copy_memory(item+1,filename,len+1); 309 | 310 | if (filelist->item_start) 311 | { 312 | filelist->item_last->next = item; 313 | } 314 | else 315 | { 316 | filelist->item_start = item; 317 | } 318 | 319 | filelist->item_last = item; 320 | item->next = 0; 321 | } 322 | 323 | // recursive. 324 | static void _filelist_add_folder_from_path_recurse(_filelist_t *e,const utf8_t *path,uintptr_t path_len,uintptr_t depth) 325 | { 326 | if (depth < _FILELIST_MAX_DEPTH) 327 | { 328 | os_find_t find_handle; 329 | fileinfo_fd_t fd; 330 | utf8_buf_t search_cbuf; 331 | utf8_buf_t filename_cbuf; 332 | 333 | utf8_buf_init(&search_cbuf); 334 | utf8_buf_init(&filename_cbuf); 335 | 336 | if (os_find_first_file(&find_handle,path,&filename_cbuf,&fd,0)) 337 | { 338 | for(;;) 339 | { 340 | if (fd.attributes & FILE_ATTRIBUTE_DIRECTORY) 341 | { 342 | // convert to utf8. 343 | utf8_buf_path_cat_filename_n(&search_cbuf,path,path_len,filename_cbuf.buf,filename_cbuf.len); 344 | 345 | if (!filename_filter_exec(&e->exclude_folders_filename_filter,search_cbuf.buf,search_cbuf.len,filename_cbuf.buf,filename_cbuf.len,1,0)) 346 | { 347 | if (filename_filter_exec(&e->include_only_folders_filename_filter,search_cbuf.buf,search_cbuf.len,filename_cbuf.buf,filename_cbuf.len,1,1)) 348 | { 349 | // add self 350 | if (!e->no_folders) 351 | { 352 | _filelist_add_item(e,search_cbuf.buf,&fd); 353 | } 354 | 355 | // add sub folders. 356 | if (!e->no_subfolders) 357 | { 358 | _filelist_add_folder_from_path_recurse(e,search_cbuf.buf,search_cbuf.len,depth+1); 359 | } 360 | } 361 | } 362 | } 363 | else 364 | { 365 | // convert to utf8. 366 | utf8_buf_path_cat_filename(&search_cbuf,path,filename_cbuf.buf); 367 | 368 | if (!filename_filter_exec(&e->exclude_folders_filename_filter,search_cbuf.buf,search_cbuf.len,filename_cbuf.buf,filename_cbuf.len,0,0)) 369 | { 370 | if (filename_filter_exec(&e->include_only_folders_filename_filter,search_cbuf.buf,search_cbuf.len,filename_cbuf.buf,filename_cbuf.len,0,1)) 371 | { 372 | if (!filename_filter_exec(&e->exclude_files_filename_filter,search_cbuf.buf,search_cbuf.len,filename_cbuf.buf,filename_cbuf.len,0,0)) 373 | { 374 | if (filename_filter_exec(&e->include_only_files_filename_filter,search_cbuf.buf,search_cbuf.len,filename_cbuf.buf,filename_cbuf.len,0,1)) 375 | { 376 | if (!e->no_files) 377 | { 378 | // add it 379 | _filelist_add_item(e,search_cbuf.buf,&fd); 380 | } 381 | } 382 | } 383 | } 384 | } 385 | } 386 | 387 | if (!os_find_next_file(&find_handle,&filename_cbuf,&fd)) 388 | { 389 | break; 390 | } 391 | } 392 | 393 | os_find_close(&find_handle); 394 | } 395 | 396 | utf8_buf_kill(&filename_cbuf); 397 | utf8_buf_kill(&search_cbuf); 398 | } 399 | } 400 | 401 | */ -------------------------------------------------------------------------------- /src/filelist.h: -------------------------------------------------------------------------------- 1 | 2 | // create a new filelist 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | int filelist_create(const utf8_t *filename,const utf8_t *path,int no_folders,int no_files,int no_subfolders,const utf8_t *include_only_folders,const utf8_t *exclude_folders,const utf8_t *include_only_files,const utf8_t *exclude_files); 9 | 10 | #ifdef __cplusplus 11 | } 12 | #endif 13 | 14 | -------------------------------------------------------------------------------- /src/ipc3.h: -------------------------------------------------------------------------------- 1 | 2 | // 3 | // Copyright (C) 2025 voidtools / David Carpenter 4 | // 5 | // Permission is hereby granted, free of charge, 6 | // to any person obtaining a copy of this software 7 | // and associated documentation files (the "Software"), 8 | // to deal in the Software without restriction, 9 | // including without limitation the rights to use, 10 | // copy, modify, merge, publish, distribute, sublicense, 11 | // and/or sell copies of the Software, and to permit 12 | // persons to whom the Software is furnished to do so, 13 | // subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be 16 | // included in all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 20 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 21 | // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 22 | // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 23 | // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 24 | // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | // 26 | 27 | // IPC pipe commands. 28 | #define IPC3_COMMAND_GET_IPC_PIPE_VERSION 0 29 | #define IPC3_COMMAND_GET_MAJOR_VERSION 1 30 | #define IPC3_COMMAND_GET_MINOR_VERSION 2 31 | #define IPC3_COMMAND_GET_REVISION 3 32 | #define IPC3_COMMAND_GET_BUILD_NUMBER 4 33 | #define IPC3_COMMAND_GET_TARGET_MACHINE 5 34 | #define IPC3_COMMAND_FIND_PROPERTY_FROM_NAME 6 35 | #define IPC3_COMMAND_SEARCH 7 36 | #define IPC3_COMMAND_IS_DB_LOADED 8 37 | #define IPC3_COMMAND_IS_PROPERTY_INDEXED 9 38 | #define IPC3_COMMAND_IS_PROPERTY_FAST_SORT 10 39 | #define IPC3_COMMAND_GET_PROPERTY_NAME 11 40 | #define IPC3_COMMAND_GET_PROPERTY_CANONICAL_NAME 12 41 | #define IPC3_COMMAND_GET_PROPERTY_TYPE 13 42 | #define IPC3_COMMAND_IS_RESULT_CHANGE 14 43 | #define IPC3_COMMAND_GET_RUN_COUNT 15 44 | #define IPC3_COMMAND_SET_RUN_COUNT 16 45 | #define IPC3_COMMAND_INC_RUN_COUNT 17 46 | #define IPC3_COMMAND_GET_FOLDER_SIZE 18 47 | #define IPC3_COMMAND_GET_FILE_ATTRIBUTES 19 48 | #define IPC3_COMMAND_GET_FILE_ATTRIBUTES_EX 20 49 | #define IPC3_COMMAND_GET_FIND_FIRST_FILE 21 50 | #define IPC3_COMMAND_GET_RESULTS 22 51 | #define IPC3_COMMAND_SORT 23 52 | #define IPC3_COMMAND_WAIT_FOR_RESULT_CHANGE 24 53 | #define IPC3_COMMAND_IS_PROPERTY_RIGHT_ALIGNED 25 54 | #define IPC3_COMMAND_IS_PROPERTY_SORT_DESCENDING 26 55 | #define IPC3_COMMAND_GET_PROPERTY_DEFAULT_WIDTH 27 56 | #define IPC3_COMMAND_GET_JOURNAL_INFO 28 57 | #define IPC3_COMMAND_READ_JOURNAL 29 58 | //#define IPC3_COMMAND_GET_JOURNAL_CHANGE_FROM_DATE 30 59 | 60 | // IPC pipe responses 61 | #define IPC3_RESPONSE_OK_MORE_DATA 100 // expect another repsonse. 62 | #define IPC3_RESPONSE_OK 200 // reply data depending on request. 63 | #define IPC3_RESPONSE_ERROR_BAD_REQUEST 400 64 | #define IPC3_RESPONSE_ERROR_CANCELLED 401 // another requested was made while processing... 65 | #define IPC3_RESPONSE_ERROR_NOT_FOUND 404 66 | #define IPC3_RESPONSE_ERROR_OUT_OF_MEMORY 500 67 | #define IPC3_RESPONSE_ERROR_INVALID_COMMAND 501 68 | 69 | #define IPC3_SEARCH_FLAG_MATCH_CASE 0x00000001 // match case 70 | #define IPC3_SEARCH_FLAG_MATCH_WHOLEWORD 0x00000002 // match whole word 71 | #define IPC3_SEARCH_FLAG_MATCH_PATH 0x00000004 // include paths in search 72 | #define IPC3_SEARCH_FLAG_REGEX 0x00000008 // enable regex 73 | #define IPC3_SEARCH_FLAG_MATCH_DIACRITICS 0x00000010 // match diacritic marks 74 | #define IPC3_SEARCH_FLAG_MATCH_PREFIX 0x00000020 // match prefix (Everything 1.5) 75 | #define IPC3_SEARCH_FLAG_MATCH_SUFFIX 0x00000040 // match suffix (Everything 1.5) 76 | #define IPC3_SEARCH_FLAG_IGNORE_PUNCTUATION 0x00000080 // ignore punctuation (Everything 1.5) 77 | #define IPC3_SEARCH_FLAG_IGNORE_WHITESPACE 0x00000100 // ignore white-space (Everything 1.5) 78 | #define IPC3_SEARCH_FLAG_FOLDERS_FIRST_ASCENDING 0x00000000 // folders first when sort ascending 79 | #define IPC3_SEARCH_FLAG_FOLDERS_FIRST_ALWAYS 0x00000200 // folders first 80 | #define IPC3_SEARCH_FLAG_FOLDERS_FIRST_NEVER 0x00000400 // folders last 81 | #define IPC3_SEARCH_FLAG_FOLDERS_FIRST_DESCENDING 0x00000600 // folders first when sort descending 82 | #define IPC3_SEARCH_FLAG_TOTAL_SIZE 0x00000800 // calculate total size 83 | #define IPC3_SEARCH_FLAG_HIDE_RESULT_OMISSIONS 0x00001000 // hide omitted results 84 | #define IPC3_SEARCH_FLAG_SORT_MIX 0x00002000 // mix file and folder results 85 | #define IPC3_SEARCH_FLAG_64BIT 0x00004000 // SIZE_T is 64bits. Otherwise, 32bits. 86 | #define IPC3_SEARCH_FLAG_FORCE 0x00008000 // Force a research, even when the search state doesn't change. 87 | 88 | #define IPC3_MESSAGE_DATA(msg) ((void *)(((ipc3_message_t *)(msg)) + 1)) 89 | 90 | #define IPC3_SEARCH_PROPERTY_REQUEST_FLAG_FORMAT 0x00000001 91 | #define IPC3_SEARCH_PROPERTY_REQUEST_FLAG_HIGHLIGHT 0x00000002 92 | 93 | #define IPC3_RESULT_LIST_ITEM_FLAG_FOLDER 0x01 94 | #define IPC3_RESULT_LIST_ITEM_FLAG_ROOT 0x02 95 | 96 | // Everything3_GetResultListPropertyRequestValueType() 97 | #define IPC3_PROPERTY_VALUE_TYPE_NULL 0 98 | #define IPC3_PROPERTY_VALUE_TYPE_BYTE 1 // Everything3_GetResultPropertyBYTE 99 | #define IPC3_PROPERTY_VALUE_TYPE_WORD 2 // Everything3_GetResultPropertyWORD 100 | #define IPC3_PROPERTY_VALUE_TYPE_DWORD 3 // Everything3_GetResultPropertyDWORD 101 | #define IPC3_PROPERTY_VALUE_TYPE_DWORD_FIXED_Q1K 4 // Everything3_GetResultPropertyDWORD / 1000 102 | #define IPC3_PROPERTY_VALUE_TYPE_UINT64 5 // Everything3_GetResultPropertyUINT64 103 | #define IPC3_PROPERTY_VALUE_TYPE_UINT128 6 // Everything3_GetResultPropertyUINT128 104 | #define IPC3_PROPERTY_VALUE_TYPE_DIMENSIONS 7 // Everything3_GetResultPropertyDIMENSIONS 105 | #define IPC3_PROPERTY_VALUE_TYPE_PSTRING 8 // Everything3_GetResultPropertyText 106 | #define IPC3_PROPERTY_VALUE_TYPE_PSTRING_MULTISTRING 9 // Everything3_GetResultPropertyText 107 | #define IPC3_PROPERTY_VALUE_TYPE_PSTRING_STRING_REFERENCE 10 // Everything3_GetResultPropertyText 108 | #define IPC3_PROPERTY_VALUE_TYPE_SIZE_T 11 // Everything3_GetResultPropertySIZE_T 109 | #define IPC3_PROPERTY_VALUE_TYPE_INT32_FIXED_Q1K 12 // Everything3_GetResultPropertyINT32 / 1000 110 | #define IPC3_PROPERTY_VALUE_TYPE_INT32_FIXED_Q1M 13 // Everything3_GetResultPropertyINT32 / 1000000 111 | #define IPC3_PROPERTY_VALUE_TYPE_PSTRING_FOLDER_REFERENCE 14 // Everything3_GetResultPropertyText 112 | #define IPC3_PROPERTY_VALUE_TYPE_PSTRING_FILE_OR_FOLDER_REFERENCE 15 // Everything3_GetResultPropertyText 113 | #define IPC3_PROPERTY_VALUE_TYPE_BLOB8 16 // Everything3_GetResultPropertyBlob 114 | #define IPC3_PROPERTY_VALUE_TYPE_DWORD_GET_TEXT 17 // Everything3_GetResultPropertyDWORD 115 | #define IPC3_PROPERTY_VALUE_TYPE_WORD_GET_TEXT 18 // Everything3_GetResultPropertyWORD 116 | #define IPC3_PROPERTY_VALUE_TYPE_BLOB16 19 // Everything3_GetResultPropertyBlob 117 | #define IPC3_PROPERTY_VALUE_TYPE_BYTE_GET_TEXT 20 // Everything3_GetResultPropertyBYTE 118 | #define IPC3_PROPERTY_VALUE_TYPE_PROPVARIANT 21 // Everything3_GetResultPropertyPropVariant 119 | 120 | #define IPC3_SEARCH_SORT_FLAG_DESCENDING 0x00000001 121 | 122 | #define IPC3_SEARCH_PROPERTY_REQUEST_FLAG_FORMAT 0x00000001 123 | #define IPC3_SEARCH_PROPERTY_REQUEST_FLAG_HIGHLIGHT 0x00000002 124 | 125 | #define IPC3_JOURNAL_ITEM_TYPE_NOP 0 // access denied. 126 | #define IPC3_JOURNAL_ITEM_TYPE_FOLDER_CREATE 1 127 | #define IPC3_JOURNAL_ITEM_TYPE_FOLDER_DELETE 2 128 | #define IPC3_JOURNAL_ITEM_TYPE_FOLDER_RENAME 3 129 | #define IPC3_JOURNAL_ITEM_TYPE_FOLDER_MOVE 4 130 | #define IPC3_JOURNAL_ITEM_TYPE_FOLDER_MODIFY 5 131 | #define IPC3_JOURNAL_ITEM_TYPE_FILE_CREATE 6 132 | #define IPC3_JOURNAL_ITEM_TYPE_FILE_DELETE 7 133 | #define IPC3_JOURNAL_ITEM_TYPE_FILE_RENAME 8 134 | #define IPC3_JOURNAL_ITEM_TYPE_FILE_MOVE 9 135 | #define IPC3_JOURNAL_ITEM_TYPE_FILE_MODIFY 10 136 | 137 | #define IPC3_READ_JOURNAL_FLAG_CHANGE_ID 0x00000001 138 | #define IPC3_READ_JOURNAL_FLAG_TIMESTAMP 0x00000002 139 | #define IPC3_READ_JOURNAL_FLAG_SOURCE_TIMESTAMP 0x00000004 140 | #define IPC3_READ_JOURNAL_FLAG_OLD_PARENT_DATE_MODIFIED 0x00000008 141 | #define IPC3_READ_JOURNAL_FLAG_OLD_PATH 0x00000010 142 | #define IPC3_READ_JOURNAL_FLAG_OLD_NAME 0x00000020 143 | #define IPC3_READ_JOURNAL_FLAG_SIZE 0x00000040 144 | #define IPC3_READ_JOURNAL_FLAG_DATE_CREATED 0x00000080 145 | #define IPC3_READ_JOURNAL_FLAG_DATE_MODIFIED 0x00000100 146 | #define IPC3_READ_JOURNAL_FLAG_DATE_ACCESSED 0x00000200 147 | #define IPC3_READ_JOURNAL_FLAG_ATTRIBUTES 0x00000400 148 | #define IPC3_READ_JOURNAL_FLAG_NEW_PARENT_DATE_MODIFIED 0x00000800 149 | #define IPC3_READ_JOURNAL_FLAG_NEW_PATH 0x00001000 150 | #define IPC3_READ_JOURNAL_FLAG_NEW_NAME 0x00002000 151 | 152 | // a sort item 153 | typedef struct ipc3_search_sort_s 154 | { 155 | DWORD property_id; 156 | DWORD flags; 157 | 158 | }ipc3_search_sort_t; 159 | 160 | // property request item 161 | typedef struct ipc3_search_property_request_s 162 | { 163 | DWORD property_id; 164 | DWORD flags; 165 | 166 | }ipc3_search_property_request_t; 167 | 168 | // IPC pipe message 169 | typedef struct ipc3_message_s 170 | { 171 | DWORD code; // IPC3_COMMAND_* or IPC3_RESPONSE_* 172 | DWORD size; // excludes header size. 173 | 174 | // data follows 175 | // BYTE data[size]; 176 | 177 | }ipc3_message_t; 178 | 179 | // stream virtual table. 180 | typedef struct ipc3_stream_vtbl_s 181 | { 182 | // jump to a position in the stream. 183 | // is_error MUST be set on seek error 184 | void (*seek_proc)(struct ipc3_stream_s *stream,ES_UINT64 position_from_start); 185 | 186 | // get the current position in the stream. 187 | ES_UINT64 (*tell_proc)(struct ipc3_stream_s *stream); 188 | 189 | // read from the stream. 190 | // is_error MUST be set on read error 191 | // returns the amount of data read. 192 | // can return less than size. 193 | // set stream->is_error on any errors. 194 | SIZE_T (*read_proc)(struct ipc3_stream_s *stream,void *buf,SIZE_T size); 195 | 196 | // close the stream. 197 | void (*close_proc)(struct ipc3_stream_s *stream); 198 | 199 | }ipc3_stream_vtbl_t; 200 | 201 | // input stream. 202 | typedef struct ipc3_stream_s 203 | { 204 | const ipc3_stream_vtbl_t *vtbl; 205 | int is_error; 206 | int is_64bit; 207 | DWORD response_code; 208 | 209 | }ipc3_stream_t; 210 | 211 | // pipe stream. 212 | typedef struct ipc3_stream_pipe_s 213 | { 214 | ipc3_stream_t base; 215 | 216 | HANDLE pipe_handle; 217 | 218 | // NULL if not yet allocated 219 | BYTE *buf; 220 | BYTE *p; 221 | SIZE_T avail; 222 | int is_last; 223 | int is_eof; 224 | DWORD pipe_avail; 225 | DWORD buf_size; 226 | ES_UINT64 pipe_totread; 227 | 228 | }ipc3_stream_pipe_t; 229 | 230 | // memory stream. 231 | typedef struct ipc3_stream_pool_s 232 | { 233 | ipc3_stream_t base; 234 | 235 | // an array of ipc3_stream_pool_chunk_t * 236 | array_t chunk_array; 237 | 238 | // current position. 239 | SIZE_T chunk_cur; 240 | BYTE *p; 241 | SIZE_T avail; 242 | 243 | int is_last; 244 | SIZE_T last_chunk_numread; 245 | 246 | // the original source input stream. 247 | struct ipc3_stream_s *source_stream; 248 | 249 | }ipc3_stream_pool_t; 250 | 251 | typedef struct ipc3_result_list_property_request_s 252 | { 253 | DWORD property_id; 254 | // one or more of IPC3_SEARCH_PROPERTY_REQUEST_FLAG_* 255 | DWORD flags; 256 | DWORD value_type; 257 | 258 | }ipc3_result_list_property_request_t; 259 | 260 | // an ipc3 result list. 261 | typedef struct ipc3_result_list_s 262 | { 263 | ES_UINT64 total_result_size; 264 | SIZE_T folder_result_count; 265 | SIZE_T file_result_count; 266 | SIZE_T viewport_offset; 267 | SIZE_T viewport_count; 268 | SIZE_T sort_count; 269 | 270 | SIZE_T property_request_count; 271 | 272 | DWORD valid_flags; 273 | 274 | // the pipe stream 275 | // -or- 276 | // the memory stream if we are in es_pause mode. 277 | ipc3_stream_t *stream; 278 | 279 | // ipc3_result_list_property_request_t *property_request_array; 280 | utf8_buf_t property_request_cbuf; 281 | 282 | // index to stream offset in bytes. 283 | // this array has a count of viewport_count items. 284 | // the first index will always be 0. 285 | // only used by es_pause. 286 | SIZE_T *index_to_stream_offset_array; 287 | 288 | // the number of valid index to stream offset items in. 289 | // index_to_stream_offset_array. 290 | // only used by es_pause. 291 | SIZE_T index_to_stream_offset_valid_count; 292 | 293 | }ipc3_result_list_t; 294 | 295 | typedef struct _ipc3_journal_change_s 296 | { 297 | ES_UINT64 journal_id; 298 | ES_UINT64 change_id; 299 | ES_UINT64 timestamp; 300 | ES_UINT64 source_timestamp; 301 | ES_UINT64 old_parent_date_modified; 302 | ES_UINT64 new_parent_date_modified; 303 | 304 | ES_UINT64 size; 305 | ES_UINT64 date_created; 306 | ES_UINT64 date_modified; 307 | ES_UINT64 date_accessed; 308 | 309 | const ES_UTF8 *old_path; 310 | SIZE_T old_path_len; 311 | const ES_UTF8 *old_name; 312 | SIZE_T old_name_len; 313 | 314 | const ES_UTF8 *new_path; 315 | SIZE_T new_path_len; 316 | const ES_UTF8 *new_name; 317 | SIZE_T new_name_len; 318 | 319 | DWORD attributes; 320 | 321 | BYTE type; 322 | 323 | }_ipc3_journal_change_t; 324 | 325 | typedef struct ipc3_journal_info_s 326 | { 327 | ES_UINT64 journal_id; 328 | ES_UINT64 first_change_id; 329 | ES_UINT64 next_change_id; 330 | ES_UINT64 size; 331 | ES_UINT64 max_size; 332 | 333 | }ipc3_journal_info_t; 334 | 335 | BOOL ipc3_write_pipe_data(HANDLE pipe_handle,const void *in_data,SIZE_T in_size); 336 | BOOL ipc3_write_pipe_message(HANDLE pipe_handle,DWORD code,const void *in_data,SIZE_T in_size); 337 | void ipc3_stream_read_data(ipc3_stream_t *stream,void *data,SIZE_T size); 338 | void ipc3_stream_read_utf8_string(ipc3_stream_t *stream,utf8_buf_t *out_cbuf); 339 | SIZE_T ipc3_stream_try_read_data(ipc3_stream_t *stream,void *data,SIZE_T size); 340 | void ipc3_stream_skip(ipc3_stream_t *stream,SIZE_T size); 341 | BYTE ipc3_stream_read_byte(ipc3_stream_t *stream); 342 | WORD ipc3_stream_read_word(ipc3_stream_t *stream); 343 | DWORD ipc3_stream_read_dword(ipc3_stream_t *stream); 344 | ES_UINT64 ipc3_stream_read_uint64(ipc3_stream_t *stream); 345 | SIZE_T ipc3_stream_read_size_t(ipc3_stream_t *stream); 346 | SIZE_T ipc3_stream_read_len_vlq(ipc3_stream_t *stream); 347 | BOOL ipc3_read_pipe(HANDLE pipe_handle,void *buf,SIZE_T buf_size); 348 | BOOL ipc3_skip_pipe(HANDLE pipe_handle,SIZE_T buf_size); 349 | HANDLE ipc3_connect_pipe(void); 350 | BOOL ipc3_ioctl(HANDLE pipe_handle,int command,const void *in_buf,SIZE_T in_size,void *out_buf,SIZE_T out_size,SIZE_T *out_numread); 351 | BOOL ipc3_ioctl_expect_output_size(HANDLE pipe_handle,int command,const void *in_buf,SIZE_T in_size,void *out_buf,SIZE_T out_size); 352 | BOOL ipc3_ioctl_alloc_out(HANDLE pipe_handle,int command,const void *in_buf,SIZE_T in_size,utf8_buf_t *out_cbuf); 353 | void ipc3_get_pipe_name(wchar_buf_t *out_wcbuf); 354 | void ipc3_stream_pipe_init(ipc3_stream_pipe_t *stream,HANDLE pipe_handle); 355 | void ipc3_stream_close(ipc3_stream_t *stream); 356 | BOOL ipc3_is_property_indexed(HANDLE pipe_handle,DWORD property_id); 357 | void ipc3_result_list_init(ipc3_result_list_t *result_list,ipc3_stream_t *stream); 358 | void ipc3_result_list_kill(ipc3_result_list_t *result_list); 359 | void ipc3_stream_pool_init(ipc3_stream_pool_t *stream,ipc3_stream_t *source_stream); 360 | void ipc3_stream_seek(ipc3_stream_t *stream,ES_UINT64 position_from_start); 361 | ES_UINT64 ipc3_stream_tell(ipc3_stream_t *stream); 362 | void ipc3_result_list_seek_to_offset_from_index(ipc3_result_list_t *result_list,SIZE_T start_index); 363 | ES_UINT64 ipc3_stream_tell(ipc3_stream_t *stream); 364 | BYTE *ipc3_copy_len_vlq(BYTE *buf,SIZE_T value); 365 | DWORD ipc3_find_property(const wchar_t *search); 366 | BOOL ipc3_get_property_canonical_name(DWORD property_id,utf8_buf_t *out_cbuf); 367 | BOOL ipc3_get_property_localized_name(DWORD property_id,utf8_buf_t *out_cbuf); 368 | BOOL ipc3_is_property_right_aligned(DWORD property_id); 369 | BOOL ipc3_is_property_sort_descending(DWORD property_id); 370 | int ipc3_get_property_default_width(DWORD property_id); 371 | BOOL ipc3_get_journal_info(ipc3_journal_info_t *out_journal_info); 372 | BOOL ipc3_read_journal(ES_UINT64 journal_id,ES_UINT64 change_id,DWORD flags,void *user_data,BOOL (*callback_proc)(void *user_data,_ipc3_journal_change_t *change)); 373 | BOOL ipc3_journal_action_is_folder(int action); 374 | int ipc3_journal_item_type_from_name(const wchar_t *name); 375 | -------------------------------------------------------------------------------- /src/mem.c: -------------------------------------------------------------------------------- 1 | 2 | // 3 | // Copyright (C) 2025 voidtools / David Carpenter 4 | // 5 | // Permission is hereby granted, free of charge, 6 | // to any person obtaining a copy of this software 7 | // and associated documentation files (the "Software"), 8 | // to deal in the Software without restriction, 9 | // including without limitation the rights to use, 10 | // copy, modify, merge, publish, distribute, sublicense, 11 | // and/or sell copies of the Software, and to permit 12 | // persons to whom the Software is furnished to do so, 13 | // subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be 16 | // included in all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 20 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 21 | // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 22 | // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 23 | // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 24 | // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | // 26 | // memory management. 27 | 28 | #include "es.h" 29 | 30 | // allocate some memory. 31 | // throws a fatal error if there is not enough memory available. 32 | // size should be <= 65536. 33 | // use a try alloc for larger sizes. 34 | // returns a pointer to the newly allocated memory. 35 | // returned memory will be garbage and will need initializing. 36 | // call mem_free to return the memory to the system. 37 | // SIZE_MAX is not a valid size and will throw an error. 38 | // use safe_size_* functions to perform safe size arithmetic. 39 | void *mem_alloc(SIZE_T size) 40 | { 41 | void *p; 42 | 43 | if (size == SIZE_MAX) 44 | { 45 | es_fatal(ES_ERROR_OUT_OF_MEMORY); 46 | } 47 | 48 | p = HeapAlloc(GetProcessHeap(),0,size); 49 | if (!p) 50 | { 51 | es_fatal(ES_ERROR_OUT_OF_MEMORY); 52 | } 53 | 54 | return p; 55 | } 56 | 57 | // same as mem_alloc, except can return NULL if there is not enought memory available. 58 | void *mem_try_alloc(SIZE_T size) 59 | { 60 | // SIZE_MAX is invalid. 61 | if (size == SIZE_MAX) 62 | { 63 | return NULL; 64 | } 65 | 66 | return HeapAlloc(GetProcessHeap(),0,size); 67 | } 68 | 69 | // return allocated memory to the system. 70 | // ptr must be a return value from mem_alloc. 71 | void mem_free(void *ptr) 72 | { 73 | HeapFree(GetProcessHeap(),0,ptr); 74 | } 75 | -------------------------------------------------------------------------------- /src/mem.h: -------------------------------------------------------------------------------- 1 | 2 | // 3 | // Copyright (C) 2025 voidtools / David Carpenter 4 | // 5 | // Permission is hereby granted, free of charge, 6 | // to any person obtaining a copy of this software 7 | // and associated documentation files (the "Software"), 8 | // to deal in the Software without restriction, 9 | // including without limitation the rights to use, 10 | // copy, modify, merge, publish, distribute, sublicense, 11 | // and/or sell copies of the Software, and to permit 12 | // persons to whom the Software is furnished to do so, 13 | // subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be 16 | // included in all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 20 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 21 | // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 22 | // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 23 | // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 24 | // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | // 26 | 27 | void *mem_alloc(SIZE_T size); 28 | void *mem_try_alloc(SIZE_T size); 29 | void mem_free(void *ptr); 30 | 31 | -------------------------------------------------------------------------------- /src/os.h: -------------------------------------------------------------------------------- 1 | 2 | // 3 | // Copyright (C) 2025 voidtools / David Carpenter 4 | // 5 | // Permission is hereby granted, free of charge, 6 | // to any person obtaining a copy of this software 7 | // and associated documentation files (the "Software"), 8 | // to deal in the Software without restriction, 9 | // including without limitation the rights to use, 10 | // copy, modify, merge, publish, distribute, sublicense, 11 | // and/or sell copies of the Software, and to permit 12 | // persons to whom the Software is furnished to do so, 13 | // subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be 16 | // included in all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 20 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 21 | // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 22 | // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 23 | // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 24 | // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | // 26 | 27 | void os_init(void); 28 | void os_kill(void); 29 | void *os_copy_memory(void *dst,const void *src,SIZE_T size); 30 | void *os_move_memory(void *dst,const void *src,SIZE_T size); 31 | void *os_zero_memory(void *dst,SIZE_T size); 32 | BOOL os_replace_file(const wchar_t *old_name,const wchar_t *new_name); 33 | HANDLE os_create_file(const wchar_t *filename); 34 | HANDLE os_open_file(const wchar_t *filename); 35 | BOOL os_write_file_utf8_string(HANDLE file_handle,const ES_UTF8 *s); 36 | BOOL os_write_file_utf8_string_n(HANDLE file_handle,const ES_UTF8 *s,SIZE_T slength_in_bytes); 37 | BOOL os_get_module_file_name(HMODULE hmod,wchar_buf_t *out_wcbuf); 38 | void os_expand_environment_variables(const wchar_t *s,wchar_buf_t *out_wcbuf); 39 | void os_get_full_path_name(const wchar_t *relative_path,wchar_buf_t *out_wcbuf); 40 | void os_get_expanded_full_path_name(const wchar_t *relative_path,wchar_buf_t *out_wcbuf); 41 | void os_sort(void **indexes,SIZE_T count,int (*comp_proc)(const void *,const void *)); 42 | BOOL os_get_special_folder_path(int nFolder,wchar_buf_t *out_wcbuf); 43 | BOOL os_get_appdata_path(wchar_buf_t *out_wcbuf); 44 | void os_make_sure_path_to_file_exists(const wchar_t *filename); 45 | void os_error_vprintf(const ES_UTF8 *format,va_list argptr); 46 | void os_error_printf(const ES_UTF8 *format,...); 47 | ES_UINT64 os_localtime_to_filetime(const SYSTEMTIME *localst); 48 | BOOL os_filetime_to_localtime(ES_UINT64 ft,SYSTEMTIME *out_localst); 49 | -------------------------------------------------------------------------------- /src/pool.c: -------------------------------------------------------------------------------- 1 | 2 | // 3 | // Copyright (C) 2025 voidtools / David Carpenter 4 | // 5 | // Permission is hereby granted, free of charge, 6 | // to any person obtaining a copy of this software 7 | // and associated documentation files (the "Software"), 8 | // to deal in the Software without restriction, 9 | // including without limitation the rights to use, 10 | // copy, modify, merge, publish, distribute, sublicense, 11 | // and/or sell copies of the Software, and to permit 12 | // persons to whom the Software is furnished to do so, 13 | // subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be 16 | // included in all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 20 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 21 | // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 22 | // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 23 | // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 24 | // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | // 26 | // pool bump allocator 27 | 28 | #include "es.h" 29 | 30 | // initialize a pool 31 | void pool_init(pool_t *buf) 32 | { 33 | buf->p = buf->stack; 34 | buf->avail = POOL_STACK_SIZE; 35 | buf->chunk_start = NULL; 36 | buf->cur_alloc_size = POOL_STACK_SIZE; 37 | } 38 | 39 | // reset a pool 40 | void pool_empty(pool_t *buf) 41 | { 42 | pool_kill(buf); 43 | pool_init(buf); 44 | } 45 | 46 | // return any allocated memory back to the system. 47 | void pool_kill(pool_t *buf) 48 | { 49 | pool_chunk_t *chunk; 50 | 51 | chunk = buf->chunk_start; 52 | 53 | while(chunk) 54 | { 55 | pool_chunk_t *next_chunk; 56 | 57 | next_chunk = chunk->next; 58 | 59 | mem_free(chunk); 60 | 61 | chunk = next_chunk; 62 | } 63 | } 64 | 65 | // allocate some memory from the pool. 66 | // memory is aligned to 8 bytes. 67 | void *pool_alloc(pool_t *buf,SIZE_T size) 68 | { 69 | void *p; 70 | SIZE_T aligned_size; 71 | 72 | aligned_size = (size + 7) & (~7); 73 | 74 | if (aligned_size > buf->avail) 75 | { 76 | SIZE_T min_alloc_size; 77 | SIZE_T alloc_size; 78 | pool_chunk_t *chunk; 79 | 80 | alloc_size = safe_size_mul_2(buf->cur_alloc_size); 81 | 82 | if (alloc_size > POOL_MAX_CHUNK_SIZE) 83 | { 84 | alloc_size = POOL_MAX_CHUNK_SIZE; 85 | } 86 | 87 | buf->cur_alloc_size = alloc_size; 88 | 89 | min_alloc_size = safe_size_add(sizeof(pool_chunk_t),aligned_size); 90 | 91 | if (alloc_size < min_alloc_size) 92 | { 93 | alloc_size = min_alloc_size; 94 | } 95 | 96 | chunk = mem_alloc(alloc_size); 97 | 98 | chunk->next = buf->chunk_start; 99 | buf->chunk_start = chunk; 100 | 101 | buf->p = POOL_CHUNK_DATA(chunk); 102 | buf->avail = alloc_size - sizeof(pool_chunk_t); 103 | } 104 | 105 | p = buf->p; 106 | 107 | buf->p += aligned_size; 108 | buf->avail -= aligned_size; 109 | 110 | return p; 111 | } 112 | -------------------------------------------------------------------------------- /src/pool.h: -------------------------------------------------------------------------------- 1 | 2 | // 3 | // Copyright (C) 2025 voidtools / David Carpenter 4 | // 5 | // Permission is hereby granted, free of charge, 6 | // to any person obtaining a copy of this software 7 | // and associated documentation files (the "Software"), 8 | // to deal in the Software without restriction, 9 | // including without limitation the rights to use, 10 | // copy, modify, merge, publish, distribute, sublicense, 11 | // and/or sell copies of the Software, and to permit 12 | // persons to whom the Software is furnished to do so, 13 | // subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be 16 | // included in all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 20 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 21 | // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 22 | // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 23 | // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 24 | // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | // 26 | 27 | #define POOL_STACK_SIZE 256 28 | #define POOL_MAX_CHUNK_SIZE 65536 29 | 30 | #define POOL_CHUNK_DATA(chunk) ((BYTE *)(((pool_chunk_t *)(chunk)) + 1)) 31 | 32 | // a chunk of memory. 33 | typedef struct pool_chunk_s 34 | { 35 | union 36 | { 37 | // the next pool in the list. 38 | struct pool_chunk_s *next; 39 | 40 | // ensure 8-byte alignment. 41 | ES_UINT64 alignment; 42 | }; 43 | 44 | // data follows. 45 | // BYTE data[]; 46 | 47 | }pool_chunk_t; 48 | 49 | // a simple pool bump allocator 50 | typedef struct pool_s 51 | { 52 | // current position 53 | BYTE *p; 54 | 55 | // bytes available at position 56 | SIZE_T avail; 57 | 58 | // chunk list. 59 | pool_chunk_t *chunk_start; 60 | 61 | // current chunk size. 62 | SIZE_T cur_alloc_size; 63 | 64 | // some stack space. 65 | // make sure this is 16-byte aligned. 66 | BYTE stack[POOL_STACK_SIZE]; 67 | 68 | }pool_t; 69 | 70 | void pool_init(pool_t *buf); 71 | void pool_empty(pool_t *buf); 72 | void pool_kill(pool_t *buf); 73 | void *pool_alloc(pool_t *buf,SIZE_T size); 74 | -------------------------------------------------------------------------------- /src/property.h: -------------------------------------------------------------------------------- 1 | 2 | // 3 | // Copyright (C) 2025 voidtools / David Carpenter 4 | // 5 | // Permission is hereby granted, free of charge, 6 | // to any person obtaining a copy of this software 7 | // and associated documentation files (the "Software"), 8 | // to deal in the Software without restriction, 9 | // including without limitation the rights to use, 10 | // copy, modify, merge, publish, distribute, sublicense, 11 | // and/or sell copies of the Software, and to permit 12 | // persons to whom the Software is furnished to do so, 13 | // subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be 16 | // included in all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 20 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 21 | // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 22 | // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 23 | // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 24 | // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | // 26 | 27 | enum 28 | { 29 | PROPERTY_FORMAT_NONE, 30 | PROPERTY_FORMAT_TEXT8, // 8 characters 31 | PROPERTY_FORMAT_TEXT10, // 10 characters 32 | PROPERTY_FORMAT_TEXT12, // 12 characters (8.3) 33 | PROPERTY_FORMAT_TEXT16, // 16 characters 34 | PROPERTY_FORMAT_TEXT24, // 24 characters 35 | PROPERTY_FORMAT_TEXT30, // 30 characters 36 | PROPERTY_FORMAT_TEXT32, // 32 characters 37 | PROPERTY_FORMAT_TEXT47, // 47 characters 38 | PROPERTY_FORMAT_TEXT48, // 48 characters 39 | PROPERTY_FORMAT_TEXT64, // 64 characters 40 | PROPERTY_FORMAT_SIZE, // 123456789 41 | PROPERTY_FORMAT_VOLUME_SIZE, // 999,999,999,999,999 42 | PROPERTY_FORMAT_EXTENSION, // 4 characters 43 | PROPERTY_FORMAT_FILETIME, // 2025-06-16 21:22:23 44 | PROPERTY_FORMAT_TIME, // 21:22:23 45 | PROPERTY_FORMAT_DATE, // 2025-06-16 46 | PROPERTY_FORMAT_ATTRIBUTES, // RASH 47 | PROPERTY_FORMAT_NOGROUPING_NUMBER1, // 1 48 | PROPERTY_FORMAT_NOGROUPING_NUMBER2, // 12 49 | PROPERTY_FORMAT_NOGROUPING_NUMBER3, // 123 50 | PROPERTY_FORMAT_NOGROUPING_NUMBER4, // 1234 (no comma) 51 | PROPERTY_FORMAT_NOGROUPING_NUMBER5, // 12345 (no comma) 52 | PROPERTY_FORMAT_GROUPING_NUMBER2, // 12 53 | PROPERTY_FORMAT_GROUPING_NUMBER3, // 123 54 | PROPERTY_FORMAT_GROUPING_NUMBER4, // 1,234 55 | PROPERTY_FORMAT_GROUPING_NUMBER5, // 12,345 56 | PROPERTY_FORMAT_GROUPING_NUMBER6, // 123,456 57 | PROPERTY_FORMAT_GROUPING_NUMBER7, // 1,234,567 58 | PROPERTY_FORMAT_KHZ, // 44.1 Khz 59 | PROPERTY_FORMAT_KBPS, // 9999 kbps 60 | PROPERTY_FORMAT_RATING, // ***** 61 | PROPERTY_FORMAT_HEX_NUMBER8, // 0xdeadbeef 62 | PROPERTY_FORMAT_HEX_NUMBER16, // 0xdeadbeefdeadbeef 63 | PROPERTY_FORMAT_HEX_NUMBER32, // 0xdeadbeefdeadbeefdeadbeefdeadbeef 64 | PROPERTY_FORMAT_DIMENSIONS, // 123x456 65 | PROPERTY_FORMAT_F_STOP, // f/9.99 66 | PROPERTY_FORMAT_EXPOSURE_TIME, // 1/350 sec 67 | PROPERTY_FORMAT_ISO_SPEED, // ISO-9999 68 | PROPERTY_FORMAT_EXPOSURE_BIAS, // +1.234 step 69 | PROPERTY_FORMAT_FOCAL_LENGTH, // 999.999 mm 70 | PROPERTY_FORMAT_SUBJECT_DISTANCE, // 999.999 m 71 | PROPERTY_FORMAT_BCPS, // 9.999 bcps 72 | PROPERTY_FORMAT_35MM_FOCAL_LENGTH, // 99999 mm 73 | PROPERTY_FORMAT_ALTITUDE, // 9999.123456 m 74 | PROPERTY_FORMAT_SEC, // 99.999 sec 75 | PROPERTY_FORMAT_FIXED_Q1K, // 0.123 76 | PROPERTY_FORMAT_FIXED_Q1M, // 0.123456 77 | PROPERTY_FORMAT_DURATION, // 1:23:45 78 | PROPERTY_FORMAT_DATA1, // data 79 | PROPERTY_FORMAT_DATA2, // data 80 | PROPERTY_FORMAT_DATA4, // crc 81 | PROPERTY_FORMAT_DATA8, // crc64 82 | PROPERTY_FORMAT_DATA16, // md5 83 | PROPERTY_FORMAT_DATA20, // sha1 84 | PROPERTY_FORMAT_DATA32, // sha256 85 | PROPERTY_FORMAT_DATA48, // sha384 86 | PROPERTY_FORMAT_DATA64, // sha512 87 | PROPERTY_FORMAT_DATA128, // data 88 | PROPERTY_FORMAT_DATA256, // data 89 | PROPERTY_FORMAT_DATA512, // data 90 | PROPERTY_FORMAT_FORMATTED_TEXT8, // formatted 8 characters 91 | PROPERTY_FORMAT_FORMATTED_TEXT12, // formatted 12 characters 92 | PROPERTY_FORMAT_FORMATTED_TEXT16, // formatted 16 characters 93 | PROPERTY_FORMAT_FORMATTED_TEXT24, // formatted 30 characters 94 | PROPERTY_FORMAT_FORMATTED_TEXT32, // formatted 30 characters 95 | PROPERTY_FORMAT_YESNO, // Yes/No 96 | PROPERTY_FORMAT_PERCENT, // 100% 97 | PROPERTY_FORMAT_ASPECT_RATIO, // 16:9 / 9.999 98 | PROPERTY_FORMAT_COUNT, 99 | }; 100 | 101 | // property name (or alias) 102 | // and proeprty ID 103 | typedef struct property_name_to_id_s 104 | { 105 | const char *name; 106 | WORD id; 107 | 108 | }property_name_to_id_t; 109 | 110 | BYTE property_get_format(DWORD property_id); 111 | BOOL property_is_right_aligned(DWORD property_id); 112 | void property_get_canonical_name(DWORD property_id,wchar_buf_t *out_wcbuf); 113 | void property_get_localized_name(DWORD property_id,wchar_buf_t *out_wcbuf); 114 | DWORD property_id_from_old_column_id(int i); 115 | DWORD property_find(const wchar_t *s,int allow_property_system); 116 | BOOL property_get_default_sort_ascending(DWORD property_id); 117 | int property_get_default_width(DWORD property_id); 118 | void property_cache_unknown_information(DWORD property_id); 119 | void property_load_read_journal_names(void); 120 | -------------------------------------------------------------------------------- /src/property_old_column_macro.h: -------------------------------------------------------------------------------- 1 | 2 | // 3 | // Copyright (C) 2025 voidtools / David Carpenter 4 | // 5 | // Permission is hereby granted, free of charge, 6 | // to any person obtaining a copy of this software 7 | // and associated documentation files (the "Software"), 8 | // to deal in the Software without restriction, 9 | // including without limitation the rights to use, 10 | // copy, modify, merge, publish, distribute, sublicense, 11 | // and/or sell copies of the Software, and to permit 12 | // persons to whom the Software is furnished to do so, 13 | // subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be 16 | // included in all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 20 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 21 | // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 22 | // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 23 | // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 24 | // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | // 26 | 27 | // #define _PROPERTY_OLD_COLUMN_MACRO(property_id) 28 | 29 | PROPERTY_OLD_COLUMN_MACRO(EVERYTHING3_PROPERTY_ID_PATH_AND_NAME) // ES_COLUMN_FILENAME, 30 | PROPERTY_OLD_COLUMN_MACRO(EVERYTHING3_PROPERTY_ID_NAME) // ES_COLUMN_NAME 31 | PROPERTY_OLD_COLUMN_MACRO(EVERYTHING3_PROPERTY_ID_PATH) // ES_COLUMN_PATH 32 | PROPERTY_OLD_COLUMN_MACRO(EVERYTHING3_PROPERTY_ID_PATH_AND_NAME) // ES_COLUMN_HIGHLIGHTED_FILENAME 33 | PROPERTY_OLD_COLUMN_MACRO(EVERYTHING3_PROPERTY_ID_NAME) // ES_COLUMN_HIGHLIGHTED_NAME 34 | PROPERTY_OLD_COLUMN_MACRO(EVERYTHING3_PROPERTY_ID_PATH) // ES_COLUMN_HIGHLIGHTED_PATH 35 | PROPERTY_OLD_COLUMN_MACRO(EVERYTHING3_PROPERTY_ID_EXTENSION) // ES_COLUMN_EXTENSION 36 | PROPERTY_OLD_COLUMN_MACRO(EVERYTHING3_PROPERTY_ID_SIZE) // ES_COLUMN_SIZE 37 | PROPERTY_OLD_COLUMN_MACRO(EVERYTHING3_PROPERTY_ID_DATE_CREATED) // ES_COLUMN_DATE_CREATED 38 | PROPERTY_OLD_COLUMN_MACRO(EVERYTHING3_PROPERTY_ID_DATE_MODIFIED) // ES_COLUMN_DATE_MODIFIED 39 | PROPERTY_OLD_COLUMN_MACRO(EVERYTHING3_PROPERTY_ID_DATE_ACCESSED) // ES_COLUMN_DATE_ACCESSED 40 | PROPERTY_OLD_COLUMN_MACRO(EVERYTHING3_PROPERTY_ID_ATTRIBUTES) // ES_COLUMN_ATTRIBUTES 41 | PROPERTY_OLD_COLUMN_MACRO(EVERYTHING3_PROPERTY_ID_FILE_LIST_NAME) // ES_COLUMN_FILE_LIST_FILENAME 42 | PROPERTY_OLD_COLUMN_MACRO(EVERYTHING3_PROPERTY_ID_RUN_COUNT) // ES_COLUMN_RUN_COUNT 43 | PROPERTY_OLD_COLUMN_MACRO(EVERYTHING3_PROPERTY_ID_DATE_RUN) // ES_COLUMN_DATE_RUN 44 | PROPERTY_OLD_COLUMN_MACRO(EVERYTHING3_PROPERTY_ID_DATE_RECENTLY_CHANGED) // ES_COLUMN_DATE_RECENTLY_CHANGED 45 | -------------------------------------------------------------------------------- /src/property_unknown.c: -------------------------------------------------------------------------------- 1 | 2 | // 3 | // Copyright (C) 2025 voidtools / David Carpenter 4 | // 5 | // Permission is hereby granted, free of charge, 6 | // to any person obtaining a copy of this software 7 | // and associated documentation files (the "Software"), 8 | // to deal in the Software without restriction, 9 | // including without limitation the rights to use, 10 | // copy, modify, merge, publish, distribute, sublicense, 11 | // and/or sell copies of the Software, and to permit 12 | // persons to whom the Software is furnished to do so, 13 | // subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be 16 | // included in all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 20 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 21 | // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 22 | // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 23 | // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 24 | // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | // 26 | // Unknown properties 27 | // property system properties. 28 | 29 | #include "es.h" 30 | 31 | static int _property_unknown_compare(const property_unknown_t *a,const void *property_id); 32 | 33 | // these must be set in main() 34 | pool_t *property_unknown_pool = NULL; // pool of property_unknown_t 35 | array_t *property_unknown_array = NULL; // array of property_unknown_t sorted by property ID. 36 | 37 | // compare two property_unknown properties by property ID. 38 | static int _property_unknown_compare(const property_unknown_t *a,const void *property_id) 39 | { 40 | if (a->property_id < (DWORD)(uintptr_t)property_id) 41 | { 42 | return -1; 43 | } 44 | 45 | if (a->property_id > (DWORD)(uintptr_t)property_id) 46 | { 47 | return 1; 48 | } 49 | 50 | return 0; 51 | } 52 | 53 | const property_unknown_t *property_unknown_find(DWORD property_id) 54 | { 55 | return array_find(property_unknown_array,_property_unknown_compare,(const void *)(uintptr_t)property_id); 56 | } 57 | 58 | // property_id >= EVERYTHING3_PROPERTY_ID_BUILTIN_COUNT 59 | const property_unknown_t *property_unknown_get(DWORD property_id) 60 | { 61 | property_unknown_t *property_unknown; 62 | SIZE_T insert_position; 63 | 64 | property_unknown = array_find_or_get_insertion_index(property_unknown_array,_property_unknown_compare,(const void *)(uintptr_t)property_id,&insert_position); 65 | if (!property_unknown) 66 | { 67 | utf8_buf_t property_canonical_name_cbuf; 68 | 69 | utf8_buf_init(&property_canonical_name_cbuf); 70 | 71 | if (ipc3_get_property_canonical_name(property_id,&property_canonical_name_cbuf)) 72 | { 73 | SIZE_T unknown_size; 74 | 75 | unknown_size = sizeof(property_unknown_t); 76 | unknown_size = safe_size_add(unknown_size,safe_size_add_one(property_canonical_name_cbuf.length_in_bytes)); 77 | 78 | property_unknown = pool_alloc(property_unknown_pool,unknown_size); 79 | 80 | property_unknown->canonical_name_len = property_canonical_name_cbuf.length_in_bytes; 81 | property_unknown->property_id = property_id; 82 | property_unknown->is_right_aligned = ipc3_is_property_right_aligned(property_id); 83 | property_unknown->is_sort_descending = ipc3_is_property_sort_descending(property_id); 84 | property_unknown->default_width = ipc3_get_property_default_width(property_id); 85 | 86 | utf8_string_copy_utf8_string_n(PROPERTY_UNKNOWN_CANONICAL_NAME(property_unknown),property_canonical_name_cbuf.buf,property_canonical_name_cbuf.length_in_bytes); 87 | 88 | array_insert(property_unknown_array,insert_position,property_unknown); 89 | } 90 | 91 | utf8_buf_kill(&property_canonical_name_cbuf); 92 | } 93 | 94 | return property_unknown; 95 | } 96 | 97 | void property_unknown_clear_all(void) 98 | { 99 | array_empty(property_unknown_array); 100 | pool_empty(property_unknown_pool); 101 | } 102 | -------------------------------------------------------------------------------- /src/property_unknown.h: -------------------------------------------------------------------------------- 1 | 2 | // 3 | // Copyright (C) 2025 voidtools / David Carpenter 4 | // 5 | // Permission is hereby granted, free of charge, 6 | // to any person obtaining a copy of this software 7 | // and associated documentation files (the "Software"), 8 | // to deal in the Software without restriction, 9 | // including without limitation the rights to use, 10 | // copy, modify, merge, publish, distribute, sublicense, 11 | // and/or sell copies of the Software, and to permit 12 | // persons to whom the Software is furnished to do so, 13 | // subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be 16 | // included in all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 20 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 21 | // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 22 | // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 23 | // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 24 | // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | // 26 | 27 | #define PROPERTY_UNKNOWN_CANONICAL_NAME(unknown) ((ES_UTF8 *)(((property_unknown_t *)(unknown)) + 1)) 28 | 29 | // an unknown property. 30 | typedef struct property_unknown_s 31 | { 32 | SIZE_T canonical_name_len; 33 | DWORD property_id; 34 | int is_right_aligned; 35 | int is_sort_descending; 36 | 37 | // width in logical pixels. 38 | int default_width; 39 | 40 | // NULL terminated canonical name follows. 41 | // ES_UTF8 canonical_name[canonical_name_len + 1]; 42 | 43 | }property_unknown_t; 44 | 45 | const property_unknown_t *property_unknown_find(DWORD property_id); 46 | const property_unknown_t *property_unknown_get(DWORD property_id); 47 | void property_unknown_clear_all(void); 48 | 49 | extern pool_t *property_unknown_pool; // pool of property_unknown_t 50 | extern array_t *property_unknown_array; // array of property_unknown_t 51 | -------------------------------------------------------------------------------- /src/safe_int.c: -------------------------------------------------------------------------------- 1 | 2 | // 3 | // Copyright (C) 2025 voidtools / David Carpenter 4 | // 5 | // Permission is hereby granted, free of charge, 6 | // to any person obtaining a copy of this software 7 | // and associated documentation files (the "Software"), 8 | // to deal in the Software without restriction, 9 | // including without limitation the rights to use, 10 | // copy, modify, merge, publish, distribute, sublicense, 11 | // and/or sell copies of the Software, and to permit 12 | // persons to whom the Software is furnished to do so, 13 | // subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be 16 | // included in all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 20 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 21 | // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 22 | // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 23 | // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 24 | // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | // 26 | // safe int arithmetic. 27 | 28 | #include "es.h" 29 | 30 | // returns a safe int from a size_t. 31 | // INT_MAX should be treated as an invalid value. 32 | int safe_int_from_size(SIZE_T a) 33 | { 34 | if (a <= INT_MAX) 35 | { 36 | return (int)a; 37 | } 38 | 39 | return INT_MAX; 40 | } 41 | -------------------------------------------------------------------------------- /src/safe_int.h: -------------------------------------------------------------------------------- 1 | 2 | // 3 | // Copyright (C) 2025 voidtools / David Carpenter 4 | // 5 | // Permission is hereby granted, free of charge, 6 | // to any person obtaining a copy of this software 7 | // and associated documentation files (the "Software"), 8 | // to deal in the Software without restriction, 9 | // including without limitation the rights to use, 10 | // copy, modify, merge, publish, distribute, sublicense, 11 | // and/or sell copies of the Software, and to permit 12 | // persons to whom the Software is furnished to do so, 13 | // subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be 16 | // included in all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 20 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 21 | // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 22 | // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 23 | // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 24 | // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | // 26 | 27 | int safe_int_from_size(SIZE_T a); 28 | -------------------------------------------------------------------------------- /src/safe_size.c: -------------------------------------------------------------------------------- 1 | 2 | // 3 | // Copyright (C) 2025 voidtools / David Carpenter 4 | // 5 | // Permission is hereby granted, free of charge, 6 | // to any person obtaining a copy of this software 7 | // and associated documentation files (the "Software"), 8 | // to deal in the Software without restriction, 9 | // including without limitation the rights to use, 10 | // copy, modify, merge, publish, distribute, sublicense, 11 | // and/or sell copies of the Software, and to permit 12 | // persons to whom the Software is furnished to do so, 13 | // subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be 16 | // included in all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 20 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 21 | // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 22 | // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 23 | // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 24 | // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | // 26 | // safe size arithmetic. 27 | // MUST be used when allocating memory. 28 | 29 | #include "es.h" 30 | 31 | // safely add two values. 32 | // SIZE_MAX is used as an invalid value. 33 | // you will be unable to allocate SIZE_MAX bytes. 34 | // always use safe_size when allocating memory. 35 | SIZE_T safe_size_add(SIZE_T a,SIZE_T b) 36 | { 37 | SIZE_T c; 38 | 39 | c = a + b; 40 | 41 | if (c < a) 42 | { 43 | return SIZE_MAX; 44 | } 45 | 46 | return c; 47 | } 48 | 49 | // safely add one to a value. 50 | // returns a + 1 51 | // returns SIZE_MAX if an overflow occurs. 52 | SIZE_T safe_size_add_one(SIZE_T a) 53 | { 54 | return safe_size_add(a,1); 55 | } 56 | 57 | // safely multiply a by sizeof(void *) 58 | // returns a * sizeof(void *) 59 | // returns SIZE_MAX if an overflow occurs. 60 | SIZE_T safe_size_mul_sizeof_pointer(SIZE_T a) 61 | { 62 | SIZE_T c; 63 | 64 | c = safe_size_add(a,a); // x2 65 | c = safe_size_add(c,c); // x4 66 | 67 | #if SIZE_MAX == 0xFFFFFFFFFFFFFFFFUI64 68 | 69 | c = safe_size_add(c,c); // x8 70 | 71 | #elif SIZE_MAX == 0xFFFFFFFF 72 | 73 | #else 74 | 75 | #error unknown SIZE_MAX 76 | 77 | #endif 78 | 79 | return c; 80 | } 81 | 82 | // safely multiply a by sizeof(wchar_t) 83 | // returns a + sizeof(wchar_t) 84 | // returns SIZE_MAX if an overflow occurs. 85 | SIZE_T safe_size_mul_sizeof_wchar(SIZE_T a) 86 | { 87 | return safe_size_add(a,a); // x2 88 | } 89 | 90 | // safely multiply a by 2 91 | // returns a * 2 92 | // returns SIZE_MAX if an overflow occurs. 93 | SIZE_T safe_size_mul_2(SIZE_T a) 94 | { 95 | return safe_size_add(a,a); // x2 96 | } 97 | 98 | // safely multiply a by b 99 | // returns a * b 100 | // returns SIZE_MAX if an overflow occurs. 101 | SIZE_T safe_size_mul(SIZE_T a,SIZE_T b) 102 | { 103 | if (b == 0) 104 | { 105 | return 0; 106 | } 107 | 108 | if (a > SIZE_MAX / b) 109 | { 110 | return SIZE_MAX; 111 | } 112 | 113 | return a * b; 114 | } 115 | 116 | // convert a UINT64 value to a SIZE_T value. 117 | // returns the converted SIZE_T value. 118 | // if the value overflows, returns SIZE_MAX. 119 | SIZE_T safe_size_from_uint64(ES_UINT64 a) 120 | { 121 | if (a <= SIZE_MAX) 122 | { 123 | return (SIZE_T)a; 124 | } 125 | 126 | return SIZE_MAX; 127 | } 128 | -------------------------------------------------------------------------------- /src/safe_size.h: -------------------------------------------------------------------------------- 1 | 2 | // 3 | // Copyright (C) 2025 voidtools / David Carpenter 4 | // 5 | // Permission is hereby granted, free of charge, 6 | // to any person obtaining a copy of this software 7 | // and associated documentation files (the "Software"), 8 | // to deal in the Software without restriction, 9 | // including without limitation the rights to use, 10 | // copy, modify, merge, publish, distribute, sublicense, 11 | // and/or sell copies of the Software, and to permit 12 | // persons to whom the Software is furnished to do so, 13 | // subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be 16 | // included in all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 20 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 21 | // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 22 | // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 23 | // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 24 | // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | // 26 | 27 | SIZE_T safe_size_add(SIZE_T a,SIZE_T b); 28 | SIZE_T safe_size_add_one(SIZE_T a); 29 | SIZE_T safe_size_mul_sizeof_pointer(SIZE_T a); 30 | SIZE_T safe_size_mul_sizeof_wchar(SIZE_T a); 31 | SIZE_T safe_size_mul_2(SIZE_T a); 32 | SIZE_T safe_size_mul(SIZE_T a,SIZE_T b); 33 | SIZE_T safe_size_from_uint64(ES_UINT64 a); 34 | -------------------------------------------------------------------------------- /src/secondary_sort.c: -------------------------------------------------------------------------------- 1 | 2 | // 3 | // Copyright (C) 2025 voidtools / David Carpenter 4 | // 5 | // Permission is hereby granted, free of charge, 6 | // to any person obtaining a copy of this software 7 | // and associated documentation files (the "Software"), 8 | // to deal in the Software without restriction, 9 | // including without limitation the rights to use, 10 | // copy, modify, merge, publish, distribute, sublicense, 11 | // and/or sell copies of the Software, and to permit 12 | // persons to whom the Software is furnished to do so, 13 | // subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be 16 | // included in all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 20 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 21 | // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 22 | // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 23 | // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 24 | // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | // 26 | // sorts 27 | 28 | #include "es.h" 29 | 30 | // pool and array must be set in main() 31 | pool_t *secondary_sort_pool = NULL; // pool of secondary_sort_t 32 | array_t *secondary_sort_array = NULL; // array of secondary_sort_t sorted by property id. 33 | secondary_sort_t *secondary_sort_start = NULL; 34 | secondary_sort_t *secondary_sort_last = NULL; 35 | 36 | // compare two secondary sorts by property ID. 37 | int secondary_sort_compare(const secondary_sort_t *a,const void *property_id) 38 | { 39 | if (a->property_id < (DWORD)(uintptr_t)property_id) 40 | { 41 | return -1; 42 | } 43 | 44 | if (a->property_id > (DWORD)(uintptr_t)property_id) 45 | { 46 | return 1; 47 | } 48 | 49 | return 0; 50 | } 51 | 52 | // add a secondarry sort 53 | // does nothing if the secondary sort already exists. 54 | // returns the new secondary sort or the existing secondary sort. 55 | secondary_sort_t *secondary_sort_add(DWORD property_id,int ascending) 56 | { 57 | secondary_sort_t *sort; 58 | SIZE_T insert_index; 59 | 60 | sort = array_find_or_get_insertion_index(secondary_sort_array,secondary_sort_compare,(const void *)(uintptr_t)property_id,&insert_index); 61 | if (!sort) 62 | { 63 | // alloc 64 | sort = pool_alloc(secondary_sort_pool,sizeof(secondary_sort_t)); 65 | 66 | // init 67 | sort->property_id = property_id; 68 | sort->ascending = ascending; 69 | 70 | // insert 71 | array_insert(secondary_sort_array,insert_index,sort); 72 | 73 | if (secondary_sort_start) 74 | { 75 | secondary_sort_last->next = sort; 76 | } 77 | else 78 | { 79 | secondary_sort_start = sort; 80 | } 81 | 82 | secondary_sort_last = sort; 83 | sort->next = NULL; 84 | } 85 | 86 | return sort; 87 | } 88 | 89 | // clears all secondary sorts. 90 | // empties the array and pools. 91 | // sets the sort list to an empty list. 92 | void secondary_sort_clear_all(void) 93 | { 94 | array_empty(secondary_sort_array); 95 | pool_empty(secondary_sort_pool); 96 | 97 | secondary_sort_start = NULL; 98 | secondary_sort_last = NULL; 99 | } 100 | 101 | -------------------------------------------------------------------------------- /src/secondary_sort.h: -------------------------------------------------------------------------------- 1 | 2 | // 3 | // Copyright (C) 2025 voidtools / David Carpenter 4 | // 5 | // Permission is hereby granted, free of charge, 6 | // to any person obtaining a copy of this software 7 | // and associated documentation files (the "Software"), 8 | // to deal in the Software without restriction, 9 | // including without limitation the rights to use, 10 | // copy, modify, merge, publish, distribute, sublicense, 11 | // and/or sell copies of the Software, and to permit 12 | // persons to whom the Software is furnished to do so, 13 | // subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be 16 | // included in all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 20 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 21 | // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 22 | // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 23 | // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 24 | // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | // 26 | 27 | typedef struct secondary_sort_s 28 | { 29 | // next secondary sort. 30 | struct secondary_sort_s *next; 31 | 32 | // EVERYTHING3_PROPERTY_ID_* 33 | DWORD property_id; 34 | 35 | // <0 == descending 36 | // 0 == use default 37 | // >0 == ascending. 38 | int ascending; 39 | 40 | }secondary_sort_t; 41 | 42 | secondary_sort_t *secondary_sort_add(DWORD property_id,int ascending); 43 | void secondary_sort_clear_all(void); 44 | 45 | extern pool_t *secondary_sort_pool; // pool of secondary_sort_t 46 | extern array_t *secondary_sort_array; // array of secondary_sort_t sorted by property id. 47 | extern secondary_sort_t *secondary_sort_start; // sort order 48 | extern secondary_sort_t *secondary_sort_last; 49 | -------------------------------------------------------------------------------- /src/unicode.c: -------------------------------------------------------------------------------- 1 | 2 | // 3 | // Copyright (C) 2025 voidtools / David Carpenter 4 | // 5 | // Permission is hereby granted, free of charge, 6 | // to any person obtaining a copy of this software 7 | // and associated documentation files (the "Software"), 8 | // to deal in the Software without restriction, 9 | // including without limitation the rights to use, 10 | // copy, modify, merge, publish, distribute, sublicense, 11 | // and/or sell copies of the Software, and to permit 12 | // persons to whom the Software is furnished to do so, 13 | // subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be 16 | // included in all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 20 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 21 | // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 22 | // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 23 | // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 24 | // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | // 26 | // Unicode functions 27 | 28 | #include "es.h" 29 | 30 | // ASCII only white space. 31 | BOOL unicode_is_ascii_ws(int c) 32 | { 33 | if ((c == ' ') || (c == '\t') || (c == '\r') || (c == '\n')) 34 | { 35 | return TRUE; 36 | } 37 | 38 | return FALSE; 39 | } 40 | 41 | // convert ASCII character to lowercase. 42 | int unicode_ascii_to_lower(int c) 43 | { 44 | if ((c >= 'A') && (c <= 'Z')) 45 | { 46 | return c - 'A' + 'a'; 47 | } 48 | 49 | return c; 50 | } 51 | 52 | // get the hex character for the specified value (0-15) 53 | int unicode_hex_char(int value) 54 | { 55 | if ((value >= 0) && (value < 10)) 56 | { 57 | return value + '0'; 58 | } 59 | 60 | return value - 10 + 'A'; 61 | } -------------------------------------------------------------------------------- /src/unicode.h: -------------------------------------------------------------------------------- 1 | 2 | // 3 | // Copyright (C) 2025 voidtools / David Carpenter 4 | // 5 | // Permission is hereby granted, free of charge, 6 | // to any person obtaining a copy of this software 7 | // and associated documentation files (the "Software"), 8 | // to deal in the Software without restriction, 9 | // including without limitation the rights to use, 10 | // copy, modify, merge, publish, distribute, sublicense, 11 | // and/or sell copies of the Software, and to permit 12 | // persons to whom the Software is furnished to do so, 13 | // subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be 16 | // included in all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 20 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 21 | // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 22 | // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 23 | // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 24 | // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | // 26 | 27 | BOOL unicode_is_ascii_ws(int c); 28 | int unicode_ascii_to_lower(int c); 29 | int unicode_hex_char(int value); 30 | -------------------------------------------------------------------------------- /src/utf8_buf.c: -------------------------------------------------------------------------------- 1 | 2 | // 3 | // Copyright (C) 2025 voidtools / David Carpenter 4 | // 5 | // Permission is hereby granted, free of charge, 6 | // to any person obtaining a copy of this software 7 | // and associated documentation files (the "Software"), 8 | // to deal in the Software without restriction, 9 | // including without limitation the rights to use, 10 | // copy, modify, merge, publish, distribute, sublicense, 11 | // and/or sell copies of the Software, and to permit 12 | // persons to whom the Software is furnished to do so, 13 | // subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be 16 | // included in all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 20 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 21 | // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 22 | // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 23 | // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 24 | // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | // 26 | // UTF-8 buffer 27 | 28 | #include "es.h" 29 | 30 | #define UTF8_BUF_CAT_MIN_ALLOC_SIZE 65536 31 | 32 | static ES_UTF8 *_utf8_buf_get_format_number(int base,int abase,ES_UTF8 *buf,ES_UTF8 *d,int sign,int paddingchar,SIZE_T padding,ES_UINT64 number); 33 | static ES_UTF8 *_utf8_buf_get_vprintf(ES_UTF8 *buf,const ES_UTF8 *format,va_list argptr); 34 | 35 | // Init a utf8 buffer to an empty string. 36 | void utf8_buf_init(utf8_buf_t *cbuf) 37 | { 38 | cbuf->buf = cbuf->stack_buf; 39 | cbuf->length_in_bytes = 0; 40 | cbuf->size_in_bytes = UTF8_BUF_STACK_SIZE; 41 | cbuf->buf[0] = 0; 42 | } 43 | 44 | // Kill the UTF-8 buffer, releasing any allocated memory back to the system. 45 | void utf8_buf_kill(utf8_buf_t *cbuf) 46 | { 47 | if (cbuf->buf != cbuf->stack_buf) 48 | { 49 | mem_free(cbuf->buf); 50 | } 51 | } 52 | 53 | // Empty the UTF-8 buffer, the buffer will be set to an empty string. 54 | void utf8_buf_empty(utf8_buf_t *cbuf) 55 | { 56 | cbuf->buf[0] = 0; 57 | cbuf->length_in_bytes = 0; 58 | } 59 | 60 | // doesn't keep the existing text. 61 | // doesn't set the text, only sets the length. 62 | void utf8_buf_grow_size(utf8_buf_t *cbuf,SIZE_T size_in_bytes) 63 | { 64 | if (size_in_bytes <= cbuf->size_in_bytes) 65 | { 66 | // already enough room 67 | } 68 | else 69 | { 70 | utf8_buf_empty(cbuf); 71 | 72 | cbuf->buf = mem_alloc(size_in_bytes); 73 | cbuf->size_in_bytes = size_in_bytes; 74 | } 75 | } 76 | // doesn't keep the existing text. 77 | // doesn't set the text, only sets the length. 78 | BOOL utf8_buf_try_grow_size(utf8_buf_t *cbuf,SIZE_T size_in_bytes) 79 | { 80 | if (size_in_bytes <= cbuf->size_in_bytes) 81 | { 82 | // already enough room 83 | return TRUE; 84 | } 85 | else 86 | { 87 | ES_UTF8 *new_buf; 88 | 89 | utf8_buf_empty(cbuf); 90 | 91 | new_buf = mem_try_alloc(size_in_bytes); 92 | if (new_buf) 93 | { 94 | cbuf->buf = new_buf; 95 | cbuf->size_in_bytes = size_in_bytes; 96 | 97 | return TRUE; 98 | } 99 | } 100 | 101 | return FALSE; 102 | } 103 | 104 | // doesn't keep the existing text. 105 | // doesn't set the text, only sets the length. 106 | void utf8_buf_grow_length(utf8_buf_t *cbuf,SIZE_T length_in_bytes) 107 | { 108 | utf8_buf_grow_size(cbuf,safe_size_add(length_in_bytes,1)); 109 | 110 | cbuf->length_in_bytes = length_in_bytes; 111 | } 112 | 113 | // copy a wide string into a UTF-8 buffer. 114 | // returns TRUE on success. Otherwise FALSE if there's not enough memory. 115 | void utf8_buf_copy_wchar_string(utf8_buf_t *cbuf,const wchar_t *ws) 116 | { 117 | utf8_buf_grow_length(cbuf,utf8_string_get_length_in_bytes_from_wchar_string(ws)); 118 | 119 | utf8_string_copy_wchar_string(cbuf->buf,ws); 120 | } 121 | 122 | // get a formatted number. 123 | // returns the buffer location after formatting the number. 124 | // if buf is NULL, returns the required buffer length in bytes. (not including the NULL terminator) 125 | static ES_UTF8 *_utf8_buf_get_format_number(int base,int abase,ES_UTF8 *buf,ES_UTF8 *dstart,int sign,int paddingchar,SIZE_T padding,ES_UINT64 number) 126 | { 127 | ES_UTF8 *d; 128 | ES_UINT64 i; 129 | SIZE_T dp; 130 | 131 | d = dstart; 132 | dp = 0; 133 | 134 | // get len 135 | i = number; 136 | if (i) 137 | { 138 | loop1: 139 | i /= base; 140 | dp++; 141 | 142 | if (i) 143 | { 144 | goto loop1; 145 | } 146 | } 147 | else 148 | { 149 | // 0 150 | dp = 1; 151 | } 152 | 153 | // do padding. 154 | if (paddingchar != '0') 155 | { 156 | if (padding > dp) 157 | { 158 | padding -= dp; 159 | 160 | if (buf) 161 | { 162 | while(padding) 163 | { 164 | *d++ = paddingchar; 165 | 166 | padding--; 167 | } 168 | } 169 | else 170 | { 171 | d = (void *)safe_size_add((uintptr_t)d,padding); 172 | } 173 | } 174 | } 175 | 176 | // sign is left aligned. 177 | if (sign) 178 | { 179 | if (buf) 180 | { 181 | *d++ = '-'; 182 | } 183 | else 184 | { 185 | d = (void *)safe_size_add_one((SIZE_T)d); 186 | } 187 | 188 | padding--; 189 | } 190 | 191 | // do padding. 192 | if (paddingchar == '0') 193 | { 194 | if (padding > dp) 195 | { 196 | padding -= dp; 197 | 198 | if (buf) 199 | { 200 | while(padding) 201 | { 202 | *d++ = paddingchar; 203 | 204 | padding--; 205 | } 206 | } 207 | else 208 | { 209 | d = (void *)safe_size_add((uintptr_t)d,padding); 210 | } 211 | } 212 | } 213 | 214 | // write the number. 215 | if (buf) 216 | { 217 | i = number; 218 | if (i) 219 | { 220 | 221 | loop2: 222 | dp--; 223 | 224 | if ((i % base) >= 10) 225 | { 226 | d[dp] = (int)(i % base) + abase - 10; 227 | } 228 | else 229 | { 230 | d[dp] = (int)(i % base) + '0'; 231 | } 232 | 233 | d++; 234 | 235 | i /= base; 236 | dp--; 237 | 238 | if (i) goto loop2; 239 | } 240 | else 241 | { 242 | // 0 243 | *d++ = '0'; 244 | } 245 | } 246 | else 247 | { 248 | // dp is a small number and is safe. 249 | d = (void *)safe_size_add((uintptr_t)d,dp); 250 | } 251 | 252 | return d; 253 | } 254 | 255 | // get printf output 256 | static ES_UTF8 *_utf8_buf_get_vprintf(ES_UTF8 *buf,const ES_UTF8 *format,va_list argptr) 257 | { 258 | ES_UTF8 *d; 259 | const ES_UTF8 *p; 260 | SIZE_T padding; 261 | SIZE_T decimal_places; 262 | int paddingchar; 263 | 264 | d = buf; 265 | p = format; 266 | 267 | while(*p) 268 | { 269 | if (*p == '%') 270 | { 271 | p++; 272 | 273 | // [0padding] | [padding] 274 | 275 | // get padding char. 276 | if (*p == '0') 277 | { 278 | paddingchar = '0'; 279 | } 280 | else 281 | { 282 | paddingchar = ' '; 283 | } 284 | 285 | // get padding 286 | padding = 0; 287 | 288 | while(*p) 289 | { 290 | if ((*p >= '0') && (*p <= '9')) 291 | { 292 | padding *= 10; 293 | padding += *p - '0'; 294 | } 295 | else 296 | { 297 | break; 298 | } 299 | 300 | p++; 301 | } 302 | 303 | // decimal places. 304 | decimal_places = 6; 305 | 306 | if (*p == '.') 307 | { 308 | p++; 309 | 310 | decimal_places = 0; 311 | 312 | while(*p) 313 | { 314 | if ((*p >= '0') && (*p <= '9')) 315 | { 316 | decimal_places *= 10; 317 | decimal_places += *p - '0'; 318 | } 319 | else 320 | { 321 | break; 322 | } 323 | 324 | p++; 325 | } 326 | 327 | if (decimal_places > 6) 328 | { 329 | decimal_places = 6; 330 | } 331 | } 332 | 333 | // calculate size.. 334 | if (*p == '%') 335 | { 336 | if (buf) 337 | { 338 | *d++ = *p; 339 | } 340 | else 341 | { 342 | d = (void *)safe_size_add_one((SIZE_T)d); 343 | } 344 | 345 | p++; 346 | } 347 | else 348 | if (*p == 'd') 349 | { 350 | __int64 num; 351 | 352 | num = va_arg(argptr,int); 353 | 354 | // we use an int64 above so we can convert -2147483648 to 2147483648 (note that the largest value for an int is 2147483647) 355 | // if we used 32bits -(-2147483648) == -2147483648 356 | d = _utf8_buf_get_format_number(10,0,buf,d,(num < 0),paddingchar,padding,(num<0)?-num:num); 357 | 358 | p++; 359 | } 360 | else 361 | if (*p == 'u') 362 | { 363 | unsigned int num; 364 | 365 | num = va_arg(argptr,unsigned int); 366 | 367 | d = _utf8_buf_get_format_number(10,0,buf,d,0,paddingchar,padding,num); 368 | 369 | p++; 370 | } 371 | else 372 | if ((*p == 'z') && (p[1] == 'u')) 373 | { 374 | SIZE_T num; 375 | 376 | num = va_arg(argptr,SIZE_T); 377 | 378 | d = _utf8_buf_get_format_number(10,0,buf,d,0,paddingchar,padding,num); 379 | 380 | p+=2; 381 | } 382 | else 383 | if ((*p == 'z') && (p[1] == 'd')) 384 | { 385 | intptr_t num; 386 | 387 | num = va_arg(argptr,intptr_t); 388 | 389 | // == 0x80000000 (32bit) 390 | // special case for -0x80000000 == 0x80000000 391 | if ((SIZE_T)num == (SIZE_MAX>>1)+1) 392 | { 393 | d = _utf8_buf_get_format_number(10,0,buf,d,1,paddingchar,padding,(SIZE_MAX>>1)+1); 394 | } 395 | else 396 | { 397 | d = _utf8_buf_get_format_number(10,0,buf,d,(num<0),paddingchar,padding,(num<0)?-num:num); 398 | } 399 | 400 | p+=2; 401 | } 402 | else 403 | if ((*p == 'I') && (p[1] == '6') && (p[2] == '4') && (p[3] == 'd')) 404 | { 405 | __int64 num; 406 | 407 | num = va_arg(argptr,__int64); 408 | 409 | // special case for 0x8000000000000000UI64, since we cant remove the sign (eg: -9223372036854775808 to 9223372036854775808) because 9223372036854775807 is the largest signed __int64. 410 | if ((ES_UINT64)num == (ES_UINT64)0x8000000000000000UI64) 411 | { 412 | d = _utf8_buf_get_format_number(10,0,buf,d,1,paddingchar,padding,(ES_UINT64)0x8000000000000000UI64); 413 | } 414 | else 415 | { 416 | d = _utf8_buf_get_format_number(10,0,buf,d,(num<0),paddingchar,padding,(num<0)?-num:num); 417 | } 418 | 419 | p+=4; 420 | } 421 | else 422 | if ((*p == 'I') && (p[1] == '6') && (p[2] == '4') && (p[3] == 'u')) 423 | { 424 | ES_UINT64 num; 425 | 426 | num = va_arg(argptr,ES_UINT64); 427 | 428 | d = _utf8_buf_get_format_number(10,0,buf,d,0,paddingchar,padding,num); 429 | 430 | p+=4; 431 | } 432 | else 433 | if ((*p == 'I') && (p[1] == '6') && (p[2] == '4') && ((p[3] == 'x') || (p[3] == 'X'))) 434 | { 435 | ES_UINT64 num; 436 | 437 | num = va_arg(argptr,ES_UINT64); 438 | 439 | d = _utf8_buf_get_format_number(16,p[3]+'A'-'X',buf,d,0,paddingchar,padding,num); 440 | 441 | p+=4; 442 | } 443 | else 444 | if ((*p == 'x') || (*p == 'X')) 445 | { 446 | unsigned int num; 447 | 448 | num = va_arg(argptr,unsigned int); 449 | 450 | d = _utf8_buf_get_format_number(16,*p+'A'-'X',buf,d,0,paddingchar,padding,num); 451 | 452 | p++; 453 | } 454 | else 455 | if ((*p == 'p') || (*p == 'P')) 456 | { 457 | SIZE_T num; 458 | 459 | num = va_arg(argptr,SIZE_T); 460 | 461 | d = _utf8_buf_get_format_number(16,*p+'A'-'P',buf,d,0,'0',8<<(sizeof(SIZE_T)>>3),num); 462 | 463 | #if SIZE_MAX == 0xffffffffffffffffui64 464 | #elif SIZE_MAX == 0xffffffffui32 465 | #else 466 | #error unknown SIZE_MAX 467 | #endif 468 | 469 | p++; 470 | } 471 | else 472 | if (*p == 's') 473 | { 474 | const ES_UTF8 *str; 475 | 476 | str = va_arg(argptr,const ES_UTF8 *); 477 | 478 | if (buf) 479 | { 480 | while(*str) 481 | { 482 | *d++ = *str; 483 | 484 | str++; 485 | } 486 | } 487 | else 488 | { 489 | d = (void *)safe_size_add((uintptr_t)d,utf8_string_get_length_in_bytes(str)); 490 | } 491 | 492 | p++; 493 | } 494 | else 495 | if (*p == 'S') 496 | { 497 | const wchar_t *wstr; 498 | 499 | wstr = va_arg(argptr,const wchar_t *); 500 | 501 | if (buf) 502 | { 503 | d = utf8_string_copy_wchar_string(d,wstr); 504 | } 505 | else 506 | { 507 | d = (void *)safe_size_add((uintptr_t)d,(SIZE_T)utf8_string_get_length_in_bytes_from_wchar_string(wstr)); 508 | } 509 | 510 | p++; 511 | } 512 | else 513 | if (*p == 'c') 514 | { 515 | int ch; 516 | 517 | // UTF-8 BYTE 518 | ch = va_arg(argptr,int); 519 | 520 | if (buf) 521 | { 522 | *d++ = ch; 523 | } 524 | else 525 | { 526 | d = (void *)safe_size_add_one((SIZE_T)d); 527 | } 528 | 529 | p++; 530 | } 531 | else 532 | if (*p == 'C') 533 | { 534 | int ch; 535 | wchar_t wch[2]; 536 | 537 | // ch MUST be ASCII 538 | ch = va_arg(argptr,int); 539 | 540 | wch[0] = (wchar_t)ch; 541 | wch[1] = 0; 542 | 543 | if (buf) 544 | { 545 | d = utf8_string_copy_wchar_string(d,wch); 546 | } 547 | else 548 | { 549 | d = (void *)safe_size_add((uintptr_t)d,(SIZE_T)utf8_string_get_length_in_bytes_from_wchar_string(wch)); 550 | } 551 | 552 | p++; 553 | } 554 | else 555 | { 556 | // ignore it. 557 | p++; 558 | } 559 | } 560 | else 561 | { 562 | if (buf) 563 | { 564 | *d++ = *p; 565 | } 566 | else 567 | { 568 | d = (void *)safe_size_add_one((SIZE_T)d); 569 | } 570 | 571 | p++; 572 | } 573 | } 574 | 575 | if (buf) 576 | { 577 | *d = 0; 578 | } 579 | 580 | return d; 581 | } 582 | 583 | // vsprintf 584 | void utf8_buf_vprintf(utf8_buf_t *cbuf,const ES_UTF8 *format,va_list argptr) 585 | { 586 | utf8_buf_grow_length(cbuf,(SIZE_T)_utf8_buf_get_vprintf(NULL,format,argptr)); 587 | 588 | _utf8_buf_get_vprintf(cbuf->buf,format,argptr); 589 | } 590 | 591 | // sprintf 592 | void utf8_buf_printf(utf8_buf_t *cbuf,const ES_UTF8 *format,...) 593 | { 594 | va_list argptr; 595 | 596 | va_start(argptr,format); 597 | 598 | utf8_buf_vprintf(cbuf,format,argptr); 599 | 600 | va_end(argptr); 601 | } 602 | 603 | // copy a simple utf8 string with the specified length. 604 | void utf8_buf_copy_utf8_string_n(utf8_buf_t *cbuf,const ES_UTF8 *s,SIZE_T length_in_bytes) 605 | { 606 | utf8_buf_grow_length(cbuf,length_in_bytes); 607 | 608 | os_copy_memory(cbuf->buf,s,length_in_bytes); 609 | 610 | cbuf->buf[length_in_bytes] = 0; 611 | } 612 | 613 | // copy a simple utf8 string 614 | void utf8_buf_copy_utf8_string(utf8_buf_t *cbuf,const ES_UTF8 *s) 615 | { 616 | utf8_buf_copy_utf8_string_n(cbuf,s,utf8_string_get_length_in_bytes(s)); 617 | } 618 | 619 | void utf8_buf_cat_utf8_string_n(utf8_buf_t *cbuf,const ES_UTF8 *s,SIZE_T slength_in_bytes) 620 | { 621 | SIZE_T size_in_bytes; 622 | SIZE_T length_in_bytes; 623 | 624 | size_in_bytes = cbuf->length_in_bytes; 625 | size_in_bytes = safe_size_add(size_in_bytes,slength_in_bytes); 626 | size_in_bytes = safe_size_add_one(size_in_bytes); 627 | 628 | if (size_in_bytes > cbuf->size_in_bytes) 629 | { 630 | SIZE_T new_size_in_bytes; 631 | BYTE *new_buf; 632 | 633 | new_size_in_bytes = cbuf->size_in_bytes; 634 | if (new_size_in_bytes < UTF8_BUF_CAT_MIN_ALLOC_SIZE) 635 | { 636 | new_size_in_bytes = UTF8_BUF_CAT_MIN_ALLOC_SIZE; 637 | } 638 | 639 | new_buf = mem_alloc(new_size_in_bytes); 640 | 641 | // don't worry about the NULL terminator. 642 | // we write a new one below. 643 | os_copy_memory(new_buf,cbuf->buf,cbuf->length_in_bytes); 644 | 645 | if (cbuf->buf != cbuf->stack_buf) 646 | { 647 | mem_free(cbuf->buf); 648 | } 649 | 650 | cbuf->size_in_bytes = new_size_in_bytes; 651 | cbuf->buf = new_buf; 652 | } 653 | 654 | length_in_bytes = cbuf->length_in_bytes; 655 | 656 | os_copy_memory(cbuf->buf + length_in_bytes,s,slength_in_bytes); 657 | 658 | length_in_bytes += slength_in_bytes; 659 | 660 | cbuf->length_in_bytes = length_in_bytes; 661 | cbuf->buf[length_in_bytes] = 0; 662 | } 663 | 664 | void utf8_buf_cat_utf8_string(utf8_buf_t *cbuf,const ES_UTF8 *s) 665 | { 666 | utf8_buf_cat_utf8_string_n(cbuf,s,utf8_string_get_length_in_bytes(s)); 667 | } 668 | 669 | void utf8_buf_cat_byte(utf8_buf_t *cbuf,BYTE ch) 670 | { 671 | utf8_buf_cat_utf8_string_n(cbuf,&ch,1); 672 | } 673 | 674 | void utf8_buf_cat_path_separator(utf8_buf_t *cbuf) 675 | { 676 | if (cbuf->length_in_bytes) 677 | { 678 | if (!utf8_string_is_trailing_path_separator_n(cbuf->buf,cbuf->length_in_bytes)) 679 | { 680 | int path_separator; 681 | 682 | path_separator = utf8_string_get_path_separator_from_root(cbuf->buf); 683 | 684 | utf8_buf_cat_byte(cbuf,path_separator); 685 | } 686 | } 687 | } 688 | 689 | void utf8_buf_path_cat_filename(const ES_UTF8 *path,const ES_UTF8 *name,utf8_buf_t *out_cbuf) 690 | { 691 | utf8_buf_copy_utf8_string(out_cbuf,path); 692 | 693 | if (*name) 694 | { 695 | if (out_cbuf->length_in_bytes) 696 | { 697 | utf8_buf_cat_path_separator(out_cbuf); 698 | } 699 | 700 | utf8_buf_cat_utf8_string(out_cbuf,name); 701 | } 702 | } 703 | -------------------------------------------------------------------------------- /src/utf8_buf.h: -------------------------------------------------------------------------------- 1 | 2 | // 3 | // Copyright (C) 2025 voidtools / David Carpenter 4 | // 5 | // Permission is hereby granted, free of charge, 6 | // to any person obtaining a copy of this software 7 | // and associated documentation files (the "Software"), 8 | // to deal in the Software without restriction, 9 | // including without limitation the rights to use, 10 | // copy, modify, merge, publish, distribute, sublicense, 11 | // and/or sell copies of the Software, and to permit 12 | // persons to whom the Software is furnished to do so, 13 | // subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be 16 | // included in all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 20 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 21 | // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 22 | // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 23 | // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 24 | // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | // 26 | 27 | #define UTF8_BUF_STACK_SIZE MAX_PATH 28 | 29 | // a dyanimcally sized UTF-8 string buffer 30 | // has some stack space to avoid memory allocations. 31 | typedef struct utf8_buf_s 32 | { 33 | // pointer to UTF-8 string data. 34 | // buf is NULL terminated. 35 | ES_UTF8 *buf; 36 | 37 | // length of buf in bytes. 38 | // does not include the null terminator. 39 | SIZE_T length_in_bytes; 40 | 41 | // size of the buffer in bytes 42 | // includes room for the null terminator. 43 | SIZE_T size_in_bytes; 44 | 45 | // align stack_buf to 16 bytes. 46 | SIZE_T padding1; 47 | 48 | // some stack for us before we need to allocate memory from the system. 49 | ES_UTF8 stack_buf[UTF8_BUF_STACK_SIZE]; 50 | 51 | }utf8_buf_t; 52 | 53 | void utf8_buf_init(utf8_buf_t *cbuf); 54 | void utf8_buf_kill(utf8_buf_t *cbuf); 55 | void utf8_buf_empty(utf8_buf_t *cbuf); 56 | BOOL utf8_buf_try_grow_size(utf8_buf_t *cbuf,SIZE_T size_in_bytes); 57 | void utf8_buf_grow_size(utf8_buf_t *cbuf,SIZE_T size_in_bytes); 58 | void utf8_buf_grow_length(utf8_buf_t *cbuf,SIZE_T length_in_bytes); 59 | void utf8_buf_copy_wchar_string(utf8_buf_t *cbuf,const wchar_t *ws); 60 | void utf8_buf_vprintf(utf8_buf_t *cbuf,const ES_UTF8 *format,va_list argptr); 61 | void utf8_buf_printf(utf8_buf_t *cbuf,const ES_UTF8 *format,...); 62 | void utf8_buf_copy_utf8_string_n(utf8_buf_t *cbuf,const ES_UTF8 *s,SIZE_T length_in_bytes); 63 | void utf8_buf_copy_utf8_string(utf8_buf_t *cbuf,const ES_UTF8 *s); 64 | void utf8_buf_cat_utf8_string_n(utf8_buf_t *cbuf,const ES_UTF8 *s,SIZE_T slength_in_bytes); 65 | void utf8_buf_cat_utf8_string(utf8_buf_t *cbuf,const ES_UTF8 *s); 66 | void utf8_buf_cat_path_separator(utf8_buf_t *cbuf); 67 | void utf8_buf_cat_byte(utf8_buf_t *cbuf,BYTE ch); 68 | void utf8_buf_path_cat_filename(const ES_UTF8 *path,const ES_UTF8 *name,utf8_buf_t *out_cbuf); 69 | -------------------------------------------------------------------------------- /src/utf8_string.c: -------------------------------------------------------------------------------- 1 | 2 | // 3 | // Copyright (C) 2025 voidtools / David Carpenter 4 | // 5 | // Permission is hereby granted, free of charge, 6 | // to any person obtaining a copy of this software 7 | // and associated documentation files (the "Software"), 8 | // to deal in the Software without restriction, 9 | // including without limitation the rights to use, 10 | // copy, modify, merge, publish, distribute, sublicense, 11 | // and/or sell copies of the Software, and to permit 12 | // persons to whom the Software is furnished to do so, 13 | // subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be 16 | // included in all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 20 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 21 | // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 22 | // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 23 | // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 24 | // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | // 26 | // UTF-8 strings 27 | 28 | #include "es.h" 29 | 30 | // calculate the utf8 length in bytes from the specified wchar string. 31 | SIZE_T utf8_string_get_length_in_bytes_from_wchar_string(const wchar_t *ws) 32 | { 33 | const wchar_t *p; 34 | SIZE_T ret_len; 35 | 36 | p = ws; 37 | ret_len = 0; 38 | 39 | while(*p) 40 | { 41 | int c; 42 | 43 | c = *p++; 44 | 45 | // surrogates 46 | if ((c >= 0xD800) && (c < 0xDC00)) 47 | { 48 | if ((*p >= 0xDC00) && (*p < 0xE000)) 49 | { 50 | c = 0x10000 + ((c - 0xD800) << 10) + (*p - 0xDC00); 51 | 52 | p++; 53 | } 54 | } 55 | 56 | if (c > 0xffff) 57 | { 58 | // 4 bytes 59 | ret_len = safe_size_add(ret_len,4); 60 | } 61 | else 62 | if (c > 0x7ff) 63 | { 64 | // 3 bytes 65 | ret_len = safe_size_add(ret_len,3); 66 | } 67 | else 68 | if (c > 0x7f) 69 | { 70 | // 2 bytes 71 | ret_len = safe_size_add(ret_len,2); 72 | } 73 | else 74 | { 75 | // ascii 76 | ret_len = safe_size_add_one(ret_len); 77 | } 78 | } 79 | 80 | return ret_len; 81 | } 82 | 83 | // Copy a wchar string into a UTF-8 buffer. 84 | // caller must ensure there is enough room in the buffer. 85 | // use utf8_string_get_length_in_bytes_from_wchar_string to calculate length. 86 | // Handles surrogates correctly. 87 | ES_UTF8 *utf8_string_copy_wchar_string(ES_UTF8 *buf,const wchar_t *ws) 88 | { 89 | const wchar_t *p; 90 | ES_UTF8 *d; 91 | 92 | p = ws; 93 | d = buf; 94 | 95 | while(*p) 96 | { 97 | int c; 98 | 99 | c = *p++; 100 | 101 | // surrogates 102 | if ((c >= 0xD800) && (c < 0xDC00)) 103 | { 104 | if ((*p >= 0xDC00) && (*p < 0xE000)) 105 | { 106 | c = 0x10000 + ((c - 0xD800) << 10) + (*p - 0xDC00); 107 | 108 | p++; 109 | } 110 | } 111 | 112 | if (c > 0xffff) 113 | { 114 | // 4 bytes 115 | *d++ = ((c >> 18) & 0x07) | 0xF0; // 11110xxx 116 | *d++ = ((c >> 12) & 0x3f) | 0x80; // 10xxxxxx 117 | *d++ = ((c >> 6) & 0x3f) | 0x80; // 10xxxxxx 118 | *d++ = (c & 0x3f) | 0x80; // 10xxxxxx 119 | } 120 | else 121 | if (c > 0x7ff) 122 | { 123 | // 3 bytes 124 | *d++ = ((c >> 12) & 0x0f) | 0xE0; // 1110xxxx 125 | *d++ = ((c >> 6) & 0x3f) | 0x80; // 10xxxxxx 126 | *d++ = (c & 0x3f) | 0x80; // 10xxxxxx 127 | } 128 | else 129 | if (c > 0x7f) 130 | { 131 | // 2 bytes 132 | *d++ = ((c >> 6) & 0x1f) | 0xC0; // 110xxxxx 133 | *d++ = (c & 0x3f) | 0x80; // 10xxxxxx 134 | } 135 | else 136 | { 137 | // ascii 138 | *d++ = c; 139 | } 140 | } 141 | 142 | *d = 0; 143 | 144 | return d; 145 | } 146 | 147 | // return the length of the specified string in number of bytes. 148 | SIZE_T utf8_string_get_length_in_bytes(const ES_UTF8 *s) 149 | { 150 | const ES_UTF8 *p; 151 | 152 | p = s; 153 | 154 | while(*p) 155 | { 156 | p++; 157 | } 158 | 159 | return p - s; 160 | } 161 | 162 | // match a search string in s 163 | // returns the position of s after matching search. 164 | // returns NULL if it doesn't match. 165 | const ES_UTF8 *utf8_string_parse_utf8_string(const ES_UTF8 *s,const ES_UTF8 *search) 166 | { 167 | const ES_UTF8 *p1; 168 | const ES_UTF8 *p2; 169 | 170 | p1 = s; 171 | p2 = search; 172 | 173 | while(*p2) 174 | { 175 | if (*p1 != *p2) 176 | { 177 | return NULL; 178 | } 179 | 180 | p1++; 181 | p2++; 182 | } 183 | 184 | return p1; 185 | } 186 | 187 | const ES_UTF8 *utf8_string_parse_ascii_string_nocase(const ES_UTF8 *s,const char *lowercase_case_ascii_search) 188 | { 189 | const ES_UTF8 *p1; 190 | const ES_UTF8 *p2; 191 | 192 | p1 = s; 193 | p2 = lowercase_case_ascii_search; 194 | 195 | while(*p2) 196 | { 197 | if (*p1 & 0x80) 198 | { 199 | return NULL; 200 | } 201 | 202 | if (unicode_ascii_to_lower(*p1) != *p2) 203 | { 204 | return NULL; 205 | } 206 | 207 | p1++; 208 | p2++; 209 | } 210 | 211 | return p1; 212 | } 213 | 214 | // compare two UTF-8 strings 215 | // returns -1 if a < b 216 | // returns 1 if a > b 217 | // returns 0 if a == b. 218 | int utf8_string_compare(const ES_UTF8 *a,const ES_UTF8 *b) 219 | { 220 | const ES_UTF8 *p1; 221 | const ES_UTF8 *p2; 222 | 223 | p1 = a; 224 | p2 = b; 225 | 226 | while(*p2) 227 | { 228 | if (!*p1) 229 | { 230 | // a < b 231 | return -1; 232 | } 233 | 234 | if (*p1 != *p2) 235 | { 236 | return *p1 - *p2; 237 | } 238 | 239 | p1++; 240 | p2++; 241 | } 242 | 243 | if (*p1) 244 | { 245 | // a > b 246 | return 1; 247 | } 248 | 249 | return 0; 250 | } 251 | 252 | // matches '\\' or '/' 253 | BOOL utf8_string_is_trailing_path_separator_n(const ES_UTF8 *s,SIZE_T slength_in_bytes) 254 | { 255 | if ((slength_in_bytes) && ((s[slength_in_bytes - 1] == '\\') || (s[slength_in_bytes - 1] == '/'))) 256 | { 257 | return TRUE; 258 | } 259 | 260 | return FALSE; 261 | } 262 | 263 | // get the path separator character from the specified root path. 264 | int utf8_string_get_path_separator_from_root(const ES_UTF8 *s) 265 | { 266 | const ES_UTF8 *p; 267 | int is_colon; 268 | 269 | p = s; 270 | is_colon = 0; 271 | 272 | while(*p) 273 | { 274 | if (*p == '\\') 275 | { 276 | return '\\'; 277 | } 278 | 279 | if (*p == '/') 280 | { 281 | return '/'; 282 | } 283 | 284 | if (*p == ':') 285 | { 286 | is_colon = 1; 287 | } 288 | 289 | p++; 290 | } 291 | 292 | // no '\\' or '/'.. 293 | // default to '\\'; 294 | 295 | // Recycle Bin => '\\' (Recycle Bin\junk.txt) 296 | // Control Panel => '\\' (Control Panel\Display) 297 | // www.google.com => '\\' (no way to know if this is a virtual folder, use a scheme name, eg: https://) 298 | // http:www.google.com => '/' 299 | // file:C: => '/' 300 | 301 | if (is_colon) 302 | { 303 | // drive letter C: 304 | // or ::{guid} 305 | // assume there's no one-character scheme names. 306 | if ((*s) && (s[1] == ':')) 307 | { 308 | return '\\'; 309 | } 310 | 311 | // scheme name: mailto: file: https: ftp: 312 | return '/'; 313 | } 314 | 315 | // default to '\\' 316 | return '\\'; 317 | } 318 | 319 | void utf8_string_copy_utf8_string_n(ES_UTF8 *d,ES_UTF8 *s,SIZE_T slength_in_bytes) 320 | { 321 | os_copy_memory(d,s,slength_in_bytes); 322 | d[slength_in_bytes] = 0; 323 | } 324 | -------------------------------------------------------------------------------- /src/utf8_string.h: -------------------------------------------------------------------------------- 1 | 2 | // 3 | // Copyright (C) 2025 voidtools / David Carpenter 4 | // 5 | // Permission is hereby granted, free of charge, 6 | // to any person obtaining a copy of this software 7 | // and associated documentation files (the "Software"), 8 | // to deal in the Software without restriction, 9 | // including without limitation the rights to use, 10 | // copy, modify, merge, publish, distribute, sublicense, 11 | // and/or sell copies of the Software, and to permit 12 | // persons to whom the Software is furnished to do so, 13 | // subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be 16 | // included in all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 20 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 21 | // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 22 | // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 23 | // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 24 | // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | // 26 | 27 | // get a unicode character from p and store it in out_ch. 28 | // advances p 29 | #define UTF8_STRING_GET_CHAR(p,out_ch) \ 30 | if (!(*p & 0x80)) \ 31 | { \ 32 | out_ch = *p; \ 33 | p++;\ 34 | } \ 35 | else \ 36 | { \ 37 | if (((*p & 0xE0) == 0xC0) && (p[1])) \ 38 | { \ 39 | /* 2 byte UTF-8 */ \ 40 | out_ch = ((*p & 0x1f) << 6) | (p[1] & 0x3f); \ 41 | p += 2; \ 42 | } \ 43 | else \ 44 | if (((*p & 0xF0) == 0xE0) && (p[1]) && (p[2])) \ 45 | { \ 46 | /* 3 byte UTF-8 */ \ 47 | out_ch = ((*p & 0x0f) << (12)) | ((p[1] & 0x3f) << 6) | (p[2] & 0x3f); \ 48 | p += 3; \ 49 | } \ 50 | else \ 51 | if (((*p & 0xF8) == 0xF0) && (p[1]) && (p[2]) && (p[3])) \ 52 | { \ 53 | /* 4 byte UTF-8 */ \ 54 | out_ch = ((*p & 0x07) << 18) | ((p[1] & 0x3f) << 12) | ((p[2] & 0x3f) << 6) | (p[3] & 0x3f); \ 55 | p += 4; \ 56 | } \ 57 | else \ 58 | { \ 59 | /* bad UTF-8 */ \ 60 | out_ch = 0; \ 61 | p++; \ 62 | } \ 63 | } 64 | 65 | SIZE_T utf8_string_get_length_in_bytes_from_wchar_string(const wchar_t *ws); 66 | ES_UTF8 *utf8_string_copy_wchar_string(ES_UTF8 *buf,const wchar_t *ws); 67 | SIZE_T utf8_string_get_length_in_bytes(const ES_UTF8 *s); 68 | const ES_UTF8 *utf8_string_parse_utf8_string(const ES_UTF8 *s,const ES_UTF8 *search); 69 | const ES_UTF8 *utf8_string_parse_ascii_string_nocase(const ES_UTF8 *s,const char *lowercase_case_ascii_search); 70 | int utf8_string_compare(const ES_UTF8 *a,const ES_UTF8 *b); 71 | BOOL utf8_string_is_trailing_path_separator_n(const ES_UTF8 *s,SIZE_T slength_in_bytes); 72 | int utf8_string_get_path_separator_from_root(const ES_UTF8 *s); 73 | void utf8_string_copy_utf8_string_n(ES_UTF8 *d,ES_UTF8 *s,SIZE_T slength_in_bytes); 74 | -------------------------------------------------------------------------------- /src/version.h: -------------------------------------------------------------------------------- 1 | 2 | #define VERSION_YEAR 2025 3 | #define VERSION_MAJOR 1 4 | #define VERSION_MINOR 1 5 | #define VERSION_REVISION 0 6 | #define VERSION_BUILD 34 7 | #define VERSION_TYPE "" 8 | 9 | #define _VERSION_TEXT_STR2(x) #x 10 | #define _VERSION_TEXT_STR(x) _VERSION_TEXT_STR2(x) 11 | #define VERSION_TEXT _VERSION_TEXT_STR(VERSION_MAJOR) "." _VERSION_TEXT_STR(VERSION_MINOR) "." _VERSION_TEXT_STR(VERSION_REVISION) "." _VERSION_TEXT_STR(VERSION_BUILD) VERSION_TYPE 12 | #define VERSION_YEAR_TEXT _VERSION_TEXT_STR(VERSION_YEAR) 13 | -------------------------------------------------------------------------------- /src/wchar_buf.c: -------------------------------------------------------------------------------- 1 | 2 | // 3 | // Copyright (C) 2025 voidtools / David Carpenter 4 | // 5 | // Permission is hereby granted, free of charge, 6 | // to any person obtaining a copy of this software 7 | // and associated documentation files (the "Software"), 8 | // to deal in the Software without restriction, 9 | // including without limitation the rights to use, 10 | // copy, modify, merge, publish, distribute, sublicense, 11 | // and/or sell copies of the Software, and to permit 12 | // persons to whom the Software is furnished to do so, 13 | // subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be 16 | // included in all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 20 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 21 | // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 22 | // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 23 | // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 24 | // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | // 26 | // wchar buffer 27 | 28 | #include "es.h" 29 | 30 | // initialize a wchar buffer. 31 | // the default value is an empty string. 32 | void wchar_buf_init(wchar_buf_t *wcbuf) 33 | { 34 | wcbuf->buf = wcbuf->stack_buf; 35 | wcbuf->length_in_wchars = 0; 36 | wcbuf->size_in_wchars = WCHAR_BUF_STACK_SIZE; 37 | wcbuf->buf[0] = 0; 38 | } 39 | 40 | // kill a wchar buffer. 41 | // returns any allocated memory back to the system. 42 | void wchar_buf_kill(wchar_buf_t *wcbuf) 43 | { 44 | if (wcbuf->buf != wcbuf->stack_buf) 45 | { 46 | mem_free(wcbuf->buf); 47 | } 48 | } 49 | 50 | // empty a wchar buffer. 51 | // the wchar buffer is set to an empty string. 52 | void wchar_buf_empty(wchar_buf_t *wcbuf) 53 | { 54 | wcbuf->buf[0] = 0; 55 | wcbuf->length_in_wchars = 0; 56 | } 57 | 58 | // doesn't keep the existing text. 59 | // doesn't set the text. 60 | // doesn't set length. 61 | // caller should set the text. 62 | void wchar_buf_grow_size(wchar_buf_t *wcbuf,SIZE_T size_in_wchars) 63 | { 64 | if (size_in_wchars <= wcbuf->size_in_wchars) 65 | { 66 | // already enough room. 67 | } 68 | else 69 | { 70 | wchar_buf_empty(wcbuf); 71 | 72 | wcbuf->buf = mem_alloc(safe_size_add(size_in_wchars,size_in_wchars)); 73 | wcbuf->size_in_wchars = size_in_wchars; 74 | } 75 | } 76 | 77 | // doesn't keep the existing text. 78 | // doesn't set the text, only sets the length. 79 | // DOES set the length. 80 | // caller should set the text. 81 | void wchar_buf_grow_length(wchar_buf_t *wcbuf,SIZE_T length_in_wchars) 82 | { 83 | wchar_buf_grow_size(wcbuf,safe_size_add(length_in_wchars,1)); 84 | 85 | wcbuf->length_in_wchars = length_in_wchars; 86 | } 87 | 88 | // concatenate a simple wchar string with specified length. 89 | void wchar_buf_cat_wchar_string_n(wchar_buf_t *wcbuf,const wchar_t *s,SIZE_T slength_in_wchars) 90 | { 91 | SIZE_T size_in_wchars; 92 | SIZE_T length_in_wchars; 93 | 94 | size_in_wchars = wcbuf->length_in_wchars; 95 | size_in_wchars = safe_size_add(size_in_wchars,slength_in_wchars); 96 | size_in_wchars = safe_size_add_one(size_in_wchars); 97 | 98 | if (size_in_wchars > wcbuf->size_in_wchars) 99 | { 100 | SIZE_T new_size_in_wchars; 101 | wchar_t *new_buf; 102 | 103 | new_size_in_wchars = safe_size_mul_2(wcbuf->size_in_wchars); 104 | if (new_size_in_wchars < WCHAR_BUF_CAT_MIN_ALLOC_SIZE) 105 | { 106 | new_size_in_wchars = WCHAR_BUF_CAT_MIN_ALLOC_SIZE; 107 | } 108 | 109 | new_buf = mem_alloc(safe_size_mul_sizeof_wchar(new_size_in_wchars)); 110 | 111 | // don't worry about the NULL terminator. 112 | // we write a new one below. 113 | os_copy_memory(new_buf,wcbuf->buf,wcbuf->length_in_wchars * 2); 114 | 115 | if (wcbuf->buf != wcbuf->stack_buf) 116 | { 117 | mem_free(wcbuf->buf); 118 | } 119 | 120 | wcbuf->size_in_wchars = new_size_in_wchars; 121 | wcbuf->buf = new_buf; 122 | } 123 | 124 | length_in_wchars = wcbuf->length_in_wchars; 125 | 126 | os_copy_memory(wcbuf->buf + length_in_wchars,s,slength_in_wchars * sizeof(wchar_t)); 127 | 128 | length_in_wchars += slength_in_wchars; 129 | 130 | wcbuf->length_in_wchars = length_in_wchars; 131 | wcbuf->buf[length_in_wchars] = 0; 132 | } 133 | 134 | // concatenate a simple wchar string. 135 | void wchar_buf_cat_wchar_string(wchar_buf_t *wcbuf,const wchar_t *s) 136 | { 137 | wchar_buf_cat_wchar_string_n(wcbuf,s,wchar_string_get_length_in_wchars(s)); 138 | } 139 | 140 | // concatenate a wchar character. 141 | void wchar_buf_cat_wchar(wchar_buf_t *wcbuf,wchar_t ch) 142 | { 143 | wchar_buf_cat_wchar_string_n(wcbuf,&ch,1); 144 | } 145 | 146 | // concatenate a UTF-8 string. 147 | void wchar_buf_cat_utf8_string(wchar_buf_t *wcbuf,const ES_UTF8 *s) 148 | { 149 | wchar_buf_t add_wcbuf; 150 | 151 | wchar_buf_init(&add_wcbuf); 152 | 153 | wchar_buf_copy_utf8_string(&add_wcbuf,s); 154 | 155 | wchar_buf_cat_wchar_string_n(wcbuf,add_wcbuf.buf,add_wcbuf.length_in_wchars); 156 | 157 | wchar_buf_kill(&add_wcbuf); 158 | } 159 | 160 | // copy a UTF-8 string. 161 | void wchar_buf_copy_utf8_string(wchar_buf_t *wcbuf,const ES_UTF8 *s) 162 | { 163 | wchar_buf_grow_length(wcbuf,wchar_string_get_length_in_wchars_from_utf8_string(s)); 164 | 165 | wchar_string_copy_utf8_string(wcbuf->buf,s); 166 | } 167 | 168 | // copy a UTF-8 string. 169 | void wchar_buf_copy_utf8_string_n(wchar_buf_t *wcbuf,const ES_UTF8 *s,SIZE_T slength_in_bytes) 170 | { 171 | wchar_buf_grow_length(wcbuf,wchar_string_get_length_in_wchars_from_utf8_string_n(s,slength_in_bytes)); 172 | 173 | wchar_string_copy_utf8_string_n(wcbuf->buf,s,slength_in_bytes); 174 | } 175 | 176 | // concatenate a simple wchar string with the specified length. 177 | void wchar_buf_copy_wchar_string_n(wchar_buf_t *wcbuf,const wchar_t *s,SIZE_T slength_in_wchars) 178 | { 179 | wchar_buf_grow_length(wcbuf,slength_in_wchars); 180 | 181 | os_copy_memory(wcbuf->buf,s,slength_in_wchars * 2); 182 | 183 | wcbuf->buf[slength_in_wchars] = 0; 184 | } 185 | 186 | // concatenate a simple wchar string. 187 | void wchar_buf_copy_wchar_string(wchar_buf_t *wcbuf,const wchar_t *s) 188 | { 189 | wchar_buf_copy_wchar_string_n(wcbuf,s,wchar_string_get_length_in_wchars(s)); 190 | } 191 | 192 | // remove the file specification from the specified wcbuf. 193 | void wchar_buf_remove_file_spec(wchar_buf_t *wcbuf) 194 | { 195 | const wchar_t *p; 196 | const wchar_t *last; 197 | 198 | p = wcbuf->buf; 199 | last = NULL; 200 | 201 | while(*p) 202 | { 203 | if ((*p == '\\') || (*p == '/')) 204 | { 205 | // ignore trailing '\\' 206 | if (!p[1]) 207 | { 208 | break; 209 | } 210 | 211 | last = p; 212 | } 213 | 214 | p++; 215 | } 216 | 217 | if (last) 218 | { 219 | SIZE_T length_in_wchars; 220 | 221 | length_in_wchars = last - wcbuf->buf; 222 | 223 | wcbuf->length_in_wchars = length_in_wchars; 224 | wcbuf->buf[length_in_wchars] = 0; 225 | } 226 | } 227 | 228 | // sprintf 229 | void wchar_buf_printf(wchar_buf_t *wcbuf,const ES_UTF8 *format,...) 230 | { 231 | va_list argptr; 232 | 233 | va_start(argptr,format); 234 | 235 | wchar_buf_vprintf(wcbuf,format,argptr); 236 | 237 | va_end(argptr); 238 | } 239 | 240 | // vsprintf 241 | void wchar_buf_vprintf(wchar_buf_t *wcbuf,const ES_UTF8 *format,va_list argptr) 242 | { 243 | utf8_buf_t cbuf; 244 | 245 | utf8_buf_init(&cbuf); 246 | 247 | utf8_buf_vprintf(&cbuf,format,argptr); 248 | 249 | wchar_buf_copy_utf8_string(wcbuf,cbuf.buf); 250 | 251 | utf8_buf_kill(&cbuf); 252 | } 253 | 254 | // cat printf 255 | void wchar_buf_cat_printf(wchar_buf_t *wcbuf,const ES_UTF8 *format,...) 256 | { 257 | va_list argptr; 258 | 259 | va_start(argptr,format); 260 | 261 | { 262 | utf8_buf_t cbuf; 263 | 264 | utf8_buf_init(&cbuf); 265 | 266 | utf8_buf_vprintf(&cbuf,format,argptr); 267 | 268 | wchar_buf_cat_utf8_string(wcbuf,cbuf.buf); 269 | 270 | utf8_buf_kill(&cbuf); 271 | } 272 | 273 | va_end(argptr); 274 | } 275 | 276 | void wchar_buf_cat_print_UINT64(wchar_buf_t *wcbuf,ES_UINT64 value) 277 | { 278 | wchar_buf_cat_printf(wcbuf,"%I64u",value); 279 | } 280 | 281 | // combine a path and filename 282 | // adds the correct path separate between path and filename if required. 283 | // stores the result in out_wcbuf. 284 | void wchar_buf_path_cat_filename(const wchar_t *path,const wchar_t *name,wchar_buf_t *out_wcbuf) 285 | { 286 | wchar_buf_copy_wchar_string(out_wcbuf,path); 287 | 288 | if (*name) 289 | { 290 | if (out_wcbuf->length_in_wchars) 291 | { 292 | wchar_buf_cat_path_separator(out_wcbuf); 293 | } 294 | 295 | wchar_buf_cat_wchar_string(out_wcbuf,name); 296 | } 297 | } 298 | 299 | // concatenate a path separator. 300 | // does nothing if there's already a path separator. 301 | // uses the correct path separator based on the root. 302 | void wchar_buf_cat_path_separator(wchar_buf_t *wcbuf) 303 | { 304 | if (wcbuf->length_in_wchars) 305 | { 306 | if (!wchar_string_is_trailing_path_separator_n(wcbuf->buf,wcbuf->length_in_wchars)) 307 | { 308 | int path_separator; 309 | 310 | path_separator = wchar_string_get_path_separator_from_root(wcbuf->buf); 311 | 312 | wchar_buf_cat_wchar(wcbuf,path_separator); 313 | } 314 | } 315 | } 316 | 317 | // add a wchar string to a semicolon (;) delimited list. 318 | void wchar_buf_cat_list_wchar_string_n(wchar_buf_t *wcbuf,const wchar_t *s,SIZE_T slength_in_wchars) 319 | { 320 | if (wcbuf->length_in_wchars) 321 | { 322 | wchar_buf_cat_wchar(wcbuf,';'); 323 | } 324 | 325 | wchar_buf_cat_wchar_string_n(wcbuf,s,slength_in_wchars); 326 | } 327 | 328 | const wchar_t *wchar_buf_parse_list_item(const wchar_t *s,wchar_buf_t *out_wcbuf) 329 | { 330 | const wchar_t *p; 331 | const wchar_t *start; 332 | SIZE_T len; 333 | 334 | p = s; 335 | if (!*p) 336 | { 337 | return NULL; 338 | } 339 | 340 | start = p; 341 | 342 | for(;;) 343 | { 344 | if (!*p) 345 | { 346 | len = p - start; 347 | break; 348 | } 349 | 350 | if (*p == ';') 351 | { 352 | len = p - start; 353 | p++; 354 | 355 | break; 356 | } 357 | 358 | p++; 359 | } 360 | 361 | wchar_buf_copy_wchar_string_n(out_wcbuf,start,len); 362 | 363 | return p; 364 | } 365 | 366 | // copy a UTF-8 string. 367 | void wchar_buf_copy_lowercase_utf8_string(wchar_buf_t *wcbuf,const ES_UTF8 *s) 368 | { 369 | wchar_buf_grow_length(wcbuf,wchar_string_get_length_in_wchars_from_utf8_string(s)); 370 | 371 | wchar_string_copy_lowercase_utf8_string(wcbuf->buf,s); 372 | } 373 | 374 | // remove command line double quotes 375 | void wchar_buf_fix_quotes(wchar_buf_t *in_out_wcbuf) 376 | { 377 | wchar_t *p; 378 | wchar_t *d; 379 | 380 | p = in_out_wcbuf->buf; 381 | d = in_out_wcbuf->buf; 382 | 383 | while(*p) 384 | { 385 | if (*p == '"') 386 | { 387 | // skip it. 388 | p++; 389 | continue; 390 | } 391 | 392 | if ((*p == '&') && (p[1] == 'q') && (p[2] == 'u') && (p[3] == 'o') && (p[4] == 't') && (p[5] == ':')) 393 | { 394 | // unescape ": 395 | p += 6; 396 | *d++ = '"'; 397 | continue; 398 | } 399 | 400 | *d++ = *p; 401 | p++; 402 | } 403 | 404 | *d = 0; 405 | 406 | in_out_wcbuf->length_in_wchars = d - in_out_wcbuf->buf; 407 | } 408 | 409 | -------------------------------------------------------------------------------- /src/wchar_buf.h: -------------------------------------------------------------------------------- 1 | 2 | // 3 | // Copyright (C) 2025 voidtools / David Carpenter 4 | // 5 | // Permission is hereby granted, free of charge, 6 | // to any person obtaining a copy of this software 7 | // and associated documentation files (the "Software"), 8 | // to deal in the Software without restriction, 9 | // including without limitation the rights to use, 10 | // copy, modify, merge, publish, distribute, sublicense, 11 | // and/or sell copies of the Software, and to permit 12 | // persons to whom the Software is furnished to do so, 13 | // subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be 16 | // included in all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 20 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 21 | // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 22 | // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 23 | // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 24 | // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | // 26 | 27 | #define WCHAR_BUF_STACK_SIZE MAX_PATH 28 | #define WCHAR_BUF_CAT_MIN_ALLOC_SIZE 65536 29 | 30 | // a dyanimcally sized wchar string 31 | // has some stack space to avoid memory allocations. 32 | typedef struct wchar_buf_s 33 | { 34 | // pointer to wchar string data. 35 | // buf is NULL terminated. 36 | wchar_t *buf; 37 | 38 | // length of buf in wide chars. 39 | // does not include the null terminator. 40 | SIZE_T length_in_wchars; 41 | 42 | // size of the buffer in wide chars 43 | // includes room for the null terminator. 44 | SIZE_T size_in_wchars; 45 | 46 | // align stack_buf to 16 bytes. 47 | SIZE_T padding1; 48 | 49 | // some stack for us before we need to allocate memory from the system. 50 | wchar_t stack_buf[WCHAR_BUF_STACK_SIZE]; 51 | 52 | }wchar_buf_t; 53 | 54 | void wchar_buf_init(wchar_buf_t *wcbuf); 55 | void wchar_buf_kill(wchar_buf_t *wcbuf); 56 | void wchar_buf_empty(wchar_buf_t *wcbuf); 57 | void wchar_buf_grow_size(wchar_buf_t *wcbuf,SIZE_T length_in_wchars); 58 | void wchar_buf_grow_length(wchar_buf_t *wcbuf,SIZE_T length_in_wchars); 59 | void wchar_buf_cat_wchar_string(wchar_buf_t *wcbuf,const wchar_t *s); 60 | void wchar_buf_cat_wchar_string_n(wchar_buf_t *wcbuf,const wchar_t *s,SIZE_T slength_in_wchars); 61 | void wchar_buf_cat_wchar(wchar_buf_t *wcbuf,wchar_t ch); 62 | void wchar_buf_copy_utf8_string(wchar_buf_t *wcbuf,const ES_UTF8 *s); 63 | void wchar_buf_copy_utf8_string_n(wchar_buf_t *wcbuf,const ES_UTF8 *s,SIZE_T slength_in_bytes); 64 | void wchar_buf_cat_utf8_string(wchar_buf_t *wcbuf,const ES_UTF8 *s); 65 | void wchar_buf_copy_wchar_string_n(wchar_buf_t *wcbuf,const wchar_t *s,SIZE_T slength_in_wchars); 66 | void wchar_buf_copy_wchar_string(wchar_buf_t *wcbuf,const wchar_t *s); 67 | void wchar_buf_remove_file_spec(wchar_buf_t *wcbuf); 68 | void wchar_buf_printf(wchar_buf_t *wcbuf,const ES_UTF8 *format,...); 69 | void wchar_buf_vprintf(wchar_buf_t *wcbuf,const ES_UTF8 *format,va_list argptr); 70 | void wchar_buf_cat_printf(wchar_buf_t *wcbuf,const ES_UTF8 *format,...); 71 | void wchar_buf_cat_print_UINT64(wchar_buf_t *wcbuf,ES_UINT64 value); 72 | void wchar_buf_path_cat_filename(const wchar_t *path,const wchar_t *name,wchar_buf_t *wcbuf); 73 | void wchar_buf_cat_path_separator(wchar_buf_t *wcbuf); 74 | void wchar_buf_cat_list_wchar_string_n(wchar_buf_t *wcbuf,const wchar_t *s,SIZE_T slength_in_wchars); 75 | void wchar_buf_copy_lowercase_utf8_string(wchar_buf_t *wcbuf,const ES_UTF8 *s); 76 | const wchar_t *wchar_buf_parse_list_item(const wchar_t *s,wchar_buf_t *out_wcbuf); 77 | void wchar_buf_fix_quotes(wchar_buf_t *in_out_wcbuf); 78 | -------------------------------------------------------------------------------- /src/wchar_string.h: -------------------------------------------------------------------------------- 1 | 2 | // 3 | // Copyright (C) 2025 voidtools / David Carpenter 4 | // 5 | // Permission is hereby granted, free of charge, 6 | // to any person obtaining a copy of this software 7 | // and associated documentation files (the "Software"), 8 | // to deal in the Software without restriction, 9 | // including without limitation the rights to use, 10 | // copy, modify, merge, publish, distribute, sublicense, 11 | // and/or sell copies of the Software, and to permit 12 | // persons to whom the Software is furnished to do so, 13 | // subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be 16 | // included in all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 20 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 21 | // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 22 | // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 23 | // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 24 | // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | // 26 | 27 | // get a unicode character from p and store it in out_ch. 28 | // advances p 29 | // allows unpaired surrogates. 30 | #define WCHAR_STRING_GET_CHAR(p,out_ch) \ 31 | if ((*p >= 0xD800) && (*p < 0xDC00) && (p[1] >= 0xDC00) && (p[1] < 0xE000)) \ 32 | { \ 33 | out_ch = 0x10000 + ((*p - 0xD800) << 10) + (p[1] - 0xDC00); \ 34 | p += 2; \ 35 | } \ 36 | else \ 37 | { \ 38 | out_ch = *p; \ 39 | p++; \ 40 | } 41 | 42 | int wchar_string_to_int(const wchar_t *s); 43 | ES_UINT64 wchar_string_to_uint64(const wchar_t *s); 44 | DWORD wchar_string_to_dword(const wchar_t *s); 45 | SIZE_T wchar_string_get_length_in_wchars(const wchar_t *s); 46 | SIZE_T wchar_string_get_highlighted_length(const wchar_t *s); 47 | void wchar_string_copy_wchar_string_n(wchar_t *buf,const wchar_t *s,SIZE_T slength_in_wchars); 48 | SIZE_T wchar_string_get_length_in_wchars_from_utf8_string(const ES_UTF8 *s); 49 | wchar_t *wchar_string_copy_utf8_string(wchar_t *buf,const ES_UTF8 *s); 50 | wchar_t *wchar_string_copy_lowercase_utf8_string(wchar_t *buf,const ES_UTF8 *s); 51 | SIZE_T wchar_string_get_length_in_wchars_from_utf8_string_n(const ES_UTF8 *s,SIZE_T slength_in_bytes); 52 | wchar_t *wchar_string_copy_utf8_string_n(wchar_t *buf,const ES_UTF8 *s,SIZE_T slength_in_bytes); 53 | const wchar_t *wchar_string_skip_ws(const wchar_t *p); 54 | wchar_t *wchar_string_alloc_wchar_string_n(const wchar_t *s,SIZE_T slength_in_wchars); 55 | BOOL wchar_string_is_trailing_path_separator_n(const wchar_t *s,SIZE_T slength_in_wchars); 56 | int wchar_string_get_path_separator_from_root(const wchar_t *s); 57 | int wchar_string_compare(const wchar_t *a,const wchar_t *b); 58 | const wchar_t *wchar_string_parse_list_item(const wchar_t *s,struct wchar_buf_s *out_wcbuf); 59 | const wchar_t *wchar_string_parse_utf8_string(const wchar_t *s,const ES_UTF8 *search); 60 | const wchar_t *wchar_string_parse_int(const wchar_t *s,int *out_value); 61 | const wchar_t *wchar_string_parse_nocase_lowercase_ascii_string(const wchar_t *s,const char *lowercase_search); 62 | void wchar_string_make_lowercase(wchar_t *s); 63 | BOOL wchar_string_wildcard_exec(const wchar_t *filename,const wchar_t *wildcard_search); 64 | -------------------------------------------------------------------------------- /vs2005/cli.sln: -------------------------------------------------------------------------------- 1 | Microsoft Visual Studio Solution File, Format Version 9.00 2 | # Visual Studio 2005 3 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cli", "cli.vcproj", "{6A2D0A68-547C-4AAF-AB84-BE0FA4A0CFCE}" 4 | EndProject 5 | Global 6 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 7 | Debug|Win32 = Debug|Win32 8 | Debug|x64 = Debug|x64 9 | Release|Win32 = Release|Win32 10 | Release|x64 = Release|x64 11 | EndGlobalSection 12 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 13 | {6A2D0A68-547C-4AAF-AB84-BE0FA4A0CFCE}.Debug|Win32.ActiveCfg = Debug|Win32 14 | {6A2D0A68-547C-4AAF-AB84-BE0FA4A0CFCE}.Debug|Win32.Build.0 = Debug|Win32 15 | {6A2D0A68-547C-4AAF-AB84-BE0FA4A0CFCE}.Debug|x64.ActiveCfg = Debug|x64 16 | {6A2D0A68-547C-4AAF-AB84-BE0FA4A0CFCE}.Debug|x64.Build.0 = Debug|x64 17 | {6A2D0A68-547C-4AAF-AB84-BE0FA4A0CFCE}.Release|Win32.ActiveCfg = Release|Win32 18 | {6A2D0A68-547C-4AAF-AB84-BE0FA4A0CFCE}.Release|Win32.Build.0 = Release|Win32 19 | {6A2D0A68-547C-4AAF-AB84-BE0FA4A0CFCE}.Release|x64.ActiveCfg = Release|x64 20 | {6A2D0A68-547C-4AAF-AB84-BE0FA4A0CFCE}.Release|x64.Build.0 = Release|x64 21 | EndGlobalSection 22 | GlobalSection(SolutionProperties) = preSolution 23 | HideSolutionNode = FALSE 24 | EndGlobalSection 25 | EndGlobal 26 | -------------------------------------------------------------------------------- /vs2005/cli.vcproj: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 14 | 17 | 18 | 19 | 20 | 21 | 29 | 32 | 35 | 38 | 41 | 44 | 58 | 61 | 64 | 67 | 80 | 83 | 87 | 90 | 93 | 96 | 99 | 102 | 105 | 106 | 114 | 117 | 120 | 123 | 126 | 130 | 141 | 144 | 147 | 150 | 159 | 162 | 166 | 169 | 172 | 175 | 178 | 181 | 184 | 185 | 194 | 197 | 200 | 203 | 206 | 209 | 231 | 234 | 237 | 241 | 260 | 263 | 270 | 273 | 276 | 279 | 282 | 285 | 289 | 290 | 299 | 302 | 305 | 308 | 311 | 315 | 336 | 339 | 342 | 345 | 363 | 366 | 373 | 376 | 379 | 382 | 385 | 388 | 391 | 392 | 393 | 394 | 395 | 396 | 400 | 403 | 404 | 407 | 408 | 411 | 412 | 415 | 416 | 419 | 420 | 423 | 424 | 427 | 428 | 431 | 432 | 435 | 436 | 439 | 440 | 443 | 444 | 447 | 448 | 451 | 452 | 455 | 456 | 459 | 460 | 463 | 464 | 467 | 468 | 471 | 472 | 475 | 476 | 479 | 480 | 483 | 484 | 487 | 488 | 491 | 492 | 495 | 496 | 499 | 500 | 503 | 504 | 507 | 508 | 511 | 512 | 515 | 516 | 519 | 520 | 523 | 524 | 527 | 528 | 531 | 532 | 535 | 536 | 539 | 540 | 543 | 544 | 547 | 548 | 551 | 552 | 555 | 556 | 559 | 560 | 563 | 564 | 567 | 568 | 571 | 572 | 575 | 576 | 579 | 580 | 583 | 584 | 587 | 588 | 589 | 592 | 595 | 596 | 599 | 600 | 601 | 602 | 603 | 604 | 605 | -------------------------------------------------------------------------------- /vs2019/cli.sln: -------------------------------------------------------------------------------- 1 | Microsoft Visual Studio Solution File, Format Version 12.00 2 | # Visual Studio Version 16 3 | VisualStudioVersion = 16.0.29920.165 4 | MinimumVisualStudioVersion = 10.0.40219.1 5 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cli", "cli.vcxproj", "{6A2D0A68-547C-4AAF-AB84-BE0FA4A0CFCE}" 6 | EndProject 7 | Global 8 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 9 | Debug|ARM = Debug|ARM 10 | Debug|ARM64 = Debug|ARM64 11 | Debug|Win32 = Debug|Win32 12 | Debug|x64 = Debug|x64 13 | Release|ARM = Release|ARM 14 | Release|ARM64 = Release|ARM64 15 | Release|Win32 = Release|Win32 16 | Release|x64 = Release|x64 17 | EndGlobalSection 18 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 19 | {6A2D0A68-547C-4AAF-AB84-BE0FA4A0CFCE}.Debug|ARM.ActiveCfg = Debug|Win32 20 | {6A2D0A68-547C-4AAF-AB84-BE0FA4A0CFCE}.Debug|ARM.Build.0 = Debug|Win32 21 | {6A2D0A68-547C-4AAF-AB84-BE0FA4A0CFCE}.Debug|ARM64.ActiveCfg = Debug|x64 22 | {6A2D0A68-547C-4AAF-AB84-BE0FA4A0CFCE}.Debug|ARM64.Build.0 = Debug|x64 23 | {6A2D0A68-547C-4AAF-AB84-BE0FA4A0CFCE}.Debug|Win32.ActiveCfg = Debug|Win32 24 | {6A2D0A68-547C-4AAF-AB84-BE0FA4A0CFCE}.Debug|Win32.Build.0 = Debug|Win32 25 | {6A2D0A68-547C-4AAF-AB84-BE0FA4A0CFCE}.Debug|x64.ActiveCfg = Debug|x64 26 | {6A2D0A68-547C-4AAF-AB84-BE0FA4A0CFCE}.Debug|x64.Build.0 = Debug|x64 27 | {6A2D0A68-547C-4AAF-AB84-BE0FA4A0CFCE}.Release|ARM.ActiveCfg = Release|ARM 28 | {6A2D0A68-547C-4AAF-AB84-BE0FA4A0CFCE}.Release|ARM.Build.0 = Release|ARM 29 | {6A2D0A68-547C-4AAF-AB84-BE0FA4A0CFCE}.Release|ARM64.ActiveCfg = Release|ARM64 30 | {6A2D0A68-547C-4AAF-AB84-BE0FA4A0CFCE}.Release|ARM64.Build.0 = Release|ARM64 31 | {6A2D0A68-547C-4AAF-AB84-BE0FA4A0CFCE}.Release|Win32.ActiveCfg = Release|Win32 32 | {6A2D0A68-547C-4AAF-AB84-BE0FA4A0CFCE}.Release|Win32.Build.0 = Release|Win32 33 | {6A2D0A68-547C-4AAF-AB84-BE0FA4A0CFCE}.Release|x64.ActiveCfg = Release|x64 34 | {6A2D0A68-547C-4AAF-AB84-BE0FA4A0CFCE}.Release|x64.Build.0 = Release|x64 35 | EndGlobalSection 36 | GlobalSection(SolutionProperties) = preSolution 37 | HideSolutionNode = FALSE 38 | EndGlobalSection 39 | GlobalSection(ExtensibilityGlobals) = postSolution 40 | SolutionGuid = {CE3EDF4D-A09B-4CC3-967A-269B5257E7BE} 41 | EndGlobalSection 42 | EndGlobal 43 | --------------------------------------------------------------------------------