chiark / gitweb /
quine.c: Fix double-close bug.
[quine] / quine.c
1 /* -*-c-*-
2  *
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
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
63 void 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
83 static 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
97 static 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
111 static 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",
134 fp);
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
149 static 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
168 skip_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
210 done:
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
226 static 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   }
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
281 static 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
308 const 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
326 int 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' },
350       { "file",         OPTF_ARGREQ,    0,      'f' },
351       { "nofiles",      0,              0,      'n' },
352       { "output",       OPTF_ARGREQ,    0,      'o' },
353       { "qqlib",        OPTF_ARGREQ,    0,      'l' },
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 -------------------------------------------------*/