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