Import ezmlm-idx 0.40
[ezmlm] / ezmlm-make.c
index 09d2b3e..11fc6f4 100644 (file)
@@ -1,3 +1,6 @@
+/*Id: ezmlm-make.c,v 1.31 1997/12/08 23:44:02 lindberg Exp lindberg $*/
+/*$Name: ezmlm-idx-040 $*/
+
 #include <sys/types.h>
 #include <sys/time.h>
 #include "sgetopt.h"
 #include "substdio.h"
 #include "str.h"
 #include "auto_bin.h"
+#include "getln.h"
+#include "error.h"
+#include "lock.h"
+#include "errtxt.h"
+#include "idx.h"
+
+                       /* defaults. All other flags are false = 0 */
+char  *defflags="ap";          /* archived list -a */
+                               /* public list -p */
+                               /* no ezmlm-archive -I */
+                               /* no text edit for remote admin -D */
+                               /* not in edit mode -E */
+                               /* no subs list for remote admin -L */
+                               /* no remote admin -R */
+                               /* no message moderation -M */
+                               /* no subscription moderation -S */
+                               /* don't use .ezmlmrc from dot-file dir -C */
+                               /* no prefix -F */
+                               /* no trailer -T */
+
+#define NO_FLAGS ('z' - 'a' + 1)
+int flags[NO_FLAGS];           /* holds flags */
+
+char *popt[10];
+stralloc dotplus = {0};
+stralloc dirplus = {0};
+stralloc line = {0};
 
 #define FATAL "ezmlm-make: fatal: "
+#define WARNING "ezmlm-make: warning: "
 
 void die_usage()
 {
-  strerr_die1x(100,"ezmlm-make: usage: ezmlm-make [ -aApP ] dir dot local host");
+ strerr_die1x(100,
+  "ezmlm-make: usage: ezmlm-make [-+] [ -a..zA..Z03..9 ] dir dot local host");
 }
 void die_relative()
 {
-  strerr_die2x(100,FATAL,"dir must start with slash");
+  strerr_die2x(100,FATAL,ERR_SLASH);
 }
 void die_newline()
 {
-  strerr_die2x(100,FATAL,"newlines not allowed");
+  strerr_die2x(100,FATAL,ERR_NEWLINE);
 }
 void die_quote()
 {
-  strerr_die2x(100,FATAL,"quotes not allowed");
+  strerr_die2x(100,FATAL,ERR_QUOTE);
 }
 void die_nomem()
 {
-  strerr_die2x(111,FATAL,"out of memory");
+  strerr_die2x(111,FATAL,ERR_NOMEM);
+}
+
+void die_read()
+{
+  strerr_die4sys(111,FATAL,ERR_READ,dirplus.s,": ");
 }
 
+stralloc cmdline = {0};
+stralloc outline = {0};
+substdio sstext;
+char textbuf[1024];
+
+stralloc fname = {0};          /* file name */
+stralloc oldfname = {0};       /* file name from prevoius tag */
+stralloc dname = {0};          /* directory name */
+stralloc lname = {0};          /* link name */
+stralloc template = {0};       /* template file name */
+stralloc ext1 = {0};           /* dot = dir/.qmail-ext1-ext2-list */
+stralloc ext2 = {0};
+stralloc f = {0};
 stralloc key = {0};
 struct timeval tv;
+char sz[2] = "?";
 
 void keyadd(u)
 unsigned long u;
 {
   char ch;
-  ch = u; if (!stralloc_append(&key,&ch)) die_nomem(); u >>= 8;
-  ch = u; if (!stralloc_append(&key,&ch)) die_nomem(); u >>= 8;
-  ch = u; if (!stralloc_append(&key,&ch)) die_nomem(); u >>= 8;
-  ch = u; if (!stralloc_append(&key,&ch)) die_nomem();
+  ch = (char) u; if (!stralloc_append(&key,&ch)) die_nomem(); u >>= 8;
+  ch = (char) u; if (!stralloc_append(&key,&ch)) die_nomem(); u >>= 8;
+  ch = (char) u; if (!stralloc_append(&key,&ch)) die_nomem(); u >>= 8;
+  ch = (char) u; if (!stralloc_append(&key,&ch)) die_nomem();
 }
 
 void keyaddtime()
@@ -54,11 +105,8 @@ void keyaddtime()
 
 char *dir;
 char *dot;
-char *local;
-char *host;
-
-stralloc dotplus = {0};
-stralloc dirplus = {0};
+char *local = (char *) 0;
+char *host = (char *) 0;
 
 void dirplusmake(slash)
 char *slash;
@@ -76,8 +124,12 @@ char *slash;
   if (!stralloc_cats(&dotplus,dash)) die_nomem();
   if (!stralloc_0(&dotplus)) die_nomem();
   dirplusmake(slash);
