1 %  Copyright (C) 2003 David Roundy
    2 %
    3 %  This program is free software; you can redistribute it and/or modify
    4 %  it under the terms of the GNU General Public License as published by
    5 %  the Free Software Foundation; either version 2, or (at your option)
    6 %  any later version.
    7 %
    8 %  This program is distributed in the hope that it will be useful,
    9 %  but WITHOUT ANY WARRANTY; without even the implied warranty of
   10 %  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   11 %  GNU General Public License for more details.
   12 %
   13 %  You should have received a copy of the GNU General Public License
   14 %  along with this program; see the file COPYING.  If not, write to
   15 %  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
   16 %  Boston, MA 02110-1301, USA.
   17 
   18 \begin{code}
   19 module Darcs.ArgumentDefaults ( get_default_flags ) where
   20 import Data.Maybe ( catMaybes, listToMaybe, mapMaybe )
   21 
   22 import Darcs.Arguments ( DarcsFlag,
   23                          DarcsOption( DarcsArgOption, DarcsNoArgOption, DarcsMultipleChoiceOption ),
   24                          arein, isin )
   25 import Darcs.Commands ( CommandControl( Command_data ),
   26                         command_alloptions )
   27 import Darcs.Commands.Help ( command_control_list )
   28 import Darcs.Repository.Prefs ( get_global, get_preflist )
   29 \end{code}
   30 
   31 \paragraph{defaults}\label{defaults}
   32 
   33 Default values for darcs commands can be configured on a per-repository
   34 basis by editing (and possibly creating) the \verb!_darcs/prefs/defaults!
   35 file.  Each line of this file has the following form:
   36 \begin{verbatim}
   37 COMMAND FLAG VALUE
   38 \end{verbatim}
   39 where \verb!COMMAND! is either the name of the command to which the default
   40 applies, or \verb!ALL! to indicate that the default applies to all commands
   41 accepting that flag.  The \verb!FLAG! term is the name of the long argument
   42 option without the ``\verb!--!'', i.e.\ \verb!verbose! rather than
   43 \verb!--verbose!.  Finally, the \verb!VALUE! option can be omitted if the
   44 flag is one such as \verb!verbose! that doesn't involve a value.
   45 If the value has spaces in it, use single quotes, not double quotes, to surround it. 
   46 Each line only takes one flag.  To set multiple defaults for the same
   47 command (or for \verb!ALL! commands), use multiple lines.
   48 
   49 Note that the use of \verb|ALL| easily can have unpredicted consequences,
   50 especially if commands in newer versions of darcs accepts flags that they
   51 didn't in previous versions. A command like \verb|obliterate| could be
   52 devastating with the ``wrong'' flags (for example --all). Only use safe
   53 flags with \verb|ALL|.
   54 
   55 \begin{tabular}{ll}
   56 {\tt \verb!~/.darcs/defaults!} & provides defaults for this user account, on MS Windows~\ref{ms_win} \\
   57 {\tt \verb!repo/_darcs/prefs/defaults!} & provides defaults for one project,\\
   58   & overrules changes per user \\
   59 \end{tabular}
   60 
   61 For example, if your system clock is bizarre, you could instruct darcs to
   62 always ignore the file modification times by adding the following line to
   63 your \verb!_darcs/prefs/defaults! file.  (Note that this would have to be
   64 done for each repository!)
   65 \begin{verbatim}
   66 ALL ignore-times
   67 \end{verbatim}
   68 
   69 If you never want to run a test when recording to a particular repository
   70 (but still want to do so when running
   71 \verb'check' on that repository), and like to name
   72 all your patches ``Stupid patch'', you could use the following:
   73 \begin{verbatim}
   74 record no-test
   75 record patch-name Stupid patch
   76 \end{verbatim}
   77 
   78 If you would like a command to be run every time patches are recorded
   79 in a particular repository (for example if you have one central
   80 repository, that all developers contribute to), then you can set apply
   81 to always run a command when apply is successful.  For example, if you
   82 need to make sure that the files in the repository have the correct
   83 access rights you might use the following.  There are two things
   84 to note about using darcs this way:
   85 \begin{itemize}
   86 \item Without the second line you will get errors, because the sub
   87       process that runs apply cannot prompt interactively.
   88 \item Whatever script is run by the post apply command should not be
   89       added to the repository with \verb!darcs add!; doing so would
   90       allow people to modify that file and then run arbitrary scripts on
   91       your main repository, possibly damaging or violating security.
   92 \end{itemize}
   93 \begin{verbatim}
   94 apply posthook chmod -R a+r *
   95 apply run-posthook
   96 \end{verbatim}
   97 
   98 Similarly, if you need a command to run automatically before darcs
   99 performs an action you can use a prehook.  Using prehooks it could be
  100 possible to canonicalize line endings before recording patches.
  101 
  102 There are some options which are meant specifically for use in
  103 \verb!_darcs/prefs/defaults!. One of them is \verb!--disable!. As the name
  104 suggests, this option will disable every command that got it as argument. So,
  105 if you are afraid that you could damage your repositories by inadvertent use of
  106 a command like amend-record, add the following line to
  107 \verb!_darcs/prefs/defaults!:
  108 \begin{verbatim}
  109 amend-record disable
  110 \end{verbatim}
  111 
  112 Also, a global preferences file can be created with the name
  113 \verb!.darcs/defaults! in your home directory, on MS Windows~\ref{ms_win}. 
  114 Options present there will be added to the repository-specific preferences.
  115 If they conflict with repository-specific options, the repository-specific
  116 ones will take precedence.
  117 
  118 \begin{code}
  119 get_default_flags :: String -> [DarcsOption] -> [DarcsFlag] -> IO [DarcsFlag]
  120 get_default_flags com com_opts already = do
  121     repo_defs   <- default_content $ get_preflist "defaults"
  122     global_defs <- default_content $ get_global   "defaults"
  123     let repo_flags = get_flags_from com com_opts already repo_defs
  124         global_flags = get_flags_from com com_opts
  125                                           (already++repo_flags) global_defs
  126     return $ repo_flags ++ global_flags
  127 
  128 get_flags_from :: String -> [DarcsOption] -> [DarcsFlag] -> [(String,String,String)] -> [DarcsFlag]
  129 get_flags_from com com_opts already defs =
  130     options_for com_defs com_opts com_opts ++
  131     options_for all_defs com_opts all_opts
  132     where com_defs = filter (\ (c,_,_) -> c == com) defs
  133           all_defs = filter (\ (c,_,_) -> c == "ALL") defs
  134           options_for d o ao = concatMap (find_option o ao already) d
  135           all_opts = concatMap get_opts command_control_list
  136           get_opts (Command_data c) = let (o1, o2) = command_alloptions c
  137                                       in o1 ++ o2
  138           get_opts _                = []
  139 
  140 find_option :: [DarcsOption] -> [DarcsOption] -> [DarcsFlag] -> (String,String,String) -> [DarcsFlag]
  141 find_option opts all_opts already (c, f, d) =
  142     if null $ mapMaybe choose_option all_opts
  143     then error $ "Bad default option: command '"++c++"' has no option '"++f++"'."
  144     else concat $ mapMaybe choose_option opts
  145     where choose_option (DarcsNoArgOption _ fls o _)
  146               | o `elem` already = Just []
  147               | f `elem` fls = if null d
  148                                then Just [o]
  149                                else error $ "Bad default option: '"++f
  150                                         ++"' takes no argument, but '"++d
  151                                         ++"' argument given."
  152           choose_option (DarcsArgOption _ fls o _ _)
  153               | o `isin` already = Just []
  154               | f `elem` fls = if null d
  155                                then error $ "Bad default option: '"++f
  156                                         ++"' requires an argument, but no "
  157                                         ++"argument given."
  158                                else Just [o d]
  159           choose_option (DarcsMultipleChoiceOption os)
  160               | os `arein` already = Just []
  161               | otherwise = listToMaybe $ mapMaybe choose_option os
  162           choose_option _ = Nothing
  163 
  164 default_content :: IO [String] -> IO [(String,String,String)]
  165 default_content = fmap (catMaybes . map (doline.words))
  166     where doline (c:a:r) = Just (c, drop_dashdash a, unwords r)
  167           doline _ = Nothing
  168           drop_dashdash ('-':'-':a) = a
  169           drop_dashdash a = a
  170 
  171 \end{code}
  172