4
* Implementation of non-destructively reading/writing files containing
5
* only shell variable declarations and full-line comments.
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.
13
* Copyright 1999,2000 Red Hat, Inc.
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.
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.
25
* You should have received a copy of the GNU General Public License along
26
* with this program; if not, write to the Free Software Foundation, Inc.,
27
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
35
#include <sys/types.h>
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. */
45
svOpenFile(const char *name, gboolean create)
50
s = g_malloc0(sizeof(shvarFile));
54
s->fd = open(name, O_RDWR); /* NOT O_CREAT */
56
if (!create || s->fd == -1) {
58
s->fd = open(name, O_RDONLY); /* NOT O_CREAT */
59
if (s->fd != -1) closefd = 1;
61
s->fileName = g_strdup(name);
67
if (fstat(s->fd, &buf) < 0) goto bail;
68
s->arena = g_malloc0(buf.st_size + 1);
70
if (read(s->fd, s->arena, buf.st_size) < 0) goto bail;
72
/* we'd use g_strsplit() here, but we want a list, not an array */
73
for(p = s->arena; (q = strchr(p, '\n')) != NULL; p = q + 1) {
74
s->lineList = g_list_append(s->lineList, g_strndup(p, q - p));
77
/* closefd is set if we opened the file read-only, so go ahead and
78
close it, because we can't write to it anyway */
92
if (s->fd != -1) close(s->fd);
93
if (s->arena) g_free (s->arena);
94
if (s->fileName) g_free (s->fileName);
99
/* Open the file <name>, return shvarFile on success, NULL on failure */
101
svNewFile(const char *name)
103
return svOpenFile(name, FALSE);
106
/* Create a new file structure, returning actual data if the file exists,
107
* and a suitable starting point if it doesn't. */
109
svCreateFile(const char *name)
111
return svOpenFile(name, TRUE);
114
/* remove escaped characters in place */
120
if ((s[0] == '"' || s[0] == '\'') && s[0] == s[len-1]) {
130
for (i = 0; i < len; i++) {
132
memmove(s+i, s+i+1, len-(i+1));
140
/* create a new string with all necessary characters escaped.
141
* caller must free returned string
143
static const char escapees[] = "\"'\\$~`"; /* must be escaped */
144
static const char spaces[] = " \t|&;()<>"; /* only require "" */
146
escape(const char *s) {
148
int i, j, mangle = 0, space = 0;
150
static int esclen, splen;
152
if (!esclen) esclen = strlen(escapees);
153
if (!splen) splen = strlen(spaces);
156
for (i = 0; i < slen; i++) {
157
if (strchr(escapees, s[i])) mangle++;
158
if (strchr(spaces, s[i])) space++;
160
if (!mangle && !space) return strdup(s);
162
newlen = slen + mangle + 3; /* 3 is extra ""\0 */
163
new = g_malloc0(newlen);
164
if (!new) return NULL;
168
for (i = 0; i < slen; i++) {
169
if (strchr(escapees, s[i])) {
175
g_assert(j == slen + mangle + 2); /* j is the index of the '\0' */
180
/* Get the value associated with the key, and leave the current pointer
181
* pointing at the line containing the value. The char* returned MUST
182
* be freed by the caller.
185
svGetValue(shvarFile *s, const char *key)
195
keyString = g_malloc0(strlen(key) + 2);
196
strcpy(keyString, key);
197
keyString[strlen(key)] = '=';
198
len = strlen(keyString);
200
for (s->current = s->lineList; s->current; s->current = s->current->next) {
201
line = s->current->data;
202
if (!strncmp(keyString, line, len)) {
203
value = g_strdup(line + len);
218
if (s->parent) value = svGetValue(s->parent, key);
222
/* return 1 if <key> resolves to any truth value (e.g. "yes", "y", "true")
223
* return 0 if <key> resolves to any non-truth value (e.g. "no", "n", "false")
224
* return <default> otherwise
227
svTrueValue(shvarFile *s, const char *key, int def)
230
int returnValue = def;
232
tmp = svGetValue(s, key);
233
if (!tmp) return returnValue;
235
if ( (!strcasecmp("yes", tmp)) ||
236
(!strcasecmp("true", tmp)) ||
237
(!strcasecmp("t", tmp)) ||
238
(!strcasecmp("y", tmp)) ) returnValue = 1;
240
if ( (!strcasecmp("no", tmp)) ||
241
(!strcasecmp("false", tmp)) ||
242
(!strcasecmp("f", tmp)) ||
243
(!strcasecmp("n", tmp)) ) returnValue = 0;
250
/* Set the variable <key> equal to the value <value>.
251
* If <key> does not exist, and the <current> pointer is set, append
252
* the key=value pair after that line. Otherwise, prepend the pair
253
* to the top of the file. Here's the algorithm, as the C code
254
* seems to be rather dense:
256
* if (value == NULL), then:
257
* if val2 (parent): change line to key= or append line key=
258
* if val1 (this) : delete line
260
* else use this table:
263
* v NULL append line noop append line
265
* l value noop noop noop
267
* other change line delete line change line
269
* No changes are ever made to the parent config file, only to the
270
* specific file passed on the command line.
274
svSetValue(shvarFile *s, const char *key, const char *value)
276
char *newval = NULL, *val1 = NULL, *val2 = NULL;
281
/* value may be NULL */
283
if (value) newval = escape(value);
284
keyValue = g_strdup_printf("%s=%s", key, newval ? newval : "");
286
val1 = svGetValue(s, key);
287
if (val1 && newval && !strcmp(val1, newval)) goto bail;
288
if (s->parent) val2 = svGetValue(s->parent, key);
290
if (!newval || !newval[0]) {
291
/* delete value somehow */
293
/* change/append line to get key= */
294
if (s->current) s->current->data = keyValue;
295
else s->lineList = g_list_append(s->lineList, keyValue);
299
s->lineList = g_list_remove_link(s->lineList, s->current);
300
g_list_free_1(s->current);
302
goto bail; /* do not need keyValue */
308
if (val2 && !strcmp(val2, newval)) goto end;
310
s->lineList = g_list_append(s->lineList, keyValue);
315
/* deal with a whole line of noops */
316
if (val1 && !strcmp(val1, newval)) goto end;
318
/* At this point, val1 && val1 != value */
319
if (val2 && !strcmp(val2, newval)) {
321
s->lineList = g_list_remove_link(s->lineList, s->current);
322
g_list_free_1(s->current);
324
goto bail; /* do not need keyValue */
327
if (s->current) s->current->data = keyValue;
328
else s->lineList = g_list_append(s->lineList, keyValue);
333
if (newval) free(newval);
334
if (val1) free(val1);
335
if (val2) free(val2);
339
if (keyValue) free (keyValue);
343
/* Write the current contents iff modified. Returns -1 on error
344
* and 0 on success. Do not write if no values have been modified.
345
* The mode argument is only used if creating the file, not if
346
* re-writing an existing file, and is passed unchanged to the
350
svWriteFile(shvarFile *s, int mode)
357
s->fd = open(s->fileName, O_WRONLY|O_CREAT, mode);
360
if (ftruncate(s->fd, 0) < 0)
364
f = fdopen(tmpfd, "w");
365
fseek(f, 0, SEEK_SET);
366
for (s->current = s->lineList; s->current; s->current = s->current->next) {
367
char *line = s->current->data;
368
fprintf(f, "%s\n", line);
377
/* Close the file descriptor (if open) and delete the shvarFile.
378
* Returns -1 on error and 0 on success.
381
svCloseFile(shvarFile *s)
386
if (s->fd != -1) close(s->fd);
390
g_list_foreach (s->lineList, (GFunc) g_free, NULL);
391
g_list_free(s->lineList); /* implicitly frees s->current */