···11+Fmt — OCaml Format pretty-printer combinators
22+-------------------------------------------------------------------------------
33+Release %%VERSION%%
44+55+Fmt exposes combinators to devise `Format` pretty-printing functions.
66+77+Fmt depends only on the OCaml standard library. It is distributed
88+under the BSD3 license.
99+1010+Home page: http://erratique.ch/software/fmt
1111+Contact: Daniel Bünzli `<daniel.buenzl i@erratique.ch>`
1212+1313+## Installation
1414+1515+Fmt can be installed with `opam`:
1616+1717+ opam install fmt
1818+1919+If you don't use `opam` consult the [`opam`](opam) file for build
2020+instructions.
2121+2222+## Documentation
2323+2424+The documentation and API reference is automatically generated by
2525+`ocamldoc` from the interfaces. It can be consulted [online][5]
2626+and there is a generated version in the `doc` directory of the
2727+distribution.
2828+2929+[5]: http://erratique.ch/software/fmt/doc/
3030+3131+## Sample programs
3232+3333+If you installed Fmt with `opam` sample programs are located in
3434+the directory `opam config var fmt:doc`.
3535+3636+In the distribution sample programs are located in the `test`
3737+directory of the distribution. They can be built with:
3838+3939+ ocamlbuild -use-ocamlfind test/tests.otarget
4040+4141+The resulting binaries are in `_build/test`.
4242+4343+- `test.native` tests the library, nothing should fail.
+5
_tags
···11+<**/*.{ml,mli}> : bin_annot, safe_string
22+33+<src> : include
44+<src/fmt_top*> : package(compiler-libs.toplevel)
55+<test> : include
+28
build
···11+#!/bin/sh
22+# This script is only used for developement. It is removed by the
33+# distribution process.
44+55+set -e
66+77+OCAMLBUILD=${OCAMLBUILD:="ocamlbuild -tag debug -classic-display \
88+ -use-ocamlfind" }
99+1010+action ()
1111+{
1212+ case $1 in
1313+ default) action lib;;
1414+ lib) $OCAMLBUILD fmt.cma fmt.cmxa ;;
1515+ test)
1616+ action lib
1717+ $OCAMLBUILD test/tests.otarget
1818+ ;;
1919+ doc) shift; pkg-doc $* doc/dev-api.docdir ;;
2020+ api-doc) shift; pkg-doc $* doc/api.docdir ;;
2121+ clean) $OCAMLBUILD -clean ;;
2222+ *) $OCAMLBUILD $* ;;
2323+ esac
2424+}
2525+2626+if [ $# -eq 0 ];
2727+then action default ;
2828+else action $*; fi
···11+#!/usr/bin/env ocaml
22+#directory "pkg"
33+#use "config.ml"
44+55+(* This is only for git checkout builds, it can be ignored
66+ for distribution builds. *)
77+88+let () =
99+ if Dir.exists ".git" then begin
1010+ Vars.subst ~skip:Config.subst_skip ~vars:Config.vars ~dir:"."
1111+ >>& fun () -> Cmd.exec_hook Config.git_hook
1212+ >>& fun () -> ()
1313+ end
+272
pkg/topkg-ext.ml
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2014 Daniel C. Bünzli. All rights reserved.
33+ Distributed under the BSD3 license, see license at the end of the file.
44+ %%NAME%% release %%VERSION%%
55+ ---------------------------------------------------------------------------*)
66+77+let ( >>= ) v f = match v with `Ok v -> f v | `Error _ as e -> e
88+let ( >>& ) v f = match v with
99+| `Ok v -> f v | `Error e -> Printf.eprintf "%s: %s\n%!" Sys.argv.(0) e; exit 1
1010+1111+type 'a result = [ `Ok of 'a | `Error of string ]
1212+1313+(** Working with files *)
1414+module File : sig
1515+ val exists : string -> bool
1616+ (** [exists file] is [true] if [file] exists. *)
1717+1818+ val read : string -> string result
1919+ (** [read file] is [file]'s contents. *)
2020+2121+ val write : string -> string -> unit result
2222+ (** [write file content] writes [contents] to [file]. *)
2323+2424+ val write_subst : string -> (string * string) list -> string -> unit result
2525+ (** [write_subst file vars content] writes [contents] to [file]
2626+ substituting variables of the form [%%ID%%] by their definition.
2727+ The [ID]'s are [List.map fst vars] and their definition content
2828+ is found with [List.assoc]. *)
2929+3030+ val delete : ?maybe:bool -> string -> unit result
3131+ (** [delete maybe file] deletes file [file]. If [maybe] is [true] (defaults
3232+ to false) no error is reported if the file doesn't exist. *)
3333+3434+ val temp : unit -> string result
3535+ (** [temp ()] creates a temporary file and returns its name. The file
3636+ is destroyed at the end of program execution. *)
3737+end = struct
3838+ let exists = Sys.file_exists
3939+ let read file = try
4040+ let ic = open_in file in
4141+ let len = in_channel_length ic in
4242+ let s = String.create len in
4343+ really_input ic s 0 len; close_in ic; `Ok s
4444+ with Sys_error e -> `Error e
4545+4646+ let write f s = try
4747+ let oc = open_out f in
4848+ output_string oc s; close_out oc; `Ok ()
4949+ with Sys_error e -> `Error e
5050+5151+ let write_subst f vars s = try
5252+ let oc = open_out f in
5353+ let start = ref 0 in
5454+ let last = ref 0 in
5555+ let len = String.length s in
5656+ while (!last < len - 4) do
5757+ if not (s.[!last] = '%' && s.[!last + 1] = '%') then incr last else
5858+ begin
5959+ let start_subst = !last in
6060+ let last_id = ref (!last + 2) in
6161+ let stop = ref false in
6262+ while (!last_id < len - 1 && not !stop) do
6363+ if not (s.[!last_id] = '%' && s.[!last_id + 1] = '%') then begin
6464+ if s.[!last_id] <> ' ' then (incr last_id) else
6565+ (stop := true; last := !last_id)
6666+ end else begin
6767+ let id_start = start_subst + 2 in
6868+ let id = String.sub s (id_start) (!last_id - id_start) in
6969+ try
7070+ let subst = List.assoc id vars in
7171+ output oc s !start (start_subst - !start);
7272+ output_string oc subst;
7373+ stop := true;
7474+ start := !last_id + 2;
7575+ last := !last_id + 2;
7676+ with Not_found ->
7777+ stop := true;
7878+ last := !last_id
7979+ end
8080+ done
8181+ end
8282+ done;
8383+ output oc s !start (len - !start); close_out oc; `Ok ()
8484+ with Sys_error e -> `Error e
8585+8686+ let delete ?(maybe = false) file = try
8787+ if maybe && not (exists file) then `Ok () else
8888+ `Ok (Sys.remove file)
8989+ with Sys_error e -> `Error e
9090+9191+ let temp () = try
9292+ let f = Filename.temp_file (Filename.basename Sys.argv.(0)) "topkg" in
9393+ at_exit (fun () -> ignore (delete f)); `Ok f
9494+ with Sys_error e -> `Error e
9595+end
9696+9797+(** Working with directories. *)
9898+module Dir : sig
9999+ val exists : string -> bool
100100+ (** [exists dir] is [true] if directory [dir] exists. *)
101101+102102+ val change_cwd : string -> unit result
103103+ (** [change_cwd dir] changes the current working directory to [dir]. *)
104104+105105+ val fold_files_rec : ?skip:string list -> (string -> 'a -> 'a result) ->
106106+ 'a -> string list -> 'a result
107107+ (** [fold_files_rec skip f acc paths] folds [f] over the files
108108+ found in [paths]. Files and directories whose suffix matches an
109109+ element of [skip] are skipped. *)
110110+end = struct
111111+ let exists dir = Sys.file_exists dir && Sys.is_directory dir
112112+ let change_cwd dir = try `Ok (Sys.chdir dir) with Sys_error e -> `Error e
113113+ let fold_files_rec ?(skip = []) f acc paths =
114114+ let is_dir d = try Sys.is_directory d with Sys_error _ -> false in
115115+ let readdir d = try Array.to_list (Sys.readdir d) with Sys_error _ -> [] in
116116+ let keep p = not (List.exists (fun s -> Filename.check_suffix p s) skip) in
117117+ let process acc file = match acc with
118118+ | `Error _ as e -> e
119119+ | `Ok acc -> f file acc
120120+ in
121121+ let rec aux f acc = function
122122+ | (d :: ds) :: up ->
123123+ let paths = List.rev_map (Filename.concat d) (readdir d) in
124124+ let paths = List.find_all keep paths in
125125+ let dirs, files = List.partition is_dir paths in
126126+ begin match List.fold_left process acc files with
127127+ | `Error _ as e -> e
128128+ | `Ok _ as acc -> aux f acc (dirs :: ds :: up)
129129+ end
130130+ | [] :: [] -> acc
131131+ | [] :: up -> aux f acc up
132132+ | _ -> assert false
133133+ in
134134+ let paths = List.find_all keep paths in
135135+ let dirs, files = List.partition is_dir paths in
136136+ let acc = List.fold_left process (`Ok acc) files in
137137+ aux f acc (dirs :: [])
138138+end
139139+140140+(** Command invocation. *)
141141+module Cmd : sig
142142+ val exec : string -> unit result
143143+ (** [exec cmd] executes [cmd]. *)
144144+145145+ val exec_hook : string option -> unit result
146146+ (** [exec_hook args] is [exec ("ocaml " ^ "args")] if [args] is some. *)
147147+148148+ val read : string -> string result
149149+ (** [read cmd] executes [cmd] and returns the contents of its stdout. *)
150150+end = struct
151151+ let exec cmd =
152152+ let code = Sys.command cmd in
153153+ if code = 0 then `Ok () else
154154+ `Error (Printf.sprintf "invocation `%s' exited with %d" cmd code)
155155+156156+ let exec_hook args = match args with
157157+ | None -> `Ok ()
158158+ | Some args -> exec (Printf.sprintf "ocaml %s" args)
159159+160160+ let read cmd =
161161+ File.temp () >>= fun file ->
162162+ exec (Printf.sprintf "%s > %s" cmd file) >>= fun () ->
163163+ File.read file >>= fun v ->
164164+ `Ok v
165165+end
166166+167167+(** Variable substitution. *)
168168+module Vars : sig
169169+ val subst : skip:string list -> vars:(string * string) list ->
170170+ dir:string -> unit result
171171+ (** [subst skip vars dir] substitutes [vars] in all files
172172+ in [dir] except those that are [skip]ped (see {!Dir.fold_files_rec}). *)
173173+174174+ val get : string -> (string * string) list -> string result
175175+ (** [get v] lookup variable [v] in [vars]. Returns an error if [v] is
176176+ absent or if it is the empty string. *)
177177+178178+end = struct
179179+ let subst ~skip ~vars ~dir =
180180+ let subst f () =
181181+ File.read f >>= fun contents ->
182182+ File.write_subst f vars contents >>= fun () -> `Ok ()
183183+ in
184184+ Dir.fold_files_rec ~skip subst () [dir]
185185+186186+ let get v vars =
187187+ let v = try List.assoc v vars with Not_found -> "" in
188188+ if v <> "" then `Ok v else
189189+ `Error (Printf.sprintf "empty or undefined variable %s in Config.vars" v)
190190+end
191191+192192+(** Git invocations. *)
193193+module Git : sig
194194+ val describe : ?chop_v:bool -> string -> string
195195+ (** [describe chop_v branch] invokes [git describe branch]. If [chop_v]
196196+ is [true] (defaults to [false]) an initial ['v'] in the result
197197+ is chopped. *)
198198+end = struct
199199+ let describe ?(chop_v = false) branch =
200200+ if not (Dir.exists ".git") then "not-a-git-checkout" else
201201+ Cmd.read (Printf.sprintf "git describe %s" branch) >>& fun d ->
202202+ let len = String.length d in
203203+ if chop_v && len > 0 && d.[0] = 'v' then String.sub d 1 (len - 2) else
204204+ String.sub d 0 (len - 1) (* remove \n *)
205205+end
206206+207207+(** Default configuration. *)
208208+module Config_default : sig
209209+ val subst_skip : string list
210210+ (** [subst_skip] is a list of suffixes that are automatically
211211+ skipped during variable substitution. *)
212212+213213+ val vars : (string * string) list
214214+ (** [vars] is the list of variables to substitute, empty. *)
215215+216216+ val git_hook : string option
217217+ (** [git_start_hook] is an ocaml script to invoke before a git package
218218+ build, after variable substitution occured. *)
219219+220220+ val distrib_remove : string list
221221+ (** [distrib_remove] is a list of files to remove before making
222222+ the distributino tarball. *)
223223+224224+ val distrib_hook : string option
225225+ (** [distrib_hook] is an ocaml script to invoke before trying
226226+ to build the distribution. *)
227227+228228+ val www_demos : string list
229229+ (** [www_demos] is a list of build targets that represent single page
230230+ js_of_ocaml demo. *)
231231+end = struct
232232+ let subst_skip = [".git"; ".png"; ".jpeg"; ".otf"; ".ttf"; ".pdf" ]
233233+ let vars = []
234234+ let git_hook = None
235235+ let distrib_remove = [".git"; ".gitignore"; "build"]
236236+ let distrib_hook = None
237237+ let www_demos = []
238238+end
239239+240240+241241+(*---------------------------------------------------------------------------
242242+ Copyright (c) 2014 Daniel C. Bünzli.
243243+ All rights reserved.
244244+245245+ Redistribution and use in source and binary forms, with or without
246246+ modification, are permitted provided that the following conditions
247247+ are met:
248248+249249+ 1. Redistributions of source code must retain the above copyright
250250+ notice, this list of conditions and the following disclaimer.
251251+252252+ 2. Redistributions in binary form must reproduce the above
253253+ copyright notice, this list of conditions and the following
254254+ disclaimer in the documentation and/or other materials provided
255255+ with the distribution.
256256+257257+ 3. Neither the name of Daniel C. Bünzli nor the names of
258258+ contributors may be used to endorse or promote products derived
259259+ from this software without specific prior written permission.
260260+261261+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
262262+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
263263+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
264264+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
265265+ OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
266266+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
267267+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
268268+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
269269+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
270270+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
271271+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
272272+ ---------------------------------------------------------------------------*)
+316
pkg/topkg.ml
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2014 Daniel C. Bünzli. All rights reserved.
33+ Distributed under the BSD3 license, see license at the end of the file.
44+ %%NAME%% release %%VERSION%%
55+ ---------------------------------------------------------------------------*)
66+77+(* Public api *)
88+99+(** Build environment access *)
1010+module type Env = sig
1111+ val bool : string -> bool
1212+ (** [bool key] declares [key] as being a boolean key in the environment.
1313+ Specifing key=(true|false) on the command line becomes mandatory. *)
1414+1515+ val native : bool
1616+ (** [native] is [bool "native"]. *)
1717+1818+ val native_dynlink : bool
1919+ (** [native_dylink] is [bool "native-dynlink"] *)
2020+end
2121+2222+(** Exts defines sets of file extensions. *)
2323+module type Exts = sig
2424+ val interface : string list
2525+ (** [interface] is [[".mli"; ".cmi"; ".cmti"]] *)
2626+2727+ val interface_opt : string list
2828+ (** [interface_opt] is [".cmx" :: interface] *)
2929+3030+ val c_library : string list
3131+ (** [c_library] is the extension for C libraries, [".a"] for unices
3232+ and [".lib"] for win32 *)
3333+3434+ val c_dll_library : string list
3535+ (** [c_dll_library] is the extension for C dynamic libraries [".so"]
3636+ for unices and [".dll"] for win32 *)
3737+3838+ val library : string list
3939+ (** [library] is [[".cma"; ".cmxa"; ".cmxs"] @ c_library] *)
4040+4141+ val module_library : string list
4242+ (** [module_library] is [(interface_opt @ library)]. *)
4343+end
4444+4545+(** Package description. *)
4646+module type Pkg = sig
4747+ type builder = [ `OCamlbuild | `Other of string * string ]
4848+ (** The type for build tools. Either [`OCamlbuild] or an
4949+ [`Other (tool, bdir)] tool [tool] that generates its build artefacts
5050+ in [bdir]. *)
5151+5252+ type moves
5353+ (** The type for install moves. *)
5454+5555+ type field = ?cond:bool -> ?exts:string list -> ?dst:string -> string -> moves
5656+ (** The type for field install functions. A call
5757+ [field cond exts dst path] generates install moves as follows:
5858+ {ul
5959+ {- If [cond] is [false] (defaults to [true]), no move is generated.}
6060+ {- If [exts] is present, generates a move for each path in
6161+ the list [List.map (fun e -> path ^ e) exts].}
6262+ {- If [dst] is present this path is used as the move destination
6363+ (allows to install in subdirectories). If absent [dst] is
6464+ [Filename.basename path].} *)
6565+6666+ val lib : field
6767+ val bin : ?auto:bool -> field
6868+ (** If [auto] is true (defaults to false) generates
6969+ [path ^ ".native"] if {!Env.native} is [true] and
7070+ [path ^ ".byte"] if {!Env.native} is [false]. *)
7171+ val sbin : ?auto:bool -> field (** See {!bin}. *)
7272+ val libexec : ?auto:bool -> field (** See {!bin}. *)
7373+ val toplevel : field
7474+ val share : field
7575+ val share_root : field
7676+ val etc : field
7777+ val doc : field
7878+ val misc : field
7979+ val stublibs : field
8080+ val man : field
8181+ val describe : string -> builder:builder -> moves list -> unit
8282+ (** [describe name builder moves] describes a package named [name] with
8383+ builder [builder] and install moves [moves]. *)
8484+end
8585+8686+(* Implementation *)
8787+8888+module Topkg : sig
8989+ val cmd : [`Build | `Explain | `Help ]
9090+ val env : (string * bool) list
9191+ val err_parse : string -> 'a
9292+ val err_mdef : string -> 'a
9393+ val err_miss : string -> 'a
9494+ val err_file : string -> string -> 'a
9595+ val warn_unused : string -> unit
9696+end = struct
9797+9898+ (* Parses the command line. The actual cmd execution occurs in the call
9999+ to Pkg.describe. *)
100100+101101+ let err fmt =
102102+ let k _ = exit 1 in
103103+ Format.kfprintf k Format.err_formatter ("%s: " ^^ fmt ^^ "@.") Sys.argv.(0)
104104+105105+ let err_parse a = err "argument `%s' is not of the form key=(true|false)" a
106106+ let err_mdef a = err "bool `%s' is defined more than once" a
107107+ let err_miss a = err "argument `%s=(true|false)' is missing" a
108108+ let err_file f e = err "%s: %s" f e
109109+ let warn_unused k =
110110+ Format.eprintf "%s: warning: environment key `%s` unused.@." Sys.argv.(0) k
111111+112112+ let cmd, env =
113113+ let rec parse_env acc = function (* not t.r. *)
114114+ | arg :: args ->
115115+ begin try
116116+ (* String.cut ... *)
117117+ let len = String.length arg in
118118+ let eq = String.index arg '=' in
119119+ let bool = bool_of_string (String.sub arg (eq + 1) (len - eq - 1)) in
120120+ let key = String.sub arg 0 eq in
121121+ if key = "" then raise Exit else
122122+ try ignore (List.assoc key acc); err_mdef key with
123123+ | Not_found -> parse_env ((key, bool) :: acc) args
124124+ with
125125+ | Invalid_argument _ | Not_found | Exit -> err_parse arg
126126+ end
127127+ | [] -> acc
128128+ in
129129+ match List.tl (Array.to_list Sys.argv) with
130130+ | "explain" :: args -> `Explain, parse_env [] args
131131+ | ("help" | "-h" | "--help" | "-help") :: args -> `Help, parse_env [] args
132132+ | args -> `Build, parse_env [] args
133133+end
134134+135135+module Env : sig
136136+ include Env
137137+ val get : unit -> (string * bool) list
138138+end = struct
139139+ let env = ref []
140140+ let get () = !env
141141+ let add_bool key b = env := (key, b) :: !env
142142+ let bool key =
143143+ let b = try List.assoc key Topkg.env with
144144+ | Not_found -> if Topkg.cmd = `Build then Topkg.err_miss key else true
145145+ in
146146+ add_bool key b; b
147147+148148+ let native = bool "native"
149149+ let native_dynlink = bool "native-dynlink"
150150+end
151151+152152+module Exts : Exts = struct
153153+ let interface = [".mli"; ".cmi"; ".cmti"]
154154+ let interface_opt = ".cmx" :: interface
155155+ let c_library = if Sys.win32 then [".lib"] else [".a"]
156156+ let c_dll_library = if Sys.win32 then [".dll"] else [".so"]
157157+ let library = [".cma"; ".cmxa"; ".cmxs"] @ c_library
158158+ let module_library = (interface_opt @ library)
159159+end
160160+161161+module Pkg : Pkg = struct
162162+ type builder = [ `OCamlbuild | `Other of string * string ]
163163+ type moves = (string * (string * string)) list
164164+ type field = ?cond:bool -> ?exts:string list -> ?dst:string -> string -> moves
165165+166166+ let str = Printf.sprintf
167167+ let warn_unused () =
168168+ let keys = List.map fst Topkg.env in
169169+ let keys_used = List.map fst (Env.get ()) in
170170+ let unused = List.find_all (fun k -> not (List.mem k keys_used)) keys in
171171+ List.iter Topkg.warn_unused unused
172172+173173+ let has_suffix = Filename.check_suffix
174174+ let build_strings ?(exec_sep = " ") btool bdir mvs =
175175+ let no_build = [ ".cmti"; ".cmt" ] in
176176+ let install = Buffer.create 1871 in
177177+ let exec = Buffer.create 1871 in
178178+ let rec add_mvs current = function
179179+ | (field, (src, dst)) :: mvs when field = current ->
180180+ if List.exists (has_suffix src) no_build then
181181+ Buffer.add_string install (str "\n \"?%s/%s\" {\"%s\"}" bdir src dst)
182182+ else begin
183183+ Buffer.add_string exec (str "%s%s" exec_sep src);
184184+ Buffer.add_string install (str "\n \"%s/%s\" {\"%s\"}" bdir src dst);
185185+ end;
186186+ add_mvs current mvs
187187+ | (((field, _) :: _) as mvs) ->
188188+ if current <> "" (* first *) then Buffer.add_string install " ]\n";
189189+ Buffer.add_string install (str "%s: [" field);
190190+ add_mvs field mvs
191191+ | [] -> ()
192192+ in
193193+ Buffer.add_string exec btool;
194194+ add_mvs "" mvs;
195195+ Buffer.add_string install " ]\n";
196196+ Buffer.contents install, Buffer.contents exec
197197+198198+ let pr = Format.printf
199199+ let pr_explanation btool bdir pkg mvs =
200200+ let env = Env.get () in
201201+ let install, exec = build_strings ~exec_sep:" \\\n " btool bdir mvs in
202202+ pr "@[<v>";
203203+ pr "Package name: %s@," pkg;
204204+ pr "Build tool: %s@," btool;
205205+ pr "Build directory: %s@," bdir;
206206+ pr "Environment:@, ";
207207+ List.iter (fun (k,v) -> pr "%s=%b@, " k v) (List.sort compare env);
208208+ pr "@,Build invocation:@,";
209209+ pr " %s@,@," exec;
210210+ pr "Install file:@,";
211211+ pr "%s@," install;
212212+ pr "@]";
213213+ ()
214214+215215+ let pr_help () =
216216+ pr "Usage example:@\n %s" Sys.argv.(0);
217217+ List.iter (fun (k,v) -> pr " %s=%b" k v) (List.sort compare (Env.get ()));
218218+ pr "@."
219219+220220+ let build btool bdir pkg mvs =
221221+ let install, exec = build_strings btool bdir mvs in
222222+ let e = Sys.command exec in
223223+ if e <> 0 then exit e else
224224+ let install_file = pkg ^ ".install" in
225225+ try
226226+ let oc = open_out install_file in
227227+ output_string oc install; flush oc; close_out oc
228228+ with Sys_error e -> Topkg.err_file install_file e
229229+230230+ let mvs ?(drop_exts = []) field ?(cond = true) ?(exts = []) ?dst src =
231231+ if not cond then [] else
232232+ let mv src dst = (field, (src, dst)) in
233233+ let expand exts s d = List.map (fun e -> mv (s ^ e) (d ^ e)) exts in
234234+ let dst = match dst with None -> Filename.basename src | Some dst -> dst in
235235+ let files = if exts = [] then [mv src dst] else expand exts src dst in
236236+ let keep (_, (src, _)) = not (List.exists (has_suffix src) drop_exts) in
237237+ List.find_all keep files
238238+239239+ let lib =
240240+ let drop_exts =
241241+ if Env.native && not Env.native_dynlink then [ ".cmxs" ] else
242242+ if not Env.native then Exts.c_library @ [".cmx"; ".cmxa"; ".cmxs" ]
243243+ else []
244244+ in
245245+ mvs ~drop_exts "lib"
246246+247247+ let share = mvs "share"
248248+ let share_root = mvs "share_root"
249249+ let etc = mvs "etc"
250250+ let toplevel = mvs "toplevel"
251251+ let doc = mvs "doc"
252252+ let misc = mvs "misc"
253253+ let stublibs = mvs "stublibs"
254254+ let man = mvs "man"
255255+256256+ let bin_drops = if not Env.native then [ ".native" ] else []
257257+ let bin_mvs field ?(auto = false) ?cond ?exts ?dst src =
258258+ let src, dst =
259259+ if not auto then src, dst else
260260+ let dst = match dst with
261261+ | None -> Some (Filename.basename src)
262262+ | Some _ as dst -> dst
263263+ in
264264+ let src = if Env.native then src ^ ".native" else src ^ ".byte" in
265265+ src, dst
266266+ in
267267+ mvs ~drop_exts:bin_drops field ?cond ?dst src
268268+269269+ let bin = bin_mvs "bin"
270270+ let sbin = bin_mvs "sbin"
271271+ let libexec = bin_mvs "libexec"
272272+273273+ let describe pkg ~builder mvs =
274274+ let mvs = List.sort compare (List.flatten mvs) in
275275+ let btool, bdir = match builder with
276276+ | `OCamlbuild -> "ocamlbuild -use-ocamlfind -classic-display", "_build"
277277+ | `Other (btool, bdir) -> btool, bdir
278278+ in
279279+ match Topkg.cmd with
280280+ | `Explain -> pr_explanation btool bdir pkg mvs
281281+ | `Help -> pr_help ()
282282+ | `Build -> warn_unused (); build btool bdir pkg mvs
283283+end
284284+285285+(*---------------------------------------------------------------------------
286286+ Copyright (c) 2014 Daniel C. Bünzli.
287287+ All rights reserved.
288288+289289+ Redistribution and use in source and binary forms, with or without
290290+ modification, are permitted provided that the following conditions
291291+ are met:
292292+293293+ 1. Redistributions of source code must retain the above copyright
294294+ notice, this list of conditions and the following disclaimer.
295295+296296+ 2. Redistributions in binary form must reproduce the above
297297+ copyright notice, this list of conditions and the following
298298+ disclaimer in the documentation and/or other materials provided
299299+ with the distribution.
300300+301301+ 3. Neither the name of Daniel C. Bünzli nor the names of
302302+ contributors may be used to endorse or promote products derived
303303+ from this software without specific prior written permission.
304304+305305+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
306306+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
307307+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
308308+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
309309+ OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
310310+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
311311+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
312312+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
313313+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
314314+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
315315+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
316316+ ---------------------------------------------------------------------------*)
+212
src/fmt.ml
···11+(*---------------------------------------------------------------------------
22+ Copyright 2014 Daniel C. Bünzli. All rights reserved.
33+ Distributed under the BSD3 license, see license at the end of the file.
44+ %%NAME%% release %%VERSION%%
55+ ---------------------------------------------------------------------------*)
66+77+(* Formatters *)
88+99+type 'a t = Format.formatter -> 'a -> unit
1010+1111+let pp ppf fmt = Format.fprintf ppf fmt
1212+let kpp ppf fmt = Format.kfprintf ppf fmt
1313+let rpp fmt ppf = Format.fprintf ppf fmt
1414+1515+let nop fmt ppf = ()
1616+let cut = Format.pp_print_cut
1717+let sp = Format.pp_print_space
1818+let const pp_v v ppf () = pp ppf "%a" pp_v v
1919+2020+(* OCaml base type formatters *)
2121+2222+let bool = Format.pp_print_bool
2323+let int = Format.pp_print_int
2424+let int32 ppf v = pp ppf "%ld" v
2525+let int64 ppf v = pp ppf "%Ld" v
2626+let uint32 ppf v = pp ppf "%lu" v
2727+let uint64 ppf v = pp ppf "%Lu" v
2828+let uint ppf v = pp ppf "%u" v
2929+3030+let string = Format.pp_print_string
3131+let const_string s ppf () = pp ppf "%s" s
3232+3333+(* Floats *)
3434+3535+let float ppf v = pp ppf "%g" v
3636+3737+let round x = floor (x +. 0.5)
3838+let round_dfrac d x =
3939+ if x -. (round x) = 0. then x else (* x is an integer. *)
4040+ let m = 10. ** (float_of_int d) in (* m moves 10^-d to 1. *)
4141+ (floor ((x *. m) +. 0.5)) /. m
4242+4343+let round_dsig d x =
4444+ if x = 0. then 0. else
4545+ let m = 10. ** (floor (log10 (abs_float x))) in (* to normalize x. *)
4646+ (round_dfrac d (x /. m)) *. m
4747+4848+let float_dfrac d ppf f = pp ppf "%g" (round_dfrac d f)
4949+let float_dsig d ppf f = pp ppf "%g" (round_dsig d f)
5050+5151+(* OCaml container formatters *)
5252+5353+let none ppf () = pp ppf "None"
5454+let some pp_v ppf v = pp ppf "@[<1>Some@ %a@]" pp_v v
5555+let option ?(pp_none = fun ppf () -> ()) pp_v ppf = function
5656+| None -> pp_none ppf ()
5757+| Some v -> pp_v ppf v
5858+5959+let rec list ?(pp_sep = cut) pp_v ppf = function
6060+| [] -> ()
6161+| v :: vs ->
6262+ pp_v ppf v; if vs <> [] then (pp_sep ppf (); list ~pp_sep pp_v ppf vs)
6363+6464+(* Brackets *)
6565+6666+let parens pp_v ppf v = pp ppf "@[<1>(%a)@]" pp_v v
6767+let brackets pp_v ppf v = pp ppf "@[<1>[%a]@]" pp_v v
6868+let braces pp_v ppf v = pp ppf "@[<1>{%a}@]" pp_v v
6969+7070+(* Text and lines *)
7171+7272+let white_str ~spaces ppf s =
7373+ let left = ref 0 and right = ref 0 and len = String.length s in
7474+ let flush () =
7575+ Format.pp_print_string ppf (String.sub s !left (!right - !left));
7676+ incr right; left := !right;
7777+ in
7878+ while (!right <> len) do
7979+ if s.[!right] = '\n' then (flush (); Format.pp_force_newline ppf ()) else
8080+ if spaces && s.[!right] = ' ' then (flush (); Format.pp_print_space ppf ())
8181+ else incr right;
8282+ done;
8383+ if !left <> len then flush ()
8484+8585+let text = white_str ~spaces:true
8686+let lines = white_str ~spaces:false
8787+let text_range ppf ((l0, c0), (l1, c1)) = pp ppf "%d.%d-%d.%d" l0 c0 l1 c1
8888+8989+let doomed ppf reason =
9090+ pp ppf "Something@ unreasonable@ is@ going@ on (%a).@ You@ are@ doomed."
9191+ text reason
9292+9393+(* Byte sizes *)
9494+9595+let _pp_byte_size k i ppf s =
9696+ let pp_frac = float_dfrac 1 in
9797+ let div_round_up m n = (m + n - 1) / n in
9898+ let float = float_of_int in
9999+ if s < k then pp ppf "%dB" s else
100100+ let m = k * k in
101101+ if s < m then begin
102102+ let kstr = if i = "" then "k" (* SI *) else "K" (* IEC *) in
103103+ let sk = s / k in
104104+ if sk < 10
105105+ then pp ppf "%a%s%sB" pp_frac (float s /. float k) kstr i
106106+ else pp ppf "%d%s%sB" (div_round_up s k) kstr i
107107+ end else
108108+ let g = k * m in
109109+ if s < g then begin
110110+ let sm = s / m in
111111+ if sm < 10
112112+ then pp ppf "%aM%sB" pp_frac (float s /. float m) i
113113+ else pp ppf "%dM%sB" (div_round_up s m) i
114114+ end else
115115+ let t = k * g in
116116+ if s < t then begin
117117+ let sg = s / g in
118118+ if sg < 10
119119+ then pp ppf "%aG%sB" pp_frac (float s /. float g) i
120120+ else pp ppf "%dG%sB" (div_round_up s g) i
121121+ end else
122122+ let p = k * t in
123123+ if s < p then begin
124124+ let st = s / t in
125125+ if st < 10
126126+ then pp ppf "%aT%sB" pp_frac (float s /. float t) i
127127+ else pp ppf "%dT%sB" (div_round_up s t) i
128128+ end else begin
129129+ let sp = s / p in
130130+ if sp < 10
131131+ then pp ppf "%aP%sB" pp_frac (float s /. float p) i
132132+ else pp ppf "%dP%sB" (div_round_up s p) i
133133+ end
134134+135135+let byte_size ppf s = _pp_byte_size 1000 "" ppf s
136136+let bi_byte_size ppf s = _pp_byte_size 1024 "i" ppf s
137137+138138+(* Conditional UTF-8 formatting *)
139139+140140+let utf_8_enabled, set_utf_8_enabled =
141141+ let enabled = ref false in
142142+ (fun () -> !enabled), (fun b -> enabled := b)
143143+144144+let if_utf_8 pp_u pp ppf v = (if utf_8_enabled () then pp_u else pp) ppf v
145145+146146+(* Styled formatting *)
147147+148148+type style_tags = [ `Ansi | `None ]
149149+type style =
150150+ [ `Bold | `Underline | `Black | `Red | `Green | `Yellow | `Blue | `Magenta
151151+ | `Cyan | `White | `None ]
152152+153153+let (style_tags : unit -> style_tags), (set_style_tags : style_tags -> unit) =
154154+ let style_tags = ref `None in
155155+ (fun () -> !style_tags), (fun s -> style_tags := s)
156156+157157+let ansi_style_code = function
158158+| `Bold -> "\027[01m"
159159+| `Underline -> "\027[04m"
160160+| `Black -> "\027[30m"
161161+| `Red -> "\027[31m"
162162+| `Green -> "\027[32m"
163163+| `Yellow -> "\027[33m"
164164+| `Blue -> "\027[1;34m"
165165+| `Magenta -> "\027[35m"
166166+| `Cyan -> "\027[36m"
167167+| `White -> "\027[37m"
168168+| `None -> "\027[m"
169169+170170+let ansi_style_reset = "\027[m"
171171+172172+let styled style pp_v ppf = match style_tags () with
173173+| `None -> pp_v ppf
174174+| `Ansi ->
175175+ Format.kfprintf
176176+ (fun ppf -> pp ppf "@<0>%s" ansi_style_reset) ppf "@<0>%s%a"
177177+ (ansi_style_code style) pp_v
178178+179179+let styled_string style = styled style string
180180+181181+(*---------------------------------------------------------------------------
182182+ Copyright 2014 Daniel C. Bünzli.
183183+ All rights reserved.
184184+185185+ Redistribution and use in source and binary forms, with or without
186186+ modification, are permitted provided that the following conditions
187187+ are met:
188188+189189+ 1. Redistributions of source code must retain the above copyright
190190+ notice, this list of conditions and the following disclaimer.
191191+192192+ 2. Redistributions in binary form must reproduce the above
193193+ copyright notice, this list of conditions and the following
194194+ disclaimer in the documentation and/or other materials provided
195195+ with the distribution.
196196+197197+ 3. Neither the name of Daniel C. Bünzli nor the names of
198198+ contributors may be used to endorse or promote products derived
199199+ from this software without specific prior written permission.
200200+201201+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
202202+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
203203+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
204204+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
205205+ OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
206206+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
207207+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
208208+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
209209+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
210210+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
211211+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
212212+ ---------------------------------------------------------------------------*)
+235
src/fmt.mli
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2014 Daniel C. Bünzli. All rights reserved.
33+ Distributed under the BSD3 license, see license at the end of the file.
44+ %%NAME%% release %%VERSION%%
55+ ---------------------------------------------------------------------------*)
66+77+(** {!Format} pretty-printer combinators. *)
88+99+(** {1 Formatters} *)
1010+1111+type 'a t = Format.formatter -> 'a -> unit
1212+(** The type for formatters of values of type ['a]. *)
1313+1414+val pp : Format.formatter ->
1515+ ('a, Format.formatter, unit) Pervasives.format -> 'a
1616+(** [pp] is {!Format.fprintf}. *)
1717+1818+val kpp : (Format.formatter -> 'a) -> Format.formatter ->
1919+ ('b, Format.formatter, unit, 'a) format4 -> 'b
2020+(** [kpp] is {!Format.kfprintf}. *)
2121+2222+val rpp : ('a, Format.formatter, unit) Pervasives.format ->
2323+ Format.formatter -> 'a
2424+(** [rpp] is [pp fmt ppf] *)
2525+2626+val nop : 'a t
2727+(** [nop] formats nothing. *)
2828+2929+val cut : unit t
3030+(** [cut] is {!Format.pp_print_cut}. *)
3131+3232+val sp : unit t
3333+(** [sp] is {!Format.pp_print_space}. *)
3434+3535+val const : 'a t -> 'a -> unit t
3636+(** [const pp_v v] always formats [v] using [pp_v]. *)
3737+3838+val doomed : string t
3939+(** [doomed] should be used for printing a message when reasonable
4040+ assumptions are being violated. The string should be a short
4141+ description of what is going on. *)
4242+4343+(** {1:basetypes OCaml base type formatters} *)
4444+4545+val bool : bool t
4646+(** [bool] is {!Format.pp_print_bool}. *)
4747+4848+val int : int t
4949+(** [int] is {!Format.pp_print_int}. *)
5050+5151+val int32 : int32 t
5252+(** [int32 ppf] is [pp ppf "%ld"]. *)
5353+5454+val int64 : int64 t
5555+(** [int64 ppf] is [pp ppf "%Ld"]. *)
5656+5757+val uint32 : int32 t
5858+(** [int32 ppf] is [pp ppf "%lu"]. *)
5959+6060+val uint64 : int64 t
6161+(** [uint64 ppf] is [pp ppf "%Lu"]. *)
6262+6363+val uint : int t
6464+(** [uint ppf] is [pp ppf "%u"]. *)
6565+6666+val float : float t
6767+(** [float ppf] is [pp ppf "%g".] *)
6868+6969+val float_dfrac : int -> float t
7070+(** [float_dfrac d] rounds the float to the [d]th {e decimal}
7171+ fractional digit and formats the result with ["%g"]. Ties are
7272+ rounded towards positive infinity. The result is only defined
7373+ for [0 <= d <= 16]. *)
7474+7575+val float_dsig : int -> float t
7676+(** [pp_float_dsig d] rounds the normalized {e decimal} significand
7777+ of the float to the [d]th decimal fractional digit and formats
7878+ the result with ["%g"]. Ties are rounded towards positive
7979+ infinity. The result is NaN on infinities and only defined for
8080+ [0 <= d <= 16].
8181+8282+ {b Warning.} The current implementation overflows on large [d]
8383+ and floats. *)
8484+8585+val string : string t
8686+(** [string] is {!Format.pp_print_string}. *)
8787+8888+val const_string : string -> unit t
8989+(** [const_string s] is [const string s]. *)
9090+9191+(** {1:conts OCaml container formatters} *)
9292+9393+val none : unit t
9494+(** [none ppf] is [pp ppf "None"]. *)
9595+9696+val some : 'a t -> 'a t
9797+(** [some pp_v ppf] is [pp ppf "@[<1>Some@ %a@]" pp_v]. *)
9898+9999+val option : ?pp_none:unit t -> 'a t -> 'a option t
100100+(** [option pp_none pp_v] formats value of type ['a option] using
101101+ [pp_v] and [pp_none] defaults to {!nop}. *)
102102+103103+val list : ?pp_sep:unit t -> 'a t -> 'a list t
104104+(** [pp_list pp_sep pp_v] formats lists of type ['a]. Each value is
105105+ printed with [pp_v], and values are separated by [pp_sep]
106106+ (defaults to {!cut}). {!nop} on empty lists. *)
107107+108108+(** {1:bracks Brackets} *)
109109+110110+val parens : 'a t -> 'a t
111111+(** [parens pp_v ppf] is [pp "@[<1>(%a)@]" pp_v]. *)
112112+113113+val brackets : 'a t -> 'a t
114114+(** [brackets pp_v ppf] is [pp "@[<1>[%a]@]" pp_v]. *)
115115+116116+val braces : 'a t -> 'a t
117117+(** [brackets pp_v ppf] is [pp "@[<1>{%a}@]" pp_v]. *)
118118+119119+(** {1:text Text and lines} *)
120120+121121+val text : string t
122122+ (** [pp_text] formats text by replacing spaces and newlines in the string
123123+ with calls to {!Format.pp_print_space} and {!Format.pp_force_newline}. *)
124124+125125+val lines : string t
126126+(** [pp_lines] formats lines by replacing newlines in the string
127127+ with calls to {!Format.pp_force_newline}. *)
128128+129129+val text_range : ((int * int) * (int * int)) t
130130+(** [text_range] formats a line-column text range according to
131131+ {{:http://www.gnu.org/prep/standards/standards.html#Errors}
132132+ GNU conventions}. *)
133133+134134+(** {1 Byte sizes} *)
135135+136136+val byte_size : int t
137137+(** [pp_byte_size] formats a byte size according to its magnitude
138138+ using {{:http://www.bipm.org/en/publications/si-brochure/chapter3.html}
139139+ SI prefixes} up to peta bytes (10{^15}). *)
140140+141141+val bi_byte_size : int t
142142+(** [pp_bi_byte_size] formats a byte size according to its magnitude
143143+ using {{:https://en.wikipedia.org/wiki/Binary_prefix}binary prefixes}
144144+ up to pebi bytes (2{^15}). *)
145145+146146+(** {1:utf8_cond Conditional UTF-8 formatting}
147147+148148+ {b Note.} Since {!Format} is not UTF-8 aware using UTF-8 output
149149+ may derail the pretty printing process. Use the pretty-printers
150150+ from {!Uuseg_string} if you are serious about UTF-8 formatting. *)
151151+152152+val if_utf_8 : 'a t -> 'a t -> 'a t
153153+(** [if_utf_8 pp_u pp] is a t that will use [pp_u] if UTF-8
154154+ output is {{!utf_8_enabled}enabled} and [pp] otherwise. *)
155155+156156+(** {2:utf8_cond Conditional UTF-8 formatting control} *)
157157+158158+val utf_8_enabled : unit -> bool
159159+(** [utf_8_enabled ()] is [true] if UTF-8 pretty-printing is enabled. *)
160160+161161+val set_utf_8_enabled : bool -> unit
162162+(** [set_utf_8_enabled b] sets UTF-8 pretty-printing to [b]. *)
163163+164164+(** {1:styled Styled formatting} *)
165165+166166+type style =
167167+ [ `Bold
168168+ | `Underline
169169+ | `Black
170170+ | `Red
171171+ | `Green
172172+ | `Yellow
173173+ | `Blue
174174+ | `Magenta
175175+ | `Cyan
176176+ | `White
177177+ | `None ]
178178+(** The type for styles. *)
179179+180180+val styled : style -> 'a t -> 'a t
181181+(** [styled style pp] formats according to [pp] but styled with [style]. *)
182182+183183+val styled_string : style -> string t
184184+(** [styled_string style] is [pp_styled style string]. *)
185185+186186+(** {2 Styled formatting control} *)
187187+188188+type style_tags = [ `Ansi | `None ]
189189+(** The type for style tags.
190190+ {ul
191191+ {- [`Ansi], tags the text with
192192+ {{:http://www.ecma-international.org/publications/standards/Ecma-048.htm}
193193+ ANSI escape sequences}.}
194194+ {- [`None], text remains untagged.}} *)
195195+196196+val style_tags : unit -> style_tags
197197+(** [style_tags ()] is the current tag style used by {!Fmt.pp_styled}.
198198+ Initial value is [`None]. *)
199199+200200+val set_style_tags : style_tags -> unit
201201+(** [set_style_tags s] sets the current tag style used by
202202+ {!Fmt.pp_styled}. *)
203203+204204+(*---------------------------------------------------------------------------
205205+ Copyright (c) 2014 Daniel C. Bünzli.
206206+ All rights reserved.
207207+208208+ Redistribution and use in source and binary forms, with or without
209209+ modification, are permitted provided that the following conditions
210210+ are met:
211211+212212+ 1. Redistributions of source code must retain the above copyright
213213+ notice, this list of conditions and the following disclaimer.
214214+215215+ 2. Redistributions in binary form must reproduce the above
216216+ copyright notice, this list of conditions and the following
217217+ disclaimer in the documentation and/or other materials provided
218218+ with the distribution.
219219+220220+ 3. Neither the name of Daniel C. Bünzli nor the names of
221221+ contributors may be used to endorse or promote products derived
222222+ from this software without specific prior written permission.
223223+224224+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
225225+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
226226+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
227227+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
228228+ OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
229229+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
230230+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
231231+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
232232+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
233233+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
234234+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
235235+ ---------------------------------------------------------------------------*)