pw_conf.c revision 282719
120253Sjoerg/*- 220302Sjoerg * Copyright (C) 1996 320302Sjoerg * David L. Nugent. All rights reserved. 420253Sjoerg * 520253Sjoerg * Redistribution and use in source and binary forms, with or without 620253Sjoerg * modification, are permitted provided that the following conditions 720253Sjoerg * are met: 820253Sjoerg * 1. Redistributions of source code must retain the above copyright 920302Sjoerg * notice, this list of conditions and the following disclaimer. 1020253Sjoerg * 2. Redistributions in binary form must reproduce the above copyright 1120253Sjoerg * notice, this list of conditions and the following disclaimer in the 1220253Sjoerg * documentation and/or other materials provided with the distribution. 1320253Sjoerg * 1420302Sjoerg * THIS SOFTWARE IS PROVIDED BY DAVID L. NUGENT AND CONTRIBUTORS ``AS IS'' AND 1520253Sjoerg * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1620253Sjoerg * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1720302Sjoerg * ARE DISCLAIMED. IN NO EVENT SHALL DAVID L. NUGENT OR CONTRIBUTORS BE LIABLE 1820253Sjoerg * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1920253Sjoerg * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2020253Sjoerg * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2120253Sjoerg * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2220253Sjoerg * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2320253Sjoerg * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2420253Sjoerg * SUCH DAMAGE. 2520253Sjoerg */ 2620253Sjoerg 2730259Scharnier#ifndef lint 2830259Scharnierstatic const char rcsid[] = 2950479Speter "$FreeBSD: head/usr.sbin/pw/pw_conf.c 282719 2015-05-10 10:15:36Z bapt $"; 3030259Scharnier#endif /* not lint */ 3130259Scharnier 32282681Sbapt#include <sys/types.h> 33282681Sbapt#include <sys/sbuf.h> 3420253Sjoerg#include <string.h> 3520253Sjoerg#include <ctype.h> 3620253Sjoerg#include <fcntl.h> 37282718Sbapt#include <err.h> 3820253Sjoerg 3920253Sjoerg#include "pw.h" 4020253Sjoerg 4120253Sjoerg#define debugging 0 4220253Sjoerg 4320253Sjoergenum { 4420253Sjoerg _UC_NONE, 4520253Sjoerg _UC_DEFAULTPWD, 4620253Sjoerg _UC_REUSEUID, 4720253Sjoerg _UC_REUSEGID, 4821330Sdavidn _UC_NISPASSWD, 4920253Sjoerg _UC_DOTDIR, 5020253Sjoerg _UC_NEWMAIL, 5120253Sjoerg _UC_LOGFILE, 5220253Sjoerg _UC_HOMEROOT, 53168044Sle _UC_HOMEMODE, 5420253Sjoerg _UC_SHELLPATH, 5520253Sjoerg _UC_SHELLS, 5620253Sjoerg _UC_DEFAULTSHELL, 5720253Sjoerg _UC_DEFAULTGROUP, 5820253Sjoerg _UC_EXTRAGROUPS, 5920253Sjoerg _UC_DEFAULTCLASS, 6020253Sjoerg _UC_MINUID, 6120253Sjoerg _UC_MAXUID, 6220253Sjoerg _UC_MINGID, 6320253Sjoerg _UC_MAXGID, 6420253Sjoerg _UC_EXPIRE, 6520253Sjoerg _UC_PASSWORD, 6620253Sjoerg _UC_FIELDS 6720253Sjoerg}; 6820253Sjoerg 6920253Sjoergstatic char bourne_shell[] = "sh"; 7020253Sjoerg 7120253Sjoergstatic char *system_shells[_UC_MAXSHELLS] = 7220253Sjoerg{ 7320253Sjoerg bourne_shell, 7463239Sdavidn "csh", 7563239Sdavidn "tcsh" 7620253Sjoerg}; 7720253Sjoerg 7820253Sjoergstatic char const *booltrue[] = 7920253Sjoerg{ 8020253Sjoerg "yes", "true", "1", "on", NULL 8120253Sjoerg}; 8220253Sjoergstatic char const *boolfalse[] = 8320253Sjoerg{ 8420253Sjoerg "no", "false", "0", "off", NULL 8520253Sjoerg}; 8620253Sjoerg 8720253Sjoergstatic struct userconf config = 8820253Sjoerg{ 8920253Sjoerg 0, /* Default password for new users? (nologin) */ 9020253Sjoerg 0, /* Reuse uids? */ 9120253Sjoerg 0, /* Reuse gids? */ 9221330Sdavidn NULL, /* NIS version of the passwd file */ 9320253Sjoerg "/usr/share/skel", /* Where to obtain skeleton files */ 9420253Sjoerg NULL, /* Mail to send to new accounts */ 9520253Sjoerg "/var/log/userlog", /* Where to log changes */ 9620253Sjoerg "/home", /* Where to create home directory */ 97219408Sjkim _DEF_DIRMODE, /* Home directory perms, modified by umask */ 9820253Sjoerg "/bin", /* Where shells are located */ 9920253Sjoerg system_shells, /* List of shells (first is default) */ 10020253Sjoerg bourne_shell, /* Default shell */ 10120253Sjoerg NULL, /* Default group name */ 10220747Sdavidn NULL, /* Default (additional) groups */ 10320253Sjoerg NULL, /* Default login class */ 10420253Sjoerg 1000, 32000, /* Allowed range of uids */ 10520253Sjoerg 1000, 32000, /* Allowed range of gids */ 10620253Sjoerg 0, /* Days until account expires */ 10749171Sdavidn 0, /* Days until password expires */ 10849171Sdavidn 0 /* size of default_group array */ 10920253Sjoerg}; 11020253Sjoerg 11120253Sjoergstatic char const *comments[_UC_FIELDS] = 11220253Sjoerg{ 11320253Sjoerg "#\n# pw.conf - user/group configuration defaults\n#\n", 11420253Sjoerg "\n# Password for new users? no=nologin yes=loginid none=blank random=random\n", 11520253Sjoerg "\n# Reuse gaps in uid sequence? (yes or no)\n", 11620253Sjoerg "\n# Reuse gaps in gid sequence? (yes or no)\n", 11721330Sdavidn "\n# Path to the NIS passwd file (blank or 'no' for none)\n", 11820253Sjoerg "\n# Obtain default dotfiles from this directory\n", 11920253Sjoerg "\n# Mail this file to new user (/etc/newuser.msg or no)\n", 12020253Sjoerg "\n# Log add/change/remove information in this file\n", 12120253Sjoerg "\n# Root directory in which $HOME directory is created\n", 122168044Sle "\n# Mode for the new $HOME directory, will be modified by umask\n", 12320253Sjoerg "\n# Colon separated list of directories containing valid shells\n", 12470133Sdougb "\n# Comma separated list of available shells (without paths)\n", 12520253Sjoerg "\n# Default shell (without path)\n", 12620253Sjoerg "\n# Default group (leave blank for new group per user)\n", 12720253Sjoerg "\n# Extra groups for new users\n", 12820253Sjoerg "\n# Default login class for new users\n", 12920253Sjoerg "\n# Range of valid default user ids\n", 13020253Sjoerg NULL, 13120253Sjoerg "\n# Range of valid default group ids\n", 13220253Sjoerg NULL, 13320253Sjoerg "\n# Days after which account expires (0=disabled)\n", 13420253Sjoerg "\n# Days after which password expires (0=disabled)\n" 13520253Sjoerg}; 13620253Sjoerg 13720253Sjoergstatic char const *kwds[] = 13820253Sjoerg{ 13920253Sjoerg "", 14020253Sjoerg "defaultpasswd", 14120253Sjoerg "reuseuids", 14220253Sjoerg "reusegids", 14321330Sdavidn "nispasswd", 14420253Sjoerg "skeleton", 14520253Sjoerg "newmail", 14620253Sjoerg "logfile", 14720253Sjoerg "home", 148168044Sle "homemode", 14920253Sjoerg "shellpath", 15020253Sjoerg "shells", 15120253Sjoerg "defaultshell", 15220253Sjoerg "defaultgroup", 15320253Sjoerg "extragroups", 15420253Sjoerg "defaultclass", 15520253Sjoerg "minuid", 15620253Sjoerg "maxuid", 15720253Sjoerg "mingid", 15820253Sjoerg "maxgid", 15920253Sjoerg "expire_days", 16020253Sjoerg "password_days", 16120253Sjoerg NULL 16220253Sjoerg}; 16320253Sjoerg 16420253Sjoergstatic char * 16520253Sjoergunquote(char const * str) 16620253Sjoerg{ 16720253Sjoerg if (str && (*str == '"' || *str == '\'')) { 16820253Sjoerg char *p = strchr(str + 1, *str); 16920253Sjoerg 17020253Sjoerg if (p != NULL) 17120253Sjoerg *p = '\0'; 17220253Sjoerg return (char *) (*++str ? str : NULL); 17320253Sjoerg } 17420253Sjoerg return (char *) str; 17520253Sjoerg} 17620253Sjoerg 17720253Sjoergint 17820253Sjoergboolean_val(char const * str, int dflt) 17920253Sjoerg{ 18020253Sjoerg if ((str = unquote(str)) != NULL) { 18120253Sjoerg int i; 18220253Sjoerg 18320253Sjoerg for (i = 0; booltrue[i]; i++) 18420253Sjoerg if (strcmp(str, booltrue[i]) == 0) 18520253Sjoerg return 1; 18620253Sjoerg for (i = 0; boolfalse[i]; i++) 18720253Sjoerg if (strcmp(str, boolfalse[i]) == 0) 18820253Sjoerg return 0; 18920253Sjoerg 19020253Sjoerg /* 19120253Sjoerg * Special cases for defaultpassword 19220253Sjoerg */ 19320253Sjoerg if (strcmp(str, "random") == 0) 19420253Sjoerg return -1; 19520253Sjoerg if (strcmp(str, "none") == 0) 19620253Sjoerg return -2; 19720253Sjoerg } 19820253Sjoerg return dflt; 19920253Sjoerg} 20020253Sjoerg 20120253Sjoergchar const * 20220253Sjoergboolean_str(int val) 20320253Sjoerg{ 20420253Sjoerg if (val == -1) 20520253Sjoerg return "random"; 20620253Sjoerg else if (val == -2) 20720253Sjoerg return "none"; 20820253Sjoerg else 20920253Sjoerg return val ? booltrue[0] : boolfalse[0]; 21020253Sjoerg} 21120253Sjoerg 21220253Sjoergchar * 21320253Sjoergnewstr(char const * p) 21420253Sjoerg{ 215282718Sbapt char *q; 21620253Sjoerg 217282718Sbapt if ((p = unquote(p)) == NULL) 218282718Sbapt return (NULL); 21920253Sjoerg 220282719Sbapt if ((q = strdup(p)) == NULL) 221282719Sbapt err(1, "strdup()"); 222282718Sbapt 223282718Sbapt return (q); 22420253Sjoerg} 22520253Sjoerg 22620253Sjoergstruct userconf * 22720253Sjoergread_userconfig(char const * file) 22820253Sjoerg{ 229264781Sbapt FILE *fp; 230264781Sbapt char *buf, *p; 231264781Sbapt size_t linecap; 232264781Sbapt ssize_t linelen; 23320253Sjoerg 234264781Sbapt buf = NULL; 235264781Sbapt linecap = 0; 236264781Sbapt 23720747Sdavidn extendarray(&config.groups, &config.numgroups, 200); 23820747Sdavidn memset(config.groups, 0, config.numgroups * sizeof(char *)); 23920253Sjoerg if (file == NULL) 24020253Sjoerg file = _PATH_PW_CONF; 241264781Sbapt 24220253Sjoerg if ((fp = fopen(file, "r")) != NULL) { 243264781Sbapt while ((linelen = getline(&buf, &linecap, fp)) > 0) { 24420747Sdavidn if (*buf && (p = strtok(buf, " \t\r\n=")) != NULL && *p != '#') { 24520747Sdavidn static char const toks[] = " \t\r\n,="; 24620747Sdavidn char *q = strtok(NULL, toks); 24720747Sdavidn int i = 0; 248168044Sle mode_t *modeset; 24920747Sdavidn 25020747Sdavidn while (i < _UC_FIELDS && strcmp(p, kwds[i]) != 0) 25120747Sdavidn ++i; 25220253Sjoerg#if debugging 25320747Sdavidn if (i == _UC_FIELDS) 25420747Sdavidn printf("Got unknown kwd `%s' val=`%s'\n", p, q ? q : ""); 25520747Sdavidn else 25620747Sdavidn printf("Got kwd[%s]=%s\n", p, q); 25720253Sjoerg#endif 25820747Sdavidn switch (i) { 25920747Sdavidn case _UC_DEFAULTPWD: 26020747Sdavidn config.default_password = boolean_val(q, 1); 26120747Sdavidn break; 26220747Sdavidn case _UC_REUSEUID: 26320747Sdavidn config.reuse_uids = boolean_val(q, 0); 26420747Sdavidn break; 26520747Sdavidn case _UC_REUSEGID: 26620747Sdavidn config.reuse_gids = boolean_val(q, 0); 26720747Sdavidn break; 26821330Sdavidn case _UC_NISPASSWD: 26921330Sdavidn config.nispasswd = (q == NULL || !boolean_val(q, 1)) 27021330Sdavidn ? NULL : newstr(q); 27121330Sdavidn break; 27220747Sdavidn case _UC_DOTDIR: 27320747Sdavidn config.dotdir = (q == NULL || !boolean_val(q, 1)) 27420747Sdavidn ? NULL : newstr(q); 27520747Sdavidn break; 27620747Sdavidn case _UC_NEWMAIL: 27720747Sdavidn config.newmail = (q == NULL || !boolean_val(q, 1)) 27820747Sdavidn ? NULL : newstr(q); 27920747Sdavidn break; 28020747Sdavidn case _UC_LOGFILE: 28120747Sdavidn config.logfile = (q == NULL || !boolean_val(q, 1)) 28220747Sdavidn ? NULL : newstr(q); 28320747Sdavidn break; 28420747Sdavidn case _UC_HOMEROOT: 28520747Sdavidn config.home = (q == NULL || !boolean_val(q, 1)) 28620747Sdavidn ? "/home" : newstr(q); 28720747Sdavidn break; 288168044Sle case _UC_HOMEMODE: 289168044Sle modeset = setmode(q); 290168044Sle config.homemode = (q == NULL || !boolean_val(q, 1)) 291219408Sjkim ? _DEF_DIRMODE : getmode(modeset, _DEF_DIRMODE); 292168044Sle free(modeset); 293168044Sle break; 29420747Sdavidn case _UC_SHELLPATH: 29520747Sdavidn config.shelldir = (q == NULL || !boolean_val(q, 1)) 29620747Sdavidn ? "/bin" : newstr(q); 29720747Sdavidn break; 29820747Sdavidn case _UC_SHELLS: 29920747Sdavidn for (i = 0; i < _UC_MAXSHELLS && q != NULL; i++, q = strtok(NULL, toks)) 30020747Sdavidn system_shells[i] = newstr(q); 30120747Sdavidn if (i > 0) 30220747Sdavidn while (i < _UC_MAXSHELLS) 30320747Sdavidn system_shells[i++] = NULL; 30420747Sdavidn break; 30520747Sdavidn case _UC_DEFAULTSHELL: 30620747Sdavidn config.shell_default = (q == NULL || !boolean_val(q, 1)) 30720747Sdavidn ? (char *) bourne_shell : newstr(q); 30820747Sdavidn break; 30920747Sdavidn case _UC_DEFAULTGROUP: 31029002Sdavidn q = unquote(q); 31144229Sdavidn config.default_group = (q == NULL || !boolean_val(q, 1) || GETGRNAM(q) == NULL) 31220747Sdavidn ? NULL : newstr(q); 31320747Sdavidn break; 31420747Sdavidn case _UC_EXTRAGROUPS: 31520747Sdavidn for (i = 0; q != NULL; q = strtok(NULL, toks)) { 31620747Sdavidn if (extendarray(&config.groups, &config.numgroups, i + 2) != -1) 31720747Sdavidn config.groups[i++] = newstr(q); 31820253Sjoerg } 31920747Sdavidn if (i > 0) 32020747Sdavidn while (i < config.numgroups) 32120747Sdavidn config.groups[i++] = NULL; 32220747Sdavidn break; 32320747Sdavidn case _UC_DEFAULTCLASS: 32420747Sdavidn config.default_class = (q == NULL || !boolean_val(q, 1)) 32520747Sdavidn ? NULL : newstr(q); 32620747Sdavidn break; 32720747Sdavidn case _UC_MINUID: 32820747Sdavidn if ((q = unquote(q)) != NULL && isdigit(*q)) 32920747Sdavidn config.min_uid = (uid_t) atol(q); 33020747Sdavidn break; 33120747Sdavidn case _UC_MAXUID: 33220747Sdavidn if ((q = unquote(q)) != NULL && isdigit(*q)) 33320747Sdavidn config.max_uid = (uid_t) atol(q); 33420747Sdavidn break; 33520747Sdavidn case _UC_MINGID: 33620747Sdavidn if ((q = unquote(q)) != NULL && isdigit(*q)) 33720747Sdavidn config.min_gid = (gid_t) atol(q); 33820747Sdavidn break; 33920747Sdavidn case _UC_MAXGID: 34020747Sdavidn if ((q = unquote(q)) != NULL && isdigit(*q)) 34120747Sdavidn config.max_gid = (gid_t) atol(q); 34220747Sdavidn break; 34320747Sdavidn case _UC_EXPIRE: 34420747Sdavidn if ((q = unquote(q)) != NULL && isdigit(*q)) 34520747Sdavidn config.expire_days = atoi(q); 34620747Sdavidn break; 34720747Sdavidn case _UC_PASSWORD: 34820747Sdavidn if ((q = unquote(q)) != NULL && isdigit(*q)) 34920747Sdavidn config.password_days = atoi(q); 35020747Sdavidn break; 35120747Sdavidn case _UC_FIELDS: 35220747Sdavidn case _UC_NONE: 35320747Sdavidn break; 35420253Sjoerg } 35520253Sjoerg } 35620253Sjoerg } 357264781Sbapt if (linecap > 0) 358264781Sbapt free(buf); 35920253Sjoerg fclose(fp); 36020253Sjoerg } 36120253Sjoerg return &config; 36220253Sjoerg} 36320253Sjoerg 36420253Sjoerg 36520253Sjoergint 36620253Sjoergwrite_userconfig(char const * file) 36720253Sjoerg{ 36820253Sjoerg int fd; 369282697Sbapt int i, j; 370282681Sbapt struct sbuf *buf; 371282697Sbapt FILE *fp; 37220253Sjoerg 37320253Sjoerg if (file == NULL) 37420253Sjoerg file = _PATH_PW_CONF; 37520253Sjoerg 376282697Sbapt if ((fd = open(file, O_CREAT|O_RDWR|O_TRUNC|O_EXLOCK, 0644)) == -1) 377282697Sbapt return (0); 37820253Sjoerg 379282697Sbapt if ((fp = fdopen(fd, "w")) == NULL) { 380282697Sbapt close(fd); 381282697Sbapt return (0); 382282697Sbapt } 383282681Sbapt 384282697Sbapt buf = sbuf_new_auto(); 385282697Sbapt for (i = _UC_NONE; i < _UC_FIELDS; i++) { 386282697Sbapt int quote = 1; 38720253Sjoerg 388282697Sbapt sbuf_clear(buf); 389282697Sbapt switch (i) { 390282697Sbapt case _UC_DEFAULTPWD: 391282697Sbapt sbuf_cat(buf, boolean_str(config.default_password)); 392282697Sbapt break; 393282697Sbapt case _UC_REUSEUID: 394282697Sbapt sbuf_cat(buf, boolean_str(config.reuse_uids)); 395282697Sbapt break; 396282697Sbapt case _UC_REUSEGID: 397282697Sbapt sbuf_cat(buf, boolean_str(config.reuse_gids)); 398282697Sbapt break; 399282697Sbapt case _UC_NISPASSWD: 400282697Sbapt sbuf_cat(buf, config.nispasswd ? config.nispasswd : 401282697Sbapt ""); 402282697Sbapt quote = 0; 403282697Sbapt break; 404282697Sbapt case _UC_DOTDIR: 405282697Sbapt sbuf_cat(buf, config.dotdir ? config.dotdir : 406282697Sbapt boolean_str(0)); 407282697Sbapt break; 408282697Sbapt case _UC_NEWMAIL: 409282697Sbapt sbuf_cat(buf, config.newmail ? config.newmail : 410282697Sbapt boolean_str(0)); 411282697Sbapt break; 412282697Sbapt case _UC_LOGFILE: 413282697Sbapt sbuf_cat(buf, config.logfile ? config.logfile : 414282697Sbapt boolean_str(0)); 415282697Sbapt break; 416282697Sbapt case _UC_HOMEROOT: 417282697Sbapt sbuf_cat(buf, config.home); 418282697Sbapt break; 419282697Sbapt case _UC_HOMEMODE: 420282697Sbapt sbuf_printf(buf, "%04o", config.homemode); 421282697Sbapt quote = 0; 422282697Sbapt break; 423282697Sbapt case _UC_SHELLPATH: 424282697Sbapt sbuf_cat(buf, config.shelldir); 425282697Sbapt break; 426282697Sbapt case _UC_SHELLS: 427282697Sbapt for (j = 0; j < _UC_MAXSHELLS && 428282697Sbapt system_shells[j] != NULL; j++) 429282697Sbapt sbuf_printf(buf, "%s\"%s\"", j ? 430282697Sbapt "," : "", system_shells[j]); 431282697Sbapt quote = 0; 432282697Sbapt break; 433282697Sbapt case _UC_DEFAULTSHELL: 434282697Sbapt sbuf_cat(buf, config.shell_default ? 435282697Sbapt config.shell_default : bourne_shell); 436282697Sbapt break; 437282697Sbapt case _UC_DEFAULTGROUP: 438282697Sbapt sbuf_cat(buf, config.default_group ? 439282697Sbapt config.default_group : ""); 440282697Sbapt break; 441282697Sbapt case _UC_EXTRAGROUPS: 442282697Sbapt for (j = 0; j < config.numgroups && 443282697Sbapt config.groups[j] != NULL; j++) 444282697Sbapt sbuf_printf(buf, "%s\"%s\"", j ? 445282697Sbapt "," : "", config.groups[j]); 446282697Sbapt quote = 0; 447282697Sbapt break; 448282697Sbapt case _UC_DEFAULTCLASS: 449282697Sbapt sbuf_cat(buf, config.default_class ? 450282697Sbapt config.default_class : ""); 451282697Sbapt break; 452282697Sbapt case _UC_MINUID: 453282697Sbapt sbuf_printf(buf, "%lu", (unsigned long) config.min_uid); 454282697Sbapt quote = 0; 455282697Sbapt break; 456282697Sbapt case _UC_MAXUID: 457282697Sbapt sbuf_printf(buf, "%lu", (unsigned long) config.max_uid); 458282697Sbapt quote = 0; 459282697Sbapt break; 460282697Sbapt case _UC_MINGID: 461282697Sbapt sbuf_printf(buf, "%lu", (unsigned long) config.min_gid); 462282697Sbapt quote = 0; 463282697Sbapt break; 464282697Sbapt case _UC_MAXGID: 465282697Sbapt sbuf_printf(buf, "%lu", (unsigned long) config.max_gid); 466282697Sbapt quote = 0; 467282697Sbapt break; 468282697Sbapt case _UC_EXPIRE: 469282697Sbapt sbuf_printf(buf, "%d", config.expire_days); 470282697Sbapt quote = 0; 471282697Sbapt break; 472282697Sbapt case _UC_PASSWORD: 473282697Sbapt sbuf_printf(buf, "%d", config.password_days); 474282697Sbapt quote = 0; 475282697Sbapt break; 476282697Sbapt case _UC_NONE: 477282697Sbapt break; 478282697Sbapt } 479282697Sbapt sbuf_finish(buf); 48020253Sjoerg 481282697Sbapt if (comments[i]) 482282697Sbapt fputs(comments[i], fp); 48320253Sjoerg 484282697Sbapt if (*kwds[i]) { 485282697Sbapt if (quote) 486282697Sbapt fprintf(fp, "%s = \"%s\"\n", kwds[i], 487282697Sbapt sbuf_data(buf)); 488282697Sbapt else 489282697Sbapt fprintf(fp, "%s = %s\n", kwds[i], sbuf_data(buf)); 49020253Sjoerg#if debugging 491282697Sbapt printf("WROTE: %s = %s\n", kwds[i], sbuf_data(buf)); 49220253Sjoerg#endif 49320253Sjoerg } 49420253Sjoerg } 495282697Sbapt sbuf_delete(buf); 496282697Sbapt return (fclose(fp) != EOF); 49720253Sjoerg} 498