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