diff --git a/2000/tomx/.gitignore b/2000/tomx/.gitignore
index 2275e9ca3..53d9473c1 100644
--- a/2000/tomx/.gitignore
+++ b/2000/tomx/.gitignore
@@ -1 +1,2 @@
tomx
+mkentry
diff --git a/2000/tomx/README.md b/2000/tomx/README.md
index 2acf58542..aa341f484 100644
--- a/2000/tomx/README.md
+++ b/2000/tomx/README.md
@@ -1,12 +1,11 @@
# Most Complete Program
-Thomas P John
-Mishybi Plamoottil
-Parottukonam
-Nalanchira P.O, Trivandrum-695015
-India
-
-http://tomx.tripod.com/
+Thomas P John
+Mishybi Plamoottil
+Parottukonam
+Nalanchira P.O, Trivandrum-695015
+India
+
## To build:
@@ -43,22 +42,21 @@ Polyglots have come and gone, but this was the first one we'd seen
where one language *used* another to perform its tasks. An interesting
approach. Admittedly, it's not all that obfuscated - but there's more
to it than you might think, since you have to make sure that your
-changes don't change the shell code or the makefile.
+changes don't change the shell code or the Makefile.
## Author's remarks:
### Summary
-This program works as a C source file, a Makefile and also as a
-Shell (/bin/sh) script. But unlike some other multi-language prog-
-rams, this one is NOT meant to print "Hello, world" in all 3 cases
-(At least in the true sense ;-)). The C, make and shell code build
-upon each other to create a C program (?) which when executed as a
-shell script will compile itself through the C compiler using a
-makefile! - Pretty useful, Eh?.
+This program works as a C source file, a Makefile and also as a shell
+(`/bin/sh`) script. But unlike some other multi-language programs, this one is
+NOT meant to print "Hello, world" in all 3 cases (At least in the true sense
+;-)). The C, make and shell code build upon each other to create a C program (?)
+which when executed as a shell script will compile itself through the C compiler
+using a Makefile! - Pretty useful, Eh?
-Probably, this is the first entry ever to fully satisfy Rule #1
-("Your entry must be a complete program") in the strictest sense;-)
+Probably, this is the first entry ever to fully satisfy Rule #1 ("Your entry
+must be a complete program") in the strictest sense;-)
### Top 5 reasons (IMHO) why this code is an entry for IOCCC
@@ -76,27 +74,25 @@ obfuscation should be in C and not elsewhere???. Mmmmm...).
3. Extending Argument #2... Obfuscation done in the C code
(comments?) create obfuscated code for the other 2 languages.
This opens new possibilities to be explored in depth in future
-IOCCCs.
+`IOCCC`s.
4. LOOKS like a Makefile, WORKS like a shell script and COMPILES
like a C program.
-5. Does something useful (See Notes #1, #2).
+5. Does something useful (See Notes #1 and #2 below).
### Notes
1. The program could have been something other than the simple "Hello, world"
program that it is. It is possible to do `#include "prog2.c"` in the place of
-the main(), leaving the complexities of the C file elsewhere and making this
-source still acceptable to "make" and "sh". The following modification to the
-last few lines will compile the IOCCC's famous `mkentry.c`.
+the `main()`, leaving the complexities of the C file elsewhere and making this
+source still acceptable to `make` and `sh`. The following modification to the
+last few lines will compile the IOCCC's famous [mkentry.c](mkentry.c).
-```make
-.PHONY: /* true clean */
-#undef true
-#include "mkentry.c"
-```
+ .PHONY: /* true clean */
+ #undef true
+ #include "mkentry.c"
Ain't that useful?
@@ -104,18 +100,18 @@ Ain't that useful?
very interesting things like generating C code on execution
by adding additional code in make/sh (open up a loop????).
This opens the possibilities of generating & compiling some
-[obfuscated?] C code. (Maybe should try this if this program
+(obfuscated?) C code. (Maybe should try this if this program
fails at IOCCC 2000)
3. Changing the `%:%.c` dependency to a `$(PROG):$(PROG).c` form
can compile this program with some other `make`s (I got it to
-work in Windows using nmake and Gnuwin32's sh ;-)
+work in Windows using nmake and Gnuwin32's sh ;-) )
4. Interesting things to do: try `./tomx.c clean`. This will
do the same as `make clean` :-)). Also: `./tomx.c all`
-5. Be careful about the Tabs in the source code. Removing them
-or converting Tabs to spaces can be disastrous.
+5. Be careful about the tabs in the source code. Removing them
+or converting tabs to spaces can be disastrous.
## Copyright and CC BY-SA 4.0 License:
diff --git a/2000/tomx/mkentry.c b/2000/tomx/mkentry.c
new file mode 100644
index 000000000..5e5fe6f4e
--- /dev/null
+++ b/2000/tomx/mkentry.c
@@ -0,0 +1,1273 @@
+/* $Id: mkentry.c,v 1.6 1999/01/27 23:19:37 chongo Exp $ */
+/*
+ * Copyright (c) Leonid A. Broukhis, Jeremy Horn, Landon Curt Noll and
+ * Peter Seebach, 1998.
+ *
+ * All Rights Reserved. Permission for personal, education or non-profit use
+ * is granted provided this this copyright and notice are included in its
+ * entirety and remains unaltered. All other uses must receive prior
+ * permission in writing from the contest judges.
+ */
+/*
+ * mkentry - make an International Obfuscated C Code Contest entry
+ *
+ * usage:
+ * mkentry -r remarks -b build -p prog.c -o ioccc.entry
+ *
+ * -r remarks file with remarks about the entry
+ * -b build file containing how prog.c should be built
+ * -p prog.c the obfuscated program source file
+ * -o ioccc.entry ioccc entry output file
+ *
+ * compile by:
+ * cc mkentry.c -o mkentry
+ */
+/*
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+/*
+ * WARNING:
+ *
+ * This program attempts to implement the IOCCC rules. Every attempt
+ * has been made to make sure that this program produces an entry that
+ * conforms to the contest rules. In all cases, where this program
+ * differs from the contest rules, the contest rules will be used. Be
+ * sure to check with the contest rules before submitting an entry.
+ *
+ * FOR MORE INFORMATION:
+ *
+ * You may contact the judges by sending an email to the following address:
+ *
+ * questions@ioccc.org (not the address for submitting entries)
+ *
+ * Questions and comments about the contest are welcome.
+ *
+ * Entries must be sent to:
+ *
+ * entry@ioccc.org
+ *
+ * but be sure to follow the rules and read the guidelines first!
+ *
+ * The rules and the guidelines may (and often do) change from year to
+ * year. You should be sure you have the current rules and guidelines
+ * prior to submitting entries. To obtain them, visit the following URL:
+ *
+ * http://www.ioccc.org
+ *
+ * Because contest rules change from year to year, one should only use this
+ * program for the year that it was intended.
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+/* logic */
+#ifndef TRUE
+# define TRUE 1
+#endif /* TRUE */
+#ifndef FALSE
+# define FALSE 0
+#endif /* FALSE */
+#define EOF_OK TRUE
+#define EOF_NOT_OK FALSE
+
+/* global limits */
+#define RULE_YEAR 2000 /* NOTE: should match the current year */
+#define START_DATE "01Feb2000 00:00 UTC" /* first confirmation received */
+#define MAX_COL 79 /* max column a line should hit */
+#define MAX_BUILD_SIZE 256 /* max how to build size */
+#define MAX_PROGRAM_SIZE 3217 /* max program source size */
+#define MAX_PROGRAM_SIZE2 1536 /* max program source size not counting
+ whitespace and {}; not followed by
+ whitespace or EOF */
+#define MAX_TITLE_LEN 31 /* max chars in the title */
+#define MAX_ENTRY_LEN 1 /* max length in the entry input line */
+#define MAX_ENTRY 8 /* max number of entries per person per year */
+#define MAX_FILE_LEN 1024 /* max filename length for a info file */
+
+/* where to send entries */
+#define ENTRY_ADDR "entry@ioccc.org"
+
+/* uuencode process - assumes ASCII */
+#define UUENCODE(c) ((c) ? encode_str[(int)(c)&0x3f] : '`')
+#define UUENCODE_LEN 45 /* max uuencode chunk size */
+#define UUINFO_MODE 0444 /* mode of an info file's uuencode file */
+#define UUBUILD_MODE 0444 /* mode of the build file's uuencode file */
+#define UUBUILD_NAME "build" /* name for the build file's uuencode file */
+#define UUPROG_MODE 0444 /* mode of the program's uuencode file */
+#define UUPROG_NAME "prog.c" /* name for the program's uuencode file */
+
+/* encode_str[(char)val] is the uuencoded character of val */
+char encode_str[] = "`!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_";
+
+/* global declarations */
+char *program; /* our name */
+long start_time; /* the startup time */
+
+/* forward declarations */
+void parse_args(int argc, char **argv, char **rname,
+ char **bname, char **pname, char **oname);
+void usage(int exitval);
+FILE *open_remark(char *filename);
+FILE *open_build(char *filename);
+FILE *open_program(char *filename);
+FILE *open_output(char *filename);
+void output_entry(FILE *output, char *oname);
+void output_remark(FILE *output, char *oname, FILE *remark, char *rname);
+void output_author(FILE *output, char *oname);
+void output_info(FILE *output, char *oname);
+void output_build(FILE *output, char *oname, FILE *build, char *bname);
+void output_program(FILE *output, char *oname, FILE *prog, char *pname);
+void output_end(FILE *output, char *oname);
+int get_line(char *buf, int siz, int maxcol);
+void output_till_dot(FILE *output, char *oname, char *leader);
+int col_len(char *string);
+void check_io(FILE *stream, char *name, int eof_ok);
+void uuencode(FILE *output, char *oname, FILE *infile,
+ char *iname, int umode, char *uname);
+
+int
+main(argc, argv)
+ int argc; /* arg count */
+ char **argv; /* the args */
+{
+ FILE *remark=NULL; /* open remarks stream */
+ FILE *build=NULL; /* open build file stream */
+ FILE *prog=NULL; /* open program stream */
+ FILE *output=NULL; /* open output stream */
+ char *rname=NULL; /* file with remarks about the entry */
+ char *bname=NULL; /* file containing how prog.c should be built */
+ char *pname=NULL; /* the obfuscated program source file */
+ char *oname=NULL; /* ioccc entry output file */
+ struct tm *tm; /* startup time structure */
+
+ /*
+ * check on the year
+ */
+ start_time = time((long *)0);
+ tm = gmtime(&start_time);
+ if (tm->tm_year != RULE_YEAR-1900 &&
+ (tm->tm_year != RULE_YEAR-1900+1 || tm->tm_mon != 0)) {
+ fprintf(stderr,
+ "%s: WARNING: this program applies to %d, which may differ from %d\n\n",
+ argv[0], RULE_YEAR, 1900+tm->tm_year);
+ }
+
+ /*
+ * parse the command line args
+ */
+ parse_args(argc, argv, &rname, &bname, &pname, &oname);
+
+ /*
+ * open/check the input and output files
+ *
+ * We open and truncate the output file first, in case it is the same
+ * as one of the input files.
+ */
+ output = open_output(oname);
+ remark = open_remark(rname);
+ build = open_build(bname);
+ prog = open_program(pname);
+ if (output==NULL || remark==NULL || build==NULL || prog==NULL) {
+ exit(1);
+ }
+
+ /*
+ * output each section
+ */
+ output_entry(output, oname);
+ output_remark(output, oname, remark, rname);
+ output_author(output, oname);
+ output_info(output, oname);
+ output_build(output, oname, build, bname);
+ output_program(output, oname, prog, pname);
+ output_end(output, oname);
+
+ /*
+ * flush the output
+ */
+ if (fflush(output) == EOF) {
+ fprintf(stderr, "%s: flush error in %s: ", program, oname);
+ perror("");
+ exit(2);
+ }
+
+ /*
+ * final words
+ */
+ printf("\nYour entry can be found in %s. You should check this file\n",
+ oname);
+ printf("correct any problems and verify that the uudecode utility will\n");
+ printf("correctly decode your build file and program.\n\n");
+ printf("This program has been provided as a guide for submitters. In\n");
+ printf("cases where it conflicts with the rules, the rules shall apply.\n");
+ printf("It is your responsibility to ensure that your entry conforms to\n");
+ printf("the current rules.\n\n");
+ printf("Email your entries to:\n");
+ printf("\t%s\n\n", ENTRY_ADDR);
+ printf("Please use the following subject when you email your entry:\n");
+ printf("\tioccc entry\n\n");
+ /* all done */
+ return 0;
+}
+
+/*
+ * parse_args - parse the command line args
+ *
+ * usage:
+ * argc arg count
+ * argv the args
+ * rname file with remarks about the entry
+ * bname file containing how prog.c should be built
+ * pname the obfuscated program source file
+ * oname ioccc entry output file
+ *
+ * Given the command line args, this function parses them and sets the
+ * required name flags. This function will return only if the command
+ * line syntax is correct.
+ */
+void
+parse_args(int argc, char **argv, char **rname,
+ char **bname, char **pname, char **oname)
+{
+ char *optarg; /* -flag option operand */
+ int flagname; /* the name of the -flag */
+ int i;
+
+ /*
+ * Not everyone has getopt, so we must parse args by hand.
+ */
+ program = argv[0];
+ for (i=1; i < argc; ++i) {
+
+ /* determine the flagname */
+ if (argv[i][0] != '-') {
+ usage(1);
+ /*NOTREACHED*/
+ }
+ flagname = (int)argv[i][1];
+
+ /* determine the flag's operand */
+ if (flagname != '\0' && argv[i][2] != '\0') {
+ optarg = &argv[i][2];
+ } else {
+ if (i+1 >= argc) {
+ usage(2);
+ /*NOTREACHED*/
+ } else {
+ optarg = argv[++i];
+ }
+ }
+
+ /* save the flag's operand in the correct global variable */
+ switch (flagname) {
+ case 'r':
+ *rname = optarg;
+ break;
+ case 'b':
+ *bname = optarg;
+ break;
+ case 'p':
+ *pname = optarg;
+ break;
+ case 'o':
+ *oname = optarg;
+ break;
+ default:
+ usage(3);
+ /*NOTREACHED*/
+ }
+ }
+
+ /*
+ * verify that we have all of the required flags
+ */
+ if (*rname == NULL || *bname == NULL || *pname == NULL || *oname == NULL) {
+ usage(4);
+ /*NOTREACHED*/
+ }
+ return;
+}
+
+/*
+ * usage - print a usage message and exit
+ *
+ * usage:
+ * exitval exit with this value
+ *
+ * This function does not return.
+ */
+void
+usage(int exitval)
+{
+ fprintf(stderr,
+ "usage: %s -r remarks -b build -p prog.c -o ioccc.entry\n\n", program);
+ fprintf(stderr, "\t-r remarks\tfile with remarks about the entry\n");
+ fprintf(stderr, "\t-b build\tfile containing how prog.c should be built\n");
+ fprintf(stderr, "\t-p prog.c\tthe obfuscated program source file\n");
+ fprintf(stderr, "\t-o ioccc.entry\tioccc entry output file\n");
+ exit(exitval);
+}
+
+/*
+ * open_remark - open/check the remark file
+ *
+ * usage:
+ * filename remark filename
+ *
+ * The remark file should be indented by 4 spaces, and should not extend
+ * beyond column MAX_COL. These are not requirements, so we only warn.
+ *
+ * This function returns NULL on I/O or format error.
+ */
+FILE *
+open_remark(char *filename)
+{
+ FILE *stream; /* the opened file stream */
+ char buf[BUFSIZ+1]; /* input buffer */
+ int toolong=0; /* number of lines that are too long */
+ int non_indent=0; /* number of lines not indented by 4 spaces */
+
+ /*
+ * open the remark input file
+ */
+ stream = fopen(filename, "r");
+ if (stream == NULL) {
+ fprintf(stderr, "%s: cannot open remark file: %s: ",
+ program, filename);
+ perror("");
+ return(NULL);
+ }
+
+ /*
+ * look at each line
+ */
+ while (fgets(buf, BUFSIZ, stream) != NULL) {
+
+ /* count lines that do not start with 4 spaces */
+ if (buf[0] != '\n' && strncmp(buf, " ", 4) != 0) {
+ ++non_indent;
+ }
+
+ /* count long lines */
+ if (col_len(buf) > MAX_COL) {
+ /* found a line that is too long */
+ ++toolong;
+ }
+ }
+
+ /* watch for I/O errors */
+ check_io(stream, filename, EOF_OK);
+
+ /* note long lines if needed */
+ if (toolong > 0) {
+ fprintf(stderr,
+ "%s: WARNING: %d line(s) from %s extend beyond the 80th column\n",
+ program, toolong, filename);
+ fprintf(stderr,
+ "%s: This is ok, but it would be nice to avoid\n\n",
+ program);
+ }
+
+ /* note non-indented lines, if needed */
+ if (non_indent > 0) {
+ fprintf(stderr,
+ "%s: WARNING: %d line(s) from %s are not indented by 4 spaces\n",
+ program, non_indent, filename);
+ fprintf(stderr,
+ "%s: This is ok, but it would be nice to avoid\n\n",
+ program);
+ }
+
+ /* return the open file */
+ rewind(stream);
+ return(stream);
+}
+
+/*
+ * open_build - open/check the build file
+ *
+ * usage:
+ * filename build filename
+ *
+ * The how to build file must not be longer than MAX_BUILD_SIZE bytes.
+ *
+ * This function returns NULL on I/O or size error.
+ */
+FILE *
+open_build(char *filename)
+{
+ FILE *stream; /* the opened file stream */
+ struct stat statbuf; /* the status of the open file */
+
+ /*
+ * open the how to build input file
+ */
+ stream = fopen(filename, "r");
+ if (stream == NULL) {
+ fprintf(stderr, "%s: cannot open how to build file: %s: ",
+ program, filename);
+ perror("");
+ return(NULL);
+ }
+
+ /*
+ * determine the size of the file
+ */
+ if (fstat(fileno(stream), &statbuf) < 0) {
+ fprintf(stderr, "%s: cannot stat how to build file: %s: ",
+ program, filename);
+ perror("");
+ return(NULL);
+ }
+ if (statbuf.st_size > MAX_BUILD_SIZE) {
+ fprintf(stderr,
+ "%s: FATAL: the how to build file: %s, is %ld bytes long\n",
+ program, filename, (long) statbuf.st_size);
+ fprintf(stderr,
+ "%s: it may not be longer than %d bytes\n",
+ program, MAX_BUILD_SIZE);
+ return(NULL);
+ }
+
+ /* return the open file */
+ return(stream);
+}
+
+/*
+ * open_program - open/check the program source file
+ *
+ * usage:
+ * filename source filename
+ *
+ * The program source file must be <= 3217 bytes. The number of
+ * non-whitespace and }{; chars not followed by whitespace must
+ * be <= 1536 bytes.
+ *
+ * This function returns NULL on I/O or size error.
+ */
+FILE *
+open_program(char *filename)
+{
+ FILE *stream; /* the opened file stream */
+ struct stat statbuf; /* the status of the open file */
+ int count; /* special count size */
+ int c; /* the character read */
+
+ /*
+ * open the program source input file
+ */
+ stream = fopen(filename, "r");
+ if (stream == NULL) {
+ fprintf(stderr, "%s: cannot open program source file: %s: ",
+ program, filename);
+ perror("");
+ exit(7);
+ }
+
+ /*
+ * determine the size of the file
+ */
+ if (fstat(fileno(stream), &statbuf) < 0) {
+ fprintf(stderr, "%s: cannot stat program source file: %s: ",
+ program, filename);
+ perror("");
+ return(NULL);
+ }
+ if (statbuf.st_size > MAX_PROGRAM_SIZE) {
+ fprintf(stderr,
+ "%s: FATAL: the program source file: %s, is %ld bytes long\n",
+ program, filename, (long) statbuf.st_size);
+ fprintf(stderr,
+ "%s: it may not be longer than %d bytes\n",
+ program, MAX_PROGRAM_SIZE);
+ return(NULL);
+ }
+
+ /*
+ * count the non-whitespace, non {}; followed by whitespace chars
+ */
+ count = 0;
+ c = 0;
+ while ((c=fgetc(stream)) != EOF) {
+ /* look at non-whitespace */
+ if (!isascii(c) || !isspace(c)) {
+ switch (c) {
+ case '{': /* count if not followed by EOF or whitespace */
+ case '}':
+ case ';':
+ /* peek at next char */
+ c = fgetc(stream);
+ if (c != EOF && isascii(c) && !isspace(c)) {
+ /* not followed by whitespace or EOF, count it */
+ ungetc(c, stream);
+ ++count;
+ }
+ break;
+ default:
+ ++count;
+ break;
+ }
+ }
+ }
+
+ /* watch for I/O errors */
+ check_io(stream, filename, EOF_OK);
+
+ /* look at the special size */
+ if (count > MAX_PROGRAM_SIZE2) {
+ fprintf(stderr,
+ "%s: FATAL: the number of bytes that are non-whitespace, and\n",
+ program);
+ fprintf(stderr,
+ "%s: that are not '{', '}', ';' followed by whitespace\n",
+ program);
+ fprintf(stderr,
+ "%s: or EOF must be <= %d bytes\n",
+ program, MAX_PROGRAM_SIZE2);
+ fprintf(stderr,
+ "%s: in %s, %d bytes were found\n",
+ program, filename, count);
+ return(NULL);
+ }
+
+ /* return the open file */
+ rewind(stream);
+ return(stream);
+}
+
+/*
+ * open_output - open/check the entry output file
+ *
+ * usage:
+ * filename output filename
+ *
+ * This function returns NULL on open error.
+ */
+FILE *
+open_output(char *filename)
+{
+ FILE *stream; /* the opened file stream */
+
+ /*
+ * open the ioccc entry output file
+ */
+ stream = fopen(filename, "w");
+ if (stream == NULL) {
+ fprintf(stderr, "%s: cannot open ioccc entry file for output: %s: ",
+ program, filename);
+ perror("");
+ exit(8);
+ }
+
+ /* return the open file */
+ return(stream);
+}
+
+/*
+ * output_entry - output the ---entry--- section
+ *
+ * usage:
+ * output entry's output file stream
+ * oname name of the output file
+ *
+ * Read the needed information form stdin, and write the entry section.
+ */
+void
+output_entry(FILE *output, char *oname)
+{
+ char title[MAX_TITLE_LEN+1+1]; /* the entry's title */
+ char buf[MAX_COL+1+1]; /* I/O buffer */
+ int entry=0; /* entry number */
+ int ret; /* fields processed by fscanf */
+ int ok_line=0; /* 0 => the line is not ok */
+ char skip; /* input to skip */
+ time_t epoch_sec; /* seconds since the epoch */
+ char *p;
+
+ /*
+ * write the start of the section
+ */
+ fprintf(output, "---entry---\n");
+ check_io(output, oname, EOF_NOT_OK);
+
+ /*
+ * write the rule year
+ */
+ fprintf(output, "rule:\t%d\n", RULE_YEAR);
+ check_io(output, oname, EOF_NOT_OK);
+
+ /* determine if this is a fix */
+ printf("Is this a fix, update or resubmission to a ");
+ printf("previous entry (enter y or n)? ");
+ while (get_line(buf, 1+1, 0) <= 0 || !(buf[0]=='y' || buf[0]=='n')) {
+ printf("\nplease answer y or n: ");
+ }
+ if (buf[0] == 'y') {
+ fprintf(output, "fix:\ty\n");
+ check_io(output, oname, EOF_NOT_OK);
+ printf("\nBe sure that the title and entry number that you give\n");
+ printf("are the same of as the entry you are replacing\n");
+ } else {
+ fprintf(output, "fix:\tn\n");
+ check_io(output, oname, EOF_NOT_OK);
+ }
+
+ /*
+ * write the title
+ */
+ printf("\nThe first character of your title should match [a-zA-Z0-9_=]\n");
+ printf("The next 0 to %d characters should match [a-zA-Z0-9_=+-]\n\n",
+ MAX_TITLE_LEN-1);
+ printf("It is suggested, but not required, that the title should\n");
+ printf("incorporate your username; in the\n");
+ printf("case of multiple authors, consider using parts of the usernames\n");
+ printf("of the authors.\n\n");
+ printf("enter your title: ");
+ do {
+ /* prompt and read a line */
+ if ((ok_line = get_line(title, MAX_TITLE_LEN+1, MAX_COL-9)) <= 0) {
+ printf("\ntitle is too long, please re-enter: ");
+ continue;
+ }
+
+ /* verify the pattern, not everyone has regexp, so do it by hand */
+ if (!isascii((int)title[0]) ||
+ !(isalnum((int)title[0]) || title[0] == '_' || title[0] == '=')) {
+ printf("\ninvalid first character in the title\n\n");
+ printf("enter your title: ");
+ ok_line = 0;
+ } else {
+ for (p=(&title[1]); *p != '\0' && *p != '\n'; ++p) {
+ if (!isascii((int)*p) ||
+ !(isalnum((int)*p) ||
+ *p == '_' || *p == '=' || *p == '+' || *p == '-')) {
+ printf("\ninvalid character in the title\n\n");
+ printf("enter your title: ");
+ ok_line = 0;
+ }
+ }
+ }
+ } while (ok_line <= 0);
+ fprintf(output, "title:\t%s", title);
+ check_io(output, oname, EOF_NOT_OK);
+
+ /*
+ * write the entry number
+ */
+ printf("\nEach person may submit up to %d entries per year.\n\n",
+ MAX_ENTRY);
+ printf("enter an entry number from 0 to %d inclusive: ", MAX_ENTRY-1);
+ do {
+ /* get a valid input line */
+ fflush(stdout);
+ ret = fscanf(stdin, "%d[\n]", &entry);
+ check_io(stdin, "stdin", EOF_NOT_OK);
+ /* skip over input until newline is found */
+ do {
+ skip = fgetc(stdin);
+ check_io(stdin, "stdin", EOF_NOT_OK);
+ if (skip != '\n') {
+ /* bad text in input, invalidate entry number */
+ entry = -1;
+ }
+ } while (skip != '\n');
+
+ /* check if we have a number, and if it is in range */
+ if (ret != 1 || entry < 0 || entry > MAX_ENTRY-1) {
+ printf(
+ "\nThe entry number must be between 0 and %d inclusive\n\n",
+ MAX_ENTRY-1);
+ printf("enter the entry number: ");
+ }
+ } while (ret != 1 || entry < 0 || entry > MAX_ENTRY-1);
+ fprintf(output, "entry:\t%d\n", entry);
+ check_io(output, oname, EOF_NOT_OK);
+
+ /*
+ * write the submission date
+ */
+ /* returns a newline */
+ epoch_sec = time(NULL);
+ fprintf(output, "date:\t%s", asctime(gmtime(&epoch_sec)));
+ check_io(output, oname, EOF_NOT_OK);
+
+ /*
+ * write the OS/machine host information
+ */
+ printf(
+ "\nEnter the machine(s) and OS(s) under which your entry was tested.\n");
+ output_till_dot(output, oname, "host:");
+}
+
+/*
+ * output_remark - output the ---remark--- section
+ *
+ * usage:
+ * output entry's output file stream
+ * oname name of the output file
+ * remark stream to the file containing remark text
+ * rname name of the remark file
+ *
+ * Read the needed information form stdin, and write the entry section.
+ */
+void
+output_remark(FILE *output, char *oname, FILE *remark, char *rname)
+{
+ char buf[BUFSIZ+1]; /* input/output buffer */
+
+ /*
+ * write the start of the section
+ */
+ fprintf(output, "---remark---\n");
+ check_io(output, oname, EOF_NOT_OK);
+
+ /*
+ * copy the remark file to the section
+ */
+ while (fgets(buf, BUFSIZ, remark) != NULL) {
+ fputs(buf, output);
+ check_io(output, oname, EOF_NOT_OK);
+ }
+ check_io(remark, rname, EOF_OK);
+
+ /* be sure that the remark section ends with a newline */
+ if (buf[strlen(buf)-1] != '\n') {
+ fputc('\n', output);
+ check_io(output, oname, EOF_NOT_OK);
+ }
+}
+
+/*
+ * output_author - output the ---author--- section
+ *
+ * usage:
+ * output entry's output file stream
+ * oname name of the output file
+ *
+ * Read the needed information from stdin, and write the author section.
+ * If multiple authors exist, multiple author sections will be written.
+ */
+void
+output_author(FILE *output, char *oname)
+{
+ char buf[MAX_COL+1+1]; /* I/O buffer */
+ int more_auths; /* TRUE => more authors to note */
+ int auth_cnt=0; /* number of authors processed */
+
+ /*
+ * prompt the user for the author section
+ */
+ printf("\nEnter information about each author. If your entry is after\n");
+ printf("%s and before the contest deadline, the judges\n", START_DATE);
+ printf("will attempt to email back a confirmation to the first author\n");
+
+ /*
+ * place author information for each author in an individual section
+ */
+ do {
+
+ /* write the start of the section */
+ fprintf(output, "---author---\n");
+ check_io(output, oname, EOF_NOT_OK);
+
+ /* write the author */
+ printf("\nAuthor #%d name: ", ++auth_cnt);
+ while (get_line(buf, MAX_COL+1, MAX_COL-9) <= 0) {
+ printf("\nname too long, please re-enter: ");
+ }
+ fprintf(output, "name:\t%s", buf);
+ check_io(output, oname, EOF_NOT_OK);
+
+ /* write the organization */
+ printf("\nEnter the School/Company/Organization of author #%d\n",
+ auth_cnt);
+ printf("\nAuthor #%d org: ", auth_cnt);
+ while (get_line(buf, MAX_COL+1, MAX_COL-9) <= 0) {
+ printf("\nline too long, please re-enter: ");
+ }
+ fprintf(output, "org:\t%s", buf);
+ check_io(output, oname, EOF_NOT_OK);
+
+ /* write the address */
+ printf(
+ "\nEnter the postal address for author #%d. Be sure to include\n",
+ auth_cnt);
+ printf("your country and do not include your name.\n");
+ output_till_dot(output, oname, "addr:");
+
+ /* write the email address */
+ printf(
+ "\nEnter the email address for author #%d. Use an address from\n",
+ auth_cnt);
+ printf(
+ "a registered domain or well known site. If you give several\n");
+ printf("forms, list them one per line.\n");
+ output_till_dot(output, oname, "email:");
+
+ /* write the home page URL */
+ printf(
+ "\nEnter the fully qualified home page URL for author #%d\n",
+ auth_cnt);
+ printf("including the http: part or type none: ");
+ while (get_line(buf, MAX_COL+1, MAX_COL-9) <= 0 ||
+ (strncmp(buf, "http://", sizeof("http://")-1) != 0 &&
+ strcmp(buf, "none\n") != 0)) {
+ printf("\nURL too long, does not begin with http:// or\n");
+ printf("is not the word none\n");
+ }
+ fprintf(output, "url:\t%s", buf);
+ check_io(output, oname, EOF_NOT_OK);
+
+ /* write the anonymous status */
+ printf("\nShould author #%d remain anonymous (enter y or n)? ",
+ auth_cnt);
+ while (get_line(buf, 1+1, 0) <= 0 || !(buf[0]=='y' || buf[0]=='n')) {
+ printf("\nplease answer y or n: ");
+ }
+ fprintf(output, "anon:\t%s", buf);
+ check_io(output, oname, EOF_NOT_OK);
+
+ /* determine if there is another author */
+ printf("\nIs there another author (enter y or n)? ");
+ while (get_line(buf, 1+1, 0) <= 0 || !(buf[0]=='y' || buf[0]=='n')) {
+ printf("\nplease answer y or n: ");
+ }
+ if (buf[0] == 'y') {
+ more_auths = TRUE;
+ } else {
+ more_auths = FALSE;
+ }
+ } while (more_auths == TRUE);
+ return;
+}
+
+/*
+ * output_info - output the ---info--- section(s)
+ *
+ * usage:
+ * output entry's output file stream
+ * oname name of the output file
+ *
+ * Read the needed information from stdin, and write the info section.
+ * If multiple info files exist, multiple info sections will be written.
+ */
+void
+output_info(FILE *output, char *oname)
+{
+ char infoname[MAX_FILE_LEN+1]; /* filename buffer */
+ char yorn[1+1]; /* y or n answer */
+ char *uuname; /* name to uuencode as */
+ FILE *infile; /* info file stream */
+
+ /*
+ * prompt the user for info information
+ */
+ printf("\nInfo files should be used only to supplement your entry.\n");
+ printf("For example, info files may provide sample input or detailed\n");
+ printf("information about your entry. Because they are supplemental,\n");
+ printf("the entry should not require them to exist.\n\n");
+
+ /*
+ * while there is another info file to save, uuencode it
+ */
+ printf("Do you have a info file to include (enter y or n)? ");
+ while (get_line(yorn, 1+1, 0) <= 0 || !(yorn[0]=='y' || yorn[0]=='n')) {
+ printf("\nplease answer y or n: ");
+ }
+ while (yorn[0] == 'y') {
+
+ /* read the filename */
+ printf("\nEnter the info filename: ");
+ while (get_line(infoname, MAX_FILE_LEN+1, 0) <= 0) {
+ printf("\nInfo filename too long, please re-enter: ");
+ }
+
+ /* compute the basename of the info filename */
+ /* remove the trailing newline */
+ uuname = &infoname[strlen(infoname)-1];
+ *uuname = '\0';
+ /* avoid rindex/shrrchr compat issues, do it by hand */
+ for (--uuname; uuname > infoname; --uuname) {
+ if (*uuname == '/') {
+ ++uuname;
+ break;
+ }
+ }
+
+ /* attempt to open the info file */
+ infile = fopen(infoname, "r");
+ if (infile == NULL) {
+ fprintf(stderr, "\n%s: cannot open info file: %s: ",
+ program, infoname);
+ perror("");
+ continue;
+ }
+
+ /*
+ * write the start of the section
+ */
+ fprintf(output, "---info---\n");
+ check_io(output, oname, EOF_NOT_OK);
+
+ /* uuencode the info file */
+ uuencode(output, oname, infile, infoname, UUINFO_MODE, uuname);
+
+ printf("\nDo you have another info file to include (enter y or n)? ");
+ while (get_line(yorn, 1+1, 0) <= 0 || !(yorn[0]=='y' || yorn[0]=='n')) {
+ printf("\nplease answer y or n: ");
+ }
+ };
+ return;
+}
+
+/*
+ * output_build - output the ---build--- section
+ *
+ * usage:
+ * output entry's output file stream
+ * oname name of the output file
+ * build open build file stream
+ * bname name of the build file
+ *
+ * Read the needed information from stdin, and write the build section.
+ */
+void
+output_build(FILE *output, char *oname, FILE *build, char *bname)
+{
+ /*
+ * write the start of the section
+ */
+ fprintf(output, "---build---\n");
+ check_io(output, oname, EOF_NOT_OK);
+
+ /*
+ * uuencode the program file
+ */
+ uuencode(output, oname, build, bname, UUBUILD_MODE, UUBUILD_NAME);
+ return;
+}
+
+/*
+ * output_program - output the ---program--- section
+ *
+ * usage:
+ * output entry's output file stream
+ * oname name of the output file
+ * prog open program stream
+ * pname name of program file
+ *
+ * Read the needed information form stdin, and write the program section.
+ */
+void
+output_program(FILE *output, char *oname, FILE *prog, char *pname)
+{
+ /*
+ * write the start of the section
+ */
+ fprintf(output, "---program---\n");
+ check_io(output, oname, EOF_NOT_OK);
+
+ /*
+ * uuencode the program file
+ */
+ uuencode(output, oname, prog, pname, UUPROG_MODE, UUPROG_NAME);
+ return;
+}
+
+/*
+ * output_end - output the ---end--- section
+ *
+ * usage:
+ * output entry's output file stream
+ * oname name of the output file
+ *
+ * Read the needed information form stdin, and write the 'end section'.
+ */
+void
+output_end(FILE *output, char *oname)
+{
+ /*
+ * write the final section terminator
+ */
+ fprintf(output, "---end---\n");
+ check_io(output, oname, EOF_NOT_OK);
+ return;
+}
+
+/*
+ * get_line - get an answer from stdin
+ *
+ * usage:
+ * buf input buffer
+ * siz length of input, including the newline
+ * maxcol max col allowed, 0 => disable check
+ *
+ * This function will flush stdout, in case a prompt is pending, and
+ * read in the answer.
+ *
+ * This function returns 0 if the line is too long, of the length of the
+ * line (including the newline) of the line was ok. This function does
+ * not return if ERROR or EOF.
+ */
+int
+get_line(char *buf, int siz, int maxcol)
+{
+ int length; /* the length of the input line */
+
+ /* flush terminal output */
+ fflush(stdout);
+
+ /* read the line */
+ if (fgets(buf, siz+1, stdin) == NULL) {
+ /* report the problem */
+ check_io(stdin, "stdin", EOF_NOT_OK);
+ }
+
+ /* look for the newline */
+ length = strlen(buf);
+ if (buf[length-1] != '\n') {
+ int eatchar; /* the char being eaten */
+
+ /* no newline found, line must be too long, eat the rest of the line */
+ do {
+ eatchar = fgetc(stdin);
+ } while (eatchar != EOF && eatchar != '\n');
+ check_io(stdin, "stdin", EOF_NOT_OK);
+
+ /* report the situation */
+ return 0;
+ }
+
+ /* watch for long lines, if needed */
+ if (maxcol > 0 && (length > maxcol || col_len(buf) > maxcol)) {
+ /* report the situation */
+ return 0;
+ }
+
+ /* return length */
+ return length;
+}
+
+/*
+ * output_till_dot - output a set of lines until '.' by itself is read
+ *
+ * usage:
+ * output entry's output file stream
+ * oname name of the output file
+ * leader the lead text for the first line
+ *
+ * This routine will read a set of lines until (but not including)
+ * a single line with '.' is read. The format of the output is:
+ *
+ * leader:\tfirst line
+ * \tnext line
+ * \tnext line
+ * ...
+ *
+ * This routine will not return if I/O error or EOF.
+ */
+void
+output_till_dot(FILE *output, char *oname, char *leader)
+{
+ char buf[BUFSIZ+1]; /* input buffer */
+ int count; /* lines read */
+ int done=FALSE; /* TRUE => finished reading input */
+
+ /* instruct the user on how to input */
+ printf("\nTo end input, enter a line with a single period.\n");
+
+ /* read lines until '.' or EOF */
+ count = 0;
+ while (!done) {
+ /* issue the prompt */
+ printf("%s\t", (count>0) ? "" : leader);
+ fflush(stdout);
+
+ /* get the line */
+ if (get_line(buf, BUFSIZ, MAX_COL-9) <= 0) {
+ printf("\nline too long, please re-enter:\n\t");
+ continue;
+ }
+
+ /* note if '.' was read */
+ if (strcmp(buf, ".\n") == 0) {
+ done = TRUE;
+ }
+
+ /* write line if we read something */
+ if (!done) {
+ fprintf(output, "%s\t%s", (count++>0) ? "" : leader, buf);
+ check_io(output, oname, EOF_NOT_OK);
+ }
+ }
+
+ /* if no lines read, at least output something */
+ if (count <= 0) {
+ fprintf(output, "%s\t.\n", leader);
+ check_io(output, oname, EOF_NOT_OK);
+ }
+ return;
+}
+
+/*
+ * col_len - determine the highest that a string would reach
+ *
+ * usage:
+ * string the string to examine
+ *
+ * Given a string, this routine returns that a string would reach
+ * if the string were printed at column 1. Tab stops are assumed
+ * to start at 9, 17, 25, 33, ...
+ */
+int
+col_len(char *string)
+{
+ int col; /* current column */
+ char *p; /* current char */
+
+ /* scan the string */
+ for (col=0, p=string; *p != '\0' && *p != '\n'; ++p) {
+ /* note the column shift */
+ col = (*p=='\t') ? 1+((col+8)/8*8) : col+1;
+ }
+ if (*p == '\n') {
+ --col;
+ }
+
+ /* return the highest column */
+ return col;
+}
+
+/*
+ * check_io - check for EOF or I/O error on a stream
+ *
+ * usage:
+ * stream the stream to check
+ * name the name of this stream
+ * eof_ok EOF_OK or EOF_NOT_OK
+ *
+ * Does not return if EOF or I/O error.
+ */
+void
+check_io(FILE *stream, char *name, int eof_ok)
+{
+ /* test for I/O error */
+ if (ferror(stream)) {
+ fprintf(stderr, "%s: error on %s: ", program, name);
+ perror("");
+ exit(1);
+
+ /* test for EOF */
+ } else if (eof_ok == EOF_NOT_OK && feof(stream)) {
+ fprintf(stderr, "%s: EOF on %s\n", program, name);
+ exit(1);
+ }
+ return;
+}
+
+/*
+ * uuencode - uuencode a file
+ *
+ * usage:
+ * output output file stream
+ * oname output filename
+ * infile input file stream
+ * iname input filename
+ * umode the mode to put on the uuencode file
+ * uname name to put on the uuencode file
+ *
+ * Perform the uuencoding process identical to the process performed
+ * by the uuencode(1) utility.
+ *
+ * This routine implements the algorithm described in the uuencode(5)
+ * 4.3BSD Reno man page.
+ */
+void
+uuencode(FILE *output, char *oname, FILE *infile,
+ char *iname, int umode, char *uname)
+{
+ char buf[UUENCODE_LEN+1]; /* the uuencode buffer */
+ int read_len; /* actual number of chars read */
+ int val; /* 6 bit chunk from buf */
+ char filler='\0'; /* filler uuencode pad text */
+ char *p;
+
+ /*
+ * output the initial uuencode header
+ */
+ fprintf(output, "begin %o %s\n", umode, uname);
+ check_io(output, oname, EOF_NOT_OK);
+
+ /*
+ * clear out the input buffer
+ */
+ for (p=buf; p < &buf[sizeof(buf)/sizeof(buf[0])]; ++p) {
+ *p = '\0';
+ }
+
+ /*
+ * We will process UUENCODE_LEN chars at a time, forming
+ * a single output line each time.
+ */
+ while ((read_len=fread(buf,sizeof(buf[0]),UUENCODE_LEN,infile)) > 0) {
+
+ /*
+ * the first character is the length character
+ */
+ fputc(UUENCODE(read_len), output);
+ check_io(output, oname, EOF_NOT_OK);
+
+ /*
+ * We will convert 24 bits at a time. Thus we will convert
+ * 3 sets of 8 bits into 4 sets of uuencoded 6 bits.
+ */
+ for (p=buf; read_len>0; read_len-=3, p+=3) {
+
+ /* bits 0 to 5 */
+ val = (p[0]>>2)&0x3f;
+ fputc(UUENCODE(val), output);
+ check_io(output, oname, EOF_NOT_OK);
+
+ /* bits 6 to 11 */
+ val = ((p[0]<<4)&0x30) | ((p[1]>>4)&0x0f);
+ fputc(UUENCODE(val), output);
+ check_io(output, oname, EOF_NOT_OK);
+
+ /* bits 12 to 17 */
+ val = ((p[1]<<2)&0x3c) | ((p[2]>>6)&0x03);
+ fputc(UUENCODE(val), output);
+ check_io(output, oname, EOF_NOT_OK);
+
+ /* bits 18 to 23 */
+ val = p[2]&0x3f;
+ fputc(UUENCODE(val), output);
+ check_io(output, oname, EOF_NOT_OK);
+ }
+
+ /* end of UUENCODE_LEN line */
+ fputc('\n', output);
+ check_io(output, oname, EOF_NOT_OK);
+
+ /*
+ * clear out the input buffer (don't depend on bzero() or memset())
+ */
+ for (p=buf; p < &buf[sizeof(buf)/sizeof(buf[0])]; ++p) {
+ *p = '\0';
+ }
+ }
+
+ /* check the last read on the input file */
+ check_io(infile, iname, EOF_OK);
+
+ /* write end of uuencode file */
+ fprintf(output, "%c\nend\n", UUENCODE(filler));
+ check_io(output, oname, EOF_NOT_OK);
+}
diff --git a/tmp/winners.csv b/tmp/winners.csv
index 6efdfa5cf..c2a0ff300 100644
--- a/tmp/winners.csv
+++ b/tmp/winners.csv
@@ -179,7 +179,7 @@ Teemu Rantanen,Teemu_Rantanen,http://www.cs.hut.fi/~tvr/,null,tvr@cs.hut.fi,null
Tero Kivinen,Tero_Kivinen,http://www.iki.fi/kivinen/,null,kivinen@iki.fi,null,null);
Thaddaeus Frogley,Thaddaeus_Frogley,http://thad.notagoth.org/,null,codemonkey_uk@hotmail.com,null,null);
Thomas A. Fine,Thomas_A._Fine,http://hea-www.harvard.edu/~fine/,null,fine@head-cfa.harvard.edu,null,null);
-Thomas P John,Thomas_P_John,http://tomx.tripod.com/,null,thomaspjohn@hotmail.com,null,null);
+Thomas P John,Thomas_P_John,https://web.archive.org/web/20010720192926/http://tomx.tripod.com/,thomaspjohn@hotmail.com,null,null);
Thomas Stewart,Thomas_Stewart,null,null,null,US,null);
Thor Eldby,Thor_Eldby,null,null,null,null,null);
Timo Poikola,Timo_Poikola,https://ig.fi/other/ioccc,null,timo@ig.fi,null,@timpoiko);
diff --git a/winners.html b/winners.html
index 16e51a694..04aef6139 100644
--- a/winners.html
+++ b/winners.html
@@ -663,7 +663,7 @@ Contest