// b1.c                           0933                           5/8/00

// NOTE: This is a somewhat non-standard 32-bit "C" compiler for use
// with the excellent "dos" extender "WDOSX" written by Michael Tippach,
// or with "flat real" mode. ("F" command-line option).
// See the file "b-flat.doc" for more information.

// B is copyright (C) 1998,1999 by Ken Martwick.

// Version 0.40 8/25/99

//  This program is free software; you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation; either version 2 of the License, or
//  (at your option) any later version.

//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU General Public License for more details.

//  You should have received a copy of the GNU General Public License
//  along with this program; if not, write to the Free Software
//  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

#include <conio.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "b.h"

int main(int argc, char **argv)
     {
     clrscr();

     if(argc < 2)
          {
          printf("\n\n\tUSAGE: b filename [ostype]\n\n");
          return 1;
          }

     strcpy(inname, argv[1]);
     strcat(inname, ".b");
     strcpy(outname, argv[1]);
     strcat(outname, ".asm");

     if(argc > 2)
          {
          if((strchr(argv[2], 'f')) || (strchr(argv[2], 'F')))
               {
               ostype = 1;    // code for "flat real" mode
               }
          if((strchr(argv[2], 'u')) || (strchr(argv[2], 'U')))
               {
               ostype = 2;    // code for "UNOS" operating system
               }
          if((strchr(argv[2], 'o')) || (strchr(argv[2], 'O')))
               {
               ostype = 3;    // code for "OverDOS" operating system
               }
          if((strchr(argv[2], 'q')) || (strchr(argv[2], 'Q')))
               {
               ostype = 4;    // code for "flat real" mode for fasm assembler
               }               
          }

     infile = fopen(inname, "rt");
     if(!infile)
          {
          printf("\n\n\tCan't open %s!\n\n", inname);
          return 1;
          }

     atexit(free_memory);     // free memory allocated by "malloc"

     init_pointers();         // initialise source pointers

     for(n=0, lines=1; n < linecount; n++, lines++)
          {
          ptr = fgets(copy, 100, infile);
          if(!ptr) break;
          scopy(copy, iptr[n]); // get rid of comments!
          if(feof(infile)) break;
          }

     printf("\n\n\t%d lines of %s read.\n", lines, inname);

     result = get_data();     // transfer global data to "temp" file
     if(!result) printf("Warning -- No global data found.\n");

     result = get_proto();    // get function prototypes
     if(!result) printf("Warning -- No function prototypes found.\n");
     else
          {
          printf("\t%d Function Prototypes found.\n", pcount);
          if(libcount) printf("\tIncluding %d Library Functions.\n\n", libcount);
          }

     get_code();              // get and translate "C" code
     if(!gotcode)
          {
          printf("ERROR -- No \"C\" code found.\n");
          fclose(code);
          fclose(infile);
          return 1;
          }

     free(iptr[0]);

     if(libcount) get_lib_code(); // read and translate "library" code

     fclose(code);
     fclose(data);
     fclose(infile);

     if(ostype)               // not 32-bit code
          {
          if(!bsfile)
               {
               bsfile = fopen("bss.tmp", "wt");
               if(ostype != 4)
                    {
                    fprintf(bsfile, "\n\tsection\t.bss align=16\n\n");
                    }
               else
                    {
                    fprintf(bsfile, "\n;\tsection\t.bss align=16\n");
                    fprintf(bsfile, "\talign 16\n\n");
                    }                    
               }
          if(ostype == 1)
               {
               fprintf(bsfile, "\talignb 16\n");
               fprintf(bsfile, "newstack\tresd\t255\n");
               fprintf(bsfile, "stacktop resd\t1\n");
               fprintf(bsfile, "freemem resd\t1\n");
               }
          else if((ostype == 2) || (ostype == 3))
               {
               fprintf(bsfile, "stackseg\tresd\t1\n");
               fprintf(bsfile, "stackpoint\tresd\t1\n");
               fprintf(bsfile, "\talignb 16\n");
               fprintf(bsfile, "newstack\tresd\t255\n");
               fprintf(bsfile, "stacktop resd\t1\n");
               fprintf(bsfile, "freemem resd\t1\n");            
               }
          else if(ostype == 4) 
               {
               fprintf(bsfile, "\talign 16\n");
               fprintf(bsfile, "newstack\trd\t255\n");
               fprintf(bsfile, "stacktop rd\t1\n");
               fprintf(bsfile, "freemem rd\t1\n");
               }
          gotparam = 1;
          }

     if(gotparam)
          {
          fclose(bsfile);
          if(ostype != 4)
               {
               sprintf(copy, "copy /b data.tmp + bss.tmp + code.tmp %s", outname);
               }
          else
               {
               sprintf(copy, "copy /b code.tmp + data.tmp + bss.tmp %s", outname);
               }               
          system(copy);
          system("del *.tmp");
          }
     else
          {
          sprintf(copy, "copy /b data.tmp + code.tmp %s", outname);
          system(copy);
          system("del *.tmp");
          }

     free(iptr[0]);
     return 0;
     }

