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