xref: /freebsd/lib/libutil/property.c (revision 74bf4e164ba5851606a27d4feff27717452583e5)
1 /*
2  *
3  * Simple property list handling code.
4  *
5  * Copyright (c) 1998
6  *	Jordan Hubbard.  All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer,
13  *    verbatim and that no modifications are made prior to this
14  *    point in the file.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR HIS PETS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31 
32 #include <sys/cdefs.h>
33 __FBSDID("$FreeBSD$");
34 
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;
79     int ch = 0, blevel = 0;
80 
81     n = v = bp = max = 0;
82     head = ptr = NULL;
83     state = LOOK;
84     while (state != STOP) {
85 	if (state != COMMIT) {
86 	    if (bp == max)
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 		break;
100 	    } else {
101 		state = LOOK;
102 		ch = buf[0];
103 		bp = 1;
104 	    }
105 	    /* FALLTHROUGH deliberately since we already have a character and state == LOOK */
106 
107 	case LOOK:
108 	    if (isspace((unsigned char)ch))
109 		continue;
110 	    /* Allow shell or lisp style comments */
111 	    else if (ch == '#' || ch == ';') {
112 		state = COMMENT;
113 		continue;
114 	    }
115 	    else if (isalnum((unsigned char)ch) || ch == '_') {
116 		if (n >= PROPERTY_MAX_NAME) {
117 		    n = 0;
118 		    state = COMMENT;
119 		}
120 		else {
121 		    hold_n[n++] = ch;
122 		    state = NAME;
123 		}
124 	    }
125 	    else
126 		state = COMMENT;	/* Ignore the rest of the line */
127 	    break;
128 
129 	case COMMENT:
130 	    if (ch == '\n')
131 		state = LOOK;
132 	    break;
133 
134 	case NAME:
135 	    if (ch == '\n' || !ch) {
136 		hold_n[n] = '\0';
137 		hold_v[0] = '\0';
138 		v = n = 0;
139 		state = COMMIT;
140 	    }
141 	    else if (isspace((unsigned char)ch))
142 		continue;
143 	    else if (ch == '=') {
144 		hold_n[n] = '\0';
145 		v = n = 0;
146 		state = VALUE;
147 	    }
148 	    else
149 		hold_n[n++] = ch;
150 	    break;
151 
152 	case VALUE:
153 	    if (v == 0 && ch == '\n') {
154 	        hold_v[v] = '\0';
155 	        v = n = 0;
156 	        state = COMMIT;
157 	    }
158 	    else if (v == 0 && isspace((unsigned char)ch))
159 		continue;
160 	    else if (ch == '{') {
161 		state = MVALUE;
162 		++blevel;
163 	    }
164 	    else if (ch == '\n' || !ch) {
165 		hold_v[v] = '\0';
166 		v = n = 0;
167 		state = COMMIT;
168 	    }
169 	    else {
170 		if (v >= PROPERTY_MAX_VALUE) {
171 		    state = COMMENT;
172 		    v = n = 0;
173 		    break;
174 		}
175 		else
176 		    hold_v[v++] = ch;
177 	    }
178 	    break;
179 
180 	case MVALUE:
181 	    /* multiline value */
182 	    if (v >= PROPERTY_MAX_VALUE) {
183 		warn("properties_read: value exceeds max length");
184 		state = COMMENT;
185 		n = v = 0;
186 	    }
187 	    else if (ch == '}' && !--blevel) {
188 		hold_v[v] = '\0';
189 		v = n = 0;
190 		state = COMMIT;
191 	    }
192 	    else {
193 		hold_v[v++] = ch;
194 		if (ch == '{')
195 		    ++blevel;
196 	    }
197 	    break;
198 
199 	case COMMIT:
200 	    if (head == NULL) {
201 		if ((head = ptr = property_alloc(hold_n, hold_v)) == NULL)
202 		    return (NULL);
203 	    } else {
204 		if ((ptr->next = property_alloc(hold_n, hold_v)) == NULL) {
205 		    properties_free(head);
206 		    return (NULL);
207 		}
208 		ptr = ptr->next;
209 	    }
210 	    state = LOOK;
211 	    v = n = 0;
212 	    break;
213 
214 	case STOP:
215 	    /* we don't handle this here, but this prevents warnings */
216 	    break;
217 	}
218     }
219     if (head == NULL && (head = property_alloc(NULL, NULL)) == NULL)
220 	return (NULL);
221 
222     return (head);
223 }
224 
225 char *
226 property_find(properties list, const char *name)
227 {
228     if (list == NULL || name == NULL || !name[0])
229 	return (NULL);
230     while (list != NULL) {
231 	if (list->name != NULL && strcmp(list->name, name) == 0)
232 	    return (list->value);
233 	list = list->next;
234     }
235     return (NULL);
236 }
237 
238 void
239 properties_free(properties list)
240 {
241     properties tmp;
242 
243     while (list) {
244 	tmp = list->next;
245 	if (list->name)
246 	    free(list->name);
247 	if (list->value)
248 	    free(list->value);
249 	free(list);
250 	list = tmp;
251     }
252 }
253