* added compilers lcc and bcc (linux86)
[mascara-docs.git] / compilers / linux86-0.16.17 / copt / copt.c
blob93923803d51054806ff051d26171577bfe530a11
1 /*
2 **************************************************************************
4 * Utility program to optimize the output of the BCC compiler
6 * Module: copt.c
7 * Purpose: Optimize BCC assembler output
8 * Entries: main
10 * This program is based on an idea from Christopher W. Fraser.
12 **************************************************************************
14 * Copyright (C) 1995,1996,1997 Gero Kuhlmann <gero@gkminix.han.de>
16 * This program is free software; you can redistribute it and/or modify
17 * it under the terms of the GNU General Public License as published by
18 * the Free Software Foundation; either version 2 of the License, or
19 * any later version.
21 * This program is distributed in the hope that it will be useful,
22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 * GNU General Public License for more details.
26 * You should have received a copy of the GNU General Public License
27 * along with this program; if not, write to the Free Software
28 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
32 #include "utility.h"
33 #include "../../headers/general.h"
34 #include "../../headers/version.h"
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <ctype.h>
42 #define KEEPCOMMENTS
44 #define MAXLINE 1024
45 #ifdef __BCC__
46 #define HASHSIZE 107
47 #else
48 #define HASHSIZE 1999
49 #endif
50 #define NOCHAR '\177'
51 #define VARNUM 10
55 /* Struct containing each string of an input file */
56 struct line_s {
57 char *text;
58 struct line_s *prev;
59 struct line_s *next;
60 int comment_flg;
64 /* Struct containing one rule */
65 struct rule_s {
66 struct line_s *old;
67 struct line_s *new;
68 struct rule_s *next;
72 /* Hash table to store strings in a space saving way */
73 struct hash_s {
74 char *text;
75 struct hash_s *next;
81 * Global variables
83 static struct rule_s *first = NULL; /* first rule */
84 static struct rule_s *last = NULL; /* last rule */
85 static struct line_s *infile = NULL; /* list of strings in input file */
86 static struct hash_s *htab[HASHSIZE]; /* string hash table */
87 static int hash_init = 0; /* flag if hash table initialized */
88 static char *vars[VARNUM]; /* variable table */
89 static char *progname; /* program name */
94 * Allocate memory and print error if none available
96 static void *mymalloc(int size)
98 void *p;
100 if ((p = malloc(size)) == NULL) {
101 fprintf(stderr, "%s: no memory\n", progname);
102 exit(1);
104 return(p);
110 * Insert a string into the hash table. If the string is already in there
111 * just return the pointer to that string.
113 static char *install(char *str, int slen)
115 struct hash_s *hp;
116 char *chkstr;
117 char *cp;
118 unsigned int hashval;
120 /* Clear the hashing table if not already done */
121 if (!hash_init) {
122 for (hashval = 0; hashval < HASHSIZE; hashval++)
123 htab[hashval] = NULL;
124 hash_init++;
127 /* Get check string */
128 if (slen < 0)
129 slen = strlen(str);
130 chkstr = mymalloc(slen + 1);
131 strncpy(chkstr, str, slen);
132 chkstr[slen] = '\0';
134 /* Determine hashing value of string */
135 hashval = 0;
136 for (cp = chkstr; *cp; cp++)
137 hashval = hashval*75 + *cp;
138 hashval %= HASHSIZE;
140 /* Check if the string is already in the hashing table */
141 for (hp = htab[hashval]; hp != NULL; hp = hp->next)
142 if (!strcmp(chkstr, hp->text)) {
143 free(chkstr);
144 return(hp->text);
147 /* String is not in hash table, so create a new entry */
148 hp = (struct hash_s *)mymalloc(sizeof(struct hash_s));
149 hp->text = chkstr;
150 hp->next = htab[hashval];
151 htab[hashval] = hp;
152 return(hp->text);
158 * Read one line from input file and skip all blanks at the beginning
160 static char *readline(FILE *fp)
162 static char buf[MAXLINE];
163 char *cp;
165 /* Read line from input file */
166 if (fgets(buf, MAXLINE-1, fp) == NULL)
167 return(NULL);
168 buf[MAXLINE-1] = '\0';
170 /* Delete trailing newline */
171 if ((cp = strchr(buf, '\n')) != NULL)
172 *cp = '\0';
174 /* Delete leading white spaces */
175 for (cp = buf; *cp && isspace(*cp); cp++) ;
176 if (cp != buf && *cp)
177 memmove(buf, cp, strlen(cp) + 1);
179 return(buf);
185 * Read a list of input lines. Terminate reading when the 'quit' character
186 * has been found in the first column of the input line. All lines with the
187 * 'comment' character in the first position will be skipped.
189 static struct line_s *readlist(FILE *fp, int quit, int comment)
191 struct line_s *lp;
192 struct line_s *first_line = NULL;
193 struct line_s *last_line = NULL;
194 char *cp;
196 while ((cp = readline(fp)) != NULL) {
197 if (quit != NOCHAR && quit == *cp)
198 break;
199 if (comment != NOCHAR && comment == *cp)
200 if (quit != NOCHAR)
201 continue;
202 if (*cp == '\0')
203 continue;
204 lp = mymalloc(sizeof(struct line_s));
205 lp->text = install(cp, -1);
206 lp->prev = last_line;
207 lp->next = NULL;
208 lp->comment_flg = (comment != NOCHAR && *cp == comment);
209 if (first_line == NULL)
210 first_line = lp;
211 if (last_line != NULL)
212 last_line->next = lp;
213 last_line = lp;
215 return(first_line);
221 * Read pattern file
223 static void readpattern(char *rulesdir, char *filename)
225 static char path[MAXLINE];
226 struct rule_s *rp;
227 FILE *fp;
229 /* Open pattern file */
230 if (rulesdir != NULL)
231 sprintf(path, "%s/%s", rulesdir, filename);
232 else
233 sprintf(path, "%s", filename);
234 if ((fp = fopen(path, "r")) == NULL) {
235 fprintf(stderr, "%s: can't open pattern file %s\n", progname, path);
236 exit(1);
239 /* Read every line of the pattern file */
240 while (!feof(fp)) {
241 rp = (struct rule_s *)mymalloc(sizeof(struct rule_s));
242 rp->old = readlist(fp, '=', '#');
243 rp->new = readlist(fp, '\0', '#');
244 if (rp->old == NULL || rp->new == NULL) {
245 free(rp);
246 break;
249 /* This put the rules into the table in reverse order; this is confusing *
250 rp->next = first;
251 first = rp;
252 if (last == NULL)
253 last = rp;
255 rp->next = NULL;
256 if (last) {
257 last->next = rp;
258 last = rp;
259 } else {
260 first = last = rp;
265 /* Close pattern file */
266 (void)fclose(fp);
272 * Clear pattern list to allow for another run
274 static void clearpattern(void)
276 struct rule_s *rp1, *rp2;
277 struct line_s *lp1, *lp2;
279 rp1 = first;
280 while (rp1 != NULL) {
281 /* Clear old rule text list */
282 lp1 = rp1->old;
283 while (lp1 != NULL) {
284 lp2 = lp1;
285 lp1 = lp1->next;
286 free(lp2);
288 /* Clear new rule text list */
289 lp1 = rp1->new;
290 while (lp1 != NULL) {
291 lp2 = lp1;
292 lp1 = lp1->next;
293 free(lp2);
295 /* Clear rule itself */
296 rp2 = rp1;
297 rp1 = rp1->next;
298 free(rp2);
301 first = NULL;
302 last = NULL;
308 * Read input file
310 static void readinfile(char *filename, int comment)
312 FILE *fp;
314 fp = stdin;
315 if (filename != NULL && (fp = fopen(filename, "r")) == NULL) {
316 fprintf(stderr, "%s: can't open input file %s\n", progname, filename);
317 exit(1);
319 infile = readlist(fp, NOCHAR, comment);
320 if (fp != stdin)
321 (void)fclose(fp);
324 #define NO_OP 0
325 #define ADD_OP 1
326 #define SUB_OP 2
327 #define MUL_OP 3
328 #define DIV_OP 4
329 #define SHL_OP 5
330 #define SHR_OP 6
332 long retval = 0;
333 long num = 0;
334 int sign = 1;
335 int base = 10;
336 int op = NO_OP;
338 /* Apply operation to current numeric value */
339 static void doretval(void)
341 switch (op) {
342 case NO_OP: retval = num * sign;
343 break;
344 case ADD_OP: retval += num * sign;
345 break;
346 case SUB_OP: retval -= num * sign;
347 break;
348 case MUL_OP: retval *= num * sign;
349 break;
350 case DIV_OP: retval /= num * sign;
351 break;
352 case SHL_OP: retval <<= num;
353 break;
354 case SHR_OP: retval >>= num;
355 break;
357 op = NO_OP;
358 num = 0;
359 sign = 1;
360 base = 10;
365 * Eval an expression into an integer number
367 static long eval(char *str, int len)
369 char *cp, c;
370 int state = 0;
371 int i, varnum;
373 retval = 0;
374 num = 0;
375 sign = 1;
376 base = 10;
377 op = NO_OP;
379 /* Scan through whole string and decode it */
380 for (cp = str, i = 0; *cp && i < len; cp++, i++) {
381 c = toupper(*cp);
382 if (c == '-' && (state == 0 || state == 5)) {
383 state = 1;
384 sign = -1;
385 } else if (c == '+' && (state == 0 || state == 5)) {
386 state = 1;
387 sign = 1;
388 } else if (c == '%' && isdigit(*(cp + 1)) && (state < 2 || state == 5)) {
389 state = 4;
390 varnum = *(cp + 1) - '0';
391 if (vars[varnum] == NULL || i >= len)
392 return(0);
393 num = eval(vars[varnum], strlen(vars[varnum]));
394 doretval();
395 cp++; i++;
396 } else if (c == '$' && (state < 2 || state == 5)) {
397 state = 2;
398 base = 16;
399 } else if (base == 10 && (c >= '0' && c <= '9') &&
400 (state <= 3 || state == 5)) {
401 state = 3;
402 num = num * 10 + (c - '0');
403 } else if (base == 16 &&
404 ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F')) &&
405 (state <= 3 || state == 5)) {
406 state = 3;
407 num = num * 16 + (c >= 'A' ? c - '0' - 7 : c - '0');
408 } else if (c == ' ' && (state == 3 || state == 4 || state == 5)) {
409 if (state == 3) {
410 doretval();
411 state = 4;
413 } else if (strchr("+-*/<>", c) != NULL && (state == 3 || state == 4)) {
414 if (state == 3)
415 doretval();
416 state = 5;
417 switch (c) {
418 case '+': op = ADD_OP;
419 break;
420 case '-': op = SUB_OP;
421 break;
422 case '*': op = MUL_OP;
423 break;
424 case '/': op = DIV_OP;
425 break;
426 case '<': op = SHL_OP;
427 break;
428 case '>': op = SHR_OP;
429 break;
431 } else
432 return(0);
435 /* Check if the string has been terminated correctly */
436 if (state != 3 && state != 4)
437 return(0);
438 if (state == 3)
439 doretval();
440 return(retval);
446 * Compare an infile string with a pattern string. If there is any variable
447 * defined, it will be inserted into the variable list from the pattern
448 * string.
450 static int match(char *ins, char *pat)
452 char *cp, *oldpat;
453 long val;
454 int varnum;
455 int len = 0;
457 while (*ins && *pat)
459 if (pat[0] != '%')
461 if (*pat++ != *ins++)
462 return(0);
463 else
464 continue;
466 if (pat[1] == '%') {
467 /* '%%' actually means '%' */
468 if (*ins != '%')
469 return(0);
470 pat += 2;
471 } else if ((pat[1] == '*' || isdigit(pat[1]))) {
472 /* Copy variable text into vars array */
473 pat += 2;
474 for (cp = ins; *ins && !match(ins, pat); ins++) ;
475 if (pat[-1] == '*')
476 continue;
477 len = ins - cp;
478 varnum = pat[-1] - '0';
479 if (vars[varnum] == NULL)
480 vars[varnum] = install(cp, len);
481 else if (strlen(vars[varnum]) != len ||
482 strncmp(vars[varnum], cp, len))
483 return(0);
484 } else if (pat[1] == '[') {
485 /* Copy only specific variable text into vars array */
486 if ((cp = strchr(pat + 2, ']')) == NULL ||
487 (*(cp + 1) != '*' && !isdigit(*(cp + 1)))) {
488 if (*ins != '[')
489 return(0);
490 pat += 2;
491 continue;
493 oldpat = pat + 1;
494 pat = cp + 2;
495 /* Seperate allowable patterns and compare them with ins */
496 while (*oldpat && *oldpat != ']') {
497 oldpat++;
498 len = strcspn(oldpat, "|]");
499 if (!strncmp(ins, oldpat, len))
500 break;
501 oldpat += len;
503 if (!*oldpat || *oldpat == ']')
504 return(0);
505 ins += len;
506 if (!match(ins, pat))
507 return(0);
508 /* Install new string into variable table */
509 if (*(cp + 1) == '*')
510 continue;
511 varnum = *(cp + 1) - '0';
512 if (vars[varnum] == NULL)
513 vars[varnum] = install(oldpat, len);
514 else if (strlen(vars[varnum]) != len ||
515 strncmp(vars[varnum], oldpat, len))
516 return(0);
517 } else if (pat[1] == '!') {
518 /* Match only if the pattern string is not found */
519 if (pat[2] != '[' || (cp = strchr(pat + 3, ']')) == NULL) {
520 if (*ins != '!')
521 return(0);
522 pat += 3;
523 continue;
525 oldpat = pat + 2;
526 pat = cp + 1;
527 /* Seperate allowable patterns and compare them with ins */
528 while (*oldpat && *oldpat != ']') {
529 oldpat++;
530 len = strcspn(oldpat, "|]");
531 if (!strncmp(ins, oldpat, len))
532 return(0);
533 oldpat += len;
535 } else if (pat[1] == '(') {
536 /* Match ins with expression */
537 if ((cp = strchr(pat + 2, ')')) == NULL) {
538 if (*ins != '(')
539 return(0);
540 pat += 2;
541 continue;
543 oldpat = pat + 2;
544 pat = cp + 1;
545 len = cp - oldpat;
546 val = eval(oldpat, len);
547 for (cp = ins; *ins && !match(ins, pat); ins++) ;
548 len = ins - cp;
549 if (val != eval(cp, len))
550 return(0);
552 else /* Bad % format cannot match */
553 return(0);
556 return(*ins == *pat);
562 * Substitute variables in a string
564 static char *subst(char *pat)
566 char buf[MAXLINE];
567 char *cp, *cp1, *cp2, *varptr;
568 long num;
569 int i = 0;
570 int j, pos;
572 while (*pat)
573 if (pat[0] == '%' && isdigit(pat[1])) {
574 /* Substitute with value of variable */
575 cp = vars[pat[1] - '0'];
576 while (cp != NULL && *cp) {
577 buf[i++] = *cp++;
578 if (i >= MAXLINE - 1) {
579 fprintf(stderr, "%s: line too long\n", progname);
580 exit(1);
583 pat += 2;
584 } else if (pat[0] == '%' && pat[1] == '(') {
585 /* Substitute with expression */
586 cp = pat + 2;
587 if ((pat = strchr(cp, ')')) == NULL || pat - cp <= 0)
588 num = 0;
589 else
590 num = eval(cp, pat - cp);
591 if (i >= MAXLINE - 20) {
592 fprintf(stderr, "%s: line too long\n", progname);
593 exit(1);
595 i += sprintf(&buf[i], "%s$%lx", num < 0 ? "-" : "", labs(num));
596 pat++;
597 } else if (pat[0] == '%' && pat[1] == '=') {
598 /* Substitute with converted variable */
599 /* First seperate all parts of the pattern string */
600 cp = pat + 2;
601 cp1 = cp2 = varptr = NULL;
602 if (*cp == '[') {
603 cp1 = ++cp;
604 while (*cp && *cp != ']')
605 cp++;
606 if (cp[0] == ']' && cp[1] == '[') {
607 cp += 2;
608 cp2 = cp;
609 while (*cp && *cp != ']')
610 cp++;
611 if (cp[0] == ']' && isdigit(cp[1]))
612 varptr = vars[cp[1] - '0'];
615 if (cp1 == NULL || cp2 == NULL || varptr == NULL) {
616 buf[i++] = *pat++;
617 if (i >= MAXLINE - 1) {
618 fprintf(stderr, "%s: line too long\n", progname);
619 exit(1);
621 continue;
623 pat = cp + 2;
624 /* Now scan through the first string to find variable value */
625 cp1--;
626 pos = 0;
627 while (*cp1 != ']') {
628 cp1++;
629 j = strcspn(cp1, "|]");
630 if (strlen(varptr) == j && !strncmp(cp1, varptr, j))
631 break;
632 pos++;
633 cp1 += j;
635 if (*cp1 == ']')
636 continue;
637 /* Scan through the second string to find the conversion */
638 cp2--;
639 while (*cp2 != ']' && pos > 0) {
640 cp2++;
641 j = strcspn(cp2, "|]");
642 pos--;
643 cp2 += j;
645 if (*cp2 == ']' || pos != 0)
646 continue;
647 /* Insert conversion string into destination */
648 cp2++;
649 while (*cp2 != '|' && *cp2 != ']') {
650 buf[i++] = *cp2++;
651 if (i >= MAXLINE - 1) {
652 fprintf(stderr, "%s: line too long\n", progname);
653 exit(1);
656 } else {
657 buf[i++] = *pat++;
658 if (i >= MAXLINE - 1) {
659 fprintf(stderr, "%s: line too long\n", progname);
660 exit(1);
664 buf[i] = '\0';
665 return(install(buf, i));
671 * Optimize one line of the input file
673 static struct line_s *optline(struct line_s *cur)
675 struct rule_s *rp;
676 struct line_s *ins, *pat;
677 struct line_s *lp1, *lp2;
678 int i;
680 for (rp = first; rp != NULL; rp = rp->next) {
681 /* Clear variable array */
682 for (i = 0; i < VARNUM; i++)
683 vars[i] = NULL;
685 /* Scan through pattern texts and match them against the input file */
686 ins = cur;
687 pat = rp->old;
688 while (ins != NULL
689 && pat != NULL
690 && ( ins->comment_flg ||
691 ( /* (pat->text[0]=='%' || ins->text[0]==pat->text[0]) && */
692 match(ins->text, pat->text)))) {
694 if (!ins->comment_flg)
695 pat = pat->next;
696 else if (ins->text[0]==pat->text[0]) /* Matching a comment! */
698 if (match(ins->text, pat->text))
699 pat = pat->next;
701 ins = ins->next;
704 /* Current pattern matched input line, so replace input with new */
705 if (pat == NULL) {
706 /* Clear all lines in the source file for this pattern */
707 lp1 = cur;
708 cur = cur->prev;
709 while (lp1 != ins) {
710 #if 0
711 if( lp1->comment_flg )
713 lp2 = lp1;
714 lp1 = lp1->next;
715 lp2->next = cur->next;
716 cur->next = lp2;
717 lp2->prev = cur;
718 cur=cur->next;
720 else
721 #endif
723 lp2 = lp1;
724 lp1 = lp1->next;
725 free(lp2);
728 /* Insert new lines into list */
729 pat = rp->new;
730 lp1 = cur;
731 lp2 = NULL;
732 while (pat != NULL) {
733 lp2 = mymalloc(sizeof(struct line_s));
734 lp2->text = subst(pat->text);
735 lp2->next = NULL;
736 lp2->prev = lp1;
737 lp2->comment_flg = 0;
738 if (lp1 != NULL)
739 lp1->next = lp2;
740 else
741 infile = lp2;
742 lp1 = lp2;
743 pat = pat->next;
745 if (ins != NULL)
746 ins->prev = lp2;
747 if (lp2 != NULL)
748 lp2->next = ins;
749 else if (lp1 != NULL)
750 lp1->next = NULL;
751 else
752 infile = NULL;
753 return(cur);
756 return(cur->next);
762 * Actually optimize all strings in the input file
764 static void optimize(int backup)
766 struct line_s *cur, *lp;
767 int i;
768 int in_asm = 0;
770 /* Scan through all lines in the input file */
771 cur = infile;
772 while (cur != NULL) {
773 if (cur->comment_flg || in_asm)
775 lp=cur->next;
776 if (memcmp(cur->text, "!BCC_", 5) == 0)
777 in_asm = (memcmp(cur->text+5, "ASM", 3) == 0);
779 else
780 if ((lp = optline(cur)) != NULL && lp != cur->next) {
781 for (i = 0; i < backup && lp != NULL; i++)
782 lp = lp->prev;
783 if (lp == NULL)
784 lp = infile;
786 cur = lp;
793 * Write out into destination file
795 static void writeoutf(char *filename, char *headstr)
797 FILE *fp;
798 struct line_s *lp;
800 fp = stdout;
801 if (filename != NULL && (fp = fopen(filename, "w")) == NULL) {
802 fprintf(stderr, "%s: can't open output file %s\n", progname, filename);
803 exit(1);
805 if (headstr != NULL) {
806 fprintf(fp, headstr);
807 fprintf(fp, "\n");
809 for (lp = infile; lp != NULL; lp = lp->next)
810 fprintf(fp, "%s\n", lp->text);
811 if (fp != stdout)
812 (void)fclose(fp);
818 * Print usage
820 static void usage(void)
822 fprintf(stderr, "usage: %s [-c<comment-char>] [-f<src-file>] [-o<out-file>]\n"
823 "\t\t[-b<backup-num>] [-h<head-str>] [-d<ruls-dir>] "
824 "<rules-file> ...\n", progname);
825 exit(1);
831 * Main program
834 main(int argc, char **argv)
836 char comment = NOCHAR;
837 char *srcfile = NULL;
838 char *outfile = NULL;
839 char *headstr = NULL;
840 char *rulesdir = NULL;
841 int backup = 0;
842 int i;
844 /* Get program name */
845 if ((progname = strrchr(argv[0], '/')) == NULL)
846 progname = argv[0];
847 else
848 progname++;
850 /* Make life easy for bcc */
851 if ( argc > 4 && strcmp(argv[2], "-o") == 0 && argv[1][0] != '-' )
853 srcfile = argv[1];
854 argv++,argc--;
857 /* Get options from command line */
858 for (i = 1; i < argc; i++)
859 if (!strncmp(argv[i], "-c", 2))
860 comment = argv[i][2];
861 else if (!strncmp(argv[i], "-b", 2))
862 backup = atoi(&(argv[i][3]));
863 else if (!strncmp(argv[i], "-f", 2))
864 srcfile = &(argv[i][2]);
865 else if (!strncmp(argv[i], "-o", 2))
867 if(argv[i][2])
868 outfile = &(argv[i][2]);
869 else if(++i<argc)
870 outfile = argv[i];
871 else
872 usage();
874 else if (!strncmp(argv[i], "-h", 2))
875 headstr = &(argv[i][2]);
876 else if (!strncmp(argv[i], "-d", 2))
877 rulesdir = &(argv[i][2]);
878 else if (argv[i][0] == '-')
879 usage();
880 else
881 break;
883 /* Have to have enough parameters for rule file names */
884 if ((argc - i) < 1)
885 usage();
887 /* Read source file and optimze it with every rules file */
888 readinfile(srcfile, comment);
889 for ( ; i < argc; i++) {
890 readpattern(rulesdir, argv[i]);
891 optimize(backup);
892 clearpattern();
894 writeoutf(outfile, headstr);
896 exit(0);