rsalz@uunet.uu.net (Rich Salz) (06/23/89)
Submitted-by: storm@texas.dk (Kim F. Storm) Posting-number: Volume 19, Issue 63 Archive-name: nn/part02 #!/bin/sh # this is part 2 of a multipart archive # do not concatenate these parts, unpack them in order with /bin/sh # file admin.c continued # CurArch=2 if test ! -r s2_seq_.tmp then echo "Please unpack part 1 first!" exit 1; fi ( read Scheck if test "$Scheck" != $CurArch then echo "Please unpack part $Scheck next!" exit 1; else exit 0; fi ) < s2_seq_.tmp || exit 1 echo "x - Continuing file admin.c" sed 's/^X//' << 'NO_NEWS_IS_GOOD_NEWS' >> admin.c X printf("You must restart nnadmin to access the new group(s)\n"); X ngp = master.number_of_groups; X } X X ngp = 0; X X Loop_Groups_Header(gh) X if (update_group(gh) < 0) ngp++; X X if (ngp) printf("There are %d blocked groups\n", ngp); X} X X Xstatic master_admin() X{ X register char c; X int cur_group, value; X register group_header *gh; X X for (;;) { X switch (c = get_cmd( X"\nG)roup A)ll E)mpty N)on-empty F)iles O)ptions S)tat T)race K)ill", X"MASTER")) { X X case 'G': X cur_group = 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 'A': X case 'E': X case 'N': X cur_group = -1; X s_keyboard = 0; X X while (++cur_group < master.number_of_groups) { X if (s_keyboard || s_hangup) break; X X gh = &active_groups[cur_group]; X if (c == 'N' && gh->last_l_article == 0) continue; X if (c == 'E' && gh->last_l_article != 0) continue; X dump_m_entry(gh); X } X break; X X case 'F': X find_files(-1); 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", 1, 10000); X if (value < 0) break; X send_master(c, (long)value, 0L); X break; X X case 'S': X master_status(); X break; X X case 'T': X send_master('T', 0L, 0L); X break; X X case 'K': X if (admin_confirm("Stop nn Master")) X kill_master_1(SIGHUP); X break; X X default: 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"\nE)rrors R)eports C)ollect e(X)pire A)dmin G)roup (1-9)tail (*)all (@)clean", X"LOG"); X X if (c < SP || c == 'Q') return; X X if (c == '@') { X if (admin_confirm("Truncation")) { X sprintf(command, X"cd %s && mv Log Log.old && ./log_entry A Truncated && chmod 666 Log", X lib_directory); X system(command); X } X return; X } X X if (c == 'G') { X char *groupname; X X raw(); X printf("Group: "); X fl; X groupname = get_s(NONE, NONE, NONE, group_completion); X no_raw(); X X putchar(NL); putchar(CR); X X if (groupname == NULL) return; X X sprintf(command, "grep '%s' %s/Log | %s", X groupname, lib_directory, 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/Log", n, lib_directory); X system(command); X goto loop; X } X X if (c == '*') { X c = '.'; X } X X sprintf(command, "grep '^%c:' %s/Log | %s", c, lib_directory, pager); X system(command); X X goto loop; X} X X Xstatic group_admin() X{ X char *groupname; X register group_header *gh; X X new_group: X X raw(); X printf("Group: "); X fl; X groupname = get_s(NONE, NONE, NONE, group_completion); X no_raw(); X X putchar(NL); putchar(CR); X X if (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 for (;;) { X switch (get_cmd( X"\nD)ata H)eader F)iles S)et_flag C)lear_flag E)xpire R)ecollect G)roup", X"GROUP")) { X case 'D': X dump_group(gh, 0); X break; X X case 'H': X dump_m_entry(gh); X break; X X case 'F': X find_files(gh->group_num); 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")) X send_master('R', (long)gh->group_num, 0L); X break; X X case 'E': X if (admin_confirm("Expire Group")) X send_master('X', (long)gh->group_num, 0L); X break; X X case 'G': X goto new_group; X X default: X return; X } X } 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 = G_MODERATED; X break; X X case 'C': X new_flag = G_CONTROL; X break; X X case 'D': X new_flag = G_NO_DIRECTORY; X break; X X case 'A': X new_flag = G_ALWAYS_DIGEST; X break; X X case 'N': X new_flag = G_NEVER_DIGEST; X break; X } X X if (new_flag & (G_CONTROL | G_NO_DIRECTORY)) X if (!admin_confirm("Flag Change")) X new_flag = 0; X X if (new_flag) { X if (set_flag) { X if (gh->group_flag & new_flag) X new_flag = 0; X else { X send_master('S', (long)gh->group_num, (long)new_flag); X gh->group_flag |= new_flag; X } X } else { X if ((gh->group_flag & new_flag) == 0) X new_flag = 0; X else { X send_master('C', (long)gh->group_num, (long)new_flag); X gh->group_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 find_files(group) Xgroup_number group; X{ X char command[512]; X X if (group < 0) X sprintf(command, "ls -l %s/DATA | %s", db_directory, pager); X else X sprintf(command, "ls -l %s/DATA/%d.*", db_directory, group); X system(command); X} X X Xstatic master_status() X{ X int cur_group; X long articles, disk_use; X register group_header *gh; X X printf("\nMaster:\n"); X printf(" last_scan: %s\n", date_time(master.last_scan)); X printf(" no of groups: %d\n", master.number_of_groups); X printf(" next write: %ld\n", master.next_group_write_offset); X X articles = disk_use = 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_l_article - gh->first_l_article + 1; X } X X printf("\n Articles: %ld\n", articles); X printf( " Disk usage: %ld k\n", disk_use); X} X Xstatic show_config() X{ X extern char *temp_file; X extern char news_active[], news_directory[]; X#ifdef NNTP X extern char nntp_server[]; X#endif X X printf("\nConfiguration:\n\n"); X printf("BIN: %s\nLIB: %s\nDB: %s\nNEWS: %s\n", X BIN_DIRECTORY, X lib_directory, X db_directory, X news_directory); 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 X#ifdef STATISTICS X printf("Recording usage statistics\n"); X#else X printf("No usage statistics are recorded\n"); X#endif X X printf("Default pager: %s\n", PAGER); X printf("Default printer: %s\n", PRINTER); 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 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_l_article, gh->last_l_article); X printf(" active info: %06ld %06d\n", X gh->first_article, gh->last_article); X printf("Offsets: index->%ld, data->%ld\n", X gh->index_write_offset, X gh->data_write_offset); X if (gh->group_flag) X dump_g_flag(gh); X} X Xstatic dump_g_flag(gh) Xregister group_header *gh; X{ X printf("Flags: "); X if (gh->group_flag & G_BLOCKED) printf(" BLOCKED"); X if (gh->group_flag & G_EXPIRE) printf(" EXPIRE"); X if (gh->group_flag & G_MODERATED) printf(" MODERATED"); X if (gh->group_flag & G_CONTROL) printf(" CONTROL"); X if (gh->group_flag & G_NO_DIRECTORY) printf(" NO_DIRECTORY"); X if (gh->group_flag & G_ALWAYS_DIGEST) printf(" ALWAYS_DIGEST"); X if (gh->group_flag & G_NEVER_DIGEST) printf(" NEVER_DIGEST"); X printf("\n"); X} X X Xstatic dump_group(gh, validate) Xgroup_header *gh; Xint validate; X{ X FILE *data, *ix; X data_header hdr; X off_t data_offset, real_offset, next_offset; X cross_post_number cross_post; X article_number first_article, next_article, this_art; X int n, was_digest; X char buffer[512]; X X if (init_group(gh) <= 0) X printf("cannot access group %s\n", gh->group_name); X X update_group(gh); X X if (validate) X first_article = gh->first_l_article; X else X first_article = get_entry("First article", X (long)gh->first_l_article, X (long)gh->last_l_article); X X if (first_article < 0) first_article = gh->first_l_article; X X ix = open_data_file(gh, 'x', OPEN_READ); X if (ix == NULL) { X if (verbose) printf("NO INDEX FILE\n"); X return 0; X } X next_offset = get_index_offset(gh, first_article); X fseek(ix, next_offset, 0); X X if (!db_read_offset(ix, &real_offset)) { X if (verbose) printf("NO INDEX FOR ARTICLE %ld\n", (long)first_article); X fclose(ix); X return 0; X } X X data_offset = real_offset; X fseek(ix, next_offset, 0); X X data = open_data_file(gh, 'd', OPEN_READ); X if (data == NULL) { X if (verbose) printf("NO DATA FILE\n"); X fclose(ix); X return 0; X } X X X next_article = first_article; X s_keyboard = 0; X was_digest = 0; X X while (next_article <= gh->last_l_article) { X if (s_hangup || s_keyboard) goto out; X X if (!db_read_offset(ix, &real_offset)) { X if (verbose) X printf("NO INDEX FOR ARTICLE %ld\n", (long)next_article); X goto err; X } X X if (data_offset != real_offset) X goto ix_data_err; X X fseek(data, data_offset, 0); X X in_digest: X X next_offset = data_offset; X if (!db_read_art(data, &hdr, &data_offset)) { X if (real_offset == gh->data_write_offset || was_digest) X break; X X if (verbose) printf("No article header for article # %ld\n", X (long)next_article); X goto err; X } X X if (hdr.dh_number) { X if (was_digest) { X next_article++; X if (!db_read_offset(ix, &real_offset)) X goto ix_eof_err; X if (real_offset != next_offset) X goto ix_data_err; X } X X this_art = hdr.dh_number < 0 ? -hdr.dh_number : hdr.dh_number; X X if (this_art != next_article) { X if (this_art < next_article) { X if (verbose) X printf("Article # %ld out of sequence\n", X (long)this_art); X goto err; X } X/* X if (this_art == (next_article + 1)) X printf("Article # %ld missing (OK)\n", X (long)next_article); X else X printf("Articles # %ld -> %ld missing (OK)\n", X (long)next_article, (long)this_art); X*/ X while (this_art > next_article) { X if (!db_read_offset(ix, &next_offset)) X goto ix_eof_err; X if (next_offset != real_offset) X goto ix_data_err; X next_article++; X } X } X } X X if (!validate) X printf("\noffset = %ld, article # = %ld\n", X (long)real_offset, (long)(hdr.dh_number)); X X if (hdr.dh_lpos == (off_t)0) { /* article not accessible */ X if (verbose) X printf("# %ld: NO ARTICLE (ok)\n", (long)next_article); X continue; X } X X data_offset += hdr.dh_cross_postings * sizeof(cross_post_number) X + hdr.dh_sender_length X + hdr.dh_subject_length; X X if (hdr.dh_cross_postings) { X if (!validate) X printf("xpost(%d):", hdr.dh_cross_postings); X X n = hdr.dh_cross_postings; X while (--n >= 0) { X if (fread((char *)&cross_post, sizeof(cross_post_number), 1, data)!= 1) X goto data_error; X#ifdef NETWORK_DATABASE X#ifndef NETWORK_BYTE_ORDER X cross_post = ntohl(cross_post); X#endif X#endif X if (validate) { X if (cross_post >= master.number_of_groups) { X if (verbose) X printf("xpost group out of range: %ld (article # %ld)\n", X (long)cross_post, (long)next_article); X goto err; X } X } else X printf(" %d", cross_post); X } X if (!validate) printf("\n"); X } X X X if (!validate) X if (IS_DIGEST_HEADER(hdr)) X printf("digest header "); X else X if (IS_SUB_DIGEST(hdr)) X printf("digest article "); X else X printf("normal article "); X X if (!validate) X printf("ts=%lu hp=%ld, fp=+%d, lp=%ld, rep=%d, lines=%d\n", X (long unsigned)hdr.dh_date, X (long)hdr.dh_hpos, (int)hdr.dh_fpos, (long)hdr.dh_lpos, X hdr.dh_replies, hdr.dh_lines); X X if (hdr.dh_sender_length) { X if (fread(buffer, sizeof(char), (int)hdr.dh_sender_length, data) X != hdr.dh_sender_length) goto data_error; X buffer[hdr.dh_sender_length] = NUL; X if (!validate) X printf("Sender(%d): %s\n", hdr.dh_sender_length, buffer); X } else X if (!validate) X printf("No sender\n"); X X if (hdr.dh_subject_length) { X if (fread(buffer, sizeof(char), (int)hdr.dh_subject_length, data) X != hdr.dh_subject_length) goto data_error; X buffer[hdr.dh_subject_length] = NUL; X if (!validate) X printf("Subj(%d): %s\n", hdr.dh_subject_length, buffer); X } else X if (!validate) X printf("No subject\n"); X X if (IS_DIGEST_HEADER(hdr) || IS_SUB_DIGEST(hdr)) { X was_digest = 1; X goto in_digest; X } X X was_digest = 0; X next_article++; X X } X Xout: X X if (s_keyboard == 0 && data_offset != gh->data_write_offset) { X if (verbose) X printf("\n*** ERROR: Next data offset %ld wrong. real offset=%ld\n", X gh->data_write_offset, data_offset); X goto err; X } X X fclose(data); X fclose(ix); X return 1; X X X X data_error: X if (verbose) printf("\n*** END OF FILE on DATA FILE\n\n"); X goto err; X X ix_eof_err: X if (verbose) printf("NO INDEX FOR ARTICLE %ld\n", (long)next_article); X goto err; X X ix_data_err: X if (verbose) { X printf("\n*** OFFSET ERROR in article # %ld offsets:\n", X (long)next_article); X printf(" Calcuated offset %ld differs from index %ld\n", X (long)data_offset, (long)real_offset); X } X goto err; X X err: X X fclose(data); X fclose(ix); X return 0; X} X X Xsend_master(command, arg1, arg2) Xchar command; Xlong arg1, arg2; X{ X FILE *gate; X X gate = open_file(relative(lib_directory, "GATE"), OPEN_APPEND); X X if (gate == NULL) { X printf("Cannot send to master\n"); X return; X } X X fprintf(gate, "%c;%ld;%ld;%s %s;\n", X command, arg1, arg2, user_name(), date_time((time_t)0)); X X fclose(gate); X X log_entry('A', "SEND %c %ld %ld", command, arg1, arg2); X} X X X X X/* fake this for visit_active_file() */ X X/*ARGSUSED*/ Xgroup_header *add_new_group(name) Xchar *name; X{ X return NULL; X} X X X/* X * make consistency check on all groups X */ X X Xstatic file_error(gh, d_or_x, write_offset) Xgroup_header *gh; Xchar d_or_x; Xoff_t write_offset; X{ X FILE *f; X X f = open_data_file(gh, d_or_x, OPEN_READ); X X if (f == NULL) { X if (verbose) X printf("FILE '%c' NOT FOUND\n", d_or_x); X } else { X fseek(f, (off_t)0, 2); X if (ftell(f) >= write_offset) { X fclose(f); X return 0; X } X X if (verbose) X printf("FILE '%c' IS SHORTER THAN NEXT WRITE OFFSET\n", d_or_x); X fclose(f); X } X X return 1; X} X X Xvalidate_groups() X{ X register group_header *gh; X group_number num; X import char *pname; X X if (strcmp(pname, "nnadmin")) { X printf("You can only run VALIDATION from nnadmin\n"); X return; X } X X s_keyboard = 0; X X Loop_Groups_Number(num) { X X if (s_hangup || s_keyboard) break; X X gh = &active_groups[num]; X if (init_group(gh) <= 0) continue; /* no directory */ X X if (verbose) { printf("\r%s: ", gh->group_name); clrline(); } X X if (gh->group_flag & G_BLOCKED) { X if (verbose) printf("BLOCKED\n"); X continue; 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_l_article == 0) { X continue; X } X X if (gh->first_article > gh->last_l_article || X gh->last_l_article > gh->last_article || X gh->first_l_article > gh->first_article) { X X if (verbose) X printf("RENUMBERING OF ARTICLES (active=%ld..%ld master=%ld..%ld)", X gh->first_article, gh->last_article, X gh->first_l_article, gh->last_l_article); X goto ask_collect; X } X X /* X * Check existence and sizes of data files X */ X X if (file_error(gh, 'x', gh->index_write_offset)) X goto ask_collect; X X if (file_error(gh, 'd', gh->data_write_offset)) X goto ask_collect; X X if (!dump_group(gh, 1)) X goto ask_collect; X X if (!verbose) continue; X X if (gh->first_article > gh->first_l_article) X printf("unexpired articles: %ld\n", X (long)(gh->first_article - gh->first_l_article)); X else X printf("OK\r"); X continue; X X ask_collect: X log_entry('V', "Database inconsistency in group %s", gh->group_name); X X if (admin_confirm("\nRepair group")) X send_master('R', (long)gh->group_num, 0L); X } X} X X X Xkill_master_1(sig) Xint sig; X{ X if (kill_master(sig)) { X if (verbose) printf("sent signal %d to master\n", sig); X } else X if (errno == ESRCH) { X if (verbose) printf("master is not running\n"); X } else X printf("cannot signal master (errno=%d)\n", errno); X} NO_NEWS_IS_GOOD_NEWS echo "File admin.c is complete" chmod 0644 admin.c || echo "restore of admin.c fails" set `wc -c admin.c`;Sum=$1 if test "$Sum" != "20076" then echo original size 20076, current size $Sum;fi echo "x - extracting answer.c (Text)" sed 's/^X//' << 'NO_NEWS_IS_GOOD_NEWS' > answer.c && X#include "config.h" X#include "news.h" X#include "term.h" X#include "keymap.h" X Xextern char *temp_file; X Xchar *news_record = NULL; Xchar *mail_record = NULL; X X X#define INCL_MARK_SIZE 10 X Xexport char included_mark[INCL_MARK_SIZE + 1] = ">"; X X Xstatic int ed_line; X X Xanswer(ah, command, incl) Xarticle_header *ah; Xint command; Xint incl; /* <0: ask, 0: don't include, >0: include article */ X{ X register FILE *t, *art; X char *pgm, *first_action, *record_file; X int edit_message; X char *str; X news_header_buffer nhbuf, dhbuf; X X first_action = "edit"; X edit_message = 1; X X if (incl < 0) { X prompt("Include original article? "); X if ((incl = yes(0)) < 0) return 0; X } X X art = NULL; X if (ah && ah->a_group) init_group(ah->a_group); X X if (incl || command != K_MAIL_OR_FORWARD) { X int open_modes; X X open_modes = FILL_NEWS_HEADER | GET_ALL_FIELDS | SKIP_HEADER; X if (ah->flag & A_DIGEST) open_modes |= FILL_DIGEST_HEADER; X X art = open_news_article(ah, open_modes, nhbuf, dhbuf); X if (art == NULL) { X msg("Can't find original article"); X return 0; X } X X if (ah->flag & A_DIGEST) { X if (digest.dg_from) X news.ng_path = news.ng_from = digest.dg_from; X if (digest.dg_subj) X news.ng_subj = digest.dg_subj; X } X } else X ah = NULL; X X /* build header */ X X if ((t = open_file(temp_file, OPEN_CREATE)) == NULL) { X msg("Can't create %s", temp_file); X return 0; X } X X ed_line = 0; X record_file = mail_record; X X if (command == K_REPLY) { X pgm = "reply"; X X if (reply_to(t, news.ng_reply) || X reply_to(t, news.ng_from) || X reply_to(t, news.ng_path)) goto alt0; X if (to_line(t, news.ng_reply)) goto alt1; X if (to_line(t, news.ng_from)) goto alt2; X if (to_line(t, news.ng_path)) goto alt3; X goto err; X X alt0: X alt_to_line(t, news.ng_reply); X alt1: X alt_to_line(t, news.ng_from); X alt2: X alt_to_line(t, news.ng_path); X alt3: X X if (news.ng_subj) X subj_line(t, ah->replies, ah->subject, (char *)NULL); X else X subj_line(t, 0, current_group->group_name, "Your Article in"); X X ng_line(t); X ref_line(t); X X end_header(t); X X if (incl) { X fprintf(t, "In %s you write:\n", current_group->group_name); X ed_line++; X } X } X X if (command == K_FOLLOW_UP) { X pgm = "follow"; X record_file = news_record; X X ng_line(t); X X if (news.ng_subj) X subj_line(t, ah->replies, ah->subject, (char *)NULL); X else X if (!subj_line(t, 0, news.ng_from, "Babble from")) X if (!subj_line(t, 0, news.ng_ident, "Article")) { X prompt("Subject: "); X str = get_s(NONE, NONE, NONE, NO_COMPLETION); X if (str == NULL) goto err; X subj_line(t, -1, str, (char *)NULL); X } X X if (news.ng_keyw) { X fprintf(t, "Keywords: %s\n", news.ng_keyw); X ed_line++; X } X X if (news.ng_dist) { X fprintf(t, "Distribution: %s\n", news.ng_dist); X ed_line++; X } X X ref_line(t); X X end_header(t); X X if (incl) { X if (news.ng_from) { X fprintf(t, "%s writes:\n", news.ng_from); X ed_line++; X } else X if (news.ng_ident) { X fprintf(t, "In %s %s:\n", X ah->flag & A_DIGEST ? "digest" : "article", X news.ng_ident); X ed_line++; X } X } X } X X if (command == K_MAIL_OR_FORWARD) { X pgm = incl ? "forward" : "mail"; X X m3_again: X prompt("To: "); X str = get_s(user_name(), NONE, NONE, NO_COMPLETION); X if (str == NULL) goto close_t; X X if (*str == NUL) str = user_name(); X if (*str == '?') goto m3_again; X X if (strcmp(str, user_name()) == 0) X record_file = NULL; /* we will get this anyway, X there is so no need to save it */ X X/* if (reply_to(t, str)) { alt_to_line(t, str); } else */ X to_line(t, str); X X do { X prompt("Subject: "); X str = get_s(incl ? ah->subject : NONE, NONE, NONE, NO_COMPLETION); X if (str == NULL) goto close_t; X if (*str == NUL && incl) str = ah->subject; X } while (*str == NUL); X X subj_line(t, -1, str, (char *)NULL); X X end_header(t); X X if (incl) { X prompt("\1Edit\1 forwarded message? "); X if ((edit_message = yes(0)) < 0) goto close_t; X if (!edit_message) { X first_action = "send"; X fseek(art, ah->hpos, 0); X } X } X X } X X /* empty line terminates header */ X fputc(NL, t); X ed_line++; X X prompt("\1WAIT\1"); X X if (incl) { X register c, prevnl = 1; X X while ((c = getc(art)) != EOF) { X if (c == NL) { X putc(c, t); X if (ftell(art) >= ah->lpos) break; X prevnl++; X continue; X } X if (prevnl) { X if (command != K_MAIL_OR_FORWARD || ftell(art) < ah->fpos) X fputs(included_mark, t); X prevnl = 0; X } X putc(c, t); X } X } else { X putc(NL, t); X ed_line++; X } X X fclose(t); X if (art) fclose(art); X X aux_sh(pgm, first_action, record_file, X command == K_FOLLOW_UP ? "Article not posted" : "Mail not sent"); X X return edit_message; X X err: X msg("Can't build header for %s", X command != K_FOLLOW_UP ? "letter" : "article"); X X close_t: X fclose(t); X unlink(temp_file); X if (art) fclose(art); X X return 0; X} X X Xcancel(ah) Xarticle_header *ah; X{ X news_header_buffer nhbuf; X FILE *f; X X if (ah->a_group) init_group(ah->a_group); X X if (ah->flag & A_DIGEST) { X fputs("\rCancel entire digest ? ", stdout); clrline(); X if (yes(1) > 0) X ah->flag &= ~A_DIGEST; X else { X msg("Can only cancel entire digests (yet?)"); X return 2; X } X } else { X fputs("\rConfirm cancel: ", stdout); clrline(); X if (yes(1) <= 0) return 0; X } X X f = open_news_article(ah, FILL_NEWS_HEADER|GET_ALL_FIELDS, nhbuf, (char *)NULL); X if (f == NULL) { X msg("Can't find original article"); X return 2; X } X fclose(f); X X printf("\rCancelling article %s in group %s", X news.ng_ident, current_group->group_name); X clrline(); X X ed_line = -1; X X if (aux_sh("cancel", X news.ng_ident, current_group->group_name, "Not canceled")) X return 3; X X return 1; X} X X Xpost_menu() X{ X FILE *t; X char *str, *tail; X char group_name[FILENAME], subject[FILENAME], X distribution[FILENAME], keywords[FILENAME]; X extern group_completion(); X X group_name[0] = NUL; X X again_group: X X prompt("\1POST to group\1 "); X X str = get_s(current_group ? current_group->group_name : NONE, X group_name, NONE, group_completion); X if (str == NULL || *str == NUL) return 0; X strcpy(group_name, str); X X for (str = group_name; str; str = tail) { X tail = strchr(str, ','); X if (tail) *tail = NUL; X X if (lookup(str) == NULL) { X msg("unknown group: %s", str); X *str = NUL; X goto again_group; X } X X if (tail) *tail++ = ','; X } X X prompt("Subject: "); X str = get_s(NONE, NONE, NONE, NO_COMPLETION); X if (str == NULL || *str == NUL) return 0; X strcpy(subject, str); X X prompt("Keywords: "); X str = get_s(NONE, NONE, NONE, NO_COMPLETION); X if (str == NULL) return 0; X strcpy(keywords, str); X X strcpy(distribution, group_name); X if (str = strchr(distribution, '.')) *str = NUL; X X prompt("\1Distribution\1 (default '%s') ", distribution); X str = get_s(NONE, NONE, NONE, NO_COMPLETION); X if (str == NULL) return 0; X if (*str) strcpy(distribution, str); X X if ((t = open_file(temp_file, OPEN_CREATE)) == NULL) { X msg("Can't create %s", temp_file); X return 0; X } X X prompt("\1WAIT\1"); X X ed_line = 5; X fprintf(t, "Newsgroups: %s\n", group_name); X fprintf(t, "Distribution: %s\n", distribution); X fprintf(t, "Subject: %s\n", subject); X if (*keywords) { X fprintf(t, "Keywords: %s\n", keywords); X ed_line++; X } X fputc(NL, t); X fputc(NL, t); X X fclose(t); X X aux_sh("post", "edit", news_record, "Article not posted"); X X return 1; X} X Xstatic subj_line(t, re, subj, prefix) XFILE *t; Xint re; Xchar *subj, *prefix; X{ X if (subj == NULL) return 0; X X fputs("Subject: ", t); X X if (re == 0) X fputs("Re: ", t); X else if (re > 0) X fprintf(t, "Re^%d: ", re + 1); X X if (prefix) { X fputs(prefix, t); X fputc(' ', t); X } X X fputs(subj, t); X fputc(NL, t); X X ed_line++; X return 1; X} X X Xstatic ng_line(t) XFILE *t; X{ X fprintf(t, "Newsgroups: %s\n", X news.ng_follow ? news.ng_follow : news.ng_groups); X ed_line++; X} X Xstatic ref_line(t) XFILE *t; X{ X if (news.ng_ref == NULL && news.ng_ident == NULL) return; X X fputs("References:", t); X if (news.ng_ref) fprintf(t, " %s", news.ng_ref); X if (news.ng_ident) fprintf(t, " %s", news.ng_ident); X putc(NL, t); X ed_line++; X} X X Xstatic to_line(t, to) XFILE *t; Xchar *to; X{ X if (to == NULL) return 0; X X fprintf(t, "To: %s\n", to); X ed_line++; X return 1; X} X Xstatic alt_to_line(t, to) XFILE *t; Xchar *to; X{ X if (to == NULL) return; X X fprintf(t, "Orig-To: %s\n", to); X ed_line++; X} X Xstatic end_header(t) XFILE *t; X{ X fputc(NL, t); X ed_line++; X} X X Xstatic reply_to(t, address) XFILE *t; Xchar *address; X{ X char route[512]; X X if (address == NULL) return 0; X X if (reroute(route, address)) { X to_line(t, route); X return 1; X } X return 0; X} X X X/* X * invoke aux shell script with suitable arguments X * X * WARNING: record may be NULL, soit must be the last argument!! X */ X Xstatic aux_sh(prog, action, record, not_sent) Xchar *prog, *action, *record, *not_sent; X{ X char *args[8]; X char number[10]; X register char **ap = args; X time_t start_t; X X *ap++ = "nnaux"; X *ap++ = relative(lib_directory, "aux"); X *ap++ = prog; X X if (ed_line >= 0) { /* not cancel */ X sprintf(number, "%d", ed_line); X *ap++ = temp_file; X *ap++ = number; X } X X *ap++ = action; /* article id for cancel */ X *ap++ = record; /* group name for cancel */ X X *ap++ = NULL; X X#ifdef STATISTICS X time(&start_t); X#endif X if (execute(SHELL, args)) { X prompt_line = -1; X prompt("\1%s\1", not_sent); X sleep(1); X return 1; X } X X#ifdef STATISTICS X tick_usage((time_t *)NULL, &start_t); X#endif X X return 0; X} NO_NEWS_IS_GOOD_NEWS chmod 0644 answer.c || echo "restore of answer.c fails" set `wc -c answer.c`;Sum=$1 if test "$Sum" != "9895" then echo original size 9895, current size $Sum;fi echo "x - extracting articles.c (Text)" sed 's/^X//' << 'NO_NEWS_IS_GOOD_NEWS' > articles.c && X/* X * access - get access to master data X */ X X#include "config.h" X#include "db.h" X#include "articles.h" X#include "match.h" X X X/* X * memory management X */ X Xstatic thunk X dummy_str_t = { X NULL, X NULL, X 0L X }, X dummy_art_t = { X NULL, X NULL, X 0L X }; X X Xstatic thunk *first_str_t = &dummy_str_t; Xstatic thunk *current_str_t = &dummy_str_t; Xstatic thunk *first_art_t = &dummy_art_t; Xstatic thunk *current_art_t = &dummy_art_t; Xstatic int cur_str_size = 0, cur_art_size = 0; Xstatic char *next_str; Xstatic article_header *next_art, **art_array; X Xstatic unsigned max_articles = 0, mem_offset = 0; X X/* X * allocate one article header X */ X X#ifndef ART_THUNK_SIZE X#define ART_THUNK_SIZE 127 X#endif X Xstatic new_thunk(t, ptr, size, chk_msg) Xthunk *t; Xchar *ptr; Xlong size; Xchar *chk_msg; X{ X thunk *new; X X mem_check(ptr, (int)size, chk_msg); X X new = (thunk *)calloc(1, sizeof(thunk)); X mem_check(new, sizeof(thunk), "memory thunk"); X X new->next_thunk = t->next_thunk; X t->next_thunk = new; X X new->this_thunk = ptr; X new->thunk_size = size; X} X X Xarticle_header *alloc_art() X{ X if (cur_art_size == 0) { X if (current_art_t->next_thunk == NULL) X new_thunk(current_art_t, X calloc(ART_THUNK_SIZE, sizeof(article_header)), X (long)ART_THUNK_SIZE, X "article headers"); X X current_art_t = current_art_t->next_thunk; X next_art = (article_header *)current_art_t->this_thunk; X cur_art_size = current_art_t->thunk_size; X } X X cur_art_size--; X return next_art++; X} X X/* X * allocate a string of length 'len' X */ X X#ifndef STR_THUNK_SIZE X#define STR_THUNK_SIZE ((1<<14) - 32) /* leave room for malloc header */ X#endif X Xchar *alloc_str(len) Xint len; X{ X char *ret; X X if (cur_str_size <= len) { /* must be room for len+1 bytes */ X if (current_str_t->next_thunk == NULL) X new_thunk(current_str_t, X malloc(STR_THUNK_SIZE), X STR_THUNK_SIZE, X "string bytes"); X X current_str_t = current_str_t->next_thunk; X next_str = current_str_t->this_thunk; X cur_str_size = current_str_t->thunk_size; X } X X ret = next_str; X cur_str_size -= len + 1; X next_str += len; X *next_str++ = NUL; /* string is null terminated */ X X return ret; X} X X/* X * "free" the allocated memory X */ X Xfree_memory() X{ X current_str_t = first_str_t; X current_art_t = first_art_t; X cur_str_size = 0; X cur_art_size = 0; X n_articles = 0; X} X X X/* X * mark/release memory X */ X X Xmark_str(str_marker) Xstring_marker *str_marker; X{ X str_marker->sm_cur_t = current_str_t; X str_marker->sm_size = cur_str_size; X str_marker->sm_next = next_str; X} X Xrelease_str(str_marker) Xstring_marker *str_marker; X{ X current_str_t = str_marker->sm_cur_t; X cur_str_size = str_marker->sm_size; X next_str = str_marker->sm_next; X} X X Xmark_memory(mem_marker) Xmemory_marker *mem_marker; X{ X mark_str(&(mem_marker->mm_string)); X X mem_marker->mm_cur_t = current_art_t; X mem_marker->mm_size = cur_art_size; X mem_marker->mm_next = next_art; X X mem_marker->mm_nart = n_articles; X mem_offset += n_articles; X X n_articles = 0; X articles = art_array + mem_offset; X} X Xrelease_memory(mem_marker) Xmemory_marker *mem_marker; X{ X release_str(&(mem_marker->mm_string)); X X current_art_t = mem_marker->mm_cur_t; X cur_art_size = mem_marker->mm_size; X next_art = mem_marker->mm_next; X X n_articles = mem_marker->mm_nart; X X mem_offset -= n_articles; X articles = art_array + mem_offset; X} X X/* X * merge all memory chunks into one. X */ X Xmerge_memory() X{ X n_articles += mem_offset; X mem_offset = 0; X articles = art_array; X} X X X/* X * save article header in 'articles' array X * 'articles' is enlarged if too small X */ X X#define FIRST_ART_ARRAY_SIZE 500 /* malloc header */ X#define NEXT_ART_ARRAY_SIZE 512 X Xadd_article(art) Xarticle_header *art; X{ X register long n; X X if ((n_articles + mem_offset) == max_articles) { X /* must increase size of 'articles' */ X X if (max_articles == 0) { X /* allocate initial 'articles' array */ X max_articles = FIRST_ART_ARRAY_SIZE; X n = 0; X } else { X new_thunk(current_str_t, X (char *)art_array, X (long)(max_articles*sizeof(article_header **)), X ""); X n = max_articles; X articles = art_array + n; X X max_articles += NEXT_ART_ARRAY_SIZE; X } X art_array = (article_header **) X calloc(max_articles, sizeof(article_header **)); X mem_check(art_array, (int)max_articles, "article headers"); X while (--n >= 0) art_array[n] = *--articles; X articles = art_array + mem_offset; X } X X articles[n_articles] = art; X n_articles++; X} X X Xstatic char match_subject[128] = { X X/* NUL SOH STX ETX EOT ENQ ACK BEL BS TAB NL VT FF CR SO SI */ X 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, X X/* DLE DC1 DC2 DC3 DC4 NAK SYN ETB CAN EM SUB ESC FS GS RS US */ X 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, X X/* SP ! " # $ % & ' ( ) * + , - . / */ X 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 99, 00, 00, 00, 00, X/* ^^ */ X X/* 0 1 2 3 4 5 6 7 8 9 : ; < = > ? */ X 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 00, 00, 00, 00, 00, 00, X X/* @ A B C D E F G H I J K L M N O */ X 00, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, X X/* P Q R S T U V W X Y Z [ \ ] ^ _ */ X 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 00, 00, X X/* ` a b c d e f g h i j k l m n o */ X 00, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, X X/* p q r s t u v w x y z { | } ~ DEL */ X 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 00, 00 X X}; X X Xstatic article_comp(ah1, ah2) Xarticle_header **ah1, **ah2; X{ X register char *a = (**ah1).subject, *b = (**ah2).subject; X register p; X X for (;; a++, b++) { X while (*a && MATCH_DROP(match_subject, *a)) a++; X while (*b && MATCH_DROP(match_subject, *b)) b++; X if (*a == NUL) { X if (*b) return -1; X break; X } X if (*b == NUL) return 1; X if (p = MATCH_CMP(match_subject, *a, *b)) return p; X } X/* X if (p = (**ah1).replies - (**ah2).replies) return p; X */ X if ((**ah1).t_stamp > (**ah2).t_stamp) return 1; X if ((**ah1).t_stamp == (**ah2).t_stamp) return 0; X return -1; X} X X Xstatic article_equal(ah1, ah2) /* ah1.hdr == ah2.hdr */ Xarticle_header **ah1, **ah2; X{ X register char *a = (**ah1).subject, *b = (**ah2).subject; X X for (;; a++, b++) { X while (*a && MATCH_DROP(match_subject, *a)) a++; X while (*b && MATCH_DROP(match_subject, *b)) b++; X if (*a == NUL) { X if (*b == NUL) break; X goto not_equal; X } X if (*b == NUL) goto not_equal; X if (MATCH_EQ(match_subject, *a, *b)) continue; X goto not_equal; X } X X return 1; X X not_equal: X return 0; X} X X Xsort_articles() X{ X register article_header **app; X register long n; X X if (n_articles <= 1) return; X X qsort(articles, n_articles, sizeof(article_header *), article_comp); X X for (n = n_articles - 1, app = articles + 1; --n >= 0; app++) X if (article_equal(app, app - 1)) (**app).flag |= A_SAME; X} X X Xstatic offset_cmp(a, b) Xarticle_header **a, **b; X{ X register i; X X if (i = (int)((*a)->a_number - (*b)->a_number)) X return i; X X return (int)((*a)->fpos - (*b)->fpos); X} X Xstatic age_cmp(ah1, ah2) Xarticle_header **ah1, **ah2; X{ X if ((**ah1).t_stamp > (**ah2).t_stamp) return 1; X if ((**ah1).t_stamp == (**ah2).t_stamp) return 0; X return -1; X} X X Xunsort_articles(arrival) X{ X register int i; X X for (i = n_articles; --i >= 0;) X articles[i]->flag &= ~A_SAME; X X if (n_articles <= 1) return; X qsort(articles, n_articles, sizeof(article_header *), X arrival ? offset_cmp : age_cmp); X} X NO_NEWS_IS_GOOD_NEWS chmod 0644 articles.c || echo "restore of articles.c fails" set `wc -c articles.c`;Sum=$1 if test "$Sum" != "7942" then echo original size 7942, current size $Sum;fi echo "x - extracting articles.h (Text)" sed 's/^X//' << 'NO_NEWS_IS_GOOD_NEWS' > articles.h && X/* X * memory handling X */ X X/* article headers */ X Xarticle_number n_articles; Xarticle_header **articles; X X/* number of articles killed by last access_group call */ X Xint killed_articles; X Xtypedef struct thunk { X char *this_thunk; X struct thunk *next_thunk; X long thunk_size; X} thunk; X X Xtypedef struct { X thunk *sm_cur_t; X int sm_size; X char *sm_next; X} string_marker; X X Xtypedef struct { X string_marker mm_string; X thunk *mm_cur_t; X int mm_size; X article_header *mm_next; X long mm_nart; X} memory_marker; X X Xextern article_header *alloc_art(); Xextern char *alloc_str(); NO_NEWS_IS_GOOD_NEWS chmod 0644 articles.h || echo "restore of articles.h fails" set `wc -c articles.h`;Sum=$1 if test "$Sum" != "611" then echo original size 611, current size $Sum;fi echo "x - extracting aux.sh (Text)" sed 's/^X//' << 'NO_NEWS_IS_GOOD_NEWS' > aux.sh && X X# CONFIG is inserted above this line during INSTALL X Xtrap : 2 3 X XPATH=/bin:$PATH Xexport PATH X X# first argument is operation to be performed: X# when saved, we shift it off X XOPERATION=$1 Xshift X X X# first we handle 'cancel' X# $1 is ident, $2 is group name. X Xif [ "$OPERATION" = "cancel" ] ; then X $INEWS -t 'cmsg cancel '"$1" -n "$2" < /dev/null > /tmp/nn$$c 2>&1 X x=$? X if [ $x != 0 ]; then X echo '' X cat /tmp/nn$$c X sleep 2 X fi X rm -f /tmp/nn$$c X exit $x Xfi X XWORK="$1" XED_LINE="$2" XFIRST_ACTION="$3" XRECORD="$4" X XTRACE=${WORK}T XCOPY="" X Xif [ "${FIRST_ACTION}" = "edit" ] ; then X COPY=${WORK}C X cp $WORK $COPY Xfi X X# loop until sent or aborted. X Xloop=true Xprompt=false Xwhile $loop ; do X if $prompt ; then X echo '' X awk 'END{printf "Action: (a)bort (e)dit (o)k (s)end: "}' < /dev/null X read act X else X act="${FIRST_ACTION}" X prompt=true X fi X X case $act in X a*) X rm -f $WORK $COPY X exit 22 X ;; X e*) X # call editor to enter at line $2 of work file X X case `basename "${EDITOR-vi}"` in X vi|emacs ) X # Berkeley vi display editor X # GNU emacs disply editor X ${EDITOR-vi} +${ED_LINE} $WORK X ;; X ded ) X # QMC ded display editor X $EDITOR -l${ED_LINE} $WORK X ;; X uemacs ) X # micro emacs X $EDITOR -g${ED_LINE} $WORK X ;; X * ) X # Unknown editor X $EDITOR $WORK X ;; X esac X ;; X X o*|s*) X loop=false X ;; X X esac Xdone X Xif [ -n "$COPY" ] ; then X if [ -s $WORK ] ; then X if cmp -s $WORK $COPY ; then X rm -f $WORK $COPY X exit 22 X fi X else X rm -f $WORK $COPY X exit 22 X fi X X rm -f $COPY Xfi X Xcase "$OPERATION" in Xreply|forward|mail) X if $APPENDSIG ; then X if [ -f $HOME/.signature ] ; then X awk 'END{printf "Append .signature? (y) : "}' < /dev/null X read ans X case $ans in X ''|y*|Y*) X echo "--" >> $WORK X cat $HOME/.signature >> $WORK X ;; X esac X fi X fi X ;; Xfollow|post) X echo "Be patient! Your new article will not show up immediately." X sleep 2 X ;; Xesac X X{ X trap 'echo SIGNAL' 1 2 3 X X case "$OPERATION" in X X reply|forward|mail) X grep -v "^Orig-To: " $WORK | $RECMAIL X x=$? X ;; X X follow|post) X grep -v "^Orig-To: " $WORK | $INEWS -h X x=$? X # wait for inews to finish X sleep 60 X ;; X X *) X echo "Invalid operation: $OPERATION -- help" X OPERATION="nn response operation" X x=1 X cat > /dev/null X ;; X X esac > $TRACE 2>&1 X X X if [ 0"$x" -ne 0 -o -s $TRACE ] ; then X if [ -s $HOME/dead.letter ] ; then X cat $HOME/dead.letter >> $HOME/dead.letters X echo '' >> $HOME/dead.letters X fi X grep -v "^Orig-To: " $WORK > $HOME/dead.letter X { X echo "To: ${LOGNAME-$USER}" X echo "Subject: $OPERATION failed" X echo '' X cat $TRACE X echo '' X echo 'Your response has been saved in ~/dead.letter' X echo '' X echo 'Your article/letter follows:' X cat $WORK X } | $RECMAIL X X elif [ -n "${RECORD}" ] ; then X { X # keep a copy of message in $RECORD (in mail format) X set `date` X if [ $3 -gt 9 ] ; then X echo From ${LOGNAME-$USER} $1 $2 $3 $4 $6 $7 X else X echo From ${LOGNAME-$USER} $1 $2 ' '$3 $4 $6 $7 X fi X echo "From: ${LOGNAME-$USER}" X grep -v '^Orig-To: ' $WORK X echo '' X } >> "$RECORD" X fi X X rm -f $WORK $TRACE X X} > /dev/null 2>&1 & X Xexit 0 NO_NEWS_IS_GOOD_NEWS chmod 0644 aux.sh || echo "restore of aux.sh fails" set `wc -c aux.sh`;Sum=$1 if test "$Sum" != "3107" then echo original size 3107, current size $Sum;fi echo "x - extracting back_act.sh (Text)" sed 's/^X//' << 'NO_NEWS_IS_GOOD_NEWS' > back_act.sh && X# prefix is inserted above by make X X# X# back_act will maintain a set of `old' active files X# in the DB directory where they can be used by nngoback X# to backtrack the rc file a number of days. X# X# It should be invoked by cron every day at midnight. X# It should run as user `news'! X# X Xcd $DB || exit 1 X Xp=15 Xl="" Xfor i in 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 Xdo X if [ -f active.$i ] X then X mv active.$i active.$p X l=$p X elif [ -n "$l" ] X then X ln active.$l active.$p X fi X p=$i Xdone X Xcp $ACTIVE active.0 Xchmod 644 active.0 NO_NEWS_IS_GOOD_NEWS chmod 0644 back_act.sh || echo "restore of back_act.sh fails" set `wc -c back_act.sh`;Sum=$1 if test "$Sum" != "522" then echo original size 522, current size $Sum;fi echo "x - extracting collect.c (Text)" sed 's/^X//' << 'NO_NEWS_IS_GOOD_NEWS' > collect.c && X#include "config.h" X#include "db.h" X#include "news.h" X Ximport int trace; X X Xstatic FILE *ix, *data; X X/* X * Collect unread articles in current group X * X * On entry, init_group has been called to setup the proper environment X */ X Xcollect_group(gh) Xregister group_header *gh; X{ X int article_count, temp; X article_number start_collect; X X if (gh->last_l_article == 0) { X gh->first_l_article = gh->first_article; X gh->last_l_article = gh->first_l_article - 1; X } X X if (gh->last_l_article >= gh->last_article) return 0; X X if (gh->index_write_offset) { X ix = open_data_file(gh, 'x', OPEN_UPDATE|MUST_EXIST); X fseek(ix, gh->index_write_offset, 0); X } else X ix = open_data_file(gh, 'x', OPEN_CREATE|MUST_EXIST); X X if (gh->data_write_offset) { X data = open_data_file(gh, 'd', OPEN_UPDATE|MUST_EXIST); X fseek(data, gh->data_write_offset, 0); X } else X data = open_data_file(gh, 'd', OPEN_CREATE|MUST_EXIST); X X article_count = 0; X start_collect = gh->last_l_article+1; X X while (gh->last_l_article < gh->last_article) { X if (s_hangup) break; X gh->last_l_article++; X gh->data_write_offset = ftell(data); X#ifdef NNTP X gh->index_write_offset = ftell(ix); X#endif X if (!db_write_offset(ix, &(gh->data_write_offset))) X write_error(); X X temp = collect_article(gh, gh->last_l_article); X#ifdef NNTP X if (temp < 0) { X /* connection failed, current article is not collected */ X gh->last_l_article--; X article_count = -1; X goto out; X } X#endif X article_count += temp; X } X X if (trace && start_collect <= gh->last_l_article) { X log_entry('T', "Col %s (%d to %d) %d", X gh->group_name, X start_collect, gh->last_l_article, X article_count); X fl; X } X X gh->data_write_offset = ftell(data); X gh->index_write_offset = ftell(ix); X X out: X fclose(data); X fclose(ix); X X return article_count; X} X X Xstatic data_header hdr; X X Xstatic collect_article(gh, art_num) Xregister group_header *gh; Xarticle_number art_num; X{ X FILE *art_file; X news_header_buffer nhbuf, dgbuf; X article_header art_hdr; X int mode, count; X cross_post_number cross_post_table[256], *cp_ptr; X count = 0; X X hdr.dh_number = art_num; X X /* get article header */ X X art_hdr.a_number = art_num; X art_hdr.hpos = (off_t)0; X art_hdr.lpos = (off_t)0; X X mode = FILL_NEWS_HEADER | FILL_OFFSETS | SKIP_HEADER; X if ((gh->group_flag & (G_CONTROL | G_NEVER_DIGEST | G_ALWAYS_DIGEST)) == 0) X mode |= DIGEST_CHECK; X X if ((art_file = open_news_article(&art_hdr, mode, nhbuf, (char *)NULL)) == NULL) { X X#ifdef NNTP X import nntp_failed; X X if (nntp_failed) { X /* X * connection to nntp_server is broken X * stop collection of articles immediately X */ X return -1; X } X#endif X /* X * it is not really necessary to save anything in the data file X * we simply use the index file to get the *first* available article X */ X return 1; /* but we have still collected one article */ X } X X /* map cross-postings into a list of group numbers */ X X hdr.dh_cross_postings = 0; X X if (gh->group_flag & G_CONTROL) { X /* we cannot trust the Newsgroups: line in the control group */ X /* so we simply ignore it (i.e. use "Newsgroups: control") */ X goto dont_digest; X } X X if (news.ng_groups) { X char *curg, *nextg; X group_header *gh1; X X for (nextg = news.ng_groups, cp_ptr = cross_post_table; *nextg; ) { X curg = nextg; X X if (nextg = strchr(curg, ',')) X *nextg++ = NUL; X else X nextg = ""; X X if (strcmp(gh->group_name, curg) == 0) break; X X if ((gh1 = lookup(curg)) == NULL) continue; X X *cp_ptr++ = gh1->group_num; X hdr.dh_cross_postings++; X } X } X X if (gh->group_flag & G_NEVER_DIGEST) X goto dont_digest; X X /* split digest */ X X X if ((gh->group_flag & G_ALWAYS_DIGEST) || (news.ng_flag & N_DIGEST)) { X int any = 0, cont = 1; X X skip_digest_body(art_file); X X while (cont && (cont = get_digest_article(art_file, dgbuf)) >= 0) { X X if (any == 0) { X /* write DIGEST_HEADER */ X build_hdr(2, -art_num, cross_post_table); X count++; X X hdr.dh_cross_postings = 0; /* no cross post in sub */ X any++; X } X /* write SUB_DIGEST */ X build_hdr(1, (article_number)0, (group_number *)NULL); X count++; X } X X if (any) goto finish; X } X X /* not a digest */ X X dont_digest: X X build_hdr(0, art_num, cross_post_table); /* normal article */ X count++; X Xfinish: X X fclose(art_file); X X return count; X} X X Xstatic build_hdr(use_digest, art_num, cross_post_table) Xint use_digest; Xarticle_number art_num; Xcross_post_number *cross_post_table; X{ X register char *name, *subj; X char name_buf[NAME_LENGTH+1], subj_buf[256]; X int re; X X if (use_digest & 1) { X X name = digest.dg_from; X subj = digest.dg_subj; X X hdr.dh_lines = digest.dg_lines; X X hdr.dh_hpos = digest.dg_hpos; X hdr.dh_fpos = (int16)(digest.dg_fpos - hdr.dh_hpos); X hdr.dh_lpos = digest.dg_lpos; X X pack_date(&(hdr.dh_date), X digest.dg_date ? digest.dg_date : news.ng_date); X } else { X X if (!news.ng_from) news.ng_from = news.ng_reply; X X name = news.ng_from; X subj = news.ng_subj; X X hdr.dh_lines = news.ng_lines; X X hdr.dh_hpos = 0; X hdr.dh_fpos = (int16)(news.ng_fpos); X hdr.dh_lpos = news.ng_lpos; X X pack_date(&(hdr.dh_date), news.ng_date); X } X X hdr.dh_number = art_num; X X /* pack name and write on .nn2 */ X X if (name) { X hdr.dh_sender_length = pack_name(name_buf, name, NAME_LENGTH); X } else X hdr.dh_sender_length = 0; X X /* write subject line on .nn2 */ X X hdr.dh_subject_length = pack_subject(subj_buf, subj, &re, 255); X hdr.dh_replies = re; X X if (use_digest & 2) hdr.dh_subject_length++; /* @ */ X X /* WRITE hdr, cross postings, name, subject */ X X db_write_art(data, &hdr); X X if (cross_post_table && hdr.dh_cross_postings) { X#ifdef NETWORK_DATABASE X#ifndef NETWORK_BYTE_ORDER X int i; X X for (i = 0; i < hdr.dh_cross_postings; i++) X cross_post_table[i] = htonl(cross_post_table[i]); X#endif X#endif X Fwrite((char *)cross_post_table, sizeof(cross_post_number), X (int)hdr.dh_cross_postings, data); X } X X if (hdr.dh_sender_length) X Fwrite(name_buf, sizeof(char), (int)hdr.dh_sender_length, data); X X if (use_digest & 2) { NO_NEWS_IS_GOOD_NEWS echo "End of part 2" echo "File collect.c is continued in part 3" echo "3" > s2_seq_.tmp exit 0 --- Kim F. Storm storm@texas.dk Tel +45 429 174 00 Texas Instruments, Marielundvej 46E, DK-2730 Herlev, Denmark No news is good news, but nn is better! -- Please send comp.sources.unix-related mail to rsalz@uunet.uu.net. Use a domain-based address or give alternate paths, or you may lose out.