3060 lines
		
	
	
		
			68 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			3060 lines
		
	
	
		
			68 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| /**
 | ||
|  **      ==============================
 | ||
|  **       O           O      O   OOOO
 | ||
|  **       O           O     O O  O   O
 | ||
|  **       O           O     O O  O   O
 | ||
|  **       OOOO   OOOO O     OOO  OOOO
 | ||
|  **       O   O       O    O   O O   O
 | ||
|  **       O   O       O    O   O O   O
 | ||
|  **       OOOO        OOOO O   O OOOO
 | ||
|  **      ==============================
 | ||
|  **      Dr. Stefan Bosse http://www.bsslab.de
 | ||
|  **
 | ||
|  **      COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED
 | ||
|  **                 BY THE AUTHOR(S).
 | ||
|  **                 THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED,
 | ||
|  **                 MODIFIED, OR OTHERWISE USED IN A CONTEXT
 | ||
|  **                 OUTSIDE OF THE SOFTWARE SYSTEM.
 | ||
|  **
 | ||
|  **    $AUTHORS:     Christopher Jeffrey and contributors, Stefan Bosse 
 | ||
|  **    $INITIAL:     (C) 2013-2015, Christopher Jeffrey and contributors (MIT License)
 | ||
|  **    $REVESIO:     1.1.5
 | ||
|  **
 | ||
|  **    $INFO:
 | ||
|  *
 | ||
|  * tput.js - parse and compile terminfo caps to javascript.
 | ||
|  * Modification: Embedded file support.
 | ||
|  *
 | ||
|  *     $ENDINFO
 | ||
|  */
 | ||
| 
 | ||
| // Resources:
 | ||
| //   $ man term
 | ||
| //   $ man terminfo
 | ||
| //   http://invisible-island.net/ncurses/man/term.5.html
 | ||
| //   https://en.wikipedia.org/wiki/Terminfo
 | ||
| 
 | ||
| // Todo:
 | ||
| // - xterm's XT (set-title capability?) value should
 | ||
| //   be true (at least tmux thinks it should).
 | ||
| //   It's not parsed as true. Investigate.
 | ||
| // - Possibly switch to other method of finding the
 | ||
| //   extended data string table: i += h.symOffsetCount * 2;
 | ||
| 
 | ||
| /**
 | ||
|  * Modules
 | ||
|  */
 | ||
| 
 | ||
| var assert = Require('assert')
 | ||
|   , path = Require('path')
 | ||
|   , fs = Require('fs')
 | ||
|   , cp = Require('child_process');
 | ||
| 
 | ||
| /**
 | ||
|  * Tput
 | ||
|  */
 | ||
| 
 | ||
| FileEmbedd('term/def/xterm');
 | ||
| FileEmbedd('term/def/windows-ansi');
 | ||
| 
 | ||
| function Tput(options) {
 | ||
|   if (!(this instanceof Tput)) {
 | ||
|     return new Tput(options);
 | ||
|   }
 | ||
| 
 | ||
|   options = options || {};
 | ||
|   if (typeof options === 'string') {
 | ||
|     options = { terminal: options };
 | ||
|   }
 | ||
| 
 | ||
|   this.options = options;
 | ||
|   this.terminal = options.terminal
 | ||
|     || options.term
 | ||
|     || process.env.TERM
 | ||
|     || (process.platform === 'win32' ? 'windows-ansi' : 'xterm');
 | ||
| 
 | ||
|   this.terminal = this.terminal.toLowerCase();
 | ||
| 
 | ||
|   this.debug = options.debug;
 | ||
|   this.padding = options.padding;
 | ||
|   this.extended = options.extended;
 | ||
|   this.printf = options.printf;
 | ||
|   this.termcap = options.termcap;
 | ||
|   this.error = null;
 | ||
| 
 | ||
|   this.terminfoPrefix = options.terminfoPrefix;
 | ||
|   this.terminfoFile = options.terminfoFile;
 | ||
|   this.termcapFile = options.termcapFile;
 | ||
| 
 | ||
|   if (options.terminal || options.term) {
 | ||
|     this.setup();
 | ||
|   }
 | ||
| }
 | ||
| 
 | ||
| Tput.prototype.setup = function() {
 | ||
|   this.error = null;
 | ||
|   try {
 | ||
|     if (this.termcap) {
 | ||
|       try {
 | ||
|         this.injectTermcap();
 | ||
|       } catch (e) {
 | ||
|         if (this.debug) throw e;
 | ||
|         this.error = new Error('Termcap parse error.');
 | ||
|         this._useInternalCap(this.terminal);
 | ||
|       }
 | ||
|     } else {
 | ||
|       try {
 | ||
|         this.injectTerminfo();
 | ||
|       } catch (e) {
 | ||
|         if (this.debug) throw e;
 | ||
|         this.error = new Error('Terminfo parse error.');
 | ||
|         this._useInternalInfo(this.terminal);
 | ||
|       }
 | ||
|     }
 | ||
|   } catch (e) {
 | ||
|     // If there was an error, fallback
 | ||
|     // to an internally stored terminfo/cap.
 | ||
|     if (this.debug) throw e;
 | ||
|     this.error = new Error('Terminfo not found.');
 | ||
|     this._useXtermInfo();
 | ||
|   }
 | ||
| };
 | ||
| 
 | ||
| Tput.prototype.term = function(is) {
 | ||
|   return this.terminal.indexOf(is) === 0;
 | ||
| };
 | ||
| 
 | ||
| Tput.prototype._debug = function() {
 | ||
|   if (!this.debug) return;
 | ||
|   return console.log.apply(console, arguments);
 | ||
| };
 | ||
| 
 | ||
| /**
 | ||
|  * Fallback
 | ||
|  */
 | ||
| 
 | ||
| Tput.prototype._useVt102Cap = function() {
 | ||
|   return this.injectTermcap('vt102');
 | ||
| };
 | ||
| 
 | ||
| Tput.prototype._useXtermCap = function() {
 | ||
|   return this.injectTermcap('term/def/xterm.termcap');
 | ||
| };
 | ||
| 
 | ||
| Tput.prototype._useXtermInfo = function() {
 | ||
|   return this.injectTerminfo('term/def/xterm');
 | ||
| };
 | ||
| 
 | ||
| Tput.prototype._useInternalInfo = function(name) {
 | ||
|   name = path.basename(name);
 | ||
|   return this.injectTerminfo('term/def/' + name);
 | ||
| };
 | ||
| 
 | ||
| Tput.prototype._useInternalCap = function(name) {
 | ||
|   name = path.basename(name);
 | ||
|   return this.injectTermcap('term/def/' + name + '.termcap');
 | ||
| };
 | ||
| 
 | ||
| /**
 | ||
|  * Terminfo
 | ||
|  */
 | ||
| 
 | ||
| Tput.ipaths = [
 | ||
|   __dirname+'/..',
 | ||
|   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',
 | ||
|   '/lib/terminfo'
 | ||
| ];
 | ||
| 
 | ||
| Tput.prototype.readTerminfo = function(term) {
 | ||
|   var data
 | ||
|     , file
 | ||
|     , info;
 | ||
| 
 | ||
|   term = term || this.terminal;
 | ||
|   try {data = FileEmbedded(term);} catch (e) {};
 | ||
|   if (!data) {
 | ||
|     file = path.normalize(this._prefix(term));
 | ||
|     console.log('Reading '+file);
 | ||
|     data = fs.readFileSync(file);
 | ||
|   } else file=term;
 | ||
|   info = this.parseTerminfo(data, file);
 | ||
| 
 | ||
|   if (this.debug) {
 | ||
|     this._terminfo = info;
 | ||
|   }
 | ||
| 
 | ||
|   return info;
 | ||
| };
 | ||
| 
 | ||
| Tput._prefix =
 | ||
| Tput.prototype._prefix = function(term) {
 | ||
|   // If we have a terminfoFile, or our
 | ||
|   // term looks like a filename, use it.
 | ||
|   if (term) {
 | ||
|     if (term.indexOf(path.sep)==0) {
 | ||
|       return term;
 | ||
|     }
 | ||
|     if (this.terminfoFile) {
 | ||
|       return this.terminfoFile;
 | ||
|     }
 | ||
|   }
 | ||
|   var paths = Tput.ipaths.slice()
 | ||
|     , file;
 | ||
| 
 | ||
|   if (this.terminfoPrefix) {
 | ||
|     paths.unshift(this.terminfoPrefix);
 | ||
|   }
 | ||
| 
 | ||
| 
 | ||
|   // Try exact matches.
 | ||
|   file = this._tprefix(paths, term);
 | ||
|   if (file) return file;
 | ||
| 
 | ||
|   // Try similar matches.
 | ||
|   file = this._tprefix(paths, term, true);
 | ||
|   if (file) return file;
 | ||
| 
 | ||
|   // Not found.
 | ||
|   throw new Error('Terminfo directory not found.');
 | ||
| };
 | ||
| 
 | ||
| Tput._tprefix =
 | ||
| Tput.prototype._tprefix = function(prefix, term, soft) {
 | ||
|   if (!prefix) return;
 | ||
| 
 | ||
|   var file
 | ||
|     , dir
 | ||
|     , i
 | ||
|     , sdiff
 | ||
|     , sfile
 | ||
|     , list;
 | ||
| 
 | ||
| 
 | ||
|   try {
 | ||
|       file=prefix+'/'+term;
 | ||
|       fs.statSync(file);
 | ||
|       return file;
 | ||
|   } catch (e) {
 | ||
|       ;
 | ||
|   }
 | ||
| 
 | ||
|   if (Array.isArray(prefix)) {
 | ||
|     for (i = 0; i < prefix.length; i++) {
 | ||
|       file = this._tprefix(prefix[i], term, soft);
 | ||
|       if (file) return file;
 | ||
|     }
 | ||
|     return;
 | ||
|   }
 | ||
| 
 | ||
|   var find = function(word) {
 | ||
|     var file, ch;
 | ||
| 
 | ||
|     file = path.resolve(prefix, word[0]);
 | ||
|     
 | ||
|     try {
 | ||
|       fs.statSync(file);
 | ||
|       return file;
 | ||
|     } catch (e) {
 | ||
|       ;
 | ||
|     }
 | ||
| 
 | ||
|     ch = word[0].charCodeAt(0).toString(16);
 | ||
|     if (ch.length < 2) ch = '0' + ch;
 | ||
| 
 | ||
|     file = path.resolve(prefix, ch);
 | ||
| 
 | ||
|     try {
 | ||
|       fs.statSync(file);
 | ||
|       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) {
 | ||
|       ;
 | ||
|     }
 | ||
|     return;
 | ||
|   }
 | ||
| 
 | ||
|   term = path.basename(term);
 | ||
|   dir = find(term);
 | ||
| 
 | ||
|   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;
 | ||
|   }
 | ||
| 
 | ||
|   file = path.resolve(dir, term);
 | ||
|   try {
 | ||
|     fs.statSync(file);
 | ||
|     return file;
 | ||
|   } catch (e) {
 | ||
|     ;
 | ||
|   }
 | ||
| };
 | ||
| 
 | ||
| /**
 | ||
|  * Terminfo Parser
 | ||
|  * All shorts are little-endian
 | ||
|  */
 | ||
| 
 | ||
| Tput.prototype.parseTerminfo = function(data, file) {
 | ||
|   var info = {}
 | ||
|     , extended
 | ||
|     , l = data.length
 | ||
|     , i = 0
 | ||
|     , v
 | ||
|     , o;
 | ||
| 
 | ||
|   var h = info.header = {
 | ||
|     dataSize: data.length,
 | ||
|     headerSize: 12,
 | ||
|     magicNumber: (data[1] << 8) | data[0],
 | ||
|     namesSize: (data[3] << 8) | data[2],
 | ||
|     boolCount: (data[5] << 8) | data[4],
 | ||
|     numCount: (data[7] << 8) | data[6],
 | ||
|     strCount: (data[9] << 8) | data[8],
 | ||
|     strTableSize: (data[11] << 8) | data[10]
 | ||
|   };
 | ||
|   console.log('parseTerminfo '+file+':'+data.length)
 | ||
| 
 | ||
|   h.total = h.headerSize
 | ||
|     + h.namesSize
 | ||
|     + h.boolCount
 | ||
|     + h.numCount * 2
 | ||
|     + h.strCount * 2
 | ||
|     + h.strTableSize;
 | ||
| 
 | ||
|   i += h.headerSize;
 | ||
| 
 | ||
|   // Names Section
 | ||
|   var names = data.toString('ascii', i, i + h.namesSize - 1)
 | ||
|     , parts = names.split('|')
 | ||
|     , name = parts[0]
 | ||
|     , desc = parts.pop();
 | ||
|   info.name = name;
 | ||
|   info.names = parts;
 | ||
|   info.desc = desc;
 | ||
|   
 | ||
|   info.dir = path.resolve(file, '..', '..');
 | ||
|   info.file = file;
 | ||
| 
 | ||
|   i += h.namesSize - 1;
 | ||
|   // Names is nul-terminated.
 | ||
|   assert.equal(data[i], 0);
 | ||
|   i++;
 | ||
| 
 | ||
|   // Booleans Section
 | ||
|   // One byte for each flag
 | ||
|   // Same order as <term.h>
 | ||
|   info.bools = {};
 | ||
|   l = i + h.boolCount;
 | ||
|   o = 0;
 | ||
|   for (; i < l; i++) {
 | ||
|     v = Tput.bools[o++];
 | ||
|     info.bools[v] = data[i] === 1;
 | ||
|   }
 | ||
| 
 | ||
|   // Null byte in between to make sure numbers begin on an even byte.
 | ||
|   if (i % 2) {
 | ||
|     assert.equal(data[i], 0);
 | ||
|     i++;
 | ||
|   }
 | ||
| 
 | ||
|   // Numbers Section
 | ||
|   info.numbers = {};
 | ||
|   l = i + h.numCount * 2;
 | ||
|   o = 0;
 | ||
|   for (; i < l; i += 2) {
 | ||
|     v = Tput.numbers[o++];
 | ||
|     if (data[i + 1] === 0377 && data[i] === 0377) {
 | ||
|       info.numbers[v] = -1;
 | ||
|     } else {
 | ||
|       info.numbers[v] = (data[i + 1] << 8) | data[i];
 | ||
|     }
 | ||
|   }
 | ||
| 
 | ||
|   // Strings Section
 | ||
|   info.strings = {};
 | ||
|   l = i + h.strCount * 2;
 | ||
|   o = 0;
 | ||
|   for (; i < l; i += 2) {
 | ||
|     v = Tput.strings[o++];
 | ||
|     if (data[i + 1] === 0377 && data[i] === 0377) {
 | ||
|       info.strings[v] = -1;
 | ||
|     } else {
 | ||
|       info.strings[v] = (data[i + 1] << 8) | data[i];
 | ||
|     }
 | ||
|   }
 | ||
| 
 | ||
|   // String Table
 | ||
|   Object.keys(info.strings).forEach(function(key) {
 | ||
|     if (info.strings[key] === -1) {
 | ||
|       delete info.strings[key];
 | ||
|       return;
 | ||
|     }
 | ||
| 
 | ||
|     // Workaround: fix an odd bug in the screen-256color terminfo where it tries
 | ||
|     // to set -1, but it appears to have {0xfe, 0xff} instead of {0xff, 0xff}.
 | ||
|     // TODO: Possibly handle errors gracefully below, as well as in the
 | ||
|     // extended info. Also possibly do: `if (info.strings[key] >= data.length)`.
 | ||
|     if (info.strings[key] === 65534) {
 | ||
|       delete info.strings[key];
 | ||
|       return;
 | ||
|     }
 | ||
| 
 | ||
|     var s = i + info.strings[key]
 | ||
|       , j = s;
 | ||
| 
 | ||
|     while (data[j]) j++;
 | ||
| 
 | ||
|     assert(j < data.length);
 | ||
| 
 | ||
|     info.strings[key] = data.toString('ascii', s, j);
 | ||
|   });
 | ||
| 
 | ||
|   // Extended Header
 | ||
|   if (this.extended !== false) {
 | ||
|     i--;
 | ||
|     i += h.strTableSize;
 | ||
|     if (i % 2) {
 | ||
|       assert.equal(data[i], 0);
 | ||
|       i++;
 | ||
|     }
 | ||
|     l = data.length;
 | ||
|     if (i < l - 1) {
 | ||
|       try {
 | ||
|         extended = this.parseExtended(data.slice(i));
 | ||
|       } catch (e) {
 | ||
|         if (this.debug) {
 | ||
|           throw e;
 | ||
|         }
 | ||
|         return info;
 | ||
|       }
 | ||
|       info.header.extended = extended.header;
 | ||
|       ['bools', 'numbers', 'strings'].forEach(function(key) {
 | ||
|         merge(info[key], extended[key]);
 | ||
|       });
 | ||
|     }
 | ||
|   }
 | ||
|   
 | ||
|   return info;
 | ||
| };
 | ||
