1156230Smux/*-
2156230Smux * Copyright (c) 2003-2006, Maxime Henrion <mux@FreeBSD.org>
3156230Smux * All rights reserved.
4156230Smux *
5156230Smux * Redistribution and use in source and binary forms, with or without
6156230Smux * modification, are permitted provided that the following conditions
7156230Smux * are met:
8156230Smux * 1. Redistributions of source code must retain the above copyright
9156230Smux *    notice, this list of conditions and the following disclaimer.
10156230Smux * 2. Redistributions in binary form must reproduce the above copyright
11156230Smux *    notice, this list of conditions and the following disclaimer in the
12156230Smux *    documentation and/or other materials provided with the distribution.
13156230Smux *
14156230Smux * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15156230Smux * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16156230Smux * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17156230Smux * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18156230Smux * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19156230Smux * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20156230Smux * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21156230Smux * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22156230Smux * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23156230Smux * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24156230Smux * SUCH DAMAGE.
25156230Smux *
26156230Smux * $FreeBSD$
27156230Smux */
28156230Smux
29156230Smux#include <sys/types.h>
30156230Smux#include <sys/stat.h>
31156230Smux
32156230Smux#include <errno.h>
33156230Smux#include <fcntl.h>
34156230Smux#include <stdio.h>
35156230Smux#include <stdlib.h>
36156230Smux#include <string.h>
37156230Smux#include <unistd.h>
38156230Smux
39156230Smux#include "config.h"
40156230Smux#include "globtree.h"
41156230Smux#include "keyword.h"
42156230Smux#include "misc.h"
43156230Smux#include "parse.h"
44156230Smux#include "stream.h"
45156230Smux#include "token.h"
46156230Smux
47156230Smuxstatic int		 config_parse_refusefiles(struct coll *);
48156230Smuxstatic int		 config_parse_refusefile(struct coll *, char *);
49156230Smux
50156230Smuxextern FILE *yyin;
51156230Smux
52156230Smux/* These are globals because I can't think of a better way with yacc. */
53156230Smuxstatic STAILQ_HEAD(, coll) colls;
54156230Smuxstatic struct coll *cur_coll;
55156230Smuxstatic struct coll *defaults;
56156701Smuxstatic struct coll *ovcoll;
57156701Smuxstatic int ovmask;
58156230Smuxstatic const char *cfgfile;
59156230Smux
60156230Smux/*
61156230Smux * Extract all the configuration information from the config
62156230Smux * file and some command line parameters.
63156230Smux */
64156230Smuxstruct config *
65156230Smuxconfig_init(const char *file, struct coll *override, int overridemask)
66156230Smux{
67156230Smux	struct config *config;
68156230Smux	struct coll *coll;
69156230Smux	size_t slen;
70156230Smux	char *prefix;
71156230Smux	int error;
72156230Smux	mode_t mask;
73156230Smux
74156230Smux	config = xmalloc(sizeof(struct config));
75156230Smux	memset(config, 0, sizeof(struct config));
76156230Smux	STAILQ_INIT(&colls);
77156230Smux
78156230Smux	defaults = coll_new(NULL);
79156230Smux	/* Set the default umask. */
80156230Smux	mask = umask(0);
81156230Smux	umask(mask);
82156230Smux	defaults->co_umask = mask;
83156701Smux	ovcoll = override;
84156701Smux	ovmask = overridemask;
85156230Smux
86156230Smux	/* Extract a list of collections from the configuration file. */
87156230Smux	cur_coll = coll_new(defaults);
88156230Smux	yyin = fopen(file, "r");
89156230Smux	if (yyin == NULL) {
90156701Smux		lprintf(-1, "Cannot open \"%s\": %s\n", file, strerror(errno));
91156230Smux		goto bad;
92156230Smux	}
93156230Smux	cfgfile = file;
94156230Smux	error = yyparse();
95156230Smux	fclose(yyin);
96156230Smux	if (error)
97156230Smux		goto bad;
98156230Smux
99156230Smux	memcpy(&config->colls, &colls, sizeof(colls));
100156230Smux	if (STAILQ_EMPTY(&config->colls)) {
101156230Smux		lprintf(-1, "Empty supfile\n");
102156230Smux		goto bad;
103156230Smux	}
104156230Smux
105156230Smux	/* Fixup the list of collections. */
106156230Smux	STAILQ_FOREACH(coll, &config->colls, co_next) {
107156230Smux 		if (coll->co_base == NULL)
108156230Smux			coll->co_base = xstrdup("/usr/local/etc/cvsup");
109156230Smux		if (coll->co_colldir == NULL)
110156230Smux			coll->co_colldir = "sup";
111156230Smux		if (coll->co_prefix == NULL) {
112156230Smux			coll->co_prefix = xstrdup(coll->co_base);
113156230Smux		/*
114156230Smux		 * If prefix is not an absolute pathname, it is
115156230Smux		 * interpreted relative to base.
116156230Smux		 */
117156230Smux		} else if (coll->co_prefix[0] != '/') {
118156230Smux			slen = strlen(coll->co_base);
119156230Smux			if (slen > 0 && coll->co_base[slen - 1] != '/')
120156230Smux				xasprintf(&prefix, "%s/%s", coll->co_base,
121156230Smux				    coll->co_prefix);
122156230Smux			else
123156230Smux				xasprintf(&prefix, "%s%s", coll->co_base,
124156230Smux				    coll->co_prefix);
125156230Smux			free(coll->co_prefix);
126156230Smux			coll->co_prefix = prefix;
127156230Smux		}
128156230Smux		coll->co_prefixlen = strlen(coll->co_prefix);
129156230Smux		/* Determine whether to checksum RCS files or not. */
130156230Smux		if (coll->co_options & CO_EXACTRCS)
131156230Smux			coll->co_options |= CO_CHECKRCS;
132156230Smux		else
133156230Smux			coll->co_options &= ~CO_CHECKRCS;
134156230Smux		/* In recent versions, we always try to set the file modes. */
135156230Smux		coll->co_options |= CO_SETMODE;
136156230Smux		coll->co_options |= CO_NORSYNC;
137156230Smux		error = config_parse_refusefiles(coll);
138156230Smux		if (error)
139156230Smux			goto bad;
140156230Smux	}
141156230Smux
142156230Smux	coll_free(cur_coll);
143156230Smux	coll_free(defaults);
144156230Smux	config->host = STAILQ_FIRST(&config->colls)->co_host;
145156230Smux	return (config);
146156230Smuxbad:
147156230Smux	coll_free(cur_coll);
148156230Smux	coll_free(defaults);
149156230Smux	config_free(config);
150156230Smux	return (NULL);
151156230Smux}
152156230Smux
153156230Smuxint
154156230Smuxconfig_checkcolls(struct config *config)
155156230Smux{
156156230Smux	char linkname[4];
157156230Smux	struct stat sb;
158156230Smux	struct coll *coll;
159156230Smux	int error, numvalid, ret;
160156230Smux
161156230Smux	numvalid = 0;
162156230Smux	STAILQ_FOREACH(coll, &config->colls, co_next) {
163156230Smux		error = stat(coll->co_prefix, &sb);
164156230Smux		if (error || !S_ISDIR(sb.st_mode)) {
165156230Smux			/* Skip this collection, and warn about it unless its
166156230Smux			   prefix is a symbolic link pointing to "SKIP". */
167156230Smux			coll->co_options |= CO_SKIP;
168156230Smux			ret = readlink(coll->co_prefix, linkname,
169156230Smux			    sizeof(linkname));
170156230Smux			if (ret != 4 || memcmp(linkname, "SKIP", 4) != 0) {
171156230Smux				lprintf(-1,"Nonexistent prefix \"%s\" for "
172156230Smux				    "%s/%s\n", coll->co_prefix, coll->co_name,
173156230Smux				    coll->co_release);
174156230Smux			}
175156230Smux			continue;
176156230Smux		}
177156230Smux		numvalid++;
178156230Smux	}
179156230Smux	return (numvalid);
180156230Smux}
181156230Smux
182156230Smuxstatic int
183156230Smuxconfig_parse_refusefiles(struct coll *coll)
184156230Smux{
185156230Smux	char *collstem, *suffix, *supdir, *path;
186156230Smux	int error;
187156230Smux
188156230Smux	if (coll->co_colldir[0] == '/')
189156230Smux		supdir = xstrdup(coll->co_colldir);
190156230Smux	else
191156230Smux		xasprintf(&supdir, "%s/%s", coll->co_base, coll->co_colldir);
192156230Smux
193156230Smux	/* First, the global refuse file that applies to all collections. */
194156230Smux	xasprintf(&path, "%s/refuse", supdir);
195156230Smux	error = config_parse_refusefile(coll, path);
196156230Smux	free(path);
197156230Smux	if (error) {
198156230Smux		free(supdir);
199156230Smux		return (error);
200156230Smux	}
201156230Smux
202156230Smux	/* Next the per-collection refuse files that applies to all release/tag
203156230Smux	   combinations. */
204156230Smux	xasprintf(&collstem, "%s/%s/refuse", supdir, coll->co_name);
205156230Smux	free(supdir);
206156230Smux	error = config_parse_refusefile(coll, collstem);
207156230Smux	if (error) {
208156230Smux		free(collstem);
209156230Smux		return (error);
210156230Smux	}
211156230Smux
212156230Smux	/* Finally, the per-release and per-tag refuse file. */
213156230Smux	suffix = coll_statussuffix(coll);
214156230Smux	if (suffix != NULL) {
215156230Smux		xasprintf(&path, "%s%s", collstem, suffix);
216156230Smux		free(suffix);
217156230Smux		error = config_parse_refusefile(coll, path);
218156230Smux		free(path);
219156230Smux	}
220156230Smux	free(collstem);
221156230Smux	return (error);
222156230Smux}
223156230Smux
224156230Smux/*
225156230Smux * Parses a "refuse" file, and records the relevant information in
226156230Smux * coll->co_refusals.  If the file does not exist, it is silently
227156230Smux * ignored.
228156230Smux */
229156230Smuxstatic int
230156230Smuxconfig_parse_refusefile(struct coll *coll, char *path)
231156230Smux{
232156230Smux	struct stream *rd;
233156701Smux	char *cp, *line, *pat;
234156230Smux
235156230Smux	rd = stream_open_file(path, O_RDONLY);
236156230Smux	if (rd == NULL)
237156230Smux		return (0);
238156701Smux	while ((line = stream_getln(rd, NULL)) != NULL) {
239156701Smux		pat = line;
240156701Smux		for (;;) {
241156701Smux			/* Trim leading whitespace. */
242156701Smux			pat += strspn(pat, " \t");
243156701Smux			if (pat[0] == '\0')
244156701Smux				break;
245156701Smux			cp = strpbrk(pat, " \t");
246156701Smux			if (cp != NULL)
247156701Smux				*cp = '\0';
248156701Smux			pattlist_add(coll->co_refusals, pat);
249156701Smux			if (cp == NULL)
250156701Smux				break;
251156701Smux			pat = cp + 1;
252156701Smux		}
253156701Smux	}
254156230Smux	if (!stream_eof(rd)) {
255156230Smux		stream_close(rd);
256156230Smux		lprintf(-1, "Read failure from \"%s\": %s\n", path,
257156230Smux		    strerror(errno));
258156230Smux		return (-1);
259156230Smux	}
260156230Smux	stream_close(rd);
261156230Smux	return (0);
262156230Smux}
263156230Smux
264156230Smuxvoid
265156230Smuxconfig_free(struct config *config)
266156230Smux{
267156230Smux	struct coll *coll;
268156230Smux
269156230Smux	while (!STAILQ_EMPTY(&config->colls)) {
270156230Smux		coll = STAILQ_FIRST(&config->colls);
271156230Smux		STAILQ_REMOVE_HEAD(&config->colls, co_next);
272156230Smux		coll_free(coll);
273156230Smux	}
274156230Smux	if (config->server != NULL)
275156230Smux		stream_close(config->server);
276156230Smux	if (config->laddr != NULL)
277156230Smux		free(config->laddr);
278156230Smux	free(config);
279156230Smux}
280156230Smux
281156230Smux/* Create a new collection, inheriting options from the default collection. */
282156230Smuxstruct coll *
283156230Smuxcoll_new(struct coll *def)
284156230Smux{
285156230Smux	struct coll *new;
286156230Smux
287156230Smux	new = xmalloc(sizeof(struct coll));
288156230Smux	memset(new, 0, sizeof(struct coll));
289156230Smux	if (def != NULL) {
290156230Smux		new->co_options = def->co_options;
291156230Smux		new->co_umask = def->co_umask;
292156230Smux		if (def->co_host != NULL)
293156230Smux			new->co_host = xstrdup(def->co_host);
294156230Smux		if (def->co_base != NULL)
295156230Smux			new->co_base = xstrdup(def->co_base);
296156230Smux		if (def->co_date != NULL)
297156230Smux			new->co_date = xstrdup(def->co_date);
298156230Smux		if (def->co_prefix != NULL)
299156230Smux			new->co_prefix = xstrdup(def->co_prefix);
300156230Smux		if (def->co_release != NULL)
301156230Smux			new->co_release = xstrdup(def->co_release);
302156230Smux		if (def->co_tag != NULL)
303156230Smux			new->co_tag = xstrdup(def->co_tag);
304156230Smux		if (def->co_listsuffix != NULL)
305156230Smux			new->co_listsuffix = xstrdup(def->co_listsuffix);
306156230Smux	} else {
307156230Smux		new->co_tag = xstrdup(".");
308156230Smux		new->co_date = xstrdup(".");
309156230Smux	}
310156230Smux	new->co_keyword = keyword_new();
311156230Smux	new->co_accepts = pattlist_new();
312156230Smux	new->co_refusals = pattlist_new();
313156230Smux	new->co_attrignore = FA_DEV | FA_INODE;
314156230Smux	return (new);
315156230Smux}
316156230Smux
317156230Smuxvoid
318156230Smuxcoll_override(struct coll *coll, struct coll *from, int mask)
319156230Smux{
320156230Smux	size_t i;
321156230Smux	int newoptions, oldoptions;
322156230Smux
323156230Smux	newoptions = from->co_options & mask;
324156230Smux	oldoptions = coll->co_options & (CO_MASK & ~mask);
325156230Smux
326156230Smux	if (from->co_release != NULL) {
327156230Smux		if (coll->co_release != NULL)
328156230Smux			free(coll->co_release);
329156230Smux		coll->co_release = xstrdup(from->co_release);
330156230Smux	}
331156230Smux	if (from->co_host != NULL) {
332156230Smux		if (coll->co_host != NULL)
333156230Smux			free(coll->co_host);
334156230Smux		coll->co_host = xstrdup(from->co_host);
335156230Smux	}
336156230Smux	if (from->co_base != NULL) {
337156230Smux		if (coll->co_base != NULL)
338156230Smux			free(coll->co_base);
339156230Smux		coll->co_base = xstrdup(from->co_base);
340156230Smux	}
341156230Smux	if (from->co_colldir != NULL)
342156230Smux		coll->co_colldir = from->co_colldir;
343156230Smux	if (from->co_prefix != NULL) {
344156230Smux		if (coll->co_prefix != NULL)
345156230Smux			free(coll->co_prefix);
346156230Smux		coll->co_prefix = xstrdup(from->co_prefix);
347156230Smux	}
348156230Smux	if (newoptions & CO_CHECKOUTMODE) {
349156230Smux		if (from->co_tag != NULL) {
350156230Smux			if (coll->co_tag != NULL)
351156230Smux				free(coll->co_tag);
352156230Smux			coll->co_tag = xstrdup(from->co_tag);
353156230Smux		}
354156230Smux		if (from->co_date != NULL) {
355156230Smux			if (coll->co_date != NULL)
356156230Smux				free(coll->co_date);
357156230Smux			coll->co_date = xstrdup(from->co_date);
358156230Smux		}
359156230Smux	}
360156230Smux	if (from->co_listsuffix != NULL) {
361156230Smux		if (coll->co_listsuffix != NULL)
362156230Smux			free(coll->co_listsuffix);
363156230Smux		coll->co_listsuffix = xstrdup(from->co_listsuffix);
364156230Smux	}
365156230Smux	for (i = 0; i < pattlist_size(from->co_accepts); i++) {
366156230Smux		pattlist_add(coll->co_accepts,
367156230Smux		    pattlist_get(from->co_accepts, i));
368156230Smux	}
369156230Smux	for (i = 0; i < pattlist_size(from->co_refusals); i++) {
370156230Smux		pattlist_add(coll->co_refusals,
371156230Smux		    pattlist_get(from->co_refusals, i));
372156230Smux	}
373156230Smux	coll->co_options = oldoptions | newoptions;
374156230Smux}
375156230Smux
376156230Smuxchar *
377156230Smuxcoll_statussuffix(struct coll *coll)
378156230Smux{
379156230Smux	const char *tag;
380156230Smux	char *suffix;
381156230Smux
382156230Smux	if (coll->co_listsuffix != NULL) {
383156230Smux		xasprintf(&suffix, ".%s", coll->co_listsuffix);
384156230Smux	} else if (coll->co_options & CO_USERELSUFFIX) {
385156230Smux		if (coll->co_tag == NULL)
386156230Smux			tag = ".";
387156230Smux		else
388156230Smux			tag = coll->co_tag;
389156230Smux		if (coll->co_release != NULL) {
390156230Smux			if (coll->co_options & CO_CHECKOUTMODE) {
391156230Smux				xasprintf(&suffix, ".%s:%s",
392156230Smux				    coll->co_release, tag);
393156230Smux			} else {
394156230Smux				xasprintf(&suffix, ".%s", coll->co_release);
395156230Smux			}
396156230Smux		} else if (coll->co_options & CO_CHECKOUTMODE) {
397156230Smux			xasprintf(&suffix, ":%s", tag);
398156230Smux		}
399156230Smux	} else
400156230Smux		suffix = NULL;
401156230Smux	return (suffix);
402156230Smux}
403156230Smux
404156230Smuxchar *
405156230Smuxcoll_statuspath(struct coll *coll)
406156230Smux{
407156230Smux	char *path, *suffix;
408156230Smux
409156230Smux	suffix = coll_statussuffix(coll);
410156230Smux	if (suffix != NULL) {
411156230Smux		if (coll->co_colldir[0] == '/')
412156230Smux			xasprintf(&path, "%s/%s/checkouts%s", coll->co_colldir,
413156230Smux			    coll->co_name, suffix);
414156230Smux		else
415156230Smux			xasprintf(&path, "%s/%s/%s/checkouts%s", coll->co_base,
416156230Smux			    coll->co_colldir, coll->co_name, suffix);
417156230Smux	} else {
418156230Smux		if (coll->co_colldir[0] == '/')
419156230Smux			xasprintf(&path, "%s/%s/checkouts", coll->co_colldir,
420156230Smux			    coll->co_name);
421156230Smux		else
422156230Smux			xasprintf(&path, "%s/%s/%s/checkouts", coll->co_base,
423156230Smux			    coll->co_colldir, coll->co_name);
424156230Smux	}
425156230Smux	free(suffix);
426156230Smux	return (path);
427156230Smux}
428156230Smux
429156230Smuxvoid
430156230Smuxcoll_add(char *name)
431156230Smux{
432156230Smux	struct coll *coll;
433156230Smux
434156230Smux	cur_coll->co_name = name;
435156701Smux	coll_override(cur_coll, ovcoll, ovmask);
436156230Smux	if (cur_coll->co_release == NULL) {
437156230Smux		lprintf(-1, "Release not specified for collection "
438156230Smux		    "\"%s\"\n", cur_coll->co_name);
439156230Smux		exit(1);
440156230Smux	}
441156230Smux	if (cur_coll->co_host == NULL) {
442156230Smux		lprintf(-1, "Host not specified for collection "
443156230Smux		    "\"%s\"\n", cur_coll->co_name);
444156230Smux		exit(1);
445156230Smux	}
446156230Smux	if (!STAILQ_EMPTY(&colls)) {
447156230Smux		coll = STAILQ_LAST(&colls, coll, co_next);
448156230Smux		if (strcmp(coll->co_host, cur_coll->co_host) != 0) {
449156230Smux			lprintf(-1, "All \"host\" fields in the supfile "
450156230Smux			    "must be the same\n");
451156230Smux			exit(1);
452156230Smux		}
453156230Smux	}
454156230Smux	STAILQ_INSERT_TAIL(&colls, cur_coll, co_next);
455156230Smux	cur_coll = coll_new(defaults);
456156230Smux}
457156230Smux
458156230Smuxvoid
459156230Smuxcoll_free(struct coll *coll)
460156230Smux{
461156230Smux
462156230Smux	if (coll == NULL)
463156230Smux		return;
464156230Smux	if (coll->co_host != NULL)
465156230Smux		free(coll->co_host);
466156230Smux	if (coll->co_base != NULL)
467156230Smux		free(coll->co_base);
468156230Smux	if (coll->co_date != NULL)
469156230Smux		free(coll->co_date);
470156230Smux	if (coll->co_prefix != NULL)
471156230Smux		free(coll->co_prefix);
472156230Smux	if (coll->co_release != NULL)
473156230Smux		free(coll->co_release);
474156230Smux	if (coll->co_tag != NULL)
475156230Smux		free(coll->co_tag);
476156230Smux	if (coll->co_cvsroot != NULL)
477156230Smux		free(coll->co_cvsroot);
478156230Smux	if (coll->co_name != NULL)
479156230Smux		free(coll->co_name);
480156230Smux	if (coll->co_listsuffix != NULL)
481156230Smux		free(coll->co_listsuffix);
482156230Smux	keyword_free(coll->co_keyword);
483156230Smux	if (coll->co_dirfilter != NULL)
484156230Smux		globtree_free(coll->co_dirfilter);
485156230Smux	if (coll->co_dirfilter != NULL)
486156230Smux		globtree_free(coll->co_filefilter);
487156701Smux	if (coll->co_norsync != NULL)
488156701Smux		globtree_free(coll->co_norsync);
489156230Smux	if (coll->co_accepts != NULL)
490156230Smux		pattlist_free(coll->co_accepts);
491156230Smux	if (coll->co_refusals != NULL)
492156230Smux		pattlist_free(coll->co_refusals);
493156230Smux	free(coll);
494156230Smux}
495156230Smux
496156230Smuxvoid
497156230Smuxcoll_setopt(int opt, char *value)
498156230Smux{
499156230Smux	struct coll *coll;
500156701Smux	int error, mask;
501156230Smux
502156230Smux	coll = cur_coll;
503156230Smux	switch (opt) {
504156230Smux	case PT_HOST:
505156230Smux		if (coll->co_host != NULL)
506156230Smux			free(coll->co_host);
507156230Smux		coll->co_host = value;
508156230Smux		break;
509156230Smux	case PT_BASE:
510156230Smux		if (coll->co_base != NULL)
511156230Smux			free(coll->co_base);
512156230Smux		coll->co_base = value;
513156230Smux		break;
514156230Smux	case PT_DATE:
515156230Smux		if (coll->co_date != NULL)
516156230Smux			free(coll->co_date);
517156230Smux		coll->co_date = value;
518156230Smux		coll->co_options |= CO_CHECKOUTMODE;
519156230Smux		break;
520156230Smux	case PT_PREFIX:
521156230Smux		if (coll->co_prefix != NULL)
522156230Smux			free(coll->co_prefix);
523156230Smux		coll->co_prefix = value;
524156230Smux		break;
525156230Smux	case PT_RELEASE:
526156230Smux		if (coll->co_release != NULL)
527156230Smux			free(coll->co_release);
528156230Smux		coll->co_release = value;
529156230Smux		break;
530156230Smux	case PT_TAG:
531156230Smux		if (coll->co_tag != NULL)
532156230Smux			free(coll->co_tag);
533156230Smux		coll->co_tag = value;
534156230Smux		coll->co_options |= CO_CHECKOUTMODE;
535156230Smux		break;
536156230Smux	case PT_LIST:
537156230Smux		if (strchr(value, '/') != NULL) {
538156230Smux			lprintf(-1, "Parse error in \"%s\": \"list\" suffix "
539156230Smux			    "must not contain slashes\n", cfgfile);
540156230Smux			exit(1);
541156230Smux		}
542156230Smux		if (coll->co_listsuffix != NULL)
543156230Smux			free(coll->co_listsuffix);
544156230Smux		coll->co_listsuffix = value;
545156230Smux		break;
546156230Smux	case PT_UMASK:
547156701Smux		error = asciitoint(value, &mask, 8);
548156230Smux		free(value);
549156701Smux		if (error) {
550156230Smux			lprintf(-1, "Parse error in \"%s\": Invalid "
551156230Smux			    "umask value\n", cfgfile);
552156230Smux			exit(1);
553156230Smux		}
554156701Smux		coll->co_umask = mask;
555156230Smux		break;
556156230Smux	case PT_USE_REL_SUFFIX:
557156230Smux		coll->co_options |= CO_USERELSUFFIX;
558156230Smux		break;
559156230Smux	case PT_DELETE:
560156230Smux		coll->co_options |= CO_DELETE | CO_EXACTRCS;
561156230Smux		break;
562156230Smux	case PT_COMPRESS:
563156230Smux		coll->co_options |= CO_COMPRESS;
564156230Smux		break;
565156701Smux	case PT_NORSYNC:
566156701Smux		coll->co_options |= CO_NORSYNC;
567156701Smux		break;
568156230Smux	}
569156230Smux}
570156230Smux
571156230Smux/* Set "coll" as being the default collection. */
572156230Smuxvoid
573156230Smuxcoll_setdef(void)
574156230Smux{
575156230Smux
576156230Smux	coll_free(defaults);
577156230Smux	defaults = cur_coll;
578156230Smux	cur_coll = coll_new(defaults);
579156230Smux}
580