65 lines
2.3 KiB
Haskell
65 lines
2.3 KiB
Haskell
-- SPDX-FileCopyrightText: 2024 David Mosbach <david.mosbach@uniworx.de>
|
|
--
|
|
-- SPDX-License-Identifier: AGPL-3.0-or-later
|
|
|
|
{-# Language OverloadedStrings, LambdaCase, TypeApplications #-}
|
|
|
|
import Data.Text (Text)
|
|
import qualified Data.Text as T
|
|
import System.Directory
|
|
import System.Environment
|
|
import System.IO
|
|
|
|
main :: IO ()
|
|
main = getArgs >>= \case
|
|
["--assign", offsetFile] -> parseOffsets offsetFile >>= uncurry nextOffset
|
|
["--remove", offset] -> removeOffset offset
|
|
_ -> fail "unsupported args"
|
|
|
|
parseOffsets :: FilePath -> IO (Int,Int)
|
|
parseOffsets offsetFile = do
|
|
user <- T.pack <$> getEnv "USER"
|
|
let pred x = "//" `T.isPrefixOf` x || T.null (T.strip x)
|
|
tokenise = map (filter (not . pred) . T.lines) . T.split (=='#')
|
|
extract = map tail . filter (\u -> not (null u) && user == (T.strip $ head u))
|
|
((extract . tokenise . T.pack) <$> readFile offsetFile) >>= \case
|
|
[[min,max]] -> return (read $ T.unpack min, read $ T.unpack max)
|
|
x -> print x >> fail "malformed offset file"
|
|
|
|
nextOffset :: Int -> Int -> IO ()
|
|
nextOffset min max
|
|
| min > max = nextOffset max min
|
|
| otherwise = do
|
|
home <- getEnv "HOME"
|
|
offset <- findFile [home] ".port-offsets" >>= \case
|
|
Nothing -> writeFile (home ++ "/.port-offsets") (show min) >> return min
|
|
Just path -> do
|
|
used <- (map (read @Int) . filter (not . null) . lines) <$> readFile path
|
|
o <- next min max used
|
|
appendFile path ('\n' : show o)
|
|
return o
|
|
print offset
|
|
where
|
|
next :: Int -> Int -> [Int] -> IO Int
|
|
next min max used
|
|
| min > max = fail "all offsets currently in use"
|
|
| min `elem` used = next (min+1) max used
|
|
| otherwise = return min
|
|
|
|
removeOffset :: String -> IO ()
|
|
removeOffset offset = do
|
|
home <- getEnv "HOME"
|
|
findFile [home] ".port-offsets" >>= \case
|
|
Nothing -> fail "offset file does not exist"
|
|
Just path -> do
|
|
remaining <- (filter (/= offset) . lines) <$> readFile path
|
|
run <- getEnv "XDG_RUNTIME_DIR"
|
|
(tempPath, fh) <- openTempFile run ".port-offsets"
|
|
let out = unlines remaining
|
|
hPutStr fh $ out
|
|
case T.null (T.strip $ T.pack out) of
|
|
True -> removeFile path
|
|
False -> writeFile path $ out
|
|
removeFile tempPath
|
|
|