You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
78 lines
2.2 KiB
78 lines
2.2 KiB
module Main where |
|
|
|
import Data.Char |
|
import System.Environment (getArgs) |
|
import System.IO (hPutStr, hGetChar, stdout, stdin, hWaitForInput |
|
, stderr, interact) |
|
import System.Exit (exitFailure) |
|
import Control.Exception (try) |
|
import System.IO.Error (isEOFError, ioError) |
|
|
|
alphaIndex :: Char -> Int |
|
alphaIndex c |
|
| elem c ['a'..'z'] = ord c - ord 'a' |
|
| elem c ['A'..'Z'] = ord c - ord 'A' |
|
| otherwise = 0 |
|
|
|
shift :: (Int -> Int -> Int) -> Char -> Char -> Char |
|
shift f c k |
|
| elem c ['a'..'z'] = go c k 'a' |
|
| elem c ['A'..'Z'] = go c k 'A' |
|
| otherwise = c |
|
where go p key base = let rel = f (alphaIndex p) (alphaIndex key) |
|
r = 26 |
|
b = ord base |
|
in chr $ (mod rel r) + b |
|
|
|
-- wrote own zipWith variant which maps only when isAlpha |
|
vigenere :: (Int -> Int -> Int) -> [Char] -> [Char] -> [Char] |
|
vigenere _ xs [] = xs -- necessary to avoid bottom |
|
vigenere f xs ys = myZipWith (shift f) xs ys |
|
where myZipWith _ [] _ = [] |
|
myZipWith f s [] = myZipWith f s ys |
|
myZipWith f (a:as) k@(b:bs) = |
|
if isAlpha a |
|
then f a b : myZipWith f as bs |
|
else a : myZipWith f as k |
|
|
|
readAll :: IO String |
|
readAll = do |
|
b <- try $ hWaitForInput stdin 5000 |
|
case b of |
|
Left e -> if isEOFError e |
|
then return "" |
|
else ioError e |
|
Right False -> do |
|
hPutStr stderr $ "Timeout." |
|
exitFailure |
|
Right True -> do |
|
c <- hGetChar stdin |
|
fmap (c:) readAll |
|
|
|
encrypt :: String -> IO () |
|
encrypt key = do |
|
s <- readAll |
|
let s' = vigenere (+) s key |
|
hPutStr stdout s' |
|
|
|
decrypt :: String -> IO () |
|
decrypt key = do |
|
s <- readAll |
|
let s' = vigenere (-) s key |
|
hPutStr stdout s' |
|
|
|
main :: IO () |
|
main = do |
|
args <- getArgs |
|
case args of |
|
["-e", key] -> encrypt key |
|
["-d", key] -> decrypt key |
|
otherwise -> putStrLn "Invalid arguments. Use (-d|-e) <key>." |
|
|
|
anotherWay :: IO () |
|
anotherWay = do |
|
args <- getArgs |
|
case args of |
|
["-e", key] -> interact $ flip (vigenere (+)) key |
|
["-d", key] -> interact $ flip (vigenere (-)) key |
|
otherwise -> putStrLn "Invalid arguments. Use (-d|-e) <key>."
|
|
|