Fix previous commit for non-Debian case.
[febootstrap.git] / febootstrap_utils.ml
1 (* febootstrap 3
2  * Copyright (C) 2009-2010 Red Hat Inc.
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17  *)
18
19 open Unix
20 open Printf
21
22 let (//) = Filename.concat
23
24 let file_exists name =
25   try access name [F_OK]; true
26   with Unix_error _ -> false
27
28 let dir_exists name =
29   try (stat name).st_kind = S_DIR
30   with Unix_error _ -> false
31
32 let rec uniq ?(cmp = Pervasives.compare) = function
33   | [] -> []
34   | [x] -> [x]
35   | x :: y :: xs when cmp x y = 0 ->
36       uniq ~cmp (x :: xs)
37   | x :: y :: xs ->
38       x :: uniq ~cmp (y :: xs)
39
40 let sort_uniq ?(cmp = Pervasives.compare) xs =
41   let xs = List.sort cmp xs in
42   let xs = uniq ~cmp xs in
43   xs
44
45 let rec input_all_lines chan =
46   try let line = input_line chan in line :: input_all_lines chan
47   with End_of_file -> []
48
49 let run_command_get_lines cmd =
50   let chan = open_process_in cmd in
51   let lines = input_all_lines chan in
52   let stat = close_process_in chan in
53   (match stat with
54    | WEXITED 0 -> ()
55    | WEXITED i ->
56        eprintf "febootstrap: command '%s' failed (returned %d), see earlier error messages\n" cmd i;
57        exit i
58    | WSIGNALED i ->
59        eprintf "febootstrap: command '%s' killed by signal %d" cmd i;
60        exit 1
61    | WSTOPPED i ->
62        eprintf "febootstrap: command '%s' stopped by signal %d" cmd i;
63        exit 1
64   );
65   lines
66
67 let run_command cmd =
68   if Sys.command cmd <> 0 then (
69     eprintf "febootstrap: %s: command failed, see earlier errors\n" cmd;
70     exit 1
71   )
72
73 let run_python code args =
74   let cmd = sprintf "python -c %s %s"
75     (Filename.quote code)
76     (String.concat " " (List.map Filename.quote args)) in
77   if Sys.command cmd <> 0 then (
78     eprintf "febootstrap: external python program failed, see earlier error messages\n";
79     exit 1
80   )
81
82 let tmpdir () =
83   let chan = open_in "/dev/urandom" in
84   let data = String.create 16 in
85   really_input chan data 0 (String.length data);
86   close_in chan;
87   let data = Digest.to_hex (Digest.string data) in
88   (* Note this is secure, because if the name already exists, even as a
89    * symlink, mkdir(2) will fail.
90    *)
91   let tmpdir = Filename.temp_dir_name // sprintf "febootstrap%s.tmp" data in
92   Unix.mkdir tmpdir 0o700;
93
94   (* Only remove the directory if --save-temps was *not* specified. *)
95   if not Febootstrap_cmdline.save_temps then
96     at_exit
97       (fun () ->
98         let cmd = sprintf "rm -rf %s" (Filename.quote tmpdir) in
99         ignore (Sys.command cmd));
100
101   tmpdir
102
103 let rec find s sub =
104   let len = String.length s in
105   let sublen = String.length sub in
106   let rec loop i =
107     if i <= len-sublen then (
108       let rec loop2 j =
109         if j < sublen then (
110           if s.[i+j] = sub.[j] then loop2 (j+1)
111           else -1
112         ) else
113           i (* found *)
114       in
115       let r = loop2 0 in
116       if r = -1 then loop (i+1) else r
117     ) else
118       -1 (* not found *)
119   in
120   loop 0
121
122 let rec string_split sep str =
123   let len = String.length str in
124   let seplen = String.length sep in
125   let i = find str sep in
126   if i = -1 then [str]
127   else (
128     let s' = String.sub str 0 i in
129     let s'' = String.sub str (i+seplen) (len-i-seplen) in
130     s' :: string_split sep s''
131   )
132
133 let string_prefix p str =
134   let len = String.length str in
135   let plen = String.length p in
136   len >= plen && String.sub str 0 plen = p
137
138 let path_prefix p path =
139   let len = String.length path in
140   let plen = String.length p in
141   path = p || (len > plen && String.sub path 0 (plen+1) = (p ^ "/"))
142
143 let rec filter_map f = function
144   | [] -> []
145   | x :: xs ->
146       let x = f x in
147       match x with
148       | None -> filter_map f xs
149       | Some x -> x :: filter_map f xs