1 /*
2 * (c) Copyright 1990, Kim Fabricius Storm. All rights reserved.
3 * Copyright (c) 1996-2005 Michael T Pins. All rights reserved.
4 *
5 * The nn user interface main program
6 */
7
8 #include <stdlib.h>
9 #include <unistd.h>
10 #include <string.h>
11 #include <sys/types.h>
12 #include "config.h"
13 #include "global.h"
14 #include "admin.h"
15 #include "answer.h"
16 #include "articles.h"
17 #include "db.h"
18 #include "execute.h"
19 #include "folder.h"
20 #include "group.h"
21 #include "init.h"
22 #include "keymap.h"
23 #include "kill.h"
24 #include "libnov.h"
25 #include "macro.h"
26 #include "match.h"
27 #include "menu.h"
28 #include "newsrc.h"
29 #include "nn.h"
30 #include "nntp.h"
31 #include "options.h"
32 #include "proto.h"
33 #include "sequence.h"
34 #include "nn_term.h"
35
36 #ifdef USE_MALLOC_H
37 #include <malloc.h>
38 #endif
39
40 /* nn.c */
41
42 static int nn_locked(void);
43 static group_header *last_group_maint(group_header * last, int upd);
44 static int read_news(flag_type access_mode, char *mask);
45 static void catch_up(void);
46 static int do_nnspew(void);
47 static void prt_version(void);
48 static void do_db_check(void);
49
50 extern char *bin_directory;
51
52 extern int
53 seq_cross_filtering, /* articles */
54 dont_sort_folders, /* folder.c */
55 dont_split_digests, dont_sort_articles, also_unsub_groups, /* group.c */
56 also_cross_postings, case_fold_search, /* match.c */
57 preview_window, fmt_linenum, fmt_rptsubj, /* menu.c */
58 show_article_date, first_page_lines, /* more.c */
59 keep_rc_backup, no_update, /* newsrc.c */
60 hex_group_args, /* sequence */
61 show_current_time, conf_dont_sleep; /* term.c */
62
63 int
64 article_limit = -1, also_read_articles = 0,
65 also_full_digest = 1, batch_mode = 0, conf_auto_quit = 0,
66 do_kill_handling = 1, merged_menu = 0, prev_also_read = 1,
67 repeat_group_query = 0, report_cost_on_exit = 1,
68 show_motd_on_entry = 1, silent = 0, verbose = 0, Debug = 0;
69
70 int check_db_update = 12 /* HOURS */ ;
71
72 extern char proto_host[];
73 extern int newsrc_update_freq, novice;
74 extern int seq_cross_filtering;
75 extern char *news_active;
76 extern long unread_articles;
77 extern long initial_memory_break;
78 extern int first_time_user;
79 extern int also_cross_postings;
80
81 static int
82 group_name_args = 0, nngrab_mode = 0, prompt_for_group = 0;
83
84 static char
85 *match_subject = NULL, *match_sender = NULL, *init_file = NULL;
86
87 static
Option_Description(nn_options)88 Option_Description(nn_options)
89 {
90 'a', Int_Option(article_limit),
91 'B', Bool_Option(keep_rc_backup),
92 'd', Bool_Option(dont_split_digests),
93 'f', Bool_Option(dont_sort_folders),
94 'g', Bool_Option(prompt_for_group),
95 'G', Bool_Option(nngrab_mode),
96 'i', Bool_Option(case_fold_search),
97 'I', String_Option_Optional(init_file, NULL),
98 'k', Bool_Option(do_kill_handling),
99 'l', Int_Option(first_page_lines),
100 'L', Int_Option_Optional(fmt_linenum, 3),
101 'm', Bool_Option(merged_menu),
102 'n', String_Option(match_sender),
103 'N', Bool_Option(no_update),
104 'q', Bool_Option(dont_sort_articles),
105 'Q', Bool_Option(silent),
106 'r', Bool_Option(repeat_group_query),
107 's', String_Option(match_subject),
108 'S', Bool_Option(fmt_rptsubj),
109 'T', Bool_Option(show_current_time),
110 'w', Int_Option_Optional(preview_window, 5),
111 'W', Bool_Option(conf_dont_sleep),
112 'x', Int_Option_Optional(also_read_articles, -1),
113 'X', Bool_Option(also_unsub_groups),
114 'Z', Int_Option(Debug),
115 '\0',
116 };
117
118
119 static int
120 report_number_of_articles = 0;
121 static char
122 *check_message_format = NULL;
123 extern int
124 quick_unread_count;
125
126 static
Option_Description(check_options)127 Option_Description(check_options)
128 {
129 'Q', Bool_Option(silent),
130 'r', Bool_Option(report_number_of_articles),
131 'f', String_Option(check_message_format),
132 't', Bool_Option(verbose),
133 'v', Bool_Option(verbose),
134 'c', Bool_Option(quick_unread_count),
135 '\0',
136 };
137
138 /* program name == argv[0] without path */
139
140 char *pname;
141
142 static int must_unlock = 0;
143
144 static int
nn_locked(void)145 nn_locked(void)
146 {
147 if (no_update)
148 return 0;
149
150 switch (who_am_i) {
151 case I_AM_ADMIN:
152 case I_AM_CHECK:
153 case I_AM_POST:
154 case I_AM_GREP:
155 case I_AM_SPEW:
156 case I_AM_VIEW:
157 return 0; /* will not update .newsrc */
158 }
159
160 if (proto_lock(I_AM_NN, PL_SET)) {
161
162 if (proto_host[0])
163 nn_exitmsg(1, "\nnn is running on host %s\nor %s/LOCK should be removed.\n\n", proto_host, nn_directory);
164 else
165 nn_exitmsg(1, "\nAnother nn process is already running\n\n");
166 }
167 must_unlock = 1;
168 return 0;
169 }
170
171 #define setup_access() ACC_PARSE_VARIABLES
172
173 flag_type
parse_access_flags(void)174 parse_access_flags(void)
175 {
176 flag_type access_mode = 0;
177
178 if (do_kill_handling)
179 access_mode |= ACC_DO_KILL;
180 if (also_cross_postings)
181 access_mode |= ACC_ALSO_CROSS_POSTINGS;
182 if (also_read_articles)
183 access_mode |= ACC_ALSO_READ_ARTICLES;
184 if (dont_split_digests)
185 access_mode |= ACC_DONT_SPLIT_DIGESTS;
186 if (also_full_digest)
187 access_mode |= ACC_ALSO_FULL_DIGEST;
188 if (dont_sort_articles)
189 access_mode |= ACC_DONT_SORT_ARTICLES;
190
191 return access_mode;
192 }
193
194 group_header *jump_to_group = NULL;
195 int enter_last_read_mode = 1;
196
197 static group_header *
last_group_maint(group_header * last,int upd)198 last_group_maint(group_header * last, int upd)
199 {
200 group_header *gh;
201 FILE *f;
202 char *lg_file;
203 char buf[256], *cp;
204
205 lg_file = relative(nn_directory, "NEXTG");
206 if (upd) {
207 if (last == NULL)
208 unlink(lg_file);
209 else {
210 f = open_file(lg_file, OPEN_CREATE | MUST_EXIST);
211 fprintf(f, "%s\n", last->group_name);
212 fclose(f);
213 }
214 return NULL;
215 }
216 if (enter_last_read_mode == 0)
217 return last;
218
219 f = open_file(lg_file, OPEN_READ);
220 if (f == NULL)
221 return last;
222
223 gh = NULL;
224 if (fgets(buf, 256, f)) {
225 if ((cp = strchr(buf, NL)))
226 *cp = NUL;
227 gh = lookup(buf);
228 }
229 fclose(f);
230
231 if (gh == NULL || gh == last || !(gh->group_flag & G_SEQUENCE))
232 return last;
233
234 switch (enter_last_read_mode) {
235 case 1: /* confirm if unread, skip if read */
236 if (gh->unread_count == 0)
237 return last;
238 /* FALLTHROUGH */
239 case 2: /* confirm any case */
240 prompt_line = Lines - 1;
241 prompt("Enter %s (%ld unread)? ", gh->group_name, gh->unread_count);
242 if (!yes(0))
243 return last;
244 break;
245 case 3: /* enter uncond if unread */
246 if (gh->unread_count == 0)
247 return last;
248 /* FALLTHROUGH */
249 case 4: /* enter uncond */
250 break;
251 }
252 return gh;
253 }
254
255 static int
read_news(flag_type access_mode,char * mask)256 read_news(flag_type access_mode, char *mask)
257 {
258 register group_header *gh, *prev, *tmp, *after_loop;
259 flag_type group_mode;
260 int menu_cmd;
261 int must_clear = 0, did_jump = 0;
262 group_header *last_group_read = NULL;
263
264 prev = group_sequence;
265 gh = group_sequence;
266 after_loop = NULL;
267
268 if (access_mode == 0 && !also_read_articles) {
269 gh = last_group_maint(gh, 0);
270 did_jump = gh != group_sequence;
271 m_invoke(-2);
272 }
273 for (;;) {
274 group_mode = access_mode;
275
276 if (s_hangup)
277 break;
278
279 if (gh == NULL) {
280 if (after_loop != NULL) {
281 gh = after_loop;
282 after_loop = NULL;
283 if (gh->unread_count <= 0)
284 group_mode |= ACC_ORIG_NEWSRC;
285 goto enter_direct;
286 }
287 if (did_jump) {
288 did_jump = 0;
289 gh = group_sequence;
290 continue;
291 }
292 if (must_clear && conf_auto_quit) {
293 prompt("\1LAST GROUP READ. QUIT NOW?\1");
294 switch (yes(1)) {
295 case 1:
296 break;
297 case 0:
298 gh = group_sequence;
299 default:
300 after_loop = prev;
301 continue;
302 }
303 }
304 last_group_read = NULL;
305 break;
306 }
307 if (gh->group_flag & G_UNSUBSCRIBED) {
308 if (!also_unsub_groups) {
309 gh = gh->next_group;
310 continue;
311 }
312 } else if (!also_read_articles && gh->unread_count <= 0) {
313 gh = gh->next_group;
314 continue;
315 }
316 enter_direct:
317
318 free_memory();
319
320 if (gh->group_flag & G_FOLDER) {
321 menu_cmd = folder_menu(gh->group_name, 0);
322 if (menu_cmd == ME_NO_REDRAW) {
323 menu_cmd = ME_NO_ARTICLES;
324 gh->last_db_article = 0;
325 }
326 } else {
327 group_mode |= setup_access();
328 if (group_mode & ACC_ORIG_NEWSRC)
329 gh->last_article = gh->first_article;
330
331 menu_cmd = group_menu(gh, (article_number) (-1), group_mode, mask, menu);
332 group_mode = access_mode;
333 }
334
335 if (menu_cmd != ME_NO_ARTICLES) {
336 last_group_read = gh;
337 after_loop = NULL;
338 must_clear++;
339 }
340 switch (menu_cmd) {
341
342 case ME_QUIT: /* or jump */
343 if (!jump_to_group)
344 goto out;
345
346 prev = jump_to_group;
347 jump_to_group = NULL;
348 did_jump = 1;
349 /* FALLTHROUGH */
350
351 case ME_PREV:
352 tmp = gh;
353 gh = prev;
354 prev = tmp;
355 if (gh->unread_count <= 0)
356 group_mode |= ACC_ORIG_NEWSRC;
357 else if (prev_also_read)
358 group_mode |= ACC_MERGED_NEWSRC;
359 goto enter_direct;
360
361 case ME_NEXT:
362 prev = gh;
363 /* FALLTHROUGH */
364
365 case ME_NO_ARTICLES:
366 if (gh->group_flag & G_MERGE_HEAD) {
367 do
368 gh = gh->next_group;
369 while (gh && (gh->group_flag & G_MERGE_SUB));
370 } else
371 gh = gh->next_group;
372 continue;
373 }
374 }
375
376 out:
377 if (access_mode == 0 && !also_read_articles)
378 last_group_maint(last_group_read, 1);
379
380 return must_clear;
381 }
382
383
384 static void
catch_up(void)385 catch_up(void)
386 {
387 register group_header *gh;
388 char answer1[50];
389 int mode, must_help;
390 flag_type access_mode;
391
392 access_mode = setup_access();
393
394 prt_unread("\nCatch-up on %u ? (auto)matically (i)nteractive ");
395 fl;
396 mode = 0;
397
398 if (fgets(answer1, sizeof(answer1), stdin)) {
399 if (strncmp(answer1, "auto", 4) == 0) {
400 tprintf("\nUPDATING .newsrc FILE....");
401 fl;
402 mode = -1;
403 } else if (*answer1 == 'i')
404 mode = 1;
405 }
406 if (mode == 0) {
407 tprintf("\nNO UPDATE\n");
408 return;
409 }
410 newsrc_update_freq = 9999;
411 must_help = novice;
412
413 Loop_Groups_Sequence(gh) {
414
415 if ((gh->group_flag & G_COUNTED) == 0)
416 continue;
417
418 if (mode < 0) {
419 update_rc_all(gh, 0);
420 continue;
421 }
422 again:
423 if (must_help) {
424 tprintf("\n y - mark all articles as read in current group\n");
425 tprintf(" n - do not update group\n");
426 tprintf(" r - read the group now\n");
427 tprintf(" U - unsubscribe to current group\n");
428 tprintf(" ? - this message\n");
429 tprintf(" q - quit\n\n");
430
431 must_help = 0;
432 }
433 tprintf("\rUpdate %s (%ld)? (ynrU?q) n\b", gh->group_name,
434 (long) (gh->last_db_article - gh->last_article));
435 fl;
436
437 if (fgets(answer1, sizeof(answer1), stdin) == NULL || s_keyboard)
438 *answer1 = 'q';
439
440 switch (*answer1) {
441
442 case 'q':
443 tputc(NL);
444 tprintf("Update rest? (yn) n\b");
445 fl;
446 if (fgets(answer1, sizeof(answer1), stdin) == NULL || *answer1 != 'y')
447 return;
448
449 mode = -1;
450 break;
451
452 case NUL:
453 case 'n':
454 continue;
455
456 case 'r':
457 nn_raw();
458 group_menu(gh, (article_number) (-1), access_mode, (char *) NULL, menu);
459 unset_raw();
460 clrdisp();
461 if (gh->unread_count > 0)
462 goto again;
463 continue;
464
465 case 'U':
466 update_rc_all(gh, 1);
467 continue;
468
469 case 'y':
470 break;
471
472 default:
473 must_help = 1;
474 goto again;
475 }
476
477 update_rc_all(gh, 0);
478 }
479
480 tprintf("DONE\n");
481
482 flush_newsrc();
483 }
484
485 char *mail_box = NULL;
486
487 int
unread_mail(time_t t)488 unread_mail(time_t t)
489 {
490 struct stat st;
491 static time_t next = 0;
492 static int any = 0;
493
494 if (next > t)
495 return any;
496
497 next = t + 60;
498 any = 0;
499
500 if (mail_box == NULL ||
501 stat(mail_box, &st) != 0 ||
502 st.st_size == 0 ||
503 st.st_mtime < st.st_atime)
504 return 0;
505
506 any = 1;
507
508 return 1;
509 }
510
511 #ifdef ACCOUNTING
512
513 #ifndef STATISTICS
514 #define STATISTICS 1
515 #endif
516
517 #define NEED_ACCOUNT
518 #else
519
520 #ifdef AUTHORIZE
521 #define NEED_ACCOUNT
522 #endif
523
524 #endif
525
526 #ifdef STATISTICS
527 static time_t usage_time = 0;
528 static time_t last_tick = 0;
529
530 void
stop_usage(void)531 stop_usage(void)
532 {
533 last_tick = 0;
534 }
535
536 time_t
tick_usage(void)537 tick_usage(void)
538 {
539 register time_t now;
540
541 now = cur_time();
542
543 /*
544 * We ignore ticks > 2 minutes because the user has probably just left
545 * the terminal inside nn and done something else
546 */
547 if (last_tick > (now - 120))
548 usage_time += now - last_tick;
549
550 last_tick = now;
551 return now;
552 }
553
554 static void
log_usage(void)555 log_usage(void)
556 {
557
558 #ifdef ACCOUNTING
559 account('U', report_cost_on_exit);
560 #else
561 if (usage_time < (STATISTICS * 60))
562 return; /* don't log short sessions */
563
564 usage_time /= 60;
565 log_entry('U', "USAGE %d.%02d", usage_time / 60, usage_time % 60);
566 #endif
567 }
568
569 #else
570 void
stop_usage(void)571 stop_usage(void)
572 {
573 }
574
575 time_t
tick_usage(void)576 tick_usage(void)
577 {
578 return cur_time();
579 }
580
581 static void
log_usage(void)582 log_usage(void)
583 {
584 }
585
586 #endif
587
588 #ifdef NEED_ACCOUNT
589 int
account(char option,int report)590 account(char option, int report)
591 {
592 char *args[10], **ap;
593 char buf1[16], buf2[16];
594 int ok;
595
596 if (who_am_i != I_AM_NN && who_am_i != I_AM_POST)
597 return 0;
598
599 if (report) {
600 tputc(CR);
601 clrline();
602 fl;
603 }
604 ap = args;
605 *ap++ = "nnacct";
606 if (report)
607 *ap++ = "-r";
608
609 #ifdef STATISTICS
610 sprintf(buf1, "-%c%ld", option, (long) usage_time / 60);
611 #else
612 sprintf(buf1, "-%c0", option);
613 #endif
614
615 *ap++ = buf1;
616 sprintf(buf2, "-W%d", who_am_i);
617 *ap++ = buf2;
618 *ap++ = NULL;
619 ok = execute(relative(bin_directory, "nnacct"), args, 0);
620 if (option == 'U' && report)
621 tputc(NL);
622 return ok;
623 }
624
625 #endif
626
627 /* this will go into emacs_mode.c when it is completed someday */
628
629 static void
emacs_mode(void)630 emacs_mode(void)
631 {
632 tprintf("EMACS MODE IS NOT SUPPORTED YET.\n");
633 }
634
635
636 static int
do_nnspew(void)637 do_nnspew(void)
638 {
639 register group_header *gh;
640 int ix;
641
642 ix = 0;
643 Loop_Groups_Header(gh) {
644 if (gh->master_flag & M_IGNORE_GROUP)
645 continue;
646 gh->preseq_index = ++ix;
647 }
648
649 /* Only output each article once */
650 /* Must also use this mode when using nngrab! */
651
652 seq_cross_filtering = 1;
653
654 Loop_Groups_Header(gh) {
655 if (s_hangup)
656 return 1;
657 if (gh->master_flag & M_IGNORE_GROUP)
658 continue;
659 access_group(gh, gh->first_db_article, gh->last_db_article,
660 ACC_DONT_SORT_ARTICLES | ACC_ALSO_FULL_DIGEST |
661 ACC_SPEW_MODE, (char *) NULL);
662 }
663 return 0;
664 }
665
666 static void
prt_version(void)667 prt_version(void)
668 {
669 clrdisp();
670 tprintf("Release %s\n by Kim F. Storm, Peter Wemm, and Michael T Pins, 2005\n\n", version_id);
671 }
672
673 int
display_motd(int check)674 display_motd(int check)
675 {
676 time_t last_motd, cur_motd;
677 char *dot_motd, motd[FILENAME];
678
679 strcpy(motd, relative(lib_directory, "motd"));
680 cur_motd = file_exist(motd, (char *) NULL);
681 if (cur_motd == 0)
682 return 0;
683
684 if (!check) {
685 display_file(motd, CLEAR_DISPLAY | CONFIRMATION);
686 return 1;
687 }
688 dot_motd = relative(nn_directory, ".motd");
689 last_motd = file_exist(dot_motd, (char *) NULL);
690
691 if (last_motd >= cur_motd)
692 return 0;
693
694 unlink(dot_motd);
695 fclose(open_file(dot_motd, OPEN_CREATE | MUST_EXIST));
696
697 clrdisp();
698
699 so_printxy(0, 0, "Message of the day");
700 gotoxy(0, 2);
701 prt_version();
702
703 display_file(motd, CONFIRMATION);
704 return 1;
705 }
706
707 static void
do_db_check(void)708 do_db_check(void)
709 {
710 time_t last_upd;
711
712 #ifdef NOV
713 time_t active_time;
714
715 if (use_nntp)
716 return;
717
718 active_time = file_exist(news_active, (char *) NULL);
719 if (active_time == 0)
720 return; /* cannot stat/read/etc... */
721
722 last_upd = (cur_time() - active_time) / (60 * 60);
723 if (last_upd < check_db_update)
724 return;
725 tprintf("Notice: no news has arrived for the last %ld hours\n", last_upd);
726
727 #else /* NOV */
728
729 last_upd = (cur_time() - master.last_scan) / (60 * 60);
730 if (last_upd < check_db_update)
731 return;
732 /* too old - but is nnmaster the culprit? */
733 if (master.last_scan == file_exist(news_active, (char *) NULL))
734 tprintf("Notice: no news has arrived for the last %ld hours\n", last_upd);
735 else
736 tprintf("Notice: nnmaster has not updated database in %ld hours\n", last_upd);
737 #endif /* NOV */
738
739 sleep(3);
740 }
741
742 int
main(int argc,char * argv[])743 main(int argc, char *argv[])
744 {
745 int say_welcome = 0, cmd;
746 flag_type access_mode = 0;
747 char *mask = NULL;
748 initial_memory_break = (long) sbrk(0);
749
750 #ifdef USE_MALLOC_H
751
752 #ifdef MALLOC_MAXFAST
753 mallopt(M_MXFAST, MALLOC_MAXFAST);
754 #endif
755
756 #ifdef MALLOC_FASTBLOCKS
757 mallopt(M_NLBLKS, MALLOC_FASTBLOCKS);
758 #endif
759
760 #ifdef MALLOC_GRAIN
761 mallopt(M_GRAIN, MALLOC_GRAIN);
762 #endif
763
764 #endif
765
766 #ifdef MALDEBUG
767 mal_debug(getenv("MALDEBUG") ? atoi(getenv("MALDEBUG")) : 0);
768 #endif
769
770 pname = program_name(argv);
771
772 if (strcmp(pname, "nnadmin") == 0) {
773 who_am_i = I_AM_ADMIN;
774 } else if (strcmp(pname, "nnemacs") == 0) {
775 who_am_i = I_AM_EMACS;
776 } else if (strcmp(pname, "nncheck") == 0) {
777 who_am_i = I_AM_CHECK;
778 } else if (strcmp(pname, "nntidy") == 0) {
779 who_am_i = I_AM_TIDY;
780 } else if (strcmp(pname, "nngoback") == 0) {
781 who_am_i = I_AM_GOBACK;
782 } else if (strcmp(pname, "nngrep") == 0) {
783 who_am_i = I_AM_GREP;
784 } else if (strcmp(pname, "nnpost") == 0) {
785 who_am_i = I_AM_POST;
786 } else if (strcmp(pname, "nnview") == 0) {
787 who_am_i = I_AM_VIEW;
788 } else if (strcmp(pname, "nnbatch") == 0) {
789 who_am_i = I_AM_NN;
790 batch_mode = 1;
791 } else {
792 who_am_i = I_AM_NN;
793 }
794
795 #ifdef NOV
796 #if defined(NOV_DIRECTORY) || defined(NOV_FILENAME)
797 if (who_am_i != I_AM_VIEW) {
798
799 #ifdef NOV_DIRECTORY
800 novartdir(NOV_DIRECTORY);
801 #endif /* NOV_DIRECTORY */
802
803 #ifdef NOV_FILENAME
804 novfilename(NOV_FILENAME);
805 #endif /* NOV_FILENAME */
806 }
807 #endif /* NOV_DIRECTORY || NOV_FILENAME */
808 #endif /* NOV */
809
810 if (who_am_i == I_AM_NN || (who_am_i == I_AM_ADMIN && argc == 1)) {
811 if (!batch_mode && !isatty(0)) {
812 if (who_am_i == I_AM_NN && argc == 2) {
813 if (strcmp(argv[1], "-SPEW") == 0) {
814 who_am_i = I_AM_SPEW;
815 init_global();
816 goto nnspew;
817 }
818 }
819 fprintf(stderr, "Input is not a tty\n");
820 exit(1);
821 }
822 }
823
824 #ifdef AUTHORIZE
825 init_execute();
826 if ((cmd = account('P', 0))) {
827 switch (cmd) {
828 case 1:
829 if (who_am_i == I_AM_POST) {
830 tprintf("You are not authorized to post news\n");
831 exit(41);
832 }
833 /* ok_to_post = 0; */
834 cmd = 0;
835 break;
836 case 2:
837 tprintf("You are not authorized to read news\n");
838 break;
839 case 3:
840 tprintf("You cannot read news at this time\n");
841 break;
842 case 4:
843 tprintf("Your news reading quota is exceeded\n");
844 break;
845 default:
846 tprintf("Bad authorization -- reason %d\n", cmd);
847 break;
848 }
849 if (cmd)
850 exit(40 + cmd);
851 }
852 #endif
853
854 /*
855 * do this before nntp_check so nntp_server can be set on cmdline as nn
856 * -someoptions nntp_server=[somehost OR /somefile] -moreoptionsmaybe
857 *
858 * You really want to do that if you are using multiple servers and/or
859 * .newsrc files. The downside is that you can't override an init file
860 * setting on the command line, but this is something I very rarely have
861 * seen a need for.
862 */
863
864 if ((say_welcome = init_global()) < 0)
865 nn_exitmsg(1, "%s: nn has not been invoked to initialize user files", pname);
866 parseargv_variables(argv, argc);
867 if ((say_welcome = init_global2()) < 0)
868 nn_exitmsg(1, "%s: nn has not been invoked to initialize user files", pname);
869
870 init_key_map();
871
872 #ifndef AUTHORIZE
873 init_execute();
874 #endif
875
876 init_macro();
877
878 switch (who_am_i) {
879 case I_AM_VIEW:
880 /* FALLTHROUGH */
881 case I_AM_NN:
882 init_term(1);
883 visit_init_file(0, argv[1]);
884 if (!silent)
885 prt_version();
886 break;
887
888 case I_AM_ADMIN:
889 if (argc == 1) {
890 init_term(1);
891 visit_init_file(0, (char *) NULL);
892 }
893 break;
894
895 case I_AM_CHECK:
896 /* FALLTHROUGH */
897 case I_AM_TIDY:
898 /* FALLTHROUGH */
899 case I_AM_GOBACK:
900 /* FALLTHROUGH */
901 case I_AM_GREP:
902 init_term(0);
903 visit_init_file(0, (char *) NULL);
904 if (!silent && who_am_i != I_AM_CHECK)
905 prt_version();
906 break;
907 }
908
909 if (nn_locked())
910 nn_exit(2);
911
912 #ifdef NNTP
913 if (who_am_i != I_AM_VIEW) {
914 nntp_check();
915 }
916 #endif
917
918 nnspew:
919
920 if (who_am_i != I_AM_VIEW)
921 open_master(OPEN_READ);
922
923 switch (who_am_i) {
924
925 case I_AM_VIEW:
926 /* FALLTHROUGH */
927 case I_AM_NN:
928 if (say_welcome) {
929 first_time_user = 1;
930 display_file("adm.welcome", CLEAR_DISPLAY | CONFIRMATION);
931 clrdisp();
932 }
933 if (show_motd_on_entry)
934 if (display_motd(1))
935 silent = 1;
936
937 group_name_args =
938 parse_options(argc, argv, (char *) NULL, nn_options,
939 " [group | [+]folder]...");
940
941 if (also_read_articles) {
942 if (article_limit < 0)
943 article_limit = also_read_articles;
944 do_kill_handling = 0;
945 no_update++;
946 }
947 if (match_subject) {
948 mask = match_subject;
949 access_mode = ACC_ON_SUBJECT;
950 } else if (match_sender) {
951 mask = match_sender;
952 access_mode = ACC_ON_SENDER;
953 } else {
954 mask = NULL;
955 access_mode = 0;
956 }
957
958 if (mask) {
959 if (case_fold_search)
960 fold_string(mask);
961 no_update++;
962 do_kill_handling = 0;
963 }
964 if (nngrab_mode) {
965 seq_cross_filtering = 1;
966 hex_group_args = 1;
967 no_update = 1;
968 }
969 if (merged_menu) {
970 no_update++;
971 }
972 if (!no_update && group_name_args > 0)
973 no_update = only_folder_args(argv + 1);
974
975 break;
976
977 case I_AM_ADMIN:
978 admin_mode(argv[1]);
979 nn_exit(0);
980 break;
981
982 case I_AM_CHECK:
983 silent = 0; /* override setting in init file */
984 group_name_args =
985 parse_options(argc, argv, (char *) NULL, check_options,
986 " [group]...");
987 no_update = 1; /* don't update .newsrc and LAST */
988 break;
989
990 case I_AM_EMACS:
991 silent = 1;
992 break;
993
994 case I_AM_TIDY:
995 group_name_args = opt_nntidy(argc, argv);
996 break;
997
998 case I_AM_GOBACK:
999 group_name_args = opt_nngoback(argc, &argv);
1000 break;
1001
1002 case I_AM_GREP:
1003 opt_nngrep(argc, argv);
1004 silent = 1;
1005 no_update = 1;
1006 break;
1007
1008 case I_AM_POST:
1009 do_nnpost(argc, argv);
1010 nn_exit(0);
1011 break;
1012
1013 case I_AM_SPEW:
1014 if (proto_lock(I_AM_SPEW, PL_SET) == 0) {
1015 cmd = do_nnspew();
1016 proto_lock(I_AM_SPEW, PL_CLEAR);
1017 } else
1018 cmd = 1; /* don't interfere with other nnspew */
1019 nn_exit(cmd);
1020 break;
1021 }
1022
1023 if (who_am_i == I_AM_VIEW) {
1024 named_group_sequence(argv + 1);
1025 count_unread_articles();
1026 if (read_news(access_mode, mask))
1027 clrdisp();
1028 nn_exit(0);
1029 }
1030
1031 /*
1032 * at one point, the command line variable settings were handled in the
1033 * next routine (named_group_sequence()), which meant you couldn't change
1034 * the .newsrc file. I moved that processing into parseargv_variables(),
1035 * which is called quite early. Olson, 11/99
1036 */
1037
1038 visit_rc_file();
1039
1040 if (group_name_args > 0)
1041 named_group_sequence(argv + 1);
1042 else
1043 normal_group_sequence();
1044
1045 if (who_am_i != I_AM_TIDY && who_am_i != I_AM_GOBACK)
1046 count_unread_articles();
1047
1048 switch (who_am_i) {
1049
1050 case I_AM_EMACS:
1051 prt_unread("%U %G\n");
1052 emacs_mode();
1053 break;
1054
1055 case I_AM_CHECK:
1056 if (report_number_of_articles) {
1057 prt_unread("%U");
1058 break;
1059 }
1060 if (check_message_format) {
1061 if (unread_articles || !silent)
1062 prt_unread(check_message_format);
1063 } else if (!silent) {
1064 if (unread_articles || report_number_of_articles)
1065 prt_unread("There %i %u in %g\n");
1066 else
1067 prt_unread((char *) NULL);
1068 }
1069 nn_exit(unread_articles ? 0 : 99);
1070 /* NOTREACHED */
1071
1072 case I_AM_TIDY:
1073 do_tidy_newsrc();
1074 break;
1075
1076 case I_AM_GOBACK:
1077 do_goback();
1078 break;
1079
1080 case I_AM_NN:
1081 if (check_db_update)
1082 do_db_check();
1083
1084 if (unread_articles == 0 &&
1085 group_name_args == 0 &&
1086 !also_read_articles &&
1087 !prompt_for_group) {
1088 if (!silent)
1089 prt_unread((char *) NULL);
1090 break;
1091 }
1092 if (do_kill_handling)
1093 do_kill_handling = init_kill();
1094
1095 if (prompt_for_group) {
1096
1097 if (mask != NULL)
1098 nn_exitmsg(1, "Cannot use -s/-n with -g\n\r");
1099
1100 also_cross_postings = 1;
1101 do {
1102 nn_raw();
1103 clrdisp();
1104 current_group = NULL;
1105 prompt_line = 2;
1106 cmd = goto_group(K_GOTO_GROUP, (article_header *) NULL, setup_access());
1107
1108 if (cmd == ME_NO_REDRAW)
1109 sleep(2);
1110 } while (repeat_group_query && cmd != ME_QUIT && cmd != ME_NO_REDRAW);
1111 clrdisp();
1112 unset_raw();
1113 break;
1114 }
1115 if (!no_update && article_limit == 0) {
1116 catch_up();
1117 break;
1118 }
1119 if (merged_menu) {
1120 merge_and_read(access_mode | setup_access(), mask);
1121 clrdisp();
1122 break;
1123 }
1124 if (read_news(access_mode, mask)) {
1125 clrdisp();
1126
1127 if (master.db_lock[0])
1128 tprintf("Database has been locked:\n%s\n", master.db_lock);
1129
1130 if (!also_read_articles &&
1131 unread_articles > 0 &&
1132 !silent && group_name_args == 0)
1133 prt_unread("There %i still %u in %g\n\n\r");
1134 break;
1135 }
1136 gotoxy(0, Lines - 1);
1137 if (group_name_args == 0) {
1138 clrdisp();
1139 tprintf("No News (is good news)\n");
1140 } else
1141 tprintf("\r\n");
1142 break;
1143
1144 case I_AM_GREP:
1145 do_grep(argv + 1);
1146 break;
1147 }
1148
1149 nn_exit(0);
1150 /* NOTREACHED */
1151 return 0;
1152 }
1153
1154 /*
1155 * nn_exit() --- called whenever a program exits.
1156 */
1157
1158 void
nn_exit(int n)1159 nn_exit(int n)
1160 {
1161 static int loop = 0;
1162
1163 if (loop)
1164 exit(n);
1165 loop++;
1166
1167 visual_off();
1168
1169 #ifdef NNTP
1170 nntp_cleanup();
1171 #endif /* NNTP */
1172
1173 close_master();
1174 flush_newsrc();
1175
1176 if (who_am_i == I_AM_NN)
1177 log_usage();
1178
1179 if (must_unlock)
1180 proto_lock(I_AM_NN, PL_CLEAR);
1181
1182 #if 0
1183 malloc_verify(0);
1184 malloc_shutdown();
1185 #endif
1186
1187 exit(n);
1188 }
1189