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
definit_open(const char * file,void ** statep)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
definit_close(void * statep)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 *
definit_nextline(definit_t * state)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 *
definit_token(void * statep)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