xref: /freebsd/lib/libutil/property.c (revision 0640d357f29fb1c0daaaffadd0416c5981413afd)
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 
33 #include <ctype.h>
34 #include <unistd.h>
35 #include <stdlib.h>
36 #include <stdio.h>
37 #include <string.h>
38 #include <sys/types.h>
39 #include <libutil.h>
40 
41 #define MAX_NAME	64
42 #define MAX_VALUE	512
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[MAX_NAME + 1];
61     char hold_v[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;
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 >= 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 && isspace(ch))
137 		continue;
138 	    else if (ch == '{')
139 		state = MVALUE;
140 	    else if (ch == '\n' || !ch) {
141 		hold_v[v] = '\0';
142 		v = n = 0;
143 		state = COMMIT;
144 	    }
145 	    else {
146 		if (v >= MAX_VALUE) {
147 		    state = COMMENT;
148 		    v = n = 0;
149 		    break;
150 		}
151 		else
152 		    hold_v[v++] = ch;
153 	    }
154 	    break;
155 
156 	case MVALUE:
157 	    /* multiline value */
158 	    if (v >= MAX_VALUE) {
159 		state = COMMENT;
160 		n = v = 0;
161 	    }
162 	    else if (ch == '}') {
163 		hold_v[v] = '\0';
164 		v = n = 0;
165 		state = COMMIT;
166 	    }
167 	    else
168 		hold_v[v++] = ch;
169 	    break;
170 
171 	case COMMIT:
172 	    if (!head)
173 		head = ptr = property_alloc(hold_n, hold_v);
174 	    else {
175 		ptr->next = property_alloc(hold_n, hold_v);
176 		ptr = ptr->next;
177 	    }
178 	    state = LOOK;
179 	    v = n = 0;
180 	    break;
181 
182 	case STOP:
183 	    /* we don't handle this here, but this prevents warnings */
184 	    break;
185 	}
186     }
187     return head;
188 }
189 
190 char *
191 property_find(properties list, const char *name)
192 {
193     if (!list || !name || !name[0])
194 	return NULL;
195     while (list) {
196 	if (!strcmp(list->name, name))
197 	    return list->value;
198 	list = list->next;
199     }
200     return NULL;
201 }
202 
203 void
204 properties_free(properties list)
205 {
206     properties tmp;
207 
208     while (list) {
209 	tmp = list->next;
210 	if (list->name)
211 	    free(list->name);
212 	if (list->value)
213 	    free(list->value);
214 	free(list);
215 	list = tmp;
216     }
217 }
218