[comp.sources.unix] v19i063: NN, a Usenet news reader, Part02/15

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.