// Klammernprüfung
// Alternative Lösung mit strchr statt switch, thx Rainer
//
// 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>

// Liste der öffnenden und der schließenden Klammern
// (müssen paarweise zusammenpassen)
const char opening[] = "([{";
const char closing[] = ")]}";

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;
    char *p = strchr(opening, c); // Pointer auf aktuelle Klammer auf in opening
    if (p != NULL) {  // c ist in opening, d.h. c ist eine Klammer auf
      ++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
      // p - opening liefert den Index der Klammer in opening und closing
      close[current] = closing[p - opening];
    } else if (strchr(closing, c) != NULL) {  // Klammer zu
      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;
      }
    } else if (c == '\n') {
       ++line; col = 0;
    } else { // Andere Zeichen: Nichts zu tun
    }
  }
  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);
}
