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

#ifndef FALSE
#define FALSE 0
#endif
#ifndef TRUE
#define TRUE (!0)
#endif
typedef struct scrap_node {
  struct scrap_node *next;
  int scrap;
} Scrap_Node;
typedef struct name {
  char *spelling;
  struct name *llink;
  struct name *rlink;
  Scrap_Node *defs;
  Scrap_Node *uses;
  int mark;
  char tab_flag;
  char indent_flag;
  char debug_flag;
} Name;

int tex_flag;      /* if FALSE, don't emit the .tex file */
int output_flag;   /* if FALSE, don't emit the output files */
int compare_flag;  /* if FALSE, overwrite without comparison */
char *command_name;
char *source_name;  /* name of the current file */
int source_line;    /* current line in the source file */
Name *file_names;
Name *macro_names;
Name *user_names;

void pass1();
void write_tex();
void write_files();
void source_open(); /* pass in the name of the source file */
int source_get();   /* no args; returns the next char or EOF */
void init_scraps();
int collect_scrap();
int write_scraps();
Name *collect_file_name();
Name *collect_macro_name();
Name *collect_scrap_name();
Name *name_add();
Name *prefix_add();
char *save_string();
void reverse_lists();
void *arena_getmem();
void arena_free();


static void copy_scrap();               /* formats the body of a scrap */
static void print_scrap_numbers();      /* formats a list of scrap numbers */
static void format_entry();             /* formats an index entry */
static void format_user_entry();
void write_tex(file_name, tex_name)
     char *file_name;
     char *tex_name;
{
  FILE *tex_file = fopen(tex_name, "w");
  if (tex_file) {
    source_open(file_name);
    {
      int scraps = 1;
      int c = source_get();
      while (c != EOF) {
        if (c == '@')
          {
            int big_definition = FALSE;
            c = source_get();
            switch (c) {
              case 'O': big_definition = TRUE;
              case 'o': {
                          Name *name = collect_file_name();
                          {
                            fputs("\\begin{flushleft}\n", tex_file);
                            if (!big_definition)
                              fputs("\\begin{minipage}{\\linewidth}\n", tex_file);
                          }
                          fprintf(tex_file, "\\verb@\"%s\"@", name->spelling);
                          fprintf(tex_file, " {\\footnotesize %d }$\\equiv$\n", scraps++);
                          {
                            fputs("\\vspace{-1.5ex}\n\\begin{quote}\n", tex_file);
                            copy_scrap(tex_file);
                            fputs("$\\Diamond$\n\\end{quote}\n\\vspace{-2ex}\n", tex_file);
                          }
                          {
                            if (name->defs->next) {
                              fputs("{\\footnotesize File defined by scraps ", tex_file);
                              print_scrap_numbers(tex_file, name->defs);
                              fputs("}\n", tex_file);
                            }
                          }
                          {
                            if (!big_definition)
                              fputs("\\end{minipage}\\\\[4ex]\n", tex_file);
                            fputs("\\end{flushleft}\n", tex_file);
                            do
                              c = source_get();
                            while (isspace(c));
                          }
                        }
                        break;
              case 'D': big_definition = TRUE;
              case 'd': {
                          Name *name = collect_macro_name();
                          {
                            fputs("\\begin{flushleft}\n", tex_file);
                            if (!big_definition)
                              fputs("\\begin{minipage}{\\linewidth}\n", tex_file);
                          }
                          fprintf(tex_file, "$\\langle$%s", name->spelling);
                          fprintf(tex_file, " {\\footnotesize %d}$\\rangle\\equiv$\n", scraps++);
                          {
                            fputs("\\vspace{-1.5ex}\n\\begin{quote}\n", tex_file);
                            copy_scrap(tex_file);
                            fputs("$\\Diamond$\n\\end{quote}\n\\vspace{-2ex}\n", tex_file);
                          }
                          {
                            fputs("{\\footnotesize ", tex_file);
                            if (name->defs->next) {
                              fputs("Macro defined by scraps ", tex_file);
                              print_scrap_numbers(tex_file, name->defs);
                              fputs("\\\\[-1ex]\n", tex_file);
                            }
                          }
                          {
                            if (name->uses) {
                              if (name->uses->next) {
                                fputs("Macro referenced in scraps ", tex_file);
                                print_scrap_numbers(tex_file, name->uses);
                                fputs("}\n", tex_file);
                              }
                              else
                                fprintf(tex_file, "Macro referenced in scrap %d.}\n", name->uses->scrap);
                            }
                            else {
                              fputs("Macro never referenced.}\n", tex_file);
                              fprintf(stderr, "%s: <%s> never referenced.\n",
                                      command_name, name->spelling);
                            }
                          }
                          {
                            if (!big_definition)
                              fputs("\\end{minipage}\\\\[4ex]\n", tex_file);
                            fputs("\\end{flushleft}\n", tex_file);
                            do
                              c = source_get();
                            while (isspace(c));
                          }
                        }
                        break;
              case 'f': {
                          if (file_names) {
                            fputs("\n\\begin{list}{}{\\setlength{\\itemsep}{-\\parsep}}\n", tex_file);
                            format_entry(file_names, tex_file, TRUE);
                            fputs("\\end{list}", tex_file);
                          }
                          c = source_get();
                        }
                        break;
              case 'm': {
                          if (macro_names) {
                            fputs("\n\\begin{list}{}{\\setlength{\\itemsep}{-\\parsep}}\n", tex_file);
                            format_entry(macro_names, tex_file, FALSE);
                            fputs("\\end{list}", tex_file);
                          }
                          c = source_get();
                        }
                        break;
              case 'u': {
                          if (user_names) {
                            fputs("\n\\begin{list}{}{\\setlength{\\itemsep}{-\\parsep}}\n", tex_file);
                            format_user_entry(user_names, tex_file);
                            fputs("\\end{list}", tex_file);
                          }
                          c = source_get();
                        }
                        break;
              case '@': putc(c, tex_file);
              default:  c = source_get();
                        break;
            }
          }
        else {
          putc(c, tex_file);
          c = source_get();
        }
      }
    }
    fclose(tex_file);
  }
  else
    fprintf(stderr, "%s: can't open %s\n", command_name, tex_name);
}
static void print_scrap_numbers(tex_file, scraps)
     FILE *tex_file;
     Scrap_Node *scraps;
{
  fprintf(tex_file, "%d", scraps->scrap);
  scraps = scraps->next;
  while (scraps->next) {
    fprintf(tex_file, ", %d", scraps->scrap);
    scraps = scraps->next;
  }
  fprintf(tex_file, " and~%d.", scraps->scrap);
}
static void copy_scrap(file)
     FILE *file;
{
  int indent = 0;
  int c = source_get();
  fputs("\\verb@", file);
  while (1) {
    switch (c) {
      case '@':  {
                   c = source_get();
                   switch (c) {
                     case '@': fputs("@{\\tt @}\\verb@", file);
                               break;
                     case '|': {
                                 do {
                                   do
                                     c = source_get();
                                   while (c != '@');
                                   c = source_get();
                                 } while (c != '}');
                               }
                     case '}': putc('@', file);
                               return;
                     case '<': {
                                 Name *name = collect_scrap_name();
                                 fprintf(file, "@$\\langle$%s {\\footnotesize ", name->spelling);
                                 if (name->defs)
                                   fprintf(file, "%d", name->defs->scrap);
                                 else {
                                   putc('?', file);
                                   fprintf(stderr, "%s: scrap never defined <%s>\n",
                                           command_name, name->spelling);
                                 }
                                 fputs("}$\\rangle$\\verb@", file);
                               }
                               break;
                     default:  /* ignore these since pass1 will have warned about them */
                               break;
                   }
                 }
                 break;
      case '\n': fputs("@\\\\\n\\verb@", file);
                 indent = 0;
                 break;
      case '\t': {
                   int delta = 8 - (indent % 8);
                   indent += delta;
                   while (delta > 0) {
                     putc(' ', file);
                     delta--;
                   }
                 }
                 break;
      default:   putc(c, file);
                 indent++;
                 break;
    }
    c = source_get();
  }
}
static void format_entry(name, tex_file, file_flag)
     Name *name;
     FILE *tex_file;
     int file_flag;
{
  while (name) {
    format_entry(name->llink, tex_file, file_flag);
    {
      fputs("\\item \\hspace{-\\leftmargin}", tex_file);
      if (file_flag) {
        fprintf(tex_file, "\\verb@\"%s\"@ ", name->spelling);
        {
          Scrap_Node *p = name->defs;
          fputs("{\\footnotesize Defined by scrap", tex_file);
          if (p->next) {
            fputs("s ", tex_file);
            print_scrap_numbers(tex_file, p);
          }
          else
            fprintf(tex_file, " %d.", p->scrap);
          putc('}', tex_file);
        }
      }
      else {
        fprintf(tex_file, "$\\langle$%s {\\footnotesize ", name->spelling);
        {
          Scrap_Node *p = name->defs;
          if (p) {
            while (1) {
              fprintf(tex_file, "%d", p->scrap);
              p = p->next;
              if (!p) break;
              fputs(", ", tex_file);
            }
          }
          else
            putc('?', tex_file);
        }
        fputs("}$\\rangle$ ", tex_file);
        {
          Scrap_Node *p = name->uses;
          fputs("{\\footnotesize ", tex_file);
          if (p) {
            fputs("Referenced in scrap", tex_file);
            if (p->next) {
              fputs("s ", tex_file);
              print_scrap_numbers(tex_file, p);
            }
            else
              fprintf(tex_file, " %d.", p->scrap);
          }
          else
            fputs("Not referenced.", tex_file);
          putc('}', tex_file);
        }
      }
      putc('\n', tex_file);
    }
    name = name->rlink;
  }
}
static void format_user_entry(name, tex_file)
     Name *name;
     FILE *tex_file;
{
  while (name) {
    format_user_entry(name->llink, tex_file);
    {
      Scrap_Node *uses = name->uses;
      Scrap_Node *defs = name->defs;
      fprintf(tex_file, "\\item \\hspace{-\\leftmargin}\\verb@%s@: ",
              name->spelling);
      do {
        if (uses && (!defs || uses->scrap < defs->scrap)) {
          fprintf(tex_file, "%d", uses->scrap);
          uses = uses->next;
        }
        else if (defs && (!uses || defs->scrap <= uses->scrap)) {
          if (uses && defs->scrap == uses->scrap)
            uses = uses->next;
          fprintf(tex_file, "\\underline{%d}", defs->scrap);
          defs = defs->next;
        }
        if (uses || defs) fputs(", ", tex_file);
      } while (uses || defs);
      fputs(".\n", tex_file);
    }
    name = name->rlink;
  }
}
