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/Path.h>
10#include <mcld/Support/FileSystem.h>
11#include <llvm/Support/ErrorHandling.h>
12
13#include <cerrno>
14#include <stdio.h>
15#include <sys/stat.h>
16#include <sys/types.h>
17#include <string>
18#include <stack>
19#include <unistd.h>
20
21namespace mcld{
22namespace sys{
23namespace fs{
24
25//===----------------------------------------------------------------------===//
26// mcld::sys::fs::detail
27//===----------------------------------------------------------------------===//
28namespace detail{
29
30// return the last charactor being handled.
31size_t canonicalize(std::string& pathname)
32{
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    }
122    else {
123      handler = pathname.find_first_of(separator, handler);
124    }
125  }
126  return handler;
127}
128
129bool not_found_error(int perrno)
130{
131  return perrno == ENOENT || perrno == ENOTDIR;
132}
133
134void status(const Path& p, FileStatus& pFileStatus)
135{
136  struct stat path_stat;
137  if(stat(p.c_str(), &path_stat)!= 0)
138  {
139    if(not_found_error(errno))
140    {
141      pFileStatus.setType(FileNotFound);
142    }
143    else
144      pFileStatus.setType(StatusError);
145  }
146  else if(S_ISDIR(path_stat.st_mode))
147    pFileStatus.setType(DirectoryFile);
148  else if(S_ISREG(path_stat.st_mode))
149    pFileStatus.setType(RegularFile);
150  else if(S_ISBLK(path_stat.st_mode))
151    pFileStatus.setType(BlockFile);
152  else if(S_ISCHR(path_stat.st_mode))
153    pFileStatus.setType(CharacterFile);
154  else if(S_ISFIFO(path_stat.st_mode))
155    pFileStatus.setType(FifoFile);
156  else if(S_ISSOCK(path_stat.st_mode))
157    pFileStatus.setType(SocketFile);
158  else
159    pFileStatus.setType(TypeUnknown);
160}
161
162void symlink_status(const Path& p, FileStatus& pFileStatus)
163{
164  struct stat path_stat;
165  if(lstat(p.c_str(), &path_stat)!= 0)
166  {
167    if(errno == ENOENT || errno == ENOTDIR) // these are not errors
168    {
169      pFileStatus.setType(FileNotFound);
170    }
171    else
172      pFileStatus.setType(StatusError);
173  }
174  if(S_ISREG(path_stat.st_mode))
175    pFileStatus.setType(RegularFile);
176  if(S_ISDIR(path_stat.st_mode))
177    pFileStatus.setType(DirectoryFile);
178  if(S_ISLNK(path_stat.st_mode))
179    pFileStatus.setType(SymlinkFile);
180  if(S_ISBLK(path_stat.st_mode))
181    pFileStatus.setType(BlockFile);
182  if(S_ISCHR(path_stat.st_mode))
183    pFileStatus.setType(CharacterFile);
184  if(S_ISFIFO(path_stat.st_mode))
185    pFileStatus.setType(FifoFile);
186  if(S_ISSOCK(path_stat.st_mode))
187    pFileStatus.setType(SocketFile);
188  else
189    pFileStatus.setType(TypeUnknown);
190}
191
192/// directory_iterator_increment - increment function implementation
193//
194//  iterator will call this function in two situations:
195//  1. All elements have been put into cache, and iterator stays at the end
196//     of cache. (a real end)
197//  2. Some but not all elements had been put into cache, and we stoped.
198//     An iterator now is staying at the end of cache. (a temporal end)
199mcld::sys::fs::PathCache::entry_type* bring_one_into_cache(DirIterator& pIter)
200{
201  mcld::sys::fs::PathCache::entry_type* entry = 0;
202  std::string path(pIter.m_pParent->m_Path.native());
203  switch (read_dir(pIter.m_pParent->m_Handler, path)) {
204  case 1: {
205    // read one
206    bool exist = false;
207    entry = pIter.m_pParent->m_Cache.insert(path, exist);
208    if (!exist)
209      entry->setValue(path);
210    break;
211  }
212  case 0:// meet real end
213    pIter.m_pParent->m_CacheFull = true;
214    break;
215  default:
216  case -1:
217    llvm::report_fatal_error(std::string("Can't read directory: ")+
218                             pIter.m_pParent->path().native());
219    break;
220  }
221  return entry;
222}
223
224} // namespace of detail
225} // namespace of fs
226} // namespace of sys
227} // namespace of mcld
228
229