// Zeilen zusammenhängen solange sie in die Ausgabe-Zeile passen
// Primitiv-Version, macht teilweise zu viele Zwischenräume
// 
// Aufruf: zeilen  [Eingabe-Filename [Ausgabe-Filename]]
// Ist kein Filename angegeben, wird stdin bzw. stdout verwendet
// 
// Klaus Kusche, 2010

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

// Max. Zeilenlänge bei Eingabe und Ausgabe
#define LINELEN 80

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

// Gib eine schöne Fehlermeldung aus und beende das Programm
// operation ... wobei ist der Fehler passiert?
// fileName  ... welcher File war betroffen?
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[])
{
  char line[LINELEN + 2];               // String zum Einlesen der neuen Zeile
                                        // +1 für \n, +1 für \0
  char oldLine[LINELEN + 2] = "";       // String zum Zusammenbau der Ausgabe
                                        // Initialisieren, damit man sieht,
                                        // ob schon etwas drin ist oder nicht
  FILE *inFile, *outFile;               // Eingabe-File und Ausgabe-File
  const char *inFileName, *outFileName; // Filenamen der beiden Files

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

  // mach den File auf, wenn auf der Befehlszeile ein Filenamen angegeben wurde
  // verwende stdin bzw. stdout, wenn kein Filenamen angegeben wurde
  if (argc >= 2) {
    inFileName = argv[1];
    if ((inFile = fopen(inFileName, "r")) == NULL) {
      errMsg("opening (for reading)", inFileName);
    }
  } else {
    inFileName = "stdin";
    inFile = stdin;
  }

  if (argc >= 3) {
    outFileName = argv[2];
    if ((outFile = fopen(outFileName, "w")) == NULL) {
      errMsg("opening (for writing)", outFileName);
    }
  } else {
    outFileName = "stdout";
    outFile = stdout;
  }

  // Input zeilenweise verarbeiten bis der File zu Ende ist
  while (fgets(line, sizeof (line), inFile)) {
    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 80 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: error reading %s: Incomplete line\n",
              progName, inFileName);
      exit(EXIT_FAILURE);
    }
    // Jetzt können wir uns darauf verlassen,
    // dass sowohl line als auch oldLine (außer im 1. Durchlauf) auf \n enden
    // strlen zählt das \n mit
    // ==> -1, wenn wir nur die sichtbare Länge wollen
    // und +1 für den Zwischenraum zwischen den zusammengehängten Zeilen
    if ((strlen(oldLine) - 1) + 1 + (strlen(line) - 1) > LINELEN) {
      // Die neue Zeile hat in der alten Zeile keinen Platz mehr
      // ==> Die alte Zeile wie sie ist ausgeben,
      // dann die neue Zeile als alte Zeile speichern
      if (fputs(oldLine, outFile) == EOF) {
        errMsg("writing", outFileName);
      }
      strcpy(oldLine, line);
    } else {
      // Die neue Zeile hat noch in der alten Zeile Platz
      // ==> dazuhängen, erst später ausgeben
      if (oldLine[0] != '\0') {
        // Wenn es nicht die allererste Zeile ist:
        // \n am Ende der alten Zeile durch einen Zwischenraum ersetzen
        *strchr(oldLine, '\n') = ' ';
      }
      strcat(oldLine, line);
    }
  }

  if (oldLine[0] != '\0') {
    // Letzte alte Zeile ausgeben
    // (außer wenn der Inputfile gar keine Zeilen enthielt)
    if (fputs(oldLine, outFile) == EOF) {
      errMsg("writing", outFileName);
    }
  }

  // Hat das fgets NULL geliefert,
  // weil das File-Ende erreicht wurde,
  // oder weil ein Fehler aufgetreten ist?
  if (ferror(inFile)) {
    errMsg("reading", inFileName);
  }

  // Files schließen
  // es schadet auch nichts, die std-Files zu schließen
  if (fclose(inFile) == EOF) {
    errMsg("closing", inFileName);
  }
  if (fclose(outFile) == EOF) {
    errMsg("closing", outFileName);
  }
  
  exit(EXIT_SUCCESS);
}
