Subversion Repositories QNX 8.QNX8 IFS tool

Rev

Rev 16 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
16 pmbaty 1
// buffer.c
2
 
3
// standard C includes
4
#include <stdio.h>
5
#include <stddef.h>
6
#include <stdlib.h>
7
#include <stdarg.h>
8
#include <stdint.h> // for SIZE_MAX
9
#include <string.h>
10
#include <fcntl.h>
11
#include <sys/stat.h>
12
#include <locale.h>
13
#include <errno.h>
14
 
15
 
16
// our own includes
17
#include "buffer.h"
18
 
19
 
20
// compiler-specific glue
21
#ifdef _MSC_VER
22
#include <io.h>
23
#include <direct.h>
24
#define strdup(s) _strdup((s))
25
#define mkdir(p,m) _mkdir ((p))
26
#define strncasecmp(s1,s2,l) _strnicmp ((s1), (s2), (l))
27
#define strtok_r(s,delim,ctx) strtok_s ((s), (delim), (ctx))
28
#define fseek(fp,off,m) _fseeki64 ((fp), (off), (m))
29
#define ftell(fp) _ftelli64 ((fp))
30
#if (SIZE_MAX == UINT64_MAX)
31
#define ssize_t signed long long
32
#else // SIZE_MAX != UINT64_MAX
33
#define ssize_t signed long
34
#endif // SIZE_MAX == UINT64_MAX
35
#define thread_local __declspec(thread)
36
#else // !_MSC_VER
37
#define thread_local __thread
38
#endif // _MSC_VER
39
 
40
 
41
// Windows-specific long path handling facilities
42
#if defined(_WIN32) && (!defined(MAXPATHLEN) || (MAXPATHLEN <= 260))
43
extern void *__stdcall CreateFileW (const wchar_t *lpFileName, unsigned long dwDesiredAccess, unsigned long dwShareMode, void *lpSecurityAttributes, unsigned long dwCreationDisposition, unsigned long dwFlagsAndAttributes, void *hTemplateFile);
44
extern int __stdcall MultiByteToWideChar (unsigned int CodePage, unsigned long dwFlags, const char *lpMultiByteStr, int cbMultiByte, wchar_t *lpWideCharStr, int cchWideChar);
45
extern unsigned long __stdcall GetFullPathNameW (const wchar_t *lpFileName, unsigned long nBufferLength, wchar_t *lpBuffer, wchar_t **lpFilePart);
46
extern int __stdcall CloseHandle (void *hObject);
47
extern int __stdcall AreFileApisANSI (void);
48
thread_local static wchar_t wide_pathname[32768] = L"";
49
thread_local static wchar_t nt_pathname[32768] = L"\\\\?\\"; // initialized to NT prefix once and for all
50
thread_local static void *nt_filehandle = NULL;
51
thread_local static int intermediary_fd = -1;
52
#endif // defined(_WIN32) && (!defined(MAXPATHLEN) || (MAXPATHLEN <= 260))
53
 
54
 
55
void Buffer_Forget (buffer_t *buffer)
56
{
57
   // this function forgets a buffer's contents, freeing its associated memory buffer,
58
   // and resets the buffer_t structure.
59
 
60
   if (buffer->bytes != NULL)
61
      free (buffer->bytes); // free buffer data
62
   buffer->bytes = NULL;
63
   buffer->size = 0; // reset data size
64
 
65
   return; // finished, buffer structure is virgin again
66
}
67
 
68
 
69
int Buffer_Reset (buffer_t *buffer)
70
{
71
   // this function makes a buffer contain only an empty string (with a null terminator), of size 0.
72
   // Returns 1 on success, 0 on error (in which case errno is set to ENOMEM).
73
 
74
   void *reallocated_ptr;
75
   size_t padding;
76
 
77
   // first, reallocate space for an empty string
78
   reallocated_ptr = realloc (buffer->bytes, sizeof (size_t)); // always null-terminate buffers, but never report the null terminator
79
   if (reallocated_ptr == NULL)
80
      return (0); // on failure, return an error value (errno already set to ENOMEM)
81
 
82
   buffer->bytes = reallocated_ptr; // save pointer to reallocated data
83
   buffer->size = 0; // and reset buffer size
84
 
85
   for (padding = 0; padding < sizeof (size_t); padding++)
86
      buffer->bytes[padding] = 0; // always null-terminate buffers, silently
87
 
88
   return (1); // buffer emptied successfully, return SUCCESS
89
}
90
 
91
 
92
int Buffer_Append (buffer_t *buffer, const void *data, const size_t data_size)
93
{
94
   // this function appends the contents of data to a buffer's data.
95
   // Buffer is always null-terminated, but the terminator is NOT counted in the buffer's length.
96
   // Returns 1 on success, 0 on error (in which case errno is set to ENOMEM).
97
 
98
   void *reallocated_ptr;
99
   size_t padding;
100
 
101
   // first, reallocate space to hold data_size bytes more
102
   reallocated_ptr = realloc (buffer->bytes, buffer->size + data_size + sizeof (size_t)); // always null-terminate buffers, but never report the null terminator
103
   if (reallocated_ptr == NULL)
104
      return (0); // on failure, return an error value (errno already set to ENOMEM)
105
 
106
   buffer->bytes = reallocated_ptr; // save pointer to reallocated data
107
   if (data_size > 0)
108
   {
109
      if (data != NULL)
110
         memcpy (&buffer->bytes[buffer->size], data, data_size); // if data points to something, write new data at the end
111
      else
112
         memset (&buffer->bytes[buffer->size], 0, data_size); // or, if data is NULL, set the extra space to zero
113
      buffer->size += data_size; // and increment buffer size
114
   }
115
   for (padding = 0; padding < sizeof (size_t); padding++)
116
      buffer->bytes[buffer->size + padding] = 0; // always null-terminate buffers, silently
117
 
118
   return (1); // buffer appended successfully, return SUCCESS
119
}
120
 
121
 
