pax_global_header00006660000000000000000000000064141342047640014517gustar00rootroot0000000000000052 comment=6a8a899c62a58a30fb128a7079f02826ed4faee0 node-shell-quote-1.7.3/000077500000000000000000000000001413420476400147145ustar00rootroot00000000000000node-shell-quote-1.7.3/.github/000077500000000000000000000000001413420476400162545ustar00rootroot00000000000000node-shell-quote-1.7.3/.github/workflows/000077500000000000000000000000001413420476400203115ustar00rootroot00000000000000node-shell-quote-1.7.3/.github/workflows/nodejs.yml000066400000000000000000000017351413420476400223240ustar00rootroot00000000000000name: Node CI on: [push, pull_request] jobs: test: strategy: matrix: os: [ubuntu-latest, windows-latest, macos-latest] node-version: ['0.8', '0.10', '0.12', '4.x', '6.x', '8.x', '10.x', '12.x'] exclude: - os: windows-latest node-version: '0.8' runs-on: ${{matrix.os}} steps: - uses: actions/checkout@v1 - name: Use Node.js ${{ matrix.node-version }} uses: actions/setup-node@v1 with: node-version: ${{ matrix.node-version }} - name: Disable strict SSL checks uses: allenevans/set-env@v1.0.0 with: NPM_CONFIG_STRICT_SSL: 'false' if: matrix.node-version == '0.8' - name: Upgrade npm to latest available version run: | curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.35.1/install.sh | bash source $HOME/.nvm/nvm.sh nvm install-latest-npm if: matrix.os != 'windows-latest' - run: npm install - run: npm test node-shell-quote-1.7.3/.npmrc000066400000000000000000000000231413420476400160270ustar00rootroot00000000000000package-lock=false node-shell-quote-1.7.3/CHANGELOG.md000066400000000000000000000025051413420476400165270ustar00rootroot00000000000000# acorn-node change log All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/). ## 1.7.3 * Fix a security issue where the regex for windows drive letters allowed some shell meta-characters to escape the quoting rules. (CVE-2021-42740) ## 1.7.2 * Fix a regression introduced in 1.6.3. This reverts the Windows path quoting fix. ([144e1c2](https://github.com/substack/node-shell-quote/commit/144e1c20cd57549a414c827fb3032e60b7b8721c)) ## 1.7.1 * Fix `$` being removed when not part of an environment variable name. ([@Adman](https://github.com/Admin) in [#32](https://github.com/substack/node-shell-quote/pull/32)) ## 1.7.0 * Add support for parsing `>>` and `>&` redirection operators. ([@forivall](https://github.com/forivall) in [#16](https://github.com/substack/node-shell-quote/pull/16)) * Add support for parsing `<(` process substitution operator. ([@cuonglm](https://github.com/cuonglm) in [#15](https://github.com/substack/node-shell-quote/pull/15)) ## 1.6.3 * Fix Windows path quoting problems. ([@dy](https://github.com/dy) in [#34](https://github.com/substack/node-shell-quote/pull/34)) ## 1.6.2 * Remove dependencies in favour of native methods. ([@zertosh](https://github.com/zertosh) in [#21](https://github.com/substack/node-shell-quote/pull/21)) node-shell-quote-1.7.3/LICENSE000066400000000000000000000021251413420476400157210ustar00rootroot00000000000000The MIT License Copyright (c) 2013 James Halliday (mail@substack.net) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.node-shell-quote-1.7.3/example/000077500000000000000000000000001413420476400163475ustar00rootroot00000000000000node-shell-quote-1.7.3/example/env.js000066400000000000000000000001611413420476400174730ustar00rootroot00000000000000var parse = require('../').parse; var xs = parse('beep --boop="$PWD"', { PWD: '/home/robot' }); console.dir(xs); node-shell-quote-1.7.3/example/op.js000066400000000000000000000001331413420476400173200ustar00rootroot00000000000000var parse = require('../').parse; var xs = parse('beep || boop > /byte'); console.dir(xs); node-shell-quote-1.7.3/example/parse.js000066400000000000000000000001471413420476400200210ustar00rootroot00000000000000var parse = require('../').parse; var xs = parse('a "b c" \\$def \'it\\\'s great\''); console.dir(xs); node-shell-quote-1.7.3/example/quote.js000066400000000000000000000001401413420476400200350ustar00rootroot00000000000000var quote = require('../').quote; var s = quote([ 'a', 'b c d', '$f', '"g"' ]); console.log(s); node-shell-quote-1.7.3/index.js000066400000000000000000000146541413420476400163730ustar00rootroot00000000000000exports.quote = function (xs) { return xs.map(function (s) { if (s && typeof s === 'object') { return s.op.replace(/(.)/g, '\\$1'); } else if (/["\s]/.test(s) && !/'/.test(s)) { return "'" + s.replace(/(['\\])/g, '\\$1') + "'"; } else if (/["'\s]/.test(s)) { return '"' + s.replace(/(["\\$`!])/g, '\\$1') + '"'; } else { return String(s).replace(/([A-Za-z]:)?([#!"$&'()*,:;<=>?@\[\\\]^`{|}])/g, '$1\\$2'); } }).join(' '); }; // '<(' is process substitution operator and // can be parsed the same as control operator var CONTROL = '(?:' + [ '\\|\\|', '\\&\\&', ';;', '\\|\\&', '\\<\\(', '>>', '>\\&', '[&;()|<>]' ].join('|') + ')'; var META = '|&;()<> \\t'; var BAREWORD = '(\\\\[\'"' + META + ']|[^\\s\'"' + META + '])+'; var SINGLE_QUOTE = '"((\\\\"|[^"])*?)"'; var DOUBLE_QUOTE = '\'((\\\\\'|[^\'])*?)\''; var TOKEN = ''; for (var i = 0; i < 4; i++) { TOKEN += (Math.pow(16,8)*Math.random()).toString(16); } exports.parse = function (s, env, opts) { var mapped = parse(s, env, opts); if (typeof env !== 'function') return mapped; return mapped.reduce(function (acc, s) { if (typeof s === 'object') return acc.concat(s); var xs = s.split(RegExp('(' + TOKEN + '.*?' + TOKEN + ')', 'g')); if (xs.length === 1) return acc.concat(xs[0]); return acc.concat(xs.filter(Boolean).map(function (x) { if (RegExp('^' + TOKEN).test(x)) { return JSON.parse(x.split(TOKEN)[1]); } else return x; })); }, []); }; function parse (s, env, opts) { var chunker = new RegExp([ '(' + CONTROL + ')', // control chars '(' + BAREWORD + '|' + SINGLE_QUOTE + '|' + DOUBLE_QUOTE + ')*' ].join('|'), 'g'); var match = s.match(chunker).filter(Boolean); var commented = false; if (!match) return []; if (!env) env = {}; if (!opts) opts = {}; return match.map(function (s, j) { if (commented) { return; } if (RegExp('^' + CONTROL + '$').test(s)) { return { op: s }; } // Hand-written scanner/parser for Bash quoting rules: // // 1. inside single quotes, all characters are printed literally. // 2. inside double quotes, all characters are printed literally // except variables prefixed by '$' and backslashes followed by // either a double quote or another backslash. // 3. outside of any quotes, backslashes are treated as escape // characters and not printed (unless they are themselves escaped) // 4. quote context can switch mid-token if there is no whitespace // between the two quote contexts (e.g. all'one'"token" parses as // "allonetoken") var SQ = "'"; var DQ = '"'; var DS = '$'; var BS = opts.escape || '\\'; var quote = false; var esc = false; var out = ''; var isGlob = false; for (var i = 0, len = s.length; i < len; i++) { var c = s.charAt(i); isGlob = isGlob || (!quote && (c === '*' || c === '?')); if (esc) { out += c; esc = false; } else if (quote) { if (c === quote) { quote = false; } else if (quote == SQ) { out += c; } else { // Double quote if (c === BS) { i += 1; c = s.charAt(i); if (c === DQ || c === BS || c === DS) { out += c; } else { out += BS + c; } } else if (c === DS) { out += parseEnvVar(); } else { out += c; } } } else if (c === DQ || c === SQ) { quote = c; } else if (RegExp('^' + CONTROL + '$').test(c)) { return { op: s }; } else if (RegExp('^#$').test(c)) { commented = true; if (out.length){ return [out, { comment: s.slice(i+1) + match.slice(j+1).join(' ') }]; } return [{ comment: s.slice(i+1) + match.slice(j+1).join(' ') }]; } else if (c === BS) { esc = true; } else if (c === DS) { out += parseEnvVar(); } else out += c; } if (isGlob) return {op: 'glob', pattern: out}; return out; function parseEnvVar() { i += 1; var varend, varname; //debugger if (s.charAt(i) === '{') { i += 1; if (s.charAt(i) === '}') { throw new Error("Bad substitution: " + s.substr(i - 2, 3)); } varend = s.indexOf('}', i); if (varend < 0) { throw new Error("Bad substitution: " + s.substr(i)); } varname = s.substr(i, varend - i); i = varend; } else if (/[*@#?$!_\-]/.test(s.charAt(i))) { varname = s.charAt(i); i += 1; } else { varend = s.substr(i).match(/[^\w\d_]/); if (!varend) { varname = s.substr(i); i = s.length; } else { varname = s.substr(i, varend.index); i += varend.index - 1; } } return getVar(null, '', varname); } }) // finalize parsed aruments .reduce(function(prev, arg){ if (arg === undefined){ return prev; } return prev.concat(arg); },[]); function getVar (_, pre, key) { var r = typeof env === 'function' ? env(key) : env[key]; if (r === undefined && key != '') r = ''; else if (r === undefined) r = '$'; if (typeof r === 'object') { return pre + TOKEN + JSON.stringify(r) + TOKEN; } else return pre + r; } } node-shell-quote-1.7.3/package.json000066400000000000000000000012201413420476400171750ustar00rootroot00000000000000{ "name": "shell-quote", "description": "quote and parse shell commands", "version": "1.7.3", "author": { "name": "James Halliday", "email": "mail@substack.net", "url": "http://substack.net" }, "bugs": "https://github.com/substack/node-shell-quote/issues", "devDependencies": { "tape": "4" }, "homepage": "https://github.com/substack/node-shell-quote", "keywords": [ "command", "parse", "quote", "shell" ], "license": "MIT", "main": "index.js", "repository": { "type": "git", "url": "http://github.com/substack/node-shell-quote.git" }, "scripts": { "test": "tape test/*.js" } } node-shell-quote-1.7.3/readme.markdown000066400000000000000000000044611413420476400177220ustar00rootroot00000000000000# shell-quote Parse and quote shell commands. # example ## quote ``` js var quote = require('shell-quote').quote; var s = quote([ 'a', 'b c d', '$f', '"g"' ]); console.log(s); ``` output ``` a 'b c d' \$f '"g"' ``` ## parse ``` js var parse = require('shell-quote').parse; var xs = parse('a "b c" \\$def \'it\\\'s great\''); console.dir(xs); ``` output ``` [ 'a', 'b c', '\\$def', 'it\'s great' ] ``` ## parse with an environment variable ``` js var parse = require('shell-quote').parse; var xs = parse('beep --boop="$PWD"', { PWD: '/home/robot' }); console.dir(xs); ``` output ``` [ 'beep', '--boop=/home/robot' ] ``` ## parse with custom escape character ``` js var parse = require('shell-quote').parse; var xs = parse('beep --boop="$PWD"', { PWD: '/home/robot' }, { escape: '^' }); console.dir(xs); ``` output ``` [ 'beep', '--boop=/home/robot' ] ``` ## parsing shell operators ``` js var parse = require('shell-quote').parse; var xs = parse('beep || boop > /byte'); console.dir(xs); ``` output: ``` [ 'beep', { op: '||' }, 'boop', { op: '>' }, '/byte' ] ``` ## parsing shell comment ``` js var parse = require('shell-quote').parse; var xs = parse('beep > boop # > kaboom'); console.dir(xs); ``` output: ``` [ 'beep', { op: '>' }, 'boop', { comment: '> kaboom' } ] ``` # methods ``` js var quote = require('shell-quote').quote; var parse = require('shell-quote').parse; ``` ## quote(args) Return a quoted string for the array `args` suitable for using in shell commands. ## parse(cmd, env={}) Return an array of arguments from the quoted string `cmd`. Interpolate embedded bash-style `$VARNAME` and `${VARNAME}` variables with the `env` object which like bash will replace undefined variables with `""`. `env` is usually an object but it can also be a function to perform lookups. When `env(key)` returns a string, its result will be output just like `env[key]` would. When `env(key)` returns an object, it will be inserted into the result array like the operator objects. When a bash operator is encountered, the element in the array with be an object with an `"op"` key set to the operator string. For example: ``` 'beep || boop > /byte' ``` parses as: ``` [ 'beep', { op: '||' }, 'boop', { op: '>' }, '/byte' ] ``` # install With [npm](http://npmjs.org) do: ``` npm install shell-quote ``` # license MIT node-shell-quote-1.7.3/security.md000066400000000000000000000004471413420476400171120ustar00rootroot00000000000000# Security Policy ## Supported Versions Only the latest major version is supported at any given time. ## Reporting a Vulnerability To report a security vulnerability, please use the [Tidelift security contact](https://tidelift.com/security). Tidelift will coordinate the fix and disclosure. node-shell-quote-1.7.3/test/000077500000000000000000000000001413420476400156735ustar00rootroot00000000000000node-shell-quote-1.7.3/test/comment.js000066400000000000000000000012301413420476400176670ustar00rootroot00000000000000var test = require('tape'); var parse = require('../').parse; test('comment', function (t) { t.same(parse('beep#boop'), [ 'beep', { comment: 'boop' } ]); t.same(parse('beep #boop'), [ 'beep', { comment: 'boop' } ]); t.same(parse('beep # boop'), [ 'beep', { comment: 'boop' } ]); t.same(parse('beep # > boop'), [ 'beep', { comment: '> boop' } ]); t.same(parse('beep # "> boop"'), [ 'beep', { comment: '"> boop"' } ]); t.same(parse('beep "#"'), [ 'beep', '#' ]); t.same(parse('beep #"#"#'), [ 'beep', { comment: '"#"#' } ]); t.same(parse('beep > boop # > foo'), [ 'beep', {op: '>'}, 'boop', { comment: '> foo' } ]); t.end(); }); node-shell-quote-1.7.3/test/env.js000066400000000000000000000030771413420476400170300ustar00rootroot00000000000000var test = require('tape'); var parse = require('../').parse; test('expand environment variables', function (t) { t.same(parse('a $XYZ c', { XYZ: 'b' }), [ 'a', 'b', 'c' ]); t.same(parse('a${XYZ}c', { XYZ: 'b' }), [ 'abc' ]); t.same(parse('a${XYZ}c $XYZ', { XYZ: 'b' }), [ 'abc', 'b' ]); t.same(parse('"-$X-$Y-"', { X: 'a', Y: 'b' }), [ '-a-b-' ]); t.same(parse("'-$X-$Y-'", { X: 'a', Y: 'b' }), [ '-$X-$Y-' ]); t.same(parse('qrs"$zzz"wxy', { zzz: 'tuv' }), [ 'qrstuvwxy' ]); t.same(parse("qrs'$zzz'wxy", { zzz: 'tuv' }), [ 'qrs$zzzwxy' ]); t.same(parse("qrs${zzz}wxy"), [ 'qrswxy' ]); t.same(parse("qrs$wxy $"), [ 'qrs', '$' ]); t.same(parse('grep "xy$"'), [ 'grep', 'xy$' ]); t.same(parse("ab$x", { x: 'c' }), [ 'abc' ]); t.same(parse("ab\\$x", { x: 'c' }), [ 'ab$x' ]); t.same(parse("ab${x}def", { x: 'c' }), [ 'abcdef' ]); t.same(parse("ab\\${x}def", { x: 'c' }), [ 'ab${x}def' ]); t.same(parse('"ab\\${x}def"', { x: 'c' }), [ 'ab${x}def' ]); t.end(); }); test('environment variables with metacharacters', function (t) { t.same(parse('a $XYZ c', { XYZ: '"b"' }), [ 'a', '"b"', 'c' ]); t.same(parse('a $XYZ c', { XYZ: '$X', X: 5 }), [ 'a', '$X', 'c' ]); t.same(parse('a"$XYZ"c', { XYZ: "'xyz'" }), [ "a'xyz'c" ]); t.end(); }); test('special shell parameters', function (t) { var chars = '*@#?-$!0_'.split(''); t.plan(chars.length); chars.forEach(function (c) { var env = {}; env[c] = 'xxx'; t.same(parse('a $' + c + ' c', env), [ 'a', 'xxx', 'c' ]); }); }); node-shell-quote-1.7.3/test/env_fn.js000066400000000000000000000010351413420476400175030ustar00rootroot00000000000000var test = require('tape'); var parse = require('../').parse; test('functional env expansion', function (t) { t.plan(4); t.same(parse('a $XYZ c', getEnv), [ 'a', 'xxx', 'c' ]); t.same(parse('a $XYZ c', getEnvObj), [ 'a', { op: '@@' }, 'c' ]); t.same(parse('a${XYZ}c', getEnvObj), [ 'a', { op: '@@' }, 'c' ]); t.same(parse('"a $XYZ c"', getEnvObj), [ 'a ', { op: '@@' }, ' c' ]); function getEnv (key) { return 'xxx'; } function getEnvObj (key) { return { op: '@@' }; } }); node-shell-quote-1.7.3/test/op.js000066400000000000000000000051021413420476400166450ustar00rootroot00000000000000var test = require('tape'); var parse = require('../').parse; test('single operators', function (t) { t.same(parse('beep | boop'), [ 'beep', { op: '|' }, 'boop' ]); t.same(parse('beep|boop'), [ 'beep', { op: '|' }, 'boop' ]); t.same(parse('beep \\| boop'), [ 'beep', '|', 'boop' ]); t.same(parse('beep "|boop"'), [ 'beep', '|boop' ]); t.same(parse('echo zing &'), [ 'echo', 'zing', { op: '&' } ]); t.same(parse('echo zing&'), [ 'echo', 'zing', { op: '&' } ]); t.same(parse('echo zing\\&'), [ 'echo', 'zing&' ]); t.same(parse('echo "zing\\&"'), [ 'echo', 'zing\\&' ]); t.same(parse('beep;boop'), [ 'beep', { op: ';' }, 'boop' ]); t.same(parse('(beep;boop)'), [ { op: '(' }, 'beep', { op: ';' }, 'boop', { op: ')' } ]); t.same(parse('beep>boop'), [ 'beep', { op: '>' }, 'boop' ]); t.same(parse('beep 2>boop'), [ 'beep', '2', { op: '>' }, 'boop' ]); t.same(parse('beep>blip'), [ 'beep', { op: ';;' }, 'boop', { op: '|&' }, 'byte', { op: '>>' }, 'blip' ] ); t.same(parse('beep 2>&1'), [ 'beep', '2', { op: '>&' }, '1' ]); t.same( parse('beep<(boop)'), [ 'beep', { op: '<(' }, 'boop', { op: ')' } ] ); t.same( parse('beep<<(boop)'), [ 'beep', { op: '<' }, { op: '<(' }, 'boop', { op: ')' } ] ); t.end(); }); test('glob patterns', function (t) { t.same( parse('tap test/*.test.js'), [ 'tap', { op: 'glob', pattern: 'test/*.test.js' } ] ); t.same(parse('tap "test/*.test.js"'), ['tap', 'test/*.test.js']); t.end(); }) node-shell-quote-1.7.3/test/parse.js000066400000000000000000000016021413420476400173420ustar00rootroot00000000000000var test = require('tape'); var parse = require('../').parse; test('parse shell commands', function (t) { t.same(parse('a \'b\' "c"'), [ 'a', 'b', 'c' ]); t.same( parse('beep "boop" \'foo bar baz\' "it\'s \\"so\\" groovy"'), [ 'beep', 'boop', 'foo bar baz', 'it\'s "so" groovy' ] ); t.same(parse('a b\\ c d'), [ 'a', 'b c', 'd' ]); t.same(parse('\\$beep bo\\`op'), [ '$beep', 'bo`op' ]); t.same(parse('echo "foo = \\"foo\\""'), [ 'echo', 'foo = "foo"' ]); t.same(parse(''), []); t.same(parse(' '), []); t.same(parse("\t"), []); t.same(parse('a"b c d"e'), [ 'ab c de' ]); t.same(parse('a\\ b"c d"\\ e f'), [ 'a bc d e', 'f' ]); t.same(parse('a\\ b"c d"\\ e\'f g\' h'), [ 'a bc d ef g', 'h' ]); t.same(parse("x \"bl'a\"'h'"), ['x', "bl'ah"]) t.same(parse("x bl^'a^'h'", {}, { escape: '^'}), ['x', "bl'a'h"]); t.end(); }); node-shell-quote-1.7.3/test/quote.js000066400000000000000000000027411413420476400173720ustar00rootroot00000000000000var test = require('tape'); var quote = require('../').quote; test('quote', function (t) { t.equal(quote([ 'a', 'b', 'c d' ]), 'a b \'c d\''); t.equal( quote([ 'a', 'b', "it's a \"neat thing\"" ]), 'a b "it\'s a \\"neat thing\\""' ); t.equal( quote([ '$', '`', '\'' ]), '\\$ \\` "\'"' ); t.equal(quote([]), ''); t.equal(quote(["a\nb"]), "'a\nb'"); t.equal(quote([' #(){}*|][!']), "' #(){}*|][!'"); t.equal(quote(["'#(){}*|][!"]), '"\'#(){}*|][\\!"'); t.equal(quote(["X#(){}*|][!"]), "X\\#\\(\\)\\{\\}\\*\\|\\]\\[\\!"); t.equal(quote(["a\n#\nb"]), "'a\n#\nb'"); t.equal(quote(['><;{}']), '\\>\\<\\;\\{\\}'); t.equal(quote([ 'a', 1, true, false ]), 'a 1 true false'); t.equal(quote([ 'a', 1, null, undefined ]), 'a 1 null undefined'); t.equal(quote([ 'a\\x' ]), 'a\\\\x'); t.end(); }); test('quote ops', function (t) { t.equal(quote([ 'a', { op: '|' }, 'b' ]), 'a \\| b'); t.equal( quote([ 'a', { op: '&&' }, 'b', { op: ';' }, 'c' ]), 'a \\&\\& b \\; c' ); t.end(); }); test('quote windows paths', { skip: 'breaking change, disabled until 2.x' }, function (t) { var path = 'C:\\projects\\node-shell-quote\\index.js' t.equal(quote([path, 'b', 'c d']), 'C:\\projects\\node-shell-quote\\index.js b \'c d\'') t.end() }) test("chars for windows paths don't break out", function (t) { var x = '`:\\a\\b' t.equal(quote([x]), '\\`\\:\\\\a\\\\b') t.end() }) node-shell-quote-1.7.3/test/set.js000066400000000000000000000012431413420476400170240ustar00rootroot00000000000000var test = require('tape'); var parse = require('../').parse; test('set env vars', function (t) { t.same( parse('ABC=444 x y z'), [ 'ABC=444', 'x', 'y', 'z' ] ); t.same( parse('ABC=3\\ 4\\ 5 x y z'), [ 'ABC=3 4 5', 'x', 'y', 'z' ] ); t.same( parse('X="7 8 9" printx'), [ 'X=7 8 9', 'printx' ] ); t.same( parse('X="7 8 9"; printx'), [ 'X=7 8 9', { op: ';' }, 'printx' ] ); t.same( parse('X="7 8 9"; printx', function (key) { t.fail('should not have matched any keys'); }), [ 'X=7 8 9', { op: ';' }, 'printx' ] ); t.end(); });