xref: /freebsd/lib/libfigpar/figpar.c (revision 9d9cc24662d95b56a87e2d92d57899224cec42df)
1041394f3SDevin Teske /*-
2*9d9cc246SDevin 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/cdefs.h>
28041394f3SDevin Teske __FBSDID("$FreeBSD$");
29041394f3SDevin Teske 
30041394f3SDevin Teske #include <sys/param.h>
31041394f3SDevin Teske 
32041394f3SDevin Teske #include <ctype.h>
33041394f3SDevin Teske #include <errno.h>
34041394f3SDevin Teske #include <fcntl.h>
35041394f3SDevin Teske #include <fnmatch.h>
36041394f3SDevin Teske #include <stdlib.h>
37041394f3SDevin Teske #include <string.h>
38041394f3SDevin Teske #include <unistd.h>
39041394f3SDevin Teske 
40041394f3SDevin Teske #include "figpar.h"
41041394f3SDevin Teske #include "string_m.h"
42041394f3SDevin Teske 
43*9d9cc246SDevin Teske struct figpar_config figpar_dummy_config = {0, NULL, {0}, NULL};
44041394f3SDevin Teske 
45041394f3SDevin Teske /*
46*9d9cc246SDevin Teske  * Search for config option (struct figpar_config) in the array of config
47*9d9cc246SDevin Teske  * options, returning the struct whose directive matches the given parameter.
48*9d9cc246SDevin Teske  * If no match is found, a pointer to the static dummy array (above) is
49*9d9cc246SDevin Teske  * returned.
50041394f3SDevin Teske  *
51041394f3SDevin Teske  * This is to eliminate dependency on the index position of an item in the
52041394f3SDevin Teske  * array, since the index position is more apt to be changed as code grows.
53041394f3SDevin Teske  */
54*9d9cc246SDevin Teske struct figpar_config *
55*9d9cc246SDevin Teske get_config_option(struct figpar_config options[], const char *directive)
56041394f3SDevin Teske {
57041394f3SDevin Teske 	uint32_t n;
58041394f3SDevin Teske 
59041394f3SDevin Teske 	/* Check arguments */
60041394f3SDevin Teske 	if (options == NULL || directive == NULL)
61*9d9cc246SDevin Teske 		return (&figpar_dummy_config);
62041394f3SDevin Teske 
63041394f3SDevin Teske 	/* Loop through the array, return the index of the first match */
64041394f3SDevin Teske 	for (n = 0; options[n].directive != NULL; n++)
65041394f3SDevin Teske 		if (strcmp(options[n].directive, directive) == 0)
66041394f3SDevin Teske 			return (&(options[n]));
67041394f3SDevin Teske 
68041394f3SDevin Teske 	/* Re-initialize the dummy variable in case it was written to */
69*9d9cc246SDevin Teske 	figpar_dummy_config.directive	= NULL;
70*9d9cc246SDevin Teske 	figpar_dummy_config.type	= 0;
71*9d9cc246SDevin Teske 	figpar_dummy_config.action	= NULL;
72*9d9cc246SDevin Teske 	figpar_dummy_config.value.u_num	= 0;
73041394f3SDevin Teske 
74*9d9cc246SDevin Teske 	return (&figpar_dummy_config);
75041394f3SDevin Teske }
76041394f3SDevin Teske 
77041394f3SDevin Teske /*
78041394f3SDevin Teske  * Parse the configuration file at `path' and execute the `action' call-back
79041394f3SDevin Teske  * functions for any directives defined by the array of config options (first
80041394f3SDevin Teske  * argument).
81041394f3SDevin Teske  *
82041394f3SDevin Teske  * For unknown directives that are encountered, you can optionally pass a
83041394f3SDevin Teske  * call-back function for the third argument to be called for unknowns.
84041394f3SDevin Teske  *
85041394f3SDevin Teske  * Returns zero on success; otherwise returns -1 and errno should be consulted.
86041394f3SDevin Teske */
87041394f3SDevin Teske int
88*9d9cc246SDevin Teske parse_config(struct figpar_config options[], const char *path,
89*9d9cc246SDevin Teske     int (*unknown)(struct figpar_config *option, uint32_t line,
90*9d9cc246SDevin Teske     char *directive, char *value), uint16_t processing_options)
91041394f3SDevin Teske {
92041394f3SDevin Teske 	uint8_t bequals;
93041394f3SDevin Teske 	uint8_t bsemicolon;
94041394f3SDevin Teske 	uint8_t case_sensitive;
95041394f3SDevin Teske 	uint8_t comment = 0;
96041394f3SDevin Teske 	uint8_t end;
97041394f3SDevin Teske 	uint8_t found;
98041394f3SDevin Teske 	uint8_t have_equals = 0;
99041394f3SDevin Teske 	uint8_t quote;
100041394f3SDevin Teske 	uint8_t require_equals;
101041394f3SDevin Teske 	uint8_t strict_equals;
102041394f3SDevin Teske 	char p[2];
103041394f3SDevin Teske 	char *directive;
104041394f3SDevin Teske 	char *t;
105041394f3SDevin Teske 	char *value;
106041394f3SDevin Teske 	int error;
107041394f3SDevin Teske 	int fd;
108041394f3SDevin Teske 	ssize_t r = 1;
109041394f3SDevin Teske 	uint32_t dsize;
110041394f3SDevin Teske 	uint32_t line = 1;
111041394f3SDevin Teske 	uint32_t n;
112041394f3SDevin Teske 	uint32_t vsize;
113041394f3SDevin Teske 	uint32_t x;
114041394f3SDevin Teske 	off_t charpos;
115041394f3SDevin Teske 	off_t curpos;
116041394f3SDevin Teske 	char rpath[PATH_MAX];
117041394f3SDevin Teske 
118041394f3SDevin Teske 	/* Sanity check: if no options and no unknown function, return */
119041394f3SDevin Teske 	if (options == NULL && unknown == NULL)
120041394f3SDevin Teske 		return (-1);
121041394f3SDevin Teske 
122041394f3SDevin Teske 	/* Processing options */
123*9d9cc246SDevin Teske 	bequals = (processing_options & FIGPAR_BREAK_ON_EQUALS) == 0 ? 0 : 1;
124*9d9cc246SDevin Teske 	bsemicolon =
125*9d9cc246SDevin Teske 		(processing_options & FIGPAR_BREAK_ON_SEMICOLON) == 0 ? 0 : 1;
126*9d9cc246SDevin Teske 	case_sensitive =
127*9d9cc246SDevin Teske 		(processing_options & FIGPAR_CASE_SENSITIVE) == 0 ? 0 : 1;
128*9d9cc246SDevin Teske 	require_equals =
129*9d9cc246SDevin Teske 		(processing_options & FIGPAR_REQUIRE_EQUALS) == 0 ? 0 : 1;
130*9d9cc246SDevin Teske 	strict_equals =
131*9d9cc246SDevin Teske 		(processing_options & FIGPAR_STRICT_EQUALS) == 0 ? 0 : 1;
132041394f3SDevin Teske 
133041394f3SDevin Teske 	/* Initialize strings */
134041394f3SDevin Teske 	directive = value = 0;
135041394f3SDevin Teske 	vsize = dsize = 0;
136041394f3SDevin Teske 
137041394f3SDevin Teske 	/* Resolve the file path */
138041394f3SDevin Teske 	if (realpath(path, rpath) == 0)
139041394f3SDevin Teske 		return (-1);
140041394f3SDevin Teske 
141041394f3SDevin Teske 	/* Open the file */
142041394f3SDevin Teske 	if ((fd = open(rpath, O_RDONLY)) < 0)
143041394f3SDevin Teske 		return (-1);
144041394f3SDevin Teske 
145041394f3SDevin Teske 	/* Read the file until EOF */
146041394f3SDevin Teske 	while (r != 0) {
147041394f3SDevin Teske 		r = read(fd, p, 1);
148041394f3SDevin Teske 
149041394f3SDevin Teske 		/* skip to the beginning of a directive */
150041394f3SDevin Teske 		while (r != 0 && (isspace(*p) || *p == '#' || comment ||
151041394f3SDevin Teske 		    (bsemicolon && *p == ';'))) {
152041394f3SDevin Teske 			if (*p == '#')
153041394f3SDevin Teske 				comment = 1;
154041394f3SDevin Teske 			else if (*p == '\n') {
155041394f3SDevin Teske 				comment = 0;
156041394f3SDevin Teske 				line++;
157041394f3SDevin Teske 			}
158041394f3SDevin Teske 			r = read(fd, p, 1);
159041394f3SDevin Teske 		}
160041394f3SDevin Teske 		/* Test for EOF; if EOF then no directive was found */
161041394f3SDevin Teske 		if (r == 0) {
162041394f3SDevin Teske 			close(fd);
163041394f3SDevin Teske 			return (0);
164041394f3SDevin Teske 		}
165041394f3SDevin Teske 
166041394f3SDevin Teske 		/* Get the current offset */
167041394f3SDevin Teske 		curpos = lseek(fd, 0, SEEK_CUR) - 1;
168041394f3SDevin Teske 		if (curpos == -1) {
169041394f3SDevin Teske 			close(fd);
170041394f3SDevin Teske 			return (-1);
171041394f3SDevin Teske 		}
172041394f3SDevin Teske 
173041394f3SDevin Teske 		/* Find the length of the directive */
174041394f3SDevin Teske 		for (n = 0; r != 0; n++) {
175041394f3SDevin Teske 			if (isspace(*p))
176041394f3SDevin Teske 				break;
177041394f3SDevin Teske 			if (bequals && *p == '=') {
178041394f3SDevin Teske 				have_equals = 1;
179041394f3SDevin Teske 				break;
180041394f3SDevin Teske 			}
181041394f3SDevin Teske 			if (bsemicolon && *p == ';')
182041394f3SDevin Teske 				break;
183041394f3SDevin Teske 			r = read(fd, p, 1);
184041394f3SDevin Teske 		}
185041394f3SDevin Teske 
186041394f3SDevin Teske 		/* Test for EOF, if EOF then no directive was found */
187041394f3SDevin Teske 		if (n == 0 && r == 0) {
188041394f3SDevin Teske 			close(fd);
189041394f3SDevin Teske 			return (0);
190041394f3SDevin Teske 		}
191041394f3SDevin Teske 
192041394f3SDevin Teske 		/* Go back to the beginning of the directive */
193041394f3SDevin Teske 		error = (int)lseek(fd, curpos, SEEK_SET);
194041394f3SDevin Teske 		if (error == (curpos - 1)) {
195041394f3SDevin Teske 			close(fd);
196041394f3SDevin Teske 			return (-1);
197041394f3SDevin Teske 		}
198041394f3SDevin Teske 
199041394f3SDevin Teske 		/* Allocate and read the directive into memory */
200041394f3SDevin Teske 		if (n > dsize) {
201041394f3SDevin Teske 			if ((directive = realloc(directive, n + 1)) == NULL) {
202041394f3SDevin Teske 				close(fd);
203041394f3SDevin Teske 				return (-1);
204041394f3SDevin Teske 			}
205041394f3SDevin Teske 			dsize = n;
206041394f3SDevin Teske 		}
207041394f3SDevin Teske 		r = read(fd, directive, n);
208041394f3SDevin Teske 
209041394f3SDevin Teske 		/* Advance beyond the equals sign if appropriate/desired */
210041394f3SDevin Teske 		if (bequals && *p == '=') {
211041394f3SDevin Teske 			if (lseek(fd, 1, SEEK_CUR) != -1)
212041394f3SDevin Teske 				r = read(fd, p, 1);
213041394f3SDevin Teske 			if (strict_equals && isspace(*p))
214041394f3SDevin Teske 				*p = '\n';
215041394f3SDevin Teske 		}
216041394f3SDevin Teske 
217041394f3SDevin Teske 		/* Terminate the string */
218041394f3SDevin Teske 		directive[n] = '\0';
219041394f3SDevin Teske 
220041394f3SDevin Teske 		/* Convert directive to lower case before comparison */
221041394f3SDevin Teske 		if (!case_sensitive)
222041394f3SDevin Teske 			strtolower(directive);
223041394f3SDevin Teske 
224041394f3SDevin Teske 		/* Move to what may be the start of the value */
225041394f3SDevin Teske 		if (!(bsemicolon && *p == ';') &&
226041394f3SDevin Teske 		    !(strict_equals && *p == '=')) {
227041394f3SDevin Teske 			while (r != 0 && isspace(*p) && *p != '\n')
228041394f3SDevin Teske 				r = read(fd, p, 1);
229041394f3SDevin Teske 		}
230041394f3SDevin Teske 
231041394f3SDevin Teske 		/* An equals sign may have stopped us, should we eat it? */
232041394f3SDevin Teske 		if (r != 0 && bequals && *p == '=' && !strict_equals) {
233041394f3SDevin Teske 			have_equals = 1;
234041394f3SDevin Teske 			r = read(fd, p, 1);
235041394f3SDevin Teske 			while (r != 0 && isspace(*p) && *p != '\n')
236041394f3SDevin Teske 				r = read(fd, p, 1);
237041394f3SDevin Teske 		}
238041394f3SDevin Teske 
239041394f3SDevin Teske 		/* If no value, allocate a dummy value and jump to action */
240041394f3SDevin Teske 		if (r == 0 || *p == '\n' || *p == '#' ||
241041394f3SDevin Teske 		    (bsemicolon && *p == ';')) {
242041394f3SDevin Teske 			/* Initialize the value if not already done */
243041394f3SDevin Teske 			if (value == NULL && (value = malloc(1)) == NULL) {
244041394f3SDevin Teske 				close(fd);
245041394f3SDevin Teske 				return (-1);
246041394f3SDevin Teske 			}
247041394f3SDevin Teske 			value[0] = '\0';
248041394f3SDevin Teske 			goto call_function;
249041394f3SDevin Teske 		}
250041394f3SDevin Teske 
251041394f3SDevin Teske 		/* Get the current offset */
252041394f3SDevin Teske 		curpos = lseek(fd, 0, SEEK_CUR) - 1;
253041394f3SDevin Teske 		if (curpos == -1) {
254041394f3SDevin Teske 			close(fd);
255041394f3SDevin Teske 			return (-1);
256041394f3SDevin Teske 		}
257041394f3SDevin Teske 
258041394f3SDevin Teske 		/* Find the end of the value */
259041394f3SDevin Teske 		quote = 0;
260041394f3SDevin Teske 		end = 0;
261041394f3SDevin Teske 		while (r != 0 && end == 0) {
262041394f3SDevin Teske 			/* Advance to the next character if we know we can */
263041394f3SDevin Teske 			if (*p != '\"' && *p != '#' && *p != '\n' &&
264041394f3SDevin Teske 			    (!bsemicolon || *p != ';')) {
265041394f3SDevin Teske 				r = read(fd, p, 1);
266041394f3SDevin Teske 				continue;
267041394f3SDevin Teske 			}
268041394f3SDevin Teske 
269041394f3SDevin Teske 			/*
270041394f3SDevin Teske 			 * If we get this far, we've hit an end-key
271041394f3SDevin Teske 			 */
272041394f3SDevin Teske 
273041394f3SDevin Teske 			/* Get the current offset */
274041394f3SDevin Teske 			charpos = lseek(fd, 0, SEEK_CUR) - 1;
275041394f3SDevin Teske 			if (charpos == -1) {
276041394f3SDevin Teske 				close(fd);
277041394f3SDevin Teske 				return (-1);
278041394f3SDevin Teske 			}
279041394f3SDevin Teske 
280041394f3SDevin Teske 			/*
281041394f3SDevin Teske 			 * Go back so we can read the character before the key
282041394f3SDevin Teske 			 * to check if the character is escaped (which means we
283041394f3SDevin Teske 			 * should continue).
284041394f3SDevin Teske 			 */
285041394f3SDevin Teske 			error = (int)lseek(fd, -2, SEEK_CUR);
286041394f3SDevin Teske 			if (error == -3) {
287041394f3SDevin Teske 				close(fd);
288041394f3SDevin Teske 				return (-1);
289041394f3SDevin Teske 			}
290041394f3SDevin Teske 			r = read(fd, p, 1);
291041394f3SDevin Teske 
292041394f3SDevin Teske 			/*
293041394f3SDevin Teske 			 * Count how many backslashes there are (an odd number
294041394f3SDevin Teske 			 * means the key is escaped, even means otherwise).
295041394f3SDevin Teske 			 */
296041394f3SDevin Teske 			for (n = 1; *p == '\\'; n++) {
297041394f3SDevin Teske 				/* Move back another offset to read */
298041394f3SDevin Teske 				error = (int)lseek(fd, -2, SEEK_CUR);
299041394f3SDevin Teske 				if (error == -3) {
300041394f3SDevin Teske 					close(fd);
301041394f3SDevin Teske 					return (-1);
302041394f3SDevin Teske 				}
303041394f3SDevin Teske 				r = read(fd, p, 1);
304041394f3SDevin Teske 			}
305041394f3SDevin Teske 
306041394f3SDevin Teske 			/* Move offset back to the key and read it */
307041394f3SDevin Teske 			error = (int)lseek(fd, charpos, SEEK_SET);
308041394f3SDevin Teske 			if (error == (charpos - 1)) {
309041394f3SDevin Teske 				close(fd);
310041394f3SDevin Teske 				return (-1);
311041394f3SDevin Teske 			}
312041394f3SDevin Teske 			r = read(fd, p, 1);
313041394f3SDevin Teske 
314041394f3SDevin Teske 			/*
315041394f3SDevin Teske 			 * If an even number of backslashes was counted meaning
316041394f3SDevin Teske 			 * key is not escaped, we should evaluate what to do.
317041394f3SDevin Teske 			 */
318041394f3SDevin Teske 			if ((n & 1) == 1) {
319041394f3SDevin Teske 				switch (*p) {
320041394f3SDevin Teske 				case '\"':
321041394f3SDevin Teske 					/*
322041394f3SDevin Teske 				 	 * Flag current sequence of characters
323041394f3SDevin Teske 					 * to follow as being quoted (hashes
324041394f3SDevin Teske 					 * are not considered comments).
325041394f3SDevin Teske 					 */
326041394f3SDevin Teske 					quote = !quote;
327041394f3SDevin Teske 					break;
328041394f3SDevin Teske 				case '#':
329041394f3SDevin Teske 					/*
330041394f3SDevin Teske 					 * If we aren't in a quoted series, we
331041394f3SDevin Teske 					 * just hit an inline comment and have
332041394f3SDevin Teske 					 * found the end of the value.
333041394f3SDevin Teske 					 */
334041394f3SDevin Teske 					if (!quote)
335041394f3SDevin Teske 						end = 1;
336041394f3SDevin Teske 					break;
337041394f3SDevin Teske 				case '\n':
338041394f3SDevin Teske 					/*
339041394f3SDevin Teske 					 * Newline characters must always be
340041394f3SDevin Teske 					 * escaped, whether inside a quoted
341041394f3SDevin Teske 					 * series or not, otherwise they
342041394f3SDevin Teske 					 * terminate the value.
343041394f3SDevin Teske 					 */
344041394f3SDevin Teske 					end = 1;
345041394f3SDevin Teske 				case ';':
346041394f3SDevin Teske 					if (!quote && bsemicolon)
347041394f3SDevin Teske 						end = 1;
348041394f3SDevin Teske 					break;
349041394f3SDevin Teske 				}
350041394f3SDevin Teske 			} else if (*p == '\n')
351041394f3SDevin Teske 				/* Escaped newline character. increment */
352041394f3SDevin Teske 				line++;
353041394f3SDevin Teske 
354041394f3SDevin Teske 			/* Advance to the next character */
355041394f3SDevin Teske 			r = read(fd, p, 1);
356041394f3SDevin Teske 		}
357041394f3SDevin Teske 
358041394f3SDevin Teske 		/* Get the current offset */
359041394f3SDevin Teske 		charpos = lseek(fd, 0, SEEK_CUR) - 1;
360041394f3SDevin Teske 		if (charpos == -1) {
361041394f3SDevin Teske 			close(fd);
362041394f3SDevin Teske 			return (-1);
363041394f3SDevin Teske 		}
364041394f3SDevin Teske 
365041394f3SDevin Teske 		/* Get the length of the value */
366041394f3SDevin Teske 		n = (uint32_t)(charpos - curpos);
367041394f3SDevin Teske 		if (r != 0) /* more to read, but don't read ending key */
368041394f3SDevin Teske 			n--;
369041394f3SDevin Teske 
370041394f3SDevin Teske 		/* Move offset back to the beginning of the value */
371041394f3SDevin Teske 		error = (int)lseek(fd, curpos, SEEK_SET);
372041394f3SDevin Teske 		if (error == (curpos - 1)) {
373041394f3SDevin Teske 			close(fd);
374041394f3SDevin Teske 			return (-1);
375041394f3SDevin Teske 		}
376041394f3SDevin Teske 
377041394f3SDevin Teske 		/* Allocate and read the value into memory */
378041394f3SDevin Teske 		if (n > vsize) {
379041394f3SDevin Teske 			if ((value = realloc(value, n + 1)) == NULL) {
380041394f3SDevin Teske 				close(fd);
381041394f3SDevin Teske 				return (-1);
382041394f3SDevin Teske 			}
383041394f3SDevin Teske 			vsize = n;
384041394f3SDevin Teske 		}
385041394f3SDevin Teske 		r = read(fd, value, n);
386041394f3SDevin Teske 
387041394f3SDevin Teske 		/* Terminate the string */
388041394f3SDevin Teske 		value[n] = '\0';
389041394f3SDevin Teske 
390041394f3SDevin Teske 		/* Cut trailing whitespace off by termination */
391041394f3SDevin Teske 		t = value + n;
392041394f3SDevin Teske 		while (isspace(*--t))
393041394f3SDevin Teske 			*t = '\0';
394041394f3SDevin Teske 
395041394f3SDevin Teske 		/* Escape the escaped quotes (replaceall is in string_m.c) */
396041394f3SDevin Teske 		x = strcount(value, "\\\""); /* in string_m.c */
397041394f3SDevin Teske 		if (x != 0 && (n + x) > vsize) {
398041394f3SDevin Teske 			if ((value = realloc(value, n + x + 1)) == NULL) {
399041394f3SDevin Teske 				close(fd);
400041394f3SDevin Teske 				return (-1);
401041394f3SDevin Teske 			}
402041394f3SDevin Teske 			vsize = n + x;
403041394f3SDevin Teske 		}
404041394f3SDevin Teske 		if (replaceall(value, "\\\"", "\\\\\"") < 0) {
405041394f3SDevin Teske 			/* Replace operation failed for some unknown reason */
406041394f3SDevin Teske 			close(fd);
407041394f3SDevin Teske 			return (-1);
408041394f3SDevin Teske 		}
409041394f3SDevin Teske 
410041394f3SDevin Teske 		/* Remove all new line characters */
411041394f3SDevin Teske 		if (replaceall(value, "\\\n", "") < 0) {
412041394f3SDevin Teske 			/* Replace operation failed for some unknown reason */
413041394f3SDevin Teske 			close(fd);
414041394f3SDevin Teske 			return (-1);
415041394f3SDevin Teske 		}
416041394f3SDevin Teske 
417041394f3SDevin Teske 		/* Resolve escape sequences */
418041394f3SDevin Teske 		strexpand(value); /* in string_m.c */
419041394f3SDevin Teske 
420041394f3SDevin Teske call_function:
421041394f3SDevin Teske 		/* Abort if we're seeking only assignments */
422041394f3SDevin Teske 		if (require_equals && !have_equals)
423041394f3SDevin Teske 			return (-1);
424041394f3SDevin Teske 
425041394f3SDevin Teske 		found = have_equals = 0; /* reset */
426041394f3SDevin Teske 
427041394f3SDevin Teske 		/* If there are no options defined, call unknown and loop */
428041394f3SDevin Teske 		if (options == NULL && unknown != NULL) {
429041394f3SDevin Teske 			error = unknown(NULL, line, directive, value);
430041394f3SDevin Teske 			if (error != 0) {
431041394f3SDevin Teske 				close(fd);
432041394f3SDevin Teske 				return (error);
433041394f3SDevin Teske 			}
434041394f3SDevin Teske 			continue;
435041394f3SDevin Teske 		}
436041394f3SDevin Teske 
437041394f3SDevin Teske 		/* Loop through the array looking for a match for the value */
438041394f3SDevin Teske 		for (n = 0; options[n].directive != NULL; n++) {
439041394f3SDevin Teske 			error = fnmatch(options[n].directive, directive,
440041394f3SDevin Teske 			    FNM_NOESCAPE);
441041394f3SDevin Teske 			if (error == 0) {
442041394f3SDevin Teske 				found = 1;
443041394f3SDevin Teske 				/* Call function for array index item */
444041394f3SDevin Teske 				if (options[n].action != NULL) {
445041394f3SDevin Teske 					error = options[n].action(
446041394f3SDevin Teske 					    &options[n],
447041394f3SDevin Teske 					    line, directive, value);
448041394f3SDevin Teske 					if (error != 0) {
449041394f3SDevin Teske 						close(fd);
450041394f3SDevin Teske 						return (error);
451041394f3SDevin Teske 					}
452041394f3SDevin Teske 				}
453041394f3SDevin Teske 			} else if (error != FNM_NOMATCH) {
454041394f3SDevin Teske 				/* An error has occurred */
455041394f3SDevin Teske 				close(fd);
456041394f3SDevin Teske 				return (-1);
457041394f3SDevin Teske 			}
458041394f3SDevin Teske 		}
459041394f3SDevin Teske 		if (!found && unknown != NULL) {
460041394f3SDevin Teske 			/*
461041394f3SDevin Teske 			 * No match was found for the value we read from the
462041394f3SDevin Teske 			 * file; call function designated for unknown values.
463041394f3SDevin Teske 			 */
464041394f3SDevin Teske 			error = unknown(NULL, line, directive, value);
465041394f3SDevin Teske 			if (error != 0) {
466041394f3SDevin Teske 				close(fd);
467041394f3SDevin Teske 				return (error);
468041394f3SDevin Teske 			}
469041394f3SDevin Teske 		}
470041394f3SDevin Teske 	}
471041394f3SDevin Teske 
472041394f3SDevin Teske 	close(fd);
473041394f3SDevin Teske 	return (0);
474041394f3SDevin Teske }
475