quine.c: Fix double-close bug.
[quine] / qqlib.c
CommitLineData
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
76void 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
122void 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
137void 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
152void 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
184void 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
234FILE *qq_mkfile(const char *fn)
235{
236 return (fopen(fn, "w"));
237}
238#else
239FILE *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
276static 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
304static 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 -------------------------------------------------*/