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 * 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 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