vcl/source/filter/png/PngImageReader.cxx |  371 ++++++++++++++++---------------
 1 file changed, 193 insertions(+), 178 deletions(-)

New commits:
commit 6f92f2cb118cc97e57c57e02bef491ecf39b1f4a
Author:     Caolán McNamara <caolan.mcnam...@collabora.com>
AuthorDate: Thu Jul 31 21:01:51 2025 +0100
Commit:     Caolán McNamara <caolan.mcnam...@collabora.com>
CommitDate: Fri Aug 1 20:19:27 2025 +0200

    ofz#435489660 Direct-leak from vcl::PngImageReader::read
    
    setjmp/longjmp skips std::vector dtor. This setjmp
    has always been a pain, lets try replacing it
    with a png_set_error_fn that does a c++ throw
    
    Direct leak of 1384 byte(s) in 1 object(s) allocated from:
        #0 0x567197b85dbd in operator new(unsigned long) 
/src/llvm-project/compiler-rt/lib/asan/asan_new_delete.cpp:86:3
        #1 0x567197e42f98 in __libcpp_operator_new<unsigned long> 
/usr/local/include/c++/v1/new:271:10
        #2 0x567197e42f98 in __libcpp_allocate 
/usr/local/include/c++/v1/new:295:10
        #3 0x567197e42f98 in allocate 
/usr/local/include/c++/v1/__memory/allocator.h:125:32
        #4 0x567197e42f98 in __allocate_at_least<std::__1::allocator<unsigned 
char> > /usr/local/include/c++/v1/__memory/allocate_at_least.h:55:19
        #5 0x567197e42f98 in __vallocate /usr/local/include/c++/v1/vector:741:25
        #6 0x567197e42f98 in std::__1::vector<unsigned char, 
std::__1::allocator<unsigned char>>::vector(unsigned long, unsigned char 
const&) /usr/local/include/c++/v1/vector:1144:5
        #7 0x567197ef9095 in (anonymous namespace)::reader(SvStream&, 
ImportOutput&, GraphicFilterImportFlags, BitmapScopedWriteAccess*, 
BitmapScopedWriteAccess*) 
/src/libreoffice/vcl/source/filter/png/PngImageReader.cxx:555:35
        #8 0x567197efbb9e in read 
/src/libreoffice/vcl/source/filter/png/PngImageReader.cxx:809:65
        #9 0x567197efbb9e in vcl::PngImageReader::read() 
/src/libreoffice/vcl/source/filter/png/PngImageReader.cxx:814:9
        #10 0x567197b89b34 in LLVMFuzzerTestOneInput 
/src/libreoffice/vcl/workben/pngfuzzer.cxx:52:19
        #11 0x567197a7a1a0 in fuzzer::Fuzzer::ExecuteCallback(unsigned char 
const*, unsigned long) 
/src/llvm-project/compiler-rt/lib/fuzzer/FuzzerLoop.cpp:614:13
        #12 0x567197a65535 in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, 
unsigned long) /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerDriver.cpp:327:6
        #13 0x567197a6afcf in fuzzer::FuzzerDriver(int*, char***, int 
(*)(unsigned char const*, unsigned long)) 
/src/llvm-project/compiler-rt/lib/fuzzer/FuzzerDriver.cpp:862:9
        #14 0x567197a95532 in main 
/src/llvm-project/compiler-rt/lib/fuzzer/FuzzerMain.cpp:20:10
        #15 0x7dffda1c4082 in __libc_start_main 
/build/glibc-LcI20x/glibc-2.31/csu/libc-start.c:308:16
    
    Change-Id: Id112c8be512b836e86a054075e7d5de83f5507c2
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/188721
    Reviewed-by: Caolán McNamara <caolan.mcnam...@collabora.com>
    Tested-by: Jenkins

diff --git a/vcl/source/filter/png/PngImageReader.cxx 
b/vcl/source/filter/png/PngImageReader.cxx
index 977787e90e95..515b150ba054 100644
--- a/vcl/source/filter/png/PngImageReader.cxx
+++ b/vcl/source/filter/png/PngImageReader.cxx
@@ -10,6 +10,7 @@
 
 #include <vcl/filter/PngImageReader.hxx>
 #include <png.h>
