Added logging, changed some directory structure

This commit is contained in:
2018-01-13 21:33:40 -05:00
parent f079a5f067
commit 8e72ffb917
73656 changed files with 35284 additions and 53718 deletions

View File

@@ -0,0 +1,130 @@
Color = require('./color').Color
Cmd = require('./cmd').Cmd
Opt = require('./opt').Opt
###*
Argument
Unnamed entity. From command line arguments passed as list of unnamed values.
@namespace
@class Presents argument
###
exports.Arg = class Arg
###*
@constructs
@param {COA.Cmd} cmd parent command
###
constructor: (@_cmd) -> @_cmd._args.push @
###*
Set a canonical argument identifier to be used anywhere in text messages.
@param {String} _name argument name
@returns {COA.Arg} this instance (for chainability)
###
name: Opt::name
###*
Set a long description for argument to be used anywhere in text messages.
@param {String} _title argument title
@returns {COA.Arg} this instance (for chainability)
###
title: Cmd::title
###*
Makes an argument accepts multiple values.
Otherwise, the value will be used by the latter passed.
@returns {COA.Arg} this instance (for chainability)
###
arr: Opt::arr
###*
Makes an argument required.
@returns {COA.Arg} this instance (for chainability)
###
req: Opt::req
###*
Set a validation (or value) function for argument.
Value from command line passes through before becoming available from API.
Using for validation and convertion simple types to any values.
@param {Function} _val validating function,
invoked in the context of argument instance
and has one parameter with value from command line
@returns {COA.Arg} this instance (for chainability)
###
val: Opt::val
###*
Set a default value for argument.
Default value passed through validation function as ordinary value.
@param {Object} _def
@returns {COA.Arg} this instance (for chainability)
###
def: Opt::def
###*
Set custom additional completion for current argument.
@param {Function} completion generation function,
invoked in the context of argument instance.
Accepts parameters:
- {Object} opts completion options
It can return promise or any other value treated as result.
@returns {COA.Arg} this instance (for chainability)
###
comp: Cmd::comp
###*
Make argument value inputting stream.
It's add useful validation and shortcut for STDIN.
@returns {COA.Arg} this instance (for chainability)
###
input: Opt::input
###*
Make argument value outputing stream.
It's add useful validation and shortcut for STDOUT.
@returns {COA.Arg} this instance (for chainability)
###
output: Opt::output
_parse: (arg, args) ->
@_saveVal(args, arg)
_saveVal: Opt::_saveVal
_checkParsed: (opts, args) -> not args.hasOwnProperty(@_name)
_usage: ->
res = []
res.push Color('lpurple', @_name.toUpperCase()), ' : ', @_title
if @_req then res.push ' ', Color('lred', '(required)')
res.join ''
_requiredText: -> 'Missing required argument:\n ' + @_usage()
###*
Return rejected promise with error code.
Use in .val() for return with error.
@param {Object} reject reason
You can customize toString() method and exitCode property
of reason object.
@returns {Q.promise} rejected promise
###
reject: Cmd::reject
###*
Finish chain for current option and return parent command instance.
@returns {COA.Cmd} parent command
###
end: Cmd::end
###*
Apply function with arguments in context of arg instance.
@param {Function} fn
@param {Array} args
@returns {COA.Arg} this instance (for chainability)
###
apply: Cmd::apply

View File

