From 00ee15a32f6796a7d13e8a6318fcfe3e46d5e1af Mon Sep 17 00:00:00 2001 From: Christopher Jeffrey Date: Sat, 24 Aug 2013 22:25:38 -0500 Subject: [PATCH] better terminfo/cap discovery. fixes #17. --- lib/tput.js | 167 +++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 125 insertions(+), 42 deletions(-) diff --git a/lib/tput.js b/lib/tput.js index ec42b1a..c954af3 100644 --- a/lib/tput.js +++ b/lib/tput.js @@ -124,6 +124,19 @@ Tput.prototype._useInternalCap = function(name) { * Terminfo */ +Tput.ipaths = [ + process.env.TERMINFO || '', + (process.env.TERMINFO_DIRS || '').split(':'), + (process.env.HOME || '') + '/.terminfo', + '/usr/share/terminfo', + '/usr/share/lib/terminfo', + '/usr/lib/terminfo', + '/usr/local/share/terminfo', + '/usr/local/share/lib/terminfo', + '/usr/local/lib/terminfo', + '/usr/local/ncurses/lib/terminfo' +]; + Tput.prototype.readTerminfo = function(term) { var term = term || this.terminal , data @@ -143,33 +156,52 @@ Tput.prototype.readTerminfo = function(term) { Tput._prefix = Tput.prototype._prefix = function(term) { - var TERMINFO = process.env.TERMINFO || '' - , TERMINFO_DIRS = process.env.TERMINFO_DIRS || '' - , HOME = process.env.HOME || ''; + // If we have a terminfoFile, or our + // term looks like a filename, use it. + if (term) { + if (~term.indexOf(path.sep)) { + return term; + } + if (this.terminfoFile) { + return this.terminfoFile; + } + } - return (term && ~term.indexOf(path.sep) && term) - || (term && this.terminfoFile) - || this._tprefix(this.terminfoPrefix, term) - || this._tprefix(TERMINFO, term) - || this._tprefix(TERMINFO_DIRS.split(':'), term) - || this._tprefix(HOME + '/.terminfo', term) - || this._tprefix('/usr/share/terminfo', term) - || this._tprefix('/usr/share/lib/terminfo', term) - || this._tprefix('/usr/lib/terminfo', term) - || this._tprefix('/usr/local/share/terminfo', term) - || this._tprefix('/usr/local/share/lib/terminfo', term) - || this._tprefix('/usr/local/lib/terminfo', term) - || this._tprefix('/usr/local/ncurses/lib/terminfo', term) - || (function() { throw new Error('Terminfo directory not found.'); })(); + var paths = Tput.ipaths.slice() + , file + , i; + + if (this.terminfoPrefix) { + paths.unshift(this.terminfoPrefix); + } + + // Try exact matches. + for (i = 0; i < paths.length; i++) { + file = this._tprefix(paths[i], term); + if (file) return file; + } + + // Try similar matches. + for (i = 0; i < paths.length; i++) { + file = this._tprefix(paths[i], term, true); + if (file) return file; + } + + // Not found. + throw new Error('Terminfo directory not found.'); }; Tput._tprefix = -Tput.prototype._tprefix = function(prefix, term) { +Tput.prototype._tprefix = function(prefix, term, soft) { if (!prefix) return; var file + , dir , ch - , i; + , i + , sdiff + , sfile + , list; if (Array.isArray(prefix)) { for (i = 0; i < prefix.length; i++) { @@ -179,18 +211,40 @@ Tput.prototype._tprefix = function(prefix, term) { return; } - if (!term) { - file = path.resolve(prefix, 'x'); + var find = function(word) { + var file, ch; + + file = path.resolve(prefix, word[0]); try { fs.statSync(file); - return prefix; + return file; } catch (e) { ; } - file = path.resolve(prefix, '78'); + + ch = word[0].charCodeAt(0).toString(16); + if (ch.length < 2) ch = '0' + ch; + + file = path.resolve(prefix, ch); try { fs.statSync(file); - return prefix; + return file; + } catch (e) { + ; + } + }; + + if (!term) { + // Make sure the directory's sub-directories + // are all one-letter, or hex digits. + // return find('x') ? prefix : null; + try { + dir = fs.readdirSync(prefix).filter(function(file) { + return file.length !== 1 && !/^[0-9a-fA-F]{2}$/.test(file); + }); + if (!dir.length) { + return prefix; + } } catch (e) { ; } @@ -198,19 +252,33 @@ Tput.prototype._tprefix = function(prefix, term) { } term = path.basename(term); + dir = find(term); - file = path.resolve(prefix, term[0], term); - try { - fs.statSync(file); - return file; - } catch (e) { - ; + if (!dir) return; + + if (soft) { + try { + list = fs.readdirSync(dir); + } catch (e) { + return; + } + + list.forEach(function(file) { + if (file.indexOf(term) === 0) { + var diff = file.length - term.length; + if (!sfile || diff < sdiff) { + sdiff = diff; + sfile = file; + } + } + }); + + return sfile && (soft || sdiff === 0) + ? path.resolve(dir, sfile) + : null; } - ch = term[0].charCodeAt(0).toString(16); - if (ch.length < 2) ch = '0' + ch; - - file = path.resolve(prefix, ch, term); + file = path.resolve(dir, term); try { fs.statSync(file); return file; @@ -1154,6 +1222,14 @@ Tput.prototype._print = function(code, print, done) { * Termcap */ +Tput.cpaths = [ + process.env.TERMCAP || '', + (process.env.TERMPATH || '').split(/[: ]/), + (process.env.HOME || '') + '/.termcap', + '/usr/share/misc/termcap', + '/etc/termcap' +]; + Tput.prototype.readTermcap = function(term) { var self = this , term = term || this.terminal @@ -1162,7 +1238,9 @@ Tput.prototype.readTermcap = function(term) { , HOME = process.env.HOME || '' , terms , term_ - , root; + , root + , paths + , i; // Termcap has a bunch of terminals usually stored in one file/string, // so we need to find the one containing our desired terminal. @@ -1176,13 +1254,18 @@ Tput.prototype.readTermcap = function(term) { term = Object.keys(terms)[0]; } } else { - terms = this._tryCap(this.termcapFile, term) - || this._tryCap(TERMCAP, term) - || this._tryCap(TERMPATH && TERMPATH.split(/[: ]/), term) - || this._tryCap(HOME + '/.termcap', term) - || this._tryCap('/usr/share/misc/termcap', term) - || this._tryCap('/etc/termcap', term) - || this._tryCap(Tput.termcap, term); + paths = Tput.cpaths.slice(); + + if (this.termcapFile) { + paths.unshift(this.termcapFile); + } + + paths.push(Tput.termcap); + + for (i = 0; i < paths.length; i++) { + terms = this._tryCap(paths[i], term); + if (terms) break; + } } if (!terms) {