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