3 * $Id: sw_env.c,v 1.3 2004/04/08 01:52:19 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 /*----- Header files ------------------------------------------------------*/
43 extern char **environ;
46 #include <mLib/alloc.h>
47 #include <mLib/dstr.h>
49 #include <mLib/report.h>
54 /*----- Main code ---------------------------------------------------------*/
56 /* --- @env_error@ --- *
58 * Arguments: @int e@ = error code
60 * Returns: String representation of error.
62 * Use: Transforms an error into something a user can understand.
65 const char *env_error(int e)
69 "Unexpected end-of-file",
70 "Bad character in variable name",
71 "Bad parameter substitution",
73 "Missing or spurious `}'",
78 return (strerror(errno));
85 * Arguments: @FILE *fp@ = stream to read from
87 * Returns: Next nonwhitespace character.
89 * Use: Advances the file position past whitespace characters, and
90 * returns the following nonwhitespace character. The character
94 static int peek(FILE *fp)
102 } while (isspace((unsigned char)ch));
107 /* --- @env_var@ --- *
109 * Arguments: @sym_table *t@ = pointer to symbol table
110 * @FILE *fp@ = pointer to stream to read from
111 * @dstr *d@ = pointer to output variable
113 * Returns: One of the @ENV_@ constants.
115 * Use: Scans a variable name from the input stream.
118 int env_var(sym_table *t, FILE *fp, dstr *d)
124 if (ch != '_' && !isalpha((unsigned char)ch))
129 if (ch == EOF || (ch != '_' && !isalnum((unsigned char)ch)))
137 /* --- @cmdsubst@ --- *
139 * Arguments: @sym_table *t@ = pointer to symbol table
140 * @FILE *fp@ = pointer to stream to read from
141 * @dstr *d@ = pointer to output variable
142 * @unsigned f@ = interesting flags
144 * Returns: An @ENV_@ magic code.
146 * Use: Rips a command line out of the input stream and writes the
147 * output of the command to the variable. The parsing has some
148 * bizarre artifacts, but it's fairly serviceable.
151 static int cmdsubst(sym_table *t, FILE *fp, dstr *d, unsigned f)
153 int term = (f & EVF_BACKTICK) ? '`' : ')';
161 /* --- Snarfle the arguments --- */
164 while (peek(fp) != term) {
165 if ((e = env_value(t, fp, d, f)) != ENV_OK)
176 /* --- Make the @argv@ array --- */
179 char *p = d->buf + l;
180 char *lim = d->buf + d->len;
183 v = argv = xmalloc(argc * sizeof(char *));
197 /* --- Do the fork/exec thing --- */
201 if ((kid = fork()) < 0)
210 environ = env_export(t);
211 execvp(argv[0], argv);
219 ssize_t n = read(fd[0], buf, sizeof(buf));
227 while (l > 0 && d->buf[l - 1] == '\n')
241 /* --- @env_value@ --- *
243 * Arguments: @sym_table *t@ = pointer to symbol table
244 * @FILE *fp@ = pointer to stream to read from
245 * @dstr *d@ = pointer to output variable
246 * @unsigned f@ = various interesting flags
248 * Returns: 0 if OK, @EOF@ if end-of-file encountered, or >0 on error.
250 * Use: Scans a value from the input stream. The value read may be
251 * quoted in a Bourne-shell sort of a way, and contain Bourne-
252 * shell-like parameter substitutions. Some substitutions
253 * aren't available because they're too awkward to implement.
256 int env_value(sym_table *t, FILE *fp, dstr *d, unsigned f)
258 enum { Q_NONE, Q_SINGLE, Q_DOUBLE, Q_BACK } qt = Q_NONE;
265 } while ((f & EVF_INITSPC) && isspace((unsigned char)ch));
267 for (;; ch = getc(fp)) {
269 /* --- Sort out the current character --- */
271 if (ch == EOF) break;
273 /* --- A backslash escapes the next character --- */
276 if ((ch = getc(fp)) == EOF) break;
282 /* --- A single quote starts single-quoting, unless quoted --- *
284 * Do the single-quoted snarf here rather than fiddling with anything
288 if (ch == '\'' && qt == Q_NONE) {
291 if ((ch = getc(fp)) == EOF) goto done;
292 if (ch == '\'') break;
299 /* --- A backtick does the obvious thing --- */
301 if (ch == '`' && !(f & EVF_BACKTICK)) {
303 if ((e = cmdsubst(t, fp, d, f | EVF_BACKTICK)) != ENV_OK)
308 /* --- Handle double-quoted text --- */
313 else if (qt == Q_NONE)
316 return (ENV_INTERNAL);
320 /* --- Handle variable references and similar magic --- */
328 /* --- Read one character ahead --- */
330 if ((ch = getc(fp)) == EOF) goto done;
332 /* --- An alphabetic means this is a direct reference --- */
334 if (ch == '_' || isalpha(ch)) {
336 if ((e = env_var(t, fp, d)) != ENV_OK) return (e);
338 if ((v = env_get(t, d->buf + l)) != 0)
342 /* --- A brace means this is a more complex substitution --- */
344 else if (ch == '{') {
345 if ((e = env_var(t, fp, d)) != ENV_OK)
348 v = env_get(t, d->buf + l);
366 goto again; /* `::' and `:}' should be errors */
373 /* Drop through hackily */
380 if ((e = env_value(t, fp, d, EVF_INCSPC)) != ENV_OK)
390 if ((e = env_value(t, fp, d, EVF_INCSPC)) != ENV_OK)
394 vn = xstrdup(d->buf + l);
395 if ((e = env_value(t, fp, d, EVF_INCSPC)) != ENV_OK)
398 env_put(t, vn, d->buf + l);
410 /* --- Handle `$(...)'-style command substitution --- */
412 else if (ch == '(') {
413 if ((e = cmdsubst(t, fp, d, f & ~EVF_BACKTICK)) != ENV_OK)
417 /* --- No other `$...' tricks implemented yet --- *
419 * Other ideas: `$((...))' arithmetic.
427 /* --- At this point, anything else double-quoted is munched --- */
429 if (qt == Q_DOUBLE) {
434 /* --- Some characters just aren't allowed unquoted --- *
436 * They're not an error; they just mean I should stop parsing. They're
437 * probably interesting to the next level up.
447 if (f & EVF_BACKTICK) {
454 /* --- Whitespace characters --- *
456 * I might snarf them myself anyway, according to flags. Or I might
457 * stop, and skip any following whitespace
460 if (isspace((unsigned char)ch) && (f & EVF_INCSPC) == 0) {
463 while (ch != EOF && isspace((unsigned char)ch));
468 /* --- Get a new character and go around again --- */
473 /* --- Tidying --- */
477 return (qt == Q_NONE ? ENV_OK : ENV_QUOTE);
480 /* --- @env_read@ --- *
482 * Arguments: @sym_table *t@ = pointer to symbol table
483 * @FILE *fp@ = file handle to read
484 * @unsigned f@ = various flags
486 * Returns: Zero if OK, @EOF@ for end-of-file, or error code.
488 * Use: Reads the environment assignment statements in the file.
491 int env_read(sym_table *t, FILE *fp, unsigned f)
493 dstr n = DSTR_INIT, v = DSTR_INIT;
502 if ((e = env_value(t, fp, &v, f)) != ENV_OK)
507 do ch = getc(fp); while (ch != EOF && ch != '\n');
509 else if (peek(fp) == '}' ||
510 (e = env_var(t, fp, &n)) != ENV_OK)
513 else if (strcmp(n.buf, "include") == 0) {
515 if ((e = env_value(t, fp, &v, f)) != ENV_OK)
521 else if (strcmp(n.buf, "arch") == 0) {
523 if ((e = env_value(t, fp, &v, f)) != ENV_OK)
525 if (peek(fp) != '{') {
530 e = env_read(t, fp, strcmp(v.buf, ARCH) ? f | EVF_SKIP : f);
533 if (getc(fp) != '}') {
539 else if (strcmp(n.buf, "unset") == 0) {
541 if ((e = env_var(t, fp, &v)) != ENV_OK)
543 env_put(t, v.buf, 0);
547 if (strcmp(n.buf, "set") == 0) {
550 if ((e = env_var(t, fp, &n)) != ENV_OK)
553 if (peek(fp) == '=') {
557 if ((e = env_value(t, fp, &v, f)) != ENV_OK)
561 env_put(t, n.buf, v.buf);
576 /* --- @env_file@ --- *
578 * Arguments: @sym_table *t@ = pointer to symbol table
579 * @const char *name@ = pointer to filename
581 * Returns: Zero if OK, or an error code.
583 * Use: Reads a named file of environment assignments.
586 int env_file(sym_table *t, const char *name)
591 if ((fp = fopen(name, "r")) == 0)
593 e = env_read(t, fp, 0);
597 else if (e == ENV_OK)
602 /*----- That's all, folks -------------------------------------------------*/