| 
 | ||
| /**
 | ||
|  * Extended Parsing
 | ||
|  */
 | ||
| 
 | ||
| // Some data to help understand:
 | ||
| 
 | ||
| // For xterm, non-extended header:
 | ||
| // { dataSize: 3270,
 | ||
| //   headerSize: 12,
 | ||
| //   magicNumber: 282,
 | ||
| //   namesSize: 48,
 | ||
| //   boolCount: 38,
 | ||
| //   numCount: 15,
 | ||
| //   strCount: 413,
 | ||
| //   strTableSize: 1388,
 | ||
| //   total: 2342 }
 | ||
| 
 | ||
| // For xterm, header:
 | ||
| // Offset: 2342
 | ||
| // { header:
 | ||
| //    { dataSize: 928,
 | ||
| //      headerSize: 10,
 | ||
| //      boolCount: 2,
 | ||
| //      numCount: 1,
 | ||
| //      strCount: 57,
 | ||
| //      strTableSize: 117,
 | ||
| //      lastStrTableOffset: 680,
 | ||
| //      total: 245 },
 | ||
| 
 | ||
| // For xterm, layout:
 | ||
| // { header: '0 - 10', // length: 10
 | ||
| //   bools: '10 - 12', // length: 2
 | ||
| //   numbers: '12 - 14', // length: 2
 | ||
| //   strings: '14 - 128', // length: 114 (57 short)
 | ||
| //   symoffsets: '128 - 248', // length: 120 (60 short)
 | ||
| //   stringtable: '248 - 612', // length: 364
 | ||
| //   sym: '612 - 928' } // length: 316
 | ||
| //
 | ||
| // How lastStrTableOffset works:
 | ||
| //   data.length - h.lastStrTableOffset === 248
 | ||
| //     (sym-offset end, string-table start)
 | ||
| //   364 + 316 === 680 (lastStrTableOffset)
 | ||
| // How strTableSize works:
 | ||
| //   h.strCount + [symOffsetCount] === h.strTableSize
 | ||
| //   57 + 60 === 117 (strTableSize)
 | ||
| //   symOffsetCount doesn't actually exist in the header. it's just implied.
 | ||
| // Getting the number of sym offsets:
 | ||
| //   h.symOffsetCount = h.strTableSize - h.strCount;
 | ||
| //   h.symOffsetSize = (h.strTableSize - h.strCount) * 2;
 | ||
| 
 | ||
| Tput.prototype.parseExtended = function(data) {
 | ||
|   var info = {}
 | ||
|     , l = data.length
 | ||
|     , i = 0;
 | ||
| 
 | ||
|   var h = info.header = {
 | ||
|     dataSize: data.length,
 | ||
|     headerSize: 10,
 | ||
|     boolCount: (data[i + 1] << 8) | data[i + 0],
 | ||
|     numCount: (data[i + 3] << 8) | data[i + 2],
 | ||
|     strCount: (data[i + 5] << 8) | data[i + 4],
 | ||
|     strTableSize: (data[i + 7] << 8) | data[i + 6],
 | ||
|     lastStrTableOffset: (data[i + 9] << 8) | data[i + 8]
 | ||
|   };
 | ||
| 
 | ||
|   // h.symOffsetCount = h.strTableSize - h.strCount;
 | ||
| 
 | ||
|   h.total = h.headerSize
 | ||
|     + h.boolCount
 | ||
|     + h.numCount * 2
 | ||
|     + h.strCount * 2
 | ||
|     + h.strTableSize;
 | ||
| 
 | ||
|   i += h.headerSize;
 | ||
| 
 | ||
|   // Booleans Section
 | ||
|   // One byte for each flag
 | ||
|   var _bools = [];
 | ||
|   l = i + h.boolCount;
 | ||
|   for (; i < l; i++) {
 | ||
|     _bools.push(data[i] === 1);
 | ||
|   }
 | ||
| 
 | ||
|   // Null byte in between to make sure numbers begin on an even byte.
 | ||
|   if (i % 2) {
 | ||
|     assert.equal(data[i], 0);
 | ||
|     i++;
 | ||
|   }
 | ||
| 
 | ||
|   // Numbers Section
 | ||
|   var _numbers = [];
 | ||
|   l = i + h.numCount * 2;
 | ||
|   for (; i < l; i += 2) {
 | ||
|     if (data[i + 1] === 0377 && data[i] === 0377) {
 | ||
|       _numbers.push(-1);
 | ||
|     } else {
 | ||
|       _numbers.push((data[i + 1] << 8) | data[i]);
 | ||
|     }
 | ||
|   }
 | ||
| 
 | ||
|   // Strings Section
 | ||
|   var _strings = [];
 | ||
|   l = i + h.strCount * 2;
 | ||
|   for (; i < l; i += 2) {
 | ||
|     if (data[i + 1] === 0377 && data[i] === 0377) {
 | ||
|       _strings.push(-1);
 | ||
|     } else {
 | ||
|       _strings.push((data[i + 1] << 8) | data[i]);
 | ||
|     }
 | ||
|   }
 | ||
| 
 | ||
|   // Pass over the sym offsets and get to the string table.
 | ||
|   i = data.length - h.lastStrTableOffset;
 | ||
|   // Might be better to do this instead if the file has trailing bytes:
 | ||
|   // i += h.symOffsetCount * 2;
 | ||
| 
 | ||
|   // String Table
 | ||
|   var high = 0;
 | ||
|   _strings.forEach(function(offset, k) {
 | ||
|     if (offset === -1) {
 | ||
|       _strings[k] = '';
 | ||
|       return;
 | ||
|     }
 | ||
| 
 | ||
|     var s = i + offset
 | ||
|       , j = s;
 | ||
| 
 | ||
|     while (data[j]) j++;
 | ||
| 
 | ||
|     assert(j < data.length);
 | ||
| 
 | ||
|     // Find out where the string table ends by
 | ||
|     // getting the highest string length.
 | ||
|     if (high < j - i) {
 | ||
|       high = j - i;
 | ||
|     }
 | ||
| 
 | ||
|     _strings[k] = data.toString('ascii', s, j);
 | ||
|   });
 | ||
| 
 | ||
|   // Symbol Table
 | ||
|   // Add one to the highest string length because we didn't count \0.
 | ||
|   i += high + 1;
 | ||
|   l = data.length;
 | ||
| 
 | ||
|   var sym = []
 | ||
|     , j;
 | ||
| 
 | ||
|   for (; i < l; i++) {
 | ||
|     j = i;
 | ||
|     while (data[j]) j++;
 | ||
|     sym.push(data.toString('ascii', i, j));
 | ||
|     i = j;
 | ||
|   }
 | ||
| 
 | ||
|   // Identify by name
 | ||
|   j = 0;
 | ||
| 
 | ||
|   info.bools = {};
 | ||
|   _bools.forEach(function(bool) {
 | ||
|     info.bools[sym[j++]] = bool;
 | ||
|   });
 | ||
| 
 | ||
|   info.numbers = {};
 | ||
|   _numbers.forEach(function(number) {
 | ||
|     info.numbers[sym[j++]] = number;
 | ||
|   });
 | ||
| 
 | ||
|   info.strings = {};
 | ||
|   _strings.forEach(function(string) {
 | ||
|     info.strings[sym[j++]] = string;
 | ||
|   });
 | ||
| 
 | ||
|   // Should be the very last bit of data.
 | ||
|   assert.equal(i, data.length);
 | ||
| 
 | ||
|   return info;
 | ||
| };
 | ||
| 
 | ||
| Tput.prototype.compileTerminfo = function(term) {
 | ||
|   return this.compile(this.readTerminfo(term));
 | ||
| };
 | ||
| 
 | ||
| Tput.prototype.injectTerminfo = function(term) {
 | ||
|   return this.inject(this.compileTerminfo(term));
 | ||
| };
 | ||
| 
 | ||
| /**
 | ||
|  * Compiler - terminfo cap->javascript
 | ||
|  */
 | ||
| 
 | ||
| Tput.prototype.compile = function(info) {
 | ||
|   var self = this;
 | ||
| 
 | ||
|   if (!info) {
 | ||
|     throw new Error('Terminal not found.');
 | ||
|   }
 | ||
| 
 | ||
|   this.detectFeatures(info);
 | ||
| 
 | ||
|   this._debug(info);
 | ||
| 
 | ||
|   info.all = {};
 | ||
|   info.methods = {};
 | ||
| 
 | ||
|   ['bools', 'numbers', 'strings'].forEach(function(type) {
 | ||
|     Object.keys(info[type]).forEach(function(key) {
 | ||
|       info.all[key] = info[type][key];
 | ||
|       info.methods[key] = self._compile(info, key, info.all[key]);
 | ||
|     });
 | ||
|   });
 | ||
| 
 | ||
|   Tput.bools.forEach(function(key) {
 | ||
|     if (info.methods[key] == null) info.methods[key] = false;
 | ||
|   });
 | ||
| 
 | ||
|   Tput.numbers.forEach(function(key) {
 | ||
|     if (info.methods[key] == null) info.methods[key] = -1;
 | ||
|   });
 | ||
| 
 | ||
|   Tput.strings.forEach(function(key) {
 | ||
|     if (!info.methods[key]) info.methods[key] = noop;
 | ||
|   });
 | ||
| 
 | ||
|   Object.keys(info.methods).forEach(function(key) {
 | ||
|     if (!Tput.alias[key]) return;
 | ||
|     Tput.alias[key].forEach(function(alias) {
 | ||
|       info.methods[alias] = info.methods[key];
 | ||
|     });
 | ||
|     // Could just use:
 | ||
|     // Object.keys(Tput.aliasMap).forEach(function(key) {
 | ||
|     //   info.methods[key] = info.methods[Tput.aliasMap[key]];
 | ||
|     // });
 | ||
|   });
 | ||
| 
 | ||
|   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 methods[key].call(self, args);
 | ||
|     };
 | ||
|   });
 | ||
| 
 | ||
|   this.info = info;
 | ||
|   this.all = info.all;
 | ||
|   this.methods = info.methods;
 | ||
|   this.bools = info.bools;
 | ||
|   this.numbers = info.numbers;
 | ||
|   this.strings = info.strings;
 | ||
| 
 | ||
|   if (!~info.names.indexOf(this.terminal)) {
 | ||
|     this.terminal = info.name;
 | ||
|   }
 | ||
| 
 | ||
|   this.features = info.features;
 | ||
|   Object.keys(info.features).forEach(function(key) {
 | ||
|     if (key === 'padding') {
 | ||
|       if (!info.features.padding && self.options.padding !== true) {
 | ||
|         self.padding = false;
 | ||
|       }
 | ||
|       return;
 | ||
|     }
 | ||
|     self[key] = info.features[key];
 | ||
|   });
 | ||
| };
 | ||
| 
 | ||
| // See:
 | ||
| // ~/ncurses/ncurses/tinfo/lib_tparm.c
 | ||
| // ~/ncurses/ncurses/tinfo/comp_scan.c
 | ||
