Rework addition of Symbian splash screen to reduce the source impact (uses SVG from Bug 2414)
Notes: by using the OPTION SOURCEDIR parameter in the mifconv extension instructions, I can
arrange to use the same source file name in sfimage, without having to export over the original
Nokia file. This means that the name inside splashscreen.mbg is the same, which removes the need
for the conditional compilation in SplashScreen.cpp, and gets rid of sf_splashscreen.mmp.
/*
* Copyright (c) 2007-2009 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description:
*
*/
/*
* This file is part of zsh, the Z shell.
*
* Copyright (c) 1992-1997 Paul Falstad
* All rights reserved.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and to distribute modified versions of this software for any
* purpose, provided that the above copyright notice and the following
* two paragraphs appear in all copies of this software.
*
* In no event shall Paul Falstad or the Zsh Development Group be liable
* to any party for direct, indirect, special, incidental, or consequential
* damages arising out of the use of this software and its documentation,
* even if Paul Falstad and the Zsh Development Group have been advised of
* the possibility of such damage.
*
* Paul Falstad and the Zsh Development Group specifically disclaim any
* warranties, including, but not limited to, the implied warranties of
* merchantability and fitness for a particular purpose. The software
* provided hereunder is on an "as is" basis, and Paul Falstad and the
* Zsh Development Group have no obligation to provide maintenance,
* support, updates, enhancements, or modifications.
*
*/
#include "zsh.mdh"
#include "hist.pro"
#ifdef __SYMBIAN32__
#ifdef __WINSCW__
#pragma warn_unusedarg off
#pragma warn_possunwant off
#endif//__WINSCW__
#endif//__SYMBIAN32__
/* Functions to call for getting/ungetting a character and for history
* word control. */
/**/
mod_export int (*hgetc) _((void));
/**/
void (*hungetc) _((int));
/**/
void (*hwaddc) _((int));
/**/
void (*hwbegin) _((int));
/**/
void (*hwend) _((void));
/**/
void (*addtoline) _((int));
/* != 0 means history substitution is turned off */
/**/
mod_export int stophist;
/* if != 0, we are expanding the current line */
/**/
mod_export int expanding;
/* these are used to modify the cursor position during expansion */
/**/
mod_export int excs, exlast;
/*
* Current history event number
*
* Note on curhist: with history inactive, this points to the
* last line actually added to the history list. With history active,
* the line does not get added to the list until hend(), if at all.
* However, curhist is incremented to reflect the current line anyway
* and a temporary history entry is inserted while the user is editing.
* If the resulting line was not added to the list, a flag is set so
* that curhist will be decremented in hbegin().
*/
/**/
mod_export zlong curhist;
/**/
struct histent curline;
/* current line count of allocated history entries */
/**/
zlong histlinect;
/* The history lines are kept in a hash, and also doubly-linked in a ring */
/**/
HashTable histtab;
/**/
mod_export Histent hist_ring;
/* capacity of history lists */
/**/
zlong histsiz;
/* desired history-file size (in lines) */
/**/
zlong savehistsiz;
/* if = 1, we have performed history substitution on the current line *
* if = 2, we have used the 'p' modifier */
/**/
int histdone;
/* state of the history mechanism */
/**/
int histactive;
/* Current setting of the associated option, but sometimes also includes
* the setting of the HIST_SAVE_NO_DUPS option. */
/**/
int hist_ignore_all_dups;
/* What flags (if any) we should skip when moving through the history */
/**/
mod_export int hist_skip_flags;
/* Bits of histactive variable */
#define HA_ACTIVE (1<<0) /* History mechanism is active */
#define HA_NOSTORE (1<<1) /* Don't store the line when finished */
#define HA_NOINC (1<<2) /* Don't store, curhist not incremented */
/* Array of word beginnings and endings in current history line. */
/**/
short *chwords;
/* Max, actual position in chwords.
* nwords = chwordpos/2 because we record beginning and end of words.
*/
/**/
int chwordlen, chwordpos;
/* the last l for s/l/r/ history substitution */
/**/
char *hsubl;
/* the last r for s/l/r/ history substitution */
/**/
char *hsubr;
/* pointer into the history line */
/**/
mod_export char *hptr;
/* the current history line */
/**/
mod_export char *chline;
/* true if the last character returned by hgetc was an escaped bangchar *
* if it is set and NOBANGHIST is unset hwaddc escapes bangchars */
/**/
int qbang;
/* max size of histline */
/**/
int hlinesz;
/* default event (usually curhist-1, that is, "!!") */
static zlong defev;
/* add a character to the current history word */
static void
ihwaddc(int c)
{
/* Only if history line exists and lexing has not finished. */
if (chline && !(errflag || lexstop)) {
/* Quote un-expanded bangs in the history line. */
if (c == bangchar && stophist < 2 && qbang)
/* If qbang is not set, we do not escape this bangchar as it's *
* not mecessary (e.g. it's a bang in !=, or it is followed *
* by a space). Roughly speaking, qbang is zero only if the *
* history interpreter has already digested this bang and *
* found that it is not necessary to escape it. */
hwaddc('\\');
*hptr++ = c;
/* Resize history line if necessary */
if (hptr - chline >= hlinesz) {
int oldsiz = hlinesz;
chline = realloc(chline, hlinesz = oldsiz + 64);
hptr = chline + oldsiz;
}
}
}
/* This function adds a character to the zle input line. It is used when *
* zsh expands history (see doexpandhist() in zle_tricky.c). It also *
* calculates the new cursor position after the expansion. It is called *
* from hgetc() and from gettok() in lex.c for characters in comments. */
/**/
void
iaddtoline(int c)
{
if (!expanding || lexstop)
return;
if (qbang && c == bangchar && stophist < 2) {
exlast--;
spaceinline(1);
line[cs++] = '\\';
}
if (excs > cs) {
excs += 1 + inbufct - exlast;
if (excs < cs)
/* this case could be handled better but it is *
* so rare that it does not worth it */
excs = cs;
}
exlast = inbufct;
spaceinline(1);
line[cs++] = itok(c) ? ztokens[c - Pound] : c;
}
static int
ihgetc(void)
{
int c = ingetc();
qbang = 0;
if (!stophist && !(inbufflags & INP_ALIAS)) {
/* If necessary, expand history characters. */
c = histsubchar(c);
if (c < 0) {
/* bad expansion */
errflag = lexstop = 1;
return ' ';
}
}
if ((inbufflags & INP_HIST) && !stophist) {
/* the current character c came from a history expansion *
* (inbufflags & INP_HIST) and history is not disabled *
* (e.g. we are not inside single quotes). In that case, \! *
* should be treated as ! (since this \! came from a previous *
* history line where \ was used to escape the bang). So if *
* c == '\\' we fetch one more character to see if it's a bang, *
* and if it is not, we unget it and reset c back to '\\' */
qbang = 0;
if (c == '\\' && !(qbang = (c = ingetc()) == bangchar))
safeinungetc(c), c = '\\';
} else if (stophist || (inbufflags & INP_ALIAS))
/* If the result is a bangchar which came from history or alias *
* expansion, we treat it as an escaped bangchar, unless history *
* is disabled. If stophist == 1 it only means that history is *
* temporarily disabled by a !" which won't appear in in the *
* history, so we still have an escaped bang. stophist > 1 if *
* history is disabled with NOBANGHIST or by someone else (e.g. *
* when the lexer scans single quoted text). */
qbang = c == bangchar && (stophist < 2);
hwaddc(c);
addtoline(c);
return c;
}
/**/
static void
safeinungetc(int c)
{
if (lexstop)
lexstop = 0;
else
inungetc(c);
}
/**/
void
herrflush(void)
{
inpopalias();
while (!lexstop && inbufct && !strin)
hwaddc(ingetc());
}
/* extract :s/foo/bar/ delimiters and arguments */
/**/
static int
getsubsargs(char *subline)
{
int del;
char *ptr1, *ptr2;
del = ingetc();
ptr1 = hdynread2(del);
if (!ptr1)
return 1;
ptr2 = hdynread2(del);
if (strlen(ptr1)) {
zsfree(hsubl);
hsubl = ptr1;
}
zsfree(hsubr);
hsubr = ptr2;
if (hsubl && !strstr(subline, hsubl)) {
herrflush();
zerr("substitution failed", NULL, 0);
return 1;
}
return 0;
}
/* Get the maximum no. of words for a history entry. */
/**/
static int
getargc(Histent ehist)
{
return ehist->nwords ? ehist->nwords-1 : 0;
}
/* Perform history substitution, returning the next character afterwards. */
/**/
static int
histsubchar(int c)
{
int farg, evset = -1, larg, argc, cflag = 0, bflag = 0;
zlong ev;
static int marg = -1;
static zlong mev = -1;
char buf[256], *ptr;
char *sline;
Histent ehist;
/* look, no goto's */
if (isfirstch && c == hatchar) {
/* Line begins ^foo^bar */
isfirstch = 0;
inungetc(hatchar);
if (!(ehist = gethist(defev))
|| !(sline = getargs(ehist, 0, getargc(ehist)))
|| getsubsargs(sline) || !hsubl)
return -1;
subst(&sline, hsubl, hsubr, 0);
} else {
/* Line doesn't begin ^foo^bar */
if (c != ' ')
isfirstch = 0;
#ifndef __SYMBIAN32__
if (c == '\\') {
int g = ingetc();
if (g != bangchar)
safeinungetc(g);
else {
qbang = 1;
return bangchar;
}
}
#endif //__SYMBIAN32__
if (c != bangchar)
return c;
*hptr = '\0';
if ((c = ingetc()) == '{') {
bflag = cflag = 1;
c = ingetc();
}
if (c == '\"') {
stophist = 1;
return ingetc();
}
if ((!cflag && inblank(c)) || c == '=' || c == '(' || lexstop) {
safeinungetc(c);
return bangchar;
}
cflag = 0;
ptr = buf;
/* get event number */
queue_signals();
if (c == '?') {
for (;;) {
c = ingetc();
if (c == '?' || c == '\n' || lexstop)
break;
else
*ptr++ = c;
}
if (c != '\n' && !lexstop)
c = ingetc();
*ptr = '\0';
mev = ev = hconsearch(hsubl = ztrdup(buf), &marg);
evset = 0;
if (ev == -1) {
herrflush();
unqueue_signals();
zerr("no such event: %s", buf, 0);
return -1;
}
} else {
zlong t0;
for (;;) {
if (inblank(c) || c == ';' || c == ':' || c == '^' ||
c == '$' || c == '*' || c == '%' || c == '}' ||
c == '\'' || c == '"' || c == '`' || lexstop)
break;
if (ptr != buf) {
if (c == '-')
break;
if ((idigit(buf[0]) || buf[0] == '-') && !idigit(c))
break;
}
*ptr++ = c;
if (c == '#' || c == bangchar) {
c = ingetc();
break;
}
c = ingetc();
}
*ptr = 0;
if (!*buf) {
if (c != '%') {
if (isset(CSHJUNKIEHISTORY))
ev = addhistnum(curhist,-1,HIST_FOREIGN);
else
ev = defev;
if (c == ':' && evset == -1)
evset = 0;
else
evset = 1;
} else {
if (marg != -1)
ev = mev;
else
ev = defev;
evset = 0;
}
} else if ((t0 = zstrtol(buf, NULL, 10))) {
ev = (t0 < 0) ? addhistnum(curhist,t0,HIST_FOREIGN) : t0;
evset = 1;
} else if ((unsigned)*buf == bangchar) {
ev = addhistnum(curhist,-1,HIST_FOREIGN);
evset = 1;
} else if (*buf == '#') {
ev = curhist;
evset = 1;
} else if ((ev = hcomsearch(buf)) == -1) {
herrflush();
unqueue_signals();
zerr("event not found: %s", buf, 0);
return -1;
} else
evset = 1;
}
/* get the event */
if (!(ehist = gethist(defev = ev))) {
unqueue_signals();
return -1;
}
/* extract the relevant arguments */
argc = getargc(ehist);
if (c == ':') {
cflag = 1;
c = ingetc();
if (c == '%' && marg != -1) {
if (!evset) {
ehist = gethist(defev = mev);
argc = getargc(ehist);
} else {
herrflush();
unqueue_signals();
zerr("Ambiguous history reference", NULL, 0);
return -1;
}
}
}
if (c == '*') {
farg = 1;
larg = argc;
cflag = 0;
} else {
inungetc(c);
larg = farg = getargspec(argc, marg, evset);
if (larg == -2) {
unqueue_signals();
return -1;
}
if (farg != -1)
cflag = 0;
c = ingetc();
if (c == '*') {
cflag = 0;
larg = argc;
} else if (c == '-') {
cflag = 0;
larg = getargspec(argc, marg, evset);
if (larg == -2) {
unqueue_signals();
return -1;
}
if (larg == -1)
larg = argc - 1;
} else
inungetc(c);
}
if (farg == -1)
farg = 0;
if (larg == -1)
larg = argc;
if (!(sline = getargs(ehist, farg, larg))) {
unqueue_signals();
return -1;
}
unqueue_signals();
}
/* do the modifiers */
for (;;) {
c = (cflag) ? ':' : ingetc();
cflag = 0;
if (c == ':') {
int gbal = 0;
if ((c = ingetc()) == 'g') {
gbal = 1;
c = ingetc();
}
switch (c) {
case 'p':
histdone = HISTFLAG_DONE | HISTFLAG_NOEXEC;
break;
case 'h':
if (!remtpath(&sline)) {
herrflush();
zerr("modifier failed: h", NULL, 0);
return -1;
}
break;
case 'e':
if (!rembutext(&sline)) {
herrflush();
zerr("modifier failed: e", NULL, 0);
return -1;
}
break;
case 'r':
if (!remtext(&sline)) {
herrflush();
zerr("modifier failed: r", NULL, 0);
return -1;
}
break;
case 't':
if (!remlpaths(&sline)) {
herrflush();
zerr("modifier failed: t", NULL, 0);
return -1;
}
break;
case 's':
if (getsubsargs(sline))
return -1; /* fall through */
case '&':
if (hsubl && hsubr)
subst(&sline, hsubl, hsubr, gbal);
else {
herrflush();
zerr("no previous substitution", NULL, 0);
return -1;
}
break;
case 'q':
quote(&sline);
break;
case 'Q':
{
int one = noerrs, oef = errflag;
noerrs = 1;
parse_subst_string(sline);
noerrs = one;
errflag = oef;
remnulargs(sline);
untokenize(sline);
}
break;
case 'x':
quotebreak(&sline);
break;
case 'l':
downcase(&sline);
break;
case 'u':
upcase(&sline);
break;
default:
herrflush();
zerr("illegal modifier: %c", NULL, c);
return -1;
}
} else {
if (c != '}' || !bflag)
inungetc(c);
if (c != '}' && bflag) {
zerr("'}' expected", NULL, 0);
return -1;
}
break;
}
}
/*
* Push the expanded value onto the input stack,
* marking this as a history word for purposes of the alias stack.
*/
lexstop = 0;
/* this function is called only called from hgetc and only if *
* !(inbufflags & INP_ALIAS). History expansion should never be *
* done with INP_ALIAS (to prevent recursive history expansion and *
* histoty expansion of aliases). Escapes are not removed here. *
* This is now handled in hgetc. */
inpush(sline, INP_HIST, NULL); /* sline from heap, don't free */
histdone |= HISTFLAG_DONE;
if (isset(HISTVERIFY))
histdone |= HISTFLAG_NOEXEC | HISTFLAG_RECALL;
/* Don't try and re-expand line. */
return ingetc();
}
/* unget a char and remove it from chline. It can only be used *
* to unget a character returned by hgetc. */
static void
ihungetc(int c)
{
int doit = 1;
while (!lexstop && !errflag) {
if (hptr[-1] != (char) c && stophist < 4 &&
hptr > chline + 1 && hptr[-1] == '\n' && hptr[-2] == '\\')
hungetc('\n'), hungetc('\\');
if (expanding) {
cs--;
ll--;
exlast++;
}
DPUTS(hptr <= chline, "BUG: hungetc attempted at buffer start");
hptr--;
DPUTS(*hptr != (char) c, "BUG: wrong character in hungetc() ");
qbang = (c == bangchar && stophist < 2 &&
hptr > chline && hptr[-1] == '\\');
if (doit)
inungetc(c);
if (!qbang)
return;
doit = !stophist && ((inbufflags & INP_HIST) ||
!(inbufflags & INP_ALIAS));
c = '\\';
}
}
/* begin reading a string */
/**/
mod_export void
strinbeg(int dohist)
{
strin++;
hbegin(dohist);
lexinit();
}
/* done reading a string */
/**/
mod_export void
strinend(void)
{
hend(NULL);
DPUTS(!strin, "BUG: strinend() called without strinbeg()");
strin--;
isfirstch = 1;
histdone = 0;
}
/* dummy functions to use instead of hwaddc(), hwbegin(), and hwend() when
* they aren't needed */
static void
nohw(UNUSED(int c))
{
}
static void
nohwe(void)
{
}
/* these functions handle adding/removing curline to/from the hist_ring */
static void
linkcurline(void)
{
if (!hist_ring)
hist_ring = curline.up = curline.down = &curline;
else {
curline.up = hist_ring;
curline.down = hist_ring->down;
hist_ring->down = hist_ring->down->up = &curline;
hist_ring = &curline;
}
curline.histnum = ++curhist;
}
static void
unlinkcurline(void)
{
curline.up->down = curline.down;
curline.down->up = curline.up;
if (hist_ring == &curline) {
if (!histlinect)
hist_ring = NULL;
else
hist_ring = curline.up;
}
curhist--;
}
/* initialize the history mechanism */
/**/
mod_export void
hbegin(int dohist)
{
isfirstln = isfirstch = 1;
errflag = histdone = 0;
if (!dohist)
stophist = 2;
else if (dohist != 2)
stophist = (!interact || unset(SHINSTDIN)) ? 2 : 0;
else
stophist = 0;
if (stophist == 2 || (inbufflags & INP_ALIAS)) {
chline = hptr = NULL;
hlinesz = 0;
chwords = NULL;
chwordlen = 0;
hgetc = ingetc;
hungetc = inungetc;
hwaddc = nohw;
hwbegin = nohw;
hwend = nohwe;
addtoline = nohw;
} else {
chline = hptr = zshcalloc(hlinesz = 64);
chwords = zalloc((chwordlen = 64) * sizeof(short));
hgetc = ihgetc;
hungetc = ihungetc;
hwaddc = ihwaddc;
hwbegin = ihwbegin;
hwend = ihwend;
addtoline = iaddtoline;
if (!isset(BANGHIST))
stophist = 4;
}
chwordpos = 0;
if (hist_ring && !hist_ring->ftim)
hist_ring->ftim = time(NULL);
if ((dohist == 2 || (interact && isset(SHINSTDIN))) && !strin) {
histactive = HA_ACTIVE;
attachtty(mypgrp);
linkcurline();
defev = addhistnum(curhist, -1, HIST_FOREIGN);
} else
histactive = HA_ACTIVE | HA_NOINC;
}
/**/
void
histreduceblanks(void)
{
int i, len, pos, needblank, spacecount = 0;
if (isset(HISTIGNORESPACE))
while (chline[spacecount] == ' ') spacecount++;
for (i = 0, len = spacecount; i < chwordpos; i += 2) {
len += chwords[i+1] - chwords[i]
+ (i > 0 && chwords[i] > chwords[i-1]);
}
if (chline[len] == '\0')
return;
for (i = 0, pos = spacecount; i < chwordpos; i += 2) {
len = chwords[i+1] - chwords[i];
needblank = (i < chwordpos-2 && chwords[i+2] > chwords[i+1]);
if (pos != chwords[i]) {
memcpy(chline + pos, chline + chwords[i], len + needblank);
chwords[i] = pos;
chwords[i+1] = chwords[i] + len;
}
pos += len + needblank;
}
chline[pos] = '\0';
}
/**/
void
histremovedups(void)
{
Histent he, next;
for (he = hist_ring; he; he = next) {
next = up_histent(he);
if (he->flags & HIST_DUP)
freehistnode((HashNode)he);
}
}
/**/
mod_export zlong
addhistnum(zlong hl, int n, int xflags)
{
int dir = n < 0? -1 : n > 0? 1 : 0;
Histent he = gethistent(hl, dir);
if (!he)
return 0;
if (he->histnum != hl)
n -= dir;
if (n)
he = movehistent(he, n, xflags);
if (!he)
return dir < 0? firsthist() - 1 : curhist + 1;
return he->histnum;
}
/**/
mod_export Histent
movehistent(Histent he, int n, int xflags)
{
while (n < 0) {
if (!(he = up_histent(he)))
return NULL;
if (!(he->flags & xflags))
n++;
}
while (n > 0) {
if (!(he = down_histent(he)))
return NULL;
if (!(he->flags & xflags))
n--;
}
checkcurline(he);
return he;
}
/**/
mod_export Histent
up_histent(Histent he)
{
return he->up == hist_ring? NULL : he->up;
}
/**/
mod_export Histent
down_histent(Histent he)
{
return he == hist_ring? NULL : he->down;
}
/**/
mod_export Histent
gethistent(zlong ev, int nearmatch)
{
Histent he;
if (!hist_ring)
return NULL;
if (ev - hist_ring->down->histnum < hist_ring->histnum - ev) {
for (he = hist_ring->down; he->histnum < ev; he = he->down) ;
if (he->histnum != ev) {
if (nearmatch == 0
|| (nearmatch < 0 && (he = up_histent(he)) == NULL))
return NULL;
}
}
else {
for (he = hist_ring; he->histnum > ev; he = he->up) ;
if (he->histnum != ev) {
if (nearmatch == 0
|| (nearmatch > 0 && (he = down_histent(he)) == NULL))
return NULL;
}
}
checkcurline(he);
return he;
}
static void
putoldhistentryontop(short keep_going)
{
static Histent next = NULL;
Histent he = keep_going? next : hist_ring->down;
next = he->down;
if (isset(HISTEXPIREDUPSFIRST) && !(he->flags & HIST_DUP)) {
static zlong max_unique_ct = 0;
if (!keep_going)
max_unique_ct = savehistsiz;
do {
if (max_unique_ct-- <= 0 || he == hist_ring) {
max_unique_ct = 0;
he = hist_ring->down;
next = hist_ring;
break;
}
he = next;
next = he->down;
} while (!(he->flags & HIST_DUP));
}
if (he != hist_ring->down) {
he->up->down = he->down;
he->down->up = he->up;
he->up = hist_ring;
he->down = hist_ring->down;
hist_ring->down = he->down->up = he;
}
hist_ring = he;
}
/**/
Histent
prepnexthistent(void)
{
Histent he;
int curline_in_ring = hist_ring == &curline;
if (curline_in_ring)
unlinkcurline();
if (hist_ring && hist_ring->flags & HIST_TMPSTORE) {
curhist--;
freehistnode((HashNode)hist_ring);
}
if (histlinect < histsiz) {
he = (Histent)zshcalloc(sizeof *he);
if (!hist_ring)
hist_ring = he->up = he->down = he;
else {
he->up = hist_ring;
he->down = hist_ring->down;
hist_ring->down = he->down->up = he;
hist_ring = he;
}
histlinect++;
}
else {
putoldhistentryontop(0);
freehistdata(hist_ring, 0);
he = hist_ring;
}
he->histnum = ++curhist;
if (curline_in_ring)
linkcurline();
return he;
}
/* A helper function for hend() */
static int
should_ignore_line(Eprog prog)
{
if (isset(HISTIGNORESPACE)) {
if (*chline == ' ' || aliasspaceflag)
return 1;
}
if (!prog)
return 0;
if (isset(HISTNOFUNCTIONS)) {
Wordcode pc = prog->prog;
wordcode code = *pc;
if (wc_code(code) == WC_LIST && WC_LIST_TYPE(code) & Z_SIMPLE
&& wc_code(pc[2]) == WC_FUNCDEF)
return 1;
}
if (isset(HISTNOSTORE)) {
char *b = getjobtext(prog, NULL);
int saw_builtin;
if (*b == 'b' && strncmp(b,"builtin ",8) == 0) {
b += 8;
saw_builtin = 1;
} else
saw_builtin = 0;
if (*b == 'h' && strncmp(b,"history",7) == 0 && (!b[7] || b[7] == ' ')
&& (saw_builtin || !shfunctab->getnode(shfunctab,"history")))
return 1;
if (*b == 'r' && (!b[1] || b[1] == ' ')
&& (saw_builtin || !shfunctab->getnode(shfunctab,"r")))
return 1;
if (*b == 'f' && b[1] == 'c' && b[2] == ' ' && b[3] == '-'
&& (saw_builtin || !shfunctab->getnode(shfunctab,"fc"))) {
b += 3;
do {
if (*++b == 'l')
return 1;
} while (ialpha(*b));
}
}
return 0;
}
/* say we're done using the history mechanism */
/**/
mod_export int
hend(Eprog prog)
{
int flag, save = 1;
char *hf;
DPUTS(stophist != 2 && !(inbufflags & INP_ALIAS) && !chline,
"BUG: chline is NULL in hend()");
queue_signals();
if (histdone & HISTFLAG_SETTY)
settyinfo(&shttyinfo);
if (!(histactive & HA_NOINC))
unlinkcurline();
if (histactive & (HA_NOSTORE|HA_NOINC)) {
zfree(chline, hlinesz);
zfree(chwords, chwordlen*sizeof(short));
chline = NULL;
histactive = 0;
unqueue_signals();
return 1;
}
if (hist_ignore_all_dups != isset(HISTIGNOREALLDUPS)
&& (hist_ignore_all_dups = isset(HISTIGNOREALLDUPS)) != 0)
histremovedups();
/* For history sharing, lock history file once for both read and write */
hf = getsparam("HISTFILE");
if (isset(SHAREHISTORY) && lockhistfile(hf, 0)) {
readhistfile(hf, 0, HFILE_USE_OPTIONS | HFILE_FAST);
curline.histnum = curhist+1;
}
flag = histdone;
histdone = 0;
if (hptr < chline + 1)
save = 0;
else {
*hptr = '\0';
if (hptr[-1] == '\n') {
if (chline[1]) {
*--hptr = '\0';
} else
save = 0;
}
if (chwordpos <= 2)
save = 0;
else if (should_ignore_line(prog))
save = -1;
}
if (flag & (HISTFLAG_DONE | HISTFLAG_RECALL)) {
char *ptr;
ptr = ztrdup(chline);
if ((flag & (HISTFLAG_DONE | HISTFLAG_RECALL)) == HISTFLAG_DONE) {
zputs(ptr, shout);
fputc('\n', shout);
fflush(shout);
}
if (flag & HISTFLAG_RECALL) {
zpushnode(bufstack, ptr);
save = 0;
} else
zsfree(ptr);
}
if (save || *chline == ' ') {
Histent he;
for (he = hist_ring; he && he->flags & HIST_FOREIGN;
he = up_histent(he)) ;
if (he && he->flags & HIST_TMPSTORE) {
if (he == hist_ring)
curline.histnum = curhist--;
freehistnode((HashNode)he);
}
}
if (save) {
Histent he;
int newflags;
#ifdef DEBUG
/* debugging only */
if (chwordpos%2) {
hwend();
DPUTS(1, "BUG: uncompleted line in history");
}
#endif
/* get rid of pesky \n which we've already nulled out */
if (chwordpos > 1 && !chline[chwords[chwordpos-2]]) {
chwordpos -= 2;
/* strip superfluous blanks, if desired */
if (isset(HISTREDUCEBLANKS))
histreduceblanks();
}
newflags = save > 0? 0 : HIST_TMPSTORE;
if ((isset(HISTIGNOREDUPS) || isset(HISTIGNOREALLDUPS)) && save > 0
&& hist_ring && histstrcmp(chline, hist_ring->text) == 0) {
/* This history entry compares the same as the previous.
* In case minor changes were made, we overwrite the
* previous one with the current one. This also gets the
* timestamp right. Perhaps, preserve the HIST_OLD flag.
*/
he = hist_ring;
newflags |= he->flags & HIST_OLD; /* Avoid re-saving */
freehistdata(he, 0);
curline.histnum = curhist;
} else
he = prepnexthistent();
he->text = ztrdup(chline);
he->stim = time(NULL);
he->ftim = 0L;
he->flags = newflags;
if ((he->nwords = chwordpos/2)) {
he->words = (short *)zalloc(chwordpos * sizeof(short));
memcpy(he->words, chwords, chwordpos * sizeof(short));
}
if (!(newflags & HIST_TMPSTORE))
addhistnode(histtab, he->text, he);
}
zfree(chline, hlinesz);
zfree(chwords, chwordlen*sizeof(short));
chline = NULL;
histactive = 0;
if (isset(SHAREHISTORY)? histfileIsLocked() : isset(INCAPPENDHISTORY))
savehistfile(hf, 0, HFILE_USE_OPTIONS | HFILE_FAST);
unlockhistfile(hf); /* It's OK to call this even if we aren't locked */
unqueue_signals();
return !(flag & HISTFLAG_NOEXEC || errflag);
}
/* Gives current expansion word if not last word before chwordpos. */
/**/
int hwgetword = -1;
/* begin a word */
/**/
void
ihwbegin(int offset)
{
if (stophist == 2)
return;
if (chwordpos%2)
chwordpos--; /* make sure we're on a word start, not end */
/* If we're expanding an alias, we should overwrite the expansion
* in the history.
*/
if ((inbufflags & INP_ALIAS) && !(inbufflags & INP_HIST))
hwgetword = chwordpos;
else
hwgetword = -1;
chwords[chwordpos++] = hptr - chline + offset;
}
/* add a word to the history List */
/**/
void
ihwend(void)
{
if (stophist == 2)
return;
if (chwordpos%2 && chline) {
/* end of word reached and we've already begun a word */
if (hptr > chline + chwords[chwordpos-1]) {
chwords[chwordpos++] = hptr - chline;
if (chwordpos >= chwordlen) {
chwords = (short *) realloc(chwords,
(chwordlen += 32) *
sizeof(short));
}
if (hwgetword > -1) {
/* We want to reuse the current word position */
chwordpos = hwgetword;
/* Start from where previous word ended, if possible */
hptr = chline + chwords[chwordpos ? chwordpos - 1 : 0];
}
} else {
/* scrub that last word, it doesn't exist */
chwordpos--;
}
}
}
/* Go back to immediately after the last word, skipping space. */
/**/
void
histbackword(void)
{
if (!(chwordpos%2) && chwordpos)
hptr = chline + chwords[chwordpos-1];
}
/* Get the start and end point of the current history word */
/**/
static void
hwget(char **startptr)
{
int pos = hwgetword > -1 ? hwgetword : chwordpos - 2;
#ifdef DEBUG
/* debugging only */
if (hwgetword == -1 && !chwordpos) {
/* no words available */
DPUTS(1, "BUG: hwget() called with no words");
*startptr = "";
return;
}
else if (hwgetword == -1 && chwordpos%2) {
DPUTS(1, "BUG: hwget() called in middle of word");
*startptr = "";
return;
}
#endif
*startptr = chline + chwords[pos];
chline[chwords[++pos]] = '\0';
}
/* Replace the current history word with rep, if different */
/**/
void
hwrep(char *rep)
{
char *start;
hwget(&start);
if (!strcmp(rep, start))
return;
hptr = start;
chwordpos = (hwgetword > -1) ? hwgetword : chwordpos - 2;
hwbegin(0);
qbang = 1;
while (*rep)
hwaddc(*rep++);
hwend();
}
/* Get the entire current line, deleting it in the history. */
/**/
mod_export char *
hgetline(void)
{
/* Currently only used by pushlineoredit().
* It's necessary to prevent that from getting too pally with
* the history code.
*/
char *ret;
if (!chline || hptr == chline)
return NULL;
*hptr = '\0';
ret = dupstring(chline);
/* reset line */
hptr = chline;
chwordpos = 0;
hwgetword = -1;
return ret;
}
/* get an argument specification */
/**/
static int
getargspec(int argc, int marg, int evset)
{
int c, ret = -1;
if ((c = ingetc()) == '0')
return 0;
if (idigit(c)) {
ret = 0;
while (idigit(c)) {
ret = ret * 10 + c - '0';
c = ingetc();
}
inungetc(c);
} else if (c == '^')
ret = 1;
else if (c == '$')
ret = argc;
else if (c == '%') {
if (evset) {
herrflush();
zerr("Ambiguous history reference", NULL, 0);
return -2;
}
if (marg == -1) {
herrflush();
zerr("%% with no previous word matched", NULL, 0);
return -2;
}
ret = marg;
} else
inungetc(c);
return ret;
}
/* do ?foo? search */
/**/
static zlong
hconsearch(char *str, int *marg)
{
int t1 = 0;
char *s;
Histent he;
for (he = up_histent(hist_ring); he; he = up_histent(he)) {
if (he->flags & HIST_FOREIGN)
continue;
if ((s = strstr(he->text, str))) {
int pos = s - he->text;
while (t1 < he->nwords && he->words[2*t1] <= pos)
t1++;
*marg = t1 - 1;
return he->histnum;
}
}
return -1;
}
/* do !foo search */
/**/
zlong
hcomsearch(char *str)
{
Histent he;
int len = strlen(str);
for (he = up_histent(hist_ring); he; he = up_histent(he)) {
if (he->flags & HIST_FOREIGN)
continue;
if (strncmp(he->text, str, len) == 0)
return he->histnum;
}
return -1;
}
/* various utilities for : modifiers */
/**/
int
remtpath(char **junkptr)
{
char *str = strend(*junkptr);
/* ignore trailing slashes */
while (str >= *junkptr && IS_DIRSEP(*str))
--str;
/* skip filename */
while (str >= *junkptr && !IS_DIRSEP(*str))
--str;
if (str < *junkptr) {
if (IS_DIRSEP(**junkptr))
*junkptr = dupstring ("/");
else
*junkptr = dupstring (".");
return 0;
}
/* repeated slashes are considered like a single slash */
while (str > *junkptr && IS_DIRSEP(str[-1]))
--str;
/* never erase the root slash */
if (str == *junkptr) {
++str;
/* Leading doubled slashes (`//') have a special meaning on cygwin
and some old flavor of UNIX, so we do not assimilate them to
a single slash. However a greater number is ok to squeeze. */
if (IS_DIRSEP(*str) && !IS_DIRSEP(str[1]))
++str;
}
*str = '\0';
return 1;
}
/**/
int
remtext(char **junkptr)
{
char *str;
for (str = strend(*junkptr); str >= *junkptr && !IS_DIRSEP(*str); --str)
if (*str == '.') {
*str = '\0';
return 1;
}
return 0;
}
/**/
int
rembutext(char **junkptr)
{
char *str;
for (str = strend(*junkptr); str >= *junkptr && !IS_DIRSEP(*str); --str)
if (*str == '.') {
*junkptr = dupstring(str + 1); /* .xx or xx? */
return 1;
}
/* no extension */
*junkptr = dupstring ("");
return 0;
}
/**/
mod_export int
remlpaths(char **junkptr)
{
char *str = strend(*junkptr);
if (IS_DIRSEP(*str)) {
/* remove trailing slashes */
while (str >= *junkptr && IS_DIRSEP(*str))
--str;
str[1] = '\0';
}
for (; str >= *junkptr; --str)
if (IS_DIRSEP(*str)) {
*str = '\0';
*junkptr = dupstring(str + 1);
return 1;
}
return 0;
}
/**/
int
makeuppercase(char **junkptr)
{
char *str = *junkptr;
for (; *str; str++)
*str = tuupper(*str);
return 1;
}
/**/
int
makelowercase(char **junkptr)
{
char *str = *junkptr;
for (; *str; str++)
*str = tulower(*str);
return 1;
}
/**/
int
makecapitals(char **junkptr)
{
char *str = *junkptr;
for (; *str;) {
for (; *str && !ialnum(*str); str++);
if (*str)
*str = tuupper(*str), str++;
for (; *str && ialnum(*str); str++)
*str = tulower(*str);
}
return 1;
}
/**/
void
subst(char **strptr, char *in, char *out, int gbal)
{
char *str = *strptr, *instr = *strptr, *substcut, *sptr, *oldstr;
int off, inlen, outlen;
if (!*in)
in = str, gbal = 0;
if (!(substcut = (char *)strstr(str, in)))
return;
inlen = strlen(in);
sptr = convamps(out, in, inlen);
outlen = strlen(sptr);
do {
*substcut = '\0';
off = substcut - *strptr + outlen;
substcut += inlen;
*strptr = tricat(oldstr = *strptr, sptr, substcut);
if (oldstr != instr)
zsfree(oldstr);
str = (char *)*strptr + off;
} while (gbal && (substcut = (char *)strstr(str, in)));
}
/**/
static char *
convamps(char *out, char *in, int inlen)
{
char *ptr, *ret, *pp;
int slen, sdup = 0;
for (ptr = out, slen = 0; *ptr; ptr++, slen++)
if (*ptr == '\\')
ptr++, sdup = 1;
else if (*ptr == '&')
slen += inlen - 1, sdup = 1;
if (!sdup)
return out;
ret = pp = (char *) zhalloc(slen + 1);
for (ptr = out; *ptr; ptr++)
if (*ptr == '\\')
*pp++ = *++ptr;
else if (*ptr == '&') {
strcpy(pp, in);
pp += inlen;
} else
*pp++ = *ptr;
*pp = '\0';
return ret;
}
/**/
mod_export void
checkcurline(Histent he)
{
if (he->histnum == curhist && (histactive & HA_ACTIVE)) {
curline.text = chline;
curline.nwords = chwordpos/2;
curline.words = chwords;
}
}
/**/
mod_export Histent
quietgethist(int ev)
{
return gethistent(ev, GETHIST_EXACT);
}
/**/
static Histent
gethist(int ev)
{
Histent ret;
ret = quietgethist(ev);
if (!ret) {
herrflush();
zerr("no such event: %d", NULL, ev);
}
return ret;
}
/**/
static char *
getargs(Histent elist, int arg1, int arg2)
{
short *words = elist->words;
int pos1, nwords = elist->nwords;
if (arg2 < arg1 || arg1 >= nwords || arg2 >= nwords) {
/* remember, argN is indexed from 0, nwords is total no. of words */
herrflush();
zerr("no such word in event", NULL, 0);
return NULL;
}
pos1 = words[2*arg1];
return dupstrpfx(elist->text + pos1, words[2*arg2+1] - pos1);
}
/**/
void
upcase(char **x)
{
char *pp = *(char **)x;
for (; *pp; pp++)
*pp = tuupper(*pp);
}
/**/
void
downcase(char **x)
{
char *pp = *(char **)x;
for (; *pp; pp++)
*pp = tulower(*pp);
}
/**/
int
quote(char **tr)
{
char *ptr, *rptr, **str = (char **)tr;
int len = 3;
int inquotes = 0;
for (ptr = *str; *ptr; ptr++, len++)
if (*ptr == '\'') {
len += 3;
if (!inquotes)
inquotes = 1;
else
inquotes = 0;
} else if (inblank(*ptr) && !inquotes && ptr[-1] != '\\')
len += 2;
ptr = *str;
*str = rptr = (char *) zhalloc(len);
*rptr++ = '\'';
for (; *ptr; ptr++)
if (*ptr == '\'') {
if (!inquotes)
inquotes = 1;
else
inquotes = 0;
*rptr++ = '\'';
*rptr++ = '\\';
*rptr++ = '\'';
*rptr++ = '\'';
} else if (inblank(*ptr) && !inquotes && ptr[-1] != '\\') {
*rptr++ = '\'';
*rptr++ = *ptr;
*rptr++ = '\'';
} else
*rptr++ = *ptr;
*rptr++ = '\'';
*rptr++ = 0;
str[1] = NULL;
return 0;
}
/**/
static int
quotebreak(char **tr)
{
char *ptr, *rptr, **str = (char **)tr;
int len = 3;
for (ptr = *str; *ptr; ptr++, len++)
if (*ptr == '\'')
len += 3;
else if (inblank(*ptr))
len += 2;
ptr = *str;
*str = rptr = (char *) zhalloc(len);
*rptr++ = '\'';
for (; *ptr;)
if (*ptr == '\'') {
*rptr++ = '\'';
*rptr++ = '\\';
*rptr++ = '\'';
*rptr++ = '\'';
ptr++;
} else if (inblank(*ptr)) {
*rptr++ = '\'';
*rptr++ = *ptr++;
*rptr++ = '\'';
} else
*rptr++ = *ptr++;
*rptr++ = '\'';
*rptr++ = '\0';
return 0;
}
/* read an arbitrary amount of data into a buffer until stop is found */
#if 0 /**/
char *
hdynread(int stop)
{
int bsiz = 256, ct = 0, c;
char *buf = (char *)zalloc(bsiz), *ptr;
ptr = buf;
while ((c = ingetc()) != stop && c != '\n' && !lexstop) {
if (c == '\\')
c = ingetc();
*ptr++ = c;
if (++ct == bsiz) {
buf = realloc(buf, bsiz *= 2);
ptr = buf + ct;
}
}
*ptr = 0;
if (c == '\n') {
inungetc('\n');
zerr("delimiter expected", NULL, 0);
zfree(buf, bsiz);
return NULL;
}
return buf;
}
#endif
/**/
static char *
hdynread2(int stop)
{
int bsiz = 256, ct = 0, c;
char *buf = (char *)zalloc(bsiz), *ptr;
ptr = buf;
while ((c = ingetc()) != stop && c != '\n' && !lexstop) {
if (c == '\n') {
inungetc(c);
break;
}
if (c == '\\')
c = ingetc();
*ptr++ = c;
if (++ct == bsiz) {
buf = realloc(buf, bsiz *= 2);
ptr = buf + ct;
}
}
*ptr = 0;
if (c == '\n')
inungetc('\n');
return buf;
}
/**/
void
inithist(void)
{
createhisttable();
}
/**/
void
resizehistents(void)
{
if (histlinect > histsiz) {
/* The reason we don't just call freehistnode(hist_ring->down) is
* so that we can honor the HISTEXPIREDUPSFIRST setting. */
putoldhistentryontop(0);
freehistnode((HashNode)hist_ring);
while (histlinect > histsiz) {
putoldhistentryontop(1);
freehistnode((HashNode)hist_ring);
}
}
}
/* Remember the last line in the history file so we can find it again. */
static struct histfile_stats {
char *text;
time_t stim, mtim;
off_t fpos, fsiz;
zlong next_write_ev;
} lasthist;
static struct histsave {
struct histfile_stats lasthist;
char *histfile;
HashTable histtab;
Histent hist_ring;
zlong curhist;
zlong histlinect;
zlong histsiz;
zlong savehistsiz;
int locallevel;
} *histsave_stack;
static int histsave_stack_size = 0;
static int histsave_stack_pos = 0;
static zlong histfile_linect;
static int
readhistline(int start, char **bufp, int *bufsiz, FILE *in)
{
char *buf = *bufp;
if (fgets(buf + start, *bufsiz - start, in)) {
int len = start + strlen(buf + start);
if (len == start)
return -1;
if (buf[len - 1] != '\n') {
if (!feof(in)) {
if (len < (*bufsiz) - 1)
return -1;
*bufp = zrealloc(buf, 2 * (*bufsiz));
*bufsiz = 2 * (*bufsiz);
return readhistline(len, bufp, bufsiz, in);
}
}
else {
buf[len - 1] = '\0';
if (len > 1 && buf[len - 2] == '\\') {
buf[--len - 1] = '\n';
if (!feof(in))
return readhistline(len, bufp, bufsiz, in);
}
}
return len;
}
return 0;
}
/**/
void
readhistfile(char *fn, int err, int readflags)
{
char *buf, *start = NULL;
FILE *in;
Histent he;
time_t stim, ftim, tim = time(NULL);
off_t fpos;
short *wordlist;
struct stat sb;
int nwordpos, nwordlist, bufsiz;
int searching, newflags, l;
if (!fn && !(fn = getsparam("HISTFILE")))
return;
if (readflags & HFILE_FAST) {
if (stat(unmeta(fn), &sb) < 0
|| (lasthist.fsiz == sb.st_size && lasthist.mtim == sb.st_mtime)
|| !lockhistfile(fn, 0))
return;
lasthist.fsiz = sb.st_size;
lasthist.mtim = sb.st_mtime;
}
else if (!lockhistfile(fn, 1))
return;
if ((in = fopen(unmeta(fn), "r"))) {
nwordlist = 64;
wordlist = (short *)zalloc(nwordlist*sizeof(short));
bufsiz = 1024;
buf = zalloc(bufsiz);
if (readflags & HFILE_FAST && lasthist.text) {
if (lasthist.fpos < lasthist.fsiz) {
fseek(in, lasthist.fpos, 0);
searching = 1;
}
else {
histfile_linect = 0;
searching = -1;
}
} else
searching = 0;
newflags = HIST_OLD | HIST_READ;
if (readflags & HFILE_FAST)
newflags |= HIST_FOREIGN;
if (readflags & HFILE_SKIPOLD
|| (hist_ignore_all_dups && newflags & hist_skip_flags))
newflags |= HIST_MAKEUNIQUE;
while (fpos = ftell(in), (l = readhistline(0, &buf, &bufsiz, in))) {
char *pt = buf;
if (l < 0) {
zerr("corrupt history file %s", fn, 0);
break;
}
if (*pt == ':') {
pt++;
stim = zstrtol(pt, NULL, 0);
for (; *pt != ':' && *pt; pt++);
if (*pt) {
pt++;
ftim = zstrtol(pt, NULL, 0);
for (; *pt != ';' && *pt; pt++);
if (*pt)
pt++;
} else
ftim = stim;
} else {
if (*pt == '\\' && pt[1] == ':')
pt++;
stim = ftim = 0;
}
if (searching) {
if (searching > 0) {
if (stim == lasthist.stim
&& histstrcmp(pt, lasthist.text) == 0)
searching = 0;
else {
fseek(in, 0, 0);
histfile_linect = 0;
searching = -1;
}
continue;
}
else if (stim < lasthist.stim) {
histfile_linect++;
continue;
}
searching = 0;
}
if (readflags & HFILE_USE_OPTIONS) {
histfile_linect++;
lasthist.fpos = fpos;
lasthist.stim = stim;
}
he = prepnexthistent();
he->text = ztrdup(pt);
he->flags = newflags;
if ((he->stim = stim) == 0)
he->stim = he->ftim = tim;
else if (ftim < stim)
he->ftim = stim + ftim;
else
he->ftim = ftim;
/* Divide up the words. We don't know how it lexes,
so just look for white-space.
*/
nwordpos = 0;
start = pt;
do {
while (inblank(*pt))
pt++;
if (*pt) {
if (nwordpos >= nwordlist)
wordlist = (short *) realloc(wordlist,
(nwordlist += 64)*sizeof(short));
wordlist[nwordpos++] = pt - start;
while (*pt && !inblank(*pt))
pt++;
wordlist[nwordpos++] = pt - start;
}
} while (*pt);
he->nwords = nwordpos/2;
if (he->nwords) {
he->words = (short *)zalloc(nwordpos*sizeof(short));
memcpy(he->words, wordlist, nwordpos*sizeof(short));
} else
he->words = (short *)NULL;
addhistnode(histtab, he->text, he);
if (he->flags & HIST_DUP) {
freehistnode((HashNode)he);
curhist--;
}
}
if (start && readflags & HFILE_USE_OPTIONS) {
zsfree(lasthist.text);
lasthist.text = ztrdup(start);
}
zfree(wordlist, nwordlist*sizeof(short));
zfree(buf, bufsiz);
fclose(in);
} else if (err)
zerr("can't read history file %s", fn, 0);
unlockhistfile(fn);
}
/**/
void
savehistfile(char *fn, int err, int writeflags)
{
char *t, *start = NULL;
FILE *out;
Histent he;
zlong xcurhist = curhist - !!(histactive & HA_ACTIVE);
int extended_history = isset(EXTENDEDHISTORY);
if (!interact || savehistsiz <= 0 || !hist_ring
|| (!fn && !(fn = getsparam("HISTFILE"))))
return;
if (writeflags & HFILE_FAST) {
he = gethistent(lasthist.next_write_ev, GETHIST_DOWNWARD);
while (he && he->flags & HIST_OLD) {
lasthist.next_write_ev = he->histnum + 1;
he = down_histent(he);
}
if (!he || !lockhistfile(fn, 0))
return;
if (histfile_linect > savehistsiz + savehistsiz / 5)
writeflags &= ~HFILE_FAST;
}
else {
if (!lockhistfile(fn, 1))
return;
he = hist_ring->down;
}
if (writeflags & HFILE_USE_OPTIONS) {
if (isset(APPENDHISTORY) || isset(INCAPPENDHISTORY)
|| isset(SHAREHISTORY))
writeflags |= HFILE_APPEND | HFILE_SKIPOLD;
else
histfile_linect = 0;
if (isset(HISTSAVENODUPS))
writeflags |= HFILE_SKIPDUPS;
if (isset(SHAREHISTORY))
extended_history = 1;
}
if (writeflags & HFILE_APPEND) {
out = fdopen(open(unmeta(fn),
O_CREAT | O_WRONLY | O_APPEND | O_NOCTTY, 0600), "a");
}
else {
out = fdopen(open(unmeta(fn),
O_CREAT | O_WRONLY | O_TRUNC | O_NOCTTY, 0600), "w");
}
if (out) {
for (; he && he->histnum <= xcurhist; he = down_histent(he)) {
if ((writeflags & HFILE_SKIPDUPS && he->flags & HIST_DUP)
|| (writeflags & HFILE_SKIPFOREIGN && he->flags & HIST_FOREIGN)
|| he->flags & HIST_TMPSTORE)
continue;
if (writeflags & HFILE_SKIPOLD) {
if (he->flags & HIST_OLD)
continue;
he->flags |= HIST_OLD;
if (writeflags & HFILE_USE_OPTIONS)
lasthist.next_write_ev = he->histnum + 1;
}
if (writeflags & HFILE_USE_OPTIONS) {
lasthist.fpos = ftell(out);
lasthist.stim = he->stim;
histfile_linect++;
}
t = start = he->text;
if (extended_history) {
fprintf(out, ": %ld:%ld;", (long)he->stim,
he->ftim? (long)(he->ftim - he->stim) : 0L);
} else if (*t == ':')
fputc('\\', out);
for (; *t; t++) {
if (*t == '\n')
fputc('\\', out);
fputc(*t, out);
}
fputc('\n', out);
}
if (start && writeflags & HFILE_USE_OPTIONS) {
struct stat sb;
fflush(out);
if (fstat(fileno(out), &sb) == 0) {
lasthist.fsiz = sb.st_size;
lasthist.mtim = sb.st_mtime;
}
zsfree(lasthist.text);
lasthist.text = ztrdup(start);
}
fclose(out);
if (writeflags & HFILE_SKIPOLD
&& !(writeflags & (HFILE_FAST | HFILE_NO_REWRITE))) {
int remember_histactive = histactive;
/* Zeroing histactive avoids unnecessary munging of curline. */
histactive = 0;
/* The NULL leaves HISTFILE alone, preserving fn's value. */
pushhiststack(NULL, savehistsiz, savehistsiz, -1);
hist_ignore_all_dups |= isset(HISTSAVENODUPS);
readhistfile(fn, err, 0);
hist_ignore_all_dups = isset(HISTIGNOREALLDUPS);
if (histlinect)
savehistfile(fn, err, 0);
pophiststack();
histactive = remember_histactive;
}
} else if (err)
zerr("can't write history file %s", fn, 0);
unlockhistfile(fn);
}
static int lockhistct;
/**/
int
lockhistfile(char *fn, int keep_trying)
{
int ct = lockhistct;
if (!fn && !(fn = getsparam("HISTFILE")))
return 0;
if (!lockhistct++) {
struct stat sb;
int fd;
char *lockfile;
#ifdef HAVE_LINK
char *tmpfile;
#endif
lockfile = bicat(unmeta(fn), ".LOCK");
#ifdef HAVE_LINK
if ((fd = gettempfile(fn, 0, &tmpfile)) >= 0) {
FILE *out = fdopen(fd, "w");
if (out) {
fprintf(out, "%ld %s\n", (long)getpid(), getsparam("HOST"));
fclose(out);
} else
close(fd);
while (link(tmpfile, lockfile) < 0) {
if (errno != EEXIST || !keep_trying)
;
else if (stat(lockfile, &sb) < 0) {
if (errno == ENOENT)
continue;
}
else {
if (time(NULL) - sb.st_mtime < 10)
sleep(1);
else
unlink(lockfile);
continue;
}
lockhistct--;
break;
}
unlink(tmpfile);
free(tmpfile);
}
#else /* not HAVE_LINK */
while ((fd = open(lockfile, O_WRONLY|O_CREAT|O_EXCL, 0644)) < 0) {
if (errno != EEXIST || !keep_trying)
break;
if (stat(lockfile, &sb) < 0) {
if (errno == ENOENT)
continue;
break;
}
if (time(NULL) - sb.st_mtime < 10)
sleep(1);
else
unlink(lockfile);
}
if (fd < 0)
lockhistct--;
else {
FILE *out = fdopen(fd, "w");
if (out) {
fprintf(out, "%ld %s\n", (long)mypid, getsparam("HOST"));
fclose(out);
} else
close(fd);
}
#endif /* not HAVE_LINK */
free(lockfile);
}
return ct != lockhistct;
}
/* Unlock the history file if this corresponds to the last nested lock
* request. If we don't have the file locked, just return.
*/
/**/
void
unlockhistfile(char *fn)
{
if (!fn && !(fn = getsparam("HISTFILE")))
return;
if (--lockhistct) {
if (lockhistct < 0)
lockhistct = 0;
}
else {
char *lockfile;
fn = unmeta(fn);
lockfile = zalloc(strlen(fn) + 5 + 1);
sprintf(lockfile, "%s.LOCK", fn);
unlink(lockfile);
free(lockfile);
}
}
/**/
int
histfileIsLocked(void)
{
return lockhistct > 0;
}
/* Get the words in the current buffer. Using the lexer. */
/**/
mod_export LinkList
bufferwords(LinkList list, char *buf, int *index)
{
int num = 0, cur = -1, got = 0, ne = noerrs, ocs = cs, oll = ll;
int owb = wb, owe = we, oadx = addedx, ozp = zleparse, onc = nocomments;
int ona = noaliases;
char *p;
if (!list)
list = newlinklist();
zleparse = 1;
addedx = 0;
noerrs = 1;
lexsave();
if (buf) {
int l = strlen(buf);
p = (char *) zhalloc(l + 2);
memcpy(p, buf, l);
p[l] = ' ';
p[l + 1] = '\0';
inpush(p, 0, NULL);
cs = strlen(p) + 1;
nocomments = 1;
} else if (!isfirstln && chline) {
p = (char *) zhalloc(hptr - chline + ll + 2);
memcpy(p, chline, hptr - chline);
memcpy(p + (hptr - chline), line, ll);
p[(hptr - chline) + ll] = ' ';
p[(hptr - chline) + ll + 1] = '\0';
inpush(p, 0, NULL);
cs += hptr - chline;
} else {
p = (char *) zhalloc(ll + 2);
memcpy(p, line, ll);
p[ll] = ' ';
p[ll + 1] = '\0';
inpush(p, 0, NULL);
}
ll = strlen(p);
if (cs)
cs--;
strinbeg(0);
noaliases = 1;
do {
if (incond)
incond = 1 + (tok != DINBRACK && tok != INPAR &&
tok != DBAR && tok != DAMPER &&
tok != BANG);
ctxtlex();
if (tok == ENDINPUT || tok == LEXERR)
break;
if (tokstr && *tokstr) {
untokenize((p = dupstring(tokstr)));
addlinknode(list, p);
num++;
} else if (buf) {
if (IS_REDIROP(tok) && tokfd >= 0) {
char b[20];
sprintf(b, "%d%s", tokfd, tokstrings[tok]);
addlinknode(list, dupstring(b));
num++;
} else if (tok != NEWLIN) {
addlinknode(list, dupstring(tokstrings[tok]));
num++;
}
}
if (!got && !zleparse) {
got = 1;
cur = num - 1;
}
} while (tok != ENDINPUT && tok != LEXERR);
if (buf && tok == LEXERR && tokstr && *tokstr) {
int plen;
untokenize((p = dupstring(tokstr)));
plen = strlen(p);
/*
* Strip the space we added for lexing but which won't have
* been swallowed by the lexer because we aborted early.
* The test is paranoia.
*/
if (plen && p[plen-1] == ' ' && (plen == 1 || p[plen-2] != Meta))
p[plen - 1] = '\0';
addlinknode(list, p);
num++;
}
if (cur < 0 && num)
cur = num - 1;
noaliases = ona;
strinend();
inpop();
errflag = 0;
zleparse = ozp;
nocomments = onc;
noerrs = ne;
lexrestore();
cs = ocs;
ll = oll;
wb = owb;
we = owe;
addedx = oadx;
if (index)
*index = cur;
return list;
}
/* Move the current history list out of the way and prepare a fresh history
* list using hf for HISTFILE, hs for HISTSIZE, and shs for SAVEHIST. If
* the hf value is an empty string, HISTFILE will be unset from the new
* environment; if it is NULL, HISTFILE will not be changed, not even by the
* pop function (this functionality is used internally to rewrite the current
* history file without affecting pointers into the environment).
*/
/**/
int
pushhiststack(char *hf, zlong hs, zlong shs, int level)
{
struct histsave *h;
int curline_in_ring = (histactive & HA_ACTIVE) && hist_ring == &curline;
if (histsave_stack_pos == histsave_stack_size) {
histsave_stack_size += 5;
histsave_stack = zrealloc(histsave_stack,
histsave_stack_size * sizeof (struct histsave));
}
if (curline_in_ring)
unlinkcurline();
h = &histsave_stack[histsave_stack_pos++];
h->lasthist = lasthist;
if (hf) {
if ((h->histfile = getsparam("HISTFILE")) != NULL && *h->histfile)
h->histfile = ztrdup(h->histfile);
else
h->histfile = "";
} else
h->histfile = NULL;
h->histtab = histtab;
h->hist_ring = hist_ring;
h->curhist = curhist;
h->histlinect = histlinect;
h->histsiz = histsiz;
h->savehistsiz = savehistsiz;
h->locallevel = level;
memset(&lasthist, 0, sizeof lasthist);
if (hf) {
if (*hf)
setsparam("HISTFILE", ztrdup(hf));
else
unsetparam("HISTFILE");
}
hist_ring = NULL;
curhist = histlinect = 0;
histsiz = hs;
savehistsiz = shs;
inithist(); /* sets histtab */
if (curline_in_ring)
linkcurline();
return histsave_stack_pos;
}
/**/
int
pophiststack(void)
{
struct histsave *h;
int curline_in_ring = (histactive & HA_ACTIVE) && hist_ring == &curline;
if (histsave_stack_pos == 0)
return 0;
if (curline_in_ring)
unlinkcurline();
deletehashtable(histtab);
zsfree(lasthist.text);
h = &histsave_stack[--histsave_stack_pos];
lasthist = h->lasthist;
if (h->histfile) {
if (*h->histfile)
setsparam("HISTFILE", h->histfile);
else
unsetparam("HISTFILE");
}
histtab = h->histtab;
hist_ring = h->hist_ring;
curhist = h->curhist;
histlinect = h->histlinect;
histsiz = h->histsiz;
savehistsiz = h->savehistsiz;
if (curline_in_ring)
linkcurline();
return histsave_stack_pos + 1;
}
/* If pop_through > 0, pop all array items >= the 1-relative index value.
* If pop_through <= 0, pop (-1)*pop_through levels off the stack.
* If the (new) top of stack is from a higher locallevel, auto-pop until
* it is not.
*/
/**/
int
saveandpophiststack(int pop_through, int writeflags)
{
if (pop_through <= 0) {
pop_through += histsave_stack_pos + 1;
if (pop_through <= 0)
pop_through = 1;
}
while (pop_through > 1
&& histsave_stack[pop_through-2].locallevel > locallevel)
pop_through--;
if (histsave_stack_pos < pop_through)
return 0;
do {
if (!nohistsave)
savehistfile(NULL, 1, writeflags);
pophiststack();
} while (histsave_stack_pos >= pop_through);
return 1;
}