#include #include #include #include #include #include #include #include #include #include #include static void die(const char *msg) { perror(msg); exit(EXIT_FAILURE); } static void usage(char *name) { fprintf(stderr, "Usage: %s ()\n", name); exit(EXIT_FAILURE); } // Struktur zum Speichern des Glossars struct schoen { char *from, *to; }; struct glossar { size_t eintraege; struct schoen *s; }; static int vergleiche(const void *a, const void *b) { struct schoen *ua = (struct schoen *)a; struct schoen *ub = (struct schoen *)b; return strcmp(ua->from, ub->from); } // Forward Declarations static struct glossar *glossar(const char *p); static void bearbeite(struct glossar *g, const char *p); static void lese_ein(struct glossar *g, FILE *in, FILE *out); static const char *verschoenere(struct glossar *g, const char *c); // Um den immer häufiger werdenden Anglizismen in der deutschen Sprache // entgegenzuwirken, ist es Ihre Aufgabe, den Wortverschönerer, kurz wosch, zu // implementieren. Dieser ersetzt mithilfe eines Glossars Begriffe durch eine // entsprechende Verschönerung. // Ihr Programm nimmt als ersten Parameter den Pfad zum Glossar entgegen, // welches die Verschönerungen von Begriffen enthält. Alle weiteren Parameter // werden als zu bearbeitende Pfade interpretiert. Falls keine weiteren // Parameter neben dem Pfad zum Glossar vorhanden sind, soll die Standardeingabe // verarbeitet werden. Die Ausgabe der verschönerten Texte geschieht auf der // Standardausgabe. // Liest mit der Funktion "glossar" das Glossar ein. Danach werden die // Verschönerungen mithilfe von "lese_ein" und "bearbeite" vorgenommen. // Abschließend werden noch belegte Ressourcen freigegeben. int main(int argc, char *argv[]) { if (argc < 2) { usage(argv[0]); } struct glossar *g = glossar(argv[1]); if (argc == 2) { lese_ein(g, stdin, stdout); } for (int i = 2; i < argc; ++i) { bearbeite(g, argv[i]); } for (size_t i = 0; g->eintraege; ++i) { free(g->s[i].from); free(g->s[i].to); } free(g->s); free(g); } // Speichert alle vorkommenden Verschönerungen aus der Datei hinter dem Pfad "p" // in einem "struct glossar" und gibt einen Zeiger darauf zurück. Die Einträge // im "struct glossar" sollen mithilfe der gegebenen Vergleichsfunktion // "vergleiche" sortiert werden. Eine valide Zeile besteht aus einem zu // ersetzenden Begriff (siehe Hinweis 1) und der zugehörigen, beliebig langen // Verschönerung. Beide Bestandteile sind mit Leerzeichen und/oder Tabulatoren // voneinander getrennt. Jede Zeile der Glossardatei beinhaltet maximal 200 // Zeichen, ebenso kommt kein zu ersetzender Begriff doppelt vor. Nicht valide // Zeilen werden ignoriert. static struct glossar *glossar(const char *p) { // Glossar anlegen struct glossar *entry = malloc(sizeof(struct glossar)); if (!entry) { die("malloc"); } entry->eintraege = 0; entry->s = NULL; // Datei öffnen FILE *f = fopen(p, "r"); if (!f) { die("fopen"); } // Einlesen der Datei char buf[202]; while (fgets(buf, sizeof(buf), f)) { // Das extra malloc(3) fuer "s" ist eigentlich unnoetig, aber wir hatten // es schon so gesagt gehabt in der Tafeluebung. ;) struct schoen *s = malloc(sizeof(struct schoen)); if (!s) { die("malloc"); } // Teilen der gelesenen Zeile s->from = strtok(buf, " \t"); s->to = strtok(NULL, " \t"); if (!s->from || !s->to || strtok(NULL, " \t\n")) { free(s); continue; } // Nachallozieren von Speicher entry->s = realloc(entry->s, (entry->eintraege + 1) * sizeof(struct schoen)); if (!entry->s) { die("realloc"); } entry->eintraege++; // Eintragen in das Glossar s->from = strdup(s->from); if (!s->from) { die("strdup"); } s->to = strdup(s->to); if (!s->to) { die("strdup"); } entry->s[entry->eintraege - 1] = *s; free(s); } if (ferror(f)) { die("fgets"); } fclose(f); // Sortierung qsort(entry->s, entry->eintraege, sizeof(struct schoen), vergleiche); return entry; } // Bearbeitet einen übergebenen Pfad "p" mit dem übergebenen "struct glossar // *g": Bei einer regulären Datei wird lese_ein aufgerufen, allerdings nur, // sofern diese auf ".txt" endet. Verzeichnisse werden rekursiv abgearbeitet. // Alle anderen Dateien werden ignoriert, insbesondere auch symbolische // Verknüpfungen. static void bearbeite(struct glossar *g, const char *p) { struct stat st; if (lstat(p, &st)) { perror("lstat"); return; } if (S_ISDIR(st.st_mode)) { DIR *dir = opendir(p); if (!dir) { perror("opendir"); return; } struct dirent *ent; while (errno = 0, ent = readdir(dir)) { if (strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0) { continue; } char path[strlen(p) + strlen(ent->d_name) + 2]; // +2 fuer '/' und '\0' sprintf(path, "%s/%s", p, ent->d_name); bearbeite(g, path); } if (errno) { perror("readdir"); } closedir(dir); return; } else if (S_ISREG(st.st_mode)) { size_t plen = strlen(p); // Alternativ: fnmatch(3) mit "*.txt". if (plen < 4 || strcmp(p + plen - 4, ".txt") != 0) { return; } FILE *f = fopen(p, "r"); if (!f) { perror("fopen"); return; } lese_ein(g, f, stdout); fclose(f); } } // Liest den Inhalt vom übergebenen "FILE *in" ein, und gibt diesen auf "FILE // *out" aus. Dabei werden Verschönerungen von Begriffen mithilfe von // "verschoenere" realisiert. static void lese_ein(struct glossar *g, FILE *in, FILE *out) { char buf[51]; size_t len = 0; for (;;) { int c = fgetc(in); if (c == EOF) { if (ferror(in)) { die("fgetc"); } buf[len] = '\0'; if (fputs(verschoenere(g, buf), out) == EOF) { die("fputs"); } break; } // Nicht ganz korrekt mit "len == 50", aber die Musterloesung ignoriert // den Fall komplett... if (len == 50 || c == ' ' || c == '\t' || c == '\n') { buf[len] = '\0'; if (fprintf(out, "%s%c", verschoenere(g, buf), c) < 0) { die("fprintf"); } len = 0; } else { buf[len++] = c; } } if (fflush(out) == EOF) { die("fflush"); } } // Überprüft, ob der Begriff "c" im übergebenen "glossar" vorhanden ist. Wenn // ja, wird dieser zurückgegeben, anderenfalls der Begriff selbst. static const char *verschoenere(struct glossar *g, const char *c) { for (size_t i = 0; i < g->eintraege; ++i) { int cmp = strcmp(c, g->s[i].from); if (cmp == 0) { return g->s[i].to; } else if (cmp < 0) { return c; } } return c; }