// quickopt.c                     1017                           4/8/00

// A program to optimize "asm" code produced by "b".  Optimization is
// limited to eliminating unused data items.  This program is somewhat
// reorganised to accommodate the "code-first" ordering of "fasm" "asm"
// code and the different nomenclature fo uninitialised data.

// NOTE: This program runs "qlabel.exe" before it opens the
//       specified "asm" file.  Running "qlabel.exe" multiple
//       times has no ill effects.

#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

FILE *infile;
FILE *outfile;

struct DATA                   // structure for variables
     {
     char name[20];
     int length;              // length of name
     int inuse;               // set if variable is used in code
     } data[500];
     
// "General" Variables:

char command[50], filename[10], *fptr;
char copy[150], instring[150];
int dcount, incode, inbss, indata, j, line, n, result, usecount;

// Function Prototypes:

void clear_instring(void);    // clear "instring"
void clear_strings(void);     // clear "copy" and "instring"
void read_data(void);         // get data and/or "bss" entries
void read_top(void);          // read/copy "asm" file until first "SECTION"
void test_usage(void);        // test if variables found are used
void write_data(void);        // write required data and code to outfile

void main(int argc, char *argv[])
     {
     if(argc < 2)
          {
          printf("\n\n\t\aUSAGE: quickopt <filename>\n\n");
          exit(1);
          }

     strcpy(filename, argv[1]); 
     sprintf(command, "qlabel %s", filename);
     result = system(command);
     if(result)
          {
          printf("\n\n\t\aError running %s!\n\n", command);
          exit(1);
          }
              
     sprintf(command, "%s.asm", filename);
     infile = fopen(command, "rt");
     if(!infile)
          {
          printf("\n\n\t\aCan't open %s!\n\n", command);
          exit(1);
          }
          
     sprintf(command, "%s.aop", filename);
     outfile = fopen(command, "wt");
     if(!outfile)
          {
          printf("\n\n\t\aCan't open %s!\n\n", command);
          fclose(infile);
          exit(1);
          }
          
     read_top();              // read/copy "asm" file until first "SECTION"
     if(indata || inbss)
          {
          read_data(); // get data and/or "bss" entries
          printf("\n\n\t%d data entries were found.\n", dcount);
          }

     if(dcount)               // if data was found
          {          
          test_usage();       // test if variables found are used
          }

     printf("\t%d of the data entries are actually used.\n\n", usecount);

     write_data();            // write required data to outfile

     fclose(infile);
     fclose(outfile);
     
     sprintf(command, "rm %s.asm", filename);
     system(command);         // erase "asm" file
     sprintf(command, "mv %s.aop %s.asm", filename, filename);
     system(command);         // move "aop" to "asm" 
     }     
     
void clear_instring(void)     // clear "instring"
     {
     int n;
     
     for(n=0; n < 150; n++) instring[n] = 0;
     }     

void clear_strings(void)      // clear "copy" and "instring"
     {
     int n;
     
     for(n=0; n < 150; n++)
          {
          copy[n] = 0;
          instring[n] = 0;
          }
     }     

