Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/jdk17u
Path: blob/master/src/java.base/share/native/libjli/args.c
67707 views
1
/*
2
* Copyright (c) 2015, 2021, Oracle and/or its affiliates. All rights reserved.
3
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4
*
5
* This code is free software; you can redistribute it and/or modify it
6
* under the terms of the GNU General Public License version 2 only, as
7
* published by the Free Software Foundation. Oracle designates this
8
* particular file as subject to the "Classpath" exception as provided
9
* by Oracle in the LICENSE file that accompanied this code.
10
*
11
* This code is distributed in the hope that it will be useful, but WITHOUT
12
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14
* version 2 for more details (a copy is included in the LICENSE file that
15
* accompanied this code).
16
*
17
* You should have received a copy of the GNU General Public License version
18
* 2 along with this work; if not, write to the Free Software Foundation,
19
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20
*
21
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22
* or visit www.oracle.com if you need additional information or have any
23
* questions.
24
*/
25
26
#include <stdlib.h>
27
#include <stdio.h>
28
#include <assert.h>
29
#include <sys/stat.h>
30
#include <ctype.h>
31
32
#ifdef DEBUG_ARGFILE
33
#ifndef NO_JNI
34
#define NO_JNI
35
#endif
36
#define JLI_ReportMessage(...) printf(__VA_ARGS__)
37
#define JDK_JAVA_OPTIONS "JDK_JAVA_OPTIONS"
38
int IsWhiteSpaceOption(const char* name) { return 1; }
39
#else
40
#include "java.h"
41
#include "jni.h"
42
#endif
43
44
#include "jli_util.h"
45
#include "emessages.h"
46
47
#define MAX_ARGF_SIZE 0x7fffffffL
48
49
static char* clone_substring(const char *begin, size_t len) {
50
char *rv = (char *) JLI_MemAlloc(len + 1);
51
memcpy(rv, begin, len);
52
rv[len] = '\0';
53
return rv;
54
}
55
56
enum STATE {
57
FIND_NEXT,
58
IN_COMMENT,
59
IN_QUOTE,
60
IN_ESCAPE,
61
SKIP_LEAD_WS,
62
IN_TOKEN
63
};
64
65
typedef struct {
66
enum STATE state;
67
const char* cptr;
68
const char* eob;
69
char quote_char;
70
JLI_List parts;
71
} __ctx_args;
72
73
#define NOT_FOUND -1
74
static int firstAppArgIndex = NOT_FOUND;
75
76
static jboolean expectingNoDashArg = JNI_FALSE;
77
// Initialize to 1, as the first argument is the app name and not preprocessed
78
static size_t argsCount = 1;
79
static jboolean stopExpansion = JNI_FALSE;
80
static jboolean relaunch = JNI_FALSE;
81
82
/*
83
* Prototypes for internal functions.
84
*/
85
static jboolean expand(JLI_List args, const char *str, const char *var_name);
86
87
JNIEXPORT void JNICALL
88
JLI_InitArgProcessing(jboolean hasJavaArgs, jboolean disableArgFile) {
89
// No expansion for relaunch
90
if (argsCount != 1) {
91
relaunch = JNI_TRUE;
92
stopExpansion = JNI_TRUE;
93
argsCount = 1;
94
} else {
95
stopExpansion = disableArgFile;
96
}
97
98
expectingNoDashArg = JNI_FALSE;
99
100
// for tools, this value remains 0 all the time.
101
firstAppArgIndex = hasJavaArgs ? 0: NOT_FOUND;
102
}
103
104
JNIEXPORT int JNICALL
105
JLI_GetAppArgIndex() {
106
// Will be 0 for tools
107
return firstAppArgIndex;
108
}
109
110
static void checkArg(const char *arg) {
111
size_t idx = 0;
112
argsCount++;
113
114
// All arguments arrive here must be a launcher argument,
115
// ie. by now, all argfile expansions must have been performed.
116
if (*arg == '-') {
117
expectingNoDashArg = JNI_FALSE;
118
if (IsWhiteSpaceOption(arg)) {
119
// expect an argument
120
expectingNoDashArg = JNI_TRUE;
121
122
if (JLI_StrCmp(arg, "-jar") == 0 ||
123
JLI_StrCmp(arg, "--module") == 0 ||
124
JLI_StrCmp(arg, "-m") == 0) {
125
// This is tricky, we do expect NoDashArg
126
// But that is considered main class to stop expansion
127
expectingNoDashArg = JNI_FALSE;
128
// We can not just update the idx here because if -jar @file
129
// still need expansion of @file to get the argument for -jar
130
}
131
} else if (JLI_StrCmp(arg, "--disable-@files") == 0) {
132
stopExpansion = JNI_TRUE;
133
} else if (JLI_StrCCmp(arg, "--module=") == 0) {
134
idx = argsCount;
135
}
136
} else {
137
if (!expectingNoDashArg) {
138
// this is main class, argsCount is index to next arg
139
idx = argsCount;
140
}
141
expectingNoDashArg = JNI_FALSE;
142
}
143
// only update on java mode and not yet found main class
144
if (firstAppArgIndex == NOT_FOUND && idx != 0) {
145
firstAppArgIndex = (int) idx;
146
}
147
}
148
149
/*
150
[\n\r] +------------+ +------------+ [\n\r]
151
+---------+ IN_COMMENT +<------+ | IN_ESCAPE +---------+
152
| +------------+ | +------------+ |
153
| [#] ^ |[#] ^ | |
154
| +----------+ | [\\]| |[^\n\r] |
155
v | | | v |
156
+------------+ [^ \t\n\r\f] +------------+['"]> +------------+ |
157
| FIND_NEXT +-------------->+ IN_TOKEN +-----------+ IN_QUOTE + |
158
+------------+ +------------+ <[quote]+------------+ |
159
| ^ | | ^ ^ |
160
| | [ \t\n\r\f]| [\n\r]| | |[^ \t\n\r\f]v
161
| +--------------------------+-----------------------+ | +--------------+
162
| ['"] | | SKIP_LEAD_WS |
163
+---------------------------------------------------------+ +--------------+
164
*/
165
static char* nextToken(__ctx_args *pctx) {
166
const char* nextc = pctx->cptr;
167
const char* const eob = pctx->eob;
168
const char* anchor = nextc;
169
char *token;
170
171
for (; nextc < eob; nextc++) {
172
register char ch = *nextc;
173
174
// Skip white space characters
175
if (pctx->state == FIND_NEXT || pctx->state == SKIP_LEAD_WS) {
176
while (ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t' || ch == '\f') {
177
nextc++;
178
if (nextc >= eob) {
179
return NULL;
180
}
181
ch = *nextc;
182
}
183
pctx->state = (pctx->state == FIND_NEXT) ? IN_TOKEN : IN_QUOTE;
184
anchor = nextc;
185
// Deal with escape sequences
186
} else if (pctx->state == IN_ESCAPE) {
187
// concatenation directive
188
if (ch == '\n' || ch == '\r') {
189
pctx->state = SKIP_LEAD_WS;
190
} else {
191
// escaped character
192
char* escaped = (char*) JLI_MemAlloc(2 * sizeof(char));
193
escaped[1] = '\0';
194
switch (ch) {
195
case 'n':
196
escaped[0] = '\n';
197
break;
198
case 'r':
199
escaped[0] = '\r';
200
break;
201
case 't':
202
escaped[0] = '\t';
203
break;
204
case 'f':
205
escaped[0] = '\f';
206
break;
207
default:
208
escaped[0] = ch;
209
break;
210
}
211
JLI_List_add(pctx->parts, escaped);
212
pctx->state = IN_QUOTE;
213
}
214
// anchor to next character
215
anchor = nextc + 1;
216
continue;
217
// ignore comment to EOL
218
} else if (pctx->state == IN_COMMENT) {
219
while (ch != '\n' && ch != '\r') {
220
nextc++;
221
if (nextc >= eob) {
222
return NULL;
223
}
224
ch = *nextc;
225
}
226
anchor = nextc + 1;
227
pctx->state = FIND_NEXT;
228
continue;
229
}
230
231
assert(pctx->state != IN_ESCAPE);
232
assert(pctx->state != FIND_NEXT);
233
assert(pctx->state != SKIP_LEAD_WS);
234
assert(pctx->state != IN_COMMENT);
235
236
switch(ch) {
237
case ' ':
238
case '\t':
239
case '\f':
240
if (pctx->state == IN_QUOTE) {
241
continue;
242
}
243
// fall through
244
case '\n':
245
case '\r':
246
if (pctx->parts->size == 0) {
247
token = clone_substring(anchor, nextc - anchor);
248
} else {
249
JLI_List_addSubstring(pctx->parts, anchor, nextc - anchor);
250
token = JLI_List_combine(pctx->parts);
251
JLI_List_free(pctx->parts);
252
pctx->parts = JLI_List_new(4);
253
}
254
pctx->cptr = nextc + 1;
255
pctx->state = FIND_NEXT;
256
return token;
257
case '#':
258
if (pctx->state == IN_QUOTE) {
259
continue;
260
}
261
pctx->state = IN_COMMENT;
262
anchor = nextc + 1;
263
break;
264
case '\\':
265
if (pctx->state != IN_QUOTE) {
266
continue;
267
}
268
JLI_List_addSubstring(pctx->parts, anchor, nextc - anchor);
269
pctx->state = IN_ESCAPE;
270
// anchor after backslash character
271
anchor = nextc + 1;
272
break;
273
case '\'':
274
case '"':
275
if (pctx->state == IN_QUOTE && pctx->quote_char != ch) {
276
// not matching quote
277
continue;
278
}
279
// partial before quote
280
if (anchor != nextc) {
281
JLI_List_addSubstring(pctx->parts, anchor, nextc - anchor);
282
}
283
// anchor after quote character
284
anchor = nextc + 1;
285
if (pctx->state == IN_TOKEN) {
286
pctx->quote_char = ch;
287
pctx->state = IN_QUOTE;
288
} else {
289
pctx->state = IN_TOKEN;
290
}
291
break;
292
default:
293
break;
294
}
295
}
296
297
assert(nextc == eob);
298
// Only need partial token, not comment or whitespaces
299
if (pctx->state == IN_TOKEN || pctx->state == IN_QUOTE) {
300
if (anchor < nextc) {
301
// not yet return until end of stream, we have part of a token.
302
JLI_List_addSubstring(pctx->parts, anchor, nextc - anchor);
303
}
304
}
305
return NULL;
306
}
307
308
static JLI_List readArgFile(FILE *file) {
309
char buf[4096];
310
JLI_List rv;
311
__ctx_args ctx;
312
size_t size;
313
char *token;
314
315
ctx.state = FIND_NEXT;
316
ctx.parts = JLI_List_new(4);
317
// initialize to avoid -Werror=maybe-uninitialized issues from gcc 7.3 onwards.
318
ctx.quote_char = '"';
319
320
/* arbitrarily pick 8, seems to be a reasonable number of arguments */
321
rv = JLI_List_new(8);
322
323
while (!feof(file)) {
324
size = fread(buf, sizeof(char), sizeof(buf), file);
325
if (ferror(file)) {
326
JLI_List_free(rv);
327
return NULL;
328
}
329
330
/* nextc is next character to read from the buffer
331
* eob is the end of input
332
* token is the copied token value, NULL if no a complete token
333
*/
334
ctx.cptr = buf;
335
ctx.eob = buf + size;
336
token = nextToken(&ctx);
337
while (token != NULL) {
338
checkArg(token);
339
JLI_List_add(rv, token);
340
token = nextToken(&ctx);
341
}
342
}
343
344
// remaining partial token
345
if (ctx.state == IN_TOKEN || ctx.state == IN_QUOTE) {
346
if (ctx.parts->size != 0) {
347
token = JLI_List_combine(ctx.parts);
348
checkArg(token);
349
JLI_List_add(rv, token);
350
}
351
}
352
JLI_List_free(ctx.parts);
353
354
return rv;
355
}
356
357
/*
358
* if the arg represent a file, that is, prefix with a single '@',
359
* return a list of arguments from the file.
360
* otherwise, return NULL.
361
*/
362
static JLI_List expandArgFile(const char *arg) {
363
JLI_List rv;
364
struct stat st;
365
FILE *fptr = fopen(arg, "r");
366
367
/* arg file cannot be openned */
368
if (fptr == NULL || fstat(fileno(fptr), &st) != 0) {
369
JLI_ReportMessage(CFG_ERROR6, arg);
370
exit(1);
371
} else {
372
if (st.st_size > MAX_ARGF_SIZE) {
373
JLI_ReportMessage(CFG_ERROR10, MAX_ARGF_SIZE);
374
exit(1);
375
}
376
}
377
378
rv = readArgFile(fptr);
379
380
/* error occurred reading the file */
381
if (rv == NULL) {
382
JLI_ReportMessage(DLL_ERROR4, arg);
383
exit(1);
384
}
385
fclose(fptr);
386
387
return rv;
388
}
389
390
/*
391
* expand a string into a list of words separated by whitespace.
392
*/
393
static JLI_List expandArg(const char *arg) {
394
JLI_List rv;
395
396
/* arbitrarily pick 8, seems to be a reasonable number of arguments */
397
rv = JLI_List_new(8);
398
399
expand(rv, arg, NULL);
400
401
return rv;
402
}
403
404
JNIEXPORT JLI_List JNICALL
405
JLI_PreprocessArg(const char *arg, jboolean expandSourceOpt) {
406
JLI_List rv;
407
408
if (firstAppArgIndex > 0) {
409
// In user application arg, no more work.
410
return NULL;
411
}
412
413
if (stopExpansion) {
414
// still looking for user application arg
415
checkArg(arg);
416
return NULL;
417
}
418
419
if (expandSourceOpt
420
&& JLI_StrCCmp(arg, "--source") == 0
421
&& JLI_StrChr(arg, ' ') != NULL) {
422
return expandArg(arg);
423
}
424
425
if (arg[0] != '@') {
426
checkArg(arg);
427
return NULL;
428
}
429
430
if (arg[1] == '\0') {
431
// @ by itself is an argument
432
checkArg(arg);
433
return NULL;
434
}
435
436
arg++;
437
if (arg[0] == '@') {
438
// escaped @argument
439
rv = JLI_List_new(1);
440
checkArg(arg);
441
JLI_List_add(rv, JLI_StringDup(arg));
442
} else {
443
rv = expandArgFile(arg);
444
}
445
return rv;
446
}
447
448
int isTerminalOpt(char *arg) {
449
return JLI_StrCmp(arg, "-jar") == 0 ||
450
JLI_StrCmp(arg, "-m") == 0 ||
451
JLI_StrCmp(arg, "--module") == 0 ||
452
JLI_StrCCmp(arg, "--module=") == 0 ||
453
JLI_StrCmp(arg, "--dry-run") == 0 ||
454
JLI_StrCmp(arg, "-h") == 0 ||
455
JLI_StrCmp(arg, "-?") == 0 ||
456
JLI_StrCmp(arg, "-help") == 0 ||
457
JLI_StrCmp(arg, "--help") == 0 ||
458
JLI_StrCmp(arg, "-X") == 0 ||
459
JLI_StrCmp(arg, "--help-extra") == 0 ||
460
JLI_StrCmp(arg, "-version") == 0 ||
461
JLI_StrCmp(arg, "--version") == 0 ||
462
JLI_StrCmp(arg, "-fullversion") == 0 ||
463
JLI_StrCmp(arg, "--full-version") == 0;
464
}
465
466
JNIEXPORT jboolean JNICALL
467
JLI_AddArgsFromEnvVar(JLI_List args, const char *var_name) {
468
char *env = getenv(var_name);
469
470
if (firstAppArgIndex == 0) {
471
// Not 'java', return
472
return JNI_FALSE;
473
}
474
475
if (relaunch) {
476
return JNI_FALSE;
477
}
478
479
if (NULL == env) {
480
return JNI_FALSE;
481
}
482
483
JLI_ReportMessage(ARG_INFO_ENVVAR, var_name, env);
484
return expand(args, env, var_name);
485
}
486
487
/*
488
* Expand a string into a list of args.
489
* If the string is the result of looking up an environment variable,
490
* var_name should be set to the name of that environment variable,
491
* for use if needed in error messages.
492
*/
493
494
static jboolean expand(JLI_List args, const char *str, const char *var_name) {
495
jboolean inEnvVar = (var_name != NULL);
496
497
char *p, *arg;
498
char quote;
499
JLI_List argsInFile;
500
501
// This is retained until the process terminates as it is saved as the args
502
p = JLI_MemAlloc(JLI_StrLen(str) + 1);
503
while (*str != '\0') {
504
while (*str != '\0' && isspace(*str)) {
505
str++;
506
}
507
508
// Trailing space
509
if (*str == '\0') {
510
break;
511
}
512
513
arg = p;
514
while (*str != '\0' && !isspace(*str)) {
515
if (inEnvVar && (*str == '"' || *str == '\'')) {
516
quote = *str++;
517
while (*str != quote && *str != '\0') {
518
*p++ = *str++;
519
}
520
521
if (*str == '\0') {
522
JLI_ReportMessage(ARG_ERROR8, var_name);
523
exit(1);
524
}
525
str++;
526
} else {
527
*p++ = *str++;
528
}
529
}
530
531
*p++ = '\0';
532
533
argsInFile = JLI_PreprocessArg(arg, JNI_FALSE);
534
535
if (NULL == argsInFile) {
536
if (isTerminalOpt(arg)) {
537
if (inEnvVar) {
538
JLI_ReportMessage(ARG_ERROR9, arg, var_name);
539
} else {
540
JLI_ReportMessage(ARG_ERROR15, arg);
541
}
542
exit(1);
543
}
544
JLI_List_add(args, arg);
545
} else {
546
size_t cnt, idx;
547
char *argFile = arg;
548
cnt = argsInFile->size;
549
for (idx = 0; idx < cnt; idx++) {
550
arg = argsInFile->elements[idx];
551
if (isTerminalOpt(arg)) {
552
if (inEnvVar) {
553
JLI_ReportMessage(ARG_ERROR10, arg, argFile, var_name);
554
} else {
555
JLI_ReportMessage(ARG_ERROR16, arg, argFile);
556
}
557
exit(1);
558
}
559
JLI_List_add(args, arg);
560
}
561
// Shallow free, we reuse the string to avoid copy
562
JLI_MemFree(argsInFile->elements);
563
JLI_MemFree(argsInFile);
564
}
565
/*
566
* Check if main-class is specified after argument being checked. It
567
* must always appear after expansion, as a main-class could be specified
568
* indirectly into environment variable via an @argfile, and it must be
569
* caught now.
570
*/
571
if (firstAppArgIndex != NOT_FOUND) {
572
if (inEnvVar) {
573
JLI_ReportMessage(ARG_ERROR11, var_name);
574
} else {
575
JLI_ReportMessage(ARG_ERROR17);
576
}
577
exit(1);
578
}
579
580
assert (*str == '\0' || isspace(*str));
581
}
582
583
return JNI_TRUE;
584
}
585
586
#ifdef DEBUG_ARGFILE
587
/*
588
* Stand-alone sanity test, build with following command line
589
* $ CC -DDEBUG_ARGFILE -DNO_JNI -g args.c jli_util.c
590
*/
591
592
void fail(char *expected, char *actual, size_t idx) {
593
printf("FAILED: Token[%lu] expected to be <%s>, got <%s>\n", idx, expected, actual);
594
exit(1);
595
}
596
597
void test_case(char *case_data, char **tokens, size_t cnt_tokens) {
598
size_t actual_cnt;
599
char *token;
600
__ctx_args ctx;
601
602
actual_cnt = 0;
603
604
ctx.state = FIND_NEXT;
605
ctx.parts = JLI_List_new(4);
606
ctx.cptr = case_data;
607
ctx.eob = case_data + strlen(case_data);
608
609
printf("Test case: <%s>, expected %lu tokens.\n", case_data, cnt_tokens);
610
611
for (token = nextToken(&ctx); token != NULL; token = nextToken(&ctx)) {
612
// should not have more tokens than expected
613
if (actual_cnt >= cnt_tokens) {
614
printf("FAILED: Extra token detected: <%s>\n", token);
615
exit(2);
616
}
617
if (JLI_StrCmp(token, tokens[actual_cnt]) != 0) {
618
fail(tokens[actual_cnt], token, actual_cnt);
619
}
620
actual_cnt++;
621
}
622
623
char* last = NULL;
624
if (ctx.parts->size != 0) {
625
last = JLI_List_combine(ctx.parts);
626
}
627
JLI_List_free(ctx.parts);
628
629
if (actual_cnt >= cnt_tokens) {
630
// same number of tokens, should have nothing left to parse
631
if (last != NULL) {
632
if (*last != '#') {
633
printf("Leftover detected: %s", last);
634
exit(2);
635
}
636
}
637
} else {
638
if (JLI_StrCmp(last, tokens[actual_cnt]) != 0) {
639
fail(tokens[actual_cnt], last, actual_cnt);
640
}
641
actual_cnt++;
642
}
643
if (actual_cnt != cnt_tokens) {
644
printf("FAILED: Number of tokens not match, expected %lu, got %lu\n",
645
cnt_tokens, actual_cnt);
646
exit(3);
647
}
648
649
printf("PASS\n");
650
}
651
652
#define DO_CASE(name) \
653
test_case(name[0], name + 1, sizeof(name)/sizeof(char*) - 1)
654
655
int main(int argc, char** argv) {
656
size_t i, j;
657
658
char* case1[] = { "-version -cp \"c:\\\\java libs\\\\one.jar\" \n",
659
"-version", "-cp", "c:\\java libs\\one.jar" };
660
DO_CASE(case1);
661
662
// note the open quote at the end
663
char* case2[] = { "com.foo.Panda \"Furious 5\"\fand\t'Shi Fu' \"escape\tprison",
664
"com.foo.Panda", "Furious 5", "and", "Shi Fu", "escape\tprison"};
665
DO_CASE(case2);
666
667
char* escaped_chars[] = { "escaped chars testing \"\\a\\b\\c\\f\\n\\r\\t\\v\\9\\6\\23\\82\\28\\377\\477\\278\\287\"",
668
"escaped", "chars", "testing", "abc\f\n\r\tv96238228377477278287"};
669
DO_CASE(escaped_chars);
670
671
char* mixed_quote[] = { "\"mix 'single quote' in double\" 'mix \"double quote\" in single' partial\"quote me\"this",
672
"mix 'single quote' in double", "mix \"double quote\" in single", "partialquote methis"};
673
DO_CASE(mixed_quote);
674
675
char* comments[] = { "line one #comment\n'line #2' #rest are comment\r\n#comment on line 3\nline 4 #comment to eof",
676
"line", "one", "line #2", "line", "4"};
677
DO_CASE(comments);
678
679
char* open_quote[] = { "This is an \"open quote \n across line\n\t, note for WS.",
680
"This", "is", "an", "open quote ", "across", "line", ",", "note", "for", "WS." };
681
DO_CASE(open_quote);
682
683
char* escape_in_open_quote[] = { "Try \"this \\\\\\\\ escape\\n double quote \\\" in open quote",
684
"Try", "this \\\\ escape\n double quote \" in open quote" };
685
DO_CASE(escape_in_open_quote);
686
687
char* quote[] = { "'-Dmy.quote.single'='Property in single quote. Here a double quote\" Add some slashes \\\\/'",
688
"-Dmy.quote.single=Property in single quote. Here a double quote\" Add some slashes \\/" };
689
DO_CASE(quote);
690
691
char* multi[] = { "\"Open quote to \n new \"line \\\n\r third\\\n\r\\\tand\ffourth\"",
692
"Open quote to ", "new", "line third\tand\ffourth" };
693
DO_CASE(multi);
694
695
char* escape_quote[] = { "c:\\\"partial quote\"\\lib",
696
"c:\\partial quote\\lib" };
697
DO_CASE(escape_quote);
698
699
if (argc > 1) {
700
for (i = 0; i < argc; i++) {
701
JLI_List tokens = JLI_PreprocessArg(argv[i], JNI_FALSE);
702
if (NULL != tokens) {
703
for (j = 0; j < tokens->size; j++) {
704
printf("Token[%lu]: <%s>\n", (unsigned long) j, tokens->elements[j]);
705
}
706
}
707
}
708
}
709
}
710
711
#endif // DEBUG_ARGFILE
712
713