mirror of
https://github.com/qpdf/qpdf.git
synced 2024-12-22 02:49:00 +00:00
setOutputStreams
git-svn-id: svn+q:///qpdf/trunk@1035 71b93d88-0707-0410-a8cf-f5a4172ac649
This commit is contained in:
parent
9f444ffef3
commit
a72ce95c92
@ -1,5 +1,9 @@
|
||||
2010-10-01 Jay Berkenbilt <ejb@ql.org>
|
||||
|
||||
* include/qpdf/QPDF.hh: Add setOutputStreams method to allow
|
||||
redirection of library-generated output/error to alternative
|
||||
streams.
|
||||
|
||||
* include/qpdf/QPDF.hh: Add processMemoryFile method for
|
||||
processing a PDF file from a memory buffer instead of a file.
|
||||
|
||||
|
10
TODO
10
TODO
@ -1,5 +1,7 @@
|
||||
General
|
||||
=======
|
||||
2.2.1
|
||||
=====
|
||||
|
||||
* Remove cerr and cout from QPDF*.cc
|
||||
|
||||
* QPDF::checkLinearization writes things to std::cout, which makes it
|
||||
hard for GUIs that want to display the result. Go through all
|
||||
@ -12,6 +14,10 @@ General
|
||||
rethrow if needed. The old version should call the ostream version
|
||||
with std::cout.
|
||||
|
||||
|
||||
General
|
||||
=======
|
||||
|
||||
* In general, take a fresh look at private methods to see which, if
|
||||
any, should be protected.
|
||||
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <list>
|
||||
#include <iostream>
|
||||
|
||||
#include <qpdf/DLL.h>
|
||||
|
||||
@ -61,6 +62,21 @@ class QPDF
|
||||
|
||||
// Parameter settings
|
||||
|
||||
// By default, warning messages are issued to std::cerr and output
|
||||
// messages printed by certain check calls are issued to
|
||||
// std::cout. This method allows you to specify alternative
|
||||
// streams for this purpose. Note that no normal QPDF operations
|
||||
// generate output to std::cout, so for applications that just
|
||||
// wish to avoid creating output and don't call any check
|
||||
// functions, calling setSuppressWarnings(true) is sufficient.
|
||||
// Applications that wish to present check or warning information
|
||||
// to users may replace the output and error streams to capture
|
||||
// the output and errors for other use. A null value for either
|
||||
// stream will cause QPDF to use std::cout or std::cerr as
|
||||
// appropriate.
|
||||
QPDF_DLL
|
||||
void setOutputStreams(std::ostream* out_stream, std::ostream* err_stream);
|
||||
|
||||
// If true, ignore any cross-reference streams in a hybrid file
|
||||
// (one that contains both cross-reference streams and
|
||||
// cross-reference tables). This can be useful for testing to
|
||||
@ -68,7 +84,8 @@ class QPDF
|
||||
QPDF_DLL
|
||||
void setIgnoreXRefStreams(bool);
|
||||
|
||||
// By default, any warnings are issued to stderr as they are
|
||||
// By default, any warnings are issued to std::cerr or the error
|
||||
// stream specified in a call to setOutputStreams as they are
|
||||
// encountered. If this is called with a true value, reporting of
|
||||
// warnings is suppressed. You may still retrieve warnings by
|
||||
// calling getWarnings.
|
||||
@ -88,7 +105,7 @@ class QPDF
|
||||
// clear the list. This method may be called even if processFile
|
||||
// throws an exception. Note that if setSuppressWarnings was not
|
||||
// called or was called with a false value, any warnings retrieved
|
||||
// here will have already been issued to stderr.
|
||||
// here will have already been output.
|
||||
QPDF_DLL
|
||||
std::vector<QPDFExc> getWarnings();
|
||||
|
||||
@ -204,12 +221,14 @@ class QPDF
|
||||
|
||||
// Performs various sanity checks on a linearized file. Return
|
||||
// true if no errors or warnings. Otherwise, return false and
|
||||
// output errors and warnings to stdout.
|
||||
// output errors and warnings to std::cout or the output stream
|
||||
// specified in a call to setOutputStreams.
|
||||
QPDF_DLL
|
||||
bool checkLinearization();
|
||||
|
||||
// Calls checkLinearization() and, if possible, prints normalized
|
||||
// contents of some of the hints tables to stdout. Normalization
|
||||
// contents of some of the hints tables to std::cout or the output
|
||||
// stream specified in a call to setOutputStreams. Normalization
|
||||
// includes adding min values to delta values and adjusting
|
||||
// offsets based on the location and size of the primary hint
|
||||
// stream.
|
||||
@ -803,6 +822,8 @@ class QPDF
|
||||
bool encryption_initialized;
|
||||
bool ignore_xref_streams;
|
||||
bool suppress_warnings;
|
||||
std::ostream* out_stream;
|
||||
std::ostream* err_stream;
|
||||
bool attempt_recovery;
|
||||
int encryption_V;
|
||||
bool encrypt_metadata;
|
||||
|
@ -267,6 +267,8 @@ QPDF::QPDF() :
|
||||
encryption_initialized(false),
|
||||
ignore_xref_streams(false),
|
||||
suppress_warnings(false),
|
||||
out_stream(&std::cout),
|
||||
err_stream(&std::cerr),
|
||||
attempt_recovery(true),
|
||||
encryption_V(0),
|
||||
encrypt_metadata(true),
|
||||
@ -332,6 +334,13 @@ QPDF::setIgnoreXRefStreams(bool val)
|
||||
this->ignore_xref_streams = val;
|
||||
}
|
||||
|
||||
void
|
||||
QPDF::setOutputStreams(std::ostream* out, std::ostream* err)
|
||||
{
|
||||
this->out_stream = out ? out : &std::cout;
|
||||
this->err_stream = err ? err : &std::cerr;
|
||||
}
|
||||
|
||||
void
|
||||
QPDF::setSuppressWarnings(bool val)
|
||||
{
|
||||
@ -449,7 +458,8 @@ QPDF::warn(QPDFExc const& e)
|
||||
this->warnings.push_back(e);
|
||||
if (! this->suppress_warnings)
|
||||
{
|
||||
std::cerr << "WARNING: " << this->warnings.back().what() << std::endl;
|
||||
*err_stream << "WARNING: "
|
||||
<< this->warnings.back().what() << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1045,16 +1055,16 @@ QPDF::showXRefTable()
|
||||
{
|
||||
ObjGen const& og = (*iter).first;
|
||||
QPDFXRefEntry const& entry = (*iter).second;
|
||||
std::cout << og.obj << "/" << og.gen << ": ";
|
||||
*out_stream << og.obj << "/" << og.gen << ": ";
|
||||
switch (entry.getType())
|
||||
{
|
||||
case 1:
|
||||
std::cout << "uncompressed; offset = " << entry.getOffset();
|
||||
*out_stream << "uncompressed; offset = " << entry.getOffset();
|
||||
break;
|
||||
|
||||
case 2:
|
||||
std::cout << "compressed; stream = " << entry.getObjStreamNumber()
|
||||
<< ", index = " << entry.getObjStreamIndex();
|
||||
*out_stream << "compressed; stream = " << entry.getObjStreamNumber()
|
||||
<< ", index = " << entry.getObjStreamIndex();
|
||||
break;
|
||||
|
||||
default:
|
||||
@ -1062,7 +1072,7 @@ QPDF::showXRefTable()
|
||||
" showing xref_table");
|
||||
break;
|
||||
}
|
||||
std::cout << std::endl;
|
||||
*out_stream << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -64,7 +64,7 @@ QPDF::checkLinearization()
|
||||
}
|
||||
catch (QPDFExc& e)
|
||||
{
|
||||
std::cout << e.what() << std::endl;
|
||||
*out_stream << e.what() << std::endl;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@ -351,9 +351,9 @@ QPDF::readHintStream(Pipeline& pl, off_t offset, size_t length)
|
||||
if ((computed_end < min_end_offset) ||
|
||||
(computed_end > max_end_offset))
|
||||
{
|
||||
std::cout << "expected = " << computed_end
|
||||
<< "; actual = " << min_end_offset << ".."
|
||||
<< max_end_offset << std::endl;
|
||||
*out_stream << "expected = " << computed_end
|
||||
<< "; actual = " << min_end_offset << ".."
|
||||
<< max_end_offset << std::endl;
|
||||
throw QPDFExc(qpdf_e_damaged_pdf, this->file->getName(),
|
||||
"linearization dictionary",
|
||||
this->file->getLastOffset(),
|
||||
@ -617,7 +617,7 @@ QPDF::checkLinearizationInternal()
|
||||
for (std::list<std::string>::iterator iter = errors.begin();
|
||||
iter != errors.end(); ++iter)
|
||||
{
|
||||
std::cout << "ERROR: " << (*iter) << std::endl;
|
||||
*out_stream << "ERROR: " << (*iter) << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
@ -627,7 +627,7 @@ QPDF::checkLinearizationInternal()
|
||||
for (std::list<std::string>::iterator iter = warnings.begin();
|
||||
iter != warnings.end(); ++iter)
|
||||
{
|
||||
std::cout << "WARNING: " << (*iter) << std::endl;
|
||||
*out_stream << "WARNING: " << (*iter) << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1008,17 +1008,17 @@ QPDF::showLinearizationData()
|
||||
}
|
||||
catch (QPDFExc& e)
|
||||
{
|
||||
std::cout << e.what() << std::endl;
|
||||
*out_stream << e.what() << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
QPDF::dumpLinearizationDataInternal()
|
||||
{
|
||||
std::cout << this->file->getName() << ": linearization data:" << std::endl
|
||||
<< std::endl;
|
||||
*out_stream << this->file->getName() << ": linearization data:" << std::endl
|
||||
<< std::endl;
|
||||
|
||||
std::cout
|
||||
*out_stream
|
||||
<< "file_size: " << this->linp.file_size << std::endl
|
||||
<< "first_page_object: " << this->linp.first_page_object << std::endl
|
||||
<< "first_page_end: " << this->linp.first_page_end << std::endl
|
||||
@ -1029,19 +1029,19 @@ QPDF::dumpLinearizationDataInternal()
|
||||
<< "H_length: " << this->linp.H_length << std::endl
|
||||
<< std::endl;
|
||||
|
||||
std::cout << "Page Offsets Hint Table" << std::endl
|
||||
<< std::endl;
|
||||
*out_stream << "Page Offsets Hint Table" << std::endl
|
||||
<< std::endl;
|
||||
dumpHPageOffset();
|
||||
std::cout << std::endl
|
||||
<< "Shared Objects Hint Table" << std::endl
|
||||
<< std::endl;
|
||||
*out_stream << std::endl
|
||||
<< "Shared Objects Hint Table" << std::endl
|
||||
<< std::endl;
|
||||
dumpHSharedObject();
|
||||
|
||||
if (this->outline_hints.nobjects > 0)
|
||||
{
|
||||
std::cout << std::endl
|
||||
<< "Outlines Hint Table" << std::endl
|
||||
<< std::endl;
|
||||
*out_stream << std::endl
|
||||
<< "Outlines Hint Table" << std::endl
|
||||
<< std::endl;
|
||||
dumpHGeneric(this->outline_hints);
|
||||
}
|
||||
}
|
||||
@ -1064,7 +1064,7 @@ void
|
||||
QPDF::dumpHPageOffset()
|
||||
{
|
||||
HPageOffset& t = this->page_offset_hints;
|
||||
std::cout
|
||||
*out_stream
|
||||
<< "min_nobjects: " << t.min_nobjects
|
||||
<< std::endl
|
||||
<< "first_page_offset: " << adjusted_offset(t.first_page_offset)
|
||||
@ -1095,7 +1095,7 @@ QPDF::dumpHPageOffset()
|
||||
for (int i1 = 0; i1 < this->linp.npages; ++i1)
|
||||
{
|
||||
HPageOffsetEntry& pe = t.entries[i1];
|
||||
std::cout
|
||||
*out_stream
|
||||
<< "Page " << i1 << ":" << std::endl
|
||||
<< " nobjects: " << pe.delta_nobjects + t.min_nobjects
|
||||
<< std::endl
|
||||
@ -1109,10 +1109,10 @@ QPDF::dumpHPageOffset()
|
||||
<< " nshared_objects: " << pe.nshared_objects << std::endl;
|
||||
for (int i2 = 0; i2 < pe.nshared_objects; ++i2)
|
||||
{
|
||||
std::cout << " identifier " << i2 << ": "
|
||||
<< pe.shared_identifiers[i2] << std::endl;
|
||||
std::cout << " numerator " << i2 << ": "
|
||||
<< pe.shared_numerators[i2] << std::endl;
|
||||
*out_stream << " identifier " << i2 << ": "
|
||||
<< pe.shared_identifiers[i2] << std::endl;
|
||||
*out_stream << " numerator " << i2 << ": "
|
||||
<< pe.shared_numerators[i2] << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1121,7 +1121,7 @@ void
|
||||
QPDF::dumpHSharedObject()
|
||||
{
|
||||
HSharedObject& t = this->shared_object_hints;
|
||||
std::cout
|
||||
*out_stream
|
||||
<< "first_shared_obj: " << t.first_shared_obj
|
||||
<< std::endl
|
||||
<< "first_shared_offset: " << adjusted_offset(t.first_shared_offset)
|
||||
@ -1140,19 +1140,19 @@ QPDF::dumpHSharedObject()
|
||||
for (int i = 0; i < t.nshared_total; ++i)
|
||||
{
|
||||
HSharedObjectEntry& se = t.entries[i];
|
||||
std::cout << "Shared Object " << i << ":" << std::endl;
|
||||
std::cout << " group length: "
|
||||
<< se.delta_group_length + t.min_group_length << std::endl;
|
||||
*out_stream << "Shared Object " << i << ":" << std::endl;
|
||||
*out_stream << " group length: "
|
||||
<< se.delta_group_length + t.min_group_length << std::endl;
|
||||
// PDF spec says signature present nobjects_minus_one are
|
||||
// always 0, so print them only if they have a non-zero value.
|
||||
if (se.signature_present)
|
||||
{
|
||||
std::cout << " signature present" << std::endl;
|
||||
*out_stream << " signature present" << std::endl;
|
||||
}
|
||||
if (se.nobjects_minus_one != 0)
|
||||
{
|
||||
std::cout << " nobjects: "
|
||||
<< se.nobjects_minus_one + 1 << std::endl;
|
||||
*out_stream << " nobjects: "
|
||||
<< se.nobjects_minus_one + 1 << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1160,7 +1160,7 @@ QPDF::dumpHSharedObject()
|
||||
void
|
||||
QPDF::dumpHGeneric(HGeneric& t)
|
||||
{
|
||||
std::cout
|
||||
*out_stream
|
||||
<< "first_object: " << t.first_object
|
||||
<< std::endl
|
||||
<< "first_object_offset: " << adjusted_offset(t.first_object_offset)
|
||||
|
@ -2074,6 +2074,61 @@ print "\n";
|
||||
<filename>ChangeLog</filename> in the source distribution.
|
||||
</para>
|
||||
<variablelist>
|
||||
<varlistentry>
|
||||
<term>2.2.1: XXX</term>
|
||||
<listitem>
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para>
|
||||
Add new method <classname>QPDF::processMemoryFile</classname>
|
||||
for operating on PDF files that are loaded into memory rather
|
||||
than in a file on disk.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
Add new method <classname>QPDF::setOutputStreams</classname>
|
||||
to replace <varname>std::cout</varname> and
|
||||
<varname>std::cerr</varname> with other streams for generation
|
||||
of diagnostic messages and error messages. This can be useful
|
||||
for GUIs or other applications that want to capture any output
|
||||
generated by the library to present to the user in some other
|
||||
way. Note that QPDF does not write to
|
||||
<varname>std::cout</varname> (or the specified output stream)
|
||||
except where explicitly mentioned in
|
||||
<filename>QPDF.hh</filename>, and that the only use of the
|
||||
error stream is for warnings. Note also that output of
|
||||
warnings is suppressed when
|
||||
<literal>setSuppressWarnings(true)</literal> is called.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
Give a warning but otherwise ignore empty PDF objects by
|
||||
treating them as null. Empty object are not permitted by the
|
||||
PDF specification but have been known to appear in some actual
|
||||
PDF files.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
Handle inline image filter abbreviations when the appear as
|
||||
stream filter abbreviations. The PDF specification does not
|
||||
allow use of stream filter abbreviations in this way, but
|
||||
Adobe Reader and some other PDF readers accept them since they
|
||||
sometimes appear incorrectly in actual PDF files.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
Implement miscellaneous enhancements to
|
||||
<classname>PointerHolder</classname> and
|
||||
<classname>Buffer</classname> to support other changes.
|
||||
</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>2.2.0: August 14, 2010</term>
|
||||
<listitem>
|
||||
|
@ -111,7 +111,7 @@ $td->runtest("new stream",
|
||||
show_ntests();
|
||||
# ----------
|
||||
$td->notify("--- Miscellaneous Tests ---");
|
||||
$n_tests += 26;
|
||||
$n_tests += 28;
|
||||
|
||||
$td->runtest("qpdf version",
|
||||
{$td->COMMAND => "qpdf --version"},
|
||||
@ -245,6 +245,18 @@ $td->runtest("empty object",
|
||||
$td->EXIT_STATUS => 3},
|
||||
$td->NORMALIZE_NEWLINES);
|
||||
|
||||
$td->runtest("error/output redirection to null",
|
||||
{$td->COMMAND => "test_driver 12 linearized-and-warnings.pdf"},
|
||||
{$td->FILE => "linearized-and-warnings-1.out",
|
||||
$td->EXIT_STATUS => 0},
|
||||
$td->NORMALIZE_NEWLINES);
|
||||
|
||||
$td->runtest("error/output redirection to strings",
|
||||
{$td->COMMAND => "test_driver 13 linearized-and-warnings.pdf"},
|
||||
{$td->FILE => "linearized-and-warnings-2.out",
|
||||
$td->EXIT_STATUS => 0},
|
||||
$td->NORMALIZE_NEWLINES);
|
||||
|
||||
show_ntests();
|
||||
# ----------
|
||||
$td->notify("--- Error Condition Tests ---");
|
||||
|
52
qpdf/qtest/qpdf/linearized-and-warnings-1.out
Normal file
52
qpdf/qtest/qpdf/linearized-and-warnings-1.out
Normal file
@ -0,0 +1,52 @@
|
||||
WARNING: linearized-and-warnings.pdf (object 2 0, file position 1117): empty object treated as null
|
||||
linearized-and-warnings.pdf: linearization data:
|
||||
|
||||
file_size: 1310
|
||||
first_page_object: 6
|
||||
first_page_end: 1044
|
||||
npages: 1
|
||||
xref_zero_offset: 1132
|
||||
first_page: 0
|
||||
H_offset: 528
|
||||
H_length: 118
|
||||
|
||||
Page Offsets Hint Table
|
||||
|
||||
min_nobjects: 4
|
||||
first_page_offset: 646
|
||||
nbits_delta_nobjects: 0
|
||||
min_page_length: 398
|
||||
nbits_delta_page_length: 0
|
||||
min_content_offset: 0
|
||||
nbits_delta_content_offset: 0
|
||||
min_content_length: 398
|
||||
nbits_delta_content_length: 0
|
||||
nbits_nshared_objects: 0
|
||||
nbits_shared_identifier: 3
|
||||
nbits_shared_numerator: 0
|
||||
shared_denominator: 4
|
||||
Page 0:
|
||||
nobjects: 4
|
||||
length: 398
|
||||
content_offset: 0
|
||||
content_length: 398
|
||||
nshared_objects: 0
|
||||
|
||||
Shared Objects Hint Table
|
||||
|
||||
first_shared_obj: 0
|
||||
first_shared_offset: 0
|
||||
nshared_first_page: 4
|
||||
nshared_total: 4
|
||||
nbits_nobjects: 0
|
||||
min_group_length: 30
|
||||
nbits_delta_group_length: 7
|
||||
Shared Object 0:
|
||||
group length: 143
|
||||
Shared Object 1:
|
||||
group length: 118
|
||||
Shared Object 2:
|
||||
group length: 30
|
||||
Shared Object 3:
|
||||
group length: 107
|
||||
test 12 done
|
54
qpdf/qtest/qpdf/linearized-and-warnings-2.out
Normal file
54
qpdf/qtest/qpdf/linearized-and-warnings-2.out
Normal file
@ -0,0 +1,54 @@
|
||||
---output---
|
||||
linearized-and-warnings.pdf: linearization data:
|
||||
|
||||
file_size: 1310
|
||||
first_page_object: 6
|
||||
first_page_end: 1044
|
||||
npages: 1
|
||||
xref_zero_offset: 1132
|
||||
first_page: 0
|
||||
H_offset: 528
|
||||
H_length: 118
|
||||
|
||||
Page Offsets Hint Table
|
||||
|
||||
min_nobjects: 4
|
||||
first_page_offset: 646
|
||||
nbits_delta_nobjects: 0
|
||||
min_page_length: 398
|
||||
nbits_delta_page_length: 0
|
||||
min_content_offset: 0
|
||||
nbits_delta_content_offset: 0
|
||||
min_content_length: 398
|
||||
nbits_delta_content_length: 0
|
||||
nbits_nshared_objects: 0
|
||||
nbits_shared_identifier: 3
|
||||
nbits_shared_numerator: 0
|
||||
shared_denominator: 4
|
||||
Page 0:
|
||||
nobjects: 4
|
||||
length: 398
|
||||
content_offset: 0
|
||||
content_length: 398
|
||||
nshared_objects: 0
|
||||
|
||||
Shared Objects Hint Table
|
||||
|
||||
first_shared_obj: 0
|
||||
first_shared_offset: 0
|
||||
nshared_first_page: 4
|
||||
nshared_total: 4
|
||||
nbits_nobjects: 0
|
||||
min_group_length: 30
|
||||
nbits_delta_group_length: 7
|
||||
Shared Object 0:
|
||||
group length: 143
|
||||
Shared Object 1:
|
||||
group length: 118
|
||||
Shared Object 2:
|
||||
group length: 30
|
||||
Shared Object 3:
|
||||
group length: 107
|
||||
---error---
|
||||
WARNING: linearized-and-warnings.pdf (object 2 0, file position 1117): empty object treated as null
|
||||
test 13 done
|
BIN
qpdf/qtest/qpdf/linearized-and-warnings.pdf
Normal file
BIN
qpdf/qtest/qpdf/linearized-and-warnings.pdf
Normal file
Binary file not shown.
@ -10,6 +10,7 @@
|
||||
#include <qpdf/Pl_Flate.hh>
|
||||
#include <qpdf/QPDFWriter.hh>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
@ -502,6 +503,22 @@ void runtest(int n, char const* filename)
|
||||
std::cout << "raw stream data okay" << std::endl;
|
||||
}
|
||||
}
|
||||
else if (n == 12)
|
||||
{
|
||||
pdf.setOutputStreams(0, 0);
|
||||
pdf.showLinearizationData();
|
||||
}
|
||||
else if (n == 13)
|
||||
{
|
||||
std::ostringstream out;
|
||||
std::ostringstream err;
|
||||
pdf.setOutputStreams(&out, &err);
|
||||
pdf.showLinearizationData();
|
||||
std::cout << "---output---" << std::endl
|
||||
<< out.str()
|
||||
<< "---error---" << std::endl
|
||||
<< err.str();
|
||||
}
|
||||
else
|
||||
{
|
||||
throw std::runtime_error(std::string("invalid test ") +
|
||||
|
Loading…
Reference in New Issue
Block a user