void read_data(void)          // get data and/or "bss" entries
     {
     int j;
     
     if(indata)
          {
          for(; line <= 10000; line++)
               {
               clear_instring();
               fgets(instring, 150, infile);
               if(strstr(instring, ".bss"))
                    {
                    indata = 0;
                    inbss = 1;
                    break;
                    }
          
               else if(strstr(instring, ".text"))
                    {
                    indata = 0;
                    inbss = 0;
                    incode = 1;
                    break;
                    }
          
               else if(feof(infile))    // bad file!
                    {
                    indata = 0;
                    inbss = 0;
                    incode = 0;
                    break;
                    }
                    
               else if((strstr(instring, "db")) || (strstr(instring, "dd")))
                    {
                    for(j=0; j < 20; j++)
                         {
                         if((!isalnum(instring[j])) &&
                         (instring[j] != '_')) break;
                         else
                              {
                              data[dcount].name[j] = instring[j];
                              data[dcount].length++;
                              }
                         }
                    dcount++;
                    }          
               }
          }          
                    
     if(inbss)
          {
          for(; line <= 10000; line++)
               {
               clear_instring();
               fgets(instring, 150, infile);
               if(strstr(instring, ".bss"))
                    {
                    indata = 0;
                    inbss = 1;
                    break;
                    }
          
               else if(strstr(instring, ".text"))
                    {
                    indata = 0;
                    inbss = 0;
                    incode = 1;
                    break;
                    }
          
               else if(feof(infile))    // bad file!
                    {
                    indata = 0;
                    inbss = 0;
                    incode = 0;
                    break;
                    }
                    
               else if((strstr(instring, "rb")) || (strstr(instring, "rd")))
                    {
                    for(j=0; j < 20; j++)
                         {
                         if((!isalnum(instring[j])) &&
                         (instring[j] != '_')) break;
                         else
                              {
                              data[dcount].name[j] = instring[j];
                              data[dcount].length++;
                              }
                         }
                    dcount++;
                    }          
               }
          }          
     }                              

void read_top(void)           // read/copy "asm" file until first "SECTION"
     {
     for(line = 1; line <= 10000; line++)
          {
          clear_instring();
          fgets(instring, 150, infile); // read line from "asm" file
          if(strstr(instring, ".data"))
               {
               fputs(instring, outfile);
               indata = 1;
               break;
               }

          else if(strstr(instring, ".bss"))
               {
               fputs(instring, outfile);
               indata = 0;
               inbss = 1;
               break;
               }         
               
          else if(feof(infile))    // bad file!
               {
               indata = 0;
               inbss = 0;
               incode = 0;
               break;
               }
               
          else fputs(instring, outfile);
          }
     }     
     
void test_usage(void)         // test if variables found are used
     {
     int k;
     
     rewind(infile);          // go back to start
     
     for(line = 0; line < 10000; line++)
          {     
          clear_instring();
          fgets(instring, 150, infile); // read line from "asm" file
          
          if(strstr(instring, "section")) return; // beyond code section
          
          if(instring[0] == '\n') continue; // blank line
          
          for(k=0; k < dcount; k++)
               {
               if(fptr = strstr(instring, data[k].name))
                    {
                    if((isalnum(*(fptr-1))) || (isalnum(*(fptr+data[k].length))))
                         {
                         continue; // probably some other variable
                         }
                    
                    if(!data[k].inuse)
                         {
                         data[k].inuse = 1; // show variable used
                         usecount++;
                         }
                    }
               }
          }
     k = 0;                   // dummy statement for debugging     
     }                                        
     
void write_data(void)         // write required data to outfile
     {
     int n;
     
     rewind(infile);          // go back to start
     
     for(line=1; line < 10000; line++)
          {
          clear_instring();
          fgets(instring, 150, infile); // read line from "asm" file
          if((strstr(instring, ".data")) || (strstr(instring, ".bss")))
               {
               line++;
               break;
               }
          }
          
     for(; line < 10000; line++)
          {
          clear_instring();
          fgets(instring, 150, infile); // read line from "asm" file
          
          if(strstr(instring, ".text"))
               {
               fputs(instring, outfile);                         
               break;
               }          
          if((strstr(instring, "db")) || (strstr(instring, "dd")) ||
          (strstr(instring, "rb")) || (strstr(instring, "rd")))
               {
               for(n=0; n < dcount; n++)
                    {
                    if(fptr = strstr(instring, data[n].name))
                         {
                         if((isalnum(*(fptr-1))) ||
                         (isalnum(*(fptr + data[n].length))))
                              {
                              continue; // probably some other variable
                              }
                         else     
                              {                    
                              if(data[n].inuse)
                                   {
                                   fputs(instring, outfile);
                                   break;
                                   }
                              else break;     
                              }
                         }     
                    }
               }
               
          if((!strstr(instring, "db")) && (!strstr(instring, "dd")) &&
          (!strstr(instring, "rb")) && (!strstr(instring, "rd")))
               {
               fputs(instring, outfile);
               }
          }
     }
                                   