/* mdecho.c (emx+gcc) */

#include <stdio.h>
#include <sys/moddef.h>

struct echo_data
{
  enum echo_state
    {
      OTHER, IMPORTS, EXPORTS, SEGMENTS
    } state;
  const char *fname;
  int export_only;
};

static void state (struct echo_data *data, enum echo_state new_state)
{
  if (new_state != data->state)
    {
      switch (new_state)
        {
        case IMPORTS:
          printf ("IMPORTS\n");
          break;
        case EXPORTS:
          printf ("IMPORTS\n");
          break;
        case SEGMENTS:
          printf ("SEGMENTS\n");
          break;
        default:
          abort ();
        }
      data->state = new_state;
    }
}


#define SEG_ATTR(x) if (stmt->segment.attr & _MDS_##x) printf (" " #x)

static void seg_attr (const _md_stmt *stmt)
{
  SEG_ATTR (ALIAS);
  SEG_ATTR (CONFORMING);
  SEG_ATTR (DISCARDABLE);
  SEG_ATTR (EXECUTEONLY);
  SEG_ATTR (EXECUTEREAD);
  SEG_ATTR (FIXED);
  SEG_ATTR (INVALID);
  SEG_ATTR (IOPL);
  SEG_ATTR (LOADONCALL);
  SEG_ATTR (MIXED1632);
  SEG_ATTR (MOVEABLE);
  SEG_ATTR (MULTIPLE);
  SEG_ATTR (NOIOPL);
  SEG_ATTR (NONCONFORMING);
  SEG_ATTR (NONDISCARDABLE);
  SEG_ATTR (NONE);
  SEG_ATTR (NONSHARED);
  SEG_ATTR (PRELOAD);
  SEG_ATTR (READONLY);
  SEG_ATTR (READWRITE);
  SEG_ATTR (SHARED);
  SEG_ATTR (SINGLE);
}


static void string (const char *s)
{
  putchar ('"');
  while (*s != 0)
    {
      putchar (*s);
      if (*s == '"')
        putchar (*s);
      ++s;
    }
  putchar ('"');
}


