node/deps/npm/node_modules/read/lib/read.js
isaacs 01d146c29f Merge remote-tracking branch 'ry/v0.6' into v0.6-merge
Conflicts:
	ChangeLog
	Makefile
	deps/npm/AUTHORS
	deps/npm/html/api/bin.html
	deps/npm/html/api/bugs.html
	deps/npm/html/api/commands.html
	deps/npm/html/api/config.html
	deps/npm/html/api/deprecate.html
	deps/npm/html/api/docs.html
	deps/npm/html/api/edit.html
	deps/npm/html/api/explore.html
	deps/npm/html/api/help-search.html
	deps/npm/html/api/init.html
	deps/npm/html/api/install.html
	deps/npm/html/api/link.html
	deps/npm/html/api/load.html
	deps/npm/html/api/ls.html
	deps/npm/html/api/npm.html
	deps/npm/html/api/outdated.html
	deps/npm/html/api/owner.html
	deps/npm/html/api/pack.html
	deps/npm/html/api/prefix.html
	deps/npm/html/api/prune.html
	deps/npm/html/api/publish.html
	deps/npm/html/api/rebuild.html
	deps/npm/html/api/restart.html
	deps/npm/html/api/root.html
	deps/npm/html/api/run-script.html
	deps/npm/html/api/search.html
	deps/npm/html/api/shrinkwrap.html
	deps/npm/html/api/start.html
	deps/npm/html/api/stop.html
	deps/npm/html/api/submodule.html
	deps/npm/html/api/tag.html
	deps/npm/html/api/test.html
	deps/npm/html/api/uninstall.html
	deps/npm/html/api/unpublish.html
	deps/npm/html/api/update.html
	deps/npm/html/api/version.html
	deps/npm/html/api/view.html
	deps/npm/html/api/whoami.html
	deps/npm/html/doc/README.html
	deps/npm/html/doc/adduser.html
	deps/npm/html/doc/bin.html
	deps/npm/html/doc/bugs.html
	deps/npm/html/doc/build.html
	deps/npm/html/doc/bundle.html
	deps/npm/html/doc/cache.html
	deps/npm/html/doc/changelog.html
	deps/npm/html/doc/coding-style.html
	deps/npm/html/doc/completion.html
	deps/npm/html/doc/config.html
	deps/npm/html/doc/deprecate.html
	deps/npm/html/doc/developers.html
	deps/npm/html/doc/disputes.html
	deps/npm/html/doc/docs.html
	deps/npm/html/doc/edit.html
	deps/npm/html/doc/explore.html
	deps/npm/html/doc/faq.html
	deps/npm/html/doc/folders.html
	deps/npm/html/doc/help-search.html
	deps/npm/html/doc/help.html
	deps/npm/html/doc/index.html
	deps/npm/html/doc/init.html
	deps/npm/html/doc/install.html
	deps/npm/html/doc/json.html
	deps/npm/html/doc/link.html
	deps/npm/html/doc/list.html
	deps/npm/html/doc/npm.html
	deps/npm/html/doc/outdated.html
	deps/npm/html/doc/owner.html
	deps/npm/html/doc/pack.html
	deps/npm/html/doc/prefix.html
	deps/npm/html/doc/prune.html
	deps/npm/html/doc/publish.html
	deps/npm/html/doc/rebuild.html
	deps/npm/html/doc/registry.html
	deps/npm/html/doc/removing-npm.html
	deps/npm/html/doc/restart.html
	deps/npm/html/doc/root.html
	deps/npm/html/doc/run-script.html
	deps/npm/html/doc/scripts.html
	deps/npm/html/doc/search.html
	deps/npm/html/doc/semver.html
	deps/npm/html/doc/shrinkwrap.html
	deps/npm/html/doc/star.html
	deps/npm/html/doc/start.html
	deps/npm/html/doc/stop.html
	deps/npm/html/doc/submodule.html
	deps/npm/html/doc/tag.html
	deps/npm/html/doc/test.html
	deps/npm/html/doc/uninstall.html
	deps/npm/html/doc/unpublish.html
	deps/npm/html/doc/update.html
	deps/npm/html/doc/version.html
	deps/npm/html/doc/view.html
	deps/npm/html/doc/whoami.html
	deps/npm/man/man1/npm.1
	deps/npm/man/man3/npm.3
	deps/npm/package.json
	doc/api/url.markdown
	lib/http.js
	src/node_version.h
	test/simple/test-fs-sync-fd-leak.js
