aboutsummaryrefslogtreecommitdiff
path: root/butl/fdstream
blob: 23cc58cce8bfde0195dd9f5c9d532a9dc94a1078 (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
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
// 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>
#include <cstdint> // uint16_t

#include <butl/path>
#include <butl/filesystem>  // permissions

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_[4096];
  };

  // 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 ();}
  };

  // File open flags.
  //
  enum class fdopen_mode: std::uint16_t
  {
    in         = 0x01, // Open for reading.
    out        = 0x02, // Open for writing.
    append     = 0x04, // Seek to the end of file before each write.
    truncate   = 0x08, // Discard the file contents on open.
    create     = 0x10, // Create a file if not exists.
    exclusive  = 0x20, // Fail if the file exists and the create flag is set.
    binary     = 0x40, // Set binary translation mode.

    none = 0           // Usefull when build the mode incrementally.
  };

  fdopen_mode operator& (fdopen_mode, fdopen_mode);
  fdopen_mode operator| (fdopen_mode, fdopen_mode);
  fdopen_mode operator&= (fdopen_mode&, fdopen_mode);
  fdopen_mode operator|= (fdopen_mode&, fdopen_mode);

  // Open a file returning the file descriptor on success and throwing
  // std::system_error otherwise.
  //
  // The mode argument should have at least one of the in or out flags set.
  // The append and truncate flags are meaningless in the absense of the out
  // flag and are ignored without it. The exclusive flag is meaningless in the
  // absense of the create flag and is ignored without it. Note also that if
  // the exclusive flag is specified then a dangling symbolic link is treated
  // as an existing file.
  //
  // The permissions argument is taken into account only if the file is
  // created. Note also that permissions can be adjusted while being set in a
  // way specific for the OS. On POSIX systems they are modified with the
  // process' umask, so effective permissions are permissions & ~umask. On
  // Windows permissions other than ru and wu are unlikelly to have effect.
  //
  int
  fdopen (const path&,
          fdopen_mode,
          permissions = permissions::ru | permissions::wu |
                        permissions::rg | permissions::wg |
                        permissions::ro | permissions::wo);

  // 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;
}

#include <butl/fdstream.ixx>

#endif // BUTL_FDSTREAM