Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
alexbevi
GitHub Repository: alexbevi/BizHawk
Path: blob/master/waterbox/libc/functions/stdio/_PDCLIB_print.c
2 views
1
/* _PDCLIB_print( const char *, struct _PDCLIB_status_t * )
2
3
This file is part of the Public Domain C Library (PDCLib).
4
Permission is granted to use, modify, and / or redistribute at will.
5
*/
6
7
#include <stdio.h>
8
#include <stdint.h>
9
#include <stdarg.h>
10
#include <string.h>
11
#include <stdlib.h>
12
#include <stddef.h>
13
#include <stdbool.h>
14
#include <limits.h>
15
16
#ifndef REGTEST
17
#include "_PDCLIB_io.h"
18
19
/* Using an integer's bits as flags for both the conversion flags and length
20
modifiers.
21
*/
22
/* FIXME: one too many flags to work on a 16-bit machine, join some (e.g. the
23
width flags) into a combined field.
24
*/
25
#define E_minus (1<<0)
26
#define E_plus (1<<1)
27
#define E_alt (1<<2)
28
#define E_space (1<<3)
29
#define E_zero (1<<4)
30
#define E_done (1<<5)
31
32
#define E_char (1<<6)
33
#define E_short (1<<7)
34
#define E_long (1<<8)
35
#define E_llong (1<<9)
36
#define E_intmax (1<<10)
37
#define E_size (1<<11)
38
#define E_ptrdiff (1<<12)
39
#define E_intptr (1<<13)
40
41
#define E_ldouble (1<<14)
42
43
#define E_lower (1<<15)
44
#define E_unsigned (1<<16)
45
46
#define E_TYPES (E_char | E_short | E_long | E_llong | E_intmax \
47
| E_size | E_ptrdiff | E_intptr)
48
49
/* returns true if callback-based output succeeded; else false */
50
static inline bool cbout(
51
struct _PDCLIB_status_t * status,
52
const void * buf,
53
size_t size )
54
{
55
size_t rv = status->write( status->ctx, buf, size );
56
status->i += rv;
57
status->current += rv;
58
return rv == size;
59
}
60
61
/* repeated output of a single character */
62
static inline bool cbrept(
63
struct _PDCLIB_status_t * status,
64
char c,
65
size_t times )
66
{
67
if ( sizeof(size_t) == 8 && CHAR_BIT == 8)
68
{
69
uint64_t spread = UINT64_C(0x0101010101010101) * c;
70
while ( times )
71
{
72
size_t n = times > 8 ? 8 : times;
73
if ( !cbout( status, &spread, n ) )
74
return false;
75
times -= n;
76
}
77
return true;
78
}
79
else if ( sizeof(size_t) == 4 && CHAR_BIT == 8)
80
{
81
uint32_t spread = UINT32_C(0x01010101) * c;
82
while ( times )
83
{
84
size_t n = times > 4 ? 4 : times;
85
if ( !cbout( status, &spread, n ) )
86
return false;
87
times -= n;
88
}
89
return true;
90
}
91
else
92
{
93
while ( times )
94
{
95
if ( !cbout( status, &c, 1) )
96
return false;
97
times--;
98
}
99
return true;
100
}
101
}
102
103
104
/* Maximum number of output characters =
105
* number of bits in (u)intmax_t / number of bits per character in smallest
106
* base. Smallest base is octal, 3 bits/char.
107
*
108
* Additionally require 2 extra characters for prefixes
109
*
110
* Returns false if an I/O error occured.
111
*/
112
static const size_t maxIntLen = sizeof(intmax_t) * CHAR_BIT / 3 + 1;
113
114
static bool int2base( uintmax_t value, struct _PDCLIB_status_t * status )
115
{
116
char sign = 0;
117
if ( ! ( status->flags & E_unsigned ) )
118
{
119
intmax_t signval = (intmax_t) value;
120
bool negative = signval < 0;
121
value = signval < 0 ? -signval : signval;
122
123
if ( negative )
124
{
125
sign = '-';
126
}
127
else if ( status->flags & E_plus )
128
{
129
sign = '+';
130
}
131
else if (status->flags & E_space )
132
{
133
sign = ' ';
134
}
135
}
136
137
// The user could theoretically ask for a silly buffer length here.
138
// Perhaps after a certain size we should malloc? Or do we refuse to protect
139
// them from their own stupidity?
140
size_t bufLen = (status->width > maxIntLen ? status->width : maxIntLen) + 2;
141
char outbuf[bufLen];
142
char * outend = outbuf + bufLen;
143
int written = 0;
144
145
// Build up our output string - backwards
146
{
147
const char * digits = (status->flags & E_lower) ?
148
_PDCLIB_digits : _PDCLIB_Xdigits;
149
uintmax_t remaining = value;
150
if(status->prec != 0 || remaining != 0) do {
151
uintmax_t digit = remaining % status->base;
152
remaining /= status->base;
153
154
outend[-++written] = digits[digit];
155
} while(remaining != 0);
156
}
157
158
// Pad field out to the precision specification
159
while( (long) written < status->prec ) outend[-++written] = '0';
160
161
// If a field width specified, and zero padding was requested, then pad to
162
// the field width
163
unsigned padding = 0;
164
if ( ( ! ( status->flags & E_minus ) ) && ( status->flags & E_zero ) )
165
{
166
while( written < (int) status->width )
167
{
168
outend[-++written] = '0';
169
padding++;
170
}
171
}
172
173
// Prefixes
174
if ( sign != 0 )
175
{
176
if ( padding == 0 ) written++;
177
outend[-written] = sign;
178
}
179
else if ( status->flags & E_alt )
180
{
181
switch ( status->base )
182
{
183
case 8:
184
if ( outend[-written] != '0' ) outend[-++written] = '0';
185
break;
186
case 16:
187
// No prefix if zero
188
if ( value == 0 ) break;
189
190
written += padding < 2 ? 2 - padding : 0;
191
outend[-written ] = '0';
192
outend[-written + 1] = (status->flags & E_lower) ? 'x' : 'X';
193
break;
194
default:
195
break;
196
}
197
}
198
199
// Space padding to field width
200
if ( ! ( status->flags & ( E_minus | E_zero ) ) )
201
{
202
while( written < (int) status->width ) outend[-++written] = ' ';
203
}
204
205
// Write output
206
return cbout( status, outend - written, written );
207
}
208
209
/* print a string. returns false if an I/O error occured */
210
static bool printstr( const char * str, struct _PDCLIB_status_t * status )
211
{
212
size_t len = status->prec >= 0 ? strnlen( str, status-> prec)
213
: strlen(str);
214
215
if ( status->width == 0 || status->flags & E_minus )
216
{
217
// Simple case or left justification
218
if ( status->prec > 0 )
219
{
220
len = (unsigned) status->prec < len ? (unsigned) status->prec : len;
221
}
222
223
if ( !cbout( status, str, len ) )
224
return false;
225
226
/* right padding */
227
if ( status->width > status->current ) {
228
len = status->width - status->current;
229
230
if ( !cbrept( status, ' ', len ) )
231
return false;
232
}
233
} else {
234
// Right justification
235
236
if ( status->width > len ) {
237
size_t padding = status->width - len;
238
239
if ( !cbrept( status, ' ', padding ))
240
return false;
241
}
242
243
if ( !cbout( status, str, len ) )
244
return false;
245
}
246
247
return true;
248
}
249
250
static bool printchar( char chr, struct _PDCLIB_status_t * status )
251
{
252
if( ! ( status->flags & E_minus ) )
253
{
254
// Right justification
255
if ( status-> width ) {
256
size_t justification = status->width - status->current - 1;
257
if ( !cbrept( status, ' ', justification ))
258
return false;
259
}
260
261
if ( !cbout( status, &chr, 1 ))
262
return false;
263
} else {
264
// Left justification
265
266
if ( !cbout( status, &chr, 1 ))
267
return false;
268
269
if ( status->width > status->current ) {
270
if ( !cbrept( status, ' ', status->width - status->current ) )
271
return false;
272
}
273
}
274
275
return true;
276
}
277
278
int _PDCLIB_print( const char * spec, struct _PDCLIB_status_t * status )
279
{
280
const char * orig_spec = spec;
281
if ( *(++spec) == '%' )
282
{
283
/* %% -> print single '%' */
284
if ( !cbout(status, spec, 1) )
285
return -1;
286
++spec;
287
return (spec - orig_spec);
288
}
289
/* Initializing status structure */
290
status->flags = 0;
291
status->base = 0;
292
status->current = 0;
293
status->width = 0;
294
status->prec = EOF;
295
296
/* First come 0..n flags */
297
do
298
{
299
switch ( *spec )
300
{
301
case '-':
302
/* left-aligned output */
303
status->flags |= E_minus;
304
++spec;
305
break;
306
case '+':
307
/* positive numbers prefixed with '+' */
308
status->flags |= E_plus;
309
++spec;
310
break;
311
case '#':
312
/* alternative format (leading 0x for hex, 0 for octal) */
313
status->flags |= E_alt;
314
++spec;
315
break;
316
case ' ':
317
/* positive numbers prefixed with ' ' */
318
status->flags |= E_space;
319
++spec;
320
break;
321
case '0':
322
/* right-aligned padding done with '0' instead of ' ' */
323
status->flags |= E_zero;
324
++spec;
325
break;
326
default:
327
/* not a flag, exit flag parsing */
328
status->flags |= E_done;
329
break;
330
}
331
} while ( ! ( status->flags & E_done ) );
332
333
/* Optional field width */
334
if ( *spec == '*' )
335
{
336
/* Retrieve width value from argument stack */
337
int width = va_arg( status->arg, int );
338
if ( width < 0 )
339
{
340
status->flags |= E_minus;
341
status->width = abs( width );
342
}
343
else
344
{
345
status->width = width;
346
}
347
++spec;
348
}
349
else
350
{
351
/* If a width is given, strtol() will return its value. If not given,
352
strtol() will return zero. In both cases, endptr will point to the
353
rest of the conversion specifier - just what we need.
354
*/
355
status->width = (int)strtol( spec, (char**)&spec, 10 );
356
}
357
358
/* Optional precision */
359
if ( *spec == '.' )
360
{
361
++spec;
362
if ( *spec == '*' )
363
{
364
/* Retrieve precision value from argument stack. A negative value
365
is as if no precision is given - as precision is initalized to
366
EOF (negative), there is no need for testing for negative here.
367
*/
368
status->prec = va_arg( status->arg, int );
369
++spec;
370
}
371
else
372
{
373
status->prec = (int)strtol( spec, (char**) &spec, 10 );
374
}
375
/* Having a precision cancels out any zero flag. */
376
status->flags &= ~E_zero;
377
}
378
379
/* Optional length modifier
380
We step one character ahead in any case, and step back only if we find
381
there has been no length modifier (or step ahead another character if it
382
has been "hh" or "ll").
383
*/
384
switch ( *(spec++) )
385
{
386
case 'h':
387
if ( *spec == 'h' )
388
{
389
/* hh -> char */
390
status->flags |= E_char;
391
++spec;
392
}
393
else
394
{
395
/* h -> short */
396
status->flags |= E_short;
397
}
398
break;
399
case 'l':
400
if ( *spec == 'l' )
401
{
402
/* ll -> long long */
403
status->flags |= E_llong;
404
++spec;
405
}
406
else
407
{
408
/* k -> long */
409
status->flags |= E_long;
410
}
411
break;
412
case 'j':
413
/* j -> intmax_t, which might or might not be long long */
414
status->flags |= E_intmax;
415
break;
416
case 'z':
417
/* z -> size_t, which might or might not be unsigned int */
418
status->flags |= E_size;
419
break;
420
case 't':
421
/* t -> ptrdiff_t, which might or might not be long */
422
status->flags |= E_ptrdiff;
423
break;
424
case 'L':
425
/* L -> long double */
426
status->flags |= E_ldouble;
427
break;
428
default:
429
--spec;
430
break;
431
}
432
433
/* Conversion specifier */
434
switch ( *spec )
435
{
436
case 'd':
437
/* FALLTHROUGH */
438
case 'i':
439
status->base = 10;
440
break;
441
case 'o':
442
status->base = 8;
443
status->flags |= E_unsigned;
444
break;
445
case 'u':
446
status->base = 10;
447
status->flags |= E_unsigned;
448
break;
449
case 'x':
450
status->base = 16;
451
status->flags |= ( E_lower | E_unsigned );
452
break;
453
case 'X':
454
status->base = 16;
455
status->flags |= E_unsigned;
456
break;
457
case 'f':
458
case 'F':
459
case 'e':
460
case 'E':
461
case 'g':
462
case 'G':
463
break;
464
case 'a':
465
case 'A':
466
break;
467
case 'c':
468
/* TODO: wide chars. */
469
if ( !printchar( va_arg( status->arg, int ), status ) )
470
return -1;
471
++spec;
472
return (spec - orig_spec);
473
case 's':
474
/* TODO: wide chars. */
475
{
476
char * s = va_arg( status->arg, char * );
477
if ( !printstr( s, status ) )
478
return -1;
479
++spec;
480
return (spec - orig_spec);
481
}
482
case 'p':
483
status->base = 16;
484
status->flags |= ( E_lower | E_unsigned | E_alt | E_intptr );
485
break;
486
case 'n':
487
{
488
int * val = va_arg( status->arg, int * );
489
*val = status->i;
490
++spec;
491
return (spec - orig_spec);
492
}
493
default:
494
/* No conversion specifier. Bad conversion. */
495
return 0;
496
}
497
/* Do the actual output based on our findings */
498
if ( status->base != 0 )
499
{
500
/* Integer conversions */
501
/* TODO: Check for invalid flag combinations. */
502
if ( status->flags & E_unsigned )
503
{
504
/* TODO: Marking the default case _PDCLIB_UNREACHABLE breaks %ju test driver? */
505
uintmax_t value = 0;
506
switch ( status->flags & E_TYPES )
507
{
508
case E_char:
509
value = (uintmax_t)(unsigned char)va_arg( status->arg, int );
510
break;
511
case E_short:
512
value = (uintmax_t)(unsigned short)va_arg( status->arg, int );
513
break;
514
case 0:
515
value = (uintmax_t)va_arg( status->arg, unsigned int );
516
break;
517
case E_long:
518
value = (uintmax_t)va_arg( status->arg, unsigned long );
519
break;
520
case E_llong:
521
value = (uintmax_t)va_arg( status->arg, unsigned long long );
522
break;
523
case E_size:
524
value = (uintmax_t)va_arg( status->arg, size_t );
525
break;
526
case E_intptr:
527
value = (uintmax_t)va_arg( status->arg, uintptr_t );
528
break;
529
case E_ptrdiff:
530
value = (uintmax_t)va_arg( status->arg, ptrdiff_t );
531
break;
532
case E_intmax:
533
value = va_arg( status->arg, uintmax_t );
534
}
535
if ( !int2base( value, status ) )
536
return -1;
537
}
538
else
539
{
540
intmax_t value = 0;
541
switch ( status->flags & E_TYPES )
542
{
543
case E_char:
544
value = (intmax_t)(char)va_arg( status->arg, int );
545
break;
546
case E_short:
547
value = (intmax_t)(short)va_arg( status->arg, int );
548
break;
549
case 0:
550
value = (intmax_t)va_arg( status->arg, int );
551
break;
552
case E_long:
553
value = (intmax_t)va_arg( status->arg, long );
554
break;
555
case E_llong:
556
value = (intmax_t)va_arg( status->arg, long long );
557
break;
558
case E_size:
559
value = (intmax_t)va_arg( status->arg, size_t );
560
break;
561
case E_intptr:
562
value = (intmax_t)va_arg( status->arg, intptr_t );
563
break;
564
case E_ptrdiff:
565
value = (intmax_t)va_arg( status->arg, ptrdiff_t );
566
break;
567
case E_intmax:
568
value = va_arg( status->arg, intmax_t );
569
break;
570
default:
571
_PDCLIB_UNREACHABLE;
572
}
573
574
if (!int2base( value, status ) )
575
return -1;
576
}
577
578
if ( status->flags & E_minus && status->current < status->width )
579
{
580
if (!cbrept( status, ' ', status->width - status->current ))
581
return -1;
582
}
583
}
584
++spec;
585
return spec - orig_spec;
586
}
587
588
#endif
589
590
#ifdef TEST
591
#define _PDCLIB_FILEID "_PDCLIB/print.c"
592
#define _PDCLIB_STRINGIO
593
594
#include "_PDCLIB_test.h"
595
596
#ifndef REGTEST
597
static size_t testcb( void *p, const char *buf, size_t size )
598
{
599
char **destbuf = p;
600
memcpy(*destbuf, buf, size);
601
*destbuf += size;
602
return size;
603
}
604
605
static int testprintf( char * buffer, const char * format, ... )
606
{
607
/* Members: base, flags, n, i, current, width, prec, ctx, cb, arg */
608
struct _PDCLIB_status_t status;
609
status.base = 0;
610
status.flags = 0;
611
status.n = 100;
612
status.i = 0;
613
status.current = 0;
614
status.width = 0;
615
status.prec = 0;
616
status.ctx = &buffer;
617
status.write = testcb;
618
va_start( status.arg, format );
619
memset( buffer, '\0', 100 );
620
if ( _PDCLIB_print( format, &status ) != (int)strlen( format ) )
621
{
622
printf( "_PDCLIB_print() did not return end-of-specifier on '%s'.\n", format );
623
++TEST_RESULTS;
624
}
625
va_end( status.arg );
626
return status.i;
627
}
628
#endif
629
630
#define TEST_CONVERSION_ONLY
631
632
int main( void )
633
{
634
#ifndef REGTEST
635
char target[100];
636
#include "printf_testcases.h"
637
#endif
638
return TEST_RESULTS;
639
}
640
641
#endif
642
643