1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 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 #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 } 260