/* $NetBSD: var.c,v 1.3 2017/04/20 13:18:23 joerg Exp $ */ /*- * Copyright (c) 2005, 2008 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Dieter Baron, Thomas Klausner, Johnny Lam, and Joerg Sonnenberger. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of The NetBSD Foundation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #if HAVE_CONFIG_H #include "config.h" #endif #include <nbcompat.h> #if HAVE_SYS_CDEFS_H #include <sys/cdefs.h> #endif __RCSID("$NetBSD: var.c,v 1.3 2017/04/20 13:18:23 joerg Exp $"); #if HAVE_SYS_STAT_H #include <sys/stat.h> #endif #if HAVE_ERR_H #include <err.h> #endif #if HAVE_ERRNO_H #include <errno.h> #endif #if HAVE_STDIO_H #include <stdio.h> #endif #include "lib.h" static const char *var_cmp(const char *, size_t, const char *, size_t); static void var_print(FILE *, const char *, const char *); /* * Copy the specified variables from the file fname to stdout. */ int var_copy_list(const char *buf, const char **variables) { const char *eol, *next; size_t len; int i; for (; *buf; buf = next) { if ((eol = strchr(buf, '\n')) != NULL) { next = eol + 1; len = eol - buf; } else { len = strlen(buf); next = buf + len; } for (i=0; variables[i]; i++) { if (var_cmp(buf, len, variables[i], strlen(variables[i])) != NULL) { printf("%.*s\n", (int)len, buf); break; } } } return 0; } /* * Print the value of variable from the file fname to stdout. */ char * var_get(const char *fname, const char *variable) { FILE *fp; char *line; size_t len; size_t varlen; char *value; size_t valuelen; size_t thislen; const char *p; varlen = strlen(variable); if (varlen == 0) return NULL; fp = fopen(fname, "r"); if (!fp) { if (errno != ENOENT) warn("var_get: can't open '%s' for reading", fname); return NULL; } value = NULL; valuelen = 0; while ((line = fgetln(fp, &len)) != (char *) NULL) { if (line[len - 1] == '\n') --len; if ((p=var_cmp(line, len, variable, varlen)) == NULL) continue; thislen = line+len - p; if (value) { value = xrealloc(value, valuelen+thislen+2); value[valuelen++] = '\n'; } else { value = xmalloc(thislen+1); } sprintf(value+valuelen, "%.*s", (int)thislen, p); valuelen += thislen; } (void) fclose(fp); return value; } /* * Print the value of variable from the memory buffer to stdout. */ char * var_get_memory(const char *buf, const char *variable) { const char *eol, *next, *data; size_t len, varlen, thislen, valuelen; char *value; varlen = strlen(variable); if (varlen == 0) return NULL; value = NULL; valuelen = 0; for (; buf && *buf; buf = next) { if ((eol = strchr(buf, '\n')) != NULL) { next = eol + 1; len = eol - buf; } else { next = eol; len = strlen(buf); } if ((data = var_cmp(buf, len, variable, varlen)) == NULL) continue; thislen = buf + len - data; if (value) { value = xrealloc(value, valuelen+thislen+2); value[valuelen++] = '\n'; } else { value = xmalloc(thislen+1); } sprintf(value + valuelen, "%.*s", (int)thislen, data); valuelen += thislen; } return value; } /* * Add given variable with given value to file, overwriting any * previous occurrence. */ int var_set(const char *fname, const char *variable, const char *value) { FILE *fp; FILE *fout; char *tmpname; int fd; char *line; size_t len; size_t varlen; Boolean done; struct stat st; varlen = strlen(variable); if (varlen == 0) return 0; fp = fopen(fname, "r"); if (fp == NULL) { if (errno != ENOENT) { warn("var_set: can't open '%s' for reading", fname); return -1; } if (value == NULL) return 0; /* Nothing to do */ } tmpname = xasprintf("%s.XXXXXX", fname); if ((fd = mkstemp(tmpname)) < 0) { free(tmpname); if (fp != NULL) fclose(fp); warn("var_set: can't open temp file for '%s' for writing", fname); return -1; } if (chmod(tmpname, 0644) < 0) { close(fd); if (fp != NULL) fclose(fp); free(tmpname); warn("var_set: can't set permissions for temp file for '%s'", fname); return -1; } if ((fout=fdopen(fd, "w")) == NULL) { close(fd); remove(tmpname); free(tmpname); if (fp != NULL) fclose(fp); warn("var_set: can't open temp file for '%s' for writing", fname); return -1; } done = FALSE; if (fp) { while ((line = fgetln(fp, &len)) != (char *) NULL) { if (var_cmp(line, len, variable, varlen) == NULL) fprintf(fout, "%.*s", (int)len, line); else { if (!done && value) { var_print(fout, variable, value); done = TRUE; } } } (void) fclose(fp); } if (!done && value) var_print(fout, variable, value); if (fclose(fout) < 0) { free(tmpname); warn("var_set: write error for '%s'", fname); return -1; } if (stat(tmpname, &st) < 0) { free(tmpname); warn("var_set: cannot stat tempfile for '%s'", fname); return -1; } if (st.st_size == 0) { if (remove(tmpname) < 0) { free(tmpname); warn("var_set: cannot remove tempfile for '%s'", fname); return -1; } free(tmpname); if (remove(fname) < 0) { warn("var_set: cannot remove '%s'", fname); return -1; } return 0; } if (rename(tmpname, fname) < 0) { free(tmpname); warn("var_set: cannot move tempfile to '%s'", fname); return -1; } free(tmpname); return 0; } /* * Check if line contains variable var, return pointer to its value or NULL. */ static const char * var_cmp(const char *line, size_t linelen, const char *var, size_t varlen) { /* * We expect lines to look like one of the following * forms: * VAR=value * VAR= value * We print out the value of VAR, or nothing if it * doesn't exist. */ if (linelen < varlen+1) return NULL; if (strncmp(var, line, varlen) != 0) return NULL; line += varlen; if (*line != '=') return NULL; ++line; linelen -= varlen+1; if (linelen > 0 && *line == ' ') ++line; return line; } /* * Print given variable with value to file f. */ static void var_print(FILE *f, const char *variable, const char *value) { const char *p; while ((p=strchr(value, '\n')) != NULL) { if (p != value) fprintf(f, "%s=%.*s\n", variable, (int)(p-value), value); value = p+1; } if (*value) fprintf(f, "%s=%s\n", variable, value); }