mirror of
https://github.com/deater/tb1.git
synced 2024-12-23 04:29:42 +00:00
1303 lines
35 KiB
C
1303 lines
35 KiB
C
/* $Id: mkentry.c,v 1.3 2004/01/27 06:53:08 chongo Exp $ */
|
|
/*
|
|
* Copyright (c) Leonid A. Broukhis, Simon Cooper, Landon Curt Noll and
|
|
* Peter Seebach, 2004.
|
|
*
|
|
* 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 EMail to the following address:
|
|
*
|
|
* questions@ioccc.org (not the address for submitting entries)
|
|
*
|
|
* Questions and comments about the contest are welcome.
|
|
*
|
|
* NOTE: You must include the words ``ioccc question'' in the
|
|
* subject of your EMail message when sending EMail to the judges.
|
|
*
|
|
* Entries must be sent to:
|
|
*
|
|
* entry@ioccc.org
|
|
*
|
|
* but be sure to follow the rules and read the guidelines first!
|
|
*
|
|
* NOTE: You must include the words ``ioccc entry'' in the subject
|
|
* of your EMail when sending in your entry!
|
|
*
|
|
* 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 <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
#include <time.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
|
|
/* 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 2004 /* NOTE: should match the current year */
|
|
#define START_DATE "07Jan2004 00:00 UTC" /* first confirmation received */
|
|
#define MAX_COL 79 /* max column a line should hit */
|
|
#define MAX_BUILD_SIZE 521 /* max how to build size */
|
|
#define MAX_PROGRAM_SIZE 4096 /* max program source size */
|
|
#define MAX_PROGRAM_SIZE2 2048 /* 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_USER "e.2004"
|
|
#define ENTRY_HOST "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@%s\n\n", ENTRY_USER, ENTRY_HOST);
|
|
printf("IMPORTANT NOTE: You must include the words:\n");
|
|
printf("\tioccc entry\n\n");
|
|
printf("in the subject of your EMail when sending in your entry!\n");
|
|
printf("Failure to do so may result in the loss of your entry!\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 */
|
|
int control_m; /* 1 ==> warn about trailing ^M's */
|
|
|
|
/*
|
|
* 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
|
|
*/
|
|
control_m = 0;
|
|
count = 0;
|
|
c = 0;
|
|
while ((c=fgetc(stream)) != EOF) {
|
|
|
|
/* look at non-whitespace */
|
|
if (isascii(c) && !isspace(c) && c != '\r') {
|
|
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) && c != '\r') {
|
|
/* not followed by whitespace or EOF, count it */
|
|
ungetc(c, stream);
|
|
++count;
|
|
}
|
|
break;
|
|
default:
|
|
++count;
|
|
break;
|
|
}
|
|
|
|
/* look for trailing ^M's */
|
|
} else if (c == '\r') {
|
|
/* peek at next char */
|
|
c = fgetc(stream);
|
|
if (c != EOF && c == '\n') {
|
|
control_m = 1;
|
|
}
|
|
/* put the peeking char back */
|
|
ungetc(c, stream);
|
|
}
|
|
}
|
|
|
|
/* 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);
|
|
}
|
|
|
|
/* warn about trailing ^M's */
|
|
if (control_m) {
|
|
fprintf(stderr,
|
|
"\nWARNING: Trailing Control-M's (\\r or \\015) found.\n"
|
|
"\t If these chars result in a compilation failure,\n"
|
|
"\t your entry may be rejected\n\n");
|
|
}
|
|
|
|
/* 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 resubmittion 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 case of multiple authors,\n");
|
|
printf("consider using parts of the usernames 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 just enter 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);
|
|
}
|