2 * WinPR: Windows Portable Runtime
5 * Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
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
11 * http://www.apache.org/licenses/LICENSE-2.0
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.
24 #include <winpr/crt.h>
26 #include <winpr/cmdline.h>
29 * Command-line syntax: some basic concepts:
30 * https://pythonconquerstheuniverse.wordpress.com/2010/07/25/command-line-syntax-some-basic-concepts/
34 * Remote Desktop Connection Usage
36 * mstsc [<connection file>] [/v:<server[:port]>] [/admin] [/f[ullscreen]]
37 * [/w:<width>] [/h:<height>] [/public] | [/span] [/multimon] [/migrate]
38 * [/edit "connection file"] [/?]
40 * "connection file" -- Specifies the name of an .RDP for the connection.
42 * /v:<server[:port]> -- Specifies the remote computer to which you want
45 * /admin -- Connects you to the session for administering a server.
47 * /f -- Starts Remote Desktop in full-screen mode.
49 * /w:<width> -- Specifies the width of the Remote Desktop window.
51 * /h:<height> -- Specifies the height of the Remote Desktop window.
53 * /public -- Runs Remote Desktop in public mode.
55 * /span -- Matches the remote desktop width and height with the local
56 * virtual desktop, spanning across multiple monitors if necessary. To
57 * span across monitors, the monitors must be arranged to form a
60 * /multimon -- Configures the remote desktop session layout to
61 * be identical to the current client-side configuration.
63 * /edit -- Opens the specified .RDP connection file for editing.
65 * /migrate -- Migrates legacy connection files that were created with
66 * Client Connection Manager to new .RDP connection files.
68 * /? -- Lists these parameters.
72 * Command-Line Syntax:
74 * <sigil><keyword><separator><value>
76 * <sigil>: '/' or '-' or ('+' | '-')
78 * <keyword>: option, named argument, flag
80 * <separator>: ':' or '='
82 * <value>: argument value
86 int CommandLineParseArgumentsA(int argc, LPCSTR* argv, COMMAND_LINE_ARGUMENT_A* options, DWORD flags,
87 void* context, COMMAND_LINE_PRE_FILTER_FN_A preFilter, COMMAND_LINE_POST_FILTER_FN_A postFilter)
100 int separator_length;
111 return COMMAND_LINE_STATUS_PRINT_HELP;
113 for (i = 1; i < argc; i++)
119 if (preFilter(context, i, argc, argv) < 0)
120 return COMMAND_LINE_ERROR;
125 sigil = (char*) &argv[i][sigil_index];
126 length = strlen(argv[i]);
128 if ((sigil[0] == '/') && (flags & COMMAND_LINE_SIGIL_SLASH))
132 else if ((sigil[0] == '-') && (flags & COMMAND_LINE_SIGIL_DASH))
138 if ((sigil[1] == '-') && (flags & COMMAND_LINE_SIGIL_DOUBLE_DASH))
142 else if ((sigil[0] == '+') && (flags & COMMAND_LINE_SIGIL_PLUS_MINUS))
146 else if ((sigil[0] == '-') && (flags & COMMAND_LINE_SIGIL_PLUS_MINUS))
150 else if (flags & COMMAND_LINE_SIGIL_NONE)
159 if ((sigil_length > 0) || (flags & COMMAND_LINE_SIGIL_NONE))
161 if (length < (sigil_length + 1))
162 return COMMAND_LINE_ERROR_NO_KEYWORD;
164 keyword_index = sigil_index + sigil_length;
165 keyword = (char*) &argv[i][keyword_index];
169 if (flags & COMMAND_LINE_SIGIL_ENABLE_DISABLE)
171 if (strncmp(keyword, "enable-", 7) == 0)
175 keyword = (char*) &argv[i][keyword_index];
177 else if (strncmp(keyword, "disable-", 8) == 0)
181 keyword = (char*) &argv[i][keyword_index];
187 if ((flags & COMMAND_LINE_SEPARATOR_COLON) && (!separator))
188 separator = strchr(keyword, ':');
190 if ((flags & COMMAND_LINE_SEPARATOR_EQUAL) && (!separator))
191 separator = strchr(keyword, '=');
195 separator_length = 1;
196 separator_index = (separator - argv[i]);
198 keyword_length = (separator - keyword);
200 value_index = separator_index + separator_length;
201 value = (char*) &argv[i][value_index];
202 value_length = (length - value_index);
206 separator_length = 0;
207 separator_index = -1;
208 keyword_length = (length - keyword_index);
215 for (j = 0; options[j].Name != NULL; j++)
219 if (strncmp(options[j].Name, keyword, keyword_length) == 0)
221 if (strlen(options[j].Name) == keyword_length)
225 if ((!match) && (options[j].Alias != NULL))
227 if (strncmp(options[j].Alias, keyword, keyword_length) == 0)
229 if (strlen(options[j].Alias) == keyword_length)
237 options[j].Index = index;
239 if ((flags & COMMAND_LINE_SEPARATOR_SPACE) && ((i + 1) < argc))
241 if ((options[j].Flags & COMMAND_LINE_VALUE_REQUIRED) ||
242 (options[j].Flags & COMMAND_LINE_VALUE_OPTIONAL))
246 length = strlen(argv[i]);
248 value = (char*) &argv[i][value_index];
249 value_length = (length - value_index);
253 if (!(flags & COMMAND_LINE_SEPARATOR_SPACE))
255 if (value && (options[j].Flags & COMMAND_LINE_VALUE_FLAG))
256 return COMMAND_LINE_ERROR_UNEXPECTED_VALUE;
260 if (value && (options[j].Flags & COMMAND_LINE_VALUE_FLAG))
269 if (!value && (options[j].Flags & COMMAND_LINE_VALUE_REQUIRED))
270 return COMMAND_LINE_ERROR_MISSING_VALUE;
272 options[j].Flags |= COMMAND_LINE_ARGUMENT_PRESENT;
276 options[j].Value = value;
277 options[j].Flags |= COMMAND_LINE_VALUE_PRESENT;
281 if (options[j].Flags & COMMAND_LINE_VALUE_FLAG)
283 options[j].Value = (LPSTR) 1;
284 options[j].Flags |= COMMAND_LINE_VALUE_PRESENT;
286 else if (options[j].Flags & COMMAND_LINE_VALUE_BOOL)
288 if (flags & COMMAND_LINE_SIGIL_ENABLE_DISABLE)
291 options[j].Value = BoolValueTrue;
293 options[j].Value = BoolValueFalse;
295 options[j].Value = BoolValueTrue;
300 options[j].Value = BoolValueTrue;
301 else if (sigil[0] == '-')
302 options[j].Value = BoolValueFalse;
304 options[j].Value = BoolValueTrue;
307 options[j].Flags |= COMMAND_LINE_VALUE_PRESENT;
312 postFilter(context, &options[j]);
314 if (options[j].Flags & COMMAND_LINE_PRINT)
315 return COMMAND_LINE_STATUS_PRINT;
316 else if (options[j].Flags & COMMAND_LINE_PRINT_HELP)
317 return COMMAND_LINE_STATUS_PRINT_HELP;
318 else if (options[j].Flags & COMMAND_LINE_PRINT_VERSION)
319 return COMMAND_LINE_STATUS_PRINT_VERSION;
327 int CommandLineParseArgumentsW(int argc, LPCWSTR* argv, COMMAND_LINE_ARGUMENT_W* options, DWORD flags,
328 void* context, COMMAND_LINE_PRE_FILTER_FN_W preFilter, COMMAND_LINE_POST_FILTER_FN_W postFilter)
333 int CommandLineClearArgumentsA(COMMAND_LINE_ARGUMENT_A* options)
337 for (i = 0; options[i].Name != NULL; i++)
339 options[i].Flags &= COMMAND_LINE_INPUT_FLAG_MASK;
340 options[i].Value = NULL;
346 int CommandLineClearArgumentsW(COMMAND_LINE_ARGUMENT_W* options)
350 for (i = 0; options[i].Name != NULL; i++)
352 options[i].Flags &= COMMAND_LINE_INPUT_FLAG_MASK;
353 options[i].Value = NULL;
359 COMMAND_LINE_ARGUMENT_A* CommandLineFindArgumentA(COMMAND_LINE_ARGUMENT_A* options, LPCSTR Name)
363 for (i = 0; options[i].Name != NULL; i++)
365 if (strcmp(options[i].Name, Name) == 0)
368 if (options[i].Alias != NULL)
370 if (strcmp(options[i].Alias, Name) == 0)
378 COMMAND_LINE_ARGUMENT_W* CommandLineFindArgumentW(COMMAND_LINE_ARGUMENT_W* options, LPCWSTR Name)
382 for (i = 0; options[i].Name != NULL; i++)
384 if (_wcscmp(options[i].Name, Name) == 0)
387 if (options[i].Alias != NULL)
389 if (_wcscmp(options[i].Alias, Name) == 0)
397 COMMAND_LINE_ARGUMENT_A* CommandLineFindNextArgumentA(COMMAND_LINE_ARGUMENT_A* argument)
399 COMMAND_LINE_ARGUMENT_A* nextArgument;
401 nextArgument = &argument[1];
403 if (nextArgument->Name == NULL)