ftp.nice.ch/pub/next/unix/network/news/nn.6.4.16.s.tar.gz#/nn/macro.c

This is macro.c in view mode; [Download] [Up]

/*
 *	(c) Copyright 1990, Kim Fabricius Storm.  All rights reserved.
 *
 *	Macro parsing and execution.
 */

#include "config.h"
#include "keymap.h"
#include "menu.h"
#include "term.h"

export int in_menu_mode = 0;
export int get_from_macro = 0;
export int macro_debug = 0;
export char *dflt_enter_macro = NULL;
export char *start_up_macro = NULL;

#define M_DUMMY		0	/* do nothing (end of branch)	 */

#define	M_COMMAND	1	/* a command (get_c()) 		 */
#define M_KEY		2	/* a key stroke (get_c()) 	 */
#define M_STRING	3	/* a string (get_s())		 */

#define M_INPUT		4	/* take input from keyboard (get_c/get_s) */

#define M_YES		5	/* answer yes to confirmation	 */
#define M_NO		6	/* answer no to confirmation and break */
				/* -- if neither are present, take input */

#define M_PUTS		7	/* puts "..."			 */
#define M_PROMPT	8	/* prompt(...)	 		 */
#define M_ECHO		9	/* msg(...)			 */

#define M_IS_MENU	10	/* in menu mode ?		 */
#define M_IS_SHOW	11	/* in reading mode ?		 */
#define M_IS_GROUP	12	/* are we in a news group ?	 */
#define M_IS_FOLDER	13	/* are we in a folder ?		 */
#define M_CONFIRM	14	/* ask for confirmation to procede	 */
#define M_REJECT	15	/* ask for !confirmation to procede	 */
#define M_VARTEST	16	/* test value of variable	 */
#define M_BREAK		17	/* exit from macroes		 */
#define	M_RETURN	18	/* return from this macro	 */

#define	M_SET_COMMAND	19	/* set/unset command		 */


struct macro {
    int			m_type;		/* entry type */
    union {
	int 		mu_int;		/* command or char */
	char 		*mu_string;	/* string for get_s */
	struct macro 	*mu_branch;	/* false conditional */
    } m_value;
    struct macro *m_next;		/* next macro element */
};

#define m_int		m_value.mu_int
#define m_string	m_value.mu_string
#define m_branch	m_value.mu_branch

#define NUM_MACRO 101		/* numbered macros */
#define ANON_MACRO 100		/* anonymous macroes */
#define NMACRO (NUM_MACRO + ANON_MACRO)

#define MSTACK 5		/* max nesting level */

static struct macro *macro[NMACRO + 1]; 	/* macro table */
				/* the extra slot is for entry macroes */

static struct macro *mstack[MSTACK];	/* macro stack */
static int cstack[MSTACK + 1];
static int m_level = 0;

static struct macro *m = NULL;		/* current macro */
static int no_advance = 0;
static int changed_prompt = 0;

static int cur_m;

#define MERROR ((struct macro *)1)

static m_error(fmt, arg)
char *fmt, *arg;
{
    char buf[80];

   if (arg) {
       sprintf(buf, fmt, arg);
       fmt = buf;
   }

   init_message("Error in macro %d: %s", cur_m, fmt);
}

init_macro()
{
    int n;

    for (n = 0; n <= NMACRO; n++)
	macro[n] = NULL;
}

static m_new(t)
int t;
{
    struct macro *m1;

    m1 = newobj(struct macro, 1);

    if (m == NULL)
	m = macro[cur_m] = m1;
    else {
	m->m_next = m1;
	m = m1;
    }
    m->m_type = t;
    m->m_next = NULL;
}


/*
 *	Define macro "id" reading from file f until "end"
 *
 *	Macro definition syntax:
 *		define <id>
 *		   <body>
 *		end
 *
 *	Id string interpretation:
 *	NULL 		use next free numbered macro
 *			Return: pointer to macro
 *	"nnn" nnn>=0	Numbered macro nnn
 *			Return: pointer to macro
 *	"-1"		entry macro
 *			Return: pointer to macro
 *	"-2"		anonymous macro
 *			Return: K_MACRO code or K_UNBOUND on error
 */

