xref: /illumos-gate/usr/src/common/definit/definit.c (revision dd72704bd9e794056c558153663c739e2012d721)
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