122
int Buffer_AppendFormattedCString (buffer_t *buffer, const char *fmt_string, ...)
123
{
124
   // this function appends some printf-style formatted data to a buffer's data.
125
   // Buffer is always null-terminated, but the terminator is NOT counted in the buffer's length.
126
   // Returns 1 on success, 0 on error (in which case errno is set to ENOMEM).
127
 
128
   void *reallocated_ptr;
129
   va_list varargs_list;
130
   va_list varargs_copy;
131
   char *saved_locale;
132
   size_t extra_size;
133
   size_t padding;
134
 
135
   saved_locale = setlocale (LC_ALL, NULL); // get the current locale
136
   if (saved_locale != NULL)
137
      saved_locale = strdup (saved_locale); // preserve it
138
   setlocale (LC_ALL, "C"); // format everything in the POSIX (international) locale
139
 
140
   // first, concatenate varargs and see how much extra space we need
141
   va_start (varargs_list, fmt_string);
142
   va_copy (varargs_copy, varargs_list);
143
   extra_size = vsnprintf (NULL, 0, fmt_string, varargs_list);
144
   va_end (varargs_list); // release the variable arguments list
145
 
146
   // now, reallocate space to hold extra_size bytes more
147
   reallocated_ptr = realloc (buffer->bytes, buffer->size + extra_size + sizeof (size_t)); // always null-terminate buffers, but never report the null terminator
148
   if (reallocated_ptr == NULL)
149
   {
150
      if (saved_locale != NULL)
151
      {
152
         setlocale (LC_ALL, saved_locale); // restore user locale
153
         free (saved_locale); // and free the memory we used to back it up
154
      }
155
      return (0); // on failure, return an error value (errno already set to ENOMEM)
156
   }
157
 
158
   buffer->bytes = reallocated_ptr; // save pointer to reallocated data
159
   if (extra_size > 0)
160
   {
161
      vsnprintf ((char *) &buffer->bytes[buffer->size], extra_size + 1, fmt_string, varargs_copy); // write new data at the end
162
      buffer->size += extra_size; // and increment buffer size
163
   }
164
   va_end (varargs_copy); // release the variable arguments list
165
   for (padding = 0; padding < sizeof (size_t); padding++)
166
      buffer->bytes[buffer->size + padding] = 0; // always null-terminate buffers, silently
167
 
168
   if (saved_locale != NULL)
169
   {
170
      setlocale (LC_ALL, saved_locale); // restore user locale
171
      free (saved_locale); // and free the memory we used to back it up
172
   }
173
   return (1); // buffer appended successfully, return SUCCESS
174
}
175
 
176
 
177
int Buffer_Prepend (buffer_t *buffer, const void *data, const size_t data_size)
178
{
179
   // this function prepends the contents of data to a buffer's data.
180
   // Returns 1 on success, 0 on error (in which case errno is set to ENOMEM).
181
 
182
   void *reallocated_ptr;
183
   size_t padding;
184
 
185
   // first, reallocate space to hold data_size bytes more
186
   reallocated_ptr = realloc (buffer->bytes, data_size + buffer->size + sizeof (size_t)); // always null-terminate buffers, but never report the null terminator
187
   if (reallocated_ptr == NULL)
188
      return (0); // on failure, return an error value (errno already set to ENOMEM)
189
 
190
   buffer->bytes = reallocated_ptr; // save pointer to reallocated data
191
   if (data_size > 0)
192
   {
193
#ifdef _MSC_VER
194
#pragma warning(push)
195
#pragma warning(disable:6385) // the Microsoft linter is wrong here.
196
#pragma warning(disable:6386)
197
#endif // _MSC_VER
198
      if (buffer->size > 0)
199
         memmove (&buffer->bytes[data_size], buffer->bytes, buffer->size); // move existing data to the end
200
      memcpy (buffer->bytes, data, data_size); // write new data at the beginning
201
#ifdef _MSC_VER
202
#pragma warning(pop)
203
#endif // _MSC_VER
204
      buffer->size += data_size; // and increment buffer size
205
   }
206
   for (padding = 0; padding < sizeof (size_t); padding++)
207
      buffer->bytes[buffer->size + padding] = 0; // always null-terminate buffers, silently
208
 
209
   return (1); // buffer appended successfully, return SUCCESS
210
}
211
 
212
 
213
int Buffer_PrependFormattedCString (buffer_t *buffer, const char *fmt_string, ...)
214
{
215
   // this function prepends some printf-style formatted data to a buffer's data.
216
   // Buffer is always null-terminated, but the terminator is NOT counted in the buffer's length.
217
   // Returns 1 on success, 0 on error (in which case errno is set to either ENOMEM, EILSEQ or EINVAL).
218
 
219
   void *reallocated_ptr;
220
   va_list varargs_list;
221
   va_list varargs_copy;
222
   uint8_t first_byte;
223
   char *saved_locale;
224
   int extra_size;
225
   size_t padding;
226
 
227
   saved_locale = setlocale (LC_ALL, NULL); // get the current locale
228
   if (saved_locale != NULL)
229
      saved_locale = strdup (saved_locale); // preserve it
230
   setlocale (LC_ALL, "C"); // format everything in the POSIX (international) locale
231
 
232
   // first, concatenate varargs and see how much extra space we need
233
   va_start (varargs_list, fmt_string);
234
   va_copy (varargs_copy, varargs_list);
235
   extra_size = vsnprintf (NULL, 0, fmt_string, varargs_list);
236
   va_end (varargs_list); // release the variable arguments list
237
 
238
   // was there an encoding error ?
239
   if (extra_size < 0)
240
   {
241
      if (saved_locale != NULL)
242
      {
243
         setlocale (LC_ALL, saved_locale); // restore user locale
244
         free (saved_locale); // and free the memory we used to back it up
245
      }
246
      return (0); // on failure, return an error value (errno already set to either EILSEQ or EINVAL)
247
   }
248
 
249
   // now, reallocate space to hold extra_size bytes more
250
   reallocated_ptr = realloc (buffer->bytes, buffer->size + extra_size + sizeof (size_t)); // always null-terminate buffers, but never report the null terminator
251
   if (reallocated_ptr == NULL)
252
   {
253
      if (saved_locale != NULL)
254
      {
255
         setlocale (LC_ALL, saved_locale); // restore user locale
256
         free (saved_locale); // and free the memory we used to back it up
257
      }
258
      return (0); // on failure, return an error value (errno already set to ENOMEM)
259
   }
260
 
261
   buffer->bytes = reallocated_ptr; // save pointer to reallocated data
262
   if (extra_size > 0)
263
   {
264
#ifdef _MSC_VER
265
#pragma warning(push)
266
#pragma warning(disable:6001) // the Microsoft linter is wrong here.
267
#pragma warning(disable:6385)
268
#pragma warning(disable:6386)
269
#endif // _MSC_VER
270
      if (buffer->size > 0)
271
      {
272
         first_byte = buffer->bytes[0]; // remember what the first byte is, since vsnprintf() will overwrite it with a null terminator
273
         memmove (&buffer->bytes[extra_size], buffer->bytes, buffer->size); // move existing data to the end
274
      }
275
      vsnprintf ((char *) buffer->bytes, (size_t) extra_size + 1, fmt_string, varargs_copy); // write new data at the start (overwriting the first character of the old string with the null terminator)
276
#ifdef _MSC_VER
277
#pragma warning(pop)
278
#endif // _MSC_VER
279
      if (buffer->size > 0)
280
         buffer->bytes[extra_size] = first_byte; // put back the old first char, which was overwriten by vsnprintf()
281
      buffer->size += extra_size; // and increment buffer size
282
   }
283
   va_end (varargs_copy); // release the variable arguments list
284
   for (padding = 0; padding < sizeof (size_t); padding++)
285
      buffer->bytes[buffer->size + padding] = 0; // always null-terminate buffers, silently
286
 
287
   if (saved_locale != NULL)
288
   {
289
      setlocale (LC_ALL, saved_locale); // restore user locale
290
      free (saved_locale); // and free the memory we used to back it up
291
   }
292
   return (1); // buffer appended successfully, return SUCCESS
293
}
294
 
