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