|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175 |
- /*
- * extsprintf.js: extended POSIX-style sprintf
- */
-
- var mod_assert = require('assert');
- var mod_util = require('util');
-
- /*
- * Public interface
- */
- exports.sprintf = jsSprintf;
- exports.printf = jsPrintf;
-
- /*
- * 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() {
- process.stdout.write(jsSprintf.apply(this, arguments));
- }
-
- 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);
- }
|