// I appear to have written essentially the same program twice.  Probably couldn't
// find the earlier one when I needed it.  Not sure which came first or which gives
// better output.  This one was called "stabs.c" and I found it on my gtoal.com server.

// It's now named recode-gcc6809-v2.c and is in the ~/src/C-PROGS directory on my home linux.
// The other one is named recode-gcc6809-v1.c and is also in that directory.

// The primary difference appears to be that v1 outputs a file that looks like a compiler
// listing, showing the generated machine code, and v2 looks more like an assembler source
// that could be assembled by as6809 with comments showing the original C source...

// A minor secondary difference is in the way the C statements follow or precede the
// corresponding assembly code in some instances.  Which is a very insignificant difference.

//
//  THIS IS A VERY CRUDE HACK.  It was really tricky to insert the C source into an asm
//  file and not change the assembly code at all, when gcc -O sometimes generates object
//  code in a different order from the C source.  It took a lot of trial and error to
//  work out acceptable places in the asm file to insert commented C statements, and frankly
//  having finally got it to work I'm not inclined to now do a rewrite just to make it
//  look nice.  It's not a critical program and it works well enough for now, so just
//  use it and don't look too closely at the code.  I can write better code than this,
//  but not today.
//

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

static int debug = 0;

#define MAXLINE 512
#define MAXC 1024
#define MAXASM (MAXC*16)

