This is nn.c in view mode; [Download] [Up]
/* * (c) Copyright 1990, Kim Fabricius Storm. All rights reserved. * * The nn user interface main program */ #include "config.h" #include "menu.h" #include "term.h" #include "keymap.h" #include "options.h" #include "proto.h" #include "articles.h" #ifdef USE_MALLOC_H #include <malloc.h> #endif import char *bin_directory; import int seq_cross_filtering, /* articles */ dont_sort_folders, /* folder.c */ dont_split_digests, dont_sort_articles, also_unsub_groups, /* group.c */ also_cross_postings, case_fold_search, /* match.c */ preview_window, fmt_linenum, fmt_rptsubj, /* menu.c */ show_article_date, first_page_lines, /* more.c */ keep_rc_backup, no_update, /* newsrc.c */ hex_group_args, /* sequence */ show_current_time, conf_dont_sleep; /* term.c */ export int article_limit = -1, also_read_articles = 0, also_full_digest = 0, batch_mode = 0, conf_auto_quit = 0, do_kill_handling = 1, group_name_args = 0, merged_menu = 0, prev_also_read = 1, repeat_group_query = 0, report_cost_on_exit = 1, show_motd_on_entry = 1, silent = 0, verbose = 0, Debug = 0; export int check_db_update = 12 /* HOURS */; static int nngrab_mode = 0, prompt_for_group = 0; static char *match_subject = NULL, *match_sender = NULL, *init_file = NULL; Option_Description(nn_options) { 'a', Int_Option(article_limit), 'B', Bool_Option(keep_rc_backup), 'd', Bool_Option(dont_split_digests), 'f', Bool_Option(dont_sort_folders), 'g', Bool_Option(prompt_for_group), 'G', Bool_Option(nngrab_mode), 'i', Bool_Option(case_fold_search), 'I', String_Option_Optional(init_file, NULL), 'k', Bool_Option(do_kill_handling), 'l', Int_Option(first_page_lines), 'L', Int_Option_Optional(fmt_linenum, 3), 'm', Bool_Option(merged_menu), 'n', String_Option(match_sender), 'N', Bool_Option(no_update), 'q', Bool_Option(dont_sort_articles), 'Q', Bool_Option(silent), 'r', Bool_Option(repeat_group_query), 's', String_Option(match_subject), 'S', Bool_Option(fmt_rptsubj), 'T', Bool_Option(show_current_time), 'w', Int_Option_Optional(preview_window, 5), 'W', Bool_Option(conf_dont_sleep), 'x', Int_Option_Optional(also_read_articles, -1), 'X', Bool_Option(also_unsub_groups), 'Z', Int_Option(Debug), '\0', }; static int report_number_of_articles = 0; static char *check_message_format = NULL; import int quick_unread_count; Option_Description(check_options) { 'Q', Bool_Option(silent), 'r', Bool_Option(report_number_of_articles), 'f', String_Option(check_message_format), 't', Bool_Option(verbose), 'v', Bool_Option(verbose), 'c', Bool_Option(quick_unread_count), '\0', }; /* program name == argv[0] without path */ char *pname; static int must_unlock = 0; static int nn_locked() { if (no_update) return 0; switch (who_am_i) { case I_AM_ADMIN: case I_AM_CHECK: case I_AM_POST: case I_AM_GREP: case I_AM_SPEW: return 0; /* will not update .newsrc */ } if (proto_lock(I_AM_NN, PL_SET)) { extern char proto_host[]; if (proto_host[0]) user_error("\nnn is running on host %s\nor .nn/LOCK should be removed.\n\n", proto_host); else user_error("\nAnother nn process is already running\n\n"); } must_unlock = 1; return 0; } #define setup_access() ACC_PARSE_VARIABLES flag_type parse_access_flags() { flag_type access_mode = 0; if (do_kill_handling) access_mode |= ACC_DO_KILL; if (also_cross_postings) access_mode |= ACC_ALSO_CROSS_POSTINGS; if (also_read_articles) access_mode |= ACC_ALSO_READ_ARTICLES; if (dont_split_digests) access_mode |= ACC_DONT_SPLIT_DIGESTS; if (also_full_digest) access_mode |= ACC_ALSO_FULL_DIGEST; if (dont_sort_articles) access_mode |= ACC_DONT_SORT_ARTICLES; return access_mode; } export group_header *jump_to_group = NULL; export int enter_last_read_mode = 1; static group_header *last_group_maint(last, upd) group_header *last; int upd; { group_header *gh; FILE *f; char *lg_file; char buf[256], *cp; lg_file = relative(nn_directory, "NEXTG"); if (upd) { if (last == NULL) unlink(lg_file); else { f = open_file(lg_file, OPEN_CREATE | MUST_EXIST); fprintf(f, "%s\n", last->group_name); fclose(f); } return NULL; } if (enter_last_read_mode == 0) goto none; f = open_file(lg_file, OPEN_READ); if (f == NULL) goto none; if (fgets(buf, 256, f)) { if (cp = strchr(buf, NL)) *cp = NUL; gh = lookup(buf); } fclose(f); if (gh == NULL || gh == last || !(gh->group_flag & G_SEQUENCE)) goto none; switch (enter_last_read_mode) { case 1: /* confirm if unread, skip if read */ if (gh->unread_count == 0) goto none; case 2: /* confirm any case */ prompt_line = Lines-1; prompt("Enter %s (%ld unread)? ", gh->group_name, gh->unread_count); if (!yes(0)) goto none; break; case 3: /* enter uncond if unread */ if (gh->unread_count == 0) goto none; case 4: /* enter uncond */ break; } return gh; none: return last; } static read_news(access_mode, mask) flag_type access_mode; char *mask; { register group_header *gh, *prev, *tmp, *after_loop; flag_type group_mode; int menu_cmd; int must_clear = 0, did_jump = 0; group_header *last_group_read = NULL; extern int menu(); prev = group_sequence; gh = group_sequence; after_loop = NULL; if (access_mode == 0 && !also_read_articles) { gh = last_group_maint(gh, 0); did_jump = gh != group_sequence; m_invoke(-2); } for (;;) { group_mode = access_mode; if (s_hangup) break; if (gh == NULL) { if (after_loop != NULL) { gh = after_loop; after_loop = NULL; if (gh->unread_count <= 0) group_mode |= ACC_ORIG_NEWSRC; goto enter_direct; } if (did_jump) { did_jump = 0; gh = group_sequence; continue; } if (must_clear && conf_auto_quit) { prompt("\1LAST GROUP READ. QUIT NOW?\1"); switch (yes(1)) { case 1: break; case 0: gh = group_sequence; default: after_loop = prev; continue; } } last_group_read = NULL; break; } if (gh->group_flag & G_UNSUBSCRIBED) { if (!also_unsub_groups) { gh = gh->next_group; continue; } } else if (!also_read_articles && gh->unread_count <= 0) { gh = gh->next_group; continue; } enter_direct: free_memory(); if (gh->group_flag & G_FOLDER) { menu_cmd = folder_menu(gh->group_name, 0); if (menu_cmd == ME_NO_REDRAW) { menu_cmd = ME_NO_ARTICLES; gh->last_db_article = 0; } } else { group_mode |= setup_access(); if (group_mode & ACC_ORIG_NEWSRC) gh->last_article = gh->first_article; menu_cmd = group_menu(gh, (article_number)(-1), group_mode, mask, menu); group_mode = access_mode; } if (menu_cmd != ME_NO_ARTICLES) { last_group_read = gh; after_loop = NULL; must_clear++; } switch (menu_cmd) { case ME_QUIT: /* or jump */ if (!jump_to_group) goto out; prev = jump_to_group; jump_to_group = NULL; did_jump = 1; /* fall thru */ case ME_PREV: tmp = gh; gh = prev; prev = tmp; if (gh->unread_count <= 0) group_mode |= ACC_ORIG_NEWSRC; else if (prev_also_read) group_mode |= ACC_MERGED_NEWSRC; goto enter_direct; case ME_NEXT: prev = gh; /* fall thru */ case ME_NO_ARTICLES: if (gh->group_flag & G_MERGE_HEAD) { do gh = gh->next_group; while (gh && (gh->group_flag & G_MERGE_SUB)); } else gh = gh->next_group; continue; } } out: if (access_mode == 0 && !also_read_articles) last_group_maint(last_group_read, 1); return must_clear; } static catch_up() { register group_header *gh; char answer[50]; int mode, must_help; import int newsrc_update_freq, novice; import int menu(); flag_type access_mode; access_mode = setup_access(); prt_unread("\nCatch-up on %u ? (auto)matically (i)nteractive "); fl; mode = 0; if (gets(answer)) { if (strncmp(answer, "auto", 4) == 0) { printf("\nUPDATING .newsrc FILE...."); fl; mode = -1; } else if (*answer == 'i') mode = 1; } if (mode == 0) { printf("\nNO UPDATE\n"); return; } newsrc_update_freq = 9999; must_help = novice; Loop_Groups_Sequence(gh) { if ((gh->group_flag & G_COUNTED) == 0) continue; if (mode < 0) { update_rc_all(gh, 0); continue; } again: if (must_help) { printf("\n y - mark all articles as read in current group\n"); printf(" n - do not update group\n"); printf(" r - read the group now\n"); printf(" U - unsubscribe to current group\n"); printf(" ? - this message\n"); printf(" q - quit\n\n"); must_help = 0; } printf("Update %s (%ld)? (ynrU?q) n\b", gh->group_name, (long)(gh->last_db_article - gh->last_article)); fl; if (gets(answer) == NULL || s_keyboard) *answer = 'q'; switch (*answer) { case 'q': putchar(NL); printf("Update rest? (yn) n\b"); fl; if (gets(answer) == NULL || *answer != 'y') return; mode = -1; break; case NUL: case 'n': continue; case 'r': raw(); group_menu(gh, (article_number)(-1), access_mode, (char *)NULL, menu); unset_raw(); clrdisp(); if (gh->unread_count > 0) goto again; continue; case 'U': update_rc_all(gh, 1); continue; case 'y': break; default: must_help = 1; goto again; } update_rc_all(gh, 0); } printf("DONE\n"); flush_newsrc(); } export char *mail_box = NULL; unread_mail(t) time_t t; { struct stat st; static time_t next = 0; static int any = 0; if (next > t) return any; next = t + 60; any = 0; if (mail_box == NULL || stat(mail_box, &st) != 0 || st.st_size == 0 || st.st_mtime < st.st_atime) return 0; any = 1; return 1; } #ifdef ACCOUNTING #ifndef STATISTICS #define STATISTICS 1 #endif #define NEED_ACCOUNT #else #ifdef AUTHORIZE #define NEED_ACCOUNT #endif #endif #ifdef STATISTICS static time_t usage_time = 0; static time_t last_tick = 0; stop_usage() { last_tick = 0; } time_t tick_usage() { register time_t now; now = cur_time(); /* * We ignore ticks > 2 minutes because the user has probably * just left the terminal inside nn and done something else */ if (last_tick > (now - 120)) usage_time += now - last_tick; last_tick = now; return now; } log_usage() { #ifdef ACCOUNTING account('U', report_cost_on_exit); #else if (usage_time < (STATISTICS * 60)) return; /* don't log short sessions */ usage_time /= 60; log_entry('U', "USAGE %d.%02d", usage_time/60, usage_time%60); #endif } #else stop_usage() { } time_t tick_usage() { return cur_time(); } log_usage() { } #endif #ifdef NEED_ACCOUNT account(option, report) char option; int report; { char *args[10], **ap; char buf1[16], buf2[16]; int ok; if (who_am_i != I_AM_NN && who_am_i != I_AM_POST) return 0; if (report) { putchar(CR); clrline(); fl; } ap = args; *ap++ = "nnacct"; if (report) *ap++ = "-r"; #ifdef STATISTICS sprintf(buf1, "-%c%ld", option, (long)usage_time/60); #else sprintf(buf1, "-%c0", option); #endif *ap++ = buf1; sprintf(buf2, "-W%d", who_am_i); *ap++ = buf2; *ap++ = NULL; ok = execute(relative(bin_directory, "nnacct"), args, 0); if (option == 'U' && report) putchar(NL); return ok; } #endif /* this will go into emacs_mode.c when it is completed someday */ emacs_mode() { printf("EMACS MODE IS NOT SUPPORTED YET.\n"); } static do_nnspew() { register group_header *gh; import int seq_cross_filtering; int ix; ix = 0; Loop_Groups_Header(gh) { if (gh->master_flag & M_IGNORE_GROUP) continue; gh->preseq_index = ++ix; } /* Only output each article once */ /* Must also use this mode when using nngrab! */ seq_cross_filtering = 1; Loop_Groups_Header(gh) { if (s_hangup) return 1; if (gh->master_flag & M_IGNORE_GROUP) continue; access_group(gh, gh->first_db_article, gh->last_db_article, ACC_DONT_SORT_ARTICLES | ACC_ALSO_FULL_DIGEST | ACC_SPEW_MODE, (char *)NULL); } return 0; } static prt_version() { printf("Release %s, Kim F. Storm, 1991\n\n", version_id); } display_motd(check) int check; { time_t last_motd, cur_motd; char *dot_motd, motd[FILENAME]; strcpy(motd, relative(lib_directory, "motd")); cur_motd = file_exist(motd, (char *)NULL); if (cur_motd == 0) return 0; if (!check) { display_file(motd, CLEAR_DISPLAY | CONFIRMATION); return 1; } dot_motd = relative(nn_directory, ".motd"); last_motd = file_exist(dot_motd, (char *)NULL); if (last_motd >= cur_motd) return 0; unlink(dot_motd); fclose(open_file(dot_motd, OPEN_CREATE|MUST_EXIST)); clrdisp(); so_printxy(0, 0, "Message of the day"); gotoxy(0, 2); prt_version(); display_file(motd, CONFIRMATION); return 1; } static do_db_check() { import char *news_active; time_t last_upd; last_upd = (cur_time() - master.last_scan) / (60 * 60); if (last_upd < check_db_update) return; /* too old - but is nnmaster the culprit? */ if (master.last_scan == file_exist(news_active, (char *)NULL)) printf("Notice: no news has arrived for the last %ld hours\n", last_upd); else printf("Notice: nnmaster has not updated database in %ld hours\n", last_upd); sleep(3); } clean_group(gh) group_header *gh; { } main(argc, argv) int argc; char *argv[]; { extern long unread_articles; extern char *program_name(); int say_welcome = 0, cmd; flag_type access_mode; char *mask; extern long initial_memory_break; extern char *sbrk(); initial_memory_break = (long)sbrk(0); #ifdef USE_MALLOC_H #ifdef MALLOC_MAXFAST mallopt(M_MXFAST, MALLOC_MAXFAST); #endif #ifdef MALLOC_FASTBLOCKS mallopt(M_NLBLKS, MALLOC_FASTBLOCKS); #endif #ifdef MALLOC_GRAIN mallopt(M_GRAIN, MALLOC_GRAIN); #endif #endif pname = program_name(argv); if (strcmp(pname, "nnadmin") == 0) { who_am_i = I_AM_ADMIN; } else if (strcmp(pname, "nnemacs") == 0) { who_am_i = I_AM_EMACS; } else if (strcmp(pname, "nncheck") == 0) { who_am_i = I_AM_CHECK; } else if (strcmp(pname, "nntidy") == 0) { who_am_i = I_AM_TIDY; } else if (strcmp(pname, "nngoback") == 0) { who_am_i = I_AM_GOBACK; } else if (strcmp(pname, "nngrep") == 0) { who_am_i = I_AM_GREP; } else if (strcmp(pname, "nnpost") == 0) { who_am_i = I_AM_POST; } else if (strcmp(pname, "nnbatch") == 0) { who_am_i = I_AM_NN; batch_mode = 1; } else { who_am_i = I_AM_NN; } if (who_am_i == I_AM_NN || (who_am_i == I_AM_ADMIN && argc == 1)) { if (!batch_mode && !isatty(0)) { if (who_am_i == I_AM_NN && argc == 2) { if (strcmp(argv[1], "-SPEW") == 0) { who_am_i = I_AM_SPEW; init_global(); goto nnspew; } } fprintf(stderr, "Input is not a tty\n"); exit(1); } } #ifdef AUTHORIZE init_execute(); if (cmd = account('P', 0)) { switch (cmd) { case 1: if (who_am_i == I_AM_POST) { printf("You are not authorized to post news\n"); exit(41); } /* ok_to_post = 0; */ cmd = 0; break; case 2: printf("You are not authorized to read news\n"); break; case 3: printf("You cannot read news at this time\n"); break; case 4: printf("Your news reading quota is exceeded\n"); break; default: printf("Bad authorization -- reason %d\n", cmd); break; } if (cmd) exit(40 + cmd); } #endif if ((say_welcome = init_global()) < 0) { printf("%s: nn has not been invoked to initialize user files\n", pname); nn_exit(1); } #ifdef NNTP nntp_check(); #endif init_key_map(); #ifndef AUTHORIZE init_execute(); #endif init_macro(); nnspew: open_master(OPEN_READ); switch (who_am_i) { case I_AM_NN: init_term(1); if (say_welcome) { extern int first_time_user; first_time_user = 1; display_file("adm.welcome", CLEAR_DISPLAY | CONFIRMATION); clrdisp(); } visit_init_file(0, argv[1]); if (show_motd_on_entry) if (display_motd(1)) silent = 1; init_answer(); group_name_args = parse_options(argc, argv, (char *)NULL, nn_options, " [group | [+]folder]..."); if (also_read_articles) { if (article_limit < 0) article_limit = also_read_articles; do_kill_handling = 0; no_update++; } if (match_subject) { mask = match_subject; access_mode = ACC_ON_SUBJECT; } else if (match_sender) { mask = match_sender; access_mode = ACC_ON_SENDER; } else { mask = NULL; access_mode = 0; } if (mask) { if (case_fold_search) fold_string(mask); no_update++; do_kill_handling = 0; } if (nngrab_mode) { seq_cross_filtering = 1; hex_group_args = 1; no_update = 1; } if (merged_menu) { no_update++; } if (!no_update && group_name_args > 0) no_update = only_folder_args(argv + 1); break; case I_AM_ADMIN: if (argc == 1) { init_term(1); visit_init_file(0, (char *)NULL); } admin_mode(argv[1]); nn_exit(0); case I_AM_CHECK: init_term(0); visit_init_file(0, (char *)NULL); silent = 0; /* override setting in init file */ group_name_args = parse_options(argc, argv, (char *)NULL, check_options, " [group]..."); no_update = 1; /* don't update .newsrc and LAST */ break; case I_AM_EMACS: silent = 1; break; case I_AM_TIDY: init_term(0); visit_init_file(0, (char *)NULL); group_name_args = opt_nntidy(argc, argv); break; case I_AM_GOBACK: init_term(0); visit_init_file(0, (char *)NULL); group_name_args = opt_nngoback(argc, &argv); break; case I_AM_GREP: init_term(0); visit_init_file(0, (char *)NULL); opt_nngrep(argc, argv); silent = 1; no_update = 1; break; case I_AM_POST: do_nnpost(argc, argv); nn_exit(0); case I_AM_SPEW: if (proto_lock(I_AM_SPEW, PL_SET) == 0) { cmd = do_nnspew(); proto_lock(I_AM_SPEW, PL_CLEAR); } else cmd = 1; /* don't interfere with other nnspew */ nn_exit(cmd); } if (!silent && who_am_i != I_AM_CHECK) prt_version(); if (nn_locked()) nn_exit(2); visit_rc_file(); if (group_name_args > 0) named_group_sequence(argv + 1); else normal_group_sequence(); if (who_am_i != I_AM_TIDY && who_am_i != I_AM_GOBACK) count_unread_articles(); switch (who_am_i) { case I_AM_EMACS: prt_unread("%U %G\n"); emacs_mode(); break; case I_AM_CHECK: if (report_number_of_articles) { prt_unread("%U"); break; } if (check_message_format) { if (unread_articles || !silent) prt_unread(check_message_format); } else if (!silent) { if (unread_articles || report_number_of_articles) prt_unread("There %i %u in %g\n"); else prt_unread((char *)NULL); } nn_exit( unread_articles ? 0 : 99 ); /*NOTREACHED*/ case I_AM_TIDY: do_tidy_newsrc(); break; case I_AM_GOBACK: do_goback(); break; case I_AM_NN: if (check_db_update) do_db_check(); if (unread_articles == 0 && group_name_args == 0 && !also_read_articles && !prompt_for_group) { if (!silent) prt_unread((char *)NULL); break; } if (do_kill_handling) do_kill_handling = init_kill(); if (prompt_for_group) { import int also_cross_postings; if (mask != NULL) user_error("Cannot use -s/-n with -g\n\r"); also_cross_postings = 1; do { raw(); clrdisp(); current_group = NULL; prompt_line = 2; cmd = goto_group(K_GOTO_GROUP, (article_header *)NULL, setup_access()); if (cmd == ME_NO_REDRAW) sleep(2); } while (repeat_group_query && cmd != ME_QUIT && cmd != ME_NO_REDRAW); clrdisp(); unset_raw(); break; } if (!no_update && article_limit == 0) { catch_up(); break; } if (merged_menu) { merge_and_read(access_mode | setup_access(), mask); clrdisp(); break; } if (read_news(access_mode, mask)) { clrdisp(); if (master.db_lock[0]) printf("Database has been locked:\n%s\n", master.db_lock); if (!also_read_articles && unread_articles > 0 && !silent && group_name_args == 0) prt_unread("There %i still %u in %g\n\n\r"); break; } gotoxy(0,Lines-1); if (group_name_args == 0) printf("No News\n"); else printf("\r\n"); break; case I_AM_GREP: do_grep(argv+1); break; } nn_exit(0); /*NOTREACHED*/ } /* * nn_exit() --- called whenever a program exits. */ nn_exit(n) { static int loop = 0; if (loop) exit(n); loop++; visual_off(); #ifdef NNTP nntp_cleanup(); #endif /* NNTP */ close_master(); flush_newsrc(); if (who_am_i == I_AM_NN) log_usage(); if (must_unlock) proto_lock(I_AM_NN, PL_CLEAR); exit(n); }
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.