Coverage Report

Created: 2023-09-25 06:56

/src/FreeRDP/winpr/libwinpr/thread/argv.c
Line
Count
Source (jump to first uncovered line)
1
/**
2
 * WinPR: Windows Portable Runtime
3
 * Process Argument Vector Functions
4
 *
5
 * Copyright 2013 Marc-Andre Moreau <marcandre.moreau@gmail.com>
6
 *
7
 * Licensed under the Apache License, Version 2.0 (the "License");
8
 * you may not use this file except in compliance with the License.
9
 * You may obtain a copy of the License at
10
 *
11
 *     http://www.apache.org/licenses/LICENSE-2.0
12
 *
13
 * Unless required by applicable law or agreed to in writing, software
14
 * distributed under the License is distributed on an "AS IS" BASIS,
15
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
 * See the License for the specific language governing permissions and
17
 * limitations under the License.
18
 */
19
20
#include <winpr/config.h>
21
22
#include <winpr/crt.h>
23
#include <winpr/handle.h>
24
25
#include <winpr/thread.h>
26
27
#ifdef WINPR_HAVE_UNISTD_H
28
#include <unistd.h>
29
#endif
30
31
#include "../log.h"
32
#define TAG WINPR_TAG("thread")
33
34
/**
35
 * CommandLineToArgvW function:
36
 * http://msdn.microsoft.com/en-us/library/windows/desktop/bb776391/
37
 *
38
 * CommandLineToArgvW has a special interpretation of backslash characters
39
 * when they are followed by a quotation mark character ("), as follows:
40
 *
41
 * 2n backslashes followed by a quotation mark produce n backslashes followed by a quotation mark.
42
 * (2n) + 1 backslashes followed by a quotation mark again produce n backslashes followed by a
43
 * quotation mark. n backslashes not followed by a quotation mark simply produce n backslashes.
44
 *
45
 * The address returned by CommandLineToArgvW is the address of the first element in an array of
46
 * LPWSTR values; the number of pointers in this array is indicated by pNumArgs. Each pointer to a
47
 * null-terminated Unicode string represents an individual argument found on the command line.
48
 *
49
 * CommandLineToArgvW allocates a block of contiguous memory for pointers to the argument strings,
50
 * and for the argument strings themselves; the calling application must free the memory used by the
51
 * argument list when it is no longer needed. To free the memory, use a single call to the LocalFree
52
 * function.
53
 */
54
55
/**
56
 * Parsing C++ Command-Line Arguments:
57
 * http://msdn.microsoft.com/en-us/library/windows/desktop/17w5ykft
58
 *
59
 * Microsoft C/C++ startup code uses the following rules when
60
 * interpreting arguments given on the operating system command line:
61
 *
62
 * Arguments are delimited by white space, which is either a space or a tab.
63
 *
64
 * The caret character (^) is not recognized as an escape character or delimiter.
65
 * The character is handled completely by the command-line parser in the operating
66
 * system before being passed to the argv array in the program.
67
 *
68
 * A string surrounded by double quotation marks ("string") is interpreted as a
69
 * single argument, regardless of white space contained within. A quoted string
70
 * can be embedded in an argument.
71
 *
72
 * A double quotation mark preceded by a backslash (\") is interpreted as a
73
 * literal double quotation mark character (").
74
 *
75
 * Backslashes are interpreted literally, unless they immediately
76
 * precede a double quotation mark.
77
 *
78
 * If an even number of backslashes is followed by a double quotation mark,
79
 * one backslash is placed in the argv array for every pair of backslashes,
80
 * and the double quotation mark is interpreted as a string delimiter.
81
 *
82
 * If an odd number of backslashes is followed by a double quotation mark,
83
 * one backslash is placed in the argv array for every pair of backslashes,
84
 * and the double quotation mark is "escaped" by the remaining backslash,
85
 * causing a literal double quotation mark (") to be placed in argv.
86
 *
87
 */
