bp/files.c

210 lines
5.3 KiB
C
Raw Normal View History

//
// files.c - Implementation of some file loading functionality.
//
2020-09-16 19:35:43 -07:00
#include <err.h>
2020-09-16 19:35:43 -07:00
#include <fcntl.h>
#include <limits.h>
2020-09-28 21:30:43 -07:00
#include <stdarg.h>
2020-09-16 19:35:43 -07:00
#include <string.h>
2020-09-24 22:10:03 -07:00
#include <sys/mman.h>
#include <sys/stat.h>
2020-09-16 19:35:43 -07:00
#include "files.h"
#include "match.h"
2021-01-15 19:44:16 -08:00
#include "pattern.h"
#include "utils.h"
2020-09-16 19:35:43 -07:00
2021-01-12 21:28:44 -08:00
//
// In the file object, populate the `lines` array with pointers to the
// beginning of each line.
//
__attribute__((nonnull))
static void populate_lines(file_t *f)
2020-09-28 21:30:43 -07:00
{
// Calculate line numbers:
size_t linecap = 10;
f->lines = new(const char*[linecap]);
2020-09-28 21:30:43 -07:00
f->nlines = 0;
char *p = f->start;
for (size_t n = 0; p && p <= f->end; ++n) {
2020-09-28 21:30:43 -07:00
++f->nlines;
if (n >= linecap)
f->lines = grow(f->lines, linecap *= 2);
2020-09-28 21:30:43 -07:00
f->lines[n] = p;
char *nl = memchr(p, '\n', (size_t)(f->end - p));
if (nl && nl < f->end) p = nl+1;
else break;
2020-09-28 21:30:43 -07:00
}
}
//
2021-01-17 18:13:00 -08:00
// Read an entire file into memory, using a printf-style formatting string to
// construct the filename.
//
file_t *load_filef(file_t **files, const char *fmt, ...)
2020-09-16 19:35:43 -07:00
{
2021-01-18 11:28:39 -08:00
char filename[PATH_MAX+1] = {'\0'};
va_list args;
va_start(args, fmt);
if (vsnprintf(filename, PATH_MAX, fmt, args) > (int)PATH_MAX)
errx(EXIT_FAILURE, "File name is too large");
va_end(args);
return load_file(files, filename);
}
//
// Read an entire file into memory.
//
file_t *load_file(file_t **files, const char *filename)
{
int fd = filename[0] == '\0' ? STDIN_FILENO : open(filename, O_RDONLY);
if (fd < 0) {
2021-05-21 00:22:05 -07:00
// Check for <file>:<line>
if (strrchr(filename, ':')) {
char tmp[PATH_MAX] = {0};
strcpy(tmp, filename);
2021-05-21 00:22:05 -07:00
char *colon = strrchr(tmp, ':');
*colon = '\0';
file_t *f = load_file(files, tmp);
if (!f) return f;
long line = strtol(colon+1, &colon, 10);
f->start = (char*)get_line(f, (size_t)line);
2021-05-21 00:22:05 -07:00
f->end = (char*)get_line(f, (size_t)line+1);
return f;
}
return NULL;
}
file_t *f = new(file_t);
2021-07-30 14:54:28 -07:00
f->filename = checked_strdup(filename);
2020-09-28 21:30:43 -07:00
2020-09-24 22:10:03 -07:00
struct stat sb;
if (fstat(fd, &sb) == -1)
goto read_file;
2020-09-24 22:10:03 -07:00
f->mmapped = mmap(NULL, (size_t)sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
if (f->mmapped == MAP_FAILED) {
f->mmapped = NULL;
goto read_file;
}
f->start = f->mmapped;
f->end = &f->mmapped[sb.st_size];
2020-09-24 22:10:03 -07:00
goto finished_loading;
read_file:
2021-05-31 12:56:49 -07:00
{
size_t capacity = 1000, length = 0;
f->allocated = new(char[capacity]);
2021-05-31 12:56:49 -07:00
ssize_t just_read;
while ((just_read=read(fd, &f->allocated[length], (capacity-1) - length)) > 0) {
2021-05-31 12:56:49 -07:00
length += (size_t)just_read;
if (length >= capacity-1)
f->allocated = grow(f->allocated, capacity *= 2);
2021-05-31 12:56:49 -07:00
}
f->allocated[length] = '\0';
f->start = f->allocated;
f->end = &f->allocated[length];
2020-09-16 19:35:43 -07:00
}
2020-09-24 22:10:03 -07:00
finished_loading:
if (fd != STDIN_FILENO)
require(close(fd), "Failed to close file");
populate_lines(f);
2021-01-13 18:56:22 -08:00
if (files != NULL) {
f->next = *files;
*files = f;
}
2020-09-28 21:30:43 -07:00
return f;
}
2020-09-16 19:35:43 -07:00
//
// Set a file struct to represent a region of a different file.
//
void slice_file(file_t *slice, file_t *src, const char *start, const char *end)
{
memset(slice, 0, sizeof(file_t));
slice->filename = src->filename;
slice->lines = src->lines;
slice->nlines = src->nlines;
slice->start = (char*)start;
slice->end = (char*)end;
}
//
// Create a virtual file from a string.
//
file_t *spoof_file(file_t **files, const char *filename, const char *text, ssize_t _len)
2020-09-28 21:30:43 -07:00
{
if (filename == NULL) filename = "";
file_t *f = new(file_t);
size_t len = _len == -1 ? strlen(text) : (size_t)_len;
2021-07-30 14:54:28 -07:00
f->filename = checked_strdup(filename);
f->allocated = new(char[len+1]);
memcpy(f->allocated, text, len);
f->start = &f->allocated[0];
f->end = &f->allocated[len];
populate_lines(f);
2021-01-13 18:56:22 -08:00
if (files != NULL) {
f->next = *files;
*files = f;
}
2020-09-16 19:35:43 -07:00
return f;
}
2021-01-12 21:28:44 -08:00
//
// Free a file and all memory contained inside its members, then set the input
// pointer to NULL.
//
void destroy_file(file_t **at_f)
2020-09-16 19:35:43 -07:00
{
file_t *f = (file_t*)*at_f;
if (f->filename)
delete(&f->filename);
if (f->lines)
delete(&f->lines);
if (f->allocated)
delete(&f->allocated);
if (f->mmapped) {
require(munmap(f->mmapped, (size_t)(f->end - f->mmapped)),
"Failure to un-memory-map some memory");
f->mmapped = NULL;
2020-09-16 19:35:43 -07:00
}
delete(at_f);
2020-09-16 19:35:43 -07:00
}
2021-01-12 21:28:44 -08:00
//
// Given a pointer, determine which line number it points to.
//
2020-09-16 19:35:43 -07:00
size_t get_line_number(file_t *f, const char *p)
{
if (f->nlines == 0) return 0;
// Binary search:
size_t lo = 0, hi = f->nlines-1;
while (lo <= hi) {
size_t mid = (lo + hi) / 2;
if (f->lines[mid] == p)
return mid + 1;
else if (f->lines[mid] < p)
lo = mid + 1;
else if (f->lines[mid] > p)
hi = mid - 1;
2020-09-16 19:35:43 -07:00
}
return lo; // Return the line number whose line starts closest before p
2020-09-16 19:35:43 -07:00
}
2021-01-12 21:28:44 -08:00
//
// Return a pointer to the line with the specified line number.
//
2020-09-16 19:35:43 -07:00
const char *get_line(file_t *f, size_t line_number)
{
if (line_number == 0 || line_number > f->nlines) return NULL;
return f->lines[line_number - 1];
}
// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0