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