2012-05-04 15:12:47 -07:00

152 lines
3.7 KiB
JavaScript

module.exports = read
var buffer = ""
, tty = require("tty")
, StringDecoder = require("string_decoder").StringDecoder
function read (opts, cb) {
if (!cb) cb = opts, opts = {}
var p = opts.prompt || ""
, def = opts.default
, silent = opts.silent
, timeout = opts.timeout
, num = opts.num || null
, delim = opts.delim || "\n"
if (p && def) p += "("+(silent ? "<default hidden>" : def)+") "
// switching into raw mode is a little bit painful.
// avoid if possible.
var r = silent || num || delim !== "\n" ? rawRead : normalRead
if (timeout) {
cb = (function (cb) {
var called = false
var t = setTimeout(function () {
tty.setRawMode(false)
process.stdout.write("\n")
if (def) done(null, def)
else done(new Error("timeout"))
}, timeout)
function done (er, data) {
clearTimeout(t)
if (called) return
// stop reading!
stdin.pause()
called = true
cb(er, data)
}
return done
})(cb)
}
if (p && !process.stdout.write(p)) {
process.stdout.on("drain", function D () {
process.stdout.removeListener("drain", D)
r(def, timeout, delim, silent, num, cb)
})
} else {
process.nextTick(function () {
r(def, timeout, delim, silent, num, cb)
})
}
}
function normalRead (def, timeout, delim, silent, num, cb) {
var stdin = process.openStdin()
, val = ""
, decoder = new StringDecoder("utf8")
stdin.resume()
stdin.on("error", cb)
stdin.on("data", function D (chunk) {
// get the characters that are completed.
val += buffer + decoder.write(chunk)
buffer = ""
// \r has no place here.
// XXX But what if \r is the delim or something dumb like that?
// Meh. If anyone complains about this, deal with it.
val = val.replace(/\r/g, "")
// TODO Make delim configurable
if (val.indexOf(delim) !== -1) {
// pluck off any delims at the beginning.
if (val !== delim) {
var i, l
for (i = 0, l = val.length; i < l; i ++) {
if (val.charAt(i) !== delim) break
}
if (i !== 0) val = val.substr(i)
}
// buffer whatever might have come *after* the delimter
var delimIndex = val.indexOf(delim)
if (delimIndex !== -1) {
buffer = val.substr(delimIndex)
val = val.substr(0, delimIndex)
} else {
buffer = ""
}
stdin.pause()
stdin.removeListener("data", D)
stdin.removeListener("error", cb)
// read(1) trims
val = val.trim() || def
cb(null, val)
}
})
}
function rawRead (def, timeout, delim, silent, num, cb) {
var stdin = process.openStdin()
, val = ""
, decoder = new StringDecoder
tty.setRawMode(true)
stdin.resume()
stdin.on("error", cb)
stdin.on("data", function D (c) {
// \r is my enemy.
c = decoder.write(c).replace(/\r/g, "\n")
switch (c) {
case "": // probably just a \r that was ignored.
break
case "\u0004": // EOF
case delim:
tty.setRawMode(false)
stdin.removeListener("data", D)
stdin.removeListener("error", cb)
val = val.trim() || def
process.stdout.write("\n")
stdin.pause()
return cb(null, val)
case "\u0003": case "\0": // ^C or other signal abort
tty.setRawMode(false)
stdin.removeListener("data", D)
stdin.removeListener("error", cb)
stdin.pause()
return cb(new Error("cancelled"))
break
default: // just a normal char
val += buffer + c
buffer = ""
if (!silent) process.stdout.write(c)
// explicitly process a delim if we have enough chars.
if (num && val.length >= num) D(delim)
break
}
})
}