2 * Copyright (C) 2009-2010 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 (* Generate a random UUID (used in tests). *)
33 let chan = open_process_in "uuidgen" in
34 let uuid = input_line chan in
35 (match close_process_in chan with
38 failwith "uuidgen: process exited with non-zero status"
39 | WSIGNALED _ | WSTOPPED _ ->
40 failwith "uuidgen: process signalled or stopped by signal"
44 type rstructs_used_t = RStructOnly | RStructListOnly | RStructAndList
46 (* Returns a list of RStruct/RStructList structs that are returned
47 * by any function. Each element of returned list is a pair:
49 * (structname, RStructOnly)
50 * == there exists function which returns RStruct (_, structname)
51 * (structname, RStructListOnly)
52 * == there exists function which returns RStructList (_, structname)
53 * (structname, RStructAndList)
54 * == there are functions returning both RStruct (_, structname)
55 * and RStructList (_, structname)
57 let rstructs_used_by functions =
58 (* ||| is a "logical OR" for rstructs_used_t *)
62 | _, RStructAndList -> RStructAndList
63 | RStructOnly, RStructListOnly
64 | RStructListOnly, RStructOnly -> RStructAndList
65 | RStructOnly, RStructOnly -> RStructOnly
66 | RStructListOnly, RStructListOnly -> RStructListOnly
69 let h = Hashtbl.create 13 in
71 (* if elem->oldv exists, update entry using ||| operator,
72 * else just add elem->newv to the hash
74 let update elem newv =
75 try let oldv = Hashtbl.find h elem in
76 Hashtbl.replace h elem (newv ||| oldv)
77 with Not_found -> Hashtbl.add h elem newv
81 fun (_, style, _, _, _, _, _) ->
83 | RStruct (_, structname) -> update structname RStructOnly
84 | RStructList (_, structname) -> update structname RStructListOnly
88 (* return key->values as a list of (key,value) *)
89 Hashtbl.fold (fun key value xs -> (key, value) :: xs) h []
91 let failwithf fs = ksprintf failwith fs
93 let unique = let i = ref 0 in fun () -> incr i; !i
95 let replace_char s c1 c2 =
96 let s2 = String.copy s in
98 for i = 0 to String.length s2 - 1 do
99 if String.unsafe_get s2 i = c1 then (
100 String.unsafe_set s2 i c2;
104 if not !r then s else s2
108 (* || c = '\f' *) || c = '\n' || c = '\r' || c = '\t' (* || c = '\v' *)
110 let triml ?(test = isspace) str =
112 let n = ref (String.length str) in
113 while !n > 0 && test str.[!i]; do
118 else String.sub str !i !n
120 let trimr ?(test = isspace) str =
121 let n = ref (String.length str) in
122 while !n > 0 && test str.[!n-1]; do
125 if !n = String.length str then str
126 else String.sub str 0 !n
128 let trim ?(test = isspace) str =
129 trimr ~test (triml ~test str)
132 let len = String.length s in
133 let sublen = String.length sub in
135 if i <= len-sublen then (
138 if s.[i+j] = sub.[j] then loop2 (j+1)
144 if r = -1 then loop (i+1) else r
150 let rec replace_str s s1 s2 =
151 let len = String.length s in
152 let sublen = String.length s1 in
156 let s' = String.sub s 0 i in
157 let s'' = String.sub s (i+sublen) (len-i-sublen) in
158 s' ^ s2 ^ replace_str s'' s1 s2
161 let rec string_split sep str =
162 let len = String.length str in
163 let seplen = String.length sep in
164 let i = find str sep in
167 let s' = String.sub str 0 i in
168 let s'' = String.sub str (i+seplen) (len-i-seplen) in
169 s' :: string_split sep s''
172 let files_equal n1 n2 =
173 let cmd = sprintf "cmp -s %s %s" (Filename.quote n1) (Filename.quote n2) in
174 match Sys.command cmd with
177 | i -> failwithf "%s: failed with error code %d" cmd i
179 let rec filter_map f = function
183 | Some y -> y :: filter_map f xs
184 | None -> filter_map f xs
186 let rec find_map f = function
187 | [] -> raise Not_found
191 | None -> find_map f xs
194 let rec loop i = function
196 | x :: xs -> f i x; loop (i+1) xs
201 let rec loop i = function
203 | x :: xs -> let r = f i x in r :: loop (i+1) xs
207 let count_chars c str =
209 for i = 0 to String.length str - 1 do
210 if c = String.unsafe_get str i then incr count
216 for i = 0 to String.length str - 1 do
217 let c = String.unsafe_get str i in
222 let map_chars f str =
223 List.map f (explode str)
225 let name_of_argt = function
226 | Pathname n | Device n | Dev_or_Path n | String n | OptString n
227 | StringList n | DeviceList n | Bool n | Int n | Int64 n
228 | FileIn n | FileOut n | BufferIn n | Key n -> n
230 let seq_of_test = function
231 | TestRun s | TestOutput (s, _) | TestOutputList (s, _)
232 | TestOutputListOfDevices (s, _)
233 | TestOutputInt (s, _) | TestOutputIntOp (s, _, _)
234 | TestOutputTrue s | TestOutputFalse s
235 | TestOutputLength (s, _) | TestOutputBuffer (s, _)
236 | TestOutputStruct (s, _)
237 | TestLastFail s -> s
240 let str = replace_str str "\r" "\\r" in
241 let str = replace_str str "\n" "\\n" in
242 let str = replace_str str "\t" "\\t" in
243 let str = replace_str str "\000" "\\0" in
246 (* Used to memoize the result of pod2text. *)
247 let pod2text_memo_filename = "generator/.pod2text.data"
248 let pod2text_memo : ((int * string * string), string list) Hashtbl.t =
250 let chan = open_in pod2text_memo_filename in
251 let v = input_value chan in
255 _ -> Hashtbl.create 13
256 let pod2text_memo_updated () =
257 let chan = open_out pod2text_memo_filename in
258 output_value chan pod2text_memo;
261 (* Useful if you need the longdesc POD text as plain text. Returns a
264 * Because this is very slow (the slowest part of autogeneration),
265 * we memoize the results.
267 let pod2text ~width name longdesc =
268 let key = width, name, longdesc in
269 try Hashtbl.find pod2text_memo key
271 let filename, chan = Filename.open_temp_file "gen" ".tmp" in
272 fprintf chan "=head1 %s\n\n%s\n" name longdesc;
274 let cmd = sprintf "pod2text -w %d %s" width (Filename.quote filename) in
275 let chan = open_process_in cmd in
276 let lines = ref [] in
278 let line = input_line chan in
279 if i = 1 then (* discard the first line of output *)
282 let line = triml line in
283 lines := line :: !lines;
286 let lines = try loop 1 with End_of_file -> List.rev !lines in
288 (match close_process_in chan with
291 failwithf "pod2text: process exited with non-zero status (%d)" i
292 | WSIGNALED i | WSTOPPED i ->
293 failwithf "pod2text: process signalled or stopped by signal %d" i
295 Hashtbl.add pod2text_memo key lines;
296 pod2text_memo_updated ();