You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					184 lines
				
				4.1 KiB
			
		
		
			
		
	
	
					184 lines
				
				4.1 KiB
			| 
								 
											3 years ago
										 
									 | 
							
								/*
							 | 
						||
| 
								 | 
							
								 * extsprintf.js: extended POSIX-style sprintf
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								var mod_assert = require('assert');
							 | 
						||
| 
								 | 
							
								var mod_util = require('util');
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/*
							 | 
						||
| 
								 | 
							
								 * Public interface
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								exports.sprintf = jsSprintf;
							 | 
						||
| 
								 | 
							
								exports.printf = jsPrintf;
							 | 
						||
| 
								 | 
							
								exports.fprintf = jsFprintf;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/*
							 | 
						||
| 
								 | 
							
								 * Stripped down version of s[n]printf(3c).  We make a best effort to throw an
							 | 
						||
| 
								 | 
							
								 * exception when given a format string we don't understand, rather than
							 | 
						||
| 
								 | 
							
								 * ignoring it, so that we won't break existing programs if/when we go implement
							 | 
						||
| 
								 | 
							
								 * the rest of this.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * This implementation currently supports specifying
							 | 
						||
| 
								 | 
							
								 *	- field alignment ('-' flag),
							 | 
						||
| 
								 | 
							
								 * 	- zero-pad ('0' flag)
							 | 
						||
| 
								 | 
							
								 *	- always show numeric sign ('+' flag),
							 | 
						||
| 
								 | 
							
								 *	- field width
							 | 
						||
| 
								 | 
							
								 *	- conversions for strings, decimal integers, and floats (numbers).
							 | 
						||
| 
								 | 
							
								 *	- argument size specifiers.  These are all accepted but ignored, since
							 | 
						||
| 
								 | 
							
								 *	  Javascript has no notion of the physical size of an argument.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * Everything else is currently unsupported, most notably precision, unsigned
							 | 
						||
| 
								 | 
							
								 * numbers, non-decimal numbers, and characters.
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								function jsSprintf(fmt)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
									var regex = [
							 | 
						||
| 
								 | 
							
									    '([^%]*)',				/* normal text */
							 | 
						||
| 
								 | 
							
									    '%',				/* start of format */
							 | 
						||
| 
								 | 
							
									    '([\'\\-+ #0]*?)',			/* flags (optional) */
							 | 
						||
| 
								 | 
							
									    '([1-9]\\d*)?',			/* width (optional) */
							 | 
						||
| 
								 | 
							
									    '(\\.([1-9]\\d*))?',		/* precision (optional) */
							 | 
						||
| 
								 | 
							
									    '[lhjztL]*?',			/* length mods (ignored) */
							 | 
						||
| 
								 | 
							
									    '([diouxXfFeEgGaAcCsSp%jr])'	/* conversion */
							 | 
						||
| 
								 | 
							
									].join('');
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									var re = new RegExp(regex);
							 | 
						||
| 
								 | 
							
									var args = Array.prototype.slice.call(arguments, 1);
							 | 
						||
| 
								 | 
							
									var flags, width, precision, conversion;
							 | 
						||
| 
								 | 
							
									var left, pad, sign, arg, match;
							 | 
						||
| 
								 | 
							
									var ret = '';
							 | 
						||
