154359Sroberto/*
254359Sroberto * ntp_filegen.c,v 3.12 1994/01/25 19:06:11 kardel Exp
354359Sroberto *
454359Sroberto *  implements file generations support for NTP
554359Sroberto *  logfiles and statistic files
654359Sroberto *
754359Sroberto *
854359Sroberto * Copyright (C) 1992, 1996 by Rainer Pruy
954359Sroberto * Friedrich-Alexander Universit�t Erlangen-N�rnberg, Germany
1054359Sroberto *
1154359Sroberto * This code may be modified and used freely
1254359Sroberto * provided credits remain intact.
1354359Sroberto */
1454359Sroberto
1554359Sroberto#ifdef HAVE_CONFIG_H
16182007Sroberto# include <config.h>
1754359Sroberto#endif
1854359Sroberto
1954359Sroberto#include <stdio.h>
2054359Sroberto#include <sys/types.h>
2154359Sroberto#include <sys/stat.h>
2254359Sroberto
2354359Sroberto#include "ntpd.h"
2454359Sroberto#include "ntp_io.h"
2554359Sroberto#include "ntp_string.h"
2654359Sroberto#include "ntp_calendar.h"
2754359Sroberto#include "ntp_filegen.h"
2854359Sroberto#include "ntp_stdlib.h"
2954359Sroberto
3054359Sroberto/*
3154359Sroberto * NTP is intended to run long periods of time without restart.
3254359Sroberto * Thus log and statistic files generated by NTP will grow large.
3354359Sroberto *
3454359Sroberto * this set of routines provides a central interface
3554359Sroberto * to generating files using file generations
3654359Sroberto *
3754359Sroberto * the generation of a file is changed according to file generation type
3854359Sroberto */
3954359Sroberto
4054359Sroberto
4154359Sroberto/*
4254359Sroberto * redefine this if your system dislikes filename suffixes like
4354359Sroberto * X.19910101 or X.1992W50 or ....
4454359Sroberto */
4554359Sroberto#define SUFFIX_SEP '.'
4654359Sroberto
4754359Sroberto/*
4854359Sroberto * other constants
4954359Sroberto */
5054359Sroberto#define FGEN_AGE_SECS   (24*60*60) /* life time of FILEGEN_AGE in seconds */
5154359Sroberto
5254359Srobertostatic	void	filegen_open	P((FILEGEN *, u_long));
5354359Srobertostatic	int	valid_fileref	P((char *, char *));
5454359Sroberto#ifdef	UNUSED
5554359Srobertostatic	FILEGEN *filegen_unregister P((char *));
5654359Sroberto#endif	/* UNUSED */
5754359Sroberto
58182007Srobertostatic void	filegen_init	P((char *, const char *, FILEGEN *));
59182007Sroberto
6054359Sroberto/*
61182007Sroberto * filegen_init
62182007Sroberto */
63182007Sroberto
64182007Srobertostatic void
65182007Srobertofilegen_init(char *prefix, const char *basename, FILEGEN *fp)
66182007Sroberto{
67182007Sroberto	fp->fp       = NULL;
68182007Sroberto	fp->prefix   = prefix;		/* Yes, this is TOTALLY lame! */
69182007Sroberto	fp->basename = (char*)emalloc(strlen(basename) + 1);
70182007Sroberto	strcpy(fp->basename, basename);
71182007Sroberto	fp->id       = 0;
72182007Sroberto	fp->type     = FILEGEN_DAY;
73182007Sroberto	fp->flag     = FGEN_FLAG_LINK; /* not yet enabled !!*/
74182007Sroberto}
75182007Sroberto
76182007Sroberto
77182007Sroberto/*
7854359Sroberto * open a file generation according to the current settings of gen
7954359Sroberto * will also provide a link to basename if requested to do so
8054359Sroberto */
8154359Sroberto
8254359Srobertostatic void
8354359Srobertofilegen_open(
8454359Sroberto	FILEGEN *gen,
8554359Sroberto	u_long  newid
8654359Sroberto	)
8754359Sroberto{
8854359Sroberto	char *filename;
8954359Sroberto	char *basename;
9054359Sroberto	u_int len;
9154359Sroberto	FILE *fp;
9254359Sroberto	struct calendar cal;
9354359Sroberto
9454359Sroberto	len = strlen(gen->prefix) + strlen(gen->basename) + 1;
9554359Sroberto	basename = (char*)emalloc(len);
9654359Sroberto	sprintf(basename, "%s%s", gen->prefix, gen->basename);
9754359Sroberto
9854359Sroberto	switch(gen->type) {
9954359Sroberto	    default:
10054359Sroberto		msyslog(LOG_ERR, "unsupported file generations type %d for \"%s\" - reverting to FILEGEN_NONE",
10154359Sroberto			gen->type, basename);
10254359Sroberto		gen->type = FILEGEN_NONE;
10354359Sroberto
10454359Sroberto		/*FALLTHROUGH*/
10554359Sroberto	    case FILEGEN_NONE:
10654359Sroberto		filename = (char*)emalloc(len);
10754359Sroberto		sprintf(filename,"%s", basename);
10854359Sroberto		break;
10954359Sroberto
11054359Sroberto	    case FILEGEN_PID:
11154359Sroberto		filename = (char*)emalloc(len + 1 + 1 + 10);
11254359Sroberto		sprintf(filename,"%s%c#%ld", basename, SUFFIX_SEP, newid);
11354359Sroberto		break;
11454359Sroberto
11554359Sroberto	    case FILEGEN_DAY:
11654359Sroberto		/* You can argue here in favor of using MJD, but
11754359Sroberto		 * I would assume it to be easier for humans to interpret dates
11854359Sroberto		 * in a format they are used to in everyday life.
11954359Sroberto		 */
12054359Sroberto		caljulian(newid,&cal);
12154359Sroberto		filename = (char*)emalloc(len + 1 + 4 + 2 + 2);
12254359Sroberto		sprintf(filename, "%s%c%04d%02d%02d",
12354359Sroberto			basename, SUFFIX_SEP, cal.year, cal.month, cal.monthday);
12454359Sroberto		break;
12554359Sroberto
12654359Sroberto	    case FILEGEN_WEEK:
12754359Sroberto		/*
12854359Sroberto		 * This is still a hack
12954359Sroberto		 * - the term week is not correlated to week as it is used
13054359Sroberto		 *   normally - it just refers to a period of 7 days
13154359Sroberto		 *   starting at Jan 1 - 'weeks' are counted starting from zero
13254359Sroberto		 */
13354359Sroberto		caljulian(newid,&cal);
13454359Sroberto		filename = (char*)emalloc(len + 1 + 4 + 1 + 2);
13554359Sroberto		sprintf(filename, "%s%c%04dw%02d",
13654359Sroberto			basename, SUFFIX_SEP, cal.year, cal.yearday / 7);
13754359Sroberto		break;
13854359Sroberto
13954359Sroberto	    case FILEGEN_MONTH:
14054359Sroberto		caljulian(newid,&cal);
14154359Sroberto		filename = (char*)emalloc(len + 1 + 4 + 2);
14254359Sroberto		sprintf(filename, "%s%c%04d%02d",
14354359Sroberto			basename, SUFFIX_SEP, cal.year, cal.month);
14454359Sroberto		break;
14554359Sroberto
14654359Sroberto	    case FILEGEN_YEAR:
14754359Sroberto		caljulian(newid,&cal);
14854359Sroberto		filename = (char*)emalloc(len + 1 + 4);
14954359Sroberto		sprintf(filename, "%s%c%04d", basename, SUFFIX_SEP, cal.year);
15054359Sroberto		break;
15154359Sroberto
15254359Sroberto	    case FILEGEN_AGE:
15354359Sroberto		filename = (char*)emalloc(len + 1 + 2 + 10);
15454359Sroberto		sprintf(filename, "%s%ca%08ld", basename, SUFFIX_SEP, newid);
15554359Sroberto		break;
15654359Sroberto	}
15754359Sroberto
15854359Sroberto	if (gen->type != FILEGEN_NONE) {
15954359Sroberto		/*
16054359Sroberto		 * check for existence of a file with name 'basename'
16154359Sroberto		 * as we disallow such a file
16254359Sroberto		 * if FGEN_FLAG_LINK is set create a link
16354359Sroberto		 */
16454359Sroberto		struct stat stats;
16554359Sroberto		/*
16654359Sroberto		 * try to resolve name collisions
16754359Sroberto		 */
16854359Sroberto		static u_long conflicts = 0;
16954359Sroberto
17054359Sroberto#ifndef	S_ISREG
17154359Sroberto#define	S_ISREG(mode)	(((mode) & S_IFREG) == S_IFREG)
17254359Sroberto#endif
17354359Sroberto		if (stat(basename, &stats) == 0) {
17454359Sroberto			/* Hm, file exists... */
17554359Sroberto			if (S_ISREG(stats.st_mode)) {
17654359Sroberto				if (stats.st_nlink <= 1)	{
17754359Sroberto					/*
17854359Sroberto					 * Oh, it is not linked - try to save it
17954359Sroberto					 */
18054359Sroberto					char *savename = (char*)emalloc(len + 1 + 1 + 10 + 10);
18154359Sroberto					sprintf(savename, "%s%c%dC%lu",
18254359Sroberto						basename,
18354359Sroberto						SUFFIX_SEP,
18454359Sroberto						(int) getpid(),
18554359Sroberto						(u_long)conflicts++);
18654359Sroberto					if (rename(basename, savename) != 0)
18754359Sroberto					    msyslog(LOG_ERR," couldn't save %s: %m", basename);
18854359Sroberto					free(savename);
18954359Sroberto				} else {
19054359Sroberto					/*
191182007Sroberto					 * there is at least a second link to
192182007Sroberto					 * this file.
19354359Sroberto					 * just remove the conflicting one
19454359Sroberto					 */
19554359Sroberto					if (
19654359Sroberto#if !defined(VMS)
19754359Sroberto						unlink(basename) != 0
19854359Sroberto#else
19954359Sroberto						delete(basename) != 0
20054359Sroberto#endif
20154359Sroberto						)
20254359Sroberto					    msyslog(LOG_ERR, "couldn't unlink %s: %m", basename);
20354359Sroberto				}
20454359Sroberto			} else {
20554359Sroberto				/*
20654359Sroberto				 * Ehh? Not a regular file ?? strange !!!!
20754359Sroberto				 */
20854359Sroberto				msyslog(LOG_ERR, "expected regular file for %s (found mode 0%lo)",
20954359Sroberto					basename, (unsigned long)stats.st_mode);
21054359Sroberto			}
21154359Sroberto		} else {
21254359Sroberto			/*
21354359Sroberto			 * stat(..) failed, but it is absolutely correct for
21454359Sroberto			 * 'basename' not to exist
21554359Sroberto			 */
21654359Sroberto			if (errno != ENOENT)
21754359Sroberto			    msyslog(LOG_ERR,"stat(%s) failed: %m", basename);
21854359Sroberto		}
21954359Sroberto	}
22054359Sroberto
22154359Sroberto	/*
22254359Sroberto	 * now, try to open new file generation...
22354359Sroberto	 */
22454359Sroberto	fp = fopen(filename, "a");
22554359Sroberto
22654359Sroberto#ifdef DEBUG
22754359Sroberto	if (debug > 3)
22854359Sroberto	    printf("opening filegen (type=%d/id=%lu) \"%s\"\n",
22954359Sroberto		   gen->type, (u_long)newid, filename);
23054359Sroberto#endif
23154359Sroberto
23254359Sroberto	if (fp == NULL)	{
23354359Sroberto		/* open failed -- keep previous state
23454359Sroberto		 *
23554359Sroberto		 * If the file was open before keep the previous generation.
23654359Sroberto		 * This will cause output to end up in the 'wrong' file,
237132451Sroberto		 * but I think this is still better than losing output
23854359Sroberto		 *
23954359Sroberto		 * ignore errors due to missing directories
24054359Sroberto		 */
24154359Sroberto
24254359Sroberto		if (errno != ENOENT)
24354359Sroberto		    msyslog(LOG_ERR, "can't open %s: %m", filename);
24454359Sroberto	} else {
24554359Sroberto		if (gen->fp != NULL) {
24654359Sroberto			fclose(gen->fp);
24754359Sroberto		}
24854359Sroberto		gen->fp = fp;
24954359Sroberto		gen->id = newid;
25054359Sroberto
25154359Sroberto		if (gen->flag & FGEN_FLAG_LINK) {
25254359Sroberto			/*
25354359Sroberto			 * need to link file to basename
25454359Sroberto			 * have to use hardlink for now as I want to allow
25554359Sroberto			 * gen->basename spanning directory levels
25654359Sroberto			 * this would make it more complex to get the correct
25754359Sroberto			 * filename for symlink
25854359Sroberto			 *
25954359Sroberto			 * Ok, it would just mean taking the part following
26054359Sroberto			 * the last '/' in the name.... Should add it later....
26154359Sroberto			 */
26254359Sroberto
26354359Sroberto			/* Windows NT does not support file links -Greg Schueman 1/18/97 */
26454359Sroberto
26554359Sroberto#if defined SYS_WINNT || defined SYS_VXWORKS
26654359Sroberto			SetLastError(0); /* On WinNT, don't support FGEN_FLAG_LINK */
26754359Sroberto#elif defined(VMS)
26854359Sroberto			errno = 0; /* On VMS, don't support FGEN_FLAG_LINK */
26954359Sroberto#else  /* not (VMS) / VXWORKS / WINNT ; DO THE LINK) */
27054359Sroberto			if (link(filename, basename) != 0)
27154359Sroberto			    if (errno != EEXIST)
27254359Sroberto				msyslog(LOG_ERR, "can't link(%s, %s): %m", filename, basename);
27354359Sroberto#endif /* SYS_WINNT || VXWORKS */
27454359Sroberto		}		/* flags & FGEN_FLAG_LINK */
27554359Sroberto	}			/* else fp == NULL */
27654359Sroberto
27754359Sroberto	free(basename);
27854359Sroberto	free(filename);
27954359Sroberto	return;
28054359Sroberto}
28154359Sroberto
28254359Sroberto/*
28354359Sroberto * this function sets up gen->fp to point to the correct
28454359Sroberto * generation of the file for the time specified by 'now'
28554359Sroberto *
28654359Sroberto * 'now' usually is interpreted as second part of a l_fp as is in the cal...
28754359Sroberto * library routines
28854359Sroberto */
28954359Sroberto
29054359Srobertovoid
29154359Srobertofilegen_setup(
29254359Sroberto	FILEGEN *gen,
29354359Sroberto	u_long   now
29454359Sroberto	)
29554359Sroberto{
29654359Sroberto	u_long new_gen = ~ (u_long) 0;
29754359Sroberto	struct calendar cal;
29854359Sroberto
29954359Sroberto	if (!(gen->flag & FGEN_FLAG_ENABLED)) {
30054359Sroberto		if (gen->fp != NULL)
30154359Sroberto		    fclose(gen->fp);
30254359Sroberto		return;
30354359Sroberto	}
30454359Sroberto
30554359Sroberto	switch (gen->type) {
30654359Sroberto	    case FILEGEN_NONE:
30754359Sroberto		if (gen->fp != NULL) return; /* file already open */
30854359Sroberto		break;
30954359Sroberto
31054359Sroberto	    case FILEGEN_PID:
31154359Sroberto		new_gen = getpid();
31254359Sroberto		break;
31354359Sroberto
31454359Sroberto	    case FILEGEN_DAY:
31554359Sroberto		caljulian(now, &cal);
31654359Sroberto		cal.hour = cal.minute = cal.second = 0;
31754359Sroberto		new_gen = caltontp(&cal);
31854359Sroberto		break;
31954359Sroberto
32054359Sroberto	    case FILEGEN_WEEK:
32154359Sroberto		/* Would be nice to have a calweekstart() routine */
32254359Sroberto		/* so just use a hack ... */
32354359Sroberto		/* just round time to integral 7 day period for actual year  */
32454359Sroberto		new_gen = now - (now - calyearstart(now)) % TIMES7(SECSPERDAY)
32554359Sroberto			+ 60;
32654359Sroberto		/*
32754359Sroberto		 * just to be sure -
32854359Sroberto		 * the computation above would fail in the presence of leap seconds
32954359Sroberto		 * so at least carry the date to the next day (+60 (seconds))
33054359Sroberto		 * and go back to the start of the day via calendar computations
33154359Sroberto		 */
33254359Sroberto		caljulian(new_gen, &cal);
33354359Sroberto		cal.hour = cal.minute = cal.second = 0;
33454359Sroberto		new_gen = caltontp(&cal);
33554359Sroberto		break;
33654359Sroberto
33754359Sroberto	    case FILEGEN_MONTH:
33854359Sroberto		caljulian(now, &cal);
339132451Sroberto		cal.yearday = (u_short) (cal.yearday - cal.monthday + 1);
34054359Sroberto		cal.monthday = 1;
34154359Sroberto		cal.hour = cal.minute = cal.second = 0;
34254359Sroberto		new_gen = caltontp(&cal);
34354359Sroberto		break;
34454359Sroberto
34554359Sroberto	    case FILEGEN_YEAR:
34654359Sroberto		new_gen = calyearstart(now);
34754359Sroberto		break;
34854359Sroberto
34954359Sroberto	    case FILEGEN_AGE:
35054359Sroberto		new_gen = current_time  - (current_time % FGEN_AGE_SECS);
35154359Sroberto		break;
35254359Sroberto	}
35354359Sroberto	/*
35454359Sroberto	 * try to open file if not yet open
35554359Sroberto	 * reopen new file generation file on change of generation id
35654359Sroberto	 */
35754359Sroberto	if (gen->fp == NULL || gen->id != new_gen) {
358182007Sroberto#if DEBUG
359182007Sroberto	if (debug)
360182007Sroberto		printf("filegen  %0x %lu %lu %lu\n", gen->type, now,
361182007Sroberto		    gen->id, new_gen);
362182007Sroberto#endif
36354359Sroberto		filegen_open(gen, new_gen);
36454359Sroberto	}
36554359Sroberto}
36654359Sroberto
36754359Sroberto
36854359Sroberto/*
36954359Sroberto * change settings for filegen files
37054359Sroberto */
37154359Srobertovoid
37254359Srobertofilegen_config(
37354359Sroberto	FILEGEN *gen,
37454359Sroberto	char    *basename,
37554359Sroberto	u_int   type,
37654359Sroberto	u_int   flag
37754359Sroberto	)
37854359Sroberto{
37954359Sroberto	/*
38054359Sroberto	 * if nothing would be changed...
38154359Sroberto	 */
38254359Sroberto	if ((basename == gen->basename || strcmp(basename,gen->basename) == 0) &&
38354359Sroberto	    type == gen->type &&
38454359Sroberto	    flag == gen->flag)
38554359Sroberto	    return;
38654359Sroberto
38754359Sroberto	/*
38854359Sroberto	 * validate parameters
38954359Sroberto	 */
39054359Sroberto	if (!valid_fileref(gen->prefix,basename))
39154359Sroberto	    return;
39254359Sroberto
39354359Sroberto	if (gen->fp != NULL)
39454359Sroberto	    fclose(gen->fp);
39554359Sroberto
39654359Sroberto#ifdef DEBUG
39754359Sroberto	if (debug > 2)
39854359Sroberto	    printf("configuring filegen:\n\tprefix:\t%s\n\tbasename:\t%s -> %s\n\ttype:\t%d -> %d\n\tflag: %x -> %x\n",
39954359Sroberto		   gen->prefix, gen->basename, basename, gen->type, type, gen->flag, flag);
40054359Sroberto#endif
40154359Sroberto	if (gen->basename != basename || strcmp(gen->basename, basename) != 0) {
40254359Sroberto		free(gen->basename);
40354359Sroberto		gen->basename = (char*)emalloc(strlen(basename) + 1);
40454359Sroberto		strcpy(gen->basename, basename);
40554359Sroberto	}
406132451Sroberto	gen->type = (u_char) type;
407132451Sroberto	gen->flag = (u_char) flag;
40854359Sroberto
40954359Sroberto	/*
41054359Sroberto	 * make filegen use the new settings
41154359Sroberto	 * special action is only required when a generation file
41254359Sroberto	 * is currently open
41354359Sroberto	 * otherwise the new settings will be used anyway at the next open
41454359Sroberto	 */
41554359Sroberto	if (gen->fp != NULL) {
41654359Sroberto		l_fp now;
41754359Sroberto
41854359Sroberto		get_systime(&now);
41954359Sroberto		filegen_setup(gen, now.l_ui);
42054359Sroberto	}
42154359Sroberto}
42254359Sroberto
42354359Sroberto
42454359Sroberto/*
42554359Sroberto * check whether concatenating prefix and basename
42654359Sroberto * yields a legal filename
42754359Sroberto */
42854359Srobertostatic int
42954359Srobertovalid_fileref(
43054359Sroberto	char *prefix,
43154359Sroberto	char *basename
43254359Sroberto	)
43354359Sroberto{
43454359Sroberto	/*
43554359Sroberto	 * prefix cannot be changed dynamically
43654359Sroberto	 * (within the context of filegen)
43754359Sroberto	 * so just reject basenames containing '..'
43854359Sroberto	 *
43954359Sroberto	 * ASSUMPTION:
44054359Sroberto	 * 		file system parts 'below' prefix may be
44154359Sroberto	 *		specified without infringement of security
44254359Sroberto	 *
44354359Sroberto	 *              restricing prefix to legal values
44454359Sroberto	 *		has to be ensured by other means
44554359Sroberto	 * (however, it would be possible to perform some checks here...)
44654359Sroberto	 */
44754359Sroberto	register char *p = basename;
44854359Sroberto
44954359Sroberto	/*
45054359Sroberto	 * Just to catch, dumb errors opening up the world...
45154359Sroberto	 */
45254359Sroberto	if (prefix == NULL || *prefix == '\0')
45354359Sroberto	    return 0;
45454359Sroberto
45554359Sroberto	if (basename == NULL)
45654359Sroberto	    return 0;
45754359Sroberto
45854359Sroberto	for (p = basename; p; p = strchr(p, '/')) {
45954359Sroberto		if (*p == '.' && *(p+1) == '.' && (*(p+2) == '\0' || *(p+2) == '/'))
46054359Sroberto		    return 0;
46154359Sroberto	}
46254359Sroberto
46354359Sroberto	return 1;
46454359Sroberto}
46554359Sroberto
46654359Sroberto
46754359Sroberto/*
46854359Sroberto * filegen registry
46954359Sroberto */
47054359Sroberto
47154359Srobertostatic struct filegen_entry {
47254359Sroberto	char *name;
47354359Sroberto	FILEGEN *filegen;
47454359Sroberto	struct filegen_entry *next;
47554359Sroberto} *filegen_registry = NULL;
47654359Sroberto
47754359Sroberto
47854359SrobertoFILEGEN *
47954359Srobertofilegen_get(
48054359Sroberto	char *name
48154359Sroberto	)
48254359Sroberto{
48354359Sroberto	struct filegen_entry *f = filegen_registry;
48454359Sroberto
48554359Sroberto	while(f) {
48654359Sroberto		if (f->name == name || strcmp(name, f->name) == 0) {
48754359Sroberto#ifdef XXX	/* this gives the Alpha compiler fits */
48854359Sroberto			if (debug > 3)
48954359Sroberto			    printf("filegen_get(\"%s\") = %x\n", name,
49054359Sroberto				   (u_int)f->filegen);
49154359Sroberto#endif
49254359Sroberto			return f->filegen;
49354359Sroberto		}
49454359Sroberto		f = f->next;
49554359Sroberto	}
49654359Sroberto#ifdef DEBUG
49754359Sroberto	if (debug > 3)
49854359Sroberto	    printf("filegen_get(\"%s\") = NULL\n", name);
49954359Sroberto#endif
50054359Sroberto	return NULL;
50154359Sroberto}
50254359Sroberto
50354359Srobertovoid
50454359Srobertofilegen_register(
505182007Sroberto	char *prefix,
50654359Sroberto	const char *name,
50754359Sroberto	FILEGEN *filegen
50854359Sroberto	)
50954359Sroberto{
51054359Sroberto	struct filegen_entry **f = &filegen_registry;
51154359Sroberto
51254359Sroberto#ifdef XXX		/* this gives the Alpha compiler fits */
51354359Sroberto	if (debug > 3)
51454359Sroberto	    printf("filegen_register(\"%s\",%x)\n", name, (u_int)filegen);
51554359Sroberto#endif
516182007Sroberto
517182007Sroberto	filegen_init(prefix, name, filegen);
518182007Sroberto
51954359Sroberto	while (*f) {
52054359Sroberto		if ((*f)->name == name || strcmp(name, (*f)->name) == 0) {
52154359Sroberto#ifdef XXX	 /* this gives the Alpha compiler fits */
52254359Sroberto			if (debug > 4) {
52354359Sroberto				printf("replacing filegen %x\n", (u_int)(*f)->filegen);
52454359Sroberto			}
52554359Sroberto#endif
52654359Sroberto			(*f)->filegen = filegen;
52754359Sroberto			return;
52854359Sroberto		}
52954359Sroberto		f = &((*f)->next);
53054359Sroberto	}
53154359Sroberto
53254359Sroberto	*f = (struct filegen_entry *) emalloc(sizeof(struct filegen_entry));
53354359Sroberto	if (*f) {
53454359Sroberto		(*f)->next = NULL;
53554359Sroberto		(*f)->name = (char*)emalloc(strlen(name) + 1);
53654359Sroberto		strcpy((*f)->name, name);
53754359Sroberto		(*f)->filegen = filegen;
53854359Sroberto#ifdef DEBUG
53954359Sroberto		if (debug > 5) {
54054359Sroberto			printf("adding new filegen\n");
54154359Sroberto		}
54254359Sroberto#endif
54354359Sroberto	}
54454359Sroberto
54554359Sroberto	return;
54654359Sroberto}
54754359Sroberto
54854359Sroberto#ifdef	UNUSED
54954359Srobertostatic FILEGEN *
55054359Srobertofilegen_unregister(
55154359Sroberto	char *name
55254359Sroberto	)
55354359Sroberto{
55454359Sroberto	struct filegen_entry **f = &filegen_registry;
55554359Sroberto
55654359Sroberto#ifdef DEBUG
55754359Sroberto	if (debug > 3)
55854359Sroberto	    printf("filegen_unregister(\"%s\")\n", name);
55954359Sroberto#endif
56054359Sroberto
56154359Sroberto	while (*f) {
56254359Sroberto		if (strcmp((*f)->name,name) == 0) {
56354359Sroberto			struct filegen_entry *ff = *f;
56454359Sroberto			FILEGEN *fg;
56554359Sroberto
56654359Sroberto			*f = (*f)->next;
56754359Sroberto			fg = ff->filegen;
56854359Sroberto			free(ff->name);
56954359Sroberto			free(ff);
57054359Sroberto			return fg;
57154359Sroberto		}
57254359Sroberto		f = &((*f)->next);
57354359Sroberto	}
57454359Sroberto	return NULL;
57554359Sroberto}
57654359Sroberto#endif	/* UNUSED */
577