@@ -99,6 +99,9 @@ module Graphics.Vty.Config
9999 , vtyDataDirectory
100100 , terminalWidthTablePath
101101 , vtyConfigFileEnvName
102+
103+ , ConfigUpdateResult(..)
104+ , addConfigWidthMap
102105 )
103106where
104107
@@ -122,9 +125,11 @@ import Graphics.Vty.Input.Events
122125
123126import GHC.Generics
124127
125−import System.Directory (getAppUserDataDirectory)
128+import System.Directory ( getAppUserDataDirectory, doesFileExist
129+ , createDirectoryIfMissing
130+ )
126131import System.Environment (lookupEnv)
127−import System.FilePath ((</>))
132+import System.FilePath ((</>), takeDirectory)
128133import System.Posix.IO (stdInput, stdOutput)
129134import System.Posix.Types (Fd(..))
130135import Foreign.C.Types (CInt(..), CChar(..))
@@ -419,3 +424,56 @@ getTtyEraseChar fd = do
419424 if c /= 0
420425 then return $ Just $ toEnum $ fromEnum c
421426 else return Nothing
427+
428+data ConfigUpdateResult =
429+ ConfigurationCreated
430+ | ConfigurationModified
431+ | ConfigurationConflict String
432+ | ConfigurationRedundant
433+ deriving (Eq, Show)
434+
435+-- | Add a @widthMap@ directive to the Vty configuration file at the
436+-- specified path.
437+--
438+-- If the configuration path refers to a configuration that already
439+-- contains the directive for the specified map and terminal type, the
440+-- configuration file will not be modified. If the file does not contain
441+-- the directive, it will be appended to the file.
442+--
443+-- If the configuration path does not exist, a new configuration file
444+-- will be created and any directories in the path will also be created.
445+--
446+-- This returns @True@ if the configuration was created or modified and
447+-- @False@ otherwise. This does not handle exceptions raised by file or
448+-- directory permissions issues.
449+addConfigWidthMap :: FilePath
450+ -- ^ The configuration file path of the configuration
451+ -- to modify or create.
452+ -> String
453+ -- ^ The @TERM@ value for the @widthMap@ directive.
454+ -> FilePath
455+ -- ^ The width table file path for the directive.
456+ -> IO ConfigUpdateResult
457+addConfigWidthMap configPath term tablePath = do
458+ configEx <- doesFileExist configPath
459+ if configEx
460+ then updateConfig
461+ else createConfig >> return ConfigurationCreated
462+
463+ where
464+ directive = "widthMap " <> show term <> " " <> show tablePath <> "\n"
465+
466+ createConfig = do
467+ let dir = takeDirectory configPath
468+ createDirectoryIfMissing True dir
469+ writeFile configPath directive
470+
471+ updateConfig = do
472+ config <- parseConfigFile configPath
473+ if (term, tablePath) `elem` termWidthMaps config
474+ then return ConfigurationRedundant
475+ else case lookup term (termWidthMaps config) of
476+ Just other -> return $ ConfigurationConflict other
477+ Nothing -> do
478+ appendFile configPath directive
479+ return ConfigurationModified