chiark / gitweb /
Debianization and various other fixes.
[ezmlm] / ezmlm-make.c
1 /*Id: ezmlm-make.c,v 1.31 1997/12/08 23:44:02 lindberg Exp lindberg $*/
2 /*$Name: ezmlm-idx-040 $*/
3
4 #include <sys/types.h>
5 #include <sys/time.h>
6 #include "sgetopt.h"
7 #include "stralloc.h"
8 #include "strerr.h"
9 #include "exit.h"
10 #include "readwrite.h"
11 #include "open.h"
12 #include "substdio.h"
13 #include "str.h"
14 #include "auto_bin.h"
15 #include "getln.h"
16 #include "error.h"
17 #include "lock.h"
18 #include "errtxt.h"
19 #include "idx.h"
20
21                         /* defaults. All other flags are false = 0 */
22 char  *defflags="ap";           /* archived list -a */
23                                 /* public list -p */
24                                 /* no ezmlm-archive -I */
25                                 /* no text edit for remote admin -D */
26                                 /* not in edit mode -E */
27                                 /* no subs list for remote admin -L */
28                                 /* no remote admin -R */
29                                 /* no message moderation -M */
30                                 /* no subscription moderation -S */
31                                 /* don't use .ezmlmrc from dot-file dir -C */
32                                 /* no prefix -F */
33                                 /* no trailer -T */
34
35 #define NO_FLAGS ('z' - 'a' + 1)
36 int flags[NO_FLAGS];            /* holds flags */
37
38 char *popt[10];
39 stralloc dotplus = {0};
40 stralloc dirplus = {0};
41 stralloc line = {0};
42
43 #define FATAL "ezmlm-make: fatal: "
44 #define WARNING "ezmlm-make: warning: "
45
46 void die_usage()
47 {
48  strerr_die1x(100,
49   "ezmlm-make: usage: ezmlm-make [-+] [ -a..zA..Z03..9 ] dir dot local host");
50 }
51 void die_relative()
52 {
53   strerr_die2x(100,FATAL,ERR_SLASH);
54 }
55 void die_newline()
56 {
57   strerr_die2x(100,FATAL,ERR_NEWLINE);
58 }
59 void die_quote()
60 {
61   strerr_die2x(100,FATAL,ERR_QUOTE);
62 }
63 void die_nomem()
64 {
65   strerr_die2x(111,FATAL,ERR_NOMEM);
66 }
67
68 void die_read()
69 {
70   strerr_die4sys(111,FATAL,ERR_READ,dirplus.s,": ");
71 }
72
73 stralloc cmdline = {0};
74 stralloc outline = {0};
75 substdio sstext;
76 char textbuf[1024];
77
78 stralloc fname = {0};           /* file name */
79 stralloc oldfname = {0};        /* file name from prevoius tag */
80 stralloc dname = {0};           /* directory name */
81 stralloc lname = {0};           /* link name */
82 stralloc template = {0};        /* template file name */
83 stralloc ext1 = {0};            /* dot = dir/.qmail-ext1-ext2-list */
84 stralloc ext2 = {0};
85 stralloc f = {0};
86 stralloc key = {0};
87 struct timeval tv;
88 char sz[2] = "?";
89
90 void keyadd(u)
91 unsigned long u;
92 {
93   char ch;
94   ch = (char) u; if (!stralloc_append(&key,&ch)) die_nomem(); u >>= 8;
95   ch = (char) u; if (!stralloc_append(&key,&ch)) die_nomem(); u >>= 8;
96   ch = (char) u; if (!stralloc_append(&key,&ch)) die_nomem(); u >>= 8;
97   ch = (char) u; if (!stralloc_append(&key,&ch)) die_nomem();
98 }
99
100 void keyaddtime()
101 {
102   gettimeofday(&tv,(struct timezone *) 0);
103   keyadd(tv.tv_usec);
104 }
105
106 char *dir;
107 char *dot;
108 char *local = (char *) 0;
109 char *host = (char *) 0;
110
111 void dirplusmake(slash)
112 char *slash;
113 {
114   if (!stralloc_copys(&dirplus,dir)) die_nomem();
115   if (!stralloc_cats(&dirplus,slash)) die_nomem();
116   if (!stralloc_0(&dirplus)) die_nomem();
117 }
118
119 void linkdotdir(dash,slash)
120 char *dash;
121 char *slash;
122 {
123   if (!stralloc_copys(&dotplus,dot)) die_nomem();
124   if (!stralloc_cats(&dotplus,dash)) die_nomem();
125   if (!stralloc_0(&dotplus)) die_nomem();
126   dirplusmake(slash);
127   if (flags['e' - 'a'])
128     if (unlink(dotplus.s) == -1)
129       if (errno != error_noent)
130         strerr_die4x(111,FATAL,ERR_DELETE,dotplus.s,": ");
131   if (symlink(dirplus.s,dotplus.s) == -1)
132     strerr_die4sys(111,FATAL,ERR_CREATE,dotplus.s,": ");
133   keyaddtime();
134 }
135
136 void dcreate(slash)
137 char *slash;
138 {
139   dirplusmake(slash);
140   if (mkdir(dirplus.s,0755) == -1)
141     if ((errno != error_exist) || !flags['e' - 'a'])
142       strerr_die4sys(111,FATAL,ERR_CREATE,dirplus.s,": ");
143   keyaddtime();
144 }
145
146 substdio ss;
147 char ssbuf[SUBSTDIO_OUTSIZE];
148
149 void f_open(slash)
150 char *slash;
151 {
152   int fd;
153
154   dirplusmake(slash);
155   fd = open_trunc(dirplus.s);
156   if (fd == -1)
157     strerr_die4sys(111,FATAL,ERR_CREATE,dirplus.s,": ");
158
159   substdio_fdbuf(&ss,write,fd,ssbuf,sizeof(ssbuf));
160 }
161
162 void f_put(buf,len)
163 char *buf;
164 unsigned int len;
165 {
166   if (substdio_bput(&ss,buf,len) == -1)
167     strerr_die4sys(111,FATAL,ERR_WRITE,dirplus.s,": ");
168 }
169 void f_puts(buf)
170 char *buf;
171 {
172   if (substdio_bputs(&ss,buf) == -1)
173     strerr_die4sys(111,FATAL,ERR_WRITE,dirplus.s,": ");
174 }
175
176 void f_close()
177 {
178   if (substdio_flush(&ss) == -1)
179     strerr_die4sys(111,FATAL,ERR_FLUSH,dirplus.s,": ");
180   if (fsync(ss.fd) == -1)
181     strerr_die4sys(111,FATAL,ERR_SYNC,dirplus.s,": ");
182   if (close(ss.fd) == -1) /* NFS stupidity */
183     strerr_die4sys(111,FATAL,ERR_CLOSE,dirplus.s,": ");
184   keyaddtime();
185 }
186
187 void frm(slash)
188 char *slash;
189 {
190   dirplusmake(slash);
191   if (unlink(dirplus.s) == -1)
192     if (errno != error_noent)
193     strerr_die4sys(111,FATAL,ERR_DELETE,dirplus.s,": ");
194 }
195
196
197 void main(argc,argv)
198 int argc;
199 char **argv;
200 {
201   unsigned long euid;
202   int opt;
203   int flagdo;
204   int flagnot;
205   int flagover;
206   int flagnotexist;
207   int flagforce = 0;
208   int flagforce_p = 0;
209   int usecfg = 0;
210   int match;
211   unsigned int next,i,j;
212   int last;
213   unsigned int slpos,hashpos,pos;
214   int fdin,fdlock,fdtmp;
215   char *p;
216   char *oldflags = (char *) 0;
217   char *code = (char *) 0;
218   char *cfname = (char *) 0;            /* config file if spec as -C cf_file */
219   char ch;
220
221   keyadd((unsigned long) getpid());
222   keyadd((unsigned long) getppid());
223   euid = (unsigned long) geteuid();
224   keyadd(euid);
225   keyadd((unsigned long) getgid());
226   gettimeofday(&tv,(struct timezone *) 0);
227   keyadd(tv.tv_sec);
228
229   (void) umask(077);
230         /* flags with defined use. vV for version. Others free */
231
232   for (pos = 0; pos < (unsigned int) NO_FLAGS; pos++) {
233     flags[pos] = 0;
234   }
235   for (pos = 0; pos < 10; popt[pos++] = (char *) 0);
236
237   while ((opt = getopt(argc,argv,
238    "+aAbBcC:dDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ0:3:4:5:6:7:8:9:"))
239            != opteof) {
240     if (opt == 'v' || opt == 'V')
241       strerr_die2x(0,"ezmlm-make version: ezmlm-0.53+",EZIDX_VERSION);
242     if (opt =='C')      /* treat this like nl switch to allow override of -c*/
243       cfname = optarg;
244     if (opt >= 'a' && opt <= 'z') {
245       flags[opt - 'a'] = 3;             /* Dominant "set" */
246       if (opt == 'e') flagforce++;      /* two 'e' => ignore 'E' */
247     } else if (opt >= 'A' && opt <= 'Z')
248       flags[opt - 'A'] = 2;             /* Dominant "unset" */
249     else if (opt >= '0' && opt <= '9')
250       popt[opt-'0'] = optarg;
251     else if (opt == '+') {
252       flagforce_p++;            /* two '+' => ignore 'E' */
253       flags['e' - 'a'] = 3;     /* -+ implies -e */
254       usecfg = 1;
255     } else
256       die_usage();
257   }
258   argv += optind;
259
260   if (flagforce_p > 1 || flagforce > 1)
261     flagforce = 1;
262   else
263     flagforce = 0;
264
265   if (!(dir = *argv++)) die_usage();
266   if (dir[0] != '/') die_relative();
267   if (dir[str_chr(dir,'\'')]) die_quote();
268   if (dir[str_chr(dir,'\n')]) die_newline();
269
270   if (flags['e' - 'a'] & 1) {   /* lock for edit */
271     dirplusmake("/lock");
272     fdlock = open_append(dirplus.s);
273     if (fdlock == -1)
274       strerr_die4sys(111,FATAL,ERR_OPEN,dirplus.s,": ");
275     if (lock_ex(fdlock) == -1)
276       strerr_die4sys(111,FATAL,ERR_OBTAIN,dirplus.s,": ");
277
278                                 /* for edit, try to get args from dir/config */
279     dirplusmake("/config");
280     if ((fdin = open_read(dirplus.s)) == -1) {
281       if (errno != error_noent) die_read();
282     } else {
283       substdio_fdbuf(&sstext,read,fdin,textbuf,sizeof(textbuf));
284       for (;;) {
285         if (getln(&sstext,&line,&match,'\n') == -1) die_read();
286         if (!match) break;
287         if (line.s[0] == '#') continue;
288         if (line.len == 1) break;
289         if (line.s[1] != ':') break;
290         line.s[line.len - 1] = '\0';
291               if (!stralloc_cat(&cmdline,&line)) die_nomem();
292       }
293       close(fdin);
294       pos = 0;
295       while (pos < cmdline.len) {
296         ch = cmdline.s[pos];
297         pos += 2;
298         switch (ch) {
299           case 'X': if (euid && !flags['c' - 'a'] && (!cfname))
300                         cfname = cmdline.s + pos;       /* cmdline overrides */
301                     break;      /* for safety: ignore if root */
302           case 'T': dot = cmdline.s + pos; break;
303           case 'L': local = cmdline.s + pos; break;
304           case 'H': host = cmdline.s + pos; break;
305           case 'C': code = cmdline.s + pos; break;
306           case 'D': break;      /* no reason to check */
307           case 'F': oldflags = cmdline.s + pos; break;
308           default:
309                  if (ch == '0' || (ch >= '3' && ch <= '9')) {
310                    if (usecfg && !popt[ch - '0'])
311                      popt[ch - '0'] = cmdline.s + pos;
312                  } else
313                    strerr_die4x(111,FATAL,dirplus.s,ERR_SYNTAX,
314                         cmdline.s+pos);
315                  break;
316         }
317         pos += str_len(cmdline.s + pos) + 1;
318       }
319     }
320   }
321
322   if (p = *argv++) {
323     dot = p;
324     if (p = *argv++) {
325       if (!local || str_diff(local,p))
326         flagforce = 1;          /* must rewrite if list name changed */
327       local = p;
328       if (p = *argv++) {
329         if (!host || str_diff(host,p))
330           flagforce = 1;        /* must rewrite if list name changed */
331         host = p;
332         if (p = *argv++) {
333           code = p;
334         }
335       }
336     }
337   }
338   if (!dot || !local || !host) die_usage();
339   if (dot[0] != '/') die_relative();            /* force absolute dot */
340
341                         /* use flags from config, overridden with new values */
342                         /* if there are old flags, we're in "edit" and "-+" */
343                         /* Previous versions only wrote _set_ flags to */
344                         /* to DIR/confiag. We need to make sure that we */
345                         /* don't apply the defaults for non-specified ones! */
346   if (usecfg && oldflags && flags['e' - 'a']) {
347     while ((ch = *(oldflags++))) {
348       if (ch >= 'a' && ch <= 'z') {             /* unset flags ignored */
349         if (ch != 'e')
350           if (!flags[ch - 'a'])                 /* cmd line overrides */
351             flags[ch - 'a'] = 1;
352       }
353     }
354   }
355
356   if (!usecfg) {                                /* apply defaults */
357     while (( ch = *(defflags++))) {             /* gets used up! */
358       if (ch >= 'a' && ch <= 'z') {             /* defensive! */
359         if (!flags[ch - 'a'])                   /* cmdline still overrides */
360           flags[ch - 'a'] = 1;
361       }
362     }
363   }
364
365   for (pos = 0; pos < (unsigned int) NO_FLAGS; pos++) { /* set real flags */
366     if (flags[pos] & 2)                         /* 2 = "dominant" 0 */
367       flags[pos] = flags[pos] & 1;              /* 3 = "dominant" 1 */
368   }
369
370   if (local[str_chr(local,'\n')]) die_newline();
371   if (host[str_chr(host,'\n')]) die_newline();
372
373         /* build 'f' for <#F#> */
374   if (!stralloc_ready(&f,28)) die_nomem();
375   if (!stralloc_copys(&f,"-")) die_nomem();
376   for (ch = 0; ch <= 'z' - 'a'; ch++) {         /* build string with flags */
377     if (flags[ch])
378       sz[0] = 'a' + ch;
379     else
380       sz[0] = 'A' + ch;
381     if (!stralloc_append(&f,sz)) die_nomem();
382   }
383
384   fdin = -1;    /* assure failure for .ezmlmrc in case flags['c'-'a'] = 0 */
385   slpos = str_len(dot);
386   while ((--slpos > 0) && dot[slpos] != '/');
387   if (dot[slpos] == '/') {
388     if (!stralloc_copyb(&template,dot,slpos+1)) die_nomem();    /* dot dir */
389     slpos += str_chr(dot+slpos,'-');
390     if (dot[slpos]) {
391       slpos++;
392       pos = slpos + str_chr(dot+slpos,'-');
393       if (dot[pos]) {
394         if (!stralloc_copyb(&ext1,dot+slpos,pos-slpos)) die_nomem();
395         pos++;
396         slpos = pos + str_chr(dot+pos,'-');
397         if (dot[slpos])
398           if (!stralloc_copyb(&ext2,dot+pos,slpos-pos)) die_nomem();
399       }
400     }
401   }
402   if (!stralloc_0(&ext1)) die_nomem();
403   if (!stralloc_0(&ext2)) die_nomem();
404   popt[1] = ext1.s;
405   popt[2] = ext2.s;
406         /* if 'c', template already has the dot directory. If 'C', cfname */
407         /* (if exists and != '') points to the file name to use instead. */
408   if (flags['c'-'a'] || (cfname && *cfname)) {
409     if (!flags['c'-'a']) {      /* i.e. there is a cfname specified */
410       if (!stralloc_copys(&template,cfname)) die_nomem();
411     } else
412       if (!stralloc_cats(&template,TXT_DOTEZMLMRC)) die_nomem();
413   if (!stralloc_0(&template)) die_nomem();
414   if ((fdin = open_read(template.s)) == -1)
415     if (errno != error_noent)
416       strerr_die4sys(111,FATAL,ERR_OPEN,template.s,": ");
417     else
418       strerr_die3x(100,FATAL,template.s,ERR_NOEXIST);
419   } else {                      /* /etc/ezmlmrc */
420     if (!stralloc_copys(&template,TXT_ETC_EZMLMRC)) die_nomem();
421     if (!stralloc_0(&template)) die_nomem();
422     if ((fdin = open_read(template.s)) == -1)
423       if (errno != error_noent)
424         strerr_die4sys(111,FATAL,ERR_OPEN,template.s,": ");
425       else {                    /* ezbin/ezmlmrc */
426         if (!stralloc_copys(&template,"/usr/share/ezmlm")) die_nomem();
427         if (!stralloc_cats(&template,TXT_EZMLMRC)) die_nomem();
428         if (!stralloc_0(&template)) die_nomem();
429         if ((fdin = open_read(template.s)) == -1)
430           if (errno != error_noent)
431             strerr_die4sys(111,FATAL,ERR_OPEN,template.s,": ");
432           else
433             strerr_die3x(100,FATAL,template.s,ERR_NOEXIST);
434       }
435   }
436
437   dcreate("");          /* This is all we do, the rest is up to ezmlmrc */
438                         /* do it after opening template to avoid aborts */
439                         /* with created DIR. Well we also write DIR/key */
440                         /* at the end except in -e[dit] mode.           */
441
442   substdio_fdbuf(&sstext,read,fdin,textbuf,sizeof(textbuf));
443   if (!stralloc_0(&oldfname)) die_nomem();              /* init oldfname */
444   flagdo = 0;
445
446   if (getln(&sstext,&line,&match,'\n') == -1)
447     strerr_die4sys(111,FATAL,ERR_READ,template.s,": ");
448   if (!match)
449     strerr_die4sys(111,FATAL,ERR_READ,template.s,": ");
450   i = str_rchr(EZIDX_VERSION,'-');                      /* check version */
451   if (EZIDX_VERSION[i]) i++;
452   j = 0;
453   while (line.s[j] == EZIDX_VERSION[i] && j < line.len &&
454                 EZIDX_VERSION[i] != '.' && EZIDX_VERSION[i]) {
455     i++; j++;                                           /* major */
456   }                                                     /* first minor */
457   if (EZIDX_VERSION[i] != '.' || j + 1 >= line.len ||
458                 EZIDX_VERSION[i+1] != line.s[j+1])
459     strerr_warn2(WARNING,ERR_VERSION, (struct strerr *) 0);
460
461   for (;;) {
462     if (getln(&sstext,&line,&match,'\n') == -1)
463       strerr_die4sys(111,FATAL,ERR_READ,template.s,": ");
464     if (!match)
465       break;
466     if (line.s[0] == '#')                               /* comment */
467       continue;
468     if (!stralloc_0(&line)) die_nomem();
469     if (line.s[0] == '<' && line.s[1] == '/') {         /* tag */
470     if (line.s[str_chr(line.s,'.')])
471       strerr_die3x(100,FATAL,ERR_PERIOD,line.s);
472       flagdo = 1;
473       flagover = 0;
474       hashpos = 0;
475       pos = str_chr(line.s+2,'#')+2;
476       if (line.s[pos]) {
477         hashpos = pos;
478         pos++;
479         flagnot = 0;
480         while ((ch = line.s[pos]) &&
481               (line.s[pos] != '/' && line.s[pos+1] != '>')) {
482           if (ch == '^') {
483             flagnot = 1;
484             pos++;
485             continue;
486           }
487                         /* E is ignored. For files => create unless exists */
488           if (ch == 'E' && !flagnot ||  ch == 'e' && flagnot) {
489                 if (flags['e' - 'a'] && !flagforce)
490             flagover = 1;               /* ignore #E & #^e, but set flagover */
491           } else if (ch >= 'a' && ch <= 'z')
492             flagdo &= (flags[ch - 'a'] ^ flagnot);
493           else if (ch >= 'A' && ch <= 'Z')
494             flagdo &= !(flags[ch - 'A'] ^ flagnot);
495           else if (ch >= '0' && ch <= '9')
496             flagdo &= (popt[ch - '0'] && *popt[ch - '0']) ^flagnot;
497           flagnot = 0;
498           pos++;
499         }
500         if (line.s[pos] != '/' || line.s[pos+1] != '>')
501           strerr_die3x(100,FATAL,ERR_ENDTAG,line.s);
502       } else {
503         flagdo = 1;
504         pos = 2;        /* name needs to be >= 1 char */
505         while (line.s[pos = str_chr(line.s+pos,'/')+pos]) {
506           if (line.s[pos+1] == '>')
507             break;
508           pos++;
509         }
510         if (!line.s[pos])
511           strerr_die3x(100,FATAL,ERR_ENDTAG,line.s);
512       }
513       if (hashpos)
514         pos = hashpos;  /* points to after file name */
515
516       if (line.s[2] == '+') {                   /* mkdir */
517         if (!flagdo)
518           continue;
519         if (!stralloc_copys(&dname,"/")) die_nomem();
520         if (!stralloc_catb(&dname,line.s+3,pos-3)) die_nomem();
521         if (!stralloc_0(&dname)) die_nomem();
522         dcreate(dname.s);
523         flagdo = 0;
524         continue;
525       } else if (line.s[2] == ':') {            /* ln -s */
526         if (!flagdo)
527           continue;
528         slpos = str_chr(line.s + 3,'/') + 3;
529         if (slpos >= pos)
530           strerr_die3x(100,FATAL,ERR_LINKDIR,line.s);
531         if (!stralloc_copyb(&dname,line.s+slpos,pos-slpos)) die_nomem();
532         if (!stralloc_copyb(&lname,line.s+3,slpos-3)) die_nomem();
533         if (!stralloc_0(&dname)) die_nomem();
534         if (!stralloc_0(&lname)) die_nomem();
535         linkdotdir(lname.s,dname.s);
536         flagdo = 0;
537         continue;
538       } else if (line.s[2] == '-') {            /* rm */
539         if (!flagdo)
540           continue;
541         if (!stralloc_copys(&dname,"/")) die_nomem();
542         if (!stralloc_catb(&dname,line.s+3,pos-3)) die_nomem();
543         if (!stralloc_0(&dname)) die_nomem();
544         frm(dname.s);
545         flagdo = 0;
546         continue;
547       }
548                                                 /* only plain files left */
549                                                 /* first get file name */
550       if (pos > 2) {                    /* </#ai/> => add to open file */
551         if (!stralloc_copyb(&fname,line.s+1,pos-1)) die_nomem();
552         if (!stralloc_0(&fname)) die_nomem();
553       }
554
555       if (str_diff(fname.s, oldfname.s)) {
556         flagnotexist = 1;
557                         /* Treat special case of #E when editing which _should*/
558                         /* write only if the file does not exist. flagover */
559                         /* is set if we need to check */
560         if (flagover) { /* skip if exists */
561           dirplusmake(fname.s);         /* decided by FIRST tag for file */
562           fdtmp = open_read(dirplus.s);
563           if (fdtmp == -1) {
564             if (errno != error_noent)
565               strerr_die3sys(111,ERR_OPEN,dirplus.s,": ");
566           } else {
567             flagnotexist = 0;           /* already there - don't do it */
568             close(fdtmp);
569           }
570         }
571         if (oldfname.len > 1) {
572           f_close();
573           if (!stralloc_copys(&oldfname,"")) die_nomem();
574           if (!stralloc_0(&oldfname)) die_nomem();
575           }
576           if (flagdo && flagnotexist) {
577             if (!fname.len)
578               strerr_die3x(100,FATAL,ERR_FILENAME,line.s);
579             f_open(fname.s);
580            if (!stralloc_copy(&oldfname,&fname)) die_nomem();
581           }
582         }
583         if (flagdo) flagdo = flagnotexist;
584         continue;
585     } else if (!flagdo)
586       continue;                 /* part not to go out */
587     last = -1;
588     next = 0;
589     outline.len = 0;
590     for (;;) {
591       pos = next + str_chr(line.s+next,'<');
592       if (line.s[pos] &&
593           line.s[pos+1] == '#' &&
594           line.s[pos+2] &&
595           line.s[pos+3] == '#' &&
596           line.s[pos+4] == '>') {       /* host/local */
597         if (!stralloc_catb(&outline,line.s+last+1,pos-last-1))
598                 die_nomem();
599         switch (line.s[pos+2]) {
600           case 'B':             /* path to ezmlm binaries (no trailing /) */
601             if (!stralloc_cats(&outline,auto_bin)) die_nomem();
602             last = pos + 4; next = pos + 5; break;
603           case 'C':             /* digestcode */
604             if (code && *code)
605               if (!stralloc_cats(&outline,code)) die_nomem();
606             last = pos + 4; next = pos + 5; break;
607           case 'D':             /* listdir */
608             if (!stralloc_cats(&outline,dir)) die_nomem();
609             last = pos + 4; next = pos + 5; break;
610           case 'F':             /* flags */
611             if (!stralloc_cat(&outline,&f)) die_nomem();
612             last = pos + 4; next = pos + 5; break;
613           case 'H':             /* hostname */
614             if (!stralloc_cats(&outline,host)) die_nomem();
615             last = pos + 4; next = pos + 5; break;
616           case 'L':             /* local */
617             if (!stralloc_cats(&outline,local)) die_nomem();
618             last = pos + 4; next = pos + 5; break;
619           case 'T':             /* dot */
620             if (!stralloc_cats(&outline,dot)) die_nomem();
621             last = pos + 4; next = pos + 5; break;
622           case 'X':             /* config file name */
623             if (cfname)
624               if (!stralloc_cats(&outline,cfname)) die_nomem();
625             last = pos + 4; next = pos + 5; break;
626           default:              /* copy unknown tag as is for e.g. <#A#> and*/
627                                 /* <#R#> to be processed by -manage/store   */
628                                 /* stuff in args for <#0#> .. <#9#> */
629             if ((line.s[pos+2] >= '0') && (line.s[pos+2] <= '9')) {
630               if (popt[line.s[pos+2] - '0'])
631                 if (!stralloc_cats(&outline,popt[line.s[pos+2]-'0']))
632                   die_nomem();
633             } else
634               if (!stralloc_catb(&outline,line.s+pos,5)) die_nomem();
635             last = pos + 4; next = pos + 5; break;
636         }
637       } else {                  /* not tag */
638         if (line.s[pos]) {
639           next++;
640         } else {
641           if (!stralloc_catb(&outline,line.s+last+1,line.len-last-1))
642             die_nomem();
643           f_puts(outline.s);
644           break;
645         }
646       }
647     }
648   }
649
650   close(fdin);
651   if (oldfname.len > 1)
652     f_close();
653
654   if (!flags['e' - 'a']) {      /* don't redo key when editing a list */
655     f_open("/key");
656     f_put(key.s,key.len);
657     f_close();
658   }
659   _exit(0);
660 }
661