diff --git a/src/frontend/com_ahelp.c b/src/frontend/com_ahelp.c new file mode 100644 index 000000000..1a24bc938 --- /dev/null +++ b/src/frontend/com_ahelp.c @@ -0,0 +1,88 @@ +#include + +#include +#include +#include + +#include "variable.h" +#include "com_help.h" +#include "hcomp.h" +#include "ftehelp.h" + +#include "plotting/plotting.h" + + +void +com_ahelp(wordlist *wl) +{ + int i, n; + /* assert: number of commands must be less than 512 */ + struct comm *cc[512]; + int env = 0; + struct comm *com; + int level; + char slevel[256]; + + if (wl) { + com_help(wl); + return; + } + + out_init(); + + /* determine environment */ + if (plot_list->pl_next) { /* plots load */ + env |= E_HASPLOTS; + } else { + env |= E_NOPLOTS; + } + + /* determine level */ + if (cp_getvar("level", VT_STRING, slevel)) { + switch (*slevel) { + case 'b': level = 1; + break; + case 'i': level = 2; + break; + case 'a': level = 4; + break; + default: level = 1; + break; + } + } else { + level = 1; + } + + out_printf( + "For a complete description read the Spice3 User's Manual manual.\n"); + out_printf( + "For a list of all commands type \"help all\", for a short\n"); + out_printf( + "description of \"command\", type \"help command\".\n"); + + /* sort the commands */ + for (n = 0; cp_coms[n].co_func != (void (*)()) NULL; n++) { + cc[n] = &cp_coms[n]; + } + qsort((char *) cc, n, sizeof(struct comm *), hcomp); + + /* filter the commands */ + for (i=0; i< n; i++) { + com = cc[i]; + if ((com->co_env < (level << 13)) && (!(com->co_env & 4095) || + (env & com->co_env))) { + if ((com->co_spiceonly && ft_nutmeg) || + (com->co_help == (char *) NULL)) { + continue; + } + out_printf("%s ", com->co_comname); + out_printf(com->co_help, cp_program); + out_send("\n"); + } + } + + out_send("\n"); + + return; + +} diff --git a/src/frontend/com_ahelp.h b/src/frontend/com_ahelp.h new file mode 100644 index 000000000..e246cec01 --- /dev/null +++ b/src/frontend/com_ahelp.h @@ -0,0 +1,7 @@ +#ifndef _COM_AHELP_H +#define _COM_AHELP_H + + +void com_ahelp(wordlist *wl); + +#endif diff --git a/src/frontend/com_cdump.c b/src/frontend/com_cdump.c new file mode 100644 index 000000000..3982e5fba --- /dev/null +++ b/src/frontend/com_cdump.c @@ -0,0 +1,140 @@ +#include + +#include + +#include "control.h" +#include "streams.h" + + +static int indent; + + +static void +tab(int num) +{ + int i; + + for (i = 0; i < num; i++) + putc(' ', cp_out); +} + + +static void +dodump(struct control *cc) +{ + struct control *tc; + + switch (cc->co_type) { + case CO_UNFILLED: + tab(indent); + fprintf(cp_out, "(unfilled)\n"); + break; + case CO_STATEMENT: + tab(indent); + wl_print(cc->co_text, cp_out); + putc('\n', cp_out); + break; + case CO_WHILE: + tab(indent); + fprintf(cp_out, "while "); + wl_print(cc->co_cond, cp_out); + putc('\n', cp_out); + indent += 8; + for (tc = cc->co_children; tc; tc = tc->co_next) + dodump(tc); + indent -= 8; + tab(indent); + fprintf(cp_out, "end\n"); + break; + case CO_REPEAT: + tab(indent); + fprintf(cp_out, "repeat "); + if (cc->co_numtimes != -1) + fprintf(cp_out, "%d\n", cc->co_numtimes); + else + putc('\n', cp_out); + indent += 8; + for (tc = cc->co_children; tc; tc = tc->co_next) + dodump(tc); + indent -= 8; + tab(indent); + fprintf(cp_out, "end\n"); + break; + case CO_DOWHILE: + tab(indent); + fprintf(cp_out, "dowhile "); + wl_print(cc->co_cond, cp_out); + putc('\n', cp_out); + indent += 8; + for (tc = cc->co_children; tc; tc = tc->co_next) + dodump(tc); + indent -= 8; + tab(indent); + fprintf(cp_out, "end\n"); + break; + case CO_IF: + tab(indent); + fprintf(cp_out, "if "); + wl_print(cc->co_cond, cp_out); + putc('\n', cp_out); + indent += 8; + for (tc = cc->co_children; tc; tc = tc->co_next) + dodump(tc); + indent -= 8; + tab(indent); + fprintf(cp_out, "end\n"); + break; + case CO_FOREACH: + tab(indent); + fprintf(cp_out, "foreach %s ", cc->co_foreachvar); + wl_print(cc->co_text, cp_out); + putc('\n', cp_out); + indent += 8; + for (tc = cc->co_children; tc; tc = tc->co_next) + dodump(tc); + indent -= 8; + tab(indent); + fprintf(cp_out, "end\n"); + break; + case CO_BREAK: + tab(indent); + if (cc->co_numtimes != 1) + fprintf(cp_out, "break %d\n", cc->co_numtimes); + else + fprintf(cp_out, "break\n"); + break; + case CO_CONTINUE: + tab(indent); + if (cc->co_numtimes != 1) + fprintf(cp_out, "continue %d\n", + cc->co_numtimes); + else + fprintf(cp_out, "continue\n"); + break; + case CO_LABEL: + tab(indent); + fprintf(cp_out, "label %s\n", cc->co_text->wl_word); + break; + case CO_GOTO: + tab(indent); + fprintf(cp_out, "goto %s\n", cc->co_text->wl_word); + break; + default: + tab(indent); + fprintf(cp_out, "bad type %d\n", cc->co_type); + break; + } + return; +} + + +void +com_cdump(wordlist *wl) +{ + struct control *c; + + indent = 0; + for (c = control[stackp]; c; c = c->co_next) + dodump(c); + return; +} diff --git a/src/frontend/com_cdump.h b/src/frontend/com_cdump.h new file mode 100644 index 000000000..ec6e5e145 --- /dev/null +++ b/src/frontend/com_cdump.h @@ -0,0 +1,6 @@ +#ifndef _COM_CDUMP_H +#define _COM_CDUMP_H + +void com_cdump(wordlist *wl); + +#endif diff --git a/src/frontend/com_ghelp.c b/src/frontend/com_ghelp.c new file mode 100644 index 000000000..ffdd2661e --- /dev/null +++ b/src/frontend/com_ghelp.c @@ -0,0 +1,69 @@ +#include +#include +#include +#include +#include + +#include "com_ghelp.h" +#include "com_help.h" +#include "variable.h" +#include "streams.h" + +void +com_ghelp(wordlist *wl) +{ + char *npath; + char *path = Help_Path; + char buf[BSIZE_SP]; + int i; + + if (cp_getvar("helppath", VT_STRING, buf)) + path = copy(buf); + if (!path) { + fprintf(cp_err, "Note: defaulting to old help.\n\n"); + com_help(wl); + return; + } + if (!(npath = cp_tildexpand(path))) { + fprintf(cp_err, "Note: can't find help dir %s\n", path); + fprintf(cp_err, "Defaulting to old help.\n\n"); + com_help(wl); + return; + } + path = npath; + if (cp_getvar("helpregfont", VT_STRING, buf)) + hlp_regfontname = copy(buf); + if (cp_getvar("helpboldfont", VT_STRING, buf)) + hlp_boldfontname = copy(buf); + if (cp_getvar("helpitalicfont", VT_STRING, buf)) + hlp_italicfontname = copy(buf); + if (cp_getvar("helptitlefont", VT_STRING, buf)) + hlp_titlefontname = copy(buf); + if (cp_getvar("helpbuttonfont", VT_STRING, buf)) + hlp_buttonfontname = copy(buf); + if (cp_getvar("helpinitxpos", VT_NUM, (char *) &i)) + hlp_initxpos = i; + if (cp_getvar("helpinitypos", VT_NUM, (char *) &i)) + hlp_initypos = i; + if (cp_getvar("helpbuttonstyle", VT_STRING, buf)) { + if (cieq(buf, "left")) + hlp_buttonstyle = BS_LEFT; + else if (cieq(buf, "center")) + hlp_buttonstyle = BS_CENTER; + else if (cieq(buf, "unif")) + hlp_buttonstyle = BS_UNIF; + else + fprintf(cp_err, "Warning: no such button style %s\n", + buf); + } + if (cp_getvar("width", VT_NUM, (char *) &i)) + hlp_width = i; + if (cp_getvar("display", VT_STRING, buf)) + hlp_displayname = copy(buf); + else if (cp_getvar("device", VT_STRING, buf)) + hlp_displayname = copy(buf); + else + hlp_displayname = NULL; + hlp_main(path, wl); + return; +} diff --git a/src/frontend/com_ghelp.h b/src/frontend/com_ghelp.h new file mode 100644 index 000000000..8fb3667f5 --- /dev/null +++ b/src/frontend/com_ghelp.h @@ -0,0 +1,8 @@ +#ifndef _COM_GHELP_H +#define _COM_GHELP_H + + +void com_ghelp(wordlist *wl); + +#endif + diff --git a/src/frontend/com_help.c b/src/frontend/com_help.c new file mode 100644 index 000000000..a734bbfbd --- /dev/null +++ b/src/frontend/com_help.c @@ -0,0 +1,86 @@ +#include + +#include +#include +#include +#include + +#include "hcomp.h" + + +void +com_help(wordlist *wl) +{ + struct comm *c; + struct comm *ccc[512]; /* Should be enough. */ + int numcoms, i; + bool allflag = FALSE; + + if (wl && eq(wl->wl_word, "all")) { + allflag = TRUE; + wl = NULL; /* XXX Probably right */ + } + + /* We want to use more mode whether "moremode" is set or not. */ + out_moremode = TRUE; + out_init(); + out_moremode = FALSE; + if (wl == NULL) { + out_printf("For a complete description " + "read the Spice3 User's Manual.\n"); + + if (!allflag) { + out_printf("For a list of all commands " + "type \"help all\", for a short\n" + "description of \"command\", " + "type \"help command\".\n"); + } + + /* Sort the commands */ + for (numcoms = 0; cp_coms[numcoms].co_func != NULL; numcoms++) + ccc[numcoms] = &cp_coms[numcoms]; + qsort((char *) ccc, numcoms, sizeof (struct comm *), hcomp); + + for (i = 0; i < numcoms; i++) { + if ((ccc[i]->co_spiceonly && ft_nutmeg) || + (ccc[i]->co_help == NULL) || + (!allflag && !ccc[i]->co_major)) + continue; + out_printf("%s ", ccc[i]->co_comname); + out_printf(ccc[i]->co_help, cp_program); + out_send("\n"); + } + } else { + while (wl != NULL) { + for (c = &cp_coms[0]; c->co_func != NULL; c++) + if (eq(wl->wl_word, c->co_comname)) { + out_printf("%s ", c->co_comname); + out_printf(c->co_help, cp_program); + if (c->co_spiceonly && ft_nutmeg) + out_send(" (Not available in nutmeg)"); + out_send("\n"); + break; + } + if (c->co_func == NULL) { + /* See if this is aliased. */ + struct alias *al; + + for (al = cp_aliases; al; al = al->al_next) + if (eq(al->al_name, wl->wl_word)) + break; + if (al == NULL) + fprintf(cp_out, "Sorry, no help for %s.\n", + wl->wl_word); + else { + out_printf("%s is aliased to ", wl->wl_word); + /* Minor badness here... */ + wl_print(al->al_text, cp_out); + out_send("\n"); + } + } + wl = wl->wl_next; + } + } + out_send("\n"); + return; +} diff --git a/src/frontend/com_help.h b/src/frontend/com_help.h new file mode 100644 index 000000000..d0ab82428 --- /dev/null +++ b/src/frontend/com_help.h @@ -0,0 +1,8 @@ +#ifndef _COM_HELP_H +#define _COM_HELP_H + + +void com_help(wordlist *wl); + +#endif + diff --git a/src/frontend/com_history.c b/src/frontend/com_history.c new file mode 100644 index 000000000..bc37b75ca --- /dev/null +++ b/src/frontend/com_history.c @@ -0,0 +1,488 @@ +/********** +Copyright 1990 Regents of the University of California. All rights reserved. +Author: 1985 Wayne A. Christopher, U. C. Berkeley CAD Group +**********/ + +/* Do history substitutions. */ + +#include +#include + +#include "com_history.h" + + +/* static declarations */ +static wordlist * dohsubst(char *string); +static wordlist * dohmod(char **string, wordlist *wl); +static wordlist * hpattern(char *buf); +static wordlist * hprefix(char *buf); +static wordlist * getevent(int num); +static void freehist(int num); +static char * dohs(char *pat, char *str); + + +struct histent *cp_lastone = NULL; +int cp_maxhistlength = 10000; /* Chris Inbody */ +char cp_hat = '^'; +char cp_bang = '!'; +bool cp_didhsubst; + +static struct histent *histlist = NULL; +static int histlength = 0; + +/* First check for a ^ at the beginning + * of the line, and then search each word for !. Following this can be any + * of string, number, ?string, -number ; then there may be a word specifier, + * the same as csh, and then the : modifiers. For the :s modifier, + * the syntax is :sXoooXnnnX, where X is any character, and ooo and nnn are + * strings not containing X. + */ + +wordlist * +cp_histsubst(wordlist *wlist) +{ + wordlist *nwl, *w, *n; + char buf[BSIZE_SP], *s, *b; + + /* Replace ^old^new with !:s^old^new. */ + + cp_didhsubst = FALSE; + if (*wlist->wl_word == cp_hat) { + (void) sprintf(buf, "%c%c:s%s", cp_bang, cp_bang, + wlist->wl_word); + tfree(wlist->wl_word); + wlist->wl_word = copy(buf); + } + for (w = wlist; w; w = w->wl_next) { + b = w->wl_word; + for (s = b; *s; s++) + if (*s == cp_bang) { + cp_didhsubst = TRUE; + n = dohsubst(s + 1); + if (!n) { + wlist->wl_word = NULL; + return (wlist); + } + if (b < s) { + (void) sprintf(buf, "%.*s%s", s - b, b, + n->wl_word); + tfree(n->wl_word); + n->wl_word = copy(buf); + } + nwl = wl_splice(w, n); + if (wlist == w) + wlist = n; + w = nwl; + break; + } + } + return (wlist); +} + +/* Do a history substitution on one word. Figure out which event is + * being referenced, then do word selections and modifications, and + * then stick anything left over on the end of the last word. + */ + +static wordlist * +dohsubst(char *string) +{ + wordlist *wl, *nwl; + char buf[BSIZE_SP], *s, *r = NULL, *t; + + if (*string == cp_bang) { + if (cp_lastone) { + wl = cp_lastone->hi_wlist; + string++; + } else { + fprintf(cp_err, "0: event not found.\n"); + return (NULL); + } + } else { + switch(*string) { + + case '-': + wl = getevent(cp_event - scannum(++string)); + if (!wl) + return (NULL); + while (isdigit(*string)) + string++; + break; + + case '?': + (void) strcpy(buf, string + 1); + if ((s =strchr(buf, '?'))) + *s = '\0'; + wl = hpattern(buf); + if (!wl) + return (NULL); + if (s == NULL) /* No modifiers on this one. */ + return (wl_copy(wl)); + break; + + case '\0': /* Maybe this should be cp_event. */ + wl = alloc(struct wordlist); + wl->wl_word = copy("!"); + wl->wl_next = NULL; + wl->wl_prev = NULL; + cp_didhsubst = FALSE; + return (wl); + + default: + if (isdigit(*string)) { + wl = getevent(scannum(string)); + if (!wl) + return (NULL); + while (isdigit(*string)) + string++; + } else { + (void) strcpy(buf, string); + for (s = ":^$*-%"; *s; s++) { + t =strchr(buf, *s); + if (t && ((t < r) || !r)) { + r = t; + string += r - buf; + } + } + if (r) + *r = '\0'; + else + while (*string) + string++; + if ((buf[0] == '\0') && cp_lastone) + wl = cp_lastone->hi_wlist; + else + wl = hprefix(buf); + if (!wl) + return (NULL); + } + } + } + if (wl == NULL) { /* Shouldn't happen. */ + fprintf(cp_err, "Event not found.\n"); + return (NULL); + } + nwl = dohmod(&string, wl_copy(wl)); + if (!nwl) + return (NULL); + if (*string) { + for (wl = nwl; wl->wl_next; wl = wl->wl_next) + ; + (void) sprintf(buf, "%s%s", wl->wl_word, string); + tfree(wl->wl_word); + wl->wl_word = copy(buf); + } + return (nwl); +} + +static wordlist * +dohmod(char **string, wordlist *wl) +{ + wordlist *w; + char *s; + char *r = NULL, *t; + int numwords, eventlo, eventhi, i; + bool globalsubst; + +anothermod: + numwords = wl_length(wl); + globalsubst = FALSE; + eventlo = 0; + eventhi = numwords - 1; + + /* Now we know what wordlist we want. Take care of modifiers now. */ + r = NULL; + for (s = ":^$*-%"; *s; s++) { + t =strchr(*string, *s); + if (t && ((t < r) || (r == NULL))) + r = t; + } + if (!r) /* No more modifiers. */ + return (wl); + + *string = r; + if (**string == ':') + (*string)++; + + switch(**string) { + case '$': /* Last word. */ + eventhi = eventlo = numwords - 1; + break; + case '*': /* Words 1 through $ */ + if (numwords == 1) + return (NULL); + eventlo = 1; + eventhi = numwords - 1; + break; + case '-': /* Words 0 through ... */ + eventlo = 0; + if (*(*string + 1)) + eventhi = scannum(*string + 1); + else + eventhi = numwords - 1; + if (eventhi > numwords - 1) + eventhi = numwords - 1; + break; + case 'p': /* Print the command and don't execute it. + * This doesn't work quite like csh. + */ + wl_print(wl, cp_out); + (void) putc('\n', cp_out); + return (NULL); + case 's': /* Do a substitution. */ + for (w = wl; w; w = w->wl_next) { + s = dohs(*string + 1, w->wl_word); + if (s) { + tfree(w->wl_word); + w->wl_word = s; + if (globalsubst == FALSE) { + while (**string) + (*string)++; + break; + } + } + } + /* In case globalsubst is TRUE... */ + while (**string) + (*string)++; + break; + default: + if (!isdigit(**string)) { + fprintf(cp_err, "Error: %s: bad modifier.\n", + *string); + return (NULL); + } + i = scannum(*string); + if (i > eventhi) { + fprintf(cp_err, "Error: bad event number %d\n", + i); + return (NULL); + } + eventhi = eventlo = i; + while (isdigit(**string)) + (*string)++; + if (**string == '*') + eventhi = numwords - 1; + if (**string == '-') { + if (!isdigit(*(*string + 1))) + eventhi = numwords - 1; + else { + eventhi = scannum(++*string); + while (isdigit(**string)) + (*string)++; + } + } + } + /* Now change the word list accordingly and make another pass + * if there is more of the substitute left. + */ + + wl = wl_range(wl, eventlo, eventhi); + numwords = wl_length(wl); + if (**string && *++*string) + goto anothermod; + return (wl); +} + +/* Look for an event with a pattern in it... */ + +static wordlist * +hpattern(char *buf) +{ + struct histent *hi; + wordlist *wl; + + if (*buf == '\0') { + fprintf(cp_err, "Bad pattern specification.\n"); + return (NULL); + } + for (hi = cp_lastone; hi; hi = hi->hi_prev) + for (wl = hi->hi_wlist; wl; wl = wl->wl_next) + if (substring(buf, wl->wl_word)) + return (hi->hi_wlist); + fprintf(cp_err, "%s: event not found.\n", buf); + return (NULL); +} + +static wordlist * +hprefix(char *buf) +{ + struct histent *hi; + + if (*buf == '\0') { + fprintf(cp_err, "Bad pattern specification.\n"); + return (NULL); + } + for (hi = cp_lastone; hi; hi = hi->hi_prev) + if (hi->hi_wlist && prefix(buf, hi->hi_wlist->wl_word)) + return (hi->hi_wlist); + fprintf(cp_err, "%s: event not found.\n", buf); + return (NULL); +} + +/* Add a wordlist to the history list. (Done after the first parse.) Note + * that if event numbers are given in a random order that's how they'll + * show up in the history list. + */ + +void +cp_addhistent(int event, wordlist *wlist) +{ + if (cp_lastone && !cp_lastone->hi_wlist) + fprintf(cp_err, "Internal error: bad history list\n"); + if (cp_lastone == NULL) { + cp_lastone = histlist = alloc(struct histent); + cp_lastone->hi_prev = NULL; + } else { + cp_lastone->hi_next = alloc(struct histent); + cp_lastone->hi_next->hi_prev = cp_lastone; + cp_lastone = cp_lastone->hi_next; + } + cp_lastone->hi_next = NULL; + cp_lastone->hi_event = event; + cp_lastone->hi_wlist = wl_copy(wlist); + freehist(histlength - cp_maxhistlength); + histlength++; + return; +} + +/* Get a copy of the wordlist associated with an event. Error if out + * of range. + */ + +static wordlist * +getevent(int num) +{ + struct histent *hi; + + for (hi = histlist; hi; hi = hi->hi_next) + if (hi->hi_event == num) + break; + if (hi == NULL) { + fprintf(cp_err, "%d: event not found.\n", num); + return (NULL); + } + return (wl_copy(hi->hi_wlist)); +} + +/* Print out history between eventhi and eventlo. + * This doesn't remember quoting, so 'hodedo' prints as hodedo. + */ + +void +cp_hprint(int eventhi, int eventlo, bool rev) +{ + struct histent *hi; + + if (rev) { + for (hi = histlist; hi->hi_next; hi = hi->hi_next) + ; + for (; hi; hi = hi->hi_prev) + if ((hi->hi_event <= eventhi) && + (hi->hi_event >= eventlo) && + hi->hi_wlist) { + fprintf(cp_out, "%d\t", hi->hi_event); + wl_print(hi->hi_wlist, cp_out); + (void) putc('\n', cp_out); + } + } else { + for (hi = histlist; hi; hi = hi->hi_next) + if ((hi->hi_event <= eventhi) && + (hi->hi_event >= eventlo) && + hi->hi_wlist) { + fprintf(cp_out, "%d\t", hi->hi_event); + wl_print(hi->hi_wlist, cp_out); + (void) putc('\n', cp_out); + } + } + return; +} + +/* This just gets rid of the first num entries on the history list, and + * decrements histlength. + */ + +static void +freehist(int num) +{ + struct histent *hi; + + if (num < 1) + return; + histlength -= num; + hi = histlist; + while (num-- && histlist->hi_next) + histlist = histlist->hi_next; + if (histlist->hi_prev) { + histlist->hi_prev->hi_next = NULL; + histlist->hi_prev = NULL; + } else + { + fprintf(cp_err, "Internal error: history list mangled\n"); + exit(0); /* Chris Inbody */ + } + while (hi->hi_next) { + wl_free(hi->hi_wlist); + hi = hi->hi_next; + tfree(hi->hi_prev); + } + wl_free(hi->hi_wlist); + tfree(hi); + return; +} + +/* Do a :s substitution. */ + +static char * +dohs(char *pat, char *str) +{ + char schar, *s, *p, buf[BSIZE_SP]; + int i = 0, plen; + bool ok = FALSE; + + pat = copy(pat); /* Don't want to mangle anything. */ + schar = *pat++; + s =strchr(pat, schar); + if (s == NULL) { + fprintf(cp_err, "Bad substitute.\n"); + return (NULL); + } + *s++ = '\0'; + p =strchr(s, schar); + if (p) + *p = '\0'; + plen = strlen(pat) - 1; + for (i = 0; *str; str++) { + if ((*str == *pat) && prefix(pat, str) && (ok == FALSE)) { + for (p = s; *p; p++) + buf[i++] = *p; + str += plen; + ok = TRUE; + } else + buf[i++] = *str; + } + buf[i] = '\0'; + if (ok) + return (copy(buf)); + else + return (NULL); +} + +/* The "history" command. history [-r] [number] */ + +void +com_history(wordlist *wl) +{ + bool rev = FALSE; + + if (wl && eq(wl->wl_word, "-r")) { + wl = wl->wl_next; + rev = TRUE; + } + if (wl == NULL) + cp_hprint(cp_event - 1, cp_event - histlength, rev); + else + cp_hprint(cp_event - 1, cp_event - 1 - atoi(wl->wl_word), rev); + return; +} + diff --git a/src/frontend/com_history.h b/src/frontend/com_history.h new file mode 100644 index 000000000..7b83b1272 --- /dev/null +++ b/src/frontend/com_history.h @@ -0,0 +1,16 @@ +/************* + * Header file for history.c + * 1999 E. Rouat + ************/ + +#ifndef _COM_HISTORY_H +#define _COM_HISTORY_H + +wordlist * cp_histsubst(wordlist *wlist); +void cp_addhistent(int event, wordlist *wlist); +void cp_hprint(int eventhi, int eventlo, bool rev); +void com_history(wordlist *wl); + + + +#endif diff --git a/src/frontend/com_set.c b/src/frontend/com_set.c new file mode 100644 index 000000000..00d81bfc5 --- /dev/null +++ b/src/frontend/com_set.c @@ -0,0 +1,51 @@ +#include +#include + +#include +#include + +#include "variable.h" + + +/* The set command. Syntax is set [opt ...] [opt = val ...]. Val may + * be a string, an int, a float, or a list of the form (elt1 elt2 + * ...). */ +void +com_set(wordlist *wl) +{ + struct variable *vars; + char *s; + + if (wl == NULL) { + cp_vprint(); + return; + } + vars = cp_setparse(wl); + + /* This is sort of a hassle... */ + while (vars) { + switch (vars->va_type) { + case VT_BOOL: + s = (char *) &vars->va_bool; + break; + case VT_NUM: + s = (char *) &vars->va_num; + break; + case VT_REAL: + s = (char *) &vars->va_real; + break; + case VT_STRING: + s = vars->va_string; + break; + case VT_LIST: + s = (char *) vars->va_vlist; + break; + default: + s = (char *) NULL; + } + cp_vset(vars->va_name, vars->va_type, s); + vars = vars->va_next; + } + return; +} + diff --git a/src/frontend/com_shift.c b/src/frontend/com_shift.c new file mode 100644 index 000000000..a53a816e1 --- /dev/null +++ b/src/frontend/com_shift.c @@ -0,0 +1,46 @@ +#include +#include +#include +#include + +#include "variable.h" +#include "streams.h" + + +/* Shift a list variable, by default argv, one to the left (or more if + * a second argument is given. */ +void +com_shift(wordlist *wl) +{ + struct variable *v, *vv; + char *n = "argv"; + int num = 1; + + if (wl) { + n = wl->wl_word; + wl = wl->wl_next; + } + if (wl) + num = scannum(wl->wl_word); + + for (v = variables; v; v = v->va_next) + if (eq(v->va_name, n)) + break; + if (!v) { + fprintf(cp_err, "Error: %s: no such variable\n", n); + return; + } + if (v->va_type != VT_LIST) { + fprintf(cp_err, "Error: %s not of type list\n", n); + return; + } + for (vv = v->va_vlist; vv && (num > 0); num--) + vv = vv->va_next; + if (num) { + fprintf(cp_err, "Error: variable %s not long enough\n", n); + return; + } + + v->va_vlist = vv; + return; +} diff --git a/src/frontend/com_strcmp.c b/src/frontend/com_strcmp.c new file mode 100644 index 000000000..dcb9e36e0 --- /dev/null +++ b/src/frontend/com_strcmp.c @@ -0,0 +1,23 @@ +#include +#include + +#include "com_strcmp.h" +#include "variable.h" + + +/* This is a truly evil thing */ +void +com_strcmp(wordlist *wl) +{ + char *var, *s1, *s2; + int i; + + var = wl->wl_word; + s1 = cp_unquote(wl->wl_next->wl_word); + s2 = cp_unquote(wl->wl_next->wl_next->wl_word); + + i = strcmp(s1, s2); + + cp_vset(var, VT_NUM, (char *) &i); + return; +} diff --git a/src/frontend/com_strcmp.h b/src/frontend/com_strcmp.h new file mode 100644 index 000000000..e94bfbe16 --- /dev/null +++ b/src/frontend/com_strcmp.h @@ -0,0 +1,7 @@ +#ifndef _COM_STRCMP_H +#define _COM_STRCMP_H + + +void com_strcmp(wordlist *wl); + +#endif diff --git a/src/frontend/com_unset.c b/src/frontend/com_unset.c new file mode 100644 index 000000000..a7ea5fd6a --- /dev/null +++ b/src/frontend/com_unset.c @@ -0,0 +1,30 @@ +#include +#include + +#include +#include +#include + +#include "variable.h" + + +void +com_unset(wordlist *wl) +{ + char *name; + struct variable *var, *nv; + + if (eq(wl->wl_word, "*")) { + for (var = variables; var; var = nv) { + nv = var->va_next; + cp_remvar(var->va_name); + } + wl = wl->wl_next; + } + while (wl != NULL) { + name = wl->wl_word; + cp_remvar(name); + wl = wl->wl_next; + } + return; +} diff --git a/src/frontend/control.c b/src/frontend/control.c new file mode 100644 index 000000000..038f44a90 --- /dev/null +++ b/src/frontend/control.c @@ -0,0 +1,799 @@ +/********** +Copyright 1990 Regents of the University of California. All rights reserved. +Author: 1985 Wayne A. Christopher, U. C. Berkeley CAD Group +**********/ + +/* The front-end command loop. */ + +#include +#include + +#include "control.h" +#include "com_cdump.h" +#include "variable.h" + +/* Return values from doblock(). I am assuming that nobody will use + * these characters in a string. */ +#define NORMAL '\001' +#define BROKEN '\002' +#define CONTINUED '\003' +#define NORMAL_STR "\001" +#define BROKEN_STR "\002" +#define CONTINUED_STR "\003" + +/* Are we waiting for a command? This lets signal handling be + * more clever. */ + +bool cp_cwait = FALSE; +char *cp_csep = ";"; + +bool cp_dounixcom = FALSE; + +/* We have to keep the control structures in a stack, so that when we + * do a 'source', we can push a fresh set onto the top... Actually + * there have to be two stacks -- one for the pointer to the list of + * control structs, and one for the 'current command' pointer... */ +struct control *control[CONTROLSTACKSIZE]; +struct control *cend[CONTROLSTACKSIZE]; +int stackp = 0; + + +/* If there is an argument, give this to cshpar to use instead of + * stdin. In a few places, we call cp_evloop again if it returns 1 and + * exit (or close a file) if it returns 0... Because of the way + * sources are done, we can't allow the control structures to get + * blown away every time we return -- probably every time we type + * source at the keyboard and every time a source returns to keyboard + * input is ok though -- use ft_controlreset. */ + +static char *noredirect[] = { "stop", NULL } ; /* Only one?? */ + + +static struct control * +findlabel(char *s, struct control *ct) +{ + while (ct) { + if ((ct->co_type == CO_LABEL) && eq(s, ct->co_text->wl_word)) + break; + ct = ct->co_next; + } + return (ct); +} + + +/* This is also in cshpar.c ... */ +static void +pwlist(wordlist *wlist, char *name) +{ + wordlist *wl; + + if (!cp_debug) + return; + fprintf(cp_err, "%s : [ ", name); + for (wl = wlist; wl; wl = wl->wl_next) + fprintf(cp_err, "%s ", wl->wl_word); + fprintf(cp_err, "]\n"); + return; +} + + + +/* Note that we only do io redirection when we get to here - we also + * postpone some other things until now. */ +static void +docommand(wordlist *wlist) +{ + char *r, *s, *t; + char *lcom; + int nargs; + int i; + struct comm *command; + wordlist *wl, *nextc, *ee, *rwlist; + + if (cp_debug) { + printf("docommand "); + wl_print(wlist, stdout); + putc('\n', stdout); + } + + /* Do all the things that used to be done by cshpar when the line + * was read... */ + wlist = cp_variablesubst(wlist); + pwlist(wlist, "After variable substitution"); + + wlist = cp_bquote(wlist); + pwlist(wlist, "After backquote substitution"); + + wlist = cp_doglob(wlist); + pwlist(wlist, "After globbing"); + + if (!wlist || !wlist->wl_word) + return; + + /* Now loop through all of the commands given. */ + rwlist = wlist; + do { + for (nextc = wlist; nextc; nextc = nextc->wl_next) + if (eq(nextc->wl_word, cp_csep)) + break; + + /* Temporarily hide the rest of the command... */ + if (nextc && nextc->wl_prev) + nextc->wl_prev->wl_next = NULL; + ee = wlist->wl_prev; + if (ee) + wlist->wl_prev = NULL; + + if (nextc == wlist) { + /* There was no text... */ + goto out; + } + + /* And do the redirection. */ + cp_ioreset(); + for (i = 0; noredirect[i]; i++) + if (eq(wlist->wl_word, noredirect[i])) + break; + if (!noredirect[i]) { + if (!(wlist = cp_redirect(wlist))) { + cp_ioreset(); + return; + } + } + + /* Get rid of all the 8th bits now... */ + cp_striplist(wlist); + + s = wlist->wl_word; + + /* Look for the command in the command list. */ + for (i = 0; cp_coms[i].co_comname; i++) { + /* strcmp(cp_coms[i].co_comname, s) ... */ + for (t = cp_coms[i].co_comname, r = s; *t && *r; + t++, r++) + if (*t != *r) + break; + if (!*t && !*r) + break; + } + + /* Now give the user-supplied command routine a try... */ + if (!cp_coms[i].co_func && cp_oddcomm(s, wlist->wl_next)) + goto out; + + /* If it's not there, try it as a unix command. */ + if (!cp_coms[i].co_comname) { + if (cp_dounixcom && cp_unixcom(wlist)) + goto out; + fprintf(cp_err,"%s: no such command available in %s\n", + s, cp_program); + goto out; + + /* If it's there but spiceonly, and this is nutmeg, error. */ + } else if (!cp_coms[i].co_func && ft_nutmeg && + (cp_coms[i].co_spiceonly)) { + fprintf(cp_err,"%s: command available only in spice\n", + s); + goto out; + } + + /* The command was a valid spice/nutmeg command. */ + command = &cp_coms[i]; + nargs = 0; + for (wl = wlist->wl_next; wl; wl = wl->wl_next) + nargs++; + if (command->co_stringargs) { + lcom = wl_flatten(wlist->wl_next); + (*command->co_func) (lcom); + } else { + if (nargs < command->co_minargs) { + if (command->co_argfn) { + (*command->co_argfn) (wlist->wl_next, command); + } else { + fprintf(cp_err, "%s: too few args.\n", s); + } + } else if (nargs > command->co_maxargs) { + fprintf(cp_err, "%s: too many args.\n", s); + } else + (*command->co_func) (wlist->wl_next); + } + + /* Now fix the pointers and advance wlist. */ + out: wlist->wl_prev = ee; + if (nextc) { + if (nextc->wl_prev) + nextc->wl_prev->wl_next = nextc; + wlist = nextc->wl_next; + } + } while (nextc && wlist); + + wl_free(rwlist); + + /* Do periodic sorts of things... */ + cp_periodic(); + + cp_ioreset(); + return; +} + + +/* Execute a block. There can be a number of return values from this routine. + * NORMAL indicates a normal termination + * BROKEN indicates a break -- if the caller is a breakable loop, + * terminate it, otherwise pass the break upwards + * CONTINUED indicates a continue -- if the caller is a continuable loop, + * continue, else pass the continue upwards + * Any other return code is considered a pointer to a string which is + * a label somewhere -- if this label is present in the block, + * goto it, otherwise pass it up. Note that this prevents jumping + * into a loop, which is good. + * + * Note that here is where we expand variables, ``, and globs for + * controls. + * + * The 'num' argument is used by break n and continue n. */ +static char * +doblock(struct control *bl, int *num) +{ + struct control *ch, *cn = NULL; + wordlist *wl; + char *i; + int nn; + + switch (bl->co_type) { + case CO_WHILE: + while (bl->co_cond && cp_isTRUE(bl->co_cond)) { + for (ch = bl->co_children; ch; ch = cn) { + cn = ch->co_next; + i = doblock(ch, &nn); + switch (*i) { + + case NORMAL: + break; + + case BROKEN: /* Break. */ + if (nn < 2) + return (NORMAL_STR); + else { + *num = nn - 1; + return (BROKEN_STR); + } + + case CONTINUED: /* Continue. */ + if (nn < 2) { + cn = NULL; + break; + } else { + *num = nn - 1; + return (CONTINUED_STR); + } + + default: + cn = findlabel(i, bl->co_children); + if (!cn) + return (i); + } + } + } + break; + + case CO_DOWHILE: + do { + for (ch = bl->co_children; ch; ch = cn) { + cn = ch->co_next; + i = doblock(ch, &nn); + switch (*i) { + + case NORMAL: + break; + + case BROKEN: /* Break. */ + if (nn < 2) + return (NORMAL_STR); + else { + *num = nn - 1; + return (BROKEN_STR); + } + + case CONTINUED: /* Continue. */ + if (nn < 2) { + cn = NULL; + break; + } else { + *num = nn - 1; + return (CONTINUED_STR); + } + + default: + cn = findlabel(i, bl->co_children); + if (!cn) + return (i); + } + } + } while (bl->co_cond && cp_isTRUE(bl->co_cond)); + break; + + case CO_REPEAT: + while ((bl->co_numtimes > 0) || + (bl->co_numtimes == -1)) { + if (bl->co_numtimes != -1) + bl->co_numtimes--; + for (ch = bl->co_children; ch; ch = cn) { + cn = ch->co_next; + i = doblock(ch, &nn); + switch (*i) { + + case NORMAL: + break; + + case BROKEN: /* Break. */ + if (nn < 2) + return (NORMAL_STR); + else { + *num = nn - 1; + return (BROKEN_STR); + } + + case CONTINUED: /* Continue. */ + if (nn < 2) { + cn = NULL; + break; + } else { + *num = nn - 1; + return (CONTINUED_STR); + } + + default: + cn = findlabel(i, bl->co_children); + if (!cn) + return (i); + } + } + } + break; + + case CO_IF: + if (bl->co_cond && cp_isTRUE(bl->co_cond)) { + for (ch = bl->co_children; ch; ch = cn) { + cn = ch->co_next; + i = doblock(ch, &nn); + if (*i > 2) { + cn = findlabel(i, + bl->co_children); + if (!cn) + return (i); + } else if (*i != NORMAL) { + *num = nn; + return (i); + } + } + } else { + for (ch = bl->co_elseblock; ch; ch = cn) { + cn = ch->co_next; + i = doblock(ch, &nn); + if (*i > 2) { + cn = findlabel(i, + bl->co_elseblock); + if (!cn) + return (i); + } else if (*i != NORMAL) { + *num = nn; + return (i); + } + } + } + break; + + case CO_FOREACH: + for (wl = cp_variablesubst(cp_bquote(cp_doglob(wl_copy(bl->co_text)))); + wl; + wl = wl->wl_next) { + cp_vset(bl->co_foreachvar, VT_STRING, wl->wl_word); + for (ch = bl->co_children; ch; ch = cn) { + cn = ch->co_next; + i = doblock(ch, &nn); + switch (*i) { + + case NORMAL: + break; + + case BROKEN: /* Break. */ + if (nn < 2) + return (NORMAL_STR); + else { + *num = nn - 1; + return (BROKEN_STR); + } + + case CONTINUED: /* Continue. */ + if (nn < 2) { + cn = NULL; + break; + } else { + *num = nn - 1; + return (CONTINUED_STR); + } + + default: + cn = findlabel(i, bl->co_children); + if (!cn) + return (i); + } + } + } + break; + + case CO_BREAK: + if (bl->co_numtimes > 0) { + *num = bl->co_numtimes; + return (BROKEN_STR); + } else { + fprintf(cp_err, "Warning: break %d a no-op\n", + bl->co_numtimes); + return (NORMAL_STR); + } + + case CO_CONTINUE: + if (bl->co_numtimes > 0) { + *num = bl->co_numtimes; + return (CONTINUED_STR); + } else { + fprintf(cp_err, "Warning: continue %d a no-op\n", + bl->co_numtimes); + return (NORMAL_STR); + } + + case CO_GOTO: + wl = cp_variablesubst(cp_bquote(cp_doglob( + wl_copy(bl->co_text)))); + return (wl->wl_word); + + case CO_LABEL: + /* Do nothing. */ + break; + + case CO_STATEMENT: + docommand(wl_copy(bl->co_text)); + break; + + case CO_UNFILLED: + /* There was probably an error here... */ + fprintf(cp_err, "Warning: ignoring previous error\n"); + break; + + default: + fprintf(cp_err, + "doblock: Internal Error: bad block type %d\n", + bl->co_type); + return (NORMAL_STR); + } + return (NORMAL_STR); +} + + +/* Get a command. This does all the bookkeeping things like turning + * command completion on and off... */ +static wordlist * +getcommand(char *string) +{ + wordlist *wlist; + int i = 0, j; + static char buf[64]; + struct control *c; + + if (cp_debug) + fprintf(cp_err, "calling getcommand %s\n", + string ? string : ""); + if (cend[stackp]) { + for (c = cend[stackp]->co_parent; c; c = c->co_parent) + i++; + if (i) { + for (j = 0; j < i; j++) + buf[j] = '>'; + buf[j] = ' '; + buf[j + 1] = '\0'; + cp_altprompt = buf; + } else + cp_altprompt = NULL; + } else + cp_altprompt = NULL; + + cp_cwait = TRUE; + wlist = cp_parse(string); + cp_cwait = FALSE; + if (cp_debug) { + printf("getcommand "); + wl_print(wlist, stdout); + putc('\n', stdout); + } + return (wlist); +} + + +int +cp_evloop(char *string) +{ + wordlist *wlist, *ww; + struct control *x; + char *i; + int nn; + +#define newblock cend[stackp]->co_children = alloc(struct control); \ + ZERO(cend[stackp]->co_children,struct control), \ + cend[stackp]->co_children->co_parent = cend[stackp]; \ + cend[stackp] = cend[stackp]->co_children; \ + cend[stackp]->co_type = CO_UNFILLED; + + for (;;) { + wlist = getcommand(string); + if (wlist == NULL) { /* End of file or end of user input. */ + if (cend[stackp]->co_parent && !string) { + cp_resetcontrol(); + continue; + } else + return (0); + } + if ((wlist->wl_word == NULL) || (*wlist->wl_word == '\0')) { + /* User just typed return. */ + if (string) + return (1); + else { + cp_event--; + continue; + } + } + + /* Just a check... */ + for (ww = wlist; ww; ww = ww->wl_next) + if (!ww->wl_word) { + fprintf(cp_err, + "cp_evloop: Internal Error: NULL word pointer\n"); + continue; + } + + + /* Add this to the control structure list. If cend->co_type is + * CO_UNFILLED, the last line was the beginning of a block, + * and this is the unfilled first statement. */ + if (cend[stackp] && (cend[stackp]->co_type != CO_UNFILLED)) { + cend[stackp]->co_next = alloc(struct control); + ZERO(cend[stackp]->co_next, struct control); + cend[stackp]->co_next->co_prev = cend[stackp]; + cend[stackp]->co_next->co_parent = + cend[stackp]->co_parent; + cend[stackp] = cend[stackp]->co_next; + } else if (!cend[stackp]) { + control[stackp] = cend[stackp] = alloc(struct control); + ZERO(cend[stackp], struct control); + } + + if (eq(wlist->wl_word, "while")) { + cend[stackp]->co_type = CO_WHILE; + cend[stackp]->co_cond = wlist->wl_next; + if (!cend[stackp]->co_cond) { + fprintf(stderr, + "Error: missing while condition.\n"); + } + newblock; + } else if (eq(wlist->wl_word, "dowhile")) { + cend[stackp]->co_type = CO_DOWHILE; + cend[stackp]->co_cond = wlist->wl_next; + if (!cend[stackp]->co_cond) { + fprintf(stderr, + "Error: missing dowhile condition.\n"); + } + newblock; + } else if (eq(wlist->wl_word, "repeat")) { + cend[stackp]->co_type = CO_REPEAT; + if (!wlist->wl_next) { + cend[stackp]->co_numtimes = -1; + } else { + char *s; + double *dd; + wlist = cp_variablesubst(cp_bquote( + cp_doglob(wl_copy(wlist)))); + s = wlist->wl_next->wl_word; + + dd = ft_numparse(&s, FALSE); + if (dd) { + if (*dd < 0) { + fprintf(cp_err, + "Error: can't repeat a negative number of times\n"); + *dd = 0.0; + } + cend[stackp]->co_numtimes = (int) *dd; + } else + fprintf(cp_err, + "Error: bad repeat argument %s\n", + wlist->wl_next->wl_word); + } + newblock; + } else if (eq(wlist->wl_word, "if")) { + cend[stackp]->co_type = CO_IF; + cend[stackp]->co_cond = wlist->wl_next; + if (!cend[stackp]->co_cond) { + fprintf(stderr, + "Error: missing if condition.\n"); + } + newblock; + } else if (eq(wlist->wl_word, "foreach")) { + cend[stackp]->co_type = CO_FOREACH; + if (wlist->wl_next) { + wlist = wlist->wl_next; + cend[stackp]->co_foreachvar = + copy(wlist->wl_word); + wlist = wlist->wl_next; + } else + fprintf(stderr, + "Error: missing foreach variable.\n"); + wlist = cp_doglob(wlist); + cend[stackp]->co_text = wl_copy(wlist); + newblock; + } else if (eq(wlist->wl_word, "label")) { + cend[stackp]->co_type = CO_LABEL; + if (wlist->wl_next) { + cend[stackp]->co_text = wl_copy(wlist->wl_next); + /* I think of everything, don't I? */ + cp_addkword(CT_LABEL, wlist->wl_next->wl_word); + if (wlist->wl_next->wl_next) + fprintf(cp_err, + "Warning: ignored extra junk after label.\n"); + } else + fprintf(stderr, "Error: missing label.\n"); + } else if (eq(wlist->wl_word, "goto")) { + /* Incidentally, this won't work if the values 1 and 2 ever get + * to be valid character pointers -- I think it's reasonably + * safe to assume they aren't... */ + cend[stackp]->co_type = CO_GOTO; + if (wlist->wl_next) { + cend[stackp]->co_text = wl_copy(wlist->wl_next); + if (wlist->wl_next->wl_next) + fprintf(cp_err, + "Warning: ignored extra junk after goto.\n"); + } else + fprintf(stderr, "Error: missing label.\n"); + } else if (eq(wlist->wl_word, "continue")) { + cend[stackp]->co_type = CO_CONTINUE; + if (wlist->wl_next) { + cend[stackp]->co_numtimes = scannum(wlist-> + wl_next->wl_word); + if (wlist->wl_next->wl_next) + fprintf(cp_err, + "Warning: ignored extra junk after continue %d.\n", + cend[stackp]->co_numtimes); + } else + cend[stackp]->co_numtimes = 1; + } else if (eq(wlist->wl_word, "break")) { + cend[stackp]->co_type = CO_BREAK; + if (wlist->wl_next) { + cend[stackp]->co_numtimes = scannum(wlist-> + wl_next->wl_word); + if (wlist->wl_next->wl_next) + fprintf(cp_err, + "Warning: ignored extra junk after break %d.\n", + cend[stackp]->co_numtimes); + } else + cend[stackp]->co_numtimes = 1; + } else if (eq(wlist->wl_word, "end")) { + /* Throw away this thing. */ + if (!cend[stackp]->co_parent) { + fprintf(stderr, "Error: no block to end.\n"); + cend[stackp]->co_type = CO_UNFILLED; + } else if (cend[stackp]->co_prev) { + cend[stackp]->co_prev->co_next = NULL; + x = cend[stackp]; + cend[stackp] = cend[stackp]->co_parent; + tfree(x); + } else { + x = cend[stackp]; + cend[stackp] = cend[stackp]->co_parent; + cend[stackp]->co_children = NULL; + tfree(x); + } + } else if (eq(wlist->wl_word, "else")) { + if (!cend[stackp]->co_parent || + (cend[stackp]->co_parent->co_type != + CO_IF)) { + fprintf(stderr, "Error: misplaced else.\n"); + cend[stackp]->co_type = CO_UNFILLED; + } else { + if (cend[stackp]->co_prev) + cend[stackp]->co_prev->co_next = NULL; + else + cend[stackp]->co_parent->co_children = NULL; + cend[stackp]->co_parent->co_elseblock = cend[stackp]; + cend[stackp]->co_prev = NULL; + } + } else { + cend[stackp]->co_type = CO_STATEMENT; + cend[stackp]->co_text = wlist; + } + if (!cend[stackp]->co_parent) { + x = cend[stackp]; + /* We have to toss this do-while loop in here so + * that gotos at the top level will work. + */ + do { + i = doblock(x, &nn); + switch (*i) { + case NORMAL: + break; + case BROKEN: + fprintf(cp_err, + "Error: break not in loop or too many break levels given\n"); + break; + case CONTINUED: + fprintf(cp_err, + "Error: continue not in loop or too many continue levels given\n"); + break; + default: + x = findlabel(i, control[stackp]); + if (!x) + fprintf(cp_err, "Error: label %s not found\n", i); + } + if (x) + x = x->co_next; + } while (x); + } + if (string) + return (1); /* The return value is irrelevant. */ + } +} + +/* This blows away the control structures... */ +void +cp_resetcontrol(void) +{ + if (cend[stackp] && cend[stackp]->co_parent) + fprintf(cp_err, "Warning: EOF before block terminated\n"); + /* We probably should free the control structures... */ + control[0] = cend[0] = NULL; + stackp = 0; + cp_kwswitch(CT_LABEL, (char *) NULL); + return; +} + + +/* Push or pop a new control structure set... */ +void +cp_popcontrol(void) +{ + if (cp_debug) + fprintf(cp_err, "pop: stackp: %d -> %d\n", stackp, stackp - 1); + if (stackp < 1) + fprintf(cp_err, "cp_popcontrol: Internal Error: stack empty\n"); + else + stackp--; + return; +} + + +void +cp_pushcontrol(void) +{ + if (cp_debug) + fprintf(cp_err, "push: stackp: %d -> %d\n", stackp, stackp + 1); + if (stackp > CONTROLSTACKSIZE - 2) { + fprintf(cp_err, "Error: stack overflow -- max depth = %d\n", + CONTROLSTACKSIZE); + stackp = 0; + } else { + stackp++; + control[stackp] = cend[stackp] = NULL; + } + return; +} + + +/* And this returns to the top level (for use in the interrupt handlers). */ +void +cp_toplevel(void) +{ + stackp = 0; + if (cend[stackp]) + while (cend[stackp]->co_parent) + cend[stackp] = cend[stackp]->co_parent; + return; +} + + diff --git a/src/frontend/control.h b/src/frontend/control.h new file mode 100644 index 000000000..7b5132193 --- /dev/null +++ b/src/frontend/control.h @@ -0,0 +1,45 @@ +#ifndef _CONTROL_H +#define _CONTROL_H + + +/* Stuff to do control structures. We keep a history (seperate from + * the cshpar history, for now at least) of commands and their event + * numbers, with a block considered as a statement. In a goto, the + * first word in co_text is where to go, likewise for label. For + * conditional controls, we have to call ft_getpnames and ft_evaluate + * each time, since the dvec pointers will change... Also we should do + * variable and backquote substitution each time... */ +struct control { + int co_type; /* One of CO_* ... */ + wordlist *co_cond; /* if, while, dowhile */ + char *co_foreachvar; /* foreach */ + int co_numtimes; /* repeat, break & continue levels */ + wordlist *co_text; /* Ordinary text and foreach values. */ + struct control *co_parent; /* If this is inside a block. */ + struct control *co_children; /* The contents of this block. */ + struct control *co_elseblock; /* For if-then-else. */ + struct control *co_next; + struct control *co_prev; +} ; + +enum co_command { + CO_UNFILLED, + CO_STATEMENT, + CO_WHILE, + CO_DOWHILE, + CO_IF, + CO_FOREACH, + CO_BREAK, + CO_CONTINUE, + CO_LABEL, + CO_GOTO, + CO_REPEAT +}; + +#define CONTROLSTACKSIZE 256 /* Better be enough. */ + +extern struct control *control[CONTROLSTACKSIZE]; +extern struct control *cend[CONTROLSTACKSIZE]; +extern int stackp; + +#endif diff --git a/src/frontend/ftehelp.h b/src/frontend/ftehelp.h new file mode 100644 index 000000000..de368fe80 --- /dev/null +++ b/src/frontend/ftehelp.h @@ -0,0 +1,22 @@ +/********** +Copyright 1990 Regents of the University of California. All rights reserved. +Author: 1987 Jeffrey M. Hsu +**********/ + +/* + + Defines for help. +*/ + +#define E_HASPLOTS 1 +#define E_NOPLOTS 2 +#define E_HASGRAPHS 4 +#define E_MENUMODE 8 + +#define E_BEGINNING 4096 +#define E_INTERMED 8192 +#define E_ADVANCED 16384 +#define E_ALWAYS 32768 + +/* default is intermediate level */ +#define E_DEFHMASK 8192 diff --git a/src/frontend/hcomp.c b/src/frontend/hcomp.c new file mode 100644 index 000000000..657296618 --- /dev/null +++ b/src/frontend/hcomp.c @@ -0,0 +1,9 @@ +#include + +int +hcomp(const void *a, const void *b) +{ + struct comm **c1 = (struct comm **) a; + struct comm **c2 = (struct comm **) b; + return (strcmp((*c1)->co_comname, (*c2)->co_comname)); +} diff --git a/src/frontend/hcomp.h b/src/frontend/hcomp.h new file mode 100644 index 000000000..eed3558b8 --- /dev/null +++ b/src/frontend/hcomp.h @@ -0,0 +1,7 @@ +#ifndef _HCOMP_H +#define _HCOMP_H + + +int hcomp(const void *a, const void *b); + +#endif diff --git a/src/frontend/init.c b/src/frontend/init.c new file mode 100644 index 000000000..c6092a269 --- /dev/null +++ b/src/frontend/init.c @@ -0,0 +1,36 @@ +/********** +Copyright 1990 Regents of the University of California. All rights reserved. +Author: 1985 Wayne A. Christopher, U. C. Berkeley CAD Group +**********/ + +/* Initialize stuff. */ + +#include +#include + +#include "init.h" +#include "variable.h" + + +char cp_chars[128]; + +static char *singlec = "<>;&"; + +void +cp_init(void) +{ + char *s, *getenv(const char *); + + bzero(cp_chars, 128); + for (s = singlec; *s; s++) + cp_chars[(int) *s] = (CPC_BRR | CPC_BRL); + cp_vset("history", VT_NUM, (char *) &cp_maxhistlength); + + cp_curin = stdin; + cp_curout = stdout; + cp_curerr = stderr; + + cp_ioreset(); + + return; +} diff --git a/src/frontend/init.h b/src/frontend/init.h new file mode 100644 index 000000000..2544db117 --- /dev/null +++ b/src/frontend/init.h @@ -0,0 +1,6 @@ +#ifndef _INIT_H +#define _INIT_H + + + +#endif diff --git a/src/frontend/terminal.c b/src/frontend/terminal.c new file mode 100644 index 000000000..0000cb8fe --- /dev/null +++ b/src/frontend/terminal.c @@ -0,0 +1,326 @@ +/********** +Copyright 1990 Regents of the University of California. All rights reserved. +Author: 1986 Wayne A. Christopher, U. C. Berkeley CAD Group +**********/ + +/* + * Routines to handle "more"d output. There are some serious system + * dependencies in here, and it isn't clear that versions of this stuff + * can be written for every possible machine... + */ +#include + +#ifdef HAVE_SGTTY_H +#include +#endif + +#if 0 +/* Bad interaction with bool type in bool.h because curses also + defines this symbol. */ +#ifdef HAVE_TERMCAP +#include +#include +#endif +#endif + +#include +#include + +#include "variable.h" +#include "terminal.h" + +static char *motion_chars; +static char *clear_chars; +static char *home_chars; +static char *cleol_chars; + + +#define DEF_SCRHEIGHT 24 +#define DEF_SCRWIDTH 80 + +bool out_moremode = TRUE; +bool out_isatty = TRUE; + +static int xsize, ysize; +static int xpos, ypos; +static bool noprint, nopause; + + +/* out_printf doesn't handle double arguments correctly, so we + sprintf into this buf and call out_send w/ it */ +char out_pbuf[BSIZE_SP]; + +/* Start output... */ + +void +out_init(void) +{ +#ifdef TIOCGWINSZ + struct winsize ws; +#endif + bool moremode; + + noprint = nopause = FALSE; + + if (cp_getvar("nomoremode", VT_BOOL, (char *) &moremode)) + out_moremode = FALSE; + else + out_moremode = TRUE; + if (!out_moremode || !cp_interactive) + out_isatty = FALSE; + + if (!out_isatty) + return; + + xsize = ysize = 0; + + /* Figure out the screen size. We try, in order, TIOCGSIZE, + * tgetent(), and cp_getvar(height). Default is 24 x 80. + */ + +#ifdef TIOCGWINSZ + if (!xsize || !ysize) { + (void) ioctl(fileno(stdout), TIOCGWINSZ, (char *) &ws); + xsize = ws.ws_col; + ysize = ws.ws_row; + } +#endif + + if (!xsize) + (void) cp_getvar("width", VT_NUM, (char *) &xsize); + if (!ysize) + (void) cp_getvar("height", VT_NUM, (char *) &ysize); + + if (!xsize) + xsize = DEF_SCRWIDTH; + if (!ysize) + ysize = DEF_SCRHEIGHT; + ysize -= 2; /* Fudge room... */ + xpos = ypos = 0; + + return; +} + +/* Putc may not be buffered (sp?), so we do it ourselves. */ + +static char staticbuf[BUFSIZ]; +struct { + int count; + char *ptr; +} ourbuf = { BUFSIZ, staticbuf }; + +/* send buffer out */ +void +outbufputc(void) +{ + + if (ourbuf.count != BUFSIZ) { + fputs(staticbuf, cp_out); + memset(staticbuf, 0, BUFSIZ-ourbuf.count); + ourbuf.count = BUFSIZ; + ourbuf.ptr = staticbuf; + } + +} + +static void +bufputc(char c) +{ + if (--ourbuf.count >= 0) { + *ourbuf.ptr++ = c; + } else { + /* Flush and reset the buffer */ + outbufputc(); + /* and store the character. */ + ourbuf.count--; + *ourbuf.ptr++ = c; + } +} + + +/* prompt for a return */ +void +promptreturn(void) +{ + char buf[16]; +moe: + fprintf(cp_out, + "\n\t-- hit return for more, ? for help -- "); + if (!fgets(buf, 16, cp_in)) { + clearerr(cp_in); + *buf = 'q'; + } + switch (*buf) { + case '\n': + break; + case 'q': + noprint = TRUE; + break; + case 'c': + nopause = TRUE; + break; + case ' ': + break; + case '?': + fprintf(cp_out, +"\nPossible responses:\n\ +\t : Print another screenful\n\ +\tq : Discard the rest of the output\n\ +\tc : Continuously print the rest of the output\n\ +\t? : Print this help message\n"); + goto moe; + default: + fprintf(cp_out, "Character %d is no good\n", *buf); + goto moe; + } + +} + +/* Print a string to the output. If this would cause the screen to scroll, + * print "more". + */ + +void +out_send(char *string) +{ + + if (noprint) + return; + if (!out_isatty || nopause) { + fputs(string, cp_out); + return; + } + while (*string) { + switch (*string) { + case '\n': + xpos = 0; + ypos++; + break; + case '\f': + ypos = ysize; + xpos = 0; + break; + case '\t': + xpos = xpos / 8 + 1; + xpos *= 8; + break; + default: + xpos++; + break; + } + while (xpos >= xsize) { + xpos -= xsize; + ypos++; + } + if (ypos >= ysize) { + outbufputc(); /* out goes buffer */ + promptreturn(); + (void) fflush(cp_out); + ypos = xpos = 0; + } + bufputc(*string); /* we need to buffer these */ + string++; + } + (void) outbufputc(); + return; +} + +/* Printf some stuff using more mode. */ + +#define MAXLEN 4096 + + +void +out_printf(char *fmt, char *s1, char *s2, char *s3, char *s4, char *s5, char *s6, char *s7, char *s8, char *s9, char *s10) +{ + char buf[MAXLEN]; + + sprintf(buf, fmt, s1, s2, s3, s4, s5, s6, s7, s8, s9, s10); + + out_send(buf); + return; +} + +static int +outfn(int c) +{ + putc(c, stdout); + return c; +} + + +void +tcap_init(void) +{ + char *s; +#ifdef HAVE_TERMCAP + char tbuf[1025]; + static char buf2[100]; + char *charbuf; + + charbuf = buf2; + + if ((s = getenv("TERM"))) { + if (tgetent(tbuf, s) != -1) { + xsize = tgetnum("co"); + ysize = tgetnum("li"); + if ((xsize <= 0) || (ysize <= 0)) + xsize = ysize = 0; + clear_chars = (char *) tgetstr("cl", &charbuf); + motion_chars = (char *) tgetstr("cm", &charbuf); + home_chars = (char *) tgetstr("ho", &charbuf); + cleol_chars = (char *) tgetstr("ce", &charbuf); + } + } +#endif + + if (!xsize) { + if ((s = getenv("COLS"))) + xsize = atoi(s); + if (xsize <= 0) + xsize = 0; + } + + if (!ysize) { + if ((s = getenv("LINES"))) + ysize = atoi(s); + if (ysize <= 0) + ysize = 0; + } +} + + +void +term_clear(void) +{ +#ifdef HAVE_TERMCAP + if (*clear_chars) + tputs(clear_chars, 1, outfn); + else + fputs("\n", stdout); +#endif +} + + +void +term_home(void) +{ +#ifdef HAVE_TERMCAP + if (*home_chars) + tputs(home_chars, 1, outfn); + else if (*motion_chars) + tputs(tgoto(motion_chars, 1, 1), 1, outfn); + else + fputs("\n", stdout); +#endif +} + + +void +term_cleol(void) +{ +#ifdef HAVE_TERMCAP + if (*cleol_chars) + tputs(cleol_chars, 1, outfn); +#endif +} diff --git a/src/frontend/terminal.h b/src/frontend/terminal.h new file mode 100644 index 000000000..a3890750c --- /dev/null +++ b/src/frontend/terminal.h @@ -0,0 +1,16 @@ +#ifndef _TERMINAL_H +#define _TERMINAL_H + +void out_init(void); +void outbufputc(void); +void promptreturn(void); +void out_send(char *string); +void out_printf(char *fmt, char *s1, char *s2, char *s3, + char *s4, char *s5, char *s6, + char *s7, char *s8, char *s9, char *s10); +void term_clear(void); +void term_home(void); +void term_cleol(void); +void tcap_init(void); + +#endif diff --git a/src/frontend/variable.c b/src/frontend/variable.c new file mode 100644 index 000000000..2dd995043 --- /dev/null +++ b/src/frontend/variable.c @@ -0,0 +1,797 @@ +/********** +Copyright 1990 Regents of the University of California. All rights reserved. +Author: 1985 Wayne A. Christopher, U. C. Berkeley CAD Group +**********/ +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "circuits.h" +#include "variable.h" +#include "streams.h" +#include "com_history.h" + + +bool cp_noglob = TRUE; +bool cp_nonomatch = FALSE; +bool cp_noclobber = FALSE; +bool cp_ignoreeof = FALSE; + +struct variable *variables = NULL; + +wordlist * +cp_varwl(struct variable *var) +{ + wordlist *wl = NULL, *w, *wx = NULL; + char buf[BSIZE_SP]; + struct variable *vt; + + switch(var->va_type) { + case VT_BOOL: + /* Can't ever be FALSE. */ + sprintf(buf, "%s", var->va_bool ? "TRUE" : "FALSE"); + break; + case VT_NUM: + sprintf(buf, "%d", var->va_num); + break; + case VT_REAL: + /* This is a case where printnum isn't too good... */ + sprintf(buf, "%G", var->va_real); + break; + case VT_STRING: + strcpy(buf, cp_unquote(var->va_string)); + break; + case VT_LIST: /* The tricky case. */ + for (vt = var->va_vlist; vt; vt = vt->va_next) { + w = cp_varwl(vt); + if (wl == NULL) + wl = wx = w; + else { + wx->wl_next = w; + w->wl_prev = wx; + wx = w; + } + } + return (wl); + default: + fprintf(cp_err, + "cp_varwl: Internal Error: bad variable type %d\n", + var->va_type); + return (NULL); + } + wl = alloc(struct wordlist); + wl->wl_next = wl->wl_prev = NULL; + wl->wl_word = copy(buf); + return (wl); +} + + +/* Set a variable. */ +void +cp_vset(char *varname, char type, char *value) +{ + struct variable *v, *u, *w; + int i; + bool alreadythere = FALSE; + + varname = cp_unquote(varname); + w = NULL; + for (v = variables; v; v = v->va_next) { + if (eq(varname, v->va_name)) { + alreadythere = TRUE; + break; + } + w = v; + } + if (!v) { + v = alloc(struct variable); + v->va_name = copy(varname); + v->va_next = NULL; + } + switch (type) { + case VT_BOOL: + if (* ((bool *) value) == FALSE) { + cp_remvar(varname); + return; + } else + v->va_bool = TRUE; + break; + + case VT_NUM: + v->va_num = * (int *) value; + break; + + case VT_REAL: + v->va_real = * (double *) value; + break; + + case VT_STRING: + v->va_string = copy(value); + break; + + case VT_LIST: + v->va_vlist = (struct variable *) value; + break; + + default: + fprintf(cp_err, + "cp_vset: Internal Error: bad variable type %d.\n", + type); + return; + } + v->va_type = type; + + /* Now, see if there is anything interesting going on. We + * recognise these special variables: noglob, nonomatch, history, + * echo, noclobber, prompt, and verbose. cp_remvar looks for these + * variables too. The host program will get any others. */ + if (eq(varname, "noglob")) + cp_noglob = TRUE; + else if (eq(varname, "nonomatch")) + cp_nonomatch = TRUE; + else if (eq(varname, "history") && (type == VT_NUM)) + cp_maxhistlength = v->va_num; + else if (eq(varname, "history") && (type == VT_REAL)) + cp_maxhistlength = v->va_real; + else if (eq(varname, "noclobber")) + cp_noclobber = TRUE; + else if (eq(varname, "prompt") && (type == VT_STRING)) + cp_promptstring = copy(v->va_string); + else if (eq(varname, "ignoreeof")) + cp_ignoreeof = TRUE; + else if (eq(varname, "cpdebug")) { + cp_debug = TRUE; +#ifndef CPDEBUG + fprintf(cp_err, + "Warning: program not compiled with cshpar debug messages\n"); +#endif + } + + switch (i = cp_usrset(v, TRUE)) { + + case US_OK: + /* Normal case. */ + if (!alreadythere) { + v->va_next = variables; + variables = v; + } + break; + + case US_DONTRECORD: + /* Do nothing... */ + if (alreadythere) { + fprintf(cp_err, "cp_vset: Internal Error: " + "%s already there, but 'dont record'\n", v->va_name); + } + break; + + case US_READONLY: + fprintf(cp_err, "Error: %s is a read-only variable.\n", v->va_name); + if (alreadythere) + fprintf(cp_err, "cp_vset: Internal Error: " + "it was already there too!!\n"); + break; + + case US_SIMVAR: + if (alreadythere) { + /* somehow it got into the front-end list of variables */ + if (w) { + w->va_next = v->va_next; + } else { + variables = v->va_next; + } + } + alreadythere = FALSE; + if (ft_curckt) { + for (u = ft_curckt->ci_vars; u; u = u->va_next) + if (eq(varname, u->va_name)) { + alreadythere = TRUE; + break; + } + if (!alreadythere) { + v->va_next = ft_curckt->ci_vars; + ft_curckt->ci_vars = v; + } else { + w = u->va_next; + bcopy(v, u, sizeof(*u)); + u->va_next = w; + } + } + break; + + case US_NOSIMVAR: + /* What do you do? */ + tfree(v); + break; + + default: + fprintf(cp_err, "cp_vset: Internal Error: bad US val %d\n", i); + break; + } + + return; +} + + +struct variable * +cp_setparse(wordlist *wl) +{ + char *name, *val, *s, *ss; + double *td; + struct variable *listv = NULL, *vv, *lv = NULL; + struct variable *vars = NULL; + int balance; + + while (wl) { + name = cp_unquote(wl->wl_word); + wl = wl->wl_next; + if (((wl == NULL) || (*wl->wl_word != '=')) && + strchr(name, '=') == NULL) { + vv = alloc(struct variable); + vv->va_name = copy(name); + vv->va_type = VT_BOOL; + vv->va_bool = TRUE; + vv->va_next = vars; + vars = vv; + continue; + } + if (wl && eq(wl->wl_word, "=")) { + wl = wl->wl_next; + if (wl == NULL) { + fprintf(cp_err, "Error: bad set form.\n"); + return (NULL); + } + val = wl->wl_word; + wl = wl->wl_next; + } else if (wl && (*wl->wl_word == '=')) { + val = wl->wl_word + 1; + wl = wl->wl_next; + } else if ((s =strchr(name, '='))) { + val = s + 1; + *s = '\0'; + if (*val == '\0') { + if (!wl) { + fprintf(cp_err, + "Error: %s equals what?.\n", + name); + return (NULL); + } else { + val = wl->wl_word; + wl = wl->wl_next; + } + } + } else { + fprintf(cp_err, "Error: bad set form.\n"); + return (NULL); + } + val = cp_unquote(val); + if (eq(val, "(")) { /* ) */ + /* The beginning of a list... We have to walk down the + * list until we find a close paren... If there are nested + * ()'s, treat them as tokens... */ + balance = 1; + while (wl && wl->wl_word) { + if (eq(wl->wl_word, "(")) { + balance++; + } else if (eq(wl->wl_word, ")")) { + if (!--balance) + break; + } + vv = alloc(struct variable); + vv->va_next = NULL; + ss = cp_unquote(wl->wl_word); + td = ft_numparse(&ss, FALSE); + if (td) { + vv->va_type = VT_REAL; + vv->va_real = *td; + } else { + vv->va_type = VT_STRING; + vv->va_string = copy(ss); + } + if (listv) { + lv->va_next = vv; + lv = vv; + } else + listv = lv = vv; + wl = wl->wl_next; + } + if (balance && !wl) { + fprintf(cp_err, "Error: bad set form.\n"); + return (NULL); + } + + vv = alloc(struct variable); + vv->va_name = copy(name); + vv->va_type = VT_LIST; + vv->va_vlist = listv; + vv->va_next = vars; + vars = vv; + + wl = wl->wl_next; + continue; + } + + ss = cp_unquote(val); + td = ft_numparse(&ss, FALSE); + vv = alloc(struct variable); + vv->va_name = copy(name); + vv->va_next = vars; + vars = vv; + if (td) { + /*** We should try to get VT_NUM's... */ + vv->va_type = VT_REAL; + vv->va_real = *td; + } else { + vv->va_type = VT_STRING; + vv->va_string = copy(val); + } + } + return (vars); +} + + +void +cp_remvar(char *varname) +{ + struct variable *v, *u, *lv = NULL; + bool found = TRUE; + int i; + + for (v = variables; v; v = v->va_next) { + if (eq(v->va_name, varname)) + break; + lv = v; + } + if (!v) { + /* Gotta make up a var struct for cp_usrset()... */ + v = alloc(struct variable); + ZERO(v, struct variable); + v->va_name = varname; + v->va_type = VT_NUM; + v->va_bool = 0; + found = FALSE; + } + + /* Note that 'unset history' doesn't do anything here... Causes + * trouble... */ + if (eq(varname, "noglob")) + cp_noglob = FALSE; + else if (eq(varname, "nonomatch")) + cp_nonomatch = FALSE; + else if (eq(varname, "noclobber")) + cp_noclobber = FALSE; + else if (eq(varname, "prompt")) + cp_promptstring = ""; + else if (eq(varname, "cpdebug")) + cp_debug = FALSE; + else if (eq(varname, "ignoreeof")) + cp_ignoreeof = FALSE; + + switch (i = cp_usrset(v, FALSE)) { + + case US_OK: + /* Normal case. */ + if (found) { + if (lv) + lv->va_next = v->va_next; + else + variables = v->va_next; + } + break; + + case US_DONTRECORD: + /* Do nothing... */ + if (found) + fprintf(cp_err, "cp_remvar: Internal Error: var %d\n", *varname); + break; + + case US_READONLY: + /* Badness... */ + fprintf(cp_err, "Error: %s is read-only.\n", v->va_name); + if (found) + fprintf(cp_err, "cp_remvar: Internal Error: var %d\n", *varname); + break; + + case US_SIMVAR: + lv = NULL; + if (ft_curckt) { + for (u = ft_curckt->ci_vars; u; u = u->va_next) { + if (eq(varname, u->va_name)) { + break; + } + lv = u; + } + if (u) { + if (lv) + lv->va_next = u->va_next; + else + ft_curckt->ci_vars = u->va_next; + tfree(u); + } + } + break; + + default: + fprintf(cp_err, "cp_remvar: Internal Error: US val %d\n", i); + break; + } + + tfree(v); + return; +} + + +/* Determine the value of a variable. Fail if the variable is unset, + * and if the type doesn't match, try and make it work... */ +bool +cp_getvar(char *name, int type, char *retval) +{ + struct variable *v; + + for (v = variables; v; v = v->va_next) + if (eq(name, v->va_name)) + break; + if (v == NULL) { + if (type == VT_BOOL) + * (bool *) retval = FALSE; + return (FALSE); + } + if (v->va_type == type) { + switch (type) { + case VT_BOOL: + * (bool *) retval = TRUE; + break; + case VT_NUM: { + int *i; + i = (int *) retval; + *i = v->va_num; + break; + } + case VT_REAL: { + double *d; + d = (double *) retval; + *d = v->va_real; + break; + } + case VT_STRING: { /* Gotta be careful to have room. */ + char *s; + s = cp_unquote(v->va_string); + cp_wstrip(s); + (void) strcpy(retval, s); + break; + } + case VT_LIST: { /* Funny case... */ + struct variable **tv; + tv = (struct variable **) retval; + *tv = v->va_vlist; + break; + } + default: + fprintf(cp_err, + "cp_getvar: Internal Error: bad var type %d.\n", + type); + break; + } + return (TRUE); + } else { + /* Try to coerce it.. */ + if ((type == VT_NUM) && (v->va_type == VT_REAL)) { + int *i; + i = (int *) retval; + *i = (int) v->va_real; + return (TRUE); + } else if ((type == VT_REAL) && (v->va_type == VT_NUM)) { + double *d; + d = (double *) retval; + *d = (double) v->va_num; + return (TRUE); + } else if ((type == VT_STRING) && (v->va_type == VT_NUM)) { + (void) sprintf(retval, "%d", v->va_num); + return (TRUE); + } else if ((type == VT_STRING) && (v->va_type == VT_REAL)) { + (void) sprintf(retval, "%f", v->va_real); + return (TRUE); + } + return (FALSE); + } +} + + +/* A variable substitution is indicated by a $, and the variable name + * is the following string of non-special characters. All variable + * values are inserted as a single word, except for lists, which are a + * list of words. A routine cp_usrset must be supplied by the host + * program to deal with variables that aren't used by cshpar -- it + * should be cp_usrset(var, isset), where var is a variable *, and + * isset is TRUE if the variable is being set, FALSE if unset. Also + * required is a routine cp_enqvar(name) which returns a struct + * variable *, which allows the host program to provide values for + * non-cshpar variables. */ + +char cp_dol = '$'; + +/* Non-alphanumeric characters that may appear in variable names. < is very + * special... + */ + +#define VALIDCHARS "$-_<#?@.()[]&" + +wordlist * +cp_variablesubst(wordlist *wlist) +{ + wordlist *wl, *nwl; + char *s, *t, buf[BSIZE_SP], wbuf[BSIZE_SP], tbuf[BSIZE_SP]; + /* MW. tbuf holds curret word after wl_splice() calls free() on it */ + int i; + + for (wl = wlist; wl; wl = wl->wl_next) { + t = wl->wl_word; + i = 0; + while ((s =strchr(t, cp_dol))) { + while (t < s) + wbuf[i++] = *t++; + wbuf[i] = '\0'; + (void) strcpy(buf, ++s); + s = buf; + t++; + while (*s && (isalphanum(*s) || + strchr(VALIDCHARS, *s))) { + /* Get s and t past the end of the var name. */ + t++; + s++; + } + *s = '\0'; + nwl = vareval(buf); + if (i) { + (void) strcpy(buf, wbuf); + if (nwl) { + (void) strcat(buf, nwl->wl_word); + tfree(nwl->wl_word); + } else { + nwl = alloc(struct wordlist); + nwl->wl_next = nwl->wl_prev = NULL; + } + nwl->wl_word = copy(buf); + } + + (void) strcpy(tbuf, t); /* MW. Save t*/ + if (!(wl = wl_splice(wl, nwl))) + return (NULL); + /* This is bad... */ + for (wlist = wl; wlist->wl_prev; wlist = wlist->wl_prev) + ; + (void) strcpy(buf, wl->wl_word); + i = strlen(buf); + (void) strcat(buf, tbuf); /* MW. tbuf is used here only */ + + tfree(wl->wl_word); + wl->wl_word = copy(buf); + t = &wl->wl_word[i]; + s = wl->wl_word; + for (i = 0; s < t; s++) + wbuf[i++] = *s; + } + } + return (wlist); +} + + +/* Evaluate a variable. */ +wordlist * +vareval(char *string) +{ + struct variable *v; + wordlist *wl; + char buf[BSIZE_SP], *s; + char *oldstring = copy(string); + char *range = NULL; + int i, up, low; + + cp_wstrip(string); + if ((s =strchr(string, '['))) { + *s = '\0'; + range = s + 1; + } + + switch (*string) { + + case '$': + wl = alloc(struct wordlist); + wl->wl_next = wl->wl_prev = NULL; + + + (void) sprintf(buf, "%d", getpid()); + + wl->wl_word = copy(buf); + return (wl); + + case '<': + (void) fflush(cp_out); + if (!fgets(buf, BSIZE_SP, cp_in)) { + clearerr(cp_in); + (void) strcpy(buf, "EOF"); + } + for (s = buf; *s && (*s != '\n'); s++) + ; + *s = '\0'; + wl = cp_lexer(buf); + /* This is a hack. */ + if (!wl->wl_word) + wl->wl_word = copy(""); + return (wl); + + case '?': + wl = alloc(struct wordlist); + wl->wl_next = wl->wl_prev = NULL; + string++; + for (v = variables; v; v = v->va_next) + if (eq(v->va_name, string)) + break; + if (!v) + v = cp_enqvar(string); + wl->wl_word = copy(v ? "1" : "0"); + return (wl); + + case '#': + wl = alloc(struct wordlist); + wl->wl_next = wl->wl_prev = NULL; + string++; + for (v = variables; v; v = v->va_next) + if (eq(v->va_name, string)) + break; + if (!v) + v = cp_enqvar(string); + if (!v) { + fprintf(cp_err, "Error: %s: no such variable.\n", + string); + return (NULL); + } + if (v->va_type == VT_LIST) + for (v = v->va_vlist, i = 0; v; v = v->va_next) + i++; + else + i = (v->va_type != VT_BOOL); + (void) sprintf(buf, "%d", i); + wl->wl_word = copy(buf); + return (wl); + + case '\0': + wl = alloc(struct wordlist); + wl->wl_next = wl->wl_prev = NULL; + wl->wl_word = copy("$"); + return (wl); + } + + /* The notation var[stuff] has two meanings... If this is a real + * variable, then the [] denotes range, but if this is a strange + * (e.g, device parameter) variable, it could be anything... + */ + for (v = variables; v; v = v->va_next) + if (eq(v->va_name, string)) + break; + if (!v && isdigit(*string)) { + for (v = variables; v; v = v->va_next) + if (eq(v->va_name, "argv")) + break; + range = string; + } + if (!v) { + range = NULL; + string = oldstring; + v = cp_enqvar(string); + } + if (!v && (s = getenv(string))) { + wl = alloc(struct wordlist); + wl->wl_next = wl->wl_prev = NULL; + wl->wl_word = copy(s); + return (wl); + } + if (!v) { + fprintf(cp_err, "Error: %s: no such variable.\n", string); + return (NULL); + } + wl = cp_varwl(v); + + /* Now parse and deal with 'range' ... */ + if (range) { + for (low = 0; isdigit(*range); range++) + low = low * 10 + *range - '0'; + if ((*range == '-') && isdigit(range[1])) + for (up = 0, range++; isdigit(*range); range++) + up = up * 10 + *range - '0'; + else if (*range == '-') + up = wl_length(wl); + else + up = low; + up--, low--; + wl = wl_range(wl, low, up); + } + + return (wl); +} + + +static int +vcmp(const void *a, const void *b) +{ + int i; + struct xxx *v1 = (struct xxx *) a; + struct xxx *v2 = (struct xxx *) b; + if ((i = strcmp(v1->x_v->va_name, v2->x_v->va_name))) + return (i); + else + return (v1->x_char - v2->x_char); +} + + + +/* Print the values of currently defined variables. */ +void +cp_vprint(void) +{ + struct variable *v; + struct variable *uv1, *uv2; + wordlist *wl; + int i, j; + char *s; + struct xxx *vars; + + cp_usrvars(&uv1, &uv2); + + for (v = uv1, i = 0; v; v = v->va_next) + i++; + for (v = uv2; v; v = v->va_next) + i++; + for (v = variables; v; v = v->va_next) + i++; + + vars = (struct xxx *) tmalloc(sizeof (struct xxx) * i); + + out_init(); + for (v = variables, i = 0; v; v = v->va_next, i++) { + vars[i].x_v = v; + vars[i].x_char = ' '; + } + for (v = uv1; v; v = v->va_next, i++) { + vars[i].x_v = v; + vars[i].x_char = '*'; + } + for (v = uv2; v; v = v->va_next, i++) { + vars[i].x_v = v; + vars[i].x_char = '+'; + } + + qsort((char *) vars, i, sizeof (struct xxx), vcmp); + + for (j = 0; j < i; j++) { + if (j && eq(vars[j].x_v->va_name, vars[j - 1].x_v->va_name)) + continue; + v = vars[j].x_v; + if (v->va_type == VT_BOOL) { +/* out_printf("%c %s\n", vars[j].x_char, v->va_name); */ + sprintf(out_pbuf, "%c %s\n", vars[j].x_char, v->va_name); + out_send(out_pbuf); + } else { + out_printf("%c %s\t", vars[j].x_char, v->va_name); + wl = vareval(v->va_name); + s = wl_flatten(wl); + if (v->va_type == VT_LIST) { + out_printf("( %s )\n", s); + } else + out_printf("%s\n", s); + } + } + + tfree(vars); + return; +} diff --git a/src/frontend/variable.h b/src/frontend/variable.h new file mode 100644 index 000000000..3c14dc1b8 --- /dev/null +++ b/src/frontend/variable.h @@ -0,0 +1,53 @@ +#ifndef _VARIABLE_H +#define _VARIABLE_H + +/* Variables that are accessible to the parser via $varname + * expansions. If the type is VT_LIST the value is a pointer to a + * list of the elements. */ +struct variable { + char va_type; + char *va_name; + union { + bool vV_bool; + int vV_num; + double vV_real; + char *vV_string; + struct variable *vV_list; + } va_V; + struct variable *va_next; /* Link. */ +} ; + +#define va_bool va_V.vV_bool +#define va_num va_V.vV_num +#define va_real va_V.vV_real +#define va_string va_V.vV_string +#define va_vlist va_V.vV_list + +enum vt_types { + VT_BOOL, + VT_NUM, + VT_REAL, + VT_STRING, + VT_LIST +}; + +struct xxx { + struct variable *x_v; + char x_char; +} ; + + +extern struct variable *variables; + +// extern struct variable *variables; +wordlist * cp_varwl(struct variable *var); +void cp_vset(char *varname, char type, char *value); +struct variable * cp_setparse(wordlist *wl); +void cp_remvar(char *varname); +bool cp_getvar(char *name, int type, char *retval); +wordlist * cp_variablesubst(wordlist *wlist); +wordlist * vareval(char *string); +void cp_vprint(void); + + +#endif