/*

A utility to merge the asm output with the C source.  This needs vbcchc12 to have been modified
to output:   ; <line> "<file>"
in the asm file, similar to what the 6502 compiler already does.


Create a shell script vrecode containing:
#!/bin/bash
cp $1 `basename $2 .o`.asm
recode `basename $2 .o`.asm > `basename $2 .o`.lis
cat `basename $2 .o`.lis


Set the vc.config entry for 'as' to
-as=vrecode %s %s

(have the vrecode script execute as if you actually want to do the assembly as well as see the compiler output)

 */

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

// TO DO: remove the noise associated with embedded asm() statements...
//        parameterise the program with argv.

char *getsourceline(FILE *f) {
  static char p[1024], *s;
  s = p;
  for (;;) {
    int c = fgetc(f);
    if (ferror(f) || c == EOF) {
      *s = '\0';
      if (s == p) return NULL;
      return strdup(p);
    }
    if (c == '\r') continue;
    if (c == '\n') {
      *s = '\0';
      return strdup(p);
    }
    *s++ = c;
  }
}

void output(FILE *f, char *s) {
  while (*s != '\0') {
    int c = *s++;
    if (c == ' ' || c == '\t' || c == '\n' || ((c > 32) && (c < 127))) fputc(c, f);
  }
}

int main(int argc, char **argv) {
  FILE *asm_source;
  FILE *c_source;
  FILE *list = NULL;
  char *line;
  int source_line, actual_source_line = 0;
  char source_file[1024];
  char target_filename[1024];
  int wrong_file = 1;
  int wrong_section = 1;
  
  asm_source = fopen(argv[1], "r");;
  if (asm_source == NULL) {
    fprintf(stderr, "recode: cannot open %s - %s\n", argv[1], strerror(errno));
    exit(EXIT_FAILURE);
  }

  list = stdout;
  
  strcpy(target_filename, argv[1]);
  if (strcmp(&target_filename[strlen(argv[1])-strlen(".asm")], ".asm") == 0) {
    strcpy(&target_filename[strlen(argv[1])-strlen(".asm")], ".c");
  }
  c_source = fopen(target_filename, "r");
  if (c_source == NULL) {
    fprintf(stderr, "recode: cannot open %s - %s\n", target_filename, strerror(errno));
    exit(EXIT_FAILURE);
  }

  fprintf(list, "\n                         vbcc V0.9h (c) in 1995-2022 by Volker Barthelmann\n\nSource: %s\n\n", target_filename);
  while ((line=getsourceline(asm_source))!=NULL) {
    if (line[0] == ';') {
      int templine;
      int rc = sscanf(line, "; %d \"%s", &templine, source_file);
      if (rc == 2) {
        if (source_file[strlen(source_file)-1] == '"') source_file[strlen(source_file)-1] = '\0'; 
        if (strcmp(source_file, target_filename)==0) {
          source_line = templine;
          //fprintf(list, "MATCH: %s  GET SOURCE UP TO LINE %d\n", source_file, source_line);
          for (;;) {
            free(line);
            line = getsourceline(c_source);
            if (line == NULL) exit(EXIT_FAILURE);
            actual_source_line += 1;
            fprintf(list, "   %5d  %s\n", actual_source_line, line);
            // Could wrap long lines here...
            if (actual_source_line >= source_line) break;
          }
          wrong_file = 0;
        } else {
          wrong_file = 1;
          //fprintf(stderr, "NO MATCH: %s %s\n", target_filename, source_file);
        }
      }
    } else if (strncmp(line, "\t.text", strlen("\t.text"))==0) {
      wrong_section=0;
      //fprintf(stderr, "TEXT\n");
    } else if (strncmp(line, "\t.section\t", strlen("\t.section\t"))==0) {
      wrong_section=1;
      //fprintf(stderr, "NOT TEXT: %s\n", line);
    } else if (!wrong_file && !wrong_section) {
      // some lines we're not interested in
      if (strncmp(line, "\t.type\t", strlen("\t.type\t"))==0) {
      } else if (strncmp(line, "\t.size\t", strlen("\t.size\t"))==0) {
      } else if (strncmp(line, "# stacksize", strlen("# stacksize"))==0) {
      } else if (strncmp(line, "\t.equ\t", strlen("\t.equ\t"))==0) {
      } else {
        fprintf(list, "%s\n", line);
      }
    }
    free(line);
  }
  for (;;) {
    free(line);
    line = getsourceline(c_source);
    if (line == NULL) break;
    actual_source_line += 1;
    fprintf(list, "   %5d  %s\n", actual_source_line, line);
    // Could wrap long lines here...
    if (actual_source_line >= source_line) break;
  }
  exit(EXIT_SUCCESS);
  return EXIT_FAILURE;
}
