add basic configure script
[suif.git] / src / basesuif / scc / scc.cc
blobd96c9adc3f4f6324b09cfdf6b9d5d965e58f7fac
1 /* file "scc.cc" */
3 /* Copyright (c) 1994 Stanford University
5 All rights reserved.
7 This software is provided under the terms described in
8 the "suif_copyright.h" include file. */
10 #include <suif_copyright.h>
12 /* SCC Compiler Driver */
14 #include <suif1.h>
15 #include <signal.h>
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <stdarg.h>
19 #include <string.h>
21 #ifndef WIN32
22 #include <unistd.h>
23 #include <sys/time.h>
24 #include <sys/wait.h>
25 #else
26 #include <process.h>
27 #include <io.h>
28 #define pid_t int
29 // for access() - since Win32 does not have UNIX-style execute permissions
30 // just check for the existence of the executable
31 #define X_OK 0
32 #endif
34 #include "String.h"
36 #ifndef NULL
37 #define NULL 0
38 #endif
40 #include "scc.h"
42 #ifdef RLIMIT_STACK
43 #ifndef WIN32
44 #define SUIF_NEED_RLIMIT
45 #endif
47 #include <machine_dependent.h>
48 #undef SUIF_NEED_RLIMIT
49 #endif /* RLIMIT_STACK */
51 int
52 main(int argc, char **argv)
54 start_suif(argc, argv);
56 filename *infile;
57 int interesting_files = 0;
58 int p;
59 pass *pp;
61 /* Try to get environment variables from the environment. If they
62 don't exist, just use the defaults. */
63 if ((suif_top = getenv("SUIFHOME")) == 0)
64 suif_top = STRINGIFY(SUIF_TOP);
65 if ((suif_path = getenv("SUIFPATH")) == 0)
66 suif_path = STRINGIFY(SUIFPATH);
67 if ((tmpdir = getenv("TMPDIR")) == 0)
68 tmpdir = TMPDIR;
70 #ifdef RLIMIT_STACK
71 /* unlimit the stack */
72 rlimit rl;
73 assert(getrlimit(RLIMIT_STACK, &rl) == 0);
74 rl.rlim_cur = rl.rlim_max;
75 setrlimit(RLIMIT_STACK, &rl);
76 #endif
78 /* initialize the command table */
79 for (p = 0; p < last_pass; p++) {
80 pp = &passtbl[p];
81 pp->flags = new String*[MAX_FLAGS];
82 for (int flag_index = 0; flag_index < MAX_FLAGS; ++flag_index)
83 pp->flags[flag_index] = NULL;
84 pp->pass_options = new String;
85 pp->exec = UNKNOWN;
88 /* create the lists of input and output filenames */
89 infiles = new filelist;
90 outfiles = new filelist;
91 tmpfiles = new filelist;
93 /* default: compile to an executable (a.out) file */
94 s_target = s_out;
96 /* default target machine: unknown */
97 target_machine = NULL;
99 /* save the name of this program (usually "scc") */
100 cmdnam = argv[0];
102 /* generate a base for temporary file names */
103 tmpbase = new char[10];
104 sprintf(tmpbase, "scc%05lu", (unsigned long)getpid());
106 /* trap signals and cleanup when we get them */
107 signal(SIGINT, interrupt);
108 signal(SIGTERM, interrupt);
109 #ifndef WIN32
110 /* catch SIGHUP if it was not previously ignored */
111 if ((void *)signal(SIGHUP, (void (*)(int))SIG_IGN) != (void *)SIG_IGN) {
112 signal(SIGHUP, interrupt);
114 #endif
116 /* read command line options */
117 argv++;
118 while (*argv) {
120 if (argv[0][0] == '-') {
122 argv = read_flag(argv);
124 } else {
126 /* if not a flag, this must be an input file */
127 interesting_files++;
128 Suffix s = getsfx(argv[0]);
129 if (s == s_a) {
130 /* special case: pass .a files to the linker */
131 *passtbl[LD].pass_options += argv[0];
132 *passtbl[LD].pass_options += " ";
133 } else {
134 if (s == s_f || s == s_F) sf2c_flag = TRUE;
135 String the_name(argv[0]);
136 infile = new filename(the_name, base_from_name(the_name),
137 find_start_pass(argv[0]), FALSE);
138 outfiles->append(infile);
141 argv++;
144 if (version_only)
146 const char *base_cmd_name;
147 if (cmdnam == NULL)
149 base_cmd_name = "suif";
151 else
153 base_cmd_name = strrchr(cmdnam, '/');
154 if (base_cmd_name == NULL)
155 base_cmd_name = cmdnam;
156 else
157 ++base_cmd_name;
159 fprintf(stderr, "%s %s%s\n", base_cmd_name, prog_ver_string,
160 prog_who_string);
161 exit(0);
164 if (interesting_files == 0) usage();
166 if (reassociate_arrays == UNKNOWN) reassociate_arrays = (opt_level > 0);
167 if (target_machine == NULL) target_machine = scc_machine;
169 if (s_target == s_s)
170 *passtbl[BACKEND_CC].pass_options += " -S";
172 if (!alternate_cc)
173 passtbl[LD].cmdname = "cc";
175 if (suif_bin) {
176 default_path = new String*[2];
177 default_path[0] = new String(suif_bin);
178 default_path[1] = NULL;
180 } else if (suif_path) {
182 /* count the colons in the path */
183 int num_paths = 2;
184 const char *c = suif_path;
185 while (*c != '\0') {
186 if (*c++ == ':') num_paths++;
188 default_path = new String*[num_paths];
190 /* copy the paths into the default path array */
191 c = suif_path;
192 int path_num = 0;
193 do {
195 /* copy the string */
196 default_path[path_num] = new String;
197 while ((*c != '\0') && (*c != ':')) {
198 *default_path[path_num] += *c++;
200 *default_path[path_num] += '\0';
202 /* go to the next element of the path */
203 path_num++;
204 } while (*c++ != '\0');
206 default_path[path_num] = NULL;
208 } else {
209 default_path = new String*[2];
210 default_path[0] = new String(suif_top);
211 *default_path[0] += "/";
212 *default_path[0] += scc_machine;
213 *default_path[0] += "/bin";
214 default_path[1] = NULL;
217 /* set the include directory name */
218 String *incl_tmp = new String(suif_top);
219 *incl_tmp += "/";
220 *incl_tmp += target_machine;
221 *incl_tmp += "/include";
222 suif_include = incl_tmp->string();
224 /* set the lib directory name */
225 String *lib_tmp = new String(suif_top);
226 *lib_tmp += "/";
227 *lib_tmp += target_machine;
228 *lib_tmp += "/lib";
229 suif_lib = lib_tmp->string();
231 /* set up the pass table */
232 for (p = 0; p < last_pass; p++) {
233 pp = &passtbl[p];
234 pp->dir = (*pp->set_dir)();
235 (*pp->set_flags)(pp);
236 /* allow -yes and -no options to override defaults */
237 if (pp->exec == UNKNOWN) pp->exec = (*pp->set_exec)();
240 /* run the passes.... */
241 process_files();
242 return 0;
247 extern void string_from_file(const char *file_name, String *the_string)
249 FILE *fp = fopen(file_name, "r");
250 if (fp == NULL)
251 return;
252 int inchar = fgetc(fp);
253 while (inchar != EOF)
255 if ((inchar == '\n') || (inchar == 0))
256 *the_string += ' ';
257 else
258 *the_string += (char)inchar;
259 inchar = fgetc(fp);
261 fclose(fp);
264 char ** read_flag(char **argv)
266 passes p;
268 switch (argv[0][1]) {
270 case 'a':
271 if (!strcmp(argv[0], "-automatic")) {
272 automatic_flag = TRUE;
273 break;
275 bad_option(argv[0]);
277 case 'c':
278 if (!strcmp(argv[0], "-checkwarn")) {
279 checksuif = TRUE;
280 break;
282 if (!strcmp(argv[0], "-checkfail")) {
283 checksuif = TRUE;
284 checkfail = TRUE;
285 break;
287 if (!strcmp(argv[0], "-cc")) {
288 if (argv[1] == 0)
289 error(1, "usage: -cc <back-end C compiler name>");
290 passtbl[BACKEND_CC].cmdname = argv[1];
291 passtbl[LD].cmdname = argv[1];
292 alternate_cc = TRUE;
293 argv++;
294 break;
296 if (!strcmp(argv[0], "-c")) {
297 s_target = s_o;
298 break;
300 bad_option(argv[0]);
302 case 'd':
303 bad_option(argv[0]);
305 case 'f':
306 if (!strcmp(argv[0], "-f2c")) {
307 sf2c_flag = TRUE;
308 break;
310 bad_option(argv[0]);
312 case 'g':
313 if (!strcmp(argv[0], "-g")) {
314 g_flag = TRUE;
315 break;
317 bad_option(argv[0]);
319 case 'k':
320 if (!strcmp(argv[0], "-keep")) {
321 keepflag++;
322 if (keepflag > 1) tmpdir = ".";
323 break;
325 if (!strcmp(argv[0], "-k")) {
326 nullflag = TRUE;
327 break;
329 bad_option(argv[0]);
331 case 'l':
332 *passtbl[LD].pass_options += argv[0];
333 *passtbl[LD].pass_options += " ";
334 break;
336 case 'm':
337 bad_option(argv[0]);
339 case 'n':
340 if (!strcmp(argv[0], "-no")) {
341 if (argv[1] == 0) error(1, "usage: -no <pass>");
342 if ((p = find_pass(argv[1])) == last_pass)
343 error(1, "-no: unknown pass \"%s\"", argv[1]);
344 passtbl[p].exec = FALSE;
345 argv++;
346 break;
348 if (!strcmp(argv[0], "-noreassoc")) {
349 reassociate_arrays = FALSE;
350 break;
352 bad_option(argv[0]);
354 case 'o':
355 if (!strcmp(argv[0],"-option")) {
356 if (argv[1] == 0 || argv[2] == 0)
357 error(1, "usage: -option <pass> <option>");
358 if ((p = find_pass(argv[1])) == last_pass)
359 error(1, "-option: unknown pass \"%s\"", argv[1]);
360 *passtbl[p].pass_options += argv[2];
361 *passtbl[p].pass_options += " ";
362 argv += 2;
363 break;
365 if (!strcmp(argv[0], "-o")) {
366 if (argv[1] == 0)
367 error(1, "-o must have file argument");
368 if (outfilename != NULL)
369 error(1, "multiple -o files specified");
370 outfilename = new String(argv[1]);
371 argv++;
372 break;
374 bad_option(argv[0]);
376 case 'p':
377 bad_option(argv[0]);
379 case 'r':
380 if (!strcmp(argv[0], "-reassoc")) {
381 reassociate_arrays = TRUE;
382 break;
384 bad_option(argv[0]);
386 case 's':
387 if (!strcmp(argv[0], "-s2c")) {
388 break;
390 if (!strcmp(argv[0], "-sf2c")) {
391 sf2c_flag = TRUE;
392 break;
394 if (!strcmp(argv[0], "-static")) {
395 automatic_flag = FALSE;
396 break;
398 if (!strcmp(argv[0], "-show")) {
399 verbose = TRUE;
400 break;
402 if (!strcmp(argv[0], "-suif-link")) {
403 option_linksuif = TRUE;
404 break;
406 bad_option(argv[0]);
408 case 't':
409 tmpdir = &argv[0][2];
410 if (argv[0][2] == 0) tmpdir = ".";
411 break;
413 case 'v':
414 if (!strcmp(argv[0], "-version")) {
415 version_only = TRUE;
416 break;
418 if (!strcmp(argv[0], "-v")) {
419 verbose = TRUE;
420 break;
422 bad_option(argv[0]);
424 case 'w':
425 if (!strcmp(argv[0], "-w")) {
426 no_warn_flag = TRUE;
427 break;
429 bad_option(argv[0]);
431 case 'y':
432 if (!strcmp(argv[0], "-yes")) {
433 if (argv[1] == 0)
434 error(1, "usage: -yes <pass>");
435 if ((p = find_pass(argv[1])) == last_pass)
436 error(1, "-yes: unknown pass \"%s\"", argv[1]);
437 passtbl[p].exec = TRUE;
438 argv++;
439 break;
441 bad_option(argv[0]);
443 case 'B':
444 suif_bin = &argv[0][2];
445 if (argv[0][2] == '\0')
446 error(1, "usage: -Bprefix");
447 break;
449 case 'E':
450 if (!strcmp(argv[0],"-E")) {
451 s_target = s_i;
452 out_to_stdout = TRUE;
453 break;
455 bad_option(argv[0]);
457 case 'G':
458 if (!strcmp(argv[0], "-G")) {
459 if (argv[1] == 0) error(1, "-G must have argument");
460 char *p;
461 Gnum = strtol(argv[1], &p, 10);
462 if (*p) error(1, "-G must have number argument");
463 if (Gnum < 0)
464 error(1, "-G must have non-negative argument");
465 argv++;
466 break;
468 bad_option(argv[0]);
470 case 'L':
471 *passtbl[LD].pass_options += argv[0];
472 *passtbl[LD].pass_options += " ";
473 break;
475 case 'M':
476 if (!strcmp(argv[0], "-M")) {
477 s_target = s_i;
478 out_to_stdout = TRUE;
479 pp_deps = TRUE;
480 break;
482 bad_option(argv[0]);
484 case 'N':
485 *passtbl[SF2C].pass_options += argv[0];
486 *passtbl[SF2C].pass_options += " ";
487 break;
489 case 'D': /* defines for cpp */
490 case 'I': /* include directory for cpp */
491 case 'U': /* undefines for cpp */
492 *passtbl[CPP].pass_options += argv[0];
493 *passtbl[CPP].pass_options += " ";
494 break;
496 case 'O': /* run scalar opts */
497 if (!strncmp(argv[0], "-O", 2)) {
498 if (argv[0][2] == 0) {
499 opt_level = 1;
500 } else if ((argv[0][2] >= '0') && (argv[0][2] <= '9') &&
501 (argv[0][3] == 0)) {
502 opt_level = argv[0][2] - '0';
503 } else {
504 bad_option(argv[0]);
506 } else {
507 bad_option(argv[0]);
509 break;
511 case 'S':
512 if (!strcmp(argv[0], "-S")) {
513 s_target = s_s;
514 } else {
515 bad_option(argv[0]);
517 break;
519 case 'T':
520 if (!strcmp(argv[0], "-T")) {
521 timing = TRUE;
522 } else if (!strcmp(argv[0], "-Target")) {
523 if (argv[1] == 0)
524 error(1, "usage: -Target <target machine name>");
525 target_machine = argv[1];
526 argv++;
527 break;
528 } else {
529 bad_option(argv[0]);
531 break;
533 case 'V':
534 if (!strcmp(argv[0], "-V")) {
535 verbose = TRUE;
536 break;
538 bad_option(argv[0]);
540 case 'W':
541 if (!strcmp(argv[0], "-Wall")) {
542 no_warn_flag = FALSE;
543 break;
545 bad_option(argv[0]);
547 case '.':
548 for (s_target = s_c; s_target <= s_o; s_target++) {
549 if (!strcmp(&argv[0][2], suffixes[s_target]))
550 break;
552 if (s_target > s_o) {
553 char buf[1024], buf2[10];
554 sprintf(buf, "bad -. option: %s\nUse one of: ",
555 argv[0]);
556 for (s_target = s_c; s_target <= s_o; s_target++) {
557 sprintf(buf2, "-.%s ", suffixes[s_target]);
558 strcat(buf, buf2);
560 error(1, buf);
562 break;
564 default:
565 bad_option(argv[0]);
568 return argv;
573 passes find_pass(const char *name)
575 int p;
577 for (p = 0; p < last_pass; p++) {
578 if (!strcmp(name, passtbl[p].passname)) break;
581 return (passes)p;
586 /* Find the first pass to run over the input file, based on the suffix of
587 the filename. */
589 passes find_start_pass(const char *infile)
591 Suffix infile_sfx, pass_sfx;
592 int p;
594 infile_sfx = getsfx(infile);
595 if (infile_sfx == s_tmp) {
596 error(1, "Unknown suffix (%s)", infile);
599 /* search the command table for the first pass which produces files with
600 * a suffix that is greater than or equal to the infile suffix */
601 for (p = 0; p < last_pass; p++) {
602 pass_sfx = passtbl[p].suffix;
603 if ((pass_sfx != s_tmp) && (pass_sfx >= infile_sfx)) break;
606 /* skip over passes that produce the same suffix as the infile suffix */
607 while (pass_sfx == infile_sfx) {
608 p += 1;
609 pass_sfx = passtbl[p].suffix;
612 return (passes)p;
617 void process_files()
619 passes p, last_real_sfx;
620 int next_many_in_pass;
621 pass *pp;
622 boolean reached_target = FALSE;
624 p = (passes)0;
625 while (p <= last_pass) {
627 /* Search through the pass table from the current pass until finding
628 * a pass that requires all of the source files at once (as indicated
629 * by the file_counts flag) or that generates the desired target. */
631 for (next_many_in_pass = p; next_many_in_pass < last_pass;
632 next_many_in_pass++) {
634 if (reached_target) break;
635 pp = &passtbl[next_many_in_pass];
637 if (pp->suffix != s_tmp) {
638 if (pp->suffix > s_target) {
639 reached_target = TRUE;
640 next_many_in_pass = last_real_sfx + 1;
641 break;
643 last_real_sfx = (passes)next_many_in_pass;
646 if (!pp->exec) continue;
647 if ((pp->file_counts == MANY_IN_ONE_OUT) ||
648 (pp->file_counts == MANY_IN_MANY_OUT)) {
649 break;
651 if (pp->suffix == s_target) {
652 reached_target = TRUE;
656 /* Compile the individual files up to the next requiring all input
657 * files at once. Note: here and throughout the remaining steps of
658 * the compilation, we make sure that each filename is kept on one
659 * of the filelists (infiles, * outfiles, or tmpfiles) so that we
660 * can clean things up correctly if we're interrupted. */
662 filelistiter fiter(outfiles);
663 while (!fiter.is_empty()) {
664 filename *test_file = (filename *)(fiter.step());
665 if (test_file->start_pass < next_many_in_pass) {
666 transfer_filename(test_file, outfiles, tmpfiles);
670 parallel_passes = contains_multiple_files(tmpfiles);
672 fiter.reset(tmpfiles);
673 while (!fiter.is_empty()) {
674 filename *infile = (filename *)(fiter.step());
675 run_passes(infile, (passes)next_many_in_pass);
678 parallel_passes = FALSE;
680 if (reached_target || (next_many_in_pass >= last_pass)) break;
682 run_many_in_pass((passes)next_many_in_pass);
683 if (passtbl[next_many_in_pass].suffix == s_target)
684 reached_target = TRUE;
686 p = (passes)(next_many_in_pass + 1);
692 /* This returns true if and only if the_list contains at least two files. */
694 boolean contains_multiple_files(filelist *the_list)
696 filelistiter fiter(the_list);
697 if (fiter.is_empty())
698 return FALSE;
699 (void)(fiter.step());
700 return !(fiter.is_empty());
705 /* Run a series of passes over the input file, beginning with the pass
706 specified in the filename structure and stopping BEFORE running the
707 stop_pass. The name of the final output file is returned. */
709 void run_passes(filename *orig_infile, passes stop_pass)
711 filename *infile, *outfile;
712 int p;
714 /* ignore files that are not used until after stop_pass */
715 passes start_pass = orig_infile->start_pass;
716 if (start_pass >= stop_pass)
717 return;
719 /* the original infile is kept around to record the filename base */
720 infile = orig_infile;
721 transfer_filename(infile, tmpfiles, outfiles);
723 for (p = start_pass; p < stop_pass; p++) {
725 /* skip over passes that don't execute */
726 if (!passtbl[p].exec) continue;
728 transfer_filename(infile, outfiles, infiles);
729 outfile = choose_output_file((passes)p, infile->basename, TRUE);
730 outfiles->append(outfile);
732 run_pass_one_in_one_out((passes)p, infile->name.string(),
733 outfile->name.string());
735 /* the output of this pass becomes the input for the next */
736 infile = outfile;
742 /* List the names of all the files in the given filelist in a single string,
743 separated by single space characters */
745 String list_names(filelist *files)
747 String result;
748 boolean first = TRUE;
750 filelistiter fiter(files);
751 while (!fiter.is_empty()) {
752 filename *infile = (filename *)(fiter.step());
753 if (first)
754 first = FALSE;
755 else
756 result += " ";
757 result += infile->name;
760 return result;
765 filename *choose_output_file(passes the_pass, String base,
766 boolean has_base)
768 /* find the appropriate suffix for the output of this pass */
769 Suffix outfile_sfx = passtbl[the_pass].suffix;
771 Suffix next_sfx = s_tmp;
772 Suffix save_sfx = s_tmp;
773 passes the_next_pass = (passes)(the_pass + 1);
775 /* check through the passes that won't be executed */
776 while (!passtbl[the_next_pass].exec) {
777 Suffix pass_sfx = passtbl[the_next_pass].suffix;
778 if ((pass_sfx != s_tmp) && (pass_sfx <= s_target)) {
780 /* save the previous value of next_sfx */
781 if (pass_sfx != next_sfx) save_sfx = next_sfx;
783 next_sfx = pass_sfx;
785 the_next_pass = (passes)(the_next_pass + 1);
786 if (the_next_pass >= last_pass) break;
789 /* go back to saved suffix if found a pass that will generate next_sfx */
790 if ((the_next_pass < last_pass) &&
791 (passtbl[the_next_pass].suffix == next_sfx)) {
792 next_sfx = save_sfx;
795 /* replace the suffix if this pass produces a file with a temporary suffix
796 or if this is the last pass before the target suffix is reached */
797 if ((outfile_sfx == s_tmp) || (next_sfx == s_target)) {
798 outfile_sfx = next_sfx;
801 String suffix_name = suffix_to_string(outfile_sfx);
803 String base_used;
804 if (has_base) {
805 base_used = base;
806 } else {
807 if (outfilename != NULL) {
808 base_used = base_from_name(*outfilename);
809 } else {
810 /* default name is "a"; this causes the linker to produce
811 "a.out" */
812 base_used += "a";
816 String the_name;
817 boolean will_erase;
818 if (outfile_sfx == s_target) {
819 will_erase = FALSE;
820 if (!out_to_stdout) {
821 if (parallel_passes) {
822 if (outfilename != NULL) {
823 error(0, "Multiple output files: \"-o %s\" ignored.\n",
824 outfilename->string());
826 the_name = base_used;
827 the_name += '.';
828 the_name += suffix_name;
829 } else {
830 if (outfilename != NULL) {
831 the_name = *outfilename;
832 } else {
833 the_name = base_used;
834 the_name += '.';
835 the_name += suffix_name;
839 } else {
840 will_erase = ((keepflag == 0) ||
841 ((keepflag == 1) && (outfile_sfx == s_tmp)));
842 if (will_erase) {
843 the_name = new_temp_base();
844 } else {
845 the_name = base_used;
847 the_name += '.';
848 the_name += suffix_name;
851 return new filename(the_name, base_used, the_next_pass, will_erase);
856 /* This returns everything before the extension of a file name. */
858 String base_from_name(String the_name)
860 const char *name_string = the_name.string();
861 const char *extension = NULL;
862 char *buffer;
864 const char *follow = name_string;
865 while (*follow != 0) {
866 if (*follow == '/')
867 extension = NULL;
868 else if (*follow == '.')
869 extension = follow;
870 ++follow;
872 if (extension == NULL)
873 extension = follow;
875 buffer = new char[(extension - name_string) + 1];
876 strncpy(buffer, name_string, (extension - name_string));
877 buffer[extension - name_string] = 0;
878 String return_value(buffer);
879 delete buffer;
880 return return_value;
885 /* If the suffix is a temporary, this returns a new unique suffix.
886 Otherwise, it returns the string for that suffix. */
888 String suffix_to_string(Suffix the_suffix)
890 static long int suffix_number = 0;
891 String return_value;
893 if (the_suffix == s_tmp) {
894 char buf[32];
896 sprintf(buf, "%ld", suffix_number++);
897 return_value += buf;
898 } else {
899 return_value += suffixes[the_suffix];
901 return return_value;
906 /* Run a pass that requires all of the input files at once. The input
907 files are removed from the infiles list, and the output file (as well as
908 any of the input files which will be used in future passes) is added to
909 the outlist. */
911 void run_many_in_pass(passes this_pass)
913 String outfile_string;
914 String in_out_string;
916 /* Search through the output files and add any files that should be
917 processed by this pass to the input files list. */
919 filelistiter fiter(outfiles);
920 while (!fiter.is_empty()) {
921 filename *test_file = (filename *)(fiter.step());
922 if (test_file->start_pass <= this_pass) {
923 transfer_filename(test_file, outfiles, infiles);
927 if (passtbl[this_pass].file_counts == MANY_IN_ONE_OUT) {
928 String dummy;
929 filename *outfile = choose_output_file(this_pass, dummy, FALSE);
930 outfiles->append(outfile);
931 outfile_string += outfile->name;
932 in_out_string += list_names(infiles);
933 in_out_string += ' ';
934 in_out_string += outfile->name;
935 } else {
936 parallel_passes = contains_multiple_files(infiles);
937 filelistiter fiter(infiles);
938 while (!fiter.is_empty()) {
939 filename *one_file = (filename *)(fiter.step());
940 filename *outfile =
941 choose_output_file(this_pass, one_file->basename, TRUE);
942 outfiles->append(outfile);
943 outfile_string += outfile->name;
944 in_out_string += one_file->name;
945 in_out_string += ' ';
946 in_out_string += outfile->name;
947 if (!fiter.is_empty()) {
948 outfile_string += ' ';
949 in_out_string += ' ';
952 parallel_passes = FALSE;
955 if (!uses_specified_output(this_pass)) {
956 error(0, "Pass %s cannot have multiple input files.\n"
957 "(the output file is not specified)",
958 passtbl[this_pass].passname);
959 cleanup();
962 String infiles_string = list_names(infiles);
963 run_pass_basic(this_pass, infiles_string.string(),
964 outfile_string.string(), in_out_string.string());
969 /* This function returns whether or not a particular pass uses the output
970 name scc gives it or not. It bases this decision on whether or not the
971 %o flag is included in that pass's format string. */
973 boolean uses_specified_output(passes p)
975 pass *pp = &passtbl[p];
977 const char *fmt_index = pp->cmdfmt;
978 do {
979 if (*fmt_index == '%') {
980 fmt_index++;
981 if ((*fmt_index == 'o') || (*fmt_index == 'a')) {
982 return TRUE;
985 } while (*fmt_index++ != '\0');
987 return FALSE;
992 void run_pass_one_in_one_out(passes p, const char *infile, const char *outfile)
994 /* if the command format string doesn't include a %o field, then the
995 * output file name cannot be specified; this flag is used to detect
996 * the presence of the %o flag in the format string. */
997 boolean used_outfile = uses_specified_output(p);
999 String in_out_string;
1000 in_out_string += infile;
1001 in_out_string += ' ';
1002 in_out_string += outfile;
1003 run_pass_basic(p, infile, outfile, in_out_string.string());
1005 if (!used_outfile) {
1007 /* Find the name of the output file. This assumes that if the output
1008 * filename cannot be specified that it defaults to just changing the
1009 * suffix of the input file. We just replace the suffix of the input
1010 * file. */
1012 /* the pass must have a named suffix */
1013 Suffix pass_sfx = passtbl[p].suffix;
1014 if (pass_sfx == s_tmp) {
1015 error(0, "Pass %s must be given a named suffix.\n"
1016 "(the output file is not specified)", passtbl[p].passname);
1017 cleanup();
1020 /* if the command didn't put the output in the right file,
1021 * we have to move it there. */
1022 String infile_name(infile);
1023 String actual_outfile = base_from_name(infile_name);
1024 actual_outfile += '.';
1025 actual_outfile += suffix_to_string(pass_sfx);
1026 move_file(actual_outfile.string(), outfile);
1032 /* Run a pass over the specified input file(s) and put the output in the
1033 specified file. The format for the command line, as well as the flags
1034 and pass_options, are read from the global pass table. */
1036 void run_pass_basic(passes p, const char *infile_names, const char *outfiles,
1037 const char *in_out_files)
1039 String command_line;
1040 int flag_index = 0;
1041 pass *pp = &passtbl[p];
1043 if (timing) {
1044 command_line += TIMECMD;
1045 command_line += " ";
1048 command_line += select_path(pp->cmdname, pp->dir);
1049 command_line += "/";
1050 command_line += pp->cmdname;
1051 command_line += " ";
1053 /* Now we have to parse the command format string.... */
1054 const char *fmt_index = pp->cmdfmt;
1055 do {
1056 if (*fmt_index != '%') {
1057 command_line += *fmt_index;
1058 } else {
1059 fmt_index++;
1060 switch (*fmt_index) {
1061 case 'i':
1062 command_line += infile_names;
1063 break;
1065 case 'o':
1066 command_line += outfiles;
1067 break;
1069 case 'a':
1070 command_line += in_out_files;
1071 break;
1073 case 'f':
1074 if (pp->flags[flag_index]) {
1075 command_line += *pp->flags[flag_index];
1077 if (++flag_index > MAX_FLAGS) {
1078 error(0, "Too many flags in command format for %s\n",
1079 pp->passname);
1080 cleanup();
1082 break;
1084 case 'p':
1085 command_line += *pp->pass_options;
1086 break;
1088 default:
1089 error(0,"Unrecognized flag %%%c in command format for %s\n",
1090 *fmt_index, pp->passname);
1091 cleanup();
1094 } while (*fmt_index++ != '\0');
1096 if (verbose)
1097 fprintf(stderr, "%s: %s\n", pp->passname, command_line.string());
1099 if (!nullflag) {
1100 int stat = execute_cmd(command_line.string());
1101 if (stat)
1102 cleanup();
1104 if (checksuif && (pp->out_file_format == OUTPUT_SUIF)) {
1105 String check_command_line;
1107 if (timing) {
1108 check_command_line += TIMECMD;
1109 check_command_line += " ";
1112 check_command_line += select_path("checksuif", NULL);
1113 check_command_line += "/";
1114 check_command_line += "checksuif ";
1115 if (checkfail)
1116 check_command_line += "-fail ";
1117 else
1118 check_command_line += "-warn ";
1119 check_command_line += outfiles;
1120 stat = execute_cmd(check_command_line.string());
1121 if (stat)
1122 cleanup();
1126 /* delete any temporary files */
1127 erase_files(infiles);
1132 /* Find a directory containing the executable cmd. The optional directory
1133 argument is checked first if it is provided. Otherwise, the directories
1134 in the default_path array are checked. If none of the directories
1135 contain the executable, then an error is signaled and the program exits. */
1137 const char * select_path(const char *cmd, const char *dir)
1139 String path, *p;
1141 #ifdef WIN32
1142 char *exeName = new char[strlen(cmd) + 10];
1143 sprintf(exeName, "%s.exe", cmd);
1144 cmd = exeName;
1145 #endif
1147 /* first try the specified directory */
1148 if (dir) {
1149 path += dir;
1150 path += "/";
1151 path += cmd;
1152 if (!access(path.string(), X_OK)) return dir;
1155 /* otherwise, try the default directories */
1156 int path_num = 0;
1157 while ((p = default_path[path_num++])) {
1158 path.clear();
1159 path += p->string();
1160 path += "/";
1161 path += cmd;
1162 if (!access(path.string(), X_OK)) return p->string();
1165 /* none of the paths worked */
1166 error(0, "Cannot find an executable file for \"%s\".\n"
1167 "Check the value of your SUIFPATH variable.", cmd);
1168 cleanup();
1170 #ifdef WIN32
1171 delete exeName;
1172 #endif
1174 return NULL;
1179 /* Execute a command. First, we have to break the command string into
1180 words and store the individual words into an array. Next, we fork off
1181 a new process and exec the specified command, and finally, we check
1182 the return status of the child process. Note: I tried using the system()
1183 call to execute commands via the shell. This avoids having to split
1184 the command into words and allows use of wildcards and other shell
1185 features. Unfortunately, the return status did not indicate whether
1186 the child process had been killed, so I went back to directly executing
1187 the commands. */
1189 /* make it all look like system V */
1190 #ifndef WIFSTOPPED
1191 #define WIFSTOPPED WIFSIGNALED
1192 #define WSTOPSIG WIFTEMSIG
1193 #endif
1195 #define SCC_INIT_CMD_ITEM_BUFFER_SIZE 20
1197 int execute_cmd(const char *cmd)
1199 pid_t pid, w;
1200 static char **cmd_items = NULL;
1201 static unsigned long cmd_item_space = 0;
1203 /* break the command into words */
1204 const char *start = cmd;
1205 const char *end = cmd;
1206 int i = 0;
1208 if (cmd_items == NULL)
1210 cmd_items = new char *[SCC_INIT_CMD_ITEM_BUFFER_SIZE];
1211 cmd_item_space = SCC_INIT_CMD_ITEM_BUFFER_SIZE;
1214 while (TRUE) {
1216 /* skip any leading blanks */
1217 while (*start == ' ') start++;
1218 if (*start == '\0') break;
1220 /* find the end of the word */
1221 boolean inside_quote = FALSE;
1222 boolean inside_dblquote = FALSE;
1223 end = start;
1224 while (TRUE) {
1225 if (*end == '\0') break;
1226 if (*end == '\'') {
1227 if (inside_quote) {
1228 inside_quote = FALSE;
1229 } else if (!inside_dblquote) {
1230 inside_quote = TRUE;
1233 if (*end == '"') {
1234 if (inside_dblquote) {
1235 inside_dblquote = FALSE;
1236 } else if (!inside_quote) {
1237 inside_dblquote = TRUE;
1240 if (!inside_quote && !inside_dblquote && (*end == ' ')) break;
1241 end++;
1244 /* store the word into the array */
1245 cmd_items[i] = new char[(end - start) + 1];
1246 strncpy(cmd_items[i], start, (end - start));
1247 cmd_items[i][(end - start)] = '\0';
1248 ++i;
1249 if ((unsigned)i == cmd_item_space)
1251 char **new_cmd_items = new char *[cmd_item_space * 2];
1252 memcpy(new_cmd_items, cmd_items, cmd_item_space * sizeof(char *));
1253 delete[] cmd_items;
1254 cmd_items = new_cmd_items;
1255 cmd_item_space *= 2;
1258 if (*end == '\0') break;
1259 start = end + 1;
1261 cmd_items[i] = NULL;
1263 #ifndef WIN32
1264 if ((pid = fork()) == -1) syscallerr("fork");
1266 /* check if this is now the child process */
1267 if (pid == 0) {
1268 /* execute the command */
1269 execv(cmd_items[0], cmd_items);
1270 /* should never reach this point */
1271 syscallerr("execv");
1273 /* otherwise, this must be the parent process */
1275 #else // WIN32
1277 char *exeName = new char[strlen(cmd_items[0]) + 10];
1278 sprintf(exeName, "%s.exe", cmd_items[0]);
1279 delete cmd_items[0];
1280 cmd_items[0] = exeName;
1282 int rc = _spawnv(_P_WAIT, cmd_items[0], (const char *const *) cmd_items);
1283 if (rc) {
1284 fprintf(stderr, "%s\nFAILED (exit status 0x%x)\n", cmd, rc);
1285 return rc;
1288 return rc;
1289 #endif
1293 /* delete the command items */
1294 for (i = i - 1; i >= 0; i--) {
1295 delete[] cmd_items[i];
1298 #ifndef WIN32
1299 /* wait for the child to finish */
1300 int retcode = 0;
1301 while ((w = wait(&retcode)) != pid) {
1302 if (w == -1) syscallerr("wait");
1303 /* I suppose w == 0 and I'm about to be interrupted. Not sure. */
1306 if (WIFEXITED(retcode)) {
1307 /* normal exit */
1308 int rc = WEXITSTATUS(retcode);
1309 if (rc) {
1310 fprintf(stderr, "%s\nFAILED (exit status 0x%x)\n", cmd, rc);
1311 return rc;
1314 } else if (WIFSTOPPED(retcode)) {
1315 /* child process caught a signal */
1316 int sig = WSTOPSIG(retcode);
1317 fprintf(stderr, "%s\nFAILED (caught signal 0x%x)\n", cmd, sig);
1318 return -1;
1320 } else {
1321 /* I have no idea, time to panic */
1322 fprintf(stderr, "%s\nFAILED (wait code 0x%x)\n", cmd, retcode);
1323 return -2;
1326 return 0;
1327 #endif
1332 /* Return a new unique temporary file base name. */
1334 String new_temp_base()
1336 char buf[32];
1337 static long int base_number = 0;
1339 String f(tmpdir);
1340 f += "/";
1341 f += tmpbase;
1342 f += "_";
1343 sprintf(buf, "%ld", base_number++);
1344 f += buf;
1345 return f;
1350 /* Return the suffix of the given filename */
1352 Suffix getsfx(const char *s)
1354 int c;
1355 const char *sfx = NULL;
1356 const char *fs = s;
1358 /* scan through the string and record the position of the last dot */
1359 while ((c = *s++)) {
1360 if (c == '.') sfx = s;
1362 if (!sfx) {
1363 error(0, "missing suffix, file \"%s\"", fs);
1364 cleanup();
1367 /* compare the suffix to all of those in the suffixes[] array */
1368 int r;
1369 for (r = s_F; r < s_tmp; r++) {
1370 if (!strcmp(suffixes[r], sfx)) break;
1373 return (Suffix)r;
1378 /* If the filenames are not identical, move the first file to the second. */
1380 void move_file(const char *f1, const char *f2)
1382 if (nullflag)
1383 return;
1385 if (strcmp(f1, f2)) {
1386 String mvcmd(MVCMD);
1387 mvcmd += " ";
1388 mvcmd += f1;
1389 mvcmd += " ";
1390 mvcmd += f2;
1391 int stat = execute_cmd(mvcmd.string());
1392 if (stat) {
1393 error(0, "Unable to move temporary file \"%s\" to \"%s\".", f1, f2);
1394 cleanup();
1401 /* The interrupt() function is called when we catch a signal. This gives
1402 us a chance to remove any temporary output files before exiting. */
1404 void interrupt(int)
1406 cleanup();
1411 void bad_option(const char *s)
1413 error(1, "unrecognized flag: %s", s);
1418 void usage()
1420 fprintf(stderr, "Usage: %s [flags] file ...\n", cmdnam);
1421 exit(-1);
1426 void error(int rc, const char *msg, ...)
1428 va_list ap;
1429 va_start(ap, msg);
1431 fprintf(stderr, "%s: ", cmdnam);
1432 vfprintf(stderr, msg, ap);
1434 putc('\n', stderr);
1435 if (rc != 0) exit(rc);
1440 /* Get rid of temporary files after detecting an error of some sort. */
1442 void cleanup()
1444 if (keepflag > 1) exit(-1);
1446 erase_files(infiles);
1447 erase_files(outfiles);
1448 erase_files(tmpfiles);
1449 if (moving_file != NULL)
1450 unlink(moving_file);
1452 exit(-1);
1457 /* Remove any temporary files on the specified list (also removes all of
1458 the filenames from the list and deletes them). */
1460 void erase_files(filelist *flist)
1462 filename *f;
1464 /* delete any temporary files */
1465 while (!flist->is_empty()) {
1466 f = (filename *)(flist->pop());
1467 /* try to remove the file -- don't worry if it fails */
1468 if (f->erase) unlink(f->name.string());
1469 delete f;
1475 void syscallerr(const char *s)
1477 fprintf(stderr, "system call error: %s\n", s);
1478 exit(-1);
1483 /* Safely transfer a file from one list to another, so it is always on at
1484 least one list or in the variable moving_file. That way whenever we catch
1485 a signal, every temporary file will be recorded in at least one place, so
1486 it will be properly removed. */
1488 void transfer_filename(filename *to_transfer, filelist *from, filelist *to)
1490 moving_file = to_transfer->name.string();
1491 from->remove(to_transfer);
1492 to->append(to_transfer);
1493 moving_file = NULL;
1498 filename::filename(String the_name, String the_basename,
1499 passes the_start_pass, boolean will_erase)
1501 name = the_name;
1502 basename = the_basename;
1503 start_pass = the_start_pass;
1504 erase = will_erase;