#line 1 "ansidecl.d"
/* Programm zum Umwandeln von Funktionsdeklarationen vom traditionellen Stil */
/* in die ANSI-Syntax bei nicht allzu übel geformten C-Programmen */
/* Bruno Haible 16.2.1993 */

/* Ziel: */
/* 1. Token &! streichen. */
/* 2. Deklarationen */
/*           funname(vname1,...,vnamek) */
/*             sspec1 tspec1 vname1 ; */
/*             ...; */
/*             sspeck tspeck vnamek ; */
/*             { */
/*    umwandeln in */
/*           funname ( sspec1 tspec1 vname1 , ..., sspeck tspeck vnamek ) { */
/*    unter Beibehaltung aller Kommentare, Präprozessor-Kommandos usw. */

/* Methode: */
/* Mit Kenntnis der Begriffe "Präprozessor-Kommando", "Kommentar", "Token" */
/* wird nach den öffnenden geschweiften Klammern '{' des äußersten */
/* Schachtelungslevels gesucht, denen ein Strichpunkt ';' oder */
/* eine Klammer zu ')' unmittelbar vorangeht. */
/* Von dort aus wird (unter Mitzählen der Strichpunkte im äußersten */
/* Schachtelungslevel) die vorige Klammer zu ')' des äußersten Schachtelungs- */
/* levels gesucht. Die Strichpunkte werden in Kommata bzw. eine Klammer zu */
/* umgewandelt. Bis zur vorigen Klammer auf '(' des äußersten Schachtelungs- */
/* levels wird alles durch Leerstellen ersetzt. */


#define MAXARGCOUNT  50  /* maximale Anzahl Argumente einer Funktionsdefinition */
#define MAXHEADERLEN  5000  /* maximale Länge eines Funktionsdefinitions-Kopfes */

#define local static
#define global
#define var
#define loop  while (1)
#define until(exp)  while (!(exp))
typedef unsigned char  uintB;
typedef unsigned short  uintW;
typedef unsigned long  uintL;
typedef int  boolean;
#define FALSE 0
#define TRUE 1
#define NULL ((void*)0)

#if defined(__TURBOC__) || defined(__GO32__) || defined(__WATCOMC__)
#define STDC_HEADERS 1
#else
#if defined(unix) || defined(__unix)
#include "config.h"
#endif
#endif
#ifdef STDC_HEADERS
#include <stdlib.h>
#endif

#include <stdio.h>

local FILE* infile;
local FILE* outfile;

/* Input */
/* ===== */

local uintL input_line;

local int in_char (void)
  { var int c = getc(infile);
    if (c=='\n') { input_line++; }
    return c;
  }

local int peek_char (void)
  { var int c = getc(infile);
    if (!(c==EOF)) { ungetc(c,infile); }
    return c;
  }

local uintL last_good_input_line;

/* Output */
/* ====== */

/* Output kann immer ein wenig gepuffert werden: */
enum out_mode { direct, buffered };
local struct { enum out_mode mode; /* Output-Modus */
               uintB buffer[MAXHEADERLEN]; /* Buffer */
               uintL buffindex; /* Index in den Buffer */
             }
      out;

#define char_out(char)  putc(char,outfile)

/* Output-Bufferung ausschalten: */
local void outbuffer_off (void)
  { if (out.mode==buffered)
      { var uintL index = 0;
        while (index < out.buffindex)
          { char_out(out.buffer[index]); index++; }
        out.mode = direct;
  }   }

/* Output-Bufferung ausschalten und dabei an einer Stelle einen String einfügen: */
local void outbuffer_off_insert (uintL insertpoint, char* insert)
  { if (out.mode==buffered)
      { var uintL index = 0;
        loop
          { if (index==insertpoint)
              { while (!(*insert==0)) { char_out(*insert++); } }
            if (index == out.buffindex) break;
            char_out(out.buffer[index]); index++;
          }
        out.mode = direct;
  }   }

/* Output-Bufferung einschalten: */
local void outbuffer_on (void)
  { if (out.mode==direct)
      { out.buffindex = 0;
        out.mode = buffered;
  }   }

