Update TODO.
[goals.git] / src / utils.ml
1 (* Goalfile utilities
2  * Copyright (C) 2019 Richard W.M. Jones
3  * Copyright (C) 2019 Red Hat Inc.
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License along
16  * with this program; if not, write to the Free Software Foundation, Inc.,
17  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18  *)
19
20 open Printf
21
22 let failwithf fs = ksprintf failwith fs
23
24 let (//) = Filename.concat
25 let is_directory d = try Sys.is_directory d with Sys_error _ -> false
26
27 (* From OCaml 4.08 sources.  We can remove this when we can
28  * depend on min OCaml 4.08.
29  *)
30 let filter_map f =
31   let rec aux accu = function
32     | [] -> List.rev accu
33     | x :: l ->
34         match f x with
35         | None -> aux accu l
36         | Some v -> aux (v :: accu) l
37   in
38   aux []
39
40
41 (* From libguestfs sources. *)
42 let rec string_find s sub =
43   let len = String.length s in
44   let sublen = String.length sub in
45   let rec loop i =
46     if i <= len-sublen then (
47       let rec loop2 j =
48         if j < sublen then (
49           if s.[i+j] = sub.[j] then loop2 (j+1)
50           else -1
51         ) else
52           i (* found *)
53       in
54       let r = loop2 0 in
55       if r = -1 then loop (i+1) else r
56     ) else
57       -1 (* not found *)
58   in
59   loop 0
60
61 let rec split sep str =
62   let len = String.length sep in
63   let seplen = String.length str in
64   let i = string_find str sep in
65   if i = -1 then str, ""
66   else (
67     String.sub str 0 i, String.sub str (i + len) (seplen - i - len)
68   )
69
70 and nsplit ?(max = 0) sep str =
71   if max < 0 then
72     invalid_arg "String.nsplit: max parameter should not be negative";
73
74   (* If we reached the limit, OR if the pattern does not match the string
75    * at all, return the rest of the string as a single element list.
76    *)
77   if max = 1 || string_find str sep = -1 then
78     [str]
79   else (
80     let s1, s2 = split sep str in
81     let max = if max = 0 then 0 else max - 1 in
82     s1 :: nsplit ~max sep s2
83   )
84
85 let isspace c =
86   c = ' '
87   (* || c = '\f' *) || c = '\n' || c = '\r' || c = '\t' (* || c = '\v' *)
88
89 let triml ?(test = isspace) str =
90   let i = ref 0 in
91   let n = ref (String.length str) in
92   while !n > 0 && test str.[!i]; do
93     decr n;
94     incr i
95   done;
96   if !i = 0 then str
97   else String.sub str !i !n
98
99 let trimr ?(test = isspace) str =
100   let n = ref (String.length str) in
101   while !n > 0 && test str.[!n-1]; do
102     decr n
103   done;
104   if !n = String.length str then str
105   else String.sub str 0 !n
106
107 let trim ?(test = isspace) str =
108   trimr ~test (triml ~test str)
109
110 let absolute_path path =
111   if not (Filename.is_relative path) then path else Sys.getcwd () // path