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