Initial revision
This commit is contained in:
267
kernel/klibc.c
Normal file
267
kernel/klibc.c
Normal file
@@ -0,0 +1,267 @@
|
||||
/* This file contains simplified versions of the standard libary functions for
|
||||
* use with the kernel. This way the kernel sources remain separate from user
|
||||
* sources and can easily be verified. Note that the functionality provided
|
||||
* can be slightly different.
|
||||
* March 2005, Jorrit N. Herder.
|
||||
* Entrypoints into this file:
|
||||
* katoi: convert string to integer
|
||||
* kmemcpy: copy n bytes from pointer p1 to pointer p2
|
||||
* kmemset: set n bytes to c starting at pointer p
|
||||
* kprintf: printf for the kernel (see working below)
|
||||
* kstrcmp: lexicographical comparison of two strings
|
||||
* kstrlen: get number of non-null characters in string
|
||||
* kstrncpy: copy string and pad or copy up to n chars
|
||||
* kstrtoulb: convert string to unsigned long value
|
||||
*
|
||||
* This file contains the routines that take care of kernel messages, i.e.,
|
||||
* diagnostic output within the kernel. Kernel messages are not directly
|
||||
* displayed on the console, because this must be done by the TTY driver.
|
||||
* Instead, the kernel accumulates characters in a buffer and notifies the
|
||||
* TTY driver when a new message is ready.
|
||||
*/
|
||||
|
||||
#include "kernel.h"
|
||||
#include <minix/com.h> /* need TTY process number */
|
||||
|
||||
#define isdigit(c) ((unsigned) ((c) - '0') < (unsigned) 10)
|
||||
#define END_OF_KMESS -1
|
||||
FORWARD _PROTOTYPE(void kputc, (int c));
|
||||
|
||||
|
||||
/*=========================================================================*
|
||||
* katoi *
|
||||
*=========================================================================*/
|
||||
PUBLIC int katoi(register const char *s)
|
||||
{
|
||||
int value = 0; /* default value */
|
||||
int sign = 1; /* assume positive */
|
||||
|
||||
while(*s == ' ') s++; /* skip spaces */
|
||||
if (*s == '-') { sign = -1; s++; } /* detect sign */
|
||||
while(isdigit(*s)) /* get integer */
|
||||
value = value*10 + (*s++) -'0';
|
||||
|
||||
return(sign * value); /* return result */
|
||||
}
|
||||
|
||||
|
||||
/*=========================================================================*
|
||||
* kmemcpy *
|
||||
*=========================================================================*/
|
||||
PUBLIC void *kmemcpy(void *s1, const void *s2, register size_t n)
|
||||
{
|
||||
register char *p1 = s1;
|
||||
register const char *p2 = s2;
|
||||
|
||||
while (n-- > 0)
|
||||
*p1++ = *p2++;
|
||||
return s1;
|
||||
}
|
||||
|
||||
|
||||
/*=========================================================================*
|
||||
* kmemset *
|
||||
*=========================================================================*/
|
||||
PUBLIC void *kmemset(void *s, register int c, register size_t n)
|
||||
{
|
||||
register char *s1 = s;
|
||||
if (n++>0) { /* optimized for speed */
|
||||
while (--n > 0)
|
||||
*s1++ = c;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
/*===========================================================================*
|
||||
* kprintf *
|
||||
*===========================================================================*/
|
||||
PUBLIC void kprintf(fmt, arg)
|
||||
const char *fmt; /* format string to be printed */
|
||||
karg_t arg; /* argument for format string */
|
||||
{
|
||||
int c; /* next character in fmt */
|
||||
unsigned long u; /* hold number argument */
|
||||
int base; /* base of number arg */
|
||||
int negative = 0; /* print minus sign */
|
||||
static char x2c[] = "0123456789ABCDEF"; /* nr conversion table */
|
||||
char ascii[8 * sizeof(long) / 3 + 2]; /* string for ascii number */
|
||||
char *s = NULL; /* string to be printed */
|
||||
|
||||
while((c=*fmt++) != 0) {
|
||||
|
||||
if (c == '%') { /* expect format '%key' */
|
||||
switch(c = *fmt++) { /* determine what to do */
|
||||
|
||||
/* Known keys are %d, %u, %x, %s, and %%. This is easily extended
|
||||
* with number types like %b and %o by providing a different base.
|
||||
* Number type keys don't set a string to 's', but use the general
|
||||
* conversion after the switch statement.
|
||||
*/
|
||||
case 'd': /* output decimal */
|
||||
u = arg < 0 ? -arg : arg;
|
||||
if (arg < 0) negative = 1;
|
||||
base = 10;
|
||||
break;
|
||||
case 'u': /* output unsigned long */
|
||||
u = (unsigned long) arg;
|
||||
base = 10;
|
||||
break;
|
||||
case 'x': /* output hexadecimal */
|
||||
u = (unsigned long) arg;
|
||||
base = 0x10;
|
||||
break;
|
||||
case 's': /* output string */
|
||||
if ((s=(char *)arg) == NULL)
|
||||
s = "(null)";
|
||||
break;
|
||||
case '%': /* output percent */
|
||||
s = "%";
|
||||
break;
|
||||
|
||||
/* Unrecognized key. */
|
||||
default: /* echo back %key */
|
||||
s = "%?";
|
||||
s[1] = c; /* set unknown key */
|
||||
}
|
||||
|
||||
/* Assume a number if no string is set. Convert to ascii. */
|
||||
if (s == NULL) {
|
||||
s = ascii + sizeof(ascii)-1;
|
||||
*s = 0;
|
||||
do { *--s = x2c[(u % base)]; } /* work backwards */
|
||||
while ((u /= base) > 0);
|
||||
}
|
||||
|
||||
/* This is where the actual output for format "%key" is done. */
|
||||
if (negative) kputc('-'); /* print sign if negative */
|
||||
while(*s != 0) { kputc(*s++); } /* print string/ number */
|
||||
}
|
||||
else {
|
||||
kputc(c); /* print and continue */
|
||||
}
|
||||
}
|
||||
kputc(END_OF_KMESS); /* terminate output */
|
||||
}
|
||||
|
||||
|
||||
/*===========================================================================*
|
||||
* kputc *
|
||||
*===========================================================================*/
|
||||
PRIVATE void kputc(c)
|
||||
int c; /* character to append */
|
||||
{
|
||||
/* Accumulate a single character for a kernel message. Send a notification
|
||||
* the to TTY driver if the buffer if a END_OF_KMESS is encountered.
|
||||
*/
|
||||
if (c != END_OF_KMESS) {
|
||||
kmess.km_buf[kmess.km_next] = c; /* put normal char in buffer */
|
||||
if (kmess.km_size < KMESS_BUF_SIZE)
|
||||
kmess.km_size += 1;
|
||||
kmess.km_next = (kmess.km_next + 1) % KMESS_BUF_SIZE;
|
||||
} else {
|
||||
notify(TTY, NEW_KMESS); /* let TTY display the message */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*=========================================================================*
|
||||
* kstrlen *
|
||||
*=========================================================================*/
|
||||
PUBLIC size_t kstrlen(const char *org)
|
||||
{
|
||||
register const char *s = org;
|
||||
while (*s++)
|
||||
/* EMPTY */ ;
|
||||
return --s - org;
|
||||
}
|
||||
|
||||
|
||||
/*=========================================================================*
|
||||
* kstrcmp *
|
||||
*=========================================================================*/
|
||||
int kstrcmp(register const char *s1, register const char *s2)
|
||||
{
|
||||
while (*s1 == *s2++) {
|
||||
if (*s1++ == '\0') return 0;
|
||||
}
|
||||
if (*s1 == '\0') return -1;
|
||||
if (*--s2 == '\0') return 1;
|
||||
return (unsigned char) *s1 - (unsigned char) *s2;
|
||||
}
|
||||
|
||||
|
||||
/*=========================================================================*
|
||||
* kstrncmp *
|
||||
*=========================================================================*/
|
||||
PUBLIC int kstrncmp(register const char *s1, register const char *s2, register size_t n)
|
||||
{
|
||||
while (n > 0 && *s1 == *s2++) {
|
||||
if (*s1++ == '\0') return 0;
|
||||
n--;
|
||||
}
|
||||
if (n > 0) {
|
||||
if (*s1 == '\0') return -1;
|
||||
if (*--s2 == '\0') return 1;
|
||||
return (unsigned char) *s1 - (unsigned char) *s2;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*=========================================================================*
|
||||
* kstrncpy *
|
||||
*=========================================================================*/
|
||||
PUBLIC char *kstrncpy(char *ret, register const char *s2, register size_t n)
|
||||
{
|
||||
register char *s1 = ret;
|
||||
while((n-- > 0) && (*s1++ = *s2++)) /* copy up to n chars */
|
||||
/* EMPTY */ ;
|
||||
while(n-- > 0) /* possibly pad target */
|
||||
*s1++ = '\0';
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/*=========================================================================*
|
||||
* kstrtoul *
|
||||
*=========================================================================*/
|
||||
PUBLIC unsigned long kstrtoul(strptr, endptr, base)
|
||||
const char *strptr; /* pointer to string to be parsed */
|
||||
char ** const endptr; /* store pointer to end here */
|
||||
int base;
|
||||
{
|
||||
/* A simplified version of strtoul() for the kernel to prevent including the
|
||||
* one in the ASNI library. No whitespaces are skipped, the numeric value is
|
||||
* expected at the start of 'string'.
|
||||
*/
|
||||
register unsigned long val = 0;
|
||||
register int c;
|
||||
register unsigned int v;
|
||||
int overflow = 0;
|
||||
|
||||
/* Get rid of 0x or 0X for hexidecimal values. */
|
||||
if (base==16 && *strptr=='0' && (*++strptr=='x' || *strptr=='X'))
|
||||
strptr++;
|
||||
|
||||
/* Now parse the actual unsigned long number. */
|
||||
for (;;) {
|
||||
c = *strptr;
|
||||
if ('0' <= c && c <= '9') v = c - '0';
|
||||
else if ('a' <= c && c <= 'z') v = c - 'a' + 0xa;
|
||||
else if ('A' <= c && c <= 'Z') v = c - 'A' + 0xA;
|
||||
else break; /* end of number */
|
||||
if (v >= base) break; /* end of number */
|
||||
if (val > (ULONG_MAX - v) / base) overflow = 1;
|
||||
val = (val*base) + v;
|
||||
strptr++;
|
||||
}
|
||||
|
||||
/* Tell caller where parsing ended unless a NULL pointer was passed. */
|
||||
if (endptr) *endptr = (char *) strptr;
|
||||
|
||||
/* Done, return parsed value or maximum value on overflow. */
|
||||
return (overflow) ? ULONG_MAX : val;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user