2 // files.c - Implementation of some file loading functionality.
17 // In the file object, populate the `lines` array with pointers to the
18 // beginning of each line.
20 __attribute__((nonnull)) static void populate_lines(file_t *f) {
21 // Calculate line numbers:
23 f->lines = new (const char *[linecap]);
26 for (size_t n = 0; p && p <= f->end; ++n) {
28 if (n >= linecap) f->lines = grow(f->lines, linecap *= 2);
30 char *nl = memchr(p, '\n', (size_t)(f->end - p));
31 if (nl && nl < f->end) p = nl + 1;
37 // Read an entire file into memory, using a printf-style formatting string to
38 // construct the filename.
41 file_t *load_filef(file_t **files, const char *fmt, ...) {
42 char filename[PATH_MAX + 1] = {'\0'};
45 if (vsnprintf(filename, PATH_MAX, fmt, args) > (int)PATH_MAX) errx(EXIT_FAILURE, "File name is too large");
47 return load_file(files, filename);
51 // Read an entire file into memory.
54 file_t *load_file(file_t **files, const char *filename) {
55 int fd = filename[0] == '\0' ? STDIN_FILENO : open(filename, O_RDONLY);
57 // Check for <file>:<line>
58 if (strrchr(filename, ':')) {
59 char tmp[PATH_MAX] = {0};
60 strlcpy(tmp, filename, sizeof(tmp));
61 char *colon = strrchr(tmp, ':');
63 file_t *f = load_file(files, tmp);
65 long line = strtol(colon + 1, &colon, 10);
66 f->start = (char *)get_line(f, (size_t)line);
67 f->end = (char *)get_line(f, (size_t)line + 1);
73 filename = checked_strdup(filename);
74 for (const char *slashes = strstr(filename, "//"); slashes; slashes = strstr(slashes, "//"))
75 memmove((char *)slashes, slashes + 1, strlen(slashes + 1) + 1);
76 file_t *f = new (file_t);
77 f->filename = filename;
80 if (fstat(fd, &sb) == -1) goto read_file;
82 f->mmapped = mmap(NULL, (size_t)sb.st_size + 1, PROT_READ, MAP_PRIVATE, fd, 0);
83 if (f->mmapped == MAP_FAILED) {
87 f->start = f->mmapped;
88 f->end = &f->mmapped[sb.st_size];
89 goto finished_loading;
92 size_t capacity = 1000, length = 0;
93 f->allocated = new (char[capacity]);
95 while ((just_read = read(fd, &f->allocated[length], (capacity - 1) - length)) > 0) {
96 length += (size_t)just_read;
97 if (length >= capacity - 1) f->allocated = grow(f->allocated, capacity *= 2);
99 f->allocated[length] = '\0';
100 f->start = f->allocated;
101 f->end = &f->allocated[length];
105 if (fd != STDIN_FILENO) require(close(fd), "Failed to close file");
116 // Set a file struct to represent a region of a different file.
119 void slice_file(file_t *slice, file_t *src, const char *start, const char *end) {
120 memset(slice, 0, sizeof(file_t));
121 slice->filename = src->filename;
122 slice->lines = src->lines;
123 slice->nlines = src->nlines;
124 slice->start = (char *)start;
125 slice->end = (char *)end;
129 // Create a virtual file from a string.
132 file_t *spoof_file(file_t **files, const char *filename, const char *text, ssize_t _len) {
133 if (filename == NULL) filename = "";
134 file_t *f = new (file_t);
135 size_t len = _len == -1 ? strlen(text) : (size_t)_len;
136 f->filename = checked_strdup(filename);
137 f->allocated = new (char[len + 1]);
138 memcpy(f->allocated, text, len);
139 f->start = &f->allocated[0];
140 f->end = &f->allocated[len];
150 // Free a file and all memory contained inside its members, then set the input
154 void destroy_file(file_t **at_f) {
155 file_t *f = (file_t *)*at_f;
156 if (f->filename) delete (&f->filename);
158 if (f->lines) delete (&f->lines);
160 if (f->allocated) delete (&f->allocated);
163 require(munmap(f->mmapped, (size_t)(f->end - f->mmapped)), "Failure to un-memory-map some memory");
171 // Given a pointer, determine which line number it points to.
174 size_t get_line_number(file_t *f, const char *p) {
175 if (f->nlines == 0) return 0;
177 size_t lo = 0, hi = f->nlines - 1;
179 size_t mid = (lo + hi) / 2;
180 if (f->lines[mid] == p) return mid + 1;
181 else if (f->lines[mid] < p) lo = mid + 1;
182 else if (f->lines[mid] > p) hi = mid - 1;
184 return lo; // Return the line number whose line starts closest before p
188 // Given a pointer, determine which line column it points to.
191 size_t get_line_column(file_t *f, const char *p) {
192 size_t line_no = get_line_number(f, p);
193 return 1 + (size_t)(p - f->lines[line_no]);
197 // Return a pointer to the line with the specified line number.
200 const char *get_line(file_t *f, size_t line_number) {
201 if (line_number == 0 || line_number > f->nlines) return NULL;
202 return f->lines[line_number - 1];
205 // vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0