295
 
296
int Buffer_InsertAt (buffer_t *buffer, const size_t insert_index, const void *data, const size_t data_size)
297
{
298
   // this function inserts the contents of data to a buffer's data at position insert_index,
299
   // shifting the remaining data from insert_index data_size forward.
300
   // Returns 1 on success, 0 on error (in which case errno is set to ENOMEM).
301
 
302
   void *reallocated_ptr;
303
   size_t padding;
304
 
305
   // reallocate data buffer at the right size
306
   reallocated_ptr = realloc (buffer->bytes, buffer->size + data_size + sizeof (size_t)); // always null-terminate buffers, but never report the null terminator
307
   if (reallocated_ptr == NULL)
308
      return (0); // on failure, return an error value (errno already set to ENOMEM)
309
 
310
   buffer->bytes = reallocated_ptr; // save pointer to reallocated data
311
   if (insert_index > buffer->size)
312
      memset (&buffer->bytes[buffer->size], 0, insert_index - buffer->size); // buffer was upsized: fill the upsized part with zeroes up to insert_index
313
   if (data_size > 0)
314
   {
315
      memmove (&buffer->bytes[insert_index + data_size], &buffer->bytes[insert_index], buffer->size - insert_index); // move existing data to the end
316
      memcpy (&buffer->bytes[insert_index], data, data_size); // write data in place
317
      buffer->size += data_size; // update data size
318
   }
319
   for (padding = 0; padding < sizeof (size_t); padding++)
320
      buffer->bytes[buffer->size + padding] = 0; // always null-terminate buffers, silently
321
 
322
   return (1); // data successfully inserted into buffer, return SUCCESS
323
}
324
 
325
 
326
int Buffer_WriteAt (buffer_t *buffer, const size_t write_index, const void *data, const size_t data_size)
327
{
328
   // this function writes the contents of data to a buffer's data at position write_index, expanding the buffer if necessary.
329
   // Returns 1 on success, 0 on error (in which case errno is set to ENOMEM).
330
 
331
   void *reallocated_ptr;
332
   size_t padding;
333
 
334
   // see if we need to grow the data
335
   if (write_index + data_size > buffer->size)
336
   {
337
      // if so, reallocate data buffer at the right size
338
      reallocated_ptr = realloc (buffer->bytes, write_index + data_size + sizeof (size_t)); // always null-terminate buffers, but never report the null terminator
339
      if (reallocated_ptr == NULL)
340
         return (0); // on failure, return an error value (errno already set to ENOMEM)
341
 
342
      buffer->bytes = reallocated_ptr; // save pointer to reallocated data
26 pmbaty 343
      if (write_index > buffer->size)
344
         memset (&buffer->bytes[buffer->size], 0, write_index - buffer->size); // buffer was upsized: fill the upsized part with zeroes up to write_index
16 pmbaty 345
   }
346
 
347
   if (data_size > 0)
348
      memcpy (&buffer->bytes[write_index], data, data_size); // write data in place
349
 
350
   if (write_index + data_size > buffer->size)
351
      buffer->size = write_index + data_size; // update data size only if it growed
352
   else
353
   {
354
      // IMPORTANT: data hasn't growed, BUT the passed buffer *MIGHT NOT* have the \0\0\0\0 suffix (e.g. hand-constructed), so we MUST ensure there's space in it to put it!
355
 
356
      // reallocate data buffer at the right size
357
      reallocated_ptr = realloc (buffer->bytes, buffer->size + sizeof (size_t)); // always null-terminate buffers, but never report the null terminator
358
      if (reallocated_ptr == NULL)
359
         return (0); // on failure, return an error value (errno already set to ENOMEM)
360
      buffer->bytes = reallocated_ptr; // save pointer to reallocated data
361
   }
362
   for (padding = 0; padding < sizeof (size_t); padding++)
363
      buffer->bytes[buffer->size + padding] = 0; // always null-terminate buffers, silently
364
 
365
   return (1); // buffer written successfully, return SUCCESS
366
}
367
 
368
 
369
int Buffer_SubsetFromTo (buffer_t *buffer, const size_t from_index, const size_t to_index)
370
{
371
   // this function shortens the passed buffer by transforming it into its subset ranging
372
   // from from_index (0 meaning beginning of buffer) to to_index (0 *NOT* meaning end of buffer!!! FIXME),
373
   // shortening the buffer as necessary.
374
   // Returns 1 on success, 0 on error (in which case errno is set to E2BIG).
375
 
376
   void *reallocated_ptr;
377
   size_t padding;
378
 
379
   if ((from_index > to_index) || (to_index > buffer->size))
380
   {
381
      errno = E2BIG; // set errno to a significant value
382
      return (0); // consistency check: return FAILURE if we're requesting an out of bounds subset
383
   }
384
 
385
   buffer->size = to_index - from_index; // report the new buffer size
386
 
387
   // should we shift the data ?
388
   if (from_index > 0)
389
      memmove (buffer->bytes, &buffer->bytes[from_index], buffer->size); // shift everything if necessary
390
 
391
   // IMPORTANT: the passed buffer *MIGHT NOT* have the \0\0\0\0 suffix (e.g. hand-constructed), so we MUST ensure there's space in it to put it!
392
 
393
   // reallocate data buffer at the right size
394
   reallocated_ptr = realloc (buffer->bytes, buffer->size + sizeof (size_t)); // always null-terminate buffers, but never report the null terminator
395
   if (reallocated_ptr == NULL)
396
      return (0); // on failure, return an error value (errno is set)
397
   buffer->bytes = reallocated_ptr; // save pointer to reallocated data
398
   for (padding = 0; padding < sizeof (size_t); padding++)
399
      buffer->bytes[buffer->size + padding] = 0; // always null-terminate buffers, silently
400
 
401
   return (1); // buffer subset isolated successfully, return SUCCESS
402
}
403
 
