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

#define LINELEN 1024
#define FILENAMELEN 1024
#define BIGLINECOUNT 100000
#define SOURCELINECOUNT 10000

#define TAB "\t"
#define TABCH '\t'

int readline(FILE *f, char *line) {
  int c;
  for (;;) {
    c = fgetc(f);

    //if (c == '\t') c = '_'; // FOR DEBUGGING ONLY
    //#undef TAB
    //#undef TABCH
    //#define TAB "_"
    //#define TABCH '_'

    if (c == EOF) return 0;
    if (c == '\n') break;
    *line++ = c&255;
  }
  *line = '\0';
  return 1;
}

int main(int argc, char **argv) {
  static char line[LINELEN];
  char armlistname[FILENAMELEN];
  char *line_to_save = NULL;
  char *decode[SOURCELINECOUNT];
  char *current_filename = NULL;
  int current_lineno = 0;
  FILE *armlistfile;
  int /*keep_lines,*/ gotlines = 0;

  for (int i = 0; i < SOURCELINECOUNT; i++) {
    decode[i] = strdup("");
  }
  
  sprintf(armlistname, "%s.lst", argv[1]);
  armlistfile = fopen(armlistname, "r");
  if (armlistfile == NULL) {
    fprintf(stderr, "Cannot open %s - %s\n", armlistname, strerror(errno));
    exit(EXIT_FAILURE);
  }
  
  while (readline(armlistfile, line)) { // readline must remove \n from end of line
    char *s = strdup(line);
    char *orig = s;

    line_to_save = NULL;
    if (*s == 'D') {
      // Directive, e.g.
      // D|12055|.loc 1 1288 4 view .LVU4618
      // D|12059|.cfi_endproc
      // D|12062|.section        .rodata
      s = strchr(s, '|')+1;
      s = strchr(s, '|')+1;
      if (
          (strncmp(s, ".cfi", 4) == 0) ||
          (strncmp(s, ".loc", 4) == 0) ||
          (strncmp(s, ".arch", 5) == 0) ||
          (strncmp(s, ".text", 5) == 0) ||
          (strncmp(s, ".file", 5) == 0)
        ) {
        // suppress some directives but not all
      } else {
        *--s = '\t';
        *--s = '\t';
        *--s = '\t';
        *--s = '\t';
        line_to_save = strdup(s);
        gotlines += 1;
        free(orig);
      }
    } else if (*s == 'P') {
      // Procedure, e.g.
      // P|8584|GETCOND.10:
      // P|8809|main:
      // P|12067|__PRETTY_FUNCTION__.47:
      s = strchr(s, '|')+1;
      s = strchr(s, '|')+1;
      *--s = '\t';
      *--s = '\t';
      line_to_save = strdup(s);
      gotlines += 1;
      free(orig);
    } else if (*s == 'J') {
      // Jump destination, e.g.
      // J|12047|.L762:
      // J|12060|.LFE199:
      // J|12080|.LC0:
      // J|12259|.Letext0:
      s = strchr(s, '|')+1;
      s = strchr(s, '|')+1;
      *--s = '\t';
      *--s = '\t';
      line_to_save = strdup(s);
      gotlines += 1;
      free(orig);
    } else if (*s == 'I') {
      // Instruction (or data), e.g.
      // I|1594|06ec FD030091|           mov     x29, sp
      // I|1597|06f0 01000090|           adrp    x1, :got:_imp_InStream;ldr      x1, [x1, :got_lo12:_imp_InStream]
      // I|1597|     210040F9|
      // I|1598|06f8 200000B9|           str     w0, [x1]
      // I|12253|00c8 00000000|          .xword  .L297
      // I|12253|     00000000|
      s = strchr(s, '|')+1;
      s = strchr(s, '|')+1;
      s = strchr(s, '|')+1;
      *--s = '\t';
      *--s = '\t';
      line_to_save = strdup(s);
      gotlines += 1;
      free(orig);
    } else if (*s == 'S') {
      // Source, e.g.
      // S|1390|regression-compile-tmp/hal7502.imp|    PUTPOS = STARTPOS;  PUT CHAR(0)
      s = strchr(s, '|')+1;
      char *e = strchr(s, '|')+1;
      *e = '\0';
      current_lineno = (int)atol(s);
      free(orig);
    } else {
      //fprintf(stderr, "* Bad line: %s\n", s); exit(EXIT_FAILURE);
    }

    if (line_to_save != NULL) {
      char *oldline = decode[current_lineno];
      int newlen = strlen(oldline) + strlen(line_to_save) + 2;
      char *newline = malloc(newlen);
      sprintf(newline, "%s%s\n", oldline, line_to_save);
      decode[current_lineno] = newline;
      free(oldline);
    }
    
  }
  fclose(armlistfile);
  
  sprintf(armlistname, "%s.lis", argv[1]);
  armlistfile = fopen(armlistname, "r");
  if (armlistfile == NULL) {
    fprintf(stderr, "Cannot open %s - %s\n", armlistname, strerror(errno));
    exit(EXIT_FAILURE);
  }

  int last_output_line = -1;
  while (readline(armlistfile, line)) { // readline must remove \n from end of line
    if (strlen(line) > 4 && isdigit(line[4]) && (strcmp(&line[5], " Statements compiled") != 0)) {
      line[5] = '\0';
      int source_line = (int)atol(line);
      fprintf(stdout, "%5d %s\n", source_line, &line[7]);
      if (source_line > last_output_line) {
        for (int i = last_output_line+1; i <= source_line; i++) {
          if (*decode[i] != '\0') printf("%s", decode[i]); 
        }
      }
      last_output_line = source_line;
    } else {
      fprintf(stdout, "%s\n", line);
    }
  }
  fclose(armlistfile);
  for (int i = last_output_line+1; i < SOURCELINECOUNT; i++) {
    if (*decode[i] != '\0') printf("%s", decode[i]); 
  }
  
  exit(EXIT_SUCCESS);
  return EXIT_FAILURE;
}



