CBMC
file_util.cpp
Go to the documentation of this file.
1 /*******************************************************************\
2 
3 Module: File Utilities
4 
5 Author:
6 
7 Date: January 2012
8 
9 \*******************************************************************/
10 
13 
14 #include "file_util.h"
15 
16 #include "exception_utils.h"
17 
18 #include <cerrno>
19 #include <cstring>
20 
21 #if defined(__linux__) || \
22  defined(__FreeBSD_kernel__) || \
23  defined(__GNU__) || \
24  defined(__unix__) || \
25  defined(__CYGWIN__) || \
26  defined(__MACH__)
27 #include <sys/stat.h>
28 #include <unistd.h>
29 #include <dirent.h>
30 #include <cstdlib>
31 #include <cstdio>
32 #endif
33 
34 #ifdef _WIN32
35 #include <util/pragma_push.def>
36 #ifdef _MSC_VER
37 #pragma warning(disable:4668)
38  // using #if/#elif on undefined macro
39 #pragma warning(disable : 5039)
40 // pointer or reference to potentially throwing function passed to extern C
41 #endif
42 #include <io.h>
43 #include <windows.h>
44 #include <direct.h>
45 #include <util/unicode.h>
46 #define chdir _chdir
47 #include <util/pragma_pop.def>
48 #endif
49 
52 {
53 #ifndef _WIN32
54  errno=0;
55  char *wd=realpath(".", nullptr);
56 
57  if(wd == nullptr)
58  throw system_exceptiont(
59  std::string("realpath failed: ") + std::strerror(errno));
60 
61  std::string working_directory=wd;
62  free(wd);
63 #else
64  TCHAR buffer[4096];
65  DWORD retval=GetCurrentDirectory(4096, buffer);
66  if(retval == 0)
67  throw system_exceptiont("failed to get current directory of process");
68 
69 # ifdef UNICODE
70  std::string working_directory(narrow(buffer));
71 # else
72  std::string working_directory(buffer);
73 # endif
74 
75 #endif
76 
77  return working_directory;
78 }
79 
82 void set_current_path(const std::string &path)
83 {
84  if(chdir(path.c_str()) != 0)
85  throw system_exceptiont(
86  std::string("chdir failed: ") + std::strerror(errno));
87 }
88 
90 #ifdef _WIN32
91 
92 void delete_directory_utf16(const std::wstring &path)
93 {
94  std::wstring pattern=path + L"\\*";
95  // NOLINTNEXTLINE(readability/identifiers)
96  struct _wfinddata_t info;
97  intptr_t hFile=_wfindfirst(pattern.c_str(), &info);
98  if(hFile!=-1)
99  {
100  do
101  {
102  if(wcscmp(info.name, L".")==0 || wcscmp(info.name, L"..")==0)
103  continue;
104  std::wstring sub_path=path+L"\\"+info.name;
105  if(info.attrib & _A_SUBDIR)
106  delete_directory_utf16(sub_path);
107  else
108  DeleteFileW(sub_path.c_str());
109  }
110  while(_wfindnext(hFile, &info)==0);
111  _findclose(hFile);
112  RemoveDirectoryW(path.c_str());
113  }
114 }
115 
116 #endif
117 
118 void delete_directory(const std::string &path)
119 {
120 #ifdef _WIN32
121  delete_directory_utf16(utf8_to_utf16_native_endian(path));
122 #else
123  DIR *dir=opendir(path.c_str());
124  if(dir!=nullptr)
125  {
126  struct dirent *ent;
127  while((ent=readdir(dir))!=nullptr)
128  {
129  // Needed for Alpine Linux
130  if(strcmp(ent->d_name, ".")==0 || strcmp(ent->d_name, "..")==0)
131  continue;
132 
133  std::string sub_path=path+"/"+ent->d_name;
134 
135  struct stat stbuf;
136  int result=stat(sub_path.c_str(), &stbuf);
137  if(result!=0)
138  throw system_exceptiont(
139  std::string("Stat failed: ") + std::strerror(errno));
140 
141  if(S_ISDIR(stbuf.st_mode))
142  delete_directory(sub_path);
143  else
144  {
145  result=remove(sub_path.c_str());
146  if(result!=0)
147  throw system_exceptiont(
148  std::string("Remove failed: ") + std::strerror(errno));
149  }
150  }
151  closedir(dir);
152  }
153  rmdir(path.c_str());
154 #endif
155 }
156 
159 std::string concat_dir_file(
160  const std::string &directory,
161  const std::string &file_name)
162 {
163 #ifdef _WIN32
164  if(
165  file_name.size() > 1 && file_name[0] != '/' && file_name[0] != '\\' &&
166  file_name[1] == ':')
167  {
168  return file_name;
169  }
170  else if(
171  !directory.empty() && (directory.back() == '/' || directory.back() == '\\'))
172  {
173  return directory + file_name;
174  }
175  else
176  return directory + '\\' + file_name;
177 #else
178  if(!file_name.empty() && file_name[0] == '/')
179  return file_name;
180  else if(!directory.empty() && directory.back() == '/')
181  return directory + file_name;
182  else
183  return directory + '/' + file_name;
184 #endif
185 }
186 
187 bool is_directory(const std::string &path)
188 {
189  if(path.empty())
190  return false;
191 
192 #ifdef _WIN32
193 
194  auto attributes = ::GetFileAttributesW(widen(path).c_str());
195  if (attributes == INVALID_FILE_ATTRIBUTES)
196  return false;
197  else
198  return (attributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
199 
200 #else
201 
202  struct stat buf;
203 
204  if(stat(path.c_str(), &buf)!=0)
205  return false;
206  else
207  return (buf.st_mode & S_IFDIR) != 0;
208 
209 #endif
210 }
211 
212 bool create_directory(const std::string &path)
213 {
214 #ifdef _WIN32
215  return _mkdir(path.c_str()) == 0;
216 #else
217  // the umask matches what std::filesystem::create_directory does
218  return mkdir(path.c_str(), 0777) == 0;
219 #endif
220 }
221 
222 bool file_exists(const std::string &path)
223 {
224 #ifdef _WIN32
225  return _waccess(utf8_to_utf16_native_endian(path).c_str(), 0) == 0;
226 #else
227  return access(path.c_str(), F_OK) == 0;
228 #endif
229 }
230 
231 bool file_remove(const std::string &path)
232 {
233 #ifdef _WIN32
234  return _wunlink(utf8_to_utf16_native_endian(path).c_str()) == 0;
235 #else
236  return unlink(path.c_str()) == 0;
237 #endif
238 }
239 
240 void file_rename(const std::string &old_path, const std::string &new_path)
241 {
242 #ifdef _WIN32
243  if(is_directory(old_path))
244  {
245  // rename() only renames directories, but does not move them.
246  // MoveFile is not atomic.
247  auto MoveFile_result =
248  MoveFileW(widen(old_path).c_str(), widen(new_path).c_str());
249 
250  if(MoveFile_result == 0)
251  throw system_exceptiont("MoveFileW failed");
252  }
253  else
254  {
255  // C++17 requires this to be atomic.
256  // MoveFile, MoveFileEx() or rename() do not guarantee this.
257  // Any existing file at new_path is to be overwritten.
258  // rename() does not do so on Windows.
259  auto MoveFileEx_result = MoveFileExW(
260  widen(old_path).c_str(),
261  widen(new_path).c_str(),
262  MOVEFILE_REPLACE_EXISTING); // flags
263 
264  if(MoveFileEx_result == 0)
265  throw system_exceptiont("MoveFileExW failed");
266  }
267 #else
268  int rename_result = rename(old_path.c_str(), new_path.c_str());
269 
270  if(rename_result != 0)
271  throw system_exceptiont(
272  std::string("rename failed: ") + std::strerror(errno));
273 #endif
274 }
exception_utils.h
file_util.h
file_remove
bool file_remove(const std::string &path)
C++17 will allow us to use std::filesystem::remove.
Definition: file_util.cpp:231
create_directory
bool create_directory(const std::string &path)
Create a directory with given path C++17 will allow us to use std::filesystem::create_directory.
Definition: file_util.cpp:212
concat_dir_file
std::string concat_dir_file(const std::string &directory, const std::string &file_name)
Definition: file_util.cpp:159
system_exceptiont
Thrown when some external system fails unexpectedly.
Definition: exception_utils.h:71
is_directory
bool is_directory(const std::string &path)
Definition: file_util.cpp:187
file_exists
bool file_exists(const std::string &path)
Check whether file with given path exists.
Definition: file_util.cpp:222
widen
std::wstring widen(const char *s)
Definition: unicode.cpp:48
utf8_to_utf16_native_endian
std::wstring utf8_to_utf16_native_endian(const std::string &in)
Convert UTF8-encoded string to UTF-16 with architecture-native endianness.
Definition: unicode.cpp:191
narrow
output_type narrow(input_type input)
Run-time checked narrowing cast.
Definition: narrow.h:34
unicode.h
get_current_working_directory
std::string get_current_working_directory()
Definition: file_util.cpp:51
set_current_path
void set_current_path(const std::string &path)
Set working directory.
Definition: file_util.cpp:82
delete_directory
void delete_directory(const std::string &path)
deletes all files in 'path' and then the directory itself
Definition: file_util.cpp:118
file_rename
void file_rename(const std::string &old_path, const std::string &new_path)
Rename a file.
Definition: file_util.cpp:240