// Text zeilenweise sortieren (qsort), von stdin nach stdout
// mit Zeilennummern
// mit dyn. Daten nur für die Zeilentexte
// 
// Aufruf: textsort-dyn
// 
// Klaus Kusche, 2010

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

// Max. Zeilenlänge der eingelesenen Zeilen (incl. \n\0) 
#define MAX_LEN 4096
// Max. Anzahl der Zeilen
#define MAX_LINES 100

// Struktur für unser Array von Zeilen
typedef struct {
  const char *text;   // Zeiger auf den dynamisch angelegten Text der Zeile
  int lineNr;         // Zeilennummer der Zeile
} line_t;

const char *progName;  // Der Programmname (argv[0]), für Fehlermeldungen

void *checkMalloc(void *p);
int lineCmp(const void *p1, const void *p2);

// Prüfe das Ergebnis von malloc und strdup: Darf nicht NULL sein!
void *checkMalloc(void *p)
{
  if (p == NULL) {
    fprintf(stderr, "%s: out of memory\n", progName);
    exit(EXIT_FAILURE);
  }
  return p;
}

// Vergleichsfunktion für qsort
// p1 und p2 zeigen auf die zu vergleichenden Elemente des Arrays "lines"
// Diese Elemente sind Strukturen,
// und die Zeilentexte, auf die die Member "text" dieser beiden struct's zeigen,
// müssen verglichen werden
int lineCmp(const void *p1, const void *p2)
{
// Möglichkeit 1:
  const line_t *s1 = (const line_t *) p1;
  const line_t *s2 = (const line_t *) p2;
  return strcmp(s1->text, s2->text);

// Möglichkeit 2:
  //return strcmp(((const line_t *) p1)->text,
  //              ((const line_t *) p2)->text);
}

int main(int argc, const char *argv[])
{
  progName = argv[0];
  if (argc != 1) {
    fprintf(stderr, "Aufruf: %s\n", progName);
    exit(EXIT_FAILURE);
  }

  char line[MAX_LEN];       // String zum Einlesen der neuen Zeile
  line_t lines[MAX_LINES];  // Fixes Array von Zeilen-Strukturen
  int lineCnt;              // Anzahl der belegten Zeilen in lines

  // Input zeilenweise verarbeiten bis der File zu Ende ist, mitzählen
  for (lineCnt = 0; fgets(line, sizeof(line), stdin); ++lineCnt) {
    if (lineCnt == MAX_LINES) {
      fprintf(stderr, "%s: Limit of %d lines exceeded\n", progName, MAX_LINES);
      exit(EXIT_FAILURE);
    }

    if (strchr(line, '\n') == NULL) {
      // Eingelesene Zeile enthält kein \n
      // ==> passiert nur, wenn kein Platz mehr für das \n war
      // ==> Zeile ist länger als MAX_LEN Zeichen
      // Problem unter Windows:
      // Letzte Zeile von Textfiles hat oft kein \n ==> falsche Fehlermeldung!
      // (in Linux hat auch die letzte Zeile immer ein \n)
      fprintf(stderr, "%s: Incomplete line\n", progName);
      exit(EXIT_FAILURE);
    }

    // Zeilentext in dynamisch angelegten String kopieren,
    // Pointer darauf im aktuellen lines-Element speichern
    lines[lineCnt].text = (const char *) checkMalloc(strdup(line));
    lines[lineCnt].lineNr = lineCnt + 1;  // "Menschliche" Zeilennummern ab 1
  }

  // sortieren, Elementgröße = Größe einer Struktur
  qsort(lines, lineCnt, sizeof(line_t), lineCmp);

  // zeilenweise ausgeben, Schleife läuft mit Pointer über das Array lines
  for (line_t *p = lines; p < lines + lineCnt; ++p) {
    // p zeigt auf eine Struktur
    // ==> die Member ausgeben
    printf("%08d: %s", p->lineNr, p->text);
  }
  
  exit(EXIT_SUCCESS);
}
