xref: /freebsd/lib/libutil/property.c (revision 1d386b48a555f61cb7325543adbbb5c3f3407a66)
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
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
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 *
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
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