1 /*
2  * bcc.c Version 2001.1
3  *       Complete rewrite because the old one was just too confusing!
4  *
5  *       There are no significant compile time options (MC6809 and CCC
6  *       just change defaults) but you should set LOCALPREFIX.
7  *
8  *       Personality flags are:
9  *
10  *	-Mn	Normal ELKS
11  *	-Md	MSDOS
12  *	-Ms	PC Standalone.
13  *	-Ml	i386 Linux
14  *	-M8	CvW's c386
15  *	-M9	MC6809 with bcc
16  *	-M0	A framework for the -B option.
17  */
18 #include <stdio.h>
19 #ifdef __STDC__
20 #include <limits.h>
21 #include <stdlib.h>
22 #ifndef MSDOS
23 #include <unistd.h>
24 #endif
25 #else
26 #include <malloc.h>
27 #endif
28 #include <string.h>
29 #include <memory.h>
30 #include <fcntl.h>
31 #include <sys/types.h>
32 #include <sys/stat.h>
33 #ifndef MSDOS
34 #include <sys/wait.h>
35 #include <signal.h>
36 #endif
37 #include "version.h"
38 
39 #ifdef MSDOS
40 #define LOCALPREFIX     /linux86
41 #define EXESUF		".exe"
42 #define R_OK	4		/* Test for read permission.  */
43 #define W_OK	2		/* Test for write permission.  */
44 #define X_OK	1		/* Test for execute permission.	 */
45 #define F_OK	0		/* Test for existence.	*/
46 #define DEFARCH 0		/* Default to 8086 code */
47 #include "version.h"
48 #else
49 #define EXESUF
50 #endif
51 
52 #define AS	"as" EXESUF
53 #define LD	"ld" EXESUF
54 #define CPP	"cpp" EXESUF
55 #define CC1	"cc1" EXESUF
56 #define OPT	"opt" EXESUF
57 
58 #define CC1C386 "c386" EXESUF
59 
60 #define AS09	"as09" EXESUF
61 #define LD09	"ld09" EXESUF
62 
63 #define CPPBCC	"bcc-cpp" EXESUF
64 #define CC1BCC	"bcc-cc1" EXESUF
65 #define AS86	"as86" EXESUF
66 #define LD86	"ld86" EXESUF
67 
68 #define GCC	"gcc"
69 #define UNPROTO "unproto" EXESUF
70 #define OPTIM	"copt" EXESUF
71 
72 #if __STDC__ == 1
73 #define P(x)	x
74 #define HASHIT(x) #x
75 #define QUOT(x) HASHIT(x)
76 #else
77 #define P(x)	()
78 /* Well you find something that works! */
79 #define QUOT(x) "x"
80 #endif
81 
82 struct command {
83    char * cmd;
84    char * altcmd;
85    char * fullpath;
86    int  numargs;
87    int  maxargs;
88    char ** arglist;
89 } command = { 0,0,0,0,0,0 };
90 
91 struct file_list {
92    struct file_list * next;
93    char * file;
94    char * oldfile;
95    char * name;
96    int    filetype;	/* Char, notional extention of file. */
97 } * files;
98 
99 struct opt_list {
100    struct opt_list * next;
101    char * opt;
102    int    opttype;	/* Where the option should go */
103 } * options;
104 
105 int opt_v, opt_V, opt_e, opt_x, opt_I, opt_L, opt_W, opt_i,
106     opt_O, opt_M, opt_f
107 #ifndef VERY_SMALL_MEMORY
108 , opt_7
109 #endif
110 ;
111 
112 #ifdef DEFARCH
113 int opt_arch = (DEFARCH != 0);
114 #else
115 int opt_arch = sizeof (char *) >= 4;
116 #endif
117 
118 int do_preproc = 1;	/* c -> i */
119 int do_unproto = 0;	/* i -> i */
120 int do_compile = 1;	/* i -> s */
121 int do_optim   = 0;	/* s -> s */
122 int do_as      = 1;	/* s -> o */
123 int do_link    = 1;	/* o -> done */
124 char * executable_name = 0;
125 
126 int file_count = 0;
127 int dyn_count = 0;
128 int error_count = 0;
129 char * progname = "C";
130 #ifdef MSDOS
131 char * tmpdir = "";
132 #else
133 char * tmpdir = "/tmp/";
134 #endif
135 
136 int main P((int argc, char **argv));
137 
138 void add_prefix P((char * path));
139 void build_prefix P((char * path1, char * path2, char * path3));
140 void run_aspreproc P((struct file_list * file));
141 void run_preproc P((struct file_list * file));
142 void run_unproto P((struct file_list * file));
143 void run_compile P((struct file_list * file));
144 void run_optim P((struct file_list * file));
145 void run_as P((struct file_list * file));
146 void run_link P((void));
147 void command_reset P((void));
148 void command_opt P((char * option));
149 void command_arch P((void));
150 void command_opts P((int opykey));
151 void newfilename P((struct file_list * file, int last_stage, int new_extn, int use_o));
152 void run_unlink P((void));
153 void validate_link_opt P((char * option));
154 void validate_link_opts P((void));
155 void append_file P((char * filename, int ftype));
156 void append_option P((char * option, int otype));
157 void prepend_option P((char * option, int otype));
158 char * build_libpath P((char * opt, char * str, char * suffix));
159 void * xalloc P((int size));
160 void Usage P((void));
161 void fatal P((char * why));
162 char * copystr P((char * str));
163 char * catstr P((char * str, char * str2));
164 void reset_prefix_path P((void));
165 void run_command P((struct file_list * file));
166 void getargs P((int argc, char ** argv));
167 
168 char * prefix_path = "";
169 
170 #ifdef LOCALPREFIX
171 char * localprefix  = QUOT(LOCALPREFIX);
172 #else
173 char * localprefix  = "/";
174 #endif
175 
176 /* These paths are NATIVE install paths, change others below */
177 char * default_include = "/usr/include";
178 char * optim_rules     = "/lib";
179 #ifdef LIBDIR
180 char * default_libdir  = QUOT(LIBDIR);
181 #else
182 char * default_libdir  = "/lib";
183 #endif
184 char * libdir_suffix   = "";
185 
186 char devnull[] = "/dev/null";
187 char * exec_prefixs[16] = {
188    0		/* Last chance is contents of $PATH */
189 };
190 
191 char * libc = "-lc";
192 
193 int
main(argc,argv)194 main(argc, argv)
195 int argc;
196 char ** argv;
197 {
198    struct file_list * next_file;
199    char * temp;
200 
201    progname = argv[0];
202    if ((temp = getenv("BCC_PREFIX")) != 0 )
203       localprefix = copystr(temp);
204 
205    getargs(argc, argv);
206    validate_link_opts();
207 
208    reset_prefix_path();
209 
210    if (!*localprefix || !localprefix[1]) {
211 
212       if (*localprefix == '/') {
213 	 /* Paths for full NATIVE install "-M/" */
214 	 build_prefix(default_libdir, libdir_suffix, "");
215 	 build_prefix(default_libdir, "", "");
216 
217 	 default_include = build_libpath("-I", "/usr/include", "");
218 	 default_libdir  = build_libpath("-L", default_libdir, libdir_suffix);
219 	 optim_rules     = build_libpath("-d", optim_rules, libdir_suffix);
220 #if 0
221       } else if (*localprefix == '+') {
222 	 /* Paths for a special */
223 #endif
224       } else {
225 	 /* Relative paths to a build dir "-M-" */
226 	 build_prefix("/lib", libdir_suffix, "");
227 	 build_prefix("/lib", "", "");
228 
229 	 default_include = build_libpath("-I", "/include", "");
230 	 default_libdir  = build_libpath("-L", "/lib", libdir_suffix);
231 	 optim_rules     = build_libpath("-d", "/lib", libdir_suffix);
232       }
233 
234    } else {
235       /* Relative paths to normal PREFIX directory */
236       default_include = build_libpath("-I", "/lib/bcc/include", "");
237       default_libdir  = build_libpath("-L", "/lib/bcc", libdir_suffix);
238       optim_rules     = build_libpath("-d", "/lib/bcc", libdir_suffix);
239 
240       build_prefix("/lib/bcc", libdir_suffix, "");
241       build_prefix("/lib/bcc", "", "");
242    }
243 
244    build_prefix("/bin", "", "");
245 #ifdef BINDIR
246    add_prefix(QUOT(BINDIR) "/");
247 #endif
248 
249    if (opt_v>1) { command.cmd = ""; command_reset(); }
250 
251    for(next_file = files; next_file && !error_count; next_file = next_file->next)
252    {
253       if (next_file->filetype == 'o') continue;
254 
255       if (opt_V)
256          fprintf(stderr, "%s:\n", next_file->file);
257 
258       /* Assembler that's not to be optimised. */
259       if (do_preproc && next_file->filetype == 'x') run_aspreproc(next_file);
260       if (do_preproc && next_file->filetype == 'S') run_aspreproc(next_file);
261       if (do_as      && next_file->filetype == 's') run_as(next_file);
262 
263       /* C source */
264       if (do_preproc && next_file->filetype == 'c') run_preproc(next_file);
265       if (do_unproto && do_compile && next_file->filetype == 'i')
266 	                                            run_unproto(next_file);
267       if (do_compile && next_file->filetype == 'i') run_compile(next_file);
268       if (do_optim   && next_file->filetype == 's') run_optim(next_file);
269       if (do_as      && next_file->filetype == 's') run_as(next_file);
270    }
271 
272    if (do_link && !error_count)
273       run_link();
274 
275    run_unlink();
276    exit(error_count>0);
277 }
278 
279 char *
copystr(str)280 copystr(str)
281 char * str;
282 {
283    return strcpy(xalloc(strlen(str)+1), str);
284 }
285 
286 char *
catstr(str,str2)287 catstr(str, str2)
288 char * str, * str2;
289 {
290    return strcat(strcpy(xalloc(strlen(str)+strlen(str2)+1), str), str2);
291 }
292 
293 void
run_aspreproc(file)294 run_aspreproc(file)
295 struct file_list * file;
296 {
297    static char cc1bcc[] = CC1BCC;
298 
299    if (opt_arch<5) {
300       if (opt_e)
301 	 command.cmd = cc1bcc;
302       else {
303 	 command.cmd = CPPBCC;
304 	 command.altcmd = cc1bcc;
305       }
306    } else
307       command.cmd = CPP;
308    command_reset();
309    newfilename(file, (!do_as && !do_optim), (do_compile?'s':'i'), (opt_arch<5));
310    if (opt_arch<5 && command.cmd == cc1bcc)
311       command_opt("-E");
312    else if (opt_arch<5 && do_unproto)
313       command_opt("-A");
314    command_opts('p');
315    command_opt("-D__ASSEMBLER__");
316 
317    command_arch();
318    run_command(file);
319 }
320 
321 void
run_preproc(file)322 run_preproc(file)
323 struct file_list * file;
324 {
325    int last_stage = 0;
326    int combined_cpp;
327    static char cc1bcc[] = CC1BCC;
328 
329    if (opt_arch<5) {
330       if (opt_e)
331 	 command.cmd = cc1bcc;
332       else {
333 	 command.cmd = CPPBCC;
334 	 command.altcmd = cc1bcc;
335       }
336    } else
337       command.cmd = CPP;
338    command_reset();
339 
340    combined_cpp = (command.cmd == cc1bcc &&
341 	           opt_arch != 3 &&
342 		   opt_e < 2 &&
343 	           !do_unproto &&
344 		   do_compile);
345 
346    if (combined_cpp && !do_optim && !do_as )    last_stage =1;
347    if (!combined_cpp && !do_compile ) 		last_stage =1;
348 
349    newfilename(file, last_stage, (combined_cpp?'s':'i'), (opt_arch<5));
350 
351    if (!combined_cpp && opt_arch<5) {
352       if (command.cmd == cc1bcc)
353 	 command_opt("-E");
354       else if (do_unproto)
355 	 command_opt("-A");
356    }
357 
358    command_opts('p');
359    command_opts('C');
360    if (combined_cpp)
361    {
362       if (opt_arch<5 && !do_as)
363 	 command_opt("-t");
364       command_opts('c');
365    }
366 
367    if (!opt_I)
368       command_opt(default_include);
369 
370    command_arch();
371 
372    run_command(file);
373 }
374 
375 void
run_unproto(file)376 run_unproto(file)
377 struct file_list * file;
378 {
379    command.cmd = UNPROTO;
380    command_reset();
381    newfilename(file, !do_compile, 'i', 0);
382    command_opts('u');
383 
384    run_command(file);
385 }
386 
387 void
run_compile(file)388 run_compile(file)
389 struct file_list * file;
390 {
391    if (opt_arch == 3)   command.cmd = CC1C386;
392    else if (opt_arch<5) command.cmd = CC1BCC;
393    else                 command.cmd = CC1;
394    command_reset();
395    newfilename(file, !(do_optim || do_as), 's', (opt_arch != 3 && opt_arch<5));
396 
397    if (opt_arch<5 && !do_as)
398       command_opt("-t");
399 #ifndef VERY_SMALL_MEMORY
400    if (opt_7)
401        command_opt("-7");
402 #endif
403 
404    command_opts('c');
405    command_opts('C');
406 
407    command_arch();
408 
409    run_command(file);
410 }
411 
412 void
run_optim(file)413 run_optim(file)
414 struct file_list * file;
415 {
416    char buf[32];
417    if (opt_arch<5) command.cmd = OPTIM;
418    else            command.cmd = OPT;
419    command_reset();
420    newfilename(file, !do_as, 's', 1);
421    command_opt("-c!");
422    if (opt_O && opt_arch == 0)
423    {
424       sprintf(buf, "-huse16 %c86", opt_O);
425       command_opt(buf);
426    }
427    command_opt(optim_rules);
428 
429    command_opt("rules.start");
430    command_opts('o');
431 
432    if (opt_O) {
433       if (opt_arch == 0)
434 	 sprintf(buf, "rules.%c86", opt_O);
435       else
436 	 sprintf(buf, "rules.lv_%c", opt_O);
437       command_opt(buf);
438    }
439 
440    switch(opt_arch) {
441    case 0: command_opt("rules.86"); break;
442    case 1:
443    case 2: command_opt("rules.i386"); break;
444    case 4: command_opt("rules.6809"); break;
445    default:command_opt("rules.mid"); break;
446    }
447 
448    command_opt("rules.end");
449 
450    run_command(file);
451 }
452 
453 void
run_as(file)454 run_as(file)
455 struct file_list * file;
456 {
457    char * buf;
458    switch(opt_arch)
459    {
460    case 0: case 1: case 2:
461             command.cmd = AS86; break;
462    case 4:  command.cmd = AS09; break;
463    default: command.cmd = AS; break;
464    }
465    command_reset();
466    newfilename(file, (!do_link && opt_arch!=2), 'o', 1);
467    if (opt_arch==3)
468       command_opt("-j");
469    if (opt_arch<5)
470       command_opt("-u");
471    command_opts('a');
472    if (opt_W)
473       command_opt("-w-");
474    else
475       command_opt("-w");
476    command_arch();
477    command_opt("-n");
478    buf = catstr(file->name, ".s");
479    command_opt(buf);
480    free(buf);
481 
482    run_command(file);
483 
484    if (opt_arch == 2)
485    {
486       command.cmd = LD86;
487       command_reset();
488       command_opt("-r");
489       command_opt("-N");
490       newfilename(file, !do_link, 'o', 1);
491       run_command(file);
492    }
493 }
494 
495 void
run_link()496 run_link()
497 {
498    struct file_list * next_file;
499 
500    switch(opt_arch)
501    {
502    case 0: case 1:
503             command.cmd = LD86; break;
504    case 2:  command.cmd = GCC; break;
505    case 4:  command.cmd = LD09; break;
506    default: command.cmd = LD; break;
507    }
508    command_reset();
509    if (executable_name) {
510       command_opt("-o");
511       command_opt(executable_name);
512    }
513 
514    if (opt_arch < 2)
515       command_opt("-y");
516 
517    command_opts('l');
518    if (opt_arch != 2)
519    {
520       if (opt_arch == 0 && !opt_i)
521 	 command_opt("-i");
522 
523       if (!opt_L)
524 	 command_opt(default_libdir);
525       command_arch();
526 
527       if (!opt_x)
528 	 command_opt("-C0");
529    }
530    /* Current Debian compilers only work in with this: */
531    else command_opt("--static");
532 
533    for(next_file = files; next_file; next_file = next_file->next)
534       command_opt(next_file->file);
535 
536    if (opt_arch != 2)
537       command_opt(libc);
538    run_command(0);
539 }
540 
541 void
validate_link_opt(option)542 validate_link_opt(option)
543 char * option;
544 {
545    int err = 0;
546    if (option[0] != '-') return;
547 
548    switch(option[1]) {
549    default:
550 	 err = 1;
551       break;
552    case '0':           /* use 16-bit libraries */
553    case '3':           /* use 32-bit libraries */
554    case 'M':           /* print symbols linked */
555    case 'i':           /* separate I & D output */
556    case 'm':           /* print modules linked */
557    case 's':           /* strip symbols */
558    case 't':           /* trace modules linked */
559    case 'z':           /* unmapped zero page */
560    case 'N':           /* Native format a.out */
561    case 'd':           /* Make a headerless outfile */
562    case 'c':           /* Write header in CP/M-86 format */
563    case 'y':           /* Use a newer symbol table */
564       if (option[2] != 0 && option[2] != '-')
565 	 err = 1;
566       break;
567    case 'C':           /* startfile name */
568    case 'L':           /* library path */
569    case 'O':           /* library file name */
570    case 'T':           /* text base address */
571    case 'D':           /* data base address */
572    case 'H':           /* heap top address */
573    case 'l':           /* library name */
574    case 'o':           /* output file name */
575       break;
576    }
577    if (err) {
578       if (do_link)
579 	 fprintf(stderr, "warning: unknown option %s passed to linker.\n",
580 	                 option);
581       else
582 	 fprintf(stderr, "warning: option %s not recognised.\n", option);
583    } else if (!do_link)
584       fprintf(stderr, "warning: linker option %s unused.\n", option);
585 }
586 
587 void
validate_link_opts()588 validate_link_opts()
589 {
590    struct opt_list * ol;
591    struct file_list * next_file;
592    if (opt_arch>1) return; /* Only check ld86 options */
593 
594    for(ol=options; ol; ol=ol->next)
595       if (ol->opttype == 'l')
596 	 validate_link_opt(ol->opt);
597 
598    for(next_file = files; next_file; next_file = next_file->next)
599       validate_link_opt(next_file->file);
600 
601    if (!do_link) {
602       if (opt_i)
603 	 fprintf(stderr, "warning: linker option -i unused.\n");
604       if (opt_x)
605 	 fprintf(stderr, "warning: linker option -x unused.\n");
606       if (opt_L)
607 	 fprintf(stderr, "warning: linker option -L unused.\n");
608    }
609 }
610 
611 void
command_reset()612 command_reset()
613 {
614 #ifndef MAXPATHLEN
615 #define MAXPATHLEN 1024
616 #endif
617    char buf[MAXPATHLEN];
618    char ** prefix;
619    char * saved_cmd;
620 
621    if (command.arglist)
622    {
623       int i;
624       for(i=0; i<command.maxargs; i++)
625          if(command.arglist[i])
626 	    free(command.arglist[i]);
627       free(command.arglist);
628    }
629    command.arglist = 0;
630    command.numargs = 1;
631    command.maxargs = 20;
632 
633    command.arglist = xalloc(command.maxargs*sizeof(char**));
634    command.arglist[0] = copystr(command.cmd);
635 
636    if (command.fullpath) free(command.fullpath);
637    command.fullpath = 0;
638 
639    /* Search for the exe, nb as this will probably be called from 'make'
640     * there's not much point saving this.
641     */
642    saved_cmd = command.cmd;
643    for(;;)
644    {
645       for(prefix=exec_prefixs; *prefix; prefix++)
646       {
647 	 char * p;
648 	 if (*prefix == devnull) continue;
649 
650 	 p = strchr(*prefix, '~');
651 	 if (!p) strcpy(buf, *prefix);
652 	 else
653 	 {
654 	    memcpy(buf, *prefix, p-*prefix);
655 	    buf[p-*prefix] = 0;
656 
657 	    strcat(buf, prefix_path);
658 	    strcat(buf, p+1);
659 	 }
660 	 strcat(buf, command.cmd);
661 
662 	 if (!*command.cmd)
663 	    fprintf(stderr, "PATH%d=%s\n", prefix-exec_prefixs, buf);
664 	 else if (access(buf, X_OK) == 0)
665 	 {
666 	    command.fullpath = copystr(buf);
667 	    break;
668 	 }
669       }
670       if (command.fullpath || !command.altcmd) break;
671       command.cmd = command.altcmd;
672       command.altcmd = 0;
673    }
674 
675    if (!command.fullpath) {
676       command.cmd = saved_cmd;
677       command.fullpath = copystr(command.cmd);
678    }
679    command.altcmd = 0;
680 }
681 
682 void
command_opt(option)683 command_opt(option)
684 char * option;
685 {
686    if (command.maxargs <= command.numargs+1) {
687       char ** newbuf = xalloc(command.maxargs*2*sizeof(char**));
688       memcpy(newbuf, command.arglist, command.maxargs*sizeof(char**));
689       command.maxargs *= 2;
690       free(command.arglist);
691       command.arglist = newbuf;
692    }
693 
694    command.arglist[command.numargs++] = copystr(option);
695 }
696 
697 void
command_arch()698 command_arch()
699 {
700    if (opt_arch==0) command_opt("-0");
701    if (opt_arch==1) command_opt("-3");
702    if (opt_arch==2) command_opt("-3");
703 }
704 
705 void
command_opts(optkey)706 command_opts(optkey)
707 int optkey;
708 {
709    struct opt_list * ol;
710    for(ol=options; ol; ol=ol->next)
711       if (ol->opttype == optkey)
712 	 command_opt(ol->opt);
713 }
714 
newfilename(file,last_stage,new_extn,use_o)715 void newfilename(file, last_stage, new_extn, use_o)
716 struct file_list * file;
717 int last_stage;
718 int new_extn;
719 int use_o;
720 {
721    file->filetype = new_extn;
722    if (file->oldfile) free(file->oldfile);
723    file->oldfile = file->file;
724    file->file = 0;
725 
726    if (last_stage) {
727       if (executable_name)
728          file->file = copystr(executable_name);
729       else
730       {
731          char buf[4];
732 	 buf[0] = '.';
733 	 buf[1] = file->filetype;
734 	 buf[2] = 0;
735 	 file->file = catstr(file->name, buf);
736       }
737    }
738    else
739    {
740       char buf[16];
741 #ifdef MSDOS
742       sprintf(buf, "$$%05d$", dyn_count++);
743 #else
744       sprintf(buf, "$$%04d%05d", dyn_count++, getpid());
745 #endif
746       file->file = catstr(tmpdir, buf);
747    }
748 
749    command_opt(file->oldfile);
750    /* *.i files go to the stdout */
751    if (last_stage && file->filetype == 'i') return;
752    if (use_o) command_opt("-o");
753    command_opt(file->file);
754 }
755 
756 void
run_unlink()757 run_unlink()
758 {
759    int i;
760    for(i=0; i<dyn_count; i++)
761    {
762       char buf[16];
763       char * p;
764 #ifdef MSDOS
765       sprintf(buf, "$$%05d$", i);
766 #else
767       sprintf(buf, "$$%04d%05d", i, getpid());
768 #endif
769       p = catstr(tmpdir, buf);
770       if (opt_v>1)
771 	 fprintf(stderr, "rm %s\n", p);
772       if (opt_v>2)
773 	 continue;
774       if (unlink(p) < 0)
775       {
776 	 if (error_count==0 || opt_v>1)
777 	    fprintf(stderr, "Error unlinking %s\n", p);
778 	 error_count++;
779       }
780       free(p);
781    }
782 }
783 
784 void
getargs(argc,argv)785 getargs(argc, argv)
786 int argc;
787 char ** argv;
788 {
789    int ar;
790    char * pflag = 0;
791    int control_count = 0;
792    int exe_count = 0;
793 
794    for(ar=1; ar<argc; ) if (argv[ar][0] != '-')
795    {
796 #ifdef __CYGWIN__
797       if ( executable_name == 0 ) {
798 	 char * p = strrchr(argv[ar], '.');
799 	 if (p && p == argv[ar] + strlen(argv[ar]) - 2)
800 	 {
801 	    /* This will actually create a COM file, but windows doesn't
802 	     * care and cygwin will only do PATH searches for EXE files.
803 	     */
804 	    *p=0;
805 	    executable_name = catstr(argv[ar], ".exe");
806 	    *p = '.';
807 	 }
808       }
809 #endif
810       append_file(argv[ar++], 0);
811       file_count++;
812    }
813    else
814    {
815       int opt;
816       int used_arg = 1, inc_ar=0;
817       char * opt_arg;
818 
819       if (argv[ar][2]) opt_arg = argv[ar]+2;
820       else
821       {
822          inc_ar++;
823          if (argv[ar+1]) opt_arg = argv[ar+1];
824 	 else
825 	 {
826 	    inc_ar++;
827 	    opt_arg = "ERROR";
828 	 }
829       }
830       /* Special case -? is different from -?abcdef */
831       if(!pflag && argv[ar][2] == 0) switch(argv[ar][1])
832       {
833       case 'a': case 'L': case 'I': case 'M': case 'O': case 'P': case 'Q':
834 	 pflag = argv[ar]+1;
835 	 used_arg = 0;
836 	 break;
837       }
838       /* Options that need an argument */
839       if(!pflag) switch(argv[ar][1])
840       {
841       case 'a':
842 	 if(strcmp(argv[ar], "-ansi") == 0) {
843 	    do_unproto = 1;
844 #if 0
845 	    /* NOTE I'm setting this to zero, this isn't a _real_ Ansi cpp. */
846 	    prepend_option("-D__STDC__=0", 'p');
847 #else
848 	    prepend_option("-D__STDC__", 'p');
849 #endif
850 	 }
851 	 else
852 	    Usage();
853 	 break;
854 
855       case 't':
856          append_option("-t", 'a');
857 	 /*FALLTHROUGH*/
858       case 'A':
859          append_option(opt_arg, 'a');
860 	 break;
861       case 'C':
862          append_option(opt_arg, 'c');
863 	 break;
864       case 'P':
865 	 append_option(opt_arg, 'p');
866 	 break;
867       case 'X':
868          append_option(opt_arg, 'l');
869 	 break;
870       case 'u':
871 	 append_option(opt_arg, 'u');
872 	 break;
873 
874       case 'L':
875          append_option(argv[ar], 'l');
876 	 break;
877 
878       case 'Q':
879          append_option(argv[ar], 'c');
880 	 break;
881 
882       case 'O':
883          do_optim=1;
884 	 if (!opt_arg[1] && ( opt_arg[0] >= '1' && opt_arg[0] <= '9' ))
885 	    opt_O = opt_arg[0];
886 	 else if (opt_arg[0] == '-')
887 	    append_option(opt_arg, 'o');
888 	 else
889 	 {
890 	    char * p = xalloc(strlen(opt_arg)+8);
891 	    strcpy(p, "rules.");
892 	    strcat(p, opt_arg);
893 	    append_option(p, 'o');
894 	    free(p);
895 	 }
896 	 break;
897 
898       case 'o':
899 	 exe_count++;
900 	 executable_name = opt_arg;
901 	 break;
902 
903       case 'B':
904 	 add_prefix(opt_arg);
905          break;
906 
907       case 'I':
908       case 'D':
909       case 'U':
910          append_option(argv[ar], 'p');
911          break;
912 
913       case 'T':
914 	 tmpdir = catstr(opt_arg, "/");
915          break;
916 
917       case 'M':
918 	 if (opt_arg[0] == '/') {
919 	    localprefix = copystr(opt_arg);
920 	    break;
921 	 }
922 	 if (opt_arg[1]) Usage();
923 	 if (opt_arg[0] == '-') {
924 	    localprefix = "";
925 	    break;
926 	 }
927 	 opt_M    = *opt_arg;
928 	 break;
929 
930       default:
931 	 pflag = argv[ar]+1;
932 	 used_arg = 0;
933 	 break;
934       }
935       /* Singleton flags */
936       if(pflag) switch(opt = *pflag++)
937       {
938       case 'P':
939          append_option("-P", 'p');
940 	 /*FALLTHROUGH*/
941       case 'E':
942 	 control_count++;
943          do_compile = do_link = do_as = 0;
944 	 break;
945       case 'S':
946 	 control_count++;
947          do_as = do_link = 0;
948 	 break;
949       case 'c':
950 	 control_count++;
951          do_link = 0;
952 	 break;
953       case 'O':
954          do_optim=1;
955 	 break;
956 
957       case 'G': opt_M = 'g'; break;
958 
959       case 'v': opt_v++; break;
960       case 'V': opt_V++; break;
961       case 'e': opt_e++; break;
962       case 'x': opt_x++; break;
963       case 'I': opt_I++; break;
964       case 'L': opt_L++; break;
965       case 'i': opt_i++; break;
966       case 'f': opt_f++; break;
967 #ifndef VERY_SMALL_MEMORY
968       case '7': opt_7++; break;
969 #endif
970 
971       case 'W': opt_W++; break;
972 
973       case '0': opt_arch=0; break;
974       case '3': opt_arch=1; break;
975 
976       case 'w': /*IGNORED*/ break;
977       case 'g': /*IGNORED*/ break;
978       case 'p': /*IGNORED*/ break;
979 
980       default:
981 	if (pflag == argv[ar]+2) {
982 	   /* Special; unknown options saved as flags for the linker */
983 	   append_file(argv[ar], 'o');
984 	   pflag = 0;
985 	}
986 	else
987 	   Usage();
988       }
989       if (!pflag || !*pflag)  { ar++; pflag = 0; }
990       if (used_arg && inc_ar) ar++;
991       if (used_arg && inc_ar==2)
992          fatal("Last option requires an argument");
993    }
994 
995    if (control_count>1)
996       fatal("only one option from -E -P -S -c allowed");
997    if (exe_count>1)
998       fatal("only one -o option allowed");
999 
1000    if (file_count==0) Usage();
1001 
1002    if (exe_count && file_count != 1 && !do_link)
1003       fatal("only one input file for each non-linked output");
1004 
1005    add_prefix(getenv("BCC_EXEC_PREFIX"));
1006 
1007 #ifdef MC6809
1008    if (opt_M==0) opt_M = '9';
1009 #endif
1010 #ifdef CCC
1011    if (opt_M==0) opt_M = '8';
1012 #endif
1013 #ifdef MSDOS
1014    if (opt_M==0) opt_M = 'd';
1015 #endif
1016 #ifdef __CYGWIN__
1017    if (opt_M==0) opt_M = 'd';
1018 #endif
1019    if (opt_M==0) opt_M = (opt_arch==1 ?'l':'n');
1020    switch(opt_M)
1021    {
1022    case 'n': 		/* Normal Elks */
1023       prepend_option("-D__unix__", 'p');
1024       prepend_option("-D__ELKS__", 'p');
1025       libc="-lc";
1026       break;
1027    case 'f': 		/* Fast Call Elks */
1028       prepend_option("-D__unix__", 'p');
1029       prepend_option("-D__ELKS__", 'p');
1030       append_option("-c", 'C');
1031       append_option("-f", 'C');
1032       libc="-lc_f";
1033       break;
1034    case 'c': 		/* Caller saves Elks */
1035       prepend_option("-D__unix__", 'p');
1036       prepend_option("-D__ELKS__", 'p');
1037       append_option("-c", 'C');
1038       libc="-lc";
1039       break;
1040    case 's': 		/* Standalone 8086 */
1041       prepend_option("-D__STANDALONE__", 'p');
1042       libc="-lc_s";
1043       break;
1044    case 'd': 		/* DOS COM file */
1045       prepend_option("-D__MSDOS__", 'p');
1046       if (do_link) {
1047 	 libc="-ldos";
1048 	 append_option("-d", 'l');
1049 	 append_option("-T100", 'l');
1050       }
1051       break;
1052    case 'l': 		/* 386 Linux a.out */
1053       opt_arch=1;
1054       prepend_option("-D__unix__", 'p');
1055       prepend_option("-D__linux__", 'p');
1056       if (do_link) {
1057 	 libc="-lc";
1058 	 append_option("-N", 'l');
1059       }
1060       break;
1061    case 'g':		/* 386 Linux object using gcc as linker */
1062       opt_arch = 2;
1063       prepend_option("-D__unix__", 'p');
1064       prepend_option("-D__linux__", 'p');
1065 
1066       /* This is a more traditional libc, it also gives a 20k executable
1067        * for hello world vs. 400k with glibc2 and --static.
1068        * NB: DLL libc no longer seems to work.
1069        */
1070       add_prefix("/usr/bin/i386-uclibc-");
1071       break;
1072    case '8':		/* Use 'c386' program as compiler */
1073       opt_arch = 3;
1074       prepend_option("-D__unix__", 'p');
1075       prepend_option("-D__c386__", 'p');
1076       break;
1077    case '9':		/* 6809 compiler */
1078       opt_arch = 4;
1079       prepend_option("-D__6809__", 'p');
1080       break;
1081    case '0':		/* Plain old Unix V7 style */
1082       opt_arch = 5;
1083       opt_I = 1;
1084       opt_L = 1;
1085       opt_x = 1;
1086       append_option("/lib/crt0.o", 'l');
1087       break;
1088    default:
1089       fatal("Unknown model specifier for -M valid are: n,f,c,s,d,l,g,8,9,0");
1090    }
1091 
1092    if (do_optim)
1093    {
1094       append_option("-O", 'C');
1095       append_option("-O", 'a');
1096    }
1097 
1098    if (opt_arch == 0) {
1099       if (opt_f) {
1100 	 /* append_option("--enable-floats", 'c'); */
1101 	 libc = catstr(libc, "+f");
1102       } else
1103 	 append_option("-D__HAS_NO_FLOATS__", 'p');
1104    }
1105 
1106    if (opt_arch == 1) libdir_suffix = "/i386";
1107    if (opt_arch == 4) libdir_suffix = "/m09";
1108 
1109 #ifdef VERSION
1110    {
1111       char verbuf[64];
1112       sprintf(verbuf, "-D__BCC_VERSION__=0x%02x%02x%02xL",
1113 	    VER_MAJ, VER_MIN, VER_PAT);
1114       append_option(verbuf, 'p');
1115    }
1116 #endif
1117 }
1118 
1119 void
build_prefix(path1,path2,path3)1120 build_prefix(path1, path2, path3)
1121 char * path1, * path2, * path3;
1122 {
1123    char * newstr;
1124    int l;
1125    newstr = xalloc(strlen(path1)+strlen(path2)+strlen(path3)
1126 	  + strlen(prefix_path)+2);
1127 
1128    strcpy(newstr, prefix_path);
1129    strcat(newstr, path1);
1130    strcat(newstr, path2);
1131    strcat(newstr, path3);
1132    l = strlen(newstr);
1133    if (l>1 && newstr[l-1] != '/')
1134       strcat(newstr, "/");
1135 
1136    add_prefix(newstr);
1137 }
1138 
1139 void
add_prefix(path)1140 add_prefix(path)
1141 char * path;
1142 {
1143    char ** p;
1144    if (!path || !*path) return;
1145 
1146    for(  p=exec_prefixs;
1147 	 p<exec_prefixs+(sizeof(exec_prefixs)/sizeof(*p))-1;
1148 	 p++) {
1149 
1150       if( !*p )
1151       {
1152 	 *p = path;
1153 	 return;
1154       }
1155       if (strcmp(*p, path) == 0) return;
1156    }
1157    fatal("Too many -B options");
1158 }
1159 
append_file(filename,ftype)1160 void append_file (filename, ftype)
1161 char * filename;
1162 int ftype;
1163 {
1164    struct file_list * newfile = xalloc(sizeof(struct file_list));
1165    char * s;
1166    char * name;
1167 
1168    newfile->file = copystr(filename);
1169    name = copystr(filename);
1170 
1171    s = strrchr(name, '.');
1172 
1173    if (ftype)
1174    {
1175       newfile->name = copystr(name);
1176       newfile->filetype = ftype;
1177    }
1178    else if (s && s == name + strlen(name) - 2) {
1179       newfile->filetype = s[1];
1180       *s = 0;
1181       newfile->name = copystr(name);
1182    }
1183    else
1184       newfile->name = copystr(name);
1185    free(name);
1186 
1187    if (newfile->filetype == 0)   newfile->filetype = 'o'; /* Objects */
1188 
1189    if (files==0)
1190       files = newfile;
1191    else
1192    {
1193       struct file_list * fptr;
1194       for(fptr=files; fptr->next; fptr=fptr->next);
1195       fptr->next = newfile;
1196    }
1197 }
1198 
1199 void
append_option(option,otype)1200 append_option (option, otype)
1201 char * option;
1202 int otype;
1203 {
1204    struct opt_list * newopt = xalloc(sizeof(struct opt_list));
1205 
1206    newopt->opt = copystr(option);
1207    newopt->opttype = otype;
1208 
1209    if (options==0)
1210       options = newopt;
1211    else
1212    {
1213       struct opt_list * optr;
1214       for(optr=options; optr->next; optr=optr->next);
1215       optr->next = newopt;
1216    }
1217 }
1218 
1219 void
prepend_option(option,otype)1220 prepend_option (option, otype)
1221 char * option;
1222 int otype;
1223 {
1224    struct opt_list * newopt = xalloc(sizeof(struct opt_list));
1225 
1226    newopt->opt = copystr(option);
1227    newopt->opttype = otype;
1228 
1229    newopt->next = options;
1230    options = newopt;
1231 }
1232 
build_libpath(opt,str,suffix)1233 char * build_libpath(opt, str, suffix)
1234 char * opt, * str, * suffix;
1235 {
1236    char * newstr;
1237    newstr = xalloc(strlen(opt)+strlen(str)+strlen(prefix_path)+strlen(suffix)+1);
1238    strcpy(newstr, opt);
1239    strcat(newstr, prefix_path);
1240    strcat(newstr, str);
1241    strcat(newstr, suffix);
1242    return newstr;
1243 }
1244 
1245 void *
xalloc(size)1246 xalloc (size)
1247 int size;
1248 {
1249    void * p = malloc(size);
1250    if (!p) fatal("Out of memory");
1251    memset(p, '\0', size);
1252    return p;
1253 }
1254 
Usage()1255 void Usage()
1256 {
1257 #ifdef VERSION
1258 #ifdef __AS386_16__
1259    if (opt_v)
1260       fprintf(stderr, "%s: version %s (16bit)\n", progname, VERSION);
1261 #else
1262    if (opt_v)
1263       fprintf(stderr, "%s: version %s\n", progname, VERSION);
1264 #endif
1265 #endif
1266    fprintf(stderr,
1267 	 "Usage: %s [-ansi] [-options] [-o output] file [files].\n", progname);
1268    exit(1);
1269 }
1270 
fatal(str)1271 void fatal(str)
1272 char * str;
1273 {
1274    fprintf(stderr, "%s: Fatal error: %s.\n", progname, str);
1275    exit(1);
1276 }
1277 
1278 #ifdef MSDOS
reset_prefix_path()1279 void reset_prefix_path()
1280 {
1281    char *ptr, *temp;
1282 
1283    if (*localprefix && localprefix[1]) {
1284       prefix_path = localprefix;
1285       return;
1286    }
1287 
1288    temp = copystr(progname);
1289    if( (ptr = strrchr(temp, '\\')) != 0
1290          && temp<ptr-4 && strncmp(ptr-4, "\\BIN", 4) == 0 )
1291    {
1292       ptr[-4] = 0;
1293       prefix_path = temp;
1294    }
1295    else
1296       free(temp);
1297 }
1298 #else
1299 
reset_prefix_path()1300 void reset_prefix_path()
1301 {
1302    char *ptr, *temp;
1303 
1304    if (*localprefix && localprefix[1]) {
1305       prefix_path = localprefix;
1306       return;
1307    }
1308 
1309    if ( *localprefix == '/' && !localprefix[1]) {
1310       prefix_path = "";
1311       return;
1312    }
1313 
1314    if( *progname == '/' )
1315       temp = copystr(progname);
1316    else
1317    {
1318       char * s, * d;
1319       ptr = getenv("PATH");
1320       if( ptr==0 || *ptr == 0 ) return;
1321       ptr = copystr(ptr);
1322       temp = copystr("");
1323 
1324       for(d=s=ptr; d && *s; s=d)
1325       {
1326 #ifndef __BCC__
1327 #ifdef PATH_MAX
1328          char buf[PATH_MAX];
1329 #else
1330 #ifdef MAXPATHLEN
1331          char buf[MAXPATHLEN];
1332 #else
1333          char buf[1024];
1334 #endif
1335 #endif
1336 #endif
1337 
1338 	 free(temp);
1339          d=strchr(s, ':');
1340 	 if( d ) *d='\0';
1341 	 temp = xalloc(strlen(progname)+strlen(s)+2);
1342 	 strcpy(temp, s);
1343 	 strcat(temp, "/");
1344 	 strcat(temp, progname);
1345 #ifndef __BCC__
1346          if( realpath(temp, buf) != 0 )
1347 	 {
1348 	    free(temp);
1349 	    temp = copystr(buf);
1350          }
1351 #endif
1352 	 if( access(temp, X_OK) == 0 ) break;
1353 	 d++;
1354       }
1355       if( s == 0 )
1356       {
1357          free(temp);
1358 	 temp = copystr(progname);
1359       }
1360       free(ptr);
1361    }
1362 
1363    if( (ptr = strrchr(temp, '/')) != 0
1364          && temp<ptr-4 && strncmp(ptr-4, "/bin", 4) == 0 )
1365    {
1366       ptr[-4] = 0;
1367       prefix_path = temp;
1368    }
1369    else
1370       free(temp);
1371 }
1372 #endif
1373 
1374 void
run_command(file)1375 run_command(file)
1376 struct file_list * file;
1377 {
1378 #ifdef __BCC__
1379 static char ** minienviron[] = {
1380    "PATH=/bin:/usr/bin",
1381    "SHELL=/bin/sh",
1382    0
1383 };
1384 #endif
1385    int i, status;
1386 #ifndef MSDOS
1387    void *oqsig, *oisig, *otsig, *ocsig;
1388 #endif
1389 
1390 
1391    if (opt_v)
1392    {
1393       fprintf(stderr, "%s", command.fullpath);
1394       for(i=1; command.arglist[i]; i++)
1395 	 fprintf(stderr, " %s", command.arglist[i]);
1396       fprintf(stderr, "\n");
1397       if (opt_v>2) return;
1398    }
1399 
1400 #ifdef MSDOS
1401    status = spawnv(0, command.fullpath, command.arglist);
1402    if (status<0)
1403    {
1404       fprintf(stderr, "Unable to execute %s\n", command.fullpath);
1405    }
1406 #else
1407    oqsig = signal(SIGQUIT, SIG_IGN);
1408    oisig = signal(SIGINT,  SIG_IGN);
1409    otsig = signal(SIGTERM,  SIG_IGN);
1410    ocsig = signal(SIGCHLD, SIG_DFL);
1411 
1412    switch(fork())
1413    {
1414    case -1:
1415       fatal("Forking failure");
1416    case 0:
1417       (void) signal(SIGQUIT, SIG_DFL);
1418       (void) signal(SIGINT,  SIG_DFL);
1419       (void) signal(SIGTERM, SIG_DFL);
1420       (void) signal(SIGCHLD, SIG_DFL);
1421 
1422 #ifdef __BCC__
1423       execve(command.fullpath, command.arglist, minienviron);
1424 #else
1425       if (command.fullpath[0] =='/')
1426 	 execv(command.fullpath, command.arglist);
1427       else
1428 	 execvp(command.fullpath, command.arglist);
1429 #endif
1430       fprintf(stderr, "Unable to execute %s.\n", command.fullpath);
1431       exit(1);
1432    default:
1433       wait(&status);
1434       if (status&0xFF)
1435       {
1436 	 fprintf(stderr, "%s: killed by signal %d\n",
1437 	                 command.fullpath, (status&0xFF));
1438       }
1439    }
1440 
1441    (void) signal(SIGQUIT, oqsig);
1442    (void) signal(SIGINT,  oisig);
1443    (void) signal(SIGTERM, otsig);
1444    (void) signal(SIGCHLD, ocsig);
1445 #endif
1446    if (status)
1447    {
1448       if (file) file->filetype = '~';
1449       error_count++;
1450    }
1451 }
1452 
1453