Fix races in ihashget that were introduced when I introduced the
[dragonfly.git] / lib / libutil / property.c
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 }