VirtualBox

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

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

kmkbuiltin: funnel output thru output.c (usually via err.c).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 19.9 KB
Line 
1/* $Id: common-env-and-cwd-opt.c 3192 2018-03-26 20:25:56Z 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#include <assert.h>
34
35#include "kmkbuiltin.h"
36#include "err.h"
37
38
39/** The environment variable compare function.
40 * We must use case insensitive compare on windows (Path vs PATH). */
41#ifdef KBUILD_OS_WINDOWS
42# define KSUBMIT_ENV_NCMP _strnicmp
43#else
44# define KSUBMIT_ENV_NCMP strncmp
45#endif
46
47
48/**
49 * Duplicates a read-only enviornment vector.
50 *
51 * @returns The duplicate enviornment.
52 * @param pCtx The built-in command context.
53 * @param papszEnv The read-only vector.
54 * @param cEnvVars The number of variables.
55 * @param pcAllocatedEnvVars The allocated papszEnv size. This is zero on
56 * input and non-zero on successful return.
57 * @param cVerbosity The verbosity level.
58 */
59static char **kBuiltinOptEnvDuplicate(PKMKBUILTINCTX pCtx, char **papszEnv, unsigned cEnvVars, unsigned *pcAllocatedEnvVars,
60 int cVerbosity)
61{
62 unsigned cAllocatedEnvVars = (cEnvVars + 2 + 0xf) & ~(unsigned)0xf;
63 char **papszEnvNew = malloc(cAllocatedEnvVars * sizeof(papszEnvNew[0]));
64 assert(*pcAllocatedEnvVars == 0);
65 if (papszEnvNew)
66 {
67 unsigned i;
68 for (i = 0; i < cEnvVars; i++)
69 {
70 papszEnvNew[i] = strdup(papszEnv[i]);
71 if (!papszEnvNew)
72 {
73 while (i-- > 0)
74 free(papszEnvNew[i]);
75 free(papszEnvNew);
76 errx(pCtx, 1, "out of memory for duplicating environment variables!", i);
77 return NULL;
78 }
79 }
80 papszEnvNew[i] = NULL;
81 *pcAllocatedEnvVars = cAllocatedEnvVars;
82 }
83 else
84 errx(pCtx, 1, "out of memory for duplicating environment vector!");
85 return papszEnvNew;
86}
87
88
89/**
90 * Common worker for kBuiltinOptEnvSet and kBuiltinOptEnvAppendPrepend that adds
91 * a new variable to the environment.
92 *
93 * @returns 0 on success, non-zero exit code on error.
94 * @param pCtx The built-in command context.
95 * @param papszEnv The environment vector.
96 * @param pcEnvVars Pointer to the variable holding the number of
97 * environment variables held by @a papszEnv.
98 * @param pcAllocatedEnvVars Pointer to the variable holding max size of the
99 * environment vector.
100 * @param cVerbosity The verbosity level.
101 * @param pszValue The var=value string to apply.
102 */
103static int kBuiltinOptEnvAddVar(PKMKBUILTINCTX pCtx, char ***ppapszEnv, unsigned *pcEnvVars, unsigned *pcAllocatedEnvVars,
104 int cVerbosity, const char *pszValue)
105{
106 /* Append new variable. We probably need to resize the vector. */
107 char **papszEnv = *ppapszEnv;
108 unsigned cEnvVars = *pcEnvVars;
109 if ((cEnvVars + 2) > *pcAllocatedEnvVars)
110 {
111 *pcAllocatedEnvVars = (cEnvVars + 2 + 0xf) & ~(unsigned)0xf;
112 papszEnv = (char **)realloc(papszEnv, *pcAllocatedEnvVars * sizeof(papszEnv[0]));
113 if (!papszEnv)
114 return errx(pCtx, 1, "out of memory growing environment vector!");
115 *ppapszEnv = papszEnv;
116 }
117 papszEnv[cEnvVars] = strdup(pszValue);
118 if (!papszEnv[cEnvVars])
119 return errx(pCtx, 1, "out of memory adding environment variable!");
120 papszEnv[++cEnvVars] = NULL;
121 *pcEnvVars = cEnvVars;
122 if (cVerbosity > 0)
123 warnx(pCtx, "added '%s'", papszEnv[cEnvVars - 1]);
124 return 0;
125}
126
127
128/**
129 * Common worker for kBuiltinOptEnvSet and kBuiltinOptEnvAppendPrepend that
130 * remove duplicates.
131 *
132 * @returns 0 on success, non-zero exit code on error.
133 * @param pCtx The built-in command context.
134 * @param papszEnv The environment vector.
135 * @param cEnvVars Number of environment variables.
136 * @param cVerbosity The verbosity level.
137 * @param pszValue The var=value string to apply.
138 * @param cchVar The length of the variable part of @a pszValue.
139 * @param iEnvVar Where to start searching after.
140 */
141static int kBuiltinOptEnvRemoveDuplicates(PKMKBUILTINCTX pCtx, char **papszEnv, unsigned cEnvVars, int cVerbosity,
142 const char *pszValue, size_t cchVar, unsigned iEnvVar)
143{
144 for (iEnvVar++; iEnvVar < cEnvVars; iEnvVar++)
145 if ( KSUBMIT_ENV_NCMP(papszEnv[iEnvVar], pszValue, cchVar) == 0
146 && papszEnv[iEnvVar][cchVar] == '=')
147 {
148 if (cVerbosity > 0)
149 warnx(pCtx, "removing duplicate '%s'", papszEnv[iEnvVar]);
150 free(papszEnv[iEnvVar]);
151 cEnvVars--;
152 if (iEnvVar != cEnvVars)
153 papszEnv[iEnvVar] = papszEnv[cEnvVars];
154 papszEnv[cEnvVars] = NULL;
155 iEnvVar--;
156 }
157 return 0;
158}
159
160
161/**
162 * Handles the --set var=value option.
163 *
164 * @returns 0 on success, non-zero exit code on error.
165 * @param pCtx The built-in command context.
166 * @param ppapszEnv The environment vector pointer.
167 * @param pcEnvVars Pointer to the variable holding the number of
168 * environment variables held by @a papszEnv.
169 * @param pcAllocatedEnvVars Pointer to the variable holding max size of the
170 * environment vector.
171 * @param cVerbosity The verbosity level.
172 * @param pszValue The var=value string to apply.
173 */
174int kBuiltinOptEnvSet(PKMKBUILTINCTX pCtx, char ***ppapszEnv, unsigned *pcEnvVars, unsigned *pcAllocatedEnvVars,
175 int cVerbosity, const char *pszValue)
176{
177 const char *pszEqual = strchr(pszValue, '=');
178 if (pszEqual)
179 {
180 char **papszEnv = *ppapszEnv;
181 unsigned iEnvVar;
182 unsigned cEnvVars = *pcEnvVars;
183 size_t const cchVar = pszEqual - pszValue;
184
185 if (!*pcAllocatedEnvVars)
186 {
187 papszEnv = kBuiltinOptEnvDuplicate(pCtx, papszEnv, cEnvVars, pcAllocatedEnvVars, cVerbosity);
188 if (!papszEnv)
189 return errx(pCtx, 1, "out of memory duplicating enviornment (setenv)!");
190 *ppapszEnv = papszEnv;
191 }
192
193 for (iEnvVar = 0; iEnvVar < cEnvVars; iEnvVar++)
194 {
195 char *pszCur = papszEnv[iEnvVar];
196 if ( KSUBMIT_ENV_NCMP(pszCur, pszValue, cchVar) == 0
197 && pszCur[cchVar] == '=')
198 {
199 if (cVerbosity > 0)
200 warnx(pCtx, "replacing '%s' with '%s'", papszEnv[iEnvVar], pszValue);
201 free(papszEnv[iEnvVar]);
202 papszEnv[iEnvVar] = strdup(pszValue);
203 if (!papszEnv[iEnvVar])
204 return errx(pCtx, 1, "out of memory for modified environment variable!");
205
206 return kBuiltinOptEnvRemoveDuplicates(pCtx, papszEnv, cEnvVars, cVerbosity, pszValue, cchVar, iEnvVar);
207 }
208 }
209 return kBuiltinOptEnvAddVar(pCtx, ppapszEnv, pcEnvVars, pcAllocatedEnvVars, cVerbosity, pszValue);
210 }
211 return errx(pCtx, 1, "Missing '=': -E %s", pszValue);
212}
213
214
215/**
216 * Common worker for kBuiltinOptEnvAppend and kBuiltinOptEnvPrepend.
217 *
218 * @returns 0 on success, non-zero exit code on error.
219 * @param pCtx The built-in command context.
220 * @param ppapszEnv The environment vector pointer.
221 * @param pcEnvVars Pointer to the variable holding the number of
222 * environment variables held by @a papszEnv.
223 * @param pcAllocatedEnvVars Pointer to the variable holding max size of the
224 * environment vector.
225 * @param cVerbosity The verbosity level.
226 * @param pszValue The var=value string to apply.
227 */
228static int kBuiltinOptEnvAppendPrepend(PKMKBUILTINCTX pCtx, char ***ppapszEnv, unsigned *pcEnvVars, unsigned *pcAllocatedEnvVars,
229 int cVerbosity, const char *pszValue, int fAppend)
230{
231 const char *pszEqual = strchr(pszValue, '=');
232 if (pszEqual)
233 {
234 char **papszEnv = *ppapszEnv;
235 unsigned iEnvVar;
236 unsigned cEnvVars = *pcEnvVars;
237 size_t const cchVar = pszEqual - pszValue;
238
239 if (!*pcAllocatedEnvVars)
240 {
241 papszEnv = kBuiltinOptEnvDuplicate(pCtx, papszEnv, cEnvVars, pcAllocatedEnvVars, cVerbosity);
242 if (!papszEnv)
243 return errx(pCtx, 1, "out of memory duplicating environment (append)!");
244 *ppapszEnv = papszEnv;
245 }
246
247 for (iEnvVar = 0; iEnvVar < cEnvVars; iEnvVar++)
248 {
249 char *pszCur = papszEnv[iEnvVar];
250 if ( KSUBMIT_ENV_NCMP(pszCur, pszValue, cchVar) == 0
251 && pszCur[cchVar] == '=')
252 {
253 size_t cchOldValue = strlen(pszCur) - cchVar - 1;
254 size_t cchNewValue = strlen(pszValue) - cchVar - 1;
255 char *pszNew = malloc(cchVar + 1 + cchOldValue + cchNewValue + 1);
256 if (!pszNew)
257 return errx(pCtx, 1, "out of memory appending to environment variable!");
258 if (fAppend)
259 {
260 memcpy(pszNew, pszCur, cchVar + 1 + cchOldValue);
261 memcpy(&pszNew[cchVar + 1 + cchOldValue], &pszValue[cchVar + 1], cchNewValue + 1);
262 }
263 else
264 {
265 memcpy(pszNew, pszCur, cchVar + 1); /* preserve variable name case */
266 memcpy(&pszNew[cchVar + 1], &pszValue[cchVar + 1], cchNewValue);
267 memcpy(&pszNew[cchVar + 1 + cchNewValue], &pszCur[cchVar + 1], cchOldValue + 1);
268 }
269
270 if (cVerbosity > 0)
271 warnx(pCtx, "replacing '%s' with '%s'", pszCur, pszNew);
272 free(pszCur);
273 papszEnv[iEnvVar] = pszNew;
274
275 return kBuiltinOptEnvRemoveDuplicates(pCtx, papszEnv, cEnvVars, cVerbosity, pszValue, cchVar, iEnvVar);
276 }
277 }
278 return kBuiltinOptEnvAddVar(pCtx, ppapszEnv, pcEnvVars, pcAllocatedEnvVars, cVerbosity, pszValue);
279 }
280 return errx(pCtx, 1, "Missing '=': -E %s", pszValue);
281}
282
283
284/**
285 * Handles the --append var=value option.
286 *
287 * @returns 0 on success, non-zero exit code on error.
288 * @param pCtx The built-in command context.
289 * @param ppapszEnv The environment vector pointer.
290 * @param pcEnvVars Pointer to the variable holding the number of
291 * environment variables held by @a papszEnv.
292 * @param pcAllocatedEnvVars Pointer to the variable holding max size of the
293 * environment vector.
294 * @param cVerbosity The verbosity level.
295 * @param pszValue The var=value string to apply.
296 */
297int kBuiltinOptEnvAppend(PKMKBUILTINCTX pCtx, char ***ppapszEnv, unsigned *pcEnvVars, unsigned *pcAllocatedEnvVars,
298 int cVerbosity, const char *pszValue)
299{
300 return kBuiltinOptEnvAppendPrepend(pCtx, ppapszEnv, pcEnvVars, pcAllocatedEnvVars, cVerbosity, pszValue, 1 /*fAppend*/);
301}
302
303
304/**
305 * Handles the --prepend var=value option.
306 *
307 * @returns 0 on success, non-zero exit code on error.
308 * @param pCtx The built-in command context.
309 * @param ppapszEnv The environment vector pointer.
310 * @param pcEnvVars Pointer to the variable holding the number of
311 * environment variables held by @a papszEnv.
312 * @param pcAllocatedEnvVars Pointer to the variable holding max size of the
313 * environment vector.
314 * @param cVerbosity The verbosity level.
315 * @param pszValue The var=value string to apply.
316 */
317int kBuiltinOptEnvPrepend(PKMKBUILTINCTX pCtx, char ***ppapszEnv, unsigned *pcEnvVars, unsigned *pcAllocatedEnvVars,
318 int cVerbosity, const char *pszValue)
319{
320 return kBuiltinOptEnvAppendPrepend(pCtx, ppapszEnv, pcEnvVars, pcAllocatedEnvVars, cVerbosity, pszValue, 0 /*fAppend*/);
321}
322
323
324/**
325 * Handles the --unset var option.
326 *
327 * @returns 0 on success, non-zero exit code on error.
328 * @param pCtx The built-in command context.
329 * @param ppapszEnv The environment vector pointer.
330 * @param pcEnvVars Pointer to the variable holding the number of
331 * environment variables held by @a papszEnv.
332 * @param pcAllocatedEnvVars Pointer to the size of the vector allocation.
333 * The size is zero when read-only (CRT, GNU make)
334 * environment.
335 * @param cVerbosity The verbosity level.
336 * @param pszVarToRemove The name of the variable to remove.
337 */
338int kBuiltinOptEnvUnset(PKMKBUILTINCTX pCtx, char ***ppapszEnv, unsigned *pcEnvVars, unsigned *pcAllocatedEnvVars,
339 int cVerbosity, const char *pszVarToRemove)
340{
341 if (strchr(pszVarToRemove, '=') == NULL)
342 {
343 char **papszEnv = *ppapszEnv;
344 unsigned cRemoved = 0;
345 size_t const cchVar = strlen(pszVarToRemove);
346 unsigned cEnvVars = *pcEnvVars;
347 unsigned iEnvVar;
348
349 for (iEnvVar = 0; iEnvVar < cEnvVars; iEnvVar++)
350 if ( KSUBMIT_ENV_NCMP(papszEnv[iEnvVar], pszVarToRemove, cchVar) == 0
351 && papszEnv[iEnvVar][cchVar] == '=')
352 {
353 if (cVerbosity > 0)
354 warnx(pCtx, !cRemoved ? "removing '%s'" : "removing duplicate '%s'", papszEnv[iEnvVar]);
355
356 if (!*pcAllocatedEnvVars)
357 {
358 papszEnv = kBuiltinOptEnvDuplicate(pCtx, papszEnv, cEnvVars, pcAllocatedEnvVars, cVerbosity);
359 if (!papszEnv)
360 return errx(pCtx, 1, "out of memory duplicating environment (unset)!");
361 *ppapszEnv = papszEnv;
362 }
363
364 free(papszEnv[iEnvVar]);
365 cEnvVars--;
366 if (iEnvVar != cEnvVars)
367 papszEnv[iEnvVar] = papszEnv[cEnvVars];
368 papszEnv[cEnvVars] = NULL;
369 cRemoved++;
370 iEnvVar--;
371 }
372 *pcEnvVars = cEnvVars;
373
374 if (cVerbosity > 0 && !cRemoved)
375 warnx(pCtx, "not found '%s'", pszVarToRemove);
376 }
377 else
378 return errx(pCtx, 1, "Found invalid variable name character '=' in: -U %s", pszVarToRemove);
379 return 0;
380}
381
382
383/**
384 * Handles the --zap-env & --ignore-environment options.
385 *
386 * @returns 0 on success, non-zero exit code on error.
387 * @param pCtx The built-in command context.
388 * @param ppapszEnv The environment vector pointer.
389 * @param pcEnvVars Pointer to the variable holding the number of
390 * environment variables held by @a papszEnv.
391 * @param pcAllocatedEnvVars Pointer to the size of the vector allocation.
392 * The size is zero when read-only (CRT, GNU make)
393 * environment.
394 * @param cVerbosity The verbosity level.
395 */
396int kBuiltinOptEnvZap(PKMKBUILTINCTX pCtx, char ***ppapszEnv, unsigned *pcEnvVars, unsigned *pcAllocatedEnvVars, int cVerbosity)
397{
398 if (*pcAllocatedEnvVars > 0)
399 {
400 char **papszEnv = *ppapszEnv;
401 unsigned i = *pcEnvVars;
402 while (i-- > 0)
403 {
404 free(papszEnv[i]);
405 papszEnv[i] = NULL;
406 }
407 }
408 else
409 {
410 char **papszEnv = calloc(4, sizeof(char *));
411 if (!papszEnv)
412 return err(pCtx, 1, "out of memory!");
413 *ppapszEnv = papszEnv;
414 *pcAllocatedEnvVars = 4;
415 }
416 *pcEnvVars = 0;
417 return 0;
418}
419
420
421/**
422 * Cleans up afterwards, if necessary.
423 *
424 * @param ppapszEnv The environment vector pointer.
425 * @param cEnvVars The number of variables in the vector.
426 * @param pcAllocatedEnvVars Pointer to the size of the vector allocation.
427 * The size is zero when read-only (CRT, GNU make)
428 * environment.
429 */
430void kBuiltinOptEnvCleanup(char ***ppapszEnv, unsigned cEnvVars, unsigned *pcAllocatedEnvVars)
431{
432 char **papszEnv = *ppapszEnv;
433 *ppapszEnv = NULL;
434 if (*pcAllocatedEnvVars > 0)
435 {
436 *pcAllocatedEnvVars = 0;
437 while (cEnvVars-- > 0)
438 {
439 free(papszEnv[cEnvVars]);
440 papszEnv[cEnvVars] = NULL;
441 }
442 free(papszEnv);
443 }
444}
445
446
447/**
448 * Handles the --chdir dir option.
449 *
450 * @returns 0 on success, non-zero exit code on error.
451 * @param pCtx The built-in command context.
452 * @param pszCwd The CWD buffer. Contains current CWD on input,
453 * modified by @a pszValue on output.
454 * @param cbCwdBuf The size of the CWD buffer.
455 * @param pszValue The --chdir value to apply.
456 */
457int kBuiltinOptChDir(PKMKBUILTINCTX pCtx, char *pszCwd, size_t cbCwdBuf, const char *pszValue)
458{
459 size_t cchNewCwd = strlen(pszValue);
460 size_t offDst;
461 if (cchNewCwd)
462 {
463#ifdef HAVE_DOS_PATHS
464 if (*pszValue == '/' || *pszValue == '\\')
465 {
466 if (pszValue[1] == '/' || pszValue[1] == '\\')
467 offDst = 0; /* UNC */
468 else if (pszCwd[1] == ':' && isalpha(pszCwd[0]))
469 offDst = 2; /* Take drive letter from CWD. */
470 else
471 return errx(pCtx, 1, "UNC relative CWD not implemented: cur='%s' new='%s'", pszCwd, pszValue);
472 }
473 else if ( pszValue[1] == ':'
474 && isalpha(pszValue[0]))
475 {
476 if (pszValue[2] == '/'|| pszValue[2] == '\\')
477 offDst = 0; /* DOS style absolute path. */
478 else if ( pszCwd[1] == ':'
479 && tolower(pszCwd[0]) == tolower(pszValue[0]) )
480 {
481 pszValue += 2; /* Same drive as CWD, append drive relative path from value. */
482 cchNewCwd -= 2;
483 offDst = strlen(pszCwd);
484 }
485 else
486 {
487 /* Get current CWD on the specified drive and append value. */
488 int iDrive = tolower(pszValue[0]) - 'a' + 1;
489 if (!_getdcwd(iDrive, pszCwd, cbCwdBuf))
490 return err(pCtx, 1, "_getdcwd(%d,,) failed", iDrive);
491 pszValue += 2;
492 cchNewCwd -= 2;
493 }
494 }
495#else
496 if (*pszValue == '/')
497 offDst = 0;
498#endif
499 else
500 offDst = strlen(pszCwd); /* Relative path, append to the existing CWD value. */
501
502 /* Do the copying. */
503#ifdef HAVE_DOS_PATHS
504 if (offDst > 0 && pszCwd[offDst - 1] != '/' && pszCwd[offDst - 1] != '\\')
505#else
506 if (offDst > 0 && pszCwd[offDst - 1] != '/')
507#endif
508 pszCwd[offDst++] = '/';
509 if (offDst + cchNewCwd >= cbCwdBuf)
510 return errx(pCtx, 1, "Too long CWD: %*.*s%s", offDst, offDst, pszCwd, pszValue);
511 memcpy(&pszCwd[offDst], pszValue, cchNewCwd + 1);
512 }
513 /* else: relative, no change - quitely ignore. */
514 return 0;
515}
516
Note: See TracBrowser for help on using the repository browser.

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