1 // Copyright (c) 2013, Thomas Goyne <plorkyeran@aegisub.org>
2 //
3 // Permission to use, copy, modify, and distribute this software for any
4 // purpose with or without fee is hereby granted, provided that the above
5 // copyright notice and this permission notice appear in all copies.
6 //
7 // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
8 // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9 // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
10 // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11 // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
12 // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
13 // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
14 //
15 // Aegisub Project http://www.aegisub.org/
16
17 #include "libaegisub/util.h"
18 #include "libaegisub/util_osx.h"
19
20 #include <boost/locale/boundary.hpp>
21 #include <boost/locale/conversion.hpp>
22 #include <boost/range/distance.hpp>
23 #include <ctime>
24
25 namespace {
26 const size_t bad_pos = (size_t)-1;
27 const std::pair<size_t, size_t> bad_match(bad_pos, bad_pos);
28
29 template<typename Iterator>
advance_both(Iterator & folded,Iterator & raw)30 size_t advance_both(Iterator& folded, Iterator& raw) {
31 size_t len;
32 if (*folded == *raw) {
33 len = folded->length();
34 ++folded;
35 }
36 else {
37 // This character was changed by case folding, so refold it and eat the
38 // appropriate number of characters from folded
39 len = boost::locale::fold_case(raw->str()).size();
40 for (size_t folded_consumed = 0; folded_consumed < len; ++folded)
41 folded_consumed += folded->length();
42 }
43
44 ++raw;
45 return len;
46 }
47
find_range(std::string const & haystack,std::string const & needle,size_t start=0)48 std::pair<size_t, size_t> find_range(std::string const& haystack, std::string const& needle, size_t start = 0) {
49 const size_t match_start = haystack.find(needle, start);
50 if (match_start == std::string::npos)
51 return bad_match;
52 return {match_start, match_start + needle.size()};
53 }
54
55 }
56
57 namespace agi { namespace util {
58
strftime(const char * fmt,const tm * tmptr)59 std::string strftime(const char *fmt, const tm *tmptr) {
60 if (!tmptr) {
61 time_t t = time(nullptr);
62 tmptr = localtime(&t);
63 }
64
65 char buff[65536];
66 ::strftime(buff, sizeof buff, fmt, tmptr);
67 return buff;
68 }
69
ifind(std::string const & haystack,std::string const & needle)70 std::pair<size_t, size_t> ifind(std::string const& haystack, std::string const& needle) {
71 const auto folded_hs = boost::locale::fold_case(haystack);
72 const auto folded_n = boost::locale::fold_case(needle);
73 auto match = find_range(folded_hs, folded_n);
74 if (match == bad_match || folded_hs == haystack)
75 return match;
76
77 // We have a match, but the position is an index into the folded string
78 // and we want an index into the unfolded string.
79
80 using namespace boost::locale::boundary;
81 const ssegment_index haystack_characters(character, begin(haystack), end(haystack));
82 const ssegment_index folded_characters(character, begin(folded_hs), end(folded_hs));
83 const size_t haystack_char_count = boost::distance(haystack_characters);
84 const size_t folded_char_count = boost::distance(folded_characters);
85
86 // As of Unicode 6.2, case folding can never reduce the number of
87 // characters, and can only reduce the number of bytes with UTF-8 when
88 // increasing the number of characters. As a result, iff the bytes and
89 // characters are unchanged, no folds changed the size of any characters
90 // and our indices are correct.
91 if (haystack.size() == folded_hs.size() && haystack_char_count == folded_char_count)
92 return match;
93
94 const auto map_folded_to_raw = [&]() -> std::pair<size_t, size_t> {
95 size_t start = -1;
96
97 // Iterate over each pair of characters and refold each character which was
98 // changed by folding, so that we can find the corresponding positions in
99 // the unfolded string
100 auto folded_it = begin(folded_characters);
101 auto haystack_it = begin(haystack_characters);
102 size_t folded_pos = 0;
103
104 while (folded_pos < match.first)
105 folded_pos += advance_both(folded_it, haystack_it);
106 // If we overshot the start then the match started in the middle of a
107 // character which was folded to multiple characters
108 if (folded_pos > match.first)
109 return bad_match;
110
111 start = distance(begin(haystack), begin(*haystack_it));
112
113 while (folded_pos < match.second)
114 folded_pos += advance_both(folded_it, haystack_it);
115 if (folded_pos > match.second)
116 return bad_match;
117
118 return {start, distance(begin(haystack), begin(*haystack_it))};
119 };
120
121 auto ret = map_folded_to_raw();
122 while (ret == bad_match) {
123 // Found something, but it was an invalid match so retry from the next character
124 match = find_range(folded_hs, folded_n, match.first + 1);
125 if (match == bad_match) return match;
126 ret = map_folded_to_raw();
127 }
128
129 return ret;
130 }
131
132 }
133
134 #ifndef __APPLE__
135 namespace osx {
AppNapDisabler(std::string reason)136 AppNapDisabler::AppNapDisabler(std::string reason) { }
~AppNapDisabler()137 AppNapDisabler::~AppNapDisabler() { }
138 }
139 #endif
140 }
141