xref: /f-stack/tools/libutil/property.c (revision 22ce4aff)
1*22ce4affSfengbojiang /*-
2*22ce4affSfengbojiang  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3*22ce4affSfengbojiang  *
41eaf0ac3Slogwang  *
51eaf0ac3Slogwang  * Simple property list handling code.
61eaf0ac3Slogwang  *
71eaf0ac3Slogwang  * Copyright (c) 1998
81eaf0ac3Slogwang  *	Jordan Hubbard.  All rights reserved.
91eaf0ac3Slogwang  *
101eaf0ac3Slogwang  * Redistribution and use in source and binary forms, with or without
111eaf0ac3Slogwang  * modification, are permitted provided that the following conditions
121eaf0ac3Slogwang  * are met:
131eaf0ac3Slogwang  * 1. Redistributions of source code must retain the above copyright
141eaf0ac3Slogwang  *    notice, this list of conditions and the following disclaimer,
151eaf0ac3Slogwang  *    verbatim and that no modifications are made prior to this
161eaf0ac3Slogwang  *    point in the file.
171eaf0ac3Slogwang  * 2. Redistributions in binary form must reproduce the above copyright
181eaf0ac3Slogwang  *    notice, this list of conditions and the following disclaimer in the
191eaf0ac3Slogwang  *    documentation and/or other materials provided with the distribution.
201eaf0ac3Slogwang  *
211eaf0ac3Slogwang  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
221eaf0ac3Slogwang  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
231eaf0ac3Slogwang  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
241eaf0ac3Slogwang  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR HIS PETS BE LIABLE
251eaf0ac3Slogwang  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
261eaf0ac3Slogwang  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
271eaf0ac3Slogwang  * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
281eaf0ac3Slogwang  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
291eaf0ac3Slogwang  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
301eaf0ac3Slogwang  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
311eaf0ac3Slogwang  * SUCH DAMAGE.
321eaf0ac3Slogwang  */
331eaf0ac3Slogwang 
341eaf0ac3Slogwang #include <sys/cdefs.h>
351eaf0ac3Slogwang __FBSDID("$FreeBSD$");
361eaf0ac3Slogwang 
371eaf0ac3Slogwang #include <sys/types.h>
381eaf0ac3Slogwang #include <ctype.h>
391eaf0ac3Slogwang #include <err.h>
401eaf0ac3Slogwang #include <libutil.h>
411eaf0ac3Slogwang #include <stdio.h>
421eaf0ac3Slogwang #include <stdlib.h>
431eaf0ac3Slogwang #include <string.h>
441eaf0ac3Slogwang #include <unistd.h>
451eaf0ac3Slogwang 
461eaf0ac3Slogwang static properties
property_alloc(char * name,char * value)471eaf0ac3Slogwang property_alloc(char *name, char *value)
481eaf0ac3Slogwang {
491eaf0ac3Slogwang     properties n;
501eaf0ac3Slogwang 
511eaf0ac3Slogwang     if ((n = (properties)malloc(sizeof(struct _property))) == NULL)
521eaf0ac3Slogwang 	return (NULL);
531eaf0ac3Slogwang     n->next = NULL;
541eaf0ac3Slogwang     if (name != NULL) {
551eaf0ac3Slogwang 	if ((n->name = strdup(name)) == NULL) {
561eaf0ac3Slogwang 	    free(n);
571eaf0ac3Slogwang 	    return (NULL);
581eaf0ac3Slogwang 	}
591eaf0ac3Slogwang     } else
601eaf0ac3Slogwang 	n->name = NULL;
611eaf0ac3Slogwang     if (value != NULL) {
621eaf0ac3Slogwang 	if ((n->value = strdup(value)) == NULL) {
631eaf0ac3Slogwang 	    free(n->name);
641eaf0ac3Slogwang 	    free(n);
651eaf0ac3Slogwang 	    return (NULL);
661eaf0ac3Slogwang 	}
671eaf0ac3Slogwang     } else
681eaf0ac3Slogwang 	n->value = NULL;
691eaf0ac3Slogwang     return (n);
701eaf0ac3Slogwang }
711eaf0ac3Slogwang 
721eaf0ac3Slogwang properties
properties_read(int fd)731eaf0ac3Slogwang properties_read(int fd)
741eaf0ac3Slogwang {
751eaf0ac3Slogwang     properties head, ptr;
761eaf0ac3Slogwang     char hold_n[PROPERTY_MAX_NAME + 1];
771eaf0ac3Slogwang     char hold_v[PROPERTY_MAX_VALUE + 1];
781eaf0ac3Slogwang     char buf[BUFSIZ * 4];
791eaf0ac3Slogwang     int bp, n, v, max;
801eaf0ac3Slogwang     enum { LOOK, COMMENT, NAME, VALUE, MVALUE, COMMIT, FILL, STOP } state, last_state;
811eaf0ac3Slogwang     int ch = 0, blevel = 0;
821eaf0ac3Slogwang 
831eaf0ac3Slogwang     n = v = bp = max = 0;
841eaf0ac3Slogwang     head = ptr = NULL;
851eaf0ac3Slogwang     state = last_state = LOOK;
861eaf0ac3Slogwang     while (state != STOP) {
871eaf0ac3Slogwang 	if (state != COMMIT) {
881eaf0ac3Slogwang 	    if (bp == max) {
891eaf0ac3Slogwang 		last_state = state;
901eaf0ac3Slogwang 		state = FILL;
911eaf0ac3Slogwang 	    } else
921eaf0ac3Slogwang 		ch = buf[bp++];
931eaf0ac3Slogwang 	}
941eaf0ac3Slogwang 	switch(state) {
951eaf0ac3Slogwang 	case FILL:
961eaf0ac3Slogwang 	    if ((max = read(fd, buf, sizeof buf)) < 0) {
971eaf0ac3Slogwang 		properties_free(head);
981eaf0ac3Slogwang 		return (NULL);
991eaf0ac3Slogwang 	    }
1001eaf0ac3Slogwang 	    if (max == 0) {
1011eaf0ac3Slogwang 		state = STOP;
1021eaf0ac3Slogwang 	    } else {
1031eaf0ac3Slogwang 		/*
1041eaf0ac3Slogwang 		 * Restore the state from before the fill (which will be
1051eaf0ac3Slogwang 		 * initialised to LOOK for the first FILL). This ensures that
1061eaf0ac3Slogwang 		 * if we were part-way through eg., a VALUE state, when the
1071eaf0ac3Slogwang 		 * buffer ran out, that the previous operation will be allowed
1081eaf0ac3Slogwang 		 * to complete.
1091eaf0ac3Slogwang 		 */
1101eaf0ac3Slogwang 		state = last_state;
1111eaf0ac3Slogwang 		ch = buf[0];
1121eaf0ac3Slogwang 		bp = 0;
1131eaf0ac3Slogwang 	    }
1141eaf0ac3Slogwang 	    continue;
1151eaf0ac3Slogwang 
1161eaf0ac3Slogwang 	case LOOK:
1171eaf0ac3Slogwang 	    if (isspace((unsigned char)ch))
1181eaf0ac3Slogwang 		continue;
1191eaf0ac3Slogwang 	    /* Allow shell or lisp style comments */
1201eaf0ac3Slogwang 	    else if (ch == '#' || ch == ';') {
1211eaf0ac3Slogwang 		state = COMMENT;
1221eaf0ac3Slogwang 		continue;
1231eaf0ac3Slogwang 	    }
1241eaf0ac3Slogwang 	    else if (isalnum((unsigned char)ch) || ch == '_') {
1251eaf0ac3Slogwang 		if (n >= PROPERTY_MAX_NAME) {
1261eaf0ac3Slogwang 		    n = 0;
1271eaf0ac3Slogwang 		    state = COMMENT;
1281eaf0ac3Slogwang 		}
1291eaf0ac3Slogwang 		else {
1301eaf0ac3Slogwang 		    hold_n[n++] = ch;
1311eaf0ac3Slogwang 		    state = NAME;
1321eaf0ac3Slogwang 		}
1331eaf0ac3Slogwang 	    }
1341eaf0ac3Slogwang 	    else
1351eaf0ac3Slogwang 		state = COMMENT;	/* Ignore the rest of the line */
1361eaf0ac3Slogwang 	    break;
1371eaf0ac3Slogwang 
1381eaf0ac3Slogwang 	case COMMENT:
1391eaf0ac3Slogwang 	    if (ch == '\n')
1401eaf0ac3Slogwang 		state = LOOK;
1411eaf0ac3Slogwang 	    break;
1421eaf0ac3Slogwang 
1431eaf0ac3Slogwang 	case NAME:
1441eaf0ac3Slogwang 	    if (ch == '\n' || !ch) {
1451eaf0ac3Slogwang 		hold_n[n] = '\0';
1461eaf0ac3Slogwang 		hold_v[0] = '\0';
1471eaf0ac3Slogwang 		v = n = 0;
1481eaf0ac3Slogwang 		state = COMMIT;
1491eaf0ac3Slogwang 	    }
1501eaf0ac3Slogwang 	    else if (isspace((unsigned char)ch))
1511eaf0ac3Slogwang 		continue;
1521eaf0ac3Slogwang 	    else if (ch == '=') {
1531eaf0ac3Slogwang 		hold_n[n] = '\0';
1541eaf0ac3Slogwang 		v = n = 0;
1551eaf0ac3Slogwang 		state = VALUE;
1561eaf0ac3Slogwang 	    }
1571eaf0ac3Slogwang 	    else
1581eaf0ac3Slogwang 		hold_n[n++] = ch;
1591eaf0ac3Slogwang 	    break;
1601eaf0ac3Slogwang 
1611eaf0ac3Slogwang 	case VALUE:
1621eaf0ac3Slogwang 	    if (v == 0 && ch == '\n') {
1631eaf0ac3Slogwang 	        hold_v[v] = '\0';
1641eaf0ac3Slogwang 	        v = n = 0;
1651eaf0ac3Slogwang 	        state = COMMIT;
1661eaf0ac3Slogwang 	    }
1671eaf0ac3Slogwang 	    else if (v == 0 && isspace((unsigned char)ch))
1681eaf0ac3Slogwang 		continue;
1691eaf0ac3Slogwang 	    else if (ch == '{') {
1701eaf0ac3Slogwang 		state = MVALUE;
1711eaf0ac3Slogwang 		++blevel;
1721eaf0ac3Slogwang 	    }
1731eaf0ac3Slogwang 	    else if (ch == '\n' || !ch) {
1741eaf0ac3Slogwang 		hold_v[v] = '\0';
1751eaf0ac3Slogwang 		v = n = 0;
1761eaf0ac3Slogwang 		state = COMMIT;
1771eaf0ac3Slogwang 	    }
1781eaf0ac3Slogwang 	    else {
1791eaf0ac3Slogwang 		if (v >= PROPERTY_MAX_VALUE) {
1801eaf0ac3Slogwang 		    state = COMMENT;
1811eaf0ac3Slogwang 		    v = n = 0;
1821eaf0ac3Slogwang 		    break;
1831eaf0ac3Slogwang 		}
1841eaf0ac3Slogwang 		else
1851eaf0ac3Slogwang 		    hold_v[v++] = ch;
1861eaf0ac3Slogwang 	    }
1871eaf0ac3Slogwang 	    break;
1881eaf0ac3Slogwang 
1891eaf0ac3Slogwang 	case MVALUE:
1901eaf0ac3Slogwang 	    /* multiline value */
1911eaf0ac3Slogwang 	    if (v >= PROPERTY_MAX_VALUE) {
1921eaf0ac3Slogwang 		warn("properties_read: value exceeds max length");
1931eaf0ac3Slogwang 		state = COMMENT;
1941eaf0ac3Slogwang 		n = v = 0;
1951eaf0ac3Slogwang 	    }
1961eaf0ac3Slogwang 	    else if (ch == '}' && !--blevel) {
1971eaf0ac3Slogwang 		hold_v[v] = '\0';
1981eaf0ac3Slogwang 		v = n = 0;
1991eaf0ac3Slogwang 		state = COMMIT;
2001eaf0ac3Slogwang 	    }
2011eaf0ac3Slogwang 	    else {
2021eaf0ac3Slogwang 		hold_v[v++] = ch;
2031eaf0ac3Slogwang 		if (ch == '{')
2041eaf0ac3Slogwang 		    ++blevel;
2051eaf0ac3Slogwang 	    }
2061eaf0ac3Slogwang 	    break;
2071eaf0ac3Slogwang 
2081eaf0ac3Slogwang 	case COMMIT:
2091eaf0ac3Slogwang 	    if (head == NULL) {
2101eaf0ac3Slogwang 		if ((head = ptr = property_alloc(hold_n, hold_v)) == NULL)
2111eaf0ac3Slogwang 		    return (NULL);
2121eaf0ac3Slogwang 	    } else {
2131eaf0ac3Slogwang 		if ((ptr->next = property_alloc(hold_n, hold_v)) == NULL) {
2141eaf0ac3Slogwang 		    properties_free(head);
2151eaf0ac3Slogwang 		    return (NULL);
2161eaf0ac3Slogwang 		}
2171eaf0ac3Slogwang 		ptr = ptr->next;
2181eaf0ac3Slogwang 	    }
2191eaf0ac3Slogwang 	    state = LOOK;
2201eaf0ac3Slogwang 	    v = n = 0;
2211eaf0ac3Slogwang 	    break;
2221eaf0ac3Slogwang 
2231eaf0ac3Slogwang 	case STOP:
2241eaf0ac3Slogwang 	    /* we don't handle this here, but this prevents warnings */
2251eaf0ac3Slogwang 	    break;
2261eaf0ac3Slogwang 	}
2271eaf0ac3Slogwang     }
2281eaf0ac3Slogwang     if (head == NULL && (head = property_alloc(NULL, NULL)) == NULL)
2291eaf0ac3Slogwang 	return (NULL);
2301eaf0ac3Slogwang 
2311eaf0ac3Slogwang     return (head);
2321eaf0ac3Slogwang }
2331eaf0ac3Slogwang 
2341eaf0ac3Slogwang char *
property_find(properties list,const char * name)2351eaf0ac3Slogwang property_find(properties list, const char *name)
2361eaf0ac3Slogwang {
2371eaf0ac3Slogwang     if (list == NULL || name == NULL || !name[0])
2381eaf0ac3Slogwang 	return (NULL);
2391eaf0ac3Slogwang     while (list != NULL) {
2401eaf0ac3Slogwang 	if (list->name != NULL && strcmp(list->name, name) == 0)
2411eaf0ac3Slogwang 	    return (list->value);
2421eaf0ac3Slogwang 	list = list->next;
2431eaf0ac3Slogwang     }
2441eaf0ac3Slogwang     return (NULL);
2451eaf0ac3Slogwang }
2461eaf0ac3Slogwang 
2471eaf0ac3Slogwang void
properties_free(properties list)2481eaf0ac3Slogwang properties_free(properties list)
2491eaf0ac3Slogwang {
2501eaf0ac3Slogwang     properties tmp;
2511eaf0ac3Slogwang 
2521eaf0ac3Slogwang     while (list) {
2531eaf0ac3Slogwang 	tmp = list->next;
2541eaf0ac3Slogwang 	if (list->name)
2551eaf0ac3Slogwang 	    free(list->name);
2561eaf0ac3Slogwang 	if (list->value)
2571eaf0ac3Slogwang 	    free(list->value);
2581eaf0ac3Slogwang 	free(list);
2591eaf0ac3Slogwang 	list = tmp;
2601eaf0ac3Slogwang     }
2611eaf0ac3Slogwang }
262