/** * $Date$ * */ package org.xmlsh.sh.shell; import static org.xmlsh.util.CharAttr.ATTR_ESCAPED; import java.io.IOException; import java.util.Collection; import java.util.List; import java.util.Map.Entry; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.xmlsh.core.CoreException; import org.xmlsh.core.EvalEnv; import org.xmlsh.core.InvalidArgumentException; import org.xmlsh.core.UnexpectedException; import org.xmlsh.core.Variables; import org.xmlsh.core.XValue; import org.xmlsh.core.XVariable; import org.xmlsh.sh.core.CharAttrs; import org.xmlsh.sh.core.EvalUtils; import org.xmlsh.types.XDMTypeFamily; import org.xmlsh.util.NameValueMap; import org.xmlsh.util.Util; import org.xmlsh.util.XMLUtils; import org.xmlsh.xpath.EvalDefinition; import org.xmlsh.xpath.ThreadLocalShell; import net.sf.saxon.s9api.Processor; import net.sf.saxon.s9api.QName; import net.sf.saxon.s9api.SaxonApiException; import net.sf.saxon.s9api.XQueryCompiler; import net.sf.saxon.s9api.XQueryEvaluator; import net.sf.saxon.s9api.XQueryExecutable; import net.sf.saxon.s9api.XdmValue; public class Expander { private static Logger mLogger = LogManager.getLogger(); public Shell mShell; /* * Attribute enums */ /* this"Is a 'string\' in' a $var "string *.x "*.y" \*.z */ public Expander(Shell shell) { mShell = shell; } /* * Expand a value into a Results buffer * Used for combining possible joined values by repeated calls */ public ParseResult expandValueToResult(XValue value, EvalEnv env, ParseResult result) throws IOException, CoreException { assert (result != null); if(!env.preserveValue() && value.isAtomic()) result = expandStringToResult(value.toString(), env, result); else result.add(value, env.preserveValue()); return result; } /* * Expand a string to a list of XValues by * 1) Parsing Quotes * 2) Expanding Variables * 3) Tokenizing by IFS (expand word) and combining adjacent words * 4) globbing */ public List<XValue> expandStringToList(String arg, EvalEnv env) throws IOException, CoreException { ParseResult result = new ParseResult(); return expandResultToList(env, expandStringToResult(arg, env, result)); } public List<XValue> expandResultToList(EvalEnv env, ParseResult result) { mLogger.entry(env, result); assert (result != null); List<XValue> xvresult = result.expandWild(env, mShell.getCurdir()); if(xvresult == null) return null; if(env.expandSequences()) xvresult = Util.expandSequences(xvresult); else xvresult = Util.combineSequence(xvresult); return mLogger.exit(xvresult); } /* * Expand a single word value : * <{{ ... }}> is a hard multi line quote with nothing inside touched * <[ ... ]> is an XQuery expression * $xxx variable expressions * "literal" simpmle literal * "$var literal $var" quoted mixed literal * * If bPreserve is set then this is inside {value} which is used to * eval/expand the value but do NO substitution * NO globbing is done */ public ParseResult expandStringToResult(String arg, EvalEnv env, ParseResult result) throws IOException, CoreException { mLogger.entry(arg, env, result); assert (result != null); CharAttrs curAttr = env.asCharAttrs(); // Special case for "$@" which does NOT 'stringify' arguments /* * if( arg.equals("\"$@\"")){ * * result = EvalUtils.evalVarToResult(mShell, "@", env, curAttr , result); * if(curAttr.isSoftQuote()) * result.resetIfEmpty(); * return result ; * * } */ // <{ big quotes }> if(arg.startsWith("<{{") && arg.endsWith("}}>")) { // Add as a raw value result.add(XValue.newXValue(arg.substring(3, arg.length() - 3)), true); return mLogger.exit(result); } // <[ XEXPR ]> if(arg.startsWith("<[") && arg.endsWith("]>")) { result.add(parseXExpr(mShell, arg.substring(2, arg.length() - 2)), true); return mLogger.exit(result); } char c; int i; for(i = 0; i < arg.length(); i++) { c = arg.charAt(i); if(env.parseQuotes()) { // Quote - if in quotes then clear only the matching quote if(CharAttrs.isQuote(c)) { CharAttrs ca = CharAttrs.valueOf(c); if(curAttr.isQuote()) { // in quotes curAttr.clear(ca); if(curAttr.isQuote()) result.append(c, curAttr); } else { result.append((String) null, curAttr); curAttr.set(ca); } continue; } // Escape // foo\bar -> foobar // "foo\bar" -> "foo\bar" // "foo\\bar" -> "foo\bar" // 'foo\\bar' -> 'foo\\bar' /* * http://pubs.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02. * html */ else if(c == '\\') { if(curAttr.isHardQuote()) result.append(c, curAttr); else if(i < arg.length()) { char nextc = arg.charAt(++i); if(curAttr.isSoftQuote()) { switch(nextc){ case '$': case '`': case '"': case '\\': case '\n': break; default: result.append(c, curAttr); break; } } // For one char we escape CharAttrs cAttr = CharAttrs.newInstance(ATTR_ESCAPED); cAttr.set(curAttr); result.append(nextc, cAttr); } continue; } } if(c == '$' && env.expandVar() && curAttr.isEvalable()) { if(++i == arg.length()) { result.append('$', curAttr); // Special case of a single "$" break; } StringBuffer sbv = new StringBuffer(); if(arg.charAt(i) == '{') { i = EvalUtils.readToMatching(arg, i, sbv, '}'); } else { // Speical case // $? $* $@ $$ $0...$9 c = arg.charAt(i); if(c == '?' || c == '@' || c == '$' || c == '#' || c == '*' || c == '!' || Character.isDigit(c)) { boolean bKeepGoing; do { bKeepGoing = false; sbv.append(c); // Special case for $<dig><dig>... // NOTE: Differs from sh/bsh/ksh - $11 is [arg 11] not [arg 1]1 // if(Character.isDigit(c)) { if(i < arg.length() - 1 && Character.isDigit(c = arg.charAt(i + 1))) { i++; bKeepGoing = true; } } } while(bKeepGoing); } else { // Eat up all a-zA-Z_ for(; i < arg.length(); i++) { c = arg.charAt(i); if(Util.isIdentifier(c)) sbv.append(c); else { i--; // back up break; } } } } String var = sbv.toString(); if(!Util.isBlank(var)) { // get value from single variable, parse and field split // guarentees no null values and empty unquoted strings were removed result = EvalUtils.evalVarToResult(mShell, var, env, curAttr, result); if(curAttr.isSoftQuote() && var.equals("@")) result.resetIfEmpty(); } else result.append('$', curAttr); } else { result.append(c, curAttr); } } if(!env.joinValues()) result.flush(); return mLogger.exit(result); } private XValue parseXExpr(Shell shell, String arg) throws IOException, CoreException { mLogger.entry(shell, arg); Processor processor = Shell.getProcessor(); XQueryCompiler compiler = processor.newXQueryCompiler(); compiler.setModuleURIResolver(new ShellModuleURIResolver(mShell)); // Declare the extension function namespace // This can be overridden by user declarations compiler.declareNamespace("xmlsh", EvalDefinition.kXMLSH_EXT_NAMESPACE); NameValueMap<String> ns = shell.getEnv().getNamespaces(); if(ns != null) { for(String prefix : ns.keySet()) { String uri = ns.get(prefix); compiler.declareNamespace(prefix, uri); } } List<XValue> args = shell.getArgs(); XQueryExecutable expr = null; StringBuffer sb = new StringBuffer(); Variables variables = shell.getEnv().getVars(); Collection<String> varnames = variables.getVarNames(); NameValueMap<XdmValue> usedVars = new NameValueMap<XdmValue>(); for(String name : varnames) { XVariable var = variables.get(name); XdmValue xdmValue = convertVar(var); if(xdmValue != null) { usedVars.put(var.getName(), xdmValue); sb.append("declare variable $").append(name).append(" external ;\n"); } } // 2010-02-04 // Express each positional parameter as $_1 $_2 ... // then each int i = 0; for(XValue xv : args) { i++; XdmValue xdmValue = convertValue(xv); if(xdmValue != null) { String name = "_" + i; usedVars.put(name, xdmValue); sb.append("declare variable $" + name + " external;\n"); } } sb.append(arg); Shell saved_shell = ThreadLocalShell.set(shell); try { expr = compiler.compile(sb.toString()); XQueryEvaluator eval = expr.load(); for(Entry<String, XdmValue> entry : usedVars.entrySet()) { XdmValue v = entry.getValue(); eval.setExternalVariable(new QName(entry.getKey()), v); } XdmValue result = eval.evaluate(); return mLogger.exit(XDMTypeFamily.getInstance().getXValue(result)); } catch (SaxonApiException e) { String msg = "Error expanding xml expression: " + arg; mLogger.warn(msg, e); mShell.printErr(msg, e); throw new CoreException(msg, e); } finally { ThreadLocalShell.set(saved_shell); } } private XdmValue convertValue(XValue xv) throws InvalidArgumentException, UnexpectedException { if(xv.isNull()) return XMLUtils.emptySequence(); if(xv.isXdmItem() || xv.canConvert(XdmValue.class) >= 0) return xv.toXdmValue(); return null; } public XdmValue convertVar(XVariable var) throws InvalidArgumentException, UnexpectedException { if(!var.isNull()) return convertValue(var.getValue()); return null; } } // // // Copyright (C) 2008-2014 David A. Lee. // // The contents of this file are subject to the "Simplified BSD License" (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.opensource.org/licenses/bsd-license.php // // Software distributed under the License is distributed on an "AS IS" basis, // WITHOUT WARRANTY OF ANY KIND, either express or implied. // See the License for the specific language governing rights and limitations // under the License. // // The Original Code is: all this file. // // The Initial Developer of the Original Code is David A. Lee // // Portions created by (your name) are Copyright (C) (your legal entity). All // Rights Reserved. // // Contributor(s): none. //