(* $Id$ *) open Printf let add_extension tbl s = let i = try String.index s ':' with Not_found -> failwith "Invalid -x argument" in let id = String.sub s 0 i in let raw_tpl = String.sub s (i+1) (String.length s - i - 1) in let cmd_tpl = Cppo_command.parse raw_tpl in if Hashtbl.mem tbl id then failwith ("Multiple definitions for extension " ^ id) else Hashtbl.add tbl id cmd_tpl let () = let extensions = Hashtbl.create 10 in let files = ref [] in let header = ref [] in let incdirs = ref [] in let out_file = ref None in let preserve_quotations = ref false in let show_exact_locations = ref false in let show_no_locations = ref false in let options = [ "-D", Arg.String (fun s -> header := ("#define " ^ s ^ "\n") :: !header), "DEF Equivalent of interpreting '#define DEF' before processing the input"; "-U", Arg.String (fun s -> header := ("#undef " ^ s ^ "\n") :: !header), "IDENT Equivalent of interpreting '#undef IDENT' before processing the input"; "-I", Arg.String (fun s -> incdirs := s :: !incdirs), "DIR Add directory DIR to the search path for included files"; "-o", Arg.String (fun s -> out_file := Some s), "FILE Output file"; "-q", Arg.Set preserve_quotations, " Identify and preserve camlp4 quotations"; "-s", Arg.Set show_exact_locations, " Output line directives pointing to the exact source location of each token, including those coming from the body of macro definitions. This behavior is off by default."; "-n", Arg.Set show_no_locations, " Do not output any line directive other than those found in the input (overrides -s)."; "-version", Arg.Unit (fun () -> print_endline Cppo_version.cppo_version; exit 0), " Print the version of the program and exit."; "-x", Arg.String (fun s -> add_extension extensions s), "NAME:CMD_TEMPLATE Define a custom preprocessor target section starting with: #ext \"NAME\" and ending with: #endext NAME must be a lowercase identifier of the form [a-z][A-Za-z0-9_]* CMD_TEMPLATE is a command template supporting the following special sequences: %F file name (unescaped; beware of potential scripting attacks) %B number of the first line %E number of the last line %% a single percent sign Filename, first line number and last line number are also available from the following environment variables: CPPO_FILE, CPPO_FIRST_LINE, CPPO_LAST_LINE. The command produced is expected to read the data lines from stdin and to write its output to stdout." ] in let msg = sprintf "\ Usage: %s [OPTIONS] [FILE1 [FILE2 ...]] Options:" Sys.argv.(0) in let add_file s = files := s :: !files in Arg.parse options add_file msg; let inputs = let preliminaries = match List.rev !header with [] -> [] | l -> let s = String.concat "" l in [ Sys.getcwd (), "<command line>", (fun () -> Lexing.from_string s), (fun () -> ()) ] in let main = match List.rev !files with [] -> [ Sys.getcwd (), "<stdin>", (fun () -> Lexing.from_channel stdin), (fun () -> ()) ] | l -> List.map ( fun file -> let ic = lazy (open_in file) in Filename.dirname file, file, (fun () -> Lexing.from_channel (Lazy.force ic)), (fun () -> close_in (Lazy.force ic)) ) l in preliminaries @ main in let env = Cppo_eval.builtin_env in let buf = Buffer.create 10_000 in let _env = try Cppo_eval.include_inputs ~extensions ~preserve_quotations: !preserve_quotations ~incdirs: (List.rev !incdirs) ~show_exact_locations: !show_exact_locations ~show_no_locations: !show_no_locations buf env inputs with Cppo_types.Cppo_error msg | Failure msg -> eprintf "Error: %s\n%!" msg; exit 1 in match !out_file with None -> print_string (Buffer.contents buf); flush stdout | Some file -> let oc = open_out file in output_string oc (Buffer.contents buf); close_out oc