1//===- PathV3.inc ---------------------------------------------------------===//
2//
3//                     The MCLinker Project
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9#include "mcld/Support/FileSystem.h"
10#include "mcld/Support/Path.h"
11
12#include <llvm/Support/ErrorHandling.h>
13
14#include <cerrno>
15#include <stack>
16#include <stdio.h>
17#include <string>
18#include <sys/stat.h>
19#include <sys/types.h>
20#include <unistd.h>
21
22namespace mcld {
23namespace sys {
24namespace fs {
25
26//===----------------------------------------------------------------------===//
27// mcld::sys::fs::detail
28//===----------------------------------------------------------------------===//
29namespace detail {
30
31// return the last charactor being handled.
32size_t canonicalize(std::string& pathname) {
33  // Variable Index //
34  // SepTable - stack of result separators
35  // LR(1) Algorithm //
36  // traverse pPathName
37  //   if we meet '//', '///', '////', ...
38  //     -> ignore it
39  //     -> push current into stack
40  //     -> jump to the next not '/'
41  //   if we meet '/./'
42  //     -> ignore
43  //     -> jump to the next not '/'
44  //   if we meet '/../'
45  //     -> pop previous position of '/' P
46  //     -> erase P+1 to now
47  //   if we meet other else
48  //     -> go go go
49  //   if we meet '/.../', '/..../', ... -> illegal
50  if (pathname.empty())
51    return 0;
52
53  size_t handler = 0;
54  std::stack<size_t> slash_stack;
55  slash_stack.push(-1);
56  while (handler < pathname.size()) {
57    if (separator == pathname[handler]) {  // handler = 1st '/'
58      size_t next = handler + 1;
59      if (next >= pathname.size())
60        return handler;
61      switch (pathname[next]) {  // next = handler + 1;
62        case separator: {        // '//'
63          while (next < pathname.size() && separator == pathname[next])
64            ++next;
65          // next is the last not '/'
66          pathname.erase(handler, next - handler - 1);
67          // handler is the first '/'
68          slash_stack.push(handler);
69          break;
70        }
71        case '.': {                     // '/.'
72          ++next;                       // next = handler + 2
73          if (next >= pathname.size())  // '/.'
74            return handler;
75          switch (pathname[next]) {
76            case separator: {  // '/./'
77              pathname.erase(handler, 2);
78              break;
79            }
80            case '.': {                     // '/..'
81              ++next;                       // next = handler + 3;
82              if (next >= pathname.size())  // '/..?'
83                return handler;
84              switch (pathname[next]) {
85                case separator: {  // '/../'
86                  handler = slash_stack.top();
87                  slash_stack.pop();
88                  pathname.erase(handler + 1, next - handler);
89                  if (static_cast<size_t>(-1) == handler) {
90                    slash_stack.push(-1);
91                    handler = pathname.find_first_of(separator, handler);
92                  }
93                  break;
94                }
95                case '.': {  // '/...', illegal
96                  return handler;
97                  break;
98                }
99                default: {  // '/..a'
100                  slash_stack.push(handler);
101                  handler = pathname.find_first_of(separator, handler + 3);
102                  break;
103                }
104              }
105              break;
106            }
107            default: {  // '/.a'
108              slash_stack.push(handler);
109              handler = pathname.find_first_of(separator, handler + 2);
110              break;
111            }
112          }
113          break;
114        }
115        default: {  // '/a
116          slash_stack.push(handler);
117          handler = pathname.find_first_of(separator, handler + 1);
118          break;
119        }
120      }
121    } else {
122      handler = pathname.find_first_of(separator, handler);
123    }
124  }
125  return handler;
126}
127
128bool not_found_error(int perrno) {
129  return perrno == ENOENT || perrno == ENOTDIR;
130}
131
132void status(const Path& p, FileStatus& pFileStatus) {
133  struct stat path_stat;
134  if (stat(p.c_str(), &path_stat) != 0) {
135    if (not_found_error(errno)) {
136      pFileStatus.setType(FileNotFound);
137    } else
138      pFileStatus.setType(StatusError);
139  } else if (S_ISDIR(path_stat.st_mode))
140    pFileStatus.setType(DirectoryFile);
141  else if (S_ISREG(path_stat.st_mode))
142    pFileStatus.setType(RegularFile);
143  else if (S_ISBLK(path_stat.st_mode))
144    pFileStatus.setType(BlockFile);
145  else if (S_ISCHR(path_stat.st_mode))
146    pFileStatus.setType(CharacterFile);
147  else if (S_ISFIFO(path_stat.st_mode))
148    pFileStatus.setType(FifoFile);
149  else if (S_ISSOCK(path_stat.st_mode))
150    pFileStatus.setType(SocketFile);
151  else
152    pFileStatus.setType(TypeUnknown);
153}
154
155void symlink_status(const Path& p, FileStatus& pFileStatus) {
156  struct stat path_stat;
157  if (lstat(p.c_str(), &path_stat) != 0) {
158    if (errno == ENOENT || errno == ENOTDIR)  // these are not errors
159    {
160      pFileStatus.setType(FileNotFound);
161    } else
162      pFileStatus.setType(StatusError);
163  }
164  if (S_ISREG(path_stat.st_mode))
165    pFileStatus.setType(RegularFile);
166  if (S_ISDIR(path_stat.st_mode))
167    pFileStatus.setType(DirectoryFile);
168  if (S_ISLNK(path_stat.st_mode))
169    pFileStatus.setType(SymlinkFile);
170  if (S_ISBLK(path_stat.st_mode))
171    pFileStatus.setType(BlockFile);
172  if (S_ISCHR(path_stat.st_mode))
173    pFileStatus.setType(CharacterFile);
174  if (S_ISFIFO(path_stat.st_mode))
175    pFileStatus.setType(FifoFile);
176  if (S_ISSOCK(path_stat.st_mode))
177    pFileStatus.setType(SocketFile);
178  else
179    pFileStatus.setType(TypeUnknown);
180}
181
182/// directory_iterator_increment - increment function implementation
183//
184//  iterator will call this function in two situations:
185//  1. All elements have been put into cache, and iterator stays at the end
186//     of cache. (a real end)
187//  2. Some but not all elements had been put into cache, and we stoped.
188//     An iterator now is staying at the end of cache. (a temporal end)
189mcld::sys::fs::PathCache::entry_type* bring_one_into_cache(DirIterator& pIter) {
190  mcld::sys::fs::PathCache::entry_type* entry = 0;
191  std::string path(pIter.m_pParent->m_Path.native());
192  switch (read_dir(pIter.m_pParent->m_Handler, path)) {
193    case 1: {
194      // read one
195      bool exist = false;
196      entry = pIter.m_pParent->m_Cache.insert(path, exist);
197      if (!exist)
198        entry->setValue(sys::fs::Path(path));
199      break;
200    }
201    case 0:  // meet real end
202      pIter.m_pParent->m_CacheFull = true;
203      break;
204    default:
205    case -1:
206      llvm::report_fatal_error(std::string("Can't read directory: ") +
207                               pIter.m_pParent->path().native());
208      break;
209  }
210  return entry;
211}
212
213}  // namespace detail
214}  // namespace fs
215}  // namespace sys
216}  // namespace mcld
217