diff --git a/src/papersearch.php b/src/papersearch.php index 299102b20e..20bbee0288 100644 --- a/src/papersearch.php +++ b/src/papersearch.php @@ -727,7 +727,7 @@ private function _kwdef_parse($kwdef, $sword, $kwexplicit) { static private function _search_word_is_paperid($str) { $ch = substr($str, 0, 1); return ($ch === "#" || ctype_digit($ch)) - && preg_match('/\A(?:#?\d+(?:(?:-|–|—)#?\d+)?(?:\s*,\s*|\z))+\z/s', $str); + && preg_match('/\A(?:#?\d++(?:(?:-|–|—)(?:|#?\d++))?(?:\s*,\s*|\z))+\z/s', $str); } /** @param string $str @@ -763,7 +763,7 @@ private function _search_word($kw, $sword, $scope) { } } - // Paper ID search term (`1-2`, `#1-#2`, etc.) + // Paper ID search term (`1-2`, `#1-#2`, `1-`, etc.) if (!$sword->quoted && !$scope->defkw && self::_search_word_is_paperid($sword->word)) { diff --git a/src/searchterm.php b/src/searchterm.php index a51b6fb040..51a1ba2522 100644 --- a/src/searchterm.php +++ b/src/searchterm.php @@ -1467,7 +1467,11 @@ private function add_drange($p0, $p1, $rev, $explicit) { $n = $this->n + ($rev ? $p1x - $p0 - 1 : 0); array_splice($this->r, $i, 0, [[$p0, $p1x, $n, $rev, $explicit]]); } - $this->n += $p1x - $p0; + // ensure `$this->n <= PHP_INT_MAX` + // (it naturally will be, UNLESS someone calls add_range + // with negative PIDs) + $delta = min($p1x - $p0, PHP_INT_MAX - $this->n); + $this->n += $delta; } $p0 = max($p0, $p1x); } @@ -1524,7 +1528,7 @@ function sql_predicate($field) { return "false"; } else if ($this->n <= 8 * count($this->r) && ($pids = $this->paper_ids()) !== null) { - return "$field in (" . join(",", $pids) . ")"; + return "{$field} in (" . join(",", $pids) . ")"; } else { $s = []; foreach ($this->r as $r) { @@ -1568,10 +1572,18 @@ static function parse_pidcode($word, SearchWord $sword, PaperSearch $srch) { * @return PaperID_SearchTerm */ static function parse_normal($word) { $st = new PaperID_SearchTerm; - while (preg_match('/\A#?(\d+)(?:(?:-|–|—)#?(\d+))?\s*,?\s*(.*)\z/s', $word, $m)) { - $m[2] = (isset($m[2]) && $m[2] ? $m[2] : $m[1]); - $st->add_range(intval($m[1]), intval($m[2])); - $word = $m[3]; + $pos = 0; + while (preg_match('/\G#?(\d++)((?:-|–|—)#?(\d++)|(?:-|–|—)|)\s*,?\s*/s', $word, $m, 0, $pos)) { + $p1 = intval($m[1]); + if ($m[2] === "") { + $p2 = $p1; + } else if (!isset($m[3]) || $m[3] === "") { + $p2 = PHP_INT_MAX; + } else { + $p2 = intval($m[3]); + } + $st->add_range($p1, $p2); + $pos += strlen($m[0]); } return $st; } diff --git a/test/t_search.php b/test/t_search.php index 74e14b9fc8..47a8e498a3 100644 --- a/test/t_search.php +++ b/test/t_search.php @@ -75,6 +75,12 @@ function test_xor() { xassert_search($this->conf->root_user(), "1-10 XOR 4-5", "1 2 3 6 7 8 9 10"); } + function test_halfopen_interval() { + xassert_search($this->conf->root_user(), "5-100000 XOR 10-100000", "5 6 7 8 9"); + xassert_search($this->conf->root_user(), "5- XOR 10-100000", "5 6 7 8 9"); + xassert_search($this->conf->root_user(), "8-,7-,6-,5- XOR 10-100000", "5 6 7 8 9"); + } + function test_review_term_to_round_mask() { $rl = $this->conf->round_list(); xassert_eqq($rl[0], "");