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

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

kmkbultin: environment fixes and stats.

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