| Tput.prototype._compile = function(info, key, str) {
 | ||
|   var v;
 | ||
| 
 | ||
|   this._debug('Compiling %s: %s', key, JSON.stringify(str));
 | ||
| 
 | ||
|   switch (typeof str) {
 | ||
|     case 'boolean':
 | ||
|       return str;
 | ||
|     case 'number':
 | ||
|       return str;
 | ||
|     case 'string':
 | ||
|       break;
 | ||
|     default:
 | ||
|       return noop;
 | ||
|   }
 | ||
| 
 | ||
|   if (!str) {
 | ||
|     return noop;
 | ||
|   }
 | ||
| 
 | ||
|   // See:
 | ||
|   // ~/ncurses/progs/tput.c - tput() - L149
 | ||
|   // ~/ncurses/progs/tset.c - set_init() - L992
 | ||
|   if (key === 'init_file' || key === 'reset_file') {
 | ||
|     try {
 | ||
|       str = fs.readFileSync(str, 'utf8');
 | ||
|       if (this.debug) {
 | ||
|         v = ('return ' + JSON.stringify(str) + ';')
 | ||
|           .replace(/\x1b/g, '\\x1b')
 | ||
|           .replace(/\r/g, '\\r')
 | ||
|           .replace(/\n/g, '\\n');
 | ||
|         process.stdout.write(v + '\n');
 | ||
|       }
 | ||
|       return function() { return str; };
 | ||
|     } catch (e) {
 | ||
|       return noop;
 | ||
|     }
 | ||
|   }
 | ||
| 
 | ||
|   var tkey = info.name + '.' + key
 | ||
|     , header = 'var v, dyn = {}, stat = {}, stack = [], out = [];'
 | ||
|     , footer = ';return out.join("");'
 | ||
|     , code = header
 | ||
|     , val = str
 | ||
|     , buff = ''
 | ||
|     , cap
 | ||
|     , ch
 | ||
|     , fi
 | ||
|     , then
 | ||
|     , els
 | ||
|     , end;
 | ||
| 
 | ||
|   function read(regex, no) {
 | ||
|     cap = regex.exec(val);
 | ||
|     if (!cap) return;
 | ||
|     val = val.substring(cap[0].length);
 | ||
|     ch = cap[1];
 | ||
|     if (!no) clear();
 | ||
|     return cap;
 | ||
|   }
 | ||
| 
 | ||
|   function stmt(c) {
 | ||
|     if (code[code.length - 1] === ',') {
 | ||
|       code = code.slice(0, -1);
 | ||
|     }
 | ||
|     code += c;
 | ||
|   }
 | ||
| 
 | ||
|   function expr(c) {
 | ||
|     code += c + ',';
 | ||
|   }
 | ||
| 
 | ||
|   function echo(c) {
 | ||
|     if (c === '""') return;
 | ||
|     expr('out.push(' + c + ')');
 | ||
|   }
 | ||
| 
 | ||
|   function print(c) {
 | ||
|     buff += c;
 | ||
|   }
 | ||
| 
 | ||
|   function clear() {
 | ||
|     if (buff) {
 | ||
|       echo(JSON.stringify(buff).replace(/\\u00([0-9a-fA-F]{2})/g, '\\x$1'));
 | ||
|       buff = '';
 | ||
|     }
 | ||
|   }
 | ||
| 
 | ||
|   while (val) {
 | ||
|     // Ignore newlines
 | ||
|     if (read(/^\n /, true)) {
 | ||
|       continue;
 | ||
|     }
 | ||
| 
 | ||
|     // '^A' -> ^A
 | ||
|     if (read(/^\^(.)/i, true)) {
 | ||
|       if (!(ch >= ' ' && ch <= '~')) {
 | ||
|         this._debug('%s: bad caret char.', tkey);
 | ||
|         // NOTE: ncurses appears to simply
 | ||
|         // continue in this situation, but
 | ||
|         // I could be wrong.
 | ||
|         print(cap[0]);
 | ||
|         continue;
 | ||
|       }
 | ||
|       if (ch === '?') {
 | ||
|         ch = '\x7f';
 | ||
|       } else {
 | ||
|         ch = ch.charCodeAt(0) & 31;
 | ||
|         if (ch === 0) ch = 128;
 | ||
|         ch = String.fromCharCode(ch);
 | ||
|       }
 | ||
|       print(ch);
 | ||
|       continue;
 | ||
|     }
 | ||
| 
 | ||
|     // 3 octal digits -> character
 | ||
|     if (read(/^\\([0-7]{3})/, true)) {
 | ||
|       print(String.fromCharCode(parseInt(ch, 8)));
 | ||
|       continue;
 | ||
|     }
 | ||
| 
 | ||
|     // '\e' -> ^[
 | ||
|     // '\n' -> \n
 | ||
|     // '\r' -> \r
 | ||
|     // '\0' -> \200 (special case)
 | ||
|     if (read(/^\\([eEnlrtbfs\^\\,:0]|.)/, true)) {
 | ||
|       switch (ch) {
 | ||
|         case 'e':
 | ||
|         case 'E':
 | ||
|           ch = '\x1b';
 | ||
|           break;
 | ||
|         case 'n':
 | ||
|           ch = '\n';
 | ||
|           break;
 | ||
|         case 'l':
 | ||
|           ch = '\x85';
 | ||
|           break;
 | ||
|         case 'r':
 | ||
|           ch = '\r';
 | ||
|           break;
 | ||
|         case 't':
 | ||
|           ch = '\t';
 | ||
|           break;
 | ||
|         case 'b':
 | ||
|           ch = '\x08';
 | ||
|           break;
 | ||
|         case 'f':
 | ||
|           ch = '\x0c';
 | ||
|           break;
 | ||
|         case 's':
 | ||
|           ch = ' ';
 | ||
|           break;
 | ||
|         case '^':
 | ||
|           ch = '^';
 | ||
|           break;
 | ||
|         case '\\':
 | ||
|           ch = '\\';
 | ||
|           break;
 | ||
|         case ',':
 | ||
|           ch = ',';
 | ||
|           break;
 | ||
|         case ':':
 | ||
|           ch = ':';
 | ||
|           break;
 | ||
|         case '0':
 | ||
|           ch = '\200';
 | ||
|           break;
 | ||
|         case 'a':
 | ||
|           ch = '\x07';
 | ||
|           break;
 | ||
|         default:
 | ||
|           this._debug('%s: bad backslash char.', tkey);
 | ||
|           ch = cap[0];
 | ||
|           break;
 | ||
|       }
 | ||
|       print(ch);
 | ||
|       continue;
 | ||
|     }
 | ||
| 
 | ||
|     // $<5> -> padding
 | ||
|     // e.g. flash_screen: '\u001b[?5h$<100/>\u001b[?5l',
 | ||
|     if (read(/^\$<(\d+)([*\/]{0,2})>/, true)) {
 | ||
|       if (this.padding) print(cap[0]);
 | ||
|       continue;
 | ||
|     }
 | ||
| 
 | ||
|     // %%   outputs `%'
 | ||
|     if (read(/^%%/, true)) {
 | ||
|       print('%');
 | ||
|       continue;
 | ||
|     }
 | ||
| 
 | ||
|     // %[[:]flags][width[.precision]][doxXs]
 | ||
|     //   as in printf, flags are [-+#] and space.  Use a `:' to allow the
 | ||
|     //   next character to be a `-' flag, avoiding interpreting "%-" as an
 | ||
|     //   operator.
 | ||
|     // %c   print pop() like %c in printf
 | ||
|     // Example from screen terminfo:
 | ||
|     //   S0: "\u001b(%p1%c"
 | ||
|     // %d   print pop()
 | ||
|     // "Print (e.g., "%d") is a special case."
 | ||
|     // %s   print pop() like %s in printf
 | ||
|     if (read(/^%((?::-|[+# ]){1,4})?(\d+(?:\.\d+)?)?([doxXsc])/)) {
 | ||
|       if (this.printf || cap[1] || cap[2] || ~'oxX'.indexOf(cap[3])) {
 | ||
|         echo('sprintf("'+ cap[0].replace(':-', '-') + '", stack.pop())');
 | ||
|       } else if (cap[3] === 'c') {
 | ||
|         echo('(v = stack.pop(), isFinite(v) '
 | ||
|           + '? String.fromCharCode(v || 0200) : "")');
 | ||
|       } else {
 | ||
|         echo('stack.pop()');
 | ||
|       }
 | ||
|       continue;
 | ||
|     }
 | ||
| 
 | ||
|     // %p[1-9]
 | ||
|     //   push i'th parameter
 | ||
|     if (read(/^%p([1-9])/)) {
 | ||
|       expr('(stack.push(v = params[' + (ch - 1) + ']), v)');
 | ||
|       continue;
 | ||
|     }
 | ||
| 
 | ||
|     // %P[a-z]
 | ||
|     //   set dynamic variable [a-z] to pop()
 | ||
|     if (read(/^%P([a-z])/)) {
 | ||
|       expr('dyn.' + ch + ' = stack.pop()');
 | ||
|       continue;
 | ||
|     }
 | ||
| 
 | ||
|     // %g[a-z]
 | ||
|     //   get dynamic variable [a-z] and push it
 | ||
|     if (read(/^%g([a-z])/)) {
 | ||
|       expr('(stack.push(dyn.' + ch + '), dyn.' + ch + ')');
 | ||
|       continue;
 | ||
|     }
 | ||
| 
 | ||
|     // %P[A-Z]
 | ||
|     //   set static variable [a-z] to pop()
 | ||
|     if (read(/^%P([A-Z])/)) {
 | ||
|       expr('stat.' + ch + ' = stack.pop()');
 | ||
|       continue;
 | ||
|     }
 | ||
| 
 | ||
|     // %g[A-Z]
 | ||
|     //   get static variable [a-z] and push it
 | ||
|     //   The  terms  "static"  and  "dynamic" are misleading.  Historically,
 | ||
|     //   these are simply two different sets of variables, whose values are
 | ||
|     //   not reset between calls to tparm.  However, that fact is not
 | ||
|     //   documented in other implementations.  Relying on it will adversely
 | ||
|     //   impact portability to other implementations.
 | ||
|     if (read(/^%g([A-Z])/)) {
 | ||
|       expr('(stack.push(v = stat.' + ch + '), v)');
 | ||
|       continue;
 | ||
|     }
 | ||
| 
 | ||
|     // %'c' char constant c
 | ||
|     // NOTE: These are stored as c chars, exemplified by:
 | ||
|     // cursor_address: "\u001b=%p1%' '%+%c%p2%' '%+%c"
 | ||
|     if (read(/^%'(.)'/)) {
 | ||
|       expr('(stack.push(v = ' + ch.charCodeAt(0) + '), v)');
 | ||
|       continue;
 | ||
|     }
 | ||
| 
 | ||
|     // %{nn}
 | ||
|     //   integer constant nn
 | ||
|     if (read(/^%\{(\d+)\}/)) {
 | ||
|       expr('(stack.push(v = ' + ch + '), v)');
 | ||
|       continue;
 | ||
|     }
 | ||
| 
 | ||
|     // %l   push strlen(pop)
 | ||
|     if (read(/^%l/)) {
 | ||
|       expr('(stack.push(v = (stack.pop() || "").length || 0), v)');
 | ||
|       continue;
 | ||
|     }
 | ||
| 
 | ||
|     // %+ %- %* %/ %m
 | ||
|     //   arithmetic (%m is mod): push(pop() op pop())
 | ||
|     // %& %| %^
 | ||
|     //   bit operations (AND, OR and exclusive-OR): push(pop() op pop())
 | ||
|     // %= %> %<
 | ||
|     //   logical operations: push(pop() op pop())
 | ||
|     if (read(/^%([+\-*\/m&|\^=><])/)) {
 | ||
|       if (ch === '=') ch = '===';
 | ||
|       else if (ch === 'm') ch = '%';
 | ||
|       expr('(v = stack.pop(),'
 | ||
|         + ' stack.push(v = (stack.pop() ' + ch + ' v) || 0),'
 | ||
|         + ' v)');
 | ||
|       continue;
 | ||
|     }
 | ||
| 
 | ||
|     // %A, %O
 | ||
|     //   logical AND and OR operations (for conditionals)
 | ||
|     if (read(/^%([AO])/)) {
 | ||
|       // Are we supposed to store the result on the stack?
 | ||
|       expr('(stack.push(v = (stack.pop() '
 | ||
|         + (ch === 'A' ? '&&' : '||')
 | ||
|         + ' stack.pop())), v)');
 | ||
|       continue;
 | ||
|     }
 | ||
| 
 | ||
|     // %! %~
 | ||
|     //   unary operations (logical and bit complement): push(op pop())
 | ||
|     if (read(/^%([!~])/)) {
 | ||
|       expr('(stack.push(v = ' + ch + 'stack.pop()), v)');
 | ||
|       continue;
 | ||
|     }
 | ||
| 
 | ||
|     // %i   add 1 to first two parameters (for ANSI terminals)
 | ||
|     if (read(/^%i/)) {
 | ||
|       // Are these supposed to go on the stack in certain situations?
 | ||
|       // ncurses doesn't seem to put them on the stack, but xterm.user6
 | ||
|       // seems to assume they're on the stack for some reason. Could
 | ||
|       // just be a bad terminfo string.
 | ||
|       // user6: "\u001b[%i%d;%dR" - possibly a termcap-style string.
 | ||
|       // expr('(params[0] |= 0, params[1] |= 0, params[0]++, params[1]++)');
 | ||
|       expr('(params[0]++, params[1]++)');
 | ||
|       continue;
 | ||
|     }
 | ||
| 
 | ||
|     // %? expr %t thenpart %e elsepart %;
 | ||
|     //   This forms an if-then-else.  The %e elsepart is optional.  Usually
 | ||
|     //   the %? expr part pushes a value onto the stack, and %t pops it from
 | ||
|     //   the stack, testing if it is nonzero (true).  If it is zero (false),
 | ||
|     //   control passes to the %e (else) part.
 | ||
|     //   It is possible to form else-if's a la Algol 68:
 | ||
|     //     %? c1 %t b1 %e c2 %t b2 %e c3 %t b3 %e c4 %t b4 %e %;
 | ||
|     //   where ci are conditions, bi are bodies.
 | ||
|     if (read(/^%\?/)) {
 | ||
|       end = -1;
 | ||
|       stmt(';if (');
 | ||
|       continue;
 | ||
|     }
 | ||
| 
 | ||
|     if (read(/^%t/)) {
 | ||
|       end = -1;
 | ||
|       // Technically this is supposed to pop everything off the stack that was
 | ||
|       // pushed onto the stack after the if statement, see man terminfo.
 | ||
|       // Right now, we don't pop anything off. This could cause compat issues.
 | ||
|       // Perhaps implement a "pushed" counter from the time the if statement
 | ||
|       // is added, to the time the then statement is added, and pop off
 | ||
|       // the appropriate number of elements.
 | ||
|       // while (pushed--) expr('stack.pop()');
 | ||
|       stmt(') {');
 | ||
|       continue;
 | ||
|     }
 | ||
| 
 | ||
|     // 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('%;');
 | ||
|       if (end === -1) end = Infinity;
 | ||
|       if (then !== -1 && then < end
 | ||
|           && (fi === -1 || then < fi)
 | ||
|           && (els === -1 || then < els)) {
 | ||
|         stmt('} else if (');
 | ||
|       } else {
 | ||
|         stmt('} else {');
 | ||
|       }
 | ||
|       continue;
 | ||
|     }
 | ||
| 
 | ||
|     if (read(/^%;/)) {
 | ||
|       end = null;
 | ||
|       stmt('}');
 | ||
|       continue;
 | ||
|     }
 | ||
| 
 | ||
|     buff += val[0];
 | ||
|     val = val.substring(1);
 | ||
|   }
 | ||
| 
 | ||
|   // Clear the buffer of any remaining text.
 | ||
|   clear();
 | ||
| 
 | ||
|   // Some terminfos (I'm looking at you, atari-color), don't end an if
 | ||
|   // statement. It's assumed terminfo will automatically end it for
 | ||
|   // them, because they are a bunch of lazy bastards.
 | ||
|   if (end != null) {
 | ||
|     stmt('}');
 | ||
|   }
 | ||
| 
 | ||
|   // Add the footer.
 | ||
|   stmt(footer);
 | ||
| 
 | ||
|   // Optimize and cleanup generated code.
 | ||
|   v = code.slice(header.length, -footer.length);
 | ||
|   if (!v.length) {
 | ||
|     code = 'return "";';
 | ||
|   } else if (v = /^out\.push\(("(?:[^"]|\\")+")\)$/.exec(v)) {
 | ||
|     code = 'return ' + v[1] + ';';
 | ||
|   } else {
 | ||
|     // Turn `(stack.push(v = params[0]), v),out.push(stack.pop())`
 | ||
|     // into `out.push(params[0])`.
 | ||
|     code = code.replace(
 | ||
|       /\(stack\.push\(v = params\[(\d+)\]\), v\),out\.push\(stack\.pop\(\)\)/g,
 | ||
|       'out.push(params[$1])');
 | ||
| 
 | ||
|     // Remove unnecessary variable initializations.
 | ||
|     v = code.slice(header.length, -footer.length);
 | ||
|     if (!~v.indexOf('v = ')) code = code.replace('v, ', '');
 | ||
|     if (!~v.indexOf('dyn')) code = code.replace('dyn = {}, ', '');
 | ||
|     if (!~v.indexOf('stat')) code = code.replace('stat = {}, ', '');
 | ||
|     if (!~v.indexOf('stack')) code = code.replace('stack = [], ', '');
 | ||
| 
 | ||
|     // Turn `var out = [];out.push("foo"),` into `var out = ["foo"];`.
 | ||
|     code = code.replace(
 | ||
|       /out = \[\];out\.push\(("(?:[^"]|\\")+")\),/,
 | ||
|       'out = [$1];');
 | ||
|   }
 | ||
| 
 | ||
|   // Terminfos `wyse350-vb`, and `wy350-w`
 | ||
|   // seem to have a few broken strings.
 | ||
|   if (str === '\u001b%?') {
 | ||
|     code = 'return "\\x1b";';
 | ||
|   }
 | ||
| 
 | ||
|   if (this.debug) {
 | ||
|     v = code
 | ||
|       .replace(/\x1b/g, '\\x1b')
 | ||
|       .replace(/\r/g, '\\r')
 | ||
|       .replace(/\n/g, '\\n');
 | ||
|     process.stdout.write(v + '\n');
 | ||
|   }
 | ||
| 
 | ||
|   try {
 | ||
|     if (this.options.stringify && code.indexOf('return ') === 0) {
 | ||
|       return new Function('', code)();
 | ||
|     }
 | ||
|     return this.printf || ~code.indexOf('sprintf(')
 | ||
|       ? new Function('sprintf, params', code).bind(null, sprintf)
 | ||
|       : new Function('params', code);
 | ||
|   } catch (e) {
 | ||
|     console.error('');
 | ||
|     console.error('Error on %s:', tkey);
 | ||
|     console.error(JSON.stringify(str));
 | ||
|     console.error('');
 | ||
|     console.error(code.replace(/(,|;)/g, '$1\n'));
 | ||
|     e.stack = e.stack.replace(/\x1b/g, '\\x1b');
 | ||
|     throw e;
 | ||
|   }
 | ||
| };
 | ||
| 
 | ||
| // See: ~/ncurses/ncurses/tinfo/lib_tputs.c
 | ||
| Tput.prototype._print = function(code, print, done) {
 | ||
|   var xon = !this.bools.needs_xon_xoff || this.bools.xon_xoff;
 | ||
| 
 | ||
|   print = print || write;
 | ||
|   done = done || noop;
 | ||
| 
 | ||
|   if (!this.padding) {
 | ||
|     print(code);
 | ||
|     return done();
 | ||
|   }
 | ||
| 
 | ||
|   var parts = code.split(/(?=\$<[\d.]+[*\/]{0,2}>)/)
 | ||
|     , i = 0;
 | ||
| 
 | ||
|   (function next() {
 | ||
|     if (i === parts.length) {
 | ||
|       return done();
 | ||
|     }
 | ||
| 
 | ||
|     var part = parts[i++]
 | ||
|       , padding = /^\$<([\d.]+)([*\/]{0,2})>/.exec(part)
 | ||
|       , amount
 | ||
|       , suffix;
 | ||
|       // , affect;
 | ||
| 
 | ||
|     if (!padding) {
 | ||
|       print(part);
 | ||
|       return next();
 | ||
|     }
 | ||
| 
 | ||
|     part = part.substring(padding[0].length);
 | ||
|     amount = +padding[1];
 | ||
|     suffix = padding[2];
 | ||
| 
 | ||
|     // A `/'  suffix indicates  that  the  padding  is  mandatory and forces a
 | ||
|     // delay of the given number of milliseconds even on devices for which xon
 | ||
|     // is present to indicate flow control.
 | ||
|     if (xon && !~suffix.indexOf('/')) {
 | ||
|       print(part);
 | ||
|       return next();
 | ||
|     }
 | ||
| 
 | ||
|     // A `*' indicates that the padding required is proportional to the number
 | ||
|     // of lines affected by the operation, and  the amount  given  is the
 | ||
|     // per-affected-unit padding required.  (In the case of insert character,
 | ||
|     // the factor is still the number of lines affected.) Normally, padding is
 | ||
|     // advisory if the device has the xon capability; it is used for cost
 | ||
|     // computation but does not trigger delays.
 | ||
|     if (~suffix.indexOf('*')) {
 | ||
|       // XXX Disable this for now.
 | ||
|       amount = amount;
 | ||
|       // if (affect = /\x1b\[(\d+)[LM]/.exec(part)) {
 | ||
|       //   amount *= +affect[1];
 | ||
|       // }
 | ||
|       // The above is a huge workaround. In reality, we need to compile
 | ||
|       // `_print` into the string functions and check the cap name and
 | ||
|       // params.
 | ||
|       // if (cap === 'insert_line' || cap === 'delete_line') {
 | ||
|       //   amount *= params[0];
 | ||
|       // }
 | ||
|       // if (cap === 'clear_screen') {
 | ||
|       //   amount *= process.stdout.rows;
 | ||
|       // }
 | ||
|     }
 | ||
| 
 | ||
|     return setTimeout(function() {
 | ||
|       print(part);
 | ||
|       return next();
 | ||
|     }, amount);
 | ||
|   })();
 | ||
| };
 | ||
| 
 | ||
| // A small helper function if we want
 | ||
| // to easily output text with setTimeouts.
 | ||
| Tput.print = function() {
 | ||
|   var fake = {
 | ||
|     padding: true,
 | ||
|     bools: { needs_xon_xoff: true, xon_xoff: false }
 | ||
|   };
 | ||
|   return Tput.prototype._print.apply(fake, arguments);
 | ||
| };
 | ||
| 
 | ||
| /**
 | ||
|  * 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
 | ||
|     , terms
 | ||
|     , term_
 | ||
|     , root
 | ||
|     , paths;
 | ||
| 
 | ||
|   term = term || this.terminal;
 | ||
| 
 | ||
|   // Termcap has a bunch of terminals usually stored in one file/string,
 | ||
|   // so we need to find the one containing our desired terminal.
 | ||
|   if (~term.indexOf(path.sep) && (terms = this._tryCap(path.resolve(term)))) {
 | ||
|     term_ = path.basename(term).split('.')[0];
 | ||
|     if (terms[process.env.TERM]) {
 | ||
|       term = process.env.TERM;
 | ||
|     } else if (terms[term_]) {
 | ||
|       term = term_;
 | ||
|     } else {
 | ||
|       term = Object.keys(terms)[0];
 | ||
|     }
 | ||
|   } else {
 | ||
|     paths = Tput.cpaths.slice();
 | ||
| 
 | ||
|     if (this.termcapFile) {
 | ||
|       paths.unshift(this.termcapFile);
 | ||
|     }
 | ||
| 
 | ||
|     paths.push(Tput.termcap);
 | ||
| 
 | ||
|     terms = this._tryCap(paths, term);
 | ||
|   }
 | ||
| 
 | ||
|   if (!terms) {
 | ||
|     throw new Error('Cannot find termcap for: ' + term);
 | ||
|   }
 | ||
| 
 | ||
|   root = terms[term];
 | ||
| 
 | ||
|   if (this.debug) {
 | ||
|     this._termcap = terms;
 | ||
|   }
 | ||
| 
 | ||
|   (function tc(term) {
 | ||
|     if (term && term.strings.tc) {
 | ||
|       root.inherits = root.inherits || [];
 | ||
|       root.inherits.push(term.strings.tc);
 | ||
| 
 | ||
|       var names = terms[term.strings.tc]
 | ||
|         ? terms[term.strings.tc].names
 | ||
|         : [term.strings.tc];
 | ||
| 
 | ||
|       self._debug('%s inherits from %s.',
 | ||
|         term.names.join('/'), names.join('/'));
 | ||
| 
 | ||
|       var inherit = tc(terms[term.strings.tc]);
 | ||
|       if (inherit) {
 | ||
|         ['bools', 'numbers', 'strings'].forEach(function(type) {
 | ||
|           merge(term[type], inherit[type]);
 | ||
|         });
 | ||
|       }
 | ||
|     }
 | ||
|     return term;
 | ||
|   })(root);
 | ||
| 
 | ||
|   // Translate termcap names to terminfo-style names.
 | ||
|   root = this.translateTermcap(root);
 | ||
| 
 | ||
|   return root;
 | ||
| };
 | ||
| 
 | ||
| Tput.prototype._tryCap = function(file, term) {
 | ||
|   if (!file) return;
 | ||
| 
 | ||
|   var terms
 | ||
|     , data
 | ||
|     , i;
 | ||
| 
 | ||
|   if (Array.isArray(file)) {
 | ||
|     for (i = 0; i < file.length; i++) {
 | ||
|       data = this._tryCap(file[i], term);
 | ||
|       if (data) return data;
 | ||
|     }
 | ||
|     return;
 | ||
|   }
 | ||
| 
 | ||
|   // If the termcap string starts with `/`,
 | ||
|   // ncurses considers it a filename.
 | ||
|   data = file[0] === '/'
 | ||
|     ? tryRead(file)
 | ||
|     : file;
 | ||
| 
 | ||
|   if (!data) return;
 | ||
| 
 | ||
|   terms = this.parseTermcap(data, file);
 | ||
| 
 | ||
|   if (term && !terms[term]) {
 | ||
|     return;
 | ||
|   }
 | ||
| 
 | ||
|   return terms;
 | ||
| };
 | ||
| 
 | ||
| /**
 | ||
|  * Termcap Parser
 | ||
|  *  http://en.wikipedia.org/wiki/Termcap
 | ||
|  *  http://www.gnu.org/software
 | ||
|  *    /termutils/manual/termcap-1.3/html_mono/termcap.html
 | ||
|  *  http://www.gnu.org/software
 | ||
|  *    /termutils/manual/termcap-1.3/html_mono/termcap.html#SEC17
 | ||
|  *  http://tldp.org/HOWTO/Text-Terminal-HOWTO.html#toc16
 | ||
|  *  man termcap
 | ||
|  */
 | ||
| 
 | ||
| // Example:
 | ||
| // vt102|dec vt102:\
 | ||
| //  :do=^J:co#80:li#24:cl=50\E[;H\E[2J:\
 | ||
| //  :le=^H:bs:cm=5\E[%i%d;%dH:nd=2\E[C:up=2\E[A:\
 | ||
| //  :ce=3\E[K:cd=50\E[J:so=2\E[7m:se=2\E[m:us=2\E[4m:ue=2\E[m:\
 | ||
| //  :md=2\E[1m:mr=2\E[7m:mb=2\E[5m:me=2\E[m:is=\E[1;24r\E[24;1H:\
 | ||
| //  :rs=\E>\E[?3l\E[?4l\E[?5l\E[?7h\E[?8h:ks=\E[?1h\E=:ke=\E[?1l\E>:\
 | ||
| //  :ku=\EOA:kd=\EOB:kr=\EOC:kl=\EOD:kb=^H:\
 | ||
| //  :ho=\E[H:k1=\EOP:k2=\EOQ:k3=\EOR:k4=\EOS:pt:sr=5\EM:vt#3:\
 | ||
| //  :sc=\E7:rc=\E8:cs=\E[%i%d;%dr:vs=\E[?7l:ve=\E[?7h:\
 | ||
| //  :mi:al=\E[L:dc=\E[P:dl=\E[M:ei=\E[4l:im=\E[4h:
 | ||
| 
 | ||
| Tput.prototype.parseTermcap = function(data, file) {
 | ||
|   var terms = {}
 | ||
|     , parts
 | ||
|     , term
 | ||
|     , entries
 | ||
|     , fields
 | ||
|     , field
 | ||
|     , names
 | ||
|     , i
 | ||
|     , j
 | ||
|     , k;
 | ||
| 
 | ||
|   // remove escaped newlines
 | ||
|   data = data.replace(/\\\n[ \t]*/g, '');
 | ||
| 
 | ||
|   // remove comments
 | ||
|   data = data.replace(/^#[^\n]+/gm, '');
 | ||
| 
 | ||
|   // split entries
 | ||
|   entries = data.trim().split(/\n+/);
 | ||
| 
 | ||
|   for (i = 0; i < entries.length; i++) {
 | ||
|     fields = entries[i].split(/:+/);
 | ||
|     for (j = 0; j < fields.length; j++) {
 | ||
|       field = fields[j].trim();
 | ||
|       if (!field) continue;
 | ||
| 
 | ||
|       if (j === 0) {
 | ||
|         names = field.split('|');
 | ||
|         term = {
 | ||
|           name: names[0],
 | ||
|           names: names,
 | ||
|           desc: names.pop(),
 | ||
|           file: ~file.indexOf(path.sep)
 | ||
|             ? path.resolve(file)
 | ||
|             : file,
 | ||
|           termcap: true
 | ||
|         };
 | ||
| 
 | ||
|         for (k = 0; k < names.length; k++) {
 | ||
|           terms[names[k]] = term;
 | ||
|         }
 | ||
| 
 | ||
|         term.bools = {};
 | ||
|         term.numbers = {};
 | ||
|         term.strings = {};
 | ||
| 
 | ||
|         continue;
 | ||
|       }
 | ||
| 
 | ||
|       if (~field.indexOf('=')) {
 | ||
|         parts = field.split('=');
 | ||
|         term.strings[parts[0]] = parts.slice(1).join('=');
 | ||
|       } else if (~field.indexOf('#')) {
 | ||
|         parts = field.split('#');
 | ||
|         term.numbers[parts[0]] = +parts.slice(1).join('#');
 | ||
|       } else {
 | ||
|         term.bools[field] = true;
 | ||
|       }
 | ||
|     }
 | ||
|   }
 | ||
| 
 | ||
|   return terms;
 | ||
| };
 | ||
| 
 | ||
| /**
 | ||
|  * Termcap Compiler
 | ||
|  *  man termcap
 | ||
|  */
 | ||
| 
 | ||
| Tput.prototype.translateTermcap = function(info) {
 | ||
|   var self = this
 | ||
|     , out = {};
 | ||
| 
 | ||
|   if (!info) return;
 | ||
| 
 | ||
|   this._debug(info);
 | ||
| 
 | ||
|   ['name', 'names', 'desc', 'file', 'termcap'].forEach(function(key) {
 | ||
|     out[key] = info[key];
 | ||
|   });
 | ||
| 
 | ||
|   // Separate aliases for termcap
 | ||
|   var map = (function() {
 | ||
|     var out = {};
 | ||
| 
 | ||
|     Object.keys(Tput.alias).forEach(function(key) {
 | ||
|       var aliases = Tput.alias[key];
 | ||
|       out[aliases.termcap] = key;
 | ||
|     });
 | ||
| 
 | ||
|     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) {
 | ||
|       if (key === 'strings') {
 | ||
|         info.strings[cap] = self._captoinfo(cap, info.strings[cap], 1);
 | ||
|       }
 | ||
|       if (map[cap]) {
 | ||
|         out[key][map[cap]] = info[key][cap];
 | ||
|       } else {
 | ||
|         // NOTE: Possibly include all termcap names
 | ||
|         // in a separate alias.js file. Some are
 | ||
|         // missing from the terminfo alias.js file
 | ||
|         // which is why we have to do this:
 | ||
|         // See: $ man termcap
 | ||
|         out[key][cap] = info[key][cap];
 | ||
|       }
 | ||
|     });
 | ||
|   });
 | ||
| 
 | ||
|   return out;
 | ||
| };
 | ||
