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