Add Respfile support for soong_zip.

Sometime the size of our command line passed to soong_zip go program
exceeds the cmdline size limit. So add an RespFile support with "@" special
character prefix.

The args in the cmdline will be considered together with the
args in RespFile during soong_zip running.

Test: real tests in my local machine, and compare the
res/libphonenumber.jar before and after changes.

./cmd -o test.zip '""'-C -> [./cmd,-o,test.zip,""-C]
./cmd -o test.zip '-C -f -> [./cmd,-o,test.zip,-C -f]
./cmd -o test.zip '\"'-C -f -> [./cmd,-o,test.zip,\"-C -f]
./cmd -o test.zip '\\'-C -f -> [./cmd,-o,test.zip,\\-C -f]
./cmd -o test.zip '\a'-C -f -> [./cmd,-o,test.zip,\a-C -f]

./cmd -o test.zip \'-C -> [./cmd,-o,test.zip,'-C]
./cmd -o test.zip \\-C -> [./cmd,-o,test.zip,\-C]
./cmd -o test.zip \"-C -> [./cmd,-o,test.zip,"-C]

./cmd -o test.zip "'"-C -> [./cmd,-o,test.zip,'-C]
./cmd -o test.zip "\\"-C -f -> [./cmd,-o,test.zip,\a-C -f]
./cmd -o test.zip "\""-C -f -> [./cmd,-o,test.zip,"a-C -f]

Bug: b/72484223

Change-Id: I83c3630b70c8396c8e8a3f266244d868d754c4e8
diff --git a/zip/zip.go b/zip/zip.go
index c878a0c..b7e3764 100644
--- a/zip/zip.go
+++ b/zip/zip.go
@@ -31,6 +31,7 @@
 	"strings"
 	"sync"
 	"time"
+	"unicode"
 
 	"github.com/google/blueprint/pathtools"
 
@@ -132,6 +133,49 @@
 	WriteIfChanged           bool
 }
 
+const NOQUOTE = '\x00'
+
+func ReadRespFile(bytes []byte) []string {
+	var args []string
+	var arg []rune
+
+	isEscaping := false
+	quotingStart := NOQUOTE
+	for _, c := range string(bytes) {
+		switch {
+		case isEscaping:
+			if quotingStart == '"' {
+				if !(c == '"' || c == '\\') {
+					// '\"' or '\\' will be escaped under double quoting.
+					arg = append(arg, '\\')
+				}
+			}
+			arg = append(arg, c)
+			isEscaping = false
+		case c == '\\' && quotingStart != '\'':
+			isEscaping = true
+		case quotingStart == NOQUOTE && (c == '\'' || c == '"'):
+			quotingStart = c
+		case quotingStart != NOQUOTE && c == quotingStart:
+			quotingStart = NOQUOTE
+		case quotingStart == NOQUOTE && unicode.IsSpace(c):
+			// Current character is a space outside quotes
+			if len(arg) != 0 {
+				args = append(args, string(arg))
+			}
+			arg = arg[:0]
+		default:
+			arg = append(arg, c)
+		}
+	}
+
+	if len(arg) != 0 {
+		args = append(args, string(arg))
+	}
+
+	return args
+}
+
 func Run(args ZipArgs) (err error) {
 	if args.CpuProfileFilePath != "" {
 		f, err := os.Create(args.CpuProfileFilePath)