Xyris  0.5
printf.cpp
Go to the documentation of this file.
1 /**
2  * @file printf.cpp
3  * @author Chris Giese ([email protected])
4  * @author Keeton Feavel ([email protected])
5  * @brief A printf implementation released under the public domain.
6  * @version 0.3
7  * @date 2020-07-09
8  *
9  * @copyright This code is public domain (no copyright).
10  * You can do whatever you want with it. Modified by
11  * Keeton Feavel.
12  *
13  * http://www.osdev.labedz.org/src/lib/stdio/printf.c
14  *
15  */
16 #include <Library/stdio.hpp>
17 #include <Library/string.hpp>
18 #include <limits.h>
19 #include <stdarg.h>
20 
21 /*****************************************************************************
22 Stripped-down printf()
23 Chris Giese <[email protected]> http://www.execpc.com/~geezer
24 Release date: Dec 12, 2003
25 This code is public domain (no copyright).
26 You can do whatever you want with it.
27 
28 Revised Dec 12, 2003
29 - fixed vsprintf() and sprintf() in test code
30 
31 Revised Jan 28, 2002
32 - changes to make characters 0x80-0xFF display properly
33 
34 Revised June 10, 2001
35 - changes to make vsprintf() terminate string with '\0'
36 
37 Revised May 12, 2000
38 - math in DO_NUM is now unsigned, as it should be
39 - %0 flag (pad left with zeroes) now works
40 - actually did some TESTING, maybe fixed some other bugs
41 
42 %[flag][width][.prec][mod][conv]
43 flag:
44  - left justify, pad right w/ blanks DONE
45  0 pad left w/ 0 for numerics DONE
46  + always print sign, + or - no
47  ' ' (blank) no
48  # (???) no
49 
50 width: (field width) DONE
51 
52 prec: (precision) no
53 
54 conv:
55  d,i decimal int DONE
56  u decimal unsigned DONE
57  o octal DONE
58  x,X hex DONE
59  f,e,g,E,G float no
60  c char DONE
61  s string DONE
62  p ptr DONE
63  z size_t DONE
64 
65 mod:
66  N near ptr DONE
67  F far ptr no
68  h short (16-bit) int DONE
69  l long (32-bit) int DONE
70  L long long (64-bit) int DONE
71 *****************************************************************************/
72 
73 /* Assume: width of stack == width of int. Don't use sizeof(char *) or
74 other pointer because sizeof(char *)==4 for LARGE-model 16-bit code.
75 Assume: width is a power of 2 */
76 #define STACK_WIDTH sizeof(int)
77 
78 /* Round up object width so it's an even multiple of STACK_WIDTH.
79 Using & for division here, so STACK_WIDTH must be a power of 2. */
80 #define TYPE_WIDTH(TYPE) ((sizeof(TYPE) + STACK_WIDTH - 1) & ~(STACK_WIDTH - 1))
81 
82 /* flags used in processing format string */
84  PR_LJ = 0b000000001, /* left justify */
85  PR_CA = 0b000000010, /* use A-F instead of a-f for hex */
86  PR_SG = 0b000000100, /* signed numeric conversion (%d vs. %u) */
87  PR_64 = 0b000001000, /* long long (64-bit) numeric conversion*/
88  PR_32 = 0b000010000, /* long (32-bit) numeric conversion */
89  PR_16 = 0b000100000, /* short (16-bit) numeric conversion */
90  PR_WS = 0b001000000, /* PR_SG set and num was < 0 */
91  PR_LZ = 0b010000000, /* pad left with '0' instead of ' ' */
92  PR_FP = 0b100000000, /* pointers are far */
93 };
94 /* largest number handled is 2^32-1, lowest radix handled is 8.
95 2^64-1 in base 8 has 22 digits (add 5 for trailing NUL and for slop)
96 round to 32 so that it's on a byte boundary */
97 #define PR_BUFLEN 32
98 
99 /*****************************************************************************
100 name: printf_helper
101 action: minimal subfunction for ?printf, calls function
102  'fn' with arg 'ptr' for each character to be output
103 returns:total number of characters output
104 *****************************************************************************/
105 int printf_helper(const char* fmt, va_list args, printf_cb_fnptr_t fn, void* ptr)
106 {
107  unsigned char radix, *where, buf[PR_BUFLEN];
108  unsigned int state, flags, actual_wd, count, given_wd;
109  long num;
110 
111  state = flags = count = given_wd = 0;
112  /* begin scanning format specifier list */
113  for (; *fmt; fmt++) {
114  switch (state) {
115  /* STATE 0: AWAITING % */
116  case 0:
117  if (*fmt != '%') /* not %... */
118  {
119  fn(*fmt, &ptr); /* ...just echo it */
120  count++;
121  break;
122  }
123  /* found %, get next char and advance state to check if next char is a flag */
124  state++;
125  fmt++;
126  /* FALL THROUGH */
127  /* STATE 1: AWAITING FLAGS (%-0) */
128  case 1:
129  if (*fmt == '%') /* %% */
130  {
131  fn(*fmt, &ptr);
132  count++;
133  state = flags = given_wd = 0;
134  break;
135  }
136  if (*fmt == '-') {
137  if (flags & PR_LJ) /* %-- is illegal */
138  state = flags = given_wd = 0;
139  else
140  flags |= PR_LJ;
141  break;
142  }
143  /* not a flag char: advance state to check if it's field width */
144  state++;
145  /* check now for '%0...' */
146  if (*fmt == '0') {
147  flags |= PR_LZ;
148  fmt++;
149  }
150  /* FALL THROUGH */
151  /* STATE 2: AWAITING (NUMERIC) FIELD WIDTH */
152  case 2:
153  if (*fmt >= '0' && *fmt <= '9') {
154  given_wd = 10 * given_wd + (*fmt - '0');
155  break;
156  }
157  /* not field width: advance state to check if it's a modifier */
158  state++;
159  /* FALL THROUGH */
160  /* STATE 3: AWAITING MODIFIER CHARS (FNlh) */
161  case 3:
162  if (*fmt == 'F') {
163  flags |= PR_FP;
164  break;
165  }
166  if (*fmt == 'N')
167  break;
168 
169  if (*fmt == 'z') {
170 #if (UINTPTR_MAX == UINT32_MAX)
171  flags |= PR_32;
172 #elif (UINTPTR_MAX == UINT64_MAX)
173  flags |= PR_64;
174 #else
175 #error "Unknown UINTPTR_MAX value! Cannot compile printf_helper!"
176 #endif
177  break;
178  }
179  if (*fmt == 'l') {
180  flags |= PR_32;
181  break;
182  }
183  if (*fmt == 'L') {
184  flags |= PR_64;
185  break;
186  }
187  if (*fmt == 'h') {
188  flags |= PR_16;
189  break;
190  }
191  /* not modifier: advance state to check if it's a conversion char */
192  state++;
193  /* FALL THROUGH */
194  /* STATE 4: AWAITING CONVERSION CHARS (Xxpndiuocs) */
195  case 4: {
196  where = buf + PR_BUFLEN - 1;
197  *where = '\0';
198  switch (*fmt) {
199  case 'X':
200  flags |= PR_CA;
201  /* FALL THROUGH */
202  /* xxx - far pointers (%Fp, %Fn) not yet supported */
203  case 'x':
204  case 'p':
205  case 'n':
206  radix = 16;
207  goto DO_NUM;
208  case 'd':
209  case 'i':
210  flags |= PR_SG;
211  /* FALL THROUGH */
212  case 'u':
213  radix = 10;
214  goto DO_NUM;
215  case 'o':
216  radix = 8;
217  /* load the value to be printed. l=long=32 bits: */
218  DO_NUM:
219  if (flags & PR_64)
220  num = va_arg(args, unsigned long long);
221  else if (flags & PR_32)
222  num = va_arg(args, unsigned long);
223  /* h=short=16 bits (signed or unsigned) */
224  else if (flags & PR_16) {
225  if (flags & PR_SG)
226  num = va_arg(args, int);
227  else
228  num = va_arg(args, int);
229  }
230  /* no h nor l: sizeof(int) bits (signed or unsigned) */
231  else {
232  if (flags & PR_SG)
233  num = va_arg(args, int);
234  else
235  num = va_arg(args, unsigned int);
236  }
237  /* take care of sign */
238  if (flags & PR_SG) {
239  if (num < 0) {
240  flags |= PR_WS;
241  num = -num;
242  }
243  }
244  /* convert binary to octal/decimal/hex ASCII
245  OK, I found my mistake. The math here is _always_ unsigned */
246  do {
247  unsigned long temp;
248 
249  temp = (unsigned long)num % radix;
250  where--;
251  if (temp < 10)
252  *where = (unsigned char)(temp + '0');
253  else if (flags & PR_CA)
254  *where = (unsigned char)(temp - 10 + 'A');
255  else
256  *where = (unsigned char)(temp - 10 + 'a');
257  num = (unsigned long)num / radix;
258  } while (num != 0);
259  goto EMIT;
260  case 'c':
261  /* disallow pad-left-with-zeroes for %c */
262  flags &= ~PR_LZ;
263  where--;
264  *where = (unsigned char)va_arg(args, int);
265  actual_wd = 1;
266  goto EMIT2;
267  case 's':
268  /* disallow pad-left-with-zeroes for %s */
269  flags &= ~PR_LZ;
270  where = va_arg(args, unsigned char*);
271  EMIT:
272  actual_wd = strlen((const char*)where);
273  if (flags & PR_WS)
274  actual_wd++;
275  /* if we pad left with ZEROES, do the sign now */
276  if ((flags & (PR_WS | PR_LZ)) == (PR_WS | PR_LZ)) {
277  fn('-', &ptr);
278  count++;
279  }
280  /* pad on left with spaces or zeroes (for right justify) */
281  EMIT2:
282  if ((flags & PR_LJ) == 0) {
283  while (given_wd > actual_wd) {
284  fn(flags & PR_LZ ? '0' : ' ', &ptr);
285  count++;
286  given_wd--;
287  }
288  }
289  /* if we pad left with SPACES, do the sign now */
290  if ((flags & (PR_WS | PR_LZ)) == PR_WS) {
291  fn('-', &ptr);
292  count++;
293  }
294  /* emit string/char/converted number */
295  while (*where != '\0') {
296  fn(*where++, &ptr);
297  count++;
298  }
299  /* pad on right with spaces (for left justify) */
300  if (given_wd < actual_wd)
301  given_wd = 0;
302  else
303  given_wd -= actual_wd;
304  for (; given_wd; given_wd--) {
305  fn(' ', &ptr);
306  count++;
307  }
308  break;
309  default:
310  break;
311  }
312  // Tell the compiler that this isn't an issue (at least I'm assuming it isn't)
313  [[fallthrough]];
314  }
315  default:
316  state = flags = given_wd = 0;
317  break;
318  }
319  }
320  return count;
321 }
322 
323 static int vsprintf_help(unsigned c, void** ptr)
324 {
325  char* dst;
326 
327  dst = (char*)*ptr;
328  *dst++ = (char)c;
329  *ptr = dst;
330  return 0;
331 }
332 
333 int kvsprintf(char* buf, const char* fmt, va_list args)
334 {
335  int ret_val;
336 
337  ret_val = printf_helper(fmt, args, vsprintf_help, (void*)buf);
338  buf[ret_val] = '\0';
339  return ret_val;
340 }
341 
342 int ksprintf(char* buf, const char* fmt, ...)
343 {
344  va_list args;
345  int ret_val;
346 
347  va_start(args, fmt);
348  ret_val = kvsprintf(buf, fmt, args);
349  va_end(args);
350  return ret_val;
351 }
stdio.hpp
PR_16
@ PR_16
Definition: printf.cpp:89
printf_state
printf_state
Definition: printf.cpp:83
ksprintf
int ksprintf(char *buf, const char *fmt,...)
Sends formatted output to a string.
Definition: printf.cpp:342
PR_64
@ PR_64
Definition: printf.cpp:87
kvsprintf
int kvsprintf(char *buf, const char *fmt, va_list args)
Sends formatted output to a string using an argument list.
Definition: printf.cpp:333
printf_cb_fnptr_t
int(* printf_cb_fnptr_t)(unsigned c, void **helper)
Definition: stdio.hpp:14
PR_LZ
@ PR_LZ
Definition: printf.cpp:91
strlen
int strlen(const char *s)
Returns the length of a string.
Definition: string.cpp:14
PR_BUFLEN
#define PR_BUFLEN
Definition: printf.cpp:97
string.hpp
Standard string and memory utility library.
PR_CA
@ PR_CA
Definition: printf.cpp:85
PR_32
@ PR_32
Definition: printf.cpp:88
PR_LJ
@ PR_LJ
Definition: printf.cpp:84
printf_helper
int printf_helper(const char *fmt, va_list args, printf_cb_fnptr_t fn, void *ptr)
Perform all printf operations on the format string using the provided argument list and uses the call...
Definition: printf.cpp:105
PR_SG
@ PR_SG
Definition: printf.cpp:86
PR_WS
@ PR_WS
Definition: printf.cpp:90
PR_FP
@ PR_FP
Definition: printf.cpp:92