The Pedigree Project  0.1
vsprintf.c
1 /*
2  * Copyright (c) 2008-2014, Pedigree Developers
3  *
4  * Please see the CONTRIB file in the root of the source tree for a full
5  * list of contributors.
6  *
7  * Permission to use, copy, modify, and distribute this software for any
8  * purpose with or without fee is hereby granted, provided that the above
9  * copyright notice and this permission notice appear in all copies.
10  *
11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18  */
19 
20 /* vsprintf.c -- Lars Wirzenius & Linus Torvalds. */
21 /*
22  * Wirzenius wrote this portably, Torvalds fucked it up :-)
23  */
24 
25 #include "pedigree/kernel/processor/types.h"
26 #include "pedigree/kernel/utilities/utility.h"
27 #include <stdarg.h>
28 
29 #ifdef __clang__
30 #pragma GCC diagnostic ignored "-Wgnu-statement-expression"
31 #endif
32 
33 /* we use this so that we can do without the ctype library */
34 #define is_digit(c) ((c) >= '0' && (c) <= '9')
35 
36 static int skip_atoi(const char **s)
37 {
38  int i = 0;
39 
40  while (is_digit(**s))
41  i = i * 10 + *((*s)++) - '0';
42  return i;
43 }
44 
45 #define ZEROPAD 1 /* pad with zero */
46 #define SIGN 2 /* unsigned/signed long */
47 #define PLUS 4 /* show plus */
48 #define SPACE 8 /* space if plus */
49 #define LEFT 16 /* left justified */
50 #define SPECIAL 32 /* 0x */
51 #define SMALL 64 /* use 'abcdef' instead of 'ABCDEF' */
52 
54 #if defined(X86_COMMON) || defined(HOSTED_X86_COMMON) || defined(UTILITY_LINUX)
55 #define do_div(n, base) \
56  ({ \
57  int __res; \
58  __asm__("div %4" : "=a"(n), "=d"(__res) : "0"(n), "1"(0), "r"(base)); \
59  __res; \
60  })
61 #else
62 #define do_div(n, base) ({ n / base; })
63 #endif
64 
65 static char *
66 number(char *str, int64_t num, int base, int size, int precision, int type)
67 {
68  char c, sign, tmp[36];
69  const char *digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
70  int i;
71 
72  if (type & SMALL)
73  digits = "0123456789abcdefghijklmnopqrstuvwxyz";
74  if (type & LEFT)
75  type &= ~ZEROPAD;
76  if (base < 2 || base > 36)
77  return 0;
78  c = (type & ZEROPAD) ? '0' : ' ';
79  if (type & SIGN && num < 0)
80  {
81  sign = '-';
82  num = -num;
83  }
84  else
85  sign = (type & PLUS) ? '+' : ((type & SPACE) ? ' ' : 0);
86  if (sign)
87  size--;
88  if (type & SPECIAL)
89  {
90  if (base == 16)
91  size -= 2;
92  else if (base == 8)
93  size--;
94  }
95  i = 0;
96  if (num == 0)
97  tmp[i++] = '0';
98  else
99  while (num != 0)
100  {
101  int d = do_div(num, base);
102  tmp[i++] = digits[d];
103  }
104  if (i > precision)
105  precision = i;
106  size -= precision;
107  if (!(type & (ZEROPAD + LEFT)))
108  while (size-- > 0)
109  *str++ = ' ';
110  if (sign)
111  *str++ = sign;
112  if (type & SPECIAL)
113  {
114  if (base == 8)
115  *str++ = '0';
116  else if (base == 16)
117  {
118  *str++ = '0';
119  *str++ = digits[33];
120  }
121  }
122  if (!(type & LEFT))
123  while (size-- > 0)
124  *str++ = c;
125  while (i < precision--)
126  *str++ = '0';
127  while (i-- > 0)
128  *str++ = tmp[i];
129  while (size-- > 0)
130  *str++ = ' ';
131  return str;
132 }
133 
134 int VStringFormat(char *buf, const char *fmt, va_list args)
135 {
136  int len;
137  int i;
138  char *str;
139  char *s;
140  int *ip;
141 
142  int flags; /* flags to number() */
143 
144  int field_width; /* width of output field */
145  int precision; /* min. # of digits for integers; max
146  number of chars for from string */
147  int qualifier; /* 'h', 'l', or 'L' for integer fields */
148 
149  for (str = buf; *fmt; ++fmt)
150  {
151  if (*fmt != '%')
152  {
153  *str++ = *fmt;
154  continue;
155  }
156 
157  /* process flags */
158  flags = 0;
159  repeat:
160  ++fmt; /* this also skips first '%' */
161  switch (*fmt)
162  {
163  case '-':
164  flags |= LEFT;
165  goto repeat;
166  case '+':
167  flags |= PLUS;
168  goto repeat;
169  case ' ':
170  flags |= SPACE;
171  goto repeat;
172  case '#':
173  flags |= SPECIAL;
174  goto repeat;
175  case '0':
176  flags |= ZEROPAD;
177  goto repeat;
178  }
179 
180  /* get field width */
181  field_width = -1;
182  if (is_digit(*fmt))
183  field_width = skip_atoi(&fmt);
184  else if (*fmt == '*')
185  {
186  /* it's the next argument */
187  field_width = va_arg(args, int);
188  if (field_width < 0)
189  {
190  field_width = -field_width;
191  flags |= LEFT;
192  }
193  }
194 
195  /* get the precision */
196  precision = -1;
197  if (*fmt == '.')
198  {
199  ++fmt;
200  if (is_digit(*fmt))
201  precision = skip_atoi(&fmt);
202  else if (*fmt == '*')
203  {
204  /* it's the next argument */
205  precision = va_arg(args, int);
206  }
207  if (precision < 0)
208  precision = 0;
209  }
210 
211  /* get the conversion qualifier */
212  qualifier = -1;
213  if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L')
214  {
215  qualifier = *fmt;
216  ++fmt;
217  }
218 
219  switch (*fmt)
220  {
221  case 'c':
222  if (!(flags & LEFT))
223  while (--field_width > 0)
224  *str++ = ' ';
225  *str++ = (unsigned char) va_arg(args, int);
226  while (--field_width > 0)
227  *str++ = ' ';
228  break;
229 
230  case 's':
231  s = va_arg(args, char *);
232  len = StringLength(s);
233  if (precision < 0)
234  precision = len;
235  else if (len > precision)
236  len = precision;
237 
238  if (!(flags & LEFT))
239  while (len < field_width--)
240  *str++ = ' ';
241  for (i = 0; i < len; ++i)
242  *str++ = *s++;
243  while (len < field_width--)
244  *str++ = ' ';
245  break;
246 
247  case 'o':
248  str = number(
249  str, va_arg(args, unsigned long), 8, field_width, precision,
250  flags);
251  break;
252 
253  case 'p':
254  if (field_width == -1)
255  {
256  field_width = sizeof(void *) * 2;
257  flags |= ZEROPAD;
258  }
259  str = number(
260  str, (uintptr_t) va_arg(args, void *), 16, field_width,
261  precision, flags);
262  break;
263 
264  case 'x':
265  flags |= SMALL;
266  case 'X':
267  str = number(
268  str, va_arg(args, unsigned long), 16, field_width,
269  precision, flags);
270  break;
271 
272  case 'd':
273  case 'i':
274  flags |= SIGN;
275  case 'u':
276  str = number(
277  str, va_arg(args, unsigned long), 10, field_width,
278  precision, flags);
279  break;
280 
281  case 'n':
282  ip = va_arg(args, int *);
283  *ip = (str - buf);
284  break;
285 
286  default:
287  if (*fmt != '%')
288  *str++ = '%';
289  if (*fmt)
290  *str++ = *fmt;
291  else
292  --fmt;
293  break;
294  }
295  }
296  *str = '\0';
297  return str - buf;
298 }