[comp.sources.unix] v19i067: NN, a Usenet news reader, Part06/15

rsalz@uunet.uu.net (Rich Salz) (06/23/89)

Submitted-by: storm@texas.dk (Kim F. Storm)
Posting-number: Volume 19, Issue 67
Archive-name: nn/part06

#!/bin/sh
# this is part 6 of a multipart archive
# do not concatenate these parts, unpack them in order with /bin/sh
# file keymap.h continued
#
CurArch=6
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 keymap.h"
sed 's/^X//' << 'NO_NEWS_IS_GOOD_NEWS' >> keymap.h
X#define K_FIRST_PAGE		0x002b /* first page 			*/
X#define K_LAST_PAGE		0x002c /* last page 			*/
X
X#define K_GOTO_LINE		0x002d /* goto specific line		*/
X#define K_GOTO_PAGE		0x002e /* goto specific page		*/
X#define K_GOTO_MATCH		0x002f /* goto line matching regexp	*/
X#define K_NEXT_MATCH		0x0030 /* find next match		*/
X
X#define K_PREVIOUS		0x0031 /* goto prev group or article	*/
X				/* (no update is performed)	*/
X
X    /* more() SPECIFIC COMMANDS */
X
X#define K_LEAVE_ARTICLE		0x0033 /* goto next article, mark current */
X#define K_NEXT_ARTICLE		0x0034 /* goto next article	 	*/
X#define K_NEXT_SUBJECT		0x0035 /* goto next subject		*/
X#define K_FULL_DIGEST		0x0036 /* show full digest		*/
X#define K_ROT13			0x0037 /* do rot13 			*/
X#define K_COMPRESS		0x0038 /* compress spaces		*/
X#define K_BACK_TO_MENU		0x0039 /* return to menu */
X
X    /* menu() SPECIFIC COMMANDS	 */
X
X#define K_SELECT		0x0041 /* select current, move down 	*/
X#define K_SELECT_INVERT		0x0042 /* invert all selections 	*/
X#define K_SELECT_SUBJECT 	0x0043 /* select all with same subject */
X#define K_SELECT_RANGE		0x0044 /* select range 		*/
X#define K_AUTO_SELECT		0x0045 /* auto select from kill file	*/
X#define K_UNSELECT_ALL		0x0046 /* undo all selections		*/
X
X#define K_LAYOUT		0x0049 /* change menu layout 		*/
X
X#define K_NEXT_GROUP_NO_UPDATE 	0x004a /* goto next group, no update 	*/
X#define K_READ_GROUP_UPDATE 	0x004b /* read selected, then next group */
X#define K_READ_GROUP_THEN_SAME	0x004c /* read selected, then same group */
X
X#define K_ADVANCE_GROUP		0x004d /* advance one group in sequence */
X#define K_BACK_GROUP		0x004e /* back-up one group in sequence */
X    
X#define K_PREVIEW		0x004f /* preview article 		*/
X
X#define	K_MACRO			0x0100 /* call macro			*/
X#define	K_ARTICLE_ID		0x0200 /* article id in lower part	*/
X
X/* special keys returned by get_c() */
X
X#define	K_interrupt	CTRL('G')
X
X#define	K_up_arrow	0x0081
X#define	K_down_arrow	0x0082
X#define K_left_arrow	0x0083
X#define K_right_arrow	0x0084
X
X#define	K_function(n)	(0x0085 + n)
X
X
X#define	GETC_COMMAND	0x4000	/* bit set by get_c to return a command */
X
X/*
X * KEY MAP SIZE is:
X *   (128 normal chars) + (0200) + (4 arrow keys) + (10 function keys)
X */
X
X#define MULTI_KEYS	(1 + 4 + 10)
X#define KEY_MAP_SIZE	(128 + MULTI_KEYS)
X
X
X/* restrictions */ 
X
X#define K_ONLY_MENU	0x0001
X#define K_ONLY_MORE	0x0002
X
Xextern int menu_key_map[];
Xextern int more_key_map[];
X
Xextern char global_key_map[];
NO_NEWS_IS_GOOD_NEWS
echo "File keymap.h is complete"
chmod 0644 keymap.h || echo "restore of keymap.h fails"
set `wc -c keymap.h`;Sum=$1
if test "$Sum" != "4343"
then echo original size 4343, current size $Sum;fi
echo "x - extracting kill.c (Text)"
sed 's/^X//' << 'NO_NEWS_IS_GOOD_NEWS' > kill.c &&
X#include "config.h"
X#include "term.h"
X
X/*
X * kill file handling
X */
X
Xchar KILL_FILE[] = 	"kill";
Xchar COMPILED_KILL[] =	"KILL.COMP";
X
Xextern char *quick_match();
X
X#define COMP_KILL_MAGIC	0x4b694c6c	/* KiLl */
X
X/*
X * kill flags
X */
X
X#define	AUTO_KILL	0x01
X#define AUTO_SELECT	0x00	/* pseudo flag */
X#define ON_SUBJECT	0x02
X#define	ON_SENDER	0x00	/* pseudo flag */
X
X#define	KILL_MUST_MATCH	0x10
X
X/*
X * external flag representation
X */
X
X#define	EXT_AUTO_KILL		'!'
X#define EXT_AUTO_SELECT		'+'
X#define EXT_ON_SUBJECT		's'
X#define	EXT_ON_SENDER		'n'
X#define	EXT_KILL_MUST_MATCH	'='
X
X/*
X * period = nnn DAYS
X */
X
X#define	DAYS	* 24 * 60 * 60
X
X
X/*
X * kill_article
X *
X *	return 1 to kill article, 0 to include it
X */
X
Xtypedef struct kill_list_entry {
X    int kill_flag;
X    char *kill_pattern;
X    struct kill_list_entry *next_kill;
X} kill_list_entry;
X
X
Xstatic kill_list_entry dummy_kill = {
X    0, (char *)NULL, (kill_list_entry *)NULL 
X};
Xstatic kill_list_entry *global_kill_list = &dummy_kill;
Xstatic kill_list_entry *end_kill_list = &dummy_kill;
X
X
Xkill_article(ah)
Xarticle_header *ah;
X{
X    register kill_list_entry *kl;
X    char *string;
X    
X    end_kill_list->next_kill = (kill_list_entry *)(current_group->kill_list);
X    
X    kl = global_kill_list;
X    while (kl = kl->next_kill) {
X	if (kl->kill_flag & ON_SUBJECT)
X	    string = ah->subject;
X	else
X	    string = ah->sender;
X	
X	if (kl->kill_flag & KILL_MUST_MATCH) {
X	    if (strcmp(kl->kill_pattern, string))
X		continue;
X	} else
X	    if (quick_match(string, kl->kill_pattern) == NULL)
X		continue;
X	
X	if (kl->kill_flag & AUTO_KILL)
X	    return 1;
X	
X	ah->flag |= A_SELECT;
X	break;
X    }
X    
X    return 0;
X}
X
X
Xauto_select_article(ah)
Xarticle_header *ah;
X{
X    register kill_list_entry *kl;
X    char *string;
X    
X    end_kill_list->next_kill = ah->a_group ?
X	(kill_list_entry *)(ah->a_group->kill_list) :
X	(kill_list_entry *)(current_group->kill_list);
X    
X    kl = global_kill_list;
X    while (kl = kl->next_kill) {
X	if (kl->kill_flag & AUTO_KILL) continue;
X	
X	if (kl->kill_flag & ON_SUBJECT)
X	    string = ah->subject;
X	else
X	    string = ah->sender;
X	
X	if (kl->kill_flag & KILL_MUST_MATCH) {
X	    if (strcmp(kl->kill_pattern, string))
X		continue;
X	} else
X	    if (quick_match(string, kl->kill_pattern) == NULL)
X		continue;
X	return 1;
X    }
X    
X    return 0;
X}
X
X
X
X
X
Xenter_kill_file(gh, pattern, flag, days)
Xgroup_header *gh;
Xchar *pattern;
Xint flag;
Xint days;
X{
X    time_t now;
X    FILE *killf;
X    register kill_list_entry *kl;
X    char *str;
X    
X    killf = open_file(relative(nn_directory, "kill"), OPEN_APPEND);
X    if (killf == NULL) {
X	msg("cannot create kill file");
X	return;
X    }
X    
X    if (days >= 0) {
X	time(&now);
X	if (days == 0) days = 30;
X	fprintf(killf, "%lu:", (long)(now + days DAYS));
X    }
X
X    if (gh) fputs(gh->group_name, killf);
X    fputc(':', killf);
X
X    fputc(flag & AUTO_KILL ? EXT_AUTO_KILL : EXT_AUTO_SELECT, killf);
X    fputc(flag & ON_SUBJECT ? EXT_ON_SUBJECT : EXT_ON_SENDER, killf);
X    if (flag & KILL_MUST_MATCH) fputc(EXT_KILL_MUST_MATCH, killf);
X    fputc(':', killf);
X
X    fputs(pattern, killf);
X    fputc(NL, killf);
X    
X    fclose(killf);
X    rm_kill_file();
X    
X    str = malloc(strlen(pattern) + 1);
X    mem_check(str, 1, "string");
X    
X    strcpy(str, pattern);
X    
X    if ((flag & KILL_MUST_MATCH) == 0)
X	init_quick_match(str);
X    
X    kl = (kill_list_entry *)calloc(1, sizeof(kill_list_entry));
X    mem_check(kl, 1, "kill list entry");
X    
X    kl->kill_pattern = str;
X    kl->kill_flag = flag;
X    
X    if (gh) {
X	kl->next_kill = (kill_list_entry *)(gh->kill_list);
X	gh->kill_list = (char *)kl;
X    } else {
X	kl->next_kill = NULL;
X	end_kill_list->next_kill = kl;
X	end_kill_list = kl;
X    }
X}
X
X
Xtypedef struct {
X    group_number	ck_group;
X    char		ck_flag;
X    long		ck_pattern_index;
X} comp_kill_entry;
X
Xtypedef struct {
X    long		ckh_magic;
X    off_t		ckh_pattern_offset;
X    long		ckh_pattern_size;
X    long		ckh_entries;
X} comp_kill_header;
X
X
Xkill_menu(ah)
Xarticle_header *ah;
X{
X    int flag, days;
X    char *mode1, *mode2;
X    char *pattern, *dflt, *days_str, buffer[512];
X    extern article_header *get_menu_article();
X    group_header *gh;
X    
X    prompt("\1AUTO\1 (K)ill or (S)elect (CR => Kill subject 1 month) ");
X    switch (get_c()) {
X     case CR:
X     case NL:
X	if (ah == NULL) {
X	    ah = get_menu_article();
X	    if (ah == NULL) return;
X	}
X	
X	strcpy(buffer, ah->subject);
X	enter_kill_file(current_group, buffer, 
X			AUTO_KILL | ON_SUBJECT | KILL_MUST_MATCH, 30);
X	msg("DONE");
X	return;
X	
X     case 'k':
X     case 'K':
X     case '!':
X	flag = AUTO_KILL;
X	mode1 = "KILL";
X	break;
X     case 's':
X     case 'S':
X     case '+':
X	flag = AUTO_SELECT;
X	mode1 = "SELECT";
X	break;
X     default:
X	return;
X    }
X    
X    prompt("\1AUTO %s\1 on (S)ubject or (N)ame ?", mode1);
X    
X    dflt = NULL;
X    switch (get_c()) {
X     case 'n':
X     case 'N':
X	flag |= ON_SENDER;
X	if (ah) dflt = ah->sender;
X	mode2 = "Name";
X	break;
X     case 's':
X     case 'S':
X     case SP:
X     case CR:
X     case NL:
X	flag |= ON_SUBJECT;
X	if (ah) dflt = ah->subject;
X	mode2 = "Subject";
X	break;
X     default:
X	return;
X    }
X
X    prompt("\1%s %s:\1", mode1, mode2);
X    
X    pattern = get_s(dflt, NONE, "%=", NO_COMPLETION);
X    if (pattern == NULL) return;
X    if (*pattern == NUL || *pattern == '%' || *pattern == '=') {
X	if (dflt && *dflt)
X	    pattern = dflt;
X	else {
X	    if ((ah = get_menu_article()) == NULL) return;
X	    pattern = (flag & ON_SUBJECT) ? ah->subject : ah->sender;
X	}
X	flag |= KILL_MUST_MATCH;
X    }
X    
X    strcpy(buffer, pattern);
X    pattern = buffer;
X    
X    prompt("\1%s\1 in (G)roup '%s' or in (A)ll groups",
X	   mode1, current_group->group_name);
X	
X    switch (get_c()) {
X      case 'g':
X      case 'G':
X      case SP:
X      case CR:
X      case NL:
X	 gh = current_group;
X	 break;
X      case 'A':
X      case 'a':
X	 gh = NULL;
X	 break;
X      default:
X	 return;
X     }
X
X    prompt("\1Lifetime of entry in days\1 (P)ermanent ");
X    days_str = get_s(" 30 days", NONE, "pP", NO_COMPLETION);
X    if (days_str == NULL) return;
X
X    if (*days_str == NUL) {
X    	days_str = "30 days";
X	days = 30;
X    } else if (*days_str == 'p' || *days_str == 'P') {
X	days_str = "perm";
X	days = -1;
X    } else if (isdigit(*days_str)) {
X	days = atoi(days_str);
X	sprintf(days_str, "%d days", days);
X    } else {
X	ding();
X	return;
X    }
X
X    prompt("\1CONFIRM\1 %s %s %s%s: %-.35s%s ",
X	   mode1, mode2, days_str, 
X	   (flag & KILL_MUST_MATCH) ? " exact" : "",
X	   pattern, strlen(pattern) > 35 ? "..." : "");
X    if (yes(0) <= 0) return;
X    
X    enter_kill_file(gh, pattern, flag, days);
X}
X
X
X    
X
Xinit_kill()
X{
X    FILE *killf;
X    comp_kill_header header;
X    comp_kill_entry  entry;
X    kill_list_entry *kill_tab;
X    register group_header *gh;
X    register kill_list_entry *kl;
X    char *patterns;
X    time_t kill_age, comp_age;
X    register n;
X    
X    Loop_Groups_Header(gh)
X	gh->kill_list = NULL;
X
X    kill_age = file_exist(relative(nn_directory, KILL_FILE), "frw");
X    if (kill_age == 0) return 0;
X    
X    comp_age = file_exist(relative(nn_directory, COMPILED_KILL), "fr");
X    if (comp_age < kill_age && !compile_kill_file()) return 0;
X	
X    killf = open_file(relative(nn_directory, COMPILED_KILL), OPEN_READ);
X    if (killf == NULL) return 0;
X
X    if (fread(&header, sizeof(header), 1, killf) != 1) goto err;
X    if (header.ckh_magic != COMP_KILL_MAGIC) goto err;
X
X    patterns = malloc(header.ckh_pattern_size);
X    mem_check(patterns, header.ckh_pattern_size, "kill bytes");
X    
X    kill_tab = (kill_list_entry *)
X	calloc(header.ckh_entries, sizeof(kill_list_entry));
X    mem_check(kill_tab, header.ckh_entries, "kill entries");
X    
X    for (n = header.ckh_entries, kl = kill_tab; --n >= 0; kl++) {
X	if (fread(&entry, sizeof(entry), 1, killf) != 1) goto err;
X	
X	kl->kill_pattern = patterns + entry.ck_pattern_index;
X	kl->kill_flag = entry.ck_flag;
X	
X	if (entry.ck_group >= 0) {
X	    gh = active_groups + entry.ck_group;
X	    kl->next_kill = (kill_list_entry *)(gh->kill_list);
X	    gh->kill_list = (char *)kl;
X	} else {
X	    kl->next_kill = NULL;
X	    end_kill_list->next_kill = kl;
X	    end_kill_list = kl;
X	}
X    }
X	      
X    if (fread(patterns, sizeof(char), header.ckh_pattern_size, killf)
X	!=  header.ckh_pattern_size) goto err;
X
X    fclose(killf);
X    
X    return 1;
X    
X err:
X    fclose(killf);
X    msg("Error in compiled kill file");
X    rm_kill_file();
X
X    Loop_Groups_Header(gh)
X	gh->kill_list = NULL;
X    
X    end_kill_list = global_kill_list = &dummy_kill;
X
X    return 0;
X}
X
X
Xstatic compile_kill_file()
X{
X    FILE *killf, *compf, *patternf, *dropf;
X    comp_kill_header header;
X    comp_kill_entry  entry;
X    time_t now, age;
X    off_t cur_line_start;
X    char line[512];
X    register char *cp, *np;
X    register int c;
X    group_header *gh;
X    int flag, any_errors;
X    extern char *temp_file;
X    
X    any_errors = 0;
X    header.ckh_entries = 0;
X    
X    killf = open_file(relative(nn_directory, KILL_FILE),
X		      OPEN_READ | DONT_CREATE);
X    if (killf == NULL) return 0;
X
X    compf = open_file(relative(nn_directory, COMPILED_KILL), OPEN_CREATE);
X    if (compf == NULL) goto err1;
X
X    if ((patternf = open_file(temp_file, OPEN_CREATE)) == NULL)
X	goto err2;
X
X    dropf = NULL;
X
X    printf("\nCompiling kill file\n");
X
X    fseek(compf, (off_t)sizeof(header), 0);
X    
X    time(&now);
X    
X next_entry:
X
X    for (;;) {
X	cur_line_start = ftell(killf);
X	
X	if (fgets(line, 512, killf) == NULL) break;
X	
X	cp = line;
X	while (*cp && isascii(*cp) && isspace(*cp)) cp++;
X	if (*cp == NUL || *cp == '#' || !isascii(*cp)) continue;
X	
X	if ((np = strchr(cp, ':')) == NULL) goto bad_entry;
X
X	/* optional "age:" */
X
X	if (np != cp && isdigit(*cp)) {
X	    *np++ = NUL;
X	    age = (time_t)atol(cp);
X	    if (age < now) goto drop_entry;
X	    cp = np;
X	    if ((np = strchr(cp, ':')) == NULL) goto bad_entry;
X	}
X
X	/* "group-name:"  or ":" for all groups */
X
X	if (np == cp) {
X	    entry.ck_group = -1;
X	    np++;
X	} else {
X	    *np++ = NUL;
X	    if ((gh = lookup(cp)) == NULL) {
X		printf("Unknown group in kill file: %s\n", cp);
X		any_errors++;
X		goto drop_entry;
X	    }
X	    entry.ck_group = gh->group_num;
X	}
X
X	/* flags */
X
X	cp = np;
X	flag = 0;
X	
X	for (;;) {
X	    switch (*cp++) {
X	     case EXT_AUTO_KILL:
X		flag |= AUTO_KILL;
X		continue;
X	     case EXT_AUTO_SELECT:
X		flag |= AUTO_SELECT;
X		continue;
X	     case EXT_ON_SUBJECT:
X		flag |= ON_SUBJECT;
X		continue;
X	     case EXT_ON_SENDER:
X		flag |= ON_SENDER;
X		continue;
X	     case EXT_KILL_MUST_MATCH:
X		flag |= KILL_MUST_MATCH;
X		continue;
X	     case ':':
X		break;
X	     case NL:
X		goto bad_entry;
X	     default:
X		printf("Ignored flag '%c' in kill file\n", cp[-1]);
X		any_errors++;
X		continue;
X	    }		
X	    break;
X	}
X
X	entry.ck_flag = flag;
X	
X	if ((np = strchr(cp, NL)) == NULL) goto bad_entry;
X	
X	*np++ = NUL;
X	if ((flag & KILL_MUST_MATCH) == 0)
X	    init_quick_match(cp);
X	
X	entry.ck_pattern_index = ftell(patternf);
X	
X	if (fwrite(&entry, sizeof(entry), 1, compf) != 1)
X	    goto err3;
X
X	if (fwrite(cp, sizeof(char), np - cp, patternf) != (np - cp)) 
X	    goto err3;
X	
X	header.ckh_entries++;
X    }
X    
X    header.ckh_pattern_size = ftell(patternf);
X    
X    fclose(patternf);
X    patternf = open_file(temp_file, OPEN_READ | OPEN_UNLINK);
X    if (patternf == NULL) goto err2;
X    
X    header.ckh_pattern_offset = ftell(compf);
X    
X    while ((c = getc(patternf)) != EOF)
X	putc(c, compf);
X
X    fclose(patternf);
X
X    rewind(compf);
X
X    header.ckh_magic = COMP_KILL_MAGIC;
X
X    if (fwrite(&header, sizeof(header), 1, compf) != 1)
X	goto err2;
X    
X    fclose(compf);
X    fclose(killf);
X    if (dropf != NULL) fclose(dropf);
X
X    if (any_errors) {
X	putchar(NL);
X	any_key(0);
X    }
X    
X    return 1;
X	
X bad_entry:
X    printf("Incomplete kill file entry:\n%s", line);
X    fl;
X    any_errors++;
X	
X drop_entry:
X    if (dropf == NULL) {
X	dropf = open_file(relative(nn_directory, KILL_FILE),
X			  OPEN_UPDATE | DONT_CREATE);
X	if (dropf == NULL) goto next_entry;
X    }
X    fseek(dropf, cur_line_start, 0);
X    fwrite("# ", sizeof(char), 2, dropf);
X    goto next_entry;
X	
X err3:	   
X    fclose(patternf);
X    unlink(temp_file);
X err2:
X    fclose(compf);
X    rm_kill_file();
X err1:
X    fclose(killf);
X    if (dropf != NULL) fclose(dropf);
X    
X    msg("cannot compile kill file");
X    return 0;
X}
X    
X
Xrm_kill_file()
X{    
X    unlink(relative(nn_directory, COMPILED_KILL));
X}
X
X
Xdump_kill_list()
X{
X    register kill_list_entry *kl;
X
X    pg_init(0, 1);
X    
X    pg_next();
X    so_printf("\1GLOBAL kill list entries:\1");
X    
X    kl = end_kill_list->next_kill = NULL;
X
X    kl = global_kill_list;
X    while (kl = kl->next_kill) 
X	if (print_kill(kl) < 0) goto out;
X
X    if (pg_next() < 0) goto out;
X    if (pg_next() < 0) goto out;
X    so_printf("\1GROUP %s kill list entries\1", current_group->group_name);
X    
X    kl = (kill_list_entry *)(current_group->kill_list);
X    while (kl) {
X	if (print_kill(kl) < 0) break;
X	kl = kl->next_kill;
X    }
X
X out:
X    
X    pg_end();
X}
X
X
X
Xprint_kill(kl)
Xregister kill_list_entry *kl;
X{
X    if (pg_next() < 0) return -1;
X
X    printf("\r%s ON %s '%.35s'%s\n",
X	   kl->kill_flag & AUTO_KILL ? "KILL" : "SELECT",
X	   kl->kill_flag & ON_SUBJECT ? "SUBJECT" : "NAME",
X	   kl->kill_pattern,
X	   kl->kill_flag & KILL_MUST_MATCH ? " (exact)" : "");
X
X    return 0;
X}
X
NO_NEWS_IS_GOOD_NEWS
chmod 0644 kill.c || echo "restore of kill.c fails"
set `wc -c kill.c`;Sum=$1
if test "$Sum" != "13252"
then echo original size 13252, current size $Sum;fi
echo "x - extracting log_entry.c (Text)"
sed 's/^X//' << 'NO_NEWS_IS_GOOD_NEWS' > log_entry.c &&
X/*
X *	log_entry type string
X *	
X *	Enter a message in the Log.
X */
X
Xmain(argc, argv)
Xint argc;
Xchar *argv[];
X{
X    if (argc != 3) exit(1);
X
X    init_global(0);
X    
X    if (log_entry(argv[1][0], "%s", argv[2]) == 0)
X	exit(1);
X    
X    nn_exit(0);
X}
X
Xnn_exit(n)
Xint n;
X{
X    exit(n);
X}
X
Xuser_error()
X{
X    nn_exit(1);
X}
X
NO_NEWS_IS_GOOD_NEWS
chmod 0644 log_entry.c || echo "restore of log_entry.c fails"
set `wc -c log_entry.c`;Sum=$1
if test "$Sum" != "320"
then echo original size 320, current size $Sum;fi
echo "x - extracting m-att3b.h (Text)"
sed 's/^X//' << 'NO_NEWS_IS_GOOD_NEWS' > m-att3b.h &&
X
X/************** Machine (and compiler) dependent definitions. **************
X *
X *	Define appropriate types for the following ranges of integer
X *	variables.  These are processor & compiler dependent, but the
X *	distributed definitions will probably work on most systems.
X */
X
X
X
X/*      MACHINE TYPE	DEFINED TYPE		VALUE RANGE	*/
X
Xtypedef unsigned char	int8;		/*        0 ..     255 */
Xtypedef short		int16;		/*  -10,000 ..  10,000 */
Xtypedef long		int32;		/* -100,000 .. 100,000 */
Xtypedef unsigned long	uint32;		/* 	  0 ..  2^31-1 */
X
X
X/*
X *	Define NETWORK_BYTE_ORDER if the machine's int32's are
X *	already in network byte order, i.e. m68k based.
X */
X
X#define NETWORK_BYTE_ORDER	/* */
NO_NEWS_IS_GOOD_NEWS
chmod 0644 m-att3b.h || echo "restore of m-att3b.h fails"
set `wc -c m-att3b.h`;Sum=$1
if test "$Sum" != "688"
then echo original size 688, current size $Sum;fi
echo "x - extracting m-dec3100.h (Text)"
sed 's/^X//' << 'NO_NEWS_IS_GOOD_NEWS' > m-dec3100.h &&
X/************** Machine (and compiler) dependent definitions. **************
X *
X *	This file is for a DECstation 3100, running Ultrix
X */
X
X/*      MACHINE TYPE	DEFINED TYPE		VALUE RANGE	*/
X
Xtypedef unsigned char	int8;		/*        0 ..     255 */
Xtypedef short		int16;		/*  -10,000 ..  10,000 */
Xtypedef long		int32;		/* -100,000 .. 100,000 */
Xtypedef unsigned long	uint32;		/* 	  0 ..  2^31-1 */
X
X#undef NO_VARARGS
X
X#ifdef NETWORK_DATABASE
X#undef NETWORK_BYTE_ORDER
X#include <netinet/in.h>
X#endif	/* NETWORK DATABASE */
NO_NEWS_IS_GOOD_NEWS
chmod 0644 m-dec3100.h || echo "restore of m-dec3100.h fails"
set `wc -c m-dec3100.h`;Sum=$1
if test "$Sum" != "519"
then echo original size 519, current size $Sum;fi
echo "x - extracting m-gould.h (Text)"
sed 's/^X//' << 'NO_NEWS_IS_GOOD_NEWS' > m-gould.h &&
X/************** Machine (and compiler) dependent definitions. **************
X *
X *	Define appropriate types for the following ranges of integer
X *	variables.  These are processor & compiler dependent, but the
X *	distributed definitions will probably work on most systems.
X */
X
X
X
X/*      MACHINE TYPE	DEFINED TYPE		VALUE RANGE	*/
X
Xtypedef unsigned char	int8;		/*        0 ..     255 */
Xtypedef short		int16;		/*  -10,000 ..  10,000 */
Xtypedef long		int32;		/* -100,000 .. 100,000 */
Xtypedef unsigned long	uint32;		/* 	  0 ..  2^31-1 */
X
X/*
X *	GOULD/UTX has varargs.h but not v[s]printf()
X */
X
X#define NO_VARARGS 
X
X
X/*
X *	Not in network byte order on the GOULD
X */
X
X#undef NETWORK_BYTE_ORDER	/* */
X#ifdef NETWORK_DATABASE
X#include <netinet/in.h>
X#endif
NO_NEWS_IS_GOOD_NEWS
chmod 0644 m-gould.h || echo "restore of m-gould.h fails"
set `wc -c m-gould.h`;Sum=$1
if test "$Sum" != "751"
then echo original size 751, current size $Sum;fi
echo "x - extracting m-hp9000.h (Text)"
sed 's/^X//' << 'NO_NEWS_IS_GOOD_NEWS' > m-hp9000.h &&
X
X/************** Machine (and compiler) dependent definitions. **************
X *
X *    This is for HP9000 Series 320 and 800 (at least)
X */
X
X
X/*      MACHINE TYPE	DEFINED TYPE		VALUE RANGE	*/
X
Xtypedef unsigned char	int8;		/*        0 ..     255 */
Xtypedef short		int16;		/*  -10,000 ..  10,000 */
Xtypedef long		int32;		/* -100,000 .. 100,000 */
Xtypedef unsigned long	uint32;		/* 	  0 ..  2^31-1 */
X
X
X/*
X *	Define NETWORK_BYTE_ORDER if the machine's int32's are
X *	already in network byte order, i.e. m68k based.
X */
X
X#define NETWORK_BYTE_ORDER	/* */
NO_NEWS_IS_GOOD_NEWS
chmod 0644 m-hp9000.h || echo "restore of m-hp9000.h fails"
set `wc -c m-hp9000.h`;Sum=$1
if test "$Sum" != "550"
then echo original size 550, current size $Sum;fi
echo "x - extracting m-m680x0.h (Text)"
sed 's/^X//' << 'NO_NEWS_IS_GOOD_NEWS' > m-m680x0.h &&
X
X/************** Machine (and compiler) dependent definitions. **************
X *
X *	These are for 680x0 based systems.
X */
X
X
X
X/*      MACHINE TYPE	DEFINED TYPE		VALUE RANGE	*/
X
Xtypedef unsigned char	int8;		/*        0 ..     255 */
Xtypedef short		int16;		/*  -10,000 ..  10,000 */
Xtypedef long		int32;		/* -100,000 .. 100,000 */
Xtypedef unsigned long	uint32;		/* 	  0 ..  2^31-1 */
X
X
X/*
X *	Define NETWORK_BYTE_ORDER if the machine's int32's are
X *	already in network byte order, i.e. m68k based.
X */
X
X#define NETWORK_BYTE_ORDER	/* */
NO_NEWS_IS_GOOD_NEWS
chmod 0644 m-m680x0.h || echo "restore of m-m680x0.h fails"
set `wc -c m-m680x0.h`;Sum=$1
if test "$Sum" != "534"
then echo original size 534, current size $Sum;fi
echo "x - extracting m-sparc.h (Text)"
sed 's/^X//' << 'NO_NEWS_IS_GOOD_NEWS' > m-sparc.h &&
X
X/************** Machine (and compiler) dependent definitions. **************
X *
X *	Define appropriate types for the following ranges of integer
X *	variables.  These are processor & compiler dependent, but the
X *	distributed definitions will probably work on most systems.
X */
X
X
X
X/*      MACHINE TYPE	DEFINED TYPE		VALUE RANGE	*/
X
Xtypedef unsigned char	int8;		/*        0 ..     255 */
Xtypedef short		int16;		/*  -10,000 ..  10,000 */
Xtypedef long		int32;		/* -100,000 .. 100,000 */
Xtypedef unsigned long	uint32;		/* 	  0 ..  2^31-1 */
X
X
X
X/*
X *	Define NETWORK_BYTE_ORDER if the machine's longs are
X *	already in network byte order.
X */
X
X#define NETWORK_BYTE_ORDER
NO_NEWS_IS_GOOD_NEWS
chmod 0644 m-sparc.h || echo "restore of m-sparc.h fails"
set `wc -c m-sparc.h`;Sum=$1
if test "$Sum" != "664"
then echo original size 664, current size $Sum;fi
echo "x - extracting m-sun.h (Text)"
sed 's/^X//' << 'NO_NEWS_IS_GOOD_NEWS' > m-sun.h &&
X
X/************** Machine (and compiler) dependent definitions. **************
X *
X *	Define appropriate types for the following ranges of integer
X *	variables.  These are processor & compiler dependent, but the
X *	distributed definitions will probably work on most systems.
X */
X
X
X
X/*      MACHINE TYPE	DEFINED TYPE		VALUE RANGE	*/
X
Xtypedef unsigned char	int8;		/*        0 ..     255 */
Xtypedef short		int16;		/*  -10,000 ..  10,000 */
Xtypedef long		int32;		/* -100,000 .. 100,000 */
Xtypedef unsigned long	uint32;		/* 	  0 ..  2^31-1 */
X
X
X
X/*
X *	Define NETWORK_BYTE_ORDER if the machine's longs are
X *	already in network byte order.
X */
X
X#define NETWORK_BYTE_ORDER	/* */
X
NO_NEWS_IS_GOOD_NEWS
chmod 0644 m-sun.h || echo "restore of m-sun.h fails"
set `wc -c m-sun.h`;Sum=$1
if test "$Sum" != "671"
then echo original size 671, current size $Sum;fi
echo "x - extracting m-sun386i.h (Text)"
sed 's/^X//' << 'NO_NEWS_IS_GOOD_NEWS' > m-sun386i.h &&
X/************** Machine (and compiler) dependent definitions. **************
X *
X *	Define appropriate types for the following ranges of integer
X *	variables.  These are processor & compiler dependent, but the
X *	distributed definitions will probably work on most systems.
X */
X
X
X
X/*      MACHINE TYPE	DEFINED TYPE		VALUE RANGE	*/
X
Xtypedef unsigned char	int8;		/*        0 ..     255 */
Xtypedef short		int16;		/*  -10,000 ..  10,000 */
Xtypedef long		int32;		/* -100,000 .. 100,000 */
Xtypedef unsigned long	uint32;		/* 	  0 ..  2^31-1 */
X
X
X
X/*
X *	Not in network byte order on the 386
X */
X
X#undef NETWORK_BYTE_ORDER	/* */
X#ifdef NETWORK_DATABASE
X#include <netinet/in.h>
X#endif
NO_NEWS_IS_GOOD_NEWS
chmod 0644 m-sun386i.h || echo "restore of m-sun386i.h fails"
set `wc -c m-sun386i.h`;Sum=$1
if test "$Sum" != "673"
then echo original size 673, current size $Sum;fi
echo "x - extracting m-template.h (Text)"
sed 's/^X//' << 'NO_NEWS_IS_GOOD_NEWS' > m-template.h &&
X
X/************** Machine (and compiler) dependent definitions. **************
X *
X *	Define appropriate types for the following ranges of integer
X *	variables.  These are processor & compiler dependent, but the
X *	distributed definitions will probably work on most systems.
X */
X
X
X
X/*      MACHINE TYPE	DEFINED TYPE		VALUE RANGE	*/
X
Xtypedef unsigned char	int8;		/*        0 ..     255 */
Xtypedef short		int16;		/*  -10,000 ..  10,000 */
Xtypedef long		int32;		/* -100,000 .. 100,000 */
Xtypedef unsigned long	uint32;		/* 	  0 ..  2^31-1 */
X
X
X/*
X * 	Define NO_VARARGS if the varargs feature is not available
X *
X *	Also define NO_VARARGS if the vprintf/vsprintf routines are not
X *	available (however, this will only by safe on some machines, like
X *	the VAX).
X *	
X */
X
X/* #define NO_VARARGS */
X
X
X
X#ifdef NETWORK_DATABASE
X
X/*
X *	Define NETWORK_BYTE_ORDER if the machine's int32's are
X *	already in network byte order, i.e. m68k based.
X */
X
X#define NETWORK_BYTE_ORDER	/* */
X
X/*
X *	OTHERWISE provide the functions/macros ntohl/htonl to
X *	convert longs from and to network byte order
X */
X
X#ifndef NETWORK_BYTE_ORDER
X
X/*
X * Include appropriate files or define macroes or functions (include them 
X * in data.c) to convert longs and shorts to and from network byte order.
X */
X
X/*
X * This will work on most BSD based systems...
X */
X
X#include <netinet/in.h>
X
X/*
X * Otherwise, define something appropriate below
X */
X
X#define htonl(l)	...	/* host long to network long */
X#define ntohl(l)	...	/* network long to host long */
X
X#endif	/* not NETWORK BYTE ORDER */
X
X#endif	/* NETWORK DATABASE */
NO_NEWS_IS_GOOD_NEWS
chmod 0644 m-template.h || echo "restore of m-template.h fails"
set `wc -c m-template.h`;Sum=$1
if test "$Sum" != "1577"
then echo original size 1577, current size $Sum;fi
echo "x - extracting m-vax.h (Text)"
sed 's/^X//' << 'NO_NEWS_IS_GOOD_NEWS' > m-vax.h &&
X
X/************** Machine (and compiler) dependent definitions. **************
X *
X *	Define appropriate types for the following ranges of integer
X *	variables.  These are processor & compiler dependent, but the
X *	distributed definitions will probably work on most systems.
X */
X
X
X
X/*      MACHINE TYPE	DEFINED TYPE		VALUE RANGE	*/
X
Xtypedef unsigned char	int8;		/*        0 ..     255 */
Xtypedef short		int16;		/*  -10,000 ..  10,000 */
Xtypedef long		int32;		/* -100,000 .. 100,000 */
Xtypedef unsigned long	uint32;		/* 	  0 ..  2^31-1 */
X
X
X/*
X *	VAX/BSD has varargs.h but not v[s]printf()
X */
X
X#define NO_VARARGS 
X
X
X/*
X *	The VAX does not have longs in network byte order
X */
X
X#undef NETWORK_BYTE_ORDER	/* we need to use ntohl/htonl */
X#ifdef NETWORK_DATABASE
X#include <netinet/in.h>
X#endif
NO_NEWS_IS_GOOD_NEWS
chmod 0644 m-vax.h || echo "restore of m-vax.h fails"
set `wc -c m-vax.h`;Sum=$1
if test "$Sum" != "789"
then echo original size 789, current size $Sum;fi
echo "x - extracting macro.c (Text)"
sed 's/^X//' << 'NO_NEWS_IS_GOOD_NEWS' > macro.c &&
X#include "config.h"
X#include "keymap.h"
X#include "term.h"
X
Xexport int in_menu_mode = 0;
Xexport int get_from_macro = 0;
Xexport int macro_debug = 0;
X
X#define M_DUMMY		0	/* do nothing (end of branch)	 */
X
X#define	M_COMMAND	1	/* a command (get_c()) 		 */
X#define M_KEY		2	/* a key stroke (get_c()) 	 */
X#define M_STRING	3	/* a string (get_s())		 */
X
X#define M_INPUT		4	/* take input from keyboard (get_c/get_s) */
X
X#define M_YES		5	/* answer yes to confirmation	 */
X#define M_NO		6	/* answer no to confirmation and break */
X				/* -- if neither are present, take input */
X
X#define M_PROMPT	8	/* prompt(...)	 		 */
X#define M_ECHO		9	/* msg(...)			 */
X
X#define M_IS_MENU	10	/* in menu mode ?		 */
X#define M_IS_SHOW	11	/* in reading mode ?		 */
X#define M_IS_GROUP	12	/* are we in a news group ?	 */
X#define M_IS_FOLDER	13	/* are we in a folder ?		 */
X#define M_CONFIRM	14	/* ask for confirmation to procede	 */
X#define M_REJECT	15	/* ask for !confirmation to procede	 */
X#define M_VARTEST	16	/* test value of variable	 */
X#define M_BREAK		17	/* exit from macroes		 */
X#define	M_RETURN	18	/* return from this macro	 */
X
X
Xstruct macro {
X    int			m_type;		/* entry type */
X    union {
X	int 		mu_int;		/* command or char */
X	char 		*mu_string;	/* string for get_s */
X	struct macro 	*mu_branch;	/* false conditional */
X    } m_value;
X    struct macro *m_next;		/* next macro element */
X};
X
X#define m_int		m_value.mu_int
X#define m_string	m_value.mu_string
X#define m_branch	m_value.mu_branch
X
X#define NMACRO 32		/* max number of macros */
X#define MSTACK 5		/* max nesting level */
X
Xstatic struct macro *macro[NMACRO]; 	/* macro table */
X
Xstatic struct macro *mstack[MSTACK];	/* macro stack */
Xstatic int cstack[MSTACK];
Xstatic int m_level = 0;
X
Xstatic struct macro *m = NULL;		/* current macro */
Xstatic int no_advance = 0;
X
Xstatic int cur_m;
X
X#define MERROR ((struct macro *)1)
X
Xinit_macro()
X{
X    int n;
X    
X    for (n = 0; n < NMACRO; n++)
X	macro[n] = NULL;
X}
X
Xstatic m_new(t)
Xint t;
X{
X    struct macro *m1;
X    
X    m1 = (struct macro *)calloc(1, sizeof(struct macro));
X    mem_check(m1, sizeof(struct macro), "for macro");
X
X    if (m == NULL)
X	m = macro[cur_m] = m1;
X    else {
X	m->m_next = m1;
X	m = m1;
X    }
X    m->m_type = t;
X    m->m_next = NULL;
X}
X
X
X/*
X *	Define macro "id" reading from file f until "end"
X *
X *	Macro definition syntax:
X *		define <id>
X *		   <body>
X *		end
X */
X
X
Xm_define(id, f)
Xchar *id;
XFILE *f;
X{
X    char line[256], *lp, skip;
X
X    if (id) {
X	cur_m = atoi(id);
X	if (cur_m < 0 || cur_m >= NMACRO) {
X	    m_error("macro number out of range\n", id);
X	    return 0;
X	}
X    } else {
X	for (cur_m = 0; cur_m < NMACRO; cur_m++)
X	    if (macro[cur_m] == NULL) break;
X	if (cur_m == NMACRO) {
X	    init_message("No unused macro numbers");
X	    return 0;
X	}
X    }
X    
X    if (f == NULL) {
X	clrdisp();
X	printf("DEFINE MACRO %d -- END WITH 'end'\n\n\r", cur_m);
X	no_raw();
X	f = stdin;
X    }
X    
X    m = NULL;
X    skip = 0;
X
X    while (fgets(line, 256, f)) {
X	for (lp = line; *lp && isspace(*lp); lp++);
X	if (*lp == NUL) continue;
X	if (strncmp(lp, "end", 3) == 0) goto out;
X	if (!skip && parse_line(lp)) {
X	    macro[cur_m] = NULL;
X	    skip++;
X	}
X    }
X
X    if (f != stdin)
X	m_error("end missing", (char *)NULL);
X
X out:
X    if (f == stdin) raw();
X    m = NULL;
X    return 1;
X}
X
Xstatic parse_line(lp)
Xchar *lp;
X{
X    char *word;
X    struct macro *m1, *branch = NULL;
X
X    while (*lp) {
X	if (*lp == '#') break;
X	
X	if (*lp == ':') {
X	    m_new(M_COMMAND);
X	    m->m_int = GETC_COMMAND | K_EXTENDED_CMD;
X	    m_new(M_STRING);
X	    m->m_string = copy_str(lp + 1);
X	    break;
X	}
X
X	if (*lp == '?') {
X	    m_new(M_IS_MENU);
X	    if (branch == NULL) {
X		m1 = m;
X		m_new(M_DUMMY);
X		branch = m;
X		m = m1;
X	    }
X	    m->m_branch = branch;
X	}
X
X	word = lp;
X	if (*lp == '"')
X	    do lp++;
X	    while (*lp && *lp != '"');
X	else
X	if (*lp == '\'')
X	    do lp++;
X	    while (*lp && *lp != '\'');
X	else
X	    while (*lp && !isspace(*lp)) lp++;
X	if (*lp) {
X	    *lp++ = NUL;
X	    while (*lp && isspace(*lp)) lp++;
X	}
X	if (parse_word(word)) return 1;
X    }
X
X    if (branch) {
X	m->m_next = branch;
X	m = branch;
X    }
X    return 0;
X}
X
Xstatic parse_word(w)
Xchar *w;
X{
X    int cmd;
X    register struct macro *m1;
X    
X    if (m && m->m_type == M_COMMAND && m->m_int == (GETC_COMMAND | K_MACRO)) {
X	if (isdigit(*w)) {
X	    m->m_int |= atoi(w);
X	    goto ok;
X	}
X	m_error("macro number missing", (char *)NULL);
X	return 1;
X    }
X
X    if (*w == '"') {
X	if (m == NULL || (m->m_type != M_PROMPT && m->m_type != M_ECHO))
X	    m_new(M_STRING);
X	m->m_string = copy_str(w + 1);
X	goto ok;
X    }
X
X    if (*w == '\'') {
X	m_new(M_KEY);
X	m->m_int = parse_key(w + 1);
X	goto ok;
X    }
X
X    if (*w == '?') {
X	if (strchr(w, '=')) {
X	    m->m_type = M_VARTEST;
X	    m1 = m;
X	    m_new(M_DUMMY);
X	    m->m_branch = m1->m_branch;
X	    m1->m_string = copy_str(w + 1);
X	    goto ok;
X	}
X	
X	switch (w[1]) {
X	 case 'f': /* ?folder */
X	    cmd = M_IS_FOLDER;
X	    break;
X	 case 'g': /* ?group */
X	    cmd = M_IS_GROUP;
X	    break;
X	 case 'm': /* ?menu */
X	    cmd = M_IS_MENU;
X	    break;
X	 case 'n': /* ?no */
X	    cmd = M_REJECT;
X	    break;
X	 case 's': /* ?show */
X	    cmd = M_IS_SHOW;
X	    break;
X	 case 'y': /* ?yes */
X	    cmd = M_CONFIRM;
X	    break;
X	 default:
X	    m_error("unknown conditional %s", w - 1);
X	    return 1;
X	}
X	m->m_type = cmd;
X	goto ok;
X    }
X
X    if ((cmd = lookup_command(w, (K_ONLY_MENU | K_ONLY_MORE))) != K_INVALID) {
X	m_new(M_COMMAND);
X	m->m_int = GETC_COMMAND | cmd;
X	goto ok;
X    }
X
X    if (strcmp(w, "prompt") == 0) {
X	m_new(M_PROMPT);
X	m->m_string = "?";
X	goto ok;
X    }
X    if (strcmp(w, "echo") == 0) {
X	m_new(M_ECHO);
X	m->m_string = "ups";
X	goto ok;
X    }
X    if (strcmp(w, "input") == 0) {
X	m_new(M_INPUT);
X	goto ok;
X    }
X    if (strcmp(w, "yes") == 0) {
X	m_new(M_YES);
X	goto ok;
X    }
X    if (strcmp(w, "no") == 0) {
X	m_new(M_NO);
X	goto ok;
X    }
X    if (strcmp(w, "break") == 0) {
X	m_new(M_BREAK);
X	goto ok;
X    }
X    if (strcmp(w, "return") == 0) {
X	m_new(M_RETURN);
X	goto ok;
X    }
X
X    m_error("Unknown word >>%s<<", w);
X    return 1;
X
X ok:
X    return 0;
X}
X
X/*
X *	Invoke macro # N
X */
X
Xm_invoke(n)
Xint n;
X{
X    if (n < 0 || n > NMACRO || macro[n] == NULL) {
X	msg("undefined macro %d", n);
X	return;
X    }
X
X    if (m == NULL)
X	no_advance = 0;
X    else {
X	if (m_level == MSTACK) {
X	    msg("Macro stack overflow");
X	    m = NULL;
X	    m_level = 0;
X	    return;
X	}
X	mstack[m_level] = m;
X	cstack[m_level] = cur_m;
X	m_level++;
X    }
X
X    cur_m = n;
X    m = macro[cur_m];
X}
X
Xm_startinput()
X{
X    no_advance = 1;
X}
X
Xm_endinput()
X{
X    if (no_advance) {
X	no_advance = 0;
X	if (m && m->m_type == M_INPUT)
X	    m = m->m_next;
X    }
X}
X
Xm_advinput()
X{
X    if (m && m->m_type == M_INPUT)
X	m = m->m_next;
X}
X
Xstatic struct macro *m_call(who)
Xint who;
X{
X    struct macro *m1;
X
X    for (;;) {
X	while (m == NULL && m_level > 0) {
X	    m_level--;
X	    m = mstack[m_level];
X	    cur_m = cstack[m_level];
X	}
X	if (m == NULL) {
X	    if (macro_debug) msg("end");
X	    return NULL;
X	}
X	
X	if (macro_debug) 
X	    macro_dbg();
X		
X	if (who == 3) {
X	    if (m->m_type == M_YES || m->m_type == M_NO) goto out;
X	    return NULL;
X	}
X
X	switch (m->m_type) {
X	 case M_COMMAND:
X	 case M_KEY:
X	    if (who == 1) goto out;
X	    goto err;
X
X	 case M_STRING:
X	    if (who == 2) goto out;
X	    goto err;
X
X	 case M_INPUT:
X	    if (no_advance) return m;
X	    goto out;
X
X	 case M_YES:
X	 case M_NO:
X	 case M_DUMMY:
X	    break;
X
X	 case M_PROMPT:
X	    prompt("\1%s\1 ", m->m_string);
X	    break;
X
X	 case M_ECHO:
X	    msg(m->m_string);
X	    break;
X
X	 case M_IS_MENU:
X	    if (!in_menu_mode) m = m->m_branch;
X	    break;
X	 case M_IS_SHOW:
X	    if (in_menu_mode) m = m->m_branch;
X	    break;
X	 case M_IS_GROUP:
X	    if (current_group->group_flag & G_FOLDER) m = m->m_branch;
X	    break;
X	 case M_IS_FOLDER:
X	    if ((current_group->group_flag & G_FOLDER) == 0) m = m->m_branch;
X	    break;
X	 case M_CONFIRM:
X	    if (!yes(0)) m = m->m_branch;
X	    break;
X	 case M_REJECT:
X	    if (yes(0)) m = m->m_branch;
X	    break;
X
X	 case M_VARTEST:
X	    m1 = m;
X	    m = m->m_next;
X	    
X	    switch (test_variable(m1->m_string)) {
X	     case 0:
X		m = m->m_branch;
X		break;
X	     case -1:
X		goto err1;
X	    }
X	    break;
X	    
X	 case M_RETURN:
X	    m = NULL;
X	    continue;
X
X	 case M_BREAK:
X	    goto term;
X	}
X
X	m = m->m_next;
X    }
X
X out:
X    m1 = m;
X    m = m->m_next;
X    no_advance = 0;
X    return m1;
X
X err:
X    msg("Error in macro %d", cur_m);
X err1:
X    user_delay(1);
X    m = NULL;
X    m_level = 0;
X    return MERROR;
X
X term:
X    m = NULL;
X    m_level = 0;
X    return NULL;
X}
X
Xm_break()
X{
X    m = NULL;
X    m_level = 0;
X}
X
Xmacro_dbg()
X{
X    extern char *command_name(), *key_name();
X    char *name;
X    
X    switch (m->m_type) {
X     case M_COMMAND:
X	msg("COMMAND: %s", command_name(m->m_int));
X	goto delay;
X	
X     case M_KEY:
X	msg("KEY: %s", key_name(m->m_int));
X	goto delay;
X	
X     case M_STRING:
X	msg("STRING: %s", m->m_string);
X	goto delay;
X	
X     case M_INPUT:
X	name = "input";
X	break;
X	
X     case M_YES:
X	name = "yes";
X	break;
X	
X     case M_NO:
X	name = "no";
X	break;
X	
X     case M_DUMMY:
X	name = "dummy";
X	break;
X	
X     case M_PROMPT:
X	msg("PROMPT: %s", m->m_string);
X	goto delay;
X	
X     case M_ECHO:
X	msg("ECHO: %s", m->m_string);
X	goto delay;
X	
X     case M_IS_MENU:
X	msg("?menu => %d", in_menu_mode);
X	goto delay;
X
X     case M_IS_SHOW:
X	msg("?show => %d", !in_menu_mode);
X	goto delay;
X
X     case M_IS_GROUP:
X	msg("?group => %d", (current_group->group_flag & G_FOLDER) == 0);
X	goto delay;
X
X     case M_IS_FOLDER:
X	msg("?group => %d", (current_group->group_flag & G_FOLDER));
X	goto delay;
X
X     case M_CONFIRM:
X	name = "?yes";
X	break;
X
X     case M_REJECT:
X	name = "?no";
X	break;
X	
X     case M_VARTEST:
X	msg("?%s => %d", m->m_string, test_variable(m->m_string));
X	goto delay;
X	
X     case M_RETURN:
X	name = "return";
X	break;
X
X     case M_BREAK:
X	name = "break";
X	break;
X    }
X    msg(name);
X
X delay:
X    user_delay(1);
X}    
X/*
X *	Macro processing for get_c()
X */
X
Xm_getc(cp)
Xint *cp;
X{
X    struct macro *m1;
X    
X    get_from_macro = 0;
X    if (m && (m1 = m_call(1))) {
X	if (m1 == MERROR) return 2;
X	if (m1->m_type == M_INPUT) return 0;
X	*cp = m1->m_int;
X	get_from_macro = 1;
X	return 1;
X    }
X    return 0;
X}
X
X/*
X *	Macro processing for get_s()
X */
X
Xm_gets(s)
Xchar *s;
X{
X    struct macro *m1;
X
X    get_from_macro = 0;
X    if (m && (m1 = m_call(2))) {
X	if (m1 == MERROR) return 2;
X	if (m1->m_type == M_INPUT) return 0;
X	strcpy(s, m1->m_string);
X	get_from_macro = 1;
X	return 1;
X    }
X    return 0;
X}
X
X/*
X *	Macro processing for yes()
X */
X
Xm_yes()
X{
X    struct macro *m1;
X
X    if (m) {
X	if (m1 = m_call(3))
X	    if (m1->m_type == M_NO)
X		return 1;
X	    else
X		return 2;
X	else
X	    return 3;
X    }
X    return 0;
X}
X
Xstatic m_error(fmt, arg)
Xchar *fmt, *arg;
X{
X    char buf[80];
X
X   if (arg) {
X       sprintf(buf, fmt, arg);
X       fmt = buf;
X   }
X
X   init_message("Error in macro %d: %s", cur_m, fmt);
X}
NO_NEWS_IS_GOOD_NEWS
chmod 0644 macro.c || echo "restore of macro.c fails"
set `wc -c macro.c`;Sum=$1
if test "$Sum" != "10847"
then echo original size 10847, current size $Sum;fi
echo "x - extracting master.c (Text)"
sed 's/^X//' << 'NO_NEWS_IS_GOOD_NEWS' > master.c &&
X/*
X *	nn master daemon
X *
X *	maintains the article header database.
X */
X
X#include <signal.h>
X#include <errno.h>
X#include "config.h"
X#include "db.h"
X
X/*
X * nnmaster options:
X *
X *	-e N	expire a group if more than N articles are gone
X *	-r N	repeat every N minutes
X *
X *	-E	expire by recolleting entire groups rather than copying files
X *	-C	check consistency of database on start-up
X *
X *	-I	initialize
X *	-t	trace collection of each group
X *	-v	print version and exit
X *	-u	update even if active is not modified
X *	-w	send wakeup to real master
X *	-D	debug
X */
X
X#include "options.h"
X
Xstatic int
X    initialize = 0,
X    prt_vers = 0,
X    unconditional = 0,
X    wakeup_master = 0,
X    expire_level = 1,
X    clean_to_expire = 0,
X    check_on_startup = 0,
X    repeat_delay = 0,
X    debug_mode = 0;
X
Xexport int
X    trace = 0,
X#ifdef NNTP
X    silent = 1,
X#endif
X    Debug = 0;
X
X
XOption_Description(master_options) {
X
X    'I', Bool_Option( initialize ),
X    'v', Bool_Option( prt_vers ),
X    'u', Bool_Option( unconditional ),
X    'w', Bool_Option( wakeup_master ),
X
X    'e', Int_Option( expire_level ),
X    'r', Int_Option_Optional( repeat_delay, 10 ),
X
X    'E', Bool_Option( clean_to_expire ),
X    'C', Bool_Option( check_on_startup ),
X
X    'D', Bool_Option( debug_mode ),
X    't', Bool_Option( trace ),
X
X    '\0',
X};
X
X
Ximport FILE *master_file;
Ximport char news_active[];
X
Xstatic int rm_mpid_on_exit = 0;
X
Xmain(argc, argv)
Xint argc;
Xchar **argv;
X{
X    time_t 			age_active;
X    register group_header	*gh;
X    register int		cur_group;
X    int				col_article_count, col_group_count;
X    int				exp_article_count, exp_group_count;
X    int				temp;
X    time_t			start_time;
X    FILE			*m_pid;
X	
X
X    umask(002);			/* avoid paranoia */
X    
X    init_global(1);
X
X    parse_options(argc, argv, (char *)NULL, master_options, (char *)NULL);
X
X    if (wakeup_master) {
X	if (!kill_master(SIGALRM) && errno == ESRCH)
X	    printf("master is not running\n");
X	nn_exit(0);
X    }
X    
X    if (prt_vers) {
X	print_version("Master: Release %R.%V.%P, Compilation %U\n");
X	nn_exit(0);
X    }
X
X    if (kill_master(SIGALRM)) {
X	printf("The master is already running\n");
X	nn_exit(0);
X    }
X#ifdef NNTP
X    nntp_check();
X#endif
X    if (initialize) {
X	build_master();
X	nn_exit(0);
X    }
X
X    if (repeat_delay && !debug_mode) {
X	while ((temp = fork()) < 0) sleep(1);
X	if (temp) nn_exit(0);
X
X	process_id = getpid();	/* init_global saved parent's pid */
X	
X	DETATCH_TERMINAL
X    }
X
X    rm_mpid_on_exit = 1;
X    m_pid = open_file(relative(lib_directory, "MPID"),
X		      OPEN_CREATE|MUST_EXIST);
X    fprintf(m_pid, "%d\n", process_id);
X    fclose(m_pid);
X
X    log_entry('M', "Master started -r%d -e%d%s", 
X	           repeat_delay, expire_level, clean_to_expire ? " -E" : "");
X
X    if (check_on_startup) {
X	char cmd[FILENAME];
X	sprintf(cmd, "%s/nnadmin Z", BIN_DIRECTORY);
X	system(cmd);
X	log_entry('M', "Database validation completed");
X    }
X    
X    repeat_delay *= 60;
X    
X    init_digest_parsing();
X    
X    open_master(OPEN_READ);
X    close_master();
X
X    open_master(OPEN_UPDATE);
X    
X again:
X
X#ifdef NNTP
X    if (use_nntp) {
X	if (nntp_get_active() < 0) {
X	    nntp_close_server();
X	    current_group = NULL; /* for init_group */
X	    log_entry('N', "Can't access active file --- %s",
X		      repeat_delay ? "sleeping" : "termating");
X	    if (repeat_delay == 0)
X		nn_exit(1);
X	    sleep(repeat_delay);
X	    goto again;
X	}
X    }
X#endif
X
X    age_active = file_exist(news_active, "fr");
X#ifdef NNTP
X    if (!use_nntp)
X#endif
X	if (age_active == (time_t)0)
X	    sys_error("Cannot access active file");
X
X    temp = receive_admin();
X    
X    if (!temp && !unconditional && age_active <= master.last_scan) {
X	if (repeat_delay == 0)
X	    goto out;
X	
X	if (s_hangup) goto out;
X#ifdef NNTP
X	if (use_nntp)
X	    nntp_cleanup();
X#endif /* NNTP */
X	if (debug_mode) 
X	    printf("NONE (*** SLEEP ***)\n");
X	else {
X	    if (trace) log_entry('T', "none");
X	    sleep(repeat_delay);
X	}
X	
X	if (s_hangup) goto out;
X
X	goto again;
X    }
X
X    unconditional = 0;	/* only first pass */
X    
X    time(&start_time);
X    col_article_count = col_group_count = 0;
X    exp_article_count = exp_group_count = 0;
X    
X    visit_active_file();
X	    
X    for (cur_group = 0; cur_group < master.number_of_groups; cur_group++) {
X
X	if (s_hangup) break;
X
X	gh = &active_groups[cur_group];
X
X	if (gh->group_flag & G_NO_DIRECTORY) {
X	    if (gh->group_flag & G_BLOCKED) goto unblock_group;
X	    continue;
X	}
X	
X	if (gh->last_l_article  > gh->last_article || 
X	    gh->first_l_article > gh->first_article) {
X	    log_entry('X', "group %s renumbered", gh->group_name);
X	    clean_group(gh);
X
X	    goto do_collect;
X	}
X
X	if (expire_level > 0 && gh->last_l_article > 0) {
X	    if ((gh->first_l_article + expire_level) <= gh->first_article) {
X		if (trace) log_entry('T', "%s expire level", gh->group_name);
X		gh->group_flag |= G_EXPIRE;
X		goto do_collect;
X	    }
X	    
X	    if (gh->first_article > gh->last_l_article) {
X		if (trace) log_entry('T', "%s expire void", gh->group_name);
X		clean_group(gh);
X		goto do_collect;
X	    }
X	}
X
X	if (gh->last_l_article == gh->last_article) {
X	    if (gh->group_flag & G_BLOCKED) goto unblock_group;
X	    continue;
X	}
X	
X     do_collect:
X	if (!init_group(gh)) {
X	    log_entry('R', "%s: no directory", gh->group_name);
X	    gh->group_flag |= G_NO_DIRECTORY;
X	    gh->group_flag &= ~(G_EXPIRE | G_BLOCKED);	    
X	    save_group(current_group);
X	    continue;
X	}
X	
X	if (gh->group_flag & G_EXPIRE) {
X	    if (clean_to_expire) {
X		temp = gh->first_article - gh->first_l_article;
X		clean_group(gh);
X	    } else {
X		if ((gh->group_flag & G_BLOCKED) == 0) {
X		    gh->group_flag |= G_BLOCKED;
X		    save_group(gh);
X		}
X		temp = expire_group(gh);
X	    }
X	    if (temp) {
X		exp_article_count += temp;
X		exp_group_count++;
X	    }
X	}
X		
X	temp = collect_group(gh);
X#ifdef NNTP
X	if (temp < 0) {
X	    /* connection broken */
X	    gh->group_flag &= ~G_EXPIRE;	/* remains blocked */
X	    save_group(gh);
X	    age_active = master.last_scan; 	/* did not complete */
X	    current_group = NULL; /* for init_group */
X	    break;
X	}
X#endif
X	if (temp > 0) {
X	    col_article_count += temp;
X	    col_group_count++;
X	}
X
X     unblock_group:
X	if (temp || (gh->group_flag & G_BLOCKED)) {
X	    gh->group_flag &= ~(G_EXPIRE | G_BLOCKED);
X	    save_group(gh);
X	}
X    }
X
X    /* if master is interrupted, all new articles may not be collected */
X
X    if (!s_hangup) 
X	master.last_scan = age_active;
X
X    save_master();
X
X    if (exp_article_count)
X	log_entry('X', "Expire: %3d art, %2d gr, %2ld s %s",
X		  exp_article_count, exp_group_count, 
X		  time((time_t *)0) - start_time,
X		  col_article_count ? "(incl. collect)" : "");
X
X    if (col_article_count)
X	log_entry('C', "Collect: %3d art, %2d gr, %2ld s %s",
X		  col_article_count, col_group_count,
X		  time((time_t *)0) - start_time,
X		  exp_article_count ? "(incl. expire)" : "");
X	
X    if (!s_hangup && repeat_delay) {
X#ifdef NNTP
X	if (use_nntp)
X	    nntp_cleanup();
X#endif /* NNTP */
X	if (!debug_mode) sleep(repeat_delay);
X	if (!s_hangup) goto again;
X    }
X    
X out:
X    nn_exit(0);
X    /*NOTREACHED*/
X}
X
X
X/* 
X * nn_exit() --- called whenever a program exits.
X */
X 
Xnn_exit(n)
X{
X#ifdef NNTP
X    if (use_nntp)
X	nntp_cleanup();
X#endif /* NNTP */
X    close_master();
X
X    if (rm_mpid_on_exit)
X	unlink(relative(lib_directory, "MPID"));
X
X    if (n)
X	log_entry('E', "Abnormal termination, exit=%d", n);
X    else
X    if (rm_mpid_on_exit)
X	log_entry('M', "Master terminated%s", s_hangup ? " (hangup)" : "");
X
X    exit(n);
X}
X
X
X/*
X * add new group to master file
X */
X
Xgroup_header *add_new_group(name)
Xchar *name;
X{
X    register group_header *gh;
X    FILE *group_file;
X    static has_warned = 0;
X    
X    if (master.free_groups <= 0) {
X	if (!has_warned) {
X	    log_entry('R', "NO MORE FREE GROUP SLOTS -- RESTART MASTER");
X	    has_warned++;
X	}
X	return NULL;
X    }
X    
X    master.free_groups--;
X    
X    gh = &active_groups[master.number_of_groups];
X    sorted_groups[master.number_of_groups] = gh;
X    
X    gh->group_name_length = strlen(name);
X    gh->group_name = (char *)malloc(gh->group_name_length + 1);
X    mem_check(gh->group_name, gh->group_name_length, "bytes for group name");
X    strcpy(gh->group_name, name);
X    
X    gh->group_num = master.number_of_groups++;
X    
X    group_file = open_groups(OPEN_UPDATE|MUST_EXIST);
X    
X    fseek(group_file, master.next_group_write_offset, 0);
X    name[gh->group_name_length] = NL;
X    Fwrite(name, sizeof(char), gh->group_name_length + 1, group_file);
X    name[gh->group_name_length] = NUL;
X    fclose(group_file);
X    
X    master.next_group_write_offset += gh->group_name_length + 1;
X
X    clean_group(gh);
X    
X    save_master();
X
X    sort_groups();
X
X    log_entry('C', "new group: %s (%d)", gh->group_name, gh->group_num);
X    
X    return gh;
X}
X
X
Xsave_group(gh)
Xgroup_header *gh;
X{
X    int flag;
X    
X    flag = gh->group_flag;
X    gh->group_flag &= G_MASTER_FLAGS;
X
X    if (!db_write_group(master_file, gh, gh->group_num))
X	write_error();
X    fflush(master_file);
X    
X    gh->group_flag = flag;
X}
X
X
Xsave_master()
X{
X    rewind(master_file);
X    if (!db_write_master(master_file, &master))
NO_NEWS_IS_GOOD_NEWS
echo "End of part 6"
echo "File master.c is continued in part 7"
echo "7" > 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.