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