3 /* Copyright (c) 1994 Stanford University
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 */
29 // for access() - since Win32 does not have UNIX-style execute permissions
30 // just check for the existence of the executable
44 #define SUIF_NEED_RLIMIT
47 #include <machine_dependent.h>
48 #undef SUIF_NEED_RLIMIT
49 #endif /* RLIMIT_STACK */
52 main(int argc
, char **argv
)
54 start_suif(argc
, argv
);
57 int interesting_files
= 0;
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)
71 /* unlimit the stack */
73 assert(getrlimit(RLIMIT_STACK
, &rl
) == 0);
74 rl
.rlim_cur
= rl
.rlim_max
;
75 setrlimit(RLIMIT_STACK
, &rl
);
78 /* initialize the command table */
79 for (p
= 0; p
< last_pass
; 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
;
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 */
96 /* default target machine: unknown */
97 target_machine
= NULL
;
99 /* save the name of this program (usually "scc") */
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
);
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
);
116 /* read command line options */
120 if (argv
[0][0] == '-') {
122 argv
= read_flag(argv
);
126 /* if not a flag, this must be an input file */
128 Suffix s
= getsfx(argv
[0]);
130 /* special case: pass .a files to the linker */
131 *passtbl
[LD
].pass_options
+= argv
[0];
132 *passtbl
[LD
].pass_options
+= " ";
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
);
146 const char *base_cmd_name
;
149 base_cmd_name
= "suif";
153 base_cmd_name
= strrchr(cmdnam
, '/');
154 if (base_cmd_name
== NULL
)
155 base_cmd_name
= cmdnam
;
159 fprintf(stderr
, "%s %s%s\n", base_cmd_name
, prog_ver_string
,
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
;
170 *passtbl
[BACKEND_CC
].pass_options
+= " -S";
173 passtbl
[LD
].cmdname
= "cc";
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 */
184 const char *c
= suif_path
;
186 if (*c
++ == ':') num_paths
++;
188 default_path
= new String
*[num_paths
];
190 /* copy the paths into the default path array */
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 */
204 } while (*c
++ != '\0');
206 default_path
[path_num
] = NULL
;
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
);
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
);
227 *lib_tmp
+= target_machine
;
229 suif_lib
= lib_tmp
->string();
231 /* set up the pass table */
232 for (p
= 0; p
< last_pass
; 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.... */
247 extern void string_from_file(const char *file_name
, String
*the_string
)
249 FILE *fp
= fopen(file_name
, "r");
252 int inchar
= fgetc(fp
);
253 while (inchar
!= EOF
)
255 if ((inchar
== '\n') || (inchar
== 0))
258 *the_string
+= (char)inchar
;
264 char ** read_flag(char **argv
)
268 switch (argv
[0][1]) {
271 if (!strcmp(argv
[0], "-automatic")) {
272 automatic_flag
= TRUE
;
278 if (!strcmp(argv
[0], "-checkwarn")) {
282 if (!strcmp(argv
[0], "-checkfail")) {
287 if (!strcmp(argv
[0], "-cc")) {
289 error(1, "usage: -cc <back-end C compiler name>");
290 passtbl
[BACKEND_CC
].cmdname
= argv
[1];
291 passtbl
[LD
].cmdname
= argv
[1];
296 if (!strcmp(argv
[0], "-c")) {
306 if (!strcmp(argv
[0], "-f2c")) {
313 if (!strcmp(argv
[0], "-g")) {
320 if (!strcmp(argv
[0], "-keep")) {
322 if (keepflag
> 1) tmpdir
= ".";
325 if (!strcmp(argv
[0], "-k")) {
332 *passtbl
[LD
].pass_options
+= argv
[0];
333 *passtbl
[LD
].pass_options
+= " ";
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
;
348 if (!strcmp(argv
[0], "-noreassoc")) {
349 reassociate_arrays
= FALSE
;
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
+= " ";
365 if (!strcmp(argv
[0], "-o")) {
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]);
380 if (!strcmp(argv
[0], "-reassoc")) {
381 reassociate_arrays
= TRUE
;
387 if (!strcmp(argv
[0], "-s2c")) {
390 if (!strcmp(argv
[0], "-sf2c")) {
394 if (!strcmp(argv
[0], "-static")) {
395 automatic_flag
= FALSE
;
398 if (!strcmp(argv
[0], "-show")) {
402 if (!strcmp(argv
[0], "-suif-link")) {
403 option_linksuif
= TRUE
;
409 tmpdir
= &argv
[0][2];
410 if (argv
[0][2] == 0) tmpdir
= ".";
414 if (!strcmp(argv
[0], "-version")) {
418 if (!strcmp(argv
[0], "-v")) {
425 if (!strcmp(argv
[0], "-w")) {
432 if (!strcmp(argv
[0], "-yes")) {
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
;
444 suif_bin
= &argv
[0][2];
445 if (argv
[0][2] == '\0')
446 error(1, "usage: -Bprefix");
450 if (!strcmp(argv
[0],"-E")) {
452 out_to_stdout
= TRUE
;
458 if (!strcmp(argv
[0], "-G")) {
459 if (argv
[1] == 0) error(1, "-G must have argument");
461 Gnum
= strtol(argv
[1], &p
, 10);
462 if (*p
) error(1, "-G must have number argument");
464 error(1, "-G must have non-negative argument");
471 *passtbl
[LD
].pass_options
+= argv
[0];
472 *passtbl
[LD
].pass_options
+= " ";
476 if (!strcmp(argv
[0], "-M")) {
478 out_to_stdout
= TRUE
;
485 *passtbl
[SF2C
].pass_options
+= argv
[0];
486 *passtbl
[SF2C
].pass_options
+= " ";
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
+= " ";
496 case 'O': /* run scalar opts */
497 if (!strncmp(argv
[0], "-O", 2)) {
498 if (argv
[0][2] == 0) {
500 } else if ((argv
[0][2] >= '0') && (argv
[0][2] <= '9') &&
502 opt_level
= argv
[0][2] - '0';
512 if (!strcmp(argv
[0], "-S")) {
520 if (!strcmp(argv
[0], "-T")) {
522 } else if (!strcmp(argv
[0], "-Target")) {
524 error(1, "usage: -Target <target machine name>");
525 target_machine
= argv
[1];
534 if (!strcmp(argv
[0], "-V")) {
541 if (!strcmp(argv
[0], "-Wall")) {
542 no_warn_flag
= FALSE
;
548 for (s_target
= s_c
; s_target
<= s_o
; s_target
++) {
549 if (!strcmp(&argv
[0][2], suffixes
[s_target
]))
552 if (s_target
> s_o
) {
553 char buf
[1024], buf2
[10];
554 sprintf(buf
, "bad -. option: %s\nUse one of: ",
556 for (s_target
= s_c
; s_target
<= s_o
; s_target
++) {
557 sprintf(buf2
, "-.%s ", suffixes
[s_target
]);
573 passes
find_pass(const char *name
)
577 for (p
= 0; p
< last_pass
; p
++) {
578 if (!strcmp(name
, passtbl
[p
].passname
)) break;
586 /* Find the first pass to run over the input file, based on the suffix of
589 passes
find_start_pass(const char *infile
)
591 Suffix infile_sfx
, pass_sfx
;
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
) {
609 pass_sfx
= passtbl
[p
].suffix
;
619 passes p
, last_real_sfx
;
620 int next_many_in_pass
;
622 boolean reached_target
= FALSE
;
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;
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
)) {
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())
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
;
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
)
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 */
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
)
748 boolean first
= TRUE
;
750 filelistiter
fiter(files
);
751 while (!fiter
.is_empty()) {
752 filename
*infile
= (filename
*)(fiter
.step());
757 result
+= infile
->name
;
765 filename
*choose_output_file(passes the_pass
, String 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
;
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
)) {
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
);
807 if (outfilename
!= NULL
) {
808 base_used
= base_from_name(*outfilename
);
810 /* default name is "a"; this causes the linker to produce
818 if (outfile_sfx
== s_target
) {
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
;
828 the_name
+= suffix_name
;
830 if (outfilename
!= NULL
) {
831 the_name
= *outfilename
;
833 the_name
= base_used
;
835 the_name
+= suffix_name
;
840 will_erase
= ((keepflag
== 0) ||
841 ((keepflag
== 1) && (outfile_sfx
== s_tmp
)));
843 the_name
= new_temp_base();
845 the_name
= base_used
;
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
;
864 const char *follow
= name_string
;
865 while (*follow
!= 0) {
868 else if (*follow
== '.')
872 if (extension
== NULL
)
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
);
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;
893 if (the_suffix
== s_tmp
) {
896 sprintf(buf
, "%ld", suffix_number
++);
899 return_value
+= suffixes
[the_suffix
];
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
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
) {
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
;
936 parallel_passes
= contains_multiple_files(infiles
);
937 filelistiter
fiter(infiles
);
938 while (!fiter
.is_empty()) {
939 filename
*one_file
= (filename
*)(fiter
.step());
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
);
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
;
979 if (*fmt_index
== '%') {
981 if ((*fmt_index
== 'o') || (*fmt_index
== 'a')) {
985 } while (*fmt_index
++ != '\0');
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
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
);
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
;
1041 pass
*pp
= &passtbl
[p
];
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
;
1056 if (*fmt_index
!= '%') {
1057 command_line
+= *fmt_index
;
1060 switch (*fmt_index
) {
1062 command_line
+= infile_names
;
1066 command_line
+= outfiles
;
1070 command_line
+= in_out_files
;
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",
1085 command_line
+= *pp
->pass_options
;
1089 error(0,"Unrecognized flag %%%c in command format for %s\n",
1090 *fmt_index
, pp
->passname
);
1094 } while (*fmt_index
++ != '\0');
1097 fprintf(stderr
, "%s: %s\n", pp
->passname
, command_line
.string());
1100 int stat
= execute_cmd(command_line
.string());
1104 if (checksuif
&& (pp
->out_file_format
== OUTPUT_SUIF
)) {
1105 String check_command_line
;
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 ";
1116 check_command_line
+= "-fail ";
1118 check_command_line
+= "-warn ";
1119 check_command_line
+= outfiles
;
1120 stat
= execute_cmd(check_command_line
.string());
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
)
1142 char *exeName
= new char[strlen(cmd
) + 10];
1143 sprintf(exeName
, "%s.exe", cmd
);
1147 /* first try the specified directory */
1152 if (!access(path
.string(), X_OK
)) return dir
;
1155 /* otherwise, try the default directories */
1157 while ((p
= default_path
[path_num
++])) {
1159 path
+= p
->string();
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
);
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
1189 /* make it all look like system V */
1191 #define WIFSTOPPED WIFSIGNALED
1192 #define WSTOPSIG WIFTEMSIG
1195 #define SCC_INIT_CMD_ITEM_BUFFER_SIZE 20
1197 int execute_cmd(const char *cmd
)
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
;
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
;
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
;
1225 if (*end
== '\0') break;
1228 inside_quote
= FALSE
;
1229 } else if (!inside_dblquote
) {
1230 inside_quote
= TRUE
;
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;
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';
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 *));
1254 cmd_items
= new_cmd_items
;
1255 cmd_item_space
*= 2;
1258 if (*end
== '\0') break;
1261 cmd_items
[i
] = NULL
;
1264 if ((pid
= fork()) == -1) syscallerr("fork");
1266 /* check if this is now the child process */
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 */
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
);
1284 fprintf(stderr
, "%s\nFAILED (exit status 0x%x)\n", cmd
, rc
);
1293 /* delete the command items */
1294 for (i
= i
- 1; i
>= 0; i
--) {
1295 delete[] cmd_items
[i
];
1299 /* wait for the child to finish */
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
)) {
1308 int rc
= WEXITSTATUS(retcode
);
1310 fprintf(stderr
, "%s\nFAILED (exit status 0x%x)\n", cmd
, 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
);
1321 /* I have no idea, time to panic */
1322 fprintf(stderr
, "%s\nFAILED (wait code 0x%x)\n", cmd
, retcode
);
1332 /* Return a new unique temporary file base name. */
1334 String
new_temp_base()
1337 static long int base_number
= 0;
1343 sprintf(buf
, "%ld", base_number
++);
1350 /* Return the suffix of the given filename */
1352 Suffix
getsfx(const char *s
)
1355 const char *sfx
= NULL
;
1358 /* scan through the string and record the position of the last dot */
1359 while ((c
= *s
++)) {
1360 if (c
== '.') sfx
= s
;
1363 error(0, "missing suffix, file \"%s\"", fs
);
1367 /* compare the suffix to all of those in the suffixes[] array */
1369 for (r
= s_F
; r
< s_tmp
; r
++) {
1370 if (!strcmp(suffixes
[r
], sfx
)) break;
1378 /* If the filenames are not identical, move the first file to the second. */
1380 void move_file(const char *f1
, const char *f2
)
1385 if (strcmp(f1
, f2
)) {
1386 String
mvcmd(MVCMD
);
1391 int stat
= execute_cmd(mvcmd
.string());
1393 error(0, "Unable to move temporary file \"%s\" to \"%s\".", f1
, f2
);
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. */
1411 void bad_option(const char *s
)
1413 error(1, "unrecognized flag: %s", s
);
1420 fprintf(stderr
, "Usage: %s [flags] file ...\n", cmdnam
);
1426 void error(int rc
, const char *msg
, ...)
1431 fprintf(stderr
, "%s: ", cmdnam
);
1432 vfprintf(stderr
, msg
, ap
);
1435 if (rc
!= 0) exit(rc
);
1440 /* Get rid of temporary files after detecting an error of some sort. */
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
);
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
)
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());
1475 void syscallerr(const char *s
)
1477 fprintf(stderr
, "system call error: %s\n", s
);
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
);
1498 filename::filename(String the_name
, String the_basename
,
1499 passes the_start_pass
, boolean will_erase
)
1502 basename
= the_basename
;
1503 start_pass
= the_start_pass
;