chiark / gitweb /
Script to transform CVS sources into buildable source tree.
[quine] / quine.c
CommitLineData
267c6003 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
73void 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
93static 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
107static 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
121static 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",
144fp);
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
159static 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
178skip_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
220done:
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
236static 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
292static 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
319const 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
337int 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 -------------------------------------------------*/