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