tin  2.6.1
About: TIN is a threaded NNTP and spool based UseNet newsreader.
  Fossies Dox: tin-2.6.1.tar.xz  ("unofficial" and yet experimental doxygen-generated source code documentation)  

page.c
Go to the documentation of this file.
1/*
2 * Project : tin - a Usenet reader
3 * Module : page.c
4 * Author : I. Lea & R. Skrenta
5 * Created : 1991-04-01
6 * Updated : 2021-09-21
7 * Notes :
8 *
9 * Copyright (c) 1991-2022 Iain Lea <iain@bricbrac.de>, Rich Skrenta <skrenta@pbm.com>
10 * All rights reserved.
11 *
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
14 * are met:
15 *
16 * 1. Redistributions of source code must retain the above copyright notice,
17 * this list of conditions and the following disclaimer.
18 *
19 * 2. Redistributions in binary form must reproduce the above copyright
20 * notice, this list of conditions and the following disclaimer in the
21 * documentation and/or other materials provided with the distribution.
22 *
23 * 3. Neither the name of the copyright holder nor the names of its
24 * contributors may be used to endorse or promote products derived from
25 * this software without specific prior written permission.
26 *
27 * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
28 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
31 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
32 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
33 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
34 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
35 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
36 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
37 * POSSIBILITY OF SUCH DAMAGE.
38 */
39
40
41#ifndef TIN_H
42# include "tin.h"
43#endif /* !TIN_H */
44#ifndef TCURSES_H
45# include "tcurses.h"
46#endif /* !TCURSES_H */
47
48
49/*
50 * PAGE_HEADER is the size in lines of the article page header
51 * ARTLINES is the number of lines available to display actual article text.
52 */
53#define PAGE_HEADER 4
54#define ARTLINES (NOTESLINES - (PAGE_HEADER - INDEX_TOP))
55
56int curr_line; /* current line in art (indexed from 0) */
57static FILE *note_fp; /* active stream (raw or cooked) */
58static int artlines; /* active # of lines in pager */
59static t_lineinfo *artline; /* active 'lineinfo' data */
60
62
63t_openartinfo pgart = /* Global context of article open in the pager */
64 {
65 { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, FALSE, NULL},
66 FALSE, 0,
67 NULL, NULL, NULL, NULL,
68 };
69
70int last_resp; /* previous & current article # in arts[] for '-' command */
72
73size_t tabwidth = 8;
74
75static struct t_header *note_h = &pgart.hdr; /* Easy access to article headers */
76
77static FILE *info_file;
78static const char *info_title;
79static int curr_info_line;
80static int hide_uue; /* set when uuencoded sections are 'hidden' */
81static int num_info_lines;
82static int reveal_ctrl_l_lines; /* number of lines (from top) with de-activated ^L */
83static int rotate; /* 0=normal, 13=rot13 decode */
84static int scroll_region_top; /* first screen line for displayed message */
85static int search_line; /* Line to commence next search from */
87
88static t_bool show_all_headers; /* all headers <-> headers in news_headers_to[_not]_display */
89static t_bool show_raw_article; /* CTRL-H raw <-> cooked article */
90static t_bool reveal_ctrl_l; /* set when ^L hiding is off */
91
92/*
93 * Local prototypes
94 */
95static int build_url_list(void);
96static int load_article(int new_respnum, struct t_group *group);
97static int prompt_response(int ch, int curr_respnum);
98static int scroll_page(int dir);
100static t_bool activate_last_ctrl_l(void);
101static t_bool process_url(int n);
102static t_bool url_page(void);
103static t_function page_left(void);
104static t_function page_right(void);
105static t_function page_mouse_action(t_function (*left_action) (void), t_function (*right_action) (void));
106static t_function url_left(void);
107static t_function url_right(void);
108static void build_url_line(int i);
109static void draw_page_header(const char *group);
110static void draw_percent_mark(long cur_num, long max_num);
111static void draw_url_arrow(void);
112static void free_url_list(void);
113static void preprocess_info_message(FILE *info_fh);
114static void print_message_page(FILE *file, t_lineinfo *messageline, size_t messagelines, size_t base_line, size_t begin, size_t end, int help_level);
115static void process_search(int *lcurr_line, size_t message_lines, size_t screen_lines, int help_level);
116static void show_url_page(void);
117static void invoke_metamail(FILE *fp);
118
120
121#ifdef XFACE_ABLE
122# define XFACE_SHOW() if (tinrc.use_slrnface) \
123 slrnface_show_xface()
124# define XFACE_CLEAR() if (tinrc.use_slrnface) \
125 slrnface_clear_xface()
126# define XFACE_SUPPRESS() if (tinrc.use_slrnface) \
127 slrnface_suppress_xface()
128#else
129# define XFACE_SHOW() /*nothing*/
130# define XFACE_CLEAR() /*nothing*/
131# define XFACE_SUPPRESS() /*nothing*/
132#endif /* XFACE_ABLE */
133
134/*
135 * Scroll visible article part of display down (+ve) or up (-ve)
136 * according to 'dir' (KEYMAP_UP or KEYMAP_DOWN) and tinrc.scroll_lines
137 * >= 1 line count
138 * 0 full page scroll
139 * -1 full page but retain last line of prev page when scrolling
140 * down. Obviously only applies when scrolling down.
141 * -2 half page scroll
142 * Return the offset we scrolled by so that redrawing can be done
143 */
144static int
146 int dir)
147{
148 int i;
149
150 if (tinrc.scroll_lines >= 1)
152 else {
154 switch (tinrc.scroll_lines) {
155 case 0:
156 break;
157
158 case -1:
159 i--;
160 break;
161
162 case -2:
163 i >>= 1;
164 break;
165 }
166 }
167
168 if (dir == KEYMAP_UP)
169 i = -i;
170
171#ifdef USE_CURSES
172 scrollok(stdscr, TRUE);
173#endif /* USE_CURSES */
175 ScrollScreen(i);
177#ifdef USE_CURSES
178 scrollok(stdscr, FALSE);
179#endif /* USE_CURSES */
180
181 return i;
182}
183
184
185/*
186 * Map keypad codes to standard keyboard characters
187 */
188static t_function
190 void)
191{
192 return GLOBAL_QUIT;
193}
194
195
196static t_function
198 void)
199{
200 return PAGE_NEXT_UNREAD;
201}
202
203
204static t_function
206 t_function (*left_action) (void),
207 t_function (*right_action) (void))
208{
210
211 switch (xmouse) {
212 case MOUSE_BUTTON_1:
213 if (xrow < PAGE_HEADER || xrow >= cLINES - 1)
215 else
216 func = right_action();
217 break;
218
219 case MOUSE_BUTTON_2:
220 if (xrow < PAGE_HEADER || xrow >= cLINES - 1)
222 else
223 func = left_action();
224 break;
225
226 case MOUSE_BUTTON_3:
228 break;
229
230 default:
231 break;
232 }
233 return func;
234}
235
236
237/*
238 * Make hidden part of article after ^L visible.
239 * Returns:
240 * FALSE no ^L found, no changes
241 * TRUE ^L found and displayed page must be updated
242 * (draw_page must be called)
243 */
244static t_bool
246 void)
247{
248 int i;
249 int end = curr_line + ARTLINES;
250
251 if (reveal_ctrl_l)
252 return FALSE;
253 if (end > artlines)
254 end = artlines;
255 for (i = reveal_ctrl_l_lines + 1; i < end; i++)
256 if (artline[i].flags & C_CTRLL) {
258 return TRUE;
259 }
261 return FALSE;
262}
263
264
265/*
266 * Re-hide revealed part of article after last ^L
267 * that is currently displayed.
268 * Returns:
269 * FALSE no ^L found, no changes
270 * TRUE ^L found and displayed page must be updated
271 * (draw_page must be called)
272 */
273static t_bool
275 void)
276{
277 int i;
278
279 if (reveal_ctrl_l)
280 return FALSE;
281 for (i = reveal_ctrl_l_lines; i >= curr_line; i--)
282 if (artline[i].flags & C_CTRLL) {
283 reveal_ctrl_l_lines = i - 1;
284 return TRUE;
285 }
287 return FALSE;
288}
289
290
291/*
292 * The main routine for viewing articles
293 * Returns:
294 * >=0 normal exit - return a new base[] note
295 * <0 indicates some unusual condition. See GRP_* in tin.h
296 * GRP_QUIT User is doing a 'Q'
297 * GRP_RETSELECT Back to selection level due to 'T' command
298 * GRP_ARTUNAVAIL We didn't make it into the art
299 * don't bother fixing the screen up
300 * GRP_ARTABORT User 'q'uit load of article
301 * GRP_GOTOTHREAD To thread menu due to 'l' command
302 * GRP_NEXT Catchup with 'c'
303 * GRP_NEXTUNREAD " " 'C'
304 */
305int
307 struct t_group *group,
308 int start_respnum, /* index into arts[] */
309 int *threadnum) /* to allow movement in thread mode */
310{
311 char buf[LEN];
312 char key[MAXKEYLEN];
313 int i, j, n = 0;
314 int art_type = GROUP_TYPE_NEWS;
315 int hide_uue_tmp;
316 t_artnum old_artnum;
317 t_bool mouse_click_on = TRUE;
318 t_bool repeat_search;
320
321 if (group->attribute->mailing_list != NULL)
322 art_type = GROUP_TYPE_MAIL;
323
324 /*
325 * Peek to see if the pager started due to a body search
326 * Stop load_article() changing context again
327 */
328 if (srch_lineno != -1)
329 this_resp = start_respnum;
330
331 if ((i = load_article(start_respnum, group)) < 0)
332 return i;
333
334 if (srch_lineno != -1)
335 process_search(&curr_line, (size_t) artlines, (size_t) ARTLINES, PAGE_LEVEL);
336
337 forever {
340 repeat_search = TRUE;
341 } else
342 repeat_search = FALSE;
343
344 switch (func) {
345 case GLOBAL_ABORT: /* Abort */
346 break;
347
348 case DIGIT_1:
349 case DIGIT_2:
350 case DIGIT_3:
351 case DIGIT_4:
352 case DIGIT_5:
353 case DIGIT_6:
354 case DIGIT_7:
355 case DIGIT_8:
356 case DIGIT_9:
359 else {
360 if ((n = prompt_response(func_to_key(func, page_keys), this_resp)) != -1) {
361 XFACE_CLEAR();
362 if ((i = load_article(n, group)) < 0)
363 return i;
364 }
365 }
366 break;
367
368#ifndef NO_SHELL_ESCAPE
370 XFACE_CLEAR();
371 shell_escape();
372 draw_page(group->name, 0);
373 break;
374#endif /* !NO_SHELL_ESCAPE */
375
377 if (mouse_click_on)
379 else
381 mouse_click_on = bool_not(mouse_click_on);
382 break;
383
384 case GLOBAL_PAGE_UP:
386 draw_page(group->name, 0);
387 else {
388 if (curr_line == 0)
390 else {
391 curr_line -= ((tinrc.scroll_lines == -2) ? ARTLINES / 2 : ARTLINES);
392 draw_page(group->name, 0);
393 }
394 }
395 break;
396
397 case GLOBAL_PAGE_DOWN: /* page down or next response */
398 case PAGE_NEXT_UNREAD:
400 draw_page(group->name, 0);
401 else {
402 if (curr_line + ARTLINES >= artlines) { /* End is already on screen */
403 switch (func) {
404 case PAGE_NEXT_UNREAD: /* <TAB> */
405 goto page_goto_next_unread;
406
407 case GLOBAL_PAGE_DOWN:
409 goto page_goto_next_unread;
410 break;
411
412 default: /* to keep gcc quiet */
413 break;
414 }
416 } else {
418 goto page_goto_next_unread;
419
420 curr_line += ((tinrc.scroll_lines == -2) ? ARTLINES / 2 : ARTLINES);
421
422 if (tinrc.scroll_lines == -1) /* formerly show_last_line_prev_page */
423 curr_line--;
424 draw_page(group->name, 0);
425 }
426 }
427 break;
428
429page_goto_next_unread:
430 XFACE_CLEAR();
431 if ((n = next_unread(next_response(this_resp))) == -1)
432 return (which_thread(this_resp));
433 if ((i = load_article(n, group)) < 0)
434 return i;
435 break;
436
437 case GLOBAL_FIRST_PAGE: /* beginning of article */
438 if (reveal_ctrl_l_lines > -1 || curr_line != 0) {
440 curr_line = 0;
441 draw_page(group->name, 0);
442 }
443 break;
444
445 case GLOBAL_LAST_PAGE: /* end of article */
448 /* Display a full last page for neatness */
450 draw_page(group->name, 0);
451 }
452 break;
453
454 case GLOBAL_LINE_UP:
456 draw_page(group->name, 0);
457 else {
458 if (curr_line == 0) {
460 break;
461 }
462
464 curr_line += i;
465 draw_page(group->name, i);
466 }
467 break;
468
469 case GLOBAL_LINE_DOWN:
471 draw_page(group->name, 0);
472 else {
473 if (curr_line + ARTLINES >= artlines) {
475 break;
476 }
477
479 curr_line += i;
480 draw_page(group->name, i);
481 }
482 break;
483
484 case GLOBAL_LAST_VIEWED: /* show last viewed article */
485 if (last_resp < 0 || (which_thread(last_resp) == -1)) {
487 break;
488 }
489 if ((i = load_article(last_resp, group)) < 0) {
490 XFACE_CLEAR();
491 return i;
492 }
493 break;
494
495 case GLOBAL_LOOKUP_MESSAGEID: /* Goto article by Message-ID */
496 if ((n = prompt_msgid()) != ART_UNAVAILABLE) {
497 if ((i = load_article(n, group)) < 0) {
498 XFACE_CLEAR();
499 return i;
500 }
501 }
502 break;
503
504 case PAGE_GOTO_PARENT: /* Goto parent of this article */
505 {
507
508 if (parent == NULL) {
510 break;
511 }
512
515 break;
516 }
517
520 break;
521 }
522
523 if ((i = load_article(parent->article, group)) < 0) {
524 XFACE_CLEAR();
525 return i;
526 }
527
528 break;
529 }
530
531 case GLOBAL_PIPE: /* pipe article/thread/tagged arts to command */
534 XFACE_SHOW();
535 break;
536
537 case PAGE_MAIL: /* mail article/thread/tagged articles to somebody */
540 XFACE_SHOW();
541 break;
542
543#ifndef DISABLE_PRINTING
544 case GLOBAL_PRINT: /* output art/thread/tagged arts to printer */
547 XFACE_SHOW();
548 break;
549#endif /* !DISABLE_PRINTING */
550
551 case PAGE_REPOST: /* repost current article */
552 if (can_post) {
555 XFACE_SHOW();
556 } else
558 break;
559
560 case PAGE_SAVE: /* save article/thread/tagged articles */
563 XFACE_SHOW();
564 break;
565
566 case PAGE_AUTOSAVE: /* Auto-save articles without prompting */
567 if (grpmenu.curr >= 0) {
570 XFACE_SHOW();
571 }
572 break;
573
576 break;
577
578 case GLOBAL_SEARCH_SUBJECT_FORWARD: /* search in article */
581 break;
582
585 draw_page(group->name, 0);
586 }
587 process_search(&curr_line, (size_t) artlines, (size_t) ARTLINES, PAGE_LEVEL);
588 break;
589
590 case GLOBAL_SEARCH_BODY: /* article body search */
591 if ((n = search_body(group, this_resp, repeat_search)) != -1) {
592 this_resp = n; /* Stop load_article() changing context again */
593 if ((i = load_article(n, group)) < 0) {
594 XFACE_CLEAR();
595 return i;
596 }
597 process_search(&curr_line, (size_t) artlines, (size_t) ARTLINES, PAGE_LEVEL);
598 }
599 break;
600
601 case PAGE_TOP_THREAD: /* first article in current thread */
602 if (arts[this_resp].prev >= 0) {
603 if ((n = which_thread(this_resp)) >= 0 && base[n] != this_resp) {
604 assert(n < grpmenu.max);
605 if ((i = load_article((int) base[n], group)) < 0) {
606 XFACE_CLEAR();
607 return i;
608 }
609 }
610 }
611 break;
612
613 case PAGE_BOTTOM_THREAD: /* last article in current thread */
614 for (i = this_resp; i >= 0; i = arts[i].thread)
615 n = i;
616
617 if (n != this_resp) {
618 if ((i = load_article(n, group)) < 0) {
619 XFACE_CLEAR();
620 return i;
621 }
622 }
623 break;
624
625 case PAGE_NEXT_THREAD: /* start of next thread */
626 XFACE_CLEAR();
627 if ((n = next_thread(this_resp)) == -1)
628 return (which_thread(this_resp));
629 if ((i = load_article(n, group)) < 0)
630 return i;
631 break;
632
633#ifdef HAVE_PGP_GPG
634 case PAGE_PGP_CHECK_ARTICLE:
636 if (pgp_check_article(&pgart))
637 draw_page(group->name, 0);
638 XFACE_SHOW();
639 break;
640#endif /* HAVE_PGP_GPG */
641
642 case PAGE_TOGGLE_HEADERS: /* toggle display of all headers */
643 XFACE_CLEAR();
645 resize_article(TRUE, &pgart); /* Also recooks it.. */
646 curr_line = 0;
647 draw_page(group->name, 0);
648 break;
649
650 case PAGE_TOGGLE_RAW: /* toggle display of whole 'raw' article */
651 XFACE_CLEAR();
652 toggle_raw(group);
653 break;
654
655 case PAGE_TOGGLE_TEX2ISO: /* toggle German TeX to ISO latin1 style conversion */
656 if (((group->attribute->tex2iso_conv) = !(group->attribute->tex2iso_conv)))
658 else
660
661 resize_article(TRUE, &pgart); /* Also recooks it.. */
662 draw_page(group->name, 0);
664 break;
665
666 case PAGE_TOGGLE_TABS: /* toggle tab stops 8 vs 4 */
667 tabwidth = (tabwidth == 8) ? 4 : 8;
668 resize_article(TRUE, &pgart); /* Also recooks it.. */
669 draw_page(group->name, 0);
671 break;
672
673 case PAGE_TOGGLE_UUE: /* toggle display of uuencoded sections */
674 hide_uue = (hide_uue + 1) % (UUE_ALL + 1);
675 resize_article(TRUE, &pgart); /* Also recooks it.. */
676 /*
677 * If we hid uue and are off the end of the article, reposition to
678 * show last page for neatness
679 */
682 draw_page(group->name, 0);
683 /* TODO: info_message()? */
684 break;
685
686 case PAGE_REVEAL: /* toggle hiding after ^L */
688 if (!reveal_ctrl_l) { /* switched back to active ^L's */
690 curr_line = 0;
691 } else
693 draw_page(group->name, 0);
694 /* TODO: info_message()? */
695 break;
696
697 case GLOBAL_QUICK_FILTER_SELECT: /* quickly auto-select article */
698 case GLOBAL_QUICK_FILTER_KILL: /* quickly kill article */
699 if (quick_filter(func, group, &arts[this_resp])) {
700 old_artnum = arts[this_resp].artnum;
701 unfilter_articles(group);
702 filter_articles(group);
703 make_threads(group, FALSE);
704 if ((n = find_artnum(old_artnum)) == -1 || which_thread(n) == -1) /* We have lost the thread */
705 return GRP_KILLED;
706 this_resp = n;
707 draw_page(group->name, 0);
709 }
710 break;
711
712 case GLOBAL_MENU_FILTER_SELECT: /* auto-select article menu */
713 case GLOBAL_MENU_FILTER_KILL: /* kill article menu */
714 XFACE_CLEAR();
715 if (filter_menu(func, group, &arts[this_resp])) {
716 old_artnum = arts[this_resp].artnum;
717 unfilter_articles(group);
718 filter_articles(group);
719 make_threads(group, FALSE);
720 if ((n = find_artnum(old_artnum)) == -1 || which_thread(n) == -1) /* We have lost the thread */
721 return GRP_KILLED;
722 this_resp = n;
723 }
724 draw_page(group->name, 0);
725 break;
726
728 XFACE_CLEAR();
730 old_artnum = arts[this_resp].artnum;
731 unfilter_articles(group);
733 filter_articles(group);
734 make_threads(group, FALSE);
735 if ((n = find_artnum(old_artnum)) == -1 || which_thread(n) == -1) /* We have lost the thread */
736 return GRP_KILLED;
737 this_resp = n;
738 }
739 draw_page(group->name, 0);
740 break;
741
742 case GLOBAL_REDRAW_SCREEN: /* redraw current page of article */
743 my_retouch();
744 draw_page(group->name, 0);
745 break;
746
747 case PAGE_TOGGLE_ROT13: /* toggle rot-13 mode */
748 rotate = rotate ? 0 : 13;
749 draw_page(group->name, 0);
751 break;
752
753 case GLOBAL_SEARCH_AUTHOR_FORWARD: /* author search forward */
754 case GLOBAL_SEARCH_AUTHOR_BACKWARD: /* author search backward */
755 if ((n = search(func, this_resp, repeat_search)) < 0)
756 break;
757 if ((i = load_article(n, group)) < 0) {
758 XFACE_CLEAR();
759 return i;
760 }
761 break;
762
763 case CATCHUP: /* catchup - mark read, goto next */
764 case CATCHUP_NEXT_UNREAD: /* goto next unread */
767 else
769 if ((!TINRC_CONFIRM_ACTION) || prompt_yn(buf, TRUE) == 1) {
771 XFACE_CLEAR();
773 }
774 break;
775
780 else
782 break;
783
784 case PAGE_CANCEL: /* cancel an article */
785 if (can_post || art_type != GROUP_TYPE_NEWS) {
787 if (cancel_article(group, &arts[this_resp], this_resp))
788 draw_page(group->name, 0);
789 XFACE_SHOW();
790 } else
792 break;
793
794 case PAGE_EDIT_ARTICLE: /* edit an article (mailgroup only) */
796 if (art_edit(group, &arts[this_resp]))
797 draw_page(group->name, 0);
798 XFACE_SHOW();
799 break;
800
801 case PAGE_FOLLOWUP_QUOTE: /* post a followup to this article */
803 case PAGE_FOLLOWUP:
804 if (!can_post && art_type == GROUP_TYPE_NEWS) {
806 break;
807 }
808 XFACE_CLEAR();
809 (void) post_response(group->name, this_resp,
812 draw_page(group->name, 0);
813 break;
814
815 case GLOBAL_HELP: /* help */
816 XFACE_CLEAR();
818 draw_page(group->name, 0);
819 break;
820
821 case GLOBAL_TOGGLE_HELP_DISPLAY: /* toggle mini help menu */
823 draw_page(group->name, 0);
824 break;
825
826 case GLOBAL_QUIT: /* return to index page */
827return_to_index:
828 XFACE_CLEAR();
830 if (threadnum)
831 *threadnum = which_response(this_resp);
832
833 return i;
834
835 case GLOBAL_TOGGLE_INVERSE_VIDEO: /* toggle inverse video */
837 draw_page(group->name, 0);
839 break;
840
841#ifdef HAVE_COLOR
842 case GLOBAL_TOGGLE_COLOR: /* toggle color */
843 if (toggle_color()) {
844 draw_page(group->name, 0);
845 show_color_status();
846 }
847 break;
848#endif /* HAVE_COLOR */
849
850 case PAGE_LIST_THREAD: /* -> thread page that this article is in */
851 XFACE_CLEAR();
853 return GRP_GOTOTHREAD;
854
855 case GLOBAL_OPTION_MENU: /* option menu */
856 XFACE_CLEAR();
857 old_artnum = arts[this_resp].artnum;
859 if ((this_resp = find_artnum(old_artnum)) == -1 || which_thread(this_resp) == -1) { /* We have lost the thread */
861 return GRP_EXIT;
862 }
864 draw_page(group->name, 0);
865 break;
866
867 case PAGE_NEXT_ARTICLE: /* skip to next article */
868 XFACE_CLEAR();
869 if ((n = next_response(this_resp)) == -1)
870 return (which_thread(this_resp));
871
872 if ((i = load_article(n, group)) < 0)
873 return i;
874 break;
875
876 case PAGE_MARK_THREAD_READ: /* mark rest of thread as read */
877 thd_mark_read(group, this_resp);
878 if ((n = next_unread(next_response(this_resp))) == -1)
879 goto return_to_index;
880 if ((i = load_article(n, group)) < 0) {
881 XFACE_CLEAR();
882 return i;
883 }
884 break;
885
886 case PAGE_NEXT_UNREAD_ARTICLE: /* next unread article */
887 goto page_goto_next_unread;
888
889 case PAGE_PREVIOUS_ARTICLE: /* previous article */
890 XFACE_CLEAR();
891 if ((n = prev_response(this_resp)) == -1)
892 return this_resp;
893
894 if ((i = load_article(n, group)) < 0)
895 return i;
896 break;
897
898 case PAGE_PREVIOUS_UNREAD_ARTICLE: /* previous unread article */
899 if ((n = prev_unread(prev_response(this_resp))) == -1)
901 else {
902 if ((i = load_article(n, group)) < 0) {
903 XFACE_CLEAR();
904 return i;
905 }
906 }
907 break;
908
909 case GLOBAL_QUIT_TIN: /* quit */
910 XFACE_CLEAR();
911 return GRP_QUIT;
912
913 case PAGE_REPLY_QUOTE: /* reply to author through mail */
915 case PAGE_REPLY:
916 XFACE_CLEAR();
918 draw_page(group->name, 0);
919 break;
920
921 case PAGE_TAG: /* tag/untag article for saving */
923 break;
924
925 case PAGE_GROUP_SELECT: /* return to group selection page */
926#if 0
927 /* Hasn't been used since tin 1.1 PL4 */
928 if (filter_state == FILTERING) {
929 filter_articles(group);
930 make_threads(group, FALSE);
931 }
932#endif /* 0 */
933 XFACE_CLEAR();
934 return GRP_RETSELECT;
935
936 case GLOBAL_VERSION:
938 break;
939
940 case GLOBAL_POST: /* post a basenote */
942 if (post_article(group->name))
943 draw_page(group->name, 0);
944 XFACE_SHOW();
945 break;
946
947 case GLOBAL_POSTPONED: /* post postponed article */
948 if (can_post || art_type != GROUP_TYPE_NEWS) {
951 draw_page(group->name, 0);
952 XFACE_SHOW();
953 } else
955 break;
956
957 case GLOBAL_DISPLAY_POST_HISTORY: /* display messages posted by user */
959 if (post_hist_page())
960 return GRP_EXIT;
961 else {
962 XFACE_SHOW();
963 }
964 break;
965
966 case MARK_ARTICLE_UNREAD: /* mark article as unread(to return) */
969 break;
970
971 case PAGE_SKIP_INCLUDED_TEXT: /* skip included text */
972 for (i = j = curr_line; i < artlines; i++) {
973 if (artline[i].flags & (C_QUOTE1 | C_QUOTE2 | C_QUOTE3)) {
974 j = i;
975 break;
976 }
977 }
978
979 for (; j < artlines; j++) {
980 if (!(artline[j].flags & (C_QUOTE1 | C_QUOTE2 | C_QUOTE3)))
981 break;
982 }
983
984 if (j != curr_line) {
985 curr_line = j;
986 draw_page(group->name, 0);
987 }
988 break;
989
990 case GLOBAL_TOGGLE_INFO_LAST_LINE: /* this is _not_ correct, we do not toggle status here */
991 info_message("%s", arts[this_resp].subject);
992 break;
993
996 draw_page(group->name, 0);
998 break;
999
1002 hide_uue_tmp = hide_uue;
1003 hide_uue = UUE_NO;
1006 hide_uue = hide_uue_tmp;
1008 draw_page(group->name, 0);
1009 XFACE_SHOW();
1010 break;
1011
1012 case PAGE_VIEW_URL:
1013 if (!show_raw_article) { /* cooked mode? */
1014 t_bool success;
1015
1017 resize_article(FALSE, &pgart); /* unbreak long lines */
1018 success = url_page();
1019 resize_article(TRUE, &pgart); /* rebreak long lines */
1020 draw_page(group->name, 0);
1021 if (!success)
1023 XFACE_SHOW();
1024 }
1025 break;
1026
1027 default:
1029 }
1030 }
1031 /* NOTREACHED */
1032 return GRP_ARTUNAVAIL;
1033}
1034
1035
1036static void
1038 FILE *file,
1039 t_lineinfo *messageline,
1040 size_t messagelines,
1041 size_t base_line,
1042 size_t begin,
1043 size_t end,
1044 int help_level)
1045{
1046 char *line;
1047 char *p;
1048 int bytes;
1049 size_t i = begin;
1050 t_lineinfo *curr;
1051
1052 for (; i < end; i++) {
1053 if (base_line + i >= messagelines) /* ran out of message */
1054 break;
1055
1056 curr = &messageline[base_line + i];
1057
1058 if (fseek(file, curr->offset, SEEK_SET) != 0)
1059 break;
1060
1061 if ((line = tin_fgets(file, FALSE)) == NULL)
1062 break; /* ran out of message */
1063
1064 if ((help_level == INFO_PAGER) && (strwidth(line) >= cCOLS - 1))
1065#if defined(MULTIBYTE_ABLE) && !defined(NO_LOCALE)
1066 {
1067 char *tmp, *f, *t;
1068
1069 f = tmp = strunc(line, cCOLS - 1);
1070 t = line;
1071 while (*f)
1072 *t++ = *f++;
1073 *t = '\0';
1074 free(tmp);
1075 }
1076#else
1077 line[cCOLS - 1] = '\0';
1078#endif /* MULTIBYTE_ABLE && !NO_LOCALE */
1079
1080 /*
1081 * use the offsets gained while doing line wrapping to
1082 * determine the correct position to truncate the line
1083 */
1084 if ((help_level != INFO_PAGER) && (base_line + i < messagelines - 1)) { /* not last line of message */
1085 bytes = (int) ((curr + 1)->offset - curr->offset);
1086 line[bytes] = '\0';
1087 }
1088
1089 /*
1090 * rotN encoding on body and sig data only
1091 */
1092 if ((rotate != 0) && ((curr->flags & (C_BODY | C_SIG)) || show_raw_article)) {
1093 for (p = line; *p; p++) {
1094 if (*p >= 'A' && *p <= 'Z')
1095 *p = (char) ((*p - 'A' + rotate) % 26 + 'A');
1096 else if (*p >= 'a' && *p <= 'z')
1097 *p = (char) ((*p - 'a' + rotate) % 26 + 'a');
1098 }
1099 }
1100
1101 strip_line(line);
1102
1103#ifndef USE_CURSES
1104 snprintf(screen[i + (size_t) scroll_region_top].col, (size_t) cCOLS, "%s" cCRLF, line);
1105#endif /* !USE_CURSES */
1106
1107 MoveCursor((int) (i + (size_t) scroll_region_top), 0);
1109
1110 /*
1111 * Highlight URL's and mail addresses
1112 */
1113 if (tinrc.url_highlight) {
1114 if (curr->flags & C_URL)
1115#ifdef HAVE_COLOR
1116 highlight_regexes((int) (i + (size_t) scroll_region_top), &url_regex, use_color ? tinrc.col_urls : -1);
1117#else
1118 highlight_regexes((int) (i + (size_t) scroll_region_top), &url_regex, -1);
1119#endif /* HAVE_COLOR */
1120
1121 if (curr->flags & C_MAIL)
1122#ifdef HAVE_COLOR
1123 highlight_regexes((int) (i + (size_t) scroll_region_top), &mail_regex, use_color ? tinrc.col_urls : -1);
1124#else
1125 highlight_regexes((int) (i + (size_t) scroll_region_top), &mail_regex, -1);
1126#endif /* HAVE_COLOR */
1127
1128 if (curr->flags & C_NEWS)
1129#ifdef HAVE_COLOR
1130 highlight_regexes((int) (i + (size_t) scroll_region_top), &news_regex, use_color ? tinrc.col_urls : -1);
1131#else
1132 highlight_regexes((int) (i + (size_t) scroll_region_top), &news_regex, -1);
1133#endif /* HAVE_COLOR */
1134 }
1135
1136 /*
1137 * Highlight /slashes/, *stars*, _underscores_ and -strokes-
1138 */
1139 if (word_highlight && (curr->flags & C_BODY) && !(curr->flags & C_CTRLL)) {
1140#ifdef HAVE_COLOR
1141 highlight_regexes((int) (i + (size_t) scroll_region_top), &slashes_regex, use_color ? tinrc.col_markslash : tinrc.mono_markslash);
1142 highlight_regexes((int) (i + (size_t) scroll_region_top), &stars_regex, use_color ? tinrc.col_markstar : tinrc.mono_markstar);
1143 highlight_regexes((int) (i + (size_t) scroll_region_top), &underscores_regex, use_color ? tinrc.col_markdash : tinrc.mono_markdash);
1144 highlight_regexes((int) (i + (size_t) scroll_region_top), &strokes_regex, use_color ? tinrc.col_markstroke : tinrc.mono_markstroke);
1145#else
1150#endif /* HAVE_COLOR */
1151 }
1152
1153 /* Blank the screen after a ^L (only occurs when showing cooked) */
1154 if (!reveal_ctrl_l && (curr->flags & C_CTRLL) && (int) (base_line + i) > reveal_ctrl_l_lines) {
1155 CleartoEOS();
1156 break;
1157 }
1158 }
1159
1160#ifdef HAVE_COLOR
1161 fcol(tinrc.col_text);
1162#endif /* HAVE_COLOR */
1163
1164 show_mini_help(help_level);
1165}
1166
1167
1168/*
1169 * Redraw the current page, curr_line will be the first line displayed
1170 * Everything that calls draw_page() just sets curr_line, this function must
1171 * ensure it is set to something sane
1172 * If part is !=0, then only draw the first (-ve) or last (+ve) few lines
1173 */
1174void
1176 const char *group,
1177 int part)
1178{
1179 int start, end; /* 1st, last line to draw */
1180
1182
1183 /*
1184 * Can't do partial draw if term can't scroll properly
1185 */
1186 if (part && !have_linescroll)
1187 part = 0;
1188
1189 /*
1190 * Ensure curr_line is in bounds
1191 */
1192 if (curr_line < 0)
1193 curr_line = 0; /* Oops - off the top */
1194 else {
1195 if (curr_line > artlines)
1196 curr_line = artlines; /* Oops - off the end */
1197 }
1198
1199 search_line = curr_line; /* Reset search to start from top of display */
1200
1202
1203 /* Down-scroll, only redraw bottom 'part' lines of screen */
1204 if ((start = (part > 0) ? ARTLINES - part : 0) < 0)
1205 start = 0;
1206
1207 /* Up-scroll, only redraw the top 'part' lines of screen */
1208 if ((end = (part < 0) ? -part : ARTLINES) > ARTLINES)
1209 end = ARTLINES;
1210
1211 /*
1212 * ncurses doesn't clear the scroll area when you scroll by more than the
1213 * window size - force full redraw
1214 */
1215 if ((end - start >= ARTLINES) || (part == 0)) {
1216 ClearScreen();
1217 draw_page_header(group);
1218 } else
1219 MoveCursor(0, 0);
1220
1221 print_message_page(note_fp, artline, (size_t) artlines, (size_t) curr_line, (size_t) start, (size_t) end, PAGE_LEVEL);
1222
1223 /*
1224 * Print an appropriate footer
1225 */
1226 if (curr_line + ARTLINES >= artlines) {
1227 char buf[LEN], *buf2;
1228 int len;
1229
1230 STRCPY(buf, (arts[this_resp].thread != -1) ? _(txt_next_resp) : _(txt_last_resp));
1231 buf2 = strunc(buf, cCOLS - 1);
1232 len = strwidth(buf2);
1233 clear_message();
1234 MoveCursor(cLINES, cCOLS - len - (1 + BLANK_PAGE_COLS));
1235#ifdef HAVE_COLOR
1236 fcol(tinrc.col_normal);
1237#endif /* HAVE_COLOR */
1238 StartInverse();
1239 my_fputs(buf2, stdout);
1240 EndInverse();
1241 my_flush();
1242 free(buf2);
1243 } else
1245
1246#ifdef XFACE_ABLE
1247 if (tinrc.use_slrnface && !show_raw_article)
1248 slrnface_display_xface(note_h->xface);
1249#endif /* XFACE_ABLE */
1250
1251 stow_cursor();
1252}
1253
1254
1255/*
1256 * Start external metamail program
1257 */
1258static void
1260 FILE *fp)
1261{
1262 char *ptr = tinrc.metamail_prog;
1263 char buf[LEN];
1264 long offset;
1265 FILE *mime_fp;
1266#ifdef DONT_HAVE_PIPING
1267 char mimefile[PATH_LEN];
1268 int fd_mime;
1269#endif /* DONT_HAVE_PIPING */
1270
1271 if ((*ptr == '\0') || (!strcmp(ptr, INTERNAL_CMD)) || (getenv("NOMETAMAIL") != NULL))
1272 return;
1273
1274 if ((offset = ftell(fp)) == -1) {
1276 return;
1277 }
1278
1279 EndWin();
1280 Raw(FALSE);
1281
1282#ifdef DONT_HAVE_PIPING
1283 if ((fd_mime = my_tmpfile(mimefile, sizeof(mimefile) - 1, homedir)) == -1) {
1285 return;
1286 }
1287 if ((mime_fp = fdopen(fd_mime, "w")))
1288#else
1289 if ((mime_fp = popen(ptr, "w")))
1290#endif /* DONT_HAVE_PIPING */
1291 {
1292 rewind(fp);
1293 while (fgets(buf, (int) sizeof(buf), fp) != NULL)
1294 fputs(buf, mime_fp);
1295
1296 fflush(mime_fp);
1297 /* This is needed if we are viewing the raw art */
1298 fseek(fp, offset, SEEK_SET); /* goto old position */
1299
1300#ifdef DONT_HAVE_PIPING
1301 snprintf(buf, sizeof(buf) - 1, "%s %s", tinrc.metamail_prog, mimefile);
1302 invoke_cmd(buf);
1303 fclose(mime_fp);
1304 unlink(mimefile);
1305#else
1306 pclose(mime_fp);
1307#endif /* DONT_HAVE_PIPING */
1308 } else
1310
1311#ifdef USE_CURSES
1312 Raw(TRUE);
1313 InitWin();
1314#endif /* USE_CURSES */
1316#ifndef USE_CURSES
1317 Raw(TRUE);
1318 InitWin();
1319#endif /* !USE_CURSES */
1320}
1321
1322
1323/*
1324 * PAGE_HEADER defines the size in lines of this header
1325 */
1326static void
1328 const char *group)
1329{
1330 char *buf, *tmp;
1331 int i;
1332 int whichresp, x_resp;
1333 int len, right_len, center_pos, cur_pos;
1334 size_t line_len;
1335#if defined(MULTIBYTE_ABLE) && !defined(NO_LOCALE)
1336 wchar_t *fmt_resp = NULL, *fmt_thread, *wtmp, *wtmp2, *wbuf;
1337#else
1338 char *tmp2;
1339#endif /* MULTIBYTE_ABLE && !NO_LOCALE */
1340
1341 whichresp = which_response(this_resp);
1342 if ((i = which_thread(this_resp)) >= 0)
1343 x_resp = num_of_responses(i);
1344 else
1345 x_resp = 0;
1346
1347 line_len = LEN + 1;
1348 buf = my_malloc(line_len);
1349
1350 if (!my_strftime(buf, line_len, curr_group->attribute->date_format, localtime(&arts[this_resp].date))) {
1351 strncpy(buf, BlankIfNull(note_h->date), line_len);
1352 buf[line_len - 1] = '\0';
1353 }
1354
1355#if defined(MULTIBYTE_ABLE) && !defined(NO_LOCALE)
1356
1357# ifdef HAVE_COLOR
1358 fcol(tinrc.col_head);
1359# endif /* HAVE_COLOR */
1360
1361 /*
1362 * first line
1363 */
1364 cur_pos = 0;
1365
1366 /* convert to wide-char format string */
1367 fmt_thread = char2wchar_t(_(txt_thread_x_of_n));
1368
1369 /*
1370 * Determine the needed space for the text at the right hand margin.
1371 * The formatting info (%4s) needs 3 positions but we need 4 positions
1372 * on the screen for each counter.
1373 */
1374 if (fmt_thread)
1375 right_len = wcswidth(fmt_thread, wcslen(fmt_thread)) - 6 + 8;
1376 else
1377 right_len = 0;
1378 FreeIfNeeded(fmt_thread);
1379
1380 /*
1381 * limit right_len to cCOLS / 3
1382 */
1383 if (right_len > cCOLS / 3 + 1)
1384 right_len = cCOLS / 3 + 1;
1385
1386 /* date */
1387 if ((wtmp = char2wchar_t(buf)) != NULL) {
1388 my_fputws(wtmp, stdout);
1389 cur_pos += wcswidth(wtmp, wcslen(wtmp));
1390 free(wtmp);
1391 }
1392
1393 /*
1394 * determine max len for centered group name
1395 * allow one space before and after group name
1396 */
1397 len = cCOLS - 2 * MAX(cur_pos, right_len) - 3;
1398
1399 /* group name */
1400 if ((wtmp = char2wchar_t(group)) != NULL) {
1401 /* wconvert_to_printable(wtmp, FALSE); */
1403 wtmp2 = abbr_wcsgroupname(wtmp, len);
1404 else
1405 wtmp2 = wstrunc(wtmp, len);
1406
1407 if ((i = wcswidth(wtmp2, wcslen(wtmp2))) < len)
1408 len = i;
1409
1410 center_pos = (cCOLS - len) / 2;
1411
1412 /* pad out to left */
1413 for (; cur_pos < center_pos; cur_pos++)
1414 my_fputc(' ', stdout);
1415
1416 my_fputws(wtmp2, stdout);
1417 cur_pos += wcswidth(wtmp2, wcslen(wtmp2));
1418 free(wtmp2);
1419 free(wtmp);
1420 }
1421
1422 /* pad out to right */
1423 for (; cur_pos < cCOLS - right_len - 1; cur_pos++)
1424 my_fputc(' ', stdout);
1425
1426 /* thread info */
1427 /* can't eval tin_ltoa() more than once in a statement due to statics */
1428 strcpy(buf, tin_ltoa(which_thread(this_resp) + 1, 4));
1429 tmp = strunc(_(txt_thread_x_of_n), cCOLS / 3 - 1);
1430 my_printf(tmp, buf, tin_ltoa(grpmenu.max, 4));
1431 free(tmp);
1432
1433 my_fputs(cCRLF, stdout);
1434
1435# if 0
1436 /* display a ruler for layout checking purposes */
1437 my_fputs("....|....3....|....2....|....1....|....0....|....1....|....2....|....3....|....\n", stdout);
1438# endif /* 0 */
1439
1440 /*
1441 * second line
1442 */
1443 cur_pos = 0;
1444
1445 /*
1446 * Convert to wide-char format string and determine the needed space
1447 * for the text at the right hand margin. The formatting info (%4s)
1448 * needs 3 positions but we need 4 positions on the screen for each
1449 * counter.
1450 */
1451
1452 right_len = 0;
1453 if (whichresp && (fmt_resp = char2wchar_t(_(txt_art_x_of_n)))) {
1454 right_len = wcswidth(fmt_resp, wcslen(fmt_resp)) - 6 + 8;
1455 } else {
1456 if ((!x_resp && (fmt_resp = char2wchar_t(_(txt_no_responses)))) || (x_resp == 1 && (fmt_resp = char2wchar_t(_(txt_1_resp)))))
1457 right_len = wcswidth(fmt_resp, wcslen(fmt_resp));
1458 else if ((fmt_resp = char2wchar_t(_(txt_x_resp))))
1459 right_len = wcswidth(fmt_resp, wcslen(fmt_resp)) - 3 + 4;
1460 }
1461 FreeIfNeeded(fmt_resp);
1462
1463 /*
1464 * limit right_len to cCOLS / 3
1465 */
1466 if (right_len > cCOLS / 3 + 1)
1467 right_len = cCOLS / 3 + 1;
1468
1469 /* line count */
1470 if (arts[this_resp].line_count < 0)
1471 strcpy(buf, "?");
1472 else
1473 snprintf(buf, line_len, "%-4d", arts[this_resp].line_count);
1474
1475 {
1476 wchar_t *fmt;
1477
1478 if ((wtmp = char2wchar_t(_(txt_lines))) != NULL) {
1479 int tex_space = pgart.tex2iso ? 5 : 0;
1480
1481 fmt = wstrunc(wtmp, cCOLS / 3 - 1 - tex_space);
1482 wtmp = my_realloc(wtmp, sizeof(wchar_t) * line_len);
1483 swprintf(wtmp, line_len, fmt, buf);
1484 my_fputws(wtmp, stdout);
1485 cur_pos += wcswidth(wtmp, wcslen(wtmp));
1486 free(fmt);
1487 free(wtmp);
1488 }
1489 }
1490
1491# ifdef HAVE_COLOR
1492 fcol(tinrc.col_subject);
1493# endif /* HAVE_COLOR */
1494
1495 /* tex2iso */
1496 if (pgart.tex2iso) {
1497 if ((wtmp = char2wchar_t(_(txt_tex))) != NULL) {
1498 wtmp2 = wstrunc(wtmp, 5);
1499 my_fputws(wtmp2, stdout);
1500 cur_pos += wcswidth(wtmp2, wcslen(wtmp2));
1501 free(wtmp);
1502 free(wtmp2);
1503 }
1504 }
1505
1506 /* subject */
1507 /*
1508 * TODO: why do we fall back to arts[this_resp].subject if !note_h->subj?
1509 * if !note_h->subj then the article just has no subject, no matter
1510 * what the overview says.
1511 *
1512 * add BiDi handling
1513 */
1514 strncpy(buf, (note_h->subj ? note_h->subj : arts[this_resp].subject), line_len);
1515 buf[line_len - 1] = '\0';
1516 if ((wtmp = char2wchar_t(buf)) != NULL) {
1517 wbuf = wexpand_tab(wtmp, tabwidth);
1518 wtmp2 = wstrunc(wbuf, cCOLS - 2 * right_len - 3);
1519 center_pos = (cCOLS - wcswidth(wtmp2, wcslen(wtmp2))) / 2;
1520
1521 /* pad out to left */
1522 for (; cur_pos < center_pos; cur_pos++)
1523 my_fputc(' ', stdout);
1524
1525 StartInverse();
1526 my_fputws(wtmp2, stdout);
1527 EndInverse();
1528 cur_pos += wcswidth(wtmp2, wcslen(wtmp2));
1529 free(wtmp2);
1530 free(wtmp);
1531 free(wbuf);
1532 }
1533
1534# ifdef HAVE_COLOR
1535 fcol(tinrc.col_response);
1536# endif /* HAVE_COLOR */
1537
1538 /* pad out to right */
1539 for (; cur_pos < cCOLS - right_len - 1; cur_pos++)
1540 my_fputc(' ', stdout);
1541
1542 if (whichresp) {
1543 tmp = strunc(_(txt_art_x_of_n), cCOLS / 3 - 1);
1544 my_printf(tmp, whichresp + 1, x_resp + 1);
1545 free(tmp);
1546 } else {
1547 /* TODO: ngettext */
1548 if (!x_resp) {
1549 tmp = strunc(_(txt_no_responses), cCOLS / 3 - 1);
1550 my_printf("%s", tmp);
1551 } else if (x_resp == 1) {
1552 tmp = strunc(_(txt_1_resp), cCOLS / 3 - 1);
1553 my_printf("%s", tmp);
1554 } else {
1555 tmp = strunc(_(txt_x_resp), cCOLS / 3 - 1);
1556 my_printf(tmp, x_resp);
1557 }
1558 free(tmp);
1559 }
1560 my_fputs(cCRLF, stdout);
1561
1562 /*
1563 * third line
1564 */
1565 cur_pos = 0;
1566
1567# ifdef HAVE_COLOR
1568 fcol(tinrc.col_from);
1569# endif /* HAVE_COLOR */
1570 /* from */
1571 /*
1572 * TODO: don't use arts[this_resp].name/arts[this_resp].from
1573 * split up note_h->from and use that instead as it might
1574 * be different _if_ the overviews are broken
1575 *
1576 * add BiDi handling
1577 */
1578 {
1579 char *p = idna_decode(arts[this_resp].from);
1580
1581 if (arts[this_resp].name)
1582 snprintf(buf, line_len, "%s <%s>", arts[this_resp].name, p);
1583 else {
1584 strncpy(buf, p, line_len);
1585 buf[line_len - 1] = '\0';
1586 }
1587 free(p);
1588 }
1589
1590 if ((wtmp = char2wchar_t(buf)) != NULL) {
1591 wtmp2 = wstrunc(wtmp, cCOLS - 1);
1592 my_fputws(wtmp2, stdout);
1593 cur_pos += wcswidth(wtmp2, wcslen(wtmp2));
1594 free(wtmp2);
1595 free(wtmp);
1596 }
1597
1598 /*
1599 * Organization
1600 *
1601 * TODO: IDNA decoding, see also comment in
1602 * cook.c:cook_article()
1603 * add BiDi handling
1604 */
1605 if (note_h->org) {
1606 snprintf(buf, line_len, _(txt_at_s), note_h->org);
1607
1608 if ((wtmp = char2wchar_t(buf)) != NULL) {
1609 wbuf = wexpand_tab(wtmp, tabwidth);
1610 wtmp2 = wstrunc(wbuf, cCOLS - cur_pos - 1);
1611
1612 i = cCOLS - wcswidth(wtmp2, wcslen(wtmp2)) - 1;
1613 for (; cur_pos < i; cur_pos++)
1614 my_fputc(' ', stdout);
1615
1616 my_fputws(wtmp2, stdout);
1617 free(wtmp2);
1618 free(wtmp);
1619 free(wbuf);
1620 }
1621 }
1622
1623 my_fputs(cCRLF, stdout);
1624 my_fputs(cCRLF, stdout);
1625
1626#else /* !MULTIBYTE_ABLE || NO_LOCALE */
1627
1628# ifdef HAVE_COLOR
1629 fcol(tinrc.col_head);
1630# endif /* HAVE_COLOR */
1631
1632 /*
1633 * first line
1634 */
1635 cur_pos = 0;
1636
1637 /*
1638 * determine the needed space for the text at the right hand margin
1639 * the formatting info (%4s) needs 3 positions but we need 4 positions
1640 * on the screen for each counter
1641 */
1642 right_len = strlen(_(txt_thread_x_of_n)) - 6 + 8;
1643
1644 /* date */
1645 my_fputs(buf, stdout);
1646 cur_pos += strlen(buf);
1647
1648 /*
1649 * determine max len for centered group name
1650 * allow one space before and after group name
1651 */
1652 len = cCOLS - 2 * MAX(cur_pos, right_len) - 3;
1653
1654 /* group name */
1656 tmp = abbr_groupname(group, len);
1657 else
1658 tmp = strunc(group, len);
1659
1660 if ((i = strlen(tmp)) < len)
1661 len = i;
1662
1663 center_pos = (cCOLS - len) / 2;
1664
1665 /* pad out to left */
1666 for (; cur_pos < center_pos; cur_pos++)
1667 my_fputc(' ', stdout);
1668
1669 my_fputs(tmp, stdout);
1670 cur_pos += strlen(tmp);
1671 free(tmp);
1672
1673 /* pad out to right */
1674 for (; cur_pos < cCOLS - right_len - 1; cur_pos++)
1675 my_fputc(' ', stdout);
1676
1677 /* thread info */
1678 /* can't eval tin_ltoa() more than once in a statement due to statics */
1679 strcpy(buf, tin_ltoa(which_thread(this_resp) + 1, 4));
1681
1682 my_fputs(cCRLF, stdout);
1683
1684# if 0
1685 /* display a ruler for layout checking purposes */
1686 my_fputs("....|....3....|....2....|....1....|....0....|....1....|....2....|....3....|....\n", stdout);
1687# endif /* 0 */
1688
1689 /*
1690 * second line
1691 */
1692 cur_pos = 0;
1693
1694 /*
1695 * determine the needed space for the text at the right hand margin
1696 * the formatting info (%4s) needs 3 positions but we need 4 positions
1697 * on the screen for each counter
1698 */
1699 if (whichresp) {
1700 right_len = strlen(_(txt_art_x_of_n)) - 6 + 8;
1701 } else {
1702 if (!x_resp)
1703 right_len = strlen(_(txt_no_responses));
1704 else if (x_resp == 1)
1705 right_len = strlen(_(txt_1_resp));
1706 else
1707 right_len = strlen(_(txt_x_resp)) - 3 + 4;
1708 }
1709
1710 /* line count */
1711 /* an accurate line count will appear in the footer anymay */
1712 if (arts[this_resp].line_count < 0)
1713 strcpy(buf, "?");
1714 else
1715 snprintf(buf, line_len, "%-4d", arts[this_resp].line_count);
1716
1717 tmp = my_malloc(line_len);
1718 snprintf(tmp, line_len, _(txt_lines), buf);
1719 my_fputs(tmp, stdout);
1720 cur_pos += strlen(tmp);
1721 free(tmp);
1722
1723# ifdef HAVE_COLOR
1724 fcol(tinrc.col_subject);
1725# endif /* HAVE_COLOR */
1726
1727 /* tex2iso */
1728 if (pgart.tex2iso) {
1729 my_fputs(_(txt_tex), stdout);
1730 cur_pos += strlen(_(txt_tex));
1731 }
1732
1733 /* subject */
1734 /*
1735 * TODO: why do we fall back to arts[this_resp].subject if !note_h->subj?
1736 * if !note_h->subj then the article just has no subject, no matter
1737 * what the overview says.
1738 */
1739 strncpy(buf, (note_h->subj ? note_h->subj : arts[this_resp].subject), line_len);
1740 buf[line_len - 1] = '\0';
1741
1742 tmp2 = expand_tab(buf, tabwidth);
1743 tmp = strunc(tmp2, cCOLS - 2 * right_len - 3);
1744
1745 center_pos = (cCOLS - strlen(tmp)) / 2;
1746
1747 /* pad out to left */
1748 for (; cur_pos < center_pos; cur_pos++)
1749 my_fputc(' ', stdout);
1750
1751 StartInverse();
1752 my_fputs(tmp, stdout);
1753 EndInverse();
1754 cur_pos += strlen(tmp);
1755 free(tmp);
1756 free(tmp2);
1757
1758# ifdef HAVE_COLOR
1759 fcol(tinrc.col_response);
1760# endif /* HAVE_COLOR */
1761
1762 /* pad out to right */
1763 for (; cur_pos < cCOLS - right_len - 1; cur_pos++)
1764 my_fputc(' ', stdout);
1765
1766 if (whichresp)
1767 my_printf(_(txt_art_x_of_n), whichresp + 1, x_resp + 1);
1768 else {
1769 /* TODO: ngettext */
1770 if (!x_resp)
1772 else if (x_resp == 1)
1773 my_printf("%s", _(txt_1_resp));
1774 else
1775 my_printf(_(txt_x_resp), x_resp);
1776 }
1777 my_fputs(cCRLF, stdout);
1778
1779 /*
1780 * third line
1781 */
1782 cur_pos = 0;
1783
1784# ifdef HAVE_COLOR
1785 fcol(tinrc.col_from);
1786# endif /* HAVE_COLOR */
1787 /* from */
1788 /*
1789 * TODO: don't use arts[this_resp].name/arts[this_resp].from
1790 * split up note_h->from and use that instead as it might
1791 * be different _if_ the overviews are broken
1792 */
1793 if (arts[this_resp].name)
1794 snprintf(buf, line_len, "%s <%s>", arts[this_resp].name, arts[this_resp].from);
1795 else {
1796 strncpy(buf, arts[this_resp].from, line_len);
1797 buf[line_len - 1] = '\0';
1798 }
1799
1800 tmp = strunc(buf, cCOLS - 1);
1801 my_fputs(tmp, stdout);
1802 cur_pos += strlen(tmp);
1803 free(tmp);
1804
1805 if (note_h->org && cCOLS - cur_pos - 1 >= (int) strlen(_(txt_at_s)) - 2 + 3) {
1806 /* we have enough space to print at least " at ..." */
1807 snprintf(buf, line_len, _(txt_at_s), note_h->org);
1808
1809 tmp2 = expand_tab(buf, tabwidth);
1810 tmp = strunc(tmp2, cCOLS - cur_pos - 1);
1811 len = cCOLS - (int) strlen(tmp) - 1;
1812 for (; cur_pos < len; cur_pos++)
1813 my_fputc(' ', stdout);
1814 my_fputs(tmp, stdout);
1815 free(tmp);
1816 free(tmp2);
1817 }
1818
1819 my_fputs(cCRLF, stdout);
1820 my_fputs(cCRLF, stdout);
1821#endif /* MULTIBYTE_ABLE && !NO_LOCALE */
1822 free(buf);
1823
1824#ifdef HAVE_COLOR
1825 fcol(tinrc.col_normal);
1826#endif /* HAVE_COLOR */
1827}
1828
1829
1830/*
1831 * Change the pager article context to arts[new_respnum]
1832 * Return GRP_ARTUNAVAIL if article could not be opened
1833 * or GRP_ARTABORT if load of article was interrupted
1834 * or 0 on success
1835 */
1836static int
1838 int new_respnum,
1839 struct t_group *group)
1840{
1841 static t_bool art_closed = FALSE;
1842
1843#ifdef DEBUG
1844 if (debug & DEBUG_MISC)
1845 fprintf(stderr, "load_art %s(new=%d, curr=%d)\n", (new_respnum == this_resp && !art_closed) ? "ALREADY OPEN!" : "", new_respnum, this_resp);
1846#endif /* DEBUG */
1847
1848 if (new_respnum != this_resp || art_closed) {
1849 int ret;
1850
1851 art_close(&pgart); /* close previously opened art in pager */
1852 ret = art_open(TRUE, &arts[new_respnum], group, &pgart, TRUE, _(txt_reading_article));
1853
1854 switch (ret) {
1855 case ART_UNAVAILABLE:
1856 art_mark(group, &arts[new_respnum], ART_READ);
1857 /* prevent retagging as unread in unfilter_articles() */
1858 if (arts[new_respnum].killed == ART_KILLED_UNREAD)
1859 arts[new_respnum].killed = ART_KILLED;
1860 art_closed = TRUE;
1862 return GRP_ARTUNAVAIL;
1863
1864 case ART_ABORT:
1865 art_close(&pgart);
1866 art_closed = TRUE;
1867 return GRP_ARTABORT; /* special retcode to stop redrawing screen */
1868
1869 default: /* Normal case */
1870#if 0 /* Very useful debugging tool */
1871 if (prompt_yn("Fake art unavailable? ", FALSE) == 1) {
1872 art_close(&pgart);
1873 art_mark(group, &arts[new_respnum], ART_READ);
1874 art_closed = TRUE;
1875 return GRP_ARTUNAVAIL;
1876 }
1877#endif /* 0 */
1878 if (art_closed)
1879 art_closed = FALSE;
1880 if (new_respnum != this_resp) {
1881 /*
1882 * Remember current & previous articles for '-' command
1883 */
1885 this_resp = new_respnum; /* Set new art globally */
1886 }
1887 break;
1888 }
1889 } else if (show_all_headers) {
1890 /*
1891 * article is already opened with show_all_headers ON
1892 * -> re-cook it
1893 */
1896 }
1897
1898 art_mark(group, &arts[this_resp], ART_READ);
1899
1900 /*
1901 * Change status if art was unread before killing to
1902 * prevent retagging as unread in unfilter_articles()
1903 */
1904 if (arts[this_resp].killed == ART_KILLED_UNREAD)
1906
1907 if (pgart.cooked == NULL) { /* harmony corruption */
1909 return GRP_ARTUNAVAIL;
1910 }
1911
1912 /*
1913 * Setup to start viewing cooked version
1914 */
1917 curr_line = 0;
1921 search_line = 0;
1922 /*
1923 * Reset offsets only if not invoked during 'body search' (srch_lineno != -1)
1924 * otherwise the found string will not be highlighted
1925 */
1926 if (srch_lineno == -1)
1928 rotate = 0; /* normal mode, not rot13 */
1930 reveal_ctrl_l_lines = -1; /* all ^L's active */
1932
1933 draw_page(group->name, 0);
1934
1935 /*
1936 * Automatically invoke attachment viewing if requested
1937 */
1938 if (!note_h->mime || IS_PLAINTEXT(note_h->ext)) /* Text only article */
1939 return 0;
1940
1941 if (*tinrc.metamail_prog == '\0' || getenv("NOMETAMAIL") != NULL) /* Viewer turned off */
1942 return 0;
1943
1944 if (group->attribute->ask_for_metamail) {
1945 if (prompt_yn(_(txt_use_mime), TRUE) != 1)
1946 return 0;
1947 }
1948
1950 if (strcmp(tinrc.metamail_prog, INTERNAL_CMD) == 0) /* Use internal viewer */
1952 else
1954 XFACE_SHOW();
1955 return 0;
1956}
1957
1958
1959static int
1961 int ch,
1962 int curr_respnum)
1963{
1964 int i, num;
1965
1966 clear_message();
1967
1968 if ((num = prompt_num(ch, _(txt_select_art))) < 0) {
1969 clear_message();
1970 return -1;
1971 }
1972
1973 if ((i = which_thread(curr_respnum)) >= 0)
1974 return find_response(i, num - 1);
1975 else
1976 return -1;
1977}
1978
1979
1980/*
1981 * Reposition within message as needed, highlight searched string
1982 * This is tied quite closely to the information stored by
1983 * get_search_vectors()
1984 */
1985static void
1987 int *lcurr_line,
1988 size_t message_lines,
1989 size_t screen_lines,
1990 int help_level)
1991{
1992 int i, start, end;
1993
1994 if ((i = get_search_vectors(&start, &end)) == -1)
1995 return;
1996
1997 /*
1998 * Is matching line off the current view?
1999 * Reposition within article if needed, try to get matched line
2000 * in the middle of the screen
2001 */
2002 if (i < *lcurr_line || i >= (int) ((size_t) *lcurr_line + screen_lines)) {
2003 *lcurr_line = (int) ((size_t) i - (screen_lines / 2));
2004 if (((size_t) *lcurr_line + screen_lines) > message_lines) /* off the end */
2005 *lcurr_line = (int) (message_lines - screen_lines);
2006 /* else pos. is just fine */
2007 }
2008
2009 switch (help_level) {
2010 case PAGE_LEVEL:
2012 break;
2013
2014 case INFO_PAGER:
2016 break;
2017
2018 default:
2019 break;
2020 }
2021 search_line = i; /* draw_page() resets this to 0 */
2022
2023 /*
2024 * Highlight found string
2025 */
2026 highlight_string(i - *lcurr_line + scroll_region_top, start, end - start);
2027}
2028
2029
2030/*
2031 * Implement ^H toggle between cooked and raw views of article
2032 */
2033void
2035 struct t_group *group)
2036{
2037 if (show_raw_article) {
2041 } else {
2042 static int j; /* Needed on successive invocations */
2043 int chunk = note_h->ext->line_count;
2044
2045 /*
2046 * We do this on the fly, since most of the time it won't be used
2047 */
2048 if (!pgart.rawl) { /* Already done this for this article? */
2049 char *line;
2050 char *p;
2051 long offset;
2052
2053 j = 0;
2054 rewind(pgart.raw);
2055 pgart.rawl = my_malloc(sizeof(t_lineinfo) * (size_t) chunk);
2056 offset = ftell(pgart.raw);
2057
2058 while ((line = tin_fgets(pgart.raw, FALSE)) != NULL) {
2059 int space;
2060#if defined(MULTIBYTE_ABLE) && !defined(NO_LOCALE)
2061 int num_bytes;
2062 wchar_t wc;
2063#endif /* MULTIBYTE_ABLE && !NO_LOCALE */
2064
2065 pgart.rawl[j].offset = offset;
2066 pgart.rawl[j].flags = 0;
2067 j++;
2068 if (j >= chunk) {
2069 chunk += 50;
2070 pgart.rawl = my_realloc(pgart.rawl, sizeof(t_lineinfo) * (size_t) chunk);
2071 }
2072
2073 p = line;
2074 while (*p) {
2075 space = cCOLS - 1;
2076
2077 while ((space > 0) && *p) {
2078#if defined(MULTIBYTE_ABLE) && !defined(NO_LOCALE)
2079 num_bytes = mbtowc(&wc, p, MB_CUR_MAX);
2080 if (num_bytes != -1 && iswprint((wint_t) wc)) {
2081 if ((space -= wcwidth(wc)) < 0)
2082 break;
2083 p += num_bytes;
2084 offset += num_bytes;
2085 }
2086#else
2087 if (my_isprint((unsigned char) *p)) {
2088 space--;
2089 p++;
2090 offset++;
2091 }
2092#endif /* MULTIBYTE_ABLE && !NO_LOCALE */
2093 else if (IS_LOCAL_CHARSET("Big5") && (unsigned char) *p >= 0xa1 && (unsigned char) *p <= 0xfe && *(p + 1)) {
2094 /*
2095 * Big5: ASCII chars are handled by the normal code
2096 * check only for 2-byte chars
2097 * TODO: should we also check if the second byte is
2098 * also valid?
2099 */
2100 p += 2;
2101 offset += 2;
2102 space--;
2103 } else {
2104 /*
2105 * the current character can't be displayed print it as
2106 * an octal value (needs 4 columns) see also
2107 * color.c:draw_pager_line()
2108 */
2109 if ((space -= 4) < 0)
2110 break;
2111 offset++;
2112 p++;
2113 }
2114 }
2115 /*
2116 * if we reached the end of the line we don't need to
2117 * remember anything
2118 */
2119 if (*p) {
2120 pgart.rawl[j].offset = offset;
2121 pgart.rawl[j].flags = 0;
2122 if (++j >= chunk) {
2123 chunk += 50;
2124 pgart.rawl = my_realloc(pgart.rawl, sizeof(t_lineinfo) * (size_t) chunk);
2125 }
2126 }
2127 }
2128
2129 /*
2130 * only use ftell's return value here because we didn't
2131 * take the \n into account above.
2132 */
2133 offset = ftell(pgart.raw);
2134 }
2135
2136 pgart.rawl = my_realloc(pgart.rawl, sizeof(t_lineinfo) * (size_t) j);
2137 }
2138 artline = pgart.rawl;
2139 artlines = j;
2140 note_fp = pgart.raw;
2141 }
2142 curr_line = 0;
2144 draw_page(group ? group->name : "", 0);
2145}
2146
2147
2148/*
2149 * Re-cook an article
2150 */
2151void
2153 t_bool wrap_lines,
2154 t_openartinfo *artinfo)
2155{
2156 free(artinfo->cookl);
2157 if (artinfo->cooked)
2158 fclose(artinfo->cooked);
2159
2160 if (!cook_article(wrap_lines, artinfo, hide_uue, show_all_headers))
2162
2167}
2168
2169
2170/*
2171 * Infopager: simply page files
2172 */
2173void
2175 FILE *info_fh,
2176 const char *title,
2177 t_bool wrap_at_ends) /* currently always TRUE */
2178{
2179 int offset;
2181
2182 search_line = 0;
2184 info_file = info_fh;
2185 info_title = title;
2186 curr_info_line = 0;
2187 preprocess_info_message(info_fh);
2188 if (!info_fh)
2189 return;
2192
2193 forever {
2195 case GLOBAL_ABORT: /* common arrow keys */
2196 break;
2197
2198 case GLOBAL_LINE_UP:
2199 if (num_info_lines <= NOTESLINES) {
2201 break;
2202 }
2203 if (curr_info_line == 0) {
2204 if (!wrap_at_ends) {
2206 break;
2207 }
2210 break;
2211 }
2215 break;
2216
2217 case GLOBAL_LINE_DOWN:
2218 if (num_info_lines <= NOTESLINES) {
2220 break;
2221 }
2223 if (!wrap_at_ends) {
2225 break;
2226 }
2227 curr_info_line = 0;
2229 break;
2230 }
2234 break;
2235
2236 case GLOBAL_PAGE_DOWN:
2237 if (num_info_lines <= NOTESLINES) {
2239 break;
2240 }
2241 if (curr_info_line + NOTESLINES >= num_info_lines) { /* End is already on screen */
2242 if (!wrap_at_ends) {
2244 break;
2245 }
2246 curr_info_line = 0;
2248 break;
2249 }
2252 break;
2253
2254 case GLOBAL_PAGE_UP:
2255 if (num_info_lines <= NOTESLINES) {
2257 break;
2258 }
2259 if (curr_info_line == 0) {
2260 if (!wrap_at_ends) {
2262 break;
2263 }
2266 break;
2267 }
2270 break;
2271
2272 case GLOBAL_FIRST_PAGE:
2273 if (curr_info_line) {
2274 curr_info_line = 0;
2276 }
2277 break;
2278
2279 case GLOBAL_LAST_PAGE:
2281 /* Display a full last page for neatness */
2284 }
2285 break;
2286
2290 break;
2291
2296 break;
2297
2299 break;
2300
2302 break;
2303
2304 case GLOBAL_QUIT: /* quit */
2305 ClearScreen();
2306 return;
2307
2308 default:
2309 break;
2310 }
2311 }
2312}
2313
2314
2315/*
2316 * Redraw the current page, curr_info_line will be the first line displayed
2317 * If part is !=0, then only draw the first (-ve) or last (+ve) few lines
2318 */
2319void
2321 int part)
2322{
2323 int start, end; /* 1st, last line to draw */
2324
2326
2327 /*
2328 * Can't do partial draw if term can't scroll properly
2329 */
2330 if (part && !have_linescroll)
2331 part = 0;
2332
2333 if (curr_info_line < 0)
2334 curr_info_line = 0;
2337
2339
2340 /* Down-scroll, only redraw bottom 'part' lines of screen */
2341 if ((start = (part > 0) ? NOTESLINES - part : 0) < 0)
2342 start = 0;
2343
2344 /* Up-scroll, only redraw the top 'part' lines of screen */
2345 if ((end = (part < 0) ? -part : NOTESLINES) > NOTESLINES)
2346 end = NOTESLINES;
2347
2348 /* Print title */
2349 if ((end - start >= NOTESLINES) || (part == 0)) {
2350 ClearScreen();
2352 }
2353
2354 print_message_page(info_file, infoline, (size_t) num_info_lines, (size_t) curr_info_line, (size_t) start, (size_t) end, INFO_PAGER);
2355
2356 /* print footer */
2358 stow_cursor();
2359}
2360
2361
2362static void
2364 FILE *info_fh)
2365{
2366 int chunk = 50;
2367
2369 if (!info_fh)
2370 return;
2371
2372 rewind(info_fh);
2373 infoline = my_malloc(sizeof(t_lineinfo) * (size_t) chunk);
2374 num_info_lines = 0;
2375
2376 do {
2377 infoline[num_info_lines].offset = ftell(info_fh);
2380 if (num_info_lines >= chunk) {
2381 chunk += 50;
2382 infoline = my_realloc(infoline, sizeof(t_lineinfo) * (size_t) chunk);
2383 }
2384 } while (tin_fgets(info_fh, FALSE) != NULL);
2385
2387 infoline = my_realloc(infoline, sizeof(t_lineinfo) * (size_t) num_info_lines);
2388}
2389
2390
2391/*
2392 * URL menu
2393 */
2394static t_function
2396 void)
2397{
2398 return GLOBAL_QUIT;
2399}
2400
2401
2402static t_function
2404 void)
2405{
2406 return URL_SELECT;
2407}
2408
2409
2410static void
2412 void)
2413{
2414 int i;
2415
2417 currmenu = &urlmenu;
2418 mark_offset = 0;
2419
2420 if (urlmenu.curr < 0)
2421 urlmenu.curr = 0;
2422
2423 ClearScreen();
2426
2427 for (i = urlmenu.first; i < urlmenu.first + NOTESLINES && i < urlmenu.max; ++i)
2428 build_url_line(i);
2429
2431
2433}
2434
2435
2436static t_bool
2438 void)
2439{
2440 char key[MAXKEYLEN];
2442 t_menu *oldmenu = NULL;
2443
2444 if (currmenu)
2445 oldmenu = currmenu;
2446 urlmenu.curr = 0;
2448 if (urlmenu.max == 0)
2449 return FALSE;
2450
2452 show_url_page();
2454
2455 forever {
2456 switch ((func = handle_keypad(url_left, url_right, NULL, url_keys))) {
2457 case GLOBAL_QUIT:
2458 free_url_list();
2459 if (oldmenu)
2460 currmenu = oldmenu;
2461 return TRUE;
2462
2463 case DIGIT_1:
2464 case DIGIT_2:
2465 case DIGIT_3:
2466 case DIGIT_4:
2467 case DIGIT_5:
2468 case DIGIT_6:
2469 case DIGIT_7:
2470 case DIGIT_8:
2471 case DIGIT_9:
2472 if (urlmenu.max)
2474 break;
2475
2476#ifndef NO_SHELL_ESCAPE
2479 break;
2480#endif /* !NO_SHELL_ESCAPE */
2481
2482 case GLOBAL_HELP:
2484 show_url_page();
2485 break;
2486
2487 case GLOBAL_FIRST_PAGE:
2488 top_of_list();
2489 break;
2490
2491 case GLOBAL_LAST_PAGE:
2492 end_of_list();
2493 break;
2494
2496 my_retouch();
2497 show_url_page();
2498 break;
2499
2500 case GLOBAL_LINE_DOWN:
2501 move_down();
2502 break;
2503
2504 case GLOBAL_LINE_UP:
2505 move_up();
2506 break;
2507
2508 case GLOBAL_PAGE_DOWN:
2509 page_down();
2510 break;
2511
2512 case GLOBAL_PAGE_UP:
2513 page_up();
2514 break;
2515
2516 case GLOBAL_SCROLL_DOWN:
2517 scroll_down();
2518 break;
2519
2520 case GLOBAL_SCROLL_UP:
2521 scroll_up();
2522 break;
2523
2526 show_url_page();
2527 break;
2528
2531 show_url_page();
2532 break;
2533
2534 case URL_SELECT:
2535 if (urlmenu.max) {
2537 show_url_page();
2538 else
2540 }
2541 break;
2542
2548 else if (urlmenu.max) {
2549 int new_pos, old_pos = urlmenu.curr;
2550
2552 if (new_pos != old_pos)
2553 move_to_item(new_pos);
2554 }
2555 break;
2556
2557 default:
2559 break;
2560 }
2561 }
2562}
2563
2564
2565static void
2567 void)
2568{
2571 t_url *lptr;
2572
2573 lptr = find_url(urlmenu.curr);
2574 info_message("%s", lptr->url);
2575 } else if (urlmenu.curr == urlmenu.max - 1)
2577}
2578
2579
2580t_url *
2582 int n)
2583{
2584 t_url *lptr;
2585
2586 lptr = url_list;
2587 while (n-- > 0 && lptr->next)
2588 lptr = lptr->next;
2589
2590 return lptr;
2591}
2592
2593
2594static void
2596 int i)
2597{
2598 char *sptr;
2599 int len = cCOLS - 9;
2600 t_url *lptr;
2601
2602#ifdef USE_CURSES
2603 /*
2604 * Allocate line buffer
2605 * make it the same size like in !USE_CURSES case to simplify some code
2606 */
2607 sptr = my_malloc(cCOLS + 2);
2608#else
2609 sptr = screen[INDEX2SNUM(i)].col;
2610#endif /* USE_CURSES */
2611
2612 lptr = find_url(i);
2613 snprintf(sptr, (size_t) cCOLS, " %s %-*.*s%s", tin_ltoa(i + 1, 4), len, len, lptr->url, cCRLF);
2614 WriteLine(INDEX2LNUM(i), sptr);
2615
2616#ifdef USE_CURSES
2617 free(sptr);
2618#endif /* USE_CURSES */
2619}
2620
2621
2622static t_bool
2624 int n)
2625{
2626 char *url, *url_esc;
2627 size_t len;
2628 t_url *lptr;
2629
2630 lptr = find_url(n);
2631 len = strlen(lptr->url) << 1; /* double size; room for editing URL */
2632 url = my_malloc(len + 1);
2633 if (prompt_default_string("URL:", url, (int) len, lptr->url, HIST_URL)) {
2634 if (!*url) { /* Don't try and open nothing */
2635 free(url);
2636 return FALSE;
2637 }
2638 wait_message(2, _(txt_url_open), url);
2639 url_esc = escape_shell_meta(url, no_quote);
2640 len = strlen(url_esc) + strlen(tinrc.url_handler) + 2;
2641 url = my_realloc(url, len);
2642 snprintf(url, len, "%s %s", tinrc.url_handler, url_esc);
2643 invoke_cmd(url);
2644 free(url);
2645 cursoroff();
2646 return TRUE;
2647 }
2648 free(url);
2649 return FALSE;
2650}
2651
2652
2653static int
2655 void)
2656{
2657 char *ptr;
2658 int i, count = 0;
2659 int offsets[6];
2660 int offsets_size = ARRAY_SIZE(offsets);
2661 t_url *lptr = NULL;
2662
2663 for (i = 0; i < artlines; ++i) {
2664 if (!(artline[i].flags & (C_URL | C_NEWS | C_MAIL)))
2665 continue;
2666
2667 /*
2668 * Line contains a URL, so read it in
2669 */
2670 if (fseek(pgart.cooked, artline[i].offset, SEEK_SET) == -1) /* skip on error */
2671 continue;
2672 if ((ptr = tin_fgets(pgart.cooked, FALSE)) == NULL)
2673 continue;
2674
2675 /*
2676 * Step through, finding URL's
2677 */
2678 forever {
2679 /* any matches left? */
2680 if (pcre_exec(url_regex.re, url_regex.extra, ptr, (int) strlen(ptr), 0, 0, offsets, offsets_size) == PCRE_ERROR_NOMATCH)
2681 if (pcre_exec(mail_regex.re, mail_regex.extra, ptr, (int) strlen(ptr), 0, 0, offsets, offsets_size) == PCRE_ERROR_NOMATCH)
2682 if (pcre_exec(news_regex.re, news_regex.extra, ptr, (int) strlen(ptr), 0, 0, offsets, offsets_size) == PCRE_ERROR_NOMATCH)
2683 break;
2684
2685 *(ptr + offsets[1]) = '\0';
2686
2687 if (!lptr)
2688 lptr = url_list = my_malloc(sizeof(t_url));
2689 else {
2690 lptr->next = my_malloc(sizeof(t_url));
2691 lptr = lptr->next;
2692 }
2693 lptr->url = my_strdup(ptr + offsets[0]);
2694 lptr->next = NULL;
2695 ++count;
2696
2697 ptr += offsets[1] + 1;
2698 }
2699 }
2700 return count;
2701}
2702
2703
2704static void
2706 void)
2707{
2708 t_url *p, *q;
2709
2710 for (p = url_list; p != NULL; p = q) {
2711 q = p->next;
2712 free(p->url);
2713 free(p);
2714 }
2715 url_list = NULL;
2716}
2717
2718
2719static void
2721 long cur_num,
2722 long max_num)
2723{
2724 char buf[32]; /* FIXME: may get truncated with long localized _(txt_more) ... */
2725 int len;
2726
2727 if (NOTESLINES <= 0)
2728 return;
2729
2730 if (cur_num <= 0 && max_num <= 0)
2731 return;
2732
2733 clear_message();
2734 snprintf(buf, sizeof(buf), "%s(%d%%) [%ld/%ld]", _(txt_more), (int) (cur_num * 100 / max_num), cur_num, max_num);
2735 len = strwidth(buf);
2736 MoveCursor(cLINES, cCOLS - len - (1 + BLANK_PAGE_COLS));
2737#ifdef HAVE_COLOR
2738 fcol(tinrc.col_normal);
2739#endif /* HAVE_COLOR */
2740 StartInverse();
2741 my_fputs(buf, stdout);
2742 EndInverse();
2743 my_flush();
2744}
unsigned t_bool
Definition: bool.h:77
#define bool_not(b)
Definition: bool.h:81
#define TRUE
Definition: bool.h:74
#define FALSE
Definition: bool.h:70
#define DEBUG_MISC
Definition: debug.h:54
constext txt_thread_upper[]
Definition: lang.c:890
constext txt_tex[]
Definition: lang.c:872
@ HIST_URL
Definition: extern.h:1576
constext txt_article_upper[]
Definition: lang.c:76
constext txt_url_done[]
Definition: lang.c:927
t_function last_search
Definition: init.c:117
constext txt_bad_command[]
Definition: lang.c:112
constext txt_url_menu[]
Definition: lang.c:923
t_bool have_linescroll
Definition: curses.c:57
constext txt_cannot_post[]
Definition: lang.c:130
constext txt_thread_x_of_n[]
Definition: lang.c:898
constext txt_art_pager_com[]
Definition: lang.c:62
int NOTESLINES
Definition: signal.c:111
constext txt_mark_art_read[]
Definition: lang.c:634
constext * txt_onoff[]
Definition: lang.c:1270
constext txt_begin_of_art[]
Definition: lang.c:117
char homedir[PATH_LEN]
Definition: init.c:78
constext txt_mark_thread_read[]
Definition: lang.c:636
t_menu grpmenu
Definition: group.c:83
constext txt_art_unavailable[]
Definition: lang.c:69
struct t_article * arts
Definition: memory.c:69
constext txt_lines[]
Definition: lang.c:608
constext txt_toggled_rot13[]
Definition: lang.c:901
constext txt_end_of_urls[]
Definition: lang.c:173
constext txt_art_x_of_n[]
Definition: lang.c:71
constext txt_no_last_message[]
Definition: lang.c:686
constext txt_select_art[]
Definition: lang.c:849
constext txt_x_resp[]
Definition: lang.c:999
constext txt_use_mime[]
Definition: lang.c:928
constext txt_1_resp[]
Definition: lang.c:45
int srch_lineno
Definition: search.c:59
t_bool word_highlight
Definition: init.c:155
constext txt_url_menu_com[]
Definition: lang.c:924
constext txt_info_add_kill[]
Definition: lang.c:547
constext txt_art_parent_killed[]
Definition: lang.c:64
int filter_file_offset
Definition: filter.c:93
char filter_file[PATH_LEN]
Definition: init.c:89
constext txt_at_s[]
Definition: lang.c:78
constext txt_end_of_art[]
Definition: lang.c:165
struct regex_cache stars_regex
char cvers[LEN]
Definition: init.c:70
int signal_context
Definition: signal.c:105
constext txt_reading_article[]
Definition: lang.c:772
struct regex_cache url_regex
int mark_offset
Definition: screen.c:48
constext txt_art_parent_none[]
Definition: lang.c:63
constext txt_no_responses[]
Definition: lang.c:696
constext txt_toggled_high[]
Definition: lang.c:900
t_menu * currmenu
Definition: init.c:166
t_artnum * base
Definition: memory.c:65
constext txt_cook_article_failed_exiting[]
Definition: lang.c:156
constext txt_command_failed[]
Definition: lang.c:150
struct regex_cache slashes_regex
constext txt_no_prev_search[]
Definition: lang.c:694
struct regex_cache news_regex
constext txt_marked_as_unread[]
Definition: lang.c:629
int cCOLS
Definition: curses.c:53
struct regex_cache mail_regex
int cLINES
Definition: curses.c:52
t_bool can_post
Definition: nntplib.c:32
char * tin_progname
Definition: init.c:105
int xmouse
Definition: init.c:123
constext txt_url_open[]
Definition: lang.c:925
constext txt_next_resp[]
Definition: lang.c:676
constext txt_no_prev_unread_art[]
Definition: lang.c:695
constext txt_url_select[]
Definition: lang.c:926
constext txt_last_resp[]
Definition: lang.c:607
constext txt_end_of_page[]
Definition: lang.c:169
constext txt_enter_next_thread[]
Definition: lang.c:176
constext txt_info_add_select[]
Definition: lang.c:548
struct regex_cache strokes_regex
Definition: init.c:185
constext txt_begin_of_page[]
Definition: lang.c:118
struct t_group * curr_group
Definition: group.c:55
struct t_config tinrc
Definition: init.c:192
unsigned short debug
Definition: debug.c:51
constext txt_toggled_tabwidth[]
Definition: lang.c:903
struct t_screen * screen
Definition: screen.c:51
constext txt_enter_next_unread_art[]
Definition: lang.c:177
constext txt_more[]
Definition: lang.c:662
constext txt_toggled_tex2iso[]
Definition: lang.c:902
constext txt_art_parent_unavail[]
Definition: lang.c:65
struct regex_cache underscores_regex
#define MAXKEYLEN
Definition: keymap.h:136
t_function handle_keypad(t_function(*left_action)(void), t_function(*right_action)(void), t_function(*mouse_action)(t_function(*left_action)(void), t_function(*right_action)(void)), const struct keylist keys)
Definition: global.c:355
struct keylist info_keys
Definition: keymap.c:68
struct keylist page_keys
Definition: keymap.c:70
@ GLOBAL_SCROLL_UP
Definition: keymap.h:214
@ PAGE_TAG
Definition: keymap.h:293
@ PAGE_LIST_THREAD
Definition: keymap.h:274
@ DIGIT_7
Definition: keymap.h:157
@ GLOBAL_SHELL_ESCAPE
Definition: keymap.h:223
@ DIGIT_3
Definition: keymap.h:153
@ GLOBAL_PAGE_UP
Definition: keymap.h:201
@ PAGE_TOGGLE_HEADERS
Definition: keymap.h:294
@ PAGE_TOGGLE_TABS
Definition: keymap.h:298
@ DIGIT_6
Definition: keymap.h:156
@ PAGE_TOGGLE_TEX2ISO
Definition: keymap.h:299
@ PAGE_NEXT_ARTICLE
Definition: keymap.h:277
@ GLOBAL_PIPE
Definition: keymap.h:202
@ PAGE_EDIT_ARTICLE
Definition: keymap.h:268
@ GLOBAL_POST
Definition: keymap.h:203
@ GLOBAL_SEARCH_SUBJECT_FORWARD
Definition: keymap.h:220
@ PAGE_NEXT_THREAD
Definition: keymap.h:278
@ GLOBAL_LINE_DOWN
Definition: keymap.h:194
@ PAGE_TOGGLE_UUE
Definition: keymap.h:300
@ GLOBAL_SCROLL_DOWN
Definition: keymap.h:213
@ PAGE_FOLLOWUP
Definition: keymap.h:269
@ GLOBAL_HELP
Definition: keymap.h:191
@ GLOBAL_LOOKUP_MESSAGEID
Definition: keymap.h:196
@ GLOBAL_QUICK_FILTER_SELECT
Definition: keymap.h:209
@ PAGE_TOGGLE_ROT13
Definition: keymap.h:297
@ PAGE_VIEW_URL
Definition: keymap.h:303
@ DIGIT_2
Definition: keymap.h:152
@ PAGE_REPLY_QUOTE_HEADERS
Definition: keymap.h:289
@ PAGE_MAIL
Definition: keymap.h:275
@ PAGE_TOGGLE_RAW
Definition: keymap.h:296
@ GLOBAL_PRINT
Definition: keymap.h:206
@ GLOBAL_SEARCH_SUBJECT_BACKWARD
Definition: keymap.h:219
@ PAGE_AUTOSAVE
Definition: keymap.h:265
@ PAGE_TOP_THREAD
Definition: keymap.h:301
@ GLOBAL_TOGGLE_INVERSE_VIDEO
Definition: keymap.h:230
@ GLOBAL_TOGGLE_HELP_DISPLAY
Definition: keymap.h:228
@ GLOBAL_VERSION
Definition: keymap.h:231
@ PAGE_GROUP_SELECT
Definition: keymap.h:273
@ DIGIT_9
Definition: keymap.h:159
@ PAGE_SAVE
Definition: keymap.h:291
@ PAGE_PREVIOUS_ARTICLE
Definition: keymap.h:284
@ NOT_ASSIGNED
Definition: keymap.h:149
@ GLOBAL_POSTPONED
Definition: keymap.h:204
@ PAGE_BOTTOM_THREAD
Definition: keymap.h:266
@ GLOBAL_SEARCH_AUTHOR_BACKWARD
Definition: keymap.h:217
@ PAGE_REVEAL
Definition: keymap.h:286
@ GLOBAL_PAGE_DOWN
Definition: keymap.h:200
@ PAGE_NEXT_UNREAD_ARTICLE
Definition: keymap.h:280
@ PAGE_SKIP_INCLUDED_TEXT
Definition: keymap.h:292
@ PAGE_GOTO_PARENT
Definition: keymap.h:272
@ PAGE_NEXT_UNREAD
Definition: keymap.h:279
@ URL_SELECT
Definition: keymap.h:373
@ GLOBAL_EDIT_FILTER
Definition: keymap.h:189
@ GLOBAL_ABORT
Definition: keymap.h:186
@ PAGE_REPLY_QUOTE
Definition: keymap.h:288
@ GLOBAL_SEARCH_REPEAT
Definition: keymap.h:216
@ GLOBAL_SEARCH_AUTHOR_FORWARD
Definition: keymap.h:218
@ GLOBAL_QUIT
Definition: keymap.h:210
@ GLOBAL_FIRST_PAGE
Definition: keymap.h:190
@ GLOBAL_DISPLAY_POST_HISTORY
Definition: keymap.h:188
@ GLOBAL_REDRAW_SCREEN
Definition: keymap.h:212
@ DIGIT_8
Definition: keymap.h:158
@ GLOBAL_TOGGLE_INFO_LAST_LINE
Definition: keymap.h:229
@ PAGE_VIEW_ATTACHMENTS
Definition: keymap.h:302
@ PAGE_PREVIOUS_UNREAD_ARTICLE
Definition: keymap.h:285
@ GLOBAL_LAST_PAGE
Definition: keymap.h:192
@ DIGIT_1
Definition: keymap.h:151
@ PAGE_REPLY
Definition: keymap.h:287
@ GLOBAL_LINE_UP
Definition: keymap.h:195
@ GLOBAL_SEARCH_BODY
Definition: keymap.h:215
@ DIGIT_4
Definition: keymap.h:154
@ GLOBAL_OPTION_MENU
Definition: keymap.h:199
@ GLOBAL_MENU_FILTER_SELECT
Definition: keymap.h:198
@ DIGIT_5
Definition: keymap.h:155
@ GLOBAL_QUIT_TIN
Definition: keymap.h:211
@ PAGE_CANCEL
Definition: keymap.h:267
@ PAGE_FOLLOWUP_QUOTE_HEADERS
Definition: keymap.h:271
@ CATCHUP_NEXT_UNREAD
Definition: keymap.h:170
@ MARK_ARTICLE_UNREAD
Definition: keymap.h:261
@ PAGE_MARK_THREAD_READ
Definition: keymap.h:276
@ GLOBAL_MENU_FILTER_KILL
Definition: keymap.h:197
@ MARK_THREAD_UNREAD
Definition: keymap.h:262
@ GLOBAL_QUICK_FILTER_KILL
Definition: keymap.h:208
@ PAGE_FOLLOWUP_QUOTE
Definition: keymap.h:270
@ SPECIAL_MOUSE_TOGGLE
Definition: keymap.h:168
@ CATCHUP
Definition: keymap.h:169
@ PAGE_TOGGLE_HIGHLIGHTING
Definition: keymap.h:295
@ GLOBAL_LAST_VIEWED
Definition: keymap.h:193
@ PAGE_REPOST
Definition: keymap.h:290
struct keylist url_keys
Definition: keymap.c:91
char func_to_key(t_function func, const struct keylist keys)
Definition: keymap.c:125
enum defined_functions t_function
Definition: keymap.h:375
#define PrintFuncKey(buf, func, keys)
Definition: keymap.h:445
static char buf[16]
Definition: langinfo.c:50
#define PAGE_HEADER
Definition: page.c:53
int this_resp
Definition: page.c:71
static int prompt_response(int ch, int curr_respnum)
Definition: page.c:1960
static int rotate
Definition: page.c:83
static void show_url_page(void)
Definition: page.c:2411
#define XFACE_SHOW()
Definition: page.c:129
#define XFACE_CLEAR()
Definition: page.c:130
int curr_line
Definition: page.c:56
static t_bool process_url(int n)
Definition: page.c:2623
static t_function url_left(void)
Definition: page.c:2395
static t_bool url_page(void)
Definition: page.c:2437
static void process_search(int *lcurr_line, size_t message_lines, size_t screen_lines, int help_level)
Definition: page.c:1986
static int curr_info_line
Definition: page.c:79
static t_bool reveal_ctrl_l
Definition: page.c:90
t_openartinfo pgart
Definition: page.c:63
void info_pager(FILE *info_fh, const char *title, t_bool wrap_at_ends)
Definition: page.c:2174
int last_resp
Definition: page.c:70
static FILE * note_fp
Definition: page.c:57
size_t tabwidth
Definition: page.c:73
static const char * info_title
Definition: page.c:78
t_url * find_url(int n)
Definition: page.c:2581
static t_bool deactivate_next_ctrl_l(void)
Definition: page.c:245
static t_function page_right(void)
Definition: page.c:197
#define ARTLINES
Definition: page.c:54
static t_bool show_all_headers
Definition: page.c:88
static void print_message_page(FILE *file, t_lineinfo *messageline, size_t messagelines, size_t base_line, size_t begin, size_t end, int help_level)
Definition: page.c:1037
static t_lineinfo * infoline
Definition: page.c:86
static int artlines
Definition: page.c:58
static void free_url_list(void)
Definition: page.c:2705
void toggle_raw(struct t_group *group)
Definition: page.c:2034
static int build_url_list(void)
Definition: page.c:2654
static t_menu urlmenu
Definition: page.c:119
static t_bool show_raw_article
Definition: page.c:89
static int hide_uue
Definition: page.c:80
static t_url * url_list
Definition: page.c:61
int show_page(struct t_group *group, int start_respnum, int *threadnum)
Definition: page.c:306
static FILE * info_file
Definition: page.c:77
static int num_info_lines
Definition: page.c:81
static t_lineinfo * artline
Definition: page.c:59
static t_function page_mouse_action(t_function(*left_action)(void), t_function(*right_action)(void))
Definition: page.c:205
static int scroll_page(int dir)
Definition: page.c:145
void resize_article(t_bool wrap_lines, t_openartinfo *artinfo)
Definition: page.c:2152
static void build_url_line(int i)
Definition: page.c:2595
static int search_line
Definition: page.c:85
static t_bool activate_last_ctrl_l(void)
Definition: page.c:274
static void preprocess_info_message(FILE *info_fh)
Definition: page.c:2363
static t_function page_left(void)
Definition: page.c:189
void draw_page(const char *group, int part)
Definition: page.c:1175
static void draw_url_arrow(void)
Definition: page.c:2566
#define XFACE_SUPPRESS()
Definition: page.c:131
static int reveal_ctrl_l_lines
Definition: page.c:82
static struct t_header * note_h
Definition: page.c:75
static int load_article(int new_respnum, struct t_group *group)
Definition: page.c:1837
static void draw_percent_mark(long cur_num, long max_num)
Definition: page.c:2720
static int scroll_region_top
Definition: page.c:84
static void draw_page_header(const char *group)
Definition: page.c:1327
static t_function url_right(void)
Definition: page.c:2403
static void invoke_metamail(FILE *fp)
Definition: page.c:1259
void display_info_page(int part)
Definition: page.c:2320
static char * end
Definition: plp_snprintf.c:205
char * expand_tab(char *str, size_t tab_width)
Definition: string.c:630
void scroll_down(void)
Definition: global.c:252
void move_to_item(int n)
Definition: global.c:227
void page_up(void)
Definition: global.c:130
t_bool is_art_tex_encoded(FILE *fp)
Definition: charset.c:352
void fixup_thread(int respnum, t_bool redraw)
Definition: thread.c:1020
void prompt_item_num(int ch, const char *prompt)
Definition: global.c:200
void draw_arrow_mark(int line)
Definition: screen.c:352
char * idna_decode(char *in)
Definition: misc.c:3810
t_bool pickup_postponed_articles(t_bool ask, t_bool all)
Definition: post.c:2904
void center_line(int line, t_bool inverse, const char *str)
Definition: screen.c:298
void show_mini_help(int level)
Definition: help.c:798
int num_of_responses(int n)
Definition: thread.c:1124
void make_threads(struct t_group *group, t_bool rethread)
Definition: art.c:1229
int my_tmpfile(char *filename, size_t name_size, const char *base_dir)
Definition: my_tmpfile.c:57
t_bool filter_articles(struct t_group *group)
Definition: filter.c:1843
t_bool art_edit(struct t_group *group, struct t_article *article)
Definition: mail.c:640
void prompt_continue(void)
Definition: prompt.c:803
t_bool tag_article(int art)
Definition: tags.c:138
t_bool read_filter_file(const char *file)
Definition: filter.c:308
void StartInverse(void)
Definition: curses.c:540
int find_artnum(t_artnum art)
Definition: art.c:3223
void show_help_page(const int level, const char *title)
Definition: help.c:734
size_t my_strftime(char *s, size_t maxsize, const char *format, struct tm *timeptr)
Definition: strftime.c:64
void reset_srch_offsets(void)
Definition: search.c:784
void art_mark(struct t_group *group, struct t_article *art, int flag)
Definition: newsrc.c:1611
_Noreturn void tin_done(int ret, const char *fmt,...)
Definition: misc.c:562
void perror_message(const char *fmt,...)
Definition: screen.c:260
int which_response(int n)
Definition: thread.c:1099
void decode_save_mime(t_openartinfo *art, t_bool postproc)
Definition: save.c:1472
char * strip_line(char *line)
Definition: misc.c:3644
void ClearScreen(void)
Definition: curses.c:410
void do_shell_escape(void)
Definition: misc.c:547
char * abbr_groupname(const char *grpname, size_t len)
Definition: string.c:997
void set_xclick_on(void)
Definition: curses.c:691
int mail_to_author(const char *group, int respnum, t_bool copy_text, t_bool with_headers, t_bool raw_data)
Definition: post.c:4005
void scroll_up(void)
Definition: global.c:278
void config_page(const char *grpname, enum context level)
Definition: options_menu.c:939
void cursoroff(void)
Definition: curses.c:721
t_bool filter_menu(t_function type, struct t_group *group, struct t_article *art)
Definition: filter.c:1061
void info_message(const char *fmt,...)
Definition: screen.c:102
void toggle_inverse_video(void)
Definition: misc.c:1050
int prev_unread(int n)
Definition: thread.c:1393
int feed_articles(int function, int level, t_function type, struct t_group *group, int respnum)
Definition: feed.c:566
t_bool invoke_cmd(const char *nam)
Definition: misc.c:813
void ScrollScreen(int lines_to_scroll)
Definition: curses.c:509
int search_article(t_bool forward, t_bool repeat, int start_line, int lines, t_lineinfo *line, int reveal_ctrl_l_lines, FILE *fp)
Definition: search.c:604
void show_inverse_video_status(void)
Definition: misc.c:1065
void pos_first_unread_thread(void)
Definition: group.c:1130
void EndWin(void)
Definition: curses.c:368
int get_search_vectors(int *start, int *end)
Definition: search.c:766
int prompt_msgid(void)
Definition: prompt.c:621
void shell_escape(void)
Definition: misc.c:496
int next_thread(int n)
Definition: thread.c:1302
void toggle_mini_help(int level)
Definition: help.c:1078
char * my_strdup(const char *str)
Definition: string.c:139
t_bool cook_article(t_bool wrap_lines, t_openartinfo *artinfo, int hide_uue, t_bool show_all_headers)
Definition: cook.c:828
void SetScrollRegion(int topline, int bottomline)
Definition: curses.c:490
int prev_response(int n)
Definition: thread.c:1321
t_bool invoke_editor(const char *filename, int lineno, struct t_group *group)
Definition: misc.c:377
void MoveCursor(int row, int col)
Definition: curses.c:441
int generic_search(t_bool forward, t_bool repeat, int current, int last, int level)
Definition: search.c:196
t_bool prompt_default_string(const char *prompt, char *buf, int buf_len, char *default_prompt, int which_hist)
Definition: prompt.c:108
void art_close(t_openartinfo *artinfo)
Definition: rfc2046.c:1623
void Raw(int state)
Definition: curses.c:624
void thd_mark_unread(struct t_group *group, long thread)
Definition: newsrc.c:806
int find_response(int i, int n)
Definition: thread.c:1342
t_bool post_hist_page(void)
Definition: post.c:519
void page_down(void)
Definition: global.c:155
void attachment_page(t_openartinfo *art)
Definition: save.c:1569
void move_up(void)
Definition: global.c:81
void set_xclick_off(void)
Definition: curses.c:703
void clear_note_area(void)
Definition: group.c:1051
void InitWin(void)
Definition: curses.c:355
void move_down(void)
Definition: global.c:110
void clear_message(void)
Definition: screen.c:283
int next_response(int n)
Definition: thread.c:1280
int search_body(struct t_group *group, int current_art, t_bool repeat)
Definition: search.c:722
char * tin_fgets(FILE *fp, t_bool header)
Definition: read.c:317
int which_thread(int n)
Definition: thread.c:1073
int strwidth(const char *str)
Definition: string.c:1050
void set_first_screen_item(void)
Definition: global.c:61
void end_of_list(void)
Definition: global.c:191
void wait_message(unsigned int sdelay, const char *fmt,...)
Definition: screen.c:133
void unfilter_articles(struct t_group *group)
Definition: filter.c:1818
t_bool post_article(const char *groupname)
Definition: post.c:2994
void stow_cursor(void)
Definition: screen.c:59
void thd_mark_read(struct t_group *group, long thread)
Definition: newsrc.c:789
int art_open(t_bool wrap_lines, struct t_article *art, struct t_group *group, t_openartinfo *artinfo, t_bool show_progress_meter, const char *pmesg)
Definition: rfc2046.c:1566
int search(t_function func, int current_art, t_bool repeat)
Definition: search.c:564
t_bool cancel_article(struct t_group *group, struct t_article *art, int respnum)
Definition: post.c:4222
void highlight_regexes(int row, struct regex_cache *regex, int color)
Definition: regex.c:148
char * escape_shell_meta(const char *source, int quote_area)
Definition: misc.c:1725
void CleartoEOS(void)
Definition: curses.c:470
void draw_pager_line(const char *str, int flags, t_bool raw_data)
Definition: color.c:245
int prompt_num(int ch, const char *prompt)
Definition: prompt.c:65
int post_response(const char *groupname, int respnum, t_bool copy_text, t_bool with_headers, t_bool raw_data)
Definition: post.c:3314
void top_of_list(void)
Definition: global.c:182
int my_isprint(int c)
Definition: misc.c:978
int next_unread(int n)
Definition: thread.c:1363
int prompt_yn(const char *prompt, t_bool default_answer)
Definition: prompt.c:165
char * strunc(const char *message, int len)
Definition: string.c:1076
char * tin_ltoa(t_artnum value, int digits)
Definition: string.c:80
t_bool quick_filter(t_function type, struct t_group *group, struct t_article *art)
Definition: filter.c:1496
void EndInverse(void)
Definition: curses.c:564
void highlight_string(int row, int col, int size)
Definition: curses.c:733
static int offset
Definition: read.c:62
#define C_NEWS
Definition: rfc2046.h:165
#define C_URL
Definition: rfc2046.h:163
#define C_CTRLL
Definition: rfc2046.h:166
#define C_MAIL
Definition: rfc2046.h:164
#define C_BODY
Definition: rfc2046.h:153
#define C_SIG
Definition: rfc2046.h:154
#define C_QUOTE1
Definition: rfc2046.h:159
#define C_QUOTE3
Definition: rfc2046.h:161
#define C_QUOTE2
Definition: rfc2046.h:160
void(* func)(SIG_ARGS)
Definition: signal.c:176
const char * name
Definition: signal.c:117
int flags
Definition: rfc2046.h:176
long offset
Definition: rfc2046.h:175
t_lineinfo * cookl
Definition: rfc2046.h:191
t_bool tex2iso
Definition: rfc2046.h:186
int cooked_lines
Definition: rfc2046.h:187
FILE * cooked
Definition: rfc2046.h:189
struct t_header hdr
Definition: rfc2046.h:185
FILE * raw
Definition: rfc2046.h:188
t_lineinfo * rawl
Definition: rfc2046.h:190
Definition: rfc2046.h:93
int line_count
Definition: rfc2046.h:104
pcre_extra * extra
Definition: tin.h:1963
pcre * re
Definition: tin.h:1962
char * subject
Definition: tin.h:1535
int thread
Definition: tin.h:1548
struct t_msgid * refptr
Definition: tin.h:1543
t_artnum artnum
Definition: tin.h:1534
unsigned int killed
Definition: tin.h:1552
unsigned thread_articles
Definition: tin.h:1677
char * mailing_list
Definition: tin.h:1626
char * date_format
Definition: tin.h:1619
unsigned tex2iso_conv
Definition: tin.h:1702
unsigned ask_for_metamail
Definition: tin.h:1654
int mono_markslash
Definition: tinrc.h:203
char url_handler[LEN]
Definition: tinrc.h:140
int scroll_lines
Definition: tinrc.h:156
int mono_markdash
Definition: tinrc.h:201
int kill_level
Definition: tinrc.h:151
int hide_uue
Definition: tinrc.h:150
t_bool url_highlight
Definition: tinrc.h:206
t_bool abbreviate_groupname
Definition: tinrc.h:211
int goto_next_unread
Definition: tinrc.h:149
int mono_markstar
Definition: tinrc.h:202
int mono_markstroke
Definition: tinrc.h:204
char metamail_prog[PATH_LEN]
Definition: tinrc.h:109
t_bool info_in_last_line
Definition: tinrc.h:226
Definition: tin.h:1816
struct t_attribute * attribute
Definition: tin.h:1834
char * name
Definition: tin.h:1817
char * subj
Definition: rfc2046.h:133
char * org
Definition: rfc2046.h:134
char * date
Definition: rfc2046.h:132
t_part * ext
Definition: rfc2046.h:146
t_bool mime
Definition: rfc2046.h:145
char * xface
Definition: rfc2046.h:144
Definition: tin.h:2055
int curr
Definition: tin.h:2056
int first
Definition: tin.h:2058
int max
Definition: tin.h:2057
Definition: tin.h:1509
struct t_msgid * parent
Definition: tin.h:1511
int article
Definition: tin.h:1514
char * col
Definition: tin.h:1974
Definition: tin.h:2112
struct urllist * next
Definition: tin.h:2114
char * url
Definition: tin.h:2113
#define my_flush()
Definition: tcurses.h:177
#define cCRLF
Definition: tcurses.h:156
#define WriteLine(row, buffer)
Definition: tcurses.h:180
#define my_fputs(str, stream)
Definition: tcurses.h:159
#define my_retouch()
Definition: tcurses.h:179
#define my_printf
Definition: tcurses.h:175
#define my_fputc(ch, stream)
Definition: tcurses.h:158
#define TINRC_CONFIRM_ACTION
Definition: tin.h:958
#define MOUSE_BUTTON_1
Definition: tin.h:2159
#define LEN
Definition: tin.h:860
long t_artnum
Definition: tin.h:229
#define SEEK_SET
Definition: tin.h:2512
#define URL_LEVEL
Definition: tin.h:1121
#define THREAD_NONE
Definition: tin.h:1139
#define UUE_NO
Definition: tin.h:1249
#define PAGE_LEVEL
Definition: tin.h:1115
@ cURL
Definition: tin.h:107
@ cPage
Definition: tin.h:107
@ cInfopager
Definition: tin.h:107
#define IS_LOCAL_CHARSET(c)
Definition: tin.h:782
#define ART_KILLED_UNREAD
Definition: tin.h:1355
#define FEED_PIPE
Definition: tin.h:1127
#define STRCPY(dst, src)
Definition: tin.h:820
#define FEED_AUTOSAVE
Definition: tin.h:1130
#define INFO_PAGER
Definition: tin.h:1116
#define FEED_SAVE
Definition: tin.h:1129
#define KEYMAP_UP
Definition: tin.h:1077
#define INDEX2LNUM(i)
Definition: tin.h:1020
#define MOUSE_BUTTON_3
Definition: tin.h:2161
#define FEED_PRINT
Definition: tin.h:1128
#define my_malloc(size)
Definition: tin.h:2245
#define unlink(file)
Definition: tin.h:387
#define GOTO_NEXT_UNREAD_TAB
Definition: tin.h:975
#define HAS_FOLLOWUPS(i)
Definition: tin.h:1040
#define ART_KILLED
Definition: tin.h:1354
#define ARRAY_SIZE(array)
Definition: tin.h:2250
#define FreeIfNeeded(p)
Definition: tin.h:2252
#define EXIT_FAILURE
Definition: tin.h:1302
#define INDEX2SNUM(i)
Definition: tin.h:1022
#define FEED_REPOST
Definition: tin.h:1131
#define ART_ABORT
Definition: tin.h:1360
#define _(Text)
Definition: tin.h:94
@ no_quote
Definition: tin.h:1257
#define KILL_NOTHREAD
Definition: tin.h:1219
#define forever
Definition: tin.h:816
#define PATH_LEN
Definition: tin.h:843
#define IS_PLAINTEXT(x)
Definition: tin.h:1036
#define INTERNAL_CMD
Definition: tin.h:574
#define snprintf
Definition: tin.h:2464
#define FreeAndNull(p)
Definition: tin.h:2253
#define ART_WILL_RETURN
Definition: tin.h:1347
#define GROUP_TYPE_NEWS
Definition: tin.h:1070
#define GROUP_TYPE_MAIL
Definition: tin.h:1069
#define GOTO_NEXT_UNREAD_PGDN
Definition: tin.h:974
#define KEYMAP_DOWN
Definition: tin.h:1078
#define INDEX_TOP
Definition: tin.h:1019
#define FEED_MAIL
Definition: tin.h:1126
#define ART_READ
Definition: tin.h:1345
#define my_realloc(ptr, size)
Definition: tin.h:2247
#define UUE_ALL
Definition: tin.h:1251
#define MOUSE_BUTTON_2
Definition: tin.h:2160
@ GRP_EXIT
Definition: tin.h:1294
@ GRP_NEXTUNREAD
Definition: tin.h:1287
@ GRP_NEXT
Definition: tin.h:1288
@ GRP_RETSELECT
Definition: tin.h:1285
@ GRP_GOTOTHREAD
Definition: tin.h:1292
@ GRP_QUIT
Definition: tin.h:1286
@ GRP_KILLED
Definition: tin.h:1291
@ GRP_ARTUNAVAIL
Definition: tin.h:1289
@ GRP_ARTABORT
Definition: tin.h:1290
#define assert(p)
Definition: tin.h:1320
#define ART_UNAVAILABLE
Definition: tin.h:1348
#define BLANK_PAGE_COLS
Definition: tin.h:950
#define MAX(a, b)
Definition: tin.h:808
#define BlankIfNull(p)
Definition: tin.h:2255