From 80d5035c1c25bbf6a17f7e235455559b27d8b5ab Mon Sep 17 00:00:00 2001
From: Lukas Stadler <lukas.stadler@oracle.com>
Date: Wed, 12 Jul 2017 17:44:21 +0200
Subject: [PATCH] parse options, fix to Rscript args parsing, don't echo input
 in Rscript

---
 .../r/launcher/JLineConsoleHandler.java       |   2 +-
 .../oracle/truffle/r/launcher/Launcher.java   | 514 ++++++++++++++++++
 .../truffle/r/launcher/RCmdOptions.java       |  19 +-
 .../oracle/truffle/r/launcher/RCommand.java   |  81 ++-
 .../truffle/r/launcher/RscriptCommand.java    |  52 +-
 com.oracle.truffle.r.native/run/Makefile      |   2 +-
 6 files changed, 627 insertions(+), 43 deletions(-)
 create mode 100644 com.oracle.truffle.r.launcher/src/com/oracle/truffle/r/launcher/Launcher.java

diff --git a/com.oracle.truffle.r.launcher/src/com/oracle/truffle/r/launcher/JLineConsoleHandler.java b/com.oracle.truffle.r.launcher/src/com/oracle/truffle/r/launcher/JLineConsoleHandler.java
index 1f0ad295e3..fc2f1e2e3b 100644
--- a/com.oracle.truffle.r.launcher/src/com/oracle/truffle/r/launcher/JLineConsoleHandler.java
+++ b/com.oracle.truffle.r.launcher/src/com/oracle/truffle/r/launcher/JLineConsoleHandler.java
@@ -75,7 +75,7 @@ public class JLineConsoleHandler extends ConsoleHandler {
 
     @Override
     public void setPrompt(String prompt) {
-        console.setPrompt(noPrompt ? "" : prompt);
+        console.setPrompt(noPrompt ? "" : prompt != null ? prompt : "");
     }
 
     public void clearHistory() {
diff --git a/com.oracle.truffle.r.launcher/src/com/oracle/truffle/r/launcher/Launcher.java b/com.oracle.truffle.r.launcher/src/com/oracle/truffle/r/launcher/Launcher.java
new file mode 100644
index 0000000000..cca67ddb07
--- /dev/null
+++ b/com.oracle.truffle.r.launcher/src/com/oracle/truffle/r/launcher/Launcher.java
@@ -0,0 +1,514 @@
+/*
+ * Copyright (c) 2017, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.truffle.r.launcher;
+
+import static java.lang.Integer.max;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.graalvm.options.OptionCategory;
+import org.graalvm.options.OptionDescriptor;
+import org.graalvm.options.OptionDescriptors;
+import org.graalvm.polyglot.Engine;
+import org.graalvm.polyglot.Instrument;
+import org.graalvm.polyglot.Language;
+
+public abstract class Launcher {
+    private static final boolean STATIC_VERBOSE = Boolean.getBoolean("org.graalvm.launcher.verbose");
+
+    private Engine tempEngine;
+
+    private final boolean verbose;
+
+    private boolean help;
+    private boolean helpDebug;
+    private boolean helpExpert;
+    private boolean helpTools;
+    private boolean helpLanguages;
+
+    private OptionCategory helpCategory;
+    private VersionAction versionAction = VersionAction.None;
+
+    protected enum VersionAction {
+        None,
+        PrintAndExit,
+        PrintAndContinue
+    }
+
+    public Launcher() {
+        verbose = STATIC_VERBOSE || Boolean.valueOf(System.getenv("VERBOSE_GRAALVM_LAUNCHERS"));
+    }
+
+    private Engine getTempEngine() {
+        if (tempEngine == null) {
+            tempEngine = Engine.create();
+        }
+        return tempEngine;
+    }
+
+    void setHelpCategory(OptionCategory helpCategory) {
+        this.helpCategory = helpCategory;
+    }
+
+    protected void setVersionAction(VersionAction versionAction) {
+        this.versionAction = versionAction;
+    }
+
+    private static class AbortException extends RuntimeException {
+        static final long serialVersionUID = 4681646279864737876L;
+
+        AbortException(String message) {
+            super(message, null);
+        }
+
+        @SuppressWarnings("sync-override")
+        @Override
+        public final Throwable fillInStackTrace() {
+            return null;
+        }
+    }
+
+    protected AbortException exit() {
+        return abort(null, 0);
+    }
+
+    protected AbortException abort(String message) {
+        return abort(message, 1);
+    }
+
+    protected AbortException abort(String message, int exitCode) {
+        if (message != null) {
+            System.err.println("ERROR: " + message);
+        }
+        System.exit(exitCode);
+        throw new AbortException(message);
+    }
+
+    protected abstract void printHelp(OptionCategory maxCategory);
+
+    protected abstract void printVersion();
+
+    protected abstract void collectArguments(Set<String> options);
+
+    protected static void printPolyglotVersions() {
+        Engine engine = Engine.create();
+        System.out.println("GraalVM Polyglot Engine Version " + engine.getVersion());
+        printLanguages(engine, true);
+        printInstruments(engine, true);
+    }
+
+    protected boolean isVerbose() {
+        return verbose;
+    }
+
+    final boolean runPolyglotAction() {
+        OptionCategory maxCategory = helpDebug ? OptionCategory.DEBUG : (helpExpert ? OptionCategory.EXPERT : (helpCategory != null ? helpCategory : OptionCategory.USER));
+
+        switch (versionAction) {
+            case PrintAndContinue:
+                printVersion();
+                return false;
+            case PrintAndExit:
+                printVersion();
+                return true;
+            case None:
+                break;
+        }
+        boolean printDefaultHelp = helpCategory != null || help || ((helpExpert || helpDebug) && !helpTools && !helpLanguages);
+        Engine tmpEngine = null;
+        if (printDefaultHelp) {
+            printHelp(maxCategory);
+            // @formatter:off
+            System.out.println();
+            System.out.println("Runtime Options:");
+            printOption("--polyglot",                   "Run with all other guest languages accessible.");
+            printOption("--native",                     "Run using the native launcher with limited Java access (default).");
+            printOption("--native.[option]",            "Pass options to the native image; for example, '--native.Xmx1G'. To see available options, use '--native.help'.");
+            printOption("--jvm",                        "Run on the Java Virtual Machine with Java access.");
+            printOption("--jvm.[option]",               "Pass options to the JVM; for example, '--jvm.classpath=myapp.jar'. To see available options. use '--jvm.help'.");
+            printOption("--help",                       "Print this help message.");
+            printOption("--help:languages",             "Print options for all installed languages.");
+            printOption("--help:tools",                 "Print options for all installed tools.");
+            printOption("--help:expert",                "Print additional engine options for experts.");
+            if (helpExpert || helpDebug) {
+                printOption("--help:debug",             "Print additional engine options for debugging.");
+            }
+            printOption("--version",                    "Print version information and exit.");
+            printOption("--show-version",               "Print version information and continue execution.");
+            // @formatter:on
+            if (tmpEngine == null) {
+                tmpEngine = Engine.create();
+            }
+            List<PrintableOption> engineOptions = new ArrayList<>();
+            for (OptionDescriptor descriptor : tmpEngine.getOptions()) {
+                if (!descriptor.getName().startsWith("engine.") && !descriptor.getName().startsWith("compiler.")) {
+                    continue;
+                }
+                if (descriptor.getCategory().ordinal() <= maxCategory.ordinal()) {
+                    engineOptions.add(asPrintableOption(descriptor));
+                }
+            }
+            if (!engineOptions.isEmpty()) {
+                printOptions(engineOptions, "Engine options:", 2);
+            }
+        }
+
+        if (helpLanguages) {
+            if (tmpEngine == null) {
+                tmpEngine = Engine.create();
+            }
+            printLanguageOptions(tmpEngine, maxCategory);
+        }
+
+        if (helpTools) {
+            if (tmpEngine == null) {
+                tmpEngine = Engine.create();
+            }
+            printInstrumentOptions(tmpEngine, maxCategory);
+        }
+
+        if (printDefaultHelp || helpLanguages || helpTools) {
+            System.out.println("\nSee http://www.oracle.com/technetwork/oracle-labs/program-languages/overview/index.html for more information.");
+            return true;
+        }
+
+        return false;
+    }
+
+    private static void printInstrumentOptions(Engine engine, OptionCategory maxCategory) {
+        Map<Instrument, List<PrintableOption>> instrumentsOptions = new HashMap<>();
+        List<Instrument> instruments = sortedInstruments(engine);
+        for (Instrument instrument : instruments) {
+            List<PrintableOption> options = new ArrayList<>();
+            for (OptionDescriptor descriptor : instrument.getOptions()) {
+                if (descriptor.getCategory().ordinal() <= maxCategory.ordinal()) {
+                    options.add(asPrintableOption(descriptor));
+                }
+            }
+            if (!options.isEmpty()) {
+                instrumentsOptions.put(instrument, options);
+            }
+        }
+        if (!instrumentsOptions.isEmpty()) {
+            System.out.println();
+            System.out.println("Tool options:");
+            for (Instrument instrument : instruments) {
+                List<PrintableOption> options = instrumentsOptions.get(instrument);
+                if (options != null) {
+                    printOptions(options, "  " + instrument.getName() + ":", 4);
+                }
+            }
+        }
+    }
+
+    private static void printLanguageOptions(Engine engine, OptionCategory maxCategory) {
+        Map<Language, List<PrintableOption>> languagesOptions = new HashMap<>();
+        List<Language> languages = sortedLanguages(engine);
+        for (Language language : languages) {
+            List<PrintableOption> options = new ArrayList<>();
+            for (OptionDescriptor descriptor : language.getOptions()) {
+                if (descriptor.getCategory().ordinal() <= maxCategory.ordinal()) {
+                    options.add(asPrintableOption(descriptor));
+                }
+            }
+            if (!options.isEmpty()) {
+                languagesOptions.put(language, options);
+            }
+        }
+        if (!languagesOptions.isEmpty()) {
+            System.out.println();
+            System.out.println("Language Options:");
+            for (Language language : languages) {
+                List<PrintableOption> options = languagesOptions.get(language);
+                if (options != null) {
+                    printOptions(options, "  " + language.getName() + ":", 4);
+                }
+            }
+        }
+    }
+
+    protected boolean parsePolyglotOption(String defaultOptionPrefix, Map<String, String> options, String arg) {
+        switch (arg) {
+            case "--help":
+                help = true;
+                return true;
+            case "--help:debug":
+                helpDebug = true;
+                return true;
+            case "--help:expert":
+                helpExpert = true;
+                return true;
+            case "--help:tools":
+                helpTools = true;
+                return true;
+            case "--help:languages":
+                helpLanguages = true;
+                return true;
+            case "--version":
+                versionAction = VersionAction.PrintAndExit;
+                return true;
+            case "--show-version":
+                versionAction = VersionAction.PrintAndContinue;
+                return true;
+            case "--polyglot":
+            case "--jvm":
+            case "--native":
+                return false;
+            default:
+                // getLanguageId() or null?
+                if (arg.length() <= 2 || !arg.startsWith("--")) {
+                    return false;
+                }
+                int eqIdx = arg.indexOf('=');
+                String key;
+                String value;
+                if (eqIdx < 0) {
+                    key = arg.substring(2);
+                    value = null;
+                } else {
+                    key = arg.substring(2, eqIdx);
+                    value = arg.substring(eqIdx + 1);
+                }
+
+                if (value == null) {
+                    value = "true";
+                }
+                int index = key.indexOf('.');
+                String group = key;
+                if (index >= 0) {
+                    group = group.substring(0, index);
+                }
+                OptionDescriptor descriptor = findPolyglotOptionDescriptor(group, key);
+                if (descriptor == null) {
+                    if (defaultOptionPrefix != null) {
+                        descriptor = findPolyglotOptionDescriptor(defaultOptionPrefix, defaultOptionPrefix + "." + key);
+                    }
+                    if (descriptor == null) {
+                        return false;
+                    }
+                }
+                try {
+                    descriptor.getKey().getType().convert(value);
+                } catch (IllegalArgumentException e) {
+                    throw abort(String.format("Invalid argument %s specified. %s'", arg, e.getMessage()));
+                }
+                options.put(key, value);
+                return true;
+        }
+    }
+
+    private OptionDescriptor findPolyglotOptionDescriptor(String group, String key) {
+        OptionDescriptors descriptors = null;
+        switch (group) {
+            case "compiler":
+            case "engine":
+                descriptors = getTempEngine().getOptions();
+                break;
+            default:
+                Engine engine = getTempEngine();
+                if (engine.getLanguages().containsKey(group)) {
+                    descriptors = engine.getLanguage(group).getOptions();
+                } else if (engine.getInstruments().containsKey(group)) {
+                    descriptors = engine.getInstrument(group).getOptions();
+                }
+                break;
+        }
+        if (descriptors == null) {
+            return null;
+        }
+        return descriptors.get(key);
+    }
+
+    protected static List<Language> sortedLanguages(Engine engine) {
+        List<Language> languages = new ArrayList<>(engine.getLanguages().values());
+        languages.sort(Comparator.comparing(Language::getId));
+        return languages;
+    }
+
+    protected static List<Instrument> sortedInstruments(Engine engine) {
+        List<Instrument> instruments = new ArrayList<>(engine.getInstruments().values());
+        instruments.sort(Comparator.comparing(Instrument::getId));
+        return instruments;
+    }
+
+    protected static void printOption(OptionCategory maxCategory, OptionDescriptor descriptor) {
+        if (descriptor.getCategory().ordinal() <= maxCategory.ordinal()) {
+            printOption(asPrintableOption(descriptor));
+        }
+    }
+
+    protected static PrintableOption asPrintableOption(OptionDescriptor descriptor) {
+        StringBuilder key = new StringBuilder("--");
+        key.append(descriptor.getName());
+        Object defaultValue = descriptor.getKey().getDefaultValue();
+        if (defaultValue instanceof Boolean && defaultValue == Boolean.FALSE) {
+            // nothing to print
+        } else {
+            key.append("=<");
+            key.append(descriptor.getKey().getType().getName());
+            key.append(">");
+        }
+        return new PrintableOption(key.toString(), descriptor.getHelp());
+    }
+
+    protected static void printOption(String option, String description) {
+        printOption(option, description, 2);
+    }
+
+    protected static void printOption(String option, String description, int indentation) {
+        StringBuilder indent = new StringBuilder(indentation);
+        for (int i = 0; i < indentation; i++) {
+            indent.append(' ');
+        }
+        String desc = description != null ? description : "";
+        if (option.length() >= 45 && description != null) {
+            System.out.println(String.format("%s%s%n%s%-45s%s", indent, option, indent, "", desc));
+        } else {
+            System.out.println(String.format("%s%-45s%s", indent, option, desc));
+        }
+    }
+
+    protected static void printOption(PrintableOption option) {
+        printOption(option, 2);
+    }
+
+    protected static void printOption(PrintableOption option, int indentation) {
+        printOption(option.option, option.description, indentation);
+    }
+
+    private static final class PrintableOption implements Comparable<PrintableOption> {
+        final String option;
+        final String description;
+
+        private PrintableOption(String option, String description) {
+            this.option = option;
+            this.description = description;
+        }
+
+        @Override
+        public int compareTo(PrintableOption o) {
+            return this.option.compareTo(o.option);
+        }
+    }
+
+    private static void printOptions(List<PrintableOption> options, String title, int indentation) {
+        Collections.sort(options);
+        System.out.println(title);
+        for (PrintableOption option : options) {
+            printOption(option, indentation);
+        }
+    }
+
+    protected enum OS {
+        Darwin,
+        Linux,
+        Solaris;
+
+        private static OS findCurrent() {
+            final String name = System.getProperty("os.name");
+            if (name.equals("Linux")) {
+                return Linux;
+            }
+            if (name.equals("SunOS")) {
+                return Solaris;
+            }
+            if (name.equals("Mac OS X") || name.equals("Darwin")) {
+                return Darwin;
+            }
+            throw new IllegalArgumentException("unknown OS: " + name);
+        }
+
+        private static final OS current = findCurrent();
+
+        public static OS getCurrent() {
+            return current;
+        }
+    }
+
+    protected static void printLanguages(Engine engine, boolean printWhenEmpty) {
+        if (engine.getLanguages().isEmpty()) {
+            if (printWhenEmpty) {
+                System.out.println("  Installed Languages: none");
+            }
+        } else {
+            System.out.println("  Installed Languages:");
+            List<Language> languages = new ArrayList<>(engine.getLanguages().size());
+            int nameLength = 0;
+            boolean hasHost = false;
+            for (Language language : engine.getLanguages().values()) {
+                languages.add(language);
+                nameLength = max(nameLength, language.getName().length());
+                hasHost |= language.isHost();
+            }
+            languages.sort(Comparator.comparing(Language::getId));
+            String langFormat = "    %-" + nameLength + "s%s version %s%n";
+            for (Language language : languages) {
+                String host;
+                if (hasHost) {
+                    if (language.isHost()) {
+                        host = " (Host)";
+                    } else {
+                        host = "       ";
+                    }
+                } else {
+                    host = "";
+                }
+                String version = language.getVersion();
+                if (version == null || version.length() == 0) {
+                    version = "";
+                }
+                System.out.printf(langFormat, language.getName().isEmpty() ? "Unnamed" : language.getName(), host, version);
+            }
+        }
+    }
+
+    protected static void printInstruments(Engine engine, boolean printWhenEmpty) {
+        if (engine.getInstruments().isEmpty()) {
+            if (printWhenEmpty) {
+                System.out.println("  Installed Tools: none");
+            }
+        } else {
+            System.out.println("  Installed Tools:");
+            List<Instrument> instruments = sortedInstruments(engine);
+            int nameLength = 0;
+            for (Instrument instrument : instruments) {
+                nameLength = max(nameLength, instrument.getName().length());
+            }
+            String instrumentFormat = "    %-" + nameLength + "s version %s%n";
+            for (Instrument instrument : instruments) {
+                String version = instrument.getVersion();
+                if (version == null || version.length() == 0) {
+                    version = "";
+                }
+                System.out.printf(instrumentFormat, instrument.getName().isEmpty() ? "Unnamed" : instrument.getName(), version);
+            }
+        }
+    }
+}
diff --git a/com.oracle.truffle.r.launcher/src/com/oracle/truffle/r/launcher/RCmdOptions.java b/com.oracle.truffle.r.launcher/src/com/oracle/truffle/r/launcher/RCmdOptions.java
index 7a54ae27c2..55f7b025f7 100644
--- a/com.oracle.truffle.r.launcher/src/com/oracle/truffle/r/launcher/RCmdOptions.java
+++ b/com.oracle.truffle.r.launcher/src/com/oracle/truffle/r/launcher/RCmdOptions.java
@@ -97,11 +97,11 @@ public final class RCmdOptions {
         // Whether this option is actually implemented in FastR
         private final boolean implemented;
         // The short option name prefixed by {@code -} or {@code null} if no {@code -} form.
-        private final String shortName;
+        final String shortName;
         // The option name prefixed by {@code --} or {@code null} if no {@code --} form.
         private final String name;
         // The plain option name as passed to the constructor.
-        public final String plainName;
+        final String plainName;
         // The '=' separated suffix, e.g. {@code --file=FILE}.
         private final String suffix;
         // The space separated suffix, e.g. {@code -g TYPE}.
@@ -161,6 +161,7 @@ public final class RCmdOptions {
             }
             return result;
         }
+
     }
 
     private final EnumMap<RCmdOption, Object> optionValues;
@@ -322,7 +323,7 @@ public final class RCmdOptions {
             }
         }
         // adjust for inserted executable name
-        return new RCmdOptions(options, args, firstNonOptionArgIndex + 1);
+        return new RCmdOptions(options, args, firstNonOptionArgIndex);
     }
 
     public String[] getArguments() {
@@ -337,7 +338,7 @@ public final class RCmdOptions {
         this.arguments = arguments;
     }
 
-    public void printHelpAndVersion() {
+    void printHelpAndVersion() {
         if (getBoolean(HELP)) {
             throw printHelpAndExit(RCmdOptions.Client.R);
         } else if (getBoolean(VERSION)) {
@@ -345,7 +346,7 @@ public final class RCmdOptions {
         }
     }
 
-    private static void printHelp(Client client) {
+    static void printHelp(Client client) {
         System.out.println(client.usage());
         System.out.println("Options:");
         for (RCmdOption option : RCmdOption.values()) {
@@ -354,16 +355,20 @@ public final class RCmdOptions {
         System.out.println("\nFILE may contain spaces but not shell metacharacters.\n");
     }
 
-    public static RuntimeException printHelpAndExit(Client client) {
+    private static RuntimeException printHelpAndExit(Client client) {
         printHelp(client);
         System.exit(0);
         throw RCommand.fatal("should not reach here");
     }
 
-    private static RuntimeException printVersionAndExit() {
+    static void printVersion() {
         System.out.print("FastR version ");
         System.out.println(RVersionNumber.FULL);
         System.out.println(RVersionNumber.LICENSE);
+    }
+
+    private static RuntimeException printVersionAndExit() {
+        printVersion();
         System.exit(0);
         throw RCommand.fatal("should not reach here");
     }
diff --git a/com.oracle.truffle.r.launcher/src/com/oracle/truffle/r/launcher/RCommand.java b/com.oracle.truffle.r.launcher/src/com/oracle/truffle/r/launcher/RCommand.java
index c4a73a7228..11e4450e7b 100644
--- a/com.oracle.truffle.r.launcher/src/com/oracle/truffle/r/launcher/RCommand.java
+++ b/com.oracle.truffle.r.launcher/src/com/oracle/truffle/r/launcher/RCommand.java
@@ -28,21 +28,60 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.nio.file.Files;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
+import java.util.Set;
 
+import org.graalvm.options.OptionCategory;
 import org.graalvm.polyglot.Context;
 import org.graalvm.polyglot.PolyglotException;
 import org.graalvm.polyglot.Source;
 
+import com.oracle.truffle.r.launcher.RCmdOptions.Client;
 import com.oracle.truffle.r.launcher.RCmdOptions.RCmdOption;
 
 import jline.console.UserInterruptException;
 
+class RLauncher extends Launcher {
+
+    private final Client client;
+
+    RLauncher(Client client) {
+        this.client = client;
+    }
+
+    @Override
+    protected void printHelp(OptionCategory maxCategory) {
+        RCmdOptions.printHelp(client);
+    }
+
+    @Override
+    protected void printVersion() {
+        RCmdOptions.printVersion();
+    }
+
+    @Override
+    protected void collectArguments(Set<String> options) {
+        for (RCmdOption option : RCmdOption.values()) {
+            if (option.shortName != null) {
+                options.add(option.shortName);
+            }
+            if (option.plainName != null) {
+                options.add(option.plainName);
+            }
+        }
+    }
+}
+
 /*
  * TODO:
+ *
  * - create a replacement for "executor"
+ *
  * - fatal needs to be handled more carefully in nested/spawned contexts
- * - handle R_DEFAULT_PACKAGES in REnvVars
  */
 
 /**
@@ -77,24 +116,38 @@ public class RCommand {
         return result;
     }
 
-    public static int doMain(String[] inArgs, String[] env, InputStream inStream, OutputStream outStream, OutputStream errStream) {
+    public static int doMain(String[] args, String[] env, InputStream inStream, OutputStream outStream, OutputStream errStream) {
         StartupTiming.timestamp("Main Entered");
-        String[] args = inArgs;
+        ArrayList<String> argsList = new ArrayList<>(Arrays.asList(args));
         if (System.console() != null) {
             // add "--interactive" to force interactive mode
-            RCmdOptions options = RCmdOptions.parseArguments(RCmdOptions.Client.R, args, false);
-            if (!options.getBoolean(RCmdOption.INTERACTIVE)) {
-                args = new String[inArgs.length + 1];
-                args[0] = inArgs[0];
-                args[1] = "--interactive";
-                System.arraycopy(inArgs, 1, args, 2, inArgs.length - 1);
+            boolean addArg = true;
+            for (String arg : args) {
+                if ("--interactive".equals(arg)) {
+                    addArg = false;
+                    break;
+                }
             }
+            if (addArg) {
+                argsList.add(1, "--interactive");
+            }
+        }
+
+        RLauncher launcher = new RLauncher(Client.R);
+        Map<String, String> polyglotOptions = new HashMap<>();
+        for (int i = 1; i < argsList.size(); i++) {
+            String arg = argsList.get(i);
+            if (launcher.parsePolyglotOption("R", polyglotOptions, arg)) {
+                argsList.remove(i);
+            }
+        }
+        if (launcher.runPolyglotAction()) {
+            return 0;
         }
-        RCmdOptions options = RCmdOptions.parseArguments(RCmdOptions.Client.R, args, false);
-        options.printHelpAndVersion();
+        RCmdOptions options = RCmdOptions.parseArguments(Client.R, argsList.toArray(new String[argsList.size()]), false);
         assert env == null : "re-enable setting environments";
         ConsoleHandler consoleHandler = createConsoleHandler(options, false, inStream, outStream);
-        try (Context context = Context.newBuilder().arguments("R", options.getArguments()).in(consoleHandler.createInputStream()).out(outStream).err(errStream).build()) {
+        try (Context context = Context.newBuilder().options(polyglotOptions).arguments("R", options.getArguments()).in(consoleHandler.createInputStream()).out(outStream).err(errStream).build()) {
             consoleHandler.setContext(context);
             StartupTiming.timestamp("VM Created");
             StartupTiming.printSummary();
@@ -180,7 +233,7 @@ public class RCommand {
         try {
             while (true) { // processing inputs
                 boolean doEcho = doEcho(context);
-                consoleHandler.setPrompt(doEcho ? getPrompt(context) : "");
+                consoleHandler.setPrompt(doEcho ? getPrompt(context) : null);
                 try {
                     String input = consoleHandler.readLine();
                     if (input == null) {
@@ -200,7 +253,7 @@ public class RCommand {
                             context.eval(Source.newBuilder("R", sb.toString(), "<REPL>").interactive(true).buildLiteral());
                         } catch (PolyglotException e) {
                             if (continuePrompt == null) {
-                                continuePrompt = doEcho ? getContinuePrompt(context) : "";
+                                continuePrompt = doEcho ? getContinuePrompt(context) : null;
                             }
                             if (e.isIncompleteSource()) {
                                 // read another line of input
diff --git a/com.oracle.truffle.r.launcher/src/com/oracle/truffle/r/launcher/RscriptCommand.java b/com.oracle.truffle.r.launcher/src/com/oracle/truffle/r/launcher/RscriptCommand.java
index 28c65b70f1..cb3c9782f0 100644
--- a/com.oracle.truffle.r.launcher/src/com/oracle/truffle/r/launcher/RscriptCommand.java
+++ b/com.oracle.truffle.r.launcher/src/com/oracle/truffle/r/launcher/RscriptCommand.java
@@ -25,9 +25,15 @@ package com.oracle.truffle.r.launcher;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
 
+import org.graalvm.options.OptionCategory;
 import org.graalvm.polyglot.Context;
 
+import com.oracle.truffle.r.launcher.Launcher.VersionAction;
+import com.oracle.truffle.r.launcher.RCmdOptions.Client;
 import com.oracle.truffle.r.launcher.RCmdOptions.RCmdOption;
 
 /**
@@ -37,17 +43,13 @@ import com.oracle.truffle.r.launcher.RCmdOptions.RCmdOption;
  *
  */
 public class RscriptCommand {
+
     // CheckStyle: stop system..print check
 
-    private static void preprocessRScriptOptions(RCmdOptions options) {
+    private static void preprocessRScriptOptions(RLauncher launcher, RCmdOptions options) {
         String[] arguments = options.getArguments();
         int resultArgsLength = arguments.length;
         int firstNonOptionArgIndex = options.getFirstNonOptionArgIndex();
-        if (options.getBoolean(RCmdOption.HELP)) {
-            RCmdOptions.printHelpAndExit(RCmdOptions.Client.RSCRIPT);
-        } else if (options.getBoolean(RCmdOption.VERSION)) {
-            printVersionAndExit();
-        }
         // Now reformat the args, setting --slave and --no-restore as per the spec
         ArrayList<String> adjArgs = new ArrayList<>(resultArgsLength + 1);
         adjArgs.add(arguments[0]);
@@ -58,8 +60,9 @@ public class RscriptCommand {
         // Either -e options are set or first non-option arg is a file
         if (options.getStringList(RCmdOption.EXPR) == null) {
             if (firstNonOptionArgIndex == resultArgsLength) {
+                launcher.setHelpCategory(OptionCategory.USER);
                 // does not return
-                RCmdOptions.printHelpAndExit(RCmdOptions.Client.RSCRIPT);
+                launcher.runPolyglotAction();
             } else {
                 if (arguments[firstNonOptionArgIndex].startsWith("-")) {
                     throw RCommand.fatal("file name is missing");
@@ -103,23 +106,32 @@ public class RscriptCommand {
 
     public static int doMain(String[] args, String[] env, InputStream inStream, OutputStream outStream, OutputStream errStream) {
         assert env == null : "re-enble environment variables";
-        // Since many of the options are shared parse them from an RSCRIPT perspective.
-        // Handle --help and --version specially, as they exit.
-        RCmdOptions options = RCmdOptions.parseArguments(RCmdOptions.Client.RSCRIPT, args, false);
-        preprocessRScriptOptions(options);
+
+        ArrayList<String> argsList = new ArrayList<>(Arrays.asList(args));
+        RLauncher launcher = new RLauncher(Client.RSCRIPT) {
+            @Override
+            protected void printVersion() {
+                System.out.print("R scripting front-end version ");
+                System.out.println(RVersionNumber.FULL);
+            }
+        };
+        Map<String, String> polyglotOptions = new HashMap<>();
+        for (int i = 1; i < argsList.size(); i++) {
+            String arg = argsList.get(i);
+            if (launcher.parsePolyglotOption("R", polyglotOptions, arg)) {
+                argsList.remove(i);
+            }
+        }
+        if (launcher.runPolyglotAction()) {
+            return 0;
+        }
+        RCmdOptions options = RCmdOptions.parseArguments(Client.RSCRIPT, argsList.toArray(new String[argsList.size()]), false);
+        preprocessRScriptOptions(launcher, options);
 
         ConsoleHandler consoleHandler = RCommand.createConsoleHandler(options, false, inStream, outStream);
-        try (Context context = Context.newBuilder().arguments("R",
-                        options.getArguments()).in(consoleHandler.createInputStream()).out(outStream).err(errStream).build()) {
+        try (Context context = Context.newBuilder().options(polyglotOptions).arguments("R", options.getArguments()).in(consoleHandler.createInputStream()).out(outStream).err(errStream).build()) {
             consoleHandler.setContext(context);
             return RCommand.readEvalPrint(context, consoleHandler);
         }
     }
-
-    private static void printVersionAndExit() {
-        // TODO Not ok in nested context
-        System.out.print("FastR scripting front-end version ");
-        System.out.println(RVersionNumber.FULL);
-        System.exit(0);
-    }
 }
diff --git a/com.oracle.truffle.r.native/run/Makefile b/com.oracle.truffle.r.native/run/Makefile
index 8f04e6ae6a..30e6a1273d 100644
--- a/com.oracle.truffle.r.native/run/Makefile
+++ b/com.oracle.truffle.r.native/run/Makefile
@@ -76,7 +76,7 @@ $(FASTR_BIN_DIR)/R: Makefile R.sh Rscript.sh Rscript_exec.sh Rclasspath.sh
 	cp Rclasspath.sh $(FASTR_BIN_DIR)/execRextras/Rclasspath
 	chmod +x $(FASTR_BIN_DIR)/exec/R $(FASTR_BIN_DIR)/execRextras/Rscript $(FASTR_BIN_DIR)/Rscript $(FASTR_BIN_DIR)/execRextras/Rclasspath
 	# update R_HOME_DIR to FastR
-	sed -e 's!^\(R_HOME_DIR=\)\(.*\)!\1"$(FASTR_R_HOME)"!' < $(R_SCRIPT) > $(FASTR_BIN_DIR)/R
+	(sed -e 's!^\(R_HOME_DIR=\)\(.*\)!\1"$(FASTR_R_HOME)"!' | sed -e 's/    -h.--help./    --r-help\)/') < $(R_SCRIPT) > $(FASTR_BIN_DIR)/R
 	chmod +x $(FASTR_BIN_DIR)/R 
 	touch $(FASTR_ETC_DIR)/ldpaths
 	sed -e "s|\(R_LIBS_USER=.*-'\)\(.*\)'}|\1$(FASTR_R_HOME)/library'}|" < $(GNUR_HOME)/etc/Renviron > $(FASTR_ETC_DIR)/Renviron
-- 
GitLab