2013-11-10 21 views
6

Ich kann perror() oder strerror() verwenden, um die „human readable“ Fehlermeldung zu einem errno gehören, zu drucken, aber was ist, wenn ich auch den symbolischen Namen drucken möchten (zum Beispiel „EAGAIN“) des errno?Wie kann ich drucken den symbolischen Namen eines errno in C?

Jede geeignete Funktion oder ein Makro, das zu tun?

Update: den Code Anbringen ich mit dem Schreiben endete, basiert auf der Idee der akzeptierte Antwort unten und seine Kommentare:

#include <ctype.h> 
#include <errno.h> 
#include <stdio.h> 

int get_errno_name(char *buf, int buf_size) { 
    // Using the linux-only gawk instead of awk, because of the convenient 
    // match() functionality. For Posix portability, use a different recipe... 
    char cmd[] = "e=  && " // errno to be inserted here (max digits = 6) 
       "echo '#include <errno.h>' | " 
       "gcc -dM -E - | " // optionally, use $CC inead of gcc 
       "gawk \"match(\\$0, /^#[[:space:]]*define[[:space:]]+" 
        "(E[[:alnum:]]+)[[:space:]]+$e($|[^[:alnum:]])/, m) " 
        "{ print m[1] }\""; 
    { 
     // Insert the errno as the "e" shell variable in the command above. 
     int errno_digit_c = snprintf(cmd + 2, 6, "%d", errno); 
     if (errno_digit_c < 1) { 
      fprintf(stderr, "Failed to stringify an errno " 
          "in get_errno_name().\n"); 
      return -1; 
     } 
     // Replace the inserted terminating '\0' with whitespace 
     cmd[errno_digit_c + 2] = ' '; 
    } 
    FILE *f = popen(cmd, "r"); 
    if (f == NULL) { 
     perror("Failed to popen() in get_errno_name()"); 
     return -1; 
    } 
    int read_size = 0, c; 
    while ((c = getc(f)) != EOF) { 
     if (isalnum(c)) { 
      buf[read_size++] = c; 
      if (read_size + 1 > buf_size) { 
       fprintf(stderr, "Read errno name is larger than the character " 
           "buffer supplied to get_errno_name().\n"); 
       return -1; 
      } 
     } 
    } 
    buf[read_size++] = '\0'; 
    if (pclose(f) == -1) { 
     perror("Failed to pclose() in get_errno_name()"); 
     return -1; 
    } 
    return read_size; 
} 
+0

Interessanter Code. Ich würde keine Funktion bereitstellen, die einen C-Compiler in einer Produktionsumgebung ausgeführt hat - es ist sowohl langsam als auch "unzuverlässig", da einige Leute zur Laufzeit keine Compiler haben.Es gibt reichlich Gelegenheit zum Zwischenspeichern von Ergebnissen unter der Annahme, dass sich der Satz von Fehlermeldungen normalerweise nicht ändert, außer (vielleicht) wenn das Betriebssystem aktualisiert wird, und selbst dann bedeutet Abwärtskompatibilität, dass die meisten Fehlernummern unverändert bleiben. Sie könnten Ihren Code jedoch dazu verwenden, die Daten für alle Systemfehler zu sammeln und diese Ausgabe dann in einer Funktion zu verwenden, die zum Zeitpunkt der Kompilierung von den Daten gesteuert wird. –

+0

@ JonathanLeffler Vielen Dank für Ihr Feedback. Vereinbarte auf beiden Konten. Ich hätte wahrscheinlich erwähnen sollen, dass das oben genannte definitiv nicht als solches bereitgestellt werden soll (es sei denn, Sie haben keine Probleme mit den Laufzeitabhängigkeiten von gcc und gawk ...). Und ja; Code wie dieser würde besser als Teil eines (c) make-Skripts zur Kompilierzeit ausgeführt werden. Meine Projekte sind jedoch eher klein, so dass ich mich normalerweise nicht mit solchen Skripten beschäftige. Aus diesem Grund ist der obige Code für meine persönlichen Debug-Builds am besten geeignet. – Will

Antwort

4

Es gibt keine einfache Möglichkeit, das zu tun.

