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