module.exports = function sprintf () {

const regex = /%%|%(?:(\d+)\$)?((?:[-+#0 ]|'[\s\S])*)(\d+)?(?:\.(\d*))?([\s\S])/g

const args = arguments

let i = 0

const format = args[i++]

const _pad = function (str, len, chr, leftJustify) {

if (!chr) {

chr = ' '

}

const padding = (str.length >= len) ? '' : new Array(1 + len - str.length >>> 0).join(chr)

return leftJustify ? str + padding : padding + str

}

const justify = function (value, prefix, leftJustify, minWidth, padChar) {

const diff = minWidth - value.length

if (diff > 0) {

if (!leftJustify && padChar === '0') {

value = [

value.slice(0, prefix.length),

_pad('', diff, '0', true),

value.slice(prefix.length)

].join('')

} else {

value = _pad(value, minWidth, padChar, leftJustify)

}

}

return value

}

const _formatBaseX = function (value, base, leftJustify, minWidth, precision, padChar) {

const number = value >>> 0

value = _pad(number.toString(base), precision || 0, '0', false)

return justify(value, '', leftJustify, minWidth, padChar)

}

const _formatString = function (value, leftJustify, minWidth, precision, customPadChar) {

if (precision !== null && precision !== undefined) {

value = value.slice(0, precision)

}

return justify(value, '', leftJustify, minWidth, customPadChar)

}

const doFormat = function (substring, argIndex, modifiers, minWidth, precision, specifier) {

let number, prefix, method, textTransform, value

if (substring === '%%') {

return '%'

}

let padChar = ' '

let leftJustify = false

let positiveNumberPrefix = ''

let j, l

for (j = 0, l = modifiers.length; j < l; j++) {

switch (modifiers.charAt(j)) {

case ' ':

case '0':

padChar = modifiers.charAt(j)

break

case '+':

positiveNumberPrefix = '+'

break

case '-':

leftJustify = true

break

case "'":

if (j + 1 < l) {

padChar = modifiers.charAt(j + 1)

j++

}

break

}

}

if (!minWidth) {

minWidth = 0

} else {

minWidth = +minWidth

}

if (!isFinite(minWidth)) {

throw new Error('Width must be finite')

}

if (!precision) {

precision = (specifier === 'd') ? 0 : 'fFeE'.indexOf(specifier) > -1 ? 6 : undefined

} else {

precision = +precision

}

if (argIndex && +argIndex === 0) {

throw new Error('Argument number must be greater than zero')

}

if (argIndex && +argIndex >= args.length) {

throw new Error('Too few arguments')

}

value = argIndex ? args[+argIndex] : args[i++]

switch (specifier) {

case '%':

return '%'

case 's':

return _formatString(value + '', leftJustify, minWidth, precision, padChar)

case 'c':

return _formatString(String.fromCharCode(+value), leftJustify, minWidth, precision, padChar)

case 'b':

return _formatBaseX(value, 2, leftJustify, minWidth, precision, padChar)

case 'o':

return _formatBaseX(value, 8, leftJustify, minWidth, precision, padChar)

case 'x':

return _formatBaseX(value, 16, leftJustify, minWidth, precision, padChar)

case 'X':

return _formatBaseX(value, 16, leftJustify, minWidth, precision, padChar)

.toUpperCase()

case 'u':

return _formatBaseX(value, 10, leftJustify, minWidth, precision, padChar)

case 'i':

case 'd':

number = +value || 0

number = Math.round(number - number % 1)

prefix = number < 0 ? '-' : positiveNumberPrefix

value = prefix + _pad(String(Math.abs(number)), precision, '0', false)

if (leftJustify && padChar === '0') {

padChar = ' '

}

return justify(value, prefix, leftJustify, minWidth, padChar)

case 'e':

case 'E':

case 'f':

case 'F':

case 'g':

case 'G':

number = +value

prefix = number < 0 ? '-' : positiveNumberPrefix

method = ['toExponential', 'toFixed', 'toPrecision']['efg'.indexOf(specifier.toLowerCase())]

textTransform = ['toString', 'toUpperCase']['eEfFgG'.indexOf(specifier) % 2]

value = prefix + Math.abs(number)[method](precision)

return justify(value, prefix, leftJustify, minWidth, padChar)[textTransform]()

default:

return ''

}

}

try {

return format.replace(regex, doFormat)

} catch (err) {

return false

}

}