1 #pragma once
2
3 #include <stdlib.h>
4 #include <sys/param.h>
5 #include <sys/stat.h>
6 #include <sys/types.h>
7
8 #include <string>
9
10 namespace scx {
11
12 namespace FileType {
13 enum e
14 {
15 None = 0,
16 Regular,
17 Directory,
18 CharacterSpecial,
19 Block,
20 Pipe,
21 Link,
22 Socket,
23 Other
24 };
25
26 inline std::string
ToString(e type)27 ToString(e type)
28 {
29 const char* names[] = {
30 "None",
31 "Regular",
32 "Directory",
33 "Character Special",
34 "Block",
35 "Pipe/FIFO",
36 "Link",
37 "Socket",
38 "Other"
39 };
40 return names[type];
41 }
42 }
43 using EmFileType = FileType::e;
44
45 class FileInfo
46 {
47 public:
FileInfo(const std::string & file)48 explicit FileInfo(const std::string& file)
49 : m_name(file)
50 , m_exists(false)
51 , m_type(FileType::None)
52 {
53 CheckFileType();
54 }
55
Name()56 std::string Name() const { return m_name; }
57
Exists()58 bool Exists() const { return m_exists; }
59
Type()60 EmFileType Type() const { return m_type; }
61
Size()62 off_t Size() const { return m_exists ? m_stat.st_size : -1; }
63
64 // NOTE: too strict
IsAbs()65 bool IsAbs() const
66 {
67 if (m_name.empty() || !m_exists)
68 return false;
69
70 if (m_name[0] != '/')
71 return false;
72
73 std::string name(m_name);
74 while (name.size() >= 2 && name[name.size() - 1] == '/')
75 name.erase(name.size() - 1, '/');
76 return (name == AbsFilePath());
77 }
78
AbsPath()79 std::string AbsPath() const
80 {
81 if (!m_exists || m_name.empty())
82 return "";
83
84 std::string name(AbsFilePath());
85 name = name.substr(0, name.find_last_of('/'));
86 return (!name.empty() ? name : "/");
87 }
88
AbsFilePath()89 std::string AbsFilePath() const
90 {
91 if (!m_exists)
92 return "";
93
94 char buf[PATH_MAX];
95 return realpath(m_name.c_str(), buf) != nullptr ? std::string(buf) : "";
96 }
97
BaseName()98 std::string BaseName() const
99 {
100 if (!m_exists)
101 return "";
102
103 std::string name(AbsFilePath());
104 size_t pos = name.find_last_of('/');
105 return (pos == std::string::npos) ? name : name.substr(pos + 1);
106 }
107
Suffix()108 std::string Suffix() const
109 {
110 if (!m_exists || NotRegularFile())
111 return "";
112
113 size_t pos = m_name.find_last_of('.');
114 return (pos == std::string::npos) ? "" : m_name.substr(pos + 1);
115 }
116
117 private:
NotRegularFile()118 bool NotRegularFile() const
119 {
120 if (m_name.empty())
121 return true;
122 if (m_exists && m_type == FileType::Directory)
123 return true;
124 if (m_name[m_name.size() - 1] == '/')
125 return true;
126
127 return false;
128 }
129
CheckFileType()130 void CheckFileType()
131 {
132 if (m_name.empty() || ::stat(m_name.c_str(), &m_stat) != 0) {
133 m_exists = false;
134 return;
135 }
136
137 m_exists = true;
138
139 if (S_ISREG(m_stat.st_mode)) {
140 m_type = FileType::Regular;
141 } else if (S_ISDIR(m_stat.st_mode)) {
142 m_type = FileType::Directory;
143 } else if (S_ISCHR(m_stat.st_mode)) {
144 m_type = FileType::CharacterSpecial;
145 } else if (S_ISBLK(m_stat.st_mode)) {
146 m_type = FileType::Block;
147 } else if (S_ISFIFO(m_stat.st_mode)) {
148 m_type = FileType::Pipe;
149 } else if (S_ISLNK(m_stat.st_mode)) {
150 m_type = FileType::Link;
151 } else if (S_ISSOCK(m_stat.st_mode)) {
152 m_type = FileType::Socket;
153 } else {
154 m_type = FileType::Other;
155 }
156 }
157
158 private:
159 struct stat m_stat;
160 std::string m_name;
161 bool m_exists = false;
162 EmFileType m_type = FileType::None;
163 };
164 }
165