#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, 2001. Freeware. */ #define MAX_LINE_LEN 100 #define MAX_INCLUDE_LEN 200 #define steq(x,y) (!strcmp(x,y)) static char *Argv0; static char *OutputFilename = NULL; static char intron1[] = "void quine() {\nstatic char q[] = {\n"; static char intron2[] = "0};\nint i;\nprintf(\""; static char intron3[] = "\");\nprintf(\"void quine() {\\nstatic char q[] = {\\n\");\n" "for (i=0; q[i]; i++)\n printf(\"%i,%c\", q[i], (i%10)?' ':'\\n');" "\nprintf(\"%s\", q);\n" "}\n\n"; 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 -.\n", 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], "-q") || steq(argv[i], "-Q")) { quine(); exit(0); } else if (steq(argv[i], "-o") || steq(argv[i], "-O")) { if (i >= argc-1) { do_error("Need output filename with -o\n"); } OutputFilename = argv[++i]; } else { do_error("Unrecognized option(s) %s\n", argv[i]); } } if (i == argc) do_error("No files to process\n"); else if (i <= argc-2) do_error("Too many arguments. This program only takes one" " input file at a time.\n"); 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.\n", argv[i]); if (out == NULL) do_error("Output file '%s' could not be opened.\n", OutputFilename); process(in, out); } return 0; } int process(FILE *fp, FILE *outfp) { int c, i; char includes[MAX_INCLUDE_LEN] = {0}; 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); } putc(c, outfp); } /* Build "#include" string */ rewind(fp); fgets(buf, sizeof buf, fp); while ((buf[0]=='\n') || (buf[0]=='#')) { strcat(includes, buf); strcpy(includes+strlen(includes)-1, "\\n"); fgets(buf, sizeof buf, fp); } /* Done building string */ fprintf(outfp, "%s", intron1); for (i=0; intron2[i]; ++i) fprintf(outfp, "%i,\n", intron2[i]); for (i=0; includes[i]; ++i) fprintf(outfp, "%i,\n", includes[i]); for (i=0; intron3[i]; ++i) fprintf(outfp, "%i,\n", intron3[i]); /* Skip over "#include" lines */ rewind(fp); for (c = getc(fp); (c=='\n') || (c=='#'); c = getc(fp)) { while (c != '\n') c = getc(fp); } /* Print out rest of file */ while (c != EOF) { fprintf(outfp, "%i,\n", c); c = getc(fp); } /* print #include lines as text */ fprintf(outfp, "%s", intron2); fprintf(outfp, "%s", includes); fprintf(outfp, "%s", intron3); /* done adding stuff; echo rest of file */ rewind(fp); for (c = getc(fp); (c=='\n') || (c=='#'); c = getc(fp)) { while (c != '\n') c = getc(fp); } while (c != EOF) { putc(c, outfp); c = getc(fp); } return 0; } void do_error(const char *fmat, ...) { va_list ap; printf("%s: ", Argv0); va_start(ap, fmat); vprintf(fmat, ap); va_end(ap); exit(EXIT_FAILURE); } void do_help(int man) { if (man) goto man; printf("quine [-?] [-q] [-o outfile] infile\n"); printf("Produces a self-replicating program.\n"); printf(" -q: produce source code to 'quine' (demo)\n"); printf(" -o: write output to 'outfile' instead of stdout\n"); exit(0); man: printf("quine: Produces a self-replicating program.\n"); printf("\n"); printf(" This program takes as input a single C program, in a\n"); printf(" single file, and produces as output the same C file\n"); printf(" with the addition of the function 'void quine(void)'.\n"); printf(" The 'quine' function will, when run, send the entire\n"); printf(" source of the program to 'stdout', including the source\n"); printf(" representing the function 'quine' itself.\n"); printf(" If the input program contains only a call to 'quine()',\n"); printf(" then the output program will simply print its own\n"); printf(" source code and then halt.\n"); printf(" Parameter -o or -O can be used to tell this program to\n"); printf(" send its output to a particular file. The -O parameter\n"); printf(" does NOT affect the generated 'quine' function; the\n"); printf(" generated program will still send its output to 'stdout'\n"); printf(" when it is compiled and run.\n"); printf(" Option -q or -Q tells this program to run its own 'quine()'\n"); printf(" function, producing a complete source listing to the\n"); printf(" standard output device (e.g., the console).\n"); printf(" If you have received the source distribution of 'quine',\n"); printf(" the -Q option will not immediately function. You must\n"); printf(" bootstrap the self-printer as follows using tools\n"); printf(" available on most Unix systems:\n"); printf("\n"); printf(" echo '#define quine rand' | cat - quine.c > quine2.c\n"); printf(" cc -o quine2 quine2.c\n"); printf(" ./quine2 -o quine2.c quine.c\n"); printf(" cc -o quine2 quine2.c\n"); printf(" ./quine2 -q > quine.c\n"); printf(" cc -o quine quine.c\n"); printf("\n"); printf(" The resulting 'quine' binary should produce 'quine.c'\n"); printf(" exactly verbatim, when run with the -Q option.\n"); printf("\n"); exit(0); }