/***********************************************************************
* *
* This software is part of the ast package *
* Copyright (c) 1982-2011 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
/*
* History file manipulation routines
*
* David Korn
* AT&T Labs
*
*/
/*
* Each command in the history file starts on an even byte is null terminated.
* The first byte must contain the special character HIST_UNDO and the second
* byte is the version number. The sequence HIST_UNDO 0, following a command,
* nullifies the previous command. A six byte sequence starting with
* HIST_CMDNO is used to store the command number so that it is not necessary
* to read the file from beginning to end to get to the last block of
* commands. This format of this sequence is different in version 1
* then in version 0. Version 1 allows commands to use the full 8 bit
* character set. It can understand version 0 format files.
*/
#if SHOPT_AUDIT
char *tty; \
int auditmask;
#else
# define _HIST_AUDIT
#endif
#define _HIST_PRIVATE \
void *histshell; \
int histflush; /* set if flushed outside of hflush() */\
int histmask; /* power of two mask for histcnt */ \
int histwfail; \
#include <ast.h>
#include <sfio.h>
#include <error.h>
#include <ls.h>
#if KSHELL
# include "defs.h"
# include "variables.h"
# include "path.h"
# include "builtins.h"
# include "io.h"
#else
# include <ctype.h>
#endif /* KSHELL */
#include "history.h"
#if !KSHELL
# define path_relative(s,x) (s,x)
# ifdef __STDC__
# else
# endif /* __STDC__ */
# define sh_translate(x) (x)
char login_sh = 0;
#endif /* KSHELL */
#ifndef O_BINARY
# define O_BINARY 0
#endif /* O_BINARY */
int _Hist = 0;
static void hist_marker(char*,long);
static int hist_check(int);
static int hist_clean(int);
#ifdef SF_BUFCONST
#else
#endif
static int histinit;
#if SHOPT_ACCTFILE
static int acctfd;
static char *logname;
# include <pwd.h>
{
return(0);
{
if(userinfo)
else
cp = "unknown";
}
(unsigned)acctfd < 10)
{
int n;
{
acctfd = n;
}
}
if(acctfd < 0)
{
acctfd = 0;
return(0);
}
if(sh_isdevfd(acctfile))
{
}
else
return(1);
}
#endif /* SHOPT_ACCTFILE */
#if SHOPT_AUDIT
{
return(0);
goto done;
n--;
logbuf[n] = 0;
goto done;
*cp = 0;
do
{
cp++;
if(*last=='-')
r |= 1;
r |= 2;
}
done:
return(r);
}
#endif /*SHOPT_AUDIT*/
{
}
/*
* open the history file
* if HISTNAME is not given and userid==0 then no history file.
* if login_sh and HISTFILE is longer than HIST_MAX bytes then it is
* cleaned up.
* hist_open() returns 1, if history file is open
*/
{
register int fd;
register char *histname;
char *fname=0;
register char *cp;
return(1);
{
stakputc(0);
}
#ifdef future
{
/* reuse history file if same name */
wasopen = 0;
return(1);
else
hist_free();
}
#endif
if(!histinit)
{
}
if((unsigned)fd <=2)
{
int n;
{
fd=n;
}
}
/* make sure that file has history file format */
{
hsize = 0;
goto retry;
fd = -1;
}
if(fd < 0)
{
#if KSHELL
/* don't allow root a history_file in /tmp */
#endif /* KSHELL */
{
return(0);
}
}
if(fd<0)
return(0);
/* set the file to close-on-exec */
else
{
return(0);
}
if(hsize==0)
{
/* put special characters at front of file */
}
/* initialize history list */
else
{
hist_start = 1;
while(first > hist_start)
{
}
if(!histinit)
{
}
histinit = 0;
}
if(fname)
{
}
{
#ifdef DEBUG
#endif /* DEBUG */
}
#if KSHELL
#endif /* KSHELL */
#if SHOPT_ACCTFILE
if(sh_isstate(SH_INTERACTIVE))
#endif /* SHOPT_ACCTFILE */
#if SHOPT_AUDIT
{
if(sh_isstate(SH_INTERACTIVE) && (hp->auditmask=sh_checkaudit(hp,SHOPT_AUDITFILE, buff, sizeof(buff))))
{
{
int n;
{
fd = n;
}
}
if(fd>=0)
{
}
}
}
#endif
return(1);
}
/*
* close the history file and free the space
*/
{
#if SHOPT_AUDIT
{
}
#endif /* SHOPT_AUDIT */
hist_ptr = 0;
#if SHOPT_ACCTFILE
if(acctfd)
{
acctfd = 0;
}
#endif /* SHOPT_ACCTFILE */
}
/*
* check history file format to see if it begins with special byte
*/
{
return(1);
return(0);
}
/*
* clean out history file OK if not modified in HIST_RECENT seconds
*/
{
}
/*
* Copy the last <n> commands to a new file and make this the history file
*/
{
register char *cp;
{
/* The unlink can fail on windows 95 */
int fd;
{
*last = 0;
*last = '/';
}
else
tmpname = 0;
}
hist_ptr = 0;
{
histinit = 1;
}
{
/* use the old history file */
}
if(--n < 0)
n = 0;
while(1)
{
if(!incmd)
{
{
}
break;
}
break;
/* copy to null byte */
incmd = 0;
while(*cp++);
incmd = 1;
else if(*cp==0)
cp++;
}
if(tmpname)
{
}
}
/*
* position history file at size and find next command number
*/
{
goto begin;
/* skip to marker command and return the number */
/* numbering commands occur after a null and begin with HIST_CMDNO */
{
while(1)
{
/* check for marker */
{
incmd = -1;
break;
}
incmd = 0;
while(*cp++);
{
incmd = 1;
break;
}
break;
}
size += n;
if(incmd < 0)
{
{
if(n < size/2)
{
return(n);
}
n=4;
}
if(n >0)
size += n;
incmd = 0;
}
}
return(1);
}
/*
* This routine reads the history file from the present position
* to the end-of-file and puts the information in the in-core
* history table
* Note that HIST_CMDNO is only recognized at the beginning of a command
* and that HIST_UNDO as the first character of a command is skipped
* unless it is followed by 0. If followed by 0 then it cancels
* the previous command.
*/
{
register int incmd = 0;
{
last = -1;
}
{
while(1)
{
while(!incmd)
{
{
#ifdef future
{
if(histinit)
{
histinit = 0;
return;
}
}
else if(n>=histinit)
#endif
}
switch(*((unsigned char*)(cp++)))
{
case HIST_CMDNO:
if(*cp==0)
{
{
}
}
break;
case HIST_UNDO:
if(*cp==0)
{
cp+=1;
}
break;
default:
cp--;
incmd = 1;
}
{
cp++;
goto refill;
}
}
while(*cp++);
break;
incmd = 0;
while(*cp==0)
{
goto refill;
}
}
}
{
count = 2;
skip = 0;
if(last<0)
{
if(fd>=0)
{
}
}
last = 0;
goto again;
}
}
/*
* This routine will cause the previous command to be cancelled
*/
{
register int c;
if(!hp)
return;
}
/*
* flush the current history command
*/
{
register char *buff;
if(hp)
{
{
}
else
{
hist_close(hp);
}
}
}
/*
* This is the write discipline for the history file
* When called from hist_flush(), trailing newlines are deleted and
* a zero byte. Line sequencing is added as required
*/
#ifdef SF_BUFCONST
#else
#endif
{
int saved=0;
{
return(-1);
}
/* remove whitespace from end of commands */
{
c= *bufptr;
if(!isspace(c))
{
bufptr++;
break;
}
}
/* don't count empty lines */
return(insize);
*bufptr++ = '\n';
*bufptr++ = 0;
#if SHOPT_AUDIT
{
sfprintf(hp->auditfp,"%u;%u;%s;%*s%c",sh_isoption(SH_PRIVILEGED)?shgd->euserid:shgd->userid,t,hp->tty,size,buff,0);
}
#endif /* SHOPT_AUDIT */
#if SHOPT_ACCTFILE
if(acctfd)
{
}
#endif /* SHOPT_ACCTFILE */
if(size&01)
{
size++;
*bufptr++ = 0;
}
{
saved=1;
size += HIST_MARKSZ;
}
errno = 0;
if(saved)
if(size>=0)
{
return(insize);
}
return(-1);
}
/*
* Put history sequence number <n> into buffer <buff>
* The buffer must be large enough to hold HIST_MARKSZ chars
*/
{
*buff++ = HIST_CMDNO;
*buff++ = 0;
*buff++ = 0;
}
/*
* return byte offset in history file for command <n>
*/
{
}
/*
* seek to the position of command <n>
*/
{
}
/*
* write the command starting at offset <offset> onto file <outfile>.
* if character <last> appears before newline it is deleted
* each new-line character is replaced with string <nl>.
*/
{
register int oldc=0;
register int c;
{
return;
}
{
if(c && oldc=='\n')
return;
else if(oldc)
oldc = c;
if(c==0)
return;
}
return;
}
/*
* find index for last line with given string
* If flag==0 then line must begin with string
* direction < 1 for backwards search
*/
{
register int index2;
int *coffset=0;
if(!hp)
return(location);
/* leading ^ means beginning of line unless escaped */
if(flag)
{
if(index2=='\\')
string++;
else if(index2=='^')
{
flag=0;
string++;
}
}
if(flag)
if(direction<0)
{
if(index2<1)
index2 = 1;
return(location);
}
return(location);
{
{
return(location);
}
#if KSHELL
/* allow a search to be aborted */
break;
#endif /* KSHELL */
}
return(location);
}
/*
* search for <string> in history file starting at location <offset>
* If coffset==0 then line must begin with string
* returns the line number of the match if successful, otherwise -1
*/
{
#if SHOPT_MULTIBYTE
mbinit();
#endif /* SHOPT_MULTIBYTE */
return(-1);
while(m > n)
{
{
if(coffset)
return(line);
}
if(!coffset)
break;
if(*cp=='\n')
line++;
#if SHOPT_MULTIBYTE
c = 1;
#endif /* SHOPT_MULTIBYTE */
cp += c;
m -= c;
}
return(-1);
}
/*
* copy command <command> from history file to s1
* at most <size> characters copied
* if s1==0 the number of lines for the command is returned
* line=linenumber for emacs copy and only this line of command will be copied
* line < 0 for full command copy
* -1 returned if there is no history file
*/
{
register int c;
register int count = 0;
if(!hp)
return(-1);
{
if(c=='\n')
{
break;
else if(line >= 0)
continue;
}
{
{
*--s1 = 0;
break;
}
*s1++ = c;
}
}
if(s1==0)
return(count);
s1--;
*s1 = '\0';
return(count);
}
/*
* return word number <word> from command number <command>
*/
{
register int c;
register int flag = 0;
if(!hp)
return(NIL(char*));
{
c = isspace(c);
if(c && flag)
{
*cp = 0;
if(--word==0)
break;
flag = 0;
}
else if(c==0 && flag==0)
{
flag++;
}
}
*cp = 0;
return(string);
}
#endif /* SHOPT_ESH */
#if SHOPT_ESH
/*
* given the current command and line number,
* and number of lines back or foward,
* compute the new command and line number.
*/
{
if(!hp)
{
command = -1;
goto done;
}
if(lines > 0)
{
register int count;
{
goto done;
command++;
}
}
else
{
while(1)
{
if(line >=0)
goto done;
break;
}
command = -1;
}
done:
return(next);
}
#endif /* SHOPT_ESH */
/*
* Handle history file exceptions
*/
#ifdef SF_BUFCONST
#else
#endif
{
{
return(0);
/* write failure could be NFS problem, try to re-open */
{
return(-1);
{
}
return(1);
}
return(-1);
}
return(0);
}