config.c revision 156230
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: vendor/csup/dist/contrib/csup/config.c 156230 2006-03-03 04:11:29Z mux $
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;
56156230Smuxstatic const char *cfgfile;
57156230Smux
58156230Smux/*
59156230Smux * Extract all the configuration information from the config
60156230Smux * file and some command line parameters.
61156230Smux */
62156230Smuxstruct config *
63156230Smuxconfig_init(const char *file, struct coll *override, int overridemask)
64156230Smux{
65156230Smux	struct config *config;
66156230Smux	struct coll *coll;
67156230Smux	size_t slen;
68156230Smux	char *prefix;
69156230Smux	int error;
70156230Smux	mode_t mask;
71156230Smux
72156230Smux	config = xmalloc(sizeof(struct config));
73156230Smux	memset(config, 0, sizeof(struct config));
74156230Smux	STAILQ_INIT(&colls);
75156230Smux
76156230Smux	defaults = coll_new(NULL);
77156230Smux	/* Set the default umask. */
78156230Smux	mask = umask(0);
79156230Smux	umask(mask);
80156230Smux	defaults->co_umask = mask;
81156230Smux
82156230Smux	/* Extract a list of collections from the configuration file. */
83156230Smux	cur_coll = coll_new(defaults);
84156230Smux	yyin = fopen(file, "r");
85156230Smux	if (yyin == NULL) {
86156230Smux		lprintf(-1, "Cannot open \"%s\": %s\n", file,
87156230Smux		    strerror(errno));
88156230Smux		goto bad;
89156230Smux	}
90156230Smux	cfgfile = file;
91156230Smux	error = yyparse();
92156230Smux	fclose(yyin);
93156230Smux	if (error)
94156230Smux		goto bad;
95156230Smux
96156230Smux	memcpy(&config->colls, &colls, sizeof(colls));
97156230Smux	if (STAILQ_EMPTY(&config->colls)) {
98156230Smux		lprintf(-1, "Empty supfile\n");
99156230Smux		goto bad;
100156230Smux	}
101156230Smux
102156230Smux	/* Fixup the list of collections. */
103156230Smux	STAILQ_FOREACH(coll, &config->colls, co_next) {
104156230Smux		coll_override(coll, override, overridemask);
105156230Smux 		if (coll->co_base == NULL)
106156230Smux			coll->co_base = xstrdup("/usr/local/etc/cvsup");
107156230Smux		if (coll->co_colldir == NULL)
108156230Smux			coll->co_colldir = "sup";
109156230Smux		if (coll->co_prefix == NULL) {
110156230Smux			coll->co_prefix = xstrdup(coll->co_base);
111156230Smux		/*
112156230Smux		 * If prefix is not an absolute pathname, it is
113156230Smux		 * interpreted relative to base.
114156230Smux		 */
115156230Smux		} else if (coll->co_prefix[0] != '/') {
116156230Smux			slen = strlen(coll->co_base);
117156230Smux			if (slen > 0 && coll->co_base[slen - 1] != '/')
118156230Smux				xasprintf(&prefix, "%s/%s", coll->co_base,
119156230Smux				    coll->co_prefix);
120156230Smux			else
121156230Smux				xasprintf(&prefix, "%s%s", coll->co_base,
122156230Smux				    coll->co_prefix);
123156230Smux			free(coll->co_prefix);
124156230Smux			coll->co_prefix = prefix;
125156230Smux		}
126156230Smux		coll->co_prefixlen = strlen(coll->co_prefix);
127156230Smux		/* Determine whether to checksum RCS files or not. */
128156230Smux		if (coll->co_options & CO_EXACTRCS)
129156230Smux			coll->co_options |= CO_CHECKRCS;
130156230Smux		else
131156230Smux			coll->co_options &= ~CO_CHECKRCS;
132156230Smux		/* In recent versions, we always try to set the file modes. */
133156230Smux		coll->co_options |= CO_SETMODE;
134156230Smux		/* XXX We don't support the rsync updating algorithm yet. */
135156230Smux		coll->co_options |= CO_NORSYNC;
136156230Smux		error = config_parse_refusefiles(coll);
137156230Smux		if (error)
138156230Smux			goto bad;
139156230Smux	}
140156230Smux
141156230Smux	coll_free(cur_coll);
142156230Smux	coll_free(defaults);
143156230Smux	config->host = STAILQ_FIRST(&config->colls)->co_host;
144156230Smux	return (config);
145156230Smuxbad:
146156230Smux	coll_free(cur_coll);
147156230Smux	coll_free(defaults);
148156230Smux	config_free(config);
149156230Smux	return (NULL);
150156230Smux}
151156230Smux
152156230Smuxint
153156230Smuxconfig_checkcolls(struct config *config)
154156230Smux{
155156230Smux	char linkname[4];
156156230Smux	struct stat sb;
157156230Smux	struct coll *coll;
158156230Smux	int error, numvalid, ret;
159156230Smux
160156230Smux	numvalid = 0;
161156230Smux	STAILQ_FOREACH(coll, &config->colls, co_next) {
162156230Smux		error = stat(coll->co_prefix, &sb);
163156230Smux		if (error || !S_ISDIR(sb.st_mode)) {
164156230Smux			/* Skip this collection, and warn about it unless its
165156230Smux			   prefix is a symbolic link pointing to "SKIP". */
166156230Smux			coll->co_options |= CO_SKIP;
167156230Smux			ret = readlink(coll->co_prefix, linkname,
168156230Smux			    sizeof(linkname));
169156230Smux			if (ret != 4 || memcmp(linkname, "SKIP", 4) != 0) {
170156230Smux				lprintf(-1,"Nonexistent prefix \"%s\" for "
171156230Smux				    "%s/%s\n", coll->co_prefix, coll->co_name,
172156230Smux				    coll->co_release);
173156230Smux			}
174156230Smux			continue;
175156230Smux		}
176156230Smux		numvalid++;
177156230Smux	}
178156230Smux	return (numvalid);
179156230Smux}
180156230Smux
181156230Smuxstatic int
182156230Smuxconfig_parse_refusefiles(struct coll *coll)
183156230Smux{
184156230Smux	char *collstem, *suffix, *supdir, *path;
185156230Smux	int error;
186156230Smux
187156230Smux	if (coll->co_colldir[0] == '/')
188156230Smux		supdir = xstrdup(coll->co_colldir);
189156230Smux	else
190156230Smux		xasprintf(&supdir, "%s/%s", coll->co_base, coll->co_colldir);
191156230Smux
192156230Smux	/* First, the global refuse file that applies to all collections. */
193156230Smux	xasprintf(&path, "%s/refuse", supdir);
194156230Smux	error = config_parse_refusefile(coll, path);
195156230Smux	free(path);
196156230Smux	if (error) {
197156230Smux		free(supdir);
198156230Smux		return (error);
199156230Smux	}
200156230Smux
201156230Smux	/* Next the per-collection refuse files that applies to all release/tag
202156230Smux	   combinations. */
203156230Smux	xasprintf(&collstem, "%s/%s/refuse", supdir, coll->co_name);
204156230Smux	free(supdir);
205156230Smux	error = config_parse_refusefile(coll, collstem);
206156230Smux	if (error) {
207156230Smux		free(collstem);
208156230Smux		return (error);
209156230Smux	}
210156230Smux
211156230Smux	/* Finally, the per-release and per-tag refuse file. */
212156230Smux	suffix = coll_statussuffix(coll);
213156230Smux	if (suffix != NULL) {
214156230Smux		xasprintf(&path, "%s%s", collstem, suffix);
215156230Smux		free(suffix);
216156230Smux		error = config_parse_refusefile(coll, path);
217156230Smux		free(path);
218156230Smux	}
219156230Smux	free(collstem);
220156230Smux	return (error);
221156230Smux}
222156230Smux
223156230Smux/*
224156230Smux * Parses a "refuse" file, and records the relevant information in
225156230Smux * coll->co_refusals.  If the file does not exist, it is silently
226156230Smux * ignored.
227156230Smux */
228156230Smuxstatic int
229156230Smuxconfig_parse_refusefile(struct coll *coll, char *path)
230156230Smux{
231156230Smux	struct stream *rd;
232156230Smux	char *line;
233156230Smux
234156230Smux	rd = stream_open_file(path, O_RDONLY);
235156230Smux	if (rd == NULL)
236156230Smux		return (0);
237156230Smux	while ((line = stream_getln(rd, NULL)) != NULL)
238156230Smux		pattlist_add(coll->co_refusals, line);
239156230Smux	if (!stream_eof(rd)) {
240156230Smux		stream_close(rd);
241156230Smux		lprintf(-1, "Read failure from \"%s\": %s\n", path,
242156230Smux		    strerror(errno));
243156230Smux		return (-1);
244156230Smux	}
245156230Smux	stream_close(rd);
246156230Smux	return (0);
247156230Smux}
248156230Smux
249156230Smuxvoid
250156230Smuxconfig_free(struct config *config)
251156230Smux{
252156230Smux	struct coll *coll;
253156230Smux
254156230Smux	while (!STAILQ_EMPTY(&config->colls)) {
255156230Smux		coll = STAILQ_FIRST(&config->colls);
256156230Smux		STAILQ_REMOVE_HEAD(&config->colls, co_next);
257156230Smux		coll_free(coll);
258156230Smux	}
259156230Smux	if (config->server != NULL)
260156230Smux		stream_close(config->server);
261156230Smux	if (config->laddr != NULL)
262156230Smux		free(config->laddr);
263156230Smux	free(config);
264156230Smux}
265156230Smux
266156230Smux/* Create a new collection, inheriting options from the default collection. */
267156230Smuxstruct coll *
268156230Smuxcoll_new(struct coll *def)
269156230Smux{
270156230Smux	struct coll *new;
271156230Smux
272156230Smux	new = xmalloc(sizeof(struct coll));
273156230Smux	memset(new, 0, sizeof(struct coll));
274156230Smux	if (def != NULL) {
275156230Smux		new->co_options = def->co_options;
276156230Smux		new->co_umask = def->co_umask;
277156230Smux		if (def->co_host != NULL)
278156230Smux			new->co_host = xstrdup(def->co_host);
279156230Smux		if (def->co_base != NULL)
280156230Smux			new->co_base = xstrdup(def->co_base);
281156230Smux		if (def->co_date != NULL)
282156230Smux			new->co_date = xstrdup(def->co_date);
283156230Smux		if (def->co_prefix != NULL)
284156230Smux			new->co_prefix = xstrdup(def->co_prefix);
285156230Smux		if (def->co_release != NULL)
286156230Smux			new->co_release = xstrdup(def->co_release);
287156230Smux		if (def->co_tag != NULL)
288156230Smux			new->co_tag = xstrdup(def->co_tag);
289156230Smux		if (def->co_listsuffix != NULL)
290156230Smux			new->co_listsuffix = xstrdup(def->co_listsuffix);
291156230Smux	} else {
292156230Smux		new->co_tag = xstrdup(".");
293156230Smux		new->co_date = xstrdup(".");
294156230Smux	}
295156230Smux	new->co_keyword = keyword_new();
296156230Smux	new->co_accepts = pattlist_new();
297156230Smux	new->co_refusals = pattlist_new();
298156230Smux	new->co_attrignore = FA_DEV | FA_INODE;
299156230Smux	return (new);
300156230Smux}
301156230Smux
302156230Smuxvoid
303156230Smuxcoll_override(struct coll *coll, struct coll *from, int mask)
304156230Smux{
305156230Smux	size_t i;
306156230Smux	int newoptions, oldoptions;
307156230Smux
308156230Smux	newoptions = from->co_options & mask;
309156230Smux	oldoptions = coll->co_options & (CO_MASK & ~mask);
310156230Smux
311156230Smux	if (from->co_release != NULL) {
312156230Smux		if (coll->co_release != NULL)
313156230Smux			free(coll->co_release);
314156230Smux		coll->co_release = xstrdup(from->co_release);
315156230Smux	}
316156230Smux	if (from->co_host != NULL) {
317156230Smux		if (coll->co_host != NULL)
318156230Smux			free(coll->co_host);
319156230Smux		coll->co_host = xstrdup(from->co_host);
320156230Smux	}
321156230Smux	if (from->co_base != NULL) {
322156230Smux		if (coll->co_base != NULL)
323156230Smux			free(coll->co_base);
324156230Smux		coll->co_base = xstrdup(from->co_base);
325156230Smux	}
326156230Smux	if (from->co_colldir != NULL)
327156230Smux		coll->co_colldir = from->co_colldir;
328156230Smux	if (from->co_prefix != NULL) {
329156230Smux		if (coll->co_prefix != NULL)
330156230Smux			free(coll->co_prefix);
331156230Smux		coll->co_prefix = xstrdup(from->co_prefix);
332156230Smux	}
333156230Smux	if (newoptions & CO_CHECKOUTMODE) {
334156230Smux		if (from->co_tag != NULL) {
335156230Smux			if (coll->co_tag != NULL)
336156230Smux				free(coll->co_tag);
337156230Smux			coll->co_tag = xstrdup(from->co_tag);
338156230Smux		}
339156230Smux		if (from->co_date != NULL) {
340156230Smux			if (coll->co_date != NULL)
341156230Smux				free(coll->co_date);
342156230Smux			coll->co_date = xstrdup(from->co_date);
343156230Smux		}
344156230Smux	}
345156230Smux	if (from->co_listsuffix != NULL) {
346156230Smux		if (coll->co_listsuffix != NULL)
347156230Smux			free(coll->co_listsuffix);
348156230Smux		coll->co_listsuffix = xstrdup(from->co_listsuffix);
349156230Smux	}
350156230Smux	for (i = 0; i < pattlist_size(from->co_accepts); i++) {
351156230Smux		pattlist_add(coll->co_accepts,
352156230Smux		    pattlist_get(from->co_accepts, i));
353156230Smux	}
354156230Smux	for (i = 0; i < pattlist_size(from->co_refusals); i++) {
355156230Smux		pattlist_add(coll->co_refusals,
356156230Smux		    pattlist_get(from->co_refusals, i));
357156230Smux	}
358156230Smux	coll->co_options = oldoptions | newoptions;
359156230Smux}
360156230Smux
361156230Smuxchar *
362156230Smuxcoll_statussuffix(struct coll *coll)
363156230Smux{
364156230Smux	const char *tag;
365156230Smux	char *suffix;
366156230Smux
367156230Smux	if (coll->co_listsuffix != NULL) {
368156230Smux		xasprintf(&suffix, ".%s", coll->co_listsuffix);
369156230Smux	} else if (coll->co_options & CO_USERELSUFFIX) {
370156230Smux		if (coll->co_tag == NULL)
371156230Smux			tag = ".";
372156230Smux		else
373156230Smux			tag = coll->co_tag;
374156230Smux		if (coll->co_release != NULL) {
375156230Smux			if (coll->co_options & CO_CHECKOUTMODE) {
376156230Smux				xasprintf(&suffix, ".%s:%s",
377156230Smux				    coll->co_release, tag);
378156230Smux			} else {
379156230Smux				xasprintf(&suffix, ".%s", coll->co_release);
380156230Smux			}
381156230Smux		} else if (coll->co_options & CO_CHECKOUTMODE) {
382156230Smux			xasprintf(&suffix, ":%s", tag);
383156230Smux		}
384156230Smux	} else
385156230Smux		suffix = NULL;
386156230Smux	return (suffix);
387156230Smux}
388156230Smux
389156230Smuxchar *
390156230Smuxcoll_statuspath(struct coll *coll)
391156230Smux{
392156230Smux	char *path, *suffix;
393156230Smux
394156230Smux	suffix = coll_statussuffix(coll);
395156230Smux	if (suffix != NULL) {
396156230Smux		if (coll->co_colldir[0] == '/')
397156230Smux			xasprintf(&path, "%s/%s/checkouts%s", coll->co_colldir,
398156230Smux			    coll->co_name, suffix);
399156230Smux		else
400156230Smux			xasprintf(&path, "%s/%s/%s/checkouts%s", coll->co_base,
401156230Smux			    coll->co_colldir, coll->co_name, suffix);
402156230Smux	} else {
403156230Smux		if (coll->co_colldir[0] == '/')
404156230Smux			xasprintf(&path, "%s/%s/checkouts", coll->co_colldir,
405156230Smux			    coll->co_name);
406156230Smux		else
407156230Smux			xasprintf(&path, "%s/%s/%s/checkouts", coll->co_base,
408156230Smux			    coll->co_colldir, coll->co_name);
409156230Smux	}
410156230Smux	free(suffix);
411156230Smux	return (path);
412156230Smux}
413156230Smux
414156230Smuxvoid
415156230Smuxcoll_add(char *name)
416156230Smux{
417156230Smux	struct coll *coll;
418156230Smux
419156230Smux	cur_coll->co_name = name;
420156230Smux	if (cur_coll->co_release == NULL) {
421156230Smux		lprintf(-1, "Release not specified for collection "
422156230Smux		    "\"%s\"\n", cur_coll->co_name);
423156230Smux		exit(1);
424156230Smux	}
425156230Smux	if (cur_coll->co_host == NULL) {
426156230Smux		lprintf(-1, "Host not specified for collection "
427156230Smux		    "\"%s\"\n", cur_coll->co_name);
428156230Smux		exit(1);
429156230Smux	}
430156230Smux	if (!(cur_coll->co_options & CO_CHECKOUTMODE)) {
431156230Smux		lprintf(-1, "Client only supports checkout mode\n");
432156230Smux		exit(1);
433156230Smux	}
434156230Smux	if (!STAILQ_EMPTY(&colls)) {
435156230Smux		coll = STAILQ_LAST(&colls, coll, co_next);
436156230Smux		if (strcmp(coll->co_host, cur_coll->co_host) != 0) {
437156230Smux			lprintf(-1, "All \"host\" fields in the supfile "
438156230Smux			    "must be the same\n");
439156230Smux			exit(1);
440156230Smux		}
441156230Smux	}
442156230Smux	STAILQ_INSERT_TAIL(&colls, cur_coll, co_next);
443156230Smux	cur_coll = coll_new(defaults);
444156230Smux}
445156230Smux
446156230Smuxvoid
447156230Smuxcoll_free(struct coll *coll)
448156230Smux{
449156230Smux
450156230Smux	if (coll == NULL)
451156230Smux		return;
452156230Smux	if (coll->co_host != NULL)
453156230Smux		free(coll->co_host);
454156230Smux	if (coll->co_base != NULL)
455156230Smux		free(coll->co_base);
456156230Smux	if (coll->co_date != NULL)
457156230Smux		free(coll->co_date);
458156230Smux	if (coll->co_prefix != NULL)
459156230Smux		free(coll->co_prefix);
460156230Smux	if (coll->co_release != NULL)
461156230Smux		free(coll->co_release);
462156230Smux	if (coll->co_tag != NULL)
463156230Smux		free(coll->co_tag);
464156230Smux	if (coll->co_cvsroot != NULL)
465156230Smux		free(coll->co_cvsroot);
466156230Smux	if (coll->co_name != NULL)
467156230Smux		free(coll->co_name);
468156230Smux	if (coll->co_listsuffix != NULL)
469156230Smux		free(coll->co_listsuffix);
470156230Smux	keyword_free(coll->co_keyword);
471156230Smux	if (coll->co_dirfilter != NULL)
472156230Smux		globtree_free(coll->co_dirfilter);
473156230Smux	if (coll->co_dirfilter != NULL)
474156230Smux		globtree_free(coll->co_filefilter);
475156230Smux	if (coll->co_accepts != NULL)
476156230Smux		pattlist_free(coll->co_accepts);
477156230Smux	if (coll->co_refusals != NULL)
478156230Smux		pattlist_free(coll->co_refusals);
479156230Smux	free(coll);
480156230Smux}
481156230Smux
482156230Smuxvoid
483156230Smuxcoll_setopt(int opt, char *value)
484156230Smux{
485156230Smux	struct coll *coll;
486156230Smux
487156230Smux	coll = cur_coll;
488156230Smux	switch (opt) {
489156230Smux	case PT_HOST:
490156230Smux		if (coll->co_host != NULL)
491156230Smux			free(coll->co_host);
492156230Smux		coll->co_host = value;
493156230Smux		break;
494156230Smux	case PT_BASE:
495156230Smux		if (coll->co_base != NULL)
496156230Smux			free(coll->co_base);
497156230Smux		coll->co_base = value;
498156230Smux		break;
499156230Smux	case PT_DATE:
500156230Smux		if (coll->co_date != NULL)
501156230Smux			free(coll->co_date);
502156230Smux		coll->co_date = value;
503156230Smux		coll->co_options |= CO_CHECKOUTMODE;
504156230Smux		break;
505156230Smux	case PT_PREFIX:
506156230Smux		if (coll->co_prefix != NULL)
507156230Smux			free(coll->co_prefix);
508156230Smux		coll->co_prefix = value;
509156230Smux		break;
510156230Smux	case PT_RELEASE:
511156230Smux		if (coll->co_release != NULL)
512156230Smux			free(coll->co_release);
513156230Smux		coll->co_release = value;
514156230Smux		break;
515156230Smux	case PT_TAG:
516156230Smux		if (coll->co_tag != NULL)
517156230Smux			free(coll->co_tag);
518156230Smux		coll->co_tag = value;
519156230Smux		coll->co_options |= CO_CHECKOUTMODE;
520156230Smux		break;
521156230Smux	case PT_LIST:
522156230Smux		if (strchr(value, '/') != NULL) {
523156230Smux			lprintf(-1, "Parse error in \"%s\": \"list\" suffix "
524156230Smux			    "must not contain slashes\n", cfgfile);
525156230Smux			exit(1);
526156230Smux		}
527156230Smux		if (coll->co_listsuffix != NULL)
528156230Smux			free(coll->co_listsuffix);
529156230Smux		coll->co_listsuffix = value;
530156230Smux		break;
531156230Smux	case PT_UMASK:
532156230Smux		errno = 0;
533156230Smux		coll->co_umask = strtol(value, NULL, 8);
534156230Smux		free(value);
535156230Smux		if (errno) {
536156230Smux			lprintf(-1, "Parse error in \"%s\": Invalid "
537156230Smux			    "umask value\n", cfgfile);
538156230Smux			exit(1);
539156230Smux		}
540156230Smux		break;
541156230Smux	case PT_USE_REL_SUFFIX:
542156230Smux		coll->co_options |= CO_USERELSUFFIX;
543156230Smux		break;
544156230Smux	case PT_DELETE:
545156230Smux		coll->co_options |= CO_DELETE | CO_EXACTRCS;
546156230Smux		break;
547156230Smux	case PT_COMPRESS:
548156230Smux		coll->co_options |= CO_COMPRESS;
549156230Smux		break;
550156230Smux	}
551156230Smux}
552156230Smux
553156230Smux/* Set "coll" as being the default collection. */
554156230Smuxvoid
555156230Smuxcoll_setdef(void)
556156230Smux{
557156230Smux
558156230Smux	coll_free(defaults);
559156230Smux	defaults = cur_coll;
560156230Smux	cur_coll = coll_new(defaults);
561156230Smux}
562