HPCToolkit
FileUtil.cpp
Go to the documentation of this file.
1 // -*-Mode: C++;-*-
2 
3 // * BeginRiceCopyright *****************************************************
4 //
5 // $HeadURL$
6 // $Id$
7 //
8 // --------------------------------------------------------------------------
9 // Part of HPCToolkit (hpctoolkit.org)
10 //
11 // Information about sources of support for research and development of
12 // HPCToolkit is at 'hpctoolkit.org' and in 'README.Acknowledgments'.
13 // --------------------------------------------------------------------------
14 //
15 // Copyright ((c)) 2002-2019, Rice University
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:
21 //
22 // * Redistributions of source code must retain the above copyright
23 // notice, this list of conditions and the following disclaimer.
24 //
25 // * Redistributions in binary form must reproduce the above copyright
26 // notice, this list of conditions and the following disclaimer in the
27 // documentation and/or other materials provided with the distribution.
28 //
29 // * Neither the name of Rice University (RICE) nor the names of its
30 // contributors may be used to endorse or promote products derived from
31 // this software without specific prior written permission.
32 //
33 // This software is provided by RICE and contributors "as is" and any
34 // express or implied warranties, including, but not limited to, the
35 // implied warranties of merchantability and fitness for a particular
36 // purpose are disclaimed. In no event shall RICE or contributors be
37 // liable for any direct, indirect, incidental, special, exemplary, or
38 // consequential damages (including, but not limited to, procurement of
39 // substitute goods or services; loss of use, data, or profits; or
40 // business interruption) however caused and on any theory of liability,
41 // whether in contract, strict liability, or tort (including negligence
42 // or otherwise) arising in any way out of the use of this software, even
43 // if advised of the possibility of such damage.
44 //
45 // ******************************************************* EndRiceCopyright *
46 
47 //***************************************************************************
48 
49 //************************* System Include Files ****************************
50 
51 #include <sys/param.h>
52 
53 #include <cstdlib> // for 'mkstemp' (not technically visible in C++)
54 #include <cstdio> // for 'tmpnam', 'rename'
55 #include <cerrno>
56 #include <cstdarg>
57 #include <cstring>
58 
59 #include <sys/types.h>
60 #include <sys/stat.h>
61 #include <unistd.h>
62 #include <fcntl.h>
63 
64 #include <fnmatch.h>
65 
66 #include <string>
67 using std::string;
68 
69 //*************************** User Include Files ****************************
70 
71 #include "FileUtil.hpp"
72 
73 #include "diagnostics.h"
74 #include "StrUtil.hpp"
75 #include "Trace.hpp"
76 
78 
79 //*************************** Forward Declarations **************************
80 
81 using std::endl;
82 
83 //***************************************************************************
84 //
85 //***************************************************************************
86 
87 namespace FileUtil {
88 
89 string
90 basename(const char* fName)
91 {
92  string baseFileName;
93 
94  const char* lastSlash = strrchr(fName, '/');
95  if (lastSlash) {
96  // valid: "/foo" || ".../foo" AND invalid: "/" || ".../"
97  baseFileName = lastSlash + 1;
98  }
99  else {
100  // filename contains no slashes, already in short form
101  baseFileName = fName;
102  }
103  return baseFileName;
104 
105 #if 0
106  // A "more C++" implementation (Gaurav)
107  std::string
108  PathFindMgr::getFileName(const std::string& path) const
109  {
110  size_t in = path.find_last_of("/");
111  if (in != path.npos && path.length() > 1)
112  return path.substr(in + 1);
113 
114  return path;
115  }
116 #endif
117 }
118 
119 
120 string
121 rmSuffix(const char* fName)
122 {
123  string baseFileName = fName;
124 
125  size_t pos = baseFileName.find_last_of('.');
126  if (pos != string::npos) {
127  baseFileName = baseFileName.substr(0, pos);
128  }
129  return baseFileName;
130 }
131 
132 
133 string
134 dirname(const char* fName)
135 {
136  const char* lastSlash = strrchr(fName, '/');
137  string pathComponent = ".";
138  if (lastSlash) {
139  pathComponent = fName;
140  pathComponent.resize(lastSlash - fName);
141  }
142  return pathComponent;
143 }
144 
145 
146 bool
147 fnmatch(const std::vector<std::string>& patternVec,
148  const char* string, int flags)
149 {
150  for (uint i = 0; i < patternVec.size(); ++i) {
151  const std::string& pat = patternVec[i];
152  bool fnd = FileUtil::fnmatch(pat, string, flags);
153  if (fnd) {
154  return true;
155  }
156  }
157 
158  return false;
159 }
160 
161 } // end of FileUtil namespace
162 
163 
164 //***************************************************************************
165 //
166 //***************************************************************************
167 
168 namespace FileUtil {
169 
170 bool
171 isReadable(const char* path)
172 {
173  struct stat sbuf;
174  if (stat(path, &sbuf) == 0) {
175  return true; // the file is readable
176  }
177  return false;
178 }
179 
180 
181 bool
182 isDir(const char* path)
183 {
184  struct stat sbuf;
185  if (stat(path, &sbuf) == 0) {
186  return (S_ISDIR(sbuf.st_mode)
187  /*|| S_ISLNK(sbuf.st_mode) && isDir(readlink(path))*/);
188  }
189  return false; // unknown
190 }
191 
192 
193 int
194 countChar(const char* path, char c)
195 {
196  int srcFd = open(path, O_RDONLY);
197  if (srcFd < 0) {
198  return -1;
199  }
200  int count = 0;
201  char buf[256];
202  ssize_t nRead;
203  while ((nRead = read(srcFd, buf, 256)) > 0) {
204  for (int i = 0; i < nRead; i++) {
205  if (buf[i] == c) count++;
206  }
207  }
208  return count;
209 }
210 
211 } // end of FileUtil namespace
212 
213 
214 //***************************************************************************
215 //
216 //***************************************************************************
217 
218 static void
219 cpy(int srcFd, int dstFd)
220 {
221  static const int bufSz = 4096;
222  char buf[bufSz];
223  ssize_t nRead;
224  while ((nRead = read(srcFd, buf, bufSz)) > 0) {
225  write(dstFd, buf, nRead);
226  }
227 }
228 
229 
230 namespace FileUtil {
231 
232 void
233 copy(const char* dst, ...)
234 {
235  va_list srcFnmList;
236  va_start(srcFnmList, dst);
237 
238  DIAG_MsgIf(0, "FileUtil::copy: ... -> " << dst);
239 
240  int dstFd = open(dst, O_WRONLY | O_CREAT | O_TRUNC,
241  S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
242  if (dstFd < 0) {
243  DIAG_Throw("[FileUtil::copy] could not open destination file '"
244  << dst << "' (" << strerror(errno) << ")");
245  }
246 
247  string errorMsg;
248 
249  char* srcFnm;
250  while ( (srcFnm = va_arg(srcFnmList, char*)) ) {
251  int srcFd = open(srcFnm, O_RDONLY);
252  if ((srcFd < 0) || (dstFd < 0)) {
253  errorMsg += (string("could not open '") + srcFnm + "' ("
254  + strerror(errno) + ")");
255  }
256  else {
257  cpy(srcFd, dstFd);
258  close(srcFd);
259  }
260  }
261 
262  va_end(srcFnmList);
263  close(dstFd);
264 
265  if (!errorMsg.empty()) {
266  DIAG_Throw("[FileUtil::copy] could not open source files: " << errorMsg);
267  }
268 }
269 
270 
271 void
272 move(const char* dst, const char* src)
273 {
274  int ret = rename(src, dst);
275  if (ret != 0) {
276  DIAG_Throw("[FileUtil::move] '" << src << "' -> '" << dst << "'");
277  }
278 }
279 
280 
281 int
282 remove(const char* file)
283 {
284  return unlink(file);
285 }
286 
287 
288 int
289 mkdir(const char* dir)
290 {
291  if (!dir) {
292  DIAG_Throw("Invalid mkdir argument: (NULL)");
293  }
294 
295  string pathStr = dir;
296  bool isAbsPath = (dir[0] == '/');
297 
298  // -------------------------------------------------------
299  // 1. Convert path string to vector of path components
300  // "/p0/p1/p2/.../pn/px" ==> [p0 p1 p2 ... pn px]
301  // "/p0" ==> [p0]
302  // "p0" ==> [p0]
303  // "./p0" ==> [. p0]
304  //
305  // Note: we could do tokenization in place (string::find_last_of()),
306  // but (1) this is more elegant and (2) sytem calls and disk
307  // accesses will overwhelm any possible difference in performance.
308  // -------------------------------------------------------
309  std::vector<string> pathVec;
310  StrUtil::tokenize_char(pathStr, "/", pathVec);
311 
312  DIAG_Assert(!pathVec.empty(), DIAG_UnexpectedInput);
313 
314  // -------------------------------------------------------
315  // 2. Find 'curIdx' such that all paths before pathVec[curIdx] have
316  // been created.
317  //
318  // Note: Start search from the last path component, assuming that in
319  // the common case, intermediate directories are already created.
320  //
321  // Note: Could make this a binary search, but it would likely have
322  // insignificant effects.
323  // -------------------------------------------------------
324  size_t begIdx = 0;
325  size_t endIdx = pathVec.size() - 1;
326 
327  size_t curIdx = endIdx;
328  for ( ; curIdx >= begIdx; --curIdx) {
329  string x = StrUtil::join(pathVec, "/", 0, curIdx + 1);
330  if (isAbsPath) {
331  x = "/" + x;
332  }
333 
334  if (isDir(x)) {
335  break; // FIXME: double check: what if this is a symlink?
336  }
337  }
338 
339  curIdx++;
340 
341  // -------------------------------------------------------
342  // 3. Build directories from pathVec[curIdx ... endIdx]
343  // -------------------------------------------------------
344  mode_t mode = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH;
345 
346  for ( ; curIdx <= endIdx; ++curIdx) {
347  string x = StrUtil::join(pathVec, "/", 0, curIdx + 1);
348  if (isAbsPath) {
349  x = "/" + x;
350  }
351 
352  int ret = ::mkdir(x.c_str(), mode);
353  if (ret != 0) {
354  DIAG_Throw("[FileUtil::mkdir] '" << pathStr << "': Could not mkdir '"
355  << x << "' (" << strerror(errno) << ")");
356  }
357  }
358 
359  return 0;
360 }
361 
362 
363 std::pair<string, bool>
364 mkdirUnique(const char* dirnm)
365 {
366  string dirnm_new = dirnm;
367  bool is_done = false;
368 
369  mode_t mkmode = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH;
370 
371  int ret = ::mkdir(dirnm, mkmode);
372  if (ret != 0) {
373  if (errno == EEXIST) {
374 
375  std::vector<string> dirnmVec;
376 
377  // qualifier 1: jobid
378  const char* jobid_cstr = OSUtil_jobid();
379  if (jobid_cstr) {
380  string dirnm1 = string(dirnm) + "-" + string(jobid_cstr);
381  dirnmVec.push_back(dirnm1);
382  }
383 
384  // qualifier 2: pid
385  uint pid = OSUtil_pid();
386  string pid_str = StrUtil::toStr(pid);
387  string dirnm2 = string(dirnm) + "-" + pid_str;
388  dirnmVec.push_back(dirnm2);
389 
390  // attempt to create alternative directories
391  for (uint i = 0; i < dirnmVec.size(); ++i) {
392  dirnm_new = dirnmVec[i];
393  DIAG_Msg(1, "Directory '" << dirnm << "' already exists. Trying '" << dirnm_new << "'");
394  ret = ::mkdir(dirnm_new.c_str(), mkmode);
395  if (ret == 0) {
396  is_done = true;
397  break;
398  }
399  }
400 
401  if (is_done) {
402  DIAG_Msg(1, "Created directory: " << dirnm_new);
403  }
404  else {
405  DIAG_Die("Could not create an alternative to directory " << dirnm);
406  }
407  }
408  else {
409  DIAG_Die("Could not create database directory " << dirnm);
410  }
411  }
412 
413  return make_pair(dirnm_new, is_done);
414 }
415 
416 
417 const char*
419 {
420  // below is a hack to replace the deprecated tmpnam which g++ 3.2.2 will
421  // no longer allow. the mkstemp routine, which is touted as the replacement
422  // for tmpnam, provides a file descriptor as a return value. there is
423  // unfortunately no way to interface this with the ofstream class constructor
424  // which requires a filename. thus, a hack is born ...
425  // John Mellor-Crummey 5/7/2003
426 
427  // eraxxon: GNU is right that 'tmpnam' can be dangerous, but
428  // 'mkstemp' is not strictly part of C++! We could create an
429  // interface to 'mkstemp' within a C file, but this is getting
430  // cumbersome... and 'tmpnam' is not quite a WMD.
431 
432 #ifdef __GNUC__
433  static char tmpfilename[MAXPATHLEN];
434 
435  // creating a unique temp name with the new mkstemp interface now
436  // requires opening, closing, and deleting a file when all we want
437  // is the filename. sigh ...
438  strcpy(tmpfilename,"/tmp/hpcviewtmpXXXXXX");
439  close(mkstemp(tmpfilename));
440  unlink(tmpfilename);
441 
442  return tmpfilename;
443 #else
444  return tmpnam(NULL);
445 #endif
446 }
447 
448 
449 } // end of FileUtil namespace
450 
ssize_t MONITOR_EXT_WRAP_NAME() write(int fd, const void *buf, size_t count)
Definition: io-over.c:189
static const char * getFileName(Struct::ANode *strct)
string toStr(const int x, int base)
Definition: StrUtil.cpp:243
const char * tmpname()
Definition: FileUtil.cpp:418
void copy(const char *dst,...)
Definition: FileUtil.cpp:233
string dirname(const char *fName)
Definition: FileUtil.cpp:134
bool isReadable(const char *path)
Definition: FileUtil.cpp:171
bool fnmatch(const std::vector< std::string > &patternVec, const char *string, int flags)
Definition: FileUtil.cpp:147
Definition: fmt.c:108
void move(const char *dst, const char *src)
Definition: FileUtil.cpp:272
static __thread u32 pid
#define DIAG_MsgIf(ifexpr,...)
Definition: diagnostics.h:236
string join(const std::vector< string > &tokenvec, const char *delim, size_t begIdx, size_t endIdx)
Definition: StrUtil.cpp:144
bool isDir(const char *path)
Definition: FileUtil.cpp:182
unsigned int uint
Definition: uint.h:124
void tokenize_char(const std::string &tokenstr, const char *delim, std::vector< std::string > &tokenvec)
Definition: StrUtil.cpp:101
uint OSUtil_pid()
Definition: OSUtil.c:92
std::pair< string, bool > mkdirUnique(const char *dirnm)
Definition: FileUtil.cpp:364
const char * DIAG_UnexpectedInput
#define DIAG_Msg(level,...)
Definition: diagnostics.h:241
int countChar(const char *path, char c)
Definition: FileUtil.cpp:194
int mkdir(const char *dir)
Definition: FileUtil.cpp:289
ssize_t MONITOR_EXT_WRAP_NAME() read(int fd, void *buf, size_t count)
Definition: io-over.c:152
const char * OSUtil_jobid()
Definition: OSUtil.c:100
#define NULL
Definition: ElfHelper.cpp:85
static void cpy(int srcFd, int dstFd)
Definition: FileUtil.cpp:219
string basename(const char *fName)
Definition: FileUtil.cpp:90
#define DIAG_Die(...)
Definition: diagnostics.h:267
string rmSuffix(const char *fName)
Definition: FileUtil.cpp:121