3 * $Id: quine.c,v 1.1 1999/04/28 19:58:07 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.1 1999/04/28 19:58:07 mdw
37 /*----- Header files ------------------------------------------------------*/
39 /* --- ANSI headers --- */
48 /* --- POSIX headers --- */
50 #ifndef QUINE_PORTABLE
51 #include <sys/types.h>
55 /* --- Local headers --- */
60 /*----- Main code ---------------------------------------------------------*/
64 * Arguments: @const char *f@ = a @printf@-style format string
65 * @...@ = other arguments
69 * Use: Reports an error and hari-kiris. Like @moan@ above, only
73 void die(const char *f, ...)
77 fprintf(stderr, "%s: ", optprog);
78 vfprintf(stderr, f, ap);
84 /* --- @quine__banner@ --- *
86 * Arguments: @FILE *fp@ = file to write on
90 * Use: Writes a banner to the given file.
93 static void quine__banner(FILE *fp)
95 fprintf(fp, "%s quine generator v. 1.1\n", optprog);
98 /* --- @quine__usage@ --- *
100 * Arguments: @FILE *fp@ = file to write on
104 * Use: Writes a usage note to the given file.
107 static void quine__usage(FILE *fp)
109 fprintf(fp, "Usage: %s [-hv0n] [-f FILE] [FILE...]\n", optprog);
112 /* --- @quine__help@ --- *
114 * Arguments: @FILE *fp@ = file to write on
118 * Use: Writes a help message to the given file.
121 static void quine__help(FILE *fp)
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"
134 "The following options are provided:\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",
147 /* --- @quine__readLine@ --- *
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
154 * Returns: Pointer to allocated line if all OK, otherwise zero.
156 * Use: Reads a line of input from the file.
159 static char *quine__readLine(FILE *fp, char **p, size_t *sz,
167 /* --- If I couldn't make my initial buffer, there's no hope --- */
173 die("out of memory");
176 /* --- Skip over initial delimiters --- */
184 if (ch == (unsigned char)*d)
188 /* --- Read characters into the buffer --- */
192 /* --- If not enough buffer space, double the buffer --- *
194 * The plus-one is for the null byte on the end.
197 if (len + 1 >= *sz) {
201 die("out of memory");
206 /* --- Read and check the next byte --- */
213 if (ch == (unsigned char)*d)
218 /* --- Terminate the string and return --- */
225 /* --- @quine__doFile@ --- *
227 * Arguments: @FILE *fp@ = output file handle
228 * @FILE *ifp@ = input file
232 * Use: Outputs the contents of a quine block containing the given
236 static void quine__doFile(FILE *fp, FILE *ifp)
249 if (ch == '\n' || p >= buff + sizeof(buff))
254 for (p = buff; p < buff + sizeof(buff); 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;
265 if (isprint((unsigned char)*p))
268 fprintf(fp, "%%x%02x%%", (unsigned char)*p);
278 fputs("0\n};\n\n", fp);
281 /* --- @quine__file@ --- *
283 * Arguments: @FILE *fp@ = output file handle
284 * @const char *name@ = name of input file
285 * @size_t fno@ = number of this file
289 * Use: Outputs a quine block containing the given file.
292 static void quine__file(FILE *fp, const char *name, size_t fno)
296 fprintf(fp, "static const char *qq__c_%lx[] = {\n", (unsigned long)fno);
298 if ((ifp = fopen(name, "r")) == 0)
299 die("couldn't read `%s': %s", name, strerror(errno));
300 fprintf(fp, "/* Filename: */ \"%s\",\n", name);
302 /* --- Write file's mode to the output --- */
304 #ifndef QUINE_PORTABLE
307 if (stat(name, &st) == 0)
308 fprintf(fp, "/* Mode: */ \"%%!0%03o\",\n", st.st_mode & 07777);
312 quine__doFile(fp, ifp);
315 /* --- Dummy quine library --- */
319 const char *qq_qqlib[] = {
320 "/* --- No quine library available in xquine --- */%n",
328 * Arguments: @int argc@ = number of command line arguments
329 * @char *argv[]@ = pointer to the command line arguments
331 * Returns: Zero if successful, @EXIT_FAILURE@ if not.
333 * Use: Given a number of source files, outputs C code which prints
337 int main(int argc, char *argv[])
343 FILE *infp = 0, *outfp = 0;
352 /* --- Scan the command line arguments --- */
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' },
372 #if !defined(QQ_XQUINE) && !defined(QQ_YQUINE)
373 # define OPTS "hv0nf:o:lqQ"
375 # define OPTS "hv0nf:o:l"
378 i = mdwopt(argc, argv, OPTS, opts, 0, 0, 0);
388 quine__banner(stdout);
405 #if !defined(QQ_XQUINE) && !defined(QQ_YQUINE)
408 die("couldn't build source distribution: %s", strerror(errno));
422 quine__usage(stderr);
426 /* --- Output a file as the qqlib library --- */
429 infp = fopen(qqlibf, "r");
431 die("couldn't open input file `%s': %s", inf, strerror(errno));
435 if (strcmp(outf, "-") == 0)
438 outfp = fopen(outf, "w");
440 die("couldn't open output file `%s': %s", outf, strerror(errno));
442 fputs("/* quine library data */\n\n"
443 "const char *qq_qqlib[] = {\n", outfp);
444 quine__doFile(outfp, infp);
450 /* --- Time to start work --- */
452 if (~f & f_noFiles && (inf || optind == argc)) {
453 if (!inf || strcmp(inf, "-") == 0)
456 infp = fopen(inf, "r");
458 die("couldn't open input file `%s': %s", inf, strerror(errno));
463 if (strcmp(outf, "-") == 0)
466 outfp = fopen(outf, "w");
468 die("couldn't open output file `%s': %s", outf, strerror(errno));
470 /* --- Output a header --- */
473 fputs("/* --- file contents tables --- */\n\n", outfp);
475 /* --- Scan the command line first --- */
477 while (~f & f_noFiles && optind < argc)
478 quine__file(outfp, argv[optind++], files++);
480 /* --- Now read the input file --- */
493 while ((q = quine__readLine(infp, &p, &sz, del)) != 0)
494 quine__file(outfp, q, files++);
497 die("error reading input: %s", strerror(errno));
500 /* --- And output the trailer --- */
502 qq_tail(outfp, qq_qqlib, files,
503 strcmp(outf, "-") == 0 ? "<stdout>" : outf);
507 if (outfp != stdout && fclose(outfp))
508 die("error writing output: %s", strerror(errno));
513 /*----- That's all, folks -------------------------------------------------*/