1 /*
2  *  Project   : tin - a Usenet reader
3  *  Module    : main.c
4  *  Author    : I. Lea & R. Skrenta
5  *  Created   : 1991-04-01
6  *  Updated   : 2020-05-22
7  *  Notes     :
8  *
9  * Copyright (c) 1991-2021 Iain Lea <iain@bricbrac.de>, Rich Skrenta <skrenta@pbm.com>
10  * All rights reserved.
11  *
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted provided that the following conditions
14  * are met:
15  *
16  * 1. Redistributions of source code must retain the above copyright notice,
17  *    this list of conditions and the following disclaimer.
18  *
19  * 2. Redistributions in binary form must reproduce the above copyright
20  *    notice, this list of conditions and the following disclaimer in the
21  *    documentation and/or other materials provided with the distribution.
22  *
23  * 3. Neither the name of the copyright holder nor the names of its
24  *    contributors may be used to endorse or promote products derived from
25  *    this software without specific prior written permission.
26  *
27  * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
28  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
31  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
32  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
33  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
34  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
35  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
36  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
37  * POSSIBILITY OF SUCH DAMAGE.
38  */
39 
40 
41 #ifndef TIN_H
42 #	include "tin.h"
43 #endif /* !TIN_H */
44 #ifndef TCURSES_H
45 #	include "tcurses.h"
46 #endif /* !TCURSES_H */
47 #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 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
main(int argc,char * argv[])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 		/*
192 		 * Init curses emulation
193 		 */
194 		if (!InitScreen()) {
195 			error_message(2, _(txt_screen_init_failed), tin_progname);
196 			free_all_arrays();
197 			giveup();
198 		}
199 
200 		EndInverse();
201 
202 		/*
203 		 * This depends on various things in tinrc
204 		 */
205 		setup_screen();
206 	}
207 
208 	if (!batch_mode || verbose)
209 		wait_message(0, "%s\n", cvers);
210 
211 	/*
212 	 * Connect to nntp server?
213 	 */
214 	if (!nntp_server || !*nntp_server)
215 		nntp_server = getserverbyfile(NNTP_SERVER_FILE);
216 	if (read_news_via_nntp && !read_saved_news && nntp_open()) {
217 		nntp_close(FALSE);
218 		free_all_arrays();
219 		giveup();
220 	}
221 
222 	read_server_config();
223 
224 	/*
225 	 * exit early - unfortunately we can't do that in read_cmd_line_options()
226 	 * as nntp_caps.over_cmd is set in nntp_open()
227 	 *
228 	 * TODO: does the logic make sense? what
229 	 * if (update_index && !nntp_caps.over_cmd && !tinrc.cache_overview_files)
230 	 * no error message? why?
231 	 */
232 	if (update_index && nntp_caps.over_cmd && !tinrc.cache_overview_files) {
233 		error_message(2, _(txt_batch_update_unavail), tin_progname, print_boolean(tinrc.cache_overview_files));
234 		free_all_arrays();
235 		giveup();
236 	}
237 
238 	/*
239 	 * Check if overview indexes contain Xref: lines
240 	 */
241 #ifdef NNTP_ABLE
242 	if ((read_news_via_nntp && nntp_caps.over_cmd) || !read_news_via_nntp)
243 #endif /* NNTP_ABLE */
244 		xref_supported = overview_xref_support();
245 
246 	/*
247 	 * avoid empty regexp, we also need to do this in batch_mode
248 	 * as read_overview() calls eat_re() which uses a regexp to
249 	 * modify the subject *sigh*
250 	 */
251 	postinit_regexp();
252 
253 	if (!(batch_mode || post_postponed_and_exit)) {
254 		/*
255 		 * Read user specific keybindings and input history
256 		 */
257 		wait_message(0, _(txt_reading_keymap_file));
258 		read_keymap_file();
259 		read_input_history_file();
260 
261 		/*
262 		 * Load the mail & news active files into active[]
263 		 *
264 		 * create_save_active_file cannot write to active.save
265 		 * if no_write != FALSE, so restore original value temporarily
266 		 */
267 		if (read_saved_news) {
268 			no_write = tmp_no_write;
269 			create_save_active_file();
270 			no_write = TRUE;
271 		}
272 	}
273 
274 #ifdef HAVE_MH_MAIL_HANDLING
275 	read_mail_active_file();
276 #endif /* HAVE_MH_MAIL_HANDLING */
277 
278 	/*
279 	 * Initialise active[] and add new newsgroups to start of my_group[]
280 	 * also reads global/local attributes
281 	 */
282 	selmenu.max = 0;
283 	/*
284 	 * we need to restore the original no_write mode to be able to handle
285 	 * $AUTOSUBSCRIBE groups
286 	 */
287 	no_write = tmp_no_write;
288 	read_attributes_file(TRUE);
289 	read_attributes_file(FALSE);
290 	start_groupnum = read_news_active_file();
291 #ifdef DEBUG
292 	if (debug & DEBUG_MISC)
293 		debug_print_active();
294 #endif /* DEBUG */
295 
296 	/*
297 	 * Read in users filter preferences file. This has to be done before
298 	 * quick post because the filters might be updated.
299 	 */
300 	read_filter_file(filter_file);
301 
302 	no_write = TRUE;
303 #ifdef DEBUG
304 	if (debug & DEBUG_FILTER)
305 		debug_print_filters();
306 #endif /* DEBUG */
307 
308 	/*
309 	 * Preloads active[] with command line groups. They will follow any
310 	 * new newsgroups
311 	 */
312 	if (!post_postponed_and_exit)
313 		num_cmd_line_groups = read_cmd_line_groups();
314 
315 	/*
316 	 * Quick post an article and exit if -w or -o specified
317 	 */
318 	if (post_article_and_exit || post_postponed_and_exit) {
319 		no_write = tmp_no_write; /* restore original value */
320 		quick_post_article(post_postponed_and_exit, num_cmd_line_groups);
321 		wait_message(2, _(txt_exiting));
322 		no_write = TRUE; /* disable newsrc updates */
323 		tin_done(EXIT_SUCCESS, NULL);
324 	}
325 
326 	/* TODO: replace hard coded key-name in txt_info_postponed */
327 	if ((count = count_postponed_articles()))
328 		wait_message(3, _(txt_info_postponed), count, PLURAL(count, txt_article));
329 
330 	/*
331 	 * Read text descriptions for mail and/or news groups
332 	 */
333 	if (show_description && !batch_mode) {
334 		no_write = tmp_no_write; /* restore original value */
335 		read_descriptions(TRUE);
336 		no_write = TRUE; /* disable newsrc updates */
337 	}
338 
339 	/* what about "if (!no_write)" here? */
340 	create_mail_save_dirs();
341 	if (created_rcdir) /* first start */
342 		write_config_file(local_config_file);
343 
344 	if (!tmp_no_write)	/* do not (over)write oldnewsrc with -X */
345 		backup_newsrc();
346 
347 	/*
348 	 * Load my_groups[] from the .newsrc file. We append these groups to any
349 	 * new newsgroups and command line newsgroups already loaded. Also does
350 	 * auto-subscribe to groups specified in /usr/lib/news/subscriptions
351 	 * locally or via NNTP if reading news remotely (LIST SUBSCRIPTIONS)
352 	 */
353 	/*
354 	 * TODO:
355 	 * if (num_cmd_line_groups != 0 && check_any_unread)
356 	 * don't read newsrc.
357 	 * This makes -Z handle command line newsgroups. Test & document
358 	 */
359 	read_newsrc_lines = read_newsrc(newsrc, FALSE);
360 	no_write = tmp_no_write; /* restore old value */
361 
362 	/*
363 	 * We have to show all groups with command line groups
364 	 */
365 	if (num_cmd_line_groups)
366 		tinrc.show_only_unread_groups = FALSE;
367 	else
368 		toggle_my_groups(NULL);
369 
370 	/*
371 	 * Check/start if any new/unread articles
372 	 */
373 	if (check_any_unread)
374 		tin_done(check_start_save_any_news(CHECK_ANY_NEWS, catchup), NULL);
375 
376 	if (start_any_unread) {
377 		batch_mode = TRUE;			/* Suppress some unwanted on-screen garbage */
378 		if ((start_groupnum = check_start_save_any_news(START_ANY_NEWS, catchup)) == -1) {
379 			batch_mode = FALSE;
380 			tin_done(EXIT_SUCCESS, NULL);
381 		}
382 		batch_mode = FALSE;
383 	}
384 
385 	/*
386 	 * Mail any new articles to specified user
387 	 * or
388 	 * Save any new articles to savedir structure for later reading
389 	 *
390 	 * TODO: should we temporarily set
391 	 *       getart_limit=-1,thread_articles=0,sort_article_type=0
392 	 *       for speed reasons?
393 	 */
394 	if (mail_news || save_news) {
395 		check_start_save_any_news(mail_news ? MAIL_ANY_NEWS : SAVE_ANY_NEWS, catchup);
396 		tin_done(EXIT_SUCCESS, NULL);
397 	}
398 
399 	/*
400 	 * Catchup newsrc file (-c option)
401 	 */
402 	if (batch_mode && catchup && !update_index) {
403 		catchup_newsrc_file();
404 		tin_done(EXIT_SUCCESS, NULL);
405 	}
406 
407 	/*
408 	 * Update index files (-u option), also does catchup if requested
409 	 */
410 	if (update_index)
411 		update_index_files();
412 
413 	/*
414 	 * the code below this point can't be reached in batch mode
415 	 */
416 
417 	/*
418 	 * If first time print welcome screen
419 	 */
420 	if (created_rcdir)
421 		show_intro_page();
422 
423 #ifdef XFACE_ABLE
424 	if (tinrc.use_slrnface && !batch_mode)
425 		slrnface_start();
426 #endif /* XFACE_ABLE */
427 
428 #ifdef USE_CURSES
429 	/* Turn scrolling off now the startup messages have been displayed */
430 	scrollok(stdscr, FALSE);
431 #endif /* USE_CURSES */
432 
433 	/*
434 	 * Work loop
435 	 */
436 	selection_page(start_groupnum, num_cmd_line_groups);
437 	/* NOTREACHED */
438 	return 0;
439 }
440 
441 
442 /*
443  * process command line options
444  * [01235789beEFijJkKLOtTyY] are unused
445  * [W] is reserved
446  * [BCPU] have been in use at some time, but now are unused:
447  *   B BBS mode (M_AMIGA only)
448  *   C count articles
449  *   P purge group index files of articles that no longer exist
450  *   U update index files in background
451  */
452 #define OPTIONS "46aAcdD:f:g:G:hHI:lm:M:nNop:qQrRs:SuvVwxXzZ"
453 
454 static void
read_cmd_line_options(int argc,char * argv[])455 read_cmd_line_options(
456 	int argc,
457 	char *argv[])
458 {
459 	int ch;
460 	t_bool newsrc_set = FALSE;
461 
462 	envargs(&argc, &argv, "TINRC");
463 
464 	while ((ch = getopt(argc, argv, OPTIONS)) != -1) {
465 		switch (ch) {
466 
467 			case '4':
468 #if defined(NNTP_ABLE) && defined(INET6)
469 				force_ipv4 = TRUE;
470 				read_news_via_nntp = TRUE;
471 #else
472 #	ifdef NNTP_ABLE
473 				error_message(2, _(txt_option_not_enabled), "-DENABLE_IPV6");
474 #	else
475 				error_message(2, _(txt_option_not_enabled), "-DNNTP_ABLE");
476 #	endif /* NNTP_ABLE */
477 				free_all_arrays();
478 				giveup();
479 				/* keep lint quiet: */
480 				/* NOTREACHED */
481 #endif /* NNTP_ABLE && INET6 */
482 				break;
483 
484 			case '6':
485 #if defined(NNTP_ABLE) && defined(INET6)
486 				force_ipv6 = TRUE;
487 				read_news_via_nntp = TRUE;
488 #	else
489 #	ifdef NNTP_ABLE
490 				error_message(2, _(txt_option_not_enabled), "-DENABLE_IPV6");
491 #	else
492 				error_message(2, _(txt_option_not_enabled), "-DNNTP_ABLE");
493 #	endif /* NNTP_ABLE */
494 				free_all_arrays();
495 				giveup();
496 				/* keep lint quiet: */
497 				/* NOTREACHED */
498 #endif /* NNTP_ABLE && INET6 */
499 				break;
500 
501 			case 'a':
502 #ifdef HAVE_COLOR
503 				cmdline.args |= CMDLINE_USE_COLOR;
504 #else
505 				error_message(2, _(txt_option_not_enabled), "-DHAVE_COLOR");
506 				free_all_arrays();
507 				giveup();
508 				/* keep lint quiet: */
509 				/* NOTREACHED */
510 #endif /* HAVE_COLOR */
511 				break;
512 
513 			case 'A':
514 #ifdef NNTP_ABLE
515 				force_auth_on_conn_open = TRUE;
516 				read_news_via_nntp = TRUE;
517 #else
518 				error_message(2, _(txt_option_not_enabled), "-DNNTP_ABLE");
519 				free_all_arrays();
520 				giveup();
521 				/* keep lint quiet: */
522 				/* NOTREACHED */
523 #endif /* NNTP_ABLE */
524 				break;
525 
526 			case 'c':
527 				batch_mode = TRUE;
528 				catchup = TRUE;
529 				break;
530 
531 			case 'd':
532 				show_description = FALSE;
533 				break;
534 
535 			case 'D':		/* debug mode */
536 #ifdef DEBUG
537 				debug = atoi(optarg) & 0xff;
538 				debug_delete_files();
539 #else
540 				error_message(2, _(txt_option_not_enabled), "-DDEBUG");
541 				free_all_arrays();
542 				giveup();
543 				/* keep lint quiet: */
544 				/* NOTREACHED */
545 #endif /* DEBUG */
546 				break;
547 
548 			case 'f':	/* newsrc file */
549 				my_strncpy(newsrc, optarg, sizeof(newsrc) - 1);
550 				newsrc_set = TRUE;
551 				break;
552 
553 			case 'G':
554 				cmdline.getart_limit = atoi(optarg);
555 				cmdline.args |= CMDLINE_GETART_LIMIT;
556 				break;
557 
558 			case 'g':	/* select alternative NNTP-server, implies -r */
559 #ifdef NNTP_ABLE
560 				my_strncpy(cmdline.nntpserver, optarg, sizeof(cmdline.nntpserver) - 1);
561 				cmdline.args |= CMDLINE_NNTPSERVER;
562 				read_news_via_nntp = TRUE;
563 #else
564 				error_message(2, _(txt_option_not_enabled), "-DNNTP_ABLE");
565 				free_all_arrays();
566 				giveup();
567 				/* keep lint quiet: */
568 				/* NOTREACHED */
569 #endif /* NNTP_ABLE */
570 				break;
571 
572 			case 'H':
573 				show_intro_page();
574 				free_all_arrays();
575 				exit(EXIT_SUCCESS);
576 				/* keep lint quiet: */
577 				/* FALLTHROUGH */
578 
579 			case 'I':
580 				joinpath(index_newsdir, sizeof(index_newsdir), optarg, INDEX_NEWSDIR);
581 				break;
582 
583 			case 'l':
584 				list_active = TRUE;
585 				break;
586 
587 			case 'm':
588 				my_strncpy(cmdline.maildir, optarg, sizeof(cmdline.maildir) - 1);
589 				cmdline.args |= CMDLINE_MAILDIR;
590 				break;
591 
592 			case 'M':	/* mail new news to specified user */
593 				my_strncpy(mail_news_user, optarg, sizeof(mail_news_user) - 1);
594 				mail_news = TRUE;
595 				batch_mode = TRUE;
596 				break;
597 
598 			case 'n':
599 				newsrc_active = TRUE;
600 				break;
601 
602 			case 'N':	/* mail new news to your posts */
603 				my_strncpy(mail_news_user, userid, sizeof(mail_news_user) - 1);
604 				mail_news = TRUE;
605 				batch_mode = TRUE;
606 				break;
607 
608 			case 'o':	/* post postponed articles & exit */
609 #ifndef NO_POSTING
610 				/*
611 				 * TODO: autoposting currently does some screen output, so we
612 				 *       can't set batch_mode
613 				 */
614 				post_postponed_and_exit = TRUE;
615 				check_for_new_newsgroups = FALSE;
616 #else
617 				error_message(2, _(txt_option_not_enabled), "-UNO_POSTING");
618 				free_all_arrays();
619 				giveup();
620 				/* keep lint quiet: */
621 				/* NOTREACHED */
622 #endif /* !NO_POSTING */
623 				break;
624 
625 			case 'p': /* implies -r */
626 #ifdef NNTP_ABLE
627 				read_news_via_nntp = TRUE;
628 				if (atoi(optarg) != 0)
629 					nntp_tcp_port = (unsigned short) atoi(optarg);
630 #else
631 				error_message(2, _(txt_option_not_enabled), "-DNNTP_ABLE");
632 				free_all_arrays();
633 				giveup();
634 				/* keep lint quiet: */
635 				/* NOTREACHED */
636 #endif /* NNTP_ABLE */
637 				break;
638 
639 			case 'q':
640 				check_for_new_newsgroups = FALSE;
641 				break;
642 
643 			case 'Q':
644 				newsrc_active = TRUE;
645 				check_for_new_newsgroups = FALSE;
646 				show_description = FALSE;
647 				break;
648 
649 			case 'r':	/* read news remotely from default NNTP server */
650 #ifdef NNTP_ABLE
651 				read_news_via_nntp = TRUE;
652 #else
653 				error_message(2, _(txt_option_not_enabled), "-DNNTP_ABLE");
654 				free_all_arrays();
655 				giveup();
656 				/* keep lint quiet: */
657 				/* NOTREACHED */
658 #endif /* NNTP_ABLE */
659 				break;
660 
661 			case 'R':	/* read news saved by -S option */
662 				read_saved_news = TRUE;
663 				list_active = TRUE;
664 				newsrc_active = FALSE;
665 				check_for_new_newsgroups = FALSE;
666 				my_strncpy(news_active_file, save_active_file, sizeof(news_active_file) - 1);
667 				break;
668 
669 			case 's':
670 				my_strncpy(cmdline.savedir, optarg, sizeof(cmdline.savedir) - 1);
671 				cmdline.args |= CMDLINE_SAVEDIR;
672 				break;
673 
674 			case 'S':	/* save new news to dir structure */
675 				save_news = TRUE;
676 				batch_mode = TRUE;
677 				break;
678 
679 			case 'u':	/* update index files */
680 				batch_mode = TRUE;
681 				update_index = TRUE;
682 				break;
683 
684 			case 'v':	/* verbose mode, can be used multiple times */
685 				verbose++;
686 				break;
687 
688 			case 'V':
689 				tin_version_info(stderr);
690 				free_all_arrays();
691 				exit(EXIT_SUCCESS);
692 				/* keep lint quiet: */
693 				/* FALLTHROUGH */
694 
695 			case 'w':	/* post article & exit */
696 #ifndef NO_POSTING
697 				post_article_and_exit = TRUE;
698 				check_for_new_newsgroups = FALSE;
699 #else
700 				error_message(2, _(txt_option_not_enabled), "-UNO_POSTING");
701 				free_all_arrays();
702 				giveup();
703 				/* keep lint quiet: */
704 				/* NOTREACHED */
705 #endif /* !NO_POSTING */
706 				break;
707 
708 #if 0
709 			case 'W':	/* reserved according to SUSV3 XDB Utility Syntax Guidelines, Guideline 3 */
710 				break;
711 #endif /* 0 */
712 
713 			case 'x':	/* enter no_posting mode */
714 				force_no_post = TRUE;
715 				break;
716 
717 			case 'X':	/* don't save ~/.newsrc on exit */
718 				no_write = TRUE;
719 				break;
720 
721 			case 'z':
722 				start_any_unread = TRUE;
723 				break;
724 
725 			case 'Z':
726 				check_any_unread = TRUE;
727 				batch_mode = TRUE;
728 				break;
729 
730 			case 'h':
731 			case '?':
732 			default:
733 				usage(tin_progname);
734 				free_all_arrays();
735 				exit(EXIT_SUCCESS);
736 		}
737 	}
738 	cmdargs = argv;
739 	num_cmdargs = optind;
740 	max_cmdargs = argc;
741 	if (!newsrc_set) {
742 		if (read_news_via_nntp) {
743 			nntp_server = getserverbyfile(NNTP_SERVER_FILE);
744 			get_newsrcname(newsrc, sizeof(newsrc), nntp_server);
745 		} else {
746 #if defined(HAVE_SYS_UTSNAME_H) && defined(HAVE_UNAME)
747 			struct utsname uts;
748 			(void) uname(&uts);
749 			get_newsrcname(newsrc, sizeof(newsrc), uts.nodename);
750 #else
751 			char nodenamebuf[256]; /* SUSv2 limit; better use HOST_NAME_MAX */
752 #	ifdef HAVE_GETHOSTNAME
753 			(void) gethostname(nodenamebuf, sizeof(nodenamebuf));
754 #	endif /* HAVE_GETHOSTNAME */
755 			get_newsrcname(newsrc, sizeof(newsrc), nodenamebuf);
756 #endif /* HAVE_SYS_UTSNAME_H && HAVE_UNAME */
757 		}
758 	}
759 
760 	/*
761 	 * Sort out option conflicts
762 	 */
763 	if (!batch_mode) {
764 		if (catchup) {
765 			wait_message(2, _(txt_useful_with_batch_mode), "-c");
766 			catchup = FALSE;
767 		}
768 	} else {
769 		if (read_saved_news) {
770 			wait_message(2, _(txt_useful_without_batch_mode), "-R");
771 			read_saved_news = FALSE;
772 		}
773 	}
774 	if (!(batch_mode || debug)) {
775 		if (verbose) {
776 			wait_message(2, _(txt_useful_with_batch_or_debug_mode), "-v");
777 			verbose = FALSE;
778 		}
779 	}
780 	if (post_postponed_and_exit && force_no_post) {
781 		wait_message(2, _(txt_useless_combination), "-o", "-x", "-x");
782 		force_no_post = FALSE;
783 	}
784 	if (post_article_and_exit && force_no_post) {
785 		wait_message(2, _(txt_useless_combination), "-w", "-x", "-x");
786 		force_no_post = FALSE;
787 	}
788 	if (catchup && start_any_unread) {
789 		wait_message(2, _(txt_useless_combination), "-c", "-z", "-c");
790 		catchup = FALSE;
791 	}
792 	if (catchup && no_write) {
793 		wait_message(2, _(txt_useless_combination), "-c", "-X", "-c");
794 		catchup = FALSE;
795 	}
796 	if (catchup && check_any_unread) {
797 		wait_message(2, _(txt_useless_combination), "-c", "-Z", "-c");
798 		catchup = FALSE;
799 	}
800 	if (newsrc_active && read_saved_news) {
801 		wait_message(2, _(txt_useless_combination), "-n", "-R", "-n");
802 		newsrc_active = read_news_via_nntp = FALSE;
803 	}
804 	if (start_any_unread && save_news) {
805 		wait_message(2, _(txt_useless_combination), "-z", "-S", "-z");
806 		start_any_unread = FALSE;
807 	}
808 	if (save_news && check_any_unread) {
809 		wait_message(2, _(txt_useless_combination), "-S", "-Z", "-S");
810 		save_news = FALSE;
811 	}
812 	if (start_any_unread && check_any_unread) {
813 		wait_message(2, _(txt_useless_combination), "-Z", "-z", "-Z");
814 		check_any_unread = FALSE;
815 	}
816 #	ifdef DEBUG
817 	if ((debug & DEBUG_NNTP) && !read_news_via_nntp) {
818 		/* TODO: lang.c */
819 		wait_message(3, _(txt_useless_combination), _("reading from local spool"), "-D nntp", "-D nntp");
820 		debug &= ~DEBUG_NNTP;
821 	}
822 #	endif /* DEBUG */
823 
824 #if defined(NNTP_ABLE) && defined(INET6)
825 	if (force_ipv4 && force_ipv6) {
826 		wait_message(2, _(txt_useless_combination), "-4", "-6", "-6");
827 		force_ipv6 = FALSE;
828 	}
829 #endif /* NNTP_ABLE && INET6 */
830 
831 	if (mail_news || save_news || update_index || check_any_unread || catchup)
832 		batch_mode = TRUE;
833 	else
834 		batch_mode = FALSE;
835 	if (batch_mode && (post_article_and_exit || post_postponed_and_exit))
836 		batch_mode = FALSE;
837 
838 	/*
839 	 * When updating index files set getart_limit to 0 in order to get overview
840 	 * information for all article; this overwrites '-G limit' and disables
841 	 * tinrc.getart_limit temporary
842 	 */
843 	if (update_index) {
844 		cmdline.getart_limit = 0;
845 		cmdline.args |= CMDLINE_GETART_LIMIT;
846 	}
847 #ifdef NNTP_ABLE
848 	/*
849 	 * If we're reading from an NNTP server and we've been asked not to look
850 	 * for new newsgroups, trust our cached copy of the newsgroups file.
851 	 */
852 	if (read_news_via_nntp)
853 		read_local_newsgroups_file = bool_not(check_for_new_newsgroups);
854 #endif /* NNTP_ABLE */
855 	/*
856 	 * If we use neither list_active nor newsrc_active,
857 	 * we use both of them.
858 	 */
859 	if (!list_active && !newsrc_active)
860 		list_active = newsrc_active = TRUE;
861 }
862 
863 
864 /*
865  * usage
866  */
867 static void
usage(char * theProgname)868 usage(
869 	char *theProgname)
870 {
871 	error_message(2, _(txt_usage_tin), theProgname);
872 
873 #if defined(NNTP_ABLE) && defined(INET6)
874 	error_message(2, _(txt_usage_force_ipv4));
875 	error_message(2, _(txt_usage_force_ipv6));
876 #endif /* NNTP_ABLE && INET6 */
877 
878 #ifdef HAVE_COLOR
879 	error_message(2, _(txt_usage_toggle_color));
880 #endif /* HAVE_COLOR */
881 #ifdef NNTP_ABLE
882 	error_message(2, _(txt_usage_force_authentication));
883 #endif /* NNTP_ABLE */
884 
885 	error_message(2, _(txt_usage_catchup));
886 	error_message(2, _(txt_usage_dont_show_descriptions));
887 
888 #ifdef DEBUG
889 	error_message(2, _(txt_usage_debug));
890 #endif /* DEBUG */
891 
892 	error_message(2, _(txt_usage_newsrc_file), newsrc);
893 	error_message(2, _(txt_usage_getart_limit));
894 
895 #ifdef NNTP_ABLE
896 #	ifdef NNTP_DEFAULT_SERVER
897 	error_message(2, _(txt_usage_newsserver), get_val("NNTPSERVER", NNTP_DEFAULT_SERVER));
898 #	else
899 	error_message(2, _(txt_usage_newsserver), get_val("NNTPSERVER", "news"));
900 #	endif /* NNTP_DEFAULT_SERVER */
901 #endif /* NNTP_ABLE */
902 
903 	error_message(2, _(txt_usage_help_message));
904 	error_message(2, _(txt_usage_help_information), theProgname);
905 	error_message(2, _(txt_usage_index_newsdir), index_newsdir);
906 	error_message(2, _(txt_usage_read_only_active));
907 	error_message(2, _(txt_usage_maildir), tinrc.maildir);
908 	error_message(2, _(txt_usage_mail_new_news_to_user));
909 	error_message(2, _(txt_usage_read_only_subscribed));
910 	error_message(2, _(txt_usage_mail_new_news));
911 	error_message(2, _(txt_usage_post_postponed_arts));
912 
913 #ifdef NNTP_ABLE
914 	error_message(2, _(txt_usage_port), nntp_tcp_port);
915 #endif /* NNTP_ABLE */
916 
917 	error_message(2, _(txt_usage_dont_check_new_newsgroups));
918 	error_message(2, _(txt_usage_quickstart));
919 
920 #ifdef NNTP_ABLE
921 	if (!read_news_via_nntp)
922 		error_message(2, _(txt_usage_read_news_remotely));
923 #endif /* NNTP_ABLE */
924 
925 	error_message(2, _(txt_usage_read_saved_news));
926 	error_message(2, _(txt_usage_savedir), tinrc.savedir);
927 	error_message(2, _(txt_usage_save_new_news));
928 	error_message(2, _(txt_usage_update_index_files));
929 	error_message(2, _(txt_usage_verbose));
930 	error_message(2, _(txt_usage_version));
931 	error_message(2, _(txt_usage_post_article));
932 	error_message(2, _(txt_usage_no_posting));
933 	error_message(2, _(txt_usage_dont_save_files_on_quit));
934 	error_message(2, _(txt_usage_start_if_unread_news));
935 	error_message(2, _(txt_usage_check_for_unread_news));
936 
937 	error_message(2, _(txt_usage_mail_bugreport), bug_addr);
938 }
939 
940 
941 /*
942  * update index files
943  */
944 static void
update_index_files(void)945 update_index_files(
946 	void)
947 {
948 	cCOLS = 132;							/* set because curses has not started */
949 	create_index_lock_file(lock_file);
950 	tinrc.thread_articles = THREAD_NONE;	/* stop threading to run faster */
951 	tinrc.sort_article_type = SORT_ARTICLES_BY_NOTHING;
952 	tinrc.sort_threads_type = SORT_THREADS_BY_NOTHING;
953 	do_update(catchup);
954 	tin_done(EXIT_SUCCESS, NULL);
955 }
956 
957 
958 /*
959  * display page of general info. for first time user.
960  */
961 static void
show_intro_page(void)962 show_intro_page(
963 	void)
964 {
965 	char buf[4096];
966 
967 	if (!cmd_line) {
968 		ClearScreen();
969 		center_line(0, TRUE, cvers);
970 		Raw(FALSE);
971 		my_printf("\n");
972 	}
973 
974 	snprintf(buf, sizeof(buf), _(txt_intro_page), PRODUCT, PRODUCT, PRODUCT, bug_addr);
975 
976 	my_fputs(buf, stdout);
977 	my_flush();
978 
979 	if (!cmd_line) {
980 		Raw(TRUE);
981 		prompt_continue();
982 	}
983 }
984 
985 
986 /*
987  * Wildcard match any newsgroups on the command line. Sort of like a limited
988  * yank at startup. Return number of groups that were matched.
989  */
990 int
read_cmd_line_groups(void)991 read_cmd_line_groups(
992 	void)
993 {
994 	int matched = 0;
995 	int num;
996 	int i;
997 
998 	if (num_cmdargs < max_cmdargs) {
999 		selmenu.max = skip_newgroups();		/* Reposition after any newgroups */
1000 
1001 		for (num = num_cmdargs; num < max_cmdargs; num++) {
1002 			if (!batch_mode)
1003 				wait_message(0, _(txt_matching_cmd_line_groups), cmdargs[num]);
1004 
1005 			for_each_group(i) {
1006 				if (match_group_list(active[i].name, cmdargs[num])) {
1007 					if (my_group_add(active[i].name, TRUE) != -1) {
1008 						matched++;
1009 						if (post_article_and_exit) {
1010 							my_strncpy(tinrc.default_post_newsgroups, active[i].name, sizeof(tinrc.default_post_newsgroups) - 1);
1011 							break;
1012 						}
1013 					}
1014 				}
1015 			}
1016 		}
1017 	}
1018 	return matched;
1019 }
1020 
1021 
1022 /*
1023  * Create default mail & save directories if they do not exist
1024  */
1025 static void
create_mail_save_dirs(void)1026 create_mail_save_dirs(
1027 	void)
1028 {
1029 	char path[PATH_LEN];
1030 	struct stat sb;
1031 
1032 	if (!strfpath(tinrc.maildir, path, sizeof(path), NULL, FALSE))
1033 		joinpath(path, sizeof(path), homedir, DEFAULT_MAILDIR);
1034 
1035 	if (stat(path, &sb) == -1)
1036 		my_mkdir(path, (mode_t) (S_IRWXU));
1037 
1038 	if (!strfpath(tinrc.savedir, path, sizeof(path), NULL, FALSE))
1039 		joinpath(path, sizeof(path), homedir, DEFAULT_SAVEDIR);
1040 
1041 	if (stat(path, &sb) == -1)
1042 		my_mkdir(path, (mode_t) (S_IRWXU));
1043 }
1044 
1045 
1046 /*
1047  * we don't try do free() any previously malloc()ed mem here as exit via
1048  * giveup() indicates a serious error and keeping track of what we've
1049  * already malloc()ed would be a PITA.
1050  */
1051 /* coverity[+kill] */
1052 void
giveup(void)1053 giveup(
1054 	void)
1055 {
1056 	static int nested;
1057 
1058 #ifdef XFACE_ABLE
1059 	slrnface_stop();
1060 #endif /* XFACE_ABLE */
1061 
1062 	if (!cmd_line && !nested++) {
1063 		cursoron();
1064 		EndWin();
1065 		Raw(FALSE);
1066 	}
1067 	exit(EXIT_FAILURE);
1068 }
1069