#include #include #include #include #include /* * This program takes a C source file as its argument and "quines" * it: that is, it outputs (to stdout) the same C source file with the * additional function quine(), which when executed produces (to stdout) * an exact copy of the source code of the output file (including the * function quine() itself). * * Program by Arthur O'Dwyer, 2003. Freeware. */ #define MAX_LINE_LEN 512 #define steq(x,y) (!strcmp(x,y)) static char *Argv0; static char *OutputFilename = NULL; static int QuineSelf = 0; static int PortableArrays = 0; static int SwitchableArrays = 0; static char *intron1; static char *intron3; static char *intron4; static char ns_intron1[] = "void quine(void)\n{\n" " static char q[] = {\n"; static char s_intron1[] = "void quine(int portable)\n{\n" " static char q[] = {\n"; static char intron2[] = "0};\n" " int i;\n" " puts(\""; static char ns_intron3[] = "\");\n" " puts(\"void quine(void)\\n{\\n static char q[] = {\");\n"; static char s_intron3[] = "\");\n" " puts(\"void quine(int portable)\\n{\\n" " static char q[] = {\");\n"; static char p_intron4[] = " for (i=0; q[i]; ++i) {\n" " int nl = (i%10)? ' ': '\\n';\n" " if (q[i] == '\\n')\n" " printf(\"'\\\\n',%c\", nl);\n" " else if (q[i] == '\\t')\n" " printf(\"'\\\\t',%c\", nl);\n" " else if (q[i] == '\\'')\n" " printf(\"'\\\\'',%c\", nl);\n" " else if (q[i] == '\\\\')\n" " printf(\"'\\\\\\\\',%c\", nl);\n" " else\n" " printf(\"'%c',%c\", q[i], nl);\n" " }\n"; static char np_intron4[] = " for (i=0; q[i]; ++i) {\n" " int nl = (i%10)? ' ': '\\n';\n" " printf(\"%d,%c\", q[i], nl);\n" " }\n"; static char intron5[] = " fputs(q, stdout);\n" "}\n\n"; static char intron_switch1[] = " if (portable) goto portable;\n"; static char intron_switch2[] = " goto done;\n" " portable:\n"; static char intron_switch3[] = " done:\n"; int skip_header(FILE *fp); int put_rep(int i, FILE *fp); int put_reps(char *intron, FILE *fp); int process(FILE *fp, FILE *outfp); void do_error(const char *fmat, ...); void do_help(int); int main(int argc, char *argv[]) { int i, j; Argv0 = argv[0]; if (argc <= 1) do_help(0); for (i=1; i < argc; i++) { if (argv[i][0] != '-') break; if (argv[i][1] == '\0') { do_error("This program does not read from stdin.\n" "Use %s -- - to read from a file named -.", Argv0); } if (steq(argv[i]+1, "-")) { ++i; break; } else if (steq(argv[i]+1, "?")) do_help(0); else if (steq(argv[i]+1, "-help")) do_help(0); else if (steq(argv[i]+1, "-man")) do_help(1); else if (steq(argv[i], "-o") || steq(argv[i], "-O")) { if (i >= argc-1) { do_error("Need output filename with -o"); } OutputFilename = argv[++i]; } else { for (j=1; argv[i][j]; ++j) { if (argv[i][j] == 'q' || argv[i][j] == 'Q') QuineSelf = 1; else if (argv[i][j] == 'c' || argv[i][j] == 'C') PortableArrays = 1; else if (argv[i][j] == 'f' || argv[i][j] == 'F') SwitchableArrays = 1; else do_error("Unrecognized option(s) %s", argv[i]); } } } if (QuineSelf) { quine((quine == do_help || PortableArrays)? 1: 0); exit(0); } intron1 = (SwitchableArrays)? s_intron1: ns_intron1; intron3 = (SwitchableArrays)? s_intron3: ns_intron3; intron4 = (PortableArrays)? p_intron4: np_intron4; if (i == argc) do_error("No files to process: '--help' for help"); else if (i <= argc-2) do_error("Too many arguments. This program only takes one" " input file at a time."); for (; i < argc; ++i) { FILE *in = fopen(argv[i], "r"); FILE *out = (OutputFilename)? fopen(OutputFilename, "w"): stdout; if (in == NULL) do_error("Input file '%s' could not be opened.", argv[i]); if (out == NULL) do_error("Output file '%s' could not be opened.", OutputFilename); if (process(in, out) < 0) { if (out != stdout) do_error("Error processing file %s", argv[i]); else return EXIT_FAILURE; } } return 0; } int skip_header(FILE *fp) { /* Skip over "#include" lines */ int c; rewind(fp); for (c = getc(fp); (c=='\n') || (c=='#'); c = getc(fp)) { while (c != '\n') { c = getc(fp); if (c == EOF) return -1; } } return c; } int put_rep(int c, FILE *fp) { static int i = -1; int nl = (i%10)? ' ': '\n'; if (PortableArrays) goto portable_arrays; fprintf(fp, "%d,%c", c, nl); goto done; portable_arrays: if (c == '\n') fprintf(fp, "'\\n',%c", nl); else if (c == '\t') fprintf(fp, "'\\t',%c", nl); else if (c == '\'') fprintf(fp, "'\\'',%c", nl); else if (c == '\\') fprintf(fp, "'\\\\',%c", nl); else fprintf(fp, "'%c',%c", c, nl); done: ++i; return 0; } int put_reps(char *intron, FILE *fp) { int i; for (i=0; intron[i]; ++i) put_rep(intron[i], fp); return 0; } int process(FILE *fp, FILE *outfp) { int c; char *includes; int includes_len; int includes_max; char buf[MAX_LINE_LEN] = {0}; /* Echo "#include" lines verbatim */ rewind(fp); for (c = getc(fp); (c=='\n') || (c=='#'); c = getc(fp)) { while (c != '\n') { putc(c, outfp); c = getc(fp); if (c == EOF) return -1; } putc(c, outfp); } /* Build "#include" string */ rewind(fp); includes = NULL; includes_len = 0; includes_max = 0; if (!fgets(buf, sizeof buf, fp)) return -1; while ((buf[0] == '\n') || (buf[0] == '#')) { int buf_len = strlen(buf); if (includes_len + buf_len >= includes_max-5) { char *tmp; includes_max = 2*includes_max + 16; tmp = realloc(includes, includes_max); if (tmp == NULL) do_error("Out of memory"); includes = tmp; } strcpy(includes+includes_len, buf); includes_len += buf_len; if (includes[includes_len-1] == '\n') { strcpy(includes+includes_len-1, "\\n"); ++includes_len; } if (!fgets(buf, sizeof buf, fp)) return -1; } /* Done building string */ fputs(intron1, outfp); put_reps(intron2, outfp); put_reps(includes, outfp); put_reps(intron3, outfp); if (SwitchableArrays) { put_reps(intron_switch1, outfp); put_reps(np_intron4, outfp); put_reps(intron_switch2, outfp); put_reps(p_intron4, outfp); put_reps(intron_switch3, outfp); } else { put_reps(intron4, outfp); } put_reps(intron5, outfp); /* Skip over "#include" lines */ if ((c = skip_header(fp)) < 0) return -1; /* Print out rest of file */ while (c != EOF) { put_rep(c, outfp); c = getc(fp); } /* print #include lines as text */ fputs(intron2, outfp); fputs(includes, outfp); fputs(intron3, outfp); if (SwitchableArrays) { fputs(intron_switch1, outfp); fputs(np_intron4, outfp); fputs(intron_switch2, outfp); fputs(p_intron4, outfp); fputs(intron_switch3, outfp); } else { fputs(intron4, outfp); } fputs(intron5, outfp); /* done adding stuff; echo rest of file */ if ((c = skip_header(fp)) < 0) return -1; while (c != EOF) { putc(c, outfp); c = getc(fp); } free(includes); return 0; } void do_error(const char *fmat, ...) { va_list ap; printf("%s: ", Argv0); va_start(ap, fmat); vprintf(fmat, ap); printf("\n"); va_end(ap); exit(EXIT_FAILURE); } void do_help(int man) { if (man) goto man; puts("quine [-?] [-cfq] [-o outfile] infile"); puts("Produces a self-replicating program."); puts(" -c: use portable character literals"); puts(" -f: use \"switchable\" quine function"); puts(" -q: produce source code to 'quine' (demo)"); puts(" -o: write output to 'outfile' instead of stdout"); exit(0); man: puts("quine: Produces a self-replicating program.\n"); puts(" This program takes as input a single C program, in a"); puts(" single file, and produces as output the same C file"); puts(" with the addition of the function 'void quine(void)'."); puts(" The 'quine' function will, when run, send the entire"); puts(" source of the program to 'stdout', including the source"); puts(" representing the function 'quine' itself."); puts(" If the input program contains only a call to 'quine()',"); puts(" then the output program will simply print its own"); puts(" source code and then halt."); puts(" Parameter -o or -O can be used to tell this program to"); puts(" send its output to a particular file. The -O parameter"); puts(" does NOT affect the generated 'quine' function; the"); puts(" generated program will still send its output to 'stdout'"); puts(" when it is compiled and run. It only affects where"); puts(" the output from this program goes."); puts(" Option -c or -C tells 'quine' to generate a data array"); puts(" using completely portable character constants, rather"); puts(" than simple numerical constants. This means that the"); puts(" generated 'quine()' function will be slightly larger,"); puts(" but the output will be 100%% pure ISO standard C"); puts(" (assuming the input program was, too)."); puts(" Option -f or -F tells 'quine' to generate a \"switchable\""); puts(" function, with the prototype 'void quine(int)', that"); puts(" will output numeric arrays when called as 'quine(0)' and"); puts(" portable character arrays when called with any other"); puts(" argument."); puts(" Options -c and -f may be used together; in this case, -c"); puts(" will affect whether the data array in the output from"); puts(" this program uses numeric or character constants, but"); puts(" will not have any effect on the output of the 'quine()'"); puts(" function itself."); puts(" Option -q or -Q tells this program to run its own 'quine()'"); puts(" function, producing a complete source listing to the"); puts(" standard output device (e.g., the console)."); puts(" Options -q and -c may be used together, assuming that this"); puts(" program has been bootstrapped as detailed below. Option"); puts(" -f has absolutely no effect when option -q is in effect."); puts(" If you have received the source distribution of 'quine',"); puts(" the -q option will not immediately function. You must"); puts(" bootstrap the self-printer as follows using tools"); puts(" available on most Unix systems:"); puts(""); puts(" echo '#define quine do_help' | cat - quine.c > quine2.c"); puts(" cc -o quine2 quine2.c"); puts(" ./quine2 -o quine2.c -cf quine.c"); puts(" cc -o quine2 quine2.c"); puts(" ./quine2 -qc > quine.c"); puts(" cc -o quine quine.c"); puts(""); puts(" The resulting 'quine' binary should produce 'quine.c'"); puts(" exactly verbatim, when run with the -qc options."); puts(""); exit(0); }