Switch to a simple X11-style license.
[polintos/scott/priv.git] / kernel / lib / libc.cc
1 // lib/libc.cc -- Standard C-library functions
2 //
3 // This software is copyright (c) 2006 Scott Wood <scott@buserror.net>.
4 // 
5 // Permission is hereby granted, free of charge, to any person obtaining a copy of
6 // this software and associated documentation files (the "Software"), to deal with
7 // the Software without restriction, including without limitation the rights to
8 // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
9 // of the Software, and to permit persons to whom the Software is furnished to do
10 // so, subject to the following condition:
11 // 
12 // The above copyright notice and this permission notice shall be
13 // included in all copies or substantial portions of the Software.
14 // 
15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17 // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
18 // CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE
21 // SOFTWARE.
22
23
24 #include <kern/types.h>
25 #include <kern/libc.h>
26
27 #include <stdarg.h>
28 #include <limits.h>
29
30 // FIXME: Move printf to lib/kernel
31
32 static const int alt_form =        0x0001;
33 static const int zero_pad =        0x0002;
34 static const int neg_field =       0x0004;
35 static const int leave_blank =     0x0008;
36 static const int always_sign =     0x0010;
37 static const int group_thousands = 0x0020; // FIXME -- unimplemented
38 static const int long_arg =        0x0040;
39 static const int long_long_arg =   0x0080;
40 static const int short_arg =       0x0100;
41 static const int short_short_arg = 0x0200;
42 static const int intmax_arg =      0x0400;
43 static const int ptrdiff_arg =     0x0800;
44 static const int size_t_arg =      0x1000;
45 static const int capital_hex =     0x2000;
46 static const int num_signed =      0x4000;
47 static const int has_precision =   0x8000;
48
49 static void printf_string(char *buf, size_t &opos, size_t limit,
50                           char *src, size_t len)
51 {
52         if (opos < limit) {
53                 size_t olen = opos + len <= limit ? len : limit - opos;
54                 memcpy(buf + opos, src, olen);
55         }
56
57         opos += len;
58 }
59
60 static void printf_fill(char *buf, size_t &opos, size_t limit,
61                         char ch, int len)
62 {
63         if (opos < limit) {
64                 size_t olen = opos + len <= limit ? len : limit - opos;
65                 memset(buf + opos, ch, olen);
66         }
67         
68         opos += len;
69 }
70
71 static void printf_num(char *obuf, size_t &opos, size_t limit,
72                        s64 value, long radix, int fieldwidth,
73                        int precision, int flags)
74 {
75         char buf[65];
76         int pos = 64;
77         int letter = (flags & capital_hex) ? 'A' - 10 : 'a' - 10;
78         u64 uval;
79
80         if (flags & num_signed)
81                 uval = value < 0 ? -value : value;
82         else
83                 uval = value;
84         
85         // An explicit precision of 0 suppresses all output if the value
86         // is zero.  Otherwise, the output size is not limited by precision
87         // or field width.
88         
89         if (uval != 0 || !(flags & has_precision) || precision != 0) do {
90                 int ch = uval % radix;
91                 
92                 if (ch < 10)
93                         buf[pos] = ch + '0';
94                 else
95                         buf[pos] = ch + letter;
96                 
97                 uval /= radix;
98                 pos--;
99         } while (uval);
100         
101         int len = 64 - pos;
102
103         // length which counts against fieldwidth but not precision
104         int extralen = 0; 
105         
106         if (flags & num_signed) {
107                 if (value < 0) {
108                         printf_fill(obuf, opos, limit, '-', 1);
109                         extralen += 1;
110                 } else if (flags & always_sign) {
111                         printf_fill(obuf, opos, limit, '+', 1);
112                         extralen += 1;
113                 } else if (flags & leave_blank) {
114                         printf_fill(obuf, opos, limit, ' ', 1);
115                         extralen += 1;
116                 }
117         }
118         
119         if ((flags & alt_form) && value != 0) {
120                 if (radix == 8 && (!(flags & has_precision) || precision <= len)) {
121                         flags |= has_precision;
122                         precision = len + 1;
123                 }
124                 
125                 if (radix == 16) {
126                         printf_string(obuf, opos, limit, "0x", 2);
127                         extralen += 2;
128                 }
129         }
130         
131         if ((flags & has_precision) && len < precision) {
132                 precision -= len;
133                 len += precision;
134         } else {
135                 precision = 0;
136         }
137         
138         len += extralen;
139         
140         if (!(flags & neg_field) && len < fieldwidth) {
141                 char padchar = (flags & zero_pad) ? '0' : ' ';
142                 printf_fill(obuf, opos, limit, padchar, fieldwidth - len);
143                 len = fieldwidth;
144         }
145
146         if (precision != 0) {
147                 printf_fill(obuf, opos, limit, '0', precision);
148                 len += precision;
149         }
150         
151         printf_string(obuf, opos, limit, buf + pos + 1, 64 - pos);
152         
153         if ((flags & neg_field) && len < fieldwidth)
154                 printf_fill(obuf, opos, limit, ' ', fieldwidth - len);
155 }
156
157 size_t vsnprintf(char *buf, size_t size, const char *str, va_list args)
158 {
159         size_t opos = 0; // position in the output string
160         unsigned int flags = 0;
161         int radix = 10;
162         int state = 0;
163         int fieldwidth = 0;
164         int precision = 0;
165
166         for (size_t pos = 0; str[pos]; pos++) switch (state) {
167                 case 0:
168                         if (str[pos] == '%') {
169                                 flags = 0;
170                                 radix = 10;
171                                 state = 1;
172                                 fieldwidth = 0;
173                                 precision = 0;
174                                 break;
175                         }
176                 
177                         if (opos < size)
178                                 buf[opos] = str[pos];
179
180                         opos++;
181                         break;
182                 
183                 case 1: // A percent has been seen; read in format characters
184                         switch (str[pos]) {
185                                 case '#':
186                                         flags |= alt_form;
187                                         break;
188                                 
189                                 case '0':
190                                         if (!(flags & has_precision)) {
191                                                 flags |= zero_pad;
192                                                 break;
193                                         }
194                                         
195                                         // else fall through
196                                 
197                                 case '1' ... '9':
198                                         if (flags & has_precision)
199                                                 goto default_case;
200                                         
201                                         do {
202                                                 fieldwidth *= 10;
203                                                 fieldwidth += str[pos++] - '0';
204                                         } while (str[pos] >= '0' && str[pos] <= '9');
205                                         
206                                         pos--;
207                                         break;
208                                 
209                                 case '*':
210                                         if (fieldwidth || (flags & has_precision))
211                                                 goto default_case;
212                                         
213                                         fieldwidth = va_arg(args, int);
214                                         break;
215                                 
216                                 case '.':
217                                         flags |= has_precision;
218                                         
219                                         if (str[pos + 1] == '*') {
220                                                 pos++;
221                                                 precision = va_arg(args, int);
222                                         } else while (str[pos + 1] >= '0' && str[pos + 1] <= '9') {
223                                                 precision *= 10;
224                                                 precision += str[++pos] - '0';
225                                         }
226                                         
227                                         break;
228                                                 
229                                 case '-':
230                                         flags |= neg_field;
231                                         break;
232                                 
233                                 case ' ':
234                                         flags |= leave_blank;
235                                         break;
236                                 
237                                 case '+':
238                                         flags |= always_sign;
239                                         break;
240                                 
241                                 case '\'':
242                                         flags |= group_thousands;
243                                         break;
244
245                                 case 'l':
246                                         if (flags & long_arg)
247                                                 flags |= long_long_arg;
248                                         else
249                                                 flags |= long_arg;
250         
251                                         break;
252                                 
253                                 case 'h':
254                                         if (flags & long_arg)
255                                                 flags |= short_short_arg;
256                                         else
257                                                 flags |= short_arg;
258
259                                         break;
260                                 
261                                 case 'j':
262                                         flags |= intmax_arg;
263                                         break;
264                                 
265                                 case 't':
266                                         flags |= ptrdiff_arg;
267                                         break;
268                                 
269                                 // Note that %z and other such "new" format characters are
270                                 // basically useless because some GCC coder actually went out
271                                 // of their way to make the compiler reject C99 format
272                                 // strings in C++ code, with no way of overriding it that I
273                                 // can find (the source code comments suggest the checking is
274                                 // only when using -pedantic, but I wasn't using -pedantic).
275                                 //
276                                 // Thus, we have the choice of either avoiding %z and friends
277                                 // (and possibly needing to insert hackish casts to silence
278                                 // the compiler's warnings if different architectures define
279                                 // types like size_t in different ways), or not using the
280                                 // format warnings at all.
281                                 //
282                                 // To mitigate this, 32-bit architectures should define
283                                 // pointer-sized special types as "long" rather than "int",
284                                 // so that %lx/%ld can always be used with them.  Fixed-size
285                                 // 32-bit types should be declared as "int" rather than
286                                 // "long" for the same reason.
287                                 
288                                 case 'z':
289                                         flags |= size_t_arg;
290                                         break;
291                                 
292                                 case 'd':
293                                 case 'i': {
294                                         s64 arg;
295                                 
296                                         if ((flags & intmax_arg) || (flags & long_long_arg))
297                                                 arg = va_arg(args, long long);
298                                         else if (flags & size_t_arg)
299                                                 arg = va_arg(args, ssize_t);
300                                         else if (flags & ptrdiff_arg)
301                                                 arg = va_arg(args, ptrdiff_t);
302                                         else if (flags & long_arg)
303                                                 arg = va_arg(args, long);
304                                         else if (flags & short_short_arg)
305                                                 arg = (signed char)va_arg(args, int);
306                                         else if (flags & short_arg)
307                                                 arg = (short)va_arg(args, int);
308                                         else
309                                                 arg = va_arg(args, int);
310                                         
311                                         flags |= num_signed;
312                                         printf_num(buf, opos, size, arg, 10,
313                                                    fieldwidth, precision, flags);
314                                         state = 0;
315                                         break;
316                                 }
317
318                                 case 'X':
319                                         flags |= capital_hex;
320                                         // fall-through
321
322                                 case 'x':
323                                         radix = 18;
324                                         // fall-through
325                                         
326                                 case 'o':
327                                         radix -= 2;
328                                         // fall-through
329                                 
330                                 case 'u': {
331                                         u64 arg;
332                                 
333                                         if ((flags & intmax_arg) || (flags & long_long_arg))
334                                                 arg = va_arg(args, unsigned long long);
335                                         else if (flags & size_t_arg)
336                                                 arg = va_arg(args, size_t);
337                                         else if (flags & ptrdiff_arg)
338                                                 arg = va_arg(args, intptr_t);
339                                         else if (flags & long_arg)
340                                                 arg = va_arg(args, unsigned long);
341                                         else if (flags & short_short_arg)
342                                                 arg = (unsigned char)va_arg(args, unsigned int);
343                                         else if (flags & short_arg)
344                                                 arg = (unsigned short)va_arg(args, unsigned int);
345                                         else if (flags & short_short_arg)
346                                                 arg = (signed char)va_arg(args, int);
347                                         else if (flags & short_arg)
348                                                 arg = (short)va_arg(args, int);
349                                         else
350                                                 arg = va_arg(args, unsigned int);
351                                         
352                                         printf_num(buf, opos, size, arg, radix,
353                                                    fieldwidth, precision, flags);
354                                         state = 0;
355                                         break;
356                                 }
357                                 
358                                 case 'c':
359                                         if (opos < size)
360                                                 buf[opos] = va_arg(args, int);
361         
362                                         opos++;
363                                         state = 0;
364                                         break;
365                                 
366                                 case 's': {
367                                         char *arg = va_arg(args, char *);
368                                         
369                                         if (!arg)
370                                                 arg = "(null)";
371                                         
372                                         size_t len = strlen(arg);
373                                         printf_string(buf, opos, size, arg, len);
374                                         state = 0;
375                                         break;
376                                 }
377                                 
378                                 case 'p': {
379                                         void *arg = va_arg(args, void *);
380
381                                         printf_num(buf, opos, size, (ulong)arg, 16,
382                                                    fieldwidth, precision, flags);
383                                         
384                                         state = 0;
385                                         break;
386                                 }
387                                 
388                                 case 'n': {
389                                         if ((flags & intmax_arg) || (flags & long_long_arg))
390                                                 *va_arg(args, unsigned long long *) = opos;
391                                         else if (flags & size_t_arg)
392                                                 *va_arg(args, ssize_t *) = opos;
393                                         else if (flags & ptrdiff_arg)
394                                                 *va_arg(args, ptrdiff_t *) = opos;
395                                         else if (flags & long_arg)
396                                                 *va_arg(args, long *) = opos;
397                                         else if (flags & short_short_arg)
398                                                 *va_arg(args, signed char *) = opos;
399                                         else if (flags & short_arg)
400                                                 *va_arg(args, short *) = opos;
401                                         else
402                                                 *va_arg(args, int *) = opos;
403                                                 
404                                         state = 0;
405                                         break;
406                                 }
407
408                                 default_case: // label for goto
409                                 default:
410                                         if (opos < size)
411                                                 buf[opos] = str[pos];
412                                         
413                                         opos++;
414                                         state = 0;
415                                         break;
416                         }
417         }
418         
419         if (size > 0 && opos >= size)
420                 buf[size - 1] = 0;
421         
422         return opos;
423 }
424
425 size_t snprintf(char *buf, size_t size, const char *str, ...)
426 {
427         va_list args;
428         va_start(args, str);
429         int ret = vsnprintf(buf, size, str, args);
430         va_end(args);
431         return ret;
432 }
433
434 size_t sprintf(char *buf, const char *str, ...)
435 {
436         va_list args;
437         va_start(args, str);
438         int ret = vsnprintf(buf, ULONG_MAX, str, args);
439         va_end(args);
440         return ret;
441 }
442
443 void *memcpy(void *dest, const void *src, size_t len)
444 {
445         const char *cs = static_cast<const char *>(src);
446         char *cd = static_cast<char *>(dest);
447
448         for (size_t i = 0; i < len; i++)
449                 cd[i] = cs[i];
450
451         return dest;
452 }
453
454 void *memmove(void *dest, const void *src, size_t len)
455 {
456         if (dest < src)
457                 return memcpy(dest, src, len);
458
459         const char *cs = static_cast<const char *>(src);
460         char *cd = static_cast<char *>(dest);
461
462         for (size_t i = len - 1; i >= 0; i--)
463                 cd[i] = cs[i];
464
465         return dest;
466 }
467
468 int memcmp(const void *b1, const void *b2, size_t len)
469 {
470         size_t pos;
471         const char *c1 = static_cast<const char *>(b1);
472         const char *c2 = static_cast<const char *>(b2);
473         
474         for (pos = 0; pos < len; pos++) {
475                 if (c1[pos] != c2[pos])
476                         return c1[pos] - c2[pos];
477                         
478                 pos++;
479         }
480         
481         return 0;
482 }
483
484 size_t strnlen(const char *s, size_t n)
485 {
486         size_t pos = 0;
487         while (pos < n && *s++)
488                 pos++;
489         return pos;
490 }
491
492 size_t strlen(const char *s)
493 {
494         size_t pos = 0;
495         while (*s++)
496                 pos++;
497         return pos;
498 }
499
500 char *strcpy(char *dest, const char *src)
501 {
502         char *orig = dest;
503
504         do {
505                 *dest = *src++;
506         } while (*dest++);
507
508         return orig;
509 }
510
511 char *strncpy(char *dest, const char *src, size_t len)
512 {
513         char *orig = dest;
514
515         while (len--) {
516                 *dest = *src++;
517                 
518                 if (!*dest++)
519                         break;
520         }
521         
522         bzero(dest, len);
523         return orig;
524 }
525
526 void bzero(void *b, size_t len)
527 {
528         char *c = static_cast<char *>(b);
529         
530         while (len--)
531                 *c++ = 0;
532 }
533
534 void *memset(void *b, int ch, size_t len)
535 {
536         char *c = static_cast<char *>(b);
537         
538         while (len--)
539                 *c++ = ch;
540
541         return b;
542 }
543
544 #include <kern/pagealloc.h>
545
546 // Temporary hack until slab allocator is added
547
548 void *malloc(size_t len)
549 {
550         len = (len + sizeof(size_t) + Arch::page_size - 1) / Arch::page_size;
551         Mem::Page *page = Mem::PageAlloc::alloc(len);
552         
553         size_t *ptr = (size_t *)Mem::page_to_kvirt(page);
554         *ptr = len;
555         
556         return ptr + 1;
557 }
558
559 void free(void *addr)
560 {
561         if (addr) {
562                 size_t *ptr = (size_t *)addr;
563                 ptr--;
564                 size_t len = *ptr;
565                 Mem::Page *page = Mem::kvirt_to_page(addr);
566                 Mem::PageAlloc::free(page, len);
567         }
568 }
569
570 void *operator new(size_t len)
571 {
572         return malloc(len);
573 }
574
575 void *operator new[](size_t len)
576 {
577         return malloc(len);
578 }
579
580 void operator delete(void *addr)
581 {
582         free(addr);
583 }
584
585 void operator delete[](void *addr)
586 {
587         free(addr);
588 }
589
590 extern "C" void __cxa_pure_virtual()
591 {
592         BUG();
593 }
594
595 void abort()
596 {
597         in_fault++;
598         printf("abort() called in kernel\n");
599         __builtin_trap();
600 }