VirtualBox

source: kBuild/trunk/src/kmk/kmkbuiltin/common-env-and-cwd-opt.c@ 3154

Last change on this file since 3154 was 3133, checked in by bird, 7 years ago

kBuiltinOptEnvAppendPrepend: Fixed heap corruption found by Vitali. Cleaned up confused code around it.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 14.2 KB
Line 
1/* $Id: common-env-and-cwd-opt.c 3133 2018-02-23 21:33:27Z bird $ */
2/** @file
3 * kMk Builtin command - Commmon environment and CWD option handling code.
4 */
5
6/*
7 * Copyright (c) 2007-2016 knut st. osmundsen <[email protected]>
8 *
9 * This file is part of kBuild.
10 *
11 * kBuild is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 3 of the License, or
14 * (at your option) any later version.
15 *
16 * kBuild is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with kBuild. If not, see <http://www.gnu.org/licenses/>
23 *
24 */
25
26/*******************************************************************************
27* Header Files *
28*******************************************************************************/
29#include "config.h"
30#include <stdio.h>
31#include <stdlib.h>
32#include <string.h>
33
34#include "kmkbuiltin.h"
35#include "err.h"
36
37
38/** The environment variable compare function.
39 * We must use case insensitive compare on windows (Path vs PATH). */
40#ifdef KBUILD_OS_WINDOWS
41# define KSUBMIT_ENV_NCMP _strnicmp
42#else
43# define KSUBMIT_ENV_NCMP strncmp
44#endif
45
46
47/**
48 * Common worker for kBuiltinOptEnvSet and kBuiltinOptEnvAppendPrepend that adds
49 * a new variable to the environment.
50 *
51 * @returns 0 on success, non-zero exit code on error.
52 * @param papszEnv The environment vector.
53 * @param pcEnvVars Pointer to the variable holding the number of
54 * environment variables held by @a papszEnv.
55 * @param pcAllocatedEnvVars Pointer to the variable holding max size of the
56 * environment vector.
57 * @param cVerbosity The verbosity level.
58 * @param pszValue The var=value string to apply.
59 */
60static int kBuiltinOptEnvAddVar(char ***ppapszEnv, unsigned *pcEnvVars, unsigned *pcAllocatedEnvVars,
61 int cVerbosity, const char *pszValue)
62{
63 /* Append new variable. We probably need to resize the vector. */
64 char **papszEnv = *ppapszEnv;
65 unsigned cEnvVars = *pcEnvVars;
66 if ((cEnvVars + 2) > *pcAllocatedEnvVars)
67 {
68 *pcAllocatedEnvVars = (cEnvVars + 2 + 0xf) & ~(unsigned)0xf;
69 papszEnv = (char **)realloc(papszEnv, *pcAllocatedEnvVars * sizeof(papszEnv[0]));
70 if (!papszEnv)
71 return errx(1, "out of memory!");
72 *ppapszEnv = papszEnv;
73 }
74 papszEnv[cEnvVars] = strdup(pszValue);
75 if (!papszEnv[cEnvVars])
76 return errx(1, "out of memory!");
77 papszEnv[++cEnvVars] = NULL;
78 *pcEnvVars = cEnvVars;
79 if (cVerbosity > 0)
80 warnx("added '%s'", papszEnv[cEnvVars - 1]);
81 return 0;
82}
83
84
85/**
86 * Common worker for kBuiltinOptEnvSet and kBuiltinOptEnvAppendPrepend that
87 * remove duplicates.
88 *
89 * @returns 0 on success, non-zero exit code on error.
90 * @param papszEnv The environment vector.
91 * @param cEnvVars Number of environment variables.
92 * @param cVerbosity The verbosity level.
93 * @param pszValue The var=value string to apply.
94 * @param cchVar The length of the variable part of @a pszValue.
95 * @param iEnvVar Where to start searching after.
96 */
97static int kBuiltinOptEnvRemoveDuplicates(char **papszEnv, unsigned cEnvVars, int cVerbosity,
98 const char *pszValue, size_t cchVar, unsigned iEnvVar)
99{
100 for (iEnvVar++; iEnvVar < cEnvVars; iEnvVar++)
101 if ( KSUBMIT_ENV_NCMP(papszEnv[iEnvVar], pszValue, cchVar) == 0
102 && papszEnv[iEnvVar][cchVar] == '=')
103 {
104 if (cVerbosity > 0)
105 warnx("removing duplicate '%s'", papszEnv[iEnvVar]);
106 free(papszEnv[iEnvVar]);
107 cEnvVars--;
108 if (iEnvVar != cEnvVars)
109 papszEnv[iEnvVar] = papszEnv[cEnvVars];
110 papszEnv[cEnvVars] = NULL;
111 iEnvVar--;
112 }
113 return 0;
114}
115
116
117/**
118 * Handles the --set var=value option.
119 *
120 * @returns 0 on success, non-zero exit code on error.
121 * @param ppapszEnv The environment vector pointer.
122 * @param pcEnvVars Pointer to the variable holding the number of
123 * environment variables held by @a papszEnv.
124 * @param pcAllocatedEnvVars Pointer to the variable holding max size of the
125 * environment vector.
126 * @param cVerbosity The verbosity level.
127 * @param pszValue The var=value string to apply.
128 */
129int kBuiltinOptEnvSet(char ***ppapszEnv, unsigned *pcEnvVars, unsigned *pcAllocatedEnvVars, int cVerbosity, const char *pszValue)
130{
131 const char *pszEqual = strchr(pszValue, '=');
132 if (pszEqual)
133 {
134 char **papszEnv = *ppapszEnv;
135 unsigned iEnvVar;
136 unsigned cEnvVars = *pcEnvVars;
137 size_t const cchVar = pszEqual - pszValue;
138 for (iEnvVar = 0; iEnvVar < cEnvVars; iEnvVar++)
139 {
140 char *pszCur = papszEnv[iEnvVar];
141 if ( KSUBMIT_ENV_NCMP(pszCur, pszValue, cchVar) == 0
142 && pszCur[cchVar] == '=')
143 {
144 if (cVerbosity > 0)
145 warnx("replacing '%s' with '%s'", papszEnv[iEnvVar], pszValue);
146 free(papszEnv[iEnvVar]);
147 papszEnv[iEnvVar] = strdup(pszValue);
148 if (!papszEnv[iEnvVar])
149 return errx(1, "out of memory!");
150
151 return kBuiltinOptEnvRemoveDuplicates(papszEnv, cEnvVars, cVerbosity, pszValue, cchVar, iEnvVar);
152 }
153 }
154 return kBuiltinOptEnvAddVar(ppapszEnv, pcEnvVars, pcAllocatedEnvVars, cVerbosity, pszValue);
155 }
156 return errx(1, "Missing '=': -E %s", pszValue);
157}
158
159
160/**
161 * Common worker for kBuiltinOptEnvAppend and kBuiltinOptEnvPrepend.
162 *
163 * @returns 0 on success, non-zero exit code on error.
164 * @param ppapszEnv The environment vector pointer.
165 * @param pcEnvVars Pointer to the variable holding the number of
166 * environment variables held by @a papszEnv.
167 * @param pcAllocatedEnvVars Pointer to the variable holding max size of the
168 * environment vector.
169 * @param cVerbosity The verbosity level.
170 * @param pszValue The var=value string to apply.
171 */
172static int kBuiltinOptEnvAppendPrepend(char ***ppapszEnv, unsigned *pcEnvVars, unsigned *pcAllocatedEnvVars,
173 int cVerbosity, const char *pszValue, int fAppend)
174{
175 const char *pszEqual = strchr(pszValue, '=');
176 if (pszEqual)
177 {
178 char **papszEnv = *ppapszEnv;
179 unsigned iEnvVar;
180 unsigned cEnvVars = *pcEnvVars;
181 size_t const cchVar = pszEqual - pszValue;
182 for (iEnvVar = 0; iEnvVar < cEnvVars; iEnvVar++)
183 {
184 char *pszCur = papszEnv[iEnvVar];
185 if ( KSUBMIT_ENV_NCMP(pszCur, pszValue, cchVar) == 0
186 && pszCur[cchVar] == '=')
187 {
188 size_t cchOldValue = strlen(pszCur) - cchVar - 1;
189 size_t cchNewValue = strlen(pszValue) - cchVar - 1;
190 char *pszNew = malloc(cchVar + 1 + cchOldValue + cchNewValue + 1);
191 if (!pszNew)
192 return errx(1, "out of memory!");
193 if (fAppend)
194 {
195 memcpy(pszNew, pszCur, cchVar + 1 + cchOldValue);
196 memcpy(&pszNew[cchVar + 1 + cchOldValue], &pszValue[cchVar + 1], cchNewValue + 1);
197 }
198 else
199 {
200 memcpy(pszNew, pszCur, cchVar + 1); /* preserve variable name case */
201 memcpy(&pszNew[cchVar + 1], &pszValue[cchVar + 1], cchNewValue);
202 memcpy(&pszNew[cchVar + 1 + cchNewValue], &pszCur[cchVar + 1], cchOldValue + 1);
203 }
204
205 if (cVerbosity > 0)
206 warnx("replacing '%s' with '%s'", pszCur, pszNew);
207 free(pszCur);
208 papszEnv[iEnvVar] = pszNew;
209
210 return kBuiltinOptEnvRemoveDuplicates(papszEnv, cEnvVars, cVerbosity, pszValue, cchVar, iEnvVar);
211 }
212 }
213 return kBuiltinOptEnvAddVar(ppapszEnv, pcEnvVars, pcAllocatedEnvVars, cVerbosity, pszValue);
214 }
215 return errx(1, "Missing '=': -E %s", pszValue);
216}
217
218
219/**
220 * Handles the --append var=value option.
221 *
222 * @returns 0 on success, non-zero exit code on error.
223 * @param ppapszEnv The environment vector pointer.
224 * @param pcEnvVars Pointer to the variable holding the number of
225 * environment variables held by @a papszEnv.
226 * @param pcAllocatedEnvVars Pointer to the variable holding max size of the
227 * environment vector.
228 * @param cVerbosity The verbosity level.
229 * @param pszValue The var=value string to apply.
230 */
231int kBuiltinOptEnvAppend(char ***ppapszEnv, unsigned *pcEnvVars, unsigned *pcAllocatedEnvVars, int cVerbosity, const char *pszValue)
232{
233 return kBuiltinOptEnvAppendPrepend(ppapszEnv, pcEnvVars, pcAllocatedEnvVars, cVerbosity, pszValue, 1 /*fAppend*/);
234}
235
236
237/**
238 * Handles the --prepend var=value option.
239 *
240 * @returns 0 on success, non-zero exit code on error.
241 * @param ppapszEnv The environment vector pointer.
242 * @param pcEnvVars Pointer to the variable holding the number of
243 * environment variables held by @a papszEnv.
244 * @param pcAllocatedEnvVars Pointer to the variable holding max size of the
245 * environment vector.
246 * @param cVerbosity The verbosity level.
247 * @param pszValue The var=value string to apply.
248 */
249int kBuiltinOptEnvPrepend(char ***ppapszEnv, unsigned *pcEnvVars, unsigned *pcAllocatedEnvVars, int cVerbosity, const char *pszValue)
250{
251 return kBuiltinOptEnvAppendPrepend(ppapszEnv, pcEnvVars, pcAllocatedEnvVars, cVerbosity, pszValue, 0 /*fAppend*/);
252}
253
254
255/**
256 * Handles the --unset var option.
257 *
258 * @returns 0 on success, non-zero exit code on error.
259 * @param papszEnv The environment vector.
260 * @param pcEnvVars Pointer to the variable holding the number of
261 * environment variables held by @a papszEnv.
262 * @param cVerbosity The verbosity level.
263 * @param pszVarToRemove The name of the variable to remove.
264 */
265int kBuiltinOptEnvUnset(char **papszEnv, unsigned *pcEnvVars, int cVerbosity, const char *pszVarToRemove)
266{
267 if (strchr(pszVarToRemove, '=') == NULL)
268 {
269 unsigned cRemoved = 0;
270 size_t const cchVar = strlen(pszVarToRemove);
271 unsigned cEnvVars = *pcEnvVars;
272 unsigned iEnvVar;
273
274 for (iEnvVar = 0; iEnvVar < cEnvVars; iEnvVar++)
275 if ( KSUBMIT_ENV_NCMP(papszEnv[iEnvVar], pszVarToRemove, cchVar) == 0
276 && papszEnv[iEnvVar][cchVar] == '=')
277 {
278 if (cVerbosity > 0)
279 warnx(!cRemoved ? "removing '%s'" : "removing duplicate '%s'", papszEnv[iEnvVar]);
280 free(papszEnv[iEnvVar]);
281 cEnvVars--;
282 if (iEnvVar != cEnvVars)
283 papszEnv[iEnvVar] = papszEnv[cEnvVars];
284 papszEnv[cEnvVars] = NULL;
285 cRemoved++;
286 iEnvVar--;
287 }
288 *pcEnvVars = cEnvVars;
289
290 if (cVerbosity > 0 && !cRemoved)
291 warnx("not found '%s'", pszVarToRemove);
292 }
293 else
294 return errx(1, "Found invalid variable name character '=' in: -U %s", pszVarToRemove);
295 return 0;
296}
297
298
299
300/**
301 * Handles the --chdir dir option.
302 *
303 * @returns 0 on success, non-zero exit code on error.
304 * @param pszCwd The CWD buffer. Contains current CWD on input,
305 * modified by @a pszValue on output.
306 * @param cbCwdBuf The size of the CWD buffer.
307 * @param pszValue The --chdir value to apply.
308 */
309int kBuiltinOptChDir(char *pszCwd, size_t cbCwdBuf, const char *pszValue)
310{
311 size_t cchNewCwd = strlen(pszValue);
312 size_t offDst;
313 if (cchNewCwd)
314 {
315#ifdef HAVE_DOS_PATHS
316 if (*pszValue == '/' || *pszValue == '\\')
317 {
318 if (pszValue[1] == '/' || pszValue[1] == '\\')
319 offDst = 0; /* UNC */
320 else if (pszCwd[1] == ':' && isalpha(pszCwd[0]))
321 offDst = 2; /* Take drive letter from CWD. */
322 else
323 return errx(1, "UNC relative CWD not implemented: cur='%s' new='%s'", pszCwd, pszValue);
324 }
325 else if ( pszValue[1] == ':'
326 && isalpha(pszValue[0]))
327 {
328 if (pszValue[2] == '/'|| pszValue[2] == '\\')
329 offDst = 0; /* DOS style absolute path. */
330 else if ( pszCwd[1] == ':'
331 && tolower(pszCwd[0]) == tolower(pszValue[0]) )
332 {
333 pszValue += 2; /* Same drive as CWD, append drive relative path from value. */
334 cchNewCwd -= 2;
335 offDst = strlen(pszCwd);
336 }
337 else
338 {
339 /* Get current CWD on the specified drive and append value. */
340 int iDrive = tolower(pszValue[0]) - 'a' + 1;
341 if (!_getdcwd(iDrive, pszCwd, cbCwdBuf))
342 return err(1, "_getdcwd(%d,,) failed", iDrive);
343 pszValue += 2;
344 cchNewCwd -= 2;
345 }
346 }
347#else
348 if (*pszValue == '/')
349 offDst = 0;
350#endif
351 else
352 offDst = strlen(pszCwd); /* Relative path, append to the existing CWD value. */
353
354 /* Do the copying. */
355#ifdef HAVE_DOS_PATHS
356 if (offDst > 0 && pszCwd[offDst - 1] != '/' && pszCwd[offDst - 1] != '\\')
357#else
358 if (offDst > 0 && pszCwd[offDst - 1] != '/')
359#endif
360 pszCwd[offDst++] = '/';
361 if (offDst + cchNewCwd >= cbCwdBuf)
362 return errx(1, "Too long CWD: %*.*s%s", offDst, offDst, pszCwd, pszValue);
363 memcpy(&pszCwd[offDst], pszValue, cchNewCwd + 1);
364 }
365 /* else: relative, no change - quitely ignore. */
366 return 0;
367}
368
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette