2016-05-25 8 views
0

Ich mache einen Parser bekommen Zeichen bis EOF, ich handle die Signale SIGTERM, SIGINT, SIGTSTP und SIGQUIT. Wenn ich also ein Signal mit Ctrl +C (zum BeispielSIGINT) die Signal-Handler drucken Ctrl +C senden und dann muss weitergehen, aber weiterhin nicht.Ctrl Signale füllt Stdin mit EOF?

Ich fand heraus, dass nach jedem Ctrl Signal, ein EOF füllt den Puffer, aber ich weiß nicht, wie man es loswerden. Ich habe versucht, while(getc(stdin) != EOF); innerhalb der Handler zu tun, aber der Parser verhalten sich normal, "Essen" jedes Zeichen eingegeben bis EOF.

Wie kann ich ein Ctrl Signal empfangen, ohne meine stdin zu verpfuschen?

#include <stdio.h> 
#include <unistd.h> 
#include <signal.h> 

static void nsh_signal_handler(int code) 
{ 
    char c; 

    switch(code) 
    { 
     case SIGQUIT: c = '\\'; break; 
    } 

    fprintf(stdout, "I will do nothing about your Ctrl - %c\n", c); 
} 

int main(void) 
{ 
    int c; 
    struct sigaction s; 

    s.sa_flags = 0; 
    s.sa_handler = nsh_signal_handler; 
    sigfillset(&s.sa_mask); 
    sigaction(SIGQUIT, &s, NULL); 

    do 
    { 
     c = getc(stdin); 
     printf("[%c][%d]\n", c, c); 
    } 
    while(c != EOF); 

    return 0; 
} 

Der obige Code zeigt diesen Fehler.

+0

Hinweis [Wie vermeidet man die Verwendung von 'printf()' in einem Signal-Handler?] (Http://Stackoverflow.com/questions/16891019/) –

+0

Hinweis: Setzen Sie in 'nsh_signal_handler()' 'char c = ' ? '; 'oder etwas, um zu vermeiden, einen nicht initialisierten Wert mit' fprintf (stdout, "I ...% c \ n", c) zu drucken; ' – chux

Antwort

1

Der getc Aufruf gibt EOF entweder für ein echtes Ende der Datei oder einen Fehlerzustand zurück.

Wenn Sie zwischen den beiden unterscheiden müssen, können Sie dazu feof oder ferror verwenden.

Für Ihre speziellen Fall können Sie einfach ersetzen:

while(c != EOF); 

mit:

while ((c != EOF) || (! feof(stdin))); 

Vielleicht wäre noch besser zu vermeiden versuchen, den Charakter überhaupt ausgegeben, wenn es eine, die gefangen wurde durch das Signal. Sie können dies tun, indem Sie die Schleife zu etwas zu ändern wie:

do { 
    c = getc(stdin); 
    while ((c == EOF) && (!feof(stdin))) { // throw away CTRL-whatever. 
     //clearerr (stdin); 
     c = getc(stdin); 
    } 
    printf("[%c][%d]\n", c, c);   // print true characters and last EOF. 
} 
while (c != EOF); 

Sie werden feststellen, es gibt einen Aufruf an clearerr oben im Code als Kommentar gekennzeichnet. Wenn Ihre Implementierung eine solche ist, die fortwährend eine Fehleranzeige liefert, bis Sie das Fehlerflag gelöscht haben (Linux gehört nicht dazu), möchten Sie es möglicherweise auskommentieren, besonders da es sonst den Fluss des Codes nicht beeinträchtigen würde.

ich tatsächlich, dass das Verhalten als wahrscheinliche Verletzung der Norm sehen, da er ausdrücklich fest:

Wenn der End-of-Datei Indikator für den Strom gesetzt ist, oder wenn der Strom ist am Ende des -Datei, wird der EndOffile-Indikator für den Stream gesetzt und die Funktion fgetc gibt EOF zurück. Andernfalls gibt die Funktion fgetc das nächste Zeichen aus dem Eingabestrom zurück, auf den Stream verweist. Wenn ein Lesefehler auftritt, wird der Fehlerindikator für den Stream gesetzt, und die Funktion fgetc gibt EOF zurück.

Mit anderen Worten, keine Erwähnung ist aus einer vorhergehenden Fehleranzeige verhindert Zukunft liest.Die einzige Möglichkeit, die ich sehen konnte, ist, wenn der Text "ein Lesefehler auftritt" das Ereignis "ein Lesefehler ist bereits aufgetreten und der Fehlerindikator wurde nicht zurückgesetzt" enthält, was für mich ein bisschen eine Strecke zu sein scheint.

+1

Sobald ein Fehler aufgetreten ist (zB ein' EINTR', wie passiert hier) JEDER Aufruf von getc wird EOF zurückgeben, und 'ferror (stdin)' wird wahr sein, bis Sie 'clearerr' aufrufen. Also müssen Sie einen Aufruf an 'clearerr (stdin)' in der inneren while-Schleife tätigen, bevor Sie 'getc' aufrufen, oder es wird für immer eine Schleife durchlaufen (scheinbar hängt es). –

+0

@Chris, eigentlich glaube ich nicht, dass das so ist. ISO (C11 7.21.7.1) besagt nur, dass, wenn das eof-Flag für den Stream gesetzt ist oder eof trifft, es EOF zurückgibt und das eof-Flag setzt. Sonst bekommt es einen Charakter. Es kann * das * Fehler-Flag setzen, aber es überprüft es nicht. – paxdiablo

+0

Der C-Standard sagt nichts darüber aus, was bei einer Fehlerbedingung passiert, und lässt die Implementierung definiert. Die meisten Implementierungen behandeln es als persistent - sobald das Error-Flag gesetzt ist, gibt jeder Aufruf einen Fehler zurück, bis er mit 'clearerr' gelöscht wird. –

0

Wenn Sie möchten, mit Fehlern auf Ihrer Lese Anrufe nicht das Signal nach dem Umgang mit umgehen, können Sie die SA_RESTART Flagge auf der Signal-Handler gesetzt:

s.sa_flags = SA_RESTART; 

, die in den getc Anrufe führen durch den Signalhandler nicht mit einem EINTR Fehler unterbrochen werden.