Go to most recent revision | Details | 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 |
||
343 | memset (&buffer->bytes[buffer->size], 0, write_index - buffer->size); // buffer was upsized: fill the upsized part with zeroes up to write_index |
||
344 | } |
||
345 | |||
346 | if (data_size > 0) |
||
347 | memcpy (&buffer->bytes[write_index], data, data_size); // write data in place |
||
348 | |||
349 | if (write_index + data_size > buffer->size) |
||
350 | buffer->size = write_index + data_size; // update data size only if it growed |
||
351 | else |
||
352 | { |
||
353 | // 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! |
||
354 | |||
355 | // reallocate data buffer at the right size |
||
356 | reallocated_ptr = realloc (buffer->bytes, buffer->size + sizeof (size_t)); // always null-terminate buffers, but never report the null terminator |
||
357 | if (reallocated_ptr == NULL) |
||
358 | return (0); // on failure, return an error value (errno already set to ENOMEM) |
||
359 | buffer->bytes = reallocated_ptr; // save pointer to reallocated data |
||
360 | } |
||
361 | for (padding = 0; padding < sizeof (size_t); padding++) |
||
362 | buffer->bytes[buffer->size + padding] = 0; // always null-terminate buffers, silently |
||
363 | |||
364 | return (1); // buffer written successfully, return SUCCESS |
||
365 | } |
||
366 | |||
367 | |||
368 | int Buffer_SubsetFromTo (buffer_t *buffer, const size_t from_index, const size_t to_index) |
||
369 | { |
||
370 | // this function shortens the passed buffer by transforming it into its subset ranging |
||
371 | // from from_index (0 meaning beginning of buffer) to to_index (0 *NOT* meaning end of buffer!!! FIXME), |
||
372 | // shortening the buffer as necessary. |
||
373 | // Returns 1 on success, 0 on error (in which case errno is set to E2BIG). |
||
374 | |||
375 | void *reallocated_ptr; |
||
376 | size_t padding; |
||
377 | |||
378 | if ((from_index > to_index) || (to_index > buffer->size)) |
||
379 | { |
||
380 | errno = E2BIG; // set errno to a significant value |
||
381 | return (0); // consistency check: return FAILURE if we're requesting an out of bounds subset |
||
382 | } |
||
383 | |||
384 | buffer->size = to_index - from_index; // report the new buffer size |
||
385 | |||
386 | // should we shift the data ? |
||
387 | if (from_index > 0) |
||
388 | memmove (buffer->bytes, &buffer->bytes[from_index], buffer->size); // shift everything if necessary |
||
389 | |||
390 | // 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! |
||
391 | |||
392 | // reallocate data buffer at the right size |
||
393 | reallocated_ptr = realloc (buffer->bytes, buffer->size + sizeof (size_t)); // always null-terminate buffers, but never report the null terminator |
||
394 | if (reallocated_ptr == NULL) |
||
395 | return (0); // on failure, return an error value (errno is set) |
||
396 | buffer->bytes = reallocated_ptr; // save pointer to reallocated data |
||
397 | for (padding = 0; padding < sizeof (size_t); padding++) |
||
398 | buffer->bytes[buffer->size + padding] = 0; // always null-terminate buffers, silently |
||
399 | |||
400 | return (1); // buffer subset isolated successfully, return SUCCESS |
||
401 | } |
||
402 | |||
403 | |||
404 | int Buffer_ReadFromFile (buffer_t *buffer, const char *file_pathname) |
||
405 | { |
||
406 | // this function copies the contents of file_pathname in a newly allocated data buffer |
||
407 | // and fills in the buffer size accordingly. It is up to the caller to free that buffer. |
||
408 | // 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). |
||
409 | |||
410 | void *allocated_ptr; |
||
411 | int errno_backup; |
||
412 | size_t filesize; |
||
413 | size_t padding; |
||
414 | FILE *fp; // buffered I/O with FILE * is ___FASTER___ than unbuffered I/O with file descriptors |
||
415 | |||
416 | // start by reporting a zero length |
||
417 | buffer->bytes = NULL; |
||
418 | buffer->size = 0; |
||
419 | |||
420 | // open file for binary reading (DO NOT stat() BEFORE, ELSE WE WOULD NEED TO APPLY THE SAME LONG PATHNAME WORKAROUND TWICE) |
||
421 | #if defined(_WIN32) && (!defined(MAXPATHLEN) || (MAXPATHLEN <= 260)) |
||
422 | // on Windows, work around of the MAX_PATH limitation by using the Unicode APIs and NT-style pathnames |
||
423 | // IMPLEMENTATION NOTE: ON WINDOWS, ALL PATHS ARE CONVERTED TO UTF-16 IN MSVCRT.DLL BEFORE THE ACTUAL SYSTEM CALL, |
||
424 | // SO IT *IS* FASTER TO SYSTEMATICALLY CONVERT ALL PATHS TO NT PATHS, LIKE THEY DO IN LLVM/CLANG (see Path.inc) |
||
425 | errno = EACCES; // set errno to something significant |
||
426 | fp = NULL; // assume failure until told otherwise |
||
427 | 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 |
||
428 | 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 |
||
429 | 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 |
||
430 | if (nt_filehandle != NULL) |
||
431 | { |
||
432 | intermediary_fd = _open_osfhandle ((intptr_t) nt_filehandle, O_RDONLY); // and convert this handle back into a POSIX file descriptor |
||
433 | if (intermediary_fd != -1) |
||
434 | { |
||
435 | fp = _fdopen (intermediary_fd, "rb"); // now convert this handle to a buffered libc file descriptor |
||
436 | if (fp == NULL) |
||
437 | _close (intermediary_fd); // if we couldn't get a libc file descriptor out of that POSIX file descriptor, close it anyway |
||
438 | } |
||
439 | else |
||
440 | CloseHandle (nt_filehandle); // if we couldn't get a POSIX file descriptor out of that handle, close it anyway |
||
441 | } |
||
442 | if (fp != NULL) |
||
443 | errno = 0; // if file could be opened, clear errno |
||
444 | #else // !defined(_WIN32) || (defined(MAXPATHLEN) && (MAXPATHLEN > 260)) |
||
445 | fp = fopen (file_pathname, "rb"); // POSIX-compliant system. Good boy. |
||
446 | #endif // defined(_WIN32) && (!defined(MAXPATHLEN) || (MAXPATHLEN <= 260)) |
||
447 | if (fp == NULL) |
||
448 | return (0); // on fopen() failure, return an error value (and errno is hopefully set to something meaningful) |
||
449 | |||
450 | // measure file size |
||
451 | fseek (fp, 0, SEEK_END); |
||
452 | filesize = ftell (fp); |
||
453 | fseek (fp, 0, SEEK_SET); |
||
454 | |||
455 | // allocate enough space for it |
||
456 | allocated_ptr = malloc (filesize + sizeof (size_t)); // always null-terminate buffers, but never report the null terminator |
||
457 | if (allocated_ptr == NULL) |
||
458 | { |
||
459 | errno_backup = errno; // preserve errno on errors |
||
460 | fclose (fp); // on malloc() error, close file |
||
461 | errno = errno_backup; |
||
462 | return (0); // and return an error value (errno already set to ENOMEM) |
||
463 | } |
||
464 | |||
465 | buffer->bytes = allocated_ptr; // save pointer to newly allocated data |
||
466 | |||
467 | // and read it at once |
||
468 | if (fread (buffer->bytes, 1, filesize, fp) != filesize) |
||
469 | { |
||
470 | errno_backup = (errno ? errno : EIO); // preserve errno on errors; if it isn't set, set it to EIO |
||
471 | fclose (fp); // fread() failed, close file |
||
472 | free (buffer->bytes); |
||
473 | errno = errno_backup; |
||
474 | return (0); // on failure, return an error value (errno already set to EIO - or something else set by the underlying buffered stream API) |
||
475 | } |
||
476 | |||
477 | fclose (fp); // finished, close file |
||
478 | buffer->size = filesize; // report its size |
||
479 | for (padding = 0; padding < sizeof (size_t); padding++) |
||
480 | buffer->bytes[buffer->size + padding] = 0; // always null-terminate buffers, silently |
||
481 | |||
482 | return (1); // and return SUCCESS |
||
483 | } |
||
484 | |||
485 | |||
486 | int Buffer_WriteToFile (buffer_t *buffer, const char *file_pathname) |
||
487 | { |
||
488 | // this function copies the contents of buffer's data into the file pointed to by |
||
489 | // 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). |
||
490 | // NOTE: "<stdout>" and "<stderr>" pathnames can be used to write to stdout and stderr, |
||
491 | // else all directories up to file_pathname are recursively created if necessary. |
||
492 | |||
493 | size_t string_index; |
||
494 | size_t length; |
||
495 | int errno_backup; |
||
496 | char *temp_pathname; |
||
497 | char *separator; |
||
498 | char *context; |
||
499 | FILE *fp; // buffered I/O with FILE * is ___FASTER___ than unbuffered I/O with file descriptors |
||
500 | |||
501 | // do we want to write to the standard output or the standard error? |
||
502 | if (strcmp (file_pathname, "<stdout>") == 0) |
||
503 | fp = stdout; |
||
504 | else if (strcmp (file_pathname, "<stderr>") == 0) |
||
505 | fp = stderr; |
||
506 | else |
||
507 | { |
||
508 | // ensure all the path up to file exists, create directories on the fly if needed |
||
509 | // FIXME: this won't work on Windows with symlinks pointing to nonexistent directories... |
||
510 | length = strlen (file_pathname); |
||
511 | if (length == 0) |
||
512 | { |
||
513 | errno = EINVAL; // if the passed pathname is empty, report invalid parameter |
||
514 | return (0); // and return an error value |
||
515 | } |
||
516 | temp_pathname = strdup (file_pathname); // have a working copy of file_pathname |
||
517 | if (temp_pathname == NULL) |
||
518 | return (0); // on strdup() failure, return an error value (errno already set to ENOMEM) |
||
519 | for (string_index = length - 1; string_index != SIZE_MAX; string_index--) // i.e. loop until it overflows |
||
520 | if ((temp_pathname[string_index] == '/') || (temp_pathname[string_index] == '\\')) |
||
521 | break; // look for the last directory separator and stop as soon as we find it |
||
522 | if (string_index != SIZE_MAX) |
||
523 | { |
||
524 | for (; string_index < length; string_index++) |
||
525 | 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 |
||
526 | context = NULL; |
||
527 | separator = strtok_r (&temp_pathname[1], "/\\", &context); // for each separator in the remaining string past the first one... |
||
528 | while (separator != NULL) |
||
529 | { |
||
530 | (void) mkdir (temp_pathname, 0755); // create directories recursively (FIXME: THIS IS NOT COMPATIBLE WITH LONG FILENAMES) |
||
531 | temp_pathname[strlen (temp_pathname)] = '/'; // put back the separator |
||
532 | separator = strtok_r (NULL, "/\\", &context); // and look for the next one |
||
533 | } |
||
534 | } |
||
535 | free (temp_pathname); // release our working copy of file_pathname |
||
536 | |||
537 | // open file for binary writing |
||
538 | #if defined(_WIN32) && (!defined(MAXPATHLEN) || (MAXPATHLEN <= 260)) |
||
539 | // on Windows, work around of the MAX_PATH limitation by using the Unicode APIs and NT-style pathnames |
||
540 | // IMPLEMENTATION NOTE: ON WINDOWS, ALL PATHS ARE CONVERTED TO UTF-16 IN MSVCRT.DLL BEFORE THE ACTUAL SYSTEM CALL, |
||
541 | // SO IT *IS* FASTER TO SYSTEMATICALLY CONVERT ALL PATHS TO NT PATHS, LIKE THEY DO IN LLVM/CLANG (see Path.inc) |
||
542 | errno = EACCES; // set errno to something significant |
||
543 | fp = NULL; // assume failure until told otherwise |
||
544 | 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 |
||
545 | 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 |
||
546 | 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 |
||
547 | if (nt_filehandle != NULL) |
||
548 | { |
||
549 | intermediary_fd = _open_osfhandle ((intptr_t) nt_filehandle, O_WRONLY); // and convert this handle back into a POSIX file descriptor |
||
550 | if (intermediary_fd != -1) |
||
551 | { |
||
552 | fp = _fdopen (intermediary_fd, "wb"); // now convert this handle to a buffered libc file descriptor |
||
553 | if (fp == NULL) |
||
554 | _close (intermediary_fd); // if we couldn't get a libc file descriptor out of that POSIX file descriptor, close it anyway |
||
555 | } |
||
556 | else |
||
557 | CloseHandle (nt_filehandle); // if we couldn't get a POSIX file descriptor out of that handle, close it anyway |
||
558 | } |
||
559 | if (fp != NULL) |
||
560 | errno = 0; // if file could be opened, clear errno |
||
561 | #else // !defined(_WIN32) || (defined(MAXPATHLEN) && (MAXPATHLEN > 260)) |
||
562 | fp = fopen (file_pathname, "wb"); // POSIX-compliant system. Good boy. |
||
563 | #endif // defined(_WIN32) && (!defined(MAXPATHLEN) || (MAXPATHLEN <= 260)) |
||
564 | if (fp == NULL) |
||
565 | return (0); // on fopen() failure, return an error value (and errno is hopefully set to something meaningful) |
||
566 | } |
||
567 | |||
568 | // and write it at once |
||
569 | if (fwrite (buffer->bytes, 1, buffer->size, fp) != buffer->size) |
||
570 | { |
||
571 | errno_backup = (errno ? errno : EIO); // preserve errno on errors; if it isn't set, set it to EIO |
||
572 | if ((fp != stdout) && (fp != stderr)) |
||
573 | fclose (fp); // either case, close the file |
||
574 | errno = errno_backup; |
||
575 | return (0); // on failure, return an error value (errno already set to EIO - or something else set by the underlying buffered stream API) |
||
576 | } |
||
577 | |||
578 | if ((fp != stdout) && (fp != stderr)) |
||
579 | fclose (fp); // finished, close file (only if it was not one of the standard streams) |
||
580 | |||
581 | errno = 0; // no error |
||
582 | return (1); // and return SUCCESS |
||
583 | } |
||
584 | |||
585 | |||
586 | uint8_t *Buffer_FindFirst (const buffer_t *buffer, const void *needle, const size_t needle_length) |
||
587 | { |
||
588 | // returns a pointer to the first occurence of needle in buffer haystack, or NULL if not found |
||
589 | |||
590 | size_t byte_index; |
||
591 | |||
592 | if (buffer->size < needle_length) |
||
593 | return (NULL); // consistency check: if buffer is too small for needle, return NULL |
||
594 | else if ((buffer->size == needle_length) && (memcmp (buffer->bytes, needle, buffer->size) == 0)) |
||
595 | return (buffer->bytes); // special case where needle is exactly as long as buffer |
||
596 | |||
597 | // parse buffer from start to end |
||
598 | for (byte_index = 0; byte_index <= buffer->size - needle_length; byte_index++) // inferior OR EQUAL, meaning last position INCLUDED! |
||
599 | if (memcmp (&buffer->bytes[byte_index], needle, needle_length) == 0) |
||
600 | return (&buffer->bytes[byte_index]); // return the first match we find |
||
601 | |||
602 | return (NULL); // definitely no needle in buffer |
||
603 | } |
||
604 | |||
605 | |||
606 | uint8_t *Buffer_FindFirstCI (const buffer_t *buffer, const char *needle) |
||
607 | { |
||
608 | // returns a pointer to the first occurence of needle in buffer haystack (CASE INSENSITIVELY), or NULL if not found |
||
609 | |||
610 | size_t needle_length; |
||
611 | size_t byte_index; |
||
612 | |||
613 | needle_length = strlen (needle); // measure needle length |
||
614 | |||
615 | if (buffer->size < needle_length) |
||
616 | return (NULL); // consistency check: if buffer is too small for needle, return NULL |
||
617 | else if ((buffer->size == needle_length) && (memcmp (buffer->bytes, needle, buffer->size) == 0)) |
||
618 | return (buffer->bytes); // special case where needle is exactly as long as buffer |
||
619 | |||
620 | // parse buffer from start to end |
||
621 | for (byte_index = 0; byte_index <= buffer->size - needle_length; byte_index++) // inferior OR EQUAL, meaning last position INCLUDED! |
||
622 | if (strncasecmp ((const char *) &buffer->bytes[byte_index], needle, needle_length) == 0) |
||
623 | return (&buffer->bytes[byte_index]); // return the first match we find |
||
624 | |||
625 | return (NULL); // definitely no needle in buffer |
||
626 | } |
||
627 | |||
628 | |||
629 | uint8_t *Buffer_FindLast (const buffer_t *buffer, const void *needle, const size_t needle_length) |
||
630 | { |
||
631 | // returns a pointer to the last occurence of needle in buffer haystack, or NULL if not found |
||
632 | |||
633 | size_t byte_index; |
||
634 | |||
635 | if (buffer->size < needle_length) |
||
636 | return (NULL); // consistency check: if buffer is too small for needle, return NULL |
||
637 | else if ((buffer->size == needle_length) && (memcmp (buffer->bytes, needle, buffer->size) == 0)) |
||
638 | return (buffer->bytes); // special case where needle is exactly as long as buffer |
||
639 | |||
640 | // parse buffer from end to start |
||
641 | for (byte_index = buffer->size - needle_length; byte_index != SIZE_MAX; byte_index--) |
||
642 | if (memcmp (&buffer->bytes[byte_index], needle, needle_length) == 0) |
||
643 | return (&buffer->bytes[byte_index]); // return the last match we find |
||
644 | |||
645 | return (NULL); // definitely no needle in buffer |
||
646 | } |
||
647 | |||
648 | |||
649 | uint8_t *Buffer_FindLastCI (const buffer_t *buffer, const char *needle) |
||
650 | { |
||
651 | // returns a pointer to the last occurence of needle in buffer haystack (CASE INSENSITIVELY), or NULL if not found |
||
652 | |||
653 | size_t needle_length; |
||
654 | size_t byte_index; |
||
655 | |||
656 | needle_length = strlen (needle); // measure needle length |
||
657 | |||
658 | if (buffer->size < needle_length) |
||
659 | return (NULL); // consistency check: if buffer is too small for needle, return NULL |
||
660 | else if ((buffer->size == needle_length) && (memcmp (buffer->bytes, needle, buffer->size) == 0)) |
||
661 | return (buffer->bytes); // special case where needle is exactly as long as buffer |
||
662 | |||
663 | // parse buffer from end to start |
||
664 | for (byte_index = buffer->size - needle_length; byte_index != SIZE_MAX; byte_index--) |
||
665 | if (strncasecmp ((const char *) &buffer->bytes[byte_index], needle, needle_length) == 0) |
||
666 | return (&buffer->bytes[byte_index]); // return the last match we find |
||
667 | |||
668 | return (NULL); // definitely no needle in buffer |
||
669 | } |
||
670 | |||
671 | |||
672 | 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) |
||
673 | { |
||
674 | // replaces as many occurences of needle with replacement in buffer haystack, reallocating as needed. |
||
675 | // If howmany is strictly positive, replacements stop after howmany replacements are made in the forward direction. |
||
676 | // If howmany is strictly negative, replacements stop after -howmany replacements are made in the REVERSE direction. |
||
677 | // 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). |
||
678 | |||
679 | void *reallocated_ptr; |
||
680 | void *safe_needle; |
||
681 | size_t replacements_made; |
||
682 | size_t replacements_max; |
||
683 | size_t byte_index; |
||
684 | size_t padding; |
||
685 | int realloc_done; |
||
686 | |||
687 | if (needle_length > buffer->size) |
||
688 | { |
||
689 | errno = E2BIG; // consistency check: if buffer is too small for needle, set errno to E2BIG |
||
690 | return (0); // and return an error value |
||
691 | } |
||
692 | |||
693 | if (needle_length == 0) |
||
694 | return (0); // consistency check: if needle is an empty string, return 0 (no replacement made) |
||
695 | |||
696 | // 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) ? |
||
697 | if ((abs (howmany_with_zero_for_all_and_minus_for_backwards) != 1) && ((uint8_t *) needle > buffer->bytes) && ((uint8_t *) needle < buffer->bytes + buffer->size)) |
||
698 | { |
||
699 | safe_needle = (uint8_t *) malloc (needle_length); // allocate space for a copy of the needle |
||
700 | if (safe_needle == NULL) |
||
701 | return (0); // on allocation error, return 0 (errno already set to ENOMEM) |
||
702 | memcpy (safe_needle, needle, needle_length); // and copy needle somewhere safe |
||
703 | } |
||
704 | else |
||
705 | safe_needle = (uint8_t *) needle; // else we can use needle directly and spare us an allocation |
||
706 | |||
707 | // see how much we need to replace and in what order |
||
708 | replacements_made = 0; |
||
709 | realloc_done = 0; |
||
710 | if (howmany_with_zero_for_all_and_minus_for_backwards < 0) |
||
711 | { |
||
712 | // backwards direction |
||
713 | replacements_max = -howmany_with_zero_for_all_and_minus_for_backwards; |
||
714 | |||
715 | // parse buffer from end to start |
||
716 | for (byte_index = buffer->size - needle_length; byte_index != SIZE_MAX; byte_index--) |
||
717 | { |
||
718 | if (memcmp (&buffer->bytes[byte_index], safe_needle, needle_length) != 0) |
||
719 | continue; // if there's no match at this location, skip it |
||
720 | |||
721 | // a replacement should be made here: see if we need to grow the data |
||
722 | if (replacement_length > needle_length) |
||
723 | { |
||
724 | // if so, reallocate data buffer at the right size |
||
725 | reallocated_ptr = realloc (buffer->bytes, buffer->size + replacement_length - needle_length + sizeof (size_t)); // always null-terminate buffers, but never report the null terminator |
||
726 | if (reallocated_ptr == NULL) |
||
727 | { |
||
728 | replacements_made = 0; |
||
729 | break; // on failure, return an error value (errno is set) |
||
730 | } |
||
731 | |||
732 | buffer->bytes = reallocated_ptr; // save pointer to reallocated data |
||
733 | realloc_done = 1; // and remember that a reallocation was done |
||
734 | } |
||
735 | |||
736 | // move the remaining first if necessary, then put the replacement in place |
||
737 | if (needle_length != replacement_length) |
||
738 | memmove (&buffer->bytes[byte_index + replacement_length], &buffer->bytes[byte_index + needle_length], buffer->size - (byte_index + needle_length)); |
||
739 | if (replacement_length > 0) |
||
740 | memcpy (&buffer->bytes[byte_index], replacement, replacement_length); |
||
741 | buffer->size += replacement_length - needle_length; // adjust buffer size |
||
742 | |||
743 | replacements_made++; // one more replacement has been made |
||
744 | if ((replacements_max > 0) && (replacements_made >= replacements_max)) |
||
745 | break; // if a maximum replacements count was specified, stop here |
||
746 | } |
||
747 | } |
||
748 | else |
||
749 | { |
||
750 | // forward direction |
||
751 | replacements_max = howmany_with_zero_for_all_and_minus_for_backwards; |
||
752 | |||
753 | // parse buffer from start to end |
||
754 | for (byte_index = 0; byte_index <= buffer->size - needle_length; byte_index++) // inferior OR EQUAL, meaning last position INCLUDED! |
||
755 | { |
||
756 | if (memcmp (&buffer->bytes[byte_index], safe_needle, needle_length) != 0) |
||
757 | continue; // if there's no match at this location, skip it |
||
758 | |||
759 | // a replacement should be made here: see if we need to grow the data |
||
760 | if (replacement_length > needle_length) |
||
761 | { |
||
762 | // if so, reallocate data buffer at the right size |
||
763 | reallocated_ptr = realloc (buffer->bytes, buffer->size + replacement_length - needle_length + sizeof (size_t)); // always null-terminate buffers, but never report the null terminator |
||
764 | if (reallocated_ptr == NULL) |
||
765 | { |
||
766 | replacements_made = 0; |
||
767 | break; // on failure, return an error value (errno is set) |
||
768 | } |
||
769 | |||
770 | buffer->bytes = reallocated_ptr; // save pointer to reallocated data |
||
771 | realloc_done = 1; // and remember that a reallocation was done |
||
772 | } |
||
773 | |||
774 | // move the remaining first (including the null terminator) if necessary, then put the replacement in place |
||
775 | if (needle_length != replacement_length) |
||
776 | memmove (&buffer->bytes[byte_index + replacement_length], &buffer->bytes[byte_index + needle_length], buffer->size - (byte_index + needle_length)); |
||
777 | if (replacement_length > 0) |
||
778 | memcpy (&buffer->bytes[byte_index], replacement, replacement_length); |
||
779 | buffer->size += replacement_length - needle_length; // adjust buffer size |
||
780 | |||
781 | replacements_made++; // one more replacement has been made |
||
782 | if ((replacements_max > 0) && (replacements_made >= replacements_max)) |
||
783 | break; // if a maximum replacements count was specified, stop here |
||
784 | |||
785 | byte_index += replacement_length - 1; // jump over this replacement and proceed to the remaining of the string |
||
786 | } |
||
787 | } |
||
788 | |||
789 | if (safe_needle != needle) |
||
790 | free (safe_needle); // if we had to allocate space for a safe copy of the needle, free it now |
||
791 | |||
792 | // 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! |
||
793 | if (!realloc_done) |
||
794 | { |
||
795 | // reallocate data buffer at the right size |
||
796 | reallocated_ptr = realloc (buffer->bytes, buffer->size + sizeof (size_t)); // always null-terminate buffers, but never report the null terminator |
||
797 | if (reallocated_ptr == NULL) |
||
798 | return (0); // on failure, return an error value (errno already set to ENOMEM) |
||
799 | buffer->bytes = reallocated_ptr; // save pointer to reallocated data |
||
800 | } |
||
801 | for (padding = 0; padding < sizeof (size_t); padding++) |
||
802 | buffer->bytes[buffer->size + padding] = 0; // always null-terminate buffers, silently |
||
803 | |||
804 | return (replacements_made); // finished, return the number of replacements made |
||
805 | } |
||
806 | |||
807 | |||
808 | int Buffer_Compare (buffer_t *buffer, const void *data, const size_t data_size) |
||
809 | { |
||
810 | // compares a buffer with some data of data_size length, and return 0 if they match, non-zero if they differ. |
||
811 | // If their size differ, a non-zero value (equivalent to the string "DIFF") is returned. |
||
812 | // If their content differ, the result of memcmp() is returned. |
||
813 | |||
814 | if ((data == buffer->bytes) && (data_size == buffer->size)) |
||
815 | return (0); // if both buffers are the same one, return 0 |
||
816 | else if (data_size != buffer->size) |
||
817 | return ((int) ((ssize_t) data_size - (ssize_t) buffer->size)); // buffers differ in size, return the shortest first |
||
818 | |||
819 | return (memcmp (buffer->bytes, data, data_size)); // do a memcmp() on both buffers |
||
820 | } |
||
821 | |||
822 | |||
823 | size_t Buffer_OffsetOf (buffer_t *buffer, const void *something) |
||
824 | { |
||
825 | // returns the absolute offset of the data pointed to by something relatively to the beginning of buffer data |
||
826 | // if something points outside of buffer, returns SIZE_MAX (in which case errno is set to EINVAL) |
||
827 | |||
828 | if (((uint8_t *) something < buffer->bytes) || ((uint8_t *) something > buffer->bytes + buffer->size)) |
||
829 | { |
||
830 | errno = EINVAL; // consistency check: if the queried pointer falls outside of the buffer, set errno to EINVAL |
||
831 | return (SIZE_MAX); // and return an error code |
||
832 | } |
||
833 | |||
834 | 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 |
||
835 | } |
||
836 | |||
837 | |||
838 | int Buffer_TrimLeftUntil (buffer_t *buffer, const uint8_t *trimmable_bytes, const size_t trimmable_count, const int howmany) |
||
839 | { |
||
840 | // removes leading characters from a buffer's data and returns 1 on success, or 0 if a memory |
||
841 | // reallocation error happens (which shouldn't ever since we're reallocating smaller), in which case errno is set to ENOMEM. |
||
842 | // Any character in the trimmable_bytes array is a candidate for elimination. |
||
843 | |||
844 | void *reallocated_ptr; |
||
845 | size_t removed_count; |
||
846 | size_t max_count; |
||
847 | size_t padding; |
||
848 | |||
849 | removed_count = 0; // parse buffer string from the end looking for any of the trimmable bytes |
||
850 | max_count = (howmany == 0 ? buffer->size : howmany); // see how many characters maximum we are allowed to trim |
||
851 | while ((removed_count < max_count) && (memchr (trimmable_bytes, buffer->bytes[removed_count], trimmable_count) != NULL)) |
||
852 | removed_count++; // count the number of bytes to remove |
||
853 | |||
854 | // did we find something to trim ? |
||
855 | if (removed_count > 0) |
||
856 | { |
||
857 | // if so, shift the buffer data start by the number of bytes to remove |
||
858 | memmove (buffer->bytes, &buffer->bytes[removed_count], buffer->size - removed_count); |
||
859 | buffer->size -= removed_count; // adjust the new buffer size |
||
860 | |||
861 | // reallocate data buffer at the right size |
||
862 | reallocated_ptr = realloc (buffer->bytes, buffer->size + sizeof (size_t)); // always null-terminate buffers, but never report the null terminator |
||
863 | if (reallocated_ptr == NULL) |
||
864 | return (0); // on failure, return an error value (errno already set to ENOMEM) |
||
865 | |||
866 | buffer->bytes = reallocated_ptr; // save pointer to reallocated data |
||
867 | for (padding = 0; padding < sizeof (size_t); padding++) |
||
868 | buffer->bytes[buffer->size + padding] = 0; // always null-terminate buffers, silently |
||
869 | } |
||
870 | |||
871 | return (1); // and return SUCCESS |
||
872 | } |
||
873 | |||
874 | |||
875 | int Buffer_TrimRightUntil (buffer_t *buffer, const uint8_t *trimmable_bytes, const size_t trimmable_count, const int howmany) |
||
876 | { |
||
877 | // removes trailing characters from a buffer's data and returns 1 on success, or 0 if a memory |
||
878 | // reallocation error happens (which shouldn't ever since we're reallocating smaller), in which case errno is set to ENOMEM. |
||
879 | // Any character in the trimmable_bytes array is a candidate for elimination. |
||
880 | |||
881 | void *reallocated_ptr; |
||
882 | size_t removed_count; |
||
883 | size_t max_count; |
||
884 | size_t padding; |
||
885 | |||
886 | removed_count = 0; // parse buffer string from the end looking for any of the trimmable bytes |
||
887 | max_count = (howmany == 0 ? buffer->size : howmany); // see how many characters maximum we are allowed to trim |
||
888 | while ((buffer->size > 0) && (removed_count < max_count) && (memchr (trimmable_bytes, buffer->bytes[buffer->size - 1], trimmable_count) != NULL)) |
||
889 | { |
||
890 | removed_count++; // count the number of bytes to remove |
||
891 | buffer->size--; // and decrease buffer size appropriately |
||
892 | } |
||
893 | |||
894 | // did we find something to trim ? |
||
895 | if (removed_count > 0) |
||
896 | { |
||
897 | // if so, reallocate data buffer at the right size |
||
898 | reallocated_ptr = realloc (buffer->bytes, buffer->size + sizeof (size_t)); // always null-terminate buffers, but never report the null terminator |
||
899 | if (reallocated_ptr == NULL) |
||
900 | return (0); // on failure, return an error value (errno already set to ENOMEM) |
||
901 | |||
902 | buffer->bytes = reallocated_ptr; // save pointer to reallocated data |
||
903 | for (padding = 0; padding < sizeof (size_t); padding++) |
||
904 | buffer->bytes[buffer->size + padding] = 0; // always null-terminate buffers, silently |
||
905 | } |
||
906 | |||
907 | return (1); // and return SUCCESS |
||
908 | } |
||
909 | |||
910 | |||
911 | 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) |
||
912 | { |
||
913 | // extract and returns the Nth bit of buffer when split according to separator and return whether |
||
914 | // it succeeded or not (in which case errno is set to EINVAL). |
||
915 | // IMPORTANT: preinitialized_outbuf MUST have been properly initialized before! |
||
916 | |||
917 | ptrdiff_t extractstart_index; |
||
918 | size_t fieldstart_index; |
||
919 | size_t char_index; |
||
920 | size_t part_index; |
||
921 | |||
922 | // handle special case first |
||
923 | if (howmany == 0) |
||
924 | { |
||
925 | if (preinitialized_outbuf->bytes != NULL) |
||
926 | Buffer_Forget (preinitialized_outbuf); // release output buffer if it was previously allocated |
||
927 | Buffer_InitWithByteArray (preinitialized_outbuf, ""); // if we want a zero-length slice, prepare an empty buffer |
||
928 | return (1); // and return SUCCESS |
||
929 | } |
||
930 | |||
931 | // read buffer characters and identify parts on the fly |
||
932 | extractstart_index = -1; |
||
933 | fieldstart_index = 0; |
||
934 | part_index = 0; |
||
935 | for (char_index = 0; char_index < buffer->size; char_index++) |
||
936 | { |
||
937 | // have we NOT found yet the start field and is this it ? |
||
938 | if ((extractstart_index == -1) && (part_index == wanted_part_index)) |
||
939 | extractstart_index = fieldstart_index; // if so, remember where it starts |
||
940 | |||
941 | if (should_parse_as_text && (buffer->bytes[char_index] == 0)) |
||
942 | break; // when end of text reached, stop searching |
||
943 | else if ((char_index + separator_len <= buffer->size) && (memcmp (&buffer->bytes[char_index], separator, separator_len) == 0)) |
||
944 | { |
||
945 | part_index++; // a separator is here, meaning that one part more was read |
||
946 | |||
947 | if ((howmany > 0) && (part_index == wanted_part_index + howmany)) |
||
948 | break; // was it ALL that we were looking for ? if so, stop searching |
||
949 | |||
950 | char_index += separator_len; // skip the separator |
||
951 | fieldstart_index = char_index; // and remember a new part starts here |
||
952 | } |
||
953 | } |
||
954 | |||
955 | // have we found the part we were looking for ? |
||
956 | if (extractstart_index != -1) |
||
957 | { |
||
958 | if (preinitialized_outbuf->bytes != NULL) |
||
959 | Buffer_Forget (preinitialized_outbuf); // release output buffer if it was previously allocated |
||
960 | Buffer_InitWithData (preinitialized_outbuf, &buffer->bytes[extractstart_index], char_index - extractstart_index); // if so, so copy it |
||
961 | return (1); // and return SUCCESS |
||
962 | } |
||
963 | |||
964 | // if we reach here, it means we haven't identified the part we were looking for |
||
965 | errno = EINVAL; // so tell the user the wanted_part_index parameter was invalid |
||
966 | return (0); // and return FAILURE |
||
967 | } |