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