fix terminfo if/else. detect features.

This commit is contained in:
Christopher Jeffrey 2013-07-14 03:14:48 -05:00
parent 342ed23b66
commit 363bc44caa
4 changed files with 195 additions and 97 deletions

View File

@ -54,8 +54,6 @@ function Program(options) {
this.terminal = options.terminal || process.env.TERM || 'xterm';
this.unicode = this.detectUnicode();
// if (!Program.global) {
// Program._write = process.stdout.write;
// process.stdout.write = function() {};
@ -783,42 +781,6 @@ Program.prototype._bindResponse = function(s) {
}
};
// Not as foolproof as I'd like it to be.
Program.prototype.detectUnicode = function() {
var LANG = process.env.LANG
+ ':' + process.env.LANGUAGE
+ ':' + process.env.LC_ALL
+ ':' + process.env.LC_CTYPE;
return /utf-?8/i.test(LANG);
};
// Could do something goofy like this:
Program.prototype.detectUnicode_ = function(callback) {
var out = String.fromCharCode((0x1b << 8) | '['.charCodeAt(0)) + 'c'
, done;
this.once('response', function(data) {
if (done) return;
done = true;
if (data.deviceAttributes) {
return callback(null, false);
}
});
this.write(out);
this.cub(2);
this.write(' ');
this.cub(2);
setTimeout(function() {
if (done) return;
done = true;
return callback(null, true);
}, 100);
};
Program.prototype.receive = function(text, callback) {
var listeners = (this._events && this._events['keypress']) || []
, bak = listeners.slice()

View File

@ -65,6 +65,8 @@ function Tput(options) {
}
this._useXtermI();
}
this.detectFeatures();
}
/**
@ -477,7 +479,7 @@ Tput.prototype.compile = function(info, inject) {
if (self.debug) {
console.log('Compiling %s: %s', key, JSON.stringify(info.all[key]));
}
info.methods[key] = self._compile(info.all[key]);
info.methods[key] = self._compile(info.all[key], key);
});
});
@ -536,8 +538,10 @@ Tput.prototype.inject = function(info) {
this.strings = info.strings;
};
Tput.prototype._compile = function(val) {
var self = this;
Tput.prototype._compile = function(val, key) {
var self = this
, _val = val
, _key = key;
switch (typeof val) {
case 'boolean':
@ -563,7 +567,8 @@ Tput.prototype._compile = function(val) {
, i
, v
var then
var fi
, then
, els
, end;
@ -902,15 +907,17 @@ Tput.prototype._compile = function(val) {
continue;
}
// if/then/else/end
// Terminfo does elseif's like
// this: %?[expr]%t...%e[expr]%t...%;
if (read(/^%e/)) {
fi = val.indexOf('%?');
then = val.indexOf('%t');
els = val.indexOf('%e');
end = val.indexOf('%;');
// Terminfo does elseif's like
// this: %?[expr]%t...%e[expr]%t...%;
// if (then < end && then < els) {
// if (then !== -1 && then < end && then < els) {
if (then !== -1 && then < end && (els !== -1 && then < els)) {
if (then !== -1 && then < end
&& (fi === -1 || then < fi)
&& (els === -1 || then < els)) {
stmt('} else if (');
} else {
stmt('} else {');
@ -1002,11 +1009,21 @@ Tput.prototype._compile = function(val) {
process.stdout.write(v + '\n');
}
// Possibly do:
// if (code.indexOf('return ') === 0) {
// return new Function('', code)();
// }
try {
return this.printf
? new Function('sprintf, params', code).bind(null, sprintf)
: new Function('params', code);
} catch (e) {
console.error('');
console.error('Error on `' + _key + '`:');
console.error(JSON.stringify(_val));
console.error('');
console.error(code.replace(/(,|;)/g, '$1\n'));
e.stack = e.stack.replace(/\x1b/g, '\\x1b');
throw e;
}
@ -1261,6 +1278,122 @@ Tput.prototype.compileTermcap = function(info, inject) {
return this.compile(info, inject);
};
/**
* Detect Features / Quirks
*/
Tput.prototype.detectFeatures = function() {
this.unicode = this.detectUnicode();
this.brokenACS = this.detectBrokenACS();
this.PCRomSet = this.detectPCRomSet();
this.magicCookie = this.detectMagicCookie();
this.padding = this.detectPadding();
this.setbuf = this.detectSetbuf();
this.parseACS();
};
Tput.prototype.detectUnicode = function() {
var LANG = process.env.LANG
+ ':' + process.env.LANGUAGE
+ ':' + process.env.LC_ALL
+ ':' + process.env.LC_CTYPE;
return /utf-?8/i.test(LANG);
};
// For some reason TERM=linux has smacs/rmacs, but it maps to `^[[11m`
// and it does not switch to the DEC SCLD character set. What the hell?
// xterm: \x1b(0, screen: \x0e, linux: \x1b[11m (doesn't work)
// `man console_codes` says:
// 11 select null mapping, set display control flag, reset tog
// gle meta flag (ECMA-48 says "first alternate font").
// See ncurses:
// ~/ncurses/ncurses/tinfo/lib_acs.c
// ~/ncurses/ncurses/tinfo/tinfo_driver.c
// ~/ncurses/ncurses/tinfo/lib_setup.c
Tput.prototype.detectBrokenACS = function() {
// ncurses-compatible env variable.
if (process.env.NCURSES_NO_UTF8_ACS != null) {
return true;
}
// An extended terminfo number.
if (this.numbers['U8'] !== -1) {
return !!this.numbers['U8'];
}
// The linux console is just broken for some reason.
if (this.term === 'linux') {
return true;
}
// screen termcap is bugged?
if (this.term.indexOf('screen') == 0
&& process.env.TERMCAP
&& ~process.env.TERMCAP.indexOf('screen')
&& ~process.env.TERMCAP.indexOf('hhII00')) {
if (~this.strings.enter_alt_charset_mode.indexOf('\016')
|| ~this.strings.enter_alt_charset_mode.indexOf('\017')
|| ~this.strings.set_attributes.indexOf('\016')
|| ~this.strings.set_attributes.indexOf('\017')) {
return true;
}
}
return false;
};
// Apparently the Linux console does not support ACS,
// but it does support the PC ROM character set.
// See: ~/ncurses/ncurses/tinfo/lib_acs.c
Tput.prototype.detectPCRomSet = function() {
var str = this.strings;
if (str.enter_pc_charset_mode === str.enter_alt_charset_mode
&& str.exit_pc_charset_mode === str.exit_alt_charset_mode) {
return true;
}
return false;
};
Tput.prototype.detectMagicCookie = function() {
return process.env.NCURSES_NO_MAGIC_COOKIE == null;
};
Tput.prototype.detectPadding = function() {
return process.env.NCURSES_NO_PADDING == null;
};
Tput.prototype.detectSetbuf = function() {
return process.env.NCURSES_NO_SETBUF == null;
};
Tput.prototype.parseACS = function() {
var self = this;
this.acsc = {};
this.acscr = {};
if (this.PCRomSet) {
;
}
// See: ~/ncurses/ncurses/tinfo/lib_acs.c: L208
Object.keys(acsc).forEach(function(ch) {
var acs_chars = self.strings.acs_chars || ''
, i = acs_chars.indexOf(ch)
, next = acs_chars[i + 1];
if (!next || i === -1) {
return;
}
self.acsc[ch] = acsc[next];
self.acscr[acsc[next]] = ch;
});
};
/**
* Helpers
*/
@ -1987,6 +2120,46 @@ Tput.strings = [
'box_chars_1'
];
// DEC Special Character and Line Drawing Set.
// Taken from tty.js.
var acsc = { // (0
'`': '\u25c6', // '◆'
'a': '\u2592', // '▒'
'b': '\u0009', // '\t'
'c': '\u000c', // '\f'
'd': '\u000d', // '\r'
'e': '\u000a', // '\n'
'f': '\u00b0', // '°'
'g': '\u00b1', // '±'
'h': '\u2424', // '\u2424' (NL)
'i': '\u000b', // '\v'
'j': '\u2518', // '┘'
'k': '\u2510', // '┐'
'l': '\u250c', // '┌'
'm': '\u2514', // '└'
'n': '\u253c', // '┼'
'o': '\u23ba', // '⎺'
'p': '\u23bb', // '⎻'
'q': '\u2500', // '─'
'r': '\u23bc', // '⎼'
's': '\u23bd', // '⎽'
't': '\u251c', // '├'
'u': '\u2524', // '┤'
'v': '\u2534', // '┴'
'w': '\u252c', // '┬'
'x': '\u2502', // '│'
'y': '\u2264', // '≤'
'z': '\u2265', // '≥'
'{': '\u03c0', // 'π'
'|': '\u2260', // '≠'
'}': '\u00a3', // '£'
'~': '\u00b7' // '·'
};
// ['b', 'c', 'd', 'e', 'h', 'i'].forEach(function(ch) {
// delete acsc[ch];
// });
/**
* Expose
*/

