printf-style string formatting Challenge

Write a function that implements C printf-style string formatting.

Rules


You must implement at least %%, %c, %s, %d and %f.
You must not use a built-in string formatting method.
You must not run external programs or connect to the Internet from your program.
It's up to you to decide how to handle invalid input, but your program must not terminate abnormally.
You should write a variadic function if possible.



  The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED",  "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119.
APL (73)

{⊃,/,⌿↑(⊂2∘↓¨Z⊂G),⊂{'c'0≡⍵,∊⊃⍺:⎕UCS⍺⋄⍕⍺}/⍵,⍪⌷∘G¨1↓1+(Z←G='%')/⍳⍴G←'%!',⍺}


Some tests:

      'a:%c b:%s c:%d'{⊃,/,⌿↑(⊂2∘↓¨Z⊂G),⊂{'c'0≡⍵,∊⊃⍺:⎕UCS⍺⋄⍕⍺}/⍵,⍪⌷∘G¨1↓1+(Z←G='%')/⍳⍴G←'%!',⍺} 65 'foo' 67
a:A b:foo c:67 

      printf←{⊃,/,⌿↑(⊂2∘↓¨Z⊂G),⊂{'c'0≡⍵,∊⊃⍺:⎕UCS⍺⋄⍕⍺}/⍵,⍪⌷∘G¨1↓1+(Z←G='%')/⍳⍴G←'%!',⍺}
      '1:%s 2:%s 3:%d 4:%c 5:%c' printf 'foo' 'bar' 100 110 'z'
1:foo 2:bar 3:100 4:n 5:z   
      'The %s brown %c%c%c jumps over the %s dog.' printf 'quick' 102 111 'x' 'lazy'
The quick brown fox jumps over the lazy dog.


Explanation:


G←'%!',⍺: prefix a dummy specifier to the string (for easier processing)
(Z←G='%')/⍳⍴G: find the indices of all the % characters in the string; also store a bitmask in Z
⌷∘G¨1↓1+: select all the characters next to the %s, and drop the dummy.
⍵,⍪: match up each specifier with its value from the right argument.
{...}/: run the following function on each pair:

'c'0≡⍵,∊⊃⍺: if the argument is a number and the specifier is c:
:⎕UCS⍺: then return the unicode value of the argument,
⋄⍕⍺: otherwise, return the string representation of the argument.

⊂: enclose
⊂2∘↓¨Z⊂G: split the string on the %s and then remove the first two characters of each substring (this is where the dummy comes in), and enclose the result of that.
↑: make a matrix out of the two enclosed arrays, matching up each substring with the value that should follow it.
,⌿: join each substring with its value.
⊃,/: then join the resulting strings.