int begin_function(void)      // start new function
     {
     char *ptr, *tok[2];
     int num;

     tok[0] = strtok(copy, " \t\n");

     if((strstr(tok[0], "void")) || (strstr(tok[0], "char")) ||
     (strstr(tok[0], "int")))
          {
          tok[1] = strtok(NULL, " (\t\n");
          if(!tok[1]) return 0; // not a function!
          }

     else return 0;

     strcpy(token, tok[1]);

     num = isfunc();
     if(!num) return 0;       // not a function!

     fprintf(code, "\n%s:\n", token);
     m++;
     if(strstr(iptr[m], "{"))
          {
          level =1;
          infunc = num;  // show we're in function "num"
          }

     ptr = iptr[m + 1];       // point to next line
     if((strstr(ptr, "char")) || (strstr(ptr, "int")))
          {
          get_local_data(num); // enter local data
          }

     return  1;
     }

void begin_switch(void)       // set up for "switch" sequence
     {
     char *tok;
     int ndx;

     tok = strtok(NULL, " \t\n)");

     if(!tok)
          {
          bad: printf("\n\nMissing or bad \"switch\" variable in:\n");
          printf("%s\n", iptr[m]);
          exit(1);
          }

     strcpy(token, tok);
     ndx = isvar();
     if(!ndx) goto bad;

     if(var.size != 2) goto bad;

     strcpy(swname, var.name);
     sprintf(exitlabel, "sout%03d", labelnum);
     labelnum++;
     inswitch[level + 1] = 1; // anticipate level switch
     }

void call_function(int num)   // code call to specified function
     {
     char *tok[5];
     int k, limit, ndx;

     if(strchr(iptr[m], '"'))
          {
          save_string();      // save/point to character string
          }

     ndx = num - 1;           // actual index into "proto"

     if(proto[ndx].callcount) // if parameters required
          {
          if(!proto[ndx].gettail) // no argument list
               {
               for(k=0; k < 5; k++)
                    {
                    tok[k] = strtok(NULL, " ,\t);\n");
                    if(tok[k] == 0) break;
                    }
               }
          else
               {
               limit = proto[ndx].callcount - 1; // save "tail"
               for(k=0; k < limit; k++)
                    {
                    tok[k] = strtok(NULL, " ,\t);\n");
                    if(tok[k] == 0) break;
                    }

               if(get_tail()) // tail is present
                    {
                    fprintf(code, "\tmov\tdword [_tail],1\n");
                    }
               else
                    {
                    fprintf(code, "\tmov\tdword [_tail],0\n");
                    }

               k++;      // for count test
               }

          if(k != proto[ndx].callcount)
               {
               printf("\n\nFunction call with incorrect prototype count!\n");
               printf("%s\n", iptr[m]);
               exit(1);
               }

          if(proto[ndx].gettail) k--; // "_tail" already set!
          limit = k;          // parameter count

          for(k=0; k < limit; k++)
               {
               if(proto[ndx].callsize[k] == 1)
                    {
                    strcpy(token, tok[k]);
                    if((isdigit(token[0])) || (token[0] == 39))
                         {
                         fprintf(code, "\tmov\tal,%s\n", tok[k]);
                         }
                    else if(isvar())
                         {
                         fprintf(code, "\tmov\tal,[%s]\n", var.name);
                         }
                    else
                         {
                         printf("\n\n%s is not defined in:\n", tok[k]);
                         printf("%s\n", iptr[m]);
                         exit(1);
                         }

                    fprintf(code, "\tmov\t[%s],al\n",
                    proto[ndx].callname[k]);
                    }

               else      // callsize[k] = 2
                    {
                    strcpy(token, tok[k]);
                    if(isdigit(token[0])) fprintf(code, "\tmov\teax,%s\n",
                    tok[k]);
                    else if(isvar())
                         {
                         if(var.size > 2) // string variable - pass address
                              {
                              fprintf(code, "\tmov\teax,%s\n", var.name);
                              }
                         else fprintf(code, "\tmov\teax,[%s]\n", var.name);
                         }
                    else
                         {
                         printf("\n\n%s is not defined in:\n", tok[k]);
                         printf("%s\n", iptr[m]);
                         exit(1);
                         }

                    fprintf(code, "\tmov\t[%s],eax\n",
                    proto[ndx].callname[k]);
                    }
               }
          }

     fprintf(code, "\tcall\t%s\n", proto[ndx].name);

     proto[ndx].inuse = 1;    // show function is used
     }

void clear_if(void)           // clear "if-else" variables
     {
     int j, k;

     for(j=0; j < 10; j++)
          {
          inif[j] = 0;
          inelse[j] = 0;

          for(k=0; k < 20; k++)
               {
               ifexit[j][k] = 0;
               pastelse[j][k] = 0;
               }
          }
     }

void clear_loop(int lvl)      // clear loop structure
     {
     int j, k;

     for(j=0; j < 50; j++) loop[lvl].loop_start[j] = 0;
     for(j=0; j < 50; j++) loop[lvl].loopend[j] = 0;

     for(j=0; j < loop[lvl].endcount; j++)
          {
          for(k=0; k < 100; k++)
               {
               loop[lvl].endcode[j][k] = 0;
               }
          }
     loop[lvl].endcount = 0;
     }

void clear_loops(void)        // clear used loop structures
     {
     int j;

     for(j=0; j < 10; j++)
          {
          if(loop[j].endcount != 0) clear_loop(j);
          }
     }

void clear_var(void)          // clear "var" structure
     {
     int j;

     for(j=0; j < 20; j++) var.name[j] = 0;
     var.size = 0;
     var.ptsize = 0;
     var.value = 0;
     var.length = 0;
     var.isptr = 0;
     for(j=0; j < 80; j++) var.text[j] = 0;
     }

