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