Files
ioccc/2004/mkentry.c
T
Cody Boone Ferguson e8a3b50211 Change references of 'EMail' to '[Ee]mail'
Depending on if it was the beginning of the sentence the EMail would
become 'Email' or 'email'.

Some references of 'Email' were changed to 'email' as well as those were
not in the beginning of a sentence.

A few places some consistency of capitalised email address (made lower
case) were changed to match other places of the same pattern.
2023-02-10 06:01:22 -08:00

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 an 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 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 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 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);
}