Synopsis - Cross-Reference
File: /src/Support/Path-posix.cc1// 2// Copyright (C) 2004 Stefan Seefeld 3// All rights reserved. 4// Licensed to the public under the terms of the GNU LGPL (>= 2), 5// see the file COPYING for details. 6// 7 8#include "Path.hh" 9#include <vector> 10#include <algorithm> 11#include <stdexcept> 12#include <cerrno> 13#include <cstring> 14#include <unistd.h> 15#include <sys/types.h> 16#include <sys/stat.h> 17 18using namespace Synopsis; 19 20const char Path::SEPARATOR = '/'; 21 22std::string Path::cwd() 23{ 24 static std::string path; 25 if (path.empty()) 26 for (long path_max = 32;; path_max *= 2) 27 { 28 char *buf = new char[path_max]; 29 if (::getcwd(buf, path_max) == 0) 30 { 31 if (errno != ERANGE) 32 { 33 delete [] buf; 34 throw std::runtime_error(strerror(errno)); 35 } 36 } 37 else 38 { 39 path = buf; 40 delete [] buf; 41 return path; 42 } 43 delete [] buf; 44 } 45 return path; 46} 47 48std::string Path::normalize(const std::string &filename) 49{ 50 std::string value = filename; 51 char separator = '/'; 52 const char *pat1 = "/./"; 53 const char *pat2 = "/../"; 54 if (value[0] != separator) 55 value.insert(0, Path::cwd() + separator); 56 57 // nothing to do... 58 if (value.find(pat1) == std::string::npos && 59 value.find(pat2) == std::string::npos) return value; 60 61 // for the rest we'll operate on a decomposition of the filename... 62 typedef std::vector<std::string> Components; 63 Components components; 64 65 std::string::size_type b = 0; 66 while (b < value.size()) 67 { 68 std::string::size_type e = value.find(separator, b); 69 components.push_back(value.substr(b, e-b)); 70 b = e == std::string::npos ? std::string::npos : e + 1; 71 } 72 73 // remove all '.' and '' components 74 components.erase(std::remove(components.begin(), components.end(), "."), components.end()); 75 components.erase(std::remove(components.begin(), components.end(), ""), components.end()); 76 // now collapse '..' components with the preceding one 77 while (true) 78 { 79 Components::iterator i = std::find(components.begin(), components.end(), ".."); 80 if (i == components.end()) break; 81 if (i == components.begin()) throw std::invalid_argument("invalid path"); 82 components.erase(i - 1, i + 1); // remove two components 83 } 84 85 // now rebuild the path as a string 86 std::string retn = '/' + components.front(); 87 for (Components::iterator i = components.begin() + 1; i != components.end(); ++i) 88 retn += '/' + *i; 89 return retn; 90} 91 92namespace Synopsis 93{ 94 95void makedirs(const Path &path) 96{ 97 const std::string &dir = path.str(); 98 if (dir.empty()) throw std::runtime_error("empty path in 'makedirs'"); 99 std::string::size_type cursor = 0; 100 while (cursor != std::string::npos) 101 { 102 cursor = dir.find(Path::SEPARATOR, cursor + 1); 103 struct stat st; 104 int error; 105 if ((error = stat(dir.substr(0, cursor).c_str(), &st)) == -1 && 106 errno == ENOENT) 107 mkdir(dir.substr(0, cursor).c_str(), 0755); 108 else if (error) throw std::runtime_error(strerror(errno)); 109 } 110} 111 112}