+  if (flags['e' - 'a'])
+    if (unlink(dotplus.s) == -1)
+      if (errno != error_noent)
+        strerr_die4x(111,FATAL,ERR_DELETE,dotplus.s,": ");
   if (symlink(dirplus.s,dotplus.s) == -1)
-    strerr_die4sys(111,FATAL,"unable to create ",dotplus.s,": ");
+    strerr_die4sys(111,FATAL,ERR_CREATE,dotplus.s,": ");
   keyaddtime();
 }
 
@@ -86,14 +138,15 @@ char *slash;
 {
   dirplusmake(slash);
   if (mkdir(dirplus.s,0755) == -1)
-    strerr_die4sys(111,FATAL,"unable to create ",dirplus.s,": ");
+    if ((errno != error_exist) || !flags['e' - 'a'])
+      strerr_die4sys(111,FATAL,ERR_CREATE,dirplus.s,": ");
   keyaddtime();
 }
 
 substdio ss;
 char ssbuf[SUBSTDIO_OUTSIZE];
 
-void fopen(slash)
+void f_open(slash)
 char *slash;
 {
   int fd;
@@ -101,335 +154,508 @@ char *slash;
   dirplusmake(slash);
   fd = open_trunc(dirplus.s);
   if (fd == -1)
-    strerr_die4sys(111,FATAL,"unable to create ",dirplus.s,": ");
+    strerr_die4sys(111,FATAL,ERR_CREATE,dirplus.s,": ");
 
   substdio_fdbuf(&ss,write,fd,ssbuf,sizeof(ssbuf));
 }
 
-void fput(buf,len)
+void f_put(buf,len)
 char *buf;
 unsigned int len;
 {
   if (substdio_bput(&ss,buf,len) == -1)
-    strerr_die4sys(111,FATAL,"unable to write to ",dirplus.s,": ");
+    strerr_die4sys(111,FATAL,ERR_WRITE,dirplus.s,": ");
 }
-void fputs(buf)
+void f_puts(buf)
 char *buf;
 {
   if (substdio_bputs(&ss,buf) == -1)
-    strerr_die4sys(111,FATAL,"unable to write to ",dirplus.s,": ");
+    strerr_die4sys(111,FATAL,ERR_WRITE,dirplus.s,": ");
 }
 
-void fclose()
+void f_close()
 {
   if (substdio_flush(&ss) == -1)
-    strerr_die4sys(111,FATAL,"unable to write to ",dirplus.s,": ");
+    strerr_die4sys(111,FATAL,ERR_FLUSH,dirplus.s,": ");
   if (fsync(ss.fd) == -1)
-    strerr_die4sys(111,FATAL,"unable to write to ",dirplus.s,": ");
+    strerr_die4sys(111,FATAL,ERR_SYNC,dirplus.s,": ");
   if (close(ss.fd) == -1) /* NFS stupidity */
-    strerr_die4sys(111,FATAL,"unable to write to ",dirplus.s,": ");
+    strerr_die4sys(111,FATAL,ERR_CLOSE,dirplus.s,": ");
   keyaddtime();
 }
 
