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 | |
34 | /*----- Revision history --------------------------------------------------* |
35 | * |
36 | * $Log: qqlib.c,v $ |
ab2f100c |
37 | * Revision 1.2 2000/08/01 17:41:39 mdw |
38 | * Remove unnecessary <ctype.h> header. |
39 | * |
40 | * Revision 1.1.1.1 1999/04/28 19:58:07 mdw |
267c6003 |
41 | * Initial revision |
42 | * |
43 | */ |
44 | |
45 | /*----- Header files ------------------------------------------------------*/ |
46 | |
47 | /* --- ANSI headers --- */ |
48 | |
267c6003 |
49 | #include <errno.h> |
50 | #include <stdio.h> |
51 | #include <stdlib.h> |
52 | #include <string.h> |
53 | |
54 | /* --- POSIX headers --- */ |
55 | |
56 | #ifndef QUINE_PORTABLE |
57 | # include <sys/types.h> |
58 | # include <sys/fcntl.h> |
59 | # include <sys/stat.h> |
60 | # include <unistd.h> |
61 | #endif |
62 | |
63 | /* --- Local headers --- */ |
64 | |
65 | #include "quine.h" |
66 | |
67 | /*----- Macro hacking -----------------------------------------------------*/ |
68 | |
69 | #ifdef QUINE_PORTABLE |
70 | # define PORT(x) x |
71 | # define NPORT(x) |
72 | #else |
73 | # define PORT(x) |
74 | # define NPORT(x) x |
75 | #endif |
76 | |
77 | /*----- Main code ---------------------------------------------------------*/ |
78 | |
79 | /* --- @qq_xlate@ --- * |
80 | * |
81 | * Arguments: @FILE *fp@ = output file handle |
82 | * @const char *p@ = pointer to encoded text |
83 | * |
84 | * Returns: --- |
85 | * |
86 | * Use: Writes the decoded string to the given file handle. |
87 | */ |
88 | |
89 | void qq_xlate(FILE *fp, const char *p) |
90 | { |
91 | while (*p) { |
92 | if (*p == '%') { |
93 | p++; |
94 | switch (*p) { |
95 | case 'q': fputc('\"', fp); break; |
96 | case 'b': fputc('\\', fp); break; |
97 | case 't': fputc('\t', fp); break; |
98 | case 'n': fputc('\n', fp); break; |
99 | case 'a': fputc('\a', fp); break; |
100 | case 'r': fputc('\r', fp); break; |
101 | case '0': fputc('\0', fp); break; |
102 | case '%': fputc('%', fp); break; |
103 | case 'x': { |
104 | unsigned char ch = 0; |
105 | p++; |
106 | while (*p != '%') { |
107 | unsigned int x = *p++; |
108 | x -= '0'; |
109 | if (x >= 10) x -= 'a' - '0' - 10; |
110 | if (x >= 16) x -= 'A' - 'a'; |
111 | ch = (ch << 4) + x; |
112 | } |
113 | fputc(ch, fp); |
114 | } break; |
115 | default: |
116 | p--; |
117 | break; |
118 | } |
119 | } else |
120 | fputc(*p, fp); |
121 | p++; |
122 | } |
123 | } |
124 | |
125 | /* --- @qq_file@ --- * |
126 | * |
127 | * Arguments: @FILE *fp@ = output file handle |
128 | * @const char **p@ = pointer to the output array |
129 | * |
130 | * Returns: --- |
131 | * |
132 | * Use: Writes the contents of a file to the output. |
133 | */ |
134 | |
135 | void qq_file(FILE *fp, const char **p) |
136 | { |
137 | while (*p) |
138 | qq_xlate(fp, *p++); |
139 | } |
140 | |
141 | /* --- @qq_head@ --- * |
142 | * |
143 | * Arguments: @FILE *fp@ = output file handle |
144 | * |
145 | * Returns: --- |
146 | * |
147 | * Use: Writes the head of a `qqout.c' file. |
148 | */ |
149 | |
150 | void qq_head(FILE *fp) |
151 | { |
152 | fputs("/* quine output file */\n\n", fp); |
153 | } |
154 | |
155 | /* --- @qq_body@ --- * |
156 | * |
157 | * Arguments: @FILE *fp@ = output file handle |
158 | * @const char ***p@ = pointer to main table |
159 | * |
160 | * Returns: --- |
161 | * |
162 | * Use: Writes the body table of a `qqout.c' file. |
163 | */ |
164 | |
165 | void qq_body(FILE *fp, const char ***p) |
166 | { |
167 | size_t fno = 0; |
168 | const char **q; |
169 | |
170 | fputs("/* --- file contents tables --- */\n\n", fp); |
171 | while (*p) { |
172 | q = *p; |
173 | fprintf(fp, "static const char *qq__c_%lx[] = {\n", (unsigned long)fno); |
174 | fprintf(fp, "/* Filename: */ \"%s\",\n", *q++); |
175 | if (strncmp(*q, "%!", 2) == 0) |
176 | fprintf(fp, "/* Mode: */ \"%s\",\n", *q++); |
177 | while (*q) |
178 | fprintf(fp, "\"%s\",\n", *q++); |
179 | fputs("0\n};\n\n", fp); |
180 | p++; |
181 | fno++; |
182 | } |
183 | } |
184 | |
185 | /* --- @qq_tail@ --- * |
186 | * |
187 | * Arguments: @FILE *fp@ = output file handle |
188 | * @const char **qql@ = pointer to qqlib file array |
189 | * @size_t fno@ = number of files written |
190 | * @const char *fn@ = name of `qqout.c' file |
191 | * |
192 | * Returns: --- |
193 | * |
194 | * Use: Writes the head of a `qqout.c' file. |
195 | */ |
196 | |
197 | void qq_tail(FILE *fp, const char **qql, size_t fno, const char *fn) |
198 | { |
199 | const char **p; |
200 | size_t i; |
201 | |
202 | /* --- Write out the qqlib array --- */ |
203 | |
204 | fputs("/* --- qqlib code --- */\n\n" |
205 | "const char *qq_qqlib[] = {\n", fp); |
206 | for (p = qql; *p; p++) |
207 | fprintf(fp, "\"%s\",\n", *p); |
208 | fputs("0\n};\n\n", fp); |
209 | |
210 | /* --- Build the main array --- */ |
211 | |
212 | fputs("/* --- table of files to output --- */\n\n" |
213 | "const char **qq_table[] = {\n", fp); |
214 | for (i = 0; i < fno; i++) |
215 | fprintf(fp, " qq__c_%lx,\n", (unsigned long)i); |
216 | fputs(" 0\n};\n\n", fp); |
217 | |
218 | /* --- Now output the qqlib code --- */ |
219 | |
220 | fputs("#define QQ_OUT\n\n", fp); |
221 | qq_file(fp, qql); |
222 | |
223 | /* --- Finally write the code to write everything out --- */ |
224 | |
225 | fprintf(fp, |
226 | "/* --- quine output routines --- */\n\n" |
227 | "void qq_dump(FILE *fp) {\n" |
228 | " qq__dump(fp, qq_table, qq_qqlib, \"%s\", %lu);\n" |
229 | "}\n" |
230 | "int qq_create(void) {\n" |
231 | " return (qq__create(qq_table, qq_qqlib, \"%s\", %lu));\n" |
232 | "}\n\n", |
233 | fn, (unsigned long)fno, fn, (unsigned long)fno); |
234 | } |
235 | |
236 | /* --- @qq_mkfile@ --- * |
237 | * |
238 | * Arguments: @const char *fn@ = pointer to a filename |
239 | * @int mode@ = file mode to use (only in Unix-specific version) |
240 | * |
241 | * Returns: A handle for the created file. |
242 | * |
243 | * Use: Creates a file, and leading directories and stuff. |
244 | */ |
245 | |
246 | #ifdef QUINE_PORTABLE |
247 | FILE *qq_mkfile(const char *fn) |
248 | { |
249 | return (fopen(fn, "w")); |
250 | } |
251 | #else |
252 | FILE *qq_mkfile(const char *fn, int mode) |
253 | { |
254 | char buf[FILENAME_MAX]; |
255 | char *p; |
256 | int fd; |
257 | |
258 | strcpy(buf, fn); |
259 | p = strchr(buf, '/'); |
260 | for (;;) { |
261 | if (!p) |
262 | break; |
263 | *p = 0; |
264 | mkdir(buf, 0777); |
265 | *p = '/'; |
266 | p = strchr(p + 1, '/'); |
267 | } |
268 | |
269 | fd = open(fn, O_WRONLY|O_CREAT|O_TRUNC, mode); |
270 | return (fd >=0 ? fdopen(fd, "w") : 0); |
271 | } |
272 | #endif |
273 | |
274 | #ifdef QQ_OUT |
275 | |
276 | /* --- @qq__dump@ --- * |
277 | * |
278 | * Arguments: @FILE *fp@ = file to dump on |
279 | * @const char ***p@ = pointer to main table |
280 | * @const char **qql@ = pointer to qqlib code |
281 | * @const char *fn@ = name of `qqout.c' file |
282 | * @size_t fno@ = number of files to be output |
283 | * |
284 | * Returns: --- |
285 | * |
286 | * Use: Outputs the entire source code to a given file. |
287 | */ |
288 | |
289 | static void qq__dump(FILE *fp, const char ***p, const char **qql, |
290 | const char *fn, size_t fno) |
291 | { |
292 | const char ***q = p; |
293 | |
294 | while (*q) { |
295 | fprintf(fp, "******** %s\n\n", **q); |
296 | qq_file(fp, *q + 1); |
297 | q++; |
298 | } |
299 | fprintf(fp, "******** %s\n\n", fn); |
300 | qq_head(fp); |
301 | qq_body(fp, p); |
302 | qq_tail(fp, qql, fno, fn); |
303 | } |
304 | |
305 | /* --- @qq__create@ --- * |
306 | * |
307 | * Arguments: @const char ***p@ = pointer to main table |
308 | * @const char **qql@ = pointer to qqlib code |
309 | * @const char *fn@ = name of `qqout.c' file |
310 | * @size_t fno@ = number of files to be output |
311 | * |
312 | * Returns: Zero if all went well, else nonzero. |
313 | * |
314 | * Use: Rebuilds the original source distribution. |
315 | */ |
316 | |
317 | static int qq__create(const char ***p, const char **qql, |
318 | const char *fn, size_t fno) |
319 | { |
320 | FILE *fp; |
321 | const char ***q = p; |
322 | |
323 | while (*q) { |
324 | const char **qq = *q + 1; |
325 | |
326 | #ifdef QUINE_PORTABLE |
327 | if (strncmp(*qq, "%!", 2) == 0) |
328 | qq++; |
329 | fp = qq_mkfile(*qq); |
330 | #else |
331 | if (strncmp(*qq, "%!", 2) == 0) { |
332 | fp = qq_mkfile(**q, (int)strtol(*qq + 2, 0, 8)); |
333 | qq++; |
334 | } else |
335 | fp = qq_mkfile(**q, 0666); |
336 | #endif |
337 | |
338 | if (!fp) |
339 | return (-1); |
340 | qq_file(fp, qq); |
341 | if (fclose(fp)) |
342 | return (-1); |
343 | q++; |
344 | } |
345 | fp = qq_mkfile(fn, 0666); |
346 | if (!fp) |
347 | return (-1); |
348 | qq_head(fp); |
349 | qq_body(fp, p); |
350 | qq_tail(fp, qql, fno, fn); |
351 | if (fclose(fp)) |
352 | return (-1); |
353 | return (0); |
354 | } |
355 | |
356 | #endif |
357 | |
358 | /*----- That's all, folks -------------------------------------------------*/ |