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