Mercurial > repo
view interps/c-intercal/src/perpet.c @ 12518:2d8fe55c6e65 draft default tip
<int-e> learn The password of the month is release incident pilot.
author | HackEso <hackeso@esolangs.org> |
---|---|
date | Sun, 03 Nov 2024 00:31:02 +0000 |
parents | 859f9b4339e6 |
children |
line wrap: on
line source
/**************************************************************************** NAME perpet.c -- main routine for C-INTERCAL compiler. DESCRIPTION This is where all the dirty work begins and ends. LICENSE TERMS Copyright (C) 1996 Eric S. Raymond This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ****************************************************************************/ /*LINTLIBRARY */ #include "config.h" /* AIS: Generated by autoconf */ #include <stdio.h> #include <stdlib.h> #ifdef HAVE_UNISTD_H # include <unistd.h> #endif #include <string.h> #include <signal.h> #include <time.h> #include <assert.h> #include "ick.h" #include "feh.h" #include "parser.h" #include "sizes.h" #include "ick_lose.h" #include "uncommon.h" /* AIS: split ICKDATADIR from ICKLIBDIR */ #ifndef ICKINCLUDEDIR #define ICKINCLUDEDIR "/usr/local/include" #endif #ifndef ICKDATADIR #define ICKDATADIR "/usr/local/share" #endif #ifndef ICKLIBDIR #define ICKLIBDIR "/usr/local/lib" #endif #ifndef ICKBINDIR #define ICKBINDIR "/usr/local/bin" #endif #ifndef CC #define CC "gcc" #endif #define ARGSTRING "abcdefghlmoptuvwxyCEFHOPUYX@" #define ICK_SYSTEM(x) do{if(showsystem)fprintf(stderr,"%s\n",x); \ (void) system(x);}while(0) #ifdef USE_YYRESTART /* function supplied by lex */ extern void yyrestart(FILE*); #endif /* USE_YYRESTART */ /* function created by yacc */ extern int yyparse(void); int yydebug; /* compilation options */ ick_bool compile_only; /* just compile into C, don't run the linker */ ick_bool ick_traditional; /* insist on strict INTERCAL-72 conformance */ ick_bool nocompilerbug; /* disable error IE774 */ int yukdebug; /* AIS: Use the yuk debugger. */ int yukprofile; /* AIS: Use the yuk profiler. */ int useprintflow=0; /* AIS: Add +printflow support. */ extern int ick_coreonerr; /* AIS: Dump core on IE778. (defined in ick_lose.c) */ int multithread; /* AIS: Allow multithreading and backtracking. */ int variableconstants; /* AIS: Allow anything on the left of an assignment. */ int createsused=0; /* AIS: Allow the use of CREATE. */ int useickec; /* AIS: Link together INTERCAL and C. */ static int nosyslib=0; /* AIS: Don't link syslib under any circumstances. */ static int showsystem=0;/* AIS: Show the command lines in system() calls */ int cdebug; /* AIS: Pass -g to our C compiler, and leave C code. */ int optdebug; /* AIS: Debug the optimizer. Value is 0, 1, 2, or 3. */ int flowoptimize; /* AIS: Do flow optimizations (in INTERCAL!). */ int coopt; /* AIS: The constant-output optimization. This should mean that INTERCAL will beat any other language at many benchmark programs (!) */ extern int ick_printfopens; /* AIS: Print messages whenever attempting to open a file: from uncommon.c */ extern int ick_checkforbugs;/* AIS: Try to find possible bugs in the source code */ int pickcompile; /* AIS: Compile for PIC? */ /*@-exportlocal@*/ /* AIS: relevant to the lexer */ int clclex; /* AIS: 1 means use CLC-INTERCAL meanings for @, ? */ /*@=exportlocal@*/ int ick_clcsemantics; /* AIS: CLC semantics for I/O, abstaining GIVE UP, &c*/ static int outtostdout; /* AIS: Output on stdout rather than the output file */ /* AIS: Autodetected compilation options */ int compucomecount=0; /* Computed COME FROM count */ int compucomesused=0; /* Are computed COME FROMs used? */ int gerucomesused=0; /* Is COME FROM gerund used? */ int nextfromsused=0; /* Is NEXT FROM used? */ int opoverused=0; /* Is operand overloading used? */ /*@null@*/ node* firstslat=0; /* The first slat expression in the program */ /*@null@*/ node* prevslat=0; /* The last slat expression used so far */ static ick_bool dooptimize; /* do optimizations? (controlled by -O) */ static ick_bool ick_clockface; /* set up output to do IIII for IV */ #define SKELETON "ick-wrap.c" #define PSKELETON "pickwrap.c" #define SYSLIB "syslib" /* numeric base defaults, exported to other files */ #define DEFAULT_BASE 2 #define DEFAULT_SMALL_DIGITS 16 #define DEFAULT_LARGE_DIGITS 32 #define DEFAULT_MAX_SMALL 0xffffL #define DEFAULT_MAX_LARGE 0xffffffffL int ick_Base; int ick_Small_digits; int ick_Large_digits; unsigned int ick_Max_small; unsigned int ick_Max_large; int ick_lineno; /* after yyparse, this is the total number of statements */ /* currently supported numeric bases, not exported */ static const int maxbase = 7; static const int smallsizes[8] = {0, 0, 16, 10, 8, 6, 6, 5}; static const unsigned int maxsmalls[8] = {0, 0, 65535, 59048, 65535, 15624, 46655, 16806}; /*@observer@*/ static const char *compiler; atom *oblist = NULL, *obdex; int obcount = 0; int nonespots, ntwospots, ntails, nhybrids; int nmeshes; /* AIS */ tuple *tuples = NULL; int tuplecount = 0; tuple *optuple = NULL; /* AIS: Tuple being optimized */ extern const assoc varstores[]; /* AIS: Need to know this for PIC compilation */ #ifndef HAVE_UNISTD_H /* AIS: We don't have unistd.h, so we can't use getopt. Write our own version that's less general but good enough. */ int optind=1; int optopt; int getopt(int argc, char * const *argv, const char *options) { if(optind>argc) return EOF; /* Out of command line */ if(!argv[optind]) return EOF; /* Out of command line */ while(!strcmp(argv[optind],"-")) { optind++; /* Go to ick_next argument */ if(!argv[optind]) return EOF; } if(*(argv[optind])!='-') return EOF; /* this arg is not an option */ optopt=argv[optind][1]; memmove(argv[optind]+1,argv[optind]+2,strlen(argv[optind]+1)); if(optopt=='-') {optind++; return EOF;} /* -- means end of options */ if(strchr(options, optopt)) return optopt; /* valid option */ return '?'; /* invalid option */ } #endif static int myfgetc(FILE* in) { char c; (void) fread(&c,1,1,in); if(feof(in)) return EOF; return (int)c; } static RETSIGTYPE abend(int signim) { /*@-noeffect@*/ (void) signim; /*@=noeffect@*/ ick_lose(IE778, iyylineno, (const char *)NULL); } static void print_usage(const char *prog, const char *options) { fprintf(stderr,"Usage: %s [-%s] <file> [<file> ...]\n",prog,options); fprintf(stderr,"\t-b\t:reduce the probability of E774 to zero\n"); fprintf(stderr,"\t-c\t:compile INTERCAL to C, but don't compile C\n"); fprintf(stderr,"\t-d\t:print yacc debugging information (implies -c)\n"); fprintf(stderr,"\t-e\t:link together INTERCAL and C files as one program\n"); fprintf(stderr,"\t\t (without this option, all INTERCAL files produce\n"); fprintf(stderr,"\t\t separate output files; with it, the first file given\n"); fprintf(stderr,"\t\t must be the only INTERCAL file) (prevents -mypPf)\n"); fprintf(stderr,"\t-E\t:never include the system library (prevents -P)\n"); fprintf(stderr,"\t-t\t:traditional mode, accept only INTERCAL-72\n"); fprintf(stderr,"\t-C\t:clockface output (e.g. use IIII instead of IV)\n"); fprintf(stderr,"\t-O\t:optimize expresssions in generated code\n"); /* AIS: Changed the help message for the previous line (because the function of -O has changed). I wrote the next group of options. */ fprintf(stderr,"\t-f\t:optimize control flow in generated code " "(prevents -yp)\n"); #ifdef HAVE_PROG_SH # ifdef HAVE_SYS_INTERPRETER fprintf(stderr,"\t-F\t:optimize everything in generated code for\n" "\t\t speed, regardless of how slow the compiler becomes or how\n" "\t\t large the object file becomes. Implies -fO, " "prevents -cdeghpyH\n"); # else fprintf(stderr,"\t-F\t:unsupported on computers without #! support\n"); # endif #else fprintf(stderr,"\t-F\t:unsupported on computers without sh or bash\n"); #endif fprintf(stderr,"\t-h\t:print optimizer debugging information " "(implies -cO)\n"); fprintf(stderr,"\t-H\t:print verbose optimizer debugging information " "(implies -cO)\n"); fprintf(stderr,"\t-hH\t:print optimizer debugging information in a\n" "\t\t different form (implies -cO)\n"); #ifdef HAVE_UNISTD_H fprintf(stderr,"\t-y\t:run the yuk debugger on the code (prevents -fme)\n"); fprintf(stderr,"\t-p\t:run the yuk profiler on the code (prevents -fme)\n"); #else fprintf(stderr,"\t-y\t:unsupported on computers without <unistd.h>\n"); fprintf(stderr,"\t-p\t:unsupported on computers without <unistd.h>\n"); #endif fprintf(stderr,"\t-w\t:add support for the +printflow option\n"); fprintf(stderr,"\t-m\t:allow multithreading and backtracking\n" "\t\t (prevents -ype, implies -w)\n"); fprintf(stderr,"\t-a\t:allow the use of CREATE (prevents -P)\n"); fprintf(stderr,"\t-v\t:allow anything on the left of an assignment. This " "is required\n\t\t if you want operand overloading to change " "meshes.\n\t\t (prevents -fFOP)\n"); fprintf(stderr,"\t-P\t:compile PIC-INTERCAL rather than INTERCAL\n"); fprintf(stderr,"\t\t (prevents -amFvxeE, implies -cfO)\n"); fprintf(stderr,"\t-o\t:output to stdout rather than .c (implies -c)\n"); fprintf(stderr,"\t-X\t:interpret ambiguous syntax as Princeton not\n" "\t\t Atari (i.e. CLC-INTERCAL not C-INTERCAL)\n"); fprintf(stderr,"\t-x\t:use CLC-INTERCAL rules for I/O and abstaining\n" "\t\t from a GIVE UP by label (prevents -P)\n"); fprintf(stderr,"\t-u\t:print a message whenever the compiler tries to " "open a file\n"); fprintf(stderr,"\t-U\t:dump core on IE778 after printing an error\n"); fprintf(stderr,"\t-Y\t:display the command line used whenever an external\n" "\t\t program is invoked\n"); fprintf(stderr,"\t-g\t:compile to both debuggable executable and C\n"); fprintf(stderr,"\t-l\t:attempt to report likely bugs " "and nonportabilities (implies -O)\n"); /* AIS: End of options I added. */ fprintf(stderr,"\t<file>\tINTERCAL source file (use extension .i\n"); fprintf(stderr,"\t\tfor base 2 or .3i, etc., for base 3, etc.).\n"); } #if __DJGPP__ /* AIS: Determine whether an environment variable exists (this is used to find a temp directory) */ static int isenv(char* e) { char* x=getenv(e); return x != NULL && *x != '\0'; } #endif extern int optind; /* set by getopt */ /** * This parses command line options. * @param argc What do you think? * @param argv Likewise. * @note May (directly) call ick_lose() with IE111 and IE256. */ static void parse_options(int argc, char *argv[]) { int c; /* getopt is POSIX, and I provide my own version if the POSIX version isn't found, so the unrecog warning is a false positive. */ /*@-unrecog@*/ while ((c = getopt(argc, argv, ARGSTRING)) != EOF) /*@=unrecog@*/ { switch (c) { case 'b': nocompilerbug = ick_TRUE; break; case 'c': compile_only = ick_TRUE; /* AIS */ coopt = ick_FALSE; break; case 'o': /* AIS */ compile_only = ick_TRUE; outtostdout = ick_TRUE; coopt = ick_FALSE; break; case 'd': yydebug = compile_only = ick_TRUE; /* AIS */ coopt = ick_FALSE; break; case 'e': /* AIS */ useickec = ick_TRUE; multithread = pickcompile = coopt = yukdebug = yukprofile = ick_FALSE; break; case 'E': /* AIS */ nosyslib = ick_TRUE; pickcompile = ick_FALSE; break; case 'C': ick_clockface = ick_TRUE; break; case 't': ick_traditional = ick_TRUE; if(multithread) ick_lose(IE111, 1, (const char*) NULL); /* AIS */ if(pickcompile) ick_lose(IE111, 1, (const char*) NULL); /* AIS */ break; case 'O': dooptimize = ick_TRUE; variableconstants = ick_FALSE; /* AIS */ break; case 'f': /* By AIS */ flowoptimize = ick_TRUE; yukdebug = yukprofile = ick_FALSE; variableconstants = ick_FALSE; break; case 'F': /* By AIS */ coopt = flowoptimize = dooptimize = ick_TRUE; variableconstants = useickec = ick_FALSE; yukdebug = yukprofile = yydebug = outtostdout = compile_only = cdebug = ick_FALSE; if(pickcompile) ick_lose(IE256, 1, (const char*) NULL); break; case 'h': /* By AIS */ optdebug|=1; compile_only=dooptimize=ick_TRUE; coopt=ick_FALSE; break; case 'H': /* By AIS */ optdebug|=2; compile_only=dooptimize=ick_TRUE; coopt=ick_FALSE; break; case 'y': /* By AIS */ #ifdef HAVE_UNISTD_H yukdebug=ick_TRUE; multithread=flowoptimize=coopt=useickec=ick_FALSE; #endif break; case 'p': /* By AIS */ #ifdef HAVE_UNISTD_H yukprofile=ick_TRUE; multithread=flowoptimize=coopt=useickec=ick_FALSE; #endif break; case 'w': /* By AIS */ useprintflow = ick_TRUE; break; case 'm': /* By AIS */ multithread=ick_TRUE; yukprofile=ick_FALSE; yukdebug=ick_FALSE; useickec=ick_FALSE; if(ick_traditional) ick_lose(IE111, 1, (const char*) NULL); break; case 'a': /* By AIS */ createsused=ick_TRUE; pickcompile=ick_FALSE; break; case 'v': /* By AIS */ variableconstants=ick_TRUE; dooptimize=ick_FALSE; flowoptimize=ick_FALSE; coopt=ick_FALSE; pickcompile=ick_FALSE; break; case 'l': /* By AIS */ ick_checkforbugs=ick_TRUE; dooptimize=ick_TRUE; break; case 'U': /* By AIS */ ick_coreonerr=ick_TRUE; break; case 'u': /* By AIS */ ick_printfopens=ick_TRUE; break; case 'Y': /* By AIS */ showsystem=ick_TRUE; break; case 'P': /* By AIS */ pickcompile=ick_TRUE; multithread=coopt=variableconstants=createsused=ick_FALSE; ick_clcsemantics=useickec=nosyslib=ick_FALSE; compile_only=ick_TRUE; dooptimize=flowoptimize=ick_TRUE; /* needed for PICs */ break; case 'X': /* By AIS */ clclex=ick_TRUE; break; case 'x': /* By AIS */ ick_clcsemantics=ick_TRUE; pickcompile=ick_FALSE; break; case 'g': /* By AIS */ cdebug=ick_TRUE; coopt=ick_FALSE; break; case '?': default: case '@': print_usage(argv[0], ARGSTRING); exit(EXIT_FAILURE); /*@-unreachable@*/ break; /*@=unreachable@*/ } } } /** * This code handles archives (for -e). * @param libbuf Pointer to a buffer to which extra files to link in prelink() * will be added. Need to be initialized up to the first zero byte. * @param libbuf_size Size of the buffer libbuf. * @param library The cmd line argument used for the library (but without the * extension). */ static void handle_archive(char *libbuf, size_t libbuf_size, /*@observer@*/ const char* library) { /* AIS: request for a library. Given a filename of the form libwhatever.a, it adds -lwhatever to libbuf (that's with a preceding space). If the filename doesn't start with lib, it instead adds a space and the filename to libbuf. */ if(library[0]=='l'&&library[1]=='i'&& library[2]=='b') ick_snprintf_or_die(libbuf+strlen(libbuf),libbuf_size - strlen(libbuf), " -l%s",library+3); else ick_snprintf_or_die(libbuf+strlen(libbuf),libbuf_size - strlen(libbuf), " %s.a",library); } /** * This handles Befunge 98 (for -e). * @param libbuf Pointer to a buffer to which extra files to link in prelink() * will be added. Need to be initialized up to the first zero byte. * @param libbuf_size Size of the buffer libbuf. * @param libdir The ick library directory. * @param argv0 Should be argv[0], which wasn't modified. * @param filename The file name of the Befunge file (but without the extension). * @note May (directly) call ick_lose() with IE888 and IE899. */ static void handle_befunge98(char *libbuf, size_t libbuf_size, /*@observer@*/ const char* libdir, /*@observer@*/ const char* argv0, /*@observer@*/ const char* filename) { /* AIS: Compile the .b98 file into a .cio so that it can be used later, and include the necessary libraries to use it, or error if the libraries aren't installed yet. I use a somewhat dubious trick here: the .b98 file's .cio, and the necessary libraries, are added in the libraries section of the command line, whereas the space on the command line where the .b98 file was is used for the expansion library ecto_b98. This is because ecto_b98 requires preprocessing/prelinking/interprocessing or whatever you want to call it, whereas unlike for other .cios, the .cio produced from the Befunge file doesn't. */ #define MARKERMAX 128 FILE* of; int x,y,jlb; char outputfilename[BUFSIZ]; int markerposns[MARKERMAX][2]; int markercount=0; /* Error if libick_ecto_b98.a is missing. It might be, and not just due to installation problems. */ if(!ick_findandtestopen("libick_ecto_b98.a",libdir,"rb",argv0)) ick_lose(IE899,-1,(const char *)NULL); /* Compile the .b98 file into a .cio. It's open on stdin right now, so we just need to handle the output side of things. */ ick_snprintf_or_die(outputfilename, sizeof outputfilename, "%s.cio", filename); if(!((of = ick_debfopen(outputfilename,"w")))) ick_lose(IE888,-1,(const char *)NULL); fprintf(of,"const unsigned char* ick_iffi_befungeString=\n\""); x=0; y=0; jlb=0; for(;;) { int c=getchar(); if(c==EOF) break; if(c==0xB7) { /* Middot (0xB7) has special handling. */ c='M'; markerposns[markercount][0]=x; markerposns[markercount++][1]=y; } if(c=='\r') {jlb = 1; x=0; y++; c='\n';} else if(c=='\n' && jlb) {jlb = 0; continue;} else if(c=='\n') {x=0; y++; jlb = 0;} else x++; fprintf(of,"\\x%x",(unsigned int)c); if(!x) fprintf(of,"\"\n\""); } fprintf(of,"\";\n\nint ick_iffi_markercount=%d;\n" "long long ick_iffi_markerposns[][2]={\n",markercount); if(!markercount) fprintf(of,"{0,0}\n"); while(markercount--) fprintf(of,"{%d,%d},\n", markerposns[markercount][0], markerposns[markercount][1]); fprintf(of,"};\n"); (void) fclose(of); /* Put the libraries and .cio file in the command line. */ ick_snprintf_or_die(libbuf+strlen(libbuf),libbuf_size - strlen(libbuf), " %s.cio -lick_ecto_b98 -lm -lncurses", filename); } /** * This computes what type the INTERCAL source file is. * It will change various globals. * @param chp A pointer to the first letter of the extension of the file. Note * that it won't be changed, but can't be const char* due to being * passed as the second parameter to strtol() as well. * @note May (directly) call ick_lose() with IE111, IE256 and IE998. */ static void find_intercal_base(char* chp) { /* wwp: reset the base variables to defaults, because if the */ /* sourcefile has extension .i they will not be reset in the */ /* following chunk of code. but i don't want to modify the */ /* following chunk of code because i think it is very clever; */ /* grabs the base on the first pass, then validates the rest */ /* of the extension on the second. */ ick_Base = DEFAULT_BASE; ick_Small_digits = DEFAULT_SMALL_DIGITS; ick_Large_digits = DEFAULT_LARGE_DIGITS; ick_Max_small = (unsigned)DEFAULT_MAX_SMALL; ick_Max_large = (unsigned)DEFAULT_MAX_LARGE; /* determine the file type from the extension */ while (strcmp(chp,"i")) { ick_Base = (int)strtol(chp,&chp,10); if (ick_Base < 2 || ick_Base > maxbase) ick_lose(IE998, 1, (const char *)NULL); else if (ick_traditional && ick_Base != 2) ick_lose(IE111, 1, (const char *)NULL); else if (pickcompile && ick_Base != 2) ick_lose(IE256, 1, (const char *)NULL); /* AIS */ ick_Small_digits = smallsizes[ick_Base]; ick_Large_digits = 2 * ick_Small_digits; ick_Max_small = maxsmalls[ick_Base]; if (ick_Max_small == 0xffff) ick_Max_large = (unsigned)0xffffffffLU; else ick_Max_large = (ick_Max_small + 1) * (ick_Max_small + 1) - 1; } } /** * This checks if we automagically need to include syslib * @param buffer Output buffer that may be modified to contain the path of * syslib.i or syslib.Ni (where N is 3-7). * @param size Size of buffer. * @param needsyslib Pointer to the ick_bool needsyslib declared in main(). * @param argv0 Should be argv[0], which wasn't modified. * @param ick_datadir The ick data directory. * @note May (directly) call ick_lose() with IE127. */ static void check_syslib(/*@partial@*/ char *buffer, size_t size, /*@out@*/ ick_bool *needsyslib, /*@observer@*/ const char *argv0, /*@observer@*/ const char *ick_datadir) { tuple *tp; /* * check if we need to magically include the system library */ *needsyslib = ick_FALSE; if(!pickcompile) /* AIS: We never need syslib when compiling for PIC, because it's preoptimized. */ { for (tp = tuples; tp->type; tp++) { /* * If some label in the (1000)-(2000) range is defined, * then clearly the syslib is already there, so we * can stop searching and won't need the syslib. */ if (tp->label >= 1000 && tp->label <= 1999) { *needsyslib = ick_FALSE; break; } /* * If some label in the (1000)-(2000) range is being * called, we might need the system library. */ if (tp->type == NEXT && tp->u.target >= 1000 && tp->u.target <= 1999) *needsyslib = ick_TRUE; } } if(nosyslib) *needsyslib = ick_FALSE; /* AIS */ if (*needsyslib) { /* AIS: modified to use ick_findandfreopen */ if (ick_Base == 2) /* see code for opening the skeleton */ (void) ick_snprintf_or_die(buffer, size, "%s.i", SYSLIB); else (void) ick_snprintf_or_die(buffer, size, "%s.%di", SYSLIB, ick_Base); if (ick_findandfreopen(buffer, ick_datadir, "r", argv0, stdin) == NULL) ick_lose(IE127, 1, (const char*) NULL); #ifdef USE_YYRESTART yyrestart(stdin); #endif /* USE_YYRESTART */ (void) yyparse(); textlinecount=iyylineno; } } /** * This code propagates type information up the expression tree. * It also does some unrelated stuff such as checking for WRITE IN and disabling * coopt if that is found. */ static void propagate_typeinfo(void) { tuple *tp; /* * Now propagate type information up the expression tree. * We need to do this because the unary-logical operations * are sensitive to the type widths of their operands, so * we have to generate different code depending on the * deducible type of the operand. */ for (tp = tuples; tp->type; tp++) { if (tp->type == GETS || tp->type == RESIZE || tp->type == WRITE_IN || tp->type == READ_OUT || tp->type == FROM || tp->type == MANYFROM || tp->type == FORGET || tp->type == RESUME || tp->type == COMPUCOME || tp->type == UNKNOWN) typecast(tp->type == MANYFROM ? tp->u.node->lval : tp->u.node); if (tp->type == WRITE_IN) coopt = 0; /* AIS: may as well do this here */ } } /** * This runs the optimiser. */ static void run_optimiser(void) { tuple *tp; /* perform optimizations */ if (dooptimize) for (tp = tuples; tp->type; tp++) { /* AIS: Allow breaching of the only specification on tuples at this point; I've checked that tuples isn't reallocated during the block, so this is fine. */ /*@-onlytrans@*/ optuple = tp; /*@=onlytrans@*/ if (tp->type == GETS || tp->type == RESIZE || tp->type == FORGET || tp->type == RESUME || tp->type == FROM || tp->type == COMPUCOME) optimize(tp->u.node); if (tp->type == MANYFROM) optimize(tp->u.node->lval); } /* AIS: Added FROM and MANYFROM support. */ /* AIS: perform flow optimizations */ if (flowoptimize) optimizef(); } /** * Generate random line number for E774. * @returns A random line number, or -1 for no random bug generated. */ static int randomise_bugline(void) { /* decide if and where to place the compiler bug */ #ifdef USG if (!nocompilerbug && lrand48() % 10 == 0) return (int)(lrand48() % ick_lineno); #else if (!nocompilerbug && rand() % 10 == 0) return rand() % ick_lineno; #endif else return -1; } /** * This opens the outfile. * @param filename The filename to open. * @returns A pointer to a FILE for the filename. If the global outtostdout is * set then it will return stdout. * @note May (directly) call ick_lose() with IE888. */ static /*@dependent@*/ FILE* open_outfile(/*@observer@*/ const char * filename) { FILE *ofp; /* AIS: ofp holds fopened storage if !outtostdout, and local-copy storage if outtostdout, and this is not a bug, although it confuses Splint. */ /*@-branchstate@*/ if(outtostdout) ofp=stdout; /* AIS */ else if((ofp = ick_debfopen(filename, "w")) == (FILE *)NULL) ick_lose(IE888, 1, (const char *)NULL); /*@=branchstate@*/ return ofp; } /** * This generates the CC command line. * @param buffer Output buffer. * @param size Size of the output buffer. * @param sourcefile The name of the C file. * @param includedir The ick include directory. * @param path The path of the ick binary (execuding filename). * @param libdir The ick library directory. * @param outputfile The name of the output file. */ static void gen_cc_command(char* buffer, size_t size, /*@observer@*/ const char* sourcefile, /*@observer@*/ const char* includedir, /*@observer@*/ const char* path, /*@observer@*/ const char* libdir, /*@observer@*/ const char* outputfile) { (void) ick_snprintf_or_die(buffer, size, "%s %s%s-I%s -I%s -I%s/../include -L%s -L%s -L%s/../lib -O%c -o %s" EXEEXT " -lick%s%s", #ifdef __DJGPP__ "", #else compiler, #endif #ifdef HAVE_CLOCK_GETTIME /* implies -lrt is available */ sourcefile, yukdebug||yukprofile?" -lyuk -lrt ":" ", #else sourcefile, yukdebug||yukprofile?" -lyuk ":" ", #endif includedir, path, path, libdir, path, path, cdebug?'0':coopt?'3':'2', /* AIS: If coopting, optimize as much as possible JH: [d]on't optimise when compiling with debugger support */ outputfile, multithread?"mt":"", cdebug?" -g":""); /* AIS: Possibly link in the debugger yuk and/or libickmt.a here. */ /* AIS: Added -g support. */ /* AIS: Added argv[0] (now path) to the -I, -L settings. */ } /** * This generates the actual C code. * @param ifp This should be opened to either ick-wrap.c or pickwrap.c. * @param ofp The out file. * @param source_name_stem Source name stem * @param needsyslib Pointer to the needsyslib bool in main(). * @param bugline What line number to add a random bug to. * @param compilercommand The compiler command line. * @note May (directly) call ick_lose() with IE256. */ static void generate_code(FILE *ifp, FILE *ofp, /*@observer@*/ const char* source_name_stem, ick_bool *needsyslib, int bugline, /*@observer@*/ const char* compilercommand) { int maxabstain; int c, i; tuple *tp; atom *op; while ((c = myfgetc(ifp)) != EOF) if (c != (int)'$') (void) fputc(c, ofp); else switch(myfgetc(ifp)) { case 'A': /* source name stem */ (void) fputs(source_name_stem, ofp); break; case 'B': /* # of statements */ (void) fprintf(ofp, "%d", ick_lineno); break; case 'C': /* initial abstentions */ /* AIS: Modified to check for coopt, pickcompile */ maxabstain = 0; for (tp = tuples; tp->type; tp++) if (((tp->exechance <= 0 || tp->exechance >= 101) && tp - tuples + 1 > maxabstain) || coopt || pickcompile) maxabstain = tp - tuples + 1; if (maxabstain) { if(!pickcompile) (void) fprintf(ofp, " = {"); for (tp = tuples; tp < tuples + maxabstain; tp++) { if(tp->exechance != 100 && tp->exechance != -100) { /* AIS: The double-oh-seven operator prevents coopt working. However, syslib contains a double-oh-seven. feh.c has checked that that isn't referenced; if it isn't, we can allow one double-oh-seven if syslib was automagically inclulded. */ if(*needsyslib) *needsyslib = 0; else coopt = 0; } if(!pickcompile) { if (tp->exechance > 0) { (void) fprintf(ofp, "0, "); tp->initabstain=0; /* AIS: -f might not be given, so we can't rely on dekludge.c doing this */ } else { (void) fprintf(ofp, "1, "); tp->exechance = -tp->exechance; tp->initabstain=1; /* AIS: As above */ /* AIS: If the line was ick_abstained, we need to swap ONCEs and AGAINs on it round, to suit the code degenerator. */ if(tp->onceagainflag == onceagain_ONCE) tp->onceagainflag = onceagain_AGAIN; else if(tp->onceagainflag == onceagain_AGAIN) tp->onceagainflag = onceagain_ONCE; } if(tp->exechance >= 101) { /* AIS: This line has a MAYBE */ tp->maybe = 1; tp->exechance /= 100; } else tp->maybe = 0; } else /* AIS: hardcoded abstain bits for PICs */ { if(!tp->abstainable) continue; if(tp->exechance > 0) (void) fprintf(ofp, "ICK_INT1 ICKABSTAINED(%d)=0;\n",(int)(tp-tuples)); else (void) fprintf(ofp, "ICK_INT1 ICKABSTAINED(%d)=1;\n",(int)(tp-tuples)); } } if(!pickcompile) (void) fprintf(ofp, "}"); } break; case 'D': /* linetypes array for abstention handling */ maxabstain = 0; for (tp = tuples; tp->type; tp++) if (tp->type == ENABLE || tp->type == DISABLE || tp->type == MANYFROM) maxabstain++; if (maxabstain || /* AIS */ gerucomesused) { int j=0; /* AIS */ /* AIS: Changed to use enablersm1 */ i = 0; for (;i < (int)(sizeof(enablersm1)/sizeof(char *));i++) (void) fprintf(ofp, "#define %s\t%d\n", enablersm1[i], i); (void) fprintf(ofp, "int linetype[] = {\n"); for (tp = tuples; tp->type; tp++) if(tp->ppnewtype) /* AIS */ (void) fprintf(ofp," %s,\n", enablers[tp->ppnewtype - GETS]); else if(tp->preproc) /* AIS */ (void) fprintf(ofp," PREPROC,\n"); else if (tp->type >= GETS && tp->type <= FROM) /* AIS: FROM added */ (void) fprintf(ofp, " %s,\n", enablers[tp->type - GETS]); else /* AIS: I didn't change this code, but relied on it when implementing just-in-case compilation; SPLATTERED and UNKNOWN (the two types of syntax error, unsalvageable and salvageable respectively) both become UNKNOWN in the linetypes array. */ (void) fprintf(ofp, " UNKNOWN,\n"); (void) fprintf(ofp, "};\n"); /* AIS: Implement the reverse of this array too (i.e. from line types to lines); this significantly speeds up up reinstate/abstain on gerunds. Joris Huizer originally suggested the optimisation in question; this implements the same algorithm in a more maintainable way. (I didn't want to have to keep five copies of the command list in sync; two is bad enough!) */ (void) fprintf(ofp, "int revlinetype[] = {\n"); for(i=0;i < (int)(sizeof(enablersm1)/sizeof(char *));i++) { (void) fprintf(ofp,"/* %s */",enablersm1[i]); for (tp = tuples; tp->type; tp++) { if((tp->ppnewtype && tp->ppnewtype-GETS == i-1) || (!tp->ppnewtype && tp->preproc && i-1 == PREPROC-GETS) || (!tp->ppnewtype && !tp->preproc && tp->type >= GETS && tp->type <= FROM && tp->type-GETS == i-1) || (!i && !tp->ppnewtype && !tp->preproc && (tp->type < GETS || tp->type > FROM))) (void) fprintf(ofp, " %ld,",(long)(tp-tuples)); } (void) fprintf(ofp,"\n"); } (void) fprintf(ofp, "};\n"); (void) fprintf(ofp, "int revlineindex[] = {\n"); for(i=0;i < (int)(sizeof(enablersm1)/sizeof(char *));i++) { (void) fprintf(ofp,"/* %s */",enablersm1[i]); (void) fprintf(ofp," %d,\n",j); for (tp = tuples; tp->type; tp++) { if((tp->ppnewtype && tp->ppnewtype-GETS == i-1) || (!tp->ppnewtype && tp->preproc && i-1 == PREPROC-GETS) || (!tp->ppnewtype && !tp->preproc && tp->type >= GETS && tp->type <= FROM && tp->type-GETS == i-1) || (!i && !tp->ppnewtype && !tp->preproc && (tp->type < GETS || tp->type > FROM))) j++; } } (void) fprintf(ofp, "/* end */ %d\n};\n",j); } break; case 'E': /* extern to intern map */ if(!pickcompile) { (void) fprintf(ofp,"int ick_Base = %d;\n",ick_Base); (void) fprintf(ofp,"int ick_Small_digits = %d;\n", ick_Small_digits); (void) fprintf(ofp,"int ick_Large_digits = %d;\n", ick_Large_digits); (void) fprintf(ofp,"unsigned int ick_Max_small = 0x%x;\n", ick_Max_small); (void) fprintf(ofp,"unsigned int ick_Max_large = 0x%x;\n", ick_Max_large); if (yukprofile || yukdebug || multithread || useickec) { /* AIS: yuk.c, multithreading require all these to exist */ if(!nonespots) nonespots = 1; if(!ntwospots) ntwospots = 1; if(!ntails) ntails = 1; if(!nhybrids) nhybrids = 1; } else if(opoverused) { /* AIS: The operand-overloading code requires onespot and twospot variables to exist. */ if(!nonespots) nonespots = 1; if(!ntwospots) ntwospots = 1; } /* AIS:I de-staticed all these so they could be accessed by yuk and cesspool, and added all the mentions of yuk and multithread. Then I changed it so the variables would be allocated dynamically, to speed up multithreading. (It's an O(1) change to the speed of ordinary programs, so I thought I could get away with it. The order is wrt the number of lines in the program. The change is O(n) wrt the number of variables, but again I hope that doesn't matter, and I won't get the entire INTERCAL community angry with me for daring to implement an extension that slows down existing programs.) */ if (variableconstants) /* AIS */ { int temp=0; (void) fprintf(ofp, "ick_type32 meshes[%d] = {",nmeshes); while(temp<nmeshes) { (void) fprintf(ofp, "%luLU, ", varextern((unsigned long)temp,MESH)); temp++; } (void) fprintf(ofp, "};\n"); } if (nonespots) { (void) fprintf(ofp, "ick_type16* ick_onespots;\n"); (void) fprintf(ofp, "ick_bool* ick_oneforget;\n"); if(yukprofile || yukdebug) { (void) fprintf(ofp, "ick_type16 oneold[%d];\n", nonespots); (void) fprintf(ofp, "signed char onewatch[%d];\n", nonespots); } if(multithread) { (void) fprintf(ofp, "int onespotcount = %d;\n", nonespots); } if(multithread || opoverused || useickec) /* AIS */ { int temp=nonespots; (void) fprintf(ofp, "ick_overop* ick_oo_onespots = 0;\n"); if(opoverused) while(temp--) (void) fprintf(ofp, "ick_type32 og1spot%d(ick_type32 t)\n{\n (void)t;\n return ick_onespots[%d];\n}\n" "void os1spot%d(ick_type32 val, void(*f)())\n{\n (void)f;\n ick_assign((void*)" "(ick_onespots+%d), ick_ONESPOT, ick_oneforget[%d], val);\n}\n",temp,temp,temp,temp,temp); } } if (ntwospots) { (void) fprintf(ofp, "ick_type32* ick_twospots;\n"); (void) fprintf(ofp, "ick_bool* ick_twoforget;\n"); if(yukprofile || yukdebug) { (void) fprintf(ofp, "ick_type32 twoold[%d];\n", ntwospots); (void) fprintf(ofp, "signed char twowatch[%d];\n", ntwospots); } if(multithread) { (void) fprintf(ofp, "int twospotcount = %d;\n", ntwospots); } if(multithread || opoverused || useickec) /* AIS */ { int temp=ntwospots; (void) fprintf(ofp, "ick_overop* ick_oo_twospots = 0;\n"); if(opoverused) while(temp--) (void) fprintf(ofp, "ick_type32 og2spot%d(ick_type32 t)\n{\n (void)t;\n return ick_twospots[%d];\n}\n" "void os2spot%d(ick_type32 val, void(*f)())\n{\n (void)f;\n ick_assign((void*)" "(ick_twospots+%d), ick_TWOSPOT, ick_twoforget[%d], val);\n}\n",temp,temp,temp,temp,temp); } } if (ntails) { (void) fprintf(ofp, "ick_array* ick_tails;\n"); (void) fprintf(ofp, "ick_bool* ick_tailforget;\n"); if(multithread) { (void) fprintf(ofp, "int tailcount = %d;\n", ntails); } } if (nhybrids) { (void) fprintf(ofp, "ick_array* ick_hybrids;\n"); (void) fprintf(ofp, "ick_bool* ick_hyforget;\n"); if(multithread) { (void) fprintf(ofp, "int hybridcount = %d;\n", nhybrids); } } if (yydebug || compile_only) { assert(oblist != NULL); for (op = oblist; op < obdex; op++) if(op->type!=MESH) /* AIS: Added this check */ (void) fprintf(ofp, " /* %s %lu -> %lu */\n", nameof(op->type, vartypes), op->extindex, op->intindex); } if (yukdebug || yukprofile) { /* AIS: drop intern to extern map into the program */ (void) fprintf(ofp, "\nyukvar yukvars[]={\n"); assert(oblist != NULL); for (op = oblist; op < obdex; op++) if(op->type!=MESH) /* AIS: Added this check */ (void) fprintf(ofp," {%s,%lu,%lu},\n", nameof(op->type, vartypes), op->extindex, op->intindex); (void) fprintf(ofp," {YUKEND,0,0}};\n"); } else if(useickec) { /* AIS: likewise, but with different identifiers */ (void) fprintf(ofp, "\nick_ec_var ick_ec_vars[]={\n"); assert(oblist != NULL); for (op = oblist; op < obdex; op++) if(op->type!=MESH) (void) fprintf(ofp," {%s,%lu,%lu},\n", nameof(op->type, vartypes), op->extindex, op->intindex); (void) fprintf(ofp," {ICK_EC_VARS_END,0,0}};\n"); } } else { /* Compiling for PIC */ /* Arrays not supported on PICs */ if(ntails || nhybrids) ick_lose(IE256, iyylineno, (const char*) NULL); /* and neither are variable constants */ if(variableconstants) ick_lose(IE256, iyylineno, (const char*) NULL); assert(oblist != NULL); for (op = oblist; op < obdex; op++) { (void) fprintf(ofp, " /* %s %lu -> %lu */\n", nameof(op->type, vartypes), op->extindex, op->intindex); (void) fprintf(ofp, "#define %s%lu %s[%lu]\n", nameof(op->type, vartypes), op->extindex, nameof(op->type, varstores), op->intindex); if(op->ignorable) (void) fprintf(ofp, "ICK_INT1 ignore%s%lu = 0;\n", nameof(op->type, varstores), op->intindex); } (void) fprintf(ofp, "#include \"pick1.h\"\n"); if(nonespots) { (void) fprintf(ofp, "ICK_INT16 ick_onespots[%d];\n" "ICK_INT16 onespotsstash[%d];\n", nonespots, nonespots); if(opoverused) /* AIS */ { int temp=nonespots; (void) fprintf(ofp,"ick_overop* ick_oo_onespots;\n"); while(temp--) (void) fprintf(ofp, "ick_type32 og1spot%d(ick_type32 t)\n{\n (void)t;\n return ick_onespots[%d];\n}\n" "void os1spot%d(ick_type32 val,void(*f)())\n{\n (void)f;\n if(!ignoreonespots%d)" " ick_onespots[%d]=val;\n}\n",temp,temp,temp,temp,temp); } } if(ntwospots) { (void) fprintf(ofp, "ICK_INT32 ick_twospots[%d];\n" "ICK_INT32 twospotsstash[%d];\n", ntwospots, ntwospots); if(opoverused) /* AIS */ { int temp=ntwospots; (void) fprintf(ofp,"ick_overop* ick_oo_twospots;\n"); while(temp--) (void) fprintf(ofp, "ick_type32 og2spot%d(ick_type32 t)\n{\n (void)t;\n return ick_twospots[%d];\n}\n" "void os2spot%d(ick_type32 val,void(*f)())\n{\n (void)f;\n if(!ignoretwospots%d)" " ick_twospots[%d]=val;\n}\n",temp,temp,temp,temp,temp); } } (void) fprintf(ofp, "#include \"pick2.h\"\n"); } break; case 'F': /* set options from command line */ if (ick_clockface) (void) fprintf(ofp, "ick_clockface(ick_TRUE);\n"); if (ick_clcsemantics) /* AIS */ (void) fprintf(ofp, "ick_setclcsemantics(ick_TRUE);\n"); break; case 'G': /* degenerated code */ for (tp = tuples, i = 0; tp->type; tp++, i++) { emit(tp, ofp); if (i == bugline) (void) fprintf(ofp, " ick_lose(IE774, ick_lineno, " "(char *)NULL);\n"); } break; case 'H': /* COMPUCOME, and dispatching for resumes */ /* AIS: Added COMPUCOME here. This line must be fully guarded to prevent a longjmp to an uninitialised buffer (it's guarded by a ick_lose() in ick-wrap.c.) Also checks for multithread; programs that mix normal and computed COME FROM need to use the same conventions for both, so even if no computed COME FROMs are used, the normal ones need this line so that COME FROMs can be handled consistently.*/ if(compucomesused || multithread) { (void) fprintf(ofp, "CCFL: ; CCF%d: longjmp(ick_cjb,1);\n", compucomecount); } break; case 'J': /* # of source file lines */ (void) fprintf(ofp, "%d", iyylineno); break; case 'K': /* AIS: yuk information (or not) */ if(yukdebug||yukprofile) { (void) fprintf(ofp, "#include \"config.h\"\n\n"); (void) fprintf(ofp, "#include \"yuk.h\"\n\n"); (void) fprintf(ofp, "char* textlines[] = {\n"); emittextlines(ofp); /* from feh.c */ (void) fprintf(ofp, "\"\"};\n\n"); (void) fprintf(ofp, "char* yukexplain[] = {\n"); for (tp = tuples; tp->type; tp++) { if (tp->type == GETS || tp->type == FORGET || tp->type == RESUME || tp->type == FROM || tp->type == COMPUCOME || tp->type == MANYFROM) { (void) fprintf(ofp, "\""); explexpr(tp->type == MANYFROM ? tp->u.node->lval : tp->type == GETS ? tp->u.node->rval : tp->u.node, ofp); (void) fprintf(ofp, "\",\n"); } else (void) fprintf(ofp, "0,"); } (void) fprintf(ofp, "0};\n\n"); (void) fprintf(ofp, "int lineofaboff[] = {\n"); for (tp = tuples; tp->type; tp++) { fprintf(ofp,"%d,",tp->ick_lineno); } (void) fprintf(ofp, "-1};\n\n"); (void) fprintf(ofp, "int yukopts = %d;\n", yukprofile+yukdebug*2); (void) fprintf(ofp, "yptimer ypexectime[%d];\n", ick_lineno); (void) fprintf(ofp, "ypcounter ypexecount[%d];\n",ick_lineno); (void) fprintf(ofp, "ypcounter ypabscount[%d];\n",ick_lineno); } break; case 'L': /* AIS: increase Emacs compatibility */ (void) fprintf(ofp, "/* -*- mode:c; compile-command:\"%s%s%s\" -*- */", #ifdef __DJGPP__ compiler," ", #else "","", #endif compilercommand); break; case 'M': /* AIS: place new features defines in program */ /* This is needed even in a non-multithread program, to let the header files know it's non-multithread */ (void) fprintf(ofp, "#define MULTITHREAD %d\n", multithread); /* Likewise, to let the header files know whether it overloads operands (I don't think this is used at the moment, though) */ (void) fprintf(ofp, "#define OPOVERUSED %d\n",opoverused); /* and whether to use the ICK_EC code */ if(useickec) (void) fprintf(ofp, "#define ICK_EC 1\n"); break; case 'N': /* allocate variables */ /* AIS:I de-staticed all these so they could be accessed by yuk and cesspool, and added all the mentions of yuk and multithread. Then I changed it so the variables would be allocated dynamically, to speed up multithreading (it's an O(1) change to the speed of ordinary programs, so I thought I could get away with it). At this point, the 'E' case must already have been done. calloc sets all the integer values to 0, as before. In the case of arrays, it will not zero pointers, but the number-of- dimensions value will become 0, which can serve as a 'deallocated' flag. */ if (nonespots) { if(!pickcompile) /* AIS */ { (void) fprintf(ofp, " ick_onespots = calloc(" "%d, sizeof *ick_onespots);\n", nonespots); (void) fprintf(ofp, " ick_oneforget = calloc(" "%d, sizeof *ick_oneforget);\n", nonespots); } if(opoverused) { int temp=nonespots; (void) fprintf(ofp, " ick_oo_onespots=malloc(%d*sizeof*ick_oo_onespots);\n",temp); while(temp--) (void) fprintf(ofp, " ick_oo_onespots[%d].get=og1spot%d;\n ick_oo_onespots[%d].set=os1spot%d;\n", temp,temp,temp,temp); } } if (ntwospots) { if(!pickcompile) { (void) fprintf(ofp, " ick_twospots = calloc(" "%d, sizeof *ick_twospots);\n", ntwospots); (void) fprintf(ofp, " ick_twoforget = calloc(" "%d, sizeof *ick_twoforget);\n", ntwospots); } if(opoverused) { int temp=ntwospots; (void) fprintf(ofp, " ick_oo_twospots=malloc(%d*sizeof*ick_oo_twospots);\n",temp); while(temp--) (void) fprintf(ofp, " ick_oo_twospots[%d].get=og2spot%d;\n ick_oo_twospots[%d].set=os2spot%d;\n", temp,temp,temp,temp); } } if (ntails&&!pickcompile) { (void) fprintf(ofp, " ick_tails = calloc(" "%d, sizeof *ick_tails);\n", ntails); (void) fprintf(ofp, " ick_tailforget = calloc(" "%d, sizeof *ick_tailforget);\n", ntails); } if (nhybrids&&!pickcompile) { (void) fprintf(ofp, " ick_hybrids = calloc(" "%d, sizeof *ick_hybrids);\n", nhybrids); (void) fprintf(ofp, " ick_hyforget = calloc(" "%d, sizeof *ick_hyforget);\n", nhybrids); } break; case 'O': /* AIS; for GERUCOME and operand overloading */ if(gerucomesused || nextfromsused) fprintf(ofp,"unsigned truelineno = 0;\n"); if(opoverused) fprintf(ofp,"%s trueval;\n", pickcompile?"ICK_INT32":"ick_type32"); break; case 'P': /* AIS: for operand overloading */ if(opoverused) emitslatproto(ofp); break; case 'Q': /* AIS: for operand overloading */ if(opoverused) emitslat(ofp); break; } } /** * This runs the C compiler, and may invoke yuk. * @param cc_command The compiler command line to use. Constructed by gen_cc_command(). * @param oldstdin The previous stdin, used for yuk. * @param yukcmdstr The command line to use for running yuk. * @param sourcefile The output filename. * @param binaryname The name of the binary. */ static void run_cc_and_maybe_debugger(/*@observer@*/ const char *cc_command, int oldstdin, /*@observer@*/ const char *yukcmdstr, /*@observer@*/ const char *sourcefile, /*@observer@*/ const char *binaryname) { #ifndef __DJGPP__ /* OK, now sic the C compiler on the results */ if (!compile_only&&!yukdebug&&!yukprofile&&!useickec) { /* AIS: buf2 now assigned elsewhere so $L works */ ICK_SYSTEM(cc_command); /* AIS: no unlink if cdebug */ if(!cdebug) (void) unlink(sourcefile); } else if(!compile_only&&!useickec) { /* AIS: run, then delete all output but yuk.out */ /* Note that the output must be deleted for copyright reasons (so as not to GPL a non-GPL file automatically) */ ICK_SYSTEM(cc_command); #ifdef HAVE_UNISTD_H (void) dup2(oldstdin,0); /* restore stdin */ #endif ICK_SYSTEM(yukcmdstr); (void) unlink(sourcefile); (void) unlink(binaryname); } #else /* we are using DJGPP */ /* OK, now sic the C compiler on the results */ if (!compile_only&&!useickec) { /* AIS: buf2 now assigned elsewhere so $L works */ /* AIS: This changes somewhat for DJGPP, due to the command-line cap. It creates a temporary file with the arguments needed to give gcc. */ FILE* rsp; /* Use current dir as temp if needed */ const char* tempfn="gcc @ickgcc.rsp"; /* Four tries are used to find a temp directory. ICKTEMP is the preferred environment variable to check; if, as expected, this isn't set, try TMPDIR (which DJGPP sets to its own temp directory, at least when running under bash), TEMP and TMP (in that order). DJGPP offers /dev/env as a method of accessing environment variables in filenames.*/ if(isenv("TMP")) tempfn="gcc @/dev/env/TMP/ickgcc.rsp"; if(isenv("TEMP")) tempfn="gcc @/dev/env/TEMP/ickgcc.rsp"; if(isenv("TMPDIR")) tempfn="gcc @/dev/env/TMPDIR/ickgcc.rsp"; if(isenv("ICKTEMP")) tempfn="gcc @/dev/env/ICKTEMP/ickgcc.rsp"; rsp=ick_debfopen(tempfn+5,"w"); fprintf(rsp,"%s\n",cc_command); fclose(rsp); ICK_SYSTEM(tempfn); remove(tempfn+5); if(yukdebug || yukprofile) { char buffer[BUFSIZ]; #ifdef HAVE_UNISTD_H dup2(oldstdin,0); /* restore stdin */ #endif /* FIXME: This looks broken (the buf2 usage and such). */ ick_snprintf_or_die(buffer, sizeof(buffer), "%s" EXEEXT,binaryname); ICK_SYSTEM(yukcmdstr); remove(sourcefile); remove(buffer); } else if(!cdebug) { remove(sourcefile); } } #endif } /** * This runs coopt.sh if -F is given and the program can be "coopted". * @param cooptsh Path to coopt.sh * @param binaryname The output binary filename. */ static void run_coopt(/*@observer@*/ /*@null@*/ /*@unused@*/ const char* cooptsh, /*@observer@*/ /*@unused@*/ const char* binaryname) { /* Note: Params are marked unused because they may not be used if sh isn't supported. */ #ifdef HAVE_PROG_SH # ifdef HAVE_SYS_INTERPRETER if(coopt) /* AIS */ { /* The constant-output optimizer is a form of post-processor. IMPORTANT NOTE: This MUST NOT be run if the input program takes any input or is affected in any way by the state of the system, as the degenerated program may be wrong. At the moment, the only INTERCAL command that takes input is WRITE IN. Double-oh-sevens screw this up, too. */ if(cooptsh) { char commandlinebuf[BUFSIZ]; (void) ick_snprintf_or_die(commandlinebuf, sizeof commandlinebuf, "sh %s %s", cooptsh, binaryname); ICK_SYSTEM(commandlinebuf); /* replaces the output executable if neccesary */ } } # endif #endif } /** * This is for -e, runs prelinking. * @param argc Exactly what you think. * @param argv Also what you think. * @param oldoptind The original optind. * @param libdir The ick library directory. * @param includedir The ick include directory. * @param path The path of the ick binary (execuding filename). * @param libbuf A string with -lfoo to add to compiler command line. * @note May (directly) call ick_lose() with IE666. IE778 and IE888. */ static void prelink(int argc, char *argv[], int oldoptind, /*@observer@*/ const char* libdir, /*@observer@*/ const char *includedir, /*@observer@*/ const char* path, /*@observer@*/ const char* libbuf) { char buffer[BUFSIZ]; FILE* cioin; FILE* cioallec; char* buf2ptr; long remspace; const char* tempfn="ickectmp.c"; int needc99=0; #if __DJGPP__ /* Look for a temp directory, as above. */ if(isenv("TMP")) tempfn="/dev/env/TMP/ickectmp.c"; if(isenv("TEMP")) tempfn="/dev/env/TEMP/ickectmp.c"; if(isenv("TMPDIR")) tempfn="/dev/env/TMPDIR/ickectmp.c"; if(isenv("ICKTEMP")) tempfn="/dev/env/ICKTEMP/ickectmp.c"; #else tempfn="/tmp/ickectmp.c"; /* always a valid temporary folder on POSIX */ #endif cioallec=ick_debfopen(tempfn,"w"); if(cioallec == NULL) ick_lose(IE888, -1, (const char*) NULL); (void) fprintf(cioallec,"void ick_doresume(unsigned short,int);\n"); (void) fprintf(cioallec,"extern int ick_global_checkmode;\n"); (void) fprintf(cioallec,"void ick_allecfuncs(void)\n{\n"); /* Here, we run the C preprocessor on the files in question, then our own preprocessor, and finally link all the files together into one executable. */ for(optind=oldoptind; optind < argc; optind++) { (void) ick_snprintf_or_die(buffer, sizeof buffer, "%s --std=c%d -E -DICK_HAVE_STDINT_H=%d " "-I%s -I%s -I%s/../include " "-x c %s.c%c%c > %s.cio", compiler, argv[optind][strlen(argv[optind])+2]=='9'?99:89, ICK_HAVE_STDINT_H+1-1, includedir, path, path, argv[optind], argv[optind][strlen(argv[optind])+2]=='9'? (needc99=1),'9':' ', argv[optind][strlen(argv[optind])+2]=='9'?'9':' ', argv[optind]); if(*(argv[optind]) && /* there is some file to compile */ (argv[optind][strlen(argv[optind])+2]=='\0' /* a .c or .i file */ ||argv[optind][strlen(argv[optind])+3]!='o')) /* not a .cio file */ ICK_SYSTEM(buffer); /* run the C preprocessor */ buf2ptr = strrchr(buffer,'>'); /* get the .cio's filename */ cioin=NULL; /* Do our preprocessing, by editing the file in place using rb+. */ if(buf2ptr != NULL && buf2ptr[1] != '\0' && buf2ptr[2] != '\0') cioin=ick_debfopen(buf2ptr+2,"rb+"); if(cioin) { int inchar=fgetc(cioin); int toparencount=0; /* The ppnums are replacements for strings in the .cio file. The choice of 65538 means that we don't clash with any line numbers in the program, but do clash with the other C-INTERCAL preprocessor (that handles WHILE); that isn't a problem because external calls are inconsistent with multithreading anyway. */ static long ppnum1=65538L*2L; static long ppnum2=65538L*2L; static long ppnum3=65538L*2L; static long ppnum6=65538L*2L; long ciopos=0L; /*@+charintliteral@*/ /* literal chars are ints */ while(inchar != EOF) { if(inchar=='I') { /* Look for the ICK_EC_PP_ string that indicates preprocessing is needed. This method of doing it works as long as the ICK_EC_PP_ string is never preceded by something which looks like part of the same string, but luckily, it never is. */ if((inchar=fgetc(cioin))!='C') continue; if((inchar=fgetc(cioin))!='K') continue; if((inchar=fgetc(cioin))!='_') continue; if((inchar=fgetc(cioin))!='E') continue; if((inchar=fgetc(cioin))!='C') continue; if((inchar=fgetc(cioin))!='_') continue; if((inchar=fgetc(cioin))!='P') continue; if((inchar=fgetc(cioin))!='P') continue; if((inchar=fgetc(cioin))!='_') continue; inchar=fgetc(cioin); toparencount=0; if(inchar=='0') { fprintf(cioallec,"#undef X\n"); fprintf(cioallec,"#define X "); while(fputc(fgetc(cioin),cioallec) != ')') toparencount++; } (void) fseek(cioin,ciopos,SEEK_SET); switch(inchar) { case '0': /* a function exists */ fprintf(cioin," "); fprintf(cioallec,"\nvoid X(void); X();\n" "if(ick_global_checkmode==5) ick_doresume(1,-1);\n"); while(toparencount--) (void) fputc(' ',cioin); break; case '1': fprintf(cioin,"%-11ld",ppnum1++/2); break; case '2': fprintf(cioin,"%-11ld",ppnum2++/2); break; case '3': fprintf(cioin,"%-11ld",ppnum3++/2); break; case '4': fprintf(cioin,"%-11d",optind); break; case '6': fprintf(cioin,"%-11ld",ppnum6++/2); break; default: ick_lose(IE778, -1, (const char*) NULL); } (void) fseek(cioin,0L,SEEK_CUR); /* synch the file */ } ciopos=ftell(cioin); inchar=fgetc(cioin); } /*@=charintliteral@*/ (void) fclose(cioin); } } fprintf(cioallec,"if(ick_global_checkmode==2)\n"); fprintf(cioallec," ick_global_checkmode=4;\n"); fprintf(cioallec,"};\n"); (void) fclose(cioallec); /* NOTE: buffer changes use around here. */ /* This command line needs some explanation, and is specific to gcc and GNU ld. The -x causes gcc to interpret the .cio files as C; the -Wl,-z,muldefs is an instruction to GNU ld, telling it to link in the first main found and ignore the others. (That way, there can be a main function in each .cio, but the .cios can be linked in any order, with the right main function foremost each time.) */ (void) ick_snprintf_or_die(buffer, sizeof buffer, "%s -L%s -L%s -L%s/../lib -O2 -o %s" EXEEXT "%s " #ifndef __DJGPP__ "-Wl,-z,muldefs " #endif "-DICK_HAVE_STDINT_H=%d -x c --std=c%d %s", compiler, libdir, path, path, argv[oldoptind], cdebug?" -g":"", ICK_HAVE_STDINT_H+1==2?1:0, needc99?99:89,tempfn); remspace = (long)(sizeof buffer - strlen(buffer) - 1); for(optind=oldoptind; optind < argc; optind++) { if(!*(argv[optind])) continue; remspace -= strlen(argv[optind]) - 5; /* 5 for <space>.cio */ if(remspace <= 0) ick_lose(IE666, -1, (const char*)NULL); strcat(buffer," "); strcat(buffer,argv[optind]); strcat(buffer,".cio"); } remspace -= strlen(libbuf); if(remspace <= 0) ick_lose(IE666, -1, (const char*)NULL); strcat(buffer,libbuf); remspace -= strlen(" -lickec"); if(remspace <= 0) ick_lose(IE666, -1, (const char*)NULL); strcat(buffer," -lickec"); ICK_SYSTEM(buffer); (void) remove(tempfn); } /*@-redef@*/ int main(int argc, char *argv[]) /*@=redef@*/ { char buf[BUFSIZ], buf2[BUFSIZ], *chp, yukcmdstr[BUFSIZ], path[BUFSIZ], libbuf[BUFSIZ]; /*@-shadow@*/ /* no it doesn't, cesspool isn't linked to perpet */ const char *includedir, *libdir, *ick_datadir; /*@=shadow@*/ /* AIS: removed getenv(), added ick_datadir */ const char *cooptsh; /* AIS */ FILE *ifp, *ofp; int /* nextcount, AIS */ bugline; ick_bool needsyslib, firstfile; int oldoptind; #ifdef HAVE_UNISTD_H int oldstdin; /* AIS: for keeping track of where stdin was */ #endif if (!(includedir = getenv("ICKINCLUDEDIR"))) includedir = ICKINCLUDEDIR; if (!(libdir = getenv("ICKLIBDIR"))) libdir = ICKLIBDIR; if (!(ick_datadir = getenv("ICKDATADIR"))) /* AIS */ ick_datadir = ICKDATADIR; /* AIS: nothing actually uses this at the moment, commenting it out for future use if (!(bindir = getenv("ICKBINDIR"))) bindir = ICKBINDIR; */ if (!(compiler = getenv("CC"))) compiler = CC; /* Parse the options. */ parse_options(argc, argv); (void) signal(SIGSEGV, abend); #ifdef SIGBUS (void) signal(SIGBUS, abend); #endif /* SIGBUS */ if (!nocompilerbug) { #ifdef USG srand48(time(NULL) + getpid()); #else srand((unsigned)time(NULL)); #endif /* UNIX */ } /* AIS: New function for enhanced file-finding */ ifp = ick_findandfopen(pickcompile?PSKELETON:SKELETON, ick_datadir, "r", argv[0]); if(!ifp) ick_lose(IE999, 1, (const char *)NULL); /* now substitute in tokens in the skeleton */ /* AIS: This doesn't actually seem to do anything, and buf is uninitialised at this point, so it's actually dangerous because it's undefined behaviour. buf[strlen(buf) - 2] = '\0'; */ /* AIS: Save the old stdin, if we can */ #ifdef HAVE_UNISTD_H oldstdin=dup(0); #endif oldoptind=optind; /* AIS */ *libbuf = '\0'; /* AIS */ /* Begin file loop */ for (firstfile = ick_TRUE; optind < argc; optind++, firstfile = ick_FALSE) { /* AIS: Read as binary to pick up Latin-1 and UTF-8 better */ if (/* AIS */ strrchr(argv[optind],'.') != NULL && freopen(argv[optind], "rb", stdin) == (FILE *)NULL && /* AIS */ strcmp(strchr(argv[optind],'.')+1,"a")) ick_lose(IE777, 1, (const char *)NULL); else { /* strip off the file extension */ if(!(chp = strrchr(argv[optind],'.'))) { if(useickec && firstfile == ick_FALSE) /* By AIS */ { /* the filename indicates a request for an expansion library, along the same lines as CLC-INTERCAL's preloads. Search for it in the usual places, then make a copy in a temp directory and substitute that on the command line. */ const char* tempfn; FILE* fromcopy; FILE* tocopy; int c2; fixexpansionlibrary: tempfn="%s.c"; (void) ick_snprintf_or_die(buf2, sizeof buf2, "%s.c", argv[optind]); fromcopy = ick_findandfopen(buf2,ick_datadir,"rb",argv[0]); if(!fromcopy) /* same error as for syslib */ ick_lose(IE127, 1, (const char*) NULL); #if __DJGPP__ /* Look for a temp directory to store a copy of the C file, the resulting .cio, .o files, etc. */ if(isenv("TMP")) tempfn="/dev/env/TMP/%s.c"; if(isenv("TEMP")) tempfn="/dev/env/TEMP/%s.c"; if(isenv("TMPDIR")) tempfn="/dev/env/TMPDIR/%s.c"; if(isenv("ICKTEMP")) tempfn="/dev/env/ICKTEMP/%s.c"; #else tempfn="/tmp/%s.c"; /* always valid on POSIX */ #endif /*@-formatconst@*/ /* all possibilities are fine */ (void) ick_snprintf_or_die(buf2, sizeof buf2, tempfn, argv[optind]); /*@=formatconst@*/ if((tocopy = fopen(buf2,"wb")) == NULL) ick_lose(IE888, 1, (const char*) NULL); for(;;) { c2=fgetc(fromcopy); if(c2==EOF) break; (void) fputc(c2,tocopy); } (void) fclose(fromcopy); (void) fclose(tocopy); /*@+onlytrans@*/ /* this is a memory leak that will need sorting out later, thus the explicit turn-warning-on */ argv[optind]=malloc(sizeof(buf2)+1); /*@=onlytrans@*/ if(!(argv[optind])) ick_lose(IE888, 1, (const char*) NULL); strcpy(argv[optind],buf2); *(strrchr(argv[optind],'.')) = '\0'; continue; } ick_lose(IE998, 1, (const char *)NULL); } *chp++ = '\0'; /* Beginning of block that figures out file type from extension. */ if(useickec && (!strcmp(chp,"c") || !strcmp(chp,"cio") || !strcmp(chp,"c99"))) /* AIS */ { if(firstfile != ick_FALSE) /* need exactly 1 INTERCAL file */ ick_lose(IE998, 1, (const char *)NULL); continue; /* don't process C or cio files further yet */ } if(useickec && !strcmp(chp,"a")) { /* AIS: request for a library. Given a filename of the form libwhatever.a, it adds -lwhatever to libbuf (that's with a preceding space). If the filename doesn't start with lib, it instead adds a space and the filename to libbuf. */ handle_archive(libbuf, sizeof libbuf, argv[optind] /* Archive name without extension. */); *argv[optind]='\0'; continue; } if(useickec && !strcmp(chp,"b98")) { handle_befunge98(libbuf, sizeof libbuf, libdir, argv[0], argv[optind] /* Filename without extension. */); /* Sort out the ecto_b98 expansion library. */ argv[optind] = "ecto_b98"; goto fixexpansionlibrary; } if(useickec && firstfile == ick_FALSE) /* AIS */ ick_lose(IE998, 1, (const char *)NULL); /* determine the file type from the extension */ /* AN: chp isn't used again after this it seems? */ find_intercal_base(chp); /* End of block that figures out file type from extension. */ /* zero out tuple and oblist storage */ treset(); politesse = 0; /* JH: default to no op-overusage and no computed come from */ opoverused = 0; compucomesused = compucomecount = 0; gerucomesused = 0; /* AIS: you forgot this one */ /* AIS: ensure that at least one variable exists, to prevent NULL pointers later on */ (void) intern(ick_ONESPOT, 1); /* mention .1 */ /* reset the lex/yacc environment */ if (!firstfile) { #ifdef NEED_YYRESTART yyrestart(stdin); #endif /* NEED_YYRESTART */ iyylineno = 1; } /* compile tuples from current input source */ (void) yyparse(); if(variableconstants) { /* AIS: Up to 4 extra meshes may be needed by feh.c. */ (void) intern(MESH, 0xFFFFFFFFLU); (void) intern(MESH, 0xFFFFLU); (void) intern(MESH, 0xAAAAAAAALU); (void) intern(MESH, 0x55555555LU); } /* * Miss Manners lives. */ if (ick_lineno > 2) { if (politesse == 0 || (ick_lineno - 1) / politesse >= 5) ick_lose(IE079, iyylineno, (const char *)NULL); else if (ick_lineno / politesse < 3) ick_lose(IE099, iyylineno, (const char *)NULL); } /* Check if we should auto add the system library. */ check_syslib(buf2, sizeof buf2, &needsyslib, argv[0], ick_datadir); /* * Now propagate type information up the expression tree. * We need to do this because the unary-logical operations * are sensitive to the type widths of their operands, so * we have to generate different code depending on the * deducible type of the operand. */ propagate_typeinfo(); codecheck(); /* check for compile-time errors */ /* AIS: And importantly, sort out line number references */ run_optimiser(); /* decide if and where to place the compiler bug */ bugline = randomise_bugline(); /* set up the generated C output file name */ (void) ick_snprintf_or_die(buf, sizeof buf, "%s.c", argv[optind]); /* Open output file. */ ofp = open_outfile(buf /* Output filename */); (void) fseek(ifp,0L,0); /* rewind skeleton file */ /* AIS: Before changing argv[0], locate coopt.sh. */ /* AN: Even though argv[0] isn't changed any more this breaks if moved out * of the per-file loop since ick_findandtestopen() returns a pointer to a * static buffer. Should be fixed. */ cooptsh = ick_findandtestopen("coopt.sh", ick_datadir, "rb", argv[0]); /* AIS: and calculate yukcmdstr. */ (void) ick_snprintf_or_die(yukcmdstr, sizeof yukcmdstr, "%s%s" EXEEXT " %s %s", strchr(argv[optind],'/')||strchr(argv[optind],'\\')? "":"./",argv[optind],ick_datadir,argv[0]); /* AIS: Remove the filename from argv[0], leaving only a directory. If this would leave it blank, change argv[0] to '.'. This is so gcc can find the includes/libraries the same way that ick_findandfreopen does. */ /* JH: use a copy of argv[0] for the path, to ensure argv[0] is * available for the next round */ strcpy(path,argv[0]); if(strchr(path,'/')) *(strrchr(path,'/')) = '\0'; else strcpy(path,"."); /* Generate the compiler command. */ gen_cc_command(buf2 /* output */, sizeof buf2, buf /* Source filename. */, includedir, path, libdir, argv[optind] /* Output binary filename. */); textlinecount=0; /* AIS: If there are no files, there's no need to free any textlines */ /* Generate code using ick-wrap.c (or pickwrap.c) */ generate_code(ifp, ofp, argv[optind] /* Source file name stem. */, &needsyslib, bugline, buf2 /* CC command. */); if(!outtostdout) (void) fclose(ofp); /* OK, now sic the C compiler on the results */ /* Also: if -y was given, run debugger */ run_cc_and_maybe_debugger(buf2, oldstdin, yukcmdstr, buf /* C file name */, argv[optind] /* Binary filename */); /* Run the constant-output optimizer (a form of post-processor). */ run_coopt(cooptsh, argv[optind]); } } /* Here ends the per-file loop. */ (void) fclose(ifp); if(!compile_only && useickec) /* AIS */ prelink(argc, argv, oldoptind, libdir, includedir, path, libbuf); /* AIS: Free malloc'd memory. */ if(textlines) { /* Marking what textlines points to as only would be the 'right' way to do this (because it is only), but I can't figure out the syntax to do it, so instead I'm supressing the warning that comes up because it isn't marked as only. */ /*@-unqualifiedtrans@*/ while(textlinecount--) free(textlines[textlinecount]); free(textlines); /*@=unqualifiedtrans@*/ } #ifdef HAVE_UNISTD_H (void) close(oldstdin); /* AIS */ #endif /* This point is the very end of the program. So it's correct for normal DO NOT FREE UNDER ANY CIRCUMSTANCES globals to be free at this point, so supressing the warning given as a result. */ /*@-globstate@*/ return 0; /*@=globstate@*/ } /* perpet.c ends here */