chiark / gitweb /
setup: Remove surplus to requirements.
[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   fclose(ifp);
267
268   fputs("0\n};\n\n", fp);
269 }  
270
271 /* --- @quine__file@ --- *
272  *
273  * Arguments:   @FILE *fp@ = output file handle
274  *              @const char *name@ = name of input file
275  *              @size_t fno@ = number of this file
276  *
277  * Returns:     ---
278  *
279  * Use:         Outputs a quine block containing the given file.
280  */
281
282 static void quine__file(FILE *fp, const char *name, size_t fno)
283 {
284   FILE *ifp;
285
286   fprintf(fp, "static const char *qq__c_%lx[] = {\n", (unsigned long)fno);
287
288   if ((ifp = fopen(name, "r")) == 0)
289     die("couldn't read `%s': %s", name, strerror(errno));
290   fprintf(fp, "/* Filename: */ \"%s\",\n", name);
291
292   /* --- Write file's mode to the output --- */
293
294 #ifndef QUINE_PORTABLE
295   {
296     struct stat st;
297     if (stat(name, &st) == 0)
298       fprintf(fp, "/* Mode: */ \"%%!0%03o\",\n", st.st_mode & 07777);
299   }
300 #endif
301
302   quine__doFile(fp, ifp);
303 }
304
305 /* --- Dummy quine library --- */
306
307 #ifdef QQ_XQUINE
308
309 const char *qq_qqlib[] = {
310   "/* --- No quine library available in xquine --- */%n",
311   0
312 };
313
314 #endif
315
316 /* --- @main@ --- *
317  *
318  * Arguments:   @int argc@ = number of command line arguments
319  *              @char *argv[]@ = pointer to the command line arguments
320  *
321  * Returns:     Zero if successful, @EXIT_FAILURE@ if not.
322  *
323  * Use:         Given a number of source files, outputs C code which prints
324  *              them (and itself).
325  */
326
327 int main(int argc, char *argv[])
328 {
329   char *inf = 0;
330   char *outf = 0;
331   char *qqlibf = 0;
332   unsigned int f = 0;
333   FILE *infp = 0, *outfp = 0;
334   size_t files = 0;
335
336   enum {
337     f_duff = 1,
338     f_null = 2,
339     f_noFiles = 4
340   };
341
342   /* --- Scan the command line arguments --- */
343
344   for (;;) {
345     int i;
346
347     static struct option opts[] = {
348       { "help",         0,              0,      'h' },
349       { "version",      0,              0,      'v' },
350       { "null",         0,              0,      '0' },
351       { "file",         OPTF_ARGREQ,    0,      'f' },
352       { "nofiles",      0,              0,      'n' },
353       { "output",       OPTF_ARGREQ,    0,      'o' },
354       { "qqlib",        OPTF_ARGREQ,    0,      'l' },
355 #if !defined(QQ_XQUINE) && !defined(QQ_YQUINE)
356       { "quine",        0,              0,      'q' },
357       { "quine-dump",   0,              0,      'Q' },
358 #endif
359       { 0,              0,              0,      0 }
360     };
361
362 #if !defined(QQ_XQUINE) && !defined(QQ_YQUINE)
363 #  define OPTS "hv0nf:o:lqQ"
364 #else
365 #  define OPTS "hv0nf:o:l"
366 #endif
367
368     i = mdwopt(argc, argv, OPTS, opts, 0, 0, 0);
369
370     if (i < 0)
371       break;
372
373     switch (i) {
374       case 'h':
375         quine__help(stdout);
376         exit(0);
377       case 'v':
378         quine__banner(stdout);
379         exit(0);
380       case '0':
381         f |= f_null;
382         break;
383       case 'f':
384         inf = optarg;
385         break;
386       case 'o':
387         outf = optarg;
388         break;
389       case 'l':
390         qqlibf = optarg;
391         break;
392       case 'n':
393         f |= f_noFiles;
394         break;
395 #if !defined(QQ_XQUINE) && !defined(QQ_YQUINE)
396       case 'q':
397         if (qq_create())
398           die("couldn't build source distribution: %s", strerror(errno));
399         exit(0);
400       case 'Q':
401         qq_dump(stdout);
402         exit(0);
403         break;
404 #endif
405       default:
406         f |= f_duff;
407         break;
408     }
409   }
410
411   if (f & f_duff) {
412     quine__usage(stderr);
413     exit(EXIT_FAILURE);
414   }
415
416   /* --- Output a file as the qqlib library --- */
417
418   if (qqlibf) {
419     infp = fopen(qqlibf, "r");
420     if (!infp)
421       die("couldn't open input file `%s': %s", inf, strerror(errno));
422
423     if (!outf)
424       outf = "qqlout.c";
425     if (strcmp(outf, "-") == 0)
426       outfp = stdout;
427     else
428       outfp = fopen(outf, "w");
429     if (!outfp)
430       die("couldn't open output file `%s': %s", outf, strerror(errno));
431
432     fputs("/* quine library data */\n\n"
433           "const char *qq_qqlib[] = {\n", outfp);
434     quine__doFile(outfp, infp);
435     fclose(infp);
436     fclose(outfp);
437     exit(0);
438   }
439
440   /* --- Time to start work --- */
441
442   if (~f & f_noFiles && (inf || optind == argc)) {
443     if (!inf || strcmp(inf, "-") == 0)
444       infp = stdin;
445     else
446       infp = fopen(inf, "r");
447     if (!infp)
448       die("couldn't open input file `%s': %s", inf, strerror(errno));
449   }
450
451   if (!outf)
452     outf = "qqout.c";
453   if (strcmp(outf, "-") == 0)
454     outfp = stdout;
455   else
456     outfp = fopen(outf, "w");
457   if (!outfp)
458     die("couldn't open output file `%s': %s", outf, strerror(errno));
459
460   /* --- Output a header --- */
461
462   qq_head(outfp);
463   fputs("/* --- file contents tables --- */\n\n", outfp);
464
465   /* --- Scan the command line first --- */
466
467   while (~f & f_noFiles && optind < argc)
468     quine__file(outfp, argv[optind++], files++);
469
470   /* --- Now read the input file --- */
471
472   if (infp) {
473     char *p = 0;
474     char *q;
475     const char *del;
476     size_t sz = 0;
477
478     if (f & f_null)
479       del = "";
480     else
481       del = " \n\t";
482
483     while ((q = quine__readLine(infp, &p, &sz, del)) != 0)
484       quine__file(outfp, q, files++);
485
486     if (fclose(infp))
487       die("error reading input: %s", strerror(errno));
488   }
489
490   /* --- And output the trailer --- */
491
492   qq_tail(outfp, qq_qqlib, files,
493           strcmp(outf, "-") == 0 ? "<stdout>" : outf);
494
495   /* --- Done --- */
496
497   if (outfp != stdout && fclose(outfp))
498     die("error writing output: %s", strerror(errno));
499
500   return (0);
501 }
502
503 /*----- That's all, folks -------------------------------------------------*/