Sie können ein Programm erstellen - und ich habe ein Programm erstellt, das als Bibliotheksfunktion neu verpackt werden kann -, das von Zahl in Name konvertiert. Aber das Erzeugen des Tisches ist mäßig hart. Ich benutze ein Perl-Skript, das den Compiler (GCC oder gleichwertig) mit Optionen (-H) ausführt, um die eingeschlossenen Header aufzulisten, einschließlich /usr/include/errno.h, und scannt dann diese Dateien nach Namen (#define plus E gefolgt von einem Großbuchstaben) oder Ziffer), Zahlen und Kommentare. Dies funktioniert unter Linux, Mac OS X, Solaris, HP-UX, AIX. Es ist nicht besonders trivial. Die errno.h unter Mac OS X enthält einen Namen ELAST (ein Name, der für die zu verwendende Implementierung reserviert ist), der ein Duplikat der höchsten Nummer ist (aber das Mapping ändert sich von Release zu Release; es war 105 in Mountain Lion) , Glaube ich, aber es ist 106 in Mavericks).

+1

'echo '#include ' | $ CC -dM -E - | grep -E '^ # [[: space:]] * definiere [[: space:]] + E'' –

+0

@R ..: mehr oder weniger, ja, aber wenn du das sinnvoll kompilieren willst, du müssen den symbolischen Namen in eine Zeichenkette konvertieren (und eine Kopie davon) und möglicherweise den Kommentar erfassen (obwohl Sie 'strerror()' verwenden könnten, um den Teil der Fehlermeldung zu behandeln), usw. –

+0

Das war nur ein Skript, um die Liste zu bekommen von '# define's in einer sauberen, meist portablen Weise (besser als grepping spezifische Pfadnamen, die Cross-Compiling und andere Setups auf schreckliche Weise kaputt machen). Sie müssen die Ausgabe weiterhin in einer Zeichenfolgetabelle verarbeiten. –

2

Meines Wissens kann man nicht. Einige ganzzahlige Fehlerkonstanten werden sowieso mehr als einem symbolischen Namen zugeordnet, z. EAGAIN und EWOULDBLOCK. Offensichtlich können Sie sie auf der Manpage des Befehls suchen, der errno gesetzt hat.

+1

EAGAIN und EWOULDBLOCK sind die einzigen beiden, die identisch sein können. Alle anderen müssen unterschiedliche Werte haben. –

+1

EDEADLK und EDEADLOCK auch auf Linux – Duck

+0

POSIX [''] (http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/errno.h.html) listet EAGAIN und EWOULDBLOCK als Paar auf, aber auch ENOTSUP und EOPNOTSUPP . Plattformen können andere doppelte Namen haben. Da Sie für eine gegebene Nummer nur einen Namen benötigen, können Sie einen als kanonischen Namen auswählen. –

0

Ich weiß nicht, von einer Funktion, die dies tut, aber es wäre nicht schwer, einen zu schreiben, wenn Sie sind vertraut mit den Fehlern in Ihrem Programm zurückgegeben werden.

Sie könnten Strings schreiben, die die Ausgaben von strerror() enthalten, und eine for-Schleife und if-Anweisungen mit strcmp() verwenden, um die von Ihnen geschriebenen Strings zu vergleichen. Wenn sie identisch sind, können Sie den symbolischen Namen * ausgeben, wenn Sie diesen auf eine andere Zeichenfolge setzen.

+0

Warum sollten Sie 'strcmp' gegen die Ausgabe von' strerror' verwenden? – Leigh

+0

Sie könnten einfach in errno.h nachschlagen und etwas darauf basierend schreiben. Das wird funktionieren ... bis es nicht geht, weil sich etwas geändert hat. – Duck

1

Wenn Sie wissen, welche Fehler Sie erwarten, können Sie große switch/case oder if/else Blöcke gegen errno, in der folgenden Art schreiben:

if (errno == EAGAIN) 
    fprintf(stderr, "EAGAIN"); 

Das offensichtliche Problem dabei ist, dass, wenn Sie die spezifische errno "Name", Sie müssen gegen jede mögliche Option schreiben, und es gibt einige.

+0

Das ist nicht sehr allgemein. Zumindest erstellen Sie eine Funktion, die ein Array von Strings verwendet, und gibt einen 'char const *' an den Namen zurück, der der Zahl entspricht. –

0

Da die symbolischen Namen als Enumerationen gespeichert sind, werden sie von C als Ints behandelt. Sie SO eine ähnliche Funktion wie diese How to convert enum names to string in c Frage erstellen müssen. Sie könnten wahrscheinlich leicht genug, um einen Makro verkürzen, aber für jeden Fehler einen Fall zu schaffen müßten getan werden.

+2

Der C-Standard (mindestens C99) besagt, dass die 'E *' -Konstanten Makros sind, keine Aufzählungen. – jwodder

+0

Nun, der GNU-Compiler schaltet sie ein und behandelt sie als enums. Ich entschuldige mich für irreführende Informationen. –