404
 
405
int Buffer_ReadFromFile (buffer_t *buffer, const char *file_pathname)
406
{
407
   // this function copies the contents of file_pathname in a newly allocated data buffer
408
   // and fills in the buffer size accordingly. It is up to the caller to free that buffer.
409
   // Returns 1 on success, 0 on error (in which case errno is set to either EACCESS, ENOMEM or EIO or anything else set by the underlying buffered stream API).
410
 
411
   void *allocated_ptr;
412
   int errno_backup;
413
   size_t filesize;
414
   size_t padding;
415
   FILE *fp; // buffered I/O with FILE * is ___FASTER___ than unbuffered I/O with file descriptors
416
 
417
   // start by reporting a zero length
418
   buffer->bytes = NULL;
419
   buffer->size = 0;
420
 
421
   // open file for binary reading (DO NOT stat() BEFORE, ELSE WE WOULD NEED TO APPLY THE SAME LONG PATHNAME WORKAROUND TWICE)
422
#if defined(_WIN32) && (!defined(MAXPATHLEN) || (MAXPATHLEN <= 260))
423
   // on Windows, work around of the MAX_PATH limitation by using the Unicode APIs and NT-style pathnames
424
   // IMPLEMENTATION NOTE: ON WINDOWS, ALL PATHS ARE CONVERTED TO UTF-16 IN MSVCRT.DLL BEFORE THE ACTUAL SYSTEM CALL,
425
   // SO IT *IS* FASTER TO SYSTEMATICALLY CONVERT ALL PATHS TO NT PATHS, LIKE THEY DO IN LLVM/CLANG (see Path.inc)
426
   errno = EACCES; // set errno to something significant
427
   fp = NULL; // assume failure until told otherwise
428
   MultiByteToWideChar (AreFileApisANSI () ? 0 /*CP_ACP*/ : 1 /*CP_OEMCP*/, 0, file_pathname, -1, wide_pathname, sizeof (wide_pathname) / sizeof (wchar_t)); // convert pathname to wide characters, including the NULL terminator
429
   GetFullPathNameW (wide_pathname, sizeof (nt_pathname) / sizeof (wchar_t) - 4, &nt_pathname[4], NULL); // canonicalize the resulting pathname and write it just after the NT path prefix
430
   nt_filehandle = CreateFileW (nt_pathname, 0x80000000 /*GENERIC_READ*/, 0x3 /*FILE_SHARE_READ|FILE_SHARE_WRITE*/, NULL, 3 /*OPEN_EXISTING*/, 0x80 /*FILE_ATTRIBUTE_NORMAL*/, NULL); // now call CreateFile and get the filesystem object handle
431
   if (nt_filehandle != NULL)
432
   {
433
      intermediary_fd = _open_osfhandle ((intptr_t) nt_filehandle, O_RDONLY); // and convert this handle back into a POSIX file descriptor
434
      if (intermediary_fd != -1)
435
      {
436
         fp = _fdopen (intermediary_fd, "rb"); // now convert this handle to a buffered libc file descriptor
437
         if (fp == NULL)
438
            _close (intermediary_fd); // if we couldn't get a libc file descriptor out of that POSIX file descriptor, close it anyway
439
      }
440
      else
441
         CloseHandle (nt_filehandle); // if we couldn't get a POSIX file descriptor out of that handle, close it anyway
442
   }
443
   if (fp != NULL)
444
      errno = 0; // if file could be opened, clear errno
445
#else // !defined(_WIN32) || (defined(MAXPATHLEN) && (MAXPATHLEN > 260))
446
   fp = fopen (file_pathname, "rb"); // POSIX-compliant system. Good boy.
447
#endif // defined(_WIN32) && (!defined(MAXPATHLEN) || (MAXPATHLEN <= 260))
448
   if (fp == NULL)
449
      return (0); // on fopen() failure, return an error value (and errno is hopefully set to something meaningful)
450
 
451
   // measure file size
452
   fseek (fp, 0, SEEK_END);
453
   filesize = ftell (fp);
454
   fseek (fp, 0, SEEK_SET);
455
 
456
   // allocate enough space for it
457
   allocated_ptr = malloc (filesize + sizeof (size_t)); // always null-terminate buffers, but never report the null terminator
458
   if (allocated_ptr == NULL)
459
   {
460
      errno_backup = errno; // preserve errno on errors
461
      fclose (fp); // on malloc() error, close file
462
      errno = errno_backup;
463
      return (0); // and return an error value (errno already set to ENOMEM)
464
   }
465
 
466
   buffer->bytes = allocated_ptr; // save pointer to newly allocated data
467
 
468
   // and read it at once
469
   if (fread (buffer->bytes, 1, filesize, fp) != filesize)
470
   {
471
      errno_backup = (errno ? errno : EIO); // preserve errno on errors; if it isn't set, set it to EIO
472
      fclose (fp); // fread() failed, close file
473
      free (buffer->bytes);
474
      errno = errno_backup;
475
      return (0); // on failure, return an error value (errno already set to EIO - or something else set by the underlying buffered stream API)
476
   }
477
 
478
   fclose (fp); // finished, close file
479
   buffer->size = filesize; // report its size
480
   for (padding = 0; padding < sizeof (size_t); padding++)
481
      buffer->bytes[buffer->size + padding] = 0; // always null-terminate buffers, silently
482
 
483
   return (1); // and return SUCCESS
484
}
485
 
486
 
