Rev 16 | Details | Compare with Previous | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
16 | pmbaty | 1 | // buffer.c |
2 | |||
3 | // standard C includes |
||
4 | #include <stdio.h> |
||
5 | #include <stddef.h> |
||
6 | #include <stdlib.h> |
||
7 | #include <stdarg.h> |
||
8 | #include <stdint.h> // for SIZE_MAX |
||
9 | #include <string.h> |
||
10 | #include <fcntl.h> |
||
11 | #include <sys/stat.h> |
||
12 | #include <locale.h> |
||
13 | #include <errno.h> |
||
14 | |||
15 | |||
16 | // our own includes |
||
17 | #include "buffer.h" |
||
18 | |||
19 | |||
20 | // compiler-specific glue |
||
21 | #ifdef _MSC_VER |
||
22 | #include <io.h> |
||
23 | #include <direct.h> |
||
24 | #define strdup(s) _strdup((s)) |
||
25 | #define mkdir(p,m) _mkdir ((p)) |
||
26 | #define strncasecmp(s1,s2,l) _strnicmp ((s1), (s2), (l)) |
||
27 | #define strtok_r(s,delim,ctx) strtok_s ((s), (delim), (ctx)) |
||
28 | #define fseek(fp,off,m) _fseeki64 ((fp), (off), (m)) |
||
29 | #define ftell(fp) _ftelli64 ((fp)) |
||
30 | #if (SIZE_MAX == UINT64_MAX) |
||
31 | #define ssize_t signed long long |
||
32 | #else // SIZE_MAX != UINT64_MAX |
||
33 | #define ssize_t signed long |
||
34 | #endif // SIZE_MAX == UINT64_MAX |
||
35 | #define thread_local __declspec(thread) |
||
36 | #else // !_MSC_VER |
||
37 | #define thread_local __thread |
||
38 | #endif // _MSC_VER |
||
39 | |||
40 | |||
41 | // Windows-specific long path handling facilities |
||
42 | #if defined(_WIN32) && (!defined(MAXPATHLEN) || (MAXPATHLEN <= 260)) |
||
43 | extern void *__stdcall CreateFileW (const wchar_t *lpFileName, unsigned long dwDesiredAccess, unsigned long dwShareMode, void *lpSecurityAttributes, unsigned long dwCreationDisposition, unsigned long dwFlagsAndAttributes, void *hTemplateFile); |
||
44 | extern int __stdcall MultiByteToWideChar (unsigned int CodePage, unsigned long dwFlags, const char *lpMultiByteStr, int cbMultiByte, wchar_t *lpWideCharStr, int cchWideChar); |
||
45 | extern unsigned long __stdcall GetFullPathNameW (const wchar_t *lpFileName, unsigned long nBufferLength, wchar_t *lpBuffer, wchar_t **lpFilePart); |
||
46 | extern int __stdcall CloseHandle (void *hObject); |
||
47 | extern int __stdcall AreFileApisANSI (void); |
||
48 | thread_local static wchar_t wide_pathname[32768] = L""; |
||
49 | thread_local static wchar_t nt_pathname[32768] = L"\\\\?\\"; // initialized to NT prefix once and for all |
||
50 | thread_local static void *nt_filehandle = NULL; |
||
51 | thread_local static int intermediary_fd = -1; |
||
52 | #endif // defined(_WIN32) && (!defined(MAXPATHLEN) || (MAXPATHLEN <= 260)) |
||
53 | |||
54 | |||
55 | void Buffer_Forget (buffer_t *buffer) |
||
56 | { |
||
57 | // this function forgets a buffer's contents, freeing its associated memory buffer, |
||
58 | // and resets the buffer_t structure. |
||
59 | |||
60 | if (buffer->bytes != NULL) |
||
61 | free (buffer->bytes); // free buffer data |
||
62 | buffer->bytes = NULL; |
||
63 | buffer->size = 0; // reset data size |
||
64 | |||
65 | return; // finished, buffer structure is virgin again |
||
66 | } |
||
67 | |||
68 | |||
69 | int Buffer_Reset (buffer_t *buffer) |
||
70 | { |
||
71 | // this function makes a buffer contain only an empty string (with a null terminator), of size 0. |
||
72 | // Returns 1 on success, 0 on error (in which case errno is set to ENOMEM). |
||
73 | |||
74 | void *reallocated_ptr; |
||
75 | size_t padding; |
||
76 | |||
77 | // first, reallocate space for an empty string |
||
78 | reallocated_ptr = realloc (buffer->bytes, sizeof (size_t)); // always null-terminate buffers, but never report the null terminator |
||
79 | if (reallocated_ptr == NULL) |
||
80 | return (0); // on failure, return an error value (errno already set to ENOMEM) |
||
81 | |||
82 | buffer->bytes = reallocated_ptr; // save pointer to reallocated data |
||
83 | buffer->size = 0; // and reset buffer size |
||
84 | |||
85 | for (padding = 0; padding < sizeof (size_t); padding++) |
||
86 | buffer->bytes[padding] = 0; // always null-terminate buffers, silently |
||
87 | |||
88 | return (1); // buffer emptied successfully, return SUCCESS |
||
89 | } |
||
90 | |||
91 | |||
92 | int Buffer_Append (buffer_t *buffer, const void *data, const size_t data_size) |
||
93 | { |
||
94 | // this function appends the contents of data to a buffer's data. |
||
95 | // Buffer is always null-terminated, but the terminator is NOT counted in the buffer's length. |
||
96 | // Returns 1 on success, 0 on error (in which case errno is set to ENOMEM). |
||
97 | |||
98 | void *reallocated_ptr; |
||
99 | size_t padding; |
||
100 | |||
101 | // first, reallocate space to hold data_size bytes more |
||
102 | reallocated_ptr = realloc (buffer->bytes, buffer->size + data_size + sizeof (size_t)); // always null-terminate buffers, but never report the null terminator |
||
103 | if (reallocated_ptr == NULL) |
||
104 | return (0); // on failure, return an error value (errno already set to ENOMEM) |
||
105 | |||
106 | buffer->bytes = reallocated_ptr; // save pointer to reallocated data |
||
107 | if (data_size > 0) |
||
108 | { |
||
109 | if (data != NULL) |
||
110 | memcpy (&buffer->bytes[buffer->size], data, data_size); // if data points to something, write new data at the end |
||
111 | else |
||
112 | memset (&buffer->bytes[buffer->size], 0, data_size); // or, if data is NULL, set the extra space to zero |
||
113 | buffer->size += data_size; // and increment buffer size |
||
114 | } |
||
115 | for (padding = 0; padding < sizeof (size_t); padding++) |
||
116 | buffer->bytes[buffer->size + padding] = 0; // always null-terminate buffers, silently |
||
117 | |||
118 | return (1); // buffer appended successfully, return SUCCESS |
||
119 | } |
||
120 | |||
121 | |||
122 | int Buffer_AppendFormattedCString (buffer_t *buffer, const char *fmt_string, ...) |
||
123 | { |
||
124 | // this function appends some printf-style formatted data to a buffer's data. |
||
125 | // Buffer is always null-terminated, but the terminator is NOT counted in the buffer's length. |
||
126 | // Returns 1 on success, 0 on error (in which case errno is set to ENOMEM). |
||
127 | |||
128 | void *reallocated_ptr; |
||
129 | va_list varargs_list; |
||
130 | va_list varargs_copy; |
||
131 | char *saved_locale; |
||
132 | size_t extra_size; |
||
133 | size_t padding; |
||
134 | |||
135 | saved_locale = setlocale (LC_ALL, NULL); // get the current locale |
||
136 | if (saved_locale != NULL) |
||
137 | saved_locale = strdup (saved_locale); // preserve it |
||
138 | setlocale (LC_ALL, "C"); // format everything in the POSIX (international) locale |
||
139 | |||
140 | // first, concatenate varargs and see how much extra space we need |
||
141 | va_start (varargs_list, fmt_string); |
||
142 | va_copy (varargs_copy, varargs_list); |
||
143 | extra_size = vsnprintf (NULL, 0, fmt_string, varargs_list); |
||
144 | va_end (varargs_list); // release the variable arguments list |
||
145 | |||
146 | // now, reallocate space to hold extra_size bytes more |
||
147 | reallocated_ptr = realloc (buffer->bytes, buffer->size + extra_size + sizeof (size_t)); // always null-terminate buffers, but never report the null terminator |
||
148 | if (reallocated_ptr == NULL) |
||
149 | { |
||
150 | if (saved_locale != NULL) |
||
151 | { |
||
152 | setlocale (LC_ALL, saved_locale); // restore user locale |
||
153 | free (saved_locale); // and free the memory we used to back it up |
||
154 | } |
||
155 | return (0); // on failure, return an error value (errno already set to ENOMEM) |
||
156 | } |
||
157 | |||
158 | buffer->bytes = reallocated_ptr; // save pointer to reallocated data |
||
159 | if (extra_size > 0) |
||
160 | { |
||
161 | vsnprintf ((char *) &buffer->bytes[buffer->size], extra_size + 1, fmt_string, varargs_copy); // write new data at the end |
||
162 | buffer->size += extra_size; // and increment buffer size |
||
163 | } |
||
164 | va_end (varargs_copy); // release the variable arguments list |
||
165 | for (padding = 0; padding < sizeof (size_t); padding++) |
||
166 | buffer->bytes[buffer->size + padding] = 0; // always null-terminate buffers, silently |
||
167 | |||
168 | if (saved_locale != NULL) |
||
169 | { |
||
170 | setlocale (LC_ALL, saved_locale); // restore user locale |
||
171 | free (saved_locale); // and free the memory we used to back it up |
||
172 | } |
||
173 | return (1); // buffer appended successfully, return SUCCESS |
||
174 | } |
||
175 | |||
176 | |||
177 | int Buffer_Prepend (buffer_t *buffer, const void *data, const size_t data_size) |
||
178 | { |
||
179 | // this function prepends the contents of data to a buffer's data. |
||
180 | // Returns 1 on success, 0 on error (in which case errno is set to ENOMEM). |
||
181 | |||
182 | void *reallocated_ptr; |
||
183 | size_t padding; |
||
184 | |||
185 | // first, reallocate space to hold data_size bytes more |
||
186 | reallocated_ptr = realloc (buffer->bytes, data_size + buffer->size + sizeof (size_t)); // always null-terminate buffers, but never report the null terminator |
||
187 | if (reallocated_ptr == NULL) |
||
188 | return (0); // on failure, return an error value (errno already set to ENOMEM) |
||
189 | |||
190 | buffer->bytes = reallocated_ptr; // save pointer to reallocated data |
||
191 | if (data_size > 0) |
||
192 | { |
||
193 | #ifdef _MSC_VER |
||
194 | #pragma warning(push) |
||
195 | #pragma warning(disable:6385) // the Microsoft linter is wrong here. |
||
196 | #pragma warning(disable:6386) |
||
197 | #endif // _MSC_VER |
||
198 | if (buffer->size > 0) |
||
199 | memmove (&buffer->bytes[data_size], buffer->bytes, buffer->size); // move existing data to the end |
||
200 | memcpy (buffer->bytes, data, data_size); // write new data at the beginning |
||
201 | #ifdef _MSC_VER |
||
202 | #pragma warning(pop) |
||
203 | #endif // _MSC_VER |
||
204 | buffer->size += data_size; // and increment buffer size |
||
205 | } |
||
206 | for (padding = 0; padding < sizeof (size_t); padding++) |
||
207 | buffer->bytes[buffer->size + padding] = 0; // always null-terminate buffers, silently |
||
208 | |||
209 | return (1); // buffer appended successfully, return SUCCESS |
||
210 | } |
||
211 | |||
212 | |||
213 | int Buffer_PrependFormattedCString (buffer_t *buffer, const char *fmt_string, ...) |
||
214 | { |
||
215 | // this function prepends some printf-style formatted data to a buffer's data. |
||
216 | // Buffer is always null-terminated, but the terminator is NOT counted in the buffer's length. |
||
217 | // Returns 1 on success, 0 on error (in which case errno is set to either ENOMEM, EILSEQ or EINVAL). |
||
218 | |||
219 | void *reallocated_ptr; |
||
220 | va_list varargs_list; |
||
221 | va_list varargs_copy; |
||
222 | uint8_t first_byte; |
||
223 | char *saved_locale; |
||
224 | int extra_size; |
||
225 | size_t padding; |
||
226 | |||
227 | saved_locale = setlocale (LC_ALL, NULL); // get the current locale |
||
228 | if (saved_locale != NULL) |
||
229 | saved_locale = strdup (saved_locale); // preserve it |
||
230 | setlocale (LC_ALL, "C"); // format everything in the POSIX (international) locale |
||
231 | |||
232 | // first, concatenate varargs and see how much extra space we need |
||
233 | va_start (varargs_list, fmt_string); |
||
234 | va_copy (varargs_copy, varargs_list); |
||
235 | extra_size = vsnprintf (NULL, 0, fmt_string, varargs_list); |
||
236 | va_end (varargs_list); // release the variable arguments list |
||
237 | |||
238 | // was there an encoding error ? |
||
239 | if (extra_size < 0) |
||
240 | { |
||
241 | if (saved_locale != NULL) |
||
242 | { |
||
243 | setlocale (LC_ALL, saved_locale); // restore user locale |
||
244 | free (saved_locale); // and free the memory we used to back it up |
||
245 | } |
||
246 | return (0); // on failure, return an error value (errno already set to either EILSEQ or EINVAL) |
||
247 | } |
||
248 | |||
249 | // now, reallocate space to hold extra_size bytes more |
||
250 | reallocated_ptr = realloc (buffer->bytes, buffer->size + extra_size + sizeof (size_t)); // always null-terminate buffers, but never report the null terminator |
||
251 | if (reallocated_ptr == NULL) |
||
252 | { |
||
253 | if (saved_locale != NULL) |
||
254 | { |
||
255 | setlocale (LC_ALL, saved_locale); // restore user locale |
||
256 | free (saved_locale); // and free the memory we used to back it up |
||
257 | } |
||
258 | return (0); // on failure, return an error value (errno already set to ENOMEM) |
||
259 | } |
||
260 | |||
261 | buffer->bytes = reallocated_ptr; // save pointer to reallocated data |
||
262 | if (extra_size > 0) |
||
263 | { |
||
264 | #ifdef _MSC_VER |
||
265 | #pragma warning(push) |
||
266 | #pragma warning(disable:6001) // the Microsoft linter is wrong here. |
||
267 | #pragma warning(disable:6385) |
||
268 | #pragma warning(disable:6386) |
||
269 | #endif // _MSC_VER |
||
270 | if (buffer->size > 0) |
||
271 | { |
||
272 | first_byte = buffer->bytes[0]; // remember what the first byte is, since vsnprintf() will overwrite it with a null terminator |
||
273 | memmove (&buffer->bytes[extra_size], buffer->bytes, buffer->size); // move existing data to the end |
||
274 | } |
||
275 | vsnprintf ((char *) buffer->bytes, (size_t) extra_size + 1, fmt_string, varargs_copy); // write new data at the start (overwriting the first character of the old string with the null terminator) |
||
276 | #ifdef _MSC_VER |
||
277 | #pragma warning(pop) |
||
278 | #endif // _MSC_VER |
||
279 | if (buffer->size > 0) |
||
280 | buffer->bytes[extra_size] = first_byte; // put back the old first char, which was overwriten by vsnprintf() |
||
281 | buffer->size += extra_size; // and increment buffer size |
||
282 | } |
||
283 | va_end (varargs_copy); // release the variable arguments list |
||
284 | for (padding = 0; padding < sizeof (size_t); padding++) |
||
285 | buffer->bytes[buffer->size + padding] = 0; // always null-terminate buffers, silently |
||
286 | |||
287 | if (saved_locale != NULL) |
||
288 | { |
||
289 | setlocale (LC_ALL, saved_locale); // restore user locale |
||
290 | free (saved_locale); // and free the memory we used to back it up |
||
291 | } |
||
292 | return (1); // buffer appended successfully, return SUCCESS |
||
293 | } |
||
294 | |||
295 | |||
296 | int Buffer_InsertAt (buffer_t *buffer, const size_t insert_index, const void *data, const size_t data_size) |
||
297 | { |
||
298 | // this function inserts the contents of data to a buffer's data at position insert_index, |
||
299 | // shifting the remaining data from insert_index data_size forward. |
||
300 | // Returns 1 on success, 0 on error (in which case errno is set to ENOMEM). |
||
301 | |||
302 | void *reallocated_ptr; |
||
303 | size_t padding; |
||
304 | |||
305 | // reallocate data buffer at the right size |
||
306 | reallocated_ptr = realloc (buffer->bytes, buffer->size + data_size + sizeof (size_t)); // always null-terminate buffers, but never report the null terminator |
||
307 | if (reallocated_ptr == NULL) |
||
308 | return (0); // on failure, return an error value (errno already set to ENOMEM) |
||
309 | |||
310 | buffer->bytes = reallocated_ptr; // save pointer to reallocated data |
||
311 | if (insert_index > buffer->size) |
||
312 | memset (&buffer->bytes[buffer->size], 0, insert_index - buffer->size); // buffer was upsized: fill the upsized part with zeroes up to insert_index |
||
313 | if (data_size > 0) |
||
314 | { |
||
315 | memmove (&buffer->bytes[insert_index + data_size], &buffer->bytes[insert_index], buffer->size - insert_index); // move existing data to the end |
||
316 | memcpy (&buffer->bytes[insert_index], data, data_size); // write data in place |
||
317 | buffer->size += data_size; // update data size |
||
318 | } |
||
319 | for (padding = 0; padding < sizeof (size_t); padding++) |
||
320 | buffer->bytes[buffer->size + padding] = 0; // always null-terminate buffers, silently |
||
321 | |||
322 | return (1); // data successfully inserted into buffer, return SUCCESS |
||
323 | } |
||
324 | |||
325 | |||
326 | int Buffer_WriteAt (buffer_t *buffer, const size_t write_index, const void *data, const size_t data_size) |
||
327 | { |
||
328 | // this function writes the contents of data to a buffer's data at position write_index, expanding the buffer if necessary. |
||
329 | // Returns 1 on success, 0 on error (in which case errno is set to ENOMEM). |
||
330 | |||
331 | void *reallocated_ptr; |
||
332 | size_t padding; |
||
333 | |||
334 | // see if we need to grow the data |
||
335 | if (write_index + data_size > buffer->size) |
||
336 | { |
||
337 | // if so, reallocate data buffer at the right size |
||
338 | reallocated_ptr = realloc (buffer->bytes, write_index + data_size + sizeof (size_t)); // always null-terminate buffers, but never report the null terminator |
||
339 | if (reallocated_ptr == NULL) |
||
340 | return (0); // on failure, return an error value (errno already set to ENOMEM) |
||
341 | |||
342 | buffer->bytes = reallocated_ptr; // save pointer to reallocated data |
||
26 | pmbaty | 343 | if (write_index > buffer->size) |
344 | memset (&buffer->bytes[buffer->size], 0, write_index - buffer->size); // buffer was upsized: fill the upsized part with zeroes up to write_index |
||
16 | pmbaty | 345 | } |
346 | |||
347 | if (data_size > 0) |
||
348 | memcpy (&buffer->bytes[write_index], data, data_size); // write data in place |
||
349 | |||
350 | if (write_index + data_size > buffer->size) |
||
351 | buffer->size = write_index + data_size; // update data size only if it growed |
||
352 | else |
||
353 | { |
||
354 | // IMPORTANT: data hasn't growed, BUT the passed buffer *MIGHT NOT* have the \0\0\0\0 suffix (e.g. hand-constructed), so we MUST ensure there's space in it to put it! |
||
355 | |||
356 | // reallocate data buffer at the right size |
||
357 | reallocated_ptr = realloc (buffer->bytes, buffer->size + sizeof (size_t)); // always null-terminate buffers, but never report the null terminator |
||
358 | if (reallocated_ptr == NULL) |
||
359 | return (0); // on failure, return an error value (errno already set to ENOMEM) |
||
360 | buffer->bytes = reallocated_ptr; // save pointer to reallocated data |
||
361 | } |
||
362 | for (padding = 0; padding < sizeof (size_t); padding++) |
||
363 | buffer->bytes[buffer->size + padding] = 0; // always null-terminate buffers, silently |
||
364 | |||
365 | return (1); // buffer written successfully, return SUCCESS |
||
366 | } |
||
367 | |||
368 | |||
369 | int Buffer_SubsetFromTo (buffer_t *buffer, const size_t from_index, const size_t to_index) |
||
370 | { |
||
371 | // this function shortens the passed buffer by transforming it into its subset ranging |
||
372 | // from from_index (0 meaning beginning of buffer) to to_index (0 *NOT* meaning end of buffer!!! FIXME), |
||
373 | // shortening the buffer as necessary. |
||
374 | // Returns 1 on success, 0 on error (in which case errno is set to E2BIG). |
||
375 | |||
376 | void *reallocated_ptr; |
||
377 | size_t padding; |
||
378 | |||
379 | if ((from_index > to_index) || (to_index > buffer->size)) |
||
380 | { |
||
381 | errno = E2BIG; // set errno to a significant value |
||
382 | return (0); // consistency check: return FAILURE if we're requesting an out of bounds subset |
||
383 | } |
||
384 | |||
385 | buffer->size = to_index - from_index; // report the new buffer size |
||
386 | |||
387 | // should we shift the data ? |
||
388 | if (from_index > 0) |
||
389 | memmove (buffer->bytes, &buffer->bytes[from_index], buffer->size); // shift everything if necessary |
||
390 | |||
391 | // IMPORTANT: the passed buffer *MIGHT NOT* have the \0\0\0\0 suffix (e.g. hand-constructed), so we MUST ensure there's space in it to put it! |
||
392 | |||
393 | // reallocate data buffer at the right size |
||
394 | reallocated_ptr = realloc (buffer->bytes, buffer->size + sizeof (size_t)); // always null-terminate buffers, but never report the null terminator |
||
395 | if (reallocated_ptr == NULL) |
||
396 | return (0); // on failure, return an error value (errno is set) |
||
397 | buffer->bytes = reallocated_ptr; // save pointer to reallocated data |
||
398 | for (padding = 0; padding < sizeof (size_t); padding++) |
||
399 | buffer->bytes[buffer->size + padding] = 0; // always null-terminate buffers, silently |
||
400 | |||
401 | return (1); // buffer subset isolated successfully, return SUCCESS |
||
402 | } |
||
403 | |||
404 | |||
405 | int Buffer_ReadFromFile (buffer_t *buffer, const char *file_pathname) |
||
406 | { |
||
407 | // this function copies the contents of file_pathname in a newly allocated data buffer |
||
408 | // and fills in the buffer size accordingly. It is up to the caller to free that buffer. |
||
409 | // Returns 1 on success, 0 on error (in which case errno is set to either EACCESS, ENOMEM or EIO or anything else set by the underlying buffered stream API). |
||
410 | |||
411 | void *allocated_ptr; |
||
412 | int errno_backup; |
||
413 | size_t filesize; |
||
414 | size_t padding; |
||
415 | FILE *fp; // buffered I/O with FILE * is ___FASTER___ than unbuffered I/O with file descriptors |
||
416 | |||
417 | // start by reporting a zero length |
||
418 | buffer->bytes = NULL; |
||
419 | buffer->size = 0; |
||
420 | |||
421 | // open file for binary reading (DO NOT stat() BEFORE, ELSE WE WOULD NEED TO APPLY THE SAME LONG PATHNAME WORKAROUND TWICE) |
||
422 | #if defined(_WIN32) && (!defined(MAXPATHLEN) || (MAXPATHLEN <= 260)) |
||
423 | // on Windows, work around of the MAX_PATH limitation by using the Unicode APIs and NT-style pathnames |
||
424 | // IMPLEMENTATION NOTE: ON WINDOWS, ALL PATHS ARE CONVERTED TO UTF-16 IN MSVCRT.DLL BEFORE THE ACTUAL SYSTEM CALL, |
||
425 | // SO IT *IS* FASTER TO SYSTEMATICALLY CONVERT ALL PATHS TO NT PATHS, LIKE THEY DO IN LLVM/CLANG (see Path.inc) |
||
426 | errno = EACCES; // set errno to something significant |
||
427 | fp = NULL; // assume failure until told otherwise |
||
428 | MultiByteToWideChar (AreFileApisANSI () ? 0 /*CP_ACP*/ : 1 /*CP_OEMCP*/, 0, file_pathname, -1, wide_pathname, sizeof (wide_pathname) / sizeof (wchar_t)); // convert pathname to wide characters, including the NULL terminator |
||
429 | GetFullPathNameW (wide_pathname, sizeof (nt_pathname) / sizeof (wchar_t) - 4, &nt_pathname[4], NULL); // canonicalize the resulting pathname and write it just after the NT path prefix |
||
430 | nt_filehandle = CreateFileW (nt_pathname, 0x80000000 /*GENERIC_READ*/, 0x3 /*FILE_SHARE_READ|FILE_SHARE_WRITE*/, NULL, 3 /*OPEN_EXISTING*/, 0x80 /*FILE_ATTRIBUTE_NORMAL*/, NULL); // now call CreateFile and get the filesystem object handle |
||
431 | if (nt_filehandle != NULL) |
||
432 | { |
||
433 | intermediary_fd = _open_osfhandle ((intptr_t) nt_filehandle, O_RDONLY); // and convert this handle back into a POSIX file descriptor |
||
434 | if (intermediary_fd != -1) |
||
435 | { |
||
436 | fp = _fdopen (intermediary_fd, "rb"); // now convert this handle to a buffered libc file descriptor |
||
437 | if (fp == NULL) |
||
438 | _close (intermediary_fd); // if we couldn't get a libc file descriptor out of that POSIX file descriptor, close it anyway |
||
439 | } |
||
440 | else |
||
441 | CloseHandle (nt_filehandle); // if we couldn't get a POSIX file descriptor out of that handle, close it anyway |
||
442 | } |
||
443 | if (fp != NULL) |
||
444 | errno = 0; // if file could be opened, clear errno |
||
445 | #else // !defined(_WIN32) || (defined(MAXPATHLEN) && (MAXPATHLEN > 260)) |
||
446 | fp = fopen (file_pathname, "rb"); // POSIX-compliant system. Good boy. |
||
447 | #endif // defined(_WIN32) && (!defined(MAXPATHLEN) || (MAXPATHLEN <= 260)) |
||
448 | if (fp == NULL) |
||
449 | return (0); // on fopen() failure, return an error value (and errno is hopefully set to something meaningful) |
||
450 | |||
451 | // measure file size |
||
452 | fseek (fp, 0, SEEK_END); |
||
453 | filesize = ftell (fp); |
||
454 | fseek (fp, 0, SEEK_SET); |
||
455 | |||
456 | // allocate enough space for it |
||
457 | allocated_ptr = malloc (filesize + sizeof (size_t)); // always null-terminate buffers, but never report the null terminator |
||
458 | if (allocated_ptr == NULL) |
||
459 | { |
||
460 | errno_backup = errno; // preserve errno on errors |
||
461 | fclose (fp); // on malloc() error, close file |
||
462 | errno = errno_backup; |
||
463 | return (0); // and return an error value (errno already set to ENOMEM) |
||
464 | } |
||
465 | |||
466 | buffer->bytes = allocated_ptr; // save pointer to newly allocated data |
||
467 | |||
468 | // and read it at once |
||
469 | if (fread (buffer->bytes, 1, filesize, fp) != filesize) |
||
470 | { |
||
471 | errno_backup = (errno ? errno : EIO); // preserve errno on errors; if it isn't set, set it to EIO |
||
472 | fclose (fp); // fread() failed, close file |
||
473 | free (buffer->bytes); |
||
474 | errno = errno_backup; |
||
475 | return (0); // on failure, return an error value (errno already set to EIO - or something else set by the underlying buffered stream API) |
||
476 | } |
||
477 | |||
478 | fclose (fp); // finished, close file |
||
479 | buffer->size = filesize; // report its size |
||
480 | for (padding = 0; padding < sizeof (size_t); padding++) |
||
481 | buffer->bytes[buffer->size + padding] = 0; // always null-terminate buffers, silently |
||
482 | |||
483 | return (1); // and return SUCCESS |
||
484 | } |
||
485 | |||
486 | |||
487 | int Buffer_WriteToFile (buffer_t *buffer, const char *file_pathname) |
||
488 | { |
||
489 | // this function copies the contents of buffer's data into the file pointed to by |
||
490 | // file_pathname. Returns 1 on success, 0 on error (in which case errno is set to either EINVAL, ENOMEM, EACCESS or EIO or anything else set by the underlying buffered stream API). |
||
491 | // NOTE: "<stdout>" and "<stderr>" pathnames can be used to write to stdout and stderr, |
||
492 | // else all directories up to file_pathname are recursively created if necessary. |
||
493 | |||
494 | size_t string_index; |
||
495 | size_t length; |
||
496 | int errno_backup; |
||
497 | char *temp_pathname; |
||
498 | char *separator; |
||
499 | char *context; |
||
500 | FILE *fp; // buffered I/O with FILE * is ___FASTER___ than unbuffered I/O with file descriptors |
||
501 | |||
502 | // do we want to write to the standard output or the standard error? |
||
503 | if (strcmp (file_pathname, "<stdout>") == 0) |
||
504 | fp = stdout; |
||
505 | else if (strcmp (file_pathname, "<stderr>") == 0) |
||
506 | fp = stderr; |
||
507 | else |
||
508 | { |
||
509 | // ensure all the path up to file exists, create directories on the fly if needed |
||
510 | // FIXME: this won't work on Windows with symlinks pointing to nonexistent directories... |
||
511 | length = strlen (file_pathname); |
||
512 | if (length == 0) |
||
513 | { |
||
514 | errno = EINVAL; // if the passed pathname is empty, report invalid parameter |
||
515 | return (0); // and return an error value |
||
516 | } |
||
517 | temp_pathname = strdup (file_pathname); // have a working copy of file_pathname |
||
518 | if (temp_pathname == NULL) |
||
519 | return (0); // on strdup() failure, return an error value (errno already set to ENOMEM) |
||
520 | for (string_index = length - 1; string_index != SIZE_MAX; string_index--) // i.e. loop until it overflows |
||
521 | if ((temp_pathname[string_index] == '/') || (temp_pathname[string_index] == '\\')) |
||
522 | break; // look for the last directory separator and stop as soon as we find it |
||
523 | if (string_index != SIZE_MAX) |
||
524 | { |
||
525 | for (; string_index < length; string_index++) |
||
526 | temp_pathname[string_index] = 0; // if we found one, break there so as to have just the path and clear the rest of the string |
||
527 | context = NULL; |
||
528 | separator = strtok_r (&temp_pathname[1], "/\\", &context); // for each separator in the remaining string past the first one... |
||
529 | while (separator != NULL) |
||
530 | { |
||
531 | (void) mkdir (temp_pathname, 0755); // create directories recursively (FIXME: THIS IS NOT COMPATIBLE WITH LONG FILENAMES) |
||
532 | temp_pathname[strlen (temp_pathname)] = '/'; // put back the separator |
||
533 | separator = strtok_r (NULL, "/\\", &context); // and look for the next one |
||
534 | } |
||
535 | } |
||
536 | free (temp_pathname); // release our working copy of file_pathname |
||
537 | |||
538 | // open file for binary writing |
||
539 | #if defined(_WIN32) && (!defined(MAXPATHLEN) || (MAXPATHLEN <= 260)) |
||
540 | // on Windows, work around of the MAX_PATH limitation by using the Unicode APIs and NT-style pathnames |
||
541 | // IMPLEMENTATION NOTE: ON WINDOWS, ALL PATHS ARE CONVERTED TO UTF-16 IN MSVCRT.DLL BEFORE THE ACTUAL SYSTEM CALL, |
||
542 | // SO IT *IS* FASTER TO SYSTEMATICALLY CONVERT ALL PATHS TO NT PATHS, LIKE THEY DO IN LLVM/CLANG (see Path.inc) |
||
543 | errno = EACCES; // set errno to something significant |
||
544 | fp = NULL; // assume failure until told otherwise |
||
545 | MultiByteToWideChar (AreFileApisANSI () ? 0 /*CP_ACP*/ : 1 /*CP_OEMCP*/, 0, file_pathname, -1, wide_pathname, sizeof (wide_pathname) / sizeof (wchar_t)); // convert pathname to wide characters, including the NULL terminator |
||
546 | GetFullPathNameW (wide_pathname, sizeof (nt_pathname) / sizeof (wchar_t) - 4, &nt_pathname[4], NULL); // canonicalize the resulting pathname and write it just after the NT path prefix |
||
547 | nt_filehandle = CreateFileW (nt_pathname, 0x40000000 /*GENERIC_WRITE*/, 0x3 /*FILE_SHARE_READ|FILE_SHARE_WRITE*/, NULL, 2 /*CREATE_ALWAYS [MANDATORY for truncation!]*/, 0x80 /*FILE_ATTRIBUTE_NORMAL*/, NULL); // now call CreateFile and get the filesystem object handle |
||
548 | if (nt_filehandle != NULL) |
||
549 | { |
||
550 | intermediary_fd = _open_osfhandle ((intptr_t) nt_filehandle, O_WRONLY); // and convert this handle back into a POSIX file descriptor |
||
551 | if (intermediary_fd != -1) |
||
552 | { |
||
553 | fp = _fdopen (intermediary_fd, "wb"); // now convert this handle to a buffered libc file descriptor |
||
554 | if (fp == NULL) |
||
555 | _close (intermediary_fd); // if we couldn't get a libc file descriptor out of that POSIX file descriptor, close it anyway |
||
556 | } |
||
557 | else |
||
558 | CloseHandle (nt_filehandle); // if we couldn't get a POSIX file descriptor out of that handle, close it anyway |
||
559 | } |
||
560 | if (fp != NULL) |
||
561 | errno = 0; // if file could be opened, clear errno |
||
562 | #else // !defined(_WIN32) || (defined(MAXPATHLEN) && (MAXPATHLEN > 260)) |
||
563 | fp = fopen (file_pathname, "wb"); // POSIX-compliant system. Good boy. |
||
564 | #endif // defined(_WIN32) && (!defined(MAXPATHLEN) || (MAXPATHLEN <= 260)) |
||
565 | if (fp == NULL) |
||
566 | return (0); // on fopen() failure, return an error value (and errno is hopefully set to something meaningful) |
||
567 | } |
||
568 | |||
569 | // and write it at once |
||
570 | if (fwrite (buffer->bytes, 1, buffer->size, fp) != buffer->size) |
||
571 | { |
||
572 | errno_backup = (errno ? errno : EIO); // preserve errno on errors; if it isn't set, set it to EIO |
||
573 | if ((fp != stdout) && (fp != stderr)) |
||
574 | fclose (fp); // either case, close the file |
||
575 | errno = errno_backup; |
||
576 | return (0); // on failure, return an error value (errno already set to EIO - or something else set by the underlying buffered stream API) |
||
577 | } |
||
578 | |||
579 | if ((fp != stdout) && (fp != stderr)) |
||
580 | fclose (fp); // finished, close file (only if it was not one of the standard streams) |
||
581 | |||
582 | errno = 0; // no error |
||
583 | return (1); // and return SUCCESS |
||
584 | } |
||
585 | |||
586 | |||
587 | uint8_t *Buffer_FindFirst (const buffer_t *buffer, const void *needle, const size_t needle_length) |
||
588 | { |
||
589 | // returns a pointer to the first occurence of needle in buffer haystack, or NULL if not found |
||
590 | |||
591 | size_t byte_index; |
||
592 | |||
593 | if (buffer->size < needle_length) |
||
594 | return (NULL); // consistency check: if buffer is too small for needle, return NULL |
||
595 | else if ((buffer->size == needle_length) && (memcmp (buffer->bytes, needle, buffer->size) == 0)) |
||
596 | return (buffer->bytes); // special case where needle is exactly as long as buffer |
||
597 | |||
598 | // parse buffer from start to end |
||
599 | for (byte_index = 0; byte_index <= buffer->size - needle_length; byte_index++) // inferior OR EQUAL, meaning last position INCLUDED! |
||
600 | if (memcmp (&buffer->bytes[byte_index], needle, needle_length) == 0) |
||
601 | return (&buffer->bytes[byte_index]); // return the first match we find |
||
602 | |||
603 | return (NULL); // definitely no needle in buffer |
||
604 | } |
||
605 | |||
606 | |||
607 | uint8_t *Buffer_FindFirstCI (const buffer_t *buffer, const char *needle) |
||
608 | { |
||
609 | // returns a pointer to the first occurence of needle in buffer haystack (CASE INSENSITIVELY), or NULL if not found |
||
610 | |||
611 | size_t needle_length; |
||
612 | size_t byte_index; |
||
613 | |||
614 | needle_length = strlen (needle); // measure needle length |
||
615 | |||
616 | if (buffer->size < needle_length) |
||
617 | return (NULL); // consistency check: if buffer is too small for needle, return NULL |
||
618 | else if ((buffer->size == needle_length) && (memcmp (buffer->bytes, needle, buffer->size) == 0)) |
||
619 | return (buffer->bytes); // special case where needle is exactly as long as buffer |
||
620 | |||
621 | // parse buffer from start to end |
||
622 | for (byte_index = 0; byte_index <= buffer->size - needle_length; byte_index++) // inferior OR EQUAL, meaning last position INCLUDED! |
||
623 | if (strncasecmp ((const char *) &buffer->bytes[byte_index], needle, needle_length) == 0) |
||
624 | return (&buffer->bytes[byte_index]); // return the first match we find |
||
625 | |||
626 | return (NULL); // definitely no needle in buffer |
||
627 | } |
||
628 | |||
629 | |||
630 | uint8_t *Buffer_FindLast (const buffer_t *buffer, const void *needle, const size_t needle_length) |
||
631 | { |
||
632 | // returns a pointer to the last occurence of needle in buffer haystack, or NULL if not found |
||
633 | |||
634 | size_t byte_index; |
||
635 | |||
636 | if (buffer->size < needle_length) |
||
637 | return (NULL); // consistency check: if buffer is too small for needle, return NULL |
||
638 | else if ((buffer->size == needle_length) && (memcmp (buffer->bytes, needle, buffer->size) == 0)) |
||
639 | return (buffer->bytes); // special case where needle is exactly as long as buffer |
||
640 | |||
641 | // parse buffer from end to start |
||
642 | for (byte_index = buffer->size - needle_length; byte_index != SIZE_MAX; byte_index--) |
||
643 | if (memcmp (&buffer->bytes[byte_index], needle, needle_length) == 0) |
||
644 | return (&buffer->bytes[byte_index]); // return the last match we find |
||
645 | |||
646 | return (NULL); // definitely no needle in buffer |
||
647 | } |
||
648 | |||
649 | |||
650 | uint8_t *Buffer_FindLastCI (const buffer_t *buffer, const char *needle) |
||
651 | { |
||
652 | // returns a pointer to the last occurence of needle in buffer haystack (CASE INSENSITIVELY), or NULL if not found |
||
653 | |||
654 | size_t needle_length; |
||
655 | size_t byte_index; |
||
656 | |||
657 | needle_length = strlen (needle); // measure needle length |
||
658 | |||
659 | if (buffer->size < needle_length) |
||
660 | return (NULL); // consistency check: if buffer is too small for needle, return NULL |
||
661 | else if ((buffer->size == needle_length) && (memcmp (buffer->bytes, needle, buffer->size) == 0)) |
||
662 | return (buffer->bytes); // special case where needle is exactly as long as buffer |
||
663 | |||
664 | // parse buffer from end to start |
||
665 | for (byte_index = buffer->size - needle_length; byte_index != SIZE_MAX; byte_index--) |
||
666 | if (strncasecmp ((const char *) &buffer->bytes[byte_index], needle, needle_length) == 0) |
||
667 | return (&buffer->bytes[byte_index]); // return the last match we find |
||
668 | |||
669 | return (NULL); // definitely no needle in buffer |
||
670 | } |
||
671 | |||
672 | |||
673 | size_t Buffer_Replace (buffer_t *buffer, const void *needle, const size_t needle_length, const void *replacement, const size_t replacement_length, const int howmany_with_zero_for_all_and_minus_for_backwards) |
||
674 | { |
||
675 | // replaces as many occurences of needle with replacement in buffer haystack, reallocating as needed. |
||
676 | // If howmany is strictly positive, replacements stop after howmany replacements are made in the forward direction. |
||
677 | // If howmany is strictly negative, replacements stop after -howmany replacements are made in the REVERSE direction. |
||
678 | // Returns the number of replacements made, or 0 if none was made OR an error occured (in which case check errno, which may be E2BIG or ENOMEM). |
||
679 | |||
680 | void *reallocated_ptr; |
||
681 | void *safe_needle; |
||
682 | size_t replacements_made; |
||
683 | size_t replacements_max; |
||
684 | size_t byte_index; |
||
685 | size_t padding; |
||
686 | int realloc_done; |
||
687 | |||
688 | if (needle_length > buffer->size) |
||
689 | { |
||
690 | errno = E2BIG; // consistency check: if buffer is too small for needle, set errno to E2BIG |
||
691 | return (0); // and return an error value |
||
692 | } |
||
693 | |||
694 | if (needle_length == 0) |
||
695 | return (0); // consistency check: if needle is an empty string, return 0 (no replacement made) |
||
696 | |||
697 | // is the number of replacements to do NOT exactly one AND does the needle we want to replace already belong to the buffer (meaning it will disappear during replacement) ? |
||
698 | if ((abs (howmany_with_zero_for_all_and_minus_for_backwards) != 1) && ((uint8_t *) needle > buffer->bytes) && ((uint8_t *) needle < buffer->bytes + buffer->size)) |
||
699 | { |
||
700 | safe_needle = (uint8_t *) malloc (needle_length); // allocate space for a copy of the needle |
||
701 | if (safe_needle == NULL) |
||
702 | return (0); // on allocation error, return 0 (errno already set to ENOMEM) |
||
703 | memcpy (safe_needle, needle, needle_length); // and copy needle somewhere safe |
||
704 | } |
||
705 | else |
||
706 | safe_needle = (uint8_t *) needle; // else we can use needle directly and spare us an allocation |
||
707 | |||
708 | // see how much we need to replace and in what order |
||
709 | replacements_made = 0; |
||
710 | realloc_done = 0; |
||
711 | if (howmany_with_zero_for_all_and_minus_for_backwards < 0) |
||
712 | { |
||
713 | // backwards direction |
||
714 | replacements_max = -howmany_with_zero_for_all_and_minus_for_backwards; |
||
715 | |||
716 | // parse buffer from end to start |
||
717 | for (byte_index = buffer->size - needle_length; byte_index != SIZE_MAX; byte_index--) |
||
718 | { |
||
719 | if (memcmp (&buffer->bytes[byte_index], safe_needle, needle_length) != 0) |
||
720 | continue; // if there's no match at this location, skip it |
||
721 | |||
722 | // a replacement should be made here: see if we need to grow the data |
||
723 | if (replacement_length > needle_length) |
||
724 | { |
||
725 | // if so, reallocate data buffer at the right size |
||
726 | reallocated_ptr = realloc (buffer->bytes, buffer->size + replacement_length - needle_length + sizeof (size_t)); // always null-terminate buffers, but never report the null terminator |
||
727 | if (reallocated_ptr == NULL) |
||
728 | { |
||
729 | replacements_made = 0; |
||
730 | break; // on failure, return an error value (errno is set) |
||
731 | } |
||
732 | |||
733 | buffer->bytes = reallocated_ptr; // save pointer to reallocated data |
||
734 | realloc_done = 1; // and remember that a reallocation was done |
||
735 | } |
||
736 | |||
737 | // move the remaining first if necessary, then put the replacement in place |
||
738 | if (needle_length != replacement_length) |
||
739 | memmove (&buffer->bytes[byte_index + replacement_length], &buffer->bytes[byte_index + needle_length], buffer->size - (byte_index + needle_length)); |
||
740 | if (replacement_length > 0) |
||
741 | memcpy (&buffer->bytes[byte_index], replacement, replacement_length); |
||
742 | buffer->size += replacement_length - needle_length; // adjust buffer size |
||
743 | |||
744 | replacements_made++; // one more replacement has been made |
||
745 | if ((replacements_max > 0) && (replacements_made >= replacements_max)) |
||
746 | break; // if a maximum replacements count was specified, stop here |
||
747 | } |
||
748 | } |
||
749 | else |
||
750 | { |
||
751 | // forward direction |
||
752 | replacements_max = howmany_with_zero_for_all_and_minus_for_backwards; |
||
753 | |||
754 | // parse buffer from start to end |
||
755 | for (byte_index = 0; byte_index <= buffer->size - needle_length; byte_index++) // inferior OR EQUAL, meaning last position INCLUDED! |
||
756 | { |
||
757 | if (memcmp (&buffer->bytes[byte_index], safe_needle, needle_length) != 0) |
||
758 | continue; // if there's no match at this location, skip it |
||
759 | |||
760 | // a replacement should be made here: see if we need to grow the data |
||
761 | if (replacement_length > needle_length) |
||
762 | { |
||
763 | // if so, reallocate data buffer at the right size |
||
764 | reallocated_ptr = realloc (buffer->bytes, buffer->size + replacement_length - needle_length + sizeof (size_t)); // always null-terminate buffers, but never report the null terminator |
||
765 | if (reallocated_ptr == NULL) |
||
766 | { |
||
767 | replacements_made = 0; |
||
768 | break; // on failure, return an error value (errno is set) |
||
769 | } |
||
770 | |||
771 | buffer->bytes = reallocated_ptr; // save pointer to reallocated data |
||
772 | realloc_done = 1; // and remember that a reallocation was done |
||
773 | } |
||
774 | |||
775 | // move the remaining first (including the null terminator) if necessary, then put the replacement in place |
||
776 | if (needle_length != replacement_length) |
||
777 | memmove (&buffer->bytes[byte_index + replacement_length], &buffer->bytes[byte_index + needle_length], buffer->size - (byte_index + needle_length)); |
||
778 | if (replacement_length > 0) |
||
779 | memcpy (&buffer->bytes[byte_index], replacement, replacement_length); |
||
780 | buffer->size += replacement_length - needle_length; // adjust buffer size |
||
781 | |||
782 | replacements_made++; // one more replacement has been made |
||
783 | if ((replacements_max > 0) && (replacements_made >= replacements_max)) |
||
784 | break; // if a maximum replacements count was specified, stop here |
||
785 | |||
786 | byte_index += replacement_length - 1; // jump over this replacement and proceed to the remaining of the string |
||
787 | } |
||
788 | } |
||
789 | |||
790 | if (safe_needle != needle) |
||
791 | free (safe_needle); // if we had to allocate space for a safe copy of the needle, free it now |
||
792 | |||
793 | // IMPORTANT: in case we haven't reallocated ourselves, the passed buffer *MIGHT NOT* have the \0\0\0\0 suffix (e.g. hand-constructed), so we MUST ensure there's space in it to put it! |
||
794 | if (!realloc_done) |
||
795 | { |
||
796 | // reallocate data buffer at the right size |
||
797 | reallocated_ptr = realloc (buffer->bytes, buffer->size + sizeof (size_t)); // always null-terminate buffers, but never report the null terminator |
||
798 | if (reallocated_ptr == NULL) |
||
799 | return (0); // on failure, return an error value (errno already set to ENOMEM) |
||
800 | buffer->bytes = reallocated_ptr; // save pointer to reallocated data |
||
801 | } |
||
802 | for (padding = 0; padding < sizeof (size_t); padding++) |
||
803 | buffer->bytes[buffer->size + padding] = 0; // always null-terminate buffers, silently |
||
804 | |||
805 | return (replacements_made); // finished, return the number of replacements made |
||
806 | } |
||
807 | |||
808 | |||
809 | int Buffer_Compare (buffer_t *buffer, const void *data, const size_t data_size) |
||
810 | { |
||
811 | // compares a buffer with some data of data_size length, and return 0 if they match, non-zero if they differ. |
||
812 | // If their size differ, a non-zero value (equivalent to the string "DIFF") is returned. |
||
813 | // If their content differ, the result of memcmp() is returned. |
||
814 | |||
815 | if ((data == buffer->bytes) && (data_size == buffer->size)) |
||
816 | return (0); // if both buffers are the same one, return 0 |
||
817 | else if (data_size != buffer->size) |
||
818 | return ((int) ((ssize_t) data_size - (ssize_t) buffer->size)); // buffers differ in size, return the shortest first |
||
819 | |||
820 | return (memcmp (buffer->bytes, data, data_size)); // do a memcmp() on both buffers |
||
821 | } |
||
822 | |||
823 | |||
824 | size_t Buffer_OffsetOf (buffer_t *buffer, const void *something) |
||
825 | { |
||
826 | // returns the absolute offset of the data pointed to by something relatively to the beginning of buffer data |
||
827 | // if something points outside of buffer, returns SIZE_MAX (in which case errno is set to EINVAL) |
||
828 | |||
829 | if (((uint8_t *) something < buffer->bytes) || ((uint8_t *) something > buffer->bytes + buffer->size)) |
||
830 | { |
||
831 | errno = EINVAL; // consistency check: if the queried pointer falls outside of the buffer, set errno to EINVAL |
||
832 | return (SIZE_MAX); // and return an error code |
||
833 | } |
||
834 | |||
835 | return ((size_t) something - (size_t) buffer->bytes); // queried pointer lies within buffer: return its offset, which will be between 0 and buffer->size inclusive |
||
836 | } |
||
837 | |||
838 | |||
839 | int Buffer_TrimLeftUntil (buffer_t *buffer, const uint8_t *trimmable_bytes, const size_t trimmable_count, const int howmany) |
||
840 | { |
||
841 | // removes leading characters from a buffer's data and returns 1 on success, or 0 if a memory |
||
842 | // reallocation error happens (which shouldn't ever since we're reallocating smaller), in which case errno is set to ENOMEM. |
||
843 | // Any character in the trimmable_bytes array is a candidate for elimination. |
||
844 | |||
845 | void *reallocated_ptr; |
||
846 | size_t removed_count; |
||
847 | size_t max_count; |
||
848 | size_t padding; |
||
849 | |||
850 | removed_count = 0; // parse buffer string from the end looking for any of the trimmable bytes |
||
851 | max_count = (howmany == 0 ? buffer->size : howmany); // see how many characters maximum we are allowed to trim |
||
852 | while ((removed_count < max_count) && (memchr (trimmable_bytes, buffer->bytes[removed_count], trimmable_count) != NULL)) |
||
853 | removed_count++; // count the number of bytes to remove |
||
854 | |||
855 | // did we find something to trim ? |
||
856 | if (removed_count > 0) |
||
857 | { |
||
858 | // if so, shift the buffer data start by the number of bytes to remove |
||
859 | memmove (buffer->bytes, &buffer->bytes[removed_count], buffer->size - removed_count); |
||
860 | buffer->size -= removed_count; // adjust the new buffer size |
||
861 | |||
862 | // reallocate data buffer at the right size |
||
863 | reallocated_ptr = realloc (buffer->bytes, buffer->size + sizeof (size_t)); // always null-terminate buffers, but never report the null terminator |
||
864 | if (reallocated_ptr == NULL) |
||
865 | return (0); // on failure, return an error value (errno already set to ENOMEM) |
||
866 | |||
867 | buffer->bytes = reallocated_ptr; // save pointer to reallocated data |
||
868 | for (padding = 0; padding < sizeof (size_t); padding++) |
||
869 | buffer->bytes[buffer->size + padding] = 0; // always null-terminate buffers, silently |
||
870 | } |
||
871 | |||
872 | return (1); // and return SUCCESS |
||
873 | } |
||
874 | |||
875 | |||
876 | int Buffer_TrimRightUntil (buffer_t *buffer, const uint8_t *trimmable_bytes, const size_t trimmable_count, const int howmany) |
||
877 | { |
||
878 | // removes trailing characters from a buffer's data and returns 1 on success, or 0 if a memory |
||
879 | // reallocation error happens (which shouldn't ever since we're reallocating smaller), in which case errno is set to ENOMEM. |
||
880 | // Any character in the trimmable_bytes array is a candidate for elimination. |
||
881 | |||
882 | void *reallocated_ptr; |
||
883 | size_t removed_count; |
||
884 | size_t max_count; |
||
885 | size_t padding; |
||
886 | |||
887 | removed_count = 0; // parse buffer string from the end looking for any of the trimmable bytes |
||
888 | max_count = (howmany == 0 ? buffer->size : howmany); // see how many characters maximum we are allowed to trim |
||
889 | while ((buffer->size > 0) && (removed_count < max_count) && (memchr (trimmable_bytes, buffer->bytes[buffer->size - 1], trimmable_count) != NULL)) |
||
890 | { |
||
891 | removed_count++; // count the number of bytes to remove |
||
892 | buffer->size--; // and decrease buffer size appropriately |
||
893 | } |
||
894 | |||
895 | // did we find something to trim ? |
||
896 | if (removed_count > 0) |
||
897 | { |
||
898 | // if so, reallocate data buffer at the right size |
||
899 | reallocated_ptr = realloc (buffer->bytes, buffer->size + sizeof (size_t)); // always null-terminate buffers, but never report the null terminator |
||
900 | if (reallocated_ptr == NULL) |
||
901 | return (0); // on failure, return an error value (errno already set to ENOMEM) |
||
902 | |||
903 | buffer->bytes = reallocated_ptr; // save pointer to reallocated data |
||
904 | for (padding = 0; padding < sizeof (size_t); padding++) |
||
905 | buffer->bytes[buffer->size + padding] = 0; // always null-terminate buffers, silently |
||
906 | } |
||
907 | |||
908 | return (1); // and return SUCCESS |
||
909 | } |
||
910 | |||
911 | |||
912 | int Buffer_GetNthParts (buffer_t *buffer, const size_t wanted_part_index, const int howmany, const void *separator, const size_t separator_len, const int should_parse_as_text, buffer_t *preinitialized_outbuf) |
||
913 | { |
||
914 | // extract and returns the Nth bit of buffer when split according to separator and return whether |
||
915 | // it succeeded or not (in which case errno is set to EINVAL). |
||
916 | // IMPORTANT: preinitialized_outbuf MUST have been properly initialized before! |
||
917 | |||
918 | ptrdiff_t extractstart_index; |
||
919 | size_t fieldstart_index; |
||
920 | size_t char_index; |
||
921 | size_t part_index; |
||
922 | |||
923 | // handle special case first |
||
924 | if (howmany == 0) |
||
925 | { |
||
926 | if (preinitialized_outbuf->bytes != NULL) |
||
927 | Buffer_Forget (preinitialized_outbuf); // release output buffer if it was previously allocated |
||
928 | Buffer_InitWithByteArray (preinitialized_outbuf, ""); // if we want a zero-length slice, prepare an empty buffer |
||
929 | return (1); // and return SUCCESS |
||
930 | } |
||
931 | |||
932 | // read buffer characters and identify parts on the fly |
||
933 | extractstart_index = -1; |
||
934 | fieldstart_index = 0; |
||
935 | part_index = 0; |
||
936 | for (char_index = 0; char_index < buffer->size; char_index++) |
||
937 | { |
||
938 | // have we NOT found yet the start field and is this it ? |
||
939 | if ((extractstart_index == -1) && (part_index == wanted_part_index)) |
||
940 | extractstart_index = fieldstart_index; // if so, remember where it starts |
||
941 | |||
942 | if (should_parse_as_text && (buffer->bytes[char_index] == 0)) |
||
943 | break; // when end of text reached, stop searching |
||
944 | else if ((char_index + separator_len <= buffer->size) && (memcmp (&buffer->bytes[char_index], separator, separator_len) == 0)) |
||
945 | { |
||
946 | part_index++; // a separator is here, meaning that one part more was read |
||
947 | |||
948 | if ((howmany > 0) && (part_index == wanted_part_index + howmany)) |
||
949 | break; // was it ALL that we were looking for ? if so, stop searching |
||
950 | |||
951 | char_index += separator_len; // skip the separator |
||
952 | fieldstart_index = char_index; // and remember a new part starts here |
||
953 | } |
||
954 | } |
||
955 | |||
956 | // have we found the part we were looking for ? |
||
957 | if (extractstart_index != -1) |
||
958 | { |
||
959 | if (preinitialized_outbuf->bytes != NULL) |
||
960 | Buffer_Forget (preinitialized_outbuf); // release output buffer if it was previously allocated |
||
961 | Buffer_InitWithData (preinitialized_outbuf, &buffer->bytes[extractstart_index], char_index - extractstart_index); // if so, so copy it |
||
962 | return (1); // and return SUCCESS |
||
963 | } |
||
964 | |||
965 | // if we reach here, it means we haven't identified the part we were looking for |
||
966 | errno = EINVAL; // so tell the user the wanted_part_index parameter was invalid |
||
967 | return (0); // and return FAILURE |
||
968 | } |