494 lines
13 KiB
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;
|
|
}
|
|
|