📄 expire.c
字号:
/* $Revision: 1.17 $**** Expire news articles.*/#include "configdata.h"#include <stdio.h>#include <ctype.h>#include <sys/types.h>#include <sys/stat.h>#if defined(DO_NEED_TIME)#include <time.h>#endif /* defined(DO_NEED_TIME) */#include <sys/time.h>#include <errno.h>#include "paths.h"#include "libinn.h"#include "clibrary.h"#include "inndcomm.h"#include "dbz.h"#include "qio.h"#include "macros.h"/*** Stuff that more or less duplicates stuff in innd.*/#define NGH_HASH(Name, p, j) \ for (p = Name, j = 0; *p; ) j = (j << 5) + j + *p++#define NGH_SIZE 128#define NGH_BUCKET(j) &NGHtable[j & (NGH_SIZE - 1)]typedef struct _BUFFER { int Size; int Used; int Left; char *Data;} BUFFER;typedef struct _NEWSGROUP { char *Name; char *Rest; long Last; /* These fields are new. */ time_t Keep; time_t Default; time_t Purge;} NEWSGROUP;typedef struct _NGHASH { int Size; int Used; NEWSGROUP **Groups;} NGHASH;/*** Expire-specific stuff.*/#define MAGIC_TIME 49710.typedef struct _BADGROUP { struct _BADGROUP *Next; char *Name; BOOL HasDirectory;} BADGROUP;STATIC BADGROUP *EXPbadgroups;STATIC BOOL EXPlinks;STATIC BOOL EXPquiet;STATIC BOOL EXPsizing;STATIC BOOL EXPtracing;STATIC BOOL EXPusepost;STATIC char EXPnewslib[] = _PATH_NEWSLIB;STATIC char ACTIVE[] = _PATH_ACTIVE;STATIC char SPOOL[] = _PATH_SPOOL;STATIC int nGroups;STATIC FILE *EXPunlinkfile;STATIC long EXPsaved;STATIC NEWSGROUP *Groups;STATIC NEWSGROUP EXPdefault;STATIC NGHASH NGHtable[NGH_SIZE];STATIC STRING EXPreason;STATIC time_t EXPremember;STATIC time_t Now;/* Statistics; for -v flag. */STATIC char *EXPgraph;STATIC int EXPverbose;STATIC long EXPprocessed;STATIC long EXPunlinked;STATIC long EXPhistdrop;STATIC long EXPhistremember;STATIC long EXPallgone;STATIC long EXPstillhere;STATIC int EXPsplit();extern double atof();/*** Hash a newsgroup and see if we get it.*/STATIC NEWSGROUP *NGfind(Name) char *Name;{ register char *p; register int i; unsigned int j; register NEWSGROUP **ngp; char c; NGHASH *htp; /* SUPPRESS 6 *//* Over/underflow from plus expression */ NGH_HASH(Name, p, j); htp = NGH_BUCKET(j); for (c = *Name, ngp = htp->Groups, i = htp->Used; --i >= 0; ngp++) if (c == ngp[0]->Name[0] && EQ(Name, ngp[0]->Name)) return ngp[0]; return NULL;}/*** Sorting predicate to put newsgroups in rough order of their activity.*/STATIC intNGcompare(p1, p2) POINTER p1; POINTER p2;{ NEWSGROUP **ng1; NEWSGROUP **ng2; ng1 = CAST(NEWSGROUP**, p1); ng2 = CAST(NEWSGROUP**, p2); return ng1[0]->Last - ng2[0]->Last;}/*** Build the newsgroup structures from the active file.*/STATIC voidBuildGroups(active) char *active;{ register NGHASH *htp; register NEWSGROUP *ngp; register char *p; register char *q; register int i; register unsigned j; register int lines; int NGHbuckets; char *fields[5]; /* Count the number of groups. */ for (p = active, i = 0; (p = strchr(p, '\n')) != NULL; p++, i++) continue; nGroups = i; Groups = NEW(NEWSGROUP, i); /* Set up the default hash buckets. */ NGHbuckets = i / NGH_SIZE; if (NGHbuckets == 0) NGHbuckets = 1; for (i = NGH_SIZE, htp = NGHtable; --i >= 0; htp++) { htp->Size = NGHbuckets; htp->Groups = NEW(NEWSGROUP*, htp->Size); htp->Used = 0; } /* Fill in the array. */ lines = 0; for (p = active, ngp = Groups, i = nGroups; --i >= 0; ngp++, p = q + 1) { lines++; if ((q = strchr(p, '\n')) == NULL) { (void)fprintf(stderr, "%s: line %d missing newline\n", ACTIVE, lines); exit(1); } *q = '\0'; if (EXPsplit(p, ' ', fields, SIZEOF(fields)) != 4) { (void)fprintf(stderr, "%s: line %d wrong number of fields\n", ACTIVE, lines); exit(1); } ngp->Name = fields[0]; ngp->Last = atol(fields[1]); ngp->Rest = fields[3]; /* Find the right bucket for the group, make sure there is room. */ /* SUPPRESS 6 *//* Over/underflow from plus expression */ NGH_HASH(ngp->Name, p, j); htp = NGH_BUCKET(j); if (htp->Used >= htp->Size) { htp->Size += NGHbuckets; RENEW(htp->Groups, NEWSGROUP*, htp->Size); } htp->Groups[htp->Used++] = ngp; } /* Sort each hash bucket. */ for (i = NGH_SIZE, htp = NGHtable; --i >= 0; htp++) if (htp->Used > 1) qsort((POINTER)htp->Groups, (SIZE_T)htp->Used, sizeof htp->Groups[0], NGcompare);}/*** Open a file or give up.*/STATIC FILE *EXPfopen(Remove, Name, Mode) BOOL Remove; STRING Name; char *Mode;{ FILE *F; if (Remove && unlink(Name) < 0 && errno != ENOENT) (void)fprintf(stderr, "Warning, can't remove %s, %s\n", Name, strerror(errno)); if ((F = fopen(Name, Mode)) == NULL) { (void)fprintf(stderr, "Can't open %s in %s mode, %s\n", Name, Mode, strerror(errno)); exit(1); } return F;}/*** Split a line at a specified field separator into a vector and return** the number of fields found, or -1 on error.*/STATIC intEXPsplit(p, sep, argv, count) register char *p; register char sep; register char **argv; register int count;{ register int i; for (i = 1, *argv++ = p; *p; ) if (*p++ == sep) { if (++i == count) /* Overflow. */ return -1; p[-1] = '\0'; for (*argv++ = p; *p == sep; p++) continue; } return i;}/*** Parse a number field converting it into a "when did this start?".** This makes the "keep it" tests fast, but inverts the logic of** just about everything you expect. Print a message and return FALSE** on error.*/STATIC BOOLEXPgetnum(line, word, v, name) int line; char *word; time_t *v; char *name;{ register char *p; register BOOL SawDot; double d; if (caseEQ(word, "never")) { *v = (time_t)0; return TRUE; } /* Check the number. We don't have strtod yet. */ for (p = word; ISWHITE(*p); p++) continue; if (*p == '+' || *p == '-') p++; for (SawDot = FALSE; *p; p++) if (*p == '.') { if (SawDot) break; SawDot = TRUE; } else if (!CTYPE(isdigit, *p)) break; if (*p) { (void)fprintf(stderr, "Line %d, bad `%c' character in %s field\n", line, *p, name); return FALSE; } d = atof(word); if (d > MAGIC_TIME) *v = (time_t)0; else *v = Now - (time_t)(d * 86400.); return TRUE;}/*** Set the expiration fields for all groups that match this pattern.*/STATIC voidEXPmatch(p, v, mod) register char *p; register NEWSGROUP *v; register char mod;{ register NEWSGROUP *ngp; register int i; register BOOL negate; negate = *p == '!'; if (negate) p++; for (ngp = Groups, i = nGroups; --i >= 0; ngp++) if (negate ? !wildmat(ngp->Name, p) : wildmat(ngp->Name, p)) if (mod == 'a' || (mod == 'm' && ngp->Rest[0] == NF_FLAG_MODERATED) || (mod == 'u' && ngp->Rest[0] != NF_FLAG_MODERATED)) { ngp->Keep = v->Keep; ngp->Default = v->Default; ngp->Purge = v->Purge; if (EXPverbose > 4) { (void)printf("%s", ngp->Name); (void)printf(" %13.13s", ctime(&v->Keep) + 3); (void)printf(" %13.13s", ctime(&v->Default) + 3); (void)printf(" %13.13s", ctime(&v->Purge) + 3); (void)printf(" (%s)\n", p); } }}/*** Parse the expiration control file. Return TRUE if okay.*/STATIC BOOLEXPreadfile(F) register FILE *F;{ register char *p; register int i; register int j; register int k; register char mod; NEWSGROUP v; BOOL SawDefault; char buff[BUFSIZ]; char *fields[6]; char **patterns; /* Scan all lines. */ EXPremember = -1; SawDefault = FALSE; patterns = NEW(char*, nGroups); for (i = 1; fgets(buff, sizeof buff, F) != NULL; i++) { if ((p = strchr(buff, '\n')) == NULL) { (void)fprintf(stderr, "Line %d too long\n", i); return FALSE; } *p = '\0'; if (buff[0] == '\0' || buff[0] == '#') continue; if ((j = EXPsplit(buff, ':', fields, SIZEOF(fields))) == -1) { (void)fprintf(stderr, "Line %d too many fields\n", i); return FALSE; } /* Expired-article remember line? */ if (EQ(fields[0], "/remember/")) { if (j != 2) { (void)fprintf(stderr, "Line %d bad format\n", i); return FALSE; } if (EXPremember != -1) { (void)fprintf(stderr, "Line %d duplicate /remember/\n", i); return FALSE; } if (!EXPgetnum(i, fields[1], &EXPremember, "remember")) return FALSE; continue; } /* Regular expiration line -- right number of fields? */ if (j != 5) { (void)fprintf(stderr, "Line %d bad format\n", i); return FALSE; } /* Parse the fields. */ if (strchr(fields[1], 'M') != NULL) mod = 'm'; else if (strchr(fields[1], 'U') != NULL) mod = 'u'; else if (strchr(fields[1], 'A') != NULL) mod = 'a'; else { (void)fprintf(stderr, "Line %d bad modflag\n", i); return FALSE; } if (!EXPgetnum(i, fields[2], &v.Keep, "keep") || !EXPgetnum(i, fields[3], &v.Default, "default") || !EXPgetnum(i, fields[4], &v.Purge, "purge")) return FALSE; /* These were turned into offsets, so the test is the opposie * of what you think it should be. If Purge isn't forever, * make sure it's greater then the other two fields. */ if (v.Purge) { /* Some value not forever; make sure other values are in range. */ if (v.Keep && v.Keep < v.Purge) { (void)fprintf(stderr, "Line %d keep>purge\n", i); return FALSE; } if (v.Default && v.Default < v.Purge) { (void)fprintf(stderr, "Line %d default>purge\n", i); return FALSE; } } /* Is this the default line? */ if (fields[0][0] == '*' && fields[0][1] == '\0' && mod == 'a') { if (SawDefault) { (void)fprintf(stderr, "Line %d duplicate default\n", i); break; } EXPdefault.Keep = v.Keep; EXPdefault.Default = v.Default; EXPdefault.Purge = v.Purge; SawDefault = TRUE; } /* Assign to all groups that match the pattern and flags. */ if ((j = EXPsplit(fields[0], ',', patterns, nGroups)) == -1) { (void)fprintf(stderr, "Line %d too many patterns\n", i); return FALSE; } for (k = 0; k < j; k++) EXPmatch(patterns[k], &v, mod); } DISPOSE(patterns); return TRUE;}/*** Handle a newsgroup that isn't in the active file.*/STATIC NEWSGROUP *EXPnotfound(Entry) char *Entry;{ static NEWSGROUP Removeit; register BADGROUP *bg; register char *p; struct stat Sb; char buff[SPOOLNAMEBUFF]; /* See if we already know about this group. */ for (bg = EXPbadgroups; bg; bg = bg->Next) if (EQ(Entry, bg->Name)) break; if (bg == NULL) { bg = NEW(BADGROUP, 1); bg->Name = COPY(Entry); (void)strcpy(buff, bg->Name); for (p = buff; *p; p++) if (*p == '.') *p = '/'; bg->HasDirectory = stat(buff, &Sb) >= 0 && S_ISDIR(Sb.st_mode); bg->Next = EXPbadgroups; EXPbadgroups = bg; if (!EXPquiet) { (void)fflush(stdout); (void)fprintf(stderr, "Group not matched (removed?) %s -- %s\n", Entry, bg->HasDirectory ? "Using default expiration" : "Purging all articles"); } } /* Directory still there; use default expiration. */ if (bg->HasDirectory) return &EXPdefault; /* No directory -- remove it all now. */ if (Removeit.Keep == 0) { Removeit.Keep = Now; Removeit.Default = Now; Removeit.Purge = Now; } return &Removeit;}/*** Should we keep the specified article?*/STATIC BOOLEXPkeepit(Entry, when, Expires) char *Entry; time_t when; time_t Expires;{ register char *p; register NEWSGROUP *ngp; if ((p = strchr(Entry, '/')) == NULL) { (void)fflush(stdout); (void)fprintf(stderr, "Bad entry, \"%s\"\n", Entry); return TRUE; } *p = '\0'; if ((ngp = NGfind(Entry)) == NULL) ngp = EXPnotfound(Entry); *p = '/'; if (EXPverbose > 2) { if (EXPverbose > 3) (void)printf("%s age = %0.2f\n", Entry, (Now - when) / 86400.); if (Expires == 0) { if (when < ngp->Default) (void)printf("%s too old (no exp)\n", Entry); } else { if (when < ngp->Purge) (void)printf("%s later than purge\n", Entry); if (when > ngp->Keep) (void)printf("%s earlier than min\n", Entry); if (Now > Expires) (void)printf("%s later than header\n", Entry); } } /* If no expiration, make sure it wasn't posted before the default. */ if (Expires == 0) return when >= ngp->Default; /* Make sure it's not posted before the purge cut-off and * that it's not due to expire. */ return when >= ngp->Purge && (Expires >= Now || when >= ngp->Keep);}/*** An article can be removed. Either print a note, or actually remove it.** Also fill in the article size.*/STATIC voidEXPremove(p, size) char *p; long *size;{ register char *q; struct stat Sb; /* Turn into a filename and get the size if we need it. */ for (q = p; *q; q++) if (*q == '.') *q = '/'; if (EXPsizing && *size < 0 && stat(p, &Sb) >= 0)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -