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