xref: /freebsd/lib/libutil/property.c (revision 2e3f49888ec8851bafb22011533217487764fdb0)
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 
43 static properties
44 property_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 
69 properties
70 properties_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 
231 char *
232 property_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 
244 void
245 properties_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