1// Copyright 2011 The Kyua Authors.
2// All rights reserved.
3//
4// Redistribution and use in source and binary forms, with or without
5// modification, are permitted provided that the following conditions are
6// met:
7//
8// * Redistributions of source code must retain the above copyright
9//   notice, this list of conditions and the following disclaimer.
10// * Redistributions in binary form must reproduce the above copyright
11//   notice, this list of conditions and the following disclaimer in the
12//   documentation and/or other materials provided with the distribution.
13// * Neither the name of Google Inc. nor the names of its contributors
14//   may be used to endorse or promote products derived from this software
15//   without specific prior written permission.
16//
17// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
29#include "utils/stream.hpp"
30
31#include <fstream>
32#include <iostream>
33#include <sstream>
34#include <stdexcept>
35
36#include "utils/format/macros.hpp"
37#include "utils/fs/path.hpp"
38#include "utils/sanity.hpp"
39
40namespace fs = utils::fs;
41
42
43namespace {
44
45
46/// Constant that represents the path to stdout.
47static const fs::path stdout_path("/dev/stdout");
48
49
50/// Constant that represents the path to stderr.
51static const fs::path stderr_path("/dev/stderr");
52
53
54}  // anonymous namespace
55
56
57/// Opens a new file for output, respecting the stdout and stderr streams.
58///
59/// \param path The path to the output file to be created.
60///
61/// \return A pointer to a new output stream.
62std::auto_ptr< std::ostream >
63utils::open_ostream(const fs::path& path)
64{
65    std::auto_ptr< std::ostream > out;
66    if (path == stdout_path) {
67        out.reset(new std::ofstream());
68        out->copyfmt(std::cout);
69        out->clear(std::cout.rdstate());
70        out->rdbuf(std::cout.rdbuf());
71    } else if (path == stderr_path) {
72        out.reset(new std::ofstream());
73        out->copyfmt(std::cerr);
74        out->clear(std::cerr.rdstate());
75        out->rdbuf(std::cerr.rdbuf());
76    } else {
77        out.reset(new std::ofstream(path.c_str()));
78        if (!(*out)) {
79            throw std::runtime_error(F("Cannot open output file %s") % path);
80        }
81    }
82    INV(out.get() != NULL);
83    return out;
84}
85
86
87/// Gets the length of a stream.
88///
89/// \param is The input stream for which to calculate its length.
90///
91/// \return The length of the stream.  This is of size_t type instead of
92/// directly std::streampos to simplify the caller.  Some systems do not
93/// support comparing a std::streampos directly to an integer (see
94/// NetBSD 1.5.x), which is what we often want to do.
95///
96/// \throw std::exception If calculating the length fails due to a stream error.
97std::size_t
98utils::stream_length(std::istream& is)
99{
100    const std::streampos current_pos = is.tellg();
101    try {
102        is.seekg(0, std::ios::end);
103        const std::streampos length = is.tellg();
104        is.seekg(current_pos, std::ios::beg);
105        return static_cast< std::size_t >(length);
106    } catch (...) {
107        is.seekg(current_pos, std::ios::beg);
108        throw;
109    }
110}
111
112
113/// Reads a whole file into memory.
114///
115/// \param path The file to read.
116///
117/// \return A plain string containing the raw contents of the file.
118///
119/// \throw std::runtime_error If the file cannot be opened.
120std::string
121utils::read_file(const fs::path& path)
122{
123    std::ifstream input(path.c_str());
124    if (!input)
125        throw std::runtime_error(F("Failed to open '%s' for read") % path);
126    return read_stream(input);
127}
128
129
130/// Reads the whole contents of a stream into memory.
131///
132/// \param input The input stream from which to read.
133///
134/// \return A plain string containing the raw contents of the file.
135std::string
136utils::read_stream(std::istream& input)
137{
138    std::ostringstream buffer;
139
140    char tmp[1024];
141    while (input.good()) {
142        input.read(tmp, sizeof(tmp));
143        if (input.good() || input.eof()) {
144            buffer.write(tmp, input.gcount());
145        }
146    }
147
148    return buffer.str();
149}
150