532 lines
12 KiB
C
532 lines
12 KiB
C
|
// Projectname: SWEB
|
||
|
// Simple operating system for educational purposes
|
||
|
//
|
||
|
// Copyright (C) 2005 Andreas Niederl
|
||
|
//
|
||
|
// This program is free software; you can redistribute it and/or
|
||
|
// modify it under the terms of the GNU General Public License
|
||
|
// as published by the Free Software Foundation; either version 2
|
||
|
// of the License, or (at your option) any later version.
|
||
|
//
|
||
|
// This program is distributed in the hope that it will be useful,
|
||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
// GNU General Public License for more details.
|
||
|
//
|
||
|
// You should have received a copy of the GNU General Public License
|
||
|
// along with this program; if not, write to the Free Software
|
||
|
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||
|
|
||
|
#include "unistd.h"
|
||
|
#include "stdio.h"
|
||
|
#include "stdlib.h"
|
||
|
#include "stdarg.h"
|
||
|
#include "ctype.h"
|
||
|
|
||
|
|
||
|
#define unlikely(arg) arg
|
||
|
|
||
|
// The following functions defined before scanf are taken from the linux
|
||
|
// kernel (v.2.6.13) file lib/vsprintf.c
|
||
|
|
||
|
/**
|
||
|
* Maximum integer value
|
||
|
*
|
||
|
*/
|
||
|
#define INT_MAX ((int)(~0U>>1))
|
||
|
|
||
|
|
||
|
/**
|
||
|
* simple_strtoul - convert a string to an unsigned long
|
||
|
* @cp: The start of the string
|
||
|
* @endp: A pointer to the end of the parsed string will be placed here
|
||
|
* @base: The number base to use
|
||
|
*/
|
||
|
unsigned long simple_strtoul(const char *cp,char **endp,unsigned int base)
|
||
|
{
|
||
|
unsigned long result = 0,value;
|
||
|
|
||
|
if (!base) {
|
||
|
base = 10;
|
||
|
if (*cp == '0') {
|
||
|
base = 8;
|
||
|
cp++;
|
||
|
if ((toupper(*cp) == 'X') && isxdigit(cp[1])) {
|
||
|
cp++;
|
||
|
base = 16;
|
||
|
}
|
||
|
}
|
||
|
} else if (base == 16) {
|
||
|
if (cp[0] == '0' && toupper(cp[1]) == 'X')
|
||
|
cp += 2;
|
||
|
}
|
||
|
while (isxdigit(*cp) &&
|
||
|
(value = isdigit(*cp) ? *cp-'0' : toupper(*cp)-'A'+10) < base) {
|
||
|
result = result*base + value;
|
||
|
cp++;
|
||
|
}
|
||
|
if (endp)
|
||
|
*endp = (char *)cp;
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* simple_strtol - convert a string to a signed long
|
||
|
* @cp: The start of the string
|
||
|
* @endp: A pointer to the end of the parsed string will be placed here
|
||
|
* @base: The number base to use
|
||
|
*/
|
||
|
long simple_strtol(const char *cp,char **endp,unsigned int base)
|
||
|
{
|
||
|
if(*cp=='-')
|
||
|
return -simple_strtoul(cp+1,endp,base);
|
||
|
return simple_strtoul(cp,endp,base);
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* simple_strtoull - convert a string to an unsigned long long
|
||
|
* @cp: The start of the string
|
||
|
* @endp: A pointer to the end of the parsed string will be placed here
|
||
|
* @base: The number base to use
|
||
|
*/
|
||
|
unsigned long long simple_strtoull(const char *cp,char **endp,unsigned int base)
|
||
|
{
|
||
|
unsigned long long result = 0,value;
|
||
|
|
||
|
if (!base) {
|
||
|
base = 10;
|
||
|
if (*cp == '0') {
|
||
|
base = 8;
|
||
|
cp++;
|
||
|
if ((toupper(*cp) == 'X') && isxdigit(cp[1])) {
|
||
|
cp++;
|
||
|
base = 16;
|
||
|
}
|
||
|
}
|
||
|
} else if (base == 16) {
|
||
|
if (cp[0] == '0' && toupper(cp[1]) == 'X')
|
||
|
cp += 2;
|
||
|
}
|
||
|
while (isxdigit(*cp) && (value = isdigit(*cp) ? *cp-'0' : (islower(*cp)
|
||
|
? toupper(*cp) : *cp)-'A'+10) < base) {
|
||
|
result = result*base + value;
|
||
|
cp++;
|
||
|
}
|
||
|
if (endp)
|
||
|
*endp = (char *)cp;
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* simple_strtoll - convert a string to a signed long long
|
||
|
* @cp: The start of the string
|
||
|
* @endp: A pointer to the end of the parsed string will be placed here
|
||
|
* @base: The number base to use
|
||
|
*/
|
||
|
long long simple_strtoll(const char *cp,char **endp,unsigned int base)
|
||
|
{
|
||
|
if(*cp=='-')
|
||
|
return -simple_strtoull(cp+1,endp,base);
|
||
|
return simple_strtoull(cp,endp,base);
|
||
|
}
|
||
|
|
||
|
static int skip_atoi(const char **s)
|
||
|
{
|
||
|
int i=0;
|
||
|
|
||
|
while (isdigit(**s))
|
||
|
i = i*10 + *((*s)++) - '0';
|
||
|
return i;
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* vsscanf - Unformat a buffer into a list of arguments
|
||
|
* @buf: input buffer
|
||
|
* @fmt: format of buffer
|
||
|
* @args: arguments
|
||
|
*/
|
||
|
int vsscanf(const char * buf, const char * fmt, va_list args)
|
||
|
{
|
||
|
const char *str = buf;
|
||
|
char *next;
|
||
|
char digit;
|
||
|
int num = 0;
|
||
|
int qualifier;
|
||
|
int base;
|
||
|
int field_width;
|
||
|
int is_sign = 0;
|
||
|
|
||
|
while(*fmt && *str) {
|
||
|
/* skip any white space in format */
|
||
|
/* white space in format matchs any amount of
|
||
|
* white space, including none, in the input.
|
||
|
*/
|
||
|
if (isspace(*fmt)) {
|
||
|
while (isspace(*fmt))
|
||
|
++fmt;
|
||
|
while (isspace(*str))
|
||
|
++str;
|
||
|
}
|
||
|
|
||
|
/* anything that is not a conversion must match exactly */
|
||
|
if (*fmt != '%' && *fmt) {
|
||
|
if (*fmt++ != *str++)
|
||
|
break;
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if (!*fmt)
|
||
|
break;
|
||
|
++fmt;
|
||
|
|
||
|
/* skip this conversion.
|
||
|
* advance both strings to next white space
|
||
|
*/
|
||
|
if (*fmt == '*') {
|
||
|
while (!isspace(*fmt) && *fmt)
|
||
|
fmt++;
|
||
|
while (!isspace(*str) && *str)
|
||
|
str++;
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
/* get field width */
|
||
|
field_width = -1;
|
||
|
if (isdigit(*fmt))
|
||
|
field_width = skip_atoi(&fmt);
|
||
|
|
||
|
/* get conversion qualifier */
|
||
|
qualifier = -1;
|
||
|
if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L' ||
|
||
|
*fmt == 'Z' || *fmt == 'z') {
|
||
|
qualifier = *fmt++;
|
||
|
if (unlikely(qualifier == *fmt)) {
|
||
|
if (qualifier == 'h') {
|
||
|
qualifier = 'H';
|
||
|
fmt++;
|
||
|
} else if (qualifier == 'l') {
|
||
|
qualifier = 'L';
|
||
|
fmt++;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
base = 10;
|
||
|
is_sign = 0;
|
||
|
|
||
|
if (!*fmt || !*str)
|
||
|
break;
|
||
|
|
||
|
switch(*fmt++) {
|
||
|
case 'c':
|
||
|
{
|
||
|
char *s = (char *) va_arg(args,char*);
|
||
|
if (field_width == -1)
|
||
|
field_width = 1;
|
||
|
do {
|
||
|
*s++ = *str++;
|
||
|
} while (--field_width > 0 && *str);
|
||
|
num++;
|
||
|
}
|
||
|
continue;
|
||
|
case 's':
|
||
|
{
|
||
|
char *s = (char *) va_arg(args, char *);
|
||
|
if(field_width == -1)
|
||
|
field_width = INT_MAX;
|
||
|
/* first, skip leading white space in buffer */
|
||
|
while (isspace(*str))
|
||
|
str++;
|
||
|
|
||
|
/* now copy until next white space */
|
||
|
while (*str && !isspace(*str) && field_width--) {
|
||
|
*s++ = *str++;
|
||
|
}
|
||
|
*s = '\0';
|
||
|
num++;
|
||
|
}
|
||
|
continue;
|
||
|
case 'n':
|
||
|
/* return number of characters read so far */
|
||
|
{
|
||
|
int *i = (int *)va_arg(args,int*);
|
||
|
*i = str - buf;
|
||
|
}
|
||
|
continue;
|
||
|
case 'o':
|
||
|
base = 8;
|
||
|
break;
|
||
|
case 'x':
|
||
|
case 'X':
|
||
|
base = 16;
|
||
|
break;
|
||
|
case 'i':
|
||
|
base = 0;
|
||
|
// fall through
|
||
|
case 'd':
|
||
|
is_sign = 1;
|
||
|
case 'u':
|
||
|
break;
|
||
|
case '%':
|
||
|
/* looking for '%' in str */
|
||
|
if (*str++ != '%')
|
||
|
return num;
|
||
|
continue;
|
||
|
default:
|
||
|
/* invalid format; stop here */
|
||
|
return num;
|
||
|
}
|
||
|
|
||
|
/* have some sort of integer conversion.
|
||
|
* first, skip white space in buffer.
|
||
|
*/
|
||
|
while (isspace(*str))
|
||
|
str++;
|
||
|
|
||
|
digit = *str;
|
||
|
if (is_sign && digit == '-')
|
||
|
digit = *(str + 1);
|
||
|
|
||
|
if (!digit
|
||
|
|| (base == 16 && !isxdigit(digit))
|
||
|
|| (base == 10 && !isdigit(digit))
|
||
|
|| (base == 8 && (!isdigit(digit) || digit > '7'))
|
||
|
|| (base == 0 && !isdigit(digit)))
|
||
|
break;
|
||
|
|
||
|
switch(qualifier) {
|
||
|
case 'H': /* that's 'hh' in format */
|
||
|
if (is_sign) {
|
||
|
signed char *s = (signed char *) va_arg(args,signed char *);
|
||
|
*s = (signed char) simple_strtol(str,&next,base);
|
||
|
} else {
|
||
|
unsigned char *s = (unsigned char *) va_arg(args, unsigned char *);
|
||
|
*s = (unsigned char) simple_strtoul(str, &next, base);
|
||
|
}
|
||
|
break;
|
||
|
case 'h':
|
||
|
if (is_sign) {
|
||
|
short *s = (short *) va_arg(args,short *);
|
||
|
*s = (short) simple_strtol(str,&next,base);
|
||
|
} else {
|
||
|
unsigned short *s = (unsigned short *) va_arg(args, unsigned short *);
|
||
|
*s = (unsigned short) simple_strtoul(str, &next, base);
|
||
|
}
|
||
|
break;
|
||
|
case 'l':
|
||
|
if (is_sign) {
|
||
|
long *l = (long *) va_arg(args,long *);
|
||
|
*l = simple_strtol(str,&next,base);
|
||
|
} else {
|
||
|
unsigned long *l = (unsigned long*) va_arg(args,unsigned long*);
|
||
|
*l = simple_strtoul(str,&next,base);
|
||
|
}
|
||
|
break;
|
||
|
case 'L':
|
||
|
if (is_sign) {
|
||
|
long long *l = (long long*) va_arg(args,long long *);
|
||
|
*l = simple_strtoll(str,&next,base);
|
||
|
} else {
|
||
|
unsigned long long *l = (unsigned long long*) va_arg(args,unsigned long long*);
|
||
|
*l = simple_strtoull(str,&next,base);
|
||
|
}
|
||
|
break;
|
||
|
case 'Z':
|
||
|
case 'z':
|
||
|
{
|
||
|
size_t *s = (size_t*) va_arg(args,size_t*);
|
||
|
*s = (size_t) simple_strtoul(str,&next,base);
|
||
|
}
|
||
|
break;
|
||
|
default:
|
||
|
if (is_sign) {
|
||
|
int *i = (int *) va_arg(args, int*);
|
||
|
*i = (int) simple_strtol(str,&next,base);
|
||
|
} else {
|
||
|
unsigned int *i = (unsigned int*) va_arg(args, unsigned int*);
|
||
|
*i = (unsigned int) simple_strtoul(str,&next,base);
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
num++;
|
||
|
|
||
|
if (!next)
|
||
|
break;
|
||
|
str = next;
|
||
|
}
|
||
|
return num;
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Size of the static input buffer for scanf
|
||
|
*
|
||
|
*/
|
||
|
#define INPUT_BUFFER_SIZE 256
|
||
|
|
||
|
/**
|
||
|
* Reads input from stdin according to the given format and
|
||
|
* assigns read values to the given variables.
|
||
|
* A detailed description of the format is given in the
|
||
|
* 'Linux Programmer's Manual'.
|
||
|
*
|
||
|
* @param format A string containing the input format, followed by an\
|
||
|
argument list of variables for assignment
|
||
|
* @return The number of input items assigned, zero indicates that no input\
|
||
|
items were assigned while input was available, EOF if failure (e.g.\
|
||
|
end-of-file) occurs before any items have been read
|
||
|
*
|
||
|
*/
|
||
|
int scanf(const char *fmt, ...)
|
||
|
{
|
||
|
va_list args;
|
||
|
int return_val = 0;
|
||
|
|
||
|
char input_buffer_array[INPUT_BUFFER_SIZE];
|
||
|
char *buffer = (char*) &input_buffer_array;
|
||
|
|
||
|
if(gets(buffer, INPUT_BUFFER_SIZE) == NULL)
|
||
|
return return_val;
|
||
|
|
||
|
va_start(args, fmt);
|
||
|
|
||
|
return_val = vsscanf(buffer, fmt, args);
|
||
|
|
||
|
va_end(args);
|
||
|
|
||
|
return return_val;
|
||
|
}
|
||
|
|
||
|
|
||
|
int sscanf(const char *buffer, const char *fmt, ...)
|
||
|
{
|
||
|
va_list args;
|
||
|
int return_val = 0;
|
||
|
|
||
|
va_start(args, fmt);
|
||
|
|
||
|
return_val = vsscanf(buffer, fmt, args);
|
||
|
|
||
|
va_end(args);
|
||
|
|
||
|
return return_val;
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Reads the next character from stdin.
|
||
|
* The read value will be returned as unsigned char cast to an int
|
||
|
*
|
||
|
* @return The read character on success, EOF otherwise and errno is set\
|
||
|
appropriately
|
||
|
*
|
||
|
*/
|
||
|
int getchar()
|
||
|
{
|
||
|
char character = 0;
|
||
|
int characters_read = read(STDIN_FILENO, (void*) &character, 1);
|
||
|
|
||
|
if(!characters_read || (characters_read == -1))
|
||
|
return EOF;
|
||
|
|
||
|
return (int) character;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Reads a line from stdin and stores it in the string pointed to by the
|
||
|
* argument.
|
||
|
* Reading is terminated by a newline, EOF which are both replaced by '\0' or
|
||
|
* when the buffer is full.
|
||
|
*
|
||
|
* @param input_buffer The buffer where the input is stored
|
||
|
* @param buffer_size The size of the buffer
|
||
|
* @return A pointer to the input_string on success, NULL otherwise
|
||
|
*
|
||
|
*/
|
||
|
char *gets(char *input_buffer, size_t buffer_size)
|
||
|
{
|
||
|
unsigned int counter = 0;
|
||
|
if (!buffer_size)
|
||
|
return input_buffer;
|
||
|
|
||
|
int cchar = 0;
|
||
|
|
||
|
while((cchar = getchar()) != EOF)
|
||
|
{
|
||
|
|
||
|
if(cchar == '\r' || cchar == '\n' || (counter + 1) >= buffer_size) // there must be one space left for the \0 at end
|
||
|
{
|
||
|
*(input_buffer + counter) = '\0';
|
||
|
break;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
*(input_buffer + counter) = (char)cchar;
|
||
|
}
|
||
|
|
||
|
if (cchar == '\b')
|
||
|
{
|
||
|
if (counter > 0)
|
||
|
counter--;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
counter++;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
return input_buffer;
|
||
|
}
|
||
|
|
||
|
|
||
|
char *fgets(char *str, int num, int fd)
|
||
|
{
|
||
|
unsigned int counter = 0;
|
||
|
ssize_t readCount = 0;
|
||
|
char buffer[128];
|
||
|
off_t oldOffset = lseek(fd, 0, SEEK_CUR);
|
||
|
|
||
|
if (!num)
|
||
|
return str;
|
||
|
|
||
|
do
|
||
|
{
|
||
|
readCount = read(fd, buffer, 128);
|
||
|
for (ssize_t i = 0; i < readCount; i++)
|
||
|
{
|
||
|
if (buffer[i] == '\r' || buffer[i] == '\n' || (counter+1) >= num)
|
||
|
{
|
||
|
*(str + counter) = '\0';
|
||
|
lseek(fd, oldOffset+counter+1, SEEK_SET);
|
||
|
return str;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
*(str + counter) = buffer[i];
|
||
|
}
|
||
|
|
||
|
if (buffer[i] == '\b')
|
||
|
{
|
||
|
if (counter > 0)
|
||
|
counter--;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
counter++;
|
||
|
}
|
||
|
}
|
||
|
} while (readCount > 0);
|
||
|
|
||
|
if (num - counter)
|
||
|
str[counter] = '\0';
|
||
|
|
||
|
if (counter == 0)
|
||
|
return 0;
|
||
|
|
||
|
return str;
|
||
|
}
|