api/valuerangeproc.cc

Go to the documentation of this file.
00001 
00004 /* Copyright (C) 2007 Olly Betts
00005  *
00006  * This program is free software; you can redistribute it and/or modify
00007  * it under the terms of the GNU General Public License as published by
00008  * the Free Software Foundation; either version 2 of the License, or
00009  * (at your option) any later version.
00010  *
00011  * This program is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  * GNU General Public License for more details.
00015  *
00016  * You should have received a copy of the GNU General Public License
00017  * along with this program; if not, write to the Free Software
00018  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
00019  */
00020 
00021 #include <config.h>
00022 
00023 #include <xapian/queryparser.h>
00024 
00025 #include <stdio.h>
00026 #include <stdlib.h>
00027 #include "safeerrno.h"
00028 
00029 #include <string>
00030 #include "stringutils.h"
00031 
00032 using namespace std;
00033 
00034 static bool
00035 decode_xxy(const string & s, int & x1, int &x2, int &y)
00036 {
00037     if (s.size() < 5 || s.size() > 10) return false;
00038     size_t i = s.find_first_not_of("0123456789");
00039     if (i < 1 || i > 2 || !(s[i] == '/' || s[i] == '-' || s[i] == '.'))
00040         return false;
00041     size_t j = s.find_first_not_of("0123456789", i + 1);
00042     if (j - (i + 1) < 1 || j - (i + 1) > 2 ||
00043         !(s[j] == '/' || s[j] == '-' || s[j] == '.'))
00044         return false;
00045     if (s.size() - j > 4 + 1) return false;
00046     if (s.find_first_not_of("0123456789", j + 1) != string::npos)
00047         return false;
00048     x1 = atoi(s.c_str());
00049     if (x1 < 1 || x1 > 31) return false;
00050     x2 = atoi(s.c_str() + i + 1);
00051     if (x2 < 1 || x2 > 31) return false;
00052     y = atoi(s.c_str() + j + 1);
00053     return true;
00054 }
00055 
00056 // We just use this to decide if an ambiguous aa/bb/cc date could be a
00057 // particular format, so there's no need to be anal about the exact number of
00058 // days in February.  The most useful check is that the month field is <= 12
00059 // so we could just check the day is <= 31 really.
00060 static const char max_month_length[12] = {
00061     31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
00062 };
00063 
00064 static bool
00065 vet_dm(int d, int m)
00066 {
00067     if (m > 12 || m < 1) return false;
00068     if (d < 1 || d > max_month_length[m - 1]) return false;
00069     return true;
00070 }
00071 
00072 Xapian::valueno
00073 Xapian::DateValueRangeProcessor::operator()(string &begin, string &end)
00074 {
00075     if (begin.size() == 8 && end.size() == 8 &&
00076         begin.find_first_not_of("0123456789") == string::npos &&
00077         end.find_first_not_of("0123456789") == string::npos) {
00078         // YYYYMMDD
00079         return valno;
00080     }
00081     if (begin.size() == 10 && end.size() == 10 &&
00082         begin.find_first_not_of("0123456789") == 4 &&
00083         end.find_first_not_of("0123456789") == 4 &&
00084         begin.find_first_not_of("0123456789", 5) == 7 &&
00085         end.find_first_not_of("0123456789", 5) == 7 &&
00086         begin.find_first_not_of("0123456789", 8) == string::npos &&
00087         end.find_first_not_of("0123456789", 8) == string::npos &&
00088         begin[4] == begin[7] && end[4] == end[7] && begin[4] == end[4] &&
00089         (end[4] == '-' || end[4] == '.' || end[4] == '/')) {
00090         // YYYY-MM-DD
00091         begin.erase(7, 1);
00092         begin.erase(4, 1);
00093         end.erase(7, 1);
00094         end.erase(4, 1);
00095         return valno;
00096     }
00097 
00098     int b_d, b_m, b_y;
00099     int e_d, e_m, e_y;
00100     if (!decode_xxy(begin, b_d, b_m, b_y) || !decode_xxy(end, e_d, e_m, e_y))
00101         return Xapian::BAD_VALUENO;
00102 
00103     // Check that the month and day are within range.  Also assume "start" <=
00104     // "end" to help decide ambiguous cases.
00105     if (!prefer_mdy && vet_dm(b_d, b_m) && vet_dm(e_d, e_m) &&
00106         (b_y != e_y || b_m < e_m || (b_m == e_m && b_d <= e_d))) {
00107         // OK.
00108     } else if (vet_dm(b_m, b_d) && vet_dm(e_m, e_d) &&
00109         (b_y != e_y || b_d < e_d || (b_d == e_d && b_m <= e_m))) {
00110         swap(b_m, b_d);
00111         swap(e_m, e_d);
00112     } else if (prefer_mdy && vet_dm(b_d, b_m) && vet_dm(e_d, e_m) &&
00113                (b_y != e_y || b_m < e_m || (b_m == e_m && b_d <= e_d))) {
00114         // OK.
00115     } else {
00116         return Xapian::BAD_VALUENO;
00117     }
00118 
00119     if (b_y < 100) {
00120         b_y += 1900;
00121         if (b_y < epoch_year) b_y += 100;
00122     }
00123     if (e_y < 100) {
00124         e_y += 1900;
00125         if (e_y < epoch_year) e_y += 100;
00126     }
00127 
00128 #ifdef SNPRINTF
00129     char buf[9];
00130     SNPRINTF(buf, sizeof(buf), "%08d", b_y * 10000 + b_m * 100 + b_d);
00131     begin.assign(buf, 8);
00132     SNPRINTF(buf, sizeof(buf), "%08d", e_y * 10000 + e_m * 100 + e_d);
00133     end.assign(buf, 8);
00134 #else
00135     char buf[100];
00136     buf[sizeof(buf) - 1] = '\0';
00137     sprintf(buf, "%08d", b_y * 10000 + b_m * 100 + b_d);
00138     if (buf[sizeof(buf) - 1]) abort(); // Buffer overrun!
00139     begin.assign(buf, 8);
00140     sprintf(buf, "%08d", e_y * 10000 + e_m * 100 + e_d);
00141     if (buf[sizeof(buf) - 1]) abort(); // Buffer overrun!
00142     end.assign(buf, 8);
00143 #endif
00144     return valno;
00145 }
00146 
00147 Xapian::valueno
00148 Xapian::NumberValueRangeProcessor::operator()(string &begin, string &end)
00149 {
00150     size_t b_b = 0, e_b = 0;
00151     size_t b_e = string::npos, e_e = string::npos;
00152 
00153     if (str.size()) {
00154         if (prefix) {
00155             // If there's a prefix, require it on the start of the range.
00156             if (!startswith(begin, str)) {
00157                 // Prefix not given.
00158                 return Xapian::BAD_VALUENO;
00159             }
00160             b_b = str.size();
00161             // But it's optional on the end of the range, e.g. $10..50
00162             if (startswith(end, str)) {
00163                 e_b = str.size();
00164             }
00165         } else {
00166             // If there's a suffix, require it on the end of the range.
00167             if (!endswith(end, str)) {
00168                 // Suffix not given.
00169                 return Xapian::BAD_VALUENO;
00170             }
00171             e_e = end.size() - str.size();
00172             // But it's optional on the start of the range, e.g. 10..50kg
00173             if (endswith(begin, str)) {
00174                 b_e = begin.size() - str.size();
00175             }
00176         }
00177     }
00178 
00179     // Adjust begin string if necessary.
00180     if (b_e != string::npos)
00181         begin.resize(b_e);
00182 
00183     // Adjust end string if necessary.
00184     if (e_e != string::npos)
00185         end.resize(e_e);
00186 
00187     // Parse the numbers to floating point.
00188     double beginnum, endnum;
00189     const char * startptr;
00190     char * endptr;
00191 
00192     errno = 0;
00193     startptr = begin.c_str() + b_b;
00194     beginnum = strtod(startptr, &endptr);
00195     if (endptr != startptr - b_b + begin.size())
00196         // Invalid characters in string
00197         return Xapian::BAD_VALUENO;
00198     if (errno)
00199         // Overflow or underflow
00200         return Xapian::BAD_VALUENO;
00201 
00202     errno = 0;
00203     startptr = end.c_str() + e_b;
00204     endnum = strtod(startptr, &endptr);
00205     if (endptr != startptr - e_b + end.size())
00206         // Invalid characters in string
00207         return Xapian::BAD_VALUENO;
00208     if (errno)
00209         // Overflow or underflow
00210         return Xapian::BAD_VALUENO;
00211 
00212     begin.assign(Xapian::sortable_serialise(beginnum));
00213     end.assign(Xapian::sortable_serialise(endnum));
00214 
00215     return valno;
00216 }

Documentation for Xapian (version 1.0.10).
Generated on 24 Dec 2008 by Doxygen 1.5.2.