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