29
29
static void write_output (
30
30
char out , char * restrict str , size_t max , size_t * written );
31
31
32
+ static void write_padding (
33
+ char * restrict str , size_t size , size_t * written , size_t len , unsigned long width , unsigned long precision , bool zero_pad , bool is_negative );
34
+
32
35
static char upcase (char c );
33
36
34
37
/* ======================================================================== *
@@ -84,7 +87,7 @@ extern unsigned long int strtoul(const char* str, char** endptr, int base);
84
87
* - c (char)
85
88
* - s (null-terminated string)
86
89
* - % (literal percent sign)
87
- * - qualifiers: l, ll, z
90
+ * - qualifiers: l, ll, z, width, (non-string) precision, left-space-pad, zero-pad
88
91
*
89
92
* Does not support:
90
93
*
@@ -96,7 +99,7 @@ extern unsigned long int strtoul(const char* str, char** endptr, int base);
96
99
* - f (decimal floating point)
97
100
* - g (the shorter of %e and %f)
98
101
* - G (the shorter of %E and %f)
99
- * - qualifiers: L, width, (non-string) precision, -, +, space-pad, zero -pad, etc
102
+ * - qualifiers: L, -, +, right -pad, etc
100
103
*
101
104
* @param str the output buffer to write to
102
105
* @param size the size of the output buffer
@@ -113,6 +116,8 @@ int vsnprintf(
113
116
int is_long = 0 ;
114
117
bool is_size_t = false;
115
118
unsigned long precision = -1 ;
119
+ unsigned long width = 0 ;
120
+ bool zero_pad = false;
116
121
117
122
while ( * fmt )
118
123
{
@@ -138,173 +143,93 @@ int vsnprintf(
138
143
is_escape = true;
139
144
break ;
140
145
case 'u' :
141
- if ( is_size_t )
142
146
{
143
- // Render %zu
144
147
char s [MAXIMUM_NUMBER_LENGTH ] = { 0 };
145
- size_t su = va_arg ( ap , size_t );
146
- utoa ( su , s , sizeof (s ), 10 );
147
- for ( const char * p = s ; * p != '\0' ; p ++ )
148
+ unsigned long long ll = 0 ;
149
+ if ( is_size_t )
148
150
{
149
- write_output ( * p , str , size , & written );
151
+ // Render %zu
152
+ ll = va_arg ( ap , size_t );
150
153
}
151
- }
152
- else if ( is_long == 2 )
153
- {
154
- // Render %lu
155
- char s [MAXIMUM_NUMBER_LENGTH ] = { 0 };
156
- unsigned long long ll = va_arg ( ap , unsigned long long );
157
- utoa ( ll , s , sizeof (s ), 10 );
158
- for ( const char * p = s ; * p != '\0' ; p ++ )
154
+ else if ( is_long == 2 )
159
155
{
160
- write_output ( * p , str , size , & written );
156
+ // Render %llu
157
+ ll = va_arg ( ap , unsigned long long );
161
158
}
162
- }
163
- else if ( is_long == 1 )
164
- {
165
- // Render %lu
166
- char s [MAXIMUM_NUMBER_LENGTH ] = { 0 };
167
- unsigned long l = va_arg ( ap , unsigned long );
168
- utoa ( l , s , sizeof (s ), 10 );
169
- for ( const char * p = s ; * p != '\0' ; p ++ )
159
+ else if ( is_long == 1 )
170
160
{
171
- write_output ( * p , str , size , & written );
161
+ // Render %lu
162
+ ll = va_arg ( ap , unsigned long );
172
163
}
173
- }
174
- else
175
- {
176
- // Render %u
177
- unsigned int i = va_arg ( ap , unsigned int );
178
- char s [MAXIMUM_NUMBER_LENGTH ] = { 0 };
179
- utoa ( i , s , sizeof (s ), 10 );
180
- for ( const char * p = s ; * p != '\0' ; p ++ )
164
+ else
181
165
{
182
- write_output ( * p , str , size , & written );
166
+ // Render %u
167
+ ll = va_arg ( ap , unsigned int );
183
168
}
184
- }
185
- break ;
186
- case 'x' :
187
- if ( is_size_t )
188
- {
189
- // Render %zu
190
- char s [MAXIMUM_NUMBER_LENGTH ] = { 0 };
191
- size_t su = va_arg ( ap , size_t );
192
- utoa ( su , s , sizeof (s ), 16 );
169
+ utoa ( ll , s , sizeof (s ), 10 );
170
+ write_padding ( str , size , & written , strlen (s ), width , precision , zero_pad , false );
193
171
for ( const char * p = s ; * p != '\0' ; p ++ )
194
172
{
195
173
write_output ( * p , str , size , & written );
196
174
}
197
175
}
198
- else if ( is_long == 2 )
176
+ break ;
177
+ case 'x' :
178
+ case 'X' :
179
+ // Render %x and %X
199
180
{
200
- // Render %llu
181
+ unsigned long long ll ;
201
182
char s [MAXIMUM_NUMBER_LENGTH ] = { 0 };
202
- unsigned long long ll = va_arg ( ap , unsigned long long );
203
- utoa ( ll , s , sizeof (s ), 16 );
204
- for ( const char * p = s ; * p != '\0' ; p ++ )
183
+ if ( is_size_t )
205
184
{
206
- write_output ( * p , str , size , & written );
185
+ ll = va_arg ( ap , size_t );
207
186
}
208
- }
209
- else if ( is_long == 1 )
210
- {
211
- // Render %lu
212
- char s [MAXIMUM_NUMBER_LENGTH ] = { 0 };
213
- unsigned long l = va_arg ( ap , unsigned long );
214
- utoa ( l , s , sizeof (s ), 16 );
215
- for ( const char * p = s ; * p != '\0' ; p ++ )
187
+ else if ( is_long == 2 )
216
188
{
217
- write_output ( * p , str , size , & written );
189
+ ll = va_arg ( ap , unsigned long long );
218
190
}
219
- }
220
- else
221
- {
222
- // Render %u
223
- unsigned int i = va_arg ( ap , unsigned int );
224
- char s [MAXIMUM_NUMBER_LENGTH ] = { 0 };
225
- utoa ( i , s , sizeof (s ), 16 );
226
- for ( const char * p = s ; * p != '\0' ; p ++ )
191
+ else if ( is_long == 1 )
227
192
{
228
- write_output ( * p , str , size , & written );
193
+ ll = va_arg ( ap , unsigned long );
229
194
}
230
- }
231
- break ;
232
- case 'X' :
233
- if ( is_size_t )
234
- {
235
- // Render %zu
236
- char s [MAXIMUM_NUMBER_LENGTH ] = { 0 };
237
- size_t su = va_arg ( ap , size_t );
238
- utoa ( su , s , sizeof (s ), 16 );
239
- for ( const char * p = s ; * p != '\0' ; p ++ )
195
+ else
240
196
{
241
- write_output ( upcase ( * p ), str , size , & written );
197
+ ll = va_arg ( ap , unsigned int );
242
198
}
243
- }
244
- else if ( is_long == 2 )
245
- {
246
- // Render %llu
247
- char s [MAXIMUM_NUMBER_LENGTH ] = { 0 };
248
- unsigned long long ll = va_arg ( ap , unsigned long long );
249
199
utoa ( ll , s , sizeof (s ), 16 );
250
- for ( const char * p = s ; * p != '\0' ; p ++ )
200
+ write_padding ( str , size , & written , strlen (s ), width , precision , zero_pad , false );
201
+ for (const char * p = s ; * p != '\0' ; p ++ )
251
202
{
252
- write_output ( upcase (* p ), str , size , & written );
253
- }
254
- }
255
- else if ( is_long == 1 )
256
- {
257
- // Render %lu
258
- char s [MAXIMUM_NUMBER_LENGTH ] = { 0 };
259
- unsigned long l = va_arg ( ap , unsigned long );
260
- utoa ( l , s , sizeof (s ), 16 );
261
- for ( const char * p = s ; * p != '\0' ; p ++ )
262
- {
263
- write_output ( upcase (* p ), str , size , & written );
264
- }
265
- }
266
- else
267
- {
268
- // Render %u
269
- unsigned int i = va_arg ( ap , unsigned int );
270
- char s [MAXIMUM_NUMBER_LENGTH ] = { 0 };
271
- utoa ( i , s , sizeof (s ), 16 );
272
- for ( const char * p = s ; * p != '\0' ; p ++ )
273
- {
274
- write_output ( upcase (* p ), str , size , & written );
203
+ char output_char = (* fmt == 'X' ) ? upcase (* p ) : * p ;
204
+ write_output ( output_char , str , size , & written );
275
205
}
276
206
}
277
207
break ;
278
208
case 'i' :
279
209
case 'd' :
280
- if ( is_long == 2 )
281
210
{
282
- // Render %ld
283
211
char s [MAXIMUM_NUMBER_LENGTH ] = { 0 };
284
- signed long long ll = va_arg ( ap , signed long long ) ;
285
- itoa ( ll , s , sizeof ( s ), 10 ) ;
286
- for ( const char * p = s ; * p != '\0' ; p ++ )
212
+ signed long long ll = 0 ;
213
+ unsigned long long ull = 0 ;
214
+ if ( is_long == 2 )
287
215
{
288
- write_output ( * p , str , size , & written );
216
+ // Render %lld
217
+ ll = va_arg ( ap , signed long long );
289
218
}
290
- }
291
- else if ( is_long == 1 )
292
- {
293
- // Render %ld
294
- char s [MAXIMUM_NUMBER_LENGTH ] = { 0 };
295
- signed long l = va_arg ( ap , signed long );
296
- itoa ( l , s , sizeof (s ), 10 );
297
- for ( const char * p = s ; * p != '\0' ; p ++ )
219
+ else if ( is_long == 1 )
298
220
{
299
- write_output ( * p , str , size , & written );
221
+ // Render %ld
222
+ ll = va_arg ( ap , signed long );
300
223
}
301
- }
302
- else
303
- {
304
- // Render %d
305
- signed int i = va_arg ( ap , signed int );
306
- char s [MAXIMUM_NUMBER_LENGTH ] = { 0 };
307
- itoa ( i , s , sizeof (s ), 10 );
224
+ else
225
+ {
226
+ // Render %d
227
+ ll = va_arg ( ap , signed int );
228
+ }
229
+ bool is_negative = ll < 0 ;
230
+ ull = is_negative ? - ll : ll ;
231
+ utoa ( ull , s , sizeof (s ), 10 );
232
+ write_padding ( str , size , & written , strlen (s ), width , precision , zero_pad , is_negative );
308
233
for ( const char * p = s ; * p != '\0' ; p ++ )
309
234
{
310
235
write_output ( * p , str , size , & written );
@@ -323,6 +248,12 @@ int vsnprintf(
323
248
{
324
249
const char * s = va_arg ( ap , const char * );
325
250
unsigned long count = precision ;
251
+
252
+ size_t len = strlen (s );
253
+ if (precision != (unsigned long )-1 && precision < len ) {
254
+ len = precision ;
255
+ }
256
+ write_padding ( str , size , & written , len , width , precision , false, false );
326
257
327
258
while (count > 0 && * s != '\0' )
328
259
{
@@ -336,7 +267,7 @@ int vsnprintf(
336
267
}
337
268
break ;
338
269
case '.' :
339
- // Render a precision specifier
270
+ // Parse a precision specifier
340
271
{
341
272
// Next up is either a number or a '*' that signifies that the number is in the arguments list
342
273
char next = * ++ fmt ;
@@ -356,6 +287,27 @@ int vsnprintf(
356
287
is_escape = true;
357
288
}
358
289
break ;
290
+ case '0' :
291
+ // Parse zero padding specifier
292
+ zero_pad = true;
293
+ fmt ++ ;
294
+ /* fall through */
295
+ case '1' :
296
+ case '2' :
297
+ case '3' :
298
+ case '4' :
299
+ case '5' :
300
+ case '6' :
301
+ case '7' :
302
+ case '8' :
303
+ case '9' :
304
+ // Parse padding specifier
305
+ width = strtoul (fmt , (char * * ) & fmt , 10 );
306
+ // Strtoul sets the fmt pointer to the char after the number,
307
+ // however the code expects the char before that.
308
+ fmt -- ;
309
+ is_escape = true;
310
+ break ;
359
311
case '%' :
360
312
write_output ( '%' , str , size , & written );
361
313
break ;
@@ -378,6 +330,9 @@ int vsnprintf(
378
330
is_escape = true;
379
331
is_long = 0 ;
380
332
is_size_t = false;
333
+ zero_pad = false;
334
+ width = 0 ;
335
+ precision = -1 ;
381
336
break ;
382
337
default :
383
338
write_output ( * fmt , str , size , & written );
@@ -451,6 +406,58 @@ static void write_output(
451
406
}
452
407
}
453
408
409
+ /**
410
+ * write_padding - Write padding to the output buffer.
411
+ *
412
+ * @param str the buffer to write to
413
+ * @param size the total size of `str`
414
+ * @param written pass in the number of characters in the buffer; is increased
415
+ * by one regardless of whether we wrote to the buffer
416
+ * @param len the length of the string to write
417
+ * @param width the total width of the padding
418
+ * @param precision the precision of the padding
419
+ * @param zero_pad whether to zero pad the string
420
+ * @param is_negative whether the number is negative
421
+ */
422
+ static void write_padding (char * restrict str , size_t size , size_t * written , size_t len , unsigned long width , unsigned long precision , bool zero_pad , bool is_negative ) {
423
+ if ( is_negative )
424
+ {
425
+ len ++ ;
426
+ }
427
+ unsigned long pad_len = width > len ? width - len : 0 ;
428
+ unsigned long zero_pad_len = 0 ;
429
+ if ( precision != 0 && precision != (unsigned long )-1 )
430
+ {
431
+ if ( is_negative )
432
+ {
433
+ zero_pad_len = precision >= len ? precision - len + 1 : 0 ;
434
+ }
435
+ else
436
+ {
437
+ zero_pad_len = precision >= len ? precision - len : 0 ;
438
+ }
439
+ }
440
+ else if ( zero_pad && precision == (unsigned long )-1 )
441
+ {
442
+ zero_pad_len = pad_len ;
443
+ }
444
+ // Apply whitespace padding if needed
445
+ pad_len = (zero_pad_len > pad_len ) ? 0 : pad_len - zero_pad_len ;
446
+ for (unsigned long i = 0 ; i < pad_len ; i ++ )
447
+ {
448
+ write_output ( ' ' , str , size , written );
449
+ }
450
+ // Apply zero padding if needed
451
+ if (is_negative )
452
+ {
453
+ write_output ( '-' , str , size , written );
454
+ }
455
+ for (unsigned long i = 0 ; i < zero_pad_len ; i ++ )
456
+ {
457
+ write_output ( '0' , str , size , written );
458
+ }
459
+ }
460
+
454
461
/**
455
462
* Converts 'a'..'z' to 'A'..'Z', leaving all other characters unchanged.
456
463
*/
0 commit comments