property.c revision 102411
1/*
2 *
3 * Simple property list handling code.
4 *
5 * Copyright (c) 1998
6 *	Jordan Hubbard.  All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer,
13 *    verbatim and that no modifications are made prior to this
14 *    point in the file.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR HIS PETS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32#include <sys/cdefs.h>
33__FBSDID("$FreeBSD: head/lib/libutil/property.c 102411 2002-08-25 13:10:45Z charnier $");
34
35#include <ctype.h>
36#include <unistd.h>
37#include <stdlib.h>
38#include <stdio.h>
39#include <string.h>
40#include <err.h>
41#include <sys/types.h>
42#include <libutil.h>
43
44static properties
45property_alloc(char *name, char *value)
46{
47    properties n;
48
49    n = (properties)malloc(sizeof(struct _property));
50    n->next = NULL;
51    n->name = name ? strdup(name) : NULL;
52    n->value = value ? strdup(value) : NULL;
53    return n;
54}
55
56properties
57properties_read(int fd)
58{
59    properties head, ptr;
60    char hold_n[PROPERTY_MAX_NAME + 1];
61    char hold_v[PROPERTY_MAX_VALUE + 1];
62    char buf[BUFSIZ * 4];
63    int bp, n, v, max;
64    enum { LOOK, COMMENT, NAME, VALUE, MVALUE, COMMIT, FILL, STOP } state;
65    int ch = 0, blevel = 0;
66
67    n = v = bp = max = 0;
68    head = ptr = NULL;
69    state = LOOK;
70    while (state != STOP) {
71	if (state != COMMIT) {
72	    if (bp == max)
73		state = FILL;
74	    else
75		ch = buf[bp++];
76	}
77	switch(state) {
78	case FILL:
79	    if ((max = read(fd, buf, sizeof buf)) <= 0) {
80		state = STOP;
81		break;
82	    }
83	    else {
84		state = LOOK;
85		ch = buf[0];
86		bp = 1;
87	    }
88	    /* FALLTHROUGH deliberately since we already have a character and state == LOOK */
89
90	case LOOK:
91	    if (isspace(ch))
92		continue;
93	    /* Allow shell or lisp style comments */
94	    else if (ch == '#' || ch == ';') {
95		state = COMMENT;
96		continue;
97	    }
98	    else if (isalnum(ch) || ch == '_') {
99		if (n >= PROPERTY_MAX_NAME) {
100		    n = 0;
101		    state = COMMENT;
102		}
103		else {
104		    hold_n[n++] = ch;
105		    state = NAME;
106		}
107	    }
108	    else
109		state = COMMENT;	/* Ignore the rest of the line */
110	    break;
111
112	case COMMENT:
113	    if (ch == '\n')
114		state = LOOK;
115	    break;
116
117	case NAME:
118	    if (ch == '\n' || !ch) {
119		hold_n[n] = '\0';
120		hold_v[0] = '\0';
121		v = n = 0;
122		state = COMMIT;
123	    }
124	    else if (isspace(ch))
125		continue;
126	    else if (ch == '=') {
127		hold_n[n] = '\0';
128		v = n = 0;
129		state = VALUE;
130	    }
131	    else
132		hold_n[n++] = ch;
133	    break;
134
135	case VALUE:
136	    if (v == 0 && ch == '\n') {
137	        hold_v[v] = '\0';
138	        v = n = 0;
139	        state = COMMIT;
140	    }
141	    else if (v == 0 && isspace(ch))
142		continue;
143	    else if (ch == '{') {
144		state = MVALUE;
145		++blevel;
146	    }
147	    else if (ch == '\n' || !ch) {
148		hold_v[v] = '\0';
149		v = n = 0;
150		state = COMMIT;
151	    }
152	    else {
153		if (v >= PROPERTY_MAX_VALUE) {
154		    state = COMMENT;
155		    v = n = 0;
156		    break;
157		}
158		else
159		    hold_v[v++] = ch;
160	    }
161	    break;
162
163	case MVALUE:
164	    /* multiline value */
165	    if (v >= PROPERTY_MAX_VALUE) {
166		warn("properties_read: value exceeds max length");
167		state = COMMENT;
168		n = v = 0;
169	    }
170	    else if (ch == '}' && !--blevel) {
171		hold_v[v] = '\0';
172		v = n = 0;
173		state = COMMIT;
174	    }
175	    else {
176		hold_v[v++] = ch;
177		if (ch == '{')
178		    ++blevel;
179	    }
180	    break;
181
182	case COMMIT:
183	    if (!head)
184		head = ptr = property_alloc(hold_n, hold_v);
185	    else {
186		ptr->next = property_alloc(hold_n, hold_v);
187		ptr = ptr->next;
188	    }
189	    state = LOOK;
190	    v = n = 0;
191	    break;
192
193	case STOP:
194	    /* we don't handle this here, but this prevents warnings */
195	    break;
196	}
197    }
198    return head;
199}
200
201char *
202property_find(properties list, const char *name)
203{
204    if (!list || !name || !name[0])
205	return NULL;
206    while (list) {
207	if (!strcmp(list->name, name))
208	    return list->value;
209	list = list->next;
210    }
211    return NULL;
212}
213
214void
215properties_free(properties list)
216{
217    properties tmp;
218
219    while (list) {
220	tmp = list->next;
221	if (list->name)
222	    free(list->name);
223	if (list->value)
224	    free(list->value);
225	free(list);
226	list = tmp;
227    }
228}
229