1 /****************************************************************************
2 
3 NAME
4    perpet.c -- main routine for C-INTERCAL compiler.
5 
6 DESCRIPTION
7    This is where all the dirty work begins and ends.
8 
9 LICENSE TERMS
10     Copyright (C) 1996 Eric S. Raymond
11 
12     This program is free software; you can redistribute it and/or modify
13     it under the terms of the GNU General Public License as published by
14     the Free Software Foundation; either version 2 of the License, or
15     (at your option) any later version.
16 
17     This program is distributed in the hope that it will be useful,
18     but WITHOUT ANY WARRANTY; without even the implied warranty of
19     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20     GNU General Public License for more details.
21 
22     You should have received a copy of the GNU General Public License
23     along with this program; if not, write to the Free Software
24     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25 
26 ****************************************************************************/
27 /*LINTLIBRARY */
28 #include "config.h" /* AIS: Generated by autoconf */
29 #include <stdio.h>
30 #include <stdlib.h>
31 #ifdef HAVE_UNISTD_H
32 # include <unistd.h>
33 #endif
34 #include <string.h>
35 #include <signal.h>
36 #include <time.h>
37 #include <assert.h>
38 #include "ick.h"
39 #include "feh.h"
40 #include "parser.h"
41 #include "sizes.h"
42 #include "ick_lose.h"
43 #include "uncommon.h"
44 
45 /* AIS: split ICKDATADIR from ICKLIBDIR */
46 #ifndef ICKINCLUDEDIR
47 # define ICKINCLUDEDIR "/usr/local/include"
48 #endif
49 #ifndef ICKSYSDIR
50 # ifdef ICKDATADIR
51 #  define ICKSYSDIR ICKDATADIR
52 # else
53 #  define ICKSYSDIR "/usr/local/share"
54 # endif
55 #endif
56 #ifndef ICKCSKELDIR
57 # ifdef ICKDATADIR
58 #  define ICKCSKELDIR ICKDATADIR
59 # else
60 #  define ICKCSKELDIR "/usr/local/share"
61 # endif
62 #endif
63 #ifndef ICKLIBDIR
64 # define ICKLIBDIR "/usr/local/lib"
65 #endif
66 #ifndef ICKBINDIR
67 # define ICKBINDIR "/usr/local/bin"
68 #endif
69 #ifndef CC
70 # define CC "gcc"
71 #endif
72 
73 #define ARGSTRING "abcdefghlmoptuvwxyCEFHOPUYX@"
74 
75 /* unused st variable quiets a GCC4 warning */
76 #define ICK_SYSTEM(x) do{int st;if(showsystem)fprintf(stderr,"%s\n",x); \
77     st=system(x);}while(0)
78 
79 #ifdef USE_YYRESTART
80 /* function supplied by lex */
81 extern void yyrestart(FILE*);
82 
83 #endif /* USE_YYRESTART */
84 
85 /* function created by yacc */
86 extern int yyparse(void);
87 
88 extern int yydebug;
89 
90 /* compilation options */
91 bool compile_only; 	/* just compile into C, don't run the linker */
92 bool ick_traditional;	/* insist on strict INTERCAL-72 conformance */
93 bool nocompilerbug;	/* disable error IE774 */
94 bool yukdebug;          /* AIS: Use the yuk debugger. */
95 bool yukprofile;        /* AIS: Use the yuk profiler. */
96 bool useprintflow;      /* AIS: Add +printflow support. */
97 extern bool ick_coreonerr;   /* AIS: Dump core on IE778. (defined in ick_lose.c) */
98 bool multithread;       /* AIS: Allow multithreading and backtracking. */
99 bool variableconstants; /* AIS: Allow anything on the left of an assignment. */
100 bool createsused;       /* AIS: Allow the use of CREATE. */
101 bool useickec;          /* AIS: Link together INTERCAL and C. */
102 static bool nosyslib;   /* AIS: Don't link syslib under any circumstances. */
103 static bool showsystem; /* AIS: Show the command lines in system() calls */
104 bool cdebug;            /* AIS: Pass -g to our C compiler, and leave C code. */
105 int optdebug;           /* AIS: Debug the optimizer. Value is 0, 1, 2, or 3. */
106 bool flowoptimize;      /* AIS: Do flow optimizations (in INTERCAL!). */
107 bool coopt;             /* AIS: The constant-output optimization. This should
108 			   mean that INTERCAL will beat any other language at
109 			   many benchmark programs (!) */
110 extern bool ick_printfopens; /* AIS: Print messages whenever attempting to open a
111 			       file: from uncommon.c */
112 extern bool ick_checkforbugs;/* AIS: Try to find possible bugs in the source code */
113 bool pickcompile;       /* AIS: Compile for PIC? */
114 /*@-exportlocal@*/      /* AIS: relevant to the lexer */
115 bool clclex;            /* AIS: 1 means use CLC-INTERCAL meanings for @, ? */
116 /*@=exportlocal@*/
117 bool ick_clcsemantics;       /* AIS: CLC semantics for I/O, abstaining GIVE UP, &c*/
118 static bool outtostdout;     /* AIS: Output on stdout rather than the output file */
119 
120 /* AIS: Autodetected compilation options */
121 int compucomecount;    /* Computed COME FROM count */
122 bool compucomesused;   /* Are computed COME FROMs used? */
123 bool gerucomesused;    /* Is COME FROM gerund used? */
124 bool nextfromsused;    /* Is NEXT FROM used? */
125 bool opoverused;       /* Is operand overloading used? */
126 /*@null@*/ node* firstslat=0;      /* The first slat expression in the program */
127 /*@null@*/ node* prevslat=0;       /* The last slat expression used so far */
128 
129 static bool dooptimize;	/* do optimizations? (controlled by -O) */
130 static bool ick_clockface;	/* set up output to do IIII for IV */
131 
132 #define SKELETON  "ick-wrap.c"
133 #define PSKELETON "pickwrap.c"
134 #define SYSLIB    "syslib"
135 
136 /* numeric base defaults, exported to other files */
137 
138 #define DEFAULT_BASE 2
139 #define DEFAULT_SMALL_DIGITS 16
140 #define DEFAULT_LARGE_DIGITS 32
141 #define DEFAULT_MAX_SMALL 0xffffL
142 #define DEFAULT_MAX_LARGE 0xffffffffL
143 
144 int ick_Base;
145 int ick_Small_digits;
146 int ick_Large_digits;
147 unsigned int ick_Max_small;
148 unsigned int ick_Max_large;
149 
150 int ick_lineno;	/* after yyparse, this is the total number of statements */
151 
152 /* currently supported numeric bases, not exported */
153 static const int maxbase = 7;
154 static const int smallsizes[8] = {0, 0, 16, 10, 8, 6, 6, 5};
155 static const unsigned int maxsmalls[8] =
156 {0, 0, 65535, 59048, 65535, 15624, 46655, 16806};
157 
158 /*@observer@*/ static const char *compiler;
159 
160 atom *oblist = NULL, *obdex;
161 int obcount = 0;
162 int nonespots, ntwospots, ntails, nhybrids;
163 int nmeshes; /* AIS */
164 
165 tuple *tuples = NULL;
166 int tuplecount = 0;
167 
168 tuple *optuple = NULL; /* AIS: Tuple being optimized */
169 
170 /*
171  * mappings from magic line ranges to system library components are
172  * declared here.
173  */
174 struct linerange_t {
175     int start, end;
176     char *libname;
177 };
178 /* Note! "syslib" must be last in this list, as other parts of perpet.c
179    care about whether syslib in particular was included. */
180 struct linerange_t lineranges[] = {
181     {5000, 5699, "floatlib"},  /* the floating-point support */
182     {1000, 1999, "syslib"},    /* the system library */
183     {0, 0, NULL},
184 };
185 
186 extern const assoc varstores[]; /* AIS: Need to know this for PIC compilation */
187 
188 #ifndef HAVE_UNISTD_H
189 /* AIS: We don't have unistd.h, so we can't use getopt. Write our own version
190    that's less general but good enough. */
191 int optind=1;
192 int optopt;
getopt(int argc,char * const * argv,const char * options)193 int getopt(int argc, char * const *argv, const char *options)
194 {
195   if(optind>argc) return EOF; /* Out of command line */
196   if(!argv[optind]) return EOF; /* Out of command line */
197   while(!strcmp(argv[optind],"-"))
198   {
199     optind++; /* Go to ick_next argument */
200     if(!argv[optind]) return EOF;
201   }
202   if(*(argv[optind])!='-') return EOF; /* this arg is not an option */
203   optopt=argv[optind][1];
204   memmove(argv[optind]+1,argv[optind]+2,strlen(argv[optind]+1));
205   if(optopt=='-') {optind++; return EOF;} /* -- means end of options */
206   if(strchr(options, optopt)) return optopt; /* valid option */
207   return '?'; /* invalid option */
208 }
209 #endif
210 
myfgetc(FILE * in)211 static int myfgetc(FILE* in)
212 {
213   char c;
214   size_t dummy;
215   dummy = fread(&c,1,1,in);
216   if(feof(in)) return EOF;
217   return (int)c;
218 }
219 
abend(int signim)220 static void abend(int signim)
221 {
222   /*@-noeffect@*/ (void) signim; /*@=noeffect@*/
223   ick_lose(IE778, iyylineno, (const char *)NULL);
224 }
print_usage(const char * prog,const char * options)225 static void print_usage(const char *prog, const char *options)
226 {
227   fprintf(stderr,"Usage: %s [-%s] <file> [<file> ...]\n",prog,options);
228   fprintf(stderr,"\t-b\t:reduce the probability of E774 to zero\n");
229   fprintf(stderr,"\t-c\t:compile INTERCAL to C, but don't compile C\n");
230   fprintf(stderr,"\t-d\t:print yacc debugging information (implies -c)\n");
231   fprintf(stderr,"\t-e\t:link together INTERCAL and C files as one program\n");
232   fprintf(stderr,"\t\t (without this option, all INTERCAL files produce\n");
233   fprintf(stderr,"\t\t separate output files; with it, the first file given\n");
234   fprintf(stderr,"\t\t must be the only INTERCAL file) (prevents -mypPf)\n");
235   fprintf(stderr,"\t-E\t:never include system libraries (prevents -P)\n");
236   fprintf(stderr,"\t-t\t:traditional mode, accept only INTERCAL-72\n");
237   fprintf(stderr,"\t-C\t:clockface output (e.g. use IIII instead of IV)\n");
238   fprintf(stderr,"\t-O\t:optimize expresssions in generated code\n");
239   /* AIS: Changed the help message for the previous line (because the
240      function of -O has changed). I wrote the next group of options. */
241   fprintf(stderr,"\t-f\t:optimize control flow in generated code "
242 	  "(prevents -yp)\n");
243 #ifdef HAVE_PROG_SH
244 # ifdef HAVE_SYS_INTERPRETER
245   fprintf(stderr,"\t-F\t:optimize everything in generated code for\n"
246 	  "\t\t speed, regardless of how slow the compiler becomes or how\n"
247 	  "\t\t large the object file becomes. Implies -fO, "
248 	  "prevents -cdeghpyH\n");
249 # else
250   fprintf(stderr,"\t-F\t:unsupported on computers without #! support\n");
251 # endif
252 #else
253   fprintf(stderr,"\t-F\t:unsupported on computers without sh or bash\n");
254 #endif
255   fprintf(stderr,"\t-h\t:print optimizer debugging information "
256 	  "(implies -cO)\n");
257   fprintf(stderr,"\t-H\t:print verbose optimizer debugging information "
258 	  "(implies -cO)\n");
259   fprintf(stderr,"\t-hH\t:print optimizer debugging information in a\n"
260 	  "\t\t different form (implies -cO)\n");
261 #ifdef HAVE_UNISTD_H
262   fprintf(stderr,"\t-y\t:run the yuk debugger on the code (prevents -fme)\n");
263   fprintf(stderr,"\t-p\t:run the yuk profiler on the code (prevents -fme)\n");
264 #else
265   fprintf(stderr,"\t-y\t:unsupported on computers without <unistd.h>\n");
266   fprintf(stderr,"\t-p\t:unsupported on computers without <unistd.h>\n");
267 #endif
268   fprintf(stderr,"\t-w\t:add support for the +printflow option\n");
269   fprintf(stderr,"\t-m\t:allow multithreading and backtracking\n"
270 	  "\t\t (prevents -ype, implies -w)\n");
271   fprintf(stderr,"\t-a\t:allow the use of CREATE (prevents -P)\n");
272   fprintf(stderr,"\t-v\t:allow anything on the left of an assignment. This "
273 	  "is required\n\t\t if you want operand overloading to change "
274 	  "meshes.\n\t\t (prevents -fFOP)\n");
275   fprintf(stderr,"\t-P\t:compile PIC-INTERCAL rather than INTERCAL\n");
276   fprintf(stderr,"\t\t (prevents -amFvxeE, implies -cfO)\n");
277   fprintf(stderr,"\t-o\t:output to stdout rather than .c (implies -c)\n");
278   fprintf(stderr,"\t-X\t:interpret ambiguous syntax as Princeton not\n"
279 	  "\t\t Atari (i.e. CLC-INTERCAL not C-INTERCAL)\n");
280   fprintf(stderr,"\t-x\t:use CLC-INTERCAL rules for I/O and abstaining\n"
281 	  "\t\t from a GIVE UP by label (prevents -P)\n");
282   fprintf(stderr,"\t-u\t:print a message whenever the compiler tries to "
283 	  "open a file\n");
284   fprintf(stderr,"\t-U\t:dump core on IE778 after printing an error\n");
285   fprintf(stderr,"\t-Y\t:display the command line used whenever an external\n"
286 	  "\t\t program is invoked\n");
287   fprintf(stderr,"\t-g\t:compile to both debuggable executable and C\n");
288   fprintf(stderr,"\t-l\t:attempt to report likely bugs "
289 	  "and nonportabilities (implies -O)\n");
290   /* AIS: End of options I added. */
291   fprintf(stderr,"\t<file>\tINTERCAL source file (use extension .i\n");
292   fprintf(stderr,"\t\tfor base 2 or .3i, etc., for base 3, etc.).\n");
293 }
294 
295 #if __DJGPP__
296 /* AIS: Determine whether an environment variable exists (this is used to
297    find a temp directory) */
isenv(char * e)298 static int isenv(char* e)
299 {
300   char* x=getenv(e);
301   return x != NULL && *x != '\0';
302 }
303 #endif
304 
305 extern int	optind;		/* set by getopt */
306 
307 /**
308  * This parses command line options.
309  * @param argc What do you think?
310  * @param argv Likewise.
311  * @note May (directly) call ick_lose() with IE111 and IE256.
312  */
parse_options(int argc,char * argv[])313 static void parse_options(int argc, char *argv[])
314 {
315   int		c;
316 
317   /* getopt is POSIX, and I provide my own version if the POSIX version
318      isn't found, so the unrecog warning is a false positive. */
319   /*@-unrecog@*/
320   while ((c = getopt(argc, argv, ARGSTRING)) != EOF)
321     /*@=unrecog@*/
322   {
323     switch (c)
324     {
325     case 'b':
326       nocompilerbug = true;
327       break;
328 
329     case 'c':
330       compile_only = true;
331       /* AIS */ coopt = false;
332       break;
333 
334     case 'o': /* AIS */
335       compile_only = true;
336       outtostdout = true;
337       coopt = false;
338       break;
339 
340     case 'd':
341 	yydebug = 1;
342 	compile_only = true;
343       /* AIS */ coopt = false;
344       break;
345 
346     case 'e': /* AIS */
347       useickec = true;
348       multithread = pickcompile = coopt = yukdebug = yukprofile = false;
349       break;
350 
351     case 'E': /* AIS */
352       nosyslib = true;
353       pickcompile = false;
354       break;
355 
356     case 'C':
357       ick_clockface = true;
358       break;
359 
360     case 't':
361       ick_traditional = true;
362       if(multithread) ick_lose(IE111, 1, (const char*) NULL); /* AIS */
363       if(pickcompile) ick_lose(IE111, 1, (const char*) NULL); /* AIS */
364       break;
365 
366     case 'O':
367       dooptimize = true;
368       variableconstants = false; /* AIS */
369       break;
370 
371     case 'f': /* By AIS */
372       flowoptimize = true;
373       yukdebug = yukprofile = false;
374       variableconstants = false;
375       break;
376 
377     case 'F': /* By AIS */
378       coopt = flowoptimize = dooptimize = true;
379       variableconstants = useickec = false;
380       yukdebug = yukprofile = outtostdout = compile_only = cdebug = false;
381       yydebug = 0;
382       if(pickcompile) ick_lose(IE256, 1, (const char*) NULL);
383       break;
384 
385     case 'h': /* By AIS */
386       optdebug|=1;
387       compile_only=dooptimize=true;
388       coopt=false;
389       break;
390 
391     case 'H': /* By AIS */
392       optdebug|=2;
393       compile_only=dooptimize=true;
394       coopt=false;
395       break;
396 
397     case 'y': /* By AIS */
398 #ifdef HAVE_UNISTD_H
399       yukdebug=true;
400       multithread=flowoptimize=coopt=useickec=false;
401 #endif
402       break;
403 
404     case 'p': /* By AIS */
405 #ifdef HAVE_UNISTD_H
406       yukprofile=true;
407       multithread=flowoptimize=coopt=useickec=false;
408 #endif
409       break;
410 
411     case 'w': /* By AIS */
412       useprintflow = true;
413       break;
414 
415     case 'm': /* By AIS */
416       multithread=true;
417       yukprofile=false;
418       yukdebug=false;
419       useickec=false;
420       if(ick_traditional) ick_lose(IE111, 1, (const char*) NULL);
421       break;
422 
423     case 'a': /* By AIS */
424       createsused=true;
425       pickcompile=false;
426       break;
427 
428     case 'v': /* By AIS */
429       variableconstants=true;
430       dooptimize=false;
431       flowoptimize=false;
432       coopt=false;
433       pickcompile=false;
434       break;
435 
436     case 'l': /* By AIS */
437       ick_checkforbugs=true;
438       dooptimize=true;
439       break;
440 
441     case 'U': /* By AIS */
442       ick_coreonerr=true;
443       break;
444 
445     case 'u': /* By AIS */
446       ick_printfopens=true;
447       break;
448 
449     case 'Y': /* By AIS */
450       showsystem=true;
451       break;
452 
453     case 'P': /* By AIS */
454       pickcompile=true;
455       multithread=coopt=variableconstants=createsused=false;
456       ick_clcsemantics=useickec=nosyslib=false;
457       compile_only=true;
458       dooptimize=flowoptimize=true; /* needed for PICs */
459       break;
460 
461     case 'X': /* By AIS */
462       clclex=true;
463       break;
464 
465     case 'x': /* By AIS */
466       ick_clcsemantics=true;
467       pickcompile=false;
468       break;
469 
470     case 'g': /* By AIS */
471       cdebug=true;
472       coopt=false;
473       break;
474 
475     case '?':
476     default:
477     case '@':
478       print_usage(argv[0], ARGSTRING);
479       exit(EXIT_FAILURE);
480       /*@-unreachable@*/ break; /*@=unreachable@*/
481     }
482   }
483 }
484 
485 
486 /**
487  * This code handles archives (for -e).
488  * @param libbuf Pointer to a buffer to which extra files to link in prelink()
489  *               will be added. Need to be initialized up to the first zero byte.
490  * @param libbuf_size Size of the buffer libbuf.
491  * @param library The cmd line argument used for the library (but without the
492  *                extension).
493  */
handle_archive(char * libbuf,size_t libbuf_size,const char * library)494 static void handle_archive(char *libbuf, size_t libbuf_size,
495                            /*@observer@*/ const char* library)
496 {
497   /* AIS: request for a library. Given a filename of the form
498      libwhatever.a, it adds  -lwhatever to libbuf (that's with
499      a preceding space). If the filename doesn't start with lib,
500      it instead adds a space and the filename to libbuf. */
501   if(library[0]=='l'&&library[1]=='i'&&
502       library[2]=='b')
503       (void) ick_snprintf_or_die(libbuf+strlen(libbuf),libbuf_size - strlen(libbuf),
504 			" -l%s",library+3);
505   else
506       (void) ick_snprintf_or_die(libbuf+strlen(libbuf),libbuf_size - strlen(libbuf),
507 			" %s.a",library);
508 }
509 
510 
511 /**
512  * This handles Befunge 98 (for -e).
513  * @param libbuf Pointer to a buffer to which extra files to link in prelink()
514  *               will be added. Need to be initialized up to the first zero byte.
515  * @param libbuf_size Size of the buffer libbuf.
516  * @param libdir The ick library directory.
517  * @param argv0 Should be argv[0], which wasn't modified.
518  * @param filename The file name  of the Befunge file (but without the extension).
519  * @note May (directly) call ick_lose() with IE888 and IE899.
520  */
handle_befunge98(char * libbuf,size_t libbuf_size,const char * libdir,const char * argv0,const char * filename)521 static void handle_befunge98(char *libbuf, size_t libbuf_size,
522                              /*@observer@*/ const char* libdir,
523                              /*@observer@*/ const char* argv0,
524                              /*@observer@*/ const char* filename)
525 {
526   /* AIS: Compile the .b98 file into a .cio so that it can be used
527      later, and include the necessary libraries to use it, or error
528      if the libraries aren't installed yet. I use a somewhat dubious
529      trick here: the .b98 file's .cio, and the necessary libraries,
530      are added in the libraries section of the command line, whereas
531      the space on the command line where the .b98 file was is used
532      for the expansion library ecto_b98. This is because ecto_b98
533      requires preprocessing/prelinking/interprocessing or whatever
534      you want to call it, whereas unlike for other .cios, the .cio
535      produced from the Befunge file doesn't.
536    */
537 
538 #define MARKERMAX 128
539 
540   FILE* of;
541   int x,y,jlb;
542   char outputfilename[BUFSIZ];
543   int markerposns[MARKERMAX][2];
544   int markercount=0;
545 
546   /* Error if libick_ecto_b98.a is missing. It might be, and not
547      just due to installation problems. */
548   if(!ick_findandtestopen("libick_ecto_b98.a",libdir,"rb",argv0))
549     ick_lose(IE899,-1,(const char *)NULL);
550 
551   /* Compile the .b98 file into a .cio. It's open on stdin right now,
552      so we just need to handle the output side of things. */
553 
554   (void) ick_snprintf_or_die(outputfilename, sizeof outputfilename, "%s.cio", filename);
555 
556   if(!((of = ick_debfopen(outputfilename,"w"))))
557     ick_lose(IE888,-1,(const char *)NULL);
558 
559   fprintf(of,"const unsigned char* ick_iffi_befungeString=\n\"");
560 
561   x=0; y=0; jlb=0;
562   for(;;)
563   {
564     int c=getchar();
565     if(c==EOF) break;
566     if(c==0xB7)
567     {
568       /* Middot (0xB7) has special handling. */
569       c='M';
570       markerposns[markercount][0]=x;
571       markerposns[markercount++][1]=y;
572     }
573     if(c=='\r') {jlb = 1; x=0; y++; c='\n';}
574     else if(c=='\n' && jlb) {jlb = 0; continue;}
575     else if(c=='\n') {x=0; y++; jlb = 0;}
576     else x++;
577     fprintf(of,"\\x%x",(unsigned int)c);
578     if(!x) fprintf(of,"\"\n\"");
579   }
580   fprintf(of,"\";\n\nint ick_iffi_markercount=%d;\n"
581 	  "long long ick_iffi_markerposns[][2]={\n",markercount);
582   if(!markercount) fprintf(of,"{0,0}\n");
583   while(markercount--) fprintf(of,"{%d,%d},\n",
584 			       markerposns[markercount][0],
585 			       markerposns[markercount][1]);
586   fprintf(of,"};\n");
587 
588   (void) fclose(of);
589 
590   /* Put the libraries and .cio file in the command line. */
591   (void) ick_snprintf_or_die(libbuf+strlen(libbuf),libbuf_size - strlen(libbuf),
592 		      " %s.cio -lick_ecto_b98 -lm -lncurses", filename);
593 }
594 
595 
596 /**
597  * This computes what type the INTERCAL source file is.
598  * It will change various globals.
599  * @param chp A pointer to the first letter of the extension of the file. Note
600  *            that it won't be changed, but can't be const char* due to being
601  *            passed as the second parameter to strtol() as well.
602  * @note May (directly) call ick_lose() with IE111, IE256 and IE998.
603  */
find_intercal_base(char * chp)604 static void find_intercal_base(char* chp)
605 {
606   /* wwp: reset the base variables to defaults, because if the  */
607   /* sourcefile has extension .i they will not be reset in the  */
608   /* following chunk of code. but i don't want to modify the    */
609   /* following chunk of code because i think it is very clever; */
610   /* grabs the base on the first pass, then validates the rest  */
611   /* of the extension on the second.                            */
612   ick_Base = DEFAULT_BASE;
613   ick_Small_digits = DEFAULT_SMALL_DIGITS;
614   ick_Large_digits = DEFAULT_LARGE_DIGITS;
615   ick_Max_small = (unsigned)DEFAULT_MAX_SMALL;
616   ick_Max_large = (unsigned)DEFAULT_MAX_LARGE;
617 
618   /* determine the file type from the extension */
619   while (strcmp(chp,"i"))
620   {
621     ick_Base = (int)strtol(chp,&chp,10);
622     if (ick_Base < 2 || ick_Base > maxbase)
623       ick_lose(IE998, 1, (const char *)NULL);
624     else if (ick_traditional && ick_Base != 2)
625       ick_lose(IE111, 1, (const char *)NULL);
626     else if (pickcompile && ick_Base != 2)
627       ick_lose(IE256, 1, (const char *)NULL); /* AIS */
628     ick_Small_digits = smallsizes[ick_Base];
629     ick_Large_digits = 2 * ick_Small_digits;
630     ick_Max_small = maxsmalls[ick_Base];
631     if (ick_Max_small == 0xffff)
632       ick_Max_large = (unsigned)0xffffffffLU;
633     else
634       ick_Max_large = (ick_Max_small + 1) * (ick_Max_small + 1) - 1;
635   }
636 }
637 
638 
639 /**
640  * This checks if we automagically need to include syslib
641  * @param buffer Output buffer that may be modified to contain the path of
642  *               syslib.i or syslib.Ni (where N is 3-7).
643  * @param size Size of buffer.
644  * @param needsyslib Pointer to the bool needsyslib declared in main().
645  * @param argv0 Should be argv[0], which wasn't modified.
646  * @param ick_sysdir The ick data directory.
647  * @note May (directly) call ick_lose() with IE127.
648  */
check_syslib(char * buffer,size_t size,bool * needsyslib,const char * argv0,const char * ick_sysdir)649 static void check_syslib(/*@partial@*/ char *buffer,
650                          size_t size,
651                          /*@out@*/ bool *needsyslib,
652                          /*@observer@*/ const char *argv0,
653                          /*@observer@*/ const char *ick_sysdir)
654 {
655     tuple *tp;
656     struct linerange_t *lp;
657 
658     if (nosyslib || pickcompile) {
659 	*needsyslib = false;
660 	return;
661     }
662 
663     /*
664      * magical inclusion of system libraries is done here
665      */
666     for (lp = lineranges; lp->start; lp++)
667     {
668 	*needsyslib = false;
669 	for (tp = tuples; tp->type; tp++) {
670 	    /*
671 	     * If some label in the specified range is defined,
672 	     * then assume the library we seek is already there, so we
673 	     * can stop searching.
674 	     */
675 	    if (tp->label >= lp->start && tp->label <= lp->end) {
676 		*needsyslib = false;
677 		goto breakout;
678 	    }
679 	    /*
680 	     * If some label in the specified range is being
681 	     * called, we might need the library.
682 	     */
683 	    if (tp->type == NEXT &&
684 		tp->u.target >= lp->start && tp->u.target <= lp->end)
685 		*needsyslib = true;
686 	}
687 	if (*needsyslib) {
688 	    if (ick_Base == 2)
689 		(void) ick_snprintf_or_die(buffer, size,
690 					   "%s.i", lp->libname);
691 	    else
692 		(void) ick_snprintf_or_die(buffer, size,
693 					   "%s%d.%di", lp->libname, ick_Base, ick_Base);
694 	    if (ick_findandfreopen(buffer, ick_sysdir, "r", argv0, stdin) == NULL)
695 		ick_lose(IE127, 1, (const char*) NULL);
696 #ifdef USE_YYRESTART
697 	    yyrestart(stdin);
698 #endif /* USE_YYRESTART */
699 	    (void) yyparse();
700 	    textlinecount=iyylineno;
701 	}
702     breakout:
703 	;
704     }
705 }
706 
707 /**
708  * This code propagates type information up the expression tree.
709  * It also does some unrelated stuff such as checking for WRITE IN and disabling
710  * coopt if that is found.
711  */
propagate_typeinfo(void)712 static void propagate_typeinfo(void)
713 {
714   tuple *tp;
715   /*
716    * Now propagate type information up the expression tree.
717    * We need to do this because the unary-logical operations
718    * are sensitive to the type widths of their operands, so
719    * we have to generate different code depending on the
720    * deducible type of the operand.
721    */
722   for (tp = tuples; tp->type; tp++)
723   {
724     if (tp->type == GETS || tp->type == RESIZE
725 	|| tp->type == WRITE_IN || tp->type == READ_OUT
726 	|| tp->type == FROM || tp->type == MANYFROM
727 	|| tp->type == FORGET || tp->type == RESUME
728 	|| tp->type == COMPUCOME || tp->type == UNKNOWN)
729       typecast(tp->type == MANYFROM ? tp->u.node->lval : tp->u.node);
730     if (tp->type == WRITE_IN) coopt = false; /* AIS: may as well do
731 					        this here */
732   }
733 }
734 
735 
736 /**
737  * This runs the optimiser.
738  */
run_optimiser(void)739 static void run_optimiser(void)
740 {
741   tuple *tp;
742   /* perform optimizations */
743   if (dooptimize)
744     for (tp = tuples; tp->type; tp++)
745     {
746       /* AIS: Allow breaching of the only specification on tuples
747 	  at this point; I've checked that tuples isn't reallocated
748 	  during the block, so this is fine. */
749       /*@-onlytrans@*/
750       optuple = tp;
751       /*@=onlytrans@*/
752       if (tp->type == GETS || tp->type == RESIZE
753 	  || tp->type == FORGET || tp->type == RESUME
754 	  || tp->type == FROM || tp->type == COMPUCOME)
755 	optimize(tp->u.node);
756       if (tp->type == MANYFROM) optimize(tp->u.node->lval);
757     } /* AIS: Added FROM and MANYFROM support. */
758 
759   /* AIS: perform flow optimizations */
760   if (flowoptimize) optimizef();
761 }
762 
763 
764 /**
765  * Generate random line number for E774.
766  * @returns A random line number, or -1 for no random bug generated.
767  */
randomise_bugline(void)768 static int randomise_bugline(void)
769 {
770   /* decide if and where to place the compiler bug */
771 #ifdef USG
772   if (!nocompilerbug && lrand48() % 10 == 0)
773     return (int)(lrand48() % ick_lineno);
774 #else
775   if (!nocompilerbug && rand() % 10 == 0)
776     return rand() % ick_lineno;
777 #endif
778   else
779     return -1;
780 }
781 
782 
783 /**
784  * This opens the outfile.
785  * @param filename The filename to open.
786  * @returns A pointer to a FILE for the filename. If the global outtostdout is
787  * set then it will return stdout.
788  * @note May (directly) call ick_lose() with IE888.
789  */
open_outfile(const char * filename)790 static /*@dependent@*/ FILE* open_outfile(/*@observer@*/ const char * filename)
791 {
792   FILE *ofp;
793   /* AIS: ofp holds fopened storage if !outtostdout, and local-copy
794      storage if outtostdout, and this is not a bug, although it
795      confuses Splint. */
796   /*@-branchstate@*/
797   if(outtostdout) ofp=stdout; /* AIS */
798   else if((ofp = ick_debfopen(filename, "w")) == (FILE *)NULL)
799     ick_lose(IE888, 1, (const char *)NULL);
800   /*@=branchstate@*/
801   return ofp;
802 }
803 
804 
805 /**
806  * This generates the CC command line.
807  * @param buffer Output buffer.
808  * @param size Size of the output buffer.
809  * @param sourcefile The name of the C file.
810  * @param includedir The ick include directory.
811  * @param path The path of the ick binary (execuding filename).
812  * @param libdir The ick library directory.
813  * @param outputfile The name of the output file.
814  */
gen_cc_command(char * buffer,size_t size,const char * sourcefile,const char * includedir,const char * path,const char * libdir,const char * outputfile)815 static void gen_cc_command(char* buffer, size_t size,
816                            /*@observer@*/ const char* sourcefile,
817                            /*@observer@*/ const char* includedir,
818                            /*@observer@*/ const char* path,
819                            /*@observer@*/ const char* libdir,
820                            /*@observer@*/ const char* outputfile)
821 {
822   (void) ick_snprintf_or_die(buffer, size,
823 			     "%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",
824 #ifdef __DJGPP__
825 			     "",
826 #else
827 			     compiler,
828 #endif
829 #ifdef HAVE_CLOCK_GETTIME /* implies -lrt is available */
830 			     sourcefile, yukdebug||yukprofile?" -lyuk -lrt ":" ",
831 #else
832 			     sourcefile, yukdebug||yukprofile?" -lyuk ":" ",
833 #endif
834 			     includedir, path, path, libdir, path, path,
835 			     cdebug?'0':coopt?'3':'2', /* AIS: If coopting, optimize as much as possible
836 							   JH: [d]on't optimise when compiling with debugger support */
837 			     outputfile, multithread?"mt":"", cdebug?" -g":"");
838   /* AIS: Possibly link in the debugger yuk and/or libickmt.a here. */
839   /* AIS: Added -g support. */
840   /* AIS: Added argv[0] (now path) to the -I, -L settings. */
841 }
842 
843 
844 /**
845  * This generates the actual C code.
846  * @param ifp This should be opened to either ick-wrap.c or pickwrap.c.
847  * @param ofp The out file.
848  * @param source_name_stem Source name stem
849  * @param needsyslib Pointer to the needsyslib bool in main().
850  * @param bugline What line number to add a random bug to.
851  * @param compilercommand The compiler command line.
852  * @note May (directly) call ick_lose() with IE256.
853  */
generate_code(FILE * ifp,FILE * ofp,const char * source_name_stem,bool * needsyslib,int bugline,const char * compilercommand)854 static void generate_code(FILE *ifp, FILE *ofp,
855                           /*@observer@*/  const char* source_name_stem,
856                           bool *needsyslib,
857                           int bugline,
858                           /*@observer@*/ const char* compilercommand)
859 {
860   int maxabstain;
861   int           c, i;
862   tuple   *tp;
863   atom    *op;
864   while ((c = myfgetc(ifp)) != EOF)
865     if (c != (int)'$')
866       (void) fputc(c, ofp);
867     else switch(myfgetc(ifp))
868 	 {
869 	 case 'A':	/* source name stem */
870 	   (void) fputs(source_name_stem, ofp);
871 	   break;
872 
873 	 case 'B':	/* # of statements */
874 	   (void) fprintf(ofp, "%d", ick_lineno);
875 	   break;
876 
877 	 case 'C':	/* initial abstentions */
878 	   /* AIS: Modified to check for coopt, pickcompile */
879 	   maxabstain = 0;
880 	   for (tp = tuples; tp->type; tp++)
881 	     if (((tp->exechance <= 0 || tp->exechance >= 101)
882 		  && tp - tuples + 1 > maxabstain)
883 		 || coopt || pickcompile)
884 	       maxabstain = tp - tuples + 1;
885 	   if (maxabstain)
886 	   {
887 	     if(!pickcompile) (void) fprintf(ofp, " = {");
888 	     for (tp = tuples; tp < tuples + maxabstain; tp++)
889 	     {
890 	       if(tp->exechance != 100 && tp->exechance != -100)
891 	       { /* AIS: The double-oh-seven operator prevents
892 		    coopt working. However, syslib contains a
893 		    double-oh-seven. feh.c has checked that that
894 		    isn't referenced; if it isn't, we can allow
895 		    one double-oh-seven if syslib was
896 		    automagically inclulded. */
897 		 if(*needsyslib) *needsyslib = false; else coopt = false;
898 	       }
899 	       if(!pickcompile)
900 	       {
901 		 if (tp->exechance > 0)
902 		 {
903 		   (void) fprintf(ofp, "0, ");
904 		   tp->initabstain=0; /* AIS: -f might not be
905 					 given, so we can't rely
906 					 on dekludge.c doing
907 					 this */
908 		 }
909 		 else {
910 		   (void) fprintf(ofp, "1, ");
911 		   tp->exechance = -tp->exechance;
912 		   tp->initabstain=true; /* AIS: As above */
913 		   /* AIS: If the line was ick_abstained, we need to
914 		      swap ONCEs and AGAINs on it round, to suit
915 		      the code degenerator. */
916 		   if(tp->onceagainflag == onceagain_ONCE)
917 		     tp->onceagainflag = onceagain_AGAIN;
918 		   else if(tp->onceagainflag == onceagain_AGAIN)
919 		     tp->onceagainflag = onceagain_ONCE;
920 		 }
921 		 if(tp->exechance >= 101)
922 		 {
923 		   /* AIS: This line has a MAYBE */
924 		   tp->maybe = true;
925 		   tp->exechance /= 100;
926 		 }
927 		 else tp->maybe = false;
928 	       }
929 	       else /* AIS: hardcoded abstain bits for PICs */
930 	       {
931 		 if(!tp->abstainable) continue;
932 		 if(tp->exechance > 0)
933 		   (void) fprintf(ofp, "ICK_INT1 ICKABSTAINED(%d)=0;\n",(int)(tp-tuples));
934 		 else
935 		   (void) fprintf(ofp, "ICK_INT1 ICKABSTAINED(%d)=1;\n",(int)(tp-tuples));
936 	       }
937 	     }
938 	     if(!pickcompile) (void) fprintf(ofp, "}");
939 	   }
940 	   break;
941 
942 	 case 'D':	/* linetypes array for abstention handling */
943 	   maxabstain = 0;
944 	   for (tp = tuples; tp->type; tp++)
945 	     if (tp->type == ENABLE || tp->type == DISABLE || tp->type == MANYFROM)
946 	       maxabstain++;
947 	   if (maxabstain || /* AIS */ gerucomesused)
948 	   {
949 	     int j=0; /* AIS */
950 	     /* AIS: Changed to use enablersm1 */
951 	     i = 0;
952 	     for (;i < (int)(sizeof(enablersm1)/sizeof(char *));i++)
953 	       (void) fprintf(ofp,
954 			      "#define %s\t%d\n",
955 			      enablersm1[i], i);
956 
957 	     (void) fprintf(ofp, "int linetype[] = {\n");
958 	     for (tp = tuples; tp->type; tp++)
959 	       if(tp->ppnewtype) /* AIS */
960 		 (void) fprintf(ofp,"    %s,\n",
961 				enablers[tp->ppnewtype - GETS]);
962 	       else if(tp->preproc) /* AIS */
963 		 (void) fprintf(ofp,"    PREPROC,\n");
964 	       else if (tp->type >= GETS && tp->type <= FROM)
965 		 /* AIS: FROM added */
966 		 (void) fprintf(ofp,
967 				"    %s,\n",
968 				enablers[tp->type - GETS]);
969 	       else
970 		 /* AIS: I didn't change this code, but relied on
971 		    it when implementing just-in-case compilation;
972 		    SPLATTERED and UNKNOWN (the two types of
973 		    syntax error, unsalvageable and salvageable
974 		    respectively) both become UNKNOWN in the
975 		    linetypes array. */
976 		 (void) fprintf(ofp, " UNKNOWN,\n");
977 	     (void) fprintf(ofp, "};\n");
978 	     /* AIS: Implement the reverse of this array too (i.e.
979 		from line types to lines); this significantly
980 		speeds up up reinstate/abstain on gerunds. Joris
981 		Huizer originally suggested the optimisation in
982 		question; this implements the same algorithm in a
983 		more maintainable way. (I didn't want to have to
984 		keep five copies of the command list in sync; two
985 		is bad enough!) */
986 	     (void) fprintf(ofp, "int revlinetype[] = {\n");
987 	     for(i=0;i < (int)(sizeof(enablersm1)/sizeof(char *));i++)
988 	     {
989 	       (void) fprintf(ofp,"/* %s */",enablersm1[i]);
990 	       for (tp = tuples; tp->type; tp++)
991 	       {
992 		 if((tp->ppnewtype && tp->ppnewtype-GETS == i-1) ||
993 		    (!tp->ppnewtype && tp->preproc &&
994 		     i-1 == PREPROC-GETS) ||
995 		    (!tp->ppnewtype && !tp->preproc &&
996 		     tp->type >= GETS && tp->type <= FROM &&
997 		     tp->type-GETS == i-1) ||
998 		    (!i && !tp->ppnewtype && !tp->preproc &&
999 		     (tp->type < GETS || tp->type > FROM)))
1000 		   (void) fprintf(ofp, " %ld,",(long)(tp-tuples));
1001 	       }
1002 	       (void) fprintf(ofp,"\n");
1003 	     }
1004 	     (void) fprintf(ofp, "};\n");
1005 	     (void) fprintf(ofp, "int revlineindex[] = {\n");
1006 	     for(i=0;i < (int)(sizeof(enablersm1)/sizeof(char *));i++)
1007 	     {
1008 	       (void) fprintf(ofp,"/* %s */",enablersm1[i]);
1009 	       (void) fprintf(ofp," %d,\n",j);
1010 	       for (tp = tuples; tp->type; tp++)
1011 	       {
1012 		 if((tp->ppnewtype && tp->ppnewtype-GETS == i-1) ||
1013 		    (!tp->ppnewtype && tp->preproc &&
1014 		     i-1 == PREPROC-GETS) ||
1015 		    (!tp->ppnewtype && !tp->preproc &&
1016 		     tp->type >= GETS && tp->type <= FROM &&
1017 		     tp->type-GETS == i-1) ||
1018 		    (!i && !tp->ppnewtype && !tp->preproc &&
1019 		     (tp->type < GETS || tp->type > FROM)))
1020 		   j++;
1021 	       }
1022 	     }
1023 	     (void) fprintf(ofp, "/* end */ %d\n};\n",j);
1024 	   }
1025 	   break;
1026 
1027 	 case 'E':	/* extern to intern map */
1028 	   if(!pickcompile)
1029 	   {
1030 	     (void) fprintf(ofp,"int ick_Base = %d;\n",ick_Base);
1031 	     (void) fprintf(ofp,"int ick_Small_digits = %d;\n",
1032 			    ick_Small_digits);
1033 	     (void) fprintf(ofp,"int ick_Large_digits = %d;\n",
1034 			    ick_Large_digits);
1035 	     (void) fprintf(ofp,"unsigned int ick_Max_small = 0x%x;\n",
1036 			    ick_Max_small);
1037 	     (void) fprintf(ofp,"unsigned int ick_Max_large = 0x%x;\n",
1038 			    ick_Max_large);
1039 	     if (yukprofile || yukdebug || multithread || useickec)
1040 	     { /* AIS: yuk.c, multithreading require all these to exist */
1041 	       if(!nonespots) nonespots = 1;
1042 	       if(!ntwospots) ntwospots = 1;
1043 	       if(!ntails) ntails = 1;
1044 	       if(!nhybrids) nhybrids = 1;
1045 	     }
1046 	     else if(opoverused)
1047 	     {
1048 	       /* AIS: The operand-overloading code requires onespot and
1049 		  twospot variables to exist. */
1050 	       if(!nonespots) nonespots = 1;
1051 	       if(!ntwospots) ntwospots = 1;
1052 	     }
1053 	     /* AIS:I de-staticed all these so they could be accessed by
1054 		yuk and cesspool, and added all the mentions of yuk and
1055 		multithread. Then I changed it so the variables would be
1056 		allocated dynamically, to speed up multithreading. (It's
1057 		an O(1) change to the speed of ordinary programs, so I
1058 		thought I could get away with it. The order is wrt the
1059 		number of lines in the program. The change is O(n) wrt
1060 		the number of variables, but again I hope that doesn't
1061 		matter, and I won't get the entire INTERCAL community
1062 		angry with me for daring to implement an extension that
1063 		slows down existing programs.) */
1064 	     if (variableconstants) /* AIS */
1065 	     {
1066 	       int temp=0;
1067 	       (void) fprintf(ofp, "ick_type32 meshes[%d] = {",nmeshes);
1068 	       while(temp<nmeshes)
1069 	       {
1070 		 (void) fprintf(ofp, "%luLU, ", varextern((unsigned long)temp,MESH));
1071 		 temp++;
1072 	       }
1073 	       (void) fprintf(ofp, "};\n");
1074 	     }
1075 
1076 	     if (nonespots)
1077 	     {
1078 	       (void) fprintf(ofp,
1079 			      "ick_type16* ick_onespots;\n");
1080 	       (void) fprintf(ofp,
1081 			      "bool* ick_oneforget;\n");
1082 	       if(yukprofile || yukdebug)
1083 	       {
1084 		 (void) fprintf(ofp,
1085 				"ick_type16 oneold[%d];\n",
1086 				nonespots);
1087 		 (void) fprintf(ofp,
1088 				"signed char onewatch[%d];\n",
1089 				nonespots);
1090 	       }
1091 	       if(multithread)
1092 	       {
1093 		 (void) fprintf(ofp,
1094 				"int onespotcount = %d;\n",
1095 				nonespots);
1096 	       }
1097 	       if(multithread || opoverused || useickec) /* AIS */
1098 	       {
1099 		 int temp=nonespots;
1100 		 (void) fprintf(ofp,
1101 				"ick_overop* ick_oo_onespots = 0;\n");
1102 		 if(opoverused)
1103 		   while(temp--)
1104 		     (void) fprintf(ofp,
1105 				    "ick_type32 og1spot%d(ick_type32 t)\n{\n  (void)t;\n  return ick_onespots[%d];\n}\n"
1106 				    "void os1spot%d(ick_type32 val, void(*f)())\n{\n  (void)f;\n  ick_assign((void*)"
1107 				    "(ick_onespots+%d), ick_ONESPOT, ick_oneforget[%d], val);\n}\n",temp,temp,temp,temp,temp);
1108 	       }
1109 	     }
1110 	     if (ntwospots)
1111 	     {
1112 	       (void) fprintf(ofp,
1113 			      "ick_type32* ick_twospots;\n");
1114 	       (void) fprintf(ofp,
1115 			      "bool* ick_twoforget;\n");
1116 	       if(yukprofile || yukdebug)
1117 	       {
1118 		 (void) fprintf(ofp,
1119 				"ick_type32 twoold[%d];\n",
1120 				ntwospots);
1121 		 (void) fprintf(ofp,
1122 				"signed char twowatch[%d];\n",
1123 				ntwospots);
1124 	       }
1125 	       if(multithread)
1126 	       {
1127 		 (void) fprintf(ofp,
1128 				"int twospotcount = %d;\n",
1129 				ntwospots);
1130 	       }
1131 	       if(multithread || opoverused || useickec) /* AIS */
1132 	       {
1133 		 int temp=ntwospots;
1134 		 (void) fprintf(ofp,
1135 				"ick_overop* ick_oo_twospots = 0;\n");
1136 		 if(opoverused)
1137 		   while(temp--)
1138 		     (void) fprintf(ofp,
1139 				    "ick_type32 og2spot%d(ick_type32 t)\n{\n  (void)t;\n  return ick_twospots[%d];\n}\n"
1140 				    "void os2spot%d(ick_type32 val, void(*f)())\n{\n  (void)f;\n  ick_assign((void*)"
1141 				    "(ick_twospots+%d), ick_TWOSPOT, ick_twoforget[%d], val);\n}\n",temp,temp,temp,temp,temp);
1142 	       }
1143 	     }
1144 	     if (ntails)
1145 	     {
1146 	       (void) fprintf(ofp,
1147 			      "ick_array* ick_tails;\n");
1148 	       (void) fprintf(ofp,
1149 			      "bool* ick_tailforget;\n");
1150 	       if(multithread)
1151 	       {
1152 		 (void) fprintf(ofp,
1153 				"int tailcount = %d;\n",
1154 				ntails);
1155 	       }
1156 	     }
1157 	     if (nhybrids)
1158 	     {
1159 	       (void) fprintf(ofp,
1160 			      "ick_array* ick_hybrids;\n");
1161 	       (void) fprintf(ofp,
1162 			      "bool* ick_hyforget;\n");
1163 	       if(multithread)
1164 	       {
1165 		 (void) fprintf(ofp,
1166 				"int hybridcount = %d;\n",
1167 				nhybrids);
1168 	       }
1169 	     }
1170 	     if (yydebug || compile_only)
1171 	     {
1172 	       assert(oblist != NULL);
1173 	       for (op = oblist; op < obdex; op++)
1174 		 if(op->type!=MESH) /* AIS: Added this check */
1175 		   (void) fprintf(ofp, " /* %s %lu -> %lu */\n",
1176 				  nameof(op->type, vartypes),
1177 				  op->extindex,
1178 				  op->intindex);
1179 	     }
1180 	     if (yukdebug || yukprofile)
1181 	     { /* AIS: drop intern to extern map into the program */
1182 	       (void) fprintf(ofp, "\nyukvar yukvars[]={\n");
1183 	       assert(oblist != NULL);
1184 	       for (op = oblist; op < obdex; op++)
1185 		 if(op->type!=MESH) /* AIS: Added this check */
1186 		   (void) fprintf(ofp,"    {%s,%lu,%lu},\n",
1187 				  nameof(op->type, vartypes),
1188 				  op->extindex,
1189 				  op->intindex);
1190 	       (void) fprintf(ofp,"    {YUKEND,0,0}};\n");
1191 	     }
1192 	     else if(useickec)
1193 	     { /* AIS: likewise, but with different identifiers */
1194 	       (void) fprintf(ofp, "\nick_ec_var ick_ec_vars[]={\n");
1195 	       assert(oblist != NULL);
1196 	       for (op = oblist; op < obdex; op++)
1197 		 if(op->type!=MESH)
1198 		   (void) fprintf(ofp,"    {%s,%lu,%lu},\n",
1199 				  nameof(op->type, vartypes),
1200 				  op->extindex,
1201 				  op->intindex);
1202 	       (void) fprintf(ofp,"    {ICK_EC_VARS_END,0,0}};\n");
1203 	     }
1204 	   }
1205 	   else
1206 	   {
1207 	     /* Compiling for PIC */
1208 	     /* Arrays not supported on PICs */
1209 	     if(ntails || nhybrids)
1210 	       ick_lose(IE256, iyylineno, (const char*) NULL);
1211 	     /* and neither are variable constants */
1212 	     if(variableconstants)
1213 	       ick_lose(IE256, iyylineno, (const char*) NULL);
1214 	     assert(oblist != NULL);
1215 	     for (op = oblist; op < obdex; op++)
1216 	     {
1217 	       (void) fprintf(ofp, " /* %s %lu -> %lu */\n",
1218 			      nameof(op->type, vartypes),
1219 			      op->extindex,
1220 			      op->intindex);
1221 	       (void) fprintf(ofp, "#define %s%lu %s[%lu]\n",
1222 			      nameof(op->type, vartypes),
1223 			      op->extindex,
1224 			      nameof(op->type, varstores),
1225 			      op->intindex);
1226 	       if(op->ignorable)
1227 		 (void) fprintf(ofp, "ICK_INT1 ignore%s%lu = 0;\n",
1228 				nameof(op->type, varstores),
1229 				op->intindex);
1230 	     }
1231 	     (void) fprintf(ofp, "#include \"pick1.h\"\n");
1232 	     if(nonespots)
1233 	     {
1234 	       (void) fprintf(ofp,
1235 			      "ICK_INT16 ick_onespots[%d];\n"
1236 			      "ICK_INT16 onespotsstash[%d];\n",
1237 			      nonespots,
1238 			      nonespots);
1239 	       if(opoverused) /* AIS */
1240 	       {
1241 		 int temp=nonespots;
1242 		 (void) fprintf(ofp,"ick_overop* ick_oo_onespots;\n");
1243 		 while(temp--)
1244 		   (void) fprintf(ofp,
1245 				  "ick_type32 og1spot%d(ick_type32 t)\n{\n  (void)t;\n  return ick_onespots[%d];\n}\n"
1246 				  "void os1spot%d(ick_type32 val,void(*f)())\n{\n  (void)f;\n  if(!ignoreonespots%d)"
1247 				  " ick_onespots[%d]=val;\n}\n",temp,temp,temp,temp,temp);
1248 	       }
1249 	     }
1250 	     if(ntwospots)
1251 	     {
1252 	       (void) fprintf(ofp,
1253 			      "ICK_INT32 ick_twospots[%d];\n"
1254 			      "ICK_INT32 twospotsstash[%d];\n",
1255 			      ntwospots,
1256 			      ntwospots);
1257 	       if(opoverused) /* AIS */
1258 	       {
1259 		 int temp=ntwospots;
1260 		 (void) fprintf(ofp,"ick_overop* ick_oo_twospots;\n");
1261 		 while(temp--)
1262 		   (void) fprintf(ofp,
1263 				  "ick_type32 og2spot%d(ick_type32 t)\n{\n  (void)t;\n  return ick_twospots[%d];\n}\n"
1264 				  "void os2spot%d(ick_type32 val,void(*f)())\n{\n  (void)f;\n  if(!ignoretwospots%d)"
1265 				  " ick_twospots[%d]=val;\n}\n",temp,temp,temp,temp,temp);
1266 	       }
1267 	     }
1268 	     (void) fprintf(ofp, "#include \"pick2.h\"\n");
1269 	   }
1270 	   break;
1271 
1272 	 case 'F':	/* set options from command line */
1273 	   if (ick_clockface)
1274 	     (void) fprintf(ofp, "ick_clockface(true);\n");
1275 	   if (ick_clcsemantics) /* AIS */
1276 	     (void) fprintf(ofp, "ick_setclcsemantics(true);\n");
1277 	   break;
1278 
1279 	 case 'G':	/* degenerated code */
1280 	   for (tp = tuples, i = 0; tp->type; tp++, i++)
1281 	   {
1282 	     emit(tp, ofp);
1283 	     if (i == bugline)
1284 	       (void) fprintf(ofp, "    ick_lose(IE774, ick_lineno, "
1285 			      "(char *)NULL);\n");
1286 	   }
1287 	   break;
1288 
1289 	 case 'H':	/* COMPUCOME, and dispatching for resumes */
1290 	   /* AIS: Added COMPUCOME here. This line must be fully guarded
1291 	      to prevent a longjmp to an uninitialised buffer (it's
1292 	      guarded by a ick_lose() in ick-wrap.c.) Also checks for
1293 	      multithread; programs that mix normal and computed COME
1294 	      FROM need to use the same conventions for both, so even
1295 	      if no computed COME FROMs are used, the normal ones need
1296 	      this line so that COME FROMs can be handled consistently.*/
1297 	   if(compucomesused || multithread)
1298 	   {
1299 	     (void) fprintf(ofp, "CCFL: ; CCF%d: longjmp(ick_cjb,1);\n",
1300 			    compucomecount);
1301 	   }
1302 	   break;
1303 
1304 	 case 'J':	/* # of source file lines */
1305 	   (void) fprintf(ofp, "%d", iyylineno);
1306 	   break;
1307 
1308 	 case 'K':       /* AIS: yuk information (or not) */
1309 	   if(yukdebug||yukprofile)
1310 	   {
1311 	     (void) fprintf(ofp, "#include \"config.h\"\n\n");
1312 	     (void) fprintf(ofp, "#include \"yuk.h\"\n\n");
1313 	     (void) fprintf(ofp, "char* textlines[] = {\n");
1314 	     emittextlines(ofp); /* from feh.c */
1315 	     (void) fprintf(ofp, "\"\"};\n\n");
1316 	     (void) fprintf(ofp, "char* yukexplain[] = {\n");
1317 	     for (tp = tuples; tp->type; tp++)
1318 	     {
1319 	       if (tp->type == GETS || tp->type == FORGET || tp->type == RESUME
1320 		   || tp->type == FROM || tp->type == COMPUCOME
1321 		   || tp->type == MANYFROM)
1322 	       {
1323 		 (void) fprintf(ofp, "\"");
1324 		 explexpr(tp->type == MANYFROM ? tp->u.node->lval :
1325 			  tp->type == GETS ? tp->u.node->rval : tp->u.node, ofp);
1326 		 (void) fprintf(ofp, "\",\n");
1327 	       }
1328 	       else (void) fprintf(ofp, "0,");
1329 	     }
1330 	     (void) fprintf(ofp, "0};\n\n");
1331 	     (void) fprintf(ofp, "int lineofaboff[] = {\n");
1332 	     for (tp = tuples; tp->type; tp++)
1333 	     {
1334 	       fprintf(ofp,"%d,",tp->ick_lineno);
1335 	     }
1336 	     (void) fprintf(ofp, "-1};\n\n");
1337 	     /*@+boolint@*/
1338 	     (void) fprintf(ofp, "int yukopts = %d;\n", yukprofile+yukdebug*2);
1339 	     /*@=boolint@*/
1340 	     (void) fprintf(ofp, "yptimer ypexectime[%d];\n", ick_lineno);
1341 	     (void) fprintf(ofp, "ypcounter ypexecount[%d];\n",ick_lineno);
1342 	     (void) fprintf(ofp, "ypcounter ypabscount[%d];\n",ick_lineno);
1343 	   }
1344 	   break;
1345 
1346 	 case 'L': /* AIS: increase Emacs compatibility */
1347 	   (void) fprintf(ofp,
1348 			  "/* -*- mode:c; compile-command:\"%s%s%s\" -*- */",
1349 #ifdef __DJGPP__
1350 			  compiler," ",
1351 #else
1352 			  "","",
1353 #endif
1354 			  compilercommand);
1355 	   break;
1356 
1357 	 case 'M': /* AIS: place new features defines in program */
1358 	   /* This is needed even in a non-multithread program, to let
1359 	      the header files know it's non-multithread */
1360 	     (void) fprintf(ofp, "#define MULTITHREAD %d\n", multithread?1:0);
1361 	   /* Likewise, to let the header files know whether it
1362 	      overloads operands (I don't think this is used at
1363 	      the moment, though) */
1364 	   (void) fprintf(ofp, "#define OPOVERUSED %d\n",opoverused?1:0);
1365 	   /* and whether to use the ICK_EC code */
1366 	   if(useickec)
1367 	     (void) fprintf(ofp, "#define ICK_EC 1\n");
1368 	   break;
1369 
1370 	 case 'N':	/* allocate variables */
1371 	   /* AIS:I de-staticed all these so they could be accessed by
1372 	      yuk and cesspool, and added all the mentions of yuk and
1373 	      multithread. Then I changed it so the variables would be
1374 	      allocated dynamically, to speed up multithreading (it's
1375 	      an O(1) change to the speed of ordinary programs, so I
1376 	      thought I could get away with it). At this point, the
1377 	      'E' case must already have been done. calloc sets all
1378 	      the integer values to 0, as before. In the case of
1379 	      arrays, it will not zero pointers, but the number-of-
1380 	      dimensions value will become 0, which can serve as a
1381 	      'deallocated' flag. */
1382 	   if (nonespots)
1383 	   {
1384 	     if(!pickcompile) /* AIS */
1385 	     {
1386 	       (void) fprintf(ofp,
1387 			      "    ick_onespots = calloc("
1388 			      "%d, sizeof *ick_onespots);\n",
1389 			      nonespots);
1390 	       (void) fprintf(ofp,
1391 			      "    ick_oneforget = calloc("
1392 			      "%d, sizeof *ick_oneforget);\n",
1393 			      nonespots);
1394 	     }
1395 	     if(opoverused)
1396 	     {
1397 	       int temp=nonespots;
1398 	       (void) fprintf(ofp,
1399 			      "    ick_oo_onespots=malloc(%d*sizeof*ick_oo_onespots);\n",temp);
1400 	       while(temp--)
1401 		 (void) fprintf(ofp,
1402 				"    ick_oo_onespots[%d].get=og1spot%d;\n    ick_oo_onespots[%d].set=os1spot%d;\n",
1403 				temp,temp,temp,temp);
1404 	     }
1405 	   }
1406 	   if (ntwospots)
1407 	   {
1408 	     if(!pickcompile)
1409 	     {
1410 	       (void) fprintf(ofp,
1411 			      "    ick_twospots = calloc("
1412 			      "%d, sizeof *ick_twospots);\n",
1413 			      ntwospots);
1414 	       (void) fprintf(ofp,
1415 			      "    ick_twoforget = calloc("
1416 			      "%d, sizeof *ick_twoforget);\n",
1417 			      ntwospots);
1418 	     }
1419 	     if(opoverused)
1420 	     {
1421 	       int temp=ntwospots;
1422 	       (void) fprintf(ofp,
1423 			      "    ick_oo_twospots=malloc(%d*sizeof*ick_oo_twospots);\n",temp);
1424 	       while(temp--)
1425 		 (void) fprintf(ofp,
1426 				"    ick_oo_twospots[%d].get=og2spot%d;\n    ick_oo_twospots[%d].set=os2spot%d;\n",
1427 				temp,temp,temp,temp);
1428 	     }
1429 	   }
1430 	   if (ntails&&!pickcompile)
1431 	   {
1432 	     (void) fprintf(ofp,
1433 			    "    ick_tails = calloc("
1434 			    "%d, sizeof *ick_tails);\n",
1435 			    ntails);
1436 	     (void) fprintf(ofp,
1437 			    "    ick_tailforget = calloc("
1438 			    "%d, sizeof *ick_tailforget);\n",
1439 			    ntails);
1440 	   }
1441 	   if (nhybrids&&!pickcompile)
1442 	   {
1443 	     (void) fprintf(ofp,
1444 			    "    ick_hybrids = calloc("
1445 			    "%d, sizeof *ick_hybrids);\n",
1446 			    nhybrids);
1447 	     (void) fprintf(ofp,
1448 			    "    ick_hyforget = calloc("
1449 			    "%d, sizeof *ick_hyforget);\n",
1450 			    nhybrids);
1451 	   }
1452 	   break;
1453 	 case 'O': /* AIS; for GERUCOME and operand overloading */
1454 	   if(gerucomesused || nextfromsused)
1455 	     fprintf(ofp,"unsigned truelineno = 0;\n");
1456 	   if(opoverused)
1457 	     fprintf(ofp,"%s trueval;\n",
1458 		     pickcompile?"ICK_INT32":"ick_type32");
1459 	   break;
1460 	 case 'P': /* AIS: for operand overloading */
1461 	   if(opoverused)
1462 	     emitslatproto(ofp);
1463 	   break;
1464 	 case 'Q': /* AIS: for operand overloading */
1465 	   if(opoverused)
1466 	     emitslat(ofp);
1467 	   break;
1468 	 }
1469 }
1470 
1471 
1472 /**
1473  * This runs the C compiler, and may invoke yuk.
1474  * @param cc_command The compiler command line to use. Constructed by gen_cc_command().
1475  * @param oldstdin The previous stdin, used for yuk.
1476  * @param yukcmdstr The command line to use for running yuk.
1477  * @param sourcefile The output filename.
1478  * @param binaryname The name of the binary.
1479  */
run_cc_and_maybe_debugger(const char * cc_command,int oldstdin,const char * yukcmdstr,const char * sourcefile,const char * binaryname)1480 static void run_cc_and_maybe_debugger(/*@observer@*/ const char *cc_command,
1481                                       int oldstdin,
1482                                       /*@observer@*/ const char *yukcmdstr,
1483                                       /*@observer@*/ const char *sourcefile,
1484                                       /*@observer@*/ const char *binaryname)
1485 {
1486 #ifndef __DJGPP__
1487   /* OK, now sic the C compiler on the results */
1488   if (!compile_only&&!yukdebug&&!yukprofile&&!useickec)
1489   {
1490     /* AIS: buf2 now assigned elsewhere so $L works */
1491     ICK_SYSTEM(cc_command);
1492     /* AIS: no unlink if cdebug */ if(!cdebug) (void) unlink(sourcefile);
1493   }
1494   else if(!compile_only&&!useickec)
1495   { /* AIS: run, then delete all output but yuk.out */
1496     /* Note that the output must be deleted for copyright
1497        reasons (so as not to GPL a non-GPL file automatically) */
1498     ICK_SYSTEM(cc_command);
1499 #ifdef HAVE_UNISTD_H
1500     (void) dup2(oldstdin,0); /* restore stdin */
1501 #endif
1502     ICK_SYSTEM(yukcmdstr);
1503     (void) unlink(sourcefile);
1504     (void) unlink(binaryname);
1505   }
1506 #else /* we are using DJGPP */
1507   /* OK, now sic the C compiler on the results */
1508   if (!compile_only&&!useickec)
1509   {
1510     /* AIS: buf2 now assigned elsewhere so $L works */
1511     /* AIS: This changes somewhat for DJGPP, due to the
1512        command-line cap. It creates a temporary file
1513        with the arguments needed to give gcc. */
1514     FILE* rsp;
1515     /* Use current dir as temp if needed */
1516     const char* tempfn="gcc @ickgcc.rsp";
1517     /* Four tries are used to find a temp directory.
1518        ICKTEMP is the preferred environment variable to check;
1519        if, as expected, this isn't set, try TMPDIR (which DJGPP
1520        sets to its own temp directory, at least when running under
1521        bash), TEMP and TMP (in that order). DJGPP offers /dev/env
1522        as a method of accessing environment variables in filenames.*/
1523     if(isenv("TMP")) tempfn="gcc @/dev/env/TMP/ickgcc.rsp";
1524     if(isenv("TEMP")) tempfn="gcc @/dev/env/TEMP/ickgcc.rsp";
1525     if(isenv("TMPDIR")) tempfn="gcc @/dev/env/TMPDIR/ickgcc.rsp";
1526     if(isenv("ICKTEMP")) tempfn="gcc @/dev/env/ICKTEMP/ickgcc.rsp";
1527     rsp=ick_debfopen(tempfn+5,"w");
1528     fprintf(rsp,"%s\n",cc_command);
1529     fclose(rsp);
1530     ICK_SYSTEM(tempfn);
1531     remove(tempfn+5);
1532     if(yukdebug || yukprofile)
1533     {
1534       char buffer[BUFSIZ];
1535 #ifdef HAVE_UNISTD_H
1536       dup2(oldstdin,0); /* restore stdin */
1537 #endif
1538 /* FIXME: This looks broken (the buf2 usage and such). */
1539       ick_snprintf_or_die(buffer, sizeof(buffer), "%s" EXEEXT,binaryname);
1540       ICK_SYSTEM(yukcmdstr);
1541       remove(sourcefile);
1542       remove(buffer);
1543     }
1544     else if(!cdebug)
1545     {
1546       remove(sourcefile);
1547     }
1548   }
1549 #endif
1550 }
1551 
1552 
1553 /**
1554  * This runs coopt.sh if -F is given and the program can be "coopted".
1555  * @param cooptsh Path to coopt.sh
1556  * @param binaryname The output binary filename.
1557  */
run_coopt(const char * cooptsh,const char * binaryname)1558 static void run_coopt(/*@observer@*/ /*@null@*/ /*@unused@*/ const char* cooptsh,
1559                       /*@observer@*/ /*@unused@*/ const char* binaryname)
1560 {
1561   /* Note: Params are marked unused because they may not be used if sh isn't supported. */
1562   /* Assume that sh exists if #! does; sh is needed to run autoconf, so this would
1563      otherwise have to be set by hand anyway. */
1564 #ifdef HAVE_SYS_INTERPRETER
1565   if(coopt) /* AIS */
1566   {
1567     /* The constant-output optimizer is a form of post-processor.
1568        IMPORTANT NOTE: This MUST NOT be run if the input program
1569        takes any input or is affected in any way by the state of
1570        the system, as the degenerated program may be wrong. At the
1571        moment, the only INTERCAL command that takes input is
1572        WRITE IN. Double-oh-sevens screw this up, too. */
1573     if(cooptsh)
1574     {
1575       char commandlinebuf[BUFSIZ];
1576       (void) ick_snprintf_or_die(commandlinebuf, sizeof commandlinebuf,
1577 	                         "sh %s %s", cooptsh, binaryname);
1578       ICK_SYSTEM(commandlinebuf); /* replaces the output executable if
1579 	                             neccesary */
1580     }
1581   }
1582 #endif
1583 }
1584 
1585 
1586 /**
1587  * This is for -e, runs prelinking.
1588  * @param argc Exactly what you think.
1589  * @param argv Also what you think.
1590  * @param oldoptind The original optind.
1591  * @param libdir The ick library directory.
1592  * @param includedir The ick include directory.
1593  * @param path The path of the ick binary (execuding filename).
1594  * @param libbuf A string with -lfoo to add to compiler command line.
1595  * @note May (directly) call ick_lose() with IE666. IE778 and IE888.
1596  */
prelink(int argc,char * argv[],int oldoptind,const char * libdir,const char * includedir,const char * path,const char * libbuf)1597 static void prelink(int argc, char *argv[], int oldoptind,
1598                     /*@observer@*/ const char* libdir,
1599                     /*@observer@*/ const char *includedir,
1600                     /*@observer@*/ const char* path,
1601                     /*@observer@*/ const char* libbuf)
1602 {
1603   char buffer[BUFSIZ];
1604   FILE* cioin;
1605   FILE* cioallec;
1606   char* buf2ptr;
1607   long remspace;
1608   const char* tempfn="ickectmp.c";
1609   int needc=89;
1610 #if __DJGPP__
1611   /* Look for a temp directory, as above. */
1612   if(isenv("TMP")) tempfn="/dev/env/TMP/ickectmp.c";
1613   if(isenv("TEMP")) tempfn="/dev/env/TEMP/ickectmp.c";
1614   if(isenv("TMPDIR")) tempfn="/dev/env/TMPDIR/ickectmp.c";
1615   if(isenv("ICKTEMP")) tempfn="/dev/env/ICKTEMP/ickectmp.c";
1616 #else
1617   tempfn="/tmp/ickectmp.c"; /* always a valid temporary folder on POSIX */
1618 #endif
1619   cioallec=ick_debfopen(tempfn,"w");
1620   if(cioallec == NULL)
1621     ick_lose(IE888, -1, (const char*) NULL);
1622   (void) fprintf(cioallec,"void ick_doresume(unsigned short,int);\n");
1623   (void) fprintf(cioallec,"extern int ick_global_checkmode;\n");
1624   (void) fprintf(cioallec,"void ick_allecfuncs(void)\n{\n");
1625 
1626   /* Here, we run the C preprocessor on the files in question, then our
1627      own preprocessor, and finally link all the files together into one
1628      executable. */
1629   for(optind=oldoptind; optind < argc; optind++)
1630   {
1631     int thisneedc = argv[optind][strlen(argv[optind])+2]=='9'?99:
1632       argv[optind][strlen(argv[optind])+2]=='1'?11:89;
1633     if(needc==89 || thisneedc==11)
1634       needc=thisneedc;
1635     (void) ick_snprintf_or_die(buffer, sizeof buffer,
1636 			       "%s --std=c%d -E -DICK_HAVE_STDINT_H=%d "
1637 			       "-I%s -I%s -I%s/../include "
1638 			       "-x c %s.c%c%c > %s.cio",
1639 			       compiler, thisneedc,
1640 			       ICK_HAVE_STDINT_H+1-1,
1641 			       includedir, path, path, argv[optind],
1642                                thisneedc==99?'9':thisneedc==11?'1':' ',
1643                                thisneedc==99?'9':thisneedc==11?'1':' ',
1644 			       argv[optind]);
1645     if(*(argv[optind]) && /* there is some file to compile */
1646        (argv[optind][strlen(argv[optind])+2]=='\0' /* a .c or .i file */
1647         ||argv[optind][strlen(argv[optind])+3]!='o')) /* not a .cio file */
1648       ICK_SYSTEM(buffer); /* run the C preprocessor */
1649     buf2ptr = strrchr(buffer,'>'); /* get the .cio's filename */
1650     cioin=NULL;
1651     /* Do our preprocessing, by editing the file in place using rb+. */
1652     if(buf2ptr != NULL && buf2ptr[1] != '\0' && buf2ptr[2] != '\0')
1653       cioin=ick_debfopen(buf2ptr+2,"rb+");
1654     if(cioin)
1655     {
1656       int inchar=fgetc(cioin);
1657       int toparencount=0;
1658       /* The ppnums are replacements for strings in the .cio file.
1659 	 The choice of 65538 means that we don't clash with any
1660 	 line numbers in the program, but do clash with the other
1661 	 C-INTERCAL preprocessor (that handles WHILE); that isn't a
1662 	 problem because external calls are inconsistent with
1663 	 multithreading anyway. */
1664       static long ppnum1=65538L*2L;
1665       static long ppnum2=65538L*2L;
1666       static long ppnum3=65538L*2L;
1667       static long ppnum6=65538L*2L;
1668       long ciopos=0L;
1669       /*@+charintliteral@*/ /* literal chars are ints */
1670       while(inchar != EOF)
1671       {
1672 	if(inchar=='I')
1673 	{
1674 	  /* Look for the ICK_EC_PP_ string that indicates preprocessing
1675 	     is needed. This method of doing it works as long as the
1676 	     ICK_EC_PP_ string is never preceded by something which looks
1677 	     like part of the same string, but luckily, it never is. */
1678 	  if((inchar=fgetc(cioin))!='C') continue;
1679 	  if((inchar=fgetc(cioin))!='K') continue;
1680 	  if((inchar=fgetc(cioin))!='_') continue;
1681 	  if((inchar=fgetc(cioin))!='E') continue;
1682 	  if((inchar=fgetc(cioin))!='C') continue;
1683 	  if((inchar=fgetc(cioin))!='_') continue;
1684 	  if((inchar=fgetc(cioin))!='P') continue;
1685 	  if((inchar=fgetc(cioin))!='P') continue;
1686 	  if((inchar=fgetc(cioin))!='_') continue;
1687 	  inchar=fgetc(cioin);
1688 	  toparencount=0;
1689 	  if(inchar=='0')
1690 	  {
1691 	    fprintf(cioallec,"#undef X\n");
1692 	    fprintf(cioallec,"#define X ");
1693 	    while(fputc(fgetc(cioin),cioallec) != ')') toparencount++;
1694 	  }
1695 	  (void) fseek(cioin,ciopos,SEEK_SET);
1696 	  switch(inchar)
1697 	  {
1698 	  case '0': /* a function exists */
1699 	    fprintf(cioin,"            ");
1700 	    fprintf(cioallec,"\nvoid X(void); X();\n"
1701 		    "if(ick_global_checkmode==5) ick_doresume(1,-1);\n");
1702 	    while(toparencount--) (void) fputc(' ',cioin);
1703 	    break;
1704 	  case '1':
1705 	    fprintf(cioin,"%-11ld",ppnum1++/2);
1706 	    break;
1707 	  case '2':
1708 	    fprintf(cioin,"%-11ld",ppnum2++/2);
1709 	    break;
1710 	  case '3':
1711 	    fprintf(cioin,"%-11ld",ppnum3++/2);
1712 	    break;
1713 	  case '4':
1714 	    fprintf(cioin,"%-11d",optind);
1715 	    break;
1716 	  case '6':
1717 	    fprintf(cioin,"%-11ld",ppnum6++/2);
1718 	    break;
1719 	  default:
1720 	    ick_lose(IE778, -1, (const char*) NULL);
1721 	  }
1722 	  (void) fseek(cioin,0L,SEEK_CUR); /* synch the file */
1723 	}
1724 	ciopos=ftell(cioin);
1725 	inchar=fgetc(cioin);
1726       }
1727       /*@=charintliteral@*/
1728       (void) fclose(cioin);
1729     }
1730   }
1731   fprintf(cioallec,"if(ick_global_checkmode==2)\n");
1732   fprintf(cioallec,"  ick_global_checkmode=4;\n");
1733   fprintf(cioallec,"};\n");
1734   (void) fclose(cioallec);
1735 
1736   /* NOTE: buffer changes use around here. */
1737 
1738   /* This command line needs some explanation, and is specific to gcc and
1739      GNU ld. The -x causes gcc to interpret the .cio files as C; the
1740      -Wl,-z,muldefs is an instruction to GNU ld, telling it to link in the
1741      first main found and ignore the others.  (That way, there can be a
1742      main function in each .cio, but the .cios can be linked in any order,
1743      with the right main function foremost each time.)
1744    */
1745   (void) ick_snprintf_or_die(buffer, sizeof buffer,
1746 			     "%s -L%s -L%s -L%s/../lib -O2 -o %s" EXEEXT "%s "
1747 #ifndef __DJGPP__
1748 			     "-Wl,-z,muldefs "
1749 #endif
1750 			     "-DICK_HAVE_STDINT_H=%d -x c --std=c%d %s", compiler, libdir,
1751 			     path, path, argv[oldoptind], cdebug?" -g":"", ICK_HAVE_STDINT_H+1==2?1:0,
1752 			     needc,tempfn);
1753   remspace = (long)(sizeof buffer - strlen(buffer) - 1);
1754   for(optind=oldoptind; optind < argc; optind++)
1755   {
1756     if(!*(argv[optind])) continue;
1757     remspace -= strlen(argv[optind]) - 5; /* 5 for <space>.cio */
1758     if(remspace <= 0)
1759       ick_lose(IE666, -1, (const char*)NULL);
1760     strcat(buffer," ");
1761     strcat(buffer,argv[optind]);
1762     strcat(buffer,".cio");
1763   }
1764   remspace -= strlen(libbuf);
1765   if(remspace <= 0)
1766     ick_lose(IE666, -1, (const char*)NULL);
1767   strcat(buffer,libbuf);
1768   remspace -= strlen(" -lickec");
1769   if(remspace <= 0)
1770     ick_lose(IE666, -1, (const char*)NULL);
1771   strcat(buffer," -lickec");
1772   ICK_SYSTEM(buffer);
1773   (void) remove(tempfn);
1774 }
1775 
1776 
1777 /*@-redef@*/
main(int argc,char * argv[])1778 int main(int argc, char *argv[])
1779 /*@=redef@*/
1780 {
1781   char	buf[BUFSIZ], buf2[BUFSIZ], *chp, yukcmdstr[BUFSIZ], path[BUFSIZ],
1782     libbuf[BUFSIZ];
1783   /*@-shadow@*/ /* no it doesn't, cesspool isn't linked to perpet */
1784   const char	*includedir, *libdir, *ick_sysdir, *ick_cskeldir;
1785   /*@=shadow@*/
1786   /* AIS: removed getenv(), added ick_sysdir */
1787   const char        *cooptsh; /* AIS */
1788   FILE	*ifp, *ofp;
1789   int		/* nextcount, AIS */ bugline;
1790   bool        needsyslib, firstfile;
1791   int         oldoptind;
1792 #ifdef HAVE_UNISTD_H
1793   int         oldstdin; /* AIS: for keeping track of where stdin was */
1794 #endif
1795   if (!(includedir = getenv("ICKINCLUDEDIR")))
1796     includedir = ICKINCLUDEDIR;
1797   if (!(libdir = getenv("ICKLIBDIR")))
1798     libdir = ICKLIBDIR;
1799   if (!(ick_sysdir = getenv("ICKSYSDIR")))
1800     ick_sysdir = ICKSYSDIR;
1801   if (!(ick_cskeldir = getenv("ICKCSKELDIR")))
1802     ick_cskeldir = ICKCSKELDIR;
1803 /*
1804   AIS: nothing actually uses this at the moment,
1805   commenting it out for future use
1806 
1807   if (!(bindir = getenv("ICKBINDIR")))
1808   bindir = ICKBINDIR;
1809 */
1810   if (!(compiler = getenv("CC")))
1811     compiler = CC;
1812 
1813   /* Parse the options. */
1814   parse_options(argc, argv);
1815 
1816   (void) signal(SIGSEGV, abend);
1817 #ifdef SIGBUS
1818   (void) signal(SIGBUS, abend);
1819 #endif /* SIGBUS */
1820 
1821   if (!nocompilerbug) {
1822 #ifdef USG
1823     srand48(time(NULL) + getpid());
1824 #else
1825     srand((unsigned)time(NULL));
1826 #endif /* UNIX */
1827   }
1828 
1829   /* AIS: New function for enhanced file-finding */
1830   ifp = ick_findandfopen(pickcompile?PSKELETON:SKELETON,
1831 			 ick_cskeldir, "r", argv[0]);
1832   if(!ifp) ick_lose(IE999, 1, (const char *)NULL);
1833 
1834   /* now substitute in tokens in the skeleton */
1835 
1836   /* AIS: This doesn't actually seem to do anything, and buf is
1837      uninitialised at this point, so it's actually dangerous
1838      because it's undefined behaviour.
1839      buf[strlen(buf) - 2] = '\0'; */
1840 
1841   /* AIS: Save the old stdin, if we can */
1842 #ifdef HAVE_UNISTD_H
1843   oldstdin=dup(0);
1844 #endif
1845 
1846   oldoptind=optind; /* AIS */
1847   *libbuf = '\0'; /* AIS */
1848   /* Begin file loop */
1849   for (firstfile = true; optind < argc; optind++, firstfile = false)
1850   {
1851     /* AIS: Read as binary to pick up Latin-1 and UTF-8 better */
1852     if (/* AIS */ strrchr(argv[optind],'.') != NULL &&
1853 	freopen(argv[optind], "rb", stdin) == (FILE *)NULL &&
1854 	/* AIS */ strcmp(strchr(argv[optind],'.')+1,"a"))
1855 	ick_lose(IE777, 1, (const char *)NULL);
1856     else
1857     {
1858       /* strip off the file extension */
1859       if(!(chp = strrchr(argv[optind],'.')))
1860       {
1861 	if(useickec && firstfile == false) /* By AIS */
1862 	{
1863 	  /* the filename indicates a request for an expansion library,
1864 	     along the same lines as CLC-INTERCAL's preloads. Search for
1865 	     it in the usual places, then make a copy in a temp directory
1866 	     and substitute that on the command line. */
1867 	  const char* tempfn;
1868 	  FILE* fromcopy;
1869 	  FILE* tocopy;
1870 	  int c2;
1871 	fixexpansionlibrary:
1872 	  tempfn="%s.c";
1873 	  (void) ick_snprintf_or_die(buf2, sizeof buf2, "%s.c", argv[optind]);
1874 	  fromcopy = ick_findandfopen(buf2,ick_cskeldir,"rb",argv[0]);
1875 	  if(!fromcopy) /* same error as for syslib */
1876 	    ick_lose(IE127, 1, (const char*) NULL);
1877 #if __DJGPP__
1878 	  /* Look for a temp directory to store a copy of the C file,
1879 	     the resulting .cio, .o files, etc. */
1880 	  if(isenv("TMP")) tempfn="/dev/env/TMP/%s.c";
1881 	  if(isenv("TEMP")) tempfn="/dev/env/TEMP/%s.c";
1882 	  if(isenv("TMPDIR")) tempfn="/dev/env/TMPDIR/%s.c";
1883 	  if(isenv("ICKTEMP")) tempfn="/dev/env/ICKTEMP/%s.c";
1884 #else
1885 	  tempfn="/tmp/%s.c"; /* always valid on POSIX */
1886 #endif
1887 	  /*@-formatconst@*/ /* all possibilities are fine */
1888 	  (void) ick_snprintf_or_die(buf2, sizeof buf2, tempfn, argv[optind]);
1889 	  /*@=formatconst@*/
1890 	  if((tocopy = fopen(buf2,"wb")) == NULL)
1891 	    ick_lose(IE888, 1, (const char*) NULL);
1892 
1893 	  for(;;)
1894 	  {
1895 	    c2=fgetc(fromcopy);
1896 	    if(c2==EOF) break;
1897 	    (void) fputc(c2,tocopy);
1898 	  }
1899 	  (void) fclose(fromcopy); (void) fclose(tocopy);
1900 	  /*@+onlytrans@*/
1901 	  /* this is a memory leak that will need sorting out later,
1902 	     thus the explicit turn-warning-on */
1903 	  argv[optind]=malloc(sizeof(buf2)+1);
1904 	  /*@=onlytrans@*/
1905 	  if(!(argv[optind]))
1906 	    ick_lose(IE888, 1, (const char*) NULL);
1907 	  strcpy(argv[optind],buf2);
1908 	  *(strrchr(argv[optind],'.')) = '\0';
1909 	  continue;
1910 	}
1911 
1912 	ick_lose(IE998, 1, (const char *)NULL);
1913       }
1914       *chp++ = '\0';
1915 
1916       /* Beginning of block that figures out file type from extension. */
1917       if(useickec && (!strcmp(chp,"c") || !strcmp(chp,"cio") ||
1918 		      !strcmp(chp,"c99") || !strcmp(chp,"c11"))) /* AIS */
1919       {
1920 	if(firstfile != false) /* need exactly 1 INTERCAL file */
1921 	  ick_lose(IE998, 1, (const char *)NULL);
1922 	continue; /* don't process C or cio files further yet */
1923       }
1924 
1925       if(useickec && !strcmp(chp,"a"))
1926       {
1927 	/* AIS: request for a library. Given a filename of the form
1928 	   libwhatever.a, it adds  -lwhatever to libbuf (that's with
1929 	   a preceding space). If the filename doesn't start with lib,
1930 	   it instead adds a space and the filename to libbuf. */
1931 	handle_archive(libbuf, sizeof libbuf,
1932 	               argv[optind] /* Archive name without extension. */);
1933 	*argv[optind]='\0';
1934 	continue;
1935       }
1936 
1937       if(useickec && !strcmp(chp,"b98"))
1938       {
1939 	handle_befunge98(libbuf, sizeof libbuf, libdir, argv[0],
1940 	                 argv[optind] /* Filename without extension. */);
1941 	/* Sort out the ecto_b98 expansion library. */
1942 	argv[optind] = "ecto_b98";
1943 	goto fixexpansionlibrary;
1944       }
1945 
1946       if(useickec && firstfile == false) /* AIS */
1947 	ick_lose(IE998, 1, (const char *)NULL);
1948 
1949       /* determine the file type from the extension */
1950       /* AN: chp isn't used again after this it seems? */
1951       find_intercal_base(chp);
1952 
1953       /* End of block that figures out file type from extension. */
1954 
1955       /* zero out tuple and oblist storage */
1956       treset();
1957       politesse = 0;
1958       /* JH: default to no op-overusage and no computed come from */
1959       opoverused = false;
1960       compucomesused = false;
1961       compucomecount = 0;
1962       gerucomesused = false; /* AIS: you forgot this one */
1963       /* AIS: ensure that at least one variable exists, to prevent
1964 	 NULL pointers later on */
1965       (void) intern(ick_ONESPOT, 1); /* mention .1 */
1966 
1967       /* reset the lex/yacc environment */
1968       if (!firstfile)
1969       {
1970 #ifdef NEED_YYRESTART
1971 	yyrestart(stdin);
1972 #endif /* NEED_YYRESTART */
1973 	iyylineno = 1;
1974       }
1975 
1976       /* compile tuples from current input source */
1977       (void) yyparse();
1978 
1979       if(variableconstants)
1980       {
1981 	/* AIS: Up to 4 extra meshes may be needed by feh.c. */
1982 	(void) intern(MESH, 0xFFFFFFFFLU);
1983 	(void) intern(MESH, 0xFFFFLU);
1984 	(void) intern(MESH, 0xAAAAAAAALU);
1985 	(void) intern(MESH, 0x55555555LU);
1986       }
1987 
1988 
1989       /*
1990        * Miss Manners lives.
1991        */
1992       if (ick_lineno > 2)
1993       {
1994 	if (politesse == 0 || (ick_lineno - 1) / politesse >= 5)
1995 	  ick_lose(IE079, iyylineno, (const char *)NULL);
1996 	else if (ick_lineno / politesse < 3)
1997 	  ick_lose(IE099, iyylineno, (const char *)NULL);
1998       }
1999 
2000       /* Check if we should auto add the system library. */
2001       check_syslib(buf2, sizeof buf2, &needsyslib, argv[0], ick_sysdir);
2002 
2003       /*
2004        * Now propagate type information up the expression tree.
2005        * We need to do this because the unary-logical operations
2006        * are sensitive to the type widths of their operands, so
2007        * we have to generate different code depending on the
2008        * deducible type of the operand.
2009        */
2010       propagate_typeinfo();
2011 
2012       codecheck();	/* check for compile-time errors */
2013       /* AIS: And importantly, sort out line number references */
2014       run_optimiser();
2015 
2016       /* decide if and where to place the compiler bug */
2017       bugline = randomise_bugline();
2018 
2019       /* set up the generated C output file name */
2020       (void) ick_snprintf_or_die(buf, sizeof buf, "%s.c", argv[optind]);
2021       /* Open output file. */
2022       ofp = open_outfile(buf /* Output filename */);
2023 
2024       (void) fseek(ifp,0L,0);	/* rewind skeleton file */
2025 
2026       /* AIS: Before changing argv[0], locate coopt.sh. */
2027       /* AN: Even though argv[0] isn't changed any more this breaks if moved out
2028        * of the per-file loop since ick_findandtestopen() returns a pointer to a
2029        * static buffer. Should be fixed.
2030        */
2031       cooptsh = ick_findandtestopen("coopt.sh", ick_sysdir, "rb", argv[0]);
2032       /* AIS: and calculate yukcmdstr. */
2033       (void) ick_snprintf_or_die(yukcmdstr, sizeof yukcmdstr, "%s%s" EXEEXT " %s %s",
2034 				 strchr(argv[optind],'/')||strchr(argv[optind],'\\')?
2035 				 "":"./",argv[optind],ick_sysdir,argv[0]);
2036 
2037       /* AIS: Remove the filename from argv[0], leaving only a directory.
2038 	 If this would leave it blank, change argv[0] to '.'.
2039 	 This is so gcc can find the includes/libraries the same way that
2040 	 ick_findandfreopen does. */
2041       /* JH: use a copy of argv[0] for the path, to ensure argv[0] is
2042        * available for the next round
2043        */
2044       strcpy(path,argv[0]);
2045       if(strchr(path,'/')) *(strrchr(path,'/')) = '\0';
2046       else strcpy(path,".");
2047 
2048       /* Generate the compiler command. */
2049       gen_cc_command(buf2 /* output */, sizeof buf2, buf /* Source filename. */,
2050 		     includedir, path, libdir, argv[optind] /* Output binary filename. */);
2051 
2052       textlinecount=0; /* AIS: If there are no files, there's
2053 			  no need to free any textlines */
2054       /* Generate code using ick-wrap.c (or pickwrap.c) */
2055       generate_code(ifp, ofp, argv[optind] /* Source file name stem. */,
2056                     &needsyslib, bugline, buf2 /* CC command. */);
2057 
2058       if(!outtostdout) (void) fclose(ofp);
2059 
2060       /* OK, now sic the C compiler on the results */
2061       /* Also: if -y was given, run debugger */
2062       run_cc_and_maybe_debugger(buf2, oldstdin, yukcmdstr, buf /* C file name */,
2063                                 argv[optind] /* Binary filename */);
2064 
2065       /* Run the constant-output optimizer (a form of post-processor). */
2066       run_coopt(cooptsh, argv[optind]);
2067 
2068     }
2069   }
2070   /* Here ends the per-file loop. */
2071   (void) fclose(ifp);
2072 
2073   if(!compile_only && useickec) /* AIS */
2074     prelink(argc, argv, oldoptind, libdir, includedir, path, libbuf);
2075 
2076   /* AIS: Free malloc'd memory. */
2077   if(textlines)
2078   {
2079     /* Marking what textlines points to as only would be the 'right'
2080        way to do this (because it is only), but I can't figure out the
2081        syntax to do it, so instead I'm supressing the warning that comes
2082        up because it isn't marked as only. */
2083     /*@-unqualifiedtrans@*/
2084     while(textlinecount--) free(textlines[textlinecount]);
2085     free(textlines);
2086     /*@=unqualifiedtrans@*/
2087   }
2088 
2089 #ifdef HAVE_UNISTD_H
2090   (void) close(oldstdin); /* AIS */
2091 #endif
2092   /* This point is the very end of the program. So it's correct for
2093      normal DO NOT FREE UNDER ANY CIRCUMSTANCES globals to be free
2094      at this point, so supressing the warning given as a result. */
2095   /*@-globstate@*/
2096   return 0;
2097   /*@=globstate@*/
2098 }
2099 
2100 /* perpet.c ends here */
2101