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