static int echo (struct _md *md, const _md_stmt *stmt, _md_token token,
                 void *arg)
{
  struct echo_data *data = arg;

  if (data->export_only && token != _MD_EXPORTS && token != _MD_parseerror)
    return (0);

  switch (token)
    {
    case _MD_BASE:
      state (data, OTHER);
      printf ("BASE 0x%lx\n", stmt->base.addr);
      break;

    case _MD_CODE:
      state (data, OTHER);
      printf ("CODE");
      seg_attr (stmt);
      printf ("\n");
      break;

    case _MD_DATA:
      state (data, OTHER);
      printf ("DATA");
      seg_attr (stmt);
      printf ("\n");
      break;

    case _MD_DESCRIPTION:
      state (data, OTHER);
      printf ("DESCRIPTION ");
      string (stmt->descr.string);
      printf ("\n");
      break;

    case _MD_EXETYPE:
      state (data, OTHER);
      printf ("EXETYPE");
      switch (stmt->exetype.type)
        {
        case _MDX_DEFAULT:
          break;
        case _MDX_OS2:
          printf (" OS2");
          break;
        case _MDX_UNKNOWN:
          printf (" UNKNOWN");
          break;
        case _MDX_WINDOWS:
          printf (" WINDOWS");
          if (stmt->exetype.major_version != 0
              || stmt->exetype.minor_version != 0)
            printf (" %d", stmt->exetype.major_version);
          if (stmt->exetype.minor_version != 0)
            printf (".%d", stmt->exetype.minor_version);
          break;
        default:
          abort ();
        }
      printf ("\n");
      break;

    case _MD_EXPORTS:
      state (data, EXPORTS);
      printf ("  %s", stmt->export.entryname);
      if (stmt->export.internalname[0] != 0)
        printf (" = %s", stmt->export.internalname);
      if (stmt->export.flags & _MDEP_ORDINAL)
        printf (" @%u", (unsigned)stmt->export.ordinal);
      if (stmt->export.flags & _MDEP_RESIDENTNAME)
        printf (" RESIDENTNAME");
      if (stmt->export.flags & _MDEP_NONAME)
        printf (" NONAME");
      if (stmt->export.flags & _MDEP_NODATA)
        printf (" NODATA");
      if (stmt->export.flags & _MDEP_PWORDS)
        printf (" %d", stmt->export.pwords);
      printf ("\n");
      break;

    case _MD_HEAPSIZE:
      state (data, OTHER);
      printf ("HEAPSIZE ");
      if (stmt->heapsize.maxval)
        printf ("MAXVAL\n");
      else
        printf ("0x%lx\n", stmt->heapsize.size);
      break;

    case _MD_IMPORTS:
      state (data, IMPORTS);
      printf ("  ");
      if (stmt->import.internalname[0] != 0)
        printf ("%s = ", stmt->import.internalname);
      printf ("%s.", stmt->import.modulename);
      if (stmt->import.flags & _MDIP_ORDINAL)
        printf ("%u\n", (unsigned)stmt->import.ordinal);
      else
        printf ("%s\n", stmt->import.entryname);
      break;

    case _MD_LIBRARY:
      state (data, OTHER);
      printf ("LIBRARY %s", stmt->library.name);
      switch (stmt->library.init)
        {
        case _MDIT_DEFAULT:
          break;
        case _MDIT_INSTANCE:
          printf (" INITINSTANCE");
          break;
        case _MDIT_GLOBAL:
          printf (" INITGLOBAL");
          break;
        default:
          abort ();
        }
      switch (stmt->library.term)
        {
        case _MDIT_DEFAULT:
          break;
        case _MDIT_INSTANCE:
          printf (" TERMINSTANCE");
          break;
        case _MDIT_GLOBAL:
          printf (" TERMGLOBAL");
          break;
        default:
          abort ();
        }
      printf ("\n");
      break;

    case _MD_NAME:
      state (data, OTHER);
      printf ("NAME %s", stmt->name.name);
      switch (stmt->name.pmtype)
        {
        case _MDT_DEFAULT:
          break;
        case _MDT_WINDOWAPI:
          printf (" WINDOWAPI");
          break;
        case _MDT_WINDOWCOMPAT:
          printf (" WINDOWCOMPAT");
          break;
        case _MDT_NOTWINDOWCOMPAT:
          printf (" NOTWINDOWCOMPAT");
          break;
        default:
          abort ();
        }
      if (stmt->name.newfiles)
        printf (" NEWFILES");
      printf ("\n");
      break;

    case _MD_OLD:
      state (data, OTHER);
      printf ("OLD ");
      string (stmt->old.name);
      printf ("\n");
      break;

    case _MD_PHYSICAL:
      state (data, OTHER);
      printf ("PHYSICAL DEVICE");
      if (stmt->device.name[0] != 0)
        printf (" %s", stmt->device.name);
      printf ("\n");
      break;

    case _MD_PROTMODE:
      state (data, OTHER);
      printf ("PROTMODE\n");
      break;

    case _MD_REALMODE:
      state (data, OTHER);
      printf ("REALMODE\n");
      break;

    case _MD_SEGMENTS:
      state (data, SEGMENTS);
      printf ("  ");
      string (stmt->segment.segname);
      if (stmt->segment.classname[0] != 0)
        {
          printf (" CLASS ");
          string (stmt->segment.classname);
        }
      seg_attr (stmt);
      printf ("\n");
      break;

    case _MD_STACKSIZE:
      state (data, OTHER);
      printf ("STACKSIZE 0x%lx\n", stmt->stacksize.size);
      break;

    case _MD_STUB:
      state (data, OTHER);
      printf ("STUB ");
      if (stmt->stub.none)
        printf ("NONE");
      else
        string (stmt->stub.name);
      printf ("\n");
      break;

    case _MD_VIRTUAL:
      state (data, OTHER);
      printf ("VIRTUAL DEVICE");
      if (stmt->device.name[0] != 0)
        printf (" %s", stmt->device.name);
      printf ("\n");
      break;

    case _MD_parseerror:
      switch (stmt->error.code)
        {
        case _MDE_IO_ERROR:
          perror (data->fname);
          break;
        case _MDE_EMPTY:
          return (0);
        default:
          fprintf (stderr, "%s:%ld: %s\n",
                   data->fname, _md_get_linenumber (md),
                   _md_errmsg (stmt->error.code));
          break;
        }
      return (1);

    default:
      abort ();
    }
  return (0);
}


int main (int argc, char *argv[])
{
  struct _md *md;
  struct echo_data data;
  int rc, i;

  data.export_only = 0;
  i = 1;
  if (i < argc && strcmp (argv[i], "-e") == 0)
    {
      data.export_only = 1;
      ++i;
    }
  if (argc - i != 1)
    {
      fputs ("Usage: mdecho [-e] <file>\n", stderr);
      return (1);
    }
  if (strcmp (argv[i], "-") == 0)
    {
      data.fname = "stdin";
      md = _md_use_file (stdin);
    }
  else
    {
      data.fname = argv[i];
      md = _md_open (data.fname);
    }
  if (md == NULL)
    {
      perror (data.fname);
      return (1);
    }
  data.state = OTHER;
  _md_next_token (md);
  rc = _md_parse (md, echo, &data);
  _md_close (md);
  return (rc);
}
