267 lines
8.8 KiB
C
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;
|
|
}
|
|
|
|
|