command_def.ml 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362
  1. (******************************************************************************)
  2. (* Copyright © Joly Clément, 2014-2015 *)
  3. (* *)
  4. (* leowzukw@vmail.me *)
  5. (* *)
  6. (* Ce logiciel est un programme informatique servant à exécuter *)
  7. (* automatiquement des programmes à l'ouverture du terminal. *)
  8. (* *)
  9. (* Ce logiciel est régi par la licence CeCILL soumise au droit français et *)
  10. (* respectant les principes de diffusion des logiciels libres. Vous pouvez *)
  11. (* utiliser, modifier et/ou redistribuer ce programme sous les conditions *)
  12. (* de la licence CeCILL telle que diffusée par le CEA, le CNRS et l'INRIA *)
  13. (* sur le site "http://www.cecill.info". *)
  14. (* *)
  15. (* En contrepartie de l'accessibilité au code source et des droits de copie, *)
  16. (* de modification et de redistribution accordés par cette licence, il n'est *)
  17. (* offert aux utilisateurs qu'une garantie limitée. Pour les mêmes raisons, *)
  18. (* seule une responsabilité restreinte pèse sur l'auteur du programme, le *)
  19. (* titulaire des droits patrimoniaux et les concédants successifs. *)
  20. (* *)
  21. (* A cet égard l'attention de l'utilisateur est attirée sur les risques *)
  22. (* associés au chargement, à l'utilisation, à la modification et/ou au *)
  23. (* développement et à la reproduction du logiciel par l'utilisateur étant *)
  24. (* donné sa spécificité de logiciel libre, qui peut le rendre complexe à *)
  25. (* manipuler et qui le réserve donc à des développeurs et des professionnels *)
  26. (* avertis possédant des connaissances informatiques approfondies. Les *)
  27. (* utilisateurs sont donc invités à charger et tester l'adéquation du *)
  28. (* logiciel à leurs besoins dans des conditions permettant d'assurer la *)
  29. (* sécurité de leurs systèmes et ou de leurs données et, plus généralement, *)
  30. (* à l'utiliser et l'exploiter dans les mêmes conditions de sécurité. *)
  31. (* *)
  32. (* Le fait que vous puissiez accéder à cet en-tête signifie que vous avez *)
  33. (* pris connaissance de la licence CeCILL, et que vous en avez accepté les *)
  34. (* termes. *)
  35. (******************************************************************************)
  36. open Core.Std;;
  37. open Command;;
  38. (* Module containing the definition of the interface of OcLaunch *)
  39. (* Type to return result of the work with common arguments *)
  40. type return_arg = {
  41. rc : Settings_t.rc_file Lazy.t;
  42. }
  43. (* A set of default arguments, usable with most of the commands *)
  44. let shared_params =
  45. let open Param in
  46. (* Way to treat common args *)
  47. return (fun verbosity assume_yes no_color rc_file_name handle_signal ->
  48. (* Set the level of verbosity *)
  49. Const.verbosity := verbosity;
  50. (* Ask question or not, see Const.ask for details *)
  51. Const.ask := Option.(
  52. merge
  53. (some_if assume_yes true)
  54. !Const.ask
  55. ~f:( || )
  56. );
  57. (* Do not use color *)
  58. Const.no_color := no_color || !Const.no_color;
  59. (* Use given rc file, preserving lazyness, since Const.rc_file is not
  60. * yet evaluated *)
  61. Const.rc_file :=
  62. Option.value_map ~f:(fun rfn -> Lazy.return rfn)
  63. ~default:!Const.rc_file rc_file_name
  64. ;
  65. (* Active signal handling *)
  66. if handle_signal then
  67. Signals.handle ();
  68. (* Debugging *)
  69. let d = Messages.debug in
  70. d (sprintf "Verbosity set to %i" !Const.verbosity);
  71. d (match !Const.ask with
  72. | None -> "Assume nothing"
  73. | Some false -> "Assume No"
  74. | Some true -> "Assume Yes");
  75. d (sprintf "Color %s" (match !Const.no_color with true -> "off" | false -> "on"));
  76. begin
  77. match Option.try_with (fun () -> Lazy.force !Const.rc_file) with
  78. | None -> d "Configuration file will fail if used";
  79. | Some rc -> d (sprintf "Configuration file is %s" rc);
  80. end;
  81. d (sprintf "Tmp file is %s" Const.tmp_file);
  82. (* Obtain data from rc_file *)
  83. d "Reading rc_file...";
  84. let rc_content = lazy (File_com.init_rc ()) in
  85. d "Read rc_file";
  86. { rc = rc_content } (* We use type for futur use *)
  87. )
  88. (* Flag to set verbosity level *)
  89. <*> flag "-v" (optional_with_default !Const.verbosity int)
  90. ~aliases:["--verbose" ; "-verbose"]
  91. ~doc:"[n] Set verbosity level. \
  92. The higher n is, the most verbose the program is."
  93. (* Flag to assume yes *)
  94. <*> flag "-y" no_arg
  95. ~aliases:["--yes" ; "-yes"]
  96. ~doc:" Assume yes, never ask anything. \
  97. Setting OC_YES environment variable to '1' is the same. \
  98. Set it to '0' to assume no. \
  99. Set it to '-1' to be asked every time."
  100. (* Flag to set colors *)
  101. <*> flag "--no-color" no_arg
  102. ~aliases:["-no-color"]
  103. ~doc:" Use this flag to disable color usage."
  104. (* Flag to use different rc file *)
  105. <*> flag "-c" (optional file)
  106. ~aliases:["--rc" ; "-rc"]
  107. ~doc:"file Read configuration from the given file and continue parsing."
  108. (* Flag to handle signals *)
  109. <*> flag "-s" no_arg
  110. ~aliases:["--signals" ; "-signals"]
  111. ~doc:" Handle signals. Warning, this is not much tested and not \
  112. implemented the best way."
  113. ;;
  114. (* basic-commands *)
  115. (* To reset tmp file *)
  116. let reset =
  117. basic
  118. ~summary:"Reinitialises launches for the command number [command] to [n]. \
  119. With both the [command] and the [n] argumennt, the command number \
  120. [command] is resetted to [n]. \
  121. With only the [n] argument, every entry in current tmp file is resetted to [n]."
  122. Spec.(
  123. empty
  124. +> shared_params
  125. +> anon ("target_number" %: int)
  126. +> anon (maybe ("command_number" %: int))
  127. )
  128. (fun { rc } num cmd () ->
  129. (* Call the right function, according to optionnal argument.
  130. * Since it's arguments passed on command line, we can not have
  131. * num = None
  132. * cmd = Some n
  133. * cmd: number of the command to be reseted
  134. * num: number to reset *)
  135. let rc = Lazy.force rc in
  136. match ( num, cmd ) with
  137. | ( num, Some cmd ) -> Tmp_file.reset_cmd ~rc num cmd
  138. | ( num, None ) -> Tmp_file.reset2num ~rc num
  139. )
  140. ;;
  141. let reset_all =
  142. basic
  143. ~summary:" Reinitialises launches for everything."
  144. Spec.(
  145. empty
  146. +> shared_params
  147. )
  148. (fun { rc } () ->
  149. Tmp_file.reset_all ()
  150. )
  151. ;;
  152. (* To list each commands with its number *)
  153. let list =
  154. basic
  155. ~summary:"Print a list of all commands with their number. Useful to launch with number. \
  156. Displays a star next to next command to launch."
  157. Spec.(
  158. empty
  159. +> shared_params
  160. +> flag "-l" (optional int)
  161. ~aliases:[ "--length" ; "-length" ; "--elength" ; "-elength" ]
  162. ~doc:" Max length of displayed entries, 0 keeps as-is"
  163. )
  164. (fun { rc } elength () ->
  165. let rc = Lazy.force rc in
  166. List_rc.run ~rc ?elength ())
  167. ;;
  168. (* To clean-up rc file *)
  169. let clean =
  170. basic
  171. ~summary:"Remove doubled entries, trailing spaces in them... \
  172. Useful after manual editing or with rc file from old version."
  173. Spec.(
  174. empty
  175. +> shared_params
  176. )
  177. (fun { rc } () ->
  178. let rc = Lazy.force rc in
  179. Clean_command.run ~rc ()
  180. )
  181. ;;
  182. (* To add a command to rc file, from stdin or directly *)
  183. let add =
  184. basic
  185. ~summary:"Add the command given on stdin to the configuration file at a \
  186. given position ([NUMBER]). If nothing is given, append it at the \
  187. end."
  188. Spec.(
  189. empty
  190. +> shared_params
  191. +> anon (maybe ("number" %: int))
  192. )
  193. (fun { rc } num_cmd () ->
  194. let rc = Lazy.force rc in
  195. Add_command.run ~rc num_cmd
  196. )
  197. ;;
  198. (* To remove a command from rc file *)
  199. let delete =
  200. basic
  201. ~summary:"Remove the [COMMAND_NUMBER]th command from configuration file. \
  202. If [COMMAND_NUMBER] is absent, remove last one."
  203. Spec.(
  204. empty
  205. +> shared_params
  206. +> anon (maybe ("command_number" %: int))
  207. )
  208. (fun { rc } num_cmd () ->
  209. let rc = Lazy.force rc in
  210. (*Tmp_file.reset ~rc reset_cmd 0)*)
  211. Remove_command.run ~rc num_cmd)
  212. ;;
  213. (* To display current state *)
  214. let state =
  215. basic
  216. ~summary:"Display current state of the program."
  217. Spec.(
  218. empty
  219. +> shared_params
  220. )
  221. (fun { rc } () ->
  222. let rc = Lazy.force rc in
  223. State.print_current ~rc ())
  224. ;;
  225. (* To edit the nth command *)
  226. let edit =
  227. basic
  228. ~summary:"Edit the [COMMAND_NUMBER]th command of the rc file in your \
  229. $EDITOR. May be used to add new entries, without argument, one new \
  230. command per line."
  231. Spec.(
  232. empty
  233. +> shared_params
  234. +> anon (maybe ("command_number" %: int))
  235. )
  236. (fun { rc } n () ->
  237. let rc = Lazy.force rc in
  238. let position = Option.value
  239. ~default:(List.length (rc.Settings_t.progs) - 1) n
  240. in
  241. Edit_command.run ~rc position)
  242. ;;
  243. (* To display informations about the licence *)
  244. let licence =
  245. basic
  246. ~summary:"Display the licence of the program"
  247. Spec.(
  248. empty
  249. +> shared_params
  250. +> flag "-header" no_arg
  251. ~doc:" Display the header associated to the licence"
  252. )
  253. (fun _ header () ->
  254. (* When cecill is false, it displays the header *)
  255. let cecill = not(header) in
  256. Licencing.print ~cecill
  257. )
  258. ;;
  259. (* Run nth command, default use *)
  260. let default =
  261. basic
  262. ~summary:"Run the [COMMAND_NUMBER]th command"
  263. Spec.(
  264. empty
  265. +> shared_params
  266. +> anon (maybe ("command_number" %: int))
  267. )
  268. (fun { rc } n () ->
  269. let rc = Lazy.force rc in
  270. Default.run ~rc n)
  271. let run ~version ~build_info () =
  272. (* Store begin time *)
  273. let start = Time.now () in
  274. (* XXX Hack to allow to run 'oclaunch 5' or 'oclaunch' as before, i.e. do not
  275. * display help for sub commands but use the program directly *)
  276. let hack_parse () =
  277. let run_default () =
  278. default
  279. |> run ~version ~build_info
  280. in
  281. match Sys.argv with
  282. | [| _ |] -> Result.Ok (run_default ()) (* Program called with nothing *)
  283. | _ -> (* Program followed by a number *)
  284. Or_error.try_with (fun () ->
  285. (* Verify the fist argument is a number, not a subcommand (string) *)
  286. ignore (Int.of_string (Sys.argv.(1)));
  287. run_default ())
  288. in
  289. (* Parsing with subcommands *)
  290. let parse_sub () =
  291. group
  292. ~summary:"OcLaunch program is published under CeCILL licence.\n\
  293. You may run the program with 'licence' command or see \
  294. http://cecill.info/licences/Licence_CeCILL_V2.1-en.html \
  295. (https://lnch.ml/cecill) for details. More here: \
  296. https://oclaunch.eu.org/floss-under-cecill (https://lnch.ml/l)."
  297. ~readme:(fun () -> File_com.welcome_msg)
  298. ~preserve_subcommand_order:()
  299. [ ("run", default) ; ("licence", licence) ; ("add", add) ; ("edit", edit)
  300. ; ("list", list) ; ("cleanup", clean) ; ("delete", delete)
  301. ; ("state", state) ; ( "reset", reset) ; ( "reset-all", reset_all) ]
  302. |> run ~version ~build_info
  303. in
  304. (* Return error code with exceptions *)
  305. let exit_code =
  306. match
  307. hack_parse ()
  308. |> (function
  309. Result.Ok () -> ()
  310. | Error _ -> parse_sub ())
  311. with
  312. | () -> `Exit 0
  313. | exception message ->
  314. "Exception: " ^ (Exn.to_string message)
  315. |> Messages.warning;
  316. `Exit 20
  317. in
  318. (* Unlock, should be done before *)
  319. Lock.(status ()
  320. |> (function
  321. Locked ->
  322. Messages.warning "Removing lockfile, should be removed before. \
  323. It's a bug!"; remove ()
  324. | Free -> ()
  325. | Error -> Messages.warning "Error with lockfile"
  326. ));
  327. (* Display total running time, pretty printing is handled by Time module *)
  328. Messages.debug Time.(diff (now ()) start
  329. |> Span.to_string_hum (* Round the value, 3 digits *)
  330. |> sprintf "Duration: %s");
  331. (* Reset display *)
  332. Messages.reset ();
  333. exit_code
  334. ;;