ngspice/src/frontend/com_history.c

494 lines
13 KiB
C

/**********
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 <ngspice.h>
#include <cpdefs.h>
#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)
{
/* MW. This test is not needed if everything works right
if (cp_lastone && !cp_lastone->hi_wlist)
fprintf(cp_err, "Internal error: bad history list\n"); */
if (cp_lastone == NULL) {
/* MW. the begging - initialize histlength*/
histlength = 1;
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;
}