88
89
LPSTR* CommandLineToArgvA(LPCSTR lpCmdLine, int* pNumArgs)
90
0
{
91
0
  const char* p;
92
0
  size_t length;
93
0
  const char* pBeg;
94
0
  const char* pEnd;
95
0
  char* buffer;
96
0
  char* pOutput;
97
0
  int numArgs = 0;
98
0
  LPSTR* pArgs;
99
0
  size_t maxNumArgs;
100
0
  size_t maxBufferSize;
101
0
  size_t cmdLineLength;
102
0
  BOOL* lpEscapedChars;
103
0
  LPSTR lpEscapedCmdLine;
104
105
0
  if (!lpCmdLine)
106
0
    return NULL;
107
108
0
  if (!pNumArgs)
109
0
    return NULL;
110
111
0
  pArgs = NULL;
112
0
  lpEscapedCmdLine = NULL;
113
0
  cmdLineLength = strlen(lpCmdLine);
114
0
  lpEscapedChars = (BOOL*)calloc(cmdLineLength + 1, sizeof(BOOL));
115
116
0
  if (!lpEscapedChars)
117
0
    return NULL;
118
119
0
  if (strstr(lpCmdLine, "\\\""))
120
0
  {
121
0
    size_t i;
122
0
    size_t n;
123
0
    const char* pLastEnd = NULL;
124
0
    lpEscapedCmdLine = (char*)calloc(cmdLineLength + 1, sizeof(char));
125
126
0
    if (!lpEscapedCmdLine)
127
0
    {
128
0
      free(lpEscapedChars);
129
0
      return NULL;
130
0
    }
131
132
0
    p = (const char*)lpCmdLine;
133
0
    pLastEnd = (const char*)lpCmdLine;
134
0
    pOutput = (char*)lpEscapedCmdLine;
135
136
0
    while (p < &lpCmdLine[cmdLineLength])
137
0
    {
138
0
      pBeg = strstr(p, "\\\"");
139
140
0
      if (!pBeg)
141
0
      {
142
0
        length = strlen(p);
143
0
        CopyMemory(pOutput, p, length);
144
0
        pOutput += length;
145
0
        break;
146
0
      }
147
148
0
      pEnd = pBeg + 2;
149
150
0
      while (pBeg >= lpCmdLine)
151
0
      {
152
0
        if (*pBeg != '\\')
153
0
        {
154
0
          pBeg++;
155
0
          break;
156
0
        }
157
158
0
        pBeg--;
159
0
      }
160
161
0
      n = ((pEnd - pBeg) - 1);
162
0
      length = (pBeg - pLastEnd);
163
0
      CopyMemory(pOutput, p, length);
164
0
      pOutput += length;
165
0
      p += length;
166
167
0
      for (i = 0; i < (n / 2); i++)
168
0
        *pOutput++ = '\\';
169
170
0
      p += n + 1;
171
172
0
      if ((n % 2) != 0)
173
0
        lpEscapedChars[pOutput - lpEscapedCmdLine] = TRUE;
174
175
0
      *pOutput++ = '"';
176
0
      pLastEnd = p;
177
0
    }
178
179
0
    *pOutput++ = '\0';
180
0
    lpCmdLine = (LPCSTR)lpEscapedCmdLine;
181
0
    cmdLineLength = strlen(lpCmdLine);
182
0
  }
183
184
0
  maxNumArgs = 2;
185
0
  p = (const char*)lpCmdLine;
186
187
0
  while (p < lpCmdLine + cmdLineLength)
188
0
  {
189
0
    p += strcspn(p, " \t");
190
0
    p += strspn(p, " \t");
191
0
    maxNumArgs++;
192
0
  }
193
194
0
  maxBufferSize = (maxNumArgs * (sizeof(char*))) + (cmdLineLength + 1);
195
0
  buffer = calloc(maxBufferSize, sizeof(char));
196
197
0
  if (!buffer)
198
0
  {
199
0
    free(lpEscapedCmdLine);
200
0
    free(lpEscapedChars);
201
0
    return NULL;
202
0
  }
203
204
0
  pArgs = (LPSTR*)buffer;
205
0
  pOutput = (char*)&buffer[maxNumArgs * (sizeof(char*))];
206
0
  p = (const char*)lpCmdLine;
207
208
0
  while (p < lpCmdLine + cmdLineLength)
209
0
  {
210
0
    pBeg = p;
211
212
0
    while (1)
213
0
    {
214
0
      p += strcspn(p, " \t\"\0");
215
216
0
      if ((*p != '"') || !lpEscapedChars[p - lpCmdLine])
217
0
        break;
218
219
0
      p++;
220
0
    }
221
222
0
    if (*p != '"')
223
0
    {
224
      /* no whitespace escaped with double quotes */
225
0
      length = (p - pBeg);
226
0
      CopyMemory(pOutput, pBeg, length);
227
0
      pOutput[length] = '\0';
228
0
      pArgs[numArgs++] = pOutput;
229
0
      pOutput += (length + 1);
230
0
    }
231
0
    else
232
0
    {
233
0
      p++;
234
235
0
      while (1)
236
0
      {
237
0
        p += strcspn(p, "\"\0");
238
239
0
        if ((*p != '"') || !lpEscapedChars[p - lpCmdLine])
240
0
          break;
241
242
0
        p++;
243
0
      }
244
245
0
      if (*p != '"')
246
0
        WLog_ERR(TAG, "parsing error: uneven number of unescaped double quotes!");
247
248
0
      if (*p && *(++p))
249
0
        p += strcspn(p, " \t\0");
250
251
0
      pArgs[numArgs++] = pOutput;
252
253
0
      while (pBeg < p)
254
0
      {
255
0
        if (*pBeg != '"')
256
0
          *pOutput++ = *pBeg;
257
258
0
        pBeg++;
259
0
      }
260
261
0
      *pOutput++ = '\0';
262
0
    }
263
264
0
    p += strspn(p, " \t");
265
0
  }
266
267
0
  free(lpEscapedCmdLine);
268
0
  free(lpEscapedChars);
269
0
  *pNumArgs = numArgs;
270
0
  return pArgs;
271
0
}
272
273
#ifndef _WIN32
274
275
LPWSTR* CommandLineToArgvW(LPCWSTR lpCmdLine, int* pNumArgs)
276
0
{
277
0
  return NULL;
278
0
}
279
280
#endif