1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 * 25 * Copyright 2021 OmniOS Community Edition (OmniOSce) Association. 26 */ 27 28 #include <stdio.h> 29 #include <stdlib.h> 30 #include <string.h> 31 #include <errno.h> 32 #include <sys/types.h> 33 #include <definit.h> 34 35 /* Tokens are separated by spaces, tabs and newlines. */ 36 #define SEPARATORS " \t\n" 37 38 typedef struct definit { 39 FILE *di_fp; 40 char *di_line; 41 char *di_tok; 42 } definit_t; 43 44 int 45 definit_open(const char *file, void **statep) 46 { 47 FILE *fp; 48 int _errno; 49 definit_t *state = NULL; 50 51 if ((fp = fopen(file, "r")) == NULL) 52 return (-1); 53 54 if ((state = calloc(1, sizeof (*state))) == NULL) 55 goto err; 56 57 if ((state->di_line = calloc(DEFINIT_MAXLINE, sizeof (char))) == NULL) 58 goto err; 59 60 state->di_fp = fp; 61 *statep = state; 62 63 return (0); 64 65 err: 66 _errno = errno; 67 (void) fclose(fp); 68 if (state != NULL) { 69 free(state->di_line); 70 free(state); 71 } 72 errno = _errno; 73 return (-1); 74 } 75 76 void 77 definit_close(void *statep) 78 { 79 definit_t *state = statep; 80 81 (void) fclose(state->di_fp); 82 free(state->di_line); 83 free(state); 84 } 85 86 /* 87 * This parser was written to produce the same output as the ones it replaced 88 * in init and svc.startd. As such it has some shortcomings: 89 * - Values may be quoted but the quotes are just stripped and separators such 90 * as whitespace are not treated specially within quotes; 91 * - Lines which are longer than DEFINIT_MAXLINE -1 bytes are split. Tokens 92 * which span a split will be truncated, one way or another. 93 * - Comments at the end of a line (after a token) are not supported. 94 * These could be corrected in the future if strict backwards compatibility is 95 * not required. 96 */ 97 98 static char * 99 definit_nextline(definit_t *state) 100 { 101 char *line; 102 103 while ((line = fgets(state->di_line, DEFINIT_MAXLINE, state->di_fp)) 104 != NULL) { 105 boolean_t inquotes; 106 char *p, *bp; 107 size_t wslength; 108 109 /* 110 * Ignore blank or comment lines. 111 */ 112 if (line[0] == '#' || line[0] == '\0' || 113 (wslength = strspn(line, SEPARATORS)) == strlen(line) || 114 line[wslength] == '#') { 115 continue; 116 } 117 118 /* 119 * Make a pass through the line and: 120 * - Replace any non-quoted semicolons with spaces; 121 * - Remove any quote characters. 122 * 123 * While walking this, 'p' is the current position in the line 124 * and, if any characters have been found which need to be 125 * removed, 'bp' tracks the position in the line where 126 * subsequent characters need to be written in order to close 127 * the gap; 'bp' trails 'p'. 128 * If 'bp' is NULL, no characters to remove have been found. 129 */ 130 inquotes = B_FALSE; 131 for (p = line, bp = NULL; *p != '\0'; p++) { 132 switch (*p) { 133 case '"': 134 case '\'': 135 inquotes = !inquotes; 136 if (bp == NULL) 137 bp = p; 138 break; 139 case ';': 140 if (!inquotes) 141 *p = ' '; 142 /* FALLTHROUGH */ 143 default: 144 if (bp != NULL) 145 *bp++ = *p; 146 break; 147 } 148 } 149 if (bp != NULL) 150 *bp = '\0'; 151 152 /* 153 * Perform an initial strtok_r() call on the new line. 154 * definit_token() will repeatedly call strtok_r() until the 155 * line is consumed, and then call this function again for 156 * more input. 157 */ 158 if ((p = strtok_r(line, SEPARATORS, &state->di_tok)) != NULL) 159 return (p); 160 } 161 162 return (NULL); 163 } 164 165 const char * 166 definit_token(void *statep) 167 { 168 definit_t *state = statep; 169 char *tok; 170 171 for (;;) { 172 tok = NULL; 173 174 if (state->di_tok != NULL) 175 tok = strtok_r(NULL, SEPARATORS, &state->di_tok); 176 177 if (tok == NULL) 178 tok = definit_nextline(state); 179 180 if (tok == NULL) 181 break; 182 183 if (strchr(tok, '=') != NULL && *tok != '=') 184 return (tok); 185 } 186 187 return (NULL); 188 } 189