@@ -37,55 +37,121 @@ QList< Argument > splitCommandLineArguments( const QString & argsStr )
3737 QList< Argument > args;
3838
3939 QString currentArg;
40+ currentArg.reserve ( 32 );
4041
41- bool escaped = false ;
42- bool inQuotes = false ;
43- for (qsizetype currentPos = 0 ; currentPos < argsStr.size (); ++currentPos)
42+ auto flushCurrentArg = [ ¤tArg, &args ]( bool wasQuoted )
4443 {
45- QChar currentChar = argsStr[ currentPos ];
44+ args.append ( Argument{ std::move (currentArg), wasQuoted } );
45+ currentArg.resize ( 0 ); // clear the content without freeing the allocated buffer
46+ };
4647
47- if (escaped)
48+ if constexpr (IS_WINDOWS) // parse according to the Windows cmd escaping rules
49+ {
50+ bool insideQuotes = false ;
51+ bool wasClosingQuotesChar = false ;
52+
53+ for (qsizetype currentPos = 0 ; currentPos < argsStr.size (); ++currentPos)
4854 {
49- escaped = false ;
50- currentArg += currentChar;
51- // We should handle all the special characters like '\n', '\t', '\b', but screw it, it's not needed.
55+ QChar currentChar = argsStr[ currentPos ];
56+
57+ if (insideQuotes)
58+ {
59+ if (currentChar == ' "' ) {
60+ insideQuotes = false ;
61+ wasClosingQuotesChar = true ;
62+ } else {
63+ currentArg += currentChar;
64+ }
65+ }
66+ else // not inside quotes
67+ {
68+ if (currentChar == ' "' ) {
69+ insideQuotes = true ;
70+ if (wasClosingQuotesChar) {
71+ // 2 consequent quote characters produce 1 quote character inside the quoted string
72+ currentArg += ' "' ;
73+ }
74+ } else if (currentChar == ' ' ) {
75+ if (!currentArg.isEmpty () || wasClosingQuotesChar) {
76+ flushCurrentArg ( wasClosingQuotesChar );
77+ }
78+ } else {
79+ currentArg += currentChar;
80+ }
81+ wasClosingQuotesChar = false ;
82+ }
5283 }
53- else if (inQuotes) // and not escaped
84+
85+ // We reached the end of the command line without encountering the final terminating space, flush the last word.
86+ if (!currentArg.isEmpty ())
5487 {
55- if (currentChar == ' \\ ' ) {
56- escaped = true ;
57- } else if (currentChar == ' "' ) {
58- inQuotes = false ;
59- args.append ( Argument{ std::move (currentArg), true } );
60- currentArg.clear ();
61- } else {
62- currentArg += currentChar;
63- }
88+ // accept also unterminated quoted argument
89+ flushCurrentArg ( insideQuotes || wasClosingQuotesChar );
6490 }
65- else // not escaped and not in quotes
91+ }
92+ else // parse according to the bash escaping rules
93+ {
94+ bool insideSingleQuotes = false ;
95+ bool insideDoubleQuotes = false ;
96+ bool wasClosingQuotesChar = false ;
97+ bool wasEscapeChar = false ;
98+
99+ for (qsizetype currentPos = 0 ; currentPos < argsStr.size (); ++currentPos)
66100 {
67- if (currentChar == ' \\ ' ) {
68- escaped = true ;
69- } else if (currentChar == ' "' ) {
70- inQuotes = true ;
71- if (!currentArg.isEmpty ()) {
72- args.append ( Argument{ std::move (currentArg), false } );
73- currentArg.clear ();
74- }
75- } else if (currentChar == ' ' ) {
76- if (!currentArg.isEmpty ()) {
77- args.append ( Argument{ std::move (currentArg), false } );
78- currentArg.clear ();
101+ QChar currentChar = argsStr[ currentPos ];
102+
103+ if (insideSingleQuotes)
104+ {
105+ if (currentChar == ' \' ' ) {
106+ insideSingleQuotes = false ;
107+ wasClosingQuotesChar = true ;
108+ } else {
109+ currentArg += currentChar;
79110 }
80- } else {
111+ }
112+ else if (wasEscapeChar)
113+ {
114+ // 2 consequent escape characters produce 1 escape character inside the quoted string and don't escape any further
115+ wasEscapeChar = false ;
81116 currentArg += currentChar;
117+ // We should handle all the special characters like '\n', '\t', '\b', but screw it, it's not needed.
118+ }
119+ else if (insideDoubleQuotes) // and wasn't escape char
120+ {
121+ if (currentChar == ' \\ ' ) {
122+ wasEscapeChar = true ;
123+ } else if (currentChar == ' "' ) {
124+ insideDoubleQuotes = false ;
125+ wasClosingQuotesChar = true ;
126+ } else {
127+ currentArg += currentChar;
128+ }
129+ }
130+ else // wasn't escape char and not in quotes
131+ {
132+ if (currentChar == ' \\ ' ) {
133+ wasEscapeChar = true ;
134+ } else if (currentChar == ' \' ' ) {
135+ insideSingleQuotes = true ;
136+ } else if (currentChar == ' "' ) {
137+ insideDoubleQuotes = true ;
138+ } else if (currentChar == ' ' ) {
139+ if (!currentArg.isEmpty () || wasClosingQuotesChar) {
140+ flushCurrentArg ( wasClosingQuotesChar );
141+ }
142+ } else {
143+ currentArg += currentChar;
144+ }
145+ wasClosingQuotesChar = false ;
82146 }
83147 }
84- }
85148
86- if (!currentArg.isEmpty ())
87- {
88- args.append ( Argument{ std::move (currentArg), (inQuotes && argsStr.back () != ' "' ) } );
149+ // We reached the end of the command line without encountering the final terminating space, flush the last word.
150+ if (!currentArg.isEmpty ())
151+ {
152+ // accept also unterminated quoted argument
153+ flushCurrentArg ( insideSingleQuotes || insideDoubleQuotes || wasClosingQuotesChar );
154+ }
89155 }
90156
91157 return args;
0 commit comments