]> git.pld-linux.org Git - projects/rc-scripts.git/blame - src/shvar.c
- cleanups in sysinit + fix for shutting down bridge
[projects/rc-scripts.git] / src / shvar.c
CommitLineData
458f14b7
AM
1/*
2 * shvar.c
3 *
4 * Implementation of non-destructively reading/writing files containing
5 * only shell variable declarations and full-line comments.
6 *
7 * Includes explicit inheritance mechanism intended for use with
8 * Red Hat Linux ifcfg-* files. There is no protection against
9 * inheritance loops; they will generally cause stack overflows.
10 * Furthermore, they are only intended for one level of inheritance;
11 * the value setting algorithm assumes this.
12 *
aa8f8aa6 13 * Copyright 1999,2000 Red Hat, Inc.
458f14b7
AM
14 *
15 * This is free software; you can redistribute it and/or modify it
16 * under the terms of the GNU General Public License as published by
17 * the Free Software Foundation; either version 2 of the License, or
18 * (at your option) any later version.
19 *
20 * This program is distributed in the hope that it will be useful, but
21 * WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
23 * General Public License for more details.
24 *
25 * You should have received a copy of the GNU General Public License
26 * along with this program; if not, write to the Free Software
27 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
28 *
29 */
30
458f14b7
AM
31#include <fcntl.h>
32#include <stdio.h>
33#include <stdlib.h>
34#include <string.h>
35#include <sys/types.h>
36#include <sys/stat.h>
37#include <unistd.h>
38
39#include "shvar.h"
40
aa8f8aa6
AM
41/* Open the file <name>, returning a shvarFile on success and NULL on failure.
42 Add a wrinkle to let the caller specify whether or not to create the file
43 (actually, return a structure anyway) if it doesn't exist. */
44static shvarFile *
45svOpenFile(const char *name, gboolean create)
458f14b7
AM
46{
47 shvarFile *s = NULL;
48 int closefd = 0;
49
aa8f8aa6 50 s = g_malloc0(sizeof(shvarFile));
458f14b7
AM
51
52 s->fd = open(name, O_RDWR); /* NOT O_CREAT */
53 if (s->fd == -1) {
54 /* try read-only */
55 s->fd = open(name, O_RDONLY); /* NOT O_CREAT */
aa8f8aa6 56 if (s->fd != -1) closefd = 1;
458f14b7 57 }
aa8f8aa6 58 s->fileName = g_strdup(name);
458f14b7
AM
59
60 if (s->fd != -1) {
61 struct stat buf;
aa8f8aa6 62 char *p, *q;
458f14b7
AM
63
64 if (fstat(s->fd, &buf) < 0) goto bail;
aa8f8aa6
AM
65 s->arena = g_malloc0(buf.st_size + 1);
66
458f14b7 67 if (read(s->fd, s->arena, buf.st_size) < 0) goto bail;
aa8f8aa6
AM
68
69 /* we'd use g_strsplit() here, but we want a list, not an array */
70 for(p = s->arena; (q = strchr(p, '\n')) != NULL; p = q + 1) {
71 s->lineList = g_list_append(s->lineList, g_strndup(p, q - p));
458f14b7 72 }
aa8f8aa6
AM
73
74 /* closefd is set if we opened the file read-only, so go ahead and
75 close it, because we can't write to it anyway */
458f14b7
AM
76 if (closefd) {
77 close(s->fd);
78 s->fd = -1;
79 }
aa8f8aa6
AM
80
81 return s;
458f14b7
AM
82 }
83
aa8f8aa6
AM
84 if (create) {
85 return s;
86 }
458f14b7
AM
87
88bail:
89 if (s->fd != -1) close(s->fd);
aa8f8aa6
AM
90 if (s->arena) g_free (s->arena);
91 if (s->fileName) g_free (s->fileName);
92 g_free (s);
458f14b7
AM
93 return NULL;
94}
95
aa8f8aa6
AM
96/* Open the file <name>, return shvarFile on success, NULL on failure */
97shvarFile *
98svNewFile(const char *name)
99{
100 return svOpenFile(name, FALSE);
101}
102
103/* Create a new file structure, returning actual data if the file exists,
104 * and a suitable starting point if it doesn't. */
105shvarFile *
106svCreateFile(const char *name)
107{
108 return svOpenFile(name, TRUE);
109}
110
458f14b7
AM
111/* remove escaped characters in place */
112static void
113unescape(char *s) {
114 int len, i;
115
116 len = strlen(s);
117 if ((s[0] == '"' || s[0] == '\'') && s[0] == s[len-1]) {
118 i = len - 2;
119 memmove(s, s+1, i);
120 s[i+1] = '\0';
121 len = i;
122 }
123 for (i = 0; i < len; i++) {
124 if (s[i] == '\\') {
125 memmove(s+i, s+i+1, len-(i+1));
126 len--;
127 }
128 s[len] = '\0';
129 }
130}
131
132
133/* create a new string with all necessary characters escaped.
134 * caller must free returned string
135 */
aa8f8aa6
AM
136static const char escapees[] = "\"'\\$~`"; /* must be escaped */
137static const char spaces[] = " \t|&;()<>"; /* only require "" */
458f14b7 138static char *
aa8f8aa6 139escape(const char *s) {
458f14b7
AM
140 char *new;
141 int i, j, mangle = 0, space = 0;
142 int newlen, slen;
143 static int esclen, splen;
144
145 if (!esclen) esclen = strlen(escapees);
146 if (!splen) splen = strlen(spaces);
aa8f8aa6
AM
147 slen = strlen(s);
148
149 for (i = 0; i < slen; i++) {
150 if (strchr(escapees, s[i])) mangle++;
151 if (strchr(spaces, s[i])) space++;
458f14b7
AM
152 }
153 if (!mangle && !space) return strdup(s);
154
458f14b7 155 newlen = slen + mangle + 3; /* 3 is extra ""\0 */
aa8f8aa6 156 new = g_malloc0(newlen);
458f14b7
AM
157 if (!new) return NULL;
158
aa8f8aa6
AM
159 j = 0;
160 new[j++] = '"';
161 for (i = 0; i < slen; i++) {
458f14b7
AM
162 if (strchr(escapees, s[i])) {
163 new[j++] = '\\';
164 }
aa8f8aa6 165 new[j++] = s[i];
458f14b7 166 }
aa8f8aa6
AM
167 new[j++] = '"';
168 g_assert(j == slen + mangle + 2); /* j is the index of the '\0' */
458f14b7
AM
169
170 return new;
171}
172
173/* Get the value associated with the key, and leave the current pointer
174 * pointing at the line containing the value. The char* returned MUST
175 * be freed by the caller.
176 */
177char *
aa8f8aa6 178svGetValue(shvarFile *s, const char *key)
458f14b7
AM
179{
180 char *value = NULL;
181 char *line;
182 char *keyString;
183 int len;
184
aa8f8aa6
AM
185 g_assert(s);
186 g_assert(key);
458f14b7 187
aa8f8aa6 188 keyString = g_malloc0(strlen(key) + 2);
458f14b7
AM
189 strcpy(keyString, key);
190 keyString[strlen(key)] = '=';
191 len = strlen(keyString);
192
193 for (s->current = s->lineList; s->current; s->current = s->current->next) {
194 line = s->current->data;
195 if (!strncmp(keyString, line, len)) {
aa8f8aa6 196 value = g_strdup(line + len);
458f14b7
AM
197 unescape(value);
198 break;
199 }
200 }
aa8f8aa6 201 g_free(keyString);
458f14b7
AM
202
203 if (value) {
204 if (value[0]) {
205 return value;
206 } else {
aa8f8aa6 207 g_free(value);
458f14b7
AM
208 return NULL;
209 }
210 }
211 if (s->parent) value = svGetValue(s->parent, key);
212 return value;
213}
214
215/* return 1 if <key> resolves to any truth value (e.g. "yes", "y", "true")
216 * return 0 if <key> resolves to any non-truth value (e.g. "no", "n", "false")
217 * return <default> otherwise
218 */
219int
aa8f8aa6 220svTrueValue(shvarFile *s, const char *key, int def)
458f14b7
AM
221{
222 char *tmp;
223 int returnValue = def;
224
225 tmp = svGetValue(s, key);
226 if (!tmp) return returnValue;
227
228 if ( (!strcasecmp("yes", tmp)) ||
229 (!strcasecmp("true", tmp)) ||
230 (!strcasecmp("t", tmp)) ||
231 (!strcasecmp("y", tmp)) ) returnValue = 1;
232 else
233 if ( (!strcasecmp("no", tmp)) ||
234 (!strcasecmp("false", tmp)) ||
235 (!strcasecmp("f", tmp)) ||
236 (!strcasecmp("n", tmp)) ) returnValue = 0;
237
aa8f8aa6 238 g_free (tmp);
458f14b7
AM
239 return returnValue;
240}
241
242
243/* Set the variable <key> equal to the value <value>.
244 * If <key> does not exist, and the <current> pointer is set, append
245 * the key=value pair after that line. Otherwise, prepend the pair
246 * to the top of the file. Here's the algorithm, as the C code
247 * seems to be rather dense:
248 *
249 * if (value == NULL), then:
250 * if val2 (parent): change line to key= or append line key=
251 * if val1 (this) : delete line
252 * else noop
253 * else use this table:
254 * val2
255 * NULL value other
256 * v NULL append line noop append line
257 * a
258 * l value noop noop noop
259 * 1
260 * other change line delete line change line
261 *
262 * No changes are ever made to the parent config file, only to the
263 * specific file passed on the command line.
264 *
265 */
266void
aa8f8aa6 267svSetValue(shvarFile *s, const char *key, const char *value)
458f14b7 268{
aa8f8aa6 269 char *newval = NULL, *val1 = NULL, *val2 = NULL;
458f14b7
AM
270 char *keyValue;
271
aa8f8aa6
AM
272 g_assert(s);
273 g_assert(key);
458f14b7
AM
274 /* value may be NULL */
275
aa8f8aa6
AM
276 if (value) newval = escape(value);
277 keyValue = g_strdup_printf("%s=%s", key, newval ? newval : "");
458f14b7
AM
278
279 val1 = svGetValue(s, key);
aa8f8aa6 280 if (val1 && newval && !strcmp(val1, newval)) goto bail;
458f14b7
AM
281 if (s->parent) val2 = svGetValue(s->parent, key);
282
aa8f8aa6 283 if (!newval || !newval[0]) {
458f14b7
AM
284 /* delete value somehow */
285 if (val2) {
286 /* change/append line to get key= */
287 if (s->current) s->current->data = keyValue;
288 else s->lineList = g_list_append(s->lineList, keyValue);
289 s->freeList = g_list_append(s->freeList, keyValue);
290 s->modified = 1;
291 } else if (val1) {
292 /* delete line */
293 s->lineList = g_list_remove_link(s->lineList, s->current);
294 g_list_free_1(s->current);
295 s->modified = 1;
296 goto bail; /* do not need keyValue */
297 }
298 goto end;
299 }
300
301 if (!val1) {
aa8f8aa6 302 if (val2 && !strcmp(val2, newval)) goto end;
458f14b7
AM
303 /* append line */
304 s->lineList = g_list_append(s->lineList, keyValue);
305 s->freeList = g_list_append(s->freeList, keyValue);
306 s->modified = 1;
307 goto end;
308 }
309
310 /* deal with a whole line of noops */
aa8f8aa6 311 if (val1 && !strcmp(val1, newval)) goto end;
458f14b7
AM
312
313 /* At this point, val1 && val1 != value */
aa8f8aa6 314 if (val2 && !strcmp(val2, newval)) {
458f14b7
AM
315 /* delete line */
316 s->lineList = g_list_remove_link(s->lineList, s->current);
317 g_list_free_1(s->current);
318 s->modified = 1;
319 goto bail; /* do not need keyValue */
320 } else {
321 /* change line */
322 if (s->current) s->current->data = keyValue;
323 else s->lineList = g_list_append(s->lineList, keyValue);
324 s->freeList = g_list_append(s->freeList, keyValue);
325 s->modified = 1;
326 }
327
328end:
aa8f8aa6 329 if (newval) free(newval);
458f14b7
AM
330 if (val1) free(val1);
331 if (val2) free(val2);
332 return;
333
334bail:
335 if (keyValue) free (keyValue);
336 goto end;
337}
338
339/* Write the current contents iff modified. Returns -1 on error
340 * and 0 on success. Do not write if no values have been modified.
341 * The mode argument is only used if creating the file, not if
342 * re-writing an existing file, and is passed unchanged to the
343 * open() syscall.
344 */
345int
346svWriteFile(shvarFile *s, int mode)
347{
348 FILE *f;
349 int tmpfd;
350
351 if (s->modified) {
352 if (s->fd == -1)
353 s->fd = open(s->fileName, O_WRONLY|O_CREAT, mode);
354 if (s->fd == -1)
355 return -1;
356 if (ftruncate(s->fd, 0) < 0)
357 return -1;
358
359 tmpfd = dup(s->fd);
360 f = fdopen(tmpfd, "w");
361 fseek(f, 0, SEEK_SET);
362 for (s->current = s->lineList; s->current; s->current = s->current->next) {
363 char *line = s->current->data;
364 fprintf(f, "%s\n", line);
365 }
366 fclose(f);
367 }
368
369 return 0;
370}
371
372
373/* Close the file descriptor (if open) and delete the shvarFile.
374 * Returns -1 on error and 0 on success.
375 */
376int
377svCloseFile(shvarFile *s)
378{
379
aa8f8aa6 380 g_assert(s);
458f14b7
AM
381
382 if (s->fd != -1) close(s->fd);
383
aa8f8aa6 384 g_free(s->arena);
458f14b7 385 for (s->current = s->freeList; s->current; s->current = s->current->next) {
aa8f8aa6 386 g_free(s->current->data);
458f14b7 387 }
aa8f8aa6 388 g_free(s->fileName);
458f14b7
AM
389 g_list_free(s->freeList);
390 g_list_free(s->lineList); /* implicitly frees s->current */
aa8f8aa6 391 g_free(s);
458f14b7
AM
392 return 0;
393}
This page took 0.110284 seconds and 4 git commands to generate.