chiark / gitweb /
Initial revision
authormdw <mdw@ce86e22d-31e3-0310-b972-e9f3c7b8d6ce>
Wed, 28 Apr 1999 19:58:06 +0000 (19:58 +0000)
committermdw <mdw@ce86e22d-31e3-0310-b972-e9f3c7b8d6ce>
Wed, 28 Apr 1999 19:58:06 +0000 (19:58 +0000)
13 files changed:
.cvsignore [new file with mode: 0644]
.links [new file with mode: 0644]
AUTHORS [new file with mode: 0644]
ChangeLog [new file with mode: 0644]
Makefile.am [new file with mode: 0644]
README [new file with mode: 0644]
ansicquine.c [new file with mode: 0644]
bournequine [new file with mode: 0755]
configure.in [new file with mode: 0644]
qqlib.c [new file with mode: 0644]
quine.c [new file with mode: 0644]
quine.h [new file with mode: 0644]
rexxquine.exec [new file with mode: 0644]

diff --git a/.cvsignore b/.cvsignore
new file mode 100644 (file)
index 0000000..355595b
--- /dev/null
@@ -0,0 +1,6 @@
+Makefile.in
+aclocal.m4
+build
+configure
+qqlout.c
+qqout.c
diff --git a/.links b/.links
new file mode 100644 (file)
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 (file)
index 0000000..e69de29
diff --git a/ChangeLog b/ChangeLog
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/Makefile.am b/Makefile.am
new file mode 100644 (file)
index 0000000..cc5036a
--- /dev/null
@@ -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 (file)
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 (file)
index 0000000..4044a74
--- /dev/null
@@ -0,0 +1,2 @@
+#include <stdio.h>
+int main(void){char n='\n';char b='\\';char q='"';char*p="#include <stdio.h>%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 (executable)
index 0000000..9ed62fe
--- /dev/null
@@ -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 (file)
index 0000000..5f9773c
--- /dev/null
@@ -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 (file)
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 <ctype.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* --- POSIX headers --- */
+
+#ifndef QUINE_PORTABLE
+#  include <sys/types.h>
+#  include <sys/fcntl.h>
+#  include <sys/stat.h>
+#  include <unistd.h>
+#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 (file)
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 <ctype.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* --- POSIX headers --- */
+
+#ifndef QUINE_PORTABLE
+#include <sys/types.h>
+#include <sys/stat.h>
+#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 ? "<stdout>" : 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 (file)
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 <stdio.h>
+
+/*----- 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 (file)
index 0000000..ccbaa96
--- /dev/null
@@ -0,0 +1,4 @@
+/* */
+q=''''
+p='say"/* */";say"q="q||q||q||q;say"p="q||p||q;say"interpret p"'
+interpret p