PatchworkOS  19e446b
A non-POSIX operating system.
Loading...
Searching...
No Matches
print.h
Go to the documentation of this file.
1#pragma once
2
3#include <assert.h>
4#include <ctype.h>
5#include <stdarg.h>
6#include <stddef.h>
7#include <stdint.h>
8#include <stdio.h>
9#include <stdlib.h>
10#include <string.h>
11#include <sys/defs.h>
12
13#include "digits.h"
14
15#ifndef _PRINT_WRITE
16#error "_PRINT_WRITE not defined"
17#endif
18
19#ifndef _PRINT_FILL
20#error "_PRINT_FILL not defined"
21#endif
22
23/**
24 * @brief Internal Print Implementation.
25 * @defgroup libstd_common_print Print
26 * @ingroup libstd_common
27 *
28 * Provides a common implementation for printing formatted output, any function that needs to print formatted output
29 * should define the `_PRINT_WRITE()` and `_PRINT_FILL()` macros before including this file.
30 *
31 * The `_PRINT_WRITE(ctx, buffer, count)` macro should evaluate to an expression that writes `count` bytes from the
32 * `buffer` to the output source and takes a pointer to the current `print_ctx_t`, a pointer to the buffer and the
33 * number of bytes to write as arguments.
34 *
35 * The `_PRINT_FILL(ctx, c, count)` macro should evaluate to an expression that writes `count` bytes of character `c` to
36 * the output source and takes a pointer to the current `print_ctx_t`, the character to write and the number of times to
37 * write it as arguments.
38 *
39 * The macros should return the number of bytes written, or `EOF` on error.
40 *
41 * @todo Implement floating point printing.
42 *
43 * @see https://cplusplus.com/reference/cstdio/printf/ for details on the format specifiers.
44 *
45 * @{
46 */
47
48typedef struct
49{
50 size_t written;
51 size_t n;
52 const char* p;
54 void* data;
65
77
85
87{
88 int padding = 0;
89 if (format->width > len)
90 {
91 padding = format->width - len;
92 }
93
94 if (!(format->flags & _PRINT_LEFT_ALIGNED))
95 {
96 char paddingChar = ' ';
97 if (format->flags & _PRINT_PAD_ZERO && format->precision == EOF)
98 {
99 paddingChar = '0';
100 }
101
102 if (_PRINT_FILL(ctx, paddingChar, padding) == EOF)
103 {
104 return EOF;
105 }
106 ctx->written += padding;
107 }
108
109 return padding;
110}
111
112static inline int _print_padding_right(_print_ctx_t* ctx, _print_format_ctx_t* format, int padding)
113{
114 if (format->flags & _PRINT_LEFT_ALIGNED)
115 {
116 if (_PRINT_FILL(ctx, ' ', padding) == EOF)
117 {
118 return EOF;
119 }
120 ctx->written += padding;
121 }
122
123 return 0;
124}
125
126typedef struct
127{
128 char prefix[20];
130 char data[32];
131 char* dataPtr;
135
136#define _PRINT_INTEGER_PUSH(integer, c) (*--(integer)->dataPtr = (c))
137
138#define _PRINT_INTEGER_PREFIX_PUSH(integer, c) (*--(integer)->prefixPtr = (c))
139
140#define _PRINT_INTEGER_LEN(integer) ((int)((sizeof((integer)->data)) - ((integer)->dataPtr - (integer)->data)))
141
142#define _PRINT_INTEGER_PREFIX_LEN(integer) \
143 ((int)((sizeof((integer)->prefix)) - ((integer)->prefixPtr - (integer)->prefix)))
144
146{
147 int integerLen = _PRINT_INTEGER_LEN(integer);
148
149 if (format->flags & _PRINT_ALTERNATE_FORM && integerLen > 0)
150 {
151 if (integer->base == 8)
152 {
153 _PRINT_INTEGER_PREFIX_PUSH(integer, '0');
154 }
155 else if (integer->base == 16)
156 {
157 _PRINT_INTEGER_PREFIX_PUSH(integer, format->flags & _PRINT_UPPER_CASE ? 'X' : 'x');
158 _PRINT_INTEGER_PREFIX_PUSH(integer, '0');
159 }
160 }
161
162 int precision = 0;
163 if (format->precision == EOF)
164 {
165 if (integerLen == 0)
166 {
167 _PRINT_INTEGER_PUSH(integer, '0');
168 integerLen++;
169 }
170 }
171 else if (integerLen < format->precision)
172 {
173 precision = format->precision - integerLen;
174 }
175
176 if (integer->sign < 0)
177 {
178 _PRINT_INTEGER_PREFIX_PUSH(integer, '-');
179 }
180 else if (integer->sign > 0)
181 {
182 if (format->flags & _PRINT_FORCE_SIGN)
183 {
184 _PRINT_INTEGER_PREFIX_PUSH(integer, '+');
185 }
186 else if (format->flags & _PRINT_SPACE_SIGN)
187 {
188 _PRINT_INTEGER_PREFIX_PUSH(integer, ' ');
189 }
190 }
191
192 int prefixLen = _PRINT_INTEGER_PREFIX_LEN(integer);
193
194 bool padZeroes = (format->flags & _PRINT_PAD_ZERO) && (format->precision == EOF);
195 if (prefixLen > 0 && padZeroes)
196 {
197 if (_PRINT_WRITE(ctx, integer->prefixPtr, prefixLen) == EOF)
198 {
199 return EOF;
200 }
201 ctx->written += prefixLen;
202 }
203
204 int padding = _print_padding_left(ctx, format, integerLen + prefixLen + precision);
205 if (padding < 0)
206 {
207 return EOF;
208 }
209
210 if (prefixLen > 0 && !padZeroes)
211 {
212 if (_PRINT_WRITE(ctx, integer->prefixPtr, prefixLen) == EOF)
213 {
214 return EOF;
215 }
216 ctx->written += prefixLen;
217 }
218
219 if (_PRINT_FILL(ctx, '0', precision) == EOF)
220 {
221 return EOF;
222 }
223 ctx->written += precision;
224
225 if (_PRINT_WRITE(ctx, integer->dataPtr, integerLen) == EOF)
226 {
227 return EOF;
228 }
229 ctx->written += integerLen;
230
231 return _print_padding_right(ctx, format, padding);
232}
233
235{
236 intmax_t value = 0;
237 switch (format->length)
238 {
239 case _PRINT_DEFAULT:
240 value = va_arg(ctx->arg, int);
241 break;
242 case _PRINT_HH:
243 value = (signed char)va_arg(ctx->arg, int);
244 break;
245 case _PRINT_H:
246 value = (short int)va_arg(ctx->arg, int);
247 break;
248 case _PRINT_L:
249 value = va_arg(ctx->arg, long int);
250 break;
251 case _PRINT_LL:
252 value = va_arg(ctx->arg, long long int);
253 break;
254 case _PRINT_J:
255 value = va_arg(ctx->arg, intmax_t);
256 break;
257 case _PRINT_Z:
258 value = va_arg(ctx->arg, size_t);
259 break;
260 case _PRINT_T:
261 value = va_arg(ctx->arg, ptrdiff_t);
262 break;
263 default:
264 return EOF;
265 }
266
267 _print_integer_t integer = {.prefixPtr = integer.prefix + sizeof(integer.prefix),
268 .dataPtr = integer.data + sizeof(integer.data),
269 .base = 10,
270 .sign = 1};
271
272 uintmax_t uvalue = (uintmax_t)value;
273 if (value < 0)
274 {
275 integer.sign = -1;
276 uvalue = (uintmax_t)(-value);
277 }
278
279 while (uvalue >= 100)
280 {
281 unsigned index = (uvalue % 100) * 2;
282 uvalue /= 100;
283 _PRINT_INTEGER_PUSH(&integer, _digitPairs[index + 1]);
284 _PRINT_INTEGER_PUSH(&integer, _digitPairs[index]);
285 }
286
287 while (uvalue > 0)
288 {
289 _PRINT_INTEGER_PUSH(&integer, _int_to_digit(uvalue % 10));
290 uvalue /= 10;
291 }
292
293 return _print_integer_print(ctx, format, &integer);
294}
295
297{
298 uintmax_t value = 0;
299 switch (format->length)
300 {
301 case _PRINT_DEFAULT:
302 value = va_arg(ctx->arg, unsigned int);
303 break;
304 case _PRINT_HH:
305 value = (unsigned char)va_arg(ctx->arg, unsigned int);
306 break;
307 case _PRINT_H:
308 value = (unsigned short int)va_arg(ctx->arg, unsigned int);
309 break;
310 case _PRINT_L:
311 value = va_arg(ctx->arg, unsigned long int);
312 break;
313 case _PRINT_LL:
314 value = va_arg(ctx->arg, unsigned long long int);
315 break;
316 case _PRINT_J:
317 value = va_arg(ctx->arg, uintmax_t);
318 break;
319 case _PRINT_Z:
320 value = va_arg(ctx->arg, size_t);
321 break;
322 case _PRINT_T:
323 value = va_arg(ctx->arg, ptrdiff_t);
324 break;
325 default:
326 return EOF;
327 }
328
329 _print_integer_t integer = {.prefixPtr = integer.prefix + sizeof(integer.prefix),
330 .dataPtr = integer.data + sizeof(integer.data),
331 .base = base,
332 .sign = 0};
333
334 if (base == 10)
335 {
336 while (value >= 100)
337 {
338 unsigned int index = (value % 100) * 2;
339 value /= 100;
340 _PRINT_INTEGER_PUSH(&integer, _digitPairs[index + 1]);
341 _PRINT_INTEGER_PUSH(&integer, _digitPairs[index]);
342 }
343 while (value > 0)
344 {
345 _PRINT_INTEGER_PUSH(&integer, _int_to_digit(value % 10));
346 value /= 10;
347 }
348 }
349 else if (base == 16)
350 {
351 const char* digits = (format->flags & _PRINT_UPPER_CASE) ? _Xdigits : _xdigits;
352 while (value > 0)
353 {
354 _PRINT_INTEGER_PUSH(&integer, digits[value & 0xF]);
355 value >>= 4;
356 }
357 }
358 else if (base == 8)
359 {
360 while (value > 0)
361 {
362 _PRINT_INTEGER_PUSH(&integer, _int_to_digit(value & 7));
363 value >>= 3;
364 }
365 }
366 else
367 {
368 return EOF;
369 }
370
371 return _print_integer_print(ctx, format, &integer);
372}
373
375{
376 char c = (char)va_arg(ctx->arg, int);
377
378 int padding = _print_padding_left(ctx, format, 1);
379 if (padding < 0)
380 {
381 return EOF;
382 }
383
384 if (_PRINT_WRITE(ctx, &c, 1) == EOF)
385 {
386 return EOF;
387 }
388 ctx->written++;
389
390 return _print_padding_right(ctx, format, padding);
391}
392
394{
395 const char* s = va_arg(ctx->arg, const char*);
396 if (s == NULL)
397 {
398 s = "(null)"; // Not standard but very useful
399 }
400
401 int len = 0;
402 for (int i = 0; s[i] != '\0' && (format->precision == EOF || i < format->precision); i++)
403 {
404 len++;
405 }
406
407 int padding = _print_padding_left(ctx, format, len);
408 if (padding < 0)
409 {
410 return EOF;
411 }
412
413 if (_PRINT_WRITE(ctx, s, len) == EOF)
414 {
415 return EOF;
416 }
417 ctx->written += len;
418
419 return _print_padding_right(ctx, format, padding);
420}
421
422static inline int _print_format_written(_print_ctx_t* ctx)
423{
424 signed int* written = va_arg(ctx->arg, signed int*);
425 *written = (signed int)ctx->written;
426 return 0;
427}
428
429static inline int _print_format_percent(_print_ctx_t* ctx)
430{
431 if (_PRINT_WRITE(ctx, "%", 1) == EOF)
432 {
433 return EOF;
434 }
435 ctx->written++;
436 return 1;
437}
438
439static inline int _print_format(_print_ctx_t* ctx)
440{
441 // %[flags][width][.precision][length]specifier
442
444 .flags = 0,
445 .width = EOF,
446 .precision = EOF,
447 .length = _PRINT_DEFAULT,
448 };
449
450 while (true)
451 {
452 switch (*ctx->p)
453 {
454 case '-':
456 ctx->p++;
457 break;
458 case '+':
459 format.flags |= _PRINT_FORCE_SIGN;
460 ctx->p++;
461 break;
462 case ' ':
463 format.flags |= _PRINT_SPACE_SIGN;
464 ctx->p++;
465 break;
466 case '#':
468 ctx->p++;
469 break;
470 case '0':
471 format.flags |= _PRINT_PAD_ZERO;
472 ctx->p++;
473 break;
474 default:
475 goto flags_done;
476 }
477 }
478flags_done:
479
480 if (*ctx->p == '*')
481 {
482 format.width = va_arg(ctx->arg, int);
483 ctx->p++;
484 }
485 else if (isdigit(*ctx->p))
486 {
487 format.width = *ctx->p - '0';
488 ctx->p++;
489 while (isdigit(*ctx->p))
490 {
491 format.width = format.width * 10 + (*ctx->p - '0');
492 ctx->p++;
493 }
494 }
495
496 if (*ctx->p == '.')
497 {
498 format.precision = 0;
499 ctx->p++;
500 if (*ctx->p == '*')
501 {
502 format.precision = va_arg(ctx->arg, int);
503 ctx->p++;
504 }
505 else
506 {
507 while (isdigit(*ctx->p))
508 {
509 format.precision = format.precision * 10 + (*ctx->p - '0');
510 ctx->p++;
511 }
512 }
513 }
514
515 switch (*ctx->p)
516 {
517 case 'h':
518 ctx->p++;
519 if (*ctx->p == 'h')
520 {
521 format.length = _PRINT_HH;
522 ctx->p++;
523 }
524 else
525 {
526 format.length = _PRINT_H;
527 }
528 break;
529 case 'l':
530 ctx->p++;
531 if (*ctx->p == 'l')
532 {
533 format.length = _PRINT_LL;
534 ctx->p++;
535 }
536 else
537 {
538 format.length = _PRINT_L;
539 }
540 break;
541 case 'j':
542 format.length = _PRINT_J;
543 ctx->p++;
544 break;
545 case 'z':
546 format.length = _PRINT_Z;
547 ctx->p++;
548 break;
549 case 't':
550 format.length = _PRINT_T;
551 ctx->p++;
552 break;
553 default:
554 break;
555 }
556
557 char specifier = *ctx->p;
558 ctx->p++;
559
560 int ret = 0;
561 switch (specifier)
562 {
563 case 'd':
564 case 'i':
566 break;
567 case 'u':
568 ret = _print_format_unsigned_integer(ctx, &format, 10);
569 break;
570 case 'o':
572 break;
573 case 'X':
574 format.flags |= _PRINT_UPPER_CASE;
575 case 'x': // Fallthrough
576 ret = _print_format_unsigned_integer(ctx, &format, 16);
577 break;
578 case 'F':
579 case 'f':
580 case 'E':
581 case 'e':
582 case 'G':
583 case 'g':
584 case 'A':
585 case 'a':
586 /// @todo Implement floating point formatting
587 ret = EOF;
588 break;
589 case 'c':
590 format.flags &= ~_PRINT_PAD_ZERO;
591 ret = _print_format_char(ctx, &format);
592 break;
593 case 's':
594 format.flags &= ~_PRINT_PAD_ZERO;
595 ret = _print_format_string(ctx, &format);
596 break;
597 case 'p':
598 format.length = _PRINT_Z;
600 format.precision = 2 * sizeof(void*);
601 ret = _print_format_unsigned_integer(ctx, &format, 16);
602 break;
603 case 'n':
604 ret = _print_format_written(ctx);
605 break;
606 case '%':
607 ret = _print_format_percent(ctx);
608 break;
609 default:
610 ret = EOF;
611 break;
612 }
613
614 return ret;
615}
616
617static inline int _print(const char* _RESTRICT format, size_t n, va_list arg, void* data)
618{
619 assert(format != NULL);
620
621 _print_ctx_t ctx = {
622 .written = 0,
623 .n = n,
624 .p = format,
625 .data = data,
626 };
627 va_copy(ctx.arg, arg);
628
629 while (true)
630 {
631 uint64_t count = 0;
632 while (ctx.p[count] != '%' && ctx.p[count] != '\0')
633 {
634 count++;
635 }
636
637 if (count > 0)
638 {
639 if (_PRINT_WRITE(&ctx, ctx.p, count) == EOF)
640 {
641 break;
642 }
643 ctx.written += count;
644 ctx.p += count;
645 }
646
647 if (*ctx.p == '\0')
648 {
649 break;
650 }
651 ctx.p++;
652
653 if (_print_format(&ctx) == EOF)
654 {
655 break;
656 }
657 }
658
659 va_end(ctx.arg);
660 return ctx.written;
661}
662
663/** @} */
#define _RESTRICT
Definition config.h:17
#define assert(expression)
Definition assert.h:29
static char format[MAX_NAME]
Definition screen.c:17
#define isdigit(c)
Definition ctype.h:20
const char _Xdigits[]
Definition digits.c:7
const char _xdigits[]
Definition digits.c:5
const char _digitPairs[]
Definition digits.c:9
static char _int_to_digit(uint8_t i)
Definition digits.h:19
static fd_t data
Definition dwm.c:21
#define _PRINT_INTEGER_PREFIX_LEN(integer)
Definition print.h:142
static int _print_format(_print_ctx_t *ctx)
Definition print.h:439
static int _print_format_written(_print_ctx_t *ctx)
Definition print.h:422
static int _print_format_char(_print_ctx_t *ctx, _print_format_ctx_t *format)
Definition print.h:374
#define _PRINT_INTEGER_PUSH(integer, c)
Definition print.h:136
_print_format_length_t
Definition print.h:67
#define _PRINT_INTEGER_LEN(integer)
Definition print.h:140
static int _print_padding_left(_print_ctx_t *ctx, _print_format_ctx_t *format, int len)
Definition print.h:86
static int _print_format_percent(_print_ctx_t *ctx)
Definition print.h:429
_print_format_flags_t
Definition print.h:57
static int _print_format_string(_print_ctx_t *ctx, _print_format_ctx_t *format)
Definition print.h:393
#define _PRINT_INTEGER_PREFIX_PUSH(integer, c)
Definition print.h:138
static int _print_format_unsigned_integer(_print_ctx_t *ctx, _print_format_ctx_t *format, uint32_t base)
Definition print.h:296
static int _print(const char *_RESTRICT format, size_t n, va_list arg, void *data)
Definition print.h:617
static int _print_padding_right(_print_ctx_t *ctx, _print_format_ctx_t *format, int padding)
Definition print.h:112
static int _print_integer_print(_print_ctx_t *ctx, _print_format_ctx_t *format, _print_integer_t *integer)
Definition print.h:145
static int _print_format_signed_integer(_print_ctx_t *ctx, _print_format_ctx_t *format)
Definition print.h:234
@ _PRINT_J
Definition print.h:73
@ _PRINT_T
Definition print.h:75
@ _PRINT_H
Definition print.h:70
@ _PRINT_Z
Definition print.h:74
@ _PRINT_DEFAULT
Definition print.h:68
@ _PRINT_LL
Definition print.h:72
@ _PRINT_HH
Definition print.h:69
@ _PRINT_L
Definition print.h:71
@ _PRINT_PAD_ZERO
Definition print.h:63
@ _PRINT_SPACE_SIGN
Definition print.h:60
@ _PRINT_FORCE_SIGN
Definition print.h:59
@ _PRINT_UPPER_CASE
Definition print.h:62
@ _PRINT_ALTERNATE_FORM
Definition print.h:61
@ _PRINT_LEFT_ALIGNED
Definition print.h:58
#define NULL
Pointer error value.
Definition NULL.h:25
__UINTMAX_TYPE__ uintmax_t
Definition inttypes.h:12
__INTMAX_TYPE__ intmax_t
Definition inttypes.h:11
static atomic_long count
Definition main.c:11
__PTRDIFF_TYPE__ ptrdiff_t
Definition ptrdiff_t.h:4
#define va_copy(dest, src)
Definition stdarg.h:14
#define va_arg(ap, type)
Definition stdarg.h:13
#define va_end(ap)
Definition stdarg.h:15
__builtin_va_list va_list
Definition stdarg.h:11
__UINT32_TYPE__ uint32_t
Definition stdint.h:15
__UINT64_TYPE__ uint64_t
Definition stdint.h:17
__INT8_TYPE__ int8_t
Definition stdint.h:10
#define EOF
Definition stdio.h:25
size_t written
Definition print.h:50
va_list arg
Definition print.h:53
const char * p
Definition print.h:52
size_t n
Definition print.h:51
void * data
Definition print.h:54
_print_format_length_t length
Definition print.h:83
_print_format_flags_t flags
Definition print.h:80
char * prefixPtr
Definition print.h:129
char * dataPtr
Definition print.h:131
int8_t base
Definition print.h:133
char prefix[20]
Definition print.h:128
char data[32]
Definition print.h:130
int8_t sign
Definition print.h:132
#define _PRINT_WRITE(ctx, buffer, count)
Definition vsnprintf.c:3
#define _PRINT_FILL(ctx, c, count)
Definition vsnprintf.c:16