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

static int read_line(FILE *f, char *s) {
  int c;
  do { if ((c = fgetc(f)) == EOF || ferror(f)) return 0; } while ((*s++ = c) != '\n'); *--s = '\0';
  return 1;
}

int main(int argc, char **argv) {
  FILE *lis, *cod;
  long here;
  int ch, list_lineno = 1, code_lineno = 0;
  char *s, *param, list[1024], code[1024], list_line[1024], next_list_line[1024], code_line[1024];
  
  if (argc != 2) { fprintf(stderr, "syntax: recode file.imp\n"); exit(EXIT_FAILURE); }

  param = strdup(argv[1]); s = strrchr(param, '.'); if (s && !strchr(s, '/')) *s++ = '\0';

  sprintf(list, "%s.lst", param);
  if (!(lis = fopen(list, "r"))) { fprintf(stderr, "recode: cannot open %s - %s\n", list, strerror(errno)); exit(EXIT_FAILURE); }
  sprintf(code, "%s.cod", param);
  if (!(cod = fopen(code, "r"))) { fprintf(stderr, "recode: cannot open %s - %s\n", code, strerror(errno)); exit(EXIT_FAILURE); }
  
  for (;;) {

    if (!read_line(lis, list_line)) {
      // drain any pending code after %endofprogram, if any (which is unlikely)
      while (read_line(cod, code_line)) {
        if (code_line[0] == ' ' && code_line[1] != ' ') fprintf(stdout, "%s\n", code_line);
      }
      exit(EXIT_SUCCESS);
    }

    if (strlen(list_line) <= 6 || !isdigit(list_line[5])) {
      fprintf(stdout, "%s\n", list_line); // empty lines, "IMP Compiler V...", etc...
    } else {
      fprintf(stdout, "%s\n", list_line);

      // lookahead for continued source lines:
      for (;;) {
        here = ftell(lis);
        if ((!read_line(lis, next_list_line)) || (next_list_line[6] != '+')) break;
        fprintf(stdout, "%s\n", next_list_line);
      }
      fseek(lis, here, SEEK_SET); // backtrack after reading too far.
      
      // Now print code lines up to and including this line number.
      ch = list_line[6]; list_line[6] = '\0'; list_lineno = atol(list_line); list_line[6] = ch;

      if (list_lineno < code_lineno) continue;
      
      // print the code lines up to this line number
      do {
        
        if (!read_line(cod, code_line)) exit(EXIT_SUCCESS);
        
        if (   strcmp(code_line, "      DATA  SEGMENT WORD PUBLIC 'DATA'") == 0
            || strcmp(code_line, "      CONST  SEGMENT WORD PUBLIC 'CONST'") == 0
            || strcmp(code_line, "      _SWTAB  SEGMENT WORD PUBLIC '_SWTAB'") == 0
           ) {
          // skip data segments
          while (read_line(cod, code_line)) {
            if (   strcmp(code_line, "      DATA    ENDS") == 0
                || strcmp(code_line, "      CONST  ENDS") == 0
                || strcmp(code_line, "      _SWTAB   ENDS") == 0
               ) break;
          }
          
          // replenish 'current line'
          if (!read_line(cod, code_line)) {
            // drain any pending code after %endofprogram, if any (which is unlikely)
            while (read_line(cod, code_line)) if (code_line[0] == ' ' && code_line[1] != ' ') fprintf(stdout, "%s\n", code_line);
            exit(EXIT_SUCCESS);
          }
        }
        
        if (strncmp(&code_line[15], "LINE", 4) == 0) {
          // Haven't yet seen what happens with %include files.  Need to match filename as well as line number.
          code_lineno = atol(&code_line[20]);
        } else {
          // A line with hex dump and asm source
          if (code_line[0] == ' ' && code_line[1] != ' ') fprintf(stdout, "%s\n", code_line);
        }
      } while (code_lineno <= list_lineno);
    } // if
  } // for (;;)

  /* NOT REACHED */
  return 1;
}
