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