1 /*
2 * (c) Copyright 1990, Kim Fabricius Storm. All rights reserved.
3 * Copyright (c) 1996-2005 Michael T Pins. All rights reserved.
4 *
5 * Article browser.
6 */
7
8 #include <stdlib.h>
9 #include <string.h>
10 #include <ctype.h>
11 #include "config.h"
12 #include "global.h"
13 #include "articles.h"
14 #include "answer.h"
15 #include "db.h"
16 #include "execute.h"
17 #include "folder.h"
18 #include "group.h"
19 #include "init.h"
20 #include "keymap.h"
21 #include "kill.h"
22 #include "macro.h"
23 #include "match.h"
24 #include "menu.h"
25 #include "news.h"
26 #include "regexp.h"
27 #include "save.h"
28 #include "nn_term.h"
29
30 /* more.c */
31
32 static void more_auto_select(void);
33 static char *a_st_flags(flag_type flag);
34 static void brief_header_line(register article_header * ah, int lno);
35
36 int monitor_mode = 0;
37 int compress_mode = 0;
38 int show_article_date = 1;
39 int first_page_lines = 0;
40 int overlap = 2;
41 int mark_overlap = 0;
42
43 #ifdef GNKSA
44 char *header_lines = "=F_S_D_n_W_R";
45 #else
46 char *header_lines = NULL;
47 #endif
48
49 int min_pv_window = 7;
50 int wrap_headers = 6;
51 int data_bits = 7;
52 int scroll_clear_page = 1;
53 int expired_msg_delay = 1;
54 char *trusted_escapes = NULL;
55 int new_read_prompt = 1;
56 int re_layout_more = -1;
57 int scroll_last_lines = 0;
58 int ignore_formfeed = 0;
59
60 extern int preview_window;
61 extern int novice;
62 extern int slow_mode;
63 extern int auto_preview_mode;
64 extern int flush_typeahead;
65 extern int case_fold_search;
66 extern int echo_prefix_key;
67 extern int re_layout;
68 extern char *folder_save_file, *default_save_file;
69 extern int show_art_next_invalid;
70 extern int mouse_y;
71 extern int mouse_state;
72 extern int STANDOUT;
73 extern int alt_cmd_key, in_menu_mode, any_message;
74 extern long n_selected;
75
76 extern char delayed_msg[];
77
78 static int rot13_must_init = 1;
79 static char rot13_table[128];
80 int rot13_active = 0;
81 #define ROT13_DECODE(c) ((c & 0x80) ? c : rot13_table[c])
82
83 static int compress_space;
84
85 static regexp *regular_expr = NULL;
86
87 #define LINEMAX 100 /* most articles are less than 100 lines */
88
89 int mark_overlap_shading = 0;
90
91 static struct header_def {
92 char field;
93 char *text;
94 char **news;
95 char **digest;
96 } header_defs[] = {
97
98 'A', "Approved", &news.ng_appr, 0,
99 'B', "Distribution", &news.ng_dist, 0,
100 'c', "Comment-To", &news.ng_comment, 0,
101 'C', "Control", &news.ng_control, 0,
102 'D', "Date", &news.ng_date, &digest.dg_date,
103 'F', "From", &news.ng_from, &digest.dg_from,
104 'f', "Sender", &news.ng_sender, 0,
105 'I', "Message-Id", &news.ng_ident, 0,
106 'J', "Originator", &news.ng_origr, 0,
107 'K', "Keywords", &news.ng_keyw, 0,
108 'L', "Lines", &news.ng_xlines, 0,
109 'N', "Newsgroups", &news.ng_groups, 0,
110 'O', "Organization", &news.ng_org, 0,
111 'P', "Path", &news.ng_path, 0,
112 'R', "Reply-To", &news.ng_reply, 0,
113 'S', "Subject", &news.ng_subj, &digest.dg_subj,
114 'W', "Followup-To", &news.ng_follow, 0,
115 'X', "References", &news.ng_ref, 0,
116 'Y', "Summary", &news.ng_summ, 0,
117 'd', "Date-Received", &news.ng_rdate, 0,
118 'g', "Newsgroup", &news.ng_groups, 0,
119 'G', "Newsgroup", &news.ng_groups, 0,
120 'n', "Newsgroups", &news.ng_groups, 0,
121 'x', "Back-Ref", &news.ng_bref, 0,
122 'v', "Save-File", NULL, 0,
123 'a', "Spool-File", NULL, 0,
124 'i', "DB-Info", NULL, 0,
125 0
126 };
127
128 static int
get_header_field(char code,char ** namep,char ** valp,register article_header * ah)129 get_header_field(char code, char **namep, char **valp, register article_header * ah)
130 {
131 static char special[FILENAME];
132 register struct header_def *hdef;
133 char *lp;
134
135 lp = NULL;
136 for (hdef = header_defs; hdef->field; hdef++) {
137 if (hdef->field != code)
138 continue;
139
140 if ((ah->flag & A_DIGEST) && hdef->digest)
141 lp = *(hdef->digest);
142 if (lp == NULL && hdef->news)
143 lp = *(hdef->news);
144 break;
145 }
146
147 switch (code) {
148 case 'n':
149 case 'g':
150 if (lp == NULL)
151 break;
152 if (!(current_group->group_flag & G_MERGED) && strchr(lp, ',') == NULL)
153 return 0;
154 if (code == 'n')
155 break;
156 /* FALLTHROUGH */
157 case 'G':
158 if (ah->a_number > 0) {
159 sprintf(special, "%s/%ld",
160 current_group->group_name, (long) ah->a_number);
161 lp = special;
162 } else
163 lp = current_group->group_name;
164 break;
165
166 case 'a':
167 if (current_group->group_flag & G_FOLDER)
168 lp = current_group->archive_file;
169 else if (group_file_name && *group_file_name)
170 lp = group_path_name;
171 break;
172
173 case 'v':
174 if ((lp = current_group->save_file) == NULL)
175 lp = (current_group->group_flag & G_FOLDER) ?
176 folder_save_file : default_save_file;
177 if (lp == NULL)
178 return 0;
179 if (expand_file_name(special, lp, 2))
180 lp = special;
181 break;
182
183 case 'i':
184 sprintf(special, "#%ld fl=%ld re=%d li=%d hp=%ld fp=%ld lp=%ld ts=%ld",
185 (long) ah->a_number, (long) ah->flag, (int) ah->replies,
186 ah->lines, ah->hpos, ah->fpos, (long) ah->lpos,
187 (long) ah->t_stamp);
188 lp = special;
189 break;
190 }
191 if (lp == NULL)
192 return 0;
193
194 *namep = hdef->text;
195 *valp = lp;
196 return 1;
197 }
198
199 static char *scan_codes;
200 static article_header *scan_arthdr;
201
202 void
scan_header_fields(char * fields,article_header * ah)203 scan_header_fields(char *fields, article_header * ah)
204 {
205 scan_codes = fields;
206 scan_arthdr = ah;
207 }
208
209 int
next_header_field(char ** namep,char ** valp,fct_type * attrp)210 next_header_field(char **namep, char **valp, fct_type * attrp)
211 {
212 fct_type attr;
213
214 while (*scan_codes) {
215 attr = NULL_FCT;
216 *namep = NULL;
217
218 switch (*scan_codes) {
219 case '*':
220 scan_codes++;
221 return 1; /* name == NULL */
222
223 case '=':
224 attr = highlight;
225 scan_codes++;
226 break;
227
228 case '_':
229 attr = underline;
230 scan_codes++;
231 break;
232
233 case '+':
234 attr = shadeline;
235 scan_codes++;
236 break;
237 }
238
239 if (*scan_codes == NUL)
240 break;
241
242 if (attrp)
243 *attrp = attr;
244 if (get_header_field(*scan_codes++, namep, valp, scan_arthdr))
245 return 1;
246 }
247 return 0;
248 }
249
250 static void
more_auto_select(void)251 more_auto_select(void)
252 {
253 register article_header *ah;
254 register int32 n;
255 int count = 0;
256
257 for (n = 0; n < n_articles; n++) {
258 ah = articles[n];
259 if (ah->attr == A_READ)
260 continue;
261 if (ah->attr & A_SELECT)
262 continue;
263
264 if (auto_select_article(ah, 2)) {
265 ah->attr = A_AUTO_SELECT;
266 show_art_next_invalid = 1;
267 count++;
268 }
269 }
270
271 if (count)
272 msg("Selected %d articles", count);
273 }
274
275 static char *
a_st_flags(flag_type flag)276 a_st_flags(flag_type flag)
277 {
278 static char buf[40];
279 register char *cp;
280 static flag_type prevflag = 0;
281
282 flag &= A_ST_FILED | A_ST_REPLY | A_ST_FOLLOW;
283 if (flag == 0) {
284 prevflag = 0;
285 return "";
286 }
287 if (flag == prevflag)
288 return buf;
289 prevflag = flag;
290
291 cp = buf;
292 *cp++ = '(';
293 if (flag & A_ST_FILED) {
294 *cp++ = 'F';
295 *cp++ = 'i';
296 *cp++ = 'l';
297 *cp++ = 'e';
298 *cp++ = 'd';
299 }
300 if (flag & A_ST_REPLY) {
301 if (cp[-1] != '(')
302 *cp++ = SP;
303 *cp++ = 'R';
304 *cp++ = 'e';
305 }
306 if (flag & A_ST_FOLLOW) {
307 if (cp[-1] != '(')
308 *cp++ = SP;
309 *cp++ = 'F';
310 *cp++ = 'o';
311 *cp++ = 'l';
312 }
313 strcpy(cp, new_read_prompt ? ")--" : ")------");
314 return buf;
315 }
316
317 static void
brief_header_line(register article_header * ah,int lno)318 brief_header_line(register article_header * ah, int lno)
319 {
320 int o_re_layout;
321
322 so_gotoxy(0, lno, 0);
323 so_printf("%s: ", ah->sender);
324 o_re_layout = re_layout;
325 if (re_layout_more >= 0)
326 re_layout = re_layout_more;
327 prt_replies(ah->replies);
328 re_layout = o_re_layout;
329 so_printf("%s", ah->subject);
330 so_end();
331 }
332
333 #ifdef NOV
334 /* fill in ah->?pos from the article itself */
335 /* This should really go into more itself */
336 void
setpos(register article_header * ah,register FILE * art)337 setpos(register article_header * ah, register FILE * art)
338 {
339 /* XXX: set to zero here? Digests? */
340 register long startpos = ftell(art);
341 char line[1024];
342
343 ah->fpos = 0; /* initialize in case not found */
344
345 /* header position == beginning of file */
346 ah->hpos = 0;
347
348 /* find the length */
349 fseek(art, 0L, 2);
350 ah->lpos = ftell(art);
351
352 /* always start from the beginning */
353 fseek(art, 0L, 0);
354 while (fgets(line, sizeof(line), art) != NULL)
355 if (*line == '\n') { /* end of header? */
356 ah->fpos = ftell(art); /* start of article */
357 break;
358 }
359 fseek(art, startpos, 0);
360 }
361
362 #endif /* NOV */
363
364
365 int
more(article_header * ah,int mode,int screen_offset)366 more(article_header * ah, int mode, int screen_offset)
367 {
368 register char *lp;
369 register int c, col, lno = 0;
370 register FILE *art;
371 int more_cmd, eof, skip_spaces, has_space, window_lines;
372 int form_feed, last_ff_line, ignore_nl = 0;
373 long lineposbuf[LINEMAX];
374 long *linepos = lineposbuf;
375 int linemax = LINEMAX;
376 char linebuf[200], skip_char;
377 int skip_wrap;
378 news_header_buffer ngheader, dgheader;
379 struct news_header news_save;
380 struct digest_header digest_save;
381 int linenum, maxline, topline, print_lines, lno1;
382 int scroll_lines, scroll_from;
383 off_t scroll_offset;
384 int underline_line, fake_underline;
385 int match_lines, match_redraw, match_topline = 0, match_botline;
386 int goto_line, prev_goto, stop_line, extra_lines;
387 flag_type in_digest = ah->flag & A_DIGEST;
388 article_header digestah;
389 char *fname, *hdrline;
390 char pr_fmt[200], send_date[40];
391 int match_expr, shade_overlap, shade_line;
392 int *key_map;
393 key_type cur_key;
394 fct_type hdrattr;
395 char *match_start, *match_end = NULL;
396 int open_modes, o_mode;
397
398 #ifdef RESIZING
399 int entry_col = Columns;
400 #endif
401
402 #define more_return(cmd) \
403 { more_cmd = cmd; goto more_exit; }
404
405 if (ah->a_group != NULL)
406 init_group(ah->a_group);
407
408 open_modes = SKIP_HEADER;
409 if (show_article_date || header_lines) {
410 open_modes |= FILL_NEWS_HEADER;
411 if (header_lines == NULL)
412 open_modes |= GET_DATE_ONLY;
413 else
414 open_modes |= GET_ALL_FIELDS;
415 if (in_digest)
416 open_modes |= FILL_DIGEST_HEADER;
417 }
418 art = open_news_article(ah, open_modes, ngheader, dgheader);
419
420 if (art == NULL) {
421 if (expired_msg_delay >= 0) {
422 msg("Canceled or expired: \"%s: %-.35s\"", ah->sender, ah->subject);
423 if ((mode & MM_PREVIEW) == 0 && expired_msg_delay > 0)
424 user_delay(expired_msg_delay);
425 }
426 return MC_NEXT;
427 }
428 o_mode = in_menu_mode;
429 in_menu_mode = 0;
430
431 if (screen_offset) {
432 if (preview_window < 1 && Lines - screen_offset < min_pv_window)
433 screen_offset = 0;
434 else {
435 brief_header_line(ah, screen_offset++);
436 if (!STANDOUT)
437 screen_offset++;
438 clrline();
439 }
440 }
441 if (show_article_date) {
442 if (in_digest && digest.dg_date)
443 strncpy(send_date, digest.dg_date, 40);
444 else if (news.ng_date) {
445 strncpy(send_date, news.ng_date, 40);
446 } else
447 send_date[0] = NUL;
448 send_date[39] = NUL;
449 if ((lp = strrchr(send_date, ':')))
450 *lp = NUL;
451 }
452 linepos[0] = ah->hpos;
453 linepos[1] = ah->fpos;
454 maxline = 1;
455 topline = 1;
456 hdrline = screen_offset == 0 ? header_lines : "";
457
458 rot13_active = 0;
459 compress_space = compress_mode;
460 last_ff_line = goto_line = -1, prev_goto = 1;
461 skip_char = NUL;
462 skip_wrap = 0;
463 match_lines = match_redraw = match_expr = 0;
464 underline_line = -1;
465 fake_underline = 0;
466 shade_overlap = 0;
467
468 /*
469 * initialization added by Peter Wemm.. These vars may be used before
470 * set, what should they *really* be set to to be safe? XXXX
471 */
472 window_lines = 0;
473 form_feed = 0;
474 lno1 = 0;
475 match_botline = 0;
476 extra_lines = 0;
477 cur_key = 0;
478 /* end */
479
480 scroll_lines = scroll_from = 0;
481 scroll_offset = 0;
482 if (scroll_last_lines < 0)
483 scroll_from = -1;
484 else if (scroll_last_lines > 0) {
485 if (ah->lines > 0)
486 scroll_from = ah->lines - scroll_last_lines;
487 else
488 scroll_offset = ah->lpos - (scroll_last_lines * 64);
489 }
490 stop_line = first_page_lines ? first_page_lines : -1;
491
492 if (new_read_prompt) {
493 char *xp, *group_name = current_group->group_name;
494 int len, maxl;
495
496 maxl = novice ? 18 : 25;
497
498 if ((len = strlen(group_name)) > maxl) {
499 for (xp = group_name + len - maxl; *xp; xp++) {
500 if (*xp == '.')
501 break;
502 if (*xp == '/') {
503 xp++;
504 break;
505 }
506 }
507 if (*xp)
508 group_name = xp;
509 else
510 group_name += len - maxl;
511 }
512 if (mode & (MM_PREVIEW | MM_DIGEST) || n_selected == 0)
513 sprintf(pr_fmt,
514 "\1\2--%s-- %s%s %s--%%s--%%s\1",
515 group_name,
516 (mode & MM_DIGEST) ? "DIGEST" :
517 (mode & MM_PREVIEW) ? "PREVIEW" : "LAST",
518 (ah->flag & A_NEXT_SAME) ? "+next" : "",
519 novice ? "--help:?" : "");
520 else
521 sprintf(pr_fmt,
522 "\1\2--%s-- %ld MORE%s %s--%%s--%%s\1",
523 group_name,
524 n_selected,
525 (ah->flag & A_NEXT_SAME) ? "+next" : "",
526 novice ? "--help:?" : "");
527 } else
528 sprintf(pr_fmt,
529 "\1\2-- %s%s %s-----%%s%s-----%%s\1",
530 (mode & MM_PREVIEW) ? "PREVIEW " : "",
531 (mode & MM_DIGEST) ? "FULL DIGEST" :
532 (mode & MM_LAST_SELECTED) ? "LAST ARTICLE" : "ARTICLE",
533 novice ? "-- help:? " : "",
534 (ah->flag & A_NEXT_SAME) ? " (+next)" : "");
535
536 if (screen_offset)
537 goto safe_redraw;
538
539 redraw: /* redraw that will destroy whole screen */
540 screen_offset = 0;
541
542 safe_redraw: /* redraw of "more window" only */
543 linenum = topline;
544
545 next_page:
546 no_raw();
547
548 s_keyboard = 0;
549
550 if (stop_line) {
551 lno = screen_offset;
552 if (scroll_clear_page || linenum <= 1) {
553 if (lno) {
554 gotoxy(0, lno);
555 clrpage();
556 } else
557 clrdisp();
558 }
559 if (linenum == 1) {
560 hdrline = screen_offset == 0 ? header_lines : "";
561 if (hdrline && hdrline[0] == '@') {
562 linenum = 0;
563 fseek(art, linepos[0], 0);
564 hdrline = NULL;
565 }
566 }
567 print_header:
568 if (hdrline == NULL || *hdrline == '*') {
569 if (hdrline && *++hdrline == NUL)
570 hdrline = NULL;
571
572 if (linenum <= 1) {
573 if (linenum == 0 || (mode & MM_DIGEST)) {
574 if (screen_offset) {
575 lno--;
576 if (!STANDOUT)
577 lno--;
578 gotoxy(0, lno);
579 }
580 so_printxy(0, lno,
581 "Newsgroup: %s, article: %ld%s",
582 current_group->group_name,
583 (long) (ah->a_number),
584 ((mode & MM_DIGEST) || in_digest)
585 ? " *DIGEST*" : "");
586 /* fseek(art, linepos[0], 0); */
587
588 lno++;
589 if (!STANDOUT)
590 lno++;
591 } else {
592 if (screen_offset == 0 && linenum == 1) {
593 if (show_article_date)
594 so_printxy(-1, 0, send_date);
595
596 brief_header_line(ah, lno++);
597 if (!STANDOUT)
598 lno++;
599 }
600 }
601 }
602 }
603 if (hdrline && screen_offset == 0) {
604
605 scan_header_fields(hdrline, ah);
606 while (next_header_field(&fname, &hdrline, &hdrattr)) {
607
608 if (fname == NULL) {
609 hdrline = --scan_codes; /* this is a hack! */
610 goto print_header;
611 }
612 lp = hdrline;
613
614 gotoxy(0, lno++);
615 tprintf("%s: ", fname);
616 c = col = strlen(fname) + 2;
617
618 split_header_line:
619 if (hdrattr)
620 CALL(hdrattr) (1);
621
622 while (*lp && c < Columns) {
623 if (isspace(*lp)) {
624 while (lp[1] && isspace(lp[1]))
625 lp++;
626 if (wrap_headers > 0 &&
627 (c + wrap_headers) >= Columns &&
628 (int) strlen(lp) >= wrap_headers) {
629 lp++;
630 break;
631 }
632 *lp = SP;
633 }
634 tputc(*lp++);
635 c++;
636 }
637 if (hdrattr)
638 CALL(hdrattr) (0);
639 if (*lp && wrap_headers >= 0) {
640 gotoxy(col, lno++);
641 c = col;
642 goto split_header_line;
643 }
644 }
645
646 hdrline = NULL;
647 tputc(NL);
648 lno++;
649 }
650 lno1 = lno;
651 topline = linenum;
652
653 window_lines = Lines - lno - 2;
654 print_lines = window_lines;
655
656 ignore_nl = 1; /* ignore blank lines at top op screen */
657 } else {
658 tputc(CR);
659 clrline();
660 print_lines = extra_lines; /* LINT complaints here -- ignore */
661 }
662
663 if (stop_line > 0) {
664 if (print_lines > stop_line) {
665 extra_lines = print_lines - stop_line;
666 print_lines = stop_line;
667 underline_line = -1;
668 shade_overlap = 0;
669 }
670 stop_line = 0;
671 } else
672 stop_line = -1;
673
674 next_line:
675
676 if (linenum == linemax) {
677 linemax += 500;
678 if (linepos == lineposbuf) {
679 linepos = newobj(long, linemax);
680 for (linenum = 0; linenum < LINEMAX; linenum++)
681 linepos[linenum] = lineposbuf[linenum];
682 } else
683 linepos = resizeobj(linepos, long, linemax);
684 }
685 if (goto_line == linenum) {
686 goto_line = -1;
687 goto next_page;
688 }
689 eof = 0;
690
691 if (linenum > maxline)
692 linepos[++maxline] = ftell(art);
693 else if (linenum > 0)
694 fseek(art, linepos[linenum], 0);
695
696
697 if (linepos[linenum] >= ah->lpos) {
698 if (match_expr) {
699 match_expr = 0;
700 topline = match_topline; /* LINT complaints here -- ignore */
701 linenum = match_botline; /* LINT complaints here -- ignore */
702 fseek(art, linepos[linenum], 0);
703 msg("Not found");
704 goto Prompt;
705 }
706 eof++;
707 if (goto_line > 0) {
708 goto_line = -1;
709 linenum -= window_lines / 2;
710 goto next_page;
711 }
712 goto Prompt;
713 }
714 if (linenum == 0) {
715 if (ftell(art) >= linepos[1]) {
716 linenum = 2; /* current line is 1st line ! */
717 lno1 = lno;
718 }
719 } else {
720 if (match_expr && linenum == match_botline)
721 msg("Searching...");
722 linenum++;
723 }
724
725 lp = linebuf;
726 col = 0;
727 form_feed = 0;
728
729 next_char:
730
731 c = getc(art);
732 if (c == EOF) {
733 eof++;
734 if (lp == linebuf)
735 goto Prompt;
736 goto end_line;
737 }
738 if (c & 0200) {
739 if (monitor_mode || data_bits != 8) {
740 col += 4;
741 if (col > Columns) {/* then there is no room for M-^X */
742 ungetc(c, art);
743 goto long_line;
744 }
745 c &= 0177;
746 *lp++ = 'M';
747 *lp++ = '-';
748 if (c < SP) {
749 *lp++ = '^';
750 c += '@';
751 } else
752 col--;
753 }
754 } else if (c < SP) {
755 if (monitor_mode) {
756 if (c == NL) {
757 *lp++ = '$';
758 goto end_line;
759 }
760 if (col + 2 > Columns) {
761 *lp++ = '\\';
762 ungetc(c, art);
763 goto end_line;
764 }
765 *lp++ = '^';
766 c += '@';
767 col++;
768 } else
769 switch (c) {
770
771 case '\f':
772 if (ignore_formfeed == 1)
773 goto default_control;
774 last_ff_line = linenum;
775 if (lp == linebuf) {
776 if (goto_line > 0 || skip_char || match_expr || lno == lno1)
777 goto next_line;
778 form_feed = 1;
779 goto Prompt;
780 }
781 form_feed = 1;
782 goto end_line;
783
784 case CR:
785 if (lp == linebuf || ignore_nl)
786 goto next_char;
787 ignore_nl = 1;
788 goto end_line;
789
790 case NL:
791 if (ignore_nl) {
792 ignore_nl = 0;
793 if (lp == linebuf) {
794 if (lno == lno1) {
795 ignore_nl = 1;
796 goto next_line;
797 }
798 goto next_char;
799 }
800 }
801 goto end_line;
802
803 case BS:
804 if (col) {
805 lp--;
806 col--;
807 }
808 goto next_char;
809
810 case TAB:
811 if (col + 8 - (col & 07) >= Columns)
812 goto long_line;
813
814 do {
815 *lp++ = SP;
816 col++;
817 } while (col & 07);
818 goto next_char;
819
820 case 033: /* ESC may be a start/end of kanji or similar */
821 if (trusted_escapes != NULL) {
822 if (col + 3 > Columns) {
823 ungetc(c, art);
824 goto long_line;
825 }
826 if (strcmp(trusted_escapes, "all") == 0)
827 break;
828 c = getc(art);
829 if (c == EOF)
830 goto next_char;
831 ungetc(c, art);
832 for (fname = trusted_escapes; *fname; fname++)
833 if (c == *fname)
834 break;
835 c = 033;
836 if (*fname != NUL)
837 break;
838 }
839 /* FALLTHROUGH */
840
841 default_control:
842 default:
843 if (col + 2 > Columns) {
844 ungetc(c, art);
845 goto long_line;
846 }
847 *lp++ = '^';
848 c += '@';
849 col++;
850 break;
851 }
852 } else if (rot13_active && linenum > 0)
853 c = ROT13_DECODE(c);
854
855 *lp++ = c;
856 col++;
857 ignore_nl = 0;
858
859 if (col < Columns)
860 goto next_char;
861
862 long_line:
863 ignore_nl = 1;
864
865 end_line:
866 /* if we are seaching for a specific line, repeat until it is found */
867 if (skip_wrap) {
868 skip_wrap = ignore_nl;
869 goto next_line;
870 }
871 if (goto_line >= linenum)
872 goto next_line;
873 if (skip_char) {
874 if (lp == linebuf || linebuf[0] == skip_char) {
875 skip_wrap = ignore_nl;
876 goto next_line;
877 }
878 skip_char = NUL;
879 if (overlap > 0) {
880 underline_line = linenum - 1;
881 linenum -= overlap + 1;
882 shade_overlap = mark_overlap_shading;
883 goto next_page;
884 }
885 }
886 *lp++ = NUL;
887
888 if (match_expr) {
889 if (!regexec_cf(regular_expr, linebuf))
890 goto next_line;
891 match_expr = 0;
892 match_lines = 1;
893 if (linenum > match_botline) {
894 match_redraw = 0;
895 if (last_ff_line > linenum)
896 last_ff_line = -1;
897 linenum -= 5;
898 if (linenum < last_ff_line)
899 linenum = last_ff_line;
900 goto next_page;
901 }
902 match_redraw = (stop_line < 0);
903 stop_line = -1;
904 lno = lno1 + linenum - topline - 1;
905 print_lines = window_lines - lno + lno1;
906 }
907 /* now print the line */
908
909 shade_line = 0;
910 if ((shade_overlap > 0 && linenum <= underline_line) ||
911 (shade_overlap < 0 && linenum > underline_line)) {
912 if (match_redraw)
913 goto no_print;
914 match_start = NULL;
915 shade_line = 1;
916 } else if (match_lines && underline_line != linenum &&
917 regexec_cf(regular_expr, linebuf)) {
918 match_start = regular_expr->startp[0];
919 match_end = regular_expr->endp[0];
920 if (match_start == match_end) {
921 match_start = NULL; /* null string */
922 if (match_redraw)
923 goto no_print;
924 }
925 } else {
926 if (match_redraw)
927 goto no_print;
928 match_start = NULL;
929 }
930
931 gotoxy(0, lno);
932 if (!scroll_clear_page)
933 clrline();
934
935 if (shade_line)
936 shadeline(1);
937 else if (mark_overlap && underline_line == linenum)
938 if (!underline(1))
939 fake_underline = 1;
940 skip_spaces = has_space = 0;
941
942 for (lp = linebuf; ((c = *lp)); lp++) {
943
944 if (match_start) {
945 if (lp == match_start)
946 highlight(1);
947 if (lp == match_end) {
948 highlight(0);
949 match_start = NULL;
950 if (regexec_cf(regular_expr, lp)) {
951 match_start = regular_expr->startp[0];
952 match_end = regular_expr->endp[0];
953 lp--;
954 continue;
955 }
956 if (match_redraw)
957 goto no_print;
958 }
959 }
960 if (c == SP) {
961 if (skip_spaces) {
962 if (has_space)
963 continue;
964 has_space++;
965 }
966 if (fake_underline)
967 c = '_';
968 } else {
969 if (compress_space && c != ' ') {
970 skip_spaces = 1;
971 has_space = 0;
972 }
973 }
974
975 tputc(c);
976 }
977
978 if (match_start)
979 highlight(0);
980
981 if (shade_line) {
982 shadeline(0);
983 if (shade_overlap > 0 && underline_line == linenum) {
984 underline_line = -1;
985 shade_overlap = 0;
986 }
987 } else if (mark_overlap && underline_line == linenum) {
988 while (lp - linebuf < 10) {
989 tputc(fake_underline ? '_' : ' ');
990 lp++;
991 }
992 underline(0);
993 underline_line = -1;
994 fake_underline = 0;
995 }
996 no_print:
997
998 ++lno;
999 if (--print_lines > 0 && s_keyboard == 0 && form_feed == 0)
1000 goto next_line;
1001
1002 if (shade_overlap) {
1003 underline_line = -1;
1004 shade_overlap = 0;
1005 }
1006 if (!eof && linenum >= maxline) {
1007 if (ignore_nl) {
1008 c = getc(art);
1009 if (c == EOF)
1010 eof++;
1011 else if (c != NL)
1012 ungetc(c, art);
1013 else
1014 ignore_nl = 0;
1015 }
1016 if (!eof && ftell(art) >= ah->lpos)
1017 eof++;
1018 }
1019 match_redraw = 0;
1020
1021 Prompt:
1022
1023 if (eof && lno == screen_offset)
1024 more_return(MC_NEXT);
1025
1026 if (scroll_lines > 0) {
1027 if (eof)
1028 scroll_lines = 0;
1029 else {
1030 print_lines = 1;
1031 scroll_lines--;
1032 prompt_line = lno;
1033 goto scroll_next;
1034 }
1035 }
1036 nn_raw();
1037
1038 prompt_line = lno;
1039
1040 if (!scroll_clear_page)
1041 clrpage();
1042
1043 dflt_prompt:
1044
1045 prompt(pr_fmt,
1046 pct(ah->fpos, (long) (ah->lpos),
1047 linepos[topline], ftell(art)),
1048 a_st_flags(ah->flag));
1049
1050 if (delayed_msg[0] != NUL) {
1051 msg(delayed_msg);
1052 delayed_msg[0] = NUL;
1053 }
1054 same_prompt:
1055
1056 if (flush_typeahead)
1057 flush_input();
1058
1059 key_map = more_key_map;
1060
1061 prefix_key:
1062 if ((c = get_c()) & GETC_COMMAND)
1063 c &= ~GETC_COMMAND;
1064 else
1065 c = key_map[cur_key = c];
1066
1067 if (s_hangup)
1068 c = K_QUIT;
1069
1070 if (c & K_PREFIX_KEY) {
1071 key_map = keymaps[c & ~K_PREFIX_KEY].km_map;
1072 if (echo_prefix_key)
1073 msg("%s", key_name(cur_key));
1074 goto prefix_key;
1075 }
1076 if (any_message && c != K_LAST_MESSAGE)
1077 clrmsg(0);
1078
1079 if (c & K_MACRO) {
1080 m_invoke(c & ~K_MACRO);
1081 goto same_prompt;
1082 }
1083 #define STATE(new_state) c = new_state; goto alt_key;
1084
1085 alt_key:
1086
1087 switch (c) {
1088 case K_UNBOUND:
1089 ding();
1090 /* FALLTHROUGH */
1091 case K_INVALID:
1092 goto same_prompt;
1093
1094 case K_REDRAW:
1095
1096 #ifdef RESIZING
1097 if (Columns != entry_col) {
1098 entry_col = Columns;
1099 maxline = topline = 1;
1100 }
1101 #endif
1102
1103 goto redraw;
1104
1105 case K_M_CONTINUE:
1106 if (mouse_y >= 0) {
1107 if (mouse_y >= prompt_line) {
1108 STATE(K_NEXT_ARTICLE);
1109 } else if (mouse_y == 0) {
1110 if (topline == 1) {
1111 STATE(K_PREVIOUS);
1112 } else {
1113 STATE(K_PREV_PAGE);
1114 }
1115 } else {
1116 STATE(K_CONTINUE);
1117 }
1118 }
1119 /* fall through??? */
1120
1121 case K_NEXT_PAGE:
1122 if (eof) {
1123 ding();
1124 goto same_prompt;
1125 }
1126 /* FALLTHROUGH */
1127 case K_CONTINUE:
1128 if (eof)
1129 break;
1130 if (screen_offset == 0 && form_feed == 0 && stop_line) {
1131 if ((scroll_from && linenum > scroll_from) ||
1132 (scroll_offset && ftell(art) > scroll_offset)) {
1133 scroll_lines = Lines - 2 - overlap;
1134 print_lines = 1;
1135 goto scroll;
1136 }
1137 if (linenum > overlap) {
1138 underline_line = linenum;
1139 linenum -= overlap;
1140 shade_overlap = mark_overlap_shading;
1141 }
1142 }
1143 goto next_page;
1144
1145 case K_LAST_MESSAGE:
1146 msg((char *) NULL);
1147 goto dflt_prompt;
1148
1149 case K_HELP:
1150 display_help("more");
1151 goto redraw;
1152
1153 case K_SHELL:
1154 tputc(CR);
1155 if (shell_escape())
1156 goto redraw;
1157 goto dflt_prompt;
1158
1159 case K_VERSION:
1160 prompt(P_VERSION);
1161 goto same_prompt;
1162
1163 case K_M_TOGGLE:
1164 if (mouse_state) {
1165 xterm_mouse_off();
1166 mouse_state = 0;
1167 } else {
1168 xterm_mouse_on();
1169 mouse_state = 1;
1170 }
1171 goto dflt_prompt;
1172
1173 case K_EXTENDED_CMD:
1174 news_save = news;
1175 digest_save = digest;
1176 more_cmd = alt_command();
1177 news = news_save;
1178 digest = digest_save;
1179
1180 switch (more_cmd) {
1181
1182 case AC_UNCHANGED:
1183 goto same_prompt;
1184
1185 case AC_QUIT:
1186 more_return(MC_QUIT);
1187 break; /* for lint */
1188
1189 case AC_PROMPT:
1190 goto dflt_prompt;
1191
1192 case AC_REENTER_GROUP:
1193 more_return(MC_REENTER_GROUP);
1194 break; /* for lint */
1195
1196 case AC_REORDER:
1197 more_return(MC_MENU);
1198 break; /* for lint */
1199
1200 case AC_REDRAW:
1201 goto redraw;
1202
1203 case AC_KEYCMD:
1204 c = alt_cmd_key;
1205 goto alt_key;
1206 }
1207 /* XXX: fall-thru? */
1208 case K_QUIT:
1209 ah->attr = A_LEAVE_NEXT;
1210 more_return(MC_QUIT);
1211 break; /* for lint */
1212
1213 case K_SAVE_NO_HEADER:
1214 case K_SAVE_SHORT_HEADER:
1215 case K_SAVE_FULL_HEADER:
1216 case K_SAVE_HEADER_ONLY:
1217 case K_PRINT:
1218 case K_UNSHAR:
1219 case K_PATCH:
1220 case K_UUDECODE:
1221 news_save = news;
1222 digest_save = digest;
1223
1224 tputc(CR);
1225 if (init_save(c, (char **) NULL) != NULL) {
1226 if (c == K_UNSHAR)
1227 prompt_line = Lines - 2;
1228
1229 save(ah);
1230 end_save();
1231 }
1232 news = news_save;
1233 digest = digest_save;
1234 if (!slow_mode && (c == K_UNSHAR || c == K_PATCH)) {
1235 tprintf("\r\n\n");
1236 any_key(0);
1237 goto redraw;
1238 }
1239 goto Prompt;
1240
1241 case K_FOLLOW_UP:
1242 case K_REPLY:
1243 case K_MAIL_OR_FORWARD:
1244 news_save = news;
1245 digest_save = digest;
1246 more_cmd = answer(ah, c, -1);
1247 news = news_save;
1248 digest = digest_save;
1249 if (more_cmd) {
1250 if (slow_mode)
1251 clrdisp();
1252 else
1253 goto redraw;
1254 }
1255 goto Prompt;
1256
1257 case K_POST:
1258 if (post_menu()) {
1259 if (slow_mode)
1260 clrdisp();
1261 else
1262 goto redraw;
1263 }
1264 goto Prompt;
1265
1266 case K_CANCEL:
1267 if (current_group->group_flag & G_FOLDER) {
1268 prompt("%s this folder entry",
1269 (ah->attr == A_CANCEL) ? "UNcancel" : "Cancel");
1270 if (yes(0))
1271 fcancel(ah);
1272 goto Prompt;
1273 }
1274 if (cancel(ah) > 0)
1275 goto Prompt;
1276 more_return(MC_NEXT);
1277 break; /* for lint */
1278
1279 case K_UNSUBSCRIBE:
1280 if (!unsubscribe(current_group))
1281 goto Prompt;
1282 if ((current_group->group_flag & G_UNSUBSCRIBED) == 0)
1283 goto Prompt;
1284 more_return(MC_NEXTGROUP);
1285 break; /* for lint */
1286
1287 case K_GROUP_OVERVIEW:
1288 group_overview(-1);
1289 goto redraw;
1290
1291 case K_KILL_HANDLING:
1292 switch (kill_menu(ah)) {
1293 case 0:
1294 more_auto_select();
1295 ah->attr = 0;
1296 break;
1297 case 1:
1298 more_return(MC_DO_KILL);
1299 default:
1300 break;
1301 }
1302 goto Prompt;
1303
1304 case K_READ_GROUP_UPDATE:
1305 if (mode & MM_PREVIEW)
1306 more_return(MC_MENU);
1307 prompt("Mark rest of current group as read?");
1308 if (yes(1) <= 0)
1309 goto Prompt;
1310 more_return(MC_READGROUP);
1311 break; /* for lint */
1312
1313 case K_NEXT_GROUP_NO_UPDATE:
1314 if (mode & MM_PREVIEW)
1315 more_return(MC_MENU);
1316 more_return(MC_NEXTGROUP);
1317 break; /* for lint */
1318
1319 case K_BACK_TO_MENU:
1320 more_return(MC_MENU);
1321 break; /* for lint */
1322
1323 case K_PREVIOUS:
1324 if ((mode & MM_PREVIOUS) == 0) {
1325 msg("No previous article");
1326 goto dflt_prompt;
1327 }
1328 more_return(MC_PREV);
1329
1330 /* FALLTHROUGH */
1331 case K_ADVANCE_GROUP:
1332 case K_BACK_GROUP:
1333 case K_GOTO_GROUP:
1334 news_save = news;
1335 digest_save = digest;
1336 more_cmd = goto_group(c, ah, (flag_type) 0);
1337 news = news_save;
1338 digest = digest_save;
1339
1340 switch (more_cmd) {
1341 case ME_NO_REDRAW:
1342 goto Prompt;
1343
1344 case ME_QUIT:
1345 more_return(ME_QUIT);
1346
1347 default:
1348 goto redraw;
1349 }
1350
1351 case K_NEXT_LINE:
1352 if (eof)
1353 break;
1354 if (screen_offset)
1355 goto same_prompt;
1356
1357 print_lines = 1;
1358 goto scroll;
1359
1360 case K_NEXT_HALF_PAGE:
1361 if (eof)
1362 break;
1363 if (screen_offset)
1364 goto same_prompt;
1365
1366 print_lines = window_lines / 2;
1367
1368 scroll:
1369 gotoxy(0, prompt_line);
1370 clrpage();
1371 no_raw();
1372
1373 if (print_lines + lno < (Lines - 1))
1374 goto next_page;
1375
1376 stop_line = -1;
1377
1378 scroll_next:
1379 gotoxy(0, Lines - 1);
1380 c = print_lines + lno - Lines + 2;
1381 while (--c >= 0) {
1382 tputc(NL);
1383 if (--lno1 < 0)
1384 topline++;
1385 prompt_line--;
1386 }
1387 if (lno1 < 0)
1388 lno1 = 0;
1389 if (prompt_line < 0)
1390 prompt_line = 0;
1391 lno = prompt_line;
1392 goto next_line;
1393
1394 case K_PREV_HALF_PAGE:
1395 /* XXX: Bug: will not back over headers */
1396 if (topline <= 1)
1397 goto Prompt;
1398 linenum = topline - window_lines / 2;
1399 if (linenum < 1)
1400 linenum = 1;
1401 goto next_page;
1402
1403 case K_PREV_PAGE:
1404 /* XXX: Bug: will not back over headers */
1405 if (topline <= 1)
1406 goto Prompt;
1407 linenum = topline - window_lines + overlap; /* not perfect after FF */
1408 underline_line = topline;
1409 shade_overlap = -mark_overlap_shading;
1410 if (linenum < 1)
1411 linenum = 1;
1412 goto next_page;
1413
1414 case K_SKIP_LINES:
1415 skip_char = linebuf[0];
1416 goto next_page;
1417
1418 case K_GOTO_LINE:
1419 prompt("\1Go to line:\1 ");
1420 if ((fname = get_s(NONE, NONE, "$^", NULL_FCT)) == NULL)
1421 goto Prompt;
1422
1423 if (*fname == NUL) {
1424 if (prev_goto < 0)
1425 goto Prompt;
1426 goto_line = prev_goto;
1427
1428 } else if (*fname == '$')
1429 goto_line = 30000;
1430 else if (*fname == '^')
1431 goto_line = 1;
1432 else {
1433 goto_line = atoi(fname);
1434 if (goto_line <= 0) {
1435 goto_line = -1;
1436 goto Prompt;
1437 }
1438 }
1439
1440 goto_page:
1441 prev_goto = topline;
1442
1443 if (goto_line <= maxline) {
1444 linenum = goto_line;
1445 goto_line = -1;
1446 }
1447 goto next_page;
1448
1449 case K_SELECT_SUBJECT:
1450 more_return(MC_ALLSUBJ);
1451 break; /* for lint */
1452
1453 case K_HEADER_PAGE:
1454 fseek(art, linepos[0], 0);
1455 goto_line = 0;
1456 goto goto_page;
1457
1458 case K_FIRST_PAGE:
1459 goto_line = 1;
1460 goto goto_page;
1461
1462 case K_LAST_PAGE:
1463 goto_line = 30000;
1464 goto goto_page;
1465
1466 case K_GOTO_MATCH:
1467 prompt("\1/\1");
1468 if ((fname = get_s(NONE, NONE, "/", NULL_FCT)) == NULL)
1469 goto Prompt;
1470
1471 if (*fname && *fname != '/') {
1472 if (regular_expr)
1473 freeobj(regular_expr);
1474 if (case_fold_search)
1475 fold_string(fname);
1476 regular_expr = regcomp(fname);
1477 match_lines = 0;
1478 }
1479 /* XXX: fall here? */
1480 case K_NEXT_MATCH:
1481 if (regular_expr == NULL) {
1482 msg("No previous expression");
1483 goto Prompt;
1484 }
1485 match_expr = 1;
1486 if (match_topline != topline)
1487 prev_goto = topline;
1488 match_topline = topline;
1489 match_botline = linenum;
1490 if (match_lines == 0 && topline <= 1)
1491 linenum = topline;
1492 match_lines = 0;
1493 goto next_line; /* don't clear the screen if no match */
1494
1495 case K_FULL_DIGEST:
1496 if (mode & MM_DIGEST)
1497 more_return(MC_NO_REDRAW);
1498
1499 if (!in_digest)
1500 goto same_prompt;
1501
1502 /* could do something more clever here later */
1503 digestah = *ah;
1504 digestah.flag &= ~A_DIGEST;
1505 digestah.hpos = digestah.fpos = 0;
1506 fseek(art, 0L, 2);
1507 digestah.lpos = ftell(art);
1508
1509 switch (more(&digestah, mode | MM_DIGEST, screen_offset)) {
1510
1511 case MC_REDRAW:
1512 goto redraw;
1513
1514 case MC_NO_REDRAW:
1515 goto safe_redraw;
1516
1517 case MC_QUIT:
1518 more_return(MC_QUIT);
1519 break; /* for lint */
1520
1521 case MC_REENTER_GROUP:
1522 more_return(MC_REENTER_GROUP);
1523
1524 default:
1525 goto safe_redraw;
1526 }
1527
1528 case K_LEAVE_NEXT:
1529 ah->attr = A_LEAVE_NEXT;
1530 more_return(MC_PREVIEW_NEXT);
1531 break; /* for lint */
1532
1533 case K_LEAVE_ARTICLE:
1534 ah->attr = (mode & MM_PREVIEW) ? A_SELECT : A_LEAVE;
1535
1536 /* FALLTHROUGH */
1537 case K_NEXT_ARTICLE:
1538 if ((mode & MM_PREVIEW) == 0)
1539 break;
1540 more_return(MC_PREVIEW_NEXT);
1541 break; /* for lint */
1542
1543 case K_BACK_ARTICLE:
1544 if (mode & MM_FIRST_ARTICLE) {
1545 msg("First article is displayed");
1546 goto same_prompt;
1547 }
1548 more_return(MC_BACK_ART);
1549 break; /* for lint */
1550
1551 case K_FORW_ARTICLE:
1552 if (mode & MM_LAST_ARTICLE) {
1553 msg("Last article is displayed");
1554 goto same_prompt;
1555 }
1556 more_return(MC_FORW_ART);
1557 break; /* for lint */
1558
1559 case K_NEXT_SUBJECT:
1560 more_return(MC_NEXTSUBJ);
1561 break; /* for lint */
1562
1563 case K_ROT13:
1564 if (rot13_must_init) {
1565 register int i;
1566 for (i = 0; i <= 127; i++) {
1567 c = i;
1568 if (c >= 'a' && c <= 'm')
1569 c += 13;
1570 else if (c >= 'n' && c <= 'z')
1571 c -= 13;
1572 else if (c >= 'A' && c <= 'M')
1573 c += 13;
1574 else if (c >= 'N' && c <= 'Z')
1575 c -= 13;
1576 rot13_table[i] = c;
1577 }
1578 rot13_must_init = 0;
1579 }
1580 rot13_active = !rot13_active;
1581 goto safe_redraw;
1582
1583 case K_COMPRESS:
1584 compress_space = !compress_space;
1585 goto safe_redraw;
1586
1587 case K_PREVIEW:
1588 if (mode & MM_PREVIEW)
1589 more_return(MC_PREVIEW_OTHER);
1590
1591 /* fall thru to "default" */
1592
1593 default:
1594 msg("Command %d not supported", c);
1595 goto dflt_prompt;
1596 }
1597
1598 more_return(MC_NEXT);
1599
1600 more_exit:
1601 in_menu_mode = o_mode;
1602 rot13_active = 0;
1603
1604 if (linepos != lineposbuf)
1605 freeobj(linepos);
1606
1607 no_raw();
1608 fclose(art);
1609
1610 if (mode & MM_PREVIEW)
1611 if (more_cmd != MC_QUIT && more_cmd != MC_REENTER_GROUP) {
1612 gotoxy(0, screen_offset);
1613 clrpage();
1614 if (auto_preview_mode && ah->attr == 0)
1615 ah->attr = A_READ;
1616 if (screen_offset == 0)
1617 prompt_line = -1;
1618 }
1619 return more_cmd;
1620 }
1621
1622
1623 void
rot13_line(register char * cp)1624 rot13_line(register char *cp)
1625 {
1626 register int c;
1627
1628 while ((c = *cp))
1629 *cp++ = ROT13_DECODE(c);
1630 }
1631