]>
git.ipfire.org Git - ipfire-3.x.git/blob - initscripts/src/shvar.c
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
26 * along with this program; if not, write to the Free Software
27 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, 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
));
52 s
->fd
= open(name
, O_RDWR
); /* NOT O_CREAT */
55 s
->fd
= open(name
, O_RDONLY
); /* NOT O_CREAT */
56 if (s
->fd
!= -1) closefd
= 1;
58 s
->fileName
= g_strdup(name
);
64 if (fstat(s
->fd
, &buf
) < 0) goto bail
;
65 s
->arena
= g_malloc0(buf
.st_size
+ 1);
67 if (read(s
->fd
, s
->arena
, buf
.st_size
) < 0) goto bail
;
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
));
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 */
89 if (s
->fd
!= -1) close(s
->fd
);
90 if (s
->arena
) g_free (s
->arena
);
91 if (s
->fileName
) g_free (s
->fileName
);
96 /* Open the file <name>, return shvarFile on success, NULL on failure */
98 svNewFile(const char *name
)
100 return svOpenFile(name
, FALSE
);
103 /* Create a new file structure, returning actual data if the file exists,
104 * and a suitable starting point if it doesn't. */
106 svCreateFile(const char *name
)
108 return svOpenFile(name
, TRUE
);
111 /* remove escaped characters in place */
117 if ((s
[0] == '"' || s
[0] == '\'') && s
[0] == s
[len
-1]) {
123 for (i
= 0; i
< len
; i
++) {
125 memmove(s
+i
, s
+i
+1, len
-(i
+1));
133 /* create a new string with all necessary characters escaped.
134 * caller must free returned string
136 static const char escapees
[] = "\"'\\$~`"; /* must be escaped */
137 static const char spaces
[] = " \t|&;()<>"; /* only require "" */
139 escape(const char *s
) {
141 int i
, j
, mangle
= 0, space
= 0;
143 static int esclen
, splen
;
145 if (!esclen
) esclen
= strlen(escapees
);
146 if (!splen
) splen
= strlen(spaces
);
149 for (i
= 0; i
< slen
; i
++) {
150 if (strchr(escapees
, s
[i
])) mangle
++;
151 if (strchr(spaces
, s
[i
])) space
++;
153 if (!mangle
&& !space
) return strdup(s
);
155 newlen
= slen
+ mangle
+ 3; /* 3 is extra ""\0 */
156 new = g_malloc0(newlen
);
157 if (!new) return NULL
;
161 for (i
= 0; i
< slen
; i
++) {
162 if (strchr(escapees
, s
[i
])) {
168 g_assert(j
== slen
+ mangle
+ 2); /* j is the index of the '\0' */
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.
178 svGetValue(shvarFile
*s
, const char *key
)
188 keyString
= g_malloc0(strlen(key
) + 2);
189 strcpy(keyString
, key
);
190 keyString
[strlen(key
)] = '=';
191 len
= strlen(keyString
);
193 for (s
->current
= s
->lineList
; s
->current
; s
->current
= s
->current
->next
) {
194 line
= s
->current
->data
;
195 if (!strncmp(keyString
, line
, len
)) {
196 value
= g_strdup(line
+ len
);
211 if (s
->parent
) value
= svGetValue(s
->parent
, key
);
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
220 svTrueValue(shvarFile
*s
, const char *key
, int def
)
223 int returnValue
= def
;
225 tmp
= svGetValue(s
, key
);
226 if (!tmp
) return returnValue
;
228 if ( (!strcasecmp("yes", tmp
)) ||
229 (!strcasecmp("true", tmp
)) ||
230 (!strcasecmp("t", tmp
)) ||
231 (!strcasecmp("y", tmp
)) ) returnValue
= 1;
233 if ( (!strcasecmp("no", tmp
)) ||
234 (!strcasecmp("false", tmp
)) ||
235 (!strcasecmp("f", tmp
)) ||
236 (!strcasecmp("n", tmp
)) ) returnValue
= 0;
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:
249 * if (value == NULL), then:
250 * if val2 (parent): change line to key= or append line key=
251 * if val1 (this) : delete line
253 * else use this table:
256 * v NULL append line noop append line
258 * l value noop noop noop
260 * other change line delete line change line
262 * No changes are ever made to the parent config file, only to the
263 * specific file passed on the command line.
267 svSetValue(shvarFile
*s
, const char *key
, const char *value
)
269 char *newval
= NULL
, *val1
= NULL
, *val2
= NULL
;
274 /* value may be NULL */
276 if (value
) newval
= escape(value
);
277 keyValue
= g_strdup_printf("%s=%s", key
, newval
? newval
: "");
279 val1
= svGetValue(s
, key
);
280 if (val1
&& newval
&& !strcmp(val1
, newval
)) goto bail
;
281 if (s
->parent
) val2
= svGetValue(s
->parent
, key
);
283 if (!newval
|| !newval
[0]) {
284 /* delete value somehow */
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
);
293 s
->lineList
= g_list_remove_link(s
->lineList
, s
->current
);
294 g_list_free_1(s
->current
);
296 goto bail
; /* do not need keyValue */
302 if (val2
&& !strcmp(val2
, newval
)) goto end
;
304 s
->lineList
= g_list_append(s
->lineList
, keyValue
);
305 s
->freeList
= g_list_append(s
->freeList
, keyValue
);
310 /* deal with a whole line of noops */
311 if (val1
&& !strcmp(val1
, newval
)) goto end
;
313 /* At this point, val1 && val1 != value */
314 if (val2
&& !strcmp(val2
, newval
)) {
316 s
->lineList
= g_list_remove_link(s
->lineList
, s
->current
);
317 g_list_free_1(s
->current
);
319 goto bail
; /* do not need keyValue */
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
);
329 if (newval
) free(newval
);
330 if (val1
) free(val1
);
331 if (val2
) free(val2
);
335 if (keyValue
) free (keyValue
);
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
346 svWriteFile(shvarFile
*s
, int mode
)
353 s
->fd
= open(s
->fileName
, O_WRONLY
|O_CREAT
, mode
);
356 if (ftruncate(s
->fd
, 0) < 0)
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
);
373 /* Close the file descriptor (if open) and delete the shvarFile.
374 * Returns -1 on error and 0 on success.
377 svCloseFile(shvarFile
*s
)
382 if (s
->fd
!= -1) close(s
->fd
);
385 for (s
->current
= s
->freeList
; s
->current
; s
->current
= s
->current
->next
) {
386 g_free(s
->current
->data
);
389 g_list_free(s
->freeList
);
390 g_list_free(s
->lineList
); /* implicitly frees s->current */