gctf2023/pwn/flipper/dist/userspace/libc/src/scanf.c

532 lines
12 KiB
C
Raw Normal View History

2023-11-24 18:11:34 +00:00
// 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;
}