(**************************************************************************)
(*  This file is part of BINSEC.                                          *)
(*                                                                        *)
(*  Copyright (C) 2016-2019                                               *)
(*    CEA (Commissariat à l'énergie atomique et aux énergies              *)
(*         alternatives)                                                  *)
(*                                                                        *)
(*  you can redistribute it and/or modify it under the terms of the GNU   *)
(*  Lesser General Public License as published by the Free Software       *)
(*  Foundation, version 2.1.                                              *)
(*                                                                        *)
(*  It is distributed in the hope that it will be useful,                 *)
(*  but WITHOUT ANY WARRANTY; without even the implied warranty of        *)
(*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *)
(*  GNU Lesser General Public License for more details.                   *)
(*                                                                        *)
(*  See the GNU Lesser General Public License version 2.1                 *)
(*  for more details (enclosed in the file licenses/LGPLv2.1).            *)
(*                                                                        *)
(**************************************************************************)

open Format

type sformat = (unit,Format.formatter,unit) Pervasives.format
type 'a formatter = Format.formatter -> 'a -> unit

(* The empty format [ef] string *)
let ef = format_of_string ""

let pp_list ?(pre=ef) ?(post=ef) ?(sep=format_of_string "; ") pp fmt l =
  let rec pp_aux fmt = function
    | [] -> assert false
    | [e] -> fprintf fmt "%a" pp e
    | e :: es -> fprintf fmt "%a%(%)%a" pp e sep pp_aux es
  in
  if l = [] then fprintf fmt ""
  else fprintf fmt "%(%)%a%(%)" pre pp_aux l post

let pp_as_string to_string fmt element =
  Format.fprintf fmt "%s" (to_string element)

let pp_opt_as_string to_string fmt = function
  | None -> fprintf fmt "None"
  | Some element ->
    pp_as_string to_string fmt element

let pp_opt pp ppf = function
  | None -> fprintf ppf "none"
  | Some v -> fprintf ppf "%a" pp v

let string_from_pp pp_fun element =
  Format.asprintf "%a" pp_fun element

let pp_dba_prelude ?(flat_memory=false) ppf () =
  let default_value = if flat_memory then "0x7fffee28" else "(stack, 0x7fffee28)" in
  Format.fprintf ppf
    "@[<v 0>\
     # configuration@ \
     \\entry_point : (0x00000000, 0)@ \
     \\addr : 32@ \
     \\endianness : little@ \
     @ \
     #declaration@ \
     var CF : 1 <FLAG>@ \
     var DF : 1 <FLAG>@ \
     var ZF : 1 <FLAG>@ \
     var OF : 1 <FLAG>@ \
     var SF : 1 <FLAG>@ \
     var AF : 1 <FLAG>@ \
     var PF : 1 <FLAG>@ \
     var NOP : 1@ \
     @ \
     var eax : 32@ \
     var ecx : 32@ \
     var edx : 32@ \
     var ebx : 32@ \
     var esp : 32@ \
     var ebp : 32@ \
     var esi : 32@ \
     var edi : 32@ \
     @ \
     var mm0 : 64@ \
     var mm1 : 64@ \
     var mm2 : 64@ \
     var mm3 : 64@ \
     var mm4 : 64@ \
     var mm5 : 64@ \
     var mm6 : 64@ \
     var mm7 : 64@ \
     @ \
     var st0 : 80@ \
     var st1 : 80@ \
     var st2 : 80@ \
     var st3 : 80@ \
     var st4 : 80@ \
     var st5 : 80@ \
     var st6 : 80@ \
     var st7 : 80@ \
     @ \
     var xmm0 : 128@ \
     var xmm1 : 128@ \
     var xmm2 : 128@ \
     var xmm3 : 128@ \
     var xmm4 : 128@ \
     var xmm5 : 128@ \
     var xmm6 : 128@ \
     var xmm7 : 128@ \
     @ \
     var res8 : 8@ \
     var res16 : 16@ \
     var res32 : 32@ \
     var res64 : 64@ \
     @ \
     var temp8 : 8@ \
     var temp16 : 16@ \
     var temp32 : 32@ \
     var temp40 : 40@ \
     var temp48 : 48@ \
     var temp56 : 56@ \
     var temp64 : 64@ \
     var temp72 : 72@ \
     var temp80 : 80@ \
     var temp88 : 88@ \
     var temp96 : 96@ \
     var temp104 : 104@ \
     var temp112 : 112@ \
     var temp120 : 120@ \
     var temp128 : 128@ \
     var temp256 : 256@ \
     @ \
     var temp9 : 9@ \
     var temp17 : 17@ \
     var temp33 : 33@ \
     @ \
     var temp : 32@ \
     @ \
     var cpt : 16@ \
     var cpt : 32@ \
     @ \
     #initialisation@ \
     @ \
     CF := 0@ \
     DF := 0@ \
     SF := 0@ \
     ebp := %s@ \
     esp := %s@ \
     eax := 0<32>@ \
     ebx := 0<32>@ \
     ecx := 0<32>@ \
     edx := 0<32>@ \
     edi := 0<32>@ \
     esi := 0<32>@ \
     @]@."
    default_value
    default_value


let pp_byte ?(prefixed=false) ppf n =
  assert (n <= 255 && n >= 0); (* Check it is indeed a 8-bits byte *)
  Format.fprintf ppf "%s%2x"
    (if prefixed then "0x" else "")
    n

let pp_to_file ~filename pp value =
  let oc = open_out filename in
  let ppf = Format.formatter_of_out_channel oc in
  fprintf ppf "%a@?" pp value;
  close_out oc
