140040Sjkh/* 240040Sjkh * 340040Sjkh * Simple property list handling code. 440040Sjkh * 540040Sjkh * Copyright (c) 1998 640040Sjkh * Jordan Hubbard. All rights reserved. 740040Sjkh * 840040Sjkh * Redistribution and use in source and binary forms, with or without 940040Sjkh * modification, are permitted provided that the following conditions 1040040Sjkh * are met: 1140040Sjkh * 1. Redistributions of source code must retain the above copyright 1240040Sjkh * notice, this list of conditions and the following disclaimer, 1340040Sjkh * verbatim and that no modifications are made prior to this 1440040Sjkh * point in the file. 1540040Sjkh * 2. Redistributions in binary form must reproduce the above copyright 1640040Sjkh * notice, this list of conditions and the following disclaimer in the 1740040Sjkh * documentation and/or other materials provided with the distribution. 1840040Sjkh * 1940040Sjkh * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 2040040Sjkh * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2140040Sjkh * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2240040Sjkh * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR HIS PETS BE LIABLE 2340040Sjkh * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2440040Sjkh * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2540040Sjkh * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION) 2640040Sjkh * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2740040Sjkh * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2840040Sjkh * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2940040Sjkh * SUCH DAMAGE. 3040040Sjkh */ 3140040Sjkh 3284225Sdillon#include <sys/cdefs.h> 3384225Sdillon__FBSDID("$FreeBSD$"); 3484225Sdillon 35116344Smarkm#include <sys/types.h> 3640040Sjkh#include <ctype.h> 37116344Smarkm#include <err.h> 38116344Smarkm#include <libutil.h> 39116344Smarkm#include <stdio.h> 4040117Sjkh#include <stdlib.h> 4140040Sjkh#include <string.h> 42116344Smarkm#include <unistd.h> 4340040Sjkh 4440040Sjkhstatic properties 4540040Sjkhproperty_alloc(char *name, char *value) 4640040Sjkh{ 4740040Sjkh properties n; 4840040Sjkh 49109515Sache if ((n = (properties)malloc(sizeof(struct _property))) == NULL) 50109515Sache return (NULL); 5140040Sjkh n->next = NULL; 52109515Sache if (name != NULL) { 53109515Sache if ((n->name = strdup(name)) == NULL) { 54109515Sache free(n); 55109515Sache return (NULL); 56109515Sache } 57109515Sache } else 58109515Sache n->name = NULL; 59109515Sache if (value != NULL) { 60109515Sache if ((n->value = strdup(value)) == NULL) { 61109515Sache free(n->name); 62109515Sache free(n); 63109515Sache return (NULL); 64109515Sache } 65109515Sache } else 66109515Sache n->value = NULL; 67109515Sache return (n); 6840040Sjkh} 6940040Sjkh 7040040Sjkhproperties 7140109Sjkhproperties_read(int fd) 7240040Sjkh{ 7340040Sjkh properties head, ptr; 7468488Smurray char hold_n[PROPERTY_MAX_NAME + 1]; 7568488Smurray char hold_v[PROPERTY_MAX_VALUE + 1]; 7640040Sjkh char buf[BUFSIZ * 4]; 7740040Sjkh int bp, n, v, max; 78152886Sjhb enum { LOOK, COMMENT, NAME, VALUE, MVALUE, COMMIT, FILL, STOP } state, last_state; 7941291Sjkh int ch = 0, blevel = 0; 8040040Sjkh 8140040Sjkh n = v = bp = max = 0; 8240040Sjkh head = ptr = NULL; 83152886Sjhb state = last_state = LOOK; 8440040Sjkh while (state != STOP) { 8540040Sjkh if (state != COMMIT) { 86152886Sjhb if (bp == max) { 87152886Sjhb last_state = state; 8840040Sjkh state = FILL; 89152886Sjhb } else 9040040Sjkh ch = buf[bp++]; 9140040Sjkh } 9240040Sjkh switch(state) { 9340040Sjkh case FILL: 94109915Sache if ((max = read(fd, buf, sizeof buf)) < 0) { 95109915Sache properties_free(head); 96109915Sache return (NULL); 97109915Sache } 98109915Sache if (max == 0) { 9940040Sjkh state = STOP; 100109915Sache } else { 101152886Sjhb /* 102152886Sjhb * Restore the state from before the fill (which will be 103152886Sjhb * initialised to LOOK for the first FILL). This ensures that 104152886Sjhb * if we were part-way through eg., a VALUE state, when the 105152886Sjhb * buffer ran out, that the previous operation will be allowed 106152886Sjhb * to complete. 107152886Sjhb */ 108152886Sjhb state = last_state; 10940040Sjkh ch = buf[0]; 110152886Sjhb bp = 0; 11140040Sjkh } 112152886Sjhb continue; 11340040Sjkh 11440040Sjkh case LOOK: 115109515Sache if (isspace((unsigned char)ch)) 11640040Sjkh continue; 11740040Sjkh /* Allow shell or lisp style comments */ 11840040Sjkh else if (ch == '#' || ch == ';') { 11940040Sjkh state = COMMENT; 12040040Sjkh continue; 12140040Sjkh } 122109515Sache else if (isalnum((unsigned char)ch) || ch == '_') { 12368488Smurray if (n >= PROPERTY_MAX_NAME) { 12440040Sjkh n = 0; 12540040Sjkh state = COMMENT; 12640040Sjkh } 12740040Sjkh else { 12840040Sjkh hold_n[n++] = ch; 12940040Sjkh state = NAME; 13040040Sjkh } 13140040Sjkh } 13240040Sjkh else 13340040Sjkh state = COMMENT; /* Ignore the rest of the line */ 13440040Sjkh break; 13540040Sjkh 13640040Sjkh case COMMENT: 13740040Sjkh if (ch == '\n') 13840040Sjkh state = LOOK; 13940040Sjkh break; 14040040Sjkh 14140040Sjkh case NAME: 14240040Sjkh if (ch == '\n' || !ch) { 14340040Sjkh hold_n[n] = '\0'; 14440040Sjkh hold_v[0] = '\0'; 14540040Sjkh v = n = 0; 14640040Sjkh state = COMMIT; 14740040Sjkh } 148109515Sache else if (isspace((unsigned char)ch)) 14940040Sjkh continue; 15040040Sjkh else if (ch == '=') { 15140040Sjkh hold_n[n] = '\0'; 15240040Sjkh v = n = 0; 15340040Sjkh state = VALUE; 15440040Sjkh } 15540040Sjkh else 15640040Sjkh hold_n[n++] = ch; 15740040Sjkh break; 15840040Sjkh 15940040Sjkh case VALUE: 16068509Smurray if (v == 0 && ch == '\n') { 16168509Smurray hold_v[v] = '\0'; 16268509Smurray v = n = 0; 16368509Smurray state = COMMIT; 16468509Smurray } 165109515Sache else if (v == 0 && isspace((unsigned char)ch)) 16640040Sjkh continue; 16741291Sjkh else if (ch == '{') { 16840040Sjkh state = MVALUE; 16941291Sjkh ++blevel; 17041291Sjkh } 17140040Sjkh else if (ch == '\n' || !ch) { 17240040Sjkh hold_v[v] = '\0'; 17340040Sjkh v = n = 0; 17440040Sjkh state = COMMIT; 17540040Sjkh } 17640040Sjkh else { 17768488Smurray if (v >= PROPERTY_MAX_VALUE) { 17840040Sjkh state = COMMENT; 17940040Sjkh v = n = 0; 18040040Sjkh break; 18140040Sjkh } 18240040Sjkh else 18340040Sjkh hold_v[v++] = ch; 18440040Sjkh } 18540040Sjkh break; 18640040Sjkh 18740040Sjkh case MVALUE: 18840040Sjkh /* multiline value */ 18968488Smurray if (v >= PROPERTY_MAX_VALUE) { 19041291Sjkh warn("properties_read: value exceeds max length"); 19140040Sjkh state = COMMENT; 19240040Sjkh n = v = 0; 19340040Sjkh } 19441291Sjkh else if (ch == '}' && !--blevel) { 19540040Sjkh hold_v[v] = '\0'; 19640040Sjkh v = n = 0; 19740040Sjkh state = COMMIT; 19840040Sjkh } 19941291Sjkh else { 20040040Sjkh hold_v[v++] = ch; 20141291Sjkh if (ch == '{') 20241291Sjkh ++blevel; 20341291Sjkh } 20440040Sjkh break; 20540040Sjkh 20640040Sjkh case COMMIT: 207109515Sache if (head == NULL) { 208109515Sache if ((head = ptr = property_alloc(hold_n, hold_v)) == NULL) 209109515Sache return (NULL); 210109515Sache } else { 211109515Sache if ((ptr->next = property_alloc(hold_n, hold_v)) == NULL) { 212109515Sache properties_free(head); 213109515Sache return (NULL); 214109515Sache } 21540040Sjkh ptr = ptr->next; 21640040Sjkh } 21740040Sjkh state = LOOK; 21840040Sjkh v = n = 0; 21940040Sjkh break; 22040040Sjkh 22140040Sjkh case STOP: 22240040Sjkh /* we don't handle this here, but this prevents warnings */ 22340040Sjkh break; 22440040Sjkh } 22540040Sjkh } 226109916Sache if (head == NULL && (head = property_alloc(NULL, NULL)) == NULL) 227109916Sache return (NULL); 228109916Sache 229109515Sache return (head); 23040040Sjkh} 23140040Sjkh 23240040Sjkhchar * 23340040Sjkhproperty_find(properties list, const char *name) 23440040Sjkh{ 235109916Sache if (list == NULL || name == NULL || !name[0]) 236109916Sache return (NULL); 237109916Sache while (list != NULL) { 238109916Sache if (list->name != NULL && strcmp(list->name, name) == 0) 239109916Sache return (list->value); 24040040Sjkh list = list->next; 24140040Sjkh } 242109916Sache return (NULL); 24340040Sjkh} 24440040Sjkh 24540040Sjkhvoid 24640040Sjkhproperties_free(properties list) 24740040Sjkh{ 24840040Sjkh properties tmp; 24940040Sjkh 25040040Sjkh while (list) { 25140040Sjkh tmp = list->next; 25240040Sjkh if (list->name) 25340040Sjkh free(list->name); 25440040Sjkh if (list->value) 25540040Sjkh free(list->value); 25640040Sjkh free(list); 25740040Sjkh list = tmp; 25840040Sjkh } 25940040Sjkh} 260