* hist.c - history expansion
* This file is part of zsh, the Z shell.
* This software is Copyright 1992 by Paul Falstad
* Permission is hereby granted to copy, reproduce, redistribute or otherwise
* use this software as long as: there is no monetary profit gained
* specifically from the use or reproduction of this software, it is not
* sold, rented, traded or otherwise marketed, and this copyright notice is
* included prominently in any copy made.
* The author make no claims as to the fitness or correctness of this software
* for any use whatsoever, and it is provided as is. Any use of this software
* is at the user's own risk.
static Hp hp_lit
, hp_lex
;
static Histent curhistent
;
/* add a character to the current history word */
if (hlastw
&& hline
&& (!(errflag
|| lexstop
) || c
== HISTSPACE
)) {
if (c
== '!' && unset(NOBANGHIST
)) hwaddc('\\');
if (hptr
-hline
>= hlinesz
) {
int ll
,flag
= 0,oldsiz
= hlinesz
;
if (curhistent
->lex
== hline
) flag
= 1;
hline
= hp_realloc(&hp_lex
,hline
,oldsiz
,hlinesz
= oldsiz
+16);
if (flag
) curhistent
->lex
= hline
;
#define habort() { errflag = lexstop = 1; return ' '; }
/* get a character after performing history substitution */
int c
,ev
,farg
,larg
,argc
,marg
= -1,cflag
= 0,bflag
= 0;
if (stophist
|| alstackind
)
if (isfirstch
&& c
== hatchar
)
if ((c
= hgetch()) == '{')
if (!cflag
&& inblank(c
) || c
== '=' || c
== '(' || lexstop
)
if (c
== '?' || c
== '\n' || lexstop
)
if (c
!= '\n' && !lexstop
)
ev
= hconsearch(hsubl
= ztrdup(buf
),&marg
);
zerr("no such event: %s",buf
,0);
if (inblank(c
) || c
== ';' || c
== ':' || c
== '^' || c
== '$' ||
c
== '*' || c
== '%' || c
== '}' || lexstop
)
if ((idigit(buf
[0]) || buf
[0] == '-') && !idigit(c
)) break;
if (c
== '#' || c
== bangchar
)
ev
= (t0
< 0) ? curhist
+t0
: t0
;
else if (*buf
== bangchar
)
else if ((ev
= hcomsearch(buf
)) == -1)
zerr("event not found: %s",buf
,0);
while (c
!= '\n' && !lexstop
)
if (!(eline
= getevent(defev
= ev
)))
/* extract the relevant arguments */
larg
= farg
= getargspec(argc
,marg
);
larg
= getargspec(argc
,marg
);
if (!(sline
= getargs(eline
,farg
,larg
)))
c
= (cflag
) ? ':' : hgetch();
if ((c
= hgetch()) == 'g')
histdone
= HISTFLAG_DONE
|HISTFLAG_NOEXEC
;
zerr("modifier failed: h",NULL
,0);
zerr("modifier failed: e",NULL
,0);
zerr("modifier failed: r",NULL
,0);
zerr("modifier failed: t",NULL
,0);
subst(&sline
,hsubl
,hsubr
,gbal
);
zerr("no previous substitution with &",NULL
,0);
zerr("illegal modifier: %c",NULL
,c
);
zerr("'}' expected",NULL
,0);
/* stuff the resulting string in the input queue and start over */
alstack
[alstackind
++] = NULL
;
for (ptr
= sline
; *ptr
; ptr
++) {
if (ptr
[0] == '\\' && ptr
[1] == '!') chuck(ptr
);
histdone
|= HISTFLAG_DONE
;
if (isset(HISTVERIFY
)) histdone
|= HISTFLAG_NOEXEC
|HISTFLAG_RECALL
;
/* reset the alias stack for lexrestore () */
ix
= alstack
[--alstackind
];
/* get a character without history expansion */
unsigned char *line
,*pmpt
,*pmpt2
= NULL
;
if ((lastc
= *inbufptr
++) == ALPOP
)
zerr("alias stack underflow",NULL
,0);
ix
= alstack
[--alstackind
];
if (*t
&& t
[strlen(t
)-1] == ' ')
if (interact
&& isset(SHINSTDIN
))
pmpt
= (unsigned char *)putprompt(prompt2
,&plen
);
pmpt
= (unsigned char *)putprompt(prompt
,&plen
);
pmpt2
= (unsigned char *)((rprompt
) ? putprompt(rprompt
,&foo
) : NULL
);
if (!(interact
&& isset(SHINSTDIN
) && SHTTY
!= -1 && isset(USEZLE
))) {
if (interact
&& isset(SHINSTDIN
))
write(2,pmpt
,strlen((char *) pmpt
));
line
= (unsigned char *)fgets(lbuf
= zalloc(256),256,bshin
);
line
= zleread(pmpt
,pmpt2
,plen
);
if (interact
&& isset(SHINSTDIN
)) {
char *s
= curhistent
->lit
;
curhistent
->lit
= hp_concat(s
,(char*)line
);
if (isfirstln
) spaceflag
= *line
== ' ';
fputs((char *) line
,stderr
);
if (*line
&& line
[strlen((char *) line
)-1] == '\n')
if (interact
&& isset(SUNKEYBOARDHACK
) && isset(SHINSTDIN
) &&
SHTTY
!= -1 && *line
&& line
[1] &&
line
[strlen((char *) line
)-2] == '`')
for (ct
= 0, ptr
= line
; *ptr
; ptr
++)
/* Read one line of at most n-1 chars from the input queue */
for (l
= 0; l
< n
-1; l
++)
if ((buf
[l
] = hgetch()) == '\n' || lexstop
)
buf
[l
+(lexstop
?0:1)] = 0;
return (!lexstop
|| l
) ? buf
: NULL
;
/* put a string in the input queue */
/* shrink inbuf if it gets too big */
if (!inbufct
&& inbufsz
> 65536)
inbuf
= zalloc(inbufsz
= 256);
inbufptr
= inbuf
+inbufsz
;
if (slen
+inbufct
> inbufsz
)
while (slen
+inbufct
> inbufsz
)
memcpy(x
+inbufsz
-inbufct
,inbufptr
,inbufct
);
inbufptr
= x
+inbufsz
-inbufct
;
memcpy(inbufptr
-= slen
,str
,slen
);
/* unget a char and remove it from hline */
zerr("hungetc attempted at buffer start",NULL
,0);
if (*hptr
== '!' && unset(NOBANGHIST
)) hptr
--;
/* begin reading a string */
/* done reading a string */
/* stuff a whole file into the input queue and print it */
if (!(in
= fopen(fn
,"r")))
zerr("can't open %s",fn
,0);
if (!(fread(buf
,len
,1,in
)))
zerr("read error on %s",fn
,0);
fwrite(buf
,len
,1,stdout
);
/* initialize the history mechanism */
isfirstln
= isfirstch
= 1;
histremmed
= errflag
= histdone
= spaceflag
= 0;
stophist
= isset(NOBANGHIST
) || unset(SHINSTDIN
);
lithist
= isset(HISTLIT
);
hline
= hptr
= hp_alloc(&hp_lex
,hlinesz
= 16);
curhistent
= gethistent(curhist
);
if (!curhistent
->ftim
) curhistent
->ftim
= time(NULL
);
if (interact
&& isset(SHINSTDIN
) && !strin
) {
if (curhist
-histsiz
>= 0) gethistent(curhist
-histsiz
)->lex
= NULL
;
if (curhist
-lithistsiz
>= 0) gethistent(curhist
-lithistsiz
)->lit
= NULL
;
curhistent
= gethistent(curhist
);
hp_purge(hp_lex
,curhist
-histsiz
);
hp_purge(hp_lit
,curhist
-lithistsiz
);
*(curhistent
->lit
= hp_alloc(&hp_lit
,1)) = '\0';
/* say we're done using the history mechanism */
if (!interact
|| strin
|| unset(SHINSTDIN
)) {
hp_free(hp_lex
,hline
,hlinesz
);
if (*s
&& *(t
= s
+strlen(s
)-1) == HISTSPACE
) *t
= '\0';
if (hptr
[-3] == HISTSPACE
) hptr
[-3] = '\0';
he
= gethistent(curhist
-1);
if (!strcmp(hline
,"\n") ||
(isset(HISTIGNOREDUPS
) && he
->lex
&& !strcmp(he
->lex
,hline
)) ||
(isset(HISTIGNORESPACE
) && spaceflag
))
if (flag
& (HISTFLAG_DONE
|HISTFLAG_RECALL
)) {
for (;*p
;p
++) if (*p
== HISTSPACE
) *p
= ' ';
if ((flag
& (HISTFLAG_DONE
|HISTFLAG_RECALL
)) == HISTFLAG_DONE
) {
fprintf(stderr
,"%s\n",ptr
);
if (flag
& HISTFLAG_RECALL
) {
curhistent
->stim
= time(NULL
);
if (hline
&& !curhistent
->lex
) hp_free(hp_lex
,hline
,hlinesz
);
return !(flag
& HISTFLAG_NOEXEC
|| errflag
);
/* remove the current line from the history List */
if (!histremmed
) { histremmed
= 1; curhist
--; }
/* add a word to the history List */
if (!(alstackind
== 1 && !alstack
[0]))
if (alstat
== ALSTAT_JUNK
)
/* get an argument specification */
int getargspec(argc
,marg
) /**/
if ((c
= hgetch()) == '0')
zerr("%% with no previous word matched",NULL
,0);
int hconsearch(str
,marg
) /**/
for (t0
= curhist
-1; hs
= quietgetevent(t0
); t0
--)
if (s
= ztrstr(hs
,str
)) {
while (s
!= hs
) if (*s
-- == HISTSPACE
) t1
++;
for (t0
= curhist
-1; hs
= quietgetevent(t0
); t0
--)
if (!strncmp(hs
,str
,strlen(str
))) return t0
;
/* various utilities for : modifiers */
int remtpath(junkptr
) /**/
char *str
= *junkptr
,*cut
;
if (cut
= strrchr(str
,'/')) {
if (str
!= cut
) *cut
= '\0';
int remtext(junkptr
) /**/
char *str
= *junkptr
,*cut
;
if ((cut
= strrchr(str
,'.')) && cut
!= str
)
int rembutext(junkptr
) /**/
char *str
= *junkptr
,*cut
;
if ((cut
= strrchr(str
,'.')) && cut
!= str
)
*junkptr
= strdup(cut
+1); /* .xx or xx? */
int remlpaths(junkptr
) /**/
char *str
= *junkptr
,*cut
;
if (cut
= strrchr(str
,'/'))
*junkptr
= strdup(cut
+1);
int makeuppercase(junkptr
) /**/
int makelowercase(junkptr
) /**/
void subst(strptr
,in
,out
,gbal
) /**/
char **strptr
;char *in
;char *out
;int gbal
;
char *str
= *strptr
,*cut
,*sptr
;
while (cut
= (char *) ztrstr(str
,in
)) {
off
= cut
-*strptr
+strlen(sptr
);
*strptr
= tricat(*strptr
,sptr
,cut
);
str
= (char *) *strptr
+off
;
char *convamps(out
,in
) /**/
int slen
,inlen
= strlen(in
);
for (ptr
= out
, slen
= 0; *ptr
; ptr
++,slen
++)
ret
= pp
= alloc(slen
+1);
for (ptr
= out
; *ptr
; ptr
++)
char *quietgetevent(ev
) /**/
if (ev
< firsthist()) return NULL
;
return (lithist
) ? ent
->lit
: ent
->lex
;
zerr("no such event: %d",NULL
,ev
);
for (; *list
; list
++) if (*list
== HISTSPACE
) argc
++;
char *getargs(elist
,arg1
,arg2
) /**/
char *elist
;int arg1
;int arg2
;
while (*ret
&& *ret
++ != HISTSPACE
);
zerr("no such word in event",NULL
,0);
retn
= ret
= strdup(ret
);
while (*ret
&& *ret
!= HISTSPACE
)
zerr("no such word in event",NULL
,0);
char *ptr
,*rptr
,**str
= (char **) tr
;
for (ptr
= *str
; *ptr
; ptr
++,len
++)
if (*ptr
== '\'') len
+= 3;
*str
= rptr
= alloc(len
);
*rptr
++ = '\''; *rptr
++ = '\\'; *rptr
++ = '\''; *rptr
++ = '\'';
char *ptr
,*rptr
,**str
= (char **) tr
;
for (ptr
= *str
; *ptr
; ptr
++,len
++)
*str
= rptr
= alloc(len
);
*rptr
++ = '\''; *rptr
++ = '\\'; *rptr
++ = '\''; *rptr
++ = '\'';
} else if (inblank(*ptr
)) {
*rptr
++ = '\''; *rptr
++ = *ptr
++; *rptr
++ = '\'';
else while (lastc
!= '\n' && !lexstop
)
/* read an arbitrary amount of data into a buffer until stop is found */
char *hdynread(stop
) /**/
char *buf
= zalloc(bsiz
),*ptr
;
while ((c
= hgetch()) != stop
&& c
!= '\n' && !lexstop
)
buf
= realloc(buf
,bsiz
*= 2);
zerr("delimiter expected",NULL
,0);
char *hdynread2(stop
) /**/
char *buf
= zalloc(bsiz
),*ptr
;
while ((c
= hgetch()) != stop
&& c
!= '\n' && !lexstop
)
buf
= realloc(buf
,bsiz
*= 2);
hp_lit
= zalloc(sizeof *hp_lit
);
hp_lit
->ptr
= hp_lit
->pool
= zalloc(HEAPSIZE
);
hp_lex
= zalloc(sizeof *hp_lex
);
hp_lex
->ptr
= hp_lex
->pool
= zalloc(HEAPSIZE
);
histentct
= (lithistsiz
> histsiz
) ? lithistsiz
: histsiz
;
histentarr
= zcalloc(histentct
*sizeof *histentarr
);
void resizehistents() /**/
int newentct
,t0
,t1
,firstlit
,firstlex
;
newentct
= (lithistsiz
> histsiz
) ? lithistsiz
: histsiz
;
newarr
= zcalloc(newentct
*sizeof *newarr
);
firstlex
= curhist
-histsiz
+1;
firstlit
= curhist
-lithistsiz
+1;
if (t0
< curhist
-newentct
) t0
= curhist
-newentct
;
for (; t0
<= curhist
; t0
++) {
newarr
[t1
] = *gethistent(t0
);
if (t0
< firstlex
) newarr
[t1
].lex
= NULL
;
if (t0
< firstlit
) newarr
[t1
].lit
= NULL
;
t1
++; if (t1
== newentct
) t1
= 0;
char *hp_alloc(hp
,siz
) /**/
fprintf(stderr
,"new heap (siz = %d, curhist = %d)\n",siz
,curhist
);
h
->free
= (siz
> HEAPSIZE
) ? siz
: HEAPSIZE
;
h
->ptr
= h
->pool
= zalloc(h
->free
);
char *hp_realloc(hp
,ptr
,oldsiz
,newsiz
) /**/
Hp
*hp
; char *ptr
; int oldsiz
; int newsiz
;
int delta
= newsiz
-oldsiz
;
if (h
->ptr
-oldsiz
== ptr
&& h
->free
>= delta
) {
fprintf(stderr
,"realloc copy\n");
memcpy(ret
= hp_alloc(hp
,newsiz
),ptr
,oldsiz
);
void hp_free(h
,ptr
,siz
) /**/
Hp h
; char *ptr
; int siz
;
char *hp_concat(old
,new) /**/
oldlen
= strlen(old
); newlen
= strlen(new);
old
= hp_realloc(&hp_lit
,old
,oldlen
+1,oldlen
+newlen
+1);
void hp_purge(h
,lim
) /**/
while (h
->next
) { hlast
= h
; h
= h
->next
; }
if (h
->histno
<= lim
|| h
->histno
== 0) {
fprintf(stderr
,"purging %d\n",lim
);
void readhistfile(s
,err
) /**/
while (fgets(buf
,1024,in
)) {
while (l
&& buf
[l
-1] == '\n') {
if (l
> 1 && buf
[l
-2] == '\\') {
fgets(buf
+l
-1,1024-(l
-1),in
);
for (;*pt
;pt
++) if (*pt
== ' ') *pt
= HISTSPACE
;
ent
= gethistent(++curhist
);
ent
->lex
= hp_alloc(&hp_lex
,strlen(buf
)+1);
ent
->lit
= hp_alloc(&hp_lit
,strlen(buf
)+1);
ent
->ftim
= ent
->stim
= tim
;
zerr("can't read history file",s
,0);
void savehistfile(s
,err
,app
) /**/
if (!s
|| !interact
) return;
flag
= (app
) ? O_APPEND
: O_TRUNC
;
if (ev
< firsthist()) ev
= firsthist();
if (out
= fdopen(open(s
,O_CREAT
|O_WRONLY
|flag
,0600),"w")) {
for (; ev
<= curhist
; ev
++) {
if (*t
== HISTSPACE
) fputc(' ',out
);
if (*t
== '\n') fputc('\\',out
);
} else if (err
) zerr("can't write history file: %s",s
,0);
ev
= curhist
-histentct
+1;
if ((lithist
) ? ent
->lit
: ent
->lex
) break;