61. Was tut look?

Ausgangspunkt: Die main-Funktion von look.c:

int
main(int argc, char *argv[])
{
  struct stat sb;
  int ch, fd, termchar;
  char *back, *file, *front, *p;

  setlocale(LC_ALL, "");
  bindtextdomain(PACKAGE, LOCALEDIR);
  textdomain(PACKAGE);

  setlocale(LC_ALL, "");

  file = _PATH_WORDS;
  termchar = '\0';
  string = NULL;    /* just for gcc */

  while ((ch = getopt(argc, argv, "adft:")) != EOF)
    switch(ch) {
    case 'a':
      file = _PATH_WORDS_ALT; break;
    case 'd':
      dflag = 1; break;
    case 'f':
      fflag = 1; break;
    case 't':
      termchar = *optarg; break;
    case '?':
    default:
      usage();
    }
  argc -= optind;
  argv += optind;

  switch (argc) {
  case 2:        /* Don't set -df for user. */
    string = *argv++;
    file = *argv;
    break;
  case 1:        /* But set -df by default. */
    dflag = fflag = 1;
    string = *argv;
    break;
  default:
    usage();
  }
  if (termchar != '\0' && (p = strchr(string, termchar))
    != NULL)
    *++p = '\0';

  if ((fd = open(file, O_RDONLY, 0)) < 0 || fstat(fd, &sb))
    err("%s: %s", file, strerror(errno));
  front = mmap(NULL, (size_t) sb.st_size, PROT_READ,
#ifdef MAP_FILE
         MAP_FILE |
#endif
         MAP_SHARED, fd, (off_t) 0);
  if
#ifdef MAP_FAILED
     (front == MAP_FAILED)
#else
     ((void *)(front) <= (void *)0)
#endif
    err("%s: %s", file, strerror(errno));

  back = front + sb.st_size;
  return look(front, back);
}

Was passiert hier? Am Anfang wird das locale gesetzt – das sollten wir im Python-Modul nicht mehr tun, Python hat das schon für uns getan, und wir sollten die Weisheit des Interpreters nicht mutwillig in Frage stellen. Gleiches gilt für die Aufrufe mit “textdomain”. Bei ihnen geht es um die automatische Übersetzung von Fehlermeldungen, was uns zu schwierig ist. Warum das locale zwei Mal gesetzt wird, entzieht sich meiner Kenntnis.

Dannwerden einige Defaults gesetzt. Die Manpage zu look verrät, dass look entweder als look <string> oder als look <string> <file> aufgerufen werden kann und im ersten Fall /usr/dict/words (was hier im Makro _PATH_WORDS steht) als Datei angenommen wird. In termchar kann ein Zeichen angegeben werden, das als “Feldtrenner” dient – dies ist zunächst das Nullbyte, also das Stringende, womit der ganze String als relevant für den Match angesehen wird. Die Initialisierung von string – nach diesem String wird später gesucht werden – dient nur der Befriedigung von Compilern, die sich ansonsten beschweren könnten, dass string möglicherweise nicht korrekt initialisert worden ist, da die eigentliche Zuweisung in einem switch-Statement erfolgt.

Dann werden die Kommandozeilenoptionen geparst, und zwar mit Hilfe der Bibliotheksfunktion getopt (“Get options”). Die Funktion interpretiert Elemente aus argv der Form -<char>, wobei char aus dem dritten Argument kommen muss, der als eine Art Formatstring dient – ist hinter einem Zeichen ein Doppelpunkt, so nimmt die entsprechende Option einen Parameter. Näheres ist der Manpage von getopt zu entnehmen. Das Codefragment hier kann durchaus als Schablone dienen, die für eigene Programme angepasst werden kann.

Was die einzelnen Optionen bedeuten, steht in der Manpage. Für uns ist auf jeden Fall wichtig -f, das festlegt, ob Groß-/Kleinschreibung beim Vergleich ignoriert werden soll oder nicht (case folding). Repräsentiert wird das hier durch eine globale Variable fflag. Dass hier mit globalen Variablen operiert wird, sollte nicht zur Nachahmung anregen.

Nach dem Parsen der Optionen werden argc und argv so manipuliert, dass nun nur noch normale Argumente sichtbar sind. Diese werden im folgenden switch ausgewertet; bemerkenswert daran ist allenfalls, dass, wenn nur ein Argument vorhanden ist, die d- und f-Flags künstlich gesetzt werden – die Manpage verlangt das so.

Danach geht die eigentliche Arbeit los. Die Details brauchen uns hier nicht zu interessieren, look verwendet hier eine Funktion, die es erlaubt, Dateien “in den Speicher einzublenden”, so dass wir in Dateien herumfahren können wie in Arrays. Dazu müssen die Dateien mit einer Funktion open geöffnet werden, die quasi unter dem bekannten fopen liegt und daher auch gefährlicher ist. Gibt sie eine negative Zahl zurück, ist ein Fehler aufgetreten und es wird eine entsprechende Medldung ausgegeben.

Danach erfolgt der Aufruf von mmap (Memory Map – “bilde in den Speicher ab”). Die Details interessieren hier wie gesagt nicht. Die folgende Erfolgskontrolle ist aber bemerkenswert – hier wird in Abhängigkeit von einem Präprozessormakro die Prüfbedingung verändert. Das ist nötig, weil die Semantik des mmap-Aufrufs verschiedener Systeme nicht einheitlich ist – manchmal deutet eine negative Rückgabe ein Problem an, manchmal die Rückgabe des magischen Werts MAP_FAILED.

Schließlich wird noch der Pointer auf das Ende des Suchbereichs gesetzt (das ist natürlich das Ende der gemappten Datei) und dann die eigentliche look-Funktion aufgerufen.


Markus Demleitner

Copyright Notice