/* Character ausgeben: */
local void out_char (int c)
  { if (out.mode==buffered)
      { if (out.buffindex < MAXHEADERLEN)
          { out.buffer[out.buffindex++] = c; }
          else
          /* Buffer voll -> Buffer abschalten */
          { outbuffer_off(); char_out(c); }
      }
      else
      { char_out(c); }
  }

/* lexikalische Analyse */
/* ==================== */

/* Holt das nächste Character: */
local int next_char (void)
  { var int c = in_char();
    if (!(c==EOF))
      { out_char(c); } /* c auch ausgeben */
    return c;
  }

/* Für unsere Zwecke brauchen ++ -> != usw. nicht als eigene Token betrachtet */
/* zu werden, wir kennen also nur: */
/*   EOF */
/*   Identifier */
/*   Zahl-Konstanten */
/*   Character-Konstanten */
/*   String-Konstanten */
/*   Operator/Separator */
/* Verallgemeinerte Tokens: Expressions (mit balancierten Klammern) */
enum token_type { eof, eol, ident, number, charconst, stringconst, sep, expr };
typedef struct { enum token_type type;
                 /* falls Bufferung aktiv: */
                 uintL startindex; /* Startindex im Buffer */
                 uintL endindex; /* Endindex im Buffer */
                 /* bei sep (Operator/Separator): */
                 uintB ch;
               }
        token_;
typedef token_* Token;

/* globale Token-Tabelle (Inhalt nur während einer Bufferungsperiode gültig): */
#define MAXTOKENS  20000
local struct { uintL index;
               token_ data[MAXTOKENS];
             }
      tokens;

