chiark / gitweb /
The Great Upheaval -- step 1.
[quine] / quine.c
CommitLineData
267c6003 1/* -*-c-*-
2 *
7f6303ef 3 * $Id$
267c6003 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 $
5b6fe5a4 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
267c6003 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
76void 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
96static 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
110static 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
124static 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",
147fp);
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
162static 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
181skip_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
223done:
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
239static 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
295static 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
322const 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
340int 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' },
5b6fe5a4 364 { "file", OPTF_ARGREQ, 0, 'f' },
267c6003 365 { "nofiles", 0, 0, 'n' },
5b6fe5a4 366 { "output", OPTF_ARGREQ, 0, 'o' },
367 { "qqlib", OPTF_ARGREQ, 0, 'l' },
267c6003 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 -------------------------------------------------*/