]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - lib/libutil/property.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.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
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, last_state;
79     int ch = 0, blevel = 0;
80
81     n = v = bp = max = 0;
82     head = ptr = NULL;
83     state = last_state = LOOK;
84     while (state != STOP) {
85         if (state != COMMIT) {
86             if (bp == max) {
87                 last_state = state;
88                 state = FILL;
89             } else
90                 ch = buf[bp++];
91         }
92         switch(state) {
93         case FILL:
94             if ((max = read(fd, buf, sizeof buf)) < 0) {
95                 properties_free(head);
96                 return (NULL);
97             }
98             if (max == 0) {
99                 state = STOP;
100             } else {
101                 /*
102                  * Restore the state from before the fill (which will be
103                  * initialised to LOOK for the first FILL). This ensures that
104                  * if we were part-way through eg., a VALUE state, when the
105                  * buffer ran out, that the previous operation will be allowed
106                  * to complete.
107                  */
108                 state = last_state;
109                 ch = buf[0];
110                 bp = 0;
111             }
112             continue;
113
114         case LOOK:
115             if (isspace((unsigned char)ch))
116                 continue;
117             /* Allow shell or lisp style comments */
118             else if (ch == '#' || ch == ';') {
119                 state = COMMENT;
120                 continue;
121             }
122             else if (isalnum((unsigned char)ch) || ch == '_') {
123                 if (n >= PROPERTY_MAX_NAME) {
124                     n = 0;
125                     state = COMMENT;
126                 }
127                 else {
128                     hold_n[n++] = ch;
129                     state = NAME;
130                 }
131             }
132             else
133                 state = COMMENT;        /* Ignore the rest of the line */
134             break;
135
136         case COMMENT:
137             if (ch == '\n')
138                 state = LOOK;
139             break;
140
141         case NAME:
142             if (ch == '\n' || !ch) {
143                 hold_n[n] = '\0';
144                 hold_v[0] = '\0';
145                 v = n = 0;
146                 state = COMMIT;
147             }
148             else if (isspace((unsigned char)ch))
149                 continue;
150             else if (ch == '=') {
151                 hold_n[n] = '\0';
152                 v = n = 0;
153                 state = VALUE;
154             }
155             else
156                 hold_n[n++] = ch;
157             break;
158
159         case VALUE:
160             if (v == 0 && ch == '\n') {
161                 hold_v[v] = '\0';
162                 v = n = 0;
163                 state = COMMIT;
164             } 
165             else if (v == 0 && isspace((unsigned char)ch))
166                 continue;
167             else if (ch == '{') {
168                 state = MVALUE;
169                 ++blevel;
170             }
171             else if (ch == '\n' || !ch) {
172                 hold_v[v] = '\0';
173                 v = n = 0;
174                 state = COMMIT;
175             }
176             else {
177                 if (v >= PROPERTY_MAX_VALUE) {
178                     state = COMMENT;
179                     v = n = 0;
180                     break;
181                 }
182                 else
183                     hold_v[v++] = ch;
184             }
185             break;
186
187         case MVALUE:
188             /* multiline value */
189             if (v >= PROPERTY_MAX_VALUE) {
190                 warn("properties_read: value exceeds max length");
191                 state = COMMENT;
192                 n = v = 0;
193             }
194             else if (ch == '}' && !--blevel) {
195                 hold_v[v] = '\0';
196                 v = n = 0;
197                 state = COMMIT;
198             }
199             else {
200                 hold_v[v++] = ch;
201                 if (ch == '{')
202                     ++blevel;
203             }
204             break;
205
206         case COMMIT:
207             if (head == NULL) {
208                 if ((head = ptr = property_alloc(hold_n, hold_v)) == NULL)
209                     return (NULL);
210             } else {
211                 if ((ptr->next = property_alloc(hold_n, hold_v)) == NULL) {
212                     properties_free(head);
213                     return (NULL);
214                 }
215                 ptr = ptr->next;
216             }
217             state = LOOK;
218             v = n = 0;
219             break;
220
221         case STOP:
222             /* we don't handle this here, but this prevents warnings */
223             break;
224         }
225     }
226     if (head == NULL && (head = property_alloc(NULL, NULL)) == NULL)
227         return (NULL);
228
229     return (head);
230 }
231
232 char *
233 property_find(properties list, const char *name)
234 {
235     if (list == NULL || name == NULL || !name[0])
236         return (NULL);
237     while (list != NULL) {
238         if (list->name != NULL && strcmp(list->name, name) == 0)
239             return (list->value);
240         list = list->next;
241     }
242     return (NULL);
243 }
244
245 void
246 properties_free(properties list)
247 {
248     properties tmp;
249
250     while (list) {
251         tmp = list->next;
252         if (list->name)
253             free(list->name);
254         if (list->value)
255             free(list->value);
256         free(list);
257         list = tmp;
258     }
259 }