xref: /freebsd/lib/libfigpar/figpar.c (revision f144058b406656fa4678f8ff2f04c2da46176c79)
1041394f3SDevin Teske /*-
29d9cc246SDevin Teske  * Copyright (c) 2002-2015 Devin Teske <dteske@FreeBSD.org>
3041394f3SDevin Teske  * All rights reserved.
4041394f3SDevin Teske  *
5041394f3SDevin Teske  * Redistribution and use in source and binary forms, with or without
6041394f3SDevin Teske  * modification, are permitted provided that the following conditions
7041394f3SDevin Teske  * are met:
8041394f3SDevin Teske  * 1. Redistributions of source code must retain the above copyright
9041394f3SDevin Teske  *    notice, this list of conditions and the following disclaimer.
10041394f3SDevin Teske  * 2. Redistributions in binary form must reproduce the above copyright
11041394f3SDevin Teske  *    notice, this list of conditions and the following disclaimer in the
12041394f3SDevin Teske  *    documentation and/or other materials provided with the distribution.
13041394f3SDevin Teske  *
14041394f3SDevin Teske  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15041394f3SDevin Teske  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16041394f3SDevin Teske  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17041394f3SDevin Teske  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18041394f3SDevin Teske  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19041394f3SDevin Teske  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20041394f3SDevin Teske  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21041394f3SDevin Teske  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22041394f3SDevin Teske  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23041394f3SDevin Teske  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24041394f3SDevin Teske  * SUCH DAMAGE.
25041394f3SDevin Teske  */
26041394f3SDevin Teske 
27041394f3SDevin Teske #include <sys/param.h>
28041394f3SDevin Teske 
29041394f3SDevin Teske #include <ctype.h>
30041394f3SDevin Teske #include <fcntl.h>
31041394f3SDevin Teske #include <fnmatch.h>
32041394f3SDevin Teske #include <stdlib.h>
33041394f3SDevin Teske #include <string.h>
34041394f3SDevin Teske #include <unistd.h>
35041394f3SDevin Teske 
36041394f3SDevin Teske #include "figpar.h"
37041394f3SDevin Teske #include "string_m.h"
38041394f3SDevin Teske 
399d9cc246SDevin Teske struct figpar_config figpar_dummy_config = {0, NULL, {0}, NULL};
40041394f3SDevin Teske 
41041394f3SDevin Teske /*
429d9cc246SDevin Teske  * Search for config option (struct figpar_config) in the array of config
439d9cc246SDevin Teske  * options, returning the struct whose directive matches the given parameter.
449d9cc246SDevin Teske  * If no match is found, a pointer to the static dummy array (above) is
459d9cc246SDevin Teske  * returned.
46041394f3SDevin Teske  *
47041394f3SDevin Teske  * This is to eliminate dependency on the index position of an item in the
48041394f3SDevin Teske  * array, since the index position is more apt to be changed as code grows.
49041394f3SDevin Teske  */
509d9cc246SDevin Teske struct figpar_config *
get_config_option(struct figpar_config options[],const char * directive)519d9cc246SDevin Teske get_config_option(struct figpar_config options[], const char *directive)
52041394f3SDevin Teske {
53041394f3SDevin Teske 	uint32_t n;
54041394f3SDevin Teske 
55041394f3SDevin Teske 	/* Check arguments */
56041394f3SDevin Teske 	if (options == NULL || directive == NULL)
579d9cc246SDevin Teske 		return (&figpar_dummy_config);
58041394f3SDevin Teske 
59041394f3SDevin Teske 	/* Loop through the array, return the index of the first match */
60041394f3SDevin Teske 	for (n = 0; options[n].directive != NULL; n++)
61041394f3SDevin Teske 		if (strcmp(options[n].directive, directive) == 0)
62041394f3SDevin Teske 			return (&(options[n]));
63041394f3SDevin Teske 
64041394f3SDevin Teske 	/* Re-initialize the dummy variable in case it was written to */
659d9cc246SDevin Teske 	figpar_dummy_config.directive	= NULL;
669d9cc246SDevin Teske 	figpar_dummy_config.type	= 0;
679d9cc246SDevin Teske 	figpar_dummy_config.action	= NULL;
689d9cc246SDevin Teske 	figpar_dummy_config.value.u_num	= 0;
69041394f3SDevin Teske 
709d9cc246SDevin Teske 	return (&figpar_dummy_config);
71041394f3SDevin Teske }
72041394f3SDevin Teske 
73041394f3SDevin Teske /*
74041394f3SDevin Teske  * Parse the configuration file at `path' and execute the `action' call-back
75041394f3SDevin Teske  * functions for any directives defined by the array of config options (first
76041394f3SDevin Teske  * argument).
77041394f3SDevin Teske  *
78041394f3SDevin Teske  * For unknown directives that are encountered, you can optionally pass a
79041394f3SDevin Teske  * call-back function for the third argument to be called for unknowns.
80041394f3SDevin Teske  *
81041394f3SDevin Teske  * Returns zero on success; otherwise returns -1 and errno should be consulted.
82041394f3SDevin Teske */
83041394f3SDevin Teske int
parse_config(struct figpar_config options[],const char * path,int (* unknown)(struct figpar_config * option,uint32_t line,char * directive,char * value),uint16_t processing_options)849d9cc246SDevin Teske parse_config(struct figpar_config options[], const char *path,
859d9cc246SDevin Teske     int (*unknown)(struct figpar_config *option, uint32_t line,
869d9cc246SDevin Teske     char *directive, char *value), uint16_t processing_options)
87041394f3SDevin Teske {
88041394f3SDevin Teske 	uint8_t bequals;
89041394f3SDevin Teske 	uint8_t bsemicolon;
90041394f3SDevin Teske 	uint8_t case_sensitive;
91041394f3SDevin Teske 	uint8_t comment = 0;
92041394f3SDevin Teske 	uint8_t end;
93041394f3SDevin Teske 	uint8_t found;
94041394f3SDevin Teske 	uint8_t have_equals = 0;
95041394f3SDevin Teske 	uint8_t quote;
96041394f3SDevin Teske 	uint8_t require_equals;
97041394f3SDevin Teske 	uint8_t strict_equals;
98041394f3SDevin Teske 	char p[2];
99041394f3SDevin Teske 	char *directive;
100041394f3SDevin Teske 	char *t;
101041394f3SDevin Teske 	char *value;
102041394f3SDevin Teske 	int error;
103041394f3SDevin Teske 	int fd;
104041394f3SDevin Teske 	ssize_t r = 1;
105041394f3SDevin Teske 	uint32_t dsize;
106041394f3SDevin Teske 	uint32_t line = 1;
107041394f3SDevin Teske 	uint32_t n;
108041394f3SDevin Teske 	uint32_t vsize;
109041394f3SDevin Teske 	uint32_t x;
110041394f3SDevin Teske 	off_t charpos;
111041394f3SDevin Teske 	off_t curpos;
112041394f3SDevin Teske 	char rpath[PATH_MAX];
113041394f3SDevin Teske 
114041394f3SDevin Teske 	/* Sanity check: if no options and no unknown function, return */
115041394f3SDevin Teske 	if (options == NULL && unknown == NULL)
116041394f3SDevin Teske 		return (-1);
117041394f3SDevin Teske 
118041394f3SDevin Teske 	/* Processing options */
1199d9cc246SDevin Teske 	bequals = (processing_options & FIGPAR_BREAK_ON_EQUALS) == 0 ? 0 : 1;
1209d9cc246SDevin Teske 	bsemicolon =
1219d9cc246SDevin Teske 		(processing_options & FIGPAR_BREAK_ON_SEMICOLON) == 0 ? 0 : 1;
1229d9cc246SDevin Teske 	case_sensitive =
1239d9cc246SDevin Teske 		(processing_options & FIGPAR_CASE_SENSITIVE) == 0 ? 0 : 1;
1249d9cc246SDevin Teske 	require_equals =
1259d9cc246SDevin Teske 		(processing_options & FIGPAR_REQUIRE_EQUALS) == 0 ? 0 : 1;
1269d9cc246SDevin Teske 	strict_equals =
1279d9cc246SDevin Teske 		(processing_options & FIGPAR_STRICT_EQUALS) == 0 ? 0 : 1;
128041394f3SDevin Teske 
129041394f3SDevin Teske 	/* Initialize strings */
130041394f3SDevin Teske 	directive = value = 0;
131041394f3SDevin Teske 	vsize = dsize = 0;
132041394f3SDevin Teske 
133041394f3SDevin Teske 	/* Resolve the file path */
134041394f3SDevin Teske 	if (realpath(path, rpath) == 0)
135041394f3SDevin Teske 		return (-1);
136041394f3SDevin Teske 
137041394f3SDevin Teske 	/* Open the file */
138041394f3SDevin Teske 	if ((fd = open(rpath, O_RDONLY)) < 0)
139041394f3SDevin Teske 		return (-1);
140041394f3SDevin Teske 
141041394f3SDevin Teske 	/* Read the file until EOF */
142041394f3SDevin Teske 	while (r != 0) {
143041394f3SDevin Teske 		r = read(fd, p, 1);
144041394f3SDevin Teske 
145041394f3SDevin Teske 		/* skip to the beginning of a directive */
146041394f3SDevin Teske 		while (r != 0 && (isspace(*p) || *p == '#' || comment ||
147041394f3SDevin Teske 		    (bsemicolon && *p == ';'))) {
148041394f3SDevin Teske 			if (*p == '#')
149041394f3SDevin Teske 				comment = 1;
150041394f3SDevin Teske 			else if (*p == '\n') {
151041394f3SDevin Teske 				comment = 0;
152041394f3SDevin Teske 				line++;
153041394f3SDevin Teske 			}
154041394f3SDevin Teske 			r = read(fd, p, 1);
155041394f3SDevin Teske 		}
156041394f3SDevin Teske 		/* Test for EOF; if EOF then no directive was found */
157041394f3SDevin Teske 		if (r == 0) {
158041394f3SDevin Teske 			close(fd);
159041394f3SDevin Teske 			return (0);
160041394f3SDevin Teske 		}
161041394f3SDevin Teske 
162041394f3SDevin Teske 		/* Get the current offset */
163*f144058bSFaraz Vahedi 		if ((curpos = lseek(fd, 0, SEEK_CUR)) == -1) {
164041394f3SDevin Teske 			close(fd);
165041394f3SDevin Teske 			return (-1);
166041394f3SDevin Teske 		}
167*f144058bSFaraz Vahedi 		curpos--;
168041394f3SDevin Teske 
169041394f3SDevin Teske 		/* Find the length of the directive */
170041394f3SDevin Teske 		for (n = 0; r != 0; n++) {
171041394f3SDevin Teske 			if (isspace(*p))
172041394f3SDevin Teske 				break;
173041394f3SDevin Teske 			if (bequals && *p == '=') {
174041394f3SDevin Teske 				have_equals = 1;
175041394f3SDevin Teske 				break;
176041394f3SDevin Teske 			}
177041394f3SDevin Teske 			if (bsemicolon && *p == ';')
178041394f3SDevin Teske 				break;
179041394f3SDevin Teske 			r = read(fd, p, 1);
180041394f3SDevin Teske 		}
181041394f3SDevin Teske 
182041394f3SDevin Teske 		/* Test for EOF, if EOF then no directive was found */
183041394f3SDevin Teske 		if (n == 0 && r == 0) {
184041394f3SDevin Teske 			close(fd);
185041394f3SDevin Teske 			return (0);
186041394f3SDevin Teske 		}
187041394f3SDevin Teske 
188041394f3SDevin Teske 		/* Go back to the beginning of the directive */
189*f144058bSFaraz Vahedi 		if (lseek(fd, curpos, SEEK_SET) == -1) {
190041394f3SDevin Teske 			close(fd);
191041394f3SDevin Teske 			return (-1);
192041394f3SDevin Teske 		}
193041394f3SDevin Teske 
194041394f3SDevin Teske 		/* Allocate and read the directive into memory */
195041394f3SDevin Teske 		if (n > dsize) {
196041394f3SDevin Teske 			if ((directive = realloc(directive, n + 1)) == NULL) {
197041394f3SDevin Teske 				close(fd);
198041394f3SDevin Teske 				return (-1);
199041394f3SDevin Teske 			}
200041394f3SDevin Teske 			dsize = n;
201041394f3SDevin Teske 		}
202041394f3SDevin Teske 		r = read(fd, directive, n);
203041394f3SDevin Teske 
204041394f3SDevin Teske 		/* Advance beyond the equals sign if appropriate/desired */
205041394f3SDevin Teske 		if (bequals && *p == '=') {
206041394f3SDevin Teske 			if (lseek(fd, 1, SEEK_CUR) != -1)
207041394f3SDevin Teske 				r = read(fd, p, 1);
208041394f3SDevin Teske 			if (strict_equals && isspace(*p))
209041394f3SDevin Teske 				*p = '\n';
210041394f3SDevin Teske 		}
211041394f3SDevin Teske 
212041394f3SDevin Teske 		/* Terminate the string */
213041394f3SDevin Teske 		directive[n] = '\0';
214041394f3SDevin Teske 
215041394f3SDevin Teske 		/* Convert directive to lower case before comparison */
216041394f3SDevin Teske 		if (!case_sensitive)
217041394f3SDevin Teske 			strtolower(directive);
218041394f3SDevin Teske 
219041394f3SDevin Teske 		/* Move to what may be the start of the value */
220041394f3SDevin Teske 		if (!(bsemicolon && *p == ';') &&
221041394f3SDevin Teske 		    !(strict_equals && *p == '=')) {
222041394f3SDevin Teske 			while (r != 0 && isspace(*p) && *p != '\n')
223041394f3SDevin Teske 				r = read(fd, p, 1);
224041394f3SDevin Teske 		}
225041394f3SDevin Teske 
226041394f3SDevin Teske 		/* An equals sign may have stopped us, should we eat it? */
227041394f3SDevin Teske 		if (r != 0 && bequals && *p == '=' && !strict_equals) {
228041394f3SDevin Teske 			have_equals = 1;
229041394f3SDevin Teske 			r = read(fd, p, 1);
230041394f3SDevin Teske 			while (r != 0 && isspace(*p) && *p != '\n')
231041394f3SDevin Teske 				r = read(fd, p, 1);
232041394f3SDevin Teske 		}
233041394f3SDevin Teske 
234041394f3SDevin Teske 		/* If no value, allocate a dummy value and jump to action */
235041394f3SDevin Teske 		if (r == 0 || *p == '\n' || *p == '#' ||
236041394f3SDevin Teske 		    (bsemicolon && *p == ';')) {
237041394f3SDevin Teske 			/* Initialize the value if not already done */
238041394f3SDevin Teske 			if (value == NULL && (value = malloc(1)) == NULL) {
239041394f3SDevin Teske 				close(fd);
240041394f3SDevin Teske 				return (-1);
241041394f3SDevin Teske 			}
242041394f3SDevin Teske 			value[0] = '\0';
243041394f3SDevin Teske 			goto call_function;
244041394f3SDevin Teske 		}
245041394f3SDevin Teske 
246041394f3SDevin Teske 		/* Get the current offset */
247*f144058bSFaraz Vahedi 		if ((curpos = lseek(fd, 0, SEEK_CUR)) == -1) {
248041394f3SDevin Teske 			close(fd);
249041394f3SDevin Teske 			return (-1);
250041394f3SDevin Teske 		}
251*f144058bSFaraz Vahedi 		curpos--;
252041394f3SDevin Teske 
253041394f3SDevin Teske 		/* Find the end of the value */
254041394f3SDevin Teske 		quote = 0;
255041394f3SDevin Teske 		end = 0;
256041394f3SDevin Teske 		while (r != 0 && end == 0) {
257041394f3SDevin Teske 			/* Advance to the next character if we know we can */
258041394f3SDevin Teske 			if (*p != '\"' && *p != '#' && *p != '\n' &&
259041394f3SDevin Teske 			    (!bsemicolon || *p != ';')) {
260041394f3SDevin Teske 				r = read(fd, p, 1);
261041394f3SDevin Teske 				continue;
262041394f3SDevin Teske 			}
263041394f3SDevin Teske 
264041394f3SDevin Teske 			/*
265041394f3SDevin Teske 			 * If we get this far, we've hit an end-key
266041394f3SDevin Teske 			 */
267041394f3SDevin Teske 
268041394f3SDevin Teske 			/* Get the current offset */
269*f144058bSFaraz Vahedi 			if ((charpos = lseek(fd, 0, SEEK_CUR)) == -1) {
270041394f3SDevin Teske 				close(fd);
271041394f3SDevin Teske 				return (-1);
272041394f3SDevin Teske 			}
273*f144058bSFaraz Vahedi 			charpos--;
274041394f3SDevin Teske 
275041394f3SDevin Teske 			/*
276041394f3SDevin Teske 			 * Go back so we can read the character before the key
277041394f3SDevin Teske 			 * to check if the character is escaped (which means we
278041394f3SDevin Teske 			 * should continue).
279041394f3SDevin Teske 			 */
280*f144058bSFaraz Vahedi 			if (lseek(fd, -2, SEEK_CUR) == -1) {
281041394f3SDevin Teske 				close(fd);
282041394f3SDevin Teske 				return (-1);
283041394f3SDevin Teske 			}
284041394f3SDevin Teske 			r = read(fd, p, 1);
285041394f3SDevin Teske 
286041394f3SDevin Teske 			/*
287041394f3SDevin Teske 			 * Count how many backslashes there are (an odd number
288041394f3SDevin Teske 			 * means the key is escaped, even means otherwise).
289041394f3SDevin Teske 			 */
290041394f3SDevin Teske 			for (n = 1; *p == '\\'; n++) {
291041394f3SDevin Teske 				/* Move back another offset to read */
292*f144058bSFaraz Vahedi 				if (lseek(fd, -2, SEEK_CUR) == -1) {
293041394f3SDevin Teske 					close(fd);
294041394f3SDevin Teske 					return (-1);
295041394f3SDevin Teske 				}
296041394f3SDevin Teske 				r = read(fd, p, 1);
297041394f3SDevin Teske 			}
298041394f3SDevin Teske 
299041394f3SDevin Teske 			/* Move offset back to the key and read it */
300*f144058bSFaraz Vahedi 			if (lseek(fd, charpos, SEEK_SET) == -1) {
301041394f3SDevin Teske 				close(fd);
302041394f3SDevin Teske 				return (-1);
303041394f3SDevin Teske 			}
304041394f3SDevin Teske 			r = read(fd, p, 1);
305041394f3SDevin Teske 
306041394f3SDevin Teske 			/*
307041394f3SDevin Teske 			 * If an even number of backslashes was counted meaning
308041394f3SDevin Teske 			 * key is not escaped, we should evaluate what to do.
309041394f3SDevin Teske 			 */
310041394f3SDevin Teske 			if ((n & 1) == 1) {
311041394f3SDevin Teske 				switch (*p) {
312041394f3SDevin Teske 				case '\"':
313041394f3SDevin Teske 					/*
314041394f3SDevin Teske 				 	 * Flag current sequence of characters
315041394f3SDevin Teske 					 * to follow as being quoted (hashes
316041394f3SDevin Teske 					 * are not considered comments).
317041394f3SDevin Teske 					 */
318041394f3SDevin Teske 					quote = !quote;
319041394f3SDevin Teske 					break;
320041394f3SDevin Teske 				case '#':
321041394f3SDevin Teske 					/*
322041394f3SDevin Teske 					 * If we aren't in a quoted series, we
323041394f3SDevin Teske 					 * just hit an inline comment and have
324041394f3SDevin Teske 					 * found the end of the value.
325041394f3SDevin Teske 					 */
326041394f3SDevin Teske 					if (!quote)
327041394f3SDevin Teske 						end = 1;
328041394f3SDevin Teske 					break;
329041394f3SDevin Teske 				case '\n':
330041394f3SDevin Teske 					/*
331041394f3SDevin Teske 					 * Newline characters must always be
332041394f3SDevin Teske 					 * escaped, whether inside a quoted
333041394f3SDevin Teske 					 * series or not, otherwise they
334041394f3SDevin Teske 					 * terminate the value.
335041394f3SDevin Teske 					 */
336041394f3SDevin Teske 					end = 1;
337041394f3SDevin Teske 				case ';':
338041394f3SDevin Teske 					if (!quote && bsemicolon)
339041394f3SDevin Teske 						end = 1;
340041394f3SDevin Teske 					break;
341041394f3SDevin Teske 				}
342041394f3SDevin Teske 			} else if (*p == '\n')
343041394f3SDevin Teske 				/* Escaped newline character. increment */
344041394f3SDevin Teske 				line++;
345041394f3SDevin Teske 
346041394f3SDevin Teske 			/* Advance to the next character */
347041394f3SDevin Teske 			r = read(fd, p, 1);
348041394f3SDevin Teske 		}
349041394f3SDevin Teske 
350041394f3SDevin Teske 		/* Get the current offset */
351*f144058bSFaraz Vahedi 		if ((charpos = lseek(fd, 0, SEEK_CUR)) == -1) {
352041394f3SDevin Teske 			close(fd);
353041394f3SDevin Teske 			return (-1);
354041394f3SDevin Teske 		}
355041394f3SDevin Teske 
356041394f3SDevin Teske 		/* Get the length of the value */
357041394f3SDevin Teske 		n = (uint32_t)(charpos - curpos);
358041394f3SDevin Teske 		if (r != 0) /* more to read, but don't read ending key */
359041394f3SDevin Teske 			n--;
360041394f3SDevin Teske 
361041394f3SDevin Teske 		/* Move offset back to the beginning of the value */
362*f144058bSFaraz Vahedi 		if (lseek(fd, curpos, SEEK_SET) == -1) {
363041394f3SDevin Teske 			close(fd);
364041394f3SDevin Teske 			return (-1);
365041394f3SDevin Teske 		}
366041394f3SDevin Teske 
367041394f3SDevin Teske 		/* Allocate and read the value into memory */
368041394f3SDevin Teske 		if (n > vsize) {
369041394f3SDevin Teske 			if ((value = realloc(value, n + 1)) == NULL) {
370041394f3SDevin Teske 				close(fd);
371041394f3SDevin Teske 				return (-1);
372041394f3SDevin Teske 			}
373041394f3SDevin Teske 			vsize = n;
374041394f3SDevin Teske 		}
375041394f3SDevin Teske 		r = read(fd, value, n);
376041394f3SDevin Teske 
377041394f3SDevin Teske 		/* Terminate the string */
378041394f3SDevin Teske 		value[n] = '\0';
379041394f3SDevin Teske 
380041394f3SDevin Teske 		/* Cut trailing whitespace off by termination */
381041394f3SDevin Teske 		t = value + n;
382041394f3SDevin Teske 		while (isspace(*--t))
383041394f3SDevin Teske 			*t = '\0';
384041394f3SDevin Teske 
385041394f3SDevin Teske 		/* Escape the escaped quotes (replaceall is in string_m.c) */
386041394f3SDevin Teske 		x = strcount(value, "\\\""); /* in string_m.c */
387041394f3SDevin Teske 		if (x != 0 && (n + x) > vsize) {
388041394f3SDevin Teske 			if ((value = realloc(value, n + x + 1)) == NULL) {
389041394f3SDevin Teske 				close(fd);
390041394f3SDevin Teske 				return (-1);
391041394f3SDevin Teske 			}
392041394f3SDevin Teske 			vsize = n + x;
393041394f3SDevin Teske 		}
394041394f3SDevin Teske 		if (replaceall(value, "\\\"", "\\\\\"") < 0) {
395041394f3SDevin Teske 			/* Replace operation failed for some unknown reason */
396041394f3SDevin Teske 			close(fd);
397041394f3SDevin Teske 			return (-1);
398041394f3SDevin Teske 		}
399041394f3SDevin Teske 
400041394f3SDevin Teske 		/* Remove all new line characters */
401041394f3SDevin Teske 		if (replaceall(value, "\\\n", "") < 0) {
402041394f3SDevin Teske 			/* Replace operation failed for some unknown reason */
403041394f3SDevin Teske 			close(fd);
404041394f3SDevin Teske 			return (-1);
405041394f3SDevin Teske 		}
406041394f3SDevin Teske 
407041394f3SDevin Teske 		/* Resolve escape sequences */
408041394f3SDevin Teske 		strexpand(value); /* in string_m.c */
409041394f3SDevin Teske 
410041394f3SDevin Teske call_function:
411041394f3SDevin Teske 		/* Abort if we're seeking only assignments */
412041394f3SDevin Teske 		if (require_equals && !have_equals)
413041394f3SDevin Teske 			return (-1);
414041394f3SDevin Teske 
415041394f3SDevin Teske 		found = have_equals = 0; /* reset */
416041394f3SDevin Teske 
417041394f3SDevin Teske 		/* If there are no options defined, call unknown and loop */
418041394f3SDevin Teske 		if (options == NULL && unknown != NULL) {
419041394f3SDevin Teske 			error = unknown(NULL, line, directive, value);
420041394f3SDevin Teske 			if (error != 0) {
421041394f3SDevin Teske 				close(fd);
422041394f3SDevin Teske 				return (error);
423041394f3SDevin Teske 			}
424041394f3SDevin Teske 			continue;
425041394f3SDevin Teske 		}
426041394f3SDevin Teske 
427041394f3SDevin Teske 		/* Loop through the array looking for a match for the value */
428041394f3SDevin Teske 		for (n = 0; options[n].directive != NULL; n++) {
429041394f3SDevin Teske 			error = fnmatch(options[n].directive, directive,
430041394f3SDevin Teske 			    FNM_NOESCAPE);
431041394f3SDevin Teske 			if (error == 0) {
432041394f3SDevin Teske 				found = 1;
433041394f3SDevin Teske 				/* Call function for array index item */
434041394f3SDevin Teske 				if (options[n].action != NULL) {
435041394f3SDevin Teske 					error = options[n].action(
436041394f3SDevin Teske 					    &options[n],
437041394f3SDevin Teske 					    line, directive, value);
438041394f3SDevin Teske 					if (error != 0) {
439041394f3SDevin Teske 						close(fd);
440041394f3SDevin Teske 						return (error);
441041394f3SDevin Teske 					}
442041394f3SDevin Teske 				}
443041394f3SDevin Teske 			} else if (error != FNM_NOMATCH) {
444041394f3SDevin Teske 				/* An error has occurred */
445041394f3SDevin Teske 				close(fd);
446041394f3SDevin Teske 				return (-1);
447041394f3SDevin Teske 			}
448041394f3SDevin Teske 		}
449041394f3SDevin Teske 		if (!found && unknown != NULL) {
450041394f3SDevin Teske 			/*
451041394f3SDevin Teske 			 * No match was found for the value we read from the
452041394f3SDevin Teske 			 * file; call function designated for unknown values.
453041394f3SDevin Teske 			 */
454041394f3SDevin Teske 			error = unknown(NULL, line, directive, value);
455041394f3SDevin Teske 			if (error != 0) {
456041394f3SDevin Teske 				close(fd);
457041394f3SDevin Teske 				return (error);
458041394f3SDevin Teske 			}
459041394f3SDevin Teske 		}
460041394f3SDevin Teske 	}
461041394f3SDevin Teske 
462041394f3SDevin Teske 	close(fd);
463041394f3SDevin Teske 	return (0);
464041394f3SDevin Teske }
465