Subject: v22i047: NN Newsreader, release 6.4, Part12/21 Newsgroups: comp.sources.unix Approved: rsalz@uunet.UU.NET X-Checksum-Snefru: ca2064a3 2388034d b708a9ba bb673f97 Submitted-by: "Kim F. Storm" Posting-number: Volume 22, Issue 47 Archive-name: nn6.4/part12 #! /bin/sh # This is a shell archive. Remove anything before this line, then feed it # into a shell via "sh file" or similar. To overwrite existing files, # type "sh file -c". # The tool that generated this appeared in the comp.sources.unix newsgroup; # send mail to comp-sources-unix@uunet.uu.net if you want that tool. # Contents: admin.c help/help.read inews/inews.c variable.c # Wrapped by storm@texas.dk on Sun May 6 18:19:49 1990 PATH=/bin:/usr/bin:/usr/ucb ; export PATH echo If this archive is complete, you will see the following message: echo ' "shar: End of archive 12 (of 22)."' if test -f 'admin.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'admin.c'\" else echo shar: Extracting \"'admin.c'\" \(21406 characters\) sed "s/^X//" >'admin.c' <<'END_OF_FILE' X/* X * (c) Copyright 1990, Kim Fabricius Storm. All rights reserved. X * X * nnadmin - nn administator program X */ X X#include X#include X#include "config.h" X#include "db.h" X#include "proto.h" X#include "term.h" X Ximport char X *master_directory, *db_directory, *db_data_directory, X *bin_directory, *news_directory, *news_lib_directory, X *log_file, *news_active, *pager, X group_path_name[]; X Ximport char *exec_chdir_to; X X Xstatic char *pre_input; Xstatic int verbose = 1; X Xextern int group_completion(); X X Xstatic get_cmd(prompt1, prompt2) Xchar *prompt1, *prompt2; X{ X char c; X X if (s_hangup) { X printf("\nnnmaster hangup\n"); X nn_exit(0); X } X X s_keyboard = 0; X X if (pre_input) { X if ((c = *pre_input++) == NUL) X nn_exit(0); X } else { X do { X if (prompt1) printf("\r%s\n", prompt1); X if (prompt2) printf("\r%s >>>", prompt2); X fl; X raw(); X c = get_c(); X unset_raw(); X if (c == K_interrupt) X s_keyboard++; X else X if (c == '!') { X putchar(NL); X if (exec_chdir_to != NULL) X printf("\n\rDirectory: %s", exec_chdir_to); X run_shell((char *)NULL, 0); X } else X printf("%c\n\n\r", c); X } while (c == '!'); X } X X if (islower(c)) X c = toupper(c); X X return c; X} X X Xstatic long get_entry(prompt_str, min_val, max_val) Xchar *prompt_str; Xlong min_val, max_val; X{ X char buf[100]; X long val; X X loop: X X printf("%s %ld..%ld (or all): ", prompt_str, min_val, max_val); X fl; X gets(buf); X if (buf[0] == 'a' || buf[0] == NUL) X return -1L; X X val = atol(buf); X if (val < min_val || val > max_val) goto loop; X X return val; X} X X Xstatic admin_confirm(action, must_confirm) Xchar *action; X{ X char buffer[100]; X X if (pre_input && !must_confirm) return 1; X X sprintf(buffer, "Confirm %s Y)es N)o", action); X X return get_cmd((char *)NULL, buffer) == 'Y'; X} X Xstatic char *get_groupname() X{ X char * groupname; X X raw(); X printf("\n\n\r"); X prompt_line = Lines - 2; X prompt("Group: "); X fl; X groupname = get_s(NONE, NONE, NONE, group_completion); X unset_raw(); X X putchar(NL); putchar(CR); X X if (groupname == NULL) return NULL; X X if (groupname[0]) return groupname; X X if (current_group == NULL) return NULL; X if (current_group->group_flag & G_FOLDER) return NULL; X X return current_group->group_name; X} X X Xstatic update_master() X{ X register group_header *gh; X int ngp; X X if (who_am_i != I_AM_ADMIN) { X printf("Can only perform (U)pdate as nnadmin\n"); X return; X } X X ngp = master.number_of_groups; X X open_master(OPEN_READ); X X if (master.number_of_groups != ngp) { X printf("\nNumber of groups changed from %d to %d\n", X ngp, master.number_of_groups); X } X X ngp = 0; X X Loop_Groups_Header(gh) X if (update_group(gh) == -1) ngp++; X X if (ngp) printf("There are %d blocked groups\n", ngp); X} X Xstatic find_files(gh) Xgroup_header *gh; X{ X char command[512], name[FILENAME], *db_data_path(); X X if (gh == NULL) { X if (db_data_directory == NULL) { X printf("Cannot list all files (they are scattered over the news partition)\n"); X return; X } X sprintf(command, "ls -l %s | %s", db_data_directory, pager); X } else X sprintf(command, "ls -l %s", db_data_path(name, gh, '*')); X system(command); X} X Xstatic master_status() X{ X int cur_group, nblocked, nignored; X long articles, disk_use; X register group_header *gh; X X printf("\nMaster:\n"); X printf(" initialized: %s\n", date_time(master.db_created)); X printf(" last_scan: %s\n", date_time(master.last_scan)); X printf(" last_size: %ld\n", (long)master.last_size); X printf(" no of groups: %d\n", master.number_of_groups); X X articles = disk_use = 0; X nblocked = nignored = 0; X X for (cur_group = 0; cur_group < master.number_of_groups; cur_group++) { X gh = &active_groups[cur_group]; X X#define DISK_BLOCKS(bytes) (((bytes) + 1023) / 1024) X X disk_use += DISK_BLOCKS(gh->index_write_offset); X disk_use += DISK_BLOCKS(gh->data_write_offset); X X articles += gh->last_db_article - gh->first_db_article + 1; X X if (gh->master_flag & M_BLOCKED) nblocked++; X if (gh->master_flag & M_IGNORE_GROUP) nignored++; X } X X printf("\n Articles: %ld\n", articles); X printf( " Disk usage: %ld k\n\n", disk_use); X X if (nblocked) printf("Blocked groups: %3d\n", nblocked); X if (nignored) printf("Ignored groups: %3d\n", nignored); X} X Xstatic dump_g_flag(gh) Xregister group_header *gh; X{ X printf("Flags: "); X if (gh->master_flag & M_BLOCKED) printf(" BLOCKED"); X if (gh->master_flag & M_EXPIRE) printf(" EXPIRE"); X if (gh->master_flag & M_MODERATED) printf(" MODERATED"); X if (gh->master_flag & M_CONTROL) printf(" CONTROL"); X if (gh->master_flag & M_NO_DIRECTORY) printf(" NO_DIRECTORY"); X if (gh->master_flag & M_ALWAYS_DIGEST) printf(" ALWAYS_DIGEST"); X if (gh->master_flag & M_NEVER_DIGEST) printf(" NEVER_DIGEST"); X if (gh->master_flag & M_INCLUDE_OLD) printf(" INCL_OLD"); X if (gh->master_flag & M_AUTO_RECOLLECT) printf(" RECOLLECT"); X if (gh->master_flag & M_AUTO_ARCHIVE) printf(" ARCHIVE"); X if (gh->master_flag & M_IGNORE_GROUP) printf(" IGNORE"); X if (gh->master_flag & M_VALID) printf(" VALID"); X printf("\n"); X} X Xstatic dump_m_entry(gh) Xregister group_header *gh; X{ X update_group(gh); X X printf("\n%s\t%d\n", gh->group_name, gh->group_num); X printf("first/last art: %06ld %06d\n", X gh->first_db_article, gh->last_db_article); X printf(" active info: %06ld %06d\n", X gh->first_a_article, gh->last_a_article); X printf("Offsets: index->%ld, data->%ld\n", X gh->index_write_offset, X gh->data_write_offset); X if (gh->master_flag & M_AUTO_ARCHIVE) X printf("Archive file: %s\n", gh->archive_file); X if (gh->master_flag) X dump_g_flag(gh); X} X X#define valerr( xxx , tp) { \ X if (verbose) { printf xxx ; fl; } \ X err_type = tp; \ X goto err; \ X} X Xstatic validate_group(gh) Xgroup_header *gh; X{ X FILE *data, *ix; X off_t data_offset, next_offset; X cross_post_number cross_post; X article_number cur_article; X int n, err_type; X X data = ix = NULL; X X if (gh->first_db_article == (gh->last_db_article + 1) X && gh->index_write_offset == 0) X return 1; X X if (verbose) { printf("\r%s: ", gh->group_name); clrline(); } X X if (init_group(gh) <= 0) { X printf("NO DIRECTORY (ok)"); X return 1; /* no directory/ignored */ X } X X update_group(gh); X X if (gh->master_flag & M_BLOCKED) { X if (verbose) printf("BLOCKED (ok)\n"); X return 1; X } X X /* X * Check for major inconcistencies. X * Sometimes, news expire will reset article numbers X * to start from 0 again X */ X X if (gh->last_db_article == 0) { X return 1; X } X X#ifdef RENUMBER_DANGER X if (gh->first_a_article > (gh->last_db_article + 1) || X gh->last_db_article > gh->last_a_article || X gh->first_db_article > gh->first_a_article) { X X if (verbose) X printf("RENUMBERING OF ARTICLES (active=%ld..%ld master=%ld..%ld)", X gh->first_a_article, gh->last_a_article, X gh->first_db_article, gh->last_db_article); X err_type = 99; X goto err; X } X#endif X X ix = open_data_file(gh, 'x', OPEN_READ); X if (ix == NULL) valerr(("NO INDEX FILE"), 1); X X data = open_data_file(gh, 'd', OPEN_READ); X if (data == NULL) valerr(("NO DATA FILE"), 2); X X cur_article = gh->first_db_article - 1; X X while (cur_article <= gh->last_db_article) { X if (s_hangup || s_keyboard) goto out; X X data_offset = ftell(data); X X switch (db_read_art(data)) { X case 0: X if (data_offset == gh->data_write_offset) goto out; X valerr(("No header for article # %ld", (long)cur_article+1), 3); X case 1: X break; X case -1: X valerr(("END OF FILE on DATA FILE"), 4); X } X X if (db_hdr.dh_number <= 0 || cur_article > db_hdr.dh_number) X valerr(("OUT OF SEQUENCE: %ld after %ld", (long)db_hdr.dh_number, (long)cur_article), 11); X X if (cur_article < db_hdr.dh_number) { X if (db_data.dh_type == DH_SUB_DIGEST) X valerr(("NO DIGEST HEADER: %ld", (long)db_hdr.dh_number), 5); X X do { X cur_article++; X if (!db_read_offset(ix, &next_offset)) X valerr(("NO INDEX FOR ARTICLE %ld", (long)cur_article), 6); X X if (data_offset != next_offset) X valerr(("OFFSET ERROR: %ld: %ld != %ld", (long)cur_article, (long)data_offset, (long)next_offset), 7); X } while (cur_article < db_hdr.dh_number); X } X X for (n = 0; n < db_hdr.dh_cross_postings; n++) { X cross_post = NETW_CROSS_INT(db_data.dh_cross[n]); X if (cross_post < master.number_of_groups) continue; X valerr(("CROSS POST RANGE ERROR: %ld (article # %ld)", (long)cross_post, (long)cur_article), 8); X } X } X X out: X if (!s_keyboard && !s_hangup) { X data_offset = ftell(data); X if (data_offset != gh->data_write_offset) X valerr(("DATA OFFSET %ld != %ld", (long)gh->data_write_offset, (long)data_offset), 9); X data_offset = ftell(ix); X if (data_offset != gh->index_write_offset) X valerr(("INDEX OFFSET %ld != %ld", (long)gh->index_write_offset, (long)data_offset), 10); X } X X fclose(data); X fclose(ix); X if (verbose) printf("OK"); X return 1; X X err: X if (data != NULL) fclose(data); X if (ix != NULL) fclose(ix); X log_entry('V', "%s: database error %d", gh->group_name, err_type); X if (verbose) { X putchar(NL); X dump_m_entry(gh); X } X X if (!pre_input) { X ding(); X X for (;;) { X switch (get_cmd((char *)NULL, X"\nRepair group Y)es N)o E)nter Q)uit")) { X case 'Y': X break; X case 'N': X return 0; X case 'Q': X s_keyboard++; X return 0; X case 'E': X group_admin(gh); X continue; X default: X continue; X } X break; X } X } X X send_master(SM_RECOLLECT, gh, SP, 0L); X return 0; X} X Xstatic dump_group(gh) Xgroup_header *gh; X{ X FILE *data, *ix; X off_t offset; X cross_post_number cross_post; X article_number first_article; X int n; X X if (init_group(gh) <= 0) X printf("cannot access group %s\n", gh->group_name); X X update_group(gh); X X first_article = get_entry("First article", X (long)gh->first_db_article, X (long)gh->last_db_article); X X if (first_article < 0) first_article = gh->first_db_article; X if (first_article <= 0) first_article = 1; X X ix = open_data_file(gh, 'x', OPEN_READ); X data = open_data_file(gh, 'd', OPEN_READ); X if (ix == NULL || data == NULL) goto err; X X fseek(ix, get_index_offset(gh, first_article), 0); X if (!db_read_offset(ix, &offset)) goto err; X fseek(data, offset, 0); X X clrdisp(); X pg_init(1, 1); X X for (;;) { X if (s_hangup || s_keyboard) break; X X if (pg_scroll(6)) { X s_keyboard = 1; X break; X } X X offset = ftell(data); X X switch (db_read_art(data)) { X case 0: X goto out; X case 1: X break; X case -1: X goto err; X } X X printf("\noffset = %ld, article # = %ld", X (long)offset, (long)(db_hdr.dh_number)); X X switch (db_data.dh_type) { X case DH_DIGEST_HEADER: X printf(" (digest header)\n"); X break; X case DH_SUB_DIGEST: X printf(" (digest sub-article)\n"); X break; X case DH_NORMAL: X putchar(NL); X break; X } X X if (db_hdr.dh_cross_postings) { X printf("xpost(%d):", db_hdr.dh_cross_postings); X X for (n = 0; n < db_hdr.dh_cross_postings; n++) { X cross_post = NETW_CROSS_INT(db_data.dh_cross[n]); X printf(" %d", cross_post); X } X putchar(NL); X } X X printf("ts=%lu hp=%ld, fp=+%d, lp=%ld, ref=%d%s, lines=%d\n", X (long unsigned)db_hdr.dh_date, (long)db_hdr.dh_hpos, X (int)db_hdr.dh_fpos, (long)db_hdr.dh_lpos, X db_hdr.dh_replies & 0x7f, X (db_hdr.dh_replies & 0x80) ? "+Re" : "", X db_hdr.dh_lines); X X if (db_hdr.dh_sender_length) X printf("Sender(%d): %s\n", db_hdr.dh_sender_length, db_data.dh_sender); X else X printf("No sender\n"); X X if (db_hdr.dh_subject_length) X printf("Subj(%d): %s\n", db_hdr.dh_subject_length, db_data.dh_subject); X else X printf("No subject\n"); X } X X out: X if (!s_keyboard && !s_hangup && ftell(data) != gh->data_write_offset) X goto err; X X fclose(data); X fclose(ix); X return 1; X X err: X if (data != NULL) fclose(data); X if (ix != NULL) fclose(ix); X printf("\n*** DATABASE INCONSISTENCY DETECTED - run V)alidate\n"); X return 0; X} X Xstatic kill_master(term) Xint term; X{ X switch (proto_lock(I_AM_MASTER, term ? PL_TERMINATE : PL_WAKEUP_SOFT)) { X case 1: X if (verbose) X printf("sent %s signal to master\n", term ? "termination" : "wakeup"); X break; X case 0: X printf("cannot signal master\n"); X break; X case -1: X if (verbose) printf("master is not running\n"); X break; X } X} X Xstatic master_admin() X{ X register char c; X int cur_group; X long value; X register group_header *gh; X X exec_chdir_to = db_directory; X X for (;;) { X switch (c = get_cmd( X"\nD)ump F)iles G)roup K)ill O)ptions S)tat T)race", X"MASTER")) { X X case 'G': X cur_group = (int)get_entry("Group number", X 0L, (long)(master.number_of_groups - 1)); X if (cur_group >= 0) X dump_m_entry(&active_groups[cur_group]); X break; X X case 'D': X do { X c = get_cmd( X"\nA)ll B)locked E)mpty H)oles I)gnored N)on-empty V)alid in(W)alid", X"DUMP"); X cur_group = -1; X pg_init(1, 1); X X while (++cur_group < master.number_of_groups) { X if (s_keyboard || s_hangup) break; X X gh = &active_groups[cur_group]; X#define NODUMP(cond) if (cond) continue; break X switch (c) { X case 'N': X NODUMP(gh->index_write_offset == 0); X case 'E': X NODUMP(gh->index_write_offset != 0); X case 'H': X NODUMP(gh->first_a_article >= gh->first_db_article); X case 'B': X NODUMP((gh->master_flag & M_BLOCKED) == 0); X case 'I': X NODUMP((gh->master_flag & M_IGNORE_GROUP) == 0); X case 'V': X NODUMP((gh->master_flag & M_VALID) == 0); X case 'W': X NODUMP(gh->master_flag & M_VALID); X default: X s_keyboard = 1; continue; X } X X if (pg_scroll(6)) break; X dump_m_entry(gh); X } X } while (!s_keyboard && !s_hangup); X break; X X case 'F': X find_files((group_header *)NULL); X break; X X case 'O': X c = get_cmd("r)epeat_delay e)xpire_level", "OPTION"); X if (c != 'R' && c != 'E') break; X value = get_entry("Option value", 1L, 10000L); X if (value < 0) break; X send_master(SM_SET_OPTION, (group_header *)NULL, tolower(c), value); X break; X X case 'S': X master_status(); X break; X X case 'T': X send_master(SM_SET_OPTION, (group_header *)NULL, 't', -1L); X break; X X case 'K': X if (admin_confirm("Stop nn Master", 0)) X kill_master(1); X break; X X default: X exec_chdir_to = NULL; X return; X } X } X} X X Xstatic log_admin() X{ X char command[FILENAME + 100], c; X X if (pre_input && *pre_input == NUL) { X c = SP; X goto log_tail; X } X X loop: X X c = get_cmd( X"\n1-9)tail A)dm C)ollect E)rr N)ntp G)roup R)eport e(X)pire .)all @)clean", X"LOG"); X X if (c < SP || c == 'Q') return; X X if (c == '@') { X if (admin_confirm("Truncation", 0)) { X sprintf(command, "%s.old", log_file); X unlink(command); X if (link(log_file, command) < 0) goto tr_failed; X if (unlink(log_file) < 0) { X unlink(command); X goto tr_failed; X } X log_entry('A', "Log Truncated"); X chmod(log_file, 0666); X } X return; X X tr_failed: X printf("Truncation failed -- check permissions etc.\n"); X goto loop; X } X X if (c == 'G') { X char *groupname; X X if ((groupname = get_groupname()) == NULL) goto loop; X sprintf(command, "fgrep '%s' %s | %s", X groupname, log_file, pager); X system(command); X X goto loop; X } X X log_tail: X if (c == '$' || c == SP || isdigit(c)) { X int n; X X n = isdigit(c) ? 10 * (c - '0') : 10; X sprintf(command, "tail -%d %s", n, log_file); X system(command); X goto loop; X } X X if (c == '*') { X c = '.'; X } X X sprintf(command, "grep '^%c:' %s | %s", c, log_file, pager); X system(command); X X goto loop; X} X X Xstatic flag_admin(gh, mode_str, set_flag) Xgroup_header *gh; Xchar *mode_str; Xint set_flag; X{ X char buffer[50]; X int new_flag = 0; X X putchar(NL); X X dump_g_flag(gh); X X sprintf(buffer, "%s FLAG", mode_str); X X switch (get_cmd( X"\nA)lways_digest N)ever_digest M)oderated C)ontrol no_(D)ir", Xbuffer)) { X X default: X return; X X case 'M': X new_flag = M_MODERATED; X break; X X case 'C': X new_flag = M_CONTROL; X break; X X case 'D': X new_flag = M_NO_DIRECTORY; X break; X X case 'A': X new_flag = M_ALWAYS_DIGEST; X break; X X case 'N': X new_flag = M_NEVER_DIGEST; X break; X } X X if (new_flag & (M_CONTROL | M_NO_DIRECTORY)) X if (!admin_confirm("Flag Change", 0)) X new_flag = 0; X X if (new_flag) { X if (set_flag) { X if (gh->master_flag & new_flag) X new_flag = 0; X else { X send_master(SM_SET_FLAG, gh, 's', (long)new_flag); X gh->master_flag |= new_flag; X } X } else { X if ((gh->master_flag & new_flag) == 0) X new_flag = 0; X else { X send_master(SM_SET_FLAG, gh, 'c', (long)new_flag); X gh->master_flag &= ~new_flag; X } X } X } X X if (new_flag == 0) X printf("NO CHANGE\n"); X else X dump_g_flag(gh); X} X X Xstatic rmgroup(gh) Xgroup_header *gh; X{ X char command[FILENAME*2]; X X if (!file_exist(news_active, "w")) { X printf("Not privileged to run rmgroup\n"); X return; X } X X sprintf(command, "%s/rmgroup %s", news_lib_directory, gh->group_name); X system(command); X any_key(0); X gh->master_flag &= ~M_VALID; /* just for nnadmin */ X gh->master_flag |= M_IGNORE_A; X} X Xstatic group_admin(gh) Xregister group_header *gh; X{ X char *groupname, gbuf[FILENAME], dirbuf[FILENAME]; X X if (gh != NULL) goto have_group; X X new_group: X if ((groupname = get_groupname()) == NULL) return; X X gh = lookup(groupname); X if (gh == NULL) { X printf("No group named %s\n", groupname); X goto new_group; X } X X have_group: X if (!use_nntp && init_group(gh)) { X strcpy(dirbuf, group_path_name); X dirbuf[strlen(dirbuf)-1] = NUL; X exec_chdir_to = dirbuf; X } X X sprintf(gbuf, "GROUP %s", gh->group_name); X X for (;;) { X switch (get_cmd( X"\nD)ata E)xpire F)iles G)roup H)eader R)ecollect V)alidate Z)ap", Xgbuf)) { X case 'D': X dump_group(gh); X break; X X case 'V': X verbose = 1; X validate_group(gh); X putchar(NL); X break; X X case 'H': X dump_m_entry(gh); X break; X X case 'F': X find_files(gh); X break; X X case 'S': X flag_admin(gh, "Set", 1); X break; X X case 'C': X flag_admin(gh, "Clear", 0); X break; X X case 'R': X if (admin_confirm("Recolletion of Group", 0)) X send_master(SM_RECOLLECT, gh, SP, 0L); X break; X X case 'Z': X if (admin_confirm("Remove Group (run rmgroup)", 1)) X rmgroup(gh); X break; X X case 'E': X if (admin_confirm("Expire Group", 0)) X send_master(SM_EXPIRE, gh, SP, 0L); X break; X X case 'G': X goto new_group; X X default: X exec_chdir_to = NULL; X return; X } X } X} X X Xstatic show_config() X{ X#ifdef NNTP X extern char nntp_server[]; X#endif X X printf("\nConfiguration:\n\n"); X printf("BIN: %s\nLIB: %s\nNEWS SPOOL: %s\nNEWS LIB: %s\n", X bin_directory, X lib_directory, X news_directory, X news_lib_directory); X X printf("DB: %s %s\n", db_directory, X db_data_directory != NULL ? db_data_directory : X "(per-group files in group directories)"); X X printf("ACTIVE: %s\n", news_active); X X#ifdef NNTP X if (use_nntp) X printf("NNTP ACTIVE. server=%s\n", nntp_server); X else X printf("NNTP NOT ACTIVE\n"); X#endif X X#ifdef NETWORK_DATABASE X printf("Database is machine independent (network format).\n"); X#ifdef NETWORK_BYTE_ORDER X printf("Local system assumes to use network byte order\n"); X#endif X#else X printf("Database format is machine dependent (byte order and alignment)\n"); X#endif X printf("Database version: %d\n", NNDB_MAGIC & 0xff); X X#ifdef STATISTICS X printf("Recording usage statistics\n"); X#else X printf("No usage statistics are recorded\n"); X#endif X X printf("Mail delivery program: %s\n", REC_MAIL); X#ifdef APPEND_SIGNATURE X printf("Query for appending .signature ENABLED\n"); X#else X printf("Query for appending .signature DISABLED\n"); X#endif X X printf("Max pathname length is %d bytes\n", FILENAME-1); X} X X Xadmin_mode(input_string) Xchar *input_string; X{ X register group_header *gh; X int was_raw = unset_raw(); X X if (input_string && *input_string) { X pre_input = input_string; X } else { X pre_input = NULL; X printf("\nMaster is%s running\n", X proto_lock(I_AM_MASTER, PL_CHECK) < 0 ? " NOT" : ""); X } X X for (;;) { X switch(get_cmd( X"\nC)onf E)xpire G)roups I)nit L)og M)aster Q)uit S)tat U)pdate V)alidate W)akeup", X"ADMIN")) { X X case 'M': X master_admin(); X break; X X case 'W': X kill_master(0); X break; X X case 'E': X if (admin_confirm("Expire All Groups", 1)) X send_master(SM_EXPIRE, (group_header *)NULL, SP, 0L); X break; X X case 'I': X if (admin_confirm("INITIALIZATION, i.e. recollect all groups", 1)) X send_master(SM_RECOLLECT, (group_header *)NULL, SP, 0L); X break; X X case 'G': X group_admin((group_header *)NULL); X break; X X case 'L': X log_admin(); X break; X X case 'S': X master_status(); X break; X X case 'C': X show_config(); X break; X X case 'U': X update_master(); X break; X X case 'Z': X verbose = 0; X /* FALL THRU */ X X case 'V': X Loop_Groups_Sorted(gh) { X if (s_hangup || s_keyboard) break; X validate_group(gh); X } X verbose = 1; X break; X X case '=': X verbose = !verbose; X break; X X case 'Q': X if (was_raw) raw(); X return; X } X } X} END_OF_FILE if test 21406 -ne `wc -c <'admin.c'`; then echo shar: \"'admin.c'\" unpacked with wrong size! fi # end of 'admin.c' fi if test -f 'help/help.read' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'help/help.read'\" else echo shar: Extracting \"'help/help.read'\" \(13 characters\) sed "s/^X//" >'help/help.read' <<'END_OF_FILE' X;:Chelp.more END_OF_FILE if test 13 -ne `wc -c <'help/help.read'`; then echo shar: \"'help/help.read'\" unpacked with wrong size! fi # end of 'help/help.read' fi if test -f 'inews/inews.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'inews/inews.c'\" else echo shar: Extracting \"'inews/inews.c'\" \(8001 characters\) sed "s/^X//" >'inews/inews.c' <<'END_OF_FILE' X#ifndef lint Xstatic char *sccsid = "@(#)inews.c 1.16 (Berkeley) 8/27/89"; X#endif X X/* X * Itty-bitty inews for talking to remote server. X * Simply accept input on stdin (or via a named file) and dump this X * to the server; add a From: and Path: line if missing in the original. X * Print meaningful errors from the server. X * Limit .signature files to MAX_SIGNATURE lines. X * No processing of command line options. X * X * Original by Steven Grady , with thanks from X * Phil Lapsley X * Send bug reports to Stan Barber X */ X X#include X#include X#include X#include "conf.h" X#include "nntp.h" X#ifndef FOR_NN X#ifdef USG X#include X#else not USG X#include X#endif not USG X#endif X X#define MAX_SIGNATURE 4 X Xextern FILE *ser_wr_fp; X Xchar host_name[256]; X Xmain(argc, argv) Xint argc; Xchar *argv[]; X{ X char line[NNTP_STRLEN], s[NNTP_STRLEN]; X int seen_fromline, in_header, seen_header; X int response; X char *server; X char *getserverbyfile(); X register char *cp; X X ++argv; X while (argc > 1) X if (*argv[0] == '-') { X ++argv; X --argc; X } else X break; X X if (argc > 1) { X if (freopen(*argv, "r", stdin) == NULL) { X perror(*argv); X exit(1); X } X } X X uname(host_name); X X server = getserverbyfile(SERVER_FILE); X if (server == NULL) { X fprintf(stderr, X "Can't get the name of the news server from %s.\n", X SERVER_FILE); X fprintf(stderr, X "Either fix this file, or put NNTPSERVER in your enviroment.\n"); X exit(1); X } X X response = server_init(server); X if (response < 0) { X printf("Couldn't connect to %s news server, try again later.\n", X server); X exit(1); X } X X if (handle_server_response(response, server) < 0 X || response == OK_NOPOST) { X close_server(); X exit(1); X } X X put_server("POST"); X (void) get_server(line, sizeof(line)); X if (*line != CHAR_CONT) { X if (atoi(line) == ERR_NOPOST) { X close_server(); X fprintf(stderr, X "Sorry, you can't post from this machine.\n"); X exit(1); X } else { X close_server(); X fprintf(stderr, "Remote error: %s\n", line); X exit(1); X } X } X X in_header = 1; X seen_header = 0; X seen_fromline = 0; X X while (gets(s) != NULL) { X if (s[0] == '.') /* Single . is eof, so put in extra one */ X (void) fputc('.', ser_wr_fp); X if (in_header && strneql(s, "From:", sizeof("From:")-1)) { X seen_header = 1; X seen_fromline = 1; X } X if (in_header && s[0] == '\0') { X if (seen_header) { X in_header = 0; X if (!seen_fromline) X gen_frompath(); X } else { X continue; X } X } else if (in_header) { X if (valid_header(s)) X seen_header = 1; X else X continue; X } X fprintf(ser_wr_fp, "%s\r\n", s); X } X X append_signature(); X X fprintf(ser_wr_fp, ".\r\n"); X (void) fflush(ser_wr_fp); X (void) get_server(line, sizeof(line)); X if (*line != CHAR_OK) { X if (atoi(line) == ERR_POSTFAIL) { X close_server(); X printf("Article not accepted by server; not posted.\n"); X for (cp = line + 4; *cp && *cp != '\r'; cp++) X if (*cp == '\\') X putchar('\n'); X else X putchar(*cp); X exit(1); X } else { X close_server(); X fprintf(stderr, "Remote error: %s\n", line); X exit(1); X } X } X X /* X * Close server sends the server a X * "quit" command for us, which is why we don't send it. X */ X X close_server(); X X exit(0); X} X X/* X * append_signature -- append the person's .signature file if X * they have one. Limit .signature to MAX_SIGNATURE lines. X * The rn-style DOTDIR environmental variable is used if present. X */ X Xappend_signature() X{ X char line[256], sigfile[256]; X char *cp; X struct passwd *passwd; X FILE *fp; X char *index(), *getenv(); X int count = 0; X char *dotdir; X X passwd = getpwuid(getuid()); X if (passwd == NULL) X return; X#ifdef DO_DOTDIR X if ((dotdir = getenv("DOTDIR")) == NULL) X#endif X { X dotdir = passwd->pw_dir; X } X X if (dotdir[0] == '~') { X (void) strcpy(sigfile, passwd->pw_dir); X (void) strcat(sigfile, &dotdir[1]); X } else { X (void) strcpy(sigfile, dotdir); X } X (void) strcat(sigfile, "/"); X (void) strcat(sigfile, ".signature"); X X#ifdef DEBUG X fprintf(stderr,"sigfile = '%s'\n", sigfile); X#endif X X fp = fopen(sigfile, "r"); X if (fp == NULL) X return; X X#ifdef DEBUG X fprintf(stderr,"sigfile opened OK\n"); X#endif X X fprintf(ser_wr_fp, "--\r\n"); X while (fgets(line, sizeof (line), fp)) { X count++; X if (count > MAX_SIGNATURE) { X fprintf(stderr, X "Warning: .signature files should be no longer than %d lines.\n", X MAX_SIGNATURE); X fprintf(stderr, X "(Only %d lines of your .signature were posted.)\n", X MAX_SIGNATURE); X break; X } X if (cp = index(line, '\n')) X *cp = '\0'; X fprintf(ser_wr_fp, "%s\r\n", line); X } X (void) fclose(fp); X#ifdef DEBUG X printf(".signature appended (from %s)\n", sigfile); X#endif X} X X X/* X * gen_frompath -- generate From: and Path: lines, in the form X * X * From: user@host.domain (full_name) X * Path: host!user X * X * This routine should only be called if the message doesn't have X * a From: line in it. X */ X Xgen_frompath() X{ X char *full_name; X char *cp; X struct passwd *passwd; X char *index(), *getenv(); X X passwd = getpwuid(getuid()); X X full_name = getenv("NAME"); X if (full_name == NULL) { X full_name = passwd->pw_gecos; X if ((cp = index(full_name, ','))) X *cp = '\0'; X } X X#ifdef DOMAIN X#ifdef HIDDENNET X fprintf(ser_wr_fp, "From: %s@%s (", X passwd->pw_name, X DOMAIN); X#else /* HIDDENNET */ X X /* A heuristic to see if we should tack on a domain */ X X cp = index(host_name, '.'); X if (cp) X fprintf(ser_wr_fp, "From: %s@%s (", X passwd->pw_name, X host_name); X else X fprintf(ser_wr_fp, "From: %s@%s.%s (", X passwd->pw_name, X host_name, X DOMAIN); X#endif /* HIDDENNET */ X#else X fprintf(ser_wr_fp, "From: %s@%s (", X passwd->pw_name, X host_name); X#endif X X for (cp = full_name; *cp != '\0'; ++cp) X if (*cp != '&') X putc(*cp, ser_wr_fp); X else { /* Stupid & hack. God damn it. */ X putc(toupper(passwd->pw_name[0]), ser_wr_fp); X fprintf(ser_wr_fp, passwd->pw_name+1); X } X X fprintf(ser_wr_fp, ")\r\n"); X X#ifdef HIDDENNET X /* Only the login name - nntp server will add uucp name */ X fprintf(ser_wr_fp, "Path: %s\r\n", passwd->pw_name); X#else /* HIDDENNET */ X fprintf(ser_wr_fp, "Path: %s!%s\r\n", host_name, passwd->pw_name); X#endif /* HIDDENNET */ X} X X X/* X * strneql -- determine if two strings are equal in the first n X * characters, ignoring case. X * X * Parameters: "a" and "b" are the pointers X * to characters to be compared. X * "n" is the number of characters to compare. X * X * Returns: 1 if the strings are equal, 0 otherwise. X * X * Side effects: None. X */ X Xstrneql(a, b, n) Xregister char *a, *b; Xint n; X{ X char lower(); X X while (n && lower(*a) == lower(*b)) { X if (*a == '\0') X return (1); X a++; X b++; X n--; X } X if (n) X return (0); X else X return (1); X} X X/* X * lower -- convert a character to lower case, if it's X * upper case. X * X * Parameters: "c" is the character to be X * converted. X * X * Returns: "c" if the character is not X * upper case, otherwise the lower X * case eqivalent of "c". X * X * Side effects: None. X */ X Xchar lower(c) Xregister char c; X{ X if (isascii(c) && isupper(c)) X c = c - 'A' + 'a'; X return(c); X} X X X/* X * valid_header -- determine if a line is a valid header line X * X * Parameters: "h" is the header line to be checked. X * X * Returns: 1 if valid, 0 otherwise X * X * Side Effects: none X * X */ X Xint valid_header(h) Xregister char *h; X{ X char *index(); X char *colon, *space; X X /* X * blank or tab in first position implies this is a continuation header X */ X if (h[0] == ' ' || h[0] == '\t') X return (1); X X /* X * just check for initial letter, colon, and space to make X * sure we discard only invalid headers X */ X colon = index(h, ':'); X space = index(h, ' '); X if (isalpha(h[0]) && colon && space == colon + 1) X return (1); X X /* X * anything else is a bad header -- it should be ignored X */ X return (0); X} END_OF_FILE if test 8001 -ne `wc -c <'inews/inews.c'`; then echo shar: \"'inews/inews.c'\" unpacked with wrong size! fi # end of 'inews/inews.c' fi if test -f 'variable.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'variable.c'\" else echo shar: Extracting \"'variable.c'\" \(20923 characters\) sed "s/^X//" >'variable.c' <<'END_OF_FILE' X/* X * (c) Copyright 1990, Kim Fabricius Storm. All rights reserved. X * X * Variabel setting and display X */ X X#include "config.h" X#include "keymap.h" X Ximport in_init; X Ximport char /* string variables */ X *bak_suffix, X *bug_address, X *decode_header_file, X *default_distribution, X *default_save_file, X *editor_program, X *extra_mail_headers, X *extra_news_headers, X *header_lines, X *folder_directory, X included_mark[], X *mail_box, X *mail_record, X *mail_script, X *mailer_program, X attributes[], X *newsrc_file, X *news_record, X *news_script, X *pager, X patch_command[], X printer[], X *response_dflt_answer, X *save_counter_format, X *spell_checker, X unshar_command[], X *unshar_header_file, X *user_shell; X Ximport int /* boolean variables */ X also_cross_postings, X also_subgroups, X append_sig_mail, X append_sig_post, X auto_junk_seen, X auto_preview_mode, X case_fold_search, X compress_mode, X conf_append, X conf_auto_quit, X conf_create, X conf_dont_sleep, X conf_group_entry, X conf_junk_seen, X delay_redraw, X do_kill_handling, X dont_sort_articles, X dont_sort_folders, X dont_split_digests, X edit_patch_command, X edit_print_command, X edit_unshar_command, X empty_answer_check, X flow_control, X flush_typeahead, X fmt_rptsubj, X include_art_id, X include_full_header, X keep_rc_backup, X keep_unsubscribed, X keep_unsub_long, X long_menu, X macro_debug, X mailer_pipe_input, X mark_overlap, X match_parts_equal, X monitor_mode, X novice, X preview_mark_read, X query_signature, X quick_save, X quick_unread_count, X repeat_group_query, X report_cost_on_exit, X retain_seen_status, X save_report, X scroll_clear_page, X select_leave_next, X select_on_sender, X seq_cross_filtering, X shell_restrictions, X show_article_date, X show_current_time, X silent, X slow_mode, X suggest_save_file, X tidy_newsrc, X use_mail_folders, X use_mmdf_folders, X use_selections, X use_visible_bell; X Ximport int /* integer variables */ X also_read_articles, X article_limit, X conf_entry_limit, X collapse_subject, X Columns, X data_bits, X Debug, X decode_skip_prefix, X entry_message_limit, X expired_msg_delay, X first_page_lines, X fmt_linenum, X Lines, X match_skip_prefix, X min_pv_window, X new_group_action, X newsrc_update_freq, X orig_to_include_mask, X overlap, X preview_continuation, X preview_window, X re_layout, X response_check_pause, X retry_on_error, X save_counter_offset, X slow_speed, X sort_mode, X subject_match_limit, X wrap_headers; X X#ifdef NNTP Ximport char *nntp_cache_dir; Ximport int nntp_cache_size, nntp_debug; X#endif X Ximport key_type /* key strokes */ X comp1_key, X comp2_key, X help_key, X erase_key, X delword_key, X kill_key; X X#undef STR X#undef BOOL X#undef INT X#undef KEY X#undef SPEC X#undef SAFE X#undef INIT X X X#define V_STRING 0x1000 X#define V_BOOLEAN 0x2000 X#define V_INTEGER 0x3000 X#define V_KEY 0x4000 X#define V_SPECIAL 0x5000 X X#define V_SAFE 0x0100 X#define V_INIT 0x0200 X X#define V_MODIFIED 0x8000 X X#define STR V_STRING | X#define BOOL V_BOOLEAN | X#define INT V_INTEGER | X#define KEY V_KEY | X#define SPEC V_SPECIAL | X X#define SAFE V_SAFE | X#define INIT V_INIT | X Xstruct variable_defs { X char *var_name; X int var_flags; X char **var_addr; X} variables[] = { X "also-subgroups", BOOL INIT 0, (char **)&also_subgroups, X "append-signature-mail", BOOL 0, (char **)&append_sig_mail, X "append-signature-post", BOOL 0, (char **)&append_sig_post, X "attributes", STR 1, (char **)attributes, X "auto-junk-seen", BOOL 0, (char **)&auto_junk_seen, X "auto-preview-mode", BOOL 0, (char **)&auto_preview_mode, X "backup", BOOL 0, (char **)&keep_rc_backup, X "backup-suffix", STR 0, (char **)&bak_suffix, X "bug-report-address", STR 0, (char **)&bug_address, X "case-fold-search", BOOL 0, (char **)&case_fold_search, X "collapse-subject", INT 3, (char **)&collapse_subject, X "columns", INT 1, (char **)&Columns, X "comp1-key", KEY 0, (char **)&comp1_key, X "comp2-key", KEY 0, (char **)&comp2_key, X "compress", BOOL 0, (char **)&compress_mode, X "confirm-append", BOOL 0, (char **)&conf_append, X "confirm-auto-quit", BOOL 0, (char **)&conf_auto_quit, X "confirm-create", BOOL 0, (char **)&conf_create, X "confirm-entry", BOOL 0, (char **)&conf_group_entry, X "confirm-entry-limit", INT 0, (char **)&conf_entry_limit, X "confirm-junk-seen", BOOL 0, (char **)&conf_junk_seen, X "confirm-messages", BOOL 0, (char **)&conf_dont_sleep, X "cross-filter-seq", BOOL 0, (char **)&seq_cross_filtering, X "cross-post", BOOL 0, (char **)&also_cross_postings, X "data-bits", INT 0, (char **)&data_bits, X "date", BOOL 0, (char **)&show_article_date, X "debug", INT 0, (char **)&Debug, X "decode-header-file", STR 0, (char **)&decode_header_file, X "decode-skip-prefix", INT 0, (char **)&decode_skip_prefix, X "default-distribution", STR 0, (char **)&default_distribution, X "default-save-file", STR 3, (char **)&default_save_file, X "delay-redraw", BOOL 0, (char **)&delay_redraw, X "edit-patch-command", BOOL 0, (char **)&edit_patch_command, X "edit-print-command", BOOL 0, (char **)&edit_print_command, X "edit-response-check", BOOL 0, (char **)&empty_answer_check, X "edit-unshar-command", BOOL 0, (char **)&edit_unshar_command, X "editor", STR 0, (char **)&editor_program, X "entry-report-limit", INT 0, (char **)&entry_message_limit, X "erase-key", KEY 0, (char **)&erase_key, X "expert", BOOL 4, (char **)&novice, X "expired-message-delay", INT 0, (char **)&expired_msg_delay, X "flow-control", BOOL 0, (char **)&flow_control, X "flush-typeahead", BOOL 0, (char **)&flush_typeahead, X "folder", STR 2, (char **)&folder_directory, X "fsort", BOOL 2, (char **)&dont_sort_folders, X "header-lines", STR 0, (char **)&header_lines, X "help-key", KEY 0, (char **)&help_key, X "include-art-id", BOOL 0, (char **)&include_art_id, X "include-full-header", BOOL 0, (char **)&include_full_header, X "included-mark", STR 1, (char **)included_mark, X "keep-unsubscribed", BOOL 0, (char **)&keep_unsubscribed, X "kill", BOOL 0, (char **)&do_kill_handling, X "kill-key", KEY 0, (char **)&kill_key, X "layout", INT 1, (char **)&fmt_linenum, X "limit", INT 2, (char **)&article_limit, X "lines", INT 1, (char **)&Lines, X "long-menu", BOOL 1, (char **)&long_menu, X "macro-debug", BOOL 0, (char **)¯o_debug, X "mail", STR 2, (char **)&mail_box, X "mail-format", BOOL 0, (char **)&use_mail_folders, X "mail-header", STR 0, (char **)&extra_mail_headers, X "mail-record", STR 2, (char **)&mail_record, X "mail-script", STR SAFE 2, (char **)&mail_script, X "mailer", STR 0, (char **)&mailer_program, X "mailer-pipe-input", BOOL 0, (char **)&mailer_pipe_input, X "mark-overlap", BOOL 0, (char **)&mark_overlap, X "min-window", INT 1, (char **)&min_pv_window, X "mmdf-format", BOOL 0, (char **)&use_mmdf_folders, X "monitor", BOOL 0, (char **)&monitor_mode, X "new-group-action", INT 0, (char **)&new_group_action, X "news-header", STR 0, (char **)&extra_news_headers, X "news-record", STR 2, (char **)&news_record, X "news-script", STR SAFE 2, (char **)&news_script, X "newsrc", STR 2, (char **)&newsrc_file, X#ifdef NNTP X "nntp-cache-dir", STR INIT 0, (char **)&nntp_cache_dir, X "nntp-cache-size", INT INIT 0, (char **)&nntp_cache_size, X "nntp-debug", BOOL 0, (char **)&nntp_debug, X#endif X/* "no....." -- cannot have variable names starting with "no" */ X "old", SPEC 2, (char **)NULL, X "orig-to-include-mask", INT 0, (char **)&orig_to_include_mask, X "overlap", INT 0, (char **)&overlap, X "pager", STR SAFE 3, (char **)&pager, X "patch-command", STR SAFE 1, (char **)patch_command, X "preview-continuation", INT 0, (char **)&preview_continuation, X "preview-mark-read", BOOL 0, (char **)&preview_mark_read, X "printer", STR SAFE 1, (char **)printer, X "query-signature", BOOL 0, (char **)&query_signature, X "quick-count", BOOL 0, (char **)&quick_unread_count, X "quick-save", BOOL 0, (char **)&quick_save, X "re-layout", INT 0, (char **)&re_layout, X "record", SPEC 1, (char **)NULL, X "repeat", BOOL 0, (char **)&fmt_rptsubj, X "repeat-group-query", BOOL 0, (char **)&repeat_group_query, X "report-cost", BOOL 0, (char **)&report_cost_on_exit, X "response-check-pause", INT 0, (char **)&response_check_pause, X "response-default-answer", STR 0, (char **)&response_dflt_answer, X "retain-seen-status", BOOL 0, (char **)&retain_seen_status, X "retry-on-error", INT 0, (char **)&retry_on_error, X "save-counter", STR 3, (char **)&save_counter_format, X "save-counter-offset", INT 0, (char **)&save_counter_offset, X "save-report", BOOL 0, (char **)&save_report, X "scroll-clear-page", BOOL 0, (char **)&scroll_clear_page, X "select-leave-next", BOOL 0, (char **)&select_leave_next, X "select-on-sender", BOOL 0, (char **)&select_on_sender, X "shell", STR SAFE 0, (char **)&user_shell, X "shell-restrictions", BOOL INIT 0, (char **)&shell_restrictions, X "silent", BOOL 0, (char **)&silent, X "slow-mode", BOOL 0, (char **)&slow_mode, X "slow-speed", INT 0, (char **)&slow_speed, X "sort", BOOL 2, (char **)&dont_sort_articles, X "sort-mode", INT 0, (char **)&sort_mode, X "spell-checker", STR 0, (char **)&spell_checker, X "split", BOOL 4, (char **)&dont_split_digests, X "stop", INT 0, (char **)&first_page_lines, X "subject-match-limit", INT 0, (char **)&subject_match_limit, X "subject-match-offset", INT 0, (char **)&match_skip_prefix, X "subject-match-parts", BOOL 0, (char **)&match_parts_equal, X "suggest-default-save", BOOL 0, (char **)&suggest_save_file, X "tidy-newsrc", BOOL 0, (char **)&tidy_newsrc, X "time", BOOL 0, (char **)&show_current_time, X "unshar-command", STR SAFE 1, (char **)unshar_command, X "unshar-header-file", STR 0, (char **)&unshar_header_file, X "unsubscribe-mark-read", BOOL 4, (char **)&keep_unsub_long, X "update-frequency", INT 0, (char **)&newsrc_update_freq, X "use-selections", BOOL 0, (char **)&use_selections, X "visible-bell", BOOL 0, (char **)&use_visible_bell, X "window", INT 1, (char **)&preview_window, X "word-key", KEY 0, (char **)&delword_key, X "wrap-header-margin", INT 2, (char **)&wrap_headers X}; X X#define TABLE_SIZE (sizeof(variables)/sizeof(struct variable_defs)) X X#define INT_VAR (*((int *)(var->var_addr))) X#define BOOL_VAR (*((int *)(var->var_addr))) X#define STR_VAR (*(var->var_addr)) X#define CBUF_VAR ((char *)(var->var_addr)) X#define KEY_VAR (*((key_type *)(var->var_addr))) X X#define VAR_TYPE (var->var_flags & 0x7000) X#define VAR_OP (var->var_flags & 0x000f) X Xstatic struct variable_defs *lookup_variable(variable) Xchar *variable; X{ X register struct variable_defs *var; X register i, j, k, t; X X i = 0; j = TABLE_SIZE - 1; X X while (i <= j) { X k = (i + j) / 2; X var = &variables[k]; X X if ( (t=strcmp(variable, var->var_name)) > 0) X i = k+1; X else X if (t < 0) X j = k-1; X else X return var; X } X X init_message("unknown variable: %s", variable); X return NULL; X} X X Xstatic adjust(str) Xregister char *str; X{ X if (str == NULL) return; X while (*str && !isspace(*str) && *str != '#') str++; X *str = NUL; X} X Xset_variable(variable, on, val_string) Xchar *variable; Xint on; Xchar *val_string; X{ X int value; X register struct variable_defs *var; X X if (strncmp(variable, "no", 2) == 0) { X on = !on; X variable += 2; X if (variable[0] == '-') variable++; X } X X if ((var = lookup_variable(variable)) == NULL) X return 0; X X if (!in_init && (var->var_flags & (V_INIT | V_SAFE))) { X if (var->var_flags & V_INIT) { X msg("'%s' can only be set in the init file", variable); X return 0; X } X if (shell_restrictions) { X msg("Restricted operation - cannot change"); X return 0; X } X } X X if (!on || val_string == NULL) X value = 0; X else X value = atoi(val_string); X X var->var_flags |= V_MODIFIED; X X switch (VAR_TYPE) { X X case V_STRING: X X switch (VAR_OP) { X case 0: X STR_VAR = (on && val_string) ? copy_str(val_string) : (char *)NULL; X break; X X case 1: X strcpy(CBUF_VAR, (on && val_string) ? val_string : ""); X break; X X case 2: X if (on) { X char exp_buf[FILENAME]; X X adjust(val_string); X if (val_string) { X if (expand_file_name(exp_buf, val_string, 1)) X STR_VAR = home_relative(exp_buf); X } X } else X STR_VAR = (char *)NULL; X break; X X case 3: X if (!on || val_string == NULL) { X msg("Cannot unset string `%s'", variable); X break; X } X STR_VAR = copy_str(val_string); X break; X } X break; X X case V_BOOLEAN: X X if (val_string) X if (val_string[0] == 'o') X on = val_string[1] == 'n'; /* on */ X else X on = val_string[0] == 't'; /* true */ X X switch (VAR_OP) { X case 0: X BOOL_VAR = on; X break; X X case 1: X BOOL_VAR = on; X return 1; X X case 2: X if (BOOL_VAR == on) { X BOOL_VAR = !on; X if (!in_init) { X sort_articles(BOOL_VAR ? 0 : -1); X return 1; X } X } X break; X X case 4: X BOOL_VAR = !on; X break; X } X break; X X case V_INTEGER: X X switch (VAR_OP) { X case 0: X case 1: X INT_VAR = value; X break; X X case 2: X case 3: X if (!on) value = -1; X INT_VAR = value; X break; X } X return (VAR_OP & 1); X X case V_KEY: X switch (VAR_OP) { X case 0: X if (val_string) { X if (*val_string) adjust(val_string + 1); /* #N is valid */ X KEY_VAR = parse_key(val_string); X } X break; X } X break; X X case V_SPECIAL: X X switch (VAR_OP) { X case 1: X if (val_string) { X adjust(val_string); X news_record = home_relative(val_string); X mail_record = news_record; X } X break; X X case 2: X also_read_articles = on; X article_limit = (on && value > 0) ? value : -1; X break; X } X break; X } X return 0; X} X Xtoggle_variable(variable) Xchar *variable; X{ X register struct variable_defs *var; X X if ((var = lookup_variable(variable)) == NULL) return; X if (VAR_TYPE != V_BOOLEAN) { X init_message("variable %s is not boolean", variable); X return; X } X X BOOL_VAR = !BOOL_VAR; X} X X Xtest_variable(expr) Xchar *expr; X{ X char *variable; X register struct variable_defs *var; X int res = -1; X X variable = expr; X if ((expr = strchr(variable, '=')) == NULL) X goto err; X X *expr++ = NUL; X X if ((var = lookup_variable(variable)) == NULL) { X msg("testing unknown variable %s=%s", variable, expr); X goto out; X } X X switch (VAR_TYPE) { X X case V_BOOLEAN: X res = BOOL_VAR; X X if (strcmp(expr, "on") == 0 || strcmp(expr, "true") == 0) break; X if (strcmp(expr, "off") == 0 || strcmp(expr, "false") == 0) { X res = !res; X break; X } X msg("boolean variables must be tested =on or =off"); X break; X X case V_INTEGER: X res = (INT_VAR == atoi(expr)) ? 1 : 0; X break; X X default: X msg("%s: cannot only test boolean and integer variables", variable); X break; X } X out: X *--expr = '='; X err: X return res; X} X X Xvar_completion(path, index) Xchar *path; Xint index; X{ X static char *head, *tail = NULL; X static int len; X static struct variable_defs *var, *help_var; X X if (index < 0) return 0; X X if (path) { X head = path; X tail = path + index; X while (*head && isspace(*head)) head++; X if (strncmp(head, "no", 2) == 0) { X head += 2; X if (*head == '-') head++; X } X X help_var = var = variables; X len = tail - head; X X return 1; X } X X if (index) { X list_completion((char *)NULL); X X for (;; help_var++) { X if (help_var >= &variables[TABLE_SIZE]) { X help_var = variables; X break; X } X X index = strncmp(help_var->var_name, head, len); X if (index < 0) continue; X if (index > 0) { X help_var = variables; X break; X } X if (list_completion(help_var->var_name) == 0) break; X } X fl; X return 1; X } X X for (; var < &variables[TABLE_SIZE]; var++) { X if (len == 0) X index = 0; X else X index = strncmp(var->var_name, head, len); X if (index < 0) continue; X if (index > 0) break; X sprintf(tail, "%s ", var->var_name + len); X var++; X return 1; X } X return 0; X} X Xstatic struct var_stack { X struct var_stack *next; X struct variable_defs *v; X int mod_flag; X union { X int ivar; X int bool; X char key; X char *str; X } value; X} *var_stack = NULL, *vs_pool = NULL; X Xmark_var_stack() X{ X register struct var_stack *vs; X X if (vs_pool) { X vs = vs_pool; X vs_pool = vs->next; X } else X vs = newobj(struct var_stack, 1); X X vs->next = var_stack; X var_stack = vs; X vs->v = NULL; X} X Xpush_variable(variable) Xchar *variable; X{ X register struct variable_defs *var; X register struct var_stack *vs; X X if (strncmp(variable, "no", 2) == 0) { X variable += 2; X if (variable[0] == '-') variable++; X } X X if ((var = lookup_variable(variable)) == NULL) { X msg("pushing unknown variable %s", variable); X return 0; X } X X mark_var_stack(); X vs = var_stack; X vs->v = var; X vs->mod_flag = var->var_flags & V_MODIFIED; X X switch (VAR_TYPE) { X X case V_STRING: X X switch (VAR_OP) { X case 0: /* if we update one of these variables, */ X case 2: /* new storage will be allocated for it */ X case 3: /* so it is ok just to save the pointer */ X vs->value.str = STR_VAR; X break; X X case 1: /* we free this memory when restored */ X vs->value.str = copy_str(CBUF_VAR); X break; X } X break; X X case V_BOOLEAN: X vs->value.bool = BOOL_VAR; X break; X X case V_INTEGER: X vs->value.ivar = INT_VAR; X break; X X case V_KEY: X vs->value.key = KEY_VAR; X break; X X case V_SPECIAL: X msg("Cannot push pseudo variable %s", var->var_name); X break; X } X X return 1; X} X Xrestore_variables() X{ X register struct variable_defs *var; X register struct var_stack *vs, *vs1; X X vs = var_stack; X X while (vs != NULL) { X if ((var = vs->v) == NULL) { X var_stack = vs->next; X vs->next = vs_pool; X vs_pool = vs; X return; X } X X var->var_flags &= ~V_MODIFIED; X var->var_flags |= vs->mod_flag; X X switch (VAR_TYPE) { X X case V_STRING: X switch (VAR_OP) { X case 0: /* only restore the string if changed; then we */ X case 2: /* can also free the memory occupied by the */ X case 3: /* 'new' value (if not NULL) */ X if (STR_VAR != vs->value.str) { X if (STR_VAR != NULL) freeobj(STR_VAR); X STR_VAR = vs->value.str; X } X break; X X case 1: /* it fitted before, so it will fit againg */ X strcpy(CBUF_VAR, vs->value.str); X freeobj(vs->value.str); X break; X } X break; X X case V_BOOLEAN: X BOOL_VAR = vs->value.bool; X break; X X case V_INTEGER: X INT_VAR = vs->value.ivar; X break; X X case V_KEY: X KEY_VAR = vs->value.key; X break; X X case V_SPECIAL: /* these are not saved, so... */ X break; X } X X vs1 = vs->next; X vs->next = vs_pool; X vs_pool = vs; X vs = vs1; X } X var_stack = NULL; X} X Xstatic var_on_stack(var) Xregister struct variable_defs *var; X{ X register struct var_stack *vs; X X for (vs = var_stack; vs; vs = vs->next) X if (vs->v == var) return 1; X return 0; X} X Xdisp_variables(all) Xint all; X{ X char *str, pushed; X int b; X register struct variable_defs *var; X X if (in_init) return; X X pg_init(0, 1); X X clrdisp(); X if (novice && !all) { X msg("Use `:set all' to see all variable settings"); X home(); X } X pg_next(); X so_printf("\1Variable settings:\1"); X X for (var = variables; var < &variables[TABLE_SIZE]; var++) { X pushed = X var_on_stack(var) ? '>' : X (var->var_flags & V_MODIFIED) ? '*' : ' '; X X if (!all && pushed == ' ') continue; X X switch (VAR_TYPE) { X case V_STRING: X if (pg_next() < 0) goto out; X str = (VAR_OP == 1) ? CBUF_VAR : STR_VAR; X if (str == NULL) str = ""; X printf("%c %-20.20s = \"%s\"\n", pushed, var->var_name, str); X break; X X case V_BOOLEAN: X if (pg_next() < 0) goto out; X b = BOOL_VAR; X if (VAR_OP == 2 || VAR_OP == 4) b = !b; X printf("%c %-20.20s = %s\n", X pushed, var->var_name, b ? "on" : "off"); X break; X X case V_INTEGER: X if (pg_next() < 0) goto out; X printf("%c %-20.20s = %d\n", pushed, var->var_name, INT_VAR); X break; X X case V_KEY: X if (pg_next() < 0) goto out; X printf("%c %-20.20s = %s\n", X pushed, var->var_name, key_name(KEY_VAR)); X break; X X case V_SPECIAL: X switch (VAR_OP) { X case 1: X break; X case 2: X if (also_read_articles) { X if (pg_next() < 0) goto out; X printf("%c %-20.20s = %d\n", X pushed, var->var_name, article_limit); X } X break; X } X break; X } X } X Xout: X pg_end(); X} END_OF_FILE if test 20923 -ne `wc -c <'variable.c'`; then echo shar: \"'variable.c'\" unpacked with wrong size! fi # end of 'variable.c' fi echo shar: End of archive 12 \(of 22\). cp /dev/null ark12isdone MISSING="" for I in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 ; do if test ! -f ark${I}isdone ; then MISSING="${MISSING} ${I}" fi done if test "${MISSING}" = "" ; then echo You have unpacked all 22 archives. rm -f ark[1-9]isdone ark[1-9][0-9]isdone else echo You still must unpack the following archives: echo " " ${MISSING} fi exit 0 exit 0 # Just in case...