quine.c: Fix double-close bug.
[quine] / qqlib.c
1 /* -*-c-*-
2 *
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
32 /*----- Header files ------------------------------------------------------*/
33
34 /* --- ANSI headers --- */
35
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 -------------------------------------------------*/