487
int Buffer_WriteToFile (buffer_t *buffer, const char *file_pathname)
488
{
489
   // this function copies the contents of buffer's data into the file pointed to by
490
   // file_pathname. Returns 1 on success, 0 on error (in which case errno is set to either EINVAL, ENOMEM, EACCESS or EIO or anything else set by the underlying buffered stream API).
491
   // NOTE: "<stdout>" and "<stderr>" pathnames can be used to write to stdout and stderr,
492
   // else all directories up to file_pathname are recursively created if necessary.
493
 
494
   size_t string_index;
495
   size_t length;
496
   int errno_backup;
497
   char *temp_pathname;
498
   char *separator;
499
   char *context;
500
   FILE *fp; // buffered I/O with FILE * is ___FASTER___ than unbuffered I/O with file descriptors
501
 
502
   // do we want to write to the standard output or the standard error?
503
   if (strcmp (file_pathname, "<stdout>") == 0)
504
      fp = stdout;
505
   else if (strcmp (file_pathname, "<stderr>") == 0)
506
      fp = stderr;
507
   else
508
   {
509
      // ensure all the path up to file exists, create directories on the fly if needed
510
      // FIXME: this won't work on Windows with symlinks pointing to nonexistent directories...
511
      length = strlen (file_pathname);
512
      if (length == 0)
513
      {
514
         errno = EINVAL; // if the passed pathname is empty, report invalid parameter
515
         return (0); // and return an error value
516
      }
517
      temp_pathname = strdup (file_pathname); // have a working copy of file_pathname
518
      if (temp_pathname == NULL)
519
         return (0); // on strdup() failure, return an error value (errno already set to ENOMEM)
520
      for (string_index = length - 1; string_index != SIZE_MAX; string_index--) // i.e. loop until it overflows
521
         if ((temp_pathname[string_index] == '/') || (temp_pathname[string_index] == '\\'))
522
            break; // look for the last directory separator and stop as soon as we find it
523
      if (string_index != SIZE_MAX)
524
      {
525
         for (; string_index < length; string_index++)
526
            temp_pathname[string_index] = 0; // if we found one, break there so as to have just the path and clear the rest of the string
527
         context = NULL;
528
         separator = strtok_r (&temp_pathname[1], "/\\", &context); // for each separator in the remaining string past the first one...
529
         while (separator != NULL)
530
         {
531
            (void) mkdir (temp_pathname, 0755); // create directories recursively (FIXME: THIS IS NOT COMPATIBLE WITH LONG FILENAMES)
532
            temp_pathname[strlen (temp_pathname)] = '/'; // put back the separator
533
            separator = strtok_r (NULL, "/\\", &context); // and look for the next one
534
         }
535
      }
536
      free (temp_pathname); // release our working copy of file_pathname
537
 
538
      // open file for binary writing
539
#if defined(_WIN32) && (!defined(MAXPATHLEN) || (MAXPATHLEN <= 260))
540
      // on Windows, work around of the MAX_PATH limitation by using the Unicode APIs and NT-style pathnames
541
      // IMPLEMENTATION NOTE: ON WINDOWS, ALL PATHS ARE CONVERTED TO UTF-16 IN MSVCRT.DLL BEFORE THE ACTUAL SYSTEM CALL,
542
      // SO IT *IS* FASTER TO SYSTEMATICALLY CONVERT ALL PATHS TO NT PATHS, LIKE THEY DO IN LLVM/CLANG (see Path.inc)
543
      errno = EACCES; // set errno to something significant
544
      fp = NULL; // assume failure until told otherwise
545
      MultiByteToWideChar (AreFileApisANSI () ? 0 /*CP_ACP*/ : 1 /*CP_OEMCP*/, 0, file_pathname, -1, wide_pathname, sizeof (wide_pathname) / sizeof (wchar_t)); // convert pathname to wide characters, including the NULL terminator
546
      GetFullPathNameW (wide_pathname, sizeof (nt_pathname) / sizeof (wchar_t) - 4, &nt_pathname[4], NULL); // canonicalize the resulting pathname and write it just after the NT path prefix
547
      nt_filehandle = CreateFileW (nt_pathname, 0x40000000 /*GENERIC_WRITE*/, 0x3 /*FILE_SHARE_READ|FILE_SHARE_WRITE*/, NULL, 2 /*CREATE_ALWAYS [MANDATORY for truncation!]*/, 0x80 /*FILE_ATTRIBUTE_NORMAL*/, NULL); // now call CreateFile and get the filesystem object handle
548
      if (nt_filehandle != NULL)
549
      {
550
         intermediary_fd = _open_osfhandle ((intptr_t) nt_filehandle, O_WRONLY); // and convert this handle back into a POSIX file descriptor
551
         if (intermediary_fd != -1)
552
         {
553
            fp = _fdopen (intermediary_fd, "wb"); // now convert this handle to a buffered libc file descriptor
554
            if (fp == NULL)
555
               _close (intermediary_fd); // if we couldn't get a libc file descriptor out of that POSIX file descriptor, close it anyway
556
         }
557
         else
558
            CloseHandle (nt_filehandle); // if we couldn't get a POSIX file descriptor out of that handle, close it anyway
559
      }
560
      if (fp != NULL)
561
         errno = 0; // if file could be opened, clear errno
562
#else // !defined(_WIN32) || (defined(MAXPATHLEN) && (MAXPATHLEN > 260))
563
      fp = fopen (file_pathname, "wb"); // POSIX-compliant system. Good boy.
564
#endif // defined(_WIN32) && (!defined(MAXPATHLEN) || (MAXPATHLEN <= 260))
565
      if (fp == NULL)
566
         return (0); // on fopen() failure, return an error value (and errno is hopefully set to something meaningful)
567
   }
568
 
569
   // and write it at once
570
   if (fwrite (buffer->bytes, 1, buffer->size, fp) != buffer->size)
571
   {
572
      errno_backup = (errno ? errno : EIO); // preserve errno on errors; if it isn't set, set it to EIO
573
      if ((fp != stdout) && (fp != stderr))
574
         fclose (fp); // either case, close the file
575
      errno = errno_backup;
576
      return (0); // on failure, return an error value (errno already set to EIO - or something else set by the underlying buffered stream API)
577
   }
578
 
579
   if ((fp != stdout) && (fp != stderr))
580
      fclose (fp); // finished, close file (only if it was not one of the standard streams)
581
 
582
   errno = 0; // no error
583
   return (1); // and return SUCCESS
584
}
585
 
586
 
