add basic configure script
[suif.git] / src / baseparsuif / pscc / pscc.cc
blobfbb538064f03f1b0f42ce753a214f660a2e353a1
1 /* file "pscc.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 /* PSCC Compiler Driver */
14 #include <suif1.h>
15 #include <signal.h>
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <stdarg.h>
19 #include <unistd.h>
20 #include <string.h>
21 #include <sys/time.h>
22 #include <sys/wait.h>
24 #include "String.h"
25 #include "pscc.h"
27 #ifdef RLIMIT_STACK
28 #define SUIF_NEED_RLIMIT
29 #include <machine_dependent.h>
30 #undef SUIF_NEED_RLIMIT
31 #endif /* RLIMIT_STACK */
33 int main(int argc, char **argv)
35 start_suif(argc, argv);
37 filename *infile;
38 int interesting_files = 0;
39 int p;
40 pass *pp;
42 /* Try to get environment variables from the environment. If they
43 don't exist, just use the defaults. */
44 if ((suif_top = getenv("SUIFHOME")) == 0)
45 suif_top = STRINGIFY(SUIF_TOP);
46 if ((suif_path = getenv("SUIFPATH")) == 0)
47 suif_path = STRINGIFY(SUIFPATH);
48 if ((tmpdir = getenv("TMPDIR")) == 0)
49 tmpdir = TMPDIR;
51 #ifdef RLIMIT_STACK
52 /* unlimit the stack */
53 rlimit rl;
54 assert(getrlimit(RLIMIT_STACK, &rl) == 0);
55 rl.rlim_cur = rl.rlim_max;
56 assert(setrlimit(RLIMIT_STACK, &rl) == 0);
57 #endif
59 /* initialize the command table */
60 for (p = 0; p < last_pass; p++) {
61 pp = &passtbl[p];
62 pp->flags = new String*[MAX_FLAGS];
63 for (int flag_index = 0; flag_index < MAX_FLAGS; ++flag_index)
64 pp->flags[flag_index] = NULL;
65 pp->pass_options = new String;
66 pp->exec = UNKNOWN;
69 /* create the lists of input and output filenames */
70 infiles = new filelist;
71 outfiles = new filelist;
72 tmpfiles = new filelist;
74 /* default: compile to an executable (a.out) file */
75 s_target = s_out;
77 /* default target machine: unknown */
78 target_machine = NULL;
80 /* save the name of this program (usually "pscc") */
81 cmdnam = argv[0];
83 /* generate a base for temporary file names */
84 tmpbase = new char[10];
85 sprintf(tmpbase, "pscc%05lu", (unsigned long)getpid());
87 /* trap signals and cleanup when we get them */
88 signal(SIGINT, interrupt);
89 signal(SIGTERM, interrupt);
90 /* catch SIGHUP if it was not previously ignored */
91 if ((void *)signal(SIGHUP, (void (*)(int))SIG_IGN) != (void *)SIG_IGN) {
92 signal(SIGHUP, interrupt);
95 /* read command line options */
96 argv++;
97 while (*argv) {
99 if (argv[0][0] == '-') {
101 argv = read_flag(argv);
103 } else {
105 /* if not a flag, this must be an input file */
106 interesting_files++;
107 Suffix s = getsfx(argv[0]);
108 if (s == s_a) {
109 /* special case: pass .a files to the linker */
110 *passtbl[LD].pass_options += argv[0];
111 *passtbl[LD].pass_options += " ";
112 } else {
113 if (s == s_f || s == s_F) sf2c_flag = TRUE;
114 String the_name(argv[0]);
115 infile = new filename(the_name, base_from_name(the_name),
116 find_start_pass(argv[0]), FALSE);
117 outfiles->append(infile);
120 argv++;
123 if (version_only)
125 char *base_cmd_name;
126 if (cmdnam == NULL)
128 base_cmd_name = "suif";
130 else
132 base_cmd_name = strrchr(cmdnam, '/');
133 if (base_cmd_name == NULL)
134 base_cmd_name = cmdnam;
135 else
136 ++base_cmd_name;
138 fprintf(stderr, "%s %s%s\n", base_cmd_name, prog_ver_string,
139 prog_who_string);
140 exit(0);
143 if (interesting_files == 0) usage();
145 if (reassociate_arrays == UNKNOWN) reassociate_arrays = (opt_level > 0);
146 if (target_machine == NULL) target_machine = pscc_machine;
148 if (s_target == s_s)
149 *passtbl[BACKEND_CC].pass_options += " -S";
151 if (!alternate_cc)
152 passtbl[LD].cmdname = "cc";
154 if (suif_bin) {
155 default_path = new String*[2];
156 default_path[0] = new String(suif_bin);
157 default_path[1] = NULL;
159 } else if (suif_path) {
161 /* count the colons in the path */
162 int num_paths = 2;
163 char *c = suif_path;
164 while (*c != '\0') {
165 if (*c++ == ':') num_paths++;
167 default_path = new String*[num_paths];
169 /* copy the paths into the default path array */
170 c = suif_path;
171 int path_num = 0;
172 do {
174 /* copy the string */
175 default_path[path_num] = new String;
176 while ((*c != '\0') && (*c != ':')) {
177 *default_path[path_num] += *c++;
179 *default_path[path_num] += '\0';
181 /* go to the next element of the path */
182 path_num++;
183 } while (*c++ != '\0');
185 default_path[path_num] = NULL;
187 } else {
188 default_path = new String*[2];
189 default_path[0] = new String(suif_top);
190 *default_path[0] += "/";
191 *default_path[0] += pscc_machine;
192 *default_path[0] += "/bin";
193 default_path[1] = NULL;
196 /* set the include directory name */
197 String *incl_tmp = new String(suif_top);
198 *incl_tmp += "/";
199 *incl_tmp += target_machine;
200 *incl_tmp += "/include";
201 suif_include = incl_tmp->string();
203 /* set the lib directory name */
204 String *lib_tmp = new String(suif_top);
205 *lib_tmp += "/";
206 *lib_tmp += target_machine;
207 *lib_tmp += "/lib";
208 suif_lib = lib_tmp->string();
210 /* set up the pass table */
211 for (p = 0; p < last_pass; p++) {
212 pp = &passtbl[p];
213 pp->dir = (*pp->set_dir)();
214 (*pp->set_flags)(pp);
215 /* allow -yes and -no options to override defaults */
216 if (pp->exec == UNKNOWN) pp->exec = (*pp->set_exec)();
219 /* run the passes.... */
220 process_files();
221 return 0;
226 extern void string_from_file(char *file_name, String *the_string)
228 FILE *fp = fopen(file_name, "r");
229 if (fp == NULL)
230 return;
231 int inchar = fgetc(fp);
232 while (inchar != EOF)
234 if ((inchar == '\n') || (inchar == 0))
235 *the_string += ' ';
236 else
237 *the_string += (char)inchar;
238 inchar = fgetc(fp);
240 fclose(fp);
243 char ** read_flag(char **argv)
245 passes p;
247 switch (argv[0][1]) {
249 case 'a':
250 if (!strcmp(argv[0], "-automatic")) {
251 automatic_flag = TRUE;
252 break;
254 bad_option(argv[0]);
256 case 'c':
257 if (!strcmp(argv[0], "-checkwarn")) {
258 checksuif = TRUE;
259 break;
261 if (!strcmp(argv[0], "-checkfail")) {
262 checksuif = TRUE;
263 checkfail = TRUE;
264 break;
266 if (!strcmp(argv[0], "-cc")) {
267 if (argv[1] == 0)
268 error(1, "usage: -cc <back-end C compiler name>");
269 passtbl[BACKEND_CC].cmdname = argv[1];
270 passtbl[LD].cmdname = argv[1];
271 alternate_cc = TRUE;
272 argv++;
273 break;
275 if (!strcmp(argv[0], "-c")) {
276 s_target = s_o;
277 break;
279 bad_option(argv[0]);
281 case 'd':
282 bad_option(argv[0]);
284 case 'f':
285 if (!strcmp(argv[0], "-f2c")) {
286 sf2c_flag = TRUE;
287 break;
289 bad_option(argv[0]);
291 case 'g':
292 if (!strcmp(argv[0], "-g")) {
293 g_flag = TRUE;
294 break;
296 bad_option(argv[0]);
298 case 'k':
299 if (!strcmp(argv[0], "-keep")) {
300 keepflag++;
301 if (keepflag > 1) tmpdir = ".";
302 break;
304 if (!strcmp(argv[0], "-k")) {
305 nullflag = TRUE;
306 break;
308 bad_option(argv[0]);
310 case 'l':
311 *passtbl[LD].pass_options += argv[0];
312 *passtbl[LD].pass_options += " ";
313 break;
315 case 'm':
316 if (!strcmp(argv[0],"-multi")) {
317 option_multi = TRUE;
318 break;
320 bad_option(argv[0]);
322 case 'n':
323 if (!strcmp(argv[0], "-no")) {
324 if (argv[1] == 0) error(1, "usage: -no <pass>");
325 if ((p = find_pass(argv[1])) == last_pass)
326 error(1, "-no: unknown pass \"%s\"", argv[1]);
327 passtbl[p].exec = FALSE;
328 argv++;
329 break;
331 if (!strcmp(argv[0], "-noreassoc")) {
332 reassociate_arrays = FALSE;
333 break;
335 bad_option(argv[0]);
337 case 'o':
338 if (!strcmp(argv[0],"-option")) {
339 if (argv[1] == 0 || argv[2] == 0)
340 error(1, "usage: -option <pass> <option>");
341 if ((p = find_pass(argv[1])) == last_pass)
342 error(1, "-option: unknown pass \"%s\"", argv[1]);
343 *passtbl[p].pass_options += argv[2];
344 *passtbl[p].pass_options += " ";
345 argv += 2;
346 break;
348 if (!strcmp(argv[0], "-o")) {
349 if (argv[1] == 0)
350 error(1, "-o must have file argument");
351 if (outfilename != NULL)
352 error(1, "multiple -o files specified");
353 outfilename = new String(argv[1]);
354 argv++;
355 break;
357 bad_option(argv[0]);
359 case 'p':
360 if (!strcmp(argv[0], "-parallel")) {
361 option_parallel = TRUE;
362 break;
364 bad_option(argv[0]);
366 case 'r':
367 if (!strcmp(argv[0], "-reassoc")) {
368 reassociate_arrays = TRUE;
369 break;
371 bad_option(argv[0]);
373 case 's':
374 if (!strcmp(argv[0], "-s2c")) {
375 break;
377 if (!strcmp(argv[0], "-sf2c")) {
378 sf2c_flag = TRUE;
379 break;
381 if (!strcmp(argv[0], "-static")) {
382 automatic_flag = FALSE;
383 break;
385 if (!strcmp(argv[0], "-show")) {
386 verbose = TRUE;
387 break;
389 if (!strcmp(argv[0], "-suif-link")) {
390 option_linksuif = TRUE;
391 break;
393 bad_option(argv[0]);
395 case 't':
396 tmpdir = &argv[0][2];
397 if (argv[0][2] == 0) tmpdir = ".";
398 break;
400 case 'v':
401 if (!strcmp(argv[0], "-version")) {
402 version_only = TRUE;
403 break;
405 if (!strcmp(argv[0], "-v")) {
406 verbose = TRUE;
407 break;
409 bad_option(argv[0]);
411 case 'w':
412 if (!strcmp(argv[0], "-w")) {
413 no_warn_flag = TRUE;
414 break;
416 bad_option(argv[0]);
418 case 'y':
419 if (!strcmp(argv[0], "-yes")) {
420 if (argv[1] == '\0')
421 error(1, "usage: -yes <pass>");
422 if ((p = find_pass(argv[1])) == last_pass)
423 error(1, "-yes: unknown pass \"%s\"", argv[1]);
424 passtbl[p].exec = TRUE;
425 argv++;
426 break;
428 bad_option(argv[0]);
430 case 'B':
431 suif_bin = &argv[0][2];
432 if (argv[0][2] == '\0')
433 error(1, "usage: -Bprefix");
434 break;
436 case 'E':
437 if (!strcmp(argv[0],"-E")) {
438 s_target = s_i;
439 out_to_stdout = TRUE;
440 break;
442 bad_option(argv[0]);
444 case 'G':
445 if (!strcmp(argv[0], "-G")) {
446 if (argv[1] == 0) error(1, "-G must have argument");
447 char *p;
448 Gnum = strtol(argv[1], &p, 10);
449 if (*p) error(1, "-G must have number argument");
450 if (Gnum < 0)
451 error(1, "-G must have non-negative argument");
452 argv++;
453 break;
455 bad_option(argv[0]);
457 case 'L':
458 *passtbl[LD].pass_options += argv[0];
459 *passtbl[LD].pass_options += " ";
460 break;
462 case 'M':
463 if (!strcmp(argv[0], "-M")) {
464 s_target = s_i;
465 out_to_stdout = TRUE;
466 pp_deps = TRUE;
467 break;
469 bad_option(argv[0]);
471 case 'N':
472 *passtbl[SF2C].pass_options += argv[0];
473 *passtbl[SF2C].pass_options += " ";
474 break;
476 case 'D': /* defines for cpp */
477 case 'I': /* include directory for cpp */
478 case 'U': /* undefines for cpp */
479 *passtbl[CPP].pass_options += argv[0];
480 *passtbl[CPP].pass_options += " ";
481 break;
483 case 'O': /* run scalar opts */
484 if (!strncmp(argv[0], "-O", 2)) {
485 if (argv[0][2] == 0) {
486 opt_level = 1;
487 } else if ((argv[0][2] >= '0') && (argv[0][2] <= '9') &&
488 (argv[0][3] == 0)) {
489 opt_level = argv[0][2] - '0';
490 } else {
491 bad_option(argv[0]);
493 } else {
494 bad_option(argv[0]);
496 break;
498 case 'S':
499 if (!strcmp(argv[0], "-S")) {
500 s_target = s_s;
501 } else {
502 bad_option(argv[0]);
504 break;
506 case 'T':
507 if (!strcmp(argv[0], "-T")) {
508 timing = TRUE;
509 } else if (!strcmp(argv[0], "-Target")) {
510 if (argv[1] == 0)
511 error(1, "usage: -Target <target machine name>");
512 target_machine = argv[1];
513 argv++;
514 break;
515 } else {
516 bad_option(argv[0]);
518 break;
520 case 'V':
521 if (!strcmp(argv[0], "-V")) {
522 verbose = TRUE;
523 break;
525 bad_option(argv[0]);
527 case 'W':
528 if (!strcmp(argv[0], "-Wall")) {
529 no_warn_flag = FALSE;
530 break;
532 bad_option(argv[0]);
534 case '.':
535 for (s_target = s_c; s_target <= s_o; s_target++) {
536 if (!strcmp(&argv[0][2], suffixes[s_target]))
537 break;
539 if (s_target > s_o) {
540 char buf[1024], buf2[10];
541 sprintf(buf, "bad -. option: %s\nUse one of: ",
542 argv[0]);
543 for (s_target = s_c; s_target <= s_o; s_target++) {
544 sprintf(buf2, "-.%s ", suffixes[s_target]);
545 strcat(buf, buf2);
547 error(1, buf);
549 break;
551 default:
552 bad_option(argv[0]);
555 return argv;
560 passes find_pass(char *name)
562 int p;
564 for (p = 0; p < last_pass; p++) {
565 if (!strcmp(name, passtbl[p].passname)) break;
568 return (passes)p;
573 /* Find the first pass to run over the input file, based on the suffix of
574 the filename. */
576 passes find_start_pass(char *infile)
578 Suffix infile_sfx, pass_sfx;
579 int p;
581 infile_sfx = getsfx(infile);
582 if (infile_sfx == s_tmp) {
583 error(1, "Unknown suffix (%s)", infile);
586 /* search the command table for the first pass which produces files with
587 * a suffix that is greater than or equal to the infile suffix */
588 for (p = 0; p < last_pass; p++) {
589 pass_sfx = passtbl[p].suffix;
590 if ((pass_sfx != s_tmp) && (pass_sfx >= infile_sfx)) break;
593 /* skip over passes that produce the same suffix as the infile suffix */
594 while (pass_sfx == infile_sfx) {
595 p += 1;
596 pass_sfx = passtbl[p].suffix;
599 return (passes)p;
604 void process_files()
606 passes p, last_real_sfx;
607 int next_many_in_pass;
608 pass *pp;
609 boolean reached_target = FALSE;
611 p = (passes)0;
612 while (p <= last_pass) {
614 /* Search through the pass table from the current pass until finding
615 * a pass that requires all of the source files at once (as indicated
616 * by the file_counts flag) or that generates the desired target. */
618 for (next_many_in_pass = p; next_many_in_pass < last_pass;
619 next_many_in_pass++) {
621 if (reached_target) break;
622 pp = &passtbl[next_many_in_pass];
624 if (pp->suffix != s_tmp) {
625 if (pp->suffix > s_target) {
626 reached_target = TRUE;
627 next_many_in_pass = last_real_sfx + 1;
628 break;
630 last_real_sfx = (passes)next_many_in_pass;
633 if (!pp->exec) continue;
634 if ((pp->file_counts == MANY_IN_ONE_OUT) ||
635 (pp->file_counts == MANY_IN_MANY_OUT)) {
636 break;
638 if (pp->suffix == s_target) {
639 reached_target = TRUE;
643 /* Compile the individual files up to the next requiring all input
644 * files at once. Note: here and throughout the remaining steps of
645 * the compilation, we make sure that each filename is kept on one
646 * of the filelists (infiles, * outfiles, or tmpfiles) so that we
647 * can clean things up correctly if we're interrupted. */
649 filelistiter fiter(outfiles);
650 while (!fiter.is_empty()) {
651 filename *test_file = (filename *)(fiter.step());
652 if (test_file->start_pass < next_many_in_pass) {
653 transfer_filename(test_file, outfiles, tmpfiles);
657 parallel_passes = contains_multiple_files(tmpfiles);
659 fiter.reset(tmpfiles);
660 while (!fiter.is_empty()) {
661 filename *infile = (filename *)(fiter.step());
662 run_passes(infile, (passes)next_many_in_pass);
665 parallel_passes = FALSE;
667 if (reached_target || (next_many_in_pass >= last_pass)) break;
669 run_many_in_pass((passes)next_many_in_pass);
670 if (passtbl[next_many_in_pass].suffix == s_target)
671 reached_target = TRUE;
673 p = (passes)(next_many_in_pass + 1);
679 /* This returns true if and only if the_list contains at least two files. */
681 boolean contains_multiple_files(filelist *the_list)
683 filelistiter fiter(the_list);
684 if (fiter.is_empty())
685 return FALSE;
686 (void)(fiter.step());
687 return !(fiter.is_empty());
692 /* Run a series of passes over the input file, beginning with the pass
693 specified in the filename structure and stopping BEFORE running the
694 stop_pass. The name of the final output file is returned. */
696 void run_passes(filename *orig_infile, passes stop_pass)
698 filename *infile, *outfile;
699 int p;
701 /* ignore files that are not used until after stop_pass */
702 passes start_pass = orig_infile->start_pass;
703 if (start_pass >= stop_pass)
704 return;
706 /* the original infile is kept around to record the filename base */
707 infile = orig_infile;
708 transfer_filename(infile, tmpfiles, outfiles);
710 for (p = start_pass; p < stop_pass; p++) {
712 /* skip over passes that don't execute */
713 if (!passtbl[p].exec) continue;
715 transfer_filename(infile, outfiles, infiles);
716 outfile = choose_output_file((passes)p, infile->basename, TRUE);
717 outfiles->append(outfile);
719 run_pass_one_in_one_out((passes)p, infile->name.string(),
720 outfile->name.string());
722 /* the output of this pass becomes the input for the next */
723 infile = outfile;
729 /* List the names of all the files in the given filelist in a single string,
730 separated by single space characters */
732 String list_names(filelist *files)
734 String result;
735 boolean first = TRUE;
737 filelistiter fiter(files);
738 while (!fiter.is_empty()) {
739 filename *infile = (filename *)(fiter.step());
740 if (first)
741 first = FALSE;
742 else
743 result += " ";
744 result += infile->name;
747 return result;
752 filename *choose_output_file(passes the_pass, String base,
753 boolean has_base)
755 /* find the appropriate suffix for the output of this pass */
756 Suffix outfile_sfx = passtbl[the_pass].suffix;
758 Suffix next_sfx = s_tmp;
759 Suffix save_sfx = s_tmp;
760 passes the_next_pass = (passes)(the_pass + 1);
762 /* check through the passes that won't be executed */
763 while (!passtbl[the_next_pass].exec) {
764 Suffix pass_sfx = passtbl[the_next_pass].suffix;
765 if ((pass_sfx != s_tmp) && (pass_sfx <= s_target)) {
767 /* save the previous value of next_sfx */
768 if (pass_sfx != next_sfx) save_sfx = next_sfx;
770 next_sfx = pass_sfx;
772 the_next_pass = (passes)(the_next_pass + 1);
773 if (the_next_pass >= last_pass) break;
776 /* go back to saved suffix if found a pass that will generate next_sfx */
777 if ((the_next_pass < last_pass) &&
778 (passtbl[the_next_pass].suffix == next_sfx)) {
779 next_sfx = save_sfx;
782 /* replace the suffix if this pass produces a file with a temporary suffix
783 or if this is the last pass before the target suffix is reached */
784 if ((outfile_sfx == s_tmp) || (next_sfx == s_target)) {
785 outfile_sfx = next_sfx;
788 String suffix_name = suffix_to_string(outfile_sfx);
790 String base_used;
791 if (has_base) {
792 base_used = base;
793 } else {
794 if (outfilename != NULL) {
795 base_used = base_from_name(*outfilename);
796 } else {
797 /* default name is "a"; this causes the linker to produce
798 "a.out" */
799 base_used += "a";
803 String the_name;
804 boolean will_erase;
805 if (outfile_sfx == s_target) {
806 will_erase = FALSE;
807 if (!out_to_stdout) {
808 if (parallel_passes) {
809 if (outfilename != NULL) {
810 error(0, "Multiple output files: \"-o %s\" ignored.\n",
811 outfilename->string());
813 the_name = base_used;
814 the_name += '.';
815 the_name += suffix_name;
816 } else {
817 if (outfilename != NULL) {
818 the_name = *outfilename;
819 } else {
820 the_name = base_used;
821 the_name += '.';
822 the_name += suffix_name;
826 } else {
827 will_erase = ((keepflag == 0) ||
828 ((keepflag == 1) && (outfile_sfx == s_tmp)));
829 if (will_erase) {
830 the_name = new_temp_base();
831 } else {
832 the_name = base_used;
834 the_name += '.';
835 the_name += suffix_name;
838 return new filename(the_name, base_used, the_next_pass, will_erase);
843 /* This returns everything before the extension of a file name. */
845 String base_from_name(String the_name)
847 char *name_string = the_name.string();
848 char *extension = NULL;
849 char *buffer;
851 char *follow = name_string;
852 while (*follow != 0) {
853 if (*follow == '/')
854 extension = NULL;
855 else if (*follow == '.')
856 extension = follow;
857 ++follow;
859 if (extension == NULL)
860 extension = follow;
862 buffer = new char[(extension - name_string) + 1];
863 strncpy(buffer, name_string, (extension - name_string));
864 buffer[extension - name_string] = 0;
865 String return_value(buffer);
866 delete buffer;
867 return return_value;
872 /* If the suffix is a temporary, this returns a new unique suffix.
873 Otherwise, it returns the string for that suffix. */
875 String suffix_to_string(Suffix the_suffix)
877 static long int suffix_number = 0;
878 String return_value;
880 if (the_suffix == s_tmp) {
881 char buf[32];
883 sprintf(buf, "%ld", suffix_number++);
884 return_value += buf;
885 } else {
886 return_value += suffixes[the_suffix];
888 return return_value;
893 /* Run a pass that requires all of the input files at once. The input
894 files are removed from the infiles list, and the output file (as well as
895 any of the input files which will be used in future passes) is added to
896 the outlist. */
898 void run_many_in_pass(passes this_pass)
900 String outfile_string;
901 String in_out_string;
903 /* Search through the output files and add any files that should be
904 processed by this pass to the input files list. */
906 filelistiter fiter(outfiles);
907 while (!fiter.is_empty()) {
908 filename *test_file = (filename *)(fiter.step());
909 if (test_file->start_pass <= this_pass) {
910 transfer_filename(test_file, outfiles, infiles);
914 if (passtbl[this_pass].file_counts == MANY_IN_ONE_OUT) {
915 String dummy;
916 filename *outfile = choose_output_file(this_pass, dummy, FALSE);
917 outfiles->append(outfile);
918 outfile_string += outfile->name;
919 in_out_string += list_names(infiles);
920 in_out_string += ' ';
921 in_out_string += outfile->name;
922 } else {
923 parallel_passes = contains_multiple_files(infiles);
924 filelistiter fiter(infiles);
925 while (!fiter.is_empty()) {
926 filename *one_file = (filename *)(fiter.step());
927 filename *outfile =
928 choose_output_file(this_pass, one_file->basename, TRUE);
929 outfiles->append(outfile);
930 outfile_string += outfile->name;
931 in_out_string += one_file->name;
932 in_out_string += ' ';
933 in_out_string += outfile->name;
934 if (!fiter.is_empty()) {
935 outfile_string += ' ';
936 in_out_string += ' ';
939 parallel_passes = FALSE;
942 if (!uses_specified_output(this_pass)) {
943 error(0, "Pass %s cannot have multiple input files.\n"
944 "(the output file is not specified)",
945 passtbl[this_pass].passname);
946 cleanup();
949 String infiles_string = list_names(infiles);
950 run_pass_basic(this_pass, infiles_string.string(),
951 outfile_string.string(), in_out_string.string());
956 /* This function returns whether or not a particular pass uses the output
957 name pscc gives it or not. It bases this decision on whether or not the
958 %o flag is included in that pass's format string. */
960 boolean uses_specified_output(passes p)
962 pass *pp = &passtbl[p];
964 char *fmt_index = pp->cmdfmt;
965 do {
966 if (*fmt_index == '%') {
967 fmt_index++;
968 if ((*fmt_index == 'o') || (*fmt_index == 'a')) {
969 return TRUE;
972 } while (*fmt_index++ != '\0');
974 return FALSE;
979 void run_pass_one_in_one_out(passes p, char *infile, char *outfile)
981 /* if the command format string doesn't include a %o field, then the
982 * output file name cannot be specified; this flag is used to detect
983 * the presence of the %o flag in the format string. */
984 boolean used_outfile = uses_specified_output(p);
986 String in_out_string;
987 in_out_string += infile;
988 in_out_string += ' ';
989 in_out_string += outfile;
990 run_pass_basic(p, infile, outfile, in_out_string.string());
992 if (!used_outfile) {
994 /* Find the name of the output file. This assumes that if the output
995 * filename cannot be specified that it defaults to just changing the
996 * suffix of the input file. We just replace the suffix of the input
997 * file. */
999 /* the pass must have a named suffix */
1000 Suffix pass_sfx = passtbl[p].suffix;
1001 if (pass_sfx == s_tmp) {
1002 error(0, "Pass %s must be given a named suffix.\n"
1003 "(the output file is not specified)", passtbl[p].passname);
1004 cleanup();
1007 /* if the command didn't put the output in the right file,
1008 * we have to move it there. */
1009 String infile_name(infile);
1010 String actual_outfile = base_from_name(infile_name);
1011 actual_outfile += '.';
1012 actual_outfile += suffix_to_string(pass_sfx);
1013 move_file(actual_outfile.string(), outfile);
1019 /* Run a pass over the specified input file(s) and put the output in the
1020 specified file. The format for the command line, as well as the flags
1021 and pass_options, are read from the global pass table. */
1023 void run_pass_basic(passes p, char *infile_names, char *outfiles,
1024 char *in_out_files)
1026 String command_line;
1027 int flag_index = 0;
1028 pass *pp = &passtbl[p];
1030 if (timing) {
1031 command_line += TIMECMD;
1032 command_line += " ";
1035 command_line += select_path(pp->cmdname, pp->dir);
1036 command_line += "/";
1037 command_line += pp->cmdname;
1038 command_line += " ";
1040 /* Now we have to parse the command format string.... */
1041 char *fmt_index = pp->cmdfmt;
1042 do {
1043 if (*fmt_index != '%') {
1044 command_line += *fmt_index;
1045 } else {
1046 fmt_index++;
1047 switch (*fmt_index) {
1048 case 'i':
1049 command_line += infile_names;
1050 break;
1052 case 'o':
1053 command_line += outfiles;
1054 break;
1056 case 'a':
1057 command_line += in_out_files;
1058 break;
1060 case 'f':
1061 if (pp->flags[flag_index]) {
1062 command_line += *pp->flags[flag_index];
1064 if (++flag_index > MAX_FLAGS) {
1065 error(0, "Too many flags in command format for %s\n",
1066 pp->passname);
1067 cleanup();
1069 break;
1071 case 'p':
1072 command_line += *pp->pass_options;
1073 break;
1075 default:
1076 error(0,"Unrecognized flag %%%c in command format for %s\n",
1077 *fmt_index, pp->passname);
1078 cleanup();
1081 } while (*fmt_index++ != '\0');
1083 if (verbose)
1084 fprintf(stderr, "%s: %s\n", pp->passname, command_line.string());
1086 if (!nullflag) {
1087 int stat = execute_cmd(command_line.string());
1088 if (stat)
1089 cleanup();
1091 if (checksuif && (pp->out_file_format == OUTPUT_SUIF)) {
1092 String check_command_line;
1094 if (timing) {
1095 check_command_line += TIMECMD;
1096 check_command_line += " ";
1099 check_command_line += select_path("checksuif", NULL);
1100 check_command_line += "/";
1101 check_command_line += "checksuif ";
1102 if (checkfail)
1103 check_command_line += "-fail ";
1104 else
1105 check_command_line += "-warn ";
1106 check_command_line += outfiles;
1107 stat = execute_cmd(check_command_line.string());
1108 if (stat)
1109 cleanup();
1113 /* delete any temporary files */
1114 erase_files(infiles);
1119 /* Find a directory containing the executable cmd. The optional directory
1120 argument is checked first if it is provided. Otherwise, the directories
1121 in the default_path array are checked. If none of the directories
1122 contain the executable, then an error is signaled and the program exits. */
1124 const char * select_path(const char *cmd, const char *dir)
1126 String path, *p;
1128 /* first try the specified directory */
1129 if (dir) {
1130 path += dir;
1131 path += "/";
1132 path += cmd;
1133 if (!access(path.string(), X_OK)) return dir;
1136 /* otherwise, try the default directories */
1137 int path_num = 0;
1138 while ((p = default_path[path_num++])) {
1139 path.clear();
1140 path += p->string();
1141 path += "/";
1142 path += cmd;
1143 if (!access(path.string(), X_OK)) return p->string();
1146 /* none of the paths worked */
1147 error(0, "Cannot find an executable file for \"%s\".\n"
1148 "Check the value of your SUIFPATH variable.", cmd);
1149 cleanup();
1150 return NULL;
1155 /* Execute a command. First, we have to break the command string into
1156 words and store the individual words into an array. Next, we fork off
1157 a new process and exec the specified command, and finally, we check
1158 the return status of the child process. Note: I tried using the system()
1159 call to execute commands via the shell. This avoids having to split
1160 the command into words and allows use of wildcards and other shell
1161 features. Unfortunately, the return status did not indicate whether
1162 the child process had been killed, so I went back to directly executing
1163 the commands. */
1165 /* make it all look like system V */
1166 #ifndef WIFSTOPPED
1167 #define WIFSTOPPED WIFSIGNALED
1168 #define WSTOPSIG WIFTEMSIG
1169 #endif
1171 int execute_cmd(char *cmd)
1173 pid_t pid, w;
1174 static char *cmd_items[100];
1176 /* break the command into words */
1177 char *start = cmd;
1178 char *end = cmd;
1179 int i = 0;
1181 while (TRUE) {
1183 /* skip any leading blanks */
1184 while (*start == ' ') start++;
1185 if (*start == '\0') break;
1187 /* find the end of the word */
1188 boolean inside_quote = FALSE;
1189 boolean inside_dblquote = FALSE;
1190 end = start;
1191 while (TRUE) {
1192 if (*end == '\0') break;
1193 if (*end == '\'') {
1194 if (inside_quote) {
1195 inside_quote = FALSE;
1196 } else if (!inside_dblquote) {
1197 inside_quote = TRUE;
1200 if (*end == '"') {
1201 if (inside_dblquote) {
1202 inside_dblquote = FALSE;
1203 } else if (!inside_quote) {
1204 inside_dblquote = TRUE;
1207 if (!inside_quote && !inside_dblquote && (*end == ' ')) break;
1208 end++;
1211 /* store the word into the array */
1212 cmd_items[i] = new char[(end - start) + 1];
1213 strncpy(cmd_items[i], start, (end - start));
1214 cmd_items[i][(end - start)] = '\0';
1215 assert(++i < 100);
1217 if (*end == '\0') break;
1218 start = end + 1;
1220 cmd_items[i] = NULL;
1222 if ((pid = fork()) == -1) syscallerr("fork");
1224 /* check if this is now the child process */
1225 if (pid == 0) {
1226 /* execute the command */
1227 execv(cmd_items[0], cmd_items);
1228 /* should never reach this point */
1229 syscallerr("execv");
1231 /* otherwise, this must be the parent process */
1233 /* delete the command items */
1234 for (i = i - 1; i >= 0; i--) {
1235 delete[] cmd_items[i];
1238 /* wait for the child to finish */
1239 int retcode = 0;
1240 while ((w = wait(&retcode)) != pid) {
1241 if (w == -1) syscallerr("wait");
1242 /* I suppose w == 0 and I'm about to be interrupted. Not sure. */
1245 if (WIFEXITED(retcode)) {
1246 /* normal exit */
1247 int rc = WEXITSTATUS(retcode);
1248 if (rc) {
1249 fprintf(stderr, "%s\nFAILED (exit status 0x%x)\n", cmd, rc);
1250 return rc;
1253 } else if (WIFSTOPPED(retcode)) {
1254 /* child process caught a signal */
1255 int sig = WSTOPSIG(retcode);
1256 fprintf(stderr, "%s\nFAILED (caught signal 0x%x)\n", cmd, sig);
1257 return -1;
1259 } else {
1260 /* I have no idea, time to panic */
1261 fprintf(stderr, "%s\nFAILED (wait code 0x%x)\n", cmd, retcode);
1262 return -2;
1265 return 0;
1270 /* Return a new unique temporary file base name. */
1272 String new_temp_base()
1274 char buf[32];
1275 static long int base_number = 0;
1277 String f(tmpdir);
1278 f += "/";
1279 f += tmpbase;
1280 f += "_";
1281 sprintf(buf, "%ld", base_number++);
1282 f += buf;
1283 return f;
1288 /* Return the suffix of the given filename */
1290 Suffix getsfx(char *s)
1292 int c;
1293 char *sfx = NULL;
1294 char *fs = s;
1296 /* scan through the string and record the position of the last dot */
1297 while ((c = *s++)) {
1298 if (c == '.') sfx = s;
1300 if (!sfx) {
1301 error(0, "missing suffix, file \"%s\"", fs);
1302 cleanup();
1305 /* compare the suffix to all of those in the suffixes[] array */
1306 int r;
1307 for (r = s_F; r < s_tmp; r++) {
1308 if (!strcmp(suffixes[r], sfx)) break;
1311 return (Suffix)r;
1316 /* If the filenames are not identical, move the first file to the second. */
1318 void move_file(char *f1, char *f2)
1320 if (nullflag)
1321 return;
1323 if (strcmp(f1, f2)) {
1324 String mvcmd(MVCMD);
1325 mvcmd += " ";
1326 mvcmd += f1;
1327 mvcmd += " ";
1328 mvcmd += f2;
1329 int stat = execute_cmd(mvcmd.string());
1330 if (stat) {
1331 error(0, "Unable to move temporary file \"%s\" to \"%s\".", f1, f2);
1332 cleanup();
1339 /* The interrupt() function is called when we catch a signal. This gives
1340 us a chance to remove any temporary output files before exiting. */
1342 void interrupt(int)
1344 cleanup();
1349 void bad_option(char *s)
1351 error(1, "unrecognized flag: %s", s);
1356 void usage()
1358 fprintf(stderr, "Usage: %s [flags] file ...\n", cmdnam);
1359 exit(-1);
1364 void error(int rc, char *msg, ...)
1366 va_list ap;
1367 va_start(ap, msg);
1369 fprintf(stderr, "%s: ", cmdnam);
1370 vfprintf(stderr, msg, ap);
1372 putc('\n', stderr);
1373 if (rc != 0) exit(rc);
1378 /* Get rid of temporary files after detecting an error of some sort. */
1380 void cleanup()
1382 if (keepflag > 1) exit(-1);
1384 erase_files(infiles);
1385 erase_files(outfiles);
1386 erase_files(tmpfiles);
1387 if (moving_file != NULL)
1388 unlink(moving_file);
1390 exit(-1);
1395 /* Remove any temporary files on the specified list (also removes all of
1396 the filenames from the list and deletes them). */
1398 void erase_files(filelist *flist)
1400 filename *f;
1402 /* delete any temporary files */
1403 while (!flist->is_empty()) {
1404 f = (filename *)(flist->pop());
1405 /* try to remove the file -- don't worry if it fails */
1406 if (f->erase) unlink(f->name.string());
1407 delete f;
1413 void syscallerr(char *s)
1415 fprintf(stderr, "system call error: %s\n", s);
1416 exit(-1);
1421 /* Safely transfer a file from one list to another, so it is always on at
1422 least one list or in the variable moving_file. That way whenever we catch
1423 a signal, every temporary file will be recorded in at least one place, so
1424 it will be properly removed. */
1426 void transfer_filename(filename *to_transfer, filelist *from, filelist *to)
1428 moving_file = to_transfer->name.string();
1429 from->remove(to_transfer);
1430 to->append(to_transfer);
1431 moving_file = NULL;
1436 filename::filename(String the_name, String the_basename,
1437 passes the_start_pass, boolean will_erase)
1439 name = the_name;
1440 basename = the_basename;
1441 start_pass = the_start_pass;
1442 erase = will_erase;