| 
 | ||
| Tput.prototype.compileTermcap = function(term) {
 | ||
|   return this.compile(this.readTermcap(term));
 | ||
| };
 | ||
| 
 | ||
| Tput.prototype.injectTermcap = function(term) {
 | ||
|   return this.inject(this.compileTermcap(term));
 | ||
| };
 | ||
| 
 | ||
| /**
 | ||
|  * _nc_captoinfo - ported to javascript directly from ncurses.
 | ||
|  * Copyright (c) 1998-2009,2010 Free Software Foundation, Inc.
 | ||
|  * See: ~/ncurses/ncurses/tinfo/captoinfo.c
 | ||
|  *
 | ||
|  * Convert a termcap string to terminfo format.
 | ||
|  * 'cap' is the relevant terminfo capability index.
 | ||
|  * 's' is the string value of the capability.
 | ||
|  * 'parameterized' tells what type of translations to do:
 | ||
|  *    % translations if 1
 | ||
|  *    pad translations if >=0
 | ||
|  */
 | ||
| 
 | ||
| Tput.prototype._captoinfo = function(cap, s, parameterized) {
 | ||
|   var self = this;
 | ||
| 
 | ||
|   var capstart;
 | ||
| 
 | ||
|   if (parameterized == null) {
 | ||
|     parameterized = 0;
 | ||
|   }
 | ||
| 
 | ||
|   var MAX_PUSHED = 16
 | ||
|     , stack = [];
 | ||
| 
 | ||
|   var stackptr = 0
 | ||
|     , onstack = 0
 | ||
|     , seenm = 0
 | ||
|     , seenn = 0
 | ||
|     , seenr = 0
 | ||
|     , param = 1
 | ||
|     , i = 0
 | ||
|     , out = '';
 | ||
| 
 | ||
|   function warn() {
 | ||
|     var args = Array.prototype.slice.call(arguments);
 | ||
|     args[0] = 'captoinfo: ' + (args[0] || '');
 | ||
|     return self._debug.apply(self, args);
 | ||
|   }
 | ||
| 
 | ||
|   function isdigit(ch) {
 | ||
|     return ch >= '0' && ch <= '9';
 | ||
|   }
 | ||
| 
 | ||
|   function isgraph(ch) {
 | ||
|     return ch > ' ' && ch <= '~';
 | ||
|   }
 | ||
| 
 | ||
|   // convert a character to a terminfo push
 | ||
|   function cvtchar(sp) {
 | ||
|     var c = '\0'
 | ||
|       , len;
 | ||
| 
 | ||
|     var j = i;
 | ||
| 
 | ||
|     switch (sp[j]) {
 | ||
|       case '\\':
 | ||
|         switch (sp[++j]) {
 | ||
|           case '\'':
 | ||
|           case '$':
 | ||
|           case '\\':
 | ||
|           case '%':
 | ||
|             c = sp[j];
 | ||
|             len = 2;
 | ||
|             break;
 | ||
|           case '\0':
 | ||
|             c = '\\';
 | ||
|             len = 1;
 | ||
|             break;
 | ||
|           case '0':
 | ||
|           case '1':
 | ||
|           case '2':
 | ||
|           case '3':
 | ||
|             len = 1;
 | ||
|             while (isdigit(sp[j])) {
 | ||
|               c = String.fromCharCode(8 * c.charCodeAt(0)
 | ||
|                 + (sp[j++].charCodeAt(0) - '0'.charCodeAt(0)));
 | ||
|               len++;
 | ||
|             }
 | ||
|             break;
 | ||
|           default:
 | ||
|             c = sp[j];
 | ||
|             len = 2;
 | ||
|             break;
 | ||
|         }
 | ||
|         break;
 | ||
|       case '^':
 | ||
|         c = String.fromCharCode(sp[++j].charCodeAt(0) & 0x1f);
 | ||
|         len = 2;
 | ||
|         break;
 | ||
|       default:
 | ||
|         c = sp[j];
 | ||
|         len = 1;
 | ||
|     }
 | ||
|     if (isgraph(c) && c !== ',' && c !== '\'' && c !== '\\' && c !== ':') {
 | ||
|       out += '%\'';
 | ||
|       out += c;
 | ||
|       out += '\'';
 | ||
|     } else {
 | ||
|       out += '%{';
 | ||
|       if (c.charCodeAt(0) > 99) {
 | ||
|         out += String.fromCharCode(
 | ||
|           (c.charCodeAt(0) / 100 | 0) + '0'.charCodeAt(0));
 | ||
|       }
 | ||
|       if (c.charCodeAt(0) > 9) {
 | ||
|         out += String.fromCharCode(
 | ||
|           (c.charCodeAt(0) / 10 | 0) % 10 + '0'.charCodeAt(0));
 | ||
|       }
 | ||
|       out += String.fromCharCode(
 | ||
|         c.charCodeAt(0) % 10 + '0'.charCodeAt(0));
 | ||
|       out += '}';
 | ||
|     }
 | ||
| 
 | ||
|     return len;
 | ||
|   }
 | ||
| 
 | ||
|   // push n copies of param on the terminfo stack if not already there
 | ||
|   function getparm(parm, n) {
 | ||
|     if (seenr) {
 | ||
|       if (parm === 1) {
 | ||
|         parm = 2;
 | ||
|       } else if (parm === 2) {
 | ||
|         parm = 1;
 | ||
|       }
 | ||
|     }
 | ||
| 
 | ||
|     if (onstack === parm) {
 | ||
|       if (n > 1) {
 | ||
|         warn('string may not be optimal');
 | ||
|         out += '%Pa';
 | ||
|         while (n--) {
 | ||
|           out += '%ga';
 | ||
|         }
 | ||
|       }
 | ||
|       return;
 | ||
|     }
 | ||
| 
 | ||
|     if (onstack !== 0) {
 | ||
|       push();
 | ||
|     }
 | ||
| 
 | ||
|     onstack = parm;
 | ||
| 
 | ||
|     while (n--) {
 | ||
|       out += '%p';
 | ||
|       out += String.fromCharCode('0'.charCodeAt(0) + parm);
 | ||
|     }
 | ||
| 
 | ||
|     if (seenn && parm < 3) {
 | ||
|       out += '%{96}%^';
 | ||
|     }
 | ||
| 
 | ||
|     if (seenm && parm < 3) {
 | ||
|       out += '%{127}%^';
 | ||
|     }
 | ||
|   }
 | ||
| 
 | ||
|   // push onstack on to the stack
 | ||
|   function push() {
 | ||
|     if (stackptr >= MAX_PUSHED) {
 | ||
|       warn('string too complex to convert');
 | ||
|     } else {
 | ||
|       stack[stackptr++] = onstack;
 | ||
|     }
 | ||
|   }
 | ||
| 
 | ||
|   // pop the top of the stack into onstack
 | ||
|   function pop() {
 | ||
|     if (stackptr === 0) {
 | ||
|       if (onstack === 0) {
 | ||
|         warn('I\'m confused');
 | ||
|       } else {
 | ||
|         onstack = 0;
 | ||
|       }
 | ||
|     } else {
 | ||
|       onstack = stack[--stackptr];
 | ||
|     }
 | ||
|     param++;
 | ||
|   }
 | ||
| 
 | ||
|   function see03() {
 | ||
|     getparm(param, 1);
 | ||
|     out += '%3d';
 | ||
|     pop();
 | ||
|   }
 | ||
| 
 | ||
|   function invalid() {
 | ||
|     out += '%';
 | ||
|     i--;
 | ||
|     warn('unknown %% code %s (%#x) in %s',
 | ||
|       JSON.stringify(s[i]), s[i].charCodeAt(0), cap);
 | ||
|   }
 | ||
| 
 | ||
|   // skip the initial padding (if we haven't been told not to)
 | ||
|   capstart = null;
 | ||
|   if (s == null) s = '';
 | ||
| 
 | ||
|   if (parameterized >= 0 && isdigit(s[i])) {
 | ||
|     for (capstart = i;; i++) {
 | ||
|       if (!(isdigit(s[i]) || s[i] === '*' || s[i] === '.')) {
 | ||
|         break;
 | ||
|       }
 | ||
|     }
 | ||
|   }
 | ||
| 
 | ||
|   while (s[i]) {
 | ||
|     switch (s[i]) {
 | ||
|       case '%':
 | ||
|         i++;
 | ||
|         if (parameterized < 1) {
 | ||
|           out += '%';
 | ||
|           break;
 | ||
|         }
 | ||
|         switch (s[i++]) {
 | ||
|           case '%':
 | ||
|             out += '%';
 | ||
|             break;
 | ||
|           case 'r':
 | ||
|             if (seenr++ === 1) {
 | ||
|               warn('saw %%r twice in %s', cap);
 | ||
|             }
 | ||
|             break;
 | ||
|           case 'm':
 | ||
|             if (seenm++ === 1) {
 | ||
|               warn('saw %%m twice in %s', cap);
 | ||
|             }
 | ||
|             break;
 | ||
|           case 'n':
 | ||
|             if (seenn++ === 1) {
 | ||
|               warn('saw %%n twice in %s', cap);
 | ||
|             }
 | ||
|             break;
 | ||
|           case 'i':
 | ||
|             out += '%i';
 | ||
|             break;
 | ||
|           case '6':
 | ||
|           case 'B':
 | ||
|             getparm(param, 1);
 | ||
|             out += '%{10}%/%{16}%*';
 | ||
|             getparm(param, 1);
 | ||
|             out += '%{10}%m%+';
 | ||
|             break;
 | ||
|           case '8':
 | ||
|           case 'D':
 | ||
|             getparm(param, 2);
 | ||
|             out += '%{2}%*%-';
 | ||
|             break;
 | ||
|           case '>':
 | ||
|             getparm(param, 2);
 | ||
|             // %?%{x}%>%t%{y}%+%;
 | ||
|             out += '%?';
 | ||
|             i += cvtchar(s);
 | ||
|             out += '%>%t';
 | ||
|             i += cvtchar(s);
 | ||
|             out += '%+%;';
 | ||
|             break;
 | ||
|           case 'a':
 | ||
|             if ((s[i] === '=' || s[i] === '+' || s[i] === '-'
 | ||
|                 || s[i] === '*' || s[i] === '/')
 | ||
|                 && (s[i + 1] === 'p' || s[i + 1] === 'c')
 | ||
|                 && s[i + 2] !== '\0' && s[i + 2]) {
 | ||
|               var l;
 | ||
|               l = 2;
 | ||
|               if (s[i] !== '=') {
 | ||
|                 getparm(param, 1);
 | ||
|               }
 | ||
|               if (s[i + 1] === 'p') {
 | ||
|                 getparm(param + s[i + 2].charCodeAt(0) - '@'.charCodeAt(0), 1);
 | ||
|                 if (param !== onstack) {
 | ||
|                   pop();
 | ||
|                   param--;
 | ||
|                 }
 | ||
|                 l++;
 | ||
|               } else {
 | ||
|                 i += 2, l += cvtchar(s), i -= 2;
 | ||
|               }
 | ||
|               switch (s[i]) {
 | ||
|                 case '+':
 | ||
|                   out += '%+';
 | ||
|                   break;
 | ||
|                 case '-':
 | ||
|                   out += '%-';
 | ||
|                   break;
 | ||
|                 case '*':
 | ||
|                   out += '%*';
 | ||
|                   break;
 | ||
|                 case '/':
 | ||
|                   out += '%/';
 | ||
|                   break;
 | ||
|                 case '=':
 | ||
|                   if (seenr) {
 | ||
|                     if (param === 1) {
 | ||
|                       onstack = 2;
 | ||
|                     } else if (param === 2) {
 | ||
|                       onstack = 1;
 | ||
|                     } else {
 | ||
|                       onstack = param;
 | ||
|                     }
 | ||
|                   } else {
 | ||
|                     onstack = param;
 | ||
|                   }
 | ||
|                   break;
 | ||
|               }
 | ||
|               i += l;
 | ||
|               break;
 | ||
|             }
 | ||
|             getparm(param, 1);
 | ||
|             i += cvtchar(s);
 | ||
|             out += '%+';
 | ||
|             break;
 | ||
|           case '+':
 | ||
|             getparm(param, 1);
 | ||
|             i += cvtchar(s);
 | ||
|             out += '%+%c';
 | ||
|             pop();
 | ||
|             break;
 | ||
|           case 's':
 | ||
| // #ifdef WATERLOO
 | ||
| //          i += cvtchar(s);
 | ||
| //          getparm(param, 1);
 | ||
| //          out += '%-';
 | ||
| // #else
 | ||
|             getparm(param, 1);
 | ||
|             out += '%s';
 | ||
|             pop();
 | ||
| // #endif /* WATERLOO */
 | ||
|             break;
 | ||
|           case '-':
 | ||
|             i += cvtchar(s);
 | ||
|             getparm(param, 1);
 | ||
|             out += '%-%c';
 | ||
|             pop();
 | ||
|             break;
 | ||
|           case '.':
 | ||
|             getparm(param, 1);
 | ||
|             out += '%c';
 | ||
|             pop();
 | ||
|             break;
 | ||
|           case '0': // not clear any of the historical termcaps did this
 | ||
|             if (s[i] === '3') {
 | ||
|               see03(); // goto
 | ||
|               break;
 | ||
|             } else if (s[i] !== '2') {
 | ||
|               invalid(); // goto
 | ||
|               break;
 | ||
|             }
 | ||
|             // FALLTHRU
 | ||
|           case '2':
 | ||
|             getparm(param, 1);
 | ||
|             out += '%2d';
 | ||
|             pop();
 | ||
|             break;
 | ||
|           case '3':
 | ||
|             see03();
 | ||
|             break;
 | ||
|           case 'd':
 | ||
|             getparm(param, 1);
 | ||
|             out += '%d';
 | ||
|             pop();
 | ||
|             break;
 | ||
|           case 'f':
 | ||
|             param++;
 | ||
|             break;
 | ||
|           case 'b':
 | ||
|             param--;
 | ||
|             break;
 | ||
|           case '\\':
 | ||
|             out += '%\\';
 | ||
|             break;
 | ||
|           default:
 | ||
|             invalid();
 | ||
|             break;
 | ||
|         }
 | ||
|         break;
 | ||
| // #ifdef REVISIBILIZE
 | ||
| //    case '\\':
 | ||
| //      out += s[i++];
 | ||
| //      out += s[i++];
 | ||
| //      break;
 | ||
| //    case '\n':
 | ||
| //      out += '\\n';
 | ||
| //      i++;
 | ||
| //      break;
 | ||
| //    case '\t':
 | ||
| //      out += '\\t';
 | ||
| //      i++;
 | ||
| //      break;
 | ||
| //    case '\r':
 | ||
| //      out += '\\r';
 | ||
| //      i++;
 | ||
| //      break;
 | ||
| //    case '\200':
 | ||
| //      out += '\\0';
 | ||
| //      i++;
 | ||
| //      break;
 | ||
| //    case '\f':
 | ||
| //      out += '\\f';
 | ||
| //      i++;
 | ||
| //      break;
 | ||
| //    case '\b':
 | ||
| //      out += '\\b';
 | ||
| //      i++;
 | ||
| //      break;
 | ||
| //    case ' ':
 | ||
| //      out += '\\s';
 | ||
| //      i++;
 | ||
| //      break;
 | ||
| //    case '^':
 | ||
| //      out += '\\^';
 | ||
| //      i++;
 | ||
| //      break;
 | ||
| //    case ':':
 | ||
| //      out += '\\:';
 | ||
| //      i++;
 | ||
| //      break;
 | ||
| //    case ',':
 | ||
| //      out += '\\,';
 | ||
| //      i++;
 | ||
| //      break;
 | ||
| //    default:
 | ||
| //      if (s[i] === '\033') {
 | ||
| //        out += '\\E';
 | ||
| //        i++;
 | ||
| //      } else if (s[i].charCodeAt(0) > 0 && s[i].charCodeAt(0) < 32) {
 | ||
| //        out += '^';
 | ||
| //        out += String.fromCharCode(s[i].charCodeAt(0) + '@'.charCodeAt(0));
 | ||
| //        i++;
 | ||
| //      } else if (s[i].charCodeAt(0) <= 0 || s[i].charCodeAt(0) >= 127) {
 | ||
| //        out += '\\';
 | ||
| //        out += String.fromCharCode(
 | ||
| //          ((s[i].charCodeAt(0) & 0300) >> 6) + '0'.charCodeAt(0));
 | ||
| //        out += String.fromCharCode(
 | ||
| //          ((s[i].charCodeAt(0) & 0070) >> 3) + '0'.charCodeAt(0));
 | ||
| //        out += String.fromCharCode(
 | ||
| //          (s[i].charCodeAt(0) & 0007) + '0'.charCodeAt(0));
 | ||
| //        i++;
 | ||
| //      } else {
 | ||
| //        out += s[i++];
 | ||
| //      }
 | ||
| //      break;
 | ||
| // #else
 | ||
|       default:
 | ||
|         out += s[i++];
 | ||
|         break;
 | ||
| // #endif
 | ||
|     }
 | ||
|   }
 | ||
| 
 | ||
|   // Now, if we stripped off some leading padding, add it at the end
 | ||
|   // of the string as mandatory padding.
 | ||
|   if (capstart != null) {
 | ||
|     out += '$<';
 | ||
|     for (i = capstart;; i++) {
 | ||
|       if (isdigit(s[i]) || s[i] === '*' || s[i] === '.') {
 | ||
|         out += s[i];
 | ||
|       } else {
 | ||
|         break;
 | ||
|       }
 | ||
|     }
 | ||
|     out += '/>';
 | ||
|   }
 | ||
| 
 | ||
|   if (s !== out) {
 | ||
|     warn('Translating %s from %s to %s.',
 | ||
|       cap, JSON.stringify(s), JSON.stringify(out));
 | ||
|   }
 | ||
| 
 | ||
|   return out;
 | ||
| };
 | ||
