1 /* $Id: rt-page.c,v 3.0 1992/12/14 00:14:12 davison Trn $
2 */
3 /* The authors make no claims as to the fitness or correctness of this software
4 * for any use whatsoever, and it is provided as is. Any use of this software
5 * is at the user's own risk.
6 */
7
8 #include "EXTERN.h"
9 #include "common.h"
10 #include "cache.h"
11 #include "term.h"
12 #include "ngdata.h"
13 #include "trn.h"
14 #include "util.h"
15 #include "rthread.h"
16 #include "rt-select.h"
17 #include "rt-util.h"
18 #include "INTERN.h"
19 #include "rt-page.h"
20
21 extern char *display_mode;
22 extern char sel_disp_char[];
23
24 bool
set_sel_mode(ch)25 set_sel_mode(ch)
26 char_int ch;
27 {
28 switch (ch) {
29 case 'a':
30 set_selector(SM_ARTICLE, sel_artsort);
31 break;
32 case 's':
33 set_selector(SM_SUBJECT, sel_threadsort);
34 break;
35 case 't':
36 if (in_ng && !ThreadedGroup) {
37 bool always_save = thread_always;
38 ThreadedGroup = TRUE;
39 thread_always = TRUE;
40 if (sel_rereading)
41 firstart = absfirst;
42 printf("\nThreading the group. "), fflush(stdout);
43 thread_open();
44 thread_always = always_save;
45 if (last_cached < lastart)
46 ThreadedGroup = FALSE;
47 }
48 /* FALL THROUGH */
49 case 'T':
50 set_selector(SM_THREAD, sel_threadsort);
51 break;
52 default:
53 return FALSE;
54 }
55 return TRUE;
56 }
57
58 bool
set_sel_sort(ch)59 set_sel_sort(ch)
60 char_int ch;
61 {
62 if (isupper(ch)) {
63 sel_direction = -1;
64 ch = tolower(ch);
65 } else
66 sel_direction = 1;
67 switch (ch) {
68 case 'd':
69 sel_sort = SS_DATE;
70 break;
71 case 's':
72 sel_sort = SS_SUBJECT;
73 break;
74 case 'a':
75 sel_sort = SS_AUTHOR;
76 break;
77 case 'c':
78 sel_sort = SS_COUNT;
79 break;
80 case 'n':
81 sel_sort = SS_NUMBER;
82 break;
83 case 'g':
84 sel_sort = SS_GROUPS;
85 break;
86 default:
87 return FALSE;
88 }
89 if (sel_mode == SM_ARTICLE)
90 set_selector(sel_mode, sel_sort);
91 else
92 set_selector(sel_threadmode, sel_sort);
93 return TRUE;
94 }
95
96 void
set_selector(smode,ssort)97 set_selector(smode, ssort)
98 int smode;
99 int ssort;
100 {
101 sel_mode = smode;
102 sel_sort = ssort;
103
104 if (!ThreadedGroup && sel_mode == SM_THREAD)
105 sel_mode = SM_SUBJECT;
106
107 if (sel_mode == SM_ARTICLE) {
108 if (sel_sort == SS_COUNT)
109 sel_sort = SS_DATE;
110 } else if (sel_sort == SS_AUTHOR || sel_sort == SS_GROUPS
111 || sel_sort == SS_NUMBER)
112 sel_sort = SS_DATE;
113
114 switch (sel_mode) {
115 case SM_THREAD:
116 sel_mode_string = "threads";
117 sel_threadmode = smode;
118 sel_threadsort = ssort;
119 break;
120 case SM_SUBJECT:
121 sel_mode_string = "subjects";
122 sel_threadmode = smode;
123 sel_threadsort = ssort;
124 break;
125 case SM_ARTICLE:
126 sel_mode_string = "articles";
127 sel_artsort = ssort;
128 break;
129 }
130
131 switch (sel_sort) {
132 case SS_DATE:
133 sel_sort_string = "date";
134 break;
135 case SS_SUBJECT:
136 sel_sort_string = "subject";
137 break;
138 case SS_AUTHOR:
139 sel_sort_string = "author";
140 break;
141 case SS_COUNT:
142 sel_sort_string = "count";
143 break;
144 case SS_NUMBER:
145 sel_sort_string = "number";
146 break;
147 case SS_GROUPS:
148 sel_sort_string = "SubjDate";
149 break;
150 }
151 }
152
sel_page_init()153 void sel_page_init()
154 {
155 sel_chars = getval("SELECTCHARS", SELECTCHARS);
156 sel_max_cnt = strlen(sel_chars);
157 if (sel_max_cnt > MAX_SEL)
158 sel_max_cnt = MAX_SEL;
159 if (sel_max_cnt > LINES-5)
160 sel_max_cnt = LINES-5;
161 sel_line = 2;
162 sel_page_arts = 0;
163 sel_item_cnt = 0;
164 }
165
166 void
init_pages(fill_last_page)167 init_pages(fill_last_page)
168 bool_int fill_last_page;
169 {
170 try_again:
171 sel_prior_arts = sel_total_arts = 0;
172
173 if (sel_mode == SM_ARTICLE) {
174 ARTICLE *ap, **app, **limit;
175
176 if (sel_page_app) {
177 int desired_flags = (sel_rereading? AF_READ : 0);
178 limit = artptr_list + artptr_list_size;
179 ap = Nullart;
180 for (app = sel_page_app; app < limit; app++) {
181 ap = *app;
182 if ((ap->flags & (AF_MISSING|AF_READ)) == desired_flags)
183 break;
184 }
185 sort_articles();
186 if (ap == Nullart)
187 sel_page_app = artptr_list + artptr_list_size;
188 else {
189 for (app = artptr_list; app < limit; app++) {
190 if (*app == ap) {
191 sel_page_app = app;
192 break;
193 }
194 }
195 }
196 } else
197 sort_articles();
198
199 while (sel_page_sp && sel_page_sp->misc == 0)
200 sel_page_sp = sel_page_sp->next;
201 /* The artptr_list contains only unread or read articles, never both */
202 limit = artptr_list + artptr_list_size;
203 for (app = artptr_list; app < limit; app++) {
204 ap = *app;
205 if (sel_rereading && !(ap->flags & sel_mask))
206 ap->flags |= AF_DEL;
207 if (sel_page_app == app
208 || (!sel_page_app && ap->subj == sel_page_sp)) {
209 sel_page_app = app;
210 sel_prior_arts = sel_total_arts;
211 }
212 if (!sel_exclusive || (ap->flags & sel_mask)) {
213 sel_total_arts++;
214 ap->flags |= AF_INCLUDED;
215 } else
216 ap->flags &= ~AF_INCLUDED;
217 }
218 if (sel_exclusive && !sel_total_arts) {
219 sel_exclusive = FALSE;
220 goto try_again;
221 }
222 if (!sel_page_app)
223 (void) first_page();
224 else if (sel_page_app >= limit)
225 (void) last_page();
226 else if (sel_prior_arts && fill_last_page) {
227 sel_page_init();
228 app = sel_page_app;
229 do {
230 if (!((*app)->flags & AF_INCLUDED))
231 continue;
232 sel_page_arts++;
233 sel_item_cnt++;
234 } while (++app < limit && sel_item_cnt < sel_max_cnt);
235 if (sel_prior_arts + sel_page_arts == sel_total_arts) {
236 (void) last_page();
237 }
238 }
239 } else {
240 SUBJECT *sp, *group_sp;
241 int group_arts;
242
243 if (sel_page_sp) {
244 while (sel_page_sp && sel_page_sp->misc == 0)
245 sel_page_sp = sel_page_sp->next;
246 sort_subjects();
247 if (!sel_page_sp)
248 sel_page_sp = last_subject;
249 } else
250 sort_subjects();
251 for (sp = first_subject; sp; sp = sp->next) {
252 if (sel_rereading && !(sp->flags & sel_mask))
253 sp->flags |= SF_DEL;
254
255 group_sp = sp;
256 group_arts = sp->misc;
257
258 if (!sel_exclusive || (sp->flags & sel_mask))
259 sp->flags |= SF_INCLUDED;
260 else
261 sp->flags &= ~SF_INCLUDED;
262
263 if (sel_page_sp == group_sp)
264 sel_prior_arts = sel_total_arts;
265 if (sel_mode == SM_THREAD) {
266 while (sp->next && sp->next->thread == sp->thread) {
267 sp = sp->next;
268 if (sp == sel_page_sp) {
269 sel_prior_arts = sel_total_arts;
270 sel_page_sp = group_sp;
271 }
272 sp->flags &= ~SF_INCLUDED;
273 if (sp->flags & sel_mask)
274 group_sp->flags |= SF_INCLUDED;
275 else if (sel_rereading)
276 sp->flags |= SF_DEL;
277 group_arts += sp->misc;
278 }
279 }
280 if (group_sp->flags & SF_INCLUDED)
281 sel_total_arts += group_arts;
282 }
283 if (sel_exclusive && !sel_total_arts) {
284 sel_exclusive = FALSE;
285 goto try_again;
286 }
287 if (!sel_page_sp)
288 (void) first_page();
289 else if (sel_page_sp == last_subject)
290 (void) last_page();
291 else if (sel_prior_arts && fill_last_page) {
292 int line_cnt, sel;
293 sel_page_init();
294 sp = sel_page_sp;
295 do {
296 if (sp->flags & SF_INCLUDED) {
297 if (sel_mode == SM_THREAD)
298 line_cnt = count_thread_lines(sp, &sel);
299 else
300 line_cnt = count_subject_lines(sp, &sel);
301 if (line_cnt) {
302 if (line_cnt > LINES - 5)
303 line_cnt = LINES - 5;
304 if (sel_line + line_cnt > LINES - 3)
305 break;
306 sel_page_arts += sp->misc;
307 sel_item_cnt++;
308 }
309 } else
310 line_cnt = 0;
311 if (sel_mode == SM_THREAD) {
312 while (sp->next && sp->next->thread == sp->thread) {
313 sp = sp->next;
314 if (!line_cnt || !sp->misc)
315 continue;
316 sel_page_arts += sp->misc;
317 }
318 }
319 } while ((sp=sp->next)!=Nullsubj && sel_item_cnt<sel_max_cnt);
320 if (sel_prior_arts + sel_page_arts == sel_total_arts) {
321 (void) last_page();
322 }
323 }
324 }
325 }
326
327 bool
first_page()328 first_page()
329 {
330 sel_prior_arts = 0;
331
332 if (sel_mode == SM_ARTICLE) {
333 ARTICLE **app, **limit;
334
335 limit = artptr_list + artptr_list_size;
336 for (app = artptr_list; app < limit; app++) {
337 if ((*app)->flags & AF_INCLUDED) {
338 if (sel_page_app != app) {
339 sel_page_app = app;
340 return TRUE;
341 }
342 break;
343 }
344 }
345 } else {
346 SUBJECT *sp;
347
348 for (sp = first_subject; sp; sp = sp->next) {
349 if (sp->flags & SF_INCLUDED) {
350 if (sel_page_sp != sp) {
351 sel_page_sp = sp;
352 return TRUE;
353 }
354 break;
355 }
356 }
357 }
358 return FALSE;
359 }
360
361 bool
last_page()362 last_page()
363 {
364 sel_prior_arts = sel_total_arts;
365
366 if (sel_mode == SM_ARTICLE) {
367 ARTICLE **app = sel_page_app;
368 sel_page_app = artptr_list + artptr_list_size;
369 if (!prev_page())
370 sel_page_app = app;
371 else if (app != sel_page_app)
372 return TRUE;
373 } else {
374 SUBJECT *sp = sel_page_sp;
375 sel_page_sp = Nullsubj;
376 if (!prev_page())
377 sel_page_sp = sp;
378 else if (sp != sel_page_sp)
379 return TRUE;
380 }
381 return FALSE;
382 }
383
384 bool
next_page()385 next_page()
386 {
387 if (sel_mode == SM_ARTICLE) {
388 if (sel_next_app < artptr_list + artptr_list_size) {
389 sel_page_app = sel_next_app;
390 sel_prior_arts += sel_page_arts;
391 return TRUE;
392 }
393 } else {
394 if (sel_next_sp) {
395 sel_page_sp = sel_next_sp;
396 sel_prior_arts += sel_page_arts;
397 return TRUE;
398 }
399 }
400 return FALSE;
401 }
402
403 bool
prev_page()404 prev_page()
405 {
406 int item_cnt = 0;
407
408 /* Scan the items in reverse to go back a page */
409 if (sel_mode == SM_ARTICLE) {
410 ARTICLE *ap, **app, **page_app = sel_page_app;
411
412 for (app = sel_page_app; --app >= artptr_list; ) {
413 ap = *app;
414 if (ap->flags & AF_INCLUDED) {
415 page_app = app;
416 sel_prior_arts--;
417 if (++item_cnt >= sel_max_cnt)
418 break;
419 }
420 }
421 if (sel_page_app != page_app) {
422 sel_page_app = page_app;
423 return TRUE;
424 }
425 } else {
426 SUBJECT *sp, *page_sp = sel_page_sp;
427 int line_cnt, item_arts, line;
428
429 line = 2;
430 for (sp = (!page_sp? last_subject : page_sp->prev); sp; sp=sp->prev) {
431 item_arts = sp->misc;
432 if (sel_mode == SM_THREAD) {
433 while (sp->prev && sp->prev->thread == sp->thread) {
434 sp = sp->prev;
435 item_arts += sp->misc;
436 }
437 line_cnt = count_thread_lines(sp, NULL);
438 } else
439 line_cnt = count_subject_lines(sp, NULL);
440 if (!(sp->flags & SF_INCLUDED) || !line_cnt)
441 continue;
442 if (line_cnt > LINES - 5)
443 line_cnt = LINES - 5;
444 line += line_cnt;
445 if (line > LINES - 3) {
446 sp = page_sp;
447 break;
448 }
449 sel_prior_arts -= item_arts;
450 page_sp = sp;
451 if (++item_cnt >= sel_max_cnt)
452 break;
453 }
454 if (sel_page_sp != page_sp) {
455 sel_page_sp = (page_sp? page_sp : first_subject);
456 return TRUE;
457 }
458 }
459 return FALSE;
460 }
461
462 void
display_page()463 display_page()
464 {
465 int sel;
466
467 #ifdef CLEAREOL
468 if (can_home_clear) {
469 home_cursor();
470 maybe_eol();
471 } else
472 #endif
473 clear();
474 carriage_return();
475
476 #ifdef NOFIREWORKS
477 no_sofire();
478 #endif
479 standout();
480 fputs(ngname, stdout);
481 un_standout();
482 printf(" %ld %sarticle%s", (long)sel_total_arts,
483 sel_rereading? "read " : nullstr,
484 article_count == 1 ? nullstr : "s");
485 if (sel_exclusive)
486 printf(" out of %ld", (long)article_count);
487 printf("%s\n", moderated);
488 #ifdef CLEAREOL
489 maybe_eol();
490 #endif
491 if (redirected && redirected != nullstr)
492 printf("\t** Please start using %s **", redirected);
493 putchar('\n') FLUSH;
494 try_again:
495 sel_page_init();
496
497 if (!sel_total_arts)
498 ;
499 else if (sel_mode == SM_ARTICLE) {
500 ARTICLE *ap, **app, **limit;
501
502 limit = artptr_list + artptr_list_size;
503 app = sel_page_app;
504 do {
505 ap = *app;
506 if (ap == sel_last_ap)
507 sel_item_index = sel_item_cnt;
508 if (!(ap->flags & AF_INCLUDED))
509 continue;
510 sel = !!(ap->flags & sel_mask) + (ap->flags & AF_DEL);
511 sel_items[sel_item_cnt].ptr = (VOIDPTR*)ap;
512 sel_items[sel_item_cnt].line = sel_line;
513 sel_items[sel_item_cnt].sel = sel;
514 sel_page_arts++;
515 /* Output the article, with optional author */
516 display_article(ap, sel_chars[sel_item_cnt], sel);
517 sel_item_cnt++;
518 } while (++app < limit && sel_item_cnt < sel_max_cnt);
519 if (!sel_page_arts) {
520 if (last_page())
521 goto try_again;
522 }
523 sel_next_app = app;
524 } else {
525 SUBJECT *sp;
526 int line_cnt;
527 bool etc;
528 char ch = ' ';
529
530 sp = sel_page_sp;
531 do {
532 if (sp == sel_last_sp)
533 sel_item_index = sel_item_cnt;
534
535 etc = FALSE;
536 if (sp->flags & SF_INCLUDED) {
537 /* Compute how many lines we need to display this group */
538 if (sel_mode == SM_THREAD)
539 line_cnt = count_thread_lines(sp, &sel);
540 else
541 line_cnt = count_subject_lines(sp, &sel);
542 if (line_cnt) {
543 /* If this item is too long to fit on the screen all by
544 ** itself, trim it to fit and set the "etc" flag.
545 */
546 if (line_cnt > LINES - 5) {
547 line_cnt = LINES - 5;
548 etc = TRUE;
549 }
550 /* If it doesn't fit, save it for the next page */
551 if (sel_line + line_cnt > LINES - 3)
552 break;
553 sel_items[sel_item_cnt].ptr = (VOIDPTR*)sp;
554 sel_items[sel_item_cnt].line = sel_line;
555 sel_items[sel_item_cnt].sel = sel;
556 sel_page_arts += sp->misc;
557
558 ch = sel_chars[sel_item_cnt];
559 sel = sel_items[sel_item_cnt].sel;
560 sel_item_cnt++;
561 if (sp->misc) {
562 display_subject(sp, ch, sel);
563 ch = ' ';
564 }
565 }
566 } else
567 line_cnt = 0;
568 if (sel_mode == SM_THREAD) {
569 while (sp->next && sp->next->thread == sp->thread) {
570 sp = sp->next;
571 if (!line_cnt || !sp->misc)
572 continue;
573 if (sel_line < LINES - 3)
574 display_subject(sp, ch, sel);
575 ch = ' ';
576 sel_page_arts += sp->misc;
577 }
578 }
579 if (etc)
580 fputs(" ...etc.", stdout);
581 } while ((sp=sp->next)!=Nullsubj && !etc && sel_item_cnt<sel_max_cnt);
582 if (!sel_page_arts) {
583 if (last_page())
584 goto try_again;
585 }
586 sel_next_sp = sp;
587 }
588 sel_last_line = sel_line+1;
589 sel_last_ap = Nullart;
590 sel_last_sp = Nullsubj;
591 sel_at_end = (sel_prior_arts + sel_page_arts == sel_total_arts);
592 #ifdef CLEAREOL
593 maybe_eol();
594 #endif
595 putchar('\n') FLUSH;
596 }
597
598 void
update_page()599 update_page()
600 {
601 int sel;
602 int j;
603
604 for (j = 0; j < sel_item_cnt; j++) {
605 sel = sel_items[j].sel;
606 if (sel_mode == SM_ARTICLE) {
607 ARTICLE *ap = (ARTICLE*)sel_items[j].ptr;
608 if (sel == !!(ap->flags & sel_mask) + (ap->flags & AF_DEL))
609 continue;
610 } else {
611 SUBJECT *sp = (SUBJECT*)sel_items[j].ptr;
612 int real_sel;
613 if (sel_mode == SM_THREAD)
614 (void) count_thread_lines(sp, &real_sel);
615 else
616 (void) count_subject_lines(sp, &real_sel);
617 if (sel == real_sel)
618 continue;
619 }
620 goto_line(sel_line, sel_items[j].line);
621 sel_line = sel_items[j].line;
622 sel_item_index = j;
623 output_sel(!sel);
624 }
625 if (++sel_item_index == sel_item_cnt)
626 sel_item_index = 0;
627 }
628
629 void
output_sel(sel)630 output_sel(sel)
631 int sel;
632 {
633 putchar(sel_chars[sel_item_index]);
634 putchar(sel_disp_char[sel]);
635 sel_items[sel_item_index].sel = sel;
636 }
637
638 /* Counts the number of lines needed to output a subject, including
639 ** optional authors.
640 */
641 static int
count_subject_lines(subj,selptr)642 count_subject_lines(subj, selptr)
643 SUBJECT *subj;
644 int *selptr;
645 {
646 register ARTICLE *ap;
647 register int sel;
648
649 if (subj->flags & SF_DEL)
650 sel = 2;
651 else if (subj->flags & sel_mask) {
652 sel = 1;
653 for (ap = subj->articles; ap; ap = ap->subj_next) {
654 if ((!(ap->flags&AF_READ) ^ sel_rereading)
655 && !(ap->flags & sel_mask)) {
656 sel = 3;
657 break;
658 }
659 }
660 } else
661 sel = 0;
662 if (selptr)
663 *selptr = sel;
664 if (*display_mode == 'l')
665 return subj->misc;
666 if (*display_mode == 'm')
667 return (subj->misc <= 4? subj->misc : (subj->misc - 4) / 3 + 4);
668 return (subj->misc != 0);
669 }
670
671 /* Counts the number of lines needed to output a thread, including
672 ** optional authors.
673 */
674 static int
count_thread_lines(subj,selptr)675 count_thread_lines(subj, selptr)
676 SUBJECT *subj;
677 int *selptr;
678 {
679 register int total = 0;
680 register ARTICLE *thread = subj->thread;
681 int sel = -1, subj_sel;
682
683 do {
684 if (subj->misc) {
685 total += count_subject_lines(subj, &subj_sel);
686 if (sel < 0)
687 sel = subj_sel;
688 else if (sel != subj_sel)
689 sel = 3;
690 }
691 } while ((subj = subj->next) != Nullsubj && subj->thread == thread);
692 if (selptr)
693 *selptr = (sel < 0? 0 : sel);
694 return total;
695 }
696
697 /* Display an article, perhaps with its author.
698 */
699 static void
display_article(ap,ch,sel)700 display_article(ap, ch, sel)
701 register ARTICLE *ap;
702 char_int ch;
703 int sel;
704 {
705 int subj_width = COLS - 5;
706 int from_width = COLS / 5;
707
708 #ifdef CLEAREOL
709 maybe_eol();
710 #endif
711 if (subj_width < 32)
712 subj_width = 32;
713
714 putchar(ch);
715 putchar(sel_disp_char[sel]);
716 if (*display_mode == 's' || from_width < 8)
717 printf(" %s\n",compress_subj(ap->subj->articles,subj_width)) FLUSH;
718 else {
719 printf("%s %s\n",
720 compress_from(ap, from_width),
721 compress_subj(ap, subj_width - from_width)) FLUSH;
722 }
723 sel_line++;
724 }
725
726 /* Display the given subject group, with optional authors.
727 */
728 static void
display_subject(subj,ch,sel)729 display_subject(subj, ch, sel)
730 SUBJECT *subj;
731 char_int ch;
732 int sel;
733 {
734 register ARTICLE *ap;
735 register int j, i;
736 int subj_width = COLS - 8;
737 int from_width = COLS / 5;
738
739 #ifdef CLEAREOL
740 maybe_eol();
741 #endif
742 if (subj_width < 32)
743 subj_width = 32;
744
745 j = subj->misc;
746
747 putchar(ch);
748 if (ch != ' ')
749 putchar(sel_disp_char[sel]);
750 else
751 putchar(' ');
752 if (*display_mode == 's' || from_width < 8)
753 printf("%3d %s\n",j,compress_subj(subj->articles,subj_width)) FLUSH;
754 else {
755 /* Find the first unread article so we get the author right */
756 for (ap = subj->articles; ap; ap = ap->subj_next) {
757 if (!(ap->flags&AF_READ) ^ sel_rereading)
758 break;
759 }
760 printf("%s%3d %s\n",
761 compress_from(ap, from_width), j,
762 compress_subj(ap, subj_width - from_width)) FLUSH;
763 i = -1;
764 if (--j && ap) {
765 for (ap = ap->subj_next; ap && j; ap = ap->subj_next) {
766 if (!(!(ap->flags&AF_READ) ^ sel_rereading))
767 continue;
768 j--;
769 if (i < 0)
770 i = 0;
771 else if (*display_mode == 'm') {
772 if (!j) {
773 if (i)
774 putchar('\n');
775 } else {
776 if (i == 3 || !i) {
777 if (i)
778 putchar('\n');
779 if (++sel_line >= LINES - 3)
780 return;
781 #ifdef CLEAREOL
782 maybe_eol();
783 #endif
784 i = 1;
785 } else
786 i++;
787 printf(" %s ",
788 compress_from(ap, from_width)) FLUSH;
789 continue;
790 }
791 }
792 if (++sel_line >= LINES - 3)
793 return;
794 #ifdef CLEAREOL
795 maybe_eol();
796 #endif
797 printf(" %s\n", compress_from(ap, from_width)) FLUSH;
798 }
799 }
800 }
801 sel_line++;
802 }
803