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