587
uint8_t *Buffer_FindFirst (const buffer_t *buffer, const void *needle, const size_t needle_length)
588
{
589
   // returns a pointer to the first occurence of needle in buffer haystack, or NULL if not found
590
 
591
   size_t byte_index;
592
 
593
   if (buffer->size < needle_length)
594
      return (NULL); // consistency check: if buffer is too small for needle, return NULL
595
   else if ((buffer->size == needle_length) && (memcmp (buffer->bytes, needle, buffer->size) == 0))
596
      return (buffer->bytes); // special case where needle is exactly as long as buffer
597
 
598
   // parse buffer from start to end
599
   for (byte_index = 0; byte_index <= buffer->size - needle_length; byte_index++) // inferior OR EQUAL, meaning last position INCLUDED!
600
      if (memcmp (&buffer->bytes[byte_index], needle, needle_length) == 0)
601
         return (&buffer->bytes[byte_index]); // return the first match we find
602
 
603
   return (NULL); // definitely no needle in buffer
604
}
605
 
606
 
607
uint8_t *Buffer_FindFirstCI (const buffer_t *buffer, const char *needle)
608
{
609
   // returns a pointer to the first occurence of needle in buffer haystack (CASE INSENSITIVELY), or NULL if not found
610
 
611
   size_t needle_length;
612
   size_t byte_index;
613
 
614
   needle_length = strlen (needle); // measure needle length
615
 
616
   if (buffer->size < needle_length)
617
      return (NULL); // consistency check: if buffer is too small for needle, return NULL
618
   else if ((buffer->size == needle_length) && (memcmp (buffer->bytes, needle, buffer->size) == 0))
619
      return (buffer->bytes); // special case where needle is exactly as long as buffer
620
 
621
   // parse buffer from start to end
622
   for (byte_index = 0; byte_index <= buffer->size - needle_length; byte_index++) // inferior OR EQUAL, meaning last position INCLUDED!
623
      if (strncasecmp ((const char *) &buffer->bytes[byte_index], needle, needle_length) == 0)
624
         return (&buffer->bytes[byte_index]); // return the first match we find
625
 
626
   return (NULL); // definitely no needle in buffer
627
}
628
 
629
 
630
uint8_t *Buffer_FindLast (const buffer_t *buffer, const void *needle, const size_t needle_length)
631
{
632
   // returns a pointer to the last occurence of needle in buffer haystack, or NULL if not found
633
 
634
   size_t byte_index;
635
 
636
   if (buffer->size < needle_length)
637
      return (NULL); // consistency check: if buffer is too small for needle, return NULL
638
   else if ((buffer->size == needle_length) && (memcmp (buffer->bytes, needle, buffer->size) == 0))
639
      return (buffer->bytes); // special case where needle is exactly as long as buffer
640
 
641
   // parse buffer from end to start
642
   for (byte_index = buffer->size - needle_length; byte_index != SIZE_MAX; byte_index--)
643
      if (memcmp (&buffer->bytes[byte_index], needle, needle_length) == 0)
644
         return (&buffer->bytes[byte_index]); // return the last match we find
645
 
646
   return (NULL); // definitely no needle in buffer
647
}
648
 
649
 
650
uint8_t *Buffer_FindLastCI (const buffer_t *buffer, const char *needle)
651
{
652
   // returns a pointer to the last occurence of needle in buffer haystack (CASE INSENSITIVELY), or NULL if not found
653
 
654
   size_t needle_length;
655
   size_t byte_index;
656
 
657
   needle_length = strlen (needle); // measure needle length
658
 
659
   if (buffer->size < needle_length)
660
      return (NULL); // consistency check: if buffer is too small for needle, return NULL
661
   else if ((buffer->size == needle_length) && (memcmp (buffer->bytes, needle, buffer->size) == 0))
662
      return (buffer->bytes); // special case where needle is exactly as long as buffer
663
 
664
   // parse buffer from end to start
665
   for (byte_index = buffer->size - needle_length; byte_index != SIZE_MAX; byte_index--)
666
      if (strncasecmp ((const char *) &buffer->bytes[byte_index], needle, needle_length) == 0)
667
         return (&buffer->bytes[byte_index]); // return the last match we find
668
 
669
   return (NULL); // definitely no needle in buffer
670
}
671
 
672
 
