% from AutoTrack by Stefan Ratschan

For extracting the options from the command line we use the \texttt{GetOpt} package
proviced by \texttt{ghc}. This is currently a little bit of a mess.

\begin{haskelllisting}

> module Option(T, getAll) where

> import qualified Haskore.Music.GeneralMIDI as MidiMusic
> import qualified Haskore.Interface.AutoTrack.Style      as Style
> import qualified Haskore.Interface.AutoTrack.ChordChart as ChordChart

> import System.Console.GetOpt (getOpt, usageInfo,
>            ArgDescr(NoArg, ReqArg), OptDescr(Option), ArgOrder(Permute))
> import System.Environment (getArgs)
> import System.Exit (exitWith, ExitCode(ExitSuccess, ExitFailure))

> import Haskore.General.Utility (headWithDefault)
> import Data.Maybe (listToMaybe, mapMaybe)
> import Data.List (intersperse)

> data T = Error     String
>        | Tempo     Integer
>        | Style     Style.T
>        | Transpose Int
>        | Choruses  Int
>        | Help

> isHelp :: T -> Bool
> isHelp Help = True
> isHelp _    = False

> errorToMaybe :: T -> Maybe String
> errorToMaybe (Option.Error m) = Just m
> errorToMaybe _                = Nothing

> options :: [ OptDescr T ]
> options = [ Option [ 't' ] [ "tempo" ] (ReqArg tempoOption "TEMPO") "TEMPO of track",
>             Option [ 'r' ] [ "transpose" ] (ReqArg transposeOption "TRANSPOSE") "TRANSPOSE track",
>             Option [ 's' ] [ "style" ] (ReqArg styleOption "STYLE") "music STYLE",
>             Option [ 'c' ] [ "choruses" ] (ReqArg chorusesOption "CHORUSES") "number of CHORUSES",
>             Option [ 'h' ] [ "help" ] (NoArg Option.Help) "display usage" ]

> tempoOption, transposeOption, styleOption,
>   chorusesOption :: String -> T

> tempoOption     = Option.Tempo     . read
> transposeOption = Option.Transpose . read

> styles :: [(String, ChordChart.T -> MidiMusic.T)]
> styles = [("jazz",     Style.jazz),
>           ("bossa",    Style.bossa),
>           ("takeFive", Style.takeFive),
>           ("rock",     Style.rock),
>           ("harmonic", Style.harmonic)]

> styleOption s =
>    maybe (Option.Error ("Unknown style '"++s++"'\n"))
>          Option.Style (lookup s styles)

> chorusesOption = Choruses . read

> usage :: String
> usage = usageInfo "\nUsage: track [OPTION...] <infile >outfile\n" options ++
>        "\nAvailable styles: " ++ concat (intersperse ", " (map fst styles)) ++ "\n\n" ++
>        "This program is free software; you can redistribute it and/or\n" ++
>        "modify it under the terms of the GNU General Public License\n" ++
>        "as published by the Free Software Foundation; either version 2\n" ++
>        "of the License, or (at your option) any later version.\n\n" ++
>        "This program is distributed in the hope that it will be useful,\n" ++
>        "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" ++
>        "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n" ++
>        "GNU General Public License for more details.\n"

> processAll :: [String] -> IO [T]
> processAll argv =
>         case (getOpt Permute options argv) of
>              (o,_,[]  ) -> return o
>              (_,_,errs) -> fail (concat errs ++ usage)

> getDefault :: (a -> Maybe b) -> [ a ] -> b -> b
> getDefault b t def = headWithDefault def (mapMaybe b t)

> getTempo :: T -> Maybe Integer
> getTempo (Option.Tempo t) = Just t
> getTempo _ = Nothing

> getTrans, getChoruses :: T -> Maybe Int
> getTrans (Option.Transpose t) = Just t
> getTrans _ = Nothing

> getChoruses (Option.Choruses c) = Just c
> getChoruses _ = Nothing

> getStyle :: T -> Maybe (ChordChart.T -> MidiMusic.T)
> getStyle (Option.Style s) = Just s
> getStyle _ = Nothing

> type Tuple = (Integer, Style.T, Int, Int)

> toTuple :: [ T ] -> Tuple
> toTuple l  = (getDefault getTempo l 120,
>               getDefault getStyle l Style.jazz,
>               getDefault getTrans l 0,
>               getDefault getChoruses l 5)

> exit :: Bool -> String -> IO a
> exit c m = do putStr (m ++ usage)
>               if c then exitWith ExitSuccess else exitWith (ExitFailure 1)

> getError :: [ T ] -> Maybe String
> getError = listToMaybe . mapMaybe errorToMaybe

> getAll :: IO Tuple
> getAll  = do opts <- (getArgs >>= processAll)
>              if any isHelp opts
>                then exit True ""
>                else maybe (return (toTuple opts)) (exit False) (getError opts)

\end{haskelllisting}
