From 29cd838eab01ed7110f3ccb2e8c6a35c8a31dbcc Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Thu, 11 Apr 2024 10:21:29 +0200 Subject: Adding upstream version 1:0.1.9998svn3589+dfsg. Signed-off-by: Daniel Baumann --- src/kmk/kmkbuiltin/common-env-and-cwd-opt.c | 516 ++++++++++++++++++++++++++++ 1 file changed, 516 insertions(+) create mode 100644 src/kmk/kmkbuiltin/common-env-and-cwd-opt.c (limited to 'src/kmk/kmkbuiltin/common-env-and-cwd-opt.c') diff --git a/src/kmk/kmkbuiltin/common-env-and-cwd-opt.c b/src/kmk/kmkbuiltin/common-env-and-cwd-opt.c new file mode 100644 index 0000000..a7f6d58 --- /dev/null +++ b/src/kmk/kmkbuiltin/common-env-and-cwd-opt.c @@ -0,0 +1,516 @@ +/* $Id: common-env-and-cwd-opt.c 3332 2020-04-19 23:08:16Z bird $ */ +/** @file + * kMk Builtin command - Commmon environment and CWD option handling code. + */ + +/* + * Copyright (c) 2007-2016 knut st. osmundsen + * + * This file is part of kBuild. + * + * kBuild is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * kBuild 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with kBuild. If not, see + * + */ + +/******************************************************************************* +* Header Files * +*******************************************************************************/ +#include "config.h" +#include +#include +#include +#include + +#include "kmkbuiltin.h" +#include "err.h" + + +/** The environment variable compare function. + * We must use case insensitive compare on windows (Path vs PATH). */ +#ifdef KBUILD_OS_WINDOWS +# define KSUBMIT_ENV_NCMP _strnicmp +#else +# define KSUBMIT_ENV_NCMP strncmp +#endif + + +/** + * Duplicates a read-only enviornment vector. + * + * @returns The duplicate enviornment. + * @param pCtx The built-in command context. + * @param papszEnv The read-only vector. + * @param cEnvVars The number of variables. + * @param pcAllocatedEnvVars The allocated papszEnv size. This is zero on + * input and non-zero on successful return. + * @param cVerbosity The verbosity level. + */ +static char **kBuiltinOptEnvDuplicate(PKMKBUILTINCTX pCtx, char **papszEnv, unsigned cEnvVars, unsigned *pcAllocatedEnvVars, + int cVerbosity) +{ + unsigned cAllocatedEnvVars = (cEnvVars + 2 + 0xf) & ~(unsigned)0xf; + char **papszEnvNew = malloc(cAllocatedEnvVars * sizeof(papszEnvNew[0])); + assert(*pcAllocatedEnvVars == 0); + if (papszEnvNew) + { + unsigned i; + for (i = 0; i < cEnvVars; i++) + { + papszEnvNew[i] = strdup(papszEnv[i]); + if (!papszEnvNew) + { + while (i-- > 0) + free(papszEnvNew[i]); + free(papszEnvNew); + errx(pCtx, 1, "out of memory for duplicating environment variables!", i); + return NULL; + } + } + papszEnvNew[i] = NULL; + *pcAllocatedEnvVars = cAllocatedEnvVars; + } + else + errx(pCtx, 1, "out of memory for duplicating environment vector!"); + return papszEnvNew; +} + + +/** + * Common worker for kBuiltinOptEnvSet and kBuiltinOptEnvAppendPrepend that adds + * a new variable to the environment. + * + * @returns 0 on success, non-zero exit code on error. + * @param pCtx The built-in command context. + * @param papszEnv The environment vector. + * @param pcEnvVars Pointer to the variable holding the number of + * environment variables held by @a papszEnv. + * @param pcAllocatedEnvVars Pointer to the variable holding max size of the + * environment vector. + * @param cVerbosity The verbosity level. + * @param pszValue The var=value string to apply. + */ +static int kBuiltinOptEnvAddVar(PKMKBUILTINCTX pCtx, char ***ppapszEnv, unsigned *pcEnvVars, unsigned *pcAllocatedEnvVars, + int cVerbosity, const char *pszValue) +{ + /* Append new variable. We probably need to resize the vector. */ + char **papszEnv = *ppapszEnv; + unsigned cEnvVars = *pcEnvVars; + if ((cEnvVars + 2) > *pcAllocatedEnvVars) + { + *pcAllocatedEnvVars = (cEnvVars + 2 + 0xf) & ~(unsigned)0xf; + papszEnv = (char **)realloc(papszEnv, *pcAllocatedEnvVars * sizeof(papszEnv[0])); + if (!papszEnv) + return errx(pCtx, 1, "out of memory growing environment vector!"); + *ppapszEnv = papszEnv; + } + papszEnv[cEnvVars] = strdup(pszValue); + if (!papszEnv[cEnvVars]) + return errx(pCtx, 1, "out of memory adding environment variable!"); + papszEnv[++cEnvVars] = NULL; + *pcEnvVars = cEnvVars; + if (cVerbosity > 0) + warnx(pCtx, "added '%s'", papszEnv[cEnvVars - 1]); + return 0; +} + + +/** + * Common worker for kBuiltinOptEnvSet and kBuiltinOptEnvAppendPrepend that + * remove duplicates. + * + * @returns 0 on success, non-zero exit code on error. + * @param pCtx The built-in command context. + * @param papszEnv The environment vector. + * @param cEnvVars Number of environment variables. + * @param cVerbosity The verbosity level. + * @param pszValue The var=value string to apply. + * @param cchVar The length of the variable part of @a pszValue. + * @param iEnvVar Where to start searching after. + */ +static int kBuiltinOptEnvRemoveDuplicates(PKMKBUILTINCTX pCtx, char **papszEnv, unsigned cEnvVars, int cVerbosity, + const char *pszValue, size_t cchVar, unsigned iEnvVar) +{ + for (iEnvVar++; iEnvVar < cEnvVars; iEnvVar++) + if ( KSUBMIT_ENV_NCMP(papszEnv[iEnvVar], pszValue, cchVar) == 0 + && papszEnv[iEnvVar][cchVar] == '=') + { + if (cVerbosity > 0) + warnx(pCtx, "removing duplicate '%s'", papszEnv[iEnvVar]); + free(papszEnv[iEnvVar]); + cEnvVars--; + if (iEnvVar != cEnvVars) + papszEnv[iEnvVar] = papszEnv[cEnvVars]; + papszEnv[cEnvVars] = NULL; + iEnvVar--; + } + return 0; +} + + +/** + * Handles the --set var=value option. + * + * @returns 0 on success, non-zero exit code on error. + * @param pCtx The built-in command context. + * @param ppapszEnv The environment vector pointer. + * @param pcEnvVars Pointer to the variable holding the number of + * environment variables held by @a papszEnv. + * @param pcAllocatedEnvVars Pointer to the variable holding max size of the + * environment vector. + * @param cVerbosity The verbosity level. + * @param pszValue The var=value string to apply. + */ +int kBuiltinOptEnvSet(PKMKBUILTINCTX pCtx, char ***ppapszEnv, unsigned *pcEnvVars, unsigned *pcAllocatedEnvVars, + int cVerbosity, const char *pszValue) +{ + const char *pszEqual = strchr(pszValue, '='); + if (pszEqual) + { + char **papszEnv = *ppapszEnv; + unsigned iEnvVar; + unsigned cEnvVars = *pcEnvVars; + size_t const cchVar = pszEqual - pszValue; + + if (!*pcAllocatedEnvVars) + { + papszEnv = kBuiltinOptEnvDuplicate(pCtx, papszEnv, cEnvVars, pcAllocatedEnvVars, cVerbosity); + if (!papszEnv) + return errx(pCtx, 1, "out of memory duplicating enviornment (setenv)!"); + *ppapszEnv = papszEnv; + } + + for (iEnvVar = 0; iEnvVar < cEnvVars; iEnvVar++) + { + char *pszCur = papszEnv[iEnvVar]; + if ( KSUBMIT_ENV_NCMP(pszCur, pszValue, cchVar) == 0 + && pszCur[cchVar] == '=') + { + if (cVerbosity > 0) + warnx(pCtx, "replacing '%s' with '%s'", papszEnv[iEnvVar], pszValue); + free(papszEnv[iEnvVar]); + papszEnv[iEnvVar] = strdup(pszValue); + if (!papszEnv[iEnvVar]) + return errx(pCtx, 1, "out of memory for modified environment variable!"); + + return kBuiltinOptEnvRemoveDuplicates(pCtx, papszEnv, cEnvVars, cVerbosity, pszValue, cchVar, iEnvVar); + } + } + return kBuiltinOptEnvAddVar(pCtx, ppapszEnv, pcEnvVars, pcAllocatedEnvVars, cVerbosity, pszValue); + } + return errx(pCtx, 1, "Missing '=': -E %s", pszValue); +} + + +/** + * Common worker for kBuiltinOptEnvAppend and kBuiltinOptEnvPrepend. + * + * @returns 0 on success, non-zero exit code on error. + * @param pCtx The built-in command context. + * @param ppapszEnv The environment vector pointer. + * @param pcEnvVars Pointer to the variable holding the number of + * environment variables held by @a papszEnv. + * @param pcAllocatedEnvVars Pointer to the variable holding max size of the + * environment vector. + * @param cVerbosity The verbosity level. + * @param pszValue The var=value string to apply. + */ +static int kBuiltinOptEnvAppendPrepend(PKMKBUILTINCTX pCtx, char ***ppapszEnv, unsigned *pcEnvVars, unsigned *pcAllocatedEnvVars, + int cVerbosity, const char *pszValue, int fAppend) +{ + const char *pszEqual = strchr(pszValue, '='); + if (pszEqual) + { + char **papszEnv = *ppapszEnv; + unsigned iEnvVar; + unsigned cEnvVars = *pcEnvVars; + size_t const cchVar = pszEqual - pszValue; + + if (!*pcAllocatedEnvVars) + { + papszEnv = kBuiltinOptEnvDuplicate(pCtx, papszEnv, cEnvVars, pcAllocatedEnvVars, cVerbosity); + if (!papszEnv) + return errx(pCtx, 1, "out of memory duplicating environment (append)!"); + *ppapszEnv = papszEnv; + } + + for (iEnvVar = 0; iEnvVar < cEnvVars; iEnvVar++) + { + char *pszCur = papszEnv[iEnvVar]; + if ( KSUBMIT_ENV_NCMP(pszCur, pszValue, cchVar) == 0 + && pszCur[cchVar] == '=') + { + size_t cchOldValue = strlen(pszCur) - cchVar - 1; + size_t cchNewValue = strlen(pszValue) - cchVar - 1; + char *pszNew = malloc(cchVar + 1 + cchOldValue + cchNewValue + 1); + if (!pszNew) + return errx(pCtx, 1, "out of memory appending to environment variable!"); + if (fAppend) + { + memcpy(pszNew, pszCur, cchVar + 1 + cchOldValue); + memcpy(&pszNew[cchVar + 1 + cchOldValue], &pszValue[cchVar + 1], cchNewValue + 1); + } + else + { + memcpy(pszNew, pszCur, cchVar + 1); /* preserve variable name case */ + memcpy(&pszNew[cchVar + 1], &pszValue[cchVar + 1], cchNewValue); + memcpy(&pszNew[cchVar + 1 + cchNewValue], &pszCur[cchVar + 1], cchOldValue + 1); + } + + if (cVerbosity > 0) + warnx(pCtx, "replacing '%s' with '%s'", pszCur, pszNew); + free(pszCur); + papszEnv[iEnvVar] = pszNew; + + return kBuiltinOptEnvRemoveDuplicates(pCtx, papszEnv, cEnvVars, cVerbosity, pszValue, cchVar, iEnvVar); + } + } + return kBuiltinOptEnvAddVar(pCtx, ppapszEnv, pcEnvVars, pcAllocatedEnvVars, cVerbosity, pszValue); + } + return errx(pCtx, 1, "Missing '=': -%c %s", fAppend ? 'A' : 'D', pszValue); +} + + +/** + * Handles the --append var=value option. + * + * @returns 0 on success, non-zero exit code on error. + * @param pCtx The built-in command context. + * @param ppapszEnv The environment vector pointer. + * @param pcEnvVars Pointer to the variable holding the number of + * environment variables held by @a papszEnv. + * @param pcAllocatedEnvVars Pointer to the variable holding max size of the + * environment vector. + * @param cVerbosity The verbosity level. + * @param pszValue The var=value string to apply. + */ +int kBuiltinOptEnvAppend(PKMKBUILTINCTX pCtx, char ***ppapszEnv, unsigned *pcEnvVars, unsigned *pcAllocatedEnvVars, + int cVerbosity, const char *pszValue) +{ + return kBuiltinOptEnvAppendPrepend(pCtx, ppapszEnv, pcEnvVars, pcAllocatedEnvVars, cVerbosity, pszValue, 1 /*fAppend*/); +} + + +/** + * Handles the --prepend var=value option. + * + * @returns 0 on success, non-zero exit code on error. + * @param pCtx The built-in command context. + * @param ppapszEnv The environment vector pointer. + * @param pcEnvVars Pointer to the variable holding the number of + * environment variables held by @a papszEnv. + * @param pcAllocatedEnvVars Pointer to the variable holding max size of the + * environment vector. + * @param cVerbosity The verbosity level. + * @param pszValue The var=value string to apply. + */ +int kBuiltinOptEnvPrepend(PKMKBUILTINCTX pCtx, char ***ppapszEnv, unsigned *pcEnvVars, unsigned *pcAllocatedEnvVars, + int cVerbosity, const char *pszValue) +{ + return kBuiltinOptEnvAppendPrepend(pCtx, ppapszEnv, pcEnvVars, pcAllocatedEnvVars, cVerbosity, pszValue, 0 /*fAppend*/); +} + + +/** + * Handles the --unset var option. + * + * @returns 0 on success, non-zero exit code on error. + * @param pCtx The built-in command context. + * @param ppapszEnv The environment vector pointer. + * @param pcEnvVars Pointer to the variable holding the number of + * environment variables held by @a papszEnv. + * @param pcAllocatedEnvVars Pointer to the size of the vector allocation. + * The size is zero when read-only (CRT, GNU make) + * environment. + * @param cVerbosity The verbosity level. + * @param pszVarToRemove The name of the variable to remove. + */ +int kBuiltinOptEnvUnset(PKMKBUILTINCTX pCtx, char ***ppapszEnv, unsigned *pcEnvVars, unsigned *pcAllocatedEnvVars, + int cVerbosity, const char *pszVarToRemove) +{ + if (strchr(pszVarToRemove, '=') == NULL) + { + char **papszEnv = *ppapszEnv; + unsigned cRemoved = 0; + size_t const cchVar = strlen(pszVarToRemove); + unsigned cEnvVars = *pcEnvVars; + unsigned iEnvVar; + + for (iEnvVar = 0; iEnvVar < cEnvVars; iEnvVar++) + if ( KSUBMIT_ENV_NCMP(papszEnv[iEnvVar], pszVarToRemove, cchVar) == 0 + && papszEnv[iEnvVar][cchVar] == '=') + { + if (cVerbosity > 0) + warnx(pCtx, !cRemoved ? "removing '%s'" : "removing duplicate '%s'", papszEnv[iEnvVar]); + + if (!*pcAllocatedEnvVars) + { + papszEnv = kBuiltinOptEnvDuplicate(pCtx, papszEnv, cEnvVars, pcAllocatedEnvVars, cVerbosity); + if (!papszEnv) + return errx(pCtx, 1, "out of memory duplicating environment (unset)!"); + *ppapszEnv = papszEnv; + } + + free(papszEnv[iEnvVar]); + cEnvVars--; + if (iEnvVar != cEnvVars) + papszEnv[iEnvVar] = papszEnv[cEnvVars]; + papszEnv[cEnvVars] = NULL; + cRemoved++; + iEnvVar--; + } + *pcEnvVars = cEnvVars; + + if (cVerbosity > 0 && !cRemoved) + warnx(pCtx, "not found '%s'", pszVarToRemove); + } + else + return errx(pCtx, 1, "Found invalid variable name character '=' in: -U %s", pszVarToRemove); + return 0; +} + + +/** + * Handles the --zap-env & --ignore-environment options. + * + * @returns 0 on success, non-zero exit code on error. + * @param pCtx The built-in command context. + * @param ppapszEnv The environment vector pointer. + * @param pcEnvVars Pointer to the variable holding the number of + * environment variables held by @a papszEnv. + * @param pcAllocatedEnvVars Pointer to the size of the vector allocation. + * The size is zero when read-only (CRT, GNU make) + * environment. + * @param cVerbosity The verbosity level. + */ +int kBuiltinOptEnvZap(PKMKBUILTINCTX pCtx, char ***ppapszEnv, unsigned *pcEnvVars, unsigned *pcAllocatedEnvVars, int cVerbosity) +{ + if (*pcAllocatedEnvVars > 0) + { + char **papszEnv = *ppapszEnv; + unsigned i = *pcEnvVars; + while (i-- > 0) + { + free(papszEnv[i]); + papszEnv[i] = NULL; + } + } + else + { + char **papszEnv = calloc(4, sizeof(char *)); + if (!papszEnv) + return err(pCtx, 1, "out of memory!"); + *ppapszEnv = papszEnv; + *pcAllocatedEnvVars = 4; + } + *pcEnvVars = 0; + return 0; +} + + +/** + * Cleans up afterwards, if necessary. + * + * @param ppapszEnv The environment vector pointer. + * @param cEnvVars The number of variables in the vector. + * @param pcAllocatedEnvVars Pointer to the size of the vector allocation. + * The size is zero when read-only (CRT, GNU make) + * environment. + */ +void kBuiltinOptEnvCleanup(char ***ppapszEnv, unsigned cEnvVars, unsigned *pcAllocatedEnvVars) +{ + char **papszEnv = *ppapszEnv; + *ppapszEnv = NULL; + if (*pcAllocatedEnvVars > 0) + { + *pcAllocatedEnvVars = 0; + while (cEnvVars-- > 0) + { + free(papszEnv[cEnvVars]); + papszEnv[cEnvVars] = NULL; + } + free(papszEnv); + } +} + + +/** + * Handles the --chdir dir option. + * + * @returns 0 on success, non-zero exit code on error. + * @param pCtx The built-in command context. + * @param pszCwd The CWD buffer. Contains current CWD on input, + * modified by @a pszValue on output. + * @param cbCwdBuf The size of the CWD buffer. + * @param pszValue The --chdir value to apply. + */ +int kBuiltinOptChDir(PKMKBUILTINCTX pCtx, char *pszCwd, size_t cbCwdBuf, const char *pszValue) +{ + size_t cchNewCwd = strlen(pszValue); + size_t offDst; + if (cchNewCwd) + { +#ifdef HAVE_DOS_PATHS + if (*pszValue == '/' || *pszValue == '\\') + { + if (pszValue[1] == '/' || pszValue[1] == '\\') + offDst = 0; /* UNC */ + else if (pszCwd[1] == ':' && isalpha(pszCwd[0])) + offDst = 2; /* Take drive letter from CWD. */ + else + return errx(pCtx, 1, "UNC relative CWD not implemented: cur='%s' new='%s'", pszCwd, pszValue); + } + else if ( pszValue[1] == ':' + && isalpha(pszValue[0])) + { + if (pszValue[2] == '/'|| pszValue[2] == '\\') + offDst = 0; /* DOS style absolute path. */ + else if ( pszCwd[1] == ':' + && tolower(pszCwd[0]) == tolower(pszValue[0]) ) + { + pszValue += 2; /* Same drive as CWD, append drive relative path from value. */ + cchNewCwd -= 2; + offDst = strlen(pszCwd); + } + else + { + /* Get current CWD on the specified drive and append value. */ + int iDrive = tolower(pszValue[0]) - 'a' + 1; + if (!_getdcwd(iDrive, pszCwd, cbCwdBuf)) + return err(pCtx, 1, "_getdcwd(%d,,) failed", iDrive); + pszValue += 2; + cchNewCwd -= 2; + } + } +#else + if (*pszValue == '/') + offDst = 0; +#endif + else + offDst = strlen(pszCwd); /* Relative path, append to the existing CWD value. */ + + /* Do the copying. */ +#ifdef HAVE_DOS_PATHS + if (offDst > 0 && pszCwd[offDst - 1] != '/' && pszCwd[offDst - 1] != '\\') +#else + if (offDst > 0 && pszCwd[offDst - 1] != '/') +#endif + pszCwd[offDst++] = '/'; + if (offDst + cchNewCwd >= cbCwdBuf) + return errx(pCtx, 1, "Too long CWD: %*.*s%s", offDst, offDst, pszCwd, pszValue); + memcpy(&pszCwd[offDst], pszValue, cchNewCwd + 1); + } + /* else: relative, no change - quitely ignore. */ + return 0; +} + -- cgit v1.2.3