Subversion Repositories QNX 8.QNX8 IFS tool

Rev

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