1 /* copied from rp3 -- DO NOT EDIT HERE, ONLY COPY FROM rp3 */
5 * Implementation of non-destructively reading/writing files containing
6 * only shell variable declarations and full-line comments.
8 * Includes explicit inheritance mechanism intended for use with
9 * Red Hat Linux ifcfg-* files. There is no protection against
10 * inheritance loops; they will generally cause stack overflows.
11 * Furthermore, they are only intended for one level of inheritance;
12 * the value setting algorithm assumes this.
14 * Copyright 1999 Red Hat, Inc.
16 * This is free software; you can redistribute it and/or modify it
17 * under the terms of the GNU General Public License as published by
18 * the Free Software Foundation; either version 2 of the License, or
19 * (at your option) any later version.
21 * This program is distributed in the hope that it will be useful, but
22 * WITHOUT ANY WARRANTY; without even the implied warranty of
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
24 * General Public License for more details.
26 * You should have received a copy of the GNU General Public License
27 * along with this program; if not, write to the Free Software
28 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
37 #include <sys/types.h>
43 /* Open the file <name>, return shvarFile on success, NULL on failure */
50 s = calloc(sizeof(shvarFile), 1);
53 s->fd = open(name, O_RDWR); /* NOT O_CREAT */
56 s->fd = open(name, O_RDONLY); /* NOT O_CREAT */
57 if (s->fd) closefd = 1;
59 s->fileName = strdup(name);
65 if (fstat(s->fd, &buf) < 0) goto bail;
66 s->arena = calloc(buf.st_size, 1);
67 if (!s->arena) goto bail;
68 if (read(s->fd, s->arena, buf.st_size) < 0) goto bail;
69 /* Yes, I know that strtok is evil, except that this is
70 * precisely what it was intended for in the first place...
72 tmp = strtok(s->arena, "\n");
74 s->lineList = g_list_append(s->lineList, tmp);
75 tmp = strtok(NULL, "\n");
86 if (s->fd != -1) close(s->fd);
87 if (s->arena) free (s->arena);
88 if (s->fileName) free (s->fileName);
93 /* remove escaped characters in place */
99 if ((s[0] == '"' || s[0] == '\'') && s[0] == s[len-1]) {
105 for (i = 0; i < len; i++) {
107 memmove(s+i, s+i+1, len-(i+1));
115 /* create a new string with all necessary characters escaped.
116 * caller must free returned string
118 static const char escapees[] = "\"'\\$~`"; /* must be escaped */
119 static const char spaces[] = " \t"; /* only require "" */
123 int i, j, mangle = 0, space = 0;
125 static int esclen, splen;
127 if (!esclen) esclen = strlen(escapees);
128 if (!splen) splen = strlen(spaces);
129 for (i = 0; i < esclen; i++) {
130 if (strchr(s, escapees[i])) mangle++;
132 for (i = 0; i < splen; i++) {
133 if (strchr(s, spaces[i])) space++;
135 if (!mangle && !space) return strdup(s);
138 newlen = slen + mangle + 3; /* 3 is extra ""\0 */
139 new = calloc(newlen, 1);
140 if (!new) return NULL;
143 for (i = 0, j = 1; i < slen; i++, j++) {
144 if (strchr(escapees, s[i])) {
154 /* Get the value associated with the key, and leave the current pointer
155 * pointing at the line containing the value. The char* returned MUST
156 * be freed by the caller.
159 svGetValue(shvarFile *s, char *key)
169 keyString = calloc (strlen(key) + 2, 1);
170 if (!keyString) return NULL;
171 strcpy(keyString, key);
172 keyString[strlen(key)] = '=';
173 len = strlen(keyString);
175 for (s->current = s->lineList; s->current; s->current = s->current->next) {
176 line = s->current->data;
177 if (!strncmp(keyString, line, len)) {
178 value = strdup(line + len);
193 if (s->parent) value = svGetValue(s->parent, key);
197 /* return 1 if <key> resolves to any truth value (e.g. "yes", "y", "true")
198 * return 0 if <key> resolves to any non-truth value (e.g. "no", "n", "false")
199 * return <default> otherwise
202 svTrueValue(shvarFile *s, char *key, int def)
205 int returnValue = def;
207 tmp = svGetValue(s, key);
208 if (!tmp) return returnValue;
210 if ( (!strcasecmp("yes", tmp)) ||
211 (!strcasecmp("true", tmp)) ||
212 (!strcasecmp("t", tmp)) ||
213 (!strcasecmp("y", tmp)) ) returnValue = 1;
215 if ( (!strcasecmp("no", tmp)) ||
216 (!strcasecmp("false", tmp)) ||
217 (!strcasecmp("f", tmp)) ||
218 (!strcasecmp("n", tmp)) ) returnValue = 0;
225 /* Set the variable <key> equal to the value <value>.
226 * If <key> does not exist, and the <current> pointer is set, append
227 * the key=value pair after that line. Otherwise, prepend the pair
228 * to the top of the file. Here's the algorithm, as the C code
229 * seems to be rather dense:
231 * if (value == NULL), then:
232 * if val2 (parent): change line to key= or append line key=
233 * if val1 (this) : delete line
235 * else use this table:
238 * v NULL append line noop append line
240 * l value noop noop noop
242 * other change line delete line change line
244 * No changes are ever made to the parent config file, only to the
245 * specific file passed on the command line.
249 svSetValue(shvarFile *s, char *key, char *value)
251 char *val1 = NULL, *val2 = NULL;
256 /* value may be NULL */
258 if (value) value = escape(value);
259 keyValue = malloc (strlen(key) + (value?strlen(value):0) + 2);
260 if (!keyValue) return;
261 sprintf(keyValue, "%s=%s", key, value?value:"");
263 val1 = svGetValue(s, key);
264 if (val1 && value && !strcmp(val1, value)) goto bail;
265 if (s->parent) val2 = svGetValue(s->parent, key);
268 /* delete value somehow */
270 /* change/append line to get key= */
271 if (s->current) s->current->data = keyValue;
272 else s->lineList = g_list_append(s->lineList, keyValue);
273 s->freeList = g_list_append(s->freeList, keyValue);
277 s->lineList = g_list_remove_link(s->lineList, s->current);
278 g_list_free_1(s->current);
280 goto bail; /* do not need keyValue */
286 if (val2 && !strcmp(val2, value)) goto end;
288 s->lineList = g_list_append(s->lineList, keyValue);
289 s->freeList = g_list_append(s->freeList, keyValue);
294 /* deal with a whole line of noops */
295 if (val1 && !strcmp(val1, value)) goto end;
297 /* At this point, val1 && val1 != value */
298 if (val2 && !strcmp(val2, value)) {
300 s->lineList = g_list_remove_link(s->lineList, s->current);
301 g_list_free_1(s->current);
303 goto bail; /* do not need keyValue */
306 if (s->current) s->current->data = keyValue;
307 else s->lineList = g_list_append(s->lineList, keyValue);
308 s->freeList = g_list_append(s->freeList, keyValue);
313 if (value) free(value);
314 if (val1) free(val1);
315 if (val2) free(val2);
319 if (keyValue) free (keyValue);
323 /* Write the current contents iff modified. Returns -1 on error
324 * and 0 on success. Do not write if no values have been modified.
325 * The mode argument is only used if creating the file, not if
326 * re-writing an existing file, and is passed unchanged to the
330 svWriteFile(shvarFile *s, int mode)
337 s->fd = open(s->fileName, O_WRONLY|O_CREAT, mode);
340 if (ftruncate(s->fd, 0) < 0)
344 f = fdopen(tmpfd, "w");
345 fseek(f, 0, SEEK_SET);
346 for (s->current = s->lineList; s->current; s->current = s->current->next) {
347 char *line = s->current->data;
348 fprintf(f, "%s\n", line);
357 /* Close the file descriptor (if open) and delete the shvarFile.
358 * Returns -1 on error and 0 on success.
361 svCloseFile(shvarFile *s)
366 if (s->fd != -1) close(s->fd);
369 for (s->current = s->freeList; s->current; s->current = s->current->next) {
370 free(s->current->data);
373 g_list_free(s->freeList);
374 g_list_free(s->lineList); /* implicitly frees s->current */