pw_conf.c revision 285412
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 285412 2015-07-11 23:07:17Z 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 */
107285412Sbapt	0			/* Days until password expires */
10820253Sjoerg};
10920253Sjoerg
11020253Sjoergstatic char const *comments[_UC_FIELDS] =
11120253Sjoerg{
11220253Sjoerg	"#\n# pw.conf - user/group configuration defaults\n#\n",
11320253Sjoerg	"\n# Password for new users? no=nologin yes=loginid none=blank random=random\n",
11420253Sjoerg	"\n# Reuse gaps in uid sequence? (yes or no)\n",
11520253Sjoerg	"\n# Reuse gaps in gid sequence? (yes or no)\n",
11621330Sdavidn	"\n# Path to the NIS passwd file (blank or 'no' for none)\n",
11720253Sjoerg	"\n# Obtain default dotfiles from this directory\n",
11820253Sjoerg	"\n# Mail this file to new user (/etc/newuser.msg or no)\n",
11920253Sjoerg	"\n# Log add/change/remove information in this file\n",
12020253Sjoerg	"\n# Root directory in which $HOME directory is created\n",
121168044Sle	"\n# Mode for the new $HOME directory, will be modified by umask\n",
12220253Sjoerg	"\n# Colon separated list of directories containing valid shells\n",
12370133Sdougb	"\n# Comma separated list of available shells (without paths)\n",
12420253Sjoerg	"\n# Default shell (without path)\n",
12520253Sjoerg	"\n# Default group (leave blank for new group per user)\n",
12620253Sjoerg	"\n# Extra groups for new users\n",
12720253Sjoerg	"\n# Default login class for new users\n",
12820253Sjoerg	"\n# Range of valid default user ids\n",
12920253Sjoerg	NULL,
13020253Sjoerg	"\n# Range of valid default group ids\n",
13120253Sjoerg	NULL,
13220253Sjoerg	"\n# Days after which account expires (0=disabled)\n",
13320253Sjoerg	"\n# Days after which password expires (0=disabled)\n"
13420253Sjoerg};
13520253Sjoerg
13620253Sjoergstatic char const *kwds[] =
13720253Sjoerg{
13820253Sjoerg	"",
13920253Sjoerg	"defaultpasswd",
14020253Sjoerg	"reuseuids",
14120253Sjoerg	"reusegids",
14221330Sdavidn	"nispasswd",
14320253Sjoerg	"skeleton",
14420253Sjoerg	"newmail",
14520253Sjoerg	"logfile",
14620253Sjoerg	"home",
147168044Sle	"homemode",
14820253Sjoerg	"shellpath",
14920253Sjoerg	"shells",
15020253Sjoerg	"defaultshell",
15120253Sjoerg	"defaultgroup",
15220253Sjoerg	"extragroups",
15320253Sjoerg	"defaultclass",
15420253Sjoerg	"minuid",
15520253Sjoerg	"maxuid",
15620253Sjoerg	"mingid",
15720253Sjoerg	"maxgid",
15820253Sjoerg	"expire_days",
15920253Sjoerg	"password_days",
16020253Sjoerg	NULL
16120253Sjoerg};
16220253Sjoerg
16320253Sjoergstatic char    *
16420253Sjoergunquote(char const * str)
16520253Sjoerg{
16620253Sjoerg	if (str && (*str == '"' || *str == '\'')) {
16720253Sjoerg		char           *p = strchr(str + 1, *str);
16820253Sjoerg
16920253Sjoerg		if (p != NULL)
17020253Sjoerg			*p = '\0';
17120253Sjoerg		return (char *) (*++str ? str : NULL);
17220253Sjoerg	}
17320253Sjoerg	return (char *) str;
17420253Sjoerg}
17520253Sjoerg
17620253Sjoergint
17720253Sjoergboolean_val(char const * str, int dflt)
17820253Sjoerg{
17920253Sjoerg	if ((str = unquote(str)) != NULL) {
18020253Sjoerg		int             i;
18120253Sjoerg
18220253Sjoerg		for (i = 0; booltrue[i]; i++)
18320253Sjoerg			if (strcmp(str, booltrue[i]) == 0)
18420253Sjoerg				return 1;
18520253Sjoerg		for (i = 0; boolfalse[i]; i++)
18620253Sjoerg			if (strcmp(str, boolfalse[i]) == 0)
18720253Sjoerg				return 0;
18820253Sjoerg
18920253Sjoerg		/*
19020253Sjoerg		 * Special cases for defaultpassword
19120253Sjoerg		 */
19220253Sjoerg		if (strcmp(str, "random") == 0)
19320253Sjoerg			return -1;
19420253Sjoerg		if (strcmp(str, "none") == 0)
19520253Sjoerg			return -2;
19620253Sjoerg	}
19720253Sjoerg	return dflt;
19820253Sjoerg}
19920253Sjoerg
20020253Sjoergchar const     *
20120253Sjoergboolean_str(int val)
20220253Sjoerg{
20320253Sjoerg	if (val == -1)
20420253Sjoerg		return "random";
20520253Sjoerg	else if (val == -2)
20620253Sjoerg		return "none";
20720253Sjoerg	else
20820253Sjoerg		return val ? booltrue[0] : boolfalse[0];
20920253Sjoerg}
21020253Sjoerg
21120253Sjoergchar           *
21220253Sjoergnewstr(char const * p)
21320253Sjoerg{
214282718Sbapt	char	*q;
21520253Sjoerg
216282718Sbapt	if ((p = unquote(p)) == NULL)
217282718Sbapt		return (NULL);
21820253Sjoerg
219282719Sbapt	if ((q = strdup(p)) == NULL)
220282719Sbapt		err(1, "strdup()");
221282718Sbapt
222282718Sbapt	return (q);
22320253Sjoerg}
22420253Sjoerg
22520253Sjoergstruct userconf *
22620253Sjoergread_userconfig(char const * file)
22720253Sjoerg{
228264781Sbapt	FILE	*fp;
229264781Sbapt	char	*buf, *p;
230264781Sbapt	size_t	linecap;
231264781Sbapt	ssize_t	linelen;
23220253Sjoerg
233264781Sbapt	buf = NULL;
234264781Sbapt	linecap = 0;
235264781Sbapt
236285412Sbapt	config.groups = sl_init();
237282720Sbapt	if (config.groups == NULL)
238285412Sbapt		err(1, "sl_init()");
23920253Sjoerg	if (file == NULL)
24020253Sjoerg		file = _PATH_PW_CONF;
241264781Sbapt
242283815Sbapt	if ((fp = fopen(file, "r")) == NULL)
243283815Sbapt		return (&config);
24420747Sdavidn
245283815Sbapt	while ((linelen = getline(&buf, &linecap, fp)) > 0) {
246283815Sbapt		if (*buf && (p = strtok(buf, " \t\r\n=")) != NULL && *p != '#') {
247283815Sbapt			static char const toks[] = " \t\r\n,=";
248283815Sbapt			char           *q = strtok(NULL, toks);
249283815Sbapt			int             i = 0;
250283815Sbapt			mode_t          *modeset;
251283815Sbapt
252283815Sbapt			while (i < _UC_FIELDS && strcmp(p, kwds[i]) != 0)
253283815Sbapt				++i;
25420253Sjoerg#if debugging
255283815Sbapt			if (i == _UC_FIELDS)
256283815Sbapt				printf("Got unknown kwd `%s' val=`%s'\n", p, q ? q : "");
257283815Sbapt			else
258283815Sbapt				printf("Got kwd[%s]=%s\n", p, q);
25920253Sjoerg#endif
260283815Sbapt			switch (i) {
261283815Sbapt			case _UC_DEFAULTPWD:
262283815Sbapt				config.default_password = boolean_val(q, 1);
263283815Sbapt				break;
264283815Sbapt			case _UC_REUSEUID:
265283815Sbapt				config.reuse_uids = boolean_val(q, 0);
266283815Sbapt				break;
267283815Sbapt			case _UC_REUSEGID:
268283815Sbapt				config.reuse_gids = boolean_val(q, 0);
269283815Sbapt				break;
270283815Sbapt			case _UC_NISPASSWD:
271283815Sbapt				config.nispasswd = (q == NULL || !boolean_val(q, 1))
272283815Sbapt					? NULL : newstr(q);
273283815Sbapt				break;
274283815Sbapt			case _UC_DOTDIR:
275283815Sbapt				config.dotdir = (q == NULL || !boolean_val(q, 1))
276283815Sbapt					? NULL : newstr(q);
277283815Sbapt				break;
27820747Sdavidn				case _UC_NEWMAIL:
279283815Sbapt				config.newmail = (q == NULL || !boolean_val(q, 1))
280283815Sbapt					? NULL : newstr(q);
281283815Sbapt				break;
282283815Sbapt			case _UC_LOGFILE:
283283815Sbapt				config.logfile = (q == NULL || !boolean_val(q, 1))
284283815Sbapt					? NULL : newstr(q);
285283815Sbapt				break;
286283815Sbapt			case _UC_HOMEROOT:
287283815Sbapt				config.home = (q == NULL || !boolean_val(q, 1))
288283815Sbapt					? "/home" : newstr(q);
289283815Sbapt				break;
290283815Sbapt			case _UC_HOMEMODE:
291283815Sbapt				modeset = setmode(q);
292283815Sbapt				config.homemode = (q == NULL || !boolean_val(q, 1))
293283815Sbapt					? _DEF_DIRMODE : getmode(modeset, _DEF_DIRMODE);
294283815Sbapt				free(modeset);
295283815Sbapt				break;
296283815Sbapt			case _UC_SHELLPATH:
297283815Sbapt				config.shelldir = (q == NULL || !boolean_val(q, 1))
298283815Sbapt					? "/bin" : newstr(q);
299283815Sbapt				break;
300283815Sbapt			case _UC_SHELLS:
301283815Sbapt				for (i = 0; i < _UC_MAXSHELLS && q != NULL; i++, q = strtok(NULL, toks))
302283815Sbapt					system_shells[i] = newstr(q);
303283815Sbapt				if (i > 0)
304283815Sbapt					while (i < _UC_MAXSHELLS)
305283815Sbapt						system_shells[i++] = NULL;
306283815Sbapt				break;
307283815Sbapt			case _UC_DEFAULTSHELL:
308283815Sbapt				config.shell_default = (q == NULL || !boolean_val(q, 1))
309283815Sbapt					? (char *) bourne_shell : newstr(q);
310283815Sbapt				break;
311283815Sbapt			case _UC_DEFAULTGROUP:
312283815Sbapt				q = unquote(q);
313283815Sbapt				config.default_group = (q == NULL || !boolean_val(q, 1) || GETGRNAM(q) == NULL)
314283815Sbapt					? NULL : newstr(q);
315283815Sbapt				break;
316283815Sbapt			case _UC_EXTRAGROUPS:
317285412Sbapt				for (i = 0; q != NULL; q = strtok(NULL, toks))
318285412Sbapt					sl_add(config.groups, newstr(q));
319283815Sbapt				break;
320283815Sbapt			case _UC_DEFAULTCLASS:
321283815Sbapt				config.default_class = (q == NULL || !boolean_val(q, 1))
322283815Sbapt					? NULL : newstr(q);
323283815Sbapt				break;
324283815Sbapt			case _UC_MINUID:
325283815Sbapt				if ((q = unquote(q)) != NULL && isdigit(*q))
326283815Sbapt					config.min_uid = (uid_t) atol(q);
327283815Sbapt				break;
328283815Sbapt			case _UC_MAXUID:
329283815Sbapt				if ((q = unquote(q)) != NULL && isdigit(*q))
330283815Sbapt					config.max_uid = (uid_t) atol(q);
331283815Sbapt				break;
332283815Sbapt			case _UC_MINGID:
333283815Sbapt				if ((q = unquote(q)) != NULL && isdigit(*q))
334283815Sbapt					config.min_gid = (gid_t) atol(q);
335283815Sbapt				break;
336283815Sbapt			case _UC_MAXGID:
337283815Sbapt				if ((q = unquote(q)) != NULL && isdigit(*q))
338283815Sbapt					config.max_gid = (gid_t) atol(q);
339283815Sbapt				break;
340283815Sbapt			case _UC_EXPIRE:
341283815Sbapt				if ((q = unquote(q)) != NULL && isdigit(*q))
342283815Sbapt					config.expire_days = atoi(q);
343283815Sbapt				break;
344283815Sbapt			case _UC_PASSWORD:
345283815Sbapt				if ((q = unquote(q)) != NULL && isdigit(*q))
346283815Sbapt					config.password_days = atoi(q);
347283815Sbapt				break;
348283815Sbapt			case _UC_FIELDS:
349283815Sbapt			case _UC_NONE:
350283815Sbapt				break;
35120253Sjoerg			}
35220253Sjoerg		}
35320253Sjoerg	}
354283818Sbapt	free(buf);
355283818Sbapt	fclose(fp);
356283818Sbapt
357283815Sbapt	return (&config);
35820253Sjoerg}
35920253Sjoerg
36020253Sjoerg
36120253Sjoergint
36220253Sjoergwrite_userconfig(char const * file)
36320253Sjoerg{
36420253Sjoerg	int             fd;
365282697Sbapt	int             i, j;
366282681Sbapt	struct sbuf	*buf;
367282697Sbapt	FILE           *fp;
36820253Sjoerg
36920253Sjoerg	if (file == NULL)
37020253Sjoerg		file = _PATH_PW_CONF;
37120253Sjoerg
372282697Sbapt	if ((fd = open(file, O_CREAT|O_RDWR|O_TRUNC|O_EXLOCK, 0644)) == -1)
373282697Sbapt		return (0);
37420253Sjoerg
375282697Sbapt	if ((fp = fdopen(fd, "w")) == NULL) {
376282697Sbapt		close(fd);
377282697Sbapt		return (0);
378282697Sbapt	}
379282681Sbapt
380282697Sbapt	buf = sbuf_new_auto();
381282697Sbapt	for (i = _UC_NONE; i < _UC_FIELDS; i++) {
382282697Sbapt		int             quote = 1;
38320253Sjoerg
384282697Sbapt		sbuf_clear(buf);
385282697Sbapt		switch (i) {
386282697Sbapt		case _UC_DEFAULTPWD:
387282697Sbapt			sbuf_cat(buf, boolean_str(config.default_password));
388282697Sbapt			break;
389282697Sbapt		case _UC_REUSEUID:
390282697Sbapt			sbuf_cat(buf, boolean_str(config.reuse_uids));
391282697Sbapt			break;
392282697Sbapt		case _UC_REUSEGID:
393282697Sbapt			sbuf_cat(buf, boolean_str(config.reuse_gids));
394282697Sbapt			break;
395282697Sbapt		case _UC_NISPASSWD:
396282697Sbapt			sbuf_cat(buf, config.nispasswd ?  config.nispasswd :
397282697Sbapt			    "");
398282697Sbapt			quote = 0;
399282697Sbapt			break;
400282697Sbapt		case _UC_DOTDIR:
401282697Sbapt			sbuf_cat(buf, config.dotdir ?  config.dotdir :
402282697Sbapt			    boolean_str(0));
403282697Sbapt			break;
404282697Sbapt		case _UC_NEWMAIL:
405282697Sbapt			sbuf_cat(buf, config.newmail ?  config.newmail :
406282697Sbapt			    boolean_str(0));
407282697Sbapt			break;
408282697Sbapt		case _UC_LOGFILE:
409282697Sbapt			sbuf_cat(buf, config.logfile ?  config.logfile :
410282697Sbapt			    boolean_str(0));
411282697Sbapt			break;
412282697Sbapt		case _UC_HOMEROOT:
413282697Sbapt			sbuf_cat(buf, config.home);
414282697Sbapt			break;
415282697Sbapt		case _UC_HOMEMODE:
416282697Sbapt			sbuf_printf(buf, "%04o", config.homemode);
417282697Sbapt			quote = 0;
418282697Sbapt			break;
419282697Sbapt		case _UC_SHELLPATH:
420282697Sbapt			sbuf_cat(buf, config.shelldir);
421282697Sbapt			break;
422282697Sbapt		case _UC_SHELLS:
423282697Sbapt			for (j = 0; j < _UC_MAXSHELLS &&
424282697Sbapt			    system_shells[j] != NULL; j++)
425282697Sbapt				sbuf_printf(buf, "%s\"%s\"", j ?
426282697Sbapt				    "," : "", system_shells[j]);
427282697Sbapt			quote = 0;
428282697Sbapt			break;
429282697Sbapt		case _UC_DEFAULTSHELL:
430282697Sbapt			sbuf_cat(buf, config.shell_default ?
431282697Sbapt			    config.shell_default : bourne_shell);
432282697Sbapt			break;
433282697Sbapt		case _UC_DEFAULTGROUP:
434282697Sbapt			sbuf_cat(buf, config.default_group ?
435282697Sbapt			    config.default_group : "");
436282697Sbapt			break;
437282697Sbapt		case _UC_EXTRAGROUPS:
438285412Sbapt			for (j = 0; config.groups != NULL &&
439285412Sbapt			    j < (int)config.groups->sl_cur; j++)
440282697Sbapt				sbuf_printf(buf, "%s\"%s\"", j ?
441285412Sbapt				    "," : "", config.groups->sl_str[j]);
442282697Sbapt			quote = 0;
443282697Sbapt			break;
444282697Sbapt		case _UC_DEFAULTCLASS:
445282697Sbapt			sbuf_cat(buf, config.default_class ?
446282697Sbapt			    config.default_class : "");
447282697Sbapt			break;
448282697Sbapt		case _UC_MINUID:
449283842Sbapt			sbuf_printf(buf, "%u", config.min_uid);
450282697Sbapt			quote = 0;
451282697Sbapt			break;
452282697Sbapt		case _UC_MAXUID:
453283842Sbapt			sbuf_printf(buf, "%u", config.max_uid);
454282697Sbapt			quote = 0;
455282697Sbapt			break;
456282697Sbapt		case _UC_MINGID:
457283842Sbapt			sbuf_printf(buf, "%u", config.min_gid);
458282697Sbapt			quote = 0;
459282697Sbapt			break;
460282697Sbapt		case _UC_MAXGID:
461283842Sbapt			sbuf_printf(buf, "%u", config.max_gid);
462282697Sbapt			quote = 0;
463282697Sbapt			break;
464282697Sbapt		case _UC_EXPIRE:
465282697Sbapt			sbuf_printf(buf, "%d", config.expire_days);
466282697Sbapt			quote = 0;
467282697Sbapt			break;
468282697Sbapt		case _UC_PASSWORD:
469282697Sbapt			sbuf_printf(buf, "%d", config.password_days);
470282697Sbapt			quote = 0;
471282697Sbapt			break;
472282697Sbapt		case _UC_NONE:
473282697Sbapt			break;
474282697Sbapt		}
475282697Sbapt		sbuf_finish(buf);
47620253Sjoerg
477282697Sbapt		if (comments[i])
478282697Sbapt			fputs(comments[i], fp);
47920253Sjoerg
480282697Sbapt		if (*kwds[i]) {
481282697Sbapt			if (quote)
482282697Sbapt				fprintf(fp, "%s = \"%s\"\n", kwds[i],
483282697Sbapt				    sbuf_data(buf));
484282697Sbapt			else
485282697Sbapt				fprintf(fp, "%s = %s\n", kwds[i], sbuf_data(buf));
48620253Sjoerg#if debugging
487282697Sbapt			printf("WROTE: %s = %s\n", kwds[i], sbuf_data(buf));
48820253Sjoerg#endif
48920253Sjoerg		}
49020253Sjoerg	}
491282697Sbapt	sbuf_delete(buf);
492282697Sbapt	return (fclose(fp) != EOF);
49320253Sjoerg}
494