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 -------------------------------------------------*/