pw_conf.c revision 81982
167754Smsmith/*-
267754Smsmith * Copyright (C) 1996
367754Smsmith *	David L. Nugent.  All rights reserved.
467754Smsmith *
567754Smsmith * Redistribution and use in source and binary forms, with or without
667754Smsmith * modification, are permitted provided that the following conditions
7217365Sjkim * are met:
8245582Sjkim * 1. Redistributions of source code must retain the above copyright
970243Smsmith *    notice, this list of conditions and the following disclaimer.
1067754Smsmith * 2. Redistributions in binary form must reproduce the above copyright
11217365Sjkim *    notice, this list of conditions and the following disclaimer in the
12217365Sjkim *    documentation and/or other materials provided with the distribution.
13217365Sjkim *
14217365Sjkim * THIS SOFTWARE IS PROVIDED BY DAVID L. NUGENT AND CONTRIBUTORS ``AS IS'' AND
15217365Sjkim * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16217365Sjkim * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17217365Sjkim * ARE DISCLAIMED.  IN NO EVENT SHALL DAVID L. NUGENT OR CONTRIBUTORS BE LIABLE
18217365Sjkim * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19217365Sjkim * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20217365Sjkim * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21217365Sjkim * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22217365Sjkim * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23217365Sjkim * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24217365Sjkim * SUCH DAMAGE.
2567754Smsmith */
26217365Sjkim
27217365Sjkim#ifndef lint
28217365Sjkimstatic const char rcsid[] =
2967754Smsmith  "$FreeBSD: head/usr.sbin/pw/pw_conf.c 81982 2001-08-20 15:09:34Z brian $";
30217365Sjkim#endif /* not lint */
31217365Sjkim
32217365Sjkim#include <string.h>
33217365Sjkim#include <ctype.h>
34217365Sjkim#include <fcntl.h>
35217365Sjkim
36217365Sjkim#include "pw.h"
37217365Sjkim
38217365Sjkim#define debugging 0
39217365Sjkim
40217365Sjkimenum {
41217365Sjkim	_UC_NONE,
42217365Sjkim	_UC_DEFAULTPWD,
4367754Smsmith	_UC_REUSEUID,
4467754Smsmith	_UC_REUSEGID,
45193341Sjkim	_UC_NISPASSWD,
46193341Sjkim	_UC_DOTDIR,
47193341Sjkim	_UC_NEWMAIL,
48193341Sjkim	_UC_LOGFILE,
4967754Smsmith	_UC_HOMEROOT,
50102550Siwasaki	_UC_SHELLPATH,
5167754Smsmith	_UC_SHELLS,
52102550Siwasaki	_UC_DEFAULTSHELL,
5391116Smsmith	_UC_DEFAULTGROUP,
5467754Smsmith	_UC_EXTRAGROUPS,
5567754Smsmith	_UC_DEFAULTCLASS,
56222544Sjkim	_UC_MINUID,
5767754Smsmith	_UC_MAXUID,
58151937Sjkim	_UC_MINGID,
5967754Smsmith	_UC_MAXGID,
60151937Sjkim	_UC_EXPIRE,
61151937Sjkim	_UC_PASSWORD,
62151937Sjkim	_UC_FIELDS
63151937Sjkim};
64151937Sjkim
65151937Sjkimstatic char     bourne_shell[] = "sh";
66151937Sjkim
67151937Sjkimstatic char    *system_shells[_UC_MAXSHELLS] =
68151937Sjkim{
69151937Sjkim	bourne_shell,
70151937Sjkim	"csh",
71151937Sjkim	"tcsh"
72151937Sjkim};
73151937Sjkim
74151937Sjkimstatic char const *booltrue[] =
75151937Sjkim{
76151937Sjkim	"yes", "true", "1", "on", NULL
77151937Sjkim};
78151937Sjkimstatic char const *boolfalse[] =
79151937Sjkim{
80151937Sjkim	"no", "false", "0", "off", NULL
81151937Sjkim};
82151937Sjkim
83151937Sjkimstatic struct userconf config =
84151937Sjkim{
8567754Smsmith	0,			/* Default password for new users? (nologin) */
8667754Smsmith	0,			/* Reuse uids? */
87222544Sjkim	0,			/* Reuse gids? */
88222544Sjkim	NULL,			/* NIS version of the passwd file */
89222544Sjkim	"/usr/share/skel",	/* Where to obtain skeleton files */
90222544Sjkim	NULL,			/* Mail to send to new accounts */
91222544Sjkim	"/var/log/userlog",	/* Where to log changes */
92222544Sjkim	"/home",		/* Where to create home directory */
93222544Sjkim	"/bin",			/* Where shells are located */
94222544Sjkim	system_shells,		/* List of shells (first is default) */
95222544Sjkim	bourne_shell,		/* Default shell */
96222544Sjkim	NULL,			/* Default group name */
97222544Sjkim	NULL,			/* Default (additional) groups */
98222544Sjkim	NULL,			/* Default login class */
99245582Sjkim	1000, 32000,		/* Allowed range of uids */
100222544Sjkim	1000, 32000,		/* Allowed range of gids */
101222544Sjkim	0,			/* Days until account expires */
102222544Sjkim	0,			/* Days until password expires */
103222544Sjkim	0			/* size of default_group array */
104222544Sjkim};
105222544Sjkim
106222544Sjkimstatic char const *comments[_UC_FIELDS] =
107222544Sjkim{
108222544Sjkim	"#\n# pw.conf - user/group configuration defaults\n#\n",
109222544Sjkim	"\n# Password for new users? no=nologin yes=loginid none=blank random=random\n",
110222544Sjkim	"\n# Reuse gaps in uid sequence? (yes or no)\n",
111222544Sjkim	"\n# Reuse gaps in gid sequence? (yes or no)\n",
112222544Sjkim	"\n# Path to the NIS passwd file (blank or 'no' for none)\n",
113222544Sjkim	"\n# Obtain default dotfiles from this directory\n",
114222544Sjkim	"\n# Mail this file to new user (/etc/newuser.msg or no)\n",
115222544Sjkim	"\n# Log add/change/remove information in this file\n",
116222544Sjkim	"\n# Root directory in which $HOME directory is created\n",
117222544Sjkim	"\n# Colon separated list of directories containing valid shells\n",
118222544Sjkim	"\n# Comma separated list of available shells (without paths)\n",
119222544Sjkim	"\n# Default shell (without path)\n",
120222544Sjkim	"\n# Default group (leave blank for new group per user)\n",
121222544Sjkim	"\n# Extra groups for new users\n",
122222544Sjkim	"\n# Default login class for new users\n",
123222544Sjkim	"\n# Range of valid default user ids\n",
124222544Sjkim	NULL,
125222544Sjkim	"\n# Range of valid default group ids\n",
126222544Sjkim	NULL,
127222544Sjkim	"\n# Days after which account expires (0=disabled)\n",
128222544Sjkim	"\n# Days after which password expires (0=disabled)\n"
129222544Sjkim};
130222544Sjkim
131222544Sjkimstatic char const *kwds[] =
132222544Sjkim{
133222544Sjkim	"",
134222544Sjkim	"defaultpasswd",
135222544Sjkim	"reuseuids",
13667754Smsmith	"reusegids",
13767754Smsmith	"nispasswd",
13867754Smsmith	"skeleton",
13967754Smsmith	"newmail",
14067754Smsmith	"logfile",
14167754Smsmith	"home",
14267754Smsmith	"shellpath",
14367754Smsmith	"shells",
14467754Smsmith	"defaultshell",
14567754Smsmith	"defaultgroup",
14667754Smsmith	"extragroups",
147151937Sjkim	"defaultclass",
14867754Smsmith	"minuid",
14991116Smsmith	"maxuid",
15067754Smsmith	"mingid",
15167754Smsmith	"maxgid",
15267754Smsmith	"expire_days",
15367754Smsmith	"password_days",
154114237Snjl	NULL
155222544Sjkim};
15667754Smsmith
15767754Smsmithstatic char    *
15867754Smsmithunquote(char const * str)
159216471Sjkim{
160216471Sjkim	if (str && (*str == '"' || *str == '\'')) {
161216471Sjkim		char           *p = strchr(str + 1, *str);
16283174Smsmith
16367754Smsmith		if (p != NULL)
16467754Smsmith			*p = '\0';
16567754Smsmith		return (char *) (*++str ? str : NULL);
16667754Smsmith	}
167193267Sjkim	return (char *) str;
168193267Sjkim}
169239340Sjkim
170193267Sjkimint
171193267Sjkimboolean_val(char const * str, int dflt)
172216471Sjkim{
17367754Smsmith	if ((str = unquote(str)) != NULL) {
174193267Sjkim		int             i;
175193267Sjkim
176193267Sjkim		for (i = 0; booltrue[i]; i++)
177193267Sjkim			if (strcmp(str, booltrue[i]) == 0)
178193267Sjkim				return 1;
17967754Smsmith		for (i = 0; boolfalse[i]; i++)
180193267Sjkim			if (strcmp(str, boolfalse[i]) == 0)
18167754Smsmith				return 0;
182222544Sjkim
183193267Sjkim		/*
184193267Sjkim		 * Special cases for defaultpassword
185222544Sjkim		 */
186222544Sjkim		if (strcmp(str, "random") == 0)
187222544Sjkim			return -1;
188217365Sjkim		if (strcmp(str, "none") == 0)
189217365Sjkim			return -2;
190217365Sjkim	}
191193267Sjkim	return dflt;
192222544Sjkim}
193222544Sjkim
194222544Sjkimchar const     *
195222544Sjkimboolean_str(int val)
196222544Sjkim{
197222544Sjkim	if (val == -1)
198222544Sjkim		return "random";
199222544Sjkim	else if (val == -2)
200222544Sjkim		return "none";
201222544Sjkim	else
202193267Sjkim		return val ? booltrue[0] : boolfalse[0];
203222544Sjkim}
20467754Smsmith
205222544Sjkimchar           *
206222544Sjkimnewstr(char const * p)
207222544Sjkim{
208193267Sjkim	char           *q = NULL;
209222544Sjkim
210222544Sjkim	if ((p = unquote(p)) != NULL) {
21167754Smsmith		int             l = strlen(p) + 1;
212222544Sjkim
213193267Sjkim		if ((q = malloc(l)) != NULL)
214193267Sjkim			memcpy(q, p, l);
215193267Sjkim	}
216193267Sjkim	return q;
217193267Sjkim}
218193267Sjkim
219193267Sjkim#define LNBUFSZ 1024
220193267Sjkim
221193267Sjkim
222193267Sjkimstruct userconf *
223193267Sjkimread_userconfig(char const * file)
224193267Sjkim{
225193267Sjkim	FILE           *fp;
226193267Sjkim
227193267Sjkim	extendarray(&config.groups, &config.numgroups, 200);
228193267Sjkim	memset(config.groups, 0, config.numgroups * sizeof(char *));
229193267Sjkim	if (file == NULL)
230193267Sjkim		file = _PATH_PW_CONF;
231193267Sjkim	if ((fp = fopen(file, "r")) != NULL) {
232202771Sjkim		int	    buflen = LNBUFSZ;
233193267Sjkim		char       *buf = malloc(buflen);
234193267Sjkim
235193267Sjkim	nextline:
236222544Sjkim		while (fgets(buf, buflen, fp) != NULL) {
237193267Sjkim			char           *p;
238222544Sjkim
239222544Sjkim			while ((p = strchr(buf, '\n')) == NULL) {
24067754Smsmith				int	  l;
24167754Smsmith				if (extendline(&buf, &buflen, buflen + LNBUFSZ) == -1) {
24267754Smsmith					int	ch;
24367754Smsmith					while ((ch = fgetc(fp)) != '\n' && ch != EOF);
244151937Sjkim					goto nextline;	/* Ignore it */
245151937Sjkim				}
24667754Smsmith				l = strlen(buf);
24767754Smsmith				if (fgets(buf + l, buflen - l, fp) == NULL)
24867754Smsmith					break;	/* Unterminated last line */
249114237Snjl			}
250151937Sjkim
251222544Sjkim			if (p != NULL)
25267754Smsmith				*p = '\0';
25367754Smsmith
25467754Smsmith			if (*buf && (p = strtok(buf, " \t\r\n=")) != NULL && *p != '#') {
25567754Smsmith				static char const toks[] = " \t\r\n,=";
256216471Sjkim				char           *q = strtok(NULL, toks);
257216471Sjkim				int             i = 0;
258216471Sjkim
259222544Sjkim				while (i < _UC_FIELDS && strcmp(p, kwds[i]) != 0)
260216471Sjkim					++i;
261216471Sjkim#if debugging
262216471Sjkim				if (i == _UC_FIELDS)
263216471Sjkim					printf("Got unknown kwd `%s' val=`%s'\n", p, q ? q : "");
264222544Sjkim				else
265216471Sjkim					printf("Got kwd[%s]=%s\n", p, q);
266216471Sjkim#endif
267216471Sjkim				switch (i) {
268216471Sjkim				case _UC_DEFAULTPWD:
269222544Sjkim					config.default_password = boolean_val(q, 1);
270222544Sjkim					break;
271222544Sjkim				case _UC_REUSEUID:
272222544Sjkim					config.reuse_uids = boolean_val(q, 0);
273216471Sjkim					break;
27467754Smsmith				case _UC_REUSEGID:
27567754Smsmith					config.reuse_gids = boolean_val(q, 0);
27667754Smsmith					break;
27767754Smsmith				case _UC_NISPASSWD:
27867754Smsmith					config.nispasswd = (q == NULL || !boolean_val(q, 1))
27967754Smsmith						? NULL : newstr(q);
28067754Smsmith					break;
28167754Smsmith				case _UC_DOTDIR:
28267754Smsmith					config.dotdir = (q == NULL || !boolean_val(q, 1))
283151937Sjkim						? NULL : newstr(q);
28467754Smsmith					break;
28567754Smsmith				case _UC_NEWMAIL:
28667754Smsmith					config.newmail = (q == NULL || !boolean_val(q, 1))
28767754Smsmith						? NULL : newstr(q);
28867754Smsmith					break;
289151937Sjkim				case _UC_LOGFILE:
29067754Smsmith					config.logfile = (q == NULL || !boolean_val(q, 1))
29199679Siwasaki						? NULL : newstr(q);
29267754Smsmith					break;
29367754Smsmith				case _UC_HOMEROOT:
29467754Smsmith					config.home = (q == NULL || !boolean_val(q, 1))
29567754Smsmith						? "/home" : newstr(q);
29667754Smsmith					break;
29767754Smsmith				case _UC_SHELLPATH:
29867754Smsmith					config.shelldir = (q == NULL || !boolean_val(q, 1))
29967754Smsmith						? "/bin" : newstr(q);
30091116Smsmith					break;
30167754Smsmith				case _UC_SHELLS:
30267754Smsmith					for (i = 0; i < _UC_MAXSHELLS && q != NULL; i++, q = strtok(NULL, toks))
30391116Smsmith						system_shells[i] = newstr(q);
30467754Smsmith					if (i > 0)
30567754Smsmith						while (i < _UC_MAXSHELLS)
30691116Smsmith							system_shells[i++] = NULL;
307240716Sjkim					break;
30867754Smsmith				case _UC_DEFAULTSHELL:
30967754Smsmith					config.shell_default = (q == NULL || !boolean_val(q, 1))
31067754Smsmith						? (char *) bourne_shell : newstr(q);
31167754Smsmith					break;
31291116Smsmith				case _UC_DEFAULTGROUP:
31367754Smsmith					q = unquote(q);
31467754Smsmith					config.default_group = (q == NULL || !boolean_val(q, 1) || GETGRNAM(q) == NULL)
31567754Smsmith						? NULL : newstr(q);
31667754Smsmith					break;
31767754Smsmith				case _UC_EXTRAGROUPS:
31867754Smsmith					for (i = 0; q != NULL; q = strtok(NULL, toks)) {
31991116Smsmith						if (extendarray(&config.groups, &config.numgroups, i + 2) != -1)
32067754Smsmith							config.groups[i++] = newstr(q);
32167754Smsmith					}
32267754Smsmith					if (i > 0)
32367754Smsmith						while (i < config.numgroups)
324151937Sjkim							config.groups[i++] = NULL;
325167802Sjkim					break;
326151937Sjkim				case _UC_DEFAULTCLASS:
327151937Sjkim					config.default_class = (q == NULL || !boolean_val(q, 1))
328151937Sjkim						? NULL : newstr(q);
329151937Sjkim					break;
330151937Sjkim				case _UC_MINUID:
331151937Sjkim					if ((q = unquote(q)) != NULL && isdigit(*q))
332151937Sjkim						config.min_uid = (uid_t) atol(q);
333151937Sjkim					break;
33467754Smsmith				case _UC_MAXUID:
33567754Smsmith					if ((q = unquote(q)) != NULL && isdigit(*q))
33682367Smsmith						config.max_uid = (uid_t) atol(q);
33782367Smsmith					break;
33882367Smsmith				case _UC_MINGID:
33982367Smsmith					if ((q = unquote(q)) != NULL && isdigit(*q))
34082367Smsmith						config.min_gid = (gid_t) atol(q);
34182367Smsmith					break;
34282367Smsmith				case _UC_MAXGID:
34382367Smsmith					if ((q = unquote(q)) != NULL && isdigit(*q))
34482367Smsmith						config.max_gid = (gid_t) atol(q);
34582367Smsmith					break;
34682367Smsmith				case _UC_EXPIRE:
34782367Smsmith					if ((q = unquote(q)) != NULL && isdigit(*q))
348151937Sjkim						config.expire_days = atoi(q);
34999679Siwasaki					break;
35099679Siwasaki				case _UC_PASSWORD:
35182367Smsmith					if ((q = unquote(q)) != NULL && isdigit(*q))
35282367Smsmith						config.password_days = atoi(q);
35382367Smsmith					break;
35482367Smsmith				case _UC_FIELDS:
35582367Smsmith				case _UC_NONE:
356151937Sjkim					break;
357151937Sjkim				}
358151937Sjkim			}
359151937Sjkim		}
36082367Smsmith		free(buf);
36182367Smsmith		fclose(fp);
36282367Smsmith	}
36382367Smsmith	return &config;
36482367Smsmith}
36582367Smsmith
36682367Smsmith
36782367Smsmithint
368114237Snjlwrite_userconfig(char const * file)
369114237Snjl{
370114237Snjl	int             fd;
371114237Snjl
372114237Snjl	if (file == NULL)
373114237Snjl		file = _PATH_PW_CONF;
374241973Sjkim
375114237Snjl	if ((fd = open(file, O_CREAT | O_RDWR | O_TRUNC | O_EXLOCK, 0644)) != -1) {
376114237Snjl		FILE           *fp;
377114237Snjl
378114237Snjl		if ((fp = fdopen(fd, "w")) == NULL)
379151937Sjkim			close(fd);
380114237Snjl		else {
381114237Snjl			int             i, j, k;
382114237Snjl			int		len = LNBUFSZ;
383114237Snjl			char           *buf = malloc(len);
384114237Snjl
385114237Snjl			for (i = _UC_NONE; i < _UC_FIELDS; i++) {
386114237Snjl				int             quote = 1;
387114237Snjl				char const     *val = buf;
388114237Snjl
389114237Snjl				*buf = '\0';
390114237Snjl				switch (i) {
391114237Snjl				case _UC_DEFAULTPWD:
392114237Snjl					val = boolean_str(config.default_password);
393114237Snjl					break;
394114237Snjl				case _UC_REUSEUID:
395114237Snjl					val = boolean_str(config.reuse_uids);
396114237Snjl					break;
397114237Snjl				case _UC_REUSEGID:
398114237Snjl					val = boolean_str(config.reuse_gids);
399114237Snjl					break;
400114237Snjl				case _UC_NISPASSWD:
401240716Sjkim					val = config.nispasswd ? config.nispasswd : "";
402114237Snjl					quote = 0;
403114237Snjl					break;
404114237Snjl				case _UC_DOTDIR:
405114237Snjl					val = config.dotdir ? config.dotdir : boolean_str(0);
406114237Snjl					break;
407114237Snjl				case _UC_NEWMAIL:
408114237Snjl					val = config.newmail ? config.newmail : boolean_str(0);
409114237Snjl					break;
410240716Sjkim				case _UC_LOGFILE:
411123315Snjl					val = config.logfile ? config.logfile : boolean_str(0);
412114237Snjl					break;
413114237Snjl				case _UC_HOMEROOT:
414114237Snjl					val = config.home;
415114237Snjl					break;
416114237Snjl				case _UC_SHELLPATH:
417114237Snjl					val = config.shelldir;
418114237Snjl					break;
419114237Snjl				case _UC_SHELLS:
42067754Smsmith					for (j = k = 0; j < _UC_MAXSHELLS && system_shells[j] != NULL; j++) {
42167754Smsmith						char	lbuf[64];
42267754Smsmith						int	l = snprintf(lbuf, sizeof lbuf, "%s\"%s\"", k ? "," : "", system_shells[j]);
42367754Smsmith						if (l < 0)
42467754Smsmith							l = 0;
42567754Smsmith						if (l + k + 1 < len || extendline(&buf, &len, len + LNBUFSZ) != -1) {
426151937Sjkim							strcpy(buf + k, lbuf);
42767754Smsmith							k += l;
428241973Sjkim						}
42967754Smsmith					}
43067754Smsmith					quote = 0;
43167754Smsmith					break;
43267754Smsmith				case _UC_DEFAULTSHELL:
43367754Smsmith					val = config.shell_default ? config.shell_default : bourne_shell;
43467754Smsmith					break;
435114237Snjl				case _UC_DEFAULTGROUP:
436114237Snjl					val = config.default_group ? config.default_group : "";
437222544Sjkim					break;
43867754Smsmith				case _UC_EXTRAGROUPS:
43967754Smsmith					extendarray(&config.groups, &config.numgroups, 200);
44067754Smsmith					for (j = k = 0; j < config.numgroups && config.groups[j] != NULL; j++) {
44169450Smsmith						char	lbuf[64];
442167802Sjkim						int	l = snprintf(lbuf, sizeof lbuf, "%s\"%s\"", k ? "," : "", config.groups[j]);
44369450Smsmith						if (l < 0)
44469450Smsmith							l = 0;
445102550Siwasaki						if (l + k + 1 < len || extendline(&buf, &len, len + 1024) != -1) {
44667754Smsmith							strcpy(buf + k, lbuf);
44767754Smsmith							k +=  l;
44867754Smsmith						}
44967754Smsmith					}
45067754Smsmith					quote = 0;
45167754Smsmith					break;
45282367Smsmith				case _UC_DEFAULTCLASS:
45369450Smsmith					val = config.default_class ? config.default_class : "";
45467754Smsmith					break;
455114237Snjl				case _UC_MINUID:
456114237Snjl					sprintf(buf, "%lu", (unsigned long) config.min_uid);
457151937Sjkim					quote = 0;
458199337Sjkim					break;
459114237Snjl				case _UC_MAXUID:
460114237Snjl					sprintf(buf, "%lu", (unsigned long) config.max_uid);
461114237Snjl					quote = 0;
462114237Snjl					break;
463167802Sjkim				case _UC_MINGID:
464167802Sjkim					sprintf(buf, "%lu", (unsigned long) config.min_gid);
465167802Sjkim					quote = 0;
466167802Sjkim					break;
467167802Sjkim				case _UC_MAXGID:
468167802Sjkim					sprintf(buf, "%lu", (unsigned long) config.max_gid);
469167802Sjkim					quote = 0;
470167802Sjkim					break;
471167802Sjkim				case _UC_EXPIRE:
472167802Sjkim					sprintf(buf, "%d", config.expire_days);
473167802Sjkim					quote = 0;
474114237Snjl					break;
475222544Sjkim				case _UC_PASSWORD:
476114237Snjl					sprintf(buf, "%d", config.password_days);
47767754Smsmith					quote = 0;
478114237Snjl					break;
479114237Snjl				case _UC_NONE:
480100966Siwasaki					break;
481114237Snjl				}
482239340Sjkim
483239340Sjkim				if (comments[i])
484239340Sjkim					fputs(comments[i], fp);
485239340Sjkim
486239340Sjkim				if (*kwds[i]) {
487245582Sjkim					if (quote)
488239340Sjkim						fprintf(fp, "%s = \"%s\"\n", kwds[i], val);
489245582Sjkim					else
490239340Sjkim						fprintf(fp, "%s = %s\n", kwds[i], val);
491167802Sjkim#if debugging
492114237Snjl					printf("WROTE: %s = %s\n", kwds[i], val);
49367754Smsmith#endif
49482367Smsmith				}
49582367Smsmith			}
49682367Smsmith			free(buf);
49782367Smsmith			return fclose(fp) != EOF;
498202771Sjkim		}
49967754Smsmith	}
500102550Siwasaki	return 0;
50169450Smsmith}
50267754Smsmith