+#include <iostream>
 #include <rtl/crc.h>
 #include <tools/stream.hxx>
 #include <vcl/bitmap.hxx>
@@ -53,6 +54,11 @@ void lclReadStream(png_structp pPng, png_bytep pOutBytes, 
png_size_t nBytesToRea
     }
 }
 
+void lclError(png_structp /*pPng*/, png_const_charp error_msg)
+{
+    throw std::runtime_error(error_msg);
+}
+
 bool isPng(SvStream& rStream)
 {
     // Check signature bytes
@@ -324,10 +330,6 @@ bool fcTLbeforeIDAT(SvStream& rStream)
     return false;
 }
 
-#if defined __GNUC__ && __GNUC__ <= 14 && !defined __clang__
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wclobbered"
-#endif
 bool reader(SvStream& rStream, ImportOutput& rImportOutput,
             GraphicFilterImportFlags nImportFlags = 
GraphicFilterImportFlags::NONE,
             BitmapScopedWriteAccess* pAccess = nullptr,
@@ -369,219 +371,181 @@ bool reader(SvStream& rStream, ImportOutput& 
rImportOutput,
     const bool bUseExistingBitmap
         = static_cast<bool>(nImportFlags & 
GraphicFilterImportFlags::UseExistingBitmap);
 
-    if (setjmp(png_jmpbuf(pPng)))
-    {
-        pWriteAccessInstance.reset();
-        pWriteAccessAlphaInstance.reset();
-        return false;
-    }
+    int nNumberOfPasses(0);
+    int colorType = -1;
+    png_uint_32 width = 0;
+    png_uint_32 height = 0;
 
-    png_set_option(pPng, PNG_MAXIMUM_INFLATE_WINDOW, PNG_OPTION_ON);
+    try
+    {
+        png_set_option(pPng, PNG_MAXIMUM_INFLATE_WINDOW, PNG_OPTION_ON);
 
-    png_set_read_fn(pPng, &rStream, lclReadStream);
+        png_set_read_fn(pPng, &rStream, lclReadStream);
 
-    if (!bFuzzing)
-        png_set_crc_action(pPng, PNG_CRC_ERROR_QUIT, PNG_CRC_WARN_DISCARD);
-    else
-        png_set_crc_action(pPng, PNG_CRC_QUIET_USE, PNG_CRC_QUIET_USE);
+        png_set_error_fn(pPng, nullptr, lclError, nullptr);
 
-    png_set_sig_bytes(pPng, PNG_SIGNATURE_SIZE);
+        if (!bFuzzing)
+            png_set_crc_action(pPng, PNG_CRC_ERROR_QUIT, PNG_CRC_WARN_DISCARD);
+        else
+            png_set_crc_action(pPng, PNG_CRC_QUIET_USE, PNG_CRC_QUIET_USE);
 
-    png_read_info(pPng, pInfo);
+        png_set_sig_bytes(pPng, PNG_SIGNATURE_SIZE);
 
-    png_uint_32 width = 0;
-    png_uint_32 height = 0;
-    int bitDepth = 0;
-    int colorType = -1;
-    int interlace = -1;
+        png_read_info(pPng, pInfo);
 
-    png_uint_32 returnValue = png_get_IHDR(pPng, pInfo, &width, &height, 
&bitDepth, &colorType,
-                                           &interlace, nullptr, nullptr);
+        int bitDepth = 0;
+        int interlace = -1;
 
-    if (returnValue != 1)
-        return false;
+        png_uint_32 returnValue = png_get_IHDR(pPng, pInfo, &width, &height, 
&bitDepth, &colorType,
+                                               &interlace, nullptr, nullptr);
 
-    if (colorType == PNG_COLOR_TYPE_PALETTE)
-        png_set_palette_to_rgb(pPng);
+        if (returnValue != 1)
+            return false;
 
-    if (colorType == PNG_COLOR_TYPE_GRAY)
-        png_set_expand_gray_1_2_4_to_8(pPng);
+        if (colorType == PNG_COLOR_TYPE_PALETTE)
+            png_set_palette_to_rgb(pPng);
 
-    if (png_get_valid(pPng, pInfo, PNG_INFO_tRNS))
-        png_set_tRNS_to_alpha(pPng);
+        if (colorType == PNG_COLOR_TYPE_GRAY)
+            png_set_expand_gray_1_2_4_to_8(pPng);
 
-    if (bitDepth == 16)
-        png_set_scale_16(pPng);
+        if (png_get_valid(pPng, pInfo, PNG_INFO_tRNS))
+            png_set_tRNS_to_alpha(pPng);
 
-    if (bitDepth < 8)
-        png_set_packing(pPng);
+        if (bitDepth == 16)
+            png_set_scale_16(pPng);
 
-    // Convert gray+alpha to RGBA, keep gray as gray.
-    if (colorType == PNG_COLOR_TYPE_GRAY_ALPHA
-        || (colorType == PNG_COLOR_TYPE_GRAY && png_get_valid(pPng, pInfo, 
PNG_INFO_tRNS)))
-    {
-        png_set_gray_to_rgb(pPng);
-    }
+        if (bitDepth < 8)
+            png_set_packing(pPng);
 
-    // Sets the filler byte - if RGB it converts to RGBA
-    // png_set_filler(pPng, 0xFF, PNG_FILLER_AFTER);
+        // Convert gray+alpha to RGBA, keep gray as gray.
+        if (colorType == PNG_COLOR_TYPE_GRAY_ALPHA
+            || (colorType == PNG_COLOR_TYPE_GRAY && png_get_valid(pPng, pInfo, 
PNG_INFO_tRNS)))
+        {
+            png_set_gray_to_rgb(pPng);
+        }
 
-    int nNumberOfPasses = png_set_interlace_handling(pPng);
+        // Sets the filler byte - if RGB it converts to RGBA
+        // png_set_filler(pPng, 0xFF, PNG_FILLER_AFTER);
 
-    png_read_update_info(pPng, pInfo);
-    returnValue = png_get_IHDR(pPng, pInfo, &width, &height, &bitDepth, 
&colorType, nullptr,
-                               nullptr, nullptr);
+        nNumberOfPasses = png_set_interlace_handling(pPng);
 
-    if (returnValue != 1)
-        return false;
+        png_read_update_info(pPng, pInfo);
+        returnValue = png_get_IHDR(pPng, pInfo, &width, &height, &bitDepth, 
&colorType, nullptr,
+                                   nullptr, nullptr);
 
-    if (bitDepth != 8
-        || (colorType != PNG_COLOR_TYPE_RGB && colorType != 
PNG_COLOR_TYPE_RGB_ALPHA
-            && colorType != PNG_COLOR_TYPE_GRAY))
-    {
-        return false;
-    }
+        if (returnValue != 1)
+            return false;
 
-    png_uint_32 res_x = 0;
-    png_uint_32 res_y = 0;
-    int unit_type = 0;
-    if (png_get_pHYs(pPng, pInfo, &res_x, &res_y, &unit_type) != 0
-        && unit_type == PNG_RESOLUTION_METER && res_x && res_y)
-    {
-        // convert into MapUnit::Map100thMM
-        prefSize = Size(static_cast<sal_Int32>((100000.0 * width) / res_x),
-                        static_cast<sal_Int32>((100000.0 * height) / res_y));
-    }
+        if (bitDepth != 8
+            || (colorType != PNG_COLOR_TYPE_RGB && colorType != 
PNG_COLOR_TYPE_RGB_ALPHA
+                && colorType != PNG_COLOR_TYPE_GRAY))
+        {
+            return false;
+        }
 
-    if (!bUseExistingBitmap)
-    {
-        switch (colorType)
+        png_uint_32 res_x = 0;
+        png_uint_32 res_y = 0;
+        int unit_type = 0;
+        if (png_get_pHYs(pPng, pInfo, &res_x, &res_y, &unit_type) != 0
+            && unit_type == PNG_RESOLUTION_METER && res_x && res_y)
         {
-            case PNG_COLOR_TYPE_RGB:
-                aBitmap = Bitmap(Size(width, height), 
vcl::PixelFormat::N24_BPP);
-                break;
-            case PNG_COLOR_TYPE_RGBA:
-                aBitmap = Bitmap(Size(width, height), 
vcl::PixelFormat::N24_BPP);
-                aBitmapAlpha = AlphaMask(Size(width, height), nullptr);
-                aBitmapAlpha.Erase(0); // opaque
-                break;
-            case PNG_COLOR_TYPE_GRAY:
-                aBitmap = Bitmap(Size(width, height), vcl::PixelFormat::N8_BPP,
-                                 &Bitmap::GetGreyPalette(256));
-                break;
-            default:
-                abort();
+            // convert into MapUnit::Map100thMM
+            prefSize = Size(static_cast<sal_Int32>((100000.0 * width) / res_x),
+                            static_cast<sal_Int32>((100000.0 * height) / 
res_y));
         }
 
-        if (bOnlyCreateBitmap)
+        if (!bUseExistingBitmap)
         {
-            if (!aBitmapAlpha.IsEmpty())
-                aBitmapEx = BitmapEx(aBitmap, aBitmapAlpha);
-            else
-                aBitmapEx = BitmapEx(aBitmap);
-            if (!prefSize.IsEmpty())
+            switch (colorType)
             {
-                aBitmapEx.SetPrefMapMode(MapMode(MapUnit::Map100thMM));
-                aBitmapEx.SetPrefSize(prefSize);
+                case PNG_COLOR_TYPE_RGB:
+                    aBitmap = Bitmap(Size(width, height), 
vcl::PixelFormat::N24_BPP);
+                    break;
+                case PNG_COLOR_TYPE_RGBA:
+                    aBitmap = Bitmap(Size(width, height), 
vcl::PixelFormat::N24_BPP);
+                    aBitmapAlpha = AlphaMask(Size(width, height), nullptr);
+                    aBitmapAlpha.Erase(0); // opaque
+                    break;
+                case PNG_COLOR_TYPE_GRAY:
+                    aBitmap = Bitmap(Size(width, height), 
vcl::PixelFormat::N8_BPP,
+                                     &Bitmap::GetGreyPalette(256));
+                    break;
+                default:
+                    abort();
             }
-            rImportOutput.moBitmap = aBitmapEx;
-            return true;
-        }
 
-        pWriteAccessInstance = aBitmap;
-        if (!pWriteAccessInstance)
-            return false;
-        if (!aBitmapAlpha.IsEmpty())
-        {
-            pWriteAccessAlphaInstance = aBitmapAlpha;
-            if (!pWriteAccessAlphaInstance)
+            if (bOnlyCreateBitmap)
+            {
+                if (!aBitmapAlpha.IsEmpty())
+                    aBitmapEx = BitmapEx(aBitmap, aBitmapAlpha);
+                else
+                    aBitmapEx = BitmapEx(aBitmap);
+                if (!prefSize.IsEmpty())
+                {
+                    aBitmapEx.SetPrefMapMode(MapMode(MapUnit::Map100thMM));
+                    aBitmapEx.SetPrefSize(prefSize);
+                }
+                rImportOutput.moBitmap = aBitmapEx;
+                return true;
+            }
+
+            pWriteAccessInstance = aBitmap;
+            if (!pWriteAccessInstance)
                 return false;
+            if (!aBitmapAlpha.IsEmpty())
+            {
+                pWriteAccessAlphaInstance = aBitmapAlpha;
+                if (!pWriteAccessAlphaInstance)
+                    return false;
+            }
         }
     }
-    BitmapScopedWriteAccess& pWriteAccess = pAccess ? *pAccess : 
pWriteAccessInstance;
-    BitmapScopedWriteAccess& pWriteAccessAlpha
-        = pAlphaAccess ? *pAlphaAccess : pWriteAccessAlphaInstance;
-
-    if (setjmp(png_jmpbuf(pPng)))
+    catch (const std::runtime_error& ex)
     {
+        std::cerr << "libpng error: " << ex.what() << std::endl;
+
         pWriteAccessInstance.reset();
         pWriteAccessAlphaInstance.reset();
-
-        if (!bUseExistingBitmap)
-        {
-            // Set the bitmap if it contains something, even on failure. This 
allows
-            // reading images that are only partially broken.
-            pWriteAccessInstance.reset();
-            pWriteAccessAlphaInstance.reset();
-            if (!aBitmap.IsEmpty() && !aBitmapAlpha.IsEmpty())
-                aBitmapEx = BitmapEx(aBitmap, aBitmapAlpha);
-            else if (!aBitmap.IsEmpty())
-                aBitmapEx = BitmapEx(aBitmap);
-            if (!aBitmapEx.IsEmpty() && !prefSize.IsEmpty())
-            {
-                aBitmapEx.SetPrefMapMode(MapMode(MapUnit::Map100thMM));
-                aBitmapEx.SetPrefSize(prefSize);
-            }
-            rImportOutput.moBitmap = aBitmapEx;
-        }
         return false;
     }
 
-    if (colorType == PNG_COLOR_TYPE_RGB)
-    {
-        ScanlineFormat eFormat = pWriteAccess->GetScanlineFormat();
-        if (eFormat == ScanlineFormat::N24BitTcBgr)
-            png_set_bgr(pPng);
+    BitmapScopedWriteAccess& pWriteAccess = pAccess ? *pAccess : 
pWriteAccessInstance;
+    BitmapScopedWriteAccess& pWriteAccessAlpha
+        = pAlphaAccess ? *pAlphaAccess : pWriteAccessAlphaInstance;
 
-        for (int pass = 0; pass < nNumberOfPasses; pass++)
-        {
-            for (png_uint_32 y = 0; y < height; y++)
-            {
-                Scanline pScanline = pWriteAccess->GetScanline(y);
-                png_read_row(pPng, pScanline, nullptr);
-            }
-        }
-    }
-    else if (colorType == PNG_COLOR_TYPE_RGB_ALPHA)
+    try
     {
-        size_t aRowSizeBytes = png_get_rowbytes(pPng, pInfo);
-        ScanlineFormat eFormat = pWriteAccess->GetScanlineFormat();
-        if (eFormat == ScanlineFormat::N24BitTcBgr)
-            png_set_bgr(pPng);
-
-        if (nNumberOfPasses == 1)
+        if (colorType == PNG_COLOR_TYPE_RGB)
         {
-            // optimise the common case, where we can use a buffer of only a 
single row
-            std::vector<png_byte> aRow(aRowSizeBytes, 0);
-            for (png_uint_32 y = 0; y < height; y++)
+            ScanlineFormat eFormat = pWriteAccess->GetScanlineFormat();
+            if (eFormat == ScanlineFormat::N24BitTcBgr)
+                png_set_bgr(pPng);
+
+            for (int pass = 0; pass < nNumberOfPasses; pass++)
             {
-                Scanline pScanline = pWriteAccess->GetScanline(y);
-                Scanline pScanAlpha = pWriteAccessAlpha->GetScanline(y);
-                png_bytep pRow = aRow.data();
-                png_read_row(pPng, pRow, nullptr);
-                size_t iAlpha = 0;
-                size_t iColor = 0;
-                for (size_t i = 0; i < aRowSizeBytes; i += 4)
+                for (png_uint_32 y = 0; y < height; y++)
                 {
-                    pScanline[iColor++] = pRow[i + 0];
-                    pScanline[iColor++] = pRow[i + 1];
-                    pScanline[iColor++] = pRow[i + 2];
-                    pScanAlpha[iAlpha++] = pRow[i + 3];
+                    Scanline pScanline = pWriteAccess->GetScanline(y);
+                    png_read_row(pPng, pScanline, nullptr);
                 }
             }
         }
-        else
+        else if (colorType == PNG_COLOR_TYPE_RGB_ALPHA)
         {
-            std::vector<std::vector<png_byte>> aRows(height);
-            for (auto& rRow : aRows)
-                rRow.resize(aRowSizeBytes, 0);
-            for (int pass = 0; pass < nNumberOfPasses; pass++)
+            size_t aRowSizeBytes = png_get_rowbytes(pPng, pInfo);
+            ScanlineFormat eFormat = pWriteAccess->GetScanlineFormat();
+            if (eFormat == ScanlineFormat::N24BitTcBgr)
+                png_set_bgr(pPng);
+
+            if (nNumberOfPasses == 1)
             {
+                // optimise the common case, where we can use a buffer of only 
a single row
+                std::vector<png_byte> aRow(aRowSizeBytes, 0);
                 for (png_uint_32 y = 0; y < height; y++)
                 {
                     Scanline pScanline = pWriteAccess->GetScanline(y);
                     Scanline pScanAlpha = pWriteAccessAlpha->GetScanline(y);
-                    png_bytep pRow = aRows[y].data();
+                    png_bytep pRow = aRow.data();
                     png_read_row(pPng, pRow, nullptr);
                     size_t iAlpha = 0;
                     size_t iColor = 0;
@@ -594,23 +558,79 @@ bool reader(SvStream& rStream, ImportOutput& 
rImportOutput,
                     }
                 }
             }
+            else
+            {
+                std::vector<std::vector<png_byte>> aRows(height);
+                for (auto& rRow : aRows)
+                    rRow.resize(aRowSizeBytes, 0);
+                for (int pass = 0; pass < nNumberOfPasses; pass++)
+                {
+                    for (png_uint_32 y = 0; y < height; y++)
+                    {
+                        Scanline pScanline = pWriteAccess->GetScanline(y);
+                        Scanline pScanAlpha = 
pWriteAccessAlpha->GetScanline(y);
+                        png_bytep pRow = aRows[y].data();
+                        png_read_row(pPng, pRow, nullptr);
+                        size_t iAlpha = 0;
+                        size_t iColor = 0;
+                        for (size_t i = 0; i < aRowSizeBytes; i += 4)
+                        {
+                            pScanline[iColor++] = pRow[i + 0];
+                            pScanline[iColor++] = pRow[i + 1];
+                            pScanline[iColor++] = pRow[i + 2];
+                            pScanAlpha[iAlpha++] = pRow[i + 3];
+                        }
+                    }
+                }
+            }
+        }
+        else if (colorType == PNG_COLOR_TYPE_GRAY)
+        {
+            for (int pass = 0; pass < nNumberOfPasses; pass++)
+            {
+                for (png_uint_32 y = 0; y < height; y++)
+                {
+                    Scanline pScanline = pWriteAccess->GetScanline(y);
+                    png_read_row(pPng, pScanline, nullptr);
+                }
+            }
         }
     }
-    else if (colorType == PNG_COLOR_TYPE_GRAY)
+    catch (const std::runtime_error& ex)
     {
-        for (int pass = 0; pass < nNumberOfPasses; pass++)
+        std::cerr << "libpng error: " << ex.what() << std::endl;
+
+        pWriteAccessInstance.reset();
+        pWriteAccessAlphaInstance.reset();
+
+        if (!bUseExistingBitmap)
         {
-            for (png_uint_32 y = 0; y < height; y++)
+            // Set the bitmap if it contains something, even on failure. This 
allows
+            // reading images that are only partially broken.
+            pWriteAccessInstance.reset();
+            pWriteAccessAlphaInstance.reset();
+            if (!aBitmap.IsEmpty() && !aBitmapAlpha.IsEmpty())
+                aBitmapEx = BitmapEx(aBitmap, aBitmapAlpha);
+            else if (!aBitmap.IsEmpty())
+                aBitmapEx = BitmapEx(aBitmap);
+            if (!aBitmapEx.IsEmpty() && !prefSize.IsEmpty())
             {
-                Scanline pScanline = pWriteAccess->GetScanline(y);
-                png_read_row(pPng, pScanline, nullptr);
+                aBitmapEx.SetPrefMapMode(MapMode(MapUnit::Map100thMM));
+                aBitmapEx.SetPrefSize(prefSize);
             }
+            rImportOutput.moBitmap = aBitmapEx;
         }
+        return false;
     }
-
     // If an error is thrown when calling png_read_end
-    if (setjmp(png_jmpbuf(pPng)))
+    try
     {
+        png_read_end(pPng, pInfo);
+    }
+    catch (const std::runtime_error& ex)
+    {
+        std::cerr << "libpng error: " << ex.what() << std::endl;
+
         if (!bUseExistingBitmap)
         {
             pWriteAccessInstance.reset();
@@ -630,8 +650,6 @@ bool reader(SvStream& rStream, ImportOutput& rImportOutput,
         return true;
     }
 
-    png_read_end(pPng, pInfo);
-
     if (!bUseExistingBitmap)
     {
         pWriteAccess.reset();
@@ -784,9 +802,6 @@ BinaryDataContainer getMsGifChunk(SvStream& rStream)
             return {};
     }
 }
-#if defined __GNUC__ && __GNUC__ <= 14 && !defined __clang__
-#pragma GCC diagnostic pop
-#endif
 
 } // anonymous namespace
 

Reply via email to