// Klammernprüfung
//
// Aufruf: klammer [infile ...]
// 
// Klaus Kusche, 2010

// Maximale Anzahl geschachtelter Klammern
#define MAX_LEVEL 100

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

const char *progName;        // für Fehlermeldungen in der Funktion

void doFile(FILE *f, const char *fName);

// verarbeite den File f (Filenamen fName)
void doFile(FILE *f, const char *fName)
{
  int c;                     // Aktuelles Zeichen (int wegen EOF!)
  int line = 1, col = 0;     // Zeile und Spalte für Fehlermeldungen
  char close[MAX_LEVEL];     // "Gegenstücke" zu den gerade offene Klammern
  int current = -1;          // Index der aktuellen Klammer in close

  while ((c = fgetc(f)) != EOF) {
    ++col;
    switch (c) {
      case '(':
      case '[':
      case '{':
        ++current;
        if (current == MAX_LEVEL) {  // Array zu kurz
          printf("%s:%d:%d: %c too deeply nested\n",
                 fName, line, col, c);
          return;
        }
        // Speichere das "Gegenstück" der Klammer
        close[current] = (c == '(') ? ')' : ((c == '[') ? ']' : '}');
        break;
      case ')':
      case ']':
      case '}':
        if (current == -1) {  // Array ist leer
          printf("%s:%d:%d: Closing %c was never opened\n",
                 fName, line, col, c);
          return;
        } else if (c != close[current]) {  // Klammer passt nicht zusammen
          printf("%s:%d:%d: Expected %c, found %c\n",
                 fName, line, col, close[current], c);
          return;
        } else {
          // alles ok, Klammern passen zusammen, 1 offene Klammer weniger
          --current;
        }
        break;
      case '\n':
        ++line; col = 0;
        break;
      default: // Andere Zeichen: Nichts zu tun
        break;
    }
  }
  if (ferror(f)) {
    fprintf(stderr, "%s: cannot read %s: %s\n",
            progName, fName, strerror(errno));
    return;
  }
  if (current >= 0) {  // Array ist nicht leer!
    printf("%s:%d:%d: %c missing at end of file\n",
           fName, line, col, close[current]);
    return;
  }
}

int main(int argc, const char *argv[])
{
  progName = argv[0];

  if (argc == 1) {   // kein File angegeben!
    doFile(stdin, "stdin");
  } else {
    for (int i = 1; i < argc; ++i) {
      FILE *f = fopen(argv[i], "r");
      if (f == NULL) {
        fprintf(stderr, "%s: cannot open %s for reading: %s\n",
                progName, argv[i], strerror(errno));
        exit(EXIT_FAILURE);
      } else {
        doFile(f, argv[i]);
        fclose(f);
      }
    }
  }

  exit(EXIT_SUCCESS);
}