| 
 | ||
| /**
 | ||
|  * Compile All Terminfo
 | ||
|  */
 | ||
| 
 | ||
| Tput.prototype.getAll = function() {
 | ||
|   var dir = this._prefix()
 | ||
|     , list = asort(fs.readdirSync(dir))
 | ||
|     , infos = [];
 | ||
| 
 | ||
|   list.forEach(function(letter) {
 | ||
|     var terms = asort(fs.readdirSync(path.resolve(dir, letter)));
 | ||
|     infos.push.apply(infos, terms);
 | ||
|   });
 | ||
| 
 | ||
|   function asort(obj) {
 | ||
|     return obj.sort(function(a, b) {
 | ||
|       a = a.toLowerCase().charCodeAt(0);
 | ||
|       b = b.toLowerCase().charCodeAt(0);
 | ||
|       return a - b;
 | ||
|     });
 | ||
|   }
 | ||
| 
 | ||
|   return infos;
 | ||
| };
 | ||
| 
 | ||
| Tput.prototype.compileAll = function(start) {
 | ||
|   var self = this
 | ||
|     , all = {};
 | ||
| 
 | ||
|   this.getAll().forEach(function(name) {
 | ||
|     if (start && name !== start) {
 | ||
|       return;
 | ||
|     } else {
 | ||
|       start = null;
 | ||
|     }
 | ||
|     all[name] = self.compileTerminfo(name);
 | ||
|   });
 | ||
| 
 | ||
|   return all;
 | ||
| };
 | ||