static int initial_set_commands;

static parse_word(w)
char *w;
{
    int cmd;
    register struct macro *m1;

    if (m && m->m_type == M_COMMAND && m->m_int == (GETC_COMMAND | K_MACRO)) {
	if (isdigit(*w)) {
	    m->m_int |= atoi(w);
	    goto ok;
	}
	m_error("macro number missing", (char *)NULL);
	return 1;
    }

    if (*w == '"') {
	if (m == NULL || (m->m_type != M_PROMPT && m->m_type != M_ECHO && m->m_type != M_PUTS))
	    m_new(M_STRING);
	m->m_string = copy_str(w + 1);
	goto ok;
    }

    if (*w == '\'') {
	m_new(M_KEY);
	m->m_int = parse_key(w + 1);
	goto ok;
    }

    if (*w == '?') {
	if (strchr(w, '=')) {
	    m->m_type = M_VARTEST;
	    m1 = m;
	    m_new(M_DUMMY);
	    m->m_branch = m1->m_branch;
	    m1->m_string = copy_str(w + 1);
	    goto ok;
	}

	switch (w[1]) {
	 case 'f': /* ?folder */
	    cmd = M_IS_FOLDER;
	    break;
	 case 'g': /* ?group */
	    cmd = M_IS_GROUP;
	    break;
	 case 'm': /* ?menu */
	    cmd = M_IS_MENU;
	    break;
	 case 'n': /* ?no */
	    cmd = M_REJECT;
	    break;
	 case 's': /* ?show */
	    cmd = M_IS_SHOW;
	    break;
	 case 'y': /* ?yes */
	    cmd = M_CONFIRM;
	    break;
	 default:
	    m_error("unknown conditional %s", w - 1);
	    return 1;
	}
	m->m_type = cmd;
	goto ok;
    }

    if ((cmd = lookup_command(w, (K_ONLY_MENU | K_ONLY_MORE))) > K_INVALID) {
	m_new(M_COMMAND);
	m->m_int = GETC_COMMAND | cmd;
	goto ok;
    }

    if (strcmp(w, "prompt") == 0) {
	m_new(M_PROMPT);
	m->m_string = "?";
	goto ok;
    }
    if (strcmp(w, "echo") == 0) {
	m_new(M_ECHO);
	m->m_string = "ups";
	goto ok;
    }
    if (strcmp(w, "puts") == 0) {
	m_new(M_PUTS);
	m->m_string = "";
	goto ok;
    }
    if (strcmp(w, "input") == 0) {
	m_new(M_INPUT);
	goto ok;
    }
    if (strcmp(w, "yes") == 0) {
	m_new(M_YES);
	goto ok;
    }
    if (strcmp(w, "no") == 0) {
	m_new(M_NO);
	goto ok;
    }
    if (strcmp(w, "break") == 0) {
	m_new(M_BREAK);
	goto ok;
    }
    if (strcmp(w, "return") == 0) {
	m_new(M_RETURN);
	goto ok;
    }

    m_error("Unknown word >>%s<<", w);
    return 1;

 ok:
    return 0;
}

