245 lines
4.9 KiB
C++

#include <stdio.h>
#include <algorithm>
#include <string>
#include <stdarg.h>
#include "os/tools/printf_helper.hpp"
#include "os/tools/ringbuffer.hpp"
#if defined(__baos_kernel_source__)
#include "os/serial.hpp"
#include "os/tty.hpp"
#else
#include "os/syscall.hpp"
#endif
struct __file
{
protected:
static constexpr unsigned BUFFER_SIZE = 4096;
baos::RingBuffer<char, BUFFER_SIZE> mBuffer;
bool mEof = false;
bool mError = false;
protected:
virtual bool underflow() noexcept = 0;
public:
[[nodiscard]] bool isEof() const noexcept { return mEof; }
[[nodiscard]] bool isError() const noexcept { return mError; }
bool readChar(char& outChar) noexcept
{
while (!mBuffer.next(outChar)) {
if (!underflow()) {
return false;
}
}
return true;
}
bool readLine(std::string& outLine) noexcept
{
outLine.clear();
while(true)
{
while (mBuffer.empty()) {
if (!underflow()) {
return false;
}
}
// first check if there is a \n in the stream
std::size_t count = mBuffer.size();
for (std::size_t idx = 0; idx < mBuffer.size(); ++idx)
{
if (mBuffer[idx] == '\n')
{
count = idx + 1;
break;
}
}
outLine.reserve(outLine.size() + count);
for (std::size_t idx = 0; idx < count; ++idx)
{
char chr = '\0';
mBuffer.next(chr);
outLine.append(chr);
}
if (outLine.back() == '\n')
{
return true;
}
}
}
};
namespace
{
struct RegularPrinter
{
void putchar(int chr) noexcept // NOLINT
{
::putchar(chr);
}
};
struct BufferPrinter
{
char* pos = nullptr;
char* end = nullptr;
void putchar(int chr) noexcept // NOLINT
{
if (pos != end)
{
*pos = static_cast<char>(chr);
++pos;
}
}
};
class StdinFile : public __file
{
public:
bool underflow() noexcept override
{
#if defined(__baos_kernel_source__)
for (char chr : tty::readLine()) {
mBuffer.append(chr);
}
return true;
#else
return false; // TODO!!!
#endif
}
};
StdinFile gStdin;
}
extern "C"
{
FILE* __stdin = &gStdin;
// TODO: line-buffering
int putchar(int chr) noexcept
{
#if defined(__baos_kernel_source__)
tty::putChar(static_cast<char>(chr));
if (chr == '\n')
{
serialWrite(PORT_COM1, '\r');
serialWrite(PORT_COM1, '\n');
}
else
{
serialWrite(PORT_COM1, static_cast<std::uint8_t>(chr));
}
return 0;
#else
char asChar = static_cast<char>(chr);
baos::doSyscall(baos::Syscall::FILE_WRITE, 0, &asChar, 1);
return 0;
#endif
}
int puts(const char* str) noexcept
{
#if defined(__baos_kernel_source__)
while (*str)
{
putchar(*str);
++str;
}
putchar('\n');
return 0;
#else
baos::doSyscall(baos::Syscall::FILE_WRITE, 0, str, std::strlen(str));
return 0;
#endif
}
int printf(const char* format, ...) noexcept
{
va_list parameters;
va_start(parameters, format);
const int result = vprintf(format, parameters);
va_end(parameters);
return result;
}
int vprintf(const char* format, va_list vlist) BA_CXX_NOEXCEPT
{
return baos::PrintFHelper<RegularPrinter>().vprintf(format, vlist);
}
int snprintf(char* buffer, size_t bufferSize, const char* format, ...) noexcept
{
va_list parameters;
va_start(parameters, format);
const int result = vsnprintf(buffer, bufferSize, format, parameters);
va_end(parameters);
return result;
}
int vsnprintf(char* buffer, size_t bufferSize, const char* format, va_list vlist) noexcept
{
BufferPrinter printer{
.pos = buffer,
.end = bufferSize > 0 ? buffer + bufferSize - 1 : buffer
};
const int length = baos::PrintFHelper(printer).vprintf(format, vlist);
if (bufferSize > 0)
{
buffer[std::min(static_cast<int>(bufferSize - 1), length)] = '\0';
}
return length;
}
int fgetc(FILE* stream) noexcept
{
char chr;
if (!stream->readChar(chr))
{
return EOF;
}
return chr;
}
char* fgets(char* buffer, int count, FILE* stream) noexcept
{
if (count < 1) {
return nullptr;
}
if (count == 1)
{
*buffer = '\0';
return buffer;
}
int pos = 0;
for (; pos < count - 1; ++pos)
{
if (!stream->readChar(buffer[pos]))
{
if (stream->isError() || pos == 0)
{
return nullptr;
}
break;
}
}
buffer[pos] = '\0';
return buffer;
}
} // extern "C"
namespace std
{
bool fgetline(FILE* stream, std::string& outLine) noexcept
{
return stream->readLine(outLine);
}
}