1 #include <ctype.h>
2 #include <err.h>
3 #include <errno.h>
4 #include <fcntl.h>
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <string.h>
8 #include <unistd.h>
9 #include <sys/mman.h>
10 
11 #include "ckc.h"
12 
13 struct srcfile {
14 	txt			name;
15 	txt			body;
16 	struct stat		stat;
17 	int			fd;
18 	TAILQ_ENTRY(srcfile)	list;
19 	struct chunk		chunk;
20 };
21 
22 
23 static TAILQ_HEAD(, srcfile)	srcfiles = TAILQ_HEAD_INITIALIZER(srcfiles);
24 
25 /*********************************************************************
26  * chunkmethods
27  */
28 
29 static void
srcfile_quote(FILE * f,struct chunk * c,txt t)30 srcfile_quote(FILE *f, struct chunk *c, txt t)
31 {
32 	struct srcfile *sf;
33 	const char *p, *q, *r;
34 
35 	while (t.b < t.e && ct_white(*t.b))
36 		t.b++;
37 	while (t.b < t.e && ct_white(t.e[-1]))
38 		t.e--;
39 	sf = c->priv;
40 	fprintf(f, "file \"%.*s\"\n", PT(sf->name));
41 	for (p = t.b; p > sf->body.b && *p != '\n'; p--)
42 		continue;
43 	if (*p == '\n')
44 		p++;
45 	for (q = t.e; q < sf->body.e && *q != '\n'; q++)
46 		continue;
47 	fprintf(f, "\t%.*s\n\t", q - p, p);
48 	for (r = p; r < t.e; r++) {
49 		if (ct_white(*r))
50 			fprintf(f, "%c", *r);
51 		else if (r < t.b)
52 			fprintf(f, " ");
53 		else
54 			fprintf(f, "^");
55 	}
56 	printf("\n");
57 
58 }
59 
60 static struct chunk_method srcfile_methods = {
61 	.name = "srcfile",
62 	.quote = srcfile_quote
63 };
64 
65 /*********************************************************************
66  * Load from a fd
67  * Return ptr or NULL+errno
68  */
69 
70 struct chunk *
LoadSrcFd(const char * name,int fd)71 LoadSrcFd(const char *name, int fd)
72 {
73 	struct srcfile	*sf, *sf2;
74 	void *p;
75 	char *dst;
76 	int rd;
77 	size_t sofar, alloced;
78 
79 	sf = calloc(sizeof *sf, 1);
80 	AAP(sf);
81 	sf->chunk.priv = sf;
82 	sf->chunk.t = &sf->body;
83 	sf->chunk.f = &srcfile_methods;
84 	sf->name = Txt(name, NULL);
85 	sf->fd = fd;
86 	assert(fstat(fd, &sf->stat) == 0);
87 	TAILQ_FOREACH(sf2, &srcfiles, list) {
88 		/* Check if we have the file already */
89 		if (sf2->stat.st_ino != sf->stat.st_ino)
90 			continue;
91 		if (sf2->stat.st_dev != sf->stat.st_dev)
92 			continue;
93 
94 		/* We have the file, but has it changed ? */
95 		if (sf2->stat.st_mtime != sf->stat.st_mtime)
96 			continue;
97 
98 		/* Also check size, for good measure */
99 		if (sf2->stat.st_size != sf->stat.st_size)
100 			continue;
101 
102 		/* If we used the same name, use the same instance */
103 		if (TxtEq(sf->name, sf2->name)) {
104 			/* XXX: close the fd ? */
105 			free(sf);
106 			return (&sf2->chunk);
107 		}
108 
109 		/* Else just reuse the content */
110 		/* XXX: close the fd ? */
111 		sf->body = sf2->body;
112 		TAILQ_INSERT_TAIL(&srcfiles, sf, list);
113 		return (&sf->chunk);
114 	}
115 
116 	if (S_ISREG(sf->stat.st_mode)) {
117 		/* Try mmap'ing regular files */
118 		sofar = (size_t)sf->stat.st_size;
119 		assert((off_t)sofar == sf->stat.st_size);
120 		p = mmap(NULL, sofar, PROT_READ, 0, fd, 0);
121 		if (p != MAP_FAILED) {
122 			sf->body.b = p;
123 			sf->body.e = sf->body.b + sofar;
124 			TAILQ_INSERT_TAIL(&srcfiles, sf, list);
125 			return (&sf->chunk);
126 		}
127 	}
128 	alloced = 256 * 1024;
129 	dst = malloc(alloced);
130 	AAP(dst);
131 	for (sofar = 0; ; ) {
132 		rd = read(fd, dst + sofar, alloced - sofar);
133 		assert(rd >= 0);
134 		if (rd == 0)
135 			break;
136 		sofar += (unsigned)rd;
137 		if (sofar == alloced) {
138 			alloced += alloced;
139 			dst = realloc(dst, alloced);
140 			AAP(dst);
141 		}
142 	}
143 	dst = realloc(dst, sofar);
144 	AAP(dst);
145 	sf->body = Txt(dst, dst + sofar);
146 	TAILQ_INSERT_TAIL(&srcfiles, sf, list);
147 	return (&sf->chunk);
148 }
149 
150 /*********************************************************************
151  * Load a file
152  * Return ptr or NULL+errno
153  */
154 
155 struct chunk *
LoadSrcFile(const char * pfx,const char * name)156 LoadSrcFile(const char *pfx, const char *name)
157 {
158 	int fd, e;
159 	char *n;
160 
161 	if (!strcmp(name, "-"))
162 		return (LoadSrcFd("<stdin>", 0));
163 
164 	if (pfx == NULL)
165 		n = strdup(name);
166 	else
167 		asprintf(&n, "%s/%s", pfx, name);
168 
169 	fd = open(n, O_RDONLY);
170 	if (fd < 0) {
171 		e = errno;
172 		warn("Cannot open \"%s\"", n);
173 		free (n);
174 		errno = e;
175 		return (NULL);
176 	}
177 	return (LoadSrcFd(n, fd));
178 }
179 
180 /*********************************************************************
181  * Load a file
182  * Return ptr or NULL+errno
183  */
184 
185 struct chunk *
LoadSrcFileTxt(const char * pfx,txt name)186 LoadSrcFileTxt(const char *pfx, txt name)
187 {
188 	int fd, e;
189 	char *n;
190 
191 	if (pfx == NULL)
192 		asprintf(&n, "%.*s", PT(name));
193 	else
194 		asprintf(&n, "%s/%.*s", pfx, PT(name));
195 
196 	fd = open(n, O_RDONLY);
197 	if (fd < 0) {
198 		free (n);
199 		return (NULL);
200 	}
201 	return (LoadSrcFd(n, fd));
202 }
203