From 7f34db4d9cb2b06850d9c293e6a4205df0f2f311 Mon Sep 17 00:00:00 2001 From: rms Date: Tue, 5 Jun 2012 12:24:21 -0700 Subject: [PATCH] Add all my old code from iBoot-574.4's libc. Based on libsaio from the Darwin bootloader. --- include/assert.h | 36 +-- include/cpio.h | 51 ++-- include/ctype.h | 48 ++-- include/inttypes.h | 7 + include/limits.h | 27 ++ include/pwd.h | 44 ++-- include/stdarg.h | 133 ++++++++++ include/stdbool.h | 19 +- include/stddef.h | 13 +- include/stdint.h | 13 + include/string.h | 13 + include/stropts.h | 128 +++++----- include/time.h | 60 ++--- include/unistd.h | 14 +- src/__ashldi3.c | 23 ++ src/__clzsi2.c | 36 +++ src/__ctzsi2.c | 42 ++++ src/__divdi3.c | 29 +++ src/__divsi3.c | 29 +++ src/__lshrdi3.c | 23 ++ src/__moddi3.c | 29 +++ src/__modsi3.c | 29 +++ src/__udivdi3.c | 13 + src/__udivmoddi4.c | 33 +++ src/__udivmodsi4.c | 32 +++ src/__udivsi3.c | 13 + src/__umoddi3.c | 16 ++ src/__umodsi3.c | 16 ++ src/atoi.c | 3 + src/atol.c | 3 + src/atoll.c | 3 + src/atox.c | 16 ++ src/memccpy.c | 23 ++ src/memchr.c | 19 ++ src/memmem.c | 52 ++++ src/memmove.c | 37 +++ src/memops.c | 38 +++ src/memrchr.c | 19 ++ src/memswap.c | 24 ++ src/snprintf.c | 18 ++ src/sscanf.c | 17 ++ src/{string.c => strlen.c} | 20 +- src/strnlen.c | 18 ++ src/strntoimax.c | 15 ++ src/strntoumax.c | 77 ++++++ src/strtol.c | 3 + src/strtoll.c | 3 + src/strtoul.c | 3 + src/strtoull.c | 3 + src/strtox.c | 16 ++ src/vsnprintf.c | 497 +++++++++++++++++++++++++++++++++++++ src/vsscanf.c | 405 ++++++++++++++++++++++++++++++ 52 files changed, 2101 insertions(+), 200 deletions(-) create mode 100644 include/inttypes.h create mode 100644 include/limits.h create mode 100644 include/stdarg.h create mode 100644 include/stdint.h create mode 100644 include/string.h create mode 100644 src/__ashldi3.c create mode 100644 src/__clzsi2.c create mode 100644 src/__ctzsi2.c create mode 100644 src/__divdi3.c create mode 100644 src/__divsi3.c create mode 100644 src/__lshrdi3.c create mode 100644 src/__moddi3.c create mode 100644 src/__modsi3.c create mode 100644 src/__udivdi3.c create mode 100644 src/__udivmoddi4.c create mode 100644 src/__udivmodsi4.c create mode 100644 src/__udivsi3.c create mode 100644 src/__umoddi3.c create mode 100644 src/__umodsi3.c create mode 100644 src/atoi.c create mode 100644 src/atol.c create mode 100644 src/atoll.c create mode 100644 src/atox.c create mode 100644 src/memccpy.c create mode 100644 src/memchr.c create mode 100644 src/memmem.c create mode 100644 src/memmove.c create mode 100644 src/memops.c create mode 100644 src/memrchr.c create mode 100644 src/memswap.c create mode 100644 src/snprintf.c create mode 100644 src/sscanf.c rename src/{string.c => strlen.c} (93%) create mode 100644 src/strnlen.c create mode 100644 src/strntoimax.c create mode 100644 src/strntoumax.c create mode 100644 src/strtol.c create mode 100644 src/strtoll.c create mode 100644 src/strtoul.c create mode 100644 src/strtoull.c create mode 100644 src/strtox.c create mode 100644 src/vsnprintf.c create mode 100644 src/vsscanf.c diff --git a/include/assert.h b/include/assert.h index 6befa21..8b03cae 100644 --- a/include/assert.h +++ b/include/assert.h @@ -1,15 +1,21 @@ -#ifdef NDEBUG - -#include "stdlib.h" -#include "stdio.h" - -#define assert(x) \ - if (!x) \ - { \ - fprintf(stderr, "\"%s\" failed at %s:%d\n", #x, __FILE__, __LINE__); \ - abort(); \ - } - -#else -#define assert(ignore) ((void) 0) -#endif + +#ifndef _CASSERT_H_ +#define _CASSERT_H_ + +#ifdef NDEBUG + +#include "stdlib.h" +#include "stdio.h" + +#define assert(x) \ + if (!x) \ + { \ + fprintf(stderr, "\"%s\" failed at %s:%d\n", #x, __FILE__, __LINE__); \ + abort(); \ + } + +#else +#define assert(ignore) ((void) 0) +#endif + +#endif \ No newline at end of file diff --git a/include/cpio.h b/include/cpio.h index 2431d4f..1c8de48 100644 --- a/include/cpio.h +++ b/include/cpio.h @@ -1,24 +1,27 @@ -#pragma once - -#define C_IRUSR 0000400 -#define C_IWUSR 0000200 -#define C_IXUSR 0000100 -#define C_IRGRP 0000040 -#define C_IWGRP 0000020 -#define C_IXGRP 0000010 -#define C_IROTH 0000004 -#define C_IWOTH 0000002 -#define C_IXOTH 0000001 -#define C_ISUID 0004000 -#define C_ISGID 0002000 -#define C_ISVTX 0001000 -#define C_ISDIR 0040000 -#define C_ISFIFO 0010000 -#define C_ISREG 0100000 -#define C_ISBLK 0060000 -#define C_ISCHR 0020000 -#define C_ISCTG 0110000 -#define C_ISLNK 0120000 -#define C_ISSOCK 0140000 - -#define MAGIC "070707" +#ifndef _CPIO_H_ +#define _CPIO_H_ + +#define C_IRUSR 0000400 +#define C_IWUSR 0000200 +#define C_IXUSR 0000100 +#define C_IRGRP 0000040 +#define C_IWGRP 0000020 +#define C_IXGRP 0000010 +#define C_IROTH 0000004 +#define C_IWOTH 0000002 +#define C_IXOTH 0000001 +#define C_ISUID 0004000 +#define C_ISGID 0002000 +#define C_ISVTX 0001000 +#define C_ISDIR 0040000 +#define C_ISFIFO 0010000 +#define C_ISREG 0100000 +#define C_ISBLK 0060000 +#define C_ISCHR 0020000 +#define C_ISCTG 0110000 +#define C_ISLNK 0120000 +#define C_ISSOCK 0140000 + +#define MAGIC "070707" + +#endif \ No newline at end of file diff --git a/include/ctype.h b/include/ctype.h index 117a08e..74ed6d6 100644 --- a/include/ctype.h +++ b/include/ctype.h @@ -1,22 +1,26 @@ -#pragma once - -#define _toupper(x) -#define _tolower(x) - -//extern int isalnum(int c); -extern int isalpha(int c); -//extern int isascii(int c); -//extern int isblank(int c); -extern int iscntrl(int c); -//extern int isdigit(int c); -extern int isgraph(int c); -extern int islower(int c); -extern int isprint(int c); -//extern int ispunct(int c); -extern int isspace(int c); -extern int isupper(int c); -//extern int isxdigit(int c); -//extern int toascii(int c); -//extern int tolower(int c); -extern int toupper(int c); - + +#ifndef _CTYPE_H_ +#define _CTYPE_H_ + +#define _toupper(x) +#define _tolower(x) + +//extern int isalnum(int c); +extern int isalpha(int c); +//extern int isascii(int c); +//extern int isblank(int c); +extern int iscntrl(int c); +//extern int isdigit(int c); +extern int isgraph(int c); +extern int islower(int c); +extern int isprint(int c); +//extern int ispunct(int c); +extern int isspace(int c); +extern int isupper(int c); +//extern int isxdigit(int c); +//extern int toascii(int c); +//extern int tolower(int c); +extern int toupper(int c); + + +#endif \ No newline at end of file diff --git a/include/inttypes.h b/include/inttypes.h new file mode 100644 index 0000000..edef84c --- /dev/null +++ b/include/inttypes.h @@ -0,0 +1,7 @@ +#ifndef _INTTYPES_H_ +#define _INTTYPES_H_ + +typedef unsigned int uintmax_t; +typedef int intmax_t; + +#endif \ No newline at end of file diff --git a/include/limits.h b/include/limits.h new file mode 100644 index 0000000..ab2d44e --- /dev/null +++ b/include/limits.h @@ -0,0 +1,27 @@ +#ifndef _LIMITS_H_ +#define _LIMITS_H_ + +#define LIMITS_H 1 +#define CHAR_BIT 8 +#define SCHAR_MIN (-128) +#define SCHAR_MAX 127 +#define UCHAR_MAX 255 +#define CHAR_MIN SCHAR_MIN +#define CHAR_MAX SCHAR_MAX +#define SHRT_MIN (-32768) +#define SHRT_MAX 32767 +#define USHRT_MAX 65535 +#define INT_MIN (-INT_MAX - 1) +#define INT_MAX 2147483647 +#define UINT_MAX 4294967295U +#define INT_MAX 2147483647 +#define INT_MIN (-INT_MAX - 1) +#define UINT_MAX 4294967295U +#define LONG_MAX 2147483647 +#define LONG_MIN (-LONG_MAX - 1L) +#define ULONG_MAX 4294967295UL +#define LLONG_MAX 9223372036854775807LL +#define LLONG_MIN (-LONG_MAX - 1LL) +#define ULLONG_MAX 18446744073709551615ULL + +#endif \ No newline at end of file diff --git a/include/pwd.h b/include/pwd.h index 39b2fbb..be67fb7 100644 --- a/include/pwd.h +++ b/include/pwd.h @@ -1,20 +1,24 @@ -#pragma once - -struct passwd -{ - char *pw_name; - uid_t pw_uid; - gid_t pw_gid; - char *pw_dir; - char *pw_shell; -}; - -extern void endpwent(void); -extern struct passwd *getpwent(void); -extern struct passwd *getpwnam(const char *name); -extern int getpwnam_r(const char *nam, struct passwd *pwd, char *buffer, - size_t bufsize, struct passwd **result); -extern struct passwd *getpwuid(uid_t uid); -extern int getpwuid_r(uid_t uid, struct passwd *pwd, char *buffer, - size_t bufsize, struct passwd **result); -extern void setpwent(void); + +#ifndef _PWD_H_ +#define _PWD_H_ + +struct passwd +{ + char *pw_name; + uid_t pw_uid; + gid_t pw_gid; + char *pw_dir; + char *pw_shell; +}; + +extern void endpwent(void); +extern struct passwd *getpwent(void); +extern struct passwd *getpwnam(const char *name); +extern int getpwnam_r(const char *nam, struct passwd *pwd, char *buffer, + size_t bufsize, struct passwd **result); +extern struct passwd *getpwuid(uid_t uid); +extern int getpwuid_r(uid_t uid, struct passwd *pwd, char *buffer, + size_t bufsize, struct passwd **result); +extern void setpwent(void); + +#endif \ No newline at end of file diff --git a/include/stdarg.h b/include/stdarg.h new file mode 100644 index 0000000..f178505 --- /dev/null +++ b/include/stdarg.h @@ -0,0 +1,133 @@ +/* Copyright (C) 1989, 1997, 1998, 1999, 2000 Free Software Foundation, Inc. + +This file is part of GCC. + +GCC 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, or (at your option) +any later version. + +GCC 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 GCC; see the file COPYING. If not, write to +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +/* As a special exception, if you include this header file into source + files compiled by GCC, this header file does not by itself cause + the resulting executable to be covered by the GNU General Public + License. This exception does not however invalidate any other + reasons why the executable file might be covered by the GNU General + Public License. */ + +/* + * ISO C Standard: 7.15 Variable arguments + */ + +#ifndef _STDARG_H +#ifndef _ANSI_STDARG_H_ +#ifndef __need___va_list +#define _STDARG_H +#define _ANSI_STDARG_H_ +#endif /* not __need___va_list */ +#undef __need___va_list + +/* Define __gnuc_va_list. */ + +#ifndef __GNUC_VA_LIST +#define __GNUC_VA_LIST +typedef __builtin_va_list __gnuc_va_list; +#endif + +/* Define the standard macros for the user, + if this invocation was from the user program. */ +#ifdef _STDARG_H + +#define va_start(v,l) __builtin_va_start(v,l) +#define va_end(v) __builtin_va_end(v) +#define va_arg(v,l) __builtin_va_arg(v,l) +#if !defined(__STRICT_ANSI__) || __STDC_VERSION__ + 0 >= 199900L +#define va_copy(d,s) __builtin_va_copy(d,s) +#endif +#define __va_copy(d,s) __builtin_va_copy(d,s) + +/* Define va_list, if desired, from __gnuc_va_list. */ +/* We deliberately do not define va_list when called from + stdio.h, because ANSI C says that stdio.h is not supposed to define + va_list. stdio.h needs to have access to that data type, + but must not use that name. It should use the name __gnuc_va_list, + which is safe because it is reserved for the implementation. */ + +#ifdef _HIDDEN_VA_LIST /* On OSF1, this means varargs.h is "half-loaded". */ +#undef _VA_LIST +#endif + +#ifdef _BSD_VA_LIST +#undef _BSD_VA_LIST +#endif + +#if defined(__svr4__) || (defined(_SCO_DS) && !defined(__VA_LIST)) +/* SVR4.2 uses _VA_LIST for an internal alias for va_list, + so we must avoid testing it and setting it here. + SVR4 uses _VA_LIST as a flag in stdarg.h, but we should + have no conflict with that. */ +#ifndef _VA_LIST_ +#define _VA_LIST_ +#ifdef __i860__ +#ifndef _VA_LIST +#define _VA_LIST va_list +#endif +#endif /* __i860__ */ +typedef __gnuc_va_list va_list; +#ifdef _SCO_DS +#define __VA_LIST +#endif +#endif /* _VA_LIST_ */ +#else /* not __svr4__ || _SCO_DS */ + +/* The macro _VA_LIST_ is the same thing used by this file in Ultrix. + But on BSD NET2 we must not test or define or undef it. + (Note that the comments in NET 2's ansi.h + are incorrect for _VA_LIST_--see stdio.h!) */ +#if !defined (_VA_LIST_) || defined (__BSD_NET2__) || defined (____386BSD____) || defined (__bsdi__) || defined (__sequent__) || defined (__FreeBSD__) || defined(WINNT) +/* The macro _VA_LIST_DEFINED is used in Windows NT 3.5 */ +#ifndef _VA_LIST_DEFINED +/* The macro _VA_LIST is used in SCO Unix 3.2. */ +#ifndef _VA_LIST +/* The macro _VA_LIST_T_H is used in the Bull dpx2 */ +#ifndef _VA_LIST_T_H +/* The macro __va_list__ is used by BeOS. */ +#ifndef __va_list__ +typedef __gnuc_va_list va_list; +#endif /* not __va_list__ */ +#endif /* not _VA_LIST_T_H */ +#endif /* not _VA_LIST */ +#endif /* not _VA_LIST_DEFINED */ +#if !(defined (__BSD_NET2__) || defined (____386BSD____) || defined (__bsdi__) || defined (__sequent__) || defined (__FreeBSD__)) +#define _VA_LIST_ +#endif +#ifndef _VA_LIST +#define _VA_LIST +#endif +#ifndef _VA_LIST_DEFINED +#define _VA_LIST_DEFINED +#endif +#ifndef _VA_LIST_T_H +#define _VA_LIST_T_H +#endif +#ifndef __va_list__ +#define __va_list__ +#endif + +#endif /* not _VA_LIST_, except on certain systems */ + +#endif /* not __svr4__ */ + +#endif /* _STDARG_H */ + +#endif /* not _ANSI_STDARG_H_ */ +#endif /* not _STDARG_H */ diff --git a/include/stdbool.h b/include/stdbool.h index 00f0b54..6ce82ba 100644 --- a/include/stdbool.h +++ b/include/stdbool.h @@ -1,8 +1,11 @@ -#pragma once - -#define bool _Bool -#define true 1 -#define false 0 -#define __bool_true_false_are_defined 1 - -typedef uint8_t _Bool; +#ifndef _STDBOOL_H_ +#define _STDBOOL_H_ + +#define bool _Bool +#define true 1 +#define false 0 +#define __bool_true_false_are_defined 1 + +typedef uint8_t _Bool; + +#endif \ No newline at end of file diff --git a/include/stddef.h b/include/stddef.h index 6d00daa..c0a81ab 100644 --- a/include/stddef.h +++ b/include/stddef.h @@ -1,5 +1,8 @@ -#pragma once - -#define NULL 0x00 -typedef unsigned long size_t; - + +#ifndef _STDDEF_H_ +#define _STDDEF_H_ + +#define NULL (void*)0x0; +typedef unsigned long size_t; + +#endif diff --git a/include/stdint.h b/include/stdint.h new file mode 100644 index 0000000..4232c25 --- /dev/null +++ b/include/stdint.h @@ -0,0 +1,13 @@ +#ifndef _STDINT_H_ +#define _STDINT_H_ + +typedef int int32_t; +typedef unsigned int uint32_t; +typedef short int16_t; +typedef unsigned short uint16_t; +typedef char int8_t; +typedef unsigned char uint8_t; +typedef long long int64_t; +typedef unsigned long long uint64_t; + +#endif \ No newline at end of file diff --git a/include/string.h b/include/string.h new file mode 100644 index 0000000..a56ce1f --- /dev/null +++ b/include/string.h @@ -0,0 +1,13 @@ +#ifndef _STRING_H_ +#define _STRING_H_ + +#include +#include +#include + +int memcmp(const void *dst, const void *src, size_t n); +void *memcpy(void *dst, const void *src, size_t n); +void *memset(void *p, int c, size_t n); +size_t strlen(const char *s); + +#endif \ No newline at end of file diff --git a/include/stropts.h b/include/stropts.h index 92edc42..e5fe4d4 100644 --- a/include/stropts.h +++ b/include/stropts.h @@ -1,62 +1,66 @@ -#pragma once -#if defined(__GNUC__) || || defined(__clang__) || defined(_INTEL_COMPILER) -#warning stropts.h is deprecated and may be removed in the future. -#else -#pragma message("warning: stropts.h is deprecated and may be removed in the future.") -#endif - -struct bandinfo -{ - int bi_flag; - unsigned char bi_pri; -}; - -struct strbuf -{ - char *buf; - int len; - int maxlen; -}; - -struct strpeek -{ - struct strbuf ctlbuf; - struct strbuf databuf; - t_uscalar_t flags; -}; - -struct strfdinsert -{ - struct strbuf ctlbuf; - struct strbuf databuf; - int filedes; - t_uscalar_t flags; - int offset; -}; - -struct strioctl -{ - int ic_cmd; - char *ic_dp; - int ic_len; - int ic_timeout; -}; - -struct strrecvfd -{ - int fd; - gid_t gid; - uid_t uid; -}; - -struct str_list -{ - struct str_mlist *sl_modlist; - int sl_nmods; -}; - -struct str_mlist -{ - char l_name[FMNAMESZ+1]; -}; - + +#ifndef _STROPTS_H_ +#define _STROPTS_H_ + +#if defined(__GNUC__) || || defined(__clang__) || defined(_INTEL_COMPILER) +#warning stropts.h is deprecated and may be removed in the future. +#else +#pragma message("warning: stropts.h is deprecated and may be removed in the future.") +#endif + +struct bandinfo +{ + int bi_flag; + unsigned char bi_pri; +}; + +struct strbuf +{ + char *buf; + int len; + int maxlen; +}; + +struct strpeek +{ + struct strbuf ctlbuf; + struct strbuf databuf; + t_uscalar_t flags; +}; + +struct strfdinsert +{ + struct strbuf ctlbuf; + struct strbuf databuf; + int filedes; + t_uscalar_t flags; + int offset; +}; + +struct strioctl +{ + int ic_cmd; + char *ic_dp; + int ic_len; + int ic_timeout; +}; + +struct strrecvfd +{ + int fd; + gid_t gid; + uid_t uid; +}; + +struct str_list +{ + struct str_mlist *sl_modlist; + int sl_nmods; +}; + +struct str_mlist +{ + char l_name[FMNAMESZ+1]; +}; + +#endif \ No newline at end of file diff --git a/include/time.h b/include/time.h index 6b490da..d715955 100644 --- a/include/time.h +++ b/include/time.h @@ -1,29 +1,31 @@ -#pragma once - -#include - -struct tm -{ - int tm_sec; - int tm_min; - int tm_hour; - int tm_mday; - int tm_mon; - int tm_year; - int tm_wday; - int tm_yday; - int tm_isdst; -}; - -struct timespec -{ - time_t tv_sec; - long tv_nsec; -}; - -struct itimerspec -{ - struct timespec it_interval; - struct timespec it_value; -}; - +#ifndef _TIME_H_ +#define _TIME_H_ + +#include + +struct tm +{ + int tm_sec; + int tm_min; + int tm_hour; + int tm_mday; + int tm_mon; + int tm_year; + int tm_wday; + int tm_yday; + int tm_isdst; +}; + +struct timespec +{ + time_t tv_sec; + long tv_nsec; +}; + +struct itimerspec +{ + struct timespec it_interval; + struct timespec it_value; +}; + +#endif \ No newline at end of file diff --git a/include/unistd.h b/include/unistd.h index 3fa194b..bab4c4b 100644 --- a/include/unistd.h +++ b/include/unistd.h @@ -1,5 +1,9 @@ -#pragma once - -#define _POSIX_VERSION 200809L -#define _POSIX2_VERSION 200809L -#define _XOPEN_VERSION 700 + +#ifndef _UNISTD_H_ +#define _UNISTD_H_ + +#define _POSIX_VERSION 200809L +#define _POSIX2_VERSION 200809L +#define _XOPEN_VERSION 700 + +#endif \ No newline at end of file diff --git a/src/__ashldi3.c b/src/__ashldi3.c new file mode 100644 index 0000000..95937f0 --- /dev/null +++ b/src/__ashldi3.c @@ -0,0 +1,23 @@ +/* + * libgcc/__ashldi3.c + */ + +#include +#include + +uint64_t __ashldi3(uint64_t v, int cnt) +{ + int c = cnt & 31; + uint32_t vl = (uint32_t) v; + uint32_t vh = (uint32_t) (v >> 32); + + if (cnt & 32) { + vh = (vl << c); + vl = 0; + } else { + vh = (vh << c) + (vl >> (32 - c)); + vl = (vl << c); + } + + return ((uint64_t) vh << 32) + vl; +} diff --git a/src/__clzsi2.c b/src/__clzsi2.c new file mode 100644 index 0000000..ebb11f0 --- /dev/null +++ b/src/__clzsi2.c @@ -0,0 +1,36 @@ +/* + * libgcc/__clzsi2.c + * + * Returns the leading number of 0 bits in the argument + */ + +#include +#include + +uint32_t __clzsi2(uint32_t v) +{ + int p = 31; + + if (v & 0xffff0000) { + p -= 16; + v >>= 16; + } + if (v & 0xff00) { + p -= 8; + v >>= 8; + } + if (v & 0xf0) { + p -= 4; + v >>= 4; + } + if (v & 0xc) { + p -= 2; + v >>= 2; + } + if (v & 0x2) { + p -= 1; + v >>= 1; + } + + return p; +} diff --git a/src/__ctzsi2.c b/src/__ctzsi2.c new file mode 100644 index 0000000..9a28f3e --- /dev/null +++ b/src/__ctzsi2.c @@ -0,0 +1,42 @@ +#include + +typedef uint32_t su_int; +typedef int32_t si_int; + +si_int +__ctzsi2(si_int a) +{ + su_int x = (su_int)a; + si_int t = ((x & 0x0000FFFF) == 0) << 4; /* if (x has no small bits) t = 16 else 0 */ + x >>= t; /* x = [0 - 0xFFFF] + higher garbage bits */ + su_int r = t; /* r = [0, 16] */ + /* return r + ctz(x) */ + t = ((x & 0x00FF) == 0) << 3; + x >>= t; /* x = [0 - 0xFF] + higher garbage bits */ + r += t; /* r = [0, 8, 16, 24] */ + /* return r + ctz(x) */ + t = ((x & 0x0F) == 0) << 2; + x >>= t; /* x = [0 - 0xF] + higher garbage bits */ + r += t; /* r = [0, 4, 8, 12, 16, 20, 24, 28] */ + /* return r + ctz(x) */ + t = ((x & 0x3) == 0) << 1; + x >>= t; + x &= 3; /* x = [0 - 3] */ + r += t; /* r = [0 - 30] and is even */ + /* return r + ctz(x) */ + +/* The branch-less return statement below is equivalent + * to the following switch statement: + * switch (x) + * { + * case 0: + * return r + 2; + * case 2: + * return r + 1; + * case 1: + * case 3: + * return r; + * } + */ + return r + ((2 - (x >> 1)) & -((x & 1) == 0)); +} diff --git a/src/__divdi3.c b/src/__divdi3.c new file mode 100644 index 0000000..973fe63 --- /dev/null +++ b/src/__divdi3.c @@ -0,0 +1,29 @@ +/* + * arch/i386/libgcc/__divdi3.c + */ + +#include +#include + +extern uint64_t __udivmoddi4(uint64_t num, uint64_t den, uint64_t * rem); + +int64_t __divdi3(int64_t num, int64_t den) +{ + int minus = 0; + int64_t v; + + if (num < 0) { + num = -num; + minus = 1; + } + if (den < 0) { + den = -den; + minus ^= 1; + } + + v = __udivmoddi4(num, den, NULL); + if (minus) + v = -v; + + return v; +} diff --git a/src/__divsi3.c b/src/__divsi3.c new file mode 100644 index 0000000..35420f5 --- /dev/null +++ b/src/__divsi3.c @@ -0,0 +1,29 @@ +/* + * libgcc/__divsi3.c + */ + +#include +#include + +extern uint32_t __udivmodsi4(uint32_t num, uint32_t den, uint32_t * rem); + +int32_t __divsi3(int32_t num, int32_t den) +{ + int minus = 0; + int32_t v; + + if (num < 0) { + num = -num; + minus = 1; + } + if (den < 0) { + den = -den; + minus ^= 1; + } + + v = __udivmodsi4(num, den, NULL); + if (minus) + v = -v; + + return v; +} diff --git a/src/__lshrdi3.c b/src/__lshrdi3.c new file mode 100644 index 0000000..765e1f2 --- /dev/null +++ b/src/__lshrdi3.c @@ -0,0 +1,23 @@ +/* + * libgcc/__lshrdi3.c + */ + +#include +#include + +uint64_t __lshrdi3(uint64_t v, int cnt) +{ + int c = cnt & 31; + uint32_t vl = (uint32_t) v; + uint32_t vh = (uint32_t) (v >> 32); + + if (cnt & 32) { + vl = (vh >> c); + vh = 0; + } else { + vl = (vl >> c) + (vh << (32 - c)); + vh = (vh >> c); + } + + return ((uint64_t) vh << 32) + vl; +} diff --git a/src/__moddi3.c b/src/__moddi3.c new file mode 100644 index 0000000..0e7ed98 --- /dev/null +++ b/src/__moddi3.c @@ -0,0 +1,29 @@ +/* + * arch/i386/libgcc/__moddi3.c + */ + +#include +#include + +extern uint64_t __udivmoddi4(uint64_t num, uint64_t den, uint64_t * rem); + +int64_t __moddi3(int64_t num, int64_t den) +{ + int minus = 0; + int64_t v; + + if (num < 0) { + num = -num; + minus = 1; + } + if (den < 0) { + den = -den; + minus ^= 1; + } + + (void)__udivmoddi4(num, den, (uint64_t *) & v); + if (minus) + v = -v; + + return v; +} diff --git a/src/__modsi3.c b/src/__modsi3.c new file mode 100644 index 0000000..33a21ba --- /dev/null +++ b/src/__modsi3.c @@ -0,0 +1,29 @@ +/* + * libgcc/__modsi3.c + */ + +#include +#include + +extern uint32_t __udivmodsi4(uint32_t num, uint32_t den, uint32_t * rem); + +int32_t __modsi3(int32_t num, int32_t den) +{ + int minus = 0; + int32_t v; + + if (num < 0) { + num = -num; + minus = 1; + } + if (den < 0) { + den = -den; + minus ^= 1; + } + + (void)__udivmodsi4(num, den, (uint32_t *) & v); + if (minus) + v = -v; + + return v; +} diff --git a/src/__udivdi3.c b/src/__udivdi3.c new file mode 100644 index 0000000..5eea461 --- /dev/null +++ b/src/__udivdi3.c @@ -0,0 +1,13 @@ +/* + * arch/i386/libgcc/__divdi3.c + */ + +#include +#include + +extern uint64_t __udivmoddi4(uint64_t num, uint64_t den, uint64_t * rem); + +uint64_t __udivdi3(uint64_t num, uint64_t den) +{ + return __udivmoddi4(num, den, NULL); +} diff --git a/src/__udivmoddi4.c b/src/__udivmoddi4.c new file mode 100644 index 0000000..afec7be --- /dev/null +++ b/src/__udivmoddi4.c @@ -0,0 +1,33 @@ +#include + +extern void __divide_error(); + +uint64_t __udivmoddi4(uint64_t num, uint64_t den, uint64_t * rem_p) +{ + uint64_t quot = 0, qbit = 1; + + if (den == 0) { + __divide_error(); + return 0; /* If trap returns... */ + } + + /* Left-justify denominator and count shift */ + while ((int64_t) den >= 0) { + den <<= 1; + qbit <<= 1; + } + + while (qbit) { + if (den <= num) { + num -= den; + quot += qbit; + } + den >>= 1; + qbit >>= 1; + } + + if (rem_p) + *rem_p = num; + + return quot; +} diff --git a/src/__udivmodsi4.c b/src/__udivmodsi4.c new file mode 100644 index 0000000..cc5fe0e --- /dev/null +++ b/src/__udivmodsi4.c @@ -0,0 +1,32 @@ +#include + +extern void __divide_error(); +uint32_t __udivmodsi4(uint32_t num, uint32_t den, uint32_t * rem_p) +{ + uint32_t quot = 0, qbit = 1; + + if (den == 0) { + __divide_error(); + return 0; /* If trap returns... */ + } + + /* Left-justify denominator and count shift */ + while ((int32_t) den >= 0) { + den <<= 1; + qbit <<= 1; + } + + while (qbit) { + if (den <= num) { + num -= den; + quot += qbit; + } + den >>= 1; + qbit >>= 1; + } + + if (rem_p) + *rem_p = num; + + return quot; +} diff --git a/src/__udivsi3.c b/src/__udivsi3.c new file mode 100644 index 0000000..5635f3f --- /dev/null +++ b/src/__udivsi3.c @@ -0,0 +1,13 @@ +/* + * libgcc/__divsi3.c + */ + +#include +#include + +extern uint32_t __udivmodsi4(uint32_t num, uint32_t den, uint32_t * rem); + +uint32_t __udivsi3(uint32_t num, uint32_t den) +{ + return __udivmodsi4(num, den, NULL); +} diff --git a/src/__umoddi3.c b/src/__umoddi3.c new file mode 100644 index 0000000..1fc754a --- /dev/null +++ b/src/__umoddi3.c @@ -0,0 +1,16 @@ +/* + * arch/i386/libgcc/__umoddi3.c + */ + +#include +#include + +extern uint64_t __udivmoddi4(uint64_t num, uint64_t den, uint64_t * rem); + +uint64_t __umoddi3(uint64_t num, uint64_t den) +{ + uint64_t v; + + (void)__udivmoddi4(num, den, &v); + return v; +} diff --git a/src/__umodsi3.c b/src/__umodsi3.c new file mode 100644 index 0000000..85e6e3c --- /dev/null +++ b/src/__umodsi3.c @@ -0,0 +1,16 @@ +/* + * libgcc/__umodsi3.c + */ + +#include +#include + +extern uint32_t __udivmodsi4(uint32_t num, uint32_t den, uint32_t * rem); + +uint32_t __umodsi3(uint32_t num, uint32_t den) +{ + uint32_t v; + + (void)__udivmodsi4(num, den, &v); + return v; +} diff --git a/src/atoi.c b/src/atoi.c new file mode 100644 index 0000000..a6ec0bf --- /dev/null +++ b/src/atoi.c @@ -0,0 +1,3 @@ +#define TYPE int +#define NAME atoi +#include "atox.c" diff --git a/src/atol.c b/src/atol.c new file mode 100644 index 0000000..e65484e --- /dev/null +++ b/src/atol.c @@ -0,0 +1,3 @@ +#define TYPE long +#define NAME atol +#include "atox.c" diff --git a/src/atoll.c b/src/atoll.c new file mode 100644 index 0000000..25df79e --- /dev/null +++ b/src/atoll.c @@ -0,0 +1,3 @@ +#define TYPE long long +#define NAME atoll +#include "atox.c" diff --git a/src/atox.c b/src/atox.c new file mode 100644 index 0000000..f6f8514 --- /dev/null +++ b/src/atox.c @@ -0,0 +1,16 @@ +/* + * atox.c + * + * atoi(), atol(), atoll() + */ + +#include +#include +#include + +extern uintmax_t strntoumax(const char *nptr, char **endptr, int base, size_t n); + +TYPE NAME(const char *nptr) +{ + return (TYPE) strntoumax(nptr, (char **)NULL, 10, ~(size_t) 0); +} diff --git a/src/memccpy.c b/src/memccpy.c new file mode 100644 index 0000000..83d02c9 --- /dev/null +++ b/src/memccpy.c @@ -0,0 +1,23 @@ +/* + * memccpy.c + * + * memccpy() + */ + +#include +#include + +void *memccpy(void *dst, const void *src, int c, size_t n) +{ + char *q = dst; + const char *p = src; + char ch; + + while (n--) { + *q++ = ch = *p++; + if (ch == (char)c) + return q; + } + + return NULL; /* No instance of "c" found */ +} diff --git a/src/memchr.c b/src/memchr.c new file mode 100644 index 0000000..a46af0f --- /dev/null +++ b/src/memchr.c @@ -0,0 +1,19 @@ +/* + * memchr.c + */ + +#include "inttypes.h" +#include "stdlib.h" + +void *memchr(const void *s, int c, size_t n) +{ + const unsigned char *sp = s; + + while (n--) { + if (*sp == (unsigned char)c) + return (void *)sp; + sp++; + } + + return NULL; +} diff --git a/src/memmem.c b/src/memmem.c new file mode 100644 index 0000000..8b5faa0 --- /dev/null +++ b/src/memmem.c @@ -0,0 +1,52 @@ +/* + * memmem.c + * + * Find a byte string inside a longer byte string + * + * This uses the "Not So Naive" algorithm, a very simple but + * usually effective algorithm, see: + * + * http://www-igm.univ-mlv.fr/~lecroq/string/ + */ + +#include + +void *memmem(const void *haystack, size_t n, const void *needle, size_t m) +{ + const unsigned char *y = (const unsigned char *)haystack; + const unsigned char *x = (const unsigned char *)needle; + + size_t j, k, l; + + if (m > n || !m || !n) + return NULL; + + if (1 != m) { + if (x[0] == x[1]) { + k = 2; + l = 1; + } else { + k = 1; + l = 2; + } + + j = 0; + while (j <= n - m) { + if (x[1] != y[j + 1]) { + j += k; + } else { + if (!memcmp(x + 2, y + j + 2, m - 2) + && x[0] == y[j]) + return (void *)&y[j]; + j += l; + } + } + } else + do { + if (*y == *x) + return (void *)y; + y++; + } while (--n); + + return NULL; +} diff --git a/src/memmove.c b/src/memmove.c new file mode 100644 index 0000000..32436f5 --- /dev/null +++ b/src/memmove.c @@ -0,0 +1,37 @@ +/* + * memmove.c + */ + +#include "stdlib.h" +#include "inttypes.h" + +void *memmove(void *dst, const void *src, size_t n) +{ + const char *p = src; + char *q = dst; +#if defined(__i386__) || defined(__x86_64__) + if (q < p) { + asm volatile("cld; rep; movsb" + : "+c" (n), "+S"(p), "+D"(q)); + } else { + p += (n - 1); + q += (n - 1); + asm volatile("std; rep; movsb; cld" + : "+c" (n), "+S"(p), "+D"(q)); + } +#else + if (q < p) { + while (n--) { + *q++ = *p++; + } + } else { + p += n; + q += n; + while (n--) { + *--q = *--p; + } + } +#endif + + return dst; +} diff --git a/src/memops.c b/src/memops.c new file mode 100644 index 0000000..de5fea5 --- /dev/null +++ b/src/memops.c @@ -0,0 +1,38 @@ + +#include + +int memcmp(const void *dst, const void *src, size_t n) +{ + if (!n) return 0; + + while (--n && *(char *) dst == *(char *) src) + { + dst = (char *) dst + 1; + src = (char *) src + 1; + } + + return *((unsigned char *) dst) - *((unsigned char *) src); +} + +void *memcpy(void *dst, const void *src, size_t n) +{ + void *ret = dst; + + while (n--) + { + *(char *)dst = *(char *)src; + dst = (char *) dst + 1; + src = (char *) src + 1; + } + + return ret; +} + +void *memset(void *p, int c, size_t n) +{ + char *pb = (char *) p; + char *pbend = pb + n; + while (pb != pbend) *pb++ = c; + return p; +} + diff --git a/src/memrchr.c b/src/memrchr.c new file mode 100644 index 0000000..ff6d711 --- /dev/null +++ b/src/memrchr.c @@ -0,0 +1,19 @@ +/* + * memrchr.c + */ + +#include +#include + +void *memrchr(const void *s, int c, size_t n) +{ + const unsigned char *sp = (const unsigned char *)s + n - 1; + + while (n--) { + if (*sp == (unsigned char)c) + return (void *)sp; + sp--; + } + + return NULL; +} diff --git a/src/memswap.c b/src/memswap.c new file mode 100644 index 0000000..b32315c --- /dev/null +++ b/src/memswap.c @@ -0,0 +1,24 @@ +/* + * memswap() + * + * Swaps the contents of two nonoverlapping memory areas. + * This really could be done faster... + */ + +#include + +void memswap(void *m1, void *m2, size_t n) +{ + char *p = m1; + char *q = m2; + char tmp; + + while (n--) { + tmp = *p; + *p = *q; + *q = tmp; + + p++; + q++; + } +} diff --git a/src/snprintf.c b/src/snprintf.c new file mode 100644 index 0000000..cddf711 --- /dev/null +++ b/src/snprintf.c @@ -0,0 +1,18 @@ +/* + * snprintf.c + */ + +#include "inttypes.h" +#include "stdarg.h" +extern int vsnprintf(char *buffer, size_t n, const char *format, va_list ap); + +int snprintf(char *buffer, size_t n, const char *format, ...) +{ + va_list ap; + int rv; + + va_start(ap, format); + rv = vsnprintf(buffer, n, format, ap); + va_end(ap); + return rv; +} \ No newline at end of file diff --git a/src/sscanf.c b/src/sscanf.c new file mode 100644 index 0000000..f44a6a6 --- /dev/null +++ b/src/sscanf.c @@ -0,0 +1,17 @@ +/* + * sscanf() + */ + +#include "stdarg.h" +extern int vsscanf(const char *buffer, const char *format, va_list ap); +int sscanf(const char *str, const char *format, ...) +{ + va_list ap; + int rv; + + va_start(ap, format); + rv = vsscanf(str, format, ap); + va_end(ap); + + return rv; +} diff --git a/src/string.c b/src/strlen.c similarity index 93% rename from src/string.c rename to src/strlen.c index a7cc8ba..b0ffbf3 100644 --- a/src/string.c +++ b/src/strlen.c @@ -1,10 +1,10 @@ -#include "string.h" - -size_t strlen(const char * s) -{ - int i; - i = 0; - while (s[i] != NULL) - ++i; - return (size_t)(i+1); -} +#include "string.h" + +size_t strlen(const char * s) +{ + int i; + i = 0; + while (s[i] != NULL) + ++i; + return (size_t)(i+1); +} diff --git a/src/strnlen.c b/src/strnlen.c new file mode 100644 index 0000000..1678f4b --- /dev/null +++ b/src/strnlen.c @@ -0,0 +1,18 @@ +/* + * strnlen() + */ + +#include + +size_t strnlen(const char *s, size_t maxlen) +{ + const char *ss = s; + + /* Important: the maxlen test must precede the reference through ss; + since the byte beyond the maximum may segfault */ + while ((maxlen > 0) && *ss) { + ss++; + maxlen--; + } + return ss - s; +} diff --git a/src/strntoimax.c b/src/strntoimax.c new file mode 100644 index 0000000..c152862 --- /dev/null +++ b/src/strntoimax.c @@ -0,0 +1,15 @@ +/* + * strntoimax.c + * + * strntoimax() + */ + +#include +#include + +extern uintmax_t strntoumax(const char *nptr, char **endptr, int base, size_t n); + +intmax_t strntoimax(const char *nptr, char **endptr, int base, size_t n) +{ + return (intmax_t) strntoumax(nptr, endptr, base, n); +} diff --git a/src/strntoumax.c b/src/strntoumax.c new file mode 100644 index 0000000..d8aec88 --- /dev/null +++ b/src/strntoumax.c @@ -0,0 +1,77 @@ +/* + * strntoumax.c + * + * The strntoumax() function and associated + */ +#include "inttypes.h" + +#define isspace(c) (c == ' ' || c == '\f' || c == '\n' || c == '\r' ||\ + c == '\t' || c == '\v') + + +static inline int digitval(int ch) +{ + if (ch >= '0' && ch <= '9') { + return ch - '0'; + } else if (ch >= 'A' && ch <= 'Z') { + return ch - 'A' + 10; + } else if (ch >= 'a' && ch <= 'z') { + return ch - 'a' + 10; + } else { + return -1; + } +} + +uintmax_t strntoumax(const char *nptr, char **endptr, int base, size_t n) +{ + int minus = 0; + uintmax_t v = 0; + int d; + + while (n && isspace((unsigned char)*nptr)) { + nptr++; + n--; + } + + /* Single optional + or - */ + if (n) { + char c = *nptr; + if (c == '-' || c == '+') { + minus = (c == '-'); + nptr++; + n--; + } + } + + if (base == 0) { + if (n >= 2 && nptr[0] == '0' && + (nptr[1] == 'x' || nptr[1] == 'X')) { + n -= 2; + nptr += 2; + base = 16; + } else if (n >= 1 && nptr[0] == '0') { + n--; + nptr++; + base = 8; + } else { + base = 10; + } + } else if (base == 16) { + if (n >= 2 && nptr[0] == '0' && + (nptr[1] == 'x' || nptr[1] == 'X')) { + n -= 2; + nptr += 2; + } + } + + while (n && (d = digitval(*nptr)) >= 0 && d < base) { + v = v * base + d; + n--; + nptr++; + } + + if (endptr) + *endptr = (char *)nptr; + + return minus ? -v : v; +} diff --git a/src/strtol.c b/src/strtol.c new file mode 100644 index 0000000..9efc8b9 --- /dev/null +++ b/src/strtol.c @@ -0,0 +1,3 @@ +#define TYPE signed long +#define NAME strtol +#include "strtox.c" diff --git a/src/strtoll.c b/src/strtoll.c new file mode 100644 index 0000000..a9428c7 --- /dev/null +++ b/src/strtoll.c @@ -0,0 +1,3 @@ +#define TYPE signed long long +#define NAME strtoll +#include "strtox.c" diff --git a/src/strtoul.c b/src/strtoul.c new file mode 100644 index 0000000..3189aaa --- /dev/null +++ b/src/strtoul.c @@ -0,0 +1,3 @@ +#define TYPE unsigned long +#define NAME strtoul +#include "strtox.c" diff --git a/src/strtoull.c b/src/strtoull.c new file mode 100644 index 0000000..83c14e9 --- /dev/null +++ b/src/strtoull.c @@ -0,0 +1,3 @@ +#define TYPE unsigned long long +#define NAME strtoull +#include "strtox.c" diff --git a/src/strtox.c b/src/strtox.c new file mode 100644 index 0000000..02d1e1b --- /dev/null +++ b/src/strtox.c @@ -0,0 +1,16 @@ +/* + * strtox.c + * + * strto...() functions, by macro definition + */ + +#include +#include +#include + +extern uintmax_t strntoumax(const char *nptr, char **endptr, int base, size_t n); + +TYPE NAME(const char *nptr, char **endptr, int base) +{ + return (TYPE) strntoumax(nptr, endptr, base, ~(size_t) 0); +} diff --git a/src/vsnprintf.c b/src/vsnprintf.c new file mode 100644 index 0000000..01e8eb5 --- /dev/null +++ b/src/vsnprintf.c @@ -0,0 +1,497 @@ +/* + * vsnprintf.c + * + * vsnprintf(), from which the rest of the printf() + * family is built + */ +#include "string.h" +#include "stdio.h" +#include "stdlib.h" +#include "inttypes.h" +#include "stdarg.h" + +#define CHAR_BIT 8 + +enum flags { + FL_ZERO = 0x01, /* Zero modifier */ + FL_MINUS = 0x02, /* Minus modifier */ + FL_PLUS = 0x04, /* Plus modifier */ + FL_TICK = 0x08, /* ' modifier */ + FL_SPACE = 0x10, /* Space modifier */ + FL_HASH = 0x20, /* # modifier */ + FL_SIGNED = 0x40, /* Number is signed */ + FL_UPPER = 0x80 /* Upper case digits */ +}; + +/* These may have to be adjusted on certain implementations */ +enum ranks { + rank_char = -2, + rank_short = -1, + rank_int = 0, + rank_long = 1, + rank_longlong = 2 +}; + +#define MIN_RANK rank_char +#define MAX_RANK rank_longlong + +#define INTMAX_RANK rank_longlong +#define SIZE_T_RANK rank_long +#define PTRDIFF_T_RANK rank_long + +#define EMIT(x) ({ if (o nchars) { + while (width > nchars) { + EMIT(' '); + width--; + } + } + + /* Emit nondigits */ + if (minus) + EMIT('-'); + else if (flags & FL_PLUS) + EMIT('+'); + else if (flags & FL_SPACE) + EMIT(' '); + + if ((flags & FL_HASH) && base == 16) { + EMIT('0'); + EMIT((flags & FL_UPPER) ? 'X' : 'x'); + } + + /* Emit zero padding */ + if ((flags & (FL_MINUS | FL_ZERO)) == FL_ZERO && width > ndigits) { + while (width > nchars) { + EMIT('0'); + width--; + } + } + + /* Generate the number. This is done from right to left. */ + q += ndigits; /* Advance the pointer to end of number */ + o += ndigits; + qq = q; + oo = o; /* Temporary values */ + + b4tick = tickskip; + while (ndigits > 0) { + if (!b4tick--) { + qq--; + oo--; + ndigits--; + if (oo < n) + *qq = '_'; + b4tick = tickskip - 1; + } + qq--; + oo--; + ndigits--; + if (oo < n) + *qq = digits[val % base]; + val /= base; + } + + /* Emit late space padding */ + while ((flags & FL_MINUS) && width > nchars) { + EMIT(' '); + width--; + } + + return o; +} + +void _printf(char* fmt, ...) { + char buffer[512]; + va_list ap; + va_start(ap, fmt); + vsnprintf(buffer, 512, fmt, ap); + puts(buffer); + va_end(ap); +} + +int vsnprintf(char *buffer, size_t n, const char *format, va_list ap) +{ + const char *p = format; + char ch; + char *q = buffer; + size_t o = 0; /* Number of characters output */ + uintmax_t val = 0; + int rank = rank_int; /* Default rank */ + int width = 0; + int prec = -1; + int base; + size_t sz; + enum flags flags = 0; + enum { + st_normal, /* Ground state */ + st_flags, /* Special flags */ + st_width, /* Field width */ + st_prec, /* Field precision */ + st_modifiers /* Length or conversion modifiers */ + } state = st_normal; + const char *sarg; /* %s string argument */ + char carg; /* %c char argument */ + int slen; /* String length */ + + while ((ch = *p++)) { + switch (state) { + case st_normal: + if (ch == '%') { + state = st_flags; + flags = 0; + rank = rank_int; + width = 0; + prec = -1; + } else { + EMIT(ch); + } + break; + + case st_flags: + switch (ch) { + case '-': + flags |= FL_MINUS; + break; + case '+': + flags |= FL_PLUS; + break; + case '\'': + flags |= FL_TICK; + break; + case ' ': + flags |= FL_SPACE; + break; + case '#': + flags |= FL_HASH; + break; + case '0': + flags |= FL_ZERO; + break; + default: + state = st_width; + p--; /* Process this character again */ + break; + } + break; + + case st_width: + if (ch >= '0' && ch <= '9') { + width = width * 10 + (ch - '0'); + } else if (ch == '*') { + width = va_arg(ap, int); + if (width < 0) { + width = -width; + flags |= FL_MINUS; + } + } else if (ch == '.') { + prec = 0; /* Precision given */ + state = st_prec; + } else { + state = st_modifiers; + p--; /* Process this character again */ + } + break; + + case st_prec: + if (ch >= '0' && ch <= '9') { + prec = prec * 10 + (ch - '0'); + } else if (ch == '*') { + prec = va_arg(ap, int); + if (prec < 0) + prec = -1; + } else { + state = st_modifiers; + p--; /* Process this character again */ + } + break; + + case st_modifiers: + switch (ch) { + /* Length modifiers - nonterminal sequences */ + case 'h': + rank--; /* Shorter rank */ + break; + case 'l': + rank++; /* Longer rank */ + break; + case 'j': + rank = INTMAX_RANK; + break; + case 'z': + rank = SIZE_T_RANK; + break; + case 't': + rank = PTRDIFF_T_RANK; + break; + case 'L': + case 'q': + rank += 2; + break; + default: + /* Output modifiers - terminal sequences */ + + /* Next state will be normal */ + state = st_normal; + + /* Canonicalize rank */ + if (rank < MIN_RANK) + rank = MIN_RANK; + else if (rank > MAX_RANK) + rank = MAX_RANK; + + switch (ch) { + case 'P': /* Upper case pointer */ + flags |= FL_UPPER; + /* fall through */ + case 'p': /* Pointer */ + base = 16; + prec = (CHAR_BIT*sizeof(void *)+3)/4; + flags |= FL_HASH; + val = (uintmax_t)(uint32_t*) + va_arg(ap, void *); + goto is_integer; + + case 'd': /* Signed decimal output */ + case 'i': + base = 10; + flags |= FL_SIGNED; + switch (rank) { + case rank_char: + /* Yes, all these casts are + needed... */ + val = (uintmax_t)(intmax_t) + (signed char) + va_arg(ap, signed int); + break; + case rank_short: + val = (uintmax_t)(intmax_t) + (signed short) + va_arg(ap, signed int); + break; + case rank_int: + val = (uintmax_t)(intmax_t) + va_arg(ap, signed int); + break; + case rank_long: + val = (uintmax_t)(intmax_t) + va_arg(ap, signed long); + break; + case rank_longlong: + val = (uintmax_t)(intmax_t) + va_arg(ap, + signed long long); + break; + } + goto is_integer; + case 'o': /* Octal */ + base = 8; + goto is_unsigned; + case 'u': /* Unsigned decimal */ + base = 10; + goto is_unsigned; + case 'X': /* Upper case hexadecimal */ + flags |= FL_UPPER; + /* fall through */ + case 'x': /* Hexadecimal */ + base = 16; + goto is_unsigned; + + is_unsigned: + switch (rank) { + case rank_char: + val = (uintmax_t) + (unsigned char) + va_arg(ap, unsigned + int); + break; + case rank_short: + val = (uintmax_t) + (unsigned short) + va_arg(ap, unsigned + int); + break; + case rank_int: + val = (uintmax_t) + va_arg(ap, unsigned + int); + break; + case rank_long: + val = (uintmax_t) + va_arg(ap, unsigned + long); + break; + case rank_longlong: + val = (uintmax_t) + va_arg(ap, unsigned + long long); + break; + } + /* fall through */ + + is_integer: + sz = format_int(q, (o < n) ? n - o : 0, + val, flags, base, + width, prec); + q += sz; + o += sz; + break; + + case 'c': /* Character */ + carg = (char)va_arg(ap, int); + sarg = &carg; + slen = 1; + goto is_string; + case 's': /* String */ + sarg = va_arg(ap, const char *); + sarg = sarg ? sarg : "(null)"; + slen = strlen(sarg); + goto is_string; + + is_string: + { + char sch; + int i; + + if (prec != -1 && slen > prec) + slen = prec; + + if (width > slen + && !(flags & FL_MINUS)) { + char pad = + (flags & FL_ZERO) ? + '0' : ' '; + while (width > slen) { + EMIT(pad); + width--; + } + } + for (i = slen; i; i--) { + sch = *sarg++; + EMIT(sch); + } + if (width > slen + && (flags & FL_MINUS)) { + while (width > slen) { + EMIT(' '); + width--; + } + } + } + break; + + case 'n': + { + /* Output the number of + characters written */ + + switch (rank) { + case rank_char: + *va_arg(ap, + signed char *) + = o; + break; + case rank_short: + *va_arg(ap, + signed short *) + = o; + break; + case rank_int: + *va_arg(ap, + signed int *) + = o; + break; + case rank_long: + *va_arg(ap, + signed long *) + = o; + break; + case rank_longlong: + *va_arg(ap, + signed long long *) + = o; + break; + } + } + break; + + default: /* Anything else, including % */ + EMIT(ch); + break; + } + } + } + } + + /* Null-terminate the string */ + if (o < n) + *q = '\0'; /* No overflow */ + else if (n > 0) + buffer[n - 1] = '\0'; /* Overflow - terminate at end of buffer */ + + return o; +} diff --git a/src/vsscanf.c b/src/vsscanf.c new file mode 100644 index 0000000..069862f --- /dev/null +++ b/src/vsscanf.c @@ -0,0 +1,405 @@ +/* + * vsscanf.c + * + * vsscanf(), from which the rest of the scanf() + * family is built + */ + +#include "string.h" +#include "stdlib.h" +#include "inttypes.h" +#include "stdarg.h" +#include "limits.h" + +#define CHAR_BIT 8 + +#define isspace(c) (c == ' ' || c == '\f' || c == '\n' || c == '\r' ||\ + c == '\t' || c == '\v') + + +extern uintmax_t strntoumax(const char *nptr, char **endptr, int base, size_t n); + +#ifndef LONG_BIT +#define LONG_BIT (CHAR_BIT*sizeof(long)) +#endif + +enum flags { + FL_SPLAT = 0x01, /* Drop the value, do not assign */ + FL_INV = 0x02, /* Character-set with inverse */ + FL_WIDTH = 0x04, /* Field width specified */ + FL_MINUS = 0x08, /* Negative number */ +}; + +enum ranks { + rank_char = -2, + rank_short = -1, + rank_int = 0, + rank_long = 1, + rank_longlong = 2, + rank_ptr = INT_MAX /* Special value used for pointers */ +}; + +#define MIN_RANK rank_char +#define MAX_RANK rank_longlong + +#define INTMAX_RANK rank_longlong +#define SIZE_T_RANK rank_long +#define PTRDIFF_T_RANK rank_long + +enum bail { + bail_none = 0, /* No error condition */ + bail_eof, /* Hit EOF */ + bail_err /* Conversion mismatch */ +}; + +static inline const char *skipspace(const char *p) +{ + while (isspace((unsigned char)*p)) + p++; + return p; +} + +#undef set_bit +static inline void set_bit(unsigned long *bitmap, unsigned int bit) +{ + bitmap[bit / LONG_BIT] |= 1UL << (bit % LONG_BIT); +} + +#undef test_bit +static inline int test_bit(unsigned long *bitmap, unsigned int bit) +{ + return (int)(bitmap[bit / LONG_BIT] >> (bit % LONG_BIT)) & 1; +} + +int vsscanf(const char *buffer, const char *format, va_list ap) +{ + const char *p = format; + char ch; + unsigned char uc; + const char *q = buffer; + const char *qq; + uintmax_t val = 0; + int rank = rank_int; /* Default rank */ + unsigned int width = UINT_MAX; + int base; + enum flags flags = 0; + enum { + st_normal, /* Ground state */ + st_flags, /* Special flags */ + st_width, /* Field width */ + st_modifiers, /* Length or conversion modifiers */ + st_match_init, /* Initial state of %[ sequence */ + st_match, /* Main state of %[ sequence */ + st_match_range, /* After - in a %[ sequence */ + } state = st_normal; + char *sarg = NULL; /* %s %c or %[ string argument */ + enum bail bail = bail_none; + int sign; + int converted = 0; /* Successful conversions */ + unsigned long matchmap[((1 << CHAR_BIT) + (LONG_BIT - 1)) / LONG_BIT]; + int matchinv = 0; /* Is match map inverted? */ + unsigned char range_start = 0; + + while ((ch = *p++) && !bail) { + switch (state) { + case st_normal: + if (ch == '%') { + state = st_flags; + flags = 0; + rank = rank_int; + width = UINT_MAX; + } else if (isspace((unsigned char)ch)) { + q = skipspace(q); + } else { + if (*q == ch) + q++; + else + bail = bail_err; /* Match failure */ + } + break; + + case st_flags: + switch (ch) { + case '*': + flags |= FL_SPLAT; + break; + case '0'...'9': + width = (ch - '0'); + state = st_width; + flags |= FL_WIDTH; + break; + default: + state = st_modifiers; + p--; /* Process this character again */ + break; + } + break; + + case st_width: + if (ch >= '0' && ch <= '9') { + width = width * 10 + (ch - '0'); + } else { + state = st_modifiers; + p--; /* Process this character again */ + } + break; + + case st_modifiers: + switch (ch) { + /* Length modifiers - nonterminal sequences */ + case 'h': + rank--; /* Shorter rank */ + break; + case 'l': + rank++; /* Longer rank */ + break; + case 'j': + rank = INTMAX_RANK; + break; + case 'z': + rank = SIZE_T_RANK; + break; + case 't': + rank = PTRDIFF_T_RANK; + break; + case 'L': + case 'q': + rank = rank_longlong; /* long double/long long */ + break; + + default: + /* Output modifiers - terminal sequences */ + /* Next state will be normal */ + state = st_normal; + + /* Canonicalize rank */ + if (rank < MIN_RANK) + rank = MIN_RANK; + else if (rank > MAX_RANK) + rank = MAX_RANK; + + switch (ch) { + case 'P': /* Upper case pointer */ + case 'p': /* Pointer */ + rank = rank_ptr; + base = 0; + sign = 0; + goto scan_int; + + case 'i': /* Base-independent integer */ + base = 0; + sign = 1; + goto scan_int; + + case 'd': /* Decimal integer */ + base = 10; + sign = 1; + goto scan_int; + + case 'o': /* Octal integer */ + base = 8; + sign = 0; + goto scan_int; + + case 'u': /* Unsigned decimal integer */ + base = 10; + sign = 0; + goto scan_int; + + case 'x': /* Hexadecimal integer */ + case 'X': + base = 16; + sign = 0; + goto scan_int; + + case 'n': /* # of characters consumed */ + val = (q - buffer); + goto set_integer; + + scan_int: + q = skipspace(q); + if (!*q) { + bail = bail_eof; + break; + } + val = + strntoumax(q, (char **)&qq, base, + width); + if (qq == q) { + bail = bail_err; + break; + } + q = qq; + if (!(flags & FL_SPLAT)) + converted++; + /* fall through */ + + set_integer: + if (!(flags & FL_SPLAT)) { + switch (rank) { + case rank_char: + *va_arg(ap, + unsigned char *) + = val; + break; + case rank_short: + *va_arg(ap, + unsigned short + *) = val; + break; + case rank_int: + *va_arg(ap, + unsigned int *) + = val; + break; + case rank_long: + *va_arg(ap, + unsigned long *) + = val; + break; + case rank_longlong: + *va_arg(ap, + unsigned long + long *) = val; + break; + case rank_ptr: + *va_arg(ap, void **) = + (void *) + (uint32_t*)val; + break; + } + } + break; + + case 'c': /* Character */ + /* Default width == 1 */ + width = (flags & FL_WIDTH) ? width : 1; + if (flags & FL_SPLAT) { + while (width--) { + if (!*q) { + bail = bail_eof; + break; + } + } + } else { + sarg = va_arg(ap, char *); + while (width--) { + if (!*q) { + bail = bail_eof; + break; + } + *sarg++ = *q++; + } + if (!bail) + converted++; + } + break; + + case 's': /* String */ + uc = 1; /* Anything nonzero */ + if (flags & FL_SPLAT) { + while (width-- && (uc = *q) && + !isspace(uc)) { + q++; + } + } else { + char *sp; + sp = sarg = va_arg(ap, char *); + while (width-- && (uc = *q) && + !isspace(uc)) { + *sp++ = uc; + q++; + } + if (sarg != sp) { + /* Terminate output */ + *sp = '\0'; + converted++; + } + } + if (!uc) + bail = bail_eof; + break; + + case '[': /* Character range */ + sarg = (flags & FL_SPLAT) ? NULL + : va_arg(ap, char *); + state = st_match_init; + matchinv = 0; + memset(matchmap, 0, sizeof matchmap); + break; + + case '%': /* %% sequence */ + if (*q == '%') + q++; + else + bail = bail_err; + break; + + default: /* Anything else */ + /* Unknown sequence */ + bail = bail_err; + break; + } + } + break; + + case st_match_init: /* Initial state for %[ match */ + if (ch == '^' && !(flags & FL_INV)) { + matchinv = 1; + } else { + set_bit(matchmap, (unsigned char)ch); + state = st_match; + } + break; + + case st_match: /* Main state for %[ match */ + if (ch == ']') { + goto match_run; + } else if (ch == '-') { + range_start = (unsigned char)ch; + state = st_match_range; + } else { + set_bit(matchmap, (unsigned char)ch); + } + break; + + case st_match_range: /* %[ match after - */ + if (ch == ']') { + /* - was last character */ + set_bit(matchmap, (unsigned char)'-'); + goto match_run; + } else { + int i; + for (i = range_start; i < (unsigned char)ch; + i++) + set_bit(matchmap, i); + state = st_match; + } + break; + + match_run: /* Match expression finished */ + qq = q; + uc = 1; /* Anything nonzero */ + while (width && (uc = *q) + && test_bit(matchmap, uc)^matchinv) { + if (sarg) + *sarg++ = uc; + q++; + } + if (q != qq && sarg) { + *sarg = '\0'; + converted++; + } else { + bail = bail_err; + } + if (!uc) + bail = bail_eof; + break; + } + } + + if (bail == bail_eof && !converted) + converted = -1; /* Return EOF (-1) */ + + return converted; +}