/* Holt das nächste Token: */
/* (Innerhalb von Präprozessor-Direktiven zählt Zeilenende als eigenes Token, */
/* und '#' leitet keine verschachtelte Präprozessor-Direktive ein.) */
local Token nexttoken (boolean within_prep_directive)
  { if (tokens.index == MAXTOKENS)
      /* kein Platz mehr in der Token-Tabelle -> nicht mehr buffern */
      { outbuffer_off(); tokens.index = 0;
        fprintf(stderr,"Token-Tabelle übergelaufen in Zeile %lu.\nFehler irgendwo nach Zeile %lu.\n",input_line,last_good_input_line);
        exit(1);
      }
    /* Nun ist tokens.index < MAXTOKENS . */
   {var Token token = &tokens.data[tokens.index]; /* Platz fürs nächste Token */
    tokens.index++;
    restart:
    token->startindex = out.buffindex;
    if (peek_char() == '&')
      { in_char(); /* '&' überlesen */
        if (peek_char() == '!')
          /* '&!' */
          { in_char(); goto restart; } /* '!' überlesen */
          else
          { out_char('&'); token->type = sep; token->ch = '&'; goto fertig; }
      }
    { var int c = next_char();
      switch (c)
        { case EOF:
            /* EOF */
            token->type = eof; goto fertig;
          case ' ': case '\v': case '\t':
            /* Whitespace. überlesen */
            goto restart;
          case '\n':
            /* Zeilenende */
            if (within_prep_directive)
              { token->type = eol; goto fertig; } /* als Token zurück */
              else
              { goto restart; } /* überlesen */
          case '\\':
            if (peek_char()=='\n')
              /* Zeilenende nach '\'. überlesen */
              { next_char(); goto restart; }
              else
              goto separator;
          case '/':
            if (peek_char() == '*')
              /* Kommentar */
              { next_char();
                loop { c = next_char();
                       if (c==EOF) { fprintf(stderr,"Unbeendeter Kommentar\n"); break; }
                       if ((c=='*') && (peek_char()=='/')) { next_char(); break; }
                     }
                goto restart;
              }
              else
              goto separator;
          case '*':
            if (peek_char() == '/')
              /* illegales Kommentar-Ende */
              { fprintf(stderr,"Kommentar-Ende außerhalb Kommentar in Zeile %lu\n",input_line); }
            goto separator;
          case '#':
            if (within_prep_directive)
              { goto separator; }
              else
              { /* Präprozessor-Anweisung. */
                /* Bis Zeilenende oder EOF lesen. */
                loop
                  { var Token subtoken = nexttoken(TRUE);
                    if ((subtoken->type == eof) || (subtoken->type == eol))
                      break;
                  }
                goto restart; /* und überlesen */
              }
          case '.':
            c = peek_char();
            if (!(((c>='0') && (c<='9')) || (c=='.'))) goto separator;
          case '0': case '1': case '2': case '3': case '4':
          case '5': case '6': case '7': case '8': case '9':
            /* Zahl. Weiterlesen, solange alphanumerisches Zeichen oder '.': */
            loop
              { c = peek_char();
                if (((c>='0') && (c<='9'))
                    || ((c>='A') && (c<='Z')) || ((c>='a') && (c<='z'))
                    || (c=='.')
                   )
                  { next_char(); }
                  else
                  break;
              }
            token->type = number; goto fertig;
          case '\'':
            /* Character-Konstante */
            loop
              { c = next_char();
                if (c==EOF) { fprintf(stderr,"Unbeendete Character-Konstante\n"); break; }
                if (c=='\'') break;
                if (c=='\\') { c = next_char(); }
              }
            token->type = charconst; goto fertig;
          case '\"':
            /* String-Konstante */
            loop
              {
#ifndef QUOTE_QUOTES
                c = next_char();
                if (c==EOF) { fprintf(stderr,"Unbeendete String-Konstante\n"); break; }
#else /* muß Single-Quotes in Strings quotieren: */
                c = in_char();
                if (c==EOF) { fprintf(stderr,"Unbeendete String-Konstante\n"); break; }
                if (c=='\'')
                  { /* statt "'" ein "\047" ausgeben: */
                    out_char('\\');
                    out_char('0'+((((unsigned char)'\'')/64)%8));
                    out_char('0'+((((unsigned char)'\'')/8)%8));
                    out_char('0'+(((unsigned char)'\'')%8));
                    continue;
                  }
                out_char(c);
#endif
                if (c=='\"') break;
                if (c=='\\') { c = next_char(); }
              }
            token->type = stringconst; goto fertig;
          case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
          case 'G': case 'H': case 'I': case 'J': case 'K': case 'L':
          case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R':
          case 'S': case 'T': case 'U': case 'V': case 'W': case 'X':
          case 'Y': case 'Z':
          case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
          case 'g': case 'h': case 'i': case 'j': case 'k': case 'l':
          case 'm': case 'n': case 'o': case 'p': case 'q': case 'r':
          case 's': case 't': case 'u': case 'v': case 'w': case 'x':
          case 'y': case 'z':
          case '_':
            /* Identifier. alles alphanumerische überlesen. */
            loop
              { c = peek_char();
                if (   ((c>='0') && (c<='9'))
                    || ((c>='A') && (c<='Z')) || ((c>='a') && (c<='z'))
                    || (c=='_')
                   )
                  { next_char(); }
                  else
                  break;
              }
            token->type = ident; goto fertig;
          default:
          separator:
            token->type = sep; token->ch = c; goto fertig;
    }   }
    fertig:
    token->endindex = out.buffindex;
    return token;
  }}
#define next_token() nexttoken(FALSE)

/* Klammern mitzählen: */
#define MAXBRACES 1000 /* maximale Verschachtelungstiefe von Klammern */
local struct { uintL count;
               struct { uintB brace_type; uintL input_line; } opening[MAXBRACES];
             }
      open_braces;

/* Mitzählen einer öffnenden Klammer: */
local void handle_opening_token (Token token)
  { if (open_braces.count < MAXBRACES)
      { open_braces.opening[open_braces.count].brace_type = token->ch;
        open_braces.opening[open_braces.count].input_line = input_line;
      }
    open_braces.count++;
  }

/* Mitzählen einer schließenden Klammer (ohne Überprüfung der Verschachtelung): */
/* local void handle_closing_token (Token token) */
/*   { open_braces.count--; } */
/* oder als Macro */
#define handle_closing_token(token)  { open_braces.count--; }

