ChipMaster's trial hacks on C++CMS starting with v1.2.1. Not sure I'll follow on with the v2 since it looks to be breaking and mostly frivolous.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

238 lines
8.3 KiB

  1. //
  2. // Copyright (c) 2009-2011 Artyom Beilis (Tonkikh)
  3. //
  4. // Distributed under the Boost Software License, Version 1.0. (See
  5. // accompanying file LICENSE_1_0.txt or copy at
  6. // http://www.boost.org/LICENSE_1_0.txt)
  7. //
  8. #define BOOSTER_SOURCE
  9. #include "time_zone.h"
  10. //
  11. // Bug - when ICU tries to find a file that is equivalent to /etc/localtime it finds /usr/share/zoneinfo/localtime
  12. // that is just a symbolic link to /etc/localtime.
  13. //
  14. // It started in 4.0 and was fixed in version 4.6, also the fix was backported to the 4.4 branch so it should be
  15. // available from 4.4.3... So we test if the workaround is required
  16. //
  17. // It is also relevant only for Linux, BSD and Apple (as I see in ICU code)
  18. //
  19. #if U_ICU_VERSION_MAJOR_NUM == 4 && (U_ICU_VERSION_MINOR_NUM * 100 + U_ICU_VERSION_PATCHLEVEL_NUM) <= 402
  20. # if defined(__linux) || defined(__FreeBSD__) || defined(__APPLE__)
  21. # define BOOSTER_LOCALE_WORKAROUND_ICU_BUG
  22. # endif
  23. #endif
  24. #ifdef BOOSTER_LOCALE_WORKAROUND_ICU_BUG
  25. #include <dirent.h>
  26. #include <sys/types.h>
  27. #include <sys/stat.h>
  28. #include <unistd.h>
  29. #include <fstream>
  30. #include <pthread.h>
  31. #include <string.h>
  32. #include <booster/auto_ptr_inc.h>
  33. #endif
  34. namespace booster {
  35. namespace locale {
  36. namespace impl_icu {
  37. #ifndef BOOSTER_LOCALE_WORKAROUND_ICU_BUG
  38. // This is normal behavior
  39. icu::TimeZone *get_time_zone(std::string const &time_zone)
  40. {
  41. if(time_zone.empty()) {
  42. return icu::TimeZone::createDefault();
  43. }
  44. else {
  45. return icu::TimeZone::createTimeZone(time_zone.c_str());
  46. }
  47. }
  48. #else
  49. //
  50. // This is a workaround for an ICU timezone detection bug.
  51. // It is \b very ICU specific and should not be used
  52. // in general. It is also designed to work only on
  53. // specific patforms: Linux, BSD and Apple, where this bug may actually
  54. // occur
  55. //
  56. namespace {
  57. // Under BSD, Linux and Mac OS X dirent has normal size
  58. // so no issues with readdir_r
  59. class directory {
  60. public:
  61. directory(char const *name) : d(0),read_result(0)
  62. {
  63. d=opendir(name);
  64. if(!d)
  65. return;
  66. }
  67. ~directory()
  68. {
  69. if(d)
  70. closedir(d);
  71. }
  72. bool is_open()
  73. {
  74. return d;
  75. }
  76. char const *next()
  77. {
  78. if(d && readdir_r(d,&de,&read_result)==0 && read_result!=0)
  79. return de.d_name;
  80. return 0;
  81. }
  82. private:
  83. DIR *d;
  84. struct dirent de;
  85. struct dirent *read_result;
  86. };
  87. bool files_equal(std::string const &left,std::string const &right)
  88. {
  89. char l[256],r[256];
  90. std::ifstream ls(left.c_str());
  91. if(!ls)
  92. return false;
  93. std::ifstream rs(right.c_str());
  94. if(!rs)
  95. return false;
  96. do {
  97. ls.read(l,sizeof(l));
  98. rs.read(r,sizeof(r));
  99. size_t n;
  100. if((n=ls.gcount())!=size_t(rs.gcount()))
  101. return false;
  102. if(memcmp(l,r,n)!=0)
  103. return false;
  104. }while(!ls.eof() || !rs.eof());
  105. if(bool(ls.eof())!=bool(rs.eof()))
  106. return false;
  107. return true;
  108. }
  109. std::string find_file_in(std::string const &ref,size_t size,std::string const &dir)
  110. {
  111. directory d(dir.c_str());
  112. if(!d.is_open())
  113. return std::string();
  114. char const *name=0;
  115. while((name=d.next())!=0) {
  116. std::string file_name = name;
  117. if( file_name == "."
  118. || file_name ==".."
  119. || file_name=="posixrules"
  120. || file_name=="localtime")
  121. {
  122. continue;
  123. }
  124. struct stat st;
  125. std::string path = dir+"/"+file_name;
  126. if(stat(path.c_str(),&st)==0) {
  127. if(S_ISDIR(st.st_mode)) {
  128. std::string res = find_file_in(ref,size,path);
  129. if(!res.empty())
  130. return file_name + "/" + res;
  131. }
  132. else {
  133. if(size_t(st.st_size) == size && files_equal(path,ref)) {
  134. return file_name;
  135. }
  136. }
  137. }
  138. }
  139. return std::string();
  140. }
  141. // This actually emulates ICU's search
  142. // algorithm... just it ignores localtime
  143. std::string detect_correct_time_zone()
  144. {
  145. char const *tz_dir = "/usr/share/zoneinfo";
  146. char const *tz_file = "/etc/localtime";
  147. struct stat st;
  148. if(::stat(tz_file,&st)!=0)
  149. return std::string();
  150. size_t size = st.st_size;
  151. std::string r = find_file_in(tz_file,size,tz_dir);
  152. if(r.empty())
  153. return r;
  154. if(r.compare(0,6,"posix/")==0 || r.compare(0,6,"right/",6)==0)
  155. return r.substr(6);
  156. return r;
  157. }
  158. //
  159. // Using pthread as:
  160. // - This bug is relevant for only Linux, BSD, Mac OS X and
  161. // pthreads are native threading API
  162. // - The dependency on boost.thread may be removed when using
  163. // more recent ICU versions (so TLS would not be needed)
  164. //
  165. // This the dependency on Boost.Thread is eliminated
  166. //
  167. pthread_once_t init_tz = PTHREAD_ONCE_INIT;
  168. std::string default_time_zone_name;
  169. extern "C" {
  170. static void init_tz_proc()
  171. {
  172. try {
  173. default_time_zone_name = detect_correct_time_zone();
  174. }
  175. catch(...){}
  176. }
  177. }
  178. std::string get_time_zone_name()
  179. {
  180. pthread_once(&init_tz,init_tz_proc);
  181. return default_time_zone_name;
  182. }
  183. } // namespace
  184. icu::TimeZone *get_time_zone(std::string const &time_zone)
  185. {
  186. if(!time_zone.empty()) {
  187. return icu::TimeZone::createTimeZone(time_zone.c_str());
  188. }
  189. std::auto_ptr<icu::TimeZone> tz(icu::TimeZone::createDefault());
  190. icu::UnicodeString id;
  191. tz->getID(id);
  192. // Check if there is a bug?
  193. if(id != icu::UnicodeString("localtime"))
  194. return tz.release();
  195. // Now let's deal with the bug and run the fixed
  196. // search loop as that of ICU
  197. std::string real_id = get_time_zone_name();
  198. if(real_id.empty()) {
  199. // if we failed fallback to ICU's time zone
  200. return tz.release();
  201. }
  202. return icu::TimeZone::createTimeZone(real_id.c_str());
  203. }
  204. #endif // bug workaround
  205. }
  206. }
  207. }
  208. // vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4