diff --git a/Makefile b/Makefile index 692df3a00a4..73e49877ca4 100644 --- a/Makefile +++ b/Makefile @@ -256,7 +256,6 @@ include third_party/nsync/mem/BUILD.mk # │ You can now use stdio include libc/proc/BUILD.mk # │ You can now use threads include libc/dlopen/BUILD.mk # │ You can now use processes include libc/thread/BUILD.mk # │ You can finally call malloc() -include ctl/BUILD.mk # │ include third_party/zlib/BUILD.mk # │ include libc/stdio/BUILD.mk # │ include tool/hello/BUILD.mk # │ @@ -285,6 +284,7 @@ include third_party/ncurses/BUILD.mk # │ include third_party/readline/BUILD.mk # │ include third_party/libunwind/BUILD.mk # | include third_party/libcxxabi/BUILD.mk # | +include ctl/BUILD.mk # │ include third_party/libcxx/BUILD.mk # │ include third_party/openmp/BUILD.mk # │ include third_party/double-conversion/BUILD.mk # │ diff --git a/ctl/BUILD.mk b/ctl/BUILD.mk index 66527b46b30..f30a3c8bcce 100644 --- a/ctl/BUILD.mk +++ b/ctl/BUILD.mk @@ -18,7 +18,11 @@ CTL_A_CHECKS = \ CTL_A_DIRECTDEPS = \ LIBC_INTRIN \ LIBC_MEM \ + LIBC_STDIO \ LIBC_STR \ + THIRD_PARTY_GDTOA \ + THIRD_PARTY_LIBCXXABI \ + THIRD_PARTY_LIBUNWIND \ CTL_A_DEPS := $(call uniq,$(foreach x,$(CTL_A_DIRECTDEPS),$($(x)))) diff --git a/ctl/ios.cc b/ctl/ios.cc new file mode 100644 index 00000000000..37547aecbd1 --- /dev/null +++ b/ctl/ios.cc @@ -0,0 +1,39 @@ +// -*- mode:c++; indent-tabs-mode:nil; c-basic-offset:4; coding:utf-8 -*- +// vi: set et ft=cpp ts=4 sts=4 sw=4 fenc=utf-8 :vi +// +// Copyright 2024 Justine Alexandra Roberts Tunney +// +// Permission to use, copy, modify, and/or distribute this software for +// any purpose with or without fee is hereby granted, provided that the +// above copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL +// WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE +// AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL +// DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR +// PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +// TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +#include "ios.h" + +namespace ctl { + +ios::ios(FILE* file) : file_(file) +{ +} + +ios::~ios() = default; + +ios& +ios::operator=(ios&& other) noexcept +{ + if (this != &other) { + file_ = other.file_; + other.file_ = nullptr; + } + return *this; +} + +} // namespace ctl diff --git a/ctl/ios.h b/ctl/ios.h new file mode 100644 index 00000000000..31e82e258ae --- /dev/null +++ b/ctl/ios.h @@ -0,0 +1,27 @@ +// -*-mode:c++;indent-tabs-mode:nil;c-basic-offset:4;tab-width:8;coding:utf-8-*- +// vi: set et ft=cpp ts=4 sts=4 sw=4 fenc=utf-8 :vi +#ifndef CTL_IOS_H_ +#define CTL_IOS_H_ +#include "ios_base.h" + +struct FILE; + +namespace ctl { + +class ios : public ios_base +{ + public: + explicit ios(FILE*); + virtual ~ios(); + + protected: + ios& operator=(ios&&) noexcept; + FILE* file_; + + private: + ios(const ios&) = delete; +}; + +} // namespace ctl + +#endif // CTL_IOS_H_ diff --git a/ctl/ios_base.cc b/ctl/ios_base.cc new file mode 100644 index 00000000000..8ff97b66978 --- /dev/null +++ b/ctl/ios_base.cc @@ -0,0 +1,118 @@ +// -*- mode:c++; indent-tabs-mode:nil; c-basic-offset:4; coding:utf-8 -*- +// vi: set et ft=cpp ts=4 sts=4 sw=4 fenc=utf-8 :vi +// +// Copyright 2024 Justine Alexandra Roberts Tunney +// +// Permission to use, copy, modify, and/or distribute this software for +// any purpose with or without fee is hereby granted, provided that the +// above copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL +// WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE +// AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL +// DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR +// PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +// TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +#include "ios_base.h" + +namespace ctl { + +ios_base::ios_base() : state_(goodbit), flags_(skipws | dec) +{ +} + +ios_base::~ios_base() = default; + +ios_base::fmtflags +ios_base::flags() const +{ + return static_cast(flags_); +} + +ios_base::fmtflags +ios_base::flags(fmtflags fmtfl) +{ + int old = flags_; + flags_ = fmtfl; + return static_cast(old); +} + +ios_base::fmtflags +ios_base::setf(fmtflags fmtfl) +{ + int old = flags_; + flags_ |= fmtfl; + return static_cast(old); +} + +ios_base::fmtflags +ios_base::setf(fmtflags fmtfl, fmtflags mask) +{ + int old = flags_; + flags_ = (flags_ & ~mask) | (fmtfl & mask); + return static_cast(old); +} + +void +ios_base::unsetf(fmtflags mask) +{ + flags_ &= ~mask; +} + +ios_base::iostate +ios_base::rdstate() const +{ + return static_cast(state_); +} + +void +ios_base::clear(int state) +{ + state_ = state; +} + +void +ios_base::setstate(int state) +{ + state_ |= state; +} + +bool +ios_base::good() const +{ + return state_ == goodbit; +} + +bool +ios_base::eof() const +{ + return (state_ & eofbit) != 0; +} + +bool +ios_base::fail() const +{ + return (state_ & (failbit | badbit)) != 0; +} + +bool +ios_base::bad() const +{ + return (state_ & badbit) != 0; +} + +bool +ios_base::operator!() const +{ + return fail(); +} + +ios_base::operator bool() const +{ + return !fail(); +} + +} // namespace ctl diff --git a/ctl/ios_base.h b/ctl/ios_base.h new file mode 100644 index 00000000000..e521149592b --- /dev/null +++ b/ctl/ios_base.h @@ -0,0 +1,86 @@ +// -*-mode:c++;indent-tabs-mode:nil;c-basic-offset:4;tab-width:8;coding:utf-8-*- +// vi: set et ft=cpp ts=4 sts=4 sw=4 fenc=utf-8 :vi +#ifndef CTL_IOS_BASE_H_ +#define CTL_IOS_BASE_H_ + +namespace ctl { + +class ios_base +{ + public: + typedef size_t streamsize; + + enum iostate + { + goodbit = 0, + badbit = 1, + failbit = 2, + eofbit = 4 + }; + + enum fmtflags + { + boolalpha = 1 << 0, + dec = 1 << 1, + fixed = 1 << 2, + hex = 1 << 3, + internal = 1 << 4, + left = 1 << 5, + oct = 1 << 6, + right = 1 << 7, + scientific = 1 << 8, + showbase = 1 << 9, + showpoint = 1 << 10, + showpos = 1 << 11, + skipws = 1 << 12, + unitbuf = 1 << 13, + uppercase = 1 << 14, + adjustfield = left | right | internal, + basefield = dec | oct | hex, + floatfield = scientific | fixed + }; + + enum openmode + { + app = 1 << 0, + binary = 1 << 1, + in = 1 << 2, + out = 1 << 3, + trunc = 1 << 4, + ate = 1 << 5 + }; + + protected: + ios_base(); + virtual ~ios_base(); + + int state_; + int flags_; + + public: + fmtflags flags() const; + fmtflags flags(fmtflags); + fmtflags setf(fmtflags); + fmtflags setf(fmtflags, fmtflags); + void unsetf(fmtflags); + + iostate rdstate() const; + void clear(int = goodbit); + void setstate(int); + + bool good() const; + bool eof() const; + bool fail() const; + bool bad() const; + + bool operator!() const; + explicit operator bool() const; + + private: + ios_base(const ios_base&) = delete; + ios_base& operator=(const ios_base&) = delete; +}; + +} // namespace ctl + +#endif // CTL_IOS_BASE_H_ diff --git a/ctl/is_convertible.h b/ctl/is_convertible.h index 9b81747e895..69a105ad370 100644 --- a/ctl/is_convertible.h +++ b/ctl/is_convertible.h @@ -2,7 +2,6 @@ // vi: set et ft=cpp ts=4 sts=4 sw=4 fenc=utf-8 :vi #ifndef CTL_IS_CONVERTIBLE_H_ #define CTL_IS_CONVERTIBLE_H_ - #include "ctl/integral_constant.h" #include "ctl/void_t.h" diff --git a/ctl/istream.cc b/ctl/istream.cc new file mode 100644 index 00000000000..bb2837a0bc3 --- /dev/null +++ b/ctl/istream.cc @@ -0,0 +1,237 @@ +// -*- mode:c++; indent-tabs-mode:nil; c-basic-offset:4; coding:utf-8 -*- +// vi: set et ft=cpp ts=4 sts=4 sw=4 fenc=utf-8 :vi +// +// Copyright 2024 Justine Alexandra Roberts Tunney +// +// Permission to use, copy, modify, and/or distribute this software for +// any purpose with or without fee is hereby granted, provided that the +// above copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL +// WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE +// AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL +// DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR +// PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +// TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +#include "istream.h" +#include "libc/fmt/conv.h" +#include "libc/stdio/stdio.h" +#include "libc/str/str.h" +#include "numeric_limits.h" +#include "string.h" +#include "utility.h" + +namespace ctl { + +istream cin(stdin); + +istream::~istream() = default; + +istream::istream(FILE* file) : ios(file), gcount_(0) +{ +} + +istream::istream(istream&& other) noexcept + : ios(other.file_), gcount_(other.gcount_) +{ + other.file_ = nullptr; + other.gcount_ = 0; +} + +istream& +istream::operator=(istream&& other) noexcept +{ + if (this != &other) { + ios::operator=(ctl::move(other)); + gcount_ = other.gcount_; + other.gcount_ = 0; + } + return *this; +} + +istream& +istream::operator>>(char& c) +{ + gcount_ = 0; + int ch = fgetc(file_); + if (ch == EOF) { + setstate(eofbit | failbit); + } else { + c = static_cast(ch); + gcount_ = 1; + } + return *this; +} + +istream& +istream::operator>>(int& n) +{ + if (fscanf(file_, "%d", &n) != 1) + setstate(failbit); + return *this; +} + +istream& +istream::operator>>(long& n) +{ + if (fscanf(file_, "%ld", &n) != 1) + setstate(failbit); + return *this; +} + +istream& +istream::operator>>(double& d) +{ + if (fscanf(file_, "%f", &d) != 1) + setstate(failbit); + return *this; +} + +istream& +istream::operator>>(ctl::string& s) +{ + char buffer[1024]; + if (fscanf(file_, "%1023s", buffer) == 1) { + s = buffer; + } else { + setstate(failbit); + } + return *this; +} + +istream& +istream::operator>>(bool& b) +{ + char buffer[6]; + if (fscanf(file_, "%5s", buffer) == 1) { + if (strcmp(buffer, "true") == 0 || strcmp(buffer, "1") == 0) { + b = true; + } else if (strcmp(buffer, "false") == 0 || strcmp(buffer, "0") == 0) { + b = false; + } else { + setstate(failbit); + } + } else { + setstate(failbit); + } + return *this; +} + +istream& +istream::operator>>(istream& (*manip)(istream&)) +{ + return manip(*this); +} + +int +istream::get() +{ + gcount_ = 0; + int ch = fgetc(file_); + if (ch == EOF) { + setstate(eofbit); + } else { + gcount_ = 1; + } + return ch; +} + +istream& +istream::get(char& c) +{ + int ch = get(); + if (ch != EOF) + c = static_cast(ch); + return *this; +} + +istream& +istream::getline(char* s, streamsize n) +{ + return getline(s, n, '\n'); +} + +istream& +istream::getline(char* s, streamsize n, char delim) +{ + gcount_ = 0; + if (n <= 0) { + setstate(failbit); + return *this; + } + while (gcount_ < n - 1) { + int ch = fgetc(file_); + if (ch == EOF) { + setstate(eofbit); + break; + } + if (ch == delim) + break; + s[gcount_++] = static_cast(ch); + } + s[gcount_] = '\0'; + if (gcount_ == 0) + setstate(failbit); + return *this; +} + +istream& +istream::read(char* s, streamsize n) +{ + gcount_ = fread(s, 1, n, file_); + if (gcount_ < n) { + setstate(eofbit); + if (gcount_ == 0) + setstate(failbit); + } + return *this; +} + +istream::streamsize +istream::gcount() const +{ + return gcount_; +} + +int +istream::peek() +{ + int ch = fgetc(file_); + if (ch != EOF) { + ungetc(ch, file_); + } else { + setstate(eofbit); + } + return ch; +} + +istream& +istream::ignore(streamsize n, int delim) +{ + gcount_ = 0; + while (gcount_ < n) { + int ch = fgetc(file_); + if (ch == EOF) { + setstate(eofbit); + break; + } + ++gcount_; + if (ch == delim) + break; + } + return *this; +} + +istream& +ws(istream& is) +{ + int ch; + while ((ch = is.peek()) != EOF && isspace(ch)) + is.get(); + return is; +} + +} // namespace ctl diff --git a/ctl/istream.h b/ctl/istream.h new file mode 100644 index 00000000000..2ec23284d65 --- /dev/null +++ b/ctl/istream.h @@ -0,0 +1,53 @@ +// -*-mode:c++;indent-tabs-mode:nil;c-basic-offset:4;tab-width:8;coding:utf-8-*- +// vi: set et ft=cpp ts=4 sts=4 sw=4 fenc=utf-8 :vi +#ifndef CTL_ISTREAM_H_ +#define CTL_ISTREAM_H_ +#include "ios.h" + +namespace ctl { + +struct string; + +class istream : public ios +{ + public: + istream() = delete; + explicit istream(FILE*); + virtual ~istream(); + + istream& operator>>(char&); + istream& operator>>(int&); + istream& operator>>(long&); + istream& operator>>(double&); + istream& operator>>(bool&); + istream& operator>>(string&); + istream& operator>>(istream& (*)(istream&)); + + int get(); + istream& get(char&); + istream& getline(char*, streamsize); + istream& getline(char*, streamsize, char); + istream& read(char*, streamsize); + streamsize gcount() const; + + int peek(); + istream& ignore(streamsize = 1, int = -1); + + istream(istream&&) noexcept; + istream& operator=(istream&&) noexcept; + + private: + streamsize gcount_; + + istream(const istream&) = delete; + istream& operator=(const istream&) = delete; +}; + +extern istream cin; + +istream& +ws(istream& is); + +} // namespace ctl + +#endif // CTL_ISTREAM_H_ diff --git a/ctl/istringstream.cc b/ctl/istringstream.cc new file mode 100644 index 00000000000..5674c620abc --- /dev/null +++ b/ctl/istringstream.cc @@ -0,0 +1,221 @@ +// -*- mode:c++; indent-tabs-mode:nil; c-basic-offset:4; coding:utf-8 -*- +// vi: set et ft=cpp ts=4 sts=4 sw=4 fenc=utf-8 :vi +// +// Copyright 2024 Justine Alexandra Roberts Tunney +// +// Permission to use, copy, modify, and/or distribute this software for +// any purpose with or without fee is hereby granted, provided that the +// above copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL +// WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE +// AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL +// DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR +// PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +// TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +#include "istringstream.h" +#include "libc/fmt/conv.h" +#include "libc/str/str.h" + +namespace ctl { + +istringstream::istringstream() : buffer_(), read_pos_(0) +{ +} + +istringstream::istringstream(const string_view& str) + : buffer_(str), read_pos_(0) +{ +} + +string +istringstream::str() const +{ + return buffer_; +} + +void +istringstream::str(const string& s) +{ + buffer_ = s; + read_pos_ = 0; + clear(); +} + +istringstream& +istringstream::operator>>(char& c) +{ + if (good() && read_pos_ < buffer_.size()) { + c = buffer_[read_pos_++]; + } else { + setstate(ios_base::failbit); + } + return *this; +} + +istringstream& +istringstream::operator>>(char* s) +{ + if (!good()) + return *this; + + while (read_pos_ < buffer_.size() && isspace(buffer_[read_pos_])) + ++read_pos_; + + size_t start = read_pos_; + while (read_pos_ < buffer_.size() && !isspace(buffer_[read_pos_])) { + s[read_pos_ - start] = buffer_[read_pos_]; + ++read_pos_; + } + s[read_pos_ - start] = '\0'; + + if (start == read_pos_) { + setstate(ios_base::failbit); + } else if (read_pos_ == buffer_.size()) { + setstate(ios_base::eofbit); + } + + return *this; +} + +istringstream& +istringstream::operator>>(string& s) +{ + if (!good()) + return *this; + + s.clear(); + while (read_pos_ < buffer_.size() && isspace(buffer_[read_pos_])) + ++read_pos_; + + while (read_pos_ < buffer_.size() && !isspace(buffer_[read_pos_])) { + s.push_back(buffer_[read_pos_]); + ++read_pos_; + } + + if (s.empty()) { + setstate(ios_base::failbit); + } else if (read_pos_ == buffer_.size()) { + setstate(ios_base::eofbit); + } + + return *this; +} + +template +istringstream& +istringstream::read_numeric(T& value) +{ + if (!good()) + return *this; + + while (read_pos_ < buffer_.size() && isspace(buffer_[read_pos_])) + ++read_pos_; + + // size_t start = read_pos_; + bool is_negative = false; + if (read_pos_ < buffer_.size() && + (buffer_[read_pos_] == '-' || buffer_[read_pos_] == '+')) { + is_negative = (buffer_[read_pos_] == '-'); + ++read_pos_; + } + + T result = 0; + bool valid = false; + while (read_pos_ < buffer_.size() && isdigit(buffer_[read_pos_])) { + result = result * 10 + (buffer_[read_pos_] - '0'); + ++read_pos_; + valid = true; + } + + if (valid) { + value = is_negative ? -result : result; + if (read_pos_ == buffer_.size()) + setstate(ios_base::eofbit); + } else { + setstate(ios_base::failbit); + } + + return *this; +} + +istringstream& +istringstream::operator>>(short& n) +{ + return read_numeric(n); +} + +istringstream& +istringstream::operator>>(unsigned short& n) +{ + return read_numeric(n); +} + +istringstream& +istringstream::operator>>(int& n) +{ + return read_numeric(n); +} + +istringstream& +istringstream::operator>>(unsigned int& n) +{ + return read_numeric(n); +} + +istringstream& +istringstream::operator>>(long& n) +{ + return read_numeric(n); +} + +istringstream& +istringstream::operator>>(unsigned long& n) +{ + return read_numeric(n); +} + +istringstream& +istringstream::operator>>(float& f) +{ + if (!good()) + return *this; + + char* end; + f = strtof(buffer_.c_str() + read_pos_, &end); + + if (end == buffer_.c_str() + read_pos_) { + setstate(ios_base::failbit); + } else { + read_pos_ = end - buffer_.c_str(); + if (read_pos_ == buffer_.size()) + setstate(ios_base::eofbit); + } + + return *this; +} + +istringstream& +istringstream::operator>>(double& d) +{ + if (!good()) + return *this; + + char* end; + d = strtod(buffer_.c_str() + read_pos_, &end); + + if (end == buffer_.c_str() + read_pos_) { + setstate(ios_base::failbit); + } else { + read_pos_ = end - buffer_.c_str(); + if (read_pos_ == buffer_.size()) + setstate(ios_base::eofbit); + } + + return *this; +} + +} // namespace ctl diff --git a/ctl/istringstream.h b/ctl/istringstream.h new file mode 100644 index 00000000000..b3c95e3d754 --- /dev/null +++ b/ctl/istringstream.h @@ -0,0 +1,41 @@ +// -*-mode:c++;indent-tabs-mode:nil;c-basic-offset:4;tab-width:8;coding:utf-8-*- +// vi: set et ft=cpp ts=4 sts=4 sw=4 fenc=utf-8 :vi +#ifndef CTL_ISTRINGSTREAM_H_ +#define CTL_ISTRINGSTREAM_H_ +#include "ios_base.h" +#include "string.h" + +namespace ctl { + +class istringstream : public ios_base +{ + public: + istringstream(); + explicit istringstream(const string_view&); + + string str() const; + void str(const string&); + + istringstream& operator>>(char&); + istringstream& operator>>(char*); + istringstream& operator>>(string&); + istringstream& operator>>(short&); + istringstream& operator>>(unsigned short&); + istringstream& operator>>(int&); + istringstream& operator>>(unsigned int&); + istringstream& operator>>(long&); + istringstream& operator>>(unsigned long&); + istringstream& operator>>(float&); + istringstream& operator>>(double&); + + private: + string buffer_; + size_t read_pos_; + + template + istringstream& read_numeric(T&); +}; + +} // namespace ctl + +#endif // CTL_ISTRINGSTREAM_H_ diff --git a/ctl/ostream.cc b/ctl/ostream.cc new file mode 100644 index 00000000000..d57e1190630 --- /dev/null +++ b/ctl/ostream.cc @@ -0,0 +1,166 @@ +// -*- mode:c++; indent-tabs-mode:nil; c-basic-offset:4; coding:utf-8 -*- +// vi: set et ft=cpp ts=4 sts=4 sw=4 fenc=utf-8 :vi +// +// Copyright 2024 Justine Alexandra Roberts Tunney +// +// Permission to use, copy, modify, and/or distribute this software for +// any purpose with or without fee is hereby granted, provided that the +// above copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL +// WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE +// AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL +// DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR +// PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +// TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +#include "ostream.h" +#include "libc/stdio/stdio.h" +#include "string_view.h" + +namespace ctl { + +ostream cout(stdout); +ostream cerr(stderr); + +ostream::~ostream() = default; + +ostream::ostream(FILE* file) : ios(file) +{ +} + +ostream::ostream(ostream&& other) noexcept : ios(other.file_) +{ + other.file_ = nullptr; +} + +ostream& +ostream::operator=(ostream&& other) noexcept +{ + if (this != &other) { + file_ = other.file_; + other.file_ = nullptr; + } + return *this; +} + +ostream& +ostream::operator<<(const char* str) +{ + if (good() && str) + if (fprintf(file_, "%s", str) < 0) + setstate(badbit); + return *this; +} + +ostream& +ostream::operator<<(char c) +{ + if (good()) + if (fputc(c, file_) == EOF) + setstate(badbit); + return *this; +} + +ostream& +ostream::operator<<(int n) +{ + if (good()) + if (fprintf(file_, "%d", n) < 0) + setstate(badbit); + return *this; +} + +ostream& +ostream::operator<<(long n) +{ + if (good()) + if (fprintf(file_, "%ld", n) < 0) + setstate(badbit); + return *this; +} + +ostream& +ostream::operator<<(double d) +{ + if (good()) + if (fprintf(file_, "%f", d) < 0) + setstate(badbit); + return *this; +} + +ostream& +ostream::operator<<(const string_view& s) +{ + if (good()) + if (fprintf(file_, "%.*s", (int)s.size(), s.data()) < 0) + setstate(badbit); + return *this; +} + +ostream& +ostream::operator<<(bool b) +{ + if (good()) { + const char* value = + (flags() & boolalpha) ? (b ? "true" : "false") : (b ? "1" : "0"); + if (fprintf(file_, "%s", value) < 0) + setstate(badbit); + } + return *this; +} + +ostream& +ostream::operator<<(ostream& (*manip)(ostream&)) +{ + return manip(*this); +} + +ostream& +ostream::put(char c) +{ + if (good()) + if (fputc(c, file_) == EOF) + setstate(badbit); + return *this; +} + +ostream& +ostream::write(const char* s, streamsize n) +{ + if (good()) + if (fwrite(s, 1, n, file_) != static_cast(n)) + setstate(badbit); + return *this; +} + +ostream& +ostream::flush() +{ + if (good()) + if (fflush(file_) != 0) + setstate(badbit); + return *this; +} + +ostream& +endl(ostream& os) +{ + return os.put('\n').flush(); +} + +ostream& +ends(ostream& os) +{ + return os.put('\0'); +} + +ostream& +flush(ostream& os) +{ + return os.flush(); +} + +} // namespace ctl diff --git a/ctl/ostream.h b/ctl/ostream.h new file mode 100644 index 00000000000..3b30421a1be --- /dev/null +++ b/ctl/ostream.h @@ -0,0 +1,53 @@ +// -*-mode:c++;indent-tabs-mode:nil;c-basic-offset:4;tab-width:8;coding:utf-8-*- +// vi: set et ft=cpp ts=4 sts=4 sw=4 fenc=utf-8 :vi +#ifndef CTL_OSTREAM_H_ +#define CTL_OSTREAM_H_ +#include "ios.h" + +namespace ctl { + +struct string_view; + +class ostream : public ios +{ + public: + ostream() = delete; + explicit ostream(FILE*); + virtual ~ostream(); + + ostream& operator<<(const char*); + ostream& operator<<(char); + ostream& operator<<(int); + ostream& operator<<(long); + ostream& operator<<(double); + ostream& operator<<(const string_view&); + ostream& operator<<(bool); + ostream& operator<<(ostream& (*)(ostream&)); + + ostream& put(char); + ostream& write(const char*, streamsize); + ostream& flush(); + + ostream(ostream&&) noexcept; + ostream& operator=(ostream&&) noexcept; + + private: + ostream(const ostream&) = delete; + ostream& operator=(const ostream&) = delete; +}; + +extern ostream cout; +extern ostream cerr; + +ostream& +endl(ostream&); + +ostream& +ends(ostream&); + +ostream& +flush(ostream&); + +} // namespace ctl + +#endif // CTL_OSTREAM_H_ diff --git a/ctl/ostringstream.cc b/ctl/ostringstream.cc new file mode 100644 index 00000000000..b322c01e8c4 --- /dev/null +++ b/ctl/ostringstream.cc @@ -0,0 +1,140 @@ +// -*- mode:c++; indent-tabs-mode:nil; c-basic-offset:4; coding:utf-8 -*- +// vi: set et ft=cpp ts=4 sts=4 sw=4 fenc=utf-8 :vi +// +// Copyright 2024 Justine Alexandra Roberts Tunney +// +// Permission to use, copy, modify, and/or distribute this software for +// any purpose with or without fee is hereby granted, provided that the +// above copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL +// WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE +// AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL +// DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR +// PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +// TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +#include "ostringstream.h" +#include "libc/fmt/itoa.h" +#include "libc/stdio/stdio.h" + +namespace ctl { + +ostringstream::ostringstream() : buffer_(), write_pos_(0) +{ +} + +ostringstream::ostringstream(const string_view& str) + : buffer_(str), write_pos_(0) +{ +} + +string +ostringstream::str() const +{ + return buffer_; +} + +void +ostringstream::str(const string& s) +{ + buffer_ = s; + write_pos_ = 0; +} + +void +ostringstream::clear() +{ + ios_base::clear(); +} + +ostringstream& +ostringstream::operator<<(char c) +{ + if (good()) { + if (write_pos_ < buffer_.size()) { + buffer_[write_pos_++] = c; + } else { + buffer_.push_back(c); + ++write_pos_; + } + } + return *this; +} + +ostringstream& +ostringstream::operator<<(const string_view& s) +{ + if (good()) { + if (write_pos_ + s.size() <= buffer_.size()) { + buffer_.replace(write_pos_, s.size(), s); + } else { + buffer_.replace(write_pos_, buffer_.size() - write_pos_, s); + buffer_.append(s.substr(buffer_.size() - write_pos_)); + } + write_pos_ += s.size(); + } + return *this; +} + +ostringstream& +ostringstream::operator<<(int n) +{ + char temp[12]; + if (good()) + *this << string_view(temp, FormatInt32(temp, n) - temp); + return *this; +} + +ostringstream& +ostringstream::operator<<(unsigned int n) +{ + char temp[12]; + if (good()) + *this << string_view(temp, FormatUint32(temp, n) - temp); + return *this; +} + +ostringstream& +ostringstream::operator<<(long n) +{ + char temp[21]; + if (good()) + *this << string_view(temp, FormatInt64(temp, n) - temp); + return *this; +} + +ostringstream& +ostringstream::operator<<(unsigned long n) +{ + char temp[21]; + if (good()) + *this << string_view(temp, FormatUint64(temp, n) - temp); + return *this; +} + +ostringstream& +ostringstream::operator<<(float f) +{ + if (good()) { + char temp[32]; + int len = snprintf(temp, sizeof(temp), "%g", f); + *this << string_view(temp, len); + } + return *this; +} + +ostringstream& +ostringstream::operator<<(double d) +{ + if (good()) { + char temp[32]; + int len = snprintf(temp, sizeof(temp), "%g", d); + *this << string_view(temp, len); + } + return *this; +} + +} // namespace ctl diff --git a/ctl/ostringstream.h b/ctl/ostringstream.h new file mode 100644 index 00000000000..09face624df --- /dev/null +++ b/ctl/ostringstream.h @@ -0,0 +1,36 @@ +// -*-mode:c++;indent-tabs-mode:nil;c-basic-offset:4;tab-width:8;coding:utf-8-*- +// vi: set et ft=cpp ts=4 sts=4 sw=4 fenc=utf-8 :vi +#ifndef CTL_OSTRINGSTREAM_H_ +#define CTL_OSTRINGSTREAM_H_ +#include "ios_base.h" +#include "string.h" + +namespace ctl { + +class ostringstream : public ios_base +{ + public: + ostringstream(); + explicit ostringstream(const string_view&); + + string str() const; + void str(const string& s); + void clear(); + + ostringstream& operator<<(char); + ostringstream& operator<<(const string_view&); + ostringstream& operator<<(int); + ostringstream& operator<<(unsigned int); + ostringstream& operator<<(long); + ostringstream& operator<<(unsigned long); + ostringstream& operator<<(float); + ostringstream& operator<<(double); + + private: + string buffer_; + size_t write_pos_; +}; + +} // namespace ctl + +#endif // CTL_OSTRINGSTREAM_H_ diff --git a/ctl/utility.h b/ctl/utility.h index 464e04f6411..547580f8180 100644 --- a/ctl/utility.h +++ b/ctl/utility.h @@ -1,7 +1,7 @@ // -*-mode:c++;indent-tabs-mode:nil;c-basic-offset:4;tab-width:8;coding:utf-8-*- // vi: set et ft=cpp ts=4 sts=4 sw=4 fenc=utf-8 :vi -#ifndef COSMOPOLITAN_CTL_UTILITY_H_ -#define COSMOPOLITAN_CTL_UTILITY_H_ +#ifndef CTL_UTILITY_H_ +#define CTL_UTILITY_H_ #include "remove_reference.h" namespace ctl { @@ -66,4 +66,4 @@ declval() noexcept; } // namespace ctl -#endif // COSMOPOLITAN_CTL_UTILITY_H_ +#endif // CTL_UTILITY_H_ diff --git a/libc/intrin/strace.internal.h b/libc/intrin/strace.internal.h index 3c521857fe6..adda49caa90 100644 --- a/libc/intrin/strace.internal.h +++ b/libc/intrin/strace.internal.h @@ -5,7 +5,7 @@ #define SYSDEBUG 0 #endif -#define _NTTRACE 0 /* not configurable w/ flag yet */ +#define _NTTRACE 1 /* not configurable w/ flag yet */ #define _POLLTRACE 0 /* not configurable w/ flag yet */ #define _DATATRACE 1 /* not configurable w/ flag yet */ #define _LOCKTRACE 0 /* not configurable w/ flag yet */ diff --git a/test/ctl/BUILD.mk b/test/ctl/BUILD.mk index 2bdcf538131..f644eb2cb7b 100644 --- a/test/ctl/BUILD.mk +++ b/test/ctl/BUILD.mk @@ -14,10 +14,11 @@ TEST_CTL_TESTS = $(TEST_CTL_COMS:%=%.ok) TEST_CTL_DIRECTDEPS = \ CTL \ LIBC_CALLS \ - LIBC_STDIO \ LIBC_INTRIN \ + LIBC_LOG \ LIBC_MEM \ LIBC_STDIO \ + LIBC_STDIO \ LIBC_THREAD \ THIRD_PARTY_LIBCXX \ THIRD_PARTY_LIBCXXABI \ diff --git a/test/ctl/istringstream_test.cc b/test/ctl/istringstream_test.cc new file mode 100644 index 00000000000..08019c93c42 --- /dev/null +++ b/test/ctl/istringstream_test.cc @@ -0,0 +1,163 @@ +// -*- mode:c++; indent-tabs-mode:nil; c-basic-offset:4; coding:utf-8 -*- +// vi: set et ft=cpp ts=4 sts=4 sw=4 fenc=utf-8 :vi +// +// Copyright 2024 Justine Alexandra Roberts Tunney +// +// Permission to use, copy, modify, and/or distribute this software for +// any purpose with or without fee is hereby granted, provided that the +// above copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL +// WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE +// AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL +// DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR +// PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +// TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +#include "ctl/istringstream.h" +#include "libc/mem/leaks.h" + +// #include +// #define ctl std + +int +main() +{ + + // Test default constructor + { + ctl::istringstream iss; + if (iss.eof()) + return 1; + if (!iss.good()) + return 1; + } + + // Test constructor with initial string + { + ctl::istringstream iss("Hello, world!"); + if (iss.str() != "Hello, world!") + return 2; + } + + // Test reading a char + { + ctl::istringstream iss("A"); + char c; + iss >> c; + if (c != 'A' || !iss.good()) + return 3; + } + + // Test reading a string + { + ctl::istringstream iss("Hello World"); + ctl::string s; + iss >> s; + if (s != "Hello" || !iss.good()) + return 4; + } + + // Test reading multiple strings + { + ctl::istringstream iss("One Two Three"); + ctl::string s1, s2, s3; + iss >> s1 >> s2 >> s3; + if (s1 != "One" || s2 != "Two" || s3 != "Three" || iss.good() || + !iss.eof()) + return 5; + } + + // Test reading integers + { + ctl::istringstream iss("123 -456 789"); + int a, b, c; + iss >> a >> b >> c; + if (a != 123 || b != -456 || c != 789 || iss.good() || !iss.eof()) + return 6; + } + + // Test reading floating-point numbers + { + ctl::istringstream iss("3.14 -2.718"); + float f; + double d; + iss >> f >> d; + if (f != 3.14f || d != -2.718 || iss.good() || !iss.eof()) + return 7; + } + + // Test reading past the end of the stream + { + ctl::istringstream iss("42"); + int n; + iss >> n; + if (n != 42 || iss.good() || !iss.eof()) + return 8; + iss >> n; + if (iss.good()) + return 9; + if (!iss.eof()) + return 10; + } + + // Test reading invalid data + { + ctl::istringstream iss("not_a_number"); + int n; + iss >> n; + if (iss.good() || !iss.fail()) + return 11; + } + + // Test str() setter + { + ctl::istringstream iss; + iss.str("New content"); + if (iss.str() != "New content" || !iss.good()) + return 12; + } + + // Test reading after setting a new string + { + ctl::istringstream iss("Old content"); + iss.str("New content"); + ctl::string s; + iss >> s; + if (s != "New" || !iss.good()) + return 13; + } + + // Test reading a mixture of types + { + ctl::istringstream iss("42 3.14 Hello"); + int i; + double d; + ctl::string s; + iss >> i >> d >> s; + if (i != 42 || d != 3.14 || s != "Hello" || iss.good() || !iss.eof()) + return 14; + } + + // Test reading with leading whitespace + { + ctl::istringstream iss(" 42"); + int n; + iss >> n; + if (n != 42 || iss.good() || !iss.eof()) + return 15; + } + + // Test clear() only affects error state, not content + { + ctl::istringstream iss("Test"); + iss.setstate(ctl::ios_base::failbit); + iss.clear(); + if (!iss.good() || iss.str() != "Test") + return 16; + } + + CheckForMemoryLeaks(); +} diff --git a/test/ctl/ostringstream_test.cc b/test/ctl/ostringstream_test.cc new file mode 100644 index 00000000000..da45a6a0304 --- /dev/null +++ b/test/ctl/ostringstream_test.cc @@ -0,0 +1,140 @@ +// -*- mode:c++; indent-tabs-mode:nil; c-basic-offset:4; coding:utf-8 -*- +// vi: set et ft=cpp ts=4 sts=4 sw=4 fenc=utf-8 :vi +// +// Copyright 2024 Justine Alexandra Roberts Tunney +// +// Permission to use, copy, modify, and/or distribute this software for +// any purpose with or without fee is hereby granted, provided that the +// above copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL +// WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE +// AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL +// DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR +// PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +// TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +#include "ctl/ostringstream.h" +#include "libc/mem/leaks.h" + +// #include +// #define ctl std + +int +main() +{ + + // Test default constructor and basic string insertion + { + ctl::ostringstream oss; + oss << "Hello, world!"; + if (oss.str() != "Hello, world!") + return 1; + } + + // Test constructor with initial string (overwrite mode) + { + ctl::ostringstream oss("Initial content"); + oss << "New"; + if (oss.str() != "Newtial content") + return 2; + } + + // Test overwrite and append behavior + { + ctl::ostringstream oss("Hello, world!"); + oss << "Hi"; + if (oss.str() != "Hillo, world!") + return 3; + oss << " Earth"; + if (oss.str() != "Hi Earthorld!") + return 4; + } + + // Test multiple insertions of different types + { + ctl::ostringstream oss; + oss << "Int: " << 42 << ", Float: " << 3.14f << ", Double: " << 2.718; + if (oss.str() != "Int: 42, Float: 3.14, Double: 2.718") + return 5; + } + + // Test char insertion + { + ctl::ostringstream oss("ABCDEF"); + oss << 'X' << 'Y' << 'Z'; + if (oss.str() != "XYZDEF") + return 6; + } + + // Test unsigned types + { + ctl::ostringstream oss("Numbers: "); + unsigned int ui = 12345; + unsigned long ul = 67890UL; + oss << ui << " " << ul; + if (oss.str() != "12345 67890") + return 7; + } + + // Test long type + { + ctl::ostringstream oss("Long: "); + long l = -9876543210L; + oss << l; + if (oss.str() != "-9876543210") + return 8; + } + + // Test clear() method + { + ctl::ostringstream oss("not that kind of clear"); + oss.clear(); + if (!oss.good() || oss.str() != "not that kind of clear") + return 9; + } + + // Test str() setter method + { + ctl::ostringstream oss; + oss.str("New content"); + oss << "Old"; + if (oss.str() != "Old content") + return 10; + } + + // Test chaining of insertion operators + { + ctl::ostringstream oss("Start: "); + oss << "Chain " << 1 << " " << 2.0 << " " << 'Z'; + if (oss.str() != "Chain 1 2 Z") + return 11; + } + + // Test insertion when stream is not in good state + { + ctl::ostringstream oss("Original"); + oss.setstate(ctl::ios_base::failbit); + oss << "This should not be inserted"; + if (oss.str() != "Original") + return 12; + } + + // Test with larger amounts of data + { + ctl::ostringstream oss("Prefix: "); + for (int i = 0; i < 1000; ++i) { + oss << "Line " << i << "\n"; + } + ctl::string result = oss.str(); + if (result.substr(0, 14) != "Line 0\nLine 1\n") + return 13; + if (result.substr(result.length() - 8) != "ine 999\n") + return 14; + } + + CheckForMemoryLeaks(); + return 0; +}