xref: /freebsd/lib/libfigpar/figpar.c (revision 041394f38a59889f0e14ace3306df5310cd5aeac)
1*041394f3SDevin Teske /*-
2*041394f3SDevin Teske  * Copyright (c) 2002-2014 Devin Teske <dteske@FreeBSD.org>
3*041394f3SDevin Teske  * All rights reserved.
4*041394f3SDevin Teske  *
5*041394f3SDevin Teske  * Redistribution and use in source and binary forms, with or without
6*041394f3SDevin Teske  * modification, are permitted provided that the following conditions
7*041394f3SDevin Teske  * are met:
8*041394f3SDevin Teske  * 1. Redistributions of source code must retain the above copyright
9*041394f3SDevin Teske  *    notice, this list of conditions and the following disclaimer.
10*041394f3SDevin Teske  * 2. Redistributions in binary form must reproduce the above copyright
11*041394f3SDevin Teske  *    notice, this list of conditions and the following disclaimer in the
12*041394f3SDevin Teske  *    documentation and/or other materials provided with the distribution.
13*041394f3SDevin Teske  *
14*041394f3SDevin Teske  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15*041394f3SDevin Teske  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16*041394f3SDevin Teske  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17*041394f3SDevin Teske  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18*041394f3SDevin Teske  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19*041394f3SDevin Teske  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20*041394f3SDevin Teske  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21*041394f3SDevin Teske  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22*041394f3SDevin Teske  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23*041394f3SDevin Teske  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24*041394f3SDevin Teske  * SUCH DAMAGE.
25*041394f3SDevin Teske  */
26*041394f3SDevin Teske 
27*041394f3SDevin Teske #include <sys/cdefs.h>
28*041394f3SDevin Teske __FBSDID("$FreeBSD$");
29*041394f3SDevin Teske 
30*041394f3SDevin Teske #include <sys/param.h>
31*041394f3SDevin Teske 
32*041394f3SDevin Teske #include <ctype.h>
33*041394f3SDevin Teske #include <errno.h>
34*041394f3SDevin Teske #include <fcntl.h>
35*041394f3SDevin Teske #include <fnmatch.h>
36*041394f3SDevin Teske #include <stdlib.h>
37*041394f3SDevin Teske #include <string.h>
38*041394f3SDevin Teske #include <unistd.h>
39*041394f3SDevin Teske 
40*041394f3SDevin Teske #include "figpar.h"
41*041394f3SDevin Teske #include "string_m.h"
42*041394f3SDevin Teske 
43*041394f3SDevin Teske struct fp_config fp_dummy_config = {0, NULL, {0}, NULL};
44*041394f3SDevin Teske 
45*041394f3SDevin Teske /*
46*041394f3SDevin Teske  * Search for config option (struct fp_config) in the array of config options,
47*041394f3SDevin Teske  * returning the struct whose directive matches the given parameter. If no
48*041394f3SDevin Teske  * match is found, a pointer to the static dummy array (above) is returned.
49*041394f3SDevin Teske  *
50*041394f3SDevin Teske  * This is to eliminate dependency on the index position of an item in the
51*041394f3SDevin Teske  * array, since the index position is more apt to be changed as code grows.
52*041394f3SDevin Teske  */
53*041394f3SDevin Teske struct fp_config *
54*041394f3SDevin Teske get_config_option(struct fp_config options[], const char *directive)
55*041394f3SDevin Teske {
56*041394f3SDevin Teske 	uint32_t n;
57*041394f3SDevin Teske 
58*041394f3SDevin Teske 	/* Check arguments */
59*041394f3SDevin Teske 	if (options == NULL || directive == NULL)
60*041394f3SDevin Teske 		return (&fp_dummy_config);
61*041394f3SDevin Teske 
62*041394f3SDevin Teske 	/* Loop through the array, return the index of the first match */
63*041394f3SDevin Teske 	for (n = 0; options[n].directive != NULL; n++)
64*041394f3SDevin Teske 		if (strcmp(options[n].directive, directive) == 0)
65*041394f3SDevin Teske 			return (&(options[n]));
66*041394f3SDevin Teske 
67*041394f3SDevin Teske 	/* Re-initialize the dummy variable in case it was written to */
68*041394f3SDevin Teske 	fp_dummy_config.directive	= NULL;
69*041394f3SDevin Teske 	fp_dummy_config.type		= 0;
70*041394f3SDevin Teske 	fp_dummy_config.action		= NULL;
71*041394f3SDevin Teske 	fp_dummy_config.value.u_num	= 0;
72*041394f3SDevin Teske 
73*041394f3SDevin Teske 	return (&fp_dummy_config);
74*041394f3SDevin Teske }
75*041394f3SDevin Teske 
76*041394f3SDevin Teske /*
77*041394f3SDevin Teske  * Parse the configuration file at `path' and execute the `action' call-back
78*041394f3SDevin Teske  * functions for any directives defined by the array of config options (first
79*041394f3SDevin Teske  * argument).
80*041394f3SDevin Teske  *
81*041394f3SDevin Teske  * For unknown directives that are encountered, you can optionally pass a
82*041394f3SDevin Teske  * call-back function for the third argument to be called for unknowns.
83*041394f3SDevin Teske  *
84*041394f3SDevin Teske  * Returns zero on success; otherwise returns -1 and errno should be consulted.
85*041394f3SDevin Teske */
86*041394f3SDevin Teske int
87*041394f3SDevin Teske parse_config(struct fp_config options[], const char *path,
88*041394f3SDevin Teske     int (*unknown)(struct fp_config *option, uint32_t line, char *directive,
89*041394f3SDevin Teske     char *value), uint16_t processing_options)
90*041394f3SDevin Teske {
91*041394f3SDevin Teske 	uint8_t bequals;
92*041394f3SDevin Teske 	uint8_t bsemicolon;
93*041394f3SDevin Teske 	uint8_t case_sensitive;
94*041394f3SDevin Teske 	uint8_t comment = 0;
95*041394f3SDevin Teske 	uint8_t end;
96*041394f3SDevin Teske 	uint8_t found;
97*041394f3SDevin Teske 	uint8_t have_equals = 0;
98*041394f3SDevin Teske 	uint8_t quote;
99*041394f3SDevin Teske 	uint8_t require_equals;
100*041394f3SDevin Teske 	uint8_t strict_equals;
101*041394f3SDevin Teske 	char p[2];
102*041394f3SDevin Teske 	char *directive;
103*041394f3SDevin Teske 	char *t;
104*041394f3SDevin Teske 	char *value;
105*041394f3SDevin Teske 	int error;
106*041394f3SDevin Teske 	int fd;
107*041394f3SDevin Teske 	ssize_t r = 1;
108*041394f3SDevin Teske 	uint32_t dsize;
109*041394f3SDevin Teske 	uint32_t line = 1;
110*041394f3SDevin Teske 	uint32_t n;
111*041394f3SDevin Teske 	uint32_t vsize;
112*041394f3SDevin Teske 	uint32_t x;
113*041394f3SDevin Teske 	off_t charpos;
114*041394f3SDevin Teske 	off_t curpos;
115*041394f3SDevin Teske 	char rpath[PATH_MAX];
116*041394f3SDevin Teske 
117*041394f3SDevin Teske 	/* Sanity check: if no options and no unknown function, return */
118*041394f3SDevin Teske 	if (options == NULL && unknown == NULL)
119*041394f3SDevin Teske 		return (-1);
120*041394f3SDevin Teske 
121*041394f3SDevin Teske 	/* Processing options */
122*041394f3SDevin Teske 	bequals = (processing_options & FP_BREAK_ON_EQUALS) == 0 ? 0 : 1;
123*041394f3SDevin Teske 	bsemicolon = (processing_options & FP_BREAK_ON_SEMICOLON) == 0 ? 0 : 1;
124*041394f3SDevin Teske 	case_sensitive = (processing_options & FP_CASE_SENSITIVE) == 0 ? 0 : 1;
125*041394f3SDevin Teske 	require_equals = (processing_options & FP_REQUIRE_EQUALS) == 0 ? 0 : 1;
126*041394f3SDevin Teske 	strict_equals = (processing_options & FP_STRICT_EQUALS) == 0 ? 0 : 1;
127*041394f3SDevin Teske 
128*041394f3SDevin Teske 	/* Initialize strings */
129*041394f3SDevin Teske 	directive = value = 0;
130*041394f3SDevin Teske 	vsize = dsize = 0;
131*041394f3SDevin Teske 
132*041394f3SDevin Teske 	/* Resolve the file path */
133*041394f3SDevin Teske 	if (realpath(path, rpath) == 0)
134*041394f3SDevin Teske 		return (-1);
135*041394f3SDevin Teske 
136*041394f3SDevin Teske 	/* Open the file */
137*041394f3SDevin Teske 	if ((fd = open(rpath, O_RDONLY)) < 0)
138*041394f3SDevin Teske 		return (-1);
139*041394f3SDevin Teske 
140*041394f3SDevin Teske 	/* Read the file until EOF */
141*041394f3SDevin Teske 	while (r != 0) {
142*041394f3SDevin Teske 		r = read(fd, p, 1);
143*041394f3SDevin Teske 
144*041394f3SDevin Teske 		/* skip to the beginning of a directive */
145*041394f3SDevin Teske 		while (r != 0 && (isspace(*p) || *p == '#' || comment ||
146*041394f3SDevin Teske 		    (bsemicolon && *p == ';'))) {
147*041394f3SDevin Teske 			if (*p == '#')
148*041394f3SDevin Teske 				comment = 1;
149*041394f3SDevin Teske 			else if (*p == '\n') {
150*041394f3SDevin Teske 				comment = 0;
151*041394f3SDevin Teske 				line++;
152*041394f3SDevin Teske 			}
153*041394f3SDevin Teske 			r = read(fd, p, 1);
154*041394f3SDevin Teske 		}
155*041394f3SDevin Teske 		/* Test for EOF; if EOF then no directive was found */
156*041394f3SDevin Teske 		if (r == 0) {
157*041394f3SDevin Teske 			close(fd);
158*041394f3SDevin Teske 			return (0);
159*041394f3SDevin Teske 		}
160*041394f3SDevin Teske 
161*041394f3SDevin Teske 		/* Get the current offset */
162*041394f3SDevin Teske 		curpos = lseek(fd, 0, SEEK_CUR) - 1;
163*041394f3SDevin Teske 		if (curpos == -1) {
164*041394f3SDevin Teske 			close(fd);
165*041394f3SDevin Teske 			return (-1);
166*041394f3SDevin Teske 		}
167*041394f3SDevin Teske 
168*041394f3SDevin Teske 		/* Find the length of the directive */
169*041394f3SDevin Teske 		for (n = 0; r != 0; n++) {
170*041394f3SDevin Teske 			if (isspace(*p))
171*041394f3SDevin Teske 				break;
172*041394f3SDevin Teske 			if (bequals && *p == '=') {
173*041394f3SDevin Teske 				have_equals = 1;
174*041394f3SDevin Teske 				break;
175*041394f3SDevin Teske 			}
176*041394f3SDevin Teske 			if (bsemicolon && *p == ';')
177*041394f3SDevin Teske 				break;
178*041394f3SDevin Teske 			r = read(fd, p, 1);
179*041394f3SDevin Teske 		}
180*041394f3SDevin Teske 
181*041394f3SDevin Teske 		/* Test for EOF, if EOF then no directive was found */
182*041394f3SDevin Teske 		if (n == 0 && r == 0) {
183*041394f3SDevin Teske 			close(fd);
184*041394f3SDevin Teske 			return (0);
185*041394f3SDevin Teske 		}
186*041394f3SDevin Teske 
187*041394f3SDevin Teske 		/* Go back to the beginning of the directive */
188*041394f3SDevin Teske 		error = (int)lseek(fd, curpos, SEEK_SET);
189*041394f3SDevin Teske 		if (error == (curpos - 1)) {
190*041394f3SDevin Teske 			close(fd);
191*041394f3SDevin Teske 			return (-1);
192*041394f3SDevin Teske 		}
193*041394f3SDevin Teske 
194*041394f3SDevin Teske 		/* Allocate and read the directive into memory */
195*041394f3SDevin Teske 		if (n > dsize) {
196*041394f3SDevin Teske 			if ((directive = realloc(directive, n + 1)) == NULL) {
197*041394f3SDevin Teske 				close(fd);
198*041394f3SDevin Teske 				return (-1);
199*041394f3SDevin Teske 			}
200*041394f3SDevin Teske 			dsize = n;
201*041394f3SDevin Teske 		}
202*041394f3SDevin Teske 		r = read(fd, directive, n);
203*041394f3SDevin Teske 
204*041394f3SDevin Teske 		/* Advance beyond the equals sign if appropriate/desired */
205*041394f3SDevin Teske 		if (bequals && *p == '=') {
206*041394f3SDevin Teske 			if (lseek(fd, 1, SEEK_CUR) != -1)
207*041394f3SDevin Teske 				r = read(fd, p, 1);
208*041394f3SDevin Teske 			if (strict_equals && isspace(*p))
209*041394f3SDevin Teske 				*p = '\n';
210*041394f3SDevin Teske 		}
211*041394f3SDevin Teske 
212*041394f3SDevin Teske 		/* Terminate the string */
213*041394f3SDevin Teske 		directive[n] = '\0';
214*041394f3SDevin Teske 
215*041394f3SDevin Teske 		/* Convert directive to lower case before comparison */
216*041394f3SDevin Teske 		if (!case_sensitive)
217*041394f3SDevin Teske 			strtolower(directive);
218*041394f3SDevin Teske 
219*041394f3SDevin Teske 		/* Move to what may be the start of the value */
220*041394f3SDevin Teske 		if (!(bsemicolon && *p == ';') &&
221*041394f3SDevin Teske 		    !(strict_equals && *p == '=')) {
222*041394f3SDevin Teske 			while (r != 0 && isspace(*p) && *p != '\n')
223*041394f3SDevin Teske 				r = read(fd, p, 1);
224*041394f3SDevin Teske 		}
225*041394f3SDevin Teske 
226*041394f3SDevin Teske 		/* An equals sign may have stopped us, should we eat it? */
227*041394f3SDevin Teske 		if (r != 0 && bequals && *p == '=' && !strict_equals) {
228*041394f3SDevin Teske 			have_equals = 1;
229*041394f3SDevin Teske 			r = read(fd, p, 1);
230*041394f3SDevin Teske 			while (r != 0 && isspace(*p) && *p != '\n')
231*041394f3SDevin Teske 				r = read(fd, p, 1);
232*041394f3SDevin Teske 		}
233*041394f3SDevin Teske 
234*041394f3SDevin Teske 		/* If no value, allocate a dummy value and jump to action */
235*041394f3SDevin Teske 		if (r == 0 || *p == '\n' || *p == '#' ||
236*041394f3SDevin Teske 		    (bsemicolon && *p == ';')) {
237*041394f3SDevin Teske 			/* Initialize the value if not already done */
238*041394f3SDevin Teske 			if (value == NULL && (value = malloc(1)) == NULL) {
239*041394f3SDevin Teske 				close(fd);
240*041394f3SDevin Teske 				return (-1);
241*041394f3SDevin Teske 			}
242*041394f3SDevin Teske 			value[0] = '\0';
243*041394f3SDevin Teske 			goto call_function;
244*041394f3SDevin Teske 		}
245*041394f3SDevin Teske 
246*041394f3SDevin Teske 		/* Get the current offset */
247*041394f3SDevin Teske 		curpos = lseek(fd, 0, SEEK_CUR) - 1;
248*041394f3SDevin Teske 		if (curpos == -1) {
249*041394f3SDevin Teske 			close(fd);
250*041394f3SDevin Teske 			return (-1);
251*041394f3SDevin Teske 		}
252*041394f3SDevin Teske 
253*041394f3SDevin Teske 		/* Find the end of the value */
254*041394f3SDevin Teske 		quote = 0;
255*041394f3SDevin Teske 		end = 0;
256*041394f3SDevin Teske 		while (r != 0 && end == 0) {
257*041394f3SDevin Teske 			/* Advance to the next character if we know we can */
258*041394f3SDevin Teske 			if (*p != '\"' && *p != '#' && *p != '\n' &&
259*041394f3SDevin Teske 			    (!bsemicolon || *p != ';')) {
260*041394f3SDevin Teske 				r = read(fd, p, 1);
261*041394f3SDevin Teske 				continue;
262*041394f3SDevin Teske 			}
263*041394f3SDevin Teske 
264*041394f3SDevin Teske 			/*
265*041394f3SDevin Teske 			 * If we get this far, we've hit an end-key
266*041394f3SDevin Teske 			 */
267*041394f3SDevin Teske 
268*041394f3SDevin Teske 			/* Get the current offset */
269*041394f3SDevin Teske 			charpos = lseek(fd, 0, SEEK_CUR) - 1;
270*041394f3SDevin Teske 			if (charpos == -1) {
271*041394f3SDevin Teske 				close(fd);
272*041394f3SDevin Teske 				return (-1);
273*041394f3SDevin Teske 			}
274*041394f3SDevin Teske 
275*041394f3SDevin Teske 			/*
276*041394f3SDevin Teske 			 * Go back so we can read the character before the key
277*041394f3SDevin Teske 			 * to check if the character is escaped (which means we
278*041394f3SDevin Teske 			 * should continue).
279*041394f3SDevin Teske 			 */
280*041394f3SDevin Teske 			error = (int)lseek(fd, -2, SEEK_CUR);
281*041394f3SDevin Teske 			if (error == -3) {
282*041394f3SDevin Teske 				close(fd);
283*041394f3SDevin Teske 				return (-1);
284*041394f3SDevin Teske 			}
285*041394f3SDevin Teske 			r = read(fd, p, 1);
286*041394f3SDevin Teske 
287*041394f3SDevin Teske 			/*
288*041394f3SDevin Teske 			 * Count how many backslashes there are (an odd number
289*041394f3SDevin Teske 			 * means the key is escaped, even means otherwise).
290*041394f3SDevin Teske 			 */
291*041394f3SDevin Teske 			for (n = 1; *p == '\\'; n++) {
292*041394f3SDevin Teske 				/* Move back another offset to read */
293*041394f3SDevin Teske 				error = (int)lseek(fd, -2, SEEK_CUR);
294*041394f3SDevin Teske 				if (error == -3) {
295*041394f3SDevin Teske 					close(fd);
296*041394f3SDevin Teske 					return (-1);
297*041394f3SDevin Teske 				}
298*041394f3SDevin Teske 				r = read(fd, p, 1);
299*041394f3SDevin Teske 			}
300*041394f3SDevin Teske 
301*041394f3SDevin Teske 			/* Move offset back to the key and read it */
302*041394f3SDevin Teske 			error = (int)lseek(fd, charpos, SEEK_SET);
303*041394f3SDevin Teske 			if (error == (charpos - 1)) {
304*041394f3SDevin Teske 				close(fd);
305*041394f3SDevin Teske 				return (-1);
306*041394f3SDevin Teske 			}
307*041394f3SDevin Teske 			r = read(fd, p, 1);
308*041394f3SDevin Teske 
309*041394f3SDevin Teske 			/*
310*041394f3SDevin Teske 			 * If an even number of backslashes was counted meaning
311*041394f3SDevin Teske 			 * key is not escaped, we should evaluate what to do.
312*041394f3SDevin Teske 			 */
313*041394f3SDevin Teske 			if ((n & 1) == 1) {
314*041394f3SDevin Teske 				switch (*p) {
315*041394f3SDevin Teske 				case '\"':
316*041394f3SDevin Teske 					/*
317*041394f3SDevin Teske 				 	 * Flag current sequence of characters
318*041394f3SDevin Teske 					 * to follow as being quoted (hashes
319*041394f3SDevin Teske 					 * are not considered comments).
320*041394f3SDevin Teske 					 */
321*041394f3SDevin Teske 					quote = !quote;
322*041394f3SDevin Teske 					break;
323*041394f3SDevin Teske 				case '#':
324*041394f3SDevin Teske 					/*
325*041394f3SDevin Teske 					 * If we aren't in a quoted series, we
326*041394f3SDevin Teske 					 * just hit an inline comment and have
327*041394f3SDevin Teske 					 * found the end of the value.
328*041394f3SDevin Teske 					 */
329*041394f3SDevin Teske 					if (!quote)
330*041394f3SDevin Teske 						end = 1;
331*041394f3SDevin Teske 					break;
332*041394f3SDevin Teske 				case '\n':
333*041394f3SDevin Teske 					/*
334*041394f3SDevin Teske 					 * Newline characters must always be
335*041394f3SDevin Teske 					 * escaped, whether inside a quoted
336*041394f3SDevin Teske 					 * series or not, otherwise they
337*041394f3SDevin Teske 					 * terminate the value.
338*041394f3SDevin Teske 					 */
339*041394f3SDevin Teske 					end = 1;
340*041394f3SDevin Teske 				case ';':
341*041394f3SDevin Teske 					if (!quote && bsemicolon)
342*041394f3SDevin Teske 						end = 1;
343*041394f3SDevin Teske 					break;
344*041394f3SDevin Teske 				}
345*041394f3SDevin Teske 			} else if (*p == '\n')
346*041394f3SDevin Teske 				/* Escaped newline character. increment */
347*041394f3SDevin Teske 				line++;
348*041394f3SDevin Teske 
349*041394f3SDevin Teske 			/* Advance to the next character */
350*041394f3SDevin Teske 			r = read(fd, p, 1);
351*041394f3SDevin Teske 		}
352*041394f3SDevin Teske 
353*041394f3SDevin Teske 		/* Get the current offset */
354*041394f3SDevin Teske 		charpos = lseek(fd, 0, SEEK_CUR) - 1;
355*041394f3SDevin Teske 		if (charpos == -1) {
356*041394f3SDevin Teske 			close(fd);
357*041394f3SDevin Teske 			return (-1);
358*041394f3SDevin Teske 		}
359*041394f3SDevin Teske 
360*041394f3SDevin Teske 		/* Get the length of the value */
361*041394f3SDevin Teske 		n = (uint32_t)(charpos - curpos);
362*041394f3SDevin Teske 		if (r != 0) /* more to read, but don't read ending key */
363*041394f3SDevin Teske 			n--;
364*041394f3SDevin Teske 
365*041394f3SDevin Teske 		/* Move offset back to the beginning of the value */
366*041394f3SDevin Teske 		error = (int)lseek(fd, curpos, SEEK_SET);
367*041394f3SDevin Teske 		if (error == (curpos - 1)) {
368*041394f3SDevin Teske 			close(fd);
369*041394f3SDevin Teske 			return (-1);
370*041394f3SDevin Teske 		}
371*041394f3SDevin Teske 
372*041394f3SDevin Teske 		/* Allocate and read the value into memory */
373*041394f3SDevin Teske 		if (n > vsize) {
374*041394f3SDevin Teske 			if ((value = realloc(value, n + 1)) == NULL) {
375*041394f3SDevin Teske 				close(fd);
376*041394f3SDevin Teske 				return (-1);
377*041394f3SDevin Teske 			}
378*041394f3SDevin Teske 			vsize = n;
379*041394f3SDevin Teske 		}
380*041394f3SDevin Teske 		r = read(fd, value, n);
381*041394f3SDevin Teske 
382*041394f3SDevin Teske 		/* Terminate the string */
383*041394f3SDevin Teske 		value[n] = '\0';
384*041394f3SDevin Teske 
385*041394f3SDevin Teske 		/* Cut trailing whitespace off by termination */
386*041394f3SDevin Teske 		t = value + n;
387*041394f3SDevin Teske 		while (isspace(*--t))
388*041394f3SDevin Teske 			*t = '\0';
389*041394f3SDevin Teske 
390*041394f3SDevin Teske 		/* Escape the escaped quotes (replaceall is in string_m.c) */
391*041394f3SDevin Teske 		x = strcount(value, "\\\""); /* in string_m.c */
392*041394f3SDevin Teske 		if (x != 0 && (n + x) > vsize) {
393*041394f3SDevin Teske 			if ((value = realloc(value, n + x + 1)) == NULL) {
394*041394f3SDevin Teske 				close(fd);
395*041394f3SDevin Teske 				return (-1);
396*041394f3SDevin Teske 			}
397*041394f3SDevin Teske 			vsize = n + x;
398*041394f3SDevin Teske 		}
399*041394f3SDevin Teske 		if (replaceall(value, "\\\"", "\\\\\"") < 0) {
400*041394f3SDevin Teske 			/* Replace operation failed for some unknown reason */
401*041394f3SDevin Teske 			close(fd);
402*041394f3SDevin Teske 			return (-1);
403*041394f3SDevin Teske 		}
404*041394f3SDevin Teske 
405*041394f3SDevin Teske 		/* Remove all new line characters */
406*041394f3SDevin Teske 		if (replaceall(value, "\\\n", "") < 0) {
407*041394f3SDevin Teske 			/* Replace operation failed for some unknown reason */
408*041394f3SDevin Teske 			close(fd);
409*041394f3SDevin Teske 			return (-1);
410*041394f3SDevin Teske 		}
411*041394f3SDevin Teske 
412*041394f3SDevin Teske 		/* Resolve escape sequences */
413*041394f3SDevin Teske 		strexpand(value); /* in string_m.c */
414*041394f3SDevin Teske 
415*041394f3SDevin Teske call_function:
416*041394f3SDevin Teske 		/* Abort if we're seeking only assignments */
417*041394f3SDevin Teske 		if (require_equals && !have_equals)
418*041394f3SDevin Teske 			return (-1);
419*041394f3SDevin Teske 
420*041394f3SDevin Teske 		found = have_equals = 0; /* reset */
421*041394f3SDevin Teske 
422*041394f3SDevin Teske 		/* If there are no options defined, call unknown and loop */
423*041394f3SDevin Teske 		if (options == NULL && unknown != NULL) {
424*041394f3SDevin Teske 			error = unknown(NULL, line, directive, value);
425*041394f3SDevin Teske 			if (error != 0) {
426*041394f3SDevin Teske 				close(fd);
427*041394f3SDevin Teske 				return (error);
428*041394f3SDevin Teske 			}
429*041394f3SDevin Teske 			continue;
430*041394f3SDevin Teske 		}
431*041394f3SDevin Teske 
432*041394f3SDevin Teske 		/* Loop through the array looking for a match for the value */
433*041394f3SDevin Teske 		for (n = 0; options[n].directive != NULL; n++) {
434*041394f3SDevin Teske 			error = fnmatch(options[n].directive, directive,
435*041394f3SDevin Teske 			    FNM_NOESCAPE);
436*041394f3SDevin Teske 			if (error == 0) {
437*041394f3SDevin Teske 				found = 1;
438*041394f3SDevin Teske 				/* Call function for array index item */
439*041394f3SDevin Teske 				if (options[n].action != NULL) {
440*041394f3SDevin Teske 					error = options[n].action(
441*041394f3SDevin Teske 					    &options[n],
442*041394f3SDevin Teske 					    line, directive, value);
443*041394f3SDevin Teske 					if (error != 0) {
444*041394f3SDevin Teske 						close(fd);
445*041394f3SDevin Teske 						return (error);
446*041394f3SDevin Teske 					}
447*041394f3SDevin Teske 				}
448*041394f3SDevin Teske 			} else if (error != FNM_NOMATCH) {
449*041394f3SDevin Teske 				/* An error has occurred */
450*041394f3SDevin Teske 				close(fd);
451*041394f3SDevin Teske 				return (-1);
452*041394f3SDevin Teske 			}
453*041394f3SDevin Teske 		}
454*041394f3SDevin Teske 		if (!found && unknown != NULL) {
455*041394f3SDevin Teske 			/*
456*041394f3SDevin Teske 			 * No match was found for the value we read from the
457*041394f3SDevin Teske 			 * file; call function designated for unknown values.
458*041394f3SDevin Teske 			 */
459*041394f3SDevin Teske 			error = unknown(NULL, line, directive, value);
460*041394f3SDevin Teske 			if (error != 0) {
461*041394f3SDevin Teske 				close(fd);
462*041394f3SDevin Teske 				return (error);
463*041394f3SDevin Teske 			}
464*041394f3SDevin Teske 		}
465*041394f3SDevin Teske 	}
466*041394f3SDevin Teske 
467*041394f3SDevin Teske 	close(fd);
468*041394f3SDevin Teske 	return (0);
469*041394f3SDevin Teske }
470