termux-app
229 строк · 9.6 Кб
1/*BEGIN_COPYRIGHT_BLOCK
2*
3* Copyright (c) 2001-2010, JavaPLT group at Rice University (drjava@rice.edu)
4* All rights reserved.
5*
6* Redistribution and use in source and binary forms, with or without
7* modification, are permitted provided that the following conditions are met:
8* * Redistributions of source code must retain the above copyright
9* notice, this list of conditions and the following disclaimer.
10* * Redistributions in binary form must reproduce the above copyright
11* notice, this list of conditions and the following disclaimer in the
12* documentation and/or other materials provided with the distribution.
13* * Neither the names of DrJava, the JavaPLT group, Rice University, nor the
14* names of its contributors may be used to endorse or promote products
15* derived from this software without specific prior written permission.
16*
17* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
21* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28*
29* This software is Open Source Initiative approved Open Source Software.
30* Open Source Initative Approved is a trademark of the Open Source Initiative.
31*
32* This file is part of DrJava. Download the current version of this project
33* from http://www.drjava.org/ or http://sourceforge.net/projects/drjava/
34*
35* END_COPYRIGHT_BLOCK*/
36
37package com.termux.shared.shell;
38
39import java.util.List;
40import java.util.LinkedList;
41
42/**
43* Utility class which can tokenize a String into a list of String arguments,
44* with behavior similar to parsing command line arguments to a program.
45* Quoted Strings are treated as single arguments, and escaped characters
46* are translated so that the tokenized arguments have the same meaning.
47* Since all methods are static, the class is declared abstract to prevent
48* instantiation.
49* @version $Id$
50*/
51public abstract class ArgumentTokenizer {
52private static final int NO_TOKEN_STATE = 0;
53private static final int NORMAL_TOKEN_STATE = 1;
54private static final int SINGLE_QUOTE_STATE = 2;
55private static final int DOUBLE_QUOTE_STATE = 3;
56
57/** Tokenizes the given String into String tokens
58* @param arguments A String containing one or more command-line style arguments to be tokenized.
59* @return A list of parsed and properly escaped arguments.
60*/
61public static List<String> tokenize(String arguments) {
62return tokenize(arguments, false);
63}
64
65/** Tokenizes the given String into String tokens.
66* @param arguments A String containing one or more command-line style arguments to be tokenized.
67* @param stringify whether or not to include escape special characters
68* @return A list of parsed and properly escaped arguments.
69*/
70public static List<String> tokenize(String arguments, boolean stringify) {
71
72LinkedList<String> argList = new LinkedList<String>();
73StringBuilder currArg = new StringBuilder();
74boolean escaped = false;
75int state = NO_TOKEN_STATE; // start in the NO_TOKEN_STATE
76int len = arguments.length();
77
78// Loop over each character in the string
79for (int i = 0; i < len; i++) {
80char c = arguments.charAt(i);
81if (escaped) {
82// Escaped state: just append the next character to the current arg.
83escaped = false;
84currArg.append(c);
85}
86else {
87switch(state) {
88case SINGLE_QUOTE_STATE:
89if (c == '\'') {
90// Seen the close quote; continue this arg until whitespace is seen
91state = NORMAL_TOKEN_STATE;
92}
93else {
94currArg.append(c);
95}
96break;
97case DOUBLE_QUOTE_STATE:
98if (c == '"') {
99// Seen the close quote; continue this arg until whitespace is seen
100state = NORMAL_TOKEN_STATE;
101}
102else if (c == '\\') {
103// Look ahead, and only escape quotes or backslashes
104i++;
105char next = arguments.charAt(i);
106if (next == '"' || next == '\\') {
107currArg.append(next);
108}
109else {
110currArg.append(c);
111currArg.append(next);
112}
113}
114else {
115currArg.append(c);
116}
117break;
118// case NORMAL_TOKEN_STATE:
119// if (Character.isWhitespace(c)) {
120// // Whitespace ends the token; start a new one
121// argList.add(currArg.toString());
122// currArg = new StringBuffer();
123// state = NO_TOKEN_STATE;
124// }
125// else if (c == '\\') {
126// // Backslash in a normal token: escape the next character
127// escaped = true;
128// }
129// else if (c == '\'') {
130// state = SINGLE_QUOTE_STATE;
131// }
132// else if (c == '"') {
133// state = DOUBLE_QUOTE_STATE;
134// }
135// else {
136// currArg.append(c);
137// }
138// break;
139case NO_TOKEN_STATE:
140case NORMAL_TOKEN_STATE:
141switch(c) {
142case '\\':
143escaped = true;
144state = NORMAL_TOKEN_STATE;
145break;
146case '\'':
147state = SINGLE_QUOTE_STATE;
148break;
149case '"':
150state = DOUBLE_QUOTE_STATE;
151break;
152default:
153if (!Character.isWhitespace(c)) {
154currArg.append(c);
155state = NORMAL_TOKEN_STATE;
156}
157else if (state == NORMAL_TOKEN_STATE) {
158// Whitespace ends the token; start a new one
159argList.add(currArg.toString());
160currArg = new StringBuilder();
161state = NO_TOKEN_STATE;
162}
163}
164break;
165default:
166throw new IllegalStateException("ArgumentTokenizer state " + state + " is invalid!");
167}
168}
169}
170
171// If we're still escaped, put in the backslash
172if (escaped) {
173currArg.append('\\');
174argList.add(currArg.toString());
175}
176// Close the last argument if we haven't yet
177else if (state != NO_TOKEN_STATE) {
178argList.add(currArg.toString());
179}
180// Format each argument if we've been told to stringify them
181if (stringify) {
182for (int i = 0; i < argList.size(); i++) {
183argList.set(i, "\"" + _escapeQuotesAndBackslashes(argList.get(i)) + "\"");
184}
185}
186return argList;
187}
188
189/** Inserts backslashes before any occurrences of a backslash or
190* quote in the given string. Also converts any special characters
191* appropriately.
192*/
193protected static String _escapeQuotesAndBackslashes(String s) {
194final StringBuilder buf = new StringBuilder(s);
195
196// Walk backwards, looking for quotes or backslashes.
197// If we see any, insert an extra backslash into the buffer at
198// the same index. (By walking backwards, the index into the buffer
199// will remain correct as we change the buffer.)
200for (int i = s.length()-1; i >= 0; i--) {
201char c = s.charAt(i);
202if ((c == '\\') || (c == '"')) {
203buf.insert(i, '\\');
204}
205// Replace any special characters with escaped versions
206else if (c == '\n') {
207buf.deleteCharAt(i);
208buf.insert(i, "\\n");
209}
210else if (c == '\t') {
211buf.deleteCharAt(i);
212buf.insert(i, "\\t");
213}
214else if (c == '\r') {
215buf.deleteCharAt(i);
216buf.insert(i, "\\r");
217}
218else if (c == '\b') {
219buf.deleteCharAt(i);
220buf.insert(i, "\\b");
221}
222else if (c == '\f') {
223buf.deleteCharAt(i);
224buf.insert(i, "\\f");
225}
226}
227return buf.toString();
228}
229}
230