267c6003 |
1 | /* -*-c-*- |
2 | * |
267c6003 |
3 | * Useful library of tools for quines |
4 | * |
5 | * (c) 1999 Mark Wooding |
6 | */ |
7 | |
8 | /*----- Licensing notice --------------------------------------------------* |
9 | * |
10 | * This file is part of Quine |
11 | * |
12 | * Quine is free software; you can redistribute it and/or modify |
13 | * it under the terms of the GNU General Public License as published by |
14 | * the Free Software Foundation; either version 2 of the License, or |
15 | * (at your option) any later version. |
16 | * |
17 | * Quine is distributed in the hope that it will be useful, |
18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
20 | * GNU General Public License for more details. |
21 | * |
22 | * You should have received a copy of the GNU General Public License |
23 | * along with Quine; if not, write to the Free Software Foundation, |
24 | * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
25 | * |
26 | * However, when this source text is embedded in a file by the Quine |
27 | * program, you may do anything you like with the resulting text; |
28 | * distribution and modification are not limited by the General Public |
29 | * License. |
30 | */ |
31 | |
267c6003 |
32 | /*----- Header files ------------------------------------------------------*/ |
33 | |
34 | /* --- ANSI headers --- */ |
35 | |
267c6003 |
36 | #include <errno.h> |
37 | #include <stdio.h> |
38 | #include <stdlib.h> |
39 | #include <string.h> |
40 | |
41 | /* --- POSIX headers --- */ |
42 | |
43 | #ifndef QUINE_PORTABLE |
44 | # include <sys/types.h> |
45 | # include <sys/fcntl.h> |
46 | # include <sys/stat.h> |
47 | # include <unistd.h> |
48 | #endif |
49 | |
50 | /* --- Local headers --- */ |
51 | |
52 | #include "quine.h" |
53 | |
54 | /*----- Macro hacking -----------------------------------------------------*/ |
55 | |
56 | #ifdef QUINE_PORTABLE |
57 | # define PORT(x) x |
58 | # define NPORT(x) |
59 | #else |
60 | # define PORT(x) |
61 | # define NPORT(x) x |
62 | #endif |
63 | |
64 | /*----- Main code ---------------------------------------------------------*/ |
65 | |
66 | /* --- @qq_xlate@ --- * |
67 | * |
68 | * Arguments: @FILE *fp@ = output file handle |
69 | * @const char *p@ = pointer to encoded text |
70 | * |
71 | * Returns: --- |
72 | * |
73 | * Use: Writes the decoded string to the given file handle. |
74 | */ |
75 | |
76 | void qq_xlate(FILE *fp, const char *p) |
77 | { |
78 | while (*p) { |
79 | if (*p == '%') { |
80 | p++; |
81 | switch (*p) { |
82 | case 'q': fputc('\"', fp); break; |
83 | case 'b': fputc('\\', fp); break; |
84 | case 't': fputc('\t', fp); break; |
85 | case 'n': fputc('\n', fp); break; |
86 | case 'a': fputc('\a', fp); break; |
87 | case 'r': fputc('\r', fp); break; |
88 | case '0': fputc('\0', fp); break; |
89 | case '%': fputc('%', fp); break; |
90 | case 'x': { |
91 | unsigned char ch = 0; |
92 | p++; |
93 | while (*p != '%') { |
94 | unsigned int x = *p++; |
95 | x -= '0'; |
96 | if (x >= 10) x -= 'a' - '0' - 10; |
97 | if (x >= 16) x -= 'A' - 'a'; |
98 | ch = (ch << 4) + x; |
99 | } |
100 | fputc(ch, fp); |
101 | } break; |
102 | default: |
103 | p--; |
104 | break; |
105 | } |
106 | } else |
107 | fputc(*p, fp); |
108 | p++; |
109 | } |
110 | } |
111 | |
112 | /* --- @qq_file@ --- * |
113 | * |
114 | * Arguments: @FILE *fp@ = output file handle |
115 | * @const char **p@ = pointer to the output array |
116 | * |
117 | * Returns: --- |
118 | * |
119 | * Use: Writes the contents of a file to the output. |
120 | */ |
121 | |
122 | void qq_file(FILE *fp, const char **p) |
123 | { |
124 | while (*p) |
125 | qq_xlate(fp, *p++); |
126 | } |
127 | |
128 | /* --- @qq_head@ --- * |
129 | * |
130 | * Arguments: @FILE *fp@ = output file handle |
131 | * |
132 | * Returns: --- |
133 | * |
134 | * Use: Writes the head of a `qqout.c' file. |
135 | */ |
136 | |
137 | void qq_head(FILE *fp) |
138 | { |
139 | fputs("/* quine output file */\n\n", fp); |
140 | } |
141 | |
142 | /* --- @qq_body@ --- * |
143 | * |
144 | * Arguments: @FILE *fp@ = output file handle |
145 | * @const char ***p@ = pointer to main table |
146 | * |
147 | * Returns: --- |
148 | * |
149 | * Use: Writes the body table of a `qqout.c' file. |
150 | */ |
151 | |
152 | void qq_body(FILE *fp, const char ***p) |
153 | { |
154 | size_t fno = 0; |
155 | const char **q; |
156 | |
157 | fputs("/* --- file contents tables --- */\n\n", fp); |
158 | while (*p) { |
159 | q = *p; |
160 | fprintf(fp, "static const char *qq__c_%lx[] = {\n", (unsigned long)fno); |
161 | fprintf(fp, "/* Filename: */ \"%s\",\n", *q++); |
162 | if (strncmp(*q, "%!", 2) == 0) |
163 | fprintf(fp, "/* Mode: */ \"%s\",\n", *q++); |
164 | while (*q) |
165 | fprintf(fp, "\"%s\",\n", *q++); |
166 | fputs("0\n};\n\n", fp); |
167 | p++; |
168 | fno++; |
169 | } |
170 | } |
171 | |
172 | /* --- @qq_tail@ --- * |
173 | * |
174 | * Arguments: @FILE *fp@ = output file handle |
175 | * @const char **qql@ = pointer to qqlib file array |
176 | * @size_t fno@ = number of files written |
177 | * @const char *fn@ = name of `qqout.c' file |
178 | * |
179 | * Returns: --- |
180 | * |
181 | * Use: Writes the head of a `qqout.c' file. |
182 | */ |
183 | |
184 | void qq_tail(FILE *fp, const char **qql, size_t fno, const char *fn) |
185 | { |
186 | const char **p; |
187 | size_t i; |
188 | |
189 | /* --- Write out the qqlib array --- */ |
190 | |
191 | fputs("/* --- qqlib code --- */\n\n" |
192 | "const char *qq_qqlib[] = {\n", fp); |
193 | for (p = qql; *p; p++) |
194 | fprintf(fp, "\"%s\",\n", *p); |
195 | fputs("0\n};\n\n", fp); |
196 | |
197 | /* --- Build the main array --- */ |
198 | |
199 | fputs("/* --- table of files to output --- */\n\n" |
200 | "const char **qq_table[] = {\n", fp); |
201 | for (i = 0; i < fno; i++) |
202 | fprintf(fp, " qq__c_%lx,\n", (unsigned long)i); |
203 | fputs(" 0\n};\n\n", fp); |
204 | |
205 | /* --- Now output the qqlib code --- */ |
206 | |
207 | fputs("#define QQ_OUT\n\n", fp); |
208 | qq_file(fp, qql); |
209 | |
210 | /* --- Finally write the code to write everything out --- */ |
211 | |
212 | fprintf(fp, |
213 | "/* --- quine output routines --- */\n\n" |
214 | "void qq_dump(FILE *fp) {\n" |
215 | " qq__dump(fp, qq_table, qq_qqlib, \"%s\", %lu);\n" |
216 | "}\n" |
217 | "int qq_create(void) {\n" |
218 | " return (qq__create(qq_table, qq_qqlib, \"%s\", %lu));\n" |
219 | "}\n\n", |
220 | fn, (unsigned long)fno, fn, (unsigned long)fno); |
221 | } |
222 | |
223 | /* --- @qq_mkfile@ --- * |
224 | * |
225 | * Arguments: @const char *fn@ = pointer to a filename |
226 | * @int mode@ = file mode to use (only in Unix-specific version) |
227 | * |
228 | * Returns: A handle for the created file. |
229 | * |
230 | * Use: Creates a file, and leading directories and stuff. |
231 | */ |
232 | |
233 | #ifdef QUINE_PORTABLE |
234 | FILE *qq_mkfile(const char *fn) |
235 | { |
236 | return (fopen(fn, "w")); |
237 | } |
238 | #else |
239 | FILE *qq_mkfile(const char *fn, int mode) |
240 | { |
241 | char buf[FILENAME_MAX]; |
242 | char *p; |
243 | int fd; |
244 | |
245 | strcpy(buf, fn); |
246 | p = strchr(buf, '/'); |
247 | for (;;) { |
248 | if (!p) |
249 | break; |
250 | *p = 0; |
251 | mkdir(buf, 0777); |
252 | *p = '/'; |
253 | p = strchr(p + 1, '/'); |
254 | } |
255 | |
256 | fd = open(fn, O_WRONLY|O_CREAT|O_TRUNC, mode); |
257 | return (fd >=0 ? fdopen(fd, "w") : 0); |
258 | } |
259 | #endif |
260 | |
261 | #ifdef QQ_OUT |
262 | |
263 | /* --- @qq__dump@ --- * |
264 | * |
265 | * Arguments: @FILE *fp@ = file to dump on |
266 | * @const char ***p@ = pointer to main table |
267 | * @const char **qql@ = pointer to qqlib code |
268 | * @const char *fn@ = name of `qqout.c' file |
269 | * @size_t fno@ = number of files to be output |
270 | * |
271 | * Returns: --- |
272 | * |
273 | * Use: Outputs the entire source code to a given file. |
274 | */ |
275 | |
276 | static void qq__dump(FILE *fp, const char ***p, const char **qql, |
277 | const char *fn, size_t fno) |
278 | { |
279 | const char ***q = p; |
280 | |
281 | while (*q) { |
282 | fprintf(fp, "******** %s\n\n", **q); |
283 | qq_file(fp, *q + 1); |
284 | q++; |
285 | } |
286 | fprintf(fp, "******** %s\n\n", fn); |
287 | qq_head(fp); |
288 | qq_body(fp, p); |
289 | qq_tail(fp, qql, fno, fn); |
290 | } |
291 | |
292 | /* --- @qq__create@ --- * |
293 | * |
294 | * Arguments: @const char ***p@ = pointer to main table |
295 | * @const char **qql@ = pointer to qqlib code |
296 | * @const char *fn@ = name of `qqout.c' file |
297 | * @size_t fno@ = number of files to be output |
298 | * |
299 | * Returns: Zero if all went well, else nonzero. |
300 | * |
301 | * Use: Rebuilds the original source distribution. |
302 | */ |
303 | |
304 | static int qq__create(const char ***p, const char **qql, |
305 | const char *fn, size_t fno) |
306 | { |
307 | FILE *fp; |
308 | const char ***q = p; |
309 | |
310 | while (*q) { |
311 | const char **qq = *q + 1; |
312 | |
313 | #ifdef QUINE_PORTABLE |
314 | if (strncmp(*qq, "%!", 2) == 0) |
315 | qq++; |
316 | fp = qq_mkfile(*qq); |
317 | #else |
318 | if (strncmp(*qq, "%!", 2) == 0) { |
319 | fp = qq_mkfile(**q, (int)strtol(*qq + 2, 0, 8)); |
320 | qq++; |
321 | } else |
322 | fp = qq_mkfile(**q, 0666); |
323 | #endif |
324 | |
325 | if (!fp) |
326 | return (-1); |
327 | qq_file(fp, qq); |
328 | if (fclose(fp)) |
329 | return (-1); |
330 | q++; |
331 | } |
332 | fp = qq_mkfile(fn, 0666); |
333 | if (!fp) |
334 | return (-1); |
335 | qq_head(fp); |
336 | qq_body(fp, p); |
337 | qq_tail(fp, qql, fno, fn); |
338 | if (fclose(fp)) |
339 | return (-1); |
340 | return (0); |
341 | } |
342 | |
343 | #endif |
344 | |
345 | /*----- That's all, folks -------------------------------------------------*/ |