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