Home | History | Annotate | Line # | Download | only in lib
      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 
     59 static const char *var_cmp(const char *, size_t, const char *, size_t);
     60 static void var_print(FILE *, const char *, const char *);
     61 
     62 /*
     63  * Copy the specified variables from the file fname to stdout.
     64  */
     65 int
     66 var_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  */
     95 char *
     96 var_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  */
    145 char *
    146 var_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  */
    188 int
    189 var_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  */
    302 static const char *
    303 var_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  */
    332 static void
    333 var_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