-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathecho.hs
69 lines (60 loc) · 2.62 KB
/
echo.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
module Main(main) where
import System.Environment (getArgs)
import System.Console.GetOpt
import Data.Char (chr, isOctDigit, digitToInt)
-- Two common echos:
-- (SysV) -n skips newline,
-- (BSD) \c in string skips newline, when escapes interpreted.
main = do args <- getArgs
let (interpret, skip, remains, errs) = get_flags args
let (reallyskip, string) = if interpret then
rewrite_string skip (unwords remains)
else (skip, unwords remains)
putStr string
if not reallyskip then putStrLn "" else return ()
escapetable '\\' = "\\"
-- escapetable '\number' = octal value
escapetable 'a' = "\a"
escapetable 'b' = "\b"
-- escapetable 'c' = suppress newline
escapetable 'f' = "\f"
escapetable 'n' = "\n"
escapetable 'r' = "\r"
escapetable 't' = "\t"
escapetable 'v' = "\v"
escapetable x = '\\':x:[]
data Flag = Interpret | SkipNewline deriving (Show, Eq)
options :: [OptDescr Flag]
options = [ Option ['n'] [] (NoArg SkipNewline) "do not output newline"
, Option ['e'] [] (NoArg Interpret) "Interpret escape sequences"
]
get_flags argv = case getOpt RequireOrder options argv of
(opts, remains, errs) ->
let interpret = Interpret `elem` opts
skip = SkipNewline `elem` opts
in (interpret, skip, remains, errs)
-- Should stick in Writer monad or something?
-- but it's the string argument that's sequential.
-- don't want to stick it in lisst monoid though.
rewrite_string :: Bool -> String -> (Bool, String)
rewrite_string skip ('\\':'c':rest) = rewrite_string True rest
rewrite_string skip ('\\':[]) = (skip, "\\")
rewrite_string skip ('\\':rest) = let (int, more) = string_escape rest
(skip', cont) = rewrite_string skip more
in (skip', int ++ cont)
rewrite_string skip (x:xs) = let (skip', cont) = rewrite_string skip xs
in (skip', x:cont)
rewrite_string skip [] = (skip, [])
string_escape :: String -> (String, String)
string_escape [] = ([],[])
string_escape x = if isOctDigit (head x)
then let (value, rest) = break isOctDigit x
in ([(chr (fromOctal value))], rest)
else (escapetable (head x), tail x)
-- Shouldn't I be able to reuse more standard library (Numeric.*) for this?
octalValue x | isOctDigit x = digitToInt x
octalValue err = error $ "octalValue called with " ++ [err] ++ "."
fromOctal :: String -> Int
fromOctal s = fromOctal' 0 s where
fromOctal' acc [] = acc
fromOctal' acc (x:xs) = fromOctal' (8*acc + octalValue x) xs