quine.c: Fix double-close bug.
[quine] / quine.c
CommitLineData
267c6003 1/* -*-c-*-
2 *
267c6003 3 * Output a program's source code as a quine file
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
267c6003 27/*----- Header files ------------------------------------------------------*/
28
29/* --- ANSI headers --- */
30
31#include <ctype.h>
32#include <errno.h>
33#include <stdarg.h>
34#include <stdio.h>
35#include <stdlib.h>
36#include <string.h>
37
38/* --- POSIX headers --- */
39
40#ifndef QUINE_PORTABLE
41#include <sys/types.h>
42#include <sys/stat.h>
43#endif
44
45/* --- Local headers --- */
46
47#include "mdwopt.h"
48#include "quine.h"
49
50/*----- Main code ---------------------------------------------------------*/
51
52/* --- @die@ --- *
53 *
54 * Arguments: @const char *f@ = a @printf@-style format string
55 * @...@ = other arguments
56 *
57 * Returns: Never.
58 *
59 * Use: Reports an error and hari-kiris. Like @moan@ above, only
60 * more permanent.
61 */
62
63void die(const char *f, ...)
64{
65 va_list ap;
66 va_start(ap, f);
67 fprintf(stderr, "%s: ", optprog);
68 vfprintf(stderr, f, ap);
69 va_end(ap);
70 putc('\n', stderr);
71 exit(EXIT_FAILURE);
72}
73
74/* --- @quine__banner@ --- *
75 *
76 * Arguments: @FILE *fp@ = file to write on
77 *
78 * Returns: ---
79 *
80 * Use: Writes a banner to the given file.
81 */
82
83static void quine__banner(FILE *fp)
84{
85 fprintf(fp, "%s quine generator v. 1.1\n", optprog);
86}
87
88/* --- @quine__usage@ --- *
89 *
90 * Arguments: @FILE *fp@ = file to write on
91 *
92 * Returns: ---
93 *
94 * Use: Writes a usage note to the given file.
95 */
96
97static void quine__usage(FILE *fp)
98{
99 fprintf(fp, "Usage: %s [-hv0n] [-f FILE] [FILE...]\n", optprog);
100}
101
102/* --- @quine__help@ --- *
103 *
104 * Arguments: @FILE *fp@ = file to write on
105 *
106 * Returns: ---
107 *
108 * Use: Writes a help message to the given file.
109 */
110
111static void quine__help(FILE *fp)
112{
113 quine__banner(fp);
114 putc('\n', fp);
115 quine__usage(fp);
116 putc('\n', fp);
117 fputs(
118"Given a list of source files, constructs a C source file which will write\n"
119"the sources, and itself, when requested. In this way, it is easy to\n"
120"create programs which contain their own source distributions, thus\n"
121"fulfilling the requirements of the GNU General Public License with a\n"
122"single binary program.\n"
123"\n"
124"The following options are provided:\n"
125"\n"
126"-h, --help\t\tdisplay this help message\n"
127"-v, --version\t\tshow the program's version number\n"
128"-0, --null\t\tread null terminated filenames\n"
129"-l, --qqlib\t\tused while bootstrapping `quine'\n"
130"-f, --file=FILE\t\tread filenames from FILE\n"
131"-o, --output=FILE\twrite output quine data to FILE\n"
132"-q, --quine\t\tbuild `quine's source distribution\n"
133"-Q, --quine-dump\twrite `quine's source code to standard output\n",
134fp);
135}
136
137/* --- @quine__readLine@ --- *
138 *
139 * Arguments: @FILE *fp@ = pointer to input file
140 * @char **p@ = address of pointer to set up
141 * @size_t *sz@ = address of array length
142 * @const char *delim@ = array of delimiters
143 *
144 * Returns: Pointer to allocated line if all OK, otherwise zero.
145 *
146 * Use: Reads a line of input from the file.
147 */
148
149static char *quine__readLine(FILE *fp, char **p, size_t *sz,
150 const char *delim)
151{
152 size_t len = 0;
153 char *q;
154 const char *d;
155 int ch;
156
157 /* --- If I couldn't make my initial buffer, there's no hope --- */
158
159 if (!*p) {
160 *sz = 64;
161 *p = malloc(*sz);
162 if (!*p)
163 die("out of memory");
164 }
165
166 /* --- Skip over initial delimiters --- */
167
168skip_delims:
169 ch = getc(fp);
170 if (ch == EOF)
171 return (0);
172 d = delim;
173 do {
174 if (ch == (unsigned char)*d)
175 goto skip_delims;
176 } while (*d++);
177
178 /* --- Read characters into the buffer --- */
179
180 for (;;) {
181
182 /* --- If not enough buffer space, double the buffer --- *
183 *
184 * The plus-one is for the null byte on the end.
185 */
186
187 if (len + 1 >= *sz) {
188 *sz <<= 1;
189 q = realloc(p, *sz);
190 if (!q)
191 die("out of memory");
192 *p = q;
193 }
194 (*p)[len++] = ch;
195
196 /* --- Read and check the next byte --- */
197
198 ch = getc(fp);
199 if (ch == EOF)
200 goto done;
201 d = delim;
202 do {
203 if (ch == (unsigned char)*d)
204 goto done;
205 } while (*d++);
206 }
207
208 /* --- Terminate the string and return --- */
209
210done:
211 (*p)[len++] = 0;
212 return (*p);
213}
214
215/* --- @quine__doFile@ --- *
216 *
217 * Arguments: @FILE *fp@ = output file handle
218 * @FILE *ifp@ = input file
219 *
220 * Returns: ---
221 *
222 * Use: Outputs the contents of a quine block containing the given
223 * file.
224 */
225
226static void quine__doFile(FILE *fp, FILE *ifp)
227{
228 char *p;
229 char buff[80];
230 int ch;
231
232 for (;;) {
233 p = buff;
234 ch = getc(ifp);
235 if (ch == EOF)
236 break;
237 for (;;) {
238 *p++ = ch;
239 if (ch == '\n' || p >= buff + sizeof(buff))
240 break;
241 ch = getc(ifp);
242 }
243 fputc('\"', fp);
244 for (p = buff; p < buff + sizeof(buff); p++) {
245 switch (*p) {
246 case '\"': fputs("%q", fp); break;
247 case '\\': fputs("%b", fp); break;
248 case '\t': fputs("%t", fp); break;
249 case '\n': fputs("%n", fp); break;
250 case '\a': fputs("%a", fp); break;
251 case '\r': fputs("%r", fp); break;
252 case '\0': fputs("%0", fp); break;
253 case '%': fputs("%%", fp); break;
254 default:
255 if (isprint((unsigned char)*p))
256 fputc(*p, fp);
257 else
258 fprintf(fp, "%%x%02x%%", (unsigned char)*p);
259 break;
260 }
261 if (*p == '\n')
262 break;
263 }
264 fputs("\",\n", fp);
265 }
267c6003 266
267 fputs("0\n};\n\n", fp);
268}
269
270/* --- @quine__file@ --- *
271 *
272 * Arguments: @FILE *fp@ = output file handle
273 * @const char *name@ = name of input file
274 * @size_t fno@ = number of this file
275 *
276 * Returns: ---
277 *
278 * Use: Outputs a quine block containing the given file.
279 */
280
281static void quine__file(FILE *fp, const char *name, size_t fno)
282{
283 FILE *ifp;
284
285 fprintf(fp, "static const char *qq__c_%lx[] = {\n", (unsigned long)fno);
286
287 if ((ifp = fopen(name, "r")) == 0)
288 die("couldn't read `%s': %s", name, strerror(errno));
289 fprintf(fp, "/* Filename: */ \"%s\",\n", name);
290
291 /* --- Write file's mode to the output --- */
292
293#ifndef QUINE_PORTABLE
294 {
295 struct stat st;
296 if (stat(name, &st) == 0)
297 fprintf(fp, "/* Mode: */ \"%%!0%03o\",\n", st.st_mode & 07777);
298 }
299#endif
300
301 quine__doFile(fp, ifp);
302}
303
304/* --- Dummy quine library --- */
305
306#ifdef QQ_XQUINE
307
308const char *qq_qqlib[] = {
309 "/* --- No quine library available in xquine --- */%n",
310 0
311};
312
313#endif
314
315/* --- @main@ --- *
316 *
317 * Arguments: @int argc@ = number of command line arguments
318 * @char *argv[]@ = pointer to the command line arguments
319 *
320 * Returns: Zero if successful, @EXIT_FAILURE@ if not.
321 *
322 * Use: Given a number of source files, outputs C code which prints
323 * them (and itself).
324 */
325
326int main(int argc, char *argv[])
327{
328 char *inf = 0;
329 char *outf = 0;
330 char *qqlibf = 0;
331 unsigned int f = 0;
332 FILE *infp = 0, *outfp = 0;
333 size_t files = 0;
334
335 enum {
336 f_duff = 1,
337 f_null = 2,
338 f_noFiles = 4
339 };
340
341 /* --- Scan the command line arguments --- */
342
343 for (;;) {
344 int i;
345
346 static struct option opts[] = {
347 { "help", 0, 0, 'h' },
348 { "version", 0, 0, 'v' },
349 { "null", 0, 0, '0' },
5b6fe5a4 350 { "file", OPTF_ARGREQ, 0, 'f' },
267c6003 351 { "nofiles", 0, 0, 'n' },
5b6fe5a4 352 { "output", OPTF_ARGREQ, 0, 'o' },
353 { "qqlib", OPTF_ARGREQ, 0, 'l' },
267c6003 354#if !defined(QQ_XQUINE) && !defined(QQ_YQUINE)
355 { "quine", 0, 0, 'q' },
356 { "quine-dump", 0, 0, 'Q' },
357#endif
358 { 0, 0, 0, 0 }
359 };
360
361#if !defined(QQ_XQUINE) && !defined(QQ_YQUINE)
362# define OPTS "hv0nf:o:lqQ"
363#else
364# define OPTS "hv0nf:o:l"
365#endif
366
367 i = mdwopt(argc, argv, OPTS, opts, 0, 0, 0);
368
369 if (i < 0)
370 break;
371
372 switch (i) {
373 case 'h':
374 quine__help(stdout);
375 exit(0);
376 case 'v':
377 quine__banner(stdout);
378 exit(0);
379 case '0':
380 f |= f_null;
381 break;
382 case 'f':
383 inf = optarg;
384 break;
385 case 'o':
386 outf = optarg;
387 break;
388 case 'l':
389 qqlibf = optarg;
390 break;
391 case 'n':
392 f |= f_noFiles;
393 break;
394#if !defined(QQ_XQUINE) && !defined(QQ_YQUINE)
395 case 'q':
396 if (qq_create())
397 die("couldn't build source distribution: %s", strerror(errno));
398 exit(0);
399 case 'Q':
400 qq_dump(stdout);
401 exit(0);
402 break;
403#endif
404 default:
405 f |= f_duff;
406 break;
407 }
408 }
409
410 if (f & f_duff) {
411 quine__usage(stderr);
412 exit(EXIT_FAILURE);
413 }
414
415 /* --- Output a file as the qqlib library --- */
416
417 if (qqlibf) {
418 infp = fopen(qqlibf, "r");
419 if (!infp)
420 die("couldn't open input file `%s': %s", inf, strerror(errno));
421
422 if (!outf)
423 outf = "qqlout.c";
424 if (strcmp(outf, "-") == 0)
425 outfp = stdout;
426 else
427 outfp = fopen(outf, "w");
428 if (!outfp)
429 die("couldn't open output file `%s': %s", outf, strerror(errno));
430
431 fputs("/* quine library data */\n\n"
432 "const char *qq_qqlib[] = {\n", outfp);
433 quine__doFile(outfp, infp);
434 fclose(infp);
435 fclose(outfp);
436 exit(0);
437 }
438
439 /* --- Time to start work --- */
440
441 if (~f & f_noFiles && (inf || optind == argc)) {
442 if (!inf || strcmp(inf, "-") == 0)
443 infp = stdin;
444 else
445 infp = fopen(inf, "r");
446 if (!infp)
447 die("couldn't open input file `%s': %s", inf, strerror(errno));
448 }
449
450 if (!outf)
451 outf = "qqout.c";
452 if (strcmp(outf, "-") == 0)
453 outfp = stdout;
454 else
455 outfp = fopen(outf, "w");
456 if (!outfp)
457 die("couldn't open output file `%s': %s", outf, strerror(errno));
458
459 /* --- Output a header --- */
460
461 qq_head(outfp);
462 fputs("/* --- file contents tables --- */\n\n", outfp);
463
464 /* --- Scan the command line first --- */
465
466 while (~f & f_noFiles && optind < argc)
467 quine__file(outfp, argv[optind++], files++);
468
469 /* --- Now read the input file --- */
470
471 if (infp) {
472 char *p = 0;
473 char *q;
474 const char *del;
475 size_t sz = 0;
476
477 if (f & f_null)
478 del = "";
479 else
480 del = " \n\t";
481
482 while ((q = quine__readLine(infp, &p, &sz, del)) != 0)
483 quine__file(outfp, q, files++);
484
485 if (fclose(infp))
486 die("error reading input: %s", strerror(errno));
487 }
488
489 /* --- And output the trailer --- */
490
491 qq_tail(outfp, qq_qqlib, files,
492 strcmp(outf, "-") == 0 ? "<stdout>" : outf);
493
494 /* --- Done --- */
495
496 if (outfp != stdout && fclose(outfp))
497 die("error writing output: %s", strerror(errno));
498
499 return (0);
500}
501
502/*----- That's all, folks -------------------------------------------------*/