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 | } |