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


int getparm(FILE *f, char *buf)
{
  int c, level=0;

  c=fgetc(f);
  switch(c)
  {
    case EOF: fputs("dn2: unexpected end of file\n", stderr); return 1;
    case '{': break;
    default:  fputs("dn2: missing parameter\n", stderr); return 1;
  }
  while((c=fgetc(f))!=EOF)
  {
    switch(c)
    {
      case '{': level++; *buf++=c; break;
      case '}': if(!level--) { *buf=0; return 0; } /* else pass thru */
      default: *buf++=c;
    }
  }
  fputs("dn2: unexpected end of file\n", stderr); return 1;
}


int gethex(FILE *f, int *x)
{
  char buf[20];
  int c,i=0;

  while((c=fgetc(f))!=EOF && isxdigit(c))
    buf[i++]=c;
  buf[i]=0;
  if(i>0)
    return sscanf(buf, "%x", x)!=1;
  fputs("dn2: bad hexadecimal number\n", stderr);
  return 1;
}


int getnum(FILE *f, int *x)
{
  char buf[20];
  int c,i=0;

  while((c=fgetc(f))!=EOF && isdigit(c))
    buf[i++]=c;
  buf[i]=0;
  if(i>0)
    return sscanf(buf, "%d", x)!=1;
  fputs("dn2: bad decimal number\n", stderr);
  return 1;
}


