diff --git a/lib/tput.js b/lib/tput.js index 64ac6bc..fad6ecc 100644 --- a/lib/tput.js +++ b/lib/tput.js @@ -33,54 +33,39 @@ function Tput(options) { this.options = options; this.term = options.term || process.env.TERM; - this.data = null; - this.info = {}; - this.methods = null; - this.termcap = null; this.debug = options.debug; this.padding = options.padding; this.extended = options.extended; this.printf = options.printf; + this.termcap = options.termcap; try { - if (options.termcap) { - this.readTermcap(); + if (this.termcap) { this.compileTermcap(); } else { - this.readTerminfo(); - this.compile(); + this.compileTerminfo(); } } catch (e) { - this.fallback(); + this.term = 'vt102'; + this.compileTermcap(); } } -Tput.prototype.fallback = function() { - delete this.term; - delete this.data; - delete this.info; - delete this.methods; - delete this.termcap; +/** + * Terminfo + */ - this.term = 'vt102'; - this.readTermcap(Tput.vt102); - this.compileTermcap(); -}; - -Tput.prototype.readTerminfo = function() { - // if (this.data) return; - - var file = path.resolve( - '/usr/share/terminfo', - path.basename(this.term[0]), - path.basename(this.term) - ); - - this.data = fs.readFileSync(file); - this.info = this.parseTerminfo(this.data); - - return this.info; +Tput.prototype.readTerminfo = function(data) { + if (!data) { + var file = path.resolve( + '/usr/share/terminfo', + path.basename(this.term[0]), + path.basename(this.term) + ); + data = fs.readFileSync(file); + } + return this.parseTerminfo(data); }; /** @@ -200,10 +185,10 @@ Tput.prototype.parseTerminfo = function(data) { l = data.length; if (i < l - 1) { var extended = this.parseExtended(data.slice(i)); + // info.header.extended = extended.header; ['bools', 'numbers', 'strings'].forEach(function(key) { merge(info[key], extended[key]); }); - // info.extendedHexer = extended.header; } } @@ -387,78 +372,86 @@ Tput.prototype.parseExtended = function(data) { return info; }; +Tput.prototype.compileTerminfo = function() { + return this.compile.apply(this, arguments); +}; + /** * Compiler - terminfo cap->javascript */ -Tput.prototype.compile = function(key) { - var self = this - , info = this.info; +Tput.prototype.compile = function(info, inject) { + var self = this; + + if (arguments.length === 0) { + info = this.readTerminfo(); + } if (!info) { throw new Error('Terminal not found.'); } if (this.debug) { - console.log(this.info); + console.log(info); } - this.methods = {}; - this.info.all = {}; + info.all = {}; + info.methods = {}; - Object.keys(info.bools).forEach(function(key) { - info.all[key] = info.bools[key]; - }); - - Object.keys(info.numbers).forEach(function(key) { - info.all[key] = info.numbers[key]; - }); - - Object.keys(info.strings).forEach(function(key) { - info.all[key] = info.strings[key]; - }); - - Object.keys(info.all).forEach(function(key) { - if (self.debug) { - console.log('Compiling %s: %s', key, JSON.stringify(info.all[key])); - } - self.methods[key] = self._compile(info.all[key]); + ['bools', 'numbers', 'strings'].forEach(function(type) { + Object.keys(info[type]).forEach(function(key) { + info.all[key] = info[type][key]; + if (self.debug) { + console.log('Compiling %s: %s', key, JSON.stringify(info.all[key])); + } + info.methods[key] = self._compile(info.all[key]); + }); }); Tput.bools.forEach(function(key) { - if (self.methods[key] == null) self.methods[key] = false; + if (info.methods[key] == null) info.methods[key] = false; }); Tput.numbers.forEach(function(key) { - if (self.methods[key] == null) self.methods[key] = -1; + if (info.methods[key] == null) info.methods[key] = -1; }); Tput.strings.forEach(function(key) { - if (!self.methods[key]) self.methods[key] = noop; + if (!info.methods[key]) info.methods[key] = noop; }); - Object.keys(this.methods).forEach(function(key) { + Object.keys(info.methods).forEach(function(key) { var alias = Tput.alias[key]; - if (alias) { - if (!Array.isArray(alias)) alias = [alias]; - alias.forEach(function(alias) { - if (self.methods[alias] && alias !== key && alias !== 'ML') { - // We can ignore `lines` and `ML` here. - throw new Error('Alias collision: ' + key + ' -> ' + alias); - } - self.methods[alias] = self.methods[key]; - }); - } + if (!alias) return; + if (!Array.isArray(alias)) alias = [alias]; + alias.forEach(function(alias) { + if (info.methods[alias] && alias !== key && alias !== 'ML') { + // We can ignore `lines` and `ML` here. + throw new Error('Alias collision: ' + key + ' -> ' + alias); + } + info.methods[alias] = info.methods[key]; + }); }); - Object.keys(self.methods).forEach(function(key) { - if (typeof self.methods[key] !== 'function') { - self[key] = self.methods[key]; + if (inject !== false) { + this.inject(info); + } + + return info; +}; + +Tput.prototype.inject = function(info) { + var self = this + , methods = info.methods || info; + + Object.keys(methods).forEach(function(key) { + if (typeof methods[key] !== 'function') { + self[key] = methods[key]; return; } self[key] = function() { var args = Array.prototype.slice.call(arguments); - return self.methods[key].call(self, args); + return methods[key].call(self, args); }; }); }; @@ -871,7 +864,7 @@ Tput.prototype._compile = function(val) { // arithmetic is possible using the stack. // $ man termcap - if (this.options.termcap) { + if (this.termcap) { // %r Single parameter capability if (read(/^%r/)) { expr('()'); @@ -991,19 +984,13 @@ Tput.prototype._parsePadding = function(code, print, done) { */ Tput.prototype.readTermcap = function(data) { - // if (this.termcap) return; - - this.termcap = {}; - - this.termcap.data = data + var data = data || process.env.TERMCAP || tryRead('/etc/termcap') || Tput.vt102; - this.termcap.terms = this.parseTermcap(this.termcap.data); - this.termcap.info = this.termcap.terms[this.term]; - - return this.termcap; + var terms = this.parseTermcap(data); + return terms[this.term]; }; /** @@ -1092,15 +1079,18 @@ Tput.prototype.parseTermcap = function(data) { * man termcap */ -Tput.prototype.translateTermcap = function() { +Tput.prototype.translateTermcap = function(info) { var self = this - , info = this.termcap.info , out = {}; if (!info) { throw new Error('Terminal not found.'); } + if (this.debug) { + console.log(info); + } + ['name', 'names', 'desc'].forEach(function(key) { out[key] = info[key]; }); @@ -1112,13 +1102,15 @@ Tput.prototype.translateTermcap = function() { Object.keys(alias).forEach(function(key) { var a = alias[key].slice() - , tc = a.terminfo || a.splice(1, 1)[0]; + , tc = a.termcap || a.splice(1, 1)[0]; out[tc] = [key].concat(a); }); return out; })(); + // Translate termcap cap names to terminfo cap names. + // e.g. `up` -> `cursor_up` ['bools', 'numbers', 'strings'].forEach(function(key) { out[key] = {}; Object.keys(info[key]).forEach(function(cap) { @@ -1131,9 +1123,14 @@ Tput.prototype.translateTermcap = function() { return out; }; -Tput.prototype.compileTermcap = function() { - this.info = this.translateTermcap(); - this.compile(); +Tput.prototype.compileTermcap = function(info) { + if (arguments.length === 0) { + info = this.readTermcap(); + } + + info = this.translateTermcap(info); + + return this.compile(info); }; /** @@ -1291,7 +1288,8 @@ delete Tput.alias.numbers; merge(Tput.alias, Tput.alias.strings); delete Tput.alias.strings; -// Make sure there are no collisions between cap and tcap. +// Make sure there are no collisions between terminfo names +// and termcap names. Terminfo takes precedence. Tput._vec = Object.keys(Tput.alias).map(function(key) { return Array.isArray(Tput.alias[key]) ? Tput.alias[key][0] @@ -1306,10 +1304,13 @@ Object.keys(Tput.alias).forEach(function(key) { var alias = Tput.alias[key] , tcap = alias[1]; + alias.name = key; + alias.terminfo = alias[0]; + alias.termcap = alias[1]; + if (!tcap) return; if (~Tput._vec.indexOf(tcap)) { - alias.termcap = tcap; alias.splice(1, 1); } }); @@ -1319,7 +1320,7 @@ assert.notEqual(Tput.alias.exit_delete_mode[1], 'ed'); // assert.notEqual(Tput.alias.set_lr_margin[1], 'ML'); /** - * Fallback Termcap + * Fallback Termcap Entry */ Tput.vt102 = ''