| //===-- Implementation of perror ------------------------------------------===// | |
| // | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |
| // See https://llvm.org/LICENSE.txt for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "src/stdio/perror.h" | |
| #include "src/__support/CPP/string_view.h" | |
| #include "src/__support/File/file.h" | |
| #include "src/__support/StringUtil/error_to_string.h" | |
| #include "src/__support/libc_errno.h" | |
| #include "src/__support/macros/config.h" | |
| #include "src/stdio/stderr.h" | |
| #include "hdr/types/FILE.h" | |
| namespace LIBC_NAMESPACE_DECL { | |
| static int write_out(cpp::string_view str_view, ::FILE *f) { | |
| if (str_view.size() > 0) { | |
| auto result = reinterpret_cast<LIBC_NAMESPACE::File *>(f)->write_unlocked( | |
| str_view.data(), str_view.size()); | |
| if (result.has_error()) | |
| return result.error; | |
| } | |
| return 0; | |
| } | |
| // separate function so that we can return early on error but still get the | |
| // unlock. This function sets errno and should not be called elsewhere. | |
| static void write_sequence(cpp::string_view str_view, | |
| cpp::string_view err_str) { | |
| int write_err; | |
| // TODO: this seems like there should be some sort of queue system to | |
| // deduplicate this code. | |
| // FORMAT: | |
| // if str != nullptr and doesn't start with a null byte: | |
| // "[str]: [strerror(errno)]\n" | |
| // else | |
| // "[strerror(errno)]\n" | |
| if (str_view.size() > 0) { | |
| write_err = write_out(str_view, LIBC_NAMESPACE::stderr); | |
| if (write_err != 0) { | |
| libc_errno = write_err; | |
| return; | |
| } | |
| write_err = write_out(": ", LIBC_NAMESPACE::stderr); | |
| if (write_err != 0) { | |
| libc_errno = write_err; | |
| return; | |
| } | |
| } | |
| write_err = write_out(err_str, LIBC_NAMESPACE::stderr); | |
| if (write_err != 0) { | |
| libc_errno = write_err; | |
| return; | |
| } | |
| write_err = write_out("\n", LIBC_NAMESPACE::stderr); | |
| if (write_err != 0) { | |
| libc_errno = write_err; | |
| return; | |
| } | |
| } | |
| LLVM_LIBC_FUNCTION(void, perror, (const char *str)) { | |
| const char empty_str[1] = {'\0'}; | |
| if (str == nullptr) | |
| str = empty_str; | |
| cpp::string_view str_view(str); | |
| cpp::string_view err_str = get_error_string(libc_errno); | |
| // We need to lock the stream to ensure the newline is always appended. | |
| LIBC_NAMESPACE::File *file = reinterpret_cast<File *>(stderr); | |
| File::FileLock lock(file); | |
| write_sequence(str_view, err_str); | |
| } | |
| } // namespace LIBC_NAMESPACE_DECL |