da9a551fa6b312f58bbb3eae01f2ba0b27673ad2
[gem5.git] / src / base / output.cc
1 /*
2 * Copyright (c) 2005 The Regents of The University of Michigan
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met: redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer;
9 * redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution;
12 * neither the name of the copyright holders nor the names of its
13 * contributors may be used to endorse or promote products derived from
14 * this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 *
28 * Authors: Nathan Binkert
29 * Chris Emmons
30 */
31
32 #include <sys/stat.h>
33 #include <sys/types.h>
34 #include <dirent.h>
35
36 #include <cassert>
37 #include <cerrno>
38 #include <climits>
39 #include <cstdlib>
40 #include <fstream>
41
42 #include <gzstream.hh>
43
44 #include "base/misc.hh"
45 #include "base/output.hh"
46
47 using namespace std;
48
49 OutputDirectory simout;
50
51 /**
52 * @file This file manages creating / deleting output files for the simulator.
53 */
54 OutputDirectory::OutputDirectory()
55 {}
56
57 OutputDirectory::~OutputDirectory()
58 {
59 for (map_t::iterator i = files.begin(); i != files.end(); i++) {
60 if (i->second)
61 delete i->second;
62 }
63 }
64
65 std::ostream *
66 OutputDirectory::checkForStdio(const string &name) const
67 {
68 if (name == "cerr" || name == "stderr")
69 return &cerr;
70
71 if (name == "cout" || name == "stdout")
72 return &cout;
73
74 return NULL;
75 }
76
77 ostream *
78 OutputDirectory::openFile(const string &filename,
79 ios_base::openmode mode)
80 {
81 if (filename.find(".gz", filename.length()-3) < filename.length()) {
82 ogzstream *file = new ogzstream(filename.c_str(), mode);
83 if (!file->is_open())
84 fatal("Cannot open file %s", filename);
85 assert(files.find(filename) == files.end());
86 files[filename] = file;
87 return file;
88 } else {
89 ofstream *file = new ofstream(filename.c_str(), mode);
90 if (!file->is_open())
91 fatal("Cannot open file %s", filename);
92 assert(files.find(filename) == files.end());
93 files[filename] = file;
94 return file;
95 }
96 }
97
98 void
99 OutputDirectory::close(ostream *openStream) {
100 map_t::iterator i;
101 for (i = files.begin(); i != files.end(); i++) {
102 if (i->second != openStream)
103 continue;
104
105 ofstream *fs = dynamic_cast<ofstream*>(i->second);
106 if (fs) {
107 fs->close();
108 delete i->second;
109 break;
110 } else {
111 ogzstream *gfs = dynamic_cast<ogzstream*>(i->second);
112 if (gfs) {
113 gfs->close();
114 delete i->second;
115 break;
116 }
117 }
118 }
119
120 if (i == files.end())
121 fatal("Attempted to close an unregistred file stream");
122
123 files.erase(i);
124 }
125
126 void
127 OutputDirectory::setDirectory(const string &d)
128 {
129 if (!dir.empty())
130 panic("Output directory already set!\n");
131
132 dir = d;
133
134 // guarantee that directory ends with a path separator
135 if (dir[dir.size() - 1] != PATH_SEPARATOR)
136 dir += PATH_SEPARATOR;
137 }
138
139 const string &
140 OutputDirectory::directory() const
141 {
142 if (dir.empty())
143 panic("Output directory not set!");
144
145 return dir;
146 }
147
148 string
149 OutputDirectory::resolve(const string &name) const
150 {
151 return (name[0] != PATH_SEPARATOR) ? dir + name : name;
152 }
153
154 ostream *
155 OutputDirectory::create(const string &name, bool binary)
156 {
157 ostream *file = checkForStdio(name);
158 if (file)
159 return file;
160
161 string filename = resolve(name);
162 ios_base::openmode mode =
163 ios::trunc | (binary ? ios::binary : (ios::openmode)0);
164 file = openFile(filename, mode);
165
166 return file;
167 }
168
169 ostream *
170 OutputDirectory::find(const string &name) const
171 {
172 ostream *file = checkForStdio(name);
173 if (file)
174 return file;
175
176 const string filename = resolve(name);
177 map_t::const_iterator i = files.find(filename);
178 if (i != files.end())
179 return (*i).second;
180
181 return NULL;
182 }
183
184 bool
185 OutputDirectory::isFile(const std::ostream *os)
186 {
187 return os && os != &cerr && os != &cout;
188 }
189
190 bool
191 OutputDirectory::isFile(const string &name) const
192 {
193 // definitely a file if in our data structure
194 if (find(name) != NULL) return true;
195
196 struct stat st_buf;
197 int st = stat(name.c_str(), &st_buf);
198 return (st == 0) && S_ISREG(st_buf.st_mode);
199 }
200
201 string
202 OutputDirectory::createSubdirectory(const string &name) const
203 {
204 const string new_dir = resolve(name);
205 if (new_dir.find(directory()) == string::npos)
206 fatal("Attempting to create subdirectory not in m5 output dir\n");
207
208 // if it already exists, that's ok; otherwise, fail if we couldn't create
209 if ((mkdir(new_dir.c_str(), 0755) != 0) && (errno != EEXIST))
210 fatal("Failed to create new output subdirectory '%s'\n", new_dir);
211
212 return name + PATH_SEPARATOR;
213 }
214
215 void
216 OutputDirectory::remove(const string &name, bool recursive)
217 {
218 const string fname = resolve(name);
219
220 if (fname.find(directory()) == string::npos)
221 fatal("Attempting to remove file/dir not in output dir\n");
222
223 if (isFile(fname)) {
224 // close and release file if we have it open
225 map_t::iterator itr = files.find(fname);
226 if (itr != files.end()) {
227 delete itr->second;
228 files.erase(itr);
229 }
230
231 if (::remove(fname.c_str()) != 0)
232 fatal("Could not erase file '%s'\n", fname);
233 } else {
234 // assume 'name' is a directory
235 if (recursive) {
236 DIR *subdir = opendir(fname.c_str());
237
238 // silently ignore removal request for non-existent directory
239 if ((!subdir) && (errno == ENOENT))
240 return;
241
242 // fail on other errors
243 if (!subdir) {
244 perror("opendir");
245 fatal("Error opening directory for recursive removal '%s'\n",
246 fname);
247 }
248
249 struct dirent *de = readdir(subdir);
250 while (de != NULL) {
251 // ignore files starting with a '.'; user must delete those
252 // manually if they really want to
253 if (de->d_name[0] != '.')
254 remove(name + PATH_SEPARATOR + de->d_name, recursive);
255
256 de = readdir(subdir);
257 }
258
259 closedir(subdir);
260 }
261
262 // try to force recognition that we deleted the files in the directory
263 sync();
264
265 if (::remove(fname.c_str()) != 0) {
266 perror("Warning! 'remove' failed. Could not erase directory.");
267 }
268 }
269 }