char-fork/llama.cpp

git clone https://git.t4t.associates/char-fork/llama.cpp

Alessandro de Oliveira Faria (A.K.A.CABELO)vendor : update subprocess.h (#27409)6657ded4f

char/aliceai-t5-moe
66.9 KiB2194 linesraw
1/*
2   The latest version of this library is available on GitHub;
3   https://github.com/sheredom/subprocess.h
4*/
5
6/*
7   This is free and unencumbered software released into the public domain.
8
9   Anyone is free to copy, modify, publish, use, compile, sell, or
10   distribute this software, either in source code form or as a compiled
11   binary, for any purpose, commercial or non-commercial, and by any
12   means.
13
14   In jurisdictions that recognize copyright laws, the author or authors
15   of this software dedicate any and all copyright interest in the
16   software to the public domain. We make this dedication for the benefit
17   of the public at large and to the detriment of our heirs and
18   successors. We intend this dedication to be an overt act of
19   relinquishment in perpetuity of all present and future rights to this
20   software under copyright law.
21
22   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
25   IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
26   OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
27   ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
28   OTHER DEALINGS IN THE SOFTWARE.
29
30   For more information, please refer to <http://unlicense.org/>
31*/
32
33#ifndef SHEREDOM_SUBPROCESS_H_INCLUDED
34#define SHEREDOM_SUBPROCESS_H_INCLUDED
35
36#if defined(_MSC_VER)
37#pragma warning(push, 1)
38
39/* disable warning: '__cplusplus' is not defined as a preprocessor macro,
40 * replacing with '0' for '#if/#elif' */
41#pragma warning(disable : 4668)
42#endif
43
44#include <stdio.h>
45#include <string.h>
46
47#if defined(_MSC_VER)
48#pragma warning(pop)
49#endif
50
51#if defined(__TINYC__)
52#define SUBPROCESS_ATTRIBUTE(a) __attribute((a))
53#else
54#define SUBPROCESS_ATTRIBUTE(a) __attribute__((a))
55#endif
56
57#if defined(_MSC_VER)
58#define subprocess_pure
59#define subprocess_weak __inline
60#define subprocess_tls __declspec(thread)
61#elif defined(__MINGW32__)
62#define subprocess_pure SUBPROCESS_ATTRIBUTE(pure)
63#define subprocess_weak static SUBPROCESS_ATTRIBUTE(used)
64#define subprocess_tls __thread
65#elif defined(__clang__) || defined(__GNUC__) || defined(__TINYC__)
66#define subprocess_pure SUBPROCESS_ATTRIBUTE(pure)
67#define subprocess_weak SUBPROCESS_ATTRIBUTE(weak)
68#define subprocess_tls __thread
69#else
70#error Non clang, non gcc, non MSVC compiler found!
71#endif
72
73struct subprocess_s;
74
75enum subprocess_option_e {
76  // stdout and stderr are the same FILE.
77  subprocess_option_combined_stdout_stderr = 0x1,
78
79  // The child process should inherit the environment variables of the parent.
80  subprocess_option_inherit_environment = 0x2,
81
82  // Enable asynchronous reading of stdout/stderr before it has completed.
83  subprocess_option_enable_async = 0x4,
84
85  // Enable the child process to be spawned with no window visible if supported
86  // by the platform.
87  subprocess_option_no_window = 0x8,
88
89  // Search for program names in the PATH variable. Always enabled on Windows.
90  // Note: this will **not** search for paths in any provided custom environment
91  // and instead uses the PATH of the spawning process.
92  subprocess_option_search_user_path = 0x10,
93
94  // Make subprocess_read_stdout and subprocess_read_stderr return immediately
95  // with 0 if no data is available. Requires subprocess_option_enable_async.
96  subprocess_option_enable_async_no_wait = 0x20
97};
98
99// Error codes returned by subprocess_create and subprocess_create_ex.
100// subprocess_error_success is always zero; all errors are non-zero.
101enum subprocess_error_e {
102  subprocess_error_success = 0,
103  subprocess_error_unknown = -1,
104  subprocess_error_invalid_options = -2,
105  subprocess_error_invalid_environment = -3,
106  subprocess_error_not_found = -4,
107  subprocess_error_permission_denied = -5,
108  subprocess_error_no_memory = -6,
109  subprocess_error_pipe = -7,
110  subprocess_error_spawn = -8,
111  subprocess_error_not_supported = -9
112};
113
114#if defined(__cplusplus)
115extern "C" {
116#endif
117
118/// @brief Create a process.
119/// @param command_line An array of strings for the command line to execute for
120/// this process. The last element must be NULL to signify the end of the array.
121/// On Windows these strings are interpreted as UTF-8 and passed to the Unicode
122/// process creation APIs. The memory backing this parameter only needs to
123/// persist until this function returns.
124/// @param options A bit field of subprocess_option_e's to pass.
125/// @param out_process The newly created process.
126/// @return On success zero is returned. On failure a non-zero
127/// `subprocess_error_e` value is returned; inspect `errno` on POSIX platforms
128/// or `GetLastError()` on Windows for the platform-specific failure reason.
129subprocess_weak int subprocess_create(const char *const command_line[],
130                                      int options,
131                                      struct subprocess_s *const out_process);
132
133/// @brief Create a process (extended create).
134/// @param command_line An array of strings for the command line to execute for
135/// this process. The last element must be NULL to signify the end of the array.
136/// On Windows these strings are interpreted as UTF-8 and passed to the Unicode
137/// process creation APIs. The memory backing this parameter only needs to
138/// persist until this function returns.
139/// @param options A bit field of subprocess_option_e's to pass.
140/// @param environment An optional array of strings for the environment to use
141/// for a child process (each element of the form FOO=BAR). The last element
142/// must be NULL to signify the end of the array. On Windows these strings are
143/// interpreted as UTF-8.
144/// @param process_cwd The current working directory of the newly created
145/// process. If NULL, will be the same as the parent process. On Windows this
146/// string is interpreted as UTF-8.
147/// @param out_process The newly created process.
148/// @return On success zero is returned. On failure a non-zero
149/// `subprocess_error_e` value is returned; inspect `errno` on POSIX platforms
150/// or `GetLastError()` on Windows for the platform-specific failure reason.
151///
152/// If `options` contains `subprocess_option_inherit_environment`, then
153/// `environment` must be NULL.
154subprocess_weak int
155subprocess_create_ex(const char *const command_line[], int options,
156                     const char *const environment[],
157                     const char *const process_cwd,
158                     struct subprocess_s *const out_process);
159
160/// @brief Get the standard input file for a process.
161/// @param process The process to query.
162/// @return The file for standard input of the process.
163///
164/// The file returned can be written to by the parent process to feed data to
165/// the standard input of the process.
166subprocess_pure subprocess_weak FILE *
167subprocess_stdin(const struct subprocess_s *const process);
168
169/// @brief Get the standard output file for a process.
170/// @param process The process to query.
171/// @return The file for standard output of the process.
172///
173/// The file returned can be read from by the parent process to read data from
174/// the standard output of the child process.
175subprocess_pure subprocess_weak FILE *
176subprocess_stdout(const struct subprocess_s *const process);
177
178/// @brief Get the standard error file for a process.
179/// @param process The process to query.
180/// @return The file for standard error of the process.
181///
182/// The file returned can be read from by the parent process to read data from
183/// the standard error of the child process.
184///
185/// If the process was created with the subprocess_option_combined_stdout_stderr
186/// option bit set, this function will return NULL, and the subprocess_stdout
187/// function should be used for both the standard output and error combined.
188subprocess_pure subprocess_weak FILE *
189subprocess_stderr(const struct subprocess_s *const process);
190
191/// @brief Wait for a process to finish execution.
192/// @param process The process to wait for.
193/// @param out_return_code The return code of the returned process (can be
194/// NULL).
195/// @return On success zero is returned.
196///
197/// Joining a process will close the stdin pipe to the process.
198subprocess_weak int subprocess_join(struct subprocess_s *const process,
199                                    int *const out_return_code);
200
201/// @brief Destroy a previously created process.
202/// @param process The process to destroy.
203/// @return On success zero is returned.
204///
205/// If the process to be destroyed had not finished execution, it may out live
206/// the parent process.
207subprocess_weak int subprocess_destroy(struct subprocess_s *const process);
208
209/// @brief Terminate a previously created process.
210/// @param process The process to terminate.
211/// @return On success zero is returned.
212///
213/// If the process to be destroyed had not finished execution, it will be
214/// terminated (i.e killed).
215subprocess_weak int subprocess_terminate(struct subprocess_s *const process);
216
217/// @brief Read the standard output from the child process.
218/// @param process The process to read from.
219/// @param buffer The buffer to read into.
220/// @param size The maximum number of bytes to read.
221/// @return The number of bytes actually read into buffer. Can only be 0 if the
222/// process has complete, or if the process was created with
223/// `subprocess_option_enable_async_no_wait` and no data is currently available.
224///
225/// The only safe way to read from the standard output of a process during it's
226/// execution is to use the `subprocess_option_enable_async` option in
227/// conjunction with this method.
228subprocess_weak unsigned
229subprocess_read_stdout(struct subprocess_s *const process, char *const buffer,
230                       unsigned size);
231
232/// @brief Read the standard error from the child process.
233/// @param process The process to read from.
234/// @param buffer The buffer to read into.
235/// @param size The maximum number of bytes to read.
236/// @return The number of bytes actually read into buffer. Can only be 0 if the
237/// process has complete, or if the process was created with
238/// `subprocess_option_enable_async_no_wait` and no data is currently available.
239///
240/// The only safe way to read from the standard error of a process during it's
241/// execution is to use the `subprocess_option_enable_async` option in
242/// conjunction with this method.
243subprocess_weak unsigned
244subprocess_read_stderr(struct subprocess_s *const process, char *const buffer,
245                       unsigned size);
246
247/// @brief Returns if the subprocess is currently still alive and executing.
248/// @param process The process to check.
249/// @return If the process is still alive non-zero is returned.
250subprocess_weak int subprocess_alive(struct subprocess_s *const process);
251
252#if defined(__cplusplus)
253#define SUBPROCESS_CAST(type, x) static_cast<type>(x)
254#define SUBPROCESS_PTR_CAST(type, x) reinterpret_cast<type>(x)
255#define SUBPROCESS_CONST_CAST(type, x) const_cast<type>(x)
256#define SUBPROCESS_NULL NULL
257#else
258#define SUBPROCESS_CAST(type, x) ((type)(x))
259#define SUBPROCESS_PTR_CAST(type, x) ((type)(x))
260#define SUBPROCESS_CONST_CAST(type, x) ((type)(x))
261#define SUBPROCESS_NULL 0
262#endif
263
264#if !defined(_WIN32)
265#include <errno.h>
266#include <fcntl.h>
267#include <signal.h>
268#include <spawn.h>
269#include <stdlib.h>
270#if defined(__APPLE__)
271#include <AvailabilityMacros.h>
272#endif
273#include <sys/types.h>
274#include <sys/wait.h>
275#include <unistd.h>
276#endif
277
278#if defined(__NetBSD__)
279#include <sys/param.h>
280#endif
281
282/* Which spelling of the chdir file action the platform provides, if any.
283   POSIX 2024 standardised posix_spawn_file_actions_addchdir; implementations
284   that shipped it earlier called it ..._np. macOS 26 and NetBSD 10 use the
285   standard name, glibc 2.29+, macOS 10.15+ and FreeBSD 13.1+ use the _np name,
286   and AIX, NetBSD 9 and older, and OpenBSD provide neither. */
287#if !defined(SUBPROCESS_ADDCHDIR_IS_POSIX)
288#if (defined(__APPLE__) && MAC_OS_X_VERSION_MIN_REQUIRED >= 260000) ||        \
289    (defined(__NetBSD__) && __NetBSD_Version__ >= 1000000000)
290#define SUBPROCESS_ADDCHDIR_IS_POSIX 1
291#else
292#define SUBPROCESS_ADDCHDIR_IS_POSIX 0
293#endif
294#endif
295
296/* Whether to launch the child with fork()+exec() instead of posix_spawn(),
297   for platforms with no posix_spawn_file_actions_addchdir under either
298   spelling: the child chdir()s before exec, and a close-on-exec pipe carries
299   exec's errno back. Define this yourself to force either implementation. */
300#if !defined(SUBPROCESS_SPAWN_VIA_FORK)
301#if defined(_AIX) || defined(__OpenBSD__) ||                                  \
302    (defined(__NetBSD__) && (__NetBSD_Version__ < 1000000000))
303#define SUBPROCESS_SPAWN_VIA_FORK 1
304#else
305#define SUBPROCESS_SPAWN_VIA_FORK 0
306#endif
307#endif
308
309/* Whether subprocess_create_ex can honour process_cwd. glibc only gained
310   posix_spawn_file_actions_addchdir_np in 2.29, and macOS in 10.15; the SDKs
311   mark it unavailable on iOS, tvOS and watchOS, where the undefined version
312   macro folds to 0 and so answers correctly. Define this yourself to override
313   the detection, for instance on musl older than 1.1.24. */
314#if !defined(SUBPROCESS_HAVE_CWD)
315#if SUBPROCESS_SPAWN_VIA_FORK
316#define SUBPROCESS_HAVE_CWD 1
317#elif defined(__GLIBC__)
318#if __GLIBC_PREREQ(2, 29)
319#define SUBPROCESS_HAVE_CWD 1
320#else
321#define SUBPROCESS_HAVE_CWD 0
322#endif
323#elif defined(__APPLE__) && MAC_OS_X_VERSION_MIN_REQUIRED < 101500
324#define SUBPROCESS_HAVE_CWD 0
325#else
326#define SUBPROCESS_HAVE_CWD 1
327#endif
328#endif
329
330/* Whether a failed exec is reported back to the caller. The fork() path always
331   reports it through its error pipe. glibc's posix_spawn only started doing so
332   in 2.24; before that the child silently exits with 127. */
333#if !defined(SUBPROCESS_SPAWN_REPORTS_EXEC_ERRORS)
334#if SUBPROCESS_SPAWN_VIA_FORK
335#define SUBPROCESS_SPAWN_REPORTS_EXEC_ERRORS 1
336#elif defined(__GLIBC__)
337#if __GLIBC_PREREQ(2, 24)
338#define SUBPROCESS_SPAWN_REPORTS_EXEC_ERRORS 1
339#else
340#define SUBPROCESS_SPAWN_REPORTS_EXEC_ERRORS 0
341#endif
342#else
343#define SUBPROCESS_SPAWN_REPORTS_EXEC_ERRORS 1
344#endif
345#endif
346
347#if defined(_WIN32)
348
349#include <wchar.h>
350
351#if defined(__clang__)
352#if __has_warning("-Wc++-keyword")
353#pragma clang diagnostic push
354#pragma clang diagnostic ignored "-Wc++-keyword"
355#endif
356#endif
357
358typedef wchar_t subprocess_wchar_t;
359
360#if defined(__clang__)
361#if __has_warning("-Wc++-keyword")
362#pragma clang diagnostic pop
363#endif
364#endif
365
366#if (_MSC_VER < 1920)
367#ifdef _WIN64
368typedef __int64 subprocess_intptr_t;
369typedef unsigned __int64 subprocess_size_t;
370#else
371typedef int subprocess_intptr_t;
372typedef unsigned int subprocess_size_t;
373#endif
374#else
375#include <inttypes.h>
376
377typedef intptr_t subprocess_intptr_t;
378typedef size_t subprocess_size_t;
379#endif
380
381/* SIZE_T is ULONG_PTR, which is not size_t: on Win32 both are 32 bits wide but
382   unsigned long and unsigned int are still distinct types. */
383#ifdef _WIN64
384typedef subprocess_size_t subprocess_ulongptr_t;
385#else
386typedef unsigned long subprocess_ulongptr_t;
387#endif
388
389#ifdef __clang__
390#pragma clang diagnostic push
391#pragma clang diagnostic ignored "-Wreserved-identifier"
392#endif
393
394typedef struct _PROCESS_INFORMATION *LPPROCESS_INFORMATION;
395typedef struct _SECURITY_ATTRIBUTES *LPSECURITY_ATTRIBUTES;
396typedef struct _STARTUPINFOW *LPSTARTUPINFOW;
397typedef struct _OVERLAPPED *LPOVERLAPPED;
398typedef struct _PROC_THREAD_ATTRIBUTE_LIST *LPPROC_THREAD_ATTRIBUTE_LIST;
399
400#ifdef __clang__
401#pragma clang diagnostic pop
402#endif
403
404#ifdef _MSC_VER
405#pragma warning(push, 1)
406#endif
407#ifdef __MINGW32__
408#pragma GCC diagnostic push
409#pragma GCC diagnostic ignored "-Wpedantic"
410#endif
411#ifdef __clang__
412#pragma clang diagnostic push
413#pragma clang diagnostic ignored "-Wpadded"
414#endif
415
416struct subprocess_subprocess_information_s {
417  void *hProcess;
418  void *hThread;
419  unsigned long dwProcessId;
420  unsigned long dwThreadId;
421};
422
423struct subprocess_security_attributes_s {
424  unsigned long nLength;
425  void *lpSecurityDescriptor;
426  int bInheritHandle;
427};
428
429struct subprocess_startup_info_s {
430  unsigned long cb;
431  subprocess_wchar_t *lpReserved;
432  subprocess_wchar_t *lpDesktop;
433  subprocess_wchar_t *lpTitle;
434  unsigned long dwX;
435  unsigned long dwY;
436  unsigned long dwXSize;
437  unsigned long dwYSize;
438  unsigned long dwXCountChars;
439  unsigned long dwYCountChars;
440  unsigned long dwFillAttribute;
441  unsigned long dwFlags;
442  unsigned short wShowWindow;
443  unsigned short cbReserved2;
444  unsigned char *lpReserved2;
445  void *hStdInput;
446  void *hStdOutput;
447  void *hStdError;
448};
449
450struct subprocess_startup_info_ex_s {
451  struct subprocess_startup_info_s startupInfo;
452  void *attributeList;
453};
454
455struct subprocess_overlapped_s {
456  uintptr_t Internal;
457  uintptr_t InternalHigh;
458  union {
459    struct {
460      unsigned long Offset;
461      unsigned long OffsetHigh;
462    } DUMMYSTRUCTNAME;
463    void *Pointer;
464  } DUMMYUNIONNAME;
465
466  void *hEvent;
467};
468
469#ifdef __clang__
470#pragma clang diagnostic pop
471#endif
472#ifdef __MINGW32__
473#pragma GCC diagnostic pop
474#endif
475#ifdef _MSC_VER
476#pragma warning(pop)
477#endif
478
479__declspec(dllimport) unsigned long __stdcall GetLastError(void);
480__declspec(dllimport) void __stdcall SetLastError(unsigned long);
481__declspec(dllimport) int __stdcall SetHandleInformation(void *, unsigned long,
482                                                         unsigned long);
483__declspec(dllimport) int __stdcall CreatePipe(void **, void **,
484                                               LPSECURITY_ATTRIBUTES,
485                                               unsigned long);
486__declspec(dllimport) void *__stdcall CreateNamedPipeA(
487    const char *, unsigned long, unsigned long, unsigned long, unsigned long,
488    unsigned long, unsigned long, LPSECURITY_ATTRIBUTES);
489__declspec(dllimport) int __stdcall ReadFile(void *, void *, unsigned long,
490                                             unsigned long *, LPOVERLAPPED);
491__declspec(dllimport) unsigned long __stdcall GetCurrentProcessId(void);
492__declspec(dllimport) unsigned long __stdcall GetCurrentThreadId(void);
493__declspec(dllimport) void *__stdcall CreateFileA(const char *, unsigned long,
494                                                  unsigned long,
495                                                  LPSECURITY_ATTRIBUTES,
496                                                  unsigned long, unsigned long,
497                                                  void *);
498__declspec(dllimport) void *__stdcall CreateEventA(LPSECURITY_ATTRIBUTES, int,
499                                                   int, const char *);
500__declspec(dllimport) int __stdcall CreateProcessW(
501    const subprocess_wchar_t *, subprocess_wchar_t *, LPSECURITY_ATTRIBUTES,
502    LPSECURITY_ATTRIBUTES, int, unsigned long, void *,
503    const subprocess_wchar_t *, LPSTARTUPINFOW, LPPROCESS_INFORMATION);
504__declspec(dllimport) int __stdcall
505InitializeProcThreadAttributeList(LPPROC_THREAD_ATTRIBUTE_LIST, unsigned long,
506                                  unsigned long, subprocess_ulongptr_t *);
507__declspec(dllimport) int __stdcall UpdateProcThreadAttribute(
508    LPPROC_THREAD_ATTRIBUTE_LIST, unsigned long, subprocess_ulongptr_t, void *,
509    subprocess_ulongptr_t, void *, subprocess_ulongptr_t *);
510__declspec(dllimport) void __stdcall
511DeleteProcThreadAttributeList(LPPROC_THREAD_ATTRIBUTE_LIST);
512__declspec(dllimport) int __stdcall MultiByteToWideChar(
513    unsigned int, unsigned long, const char *, int, subprocess_wchar_t *, int);
514__declspec(dllimport) int __stdcall CloseHandle(void *);
515__declspec(dllimport) unsigned long __stdcall WaitForSingleObject(
516    void *, unsigned long);
517__declspec(dllimport) int __stdcall GetExitCodeProcess(
518    void *, unsigned long *lpExitCode);
519__declspec(dllimport) int __stdcall TerminateProcess(void *, unsigned int);
520__declspec(dllimport) unsigned long __stdcall WaitForMultipleObjects(
521    unsigned long, void *const *, int, unsigned long);
522__declspec(dllimport) int __stdcall GetOverlappedResult(void *, LPOVERLAPPED,
523                                                        unsigned long *, int);
524__declspec(dllimport) int __stdcall PeekNamedPipe(void *, void *, unsigned long,
525                                                  unsigned long *,
526                                                  unsigned long *,
527                                                  unsigned long *);
528
529#if defined(_DLL)
530#define SUBPROCESS_DLLIMPORT __declspec(dllimport)
531#else
532#define SUBPROCESS_DLLIMPORT
533#endif
534
535#ifdef __clang__
536#pragma clang diagnostic push
537#pragma clang diagnostic ignored "-Wreserved-identifier"
538#endif
539
540SUBPROCESS_DLLIMPORT int __cdecl _fileno(FILE *);
541SUBPROCESS_DLLIMPORT int __cdecl _open_osfhandle(subprocess_intptr_t, int);
542SUBPROCESS_DLLIMPORT subprocess_intptr_t __cdecl _get_osfhandle(int);
543SUBPROCESS_DLLIMPORT int __cdecl _close(int);
544
545#ifndef __MINGW32__
546void *__cdecl _alloca(subprocess_size_t);
547#else
548#include <malloc.h>
549#endif
550
551#ifdef __clang__
552#pragma clang diagnostic pop
553#endif
554
555#else
556typedef size_t subprocess_size_t;
557#endif
558
559#ifdef __clang__
560#pragma clang diagnostic push
561#pragma clang diagnostic ignored "-Wpadded"
562#endif
563struct subprocess_s {
564  FILE *stdin_file;
565  FILE *stdout_file;
566  FILE *stderr_file;
567
568#if defined(_WIN32)
569  void *hProcess;
570  void *hStdInput;
571  void *hEventOutput;
572  void *hEventError;
573#else
574  pid_t child;
575  int return_status;
576#endif
577
578  int alive;
579  int no_wait;
580};
581#ifdef __clang__
582#pragma clang diagnostic pop
583#endif
584
585#if defined(_WIN32)
586subprocess_weak int subprocess_error_from_windows_error(unsigned long error);
587int subprocess_error_from_windows_error(unsigned long error) {
588  enum {
589    errorFileNotFound = 2,
590    errorPathNotFound = 3,
591    errorTooManyOpenFiles = 4,
592    errorAccessDenied = 5,
593    errorInvalidHandle = 6,
594    errorNotEnoughMemory = 8,
595    errorOutOfMemory = 14,
596    errorInvalidDrive = 15,
597    errorBadPathname = 161,
598    errorDirectory = 267
599  };
600
601  switch (error) {
602  case errorFileNotFound:
603  case errorPathNotFound:
604  case errorInvalidDrive:
605  case errorBadPathname:
606  case errorDirectory:
607    return subprocess_error_not_found;
608  case errorAccessDenied:
609    return subprocess_error_permission_denied;
610  case errorTooManyOpenFiles:
611  case errorNotEnoughMemory:
612  case errorOutOfMemory:
613    return subprocess_error_no_memory;
614  case errorInvalidHandle:
615    return subprocess_error_pipe;
616  default:
617    return subprocess_error_unknown;
618  }
619}
620#else
621subprocess_weak int subprocess_error_from_errno(int error);
622int subprocess_error_from_errno(int error) {
623  switch (error) {
624  case ENOENT:
625  case ENOTDIR:
626    return subprocess_error_not_found;
627  case EACCES:
628  case EPERM:
629    return subprocess_error_permission_denied;
630  case EMFILE:
631  case ENFILE:
632  case ENOMEM:
633    return subprocess_error_no_memory;
634  case ENOSYS:
635    return subprocess_error_not_supported;
636  default:
637    return subprocess_error_unknown;
638  }
639}
640#endif
641
642#if defined(__clang__)
643#if __has_warning("-Wunsafe-buffer-usage")
644#pragma clang diagnostic push
645#pragma clang diagnostic ignored "-Wunsafe-buffer-usage"
646#endif
647#endif
648
649#if defined(_WIN32)
650subprocess_weak int subprocess_create_named_pipe_helper(void **rd, void **wr);
651subprocess_weak void subprocess_close_handle(void **handle);
652void subprocess_close_handle(void **handle) {
653  const void *const invalidHandleValue =
654      SUBPROCESS_PTR_CAST(void *, ~(SUBPROCESS_CAST(subprocess_intptr_t, 0)));
655
656  if (*handle && (invalidHandleValue != *handle)) {
657    CloseHandle(*handle);
658  }
659
660  *handle = SUBPROCESS_NULL;
661}
662
663int subprocess_create_named_pipe_helper(void **rd, void **wr) {
664  const unsigned long pipeAccessInbound = 0x00000001;
665  const unsigned long fileFlagOverlapped = 0x40000000;
666  const unsigned long pipeTypeByte = 0x00000000;
667  const unsigned long pipeWait = 0x00000000;
668  const unsigned long genericWrite = 0x40000000;
669  const unsigned long openExisting = 3;
670  const unsigned long fileAttributeNormal = 0x00000080;
671  const void *const invalidHandleValue =
672      SUBPROCESS_PTR_CAST(void *, ~(SUBPROCESS_CAST(subprocess_intptr_t, 0)));
673  struct subprocess_security_attributes_s saAttr = {sizeof(saAttr),
674                                                    SUBPROCESS_NULL, 1};
675  char name[256] = {0};
676#if defined(__clang__)
677#if __has_warning("-Wunique-object-duplication")
678#pragma clang diagnostic push
679#pragma clang diagnostic ignored "-Wunique-object-duplication"
680#endif
681#endif
682  static subprocess_tls long index = 0;
683#if defined(__clang__)
684#if __has_warning("-Wunique-object-duplication")
685#pragma clang diagnostic pop
686#endif
687#endif
688  const long unique = index++;
689
690  *rd = SUBPROCESS_NULL;
691  *wr = SUBPROCESS_NULL;
692
693#if defined(_MSC_VER) && _MSC_VER < 1900
694#pragma warning(push, 1)
695#pragma warning(disable : 4996)
696  _snprintf(name, sizeof(name) - 1,
697            "\\\\.\\pipe\\sheredom_subprocess_h.%08lx.%08lx.%ld",
698            GetCurrentProcessId(), GetCurrentThreadId(), unique);
699#pragma warning(pop)
700#else
701  snprintf(name, sizeof(name) - 1,
702           "\\\\.\\pipe\\sheredom_subprocess_h.%08lx.%08lx.%ld",
703           GetCurrentProcessId(), GetCurrentThreadId(), unique);
704#endif
705
706  *rd =
707      CreateNamedPipeA(name, pipeAccessInbound | fileFlagOverlapped,
708                       pipeTypeByte | pipeWait, 1, 4096, 4096, SUBPROCESS_NULL,
709                       SUBPROCESS_PTR_CAST(LPSECURITY_ATTRIBUTES, &saAttr));
710
711  if (invalidHandleValue == *rd) {
712    return -1;
713  }
714
715  *wr = CreateFileA(name, genericWrite, SUBPROCESS_NULL,
716                    SUBPROCESS_PTR_CAST(LPSECURITY_ATTRIBUTES, &saAttr),
717                    openExisting, fileAttributeNormal, SUBPROCESS_NULL);
718
719  if (invalidHandleValue == *wr) {
720    subprocess_close_handle(rd);
721    return -1;
722  }
723
724  return 0;
725}
726#endif
727
728#if !defined(_WIN32)
729/* Move a pipe end off 0, 1 or 2. Duplicating a descriptor onto itself is a
730   no-op, so a pipe end already sitting on a standard descriptor would keep its
731   FD_CLOEXEC and be closed by exec, leaving the child without that stream. */
732static int subprocess_fds_above_std(int fds[2]) {
733  int fd_flags;
734  int index;
735  int moved;
736  int saved_errno;
737
738  for (index = 0; index < 2; index++) {
739    if (fds[index] > STDERR_FILENO) {
740      continue;
741    }
742
743    moved = fcntl(fds[index], F_DUPFD, STDERR_FILENO + 1);
744    if (-1 != moved) {
745      fd_flags = fcntl(moved, F_GETFD, 0);
746      if ((-1 == fd_flags) ||
747          (-1 == fcntl(moved, F_SETFD, fd_flags | FD_CLOEXEC))) {
748        saved_errno = errno;
749        close(moved);
750        errno = saved_errno;
751        moved = -1;
752      }
753    }
754
755    if (-1 == moved) {
756      saved_errno = errno;
757      close(fds[0]);
758      close(fds[1]);
759      fds[0] = -1;
760      fds[1] = -1;
761      errno = saved_errno;
762      return -1;
763    }
764
765    close(fds[index]);
766    fds[index] = moved;
767  }
768
769  return 0;
770}
771
772/* Create pipes with close-on-exec set so later subprocesses do not inherit
773   descriptors belonging to subprocesses which are already running. */
774static int subprocess_pipe_cloexec(int fds[2]) {
775  int fd_flags;
776  int index;
777  int saved_errno;
778
779#if defined(__linux__) || defined(__FreeBSD__) || defined(__NetBSD__) ||       \
780    defined(__OpenBSD__) || defined(__DragonFly__) ||                         \
781    (defined(__sun) && defined(__SVR4))
782  if (0 == pipe2(fds, O_CLOEXEC)) {
783    return subprocess_fds_above_std(fds);
784  }
785
786  /* Older kernels can lack pipe2 even when the C library declares it. */
787  if (ENOSYS != errno) {
788    return -1;
789  }
790#endif
791
792  if (0 != pipe(fds)) {
793    return -1;
794  }
795
796  for (index = 0; index < 2; index++) {
797    fd_flags = fcntl(fds[index], F_GETFD, 0);
798    if ((-1 == fd_flags) ||
799        (-1 == fcntl(fds[index], F_SETFD, fd_flags | FD_CLOEXEC))) {
800      saved_errno = errno;
801      close(fds[0]);
802      close(fds[1]);
803      fds[0] = -1;
804      fds[1] = -1;
805      errno = saved_errno;
806      return -1;
807    }
808  }
809
810  return subprocess_fds_above_std(fds);
811}
812#endif
813
814int subprocess_create(const char *const commandLine[], int options,
815                      struct subprocess_s *const out_process) {
816  return subprocess_create_ex(commandLine, options, SUBPROCESS_NULL,
817                              SUBPROCESS_NULL, out_process);
818}
819
820#if SUBPROCESS_SPAWN_VIA_FORK
821/* Not every platform declares execvpe: AIX exports it from libc without ever
822   naming it in a header, and glibc hides it behind _GNU_SOURCE. */
823extern int execvpe(const char *, char *const *, char *const *);
824#endif
825
826int subprocess_create_ex(const char *const commandLine[], int options,
827                         const char *const environment[],
828                         const char *const process_cwd,
829                         struct subprocess_s *const out_process) {
830#if defined(_WIN32)
831  int fd;
832  int async_no_wait;
833  void *rd = SUBPROCESS_NULL;
834  void *wr = SUBPROCESS_NULL;
835  char *commandLineCombined;
836  subprocess_wchar_t *commandLineCombinedWide = SUBPROCESS_NULL;
837  subprocess_wchar_t *process_cwd_wide = SUBPROCESS_NULL;
838  subprocess_size_t len;
839  int wide_len;
840  int i, j;
841  int need_quoting;
842  subprocess_size_t bs_run;
843  unsigned long flags = 0;
844  unsigned long last_error = 0;
845  int attribute_list_initialized = 0;
846  int result = subprocess_error_unknown;
847  const unsigned int codePageUtf8 = 65001;
848  const unsigned long mbErrInvalidChars = 0x00000008;
849  const unsigned long startFUseStdHandles = 0x00000100;
850  const unsigned long handleFlagInherit = 0x00000001;
851  const unsigned long createNoWindow = 0x08000000;
852  const unsigned long createUnicodeEnvironment = 0x00000400;
853  const unsigned long extendedStartupInfoPresent = 0x00080000;
854  const subprocess_size_t procThreadAttributeHandleList = 0x00020002;
855  struct subprocess_subprocess_information_s processInfo = {SUBPROCESS_NULL,
856                                                            SUBPROCESS_NULL, 0,
857                                                            0};
858  struct subprocess_security_attributes_s saAttr = {sizeof(saAttr),
859                                                    SUBPROCESS_NULL, 1};
860  subprocess_wchar_t empty_environment[2] = {0, 0};
861  subprocess_wchar_t *used_environment = SUBPROCESS_NULL;
862  subprocess_ulongptr_t attribute_list_size = 0;
863  subprocess_size_t inherited_handle_count = 0;
864  LPPROC_THREAD_ATTRIBUTE_LIST attribute_list = SUBPROCESS_NULL;
865  void *inherited_handles[3];
866  struct subprocess_startup_info_ex_s startInfoEx;
867  struct subprocess_startup_info_s startInfo = {0,
868                                                SUBPROCESS_NULL,
869                                                SUBPROCESS_NULL,
870                                                SUBPROCESS_NULL,
871                                                0,
872                                                0,
873                                                0,
874                                                0,
875                                                0,
876                                                0,
877                                                0,
878                                                0,
879                                                0,
880                                                0,
881                                                SUBPROCESS_NULL,
882                                                SUBPROCESS_NULL,
883                                                SUBPROCESS_NULL,
884                                                SUBPROCESS_NULL};
885
886  async_no_wait = subprocess_option_enable_async_no_wait ==
887                  (options & subprocess_option_enable_async_no_wait);
888
889  if (async_no_wait && (subprocess_option_enable_async !=
890                        (options & subprocess_option_enable_async))) {
891    return subprocess_error_invalid_options;
892  }
893
894  startInfo.cb = sizeof(startInfo);
895  startInfo.dwFlags = startFUseStdHandles;
896
897  if (subprocess_option_no_window == (options & subprocess_option_no_window)) {
898    flags |= createNoWindow;
899  }
900
901  memset(out_process, 0, sizeof(*out_process));
902
903  if (subprocess_option_inherit_environment !=
904      (options & subprocess_option_inherit_environment)) {
905    flags |= createUnicodeEnvironment;
906
907    if (SUBPROCESS_NULL == environment) {
908      used_environment = empty_environment;
909    } else {
910      // We always end with two null terminators. MultiByteToWideChar includes
911      // each environment string's null terminator, so start with one extra.
912      len = 1;
913
914      for (i = 0; environment[i]; i++) {
915        wide_len = MultiByteToWideChar(codePageUtf8, mbErrInvalidChars,
916                                       environment[i], -1, SUBPROCESS_NULL, 0);
917        if (0 == wide_len) {
918          result = subprocess_error_from_windows_error(GetLastError());
919          if (subprocess_error_unknown == result) {
920            result = subprocess_error_spawn;
921          }
922          goto cleanup;
923        }
924
925        len += SUBPROCESS_CAST(subprocess_size_t, wide_len);
926      }
927
928      if (((SUBPROCESS_CAST(subprocess_size_t, -1)) /
929           sizeof(subprocess_wchar_t)) < len) {
930        result = subprocess_error_no_memory;
931        goto cleanup;
932      }
933
934      used_environment = SUBPROCESS_CAST(
935          subprocess_wchar_t *, _alloca(len * sizeof(subprocess_wchar_t)));
936      if (!used_environment) {
937        result = subprocess_error_no_memory;
938        goto cleanup;
939      }
940
941      // Re-use len for the insertion position.
942      len = 0;
943
944      for (i = 0; environment[i]; i++) {
945        wide_len = MultiByteToWideChar(codePageUtf8, mbErrInvalidChars,
946                                       environment[i], -1, SUBPROCESS_NULL, 0);
947        if (0 == wide_len) {
948          result = subprocess_error_from_windows_error(GetLastError());
949          if (subprocess_error_unknown == result) {
950            result = subprocess_error_spawn;
951          }
952          goto cleanup;
953        }
954
955        if (0 == MultiByteToWideChar(codePageUtf8, mbErrInvalidChars,
956                                     environment[i], -1,
957                                     &used_environment[len], wide_len)) {
958          result = subprocess_error_from_windows_error(GetLastError());
959          if (subprocess_error_unknown == result) {
960            result = subprocess_error_spawn;
961          }
962          goto cleanup;
963        }
964
965        len += SUBPROCESS_CAST(subprocess_size_t, wide_len);
966      }
967
968      // End with the second null terminator.
969      used_environment[len++] = 0;
970    }
971  } else {
972    if (SUBPROCESS_NULL != environment) {
973      return subprocess_error_invalid_environment;
974    }
975  }
976
977  if (!CreatePipe(&rd, &wr, SUBPROCESS_PTR_CAST(LPSECURITY_ATTRIBUTES, &saAttr),
978                  0)) {
979    result = subprocess_error_pipe;
980    goto cleanup;
981  }
982
983  if (!SetHandleInformation(wr, handleFlagInherit, 0)) {
984    result = subprocess_error_pipe;
985    goto cleanup;
986  }
987
988  fd = _open_osfhandle(SUBPROCESS_PTR_CAST(subprocess_intptr_t, wr), 0);
989  if (-1 == fd) {
990    result = subprocess_error_pipe;
991    goto cleanup;
992  }
993  wr = SUBPROCESS_NULL;
994
995  out_process->stdin_file = _fdopen(fd, "wb");
996  if (SUBPROCESS_NULL == out_process->stdin_file) {
997    _close(fd);
998    goto cleanup;
999  }
1000
1001  startInfo.hStdInput = rd;
1002  rd = SUBPROCESS_NULL;
1003
1004  if (options & subprocess_option_enable_async) {
1005    if (subprocess_create_named_pipe_helper(&rd, &wr)) {
1006      result = subprocess_error_pipe;
1007      goto cleanup;
1008    }
1009  } else {
1010    if (!CreatePipe(&rd, &wr,
1011                    SUBPROCESS_PTR_CAST(LPSECURITY_ATTRIBUTES, &saAttr), 0)) {
1012      result = subprocess_error_pipe;
1013      goto cleanup;
1014    }
1015  }
1016
1017  if (!SetHandleInformation(rd, handleFlagInherit, 0)) {
1018    result = subprocess_error_pipe;
1019    goto cleanup;
1020  }
1021
1022  fd = _open_osfhandle(SUBPROCESS_PTR_CAST(subprocess_intptr_t, rd), 0);
1023  if (-1 == fd) {
1024    result = subprocess_error_pipe;
1025    goto cleanup;
1026  }
1027  rd = SUBPROCESS_NULL;
1028
1029  out_process->stdout_file = _fdopen(fd, "rb");
1030  if (SUBPROCESS_NULL == out_process->stdout_file) {
1031    _close(fd);
1032    goto cleanup;
1033  }
1034
1035  startInfo.hStdOutput = wr;
1036  wr = SUBPROCESS_NULL;
1037
1038  if (subprocess_option_combined_stdout_stderr ==
1039      (options & subprocess_option_combined_stdout_stderr)) {
1040    out_process->stderr_file = out_process->stdout_file;
1041    startInfo.hStdError = startInfo.hStdOutput;
1042  } else {
1043    if (options & subprocess_option_enable_async) {
1044      if (subprocess_create_named_pipe_helper(&rd, &wr)) {
1045        result = subprocess_error_pipe;
1046        goto cleanup;
1047      }
1048    } else {
1049      if (!CreatePipe(&rd, &wr,
1050                      SUBPROCESS_PTR_CAST(LPSECURITY_ATTRIBUTES, &saAttr), 0)) {
1051        result = subprocess_error_pipe;
1052        goto cleanup;
1053      }
1054    }
1055
1056    if (!SetHandleInformation(rd, handleFlagInherit, 0)) {
1057      result = subprocess_error_pipe;
1058      goto cleanup;
1059    }
1060
1061    fd = _open_osfhandle(SUBPROCESS_PTR_CAST(subprocess_intptr_t, rd), 0);
1062    if (-1 == fd) {
1063      result = subprocess_error_pipe;
1064      goto cleanup;
1065    }
1066    rd = SUBPROCESS_NULL;
1067
1068    out_process->stderr_file = _fdopen(fd, "rb");
1069    if (SUBPROCESS_NULL == out_process->stderr_file) {
1070      _close(fd);
1071      goto cleanup;
1072    }
1073
1074    startInfo.hStdError = wr;
1075    wr = SUBPROCESS_NULL;
1076  }
1077
1078  if (options & subprocess_option_enable_async) {
1079    out_process->hEventOutput =
1080        CreateEventA(SUBPROCESS_PTR_CAST(LPSECURITY_ATTRIBUTES, &saAttr), 1, 1,
1081                     SUBPROCESS_NULL);
1082    out_process->hEventError =
1083        CreateEventA(SUBPROCESS_PTR_CAST(LPSECURITY_ATTRIBUTES, &saAttr), 1, 1,
1084                     SUBPROCESS_NULL);
1085    if (!out_process->hEventOutput || !out_process->hEventError) {
1086      result = subprocess_error_from_windows_error(GetLastError());
1087      if (subprocess_error_unknown == result) {
1088        result = subprocess_error_no_memory;
1089      }
1090      goto cleanup;
1091    }
1092  } else {
1093    out_process->hEventOutput = SUBPROCESS_NULL;
1094    out_process->hEventError = SUBPROCESS_NULL;
1095  }
1096
1097  // Combine commandLine together into a single string
1098  len = 0;
1099  for (i = 0; commandLine[i]; i++) {
1100    // for the trailing \0
1101    len++;
1102
1103    // Quote the argument if it has a space in it
1104    need_quoting = strpbrk(commandLine[i], "\t\v ") != SUBPROCESS_NULL ||
1105                   commandLine[i][0] == SUBPROCESS_NULL;
1106    if (need_quoting)
1107      len += 2;
1108
1109    bs_run = 0;
1110    for (j = 0; '\0' != commandLine[i][j]; j++) {
1111      len++;
1112
1113      if ('\\' == commandLine[i][j]) {
1114        bs_run++;
1115      } else {
1116        if ('"' == commandLine[i][j]) {
1117          // Duplicate the preceding run and escape the quote.
1118          len += bs_run + 1;
1119        }
1120        bs_run = 0;
1121      }
1122    }
1123
1124    if (need_quoting) {
1125      // Duplicate trailing slashes before the generated closing quote.
1126      len += bs_run;
1127    }
1128  }
1129
1130  commandLineCombined = SUBPROCESS_CAST(char *, _alloca(len));
1131
1132  if (!commandLineCombined) {
1133    result = subprocess_error_no_memory;
1134    goto cleanup;
1135  }
1136
1137  // Gonna re-use len to store the write index into commandLineCombined
1138  len = 0;
1139
1140  for (i = 0; commandLine[i]; i++) {
1141    if (0 != i) {
1142      commandLineCombined[len++] = ' ';
1143    }
1144
1145    need_quoting = strpbrk(commandLine[i], "\t\v ") != SUBPROCESS_NULL ||
1146                   commandLine[i][0] == SUBPROCESS_NULL;
1147    if (need_quoting) {
1148      commandLineCombined[len++] = '"';
1149    }
1150
1151    for (j = 0; '\0' != commandLine[i][j];) {
1152      bs_run = 0;
1153      while ('\\' == commandLine[i][j]) {
1154        bs_run++;
1155        j++;
1156      }
1157
1158      if ('"' == commandLine[i][j]) {
1159        // 2n + 1 slashes preserve n slashes and escape the quote.
1160        bs_run = (bs_run * 2) + 1;
1161      } else if ('\0' == commandLine[i][j] && need_quoting) {
1162        // 2n slashes preserve n slashes before the closing quote.
1163        bs_run *= 2;
1164      }
1165
1166      while (bs_run > 0) {
1167        commandLineCombined[len++] = '\\';
1168        bs_run--;
1169      }
1170
1171      if ('\0' != commandLine[i][j]) {
1172        commandLineCombined[len++] = commandLine[i][j++];
1173      }
1174    }
1175    if (need_quoting) {
1176      commandLineCombined[len++] = '"';
1177    }
1178  }
1179
1180  commandLineCombined[len] = '\0';
1181
1182  wide_len = MultiByteToWideChar(codePageUtf8, mbErrInvalidChars,
1183                                 commandLineCombined, -1, SUBPROCESS_NULL, 0);
1184  if (0 == wide_len) {
1185    result = subprocess_error_from_windows_error(GetLastError());
1186    if (subprocess_error_unknown == result) {
1187      result = subprocess_error_spawn;
1188    }
1189    goto cleanup;
1190  }
1191
1192  commandLineCombinedWide = SUBPROCESS_CAST(
1193      subprocess_wchar_t *,
1194      _alloca(SUBPROCESS_CAST(subprocess_size_t, wide_len) *
1195              sizeof(subprocess_wchar_t)));
1196  if (!commandLineCombinedWide) {
1197    result = subprocess_error_no_memory;
1198    goto cleanup;
1199  }
1200
1201  if (0 == MultiByteToWideChar(codePageUtf8, mbErrInvalidChars,
1202                               commandLineCombined, -1,
1203                               commandLineCombinedWide, wide_len)) {
1204    result = subprocess_error_from_windows_error(GetLastError());
1205    if (subprocess_error_unknown == result) {
1206      result = subprocess_error_spawn;
1207    }
1208    goto cleanup;
1209  }
1210
1211  if (process_cwd) {
1212    wide_len = MultiByteToWideChar(codePageUtf8, mbErrInvalidChars, process_cwd,
1213                                   -1, SUBPROCESS_NULL, 0);
1214    if (0 == wide_len) {
1215      result = subprocess_error_from_windows_error(GetLastError());
1216      if (subprocess_error_unknown == result) {
1217        result = subprocess_error_spawn;
1218      }
1219      goto cleanup;
1220    }
1221
1222    process_cwd_wide = SUBPROCESS_CAST(
1223        subprocess_wchar_t *,
1224        _alloca(SUBPROCESS_CAST(subprocess_size_t, wide_len) *
1225                sizeof(subprocess_wchar_t)));
1226    if (!process_cwd_wide) {
1227      result = subprocess_error_no_memory;
1228      goto cleanup;
1229    }
1230
1231    if (0 == MultiByteToWideChar(codePageUtf8, mbErrInvalidChars, process_cwd,
1232                                 -1, process_cwd_wide, wide_len)) {
1233      result = subprocess_error_from_windows_error(GetLastError());
1234      if (subprocess_error_unknown == result) {
1235        result = subprocess_error_spawn;
1236      }
1237      goto cleanup;
1238    }
1239  }
1240
1241  /* Restrict inheritance to this subprocess's standard streams. Without a
1242     handle list, concurrent subprocess_create calls can inherit each other's
1243     temporarily-inheritable child pipe handles. */
1244  inherited_handles[inherited_handle_count++] = startInfo.hStdInput;
1245  inherited_handles[inherited_handle_count++] = startInfo.hStdOutput;
1246  if (startInfo.hStdError != startInfo.hStdOutput) {
1247    inherited_handles[inherited_handle_count++] = startInfo.hStdError;
1248  }
1249
1250  InitializeProcThreadAttributeList(SUBPROCESS_NULL, 1, 0,
1251                                    &attribute_list_size);
1252  if (0 == attribute_list_size) {
1253    result = subprocess_error_spawn;
1254    goto cleanup;
1255  }
1256
1257  attribute_list = SUBPROCESS_PTR_CAST(LPPROC_THREAD_ATTRIBUTE_LIST,
1258                                       _alloca(attribute_list_size));
1259  if (!attribute_list || !InitializeProcThreadAttributeList(
1260                             attribute_list, 1, 0, &attribute_list_size)) {
1261    result = subprocess_error_spawn;
1262    goto cleanup;
1263  }
1264  attribute_list_initialized = 1;
1265
1266  if (!UpdateProcThreadAttribute(
1267          attribute_list, 0, procThreadAttributeHandleList, inherited_handles,
1268          inherited_handle_count * sizeof(inherited_handles[0]),
1269          SUBPROCESS_NULL, SUBPROCESS_NULL)) {
1270    result = subprocess_error_spawn;
1271    goto cleanup;
1272  }
1273
1274  startInfoEx.startupInfo = startInfo;
1275  startInfoEx.startupInfo.cb = sizeof(startInfoEx);
1276  startInfoEx.attributeList = attribute_list;
1277  flags |= extendedStartupInfoPresent;
1278
1279  if (!CreateProcessW(
1280          SUBPROCESS_NULL,
1281          commandLineCombinedWide, // command line
1282          SUBPROCESS_NULL,         // process security attributes
1283          SUBPROCESS_NULL,         // primary thread security attributes
1284          1,                       // handles are inherited
1285          flags,                   // creation flags
1286          used_environment,        // used environment
1287          process_cwd_wide,        // use specified current directory
1288          SUBPROCESS_PTR_CAST(LPSTARTUPINFOW,
1289                              &startInfoEx), // STARTUPINFOEX pointer
1290          SUBPROCESS_PTR_CAST(LPPROCESS_INFORMATION, &processInfo))) {
1291    result = subprocess_error_from_windows_error(GetLastError());
1292    if (subprocess_error_unknown == result) {
1293      result = subprocess_error_spawn;
1294    }
1295    goto cleanup;
1296  }
1297
1298  DeleteProcThreadAttributeList(attribute_list);
1299  attribute_list_initialized = 0;
1300
1301  out_process->hProcess = processInfo.hProcess;
1302  processInfo.hProcess = SUBPROCESS_NULL;
1303
1304  out_process->hStdInput = startInfo.hStdInput;
1305  startInfo.hStdInput = SUBPROCESS_NULL;
1306
1307  // We don't need the handle of the primary thread in the called process.
1308  CloseHandle(processInfo.hThread);
1309  processInfo.hThread = SUBPROCESS_NULL;
1310
1311  if (SUBPROCESS_NULL != startInfo.hStdOutput) {
1312    CloseHandle(startInfo.hStdOutput);
1313
1314    if (startInfo.hStdError != startInfo.hStdOutput) {
1315      CloseHandle(startInfo.hStdError);
1316    }
1317
1318    startInfo.hStdOutput = SUBPROCESS_NULL;
1319    startInfo.hStdError = SUBPROCESS_NULL;
1320  }
1321
1322  out_process->alive = 1;
1323  out_process->no_wait = async_no_wait;
1324
1325  return 0;
1326
1327cleanup:
1328  last_error = GetLastError();
1329
1330  if (attribute_list_initialized) {
1331    DeleteProcThreadAttributeList(attribute_list);
1332  }
1333
1334  if (subprocess_error_unknown == result) {
1335    result = subprocess_error_from_windows_error(last_error);
1336  }
1337
1338  if (out_process->stdin_file) {
1339    fclose(out_process->stdin_file);
1340    out_process->stdin_file = SUBPROCESS_NULL;
1341  }
1342
1343  if (out_process->stdout_file) {
1344    fclose(out_process->stdout_file);
1345    if (out_process->stderr_file &&
1346        (out_process->stdout_file != out_process->stderr_file)) {
1347      fclose(out_process->stderr_file);
1348    }
1349    out_process->stdout_file = SUBPROCESS_NULL;
1350    out_process->stderr_file = SUBPROCESS_NULL;
1351  }
1352
1353  subprocess_close_handle(&rd);
1354  subprocess_close_handle(&wr);
1355  subprocess_close_handle(&startInfo.hStdInput);
1356
1357  if (startInfo.hStdOutput == startInfo.hStdError) {
1358    subprocess_close_handle(&startInfo.hStdOutput);
1359    startInfo.hStdError = SUBPROCESS_NULL;
1360  } else {
1361    subprocess_close_handle(&startInfo.hStdOutput);
1362    subprocess_close_handle(&startInfo.hStdError);
1363  }
1364
1365  subprocess_close_handle(&out_process->hEventOutput);
1366  subprocess_close_handle(&out_process->hEventError);
1367  subprocess_close_handle(&processInfo.hThread);
1368  subprocess_close_handle(&processInfo.hProcess);
1369
1370  SetLastError(last_error);
1371
1372  return result;
1373#else
1374  int stdinfd[2] = {-1, -1};
1375  int stdoutfd[2] = {-1, -1};
1376  int stderrfd[2] = {-1, -1};
1377  int fd, fd_flags;
1378  int async_no_wait;
1379  int result = subprocess_error_unknown;
1380  int saved_errno = 0;
1381  pid_t child = 0;
1382  extern char **environ;
1383  char *const empty_environment[1] = {SUBPROCESS_NULL};
1384  char *const *used_environment;
1385#if SUBPROCESS_SPAWN_VIA_FORK
1386  /* Pipe used to relay the child's exec() errno back to the parent. */
1387  int exec_errfd[2] = {-1, -1};
1388#else
1389  int actions_created = 0;
1390  int posix_error;
1391  posix_spawn_file_actions_t actions;
1392#endif
1393
1394  async_no_wait = subprocess_option_enable_async_no_wait ==
1395                  (options & subprocess_option_enable_async_no_wait);
1396
1397  if (async_no_wait && (subprocess_option_enable_async !=
1398                        (options & subprocess_option_enable_async))) {
1399    errno = EINVAL;
1400    return subprocess_error_invalid_options;
1401  }
1402
1403  if (subprocess_option_inherit_environment ==
1404      (options & subprocess_option_inherit_environment)) {
1405    if (SUBPROCESS_NULL != environment) {
1406      errno = EINVAL;
1407      return subprocess_error_invalid_environment;
1408    }
1409  }
1410
1411  memset(out_process, 0, sizeof(*out_process));
1412
1413  if (0 != subprocess_pipe_cloexec(stdinfd)) {
1414    saved_errno = errno;
1415    result = subprocess_error_pipe;
1416    goto cleanup;
1417  }
1418
1419  if (0 != subprocess_pipe_cloexec(stdoutfd)) {
1420    saved_errno = errno;
1421    result = subprocess_error_pipe;
1422    goto cleanup;
1423  }
1424
1425  if (subprocess_option_combined_stdout_stderr !=
1426      (options & subprocess_option_combined_stdout_stderr)) {
1427    if (0 != subprocess_pipe_cloexec(stderrfd)) {
1428      saved_errno = errno;
1429      result = subprocess_error_pipe;
1430      goto cleanup;
1431    }
1432  }
1433
1434  if (environment) {
1435#ifdef __clang__
1436#pragma clang diagnostic push
1437#pragma clang diagnostic ignored "-Wcast-qual"
1438#pragma clang diagnostic ignored "-Wold-style-cast"
1439#endif
1440    used_environment = SUBPROCESS_CONST_CAST(char *const *, environment);
1441#ifdef __clang__
1442#pragma clang diagnostic pop
1443#endif
1444  } else if (subprocess_option_inherit_environment ==
1445             (options & subprocess_option_inherit_environment)) {
1446    used_environment = environ;
1447  } else {
1448    used_environment = empty_environment;
1449  }
1450
1451#if SUBPROCESS_SPAWN_VIA_FORK
1452  /* fork()+exec() instead of posix_spawn, so the child can chdir() first.
1453     exec_errfd[1] is close-on-exec: a successful exec closes it and the parent
1454     reads EOF; a failed exec writes errno through it before _exit. */
1455  if (0 != pipe(exec_errfd)) {
1456    saved_errno = errno;
1457    result = subprocess_error_pipe;
1458    goto cleanup;
1459  }
1460
1461  if (-1 == fcntl(exec_errfd[1], F_SETFD, FD_CLOEXEC)) {
1462    saved_errno = errno;
1463    result = subprocess_error_spawn;
1464    goto cleanup;
1465  }
1466
1467  child = fork();
1468
1469  if (child < 0) {
1470    saved_errno = errno;
1471    result = subprocess_error_spawn;
1472    goto cleanup;
1473  }
1474
1475  if (0 == child) {
1476    /* Child. Everything below must stay async-signal-safe: after fork() in a
1477       threaded process only such functions may be called before exec. */
1478    int child_errno;
1479
1480    close(exec_errfd[0]);
1481
1482    if ((-1 == dup2(stdinfd[0], STDIN_FILENO)) ||
1483        (-1 == dup2(stdoutfd[1], STDOUT_FILENO))) {
1484      goto child_failed;
1485    }
1486
1487    if (subprocess_option_combined_stdout_stderr ==
1488        (options & subprocess_option_combined_stdout_stderr)) {
1489      if (-1 == dup2(STDOUT_FILENO, STDERR_FILENO)) {
1490        goto child_failed;
1491      }
1492    } else {
1493      if (-1 == dup2(stderrfd[1], STDERR_FILENO)) {
1494        goto child_failed;
1495      }
1496    }
1497
1498    /* The originals are only closed once they have been duplicated, so that a
1499       pipe end that already sits on 0, 1 or 2 is not closed out from under us. */
1500    if (stdinfd[0] > STDERR_FILENO) {
1501      close(stdinfd[0]);
1502    }
1503    if (stdinfd[1] > STDERR_FILENO) {
1504      close(stdinfd[1]);
1505    }
1506    if (stdoutfd[0] > STDERR_FILENO) {
1507      close(stdoutfd[0]);
1508    }
1509    if (stdoutfd[1] > STDERR_FILENO) {
1510      close(stdoutfd[1]);
1511    }
1512    if (stderrfd[0] > STDERR_FILENO) {
1513      close(stderrfd[0]);
1514    }
1515    if (stderrfd[1] > STDERR_FILENO) {
1516      close(stderrfd[1]);
1517    }
1518
1519    if (process_cwd && (0 != chdir(process_cwd))) {
1520      goto child_failed;
1521    }
1522
1523#ifdef __clang__
1524#pragma clang diagnostic push
1525#pragma clang diagnostic ignored "-Wcast-qual"
1526#pragma clang diagnostic ignored "-Wold-style-cast"
1527#endif
1528    if (subprocess_option_search_user_path ==
1529        (options & subprocess_option_search_user_path)) {
1530      execvpe(commandLine[0],
1531              SUBPROCESS_CONST_CAST(char *const *, commandLine),
1532              SUBPROCESS_CONST_CAST(char *const *, used_environment));
1533    } else {
1534      execve(commandLine[0],
1535             SUBPROCESS_CONST_CAST(char *const *, commandLine),
1536             SUBPROCESS_CONST_CAST(char *const *, used_environment));
1537    }
1538#ifdef __clang__
1539#pragma clang diagnostic pop
1540#endif
1541
1542  child_failed:
1543    child_errno = errno;
1544    /* Nothing useful can be done if this write fails; the parent then sees EOF
1545       and reports success, exactly as posix_spawn would without exec reporting. */
1546    (void)!write(exec_errfd[1], &child_errno, sizeof(child_errno));
1547    /* 127 is what POSIX requires posix_spawn's child to exit with when exec
1548       fails, so both implementations look the same to a caller. */
1549    _exit(127);
1550  }
1551
1552  /* Parent. */
1553  close(exec_errfd[1]);
1554  exec_errfd[1] = -1;
1555
1556  {
1557    int child_errno = 0;
1558    ssize_t bytes_read;
1559
1560    do {
1561      bytes_read = read(exec_errfd[0], &child_errno, sizeof(child_errno));
1562    } while ((-1 == bytes_read) && (EINTR == errno));
1563
1564    close(exec_errfd[0]);
1565    exec_errfd[0] = -1;
1566
1567    if (bytes_read == (ssize_t)sizeof(child_errno)) {
1568      /* exec failed in the child. Reap it and surface the reason. */
1569      while ((-1 == waitpid(child, SUBPROCESS_NULL, 0)) && (EINTR == errno)) {
1570      }
1571      child = 0;
1572      saved_errno = child_errno;
1573      result = subprocess_error_from_errno(child_errno);
1574      if (subprocess_error_unknown == result) {
1575        result = subprocess_error_spawn;
1576      }
1577      goto cleanup;
1578    }
1579  }
1580#else
1581  posix_error = posix_spawn_file_actions_init(&actions);
1582  if (0 != posix_error) {
1583    saved_errno = posix_error;
1584    result = subprocess_error_from_errno(posix_error);
1585    if (subprocess_error_unknown == result) {
1586      result = subprocess_error_spawn;
1587    }
1588    goto cleanup;
1589  }
1590  actions_created = 1;
1591
1592  // Set working directory
1593  if (process_cwd) {
1594#if SUBPROCESS_ADDCHDIR_IS_POSIX
1595    posix_error = posix_spawn_file_actions_addchdir(&actions, process_cwd);
1596#elif !SUBPROCESS_HAVE_CWD
1597    posix_error = ENOSYS;
1598#else
1599#if defined(__APPLE__) && defined(__clang__)
1600#pragma clang diagnostic push
1601#pragma clang diagnostic ignored "-Wdeprecated-declarations"
1602#endif
1603    posix_error = posix_spawn_file_actions_addchdir_np(&actions, process_cwd);
1604#if defined(__APPLE__) && defined(__clang__)
1605#pragma clang diagnostic pop
1606#endif
1607#endif
1608    if (0 != posix_error) {
1609      saved_errno = posix_error;
1610      result = subprocess_error_from_errno(posix_error);
1611      if (subprocess_error_unknown == result) {
1612        result = subprocess_error_spawn;
1613      }
1614      goto cleanup;
1615    }
1616  }
1617
1618  // Close the stdin write end
1619  posix_error = posix_spawn_file_actions_addclose(&actions, stdinfd[1]);
1620  if (0 != posix_error) {
1621    saved_errno = posix_error;
1622    result = subprocess_error_from_errno(posix_error);
1623    if (subprocess_error_unknown == result) {
1624      result = subprocess_error_spawn;
1625    }
1626    goto cleanup;
1627  }
1628
1629  // Map the read end to stdin
1630  posix_error =
1631      posix_spawn_file_actions_adddup2(&actions, stdinfd[0], STDIN_FILENO);
1632  if (0 != posix_error) {
1633    saved_errno = posix_error;
1634    result = subprocess_error_from_errno(posix_error);
1635    if (subprocess_error_unknown == result) {
1636      result = subprocess_error_spawn;
1637    }
1638    goto cleanup;
1639  }
1640
1641  // Close the stdout read end
1642  posix_error = posix_spawn_file_actions_addclose(&actions, stdoutfd[0]);
1643  if (0 != posix_error) {
1644    saved_errno = posix_error;
1645    result = subprocess_error_from_errno(posix_error);
1646    if (subprocess_error_unknown == result) {
1647      result = subprocess_error_spawn;
1648    }
1649    goto cleanup;
1650  }
1651
1652  // Map the write end to stdout
1653  posix_error =
1654      posix_spawn_file_actions_adddup2(&actions, stdoutfd[1], STDOUT_FILENO);
1655  if (0 != posix_error) {
1656    saved_errno = posix_error;
1657    result = subprocess_error_from_errno(posix_error);
1658    if (subprocess_error_unknown == result) {
1659      result = subprocess_error_spawn;
1660    }
1661    goto cleanup;
1662  }
1663
1664  if (subprocess_option_combined_stdout_stderr ==
1665      (options & subprocess_option_combined_stdout_stderr)) {
1666    posix_error = posix_spawn_file_actions_adddup2(&actions, STDOUT_FILENO,
1667                                                   STDERR_FILENO);
1668    if (0 != posix_error) {
1669      saved_errno = posix_error;
1670      result = subprocess_error_from_errno(posix_error);
1671      if (subprocess_error_unknown == result) {
1672        result = subprocess_error_spawn;
1673      }
1674      goto cleanup;
1675    }
1676  } else {
1677    // Close the stderr read end
1678    posix_error = posix_spawn_file_actions_addclose(&actions, stderrfd[0]);
1679    if (0 != posix_error) {
1680      saved_errno = posix_error;
1681      result = subprocess_error_from_errno(posix_error);
1682      if (subprocess_error_unknown == result) {
1683        result = subprocess_error_spawn;
1684      }
1685      goto cleanup;
1686    }
1687    // Map the write end to stdout
1688    posix_error = posix_spawn_file_actions_adddup2(&actions, stderrfd[1],
1689                                                   STDERR_FILENO);
1690    if (0 != posix_error) {
1691      saved_errno = posix_error;
1692      result = subprocess_error_from_errno(posix_error);
1693      if (subprocess_error_unknown == result) {
1694        result = subprocess_error_spawn;
1695      }
1696      goto cleanup;
1697    }
1698  }
1699
1700#ifdef __clang__
1701#pragma clang diagnostic push
1702#pragma clang diagnostic ignored "-Wcast-qual"
1703#pragma clang diagnostic ignored "-Wold-style-cast"
1704#endif
1705  if (subprocess_option_search_user_path ==
1706      (options & subprocess_option_search_user_path)) {
1707    posix_error = posix_spawnp(&child, commandLine[0], &actions,
1708                               SUBPROCESS_NULL,
1709                               SUBPROCESS_CONST_CAST(char *const *, commandLine),
1710                               used_environment);
1711    if (0 != posix_error) {
1712      saved_errno = posix_error;
1713      result = subprocess_error_from_errno(posix_error);
1714      if (subprocess_error_unknown == result) {
1715        result = subprocess_error_spawn;
1716      }
1717      goto cleanup;
1718    }
1719  } else {
1720#if !SUBPROCESS_SPAWN_REPORTS_EXEC_ERRORS
1721    /* posix_spawn cannot tell us the exec failed, so check up front */
1722    if (0 != access(commandLine[0], X_OK)) {
1723      saved_errno = errno;
1724      result = subprocess_error_from_errno(saved_errno);
1725      if (subprocess_error_unknown == result) {
1726        result = subprocess_error_spawn;
1727      }
1728      goto cleanup;
1729    }
1730#endif
1731    posix_error = posix_spawn(&child, commandLine[0], &actions,
1732                              SUBPROCESS_NULL,
1733                              SUBPROCESS_CONST_CAST(char *const *, commandLine),
1734                              used_environment);
1735    if (0 != posix_error) {
1736      saved_errno = posix_error;
1737      result = subprocess_error_from_errno(posix_error);
1738      if (subprocess_error_unknown == result) {
1739        result = subprocess_error_spawn;
1740      }
1741      goto cleanup;
1742    }
1743  }
1744#ifdef __clang__
1745#pragma clang diagnostic pop
1746#endif
1747#endif /* SUBPROCESS_SPAWN_VIA_FORK */
1748
1749  // Close the stdin read end
1750  close(stdinfd[0]);
1751  stdinfd[0] = -1;
1752  // Store the stdin write end
1753  out_process->stdin_file = fdopen(stdinfd[1], "wb");
1754  if (SUBPROCESS_NULL == out_process->stdin_file) {
1755    saved_errno = errno;
1756    result = subprocess_error_from_errno(saved_errno);
1757    goto cleanup;
1758  }
1759  stdinfd[1] = -1;
1760
1761  // Close the stdout write end
1762  close(stdoutfd[1]);
1763  stdoutfd[1] = -1;
1764  // Store the stdout read end
1765  out_process->stdout_file = fdopen(stdoutfd[0], "rb");
1766  if (SUBPROCESS_NULL == out_process->stdout_file) {
1767    saved_errno = errno;
1768    result = subprocess_error_from_errno(saved_errno);
1769    goto cleanup;
1770  }
1771  stdoutfd[0] = -1;
1772
1773  // Set non blocking if we are async and asked not to wait.
1774  if (async_no_wait) {
1775    fd = fileno(out_process->stdout_file);
1776    fd_flags = fcntl(fd, F_GETFL, 0);
1777    fcntl(fd, F_SETFL, fd_flags | O_NONBLOCK);
1778  }
1779
1780  if (subprocess_option_combined_stdout_stderr ==
1781      (options & subprocess_option_combined_stdout_stderr)) {
1782    out_process->stderr_file = out_process->stdout_file;
1783  } else {
1784    // Close the stderr write end
1785    close(stderrfd[1]);
1786    stderrfd[1] = -1;
1787    // Store the stderr read end
1788    out_process->stderr_file = fdopen(stderrfd[0], "rb");
1789    if (SUBPROCESS_NULL == out_process->stderr_file) {
1790      saved_errno = errno;
1791      result = subprocess_error_from_errno(saved_errno);
1792      goto cleanup;
1793    }
1794    stderrfd[0] = -1;
1795
1796    // Set non blocking if we are async and asked not to wait.
1797    if (async_no_wait) {
1798      fd = fileno(out_process->stderr_file);
1799      fd_flags = fcntl(fd, F_GETFL, 0);
1800      fcntl(fd, F_SETFL, fd_flags | O_NONBLOCK);
1801    }
1802  }
1803
1804  // Store the child's pid
1805  out_process->child = child;
1806  child = 0;
1807
1808  out_process->alive = 1;
1809  out_process->no_wait = async_no_wait;
1810
1811  result = 0;
1812
1813cleanup:
1814  if ((0 != result) && (0 == saved_errno)) {
1815    saved_errno = errno;
1816  }
1817
1818  if ((subprocess_error_unknown == result) && (0 != saved_errno)) {
1819    result = subprocess_error_from_errno(saved_errno);
1820  }
1821
1822#if SUBPROCESS_SPAWN_VIA_FORK
1823  if (-1 != exec_errfd[0]) {
1824    close(exec_errfd[0]);
1825    exec_errfd[0] = -1;
1826  }
1827
1828  if (-1 != exec_errfd[1]) {
1829    close(exec_errfd[1]);
1830    exec_errfd[1] = -1;
1831  }
1832#else
1833  if (actions_created) {
1834    posix_spawn_file_actions_destroy(&actions);
1835  }
1836#endif
1837
1838  if (0 != result) {
1839    if (child) {
1840      kill(child, 9);
1841      waitpid(child, SUBPROCESS_NULL, 0);
1842    }
1843
1844    if (out_process->stdin_file) {
1845      fclose(out_process->stdin_file);
1846      out_process->stdin_file = SUBPROCESS_NULL;
1847    }
1848
1849    if (out_process->stdout_file) {
1850      fclose(out_process->stdout_file);
1851      if (out_process->stderr_file &&
1852          (out_process->stdout_file != out_process->stderr_file)) {
1853        fclose(out_process->stderr_file);
1854      }
1855      out_process->stdout_file = SUBPROCESS_NULL;
1856      out_process->stderr_file = SUBPROCESS_NULL;
1857    }
1858  }
1859
1860  if (-1 != stdinfd[0]) {
1861    close(stdinfd[0]);
1862  }
1863  if (-1 != stdinfd[1]) {
1864    close(stdinfd[1]);
1865  }
1866  if (-1 != stdoutfd[0]) {
1867    close(stdoutfd[0]);
1868  }
1869  if (-1 != stdoutfd[1]) {
1870    close(stdoutfd[1]);
1871  }
1872  if (-1 != stderrfd[0]) {
1873    close(stderrfd[0]);
1874  }
1875  if (-1 != stderrfd[1]) {
1876    close(stderrfd[1]);
1877  }
1878
1879  if ((0 != result) && (0 != saved_errno)) {
1880    errno = saved_errno;
1881  }
1882
1883  return result;
1884#endif
1885}
1886
1887FILE *subprocess_stdin(const struct subprocess_s *const process) {
1888  return process->stdin_file;
1889}
1890
1891FILE *subprocess_stdout(const struct subprocess_s *const process) {
1892  return process->stdout_file;
1893}
1894
1895FILE *subprocess_stderr(const struct subprocess_s *const process) {
1896  if (process->stdout_file != process->stderr_file) {
1897    return process->stderr_file;
1898  } else {
1899    return SUBPROCESS_NULL;
1900  }
1901}
1902
1903int subprocess_join(struct subprocess_s *const process,
1904                    int *const out_return_code) {
1905#if defined(_WIN32)
1906  const unsigned long infinite = 0xFFFFFFFF;
1907
1908  if (process->stdin_file) {
1909    fclose(process->stdin_file);
1910    process->stdin_file = SUBPROCESS_NULL;
1911  }
1912
1913  if (process->hStdInput) {
1914    CloseHandle(process->hStdInput);
1915    process->hStdInput = SUBPROCESS_NULL;
1916  }
1917
1918  WaitForSingleObject(process->hProcess, infinite);
1919
1920  if (out_return_code) {
1921    if (!GetExitCodeProcess(
1922            process->hProcess,
1923            SUBPROCESS_PTR_CAST(unsigned long *, out_return_code))) {
1924      return -1;
1925    }
1926  }
1927
1928  process->alive = 0;
1929
1930  return 0;
1931#else
1932  int status;
1933
1934  if (process->stdin_file) {
1935    fclose(process->stdin_file);
1936    process->stdin_file = SUBPROCESS_NULL;
1937  }
1938
1939  if (process->child) {
1940    if (process->child != waitpid(process->child, &status, 0)) {
1941      return -1;
1942    }
1943
1944    process->child = 0;
1945
1946    if (WIFEXITED(status)) {
1947      process->return_status = WEXITSTATUS(status);
1948    } else {
1949      process->return_status = EXIT_FAILURE;
1950    }
1951
1952    process->alive = 0;
1953  }
1954
1955  if (out_return_code) {
1956    *out_return_code = process->return_status;
1957  }
1958
1959  return 0;
1960#endif
1961}
1962
1963int subprocess_destroy(struct subprocess_s *const process) {
1964  if (process->stdin_file) {
1965    fclose(process->stdin_file);
1966    process->stdin_file = SUBPROCESS_NULL;
1967  }
1968
1969  if (process->stdout_file) {
1970    fclose(process->stdout_file);
1971
1972    if (process->stdout_file != process->stderr_file) {
1973      fclose(process->stderr_file);
1974    }
1975
1976    process->stdout_file = SUBPROCESS_NULL;
1977    process->stderr_file = SUBPROCESS_NULL;
1978  }
1979
1980#if defined(_WIN32)
1981  if (process->hProcess) {
1982    CloseHandle(process->hProcess);
1983    process->hProcess = SUBPROCESS_NULL;
1984
1985    if (process->hStdInput) {
1986      CloseHandle(process->hStdInput);
1987    }
1988
1989    if (process->hEventOutput) {
1990      CloseHandle(process->hEventOutput);
1991    }
1992
1993    if (process->hEventError) {
1994      CloseHandle(process->hEventError);
1995    }
1996  }
1997#endif
1998
1999  return 0;
2000}
2001
2002int subprocess_terminate(struct subprocess_s *const process) {
2003#if defined(_WIN32)
2004  unsigned int killed_process_exit_code;
2005  int success_terminate;
2006  int windows_call_result;
2007
2008  killed_process_exit_code = 99;
2009  windows_call_result =
2010      TerminateProcess(process->hProcess, killed_process_exit_code);
2011  success_terminate = (windows_call_result == 0) ? 1 : 0;
2012  return success_terminate;
2013#else
2014  int result;
2015  result = kill(process->child, 9);
2016  return result;
2017#endif
2018}
2019
2020unsigned subprocess_read_stdout(struct subprocess_s *const process,
2021                                char *const buffer, unsigned size) {
2022#if defined(_WIN32)
2023  void *handle;
2024  unsigned long bytes_available = 0;
2025  unsigned long bytes_read = 0;
2026  struct subprocess_overlapped_s overlapped = {0, 0, {{0, 0}}, SUBPROCESS_NULL};
2027  overlapped.hEvent = process->hEventOutput;
2028
2029  handle = SUBPROCESS_PTR_CAST(void *,
2030                               _get_osfhandle(_fileno(process->stdout_file)));
2031
2032  if (process->no_wait) {
2033    if (!PeekNamedPipe(handle, SUBPROCESS_NULL, 0, SUBPROCESS_NULL,
2034                       &bytes_available, SUBPROCESS_NULL)) {
2035      return 0;
2036    }
2037
2038    if (0 == bytes_available) {
2039      return 0;
2040    }
2041  }
2042
2043  if (!ReadFile(handle, buffer, size, &bytes_read,
2044                SUBPROCESS_PTR_CAST(LPOVERLAPPED, &overlapped))) {
2045    const unsigned long errorIoPending = 997;
2046    unsigned long error = GetLastError();
2047
2048    // Means we've got an async read!
2049    if (error == errorIoPending) {
2050      const uintptr_t statusPending = 0x00000103;
2051
2052      const int wait = statusPending == overlapped.Internal;
2053
2054      if (!GetOverlappedResult(handle,
2055                               SUBPROCESS_PTR_CAST(LPOVERLAPPED, &overlapped),
2056                               &bytes_read, wait)) {
2057        const unsigned long errorHandleEOF = 38;
2058        const unsigned long errorBrokenPipe = 109;
2059        const unsigned long errorIoIncomplete = 996;
2060        error = GetLastError();
2061
2062        if ((errorHandleEOF != error) && (errorBrokenPipe != error) &&
2063            (errorIoIncomplete != error)) {
2064          return 0;
2065        }
2066      }
2067    }
2068  }
2069
2070  return SUBPROCESS_CAST(unsigned, bytes_read);
2071#else
2072  const int fd = fileno(process->stdout_file);
2073  const ssize_t bytes_read = read(fd, buffer, size);
2074
2075  if (bytes_read < 0) {
2076    return 0;
2077  }
2078
2079  return SUBPROCESS_CAST(unsigned, bytes_read);
2080#endif
2081}
2082
2083unsigned subprocess_read_stderr(struct subprocess_s *const process,
2084                                char *const buffer, unsigned size) {
2085#if defined(_WIN32)
2086  void *handle;
2087  unsigned long bytes_available = 0;
2088  unsigned long bytes_read = 0;
2089  struct subprocess_overlapped_s overlapped = {0, 0, {{0, 0}}, SUBPROCESS_NULL};
2090  overlapped.hEvent = process->hEventError;
2091
2092  handle = SUBPROCESS_PTR_CAST(void *,
2093                               _get_osfhandle(_fileno(process->stderr_file)));
2094
2095  if (process->no_wait) {
2096    if (!PeekNamedPipe(handle, SUBPROCESS_NULL, 0, SUBPROCESS_NULL,
2097                       &bytes_available, SUBPROCESS_NULL)) {
2098      return 0;
2099    }
2100
2101    if (0 == bytes_available) {
2102      return 0;
2103    }
2104  }
2105
2106  if (!ReadFile(handle, buffer, size, &bytes_read,
2107                SUBPROCESS_PTR_CAST(LPOVERLAPPED, &overlapped))) {
2108    const unsigned long errorIoPending = 997;
2109    unsigned long error = GetLastError();
2110
2111    // Means we've got an async read!
2112    if (error == errorIoPending) {
2113      if (!GetOverlappedResult(handle,
2114                               SUBPROCESS_PTR_CAST(LPOVERLAPPED, &overlapped),
2115                               &bytes_read, 1)) {
2116        const unsigned long errorIoIncomplete = 996;
2117        const unsigned long errorHandleEOF = 38;
2118        error = GetLastError();
2119
2120        if ((error != errorIoIncomplete) && (error != errorHandleEOF)) {
2121          return 0;
2122        }
2123      }
2124    }
2125  }
2126
2127  return SUBPROCESS_CAST(unsigned, bytes_read);
2128#else
2129  const int fd = fileno(process->stderr_file);
2130  const ssize_t bytes_read = read(fd, buffer, size);
2131
2132  if (bytes_read < 0) {
2133    return 0;
2134  }
2135
2136  return SUBPROCESS_CAST(unsigned, bytes_read);
2137#endif
2138}
2139
2140int subprocess_alive(struct subprocess_s *const process) {
2141  int is_alive = SUBPROCESS_CAST(int, process->alive);
2142
2143  if (!is_alive) {
2144    return 0;
2145  }
2146#if defined(_WIN32)
2147  {
2148    const unsigned long zero = 0x0;
2149    const unsigned long wait_object_0 = 0x00000000L;
2150
2151    is_alive = wait_object_0 != WaitForSingleObject(process->hProcess, zero);
2152  }
2153#else
2154  {
2155    int status;
2156    is_alive = 0 == waitpid(process->child, &status, WNOHANG);
2157
2158    // If the process was successfully waited on we need to cleanup now.
2159    if (!is_alive) {
2160      if (WIFEXITED(status)) {
2161        process->return_status = WEXITSTATUS(status);
2162      } else {
2163        process->return_status = EXIT_FAILURE;
2164      }
2165
2166      // Since we've already successfully waited on the process, we need to wipe
2167      // the child now.
2168      process->child = 0;
2169
2170      if (subprocess_join(process, SUBPROCESS_NULL)) {
2171        return -1;
2172      }
2173    }
2174  }
2175#endif
2176
2177  if (!is_alive) {
2178    process->alive = 0;
2179  }
2180
2181  return is_alive;
2182}
2183
2184#if defined(__clang__)
2185#if __has_warning("-Wunsafe-buffer-usage")
2186#pragma clang diagnostic pop
2187#endif
2188#endif
2189
2190#if defined(__cplusplus)
2191} // extern "C"
2192#endif
2193
2194#endif /* SHEREDOM_SUBPROCESS_H_INCLUDED */