output.cc revision 10810:683ab55819fd
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#include <unistd.h> 36 37#include <cassert> 38#include <cerrno> 39#include <climits> 40#include <cstdlib> 41#include <fstream> 42 43#include <gzstream.hh> 44 45#include "base/misc.hh" 46#include "base/output.hh" 47 48using namespace std; 49 50OutputDirectory simout; 51 52/** 53 * @file This file manages creating / deleting output files for the simulator. 54 */ 55OutputDirectory::OutputDirectory() 56{} 57 58OutputDirectory::~OutputDirectory() 59{ 60 for (map_t::iterator i = files.begin(); i != files.end(); i++) { 61 if (i->second) 62 delete i->second; 63 } 64} 65 66std::ostream * 67OutputDirectory::checkForStdio(const string &name) const 68{ 69 if (name == "cerr" || name == "stderr") 70 return &cerr; 71 72 if (name == "cout" || name == "stdout") 73 return &cout; 74 75 return NULL; 76} 77 78ostream * 79OutputDirectory::openFile(const string &filename, 80 ios_base::openmode mode) 81{ 82 if (filename.find(".gz", filename.length()-3) < filename.length()) { 83 ogzstream *file = new ogzstream(filename.c_str(), mode); 84 if (!file->is_open()) 85 fatal("Cannot open file %s", filename); 86 assert(files.find(filename) == files.end()); 87 files[filename] = file; 88 return file; 89 } else { 90 ofstream *file = new ofstream(filename.c_str(), mode); 91 if (!file->is_open()) 92 fatal("Cannot open file %s", filename); 93 assert(files.find(filename) == files.end()); 94 files[filename] = file; 95 return file; 96 } 97} 98 99void 100OutputDirectory::close(ostream *openStream) { 101 map_t::iterator i; 102 for (i = files.begin(); i != files.end(); i++) { 103 if (i->second != openStream) 104 continue; 105 106 ofstream *fs = dynamic_cast<ofstream*>(i->second); 107 if (fs) { 108 fs->close(); 109 delete i->second; 110 break; 111 } else { 112 ogzstream *gfs = dynamic_cast<ogzstream*>(i->second); 113 if (gfs) { 114 gfs->close(); 115 delete i->second; 116 break; 117 } 118 } 119 } 120 121 if (i == files.end()) 122 fatal("Attempted to close an unregistred file stream"); 123 124 files.erase(i); 125} 126 127void 128OutputDirectory::setDirectory(const string &d) 129{ 130 if (!dir.empty()) 131 panic("Output directory already set!\n"); 132 133 dir = d; 134 135 // guarantee that directory ends with a path separator 136 if (dir[dir.size() - 1] != PATH_SEPARATOR) 137 dir += PATH_SEPARATOR; 138} 139 140const string & 141OutputDirectory::directory() const 142{ 143 if (dir.empty()) 144 panic("Output directory not set!"); 145 146 return dir; 147} 148 149string 150OutputDirectory::resolve(const string &name) const 151{ 152 return (name[0] != PATH_SEPARATOR) ? dir + name : name; 153} 154 155ostream * 156OutputDirectory::create(const string &name, bool binary) 157{ 158 ostream *file = checkForStdio(name); 159 if (file) 160 return file; 161 162 string filename = resolve(name); 163 ios_base::openmode mode = 164 ios::trunc | (binary ? ios::binary : (ios::openmode)0); 165 file = openFile(filename, mode); 166 167 return file; 168} 169 170ostream * 171OutputDirectory::find(const string &name) const 172{ 173 ostream *file = checkForStdio(name); 174 if (file) 175 return file; 176 177 const string filename = resolve(name); 178 map_t::const_iterator i = files.find(filename); 179 if (i != files.end()) 180 return (*i).second; 181 182 return NULL; 183} 184 185bool 186OutputDirectory::isFile(const std::ostream *os) 187{ 188 return os && os != &cerr && os != &cout; 189} 190 191bool 192OutputDirectory::isFile(const string &name) const 193{ 194 // definitely a file if in our data structure 195 if (find(name) != NULL) return true; 196 197 struct stat st_buf; 198 int st = stat(name.c_str(), &st_buf); 199 return (st == 0) && S_ISREG(st_buf.st_mode); 200} 201 202string 203OutputDirectory::createSubdirectory(const string &name) const 204{ 205 const string new_dir = resolve(name); 206 if (new_dir.find(directory()) == string::npos) 207 fatal("Attempting to create subdirectory not in m5 output dir\n"); 208 209 // if it already exists, that's ok; otherwise, fail if we couldn't create 210 if ((mkdir(new_dir.c_str(), 0755) != 0) && (errno != EEXIST)) 211 fatal("Failed to create new output subdirectory '%s'\n", new_dir); 212 213 return name + PATH_SEPARATOR; 214} 215 216void 217OutputDirectory::remove(const string &name, bool recursive) 218{ 219 const string fname = resolve(name); 220 221 if (fname.find(directory()) == string::npos) 222 fatal("Attempting to remove file/dir not in output dir\n"); 223 224 if (isFile(fname)) { 225 // close and release file if we have it open 226 map_t::iterator itr = files.find(fname); 227 if (itr != files.end()) { 228 delete itr->second; 229 files.erase(itr); 230 } 231 232 if (::remove(fname.c_str()) != 0) 233 fatal("Could not erase file '%s'\n", fname); 234 } else { 235 // assume 'name' is a directory 236 if (recursive) { 237 DIR *subdir = opendir(fname.c_str()); 238 239 // silently ignore removal request for non-existent directory 240 if ((!subdir) && (errno == ENOENT)) 241 return; 242 243 // fail on other errors 244 if (!subdir) { 245 perror("opendir"); 246 fatal("Error opening directory for recursive removal '%s'\n", 247 fname); 248 } 249 250 struct dirent *de = readdir(subdir); 251 while (de != NULL) { 252 // ignore files starting with a '.'; user must delete those 253 // manually if they really want to 254 if (de->d_name[0] != '.') 255 remove(name + PATH_SEPARATOR + de->d_name, recursive); 256 257 de = readdir(subdir); 258 } 259 260 closedir(subdir); 261 } 262 263 // try to force recognition that we deleted the files in the directory 264 sync(); 265 266 if (::remove(fname.c_str()) != 0) { 267 perror("Warning! 'remove' failed. Could not erase directory."); 268 } 269 } 270} 271