Spaces:
Configuration error
Configuration error
/* | |
* 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); | |
} | |