1/*
2 * Copyright (c) 2015 ARM Limited
3 * All rights reserved
4 *
5 * The license below extends only to copyright in the software and shall
6 * not be construed as granting a license to any other intellectual
7 * property including but not limited to intellectual property relating
8 * to a hardware implementation of the functionality of the software
9 * licensed hereunder.  You may use the software subject to the license
10 * terms below provided that you ensure that this notice is replicated
11 * unmodified and in its entirety in all distributions of the software,
12 * modified or unmodified, in source code or in binary form.
13 *
14 * Copyright (c) 2013 Andreas Sandberg
15 * Copyright (c) 2005 The Regents of The University of Michigan
16 * All rights reserved.
17 *
18 * Redistribution and use in source and binary forms, with or without
19 * modification, are permitted provided that the following conditions are
20 * met: redistributions of source code must retain the above copyright
21 * notice, this list of conditions and the following disclaimer;
22 * redistributions in binary form must reproduce the above copyright
23 * notice, this list of conditions and the following disclaimer in the
24 * documentation and/or other materials provided with the distribution;
25 * neither the name of the copyright holders nor the names of its
26 * contributors may be used to endorse or promote products derived from
27 * this software without specific prior written permission.
28 *
29 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
30 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
31 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
32 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
33 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
34 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
35 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
36 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
37 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
38 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
39 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
40 *
41 * Authors: Nathan Binkert
42 *          Chris Emmons
43 *          Andreas Sandberg
44 *          Sascha Bischoff
45 */
46
47#ifndef __BASE_OUTPUT_HH__
48#define __BASE_OUTPUT_HH__
49
50#include <ios>
51#include <map>
52#include <string>
53
54#include "base/compiler.hh"
55
56class OutputDirectory;
57
58class OutputStream
59{
60  public:
61    virtual ~OutputStream();
62
63    /** Get the output underlying output stream */
64    std::ostream *stream() const { return _stream; };
65
66    /**
67     * Can the file be recreated if the output directory is moved?
68     *
69     * @return true if the file will be created in the new location,
70     * false otherwise.
71     */
72    virtual bool recreateable() const { return false; }
73
74    /** Get the file name in the output directory */
75    const std::string &name() const { return _name; }
76
77  protected:
78    friend class OutputDirectory;
79
80    /** Wrap an existing stream */
81    OutputStream(const std::string &name,
82                 std::ostream *stream);
83
84    /* Prevent copying */
85    OutputStream(const OutputStream &f);
86
87    /** Re-create the in a new location if recreateable. */
88    virtual void relocate(const OutputDirectory &dir);
89
90    /** Name in output directory */
91    const std::string _name;
92
93    /** Underlying output stream */
94    std::ostream *const _stream;
95};
96
97template<class StreamType>
98class OutputFile
99    : public OutputStream
100{
101  public:
102    typedef StreamType stream_type_t;
103
104    virtual ~OutputFile();
105
106    /**
107     * Can the file be recreated if the output directory is moved?
108     *
109     * @return true if the file will be created in the new location,
110     * false otherwise.
111     */
112    bool recreateable() const override { return _recreateable; }
113
114  protected:
115    friend class OutputDirectory;
116
117    OutputFile(const OutputDirectory &dir,
118               const std::string &name,
119               std::ios_base::openmode mode,
120               bool recreateable);
121
122    /* Prevent copying */
123    OutputFile(const OutputFile<StreamType> &f);
124
125    /** Re-create the file in a new location if it is relocatable. */
126    void relocate(const OutputDirectory &dir) override;
127
128    /** File mode when opened */
129    const std::ios_base::openmode _mode;
130
131    /** Can the file be recreated in a new location? */
132    const bool _recreateable;
133
134    /** Pointer to the file stream */
135    stream_type_t *const _fstream;
136};
137
138/** Interface for creating files in a gem5 output directory. */
139class OutputDirectory
140{
141  private:
142    /** File names and associated stream handles */
143    typedef std::map<std::string, OutputStream *> file_map_t;
144
145    /** Output subdirectories */
146    typedef std::map<std::string, OutputDirectory *> dir_map_t;
147
148    /** Open file streams within this directory */
149    file_map_t files;
150
151    /** Output sub-directories */
152    dir_map_t dirs;
153
154    /** Name of this directory */
155    std::string dir;
156
157    /** System-specific path separator character */
158    static const char PATH_SEPARATOR = '/';
159
160    static OutputStream stdout;
161    static OutputStream stderr;
162
163  protected:
164    /**
165     * Determines whether given file name corresponds to standard output
166     * streams.
167     *
168     * @param name name of file to check
169     * @return output stream for standard output or error stream if name
170     *         corresponds to one or the other; NULL otherwise
171     */
172    static OutputStream *checkForStdio(const std::string &name);
173
174  public:
175    /** Constructor. */
176    OutputDirectory();
177
178    /** Constructor. */
179    OutputDirectory(const std::string &name);
180
181    /** Destructor. */
182    ~OutputDirectory();
183
184    /**
185     * Returns relative file names prepended with name of this directory.
186     * Returns absolute file names unaltered.
187     *
188     * @param name file name to prepend with directory name
189     * @return file name prepended with base directory name or unaltered
190     *          absolute file name
191     */
192    std::string resolve(const std::string &name) const;
193
194    /**
195     * Sets name of this directory.
196     * @param dir name of this directory
197     */
198    void setDirectory(const std::string &dir);
199
200    /**
201     * Gets name of this directory.
202     * @return name of this directory
203     */
204    const std::string &directory() const;
205
206    /**
207     * Creates a file in this directory (optionally compressed).
208     *
209     * Will open a file as a compressed stream if filename ends in .gz, unless
210     * explicitly disabled.
211     *
212     * Relative output paths will result in the creation of a
213     * recreateable (see OutputFile) output file in the current output
214     * directory. Files created with an absolute path will not be
215     * recreateable.
216     *
217     * @param name name of file to create (without this directory's name
218     *          leading it)
219     * @param binary true to create a binary file; false otherwise
220     * @param no_gz true to disable opening the file as a gzip compressed output
221     *     stream; false otherwise
222     * @return OutputStream instance representing the created file
223     */
224    OutputStream *create(const std::string &name,
225                         bool binary = false,
226                         bool no_gz = false);
227
228    /**
229     * Open a file in this directory (optionally compressed).
230     *
231     * Will open a file as a compressed stream if filename ends in .gz, unless
232     * explicitly disabled.
233     *
234     * @param filename file to open
235     * @param mode attributes to open file with
236     * @param recreateable Set to true if the file can be recreated in a new
237     *     location.
238     * @param no_gz true to disable opening the file as a gzip compressed output
239     *     stream; false otherwise
240     * @return OutputStream instance representing the opened file
241     */
242    OutputStream *open(const std::string &name,
243                       std::ios_base::openmode mode,
244                       bool recreateable = true,
245                       bool no_gz = false);
246
247    /**
248     * Closes an output file and free the corresponding OutputFile.
249     *
250     * The output file must have been opened by the same
251     * OutputDirectory instance as the one closing it, or sim will
252     * fail.
253     *
254     * @param file OutputStream instance in this OutputDirectory.
255     */
256    void close(OutputStream *file);
257
258    /**
259     * Finds stream associated with an open file or stdout/stderr.
260     *
261     * @param name of file
262     * @return stream to specified file or NULL if file does not exist
263     */
264    OutputStream *find(const std::string &name) const;
265
266    OutputStream *findOrCreate(const std::string &name, bool binary = false);
267
268    /**
269     * Determines whether a file name corresponds to a file in this directory.
270     * @param name name of file to evaluate
271     * @return true iff file has been opened in this directory or exists on the
272     *          file system within this directory
273     */
274    bool isFile(const std::string &name) const;
275
276    /**
277     * Test if a path is absolute.
278     */
279    static inline bool isAbsolute(const std::string &name) {
280        return name[0] == PATH_SEPARATOR;
281    }
282
283    /**
284     * Creates a subdirectory within this directory.
285     * @param name name of subdirectory
286     * @return the new subdirectory's name suffixed with a path separator
287     */
288    OutputDirectory *createSubdirectory(const std::string &name);
289
290    /**
291     * Removes a specified file or subdirectory.
292     *
293     * Will cause sim to fail for most errors.  However, it will only warn the
294     * user if a directory could not be removed.  This is in place to
295     * accommodate slow file systems where file deletions within a subdirectory
296     * may not be recognized quickly enough thereby causing the subsequent call
297     * to remove the directory to fail (seemingly unempty directory).
298     *
299     * @param name name of file or subdirectory to remove; name should not
300     *              be prepended with the name of this directory object
301     * @param recursive set to true to attempt to recursively delete a
302     *                  subdirectory and its contents
303     */
304    void remove(const std::string &name, bool recursive=false);
305};
306
307extern OutputDirectory simout;
308
309#endif // __BASE_OUTPUT_HH__
310