| 
 | ||
| /**
 | ||
|  * Detect Features / Quirks
 | ||
|  */
 | ||
| 
 | ||
| Tput.prototype.detectFeatures = function(info) {
 | ||
|   var data = this.parseACS(info);
 | ||
|   info.features = {
 | ||
|     unicode: this.detectUnicode(info),
 | ||
|     brokenACS: this.detectBrokenACS(info),
 | ||
|     PCRomSet: this.detectPCRomSet(info),
 | ||
|     magicCookie: this.detectMagicCookie(info),
 | ||
|     padding: this.detectPadding(info),
 | ||
|     setbuf: this.detectSetbuf(info),
 | ||
|     acsc: data.acsc,
 | ||
|     acscr: data.acscr
 | ||
|   };
 | ||
|   return info.features;
 | ||
| };
 | ||
| 
 | ||
| Tput.prototype.detectUnicode = function() {
 | ||
|   if (this.options.forceUnicode != null) {
 | ||
|     return this.options.forceUnicode;
 | ||
|   }
 | ||
| 
 | ||
|   var LANG = process.env.LANG
 | ||
|     + ':' + process.env.LANGUAGE
 | ||
|     + ':' + process.env.LC_ALL
 | ||
|     + ':' + process.env.LC_CTYPE;
 | ||
| 
 | ||
|   return /utf-?8/i.test(LANG) || (this.GetConsoleCP() === 65001);
 | ||
| };
 | ||
| 
 | ||
| // 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/base/lib_set_term.c
 | ||
| // ~/ncurses/ncurses/tinfo/lib_acs.c
 | ||
| // ~/ncurses/ncurses/tinfo/tinfo_driver.c
 | ||
| // ~/ncurses/ncurses/tinfo/lib_setup.c
 | ||
| Tput.prototype.detectBrokenACS = function(info) {
 | ||
|   // ncurses-compatible env variable.
 | ||
|   if (process.env.NCURSES_NO_UTF8_ACS != null) {
 | ||
|     return !!+process.env.NCURSES_NO_UTF8_ACS;
 | ||
|   }
 | ||
| 
 | ||
|   // If the terminal supports unicode, we don't need ACS.
 | ||
|   if (info.numbers.U8 >= 0) {
 | ||
|     return !!info.numbers.U8;
 | ||
|   }
 | ||
| 
 | ||
|   // The linux console is just broken for some reason.
 | ||
|   // Apparently the Linux console does not support ACS,
 | ||
|   // but it does support the PC ROM character set.
 | ||
|   if (info.name === 'linux') {
 | ||
|     return true;
 | ||
|   }
 | ||
| 
 | ||
|   // PC alternate charset
 | ||
|   // if (acsc.indexOf('+\x10,\x11-\x18.\x190') === 0) {
 | ||
|   if (this.detectPCRomSet(info)) {
 | ||
|     return true;
 | ||
|   }
 | ||
| 
 | ||
|   // screen termcap is bugged?
 | ||
|   if (this.termcap
 | ||
|       && info.name.indexOf('screen') === 0
 | ||
|       && process.env.TERMCAP
 | ||
|       && ~process.env.TERMCAP.indexOf('screen')
 | ||
|       && ~process.env.TERMCAP.indexOf('hhII00')) {
 | ||
|     if (~info.strings.enter_alt_charset_mode.indexOf('\016')
 | ||
|         || ~info.strings.enter_alt_charset_mode.indexOf('\017')
 | ||
|         || ~info.strings.set_attributes.indexOf('\016')
 | ||
|         || ~info.strings.set_attributes.indexOf('\017')) {
 | ||
|       return true;
 | ||
|     }
 | ||
|   }
 | ||
| 
 | ||
|   return false;
 | ||
| };
 | ||
| 
 | ||
| // If enter_pc_charset is the same as enter_alt_charset,
 | ||
| // the terminal does not support SCLD as ACS.
 | ||
| // See: ~/ncurses/ncurses/tinfo/lib_acs.c
 | ||
