xref: /freebsd/lib/libutil/property.c (revision 5e53a4f90f82c4345f277dd87cc9292f26e04a29)
1*5e53a4f9SPedro F. Giffuni /*-
2*5e53a4f9SPedro F. Giffuni  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3*5e53a4f9SPedro F. Giffuni  *
4b704025fSJordan K. Hubbard  *
5b704025fSJordan K. Hubbard  * Simple property list handling code.
6b704025fSJordan K. Hubbard  *
7b704025fSJordan K. Hubbard  * Copyright (c) 1998
8b704025fSJordan K. Hubbard  *	Jordan Hubbard.  All rights reserved.
9b704025fSJordan K. Hubbard  *
10b704025fSJordan K. Hubbard  * Redistribution and use in source and binary forms, with or without
11b704025fSJordan K. Hubbard  * modification, are permitted provided that the following conditions
12b704025fSJordan K. Hubbard  * are met:
13b704025fSJordan K. Hubbard  * 1. Redistributions of source code must retain the above copyright
14b704025fSJordan K. Hubbard  *    notice, this list of conditions and the following disclaimer,
15b704025fSJordan K. Hubbard  *    verbatim and that no modifications are made prior to this
16b704025fSJordan K. Hubbard  *    point in the file.
17b704025fSJordan K. Hubbard  * 2. Redistributions in binary form must reproduce the above copyright
18b704025fSJordan K. Hubbard  *    notice, this list of conditions and the following disclaimer in the
19b704025fSJordan K. Hubbard  *    documentation and/or other materials provided with the distribution.
20b704025fSJordan K. Hubbard  *
21b704025fSJordan K. Hubbard  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22b704025fSJordan K. Hubbard  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23b704025fSJordan K. Hubbard  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24b704025fSJordan K. Hubbard  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR HIS PETS BE LIABLE
25b704025fSJordan K. Hubbard  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26b704025fSJordan K. Hubbard  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27b704025fSJordan K. Hubbard  * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
28b704025fSJordan K. Hubbard  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29b704025fSJordan K. Hubbard  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30b704025fSJordan K. Hubbard  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31b704025fSJordan K. Hubbard  * SUCH DAMAGE.
32b704025fSJordan K. Hubbard  */
33b704025fSJordan K. Hubbard 
348719c58fSMatthew Dillon #include <sys/cdefs.h>
358719c58fSMatthew Dillon __FBSDID("$FreeBSD$");
368719c58fSMatthew Dillon 
3769761016SJordan K. Hubbard #include <sys/types.h>
380ebec5d3SMark Murray #include <ctype.h>
390ebec5d3SMark Murray #include <err.h>
40b704025fSJordan K. Hubbard #include <libutil.h>
410ebec5d3SMark Murray #include <stdio.h>
420ebec5d3SMark Murray #include <stdlib.h>
430ebec5d3SMark Murray #include <string.h>
440ebec5d3SMark Murray #include <unistd.h>
45b704025fSJordan K. Hubbard 
46b704025fSJordan K. Hubbard static properties
47b704025fSJordan K. Hubbard property_alloc(char *name, char *value)
48b704025fSJordan K. Hubbard {
49b704025fSJordan K. Hubbard     properties n;
50b704025fSJordan K. Hubbard 
51779092a4SAndrey A. Chernov     if ((n = (properties)malloc(sizeof(struct _property))) == NULL)
52779092a4SAndrey A. Chernov 	return (NULL);
53b704025fSJordan K. Hubbard     n->next = NULL;
54779092a4SAndrey A. Chernov     if (name != NULL) {
55779092a4SAndrey A. Chernov 	if ((n->name = strdup(name)) == NULL) {
56779092a4SAndrey A. Chernov 	    free(n);
57779092a4SAndrey A. Chernov 	    return (NULL);
58779092a4SAndrey A. Chernov 	}
59779092a4SAndrey A. Chernov     } else
60779092a4SAndrey A. Chernov 	n->name = NULL;
61779092a4SAndrey A. Chernov     if (value != NULL) {
62779092a4SAndrey A. Chernov 	if ((n->value = strdup(value)) == NULL) {
63779092a4SAndrey A. Chernov 	    free(n->name);
64779092a4SAndrey A. Chernov 	    free(n);
65779092a4SAndrey A. Chernov 	    return (NULL);
66779092a4SAndrey A. Chernov 	}
67779092a4SAndrey A. Chernov     } else
68779092a4SAndrey A. Chernov 	n->value = NULL;
69779092a4SAndrey A. Chernov     return (n);
70b704025fSJordan K. Hubbard }
71b704025fSJordan K. Hubbard 
72b704025fSJordan K. Hubbard properties
7350dfa596SJordan K. Hubbard properties_read(int fd)
74b704025fSJordan K. Hubbard {
75b704025fSJordan K. Hubbard     properties head, ptr;
76f9f81f78SMurray Stokely     char hold_n[PROPERTY_MAX_NAME + 1];
77f9f81f78SMurray Stokely     char hold_v[PROPERTY_MAX_VALUE + 1];
78b704025fSJordan K. Hubbard     char buf[BUFSIZ * 4];
79b704025fSJordan K. Hubbard     int bp, n, v, max;
80a54bb702SJohn Baldwin     enum { LOOK, COMMENT, NAME, VALUE, MVALUE, COMMIT, FILL, STOP } state, last_state;
81efbcb4aeSJordan K. Hubbard     int ch = 0, blevel = 0;
82b704025fSJordan K. Hubbard 
83b704025fSJordan K. Hubbard     n = v = bp = max = 0;
84b704025fSJordan K. Hubbard     head = ptr = NULL;
85a54bb702SJohn Baldwin     state = last_state = LOOK;
86b704025fSJordan K. Hubbard     while (state != STOP) {
87b704025fSJordan K. Hubbard 	if (state != COMMIT) {
88a54bb702SJohn Baldwin 	    if (bp == max) {
89a54bb702SJohn Baldwin 		last_state = state;
90b704025fSJordan K. Hubbard 		state = FILL;
91a54bb702SJohn Baldwin 	    } else
92b704025fSJordan K. Hubbard 		ch = buf[bp++];
93b704025fSJordan K. Hubbard 	}
94b704025fSJordan K. Hubbard 	switch(state) {
95b704025fSJordan K. Hubbard 	case FILL:
9633a155e4SAndrey A. Chernov 	    if ((max = read(fd, buf, sizeof buf)) < 0) {
9733a155e4SAndrey A. Chernov 		properties_free(head);
9833a155e4SAndrey A. Chernov 		return (NULL);
9933a155e4SAndrey A. Chernov 	    }
10033a155e4SAndrey A. Chernov 	    if (max == 0) {
101b704025fSJordan K. Hubbard 		state = STOP;
10233a155e4SAndrey A. Chernov 	    } else {
103a54bb702SJohn Baldwin 		/*
104a54bb702SJohn Baldwin 		 * Restore the state from before the fill (which will be
105a54bb702SJohn Baldwin 		 * initialised to LOOK for the first FILL). This ensures that
106a54bb702SJohn Baldwin 		 * if we were part-way through eg., a VALUE state, when the
107a54bb702SJohn Baldwin 		 * buffer ran out, that the previous operation will be allowed
108a54bb702SJohn Baldwin 		 * to complete.
109a54bb702SJohn Baldwin 		 */
110a54bb702SJohn Baldwin 		state = last_state;
111b704025fSJordan K. Hubbard 		ch = buf[0];
112a54bb702SJohn Baldwin 		bp = 0;
113b704025fSJordan K. Hubbard 	    }
114a54bb702SJohn Baldwin 	    continue;
115b704025fSJordan K. Hubbard 
116b704025fSJordan K. Hubbard 	case LOOK:
117779092a4SAndrey A. Chernov 	    if (isspace((unsigned char)ch))
118b704025fSJordan K. Hubbard 		continue;
119b704025fSJordan K. Hubbard 	    /* Allow shell or lisp style comments */
120b704025fSJordan K. Hubbard 	    else if (ch == '#' || ch == ';') {
121b704025fSJordan K. Hubbard 		state = COMMENT;
122b704025fSJordan K. Hubbard 		continue;
123b704025fSJordan K. Hubbard 	    }
124779092a4SAndrey A. Chernov 	    else if (isalnum((unsigned char)ch) || ch == '_') {
125f9f81f78SMurray Stokely 		if (n >= PROPERTY_MAX_NAME) {
126b704025fSJordan K. Hubbard 		    n = 0;
127b704025fSJordan K. Hubbard 		    state = COMMENT;
128b704025fSJordan K. Hubbard 		}
129b704025fSJordan K. Hubbard 		else {
130b704025fSJordan K. Hubbard 		    hold_n[n++] = ch;
131b704025fSJordan K. Hubbard 		    state = NAME;
132b704025fSJordan K. Hubbard 		}
133b704025fSJordan K. Hubbard 	    }
134b704025fSJordan K. Hubbard 	    else
135b704025fSJordan K. Hubbard 		state = COMMENT;	/* Ignore the rest of the line */
136b704025fSJordan K. Hubbard 	    break;
137b704025fSJordan K. Hubbard 
138b704025fSJordan K. Hubbard 	case COMMENT:
139b704025fSJordan K. Hubbard 	    if (ch == '\n')
140b704025fSJordan K. Hubbard 		state = LOOK;
141b704025fSJordan K. Hubbard 	    break;
142b704025fSJordan K. Hubbard 
143b704025fSJordan K. Hubbard 	case NAME:
144b704025fSJordan K. Hubbard 	    if (ch == '\n' || !ch) {
145b704025fSJordan K. Hubbard 		hold_n[n] = '\0';
146b704025fSJordan K. Hubbard 		hold_v[0] = '\0';
147b704025fSJordan K. Hubbard 		v = n = 0;
148b704025fSJordan K. Hubbard 		state = COMMIT;
149b704025fSJordan K. Hubbard 	    }
150779092a4SAndrey A. Chernov 	    else if (isspace((unsigned char)ch))
151b704025fSJordan K. Hubbard 		continue;
152b704025fSJordan K. Hubbard 	    else if (ch == '=') {
153b704025fSJordan K. Hubbard 		hold_n[n] = '\0';
154b704025fSJordan K. Hubbard 		v = n = 0;
155b704025fSJordan K. Hubbard 		state = VALUE;
156b704025fSJordan K. Hubbard 	    }
157b704025fSJordan K. Hubbard 	    else
158b704025fSJordan K. Hubbard 		hold_n[n++] = ch;
159b704025fSJordan K. Hubbard 	    break;
160b704025fSJordan K. Hubbard 
161b704025fSJordan K. Hubbard 	case VALUE:
16240863abaSMurray Stokely 	    if (v == 0 && ch == '\n') {
16340863abaSMurray Stokely 	        hold_v[v] = '\0';
16440863abaSMurray Stokely 	        v = n = 0;
16540863abaSMurray Stokely 	        state = COMMIT;
16640863abaSMurray Stokely 	    }
167779092a4SAndrey A. Chernov 	    else if (v == 0 && isspace((unsigned char)ch))
168b704025fSJordan K. Hubbard 		continue;
169efbcb4aeSJordan K. Hubbard 	    else if (ch == '{') {
170b704025fSJordan K. Hubbard 		state = MVALUE;
171efbcb4aeSJordan K. Hubbard 		++blevel;
172efbcb4aeSJordan K. Hubbard 	    }
173b704025fSJordan K. Hubbard 	    else if (ch == '\n' || !ch) {
174b704025fSJordan K. Hubbard 		hold_v[v] = '\0';
175b704025fSJordan K. Hubbard 		v = n = 0;
176b704025fSJordan K. Hubbard 		state = COMMIT;
177b704025fSJordan K. Hubbard 	    }
178b704025fSJordan K. Hubbard 	    else {
179f9f81f78SMurray Stokely 		if (v >= PROPERTY_MAX_VALUE) {
180b704025fSJordan K. Hubbard 		    state = COMMENT;
181b704025fSJordan K. Hubbard 		    v = n = 0;
182b704025fSJordan K. Hubbard 		    break;
183b704025fSJordan K. Hubbard 		}
184b704025fSJordan K. Hubbard 		else
185b704025fSJordan K. Hubbard 		    hold_v[v++] = ch;
186b704025fSJordan K. Hubbard 	    }
187b704025fSJordan K. Hubbard 	    break;
188b704025fSJordan K. Hubbard 
189b704025fSJordan K. Hubbard 	case MVALUE:
190b704025fSJordan K. Hubbard 	    /* multiline value */
191f9f81f78SMurray Stokely 	    if (v >= PROPERTY_MAX_VALUE) {
192efbcb4aeSJordan K. Hubbard 		warn("properties_read: value exceeds max length");
193b704025fSJordan K. Hubbard 		state = COMMENT;
194b704025fSJordan K. Hubbard 		n = v = 0;
195b704025fSJordan K. Hubbard 	    }
196efbcb4aeSJordan K. Hubbard 	    else if (ch == '}' && !--blevel) {
197b704025fSJordan K. Hubbard 		hold_v[v] = '\0';
198b704025fSJordan K. Hubbard 		v = n = 0;
199b704025fSJordan K. Hubbard 		state = COMMIT;
200b704025fSJordan K. Hubbard 	    }
201efbcb4aeSJordan K. Hubbard 	    else {
202b704025fSJordan K. Hubbard 		hold_v[v++] = ch;
203efbcb4aeSJordan K. Hubbard 		if (ch == '{')
204efbcb4aeSJordan K. Hubbard 		    ++blevel;
205efbcb4aeSJordan K. Hubbard 	    }
206b704025fSJordan K. Hubbard 	    break;
207b704025fSJordan K. Hubbard 
208b704025fSJordan K. Hubbard 	case COMMIT:
209779092a4SAndrey A. Chernov 	    if (head == NULL) {
210779092a4SAndrey A. Chernov 		if ((head = ptr = property_alloc(hold_n, hold_v)) == NULL)
211779092a4SAndrey A. Chernov 		    return (NULL);
212779092a4SAndrey A. Chernov 	    } else {
213779092a4SAndrey A. Chernov 		if ((ptr->next = property_alloc(hold_n, hold_v)) == NULL) {
214779092a4SAndrey A. Chernov 		    properties_free(head);
215779092a4SAndrey A. Chernov 		    return (NULL);
216779092a4SAndrey A. Chernov 		}
217b704025fSJordan K. Hubbard 		ptr = ptr->next;
218b704025fSJordan K. Hubbard 	    }
219b704025fSJordan K. Hubbard 	    state = LOOK;
220b704025fSJordan K. Hubbard 	    v = n = 0;
221b704025fSJordan K. Hubbard 	    break;
222b704025fSJordan K. Hubbard 
223b704025fSJordan K. Hubbard 	case STOP:
224b704025fSJordan K. Hubbard 	    /* we don't handle this here, but this prevents warnings */
225b704025fSJordan K. Hubbard 	    break;
226b704025fSJordan K. Hubbard 	}
227b704025fSJordan K. Hubbard     }
2285fe03abaSAndrey A. Chernov     if (head == NULL && (head = property_alloc(NULL, NULL)) == NULL)
2295fe03abaSAndrey A. Chernov 	return (NULL);
2305fe03abaSAndrey A. Chernov 
231779092a4SAndrey A. Chernov     return (head);
232b704025fSJordan K. Hubbard }
233b704025fSJordan K. Hubbard 
234b704025fSJordan K. Hubbard char *
235b704025fSJordan K. Hubbard property_find(properties list, const char *name)
236b704025fSJordan K. Hubbard {
2375fe03abaSAndrey A. Chernov     if (list == NULL || name == NULL || !name[0])
2385fe03abaSAndrey A. Chernov 	return (NULL);
2395fe03abaSAndrey A. Chernov     while (list != NULL) {
2405fe03abaSAndrey A. Chernov 	if (list->name != NULL && strcmp(list->name, name) == 0)
2415fe03abaSAndrey A. Chernov 	    return (list->value);
242b704025fSJordan K. Hubbard 	list = list->next;
243b704025fSJordan K. Hubbard     }
2445fe03abaSAndrey A. Chernov     return (NULL);
245b704025fSJordan K. Hubbard }
246b704025fSJordan K. Hubbard 
247b704025fSJordan K. Hubbard void
248b704025fSJordan K. Hubbard properties_free(properties list)
249b704025fSJordan K. Hubbard {
250b704025fSJordan K. Hubbard     properties tmp;
251b704025fSJordan K. Hubbard 
252b704025fSJordan K. Hubbard     while (list) {
253b704025fSJordan K. Hubbard 	tmp = list->next;
254b704025fSJordan K. Hubbard 	if (list->name)
255b704025fSJordan K. Hubbard 	    free(list->name);
256b704025fSJordan K. Hubbard 	if (list->value)
257b704025fSJordan K. Hubbard 	    free(list->value);
258b704025fSJordan K. Hubbard 	free(list);
259b704025fSJordan K. Hubbard 	list = tmp;
260b704025fSJordan K. Hubbard     }
261b704025fSJordan K. Hubbard }
262