| 
								 | 
							
									var argn = 1;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									mod_assert.equal('string', typeof (fmt));
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									while ((match = re.exec(fmt)) !== null) {
							 | 
						||
| 
								 | 
							
										ret += match[1];
							 | 
						||
| 
								 | 
							
										fmt = fmt.substring(match[0].length);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										flags = match[2] || '';
							 | 
						||
| 
								 | 
							
										width = match[3] || 0;
							 | 
						||
| 
								 | 
							
										precision = match[4] || '';
							 | 
						||
| 
								 | 
							
										conversion = match[6];
							 | 
						||
| 
								 | 
							
										left = false;
							 | 
						||
| 
								 | 
							
										sign = false;
							 | 
						||
| 
								 | 
							
										pad = ' ';
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										if (conversion == '%') {
							 | 
						||
| 
								 | 
							
											ret += '%';
							 | 
						||
| 
								 | 
							
											continue;
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										if (args.length === 0)
							 | 
						||
| 
								 | 
							
											throw (new Error('too few args to sprintf'));
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										arg = args.shift();
							 | 
						||
| 
								 | 
							
										argn++;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										if (flags.match(/[\' #]/))
							 | 
						||
| 
								 | 
							
											throw (new Error(
							 | 
						||
| 
								 | 
							
											    'unsupported flags: ' + flags));
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										if (precision.length > 0)
							 | 
						||
| 
								 | 
							
											throw (new Error(
							 | 
						||
| 
								 | 
							
											    'non-zero precision not supported'));
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										if (flags.match(/-/))
							 | 
						||
| 
								 | 
							
											left = true;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										if (flags.match(/0/))
							 | 
						||
| 
								 | 
							
											pad = '0';
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										if (flags.match(/\+/))
							 | 
						||
| 
								 | 
							
											sign = true;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										switch (conversion) {
							 | 
						||
| 
								 | 
							
										case 's':
							 | 
						||
| 
								 | 
							
											if (arg === undefined || arg === null)
							 | 
						||
| 
								 | 
							
												throw (new Error('argument ' + argn +
							 | 
						||
| 
								 | 
							
												    ': attempted to print undefined or null ' +
							 | 
						||
| 
								 | 
							
												    'as a string'));
							 | 
						||
| 
								 | 
							
											ret += doPad(pad, width, left, arg.toString());
							 | 
						||
| 
								 | 
							
											break;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										case 'd':
							 | 
						||
| 
								 | 
							
											arg = Math.floor(arg);
							 | 
						||
| 
								 | 
							
											/*jsl:fallthru*/
							 | 
						||
| 
								 | 
							
										case 'f':
							 | 
						||
| 
								 | 
							
											sign = sign && arg > 0 ? '+' : '';
							 | 
						||
| 
								 | 
							
											ret += sign + doPad(pad, width, left,
							 | 
						||
| 
								 | 
							
											    arg.toString());
							 | 
						||
| 
								 | 
							
											break;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										case 'x':
							 | 
						||
| 
								 | 
							
											ret += doPad(pad, width, left, arg.toString(16));
							 | 
						||
| 
								 | 
							
											break;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										case 'j': /* non-standard */
							 | 
						||
| 
								 | 
							
											if (width === 0)
							 | 
						||
| 
								 | 
							
												width = 10;
							 | 
						||
| 
								 | 
							
											ret += mod_util.inspect(arg, false, width);
							 | 
						||
| 
								 | 
							
											break;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										case 'r': /* non-standard */
							 | 
						||
| 
								 | 
							
											ret += dumpException(arg);
							 | 
						||
| 
								 | 
							
											break;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										default:
							 | 
						||
| 
								 | 
							
											throw (new Error('unsupported conversion: ' +
							 | 
						||
| 
								 | 
							
											    conversion));
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									ret += fmt;
							 | 
						||
| 
								 | 
							
									return (ret);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function jsPrintf() {
							 | 
						||
| 
								 | 
							
									var args = Array.prototype.slice.call(arguments);
							 | 
						||
| 
								 | 
							
									args.unshift(process.stdout);
							 | 
						||
| 
								 | 
							
									jsFprintf.apply(null, args);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function jsFprintf(stream) {
							 | 
						||
| 
								 | 
							
									var args = Array.prototype.slice.call(arguments, 1);
							 | 
						||
| 
								 | 
							
									return (stream.write(jsSprintf.apply(this, args)));
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function doPad(chr, width, left, str)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
									var ret = str;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									while (ret.length < width) {
							 | 
						||
| 
								 | 
							
										if (left)
							 | 
						||
| 
								 | 
							
											ret += chr;
							 | 
						||
| 
								 | 
							
										else
							 | 
						||
| 
								 | 
							
											ret = chr + ret;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									return (ret);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/*
							 | 
						||
| 
								 | 
							
								 * This function dumps long stack traces for exceptions having a cause() method.
							 | 
						||
| 
								 | 
							
								 * See node-verror for an example.
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								function dumpException(ex)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
									var ret;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									if (!(ex instanceof Error))
							 | 
						||
| 
								 | 
							
										throw (new Error(jsSprintf('invalid type for %%r: %j', ex)));
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									/* Note that V8 prepends "ex.stack" with ex.toString(). */
							 | 
						||
| 
								 | 
							
									ret = 'EXCEPTION: ' + ex.constructor.name + ': ' + ex.stack;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									if (ex.cause && typeof (ex.cause) === 'function') {
							 | 
						||
| 
								 | 
							
										var cex = ex.cause();
							 | 
						||
| 
								 | 
							
										if (cex) {
							 | 
						||
| 
								 | 
							
											ret += '\nCaused by: ' + dumpException(cex);
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									return (ret);
							 | 
						||
| 
								 | 
							
								}
							 |