int define_structure(void)    // make structure "template"
     {
     char clean[100], *nmptr, *lenptr, *size;
     int isstring, vnum;
 
     lenptr = 0;              // make "gcc" happy!

     strcpy(clean, copy);     // make copy for error report
 
     nmptr = strtok(copy, " \t\n"); // get "typedef"
 
     if(!strstr(nmptr, "typedef")) return(0); // false alarm!
 
     nmptr = strtok(NULL, " \t\n;"); // get typedef name
 
     if(!nmptr)
          {
          tempbad: printf("\n\n\tStructure definition incorrect in:\n");
          printf("\t%s\n\n", clean);
          exit(1);
          }
 
     strcpy(template[tempcount].tempname, nmptr); // save "typedef" name
     
     if(inlib)                // reading library data
          {
          fgets(text, 100, libhead); // read line of header file
          if(!strchr(text, '{')) goto tempbad;
          fgets(text, 100, libhead); // read line of header file
          vnum = 0;                // alias for template[tempcount].varnum
          while(vnum < 25)
               {
               if(strchr(text, '}')) break; // end of typedef
               scopy(text, copy); // make copy to parse
               strcpy(clean, copy); // make copy for error report
               if(strchr(copy, '[') && strchr(copy, ']'))
                    {
                    isstring = 1;
                    }
               else
                    {
                    isstring = 0;
                    }
               size = strtok(copy, " [\t\n;"); // get variable size
               if(isstring)
                    {
                    lenptr = strtok(NULL, "]");
                    if(!lenptr) goto tempbad;
                    }
               nmptr = strtok(NULL, " \t\n[;"); // get variable name
               if((!size) || (!nmptr)) goto tempbad;
               strcpy(template[tempcount].name[vnum], nmptr);
               if(strstr(size, "int")) 
                    {
                    template[tempcount].size[vnum] = 2;
                    template[tempcount].tempsize += 4;
                    }
               else if(strstr(size, "char"))
                    {
                    if(isstring && lenptr) // character string
                         {
                         template[tempcount].size[vnum] = 3;
                         template[tempcount].length[vnum] = atoi(lenptr);
                         template[tempcount].tempsize += 
                         template[tempcount].length[vnum];
                         }
                    else 
                         { 
                         template[tempcount].size[vnum] = 1; // single character
                         template[tempcount].tempsize += 1;
                         }
                    }
               else goto tempbad;
               fgets(text, 100, libhead); // read line of header file
               vnum++;
               template[tempcount].varnum = vnum;
               }          
          }
          
     else
          {     
          m++;
          if(!strchr(iptr[m], '{')) goto tempbad;
          m++;
          vnum = 0;                // alias for template[tempcount].varnum
          while(vnum < 25)
               {
               if(strchr(iptr[m], '}')) break; // end of typedef
               scopy(iptr[m], copy); // make copy to parse
               strcpy(clean, copy); // make copy for error report
               if(strchr(copy, '[') && strchr(copy, ']'))
                    {
                    isstring = 1;
                    }
               else
                    {
                    isstring = 0;
                    }
               size = strtok(copy, " [\t\n;"); // get variable size
               if(isstring)
                    {
                    lenptr = strtok(NULL, "]");
                    if(!lenptr) goto tempbad;
                    }
               nmptr = strtok(NULL, " \t\n[;"); // get variable name
               if((!size) || (!nmptr)) goto tempbad;
               strcpy(template[tempcount].name[vnum], nmptr);
               if(strstr(size, "int")) 
                    {
                    template[tempcount].size[vnum] = 2;
                    template[tempcount].tempsize += 4;
                    }
               else if(strstr(size, "char"))
                    {
                    if(isstring && lenptr) // character string
                         {
                         template[tempcount].size[vnum] = 3;
                         template[tempcount].length[vnum] = atoi(lenptr);
                         template[tempcount].tempsize +=
                         template[tempcount].length[vnum];                         
                         }
                    else 
                         { 
                         template[tempcount].size[vnum] = 1; // single character
                         template[tempcount].tempsize += 1;
                         }
                    }
               else goto tempbad;
               m++;
               vnum++;
               template[tempcount].varnum = vnum;
               }
          }     
     
 
     tempcount++;
     return(1);
     }

void do_add(int size)         // encode addition operation
     {
     if(size == 1)            // "byte" operation
          {
          fprintf(code, "\tadd\tal,bl\n");
          }
     else
          {
          fprintf(code, "\tadd\teax,ebx\n");
          }
     }

void do_and(int size)         // encode bitwise "and" operation
     {
     if(size == 1)            // "byte" operation
          {
          fprintf(code, "\tand\tal,bl\n");
          }
     else
          {
          fprintf(code, "\tand\teax,ebx\n");
          }
     }

void do_assembly(void)        // transfer in-line assembler code
     {
     char *optr;

     optr = strstr(iptr[m], "_asm ") + 5;
     fprintf(code, "\t%s", optr);
 
     while(1)
          {
          if(strstr(iptr[m+1], "_asm"))
               {
               m++;
               optr = strstr(iptr[m], "_asm ") + 5;
               fprintf(code, "\t%s", optr);
               }

          else break;
          }
     }

void do_default(void)         // start "default" code
     {
     indefault[level] = 1;    // show default code exists

     fprintf(code, "%s:\n", nextlabel);
     }