+void frm(slash)
+char *slash;
+{
+  dirplusmake(slash);
+  if (unlink(dirplus.s) == -1)
+    if (errno != error_noent)
+    strerr_die4sys(111,FATAL,ERR_DELETE,dirplus.s,": ");
+}
+
+
 void main(argc,argv)
 int argc;
 char **argv;
 {
+  unsigned long euid;
   int opt;
-  int flagarchived;
-  int flagpublic;
+  int flagdo;
+  int flagnot;
+  int flagover;
+  int flagnotexist;
+  int flagforce = 0;
+  int flagforce_p = 0;
+  int usecfg = 0;
+  int match;
+  unsigned int next,i,j;
+  int last;
+  unsigned int slpos,hashpos,pos;
+  int fdin,fdlock,fdtmp;
+  char *p;
+  char *oldflags = (char *) 0;
+  char *code = (char *) 0;
+  char *cfname = (char *) 0;           /* config file if spec as -C cf_file */
+  char ch;
 
-  keyadd(getpid());
-  keyadd(getppid());
-  keyadd(getuid());
-  keyadd(getgid());
+  keyadd((unsigned long) getpid());
+  keyadd((unsigned long) getppid());
+  euid = (unsigned long) geteuid();
+  keyadd(euid);
+  keyadd((unsigned long) getgid());
   gettimeofday(&tv,(struct timezone *) 0);
   keyadd(tv.tv_sec);
 
-  umask(077);
-
-  flagarchived = 1;
-  flagpublic = 1;
+  (void) umask(077);
+       /* flags with defined use. vV for version. Others free */
 
-  while ((opt = getopt(argc,argv,"aApP")) != opteof)
-    switch(opt) {
-      case 'a': flagarchived = 1; break;
-      case 'A': flagarchived = 0; break;
-      case 'p': flagpublic = 1; break;
-      case 'P': flagpublic = 0; break;
-      default:
-       die_usage();
-    }
+  for (pos = 0; pos < (unsigned int) NO_FLAGS; pos++) {
+    flags[pos] = 0;
+  }
+  for (pos = 0; pos < 10; popt[pos++] = (char *) 0);
+
+  while ((opt = getopt(argc,argv,
+   "+aAbBcC:dDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ0:3:4:5:6:7:8:9:"))
+           != opteof) {
+    if (opt == 'v' || opt == 'V')
+      strerr_die2x(0,"ezmlm-make version: ezmlm-0.53+",EZIDX_VERSION);
+    if (opt =='C')     /* treat this like nl switch to allow override of -c*/
+      cfname = optarg;
+    if (opt >= 'a' && opt <= 'z') {
+      flags[opt - 'a'] = 3;            /* Dominant "set" */
+      if (opt == 'e') flagforce++;     /* two 'e' => ignore 'E' */
+    } else if (opt >= 'A' && opt <= 'Z')
+      flags[opt - 'A'] = 2;            /* Dominant "unset" */
+    else if (opt >= '0' && opt <= '9')
+      popt[opt-'0'] = optarg;
+    else if (opt == '+') {
+      flagforce_p++;           /* two '+' => ignore 'E' */
+      flags['e' - 'a'] = 3;    /* -+ implies -e */
+      usecfg = 1;
+    } else
+      die_usage();
+  }
   argv += optind;
 
-  if (!(dir = *argv++)) die_usage();
-  if (!(dot = *argv++)) die_usage();
-  if (!(local = *argv++)) die_usage();
-  if (!(host = *argv++)) die_usage();
+  if (flagforce_p > 1 || flagforce > 1)
+    flagforce = 1;
+  else
+    flagforce = 0;
 
+  if (!(dir = *argv++)) die_usage();
   if (dir[0] != '/') die_relative();
   if (dir[str_chr(dir,'\'')]) die_quote();
   if (dir[str_chr(dir,'\n')]) die_newline();
-  if (local[str_chr(local,'\n')]) die_newline();
-  if (host[str_chr(host,'\n')]) die_newline();
 
-  dcreate("");
-  dcreate("/archive");
-  dcreate("/subscribers");
-  dcreate("/bounce");
-  dcreate("/text");
+  if (flags['e' - 'a'] & 1) {  /* lock for edit */
+    dirplusmake("/lock");
+    fdlock = open_append(dirplus.s);
+    if (fdlock == -1)
+      strerr_die4sys(111,FATAL,ERR_OPEN,dirplus.s,": ");
+    if (lock_ex(fdlock) == -1)
+      strerr_die4sys(111,FATAL,ERR_OBTAIN,dirplus.s,": ");
+
+                               /* for edit, try to get args from dir/config */
+    dirplusmake("/config");
+    if ((fdin = open_read(dirplus.s)) == -1) {
+      if (errno != error_noent) die_read();
+    } else {
+      substdio_fdbuf(&sstext,read,fdin,textbuf,sizeof(textbuf));
+      for (;;) {
+       if (getln(&sstext,&line,&match,'\n') == -1) die_read();
+       if (!match) break;
+       if (line.s[0] == '#') continue;
+       if (line.len == 1) break;
+       if (line.s[1] != ':') break;
+       line.s[line.len - 1] = '\0';
+             if (!stralloc_cat(&cmdline,&line)) die_nomem();
+      }
+      close(fdin);
+      pos = 0;
+      while (pos < cmdline.len) {
+       ch = cmdline.s[pos];
+       pos += 2;
+       switch (ch) {
+         case 'X': if (euid && !flags['c' - 'a'] && (!cfname))
+                       cfname = cmdline.s + pos;       /* cmdline overrides */
+                   break;      /* for safety: ignore if root */
+          case 'T': dot = cmdline.s + pos; break;
+          case 'L': local = cmdline.s + pos; break;
+          case 'H': host = cmdline.s + pos; break;
+          case 'C': code = cmdline.s + pos; break;
+          case 'D': break;     /* no reason to check */
+          case 'F': oldflags = cmdline.s + pos; break;
+          default:
+                 if (ch == '0' || (ch >= '3' && ch <= '9')) {
+                   if (usecfg && !popt[ch - '0'])
+                     popt[ch - '0'] = cmdline.s + pos;
+                 } else
+                   strerr_die4x(111,FATAL,dirplus.s,ERR_SYNTAX,
+                        cmdline.s+pos);
+                 break;
+        }
+        pos += str_len(cmdline.s + pos) + 1;
+      }
+    }
+  }
 
+  if (p = *argv++) {
+    dot = p;
+    if (p = *argv++) {
+      if (!local || str_diff(local,p))
+       flagforce = 1;          /* must rewrite if list name changed */
+      local = p;
+      if (p = *argv++) {
+       if (!host || str_diff(host,p))
+         flagforce = 1;        /* must rewrite if list name changed */
+        host = p;
+        if (p = *argv++) {
+          code = p;
+        }
+      }
+    }
+  }
+  if (!dot || !local || !host) die_usage();
+  if (dot[0] != '/') die_relative();           /* force absolute dot */
+
+                       /* use flags from config, overridden with new values */
+                       /* if there are old flags, we're in "edit" and "-+" */
+                       /* Previous versions only wrote _set_ flags to */
+                       /* to DIR/confiag. We need to make sure that we */
+                       /* don't apply the defaults for non-specified ones! */
+  if (usecfg && oldflags && flags['e' - 'a']) {
+    while ((ch = *(oldflags++))) {
+      if (ch >= 'a' && ch <= 'z') {            /* unset flags ignored */
+        if (ch != 'e')
+          if (!flags[ch - 'a'])                        /* cmd line overrides */
+           flags[ch - 'a'] = 1;
+      }
+    }
+  }
 
-  linkdotdir("-owner","/owner");
-  linkdotdir("-default","/manager");
-  linkdotdir("-return-default","/bouncer");
-  linkdotdir("","/editor");
+  if (!usecfg) {                               /* apply defaults */
+    while (( ch = *(defflags++))) {            /* gets used up! */
+      if (ch >= 'a' && ch <= 'z') {            /* defensive! */
+       if (!flags[ch - 'a'])                   /* cmdline still overrides */
+         flags[ch - 'a'] = 1;
+      }
+    }
+  }
 
-  fopen("/lock"); fclose();
-  fopen("/lockbounce"); fclose();
-  if (flagpublic) {
-    fopen("/public"); fclose();
+  for (pos = 0; pos < (unsigned int) NO_FLAGS; pos++) {        /* set real flags */
+    if (flags[pos] & 2)                                /* 2 = "dominant" 0 */
+      flags[pos] = flags[pos] & 1;             /* 3 = "dominant" 1 */
   }
-  if (flagarchived) {
-    fopen("/archived"); fclose();
+
+  if (local[str_chr(local,'\n')]) die_newline();
+  if (host[str_chr(host,'\n')]) die_newline();
+
+       /* build 'f' for <#F#> */
+  if (!stralloc_ready(&f,28)) die_nomem();
+  if (!stralloc_copys(&f,"-")) die_nomem();
+  for (ch = 0; ch <= 'z' - 'a'; ch++) {                /* build string with flags */
+    if (flags[ch])
+      sz[0] = 'a' + ch;
+    else
+      sz[0] = 'A' + ch;
+    if (!stralloc_append(&f,sz)) die_nomem();
+  }
+
+  fdin = -1;   /* assure failure for .ezmlmrc in case flags['c'-'a'] = 0 */
+  slpos = str_len(dot);
+  while ((--slpos > 0) && dot[slpos] != '/');
+  if (dot[slpos] == '/') {
+    if (!stralloc_copyb(&template,dot,slpos+1)) die_nomem();   /* dot dir */
+    slpos += str_chr(dot+slpos,'-');
+    if (dot[slpos]) {
+      slpos++;
+      pos = slpos + str_chr(dot+slpos,'-');
+      if (dot[pos]) {
+        if (!stralloc_copyb(&ext1,dot+slpos,pos-slpos)) die_nomem();
+        pos++;
+        slpos = pos + str_chr(dot+pos,'-');
+        if (dot[slpos])
+          if (!stralloc_copyb(&ext2,dot+pos,slpos-pos)) die_nomem();
+      }
+    }
+  }
+  if (!stralloc_0(&ext1)) die_nomem();
+  if (!stralloc_0(&ext2)) die_nomem();
+  popt[1] = ext1.s;
+  popt[2] = ext2.s;
+       /* if 'c', template already has the dot directory. If 'C', cfname */
+       /* (if exists and != '') points to the file name to use instead. */
+  if (flags['c'-'a'] || (cfname && *cfname)) {
+    if (!flags['c'-'a']) {     /* i.e. there is a cfname specified */
+      if (!stralloc_copys(&template,cfname)) die_nomem();
+    } else
+      if (!stralloc_cats(&template,TXT_DOTEZMLMRC)) die_nomem();
+  if (!stralloc_0(&template)) die_nomem();
+  if ((fdin = open_read(template.s)) == -1)
+    if (errno != error_noent)
+      strerr_die4sys(111,FATAL,ERR_OPEN,template.s,": ");
+    else
+      strerr_die3x(100,FATAL,template.s,ERR_NOEXIST);
+  } else {                     /* /etc/ezmlmrc */
+    if (!stralloc_copys(&template,TXT_ETC_EZMLMRC)) die_nomem();
+    if (!stralloc_0(&template)) die_nomem();
+    if ((fdin = open_read(template.s)) == -1)
+      if (errno != error_noent)
+        strerr_die4sys(111,FATAL,ERR_OPEN,template.s,": ");
+      else {                   /* ezbin/ezmlmrc */
+       if (!stralloc_copys(&template,auto_bin)) die_nomem();
+       if (!stralloc_cats(&template,TXT_EZMLMRC)) die_nomem();
+       if (!stralloc_0(&template)) die_nomem();
+       if ((fdin = open_read(template.s)) == -1)
+         if (errno != error_noent)
+           strerr_die4sys(111,FATAL,ERR_OPEN,template.s,": ");
+         else
+           strerr_die3x(100,FATAL,template.s,ERR_NOEXIST);
+      }
   }
-  fopen("/num"); fputs("0\n"); fclose();
-  fopen("/inhost"); fputs(host); fputs("\n"); fclose();
-  fopen("/outhost"); fputs(host); fputs("\n"); fclose();
-  fopen("/inlocal"); fputs(local); fputs("\n"); fclose();
-  fopen("/outlocal"); fputs(local); fputs("\n"); fclose();
-
-  fopen("/mailinglist");
-  fputs("contact ");
-  fputs(local); fputs("-help@"); fputs(host); fputs("; run by ezmlm\n");
-  fclose();
-
-  fopen("/owner");
-  fputs(dir); fputs("/Mailbox\n");
-  fputs("|"); fputs(auto_bin); fputs("/ezmlm-warn '"); fputs(dir);
-  fputs("' || exit 0\n");
-  fclose();
-
-  fopen("/manager");
-  fputs("|"); fputs(auto_bin); fputs("/ezmlm-manage '"); fputs(dir); fputs("'\n");
-  fputs("|"); fputs(auto_bin); fputs("/ezmlm-warn '"); fputs(dir);
-  fputs("' || exit 0\n");
-  fclose();
-
-  fopen("/editor");
-  fputs("|"); fputs(auto_bin); fputs("/ezmlm-reject\n");
-  fputs("|"); fputs(auto_bin); fputs("/ezmlm-send '"); fputs(dir); fputs("'\n");
-  fputs("|"); fputs(auto_bin); fputs("/ezmlm-warn '"); fputs(dir);
-  fputs("' || exit 0\n");
-  fclose();
-
-  fopen("/bouncer");
-  fputs("|"); fputs(auto_bin); fputs("/ezmlm-warn '"); fputs(dir);
-  fputs("' || exit 0\n");
-  fputs("|"); fputs(auto_bin); fputs("/ezmlm-weed\n");
-  fputs("|"); fputs(auto_bin); fputs("/ezmlm-return '"); fputs(dir); fputs("'\n");
-  fclose();
-
-  fopen("/headerremove");
-  fputs("\
-return-path\n\
-return-receipt-to\n\
-content-length\n\
-");
-  fclose();
-
-  fopen("/headeradd");
-  fclose();
-
-
-  fopen("/text/top");
-  fputs("Hi! This is the ezmlm program. I'm managing the\n");
-  fputs(local); fputs("@"); fputs(host); fputs(" mailing list.\n\n");
-  fclose();
-
-  fopen("/text/bottom");
-  fputs("\n--- Here are the ezmlm command addresses.\n\
-\n\
-I can handle administrative requests automatically.\n\
-Just send an empty note to any of these addresses:\n\n   <");
-  fputs(local); fputs("-subscribe@"); fputs(host); fputs(">:\n");
-  fputs("   Receive future messages sent to the mailing list.\n\n   <");
-  fputs(local); fputs("-unsubscribe@"); fputs(host); fputs(">:\n");
-  fputs("   Stop receiving messages.\n\n   <");
-  fputs(local); fputs("-get.12345@"); fputs(host); fputs(">:\n");
-  fputs("   Retrieve a copy of message 12345 from the archive.\n\
-\n\
-DO NOT SEND ADMINISTRATIVE REQUESTS TO THE MAILING LIST!\n\
-If you do, I won't see them, and subscribers will yell at you.\n\
-\n\
-To specify God@heaven.af.mil as your subscription address, send mail\n\
-to <");
-  fputs(local); fputs("-subscribe-God=heaven.af.mil@"); fputs(host);
-  fputs(">.\n\
-I'll send a confirmation message to that address; when you receive that\n\
-message, simply reply to it to complete your subscription.\n\
-\n");
-  fputs("\n--- Below this line is a copy of the request I received.\n\n");
-  fclose();
-
-  fopen("/text/sub-confirm");
-  fputs("To confirm that you would like\n\
-\n\
-!A\n\
-\n\
-added to this mailing list, please send an empty reply to this address:\n\
-\n\
-!R\n\
-\n\
-Your mailer should have a Reply feature that uses this address automatically.\n\
-\n\
-This confirmation serves two purposes. First, it verifies that I am able\n\
-to get mail through to you. Second, it protects you in case someone\n\
-forges a subscription request in your name.\n\
-\n");
-  fclose();
-
-  fopen("/text/unsub-confirm");
-  fputs("To confirm that you would like\n\
-\n\
-!A\n\
-\n\
-removed from this mailing list, please send an empty reply to this address:\n\
-\n\
-!R\n\
-\n\
-Your mailer should have a Reply feature that uses this address automatically.\n\
-\n\
-I haven't checked whether your address is currently on the mailing list.\n\
-To see what address you used to subscribe, look at the messages you are\n\
-receiving from the mailing list. Each message has your address hidden\n\
-inside its return path; for example, God@heaven.af.mil receives messages\n\
-with return path ...-God=heaven.af.mil.\n\
-\n");
-  fclose();
-
-  fopen("/text/sub-ok");
-  fputs("Acknowledgment: I have added the address\n\
-\n\
-!A\n\
-\n\
-to this mailing list.\n\
-\n");
-  fclose();
-
-  fopen("/text/unsub-ok");
-  fputs("Acknowledgment: I have removed the address\n\
-\n\
-!A\n\
-\n\
-from this mailing list.\n\
-\n");
-  fclose();
-
-  fopen("/text/sub-nop");
-  fputs("Acknowledgment: The address\n\
-\n\
-!A\n\
-\n\
-is on this mailing list.\n\
-\n");
-  fclose();
-
-  fopen("/text/unsub-nop");
-  fputs("Acknowledgment: The address\n\
-\n\
-!A\n\
-\n\
-is not on this mailing list.\n\
-\n");
-  fclose();
-
-  fopen("/text/sub-bad");
-  fputs("Oops, that confirmation number appears to be invalid.\n\
-\n\
-The most common reason for invalid numbers is expiration. I have to\n\
-receive confirmation of each request within ten days.\n\
-\n\
-I've set up a new confirmation number. To confirm that you would like\n\
-\n\
-!A\n\
-\n\
-added to this mailing list, please send an empty reply to this address:\n\
-\n\
-!R\n\
-\n\
-Sorry for the trouble.\n\
-\n");
-  fclose();
-
-  fopen("/text/unsub-bad");
-  fputs("Oops, that confirmation number appears to be invalid.\n\
-\n\
-The most common reason for invalid numbers is expiration. I have to\n\
-receive confirmation of each request within ten days.\n\
-\n\
-I've set up a new confirmation number. To confirm that you would like\n\
-\n\
-!A\n\
-\n\
-removed from this mailing list, please send an empty reply to this address:\n\
-\n\
-!R\n\
-\n\
-Sorry for the trouble.\n\
-\n");
-  fclose();
-
-  fopen("/text/get-bad");
-  fputs("Sorry, I don't see that message.\n\n");
-  fclose();
-
-  fopen("/text/bounce-bottom");
-  fputs("\n\
---- Below this line is a copy of the bounce message I received.\n\n");
-  fclose();
-
-  fopen("/text/bounce-warn");
-  fputs("\n\
-Messages to you seem to have been bouncing. I've attached a copy of\n\
-the first bounce message I received.\n\
-\n\
-If this message bounces too, I will send you a probe. If the probe bounces,\n\
-I will remove your address from the mailing list, without further notice.\n\
-\n");
-  fclose();
-
-  fopen("/text/bounce-probe");
-  fputs("\n\
-Messages to you seem to have been bouncing. I sent you a warning\n\
-message, but it bounced. I've attached a copy of the bounce message.\n\
-\n\
-This is a probe to check whether your address is reachable. If this\n\
-probe bounces, I will remove your address from the mailing list, without\n\
-further notice.\n\
-\n");
-  fclose();
-
-  fopen("/text/bounce-num");
-  fputs("\n\
-I've kept a list of which messages bounced from your address. Copies of\n\
-these messages may be in the archive. To get message 12345 from the\n\
-archive, send an empty note to ");
-  fputs(local); fputs("-get.12345@"); fputs(host); fputs(".\n\
-Here are the message numbers:\n\
-\n");
-  fclose();
-
-  fopen("/text/help");
-  fputs("\
-This is a generic help message. The message I received wasn't sent to\n\
-any of my command addresses.\n\
-\n");
-  fclose();
-
-  fopen("/key");
-  fput(key.s,key.len);
-  fclose();
 
+  dcreate("");         /* This is all we do, the rest is up to ezmlmrc */
+                       /* do it after opening template to avoid aborts */
+                       /* with created DIR. Well we also write DIR/key */
+                       /* at the end except in -e[dit] mode.           */
+
+  substdio_fdbuf(&sstext,read,fdin,textbuf,sizeof(textbuf));
+  if (!stralloc_0(&oldfname)) die_nomem();             /* init oldfname */
+  flagdo = 0;
+
+  if (getln(&sstext,&line,&match,'\n') == -1)
+    strerr_die4sys(111,FATAL,ERR_READ,template.s,": ");
+  if (!match)
+    strerr_die4sys(111,FATAL,ERR_READ,template.s,": ");
+  i = str_rchr(EZIDX_VERSION,'-');                     /* check version */
+  if (EZIDX_VERSION[i]) i++;
+  j = 0;
+  while (line.s[j] == EZIDX_VERSION[i] && j < line.len &&
+               EZIDX_VERSION[i] != '.' && EZIDX_VERSION[i]) {
+    i++; j++;                                          /* major */
+  }                                                    /* first minor */
+  if (EZIDX_VERSION[i] != '.' || j + 1 >= line.len ||
+               EZIDX_VERSION[i+1] != line.s[j+1])
+    strerr_warn2(WARNING,ERR_VERSION, (struct strerr *) 0);
+
+  for (;;) {
+    if (getln(&sstext,&line,&match,'\n') == -1)
+      strerr_die4sys(111,FATAL,ERR_READ,template.s,": ");
+    if (!match)
+      break;
+    if (line.s[0] == '#')                              /* comment */
+      continue;
+    if (!stralloc_0(&line)) die_nomem();
+    if (line.s[0] == '<' && line.s[1] == '/') {                /* tag */
+    if (line.s[str_chr(line.s,'.')])
+      strerr_die3x(100,FATAL,ERR_PERIOD,line.s);
+      flagdo = 1;
+      flagover = 0;
+      hashpos = 0;
+      pos = str_chr(line.s+2,'#')+2;
+      if (line.s[pos]) {
+        hashpos = pos;
+        pos++;
+        flagnot = 0;
+        while ((ch = line.s[pos]) &&
+              (line.s[pos] != '/' && line.s[pos+1] != '>')) {
+          if (ch == '^') {
+            flagnot = 1;
+            pos++;
+            continue;
+          }
+                       /* E is ignored. For files => create unless exists */
+         if (ch == 'E' && !flagnot ||  ch == 'e' && flagnot) {
+               if (flags['e' - 'a'] && !flagforce)
+           flagover = 1;               /* ignore #E & #^e, but set flagover */
+          } else if (ch >= 'a' && ch <= 'z')
+            flagdo &= (flags[ch - 'a'] ^ flagnot);
+          else if (ch >= 'A' && ch <= 'Z')
+            flagdo &= !(flags[ch - 'A'] ^ flagnot);
+          else if (ch >= '0' && ch <= '9')
+            flagdo &= (popt[ch - '0'] && *popt[ch - '0']) ^flagnot;
+          flagnot = 0;
+          pos++;
+        }
+        if (line.s[pos] != '/' || line.s[pos+1] != '>')
+          strerr_die3x(100,FATAL,ERR_ENDTAG,line.s);
+      } else {
+        flagdo = 1;
+        pos = 2;       /* name needs to be >= 1 char */
+        while (line.s[pos = str_chr(line.s+pos,'/')+pos]) {
+          if (line.s[pos+1] == '>')
+            break;
+          pos++;
+        }
+        if (!line.s[pos])
+          strerr_die3x(100,FATAL,ERR_ENDTAG,line.s);
+      }
+      if (hashpos)
+        pos = hashpos; /* points to after file name */
+
+      if (line.s[2] == '+') {                  /* mkdir */
+        if (!flagdo)
+          continue;
+        if (!stralloc_copys(&dname,"/")) die_nomem();
+        if (!stralloc_catb(&dname,line.s+3,pos-3)) die_nomem();
+        if (!stralloc_0(&dname)) die_nomem();
+        dcreate(dname.s);
+        flagdo = 0;
+        continue;
+      } else if (line.s[2] == ':') {           /* ln -s */
+        if (!flagdo)
+          continue;
+        slpos = str_chr(line.s + 3,'/') + 3;
+        if (slpos >= pos)
+          strerr_die3x(100,FATAL,ERR_LINKDIR,line.s);
+        if (!stralloc_copyb(&dname,line.s+slpos,pos-slpos)) die_nomem();
+        if (!stralloc_copyb(&lname,line.s+3,slpos-3)) die_nomem();
+        if (!stralloc_0(&dname)) die_nomem();
+        if (!stralloc_0(&lname)) die_nomem();
+        linkdotdir(lname.s,dname.s);
+        flagdo = 0;
+        continue;
+      } else if (line.s[2] == '-') {           /* rm */
+        if (!flagdo)
+          continue;
+        if (!stralloc_copys(&dname,"/")) die_nomem();
+        if (!stralloc_catb(&dname,line.s+3,pos-3)) die_nomem();
+        if (!stralloc_0(&dname)) die_nomem();
+        frm(dname.s);
+        flagdo = 0;
+        continue;
+      }
+                                               /* only plain files left */
+                                               /* first get file name */
+      if (pos > 2) {                   /* </#ai/> => add to open file */
+        if (!stralloc_copyb(&fname,line.s+1,pos-1)) die_nomem();
+        if (!stralloc_0(&fname)) die_nomem();
+      }
+
+      if (str_diff(fname.s, oldfname.s)) {
+       flagnotexist = 1;
+                       /* Treat special case of #E when editing which _should*/
+                       /* write only if the file does not exist. flagover */
+                       /* is set if we need to check */
+        if (flagover) {        /* skip if exists */
+         dirplusmake(fname.s);         /* decided by FIRST tag for file */
+         fdtmp = open_read(dirplus.s);
+         if (fdtmp == -1) {
+           if (errno != error_noent)
+             strerr_die3sys(111,ERR_OPEN,dirplus.s,": ");
+          } else {
+           flagnotexist = 0;           /* already there - don't do it */
+           close(fdtmp);
+         }
+        }
+        if (oldfname.len > 1) {
+          f_close();
+          if (!stralloc_copys(&oldfname,"")) die_nomem();
+          if (!stralloc_0(&oldfname)) die_nomem();
+          }
+          if (flagdo && flagnotexist) {
+            if (!fname.len)
+              strerr_die3x(100,FATAL,ERR_FILENAME,line.s);
+            f_open(fname.s);
+           if (!stralloc_copy(&oldfname,&fname)) die_nomem();
+          }
+        }
+       if (flagdo) flagdo = flagnotexist;
+        continue;
+    } else if (!flagdo)
+      continue;                        /* part not to go out */
+    last = -1;
+    next = 0;
+    outline.len = 0;
+    for (;;) {
+      pos = next + str_chr(line.s+next,'<');
+      if (line.s[pos] &&
+          line.s[pos+1] == '#' &&
+          line.s[pos+2] &&
+          line.s[pos+3] == '#' &&
+          line.s[pos+4] == '>') {      /* host/local */
+        if (!stralloc_catb(&outline,line.s+last+1,pos-last-1))
+                die_nomem();
+        switch (line.s[pos+2]) {
+          case 'B':            /* path to ezmlm binaries (no trailing /) */
+            if (!stralloc_cats(&outline,auto_bin)) die_nomem();
+            last = pos + 4; next = pos + 5; break;
+          case 'C':            /* digestcode */
+            if (code && *code)
+              if (!stralloc_cats(&outline,code)) die_nomem();
+            last = pos + 4; next = pos + 5; break;
+          case 'D':            /* listdir */
+            if (!stralloc_cats(&outline,dir)) die_nomem();
+            last = pos + 4; next = pos + 5; break;
+          case 'F':            /* flags */
+            if (!stralloc_cat(&outline,&f)) die_nomem();
+            last = pos + 4; next = pos + 5; break;
+          case 'H':            /* hostname */
+            if (!stralloc_cats(&outline,host)) die_nomem();
+            last = pos + 4; next = pos + 5; break;
+          case 'L':            /* local */
+            if (!stralloc_cats(&outline,local)) die_nomem();
+            last = pos + 4; next = pos + 5; break;
+          case 'T':            /* dot */
+            if (!stralloc_cats(&outline,dot)) die_nomem();
+            last = pos + 4; next = pos + 5; break;
+          case 'X':            /* config file name */
+            if (cfname)
+             if (!stralloc_cats(&outline,cfname)) die_nomem();
+            last = pos + 4; next = pos + 5; break;
+          default:             /* copy unknown tag as is for e.g. <#A#> and*/
+                               /* <#R#> to be processed by -manage/store   */
+                                /* stuff in args for <#0#> .. <#9#> */
+            if ((line.s[pos+2] >= '0') && (line.s[pos+2] <= '9')) {
+              if (popt[line.s[pos+2] - '0'])
+                if (!stralloc_cats(&outline,popt[line.s[pos+2]-'0']))
+                  die_nomem();
+            } else
+              if (!stralloc_catb(&outline,line.s+pos,5)) die_nomem();
+            last = pos + 4; next = pos + 5; break;
+        }
+      } else {                 /* not tag */
+        if (line.s[pos]) {
+          next++;
+        } else {
+          if (!stralloc_catb(&outline,line.s+last+1,line.len-last-1))
+            die_nomem();
+          f_puts(outline.s);
+          break;
+        }
+      }
+    }
+  }
+
+  close(fdin);
+  if (oldfname.len > 1)
+    f_close();
+
+  if (!flags['e' - 'a']) {     /* don't redo key when editing a list */
+    f_open("/key");
+    f_put(key.s,key.len);
+    f_close();
+  }
   _exit(0);
 }
+