1 |
|
|
/* $OpenBSD: env.c,v 1.33 2017/06/07 23:36:43 millert Exp $ */ |
2 |
|
|
|
3 |
|
|
/* Copyright 1988,1990,1993,1994 by Paul Vixie |
4 |
|
|
* Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") |
5 |
|
|
* Copyright (c) 1997,2000 by Internet Software Consortium, Inc. |
6 |
|
|
* |
7 |
|
|
* Permission to use, copy, modify, and distribute this software for any |
8 |
|
|
* purpose with or without fee is hereby granted, provided that the above |
9 |
|
|
* copyright notice and this permission notice appear in all copies. |
10 |
|
|
* |
11 |
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES |
12 |
|
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
13 |
|
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR |
14 |
|
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
15 |
|
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
16 |
|
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT |
17 |
|
|
* OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
18 |
|
|
*/ |
19 |
|
|
|
20 |
|
|
#include <sys/types.h> |
21 |
|
|
|
22 |
|
|
#include <bitstring.h> /* for structs.h */ |
23 |
|
|
#include <ctype.h> |
24 |
|
|
#include <errno.h> |
25 |
|
|
#include <stdio.h> |
26 |
|
|
#include <stdlib.h> |
27 |
|
|
#include <string.h> |
28 |
|
|
#include <time.h> /* for structs.h */ |
29 |
|
|
|
30 |
|
|
#include "macros.h" |
31 |
|
|
#include "structs.h" |
32 |
|
|
#include "funcs.h" |
33 |
|
|
#include "globals.h" |
34 |
|
|
|
35 |
|
|
char ** |
36 |
|
|
env_init(void) |
37 |
|
|
{ |
38 |
|
|
char **p = malloc(sizeof(char *)); |
39 |
|
|
|
40 |
|
|
if (p != NULL) |
41 |
|
|
p[0] = NULL; |
42 |
|
|
return (p); |
43 |
|
|
} |
44 |
|
|
|
45 |
|
|
void |
46 |
|
|
env_free(char **envp) |
47 |
|
|
{ |
48 |
|
|
char **p; |
49 |
|
|
|
50 |
|
|
for (p = envp; *p != NULL; p++) |
51 |
|
|
free(*p); |
52 |
|
|
free(envp); |
53 |
|
|
} |
54 |
|
|
|
55 |
|
|
char ** |
56 |
|
|
env_copy(char **envp) |
57 |
|
|
{ |
58 |
|
|
int count, i, save_errno; |
59 |
|
|
char **p; |
60 |
|
|
|
61 |
|
|
for (count = 0; envp[count] != NULL; count++) |
62 |
|
|
continue; |
63 |
|
|
p = reallocarray(NULL, count+1, sizeof(char *)); /* 1 for the NULL */ |
64 |
|
|
if (p != NULL) { |
65 |
|
|
for (i = 0; i < count; i++) |
66 |
|
|
if ((p[i] = strdup(envp[i])) == NULL) { |
67 |
|
|
save_errno = errno; |
68 |
|
|
while (--i >= 0) |
69 |
|
|
free(p[i]); |
70 |
|
|
free(p); |
71 |
|
|
errno = save_errno; |
72 |
|
|
return (NULL); |
73 |
|
|
} |
74 |
|
|
p[count] = NULL; |
75 |
|
|
} |
76 |
|
|
return (p); |
77 |
|
|
} |
78 |
|
|
|
79 |
|
|
static char * |
80 |
|
|
env_find(char *name, char **envp, size_t *count) |
81 |
|
|
{ |
82 |
|
|
char **ep, *p, *q; |
83 |
|
|
size_t len; |
84 |
|
|
|
85 |
|
|
/* |
86 |
|
|
* Find name in envp and return its value along with the |
87 |
|
|
* index it was found at or the length of envp if not found. |
88 |
|
|
* We treat a '=' in name as end of string for env_set(). |
89 |
|
|
*/ |
90 |
|
|
for (p = name; *p && *p != '='; p++) |
91 |
|
|
continue; |
92 |
|
|
len = (size_t)(p - name); |
93 |
|
|
for (ep = envp; (p = *ep) != NULL; ep++) { |
94 |
|
|
if ((q = strchr(p, '=')) == NULL) |
95 |
|
|
continue; |
96 |
|
|
if ((size_t)(q - p) == len && strncmp(p, name, len) == 0) { |
97 |
|
|
p = q + 1; |
98 |
|
|
break; |
99 |
|
|
} |
100 |
|
|
} |
101 |
|
|
*count = (size_t)(ep - envp); |
102 |
|
|
return (p); |
103 |
|
|
} |
104 |
|
|
|
105 |
|
|
char * |
106 |
|
|
env_get(char *name, char **envp) |
107 |
|
|
{ |
108 |
|
|
size_t count; |
109 |
|
|
|
110 |
|
|
return (env_find(name, envp, &count)); |
111 |
|
|
} |
112 |
|
|
|
113 |
|
|
char ** |
114 |
|
|
env_set(char **envp, char *envstr) |
115 |
|
|
{ |
116 |
|
|
size_t count; |
117 |
|
|
char **p, *envcopy; |
118 |
|
|
|
119 |
|
|
if ((envcopy = strdup(envstr)) == NULL) |
120 |
|
|
return (NULL); |
121 |
|
|
|
122 |
|
|
/* Replace existing name if found. */ |
123 |
|
|
if (env_find(envstr, envp, &count) != NULL) { |
124 |
|
|
free(envp[count]); |
125 |
|
|
envp[count] = envcopy; |
126 |
|
|
return (envp); |
127 |
|
|
} |
128 |
|
|
|
129 |
|
|
/* Realloc envp and append new variable. */ |
130 |
|
|
p = reallocarray(envp, count + 2, sizeof(char **)); |
131 |
|
|
if (p == NULL) { |
132 |
|
|
free(envcopy); |
133 |
|
|
return (NULL); |
134 |
|
|
} |
135 |
|
|
p[count++] = envcopy; |
136 |
|
|
p[count] = NULL; |
137 |
|
|
return (p); |
138 |
|
|
} |
139 |
|
|
|
140 |
|
|
/* The following states are used by load_env(), traversed in order: */ |
141 |
|
|
enum env_state { |
142 |
|
|
NAMEI, /* First char of NAME, may be quote */ |
143 |
|
|
NAME, /* Subsequent chars of NAME */ |
144 |
|
|
EQ1, /* After end of name, looking for '=' sign */ |
145 |
|
|
EQ2, /* After '=', skipping whitespace */ |
146 |
|
|
VALUEI, /* First char of VALUE, may be quote */ |
147 |
|
|
VALUE, /* Subsequent chars of VALUE */ |
148 |
|
|
FINI, /* All done, skipping trailing whitespace */ |
149 |
|
|
ERROR /* Error */ |
150 |
|
|
}; |
151 |
|
|
|
152 |
|
|
/* return -1 = end of file |
153 |
|
|
* FALSE = not an env setting (file was repositioned) |
154 |
|
|
* TRUE = was an env setting |
155 |
|
|
*/ |
156 |
|
|
int |
157 |
|
|
load_env(char *envstr, FILE *f) |
158 |
|
|
{ |
159 |
|
|
long filepos; |
160 |
|
|
int fileline; |
161 |
|
|
enum env_state state; |
162 |
|
|
char name[MAX_ENVSTR], val[MAX_ENVSTR]; |
163 |
|
|
char quotechar, *c, *str; |
164 |
|
|
|
165 |
|
|
filepos = ftell(f); |
166 |
|
|
fileline = LineNumber; |
167 |
|
|
skip_comments(f); |
168 |
|
|
if (EOF == get_string(envstr, MAX_ENVSTR, f, "\n")) |
169 |
|
|
return (-1); |
170 |
|
|
|
171 |
|
|
bzero(name, sizeof name); |
172 |
|
|
bzero(val, sizeof val); |
173 |
|
|
str = name; |
174 |
|
|
state = NAMEI; |
175 |
|
|
quotechar = '\0'; |
176 |
|
|
c = envstr; |
177 |
|
|
while (state != ERROR && *c) { |
178 |
|
|
switch (state) { |
179 |
|
|
case NAMEI: |
180 |
|
|
case VALUEI: |
181 |
|
|
if (*c == '\'' || *c == '"') |
182 |
|
|
quotechar = *c++; |
183 |
|
|
state++; |
184 |
|
|
/* FALLTHROUGH */ |
185 |
|
|
case NAME: |
186 |
|
|
case VALUE: |
187 |
|
|
if (quotechar) { |
188 |
|
|
if (*c == quotechar) { |
189 |
|
|
state++; |
190 |
|
|
c++; |
191 |
|
|
break; |
192 |
|
|
} |
193 |
|
|
if (state == NAME && *c == '=') { |
194 |
|
|
state = ERROR; |
195 |
|
|
break; |
196 |
|
|
} |
197 |
|
|
} else { |
198 |
|
|
if (state == NAME) { |
199 |
|
|
if (isspace((unsigned char)*c)) { |
200 |
|
|
c++; |
201 |
|
|
state++; |
202 |
|
|
break; |
203 |
|
|
} |
204 |
|
|
if (*c == '=') { |
205 |
|
|
state++; |
206 |
|
|
break; |
207 |
|
|
} |
208 |
|
|
} |
209 |
|
|
} |
210 |
|
|
*str++ = *c++; |
211 |
|
|
break; |
212 |
|
|
case EQ1: |
213 |
|
|
if (*c == '=') { |
214 |
|
|
state++; |
215 |
|
|
str = val; |
216 |
|
|
quotechar = '\0'; |
217 |
|
|
} else { |
218 |
|
|
if (!isspace((unsigned char)*c)) |
219 |
|
|
state = ERROR; |
220 |
|
|
} |
221 |
|
|
c++; |
222 |
|
|
break; |
223 |
|
|
case EQ2: |
224 |
|
|
case FINI: |
225 |
|
|
if (isspace((unsigned char)*c)) |
226 |
|
|
c++; |
227 |
|
|
else |
228 |
|
|
state++; |
229 |
|
|
break; |
230 |
|
|
case ERROR: |
231 |
|
|
/* handled below */ |
232 |
|
|
break; |
233 |
|
|
} |
234 |
|
|
} |
235 |
|
|
if (state != FINI && !(state == VALUE && !quotechar)) |
236 |
|
|
goto not_env; |
237 |
|
|
if (state == VALUE) { |
238 |
|
|
/* End of unquoted value: trim trailing whitespace */ |
239 |
|
|
c = val + strlen(val); |
240 |
|
|
while (c > val && isspace((unsigned char)c[-1])) |
241 |
|
|
*(--c) = '\0'; |
242 |
|
|
} |
243 |
|
|
|
244 |
|
|
/* 2 fields from parser; looks like an env setting */ |
245 |
|
|
|
246 |
|
|
/* |
247 |
|
|
* This can't overflow because get_string() limited the size of the |
248 |
|
|
* name and val fields. Still, it doesn't hurt to be careful... |
249 |
|
|
*/ |
250 |
|
|
if (snprintf(envstr, MAX_ENVSTR, "%s=%s", name, val) >= MAX_ENVSTR) |
251 |
|
|
goto not_env; |
252 |
|
|
return (TRUE); |
253 |
|
|
not_env: |
254 |
|
|
fseek(f, filepos, SEEK_SET); |
255 |
|
|
Set_LineNum(fileline); |
256 |
|
|
return (FALSE); |
257 |
|
|
} |