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 -------------------------------------------------*/