3 * $Id: sw_env.c,v 1.2 1999/07/27 13:38:27 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.2 1999/07/27 13:38:27 mdw
33 * Cauterized out the low-level environment operations and put them in
36 * Revision 1.1.1.1 1999/06/02 16:53:35 mdw
41 /*----- Header files ------------------------------------------------------*/
55 extern char **environ
;
58 #include <mLib/alloc.h>
59 #include <mLib/dstr.h>
61 #include <mLib/report.h>
66 /*----- Main code ---------------------------------------------------------*/
68 /* --- @env_error@ --- *
70 * Arguments: @int e@ = error code
72 * Returns: String representation of error.
74 * Use: Transforms an error into something a user can understand.
77 const char *env_error(int e
)
81 "Unexpected end-of-file",
82 "Bad character in variable name",
83 "Bad parameter substitution",
85 "Missing or spurious `}'",
90 return (strerror(errno
));
97 * Arguments: @FILE *fp@ = stream to read from
99 * Returns: Next nonwhitespace character.
101 * Use: Advances the file position past whitespace characters, and
102 * returns the following nonwhitespace character. The character
106 static int peek(FILE *fp
)
114 } while (isspace((unsigned char)ch
));
119 /* --- @env_var@ --- *
121 * Arguments: @sym_table *t@ = pointer to symbol table
122 * @FILE *fp@ = pointer to stream to read from
123 * @dstr *d@ = pointer to output variable
125 * Returns: One of the @ENV_@ constants.
127 * Use: Scans a variable name from the input stream.
130 int env_var(sym_table
*t
, FILE *fp
, dstr
*d
)
136 if (ch
!= '_' && !isalpha((unsigned char)ch
))
141 if (ch
== EOF
|| (ch
!= '_' && !isalnum((unsigned char)ch
)))
149 /* --- @cmdsubst@ --- *
151 * Arguments: @sym_table *t@ = pointer to symbol table
152 * @FILE *fp@ = pointer to stream to read from
153 * @dstr *d@ = pointer to output variable
154 * @unsigned f@ = interesting flags
156 * Returns: An @ENV_@ magic code.
158 * Use: Rips a command line out of the input stream and writes the
159 * output of the command to the variable. The parsing has some
160 * bizarre artifacts, but it's fairly serviceable.
163 static int cmdsubst(sym_table
*t
, FILE *fp
, dstr
*d
, unsigned f
)
165 int term
= (f
& EVF_BACKTICK
) ?
'`' : ')';
173 /* --- Snarfle the arguments --- */
176 while (peek(fp
) != term
) {
177 if ((e
= env_value(t
, fp
, d
, f
)) != ENV_OK
)
188 /* --- Make the @argv@ array --- */
191 char *p
= d
->buf
+ l
;
192 char *lim
= d
->buf
+ d
->len
;
195 v
= argv
= xmalloc(argc
* sizeof(char *));
209 /* --- Do the fork/exec thing --- */
213 if ((kid
= fork()) < 0)
222 environ
= env_export(t
);
223 execvp(argv
[0], argv
);
231 ssize_t n
= read(fd
[0], buf
, sizeof(buf
));
239 while (l
> 0 && d
->buf
[l
- 1] == '\n')
253 /* --- @env_value@ --- *
255 * Arguments: @sym_table *t@ = pointer to symbol table
256 * @FILE *fp@ = pointer to stream to read from
257 * @dstr *d@ = pointer to output variable
258 * @unsigned f@ = various interesting flags
260 * Returns: 0 if OK, @EOF@ if end-of-file encountered, or >0 on error.
262 * Use: Scans a value from the input stream. The value read may be
263 * quoted in a Bourne-shell sort of a way, and contain Bourne-
264 * shell-like parameter substitutions. Some substitutions
265 * aren't available because they're too awkward to implement.
268 int env_value(sym_table
*t
, FILE *fp
, dstr
*d
, unsigned f
)
270 enum { Q_NONE
, Q_SINGLE
, Q_DOUBLE
, Q_BACK
} qt
= Q_NONE
;
277 } while ((f
& EVF_INITSPC
) && isspace((unsigned char)ch
));
279 for (;; ch
= getc(fp
)) {
281 /* --- Sort out the current character --- */
283 if (ch
== EOF
) break;
285 /* --- A backslash escapes the next character --- */
288 if ((ch
= getc(fp
)) == EOF
) break;
294 /* --- A single quote starts single-quoting, unless quoted --- *
296 * Do the single-quoted snarf here rather than fiddling with anything
300 if (ch
== '\'' && qt
== Q_NONE
) {
303 if ((ch
= getc(fp
)) == EOF
) goto done
;
304 if (ch
== '\'') break;
311 /* --- A backtick does the obvious thing --- */
313 if (ch
== '`' && !(f
& EVF_BACKTICK
)) {
315 if ((e
= cmdsubst(t
, fp
, d
, f
| EVF_BACKTICK
)) != ENV_OK
)
320 /* --- Handle double-quoted text --- */
325 else if (qt
== Q_NONE
)
328 return (ENV_INTERNAL
);
332 /* --- Handle variable references and similar magic --- */
340 /* --- Read one character ahead --- */
342 if ((ch
= getc(fp
)) == EOF
) goto done
;
344 /* --- An alphabetic means this is a direct reference --- */
346 if (ch
== '_' || isalpha(ch
)) {
348 if ((e
= env_var(t
, fp
, d
)) != ENV_OK
) return (e
);
350 if ((v
= env_get(t
, d
->buf
+ l
)) != 0)
354 /* --- A brace means this is a more complex substitution --- */
356 else if (ch
== '{') {
357 if ((e
= env_var(t
, fp
, d
)) != ENV_OK
)
360 v
= env_get(t
, d
->buf
+ l
);
378 goto again
; /* `::' and `:}' should be errors */
385 /* Drop through hackily */
392 if ((e
= env_value(t
, fp
, d
, EVF_INCSPC
)) != ENV_OK
)
402 if ((e
= env_value(t
, fp
, d
, EVF_INCSPC
)) != ENV_OK
)
406 vn
= xstrdup(d
->buf
+ l
);
407 if ((e
= env_value(t
, fp
, d
, EVF_INCSPC
)) != ENV_OK
)
410 env_put(t
, vn
, d
->buf
+ l
);
422 /* --- Handle `$(...)'-style command substitution --- */
424 else if (ch
== '(') {
425 if ((e
= cmdsubst(t
, fp
, d
, f
& ~EVF_BACKTICK
)) != ENV_OK
)
429 /* --- No other `$...' tricks implemented yet --- *
431 * Other ideas: `$((...))' arithmetic.
439 /* --- At this point, anything else double-quoted is munched --- */
441 if (qt
== Q_DOUBLE
) {
446 /* --- Some characters just aren't allowed unquoted --- *
448 * They're not an error; they just mean I should stop parsing. They're
449 * probably interesting to the next level up.
459 if (f
& EVF_BACKTICK
) {
466 /* --- Whitespace characters --- *
468 * I might snarf them myself anyway, according to flags. Or I might
469 * stop, and skip any following whitespace
472 if (isspace((unsigned char)ch
) && (f
& EVF_INCSPC
) == 0) {
475 while (ch
!= EOF
&& isspace((unsigned char)ch
));
480 /* --- Get a new character and go around again --- */
485 /* --- Tidying --- */
489 return (qt
== Q_NONE ? ENV_OK
: ENV_QUOTE
);
492 /* --- @env_read@ --- *
494 * Arguments: @sym_table *t@ = pointer to symbol table
495 * @FILE *fp@ = file handle to read
496 * @unsigned f@ = various flags
498 * Returns: Zero if OK, @EOF@ for end-of-file, or error code.
500 * Use: Reads the environment assignment statements in the file.
503 int env_read(sym_table
*t
, FILE *fp
, unsigned f
)
505 dstr n
= DSTR_INIT
, v
= DSTR_INIT
;
514 if ((e
= env_value(t
, fp
, &v
, f
)) != ENV_OK
)
519 do ch
= getc(fp
); while (ch
!= EOF
&& ch
!= '\n');
521 else if (peek(fp
) == '}' ||
522 (e
= env_var(t
, fp
, &n
)) != ENV_OK
)
525 else if (strcmp(n
.buf
, "include") == 0) {
527 if ((e
= env_value(t
, fp
, &v
, f
)) != ENV_OK
)
533 else if (strcmp(n
.buf
, "arch") == 0) {
535 if ((e
= env_value(t
, fp
, &v
, f
)) != ENV_OK
)
537 if (peek(fp
) != '{') {
542 e
= env_read(t
, fp
, strcmp(v
.buf
, ARCH
) ? f
| EVF_SKIP
: f
);
545 if (getc(fp
) != '}') {
551 else if (strcmp(n
.buf
, "unset") == 0) {
553 if ((e
= env_var(t
, fp
, &v
)) != ENV_OK
)
555 env_put(t
, v
.buf
, 0);
559 if (strcmp(n
.buf
, "set") == 0) {
562 if ((e
= env_var(t
, fp
, &n
)) != ENV_OK
)
565 if (peek(fp
) == '=') {
569 if ((e
= env_value(t
, fp
, &v
, f
)) != ENV_OK
)
573 env_put(t
, n
.buf
, v
.buf
);
588 /* --- @env_file@ --- *
590 * Arguments: @sym_table *t@ = pointer to symbol table
591 * @const char *name@ = pointer to filename
593 * Returns: Zero if OK, or an error code.
595 * Use: Reads a named file of environment assignments.
598 int env_file(sym_table
*t
, const char *name
)
603 if ((fp
= fopen(name
, "r")) == 0)
605 e
= env_read(t
, fp
, 0);
609 else if (e
== ENV_OK
)
614 /*----- That's all, folks -------------------------------------------------*/