@@ -0,0 +1,456 @@
UTIL = require 'util'
PATH = require 'path'
Color = require('./color').Color
Q = require('q')
#inspect = require('eyes').inspector { maxLength: 99999, stream: process.stderr }
###*
Command
Top level entity. Commands may have options and arguments.
@namespace
@class Presents command
###
exports.Cmd = class Cmd
###*
@constructs
@param {COA.Cmd} [cmd] parent command
###
constructor: (cmd) ->
if this not instanceof Cmd
return new Cmd cmd
@_parent cmd
@_cmds = []
@_cmdsByName = {}
@_opts = []
@_optsByKey = {}
@_args = []
@_ext = false
@get: (propertyName, func) ->
Object.defineProperty @::, propertyName,
configurable: true
enumerable: true
get: func
###*
Returns object containing all its subcommands as methods
to use from other programs.
@returns {Object}
###
@get 'api', () ->
if not @_api
@_api = => @invoke.apply @, arguments
for c of @_cmdsByName
do (c) =>
@_api[c] = @_cmdsByName[c].api
@_api
_parent: (cmd) ->
@_cmd = cmd or this
if cmd
cmd._cmds.push @
if @_name then @_cmd._cmdsByName[@_name] = @
@
###*
Set a canonical command identifier to be used anywhere in the API.
@param {String} _name command name
@returns {COA.Cmd} this instance (for chainability)
###
name: (@_name) ->
if @_cmd isnt @ then @_cmd._cmdsByName[_name] = @
@
###*
Set a long description for command to be used anywhere in text messages.
@param {String} _title command title
@returns {COA.Cmd} this instance (for chainability)
###
title: (@_title) -> @
###*
Create new or add existing subcommand for current command.
@param {COA.Cmd} [cmd] existing command instance
@returns {COA.Cmd} new subcommand instance
###
cmd: (cmd) ->
if cmd then cmd._parent @
else new Cmd @
###*
Create option for current command.
@returns {COA.Opt} new option instance
###
opt: -> new (require('./opt').Opt) @
###*
Create argument for current command.
@returns {COA.Opt} new argument instance
###
arg: -> new (require('./arg').Arg) @
###*
Add (or set) action for current command.
@param {Function} act action function,
invoked in the context of command instance
and has the parameters:
- {Object} opts parsed options
- {Array} args parsed arguments
- {Object} res actions result accumulator
It can return rejected promise by Cmd.reject (in case of error)
or any other value treated as result.
@param {Boolean} [force=false] flag for set action instead add to existings
@returns {COA.Cmd} this instance (for chainability)
###
act: (act, force) ->
return @ unless act
if not force and @_act
@_act.push act
else
@_act = [act]
@
###*
Set custom additional completion for current command.
@param {Function} completion generation function,
invoked in the context of command instance.
Accepts parameters:
- {Object} opts completion options
It can return promise or any other value treated as result.
@returns {COA.Cmd} this instance (for chainability)
###
comp: (@_comp) -> @
###*
Apply function with arguments in context of command instance.
@param {Function} fn
@param {Array} args
@returns {COA.Cmd} this instance (for chainability)
###
apply: (fn, args...) ->
fn.apply this, args
@
###*
Make command "helpful", i.e. add -h --help flags for print usage.
@returns {COA.Cmd} this instance (for chainability)
###
helpful: ->
@opt()
.name('help').title('Help')
.short('h').long('help')
.flag()
.only()
.act ->
return @usage()
.end()
###*
Adds shell completion to command, adds "completion" subcommand,
that makes all the magic.
Must be called only on root command.
@returns {COA.Cmd} this instance (for chainability)
###
completable: ->
@cmd()
.name('completion')
.apply(require './completion')
.end()
###*
Allow command to be extendable by external node.js modules.
@param {String} [pattern] Pattern of node.js module to find subcommands at.
@returns {COA.Cmd} this instance (for chainability)
###
extendable: (pattern) ->
@_ext = pattern or true
@
_exit: (msg, code) ->
process.once 'exit', ->
if msg then console.error msg
process.exit code or 0
###*
Build full usage text for current command instance.
@returns {String} usage text
###
usage: ->
res = []
if @_title then res.push @_fullTitle()
res.push('', 'Usage:')
if @_cmds.length then res.push(['', '',
Color('lred', @_fullName()),
Color('lblue', 'COMMAND'),
Color('lgreen', '[OPTIONS]'),
Color('lpurple', '[ARGS]')].join ' ')
if @_opts.length + @_args.length then res.push(['', '',
Color('lred', @_fullName()),
Color('lgreen', '[OPTIONS]'),
Color('lpurple', '[ARGS]')].join ' ')
res.push(
@_usages(@_cmds, 'Commands'),
@_usages(@_opts, 'Options'),
@_usages(@_args, 'Arguments'))
res.join '\n'
_usage: ->
Color('lblue', @_name) + ' : ' + @_title
_usages: (os, title) ->
unless os.length then return
res = ['', title + ':']
for o in os
res.push ' ' + o._usage()
res.join '\n'
_fullTitle: ->
(if @_cmd is this then '' else @_cmd._fullTitle() + '\n') + @_title
_fullName: ->
(if this._cmd is this then '' else @_cmd._fullName() + ' ') + PATH.basename(@_name)
_ejectOpt: (opts, opt) ->
if (pos = opts.indexOf(opt)) >= 0
if opts[pos]._arr
opts[pos]
else
opts.splice(pos, 1)[0]
_checkRequired: (opts, args) ->
if not (@_opts.filter (o) -> o._only and o._name of opts).length
all = @_opts.concat @_args
while i = all.shift()
if i._req and i._checkParsed opts, args
return @reject i._requiredText()
_parseCmd: (argv, unparsed = []) ->
argv = argv.concat()
optSeen = false
while i = argv.shift()
if not i.indexOf '-'
optSeen = true
if not optSeen and /^\w[\w-_]*$/.test(i)
cmd = @_cmdsByName[i]
if not cmd and @_ext
# construct package name to require
if typeof @_ext is 'string'
if ~@_ext.indexOf('%s')
# use formatted string
pkg = UTIL.format(@_ext, i)
else
# just append subcommand name to the prefix
pkg = @_ext + i
else if @_ext is true
# use default scheme: <command>-<subcommand>-<subcommand> and so on
pkg = i
c = @
loop
pkg = c._name + '-' + pkg
if c._cmd is c then break
c = c._cmd
try
cmdDesc = require(pkg)
catch e
if cmdDesc
if typeof cmdDesc == 'function'
# set create subcommand, set its name and apply imported function
@cmd()
.name(i)
.apply(cmdDesc)
.end()
else if typeof cmdDesc == 'object'
# register subcommand
@cmd(cmdDesc)
# set command name
cmdDesc.name(i)
else
throw new Error 'Error: Unsupported command declaration type, ' +
'should be function or COA.Cmd() object'
cmd = @_cmdsByName[i]
if cmd
return cmd._parseCmd argv, unparsed
unparsed.push i
{ cmd: @, argv: unparsed }
_parseOptsAndArgs: (argv) ->
opts = {}
args = {}
nonParsedOpts = @_opts.concat()
nonParsedArgs = @_args.concat()
while i = argv.shift()
# opt
if i isnt '--' and not i.indexOf '-'
if m = i.match /^(--\w[\w-_]*)=(.*)$/
i = m[1]
# suppress 'unknown argument' error for flag options with values
if not @_optsByKey[i]._flag
argv.unshift m[2]
if opt = @_ejectOpt nonParsedOpts, @_optsByKey[i]
if Q.isRejected(res = opt._parse argv, opts)
return res
else
return @reject "Unknown option: #{ i }"
# arg
else
if i is '--'
i = argv.splice(0)
i = if Array.isArray(i) then i else [i]
while a = i.shift()
if arg = nonParsedArgs.shift()
if arg._arr then nonParsedArgs.unshift arg
if Q.isRejected(res = arg._parse a, args)
return res
else
return @reject "Unknown argument: #{ a }"
# set defaults
{
opts: @_setDefaults(opts, nonParsedOpts),
args: @_setDefaults(args, nonParsedArgs)
}
_setDefaults: (params, desc) ->
for i in desc
if i._name not of params and '_def' of i
i._saveVal params, i._def
params
_processParams: (params, desc) ->
notExists = []
for i in desc
n = i._name
if n not of params
notExists.push i
continue
vals = params[n]
delete params[n]
if not Array.isArray vals
vals = [vals]
for v in vals
if Q.isRejected(res = i._saveVal(params, v))
return res
# set defaults
@_setDefaults params, notExists
_parseArr: (argv) ->
Q.when @_parseCmd(argv), (p) ->
Q.when p.cmd._parseOptsAndArgs(p.argv), (r) ->
{ cmd: p.cmd, opts: r.opts, args: r.args }
_do: (input) ->
Q.when input, (input) =>
cmd = input.cmd
[@_checkRequired].concat(cmd._act or []).reduce(
(res, act) ->
Q.when res, (res) ->
act.call(
cmd
input.opts
input.args
res)
undefined
)
###*
Parse arguments from simple format like NodeJS process.argv
and run ahead current program, i.e. call process.exit when all actions done.
@param {Array} argv
@returns {COA.Cmd} this instance (for chainability)
###
run: (argv = process.argv.slice(2)) ->
cb = (code) => (res) =>
if res
@_exit res.stack ? res.toString(), res.exitCode ? code
else
@_exit()
Q.when(@do(argv), cb(0), cb(1)).done()
@
###*
Convenient function to run command from tests.
@param {Array} argv
@returns {Q.Promise}
###
do: (argv) ->
@_do(@_parseArr argv || [])
###*
Invoke specified (or current) command using provided
options and arguments.
@param {String|Array} cmds subcommand to invoke (optional)
@param {Object} opts command options (optional)
@param {Object} args command arguments (optional)
@returns {Q.Promise}
###
invoke: (cmds = [], opts = {}, args = {}) ->
if typeof cmds == 'string'
cmds = cmds.split(' ')
if arguments.length < 3
if not Array.isArray cmds
args = opts
opts = cmds
cmds = []
Q.when @_parseCmd(cmds), (p) =>
if p.argv.length
return @reject "Unknown command: " + cmds.join ' '
Q.all([@_processParams(opts, @_opts), @_processParams(args, @_args)])
.spread (opts, args) =>
@_do({ cmd: p.cmd, opts: opts, args: args })
# catch fails from .only() options
.fail (res) =>
if res and res.exitCode is 0
res.toString()
else
@reject(res)
###*
Return reject of actions results promise with error code.
Use in .act() for return with error.
@param {Object} reject reason
You can customize toString() method and exitCode property
of reason object.
@returns {Q.promise} rejected promise
###
reject: (reason) -> Q.reject(reason)
###*
Finish chain for current subcommand and return parent command instance.
@returns {COA.Cmd} parent command
###
end: -> @_cmd

