1 /** @file valuerangeproc.cc
2 * @brief Standard ValueRangeProcessor subclass implementations
4 /* Copyright (C) 2007,2008,2009,2010,2012 Olly Betts
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
23 #include <xapian/queryparser.h>
25 #include <cstdio> // For sprintf().
26 #include <cstdlib> // For atoi().
27 #include "safeerrno.h"
30 #include "stringutils.h"
37 StringValueRangeProcessor::operator()(string
&begin
, string
&end
)
41 // If there's a prefix, require it on the start of the range.
42 if (!startswith(begin
, str
)) {
44 return Xapian::BAD_VALUENO
;
46 begin
.erase(0, str
.size());
47 // But it's optional on the end of the range, e.g. $10..50
48 if (startswith(end
, str
)) {
49 end
.erase(0, str
.size());
52 // If there's a suffix, require it on the end of the range.
53 if (!endswith(end
, str
)) {
55 return Xapian::BAD_VALUENO
;
57 end
.resize(end
.size() - str
.size());
58 // But it's optional on the start of the range, e.g. 10..50kg
59 if (endswith(begin
, str
)) {
60 begin
.resize(begin
.size() - str
.size());
68 decode_xxy(const string
& s
, int & x1
, int &x2
, int &y
)
74 if (s
.size() < 5 || s
.size() > 10) return false;
75 size_t i
= s
.find_first_not_of("0123456789");
76 if (i
< 1 || i
> 2 || !(s
[i
] == '/' || s
[i
] == '-' || s
[i
] == '.'))
78 size_t j
= s
.find_first_not_of("0123456789", i
+ 1);
79 if (j
- (i
+ 1) < 1 || j
- (i
+ 1) > 2 ||
80 !(s
[j
] == '/' || s
[j
] == '-' || s
[j
] == '.'))
82 if (s
.size() - j
> 4 + 1) return false;
83 if (s
.find_first_not_of("0123456789", j
+ 1) != string::npos
)
86 if (x1
< 1 || x1
> 31) return false;
87 x2
= atoi(s
.c_str() + i
+ 1);
88 if (x2
< 1 || x2
> 31) return false;
89 y
= atoi(s
.c_str() + j
+ 1);
93 // We just use this to decide if an ambiguous aa/bb/cc date could be a
94 // particular format, so there's no need to be anal about the exact number of
95 // days in February. The most useful check is that the month field is <= 12
96 // so we could just check the day is <= 31 really.
97 static const char max_month_length
[12] = {
98 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
104 if (m
== -1) return true;
105 if (m
> 12 || m
< 1) return false;
106 if (d
< 1 || d
> max_month_length
[m
- 1]) return false;
110 // NB Assumes the length has been checked to be 10 already.
112 is_yyyy_mm_dd(const string
&s
)
114 return (s
.find_first_not_of("0123456789") == 4 &&
115 s
.find_first_not_of("0123456789", 5) == 7 &&
116 s
.find_first_not_of("0123456789", 8) == string::npos
&&
118 (s
[4] == '-' || s
[4] == '.' || s
[4] == '/'));
122 DateValueRangeProcessor::operator()(string
&begin
, string
&end
)
124 if (StringValueRangeProcessor::operator()(begin
, end
) == BAD_VALUENO
)
127 if ((begin
.size() == 8 || begin
.size() == 0) &&
128 (end
.size() == 8 || end
.size() == 0) &&
129 begin
.find_first_not_of("0123456789") == string::npos
&&
130 end
.find_first_not_of("0123456789") == string::npos
) {
134 if ((begin
.size() == 10 || begin
.size() == 0) &&
135 (end
.size() == 10 || end
.size() == 0)) {
136 if ((begin
.empty() || is_yyyy_mm_dd(begin
)) &&
137 (end
.empty() || is_yyyy_mm_dd(end
))) {
139 if (!begin
.empty()) {
153 if (!decode_xxy(begin
, b_d
, b_m
, b_y
) || !decode_xxy(end
, e_d
, e_m
, e_y
))
154 return Xapian::BAD_VALUENO
;
156 // Check that the month and day are within range. Also assume "start" <=
157 // "end" to help decide ambiguous cases.
158 if (!prefer_mdy
&& vet_dm(b_d
, b_m
) && vet_dm(e_d
, e_m
) &&
159 (b_y
!= e_y
|| b_m
< e_m
|| (b_m
== e_m
&& b_d
<= e_d
))) {
161 } else if (vet_dm(b_m
, b_d
) && vet_dm(e_m
, e_d
) &&
162 (b_y
!= e_y
|| b_d
< e_d
|| (b_d
== e_d
&& b_m
<= e_m
))) {
165 } else if (prefer_mdy
&& vet_dm(b_d
, b_m
) && vet_dm(e_d
, e_m
) &&
166 (b_y
!= e_y
|| b_m
< e_m
|| (b_m
== e_m
&& b_d
<= e_d
))) {
169 return Xapian::BAD_VALUENO
;
174 if (b_y
< epoch_year
) b_y
+= 100;
178 if (e_y
< epoch_year
) e_y
+= 100;
183 if (!begin
.empty()) {
184 SNPRINTF(buf
, sizeof(buf
), "%08d", b_y
* 10000 + b_m
* 100 + b_d
);
185 begin
.assign(buf
, 8);
188 SNPRINTF(buf
, sizeof(buf
), "%08d", e_y
* 10000 + e_m
* 100 + e_d
);
193 buf
[sizeof(buf
) - 1] = '\0';
194 if (!begin
.empty()) {
195 sprintf(buf
, "%08d", b_y
* 10000 + b_m
* 100 + b_d
);
196 if (buf
[sizeof(buf
) - 1]) abort(); // Buffer overrun!
197 begin
.assign(buf
, 8);
200 sprintf(buf
, "%08d", e_y
* 10000 + e_m
* 100 + e_d
);
201 if (buf
[sizeof(buf
) - 1]) abort(); // Buffer overrun!
209 NumberValueRangeProcessor::operator()(string
&begin
, string
&end
)
211 if (StringValueRangeProcessor::operator()(begin
, end
) == BAD_VALUENO
)
214 // Parse the numbers to floating point.
217 if (!begin
.empty()) {
219 const char * startptr
= begin
.c_str();
221 beginnum
= strtod(startptr
, &endptr
);
222 if (endptr
!= startptr
+ begin
.size())
223 // Invalid characters in string
224 return Xapian::BAD_VALUENO
;
226 // Overflow or underflow
227 return Xapian::BAD_VALUENO
;
229 // Silence GCC warning.
235 const char * startptr
= end
.c_str();
237 double endnum
= strtod(startptr
, &endptr
);
238 if (endptr
!= startptr
+ end
.size())
239 // Invalid characters in string
240 return Xapian::BAD_VALUENO
;
242 // Overflow or underflow
243 return Xapian::BAD_VALUENO
;
244 end
.assign(Xapian::sortable_serialise(endnum
));
247 if (!begin
.empty()) {
248 begin
.assign(Xapian::sortable_serialise(beginnum
));