1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 * 4 * 5 * Simple property list handling code. 6 * 7 * Copyright (c) 1998 8 * Jordan Hubbard. All rights reserved. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer, 15 * verbatim and that no modifications are made prior to this 16 * point in the file. 17 * 2. Redistributions in binary form must reproduce the above copyright 18 * notice, this list of conditions and the following disclaimer in the 19 * documentation and/or other materials provided with the distribution. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 22 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR HIS PETS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 */ 33 34 #include <sys/cdefs.h> 35 __FBSDID("$FreeBSD$"); 36 37 #include <sys/types.h> 38 #include <ctype.h> 39 #include <err.h> 40 #include <libutil.h> 41 #include <stdio.h> 42 #include <stdlib.h> 43 #include <string.h> 44 #include <unistd.h> 45 46 static properties 47 property_alloc(char *name, char *value) 48 { 49 properties n; 50 51 if ((n = (properties)malloc(sizeof(struct _property))) == NULL) 52 return (NULL); 53 n->next = NULL; 54 if (name != NULL) { 55 if ((n->name = strdup(name)) == NULL) { 56 free(n); 57 return (NULL); 58 } 59 } else 60 n->name = NULL; 61 if (value != NULL) { 62 if ((n->value = strdup(value)) == NULL) { 63 free(n->name); 64 free(n); 65 return (NULL); 66 } 67 } else 68 n->value = NULL; 69 return (n); 70 } 71 72 properties 73 properties_read(int fd) 74 { 75 properties head, ptr; 76 char hold_n[PROPERTY_MAX_NAME + 1]; 77 char hold_v[PROPERTY_MAX_VALUE + 1]; 78 char buf[BUFSIZ * 4]; 79 int bp, n, v, max; 80 enum { LOOK, COMMENT, NAME, VALUE, MVALUE, COMMIT, FILL, STOP } state, last_state; 81 int ch = 0, blevel = 0; 82 83 n = v = bp = max = 0; 84 head = ptr = NULL; 85 state = last_state = LOOK; 86 while (state != STOP) { 87 if (state != COMMIT) { 88 if (bp == max) { 89 last_state = state; 90 state = FILL; 91 } else 92 ch = buf[bp++]; 93 } 94 switch(state) { 95 case FILL: 96 if ((max = read(fd, buf, sizeof buf)) < 0) { 97 properties_free(head); 98 return (NULL); 99 } 100 if (max == 0) { 101 state = STOP; 102 } else { 103 /* 104 * Restore the state from before the fill (which will be 105 * initialised to LOOK for the first FILL). This ensures that 106 * if we were part-way through eg., a VALUE state, when the 107 * buffer ran out, that the previous operation will be allowed 108 * to complete. 109 */ 110 state = last_state; 111 ch = buf[0]; 112 bp = 0; 113 } 114 continue; 115 116 case LOOK: 117 if (isspace((unsigned char)ch)) 118 continue; 119 /* Allow shell or lisp style comments */ 120 else if (ch == '#' || ch == ';') { 121 state = COMMENT; 122 continue; 123 } 124 else if (isalnum((unsigned char)ch) || ch == '_') { 125 if (n >= PROPERTY_MAX_NAME) { 126 n = 0; 127 state = COMMENT; 128 } 129 else { 130 hold_n[n++] = ch; 131 state = NAME; 132 } 133 } 134 else 135 state = COMMENT; /* Ignore the rest of the line */ 136 break; 137 138 case COMMENT: 139 if (ch == '\n') 140 state = LOOK; 141 break; 142 143 case NAME: 144 if (ch == '\n' || !ch) { 145 hold_n[n] = '\0'; 146 hold_v[0] = '\0'; 147 v = n = 0; 148 state = COMMIT; 149 } 150 else if (isspace((unsigned char)ch)) 151 continue; 152 else if (ch == '=') { 153 hold_n[n] = '\0'; 154 v = n = 0; 155 state = VALUE; 156 } 157 else 158 hold_n[n++] = ch; 159 break; 160 161 case VALUE: 162 if (v == 0 && ch == '\n') { 163 hold_v[v] = '\0'; 164 v = n = 0; 165 state = COMMIT; 166 } 167 else if (v == 0 && isspace((unsigned char)ch)) 168 continue; 169 else if (ch == '{') { 170 state = MVALUE; 171 ++blevel; 172 } 173 else if (ch == '\n' || !ch) { 174 hold_v[v] = '\0'; 175 v = n = 0; 176 state = COMMIT; 177 } 178 else { 179 if (v >= PROPERTY_MAX_VALUE) { 180 state = COMMENT; 181 v = n = 0; 182 break; 183 } 184 else 185 hold_v[v++] = ch; 186 } 187 break; 188 189 case MVALUE: 190 /* multiline value */ 191 if (v >= PROPERTY_MAX_VALUE) { 192 warn("properties_read: value exceeds max length"); 193 state = COMMENT; 194 n = v = 0; 195 } 196 else if (ch == '}' && !--blevel) { 197 hold_v[v] = '\0'; 198 v = n = 0; 199 state = COMMIT; 200 } 201 else { 202 hold_v[v++] = ch; 203 if (ch == '{') 204 ++blevel; 205 } 206 break; 207 208 case COMMIT: 209 if (head == NULL) { 210 if ((head = ptr = property_alloc(hold_n, hold_v)) == NULL) 211 return (NULL); 212 } else { 213 if ((ptr->next = property_alloc(hold_n, hold_v)) == NULL) { 214 properties_free(head); 215 return (NULL); 216 } 217 ptr = ptr->next; 218 } 219 state = LOOK; 220 v = n = 0; 221 break; 222 223 case STOP: 224 /* we don't handle this here, but this prevents warnings */ 225 break; 226 } 227 } 228 if (head == NULL && (head = property_alloc(NULL, NULL)) == NULL) 229 return (NULL); 230 231 return (head); 232 } 233 234 char * 235 property_find(properties list, const char *name) 236 { 237 if (list == NULL || name == NULL || !name[0]) 238 return (NULL); 239 while (list != NULL) { 240 if (list->name != NULL && strcmp(list->name, name) == 0) 241 return (list->value); 242 list = list->next; 243 } 244 return (NULL); 245 } 246 247 void 248 properties_free(properties list) 249 { 250 properties tmp; 251 252 while (list) { 253 tmp = list->next; 254 if (list->name) 255 free(list->name); 256 if (list->value) 257 free(list->value); 258 free(list); 259 list = tmp; 260 } 261 } 262