Line data Source code
1 : // DUNE DAQ modification notice:
2 : // This file has been modified from the original ATLAS system source for the DUNE DAQ project.
3 : // Fork baseline commit: system-00-00-20 (2020-09-25).
4 : // Renamed since fork: yes (from src/File.cxx to src/File.cpp).
5 :
6 : /*
7 : * File.cxx
8 : * ers
9 : *
10 : * Created by Matthias Wiesmann on 04.01.05.
11 : * Copyright 2005 CERN. All rights reserved.
12 : *
13 : */
14 :
15 : #include <assert.h>
16 : #include <dirent.h>
17 : #include <errno.h>
18 : #include <fcntl.h>
19 : #include <fstream>
20 : #include <iostream>
21 : #include <libgen.h>
22 : #include <pwd.h>
23 : #include <sstream>
24 : #include <stdio.h>
25 : #include <sys/param.h>
26 : #include <sys/stat.h>
27 : #include <sys/types.h>
28 : #include <unistd.h>
29 :
30 : #include "ers/ers.hpp"
31 :
32 : #include "okssystem/Executable.hpp"
33 : #include "okssystem/File.hpp"
34 : #include "okssystem/User.hpp"
35 : #include "okssystem/exceptions.hpp"
36 :
37 : #define SPACE_CHAR ' '
38 :
39 : const char* const OksSystem::File::HUMAN_SIZE_STR[] = { "B", "KB", "MB", "GB" };
40 : const char* const OksSystem::File::HUMAN_OPEN_STR[] = { "READ", "WRITE", "NOBLOCK", "APPEND",
41 : "CREATE", "TRUNCATE", "EXCLUSIVE" };
42 : const char* const OksSystem::File::FILE_COMMAND_PATH = "/usr/bin/file";
43 : const char* const OksSystem::File::FILE_FLAG_STR = "-rwxS";
44 : const char* const OksSystem::File::FILE_PROTOCOL = "file";
45 :
46 : #define READ_CHAR_CODE (OksSystem::File::FILE_FLAG_STR[1])
47 : #define WRITE_CHAR_CODE (OksSystem::File::FILE_FLAG_STR[2])
48 : #define EXECUTE_CHAR_CODE (OksSystem::File::FILE_FLAG_STR[3])
49 : #define NOTHING_CHAR_CODE (OksSystem::File::FILE_FLAG_STR[0])
50 : #define SETUID_CHAR_CODE (OksSystem::File::FILE_FLAG_STR[4])
51 :
52 : #define SLASH_CHAR '/'
53 : #define COLON_CHAR ':'
54 : #define DOT_CHAR '.'
55 :
56 : const int OksSystem::File::KILOBYTE = 1024;
57 :
58 : // --------------------------------------
59 : // Static Methods
60 : // --------------------------------------
61 :
62 : /** Extracts the protocol part of an url.
63 : * The protocol part is everything until the colon (:)
64 : * \param url an url
65 : * \return the protocol part of an empty string
66 : */
67 :
68 : std::string
69 0 : OksSystem::File::protocol(const std::string& url) throw()
70 : {
71 0 : std::string::size_type colon = url.find(COLON_CHAR);
72 0 : if (colon == std::string::npos)
73 0 : return std::string();
74 0 : return url.substr(0, colon);
75 : } // protocol
76 :
77 : /** Extracts the extension of an url or path
78 : * \param url an url or file path
79 : * \return the extension (without the dot) or an empty string
80 : */
81 :
82 : std::string
83 0 : OksSystem::File::extension(const std::string& url) throw()
84 : {
85 0 : std::string::size_type dot = url.rfind(DOT_CHAR);
86 0 : if (dot == std::string::npos)
87 0 : return std::string();
88 0 : return url.substr(dot + 1);
89 : } // extension
90 :
91 : /** Extracts the short file name of an url or path
92 : * \param url an url or file path
93 : * \return the short name or the full url if it cannot be found
94 : */
95 :
96 : std::string
97 0 : OksSystem::File::short_name(const std::string& url) throw()
98 : {
99 0 : std::string::size_type slash = url.rfind(SLASH_CHAR);
100 0 : if (slash == std::string::npos)
101 0 : return url;
102 0 : return url.substr(slash + 1);
103 : } // extension
104 :
105 : /** Extacts the uri part of an url.
106 : * The uri of an url is everything except the protocol part
107 : * In the case of \c file or \c http urls, the uri is the local path
108 : * \param url an url
109 : * \return the uri or the full url if no colon in the string
110 : */
111 :
112 : std::string
113 0 : OksSystem::File::uri(const std::string& url) throw()
114 : {
115 0 : std::string::size_type colon = url.find(COLON_CHAR);
116 0 : if (colon == std::string::npos)
117 0 : return url;
118 0 : return url.substr(colon + 1);
119 : } // uri
120 :
121 : /** Calculates the depth of a file.
122 : * This depth is the number of parent directories.
123 : * Files in the root directory have depht of 0,
124 : * other files have a depth of their parent's depth + 1
125 : * \param path to analyze
126 : * \return depth or -1 if the format of the path is not understood.
127 : */
128 :
129 : int
130 0 : OksSystem::File::depth(const std::string& path) throw()
131 : {
132 0 : if (path[0] == SLASH_CHAR) {
133 0 : int count = 0;
134 0 : std::string rest = path.substr(1);
135 0 : while (!rest.empty()) {
136 0 : std::string::size_type slash = rest.find(SLASH_CHAR);
137 0 : if (slash == std::string::npos)
138 : return count;
139 0 : count++;
140 0 : rest = rest.substr(slash + 1);
141 : } // while
142 : return count;
143 0 : } // path with slash
144 0 : if (protocol(path) == FILE_PROTOCOL) {
145 0 : return depth(uri(path));
146 : } //
147 : return -1;
148 : } // depth
149 :
150 : /** Extracts the first line of a text
151 : * \param text the text to process
152 : * \return the first line of the text
153 : */
154 :
155 : std::string
156 0 : OksSystem::File::first_line(const std::string& text)
157 : {
158 0 : std::string::size_type nl = text.find('\n');
159 0 : if (nl > 0)
160 0 : return text.substr(0, nl);
161 0 : return text;
162 : } // first_line
163 :
164 : /** Builds a \c File object ou of an url
165 : * \param url the url, it must start with the file protocol
166 : * \return a File object
167 : */
168 :
169 : OksSystem::File
170 0 : OksSystem::File::from_url(const std::string& url)
171 : {
172 0 : std::string prot = protocol(url);
173 0 : ERS_PRECONDITION(prot == FILE_PROTOCOL);
174 0 : const std::string path = uri(url);
175 0 : return File(path);
176 0 : } // from_url
177 :
178 : /** \return a string containing the working directory for the process */
179 :
180 : std::string
181 0 : OksSystem::File::working_directory()
182 : {
183 0 : char* wd_buffer = getcwd(0, MAXPATHLEN);
184 0 : if (wd_buffer) {
185 0 : std::string directory = std::string(wd_buffer);
186 0 : free(wd_buffer);
187 0 : return directory;
188 : }
189 0 : std::string message("getting the current working directory");
190 0 : throw OksSystem::OksSystemCallIssue(ERS_HERE, errno, "getcwd", message.c_str());
191 0 : } // working_directory
192 :
193 : void
194 0 : OksSystem::File::working_directory(const File& dir)
195 : {
196 0 : const char* path = dir.c_full_name();
197 0 : const int status = ::chdir(path);
198 0 : if (status < 0) {
199 0 : std::string message = "on directory " + dir.full_name();
200 0 : throw OksSystem::OksSystemCallIssue(ERS_HERE, errno, "chdir", message.c_str());
201 0 : }
202 0 : } // working_directory
203 :
204 : /** Expands a file containing a home directory reference (~user).
205 : */
206 :
207 : std::string
208 0 : OksSystem::File::expand_home(const std::string path)
209 : {
210 0 : ERS_PRECONDITION(!path.empty());
211 0 : ERS_PRECONDITION(path[0] == '~');
212 0 : std::string::size_type slash = path.find('/');
213 0 : const std::string user_name = path.substr(1, slash - 1);
214 0 : const std::string rest_path = path.substr(slash);
215 0 : OksSystem::User user;
216 0 : if (!user_name.empty()) {
217 0 : user = OksSystem::User(user_name);
218 : }
219 0 : return user.home() + rest_path;
220 0 : } // home_directory
221 :
222 : /** Builds a prettyfied version of permissions.
223 : * This should look similar to what \c ls returns with the -l flag
224 : * \param permissions the permission to beautify
225 : * \return a string containing the beautified permissions
226 : */
227 :
228 : std::string
229 0 : OksSystem::File::pretty_permissions(mode_t permissions)
230 : {
231 0 : char buffer[11];
232 0 : buffer[0] = (permissions & S_ISUID) ? SETUID_CHAR_CODE : NOTHING_CHAR_CODE;
233 0 : buffer[1] = (permissions & S_IRUSR) ? READ_CHAR_CODE : NOTHING_CHAR_CODE;
234 0 : buffer[2] = (permissions & S_IWUSR) ? WRITE_CHAR_CODE : NOTHING_CHAR_CODE;
235 0 : buffer[3] = (permissions & S_IXUSR) ? EXECUTE_CHAR_CODE : NOTHING_CHAR_CODE;
236 0 : buffer[4] = (permissions & S_IRGRP) ? READ_CHAR_CODE : NOTHING_CHAR_CODE;
237 0 : buffer[5] = (permissions & S_IWGRP) ? WRITE_CHAR_CODE : NOTHING_CHAR_CODE;
238 0 : buffer[6] = (permissions & S_IXGRP) ? EXECUTE_CHAR_CODE : NOTHING_CHAR_CODE;
239 0 : buffer[7] = (permissions & S_IROTH) ? READ_CHAR_CODE : NOTHING_CHAR_CODE;
240 0 : buffer[8] = (permissions & S_IWOTH) ? WRITE_CHAR_CODE : NOTHING_CHAR_CODE;
241 0 : buffer[9] = (permissions & S_IXOTH) ? EXECUTE_CHAR_CODE : NOTHING_CHAR_CODE;
242 0 : buffer[10] = '\0';
243 0 : return std::string(buffer);
244 : } // pretty_permissions
245 :
246 : /** Builds a prettyfied version of flags used by the \c open OksSystem call
247 : * \param flags as passed to \c open
248 : * \return a string containing the beautified flags
249 : * \note This method only handles flags available accross plateforms.
250 : */
251 :
252 : std::string
253 0 : OksSystem::File::pretty_open_flag(int flags)
254 : {
255 0 : std::ostringstream flag_str;
256 0 : if (flags & O_WRONLY) {
257 0 : flag_str << HUMAN_OPEN_STR[1];
258 0 : } else if (flags & O_RDWR) {
259 0 : flag_str << HUMAN_OPEN_STR[0] << "-" << HUMAN_OPEN_STR[1];
260 : } else {
261 0 : flag_str << HUMAN_OPEN_STR[0];
262 : }
263 0 : if (flags & O_NONBLOCK) {
264 0 : flag_str << "/" << HUMAN_OPEN_STR[2];
265 : }
266 0 : if (flags & O_APPEND) {
267 0 : flag_str << "/" << HUMAN_OPEN_STR[3];
268 : }
269 0 : if (flags & O_CREAT) {
270 0 : flag_str << "/" << HUMAN_OPEN_STR[4];
271 : }
272 0 : if (flags & O_TRUNC) {
273 0 : flag_str << "/" << HUMAN_OPEN_STR[5];
274 : }
275 0 : if (flags & O_EXCL) {
276 0 : flag_str << "/" << HUMAN_OPEN_STR[6];
277 : }
278 0 : flag_str << "(" << flags << ")";
279 0 : return flag_str.str();
280 0 : } // pretty_open_permission
281 :
282 : int
283 0 : OksSystem::File::unit(int u)
284 : {
285 0 : ERS_PRECONDITION(u >= 0);
286 0 : if (u == 0)
287 0 : return 1;
288 0 : return KILOBYTE * unit(u - 1);
289 : } // unit
290 :
291 : /** Builds a human readable textual description of a file size.
292 : * The file size is expressed in Gigabytes (GB), Megabytes (MB) and Kilobytes (KB) and Bytes (B).
293 : * \param size the size of the file
294 : * \param cut_small should the display only contain the highest quantity?
295 : * For example if a file is 1 gigabyte and 20 bytes, if \c cut_small is \c true, then
296 : * the 20 bytes are ignored.
297 : * \return a string containing the textual description
298 : */
299 :
300 : std::string
301 0 : OksSystem::File::pretty_size(size_t size, bool cut_small)
302 : {
303 0 : if (0 == size)
304 0 : return "0";
305 0 : std::ostringstream size_str;
306 : bool writen_something = false;
307 0 : for (int i = 3; i >= 0; i--) {
308 0 : const unsigned int size_unit = unit(i);
309 0 : if (size > size_unit && !(cut_small && writen_something)) {
310 0 : const int amount = size / size_unit;
311 0 : size = size % size_unit;
312 0 : size_str << amount << SPACE_CHAR << HUMAN_SIZE_STR[i];
313 : writen_something = true;
314 : } // if unit is present
315 : } // loop
316 0 : return size_str.str();
317 0 : } // pretty_size
318 :
319 : // --------------------------------------
320 : // Constructors
321 : // --------------------------------------
322 :
323 : /** Constructor
324 : * \note This method does not create a file, it simply builds an object describing a file
325 : */
326 :
327 0 : OksSystem::File::File(const std::string& name)
328 : {
329 0 : set_name(name);
330 0 : } // File
331 :
332 : /** Constructor
333 : * \overload
334 : */
335 :
336 0 : OksSystem::File::File(const char* c_name)
337 : {
338 0 : ERS_PRECONDITION(c_name);
339 0 : std::string name = std::string(c_name);
340 0 : set_name(name);
341 0 : } // File
342 :
343 : /** Constructor
344 : * \overload
345 : */
346 :
347 0 : OksSystem::File::File(const File& other)
348 : {
349 0 : set_name(other.m_full_name);
350 0 : } // File
351 :
352 : // --------------------------------------
353 : // Operators
354 : // --------------------------------------
355 :
356 0 : OksSystem::File::operator std::string() const throw()
357 : {
358 0 : return m_full_name;
359 : } // operator std::string
360 :
361 0 : OksSystem::File::operator const char*() const throw()
362 : {
363 0 : return m_full_name.c_str();
364 : } // operator const char*
365 :
366 : /** Cast to boolean, checks if the file exists
367 : * \return \c true if the file exists
368 : */
369 :
370 0 : OksSystem::File::operator bool() const throw()
371 : {
372 0 : return exists();
373 : } // operator bool
374 :
375 : /** Comparison operator
376 : * As path are canonicalized, we simply compare the full paths.
377 : * \param other file to compare this file to
378 : * \return true if both file are equal
379 : * \note This comparison method does not taken hard links into account
380 : */
381 :
382 : bool
383 0 : OksSystem::File::equals(const File& other) const throw()
384 : {
385 0 : return (m_full_name == other.m_full_name);
386 : } // operator==
387 :
388 : /** Cast to size type
389 : * \return the size of the file
390 : * \see size()
391 : */
392 :
393 0 : OksSystem::File::operator size_t() const
394 : {
395 0 : return size();
396 : } // operator size_t
397 :
398 : // --------------------------------------
399 : // Methods
400 : // --------------------------------------
401 :
402 : /** Sets the name for a file.
403 : * The path name is first expanded and the canonlicalized.
404 : * Expension means the following
405 : * \li If the path starts with / nothing is done
406 : * \li If the path starts with ~ the username is resolved and the path starts from there
407 : * \li If the path starts with any other character, the working directory is prepended.
408 : *
409 : * The path is then made canonical, this implies removing all ./ and ../ sequences,
410 : * and resolving all symbolic links.
411 : * \param name the name of the file
412 : */
413 :
414 : void
415 0 : OksSystem::File::set_name(const std::string& name)
416 : {
417 0 : ERS_PRECONDITION(!name.empty());
418 0 : const char c = name[0];
419 0 : std::string long_path;
420 0 : switch (c) {
421 0 : case '/':
422 0 : long_path = name;
423 : break;
424 0 : case '~':
425 0 : long_path = expand_home(name);
426 0 : break;
427 0 : default:
428 0 : long_path = working_directory() + "/" + name;
429 0 : break;
430 : } // switch
431 0 : char buffer[PATH_MAX];
432 0 : const char* result = realpath(long_path.c_str(), buffer);
433 0 : if (result == 0) { // could not resolve path
434 0 : m_full_name = long_path;
435 : } else { // we could canonicalize the path
436 0 : m_full_name = buffer;
437 : } //
438 0 : } // set_name
439 :
440 : /** \return the full (absolute) path of the file */
441 :
442 : const std::string&
443 0 : OksSystem::File::full_name() const throw()
444 : {
445 0 : return m_full_name;
446 : } // full_name
447 :
448 : /** \return the full (absolute) path of the file */
449 :
450 : const char*
451 0 : OksSystem::File::c_full_name() const throw()
452 : {
453 0 : return m_full_name.c_str();
454 : } // c_full_name
455 :
456 : /** \return the short name of the file - that is the name of the file in its directory */
457 :
458 : std::string
459 0 : OksSystem::File::short_name() const throw()
460 : {
461 0 : return short_name(m_full_name);
462 : } // short_name
463 :
464 : /** Finds the name of the enclosing directory
465 : * \return the path of the directory containing the file
466 : */
467 :
468 : std::string
469 0 : OksSystem::File::parent_name() const throw()
470 : {
471 0 : std::string::size_type size = m_full_name.size();
472 0 : std::string::size_type slash = m_full_name.rfind(SLASH_CHAR, size - 1);
473 0 : if (slash == std::string::npos || slash == 0)
474 0 : return ("/");
475 0 : return m_full_name.substr(0, slash);
476 : } // directory
477 :
478 : /** \return the extension of the file */
479 :
480 : std::string
481 0 : OksSystem::File::extension() const throw()
482 : {
483 0 : return extension(m_full_name);
484 : } // extension
485 :
486 : int
487 0 : OksSystem::File::depth() const throw()
488 : {
489 0 : return depth(m_full_name);
490 : } // depth
491 :
492 : /** \return the parent directory */
493 :
494 : OksSystem::File
495 0 : OksSystem::File::parent() const
496 : {
497 0 : return File(parent_name());
498 : } // parent
499 :
500 : OksSystem::File
501 0 : OksSystem::File::child(const std::string& name) const
502 : {
503 0 : std::string child_name = m_full_name + "/" + name;
504 0 : return OksSystem::File(child_name);
505 0 : } // child
506 :
507 : OksSystem::File
508 0 : OksSystem::File::temporary(const std::string& prefix) const
509 : {
510 0 : char* tmp_name = tempnam(m_full_name.c_str(), prefix.c_str());
511 0 : if (!tmp_name) {
512 0 : std::string message = "while creating a valid filename in directory " + m_full_name;
513 0 : throw OksSystem::OksSystemCallIssue(ERS_HERE, errno, "tempnam", message.c_str());
514 0 : }
515 0 : OksSystem::File tmp_file(tmp_name);
516 0 : free(tmp_name);
517 0 : return tmp_file;
518 : } // temporary
519 :
520 : /** Checks if the file exists in the fileOksSystem
521 : * \return \c true if the file exists, false otherwise
522 : * \exception ers::IOIssue if an error occurs
523 : */
524 :
525 : bool
526 0 : OksSystem::File::exists() const throw()
527 : {
528 0 : struct stat file_status;
529 0 : const int result = stat(m_full_name.c_str(), &file_status);
530 0 : if (0 == result)
531 0 : return true;
532 : return false;
533 : } // exists
534 :
535 : /** Extracts the mode information of the file.
536 : * This is used to determine both the file type and the file permissions
537 : * \return the mode of the file
538 : * \exception OksSystem::FStatFail if fstat fails
539 : */
540 :
541 : mode_t
542 0 : OksSystem::File::get_mode() const
543 : {
544 0 : struct stat file_status;
545 0 : const int result = stat(m_full_name.c_str(), &file_status);
546 0 : if (0 == result) {
547 0 : return file_status.st_mode;
548 : } // if
549 0 : std::string message = "on file/directory " + m_full_name;
550 0 : throw OksSystem::OksSystemCallIssue(ERS_HERE, errno, "stat", message.c_str());
551 0 : } // mode
552 :
553 : /** \return the permissions for the file
554 : * \exception ers::IOIssue if an error occurs or the file does not exist
555 : */
556 :
557 : mode_t
558 0 : OksSystem::File::permissions() const
559 : {
560 0 : mode_t mode = get_mode();
561 0 : return (mode & 07777);
562 : } // permissions
563 :
564 : /** \return the prettfied permissions for the file
565 : * \exception ers::IOIssue if an error occurs or the file does not exist
566 : */
567 :
568 : std::string
569 0 : OksSystem::File::pretty_permissions() const
570 : {
571 0 : return pretty_permissions(permissions());
572 : } // pretty_permissions
573 :
574 : /** Converts permissions into a string
575 : * \param permissions the permissions
576 : * \return a string contaning the permissions in octal form
577 : */
578 :
579 : std::string
580 0 : OksSystem::File::to_string(mode_t permissions) throw()
581 : {
582 0 : std::ostringstream stream;
583 0 : stream.setf(std::ios::oct, std::ios::basefield);
584 0 : stream << permissions;
585 0 : return stream.str();
586 0 : } // to_string
587 :
588 : /** \return the size (in bytes) of the file
589 : * \exception OksSystem::IOIssue if an error occurs or the file does not exist
590 : */
591 :
592 : size_t
593 0 : OksSystem::File::size() const
594 : {
595 0 : struct stat file_status;
596 0 : const int result = stat(m_full_name.c_str(), &file_status);
597 0 : if (0 == result)
598 0 : return file_status.st_size;
599 0 : std::string message = "on file/directory " + m_full_name;
600 0 : throw OksSystem::OksSystemCallIssue(ERS_HERE, errno, "stat", message.c_str());
601 0 : } // size
602 :
603 : /** \return the user-id of the owner of the file
604 : * \exception OksSystem::IOIssue if an error occurs or the file does not exist
605 : */
606 :
607 : uid_t
608 0 : OksSystem::File::owner_id() const
609 : {
610 0 : struct stat file_status;
611 0 : const int result = stat(m_full_name.c_str(), &file_status);
612 0 : if (0 == result)
613 0 : return file_status.st_uid;
614 0 : std::string message = "on file/directory " + m_full_name;
615 0 : throw OksSystem::OksSystemCallIssue(ERS_HERE, errno, "stat", message.c_str());
616 0 : } // owner_id
617 :
618 : OksSystem::User
619 0 : OksSystem::File::owner() const
620 : {
621 0 : return User(owner_id());
622 : } // owner
623 :
624 : /** \return the group-id of the group of the file
625 : * \exception ers::IOIssue if an error occurs or the file does not exist
626 : */
627 :
628 : gid_t
629 0 : OksSystem::File::group() const
630 : {
631 0 : struct stat file_status;
632 0 : const int result = stat(m_full_name.c_str(), &file_status);
633 0 : if (0 == result)
634 0 : return file_status.st_gid;
635 0 : std::string message = "on file/directory " + m_full_name;
636 0 : throw OksSystem::OksSystemCallIssue(ERS_HERE, errno, "stat", message.c_str());
637 0 : } // owner
638 :
639 : /** Checks if the file a regular file (i.e not a directory, named pipe or anything else).
640 : * \return \c true if the file is regular
641 : * \exception ers::IOIssue if an error occurs or the file does not exist
642 : */
643 :
644 : bool
645 0 : OksSystem::File::is_regular() const
646 : {
647 0 : const mode_t mode = get_mode();
648 0 : return (mode & S_IFREG);
649 : } // is_regular
650 :
651 : /** Checks if the file a directory
652 : * \return \c true if the file is a directory
653 : * \exception ers::IOIssue if an error occurs or the file does not exist
654 : */
655 :
656 : bool
657 0 : OksSystem::File::is_directory() const
658 : {
659 0 : const mode_t mode = get_mode();
660 0 : return (mode & S_IFDIR);
661 : } // is_regular
662 :
663 : /** Checks if the file a named pipe (FIFO)
664 : * \return \c true if the file is named pipe (FIFO)
665 : * \exception ers::IOIssue if an error occurs or the file does not exist
666 : */
667 :
668 : bool
669 0 : OksSystem::File::is_fifo() const
670 : {
671 0 : const mode_t mode = get_mode();
672 0 : return (mode & S_IFIFO);
673 : } // is_pipe
674 :
675 : std::string
676 0 : OksSystem::File::file_type() const
677 : {
678 0 : OksSystem::Executable file_command(FILE_COMMAND_PATH);
679 0 : OksSystem::Executable::param_collection params;
680 0 : params.push_back("-b");
681 0 : params.push_back(full_name());
682 0 : return first_line(file_command.pipe_in(params));
683 0 : } // file_type
684 :
685 : /** Builds a vector containing all the files contained in a directory
686 : * \return a vector of files contained in the directory
687 : * \exception OksSystem::OpenFail if the directory could not be openend
688 : * \exception OksSystem::CloseFail if the directory cannot be closed
689 : */
690 :
691 : OksSystem::File::file_list_t
692 0 : OksSystem::File::directory() const
693 : {
694 0 : file_list_t file_vector;
695 0 : DIR* directory_ptr = opendir(m_full_name.c_str());
696 0 : if (!directory_ptr) {
697 0 : std::string message = "on directory " + m_full_name;
698 0 : throw OksSystem::OksSystemCallIssue(ERS_HERE, errno, "opendir", message.c_str());
699 0 : }
700 0 : while (true) {
701 0 : struct dirent* directory_entry = readdir(directory_ptr);
702 0 : if (directory_entry) {
703 0 : const std::string name = directory_entry->d_name;
704 0 : if ((name != ".") && (name != "..")) {
705 0 : std::string path = m_full_name + "/" + name;
706 0 : OksSystem::File file(path);
707 0 : file_vector.push_back(file);
708 0 : } // if
709 0 : } else {
710 : break;
711 : } // if / else
712 0 : } // while
713 0 : int status = closedir(directory_ptr);
714 0 : if (status < 0) {
715 0 : std::string message = "on directory " + m_full_name;
716 0 : throw OksSystem::OksSystemCallIssue(ERS_HERE, errno, "closedir", message.c_str());
717 0 : }
718 0 : return file_vector;
719 0 : } // directory
720 :
721 : /** Unlinks (i.e deletes) a file.
722 : * \exception ers::IOIssue if an error occurs or the file does not exist
723 : */
724 :
725 : void
726 0 : OksSystem::File::unlink() const
727 : {
728 0 : const int result = ::unlink(m_full_name.c_str());
729 0 : if (0 == result)
730 0 : return;
731 0 : throw OksSystem::RemoveFileIssue(ERS_HERE, errno, m_full_name.c_str());
732 : } // unlink
733 :
734 : void
735 0 : OksSystem::File::rmdir() const
736 : {
737 0 : const int result = ::rmdir(m_full_name.c_str());
738 0 : if (0 == result)
739 0 : return;
740 0 : std::string message = "on directory " + m_full_name;
741 0 : throw OksSystem::OksSystemCallIssue(ERS_HERE, errno, "rmdir", message.c_str());
742 0 : } // rmdir
743 :
744 : /** Recursively delete files and directories.
745 : * If the file is a directory, all its child are deleted recursively.
746 : * \exception OksSystem::UnlinkFail if \c unlink fails
747 : */
748 :
749 : void
750 0 : OksSystem::File::remove() const
751 : {
752 0 : if (is_directory()) {
753 0 : file_list_t childs = directory();
754 0 : for (file_list_t::const_iterator p = childs.begin(); p != childs.end(); p++) {
755 0 : const File f = *p;
756 0 : if (f.exists()) {
757 0 : f.remove();
758 : }
759 0 : } // for
760 0 : rmdir();
761 0 : } else {
762 0 : unlink();
763 : }
764 0 : } // remove
765 :
766 : /** Renames or moves a file.
767 : * \param new_name the new name of the file
768 : * \exception ers::IOIssue if an error occurs or the file does not exist
769 : */
770 :
771 : void
772 0 : OksSystem::File::rename(const File& other) const
773 : {
774 0 : const char* source = c_full_name();
775 0 : const char* dest = other.c_full_name();
776 0 : const int result = ::rename(source, dest);
777 0 : if (0 == result)
778 0 : return;
779 0 : throw OksSystem::RenameFileIssue(ERS_HERE, errno, source, dest);
780 : } // rename
781 :
782 : /** Sets the permissions of the file
783 : * \param permissions the new permissions
784 : * \exception ers::IOIssue if an error occurs or the file does not exist
785 : */
786 :
787 : void
788 0 : OksSystem::File::permissions(mode_t perm) const
789 : {
790 0 : if (perm != permissions()) {
791 0 : const int result = ::chmod(m_full_name.c_str(), perm);
792 0 : if (0 == result) {
793 0 : return;
794 : }
795 0 : std::string message = "on file/directory " + m_full_name;
796 0 : throw OksSystem::OksSystemCallIssue(ERS_HERE, errno, "chmod", message.c_str());
797 0 : }
798 : } // permissions
799 :
800 : /** Creates a directory
801 : * The current object is taken as the name of the directory to create.
802 : * \param perm the permissions to associate with the directory
803 : * \note if the directory exists, the method attempts to do a \c chmod
804 : * to match the permissions
805 : */
806 :
807 : void
808 0 : OksSystem::File::make_dir(mode_t perm) const
809 : {
810 :
811 0 : errno = 0;
812 0 : const int result = ::mkdir(m_full_name.c_str(), perm);
813 0 : int mkdir_error = errno;
814 :
815 0 : if (0 == result) {
816 0 : try {
817 0 : permissions(perm);
818 0 : } catch (OksSystem::OksSystemCallIssue& e) {
819 0 : std::string message = "on directory " + m_full_name;
820 0 : throw OksSystem::OksSystemCallIssue(ERS_HERE, errno, "mkdir -> chmod", message.c_str());
821 0 : }
822 : return;
823 : } else {
824 0 : if (exists()) {
825 0 : if (is_directory()) { // already exists we attempt a chmod
826 0 : try {
827 0 : permissions(perm);
828 0 : } catch (OksSystem::OksSystemCallIssue& e) {
829 : // ignore this case...
830 0 : }
831 0 : return;
832 : } // is directory
833 : } else { // exists
834 0 : std::string message = "on directory " + m_full_name;
835 0 : throw OksSystem::OksSystemCallIssue(ERS_HERE, mkdir_error, "mkdir", message.c_str());
836 0 : }
837 : }
838 :
839 : } // makedir
840 :
841 : /** Builds a full path.
842 : * The current object is taken as the name of a directory to create.
843 : * This method creates all the parent directories as needed
844 : * \param permissions the permissions to associate with the directory
845 : * \note If parent directories are created, their permissions will be those
846 : * defined in \c permissions ORed with 0700.
847 : * This is needed to ensure we can actually write into the directories
848 : * we create.
849 : */
850 :
851 : void
852 0 : OksSystem::File::make_path(mode_t perm) const
853 : {
854 0 : const File father = parent();
855 0 : if (!father.exists()) {
856 0 : mode_t father_permission = perm | S_IRWXU; // we need rights to write sons
857 0 : father.make_path(father_permission);
858 : } // if
859 0 : make_dir(perm);
860 0 : } // makepath
861 :
862 : /** Makes sure that the path for a file exists
863 : * This is done by calling \c make_path on the parent directory
864 : * \param permissions permissions used to create directories
865 : * \see OksSystem::File::make_path()
866 : */
867 :
868 : void
869 0 : OksSystem::File::ensure_path(mode_t perm) const
870 : {
871 0 : const File father = parent();
872 0 : father.make_path(perm);
873 0 : } // ensure_path
874 :
875 : /** Creates a named pipe (FIFO)
876 : * \param perm the permission to give the pipe
877 : */
878 :
879 : void
880 0 : OksSystem::File::make_fifo(mode_t perm) const
881 : {
882 0 : if (exists()) {
883 0 : if (is_fifo()) {
884 0 : if (perm != permissions()) {
885 0 : permissions(perm);
886 0 : return;
887 : } else {
888 : return;
889 : }
890 : } // is_fifo
891 : } // exists
892 0 : const int status = ::mkfifo(m_full_name.c_str(), perm);
893 0 : if (status == 0) {
894 0 : permissions(perm);
895 0 : return;
896 : }
897 0 : std::string message = "while creating FIFO " + m_full_name;
898 0 : throw OksSystem::OksSystemCallIssue(ERS_HERE, errno, "mkfifo", message.c_str());
899 0 : } // makefifo
900 :
901 : /** Conversion into a input stream pointer
902 : * This actually creates a new input stream that reads from the file.
903 : * \return a dynamically allocated input stream
904 : * \exception ers::IOIssue if an error occurs or the file does not exist
905 : */
906 :
907 : std::istream*
908 0 : OksSystem::File::input() const
909 : {
910 0 : try {
911 0 : std::ifstream* stream = new std::ifstream(m_full_name.c_str());
912 0 : stream->exceptions(std::ios::failbit | std::ios::badbit);
913 0 : return stream;
914 0 : } catch (std::ios_base::failure& ex) {
915 0 : throw OksSystem::OpenFileIssue(ERS_HERE, errno, m_full_name.c_str(), ex);
916 0 : } // catch
917 : } // std::istream*
918 :
919 : /** Conversion into an output stream pointer
920 : * This actually creates a new output stream that writes to the file.
921 : * \return a dynamically allocated output stream
922 : * \param append is the file opened in append mode
923 : * \exception ers::IOIssue if an error occurs
924 : */
925 :
926 : std::ostream*
927 0 : OksSystem::File::output(bool append) const
928 : {
929 0 : try {
930 0 : std::ios::openmode mode = std::ios::out;
931 0 : if (append) {
932 0 : mode |= std::ios::app;
933 : }
934 0 : std::ofstream* stream = new std::ofstream(m_full_name.c_str(), mode);
935 0 : stream->exceptions(std::ios::failbit | std::ios::badbit);
936 0 : return stream;
937 0 : } catch (std::ios_base::failure& ex) {
938 0 : throw OksSystem::OpenFileIssue(ERS_HERE, errno, m_full_name.c_str(), ex);
939 0 : } // catch
940 : } // std::ostream*
941 :
942 : /** Stream a file object into a STL stream.
943 : * \param stream destination stream.
944 : * \param file the file to write
945 : * \return the stream passed as parameter
946 : */
947 :
948 : std::ostream&
949 0 : operator<<(std::ostream& stream, const OksSystem::File& file)
950 : {
951 0 : stream << OksSystem::File::FILE_PROTOCOL << ":/" << file.full_name();
952 0 : return stream;
953 : } // operator<<
954 :
955 : bool
956 0 : operator==(const OksSystem::File& a, const OksSystem::File& b) throw()
957 : {
958 0 : return a.equals(b);
959 : } // operator ==
960 :
961 : bool
962 0 : operator!=(const OksSystem::File& a, const OksSystem::File& b) throw()
963 : {
964 0 : return !a.equals(b);
965 : } // operator !=
|