diff --git a/include/qpdf/Pl_Function.hh b/include/qpdf/Pl_Function.hh index dd700e80..41c56a98 100644 --- a/include/qpdf/Pl_Function.hh +++ b/include/qpdf/Pl_Function.hh @@ -31,6 +31,9 @@ // // It is okay to not call finish() on this pipeline if it has no // "next". +// +// It is okay to keep calling write() after a previous write throws an +// exception as long as the delegated function allows it. #include @@ -41,8 +44,26 @@ class QPDF_DLL_CLASS Pl_Function: public Pipeline public: typedef std::function writer_t; + // The supplied function is called every time write is called. QPDF_DLL Pl_Function(char const* identifier, Pipeline* next, writer_t fn); + + // The supplied C-style function is called every time write is + // called. The udata option is passed into the function with each + // call. If the function returns a non-zero value, a runtime error + // is thrown. + typedef int (*writer_c_t)(unsigned char const*, size_t, void*); + QPDF_DLL + Pl_Function( + char const* identifier, Pipeline* next, writer_c_t fn, void* udata); + typedef int (*writer_c_char_t)(char const*, size_t, void*); + QPDF_DLL + Pl_Function( + char const* identifier, + Pipeline* next, + writer_c_char_t fn, + void* udata); + QPDF_DLL virtual ~Pl_Function(); diff --git a/libqpdf/Pl_Function.cc b/libqpdf/Pl_Function.cc index 32fcccf1..5f2dc042 100644 --- a/libqpdf/Pl_Function.cc +++ b/libqpdf/Pl_Function.cc @@ -15,6 +15,36 @@ Pl_Function::Pl_Function(char const* identifier, Pipeline* next, writer_t fn) : { } +Pl_Function::Pl_Function( + char const* identifier, Pipeline* next, writer_c_t fn, void* udata) : + Pipeline(identifier, next), + m(new Members(nullptr)) +{ + m->fn = [identifier, fn, udata](unsigned char const* data, size_t len) { + int code = fn(data, len, udata); + if (code != 0) { + throw std::runtime_error( + std::string(identifier) + " function returned code " + + QUtil::int_to_string(code)); + } + }; +} + +Pl_Function::Pl_Function( + char const* identifier, Pipeline* next, writer_c_char_t fn, void* udata) : + Pipeline(identifier, next), + m(new Members(nullptr)) +{ + m->fn = [identifier, fn, udata](unsigned char const* data, size_t len) { + int code = fn(reinterpret_cast(data), len, udata); + if (code != 0) { + throw std::runtime_error( + std::string(identifier) + " function returned code " + + QUtil::int_to_string(code)); + } + }; +} + Pl_Function::~Pl_Function() { // Must be explicit and not inline -- see QPDF_DLL_CLASS in diff --git a/libtests/pl_function.cc b/libtests/pl_function.cc index 101ec02e..2829671b 100644 --- a/libtests/pl_function.cc +++ b/libtests/pl_function.cc @@ -5,6 +5,38 @@ #include #include +namespace +{ + struct Count + { + int count{0}; + }; +} // namespace + +int +f(unsigned char const* data, size_t len, void* udata) +{ + auto c = reinterpret_cast(udata); + ++c->count; + std::cout << "got " << data << "(" << len << ")" << std::endl; + if (c->count == 3) { + return 1; + } + return 0; +} + +int +g(char const* data, size_t len, void* udata) +{ + auto c = reinterpret_cast(udata); + ++c->count; + std::cout << "signed got " << data << "(" << len << ")" << std::endl; + if (c->count == 2) { + return 2; + } + return 0; +} + int main(int argc, char* argv[]) { @@ -23,5 +55,32 @@ main(int argc, char* argv[]) p2.finish(); assert(s == "c2FsYWQ="); + Count c; + Pl_Function p3("c-function", nullptr, f, &c); + p3 << "one"; + p3 << "two"; + try { + p3 << "three"; + assert(false); + } catch (std::runtime_error& e) { + std::cout << "three threw " << e.what() << std::endl; + } + p3 << "four"; + p3.finish(); + assert(c.count == 4); + + c.count = 0; + Pl_Function p4("c-function", nullptr, g, &c); + p4 << "potato"; + try { + p4 << "salad"; + assert(false); + } catch (std::runtime_error& e) { + std::cout << "salad threw " << e.what() << std::endl; + } + p4 << "quack"; + p4.finish(); + assert(c.count == 3); + return 0; } diff --git a/libtests/qtest/pl_function/exp b/libtests/qtest/pl_function/exp index fd2ea1bc..930240d9 100644 --- a/libtests/qtest/pl_function/exp +++ b/libtests/qtest/pl_function/exp @@ -1,2 +1,11 @@ p1: 6: potato p2: 5: salad +got one(3) +got two(3) +got three(5) +three threw c-function function returned code 1 +got four(4) +signed got potato(6) +signed got salad(5) +salad threw c-function function returned code 2 +signed got quack(5)