minix/kernel/klibc.c
2005-04-21 14:53:53 +00:00

267 lines
8.8 KiB
C

/* 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;
}