#ifdef NEVER
    if (s[4] == ':') {
      char *checkfilename = &s[5]; s[4] = '\0';
      current_lineno = (int)atol(s);
      while (checkfilename[0] != '\0' && checkfilename[strlen(checkfilename)-1] == ' ') {
        checkfilename[strlen(checkfilename)-1] = '\0';
      }
      if (current_filename == NULL) current_filename = strdup(checkfilename);
      if (1 || strcmp(current_filename, checkfilename) == 0) {
        //fprintf(stderr, "[%s:%d] @ %s\n", checkfilename, current_lineno, s, e);
        //keep_lines = 1;
      } else {
        //keep_lines = 0;
      }
    } else { // if (keep_lines) {
      int tabbed;
      tabbed = 0;
      if (*e == ' ' && e[1] == TABCH) {
        e += 1;
        if (*e == TABCH) tabbed = 1;
        while (*e == TABCH) e += 1;
      }
      if (
          (strncmp(e, ".cfi", 4) == 0) ||
          (strncmp(e, ".loc", 4) == 0) ||
          (strncmp(e, ".arch", 5) == 0) ||
          (strncmp(e, ".text", 5) == 0) ||
          (strncmp(e, ".file", 5) == 0)
        ) {
        // suppress some directives but not all
        // fprintf(stderr, "%s @ \"%s\"\n", s, e);
      } else {
        char tmp[LINELEN];
        if (tabbed) {
          sprintf(tmp, "%s" TAB TAB "{%s}\n", &s[5], &e[0]);
        } else {
          sprintf(tmp, "%s%s\n", &s[5], &e[0]);
        }
        int newlen = strlen(decode[current_lineno]) + strlen(tmp) + 1;
        char *newline = malloc(newlen);
        sprintf(newline, "%s%s", decode[current_lineno], tmp);
        char *oldline = decode[current_lineno];
        decode[current_lineno] = newline;
        free(oldline);
      }
    }
#endif

    
