libfreerdp-client: introduce CLI compatibility layer and migration assistant
[platform/upstream/freerdp.git] / winpr / libwinpr / utils / cmdline.c
1 /**
2  * WinPR: Windows Portable Runtime
3  * Command-Line Utils
4  *
5  * Copyright 2012 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 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23
24 #include <winpr/crt.h>
25
26 #include <winpr/cmdline.h>
27
28 /**
29  * Command-line syntax: some basic concepts:
30  * https://pythonconquerstheuniverse.wordpress.com/2010/07/25/command-line-syntax-some-basic-concepts/
31  */
32
33 /**
34  * Remote Desktop Connection Usage
35  *
36  * mstsc [<connection file>] [/v:<server[:port]>] [/admin] [/f[ullscreen]]
37  * [/w:<width>] [/h:<height>] [/public] | [/span] [/multimon] [/migrate]
38  * [/edit "connection file"] [/?]
39  *
40  * "connection file" -- Specifies the name of an .RDP for the connection.
41  *
42  * /v:<server[:port]> -- Specifies the remote computer to which you want
43  * to connect.
44  *
45  * /admin -- Connects you to the session for administering a server.
46  *
47  * /f -- Starts Remote Desktop in full-screen mode.
48  *
49  * /w:<width> -- Specifies the width of the Remote Desktop window.
50  *
51  * /h:<height> -- Specifies the height of the Remote Desktop window.
52  *
53  * /public -- Runs Remote Desktop in public mode.
54  *
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
58  * rectangle.
59  *
60  * /multimon -- Configures the remote desktop session layout to
61  * be identical to the current client-side configuration.
62  *
63  * /edit -- Opens the specified .RDP connection file for editing.
64  *
65  * /migrate -- Migrates legacy connection files that were created with
66  * Client Connection Manager to new .RDP connection files.
67  *
68  * /? -- Lists these parameters.
69  */
70
71 /**
72  * Command-Line Syntax:
73  *
74  * <sigil><keyword><separator><value>
75  *
76  * <sigil>: '/' or '-' or ('+' | '-')
77  *
78  * <keyword>: option, named argument, flag
79  *
80  * <separator>: ':' or '='
81  *
82  * <value>: argument value
83  *
84  */
85
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)
88 {
89         int i, j;
90         int length;
91         int index;
92         BOOL match;
93         char* sigil;
94         int sigil_length;
95         int sigil_index;
96         char* keyword;
97         int keyword_length;
98         int keyword_index;
99         char* separator;
100         int separator_length;
101         int separator_index;
102         char* value;
103         int value_length;
104         int value_index;
105         int toggle;
106
107         if (!argv)
108                 return 0;
109
110         if (argc == 1)
111                 return COMMAND_LINE_STATUS_PRINT_HELP;
112
113         for (i = 1; i < argc; i++)
114         {
115                 index = i;
116
117                 if (preFilter)
118                 {
119                         if (preFilter(context, i, argc, argv) < 0)
120                                 return COMMAND_LINE_ERROR;
121                 }
122
123                 sigil_index = 0;
124                 sigil_length = 0;
125                 sigil = (char*) &argv[i][sigil_index];
126                 length = strlen(argv[i]);
127
128                 if ((sigil[0] == '/') && (flags & COMMAND_LINE_SIGIL_SLASH))
129                 {
130                         sigil_length = 1;
131                 }
132                 else if ((sigil[0] == '-') && (flags & COMMAND_LINE_SIGIL_DASH))
133                 {
134                         sigil_length = 1;
135
136                         if (length > 2)
137                         {
138                                 if ((sigil[1] == '-') && (flags & COMMAND_LINE_SIGIL_DOUBLE_DASH))
139                                         sigil_length = 2;
140                         }
141                 }
142                 else if ((sigil[0] == '+') && (flags & COMMAND_LINE_SIGIL_PLUS_MINUS))
143                 {
144                         sigil_length = 1;
145                 }
146                 else if ((sigil[0] == '-') && (flags & COMMAND_LINE_SIGIL_PLUS_MINUS))
147                 {
148                         sigil_length = 1;
149                 }
150                 else if (flags & COMMAND_LINE_SIGIL_NONE)
151                 {
152                         sigil_length = 0;
153                 }
154                 else
155                 {
156                         continue;
157                 }
158
159                 if ((sigil_length > 0) || (flags & COMMAND_LINE_SIGIL_NONE))
160                 {
161                         if (length < (sigil_length + 1))
162                                 return COMMAND_LINE_ERROR_NO_KEYWORD;
163
164                         keyword_index = sigil_index + sigil_length;
165                         keyword = (char*) &argv[i][keyword_index];
166
167                         toggle = -1;
168
169                         if (flags & COMMAND_LINE_SIGIL_ENABLE_DISABLE)
170                         {
171                                 if (strncmp(keyword, "enable-", 7) == 0)
172                                 {
173                                         toggle = TRUE;
174                                         keyword_index += 7;
175                                         keyword = (char*) &argv[i][keyword_index];
176                                 }
177                                 else if (strncmp(keyword, "disable-", 8) == 0)
178                                 {
179                                         toggle = FALSE;
180                                         keyword_index += 8;
181                                         keyword = (char*) &argv[i][keyword_index];
182                                 }
183                         }
184
185                         separator = NULL;
186
187                         if ((flags & COMMAND_LINE_SEPARATOR_COLON) && (!separator))
188                                 separator = strchr(keyword, ':');
189
190                         if ((flags & COMMAND_LINE_SEPARATOR_EQUAL) && (!separator))
191                                 separator = strchr(keyword, '=');
192
193                         if (separator)
194                         {
195                                 separator_length = 1;
196                                 separator_index = (separator - argv[i]);
197
198                                 keyword_length = (separator - keyword);
199
200                                 value_index = separator_index + separator_length;
201                                 value = (char*) &argv[i][value_index];
202                                 value_length = (length - value_index);
203                         }
204                         else
205                         {
206                                 separator_length = 0;
207                                 separator_index = -1;
208                                 keyword_length = (length - keyword_index);
209
210                                 value_index = -1;
211                                 value = NULL;
212                                 value_length = 0;
213                         }
214
215                         for (j = 0; options[j].Name != NULL; j++)
216                         {
217                                 match = FALSE;
218
219                                 if (strncmp(options[j].Name, keyword, keyword_length) == 0)
220                                 {
221                                         if (strlen(options[j].Name) == keyword_length)
222                                                 match = TRUE;
223                                 }
224
225                                 if ((!match) && (options[j].Alias != NULL))
226                                 {
227                                         if (strncmp(options[j].Alias, keyword, keyword_length) == 0)
228                                         {
229                                                 if (strlen(options[j].Alias) == keyword_length)
230                                                         match = TRUE;
231                                         }
232                                 }
233
234                                 if (!match)
235                                         continue;
236
237                                 options[j].Index = index;
238
239                                 if ((flags & COMMAND_LINE_SEPARATOR_SPACE) && ((i + 1) < argc))
240                                 {
241                                         if ((options[j].Flags & COMMAND_LINE_VALUE_REQUIRED) ||
242                                                 (options[j].Flags & COMMAND_LINE_VALUE_OPTIONAL))
243                                         {
244                                                 i++;
245                                                 value_index = 0;
246                                                 length = strlen(argv[i]);
247
248                                                 value = (char*) &argv[i][value_index];
249                                                 value_length = (length - value_index);
250                                         }
251                                 }
252
253                                 if (!(flags & COMMAND_LINE_SEPARATOR_SPACE))
254                                 {
255                                         if (value && (options[j].Flags & COMMAND_LINE_VALUE_FLAG))
256                                                 return COMMAND_LINE_ERROR_UNEXPECTED_VALUE;
257                                 }
258                                 else
259                                 {
260                                         if (value && (options[j].Flags & COMMAND_LINE_VALUE_FLAG))
261                                         {
262                                                 i--;
263                                                 value_index = -1;
264                                                 value = NULL;
265                                                 value_length = 0;
266                                         }
267                                 }
268
269                                 if (!value && (options[j].Flags & COMMAND_LINE_VALUE_REQUIRED))
270                                         return COMMAND_LINE_ERROR_MISSING_VALUE;
271
272                                 options[j].Flags |= COMMAND_LINE_ARGUMENT_PRESENT;
273
274                                 if (value)
275                                 {
276                                         options[j].Value = value;
277                                         options[j].Flags |= COMMAND_LINE_VALUE_PRESENT;
278                                 }
279                                 else
280                                 {
281                                         if (options[j].Flags & COMMAND_LINE_VALUE_FLAG)
282                                         {
283                                                 options[j].Value = (LPSTR) 1;
284                                                 options[j].Flags |= COMMAND_LINE_VALUE_PRESENT;
285                                         }
286                                         else if (options[j].Flags & COMMAND_LINE_VALUE_BOOL)
287                                         {
288                                                 if (flags & COMMAND_LINE_SIGIL_ENABLE_DISABLE)
289                                                 {
290                                                         if (toggle == -1)
291                                                                 options[j].Value = BoolValueTrue;
292                                                         else if (!toggle)
293                                                                 options[j].Value = BoolValueFalse;
294                                                         else
295                                                                 options[j].Value = BoolValueTrue;
296                                                 }
297                                                 else
298                                                 {
299                                                         if (sigil[0] == '+')
300                                                                 options[j].Value = BoolValueTrue;
301                                                         else if (sigil[0] == '-')
302                                                                 options[j].Value = BoolValueFalse;
303                                                         else
304                                                                 options[j].Value = BoolValueTrue;
305                                                 }
306
307                                                 options[j].Flags |= COMMAND_LINE_VALUE_PRESENT;
308                                         }
309                                 }
310
311                                 if (postFilter)
312                                         postFilter(context, &options[j]);
313
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;
320                         }
321                 }
322         }
323
324         return 0;
325 }
326
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)
329 {
330         return 0;
331 }
332
333 int CommandLineClearArgumentsA(COMMAND_LINE_ARGUMENT_A* options)
334 {
335         int i;
336
337         for (i = 0; options[i].Name != NULL; i++)
338         {
339                 options[i].Flags &= COMMAND_LINE_INPUT_FLAG_MASK;
340                 options[i].Value = NULL;
341         }
342
343         return 0;
344 }
345
346 int CommandLineClearArgumentsW(COMMAND_LINE_ARGUMENT_W* options)
347 {
348         int i;
349
350         for (i = 0; options[i].Name != NULL; i++)
351         {
352                 options[i].Flags &= COMMAND_LINE_INPUT_FLAG_MASK;
353                 options[i].Value = NULL;
354         }
355
356         return 0;
357 }
358
359 COMMAND_LINE_ARGUMENT_A* CommandLineFindArgumentA(COMMAND_LINE_ARGUMENT_A* options, LPCSTR Name)
360 {
361         int i;
362
363         for (i = 0; options[i].Name != NULL; i++)
364         {
365                 if (strcmp(options[i].Name, Name) == 0)
366                         return &options[i];
367
368                 if (options[i].Alias != NULL)
369                 {
370                         if (strcmp(options[i].Alias, Name) == 0)
371                                 return &options[i];
372                 }
373         }
374
375         return NULL;
376 }
377
378 COMMAND_LINE_ARGUMENT_W* CommandLineFindArgumentW(COMMAND_LINE_ARGUMENT_W* options, LPCWSTR Name)
379 {
380         int i;
381
382         for (i = 0; options[i].Name != NULL; i++)
383         {
384                 if (_wcscmp(options[i].Name, Name) == 0)
385                         return &options[i];
386
387                 if (options[i].Alias != NULL)
388                 {
389                         if (_wcscmp(options[i].Alias, Name) == 0)
390                                 return &options[i];
391                 }
392         }
393
394         return NULL;
395 }
396
397 COMMAND_LINE_ARGUMENT_A* CommandLineFindNextArgumentA(COMMAND_LINE_ARGUMENT_A* argument)
398 {
399         COMMAND_LINE_ARGUMENT_A* nextArgument;
400
401         nextArgument = &argument[1];
402
403         if (nextArgument->Name == NULL)
404                 return NULL;
405
406         return nextArgument;
407 }