#pragma prototyped
#include "defs.h"
#include "edit.h"
#if ! SHOPT_HISTEXPAND
NoN(hexpand)
#else
static char *modifiers = "htrepqxs&";
static int mod_flags[] = { 0, 0, 0, 0, HIST_PRINT, HIST_QUOTE, HIST_QUOTE|HIST_QUOTE_BR, 0, 0 };
#define DONE() {flag |= HIST_ERROR; cp = 0; stakseek(0); goto done;}
struct subst
{
char *str[2];
};
static char *parse_subst(const char *s, struct subst *sb)
{
char *cp,del;
int off,n = 0;
off = staktell();
if(sb->str[1])
free(sb->str[1]);
sb->str[1] = strdup("");
del = *s;
cp = (char*) s + 1;
while(n < 2)
{
if(*cp == del || *cp == '\n' || *cp == '\0')
{
if(staktell() != off)
{
stakputc('\0');
if(sb->str[n])
free(sb->str[n]);
sb->str[n] = strdup(stakptr(off));
stakseek(off);
}
n++;
if(*cp != del)
break;
}
else if(*cp == '\\')
{
if(*(cp+1) == del)
{
stakputc(del);
cp++;
}
else if(*(cp+1) == '&' && n == 1)
{
stakputc('&');
cp++;
}
else
stakputc('\\');
}
else if(*cp == '&' && n == 1 && sb->str[0])
stakputs(sb->str[0]);
else
stakputc(*cp);
cp++;
}
stakseek(off);
return cp;
}
int hist_expand(const char *ln, char **xp)
{
int off,
q,
p,
c,
flag=0;
Sfoff_t n,
i,
w[2];
char *sp,
*cp,
*str,
*evp,
*cc=0,
hc[3],
*qc="\'\"`";
Sfio_t *ref=0,
*tmp=0,
*tmp2=0;
Histloc_t hl;
static Namval_t *np = 0;
static struct subst sb = {0,0};
static Sfio_t *wm=0;
if(!wm)
wm = sfopen(NULL, NULL, "swr");
hc[0] = '!';
hc[1] = '^';
hc[2] = 0;
if((np = nv_open("histchars",sh.var_tree,0)) && (cp = nv_getval(np)))
{
if(cp[0])
{
hc[0] = cp[0];
if(cp[1])
{
hc[1] = cp[1];
if(cp[2])
hc[2] = cp[2];
}
}
}
if(off = staktell())
sp = stakfreeze(0);
cp = (char*)ln;
while(cp && *cp)
{
if((*cp != hc[0] && *cp != hc[1] && *cp != hc[2])
|| (*cp == hc[1] && cp != ln))
{
if(*cp == '\\')
stakputc(*cp++);
else if(*cp == '\'')
{
do
stakputc(*cp);
while(*++cp && *cp != '\'');
}
stakputc(*cp++);
continue;
}
if(hc[2] && *cp == hc[2])
{
stakputc(*cp++);
stakputs(cp);
DONE();
}
n = -1;
str = 0;
flag &= HIST_EVENT;
evp = cp;
ref = 0;
if(*cp == hc[1])
{
flag |= HIST_QUICKSUBST;
goto getline;
}
if(*cp == hc[0] && *(cp+1) == hc[0])
{
cp += 2;
goto getline;
}
switch(c = *++cp) {
case ' ':
case '\t':
case '\n':
case '\0':
case '=':
case '(':
stakputc(hc[0]);
continue;
case '#':
flag |= HIST_HASH;
cp++;
n = staktell();
stakputc('\0');
cc = strdup(stakptr(0));
stakseek(n);
ref = sfopen(ref, cc, "s");
n = 0;
break;
case '-':
if(!isdigit(*(cp+1)))
goto string_event;
cp++;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
n = 0;
while(isdigit(*cp))
n = n * 10 + (*cp++) - '0';
if(c == '-')
n = -n;
break;
case '$':
n = -1;
case ':':
break;
case '?':
cp++;
flag |= HIST_QUESTION;
string_event:
default:
str = cp;
while(*cp)
{
cp++;
if((!(flag&HIST_QUESTION) &&
(*cp == ':' || isspace(*cp)
|| *cp == '^' || *cp == '$'
|| *cp == '*' || *cp == '-'
|| *cp == '%')
)
|| ((flag&HIST_QUESTION) && (*cp == '?' || *cp == '\n')))
{
c = *cp;
*cp = '\0';
}
}
break;
}
getline:
flag |= HIST_EVENT;
if(str)
{
hl = hist_find(shgd->hist_ptr, str,
shgd->hist_ptr->histind,
flag&HIST_QUESTION, -1);
if((n = hl.hist_command) == -1)
n = 0;
}
if(n)
{
if(n < 0)
n = shgd->hist_ptr->histind + n;
if(n > 0 && hist_seek(shgd->hist_ptr, n) != -1)
ref = shgd->hist_ptr->histfp;
}
if(!ref)
{
c = *cp;
*cp = '\0';
errormsg(SH_DICT, ERROR_ERROR, "%s: event not found", evp);
*cp = c;
DONE();
}
if(str)
{
if(flag&HIST_QUESTION)
*cp++ = c;
else
*cp = c;
}
if(*(evp = cp) == ':')
cp++;
w[0] = 0;
w[1] = -1;
if(flag & HIST_QUICKSUBST)
goto getsel;
n = 0;
while(n < 2)
{
switch(c = *cp++) {
case '^':
if(n == 0)
{
w[0] = w[1] = 1;
goto skip;
}
else
goto skip2;
case '$':
w[n] = -1;
goto skip;
case '%':
if(n == 0)
{
if(!str)
{
w[0] = 0;
w[1] = -1;
ref = wm;
}
else
{
w[0] = -2;
w[1] = sftell(ref) + hl.hist_char;
}
sfseek(wm, 0, SEEK_SET);
goto skip;
}
default:
skip2:
cp--;
n = 2;
break;
case '*':
if(n == 0)
w[0] = 1;
w[1] = -1;
skip:
flag |= HIST_WORDDSGN;
n = 2;
break;
case '-':
w[1] = -2;
flag |= HIST_WORDDSGN;
n = 1;
break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
if((*evp == ':') || w[1] == -2)
{
w[n] = c - '0';
while(isdigit(c=*cp++))
w[n] = w[n] * 10 + c - '0';
flag |= HIST_WORDDSGN;
if(n == 0)
w[1] = w[0];
n++;
}
else
n = 2;
cp--;
break;
}
}
if(w[0] != -2 && w[1] > 0 && w[0] > w[1])
{
c = *cp;
*cp = '\0';
errormsg(SH_DICT, ERROR_ERROR, "%s: bad word specifier", evp);
*cp = c;
DONE();
}
if(!(flag & HIST_WORDDSGN) && (*evp == ':'))
cp = evp;
getsel:
tmp = sfopen(NULL, NULL, "swr");
n = i = q = 0;
while((c = sfgetc(ref)) > 0)
{
if(isspace(c))
{
flag |= (c == '\n' ? HIST_NEWLINE : 0);
continue;
}
if(n >= w[0] && ((w[0] != -2) ? (w[1] < 0 || n <= w[1]) : 1))
{
if(w[0] < 0)
sfseek(tmp, 0, SEEK_SET);
else
i = sftell(tmp);
if(i > 0)
sfputc(tmp, flag & HIST_NEWLINE ? '\n' : ' ');
flag &= ~HIST_NEWLINE;
p = 1;
}
else
p = 0;
do
{
cc = strchr(qc, c);
q ^= cc ? 1<<(int)(cc - qc) : 0;
if(p)
sfputc(tmp, c);
}
while((c = sfgetc(ref)) > 0 && (!isspace(c) || q));
if(w[0] == -2 && sftell(ref) > w[1])
break;
flag |= (c == '\n' ? HIST_NEWLINE : 0);
n++;
}
if(w[0] != -2 && w[1] >= 0 && w[1] >= n)
{
c = *cp;
*cp = '\0';
errormsg(SH_DICT, ERROR_ERROR, "%s: bad word specifier", evp);
*cp = c;
DONE();
}
else if(w[1] == -2)
sfseek(tmp, i, SEEK_SET);
if(sftell(tmp))
{
sfseek(tmp, -1, SEEK_CUR);
if(sfgetc(tmp) == '\n')
sfungetc(tmp, '\n');
}
sfputc(tmp, '\0');
if(str)
{
if(wm)
sfclose(wm);
wm = tmp;
}
if(cc && (flag&HIST_HASH))
{
sfclose(ref);
flag &= ~HIST_HASH;
free(cc);
cc = 0;
}
evp = cp;
while(*cp == ':' || (flag & HIST_QUICKSUBST))
{
if(flag & HIST_QUICKSUBST)
{
flag &= ~HIST_QUICKSUBST;
c = 's';
cp--;
}
else
c = *++cp;
sfseek(tmp, 0, SEEK_SET);
tmp2 = sfopen(tmp2, NULL, "swr");
if(c == 'g')
{
flag |= HIST_GLOBALSUBST;
c = *++cp;
}
if(cc = strchr(modifiers, c))
flag |= mod_flags[cc - modifiers];
else
{
errormsg(SH_DICT, ERROR_ERROR, "%c: unrecognized history modifier", c);
DONE();
}
if(c == 'h' || c == 'r')
{
n = -1;
while((c = sfgetc(tmp)) > 0)
{
if((c == '/' && *cp == 'h') || (c == '.' && *cp == 'r'))
n = sftell(tmp2);
sfputc(tmp2, c);
}
if(n > 0)
{
sfseek(tmp2, n, SEEK_SET);
sfputc(tmp2, '\0');
}
}
else if(c == 't' || c == 'e')
{
n = 0;
while((c = sfgetc(tmp)) > 0)
{
if((c == '/' && *cp == 't') || (c == '.' && *cp == 'e'))
n = sftell(tmp);
}
sfseek(tmp, n, SEEK_SET);
while((c = sfgetc(tmp)) > 0)
sfputc(tmp2, c);
}
else if(c == 's' || c == '&')
{
cp++;
if(c == 's')
{
if(!sb.str[0] && wm)
sb.str[0] = strdup(sfsetbuf(wm, (Void_t*)1, 0));
cp = parse_subst(cp, &sb);
}
if(!sb.str[0] || !sb.str[1])
{
c = *cp;
*cp = '\0';
errormsg(SH_DICT, ERROR_ERROR,
"%s%s: no previous substitution",
(flag & HIST_QUICKSUBST) ? ":s" : "",
evp);
*cp = c;
DONE();
}
str = sfsetbuf(tmp, (Void_t*)1, 0);
flag |= HIST_SUBSTITUTE;
while(flag & HIST_SUBSTITUTE)
{
if(cc = strstr(str, sb.str[0]))
{
c = *cc;
*cc = '\0';
sfputr(tmp2, str, -1);
sfputr(tmp2, sb.str[1], -1);
*cc = c;
str = cc + strlen(sb.str[0]);
}
else if(!sftell(tmp2))
{
c = *cp;
*cp = '\0';
errormsg(SH_DICT, ERROR_ERROR,
"%s%s: substitution failed",
(flag & HIST_QUICKSUBST) ? ":s" : "",
evp);
*cp = c;
DONE();
}
if(!cc || !(flag & HIST_GLOBALSUBST))
flag &= ~HIST_SUBSTITUTE;
}
sfputr(tmp2, str, -1);
if(*cp)
cp--;
}
if(sftell(tmp2))
{
if(wm != tmp)
sfclose(tmp);
tmp = tmp2;
tmp2 = 0;
}
cc = 0;
if(*cp)
cp++;
}
if(tmp)
{
sfseek(tmp, 0, SEEK_SET);
if(flag & HIST_QUOTE)
stakputc('\'');
while((c = sfgetc(tmp)) > 0)
{
if(isspace(c))
{
flag = flag & ~HIST_NEWLINE;
do
flag |= (c == '\n' ? HIST_NEWLINE : 0);
while((c = sfgetc(tmp)) > 0 && isspace(c));
sfungetc(tmp, c);
c = (flag & HIST_NEWLINE) ? '\n' : ' ';
if(flag & HIST_QUOTE_BR)
{
stakputc('\'');
stakputc(c);
stakputc('\'');
}
else
stakputc(c);
}
else if((c == '\'') && (flag & HIST_QUOTE))
{
stakputc('\'');
stakputc('\\');
stakputc(c);
stakputc('\'');
}
else
stakputc(c);
}
if(flag & HIST_QUOTE)
stakputc('\'');
}
}
stakputc('\0');
done:
if(cc && (flag&HIST_HASH))
{
sfclose(ref);
free(cc);
cc = 0;
}
if(staktell() && !(flag & HIST_ERROR))
*xp = strdup(stakfreeze(1));
if(off)
stakset(sp,off);
else
stakseek(0);
if(tmp && tmp != wm)
sfclose(tmp);
if(tmp2)
sfclose(tmp2);
return (flag & HIST_ERROR ? HIST_ERROR : flag & HIST_FLAG_RETURN_MASK);
}
#endif