Allow odd/even modifiers in numeric range (fixes #364)

This commit is contained in:
Jay Berkenbilt 2019-11-09 11:54:16 -05:00
parent c9cc83621b
commit c4478e5249
4 changed files with 81 additions and 5 deletions

View File

@ -1116,6 +1116,8 @@ QUtil::parse_numrange(char const* range, int max)
std::vector<int> work;
static int const comma = -1;
static int const dash = -2;
size_t start_idx = 0;
size_t skip = 1;
enum { st_top,
st_in_number,
@ -1182,6 +1184,14 @@ QUtil::parse_numrange(char const* range, int max)
work.push_back(dash);
}
}
else if (ch == ':')
{
if (! ((state == st_in_number) || (state == st_after_number)))
{
throw std::runtime_error("unexpected colon");
}
break;
}
else
{
throw std::runtime_error("unexpected character");
@ -1197,6 +1207,22 @@ QUtil::parse_numrange(char const* range, int max)
{
throw std::runtime_error("number expected");
}
if (*p == ':')
{
if (strcmp(p, ":odd") == 0)
{
skip = 2;
}
else if (strcmp(p, ":even") == 0)
{
skip = 2;
start_idx = 1;
}
else
{
throw std::runtime_error("unexpected even/odd modifier");
}
}
p = 0;
for (size_t i = 0; i < work.size(); i += 2)
@ -1245,6 +1271,15 @@ QUtil::parse_numrange(char const* range, int max)
}
}
}
if ((start_idx > 0) || (skip != 1))
{
auto t = result;
result.clear();
for (size_t i = start_idx; i < t.size(); i += skip)
{
result.push_back(t.at(i));
}
}
}
catch (std::runtime_error const& e)
{

View File

@ -49,6 +49,24 @@ my @nrange_tests = (
"numeric range r1-r15" .
" -> 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1",
0],
["1-10:quack",
"error at * in numeric range 1-10*:quack: unexpected even/odd modifier",
2],
["1-10:",
"error at * in numeric range 1-10*:: unexpected even/odd modifier",
2],
["1-10,r:",
"error at * in numeric range 1-10,r*:: unexpected even/odd modifier",
2],
["1-10,:",
"error at * in numeric range 1-10,*:: unexpected colon",
2],
["1-6,8-12:odd",
"numeric range 1-6,8-12:odd -> 1 3 5 8 10 12",
0],
["1-6,8-12:even",
"numeric range 1-6,8-12:even -> 2 4 6 9 11",
0],
);
foreach my $d (@nrange_tests)
{

View File

@ -1391,9 +1391,12 @@ make
<literal>r3-r1</literal> would be the last three pages of the
document. Pages can appear in any order. Ranges can appear with a
high number followed by a low number, which causes the pages to
appear in reverse. Repeating a number will cause an error, but you
can use the workaround discussed above should you really want to
include the same page twice.
appear in reverse. Numbers may be repeated in a page range. A page
range may be optionally appended with <literal>:even</literal> or
<literal>:odd</literal> to indicate only the even or odd pages in
the given range. Note that even and odd refer to the positions
within the specified, range, not whether the original number is
even or odd.
</para>
<para>
Example page ranges:
@ -1420,6 +1423,18 @@ make
in reverse order
</para>
</listitem>
<listitem>
<para>
<literal>1-20:even</literal>: even pages from 2 to 20
</para>
</listitem>
<listitem>
<para>
<literal>5,7-9,12:odd</literal>: pages 5, 8, and, 12, which are
the pages in odd positions from among the original range, which
represents pages 5, 7, 8, 9, and 12.
</para>
</listitem>
</itemizedlist>
</para>
<para>
@ -4663,6 +4678,13 @@ print "\n";
<xref linkend="ref.crypto"/>.
</para>
</listitem>
<listitem>
<para>
Allow <literal>:even</literal> or <literal>:odd</literal> to
be appended to numeric ranges for specification of the even
or odd pages from among the pages specified in the range.
</para>
</listitem>
</itemizedlist>
</listitem>
</itemizedlist>

View File

@ -1286,8 +1286,9 @@ ArgParser::argHelp()
<< "to count from the end, so \"r3-r1\" would be the last three pages of the\n"
<< "document. Pages can appear in any order. Ranges can appear with a\n"
<< "high number followed by a low number, which causes the pages to appear in\n"
<< "reverse. Repeating a number will cause an error, but the manual discusses\n"
<< "a workaround should you really want to include the same page twice.\n"
<< "reverse. Numbers may be repeated. A page range may be appended with :odd\n"
<< "to indicate odd pages in the selected range or :even to indicate even\n"
<< "pages.\n"
<< "\n"
<< "If the page range is omitted, the range of 1-z is assumed. qpdf decides\n"
<< "that the page range is omitted if the range argument is either -- or a\n"