673
size_t Buffer_Replace (buffer_t *buffer, const void *needle, const size_t needle_length, const void *replacement, const size_t replacement_length, const int howmany_with_zero_for_all_and_minus_for_backwards)
674
{
675
   // replaces as many occurences of needle with replacement in buffer haystack, reallocating as needed.
676
   // If howmany is strictly positive, replacements stop after howmany replacements are made in the forward direction.
677
   // If howmany is strictly negative, replacements stop after -howmany replacements are made in the REVERSE direction.
678
   // Returns the number of replacements made, or 0 if none was made OR an error occured (in which case check errno, which may be E2BIG or ENOMEM).
679
 
680
   void *reallocated_ptr;
681
   void *safe_needle;
682
   size_t replacements_made;
683
   size_t replacements_max;
684
   size_t byte_index;
685
   size_t padding;
686
   int realloc_done;
687
 
688
   if (needle_length > buffer->size)
689
   {
690
      errno = E2BIG; // consistency check: if buffer is too small for needle, set errno to E2BIG
691
      return (0); // and return an error value
692
   }
693
 
694
   if (needle_length == 0)
695
      return (0); // consistency check: if needle is an empty string, return 0 (no replacement made)
696
 
697
   // is the number of replacements to do NOT exactly one AND does the needle we want to replace already belong to the buffer (meaning it will disappear during replacement) ?
698
   if ((abs (howmany_with_zero_for_all_and_minus_for_backwards) != 1) && ((uint8_t *) needle > buffer->bytes) && ((uint8_t *) needle < buffer->bytes + buffer->size))
699
   {
700
      safe_needle = (uint8_t *) malloc (needle_length); // allocate space for a copy of the needle
701
      if (safe_needle == NULL)
702
         return (0); // on allocation error, return 0 (errno already set to ENOMEM)
703
      memcpy (safe_needle, needle, needle_length); // and copy needle somewhere safe
704
   }
705
   else
706
      safe_needle = (uint8_t *) needle; // else we can use needle directly and spare us an allocation
707
 
708
   // see how much we need to replace and in what order
709
   replacements_made = 0;
710
   realloc_done = 0;
711
   if (howmany_with_zero_for_all_and_minus_for_backwards < 0)
712
   {
713
      // backwards direction
714
      replacements_max = -howmany_with_zero_for_all_and_minus_for_backwards;
715
 
716
      // parse buffer from end to start
717
      for (byte_index = buffer->size - needle_length; byte_index != SIZE_MAX; byte_index--)
718
      {
719
         if (memcmp (&buffer->bytes[byte_index], safe_needle, needle_length) != 0)
720
            continue; // if there's no match at this location, skip it
721
 
722
         // a replacement should be made here: see if we need to grow the data
723
         if (replacement_length > needle_length)
724
         {
725
            // if so, reallocate data buffer at the right size
726
            reallocated_ptr = realloc (buffer->bytes, buffer->size + replacement_length - needle_length + sizeof (size_t)); // always null-terminate buffers, but never report the null terminator
727
            if (reallocated_ptr == NULL)
728
            {
729
               replacements_made = 0;
730
               break; // on failure, return an error value (errno is set)
731
            }
732
 
733
            buffer->bytes = reallocated_ptr; // save pointer to reallocated data
734
            realloc_done = 1; // and remember that a reallocation was done
735
         }
736
 
737
         // move the remaining first if necessary, then put the replacement in place
738
         if (needle_length != replacement_length)
739
            memmove (&buffer->bytes[byte_index + replacement_length], &buffer->bytes[byte_index + needle_length], buffer->size - (byte_index + needle_length));
740
         if (replacement_length > 0)
741
            memcpy (&buffer->bytes[byte_index], replacement, replacement_length);
742
         buffer->size += replacement_length - needle_length; // adjust buffer size
743
 
744
         replacements_made++; // one more replacement has been made
745
         if ((replacements_max > 0) && (replacements_made >= replacements_max))
746
            break; // if a maximum replacements count was specified, stop here
747
      }
748
   }
749
   else
750
   {
751
      // forward direction
752
      replacements_max = howmany_with_zero_for_all_and_minus_for_backwards;
753
 
754
      // parse buffer from start to end
755
      for (byte_index = 0; byte_index <= buffer->size - needle_length; byte_index++) // inferior OR EQUAL, meaning last position INCLUDED!
756
      {
757
         if (memcmp (&buffer->bytes[byte_index], safe_needle, needle_length) != 0)
758
            continue; // if there's no match at this location, skip it
759
 
760
         // a replacement should be made here: see if we need to grow the data
761
         if (replacement_length > needle_length)
762
         {
763
            // if so, reallocate data buffer at the right size
764
            reallocated_ptr = realloc (buffer->bytes, buffer->size + replacement_length - needle_length + sizeof (size_t)); // always null-terminate buffers, but never report the null terminator
765
            if (reallocated_ptr == NULL)
766
            {
767
               replacements_made = 0;
768
               break; // on failure, return an error value (errno is set)
769
            }
770
 
771
            buffer->bytes = reallocated_ptr; // save pointer to reallocated data
772
            realloc_done = 1; // and remember that a reallocation was done
773
         }
774
 
775
         // move the remaining first (including the null terminator) if necessary, then put the replacement in place
776
         if (needle_length != replacement_length)
777
            memmove (&buffer->bytes[byte_index + replacement_length], &buffer->bytes[byte_index + needle_length], buffer->size - (byte_index + needle_length));
778
         if (replacement_length > 0)
779
            memcpy (&buffer->bytes[byte_index], replacement, replacement_length);
780
         buffer->size += replacement_length - needle_length; // adjust buffer size
781
 
782
         replacements_made++; // one more replacement has been made
783
         if ((replacements_max > 0) && (replacements_made >= replacements_max))
784
            break; // if a maximum replacements count was specified, stop here
785
 
786
         byte_index += replacement_length - 1; // jump over this replacement and proceed to the remaining of the string
787
      }
788
   }
789
 
790
   if (safe_needle != needle)
791
      free (safe_needle); // if we had to allocate space for a safe copy of the needle, free it now
792
 
793
   // IMPORTANT: in case we haven't reallocated ourselves, the passed buffer *MIGHT NOT* have the \0\0\0\0 suffix (e.g. hand-constructed), so we MUST ensure there's space in it to put it!
794
   if (!realloc_done)
795
   {
796
      // reallocate data buffer at the right size
797
      reallocated_ptr = realloc (buffer->bytes, buffer->size + sizeof (size_t)); // always null-terminate buffers, but never report the null terminator
798
      if (reallocated_ptr == NULL)
799
         return (0); // on failure, return an error value (errno already set to ENOMEM)
800
      buffer->bytes = reallocated_ptr; // save pointer to reallocated data
801
   }
802
   for (padding = 0; padding < sizeof (size_t); padding++)
803
      buffer->bytes[buffer->size + padding] = 0; // always null-terminate buffers, silently
804
 
805
   return (replacements_made); // finished, return the number of replacements made
806
}
807
 
808
 
809
int Buffer_Compare (buffer_t *buffer, const void *data, const size_t data_size)
810
{
811
   // compares a buffer with some data of data_size length, and return 0 if they match, non-zero if they differ.
812
   // If their size differ, a non-zero value (equivalent to the string "DIFF") is returned.
813
   // If their content differ, the result of memcmp() is returned.
814
 
815
   if ((data == buffer->bytes) && (data_size == buffer->size))
816
      return (0); // if both buffers are the same one, return 0
817
   else if (data_size != buffer->size)
818
      return ((int) ((ssize_t) data_size - (ssize_t) buffer->size)); // buffers differ in size, return the shortest first
819
 
820
   return (memcmp (buffer->bytes, data, data_size)); // do a memcmp() on both buffers
821
}
822
 
823
 
824
size_t Buffer_OffsetOf (buffer_t *buffer, const void *something)
825
{
826
   // returns the absolute offset of the data pointed to by something relatively to the beginning of buffer data
827
   // if something points outside of buffer, returns SIZE_MAX (in which case errno is set to EINVAL)
828
 
829
   if (((uint8_t *) something < buffer->bytes) || ((uint8_t *) something > buffer->bytes + buffer->size))
830
   {
831
      errno = EINVAL; // consistency check: if the queried pointer falls outside of the buffer, set errno to EINVAL
832
      return (SIZE_MAX); // and return an error code
833
   }
834
 
835
   return ((size_t) something - (size_t) buffer->bytes); // queried pointer lies within buffer: return its offset, which will be between 0 and buffer->size inclusive
836
}
837
 
838
 
