"Fossies" - the Fresh Open Source Software Archive

Member "tin-2.6.2/src/main.c" (9 Dec 2022, 29579 Bytes) of package /linux/misc/tin-2.6.2.tar.xz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "main.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 2.6.1_vs_2.6.2.

    1 /*
    2  *  Project   : tin - a Usenet reader
    3  *  Module    : main.c
    4  *  Author    : I. Lea & R. Skrenta
    5  *  Created   : 1991-04-01
    6  *  Updated   : 2022-11-01
    7  *  Notes     :
    8  *
    9  * Copyright (c) 1991-2023 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 #ifndef VERSION_H
   48 #   include "version.h"
   49 #endif /* !VERSION_H */
   50 
   51 signed long int read_newsrc_lines = -1;
   52 
   53 static char **cmdargs;
   54 static int num_cmdargs;
   55 static int max_cmdargs;
   56 
   57 static t_bool catchup = FALSE;      /* mark all arts read in all subscribed groups */
   58 static t_bool update_index = FALSE;     /* update local overviews */
   59 static t_bool check_any_unread = FALSE; /* print/return status if any unread */
   60 static t_bool mail_news = FALSE;        /* mail all arts to specified user */
   61 static t_bool save_news = FALSE;        /* save all arts to savedir structure */
   62 static t_bool start_any_unread = FALSE; /* only start if unread news */
   63 
   64 
   65 /*
   66  * Local prototypes
   67  */
   68 static void create_mail_save_dirs(void);
   69 static void read_cmd_line_options(int argc, char *argv[]);
   70 static void show_intro_page(void);
   71 _Noreturn static void update_index_files(void);
   72 static void usage(char *theProgname);
   73 
   74 
   75 /*
   76  * OK lets start the ball rolling...
   77  */
   78 int
   79 main(
   80     int argc,
   81     char *argv[])
   82 {
   83     int count, start_groupnum;
   84     int num_cmd_line_groups = 0;
   85     t_bool tmp_no_write;
   86 
   87     cmd_line = TRUE;
   88 
   89     /* initialize locale support */
   90 #if defined(HAVE_SETLOCALE) && !defined(NO_LOCALE)
   91     if (setlocale(LC_ALL, "")) {
   92 #   ifdef ENABLE_NLS
   93         bindtextdomain(NLS_TEXTDOMAIN, LOCALEDIR);
   94         textdomain(NLS_TEXTDOMAIN);
   95 #   endif /* ENABLE_NLS */
   96     } else
   97         error_message(4, txt_error_locale);
   98 #endif /* HAVE_SETLOCALE && !NO_LOCALE */
   99 
  100     /*
  101      * determine local charset
  102      */
  103 #ifndef NO_LOCALE
  104     {
  105         const char *p;
  106 
  107         if ((p = tin_nl_langinfo(CODESET)) != NULL) {
  108             if (!strcasecmp(p, "ANSI_X3.4-1968"))
  109                 STRCPY(tinrc.mm_local_charset, "US-ASCII");
  110             else
  111                 STRCPY(tinrc.mm_local_charset, p);
  112         }
  113     }
  114 #endif /* !NO_LOCALE */
  115     /* always set a default value */
  116     if (!*tinrc.mm_local_charset)
  117         STRCPY(tinrc.mm_local_charset, "US-ASCII");
  118 
  119     set_signal_handlers();
  120 
  121     debug = 0;  /* debug OFF */
  122 
  123     tin_progname = my_malloc(strlen(argv[0]) + 1);
  124     base_name(argv[0], tin_progname);
  125 
  126 #ifdef NNTP_ONLY
  127     read_news_via_nntp = TRUE;
  128 #else
  129     /*
  130      * If called as rtin, read news remotely via NNTP
  131      */
  132     if (tin_progname[0] == 'r') {
  133 #   ifdef NNTP_ABLE
  134         read_news_via_nntp = TRUE;
  135 #   else
  136         error_message(2, _(txt_option_not_enabled), "-DNNTP_ABLE");
  137         free(tin_progname);
  138         giveup();
  139 #   endif /* NNTP_ABLE */
  140     }
  141 #endif /* NNTP_ONLY */
  142 
  143     /*
  144      * Set up initial array sizes, char *'s: homedir, newsrc, etc.
  145      */
  146     init_alloc();
  147     hash_init();
  148     init_selfinfo();
  149     init_group_hash();
  150 
  151     /*
  152      * Set cCOLS temporarily to trim (localized) messages
  153      * It does not matter if this value is greater than the actual
  154      * terminal size
  155      *
  156      * cCOLS will be set later to the real terminal width
  157      */
  158     cCOLS = 80;
  159 
  160     /*
  161      * Process envargs & command line options
  162      * These override the configured in values
  163      */
  164     read_cmd_line_options(argc, argv);
  165 
  166     /* preinit keybindings if interactive */
  167     if (!batch_mode)
  168         setup_default_keys();
  169 
  170     /*
  171      * Read user local & global config files
  172      * These override the compiled in defaults
  173      *
  174      * must be called before setup_screen()
  175      */
  176     read_config_file(global_config_file, TRUE);
  177     read_config_file(local_config_file, FALSE);
  178 
  179     tmp_no_write = no_write; /* keep no_write */
  180     no_write = TRUE;        /* don't allow any writing back during startup */
  181 
  182     if (!batch_mode) {
  183 #ifndef USE_CURSES
  184         if (!get_termcaps()) {
  185             error_message(2, _(txt_screen_init_failed), tin_progname);
  186             free_all_arrays();
  187             giveup();
  188         }
  189 #endif /* !USE_CURSES */
  190 
  191         /* exit early at -o without postponed articles */
  192         if (post_postponed_and_exit && !count_postponed_articles()) {
  193             no_write = TRUE;
  194             /* TODO: looks ugly */
  195             error_message(0, txt_info_nopostponed);
  196             tin_done(EXIT_SUCCESS, NULL);
  197         }
  198 
  199         /*
  200          * Init curses emulation
  201          */
  202         if (!InitScreen()) {
  203             error_message(2, _(txt_screen_init_failed), tin_progname);
  204             free_all_arrays();
  205             giveup();
  206         }
  207 
  208         EndInverse();
  209 
  210         /*
  211          * This depends on various things in tinrc
  212          */
  213         setup_screen();
  214     }
  215 
  216     if (!batch_mode || verbose) {
  217         if (!batch_mode && (cLINES < MIN_LINES_ON_TERMINAL || cCOLS < MIN_COLUMNS_ON_TERMINAL)) {
  218             ring_bell();
  219             tin_done(EXIT_FAILURE, _(txt_screen_too_small_exiting), tin_progname);
  220         }
  221         wait_message(0, "%s\n", cvers);
  222     }
  223 
  224     /*
  225      * Connect to nntp server?
  226      */
  227     if (!nntp_server || !*nntp_server)
  228         nntp_server = getserverbyfile(NNTP_SERVER_FILE);
  229 
  230     open_msglog(); /* depends on nntp_server */
  231 
  232     if (read_news_via_nntp && !read_saved_news) {
  233         if (use_nntps && tintls_init()) {
  234             tintls_exit();
  235             free_all_arrays();
  236             giveup();
  237         }
  238 
  239         if (nntp_open()) {
  240             nntp_close(FALSE);
  241             tintls_exit();
  242             free_all_arrays();
  243             giveup();
  244         }
  245     }
  246 
  247     read_server_config();
  248 
  249     /*
  250      * exit early - unfortunately we can't do that in read_cmd_line_options()
  251      * as nntp_caps.over_cmd is set in nntp_open()
  252      *
  253      * TODO: does the logic make sense? what
  254      * if (update_index && !nntp_caps.over_cmd && !tinrc.cache_overview_files)
  255      * no error message? why?
  256      */
  257     if (update_index && nntp_caps.over_cmd && !tinrc.cache_overview_files) {
  258         error_message(2, _(txt_batch_update_unavail), tin_progname, print_boolean(tinrc.cache_overview_files));
  259         free_all_arrays();
  260         giveup();
  261     }
  262 
  263     /*
  264      * Check if overview indexes contain Xref: lines
  265      */
  266 #ifdef NNTP_ABLE
  267     if ((read_news_via_nntp && nntp_caps.over_cmd) || !read_news_via_nntp)
  268 #endif /* NNTP_ABLE */
  269         xref_supported = overview_xref_support();
  270 
  271     /*
  272      * avoid empty regexp, we also need to do this in batch_mode
  273      * as read_overview() calls eat_re() which uses a regexp to
  274      * modify the subject *sigh*
  275      */
  276     postinit_regexp();
  277 
  278     if (!(batch_mode || post_postponed_and_exit)) {
  279         /*
  280          * Read user specific keybindings and input history
  281          */
  282         wait_message(0, _(txt_reading_keymap_file));
  283         read_keymap_file();
  284         read_input_history_file();
  285 
  286         /*
  287          * Load the mail & news active files into active[]
  288          *
  289          * create_save_active_file cannot write to active.save
  290          * if no_write != FALSE, so restore original value temporarily
  291          */
  292         if (read_saved_news) {
  293             no_write = tmp_no_write;
  294             create_save_active_file();
  295             no_write = TRUE;
  296         }
  297     }
  298 
  299 #ifdef HAVE_MH_MAIL_HANDLING
  300     read_mail_active_file();
  301 #endif /* HAVE_MH_MAIL_HANDLING */
  302 
  303     /*
  304      * Initialise active[] and add new newsgroups to start of my_group[]
  305      * also reads global/local attributes
  306      */
  307     selmenu.max = 0;
  308     /*
  309      * we need to restore the original no_write mode to be able to handle
  310      * $AUTOSUBSCRIBE groups
  311      */
  312     no_write = tmp_no_write;
  313     read_attributes_file(TRUE);
  314     read_attributes_file(FALSE);
  315     start_groupnum = read_news_active_file();
  316 #ifdef DEBUG
  317     if (debug & DEBUG_MISC)
  318         debug_print_active();
  319 #endif /* DEBUG */
  320 
  321     /*
  322      * Read in users filter preferences file. This has to be done before
  323      * quick post because the filters might be updated.
  324      */
  325     read_filter_file(filter_file);
  326 
  327     no_write = TRUE;
  328 #ifdef DEBUG
  329     if (debug & DEBUG_FILTER)
  330         debug_print_filters();
  331 #endif /* DEBUG */
  332 
  333     /*
  334      * Preloads active[] with command line groups. They will follow any
  335      * new newsgroups
  336      */
  337     if (!post_postponed_and_exit)
  338         num_cmd_line_groups = read_cmd_line_groups();
  339 
  340     /*
  341      * Quick post an article and exit if -w or -o specified
  342      */
  343     if (post_article_and_exit || post_postponed_and_exit) {
  344         no_write = tmp_no_write; /* restore original value */
  345         quick_post_article(post_postponed_and_exit, num_cmd_line_groups);
  346         wait_message(2, _(txt_exiting));
  347         no_write = TRUE; /* disable newsrc updates */
  348         tin_done(EXIT_SUCCESS, NULL);
  349     }
  350 
  351     /* TODO: replace hard coded key-name in txt_info_postponed */
  352     if ((count = count_postponed_articles()))
  353         wait_message(3, _(txt_info_postponed), count, PLURAL(count, txt_article));
  354 
  355     /*
  356      * Read text descriptions for mail and/or news groups
  357      */
  358     if (show_description && !batch_mode) {
  359         no_write = tmp_no_write; /* restore original value */
  360         read_descriptions(TRUE);
  361         no_write = TRUE; /* disable newsrc updates */
  362     }
  363 
  364     /* what about "if (!no_write)" here? */
  365     create_mail_save_dirs();
  366     if (created_rcdir) /* first start */
  367         write_config_file(local_config_file);
  368 
  369     if (!tmp_no_write)  /* do not (over)write oldnewsrc with -X */
  370         backup_newsrc();
  371 
  372     /*
  373      * Load my_groups[] from the .newsrc file. We append these groups to any
  374      * new newsgroups and command line newsgroups already loaded. Also does
  375      * auto-subscribe to groups specified in /usr/lib/news/subscriptions
  376      * locally or via NNTP if reading news remotely (LIST SUBSCRIPTIONS)
  377      */
  378     /*
  379      * TODO:
  380      * if (num_cmd_line_groups != 0 && check_any_unread)
  381      * don't read newsrc.
  382      * This makes -Z handle command line newsgroups. Test & document
  383      */
  384     read_newsrc_lines = read_newsrc(newsrc, FALSE);
  385     no_write = tmp_no_write; /* restore old value */
  386 
  387     /*
  388      * We have to show all groups with command line groups
  389      */
  390     if (num_cmd_line_groups)
  391         tinrc.show_only_unread_groups = FALSE;
  392     else
  393         toggle_my_groups(NULL);
  394 
  395     /*
  396      * Check/start if any new/unread articles
  397      */
  398     if (check_any_unread)
  399         tin_done(check_start_save_any_news(CHECK_ANY_NEWS, catchup), NULL);
  400 
  401     if (start_any_unread) {
  402         batch_mode = TRUE;          /* Suppress some unwanted on-screen garbage */
  403         if ((start_groupnum = check_start_save_any_news(START_ANY_NEWS, catchup)) == -1) {
  404             batch_mode = FALSE;
  405             tin_done(EXIT_SUCCESS, NULL);
  406         }
  407         batch_mode = FALSE;
  408     }
  409 
  410     /*
  411      * Mail any new articles to specified user
  412      * or
  413      * Save any new articles to savedir structure for later reading
  414      *
  415      * TODO: should we temporarily set
  416      *       getart_limit=-1,thread_articles=0,sort_article_type=0
  417      *       for speed reasons?
  418      */
  419     if (mail_news || save_news) {
  420         check_start_save_any_news(mail_news ? MAIL_ANY_NEWS : SAVE_ANY_NEWS, catchup);
  421         tin_done(EXIT_SUCCESS, NULL);
  422     }
  423 
  424     /*
  425      * Catchup newsrc file (-c option)
  426      */
  427     if (batch_mode && catchup && !update_index) {
  428         catchup_newsrc_file();
  429         tin_done(EXIT_SUCCESS, NULL);
  430     }
  431 
  432     /*
  433      * Update index files (-u option), also does catchup if requested
  434      */
  435     if (update_index)
  436         update_index_files();
  437 
  438     /*
  439      * the code below this point can't be reached in batch mode
  440      */
  441 
  442     /*
  443      * If first time print welcome screen
  444      */
  445     if (created_rcdir)
  446         show_intro_page();
  447 
  448 #ifdef XFACE_ABLE
  449     if (tinrc.use_slrnface && !batch_mode)
  450         slrnface_start();
  451 #endif /* XFACE_ABLE */
  452 
  453 #ifdef USE_CURSES
  454     /* Turn scrolling off now the startup messages have been displayed */
  455     scrollok(stdscr, FALSE);
  456 #endif /* USE_CURSES */
  457 
  458     /*
  459      * Work loop
  460      */
  461     selection_page(start_groupnum, num_cmd_line_groups);
  462     /* NOTREACHED */
  463     return 0;
  464 }
  465 
  466 
  467 /*
  468  * process command line options
  469  * [01235789beEFijJKLOtyY] are unused
  470  * [W] is reserved
  471  * [BCPU] have been in use at some time, but now are unused:
  472  *   B BBS mode (M_AMIGA only)
  473  *   C count articles
  474  *   P purge group index files of articles that no longer exist
  475  *   U update index files in background
  476  */
  477 #define OPTIONS "46aAcdD:f:g:G:hHI:klm:M:nNop:qQrRs:STuvVwxXzZ"
  478 
  479 static void
  480 read_cmd_line_options(
  481     int argc,
  482     char *argv[])
  483 {
  484     int ch;
  485     t_bool newsrc_set = FALSE;
  486 
  487     envargs(&argc, &argv, "TINRC");
  488 
  489     while ((ch = getopt(argc, argv, OPTIONS)) != -1) {
  490         switch (ch) {
  491 
  492             case '4':
  493 #if defined(NNTP_ABLE) && defined(INET6)
  494                 force_ipv4 = TRUE;
  495                 read_news_via_nntp = TRUE;
  496 #else
  497 #   ifdef NNTP_ABLE
  498                 error_message(2, _(txt_option_not_enabled), "-DENABLE_IPV6");
  499 #   else
  500                 error_message(2, _(txt_option_not_enabled), "-DNNTP_ABLE");
  501 #   endif /* NNTP_ABLE */
  502                 free_all_arrays();
  503                 giveup();
  504                 /* keep lint quiet: */
  505                 /* NOTREACHED */
  506 #endif /* NNTP_ABLE && INET6 */
  507                 break;
  508 
  509             case '6':
  510 #if defined(NNTP_ABLE) && defined(INET6)
  511                 force_ipv6 = TRUE;
  512                 read_news_via_nntp = TRUE;
  513 #   else
  514 #   ifdef NNTP_ABLE
  515                 error_message(2, _(txt_option_not_enabled), "-DENABLE_IPV6");
  516 #   else
  517                 error_message(2, _(txt_option_not_enabled), "-DNNTP_ABLE");
  518 #   endif /* NNTP_ABLE */
  519                 free_all_arrays();
  520                 giveup();
  521                 /* keep lint quiet: */
  522                 /* NOTREACHED */
  523 #endif /* NNTP_ABLE && INET6 */
  524                 break;
  525 
  526             case 'a':
  527 #ifdef HAVE_COLOR
  528                 cmdline.args |= CMDLINE_USE_COLOR;
  529 #else
  530                 error_message(2, _(txt_option_not_enabled), "-DHAVE_COLOR");
  531                 free_all_arrays();
  532                 giveup();
  533                 /* keep lint quiet: */
  534                 /* NOTREACHED */
  535 #endif /* HAVE_COLOR */
  536                 break;
  537 
  538             case 'A':
  539 #ifdef NNTP_ABLE
  540                 force_auth_on_conn_open = TRUE;
  541                 read_news_via_nntp = TRUE;
  542 #else
  543                 error_message(2, _(txt_option_not_enabled), "-DNNTP_ABLE");
  544                 free_all_arrays();
  545                 giveup();
  546                 /* keep lint quiet: */
  547                 /* NOTREACHED */
  548 #endif /* NNTP_ABLE */
  549                 break;
  550 
  551             case 'c':
  552                 batch_mode = TRUE;
  553                 catchup = TRUE;
  554                 break;
  555 
  556             case 'd':
  557                 show_description = FALSE;
  558                 break;
  559 
  560             case 'D':       /* debug mode */
  561 #ifdef DEBUG
  562                 debug = atoi(optarg) & 0xff;
  563                 debug_delete_files();
  564 #else
  565                 error_message(2, _(txt_option_not_enabled), "-DDEBUG");
  566                 free_all_arrays();
  567                 giveup();
  568                 /* keep lint quiet: */
  569                 /* NOTREACHED */
  570 #endif /* DEBUG */
  571                 break;
  572 
  573             case 'f':   /* newsrc file */
  574                 my_strncpy(newsrc, optarg, sizeof(newsrc) - 1);
  575                 newsrc_set = TRUE;
  576                 break;
  577 
  578             case 'G':
  579                 cmdline.getart_limit = atoi(optarg);
  580                 cmdline.args |= CMDLINE_GETART_LIMIT;
  581                 break;
  582 
  583             case 'g':   /* select alternative NNTP-server, implies -r */
  584 #ifdef NNTP_ABLE
  585                 my_strncpy(cmdline.nntpserver, optarg, sizeof(cmdline.nntpserver) - 1);
  586                 cmdline.args |= CMDLINE_NNTPSERVER;
  587                 read_news_via_nntp = TRUE;
  588 #else
  589                 error_message(2, _(txt_option_not_enabled), "-DNNTP_ABLE");
  590                 free_all_arrays();
  591                 giveup();
  592                 /* keep lint quiet: */
  593                 /* NOTREACHED */
  594 #endif /* NNTP_ABLE */
  595                 break;
  596 
  597             case 'H':
  598                 show_intro_page();
  599                 free_all_arrays();
  600                 exit(EXIT_SUCCESS);
  601                 /* keep lint quiet: */
  602                 /* FALLTHROUGH */
  603 
  604             case 'I':
  605                 joinpath(index_newsdir, sizeof(index_newsdir), optarg, INDEX_NEWSDIR);
  606                 break;
  607 
  608             case 'k':
  609 #ifdef NNTPS_ABLE
  610                 insecure_nntps = TRUE;
  611                 use_nntps = TRUE;
  612 #else
  613                 error_message(2, _(txt_option_not_enabled), "--with-nntps");
  614                 free_all_arrays();
  615                 giveup();
  616                 /* keep lint quiet: */
  617                 /* NOTREACHED */
  618 #endif /* NNTPS_ABLE */
  619                 break;
  620 
  621             case 'l':
  622                 list_active = TRUE;
  623                 break;
  624 
  625             case 'm':
  626                 my_strncpy(cmdline.maildir, optarg, sizeof(cmdline.maildir) - 1);
  627                 cmdline.args |= CMDLINE_MAILDIR;
  628                 break;
  629 
  630             case 'M':   /* mail new news to specified user */
  631                 my_strncpy(mail_news_user, optarg, sizeof(mail_news_user) - 1);
  632                 mail_news = TRUE;
  633                 batch_mode = TRUE;
  634                 break;
  635 
  636             case 'n':
  637                 newsrc_active = TRUE;
  638                 break;
  639 
  640             case 'N':   /* mail new news to your posts */
  641                 my_strncpy(mail_news_user, userid, sizeof(mail_news_user) - 1);
  642                 mail_news = TRUE;
  643                 batch_mode = TRUE;
  644                 break;
  645 
  646             case 'o':   /* post postponed articles & exit */
  647 #ifndef NO_POSTING
  648                 /*
  649                  * TODO: autoposting currently does some screen output, so we
  650                  *       can't set batch_mode
  651                  */
  652                 post_postponed_and_exit = TRUE;
  653                 check_for_new_newsgroups = FALSE;
  654 #else
  655                 error_message(2, _(txt_option_not_enabled), "-UNO_POSTING");
  656                 free_all_arrays();
  657                 giveup();
  658                 /* keep lint quiet: */
  659                 /* NOTREACHED */
  660 #endif /* !NO_POSTING */
  661                 break;
  662 
  663             case 'p': /* implies -r */
  664 #ifdef NNTP_ABLE
  665                 read_news_via_nntp = TRUE;
  666                 if (atoi(optarg) != 0)
  667                     nntp_tcp_port = (unsigned short) atoi(optarg);
  668 #else
  669                 error_message(2, _(txt_option_not_enabled), "-DNNTP_ABLE");
  670                 free_all_arrays();
  671                 giveup();
  672                 /* keep lint quiet: */
  673                 /* NOTREACHED */
  674 #endif /* NNTP_ABLE */
  675                 break;
  676 
  677             case 'q':
  678                 check_for_new_newsgroups = FALSE;
  679                 break;
  680 
  681             case 'Q':
  682                 newsrc_active = TRUE;
  683                 check_for_new_newsgroups = FALSE;
  684                 show_description = FALSE;
  685                 break;
  686 
  687             case 'r':   /* read news remotely from default NNTP server */
  688 #ifdef NNTP_ABLE
  689                 read_news_via_nntp = TRUE;
  690 #else
  691                 error_message(2, _(txt_option_not_enabled), "-DNNTP_ABLE");
  692                 free_all_arrays();
  693                 giveup();
  694                 /* keep lint quiet: */
  695                 /* NOTREACHED */
  696 #endif /* NNTP_ABLE */
  697                 break;
  698 
  699             case 'R':   /* read news saved by -S option */
  700                 read_saved_news = TRUE;
  701                 list_active = TRUE;
  702                 newsrc_active = FALSE;
  703                 check_for_new_newsgroups = FALSE;
  704                 my_strncpy(news_active_file, save_active_file, sizeof(news_active_file) - 1);
  705                 break;
  706 
  707             case 's':
  708                 my_strncpy(cmdline.savedir, optarg, sizeof(cmdline.savedir) - 1);
  709                 cmdline.args |= CMDLINE_SAVEDIR;
  710                 break;
  711 
  712             case 'S':   /* save new news to dir structure */
  713                 save_news = TRUE;
  714                 batch_mode = TRUE;
  715                 break;
  716 
  717             case 'T':
  718 #ifdef NNTPS_ABLE
  719                 use_nntps = TRUE;
  720 #else
  721                 error_message(2, _(txt_option_not_enabled), "--with-nntps");
  722                 free_all_arrays();
  723                 giveup();
  724                 /* keep lint quiet: */
  725                 /* NOTREACHED */
  726 #endif /* NNTPS_ABLE */
  727                 break;
  728 
  729             case 'u':   /* update index files */
  730                 batch_mode = TRUE;
  731                 update_index = TRUE;
  732                 break;
  733 
  734             case 'v':   /* verbose mode, can be used multiple times */
  735                 verbose++;
  736                 break;
  737 
  738             case 'V':
  739                 tin_version_info(stderr);
  740                 free_all_arrays();
  741                 exit(EXIT_SUCCESS);
  742                 /* keep lint quiet: */
  743                 /* FALLTHROUGH */
  744 
  745             case 'w':   /* post article & exit */
  746 #ifndef NO_POSTING
  747                 post_article_and_exit = TRUE;
  748                 check_for_new_newsgroups = FALSE;
  749 #else
  750                 error_message(2, _(txt_option_not_enabled), "-UNO_POSTING");
  751                 free_all_arrays();
  752                 giveup();
  753                 /* keep lint quiet: */
  754                 /* NOTREACHED */
  755 #endif /* !NO_POSTING */
  756                 break;
  757 
  758 #if 0
  759             case 'W':   /* reserved according to SUSV3 XDB Utility Syntax Guidelines, Guideline 3 */
  760                 break;
  761 #endif /* 0 */
  762 
  763             case 'x':   /* enter no_posting mode */
  764                 force_no_post = TRUE;
  765                 break;
  766 
  767             case 'X':   /* don't save ~/.newsrc on exit */
  768                 no_write = TRUE;
  769                 break;
  770 
  771             case 'z':
  772                 start_any_unread = TRUE;
  773                 break;
  774 
  775             case 'Z':
  776                 check_any_unread = TRUE;
  777                 batch_mode = TRUE;
  778                 break;
  779 
  780             case 'h':
  781             case '?':
  782             default:
  783                 usage(tin_progname);
  784                 free_all_arrays();
  785                 exit(EXIT_SUCCESS);
  786         }
  787     }
  788 
  789 #ifdef NNTP_ABLE
  790     if (nntp_tcp_port == 0) {
  791 #ifdef NNTPS_ABLE
  792         if (use_nntps)
  793             nntp_tcp_port = nntps_tcp_default_port;
  794         else
  795 #endif /* NNTPS_ABLE */
  796             nntp_tcp_port = nntp_tcp_default_port;
  797     }
  798 #endif /* NNTP_ABLE*/
  799 
  800     cmdargs = argv;
  801     num_cmdargs = optind;
  802     max_cmdargs = argc;
  803     if (!newsrc_set) {
  804         if (read_news_via_nntp) {
  805             nntp_server = getserverbyfile(NNTP_SERVER_FILE);
  806             get_newsrcname(newsrc, sizeof(newsrc), nntp_server);
  807         } else {
  808 #if defined(HAVE_SYS_UTSNAME_H) && defined(HAVE_UNAME)
  809             struct utsname uts;
  810             (void) uname(&uts);
  811             get_newsrcname(newsrc, sizeof(newsrc), uts.nodename);
  812 #else
  813             char nodenamebuf[256]; /* SUSv2 limit; better use HOST_NAME_MAX */
  814 #   ifdef HAVE_GETHOSTNAME
  815             (void) gethostname(nodenamebuf, sizeof(nodenamebuf));
  816 #   endif /* HAVE_GETHOSTNAME */
  817             get_newsrcname(newsrc, sizeof(newsrc), nodenamebuf);
  818 #endif /* HAVE_SYS_UTSNAME_H && HAVE_UNAME */
  819         }
  820     }
  821 
  822     /*
  823      * Sort out option conflicts
  824      */
  825     if (!batch_mode) {
  826         if (catchup) {
  827             wait_message(2, _(txt_useful_with_batch_mode), "-c");
  828             catchup = FALSE;
  829         }
  830     } else {
  831         if (read_saved_news) {
  832             wait_message(2, _(txt_useful_without_batch_mode), "-R");
  833             read_saved_news = FALSE;
  834         }
  835     }
  836     if (!(batch_mode || debug)) {
  837         if (verbose) {
  838             wait_message(2, _(txt_useful_with_batch_or_debug_mode), "-v");
  839             verbose = FALSE;
  840         }
  841     }
  842     /*
  843      * TODO: also disallow
  844      *       -NM
  845      *       -oN, -oM (at this stage we no longer know if -N or -M was given)
  846      *       -wN, -wM (at this stage we no longer know if -N or -M was given)
  847      */
  848     if (post_postponed_and_exit && force_no_post) {
  849         wait_message(2, _(txt_useless_combination), "-o", "-x", "-x");
  850         force_no_post = FALSE;
  851     }
  852     if (post_postponed_and_exit && start_any_unread) {
  853         wait_message(2, _(txt_useless_combination), "-o", "-z", "-z");
  854         start_any_unread = FALSE;
  855     }
  856     if (post_postponed_and_exit && post_article_and_exit) {
  857         wait_message(2, _(txt_useless_combination), "-o", "-w", "-w");
  858         post_article_and_exit = FALSE;
  859     }
  860     if (post_article_and_exit && force_no_post) {
  861         wait_message(2, _(txt_useless_combination), "-w", "-x", "-x");
  862         force_no_post = FALSE;
  863     }
  864     if (post_article_and_exit && start_any_unread) {
  865         wait_message(2, _(txt_useless_combination), "-w", "-z", "-z");
  866         start_any_unread = FALSE;
  867     }
  868     if (catchup && start_any_unread) {
  869         wait_message(2, _(txt_useless_combination), "-c", "-z", "-c");
  870         catchup = FALSE;
  871     }
  872     if (catchup && no_write) {
  873         wait_message(2, _(txt_useless_combination), "-c", "-X", "-c");
  874         catchup = FALSE;
  875     }
  876     if (catchup && check_any_unread) {
  877         wait_message(2, _(txt_useless_combination), "-c", "-Z", "-c");
  878         catchup = FALSE;
  879     }
  880     if (newsrc_active && read_saved_news) {
  881         wait_message(2, _(txt_useless_combination), "-n", "-R", "-n");
  882         newsrc_active = read_news_via_nntp = FALSE;
  883     }
  884     if (start_any_unread && save_news) {
  885         wait_message(2, _(txt_useless_combination), "-z", "-S", "-z");
  886         start_any_unread = FALSE;
  887     }
  888     if (save_news && check_any_unread) {
  889         wait_message(2, _(txt_useless_combination), "-S", "-Z", "-S");
  890         save_news = FALSE;
  891     }
  892     if (start_any_unread && check_any_unread) {
  893         wait_message(2, _(txt_useless_combination), "-Z", "-z", "-Z");
  894         check_any_unread = FALSE;
  895     }
  896 #   ifdef DEBUG
  897     if ((debug & DEBUG_NNTP) && !read_news_via_nntp) {
  898         /* TODO: lang.c */
  899         wait_message(3, _(txt_useless_combination), _("reading from local spool"), "-D nntp", "-D nntp");
  900         debug &= ~DEBUG_NNTP;
  901     }
  902 #   endif /* DEBUG */
  903 
  904 #if defined(NNTP_ABLE) && defined(INET6)
  905     if (force_ipv4 && force_ipv6) {
  906         wait_message(2, _(txt_useless_combination), "-4", "-6", "-6");
  907         force_ipv6 = FALSE;
  908     }
  909 #endif /* NNTP_ABLE && INET6 */
  910 
  911     if (mail_news || save_news || update_index || check_any_unread || catchup)
  912         batch_mode = TRUE;
  913     else
  914         batch_mode = FALSE;
  915     if (batch_mode && (post_article_and_exit || post_postponed_and_exit))
  916         batch_mode = FALSE;
  917 
  918     /*
  919      * When updating index files set getart_limit to 0 in order to get overview
  920      * information for all article; this overwrites '-G limit' and disables
  921      * tinrc.getart_limit temporary
  922      */
  923     if (update_index) {
  924         cmdline.getart_limit = 0;
  925         cmdline.args |= CMDLINE_GETART_LIMIT;
  926     }
  927 #ifdef NNTP_ABLE
  928     /*
  929      * If we're reading from an NNTP server and we've been asked not to look
  930      * for new newsgroups, trust our cached copy of the newsgroups file.
  931      */
  932     if (read_news_via_nntp)
  933         read_local_newsgroups_file = bool_not(check_for_new_newsgroups);
  934 #endif /* NNTP_ABLE */
  935     /*
  936      * If we use neither list_active nor newsrc_active,
  937      * we use both of them.
  938      */
  939     if (!list_active && !newsrc_active)
  940         list_active = newsrc_active = TRUE;
  941 }
  942 
  943 
  944 /*
  945  * usage
  946  */
  947 static void
  948 usage(
  949     char *theProgname)
  950 {
  951     error_message(2, _(txt_usage_tin), theProgname);
  952 
  953 #if defined(NNTP_ABLE) && defined(INET6)
  954     error_message(2, _(txt_usage_force_ipv4));
  955     error_message(2, _(txt_usage_force_ipv6));
  956 #endif /* NNTP_ABLE && INET6 */
  957 
  958 #ifdef HAVE_COLOR
  959     error_message(2, _(txt_usage_toggle_color));
  960 #endif /* HAVE_COLOR */
  961 #ifdef NNTP_ABLE
  962     error_message(2, _(txt_usage_force_authentication));
  963 #endif /* NNTP_ABLE */
  964 
  965     error_message(2, _(txt_usage_catchup));
  966     error_message(2, _(txt_usage_dont_show_descriptions));
  967 
  968 #ifdef DEBUG
  969     error_message(2, _(txt_usage_debug));
  970 #endif /* DEBUG */
  971 
  972     error_message(2, _(txt_usage_newsrc_file), newsrc);
  973     error_message(2, _(txt_usage_getart_limit));
  974 
  975 #ifdef NNTP_ABLE
  976 #   ifdef NNTP_DEFAULT_SERVER
  977     error_message(2, _(txt_usage_newsserver), get_val("NNTPSERVER", NNTP_DEFAULT_SERVER));
  978 #   else
  979     error_message(2, _(txt_usage_newsserver), get_val("NNTPSERVER", "news"));
  980 #   endif /* NNTP_DEFAULT_SERVER */
  981 #endif /* NNTP_ABLE */
  982 
  983     error_message(2, _(txt_usage_help_message));
  984     error_message(2, _(txt_usage_help_information), theProgname);
  985     error_message(2, _(txt_usage_index_newsdir), index_newsdir);
  986 
  987 #ifdef NNTP_ABLE
  988 #   ifdef NNTPS_ABLE
  989     error_message(2, _(txt_usage_use_insecure_nntps));
  990 #   endif /* NNTPS_ABLE */
  991 #endif /* NNTP_ABLE */
  992 
  993     error_message(2, _(txt_usage_read_only_active));
  994     error_message(2, _(txt_usage_maildir), tinrc.maildir);
  995     error_message(2, _(txt_usage_mail_new_news_to_user));
  996     error_message(2, _(txt_usage_read_only_subscribed));
  997     error_message(2, _(txt_usage_mail_new_news));
  998     error_message(2, _(txt_usage_post_postponed_arts));
  999 
 1000 #ifdef NNTP_ABLE
 1001 #   ifdef NNTPS_ABLE
 1002     error_message(2, _(txt_usage_port), use_nntps ? nntps_tcp_default_port : nntp_tcp_default_port);
 1003 #   else
 1004     error_message(2, _(txt_usage_port), nntp_tcp_default_port);
 1005 #   endif /* NNTPS_ABLE */
 1006 #endif /* NNTP_ABLE */
 1007 
 1008     error_message(2, _(txt_usage_dont_check_new_newsgroups));
 1009     error_message(2, _(txt_usage_quickstart));
 1010 
 1011 #ifdef NNTP_ABLE
 1012     if (!read_news_via_nntp)
 1013         error_message(2, _(txt_usage_read_news_remotely));
 1014 #endif /* NNTP_ABLE */
 1015 
 1016     error_message(2, _(txt_usage_read_saved_news));
 1017     error_message(2, _(txt_usage_savedir), tinrc.savedir);
 1018     error_message(2, _(txt_usage_save_new_news));
 1019 #ifdef NNTP_ABLE
 1020 #   ifdef NNTPS_ABLE
 1021     error_message(2, _(txt_usage_use_nntps));
 1022 #   endif /* NNTPS_ABLE */
 1023 #endif /* NNTP_ABLE */
 1024     error_message(2, _(txt_usage_update_index_files));
 1025     error_message(2, _(txt_usage_verbose));
 1026     error_message(2, _(txt_usage_version));
 1027     error_message(2, _(txt_usage_post_article));
 1028     error_message(2, _(txt_usage_no_posting));
 1029     error_message(2, _(txt_usage_dont_save_files_on_quit));
 1030     error_message(2, _(txt_usage_start_if_unread_news));
 1031     error_message(2, _(txt_usage_check_for_unread_news));
 1032 
 1033     error_message(2, _(txt_usage_mail_bugreport), bug_addr);
 1034 }
 1035 
 1036 
 1037 /*
 1038  * update index files
 1039  */
 1040 _Noreturn static void
 1041 update_index_files(
 1042     void)
 1043 {
 1044     cCOLS = 132;                            /* set because curses has not started */
 1045     create_index_lock_file(lock_file);
 1046     tinrc.thread_articles = THREAD_NONE;    /* stop threading to run faster */
 1047     tinrc.sort_article_type = SORT_ARTICLES_BY_NOTHING;
 1048     tinrc.sort_threads_type = SORT_THREADS_BY_NOTHING;
 1049     do_update(catchup);
 1050     tin_done(EXIT_SUCCESS, NULL);
 1051 }
 1052 
 1053 
 1054 /*
 1055  * display page of general info. for first time user.
 1056  */
 1057 static void
 1058 show_intro_page(
 1059     void)
 1060 {
 1061     char buf[4096];
 1062 
 1063     if (!cmd_line) {
 1064         ClearScreen();
 1065         center_line(0, TRUE, cvers);
 1066         Raw(FALSE);
 1067         my_printf("\n");
 1068     }
 1069 
 1070     snprintf(buf, sizeof(buf), _(txt_intro_page), PRODUCT, PRODUCT, PRODUCT, bug_addr);
 1071 
 1072     my_fputs(buf, stdout);
 1073     my_flush();
 1074 
 1075     if (!cmd_line) {
 1076         Raw(TRUE);
 1077         prompt_continue();
 1078     }
 1079 }
 1080 
 1081 
 1082 /*
 1083  * Wildcard match any newsgroups on the command line. Sort of like a limited
 1084  * yank at startup. Return number of groups that were matched.
 1085  */
 1086 int
 1087 read_cmd_line_groups(
 1088     void)
 1089 {
 1090     int matched = 0;
 1091     int num;
 1092     int i;
 1093 
 1094     if (num_cmdargs < max_cmdargs) {
 1095         selmenu.max = skip_newgroups();     /* Reposition after any newgroups */
 1096 
 1097         for (num = num_cmdargs; num < max_cmdargs; num++) {
 1098             if (!batch_mode)
 1099                 wait_message(0, _(txt_matching_cmd_line_groups), cmdargs[num]);
 1100 
 1101             for_each_group(i) {
 1102                 if (match_group_list(active[i].name, cmdargs[num])) {
 1103                     if (my_group_add(active[i].name, TRUE) != -1) {
 1104                         matched++;
 1105                         if (post_article_and_exit) {
 1106                             my_strncpy(tinrc.default_post_newsgroups, active[i].name, sizeof(tinrc.default_post_newsgroups) - 1);
 1107                             break;
 1108                         }
 1109                     }
 1110                 }
 1111             }
 1112         }
 1113     }
 1114     return matched;
 1115 }
 1116 
 1117 
 1118 /*
 1119  * Create default mail & save directories if they do not exist
 1120  */
 1121 static void
 1122 create_mail_save_dirs(
 1123     void)
 1124 {
 1125     char path[PATH_LEN];
 1126     struct stat sb;
 1127 
 1128     if (!strfpath(tinrc.maildir, path, sizeof(path), NULL, FALSE))
 1129         joinpath(path, sizeof(path), homedir, DEFAULT_MAILDIR);
 1130 
 1131     if (stat(path, &sb) == -1)
 1132         my_mkdir(path, (mode_t) (S_IRWXU));
 1133 
 1134     if (!strfpath(tinrc.savedir, path, sizeof(path), NULL, FALSE))
 1135         joinpath(path, sizeof(path), homedir, DEFAULT_SAVEDIR);
 1136 
 1137     if (stat(path, &sb) == -1)
 1138         my_mkdir(path, (mode_t) (S_IRWXU));
 1139 }
 1140 
 1141 
 1142 /*
 1143  * we don't try do free() any previously malloc()ed mem here as exit via
 1144  * giveup() indicates a serious error and keeping track of what we've
 1145  * already malloc()ed would be a PITA.
 1146  */
 1147 /* coverity[+kill] */
 1148 _Noreturn void
 1149 giveup(
 1150     void)
 1151 {
 1152     static int nested;
 1153 
 1154 #ifdef XFACE_ABLE
 1155     slrnface_stop();
 1156 #endif /* XFACE_ABLE */
 1157 
 1158     if (!cmd_line && !nested++) {
 1159         cursoron();
 1160         EndWin();
 1161         Raw(FALSE);
 1162     }
 1163     close_msglog();
 1164 
 1165     exit(EXIT_FAILURE);
 1166 }