void do_div(int size)         // encode division operation
     {
     if(size == 1)            // "byte" operation
          {
          fprintf(code, "\txor\tah,ah\n");
          fprintf(code, "\tdiv\tbl\n");
          }
     else
          {
          fprintf(code, "\txor\tedx,edx\n");
          fprintf(code, "\tdiv\tebx\n");
          }
     }

void do_exit(void)            // write exit code for OS specified
     {
     if((ostype == 2) || (ostype ==3)) // "UNOS" or "OverDOS" program code
          {
          fprintf(code, "mainend: mov\tah,[exitcode]\n");
          fprintf(code, "\tpop\tdx\n"); // get caller's ds value
          fprintf(code, "\tmov\tebx,[stackseg]\n");
          fprintf(code, "\tmov\tecx,[stackpoint]\n");
          fprintf(code, "\tpush\tbx\n");
          fprintf(code, "\tpop\tss\n");
          fprintf(code, "\tpush\tcx\n");
          fprintf(code, "\tpop\tsp\n");
          fprintf(code, "\tmov\tds,dx\n"); // restore caller's ds value
          fprintf(code, "\tretf\n");
          }
     else
          {
          fprintf(code, "mainend: mov\tah,0x4C\n");
          fprintf(code, "\tmov\tal,[exitcode]\n");
          fprintf(code, "\tint\t0x21\n");
          }
     }

void do_lbreak(int lvl)       // insert "loop" break code
     {
     lvl -= 1;                // correct index
     fprintf(code, "\tjmp\t%s\n", loop[lvl].loopend);
     }

void do_math(int op)          // code math operation with specified variables
     {                        // result is in variable in "token"
     switch(op)
          {
          case BADD:     do_add(1);
                         break;

          case WADD:     do_add(2);
                         break;

          case BAND:     do_and(1);
                         break;

          case WAND:     do_and(2);
                         break;

          case BSUB:     do_sub(1);
                         break;

          case WSUB:     do_sub(2);
                         break;

          case BMUL:     do_mul(1);
                         break;

          case BSHL:     shift_left(1);
                         break;

          case BSHR:     shift_right(1);
                         break;

          case WMUL:     do_mul(2);
                         break;

          case BDIV:     do_div(1);
                         break;

          case WDIV:     do_div(2);
                         break;

          case BMOD:     do_mod(1);
                         break;

          case WMOD:     do_mod(2);
                         break;

          case WSHL:     shift_left(2);
                         break;

          case WSHR:     shift_right(2);
                         break;

          case BOR:      do_or(1);
                         break;

          case WOR:      do_or(2);
                         break;

          default:       break;
          }
     }

void do_mod(int size)         // encode modulus operation
     {
     if(size == 1)            // "byte" operation
          {
          fprintf(code, "\txor\tah,ah\n");
          fprintf(code, "\tdiv\tbl\n");
          fprintf(code, "\tmov\tal,ah\n"); // save result
          }
     else
          {
          fprintf(code, "\txor\tedx,edx\n");
          fprintf(code, "\tdiv\tebx\n");
          fprintf(code, "\tmov\teax,edx\n"); // save result
          }
     }

void do_mul(int size)         // encode multipllcation operation
     {
     if(size == 1)            // "byte" operation
          {
          fprintf(code, "\tmul\tbl\n");
          }
     else
          {
          fprintf(code, "\tmul\tebx\n");
          }
     }

void do_or(int size)          // encode bitwise "or" operation
     {
     if(size == 1)            // "byte" operation
          {
          fprintf(code, "\tor\tal,bl\n");
          }
     else
          {
          fprintf(code, "\tor\teax,ebx\n");
          }
     }

void do_return(void)          // encode return from function
     {
     char *tok;
     int retnum, retsize, retvar;

     retnum = 0;
     retvar = 0;

     retsize = proto[infunc - 1].retsize;

     if(retsize)              // if non-void function
          {
          tok = strtok(NULL, ")");
          if(tok)
               {
               strcpy(token, tok);
               if(isdigit(token[0])) retnum = 1;
               else if(isvar()) retvar = 1;
               else
                    {
                    printf("\n\n\t%s is not valid in:\n", token);
                    printf("\t%s\n\n", iptr[m]);
                    exit(1);
                    }
               }
          else printf("\n\n\tWarning - Return from non-void function %s with no value\n\n",
          proto[infunc - 1].name);
          }

     if((retsize == 1) && (retnum))
          {
          fprintf(code, "\tmov\tal,%s\n", token);
          }

     else if((retsize == 1) && (retvar))
          {
          fprintf(code, "\tmov\tal,[%s]\n", var.name);
          if(level == 1) havereturn = 1;
          }

     else if((retsize == 2) && (retnum))
          {
          fprintf(code, "\tmov\teax,%s\n", token);
          if(level == 1) havereturn = 1;
          }

     else if((retsize == 2) && (retvar))
          {
          fprintf(code, "\tmov\teax,[%s]\n", var.name);
          if(level == 1) havereturn = 1;
          }

     fprintf(code, "\tret\n");
     }

void do_sbreak(void)          // insert "switch" break code
     {
     fprintf(code, "\tjmp\t%s\n", exitlabel);
     }

