2 * Copyright (C) 2009-2011 Red Hat Inc.
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.
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.
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
19 (* Please read generator/README first. *)
22 * Note we don't want to use any external OCaml libraries which
23 * makes this a bit harder than it should be.
31 let errcode_of_ret = function
32 | RConstOptString _ ->
34 | RErr | RInt _ | RBool _ | RInt64 _ ->
37 | RString _ | RBufferOut _
38 | RStringList _ | RHashtable _
39 | RStruct _ | RStructList _ ->
42 let string_of_errcode = function
43 | `ErrorIsMinusOne -> "-1"
44 | `ErrorIsNULL -> "NULL"
46 (* Generate a uuidgen-compatible UUID (used in tests). However to
47 * avoid having the UUID change every time we rebuild the tests,
48 * generate it as a function of the contents of the
49 * generator_actions.ml file.
51 * Originally I thought uuidgen was using RFC 4122, but it doesn't
54 * Note that the format must be 01234567-0123-0123-0123-0123456789ab
57 let s = Digest.to_hex (Digest.file "generator/generator_actions.ml") in
59 (* In util-linux <= 2.19, mkswap -U cannot handle the first byte of
60 * the UUID being zero, so we artificially rewrite such UUIDs.
61 * http://article.gmane.org/gmane.linux.utilities.util-linux-ng/4273
63 if s.[0] = '0' && s.[1] = '0' then
66 String.sub s 0 8 ^ "-"
67 ^ String.sub s 8 4 ^ "-"
68 ^ String.sub s 12 4 ^ "-"
69 ^ String.sub s 16 4 ^ "-"
72 type rstructs_used_t = RStructOnly | RStructListOnly | RStructAndList
74 (* Returns a list of RStruct/RStructList structs that are returned
75 * by any function. Each element of returned list is a pair:
77 * (structname, RStructOnly)
78 * == there exists function which returns RStruct (_, structname)
79 * (structname, RStructListOnly)
80 * == there exists function which returns RStructList (_, structname)
81 * (structname, RStructAndList)
82 * == there are functions returning both RStruct (_, structname)
83 * and RStructList (_, structname)
85 let rstructs_used_by functions =
86 (* ||| is a "logical OR" for rstructs_used_t *)
90 | _, RStructAndList -> RStructAndList
91 | RStructOnly, RStructListOnly
92 | RStructListOnly, RStructOnly -> RStructAndList
93 | RStructOnly, RStructOnly -> RStructOnly
94 | RStructListOnly, RStructListOnly -> RStructListOnly
97 let h = Hashtbl.create 13 in
99 (* if elem->oldv exists, update entry using ||| operator,
100 * else just add elem->newv to the hash
102 let update elem newv =
103 try let oldv = Hashtbl.find h elem in
104 Hashtbl.replace h elem (newv ||| oldv)
105 with Not_found -> Hashtbl.add h elem newv
109 fun (_, (ret, _, _), _, _, _, _, _) ->
111 | RStruct (_, structname) -> update structname RStructOnly
112 | RStructList (_, structname) -> update structname RStructListOnly
116 (* return key->values as a list of (key,value) *)
117 Hashtbl.fold (fun key value xs -> (key, value) :: xs) h []
119 let failwithf fs = ksprintf failwith fs
121 let unique = let i = ref 0 in fun () -> incr i; !i
123 let replace_char s c1 c2 =
124 let s2 = String.copy s in
126 for i = 0 to String.length s2 - 1 do
127 if String.unsafe_get s2 i = c1 then (
128 String.unsafe_set s2 i c2;
132 if not !r then s else s2
136 (* || c = '\f' *) || c = '\n' || c = '\r' || c = '\t' (* || c = '\v' *)
138 let triml ?(test = isspace) str =
140 let n = ref (String.length str) in
141 while !n > 0 && test str.[!i]; do
146 else String.sub str !i !n
148 let trimr ?(test = isspace) str =
149 let n = ref (String.length str) in
150 while !n > 0 && test str.[!n-1]; do
153 if !n = String.length str then str
154 else String.sub str 0 !n
156 let trim ?(test = isspace) str =
157 trimr ~test (triml ~test str)
160 let len = String.length s in
161 let sublen = String.length sub in
163 if i <= len-sublen then (
166 if s.[i+j] = sub.[j] then loop2 (j+1)
172 if r = -1 then loop (i+1) else r
178 let rec replace_str s s1 s2 =
179 let len = String.length s in
180 let sublen = String.length s1 in
184 let s' = String.sub s 0 i in
185 let s'' = String.sub s (i+sublen) (len-i-sublen) in
186 s' ^ s2 ^ replace_str s'' s1 s2
189 let rec string_split sep str =
190 let len = String.length str in
191 let seplen = String.length sep in
192 let i = find str sep in
195 let s' = String.sub str 0 i in
196 let s'' = String.sub str (i+seplen) (len-i-seplen) in
197 s' :: string_split sep s''
200 let files_equal n1 n2 =
201 let cmd = sprintf "cmp -s %s %s" (Filename.quote n1) (Filename.quote n2) in
202 match Sys.command cmd with
205 | i -> failwithf "%s: failed with error code %d" cmd i
207 let rec filter_map f = function
211 | Some y -> y :: filter_map f xs
212 | None -> filter_map f xs
214 let rec find_map f = function
215 | [] -> raise Not_found
219 | None -> find_map f xs
222 let rec loop i = function
224 | x :: xs -> f i x; loop (i+1) xs
229 let rec loop i = function
231 | x :: xs -> let r = f i x in r :: loop (i+1) xs
235 let count_chars c str =
237 for i = 0 to String.length str - 1 do
238 if c = String.unsafe_get str i then incr count
244 for i = 0 to String.length str - 1 do
245 let c = String.unsafe_get str i in
250 let map_chars f str =
251 List.map f (explode str)
253 let name_of_argt = function
254 | Pathname n | Device n | Dev_or_Path n | String n | OptString n
255 | StringList n | DeviceList n | Bool n | Int n | Int64 n
256 | FileIn n | FileOut n | BufferIn n | Key n | Pointer (_, n) -> n
258 let seq_of_test = function
259 | TestRun s | TestOutput (s, _) | TestOutputList (s, _)
260 | TestOutputListOfDevices (s, _)
261 | TestOutputInt (s, _) | TestOutputIntOp (s, _, _)
262 | TestOutputTrue s | TestOutputFalse s
263 | TestOutputLength (s, _) | TestOutputBuffer (s, _)
264 | TestOutputStruct (s, _)
265 | TestOutputFileMD5 (s, _)
266 | TestOutputDevice (s, _)
267 | TestOutputHashtable (s, _)
268 | TestLastFail s -> s
271 let str = replace_str str "\\" "\\\\" in
272 let str = replace_str str "\r" "\\r" in
273 let str = replace_str str "\n" "\\n" in
274 let str = replace_str str "\t" "\\t" in
275 let str = replace_str str "\000" "\\0" in
276 let str = replace_str str "\"" "\\\"" in
279 (* Used to memoize the result of pod2text. *)
280 let pod2text_memo_filename = "generator/.pod2text.data.version.2"
281 let pod2text_memo : ((int option * bool * bool * string * string), string list) Hashtbl.t =
283 let chan = open_in pod2text_memo_filename in
284 let v = input_value chan in
288 _ -> Hashtbl.create 13
289 let pod2text_memo_updated () =
290 let chan = open_out pod2text_memo_filename in
291 output_value chan pod2text_memo;
294 (* Useful if you need the longdesc POD text as plain text. Returns a
297 * Because this is very slow (the slowest part of autogeneration),
298 * we memoize the results.
300 let pod2text ?width ?(trim = true) ?(discard = true) name longdesc =
301 let key = width, trim, discard, name, longdesc in
302 try Hashtbl.find pod2text_memo key
304 let filename, chan = Filename.open_temp_file "gen" ".tmp" in
305 fprintf chan "=head1 %s\n\n%s\n" name longdesc;
310 sprintf "pod2text -w %d %s" width (Filename.quote filename)
312 sprintf "pod2text %s" (Filename.quote filename) in
313 let chan = open_process_in cmd in
314 let lines = ref [] in
316 let line = input_line chan in
317 if i = 1 && discard then (* discard the first line of output *)
320 let line = if trim then triml line else line in
321 lines := line :: !lines;
324 let lines = try loop 1 with End_of_file -> List.rev !lines in
326 (match close_process_in chan with
329 failwithf "pod2text: process exited with non-zero status (%d)" i
330 | WSIGNALED i | WSTOPPED i ->
331 failwithf "pod2text: process signalled or stopped by signal %d" i
333 Hashtbl.add pod2text_memo key lines;
334 pod2text_memo_updated ();
337 (* Compare two actions (for sorting). *)
338 let action_compare (n1,_,_,_,_,_,_) (n2,_,_,_,_,_,_) = compare n1 n2
341 let str = String.create n in
343 String.unsafe_set str i c
347 let spaces n = chars ' ' n