| /* Auxiliary functions for the creation of subprocesses on Windows. | |
| Copyright (C) 2024-2026 Free Software Foundation, Inc. | |
| Written by Bruno Haible <bruno@clisp.org>, 2024. | |
| This file is free software: you can redistribute it and/or modify | |
| it under the terms of the GNU Lesser General Public License as | |
| published by the Free Software Foundation; either version 2.1 of the | |
| License, or (at your option) any later version. | |
| This file is distributed in the hope that it will be useful, | |
| but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| GNU Lesser General Public License for more details. | |
| You should have received a copy of the GNU Lesser General Public License | |
| along with this program. If not, see <https://www.gnu.org/licenses/>. */ | |
| #include <config.h> | |
| /* Specification. */ | |
| #include "windows-path.h" | |
| #include <errno.h> | |
| #include <stdlib.h> | |
| #include <string.h> | |
| #include <unistd.h> | |
| char * | |
| extended_PATH (const char * const *dll_dirs) | |
| { | |
| /* Create a string PATH=(dll_dirs[0];dll_dirs[1];...;)old_PATH */ | |
| const char *old_PATH = getenv ("PATH"); | |
| if (old_PATH == NULL) | |
| old_PATH = ""; | |
| size_t size; | |
| { | |
| size = 5; | |
| for (size_t i = 0; dll_dirs[i] != NULL; i++) | |
| size += strlen (dll_dirs[i]) + 1; | |
| size += strlen (old_PATH) + 1; | |
| } | |
| char *new_PATH = (char *) malloc (size); | |
| if (new_PATH == NULL) | |
| return NULL; | |
| { | |
| char *p = new_PATH; | |
| { | |
| memcpy (p, "PATH=", 5); | |
| p += 5; | |
| } | |
| for (size_t i = 0; dll_dirs[i] != NULL; i++) | |
| { | |
| size_t l = strlen (dll_dirs[i]); | |
| memcpy (p, dll_dirs[i], l); | |
| p += l; | |
| #if defined _WIN32 && !defined __CYGWIN__ | |
| *p++ = ';'; | |
| #else | |
| *p++ = ':'; | |
| #endif | |
| } | |
| { | |
| size_t l = strlen (old_PATH); | |
| memcpy (p, old_PATH, l); | |
| p += l; | |
| *p = '\0'; | |
| } | |
| } | |
| return new_PATH; | |
| } | |
| char ** | |
| extended_environ (const char * const *dll_dirs) | |
| { | |
| char *child_PATH = extended_PATH (dll_dirs); | |
| if (child_PATH == NULL) | |
| return NULL; | |
| /* Create a shallow copy of environ, adding the child_PATH and removing | |
| the original "PATH=..." string. | |
| This is a bit hairy, because we don't have a lock that would prevent | |
| other threads from making modifications in ENVP. So, just make sure | |
| we don't crash; but if other threads are making modifications, part | |
| of the result may be wrong. */ | |
| char **envp; | |
| #if defined _WIN32 && !defined __CYGWIN__ | |
| envp = _environ; | |
| #else | |
| envp = environ; | |
| #endif | |
| retry: | |
| { | |
| /* Guess the size of the needed block of memory. | |
| The guess will be exact if other threads don't make modifications. */ | |
| size_t size = 0; | |
| { | |
| char *p; | |
| for (char **ep = envp; (p = *ep) != NULL; ep++) | |
| if (strncmp (p, "PATH=", 5) != 0) | |
| size += 1; | |
| } | |
| char **new_environ = (char **) malloc ((1 + size + 1) * sizeof (char *)); | |
| if (new_environ == NULL) | |
| { | |
| free (child_PATH); | |
| errno = ENOMEM; | |
| return NULL; | |
| } | |
| char **nep = new_environ; | |
| *nep++ = child_PATH; | |
| { | |
| size_t i = 0; | |
| char *p; | |
| for (char **ep = envp; (p = *ep) != NULL; ep++) | |
| if (strncmp (p, "PATH=", 5) != 0) | |
| { | |
| if (i == size) | |
| { | |
| /* Other threads did modifications. Restart. */ | |
| free (new_environ); | |
| goto retry; | |
| } | |
| *nep++ = p; | |
| i += 1; | |
| } | |
| if (i < size) | |
| { | |
| /* Other threads did modifications. Restart. */ | |
| free (new_environ); | |
| goto retry; | |
| } | |
| } | |
| *nep = NULL; | |
| return new_environ; | |
| } | |
| } |