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