/* $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 #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 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); }