1 /* art.c
2 */
3 /* This software is copyrighted as detailed in the LICENSE file. */
4
5
6 #include "EXTERN.h"
7 #include "common.h"
8 #include "list.h"
9 #include "trn.h"
10 #include "hash.h"
11 #include "ngdata.h"
12 #include "nntpclient.h"
13 #include "datasrc.h"
14 #include "addng.h"
15 #include "nntp.h"
16 #include "ngstuff.h"
17 #include "cache.h"
18 #include "bits.h"
19 #include "head.h"
20 #include "help.h"
21 #include "search.h"
22 #include "artio.h"
23 #include "ng.h"
24 #include "final.h"
25 #include "artstate.h"
26 #include "rcstuff.h"
27 #include "mime.h"
28 #include "term.h"
29 #include "env.h"
30 #include "util.h"
31 #include "util2.h"
32 #include "sw.h"
33 #include "kfile.h"
34 #include "decode.h"
35 #include "backpage.h"
36 #include "intrp.h"
37 #include "rthread.h"
38 #include "rt-select.h"
39 #include "rt-util.h"
40 #include "rt-wumpus.h"
41 #include "charsubst.h"
42 #ifdef SCAN_ART
43 #include "scanart.h"
44 #endif
45 #ifdef SCORE
46 #include "score.h" /* for sc_lookahead() */
47 #endif
48 #ifdef USE_TK
49 #include "tkstuff.h"
50 #include "tktree.h"
51 #endif
52 #include "color.h"
53 #include "INTERN.h"
54 #include "art.h"
55 #include "artstate.h" /* somebody has to do it */
56
57 #define LINE_PTR(pos) (artbuf + (pos) - htype[PAST_HEADER].minpos)
58 #define LINE_OFFSET(ptr) ((ptr) - artbuf + htype[PAST_HEADER].minpos)
59
60 /* page_switch() return values */
61
62 #define PS_NORM 0
63 #define PS_ASK 1
64 #define PS_RAISE 2
65 #define PS_TOEND 3
66
67 bool special = FALSE; /* is next page special length? */
68 int slines = 0; /* how long to make page when special */
69 ART_POS restart = 0; /* if nonzero, the place where last */
70 /* line left off on line split */
71 ART_POS alinebeg; /* where in file current line began */
72 int more_prompt_col; /* non-zero when the more prompt is indented */
73
74 ART_LINE isrchline = 0; /* last line to display */
75 #ifdef INNERSEARCH
76 COMPEX gcompex; /* in article search pattern */
77 #endif
78
79 bool firstpage; /* is this the 1st page of article? */
80 bool continuation; /* this line/header is being continued */
81
82 void
art_init()83 art_init()
84 {
85 #ifdef INNERSEARCH
86 init_compex(&gcompex);
87 #endif
88 }
89
90 int
do_article()91 do_article()
92 {
93 register char* s;
94 bool hide_this_line = FALSE; /* hidden header line? */
95 bool restart_color;
96 ART_LINE linenum; /* line # on page, 1 origin */
97 #ifdef ULSMARTS
98 bool under_lining = FALSE; /* are we underlining a word? */
99 #endif
100 register char* bufptr = art_line; /* pointer to input buffer */
101 register int outpos; /* column position of output */
102 static char prompt_buf[64]; /* place to hold prompt */
103 bool notesfiles = FALSE; /* might there be notesfiles junk? */
104 char oldmode = mode;
105 register int outputok = TRUE;
106
107 #ifdef SUPPORT_NNTP
108 if (datasrc->flags & DF_REMOTE)
109 artsize = raw_artsize = nntp_artsize();
110 else
111 #endif
112 {
113 if (fstat(fileno(artfp),&filestat)) /* get article file stats */
114 return DA_CLEAN;
115 if (!S_ISREG(filestat.st_mode))
116 return DA_NORM;
117 artsize = raw_artsize = filestat.st_size;
118 }
119 #ifdef SCORE
120 #ifdef USE_TK
121 if (ttcl_running)
122 ttcl_set_int("score_curvar",sc_score_art((int)art,TRUE));
123 #endif
124 #endif
125 sprintf(prompt_buf, mousebar_cnt>3? "%%sEnd of art %ld (of %ld) %%s[%%s]"
126 : "%%sEnd of article %ld (of %ld) %%s-- what next? [%%s]",
127 (long)art,(long)lastart); /* format prompt string */
128 prompt = prompt_buf;
129 int_count = 0; /* interrupt count is 0 */
130 if ((firstpage = (topline < 0)) != 0) {
131 parseheader(art);
132 mime_SetArticle();
133 clear_artbuf();
134 seekart(artbuf_seek = htype[PAST_HEADER].minpos);
135 }
136 term_scrolled = 0;
137 #ifdef USE_TK
138 if (ThreadedGroup && ttk_running)
139 ttk_draw_tree(curr_artp, 0, 0);
140 #endif
141
142 for (;;) { /* for each page */
143 if (ThreadedGroup && max_tree_lines)
144 init_tree(); /* init tree display */
145 assert(art == openart);
146 if (do_fseek) {
147 parseheader(art); /* make sure header is ours */
148 if (!*artbuf) {
149 mime_SetArticle();
150 artbuf_seek = htype[PAST_HEADER].minpos;
151 }
152 artpos = vrdary(artline);
153 if (artpos < 0)
154 artpos = -artpos; /* labs(), anyone? */
155 if (firstpage)
156 artpos = (ART_POS)0;
157 if (artpos < htype[PAST_HEADER].minpos) {
158 in_header = SOME_LINE;
159 seekart(htype[PAST_HEADER].minpos);
160 seekartbuf(htype[PAST_HEADER].minpos);
161 }
162 else {
163 seekart(artbuf_seek);
164 seekartbuf(artpos);
165 }
166 do_fseek = FALSE;
167 restart = 0;
168 }
169 linenum = 1;
170 if (!do_hiding)
171 is_mime = FALSE;
172 if (firstpage) {
173 if (firstline) {
174 interp(art_line,sizeof art_line,firstline);
175 linenum += tree_puts(art_line,linenum+topline,0);
176 } else {
177 ART_NUM i;
178 int selected, unseen;
179
180 selected = (curr_artp->flags & AF_SEL);
181 unseen = article_unread(art)? 1 : 0;
182 sprintf(art_line,"%s%s #%ld",ngname,moderated,(long)art);
183 if (selected_only) {
184 i = selected_count - (unseen && selected);
185 sprintf(art_line+strlen(art_line)," (%ld + %ld more)",
186 (long)i,(long)ngptr->toread - selected_count
187 - (!selected && unseen));
188 }
189 else if ((i = (ART_NUM)(ngptr->toread - unseen)) != 0
190 || (!ThreadedGroup && dmcount)) {
191 sprintf(art_line+strlen(art_line),
192 " (%ld more)",(long)i);
193 }
194 if (!ThreadedGroup && dmcount)
195 sprintf(art_line+strlen(art_line)-1,
196 " + %ld Marked to return)",(long)dmcount);
197 linenum += tree_puts(art_line,linenum+topline,0);
198 }
199 start_header(art);
200 forcelast = FALSE; /* we will have our day in court */
201 restart = 0;
202 artline = 0; /* start counting lines */
203 artpos = 0;
204 vwtary(artline,artpos); /* remember pos in file */
205 }
206 for (restart_color = 1; /* linenum already set */
207 innersearch? (in_header || innermore())
208 : special? (linenum < slines)
209 : (firstpage && !in_header)? (linenum < initlines)
210 : (linenum < tc_LINES);
211 linenum++) { /* for each line on page */
212 if (int_count) { /* exit via interrupt? */
213 newline(); /* get to left margin */
214 int_count = 0; /* reset interrupt count */
215 set_mode(gmode,oldmode);
216 special = FALSE;
217 return DA_NORM; /* skip out of loops */
218 }
219 if (restart) { /* did not finish last line? */
220 bufptr = LINE_PTR(restart);/* then start again here */
221 restart = 0; /* and reset the flag */
222 continuation = 1;
223 if (restart_color && do_hiding && !in_header)
224 maybe_set_color(bufptr, 1);
225 }
226 else if (in_header && *(bufptr = headbuf + artpos))
227 continuation = *bufptr == ' ' || *bufptr == '\t';
228 else {
229 if ((bufptr = readartbuf(auto_view_inline)) == NULL) {
230 special = FALSE;
231 if (innersearch)
232 (void)innermore();
233 break;
234 }
235 if (do_hiding && !in_header)
236 continuation = maybe_set_color(bufptr, restart_color);
237 else
238 continuation = 0;
239 }
240 alinebeg = artpos; /* remember where we began */
241 restart_color = 0;
242 if (in_header) {
243 hide_this_line = parseline(bufptr,do_hiding,hide_this_line);
244 if (!in_header) {
245 linenum += finish_tree(linenum+topline);
246 end_header();
247 seekart(artbuf_seek);
248 }
249 } else if (notesfiles && do_hiding && !continuation
250 && *bufptr == '#' && isupper(bufptr[1])
251 && bufptr[2] == ':' ) {
252 if ((bufptr = readartbuf(auto_view_inline)) == NULL)
253 break;
254 for (s = bufptr; *s && *s != '\n' && *s != '!'; s++) ;
255 if (*s != '!')
256 readartbuf(auto_view_inline);
257 mime_SetArticle();
258 clear_artbuf(); /* exclude notesfiles droppings */
259 artbuf_seek = htype[PAST_HEADER].minpos = tellart();
260 hide_this_line = TRUE; /* and do not print either */
261 notesfiles = FALSE;
262 }
263 #ifdef CUSTOMLINES
264 if (hideline && !continuation && execute(&hide_compex,bufptr))
265 hide_this_line = TRUE;
266 #endif
267 if (in_header && do_hiding && (htype[in_header].flags & HT_MAGIC)) {
268 switch (in_header) {
269 case NGS_LINE:
270 if ((s = index(bufptr,'\n')) != NULL)
271 *s = '\0';
272 hide_this_line = (index(bufptr,',') == NULL)
273 && strEQ(bufptr+12, ngname);
274 if (s != NULL)
275 *s = '\n';
276 break;
277 case EXPIR_LINE:
278 if (!(htype[EXPIR_LINE].flags & HT_HIDE)) {
279 s = bufptr + htype[EXPIR_LINE].length + 1;
280 hide_this_line = *s != ' ' || s[1] == '\n';
281 }
282 break;
283 case FROM_LINE:
284 if ((s = index(bufptr,'\n')) != NULL
285 && s-bufptr < sizeof art_line)
286 safecpy(art_line,bufptr,s-bufptr+1);
287 else
288 safecpy(art_line,bufptr,sizeof art_line);
289 if ((s = extract_name(art_line+6)) != NULL) {
290 strcpy(art_line+6,s);
291 bufptr = art_line;
292 }
293 break;
294 #ifdef HAS_STRFTIME
295 case DATE_LINE:
296 if (curr_artp->date != -1) {
297 strncpy(art_line,bufptr,6);
298 strftime(art_line+6, (sizeof art_line)-6,
299 getval("LOCALTIMEFMT", LOCALTIMEFMT),
300 localtime(&curr_artp->date));
301 bufptr = art_line;
302 }
303 break;
304 #endif
305 }
306 }
307 if (in_header == SUBJ_LINE && do_hiding
308 && (htype[SUBJ_LINE].flags & HT_MAGIC)) { /* handle the subject */
309 s = get_cached_line(artp, SUBJ_LINE, FALSE);
310 if (s && continuation) {
311 /* continuation lines were already output */
312 linenum--;
313 }
314 else {
315 int length = strlen(bufptr+1);
316 notesfiles = instr(&bufptr[length-10]," - (nf", TRUE)!=NULL;
317 artline++;
318 if (!s)
319 bufptr += (continuation? 0 : 9);
320 else
321 bufptr = s;
322 /* tree_puts(, ,1) underlines subject */
323 linenum += tree_puts(bufptr,linenum+topline,1)-1;
324 }
325 }
326 else if (hide_this_line && do_hiding) { /* do not print line? */
327 linenum--; /* compensate for linenum++ */
328 if (!in_header)
329 hide_this_line = FALSE;
330 }
331 else if (in_header) {
332 artline++;
333 linenum += tree_puts(bufptr,linenum+topline,0)-1;
334 }
335 else { /* just a normal line */
336 if (outputok && erase_each_line)
337 erase_line(0);
338 if (highlight == artline) { /* this line to be highlit? */
339 if (marking == STANDOUT) {
340 #ifdef NOFIREWORKS
341 if (erase_screen)
342 no_sofire();
343 #endif
344 standout();
345 }
346 else {
347 #ifdef NOFIREWORKS
348 if (erase_screen)
349 no_ulfire();
350 #endif
351 underline();
352 carriage_return();
353 }
354 if (*bufptr == '\n')
355 putchar(' ');
356 }
357 outputok = !hide_everything; /* registerize it, hopefully */
358 #ifdef CUSTOMLINES
359 if (pagestop && !continuation && execute(&page_compex,bufptr))
360 linenum = 32700;
361 #endif
362 for (outpos = 0; outpos < tc_COLS; ) { /* while line has room */
363 if (AT_NORM_CHAR(bufptr)) { /* normal char? */
364 #ifdef ULSMARTS
365 if (*bufptr == '_') {
366 if (bufptr[1] == '\b') {
367 if (outputok && !under_lining
368 && highlight != artline) {
369 under_lining++;
370 if (tc_UG) {
371 if (bufptr != buf && bufptr[-1]==' ') {
372 outpos--;
373 backspace();
374 }
375 }
376 underline();
377 }
378 bufptr += 2;
379 }
380 }
381 else {
382 if (under_lining) {
383 under_lining = 0;
384 un_underline();
385 if (tc_UG) {
386 outpos++;
387 if (*bufptr == ' ')
388 goto skip_put;
389 }
390 }
391 }
392 #endif
393 /* handle rot-13 if wanted */
394 if (rotate && !in_header && isalpha(*bufptr)) {
395 if (outputok) {
396 if ((*bufptr & 31) <= 13)
397 putchar(*bufptr+13);
398 else
399 putchar(*bufptr-13);
400 }
401 outpos++;
402 }
403 else {
404 #ifdef CHARSUBST
405 register int i;
406 i = putsubstchar(*bufptr, tc_COLS - outpos, outputok);
407 if (i < 0) {
408 outpos += -i - 1;
409 break;
410 }
411 outpos += i;
412 #else
413 if (outputok)
414 putchar(*bufptr);
415 outpos++;
416 #endif /* CHARSUBST */
417 }
418 if (*tc_UC && ((highlight==artline && marking == STANDOUT)
419 #ifdef ULSMARTS
420 || under_lining
421 #endif
422 )) {
423 backspace();
424 underchar();
425 }
426 skip_put:
427 bufptr++;
428 }
429 else if (AT_NL(*bufptr) || !*bufptr) { /* newline? */
430 #ifdef ULSMARTS
431 if (under_lining) {
432 under_lining = 0;
433 un_underline();
434 }
435 #endif
436 #ifdef DEBUG
437 if (debug & DEB_INNERSRCH && outpos < tc_COLS - 6) {
438 standout();
439 printf("%4d",artline);
440 un_standout();
441 }
442 #endif
443 if (outputok)
444 newline();
445 restart = 0;
446 outpos = 1000; /* signal normal \n */
447 }
448 else if (*bufptr == '\t') { /* tab? */
449 int incpos = 8 - outpos % 8;
450 if (outputok) {
451 if (tc_GT)
452 putchar(*bufptr);
453 else
454 while (incpos--) putchar(' ');
455 }
456 bufptr++;
457 outpos += 8 - outpos % 8;
458 }
459 else if (*bufptr == '\f') { /* form feed? */
460 if (outpos+2 > tc_COLS)
461 break;
462 if (outputok)
463 fputs("^L",stdout);
464 if (bufptr == LINE_PTR(alinebeg) && highlight != artline)
465 linenum = 32700;
466 /* how is that for a magic number? */
467 bufptr++;
468 outpos += 2;
469 }
470 else { /* other control char */
471 if (dont_filter_control) {
472 if (outputok)
473 putchar(*bufptr);
474 outpos++;
475 }
476 else if (*bufptr != '\r' || bufptr[1] != '\n') {
477 if (outpos+2 > tc_COLS)
478 break;
479 if (outputok) {
480 putchar('^');
481 if (highlight == artline && *tc_UC
482 && marking == STANDOUT) {
483 backspace();
484 underchar();
485 putchar((*bufptr & 0x7F) ^ 0x40);
486 backspace();
487 underchar();
488 }
489 else
490 putchar((*bufptr & 0x7F) ^ 0x40);
491 }
492 outpos += 2;
493 }
494 bufptr++;
495 }
496
497 } /* end of column loop */
498
499 if (outpos < 1000) { /* did line overflow? */
500 restart = LINE_OFFSET(bufptr);/* restart here next time */
501 if (outputok) {
502 if (!tc_AM || tc_XN || outpos < tc_COLS)
503 newline();
504 else
505 term_line++;
506 }
507 if (AT_NL(*bufptr)) /* skip the newline */
508 restart = 0;
509 }
510
511 /* handle normal end of output line formalities */
512
513 if (highlight == artline) {
514 /* were we highlighting line? */
515 if (marking == STANDOUT)
516 un_standout();
517 else
518 un_underline();
519 carriage_return();
520 highlight = -1; /* no more we are */
521 /* in case terminal highlighted rest of line earlier */
522 /* when we did an eol with highlight turned on: */
523 if (erase_each_line)
524 erase_eol();
525 }
526 artline++; /* count the line just printed */
527 if (artline - tc_LINES + 1 > topline)
528 /* did we just scroll top line off? */
529 topline = artline - tc_LINES + 1;
530 /* then recompute top line # */
531 }
532
533 /* determine actual position in file */
534
535 if (restart) /* stranded somewhere in the buffer? */
536 artpos += restart - alinebeg;
537 else if (in_header)
538 artpos = index(headbuf+artpos,'\n') - headbuf + 1;
539 else
540 artpos = artbuf_pos + htype[PAST_HEADER].minpos;
541 vwtary(artline,artpos); /* remember pos in file */
542 } /* end of line loop */
543
544 #ifdef INNERSEARCH
545 innersearch = 0;
546 if (hide_everything) {
547 *buf = hide_everything;
548 hide_everything = 0;
549 goto fake_command;
550 }
551 #endif
552 if (linenum >= 32700) /* did last line have formfeed? */
553 vwtary(artline-1,-vrdary(artline-1));
554 /* remember by negating pos in file */
555
556 special = FALSE; /* end of page, so reset page length */
557 firstpage = FALSE; /* and say it is not 1st time thru */
558 highlight = -1;
559
560 /* extra loop bombout */
561
562 #ifdef SUPPORT_NNTP
563 if (artsize < 0 && (raw_artsize = nntp_artsize()) >= 0)
564 artsize = raw_artsize-artbuf_seek+artbuf_len+htype[PAST_HEADER].minpos;
565 recheck_pager:
566 if (do_hiding && artbuf_pos == artbuf_len) {
567 /* If we're filtering we need to figure out if any
568 * remaining text is going to vanish or not. */
569 long seekpos = artbuf_pos + htype[PAST_HEADER].minpos;
570 readartbuf(FALSE);
571 seekartbuf(seekpos);
572 }
573 #endif
574 if (artpos == artsize) {/* did we just now reach EOF? */
575 color_default();
576 set_mode(gmode,oldmode);
577 return DA_NORM; /* avoid --MORE--(100%) */
578 }
579
580 /* not done with this article, so pretend we are a pager */
581
582 reask_pager:
583 if (term_line >= tc_LINES) {
584 term_scrolled += term_line - tc_LINES + 1;
585 term_line = tc_LINES-1;
586 }
587 more_prompt_col = term_col;
588
589 unflush_output(); /* disable any ^O in effect */
590 maybe_eol();
591 color_default();
592 #ifdef SUPPORT_NNTP
593 if (artsize < 0)
594 strcpy(cmd_buf,"?");
595 else
596 #endif
597 sprintf(cmd_buf,"%ld",(long)(artpos*100/artsize));
598 #ifdef CHARSUBST
599 sprintf(buf,"%s--MORE--(%s%%)",current_charsubst(),cmd_buf);
600 #else
601 sprintf(buf,"--MORE--(%s%%)",cmd_buf);
602 #endif
603 outpos = term_col + strlen(buf);
604 draw_mousebar(tc_COLS - (term_line == tc_LINES-1? outpos+5 : 0), 1);
605 color_string(COLOR_MORE,buf);
606 fflush(stdout);
607 term_col = outpos;
608 eat_typeahead();
609 #ifdef DEBUG
610 if (debug & DEB_CHECKPOINTING) {
611 printf("(%d %d %d)",checkcount,linenum,artline);
612 fflush(stdout);
613 }
614 #endif
615 if (checkcount >= docheckwhen && linenum == tc_LINES
616 && (artline > 40 || checkcount >= docheckwhen+10)) {
617 /* while he is reading a whole page */
618 /* in an article he is interested in */
619 checkcount = 0;
620 checkpoint_newsrcs(); /* update all newsrcs */
621 #ifdef KILLFILES
622 update_thread_kfile();
623 #endif
624 }
625 cache_until_key();
626 #ifdef SUPPORT_NNTP
627 if (artsize < 0 && (raw_artsize = nntp_artsize()) >= 0) {
628 artsize = raw_artsize-artbuf_seek+artbuf_len+htype[PAST_HEADER].minpos;
629 goto_xy(more_prompt_col,term_line);
630 goto recheck_pager;
631 }
632 #endif
633 set_mode(gmode,'p');
634 getcmd(buf);
635 if (errno) {
636 if (tc_LINES < 100 && !int_count)
637 *buf = '\f';/* on CONT fake up refresh */
638 else {
639 *buf = 'q'; /* on INTR or paper just quit */
640 }
641 }
642 erase_line(erase_screen && erase_each_line);
643
644 fake_command: /* used by innersearch */
645 color_default();
646 output_chase_phrase = TRUE;
647
648 /* parse and process pager command */
649
650 if (mousebar_cnt)
651 clear_rest();
652 switch (page_switch()) {
653 case PS_ASK: /* reprompt "--MORE--..." */
654 goto reask_pager;
655 case PS_RAISE: /* reparse on article level */
656 set_mode(gmode,oldmode);
657 return DA_RAISE;
658 case PS_TOEND: /* fast pager loop exit */
659 set_mode(gmode,oldmode);
660 return DA_TOEND;
661 case PS_NORM: /* display more article */
662 break;
663 }
664 } /* end of page loop */
665 }
666
667 int
maybe_set_color(cp,backsearch)668 maybe_set_color(cp, backsearch)
669 char* cp;
670 bool_int backsearch;
671 {
672 register char ch = (cp == artbuf || cp == art_line? 0 : cp[-1]);
673 if (ch == '\001')
674 color_object(COLOR_MIMEDESC, 0);
675 else if (ch == '\002')
676 color_object(COLOR_MIMESEP, 0);
677 else if (ch == WRAPPED_NL) {
678 if (backsearch) {
679 while (cp > artbuf && cp[-1] != '\n') cp--;
680 maybe_set_color(cp, 0);
681 }
682 return 1;
683 }
684 else {
685 while (*cp == ' ' || *cp == '\t') cp++;
686 if (index(">}]#!:|", *cp))
687 color_object(COLOR_CITEDTEXT, 0);
688 else
689 color_object(COLOR_BODYTEXT, 0);
690 }
691 return 0;
692 }
693
694 /* process pager commands */
695
696 int
page_switch()697 page_switch()
698 {
699 register char* s;
700
701 switch (*buf) {
702 case '!': /* shell escape */
703 escapade();
704 return PS_ASK;
705 #ifdef INNERSEARCH
706 case Ctl('i'): {
707 ART_LINE i = artline;
708 ART_POS pos;
709 gline = 3;
710 s = LINE_PTR(alinebeg);
711 while (AT_NL(*s) && i >= topline) {
712 pos = vrdary(--i);
713 if (pos < 0)
714 pos = -pos;
715 if (pos < htype[PAST_HEADER].minpos)
716 break;
717 seekartbuf(pos);
718 if ((s = readartbuf(FALSE)) == NULL) {
719 s = LINE_PTR(alinebeg);
720 break;
721 }
722 }
723 sprintf(cmd_buf,"^[^%c\n]",*s);
724 compile(&gcompex,cmd_buf,TRUE,TRUE);
725 goto caseG;
726 }
727 case Ctl('g'):
728 gline = 3;
729 compile(&gcompex,"^Subject:",TRUE,TRUE);
730 goto caseG;
731 case 'g': /* in-article search */
732 if (!finish_command(FALSE))/* get rest of command */
733 return PS_ASK;
734 s = buf+1;
735 if (isspace(*s)) s++;
736 if ((s = compile(&gcompex,s,TRUE,TRUE)) != NULL) {
737 /* compile regular expression */
738 printf("\n%s\n",s) FLUSH;
739 termdown(2);
740 return PS_ASK;
741 }
742 erase_line(0); /* erase the prompt */
743 /* FALL THROUGH */
744 caseG:
745 case 'G': {
746 ART_POS start_where;
747 bool success;
748 char* nlptr;
749 char ch;
750
751 if (gline < 0 || gline > tc_LINES-2)
752 gline = tc_LINES-2;
753 #ifdef DEBUG
754 if (debug & DEB_INNERSRCH) {
755 printf("Start here? %d >=? %d\n",topline + gline + 1,artline)
756 FLUSH;
757 termdown(1);
758 }
759 #endif
760 if (*buf == Ctl('i') || topline+gline+1 >= artline)
761 start_where = artpos;
762 /* in case we had a line wrap */
763 else {
764 start_where = vrdary(topline+gline+1);
765 if (start_where < 0)
766 start_where = -start_where;
767 }
768 if (start_where < htype[PAST_HEADER].minpos)
769 start_where = htype[PAST_HEADER].minpos;
770 seekartbuf(start_where);
771 innerlight = 0;
772 innersearch = 0; /* assume not found */
773 while ((s = readartbuf(FALSE)) != NULL) {
774 if ((nlptr = index(s,'\n')) != NULL) {
775 ch = *++nlptr;
776 *nlptr = '\0';
777 }
778 #ifdef DEBUG
779 if (debug & DEB_INNERSRCH)
780 printf("Test %s\n",s) FLUSH;
781 #endif
782 success = execute(&gcompex,s) != NULL;
783 if (nlptr)
784 *nlptr = ch;
785 if (success) {
786 innersearch = artbuf_pos + htype[PAST_HEADER].minpos;
787 break;
788 }
789 }
790 if (!innersearch) {
791 seekartbuf(artpos);
792 fputs("(Not found)",stdout) FLUSH;
793 term_col = 11;
794 return PS_ASK;
795 }
796 #ifdef DEBUG
797 if (debug & DEB_INNERSRCH) {
798 printf("On page? %ld <=? %ld\n",(long)innersearch,(long)artpos)
799 FLUSH;
800 termdown(1);
801 }
802 #endif
803 if (innersearch <= artpos) { /* already on page? */
804 if (innersearch < artpos) {
805 artline = topline+1;
806 while (vrdary(artline) < innersearch)
807 artline++;
808 }
809 highlight = artline - 1;
810 #ifdef DEBUG
811 if (debug & DEB_INNERSRCH) {
812 printf("@ %d\n",highlight) FLUSH;
813 termdown(1);
814 }
815 #endif
816 topline = highlight - gline;
817 if (topline < -1)
818 topline = -1;
819 *buf = '\f'; /* fake up a refresh */
820 innersearch = 0;
821 return page_switch();
822 }
823 else { /* who knows how many lines it is? */
824 do_fseek = TRUE;
825 hide_everything = '\f';
826 }
827 return PS_NORM;
828 }
829 #else
830 case 'g': case 'G': case Ctl('g'):
831 notincl("g");
832 return PS_ASK;
833 #endif
834 case '\n': /* one line down */
835 case '\r':
836 special = TRUE;
837 slines = 2;
838 return PS_NORM;
839 case 'X':
840 rotate = !rotate;
841 /* FALL THROUGH */
842 case 'l':
843 case '\f': /* refresh screen */
844 refresh_screen:
845 #ifdef DEBUG
846 if (debug & DEB_INNERSRCH) {
847 printf("Topline = %d",topline) FLUSH;
848 fgets(buf, sizeof buf, stdin);
849 }
850 #endif
851 clear();
852 do_fseek = TRUE;
853 artline = topline;
854 if (artline < 0)
855 artline = 0;
856 firstpage = (topline < 0);
857 return PS_NORM;
858 #ifdef INNERSEARCH
859 case Ctl('e'):
860 #ifdef SUPPORT_NNTP
861 if (artsize < 0) {
862 nntp_finishbody(FB_OUTPUT);
863 raw_artsize = nntp_artsize();
864 artsize = raw_artsize-artbuf_seek+artbuf_len+htype[PAST_HEADER].minpos;
865 }
866 #endif
867 if (do_hiding) {
868 seekartbuf(artsize);
869 seekartbuf(artpos);
870 }
871 topline = artline;
872 innerlight = artline - 1;
873 innersearch = artsize;
874 gline = 0;
875 hide_everything = 'b';
876 return PS_NORM;
877 #endif
878 case 'B': /* one line up */
879 if (topline < 0)
880 break;
881 if (*tc_IL && *tc_HO) {
882 ART_POS pos;
883 home_cursor();
884 insert_line();
885 carriage_return();
886 pos = vrdary(topline-1);
887 if (pos < 0)
888 pos = -pos;
889 if (pos >= htype[PAST_HEADER].minpos) {
890 seekartbuf(pos);
891 if ((s = readartbuf(FALSE)) != NULL) {
892 artpos = vrdary(topline);
893 if (artpos < 0)
894 artpos = -artpos;
895 maybe_set_color(s, 1);
896 for (pos = artpos - pos; pos-- && !AT_NL(*s); s++)
897 putchar(*s);
898 color_default();
899 putchar('\n') FLUSH;
900 topline--;
901 artpos = vrdary(--artline);
902 if (artpos < 0)
903 artpos = -artpos;
904 seekartbuf(artpos);
905 alinebeg = vrdary(artline-1);
906 if (alinebeg < 0)
907 alinebeg = -alinebeg;
908 goto_xy(0,artline-topline);
909 erase_line(0);
910 return PS_ASK;
911 }
912 }
913 }
914 /* FALL THROUGH */
915 case 'b':
916 case Ctl('b'): { /* back up a page */
917 ART_LINE target;
918
919 if (erase_each_line)
920 home_cursor();
921 else
922 clear();
923
924 do_fseek = TRUE; /* reposition article file */
925 if (*buf == 'B')
926 target = topline - 1;
927 else {
928 target = topline - (tc_LINES - 2);
929 if (marking && (marking_areas & BACKPAGE_MARKING))
930 highlight = topline;
931 }
932 artline = topline;
933 if (artline >= 0) do {
934 artline--;
935 } while(artline >= 0 && artline > target && vrdary(artline-1) >= 0);
936 topline = artline; /* remember top line of screen */
937 /* (line # within article file) */
938 if (artline < 0)
939 artline = 0;
940 firstpage = (topline < 0);
941 return PS_NORM;
942 }
943 case 'H': /* help */
944 help_page();
945 return PS_ASK;
946 case 't': /* output thread data */
947 page_line = 1;
948 entire_tree(curr_artp);
949 return PS_ASK;
950 case '_':
951 if (!finish_dblchar())
952 return PS_ASK;
953 switch (buf[1] & 0177) {
954 #ifdef CHARSUBST
955 case 'C':
956 if (!*(++charsubst))
957 charsubst = charsets;
958 goto refresh_screen;
959 #endif
960 default:
961 break;
962 }
963 goto leave_pager;
964 case '\0': /* treat break as 'n' */
965 *buf = 'n';
966 /* FALL THROUGH */
967 case 'a': case 'A':
968 case 'e':
969 case 'k': case 'K': case 'J':
970 case 'n': case 'N': case Ctl('n'):
971 case 'F':
972 case 'R':
973 case 's': case 'S':
974 case 'T':
975 case 'u':
976 case 'w': case 'W':
977 case '|':
978 mark_as_read(artp); /* mark article as read */
979 /* FALL THROUGH */
980 case 'U': case ',':
981 case '<': case '>':
982 case '[': case ']':
983 case '{': case '}':
984 case '(': case ')':
985 case ':':
986 case '+':
987 case Ctl('v'): /* verify crypto signature */
988 #ifdef SCAN_ART
989 case ';': /* enter article scan mode */
990 #endif
991 #ifdef SCORE
992 case '"': /* append to local scorefile */
993 case '\'': /* score command */
994 #endif
995 case '#':
996 case '$':
997 case '&':
998 case '-':
999 case '.':
1000 case '/':
1001 case '1': case '2': case '3': case '4': case '5':
1002 case '6': case '7': case '8': case '9':
1003 case '=':
1004 case '?':
1005 case 'c': case 'C':
1006 #ifdef DEBUG
1007 case 'D':
1008 #endif
1009 case 'f': case Ctl('f'):
1010 case 'h':
1011 case 'j':
1012 case Ctl('k'):
1013 case 'm': case 'M':
1014 case 'p': case 'P': case Ctl('p'):
1015 case '`': case 'Q':
1016 case 'r': case Ctl('r'):
1017 case 'v':
1018 case 'x': case Ctl('x'):
1019 case 'Y':
1020 case 'z': case 'Z':
1021 case '^': case Ctl('^'):
1022 case '\b': case '\177':
1023 leave_pager:
1024 reread = FALSE;
1025 if (index("nNpP\016\020",*buf) == NULL
1026 && index("wWsSe:!&|/?123456789.",*buf) != NULL) {
1027 setdfltcmd();
1028 color_object(COLOR_CMD, 1);
1029 interpsearch(cmd_buf, sizeof cmd_buf, mailcall, buf);
1030 printf(prompt,cmd_buf,
1031 #ifdef CHARSUBST
1032 current_charsubst(),
1033 #else
1034 nullstr,
1035 #endif
1036 dfltcmd); /* print prompt, whatever it is */
1037 color_pop(); /* of COLOR_CMD */
1038 putchar(' ');
1039 fflush(stdout);
1040 }
1041 return PS_RAISE; /* and pretend we were at end */
1042 case 'd': /* half page */
1043 case Ctl('d'):
1044 special = TRUE;
1045 slines = tc_LINES / 2 + 1;
1046 /* no divide-by-zero, thank you */
1047 if (tc_LINES > 2 && (tc_LINES & 1) && artline % (tc_LINES-2) >= tc_LINES/2 - 1)
1048 slines++;
1049 goto go_forward;
1050 case 'y':
1051 case ' ': /* continue current article */
1052 if (erase_screen) { /* -e? */
1053 if (erase_each_line)
1054 home_cursor();
1055 else
1056 clear(); /* clear screen */
1057 fflush(stdout);
1058 }
1059 else {
1060 special = TRUE;
1061 slines = tc_LINES;
1062 }
1063 go_forward:
1064 if (*LINE_PTR(alinebeg) != '\f'
1065 #ifdef CUSTOMLINES
1066 && (!pagestop || continuation || !execute(&page_compex,LINE_PTR(alinebeg)))
1067 #endif
1068 ) {
1069 if (!special
1070 || (marking && (*buf!='d' || (marking_areas&HALFPAGE_MARKING)))) {
1071 restart = alinebeg;
1072 artline--; /* restart this line */
1073 artpos = alinebeg;
1074 if (special)
1075 up_line();
1076 else
1077 topline = artline;
1078 if (marking)
1079 highlight = artline;
1080 }
1081 else
1082 slines--;
1083 }
1084 return PS_NORM;
1085 case 'i':
1086 if ((auto_view_inline = !auto_view_inline) != 0)
1087 first_view = 0;
1088 printf("\nAuto-View inlined mime is %s\n", auto_view_inline? "on" : "off");
1089 termdown(2);
1090 break;
1091 case 'q': /* quit this article? */
1092 return PS_TOEND;
1093 default:
1094 fputs(hforhelp,stdout) FLUSH;
1095 termdown(1);
1096 settle_down();
1097 return PS_ASK;
1098 }
1099 return PS_ASK;
1100 }
1101
1102 bool
innermore()1103 innermore()
1104 {
1105 if (artpos < innersearch) { /* not even on page yet? */
1106 #ifdef DEBUG
1107 if (debug & DEB_INNERSRCH)
1108 printf("Not on page %ld < %ld\n",(long)artpos,(long)innersearch)
1109 FLUSH;
1110 #endif
1111 return TRUE;
1112 }
1113 if (artpos == innersearch) { /* just got onto page? */
1114 isrchline = artline; /* remember first line after */
1115 if (innerlight)
1116 highlight = innerlight;
1117 else
1118 highlight = artline - 1;
1119 #ifdef DEBUG
1120 if (debug & DEB_INNERSRCH) {
1121 printf("There it is %ld = %ld, %d @ %d\n",(long)artpos,
1122 (long)innersearch,hide_everything,highlight) FLUSH;
1123 termdown(1);
1124 }
1125 #endif
1126 if (hide_everything) { /* forced refresh? */
1127 topline = artline - gline - 1;
1128 if (topline < -1)
1129 topline = -1;
1130 return FALSE; /* let refresh do it all */
1131 }
1132 }
1133 #ifdef DEBUG
1134 if (debug & DEB_INNERSRCH) {
1135 printf("Not far enough? %d <? %d + %d\n",artline,isrchline,gline)
1136 FLUSH;
1137 termdown(1);
1138 }
1139 #endif
1140 if (artline < isrchline + gline)
1141 return TRUE;
1142 return FALSE;
1143 }
1144
1145 /* On click:
1146 * btn = 0 (left), 1 (middle), or 2 (right) + 4 if double-clicked;
1147 * x = 0 to tc_COLS-1; y = 0 to tc_LINES-1;
1148 * btn_clk = 0, 1, or 2 (no 4); x_clk = x; y_clk = y.
1149 * On release:
1150 * btn = 3; x = released x; y = released y;
1151 * btn_clk = click's 0, 1, or 2; x_clk = clicked x; y_clk = clicked y.
1152 */
1153 void
pager_mouse(btn,x,y,btn_clk,x_clk,y_clk)1154 pager_mouse(btn, x,y, btn_clk, x_clk,y_clk)
1155 int btn;
1156 int x, y;
1157 int btn_clk;
1158 int x_clk, y_clk;
1159 {
1160 ARTICLE* ap;
1161
1162 if (check_mousebar(btn, x,y, btn_clk, x_clk,y_clk))
1163 return;
1164
1165 if (btn != 3)
1166 return;
1167
1168 ap = get_tree_artp(x_clk,y_clk+topline+1+term_scrolled);
1169 if (ap && ap != get_tree_artp(x,y+topline+1+term_scrolled))
1170 return;
1171
1172 switch (btn_clk) {
1173 case 0:
1174 if (ap) {
1175 if (ap == artp)
1176 return;
1177 artp = ap;
1178 art = article_num(ap);
1179 reread = TRUE;
1180 pushchar(Ctl('r'));
1181 }
1182 else if (y > tc_LINES/2)
1183 pushchar(' ');
1184 else if (topline != -1)
1185 pushchar('b');
1186 break;
1187 case 1:
1188 if (ap) {
1189 select_subthread(ap, 0);
1190 special = TRUE;
1191 slines = 1;
1192 pushchar(Ctl('r'));
1193 }
1194 else if (y > tc_LINES/2)
1195 pushchar('\n');
1196 else if (topline != -1)
1197 pushchar('B');
1198 break;
1199 case 2:
1200 if (ap) {
1201 kill_subthread(ap, 0);
1202 special = TRUE;
1203 slines = 1;
1204 pushchar(Ctl('r'));
1205 }
1206 else if (y > tc_LINES/2)
1207 pushchar('n');
1208 else
1209 pushchar(Ctl('r'));
1210 break;
1211 }
1212 }
1213