/* nächste Expression mit balancierten Klammern '()', '{}', '[]' lesen: */
/* (Dabei ist auf das Niveau open_braces.count=0 zu kommen, */
/*  evtl. ist jetzt schon open_braces.count>0.) */
local Token next_balanced_token (Token start_token)
  { var uintL open_braces_start = 0; /* Ziel-Niveau: 0 */
    var Token token = (start_token==NULL ? next_token() : start_token);
    var uintL startindex = token->startindex;
    loop
      { /* Hier stets  open_braces.count >= open_braces_start . */
        switch (token->type)
          { case eof:
              if (open_braces.count > open_braces_start)
                { if (open_braces.count <= MAXBRACES)
                    fprintf(stderr,"Nicht geschlossene '%c' in Zeile %lu\n",
                                   open_braces.opening[open_braces.count-1].brace_type,
                                   open_braces.opening[open_braces.count-1].input_line
                           );
                    else
                    fprintf(stderr,"Nicht geschlossene '(' oder '{' oder '['\n");
                }
              return token; /* EOF-Token als Ergebnis */
            case sep:
              switch (token->ch)
                { case '(': case '{': case '[':
                    handle_opening_token(token);
                    break;
                  case ')': case '}': case ']':
                    if (open_braces.count > open_braces_start)
                      { open_braces.count--;
                        if (open_braces.count < MAXBRACES)
                          { var uintB opening_ch = open_braces.opening[open_braces.count].brace_type;
                            var uintB closing_ch = token->ch;
                            if (!(   ((opening_ch == '(') && (closing_ch == ')'))
                                  || ((opening_ch == '{') && (closing_ch == '}'))
                                  || ((opening_ch == '[') && (closing_ch == ']'))
                               ) )
                              { fprintf(stderr,"Öffnende Klammer '%c' in Zeile %lu\n und schließende Klammer '%c'\n in Zeile %lu passen nicht zusammen.\n",
                                        opening_ch,open_braces.opening[open_braces.count].input_line,
                                        closing_ch,input_line
                                       );
                          }   }
                      }
                      else
                      { fprintf(stderr,"Nicht geöffnete '%c' in Zeile %lu\n",
                                token->ch,input_line
                               );
                        goto fertig;
                      }
                    break;
                  default:
                    break;
                }
            default: ;
              /* alles andere ist ausbalanciert */
          }
        if (open_braces.count == open_braces_start) break; /* fertig ausbalanciert? */
        token = next_token(); /* nein -> nächstes Token lesen */
      }
    fertig:
    token->startindex = startindex;
    return token;
  }

