2012-01-23 14 views
12

Esiste un modo per disattivare (temporaneamente) l'effetto di interruzione di control-C in un eseguibile Haskell con run-in-a-shell?Posso disabilitare control-C?

Contesto: ho un piccolo esercizio che ho impostato ogni anno. È un gioco in cui gli studenti eseguono la chiusura lampo intorno a espressioni che attivano la riscrittura della corrispondenza del modello come applicabile, eseguendo in una finestra della shell utilizzando hncurses. Quando gli studenti hanno terminato l'ultimo puzzle, ottengono una password individuale di cui hanno bisogno per mandarmi. Essendo veterani di Windows e neofiti di Unix, e naturalmente (virtuosamente) pigri, tendono a selezionare la password e digitare control-C. Questo ha l'effetto non intenzionale di interrompere il programma e far scomparire la password. L'unico modo per recuperarlo è ripetere l'esercizio. Che crudele!

Mentre ci sono altri modi per aggirare il problema (ad es., Stampare un messaggio di avviso o scrivere la password in un file), sono curioso di sapere se c'è modo di disattivare il controllo-C.

+0

Io so che sono passati alcuni anni, ma dovresti essere in grado di fare qualcosa di simile a ciò che Ingo suggerisce all'interno del tuo programma, ricablando il terminale dell'utente invece di installare un blocco di segnale. Questo mi sembra l'approccio più pulito. – dfeuer

risposta

12

Sì, basta ignorare temporaneamente SIGINT: (. E 'possibile anche scrivere keyboardSignal come sigINT se si vuole dare un nome più direttamente)

import Prelude hiding (catch) 
import Control.Exception 
import Control.Concurrent 
import System.Posix.Signals 

-- Ensures the original handler is restored even after an exception. 
-- 
-- installHandler returns the previous signal handler, so if you're 
-- tied to some existing main loop, you could instead just keep 
-- its return value around for later restoration. 
ignoreSignal :: Signal -> IO a -> IO a 
ignoreSignal sig = bracket (install Ignore) install . const 
    where install handler = installHandler sig handler Nothing 

pause :: IO() 
pause = threadDelay 2000000 

main :: IO() 
main = do 
    ignoreSignal keyboardSignal $ do 
    putStrLn "Ctrl+C disabled." 
    pause 
    putStrLn "\nCtrl+C re-enabled." 
    pause 

Si può anche sostituire Ignore con Catch m , dove m è un'azione che stampa le indicazioni sul modo corretto di copiare il testo. (Attenzione, però: sarà eseguito in un thread Haskell separato.)

1

si poteva prendere esplicitamente l'eccezione, che credo sia UserInterrupt:

handle (\exception -> do 
    case fromException exception of 
      Just UserInterrupt -> -- return to problem 
      _     -> error "unhandled exception") playWithStudents 
8

Facile e veloce, utilizzare la shell:

stty intr ^T 
run your haskell program here 
stty intr ^C 

Questo ha l'ulteriore vantaggio che i programmi possono ancora essere interrotti.

Problemi correlati