From 267c6003e8f73b1e1d1f918a8d9e149db26e9a6a Mon Sep 17 00:00:00 2001 Message-Id: <267c6003e8f73b1e1d1f918a8d9e149db26e9a6a.1715627307.git.mdw@distorted.org.uk> From: Mark Wooding Date: Wed, 28 Apr 1999 19:58:06 +0000 Subject: [PATCH] Initial revision Organization: Straylight/Edgeware From: mdw --- .cvsignore | 6 + .links | 8 + AUTHORS | 0 ChangeLog | 0 Makefile.am | 107 +++++++++++ README | 72 +++++++ ansicquine.c | 2 + bournequine | 3 + configure.in | 41 ++++ qqlib.c | 356 ++++++++++++++++++++++++++++++++++ quine.c | 513 +++++++++++++++++++++++++++++++++++++++++++++++++ quine.h | 161 ++++++++++++++++ rexxquine.exec | 4 + 13 files changed, 1273 insertions(+) create mode 100644 .cvsignore create mode 100644 .links create mode 100644 AUTHORS create mode 100644 ChangeLog create mode 100644 Makefile.am create mode 100644 README create mode 100644 ansicquine.c create mode 100755 bournequine create mode 100644 configure.in create mode 100644 qqlib.c create mode 100644 quine.c create mode 100644 quine.h create mode 100644 rexxquine.exec diff --git a/.cvsignore b/.cvsignore new file mode 100644 index 0000000..355595b --- /dev/null +++ b/.cvsignore @@ -0,0 +1,6 @@ +Makefile.in +aclocal.m4 +build +configure +qqlout.c +qqout.c diff --git a/.links b/.links new file mode 100644 index 0000000..21d12bd --- /dev/null +++ b/.links @@ -0,0 +1,8 @@ +COPYING +INSTALL +install-sh +mdwopt.c +mdwopt.h +missing +mkinstalldirs +missing diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..e69de29 diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..e69de29 diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..cc5036a --- /dev/null +++ b/Makefile.am @@ -0,0 +1,107 @@ +## Process with `automake' to generate `Makefile.in' +## -*-makefile-*- +## +## $Id: Makefile.am,v 1.1 1999/04/28 19:58:07 mdw Exp $ +## +## Makefile for Quine +## +## (c) 1999 Mark Wooding +## + +##----- Licensing notice ---------------------------------------------------- +## +## This file is part of Quine. +## +## Quine is free software; you can redistribute it and/or modify it +## under the terms of the GNU General Public License as published by +## the Free Software Foundation; either version 2 of the License, or +## (at your option) any later version. +## +## Quine is distributed in the hope that it will be useful, but +## WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with Quine; if not, write to the Free Software Foundation, +## Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +##----- Revision history ---------------------------------------------------- +## +## $Log: Makefile.am,v $ +## Revision 1.1 1999/04/28 19:58:07 mdw +## Initial revision +## + +AUTOMAKE_OPTIONS = foreign + +## --- What needs installing --- + +bin_PROGRAMS = quine +noinst_PROGRAMS = ansicquine +include_HEADERS = quine.h + +## --- How to build the program --- + +quine_SOURCES = quine.c qqout.c mdwopt.c quine.h mdwopt.h +ansicquine_SOURCES = ansicquine.c +EXTRA_DIST = qqlib.c qqout.c qqlout.c rexxquine.exec bournequine +CLEANFILES = xquine yquine +MAINTAINERCLEANFILES = qqout.c qqlout.c $(srcdir)/qqout.c $(srcdir)/qqlout.c + +## --- I need recursive makeness --- +## +## In this way, I can rebuild `xquine' and `yquine' only when they really +## get needed. + +@SET_MAKE@ + +## --- Some hacking for the bootstrapping process --- +## +## The outputtable library gets built from `qqlib.c'. I therefore have to +## build a `quine' which can do this. This is `xquine'. Then, I can +## build a `yquine' which is capable of writing full `qqout.c' files, with +## which I can build the final glorious `quine'. + +xquine: xquine.o qqlib.o mdwopt.o + $(LINK) xquine.o qqlib.o mdwopt.o +xquine.o: quine.c + $(COMPILE) -c -DQQ_XQUINE $(srcdir)/quine.c -o xquine.o + +yquine: yquine.o qqlib.o qqlout.o mdwopt.o + $(LINK) yquine.o qqlib.o qqlout.o mdwopt.o +yquine.o: quine.c + $(COMPILE) -c -DQQ_YQUINE $(srcdir)/quine.c -o yquine.o + +## --- The `qqlout.c' file --- +## +## The contents of `qqlib.c' are included in every `qqout.c' file we write. +## But I've got to get it from somewhere so that I can write it to the first +## `qqout.c' file. The solution is, as described above, to use a cut-down +## `quine' program which can just about build `qqlout.c' which contains only +## the library. + +qqlout.c: qqlib.c + if [ -z "$(qq_xquine)" ]; then \ + $(MAKE) qq_xquine=true xquine; \ + else :; fi + ./xquine --qqlib $(srcdir)/qqlib.c -o qqlout.c + +## --- The `qqout.c' file --- +## +## This contains the complete source code for the program. + +qqout.c: quine.c qqlout.c qqlib.c mdwopt.c quine.h mdwopt.h + if [ -z "$(qq_yquine)" ]; then \ + $(MAKE) qq_yquine=true yquine; \ + else :; fi + touch qqout.c + -ln qqout.c qqlout.c $(srcdir) + $(MAKE) distdir + rm qqout.c + find $(distdir) \( -type f -o -type l \) ! -name qqout.c -print | \ + ./yquine -o $(distdir)/qqout.c + ln $(distdir)/qqout.c qqout.c + rm -rf $(distdir) + +##----- That's all, folks --------------------------------------------------- diff --git a/README b/README new file mode 100644 index 0000000..c7d2f6a --- /dev/null +++ b/README @@ -0,0 +1,72 @@ +Quine +~~~~~ + +The `Jargon File' (well, my GNU Info edition) defines a `quine' as: + +:quine: /kwi:n/ n. [from the name of the logician Willard + van Orman Quine, via Douglas Hofstadter] A program that generates a + copy of its own source text as its complete output. Devising the + shortest possible quine in some given programming language is a + common hackish amusement. Here is one classic quine: + + ((lambda (x) + (list x (list (quote quote) x))) + (quote + (lambda (x) + (list x (list (quote quote) x))))) + + This one works in LISP or Scheme. It's relatively easy to write + quines in other languages such as Postscript which readily handle + programs as data; much harder (and thus more challenging!) in + languages like C which do not. Here is a classic C quine for ASCII + machines: + + char*f="char*f=%c%s%c;main() + {printf(f,34,f,34,10);}%c"; + main(){printf(f,34,f,34,10);} + + For excruciatingly exact quinishness, remove the interior line + breaks. Some infamous *note Obfuscated C Contest:: entries have been + quines that reproduced in exotic ways. + +Small quines, like the one above (or the author's strictly conformant +ANSI C version) have been around for ages. However, I'm not aware of +any programs which provide quine support for arbitrary pieces of +software. The `quine' program is intended to remedy this omission. + +Building the package is easy. Run the `configure' script, and then say +`make'. This then gowes through the rather convoluted build process +automatically, and produces a binary `quine'. + +As an example of `quine's ability to enquine programs, it is (of course) +a quine itself. To demonstrate this, run the command + + ./quine --quine + +It should (very quickly) create a `quine' source distribution in the +current directory. + +Conferring quineishness upon other programs isn't hard. You require the +`quine.h' file from the source distribution (`make install' will install +it in some standard place for you), and the `quine' binary. + +The `quine' program works by writing a file describing the contents of +your source files, and the code necessary to recreate them. It reads +the names of your sources from standard input and writes (by default) to +`qqout.c'; thus an incantation like + + find . -type f -print | quine + +should be sufficient to build the `qqout.c' file. Then all you need to +do to your code to output the source code is call `qq_create' to build a +distribution directory, or `qq_dump(STREAM)' to dump the source code to +the output stream STREAM. Easy, no? + +The author's own preference for creating source distributions is to +create a distribution directory (with `make distdir') and use `find' +within that to build the `qqout.c' file. See the `Makefile.am' to see +how this was achieved for `quine' itself. (Note that `quine' has to use +itself to build its `qqout.c' file, which makes the whole procedure a +little more interesting.) + +I'll admit it: this isn't what you'd call a `useful' program. diff --git a/ansicquine.c b/ansicquine.c new file mode 100644 index 0000000..4044a74 --- /dev/null +++ b/ansicquine.c @@ -0,0 +1,2 @@ +#include +int main(void){char n='\n';char b='\\';char q='"';char*p="#include %cint main(void){char n='%cn';char b='%c%c';char q='%c';char*p=%c%s%c;printf(p,n,b,b,b,q,q,p,q,n);return 0;}%c";printf(p,n,b,b,b,q,q,p,q,n);return 0;} diff --git a/bournequine b/bournequine new file mode 100755 index 0000000..9ed62fe --- /dev/null +++ b/bournequine @@ -0,0 +1,3 @@ +#! /bin/sh +q="'" qq='echo \#! /bin/sh;echo q=\"$q\" qq=${q}$qq$q;echo eval \$qq' +eval $qq diff --git a/configure.in b/configure.in new file mode 100644 index 0000000..5f9773c --- /dev/null +++ b/configure.in @@ -0,0 +1,41 @@ +dnl -*-fundamental-*- +dnl +dnl $Id: configure.in,v 1.1 1999/04/28 19:58:07 mdw Exp $ +dnl +dnl Configure script for Quine +dnl +dnl (c) 1999 Mark Wooding +dnl + +dnl----- Licensing notice --------------------------------------------------- +dnl +dnl This file is part of Quine. +dnl +dnl Quine is free software; you can redistribute it and/or modify it +dnl under the terms of the GNU General Public License as published by +dnl the Free Software Foundation; either version 2 of the License, or +dnl (at your option) any later version. +dnl +dnl Quine is distributed in the hope that it will be useful, but +dnl WITHOUT ANY WARRANTY; without even the implied warranty of +dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +dnl GNU General Public License for more details. +dnl +dnl You should have received a copy of the GNU General Public License +dnl along with Quine; if not, write to the Free Software Foundation, +dnl Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +dnl----- Revision history --------------------------------------------------- +dnl +dnl $Log: configure.in,v $ +dnl Revision 1.1 1999/04/28 19:58:07 mdw +dnl Initial revision +dnl + +AC_INIT(quine.c) +AM_INIT_AUTOMAKE(quine, 1.1.0) +AC_ARG_PROGRAM +AC_PROG_CC +AC_PROG_MAKE_SET +mdw_GCC_FLAGS +AC_OUTPUT(Makefile) diff --git a/qqlib.c b/qqlib.c new file mode 100644 index 0000000..9edacbc --- /dev/null +++ b/qqlib.c @@ -0,0 +1,356 @@ +/* -*-c-*- + * + * $Id: qqlib.c,v 1.1 1999/04/28 19:58:07 mdw Exp $ + * + * Useful library of tools for quines + * + * (c) 1999 Mark Wooding + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of Quine + * + * Quine is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Quine is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Quine; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * However, when this source text is embedded in a file by the Quine + * program, you may do anything you like with the resulting text; + * distribution and modification are not limited by the General Public + * License. + */ + +/*----- Revision history --------------------------------------------------* + * + * $Log: qqlib.c,v $ + * Revision 1.1 1999/04/28 19:58:07 mdw + * Initial revision + * + */ + +/*----- Header files ------------------------------------------------------*/ + +/* --- ANSI headers --- */ + +#include +#include +#include +#include +#include + +/* --- POSIX headers --- */ + +#ifndef QUINE_PORTABLE +# include +# include +# include +# include +#endif + +/* --- Local headers --- */ + +#include "quine.h" + +/*----- Macro hacking -----------------------------------------------------*/ + +#ifdef QUINE_PORTABLE +# define PORT(x) x +# define NPORT(x) +#else +# define PORT(x) +# define NPORT(x) x +#endif + +/*----- Main code ---------------------------------------------------------*/ + +/* --- @qq_xlate@ --- * + * + * Arguments: @FILE *fp@ = output file handle + * @const char *p@ = pointer to encoded text + * + * Returns: --- + * + * Use: Writes the decoded string to the given file handle. + */ + +void qq_xlate(FILE *fp, const char *p) +{ + while (*p) { + if (*p == '%') { + p++; + switch (*p) { + case 'q': fputc('\"', fp); break; + case 'b': fputc('\\', fp); break; + case 't': fputc('\t', fp); break; + case 'n': fputc('\n', fp); break; + case 'a': fputc('\a', fp); break; + case 'r': fputc('\r', fp); break; + case '0': fputc('\0', fp); break; + case '%': fputc('%', fp); break; + case 'x': { + unsigned char ch = 0; + p++; + while (*p != '%') { + unsigned int x = *p++; + x -= '0'; + if (x >= 10) x -= 'a' - '0' - 10; + if (x >= 16) x -= 'A' - 'a'; + ch = (ch << 4) + x; + } + fputc(ch, fp); + } break; + default: + p--; + break; + } + } else + fputc(*p, fp); + p++; + } +} + +/* --- @qq_file@ --- * + * + * Arguments: @FILE *fp@ = output file handle + * @const char **p@ = pointer to the output array + * + * Returns: --- + * + * Use: Writes the contents of a file to the output. + */ + +void qq_file(FILE *fp, const char **p) +{ + while (*p) + qq_xlate(fp, *p++); +} + +/* --- @qq_head@ --- * + * + * Arguments: @FILE *fp@ = output file handle + * + * Returns: --- + * + * Use: Writes the head of a `qqout.c' file. + */ + +void qq_head(FILE *fp) +{ + fputs("/* quine output file */\n\n", fp); +} + +/* --- @qq_body@ --- * + * + * Arguments: @FILE *fp@ = output file handle + * @const char ***p@ = pointer to main table + * + * Returns: --- + * + * Use: Writes the body table of a `qqout.c' file. + */ + +void qq_body(FILE *fp, const char ***p) +{ + size_t fno = 0; + const char **q; + + fputs("/* --- file contents tables --- */\n\n", fp); + while (*p) { + q = *p; + fprintf(fp, "static const char *qq__c_%lx[] = {\n", (unsigned long)fno); + fprintf(fp, "/* Filename: */ \"%s\",\n", *q++); + if (strncmp(*q, "%!", 2) == 0) + fprintf(fp, "/* Mode: */ \"%s\",\n", *q++); + while (*q) + fprintf(fp, "\"%s\",\n", *q++); + fputs("0\n};\n\n", fp); + p++; + fno++; + } +} + +/* --- @qq_tail@ --- * + * + * Arguments: @FILE *fp@ = output file handle + * @const char **qql@ = pointer to qqlib file array + * @size_t fno@ = number of files written + * @const char *fn@ = name of `qqout.c' file + * + * Returns: --- + * + * Use: Writes the head of a `qqout.c' file. + */ + +void qq_tail(FILE *fp, const char **qql, size_t fno, const char *fn) +{ + const char **p; + size_t i; + + /* --- Write out the qqlib array --- */ + + fputs("/* --- qqlib code --- */\n\n" + "const char *qq_qqlib[] = {\n", fp); + for (p = qql; *p; p++) + fprintf(fp, "\"%s\",\n", *p); + fputs("0\n};\n\n", fp); + + /* --- Build the main array --- */ + + fputs("/* --- table of files to output --- */\n\n" + "const char **qq_table[] = {\n", fp); + for (i = 0; i < fno; i++) + fprintf(fp, " qq__c_%lx,\n", (unsigned long)i); + fputs(" 0\n};\n\n", fp); + + /* --- Now output the qqlib code --- */ + + fputs("#define QQ_OUT\n\n", fp); + qq_file(fp, qql); + + /* --- Finally write the code to write everything out --- */ + + fprintf(fp, + "/* --- quine output routines --- */\n\n" + "void qq_dump(FILE *fp) {\n" + " qq__dump(fp, qq_table, qq_qqlib, \"%s\", %lu);\n" + "}\n" + "int qq_create(void) {\n" + " return (qq__create(qq_table, qq_qqlib, \"%s\", %lu));\n" + "}\n\n", + fn, (unsigned long)fno, fn, (unsigned long)fno); +} + +/* --- @qq_mkfile@ --- * + * + * Arguments: @const char *fn@ = pointer to a filename + * @int mode@ = file mode to use (only in Unix-specific version) + * + * Returns: A handle for the created file. + * + * Use: Creates a file, and leading directories and stuff. + */ + +#ifdef QUINE_PORTABLE +FILE *qq_mkfile(const char *fn) +{ + return (fopen(fn, "w")); +} +#else +FILE *qq_mkfile(const char *fn, int mode) +{ + char buf[FILENAME_MAX]; + char *p; + int fd; + + strcpy(buf, fn); + p = strchr(buf, '/'); + for (;;) { + if (!p) + break; + *p = 0; + mkdir(buf, 0777); + *p = '/'; + p = strchr(p + 1, '/'); + } + + fd = open(fn, O_WRONLY|O_CREAT|O_TRUNC, mode); + return (fd >=0 ? fdopen(fd, "w") : 0); +} +#endif + +#ifdef QQ_OUT + +/* --- @qq__dump@ --- * + * + * Arguments: @FILE *fp@ = file to dump on + * @const char ***p@ = pointer to main table + * @const char **qql@ = pointer to qqlib code + * @const char *fn@ = name of `qqout.c' file + * @size_t fno@ = number of files to be output + * + * Returns: --- + * + * Use: Outputs the entire source code to a given file. + */ + +static void qq__dump(FILE *fp, const char ***p, const char **qql, + const char *fn, size_t fno) +{ + const char ***q = p; + + while (*q) { + fprintf(fp, "******** %s\n\n", **q); + qq_file(fp, *q + 1); + q++; + } + fprintf(fp, "******** %s\n\n", fn); + qq_head(fp); + qq_body(fp, p); + qq_tail(fp, qql, fno, fn); +} + +/* --- @qq__create@ --- * + * + * Arguments: @const char ***p@ = pointer to main table + * @const char **qql@ = pointer to qqlib code + * @const char *fn@ = name of `qqout.c' file + * @size_t fno@ = number of files to be output + * + * Returns: Zero if all went well, else nonzero. + * + * Use: Rebuilds the original source distribution. + */ + +static int qq__create(const char ***p, const char **qql, + const char *fn, size_t fno) +{ + FILE *fp; + const char ***q = p; + + while (*q) { + const char **qq = *q + 1; + +#ifdef QUINE_PORTABLE + if (strncmp(*qq, "%!", 2) == 0) + qq++; + fp = qq_mkfile(*qq); +#else + if (strncmp(*qq, "%!", 2) == 0) { + fp = qq_mkfile(**q, (int)strtol(*qq + 2, 0, 8)); + qq++; + } else + fp = qq_mkfile(**q, 0666); +#endif + + if (!fp) + return (-1); + qq_file(fp, qq); + if (fclose(fp)) + return (-1); + q++; + } + fp = qq_mkfile(fn, 0666); + if (!fp) + return (-1); + qq_head(fp); + qq_body(fp, p); + qq_tail(fp, qql, fno, fn); + if (fclose(fp)) + return (-1); + return (0); +} + +#endif + +/*----- That's all, folks -------------------------------------------------*/ diff --git a/quine.c b/quine.c new file mode 100644 index 0000000..ab7c5ef --- /dev/null +++ b/quine.c @@ -0,0 +1,513 @@ +/* -*-c-*- + * + * $Id: quine.c,v 1.1 1999/04/28 19:58:07 mdw Exp $ + * + * Output a program's source code as a quine file + * + * (c) 1999 Mark Wooding + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of Quine + * + * Quine is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Quine is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Quine; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/*----- Revision history --------------------------------------------------* + * + * $Log: quine.c,v $ + * Revision 1.1 1999/04/28 19:58:07 mdw + * Initial revision + * + */ + +/*----- Header files ------------------------------------------------------*/ + +/* --- ANSI headers --- */ + +#include +#include +#include +#include +#include +#include + +/* --- POSIX headers --- */ + +#ifndef QUINE_PORTABLE +#include +#include +#endif + +/* --- Local headers --- */ + +#include "mdwopt.h" +#include "quine.h" + +/*----- Main code ---------------------------------------------------------*/ + +/* --- @die@ --- * + * + * Arguments: @const char *f@ = a @printf@-style format string + * @...@ = other arguments + * + * Returns: Never. + * + * Use: Reports an error and hari-kiris. Like @moan@ above, only + * more permanent. + */ + +void die(const char *f, ...) +{ + va_list ap; + va_start(ap, f); + fprintf(stderr, "%s: ", optprog); + vfprintf(stderr, f, ap); + va_end(ap); + putc('\n', stderr); + exit(EXIT_FAILURE); +} + +/* --- @quine__banner@ --- * + * + * Arguments: @FILE *fp@ = file to write on + * + * Returns: --- + * + * Use: Writes a banner to the given file. + */ + +static void quine__banner(FILE *fp) +{ + fprintf(fp, "%s quine generator v. 1.1\n", optprog); +} + +/* --- @quine__usage@ --- * + * + * Arguments: @FILE *fp@ = file to write on + * + * Returns: --- + * + * Use: Writes a usage note to the given file. + */ + +static void quine__usage(FILE *fp) +{ + fprintf(fp, "Usage: %s [-hv0n] [-f FILE] [FILE...]\n", optprog); +} + +/* --- @quine__help@ --- * + * + * Arguments: @FILE *fp@ = file to write on + * + * Returns: --- + * + * Use: Writes a help message to the given file. + */ + +static void quine__help(FILE *fp) +{ + quine__banner(fp); + putc('\n', fp); + quine__usage(fp); + putc('\n', fp); + fputs( +"Given a list of source files, constructs a C source file which will write\n" +"the sources, and itself, when requested. In this way, it is easy to\n" +"create programs which contain their own source distributions, thus\n" +"fulfilling the requirements of the GNU General Public License with a\n" +"single binary program.\n" +"\n" +"The following options are provided:\n" +"\n" +"-h, --help\t\tdisplay this help message\n" +"-v, --version\t\tshow the program's version number\n" +"-0, --null\t\tread null terminated filenames\n" +"-l, --qqlib\t\tused while bootstrapping `quine'\n" +"-f, --file=FILE\t\tread filenames from FILE\n" +"-o, --output=FILE\twrite output quine data to FILE\n" +"-q, --quine\t\tbuild `quine's source distribution\n" +"-Q, --quine-dump\twrite `quine's source code to standard output\n", +fp); +} + +/* --- @quine__readLine@ --- * + * + * Arguments: @FILE *fp@ = pointer to input file + * @char **p@ = address of pointer to set up + * @size_t *sz@ = address of array length + * @const char *delim@ = array of delimiters + * + * Returns: Pointer to allocated line if all OK, otherwise zero. + * + * Use: Reads a line of input from the file. + */ + +static char *quine__readLine(FILE *fp, char **p, size_t *sz, + const char *delim) +{ + size_t len = 0; + char *q; + const char *d; + int ch; + + /* --- If I couldn't make my initial buffer, there's no hope --- */ + + if (!*p) { + *sz = 64; + *p = malloc(*sz); + if (!*p) + die("out of memory"); + } + + /* --- Skip over initial delimiters --- */ + +skip_delims: + ch = getc(fp); + if (ch == EOF) + return (0); + d = delim; + do { + if (ch == (unsigned char)*d) + goto skip_delims; + } while (*d++); + + /* --- Read characters into the buffer --- */ + + for (;;) { + + /* --- If not enough buffer space, double the buffer --- * + * + * The plus-one is for the null byte on the end. + */ + + if (len + 1 >= *sz) { + *sz <<= 1; + q = realloc(p, *sz); + if (!q) + die("out of memory"); + *p = q; + } + (*p)[len++] = ch; + + /* --- Read and check the next byte --- */ + + ch = getc(fp); + if (ch == EOF) + goto done; + d = delim; + do { + if (ch == (unsigned char)*d) + goto done; + } while (*d++); + } + + /* --- Terminate the string and return --- */ + +done: + (*p)[len++] = 0; + return (*p); +} + +/* --- @quine__doFile@ --- * + * + * Arguments: @FILE *fp@ = output file handle + * @FILE *ifp@ = input file + * + * Returns: --- + * + * Use: Outputs the contents of a quine block containing the given + * file. + */ + +static void quine__doFile(FILE *fp, FILE *ifp) +{ + char *p; + char buff[80]; + int ch; + + for (;;) { + p = buff; + ch = getc(ifp); + if (ch == EOF) + break; + for (;;) { + *p++ = ch; + if (ch == '\n' || p >= buff + sizeof(buff)) + break; + ch = getc(ifp); + } + fputc('\"', fp); + for (p = buff; p < buff + sizeof(buff); p++) { + switch (*p) { + case '\"': fputs("%q", fp); break; + case '\\': fputs("%b", fp); break; + case '\t': fputs("%t", fp); break; + case '\n': fputs("%n", fp); break; + case '\a': fputs("%a", fp); break; + case '\r': fputs("%r", fp); break; + case '\0': fputs("%0", fp); break; + case '%': fputs("%%", fp); break; + default: + if (isprint((unsigned char)*p)) + fputc(*p, fp); + else + fprintf(fp, "%%x%02x%%", (unsigned char)*p); + break; + } + if (*p == '\n') + break; + } + fputs("\",\n", fp); + } + fclose(ifp); + + fputs("0\n};\n\n", fp); +} + +/* --- @quine__file@ --- * + * + * Arguments: @FILE *fp@ = output file handle + * @const char *name@ = name of input file + * @size_t fno@ = number of this file + * + * Returns: --- + * + * Use: Outputs a quine block containing the given file. + */ + +static void quine__file(FILE *fp, const char *name, size_t fno) +{ + FILE *ifp; + + fprintf(fp, "static const char *qq__c_%lx[] = {\n", (unsigned long)fno); + + if ((ifp = fopen(name, "r")) == 0) + die("couldn't read `%s': %s", name, strerror(errno)); + fprintf(fp, "/* Filename: */ \"%s\",\n", name); + + /* --- Write file's mode to the output --- */ + +#ifndef QUINE_PORTABLE + { + struct stat st; + if (stat(name, &st) == 0) + fprintf(fp, "/* Mode: */ \"%%!0%03o\",\n", st.st_mode & 07777); + } +#endif + + quine__doFile(fp, ifp); +} + +/* --- Dummy quine library --- */ + +#ifdef QQ_XQUINE + +const char *qq_qqlib[] = { + "/* --- No quine library available in xquine --- */%n", + 0 +}; + +#endif + +/* --- @main@ --- * + * + * Arguments: @int argc@ = number of command line arguments + * @char *argv[]@ = pointer to the command line arguments + * + * Returns: Zero if successful, @EXIT_FAILURE@ if not. + * + * Use: Given a number of source files, outputs C code which prints + * them (and itself). + */ + +int main(int argc, char *argv[]) +{ + char *inf = 0; + char *outf = 0; + char *qqlibf = 0; + unsigned int f = 0; + FILE *infp = 0, *outfp = 0; + size_t files = 0; + + enum { + f_duff = 1, + f_null = 2, + f_noFiles = 4 + }; + + /* --- Scan the command line arguments --- */ + + for (;;) { + int i; + + static struct option opts[] = { + { "help", 0, 0, 'h' }, + { "version", 0, 0, 'v' }, + { "null", 0, 0, '0' }, + { "file", gFlag_argReq, 0, 'f' }, + { "nofiles", 0, 0, 'n' }, + { "output", gFlag_argReq, 0, 'o' }, + { "qqlib", gFlag_argReq, 0, 'l' }, +#if !defined(QQ_XQUINE) && !defined(QQ_YQUINE) + { "quine", 0, 0, 'q' }, + { "quine-dump", 0, 0, 'Q' }, +#endif + { 0, 0, 0, 0 } + }; + +#if !defined(QQ_XQUINE) && !defined(QQ_YQUINE) +# define OPTS "hv0nf:o:lqQ" +#else +# define OPTS "hv0nf:o:l" +#endif + + i = mdwopt(argc, argv, OPTS, opts, 0, 0, 0); + + if (i < 0) + break; + + switch (i) { + case 'h': + quine__help(stdout); + exit(0); + case 'v': + quine__banner(stdout); + exit(0); + case '0': + f |= f_null; + break; + case 'f': + inf = optarg; + break; + case 'o': + outf = optarg; + break; + case 'l': + qqlibf = optarg; + break; + case 'n': + f |= f_noFiles; + break; +#if !defined(QQ_XQUINE) && !defined(QQ_YQUINE) + case 'q': + if (qq_create()) + die("couldn't build source distribution: %s", strerror(errno)); + exit(0); + case 'Q': + qq_dump(stdout); + exit(0); + break; +#endif + default: + f |= f_duff; + break; + } + } + + if (f & f_duff) { + quine__usage(stderr); + exit(EXIT_FAILURE); + } + + /* --- Output a file as the qqlib library --- */ + + if (qqlibf) { + infp = fopen(qqlibf, "r"); + if (!infp) + die("couldn't open input file `%s': %s", inf, strerror(errno)); + + if (!outf) + outf = "qqlout.c"; + if (strcmp(outf, "-") == 0) + outfp = stdout; + else + outfp = fopen(outf, "w"); + if (!outfp) + die("couldn't open output file `%s': %s", outf, strerror(errno)); + + fputs("/* quine library data */\n\n" + "const char *qq_qqlib[] = {\n", outfp); + quine__doFile(outfp, infp); + fclose(infp); + fclose(outfp); + exit(0); + } + + /* --- Time to start work --- */ + + if (~f & f_noFiles && (inf || optind == argc)) { + if (!inf || strcmp(inf, "-") == 0) + infp = stdin; + else + infp = fopen(inf, "r"); + if (!infp) + die("couldn't open input file `%s': %s", inf, strerror(errno)); + } + + if (!outf) + outf = "qqout.c"; + if (strcmp(outf, "-") == 0) + outfp = stdout; + else + outfp = fopen(outf, "w"); + if (!outfp) + die("couldn't open output file `%s': %s", outf, strerror(errno)); + + /* --- Output a header --- */ + + qq_head(outfp); + fputs("/* --- file contents tables --- */\n\n", outfp); + + /* --- Scan the command line first --- */ + + while (~f & f_noFiles && optind < argc) + quine__file(outfp, argv[optind++], files++); + + /* --- Now read the input file --- */ + + if (infp) { + char *p = 0; + char *q; + const char *del; + size_t sz = 0; + + if (f & f_null) + del = ""; + else + del = " \n\t"; + + while ((q = quine__readLine(infp, &p, &sz, del)) != 0) + quine__file(outfp, q, files++); + + if (fclose(infp)) + die("error reading input: %s", strerror(errno)); + } + + /* --- And output the trailer --- */ + + qq_tail(outfp, qq_qqlib, files, + strcmp(outf, "-") == 0 ? "" : outf); + + /* --- Done --- */ + + if (outfp != stdout && fclose(outfp)) + die("error writing output: %s", strerror(errno)); + + return (0); +} + +/*----- That's all, folks -------------------------------------------------*/ diff --git a/quine.h b/quine.h new file mode 100644 index 0000000..6135da8 --- /dev/null +++ b/quine.h @@ -0,0 +1,161 @@ +/* -*-c-*- + * + * $Id: quine.h,v 1.1 1999/04/28 19:58:07 mdw Exp $ + * + * Definitions for building quines + * + * (c) 1999 Mark Wooding + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of Quine + * + * Quine is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Quine is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Quine; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/*----- Revision history --------------------------------------------------* + * + * $Log: quine.h,v $ + * Revision 1.1 1999/04/28 19:58:07 mdw + * Initial revision + * + */ + +#ifndef QUINE_H +#define QUINE_H + +#ifdef __cplusplus + extern "C" { +#endif + +/*----- Required headers --------------------------------------------------*/ + +#include + +/*----- External tables ---------------------------------------------------*/ + +extern const char **qq_table[]; +extern const char *qq_qqlib[]; + +/*----- Functions provided ------------------------------------------------*/ + +/* --- @qq_xlate@ --- * + * + * Arguments: @FILE *fp@ = output file handle + * @const char *p@ = pointer to encoded text + * + * Returns: --- + * + * Use: Writes the decoded string to the given file handle. + */ + +extern void qq_xlate(FILE */*fp*/, const char */*p*/); + +/* --- @qq_file@ --- * + * + * Arguments: @FILE *fp@ = output file handle + * @const char **p@ = pointer to the output array + * + * Returns: --- + * + * Use: Writes the contents of a file to the output. + */ + +extern void qq_file(FILE */*fp*/, const char **/*p*/); + +/* --- @qq_head@ --- * + * + * Arguments: @FILE *fp@ = output file handle + * + * Returns: --- + * + * Use: Writes the head of a `qqout.c' file. + */ + +extern void qq_head(FILE */*fp*/); + +/* --- @qq_body@ --- * + * + * Arguments: @FILE *fp@ = output file handle + * @const char ***p@ = pointer to main table + * + * Returns: --- + * + * Use: Writes the body table of a `qqout.c' file. + */ + +extern void qq_body(FILE */*fp*/, const char ***/*p*/); + +/* --- @qq_tail@ --- * + * + * Arguments: @FILE *fp@ = output file handle + * @const char **qql@ = pointer to qqlib file array + * @size_t fno@ = number of files written + * @const char *fn@ = name of `qqout.c' file + * + * Returns: --- + * + * Use: Writes the head of a `qqout.c' file. + */ + +extern void qq_tail(FILE */*fp*/, const char **/*qql*/, + size_t /*fno*/, const char */*fn*/); + +/* --- @qq_mkfile@ --- * + * + * Arguments: @const char *fn@ = pointer to a filename + * @int mode@ = mode to create file with + * + * Returns: A handle for the created file. + * + * Use: Creates a file, and leading directories and stuff. + */ + +#ifdef QUINE_PORTABLE +extern FILE *qq_mkfile(const char */*fn*/); +#else +extern FILE *qq_mkfile(const char */*fn*/, int /*mode*/); +#endif + +/* --- @qq_dump@ --- * + * + * Arguments: @FILE *fp@ = stream to dump on + * + * Returns: --- + * + * Use: Writes the program's source code to the given file. + */ + +extern void qq_dump(FILE */*fp*/); + +/* --- @qq_create@ --- * + * + * Arguments: --- + * + * Returns: --- + * + * Use: Writes a source distribution to the current directory. + */ + +extern int qq_create(void); + +/*----- That's all, folks -------------------------------------------------*/ + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/rexxquine.exec b/rexxquine.exec new file mode 100644 index 0000000..ccbaa96 --- /dev/null +++ b/rexxquine.exec @@ -0,0 +1,4 @@ +/* */ +q='''' +p='say"/* */";say"q="q||q||q||q;say"p="q||p||q;say"interpret p"' +interpret p -- [mdw]