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