|
24 | 24 |
|
25 | 25 | import org.apache.commons.lang3.StringUtils; |
26 | 26 |
|
| 27 | +import com.microsoft.java.debug.core.adapter.AdapterUtils; |
27 | 28 | import com.sun.jdi.Method; |
28 | 29 | import com.sun.jdi.ObjectCollectedException; |
29 | 30 | import com.sun.jdi.ThreadReference; |
@@ -438,4 +439,204 @@ public static String[] decodeArrayArgument(String argument) { |
438 | 439 |
|
439 | 440 | return result.toArray(new String[0]); |
440 | 441 | } |
| 442 | + |
| 443 | + /** |
| 444 | + * Parses the given command line into separate arguments that can be passed |
| 445 | + * to <code>Runtime.getRuntime().exec(cmdArray)</code>. |
| 446 | + * |
| 447 | + * @param cmdStr command line as a single string. |
| 448 | + * @return the individual arguments. |
| 449 | + */ |
| 450 | + public static List<String> parseArguments(String cmdStr) { |
| 451 | + if (cmdStr == null) { |
| 452 | + return new ArrayList<>(); |
| 453 | + } |
| 454 | + |
| 455 | + return AdapterUtils.isWindows() ? parseArgumentsWindows(cmdStr) : parseArgumentsNonWindows(cmdStr); |
| 456 | + } |
| 457 | + |
| 458 | + |
| 459 | + /** |
| 460 | + * Parses the given command line into separate arguments for mac/linux platform. |
| 461 | + * This piece of code is mainly copied from |
| 462 | + * https://github.com/eclipse/eclipse.platform.debug/blob/master/org.eclipse.debug.core/core/org/eclipse/debug/core/DebugPlugin.java#L1374 |
| 463 | + * |
| 464 | + * @param args |
| 465 | + * the command line arguments as a single string. |
| 466 | + * @return the individual arguments |
| 467 | + */ |
| 468 | + private static List<String> parseArgumentsNonWindows(String args) { |
| 469 | + // man sh, see topic QUOTING |
| 470 | + List<String> result = new ArrayList<>(); |
| 471 | + |
| 472 | + final int DEFAULT = 0; |
| 473 | + final int ARG = 1; |
| 474 | + final int IN_DOUBLE_QUOTE = 2; |
| 475 | + final int IN_SINGLE_QUOTE = 3; |
| 476 | + |
| 477 | + int state = DEFAULT; |
| 478 | + StringBuilder buf = new StringBuilder(); |
| 479 | + int len = args.length(); |
| 480 | + for (int i = 0; i < len; i++) { |
| 481 | + char ch = args.charAt(i); |
| 482 | + if (Character.isWhitespace(ch)) { |
| 483 | + if (state == DEFAULT) { |
| 484 | + // skip |
| 485 | + continue; |
| 486 | + } else if (state == ARG) { |
| 487 | + state = DEFAULT; |
| 488 | + result.add(buf.toString()); |
| 489 | + buf.setLength(0); |
| 490 | + continue; |
| 491 | + } |
| 492 | + } |
| 493 | + switch (state) { |
| 494 | + case DEFAULT: |
| 495 | + case ARG: |
| 496 | + if (ch == '"') { |
| 497 | + state = IN_DOUBLE_QUOTE; |
| 498 | + } else if (ch == '\'') { |
| 499 | + state = IN_SINGLE_QUOTE; |
| 500 | + } else if (ch == '\\' && i + 1 < len) { |
| 501 | + state = ARG; |
| 502 | + ch = args.charAt(++i); |
| 503 | + buf.append(ch); |
| 504 | + } else { |
| 505 | + state = ARG; |
| 506 | + buf.append(ch); |
| 507 | + } |
| 508 | + break; |
| 509 | + |
| 510 | + case IN_DOUBLE_QUOTE: |
| 511 | + if (ch == '"') { |
| 512 | + state = ARG; |
| 513 | + } else if (ch == '\\' && i + 1 < len && (args.charAt(i + 1) == '\\' || args.charAt(i + 1) == '"')) { |
| 514 | + ch = args.charAt(++i); |
| 515 | + buf.append(ch); |
| 516 | + } else { |
| 517 | + buf.append(ch); |
| 518 | + } |
| 519 | + break; |
| 520 | + |
| 521 | + case IN_SINGLE_QUOTE: |
| 522 | + if (ch == '\'') { |
| 523 | + state = ARG; |
| 524 | + } else { |
| 525 | + buf.append(ch); |
| 526 | + } |
| 527 | + break; |
| 528 | + |
| 529 | + default: |
| 530 | + throw new IllegalStateException(); |
| 531 | + } |
| 532 | + } |
| 533 | + if (buf.length() > 0 || state != DEFAULT) { |
| 534 | + result.add(buf.toString()); |
| 535 | + } |
| 536 | + |
| 537 | + return result; |
| 538 | + } |
| 539 | + |
| 540 | + |
| 541 | + /** |
| 542 | + * Parses the given command line into separate arguments for windows platform. |
| 543 | + * This piece of code is mainly copied from |
| 544 | + * https://github.com/eclipse/eclipse.platform.debug/blob/master/org.eclipse.debug.core/core/org/eclipse/debug/core/DebugPlugin.java#L1264 |
| 545 | + * |
| 546 | + * @param args |
| 547 | + * the command line arguments as a single string. |
| 548 | + * @return the individual arguments |
| 549 | + */ |
| 550 | + private static List<String> parseArgumentsWindows(String args) { |
| 551 | + // see http://msdn.microsoft.com/en-us/library/a1y7w461.aspx |
| 552 | + List<String> result = new ArrayList<>(); |
| 553 | + final int DEFAULT = 0; |
| 554 | + final int ARG = 1; |
| 555 | + final int IN_DOUBLE_QUOTE = 2; |
| 556 | + |
| 557 | + int state = DEFAULT; |
| 558 | + int backslashes = 0; |
| 559 | + StringBuilder buf = new StringBuilder(); |
| 560 | + int len = args.length(); |
| 561 | + for (int i = 0; i < len; i++) { |
| 562 | + char ch = args.charAt(i); |
| 563 | + if (ch == '\\') { |
| 564 | + backslashes++; |
| 565 | + continue; |
| 566 | + } else if (backslashes != 0) { |
| 567 | + if (ch == '"') { |
| 568 | + for (; backslashes >= 2; backslashes -= 2) { |
| 569 | + buf.append('\\'); |
| 570 | + } |
| 571 | + if (backslashes == 1) { |
| 572 | + if (state == DEFAULT) { |
| 573 | + state = ARG; |
| 574 | + } |
| 575 | + buf.append('"'); |
| 576 | + backslashes = 0; |
| 577 | + continue; |
| 578 | + } // else fall through to switch |
| 579 | + } else { |
| 580 | + // false alarm, treat passed backslashes literally... |
| 581 | + if (state == DEFAULT) { |
| 582 | + state = ARG; |
| 583 | + } |
| 584 | + for (; backslashes > 0; backslashes--) { |
| 585 | + buf.append('\\'); |
| 586 | + } |
| 587 | + // fall through to switch |
| 588 | + } |
| 589 | + } |
| 590 | + if (Character.isWhitespace(ch)) { |
| 591 | + if (state == DEFAULT) { |
| 592 | + // skip |
| 593 | + continue; |
| 594 | + } else if (state == ARG) { |
| 595 | + state = DEFAULT; |
| 596 | + result.add(buf.toString()); |
| 597 | + buf.setLength(0); |
| 598 | + continue; |
| 599 | + } |
| 600 | + } |
| 601 | + switch (state) { |
| 602 | + case DEFAULT: |
| 603 | + case ARG: |
| 604 | + if (ch == '"') { |
| 605 | + state = IN_DOUBLE_QUOTE; |
| 606 | + } else { |
| 607 | + state = ARG; |
| 608 | + buf.append(ch); |
| 609 | + } |
| 610 | + break; |
| 611 | + |
| 612 | + case IN_DOUBLE_QUOTE: |
| 613 | + if (ch == '"') { |
| 614 | + if (i + 1 < len && args.charAt(i + 1) == '"') { |
| 615 | + /* Undocumented feature in Windows: |
| 616 | + * Two consecutive double quotes inside a double-quoted argument are interpreted as |
| 617 | + * a single double quote. |
| 618 | + */ |
| 619 | + buf.append('"'); |
| 620 | + i++; |
| 621 | + } else if (buf.length() == 0) { |
| 622 | + // empty string on Windows platform. Account for bug in constructor of JDK's java.lang.ProcessImpl. |
| 623 | + result.add("\"\""); //$NON-NLS-1$ |
| 624 | + state = DEFAULT; |
| 625 | + } else { |
| 626 | + state = ARG; |
| 627 | + } |
| 628 | + } else { |
| 629 | + buf.append(ch); |
| 630 | + } |
| 631 | + break; |
| 632 | + |
| 633 | + default: |
| 634 | + throw new IllegalStateException(); |
| 635 | + } |
| 636 | + } |
| 637 | + if (buf.length() > 0 || state != DEFAULT) { |
| 638 | + result.add(buf.toString()); |
| 639 | + } |
| 640 | + return result; |
| 641 | + } |
441 | 642 | } |
0 commit comments