int main(int argc, char **argv)
{
  // not worth the effort of sorting out parameters.  Handled by gcc6809 shell script.
  // Just don't write your own program called testgcc09.
  char *SourceName;
  FILE *assem;
  char assemline[MAXLINE];
  char buffer[256*1024];
  char *cline[MAXC];
  char *asmline[MAXASM];
  int printed[MAXC], reallyprinted[MAXC];
  FILE *source;
  char *buffp = buffer;
  int first_source_line, last_source_line, lineno, asmlineno;
  int to_source_line[MAXASM], from_source_line[MAXASM]; // source line no. corresponding to this asm line no.
  int suppress_code = 0;
  int i, c;
  int last_lineno = 0, pending_lineno = 0, trigger = MAXC;
  char asmname[512];
  
  if (argc == 2) {
    char *s;
    SourceName = argv[1];
    sprintf(asmname, "%s", SourceName);
    s = strrchr(asmname, '.');
    if (s) {
      if (strcmp(s, ".c")==0) {
        *s = '\0'; strcat(s, ".s");
      } else {
        fprintf(stderr, "stabs: argument should be a .c file\n");
        exit(EXIT_FAILURE);
      }
    } else {
      fprintf(stderr, "stabs: argument should be a .c file\n");
      exit(EXIT_FAILURE);      
    }
    assem = fopen(asmname, "r");
    source = fopen(SourceName, "r");
  } else {
    fprintf(stderr, "syntax: stabs file.c\n");
    exit(EXIT_FAILURE);
  }
  
  for (i = 0; i < MAXASM; i++) asmline[i] = NULL;
  for (i = 0; i < MAXC; i++) reallyprinted[i] = printed[i] = 0;
 
  cline[0] = buffp; *buffp++ = '\0';
  first_source_line = lineno = 1;
  for (;;) {
    cline[lineno] = buffp;
    for (;;) {
      c = fgetc(source);
      if (c == EOF) break;
      if (c == '\n') break;
      *buffp++ = c;
    }
    if (c == EOF) break;
    *buffp++ = '\0';
    /*fprintf(stderr, "[Line %d] %s\n", lineno, cline[lineno]);*/
    lineno += 1;
  }
  *buffp++ = '\0';
  last_source_line = lineno;

  lineno = 1; // starting line for text from asm up to first #line n.
  asmlineno = 0;
  for (;;) {
    char *s = fgets(assemline, MAXLINE, assem), *t;
    if (!s) break;
    s = strchr(assemline, '\n'); if (s) *s = '\0';
    if (strncmp(assemline, ";\t.stab", strlen(";\t.stab")) == 0) {
      int stabtype = assemline[strlen(";\t.stab")];
      char *s = assemline+strlen(";\t.stab")+2;
      if (debug) fprintf(stderr, "Stabtype: %c  Line: {%s}\n", stabtype, s);
      switch (stabtype) {
      case 's': // .stabs "string",type,0,desc,value
        if (s[0] == '"' && (t=strstr(s, "\",100,0,0,"))!=0) {
          s+=1; *t = '\0';
          suppress_code = strcmp(s, SourceName);
          /*if (!suppress_code) fprintf(stderr, "#file \"%s\"\n", s);*/
        } else if (s[0] == '"' && (t=strstr(s, "\",132,0,0,"))!=0) {
          s+=1; *t = '\0';
          suppress_code = strcmp(s, SourceName);
          /*if (!suppress_code) fprintf(stderr, "#file \"%s\"\n", s);*/
        } else {
        }
        break;
      case 'n': // .stabn          type,0,desc,value
        break;
      case 'd': // .stabd          type,0,desc
        if (strncmp(s, "68,0,", strlen("68,0,"))==0) {
          if (!suppress_code) {
            if (lineno > trigger) {
              if (to_source_line[asmlineno-1]==0) {
                to_source_line[asmlineno-1] = trigger-1;
                if (debug) fprintf(stderr, "TO{1}[%d] = %d\n", asmlineno-1, trigger-1);
              }
              trigger = MAXC;
              if (debug) fprintf(stderr, "TRIGGER{1} = %d\n", trigger);
            }
            if (pending_lineno && (lineno > pending_lineno)) {
              //to_source_line[asmlineno-1] = lineno;
              to_source_line[asmlineno-1] = pending_lineno;
              if (debug) fprintf(stderr, "TO{2}[%d] = %d\n", asmlineno-1, pending_lineno);
              pending_lineno = 0;
              if (debug) fprintf(stderr, "PENDING_LINENO = 0\n");
            } else {
              s += strlen("68,0,"); // ignore line numbers while in an include file
              lineno = atoi(s);
              if (lineno < last_lineno) {
                // find the most recent from_source_line that is less than lineno.
                int last = asmlineno-1;
                while (from_source_line[last] > lineno) {
                  last -= 1;
                  if (last == 0) break;
                }
                to_source_line[last] = lineno-1;
                if (debug) fprintf(stderr, "TO{3}[%d] = %d\n", last, lineno-1);
                if (to_source_line[last] < from_source_line[last]) {
                  to_source_line[last] = from_source_line[last];
                  if (debug) fprintf(stderr, "TO{4}[%d] = %d\n", last, from_source_line[last]);
                }
                //now fix the other end of the bracket (last_lineno):
                pending_lineno = from_source_line[last+1]-1;
                if (to_source_line[asmlineno-1]==0) {
                  to_source_line[asmlineno-1] = from_source_line[asmlineno-1];
                  if (debug) fprintf(stderr, "TO{5}[%d] = %d\n", asmlineno-1, from_source_line[asmlineno-1]);
                }
                trigger = last_lineno;
                if (debug) fprintf(stderr, "TRIGGER{2} = %d\n", trigger);
                if (debug) fprintf(stderr, "PENDING_LINENO = %d\n", pending_lineno);
              }
              if (pending_lineno && (lineno > pending_lineno)) {
                //to_source_line[asmlineno-1] = lineno;
                to_source_line[asmlineno-1] = pending_lineno;
                if (debug) fprintf(stderr, "TO{6}[%d] = %d\n", asmlineno-1, pending_lineno);
                pending_lineno = 0;
                if (debug) fprintf(stderr, "PENDING_LINENO = 0\n");
                trigger=MAXC;
                if (debug) fprintf(stderr, "TRIGGER{3} = %d\n", trigger);
              }
            }
          } else {
          }
        } else {
        }
        last_lineno = lineno;
        break;
      default:
        fprintf(stderr, "Bad stabs directive: %s\n", assemline);
        exit(EXIT_FAILURE);
      }
    } else {
      asmline[asmlineno] = buffp;
      buffp += sprintf(buffp, "%s", assemline); *buffp++ = '\0';
      to_source_line[asmlineno] = 0;
      from_source_line[asmlineno] = lineno;
      asmlineno += 1;
    }
    if (debug) fprintf(stderr, "@@ suppress=%d asmlineno=%d lineno=%d pending_lineno=%d last_lineno=%d trigger=%d %s\n",
                       suppress_code, asmlineno, lineno, pending_lineno, last_lineno, trigger, assemline);
  }
  // And now we merge the two line arrays!
  // Must be done by asm order.  Doing it by source line order breaks the code.

  asmlineno = 0;
  for (;;) {
    if (asmline[asmlineno]) {
      int from = from_source_line[asmlineno], to = to_source_line[asmlineno];
      if (debug) if (debug) fprintf(stderr, "+++ [%d %d] %s\n", from, to, asmline[asmlineno]);
      if (to_source_line[asmlineno] == 0) {
        if (from_source_line[asmlineno+1] > from_source_line[asmlineno]) {
          to_source_line[asmlineno] = from_source_line[asmlineno+1]-1;
        } else {
          to_source_line[asmlineno] = from_source_line[asmlineno+1];
        }
      }
      from = from_source_line[asmlineno]; to = to_source_line[asmlineno];
      if (debug) fprintf(stderr, "--- [%d %d] %s\n", from, to, asmline[asmlineno]);
    }
    asmlineno += 1;
    if (asmlineno == MAXASM) break; 
  }
  
  asmlineno = 0; last_lineno = 0;
  for (;;) {
    int from = from_source_line[asmlineno], to = to_source_line[asmlineno];
    if (from != last_lineno || to != last_lineno) {
      if (last_lineno == from) last_lineno = from+1; else last_lineno = from;
      for (;;) {
        if (cline[last_lineno]) printed[last_lineno] = 1;// fprintf(stdout, "                                        ; [%d] %s\n", last_lineno, cline[last_lineno]);
        if (last_lineno == to) break;
        last_lineno += 1;
        if (last_lineno == MAXC) break;
      }
      if (last_lineno == MAXC) break;
    }
    asmlineno += 1;
    if (asmlineno == MAXASM) break; 
  }
  
  asmlineno = 0; last_lineno = 0;
  for (;;) {
    int from = from_source_line[asmlineno], to = to_source_line[asmlineno];
    if (from != last_lineno || to != last_lineno) {
      if (last_lineno == from) last_lineno = from+1; else last_lineno = from;
      // insert any missing lines immediately before next line that would follow them.
      while ((last_lineno-1 > 0) && (!printed[last_lineno-1])) {
        last_lineno -= 1;
      }
      for (;;) {
        if (last_lineno && cline[last_lineno] && (!reallyprinted[last_lineno])) {
          fprintf(stdout, "                                        ; [%d] %s\n", last_lineno, cline[last_lineno]);
          reallyprinted[last_lineno] = 1;
        }
        printed[last_lineno] = 1;
        if (last_lineno == to) break;
        last_lineno += 1;
        if (last_lineno == MAXC) break;
      }
    }
    if (debug) fprintf(stdout, "[%d] ", asmlineno);
    if (asmline[asmlineno]) fprintf(stdout, "%s\n", /*from, to,*/ asmline[asmlineno]);
    asmlineno += 1;
    if (asmlineno == MAXASM) break; 
  }

  // shouldn't be any, but just in case, mop up any stragglers.
  last_lineno = 1;
  for (;;) {
    if (cline[last_lineno] && !reallyprinted[last_lineno]) fprintf(stdout, "                                        ; [%d] %s\n", last_lineno, cline[last_lineno]);
    last_lineno += 1;
    if (last_lineno == MAXC) break; 
  }
  
  exit(EXIT_SUCCESS);
  return 0;
}
