1 {-# LANGUAGE NamedFieldPuns #-}
2 {-# LANGUAGE TupleSections #-}
4 -----------------------------------------------------------------------------
7 -- Module : Distribution.GetOpt
8 -- Copyright : (c) Sven Panne 2002-2005
9 -- License : BSD-style (see the file libraries/base/LICENSE)
11 -- Maintainer : libraries@haskell.org
12 -- Portability : portable
14 -- This is a fork of "System.Console.GetOpt" with the following changes:
16 -- * Treat "cabal --flag command" as "cabal command --flag" e.g.
17 -- "cabal -v configure" to mean "cabal configure -v" For flags that are
18 -- not recognised as global flags, pass them on to the sub-command. See
19 -- the difference in 'shortOpt'.
21 -- * Line wrapping in the 'usageInfo' output, plus a more compact
22 -- rendering of short options, and slightly less padding.
24 -- * Parsing of option arguments is allowed to fail.
26 -- * 'ReturnInOrder' argument order is removed.
27 module Distribution
.GetOpt
38 -- | See "System.Console.GetOpt" for examples
41 import Distribution
.Compat
.Prelude
44 -- | What to do with options following non-options
46 = -- | no option processing after first non-option
48 |
-- | freely intersperse options and non-options
51 data OptDescr a
-- description of a single options:
53 [Char] -- list of short option characters
54 [String] -- list of long option strings (without "--")
55 (ArgDescr a
) -- argument descriptor
56 String -- explanation of option for user
58 instance Functor OptDescr
where
59 fmap f
(Option a b argDescr c
) = Option a b
(fmap f argDescr
) c
61 -- | Describes whether an option takes an argument or not, and if so
62 -- how the argument is parsed to a value of type @a@.
64 -- Compared to System.Console.GetOpt, we allow for parse errors.
66 = -- | no argument expected
68 |
-- | option requires argument
69 ReqArg
(String -> Either String a
) String
70 |
-- | optional argument
71 OptArg
String (Maybe String -> Either String a
) String
73 instance Functor ArgDescr
where
74 fmap f
(NoArg a
) = NoArg
(f a
)
75 fmap f
(ReqArg g s
) = ReqArg
(fmap f
. g
) s
76 fmap f
(OptArg dv g s
) = OptArg dv
(fmap f
. g
) s
78 data OptKind a
-- kind of cmd line arg (internal use only):
80 | UnreqOpt
String -- an un-recognized option
81 | NonOpt
String -- a non-option
82 | EndOfOpts
-- end-of-options marker (i.e. "--")
83 | OptErr
String -- something went wrong...
85 data OptHelp
= OptHelp
90 -- | Return a string describing the usage of a command, derived from
91 -- the header (first argument) and the options described by the
95 -> [OptDescr a
] -- option descriptors
96 -> String -- nicely formatted description of options
97 usageInfo header optDescr
= unlines (header
: table
)
99 options
= flip map optDescr
$ \(Option sos los ad d
) ->
103 map (fmtShort ad
) sos
104 ++ map (fmtLong ad
) (take 1 los
)
109 descolWidth
= 80 - (maxOptNameWidth
+ 3)
113 OptHelp
{optNames
, optHelp
} <- options
114 let wrappedHelp
= wrapText descolWidth optHelp
115 if length optNames
>= maxOptNameWidth
118 ++ renderColumns
[] wrappedHelp
119 else renderColumns
[optNames
] wrappedHelp
121 renderColumns
:: [String] -> [String] -> [String]
122 renderColumns xs ys
= do
123 (x
, y
) <- zipDefault
"" "" xs ys
124 return $ " " ++ padTo maxOptNameWidth x
++ " " ++ y
126 padTo n x
= take n
(x
++ repeat ' ')
128 zipDefault
:: a
-> b
-> [a
] -> [b
] -> [(a
, b
)]
129 zipDefault _ _
[] [] = []
130 zipDefault _ bd
(a
: as) [] = (a
, bd
) : map (,bd
) as
131 zipDefault ad _
[] (b
: bs
) = (ad
, b
) : map (ad
,) bs
132 zipDefault ad bd
(a
: as) (b
: bs
) = (a
, b
) : zipDefault ad bd
as bs
134 -- | Pretty printing of short options.
135 -- * With required arguments can be given as:
136 -- @-w PATH or -wPATH (but not -w=PATH)@
137 -- This is dislayed as:
138 -- @-w PATH or -wPATH@
139 -- * With optional but default arguments can be given as:
140 -- @-j or -jNUM (but not -j=NUM or -j NUM)@
141 -- This is dislayed as:
143 fmtShort
:: ArgDescr a
-> Char -> String
144 fmtShort
(NoArg _
) so
= "-" ++ [so
]
145 fmtShort
(ReqArg _ ad
) so
=
146 let opt
= "-" ++ [so
]
147 in opt
++ " " ++ ad
++ " or " ++ opt
++ ad
148 fmtShort
(OptArg _ _ ad
) so
=
149 let opt
= "-" ++ [so
]
150 in opt
++ "[" ++ ad
++ "]"
152 -- | Pretty printing of long options.
153 -- * With required arguments can be given as:
154 -- @--with-compiler=PATH (but not --with-compiler PATH)@
155 -- This is dislayed as:
156 -- @--with-compiler=PATH@
157 -- * With optional but default arguments can be given as:
158 -- @--jobs or --jobs=NUM (but not --jobs NUM)@
159 -- This is dislayed as:
161 fmtLong
:: ArgDescr a
-> String -> String
162 fmtLong
(NoArg _
) lo
= "--" ++ lo
163 fmtLong
(ReqArg _ ad
) lo
=
166 fmtLong
(OptArg _ _ ad
) lo
=
168 in opt
++ "[=" ++ ad
++ "]"
170 wrapText
:: Int -> String -> [String]
171 wrapText width
= map unwords . wrap
0 [] . words
173 wrap
:: Int -> [String] -> [String] -> [[String]]
175 |
length w
+ 1 > width
=
176 wrap
(length w
) [w
] ws
177 wrap col line
(w
: ws
)
178 | col
+ length w
+ 1 > width
=
179 reverse line
: wrap
0 [] (w
: ws
)
180 wrap col line
(w
: ws
) =
181 let col
' = col
+ length w
+ 1
182 in wrap col
' (w
: line
) ws
184 wrap _ line
[] = [reverse line
]
187 -- Process the command-line, and return the list of values that matched
188 -- (and those that didn\'t). The arguments are:
190 -- * The order requirements (see 'ArgOrder')
192 -- * The option descriptions (see 'OptDescr')
194 -- * The actual command line arguments (presumably got from
195 -- 'System.Environment.getArgs').
197 -- 'getOpt' returns a triple consisting of the option arguments, a list
198 -- of non-options, and a list of error messages.
200 :: ArgOrder a
-- non-option handling
201 -> [OptDescr a
] -- option descriptors
202 -> [String] -- the command-line arguments
203 -> ([a
], [String], [String]) -- (options,non-options,error messages)
204 getOpt ordering optDescr args
= (os
, xs
, es
++ map errUnrec us
)
206 (os
, xs
, us
, es
) = getOpt
' ordering optDescr args
209 -- This is almost the same as 'getOpt', but returns a quadruple
210 -- consisting of the option arguments, a list of non-options, a list of
211 -- unrecognized options, and a list of error messages.
213 :: ArgOrder a
-- non-option handling
214 -> [OptDescr a
] -- option descriptors
215 -> [String] -- the command-line arguments
216 -> ([a
], [String], [String], [String]) -- (options,non-options,unrecognized,error messages)
217 getOpt
' _ _
[] = ([], [], [], [])
218 getOpt
' ordering optDescr
(arg
: args
) = procNextOpt opt ordering
220 procNextOpt
(Opt o
) _
= (o
: os
, xs
, us
, es
)
221 procNextOpt
(UnreqOpt u
) _
= (os
, xs
, u
: us
, es
)
222 procNextOpt
(NonOpt x
) RequireOrder
= ([], x
: rest
, [], [])
223 procNextOpt
(NonOpt x
) Permute
= (os
, x
: xs
, us
, es
)
224 procNextOpt EndOfOpts RequireOrder
= ([], rest
, [], [])
225 procNextOpt EndOfOpts Permute
= ([], rest
, [], [])
226 procNextOpt
(OptErr e
) _
= (os
, xs
, us
, e
: es
)
228 (opt
, rest
) = getNext arg args optDescr
229 (os
, xs
, us
, es
) = getOpt
' ordering optDescr rest
231 -- take a look at the next cmd line arg and decide what to do with it
232 getNext
:: String -> [String] -> [OptDescr a
] -> (OptKind a
, [String])
233 getNext
('-' : '-' : []) rest _
= (EndOfOpts
, rest
)
234 getNext
('-' : '-' : xs
) rest optDescr
= longOpt xs rest optDescr
235 getNext
('-' : x
: xs
) rest optDescr
= shortOpt x xs rest optDescr
236 getNext a rest _
= (NonOpt a
, rest
)
238 -- handle long option
239 longOpt
:: String -> [String] -> [OptDescr a
] -> (OptKind a
, [String])
240 longOpt ls rs optDescr
= long ads arg rs
242 (opt
, arg
) = break (== '=') ls
244 [ o | o
@(Option _ xs _ _
) <- optDescr
, isJust (find (p opt
) xs
)
247 options
= if null exact
then getWith
isPrefixOf else exact
248 ads
= [ad | Option _ _ ad _
<- options
]
250 fromRes
= fromParseResult optStr
252 long
(_
: _
: _
) _ rest
= (errAmbig options optStr
, rest
)
253 long
[NoArg a
] [] rest
= (Opt a
, rest
)
254 long
[NoArg _
] ('=' : _
) rest
= (errNoArg optStr
, rest
)
255 long
[ReqArg _ d
] [] [] = (errReq d optStr
, [])
256 long
[ReqArg f _
] [] (r
: rest
) = (fromRes
(f r
), rest
)
257 long
[ReqArg f _
] ('=' : xs
) rest
= (fromRes
(f xs
), rest
)
258 long
[OptArg _ f _
] [] rest
= (fromRes
(f Nothing
), rest
)
259 long
[OptArg _ f _
] ('=' : xs
) rest
= (fromRes
(f
(Just xs
)), rest
)
260 long _ _ rest
= (UnreqOpt
("--" ++ ls
), rest
)
262 -- handle short option
263 shortOpt
:: Char -> String -> [String] -> [OptDescr a
] -> (OptKind a
, [String])
264 shortOpt y ys rs optDescr
= short ads ys rs
266 options
= [o | o
@(Option ss _ _ _
) <- optDescr
, s
<- ss
, y
== s
]
267 ads
= [ad | Option _ _ ad _
<- options
]
269 fromRes
= fromParseResult optStr
271 short
(_
: _
: _
) _ rest
= (errAmbig options optStr
, rest
)
272 short
(NoArg a
: _
) [] rest
= (Opt a
, rest
)
273 short
(NoArg a
: _
) xs rest
= (Opt a
, ('-' : xs
) : rest
)
274 short
(ReqArg _ d
: _
) [] [] = (errReq d optStr
, [])
275 short
(ReqArg f _
: _
) [] (r
: rest
) = (fromRes
(f r
), rest
)
276 short
(ReqArg f _
: _
) xs rest
= (fromRes
(f xs
), rest
)
277 short
(OptArg _ f _
: _
) [] rest
= (fromRes
(f Nothing
), rest
)
278 short
(OptArg _ f _
: _
) xs rest
= (fromRes
(f
(Just xs
)), rest
)
279 short
[] [] rest
= (UnreqOpt optStr
, rest
)
280 short
[] xs rest
= (UnreqOpt
(optStr
++ xs
), rest
)
282 -- This is different vs upstream = (UnreqOpt optStr,('-':xs):rest)
283 -- Apparently this was part of the change so that flags that are
284 -- not recognised as global flags are passed on to the sub-command.
285 -- But why was no equivalent change required for longOpt? So could
286 -- this change go upstream?
288 fromParseResult
:: String -> Either String a
-> OptKind a
289 fromParseResult optStr res
= case res
of
291 Left err
-> OptErr
("invalid argument to option `" ++ optStr
++ "': " ++ err
++ "\n")
293 -- miscellaneous error formatting
295 errAmbig
:: [OptDescr a
] -> String -> OptKind b
296 errAmbig ods optStr
= OptErr
(usageInfo header ods
)
298 header
= "option `" ++ optStr
++ "' is ambiguous; could be one of:"
300 errReq
:: String -> String -> OptKind a
301 errReq d optStr
= OptErr
("option `" ++ optStr
++ "' requires an argument " ++ d
++ "\n")
303 errUnrec
:: String -> String
304 errUnrec optStr
= "unrecognized option `" ++ optStr
++ "'\n"
306 errNoArg
:: String -> OptKind a
307 errNoArg optStr
= OptErr
("option `" ++ optStr
++ "' doesn't allow an argument\n")