Initial commit
[yaffs-website] / node_modules / extsprintf / lib / extsprintf.js
1 /*
2  * extsprintf.js: extended POSIX-style sprintf
3  */
4
5 var mod_assert = require('assert');
6 var mod_util = require('util');
7
8 /*
9  * Public interface
10  */
11 exports.sprintf = jsSprintf;
12
13 /*
14  * Stripped down version of s[n]printf(3c).  We make a best effort to throw an
15  * exception when given a format string we don't understand, rather than
16  * ignoring it, so that we won't break existing programs if/when we go implement
17  * the rest of this.
18  *
19  * This implementation currently supports specifying
20  *      - field alignment ('-' flag),
21  *      - zero-pad ('0' flag)
22  *      - always show numeric sign ('+' flag),
23  *      - field width
24  *      - conversions for strings, decimal integers, and floats (numbers).
25  *      - argument size specifiers.  These are all accepted but ignored, since
26  *        Javascript has no notion of the physical size of an argument.
27  *
28  * Everything else is currently unsupported, most notably precision, unsigned
29  * numbers, non-decimal numbers, and characters.
30  */
31 function jsSprintf(fmt)
32 {
33         var regex = [
34             '([^%]*)',                          /* normal text */
35             '%',                                /* start of format */
36             '([\'\\-+ #0]*?)',                  /* flags (optional) */
37             '([1-9]\\d*)?',                     /* width (optional) */
38             '(\\.([1-9]\\d*))?',                /* precision (optional) */
39             '[lhjztL]*?',                       /* length mods (ignored) */
40             '([diouxXfFeEgGaAcCsSp%jr])'        /* conversion */
41         ].join('');
42
43         var re = new RegExp(regex);
44         var args = Array.prototype.slice.call(arguments, 1);
45         var flags, width, precision, conversion;
46         var left, pad, sign, arg, match;
47         var ret = '';
48         var argn = 1;
49
50         mod_assert.equal('string', typeof (fmt));
51
52         while ((match = re.exec(fmt)) !== null) {
53                 ret += match[1];
54                 fmt = fmt.substring(match[0].length);
55
56                 flags = match[2] || '';
57                 width = match[3] || 0;
58                 precision = match[4] || '';
59                 conversion = match[6];
60                 left = false;
61                 sign = false;
62                 pad = ' ';
63
64                 if (conversion == '%') {
65                         ret += '%';
66                         continue;
67                 }
68
69                 if (args.length === 0)
70                         throw (new Error('too few args to sprintf'));
71
72                 arg = args.shift();
73                 argn++;
74
75                 if (flags.match(/[\' #]/))
76                         throw (new Error(
77                             'unsupported flags: ' + flags));
78
79                 if (precision.length > 0)
80                         throw (new Error(
81                             'non-zero precision not supported'));
82
83                 if (flags.match(/-/))
84                         left = true;
85
86                 if (flags.match(/0/))
87                         pad = '0';
88
89                 if (flags.match(/\+/))
90                         sign = true;
91
92                 switch (conversion) {
93                 case 's':
94                         if (arg === undefined || arg === null)
95                                 throw (new Error('argument ' + argn +
96                                     ': attempted to print undefined or null ' +
97                                     'as a string'));
98                         ret += doPad(pad, width, left, arg.toString());
99                         break;
100
101                 case 'd':
102                         arg = Math.floor(arg);
103                         /*jsl:fallthru*/
104                 case 'f':
105                         sign = sign && arg > 0 ? '+' : '';
106                         ret += sign + doPad(pad, width, left,
107                             arg.toString());
108                         break;
109
110                 case 'j': /* non-standard */
111                         if (width === 0)
112                                 width = 10;
113                         ret += mod_util.inspect(arg, false, width);
114                         break;
115
116                 case 'r': /* non-standard */
117                         ret += dumpException(arg);
118                         break;
119
120                 default:
121                         throw (new Error('unsupported conversion: ' +
122                             conversion));
123                 }
124         }
125
126         ret += fmt;
127         return (ret);
128 }
129
130 function doPad(chr, width, left, str)
131 {
132         var ret = str;
133
134         while (ret.length < width) {
135                 if (left)
136                         ret += chr;
137                 else
138                         ret = chr + ret;
139         }
140
141         return (ret);
142 }
143
144 /*
145  * This function dumps long stack traces for exceptions having a cause() method.
146  * See node-verror for an example.
147  */
148 function dumpException(ex)
149 {
150         var ret;
151
152         if (!(ex instanceof Error))
153                 throw (new Error(jsSprintf('invalid type for %%r: %j', ex)));
154
155         /* Note that V8 prepends "ex.stack" with ex.toString(). */
156         ret = 'EXCEPTION: ' + ex.constructor.name + ': ' + ex.stack;
157
158         if (ex.cause && typeof (ex.cause) === 'function') {
159                 var cex = ex.cause();
160                 if (cex) {
161                         ret += '\nCaused by: ' + dumpException(cex);
162                 }
163         }
164
165         return (ret);
166 }