1/*	$NetBSD: var.c,v 1.4 2021/04/10 19:49:59 nia Exp $	*/
2
3/*-
4 * Copyright (c) 2005, 2008 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Dieter Baron, Thomas Klausner, Johnny Lam, and Joerg Sonnenberger.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 * 3. Neither the name of The NetBSD Foundation nor the names of its
19 *    contributors may be used to endorse or promote products derived
20 *    from this software without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
23 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
24 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
25 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
26 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32 * POSSIBILITY OF SUCH DAMAGE.
33 */
34
35#if HAVE_CONFIG_H
36#include "config.h"
37#endif
38#include <nbcompat.h>
39#if HAVE_SYS_CDEFS_H
40#include <sys/cdefs.h>
41#endif
42__RCSID("$NetBSD: var.c,v 1.4 2021/04/10 19:49:59 nia Exp $");
43
44#if HAVE_SYS_STAT_H
45#include <sys/stat.h>
46#endif
47#if HAVE_ERR_H
48#include <err.h>
49#endif
50#if HAVE_ERRNO_H
51#include <errno.h>
52#endif
53#if HAVE_STDIO_H
54#include <stdio.h>
55#endif
56
57#include "lib.h"
58
59static const char *var_cmp(const char *, size_t, const char *, size_t);
60static void var_print(FILE *, const char *, const char *);
61
62/*
63 * Copy the specified variables from the file fname to stdout.
64 */
65int
66var_copy_list(const char *buf, const char **variables)
67{
68	const char *eol, *next;
69	size_t len;
70	int i;
71
72	for (; *buf; buf = next) {
73		if ((eol = strchr(buf, '\n')) != NULL) {
74			next = eol + 1;
75			len = eol - buf;
76		} else {
77			len = strlen(buf);
78			next = buf + len;
79		}
80
81		for (i=0; variables[i]; i++) {
82			if (var_cmp(buf, len, variables[i],
83				       strlen(variables[i])) != NULL) {
84				printf("%.*s\n", (int)len, buf);
85				break;
86			}
87		}
88	}
89	return 0;
90}
91
92/*
93 * Print the value of variable from the file fname to stdout.
94 */
95char *
96var_get(const char *fname, const char *variable)
97{
98	FILE   *fp;
99	char   *line;
100	size_t  len;
101	size_t  varlen;
102	char   *value;
103	size_t  valuelen;
104	size_t  thislen;
105	const char *p;
106
107	varlen = strlen(variable);
108	if (varlen == 0)
109		return NULL;
110
111	fp = fopen(fname, "r");
112	if (!fp) {
113		if (errno != ENOENT)
114			warn("var_get: can't open '%s' for reading", fname);
115		return NULL;
116	}
117
118	value = NULL;
119	valuelen = 0;
120
121	while ((line = fgetln(fp, &len)) != (char *) NULL) {
122		if (line[len - 1] == '\n')
123			--len;
124		if ((p=var_cmp(line, len, variable, varlen)) == NULL)
125			continue;
126
127		thislen = line+len - p;
128		if (value) {
129			value = xrealloc(value, valuelen+thislen+2);
130			value[valuelen++] = '\n';
131		}
132		else {
133			value = xmalloc(thislen+1);
134		}
135		sprintf(value+valuelen, "%.*s", (int)thislen, p);
136		valuelen += thislen;
137	}
138	(void) fclose(fp);
139	return value;
140}
141
142/*
143 * Print the value of variable from the memory buffer to stdout.
144 */
145char *
146var_get_memory(const char *buf, const char *variable)
147{
148	const char *eol, *next, *data;
149	size_t len, varlen, thislen, valuelen;
150	char *value;
151
152	varlen = strlen(variable);
153	if (varlen == 0)
154		return NULL;
155
156	value = NULL;
157	valuelen = 0;
158
159	for (; buf && *buf; buf = next) {
160		if ((eol = strchr(buf, '\n')) != NULL) {
161			next = eol + 1;
162			len = eol - buf;
163		} else {
164			next = eol;
165			len = strlen(buf);
166		}
167		if ((data = var_cmp(buf, len, variable, varlen)) == NULL)
168			continue;
169
170		thislen = buf + len - data;
171		if (value) {
172			value = xrealloc(value, valuelen+thislen+2);
173			value[valuelen++] = '\n';
174		}
175		else {
176			value = xmalloc(thislen+1);
177		}
178		sprintf(value + valuelen, "%.*s", (int)thislen, data);
179		valuelen += thislen;
180	}
181	return value;
182}
183
184/*
185 * Add given variable with given value to file, overwriting any
186 * previous occurrence.
187 */
188int
189var_set(const char *fname, const char *variable, const char *value)
190{
191	FILE   *fp;
192	FILE   *fout;
193	char   *tmpname;
194	int     fd;
195	char   *line;
196	size_t  len;
197	size_t  varlen;
198	Boolean done;
199	struct stat st;
200
201	varlen = strlen(variable);
202	if (varlen == 0)
203		return 0;
204
205	fp = fopen(fname, "r");
206	if (fp == NULL) {
207		if (errno != ENOENT) {
208			warn("var_set: can't open '%s' for reading", fname);
209			return -1;
210		}
211		if (value == NULL)
212			return 0; /* Nothing to do */
213	}
214
215	tmpname = xasprintf("%s.XXXXXX", fname);
216	if ((fd = mkstemp(tmpname)) < 0) {
217		free(tmpname);
218		if (fp != NULL)
219			fclose(fp);
220		warn("var_set: can't open temp file for '%s' for writing",
221		      fname);
222		return -1;
223	}
224	if (chmod(tmpname, 0644) < 0) {
225		close(fd);
226		if (fp != NULL)
227			fclose(fp);
228		free(tmpname);
229		warn("var_set: can't set permissions for temp file for '%s'",
230		      fname);
231		return -1;
232	}
233	if ((fout=fdopen(fd, "w")) == NULL) {
234		close(fd);
235		remove(tmpname);
236		free(tmpname);
237		if (fp != NULL)
238			fclose(fp);
239		warn("var_set: can't open temp file for '%s' for writing",
240		      fname);
241		return -1;
242	}
243
244	done = FALSE;
245
246	if (fp) {
247		while ((line = fgetln(fp, &len)) != (char *) NULL) {
248			if (var_cmp(line, len, variable, varlen) == NULL)
249				fprintf(fout, "%.*s", (int)len, line);
250			else {
251				if (!done && value) {
252					var_print(fout, variable, value);
253					done = TRUE;
254				}
255			}
256		}
257		(void) fclose(fp);
258	}
259
260	if (!done && value)
261		var_print(fout, variable, value);
262
263	if (fclose(fout) < 0) {
264		free(tmpname);
265		warn("var_set: write error for '%s'", fname);
266		return -1;
267	}
268
269	if (stat(tmpname, &st) < 0) {
270		free(tmpname);
271		warn("var_set: cannot stat tempfile for '%s'", fname);
272		return -1;
273	}
274
275	if (st.st_size == 0) {
276		if (remove(tmpname) < 0) {
277			free(tmpname);
278			warn("var_set: cannot remove tempfile for '%s'",
279			     fname);
280			return -1;
281		}
282		free(tmpname);
283		if (remove(fname) < 0) {
284			warn("var_set: cannot remove '%s'", fname);
285			return -1;
286		}
287		return 0;
288	}
289
290	if (rename(tmpname, fname) < 0) {
291		free(tmpname);
292		warn("var_set: cannot move tempfile to '%s'", fname);
293		return -1;
294	}
295	free(tmpname);
296	return 0;
297}
298
299/*
300 * Check if line contains variable var, return pointer to its value or NULL.
301 */
302static const char *
303var_cmp(const char *line, size_t linelen, const char *var, size_t varlen)
304{
305	/*
306	 * We expect lines to look like one of the following
307	 * forms:
308	 *      VAR=value
309	 *      VAR= value
310	 * We print out the value of VAR, or nothing if it
311	 * doesn't exist.
312	 */
313	if (linelen < varlen+1)
314		return NULL;
315	if (strncmp(var, line, varlen) != 0)
316		return NULL;
317
318	line += varlen;
319	if (*line != '=')
320		return NULL;
321
322	++line;
323	linelen -= varlen+1;
324	if (linelen > 0 && *line == ' ')
325		++line;
326	return line;
327}
328
329/*
330 * Print given variable with value to file f.
331 */
332static void
333var_print(FILE *f, const char *variable, const char *value)
334{
335	const char *p;
336
337	while ((p=strchr(value, '\n')) != NULL) {
338		if (p != value)
339			fprintf(f, "%s=%.*s\n", variable, (int)(p-value), value);
340		value = p+1;
341	}
342
343	if (*value)
344		fprintf(f, "%s=%s\n", variable, value);
345}
346