]> git.pld-linux.org Git - projects/rc-scripts.git/blame - src/shvar.c
sysconfig/clock was missing
[projects/rc-scripts.git] / src / shvar.c
CommitLineData
458f14b7
AM
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 */
44shvarFile *
45svNewFile(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
85bail:
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 */
94static void
95unescape(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 */
118static const char escapees[] = "\"'\\$~`"; /* must be escaped */
119static const char spaces[] = " \t"; /* only require "" */
120static char *
121escape(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 */
158char *
159svGetValue(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 */
201int
202svTrueValue(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 */
248void
249svSetValue(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
312end:
313 if (value) free(value);
314 if (val1) free(val1);
315 if (val2) free(val2);
316 return;
317
318bail:
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 */
329int
330svWriteFile(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 */
360int
361svCloseFile(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.062277 seconds and 4 git commands to generate.