/* 08May24: Eine einfache Version von tail(1), welches Zeilen bis zu
 * 100 Zeichen (Newline ausgeschlossen) einliest. */

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

/* Damit wir nicht jedes mal eine konstante Zahl angeben müssen,
 * definiere ich ein Makro um den Wert zentral zu setzen. */
#define N 10

int main()
{
     /* Mit dieser Syntax, deklarieren wir ein Array von Pointern mit
      * N einträgen.  Mit "= {}" werden alle Einträge ge-NULL't. */
     char *lines[N] = {};
     unsigned i = 0;

     /* Weil es nicht statisch bestimmbar ist wie viele Zeilen aus der
      * Standard Eingabe gelesen werden.  Daher können wir nicht eine
      * fixe Anzahl an Iterationen vornehmen, sondern müssen lesen bis
      * ein /End of File/ erreicht wird. */
     for (;;) {
	  /* Mit fgets können wir versuchen bis zu einer Zeile
	   * einzulesen.  Wir sind selbst dafür zuständig den Speicher
	   * bereitzustellen.  Wir legen den Speicher auf dem Stack
	   * an: */
	  char line[100 + 1 + 1];

	  /* Mit fgets(3) fragen wir das Betriebsystem nach einer
	   * Ressource, welche möglicherweise nicht vorhanden ist
	   * (E/A-Fehler).  Daher muss es Fehlerbehandelt werden, aber
	   * auch um das Ende des Eingabestroms zu erkennen. */
	  if (NULL == fgets(line, sizeof line, stdin)) {
	       /* Mit ferror(3) und feof(3) können wir aus einem
		* File-Pointer ablesen was den Fehler ausgelöst
		* hat. */
	       if (ferror(stdin)) {
		    perror("fgets");
		    exit(EXIT_FAILURE);
	       } else {
		    /* Wenn wir EOF erreicht haben, dann brechen wir
		     * die Eingabeschleife ab. */
		    break;
	       }
	  }

	  /* Wir verwalten die Zeilen in einem Ring-Puffer.  Alte
	   * Einträge wollen wir wieder Freigeben, anstatt diese zu
	   * überschreiben und damit die Referenz zu verlieren (was
	   * man mit Valgrind erkennen kann). */
	  free(lines[i % N]);

	  /* Wenn wir direkt `line' in `lines' abspeichert, dann kommt
	   * der gleiche Pointer wieder und wieder vor.  Mit strdup(3)
	   * kopieren wir den Inhalt auf die Halde, damit die
	   * Gültigkeit unabhängig vom Kontrollfluss erhalten bleiben
	   * soll. */
	  lines[i % N] = strdup(line);
	  if (lines[i % N] == NULL) {
	       perror("strdup");
	       exit(EXIT_FAILURE);
	  }
	  i++;
     }

     for (unsigned j = 0; j < N; j++) {
	  /* Mit einem Modulo-"Trick" lesen wir die Zeilen wir den
	   * Rest von dem Ringpuffer aus.  Sollte ein Array-Eintrag
	   * noch nicht beschrieben sein, kann dann wissen wir das
	   * weniger als N Einträge ausgelesen wurden. */
	  if (lines[(i + j) % N] == NULL) break;

	  /* Hier lieber `fgets' and `printf', weil man keine
	   * Format-Strings braucht. */
	  fputs(lines[(i + j) % N], stdout);

	  /* Den Speicher müssen wir am Ende wieder freigeben, damit
	   * Valgrind bei einer erfolgreichen Terminierung nicht
	   * beschwert. */
	  free(lines[(i + j) % N]);
     }

     /* Am Ende überprüfen wir ob der Ausgabe-Puffer erfolgreich
      * geleert werden kann. */
     if (EOF == fflush(stdout)) {
	  perror("fflush");
	  exit(EXIT_FAILURE);
     }

     return EXIT_SUCCESS;
}