/*$Id: ezmlm-archive.c,v 1.13 1999/11/28 20:13:32 lindberg Exp $*/ /*$Name: ezmlm-idx-040 $*/ #include "alloc.h" #include "error.h" #include "stralloc.h" #include "str.h" #include "sig.h" #include "getconf.h" #include "strerr.h" #include "getln.h" #include "substdio.h" #include "readwrite.h" #include "fmt.h" #include "sgetopt.h" #include "idxthread.h" #include "makehash.h" #include "idx.h" #include "errtxt.h" #define FATAL "ezmlm-archive: fatal: " #define WARNING "ezmlm-archive: warning: inconsistent index: " substdio ssin; char inbuf[1024]; substdio ssout; char outbuf[1024]; substdio ssnum; char numbuf[16]; stralloc line = {0}; stralloc num = {0}; stralloc fn = {0}; stralloc fnn = {0}; char strnum[FMT_ULONG]; int flagerror = 0; int flagsync = 1; /* sync() by default, not for -c or -f or -t */ char *dir; struct ca { char *s; /* start */ unsigned int l; /* length */ } ca; void die_usage() { strerr_die1x(100, "ezmlm-archive: usage: " "ezmlm-archive [-cCFsSTvV] [-f min_msg] [-t max_msg] dir"); } void die_nomem() { strerr_die2x(111,FATAL,ERR_NOMEM); } void close_proper(ss,s,sn) /* flush,sync,close,move sn->s) */ substdio *ss; char *s, *sn; { if (substdio_flush(ss) == -1) strerr_die6sys(111,FATAL,ERR_FLUSH,dir,"/",s,": "); if (flagsync) if (fsync(ss->fd) == -1) strerr_die6sys(111,FATAL,ERR_SYNC,dir,"/",s,": "); if (close(ss->fd) == -1) strerr_die6sys(111,FATAL,ERR_CLOSE,dir,"/",s,": "); if (rename(sn,s) == -1) strerr_die6sys(111,FATAL,ERR_MOVE,dir,"/",sn,": "); } void write_threads(msgtable,subtable,authtable,datetable,from,to) /* Add the current threading data to the thread database without dups */ /* Writes the subject index first, then processes the individual files */ msgentry *msgtable; subentry *subtable; authentry *authtable; dateentry *datetable; unsigned long from,to; { msgentry *pmsgt; subentry *psubt,*psubtm, *psubtlast; subentry *presubt = (subentry *)0; authentry *pautht; dateentry *pdatet; char *cp,*cp1; unsigned long msg; unsigned long ulmsginthread; unsigned long subnum; unsigned long authnum; unsigned long msgnum; unsigned int pos,l; unsigned int startdate,nextdate; unsigned int startmsg,nextmsg; int fd = -1; int fdn = -1; int match; int ffound; int lineno; int res; psubtm = subtable; /* now for new threads */ pdatet = datetable; nextmsg = 0L; nextdate = pdatet->date; while (psubtm->sub) { /* these are in msgnum order */ if (!presubt) /* for rewind */ if (psubtm->lastmsg >= nextmsg) presubt = psubtm; /* this thread extends beyond current month */ if (psubtm->firstmsg >= nextmsg) { /* done with this month */ if (fdn != -1) close_proper(&ssout,fn.s,fnn.s); psubtlast = psubtm; /* last thread done */ if (presubt) /* need to rewind? */ psubtm = presubt; /* do it */ psubt = psubtm; /* tmp pointer to reset done flag */ presubt = (subentry *)0; /* reset rewind pointer */ pdatet++; /* next month */ startdate = nextdate; /* startdate */ nextdate = pdatet->date; /* end date */ startmsg = nextmsg; /* first message in month */ nextmsg = pdatet->msg; /* first message in next month */ if (!stralloc_copys(&fn,"archive/threads/")) die_nomem(); if (!stralloc_catb(&fn,strnum,fmt_uint(strnum,startdate))) die_nomem(); if (!stralloc_copy(&fnn,&fn)) die_nomem(); if (!stralloc_0(&fn)) die_nomem(); if (!stralloc_cats(&fnn,"n")) die_nomem(); if (!stralloc_0(&fnn)) die_nomem(); if ((fdn = open_trunc(fnn.s)) == -1) strerr_die6sys(111,FATAL,ERR_CREATE,dir,"/",fnn.s,": "); substdio_fdbuf(&ssout,write,fdn,outbuf,sizeof(outbuf)); if ((fd = open_read(fn.s)) == -1) { if (errno != error_noent) strerr_die6sys(111,FATAL,ERR_OPEN,dir,"/",fn.s,": "); } else { substdio_fdbuf(&ssin,read,fd,inbuf,sizeof(inbuf)); for (;;) { if (getln(&ssin,&line,&match,'\n') == -1) strerr_die6sys(111,FATAL,ERR_READ,dir,"/",fn.s,": "); if (!match) break; pos = scan_ulong(line.s,&msgnum); pos++; /* skip ':' */ if (msgnum >= from) continue; /* ignore entries from threading range */ if (line.len < pos + HASHLEN) { flagerror = -1; /* and bad ones */ continue; } psubt = subtable; cp = line.s + pos; ffound = 0; /* search among already known subjects */ for (;;) { res = str_diffn(psubt->sub,cp,HASHLEN); if (res < 0) { if (psubt->higher) psubt = psubt->higher; else break; } else if (res > 0) { if (psubt->lower) psubt = psubt->lower; else break; } else { ffound = 1; break; } } if (!ffound) { if (substdio_put(&ssout,line.s,line.len) == -1) strerr_die6sys(111,FATAL,ERR_WRITE,dir,"/",fnn.s,": "); } else { /* new # of msg in thread */ cp += HASHLEN; /* HASHLEN [#] Subject always \n at end */ if (*(cp++) == ' ' && *(cp++) == '[') { cp += scan_ulong(cp,&ulmsginthread); if (*cp == ']') { psubt->msginthread += (unsigned char) (ulmsginthread & 0xff); } } else flagerror = -5; } } close(fd); } continue; } if (psubtm->firstmsg < nextmsg && psubtm->lastmsg >= startmsg) { if (!stralloc_copyb(&line,strnum,fmt_ulong(strnum,psubtm->lastmsg))) die_nomem(); if (!stralloc_cats(&line,":")) die_nomem(); if (!stralloc_catb(&line,psubtm->sub,HASHLEN)) die_nomem(); if (!stralloc_cats(&line," [")) die_nomem(); if (!stralloc_catb(&line,strnum, fmt_ulong(strnum,(unsigned long) psubtm->msginthread))) die_nomem(); if (!stralloc_cats(&line,"]")) die_nomem(); if (!stralloc_catb(&line,psubtm->sub + HASHLEN,psubtm->sublen - HASHLEN)) die_nomem(); /* has \n */ if (substdio_put(&ssout,line.s,line.len) == -1) strerr_die6sys(111,FATAL,ERR_WRITE,dir,"/",fnn.s,": "); } psubtm++; } if (fdn != -1) close_proper(&ssout,fn.s,fnn.s); psubt = subtable; while (psubt->sub) { /* now the threads */ if (!stralloc_copys(&fn,"archive/subjects/")) die_nomem(); if (!stralloc_catb(&fn,psubt->sub,2)) die_nomem(); if (!stralloc_0(&fn)) die_nomem(); if (mkdir(fn.s,0755) == -1) if (errno != error_exist) strerr_die6sys(111,FATAL,ERR_CREATE,dir,"/",fn.s,": "); fn.s[fn.len - 1] = '/'; if (!stralloc_catb(&fn,psubt->sub+2,HASHLEN-2)) die_nomem(); if (!stralloc_copy(&fnn,&fn)) die_nomem(); if (!stralloc_cats(&fnn,"n")) die_nomem(); if (!stralloc_0(&fn)) die_nomem(); if (!stralloc_0(&fnn)) die_nomem(); if ((fdn = open_trunc(fnn.s)) == -1) strerr_die4sys(111,FATAL,ERR_CREATE,fnn.s,": "); substdio_fdbuf(&ssout,write,fdn,outbuf,sizeof(outbuf)); if ((fd = open_read(fn.s)) == -1) { if (errno != error_noent) strerr_die4sys(111,FATAL,ERR_OPEN,fn.s,": "); if (substdio_puts(&ssout,psubt->sub) == -1) /* write subject */ strerr_die6sys(111,FATAL,ERR_WRITE,dir,"/",fnn.s,": "); } else { /* copy data */ substdio_fdbuf(&ssin,read,fd,inbuf,sizeof(inbuf)); lineno = 0; for (;;) { if (getln(&ssin,&line,&match,'\n') == -1) strerr_die6sys(111,FATAL,ERR_READ,dir,"/",fn.s,": "); if (!match) break; if (!lineno) { /* write subject */ if (line.len < HASHLEN + 1 || line.s[HASHLEN] != ' ') flagerror = -3; if (substdio_put(&ssout,line.s,line.len) == -1) strerr_die6sys(111,FATAL,ERR_WRITE,dir,"/",fnn.s,": "); lineno = 1; continue; } (void) scan_ulong(line.s,&msgnum); if (msgnum >= from) break; if (substdio_put(&ssout,line.s,line.len) == -1) strerr_die6sys(111,FATAL,ERR_WRITE,dir,"/",fnn.s,": "); } (void) close(fd); /* close old index */ } subnum = (unsigned long) (psubt - subtable + 1); /* idx of this subj */ pmsgt = msgtable + psubt->firstmsg - from; /* first message entry */ for (msg = psubt->firstmsg; msg <= psubt->lastmsg; msg++) { if (pmsgt->subnum == subnum) { if (!stralloc_copyb(&line,strnum,fmt_ulong(strnum,msg))) die_nomem(); if (!stralloc_cats(&line,":")) die_nomem(); if (!stralloc_catb(&line,strnum,fmt_uint(strnum,pmsgt->date))) die_nomem(); if (!stralloc_cats(&line,":")) die_nomem(); if (pmsgt->authnum) { pautht = authtable + pmsgt->authnum - 1; cp = pautht->auth; cp1 = cp + str_chr(cp,' '); if (cp + HASHLEN != cp1) strerr_die1x(100,ERR_BAD_INDEX); if (!stralloc_cats(&line,cp)) die_nomem(); /* hash */ } else if (!stralloc_cats(&line,"\n")) die_nomem(); if (substdio_put(&ssout,line.s,line.len) == -1) strerr_die6sys(111,FATAL,ERR_WRITE,dir,"/",fnn.s,": "); } pmsgt++; } close_proper(&ssout,fn.s,fnn.s); psubt++; } /* (no master author index) */ pautht = authtable; while (pautht->auth) { /* now the authors */ if (!stralloc_copys(&fn,"archive/authors/")) die_nomem(); if (!stralloc_catb(&fn,pautht->auth,2)) die_nomem(); if (!stralloc_0(&fn)) die_nomem(); if (mkdir(fn.s,0755) == -1) if (errno != error_exist) strerr_die6sys(111,FATAL,ERR_CREATE,dir,"/",fn.s,": "); fn.s[fn.len - 1] = '/'; if (!stralloc_catb(&fn,pautht->auth+2,HASHLEN-2)) die_nomem(); if (!stralloc_copy(&fnn,&fn)) die_nomem(); if (!stralloc_cats(&fnn,"n")) die_nomem(); if (!stralloc_0(&fn)) die_nomem(); if (!stralloc_0(&fnn)) die_nomem(); if ((fdn = open_trunc(fnn.s)) == -1) strerr_die4sys(111,FATAL,ERR_CREATE,fnn.s,": "); substdio_fdbuf(&ssout,write,fdn,outbuf,sizeof(outbuf)); if ((fd = open_read(fn.s)) == -1) { if (errno != error_noent) strerr_die4sys(111,FATAL,ERR_OPEN,fn.s,": "); else { /* didn't exist before: write author */ if (substdio_put(&ssout,pautht->auth,pautht->authlen) == -1) strerr_die6sys(111,FATAL,ERR_WRITE,dir,"/",fnn.s,": "); } } else { /* copy data */ substdio_fdbuf(&ssin,read,fd,inbuf,sizeof(inbuf)); lineno = 0; for (;;) { if (getln(&ssin,&line,&match,'\n') == -1) strerr_die6sys(111,FATAL,ERR_READ,dir,"/",fn.s,": "); if (!match) break; if (!lineno) { /* write author */ if (line.len < HASHLEN + 1 || line.s[HASHLEN] != ' ') flagerror = - 4; if (substdio_put(&ssout,line.s,line.len) == -1) strerr_die6sys(111,FATAL,ERR_WRITE,dir,"/",fnn.s,": "); lineno = 1; continue; } (void) scan_ulong(line.s,&msgnum); if (msgnum >= from) break; if (substdio_put(&ssout,line.s,line.len) == -1) strerr_die6sys(111,FATAL,ERR_WRITE,dir,"/",fnn.s,": "); } (void) close(fd); /* close old index */ } authnum = (unsigned long) (pautht - authtable + 1); /* idx of this auth */ pmsgt = msgtable + pautht->firstmsg - from; /* first message entry */ for (msg = pautht->firstmsg; msg <= to; msg++) { if (pmsgt->authnum == authnum) { if (!stralloc_copyb(&line,strnum,fmt_ulong(strnum,msg))) die_nomem(); if (!stralloc_cats(&line,":")) die_nomem(); if (!stralloc_catb(&line,strnum,fmt_uint(strnum,pmsgt->date))) die_nomem(); if (!stralloc_cats(&line,":")) die_nomem(); if (pmsgt->subnum) { psubt = subtable + pmsgt->subnum - 1; if (!stralloc_catb(&line,psubt->sub,psubt->sublen)) die_nomem(); } if (substdio_put(&ssout,line.s,line.len) == -1) strerr_die6sys(111,FATAL,ERR_WRITE,dir,"/",fnn.s,": "); } pmsgt++; } close_proper(&ssout,fn.s,fnn.s); pautht++; } } int main(argc,argv) int argc; char **argv; { unsigned long archnum = 0L; unsigned long to = 0L; unsigned long max; int fd; int fdlock; int flagcreate = 0; int flagsyncall = 0; int opt; msgentry *msgtable; subentry *subtable; authentry *authtable; dateentry *datetable; (void) umask(022); sig_pipeignore(); while ((opt = getopt(argc,argv,"cCf:FsSt:TvV")) != opteof) switch (opt) { case 'c': flagcreate = 1; flagsync = 0; break; /* start at beginning of archive */ case 'C': flagcreate = 0; break; /* Do only archnum+1 => num */ case 'f': if (optarg) { (void) scan_ulong(optarg,&archnum); archnum = (archnum / 100) * 100; } flagsync = 0; break; case 'F': archnum = 0; break; case 's': flagsyncall = 1; break; case 'S': flagsyncall = 0; break; case 't': if (optarg) { (void) scan_ulong(optarg,&to); } flagsync = 0; break; case 'T': to = 0; break; case 'v': case 'V': strerr_die2x(0,"ezmlm-archive version: ",EZIDX_VERSION); default: die_usage(); } if (flagsyncall) flagsync = 1; /* overrides */ dir = argv[optind++]; if (!dir) die_usage(); if (chdir(dir) == -1) strerr_die4sys(111,FATAL,ERR_SWITCH,dir,": "); if (mkdir("archive/threads",0755) == -1) if (errno != error_exist) strerr_die4sys(111,FATAL,ERR_CREATE,dir,"/archive/threads: "); if (mkdir("archive/subjects",0755) == -1) if (errno != error_exist) strerr_die4sys(111,FATAL,ERR_CREATE,dir,"/archive/subjects: "); if (mkdir("archive/authors",0755) == -1) if (errno != error_exist) strerr_die4sys(111,FATAL,ERR_CREATE,dir,"/archive/authors: "); /* Lock list to assure that no ezmlm-send is working on it */ /* and that the "num" message is final */ fdlock = open_append("lock"); if (fdlock == -1) strerr_die2sys(111,FATAL,ERR_OPEN_LOCK); if (lock_ex(fdlock) == -1) { (void) close(fdlock); strerr_die2sys(111,FATAL,ERR_OBTAIN_LOCK); } /* get num */ if (!getconf_line(&num,"num",0,FATAL,dir)) strerr_die1x(100,ERR_EMPTY_LIST); (void) close(fdlock); if (!stralloc_0(&num)) die_nomem(); /* parse num */ (void) scan_ulong(num.s,&max); if (!to || to > max) to = max; fdlock = open_append("archive/lock"); /* lock index */ if (fdlock == -1) strerr_die4sys(111,FATAL,ERR_OPEN,dir,"/archive/lock: "); if (lock_ex(fdlock) == -1) { (void) close(fdlock); strerr_die4sys(111,FATAL,ERR_OBTAIN,dir,"/archive/lock: "); } if (!flagcreate && !archnum) { /* adjust archnum (from) / to */ if (getconf_line(&num,"archnum",0,FATAL,dir)) { if (!stralloc_0(&num)) die_nomem(); (void) scan_ulong(num.s,&archnum); archnum++; } } if (archnum > to) _exit(0); /* nothing to do */ /* do the subject threading */ idx_mkthreads(&msgtable,&subtable,&authtable,&datetable, archnum,to,max,0,FATAL); /* update the index */ write_threads(msgtable,subtable,authtable,datetable,archnum,to); /* update archnum */ if ((fd = open_trunc("archnumn")) == -1) strerr_die4sys(111,FATAL,ERR_CREATE,dir,"/archnumn: "); substdio_fdbuf(&ssnum,write,fd,numbuf,sizeof(numbuf)); if (substdio_put(&ssnum,strnum,fmt_ulong(strnum,to)) == -1) strerr_die6sys(111,FATAL,ERR_WRITE,dir,"/",fnn.s,": "); if (substdio_puts(&ssnum,"\n") == -1) strerr_die6sys(111,FATAL,ERR_WRITE,dir,"/",fnn.s,": "); close_proper(&ssnum,"archnum","archnumn"); switch (flagerror) { case 0: _exit(0); /* go bye-bye */ case -1: strerr_die2x(99,WARNING,"threads entry with illegal format"); case -2: strerr_die2x(99,WARNING,"thread in index, but threadfile missing"); case -3: strerr_die2x(99,WARNING,"a subject file lacks subject"); case -4: strerr_die2x(99,WARNING,"an author file lacks author/hash"); case -5: strerr_die2x(99,WARNING,"threads entry lacks message count"); default: strerr_die2x(99,WARNING,"something happened that isn't quite right"); } }