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