You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

157 lines
3.7 KiB

(*
* Copyright (c) 2016 - present Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*)
open! IStd
(** Escape a string for use in a CSV or XML file: replace reserved
characters with escape sequences *)
module F = Format
(** apply a map function for escape sequences *)
let escape_map map_fun s =
let needs_escape = String.exists ~f:(fun c -> Option.is_some (map_fun c)) s in
if needs_escape then (
let len = String.length s in
let buf = Buffer.create len in
for i = 0 to len - 1 do
let c = String.unsafe_get s i in
match map_fun c with None -> Buffer.add_char buf c | Some s' -> Buffer.add_string buf s'
done ;
Buffer.contents buf )
else (* not escaping anything, so don't waste memory on a copy of the string *)
s
let escape_csv s =
let map = function
| '"' ->
Some "\"\""
| c when Char.to_int c > 127 ->
Some "?" (* non-ascii character: escape *)
| _ ->
None
in
escape_map map s
let escape_xml s =
let map = function
| '"' ->
(* on next line to avoid bad indentation *)
Some """
| '>' ->
Some ">"
| '<' ->
Some "&lt;"
| '&' ->
Some "&amp;"
| '%' ->
Some "&#37;"
| c when Char.to_int c > 127 ->
(* non-ascii character: escape *)
Some ("&#" ^ string_of_int (Char.to_int c) ^ ";")
| _ ->
None
in
escape_map map s
let escape_url s =
let map = function
| '!' ->
Some "%21"
| '#' ->
Some "%23"
| '$' ->
Some "%24"
| '&' ->
Some "%26"
| '\'' ->
Some "%27"
| '(' ->
Some "%28"
| ')' ->
Some "%29"
| '*' ->
Some "%2A"
| '+' ->
Some "%2B"
| ',' ->
Some "%2C"
| '/' ->
Some "%2F"
| ':' ->
Some "%3A"
| ';' ->
Some "%3B"
| '=' ->
Some "%3D"
| '?' ->
Some "%3F"
| '@' ->
Some "%40"
| '[' ->
Some "%5B"
| ']' ->
Some "%5D"
| _ ->
None
in
escape_map map s
let escape_dotty s =
let map = function '"' -> Some "\\\"" | '\\' -> Some "\\\\" | _ -> None in
escape_map map s
let escape_path s =
let map = function
| c ->
if String.equal (Char.escaped c) Filename.dir_sep then Some "_" else None
in
escape_map map s
(* Python 2 sucks at utf8 so do not write unicode file names to disk
as Python may need to see them *)
let escape_filename s =
let map = function
| c when Char.to_int c > 127 ->
Some "?" (* non-ascii character: escape *)
| _ ->
None
in
escape_map map s
let escape_double_quotes s = escape_map (function '"' -> Some "\\\"" | _ -> None) s
let escape_in_single_quotes s =
Printf.sprintf "'%s'" (escape_map (function '\'' -> Some "'\\''" | _ -> None) s)
let escape_shell =
let no_quote_needed = Str.regexp "^[A-Za-z0-9-_%/:,.]+$" in
let easy_single_quotable = Str.regexp "^[^']+$" in
let easy_double_quotable = Str.regexp "^[^$`\\!]+$" in
function
| "" ->
"''"
| arg ->
if Str.string_match no_quote_needed arg 0 then arg
else if Str.string_match easy_single_quotable arg 0 then F.sprintf "'%s'" arg
else if Str.string_match easy_double_quotable arg 0 then
escape_double_quotes arg |> F.sprintf "\"%s\""
else
(* ends on-going single quote, output single quote inside double quotes, then open a new
single quote *)
escape_map (function '\'' -> Some "'\"'\"'" | _ -> None) arg |> F.sprintf "'%s'"