View File

@ -766,14 +766,16 @@ Screen.prototype.draw = function(start, end) {
}
}
if (!this.program.unicode && this.tput
&& this.tput.strings.enter_alt_charset_mode && SCLD[ch]
&& this.tput.strings.enter_alt_charset_mode !== '\x1b[11m') {
// TODO: Possibly do not check for unicode above.
// For some reason TERM=linux has smacs/rmacs, but it maps to `^[[11m`
// and it does not switch to the DEC SCLD character set. What the hell?
// xterm: \x1b(0, screen: \x0e
ch = this.tput.smacs() + SCLD[ch] + this.tput.rmacs();
// Attempt to use ACS for supported characters.
// XXX We may not want to check tput.unicode here.
if (this.tput
&& this.tput.strings.enter_alt_charset_mode
&& this.tput.acscr[ch]
&& !this.tput.brokenACS
&& !this.tput.unicode) {
ch = this.tput.smacs()
+ this.tput.acscr[ch]
+ this.tput.rmacs();
}
out += ch;
@ -4084,48 +4086,6 @@ function sattr(obj, fg, bg) {
| bg;
}
// DEC Special Character and Line Drawing Set.
// Taken from tty.js.
var SCLD = { // (0
'`': '\u25c6', // '◆'
'a': '\u2592', // '▒'
// 'b': '\u0009', // '\t'
// 'c': '\u000c', // '\f'
// 'd': '\u000d', // '\r'
// 'e': '\u000a', // '\n'
'f': '\u00b0', // '°'
'g': '\u00b1', // '±'
'h': '\u2424', // '\u2424' (NL)
// 'i': '\u000b', // '\v'
'j': '\u2518', // '┘'
'k': '\u2510', // '┐'
'l': '\u250c', // '┌'
'm': '\u2514', // '└'
'n': '\u253c', // '┼'
'o': '\u23ba', // '⎺'
'p': '\u23bb', // '⎻'
'q': '\u2500', // '─'
'r': '\u23bc', // '⎼'
's': '\u23bd', // '⎽'
't': '\u251c', // '├'
'u': '\u2524', // '┤'
'v': '\u2534', // '┴'
'w': '\u252c', // '┬'
'x': '\u2502', // '│'
'y': '\u2264', // '≤'
'z': '\u2265', // '≥'
'{': '\u03c0', // 'π'
'|': '\u2260', // '≠'
'}': '\u00a3', // '£'
'~': '\u00b7' // '·'
};
Object.keys(SCLD).forEach(function(key) {
var val = SCLD[key];
delete SCLD[key];
SCLD[val] = key;
});
/**
* Expose
*/

View File

@ -92,6 +92,9 @@ var tput = Tput({
console.log('Max colors: %d.', tput.colors);
// console.log(tput.strings.acs_chars.split('').map(function(ch) { return ch.charCodeAt(0); }));
// console.log(JSON.stringify(tput.strings.acs_chars));
// process.stdout.write(Tput.sprintf('%-10s\n', 'hello'));
// tput._compile('%?%p9%t\u001b(0%e\u001b(B%;\u001b[0%?%p6%t;1%;%?%p2%t;4%;%?%p1%p3%|%t;7%;%?%p4%t;5%;%?%p7%t;8%;m');