1 /*
2 * (c) Copyright 1990, Kim Fabricius Storm. All rights reserved.
3 * Copyright (c) 1996-2005 Michael T Pins. All rights reserved.
4 *
5 * Read presentation sequence file
6 */
7
8 #include <stdlib.h>
9 #include <string.h>
10 #include <ctype.h>
11 #include "config.h"
12 #include "global.h"
13 #include "db.h"
14 #include "debug.h"
15 #include "macro.h"
16 #include "nn.h"
17 #include "sequence.h"
18 #include "nn_term.h"
19 #include "variable.h"
20
21 /* sequence.c */
22
23 static int enter_sequence(int mode, group_header * gh);
24 static void faked_entry(char *name, flag_type flag);
25 static void end_sequence(void);
26 static int visit_presentation_file(char *directory, char *seqfile, FILE * hook);
27
28 group_header *group_sequence;
29 char *read_mail = NULL;
30 int also_subgroups = 1;
31 int hex_group_args = 0;
32
33 static int seq_break_enabled = 1; /* !! enabled */
34 static int ignore_done_flag = 0; /* % toggle */
35
36 static group_header *tail_sequence = NULL;
37 static group_header *final_sequence = NULL;
38
39 static int gs_more_groups;
40
41 extern group_header *rc_sequence;
42
43 int
only_folder_args(char ** args)44 only_folder_args(char **args)
45 {
46 register char *arg;
47
48 while ((arg = *args++)) {
49 if (*arg == '+' || *arg == '~' || *arg == '/')
50 continue;
51 if (file_exist(arg, "fr"))
52 continue;
53 return 0;
54 }
55 return 1;
56 }
57
58
59 #define SHOW_NORMAL 0 /* : put this in at current pos */
60 #define SHOW_FIRST 1 /* < : show these groups first */
61 #define SHOW_LAST 2 /* > : show this as late as possible */
62 #define IGNORE_ALWAYS 3 /* ! : ignore these groups completely */
63 #define IGN_UNLESS_RC 4 /* !:X ignore these groups unless in rc */
64 #define IGN_UNLESS_NEW 5 /* !:O ignore these groups unless new */
65 #define IGN_UNL_RC_NEW 6 /* !:U ignore unsubscribed */
66 #define IGN_IF_NEW 7 /* !:N ignore these groups if new */
67
68 #define SHOW_MODES " <>!-?*"
69
70 static int
enter_sequence(int mode,group_header * gh)71 enter_sequence(int mode, group_header * gh)
72 {
73
74 #ifdef SEQ_TEST
75 if (Debug & SEQ_TEST && mode != SHOW_NORMAL)
76 tprintf("SEQ(%c), %s\n", SHOW_MODES[mode], gh->group_name);
77 #endif
78
79 if (gh->master_flag & M_IGNORE_GROUP)
80 return 0;
81 if (ignore_done_flag) {
82 if (gh->group_flag & G_SEQUENCE)
83 return 0;
84 } else if (gh->group_flag & G_DONE)
85 return 0;
86
87 switch (mode) {
88 case IGN_UNLESS_NEW:
89 if ((gh->group_flag & G_NEW) == 0)
90 gh->group_flag |= G_DONE;
91 return 0;
92
93 case IGN_IF_NEW:
94 if (gh->group_flag & G_NEW)
95 gh->group_flag |= G_DONE;
96 return 0;
97
98 case IGN_UNL_RC_NEW:
99 if (gh->group_flag & G_NEW)
100 return 0;
101 if (gh->newsrc_line == NULL || (gh->group_flag & G_UNSUBSCRIBED))
102 gh->group_flag |= G_DONE;
103 return 0;
104
105 case IGN_UNLESS_RC:
106 if (gh->newsrc_line == NULL || (gh->group_flag & (G_UNSUBSCRIBED | G_NEW)))
107 gh->group_flag |= G_DONE;
108 return 0;
109
110 case IGNORE_ALWAYS:
111 gh->group_flag |= G_DONE;
112 return 0;
113
114 default:
115 gh->group_flag |= G_DONE;
116 break;
117 }
118
119 gh->group_flag |= G_SEQUENCE;
120
121 if (gh->master_flag & M_NO_DIRECTORY)
122 return 0; /* for nntidy -s */
123
124 switch (mode) {
125 case SHOW_FIRST:
126 if (tail_sequence) {
127 gh->next_group = group_sequence;
128 group_sequence = gh;
129 break;
130 }
131 /* FALLTHRU */
132 case SHOW_NORMAL:
133 if (tail_sequence)
134 tail_sequence->next_group = gh;
135 else
136 group_sequence = gh;
137 tail_sequence = gh;
138 break;
139
140 case SHOW_LAST:
141 gh->next_group = final_sequence;
142 final_sequence = gh;
143 break;
144 }
145 return 1;
146 }
147
148
149 static void
faked_entry(char * name,flag_type flag)150 faked_entry(char *name, flag_type flag)
151 {
152 group_header *gh;
153
154 gh = newobj(group_header, 1);
155
156 gh->group_name = name;
157 gh->group_flag = flag | G_FAKED;
158 gh->master_flag = 0;
159
160 /* "invent" an unread article for read_news */
161 gh->last_article = 1;
162 gh->last_db_article = 2;
163
164 enter_sequence(SHOW_NORMAL, gh);
165 }
166
167 static void
end_sequence(void)168 end_sequence(void)
169 {
170 register group_header *gh, *backp;
171 register int seq_ix;
172
173 if (tail_sequence)
174 tail_sequence->next_group = NULL;
175
176 /* set up backward pointers */
177
178 backp = NULL;
179 seq_ix = 0;
180 Loop_Groups_Sequence(gh) {
181 gh->preseq_index = ++seq_ix;
182 gh->prev_group = backp;
183 backp = gh;
184 }
185
186 #ifdef SEQ_DUMP
187 if (Debug & SEQ_DUMP) {
188 for (gh = group_sequence; gh; gh = gh->next_group)
189 tprintf("%s\n", gh->group_name);
190 tputc(NL);
191
192 nn_exit(0);
193 }
194 #endif
195
196 }
197
198
199 #ifdef MAIL_READING
200 static int
mail_check(void)201 mail_check(void)
202 {
203 static group_header mail_group;
204 struct stat st;
205
206 if (read_mail == NULL)
207 return;
208 if (stat(read_mail, &st) < 0)
209 return;
210 if (st.st_size == 0 || st.st_mtime < st.st_atime)
211 return;
212
213 mail_group.group_name = read_mail;
214 gh->group_flag = G_FOLDER | G_MAILBOX | G_FAKED;
215 gh->master_flag = 0;
216
217 /* "invent" an unread article for read_news */
218 gh->last_article = 1;
219 gh->last_db_article = 2;
220
221
222 if (tail_sequence) {
223 mail_group.next_group = group_sequence;
224 group_sequence = mail_group;
225 } else
226 enter_sequence(SHOW_NORMAL, &mail_group);
227 }
228
229 #endif
230
231
232
233 static int
visit_presentation_file(char * directory,char * seqfile,FILE * hook)234 visit_presentation_file(char *directory, char *seqfile, FILE * hook)
235 {
236 register FILE *sf;
237 register int c;
238 register group_header *gh;
239 group_header *mp_group;
240 char group[FILENAME], *gname;
241 char savefile[FILENAME], *dflt_save, *enter_macro;
242 register char *gp;
243 int mode, merge_groups;
244
245 if (gs_more_groups == 0)
246 return 0;
247
248 if (hook != NULL)
249 sf = hook; /* hook to init file */
250 else if ((sf = open_file(relative(directory, seqfile), OPEN_READ)) == NULL)
251 return 0;
252
253 #ifdef SEQ_TEST
254 if (Debug & SEQ_TEST)
255 tprintf("Sequence file %s/%s\n", directory, seqfile);
256 #endif
257
258 mode = SHOW_NORMAL;
259 savefile[0] = NUL;
260 dflt_save = NULL;
261
262 while (gs_more_groups) {
263
264 if ((c = getc(sf)) == EOF)
265 break;
266 if (!isascii(c) || isspace(c))
267 continue;
268
269 switch (c) {
270 case '!':
271 mode = IGNORE_ALWAYS;
272 if ((c = getc(sf)) == EOF)
273 continue;
274 if (c == '!') {
275 if (seq_break_enabled) {
276 fclose(sf);
277 return 1;
278 }
279 mode = SHOW_NORMAL;
280 continue;
281 }
282 if (c == ':') {
283 if ((c = getc(sf)) == EOF)
284 continue;
285 if (!isascii(c) || isspace(c) || !isupper(c))
286 continue;
287 switch (c) {
288 case 'O':
289 mode = IGN_UNLESS_NEW;
290 continue;
291 case 'N':
292 mode = IGN_IF_NEW;
293 continue;
294 case 'U':
295 mode = IGN_UNL_RC_NEW;
296 continue;
297 case 'X':
298 mode = IGN_UNLESS_RC;
299 continue;
300 default:
301 /* should give error here */
302 mode = SHOW_NORMAL;
303 continue;
304 }
305 }
306 ungetc(c, sf);
307 continue;
308
309 case '<':
310 mode = SHOW_FIRST;
311 continue;
312
313 case '>':
314 mode = SHOW_LAST;
315 continue;
316
317 case '%':
318 ignore_done_flag = !ignore_done_flag;
319 continue;
320
321 case '@':
322 seq_break_enabled = 0;
323 mode = SHOW_NORMAL;
324 continue;
325
326 case '#':
327 do
328 c = getc(sf);
329 while (c != EOF && c != NL);
330 mode = SHOW_NORMAL;
331 continue;
332
333 }
334
335 gp = group;
336 merge_groups = 0;
337 do {
338 *gp++ = c;
339 if (c == ',')
340 merge_groups = 1;
341 c = getc(sf);
342 } while (c != EOF && isascii(c) && !isspace(c));
343
344 *gp = NUL;
345
346 while (c != EOF && (!isascii(c) || isspace(c)))
347 c = getc(sf);
348 if (c == '+' || c == '~' || c == '/') {
349 gp = savefile;
350 if (c == '+') {
351 c = getc(sf);
352 if (c == EOF || (isascii(c) && isspace(c)))
353 goto use_same_savefile;
354 *gp++ = '+';
355 }
356 do {
357 *gp++ = c;
358 c = getc(sf);
359 } while (c != EOF && isascii(c) && !isspace(c));
360 *gp = NUL;
361 dflt_save = savefile[0] ? copy_str(savefile) : NULL;
362 } else
363 dflt_save = NULL;
364
365 use_same_savefile:
366 while (c != EOF && (!isascii(c) || isspace(c)))
367 c = getc(sf);
368 if (c == '(') {
369 enter_macro = parse_enter_macro(sf, getc(sf));
370 } else {
371 enter_macro = NULL;
372 if (c != EOF)
373 ungetc(c, sf);
374 }
375
376 mp_group = NULL;
377 for (gp = group; *gp;) {
378 gname = gp;
379 if (merge_groups) {
380 while (*gp && *gp != ',')
381 gp++;
382 if (*gp)
383 *gp++ = NUL;
384 }
385 start_group_search(gname);
386
387 while ((gh = get_group_search())) {
388 if (merge_groups && gh->group_flag & G_UNSUBSCRIBED)
389 continue;
390
391 if (!enter_sequence(mode, gh))
392 continue;
393
394 if (merge_groups) {
395 if (mp_group == NULL) {
396 gh->group_flag |= G_MERGE_HEAD;
397 } else {
398 mp_group->merge_with = gh;
399 gh->group_flag |= G_MERGE_SUB;
400 }
401 mp_group = gh;
402 }
403 if (gh->save_file == NULL) /* not set by "save-files" */
404 gh->save_file = dflt_save;
405 if (gh->enter_macro == NULL) /* not set by "on entry" */
406 gh->enter_macro = enter_macro;
407 }
408 if (!merge_groups)
409 *gp = NUL;
410 }
411 if (merge_groups && mp_group != NULL)
412 mp_group->merge_with = NULL;
413 mode = SHOW_NORMAL;
414 }
415
416 fclose(sf);
417 return 0;
418 }
419
420 void
parse_save_files(register FILE * sf)421 parse_save_files(register FILE * sf)
422 {
423 register int c;
424 register group_header *gh;
425 char group[FILENAME];
426 char *savefile = NULL;
427 char namebuf[FILENAME];
428 register char *gp;
429
430 for (;;) {
431 if ((c = getc(sf)) == EOF)
432 break;
433 if (!isascii(c) || isspace(c))
434 continue;
435 if (c == '#') {
436 do
437 c = getc(sf);
438 while (c != EOF && c != NL);
439 continue;
440 }
441 gp = group;
442 do {
443 *gp++ = c;
444 c = getc(sf);
445 } while (c != EOF && isascii(c) && !isspace(c));
446 *gp = NUL;
447
448 if (strcmp(group, "end") == 0)
449 break;
450
451 while (c != EOF && (!isascii(c) || isspace(c)))
452 c = getc(sf);
453
454 gp = namebuf;
455 do {
456 *gp++ = c;
457 c = getc(sf);
458 } while (c != EOF && isascii(c) && !isspace(c));
459 *gp = NUL;
460 if (namebuf[0] == NUL)
461 break;
462 if (strcmp(namebuf, "+"))
463 savefile = copy_str(namebuf);
464
465 start_group_search(group);
466
467 while ((gh = get_group_search()))
468 gh->save_file = savefile;
469 }
470 }
471
472 void
named_group_sequence(char ** groups)473 named_group_sequence(char **groups)
474 {
475 register group_header *gh;
476 register char *group;
477 register char *value;
478 int non_vars;
479 int found, errors, gnum;
480
481 group_sequence = NULL;
482 also_subgroups = 0;
483
484 errors = 0;
485 non_vars = 0;
486 while ((group = *groups++)) {
487 non_vars++;
488
489 if (hex_group_args) {
490 sscanf(group, "%d", &gnum);
491 if (gnum < 0 || gnum >= master.number_of_groups)
492 continue;
493 gh = ACTIVE_GROUP(gnum);
494 enter_sequence(SHOW_NORMAL, gh);
495 continue;
496 }
497 if ((gh = lookup(group))) {
498 enter_sequence(SHOW_NORMAL, gh);
499 continue;
500 }
501 if ((value = strchr(group, '='))) {
502 *value++ = NUL;
503 set_variable(group, 1, value);
504 non_vars--;
505 continue;
506 }
507 if (*group == '+' || *group == '~' || file_exist(group, "fr")) {
508 faked_entry(group, G_FOLDER);
509 continue;
510 }
511
512 found = 0;
513 start_group_search(group);
514 while ((gh = get_group_search())) {
515 found++;
516 enter_sequence(SHOW_NORMAL, gh);
517 }
518
519 if (!found) {
520 tprintf("Group %s not found\n", group);
521 fl;
522 errors++;
523 }
524 }
525
526 if (non_vars == 0) {
527 normal_group_sequence();
528 return;
529 }
530 end_sequence();
531
532 if (errors)
533 user_delay(2);
534
535 return;
536 }
537
538 FILE *loc_seq_hook = NULL; /* sequence in local "init" file */
539 FILE *glob_seq_hook = NULL; /* sequence in global "init" file */
540
541 void
normal_group_sequence(void)542 normal_group_sequence(void)
543 {
544 register group_header *gh;
545
546 group_sequence = NULL;
547 gs_more_groups = 1;
548
549 /* visit_p_f returns non-zero if terminated by !! */
550
551 if (visit_presentation_file(nn_directory, "seq", loc_seq_hook))
552 goto final;
553
554 if (visit_presentation_file(lib_directory, "sequence", glob_seq_hook))
555 goto final;
556
557 Loop_Groups_Sorted(gh) {
558 enter_sequence(SHOW_NORMAL, gh);
559 }
560
561 final:
562 if (final_sequence) {
563 if (tail_sequence) {
564 tail_sequence->next_group = final_sequence;
565 tail_sequence = NULL;
566 } else {
567 group_sequence = final_sequence;
568 }
569 }
570
571 #ifdef MAIL_READING
572 mail_check();
573 #endif
574
575 end_sequence();
576 }
577
578
579
580 static char *gs_group;
581 static int gs_length, gs_index, gs_mode;
582 static group_header *gs_only_group = NULL;
583
584 #define GS_PREFIX0 0 /* group (or group*) */
585 #define GS_PREFIX 1 /* group. */
586 #define GS_SUFFIX 2 /* .group */
587 #define GS_INFIX 3 /* .group. */
588 #define GS_NEW_GROUP 4 /* new group */
589 #define GS_ALL 5 /* all / . */
590 #define GS_NEWSRC 6 /* RC */
591
592 void
start_group_search(char * group)593 start_group_search(char *group)
594 {
595 char *dot;
596 int last;
597
598 gs_index = master.number_of_groups; /* loop will fail */
599
600 if ((last = strlen(group) - 1) < 0)
601 return;
602 if (group[last] == '*')
603 group[last] = NUL;
604 else if (!also_subgroups && (gs_only_group = lookup(group)) != NULL)
605 return;
606
607 gs_index = 0;
608 gs_more_groups = 0;
609 gs_length = 0;
610 gs_group = NULL;
611
612 if (strcmp(group, "NEW") == 0) {
613 gs_mode = GS_NEW_GROUP;
614 return;
615 }
616 if (strncmp(group, "RC", 2) == 0) {
617 gs_mode = GS_NEWSRC;
618 gs_only_group = rc_sequence;
619 gs_more_groups = 1; /* we just can't know! */
620
621 if (group[2] != ':')
622 return;
623 if (isdigit(group[3]))
624 gs_index = atoi(group + 3);
625 else {
626 gs_group = group + 3;
627 gs_length = strlen(gs_group);
628 }
629 return;
630 }
631 if (strcmp(group, "all") == 0 || strcmp(group, ".") == 0) {
632 gs_mode = GS_ALL;
633 return;
634 }
635 gs_mode = GS_PREFIX0;
636
637 if (strncmp(group, "all.", 4) == 0)
638 group += 3;
639
640 if (*group == '.')
641 gs_mode = GS_SUFFIX;
642
643 if ((dot = strrchr(group, '.')) != NULL && dot != group) {
644 if (dot[1] == NUL || strcmp(dot + 1, "all") == 0) {
645 dot[1] = NUL;
646 gs_mode = (gs_mode == GS_SUFFIX) ? GS_INFIX : GS_PREFIX;
647 }
648 }
649 gs_length = strlen(group);
650 gs_group = group;
651 }
652
653 #define STREQN(a, b, n) ((a)[0] == (b)[0] && strncmp(a, b, (int)(n)) == 0)
654
655 group_header *
get_group_search(void)656 get_group_search(void)
657 {
658 register group_header *gh, **ghp;
659 register int gsidx = gs_index; /* cached gs_index (RW) */
660 register int mstngrps = master.number_of_groups; /* cached (RO) */
661 register int gdonemask = (ignore_done_flag ? 0 : G_DONE);
662 register int tail;
663 register int gslen = gs_length; /* cached (RO) */
664 register int gsmgrps = gs_more_groups; /* cached (RW) */
665 register char *ghgrpnm;
666 register char *gsgrp = gs_group; /* cached (RO) */
667 register int gsmode = gs_mode; /* cached (RO) */
668 register group_header *gsog = gs_only_group; /* cached (RW) */
669
670 /* most of start up CPU time goes here */
671 if (gsmode == GS_NEWSRC) {
672 /* gsgrp is usually "" */
673
674 #ifdef notdef /* seemed like a good idea at the time;
675 * actually slower */
676 if (gslen == 0) { /* common case */
677 if (gsidx != 0)
678 --gs_index;
679 gs_only_group = NULL;
680 return NULL;
681 }
682 #endif
683
684 do {
685 gh = gsog;
686 if (gh == NULL)
687 break;
688 if (gsidx && --gsidx == 0)
689 gsog = NULL;
690 else if (gsgrp && gh->group_name_length >= gslen &&
691 strncmp(gh->group_name, gsgrp, gslen) == 0) {
692 gsog = NULL;
693 } else
694 gsog = gh->newsrc_seq;
695 } while (gh->group_flag & gdonemask || gh->master_flag & M_IGNORE_GROUP);
696 gs_only_group = gsog;
697 gs_index = gsidx;
698 return gh;
699 }
700 if (gsog != NULL) {
701 gh = gsog;
702 gs_only_group = NULL;
703 if (gh->group_flag & gdonemask || gh->master_flag & M_IGNORE_GROUP)
704 return NULL;
705 return gh;
706 }
707 for (ghp = &sorted_groups[gsidx]; gsidx < mstngrps; ++ghp) {
708 gh = *ghp;
709 gsidx++;
710 if (gh->group_flag & gdonemask || gh->master_flag & M_IGNORE_GROUP)
711 continue;
712 gsmgrps++;
713 if ((tail = gh->group_name_length - gslen) < 0)
714 continue;
715
716 ghgrpnm = gh->group_name;
717 if (gsmode == GS_PREFIX0) {
718 /* the big winner */
719 if (((tail = ghgrpnm[gslen]) != NUL && tail != '.') ||
720 !STREQN(ghgrpnm, gsgrp, gslen))
721 continue;
722 } else if (gsmode == GS_PREFIX) {
723 if (!STREQN(ghgrpnm, gsgrp, gslen))
724 continue;
725 } else
726 switch (gsmode) {
727 case GS_NEW_GROUP:
728 if ((gh->group_flag & G_NEW) == 0 || gh->group_flag & G_UNSUBSCRIBED)
729 continue;
730 break;
731
732 case GS_SUFFIX:
733 if (strcmp(ghgrpnm + tail, gsgrp) != 0)
734 continue;
735 break;
736
737 case GS_INFIX:
738 nn_exitmsg(1, ".name. notation not supported (yet)");
739 break;
740
741 case GS_ALL:
742 break;
743 }
744 gsmgrps--;
745 gs_more_groups = gsmgrps;
746 gs_only_group = gsog;
747 gs_index = gsidx;
748 return gh;
749 }
750 gs_more_groups = gsmgrps;
751 gs_only_group = gsog;
752 gs_index = gsidx;
753 return NULL;
754 }
755