{-# LANGUAGE RecordWildCards #-}
module System.Console.CmdArgs.Explicit.SplitJoin(splitArgs, joinArgs) where

import Data.Char
import Data.Maybe


-- | Given a sequence of arguments, join them together in a manner that could be used on
--   the command line, giving preference to the Windows @cmd@ shell quoting conventions.
--
--   For an alternative version, intended for actual running the result in a shell, see "System.Process.showCommandForUser"
joinArgs :: [String] -> String
joinArgs :: [String] -> String
joinArgs = [String] -> String
unwords ([String] -> String)
-> ([String] -> [String]) -> [String] -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (String -> String) -> [String] -> [String]
forall a b. (a -> b) -> [a] -> [b]
map String -> String
f
    where
        f :: String -> String
f x :: String
x = String
q String -> String -> String
forall a. [a] -> [a] -> [a]
++ String -> String
g String
x String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
q
            where
                hasSpace :: Bool
hasSpace = (Char -> Bool) -> String -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any Char -> Bool
isSpace String
x
                q :: String
q = ['\"' | Bool
hasSpace Bool -> Bool -> Bool
|| String -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null String
x]

                g :: String -> String
g ('\\':'\"':xs :: String
xs) = '\\'Char -> String -> String
forall a. a -> [a] -> [a]
:'\\'Char -> String -> String
forall a. a -> [a] -> [a]
:'\\'Char -> String -> String
forall a. a -> [a] -> [a]
:'\"'Char -> String -> String
forall a. a -> [a] -> [a]
: String -> String
g String
xs
                g "\\" | Bool
hasSpace = "\\\\"
                g ('\"':xs :: String
xs) = '\\'Char -> String -> String
forall a. a -> [a] -> [a]
:'\"'Char -> String -> String
forall a. a -> [a] -> [a]
: String -> String
g String
xs
                g (x :: Char
x:xs :: String
xs) = Char
x Char -> String -> String
forall a. a -> [a] -> [a]
: String -> String
g String
xs
                g [] = []


data State = Init -- either I just started, or just emitted something
           | Norm -- I'm seeing characters
           | Quot -- I've seen a quote

-- | Given a string, split into the available arguments. The inverse of 'joinArgs'.
splitArgs :: String -> [String]
splitArgs :: String -> [String]
splitArgs = [Maybe Char] -> [String]
join ([Maybe Char] -> [String])
-> (String -> [Maybe Char]) -> String -> [String]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. State -> String -> [Maybe Char]
f State
Init
    where
        -- Nothing is start a new string
        -- Just x is accumulate onto the existing string
        join :: [Maybe Char] -> [String]
        join :: [Maybe Char] -> [String]
join [] = []
        join xs :: [Maybe Char]
xs = (Maybe Char -> Char) -> [Maybe Char] -> String
forall a b. (a -> b) -> [a] -> [b]
map Maybe Char -> Char
forall a. HasCallStack => Maybe a -> a
fromJust [Maybe Char]
a String -> [String] -> [String]
forall a. a -> [a] -> [a]
: [Maybe Char] -> [String]
join (Int -> [Maybe Char] -> [Maybe Char]
forall a. Int -> [a] -> [a]
drop 1 [Maybe Char]
b)
            where (a :: [Maybe Char]
a,b :: [Maybe Char]
b) = (Maybe Char -> Bool)
-> [Maybe Char] -> ([Maybe Char], [Maybe Char])
forall a. (a -> Bool) -> [a] -> ([a], [a])
break Maybe Char -> Bool
forall a. Maybe a -> Bool
isNothing [Maybe Char]
xs

        f :: State -> String -> [Maybe Char]
f Init (x :: Char
x:xs :: String
xs) | Char -> Bool
isSpace Char
x = State -> String -> [Maybe Char]
f State
Init String
xs
        f Init "\"\"" = [Maybe Char
forall a. Maybe a
Nothing]
        f Init "\"" = [Maybe Char
forall a. Maybe a
Nothing]
        f Init xs :: String
xs = State -> String -> [Maybe Char]
f State
Norm String
xs
        f m :: State
m ('\"':'\"':'\"':xs :: String
xs) = Char -> Maybe Char
forall a. a -> Maybe a
Just '\"' Maybe Char -> [Maybe Char] -> [Maybe Char]
forall a. a -> [a] -> [a]
: State -> String -> [Maybe Char]
f State
m String
xs
        f m :: State
m ('\\':'\"':xs :: String
xs) = Char -> Maybe Char
forall a. a -> Maybe a
Just '\"' Maybe Char -> [Maybe Char] -> [Maybe Char]
forall a. a -> [a] -> [a]
: State -> String -> [Maybe Char]
f State
m String
xs
        f m :: State
m ('\\':'\\':'\"':xs :: String
xs) = Char -> Maybe Char
forall a. a -> Maybe a
Just '\\' Maybe Char -> [Maybe Char] -> [Maybe Char]
forall a. a -> [a] -> [a]
: State -> String -> [Maybe Char]
f State
m ('\"'Char -> String -> String
forall a. a -> [a] -> [a]
:String
xs)
        f Norm ('\"':xs :: String
xs) = State -> String -> [Maybe Char]
f State
Quot String
xs
        f Quot ('\"':'\"':xs :: String
xs) = Char -> Maybe Char
forall a. a -> Maybe a
Just '\"' Maybe Char -> [Maybe Char] -> [Maybe Char]
forall a. a -> [a] -> [a]
: State -> String -> [Maybe Char]
f State
Norm String
xs
        f Quot ('\"':xs :: String
xs) = State -> String -> [Maybe Char]
f State
Norm String
xs
        f Norm (x :: Char
x:xs :: String
xs) | Char -> Bool
isSpace Char
x = Maybe Char
forall a. Maybe a
Nothing Maybe Char -> [Maybe Char] -> [Maybe Char]
forall a. a -> [a] -> [a]
: State -> String -> [Maybe Char]
f State
Init String
xs
        f m :: State
m (x :: Char
x:xs :: String
xs) = Char -> Maybe Char
forall a. a -> Maybe a
Just Char
x Maybe Char -> [Maybe Char] -> [Maybe Char]
forall a. a -> [a] -> [a]
: State -> String -> [Maybe Char]
f State
m String
xs
        f m :: State
m [] = []