/* 03Jul24: Beispiel für das Auslesen der Einträge in einem
 * Verzeichnis (opendir+readdir+closedir) und dem Abfragen von
 * Metadaten einer Datei (stat+lstat).
 *
 * Idee: Gebe alle symbolischen Verweise in dem jetzigem Verzeichnis
 * aus. */

#include <dirent.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>

int main()
{
     /* Mit opendir(2) bekommen wir ein "undurchsichtiges" Objekt, was
      * als "Strom an Dateien" aus einem Verzeichnis interpretiert
      * werden kann, genau wie man mit fopen(3) ein "Strom an Bytes"
      * bekommt. */
     DIR *dir = opendir(".");
     struct dirent *ent;

     for (;;) {
	  /* Die Fehlerbehandlung von readir(2) muss unterschieden
	   * zwischen einem "echtem" Fehler und dem Ende des
	   * Verzeichnisses.  Bei FILE* konnte man da `feof' und
	   * `ferror' benutzen, was aber vom Betriebsystem nicht
	   * angeboten wird für DIR*.  Stattdessen, gibt readdir(2)
	   * vor, dass es `errno' /nicht/ setzen wird, wenn kein
	   * Fehler passiert (allgemein nicht der Fall, `errno' ist
	   * üblicherweise nur "gültig" wenn die Funktion einen Fehler
	   * andeutet).  Das kann man dann ausnutzen, um zwischen den
	   * beiden möglichen "Fehlerfällen" zu unterscheiden.  Wenn
	   * jedoch beides nicht eingetreten ist, haben wir einen
	   * weiteren Eintrag aus dem Verzeichnis gelesen. */
	  errno = 0;
	  ent = readdir(dir);
	  if (ent == NULL) {	 /* Problem? */
	       if (errno == 0) { /* End-Of-Directory */
		    break;
	       } else {		/* Real Problem */
		    perror("readdir");
		    exit(EXIT_FAILURE);
	       }
	  }

	  char *path = ent->d_name;

	  /* Wenn wir den Pfad (in unserem Fall relativ zu dem
	   * jetzigem Arbeitsverzeichnis), haben, können wir mit
	   * `stat' bzw. `lstat' Informationen über die Datei
	   * beantragen.  Wir müssen dabei selbst den Speicher für
	   * diese Information bereitstellen, in diesem Fall auf dem
	   * Stack.  Der Unterschied zwischen `stat' und `lstat' liegt
	   * darin, ob ein symbolischer Verweis (eine Datei welche
	   * üblicherweise beim öffnen, vom Betriebsystem so
	   * interpretiert wird, dass es gleich eine andere Datei
	   * stattdessen aufmacht) aufgelöst wird (stat) oder nicht
	   * (lstat). */
	  struct stat sbuf;	/* siehe sys_stat.h(0p) */
	  if (-1 == lstat(path, &sbuf)) {
	       perror("stat");
	       continue;
	  }

	  /* In .st_mode feld eines "struct stat" buffers beschreibt
	   * zusammen sowohl die Art von Datei (Regulär, Verzeichnis,
	   * Symlink, Pipe, Socket, Block Device, ...) und dessen
	   * Berechtigungen.  Mit einem Makro wie S_ISLNK können wir
	   * leicht prüfen ob die Datei von einem spezifischem Typen
	   * ist (hier ein symbolischer Verweis), und unser Programm
	   * dann davon abhängig steuern. */
	  if (S_ISLNK(sbuf.st_mode)) {
	       puts(path);
	  }
     }

     /* Am ende sollte man natürlich nicht vergessen alle Ressourcen
      * wieder freizugeben, in diesem Fall Lese-Kopf für das
      * Verzeichnis.  Dahingehenden muss `ent' und `stat' nicht
      * freigegeben werden, weil beim ersten `readdir' für den
      * Speicher verantworlich ist, und beim zweiten der Speicher auf
      * dem Stack angelegt wurde. */
     closedir(dir);
     return 0;
}