Synopsis - Cross-Reference

File: /src/Support/Path-posix.cc
  1//
  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}