void do_start(void)           // write "startup" code for OS specified
     {
     if(ostype == 1)          // "flat real" mode
          {
          fprintf(code, "main:\tpush\tcs\n");
          fprintf(code, "\tpop\tds\n");
          fprintf(code, "\tmov\tax,newstack\n"); // makes "bopt2" keep "newstack"
          fprintf(code, "\tmov\tax,freemem\n"); // makes "bopt2" keep "freemem"
          fprintf(code, "\tmov\tsp,stacktop\n");
          }
     else if((ostype == 2) || (ostype == 3)) // "UNOS" or "OverDOS" code
          {
          fprintf(code, "main:\tmov\tdx,ds\n");
          fprintf(code, "\tpush\tcs\n");
          fprintf(code, "\tpop\tds\n");
          fprintf(code, "\tmov\t[stackseg],ss\n");
          fprintf(code, "\tmov\t[stackpoint],sp\n");
          fprintf(code, "\tpush\tcs\n");
          fprintf(code, "\tpop\tss\n");
          fprintf(code, "\tmov\tax,newstack\n"); // makes "bopt2" keep "newstack"
          fprintf(code, "\tmov\tax,freemem\n"); // makes "bopt2" keep "freemem"          
          fprintf(code, "\tmov\tsp,stacktop\n");
          fprintf(code, "\tpush\tdx\n"); // caller's ds on local stack
          }
     else if(ostype == 4)     // "flat real" mode for "fasm" assembler
          {
          time(&now);
          fprintf(code, "; %s\t%s\n\n", outname, asctime(localtime(&now)));
          fprintf(code, "macro align value { rb (value-1) - ($ + value-1) mod value }\n\n");
          fprintf(code, "\tuse16\n");
          fprintf(code, "\torg\t0x0100\n\n");
          fprintf(code, "main:\tpush\tcs\n");
          fprintf(code, "\tpop\tds\n");
          fprintf(code, "\tmov\tax,newstack\n"); // makes "bopt2" keep "newstack"
          fprintf(code, "\tmov\tax,freemem\n"); // makes "bopt2" keep "freemem"
          fprintf(code, "\tmov\tsp,stacktop\n");
          }          
          
     else                     // WDOSX extender
          {
          fprintf(code, "main:\tmov\teax,0x901\n");
          fprintf(code, "\tint\t0x31\n");
          }
     }

void do_sub(int size)         // encode subtraction operation
     {
     if(size == 1)            // "byte" operation
          {
          fprintf(code, "\tsub\tal,bl\n");
          }
     else
          {
          fprintf(code, "\tsub\teax,ebx\n");
          }
     }

void early_exit(void)         // encode early exit
     {
     char *tok;

     tok = strtok(NULL, " \n)");

     if(!tok)
          {
          printf("\n\nReturn value not found:\n");
          printf("%s\n\n", iptr[m]);
          exit(1);
          }

     fprintf(code, "\tmov\tal,%s\n", tok);
     fprintf(code, "\tmov\t[exitcode],al\n");
     fprintf(code, "\tjmp\tmainend\n");
     }

void end_else(void)           // provide label to bypass "else" code
     {
     int l;

     fprintf(code, "%s:\n", pastelse[level]);

     inelse[level] = 0;
     for(l=0; l < 20; l++)
          {
          pastelse[level][l] = 0; // clear label
          }
     }

void end_function(void)       // insert return code for function
     {
     if(!havereturn) fprintf(code, "\tret\n");
     else havereturn = 0;
     proto[infunc - 1].entered = 1; // show function code entered
     infunc = 0;              // no longer in a function
     }

void end_if(int lvl)          // exit label for failed "if" test
     {
     int l;

     char text[20];

     if(strstr(iptr[m+1], "else")) // if "else" clause follows
          {
          sprintf(text, "elen%03d", labelnum); // create "bypass" label
          labelnum++;
          strcpy(pastelse[lvl], text); // save it
          fprintf(code, "\tjmp\t%s\n", text); // jump past "else"
          }

     fprintf(code, "%s:\n", ifexit[lvl]);

     inif[lvl] = 0;           // out of "if" code
     for(l=0; l < 20; l++)
          {
          ifexit[lvl][l] = 0; // clear label
          }
     }

void end_loop(void)           // "loop" back to start of current loop
     {
     int s, n, limit;

     s = level;               // keep code somewhat compact

     if(loop[s].endcount)
          {
          limit = loop[s].endcount;
          for(n=0; n < limit; n++) fprintf(code, "%s", loop[s].endcode[n]);
          }

     inloop[s] = 0;       // out of loop
     fprintf(code, "%s:\n", loop[s].loopend); // "exit" label

     clear_loop(s);       // clear loop structure
     }

void end_switch(void)         // terminate "switch" sequence
     {
     int l;

     if(!indefault[level]) // no default code
          {
          fprintf(code, "%s:\n", nextlabel); // exit from "case"
          }
     else indefault[level] = 0; // clear flag

     fprintf(code, "%s:\n", exitlabel); // exit from "switch"

     inswitch[level] = 0;

     for(l=0; l < 50; l++) swname[l] = 0;
     for(l=0; l < 20; l++) nextlabel[l] = 0;
     for(l=0; l < 20; l++) exitlabel[l] = 0;
     }

