aboutsummaryrefslogtreecommitdiff
path: root/libc/src/stdio/generic/perror.cpp
blob: 68b4ad644caab7cd90c9f2a708a842ed889bb763 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
//===-- 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"

namespace LIBC_NAMESPACE_DECL {

static int write_out(cpp::string_view str_view, File *f) {
  if (str_view.size() > 0) {
    auto result = 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::stderr->lock();
  write_sequence(str_view, err_str);
  LIBC_NAMESPACE::stderr->unlock();
}

} // namespace LIBC_NAMESPACE_DECL