/* 08May24: Eine einfache Version von tail(1), welches Zeilen bis zu * 100 Zeichen (Newline ausgeschlossen) einliest. */ #include #include #include /* 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; }