3 * $Id: sw_env.c,v 1.1 1999/06/02 16:53:35 mdw Exp $
5 * Mangling of environment variables
10 /*----- Licensing notice --------------------------------------------------*
12 * This file is part of sw-tools.
14 * sw-tools is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation; either version 2 of the License, or
17 * (at your option) any later version.
19 * sw-tools is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
24 * You should have received a copy of the GNU General Public License
25 * along with sw-tools; if not, write to the Free Software Foundation,
26 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
29 /*----- Revision history --------------------------------------------------*
32 * Revision 1.1 1999/06/02 16:53:35 mdw
37 /*----- Header files ------------------------------------------------------*/
51 extern char **environ;
54 #include <mLib/alloc.h>
55 #include <mLib/dstr.h>
56 #include <mLib/report.h>
61 /*----- Data structures ---------------------------------------------------*/
68 /*----- Main code ---------------------------------------------------------*/
70 /* --- @env_get@ --- *
72 * Arguments: @sym_table *t@ = pointer to a symbol table
73 * @const char *name@ = pointer to variable name to look up
75 * Returns: Pointer to corresponding value string, or null.
77 * Use: Looks up an environment variable in the table and returns its
78 * value. If the variable can't be found, a null pointer is
82 char *env_get(sym_table *t, const char *name)
84 var *e = sym_find(t, name, -1, 0, 0);
85 return (e ? e->v : 0);
88 /* --- @env_put@ --- *
90 * Arguments: @sym_table *t@ = pointer to a symbol table
91 * @const char *name@ = pointer to variable name to set
92 * @const char *value@ = pointer to value string to assign
96 * Use: Assigns a value to a variable. If the @name@ contains an
97 * equals character, then it's assumed to be of the form
98 * `VAR=VALUE' and @value@ argument is ignored. Otherwise, if
99 * @value@ is null, the variable is deleted. Finally, the
100 * normal case: @name@ is a plain name, and @value@ is a normal
101 * string causes the variable to be assigned the value in the
105 void env_put(sym_table *t, const char *name, const char *value)
109 /* --- Sort out the mess with `NAME=VALUE' forms --- */
112 size_t eq = strcspn(name, "=");
113 if (name[eq] == '=') {
117 value = name + eq + 1;
122 /* --- Read the current value --- */
126 if ((v = sym_find(t, name, -1, 0, 0)) != 0) {
132 var *v = sym_find(t, name, -1, sizeof(*v), &found);
135 v->v = xstrdup(value);
138 /* --- Tidying --- */
144 /* --- @env_import@ --- *
146 * Arguments: @sym_table *t@ = pointer to a symbol table
147 * @char **env@ = pointer to an environment list
151 * Use: Inserts all of the environment variables listed into a symbol
152 * table for rapid access. Equivalent to a lot of calls to
156 void env_import(sym_table *t, char **env)
164 /* --- @env_export@ --- *
166 * Arguments: @sym_table *t@ = pointer to a symbol table
168 * Returns: A big environment list.
170 * Use: Extracts an environment table from a symbol table
171 * representation of an environment. The table and all of the
172 * strings are in one big block allocated from the heap.
175 char **env_export(sym_table *t)
184 /* --- Work out sizes for everything --- */
186 for (sym_mkiter(&i, t); (v = sym_next(&i)) != 0; ) {
188 sz += strlen(SYM_NAME(v)) + strlen(v->v) + 2;
191 /* --- Allocate the big chunk of memory --- */
193 env = pp = xmalloc(n * sizeof(char *) + sz);
194 p = (char *)(env + n);
196 /* --- Dump the output in the big chunk of memory --- */
198 for (sym_mkiter(&i, t); (v = sym_next(&i)) != 0; ) {
199 const char *name = SYM_NAME(v);
200 size_t nlen = strlen(name), vlen = strlen(v->v);
202 memcpy(p, name, nlen); p += nlen;
204 memcpy(p, v->v, vlen); p += vlen;
211 /* --- @env_destroy@ --- *
213 * Arguments: @sym_table *t@ = pointer to symbol table
217 * Use: Destroys all the variables in the symbol table.
220 void env_destroy(sym_table *t)
225 for (sym_mkiter(&i, t); (v = sym_next(&i)) != 0; )
230 /* --- @env_error@ --- *
232 * Arguments: @int e@ = error code
234 * Returns: String representation of error.
236 * Use: Transforms an error into something a user can understand.
239 const char *env_error(int e)
241 const char *tab[] = {
242 "Everything is fine",
243 "Unexpected end-of-file",
244 "Bad character in variable name",
245 "Bad parameter substitution",
247 "Missing or spurious `}'",
252 return (strerror(errno));
259 * Arguments: @FILE *fp@ = stream to read from
261 * Returns: Next nonwhitespace character.
263 * Use: Advances the file position past whitespace characters, and
264 * returns the following nonwhitespace character. The character
268 static int peek(FILE *fp)
276 } while (isspace((unsigned char)ch));
281 /* --- @env_var@ --- *
283 * Arguments: @sym_table *t@ = pointer to symbol table
284 * @FILE *fp@ = pointer to stream to read from
285 * @dstr *d@ = pointer to output variable
287 * Returns: One of the @ENV_@ constants.
289 * Use: Scans a variable name from the input stream.
292 int env_var(sym_table *t, FILE *fp, dstr *d)
298 if (ch != '_' && !isalpha((unsigned char)ch))
303 if (ch == EOF || (ch != '_' && !isalnum((unsigned char)ch)))
311 /* --- @cmdsubst@ --- *
313 * Arguments: @sym_table *t@ = pointer to symbol table
314 * @FILE *fp@ = pointer to stream to read from
315 * @dstr *d@ = pointer to output variable
316 * @unsigned f@ = interesting flags
318 * Returns: An @ENV_@ magic code.
320 * Use: Rips a command line out of the input stream and writes the
321 * output of the command to the variable. The parsing has some
322 * bizarre artifacts, but it's fairly serviceable.
325 static int cmdsubst(sym_table *t, FILE *fp, dstr *d, unsigned f)
327 int term = (f & EVF_BACKTICK) ? '`' : ')';
335 /* --- Snarfle the arguments --- */
338 while (peek(fp) != term) {
339 if ((e = env_value(t, fp, d, f)) != ENV_OK)
350 /* --- Make the @argv@ array --- */
353 char *p = d->buf + l;
354 char *lim = d->buf + d->len;
357 v = argv = xmalloc(argc * sizeof(char *));
371 /* --- Do the fork/exec thing --- */
375 if ((kid = fork()) < 0)
384 environ = env_export(t);
385 execvp(argv[0], argv);
393 ssize_t n = read(fd[0], buf, sizeof(buf));
401 while (l > 0 && d->buf[l - 1] == '\n')
415 /* --- @env_value@ --- *
417 * Arguments: @sym_table *t@ = pointer to symbol table
418 * @FILE *fp@ = pointer to stream to read from
419 * @dstr *d@ = pointer to output variable
420 * @unsigned f@ = various interesting flags
422 * Returns: 0 if OK, @EOF@ if end-of-file encountered, or >0 on error.
424 * Use: Scans a value from the input stream. The value read may be
425 * quoted in a Bourne-shell sort of a way, and contain Bourne-
426 * shell-like parameter substitutions. Some substitutions
427 * aren't available because they're too awkward to implement.
430 int env_value(sym_table *t, FILE *fp, dstr *d, unsigned f)
432 enum { Q_NONE, Q_SINGLE, Q_DOUBLE, Q_BACK } qt = Q_NONE;
439 } while ((f & EVF_INITSPC) && isspace((unsigned char)ch));
441 for (;; ch = getc(fp)) {
443 /* --- Sort out the current character --- */
445 if (ch == EOF) break;
447 /* --- A backslash escapes the next character --- */
450 if ((ch = getc(fp)) == EOF) break;
456 /* --- A single quote starts single-quoting, unless quoted --- *
458 * Do the single-quoted snarf here rather than fiddling with anything
462 if (ch == '\'' && qt == Q_NONE) {
465 if ((ch = getc(fp)) == EOF) goto done;
466 if (ch == '\'') break;
473 /* --- A backtick does the obvious thing --- */
475 if (ch == '`' && !(f & EVF_BACKTICK)) {
477 if ((e = cmdsubst(t, fp, d, f | EVF_BACKTICK)) != ENV_OK)
482 /* --- Handle double-quoted text --- */
487 else if (qt == Q_NONE)
490 return (ENV_INTERNAL);
494 /* --- Handle variable references and similar magic --- */
502 /* --- Read one character ahead --- */
504 if ((ch = getc(fp)) == EOF) goto done;
506 /* --- An alphabetic means this is a direct reference --- */
508 if (ch == '_' || isalpha(ch)) {
510 if ((e = env_var(t, fp, d)) != ENV_OK) return (e);
512 if ((v = env_get(t, d->buf + l)) != 0)
516 /* --- A brace means this is a more complex substitution --- */
518 else if (ch == '{') {
519 if ((e = env_var(t, fp, d)) != ENV_OK)
522 v = env_get(t, d->buf + l);
540 goto again; /* `::' and `:}' should be errors */
547 /* Drop through hackily */
554 if ((e = env_value(t, fp, d, EVF_INCSPC)) != ENV_OK)
564 if ((e = env_value(t, fp, d, EVF_INCSPC)) != ENV_OK)
568 vn = xstrdup(d->buf + l);
569 if ((e = env_value(t, fp, d, EVF_INCSPC)) != ENV_OK)
572 env_put(t, vn, d->buf + l);
584 /* --- Handle `$(...)'-style command substitution --- */
586 else if (ch == '(') {
587 if ((e = cmdsubst(t, fp, d, f & ~EVF_BACKTICK)) != ENV_OK)
591 /* --- No other `$...' tricks implemented yet --- *
593 * Other ideas: `$((...))' arithmetic.
601 /* --- At this point, anything else double-quoted is munched --- */
603 if (qt == Q_DOUBLE) {
608 /* --- Some characters just aren't allowed unquoted --- *
610 * They're not an error; they just mean I should stop parsing. They're
611 * probably interesting to the next level up.
621 if (f & EVF_BACKTICK) {
628 /* --- Whitespace characters --- *
630 * I might snarf them myself anyway, according to flags. Or I might
631 * stop, and skip any following whitespace
634 if (isspace((unsigned char)ch) && (f & EVF_INCSPC) == 0) {
637 while (ch != EOF && isspace((unsigned char)ch));
642 /* --- Get a new character and go around again --- */
647 /* --- Tidying --- */
651 return (qt == Q_NONE ? ENV_OK : ENV_QUOTE);
654 /* --- @env_read@ --- *
656 * Arguments: @sym_table *t@ = pointer to symbol table
657 * @FILE *fp@ = file handle to read
658 * @unsigned f@ = various flags
660 * Returns: Zero if OK, @EOF@ for end-of-file, or error code.
662 * Use: Reads the environment assignment statements in the file.
665 int env_read(sym_table *t, FILE *fp, unsigned f)
667 dstr n = DSTR_INIT, v = DSTR_INIT;
676 if ((e = env_value(t, fp, &v, f)) != ENV_OK)
681 do ch = getc(fp); while (ch != EOF && ch != '\n');
683 else if (peek(fp) == '}' ||
684 (e = env_var(t, fp, &n)) != ENV_OK)
687 else if (strcmp(n.buf, "include") == 0) {
689 if ((e = env_value(t, fp, &v, f)) != ENV_OK)
695 else if (strcmp(n.buf, "arch") == 0) {
697 if ((e = env_value(t, fp, &v, f)) != ENV_OK)
699 if (peek(fp) != '{') {
704 e = env_read(t, fp, strcmp(v.buf, ARCH) ? f | EVF_SKIP : f);
707 if (getc(fp) != '}') {
713 else if (strcmp(n.buf, "unset") == 0) {
715 if ((e = env_var(t, fp, &v)) != ENV_OK)
717 env_put(t, v.buf, 0);
721 if (strcmp(n.buf, "set") == 0) {
724 if ((e = env_var(t, fp, &n)) != ENV_OK)
727 if (peek(fp) == '=') {
731 if ((e = env_value(t, fp, &v, f)) != ENV_OK)
735 env_put(t, n.buf, v.buf);
750 /* --- @env_file@ --- *
752 * Arguments: @sym_table *t@ = pointer to symbol table
753 * @const char *name@ = pointer to filename
755 * Returns: Zero if OK, or an error code.
757 * Use: Reads a named file of environment assignments.
760 int env_file(sym_table *t, const char *name)
765 if ((fp = fopen(name, "r")) == 0)
767 e = env_read(t, fp, 0);
771 else if (e == ENV_OK)
776 /*----- That's all, folks -------------------------------------------------*/