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