forked from coccinelle/coccinelle
-
Notifications
You must be signed in to change notification settings - Fork 0
/
read_options.ml
131 lines (116 loc) · 4.26 KB
/
read_options.ml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
(*
* This file is part of Coccinelle, lincensed under the terms of the GPL v2.
* See copyright.txt in the Coccinelle source code for more information.
* The Coccinelle source code can be obtained at http://coccinelle.lip6.fr
*)
(* This reads Coccinelle command line arguments from a configuration file.
The configuration file is named .cocci. The only format allowed at the
moment is
[spatch]
options = --opt1 --opt2
options = --opt3 --opt4
...
Perhaps entries for other tools may be added later. Multiple options lines
are permitted only for readability. Later lines extend and may override
earlier ones.
.cocciconfig files can be placed in the user's home directory, the
directory from which spatch is called, and the directory provided with the
--dir option. The .cocciconfig file in the user's home directory is
processed first, the .cocciconfig file in the directory from which spatch
is called is processed next, and the .cocciconfig file in the directory
provided with the --dir option is processed last. In each case, the read
options extend/override the previously read ones. In all cases, the user
can extend/override the options found in the .cocciconfig files on the
command line.
Newlines, even with \, are not tolerated in attribute values *)
let get_home () = Sys.getenv "HOME"
let split_at_spaces s = Str.split (Str.regexp "[ \t]+") s
(* not very robust - may just skip some garbage lines *)
let rec read_to_header i l =
if l = "" || String.get l 0 = '#' (* comment character *)
then read_to_header i (input_line i)
else
match Str.split_delim (Str.regexp_string "[") l with
[before;after] ->
(match Str.split_delim (Str.regexp_string "]") after with
[spatch;after] -> spatch
| _ -> failwith ("unexpected entry: "^l))
| _ -> read_to_header i (input_line i)
let parse_file file =
let i = open_in file in
let options = ref [] in
let rec loop l =
let header = read_to_header i l in
match header with
"spatch" ->
let rec iloop _ =
let l = input_line i in
if l = "" || String.get l 0 = '#' (* comment character *)
then iloop()
else
(* bounded split doesn't split at = in value part *)
match Str.bounded_split (Str.regexp "[ \t]*=[ \t]*") l 2 with
[opts;new_options] ->
(match split_at_spaces opts with
["options"] ->
options := split_at_spaces new_options :: !options;
iloop()
| [other] ->
failwith
(Printf.sprintf "expected options, found %s" other)
| xs ->
failwith
("options is the only supported attribute: "^l))
| _ -> loop l in
iloop()
| _ -> failwith
"only spatch supported as a header in a .cocciconfig file" in
try loop (input_line i)
with End_of_file -> List.concat (List.rev !options)
(* ------------------------------------------------------------------------ *)
let process_arglist strings = function
spatch::rest ->
let before = [spatch] in
let after = rest in
let rec loop = function
x::opt::xs when List.mem opt strings -> loop xs
| ""::xs -> loop xs (* not sure why it would arise *)
| x::xs ->
if String.get x 0 = '-'
then loop xs
else
if Filename.check_suffix x ".cocci"
then loop xs
else
if Sys.file_exists x && Common.is_directory x
then Some x
else loop xs
| [] -> None in
(before,after,loop (List.rev rest))
| [] -> failwith "arglist should always contain the command"
(* ------------------------------------------------------------------------ *)
let check_one file =
if Sys.file_exists file
then Some (parse_file file)
else None
let unoption = function None -> [] | Some l -> l
let read_options strings arglist =
let hd = get_home() in
let cwd = Sys.getcwd() in
let home_dir_options = check_one (Printf.sprintf "%s/.cocciconfig" hd) in
let cwd_options = check_one ".cocciconfig" in
let (before,after,dir) = process_arglist strings arglist in
let dir_options =
let rec loop dir =
if dir = "/" || dir = "." || dir = hd || dir = cwd
then None
else
match check_one (Printf.sprintf "%s/.cocciconfig" dir) with
None -> loop (Filename.dirname dir)
| Some l -> Some l in
match dir with
None -> None
| Some dir -> loop dir in
before @
(unoption home_dir_options) @ (unoption cwd_options) @ (unoption dir_options)
@ after