xref: /aosp_15_r20/build/soong/response/response.go (revision 333d2b3687b3a337dbcca9d65000bca186795e39)
1*333d2b36SAndroid Build Coastguard Worker// Copyright 2021 Google Inc. All rights reserved.
2*333d2b36SAndroid Build Coastguard Worker//
3*333d2b36SAndroid Build Coastguard Worker// Licensed under the Apache License, Version 2.0 (the "License");
4*333d2b36SAndroid Build Coastguard Worker// you may not use this file except in compliance with the License.
5*333d2b36SAndroid Build Coastguard Worker// You may obtain a copy of the License at
6*333d2b36SAndroid Build Coastguard Worker//
7*333d2b36SAndroid Build Coastguard Worker//     http://www.apache.org/licenses/LICENSE-2.0
8*333d2b36SAndroid Build Coastguard Worker//
9*333d2b36SAndroid Build Coastguard Worker// Unless required by applicable law or agreed to in writing, software
10*333d2b36SAndroid Build Coastguard Worker// distributed under the License is distributed on an "AS IS" BASIS,
11*333d2b36SAndroid Build Coastguard Worker// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*333d2b36SAndroid Build Coastguard Worker// See the License for the specific language governing permissions and
13*333d2b36SAndroid Build Coastguard Worker// limitations under the License.
14*333d2b36SAndroid Build Coastguard Worker
15*333d2b36SAndroid Build Coastguard Workerpackage response
16*333d2b36SAndroid Build Coastguard Worker
17*333d2b36SAndroid Build Coastguard Workerimport (
18*333d2b36SAndroid Build Coastguard Worker	"io"
19*333d2b36SAndroid Build Coastguard Worker	"io/ioutil"
20*333d2b36SAndroid Build Coastguard Worker	"strings"
21*333d2b36SAndroid Build Coastguard Worker	"unicode"
22*333d2b36SAndroid Build Coastguard Worker)
23*333d2b36SAndroid Build Coastguard Worker
24*333d2b36SAndroid Build Coastguard Workerconst noQuote = '\x00'
25*333d2b36SAndroid Build Coastguard Worker
26*333d2b36SAndroid Build Coastguard Worker// ReadRspFile reads a file in Ninja's response file format and returns its contents.
27*333d2b36SAndroid Build Coastguard Workerfunc ReadRspFile(r io.Reader) ([]string, error) {
28*333d2b36SAndroid Build Coastguard Worker	var files []string
29*333d2b36SAndroid Build Coastguard Worker	var file []byte
30*333d2b36SAndroid Build Coastguard Worker
31*333d2b36SAndroid Build Coastguard Worker	buf, err := ioutil.ReadAll(r)
32*333d2b36SAndroid Build Coastguard Worker	if err != nil {
33*333d2b36SAndroid Build Coastguard Worker		return nil, err
34*333d2b36SAndroid Build Coastguard Worker	}
35*333d2b36SAndroid Build Coastguard Worker
36*333d2b36SAndroid Build Coastguard Worker	isEscaping := false
37*333d2b36SAndroid Build Coastguard Worker	quotingStart := byte(noQuote)
38*333d2b36SAndroid Build Coastguard Worker	for _, c := range buf {
39*333d2b36SAndroid Build Coastguard Worker		switch {
40*333d2b36SAndroid Build Coastguard Worker		case isEscaping:
41*333d2b36SAndroid Build Coastguard Worker			if quotingStart == '"' {
42*333d2b36SAndroid Build Coastguard Worker				if !(c == '"' || c == '\\') {
43*333d2b36SAndroid Build Coastguard Worker					// '\"' or '\\' will be escaped under double quoting.
44*333d2b36SAndroid Build Coastguard Worker					file = append(file, '\\')
45*333d2b36SAndroid Build Coastguard Worker				}
46*333d2b36SAndroid Build Coastguard Worker			}
47*333d2b36SAndroid Build Coastguard Worker			file = append(file, c)
48*333d2b36SAndroid Build Coastguard Worker			isEscaping = false
49*333d2b36SAndroid Build Coastguard Worker		case c == '\\' && quotingStart != '\'':
50*333d2b36SAndroid Build Coastguard Worker			isEscaping = true
51*333d2b36SAndroid Build Coastguard Worker		case quotingStart == noQuote && (c == '\'' || c == '"'):
52*333d2b36SAndroid Build Coastguard Worker			quotingStart = c
53*333d2b36SAndroid Build Coastguard Worker		case quotingStart != noQuote && c == quotingStart:
54*333d2b36SAndroid Build Coastguard Worker			quotingStart = noQuote
55*333d2b36SAndroid Build Coastguard Worker		case quotingStart == noQuote && unicode.IsSpace(rune(c)):
56*333d2b36SAndroid Build Coastguard Worker			// Current character is a space outside quotes
57*333d2b36SAndroid Build Coastguard Worker			if len(file) != 0 {
58*333d2b36SAndroid Build Coastguard Worker				files = append(files, string(file))
59*333d2b36SAndroid Build Coastguard Worker			}
60*333d2b36SAndroid Build Coastguard Worker			file = file[:0]
61*333d2b36SAndroid Build Coastguard Worker		default:
62*333d2b36SAndroid Build Coastguard Worker			file = append(file, c)
63*333d2b36SAndroid Build Coastguard Worker		}
64*333d2b36SAndroid Build Coastguard Worker	}
65*333d2b36SAndroid Build Coastguard Worker
66*333d2b36SAndroid Build Coastguard Worker	if len(file) != 0 {
67*333d2b36SAndroid Build Coastguard Worker		files = append(files, string(file))
68*333d2b36SAndroid Build Coastguard Worker	}
69*333d2b36SAndroid Build Coastguard Worker
70*333d2b36SAndroid Build Coastguard Worker	return files, nil
71*333d2b36SAndroid Build Coastguard Worker}
72*333d2b36SAndroid Build Coastguard Worker
73*333d2b36SAndroid Build Coastguard Workerfunc rspUnsafeChar(r rune) bool {
74*333d2b36SAndroid Build Coastguard Worker	switch {
75*333d2b36SAndroid Build Coastguard Worker	case 'A' <= r && r <= 'Z',
76*333d2b36SAndroid Build Coastguard Worker		'a' <= r && r <= 'z',
77*333d2b36SAndroid Build Coastguard Worker		'0' <= r && r <= '9',
78*333d2b36SAndroid Build Coastguard Worker		r == '_',
79*333d2b36SAndroid Build Coastguard Worker		r == '+',
80*333d2b36SAndroid Build Coastguard Worker		r == '-',
81*333d2b36SAndroid Build Coastguard Worker		r == '.',
82*333d2b36SAndroid Build Coastguard Worker		r == '/':
83*333d2b36SAndroid Build Coastguard Worker		return false
84*333d2b36SAndroid Build Coastguard Worker	default:
85*333d2b36SAndroid Build Coastguard Worker		return true
86*333d2b36SAndroid Build Coastguard Worker	}
87*333d2b36SAndroid Build Coastguard Worker}
88*333d2b36SAndroid Build Coastguard Worker
89*333d2b36SAndroid Build Coastguard Workervar rspEscaper = strings.NewReplacer(`'`, `'\''`)
90*333d2b36SAndroid Build Coastguard Worker
91*333d2b36SAndroid Build Coastguard Worker// WriteRspFile writes a list of files to a file in Ninja's response file format.
92*333d2b36SAndroid Build Coastguard Workerfunc WriteRspFile(w io.Writer, files []string) error {
93*333d2b36SAndroid Build Coastguard Worker	for i, f := range files {
94*333d2b36SAndroid Build Coastguard Worker		if i != 0 {
95*333d2b36SAndroid Build Coastguard Worker			_, err := io.WriteString(w, " ")
96*333d2b36SAndroid Build Coastguard Worker			if err != nil {
97*333d2b36SAndroid Build Coastguard Worker				return err
98*333d2b36SAndroid Build Coastguard Worker			}
99*333d2b36SAndroid Build Coastguard Worker		}
100*333d2b36SAndroid Build Coastguard Worker
101*333d2b36SAndroid Build Coastguard Worker		if strings.IndexFunc(f, rspUnsafeChar) != -1 {
102*333d2b36SAndroid Build Coastguard Worker			f = `'` + rspEscaper.Replace(f) + `'`
103*333d2b36SAndroid Build Coastguard Worker		}
104*333d2b36SAndroid Build Coastguard Worker
105*333d2b36SAndroid Build Coastguard Worker		_, err := io.WriteString(w, f)
106*333d2b36SAndroid Build Coastguard Worker		if err != nil {
107*333d2b36SAndroid Build Coastguard Worker			return err
108*333d2b36SAndroid Build Coastguard Worker		}
109*333d2b36SAndroid Build Coastguard Worker	}
110*333d2b36SAndroid Build Coastguard Worker
111*333d2b36SAndroid Build Coastguard Worker	return nil
112*333d2b36SAndroid Build Coastguard Worker}
113