mte/unikernel/duniverse/dune_/vendor/opam-file-format/opamPrinter.ml
2025-11-11 02:07:51 +01:00

739 lines
25 KiB
OCaml

(**************************************************************************)
(* *)
(* Copyright 2012-2020 OCamlPro *)
(* Copyright 2012 INRIA *)
(* *)
(* All rights reserved. This file is distributed under the terms of the *)
(* GNU Lesser General Public License version 2.1, with the special *)
(* exception on linking described in the file LICENSE. *)
(* *)
(**************************************************************************)
open OpamParserTypes
(* The code duplication with OpamBaseParser is irritating, but can't be solved
without introducing another module. *)
let nopatch v =
let s =
try
let i = String.index v '.' in
let i = String.index_from v (i+1) '.' in
(String.sub v 0 i)
with Not_found ->
let rec f i =
if i >= String.length v then v
else match String.get v i with
| '0'..'9' | '.' -> f (i+1)
| _ -> String.sub v 0 i
in
f 0
in
try Scanf.sscanf s "%u.%u%!" (fun maj min -> (maj, min))
with Scanf.Scan_failure _
| Failure _
| End_of_file ->
try Scanf.sscanf s "%u%!" (fun maj -> (maj, 0))
with Scanf.Scan_failure _
| Failure _
| End_of_file -> (0, 0)
let valid_opamfile_contents = function
| (Variable (_, "opam-version", String (_, ver)))::items
when nopatch ver >= (2, 1) ->
let opam_version_field = function
| Variable (_, "opam-version", _) -> true
| _ -> false
in
not (List.exists opam_version_field items)
| _::items ->
let greater_2_0_opam_version_field = function
| Variable (_, "opam-version", String (_, ver))
when nopatch ver >= (2, 1) -> true
| _ -> false
in
not (List.exists greater_2_0_opam_version_field items)
| [] -> true
let relop = function
| `Eq -> "="
| `Neq -> "!="
| `Geq -> ">="
| `Gt -> ">"
| `Leq -> "<="
| `Lt -> "<"
| `Sem -> "~"
let logop = function
| `And -> "&"
| `Or -> "|"
let pfxop = function
| `Not -> "!"
| `Defined -> "?"
let env_update_op = function
| Eq -> "="
| PlusEq -> "+="
| EqPlus -> "=+"
| EqPlusEq -> "=+="
| ColonEq -> ":="
| EqColon -> "=:"
let escape_string ?(triple=false) s =
let len = String.length s in
let buf = Buffer.create (len * 2) in
for i = 0 to len -1 do
let c = s.[i] in
(match c with
| '"'
when not triple
|| (i < len - 2 && s.[i+1] = '"' && s.[i+2] = '"')
|| i = len - 1 ->
Buffer.add_char buf '\\'
| '\\' -> Buffer.add_char buf '\\'
| _ -> ());
Buffer.add_char buf c
done;
Buffer.contents buf
let rec format_value fmt = function
| Relop (_,op,l,r) ->
Format.fprintf fmt "@[<h>%a %s@ %a@]"
format_value l (relop op) format_value r
| Logop (_,op,l,r) ->
Format.fprintf fmt "@[<hv>%a %s@ %a@]"
format_value l (logop op) format_value r
| Pfxop (_,op,r) ->
Format.fprintf fmt "@[<h>%s%a@]" (pfxop op) format_value r
| Prefix_relop (_,op,r) ->
Format.fprintf fmt "@[<h>%s@ %a@]"
(relop op) format_value r
| Ident (_,s) -> Format.fprintf fmt "%s" s
| Int (_,i) -> Format.fprintf fmt "%d" i
| Bool (_,b) -> Format.fprintf fmt "%b" b
| String (_,s) ->
if String.contains s '\n'
then Format.fprintf fmt "\"\"\"%s%s\"\"\""
(if s.[0] = '\n' then "" else "\\\n")
(escape_string ~triple:true s)
else Format.fprintf fmt "\"%s\"" (escape_string s)
| List (_, l) ->
Format.fprintf fmt "@[<hv>[@;<0 2>@[<hv>%a@]@,]@]" format_values l
| Group (_,g) -> Format.fprintf fmt "@[<hv>(%a)@]" format_values g
| Option(_,v,l) -> Format.fprintf fmt "@[<hov 2>%a@ {@[<hv>%a@]}@]"
format_value v format_values l
| Env_binding (_,id,op,v) ->
Format.fprintf fmt "@[<h>%a %s@ %a@]"
format_value id (env_update_op op) format_value v
and format_values fmt = function
| [] -> ()
| [v] -> format_value fmt v
| v::r ->
format_value fmt v;
Format.pp_print_space fmt ();
format_values fmt r
let value v =
format_value Format.str_formatter v; Format.flush_str_formatter ()
let value_list vl =
Format.fprintf Format.str_formatter "@[<hv>%a@]" format_values vl;
Format.flush_str_formatter ()
let rec format_item fmt = function
| Variable (_, _, List (_,[])) -> ()
| Variable (_, _, List (_,[List(_,[])])) -> ()
| Variable (_, i, List (_,l)) ->
if List.exists
(function List _ | Option (_,_,_::_) -> true | _ -> false)
l
then Format.fprintf fmt "@[<v>%s: [@;<0 2>@[<v>%a@]@,]@]"
i format_values l
else Format.fprintf fmt "@[<hv>%s: [@;<0 2>@[<hv>%a@]@,]@]"
i format_values l
| Variable (_, i, (String (_,s) as v)) when String.contains s '\n' ->
Format.fprintf fmt "@[<hov 0>%s: %a@]" i format_value v
| Variable (_, i, v) ->
Format.fprintf fmt "@[<hov 2>%s:@ %a@]" i format_value v
| Section (_,s) ->
Format.fprintf fmt "@[<v 0>%s %s{@;<0 2>@[<v>%a@]@,}@]"
s.section_kind
(match s.section_name with
| Some s -> Printf.sprintf "\"%s\" " (escape_string s)
| None -> "")
format_items s.section_items
and format_items fmt is =
Format.pp_open_vbox fmt 0;
(match is with
| [] -> ()
| i::r ->
format_item fmt i;
List.iter (fun i -> Format.pp_print_cut fmt (); format_item fmt i) r);
Format.pp_close_box fmt ()
let format_opamfile fmt f =
format_items fmt f.file_contents;
Format.pp_print_newline fmt ()
let items l =
if not (valid_opamfile_contents l) then
invalid_arg "OpamPrinter.items";
format_items Format.str_formatter l; Format.flush_str_formatter ()
let opamfile f =
items f.file_contents
let rec value_equals v1 v2 = match v1, v2 with
| Bool (_, b1), Bool (_, b2) -> b1 = b2
| Int (_, i1), Int (_, i2) -> i1 = i2
| String (_, s1), String (_, s2) -> s1 = s2
| Relop (_, r1, va1, vb1), Relop (_, r2, va2, vb2) ->
r1 = r2 && value_equals va1 va2 && value_equals vb1 vb2
| Prefix_relop (_, r1, v1), Prefix_relop (_, r2, v2) ->
r1 = r2 && value_equals v1 v2
| Logop (_, l1, va1, vb1), Logop (_, l2, va2, vb2) ->
l1 = l2 && value_equals va1 va2 && value_equals vb1 vb2
| Pfxop (_, p1, v1), Pfxop (_, p2, v2) ->
p1 = p2 && value_equals v1 v2
| Ident (_, s1), Ident (_, s2) ->
s1 = s2
| List (_, vl1), List (_, vl2) ->
(try List.for_all2 value_equals vl1 vl2 with Invalid_argument _ -> false)
| Group (_, vl1), Group (_, vl2) ->
(try List.for_all2 value_equals vl1 vl2 with Invalid_argument _ -> false)
| Option (_, v1, vl1), Option (_, v2, vl2) ->
value_equals v1 v2 &&
(try List.for_all2 value_equals vl1 vl2 with Invalid_argument _ -> false)
| Env_binding (_, v1, op1, vx1), Env_binding (_, v2, op2, vx2) ->
op1 = op2 && value_equals v1 v2 && value_equals vx1 vx2
| _ -> false
let rec opamfile_item_equals i1 i2 = match i1, i2 with
| Variable (_, n1, v1), Variable (_, n2, v2) ->
n1 = n2 && value_equals v1 v2
| Section (_, s1), Section (_, s2) ->
s1.section_kind = s2.section_kind &&
s1.section_name = s2.section_name &&
(try List.for_all2 opamfile_item_equals s1.section_items s2.section_items
with Invalid_argument _ -> false)
| _ -> false
module Normalise = struct
let escape_string s =
let len = String.length s in
let buf = Buffer.create (len * 2) in
Buffer.add_char buf '"';
for i = 0 to len -1 do
match s.[i] with
| '\\' | '"' as c -> Buffer.add_char buf '\\'; Buffer.add_char buf c
| '\n' -> Buffer.add_string buf "\\n"
| c -> Buffer.add_char buf c
done;
Buffer.add_char buf '"';
Buffer.contents buf
let rec value = function
| Relop (_,op,l,r) ->
String.concat " " [value l; relop op; value r]
| Logop (_,op,l,r) ->
String.concat " " [value l; logop op; value r]
| Pfxop (_,op,r) ->
String.concat " " [pfxop op; value r]
| Prefix_relop (_,op,r) ->
String.concat " " [relop op; value r]
| Ident (_,s) -> s
| Int (_,i) -> string_of_int i
| Bool (_,b) -> string_of_bool b
| String (_,s) -> escape_string s
| List (_, l) -> Printf.sprintf "[%s]" (String.concat " " (List.map value l))
| Group (_,g) -> Printf.sprintf "(%s)" (String.concat " " (List.map value g))
| Option(_,v,l) ->
Printf.sprintf "%s {%s}" (value v) (String.concat " " (List.map value l))
| Env_binding (_,id,op,v) ->
String.concat " "
[value id; env_update_op op; value v]
let rec item = function
| Variable (_, _, List (_,([]|[List(_,[])]))) -> ""
| Variable (_, i, List (_,l)) ->
Printf.sprintf "%s: [%s]" i (String.concat " " (List.map value l))
| Variable (_, i, v) -> String.concat ": " [i; value v]
| Section (_,s) ->
Printf.sprintf "%s %s{\n%s\n}"
s.section_kind
(match s.section_name with
| Some s -> escape_string s ^ " "
| None -> "")
(String.concat "\n" (List.map item s.section_items))
let item_order a b = match a,b with
| Variable (_, "opam-version", String (_, ver)), _
when nopatch ver >= (2, 1) -> -1
| _, Variable (_, "opam-version", String (_, ver))
when nopatch ver >= (2, 1) -> 1
| Section _, Variable _ -> 1
| Variable _, Section _ -> -1
| Variable (_,i,_), Variable (_,j,_) -> String.compare i j
| Section (_,s), Section (_,t) ->
let r = String.compare s.section_kind t.section_kind in
if r <> 0 then r
else compare s.section_name t.section_name
let items its =
if not (valid_opamfile_contents its) then
invalid_arg "OpamPrinter.Normalise.items";
let its = List.sort item_order its in
String.concat "\n" (List.map item its) ^ "\n"
let opamfile f = items f.file_contents
end
module Preserved = struct
let items txt orig f =
if not (valid_opamfile_contents f) then
invalid_arg "OpamPrinter.Preserved.items";
let pos_index =
let lines_index =
let rec aux acc s =
let until =
try Some (String.index_from s (List.hd acc) '\n')
with Not_found -> None
in
match until with
| Some until -> aux (until+1 :: acc) s
| None -> Array.of_list (List.rev acc)
in
aux [0] txt
in
fun (_file, li, col) -> lines_index.(li - 1) + col
in
let get_substring start_pos rest =
let start = pos_index start_pos in
let stop = match rest with
| (Section (pos,_) | Variable (pos,_,_)) :: _ -> pos_index pos
| [] -> String.length txt
in
if stop < start then raise Exit
else String.sub txt start (stop - start)
in
let list_take f l =
let rec aux acc = function
| [] -> None, List.rev acc
| x::r ->
if f x then Some x, List.rev_append acc r
else aux (x::acc) r
in
aux [] l
in
let is_variable name = function
| Variable (_, name1, _v1) -> name = name1
| _ -> false
in
let is_section kind name = function
| Section (_, {section_kind; section_name; _}) ->
kind = section_kind && name = section_name
| _ -> false
in
let rec aux acc f = function
| Variable (pos, name, v) :: r ->
(match list_take (is_variable name) f with
| Some (Variable (_, _, v1)), f when value_equals v v1 ->
aux (get_substring pos r :: acc) f r
| Some item, f ->
aux ((items [item] ^ "\n") :: acc) f r
| None, f ->
aux acc f r)
| Section (pos, {section_kind; section_name; _}) as sec :: r ->
(match list_take (is_section section_kind section_name) f with
| Some s, f when opamfile_item_equals sec s ->
aux (get_substring pos r :: acc) f r
| Some item, f ->
aux ((items [item] ^ "\n") :: acc) f r
| None, f -> aux acc f r)
| [] ->
let remaining = match f with
| [] -> []
| f -> [items f ^ "\n"]
in
List.rev_append acc remaining
in
let header = [get_substring ("",1,0) orig] in
String.concat "" (aux header f orig)
let opamfile ?format_from f =
let orig_file = match format_from with
| Some name -> name
| None -> f.file_name
in
let txt =
let b = Buffer.create 4096 in
let ic = open_in orig_file in
try while true do Buffer.add_channel b ic 4096 done; assert false with
| End_of_file -> close_in ic; Buffer.contents b
| e -> close_in ic; raise e
in
let[@ocaml.warning "-3"] orig = OpamParser.string txt orig_file in
items txt orig.file_contents f.file_contents
end
module FullPos = struct
open OpamParserTypes.FullPos
let valid_opamfile_contents = function
| {pelem = Variable ({pelem = "opam-version"; _}, {pelem = String ver; _}); _}::items
when nopatch ver >= (2, 1) ->
let opam_version_field = function
| {pelem = Variable ({pelem = "opam-version"; _}, _); _} -> true
| _ -> false
in
not (List.exists opam_version_field items)
| _::items ->
let greater_2_0_opam_version_field = function
| {pelem = Variable ({pelem = "opam-version"; _}, {pelem = String ver; _}); _}
when nopatch ver >= (2, 1) -> true
| _ -> false
in
not (List.exists greater_2_0_opam_version_field items)
| [] -> true
let relop_kind = relop
let relop r = relop_kind r.pelem
let logop_kind = logop
let logop r = logop_kind r.pelem
let pfxop_kind = pfxop
let pfxop r = pfxop_kind r.pelem
let env_update_op_kind = env_update_op
let env_update_op r = env_update_op_kind r.pelem
let rec format_value fmt v =
match v.pelem with
| Relop (op,l,r) ->
Format.fprintf fmt "@[<h>%a %s@ %a@]"
format_value l (relop op) format_value r
| Logop (op,l,r) ->
Format.fprintf fmt "@[<hv>%a %s@ %a@]"
format_value l (logop op) format_value r
| Pfxop (op,r) ->
Format.fprintf fmt "@[<h>%s%a@]" (pfxop op) format_value r
| Prefix_relop (op,r) ->
Format.fprintf fmt "@[<h>%s@ %a@]"
(relop op) format_value r
| Ident s -> Format.fprintf fmt "%s" s
| Int i -> Format.fprintf fmt "%d" i
| Bool b -> Format.fprintf fmt "%b" b
| String s ->
if String.contains s '\n'
then Format.fprintf fmt "\"\"\"\n%s\"\"\""
(escape_string ~triple:true s)
else Format.fprintf fmt "\"%s\"" (escape_string s)
| List l ->
Format.fprintf fmt "@[<hv>[@;<0 2>@[<hv>%a@]@,]@]" format_values l.pelem
| Group g -> Format.fprintf fmt "@[<hv>(%a)@]" format_values g.pelem
| Option (v,l) ->
Format.fprintf fmt "@[<hov 2>%a@ {@[<hv>%a@]}@]"
format_value v format_values l.pelem
| Env_binding (id,op,v) ->
Format.fprintf fmt "@[<h>%a %s@ %a@]"
format_value id (env_update_op op) format_value v
and format_values fmt = function
| [] -> ()
| [v] -> format_value fmt v
| v::r ->
format_value fmt v;
Format.pp_print_space fmt ();
format_values fmt r
let value v =
format_value Format.str_formatter v; Format.flush_str_formatter ()
let value_list vl =
Format.fprintf Format.str_formatter "@[<hv>%a@]" format_values vl.pelem;
Format.flush_str_formatter ()
let format_sstring fmt s =
Format.fprintf fmt "%s" s.pelem
let rec format_item fmt i =
match i.pelem with
| Variable (_, { pelem = List { pelem = []; _}; _}) -> ()
| Variable (_,
{ pelem =
List { pelem =
[{ pelem =
List { pelem = []; _}; _}]; _}; _}) -> ()
| Variable (i, { pelem = List { pelem = l; _}; _}) ->
if List.exists
(fun v ->
match v.pelem with
List _ | Option (_,{ pelem = _::_; _}) -> true
| _ -> false)
l
then Format.fprintf fmt "@[<v>%a: [@;<0 2>@[<v>%a@]@,]@]"
format_sstring i format_values l
else Format.fprintf fmt "@[<hv>%a: [@;<0 2>@[<hv>%a@]@,]@]"
format_sstring i format_values l
| Variable (i, ({ pelem = String s ; _} as v))
when String.contains s '\n' ->
Format.fprintf fmt "@[<hov 0>%a: %a@]" format_sstring i format_value v
| Variable (i, v) ->
Format.fprintf fmt "@[<hov 2>%a:@ %a@]" format_sstring i format_value v
| Section s ->
Format.fprintf fmt "@[<v 0>%a %s{@;<0 2>@[<v>%a@]@,}@]"
format_sstring s.section_kind
(match s.section_name with
| Some s -> Printf.sprintf "\"%s\" " (escape_string s.pelem)
| None -> "")
format_items s.section_items.pelem
and format_items fmt is =
Format.pp_open_vbox fmt 0;
(match is with
| [] -> ()
| i::r ->
format_item fmt i;
List.iter (fun i -> Format.pp_print_cut fmt (); format_item fmt i) r);
Format.pp_close_box fmt ()
let format_opamfile fmt f =
format_items fmt f.file_contents;
Format.pp_print_newline fmt ()
let items l =
if not (valid_opamfile_contents l) then
invalid_arg "OpamPrinter.FullPos.items";
format_items Format.str_formatter l; Format.flush_str_formatter ()
let opamfile f =
items f.file_contents
let relop_equals r1 r2 = r1.pelem = r2.pelem
let logop_equals r1 r2 = r1.pelem = r2.pelem
let pfxop_equals r1 r2 = r1.pelem = r2.pelem
let env_update_op_equals r1 r2 = r1.pelem = r2.pelem
let rec value_equals v1 v2 =
match v1.pelem, v2.pelem with
| Bool (b1), Bool (b2) -> b1 = b2
| Int (i1), Int (i2) -> i1 = i2
| String (s1), String (s2) -> s1 = s2
| Relop (r1, va1, vb1), Relop (r2, va2, vb2) ->
relop_equals r1 r2 && value_equals va1 va2 && value_equals vb1 vb2
| Prefix_relop (r1, v1), Prefix_relop (r2, v2) ->
relop_equals r1 r2 && value_equals v1 v2
| Logop (l1, va1, vb1), Logop (l2, va2, vb2) ->
logop_equals l1 l2 && value_equals va1 va2 && value_equals vb1 vb2
| Pfxop (p1, v1), Pfxop (p2, v2) ->
pfxop_equals p1 p2 && value_equals v1 v2
| Ident (s1), Ident (s2) ->
s1 = s2
| List (vl1), List (vl2) ->
(try List.for_all2 value_equals vl1.pelem vl2.pelem
with Invalid_argument _ -> false)
| Group (vl1), Group (vl2) ->
(try List.for_all2 value_equals vl1.pelem vl2.pelem
with Invalid_argument _ -> false)
| Option (v1, vl1), Option (v2, vl2) ->
value_equals v1 v2 &&
(try List.for_all2 value_equals vl1.pelem vl2.pelem
with Invalid_argument _ -> false)
| Env_binding (v1, op1, vx1), Env_binding (v2, op2, vx2) ->
env_update_op_equals op1 op2 && value_equals v1 v2 && value_equals vx1 vx2
| _ -> false
let sstring_equals s1 s2 = s1.pelem = s2.pelem
let rec opamfile_item_equals i1 i2 =
match i1.pelem , i2.pelem with
| Variable (n1, v1), Variable (n2, v2) ->
sstring_equals n1 n2 && value_equals v1 v2
| Section (s1), Section (s2) ->
sstring_equals s1.section_kind s2.section_kind &&
(match s1.section_name, s2.section_name with
| None, None -> true
| Some s1, Some s2 -> sstring_equals s1 s2
| _,_ -> false)
&& (try List.for_all2 opamfile_item_equals
s1.section_items.pelem s2.section_items.pelem
with Invalid_argument _ -> false)
| _ -> false
module Normalise = struct
let escape_string s =
let len = String.length s in
let buf = Buffer.create (len * 2) in
Buffer.add_char buf '"';
for i = 0 to len -1 do
match s.[i] with
| '\\' | '"' as c -> Buffer.add_char buf '\\'; Buffer.add_char buf c
| '\n' -> Buffer.add_string buf "\\n"
| c -> Buffer.add_char buf c
done;
Buffer.add_char buf '"';
Buffer.contents buf
let rec value v =
match v.pelem with
| Relop (op,l,r) ->
String.concat " " [value l; relop op; value r]
| Logop (op,l,r) ->
String.concat " " [value l; logop op; value r]
| Pfxop (op,r) ->
String.concat " " [pfxop op; value r]
| Prefix_relop (op,r) ->
String.concat " " [relop op; value r]
| Ident s -> s
| Int i -> string_of_int i
| Bool b -> string_of_bool b
| String s -> escape_string s
| List l ->
Printf.sprintf "[%s]" (String.concat " " (List.map value l.pelem))
| Group g ->
Printf.sprintf "(%s)" (String.concat " " (List.map value g.pelem))
| Option(v,l) ->
Printf.sprintf "%s {%s}" (value v)
(String.concat " " (List.map value l.pelem))
| Env_binding (id,op,v) ->
String.concat " "
[value id; env_update_op op; value v]
let rec item i =
match i.pelem with
| Variable (_, { pelem = List { pelem = []; _}; _})
| Variable (_, { pelem =
List { pelem =
[{ pelem = List { pelem = []; _}; _}]; _}; _})
-> ""
| Variable (i, { pelem = List { pelem = l; _}; _}) ->
Printf.sprintf "%s: [%s]" i.pelem (String.concat " " (List.map value l))
| Variable (i, v) -> String.concat ": " [i.pelem; value v]
| Section s ->
Printf.sprintf "%s %s{\n%s\n}"
s.section_kind.pelem
(match s.section_name with
| Some s -> escape_string s.pelem ^ " "
| None -> "")
(String.concat "\n" (List.map item s.section_items.pelem))
let item_order a b =
match a.pelem ,b.pelem with
| Variable ({pelem = "opam-version"; _}, {pelem = String ver; _}), _
when nopatch ver >= (2, 1) -> -1
| _, Variable ({pelem = "opam-version"; _}, {pelem = String ver; _})
when nopatch ver >= (2, 1) -> 1
| Section _, Variable _ -> 1
| Variable _, Section _ -> -1
| Variable (i,_), Variable (j,_) -> String.compare i.pelem j.pelem
| Section s, Section t ->
let r = String.compare s.section_kind.pelem t.section_kind.pelem in
if r <> 0 then r
else compare s.section_name t.section_name
let items its =
if not (valid_opamfile_contents its) then
invalid_arg "OpamPrinter.Normalise.items";
let its = List.sort item_order its in
String.concat "\n" (List.map item its) ^ "\n"
let opamfile f = items f.file_contents
end
module Preserved = struct
let items txt orig f =
if not (valid_opamfile_contents f) then
invalid_arg "OpamPrinter.Preserved.items";
let pos_index =
let lines_index =
let rec aux acc s =
let until =
try Some (String.index_from s (List.hd acc) '\n')
with Not_found -> None
in
match until with
| Some until -> aux (until+1 :: acc) s
| None -> Array.of_list (List.rev acc)
in
aux [0] txt
in
fun p ->
let sli, scol = p.start in
let eli, ecol = p.stop in
lines_index.(sli - 1) + scol, lines_index.(eli -1) + ecol
in
let get_substring pos =
let start, stop = pos_index pos in
if stop < start then raise Exit
else String.sub txt start (stop - start)
in
let list_take f l =
let rec aux acc = function
| [] -> None, List.rev acc
| x::r ->
if f x then Some x, List.rev_append acc r
else aux (x::acc) r
in
aux [] l
in
let is_variable name = function
| { pelem = Variable (name1, _v1); _} -> sstring_equals name name1
| _ -> false
in
let is_section kind name = function
| { pelem = Section ({section_kind; section_name; _}); _} ->
sstring_equals kind section_kind &&
(match name, section_name with
| None, None -> true
| Some s1, Some s2 -> sstring_equals s1 s2
| _,_ -> false)
| _ -> false
in
let rec aux acc f = function
| { pos; pelem = Variable ( name, v)} :: r ->
(match list_take (is_variable name) f with
| Some {pelem = Variable (_, v1); _}, f when value_equals v v1 ->
aux (get_substring pos :: acc) f r
| Some item, f ->
aux (items [item] :: acc) f r
| None, f ->
aux acc f r)
| {pos; pelem = Section {section_kind; section_name; _}} as sec :: r ->
(match list_take (is_section section_kind section_name) f with
| Some s, f when opamfile_item_equals sec s ->
aux (get_substring pos :: acc) f r
| Some item, f ->
aux (items [item] :: acc) f r
| None, f -> aux acc f r)
| [] ->
let remaining = match f with
| [] -> [""]
| f -> [items f; ""]
in
List.rev_append acc remaining
in
let body = String.concat "\n" (aux [] f orig) in
let header =
match orig with
| [] -> ""
| h::_ -> get_substring {filename=""; start=(1,0); stop=h.pos.start}
in
header ^ body
let opamfile ?format_from f =
let orig_file = match format_from with
| Some name -> name
| None -> f.file_name
in
let txt =
let b = Buffer.create 4096 in
let ic = open_in orig_file in
try while true do Buffer.add_channel b ic 4096 done; assert false with
| End_of_file -> close_in ic; Buffer.contents b
| e -> close_in ic; raise e
in
let orig = OpamParser.FullPos.string txt orig_file in
items txt orig.file_contents f.file_contents
end
end