chiark / gitweb /
Infrastructure: Kill CVS Id tags.
[quine] / quine.c
CommitLineData
267c6003 1/* -*-c-*-
267c6003 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
267c6003 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
63void 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
83static 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
97static 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
111static 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",
134fp);
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
149static 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
168skip_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
210done:
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
226static 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
282static 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
309const 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
327int 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' },
5b6fe5a4 351 { "file", OPTF_ARGREQ, 0, 'f' },
267c6003 352 { "nofiles", 0, 0, 'n' },
5b6fe5a4 353 { "output", OPTF_ARGREQ, 0, 'o' },
354 { "qqlib", OPTF_ARGREQ, 0, 'l' },
267c6003 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 -------------------------------------------------*/