Subversion Repositories QNX 8.QNX8 IFS tool

Rev

Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

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