// "dir"-Befehl, Filenamen im aktuellen Verzeichnis, mit File-Länge usw.
// 
// Aufruf: mydir2
// 
// Klaus Kusche, 2012

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <dirent.h>
#include <sys/stat.h>
#include <time.h>     // für ctime

const char *progName;  // für Fehlermeldungen

// Fehlermeldung nach missglückter Dir-Operationen ausgeben 
// (mit errno als Fehler-Grund)
void errMsg(const char *operation, const char *fileName)
{
  fprintf(stderr, "%s: error %s %s: %s\n",
          progName, operation, fileName, strerror(errno));
  exit(EXIT_FAILURE);
}

int main(int argc, const char *argv[])
{
  // Verzeichnis das angezeigt werden soll: Aktuelles Verzeichnis
  const char dirName[] = ".";

  progName = argv[0];  
  if (argc > 1) {
    fprintf(stderr, "Aufruf: %s\n", argv[0]);
    exit(EXIT_FAILURE);
  }

  // Analog zu FILE *: Das Ergebnis von opendir, geht ins readdir
  // Was da drin ist, ist uns egal
  DIR *d = opendir(dirName);
  if (d == NULL) {
    errMsg("opening directory", dirName);
  }

  for (;;) {  // ein Schleifenumlauf pro File im Verzeichnis d
    // bei readdir kann man "Ende des Directories" und "Fehler"
    // nur durch Auswertung von "errno" unterscheiden,
    // dazu muss man errno vor jedem readdir-Aufruf explizit auf 0 setzen
    errno = 0;
    // Das Ergebnis von readdir zeigt auf die interne statische struct,
    // in die readdir den Filenamen gespeichert hat
    struct dirent *entry = readdir(d);
    if (entry == NULL) break;  // Fehler oder Ende des Verzeichnisses
    // entry ist ein Pointer auf eine struct
    // ==> Zugriff auf deren Member mit "->"
    // für den von readdir gelieferten Namen stat aufrufen
    // stat hat keine interne struct für sein Ergebnis
    // sondern wir müssen ihm eine leere struct übergeben,
    // in die es sein Ergebnis einträgt
    // ==> wir brauchen eine struct, nicht nur einen Pointer
    struct stat fileStat;
    // stat bekommt einen Pointer auf die struct,
    // in der es sein Ergebnis abliefern soll
    if (stat(entry->d_name, &fileStat) != 0) {
      errMsg("getting entry", entry->d_name);
    }
    // Jetzt stehen in fileStat die Informationen zum aktuellen File:
    // Art des Files prüfen wie in der Angabe angegeben
    // und als typ speichern (einzelnen Zeichen, kein ganzer String!)
    // fileStat ist die struct selbst, kein Pointer
    // ==> Member-Zugriff mit "."
    char typ = S_ISREG(fileStat.st_mode) ? 'f' :
          S_ISDIR(fileStat.st_mode) ? 'd' : '?';
    // ... Filedatum formatieren wie in der Angabe angegeben ...
    // (mit localtime(...) ginge das viel schöner, aber aufwändiger)
    char *date = ctime(&(fileStat.st_mtime));
    // ... und im Datum hinten das \n suchen
    // und an diese Stelle eine Ende-Markierung schreiben
    *(strchr(date, '\n')) = '\0';
    printf("%10ld %c %s %s\n", fileStat.st_size, typ, date, entry->d_name);
  }
  
  if (errno != 0) {
    errMsg("reading directory", dirName);
  }

  if (closedir(d) == -1) {
    errMsg("closing directory", dirName);
  }
  
  exit(EXIT_SUCCESS);
}
