/***********************************************************************
* *
* This software is part of the ast package *
* Copyright (c) 1984-2011 AT&T Intellectual Property *
* and is licensed under the *
* Eclipse Public License, Version 1.0 *
* by AT&T Intellectual Property *
* *
* A copy of the License is available at *
* (with md5 checksum b35adb5213ca9657e911e9befb180842) *
* *
* Information and Software Systems Research *
* AT&T Research *
* Florham Park NJ *
* *
* David Korn <dgk@research.att.com> *
* Pat Sullivan *
* *
***********************************************************************/
/*
* History file manipulation routines
*
* David Korn
* AT&T Bell Laboratories
* Room 3C-526B
* Murray Hill, N. J. 07974
* Tel. x7975
*
*/
/*
* Each command in the history file starts on an even byte is null terminated.
* The first byte must contain the special character H_UNDO and the second
* byte is the version number. The sequence H_UNDO 0, following a command,
* nullifies the previous command. A six byte sequence starting with
* H_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.
*/
#ifdef KSHELL
# include "defs.h"
# include "builtins.h"
#else
# include "io.h"
# include "edit.h"
# include <signal.h>
# include <ctype.h>
#endif /* KSHELL */
#include "history.h"
#ifdef MULTIBYTE
# include "national.h"
#endif /* MULTIBYTE */
#include <tm.h>
#ifndef KSHELL
# define path_relative(x) (x)
static int io_readbuff();
static void io_fclose();
static int io_getc();
static void io_init();
static int io_renumber();
static int io_mktmp();
#endif /* KSHELL */
static int fixmask;
static int noclean;
static void hist_trim();
static int hist_nearend();
static int hist_check();
static int hist_clean();
static void hist_free();
static int hist_version;
/*
* open the history file
* if HISTNAME is not given and userid==0 then no history file.
* if login_sh and HISTFILE is longer than HISTMAX bytes then it is
* cleaned up.
* hist_open() returns 1, if histor file is open
*/
int hist_open()
{
register int fd;
register char *histname;
int maxlines;
register char *cp;
int his_start;
if(hist_ptr)
return(1);
if(!histname)
{
else
}
*fname = 0;
{
/* reuse history file if same name */
wasopen = 0;
return(1);
else
hist_free();
}
{
}
/* make sure that file has history file format */
{
hsize = 0;
goto retry;
fd = -1;
}
if(fd < 0)
{
#ifdef KSHELL
/* don't allow root a history_file in /tmp */
#endif /* KSHELL */
}
if(fd<0)
return(0);
else
{
return(0);
}
if(hsize==0)
/* put special characters at front of file */
{
}
/* initialize history list */
else
{
int first;
hist_eof(); /* this sets fixind to last command */
if(his_start <=0)
his_start = 1;
{
}
hist_eof();
}
if(*fname)
{
#ifdef DEBUG
p_flush();
#endif /* DEBUG */
}
return(1);
}
/*
* close the history file and free the space
*/
static void hist_free()
{
hist_ptr = 0;
}
/*
* check history file format to see if it begins with special byte
*/
register int fd;
{
return(1);
return(0);
}
/*
* Decide whether to clean out the history file
*/
#ifdef YELLOWP
/* NFS systems do not handle file locking correctly */
#endif /* YELLOWP */
#ifdef _FLOCK
# ifndef LOCK_EX
# endif
#endif /*_FLOCK */
#ifdef F_SETLK
# endif
#endif
register int fd;
{
register int r = 0;
if(noclean)
return(0);
#ifdef _FLOCK
#else
# ifdef F_SETLK
# else
# ifdef KSHELL
# endif /* KSHELL */
# endif
#endif
{
/* see if history file was recently accessed */
if(r)
{
r = 0;
}
}
return(r);
}
/*
* Copy the last <n> commands to a new file and make this the history file
*/
static void hist_trim(n)
int n;
{
register int c;
int fdo;
/* move to an available descriptor >= USERIO */
hist_ptr = 0;
if(!hist_open())
{
/* use the old history file */
return;
}
if(n < 0)
n = 0;
htrim++;
do
{
{
p_flush();
}
#ifdef KSHELL
#endif /* KSHELL */
hist_flush();
}
htrim = 0;
hist_cancel();
}
/*
* position history file at size and find next command number
*/
register int fd;
{
register int n = 0;
register int state = 0;
register int c;
if(size <=0)
goto begin;
/* skip to numbered command and return the number */
/* numbering commands occur after a null and begin with H_CMDNO */
{
case 1:
if(c==H_CMDNO)
{
state = 2;
}
break;
case 2:
/* see if H_CMDNO is followed by 0 */
if(hist_version && c)
{
n += 2;
state = 0;
break;
}
n = 0;
case 3:
case 4:
case 5:
if(hist_version)
n = (n<<8) + c;
else if(state<4)
n = (n<<7) + (c&0177);
state++;
break;
case 6:
return(n);
default:
state = (c==0);
n++;
}
return(1);
}
/*
* This routine marks the history file as closed. The file actually
* closes when the program exits, or when you open a history file
* with a different name.
*/
void hist_close()
{
hist_ptr = 0;
}
/*
* 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 H_CMDNO is only recognized at the beginning of a command
* and that H_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.
*/
void hist_eof()
{
register int c;
register int incr = 0;
register int oldc = 0;
int skip = 0;
#ifdef INT16
{
#else
/* could use io_getc() but this is faster */
while(1)
{
{
if(c == EOF)
break;
}
c &= STRIP;
#endif /* INT16 */
count++;
if(skip-- > 0)
{
if(skip==2)
hist_marker = count;
oldc = 0;
continue;
}
if(c == 0)
{
skip = 3;
incr = 0;
}
else if(oldc == 0)
{
if(c == H_CMDNO)
{
/* old format history file */
if(hist_version==0)
skip = 4;
incr = 0;
}
else if(c==H_UNDO)
incr = -1;
}
else
incr = 1;
oldc = c;
}
}
/*
* This routine will cause the previous command to be cancelled
*/
void hist_cancel()
{
register int c;
if(!fp)
return;
p_char(0);
p_flush();
}
/*
* This routine adds one or two null bytes and flushes the history buffer
*/
void hist_flush()
{
register int c;
int flush = 0;
int savcount;
if(!fp)
return;
#ifdef KSHELL
return;
#endif /* KSHELL */
if(htrim)
{
p_char(0);
goto set_count;
}
{
#ifdef DEBUG
p_flush();
#endif /* DEBUG */
hist_free();
noclean++;
if(!htrim)
hist_open();
noclean = 0;
return;
}
/* remove whitespace from end of commands */
{
if(!isspace(c))
{
break;
}
}
/* don't count empty lines */
{
goto set_count;
}
p_char('\n');
p_char(0);
flush++;
/* start each command on an even byte boundary */
{
p_char(0);
flush++;
}
{
/* put line number in file */
p_char(0);
p_char(c);
p_char(c);
p_char(c);
p_char(0);
flush++;
}
{
p_flush();
/* check for write errors, like ulimit */
{
}
}
}
/*
* return byte offset in history file for command <n>
*/
int n;
{
}
/*
* write the command starting at offset <offset> onto file <fd>.
* if character <last> appears before newline it is deleted
* each new-line character is replaced with string <nl>.
*/
int last;
char *nl;
{
register int oldc=0;
register int c;
{
return;
}
{
if(c && oldc=='\n')
return;
else if(c==0 && last)
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
*/
char *string;
register int index1;
int flag;
int direction;
{
register int index2;
if(!fp)
return(location);
/* leading ^ means beginning of line unless escaped */
if(flag)
{
if(index2=='\\')
string++;
else if(index2=='^')
{
flag=0;
string++;
}
}
if(direction<0)
{
if(index2<1)
index2 = 1;
return(location);
}
return(location);
{
{
return(location);
}
#ifdef KSHELL
/* allow a search to be aborted */
break;
#endif /* KSHELL */
}
return(location);
}
/*
* search for <string> in history file starting at location <offset>
* If flag==0 then line must begin with string
* returns the line number of the match if successful, otherwise -1
*/
char *string;
int flag;
{
register char *cp;
register int c;
int line = 0;
#ifdef MULTIBYTE
int nbytes = 0;
#endif /* MULTIBYTE */
do
{
if(offset>=0)
{
}
offset = -1;
{
break;
count++;
#ifdef MULTIBYTE
/* always position at character boundary */
if(--nbytes > 0)
{
{
cp--;
continue;
}
}
else
{
}
#endif /* MULTIBYTE */
if(c == '\n')
line++;
/* save earliest possible matching character */
if(*cp != c )
break;
}
if(*cp==0) /* match found */
return(line);
}
return(-1);
}
/*
* copy command <command> from history file to s1
* at most MAXLINE 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 char *s1;
{
register int c;
register int count = 0;
if(!fp)
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>
*/
char *s1;
int word;
{
register int c;
register int flag = 0;
if(hist_ptr==0)
#ifdef KSHELL
#else
return((char*)0);
#endif /* KSHELL */
{
c = isspace(c);
if(c && flag)
{
*cp = 0;
if(--word==0)
break;
flag = 0;
}
else if(c==0 && flag==0)
{
flag++;
}
}
*cp = 0;
return(s1);
}
#endif /* ESH */
#ifdef ESH
/*
* given the current command and line number,
* and number of lines back or foward,
* compute the new command and line number.
*/
register int command;
register int line;
int lines;
{
if(!hist_ptr)
{
command = -1;
goto done;
}
if(lines > 0)
{
register int count;
{
goto done;
command++;
}
}
else
{
while(1)
{
if(line >=0)
goto done;
break;
}
command = -1;
}
return(next);
done:
return(next);
}
#endif /* ESH */
#ifdef KSHELL
/*
* given a file containing a command and a string of the form old=new,
* execute the command with the string old replaced by new
*/
const char *command;
int fd;
char *replace;
{
register char *sp;
register int c;
char *string;
stakseek(0);
stakputc(c);
*new++ = 0;
hist_flush();
}
#else /* !KSHELL */
/*
* initialize file structure
*/
int fd;
char *buf;
{
if(!fp)
{
}
else
}
/*
* returns the next character from file <fd>
*/
int fd;
{
register int c;
if(!fp)
return(EOF);
return(c&STRIP);
return(0);
return(io_readbuff(fp));
}
/*
* This special version does not handle ptrname==1
* It also saves a lot of real seeks on history file
*/
int fd;
register int ptrname;
{
register int c;
off_t p;
{
p_flush();
}
c = 0;
/* check history file to see if already in the buffer */
{
if(offset >= p)
{
return(offset);
}
else
{
offset -= c;
}
}
{
}
{
if(ptrname==0)
hoffset = p;
else
hoffset = -1;
if(c)
{
}
}
return(p);
}
/*
* Read from file into fp
*/
{
register int n;
return(EOF);
#ifdef SYSCALL
#else
#endif /* SYSCALL */
if(n > 0)
else
if (n <= 0)
{
if (n == 0)
{
}
else
return(-1);
}
hoffset += n;
}
/*
* close file stream and reopen for reading and writing
*/
register int fd;
{
/* reposition seek pointer if necessary */
p_flush();
}
/*
* move the file number fa to unit fb
*/
register int fa;
register int fb;
{
if(fa >= 0)
{
/* set fb close-on-exec */
#ifdef F_SETFD
#else
# ifdef FIOCLEX
# endif /* FIOCLEX */
#endif /* F_SETFD */
}
return(fb);
}
/*
* return file descriptor for an open file
*/
register char *fname;
int len;
{
int fd;
{
fd = -1;
}
return(fd);
}
#endif /* KSHELL */