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