/***********************************************************************
* *
* This software is part of the ast package *
* Copyright (c) 1982-2010 AT&T Intellectual Property *
* and is licensed under the *
* Common Public License, Version 1.0 *
* by AT&T Intellectual Property *
* *
* A copy of the License is available at *
* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) *
* *
* Information and Software Systems Research *
* AT&T Research *
* Florham Park NJ *
* *
* David Korn <dgk@research.att.com> *
* *
***********************************************************************/
#pragma prototyped
/*
* bash style history expansion
*
* Author:
* Karsten Fleischer
* Omnium Software Engineering
* An der Luisenburg 7
* D-51379 Leverkusen
* Germany
*
* <K.Fleischer@omnium.de>
*/
#include "defs.h"
#include "edit.h"
#if ! SHOPT_HISTEXPAND
#else
static char *modifiers = "htrepqxs&";
struct subst
{
};
/*
* if "old" not specified, keep sb->str[0]
* if "new" not specified, set sb->str[1] to empty string
* read up to third delimeter char, \n or \0, whichever comes first.
* return adress is one past the last valid char in s:
* - the address containing \n or \0 or
* - one char beyond the third delimiter
*/
{
int off,n = 0;
/* build the strings on the stack, mainly for '&' substition in "new" */
/* init "new" with empty string */
/* get delimiter */
del = *s;
cp = (char*) s + 1;
while(n < 2)
{
{
/* delimiter or EOL */
{
/* dupe string on stack and rewind stack */
stakputc('\0');
}
n++;
/* if not delimiter, we've reached EOL. Get outta here. */
break;
}
else if(*cp == '\\')
{
{
cp++;
}
{ /* quote '&' only in "new" */
stakputc('&');
cp++;
}
else
stakputc('\\');
}
/* substitute '&' with "old" in "new" */
else
cp++;
}
/* rewind stack */
return cp;
}
/*
* history expansion main routine
*/
{
int off, /* stack offset */
q, /* quotation flags */
p, /* flag */
c, /* current char */
flag=0; /* HIST_* flags */
Sfoff_t n, /* history line number, counter, etc. */
i, /* counter */
w[2]; /* word range */
char *sp, /* stack pointer */
*cp, /* current char in ln */
*str, /* search string */
*cc=0, /* copy of current line up to cp; temp ptr */
*tmp=0, /* temporary line buffer */
*tmp2=0;/* temporary line buffer */
if(!wm)
hc[0] = '!';
hc[2] = 0;
{
if(cp[0])
{
if(cp[1])
{
if(cp[2])
}
}
}
/* save shell stack */
sp = stakfreeze(0);
{
/* read until event/quick substitution/comment designator */
{
{
do
}
continue;
}
{
DONE();
}
n = -1;
str = 0;
ref = 0;
{
flag |= HIST_QUICKSUBST;
goto getline;
}
{
cp += 2;
goto getline;
}
switch(c = *++cp) {
case ' ':
case '\t':
case '\n':
case '\0':
case '=':
case '(':
continue;
case '#': /* the line up to current position */
cp++;
n = staktell(); /* terminate string and dup */
stakputc('\0');
stakseek(n); /* remove null byte again */
n = 0; /* skip history file referencing */
break;
case '-': /* back reference by number */
goto string_event;
cp++;
case '0': /* reference by number */
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
n = 0;
if(c == '-')
n = -n;
break;
case '$':
n = -1;
case ':':
break;
case '?':
cp++;
flag |= HIST_QUESTION;
default:
/* read until end of string or word designator/modifier */
while(*cp)
{
cp++;
if((!(flag&HIST_QUESTION) &&
|| *cp == '%')
)
{
c = *cp;
*cp = '\0';
}
}
break;
}
flag |= HIST_EVENT;
if(str) /* !string or !?string? event designator */
{
/* search history for string */
n = 0; /* not found */
}
if(n)
{
if(n < 0) /* determine index for backref */
/* search and use history file if found */
}
if(!ref)
{
/* string not found or command # out of range */
c = *cp;
*cp = '\0';
*cp = c;
DONE();
}
if(str) /* string search: restore orig. line */
{
if(flag&HIST_QUESTION)
*cp++ = c; /* skip second question mark */
else
*cp = c;
}
/* colon introduces either word designators or modifiers */
cp++;
w[0] = 0; /* -1 means last word, -2 means match from !?string? */
w[1] = -1; /* -1 means last word, -2 means suppress last word */
goto getsel;
n = 0;
while(n < 2)
{
switch(c = *cp++) {
case '^': /* first word */
if(n == 0)
{
w[0] = w[1] = 1;
goto skip;
}
else
goto skip2;
case '$': /* last word */
w[n] = -1;
goto skip;
case '%': /* match from !?string? event designator */
if(n == 0)
{
if(!str)
{
w[0] = 0;
w[1] = -1;
}
else
{
w[0] = -2;
}
goto skip;
}
default:
cp--;
n = 2;
break;
case '*': /* until last word */
if(n == 0)
w[0] = 1;
w[1] = -1;
skip:
flag |= HIST_WORDDSGN;
n = 2;
break;
case '-': /* until last word or specified index */
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': /* specify index */
{
w[n] = c - '0';
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';
*cp = c;
DONE();
}
/* no valid word designator after colon, rewind */
/* open temp buffer, let sfio do the (re)allocation */
/* push selected words into buffer, squash
whitespace into single blank or a newline */
n = i = q = 0;
{
if(isspace(c))
{
continue;
}
if(n >= w[0] && ((w[0] != -2) ? (w[1] < 0 || n <= w[1]) : 1))
{
if(w[0] < 0)
else
if(i > 0)
flag &= ~HIST_NEWLINE;
p = 1;
}
else
p = 0;
do
{
if(p)
}
break;
n++;
}
if(w[0] != -2 && w[1] >= 0 && w[1] >= n)
{
c = *cp;
*cp = '\0';
*cp = c;
DONE();
}
else if(w[1] == -2) /* skip last word */
/* remove trailing newline */
{
}
if(str)
{
if(wm)
}
{
/* close !# temp file */
cc = 0;
}
{
if(flag & HIST_QUICKSUBST)
{
flag &= ~HIST_QUICKSUBST;
c = 's';
cp--;
}
else
c = *++cp;
if(c == 'g') /* global substitution */
{
flag |= HIST_GLOBALSUBST;
c = *++cp;
}
else
{
DONE();
}
if(c == 'h' || c == 'r') /* head or base */
{
n = -1;
{ /* remember position of / or . */
}
if(n > 0)
{ /* rewind to last / or . */
/* end string there */
}
}
else if(c == 't' || c == 'e') /* tail or suffix */
{
n = 0;
{ /* remember position of / or . */
}
/* rewind to last / or . */
/* copy from there on */
}
else if(c == 's' || c == '&')
{
cp++;
if(c == 's')
{
/* preset old with match from !?string? */
}
{
c = *cp;
*cp = '\0';
"%s%s: no previous substitution",
evp);
*cp = c;
DONE();
}
/* need pointer for strstr() */
flag |= HIST_SUBSTITUTE;
while(flag & HIST_SUBSTITUTE)
{
/* find string */
{ /* replace it */
c = *cc;
*cc = '\0';
*cc = c;
}
{ /* not successfull */
c = *cp;
*cp = '\0';
"%s%s: substitution failed",
evp);
*cp = c;
DONE();
}
/* loop if g modifier specified */
flag &= ~HIST_SUBSTITUTE;
}
/* output rest of line */
if(*cp)
cp--;
}
{ /* if any substitions done, swap buffers */
tmp2 = 0;
}
cc = 0;
if(*cp)
cp++;
}
/* flush temporary buffer to stack */
if(tmp)
{
if(flag & HIST_QUOTE)
stakputc('\'');
{
if(isspace(c))
{
/* squash white space to either a
blank or a newline */
do
if(flag & HIST_QUOTE_BR)
{
stakputc('\'');
stakputc(c);
stakputc('\'');
}
else
stakputc(c);
}
{
stakputc('\'');
stakputc('\\');
stakputc(c);
stakputc('\'');
}
else
stakputc(c);
}
if(flag & HIST_QUOTE)
stakputc('\'');
}
}
stakputc('\0');
done:
{
/* close !# temp file */
cc = 0;
}
/* error? */
/* restore shell stack */
if(off)
else
stakseek(0);
/* drop temporary files */
if(tmp2)
}
#endif