3 * $Id: quine.c,v 1.2 1999/05/19 21:11:15 mdw Exp $
5 * Output a program's source code as a quine file
7 * (c) 1999 Mark Wooding
10 /*----- Licensing notice --------------------------------------------------*
12 * This file is part of Quine
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.
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.
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.
29 /*----- Revision history --------------------------------------------------*
32 * Revision 1.2 1999/05/19 21:11:15 mdw
33 * Track gratuitous change to mdwopt interface.
35 * Revision 1.1.1.1 1999/04/28 19:58:07 mdw
40 /*----- Header files ------------------------------------------------------*/
42 /* --- ANSI headers --- */
51 /* --- POSIX headers --- */
53 #ifndef QUINE_PORTABLE
54 #include <sys/types.h>
58 /* --- Local headers --- */
63 /*----- Main code ---------------------------------------------------------*/
67 * Arguments: @const char *f@ = a @printf@-style format string
68 * @...@ = other arguments
72 * Use: Reports an error and hari-kiris. Like @moan@ above, only
76 void die(const char *f, ...)
80 fprintf(stderr, "%s: ", optprog);
81 vfprintf(stderr, f, ap);
87 /* --- @quine__banner@ --- *
89 * Arguments: @FILE *fp@ = file to write on
93 * Use: Writes a banner to the given file.
96 static void quine__banner(FILE *fp)
98 fprintf(fp, "%s quine generator v. 1.1\n", optprog);
101 /* --- @quine__usage@ --- *
103 * Arguments: @FILE *fp@ = file to write on
107 * Use: Writes a usage note to the given file.
110 static void quine__usage(FILE *fp)
112 fprintf(fp, "Usage: %s [-hv0n] [-f FILE] [FILE...]\n", optprog);
115 /* --- @quine__help@ --- *
117 * Arguments: @FILE *fp@ = file to write on
121 * Use: Writes a help message to the given file.
124 static void quine__help(FILE *fp)
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"
137 "The following options are provided:\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",
150 /* --- @quine__readLine@ --- *
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
157 * Returns: Pointer to allocated line if all OK, otherwise zero.
159 * Use: Reads a line of input from the file.
162 static char *quine__readLine(FILE *fp, char **p, size_t *sz,
170 /* --- If I couldn't make my initial buffer, there's no hope --- */
176 die("out of memory");
179 /* --- Skip over initial delimiters --- */
187 if (ch == (unsigned char)*d)
191 /* --- Read characters into the buffer --- */
195 /* --- If not enough buffer space, double the buffer --- *
197 * The plus-one is for the null byte on the end.
200 if (len + 1 >= *sz) {
204 die("out of memory");
209 /* --- Read and check the next byte --- */
216 if (ch == (unsigned char)*d)
221 /* --- Terminate the string and return --- */
228 /* --- @quine__doFile@ --- *
230 * Arguments: @FILE *fp@ = output file handle
231 * @FILE *ifp@ = input file
235 * Use: Outputs the contents of a quine block containing the given
239 static void quine__doFile(FILE *fp, FILE *ifp)
252 if (ch == '\n' || p >= buff + sizeof(buff))
257 for (p = buff; p < buff + sizeof(buff); 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;
268 if (isprint((unsigned char)*p))
271 fprintf(fp, "%%x%02x%%", (unsigned char)*p);
281 fputs("0\n};\n\n", fp);
284 /* --- @quine__file@ --- *
286 * Arguments: @FILE *fp@ = output file handle
287 * @const char *name@ = name of input file
288 * @size_t fno@ = number of this file
292 * Use: Outputs a quine block containing the given file.
295 static void quine__file(FILE *fp, const char *name, size_t fno)
299 fprintf(fp, "static const char *qq__c_%lx[] = {\n", (unsigned long)fno);
301 if ((ifp = fopen(name, "r")) == 0)
302 die("couldn't read `%s': %s", name, strerror(errno));
303 fprintf(fp, "/* Filename: */ \"%s\",\n", name);
305 /* --- Write file's mode to the output --- */
307 #ifndef QUINE_PORTABLE
310 if (stat(name, &st) == 0)
311 fprintf(fp, "/* Mode: */ \"%%!0%03o\",\n", st.st_mode & 07777);
315 quine__doFile(fp, ifp);
318 /* --- Dummy quine library --- */
322 const char *qq_qqlib[] = {
323 "/* --- No quine library available in xquine --- */%n",
331 * Arguments: @int argc@ = number of command line arguments
332 * @char *argv[]@ = pointer to the command line arguments
334 * Returns: Zero if successful, @EXIT_FAILURE@ if not.
336 * Use: Given a number of source files, outputs C code which prints
340 int main(int argc, char *argv[])
346 FILE *infp = 0, *outfp = 0;
355 /* --- Scan the command line arguments --- */
360 static struct option opts[] = {
361 { "help", 0, 0, 'h' },
362 { "version", 0, 0, 'v' },
363 { "null", 0, 0, '0' },
364 { "file", OPTF_ARGREQ, 0, 'f' },
365 { "nofiles", 0, 0, 'n' },
366 { "output", OPTF_ARGREQ, 0, 'o' },
367 { "qqlib", OPTF_ARGREQ, 0, 'l' },
368 #if !defined(QQ_XQUINE) && !defined(QQ_YQUINE)
369 { "quine", 0, 0, 'q' },
370 { "quine-dump", 0, 0, 'Q' },
375 #if !defined(QQ_XQUINE) && !defined(QQ_YQUINE)
376 # define OPTS "hv0nf:o:lqQ"
378 # define OPTS "hv0nf:o:l"
381 i = mdwopt(argc, argv, OPTS, opts, 0, 0, 0);
391 quine__banner(stdout);
408 #if !defined(QQ_XQUINE) && !defined(QQ_YQUINE)
411 die("couldn't build source distribution: %s", strerror(errno));
425 quine__usage(stderr);
429 /* --- Output a file as the qqlib library --- */
432 infp = fopen(qqlibf, "r");
434 die("couldn't open input file `%s': %s", inf, strerror(errno));
438 if (strcmp(outf, "-") == 0)
441 outfp = fopen(outf, "w");
443 die("couldn't open output file `%s': %s", outf, strerror(errno));
445 fputs("/* quine library data */\n\n"
446 "const char *qq_qqlib[] = {\n", outfp);
447 quine__doFile(outfp, infp);
453 /* --- Time to start work --- */
455 if (~f & f_noFiles && (inf || optind == argc)) {
456 if (!inf || strcmp(inf, "-") == 0)
459 infp = fopen(inf, "r");
461 die("couldn't open input file `%s': %s", inf, strerror(errno));
466 if (strcmp(outf, "-") == 0)
469 outfp = fopen(outf, "w");
471 die("couldn't open output file `%s': %s", outf, strerror(errno));
473 /* --- Output a header --- */
476 fputs("/* --- file contents tables --- */\n\n", outfp);
478 /* --- Scan the command line first --- */
480 while (~f & f_noFiles && optind < argc)
481 quine__file(outfp, argv[optind++], files++);
483 /* --- Now read the input file --- */
496 while ((q = quine__readLine(infp, &p, &sz, del)) != 0)
497 quine__file(outfp, q, files++);
500 die("error reading input: %s", strerror(errno));
503 /* --- And output the trailer --- */
505 qq_tail(outfp, qq_qqlib, files,
506 strcmp(outf, "-") == 0 ? "<stdout>" : outf);
510 if (outfp != stdout && fclose(outfp))
511 die("error writing output: %s", strerror(errno));
516 /*----- That's all, folks -------------------------------------------------*/