2 **************************************************************************
4 * Utility program to optimize the output of the BCC compiler
7 * Purpose: Optimize BCC assembler output
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
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.
33 #include "../../headers/general.h"
34 #include "../../headers/version.h"
55 /* Struct containing each string of an input file */
64 /* Struct containing one rule */
72 /* Hash table to store strings in a space saving way */
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
)
100 if ((p
= malloc(size
)) == NULL
) {
101 fprintf(stderr
, "%s: no memory\n", progname
);
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
)
118 unsigned int hashval
;
120 /* Clear the hashing table if not already done */
122 for (hashval
= 0; hashval
< HASHSIZE
; hashval
++)
123 htab
[hashval
] = NULL
;
127 /* Get check string */
130 chkstr
= mymalloc(slen
+ 1);
131 strncpy(chkstr
, str
, slen
);
134 /* Determine hashing value of string */
136 for (cp
= chkstr
; *cp
; cp
++)
137 hashval
= hashval
*75 + *cp
;
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
)) {
147 /* String is not in hash table, so create a new entry */
148 hp
= (struct hash_s
*)mymalloc(sizeof(struct hash_s
));
150 hp
->next
= htab
[hashval
];
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
];
165 /* Read line from input file */
166 if (fgets(buf
, MAXLINE
-1, fp
) == NULL
)
168 buf
[MAXLINE
-1] = '\0';
170 /* Delete trailing newline */
171 if ((cp
= strchr(buf
, '\n')) != NULL
)
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);
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
)
192 struct line_s
*first_line
= NULL
;
193 struct line_s
*last_line
= NULL
;
196 while ((cp
= readline(fp
)) != NULL
) {
197 if (quit
!= NOCHAR
&& quit
== *cp
)
199 if (comment
!= NOCHAR
&& comment
== *cp
)
204 lp
= mymalloc(sizeof(struct line_s
));
205 lp
->text
= install(cp
, -1);
206 lp
->prev
= last_line
;
208 lp
->comment_flg
= (comment
!= NOCHAR
&& *cp
== comment
);
209 if (first_line
== NULL
)
211 if (last_line
!= NULL
)
212 last_line
->next
= lp
;
223 static void readpattern(char *rulesdir
, char *filename
)
225 static char path
[MAXLINE
];
229 /* Open pattern file */
230 if (rulesdir
!= NULL
)
231 sprintf(path
, "%s/%s", rulesdir
, filename
);
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
);
239 /* Read every line of the pattern file */
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
) {
249 /* This put the rules into the table in reverse order; this is confusing *
265 /* Close pattern file */
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
;
280 while (rp1
!= NULL
) {
281 /* Clear old rule text list */
283 while (lp1
!= NULL
) {
288 /* Clear new rule text list */
290 while (lp1
!= NULL
) {
295 /* Clear rule itself */
310 static void readinfile(char *filename
, int comment
)
315 if (filename
!= NULL
&& (fp
= fopen(filename
, "r")) == NULL
) {
316 fprintf(stderr
, "%s: can't open input file %s\n", progname
, filename
);
319 infile
= readlist(fp
, NOCHAR
, comment
);
338 /* Apply operation to current numeric value */
339 static void doretval(void)
342 case NO_OP
: retval
= num
* sign
;
344 case ADD_OP
: retval
+= num
* sign
;
346 case SUB_OP
: retval
-= num
* sign
;
348 case MUL_OP
: retval
*= num
* sign
;
350 case DIV_OP
: retval
/= num
* sign
;
352 case SHL_OP
: retval
<<= num
;
354 case SHR_OP
: retval
>>= num
;
365 * Eval an expression into an integer number
367 static long eval(char *str
, int len
)
379 /* Scan through whole string and decode it */
380 for (cp
= str
, i
= 0; *cp
&& i
< len
; cp
++, i
++) {
382 if (c
== '-' && (state
== 0 || state
== 5)) {
385 } else if (c
== '+' && (state
== 0 || state
== 5)) {
388 } else if (c
== '%' && isdigit(*(cp
+ 1)) && (state
< 2 || state
== 5)) {
390 varnum
= *(cp
+ 1) - '0';
391 if (vars
[varnum
] == NULL
|| i
>= len
)
393 num
= eval(vars
[varnum
], strlen(vars
[varnum
]));
396 } else if (c
== '$' && (state
< 2 || state
== 5)) {
399 } else if (base
== 10 && (c
>= '0' && c
<= '9') &&
400 (state
<= 3 || state
== 5)) {
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)) {
407 num
= num
* 16 + (c
>= 'A' ? c
- '0' - 7 : c
- '0');
408 } else if (c
== ' ' && (state
== 3 || state
== 4 || state
== 5)) {
413 } else if (strchr("+-*/<>", c
) != NULL
&& (state
== 3 || state
== 4)) {
418 case '+': op
= ADD_OP
;
420 case '-': op
= SUB_OP
;
422 case '*': op
= MUL_OP
;
424 case '/': op
= DIV_OP
;
426 case '<': op
= SHL_OP
;
428 case '>': op
= SHR_OP
;
435 /* Check if the string has been terminated correctly */
436 if (state
!= 3 && state
!= 4)
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
450 static int match(char *ins
, char *pat
)
461 if (*pat
++ != *ins
++)
467 /* '%%' actually means '%' */
471 } else if ((pat
[1] == '*' || isdigit(pat
[1]))) {
472 /* Copy variable text into vars array */
474 for (cp
= ins
; *ins
&& !match(ins
, pat
); ins
++) ;
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
))
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)))) {
495 /* Seperate allowable patterns and compare them with ins */
496 while (*oldpat
&& *oldpat
!= ']') {
498 len
= strcspn(oldpat
, "|]");
499 if (!strncmp(ins
, oldpat
, len
))
503 if (!*oldpat
|| *oldpat
== ']')
506 if (!match(ins
, pat
))
508 /* Install new string into variable table */
509 if (*(cp
+ 1) == '*')
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
))
517 } else if (pat
[1] == '!') {
518 /* Match only if the pattern string is not found */
519 if (pat
[2] != '[' || (cp
= strchr(pat
+ 3, ']')) == NULL
) {
527 /* Seperate allowable patterns and compare them with ins */
528 while (*oldpat
&& *oldpat
!= ']') {
530 len
= strcspn(oldpat
, "|]");
531 if (!strncmp(ins
, oldpat
, len
))
535 } else if (pat
[1] == '(') {
536 /* Match ins with expression */
537 if ((cp
= strchr(pat
+ 2, ')')) == NULL
) {
546 val
= eval(oldpat
, len
);
547 for (cp
= ins
; *ins
&& !match(ins
, pat
); ins
++) ;
549 if (val
!= eval(cp
, len
))
552 else /* Bad % format cannot match */
556 return(*ins
== *pat
);
562 * Substitute variables in a string
564 static char *subst(char *pat
)
567 char *cp
, *cp1
, *cp2
, *varptr
;
573 if (pat
[0] == '%' && isdigit(pat
[1])) {
574 /* Substitute with value of variable */
575 cp
= vars
[pat
[1] - '0'];
576 while (cp
!= NULL
&& *cp
) {
578 if (i
>= MAXLINE
- 1) {
579 fprintf(stderr
, "%s: line too long\n", progname
);
584 } else if (pat
[0] == '%' && pat
[1] == '(') {
585 /* Substitute with expression */
587 if ((pat
= strchr(cp
, ')')) == NULL
|| pat
- cp
<= 0)
590 num
= eval(cp
, pat
- cp
);
591 if (i
>= MAXLINE
- 20) {
592 fprintf(stderr
, "%s: line too long\n", progname
);
595 i
+= sprintf(&buf
[i
], "%s$%lx", num
< 0 ? "-" : "", labs(num
));
597 } else if (pat
[0] == '%' && pat
[1] == '=') {
598 /* Substitute with converted variable */
599 /* First seperate all parts of the pattern string */
601 cp1
= cp2
= varptr
= NULL
;
604 while (*cp
&& *cp
!= ']')
606 if (cp
[0] == ']' && cp
[1] == '[') {
609 while (*cp
&& *cp
!= ']')
611 if (cp
[0] == ']' && isdigit(cp
[1]))
612 varptr
= vars
[cp
[1] - '0'];
615 if (cp1
== NULL
|| cp2
== NULL
|| varptr
== NULL
) {
617 if (i
>= MAXLINE
- 1) {
618 fprintf(stderr
, "%s: line too long\n", progname
);
624 /* Now scan through the first string to find variable value */
627 while (*cp1
!= ']') {
629 j
= strcspn(cp1
, "|]");
630 if (strlen(varptr
) == j
&& !strncmp(cp1
, varptr
, j
))
637 /* Scan through the second string to find the conversion */
639 while (*cp2
!= ']' && pos
> 0) {
641 j
= strcspn(cp2
, "|]");
645 if (*cp2
== ']' || pos
!= 0)
647 /* Insert conversion string into destination */
649 while (*cp2
!= '|' && *cp2
!= ']') {
651 if (i
>= MAXLINE
- 1) {
652 fprintf(stderr
, "%s: line too long\n", progname
);
658 if (i
>= MAXLINE
- 1) {
659 fprintf(stderr
, "%s: line too long\n", progname
);
665 return(install(buf
, i
));
671 * Optimize one line of the input file
673 static struct line_s
*optline(struct line_s
*cur
)
676 struct line_s
*ins
, *pat
;
677 struct line_s
*lp1
, *lp2
;
680 for (rp
= first
; rp
!= NULL
; rp
= rp
->next
) {
681 /* Clear variable array */
682 for (i
= 0; i
< VARNUM
; i
++)
685 /* Scan through pattern texts and match them against the input file */
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
)
696 else if (ins
->text
[0]==pat
->text
[0]) /* Matching a comment! */
698 if (match(ins
->text
, pat
->text
))
704 /* Current pattern matched input line, so replace input with new */
706 /* Clear all lines in the source file for this pattern */
711 if( lp1
->comment_flg
)
715 lp2
->next
= cur
->next
;
728 /* Insert new lines into list */
732 while (pat
!= NULL
) {
733 lp2
= mymalloc(sizeof(struct line_s
));
734 lp2
->text
= subst(pat
->text
);
737 lp2
->comment_flg
= 0;
749 else if (lp1
!= NULL
)
762 * Actually optimize all strings in the input file
764 static void optimize(int backup
)
766 struct line_s
*cur
, *lp
;
770 /* Scan through all lines in the input file */
772 while (cur
!= NULL
) {
773 if (cur
->comment_flg
|| in_asm
)
776 if (memcmp(cur
->text
, "!BCC_", 5) == 0)
777 in_asm
= (memcmp(cur
->text
+5, "ASM", 3) == 0);
780 if ((lp
= optline(cur
)) != NULL
&& lp
!= cur
->next
) {
781 for (i
= 0; i
< backup
&& lp
!= NULL
; i
++)
793 * Write out into destination file
795 static void writeoutf(char *filename
, char *headstr
)
801 if (filename
!= NULL
&& (fp
= fopen(filename
, "w")) == NULL
) {
802 fprintf(stderr
, "%s: can't open output file %s\n", progname
, filename
);
805 if (headstr
!= NULL
) {
806 fprintf(fp
, headstr
);
809 for (lp
= infile
; lp
!= NULL
; lp
= lp
->next
)
810 fprintf(fp
, "%s\n", lp
->text
);
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
);
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
;
844 /* Get program name */
845 if ((progname
= strrchr(argv
[0], '/')) == NULL
)
850 /* Make life easy for bcc */
851 if ( argc
> 4 && strcmp(argv
[2], "-o") == 0 && argv
[1][0] != '-' )
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))
868 outfile
= &(argv
[i
][2]);
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] == '-')
883 /* Have to have enough parameters for rule file names */
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
]);
894 writeoutf(outfile
, headstr
);