839
int Buffer_TrimLeftUntil (buffer_t *buffer, const uint8_t *trimmable_bytes, const size_t trimmable_count, const int howmany)
840
{
841
   // removes leading characters from a buffer's data and returns 1 on success, or 0 if a memory
842
   // reallocation error happens (which shouldn't ever since we're reallocating smaller), in which case errno is set to ENOMEM.
843
   // Any character in the trimmable_bytes array is a candidate for elimination.
844
 
845
   void *reallocated_ptr;
846
   size_t removed_count;
847
   size_t max_count;
848
   size_t padding;
849
 
850
   removed_count = 0; // parse buffer string from the end looking for any of the trimmable bytes
851
   max_count = (howmany == 0 ? buffer->size : howmany); // see how many characters maximum we are allowed to trim
852
   while ((removed_count < max_count) && (memchr (trimmable_bytes, buffer->bytes[removed_count], trimmable_count) != NULL))
853
      removed_count++; // count the number of bytes to remove
854
 
855
   // did we find something to trim ?
856
   if (removed_count > 0)
857
   {
858
      // if so, shift the buffer data start by the number of bytes to remove
859
      memmove (buffer->bytes, &buffer->bytes[removed_count], buffer->size - removed_count);
860
      buffer->size -= removed_count; // adjust the new buffer size
861
 
862
      // reallocate data buffer at the right size
863
      reallocated_ptr = realloc (buffer->bytes, buffer->size + sizeof (size_t)); // always null-terminate buffers, but never report the null terminator
864
      if (reallocated_ptr == NULL)
865
         return (0); // on failure, return an error value (errno already set to ENOMEM)
866
 
867
      buffer->bytes = reallocated_ptr; // save pointer to reallocated data
868
      for (padding = 0; padding < sizeof (size_t); padding++)
869
         buffer->bytes[buffer->size + padding] = 0; // always null-terminate buffers, silently
870
   }
871
 
872
   return (1); // and return SUCCESS
873
}
874
 
875
 
876
int Buffer_TrimRightUntil (buffer_t *buffer, const uint8_t *trimmable_bytes, const size_t trimmable_count, const int howmany)
877
{
878
   // removes trailing characters from a buffer's data and returns 1 on success, or 0 if a memory
879
   // reallocation error happens (which shouldn't ever since we're reallocating smaller), in which case errno is set to ENOMEM.
880
   // Any character in the trimmable_bytes array is a candidate for elimination.
881
 
882
   void *reallocated_ptr;
883
   size_t removed_count;
884
   size_t max_count;
885
   size_t padding;
886
 
887
   removed_count = 0; // parse buffer string from the end looking for any of the trimmable bytes
888
   max_count = (howmany == 0 ? buffer->size : howmany); // see how many characters maximum we are allowed to trim
889
   while ((buffer->size > 0) && (removed_count < max_count) && (memchr (trimmable_bytes, buffer->bytes[buffer->size - 1], trimmable_count) != NULL))
890
   {
891
      removed_count++; // count the number of bytes to remove
892
      buffer->size--; // and decrease buffer size appropriately
893
   }
894
 
895
   // did we find something to trim ?
896
   if (removed_count > 0)
897
   {
898
      // if so, reallocate data buffer at the right size
899
      reallocated_ptr = realloc (buffer->bytes, buffer->size + sizeof (size_t)); // always null-terminate buffers, but never report the null terminator
900
      if (reallocated_ptr == NULL)
901
         return (0); // on failure, return an error value (errno already set to ENOMEM)
902
 
903
      buffer->bytes = reallocated_ptr; // save pointer to reallocated data
904
      for (padding = 0; padding < sizeof (size_t); padding++)
905
         buffer->bytes[buffer->size + padding] = 0; // always null-terminate buffers, silently
906
   }
907
 
908
   return (1); // and return SUCCESS
909
}
910
 
911
 
912
int Buffer_GetNthParts (buffer_t *buffer, const size_t wanted_part_index, const int howmany, const void *separator, const size_t separator_len, const int should_parse_as_text, buffer_t *preinitialized_outbuf)
913
{
914
   // extract and returns the Nth bit of buffer when split according to separator and return whether
915
   // it succeeded or not (in which case errno is set to EINVAL).
916
   // IMPORTANT: preinitialized_outbuf MUST have been properly initialized before!
917
 
918
   ptrdiff_t extractstart_index;
919
   size_t fieldstart_index;
920
   size_t char_index;
921
   size_t part_index;
922
 
923
   // handle special case first
924
   if (howmany == 0)
925
   {
926
      if (preinitialized_outbuf->bytes != NULL)
927
         Buffer_Forget (preinitialized_outbuf); // release output buffer if it was previously allocated
928
      Buffer_InitWithByteArray (preinitialized_outbuf, ""); // if we want a zero-length slice, prepare an empty buffer
929
      return (1); // and return SUCCESS
930
   }
931
 
932
   // read buffer characters and identify parts on the fly
933
   extractstart_index = -1;
934
   fieldstart_index = 0;
935
   part_index = 0;
936
   for (char_index = 0; char_index < buffer->size; char_index++)
937
   {
938
      // have we NOT found yet the start field and is this it ?
939
      if ((extractstart_index == -1) && (part_index == wanted_part_index))
940
         extractstart_index = fieldstart_index; // if so, remember where it starts
941
 
942
      if (should_parse_as_text && (buffer->bytes[char_index] == 0))
943
         break; // when end of text reached, stop searching
944
      else if ((char_index + separator_len <= buffer->size) && (memcmp (&buffer->bytes[char_index], separator, separator_len) == 0))
945
      {
946
         part_index++; // a separator is here, meaning that one part more was read
947
 
948
         if ((howmany > 0) && (part_index == wanted_part_index + howmany))
949
            break; // was it ALL that we were looking for ? if so, stop searching
950
 
951
         char_index += separator_len; // skip the separator
952
         fieldstart_index = char_index; // and remember a new part starts here
953
      }
954
   }
955
 
956
   // have we found the part we were looking for ?
957
   if (extractstart_index != -1)
958
   {
959
      if (preinitialized_outbuf->bytes != NULL)
960
         Buffer_Forget (preinitialized_outbuf); // release output buffer if it was previously allocated
961
      Buffer_InitWithData (preinitialized_outbuf, &buffer->bytes[extractstart_index], char_index - extractstart_index); // if so, so copy it
962
      return (1); // and return SUCCESS
963
   }
964
 
965
   // if we reach here, it means we haven't identified the part we were looking for
966
   errno = EINVAL; // so tell the user the wanted_part_index parameter was invalid
967
   return (0); // and return FAILURE
968
}