xref: /dragonfly/lib/libutil/property.c (revision 1de703da)
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  * $FreeBSD: src/lib/libutil/property.c,v 1.5.6.1 2000/11/22 03:49:49 murray Exp $
32  * $DragonFly: src/lib/libutil/property.c,v 1.2 2003/06/17 04:26:52 dillon Exp $
33  *
34  */
35 
36 #include <ctype.h>
37 #include <unistd.h>
38 #include <stdlib.h>
39 #include <stdio.h>
40 #include <string.h>
41 #include <err.h>
42 #include <sys/types.h>
43 #include <libutil.h>
44 
45 static properties
46 property_alloc(char *name, char *value)
47 {
48     properties n;
49 
50     n = (properties)malloc(sizeof(struct _property));
51     n->next = NULL;
52     n->name = name ? strdup(name) : NULL;
53     n->value = value ? strdup(value) : NULL;
54     return n;
55 }
56 
57 properties
58 properties_read(int fd)
59 {
60     properties head, ptr;
61     char hold_n[PROPERTY_MAX_NAME + 1];
62     char hold_v[PROPERTY_MAX_VALUE + 1];
63     char buf[BUFSIZ * 4];
64     int bp, n, v, max;
65     enum { LOOK, COMMENT, NAME, VALUE, MVALUE, COMMIT, FILL, STOP } state;
66     int ch = 0, blevel = 0;
67 
68     n = v = bp = max = 0;
69     head = ptr = NULL;
70     state = LOOK;
71     while (state != STOP) {
72 	if (state != COMMIT) {
73 	    if (bp == max)
74 		state = FILL;
75 	    else
76 		ch = buf[bp++];
77 	}
78 	switch(state) {
79 	case FILL:
80 	    if ((max = read(fd, buf, sizeof buf)) <= 0) {
81 		state = STOP;
82 		break;
83 	    }
84 	    else {
85 		state = LOOK;
86 		ch = buf[0];
87 		bp = 1;
88 	    }
89 	    /* Fall through deliberately since we already have a character and state == LOOK */
90 
91 	case LOOK:
92 	    if (isspace(ch))
93 		continue;
94 	    /* Allow shell or lisp style comments */
95 	    else if (ch == '#' || ch == ';') {
96 		state = COMMENT;
97 		continue;
98 	    }
99 	    else if (isalnum(ch) || ch == '_') {
100 		if (n >= PROPERTY_MAX_NAME) {
101 		    n = 0;
102 		    state = COMMENT;
103 		}
104 		else {
105 		    hold_n[n++] = ch;
106 		    state = NAME;
107 		}
108 	    }
109 	    else
110 		state = COMMENT;	/* Ignore the rest of the line */
111 	    break;
112 
113 	case COMMENT:
114 	    if (ch == '\n')
115 		state = LOOK;
116 	    break;
117 
118 	case NAME:
119 	    if (ch == '\n' || !ch) {
120 		hold_n[n] = '\0';
121 		hold_v[0] = '\0';
122 		v = n = 0;
123 		state = COMMIT;
124 	    }
125 	    else if (isspace(ch))
126 		continue;
127 	    else if (ch == '=') {
128 		hold_n[n] = '\0';
129 		v = n = 0;
130 		state = VALUE;
131 	    }
132 	    else
133 		hold_n[n++] = ch;
134 	    break;
135 
136 	case VALUE:
137 	    if (v == 0 && ch == '\n') {
138 	        hold_v[v] = '\0';
139 	        v = n = 0;
140 	        state = COMMIT;
141 	    }
142 	    else if (v == 0 && isspace(ch))
143 		continue;
144 	    else if (ch == '{') {
145 		state = MVALUE;
146 		++blevel;
147 	    }
148 	    else if (ch == '\n' || !ch) {
149 		hold_v[v] = '\0';
150 		v = n = 0;
151 		state = COMMIT;
152 	    }
153 	    else {
154 		if (v >= PROPERTY_MAX_VALUE) {
155 		    state = COMMENT;
156 		    v = n = 0;
157 		    break;
158 		}
159 		else
160 		    hold_v[v++] = ch;
161 	    }
162 	    break;
163 
164 	case MVALUE:
165 	    /* multiline value */
166 	    if (v >= PROPERTY_MAX_VALUE) {
167 		warn("properties_read: value exceeds max length");
168 		state = COMMENT;
169 		n = v = 0;
170 	    }
171 	    else if (ch == '}' && !--blevel) {
172 		hold_v[v] = '\0';
173 		v = n = 0;
174 		state = COMMIT;
175 	    }
176 	    else {
177 		hold_v[v++] = ch;
178 		if (ch == '{')
179 		    ++blevel;
180 	    }
181 	    break;
182 
183 	case COMMIT:
184 	    if (!head)
185 		head = ptr = property_alloc(hold_n, hold_v);
186 	    else {
187 		ptr->next = property_alloc(hold_n, hold_v);
188 		ptr = ptr->next;
189 	    }
190 	    state = LOOK;
191 	    v = n = 0;
192 	    break;
193 
194 	case STOP:
195 	    /* we don't handle this here, but this prevents warnings */
196 	    break;
197 	}
198     }
199     return head;
200 }
201 
202 char *
203 property_find(properties list, const char *name)
204 {
205     if (!list || !name || !name[0])
206 	return NULL;
207     while (list) {
208 	if (!strcmp(list->name, name))
209 	    return list->value;
210 	list = list->next;
211     }
212     return NULL;
213 }
214 
215 void
216 properties_free(properties list)
217 {
218     properties tmp;
219 
220     while (list) {
221 	tmp = list->next;
222 	if (list->name)
223 	    free(list->name);
224 	if (list->value)
225 	    free(list->value);
226 	free(list);
227 	list = tmp;
228     }
229 }
230