pw_conf.c revision 21330
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 * 2621330Sdavidn * $Id: pw_conf.c,v 1.2 1996/12/21 15:35:42 davidn Exp $ 2720253Sjoerg */ 2820253Sjoerg 2920253Sjoerg#include <string.h> 3020253Sjoerg#include <ctype.h> 3120253Sjoerg#include <fcntl.h> 3220253Sjoerg 3320253Sjoerg#include "pw.h" 3420747Sdavidn#include "pwupd.h" 3520253Sjoerg 3620253Sjoerg#define debugging 0 3720253Sjoerg 3820253Sjoergenum { 3920253Sjoerg _UC_NONE, 4020253Sjoerg _UC_DEFAULTPWD, 4120253Sjoerg _UC_REUSEUID, 4220253Sjoerg _UC_REUSEGID, 4321330Sdavidn _UC_NISPASSWD, 4420253Sjoerg _UC_DOTDIR, 4520253Sjoerg _UC_NEWMAIL, 4620253Sjoerg _UC_LOGFILE, 4720253Sjoerg _UC_HOMEROOT, 4820253Sjoerg _UC_SHELLPATH, 4920253Sjoerg _UC_SHELLS, 5020253Sjoerg _UC_DEFAULTSHELL, 5120253Sjoerg _UC_DEFAULTGROUP, 5220253Sjoerg _UC_EXTRAGROUPS, 5320253Sjoerg _UC_DEFAULTCLASS, 5420253Sjoerg _UC_MINUID, 5520253Sjoerg _UC_MAXUID, 5620253Sjoerg _UC_MINGID, 5720253Sjoerg _UC_MAXGID, 5820253Sjoerg _UC_EXPIRE, 5920253Sjoerg _UC_PASSWORD, 6020253Sjoerg _UC_FIELDS 6120253Sjoerg}; 6220253Sjoerg 6320253Sjoergstatic char bourne_shell[] = "sh"; 6420253Sjoerg 6520253Sjoergstatic char *system_shells[_UC_MAXSHELLS] = 6620253Sjoerg{ 6720253Sjoerg bourne_shell, 6820253Sjoerg "csh" 6920253Sjoerg}; 7020253Sjoerg 7120253Sjoergstatic char const *booltrue[] = 7220253Sjoerg{ 7320253Sjoerg "yes", "true", "1", "on", NULL 7420253Sjoerg}; 7520253Sjoergstatic char const *boolfalse[] = 7620253Sjoerg{ 7720253Sjoerg "no", "false", "0", "off", NULL 7820253Sjoerg}; 7920253Sjoerg 8020253Sjoergstatic struct userconf config = 8120253Sjoerg{ 8220253Sjoerg 0, /* Default password for new users? (nologin) */ 8320253Sjoerg 0, /* Reuse uids? */ 8420253Sjoerg 0, /* Reuse gids? */ 8521330Sdavidn NULL, /* NIS version of the passwd file */ 8620253Sjoerg "/usr/share/skel", /* Where to obtain skeleton files */ 8720253Sjoerg NULL, /* Mail to send to new accounts */ 8820253Sjoerg "/var/log/userlog", /* Where to log changes */ 8920253Sjoerg "/home", /* Where to create home directory */ 9020253Sjoerg "/bin", /* Where shells are located */ 9120253Sjoerg system_shells, /* List of shells (first is default) */ 9220253Sjoerg bourne_shell, /* Default shell */ 9320253Sjoerg NULL, /* Default group name */ 9420747Sdavidn NULL, /* Default (additional) groups */ 9520253Sjoerg NULL, /* Default login class */ 9620253Sjoerg 1000, 32000, /* Allowed range of uids */ 9720253Sjoerg 1000, 32000, /* Allowed range of gids */ 9820253Sjoerg 0, /* Days until account expires */ 9920253Sjoerg 0 /* Days until password expires */ 10020253Sjoerg}; 10120253Sjoerg 10220253Sjoergstatic char const *comments[_UC_FIELDS] = 10320253Sjoerg{ 10420253Sjoerg "#\n# pw.conf - user/group configuration defaults\n#\n", 10520253Sjoerg "\n# Password for new users? no=nologin yes=loginid none=blank random=random\n", 10620253Sjoerg "\n# Reuse gaps in uid sequence? (yes or no)\n", 10720253Sjoerg "\n# Reuse gaps in gid sequence? (yes or no)\n", 10821330Sdavidn "\n# Path to the NIS passwd file (blank or 'no' for none)\n", 10920253Sjoerg "\n# Obtain default dotfiles from this directory\n", 11020253Sjoerg "\n# Mail this file to new user (/etc/newuser.msg or no)\n", 11120253Sjoerg "\n# Log add/change/remove information in this file\n", 11220253Sjoerg "\n# Root directory in which $HOME directory is created\n", 11320253Sjoerg "\n# Colon separated list of directories containing valid shells\n", 11420253Sjoerg "\n# Space separated list of available shells (without paths)\n", 11520253Sjoerg "\n# Default shell (without path)\n", 11620253Sjoerg "\n# Default group (leave blank for new group per user)\n", 11720253Sjoerg "\n# Extra groups for new users\n", 11820253Sjoerg "\n# Default login class for new users\n", 11920253Sjoerg "\n# Range of valid default user ids\n", 12020253Sjoerg NULL, 12120253Sjoerg "\n# Range of valid default group ids\n", 12220253Sjoerg NULL, 12320253Sjoerg "\n# Days after which account expires (0=disabled)\n", 12420253Sjoerg "\n# Days after which password expires (0=disabled)\n" 12520253Sjoerg}; 12620253Sjoerg 12720253Sjoergstatic char const *kwds[] = 12820253Sjoerg{ 12920253Sjoerg "", 13020253Sjoerg "defaultpasswd", 13120253Sjoerg "reuseuids", 13220253Sjoerg "reusegids", 13321330Sdavidn "nispasswd", 13420253Sjoerg "skeleton", 13520253Sjoerg "newmail", 13620253Sjoerg "logfile", 13720253Sjoerg "home", 13820253Sjoerg "shellpath", 13920253Sjoerg "shells", 14020253Sjoerg "defaultshell", 14120253Sjoerg "defaultgroup", 14220253Sjoerg "extragroups", 14320253Sjoerg "defaultclass", 14420253Sjoerg "minuid", 14520253Sjoerg "maxuid", 14620253Sjoerg "mingid", 14720253Sjoerg "maxgid", 14820253Sjoerg "expire_days", 14920253Sjoerg "password_days", 15020253Sjoerg NULL 15120253Sjoerg}; 15220253Sjoerg 15320253Sjoergstatic char * 15420253Sjoergunquote(char const * str) 15520253Sjoerg{ 15620253Sjoerg if (str && (*str == '"' || *str == '\'')) { 15720253Sjoerg char *p = strchr(str + 1, *str); 15820253Sjoerg 15920253Sjoerg if (p != NULL) 16020253Sjoerg *p = '\0'; 16120253Sjoerg return (char *) (*++str ? str : NULL); 16220253Sjoerg } 16320253Sjoerg return (char *) str; 16420253Sjoerg} 16520253Sjoerg 16620253Sjoergint 16720253Sjoergboolean_val(char const * str, int dflt) 16820253Sjoerg{ 16920253Sjoerg if ((str = unquote(str)) != NULL) { 17020253Sjoerg int i; 17120253Sjoerg 17220253Sjoerg for (i = 0; booltrue[i]; i++) 17320253Sjoerg if (strcmp(str, booltrue[i]) == 0) 17420253Sjoerg return 1; 17520253Sjoerg for (i = 0; boolfalse[i]; i++) 17620253Sjoerg if (strcmp(str, boolfalse[i]) == 0) 17720253Sjoerg return 0; 17820253Sjoerg 17920253Sjoerg /* 18020253Sjoerg * Special cases for defaultpassword 18120253Sjoerg */ 18220253Sjoerg if (strcmp(str, "random") == 0) 18320253Sjoerg return -1; 18420253Sjoerg if (strcmp(str, "none") == 0) 18520253Sjoerg return -2; 18620253Sjoerg } 18720253Sjoerg return dflt; 18820253Sjoerg} 18920253Sjoerg 19020253Sjoergchar const * 19120253Sjoergboolean_str(int val) 19220253Sjoerg{ 19320253Sjoerg if (val == -1) 19420253Sjoerg return "random"; 19520253Sjoerg else if (val == -2) 19620253Sjoerg return "none"; 19720253Sjoerg else 19820253Sjoerg return val ? booltrue[0] : boolfalse[0]; 19920253Sjoerg} 20020253Sjoerg 20120253Sjoergchar * 20220253Sjoergnewstr(char const * p) 20320253Sjoerg{ 20420253Sjoerg char *q = NULL; 20520253Sjoerg 20620253Sjoerg if ((p = unquote(p)) != NULL) { 20720253Sjoerg int l = strlen(p) + 1; 20820253Sjoerg 20920253Sjoerg if ((q = malloc(l)) != NULL) 21020253Sjoerg memcpy(q, p, l); 21120253Sjoerg } 21220253Sjoerg return q; 21320253Sjoerg} 21420253Sjoerg 21520747Sdavidn#define LNBUFSZ 1024 21620253Sjoerg 21720747Sdavidn 21820253Sjoergstruct userconf * 21920253Sjoergread_userconfig(char const * file) 22020253Sjoerg{ 22120253Sjoerg FILE *fp; 22220253Sjoerg 22320747Sdavidn extendarray(&config.groups, &config.numgroups, 200); 22420747Sdavidn memset(config.groups, 0, config.numgroups * sizeof(char *)); 22520253Sjoerg if (file == NULL) 22620253Sjoerg file = _PATH_PW_CONF; 22720253Sjoerg if ((fp = fopen(file, "r")) != NULL) { 22820747Sdavidn int buflen = LNBUFSZ; 22920747Sdavidn char *buf = malloc(buflen); 23020253Sjoerg 23120747Sdavidn nextline: 23220747Sdavidn while (fgets(buf, buflen, fp) != NULL) { 23320747Sdavidn char *p; 23420253Sjoerg 23520747Sdavidn while ((p = strchr(buf, '\n')) == NULL) { 23620747Sdavidn int l; 23720747Sdavidn if (extendline(&buf, &buflen, buflen + LNBUFSZ) == -1) { 23820747Sdavidn int ch; 23920747Sdavidn while ((ch = fgetc(fp)) != '\n' && ch != EOF); 24020747Sdavidn goto nextline; /* Ignore it */ 24120747Sdavidn } 24220747Sdavidn l = strlen(buf); 24320747Sdavidn if (fgets(buf + l, buflen - l, fp) == NULL) 24420747Sdavidn break; /* Unterminated last line */ 24520747Sdavidn } 24620253Sjoerg 24720747Sdavidn if (p != NULL) 24820253Sjoerg *p = '\0'; 24920253Sjoerg 25020747Sdavidn if (*buf && (p = strtok(buf, " \t\r\n=")) != NULL && *p != '#') { 25120747Sdavidn static char const toks[] = " \t\r\n,="; 25220747Sdavidn char *q = strtok(NULL, toks); 25320747Sdavidn int i = 0; 25420747Sdavidn 25520747Sdavidn while (i < _UC_FIELDS && strcmp(p, kwds[i]) != 0) 25620747Sdavidn ++i; 25720253Sjoerg#if debugging 25820747Sdavidn if (i == _UC_FIELDS) 25920747Sdavidn printf("Got unknown kwd `%s' val=`%s'\n", p, q ? q : ""); 26020747Sdavidn else 26120747Sdavidn printf("Got kwd[%s]=%s\n", p, q); 26220253Sjoerg#endif 26320747Sdavidn switch (i) { 26420747Sdavidn case _UC_DEFAULTPWD: 26520747Sdavidn config.default_password = boolean_val(q, 1); 26620747Sdavidn break; 26720747Sdavidn case _UC_REUSEUID: 26820747Sdavidn config.reuse_uids = boolean_val(q, 0); 26920747Sdavidn break; 27020747Sdavidn case _UC_REUSEGID: 27120747Sdavidn config.reuse_gids = boolean_val(q, 0); 27220747Sdavidn break; 27321330Sdavidn case _UC_NISPASSWD: 27421330Sdavidn config.nispasswd = (q == NULL || !boolean_val(q, 1)) 27521330Sdavidn ? NULL : newstr(q); 27621330Sdavidn break; 27720747Sdavidn case _UC_DOTDIR: 27820747Sdavidn config.dotdir = (q == NULL || !boolean_val(q, 1)) 27920747Sdavidn ? NULL : newstr(q); 28020747Sdavidn break; 28120747Sdavidn case _UC_NEWMAIL: 28220747Sdavidn config.newmail = (q == NULL || !boolean_val(q, 1)) 28320747Sdavidn ? NULL : newstr(q); 28420747Sdavidn break; 28520747Sdavidn case _UC_LOGFILE: 28620747Sdavidn config.logfile = (q == NULL || !boolean_val(q, 1)) 28720747Sdavidn ? NULL : newstr(q); 28820747Sdavidn break; 28920747Sdavidn case _UC_HOMEROOT: 29020747Sdavidn config.home = (q == NULL || !boolean_val(q, 1)) 29120747Sdavidn ? "/home" : newstr(q); 29220747Sdavidn break; 29320747Sdavidn case _UC_SHELLPATH: 29420747Sdavidn config.shelldir = (q == NULL || !boolean_val(q, 1)) 29520747Sdavidn ? "/bin" : newstr(q); 29620747Sdavidn break; 29720747Sdavidn case _UC_SHELLS: 29820747Sdavidn for (i = 0; i < _UC_MAXSHELLS && q != NULL; i++, q = strtok(NULL, toks)) 29920747Sdavidn system_shells[i] = newstr(q); 30020747Sdavidn if (i > 0) 30120747Sdavidn while (i < _UC_MAXSHELLS) 30220747Sdavidn system_shells[i++] = NULL; 30320747Sdavidn break; 30420747Sdavidn case _UC_DEFAULTSHELL: 30520747Sdavidn config.shell_default = (q == NULL || !boolean_val(q, 1)) 30620747Sdavidn ? (char *) bourne_shell : newstr(q); 30720747Sdavidn break; 30820747Sdavidn case _UC_DEFAULTGROUP: 30920747Sdavidn config.default_group = (q == NULL || !boolean_val(q, 1) || getgrnam(q) == NULL) 31020747Sdavidn ? NULL : newstr(q); 31120747Sdavidn break; 31220747Sdavidn case _UC_EXTRAGROUPS: 31320747Sdavidn for (i = 0; q != NULL; q = strtok(NULL, toks)) { 31420747Sdavidn if (extendarray(&config.groups, &config.numgroups, i + 2) != -1) 31520747Sdavidn config.groups[i++] = newstr(q); 31620253Sjoerg } 31720747Sdavidn if (i > 0) 31820747Sdavidn while (i < config.numgroups) 31920747Sdavidn config.groups[i++] = NULL; 32020747Sdavidn break; 32120747Sdavidn case _UC_DEFAULTCLASS: 32220747Sdavidn config.default_class = (q == NULL || !boolean_val(q, 1)) 32320747Sdavidn ? NULL : newstr(q); 32420747Sdavidn break; 32520747Sdavidn case _UC_MINUID: 32620747Sdavidn if ((q = unquote(q)) != NULL && isdigit(*q)) 32720747Sdavidn config.min_uid = (uid_t) atol(q); 32820747Sdavidn break; 32920747Sdavidn case _UC_MAXUID: 33020747Sdavidn if ((q = unquote(q)) != NULL && isdigit(*q)) 33120747Sdavidn config.max_uid = (uid_t) atol(q); 33220747Sdavidn break; 33320747Sdavidn case _UC_MINGID: 33420747Sdavidn if ((q = unquote(q)) != NULL && isdigit(*q)) 33520747Sdavidn config.min_gid = (gid_t) atol(q); 33620747Sdavidn break; 33720747Sdavidn case _UC_MAXGID: 33820747Sdavidn if ((q = unquote(q)) != NULL && isdigit(*q)) 33920747Sdavidn config.max_gid = (gid_t) atol(q); 34020747Sdavidn break; 34120747Sdavidn case _UC_EXPIRE: 34220747Sdavidn if ((q = unquote(q)) != NULL && isdigit(*q)) 34320747Sdavidn config.expire_days = atoi(q); 34420747Sdavidn break; 34520747Sdavidn case _UC_PASSWORD: 34620747Sdavidn if ((q = unquote(q)) != NULL && isdigit(*q)) 34720747Sdavidn config.password_days = atoi(q); 34820747Sdavidn break; 34920747Sdavidn case _UC_FIELDS: 35020747Sdavidn case _UC_NONE: 35120747Sdavidn break; 35220253Sjoerg } 35320253Sjoerg } 35420253Sjoerg } 35520747Sdavidn free(buf); 35620253Sjoerg fclose(fp); 35720253Sjoerg } 35820253Sjoerg return &config; 35920253Sjoerg} 36020253Sjoerg 36120253Sjoerg 36220253Sjoergint 36320253Sjoergwrite_userconfig(char const * file) 36420253Sjoerg{ 36520253Sjoerg int fd; 36620253Sjoerg 36720253Sjoerg if (file == NULL) 36820253Sjoerg file = _PATH_PW_CONF; 36920253Sjoerg 37020253Sjoerg if ((fd = open(file, O_CREAT | O_RDWR | O_TRUNC | O_EXLOCK, 0644)) != -1) { 37120253Sjoerg FILE *fp; 37220253Sjoerg 37320253Sjoerg if ((fp = fdopen(fd, "w")) == NULL) 37420253Sjoerg close(fd); 37520253Sjoerg else { 37620253Sjoerg int i, j, k; 37720747Sdavidn int len = LNBUFSZ; 37820747Sdavidn char *buf = malloc(len); 37920253Sjoerg 38020253Sjoerg for (i = _UC_NONE; i < _UC_FIELDS; i++) { 38120253Sjoerg int quote = 1; 38220253Sjoerg char const *val = buf; 38320253Sjoerg 38420253Sjoerg *buf = '\0'; 38520253Sjoerg switch (i) { 38620253Sjoerg case _UC_DEFAULTPWD: 38720253Sjoerg val = boolean_str(config.default_password); 38820253Sjoerg break; 38920253Sjoerg case _UC_REUSEUID: 39020253Sjoerg val = boolean_str(config.reuse_uids); 39120253Sjoerg break; 39220253Sjoerg case _UC_REUSEGID: 39320253Sjoerg val = boolean_str(config.reuse_gids); 39420253Sjoerg break; 39521330Sdavidn case _UC_NISPASSWD: 39621330Sdavidn val = config.nispasswd ? config.nispasswd : ""; 39721330Sdavidn quote = 0; 39821330Sdavidn break; 39920253Sjoerg case _UC_DOTDIR: 40020253Sjoerg val = config.dotdir ? config.dotdir : boolean_str(0); 40120253Sjoerg break; 40220253Sjoerg case _UC_NEWMAIL: 40320253Sjoerg val = config.newmail ? config.newmail : boolean_str(0); 40420253Sjoerg break; 40520253Sjoerg case _UC_LOGFILE: 40620253Sjoerg val = config.logfile ? config.logfile : boolean_str(0); 40720253Sjoerg break; 40820253Sjoerg case _UC_HOMEROOT: 40920253Sjoerg val = config.home; 41020253Sjoerg break; 41120253Sjoerg case _UC_SHELLPATH: 41220253Sjoerg val = config.shelldir; 41320253Sjoerg break; 41420253Sjoerg case _UC_SHELLS: 41520747Sdavidn for (j = k = 0; j < _UC_MAXSHELLS && system_shells[j] != NULL; j++) { 41620747Sdavidn char lbuf[64]; 41720747Sdavidn int l = snprintf(lbuf, sizeof lbuf, "%s\"%s\"", k ? "," : "", system_shells[j]); 41820747Sdavidn if (l + k + 1 < len || extendline(&buf, &len, len + LNBUFSZ) != -1) { 41920747Sdavidn strcpy(buf + k, lbuf); 42020747Sdavidn k += l; 42120747Sdavidn } 42220747Sdavidn } 42320253Sjoerg quote = 0; 42420253Sjoerg break; 42520253Sjoerg case _UC_DEFAULTSHELL: 42620253Sjoerg val = config.shell_default ? config.shell_default : bourne_shell; 42720253Sjoerg break; 42820253Sjoerg case _UC_DEFAULTGROUP: 42920253Sjoerg val = config.default_group ? config.default_group : ""; 43020253Sjoerg break; 43120253Sjoerg case _UC_EXTRAGROUPS: 43220747Sdavidn extendarray(&config.groups, &config.numgroups, 200); 43320747Sdavidn for (j = k = 0; j < config.numgroups && config.groups[j] != NULL; j++) { 43420747Sdavidn char lbuf[64]; 43520747Sdavidn int l = snprintf(lbuf, sizeof lbuf, "%s\"%s\"", k ? "," : "", config.groups[j]); 43620747Sdavidn if (l + k + 1 < len || extendline(&buf, &len, len + 1024) != -1) { 43720747Sdavidn strcpy(buf + k, lbuf); 43820747Sdavidn k += l; 43920747Sdavidn } 44020747Sdavidn } 44120253Sjoerg quote = 0; 44220253Sjoerg break; 44320253Sjoerg case _UC_DEFAULTCLASS: 44420253Sjoerg val = config.default_class ? config.default_class : ""; 44520253Sjoerg break; 44620253Sjoerg case _UC_MINUID: 44720253Sjoerg sprintf(buf, "%lu", (unsigned long) config.min_uid); 44820253Sjoerg quote = 0; 44920253Sjoerg break; 45020253Sjoerg case _UC_MAXUID: 45120253Sjoerg sprintf(buf, "%lu", (unsigned long) config.max_uid); 45220253Sjoerg quote = 0; 45320253Sjoerg break; 45420253Sjoerg case _UC_MINGID: 45520253Sjoerg sprintf(buf, "%lu", (unsigned long) config.min_gid); 45620253Sjoerg quote = 0; 45720253Sjoerg break; 45820253Sjoerg case _UC_MAXGID: 45920253Sjoerg sprintf(buf, "%lu", (unsigned long) config.max_gid); 46020253Sjoerg quote = 0; 46120253Sjoerg break; 46220253Sjoerg case _UC_EXPIRE: 46320253Sjoerg sprintf(buf, "%d", config.expire_days); 46420253Sjoerg quote = 0; 46520253Sjoerg break; 46620253Sjoerg case _UC_PASSWORD: 46720253Sjoerg sprintf(buf, "%d", config.password_days); 46820253Sjoerg quote = 0; 46920253Sjoerg break; 47020253Sjoerg case _UC_NONE: 47120253Sjoerg break; 47220253Sjoerg } 47320253Sjoerg 47420253Sjoerg if (comments[i]) 47520253Sjoerg fputs(comments[i], fp); 47620253Sjoerg 47720253Sjoerg if (*kwds[i]) { 47820253Sjoerg if (quote) 47920253Sjoerg fprintf(fp, "%s = \"%s\"\n", kwds[i], val); 48020253Sjoerg else 48120253Sjoerg fprintf(fp, "%s = %s\n", kwds[i], val); 48220253Sjoerg#if debugging 48320253Sjoerg printf("WROTE: %s = %s\n", kwds[i], val); 48420253Sjoerg#endif 48520253Sjoerg } 48620253Sjoerg } 48720747Sdavidn free(buf); 48820253Sjoerg return fclose(fp) != EOF; 48920253Sjoerg } 49020253Sjoerg } 49120253Sjoerg return 0; 49220253Sjoerg} 493