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