1// Copyright (c) 2013 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "tools/gn/source_dir.h"
6
7#include "base/logging.h"
8#include "tools/gn/filesystem_utils.h"
9#include "tools/gn/source_file.h"
10
11namespace {
12
13void AssertValueSourceDirString(const std::string& s) {
14  if (!s.empty()) {
15    DCHECK(s[0] == '/');
16    DCHECK(EndsWithSlash(s));
17  }
18}
19
20}  // namespace
21
22SourceDir::SourceDir() {
23}
24
25SourceDir::SourceDir(const base::StringPiece& p)
26    : value_(p.data(), p.size()) {
27  if (!EndsWithSlash(value_))
28    value_.push_back('/');
29  AssertValueSourceDirString(value_);
30}
31
32SourceDir::SourceDir(SwapIn, std::string* s) {
33  value_.swap(*s);
34  if (!EndsWithSlash(value_))
35    value_.push_back('/');
36  AssertValueSourceDirString(value_);
37}
38
39SourceDir::~SourceDir() {
40}
41
42SourceFile SourceDir::ResolveRelativeFile(
43    const base::StringPiece& p,
44    const base::StringPiece& source_root) const {
45  SourceFile ret;
46
47  // It's an error to resolve an empty string or one that is a directory
48  // (indicated by a trailing slash) because this is the function that expects
49  // to return a file.
50  if (p.empty() || (p.size() > 0 && p[p.size() - 1] == '/'))
51    return SourceFile();
52  if (p.size() >= 2 && p[0] == '/' && p[1] == '/') {
53    // Source-relative.
54    ret.value_.assign(p.data(), p.size());
55    NormalizePath(&ret.value_);
56    return ret;
57  } else if (IsPathAbsolute(p)) {
58    if (source_root.empty() ||
59        !MakeAbsolutePathRelativeIfPossible(source_root, p, &ret.value_)) {
60#if defined(OS_WIN)
61      // On Windows we'll accept "C:\foo" as an absolute path, which we want
62      // to convert to "/C:..." here.
63      if (p[0] != '/')
64        ret.value_ = "/";
65#endif
66      ret.value_.append(p.data(), p.size());
67    }
68    NormalizePath(&ret.value_);
69    return ret;
70  }
71
72  ret.value_.reserve(value_.size() + p.size());
73  ret.value_.assign(value_);
74  ret.value_.append(p.data(), p.size());
75
76  NormalizePath(&ret.value_);
77  return ret;
78}
79
80SourceDir SourceDir::ResolveRelativeDir(
81    const base::StringPiece& p,
82    const base::StringPiece& source_root) const {
83  SourceDir ret;
84
85  if (p.empty())
86    return ret;
87  if (p.size() >= 2 && p[0] == '/' && p[1] == '/') {
88    // Source-relative.
89    ret.value_.assign(p.data(), p.size());
90    if (!EndsWithSlash(ret.value_))
91      ret.value_.push_back('/');
92    NormalizePath(&ret.value_);
93    return ret;
94  } else if (IsPathAbsolute(p)) {
95    if (source_root.empty() ||
96        !MakeAbsolutePathRelativeIfPossible(source_root, p, &ret.value_)) {
97#if defined(OS_WIN)
98      if (p[0] != '/')  // See the file case for why we do this check.
99        ret.value_ = "/";
100#endif
101      ret.value_.append(p.data(), p.size());
102    }
103    NormalizePath(&ret.value_);
104    if (!EndsWithSlash(ret.value_))
105      ret.value_.push_back('/');
106    return ret;
107  }
108
109  ret.value_.reserve(value_.size() + p.size());
110  ret.value_.assign(value_);
111  ret.value_.append(p.data(), p.size());
112
113  NormalizePath(&ret.value_);
114  if (!EndsWithSlash(ret.value_))
115    ret.value_.push_back('/');
116  AssertValueSourceDirString(ret.value_);
117
118  return ret;
119}
120
121base::FilePath SourceDir::Resolve(const base::FilePath& source_root) const {
122  if (is_null())
123    return base::FilePath();
124
125  std::string converted;
126  if (is_system_absolute()) {
127    if (value_.size() > 2 && value_[2] == ':') {
128      // Windows path, strip the leading slash.
129      converted.assign(&value_[1], value_.size() - 1);
130    } else {
131      converted.assign(value_);
132    }
133    return base::FilePath(UTF8ToFilePath(converted));
134  }
135
136  // String the double-leading slash for source-relative paths.
137  converted.assign(&value_[2], value_.size() - 2);
138  return source_root.Append(UTF8ToFilePath(converted))
139      .NormalizePathSeparatorsTo('/');
140}
141
142void SourceDir::SwapValue(std::string* v) {
143  value_.swap(*v);
144  AssertValueSourceDirString(value_);
145}
146