#define READLINE_LIBRARY
#if defined (HAVE_CONFIG_H)
# include <config.h>
#endif
#include <stdio.h>
#include <sys/types.h>
#ifndef _MINIX
# include <sys/file.h>
#endif
#include "posixstat.h"
#include <fcntl.h>
#if defined (HAVE_STDLIB_H)
# include <stdlib.h>
#else
# include "ansi_stdlib.h"
#endif
#if defined (HAVE_UNISTD_H)
# include <unistd.h>
#endif
#if defined (__EMX__) || defined (__CYGWIN__)
# undef HAVE_MMAP
#endif
#ifdef HAVE_MMAP
# include <sys/mman.h>
# ifdef MAP_FILE
# define MAP_RFLAGS (MAP_FILE|MAP_PRIVATE)
# define MAP_WFLAGS (MAP_FILE|MAP_SHARED)
# else
# define MAP_RFLAGS MAP_PRIVATE
# define MAP_WFLAGS MAP_SHARED
# endif
# ifndef MAP_FAILED
# define MAP_FAILED ((void *)-1)
# endif
#endif
#if defined (__EMX__) || defined (__CYGWIN__)
# ifndef O_BINARY
# define O_BINARY 0
# endif
#else
# undef O_BINARY
# define O_BINARY 0
#endif
#include <errno.h>
#if !defined (errno)
extern int errno;
#endif
#include "history.h"
#include "histlib.h"
#include "rlshell.h"
#include "xmalloc.h"
static char *
history_filename (filename)
const char *filename;
{
char *return_val;
const char *home;
int home_len;
return_val = filename ? savestring (filename) : (char *)NULL;
if (return_val)
return (return_val);
home = sh_get_env_value ("HOME");
if (home == 0)
{
home = ".";
home_len = 1;
}
else
home_len = strlen (home);
return_val = (char *)xmalloc (2 + home_len + 8);
strcpy (return_val, home);
return_val[home_len] = '/';
#if defined (__MSDOS__)
strcpy (return_val + home_len + 1, "_history");
#else
strcpy (return_val + home_len + 1, ".history");
#endif
return (return_val);
}
int
read_history (filename)
const char *filename;
{
return (read_history_range (filename, 0, -1));
}
int
read_history_range (filename, from, to)
const char *filename;
int from, to;
{
register char *line_start, *line_end;
char *input, *buffer, *bufend;
int file, current_line, chars_read;
struct stat finfo;
size_t file_size;
int eret;
errno = 0;
buffer = (char *)NULL;
input = history_filename (filename);
file = open (input, O_RDONLY|O_BINARY, 0666);
if ((file < 0) || (fstat (file, &finfo) == -1))
goto error_and_exit;
file_size = (size_t)finfo.st_size;
if (file_size != finfo.st_size || file_size + 1 < file_size)
{
#if defined (EFBIG)
errno = EFBIG;
#elif defined (EOVERFLOW)
errno = EOVERFLOW;
#else
errno = EIO;
#endif
goto error_and_exit;
}
#ifdef HAVE_MMAP
buffer = (char *)mmap (0, file_size, PROT_READ|PROT_WRITE, MAP_RFLAGS, file, 0);
if ((void *)buffer == MAP_FAILED)
goto error_and_exit;
chars_read = file_size;
#else
buffer = (char *)malloc (file_size + 1);
if (buffer == 0)
goto error_and_exit;
chars_read = read (file, buffer, file_size);
#endif
if (chars_read < 0)
{
error_and_exit:
eret = errno;
if (eret == 0)
eret = EIO;
if (file >= 0)
close (file);
FREE (input);
#ifndef HAVE_MMAP
FREE (buffer);
#endif
return (eret);
}
close (file);
if (to < 0)
to = chars_read;
bufend = buffer + chars_read;
current_line = 0;
for (line_start = line_end = buffer; line_end < bufend && current_line < from; line_end++)
if (*line_end == '\n')
{
current_line++;
line_start = line_end + 1;
}
for (line_end = line_start; line_end < bufend; line_end++)
if (*line_end == '\n')
{
*line_end = '\0';
if (*line_start)
add_history (line_start);
current_line++;
if (current_line >= to)
break;
line_start = line_end + 1;
}
FREE (input);
#ifndef HAVE_MMAP
FREE (buffer);
#else
munmap (buffer, file_size);
#endif
return (0);
}
int
history_truncate_file (fname, lines)
const char *fname;
int lines;
{
char *buffer, *filename, *bp;
int file, chars_read, rv;
struct stat finfo;
size_t file_size;
buffer = (char *)NULL;
filename = history_filename (fname);
file = open (filename, O_RDONLY|O_BINARY, 0666);
rv = 0;
if (file == -1 || fstat (file, &finfo) == -1)
{
rv = errno;
if (file != -1)
close (file);
goto truncate_exit;
}
if (S_ISREG (finfo.st_mode) == 0)
{
close (file);
#ifdef EFTYPE
rv = EFTYPE;
#else
rv = EINVAL;
#endif
goto truncate_exit;
}
file_size = (size_t)finfo.st_size;
if (file_size != finfo.st_size || file_size + 1 < file_size)
{
close (file);
#if defined (EFBIG)
rv = errno = EFBIG;
#elif defined (EOVERFLOW)
rv = errno = EOVERFLOW;
#else
rv = errno = EINVAL;
#endif
goto truncate_exit;
}
buffer = (char *)malloc (file_size + 1);
if (buffer == 0)
{
close (file);
goto truncate_exit;
}
chars_read = read (file, buffer, file_size);
close (file);
if (chars_read <= 0)
{
rv = (chars_read < 0) ? errno : 0;
goto truncate_exit;
}
for (bp = buffer + chars_read - 1; lines && bp > buffer; bp--)
{
if (*bp == '\n')
lines--;
}
for ( ; bp > buffer; bp--)
if (*bp == '\n')
{
bp++;
break;
}
if (bp > buffer && ((file = open (filename, O_WRONLY|O_TRUNC|O_BINARY, 0600)) != -1))
{
write (file, bp, chars_read - (bp - buffer));
#if defined (__BEOS__)
ftruncate (file, chars_read - (bp - buffer));
#endif
close (file);
}
truncate_exit:
FREE (buffer);
free (filename);
return rv;
}
static int
history_do_write (filename, nelements, overwrite)
const char *filename;
int nelements, overwrite;
{
register int i;
char *output;
int file, mode, rv;
size_t cursize;
#ifdef HAVE_MMAP
mode = overwrite ? O_RDWR|O_CREAT|O_TRUNC|O_BINARY : O_RDWR|O_APPEND|O_BINARY;
#else
mode = overwrite ? O_WRONLY|O_CREAT|O_TRUNC|O_BINARY : O_WRONLY|O_APPEND|O_BINARY;
#endif
output = history_filename (filename);
rv = 0;
if ((file = open (output, mode, 0600)) == -1)
{
FREE (output);
return (errno);
}
#ifdef HAVE_MMAP
cursize = overwrite ? 0 : lseek (file, 0, SEEK_END);
#endif
if (nelements > history_length)
nelements = history_length;
{
HIST_ENTRY **the_history;
register int j;
int buffer_size;
char *buffer;
the_history = history_list ();
for (buffer_size = 0, i = history_length - nelements; i < history_length; i++)
buffer_size += 1 + strlen (the_history[i]->line);
#ifdef HAVE_MMAP
if (ftruncate (file, buffer_size+cursize) == -1)
goto mmap_error;
buffer = (char *)mmap (0, buffer_size, PROT_READ|PROT_WRITE, MAP_WFLAGS, file, cursize);
if ((void *)buffer == MAP_FAILED)
{
mmap_error:
rv = errno;
FREE (output);
close (file);
return rv;
}
#else
buffer = (char *)malloc (buffer_size);
if (buffer == 0)
{
rv = errno;
FREE (output);
close (file);
return rv;
}
#endif
for (j = 0, i = history_length - nelements; i < history_length; i++)
{
strcpy (buffer + j, the_history[i]->line);
j += strlen (the_history[i]->line);
buffer[j++] = '\n';
}
#ifdef HAVE_MMAP
if (msync (buffer, buffer_size, 0) != 0 || munmap (buffer, buffer_size) != 0)
rv = errno;
#else
if (write (file, buffer, buffer_size) < 0)
rv = errno;
free (buffer);
#endif
}
close (file);
FREE (output);
return (rv);
}
int
append_history (nelements, filename)
int nelements;
const char *filename;
{
return (history_do_write (filename, nelements, HISTORY_APPEND));
}
int
write_history (filename)
const char *filename;
{
return (history_do_write (filename, history_length, HISTORY_OVERWRITE));
}