1 /* 2 * Copyright (C) 2002 - David W. Durham 3 * 4 * This file is part of ReZound, an audio editing application. 5 * 6 * ReZound is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published 8 * by the Free Software Foundation; either version 2 of the License, 9 * or (at your option) any later version. 10 * 11 * ReZound is distributed in the hope that it will be useful, but 12 * WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program; if not, write to the Free Software 18 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA 19 */ 20 21 #ifndef CCXX_path_H_ 22 #define CCXX_path_H_ 23 24 #include "../../config/common.h" 25 26 /* 27 This was first written and tested under linux, however a few provisions 28 have been made for a win32 port. 29 Also, several functions used are probably only available under linux, i.e. 30 realpath, dirname, basename 31 But there should be some equivalent under other unices, and implementation 32 changes should be made when this file is compiled under these other platforms. 33 */ 34 35 #ifndef WIN32 36 #include <sys/types.h> 37 #include <sys/stat.h> 38 39 #include <errno.h> 40 #include <unistd.h> 41 42 #include <fcntl.h> 43 44 #include <limits.h> 45 #include <stdlib.h> 46 47 #include <string.h> 48 49 #include <libgen.h> // for basename and dirname 50 #else 51 #error unimplemented for win32 platform (yet) 52 #endif 53 54 #include <stdio.h> /* for popen/pclose */ 55 56 #include <stdexcept> 57 #include <string> 58 59 60 // this needs to be placed somewhere once in the application 61 #ifdef WIN32 62 #define DECLARE_STATIC_CPATH const char CPath::dirDelim='\\'; 63 #else 64 #define DECLARE_STATIC_CPATH const char CPath::dirDelim='/'; 65 #endif 66 67 68 69 class CPath 70 { 71 public: 72 static const char dirDelim; 73 74 CPath(const string &_path="") : 75 path(NULL), 76 _exists(false), 77 _permDenied(false) 78 { 79 setPath(_path); 80 } 81 82 virtual ~CPath() 83 { 84 free(path); 85 } 86 87 /* 88 changes the path from what it was constructed with 89 */ 90 void setPath(const string &_path) 91 { 92 if(_path.size()>=PATH_MAX) 93 throw runtime_error(string(__func__)+" -- path is >= than PATH_MAX -- '"+path+"'"); 94 95 if(path!=NULL) 96 free(path); 97 path=strdup(_path.c_str()); 98 99 if(stat(path,&statBuf)!=0) 100 { 101 _exists=false; 102 _permDenied= (errno==EACCES); 103 if(errno!=ENOENT && errno!=EACCES) 104 throw runtime_error(string(__func__)+" -- error stat-ing path name -- '"+path+"' -- "+strerror(errno)); 105 } 106 else 107 { 108 _exists=true; 109 _permDenied=false; 110 } 111 } 112 113 /* 114 returns the path constructed with or the last one passed to setPath 115 */ 116 const char *getPath() const 117 { 118 return(path); 119 } 120 121 /* 122 returns true iff the pathname exists and we could access it 123 hence, permission denied makes this returned false even if it did exist :-/ 124 */ 125 bool exists() const 126 { 127 return(_exists); 128 } 129 130 /* 131 returns true if the maybe some of the pathname exists but we couldn't access it 132 */ 133 bool permDenied() const 134 { 135 return(_permDenied); 136 } 137 138 /* 139 possbily creates the path and/or updates its time(s) to current 140 */ 141 bool touch(bool canCreate=true,bool throwIfError=true) const 142 { 143 if(canCreate) 144 { 145 int fd = open (path, O_WRONLY | O_CREAT | O_NONBLOCK | O_NOCTTY,S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH); 146 if (fd == -1) 147 { 148 if(throwIfError) 149 throw runtime_error(string(__func__)+" -- error touching path name '"+path+"' -- "+strerror(errno)); 150 else 151 return(false); 152 } 153 close(fd); 154 } 155 else 156 { 157 int fd = open (path, O_WRONLY | O_NONBLOCK | O_NOCTTY,S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH); 158 if (fd == -1 && errno!=ENOENT) 159 { 160 if(throwIfError) 161 throw runtime_error(string(__func__)+" -- error touching path name '"+path+"'-- "+strerror(errno)); 162 else 163 return(false); 164 } 165 166 if(fd!=-1) 167 close(fd); 168 } 169 170 return(true); 171 } 172 173 /* 174 returns the absolute path resolved from a possibly relative path 175 */ 176 const string realPath() const 177 { 178 if(!_exists) 179 return(""); 180 181 char resolvedPath[PATH_MAX]; 182 char *result=realpath(path,resolvedPath); 183 if(result!=NULL) 184 return(result); 185 else 186 return(""); 187 } 188 189 /* 190 returns the extension of the path name (i.e. returns "ext" for "/dir1/dir2/file.ext") 191 */ 192 const string extension() const 193 { 194 // find the right-most '.' which is also after the right-most dirDelim 195 196 char *lastDot=strrchr(path,'.'); 197 if(lastDot==NULL) // go ahead and bail if there wasn't even a '.' in the path 198 return(""); 199 200 char *lastDirDelim=strrchr(path,CPath::dirDelim); 201 202 if(lastDot>lastDirDelim) 203 return(lastDot+1); 204 else 205 return(""); 206 207 } 208 209 /* 210 returns just the directory name(s) part of the path (i.e. returns "/dir1/dir2" for "/dir1/dir2/file.ext") 211 */ 212 const string dirName() const 213 { 214 // make a copy because the function modifies the contents 215 char tmp[PATH_MAX]; 216 strcpy(tmp,path); 217 218 return(dirname(tmp)); 219 } 220 221 /* 222 returns just the filename part of the path (i.e. returns "file.ext" for "/dir1/dir2/file.ext") 223 */ 224 const string baseName() const 225 { 226 // make a copy because the function modifies the contents 227 char tmp[PATH_MAX]; 228 strcpy(tmp,path); 229 230 return(basename(tmp)); 231 } 232 233 /* 234 returns the number of bytes allocated for the file 235 either returns 0 or throws an exception if it doesn't exist or wasn't accessible 236 */ 237 long getSize(bool throwIfError=true) const 238 { 239 if(!_exists) 240 { 241 if(throwIfError) 242 throw(runtime_error(string(__func__)+" -- path did not exist or was inaccessible -- '"+path+"'")); 243 else 244 return(0); 245 } 246 247 return(statBuf.st_size); 248 } 249 250 // many other things could be returned from the stat 251 //time_t get ... Time() const; 252 253 254 // returns true even if it's a link to a regular file 255 bool isRegularFile() const 256 { 257 return S_ISREG(statBuf.st_mode); 258 } 259 260 // returns true if the path is a directory 261 bool isDirectory() const 262 { 263 return S_ISDIR(statBuf.st_mode); 264 } 265 266 bool isLink() const 267 { 268 return S_ISLNK(statBuf.st_mode); 269 } 270 271 bool isDevice() const 272 { 273 return S_ISCHR(statBuf.st_mode) || S_ISBLK(statBuf.st_mode); 274 } 275 276 /* returns a string containing the first time the executable 'exeName' is found on $PATH */ 277 static const string which(const string exeName) 278 { 279 /*??? would have to change implementation if WIN32 was ACTUALLY supported */ 280 string ret; 281 FILE *p=popen(("which '"+exeName+"' 2>/dev/null").c_str(),"r"); 282 283 char buffer[4096+1]; 284 redo: 285 buffer[0]=0; 286 if(p!=NULL && fgets(buffer,4096,p)) 287 { 288 // remove \n at end 289 if(strlen(buffer)>0) 290 buffer[strlen(buffer)-1]=0; 291 292 #if defined(rez_OS_LINUX) 293 // on linux which simply returns no stdout when not found 294 ret=buffer; 295 #elif defined(rez_OS_BSD) 296 // on bsd which returns "XXX: Command not found." when not found 297 if(strstr(buffer,": Command not found.")==NULL) 298 ret=buffer; 299 #elif defined(rez_OS_SOLARIS) 300 // on solaris if the path is really long it puts "Warning: ..." on the first line 301 if(strncmp(buffer,"Warning: ",9)==0) 302 goto redo; 303 304 // on solaris which returns "no XXX in ..." when not found 305 if(strncmp(buffer,"no ",3)!=0) 306 ret=buffer; 307 #else 308 #error CPath::which needs to be implemented on this platform 309 #endif 310 } 311 pclose(p); 312 313 return ret; 314 } 315 316 private: 317 char *path; 318 bool _exists; 319 bool _permDenied; 320 struct stat statBuf; 321 322 }; 323 324 #endif 325