1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
|
exception Syntax_error of ((int * int) option * string)
external lex_numeric_compare: string -> string -> int = "caml_lex_numeric_compare"
external length : string -> int = "%string_length"
external unsafe_get : string -> int -> char = "%string_unsafe_get"
external char_code: char -> int = "%identity"
external char_chr: int -> char = "%identity"
module B = Bytes
let bts = B.unsafe_to_string
let bos = B.unsafe_of_string
let get_lexing_position lexbuf =
let p = Lexing.lexeme_start_p lexbuf in
let line_number = p.Lexing.pos_lnum in
let column = p.Lexing.pos_cnum - p.Lexing.pos_bol + 1 in
(line_number, column)
(* Modification of Bytes.escaped to leave UTF-8 bytes unescaped *)
let escape_bytes s =
let char_code_zero = 48 in
let high_bit_set = 128 in
let n = ref 0 in
for i = 0 to B.length s - 1 do
n := !n +
(match B.unsafe_get s i with
| '\"' | '\\' | '\n' | '\t' | '\r' | '\b' -> 2
| ' ' .. '~' -> 1
| c when (char_code c >= high_bit_set) -> 1
| _ -> 4)
done;
if !n = B.length s then B.copy s else begin
let s' = B.create !n in
n := 0;
for i = 0 to B.length s - 1 do
begin match B.unsafe_get s i with
| ('\"' | '\\') as c ->
B.unsafe_set s' !n '\\'; incr n; B.unsafe_set s' !n c
| '\n' ->
B.unsafe_set s' !n '\\'; incr n; B.unsafe_set s' !n 'n'
| '\t' ->
B.unsafe_set s' !n '\\'; incr n; B.unsafe_set s' !n 't'
| '\r' ->
B.unsafe_set s' !n '\\'; incr n; B.unsafe_set s' !n 'r'
| '\b' ->
B.unsafe_set s' !n '\\'; incr n; B.unsafe_set s' !n 'b'
| (' ' .. '~') as c -> B.unsafe_set s' !n c
| c when (char_code c >= high_bit_set ) -> B.unsafe_set s' !n c
| c ->
let a = char_code c in
B.unsafe_set s' !n '\\';
incr n;
B.unsafe_set s' !n (char_chr (char_code_zero + a / 100));
incr n;
B.unsafe_set s' !n (char_chr (char_code_zero + (a / 10) mod 10));
incr n;
B.unsafe_set s' !n (char_chr (char_code_zero + a mod 10));
end;
incr n
done;
s'
end
(* Modification of String.escaped to leave UTF-8 bytes unescaped *)
let escape_string s =
let rec escape_if_needed s n i =
if i >= n then s else
match unsafe_get s i with
| '\"' | '\\' | '\000'..'\031' | '\127' ->
bts (escape_bytes (bos s))
| _ -> escape_if_needed s n (i+1)
in
escape_if_needed s (length s) 0
let default default_value opt =
match opt with
| None -> default_value
| Some value -> value
let lexical_numeric_compare s t =
lex_numeric_compare s t
(** Convert a relative path to an absolute path based on the current working directory *)
let absolute_path relative_path =
FilePath.make_absolute (Sys.getcwd ()) relative_path
(** Convert a list of strings to a string of unquoted, space separated words *)
let string_of_list ss =
let rec aux xs acc =
match xs with
| [] -> acc
| x :: xs' -> aux xs' (Printf.sprintf "%s %s" acc x)
in
match ss with
| [] -> ""
| x :: xs -> Printf.sprintf "%s%s" x (aux xs "")
(** Convert a list of strings to JSON *)
let json_of_list ss =
let ss = List.map (fun x -> `String x) ss in
Yojson.Safe.to_string (`List ss)
(** Split string on whitespace *)
let list_of_string s =
Pcre2.split ~pat:"\\s+" s
(** Split string on whitespace, excluding last if single-quoted value,
as needed for parsing vyconf request path option **)
let list_of_path p =
let seg = String.trim p |> String.split_on_char '\'' in
match seg with
| [h] -> Pcre2.split ~pat:"\\s+" h
| h :: h' :: _ -> (Pcre2.split ~pat:"\\s+" h) @ [h']
| _ -> []
let drop_last l =
let rec aux acc l =
match l with
| [] | [_] -> List.rev acc
| hd :: tl ->
let acc' = hd :: acc in
aux acc' tl
in
aux [] l
let drop_last_n l n =
let rec aux k l =
match l with
| [] -> []
| _ -> if k <= 0 then l else aux (k - 1) (drop_last l)
in aux n l
let drop_first l =
match l with
| [] -> []
| _ :: tl -> tl
let rec get_last l =
match l with
| [] -> None
| h :: [] -> Some h
| _ :: tl -> get_last tl
let get_last_n l n =
get_last (drop_last_n l n)
let lex_order l k =
let c = compare (get_last l) (get_last k) in
match c with
| 0 -> compare (drop_last l) (drop_last k)
| _ as r -> r
let colex_order l k =
let rec comp x y =
let c = compare (get_last x) (get_last y) in
match c with
| 0 -> comp (drop_last x) (drop_last y)
| _ as r -> r
in comp l k
let is_empty l =
List.compare_length_with l 0 = 0
let rec is_sublist l k =
match l, k with
| [], _ -> true
| _, [] -> false
| hl::tl, hk::tk -> hl = hk && is_sublist tl tk
let flag path =
let len = List.length path in
let aux p i =
drop_last_n p (len - i - 1)
in
List.mapi (fun k _ -> aux path k) path
exception End_of_read of in_channel
let file_compare ?(ignore_line_prefix="") file1 file2 =
let open_files f1 f2 =
let ic1_opt =
try Some (open_in f1)
with Sys_error _ -> None
in
let ic2_opt =
try Some (open_in f2)
with Sys_error _ -> None
in
ic1_opt, ic2_opt
in
let rec line_loop ic =
let line =
try
input_line ic
with End_of_file -> raise (End_of_read ic)
in
let line' = String.trim line in
if line' <> "" &&
(ignore_line_prefix = "" ||
not (String.starts_with ~prefix:ignore_line_prefix line'))
then line'
else line_loop ic
in
let rec loop ic1 ic2 =
try
let line1 = line_loop ic1 in
let line2 = line_loop ic2 in
if line1 <> line2 then false
else loop ic1 ic2
with End_of_read ic ->
try
if ic = ic1 then
let () = ignore (line_loop ic2) in false
else false
with End_of_read _ ->
true
in
match open_files file1 file2 with
| Some ic1, Some ic2 ->
let result = loop ic1 ic2 in
close_in ic1;
close_in ic2;
result
| Some ic1, None -> close_in ic1; false
| None, Some ic2 -> close_in ic2; false
| None, None -> false
|