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 * $FreeBSD: head/lib/libutil/property.c 152886 2005-11-28 16:30:16Z jhb $
32 */
33
34 #include <sys/types.h>
35 #include <ctype.h>
36 #include <err.h>
37 #include <libutil.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <unistd.h>
42
43 static properties
property_alloc(char * name,char * value)44 property_alloc(char *name, char *value)
45 {
46 properties n;
47
48 if ((n = (properties)malloc(sizeof(struct _property))) == NULL)
49 return (NULL);
50 n->next = NULL;
51 if (name != NULL) {
52 if ((n->name = strdup(name)) == NULL) {
53 free(n);
54 return (NULL);
55 }
56 } else
57 n->name = NULL;
58 if (value != NULL) {
59 if ((n->value = strdup(value)) == NULL) {
60 free(n->name);
61 free(n);
62 return (NULL);
63 }
64 } else
65 n->value = NULL;
66 return (n);
67 }
68
69 properties
properties_read(int fd)70 properties_read(int fd)
71 {
72 properties head, ptr;
73 char hold_n[PROPERTY_MAX_NAME + 1];
74 char hold_v[PROPERTY_MAX_VALUE + 1];
75 char buf[BUFSIZ * 4];
76 int bp, n, v, max;
77 enum { LOOK, COMMENT, NAME, VALUE, MVALUE, COMMIT, FILL, STOP } state, last_state;
78 int ch = 0, blevel = 0;
79
80 n = v = bp = max = 0;
81 head = ptr = NULL;
82 state = last_state = LOOK;
83 while (state != STOP) {
84 if (state != COMMIT) {
85 if (bp == max) {
86 last_state = state;
87 state = FILL;
88 } else
89 ch = buf[bp++];
90 }
91 switch(state) {
92 case FILL:
93 if ((max = read(fd, buf, sizeof buf)) < 0) {
94 properties_free(head);
95 return (NULL);
96 }
97 if (max == 0) {
98 state = STOP;
99 } else {
100 /*
101 * Restore the state from before the fill (which will be
102 * initialised to LOOK for the first FILL). This ensures that
103 * if we were part-way through eg., a VALUE state, when the
104 * buffer ran out, that the previous operation will be allowed
105 * to complete.
106 */
107 state = last_state;
108 ch = buf[0];
109 bp = 0;
110 }
111 continue;
112
113 case LOOK:
114 if (isspace((unsigned char)ch))
115 continue;
116 /* Allow shell or lisp style comments */
117 else if (ch == '#' || ch == ';') {
118 state = COMMENT;
119 continue;
120 }
121 else if (isalnum((unsigned char)ch) || ch == '_') {
122 if (n >= PROPERTY_MAX_NAME) {
123 n = 0;
124 state = COMMENT;
125 }
126 else {
127 hold_n[n++] = ch;
128 state = NAME;
129 }
130 }
131 else
132 state = COMMENT; /* Ignore the rest of the line */
133 break;
134
135 case COMMENT:
136 if (ch == '\n')
137 state = LOOK;
138 break;
139
140 case NAME:
141 if (ch == '\n' || !ch) {
142 hold_n[n] = '\0';
143 hold_v[0] = '\0';
144 v = n = 0;
145 state = COMMIT;
146 }
147 else if (isspace((unsigned char)ch))
148 continue;
149 else if (ch == '=') {
150 hold_n[n] = '\0';
151 v = n = 0;
152 state = VALUE;
153 }
154 else
155 hold_n[n++] = ch;
156 break;
157
158 case VALUE:
159 if (v == 0 && ch == '\n') {
160 hold_v[v] = '\0';
161 v = n = 0;
162 state = COMMIT;
163 }
164 else if (v == 0 && isspace((unsigned char)ch))
165 continue;
166 else if (ch == '{') {
167 state = MVALUE;
168 ++blevel;
169 }
170 else if (ch == '\n' || !ch) {
171 hold_v[v] = '\0';
172 v = n = 0;
173 state = COMMIT;
174 }
175 else {
176 if (v >= PROPERTY_MAX_VALUE) {
177 state = COMMENT;
178 v = n = 0;
179 break;
180 }
181 else
182 hold_v[v++] = ch;
183 }
184 break;
185
186 case MVALUE:
187 /* multiline value */
188 if (v >= PROPERTY_MAX_VALUE) {
189 warn("properties_read: value exceeds max length");
190 state = COMMENT;
191 n = v = 0;
192 }
193 else if (ch == '}' && !--blevel) {
194 hold_v[v] = '\0';
195 v = n = 0;
196 state = COMMIT;
197 }
198 else {
199 hold_v[v++] = ch;
200 if (ch == '{')
201 ++blevel;
202 }
203 break;
204
205 case COMMIT:
206 if (head == NULL) {
207 if ((head = ptr = property_alloc(hold_n, hold_v)) == NULL)
208 return (NULL);
209 } else {
210 if ((ptr->next = property_alloc(hold_n, hold_v)) == NULL) {
211 properties_free(head);
212 return (NULL);
213 }
214 ptr = ptr->next;
215 }
216 state = LOOK;
217 v = n = 0;
218 break;
219
220 case STOP:
221 /* we don't handle this here, but this prevents warnings */
222 break;
223 }
224 }
225 if (head == NULL && (head = property_alloc(NULL, NULL)) == NULL)
226 return (NULL);
227
228 return (head);
229 }
230
231 char *
property_find(properties list,const char * name)232 property_find(properties list, const char *name)
233 {
234 if (list == NULL || name == NULL || !name[0])
235 return (NULL);
236 while (list != NULL) {
237 if (list->name != NULL && strcmp(list->name, name) == 0)
238 return (list->value);
239 list = list->next;
240 }
241 return (NULL);
242 }
243
244 void
properties_free(properties list)245 properties_free(properties list)
246 {
247 properties tmp;
248
249 while (list) {
250 tmp = list->next;
251 if (list->name)
252 free(list->name);
253 if (list->value)
254 free(list->value);
255 free(list);
256 list = tmp;
257 }
258 }
259