void process(FILE *fi, FILE *fo)
{
  int blevel=0, dnparse=-1, c, c1, c2, n, lastc=EOF, active=0;
  static const char *id="\\dn ";
  char buf[256];

  while((c=fgetc(fi))!=EOF)
  {
    switch(c)
    {
      case '%':
        if(lastc!='\\')
        {
          fputc(c,fo);
          while((c=fgetc(fi))!=EOF)
          {
            fputc(c,fo);
            if(c=='\n')
              break;
          }
          if(c==EOF)
            goto bye;
        }
        else
          fputc(c,fo);
        break;
      case '{':
        blevel++;
        if(!active) dnparse=0;
        fputc(c,fo);
        break;
      case '}':
        if(!blevel--)
        {
          blevel=0;
          fputs("dn2: unexpected '}'\n", stderr);
        }
        if(blevel<active)
        {
          if(active && lastc=='\\')
            fputc(lastc,fo);
          active=0;
        }
        fputc(c,fo);
        break;
      default:
        if(dnparse>=0)
        {
          if(id[dnparse]!=c && (id[dnparse]!=' ' || !isspace(c)))
            dnparse=-1;
          else
            if(!id[++dnparse])
            {
              fputc(c,fo);
              dnparse=-1;
              active=blevel;
              break;
            }
        }
        if(active)
        {
          if(c=='\"')
            fputs("\\symbol{34}",fo); 
          else
            if(lastc=='\\')
              switch(c)
              {
                case '0':
                  fputs("{\\llap{\\char13}}", fo);
                  break;
                case '1':
                  fputs("{\\llap{\\char32}}", fo);
                  break;
                case '2':
                  fputs("{\\llap{\\char92}}", fo);
                  break;
                case '3':
                  if(!gethex(fi, &n))
                    fprintf(fo, "{\\char%d}", n);
                  break;
                case '4':
                  fputs("{\\llap{\\char123}}", fo);
                  break;
                case '5':
                  fputs("{\\llap{\\char125}}", fo);
                  break;
                case '6':
                  if(!getparm(fi,buf))
                    fprintf(fo, "{\\setbox0=\\hbox{%s}%s\\subscr{\\char126}}", buf, buf);
                  break;
                case '7':
                  if(!getparm(fi,buf))
                    fprintf(fo, "{\\setbox0=\\hbox{%s}%s\\subscr{\\char0}}", buf, buf);
                  break;
                case '8':
                  if(!getparm(fi,buf))
                    fprintf(fo, "{\\setbox0=\\hbox{%s}%s\\subscr{\\char1}}", buf, buf);
                  break;
                case '9':
                  if(!getparm(fi,buf))
                    fprintf(fo, "{\\setbox0=\\hbox{%s}%s\\subscr{\\char2}}", buf, buf);
                  break;
                case 'q':
                  c1=fgetc(fi);
                  if(c1==EOF) { fputc(c,fo); goto bye; }
                  switch(c1)
                  {
                    case 'a':
                      if(!getparm(fi,buf) && !getnum(fi,&n))
                        fprintf(fo, "{\\setbox0=\\hbox{%s}%s"
                                    "\\subscr{\\char253\\kern1.5ex\\lower1.25ex"
                                    "\\hbox{\\char%d}\\kern-1.5ex}}", buf, buf, n);
                      break;
                    case 'b':
                      if(!getparm(fi,buf))
                        fprintf(fo, "{\\setbox0=\\hbox{%s}%s\\subscr{\\char253}}", buf, buf);
                      break;
                    case 'q':
                      if(!getparm(fi,buf))
                        fprintf(fo, "{\\setbox0=\\hbox{%s}%s\\subscr{\\char94}}", buf, buf);
                      break;
                    case 'x':
                      if(!getparm(fi,buf))
                        fprintf(fo, "{\\setbox0=\\hbox{%s}%s\\subscr{\\char14}}", buf, buf);
                      break;
                    case 'y':
                      if(!getparm(fi,buf))
                        fprintf(fo, "{\\setbox0=\\hbox{%s}%s\\subscr{\\char31}}", buf, buf);
                      break;
                    case 'z':
                      if(!getparm(fi,buf))
                        fprintf(fo, "{\\setbox0=\\hbox{%s}%s\\subscr{\\char124}}", buf, buf);
                      break;
                    case 'v':
                      c2=fgetc(fi);
                      if(c2==EOF) { fputc(c,fo); fputc(c1,fo); goto bye; }
                      switch(c2)
                      {
                        case 'a':
                          fprintf(fo, "{\\kern0.5ex"
                                      "{\\llap{\\char92}}"
                                      "\\kern-0.5ex}");
                          break;
                        case 'b':
                          fprintf(fo, "{\\kern1ex"
                                      "{\\llap{\\char13}}"
                                      "\\kern-1ex}");
                          break;
                        case 'c':
                          fprintf(fo, "{\\kern1ex\\rdt\\kern-1ex}");
                          break;
                        default:
                          fputc(lastc,fo);
                          fputc(c,fo);
                          fputc(c1,fo);
                          ungetc(c2,fi);
                      }
                      break;
                    default:
                      fputc(lastc,fo);
                      fputc(c,fo);
                      ungetc(c1,fi);
                  }
                  break;
                default:
                  fputc(lastc,fo);
                  fputc(c,fo);
              }
            else
              if(c!='\\' || lastc=='\\')
                fputc(c,fo);
        }
        else
          fputc(c,fo);
    }
    lastc=c;
  }
 bye:;
}


int main(int argc, char *argv[])
{
  if(argc<=1)
    process(stdin, stdout);
  else
    if(argc==3)
    {
      FILE *ifi, *tfi, *ofi;

      ifi=fopen(argv[1],"r");
      if(ifi==NULL)
      {
        fprintf(stderr,"dn2: cannot open %s\n", argv[1]);
        exit(11);
      }
      ofi=fopen(argv[2],"w");
      if(ofi==NULL)
      {
        fprintf(stderr,"dn2: cannot open %s\n", argv[2]);
        exit(11);
      }
      tfi=tmpfile();
      if(tfi==NULL)
      {
        fprintf(stderr,"dn2: cannot open tempfile\n");
        exit(11);
      }
      process(ifi,tfi); rewind(tfi);
      process(tfi,ofi);
      fclose(tfi);
      fclose(ifi); fclose(ofi);
    }
    else
    {
      fputs("dn2: please supply input and output file or use with pipes\n", stderr);
      exit(10);
    }
  return 0;
}
