#include <sys/param.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <wchar.h>
#include <wctype.h>
#include "grep.h"
#define MAXBUFSIZ (32 * 1024)
#define LNBUFBUMP 80
static char *buffer;
static char *bufpos;
static size_t bufrem;
static size_t fsiz;
static char *lnbuf;
static size_t lnbuflen;
static inline int
grep_refill(struct file *f)
{
ssize_t nr;
if (filebehave == FILE_MMAP)
return (0);
bufpos = buffer;
bufrem = 0;
nr = read(f->fd, buffer, MAXBUFSIZ);
if (nr < 0 && errno == EISDIR)
nr = 0;
if (nr < 0)
return (-1);
bufrem = nr;
return (0);
}
static inline int
grep_lnbufgrow(size_t newlen)
{
if (lnbuflen < newlen) {
lnbuf = grep_realloc(lnbuf, newlen);
lnbuflen = newlen;
}
return (0);
}
char *
grep_fgetln(struct file *f, struct parsec *pc)
{
char *p;
size_t len;
size_t off;
ptrdiff_t diff;
if (bufrem == 0 && grep_refill(f) != 0)
goto error;
if (bufrem == 0) {
pc->ln.len= 0;
return (bufpos);
}
if ((p = memchr(bufpos, fileeol, bufrem)) != NULL) {
++p;
len = p - bufpos;
if (grep_lnbufgrow(len + 1))
goto error;
memcpy(lnbuf, bufpos, len);
bufrem -= len;
bufpos = p;
pc->ln.len = len;
lnbuf[len] = '\0';
return (lnbuf);
}
for (len = bufrem, off = 0; ; len += bufrem) {
if (grep_lnbufgrow(len + LNBUFBUMP))
goto error;
memcpy(lnbuf + off, bufpos, len - off);
if (filebehave == FILE_MMAP) {
bufrem -= len;
break;
}
off = len;
if (grep_refill(f) != 0)
goto error;
if (bufrem == 0)
break;
if ((p = memchr(bufpos, fileeol, bufrem)) == NULL)
continue;
++p;
diff = p - bufpos;
len += diff;
if (grep_lnbufgrow(len + 1))
goto error;
memcpy(lnbuf + off, bufpos, diff);
bufrem -= diff;
bufpos = p;
break;
}
pc->ln.len = len;
lnbuf[len] = '\0';
return (lnbuf);
error:
pc->ln.len = 0;
return (NULL);
}
struct file *
grep_open(const char *path)
{
struct file *f;
f = grep_malloc(sizeof *f);
memset(f, 0, sizeof *f);
if (path == NULL) {
lbflag = true;
f->fd = STDIN_FILENO;
} else if ((f->fd = open(path, O_RDONLY)) == -1)
goto error1;
if (filebehave == FILE_MMAP) {
struct stat st;
if (fstat(f->fd, &st) == -1 || !S_ISREG(st.st_mode))
filebehave = FILE_STDIO;
else {
int flags = MAP_PRIVATE | MAP_NOCORE | MAP_NOSYNC;
#ifdef MAP_PREFAULT_READ
flags |= MAP_PREFAULT_READ;
#endif
fsiz = st.st_size;
buffer = mmap(NULL, fsiz, PROT_READ, flags,
f->fd, (off_t)0);
if (buffer == MAP_FAILED)
filebehave = FILE_STDIO;
else {
bufrem = st.st_size;
bufpos = buffer;
madvise(buffer, st.st_size, MADV_SEQUENTIAL);
}
}
}
if ((buffer == NULL) || (buffer == MAP_FAILED))
buffer = grep_malloc(MAXBUFSIZ);
if (bufrem == 0 && grep_refill(f) != 0)
goto error2;
if (binbehave != BINFILE_TEXT && fileeol != '\0' &&
memchr(bufpos, '\0', bufrem) != NULL)
f->binary = true;
return (f);
error2:
close(f->fd);
error1:
free(f);
return (NULL);
}
void
grep_close(struct file *f)
{
close(f->fd);
if (filebehave == FILE_MMAP) {
munmap(buffer, fsiz);
buffer = NULL;
}
bufpos = buffer;
bufrem = 0;
free(lnbuf);
lnbuf = NULL;
lnbuflen = 0;
}