packages feed

tap-0.1.0.0: exe/Main.hs

module Main (main) where

import Control.Monad
import Data.Binary.Get
import Data.Binary.Put
import qualified Data.ByteString.Lazy as BS
import qualified Data.List.NonEmpty as NE
import Data.Tap
import Data.Version
import Data.Word
import Options.Applicative
import Paths_tap

main :: IO ()
main = do
  CLI dfn cmd <- customExecParser prefs' $ pinfo $ showVersion version
  d <- either (fail . mappend "definition error: ") return $ fmt dfn
  case cmd of
    ReadTap fM -> print . runGet (getTap d) =<< maybe BS.getContents BS.readFile fM
    WriteTap (v,p) fs fM -> do
      fs' <- either (fail . mappend "field error: ") return $ unk d =<< fmt fs
      let ps = mkPrsnts $ map num fs'
          hlen = 4 + fromIntegral (NE.length ps) * 4
          slen = sum $ map structLen $ padStructs $ structs =<< fs'
          tlen = hlen + slen
      when (tlen >= fromIntegral (maxBound :: Word16)) $
        fail $ "struct length too long: " <> show tlen
      maybe BS.putStr BS.writeFile fM $ runPut $ putTap $
        Tap (Hdr v p (fromIntegral tlen) ps) fs'

data CLI = CLI [Fld] Cmd

data Cmd = ReadTap (Maybe FilePath)
  | WriteTap (Word8, Word8) [Fld] (Maybe FilePath)

prefs' :: ParserPrefs
prefs' = prefs $ showHelpOnError <> showHelpOnEmpty

pinfo :: String -> ParserInfo CLI
pinfo v = info (parser <**> simpleVersioner v <**> helper) $ progDesc "radiotap"

parser :: Parser CLI
parser = liftA2 CLI (concat <$> many parseDefn) $ hsubparser $ mconcat
  [ command "r" $ info (ReadTap <$>
    optional (fileArg "binary intput file or stream STDIN")) $
    progDesc "read radiotap data"
  , command "w" $ info (WriteTap <$> parseHdr <*> many parseFld <*>
    optional (fileArg "binary output file or stream STDOUT")) $
    progDesc "write radiotap data"]

fileArg :: String -> Parser FilePath
fileArg h = strArgument $ metavar "FILE" <> completer (bashCompleter "file") <> help h

parseHdr :: Parser (Word8, Word8)
parseHdr = parserOptionGroup "Header" $ (,)
  <$> option auto (long "ver" <> metavar "WORD8" <> value 0 <> showDefault <> help "header version")
  <*> option auto (long "hpd" <> metavar "WORD8" <> value 0 <> showDefault <> help "header pad")

parseFld :: Parser Fld
parseFld = parserOptionGroup "Fields" $ Fld ""
  <$> option auto (short 'f' <> metavar "INT" <> help "field num")
  <*> many parseStructVal

parseDefn :: Parser [Fld]
parseDefn = parserOptionGroup "Definition" $ asum
  [ flag' defFlds $ long "dfs" <> help "radiotap defined fields"
  , fmap pure $ Fld
    <$> strOption (long "name" <> metavar "STR" <> help "field definition name")
    <*> option auto (long "num" <> metavar "INT" <> help "field definition num")
    <*> many parseStruct
  ]

parseStruct :: Parser Struct
parseStruct = asum
  [ flag' (U8  0) $ long "du8"  <> help "u8 field definition"
  , flag' (S8  0) $ long "ds8"  <> help "s8 field definition"
  , flag' (U16 0) $ long "du16" <> help "u16 field definition"
  , flag' (S16 0) $ long "ds16" <> help "s16 field definition"
  , flag' (U32 0) $ long "du32" <> help "u32 field definition"
  , flag' (S32 0) $ long "ds32" <> help "s32 field definition"
  , flag' (U64 0) $ long "du64" <> help "u64 field definition"
  , flag' (S64 0) $ long "ds64" <> help "s64 field definition"
  ]

parseStructVal :: Parser Struct
parseStructVal = asum
  [ fmap U8  $ option auto $ long "u8"  <> metavar "WORD8"  <> help "u8 value"
  , fmap S8  $ option auto $ long "s8"  <> metavar "INT8"   <> help "s8 value"
  , fmap U16 $ option auto $ long "u16" <> metavar "WORD16" <> help "u16 value"
  , fmap S16 $ option auto $ long "s16" <> metavar "INT16"  <> help "s16 value"
  , fmap U32 $ option auto $ long "u32" <> metavar "WORD32" <> help "u32 value"
  , fmap S32 $ option auto $ long "s32" <> metavar "INT32"  <> help "s32 value"
  , fmap U64 $ option auto $ long "u64" <> metavar "WORD64" <> help "u64 value"
  , fmap S64 $ option auto $ long "s64" <> metavar "INT64"  <> help "s64 value"
  ]