source: projects/initscripts/tags/initscripts-8.91.3/src/shvar.c @ 1108

Revision 1108, 9.9 KB checked in by daisuke, 14 years ago (diff)

import initscripts-8.90.6 from internal cvs repository

Line 
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 *
13 * Copyright 1999,2000 Red Hat, Inc.
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
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
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)
46{
47    shvarFile *s = NULL;
48    int closefd = 0;
49
50    s = g_malloc0(sizeof(shvarFile));
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 */
56        if (s->fd != -1) closefd = 1;
57    }
58    s->fileName = g_strdup(name);
59
60    if (s->fd != -1) {
61        struct stat buf;
62        char *p, *q;
63
64        if (fstat(s->fd, &buf) < 0) goto bail;
65        s->arena = g_malloc0(buf.st_size + 1);
66
67        if (read(s->fd, s->arena, buf.st_size) < 0) goto bail;
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));
72        }
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 */
76        if (closefd) {
77            close(s->fd);
78            s->fd = -1;
79        }
80
81        return s;
82    }
83
84    if (create) {
85        return s;
86    }
87
88bail:
89    if (s->fd != -1) close(s->fd);
90    if (s->arena) g_free (s->arena);
91    if (s->fileName) g_free (s->fileName);
92    g_free (s);
93    return NULL;
94}
95
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
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 */
136static const char escapees[] = "\"'\\$~`";              /* must be escaped */
137static const char spaces[] = " \t|&;()<>";              /* only require "" */
138static char *
139escape(const char *s) {
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);
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++;
152    }
153    if (!mangle && !space) return strdup(s);
154
155    newlen = slen + mangle + 3; /* 3 is extra ""\0 */
156    new = g_malloc0(newlen);
157    if (!new) return NULL;
158
159    j = 0;
160    new[j++] = '"';
161    for (i = 0; i < slen; i++) {
162        if (strchr(escapees, s[i])) {
163            new[j++] = '\\';
164        }
165        new[j++] = s[i];
166    }
167    new[j++] = '"';
168    g_assert(j == slen + mangle + 2); /* j is the index of the '\0' */
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 *
178svGetValue(shvarFile *s, const char *key)
179{
180    char *value = NULL;
181    char *line;
182    char *keyString;
183    int len;
184
185    g_assert(s);
186    g_assert(key);
187
188    keyString = g_malloc0(strlen(key) + 2);
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)) {
196            value = g_strdup(line + len);
197            unescape(value);
198            break;
199        }
200    }
201    g_free(keyString);
202
203    if (value) {
204        if (value[0]) {
205            return value;
206        } else {
207            g_free(value);
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
220svTrueValue(shvarFile *s, const char *key, int def)
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
238    g_free (tmp);
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
267svSetValue(shvarFile *s, const char *key, const char *value)
268{
269    char *newval = NULL, *val1 = NULL, *val2 = NULL;
270    char *keyValue;
271
272    g_assert(s);
273    g_assert(key);
274    /* value may be NULL */
275
276    if (value) newval = escape(value);
277    keyValue = g_strdup_printf("%s=%s", key, newval ? newval : "");
278
279    val1 = svGetValue(s, key);
280    if (val1 && newval && !strcmp(val1, newval)) goto bail;
281    if (s->parent) val2 = svGetValue(s->parent, key);
282
283    if (!newval || !newval[0]) {
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) {
302        if (val2 && !strcmp(val2, newval)) goto end;
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 */
311    if (val1 && !strcmp(val1, newval)) goto end;
312
313    /* At this point, val1 && val1 != value */
314    if (val2 && !strcmp(val2, newval)) {
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:
329    if (newval) free(newval);
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
380    g_assert(s);
381
382    if (s->fd != -1) close(s->fd);
383
384    g_free(s->arena);
385    for (s->current = s->freeList; s->current; s->current = s->current->next) {
386        g_free(s->current->data);
387    }
388    g_free(s->fileName);
389    g_list_free(s->freeList);
390    g_list_free(s->lineList); /* implicitly frees s->current */
391    g_free(s);
392    return 0;
393}
Note: See TracBrowser for help on using the repository browser.