| Tput.prototype.detectPCRomSet = function(info) {
 | ||
|   var s = info.strings;
 | ||
|   if (s.enter_pc_charset_mode && s.enter_alt_charset_mode
 | ||
|       && s.enter_pc_charset_mode === s.enter_alt_charset_mode
 | ||
|       && s.exit_pc_charset_mode === s.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(info) {
 | ||
|   var data = {};
 | ||
| 
 | ||
|   data.acsc = {};
 | ||
|   data.acscr = {};
 | ||
| 
 | ||
|   // Possibly just return an empty object, as done here, instead of
 | ||
|   // specifically saying ACS is "broken" above. This would be more
 | ||
|   // accurate to ncurses logic. But it doesn't really matter.
 | ||
|   if (this.detectPCRomSet(info)) {
 | ||
|     return data;
 | ||
|   }
 | ||
| 
 | ||
|   // See: ~/ncurses/ncurses/tinfo/lib_acs.c: L208
 | ||
|   Object.keys(Tput.acsc).forEach(function(ch) {
 | ||
|     var acs_chars = info.strings.acs_chars || ''
 | ||
|       , i = acs_chars.indexOf(ch)
 | ||
|       , next = acs_chars[i + 1];
 | ||
| 
 | ||
|     if (!next || i === -1 || !Tput.acsc[next]) {
 | ||
|       return;
 | ||
|     }
 | ||
| 
 | ||
|     data.acsc[ch] = Tput.acsc[next];
 | ||
|     data.acscr[Tput.acsc[next]] = ch;
 | ||
|   });
 | ||
| 
 | ||
|   return data;
 | ||
| };
 | ||
| 
 | ||
| Tput.prototype.GetConsoleCP = function() {
 | ||
|   var ccp;
 | ||
| 
 | ||
|   if (process.platform !== 'win32') {
 | ||
|     return -1;
 | ||
|   }
 | ||
| 
 | ||
|   // Allow unicode on all windows consoles for now:
 | ||
|   if (+process.env.NCURSES_UNICODE !== 0) {
 | ||
|     return 65001;
 | ||
|   }
 | ||
| 
 | ||
|   // cp.execSync('chcp 65001', { stdio: 'ignore', timeout: 1500 });
 | ||
| 
 | ||
|   try {
 | ||
|     // Produces something like: 'Active code page: 437\n\n'
 | ||
|     ccp = cp.execFileSync(process.env.WINDIR + '\\system32\\chcp.com', [], {
 | ||
|       stdio: ['ignore', 'pipe', 'ignore'],
 | ||
|       encoding: 'ascii',
 | ||
|       timeout: 1500
 | ||
|     });
 | ||
|     // ccp = cp.execSync('chcp', {
 | ||
|     //   stdio: ['ignore', 'pipe', 'ignore'],
 | ||
|     //   encoding: 'ascii',
 | ||
|     //   timeout: 1500
 | ||
|     // });
 | ||
|   } catch (e) {
 | ||
|     ;
 | ||
|   }
 | ||
| 
 | ||
|   ccp = /\d+/.exec(ccp);
 | ||
| 
 | ||
|   if (!ccp) {
 | ||
|     return -1;
 | ||
|   }
 | ||
| 
 | ||
|   ccp = +ccp[0];
 | ||
| 
 | ||
|   return ccp;
 | ||
| };
 | ||
| 
 | ||
| /**
 | ||
|  * Helpers
 | ||
|  */
 | ||
| 
 | ||
| function noop() {
 | ||
|   return '';
 | ||
| }
 | ||
| 
 | ||
| noop.unsupported = true;
 | ||
| 
 | ||
| function merge(a, b) {
 | ||
|   Object.keys(b).forEach(function(key) {
 | ||
|     a[key] = b[key];
 | ||
|   });
 | ||
|   return a;
 | ||
| }
 | ||
| 
 | ||
| function write(data) {
 | ||
|   return process.stdout.write(data);
 | ||
| }
 | ||
| 
 | ||
| function tryRead(file) {
 | ||
|   if (Array.isArray(file)) {
 | ||
|     for (var i = 0; i < file.length; i++) {
 | ||
|       var data = tryRead(file[i]);
 | ||
|       if (data) return data;
 | ||
|     }
 | ||
|     return '';
 | ||
|   }
 | ||
|   if (!file) return '';
 | ||
|   file = path.resolve.apply(path, arguments);
 | ||
|   try {
 | ||
|     return fs.readFileSync(file, 'utf8');
 | ||
|   } catch (e) {
 | ||
|     return '';
 | ||
|   }
 | ||
| }
 | ||
| 
 | ||
| /**
 | ||
|  * sprintf
 | ||
|  *  http://www.cplusplus.com/reference/cstdio/printf/
 | ||
|  */
 | ||
| 
 | ||
| function sprintf(src) {
 | ||
|   var params = Array.prototype.slice.call(arguments, 1)
 | ||
|     , rule = /%([\-+# ]{1,4})?(\d+(?:\.\d+)?)?([doxXsc])/g
 | ||
|     , i = 0;
 | ||
| 
 | ||
|   return src.replace(rule, function(_, flag, width, type) {
 | ||
|     var flags = (flag || '').split('')
 | ||
|       , param = params[i] != null ? params[i] : ''
 | ||
|       , initial = param
 | ||
|       // , width = +width
 | ||
|       , opt = {}
 | ||
|       , pre = '';
 | ||
| 
 | ||
|     i++;
 | ||
| 
 | ||
|     switch (type) {
 | ||
|       case 'd': // signed int
 | ||
|         param = (+param).toString(10);
 | ||
|         break;
 | ||
|       case 'o': // unsigned octal
 | ||
|         param = (+param).toString(8);
 | ||
|         break;
 | ||
|       case 'x': // unsigned hex int
 | ||
|         param = (+param).toString(16);
 | ||
|         break;
 | ||
|       case 'X': // unsigned hex int uppercase
 | ||
|         param = (+param).toString(16).toUppercase();
 | ||
|         break;
 | ||
|       case 's': // string
 | ||
|         break;
 | ||
|       case 'c': // char
 | ||
|         param = isFinite(param)
 | ||
|           ? String.fromCharCode(param || 0200)
 | ||
|           : '';
 | ||
|         break;
 | ||
|     }
 | ||
| 
 | ||
|     flags.forEach(function(flag) {
 | ||
|       switch (flag) {
 | ||
|         // left-justify by width
 | ||
|         case '-':
 | ||
|           opt.left = true;
 | ||
|           break;
 | ||
|         // always precede numbers with their signs
 | ||
|         case '+':
 | ||
|           opt.signs = true;
 | ||
|           break;
 | ||
|         // used with o, x, X - value is preceded with 0, 0x, or 0X respectively.
 | ||
|         // used with a, A, e, E, f, F, g, G - forces written output to contain
 | ||
|         // a decimal point even if no more digits follow
 | ||
|         case '#':
 | ||
|           opt.hexpoint = true;
 | ||
|           break;
 | ||
|         // if no sign is going to be written, black space in front of the value
 | ||
|         case ' ':
 | ||
|           opt.space = true;
 | ||
|           break;
 | ||
|       }
 | ||
|     });
 | ||
| 
 | ||
|     width = +width.split('.')[0];
 | ||
| 
 | ||
|     // Should this be for opt.left too?
 | ||
|     // Example: %2.2X - turns 0 into 00
 | ||
|     if (width && !opt.left) {
 | ||
|       param = param + '';
 | ||
|       while (param.length < width) {
 | ||
|         param = '0' + param;
 | ||
|       }
 | ||
|     }
 | ||
| 
 | ||
|     if (opt.signs) {
 | ||
|       if (+initial >= 0) {
 | ||
|         pre += '+';
 | ||
|       }
 | ||
|     }
 | ||
| 
 | ||
|     if (opt.space) {
 | ||
|       if (!opt.signs && +initial >= 0) {
 | ||
|         pre += ' ';
 | ||
|       }
 | ||
|     }
 | ||
| 
 | ||
|     if (opt.hexpoint) {
 | ||
|       switch (type) {
 | ||
|         case 'o': // unsigned octal
 | ||
|           pre += '0';
 | ||
|           break;
 | ||
|         case 'x': // unsigned hex int
 | ||
|           pre += '0x';
 | ||
|           break;
 | ||
|         case 'X': // unsigned hex int uppercase
 | ||
|           pre += '0X';
 | ||
|           break;
 | ||
|       }
 | ||
|     }
 | ||
| 
 | ||
|     if (opt.left) {
 | ||
|       if (width > (pre.length + param.length)) {
 | ||
|         width -= pre.length + param.length;
 | ||
|         pre = Array(width + 1).join(' ') + pre;
 | ||
|       }
 | ||
|     }
 | ||
| 
 | ||
|     return pre + param;
 | ||
|   });
 | ||
| }
 | ||
| 
 | ||
| /**
 | ||
|  * Aliases
 | ||
|  */
 | ||
| 
 | ||
| Tput._alias = Require('term/alias');
 | ||
| 
 | ||
| Tput.alias = {};
 | ||
| 
 | ||
| ['bools', 'numbers', 'strings'].forEach(function(type) {
 | ||
|   Object.keys(Tput._alias[type]).forEach(function(key) {
 | ||
|     var aliases = Tput._alias[type][key];
 | ||
|     Tput.alias[key] = [aliases[0]];
 | ||
|     Tput.alias[key].terminfo = aliases[0];
 | ||
|     Tput.alias[key].termcap = aliases[1];
 | ||
|   });
 | ||
| });
 | ||
| 
 | ||
| // Bools
 | ||
| Tput.alias.no_esc_ctlc.push('beehive_glitch');
 | ||
| Tput.alias.dest_tabs_magic_smso.push('teleray_glitch');
 | ||
| 
 | ||
| // Numbers
 | ||
| Tput.alias.micro_col_size.push('micro_char_size');
 | ||
| 
 | ||
| /**
 | ||
|  * Feature Checking
 | ||
|  */
 | ||
| 
 | ||
| Tput.aliasMap = {};
 | ||
| 
 | ||
| Object.keys(Tput.alias).forEach(function(key) {
 | ||
|   Tput.aliasMap[key] = key;
 | ||
|   Tput.alias[key].forEach(function(k) {
 | ||
|     Tput.aliasMap[k] = key;
 | ||
|   });
 | ||
| });
 | ||
| 
 | ||
| Tput.prototype.has = function(name) {
 | ||
|   name = Tput.aliasMap[name];
 | ||
| 
 | ||
|   var val = this.all[name];
 | ||
| 
 | ||
|   if (!name) return false;
 | ||
| 
 | ||
|   if (typeof val === 'number') {
 | ||
|     return val !== -1;
 | ||
|   }
 | ||
| 
 | ||
|   return !!val;
 | ||
| };
 | ||
| 
 | ||
| /**
 | ||
|  * Fallback Termcap Entry
 | ||
|  */
 | ||
| 
 | ||
| Tput.termcap = ''
 | ||
|   + 'vt102|dec vt102:'
 | ||
|   + ':do=^J:co#80:li#24:cl=50\\E[;H\\E[2J:'
 | ||
|   + ':le=^H:bs:cm=5\\E[%i%d;%dH:nd=2\\E[C:up=2\\E[A:'
 | ||
|   + ':ce=3\\E[K:cd=50\\E[J:so=2\\E[7m:se=2\\E[m:us=2\\E[4m:ue=2\\E[m:'
 | ||
|   + ':md=2\\E[1m:mr=2\\E[7m:mb=2\\E[5m:me=2\\E[m:is=\\E[1;24r\\E[24;1H:'
 | ||
|   + ':rs=\\E>\\E[?3l\\E[?4l\\E[?5l\\E[?7h\\E[?8h:ks=\\E[?1h\\E=:ke=\\E[?1l\\E>:'
 | ||
|   + ':ku=\\EOA:kd=\\EOB:kr=\\EOC:kl=\\EOD:kb=^H:\\\n'
 | ||
|   + ':ho=\\E[H:k1=\\EOP:k2=\\EOQ:k3=\\EOR:k4=\\EOS:pt:sr=5\\EM:vt#3:'
 | ||
|   + ':sc=\\E7:rc=\\E8:cs=\\E[%i%d;%dr:vs=\\E[?7l:ve=\\E[?7h:'
 | ||
|   + ':mi:al=\\E[L:dc=\\E[P:dl=\\E[M:ei=\\E[4l:im=\\E[4h:';
 | ||
| 
 | ||
| /**
 | ||
|  * Terminfo Data
 | ||
|  */
 | ||
| 
 | ||
| Tput.bools = [
 | ||
|   'auto_left_margin',
 | ||
|   'auto_right_margin',
 | ||
|   'no_esc_ctlc',
 | ||
|   'ceol_standout_glitch',
 | ||
|   'eat_newline_glitch',
 | ||
|   'erase_overstrike',
 | ||
|   'generic_type',
 | ||
|   'hard_copy',
 | ||
|   'has_meta_key',
 | ||
|   'has_status_line',
 | ||
|   'insert_null_glitch',
 | ||
|   'memory_above',
 | ||
|   'memory_below',
 | ||
|   'move_insert_mode',
 | ||
|   'move_standout_mode',
 | ||
|   'over_strike',
 | ||
|   'status_line_esc_ok',
 | ||
|   'dest_tabs_magic_smso',
 | ||
|   'tilde_glitch',
 | ||
|   'transparent_underline',
 | ||
|   'xon_xoff',
 | ||
|   'needs_xon_xoff',
 | ||
|   'prtr_silent',
 | ||
|   'hard_cursor',
 | ||
|   'non_rev_rmcup',
 | ||
|   'no_pad_char',
 | ||
|   'non_dest_scroll_region',
 | ||
|   'can_change',
 | ||
|   'back_color_erase',
 | ||
|   'hue_lightness_saturation',
 | ||
|   'col_addr_glitch',
 | ||
|   'cr_cancels_micro_mode',
 | ||
|   'has_print_wheel',
 | ||
|   'row_addr_glitch',
 | ||
|   'semi_auto_right_margin',
 | ||
|   'cpi_changes_res',
 | ||
|   'lpi_changes_res',
 | ||
| 
 | ||
|   // #ifdef __INTERNAL_CAPS_VISIBLE
 | ||
|   'backspaces_with_bs',
 | ||
|   'crt_no_scrolling',
 | ||
|   'no_correctly_working_cr',
 | ||
|   'gnu_has_meta_key',
 | ||
|   'linefeed_is_newline',
 | ||
|   'has_hardware_tabs',
 | ||
|   'return_does_clr_eol'
 | ||
| ];
 | ||
| 
 | ||
| Tput.numbers = [
 | ||
|   'columns',
 | ||
|   'init_tabs',
 | ||
|   'lines',
 | ||
|   'lines_of_memory',
 | ||
|   'magic_cookie_glitch',
 | ||
|   'padding_baud_rate',
 | ||
|   'virtual_terminal',
 | ||
|   'width_status_line',
 | ||
|   'num_labels',
 | ||
|   'label_height',
 | ||
|   'label_width',
 | ||
|   'max_attributes',
 | ||
|   'maximum_windows',
 | ||
|   'max_colors',
 | ||
|   'max_pairs',
 | ||
|   'no_color_video',
 | ||
|   'buffer_capacity',
 | ||
|   'dot_vert_spacing',
 | ||
|   'dot_horz_spacing',
 | ||
|   'max_micro_address',
 | ||
|   'max_micro_jump',
 | ||
|   'micro_col_size',
 | ||
|   'micro_line_size',
 | ||
|   'number_of_pins',
 | ||
|   'output_res_char',
 | ||
|   'output_res_line',
 | ||
|   'output_res_horz_inch',
 | ||
|   'output_res_vert_inch',
 | ||
|   'print_rate',
 | ||
|   'wide_char_size',
 | ||
|   'buttons',
 | ||
|   'bit_image_entwining',
 | ||
|   'bit_image_type',
 | ||
| 
 | ||
|   // #ifdef __INTERNAL_CAPS_VISIBLE
 | ||
|   'magic_cookie_glitch_ul',
 | ||
|   'carriage_return_delay',
 | ||
|   'new_line_delay',
 | ||
|   'backspace_delay',
 | ||
|   'horizontal_tab_delay',
 | ||
|   'number_of_function_keys'
 | ||
| ];
 | ||
| 
 | ||
| Tput.strings = [
 | ||
|   'back_tab',
 | ||
|   'bell',
 | ||
|   'carriage_return',
 | ||
|   'change_scroll_region',
 | ||
|   'clear_all_tabs',
 | ||
|   'clear_screen',
 | ||
|   'clr_eol',
 | ||
|   'clr_eos',
 | ||
|   'column_address',
 | ||
|   'command_character',
 | ||
|   'cursor_address',
 | ||
|   'cursor_down',
 | ||
|   'cursor_home',
 | ||
|   'cursor_invisible',
 | ||
|   'cursor_left',
 | ||
|   'cursor_mem_address',
 | ||
|   'cursor_normal',
 | ||
|   'cursor_right',
 | ||
|   'cursor_to_ll',
 | ||
|   'cursor_up',
 | ||
|   'cursor_visible',
 | ||
|   'delete_character',
 | ||
|   'delete_line',
 | ||
|   'dis_status_line',
 | ||
|   'down_half_line',
 | ||
|   'enter_alt_charset_mode',
 | ||
|   'enter_blink_mode',
 | ||
|   'enter_bold_mode',
 | ||
|   'enter_ca_mode',
 | ||
|   'enter_delete_mode',
 | ||
|   'enter_dim_mode',
 | ||
|   'enter_insert_mode',
 | ||
|   'enter_secure_mode',
 | ||
|   'enter_protected_mode',
 | ||
|   'enter_reverse_mode',
 | ||
|   'enter_standout_mode',
 | ||
|   'enter_underline_mode',
 | ||
|   'erase_chars',
 | ||
|   'exit_alt_charset_mode',
 | ||
|   'exit_attribute_mode',
 | ||
|   'exit_ca_mode',
 | ||
|   'exit_delete_mode',
 | ||
|   'exit_insert_mode',
 | ||
|   'exit_standout_mode',
 | ||
|   'exit_underline_mode',
 | ||
|   'flash_screen',
 | ||
|   'form_feed',
 | ||
|   'from_status_line',
 | ||
|   'init_1string',
 | ||
|   'init_2string',
 | ||
|   'init_3string',
 | ||
|   'init_file',
 | ||
|   'insert_character',
 | ||
|   'insert_line',
 | ||
|   'insert_padding',
 | ||
|   'key_backspace',
 | ||
|   'key_catab',
 | ||
|   'key_clear',
 | ||
|   'key_ctab',
 | ||
|   'key_dc',
 | ||
|   'key_dl',
 | ||
|   'key_down',
 | ||
|   'key_eic',
 | ||
|   'key_eol',
 | ||
|   'key_eos',
 | ||
|   'key_f0',
 | ||
|   'key_f1',
 | ||
|   'key_f10',
 | ||
|   'key_f2',
 | ||
|   'key_f3',
 | ||
|   'key_f4',
 | ||
|   'key_f5',
 | ||
|   'key_f6',
 | ||
|   'key_f7',
 | ||
|   'key_f8',
 | ||
|   'key_f9',
 | ||
|   'key_home',
 | ||
|   'key_ic',
 | ||
|   'key_il',
 | ||
|   'key_left',
 | ||
|   'key_ll',
 | ||
|   'key_npage',
 | ||
|   'key_ppage',
 | ||
|   'key_right',
 | ||
|   'key_sf',
 | ||
|   'key_sr',
 | ||
|   'key_stab',
 | ||
|   'key_up',
 | ||
|   'keypad_local',
 | ||
|   'keypad_xmit',
 | ||
|   'lab_f0',
 | ||
|   'lab_f1',
 | ||
|   'lab_f10',
 | ||
|   'lab_f2',
 | ||
|   'lab_f3',
 | ||
|   'lab_f4',
 | ||
|   'lab_f5',
 | ||
|   'lab_f6',
 | ||
|   'lab_f7',
 | ||
|   'lab_f8',
 | ||
|   'lab_f9',
 | ||
|   'meta_off',
 | ||
|   'meta_on',
 | ||
|   'newline',
 | ||
|   'pad_char',
 | ||
|   'parm_dch',
 | ||
|   'parm_delete_line',
 | ||
|   'parm_down_cursor',
 | ||
|   'parm_ich',
 | ||
|   'parm_index',
 | ||
|   'parm_insert_line',
 | ||
|   'parm_left_cursor',
 | ||
|   'parm_right_cursor',
 | ||
|   'parm_rindex',
 | ||
|   'parm_up_cursor',
 | ||
|   'pkey_key',
 | ||
|   'pkey_local',
 | ||
|   'pkey_xmit',
 | ||
|   'print_screen',
 | ||
|   'prtr_off',
 | ||
|   'prtr_on',
 | ||
|   'repeat_char',
 | ||
|   'reset_1string',
 | ||
|   'reset_2string',
 | ||
|   'reset_3string',
 | ||
|   'reset_file',
 | ||
|   'restore_cursor',
 | ||
|   'row_address',
 | ||
|   'save_cursor',
 | ||
|   'scroll_forward',
 | ||
|   'scroll_reverse',
 | ||
|   'set_attributes',
 | ||
|   'set_tab',
 | ||
|   'set_window',
 | ||
|   'tab',
 | ||
|   'to_status_line',
 | ||
|   'underline_char',
 | ||
|   'up_half_line',
 | ||
|   'init_prog',
 | ||
|   'key_a1',
 | ||
|   'key_a3',
 | ||
|   'key_b2',
 | ||
|   'key_c1',
 | ||
|   'key_c3',
 | ||
|   'prtr_non',
 | ||
|   'char_padding',
 | ||
|   'acs_chars',
 | ||
|   'plab_norm',
 | ||
|   'key_btab',
 | ||
|   'enter_xon_mode',
 | ||
|   'exit_xon_mode',
 | ||
|   'enter_am_mode',
 | ||
|   'exit_am_mode',
 | ||
|   'xon_character',
 | ||
|   'xoff_character',
 | ||
|   'ena_acs',
 | ||
|   'label_on',
 | ||
|   'label_off',
 | ||
|   'key_beg',
 | ||
|   'key_cancel',
 | ||
|   'key_close',
 | ||
|   'key_command',
 | ||
|   'key_copy',
 | ||
|   'key_create',
 | ||
|   'key_end',
 | ||
|   'key_enter',
 | ||
|   'key_exit',
 | ||
|   'key_find',
 | ||
|   'key_help',
 | ||
|   'key_mark',
 | ||
|   'key_message',
 | ||
|   'key_move',
 | ||
|   'key_next',
 | ||
|   'key_open',
 | ||
|   'key_options',
 | ||
|   'key_previous',
 | ||
|   'key_print',
 | ||
|   'key_redo',
 | ||
|   'key_reference',
 | ||
|   'key_refresh',
 | ||
|   'key_replace',
 | ||
|   'key_restart',
 | ||
|   'key_resume',
 | ||
|   'key_save',
 | ||
|   'key_suspend',
 | ||
|   'key_undo',
 | ||
|   'key_sbeg',
 | ||
|   'key_scancel',
 | ||
|   'key_scommand',
 | ||
|   'key_scopy',
 | ||
|   'key_screate',
 | ||
|   'key_sdc',
 | ||
|   'key_sdl',
 | ||
|   'key_select',
 | ||
|   'key_send',
 | ||
|   'key_seol',
 | ||
|   'key_sexit',
 | ||
|   'key_sfind',
 | ||
|   'key_shelp',
 | ||
|   'key_shome',
 | ||
|   'key_sic',
 | ||
|   'key_sleft',
 | ||
|   'key_smessage',
 | ||
|   'key_smove',
 | ||
|   'key_snext',
 | ||
|   'key_soptions',
 | ||
|   'key_sprevious',
 | ||
|   'key_sprint',
 | ||
|   'key_sredo',
 | ||
|   'key_sreplace',
 | ||
|   'key_sright',
 | ||
|   'key_srsume',
 | ||
|   'key_ssave',
 | ||
|   'key_ssuspend',
 | ||
|   'key_sundo',
 | ||
|   'req_for_input',
 | ||
|   'key_f11',
 | ||
|   'key_f12',
 | ||
|   'key_f13',
 | ||
|   'key_f14',
 | ||
|   'key_f15',
 | ||
|   'key_f16',
 | ||
|   'key_f17',
 | ||
|   'key_f18',
 | ||
|   'key_f19',
 | ||
|   'key_f20',
 | ||
|   'key_f21',
 | ||
|   'key_f22',
 | ||
|   'key_f23',
 | ||
|   'key_f24',
 | ||
|   'key_f25',
 | ||
|   'key_f26',
 | ||
|   'key_f27',
 | ||
|   'key_f28',
 | ||
|   'key_f29',
 | ||
|   'key_f30',
 | ||
|   'key_f31',
 | ||
|   'key_f32',
 | ||
|   'key_f33',
 | ||
|   'key_f34',
 | ||
|   'key_f35',
 | ||
|   'key_f36',
 | ||
|   'key_f37',
 | ||
|   'key_f38',
 | ||
|   'key_f39',
 | ||
|   'key_f40',
 | ||
|   'key_f41',
 | ||
|   'key_f42',
 | ||
|   'key_f43',
 | ||
|   'key_f44',
 | ||
|   'key_f45',
 | ||
|   'key_f46',
 | ||
|   'key_f47',
 | ||
|   'key_f48',
 | ||
|   'key_f49',
 | ||
|   'key_f50',
 | ||
|   'key_f51',
 | ||
|   'key_f52',
 | ||
|   'key_f53',
 | ||
|   'key_f54',
 | ||
|   'key_f55',
 | ||
|   'key_f56',
 | ||
|   'key_f57',
 | ||
|   'key_f58',
 | ||
|   'key_f59',
 | ||
|   'key_f60',
 | ||
|   'key_f61',
 | ||
|   'key_f62',
 | ||
|   'key_f63',
 | ||
|   'clr_bol',
 | ||
|   'clear_margins',
 | ||
|   'set_left_margin',
 | ||
|   'set_right_margin',
 | ||
|   'label_format',
 | ||
|   'set_clock',
 | ||
|   'display_clock',
 | ||
|   'remove_clock',
 | ||
|   'create_window',
 | ||
|   'goto_window',
 | ||
|   'hangup',
 | ||
|   'dial_phone',
 | ||
|   'quick_dial',
 | ||
|   'tone',
 | ||
|   'pulse',
 | ||
|   'flash_hook',
 | ||
|   'fixed_pause',
 | ||
|   'wait_tone',
 | ||
|   'user0',
 | ||
|   'user1',
 | ||
|   'user2',
 | ||
|   'user3',
 | ||
|   'user4',
 | ||
|   'user5',
 | ||
|   'user6',
 | ||
|   'user7',
 | ||
|   'user8',
 | ||
|   'user9',
 | ||
|   'orig_pair',
 | ||
|   'orig_colors',
 | ||
|   'initialize_color',
 | ||
|   'initialize_pair',
 | ||
|   'set_color_pair',
 | ||
|   'set_foreground',
 | ||
|   'set_background',
 | ||
|   'change_char_pitch',
 | ||
|   'change_line_pitch',
 | ||
|   'change_res_horz',
 | ||
|   'change_res_vert',
 | ||
|   'define_char',
 | ||
|   'enter_doublewide_mode',
 | ||
|   'enter_draft_quality',
 | ||
|   'enter_italics_mode',
 | ||
|   'enter_leftward_mode',
 | ||
|   'enter_micro_mode',
 | ||
|   'enter_near_letter_quality',
 | ||
|   'enter_normal_quality',
 | ||
|   'enter_shadow_mode',
 | ||
|   'enter_subscript_mode',
 | ||
|   'enter_superscript_mode',
 | ||
|   'enter_upward_mode',
 | ||
|   'exit_doublewide_mode',
 | ||
|   'exit_italics_mode',
 | ||
|   'exit_leftward_mode',
 | ||
|   'exit_micro_mode',
 | ||
|   'exit_shadow_mode',
 | ||
|   'exit_subscript_mode',
 | ||
|   'exit_superscript_mode',
 | ||
|   'exit_upward_mode',
 | ||
|   'micro_column_address',
 | ||
|   'micro_down',
 | ||
|   'micro_left',
 | ||
|   'micro_right',
 | ||
|   'micro_row_address',
 | ||
|   'micro_up',
 | ||
|   'order_of_pins',
 | ||
|   'parm_down_micro',
 | ||
|   'parm_left_micro',
 | ||
|   'parm_right_micro',
 | ||
|   'parm_up_micro',
 | ||
|   'select_char_set',
 | ||
|   'set_bottom_margin',
 | ||
|   'set_bottom_margin_parm',
 | ||
|   'set_left_margin_parm',
 | ||
|   'set_right_margin_parm',
 | ||
|   'set_top_margin',
 | ||
|   'set_top_margin_parm',
 | ||
|   'start_bit_image',
 | ||
|   'start_char_set_def',
 | ||
|   'stop_bit_image',
 | ||
|   'stop_char_set_def',
 | ||
|   'subscript_characters',
 | ||
|   'superscript_characters',
 | ||
|   'these_cause_cr',
 | ||
|   'zero_motion',
 | ||
|   'char_set_names',
 | ||
|   'key_mouse',
 | ||
|   'mouse_info',
 | ||
|   'req_mouse_pos',
 | ||
|   'get_mouse',
 | ||
|   'set_a_foreground',
 | ||
|   'set_a_background',
 | ||
|   'pkey_plab',
 | ||
|   'device_type',
 | ||
|   'code_set_init',
 | ||
|   'set0_des_seq',
 | ||
|   'set1_des_seq',
 | ||
|   'set2_des_seq',
 | ||
|   'set3_des_seq',
 | ||
|   'set_lr_margin',
 | ||
|   'set_tb_margin',
 | ||
|   'bit_image_repeat',
 | ||
|   'bit_image_newline',
 | ||
|   'bit_image_carriage_return',
 | ||
|   'color_names',
 | ||
|   'define_bit_image_region',
 | ||
|   'end_bit_image_region',
 | ||
|   'set_color_band',
 | ||
|   'set_page_length',
 | ||
|   'display_pc_char',
 | ||
|   'enter_pc_charset_mode',
 | ||
|   'exit_pc_charset_mode',
 | ||
|   'enter_scancode_mode',
 | ||
|   'exit_scancode_mode',
 | ||
|   'pc_term_options',
 | ||
|   'scancode_escape',
 | ||
|   'alt_scancode_esc',
 | ||
|   'enter_horizontal_hl_mode',
 | ||
|   'enter_left_hl_mode',
 | ||
|   'enter_low_hl_mode',
 | ||
|   'enter_right_hl_mode',
 | ||
|   'enter_top_hl_mode',
 | ||
|   'enter_vertical_hl_mode',
 | ||
|   'set_a_attributes',
 | ||
|   'set_pglen_inch',
 | ||
| 
 | ||
|   // #ifdef __INTERNAL_CAPS_VISIBLE
 | ||
|   'termcap_init2',
 | ||
|   'termcap_reset',
 | ||
|   'linefeed_if_not_lf',
 | ||
|   'backspace_if_not_bs',
 | ||
|   'other_non_function_keys',
 | ||
|   'arrow_key_map',
 | ||
|   'acs_ulcorner',
 | ||
|   'acs_llcorner',
 | ||
|   'acs_urcorner',
 | ||
|   'acs_lrcorner',
 | ||
|   'acs_ltee',
 | ||
|   'acs_rtee',
 | ||
|   'acs_btee',
 | ||
|   'acs_ttee',
 | ||
|   'acs_hline',
 | ||
|   'acs_vline',
 | ||
|   'acs_plus',
 | ||
|   'memory_lock',
 | ||
|   'memory_unlock',
 | ||
|   'box_chars_1'
 | ||
| ];
 | ||
| 
 | ||
| // DEC Special Character and Line Drawing Set.
 | ||
| // Taken from tty.js.
 | ||
| Tput.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'  // '·'
 | ||
| };
 | ||
| 
 | ||
| // Convert ACS unicode characters to the
 | ||
| // most similar-looking ascii characters.
 | ||
| Tput.utoa = Tput.prototype.utoa = {
 | ||
|   '\u25c6': '*', // '◆'
 | ||
|   '\u2592': ' ', // '▒'
 | ||
|   // '\u0009': '\t', // '\t'
 | ||
|   // '\u000c': '\f', // '\f'
 | ||
|   // '\u000d': '\r', // '\r'
 | ||
|   // '\u000a': '\n', // '\n'
 | ||
|   '\u00b0': '*', // '°'
 | ||
|   '\u00b1': '+', // '±'
 | ||
|   '\u2424': '\n', // '\u2424' (NL)
 | ||
|   // '\u000b': '\v', // '\v'
 | ||
|   '\u2518': '+', // '┘'
 | ||
|   '\u2510': '+', // '┐'
 | ||
|   '\u250c': '+', // '┌'
 | ||
|   '\u2514': '+', // '└'
 | ||
|   '\u253c': '+', // '┼'
 | ||
|   '\u23ba': '-', // '⎺'
 | ||
|   '\u23bb': '-', // '⎻'
 | ||
|   '\u2500': '-', // '─'
 | ||
|   '\u23bc': '-', // '⎼'
 | ||
|   '\u23bd': '_', // '⎽'
 | ||
|   '\u251c': '+', // '├'
 | ||
|   '\u2524': '+', // '┤'
 | ||
|   '\u2534': '+', // '┴'
 | ||
|   '\u252c': '+', // '┬'
 | ||
|   '\u2502': '|', // '│'
 | ||
|   '\u2264': '<', // '≤'
 | ||
|   '\u2265': '>', // '≥'
 | ||
|   '\u03c0': '?', // 'π'
 | ||
|   '\u2260': '=', // '≠'
 | ||
|   '\u00a3': '?', // '£'
 | ||
|   '\u00b7': '*'  // '·'
 | ||
| };
 | ||
| 
 | ||
| /**
 | ||
|  * Expose
 | ||
|  */
 | ||
| 
 | ||
| exports = Tput;
 | ||
| exports.sprintf = sprintf;
 | ||
| exports.tryRead = tryRead;
 | ||
| 
 | ||
| module.exports = exports;
 |