aboutsummaryrefslogtreecommitdiffstats
path: root/lib/Responsefile.ml
blob: c10fe302a8273512e80a861c83fd666d40eebb98 (plain)
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
132
133
(* *********************************************************************)
(*                                                                     *)
(*              The Compcert verified compiler                         *)
(*                                                                     *)
(*          Xavier Leroy, INRIA Paris-Rocquencourt                     *)
(*        Bernhard Schommer, AbsInt Angewandte Informatik GmbH         *)
(*                                                                     *)
(*  Copyright Institut National de Recherche en Informatique et en     *)
(*  Automatique.  All rights reserved.  This file is distributed       *)
(*  under the terms of the GNU General Public License as published by  *)
(*  the Free Software Foundation, either version 2 of the License, or  *)
(*  (at your option) any later version.  This file is also distributed *)
(*  under the terms of the INRIA Non-Commercial License Agreement.     *)
(*                                                                     *)
(* *********************************************************************)


let rec singlequote ic buf =
  match input_char ic with
  | exception End_of_file -> ()
  | '\'' -> ()
  | c -> Buffer.add_char buf c; singlequote ic buf

let doublequote ic buf =
  let rec aux buf =
    match input_char ic with
    | exception End_of_file -> (* Backslash-newline is ignored. *)
        ()
    | '\"' ->
        ()
    | '\\' ->
        begin match input_char ic with
        | exception End_of_file ->    (* tolerance *)
            Buffer.add_char buf '\\'
        | '\n' ->
            aux buf
        | ('\\' | '\"') as c ->
            Buffer.add_char buf c; aux buf
        | c ->
            Buffer.add_char buf '\\'; Buffer.add_char buf c; aux buf
        end
    | c ->
        Buffer.add_char buf c; aux buf in
  aux buf

let doublequote_win ic buf =
  let rec aux_win buf n =
    match input_char ic with
    | exception End_of_file ->   (* tolerance *)
        add_backslashes n
    | '\\' ->
        aux_win buf (n+1)
    | '\"' ->
      if n land 1 = 1 then begin
        add_backslashes (n/2); Buffer.add_char buf '\"';
          aux_win buf 0
      end else begin
        add_backslashes n
      end
    | '\n' ->
        if n >= 1 then add_backslashes (n-1) else Buffer.add_char buf '\n';
        aux_win buf 0
    | c ->
        add_backslashes n; Buffer.add_char buf c; aux_win buf 0
    and add_backslashes n =
      for _i = 1 to n do Buffer.add_char buf '\\' done in
    aux_win buf 0

let doublequote = if Sys.win32 then doublequote_win else doublequote

let is_add_file file =
  String.length file > 1 && String.get file 0 = '@'

let cut_add file =
  String.sub file 1 (String.length file - 1)

let readwords file =
  let visited = ref [] in
  let rec aux file =
    if Sys.file_exists file then begin
      if List.mem file !visited then
        raise (Arg.Bad "Circular includes in response files");
      visited := file :: !visited;
      let ic = open_in_bin file in
      let buf = Buffer.create 32 in
      let words = ref [] in
      let stash inw =
        if inw then begin
          let word = Buffer.contents buf in
          if is_add_file word then
            words := (aux (cut_add word))@ !words
          else
            words := Buffer.contents buf :: !words;
          Buffer.clear buf
        end in
      let rec unquoted inw =
        match input_char ic with
        | exception End_of_file ->
            stash inw
        | ' ' | '\t' | '\r' | '\n' ->
            stash inw; unquoted false
        | '\\' ->
            begin match input_char ic with
            | exception End_of_file ->    (* tolerance; treat like \newline *)
                unquoted inw
            | '\n' ->
              unquoted inw
            | c ->
                Buffer.add_char buf c; unquoted true
            end
        | '\'' ->
            singlequote ic buf; unquoted true
        | '\"' ->
            doublequote ic buf;
            unquoted true
        | c ->
            Buffer.add_char buf c; unquoted true in
      unquoted false;
      close_in ic;
      !words
    end else [file] in
  List.rev (aux file)

let expand_responsefiles args =
  let acc = ref [] in
  for i = (Array.length args - 1) downto 0 do
    let file = args.(i) in
    if is_add_file file then
      acc := readwords (cut_add file) @ !acc
    else
      acc := file::!acc
  done;
  Array.of_list !acc