/* Umwandlung der Funktionsdefinitions-Deklarationen auf dem ganzen File */
/* vornehmen: */
local void convert (void)
  { input_line = 1; last_good_input_line = 1;
    out.mode = direct;
    open_braces.count = 0;
    restart1: /* Hier out.mode=direct, neues Token lesen: */
    tokens.index = 0; /* Token-Tabelle leeren */
   {var Token token = next_token(); /* nächstes Token lesen */
    restart2: /* Hier out.mode=direct, neues Token gelesen. */
    if ((token->type == sep) && (token->ch == '(')) /* Klammer auf? */
      /* ja -> aufpassen: */
      { handle_opening_token(token);
        outbuffer_on(); /* Buffer einschalten */
        /* Es sollten nun k Identifier, getrennt durch k-1 Kommata, kommen: */
        token = next_token();
        if ((token->type == sep) && (token->ch == ')')) /* Klammer zu? */
          /* ja -> in eine leere Parameterliste evtl. 'void' einfügen: */
          { var uintL insertpoint = token->startindex;
            handle_closing_token(token);
            token = next_token();
            if (!((token->type == sep) && (token->ch == '{'))) /* geschweifte Klammer auf? */
              { outbuffer_off(); goto restart2; }
            /* token = '{' */
            /* Umwandeln: "void" einfügen. */
            outbuffer_off_insert(insertpoint,"void");
            token = next_balanced_token(token); /* Funktionsdefinition überlesen */
          }
          else
          /* nein -> nichtleere Parameterliste verarbeiten: */
          { var Token param_names[MAXARGCOUNT];
            var Token param_comma[MAXARGCOUNT];
            var uintL param_count = 0;
            loop
              { if (param_count==MAXARGCOUNT) goto nix; /* kein Platz mehr? */
                if (!(token->type == ident)) /* Identifier? */
                  goto nix;
                param_names[param_count] = token; /* ja -> abspeichern */
                token = next_token();
                if (!(   ((token->type == sep) && (token->ch == ')')) /* Klammer zu? */
                      || ((token->type == sep) && (token->ch == ',')) /* oder Komma? */
                   ) )
                  goto nix;
                param_comma[param_count] = token; /* ja -> abspeichern */
                param_count++;
                if ((token->type == sep) && (token->ch == ')')) /* Klammer zu? */
                  break;
                token = next_token();
              }
            /* token = ')' */
            handle_closing_token(token);
            /* Parameter-Deklarationen verarbeiten: */
           {var Token paramdecl_semicolons[MAXARGCOUNT];
            var uintL paramdecl_count = 0;
            loop
              { token = next_token();
                if ((token->type == sep) && (token->ch == '(')) /* Klammer auf? */
                  /* Entscheidung der Zweideutigkeit (Beispiel: */
                  /* " macro(macroarg) (x) int x; {los();} " */
                  /* -> zugunsten der Erkennung von "macro(macroarg)" und nicht */
                  /* "macro": */
                  { outbuffer_off(); goto restart2; }
                if ((token->type == sep) && (token->ch == '{')) /* geschweifte Klammer auf? */
                  break;
                loop
                  { token = next_balanced_token(token);
                    if (token->type==eof) goto nix;
                    if ((token->type == sep) && (token->ch == ';')) /* Semikolon? */
                      break;
                    /* Entscheidung der Zweideutigkeit (Beispiel: */
                    /* " foo(a,b) int a; bar(x,y) int x; int y; {los();} " */
                    /* -> zugunsten der Erkennung von "bar" und nicht "foo"): */
                    token = next_token();
                    if ((token->type == sep) && (token->ch == '(')) /* Klammer auf? */
                      { outbuffer_off(); goto restart2; }
                  }
                if (paramdecl_count==MAXARGCOUNT) goto nix; /* kein Platz mehr? */
                paramdecl_semicolons[paramdecl_count] = token;
                paramdecl_count++;
              }
            /* token = '{' */
            if ((param_count == paramdecl_count) /* gleichviele Variablen wie Deklarationen? */
                && (out.mode==buffered) /* und Buffer noch da? */
               )
              /* ja -> Modifikation durchführen: */
              /* Alle param_names und param_comma durch Leerstellen und */
              /* alle paramdecl_semicolons durch Komma bzw. Klammer zu */
              /* überschreiben: */
              { do { param_count--;
                    {var Token name = param_names[param_count];
                     var uintL index = name->startindex;
                     var uintL endindex = name->endindex;
                     until (index==endindex) { out.buffer[index++] = ' '; }
                    }
                    {var Token comma = param_comma[param_count];
                     var uintL index = comma->startindex;
                     var uintL endindex = comma->endindex;
                     until (index==endindex) { out.buffer[index++] = ' '; }
                   }}
                   until (param_count==0);
                out.buffer[paramdecl_semicolons[--paramdecl_count]->startindex] = ')';
                until (paramdecl_count==0)
                  { out.buffer[paramdecl_semicolons[--paramdecl_count]->startindex] = ','; }
              }
            outbuffer_off();
            last_good_input_line = input_line;
            token = next_balanced_token(token); /* Funktionsdefinition überlesen */
          }}
      }
      else
      { nix: /* keine umwandelbare Funktionsdefinition */
        outbuffer_off();
        token = next_balanced_token(token);
      }
    /* Hier ist wieder out.mode=direct. */
    if (!(token->type==eof)) goto restart1;
    /* Bei EOF am Input: fertig (da outfile ungebuffert). */
  }}

int main ()
  { infile = stdin;
    outfile = stdout;
    convert();
    if (ferror(stdin) || ferror(stdout)) { exit(1); }
    exit(0);
  }

