]> git.pld-linux.org Git - projects/rc-scripts.git/blob - src/shvar.c
- fixed lang.csh
[projects/rc-scripts.git] / src / shvar.c
1 /* copied from rp3 -- DO NOT EDIT HERE, ONLY COPY FROM rp3 */
2 /*
3  * shvar.c
4  *
5  * Implementation of non-destructively reading/writing files containing
6  * only shell variable declarations and full-line comments.
7  *
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.
13  *
14  * Copyright 1999 Red Hat, Inc.
15  *
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.
20  *
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.
25  *
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.
29  *
30  */
31
32 #include <assert.h>
33 #include <fcntl.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <sys/types.h>
38 #include <sys/stat.h>
39 #include <unistd.h>
40
41 #include "shvar.h"
42
43 /* Open the file <name>, return shvarFile on success, NULL on failure */
44 shvarFile *
45 svNewFile(char *name)
46 {
47     shvarFile *s = NULL;
48     int closefd = 0;
49
50     s = calloc(sizeof(shvarFile), 1);
51     if (!s) return NULL;
52
53     s->fd = open(name, O_RDWR); /* NOT O_CREAT */
54     if (s->fd == -1) {
55         /* try read-only */
56         s->fd = open(name, O_RDONLY); /* NOT O_CREAT */
57         if (s->fd) closefd = 1;
58     }
59     s->fileName = strdup(name);
60
61     if (s->fd != -1) {
62         struct stat buf;
63         char *tmp;
64
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...
71          */
72         tmp = strtok(s->arena, "\n");
73         while (tmp) {
74             s->lineList = g_list_append(s->lineList, tmp);
75             tmp = strtok(NULL, "\n");
76         }
77         if (closefd) {
78             close(s->fd);
79             s->fd = -1;
80         }
81     }
82
83     return s;
84
85 bail:
86     if (s->fd != -1) close(s->fd);
87     if (s->arena) free (s->arena);
88     if (s->fileName) free (s->fileName);
89     free (s);
90     return NULL;
91 }
92
93 /* remove escaped characters in place */
94 static void
95 unescape(char *s) {
96     int len, i;
97
98     len = strlen(s);
99     if ((s[0] == '"' || s[0] == '\'') && s[0] == s[len-1]) {
100         i = len - 2;
101         memmove(s, s+1, i);
102         s[i+1] = '\0';
103         len = i;
104     }
105     for (i = 0; i < len; i++) {
106         if (s[i] == '\\') {
107             memmove(s+i, s+i+1, len-(i+1));
108             len--;
109         }
110         s[len] = '\0';
111     }
112 }
113
114
115 /* create a new string with all necessary characters escaped.
116  * caller must free returned string
117  */
118 static const char escapees[] = "\"'\\$~`";      /* must be escaped */
119 static const char spaces[] = " \t";             /* only require "" */
120 static char *
121 escape(char *s) {
122     char *new;
123     int i, j, mangle = 0, space = 0;
124     int newlen, slen;
125     static int esclen, splen;
126
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++;
131     }
132     for (i = 0; i < splen; i++) {
133         if (strchr(s, spaces[i])) space++;
134     }
135     if (!mangle && !space) return strdup(s);
136
137     slen = strlen(s);
138     newlen = slen + mangle + 3; /* 3 is extra ""\0 */
139     new = calloc(newlen, 1);
140     if (!new) return NULL;
141
142     new[0] = '"';
143     for (i = 0, j = 1; i < slen; i++, j++) {
144         if (strchr(escapees, s[i])) {
145             new[j++] = '\\';
146         }
147         new[j] = s[i];
148     }
149     new[j] = '"';
150
151     return new;
152 }
153
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.
157  */
158 char *
159 svGetValue(shvarFile *s, char *key)
160 {
161     char *value = NULL;
162     char *line;
163     char *keyString;
164     int len;
165
166     assert(s);
167     assert(key);
168
169     keyString = calloc (strlen(key) + 2, 1);
170     if (!keyString) return NULL;
171     strcpy(keyString, key);
172     keyString[strlen(key)] = '=';
173     len = strlen(keyString);
174
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);
179             unescape(value);
180             break;
181         }
182     }
183     free(keyString);
184
185     if (value) {
186         if (value[0]) {
187             return value;
188         } else {
189             free (value);
190             return NULL;
191         }
192     }
193     if (s->parent) value = svGetValue(s->parent, key);
194     return value;
195 }
196
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
200  */
201 int
202 svTrueValue(shvarFile *s, char *key, int def)
203 {
204     char *tmp;
205     int returnValue = def;
206
207     tmp = svGetValue(s, key);
208     if (!tmp) return returnValue;
209
210     if ( (!strcasecmp("yes", tmp)) ||
211          (!strcasecmp("true", tmp)) ||
212          (!strcasecmp("t", tmp)) ||
213          (!strcasecmp("y", tmp)) ) returnValue = 1;
214     else
215     if ( (!strcasecmp("no", tmp)) ||
216          (!strcasecmp("false", tmp)) ||
217          (!strcasecmp("f", tmp)) ||
218          (!strcasecmp("n", tmp)) ) returnValue = 0;
219
220     free (tmp);
221     return returnValue;
222 }
223
224
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:
230  *
231  * if (value == NULL), then:
232  *     if val2 (parent): change line to key= or append line key=
233  *     if val1 (this)  : delete line
234  *     else noop
235  * else use this table:
236  *                                val2
237  *             NULL              value               other
238  * v   NULL    append line       noop                append line
239  * a
240  * l   value   noop              noop                noop
241  * 1
242  *     other   change line       delete line         change line
243  *
244  * No changes are ever made to the parent config file, only to the
245  * specific file passed on the command line.
246  *
247  */
248 void
249 svSetValue(shvarFile *s, char *key, char *value)
250 {
251     char *val1 = NULL, *val2 = NULL;
252     char *keyValue;
253
254     assert(s);
255     assert(key);
256     /* value may be NULL */
257
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:"");
262
263     val1 = svGetValue(s, key);
264     if (val1 && value && !strcmp(val1, value)) goto bail;
265     if (s->parent) val2 = svGetValue(s->parent, key);
266
267     if (!value) {
268         /* delete value somehow */
269         if (val2) {
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);
274             s->modified = 1;
275         } else if (val1) {
276             /* delete line */
277             s->lineList = g_list_remove_link(s->lineList, s->current);
278             g_list_free_1(s->current);
279             s->modified = 1;
280             goto bail; /* do not need keyValue */
281         }
282         goto end;
283     }
284
285     if (!val1) {
286         if (val2 && !strcmp(val2, value)) goto end;
287         /* append line */
288         s->lineList = g_list_append(s->lineList, keyValue);
289         s->freeList = g_list_append(s->freeList, keyValue);
290         s->modified = 1;
291         goto end;
292     }
293
294     /* deal with a whole line of noops */
295     if (val1 && !strcmp(val1, value)) goto end;
296
297     /* At this point, val1 && val1 != value */
298     if (val2 && !strcmp(val2, value)) {
299         /* delete line */
300         s->lineList = g_list_remove_link(s->lineList, s->current);
301         g_list_free_1(s->current);
302         s->modified = 1;
303         goto bail; /* do not need keyValue */
304     } else {
305         /* change line */
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);
309         s->modified = 1;
310     }
311
312 end:
313     if (value) free(value);
314     if (val1) free(val1);
315     if (val2) free(val2);
316     return;
317
318 bail:
319     if (keyValue) free (keyValue);
320     goto end;
321 }
322
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
327  * open() syscall.
328  */
329 int
330 svWriteFile(shvarFile *s, int mode)
331 {
332     FILE *f;
333     int tmpfd;
334
335     if (s->modified) {
336         if (s->fd == -1)
337             s->fd = open(s->fileName, O_WRONLY|O_CREAT, mode);
338         if (s->fd == -1)
339             return -1;
340         if (ftruncate(s->fd, 0) < 0)
341             return -1;
342
343         tmpfd = dup(s->fd);
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);
349         }
350         fclose(f);
351     }
352
353     return 0;
354 }
355
356  
357 /* Close the file descriptor (if open) and delete the shvarFile.
358  * Returns -1 on error and 0 on success.
359  */
360 int
361 svCloseFile(shvarFile *s)
362 {
363
364     assert(s);
365
366     if (s->fd != -1) close(s->fd);
367
368     free(s->arena);
369     for (s->current = s->freeList; s->current; s->current = s->current->next) {
370         free(s->current->data);
371     }
372     free(s->fileName);
373     g_list_free(s->freeList);
374     g_list_free(s->lineList); /* implicitly frees s->current */
375     free(s);
376     return 0;
377 }
This page took 0.996931 seconds and 3 git commands to generate.