Coverage Report

Created: 2025-07-01 06:46

/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 = NULL;
92
0
  size_t length = 0;
93
0
  const char* pBeg = NULL;
94
0
  const char* pEnd = NULL;
95
0
  char* buffer = NULL;
96
0
  char* pOutput = NULL;
97
0
  int numArgs = 0;
98
0
  LPSTR* pArgs = NULL;
99
0
  size_t maxNumArgs = 0;
100
0
  size_t maxBufferSize = 0;
101
0
  size_t cmdLineLength = 0;
102
0
  BOOL* lpEscapedChars = NULL;
103
0
  LPSTR lpEscapedCmdLine = NULL;
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 n = 0;
122
0
    const char* pLastEnd = NULL;
123
0
    lpEscapedCmdLine = (char*)calloc(cmdLineLength + 1, sizeof(char));
124
125
0
    if (!lpEscapedCmdLine)
126
0
    {
127
0
      free(lpEscapedChars);
128
0
      return NULL;
129
0
    }
130
131
0
    p = (const char*)lpCmdLine;
132
0
    pLastEnd = (const char*)lpCmdLine;
133
0
    pOutput = (char*)lpEscapedCmdLine;
134
135
0
    while (p < &lpCmdLine[cmdLineLength])
136
0
    {
137
0
      pBeg = strstr(p, "\\\"");
138
139
0
      if (!pBeg)
140
0
      {
141
0
        length = strlen(p);
142
0
        CopyMemory(pOutput, p, length);
143
0
        pOutput += length;
144
0
        break;
145
0
      }
146
147
0
      pEnd = pBeg + 2;
148
149
0
      while (pBeg >= lpCmdLine)
150
0
      {
151
0
        if (*pBeg != '\\')
152
0
        {
153
0
          pBeg++;
154
0
          break;
155
0
        }
156
157
0
        pBeg--;
158
0
      }
159
160
0
      n = WINPR_ASSERTING_INT_CAST(size_t, ((pEnd - pBeg) - 1));
161
0
      length = WINPR_ASSERTING_INT_CAST(size_t, (pBeg - pLastEnd));
162
0
      CopyMemory(pOutput, p, length);
163
0
      pOutput += length;
164
0
      p += length;
165
166
0
      for (size_t i = 0; i < (n / 2); i++)
167
0
        *pOutput++ = '\\';
168
169
0
      p += n + 1;
170
171
0
      if ((n % 2) != 0)
172
0
        lpEscapedChars[pOutput - lpEscapedCmdLine] = TRUE;
173
174
0
      *pOutput++ = '"';
175
0
      pLastEnd = p;
176
0
    }
177
178
0
    *pOutput++ = '\0';
179
0
    lpCmdLine = (LPCSTR)lpEscapedCmdLine;
180
0
    cmdLineLength = strlen(lpCmdLine);
181
0
  }
182
183
0
  maxNumArgs = 2;
184
0
  p = (const char*)lpCmdLine;
185
186
0
  while (p < lpCmdLine + cmdLineLength)
187
0
  {
188
0
    p += strcspn(p, " \t");
189
0
    p += strspn(p, " \t");
190
0
    maxNumArgs++;
191
0
  }
192
193
0
  maxBufferSize = (maxNumArgs * (sizeof(char*))) + (cmdLineLength + 1);
194
0
  buffer = calloc(maxBufferSize, sizeof(char));
195
196
0
  if (!buffer)
197
0
  {
198
0
    free(lpEscapedCmdLine);
199
0
    free(lpEscapedChars);
200
0
    return NULL;
201
0
  }
202
203
0
  pArgs = (LPSTR*)buffer;
204
0
  pOutput = &buffer[maxNumArgs * (sizeof(char*))];
205
0
  p = (const char*)lpCmdLine;
206
207
0
  while (p < lpCmdLine + cmdLineLength)
208
0
  {
209
0
    pBeg = p;
210
211
0
    while (1)
212
0
    {
213
0
      p += strcspn(p, " \t\"\0");
214
215
0
      if ((*p != '"') || !lpEscapedChars[p - lpCmdLine])
216
0
        break;
217
218
0
      p++;
219
0
    }
220
221
0
    if (*p != '"')
222
0
    {
223
      /* no whitespace escaped with double quotes */
224
0
      length = WINPR_ASSERTING_INT_CAST(size_t, (p - pBeg));
225
0
      CopyMemory(pOutput, pBeg, length);
226
0
      pOutput[length] = '\0';
227
0
      pArgs[numArgs++] = pOutput;
228
0
      pOutput += (length + 1);
229
0
    }
230
0
    else
231
0
    {
232
0
      p++;
233
234
0
      while (1)
235
0
      {
236
0
        p += strcspn(p, "\"\0");
237
238
0
        if ((*p != '"') || !lpEscapedChars[p - lpCmdLine])
239
0
          break;
240
241
0
        p++;
242
0
      }
243
244
0
      if (*p != '"')
245
0
        WLog_ERR(TAG, "parsing error: uneven number of unescaped double quotes!");
246
247
0
      if (p[0] && p[1])
248
0
        p += 1 + strcspn(&p[1], " \t\0");
249
250
0
      pArgs[numArgs++] = pOutput;
251
252
0
      while (pBeg < p)
253
0
      {
254
0
        if (*pBeg != '"')
255
0
          *pOutput++ = *pBeg;
256
257
0
        pBeg++;
258
0
      }
259
260
0
      *pOutput++ = '\0';
261
0
    }
262
263
0
    p += strspn(p, " \t");
264
0
  }
265
266
0
  free(lpEscapedCmdLine);
267
0
  free(lpEscapedChars);
268
0
  *pNumArgs = numArgs;
269
0
  return pArgs;
270
0
}
271
272
#ifndef _WIN32
273
274
LPWSTR* CommandLineToArgvW(WINPR_ATTR_UNUSED LPCWSTR lpCmdLine, WINPR_ATTR_UNUSED int* pNumArgs)
275
0
{
276
0
  WLog_ERR("TODO", "TODO: Implement");
277
0
  return NULL;
278
0
}
279
280
#endif