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