xref: /openbsd/usr.sbin/cron/env.c (revision 4cfece93)
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 }
258