mirror of
git://git.code.sf.net/p/cdesktopenv/code
synced 2025-03-09 15:50:02 +00:00
This combines 20 cleanup commits from the dev branch. All changed files: - Clean up pointer defererences to sh. - Remove shp arguments from functions. Other notable changes: src/cmd/ksh93/include/shell.h, src/cmd/ksh93/sh/init.c: - On second thought, get rid of the function version of sh_getinterp() as libshell ABI compatibility is moot. We've already been breaking that by reordering the sh struct, so there is no way it's going to work without recompiling. src/cmd/ksh93/sh/name.c: - De-obfuscate the relationship between nv_scan() and scanfilter(). The former just calls the latter as a static function, there's no need to do that via a function pointer and void* type conversions. src/cmd/ksh93/bltins/typeset.c, src/cmd/ksh93/sh/name.c, src/cmd/ksh93/sh/nvdisc.c: - 'struct adata' and 'struct tdata', defined as local struct types in these files, need to have their first three fields in common, the first being a pointer to sh. This is because scanfilter() in name.c accesses these fields via a type conversion. So the sh field needed to be removed in all three at the same time. TODO: de-obfuscate: good practice definition via a header file. src/cmd/ksh93/sh/path.c: - Naming consistency: reserve the path_ function name prefix for externs and rename statics with that prefix. - The default path was sometimes referred to as the standard path. To use one term, rename std_path to defpath and onstdpath() to ondefpath(). - De-obfuscate SHOPT_PFSH conditional code by only calling pf_execve() (was path_pfexecve()) if that is compiled in. src/cmd/ksh93/include/streval.h, src/cmd/ksh93/sh/streval.c: - Rename extern strval() to arith_strval() for consistency. src/cmd/ksh93/sh/string.c: - Remove outdated/incorrect isxdigit() fallback; '#ifnded isxdigit' is not a correct test as isxdigit() is specified as a function. Plus, it's part of C89/C90 which we now require. (re:ac8991e5) src/cmd/ksh93/sh/suid_exec.c: - Replace an incorrect reference to shgd->current_pid with getpid(); it cannot work as (contrary to its misleading directory placement) suid_exec is an independent libast program with no link to ksh or libshell at all. However, no one noticed because this was in fallback code for ancient systems without setreuid(2). Since that standard function was specified in POSIX Issue 4 Version 2 from 1994, we should remove that fallback code sometime as part of another obsolete code cleanup operation to avoid further bit rot. (re:843b546c) src/cmd/ksh93/bltins/print.c: genformat(): - Remove preformat[] which was always empty and had no effect. src/cmd/ksh93/shell.3: - Minor copy-edit. - Remove documentation for nonexistent sh.infile_name. A search through ast-open-archive[*] reveals this never existed at all. - Document sh.savexit (== $?). src/cmd/ksh93/shell.3, src/cmd/ksh93/include/shell.h, src/cmd/ksh93/sh/init.c: - Remove sh.gd/shgd; this is now unused and was never documented or exposed in the shell.h public interface. - sh_sigcheck() was documented in shell.3 as taking no arguments whereas in the actual code it took a shp argument. I decided to go with the documentation. - That leaves sh_parse() as the only documented function that still takes an shp argument. I'm just going to go ahead and remove it for consistency, reverting sh_parse() to its pre-2003 spec. - Remove undocumented/unused sh_bltin_tree() function which simply returned sh.bltin_tree. - Bump SH_VERSION to 20220106.
737 lines
15 KiB
C
737 lines
15 KiB
C
/***********************************************************************
|
|
* *
|
|
* This software is part of the ast package *
|
|
* Copyright (c) 1982-2011 AT&T Intellectual Property *
|
|
* Copyright (c) 2020-2021 Contributors to ksh 93u+m *
|
|
* and is licensed under the *
|
|
* Eclipse Public License, Version 1.0 *
|
|
* by AT&T Intellectual Property *
|
|
* *
|
|
* A copy of the License is available at *
|
|
* http://www.eclipse.org/org/documents/epl-v10.html *
|
|
* (with md5 checksum b35adb5213ca9657e911e9befb180842) *
|
|
* *
|
|
* Information and Software Systems Research *
|
|
* AT&T Research *
|
|
* Florham Park NJ *
|
|
* *
|
|
* David Korn <dgk@research.att.com> *
|
|
* *
|
|
***********************************************************************/
|
|
/*
|
|
* 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
|
|
|
|
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]; /* [0] is "old", [1] is "new" string */
|
|
};
|
|
|
|
|
|
/*
|
|
* parse an /old/new/ string, delimiter expected as first char.
|
|
* if "old" not specified, keep sb->str[0]
|
|
* if "new" not specified, set sb->str[1] to empty string
|
|
* read up to third delimiter char, \n or \0, whichever comes first.
|
|
* return address is one past the last valid char in s:
|
|
* - the address containing \n or \0 or
|
|
* - one char beyond the third delimiter
|
|
*/
|
|
|
|
static char *parse_subst(const char *s, struct subst *sb)
|
|
{
|
|
char *cp,del;
|
|
int off,n = 0;
|
|
|
|
/* build the strings on the stack, mainly for '&' substitution in "new" */
|
|
off = staktell();
|
|
|
|
/* init "new" with empty string */
|
|
if(sb->str[1])
|
|
free(sb->str[1]);
|
|
sb->str[1] = sh_strdup("");
|
|
|
|
/* get delimiter */
|
|
del = *s;
|
|
|
|
cp = (char*) s + 1;
|
|
|
|
while(n < 2)
|
|
{
|
|
if(*cp == del || *cp == '\n' || *cp == '\0')
|
|
{
|
|
/* delimiter or EOL */
|
|
if(staktell() != off)
|
|
{
|
|
/* dupe string on stack and rewind stack */
|
|
stakputc('\0');
|
|
if(sb->str[n])
|
|
free(sb->str[n]);
|
|
sb->str[n] = sh_strdup(stakptr(off));
|
|
stakseek(off);
|
|
}
|
|
n++;
|
|
|
|
/* if not delimiter, we've reached EOL. Get outta here. */
|
|
if(*cp != del)
|
|
break;
|
|
}
|
|
else if(*cp == '\\')
|
|
{
|
|
if(*(cp+1) == del) /* quote delimiter */
|
|
{
|
|
stakputc(del);
|
|
cp++;
|
|
}
|
|
else if(*(cp+1) == '&' && n == 1)
|
|
{ /* quote '&' only in "new" */
|
|
stakputc('&');
|
|
cp++;
|
|
}
|
|
else
|
|
stakputc('\\');
|
|
}
|
|
else if(*cp == '&' && n == 1 && sb->str[0])
|
|
/* substitute '&' with "old" in "new" */
|
|
stakputs(sb->str[0]);
|
|
else
|
|
stakputc(*cp);
|
|
cp++;
|
|
}
|
|
|
|
/* rewind stack */
|
|
stakseek(off);
|
|
|
|
return cp;
|
|
}
|
|
|
|
/*
|
|
* history expansion main routine
|
|
*/
|
|
|
|
int hist_expand(const char *ln, char **xp)
|
|
{
|
|
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 */
|
|
*evp, /* event/word designator string, for error msgs */
|
|
*cc=0, /* copy of current line up to cp; temp ptr */
|
|
hc[3], /* default histchars */
|
|
*qc="\'\"`"; /* quote characters */
|
|
Sfio_t *ref=0, /* line referenced by event designator */
|
|
*tmp=0, /* temporary line buffer */
|
|
*tmp2=0;/* temporary line buffer */
|
|
Histloc_t hl; /* history location */
|
|
static Namval_t *np = 0; /* histchars variable */
|
|
static struct subst sb = {0,0}; /* substitution strings */
|
|
static Sfio_t *wm=0; /* word match from !?string? event designator */
|
|
|
|
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];
|
|
}
|
|
}
|
|
}
|
|
|
|
/* save shell stack */
|
|
if(off = staktell())
|
|
sp = stakfreeze(0);
|
|
|
|
cp = (char*)ln;
|
|
|
|
while(cp && *cp)
|
|
{
|
|
/* read until event/quick substitution/comment designator */
|
|
if((*cp != hc[0] && *cp != hc[1] && *cp != hc[2])
|
|
|| (*cp == hc[1] && cp != ln))
|
|
{
|
|
if(*cp == '\\') /* skip escaped designators */
|
|
stakputc(*cp++);
|
|
else if(*cp == '\'') /* skip quoted designators */
|
|
{
|
|
do
|
|
stakputc(*cp);
|
|
while(*++cp && *cp != '\'');
|
|
}
|
|
stakputc(*cp++);
|
|
continue;
|
|
}
|
|
|
|
if(hc[2] && *cp == hc[2]) /* history comment designator, skip rest of line */
|
|
{
|
|
stakputc(*cp++);
|
|
stakputs(cp);
|
|
DONE();
|
|
}
|
|
|
|
n = -1;
|
|
str = 0;
|
|
flag &= HIST_EVENT; /* save event flag for returning later */
|
|
evp = cp;
|
|
ref = 0;
|
|
|
|
if(*cp == hc[1]) /* shortcut substitution */
|
|
{
|
|
flag |= HIST_QUICKSUBST;
|
|
goto getline;
|
|
}
|
|
|
|
if(*cp == hc[0] && *(cp+1) == hc[0]) /* refer to line -1 */
|
|
{
|
|
cp += 2;
|
|
goto getline;
|
|
}
|
|
|
|
switch(c = *++cp) {
|
|
case ' ':
|
|
case '\t':
|
|
case '\n':
|
|
case '\0':
|
|
case '=':
|
|
case '(':
|
|
stakputc(hc[0]);
|
|
continue;
|
|
case '#': /* the line up to current position */
|
|
flag |= HIST_HASH;
|
|
cp++;
|
|
n = staktell(); /* terminate string and dup */
|
|
stakputc('\0');
|
|
cc = sh_strdup(stakptr(0));
|
|
stakseek(n); /* remove null byte again */
|
|
ref = sfopen(ref, cc, "s"); /* open as file */
|
|
n = 0; /* skip history file referencing */
|
|
break;
|
|
case '-': /* back reference by number */
|
|
if(!isdigit(*(cp+1)))
|
|
goto string_event;
|
|
cp++;
|
|
/* FALLTHROUGH */
|
|
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;
|
|
while(isdigit(*cp))
|
|
n = n * 10 + (*cp++) - '0';
|
|
if(c == '-')
|
|
n = -n;
|
|
break;
|
|
case '$':
|
|
n = -1;
|
|
case ':':
|
|
break;
|
|
case '?':
|
|
cp++;
|
|
flag |= HIST_QUESTION;
|
|
/* FALLTHROUGH */
|
|
string_event:
|
|
default:
|
|
/* read until end of string or word designator/modifier */
|
|
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) /* !string or !?string? event designator */
|
|
{
|
|
|
|
/* search history for string */
|
|
hl = hist_find(sh.hist_ptr, str,
|
|
sh.hist_ptr->histind,
|
|
flag&HIST_QUESTION, -1);
|
|
if((n = hl.hist_command) == -1)
|
|
n = 0; /* not found */
|
|
}
|
|
if(n)
|
|
{
|
|
if(n < 0) /* determine index for backref */
|
|
n = sh.hist_ptr->histind + n;
|
|
/* search and use history file if found */
|
|
if(n > 0 && hist_seek(sh.hist_ptr, n) != -1)
|
|
ref = sh.hist_ptr->histfp;
|
|
|
|
}
|
|
if(!ref)
|
|
{
|
|
/* string not found or command # out of range */
|
|
c = *cp;
|
|
*cp = '\0';
|
|
errormsg(SH_DICT, ERROR_ERROR, "%s: event not found", evp);
|
|
*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 */
|
|
if(*(evp = cp) == ':')
|
|
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 */
|
|
|
|
if(flag & HIST_QUICKSUBST) /* shortcut substitution */
|
|
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;
|
|
ref = wm;
|
|
}
|
|
else
|
|
{
|
|
w[0] = -2;
|
|
w[1] = sftell(ref) + hl.hist_char;
|
|
}
|
|
sfseek(wm, 0, SEEK_SET);
|
|
goto skip;
|
|
}
|
|
/* FALLTHROUGH */
|
|
default:
|
|
skip2:
|
|
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 */
|
|
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();
|
|
}
|
|
|
|
/* no valid word designator after colon, rewind */
|
|
if(!(flag & HIST_WORDDSGN) && (*evp == ':'))
|
|
cp = evp;
|
|
|
|
getsel:
|
|
/* open temp buffer, let sfio do the (re)allocation */
|
|
tmp = sfopen(NULL, NULL, "swr");
|
|
|
|
/* push selected words into buffer, squash
|
|
whitespace into single blank or a newline */
|
|
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) /* skip last word */
|
|
sfseek(tmp, i, SEEK_SET);
|
|
|
|
/* remove trailing newline */
|
|
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))
|
|
{
|
|
/* close !# temp file */
|
|
sfclose(ref);
|
|
flag &= ~HIST_HASH;
|
|
free(cc);
|
|
cc = 0;
|
|
}
|
|
|
|
evp = cp;
|
|
|
|
/* selected line/words are now in buffer, now go for the modifiers */
|
|
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') /* global substitution */
|
|
{
|
|
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') /* head or base */
|
|
{
|
|
n = -1;
|
|
while((c = sfgetc(tmp)) > 0)
|
|
{ /* remember position of / or . */
|
|
if((c == '/' && *cp == 'h') || (c == '.' && *cp == 'r'))
|
|
n = sftell(tmp2);
|
|
sfputc(tmp2, c);
|
|
}
|
|
if(n > 0)
|
|
{ /* rewind to last / or . */
|
|
sfseek(tmp2, n, SEEK_SET);
|
|
/* end string there */
|
|
sfputc(tmp2, '\0');
|
|
}
|
|
}
|
|
else if(c == 't' || c == 'e') /* tail or suffix */
|
|
{
|
|
n = 0;
|
|
while((c = sfgetc(tmp)) > 0)
|
|
{ /* remember position of / or . */
|
|
if((c == '/' && *cp == 't') || (c == '.' && *cp == 'e'))
|
|
n = sftell(tmp);
|
|
}
|
|
/* rewind to last / or . */
|
|
sfseek(tmp, n, SEEK_SET);
|
|
/* copy from there on */
|
|
while((c = sfgetc(tmp)) > 0)
|
|
sfputc(tmp2, c);
|
|
}
|
|
else if(c == 's' || c == '&')
|
|
{
|
|
cp++;
|
|
|
|
if(c == 's')
|
|
{
|
|
/* preset old with match from !?string? */
|
|
if(!sb.str[0] && wm)
|
|
sb.str[0] = sh_strdup(sfsetbuf(wm, (void*)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();
|
|
}
|
|
|
|
/* need pointer for strstr() */
|
|
str = sfsetbuf(tmp, (void*)1, 0);
|
|
|
|
flag |= HIST_SUBSTITUTE;
|
|
while(flag & HIST_SUBSTITUTE)
|
|
{
|
|
/* find string */
|
|
if(cc = strstr(str, sb.str[0]))
|
|
{ /* replace it */
|
|
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))
|
|
{ /* not successful */
|
|
c = *cp;
|
|
*cp = '\0';
|
|
errormsg(SH_DICT, ERROR_ERROR,
|
|
"%s%s: substitution failed",
|
|
(flag & HIST_QUICKSUBST) ? ":s" : "",
|
|
evp);
|
|
*cp = c;
|
|
DONE();
|
|
}
|
|
/* loop if g modifier specified */
|
|
if(!cc || !(flag & HIST_GLOBALSUBST))
|
|
flag &= ~HIST_SUBSTITUTE;
|
|
}
|
|
/* output rest of line */
|
|
sfputr(tmp2, str, -1);
|
|
if(*cp)
|
|
cp--;
|
|
}
|
|
|
|
if(sftell(tmp2))
|
|
{ /* if any substitutions done, swap buffers */
|
|
if(wm != tmp)
|
|
sfclose(tmp);
|
|
tmp = tmp2;
|
|
tmp2 = 0;
|
|
}
|
|
cc = 0;
|
|
if(*cp)
|
|
cp++;
|
|
}
|
|
|
|
/* flush temporary buffer to stack */
|
|
if(tmp)
|
|
{
|
|
sfseek(tmp, 0, SEEK_SET);
|
|
|
|
if(flag & HIST_QUOTE)
|
|
stakputc('\'');
|
|
|
|
while((c = sfgetc(tmp)) > 0)
|
|
{
|
|
if(isspace(c))
|
|
{
|
|
flag = flag & ~HIST_NEWLINE;
|
|
|
|
/* squash white space to either a
|
|
blank or a 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))
|
|
{
|
|
/* close !# temp file */
|
|
sfclose(ref);
|
|
free(cc);
|
|
cc = 0;
|
|
}
|
|
|
|
/* error? */
|
|
if(staktell() && !(flag & HIST_ERROR))
|
|
*xp = sh_strdup(stakfreeze(1));
|
|
|
|
/* restore shell stack */
|
|
if(off)
|
|
stakset(sp,off);
|
|
else
|
|
stakseek(0);
|
|
|
|
/* drop temporary files */
|
|
|
|
if(tmp && tmp != wm)
|
|
sfclose(tmp);
|
|
if(tmp2)
|
|
sfclose(tmp2);
|
|
|
|
return (flag & HIST_ERROR ? HIST_ERROR : flag & HIST_FLAG_RETURN_MASK);
|
|
}
|
|
|
|
#endif
|