Synopsis - Cross-Reference

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