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)  

select.c
Go to the documentation of this file.
1/*
2 * Project : tin - a Usenet reader
3 * Module : select.c
4 * Author : I. Lea & R. Skrenta
5 * Created : 1991-04-01
6 * Updated : 2021-08-28
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
49static struct t_fmt sel_fmt;
50
51/*
52 * Local prototypes
53 */
54static t_function select_left(void);
55static t_function select_right(void);
56static int active_comp(t_comptype p1, t_comptype p2);
57static int reposition_group(struct t_group *group, int default_num);
58static int save_restore_curr_group(t_bool saving);
61static void build_gline(int i);
62static void catchup_group(struct t_group *group, t_bool goto_next_unread_group);
63static void draw_group_arrow(void);
64static void read_groups(void);
65static void select_done(void);
66static void select_quit(void);
67static void select_read_group(void);
68static void sort_active_file(void);
69static void subscribe_pattern(const char *prompt, const char *message, const char *result, t_bool state);
70static void sync_active_file(void);
71static void yank_active_file(void);
72#ifdef NNTP_ABLE
73 static char *lookup_msgid(char *msgid);
74 static struct t_group *get_group_from_list(char *newsgroups);
75#endif /* NNTP_ABLE */
76
77
78/*
79 * selmenu.curr = index (start at 0) of cursor position on menu,
80 * or -1 when no groups visible on screen
81 * selmenu.max = Total # of groups in my_group[]
82 * selmenu.first is static here
83 */
85
86static int groupname_len; /* max. group name length */
87static int flags_offset;
88static int ucnt_offset;
89
90
91static t_function
93 void)
94{
95 return GLOBAL_QUIT;
96}
97
98
99static t_function
101 void)
102{
103 return SELECT_ENTER_GROUP;
104}
105
106
107void
109 int start_groupnum,
110 int num_cmd_line_groups)
111{
112 char buf[LEN];
113 char key[MAXKEYLEN];
114 int i, n;
116#if defined(MULTIBYTE_ABLE) && !defined(NO_LOCALE)
117 wchar_t *wtmp;
118#endif /* MULTIBYTE_ABLE && !NO_LOCALE */
119
120 selmenu.curr = start_groupnum;
121
122#ifdef READ_CHAR_HACK
123 setbuf(stdin, 0);
124#endif /* READ_CHAR_HACK */
125
126 Raw(TRUE);
127 ClearScreen();
128
129 /*
130 * If user specified only 1 cmd line groupname (eg. tin -r alt.sources)
131 * then go there immediately.
132 */
133 if (num_cmd_line_groups == 1)
135
136 cursoroff();
137 show_selection_page(); /* display group selection page */
138
139 forever {
140 if (!resync_active_file()) {
141 if (reread_active_after_posting()) /* reread active file if necessary */
143 } else {
144 if (!yanked_out)
145 yanked_out = bool_not(yanked_out); /* yank out if yanked in */
146 }
147
149
151 case GLOBAL_ABORT: /* Abort */
152 break;
153
154 case DIGIT_1:
155 case DIGIT_2:
156 case DIGIT_3:
157 case DIGIT_4:
158 case DIGIT_5:
159 case DIGIT_6:
160 case DIGIT_7:
161 case DIGIT_8:
162 case DIGIT_9:
163 if (selmenu.max)
165 else
167 break;
168
169#ifndef NO_SHELL_ESCAPE
172 break;
173#endif /* !NO_SHELL_ESCAPE */
174
175 case GLOBAL_FIRST_PAGE: /* show first page of groups */
176 top_of_list();
177 break;
178
179 case GLOBAL_LAST_PAGE: /* show last page of groups */
180 end_of_list();
181 break;
182
183 case GLOBAL_PAGE_UP:
184 page_up();
185 break;
186
187 case GLOBAL_PAGE_DOWN:
188 page_down();
189 break;
190
191 case GLOBAL_LINE_UP:
192 move_up();
193 break;
194
195 case GLOBAL_LINE_DOWN:
196 move_down();
197 break;
198
200 scroll_down();
201 break;
202
203 case GLOBAL_SCROLL_UP:
204 scroll_up();
205 break;
206
207 case SELECT_SORT_ACTIVE: /* sort active groups */
209 break;
210
211 case GLOBAL_SET_RANGE:
212 if (selmenu.max) {
215 } else
217 break;
218
224 else {
226 move_to_item(i);
228 }
229 }
230 break;
231
232 case SELECT_ENTER_GROUP: /* go into group */
234 break;
235
236 case SELECT_ENTER_NEXT_UNREAD_GROUP: /* enter next group containing unread articles */
238 read_groups();
239 break; /* Nothing more to do at the moment */
240
242 my_retouch(); /* TODO: not done elsewhere, maybe should be in show_selection_page */
245 break;
246
247 case SELECT_RESET_NEWSRC: /* reset .newsrc */
248 if (no_write) {
250 break;
251 }
252 if (prompt_yn(_(txt_reset_newsrc), FALSE) == 1) {
253 reset_newsrc();
255 selmenu.curr = 0;
257 }
258 break;
259
260 case CATCHUP: /* catchup - mark all articles as read */
261 case CATCHUP_NEXT_UNREAD: /* and goto next unread group */
262 if (selmenu.max)
264 else
266 break;
267
272 break;
273
274 case SELECT_TOGGLE_DESCRIPTIONS: /* toggle newsgroup descriptions */
275 if (sel_fmt.show_grpdesc) {
280 } else
282 break;
283
284 case SELECT_GOTO: /* prompt for a new group name */
285 {
286 int oldmax = selmenu.max;
287
288 if ((n = choose_new_group()) >= 0) {
289 /*
290 * If a new group was added and it is on the actual screen
291 * draw it. If it is off screen the redraw will handle it.
292 */
293 if (oldmax != selmenu.max && n >= selmenu.first && n < selmenu.first + NOTESLINES)
294 build_gline(n);
295 move_to_item(n);
296 }
297 }
298 break;
299
300 case GLOBAL_HELP:
303 break;
304
305#ifdef NNTP_ABLE
307 switch (show_article_by_msgid(NULL)) {
308 case LOOKUP_OK:
310 break;
311
312 case LOOKUP_UNAVAIL:
314 break;
315
316 case LOOKUP_QUIT:
317 select_quit();
318 break;
319
320 default:
321 break;
322 }
323 break;
324#endif /* NNTP_ABLE */
325
326 case GLOBAL_TOGGLE_HELP_DISPLAY: /* toggle mini help menu */
329 break;
330
335 break;
336
337#ifdef HAVE_COLOR
338 case GLOBAL_TOGGLE_COLOR:
339 if (toggle_color()) {
341 show_color_status();
342 }
343 break;
344#endif /* HAVE_COLOR */
345
346 case GLOBAL_TOGGLE_INFO_LAST_LINE: /* display group description */
349 break;
350
351 case SELECT_MOVE_GROUP: /* reposition group within group list */
352 /* TODO: move all this to reposition_group() */
353 if (!selmenu.max) {
355 break;
356 }
357
358 if (!CURR_GROUP.subscribed) {
360 break;
361 }
362
363 if (no_write) {
365 break;
366 }
367
368 n = selmenu.curr;
373 else {
374 i = selmenu.curr;
375 selmenu.curr = n;
376 erase_arrow();
377 selmenu.curr = i;
380 }
381 break;
382
386 break;
387
388 case SELECT_NEXT_UNREAD_GROUP: /* goto next unread group */
390 break;
391
392 case GLOBAL_QUIT: /* quit */
393 select_done();
394 break;
395
396 case GLOBAL_QUIT_TIN: /* quit, no ask */
397 select_quit();
398 break;
399
400 case SELECT_QUIT_NO_WRITE: /* quit, but don't save configuration */
401 if (prompt_yn(_(txt_quit_no_write), TRUE) == 1)
402 tin_done(EXIT_SUCCESS, NULL);
404 break;
405
407 /*
408 * If in tinrc.show_only_unread_groups mode toggle all
409 * subscribed to groups and only groups that contain unread
410 * articles
411 */
413 /*
414 * as we effectively do a yank out on each change, set yanked_out accordingly
415 */
417 wait_message(0, _(txt_reading_groups), (tinrc.show_only_unread_groups) ? _("unread") : _("all"));
418
419 toggle_my_groups(NULL);
423 else
425 break;
426
427 case GLOBAL_BUGREPORT:
428 bug_report();
429 break;
430
431 case SELECT_SUBSCRIBE: /* subscribe to current group */
432 if (!selmenu.max) {
434 break;
435 }
436 if (no_write) {
438 break;
439 }
440 if (!CURR_GROUP.subscribed && !CURR_GROUP.bogus) {
444 move_down();
445 }
446 break;
447
448 case SELECT_SUBSCRIBE_PATTERN: /* subscribe to groups matching pattern */
449 if (no_write) {
451 break;
452 }
454 break;
455
456 case SELECT_UNSUBSCRIBE: /* unsubscribe to current group */
457 if (!selmenu.max) {
459 break;
460 }
461 if (no_write) {
463 break;
464 }
465 if (CURR_GROUP.subscribed) {
466#if defined(MULTIBYTE_ABLE) && !defined(NO_LOCALE)
467 mark_screen(selmenu.curr, flags_offset, CURR_GROUP.newgroup ? L"N" : L"u");
468#else
469 mark_screen(selmenu.curr, flags_offset, CURR_GROUP.newgroup ? "N" : "u");
470#endif /* MULTIBYTE_ABLE && !NO_LOCALE */
473 move_down();
474 } else if (CURR_GROUP.bogus && tinrc.strip_bogus == BOGUS_SHOW) {
475 /* Bogus groups aren't subscribed to avoid confusion */
476 /* Note that there is no way to remove the group from active[] */
477 snprintf(buf, sizeof(buf), _(txt_remove_bogus), CURR_GROUP.name);
478 write_newsrc(); /* save current newsrc */
479 delete_group(CURR_GROUP.name); /* remove bogus group */
480 read_newsrc(newsrc, TRUE); /* reload newsrc */
481 toggle_my_groups(NULL); /* keep current display-state */
482 show_selection_page(); /* redraw screen */
484 }
485 break;
486
487 case SELECT_UNSUBSCRIBE_PATTERN: /* unsubscribe to groups matching pattern */
488 if (no_write) {
490 break;
491 }
494 break;
495
496 case GLOBAL_VERSION: /* show tin version */
498 break;
499
500 case GLOBAL_POST: /* post a basenote */
501 if (!selmenu.max) {
502 if (!can_post) {
504 break;
505 }
508 break;
511 break;
512 } else {
514#if 1 /* TODO: fix the rest of the code so we don't need this anymore */
515 /*
516 * this is a gross hack to avoid a crash in the
517 * CHARSET_CONVERSION conversion case in new_part()
518 * which currently relies on CURR_GROUP
519 */
521 /*
522 * and the next hack to avoid a grabbled selection
523 * screen after the posting
524 */
525 toggle_my_groups(NULL);
526 toggle_my_groups(NULL);
527#endif /* 1 */
528 }
529 } else
530 STRCPY(buf, CURR_GROUP.name);
531 if (!can_post && !CURR_GROUP.bogus && !CURR_GROUP.attribute->mailing_list) {
533 break;
534 }
535 if (post_article(buf))
537 break;
538
539 case GLOBAL_POSTPONED: /* post postponed article */
540 if (can_post) {
543 } else
545 break;
546
547 case GLOBAL_DISPLAY_POST_HISTORY: /* display messages posted by user */
548 if (post_hist_page())
550 break;
551
552 case SELECT_YANK_ACTIVE: /* yank in/out rest of groups from active */
554 break;
555
556 case SELECT_SYNC_WITH_ACTIVE: /* Re-read active file to see if any new news */
558 if (!yanked_out)
559 yank_active_file(); /* yank out if yanked in */
560 break;
561
563 if (!selmenu.max) {
565 break;
566 }
568 if (CURR_GROUP.newsrc.num_unread)
569 STRCPY(buf, tin_ltoa(CURR_GROUP.newsrc.num_unread, (int) sel_fmt.len_ucnt));
570 else {
571 size_t j = 0;
572
573 while (j < sel_fmt.len_ucnt)
574 buf[j++] = ' ';
575 buf[j] = '\0';
576 }
577#if defined(MULTIBYTE_ABLE) && !defined(NO_LOCALE)
578 if ((wtmp = char2wchar_t(buf))) {
580 free(wtmp);
581 }
582#else
584#endif /* MULTIBYTE_ABLE && !NO_LOCALE */
585 break;
586
587 default:
589 }
590 }
591}
592
593
594void
596 void)
597{
598 char buf[LEN];
599 int i;
600 size_t len;
601
603 currmenu = &selmenu;
605 groupname_len = 0;
606 flags_offset = 0;
607 mark_offset = 0;
608 ucnt_offset = 0;
609
611 snprintf(buf, sizeof(buf), "%s (%s %d%s)", _(txt_group_selection), nntp_server, selmenu.max, (tinrc.show_only_unread_groups ? _(" R") : ""));
612 else
613 snprintf(buf, sizeof(buf), "%s (%d%s)", _(txt_group_selection), selmenu.max, (tinrc.show_only_unread_groups ? _(" R") : ""));
614
615 if (selmenu.curr < 0)
616 selmenu.curr = 0;
617
618 ClearScreen();
621
623 /*
624 * calculate max length of groupname field
625 * if yanked in (yanked_out == FALSE) check all groups in active file
626 * otherwise just subscribed to groups
627 */
628 if (yanked_out) {
629 for (i = 0; i < selmenu.max; i++) {
630 if ((len = (size_t) strwidth(active[my_group[i]].name)) > sel_fmt.len_grpname)
631 sel_fmt.len_grpname = len;
632 }
633 } else {
634 for_each_group(i) {
635 if ((len = (size_t) strwidth(active[i].name)) > sel_fmt.len_grpname)
636 sel_fmt.len_grpname = len;
637 }
638 }
639 }
640
642
645 if (groupname_len < 0)
646 groupname_len = 0;
647
648 if (!sel_fmt.len_grpdesc)
650 else {
653 }
654
657
658 for (i = selmenu.first; i < selmenu.first + NOTESLINES && i < selmenu.max; i++)
659 build_gline(i);
660
662
663#ifdef NNTP_ABLE
664 did_reconnect = FALSE;
665#endif /* NNTP_ABLE */
666
667 if (selmenu.max <= 0) {
669 return;
670 }
671
673}
674
675
676static void
678 int i)
679{
680 char *sptr, *fmt, *buf;
681 char subs;
682 int n;
683 size_t j;
684#if defined(MULTIBYTE_ABLE) && !defined(NO_LOCALE)
685 char *name_buf = NULL;
686 char *desc_buf = NULL;
687 wchar_t *active_name, *active_name2, *active_desc, *active_desc2;
688#else
689 char *active_name, *active_name2;
690 size_t fill, len_start;
691#endif /* MULTIBYTE_ABLE && !NO_LOCALE */
692
693#ifdef USE_CURSES
694 /*
695 * Allocate line buffer
696 * make it the same size like in !USE_CURSES case to simplify the code
697 */
698# if defined(MULTIBYTE_ABLE) && !defined(NO_LOCALE)
699 sptr = my_malloc(cCOLS * MB_CUR_MAX + 2);
700# else
701 sptr = my_malloc(cCOLS + 2);
702# endif /* MULTIBYTE_ABLE && !NO_LOCALE */
703#else
704 sptr = screen[INDEX2SNUM(i)].col;
705#endif /* USE_CURSES */
706
707 sptr[0] = '\0';
708 fmt = sel_fmt.str;
709 n = my_group[i];
710
711 if (tinrc.draw_arrow)
712 strcat(sptr, " ");
713
714 for (; *fmt; fmt++) {
715 if (*fmt != '%') {
716 strncat(sptr, fmt, 1);
717 continue;
718 }
719 switch (*++fmt) {
720 case '\0':
721 break;
722
723 case '%':
724 strncat(sptr, fmt, 1);
725 break;
726
727 case 'd':
728#if defined(MULTIBYTE_ABLE) && !defined(NO_LOCALE)
730 active_desc = char2wchar_t(active[n].description);
731 if (active_desc) {
732 if ((active_desc2 = wcspart(active_desc, (int) sel_fmt.len_grpdesc, TRUE)) != NULL) {
733 desc_buf = wchar_t2char(active_desc2);
734 free(active_desc);
735 free(active_desc2);
736 }
737 }
738 if (desc_buf) {
739 strcat(sptr, desc_buf);
740 FreeAndNull(desc_buf);
741 }
742 } else {
743 buf = sptr + strlen(sptr);
744 for (j = 0; j < sel_fmt.len_grpdesc; ++j)
745 *buf++ = ' ';
746 *buf = '\0';
747 }
748#else
750 len_start = strwidth(sptr);
751 strncat(sptr, active[n].description, sel_fmt.len_grpdesc);
752 fill = sel_fmt.len_grpdesc - (strwidth(sptr) - len_start);
753 } else
754 fill = sel_fmt.len_grpdesc;
755 buf = sptr + strlen(sptr);
756 for (j = 0; j < fill; ++j)
757 *buf++ = ' ';
758 *buf = '\0';
759#endif /* MULTIBYTE_ABLE && !NO_LOCALE */
760 break;
761
762 case 'f':
763 /*
764 * Display a flag for this group if needed
765 * . Bogus groups are dumped immediately 'D'
766 * . Normal subscribed groups may be
767 * ' ' normal, 'X' not postable, 'M' moderated, '=' renamed
768 * . Newgroups are 'N'
769 * . Unsubscribed groups are 'u'
770 *
771 * TODO: make flags configurable via tinrc?
772 */
773 if (active[n].bogus) /* Group is not in active list */
774 subs = 'D';
775 else if (active[n].subscribed) /* Important that this precedes Newgroup */
776 subs = group_flag(active[n].moderated);
777 else
778 subs = ((active[n].newgroup) ? 'N' : 'u'); /* New (but unsubscribed) group or unsubscribed group */
779 buf = sptr + strlen(sptr);
780 *buf++ = subs;
781 *buf = '\0';
782 break;
783
784 case 'G':
785#if defined(MULTIBYTE_ABLE) && !defined(NO_LOCALE)
786 if ((active_name = char2wchar_t(active[n].name)) == NULL) /* If char2wchar_t() fails try again after replacing unprintable characters */
787 active_name = char2wchar_t(convert_to_printable(active[n].name, FALSE));
788
789 if (active_name && tinrc.abbreviate_groupname) {
790 active_name2 = abbr_wcsgroupname(active_name, groupname_len);
791 free(active_name);
792 } else
793 active_name2 = active_name;
794
795 if (active_name2 && (active_name = wcspart(active_name2, groupname_len, TRUE)) != NULL) {
796 free(active_name2);
797 name_buf = wchar_t2char(active_name);
798 free(active_name);
799 }
800 if (name_buf) {
801 strcat(sptr, name_buf);
802 FreeAndNull(name_buf);
803 }
804#else
806 active_name = abbr_groupname(active[n].name, groupname_len);
807 else
808 active_name = my_strdup(active[n].name);
809
810 active_name2 = my_malloc(groupname_len + 1);
811 snprintf(active_name2, groupname_len + 1, "%-*s", groupname_len, active_name);
812 strcat(sptr, active_name2);
813 free(active_name);
814 free(active_name2);
815#endif /* MULTIBYTE_ABLE && !NO_LOCALE */
816 break;
817
818 case 'n':
819 strcat(sptr, tin_ltoa(i + 1, (int) sel_fmt.len_linenumber));
820 break;
821
822 case 'U':
823 if (active[my_group[i]].inrange) {
824#if defined(MULTIBYTE_ABLE) && !defined(NO_LOCALE)
825 char tmp_buf[10];
826
827 buf = sptr + strlen(sptr);
828 for (j = 1; j <= sel_fmt.len_ucnt - (art_mark_width - (art_mark_width - wcwidth(tinrc.art_marked_inrange))); ++j)
829 *buf++ = ' ';
830 snprintf(tmp_buf, sizeof(tmp_buf), "%"T_CHAR_FMT, tinrc.art_marked_inrange);
831 *buf-- = '\0';
832 strcat(buf, tmp_buf);
833#else
834 buf = sptr + strlen(sptr);
835 for (j = 1; j < sel_fmt.len_ucnt; ++j)
836 *buf++ = ' ';
838 *buf = '\0';
839#endif /* MULTIBYTE_ABLE && !NO_LOCALE */
840 } else if (active[my_group[i]].newsrc.num_unread) {
841 int getart_limit;
842 t_artnum num_unread;
843
845 num_unread = active[my_group[i]].newsrc.num_unread;
846 if (getart_limit > 0 && getart_limit < num_unread)
847 num_unread = getart_limit;
848 strcat(sptr, tin_ltoa(num_unread, (int) sel_fmt.len_ucnt));
849 } else {
850 buf = sptr + strlen(sptr);
851 for (j = 0; j < sel_fmt.len_ucnt; ++j)
852 *buf++ = ' ';
853 *buf = '\0';
854 }
855 break;
856
857 default:
858 break;
859 }
860 }
861#ifndef USE_CURSES
863 strcat(strip_line(sptr), cCRLF);
864#endif /* !USE_CURSES */
865
866 WriteLine(INDEX2LNUM(i), sptr);
867
868#ifdef USE_CURSES
869 free(sptr);
870#endif /* USE_CURSES */
871}
872
873
874static void
876 void)
877{
878 if (!selmenu.max)
880 else {
882 if (CURR_GROUP.aliasedto)
884 else if (tinrc.info_in_last_line)
885 info_message("%s", CURR_GROUP.description ? CURR_GROUP.description : _(txt_no_description));
886 else if (selmenu.curr == selmenu.max - 1)
888 }
889}
890
891
892static void
894 void)
895{
899}
900
901
902static void
904 void)
905{
906 if (yanked_out && selmenu.max == num_active) { /* All groups currently present? */
908 return;
909 }
910
911 if (yanked_out) { /* Yank in */
912 int i;
913 int prevmax = selmenu.max;
914
915 save_restore_curr_group(TRUE); /* Save group position */
916
917 /*
918 * Reset counter and load all the groups in active[] into my_group[]
919 */
920 selmenu.max = 0;
922 my_group[selmenu.max++] = i;
923
924 selmenu.curr = save_restore_curr_group(FALSE); /* Restore previous group position */
928 } else { /* Yank out */
929 toggle_my_groups(NULL);
934 }
935}
936
937
938/*
939 * Sort active[] and associated tin_sort() helper function
940 */
941static int
943 t_comptype p1,
944 t_comptype p2)
945{
946 const struct t_group *s1 = (const struct t_group *) p1;
947 const struct t_group *s2 = (const struct t_group *) p2;
948
949 return strcasecmp(s1->name, s2->name);
950}
951
952
953/*
954 * Call with TRUE to file away the current cursor position
955 * Call again with FALSE to return a suggested value to restore the
956 * current cursor (selmenu.curr) position
957 */
958static int
960 t_bool saving)
961{
962 static char *oldgroup;
963 static int oldmax = 0;
964 int ret;
965
966 /*
967 * Take a copy of the current groupname, if present
968 */
969 if (saving) {
970 oldmax = selmenu.max;
971 if (oldmax)
972 oldgroup = my_strdup(CURR_GROUP.name);
973 return 0;
974 }
975
976 /*
977 * Find & return the new screen position of the group
978 */
979 ret = -1;
980
981 if (oldmax) {
982 ret = my_group_find(oldgroup);
983 FreeAndNull(oldgroup);
984 }
985
986 if (ret == -1) { /* Group not present, return something semi-useful */
987 if (selmenu.max > 0)
988 ret = selmenu.max - 1;
989 else
990 ret = 0;
991 }
992 return ret;
993}
994
995
996static void
998 void)
999{
1001
1002 tin_sort(active, (size_t) num_active, sizeof(struct t_group), active_comp);
1004
1006
1008}
1009
1010
1011int
1013 void)
1014{
1015 char *prompt;
1016 int idx;
1017
1019
1021 free(prompt);
1022 return -1;
1023 }
1024 free(prompt);
1025
1027
1028 if (tinrc.default_goto_group[0] == '\0')
1029 return -1;
1030
1031 clear_message();
1032
1033 if ((idx = my_group_add(tinrc.default_goto_group, TRUE)) == -1)
1035
1036 return idx;
1037}
1038
1039
1040/*
1041 * Return new value for selmenu.max, skipping any new newsgroups that have been
1042 * found
1043 */
1044int
1046 void)
1047{
1048 int i = 0;
1049
1050 if (selmenu.max) {
1051 while (i < selmenu.max && active[my_group[i]].newgroup)
1052 i++;
1053 }
1054
1055 return i;
1056}
1057
1058
1059/*
1060 * Find a group in the users selection list, my_group[].
1061 * If 'add' is TRUE, then add the supplied group return the index into
1062 * my_group[] if group is added or was already there. Return -1 if group
1063 * is not in active[]
1064 *
1065 * NOTE: can't be static due to my_group_add() macro
1066 */
1067int
1069 const char *group,
1070 t_bool add,
1071 t_bool ignore_case)
1072{
1073 int i, j;
1074
1075 if ((i = find_group_index(group, ignore_case)) < 0)
1076 return -1;
1077
1078 for (j = 0; j < selmenu.max; j++) {
1079 if (my_group[j] == i)
1080 return j;
1081 }
1082
1083 if (add) {
1084 my_group[selmenu.max++] = i;
1085 return (selmenu.max - 1);
1086 }
1087
1088 return -1;
1089}
1090
1091
1092static int
1094 struct t_group *group,
1095 int default_num)
1096{
1097 char buf[LEN];
1098 char pos[LEN];
1099 int pos_num, newgroups;
1100
1101 /* Have already trapped no_write at this point */
1102
1103 snprintf(buf, sizeof(buf), _(txt_newsgroup_position), group->name,
1104 (tinrc.default_move_group ? tinrc.default_move_group : default_num + 1));
1105
1106 if (!prompt_string(buf, pos, HIST_MOVE_GROUP))
1107 return default_num;
1108
1109 if (strlen(pos))
1110 pos_num = ((pos[0] == '$') ? selmenu.max : atoi(pos));
1111 else {
1113 pos_num = tinrc.default_move_group;
1114 else
1115 return default_num;
1116 }
1117
1118 if (pos_num > selmenu.max)
1119 pos_num = selmenu.max;
1120 else if (pos_num <= 0)
1121 pos_num = 1;
1122
1123 newgroups = skip_newgroups();
1124
1125 /*
1126 * Can't move into newgroups, they aren't in .newsrc
1127 */
1128 if (pos_num <= newgroups) {
1130 return default_num;
1131 }
1132
1133 wait_message(0, _(txt_moving), group->name);
1134
1135 /*
1136 * seems to have the side effect of rearranging
1137 * my_groups, so tinrc.show_only_unread_groups has to be updated
1138 */
1140
1141 /*
1142 * New newgroups aren't in .newsrc so we need to offset to
1143 * get the right position
1144 */
1145 if (pos_group_in_newsrc(group, pos_num - newgroups)) {
1147 tinrc.default_move_group = pos_num;
1148 return (pos_num - 1);
1149 } else {
1150 tinrc.default_move_group = default_num + 1;
1151 return default_num;
1152 }
1153}
1154
1155
1156static void
1158 struct t_group *group,
1159 t_bool goto_next_unread_group)
1160{
1161 char *smsg = NULL;
1162#if defined(MULTIBYTE_ABLE) && !defined(NO_LOCALE)
1163 wchar_t *wtmp;
1164#endif /* MULTIBYTE_ABLE && !NO_LOCALE */
1165
1166 if ((!TINRC_CONFIRM_ACTION) || prompt_yn(sized_message(&smsg, _(txt_mark_group_read), group->name), TRUE) == 1) {
1167 grp_mark_read(group, NULL);
1168 {
1169 char buf[LEN];
1170 size_t i = 0;
1171
1172 while (i < sel_fmt.len_ucnt)
1173 buf[i++] = ' ';
1174 buf[i] = '\0';
1175#if defined(MULTIBYTE_ABLE) && !defined(NO_LOCALE)
1176 if ((wtmp = char2wchar_t(buf))) {
1178 free(wtmp);
1179 }
1180#else
1182#endif /* MULTIBYTE_ABLE && !NO_LOCALE */
1183 }
1184
1185 if (goto_next_unread_group)
1187 else
1188 move_down();
1189 }
1190 FreeIfNeeded(smsg);
1191}
1192
1193
1194/*
1195 * Set selmenu.curr to next group with unread arts
1196 * If the current group has unread arts, it will be found first !
1197 * If redraw is set, update the selection menu appropriately
1198 * Return FALSE if no groups left to read
1199 * TRUE at all other times
1200 */
1201static t_bool
1203 t_bool redraw)
1204{
1205 int i;
1206 t_bool all_groups_read = TRUE;
1207
1208 if (!selmenu.max)
1209 return FALSE;
1210
1211 for (i = selmenu.curr; i < selmenu.max; i++) {
1212 if (UNREAD_GROUP(i)) {
1213 all_groups_read = FALSE;
1214 break;
1215 }
1216 }
1217
1218 if (all_groups_read) {
1219 for (i = 0; i < selmenu.curr; i++) {
1220 if (UNREAD_GROUP(i)) {
1221 all_groups_read = FALSE;
1222 break;
1223 }
1224 }
1225 }
1226
1227 if (all_groups_read) {
1229 return FALSE;
1230 }
1231
1232 if (redraw)
1233 move_to_item(i);
1234 else
1235 selmenu.curr = i;
1236
1237 return TRUE;
1238}
1239
1240
1241/*
1242 * This is the main loop that cycles through, reading groups.
1243 * We keep going until we return to the selection screen or exit tin
1244 */
1245static void
1247 void)
1248{
1249 t_bool done = FALSE;
1250
1251 clear_message();
1252
1253 while (!done) { /* if xmin > xmax the newsservers active is broken */
1254 switch (group_page(&CURR_GROUP)) {
1255 case GRP_QUIT:
1256 select_quit();
1257 break;
1258
1259 case GRP_NEXT:
1260 if (selmenu.curr + 1 < selmenu.max)
1261 selmenu.curr++;
1262 done = TRUE;
1263 break;
1264
1265 case GRP_NEXTUNREAD:
1267 done = TRUE;
1268 break;
1269
1270 case GRP_ENTER: /* group_page() has already set selmenu.curr */
1271 break;
1272
1273 case GRP_RETSELECT:
1274 case GRP_EXIT:
1275 default:
1276 done = TRUE;
1277 break;
1278 }
1279 }
1280
1283}
1284
1285
1286/*
1287 * Toggle my_group[] between all groups / only unread groups
1288 * We make a special case for Newgroups (always appear, at the top)
1289 * and Bogus groups if tinrc.strip_bogus = BOGUS_SHOW
1290 */
1291void
1293 const char *group)
1294{
1295#if 1
1296 FILE *fp;
1297 char buf[NEWSRC_LINE];
1298 char *ptr;
1299#endif /* 1 */
1300 int i;
1301
1302 /*
1303 * Save current or next group with unread arts for later use
1304 */
1305 if (selmenu.max) {
1306 int old_curr_group_idx = 0;
1307
1308 if (group != NULL) {
1309 if ((i = my_group_find(group)) >= 0)
1310 old_curr_group_idx = i;
1311 } else
1312 old_curr_group_idx = (selmenu.curr == -1) ? 0 : selmenu.curr;
1313
1315 for (i = old_curr_group_idx; i < selmenu.max; i++) {
1316 if (UNREAD_GROUP(i) || active[my_group[i]].newgroup) {
1317 old_curr_group_idx = i;
1318 break;
1319 }
1320 }
1321 }
1322 selmenu.curr = old_curr_group_idx; /* Set current group to save */
1323 } else
1324 selmenu.curr = 0;
1325
1327
1328 selmenu.max = skip_newgroups(); /* Reposition after any newgroups */
1329
1330 /* TODO: why re-read .newsrc here, instead of something like this... */
1331#if 0
1332 for_each_group(i) {
1333 if (active[i].subscribed) {
1335 if (active[i].newsrc.num_unread > 0 || (active[i].bogus && tinrc.strip_bogus == BOGUS_SHOW))
1336 my_group[selmenu.max++] = i;
1337 } else
1338 my_group[selmenu.max++] = i;
1339 }
1340 }
1341#else
1342 /* preserve group ordering based on newsrc */
1343 if ((fp = fopen(newsrc, "r")) == NULL)
1344 return;
1345
1346 while (fgets(buf, (int) sizeof(buf), fp) != NULL) {
1347 if ((ptr = strchr(buf, SUBSCRIBED)) != NULL) {
1348 *ptr = '\0';
1349
1350 if ((i = find_group_index(buf, FALSE)) < 0)
1351 continue;
1352
1354 if (active[i].newsrc.num_unread || (active[i].bogus && tinrc.strip_bogus == BOGUS_SHOW))
1356 } else
1358 }
1359 }
1360 fclose(fp);
1361#endif /* 0 */
1362 selmenu.curr = save_restore_curr_group(FALSE); /* Restore saved group position */
1363}
1364
1365
1366/*
1367 * Subscribe or unsubscribe from a list of groups. List can be full list as
1368 * supported by match_group_list()
1369 */
1370static void
1372 const char *prompt,
1373 const char *message,
1374 const char *result,
1375 t_bool state)
1376{
1377 char buf[LEN];
1378 int i, subscribe_num = 0;
1379
1380 if (!num_active || no_write)
1381 return;
1382
1383 if (!prompt_string(prompt, buf, HIST_OTHER) || !*buf) {
1384 clear_message();
1385 return;
1386 }
1387
1388 wait_message(0, "%s", message);
1389
1390 for_each_group(i) {
1391 if (match_group_list(active[i].name, buf)) {
1392 if (active[i].subscribed != (state != FALSE)) {
1393 spin_cursor();
1394 /* If found and group is not subscribed add it to end of my_group[]. */
1396 if (state) {
1399 }
1400 subscribe_num++;
1401 }
1402 }
1403 }
1404
1405 if (subscribe_num) {
1406 toggle_my_groups(NULL);
1408 info_message(result, subscribe_num);
1409 } else
1411}
1412
1413
1414/*
1415 * Does NOT return
1416 */
1417static void
1419 void)
1420{
1422 ClearScreen();
1423 tin_done(EXIT_SUCCESS, NULL); /* Tin END */
1424}
1425
1426
1427static void
1429 void)
1430{
1432 select_quit();
1433 if (!no_write && prompt_yn(_(txt_save_config), TRUE) == 1) {
1435 write_newsrc();
1436 }
1438}
1439
1440
1441static void
1443 void)
1444{
1445 struct t_group *currgrp;
1446
1447 if (!selmenu.max || selmenu.curr == -1) {
1449 return;
1450 }
1451
1452 currgrp = &CURR_GROUP;
1453
1454 if (currgrp->bogus) {
1456 return;
1457 }
1458
1459 if (currgrp->xmax > 0 && (currgrp->xmin <= currgrp->xmax))
1460 read_groups();
1461 else
1463}
1464
1465
1466#ifdef NNTP_ABLE
1467/*
1468 * Try to fetch articles Nesgroups-header via [X]HDR or XPAT.
1469 */
1470static char *
1471lookup_msgid(
1472 char *msgid)
1473{
1475 if (!nntp_caps.hdr_cmd && !nntp_caps.xpat) {
1477 return NULL;
1478 }
1479 if (msgid) {
1480 char *ptr, *r = NULL;
1481 static char *x;
1482 char buf[NNTP_STRLEN];
1483 int ret;
1484
1485 if (nntp_caps.hdr_cmd) {
1486 snprintf(buf, sizeof(buf), "%s Newsgroups %s", nntp_caps.hdr_cmd, msgid);
1487 ret = new_nntp_command(buf, (nntp_caps.type == CAPABILITIES) ? OK_HDR : OK_HEAD, NULL, 0);
1488
1489 switch (ret) {
1490 case OK_HEAD:
1491 case OK_HDR:
1492 x = NULL;
1493 while ((ptr = tin_fgets(FAKE_NNTP_FP, FALSE)) != NULL) {
1494# ifdef DEBUG
1495 if (debug & DEBUG_NNTP)
1496 debug_print_file("NNTP", "<<<%s%s", logtime(), ptr);
1497# endif /* DEBUG */
1498
1499 if (ret == OK_HEAD) { /* RFC 2980 ("%s %s", id, grp) */
1500 if (!strncmp(ptr, msgid, strlen(msgid))) { /* INN, MPNews, Leafnode, Cnews nntpd */
1501 r = ptr + strlen(msgid) + 1;
1502 } else { /* DNEWS ("%d %s", num, grp) */
1503 r = ptr;
1504 while (*r && *r != ' ' && *r != '\t')
1505 r++;
1506 while (*r && (*r == ' ' || *r == '\t'))
1507 r++;
1508 }
1509 }
1510
1511 if (ret == OK_HDR) { /* RFC 3977 ("0 %s", grp) */
1512 if (*ptr == '0' && (*(ptr + 1) == ' ' || *(ptr + 1) == '\t'))
1513 r = ptr + 2;
1514 }
1515
1516 if (r) {
1517 FreeIfNeeded(x); /* only required on bogus multi responses, just to be safe */
1518 x = my_strdup(r);
1519 }
1520 }
1521
1522 if (x)
1523 return x;
1524
1525 if (!r) {
1526# ifdef DEBUG
1527 if ((debug & DEBUG_NNTP) && verbose > 1)
1528 debug_print_file("NNTP", "lookup_msgid(%s) response empty or not recognized", buf);
1529# endif /* DEBUG */
1530 if (!nntp_caps.xpat)
1532 }
1533 if (r || !nntp_caps.xpat)
1534 return NULL;
1535 break;
1536
1537 case ERR_NOART:
1539 return NULL;
1540
1541 default:
1542 if (!nntp_caps.xpat) { /* try only once */
1544 return NULL;
1545 }
1546 break;
1547 }
1548 }
1549
1550 if (nntp_caps.xpat) {
1551 snprintf(buf, sizeof(buf), "XPAT Newsgroups %s *", msgid);
1552 ret = new_nntp_command(buf, OK_HEAD, NULL, 0);
1553 x = NULL;
1554 switch (ret) {
1555 case OK_HEAD:
1556 while ((ptr = tin_fgets(FAKE_NNTP_FP, FALSE)) != NULL) {
1557# ifdef DEBUG
1558 if (debug & DEBUG_NNTP)
1559 debug_print_file("NNTP", "<<<%s%s", logtime(), ptr);
1560# endif /* DEBUG */
1561 if (!strncmp(ptr, msgid, strlen(msgid)))
1562 r = ptr + strlen(msgid) + 1;
1563
1564 if (r) {
1565 FreeIfNeeded(x); /* only required on bogus multi responses, just to be safe */
1566 x = my_strdup(r);
1567 }
1568 }
1569
1570 if (x)
1571 return x;
1572
1573 if (!r) {
1574# ifdef DEBUG
1575 if ((debug & DEBUG_NNTP) && verbose > 1)
1576 debug_print_file("NNTP", "lookup_msgid(%s) response empty or not recognized", buf);
1577# endif /* DEBUG */
1579 /* nntp_caps.xpat = FALSE; */ /* ? */
1580 }
1581 return NULL;
1582
1583 case ERR_NOART:
1585 return NULL;
1586
1587 default:
1589 break;
1590 }
1591 }
1593 }
1594 } else
1596
1597 return NULL;
1598}
1599
1600
1601/*
1602 * Get a message ID for the 'L' command. Add <> if needed.
1603 * Try to enter an appropriate group and display the referenced article.
1604 * If no group from the Newsgroups:-header is available, display the
1605 * contents of the header.
1606 */
1607int
1609 char *messageid)
1610{
1611 char id[NNTP_STRLEN]; /* still way too big; RFC 3977 3.6 & RFC 5536 3.1.3 limit Message-ID to max 250 octets */
1612 char *idptr = NULL;
1613 char *newsgroups = NULL;
1614 int i, ret = 0;
1615 struct t_article *art;
1616 struct t_group *group = NULL;
1617 struct t_group *tmp_group = NULL;
1618 struct t_msgid *msgid = NULL;
1619 t_bool tmp_cache_overview_files;
1620 t_bool tmp_show_only_unread_arts;
1621
1623 return LOOKUP_UNAVAIL;
1624 }
1625
1626 if (messageid) {
1627 idptr = messageid;
1628 newsgroups = lookup_msgid(idptr);
1629 } else {
1630 if (prompt_string(_(txt_enter_message_id), id + 1, HIST_MESSAGE_ID) && id[1]) {
1631 idptr = str_trim(id + 1);
1632 if (id[1] != '<') {
1633 id[0] = '<';
1634 strcat(id, ">");
1635 idptr = id;
1636 }
1637 newsgroups = lookup_msgid(idptr);
1638 }
1639 }
1640
1641 if (!newsgroups)
1642 return LOOKUP_ART_UNAVAIL;
1643
1644 if ((group = get_group_from_list(newsgroups)) == NULL) {
1645 info_message(strchr(newsgroups, ',') ? _(txt_lookup_show_groups) : _(txt_lookup_show_group), newsgroups);
1646 free(newsgroups);
1647 return LOOKUP_FAILED;
1648 }
1649
1650 if (curr_group)
1651 tmp_group = curr_group;
1652 curr_group = group;
1655 last_resp = -1;
1656 this_resp = -1;
1657 tmp_cache_overview_files = tinrc.cache_overview_files;
1659 tmp_show_only_unread_arts = curr_group->attribute->show_only_unread_arts;
1661
1662 if (!index_group(group)) {
1663 for_each_art(i) {
1664 art = &arts[i];
1665 FreeAndNull(art->refs);
1666 FreeAndNull(art->msgid);
1667 }
1668 tin_errno = 0;
1669 ret = LOOKUP_FAILED;
1670 }
1671
1672 if (!ret) {
1673 grpmenu.first = 0;
1674
1675 if (*idptr == '\0')
1676 ret = LOOKUP_ART_UNAVAIL;
1677
1678 if ((msgid = find_msgid(idptr)) == NULL)
1679 ret = LOOKUP_ART_UNAVAIL;
1680
1681 if (!ret && msgid->article == ART_UNAVAILABLE)
1682 ret = LOOKUP_ART_UNAVAIL;
1683
1684 if (!ret && which_thread(msgid->article) == -1)
1685 ret = LOOKUP_NO_LAST;
1686 }
1687
1688 if (!ret) {
1689 switch (show_page(group, msgid->article, NULL)) {
1690 case GRP_QUIT:
1691 ret = LOOKUP_QUIT;
1692 break;
1693
1694 default:
1695 break;
1696 }
1697 }
1698
1699 free(newsgroups);
1700 art_close(&pgart);
1701 tinrc.cache_overview_files = tmp_cache_overview_files;
1702 curr_group->attribute->show_only_unread_arts = CAST_BOOL(tmp_show_only_unread_arts);
1703 if (tmp_group) {
1704 curr_group = tmp_group;
1705 if (!index_group(curr_group)) {
1706 for_each_art(i) {
1707 art = &arts[i];
1708 FreeAndNull(art->refs);
1709 FreeAndNull(art->msgid);
1710 }
1711 curr_group = NULL;
1712 tin_errno = 0;
1713 ret = LOOKUP_FAILED;
1714 }
1715 } else
1716 curr_group = NULL;
1717
1718 this_resp = last_resp = -1;
1719
1720 return ret;
1721}
1722
1723
1724/*
1725 * Takes a list of newsgroups and determines if one of them is available.
1726 */
1727static struct t_group *
1728get_group_from_list(
1729 char *newsgroups)
1730{
1731 char *ptr, *tr;
1732 t_bool found = FALSE;
1733 struct t_group *group = NULL;
1734
1735 if (!newsgroups || (ptr = strtok(newsgroups, ",")) == NULL)
1736 return NULL;
1737
1738 /* find first available group of type news */
1739 do {
1740 tr = str_trim(ptr);
1741 group = group_find(tr, TRUE);
1742 if (group && group->type == GROUP_TYPE_NEWS)
1743 found = TRUE;
1744 } while (!found && (ptr = strtok(NULL, ",")) != NULL);
1745
1746 return found ? group : NULL;
1747}
1748#endif /* NNTP_ABLE */
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
static t_openartinfo * art
Definition: cook.c:78
#define DEBUG_NNTP
Definition: debug.h:47
int this_resp
Definition: page.c:71
constext txt_quit_no_write[]
Definition: lang.c:766
constext txt_unsubscribe_pattern[]
Definition: lang.c:911
@ HIST_GOTO_GROUP
Definition: extern.h:1561
@ HIST_MOVE_GROUP
Definition: extern.h:1565
@ HIST_OTHER
Definition: extern.h:1558
@ HIST_MESSAGE_ID
Definition: extern.h:1564
@ HIST_POST_NEWSGROUPS
Definition: extern.h:1567
constext txt_no_newsgroups[]
Definition: lang.c:691
t_function last_search
Definition: init.c:117
int verbose
Definition: init.c:154
constext txt_moving[]
Definition: lang.c:663
constext txt_subscribing[]
Definition: lang.c:864
constext txt_bad_command[]
Definition: lang.c:112
constext txt_lookup_func_not_nntp[]
Definition: lang.c:611
constext txt_grpdesc_disabled[]
Definition: lang.c:327
constext txt_cannot_post[]
Definition: lang.c:130
constext txt_quit[]
Definition: lang.c:760
int * my_group
Definition: memory.c:64
constext txt_not_exist[]
Definition: lang.c:709
int NOTESLINES
Definition: signal.c:111
constext txt_not_in_active_file[]
Definition: lang.c:710
constext txt_no_groups[]
Definition: lang.c:684
t_bool show_description
Definition: init.c:153
constext txt_select_group[]
Definition: lang.c:851
t_openartinfo pgart
Definition: page.c:63
constext txt_yanked_groups[]
Definition: lang.c:1001
struct t_capabilities nntp_caps
Definition: init.c:513
int last_resp
Definition: page.c:70
char * nntp_server
Definition: nntplib.c:28
t_menu grpmenu
Definition: group.c:83
int tin_errno
Definition: read.c:59
t_bool read_saved_news
Definition: init.c:152
constext txt_remove_bogus[]
Definition: lang.c:788
constext txt_subscribed_num_groups[]
Definition: lang.c:862
constext txt_art_unavailable[]
Definition: lang.c:69
struct t_article * arts
Definition: memory.c:69
constext txt_group_aliased[]
Definition: lang.c:319
constext txt_unsubscribing[]
Definition: lang.c:919
constext txt_no_groups_to_read[]
Definition: lang.c:685
int art_mark_width
Definition: init.c:118
constext txt_newsgroup_position[]
Definition: lang.c:671
constext txt_reading_groups[]
Definition: lang.c:777
constext txt_reading_news_newsrc_file[]
Definition: lang.c:781
constext txt_group[]
Definition: lang.c:1230
constext txt_no_description[]
Definition: lang.c:681
constext txt_show_unread[]
Definition: lang.c:815
int num_of_tagged_arts
Definition: tags.c:47
constext txt_unsubscribed_to[]
Definition: lang.c:918
int filter_file_offset
Definition: filter.c:93
char filter_file[PATH_LEN]
Definition: init.c:89
constext txt_no_arts[]
Definition: lang.c:678
t_bool range_active
Definition: init.c:148
char cvers[LEN]
Definition: init.c:70
int signal_context
Definition: signal.c:105
int mark_offset
Definition: screen.c:48
constext txt_subscribed_to[]
Definition: lang.c:863
constext txt_post_newsgroups[]
Definition: lang.c:739
constext txt_reset_newsrc[]
Definition: lang.c:795
constext txt_info_no_write[]
Definition: lang.c:558
constext txt_newsgroup[]
Definition: lang.c:669
t_menu * currmenu
Definition: init.c:166
char newsrc[PATH_LEN]
Definition: init.c:96
constext txt_no_prev_search[]
Definition: lang.c:694
constext txt_end_of_groups[]
Definition: lang.c:168
int cCOLS
Definition: curses.c:53
constext txt_lookup_func_not_available[]
Definition: lang.c:610
constext txt_info_not_subscribed[]
Definition: lang.c:556
constext txt_unsubscribed_num_groups[]
Definition: lang.c:917
t_bool can_post
Definition: nntplib.c:32
char local_config_file[PATH_LEN]
Definition: init.c:84
constext txt_subscribe_pattern[]
Definition: lang.c:861
constext txt_save_config[]
Definition: lang.c:820
constext txt_yanked_sub_groups[]
Definition: lang.c:1003
constext txt_group_selection[]
Definition: lang.c:325
constext txt_group_select_com[]
Definition: lang.c:324
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_yanked_none[]
Definition: lang.c:1002
struct t_screen * screen
Definition: screen.c:51
t_bool no_write
Definition: init.c:145
constext txt_mark_group_read[]
Definition: lang.c:635
struct t_cmdlineopts cmdline
Definition: init.c:190
constext txt_enter_message_id[]
Definition: lang.c:175
t_bool read_news_via_nntp
Definition: init.c:151
int num_active
Definition: memory.c:51
constext txt_no_match[]
Definition: lang.c:689
struct t_group * active
Definition: memory.c:66
constext txt_skipping_newgroups[]
Definition: lang.c:856
t_bool force_reread_active_file
Definition: active.c:62
struct keylist select_keys
Definition: keymap.c:89
t_function global_mouse_action(t_function(*left_action)(void), t_function(*right_action)(void))
Definition: global.c:321
#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
@ GLOBAL_SCROLL_UP
Definition: keymap.h:214
@ 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
@ GLOBAL_SET_RANGE
Definition: keymap.h:221
@ DIGIT_6
Definition: keymap.h:156
@ GLOBAL_POST
Definition: keymap.h:203
@ GLOBAL_SEARCH_SUBJECT_FORWARD
Definition: keymap.h:220
@ SELECT_MOVE_GROUP
Definition: keymap.h:345
@ GLOBAL_LINE_DOWN
Definition: keymap.h:194
@ SELECT_SYNC_WITH_ACTIVE
Definition: keymap.h:351
@ GLOBAL_SCROLL_DOWN
Definition: keymap.h:213
@ GLOBAL_HELP
Definition: keymap.h:191
@ GLOBAL_LOOKUP_MESSAGEID
Definition: keymap.h:196
@ SELECT_UNSUBSCRIBE_PATTERN
Definition: keymap.h:355
@ SELECT_ENTER_GROUP
Definition: keymap.h:341
@ DIGIT_2
Definition: keymap.h:152
@ SELECT_NEXT_UNREAD_GROUP
Definition: keymap.h:346
@ GLOBAL_SEARCH_SUBJECT_BACKWARD
Definition: keymap.h:219
@ SELECT_GOTO
Definition: keymap.h:343
@ GLOBAL_TOGGLE_INVERSE_VIDEO
Definition: keymap.h:230
@ GLOBAL_TOGGLE_HELP_DISPLAY
Definition: keymap.h:228
@ SELECT_MARK_GROUP_UNREAD
Definition: keymap.h:344
@ GLOBAL_VERSION
Definition: keymap.h:231
@ SELECT_SUBSCRIBE_PATTERN
Definition: keymap.h:350
@ DIGIT_9
Definition: keymap.h:159
@ GLOBAL_POSTPONED
Definition: keymap.h:204
@ SELECT_RESET_NEWSRC
Definition: keymap.h:347
@ SELECT_SORT_ACTIVE
Definition: keymap.h:348
@ SELECT_SUBSCRIBE
Definition: keymap.h:349
@ GLOBAL_PAGE_DOWN
Definition: keymap.h:200
@ SELECT_TOGGLE_DESCRIPTIONS
Definition: keymap.h:352
@ SELECT_ENTER_NEXT_UNREAD_GROUP
Definition: keymap.h:342
@ GLOBAL_EDIT_FILTER
Definition: keymap.h:189
@ GLOBAL_ABORT
Definition: keymap.h:186
@ GLOBAL_SEARCH_REPEAT
Definition: keymap.h:216
@ GLOBAL_QUIT
Definition: keymap.h:210
@ SELECT_TOGGLE_READ_DISPLAY
Definition: keymap.h:353
@ 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
@ GLOBAL_LAST_PAGE
Definition: keymap.h:192
@ DIGIT_1
Definition: keymap.h:151
@ GLOBAL_LINE_UP
Definition: keymap.h:195
@ DIGIT_4
Definition: keymap.h:154
@ GLOBAL_OPTION_MENU
Definition: keymap.h:199
@ DIGIT_5
Definition: keymap.h:155
@ GLOBAL_QUIT_TIN
Definition: keymap.h:211
@ SELECT_UNSUBSCRIBE
Definition: keymap.h:354
@ CATCHUP_NEXT_UNREAD
Definition: keymap.h:170
@ SELECT_YANK_ACTIVE
Definition: keymap.h:357
@ SELECT_QUIT_NO_WRITE
Definition: keymap.h:356
@ CATCHUP
Definition: keymap.h:169
@ GLOBAL_BUGREPORT
Definition: keymap.h:187
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 NNTP_STRLEN
Definition: nntplib.h:155
#define OK_HDR
Definition: nntplib.h:106
@ CAPABILITIES
Definition: nntplib.h:171
#define ERR_NOART
Definition: nntplib.h:133
#define OK_HEAD
Definition: nntplib.h:100
struct t_group * group_find(const char *group_name, t_bool ignore_case)
Definition: list.c:154
void scroll_down(void)
Definition: global.c:252
void move_to_item(int n)
Definition: global.c:227
void grp_mark_read(struct t_group *group, struct t_article *art)
Definition: newsrc.c:724
void page_up(void)
Definition: global.c:130
t_bool reread_active_after_posting(void)
Definition: post.c:5111
void prompt_item_num(int ch, const char *prompt)
Definition: global.c:200
void draw_arrow_mark(int line)
Definition: screen.c:352
t_bool pickup_postponed_articles(t_bool ask, t_bool all)
Definition: post.c:2904
t_bool pos_group_in_newsrc(struct t_group *group, int pos)
Definition: newsrc.c:1215
struct t_msgid * find_msgid(const char *msgid)
Definition: refs.c:351
void show_mini_help(int level)
Definition: help.c:798
void group_rehash(t_bool yanked_out)
Definition: list.c:232
char * prompt_string_default(const char *prompt, char *def, const char *failtext, int history)
Definition: prompt.c:587
char * str_trim(char *string)
Definition: string.c:539
t_bool read_filter_file(const char *file)
Definition: filter.c:308
void show_help_page(const int level, const char *title)
Definition: help.c:734
int atoi(const char *s)
int search_active(t_bool forward, t_bool repeat)
Definition: search.c:278
t_bool index_group(struct t_group *group)
Definition: art.c:396
t_bool set_range(int level, int min, int max, int curr)
Definition: tags.c:214
int find_group_index(const char *group, t_bool ignore_case)
Definition: list.c:106
void grp_mark_unread(struct t_group *group)
Definition: newsrc.c:750
void error_message(unsigned int sdelay, const char *fmt,...)
Definition: screen.c:224
_Noreturn void tin_done(int ret, const char *fmt,...)
Definition: misc.c:562
void delete_group(char *group)
Definition: newsrc.c:672
void parse_format_string(const char *fmtstr, struct t_fmt *fmt)
Definition: string.c:1437
t_bool match_group_list(const char *group, const char *group_list)
Definition: active.c:1117
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
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 prompt_string(const char *prompt, char *buf, int which_hist)
Definition: prompt.c:93
char group_flag(char ch)
Definition: active.c:1247
void info_message(const char *fmt,...)
Definition: screen.c:102
void toggle_inverse_video(void)
Definition: misc.c:1050
void write_config_file(char *file)
Definition: config.c:932
char * convert_to_printable(char *buf, t_bool keep_tab)
Definition: charset.c:394
void show_inverse_video_status(void)
Definition: misc.c:1065
void spin_cursor(void)
Definition: screen.c:497
void reset_newsrc(void)
Definition: newsrc.c:627
int show_article_by_msgid(char *messageid)
void erase_arrow(void)
Definition: screen.c:404
void toggle_mini_help(int level)
Definition: help.c:1078
int show_page(struct t_group *group, int start_respnum, int *threadnum)
Definition: page.c:306
char * my_strdup(const char *str)
Definition: string.c:139
void subscribe(struct t_group *group, int sub_state, t_bool get_info)
Definition: newsrc.c:553
t_bool resync_active_file(void)
Definition: active.c:102
t_bool invoke_editor(const char *filename, int lineno, struct t_group *group)
Definition: misc.c:377
signed long int read_newsrc(char *newsrc_file, t_bool allgroups)
Definition: newsrc.c:83
void art_close(t_openartinfo *artinfo)
Definition: rfc2046.c:1623
void Raw(int state)
Definition: curses.c:624
void read_descriptions(t_bool verb)
Definition: mail.c:441
t_bool post_hist_page(void)
Definition: post.c:519
void page_down(void)
Definition: global.c:155
void move_up(void)
Definition: global.c:81
void set_xclick_off(void)
Definition: curses.c:703
void move_down(void)
Definition: global.c:110
int group_page(struct t_group *group)
Definition: group.c:153
void clear_message(void)
Definition: screen.c:283
char * tin_fgets(FILE *fp, t_bool header)
Definition: read.c:317
int which_thread(int n)
Definition: thread.c:1073
signed long int write_newsrc(void)
Definition: newsrc.c:201
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
t_bool post_article(const char *groupname)
Definition: post.c:2994
char * sized_message(char **result, const char *format, const char *subject)
Definition: prompt.c:675
void mark_screen(int screen_row, int screen_col, const char *value)
Definition: group.c:1147
void show_title(const char *title)
Definition: screen.c:457
int strcasecmp(const char *p, const char *q)
Definition: string.c:475
void top_of_list(void)
Definition: global.c:182
int prompt_yn(const char *prompt, t_bool default_answer)
Definition: prompt.c:165
char * fmt_string(const char *fmt,...)
Definition: string.c:1386
t_bool need_reread_active_file(void)
Definition: active.c:88
char * tin_ltoa(t_artnum value, int digits)
Definition: string.c:80
void bug_report(void)
Definition: global.c:430
state
Definition: save.c:56
static void sync_active_file(void)
Definition: select.c:893
static t_bool yanked_out
Definition: select.c:60
static void subscribe_pattern(const char *prompt, const char *message, const char *result, t_bool state)
Definition: select.c:1371
static void catchup_group(struct t_group *group, t_bool goto_next_unread_group)
Definition: select.c:1157
int add_my_group(const char *group, t_bool add, t_bool ignore_case)
Definition: select.c:1068
static void draw_group_arrow(void)
Definition: select.c:875
static void select_read_group(void)
Definition: select.c:1442
int choose_new_group(void)
Definition: select.c:1012
static t_function select_left(void)
Definition: select.c:92
static void sort_active_file(void)
Definition: select.c:997
static t_function select_right(void)
Definition: select.c:100
t_menu selmenu
Definition: select.c:84
static void yank_active_file(void)
Definition: select.c:903
static int reposition_group(struct t_group *group, int default_num)
Definition: select.c:1093
static void select_done(void)
Definition: select.c:1428
static int save_restore_curr_group(t_bool saving)
Definition: select.c:959
void show_selection_page(void)
Definition: select.c:595
static void read_groups(void)
Definition: select.c:1246
static t_bool pos_next_unread_group(t_bool redraw)
Definition: select.c:1202
static int active_comp(t_comptype p1, t_comptype p2)
Definition: select.c:942
int skip_newgroups(void)
Definition: select.c:1045
static int flags_offset
Definition: select.c:87
void selection_page(int start_groupnum, int num_cmd_line_groups)
Definition: select.c:108
static int groupname_len
Definition: select.c:86
static int ucnt_offset
Definition: select.c:88
static struct t_fmt sel_fmt
Definition: select.c:49
static void build_gline(int i)
Definition: select.c:677
void toggle_my_groups(const char *group)
Definition: select.c:1292
static void select_quit(void)
Definition: select.c:1418
void(* func)(SIG_ARGS)
Definition: signal.c:176
const char * name
Definition: signal.c:117
Definition: tin.h:1533
unsigned show_only_unread_arts
Definition: tin.h:1674
const char * hdr_cmd
Definition: nntplib.h:207
t_bool xpat
Definition: nntplib.h:205
enum extension_type type
Definition: nntplib.h:187
unsigned int args
Definition: tin.h:1503
int getart_limit
Definition: tin.h:1499
int default_move_group
Definition: tinrc.h:145
t_bool strip_blanks
Definition: tinrc.h:249
t_bool draw_arrow
Definition: tinrc.h:223
int strip_bogus
Definition: tinrc.h:163
char default_post_newsgroups[HEADER_LEN]
Definition: tinrc.h:87
t_bool abbreviate_groupname
Definition: tinrc.h:211
t_bool cache_overview_files
Definition: tinrc.h:220
t_bool show_only_unread_groups
Definition: tinrc.h:244
char default_goto_group[HEADER_LEN]
Definition: tinrc.h:81
char select_format[LEN]
Definition: tinrc.h:282
char art_marked_inrange
Definition: tinrc.h:71
t_bool info_in_last_line
Definition: tinrc.h:226
int getart_limit
Definition: tinrc.h:147
Definition: tin.h:1853
size_t len_grpname_max
Definition: tin.h:1862
t_bool d_before_u
Definition: tin.h:1877
size_t len_grpdesc
Definition: tin.h:1858
t_bool g_before_u
Definition: tin.h:1878
size_t len_linenumber
Definition: tin.h:1864
size_t flags_offset
Definition: tin.h:1871
size_t len_grpname
Definition: tin.h:1860
t_bool g_before_f
Definition: tin.h:1876
t_bool show_grpdesc
Definition: tin.h:1874
t_bool d_before_f
Definition: tin.h:1875
char str[1024]
Definition: tin.h:1854
size_t ucnt_offset
Definition: tin.h:1873
size_t len_grpname_dsc
Definition: tin.h:1861
size_t len_ucnt
Definition: tin.h:1870
Definition: tin.h:1816
t_bool newgroup
Definition: tin.h:1830
char * description
Definition: tin.h:1819
t_bool inrange
Definition: tin.h:1826
t_bool bogus
Definition: tin.h:1831
char moderated
Definition: tin.h:1821
t_artnum xmin
Definition: tin.h:1824
t_artnum xmax
Definition: tin.h:1823
struct t_attribute * attribute
Definition: tin.h:1834
unsigned int type
Definition: tin.h:1825
t_bool subscribed
Definition: tin.h:1829
char * name
Definition: tin.h:1817
struct t_newsrc newsrc
Definition: tin.h:1833
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
int article
Definition: tin.h:1514
t_artnum num_unread
Definition: tin.h:1806
char * col
Definition: tin.h:1974
#define cCRLF
Definition: tcurses.h:156
#define HpGlitch(func)
Definition: tcurses.h:182
#define WriteLine(row, buffer)
Definition: tcurses.h:180
#define my_retouch()
Definition: tcurses.h:179
#define TINRC_CONFIRM_ACTION
Definition: tin.h:958
#define PLURAL(x, y)
Definition: tin.h:1064
#define SUBSCRIBED
Definition: tin.h:1366
#define LEN
Definition: tin.h:860
#define my_group_find(x)
Definition: tin.h:2257
long t_artnum
Definition: tin.h:229
#define SUB_CHAR(x)
Definition: tin.h:1369
#define UNSUBSCRIBED
Definition: tin.h:1365
@ cSelect
Definition: tin.h:107
#define for_each_group(x)
Definition: tin.h:2259
#define STRCPY(dst, src)
Definition: tin.h:820
#define for_each_art(x)
Definition: tin.h:2260
#define CAST_BOOL(value)
Definition: tin.h:1588
#define INDEX2LNUM(i)
Definition: tin.h:1020
#define my_malloc(size)
Definition: tin.h:2245
#define EXIT_SUCCESS
Definition: tin.h:1298
#define SELECT_LEVEL
Definition: tin.h:1112
#define FreeIfNeeded(p)
Definition: tin.h:2252
#define INDEX2SNUM(i)
Definition: tin.h:1022
#define CURR_GROUP
Definition: tin.h:1054
#define _(Text)
Definition: tin.h:94
#define forever
Definition: tin.h:816
#define CMDLINE_GETART_LIMIT
Definition: tin.h:1102
#define snprintf
Definition: tin.h:2464
#define TINRC_CONFIRM_TO_QUIT
Definition: tin.h:959
#define FreeAndNull(p)
Definition: tin.h:2253
#define tin_sort
Definition: tin.h:2148
#define BOGUS_SHOW
Definition: tin.h:1212
#define GROUP_TYPE_NEWS
Definition: tin.h:1070
#define UNREAD_GROUP(i)
Definition: tin.h:1059
@ LOOKUP_ART_UNAVAIL
Definition: tin.h:1272
@ LOOKUP_UNAVAIL
Definition: tin.h:1270
@ LOOKUP_OK
Definition: tin.h:1267
@ LOOKUP_NO_LAST
Definition: tin.h:1273
@ LOOKUP_QUIT
Definition: tin.h:1269
@ LOOKUP_FAILED
Definition: tin.h:1268
#define INDEX_TOP
Definition: tin.h:1019
#define my_group_add(x, y)
Definition: tin.h:2258
#define T_CHAR_FMT
Definition: tin.h:884
@ 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_QUIT
Definition: tin.h:1286
@ GRP_ENTER
Definition: tin.h:1293
#define NEWSRC_LINE
Definition: tin.h:862
#define ART_UNAVAILABLE
Definition: tin.h:1348
#define FAKE_NNTP_FP
Definition: tin.h:458