recursivefilereader.cpp 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. ///////////////////////////////////////////////////////////////////////////////////////
  2. /// \file recursivefilereader.cpp
  3. /// \brief Implementation of the RecursiveFileReader class
  4. ///
  5. /// $Date: 2011-05-04 14:13:45 +0200 (Wed, 04 May 2011) $
  6. ///////////////////////////////////////////////////////////////////////////////////////
  7. #include "recursivefilereader.h"
  8. #include <stdexcept>
  9. #ifdef WIN32
  10. #include <windows.h>
  11. #else
  12. #include <stdlib.h>
  13. #endif
  14. // Some internal functions for handling paths
  15. namespace {
  16. // Internal exception class
  17. // Isn't allowed to propagate outside of RecursiveFileReader
  18. struct PathError : public std::runtime_error {
  19. PathError() : std::runtime_error("path error") {}
  20. };
  21. // Checks if ch is a letter in the english alphabet
  22. bool valid_windows_drive(char ch) {
  23. return (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z');
  24. }
  25. // Checks if ch is a path separator
  26. inline bool is_path_separator(char ch) {
  27. #ifdef WIN32
  28. return ch == '/' || ch == '\\';
  29. #else
  30. return ch == '/';
  31. #endif
  32. }
  33. // Checks if a path is absolute
  34. bool is_absolute(xtring path) {
  35. #ifdef WIN32
  36. // could use PathIsRelative() here, but that requires linking with shlwapi
  37. // Consider the path to be absolute if it starts with \ or a drive
  38. // specifier (like C:\)
  39. return path.left(1) == "\\" ||
  40. (path.len() >= 3 && valid_windows_drive(path[0]) && path[1] == ':' &&
  41. is_path_separator(path[2]));
  42. #else
  43. // On non-Windows systems, consider the path to be absolute if it
  44. // starts with /
  45. return path.left(1) == "/";
  46. #endif
  47. }
  48. // Given a path to a file, this function returns the containing directory
  49. // Trailing directory separator is kept, so "/a/b/c.txt" returns "/a/b/"
  50. xtring get_directory(xtring path) {
  51. // find the last separator
  52. int separator_pos = -1;
  53. for (int i = path.len()-1; i >= 0; i--) {
  54. if (is_path_separator(path[i])) {
  55. separator_pos = i;
  56. break;
  57. }
  58. }
  59. if (separator_pos == -1) {
  60. // no separator found, no directory to be found
  61. throw PathError();
  62. }
  63. // return everything up to and including the separator
  64. return path.left(separator_pos+1);
  65. }
  66. // Convert a path, relative or absolute, to absolute
  67. xtring to_absolute_path(xtring path) {
  68. const int BUFSIZE = 8192;
  69. char buffer[BUFSIZE];
  70. #ifdef WIN32
  71. if (GetFullPathNameA((char*)path, BUFSIZE, buffer, NULL) == 0) {
  72. #else
  73. if (realpath((char*)path, buffer) == NULL) {
  74. #endif
  75. // failed to convert to absolute path
  76. throw PathError();
  77. }
  78. else {
  79. return xtring(buffer);
  80. }
  81. }
  82. // Convert a path to absolute
  83. // If the path is relative, it will be considered to be relative
  84. // to the directory in which the file pointed to by reference is in.
  85. xtring to_absolute_path(xtring path, xtring reference) {
  86. if (is_absolute(path)) {
  87. return path;
  88. }
  89. else {
  90. return to_absolute_path(get_directory(reference) + path);
  91. }
  92. }
  93. }
  94. RecursiveFileReader::RecursiveFileReader() {
  95. }
  96. RecursiveFileReader::~RecursiveFileReader() {
  97. while (!files.empty()) {
  98. closeandpop();
  99. }
  100. }
  101. int RecursiveFileReader::Feof() {
  102. if (files.empty()) {
  103. return 1;
  104. }
  105. int result = feof(currentstream());
  106. if (result != 0) {
  107. // get rid of the current file and try again recursively
  108. closeandpop();
  109. return Feof();
  110. }
  111. else {
  112. return 0;
  113. }
  114. }
  115. int RecursiveFileReader::Fgetc() {
  116. if (files.empty()) {
  117. return EOF;
  118. }
  119. int result = fgetc(currentstream());
  120. if (result == EOF) {
  121. // EOF is returned both on eof and on read error
  122. if (feof(currentstream())) {
  123. // eof, so continue with previous file
  124. closeandpop();
  125. return Fgetc();
  126. }
  127. else {
  128. // read error, don't try to continue
  129. return EOF;
  130. }
  131. }
  132. else {
  133. if (result == '\n') {
  134. files.top().lineno++;
  135. }
  136. return result;
  137. }
  138. }
  139. bool RecursiveFileReader::addfile(const char* path) {
  140. xtring absolute_path;
  141. // Get the absolute path. If the path is relative and we're currently
  142. // reading from a file, consider it to be relative to that file.
  143. try {
  144. if (files.empty()) {
  145. absolute_path = to_absolute_path(path);
  146. }
  147. else {
  148. absolute_path = to_absolute_path(path, currentfilename());
  149. }
  150. } catch (const PathError&) {
  151. return false;
  152. }
  153. FILE* stream = fopen(absolute_path, "rt");
  154. if (!stream) {
  155. return false;
  156. }
  157. else {
  158. files.push(File(stream, absolute_path));
  159. return true;
  160. }
  161. }
  162. xtring RecursiveFileReader::currentfilename() const {
  163. return files.top().filename;
  164. }
  165. int RecursiveFileReader::currentlineno() const {
  166. return files.top().lineno;
  167. }
  168. void RecursiveFileReader::closeandpop() {
  169. fclose(currentstream());
  170. files.pop();
  171. }
  172. FILE* RecursiveFileReader::currentstream() const {
  173. return files.top().stream;
  174. }