int find_function(int retval) // find/call function returning "retval"
     {
     char *junk, retname[20], mycopy[200], test[50], *tok;
     int foundfunc, k, retsize;

     foundfunc = 0;
     scopy(iptr[m], mycopy);  // make "local" copy for parsing
     junk = strtok(mycopy, " \t("); // "call name" of "retval"
     junk = strtok(NULL, " "); // get rid of "=" sign
     if(strcmp(junk, "=")) return 0; // not a function call
     tok = strtok(NULL, " (\t\n"); // should be function name
     strcpy(test, tok);

     for(k=0; k < pcount; k++)
          {
          if(!strcmp(test, proto[k].name)) // if name matches perfectly
               {
               foundfunc = 1;
               break;
               }
          }

     if(!foundfunc)
          {
          scopy(iptr[m], copy); // "set up" strtok again
          junk = strtok(copy, " \t("); // "call name" of "retval"
          return 0;
          }

     strcpy(retname, var.name);
     retsize = var.size;

     if(proto[k].retsize != 0) // not a "void" function
          {
          scopy(iptr[m], copy); // "fix up" "copy" for function call
          junk = strtok(copy, " \t("); // "call name" of "retval"
          junk = strtok(NULL, " "); // get rid of "=" sign
          junk = strtok(NULL, " \t("); // get rid of function name
          call_function(k + 1);
          }
     else
          {
          printf("\n\n\t\aFunction %s is void!\n", proto[k].name);
          printf("\t%s\n\n", iptr[m]);
          exit(1);
          }

     if(retsize == 1)         // "byte" operation
          {
          fprintf(code, "\tmov\t[%s],al\n", retname);
          }

     else
          {
          fprintf(code, "\tmov\t[%s],eax\n", retname);
          }

     return 1;
     }

int find_local(int fnum)      // find local variable
     {
     int count, found, n, ndx;

     ndx = fnum - 1;          // actual index to "proto"
     found = 0;

     if(!proto[ndx].localvar) return 0; // no local variables

     count = proto[ndx].localndx + proto[ndx].localvar; // limit
     n = proto[ndx].localndx; // first index

     for(; n < count; n++)
          {
          if(!strcmp(var.name, local[n].callname))
               {
               found = 1;     // found variable
               break;
               }
          }

     if(!found) return 0;

     strcpy(var.name, local[n].name); // return "full" name
     var.size = local[n].size;
     var.ptsize = local[n].ptsize;
     var.isptr = local[n].isptr;
     var.value = 0;

     return 1;
     }

void free_memory(void)        // free memory allocated by "malloc"
     {
     free(iptr[0]);
     }

int get_code(void)            // get and translate "C" code
     {
     char parse, *tok;

     foundmain = 0;           // double check code start
     parse = 0;

     if(strstr(iptr[m], " main(")) foundmain = 1; // are we really there?
     else
          {
          for(m=0; m < lines; m++)
               {
               if(strstr(iptr[m], " main("))
                    {
                    foundmain = 1;
                    break;
                    }
               }
          }

     if(!foundmain)
          {
          printf("\a\n\n\tCan't find start of code!\n\n");
          exit(1);
          }
     else
          {
          if((strstr(iptr[m], "argc")) && (strstr(iptr[m], "argv"))) parse = 1;
          }

     m++;
     if(strstr(iptr[m], "{"))
          {
          level = 1;
          gotcode = 1;
          inmain = 1;
          code = fopen("code.tmp", "wt");
          if(ostype != 4)
               {
               fprintf(code, "\n\tsection\t.text\n\n");
               }
          do_start();         // write "startup" code for OS specified
          if(parse)
               {
               setup_parse(); // enter code to parse "command tail"
               }
          m++;
          }

     for(; m < lines; m++)
          {
          scopy(iptr[m], copy); // make copy for parsing
          if(test_copy()) continue; // blank line or comment
          if(level == 0)
               {
               clear_loops(); // be sure loop structures are clear
               clear_if();    // clear "if-else" variables
               result = begin_function(); // start new function
               if(result) continue;
               else break;
               }
          tok = strtok(copy, " \t(");
          if(strchr(tok, '{'))
               {
               level++;
               continue;
               }
          if(strchr(tok, '}'))
               {
               if((inmain) && (level == 1)) // at end of "main"
                    {
                    inmain = 0;
                    do_exit(); // write exit code for specific OS
                    }
               if(inswitch[level]) end_switch(); // terminate "switch" sequence
               if(inloop[level]) end_loop();
               if(inif[level]) end_if(level);
               if(inelse[level]) end_else();
               if((infunc) && (level == 1)) end_function();
               level--;
               if(level == 0) clear_if();
               continue;
               }
          if(strstr(tok, "continue"))
               {
               jump_back();
               continue;
               }
          if(strstr(tok, "if"))
			{
			parse_if();
			continue;
			}
          if(strstr(tok, "else"))
			{
			inelse[level + 1] = 1; // anticipate shift
			continue;
			}
          if(strstr(tok, "for"))
			{
			parse_for();
			continue;
			}
          if(strstr(tok, "while"))
			{
			parse_while();
			continue;
			}
          if(strstr(tok, "_asm"))
               {
               do_assembly();
               continue;
               }
          if(strstr(tok, "switch"))
			{
			begin_switch(); // set up for "switch" sequence
			continue;
			}
          if(strstr(tok, "case"))
			{
			if(inswitch[level])
                    {
                    parse_case();  // do "case" test
                    }
               else
                    {
                    printf("\n\nCase statement outside \"switch\":\n");
                    printf("%s\n\n", iptr[m]);
                    exit(1);
                    }
			continue;
			}
          if(strstr(tok, "return"))
			{
			if(infunc)
                    {
                    do_return();   // encode early return
                    }
               else
                    {
                    printf("\n\n\"Return\" statement outside function:\n");
                    printf("%s\n\n", iptr[m]);
                    exit(1);
                    }
			continue;
			}
          if(strstr(tok, "exit"))
			{
               early_exit();  // encode early exit
               continue;
               }
          if(strstr(tok, "default:"))
			{
			if(inswitch[level])
                    {
                    do_default();  // start "default" code
                    }
               else
                    {
                    printf("\n\n\"Default\" statement outside \"switch\":\n");
                    printf("%s\n\n", iptr[m]);
                    exit(1);
                    }
			continue;
			}

          if(strstr(tok, "break;"))
			{
               if(inswitch[level])
                    {
			     do_sbreak();   // insert "switch" break code
			     continue;
                    }
               else if(inloop[level])
                    {
                    do_lbreak(level); // insert "loop" break code
                    continue;
                    }
               else if((result = next_level()))
                    {
                    result--;
                    do_lbreak(level); // insert "loop" break code
                    continue;
                    }
               else
                    {
                    printf("\n\nWarning -- \"break\" not supported in:\n");
                    printf("%s\n", iptr[m]);
                    continue;
                    }
               }
          strcpy(token, tok);
          result = isfunc();
          if(result)
               {
               call_function(result);
               continue;
               }
          result = isvar();
          if(result)
               {
               if(strstr(token, "++"))
                    {
                    increment(1, result);
                    continue;
                    }
               else if(strstr(token, "--"))
                    {
                    increment(0, result);
                    continue;
                    }
               else if(find_function(result)) continue;
               else
                    {
                    parse_math(result);
                    continue;
                    }
               }
          else
               {
               printf("\n\n\t\aError: %s not defined!\n\n", token);
               exit (1);
               }
          }
     if(gotcode)
          {
          return 1;
          }

     return 0;
     }

