blob: 525cb7722725440ed1b8444b1db72c14b8403b3b [file] [log] [blame]
apellbcb3c572017-09-20 22:02:20 +02001// Copyright 2017 The Bazel Authors. All rights reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14package com.google.devtools.common.options;
15
16import static java.nio.charset.StandardCharsets.UTF_8;
17
18import java.io.IOException;
19import java.io.PushbackReader;
20import java.io.Reader;
21import java.nio.file.FileSystem;
22import java.nio.file.Files;
23import java.nio.file.Path;
24import java.util.ArrayList;
25import java.util.List;
26
27/**
28 * A {@link ParamsFilePreProcessor} that processes a parameter file using the {@code
29 * com.google.devtools.build.lib.actions.ParameterFile.ParameterFileType.SHELL_QUOTED} format. This
30 * format assumes each parameter is separated by whitespace and is quoted using singe quotes
31 * ({@code '}) if it contains any special characters or is an empty string.
32 */
33public class ShellQuotedParamsFilePreProcessor extends ParamsFilePreProcessor {
34
35 public ShellQuotedParamsFilePreProcessor(FileSystem fs) {
36 super(fs);
37 }
38
39 @Override
40 protected List<String> parse(Path paramsFile) throws IOException {
41 List<String> args = new ArrayList<>();
42 try (ShellQuotedReader reader =
43 new ShellQuotedReader(Files.newBufferedReader(paramsFile, UTF_8))) {
44 String arg;
45 while ((arg = reader.readArg()) != null) {
46 args.add(arg);
47 }
48 }
49 return args;
50 }
51
52 private static class ShellQuotedReader implements AutoCloseable {
53
54 private final PushbackReader reader;
55 private int position = -1;
56
57 public ShellQuotedReader(Reader reader) {
58 this.reader = new PushbackReader(reader, 10);
59 }
60
61 private char read() throws IOException {
62 int value = reader.read();
63 position++;
64 return (char) value;
65 }
66
67 private void unread(char value) throws IOException {
68 reader.unread(value);
69 position--;
70 }
71
72 private boolean hasNext() throws IOException {
73 char value = read();
74 boolean hasNext = value != (char) -1;
75 unread(value);
76 return hasNext;
77 }
78
79 @Override
80 public void close() throws IOException {
81 reader.close();
82 }
83
84 public String readArg() throws IOException {
85 if (!hasNext()) {
86 return null;
87 }
88
89 StringBuilder arg = new StringBuilder();
90
91 int quoteStart = -1;
92 boolean quoted = false;
93 char current;
94
95 while ((current = read()) != (char) -1) {
96 if (quoted) {
97 if (current == '\'') {
98 StringBuilder escapedQuoteRemainder =
99 new StringBuilder().append(read()).append(read()).append(read());
100 if (escapedQuoteRemainder.toString().equals("\\''")) {
101 arg.append("'");
102 } else {
103 for (char c : escapedQuoteRemainder.reverse().toString().toCharArray()) {
104 unread(c);
105 }
106 quoted = false;
107 quoteStart = -1;
108 }
109 } else {
110 arg.append(current);
111 }
112 } else {
113 if (current == '\'') {
114 quoted = true;
115 quoteStart = position;
116 } else if (current == '\r') {
117 char next = read();
118 if (next == '\n') {
119 return arg.toString();
120 } else {
121 unread(next);
122 return arg.toString();
123 }
124 } else if (Character.isWhitespace(current)) {
125 return arg.toString();
126 } else {
127 arg.append(current);
128 }
129 }
130 }
131 if (quoted) {
132 throw new IOException(
133 String.format(UNFINISHED_QUOTE_MESSAGE_FORMAT, "'", quoteStart));
134 }
135 return arg.toString();
136 }
137 }
138}