static parse_line(lp)
char *lp;
{
    char *word;
    struct macro *m1, *branch = NULL;

    while (*lp) {
	if (*lp == '#') break;

	if (*lp == ':') {
	    lp++;
	    if (initial_set_commands) {
#ifdef REL_640_COMPAT
		if (strncmp(lp, "local",  5) == 0 ||
		    strncmp(lp, "set",    3) == 0 ||
		    strncmp(lp, "unset",  5) == 0) {
#else
	        if (lp[0] == ':')
		    lp++;
		else {
#endif
		    m_new(M_SET_COMMAND);
		    m->m_string = copy_str(lp);
		    break;
		}
		initial_set_commands = 0;
	    }
	    m_new(M_COMMAND);
	    m->m_int = GETC_COMMAND | K_EXTENDED_CMD;
	    m_new(M_STRING);
	    m->m_string = copy_str(lp);
	    break;
	}
	initial_set_commands = 0;

	if (*lp == '?') {
	    m_new(M_IS_MENU);
	    if (branch == NULL) {
		m1 = m;
		m_new(M_DUMMY);
		branch = m;
		m = m1;
	    }
	    m->m_branch = branch;
	}

	word = lp;
	if (*lp == '"')
	    do lp++;
	    while (*lp && *lp != '"');
	else
	if (*lp == '\'')
	    do lp++;
	    while (*lp && *lp != '\'');
	else
	    while (*lp && !isspace(*lp)) lp++;
	if (*lp) {
	    *lp++ = NUL;
	    while (*lp && isspace(*lp)) lp++;
	}
	if (parse_word(word)) return 1;
    }

    if (branch) {
	m->m_next = branch;
	m = branch;
    }
    return 0;
}

char *m_define(id, f)
char *id;
FILE *f;
{
    char line[1024], *lp, skip;
    int type = 0;

    if (id) {
	cur_m = atoi(id);
	if (cur_m == -1) {
	    cur_m = NMACRO;	/* special slot for this purpose */
	} else if (cur_m == -2) {
	    for (cur_m = NUM_MACRO; cur_m < NMACRO; cur_m++)
		if (macro[cur_m] == NULL) break;
	    if (cur_m == NMACRO) {
		init_message("No unused macro slots");
		return (char *)K_UNBOUND;
	    }
	    type = 1;
	} else if (cur_m < 0 || cur_m >= NUM_MACRO) {
	    m_error("macro number out of range\n", id);
	    return (char *)0;
	}
    } else {
	for (cur_m = 0; cur_m < NUM_MACRO; cur_m++)
	    if (macro[cur_m] == NULL) break;
	if (cur_m == NUM_MACRO) {
	    init_message("No unused macro numbers");
	    return (char *)0;
	}
    }

    if (f == NULL) {
	clrdisp();
	printf("DEFINE %sMACRO %d -- END WITH 'end'\n\n\r",
	       cur_m >= NUM_MACRO ? "ANONYMOUS " : "",
	       cur_m >= NUM_MACRO ? cur_m - NUM_MACRO : cur_m);
	unset_raw();
	f = stdin;
    }

    m = NULL;
    skip = 0;
    initial_set_commands = (cur_m == NMACRO);

    while (fgets_multi(line, 1024, f)) {
	for (lp = line; *lp && isspace(*lp); lp++);
	if (*lp == NUL) continue;
	if (*lp == ')' || strncmp(lp, "end", 3) == 0) goto out;
	if (!skip && parse_line(lp)) {
	    macro[cur_m] = NULL;
	    skip++;
	}
    }

    if (f != stdin)
	m_error("end missing", (char *)NULL);

 out:
    if (f == stdin) raw();
    m = NULL;
    return type == 0 ? (char *)macro[cur_m] : (char *)(K_MACRO | cur_m);
}

char *m_get_macro(id)
char *id;
{
    if (id) {
	cur_m = atoi(id);
	if (cur_m < 0 || cur_m >= NMACRO) {
	    m_error("macro number out of range\n", id);
	    return (char *)0;
	}
    }
    return (char *)macro[cur_m];
}

char *parse_enter_macro(f, c)
FILE *f;
register int c;
{
    register char *gp;
    char other[FILENAME];
    group_header *gh;
    static char *last_defined = NULL;

    while (c != EOF && c != NL && (!isascii(c) || isspace(c))) c = getc(f);

    if (c == ')') return last_defined;

    if (c == EOF) return (char *)NULL;

    if (c == NL) return last_defined = m_define("-1", f);

    gp = other;
    do {
	*gp++ = c;
	c = getc(f);
    } while (c != EOF && c != ')' && isascii(c) && !isspace(c));

    *gp = NUL;
    if (gh = lookup(other)) return gh->enter_macro;

    return m_get_macro(other);
}

/*
 *	Invoke macro # N
 */

m_invoke(n)
int n;
{
    if (n == -2) {
	n = NMACRO;
	if ((macro[n] = (struct macro *)start_up_macro) == NULL)
	    return;
    } else
    if (n < 0) {
	n = NMACRO;
	if ((macro[n] = (struct macro *)(current_group->enter_macro)) == NULL)
	    if ((macro[n] = (struct macro *)dflt_enter_macro) == NULL)
		return;
    } else
    if (n >= NMACRO || macro[n] == NULL) {
	msg("undefined macro %d", n);
	return;
    }

    if (m_level == 0)
	no_advance = 0;
    else if (m == NULL)
	m_level--;
    else {
	if (m_level > MSTACK) {
	    msg("Macro stack overflow");
	    m_break();
	    return;
	}
	mstack[m_level] = m;
	cstack[m_level] = cur_m;
    }
    m_level++;

    cur_m = n;
    m = macro[cur_m];
    while (m && m->m_type == M_SET_COMMAND) {
	char buffer[128];
	strcpy(buffer, m->m_string);
	if (macro_debug) { msg(":%s", buffer); user_delay(1); }
	parse_command(buffer, AC_UNCHANGED, (FILE *)NULL);
	m = m->m_next;
    }
}

m_startinput()
{
    no_advance = 1;
}

m_endinput()
{
    if (no_advance) {
	no_advance = 0;
	if (m && m->m_type == M_INPUT)
	    m = m->m_next;
    }
}

m_advinput()
{
    if (m && m->m_type == M_INPUT)
	m = m->m_next;
}

static struct macro *m_call(who)
int who;
{
    struct macro *m1;

    for (;;) {
	while (m == NULL && m_level > 1) {
	    m_level--;
	    m = mstack[m_level];
	    cur_m = cstack[m_level];
	}
	if (m == NULL) {
	    if (macro_debug) msg("end");
	    m_break();
	    return NULL;
	}

	if (macro_debug)
	    macro_dbg();

	if (who == 3) {
	    if (m->m_type == M_YES || m->m_type == M_NO) goto out;
	    return NULL;
	}

	switch (m->m_type) {
	 case M_COMMAND:
	    if (m->m_int == (GETC_COMMAND | K_REDRAW))
		changed_prompt = 0;
	 case M_KEY:
	    if (who == 1) goto out;
	    goto err;

	 case M_STRING:
	    if (who == 2) goto out;
	    goto err;

	 case M_INPUT:
	    if (no_advance) return m;
	    goto out;

	 case M_YES:
	 case M_NO:
	 case M_DUMMY:
	    break;

	 case M_PUTS:
	    fputs(m->m_string, stdout); fl;
	    break;

	 case M_PROMPT:
	    if (m->m_string[0] == NUL) {
		changed_prompt = 0;
		break;
	    }
	    if (!changed_prompt) prompt(P_SAVE);
	    changed_prompt = 1;
	    prompt("\1%s\1 ", m->m_string);
	    break;

	 case M_ECHO:
	    msg(m->m_string);
	    restore_xy();
	    break;

	 case M_IS_MENU:
	    if (!in_menu_mode) m = m->m_branch;
	    break;
	 case M_IS_SHOW:
	    if (in_menu_mode) m = m->m_branch;
	    break;
	 case M_IS_GROUP:
	    if (current_group->group_flag & G_FOLDER) m = m->m_branch;
	    break;
	 case M_IS_FOLDER:
	    if ((current_group->group_flag & G_FOLDER) == 0) m = m->m_branch;
	    break;
	 case M_CONFIRM:
	    if (yes(0) == 0) m = m->m_branch;
	    break;
	 case M_REJECT:
	    if (yes(0) == 1) m = m->m_branch;
	    break;

	 case M_VARTEST:
	    m1 = m;
	    m = m->m_next;

	    switch (test_variable(m1->m_string)) {
	     case 0:
		m = m->m_branch;
		break;
	     case -1:
		goto err1;
	    }
	    break;

	 case M_RETURN:
	    m = NULL;
	    continue;

	 case M_BREAK:
	    goto term;
	}

	if (m) m = m->m_next;
    }

 out:
    m1 = m;
    m = m->m_next;
    no_advance = 0;
    return m1;

 err:
    msg("Error in macro %d", cur_m);
 err1:
    user_delay(1);
    m_break();
    return MERROR;

 term:
    m_break();
    return NULL;
}

m_break_entry()
{
    if (current_group->enter_macro || dflt_enter_macro)
	m = NULL;
}

m_break()
{
    if (changed_prompt) prompt(P_RESTORE);
    changed_prompt = 0;
    m = NULL;
    m_level = 0;
}

macro_dbg()
{
    extern char *command_name();
    char *name;

    switch (m->m_type) {
     case M_COMMAND:
	msg("COMMAND: %s", command_name(m->m_int));
	goto delay;

     case M_KEY:
	msg("KEY: %s", key_name((key_type)(m->m_int)));
	goto delay;

     case M_STRING:
	msg("STRING: %s", m->m_string);
	goto delay;

     case M_INPUT:
	name = "input";
	break;

     case M_YES:
	name = "yes";
	break;

     case M_NO:
	name = "no";
	break;

     case M_DUMMY:
	name = "dummy";
	break;

     case M_PROMPT:
	msg("PROMPT: %s", m->m_string);
	goto delay;

     case M_ECHO:
	msg("ECHO: %s", m->m_string);
	goto delay;

     case M_IS_MENU:
	msg("?menu => %d", in_menu_mode);
	goto delay;

     case M_IS_SHOW:
	msg("?show => %d", !in_menu_mode);
	goto delay;

     case M_IS_GROUP:
	msg("?group => %d", (current_group->group_flag & G_FOLDER) == 0);
	goto delay;

     case M_IS_FOLDER:
	msg("?group => %d", (current_group->group_flag & G_FOLDER));
	goto delay;

     case M_CONFIRM:
	name = "?yes";
	break;

     case M_REJECT:
	name = "?no";
	break;

     case M_VARTEST:
	msg("?%s => %d", m->m_string, test_variable(m->m_string));
	goto delay;

     case M_RETURN:
	name = "return";
	break;

     case M_BREAK:
	name = "break";
	break;
    }
    msg(name);

 delay:
    user_delay(1);
}

/*
 *	Macro processing for get_c()
 */

m_getc(cp)
int *cp;
{
    struct macro *m1;

    get_from_macro = 0;
    if (m_level && (m1 = m_call(1))) {
	if (m1 == MERROR) return 2;
	if (m1->m_type == M_INPUT) return 0;
	*cp = m1->m_int;
	get_from_macro = 1;
	return 1;
    }
    return 0;
}

/*
 *	Macro processing for get_s()
 */

m_gets(s)
char *s;
{
    struct macro *m1;

    get_from_macro = 0;
    if (m_level && (m1 = m_call(2))) {
	if (m1 == MERROR) return 2;
	if (m1->m_type == M_INPUT) return 0;
	strcpy(s, m1->m_string);
	get_from_macro = 1;
	return 1;
    }
    return 0;
}

/*
 *	Macro processing for yes()
 */

m_yes()
{
    struct macro *m1;

    if (m)
	if (m->m_type == M_CONFIRM || m->m_type == M_REJECT) return 3;

    if (m_level) {
	if (m1 = m_call(3))
	    if (m1->m_type == M_NO)
		return 1;
	    else
		return 2;
	else
	    return 3;
    }
    return 0;
}

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.