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