int get_data(void)            // transfer global data to "temp" file
     {
     char cstr[5], *dptr, *length, *sptr, temp[50], *tok[4];
     int num, oldcount;

     oldcount = 0;


     data = fopen("data.tmp", "wt");
     if(ostype != 4)
          {
          time(&now);
          fprintf(data, "; %s\t%s\n\n", outname, asctime(localtime(&now)));
          }

     if((ostype == 1) || (ostype == 3))    // "flat real" or OverDOS program
          {
          fprintf(data, "\tbits\t16\n");
          fprintf(data, "\torg\t0x0100\n\n");
          }
     else if(ostype == 2)     // "UNOS" system code
          {
          fprintf(data, "\tbits\t16\n");
          fprintf(data, "\torg\t0x0000\n\n");
          }
     else if(!ostype)         // "WDOSX" 32-bit extender
          {
          fprintf(data, "\tbits\t32\n");
          fprintf(data, "\torg\t0x0000\n\n");
          }
          
     if(ostype != 4)
          {     
          fprintf(data, "\tsection\t.data align=16\n\n");
          }
     else
          {     
          fprintf(data, "\n;\tsection\t.data align=16\n"); // needed for "datopt"
          fprintf(data, "align 16\n\n");
          }          

     n = 0;

     for(m=0; m < lines; m++)
          {
          if(strstr(iptr[m], ");")) break; // don't get prototypes
          if((strstr(iptr[m], "main")) && (!strstr(iptr[m], ");")))
               {
               foundmain = 1;
               break;         // don't get code
               }

          if(strstr(iptr[m], "#lib")) // "library" filename
               {
               needlib = 1;
               scopy(iptr[m], copy);
               tok[0] = strtok(copy, " \t\n"); // "#lib"
               tok[1] = strtok(NULL, " \t\n;,"); // name
               strcpy(libname, tok[1]);
               strcat(libname, ".lib"); // full name of "library" file
               strcpy(libheadname, tok[1]);
               strcat(libheadname, ".h"); // full name of "header" file
               oldcount = get_lib_data(); // transfer global data to "temp" file
               if(!oldcount) printf("Warning -- No library data found.\n");
               continue;
               }

          if(strstr(iptr[m], "typedef")) // structure "template"
               {
               scopy(iptr[m], copy);  // make copy for parsing
               if(define_structure()) // make structure "template"
                    {
                    continue;
                    }
               }
 
          if(strstr(iptr[m], "struct")) // structure definition
               {
               scopy(iptr[m], copy);
               if(make_structure()) // create structure
                    {
                    continue;
                    }
               }

          if((*iptr[m] == 'i') && (*(iptr[m]+1) == 'n') && (*(iptr[m]+2) == 't'))
               {
               scopy(iptr[m], copy);
               tok[0] = strtok(copy, " \t\n"); // "int"
               tok[1] = strtok(NULL, " [\t\n;,"); // name
               strcpy(token, tok[1]);
               if(isvar())
                    {
                    printf("\nB1-1231 Warning - %s re-defined!\n", var.name);
                    continue;
                    }
               if(strchr(tok[1], '*')) // if pointer
                    {
                    cvar[dcount].isptr = 1;
                    strcpy(temp, tok[1]);
                    ptr = temp + 1;
                    strcpy(tok[1], ptr);
                    cvar[dcount].size = 2; // not if array of ptrs - see following
                    cvar[dcount].ptsize = 2;
                    }
               if((strchr(iptr[m], '[')) && (strchr(iptr[m], ']'))) // if string
                    {
                    length = strtok(NULL, "]"); // string length
                    if(length)
                         {
                         cvar[dcount].length = atoi(length);
                         cvar[dcount].size = 4; // array of pointers
                         }
                    strcpy(cvar[dcount].name, tok[1]);
                    dcount++;
                    }
               else
                    {
                    tok[2] = strtok(NULL, " \t\n;,"); // "="?
                    if(tok[2]) tok[3] = strtok(NULL, " \t\n;,"); // value
                    strcpy(cvar[dcount].name, tok[1]);
                    cvar[dcount].size = 2;
                    if(tok[3])
                         {
                         if((strchr(tok[3], 'x')) || (strchr(tok[3], 'X')))
                              {
                              cvar[dcount].value = (int)strtol(tok[3], NULL, 16);
                              }
                         else cvar[dcount].value = atoi(tok[3]);
                         }
                    else cvar[dcount].value = 0;
                    dcount++;
                    }
               }

          else if((*iptr[m] == 'c') && (*(iptr[m]+1) == 'h') && // "char" or "ptr"
          (*(iptr[m]+2) == 'a') && (*(iptr[m]+3) == 'r'))
               {
               scopy(iptr[m], copy);
               tok[0] = strtok(copy, " \t\n"); // "char"
               tok[1] = strtok(NULL, " [\t\n;"); // name
               strcpy(token, tok[1]);
               if(isvar())
                    {
                    printf("\nB1-1282 Warning - %s re-defined!\n", var.name);
                    continue;
                    }
               if(strchr(tok[1], '*')) // if pointer
                    {
                    cvar[dcount].isptr = 1;
                    strcpy(temp, tok[1]);
                    ptr = temp + 1;
                    strcpy(tok[1], ptr);
                    cvar[dcount].size = 2; // not if array of ptrs - see following
                    cvar[dcount].ptsize = 1;
                    }
               if((strchr(iptr[m], '[')) && (strchr(iptr[m], ']'))) // if string
                    {
                    length = strtok(NULL, "]"); // string length
                    if(length)
                         {
                         cvar[dcount].length = atoi(length);
                         if(cvar[dcount].isptr) cvar[dcount].size = 4; // array of pointers
                         else cvar[dcount].size = 3;
                         }
                    if((strstr(iptr[m], "{")) && (strstr(iptr[m], "}")))
                         {
                         sptr = strchr(iptr[m], '{'); // start of string
                         sptr += 1; // point beyond "{"
                         dptr = cvar[dcount].text;
                         for(; ; dptr++, sptr++)
                              {
                              if(*sptr == '}') break;
                              if(*sptr == '"') *dptr = 39;
                              else *dptr = *sptr;
                              }
                         *dptr++ = 44;
                         *dptr++ = 48; // end string with a "zero" byte
                         }
                    strcpy(cvar[dcount].name, tok[1]);
                    dcount++;
                    }
               else
                    {
                    tok[2] = strtok(NULL, " \t\n;,"); // "="?
                    if((tok[2]) && (strchr(tok[2], '=')))
                         {
                         tok[3] = strtok(NULL, " \t\n;");
                         if(tok[3])
                              {
                              if(*tok[3] == 39) // if character value
                                   {
                                   tok[3]++; // point beyond quote
                                   sprintf(cstr, "%d", *tok[3]);
                                   cvar[dcount].value = atoi(cstr);
                                   strcpy(cvar[dcount].name, tok[1]);
                                   cvar[dcount].size = 1;
                                   dcount++;
                                   continue;
                                   }
                              else if((strchr(tok[3], 'x')) || (strchr(tok[3], 'X')))
                                   {
                                   cvar[dcount].value = (int)strtol(tok[3], NULL, 16);
                                   }
                              else cvar[dcount].value = atoi(tok[3]);
                              }
                         }
                    else cvar[dcount].value = 0;
                    strcpy(cvar[dcount].name, tok[1]);
                    if(!cvar[dcount].isptr) cvar[dcount].size = 1;
                    dcount++;
                    }
               }
          }

     if(dcount)
          {
          for(num=oldcount; num < dcount; num++)
               {
               if(cvar[num].size == 1)
                    {
                    fprintf(data, "%s\tdb\t%d\n", cvar[num].name, cvar[num].value);
                    }
               if(cvar[num].size == 2)
                    {
                    fprintf(data, "%s\tdd\t%d\n", cvar[num].name, cvar[num].value);
                    }
               if(cvar[num].size == 3)
                    {
                    if((cvar[num].length) && (cvar[num].text[0] == 0))
                         {
                         fprintf(data, "%s:\ttimes %d db 0\n", cvar[num].name,
                         cvar[num].length);
                         }
                    else fprintf(data, "%s\tdb\t%s\n", cvar[num].name,
                    cvar[num].text);
                    }
               if(cvar[num].size == 4)
                    {
                    if(cvar[num].length)
                         {
                         fprintf(data, "%s:\ttimes %d dd 0\n", cvar[num].name,
                         cvar[num].length);
                         }
                    }
               if(cvar[num].addbytes)
                    {
                    fprintf(data, "\ttimes %d db 0\n", cvar[num].addbytes);
                    }     
               }
          }

     return (dcount);
     }
