Line data Source code
1 : ///
2 : /// @file ar/internal/utilities/os.cpp
3 : /// @copyright (c) 2015 by Petr Zemek (s3rvac@gmail.com) and contributors
4 : /// @license MIT, see the @c LICENSE file for more details
5 : /// @brief Implementation of the operating-system-related utilities.
6 : ///
7 :
8 : #include <algorithm>
9 : #include <fstream>
10 : #include <regex>
11 :
12 : #include "ar/exceptions.h"
13 : #include "ar/internal/utilities/os.h"
14 :
15 : namespace ar {
16 : namespace internal {
17 :
18 : namespace {
19 :
20 5 : bool isPathFromRoot(const std::string& path) {
21 : #ifdef AR_OS_WINDOWS
22 : return std::regex_match(path, std::regex{R"([a-zA-Z]:(/|\\).*)"}) ||
23 : path.front() == '/';
24 : #else
25 5 : return path.front() == '/';
26 : #endif
27 : }
28 :
29 4 : bool endsWithSlash(const std::string& path) {
30 : #ifdef AR_OS_WINDOWS
31 : return path.back() == '\\' || path.back() == '/';
32 : #else
33 4 : return path.back() == '/';
34 : #endif
35 : }
36 :
37 : #ifdef AR_OS_WINDOWS
38 : bool containsForwardSlash(const std::string& path) {
39 : return path.find('/') != std::string::npos;
40 : }
41 : #endif
42 :
43 : } // anonymous namespace
44 :
45 : ///
46 : /// Returns the file name from the given path.
47 : ///
48 : /// If there is no file name, it returns the empty string.
49 : ///
50 6 : std::string fileNameFromPath(const std::string& path) {
51 66 : auto isPathSeparator = [](const auto c) {
52 : #ifdef AR_OS_WINDOWS
53 : return c == '\\' || c == '/';
54 : #else
55 : return c == '/';
56 : #endif
57 66 : };
58 :
59 : return {
60 12 : std::find_if(path.rbegin(), path.rend(),
61 : isPathSeparator).base(),
62 : path.end()
63 12 : };
64 : }
65 :
66 : ///
67 : /// Joins the given paths.
68 : ///
69 7 : std::string joinPaths(const std::string& path1, const std::string& path2) {
70 7 : if (path1.empty()) {
71 1 : return path2;
72 : }
73 :
74 6 : if (path2.empty()) {
75 1 : return path1;
76 : }
77 :
78 5 : if (isPathFromRoot(path2)) {
79 1 : return path2;
80 : }
81 :
82 4 : if (endsWithSlash(path1)) {
83 1 : return path1 + path2;
84 : }
85 :
86 : #ifdef AR_OS_WINDOWS
87 : if (!containsForwardSlash(path1) && !containsForwardSlash(path2)) {
88 : return path1 + '\\' + path2;
89 : }
90 : #endif
91 :
92 3 : return path1 + '/' + path2;
93 : }
94 :
95 : ///
96 : /// Returns the content of the given file.
97 : ///
98 : /// @param[in] path Path to the file.
99 : ///
100 : /// @throws IOError When the file cannot be opened or read.
101 : ///
102 : /// The file is opened in the binary mode, so no conversions are performed
103 : /// during the reading.
104 : ///
105 10 : std::string readFile(const std::string& path) {
106 20 : std::ifstream file{path, std::ios::binary};
107 10 : if (!file) {
108 2 : throw IOError{"cannot open file \"" + path + "\""};
109 : }
110 :
111 8 : file.exceptions(std::ifstream::failbit | std::ifstream::badbit);
112 8 : std::string content;
113 : try {
114 : // The following method gets the file size, resizes the string holding
115 : // the content, and reads the file afterwards. The obtained file size
116 : // is reliable because we have opened the file in the binary mode, so
117 : // there are no conversions performed.
118 8 : file.seekg(0, std::ios::end);
119 8 : content.resize(file.tellg());
120 8 : file.seekg(0, std::ios::beg);
121 8 : file.read(&content[0], content.size());
122 0 : } catch (const std::ifstream::failure& ex) {
123 0 : throw IOError{"cannot read file \"" + path + "\" (" + ex.what() + ")"};
124 : }
125 16 : return content;
126 : }
127 :
128 : ///
129 : /// Stores a file with the given @a content into the given @a path.
130 : ///
131 : /// @throws IOError When the file cannot be opened or written.
132 : ///
133 : /// The file is opened in the binary mode, so no conversions are performed
134 : /// during writing.
135 : ///
136 5 : void writeFile(const std::string& path, const std::string& content) {
137 10 : std::ofstream file{path, std::ios::binary};
138 5 : if (!file) {
139 1 : throw IOError{"cannot open file \"" + path + "\""};
140 : }
141 :
142 4 : file << content;
143 4 : if (!file) {
144 0 : throw IOError{"cannot write file \"" + path + "\""};
145 : }
146 4 : }
147 :
148 : ///
149 : /// Copies file in @a srcPath to a file in @a dstPath.
150 : ///
151 : /// @throws IOError When a file cannot be opened, read, or written.
152 : ///
153 3 : void copyFile(const std::string& srcPath, const std::string& dstPath) {
154 5 : auto content = readFile(srcPath);
155 2 : writeFile(dstPath, content);
156 2 : }
157 :
158 : } // namespace internal
159 : } // namespace ar
|