View File

@@ -0,0 +1,25 @@
colors =
black: '30'
dgray: '1;30'
red: '31'
lred: '1;31'
green: '32'
lgreen: '1;32'
brown: '33'
yellow: '1;33'
blue: '34'
lblue: '1;34'
purple: '35'
lpurple: '1;35'
cyan: '36'
lcyan: '1;36'
lgray: '37'
white: '1;37'
exports.Color = (c, str) ->
# Use \x1B instead of \033 because of CoffeeScript 1.3.x compilation error
[
'\x1B[', colors[c], 'm'
str
'\x1B[m'
].join ''

View File

@@ -0,0 +1,156 @@
###*
Most of the code adopted from the npm package shell completion code.
See https://github.com/isaacs/npm/blob/master/lib/completion.js
###
Q = require 'q'
escape = require('./shell').escape
unescape = require('./shell').unescape
module.exports = ->
@title('Shell completion')
.helpful()
.arg()
.name('raw')
.title('Completion words')
.arr()
.end()
.act (opts, args) ->
if process.platform == 'win32'
e = new Error 'shell completion not supported on windows'
e.code = 'ENOTSUP'
e.errno = require('constants').ENOTSUP
return @reject(e)
# if the COMP_* isn't in the env, then just dump the script
if !process.env.COMP_CWORD? or !process.env.COMP_LINE? or !process.env.COMP_POINT?
return dumpScript(@_cmd._name)
console.error 'COMP_LINE: %s', process.env.COMP_LINE
console.error 'COMP_CWORD: %s', process.env.COMP_CWORD
console.error 'COMP_POINT: %s', process.env.COMP_POINT
console.error 'args: %j', args.raw
# completion opts
opts = getOpts args.raw
# cmd
{ cmd, argv } = @_cmd._parseCmd opts.partialWords
Q.when complete(cmd, opts), (compls) ->
console.error 'filtered: %j', compls
console.log compls.map(escape).join('\n')
dumpScript = (name) ->
fs = require 'fs'
path = require 'path'
defer = Q.defer()
fs.readFile path.resolve(__dirname, 'completion.sh'), 'utf8', (err, d) ->
if err then return defer.reject err
d = d.replace(/{{cmd}}/g, path.basename name).replace(/^\#\!.*?\n/, '')
onError = (err) ->
# Darwin is a real dick sometimes.
#
# This is necessary because the "source" or "." program in
# bash on OS X closes its file argument before reading
# from it, meaning that you get exactly 1 write, which will
# work most of the time, and will always raise an EPIPE.
#
# Really, one should not be tossing away EPIPE errors, or any
# errors, so casually. But, without this, `. <(cmd completion)`
# can never ever work on OS X.
if err.errno == require('constants').EPIPE
process.stdout.removeListener 'error', onError
defer.resolve()
else
defer.reject(err)
process.stdout.on 'error', onError
process.stdout.write d, -> defer.resolve()
defer.promise
getOpts = (argv) ->
# get the partial line and partial word, if the point isn't at the end
# ie, tabbing at: cmd foo b|ar
line = process.env.COMP_LINE
w = +process.env.COMP_CWORD
point = +process.env.COMP_POINT
words = argv.map unescape
word = words[w]
partialLine = line.substr 0, point
partialWords = words.slice 0, w
# figure out where in that last word the point is
partialWord = argv[w] or ''
i = partialWord.length
while partialWord.substr(0, i) isnt partialLine.substr(-1 * i) and i > 0
i--
partialWord = unescape partialWord.substr 0, i
if partialWord then partialWords.push partialWord
{
line: line
w: w
point: point
words: words
word: word
partialLine: partialLine
partialWords: partialWords
partialWord: partialWord
}
complete = (cmd, opts) ->
compls = []
# complete on cmds
if opts.partialWord.indexOf('-')
compls = Object.keys(cmd._cmdsByName)
# Complete on required opts without '-' in last partial word
# (if required not already specified)
#
# Commented out because of uselessness:
# -b, --block suggest results in '-' on cmd line;
# next completion suggest all options, because of '-'
#.concat Object.keys(cmd._optsByKey).filter (v) -> cmd._optsByKey[v]._req
else
# complete on opt values: --opt=| case
if m = opts.partialWord.match /^(--\w[\w-_]*)=(.*)$/
optWord = m[1]
optPrefix = optWord + '='
else
# complete on opts
# don't complete on opts in case of --opt=val completion
# TODO: don't complete on opts in case of unknown arg after commands
# TODO: complete only on opts with arr() or not already used
# TODO: complete only on full opts?
compls = Object.keys cmd._optsByKey
# complete on opt values: next arg case
if not (o = opts.partialWords[opts.w - 1]).indexOf '-'
optWord = o
# complete on opt values: completion
if optWord and opt = cmd._optsByKey[optWord]
if not opt._flag and opt._comp
compls = Q.join compls, Q.when opt._comp(opts), (c, o) ->
c.concat o.map (v) -> (optPrefix or '') + v
# TODO: complete on args values (context aware, custom completion?)
# custom completion on cmds
if cmd._comp
compls = Q.join compls, Q.when(cmd._comp(opts)), (c, o) ->
c.concat o
# TODO: context aware custom completion on cmds, opts and args
# (can depend on already entered values, especially options)
Q.when compls, (compls) ->
console.error 'partialWord: %s', opts.partialWord
console.error 'compls: %j', compls
compls.filter (c) -> c.indexOf(opts.partialWord) is 0

View File

@@ -0,0 +1,5 @@
exports.Cmd = require('./cmd').Cmd
exports.Opt = require('./cmd').Opt
exports.Arg = require('./cmd').Arg
exports.shell = require('./shell')
exports.require = require;

View File

@@ -0,0 +1,243 @@
fs = require 'fs'
Q = require 'q'
Color = require('./color').Color
Cmd = require('./cmd').Cmd
###*
Option
Named entity. Options may have short and long keys for use from command line.
@namespace
@class Presents option
###
exports.Opt = class Opt
###*
@constructs
@param {COA.Cmd} cmd parent command
###
constructor: (@_cmd) -> @_cmd._opts.push @
###*
Set a canonical option identifier to be used anywhere in the API.
@param {String} _name option name
@returns {COA.Opt} this instance (for chainability)
###
name: (@_name) -> @
###*
Set a long description for option to be used anywhere in text messages.
@param {String} _title option title
@returns {COA.Opt} this instance (for chainability)
###
title: Cmd::title
###*
Set a short key for option to be used with one hyphen from command line.
@param {String} _short
@returns {COA.Opt} this instance (for chainability)
###
short: (@_short) -> @_cmd._optsByKey['-' + _short] = @
###*
Set a short key for option to be used with double hyphens from command line.
@param {String} _long
@returns {COA.Opt} this instance (for chainability)
###
long: (@_long) -> @_cmd._optsByKey['--' + _long] = @
###*
Make an option boolean, i.e. option without value.
@returns {COA.Opt} this instance (for chainability)
###
flag: () ->
@_flag = true
@
###*
Makes an option accepts multiple values.
Otherwise, the value will be used by the latter passed.
@returns {COA.Opt} this instance (for chainability)
###
arr: ->
@_arr = true
@
###*
Makes an option required.
@returns {COA.Opt} this instance (for chainability)
###
req: ->
@_req = true
@
###*
Makes an option to act as a command,
i.e. program will exit just after option action.
@returns {COA.Opt} this instance (for chainability)
###
only: ->
@_only = true
@
###*
Set a validation (or value) function for option.
Value from command line passes through before becoming available from API.
Using for validation and convertion simple types to any values.
@param {Function} _val validating function,
invoked in the context of option instance
and has one parameter with value from command line
@returns {COA.Opt} this instance (for chainability)
###
val: (@_val) -> @
###*
Set a default value for option.
Default value passed through validation function as ordinary value.
@param {Object} _def
@returns {COA.Opt} this instance (for chainability)
###
def: (@_def) -> @
###*
Make option value inputting stream.
It's add useful validation and shortcut for STDIN.
@returns {COA.Opt} this instance (for chainability)
###
input: ->
# XXX: hack to workaround a bug in node 0.6.x,
# see https://github.com/joyent/node/issues/2130
process.stdin.pause();
@
.def(process.stdin)
.val (v) ->
if typeof v is 'string'
if v is '-'
process.stdin
else
s = fs.createReadStream v, { encoding: 'utf8' }
s.pause()
s
else v
###*
Make option value outputing stream.
It's add useful validation and shortcut for STDOUT.
@returns {COA.Opt} this instance (for chainability)
###
output: ->
@
.def(process.stdout)
.val (v) ->
if typeof v is 'string'
if v is '-'
process.stdout
else
fs.createWriteStream v, { encoding: 'utf8' }
else v
###*
Add action for current option command.
This action is performed if the current option
is present in parsed options (with any value).
@param {Function} act action function,
invoked in the context of command instance
and has the parameters:
- {Object} opts parsed options
- {Array} args parsed arguments
- {Object} res actions result accumulator
It can return rejected promise by Cmd.reject (in case of error)
or any other value treated as result.
@returns {COA.Opt} this instance (for chainability)
###
act: (act) ->
opt = @
name = @_name
@_cmd.act (opts) ->
if name of opts
res = act.apply @, arguments
if opt._only
Q.when res, (res) =>
@reject {
toString: -> res.toString()
exitCode: 0
}
else
res
@
###*
Set custom additional completion for current option.
@param {Function} completion generation function,
invoked in the context of option instance.
Accepts parameters:
- {Object} opts completion options
It can return promise or any other value treated as result.
@returns {COA.Opt} this instance (for chainability)
###
comp: Cmd::comp
_saveVal: (opts, val) ->
if @_val then val = @_val val
if @_arr
(opts[@_name] or= []).push val
else
opts[@_name] = val
val
_parse: (argv, opts) ->
@_saveVal(
opts,
if @_flag
true
else
argv.shift()
)
_checkParsed: (opts, args) -> not opts.hasOwnProperty @_name
_usage: ->
res = []
nameStr = @_name.toUpperCase()
if @_short
res.push '-', Color 'lgreen', @_short
unless @_flag then res.push ' ' + nameStr
res.push ', '
if @_long
res.push '--', Color 'green', @_long
unless @_flag then res.push '=' + nameStr
res.push ' : ', @_title
if @_req then res.push ' ', Color('lred', '(required)')
res.join ''
_requiredText: -> 'Missing required option:\n ' + @_usage()
###*
Return rejected promise with error code.
Use in .val() for return with error.
@param {Object} reject reason
You can customize toString() method and exitCode property
of reason object.
@returns {Q.promise} rejected promise
###
reject: Cmd::reject
###*
Finish chain for current option and return parent command instance.
@returns {COA.Cmd} parent command
###
end: Cmd::end
###*
Apply function with arguments in context of option instance.
@param {Function} fn
@param {Array} args
@returns {COA.Opt} this instance (for chainability)
###
apply: Cmd::apply

View File

@@ -0,0 +1,10 @@
exports.unescape = (w) ->
w = if w.charAt(0) is '"'
w.replace(/^"|([^\\])"$/g, '$1')
else
w.replace(/\\ /g, ' ')
w.replace(/\\("|'|\$|`|\\)/g, '$1')
exports.escape = (w) ->
w = w.replace(/(["'$`\\])/g,'\\$1')
if w.match(/\s+/) then '"' + w + '"' else w