// // files.c - Implementation of some file loading functionality. //
#include
#include “files.h” #include “utils.h”
//
// In the file object, populate the lines array with pointers to the
// beginning of each line.
//
attribute((nonnull)) static void populatelines(filet *f) {
// Calculate line numbers:
sizet linecap = 10;
f->lines = new (const char *[linecap]);
f->nlines = 0;
char *p = f->start;
for (sizet n = 0; p && p <= f->end; ++n) {
++f->nlines;
if (n >= linecap) f->lines = grow(f->lines, linecap *= 2);
f->lines[n] = p;
char *nl = memchr(p, ‘\n’, (size_t)(f->end - p));
if (nl && nl < f->end) p = nl + 1;
else break;
}
}
// // Read an entire file into memory, using a printf-style formatting string to // construct the filename. // public filet *loadfilef(filet **files, const char *fmt, …) { char filename[PATHMAX + 1] = {’\0’}; valist args; vastart(args, fmt); if (vsnprintf(filename, PATHMAX, fmt, args) > (int)PATHMAX) errx(EXITFAILURE, “File name is too large”); vaend(args); return load_file(files, filename); }
//
// Read an entire file into memory.
//
public
filet *loadfile(filet **files, const char *filename) {
int fd = filename[0] == ‘\0’ ? STDINFILENO : open(filename, ORDONLY);
if (fd < 0) {
// Check for
filename = checked_strdup(filename);
for (const char *slashes = strstr(filename, "//"); slashes; slashes = strstr(slashes, "//"))
memmove((char *)slashes, slashes + 1, strlen(slashes + 1) + 1);
file_t *f = new (file_t);
f->filename = filename;
struct stat sb;
if (fstat(fd, &sb) == -1) goto read_file;
f->mmapped = mmap(NULL, (size_t)sb.st_size + 1, 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];
goto finished_loading;
readfile: { sizet capacity = 1000, length = 0; f->allocated = new (char[capacity]); ssizet justread; while ((justread = read(fd, &f->allocated[length], (capacity - 1) - length)) > 0) { length += (sizet)just_read; if (length >= capacity - 1) f->allocated = grow(f->allocated, capacity *= 2); } f->allocated[length] = ‘\0’; f->start = f->allocated; f->end = &f->allocated[length]; }
finishedloading: if (fd != STDINFILENO) require(close(fd), “Failed to close file”);
populate_lines(f);
if (files != NULL) {
f->next = *files;
*files = f;
}
return f;
}
// // Set a file struct to represent a region of a different file. // public void slicefile(filet *slice, filet *src, const char *start, const char *end) { memset(slice, 0, sizeof(filet)); 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. // public filet *spooffile(filet **files, const char *filename, const char *text, ssizet len) { if (filename == NULL) filename = ““; filet *f = new (filet); sizet len = len == -1 ? strlen(text) : (sizet)len; f->filename = checkedstrdup(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); if (files != NULL) { f->next = *files; *files = f; } return f; }
// // Free a file and all memory contained inside its members, then set the input // pointer to NULL. // public void destroyfile(filet **atf) { filet *f = (filet )atf; 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;
}
delete (at_f);
}
// // Given a pointer, determine which line number it points to. // public sizet getlinenumber(filet *f, const char *p) { if (f->nlines == 0) return 0; // Binary search: sizet lo = 0, hi = f->nlines - 1; while (lo <= hi) { sizet 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; } return lo; // Return the line number whose line starts closest before p }
// // Given a pointer, determine which line column it points to. // public sizet getlinecolumn(filet *f, const char *p) { sizet lineno = getlinenumber(f, p); return 1 + (sizet)(p - f->lines[lineno]); }
// // Return a pointer to the line with the specified line number. // public const char *getline(filet *f, sizet linenumber) { if (linenumber == 0 || linenumber > f->nlines) return NULL; return f->lines[line_number - 1]; }
// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,:0
