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/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
43static properties
44property_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
69properties
70properties_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
231char *
232property_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
244void
245properties_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