aboutsummaryrefslogtreecommitdiff
path: root/butl/fdstream
blob: 21df3f83f8b47e0e0986ba29516dee5bac456765 (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
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
// file      : butl/fdstream -*- C++ -*-
// copyright : Copyright (c) 2014-2016 Code Synthesis Ltd
// license   : MIT; see accompanying LICENSE file

#ifndef BUTL_FDSTREAM
#define BUTL_FDSTREAM

#include <istream>
#include <ostream>

namespace butl
{
  // An iostream that is initialized with a file descriptor rather than
  // a file name.
  //
  // Notes and limitations:
  //
  // - char only
  // - input or output but not both
  // - no support for put back
  // - throws std::system_error in case of a read()/write() error
  // - not movable, though can be easily supported
  //
  class fdbuf: public std::basic_streambuf<char>
  {
  public:
    virtual
    ~fdbuf ();
    fdbuf () = default;
    fdbuf (int fd) {open (fd);}

    fdbuf (const fdbuf&) = delete;
    fdbuf& operator= (const fdbuf&) = delete;

    void
    close ();

    void
    open (int fd);

    bool
    is_open () const {return fd_ != -1;}

  public:
    using int_type = std::basic_streambuf<char>::int_type;
    using traits_type = std::basic_streambuf<char>::traits_type;

    // basic_streambuf input interface.
    //
  public:
    virtual std::streamsize
    showmanyc ();

    virtual int_type
    underflow ();

  private:
    bool
    load ();

    // basic_streambuf output interface.
    //
  public:
    virtual int_type
    overflow (int_type);

    virtual int
    sync ();

  private:
    bool
    save ();

  private:
    int fd_ = -1;
    char buf_[2048];
  };

  // File descriptor translation mode. It has the same semantics as the
  // binary/text opening modes in std::fstream. Specifically, this is a
  // noop for POSIX systems where the two modes are the same.
  //
  enum class fdtranslate
  {
    text,
    binary
  };

  class fdstream_base
  {
  protected:
    fdstream_base () = default;
    fdstream_base (int fd): buf_ (fd) {}
    fdstream_base (int, fdtranslate);

  protected:
    fdbuf buf_;
  };

  // Note that the destructor may throw.
  //
  class ifdstream: fdstream_base, public std::istream
  {
  public:
    ifdstream (): std::istream (&buf_) {}
    ifdstream (int fd): fdstream_base (fd), std::istream (&buf_) {}
    ifdstream (int fd, fdtranslate m)
        : fdstream_base (fd, m), std::istream (&buf_) {}

    void close () {buf_.close ();}
    void open (int fd) {buf_.open (fd);}
    bool is_open () const {return buf_.is_open ();}
  };

  // Note that the destructor flushes the stream and may throw.
  //
  class ofdstream: fdstream_base, public std::ostream
  {
  public:
    ofdstream (): std::ostream (&buf_) {}
    ofdstream (int fd): fdstream_base (fd), std::ostream (&buf_) {}
    ofdstream (int fd, fdtranslate m)
        : fdstream_base (fd, m), std::ostream (&buf_) {}

    ~ofdstream () override {if (is_open () && good ()) buf_.sync ();}

    void close () {flush (); buf_.close ();}
    void open (int fd) {buf_.open (fd);}
    bool is_open () const {return buf_.is_open ();}
  };

  // Set the translation mode for the file descriptor. Return the previous
  // mode on success, throw std::system_error otherwise.
  //
  fdtranslate
  fdmode (int, fdtranslate);

  // Convenience functions for setting the translation mode for standard
  // streams.
  //
  fdtranslate stdin_fdmode  (fdtranslate);
  fdtranslate stdout_fdmode (fdtranslate);
  fdtranslate stderr_fdmode (fdtranslate);

  // Low-level, nothrow file descriptor API.
  //

  // Close the file descriptor. Return true on success, set errno and return
  // false otherwise.
  //
  bool
  fdclose (int) noexcept;

  // Open the null device (e.g., /dev/null) that discards all data written to
  // it and provides no data for read operations (i.e., yelds EOF on read).
  // Return file descriptor on success, set errno and return -1 otherwise.
  // Note that it's the caller's responsibility to close the returned file
  // descriptor.
  //
  int
  fdnull () noexcept;
}

#endif // BUTL_FDSTREAM