| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| package org.apache.felix.gogo.shell; |
| |
| import java.io.File; |
| import java.io.InputStreamReader; |
| import java.net.HttpURLConnection; |
| import java.net.URI; |
| import java.net.URL; |
| import java.net.URLConnection; |
| import java.nio.CharBuffer; |
| import java.util.ArrayList; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Set; |
| |
| import org.apache.felix.gogo.options.Option; |
| import org.apache.felix.gogo.options.Options; |
| import org.apache.felix.service.command.CommandProcessor; |
| import org.apache.felix.service.command.CommandSession; |
| import org.osgi.framework.Bundle; |
| import org.osgi.framework.BundleContext; |
| import org.osgi.framework.BundleException; |
| |
| public class Shell |
| { |
| static final String[] functions = { "gosh", "sh", "source", "history" }; |
| |
| private final static URI CWD = new File(".").toURI(); |
| |
| private final URI baseURI; |
| private final BundleContext context; |
| private final CommandProcessor processor; |
| private final History history; |
| |
| private volatile Bundle systemBundle; |
| |
| public Shell(BundleContext context, CommandProcessor processor) |
| { |
| this.context = context; |
| this.processor = processor; |
| String baseDir = context.getProperty("gosh.home"); |
| baseDir = (baseDir == null) ? context.getProperty("user.dir") : baseDir; |
| baseURI = new File(baseDir).toURI(); |
| this.history = new History(); |
| } |
| |
| public Object gosh(final CommandSession session, String[] argv) throws Exception |
| { |
| final String[] usage = { |
| "gosh - execute script with arguments in a new session", |
| " args are available as session variables $1..$9 and $args.", |
| "Usage: gosh [OPTIONS] [script-file [args..]]", |
| " -c --command pass all remaining args to sub-shell", |
| " --nointeractive don't start interactive session", |
| " --login login shell (same session, reads etc/gosh_profile)", |
| " -s --noshutdown don't shutdown framework when script completes", |
| " -q --quiet don't display message-of-the-day", |
| " -x --xtrace echo commands before execution", |
| " -? --help show help", |
| "If no script-file, an interactive shell is started, type $D to exit." }; |
| |
| Option opt = Options.compile(usage).setOptionsFirst(true).parse(argv); |
| List<String> args = opt.args(); |
| |
| boolean login = opt.isSet("login"); |
| boolean interactive = !opt.isSet("nointeractive"); |
| |
| // We grab this bundle as early as possible to avoid having to deal with invalid bundleContexts of this bundle during shutdowns... |
| systemBundle = context.getBundle(0); |
| |
| if (opt.isSet("help")) |
| { |
| opt.usage(); |
| if (login && !opt.isSet("noshutdown")) |
| { |
| shutdown(); |
| } |
| return null; |
| } |
| |
| if (opt.isSet("command") && args.isEmpty()) |
| { |
| throw opt.usageError("option --command requires argument(s)"); |
| } |
| |
| CommandSession newSession = (login ? session : processor.createSession(session.getKeyboard(), session.getConsole(), System.err)); |
| // Make some of the given arguments available to the shell itself... |
| newSession.put(".gosh_login", login); |
| newSession.put(".gosh_interactive", interactive); |
| newSession.put(".gosh_quiet", opt.isSet("quiet")); |
| |
| if (opt.isSet("xtrace")) |
| { |
| newSession.put("echo", true); |
| } |
| |
| if (login && interactive) |
| { |
| URI uri = baseURI.resolve("etc/gosh_profile"); |
| if (!new File(uri).exists()) |
| { |
| URL url = getClass().getResource("/ext/gosh_profile"); |
| if (url == null) |
| { |
| url = getClass().getResource("/gosh_profile"); |
| } |
| uri = (url == null) ? null : url.toURI(); |
| } |
| if (uri != null) |
| { |
| source(session, uri.toString()); |
| } |
| } |
| |
| // export variables starting with upper-case to newSession |
| for (String key : getVariables(session)) |
| { |
| if (key.matches("[.]?[A-Z].*")) |
| { |
| newSession.put(key, session.get(key)); |
| } |
| } |
| |
| Object result = null; |
| |
| if (args.isEmpty()) |
| { |
| if (interactive) |
| { |
| result = console(newSession); |
| } |
| } |
| else |
| { |
| CharSequence program; |
| |
| if (opt.isSet("command")) |
| { |
| StringBuilder buf = new StringBuilder(); |
| for (String arg : args) |
| { |
| if (buf.length() > 0) |
| { |
| buf.append(' '); |
| } |
| buf.append(arg); |
| } |
| program = buf; |
| } |
| else |
| { |
| URI script = cwd(session).resolve(args.remove(0)); |
| |
| // set script arguments |
| newSession.put("0", script); |
| newSession.put("args", args); |
| |
| for (int i = 0; i < args.size(); ++i) |
| { |
| newSession.put(String.valueOf(i + 1), args.get(i)); |
| } |
| |
| program = readScript(script); |
| } |
| |
| result = newSession.execute(program); |
| } |
| |
| if (login && interactive) |
| { |
| if (opt.isSet("noshutdown")) |
| { |
| System.out.println("gosh: stopping shell"); |
| } |
| else |
| { |
| System.out.println("gosh: stopping shell and framework"); |
| shutdown(); |
| } |
| } |
| |
| return result; |
| } |
| |
| public Object sh(final CommandSession session, String[] argv) throws Exception |
| { |
| return gosh(session, argv); |
| } |
| |
| private void shutdown() throws BundleException |
| { |
| if (systemBundle != null) |
| { |
| systemBundle.stop(); |
| systemBundle = null; |
| } |
| } |
| |
| public Object source(CommandSession session, String script) throws Exception |
| { |
| URI uri = cwd(session).resolve(script); |
| session.put("0", uri); |
| try |
| { |
| return session.execute(readScript(uri)); |
| } |
| finally |
| { |
| session.put("0", null); // API doesn't support remove |
| } |
| } |
| |
| private Object console(CommandSession session) |
| { |
| Console console = new Console(session, history); |
| console.run(); |
| return null; |
| } |
| |
| private CharSequence readScript(URI script) throws Exception |
| { |
| CharBuffer buf = CharBuffer.allocate(4096); |
| StringBuilder sb = new StringBuilder(); |
| |
| URLConnection conn = script.toURL().openConnection(); |
| |
| try (InputStreamReader in = new InputStreamReader(conn.getInputStream())) |
| { |
| while (in.read(buf) > 0) |
| { |
| buf.flip(); |
| sb.append(buf); |
| buf.clear(); |
| } |
| } |
| finally |
| { |
| if (conn instanceof HttpURLConnection) |
| { |
| ((HttpURLConnection) conn).disconnect(); |
| } |
| } |
| |
| return sb; |
| } |
| |
| @SuppressWarnings("unchecked") |
| static Set<String> getVariables(CommandSession session) |
| { |
| return (Set<String>) session.get(".variables"); |
| } |
| |
| static URI cwd(CommandSession session) |
| { |
| Object cwd = session.get("_cwd"); // _cwd is set by felixcommands:cd |
| |
| if (cwd instanceof URI) |
| { |
| return (URI) cwd; |
| } |
| else if (cwd instanceof File) |
| { |
| return ((File) cwd).toURI(); |
| } |
| else |
| { |
| return CWD; |
| } |
| } |
| |
| public String[] history() |
| { |
| Iterator<String> history = this.history.getHistory(); |
| List<String> lines = new ArrayList<>(); |
| for (int i = 1; history.hasNext(); i++) |
| { |
| lines.add(String.format("%5d %s", i, history.next())); |
| } |
| return lines.toArray(new String[lines.size()]); |
| } |
| } |