<commit_before>/** \brief Utility for augmenting MARC records with links to a local full-text database.
 *  \author Dr. Johannes Ruscheinski (johannes.ruscheinski@uni-tuebingen.de)
 *
 *  \copyright 2015-2019 Universitätsbibliothek Tübingen.  All rights reserved.
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as
 *  published by the Free Software Foundation, either version 3 of the
 *  License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <iostream>
#include <memory>
#include <vector>
#include <cassert>
#include <cstdio>
#include <cstdlib>
#include "Compiler.h"
#include "FullTextCache.h"
#include "MARC.h"
#include "MediaTypeUtil.h"
#include "OCR.h"
#include "PdfUtil.h"
#include "Semaphore.h"
#include "SmartDownloader.h"
#include "StringUtil.h"
#include "TextUtil.h"
#include "util.h"

namespace {

static void Usage() __attribute__((noreturn));

static void Usage() {
    std::cerr << "Usage: " << ::progname << " [--pdf-extraction-timeout=timeout]file_offset marc_input marc_output\n"
              << "       \"--pdf-extraction-timeout\" timeout in seconds (default " << PdfUtil::DEFAULT_PDF_EXTRACTION_TIMEOUT << ").\n"
              << "       \"file_offset\" Where to start reading a MARC data set from in marc_input.\n\n";
    std::exit(EXIT_FAILURE);
}

// \note Sets "error_message" when it returns false.
bool GetDocumentAndMediaType(const std::string &url, const unsigned timeout, std::string * const document,
                             std::string * const media_type, std::string * const media_subtype,
                             std::string * const http_header_charset, std::string * const error_message)
{
    if (not SmartDownload(url, timeout, document, http_header_charset, error_message))
        return false;

    *media_type = MediaTypeUtil::GetMediaType(*document, media_subtype);
    if (media_type->empty()) {
        *error_message = "Failed to get media type";
        return false;
    }

    return true;
}

static const std::map<std::string, std::string> marc_to_tesseract_language_codes_map {
    { "bul", "bul" },
    { "cze", "ces" },
    { "dan", "dan" },
    { "dut", "nld" },
    { "eng", "eng" },
    { "fin", "fin" },
    { "fre", "fra" },
    { "ger", "deu" },
    { "grc", "grc" },
    { "heb", "heb" },
    { "hun", "hun" },
    { "ita", "ita" },
    { "lat", "lat" },
    { "nor", "nor" },
    { "pol", "pol" },
    { "por", "por" },
    { "rus", "rus" },
    { "slv", "slv" },
    { "spa", "spa" },
    { "swe", "swe" },
};

std::string GetTesseractLanguageCode(const MARC::Record &record) {
    const auto map_iter(marc_to_tesseract_language_codes_map.find(MARC::GetLanguageCode(record)));
    return (map_iter == marc_to_tesseract_language_codes_map.cend()) ? "" : map_iter->second;
}

// Checks subfields "3" and "z" to see if they start w/ "Rezension".
bool IsProbablyAReview(const MARC::Subfields &subfields) {
    const std::vector<std::string> _3_subfields(subfields.extractSubfields('3'));
    if (not _3_subfields.empty()) {
        for (const auto &subfield_value : _3_subfields) {
            if (StringUtil::StartsWith(subfield_value, "Rezension"))
                return true;
        }
    } else {
        const std::vector<std::string> z_subfields(subfields.extractSubfields('z'));
        for (const auto &subfield_value : z_subfields) {
            if (StringUtil::StartsWith(subfield_value, "Rezension"))
                return true;
        }
    }

    return false;
}

// \return The concatenated contents of all 520$a subfields.
std::string GetTextFrom520a(const MARC::Record &record) {
    std::string concatenated_text;

    for (const auto &field : record.getTagRange("520")) {
        const MARC::Subfields subfields(field.getSubfields());
        if (subfields.hasSubfield('a')) {
            if (not concatenated_text.empty())
                concatenated_text += ' ';
            concatenated_text += subfields.getFirstSubfieldWithCode('a');
        }
    }

    return concatenated_text;
}

bool IsUTF8(const std::string &charset) {
    return charset == "utf-8" or charset == "utf8" or charset == "UFT-8" or charset == "UTF8";
}

std::string ConvertToPlainText(const std::string &media_type, const std::string &media_subtype, const std::string &http_header_charset,
                               const std::string &tesseract_language_code, const std::string &document,
                               const unsigned pdf_extraction_timeout, std::string * const error_message)
{
    std::string extracted_text;
    if (media_type == "text/html" or media_type == "text/xhtml") {
        extracted_text = TextUtil::ExtractTextFromHtml(document, http_header_charset);
        return TextUtil::CollapseWhitespace(&extracted_text);
    }

    if (media_type == "text/xml" and media_subtype == "tei") {
        extracted_text = TextUtil::ExtractTextFromUBTei(document);
        return TextUtil::CollapseWhitespace(&extracted_text);
    }

    if (StringUtil::StartsWith(media_type, "text/")) {
        if (not (media_type == "text/plain"))
            LOG_WARNING("treating " + media_type + " as text/plain");

        if (IsUTF8(http_header_charset))
            return document;

        std::string error_msg;
        std::unique_ptr<TextUtil::EncodingConverter> encoding_converter(TextUtil::EncodingConverter::Factory(http_header_charset,
                                                                                "utf8", &error_msg));
        if (encoding_converter.get() == nullptr) {
            LOG_WARNING("can't convert from \"" + http_header_charset + "\" to UTF-8! (" + error_msg + ")");
            return document;
        }

        std::string utf8_document;
        if (unlikely(not encoding_converter->convert(document, &utf8_document)))
            LOG_WARNING("conversion error while converting text from \"" + http_header_charset + "\" to UTF-8!");
        return TextUtil::CollapseWhitespace(&utf8_document);
    }

    if (StringUtil::StartsWith(media_type, "application/pdf")) {
        if (PdfUtil::PdfDocContainsNoText(document)) {
            if (not PdfUtil::GetTextFromImagePDF(document, tesseract_language_code, &extracted_text, pdf_extraction_timeout)) {
                *error_message = "Failed to extract text from an image PDF!";
                LOG_WARNING(*error_message);
                return "";
            }
            return TextUtil::CollapseWhitespace(&extracted_text);
        }
        PdfUtil::ExtractText(document, &extracted_text);
        return TextUtil::CollapseWhitespace(&extracted_text);
    }

    if (media_type == "image/jpeg" or media_type == "image/png") {
        if (OCR(document, &extracted_text, tesseract_language_code) != 0) {
            *error_message = "Failed to extract text by using OCR on " + media_type;
            LOG_WARNING(*error_message);
            return "";
        }
        return TextUtil::CollapseWhitespace(&extracted_text);
    }

    *error_message = "Don't know how to handle media type: " + media_type;
    LOG_WARNING(*error_message);
    return "";
}

bool ProcessRecordUrls(MARC::Record * const record, const unsigned pdf_extraction_timeout) {
    const std::string ppn(record->getControlNumber());
    std::vector<std::string> urls;

    // Get URL's:
    for (const auto _856_field : record->getTagRange("856")) {
        const MARC::Subfields _856_subfields(_856_field.getSubfields());

        if (_856_field.getIndicator1() == '7' or not _856_subfields.hasSubfield('u'))
            continue;

        if (IsProbablyAReview(_856_subfields))
            continue;

        urls.emplace_back(_856_subfields.getFirstSubfieldWithCode('u'));
    }

    // Get or create cache entry
    FullTextCache cache;
    std::string combined_text_final;
    bool success(false);
    if (not cache.entryExpired(ppn, urls)) {
        cache.getFullText(ppn, &combined_text_final);
        Semaphore semaphore("/full_text_cached_counter", Semaphore::ATTACH);
        ++semaphore;
        success = true;
    } else {
        FullTextCache::Entry entry;
        std::vector<FullTextCache::EntryUrl> entry_urls;
        std::string combined_text(GetTextFrom520a(*record));
        constexpr unsigned PER_DOC_TIMEOUT(30000); // in milliseconds
        bool at_least_one_error(false);

        for (const auto &url : urls) {
            FullTextCache::EntryUrl entry_url;
            entry_url.id_ = ppn;
            entry_url.url_ = url;
            std::string domain;
            cache.getDomainFromUrl(url, &domain);
            entry_url.domain_ = domain;
            std::string document, media_type, media_subtype, http_header_charset, error_message;
            if ((not GetDocumentAndMediaType(url, PER_DOC_TIMEOUT, &document, &media_type, &media_subtype, &http_header_charset,
                                             &error_message))) {
                LOG_WARNING("URL " + url + ": could not get document and media type! (" + error_message + ")");
                entry_url.error_message_ = "could not get document and media type! (" + error_message + ")";
            } else {
                std::string extracted_text(ConvertToPlainText(media_type, media_subtype, http_header_charset, GetTesseractLanguageCode(*record),
                                                              document, pdf_extraction_timeout, &error_message));

                if (unlikely(extracted_text.empty())) {
                    LOG_WARNING("URL " + url + ": failed to extract text from the downloaded document! (" + error_message + ")");
                    entry_url.error_message_ = "failed to extract text from the downloaded document! (" + error_message + ")";
                } else {
                    if (combined_text.empty())
                        combined_text.swap(extracted_text);
                    else
                        combined_text += " " + extracted_text;
                }
            }
            at_least_one_error = at_least_one_error ? at_least_one_error : not entry_url.error_message_.empty();
            entry_urls.push_back(entry_url);
        }

        success = not at_least_one_error && not urls.empty();

        combined_text_final = combined_text;
        TextUtil::CollapseAndTrimWhitespace(&combined_text_final);
        cache.insertEntry(ppn, combined_text_final, entry_urls);
    }

    if (not combined_text_final.empty())
        record->insertField("FUL", { { 'e', "http://localhost/cgi-bin/full_text_lookup?id=" + ppn } });

    return success;
}

bool ProcessRecord(MARC::Record * const record, const std::string &marc_output_filename, const unsigned pdf_extraction_timeout) {
    bool success(false);
    try {
        success = ProcessRecordUrls(record, pdf_extraction_timeout);
    } catch (const std::exception &x) {
        LOG_WARNING("caught exception: " + std::string(x.what()));
    }

    // Safely append the modified MARC data to the MARC output file:
    std::unique_ptr<MARC::Writer> marc_writer(MARC::Writer::Factory(marc_output_filename, MARC::FileType::BINARY,
                                                                    MARC::Writer::APPEND));
    MARC::FileLockedComposeAndWriteRecord(marc_writer.get(), *record);

    return success;
}

// Returns true if text has been successfully extracted, else false.
bool ProcessRecord(MARC::Reader * const marc_reader, const std::string &marc_output_filename, const unsigned pdf_extraction_timeout) {
    MARC::Record record(marc_reader->read());
    try {
        LOG_INFO("processing record " + record.getControlNumber());
        return ProcessRecord(&record, marc_output_filename, pdf_extraction_timeout);
    } catch (const std::exception &x) {
        throw std::runtime_error(x.what() + std::string(" (PPN: ") + record.getControlNumber() + ")");
    }
}

} // unnamed namespace

int main(int argc, char *argv[]) {
    ::progname = argv[0];

    unsigned pdf_extraction_timeout(PdfUtil::DEFAULT_PDF_EXTRACTION_TIMEOUT);
    if (argc > 1 and StringUtil::StartsWith(argv[1], "--pdf-extraction-timeout=")) {
        if (not StringUtil::ToNumber(argv[1] + __builtin_strlen("--pdf-extraction-timeout="), &pdf_extraction_timeout)
            or pdf_extraction_timeout == 0)
                LOG_ERROR("bad value for --pdf-extraction-timeout!");
        ++argv, --argc;
    }

    if (argc != 4)
        Usage();

    long offset;
    if (not StringUtil::ToNumber(argv[1], &offset))
        LOG_ERROR("file offset must be a number!");

    std::unique_ptr<MARC::Reader> marc_reader(MARC::Reader::Factory(argv[2], MARC::FileType::BINARY));
    if (not marc_reader->seek(offset, SEEK_SET))
        LOG_ERROR("failed to position " + marc_reader->getPath() + " at offset " + std::to_string(offset) + "!");

    try {
        return ProcessRecord(marc_reader.get(), argv[3], pdf_extraction_timeout) ? EXIT_SUCCESS : EXIT_FAILURE;
    } catch (const std::exception &e) {
        LOG_ERROR("While reading \"" + marc_reader->getPath() + "\" starting at offset \""
              + std::string(argv[1]) + "\", caught exception: " + std::string(e.what()));
    }
}
<commit_msg>Minor fix<commit_after>/** \brief Utility for augmenting MARC records with links to a local full-text database.
 *  \author Dr. Johannes Ruscheinski (johannes.ruscheinski@uni-tuebingen.de)
 *
 *  \copyright 2015-2019 Universitätsbibliothek Tübingen.  All rights reserved.
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as
 *  published by the Free Software Foundation, either version 3 of the
 *  License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <iostream>
#include <memory>
#include <vector>
#include <cassert>
#include <cstdio>
#include <cstdlib>
#include "Compiler.h"
#include "FullTextCache.h"
#include "MARC.h"
#include "MediaTypeUtil.h"
#include "OCR.h"
#include "PdfUtil.h"
#include "Semaphore.h"
#include "SmartDownloader.h"
#include "StringUtil.h"
#include "TextUtil.h"
#include "util.h"

namespace {

static void Usage() __attribute__((noreturn));

static void Usage() {
    std::cerr << "Usage: " << ::progname << " [--pdf-extraction-timeout=timeout]file_offset marc_input marc_output\n"
              << "       \"--pdf-extraction-timeout\" timeout in seconds (default " << PdfUtil::DEFAULT_PDF_EXTRACTION_TIMEOUT << ").\n"
              << "       \"file_offset\" Where to start reading a MARC data set from in marc_input.\n\n";
    std::exit(EXIT_FAILURE);
}

// \note Sets "error_message" when it returns false.
bool GetDocumentAndMediaType(const std::string &url, const unsigned timeout, std::string * const document,
                             std::string * const media_type, std::string * const media_subtype,
                             std::string * const http_header_charset, std::string * const error_message)
{
    if (not SmartDownload(url, timeout, document, http_header_charset, error_message))
        return false;

    *media_type = MediaTypeUtil::GetMediaType(*document, media_subtype);
    if (media_type->empty()) {
        *error_message = "Failed to get media type";
        return false;
    }

    return true;
}

static const std::map<std::string, std::string> marc_to_tesseract_language_codes_map {
    { "bul", "bul" },
    { "cze", "ces" },
    { "dan", "dan" },
    { "dut", "nld" },
    { "eng", "eng" },
    { "fin", "fin" },
    { "fre", "fra" },
    { "ger", "deu" },
    { "grc", "grc" },
    { "heb", "heb" },
    { "hun", "hun" },
    { "ita", "ita" },
    { "lat", "lat" },
    { "nor", "nor" },
    { "pol", "pol" },
    { "por", "por" },
    { "rus", "rus" },
    { "slv", "slv" },
    { "spa", "spa" },
    { "swe", "swe" },
};

std::string GetTesseractLanguageCode(const MARC::Record &record) {
    const auto map_iter(marc_to_tesseract_language_codes_map.find(MARC::GetLanguageCode(record)));
    return (map_iter == marc_to_tesseract_language_codes_map.cend()) ? "" : map_iter->second;
}

// Checks subfields "3" and "z" to see if they start w/ "Rezension".
bool IsProbablyAReview(const MARC::Subfields &subfields) {
    const std::vector<std::string> _3_subfields(subfields.extractSubfields('3'));
    if (not _3_subfields.empty()) {
        for (const auto &subfield_value : _3_subfields) {
            if (StringUtil::StartsWith(subfield_value, "Rezension"))
                return true;
        }
    } else {
        const std::vector<std::string> z_subfields(subfields.extractSubfields('z'));
        for (const auto &subfield_value : z_subfields) {
            if (StringUtil::StartsWith(subfield_value, "Rezension"))
                return true;
        }
    }

    return false;
}

// \return The concatenated contents of all 520$a subfields.
std::string GetTextFrom520a(const MARC::Record &record) {
    std::string concatenated_text;

    for (const auto &field : record.getTagRange("520")) {
        const MARC::Subfields subfields(field.getSubfields());
        if (subfields.hasSubfield('a')) {
            if (not concatenated_text.empty())
                concatenated_text += ' ';
            concatenated_text += subfields.getFirstSubfieldWithCode('a');
        }
    }

    return concatenated_text;
}

bool IsUTF8(const std::string &charset) {
    return charset == "utf-8" or charset == "utf8" or charset == "UFT-8" or charset == "UTF8";
}

std::string ConvertToPlainText(const std::string &media_type, const std::string &media_subtype, const std::string &http_header_charset,
                               const std::string &tesseract_language_code, const std::string &document,
                               const unsigned pdf_extraction_timeout, std::string * const error_message)
{
    std::string extracted_text;
    if (media_type == "text/html" or media_type == "text/xhtml") {
        extracted_text = TextUtil::ExtractTextFromHtml(document, http_header_charset);
        return TextUtil::CollapseWhitespace(&extracted_text);
    }

    if (media_type == "text/xml" and media_subtype == "tei") {
        extracted_text = TextUtil::ExtractTextFromUBTei(document);
        return TextUtil::CollapseWhitespace(&extracted_text);
    }

    if (StringUtil::StartsWith(media_type, "text/")) {
        if (not (media_type == "text/plain"))
            LOG_WARNING("treating " + media_type + " as text/plain");

        if (IsUTF8(http_header_charset))
            return document;

        std::string error_msg;
        std::unique_ptr<TextUtil::EncodingConverter> encoding_converter(TextUtil::EncodingConverter::Factory(http_header_charset,
                                                                                "utf8", &error_msg));
        if (encoding_converter.get() == nullptr) {
            LOG_WARNING("can't convert from \"" + http_header_charset + "\" to UTF-8! (" + error_msg + ")");
            return document;
        }

        std::string utf8_document;
        if (unlikely(not encoding_converter->convert(document, &utf8_document)))
            LOG_WARNING("conversion error while converting text from \"" + http_header_charset + "\" to UTF-8!");
        return TextUtil::CollapseWhitespace(&utf8_document);
    }

    if (StringUtil::StartsWith(media_type, "application/pdf")) {
        if (PdfUtil::PdfDocContainsNoText(document)) {
            if (not PdfUtil::GetTextFromImagePDF(document, tesseract_language_code, &extracted_text, pdf_extraction_timeout)) {
                *error_message = "Failed to extract text from an image PDF!";
                LOG_WARNING(*error_message);
                return "";
            }
            return TextUtil::CollapseWhitespace(&extracted_text);
        }
        PdfUtil::ExtractText(document, &extracted_text);
        return TextUtil::CollapseWhitespace(&extracted_text);
    }

    if (media_type == "image/jpeg" or media_type == "image/png") {
        if (OCR(document, &extracted_text, tesseract_language_code) != 0) {
            *error_message = "Failed to extract text by using OCR on " + media_type;
            LOG_WARNING(*error_message);
            return "";
        }
        return TextUtil::CollapseWhitespace(&extracted_text);
    }

    *error_message = "Don't know how to handle media type: " + media_type;
    LOG_WARNING(*error_message);
    return "";
}

bool ProcessRecordUrls(MARC::Record * const record, const unsigned pdf_extraction_timeout) {
    const std::string ppn(record->getControlNumber());
    std::vector<std::string> urls;

    // Get URL's:
    for (const auto _856_field : record->getTagRange("856")) {
        const MARC::Subfields _856_subfields(_856_field.getSubfields());

        if (_856_field.getIndicator1() == '7' or not _856_subfields.hasSubfield('u'))
            continue;

        if (IsProbablyAReview(_856_subfields))
            continue;

        urls.emplace_back(_856_subfields.getFirstSubfieldWithCode('u'));
    }

    // Get or create cache entry
    FullTextCache cache;
    std::string combined_text_final;
    bool success(false);
    if (not cache.entryExpired(ppn, urls)) {
        cache.getFullText(ppn, &combined_text_final);
        Semaphore semaphore("/full_text_cached_counter", Semaphore::ATTACH);
        ++semaphore;
        success = true;
    } else {
        FullTextCache::Entry entry;
        std::vector<FullTextCache::EntryUrl> entry_urls;
        std::string combined_text(GetTextFrom520a(*record));
        constexpr unsigned PER_DOC_TIMEOUT(30000); // in milliseconds
        bool at_least_one_error(false);

        for (const auto &url : urls) {
            FullTextCache::EntryUrl entry_url;
            entry_url.id_ = ppn;
            entry_url.url_ = url;
            std::string domain;
            cache.getDomainFromUrl(url, &domain);
            entry_url.domain_ = domain;
            std::string document, media_type, media_subtype, http_header_charset, error_message;
            if ((not GetDocumentAndMediaType(url, PER_DOC_TIMEOUT, &document, &media_type, &media_subtype, &http_header_charset,
                                             &error_message))) {
                LOG_WARNING("URL " + url + ": could not get document and media type! (" + error_message + ")");
                entry_url.error_message_ = "could not get document and media type! (" + error_message + ")";
            } else {
                std::string extracted_text(ConvertToPlainText(media_type, media_subtype, http_header_charset, GetTesseractLanguageCode(*record),
                                                              document, pdf_extraction_timeout, &error_message));

                if (unlikely(extracted_text.empty())) {
                    LOG_WARNING("URL " + url + ": failed to extract text from the downloaded document! (" + error_message + ")");
                    entry_url.error_message_ = "failed to extract text from the downloaded document! (" + error_message + ")";
                } else {
                    if (combined_text.empty())
                        combined_text.swap(extracted_text);
                    else
                        combined_text += " " + extracted_text;
                }
            }
            at_least_one_error = at_least_one_error ? at_least_one_error : not entry_url.error_message_.empty();
            entry_urls.push_back(entry_url);
        }

        success = not at_least_one_error && not urls.empty();

        combined_text_final = TextUtil::CollapseAndTrimWhitespace(&combined_text);
        cache.insertEntry(ppn, combined_text_final, entry_urls);
    }

    if (not combined_text_final.empty())
        record->insertField("FUL", { { 'e', "http://localhost/cgi-bin/full_text_lookup?id=" + ppn } });

    return success;
}

bool ProcessRecord(MARC::Record * const record, const std::string &marc_output_filename, const unsigned pdf_extraction_timeout) {
    bool success(false);
    try {
        success = ProcessRecordUrls(record, pdf_extraction_timeout);
    } catch (const std::exception &x) {
        LOG_WARNING("caught exception: " + std::string(x.what()));
    }

    // Safely append the modified MARC data to the MARC output file:
    std::unique_ptr<MARC::Writer> marc_writer(MARC::Writer::Factory(marc_output_filename, MARC::FileType::BINARY,
                                                                    MARC::Writer::APPEND));
    MARC::FileLockedComposeAndWriteRecord(marc_writer.get(), *record);

    return success;
}

// Returns true if text has been successfully extracted, else false.
bool ProcessRecord(MARC::Reader * const marc_reader, const std::string &marc_output_filename, const unsigned pdf_extraction_timeout) {
    MARC::Record record(marc_reader->read());
    try {
        LOG_INFO("processing record " + record.getControlNumber());
        return ProcessRecord(&record, marc_output_filename, pdf_extraction_timeout);
    } catch (const std::exception &x) {
        throw std::runtime_error(x.what() + std::string(" (PPN: ") + record.getControlNumber() + ")");
    }
}

} // unnamed namespace

int main(int argc, char *argv[]) {
    ::progname = argv[0];

    unsigned pdf_extraction_timeout(PdfUtil::DEFAULT_PDF_EXTRACTION_TIMEOUT);
    if (argc > 1 and StringUtil::StartsWith(argv[1], "--pdf-extraction-timeout=")) {
        if (not StringUtil::ToNumber(argv[1] + __builtin_strlen("--pdf-extraction-timeout="), &pdf_extraction_timeout)
            or pdf_extraction_timeout == 0)
                LOG_ERROR("bad value for --pdf-extraction-timeout!");
        ++argv, --argc;
    }

    if (argc != 4)
        Usage();

    long offset;
    if (not StringUtil::ToNumber(argv[1], &offset))
        LOG_ERROR("file offset must be a number!");

    std::unique_ptr<MARC::Reader> marc_reader(MARC::Reader::Factory(argv[2], MARC::FileType::BINARY));
    if (not marc_reader->seek(offset, SEEK_SET))
        LOG_ERROR("failed to position " + marc_reader->getPath() + " at offset " + std::to_string(offset) + "!");

    try {
        return ProcessRecord(marc_reader.get(), argv[3], pdf_extraction_timeout) ? EXIT_SUCCESS : EXIT_FAILURE;
    } catch (const std::exception &e) {
        LOG_ERROR("While reading \"" + marc_reader->getPath() + "\" starting at offset \""
              + std::string(argv[1]) + "\", caught exception: " + std::string(e.what()));
    }
}
<|endoftext|>


<commit_before>/*
 * HistoryEntryBuffer.cpp
 *
 *  Created on: 24.02.2016
 *      Author: hartung
 */

#include <synchronization/HistoryEntryBuffer.hpp>
#include <cstdint>

namespace Synchronization
{

    HistoryEntryBuffer::HistoryEntryBuffer(const HistoryEntry& in, bool init)
            : _free(init),
              _time(in.getTime()),
              _realSize(in.getValueCollection().getValues<real_type>().size()),
              _realVals(nullptr),
              _intSize(in.getValueCollection().getValues<int_type>().size()),
              _intVals(nullptr),
              _bool_typeSize(in.getValueCollection().getValues<bool_type>().size()),
              _bool_typeVals(nullptr),
              _dataSize((_realSize + 1) * sizeof(real_type) + (_intSize + 1) * sizeof(int) + (_bool_typeSize + 1) * sizeof(bool_type))
    {
        initialize(in);
    }

    HistoryEntryBuffer::~HistoryEntryBuffer()
    {
    }

    HistoryEntryBuffer::operator HistoryEntry() const
    {
        HistoryEntry res(
                ((real_type*)_data.get())[0],
                (getStartOfAdditionalIntValues()[0]),
                FMI::ValueCollection(_realSize,_intSize,_bool_typeSize,0ul),
                getStartOfAdditionalBoolValues()[0]
                );

        copy(_realVals,_realVals+_realSize,res.getValueCollection().getValues<real_type>().begin());
        copy(_intVals,_intVals+_intSize,res.getValueCollection().getValues<int_type>().begin());
        for(size_type i=0;i<_bool_typeSize;++i)
            res.getValueCollection().getValues<int_type>()[i] = _bool_typeVals[i];
        return res;
    }

    HistoryEntryBuffer& HistoryEntryBuffer::operator=(const HistoryEntry& in)
    {
        return (*this = HistoryEntryBuffer(in,true));
    }

    void* HistoryEntryBuffer::data()
    {
        return reinterpret_cast<void*>(_data.get());
    }

    size_type HistoryEntryBuffer::dataSize() const
    {
        return _dataSize;
    }

    void HistoryEntryBuffer::initialize(const HistoryEntry & in)
    {
        //setting up pointers
        _data = shared_ptr<char>(new char[_dataSize]);
        _realVals = getStartOfValueCollection();
        _intVals = reinterpret_cast<int*>(_realVals + _realSize);
        _bool_typeVals = reinterpret_cast<bool_type*>(_intVals + _intSize);

        //copy data
        ((real_type*)_data.get())[0] = in.getTime();
        getStartOfAdditionalIntValues()[0] = in.getSolverOrder();
        getStartOfAdditionalBoolValues()[0] = in.hasEvent();

        copy(in.getValueCollection().getValues<real_type>().begin(), in.getValueCollection().getValues<real_type>().end(), _realVals);
        copy(in.getValueCollection().getValues<int_type>().begin(), in.getValueCollection().getValues<int_type>().end(), _intVals);
        for(size_type i=0;i<_bool_typeSize;++i)
            _bool_typeVals[i] = in.getValueCollection().getValues<bool_type>()[i-1];
    }

    real_type* HistoryEntryBuffer::getStartOfValueCollection()
    {
        return reinterpret_cast<real_type*>(_data.get()+sizeof(real_type)+sizeof(int)+sizeof(bool_type));
    }

    int* HistoryEntryBuffer::getStartOfAdditionalIntValues()
    {
        return reinterpret_cast<int*>(_data.get()+sizeof(real_type));
    }

    bool_type* HistoryEntryBuffer::getStartOfAdditionalBoolValues()
    {
        return reinterpret_cast<bool_type*>(_data.get()+sizeof(real_type)+sizeof(int));
    }

    const int* HistoryEntryBuffer::getStartOfAdditionalIntValues() const
    {
        return reinterpret_cast<int*>(_data.get()+sizeof(real_type));
    }

    bool HistoryEntryBuffer::isFree() const
    {
        return _free;
    }

    void HistoryEntryBuffer::setFree(const bool& in)
    {
        _free = in;
    }

    const bool_type* HistoryEntryBuffer::getStartOfAdditionalBoolValues() const
    {
        return reinterpret_cast<bool_type*>(_data.get()+sizeof(real_type)+sizeof(int));
    }

} /* namespace Synchronization */

<commit_msg>Harmonize file and add member to initialization list<commit_after>/*
 * HistoryEntryBuffer.cpp
 *
 *  Created on: 24.02.2016
 *      Author: hartung
 */

#include <synchronization/HistoryEntryBuffer.hpp>
#include <cstdint>

namespace Synchronization
{

    HistoryEntryBuffer::HistoryEntryBuffer(const HistoryEntry & in, bool init)
            : _free(init),
              _time(in.getTime()),
              _realSize(in.getValueCollection().getValues<real_type>().size()),
              _realVals(nullptr),
              _intSize(in.getValueCollection().getValues<int_type>().size()),
              _intVals(nullptr),
              _bool_typeSize(in.getValueCollection().getValues<bool_type>().size()),
              _bool_typeVals(nullptr),
              _dataSize(
                      (_realSize + 1) * sizeof(real_type) + (_intSize + 1) * sizeof(int)
                              + (_bool_typeSize + 1) * sizeof(bool_type)),
              _data(nullptr)
    {
        initialize(in);
    }

    HistoryEntryBuffer::operator HistoryEntry() const
    {
        HistoryEntry res(((real_type*) _data.get())[0], (getStartOfAdditionalIntValues()[0]),
                         FMI::ValueCollection(_realSize, _intSize, _bool_typeSize, 0ul),
                         getStartOfAdditionalBoolValues()[0]);

        copy(_realVals, _realVals + _realSize, res.getValueCollection().getValues<real_type>().begin());
        copy(_intVals, _intVals + _intSize, res.getValueCollection().getValues<int_type>().begin());
        for (size_type i = 0; i < _bool_typeSize; ++i)
            res.getValueCollection().getValues<int_type>()[i] = _bool_typeVals[i];
        return res;
    }

    HistoryEntryBuffer & HistoryEntryBuffer::operator=(const HistoryEntry & in)
    {
        return (*this = HistoryEntryBuffer(in, true));
    }

    void * HistoryEntryBuffer::data()
    {
        return reinterpret_cast<void*>(_data.get());
    }

    size_type HistoryEntryBuffer::dataSize() const
    {
        return _dataSize;
    }

    void HistoryEntryBuffer::initialize(const HistoryEntry & in)
    {
        //setting up pointers
        _data = shared_ptr<char>(new char[_dataSize], std::default_delete<char[]>());
        _realVals = getStartOfValueCollection();
        _intVals = reinterpret_cast<int*>(_realVals + _realSize);
        _bool_typeVals = reinterpret_cast<bool_type*>(_intVals + _intSize);

        //copy data
        ((real_type*) _data.get())[0] = in.getTime();
        getStartOfAdditionalIntValues()[0] = in.getSolverOrder();
        getStartOfAdditionalBoolValues()[0] = in.hasEvent();

        copy(in.getValueCollection().getValues<real_type>().begin(),
             in.getValueCollection().getValues<real_type>().end(), _realVals);
        copy(in.getValueCollection().getValues<int_type>().begin(), in.getValueCollection().getValues<int_type>().end(),
             _intVals);
        for (size_type i = 0; i < _bool_typeSize; ++i)
            _bool_typeVals[i] = in.getValueCollection().getValues<bool_type>()[i - 1];
    }

    real_type * HistoryEntryBuffer::getStartOfValueCollection()
    {
        return reinterpret_cast<real_type*>(_data.get() + sizeof(real_type) + sizeof(int) + sizeof(bool_type));
    }

    int * HistoryEntryBuffer::getStartOfAdditionalIntValues()
    {
        return reinterpret_cast<int*>(_data.get() + sizeof(real_type));
    }

    bool_type * HistoryEntryBuffer::getStartOfAdditionalBoolValues()
    {
        return reinterpret_cast<bool_type*>(_data.get() + sizeof(real_type) + sizeof(int));
    }

    const int * HistoryEntryBuffer::getStartOfAdditionalIntValues() const
    {
        return reinterpret_cast<int*>(_data.get() + sizeof(real_type));
    }

    bool HistoryEntryBuffer::isFree() const
    {
        return _free;
    }

    void HistoryEntryBuffer::setFree(const bool& in)
    {
        _free = in;
    }

    const bool_type * HistoryEntryBuffer::getStartOfAdditionalBoolValues() const
    {
        return reinterpret_cast<bool_type*>(_data.get() + sizeof(real_type) + sizeof(int));
    }

} /* namespace Synchronization */

<|endoftext|>


<commit_before>/*
 * Copyright (c) 2011 Stephen Williams (steve@icarus.com)
 * Copyright CERN 2014
 * @author Maciej Suminski (maciej.suminski@cern.ch)
 *
 *    This source code is free software; you can redistribute it
 *    and/or modify it in source code form under the terms of the GNU
 *    General Public License as published by the Free Software
 *    Foundation; either version 2 of the License, or (at your option)
 *    any later version.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU General Public License for more details.
 *
 *    You should have received a copy of the GNU General Public License
 *    along with this program; if not, write to the Free Software
 *    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */

# include  "vsignal.h"
# include  "expression.h"
# include  "vtype.h"
# include  <iostream>

using namespace std;

SigVarBase::SigVarBase(perm_string nam, const VType*typ, Expression*exp)
: name_(nam), type_(typ), init_expr_(exp), refcnt_sequ_(0)
{
}

SigVarBase::~SigVarBase()
{
}

void SigVarBase::elaborate(Entity*ent, ScopeBase*scope)
{
    if(init_expr_)
        init_expr_->elaborate_expr(ent, scope, peek_type());

    type_->elaborate(ent, scope);
}

void SigVarBase::type_elaborate_(VType::decl_t&decl)
{
      decl.type = type_;
}

int Signal::emit(ostream&out, Entity*ent, ScopeBase*scope)
{
      int errors = 0;

      VType::decl_t decl;
      type_elaborate_(decl);

      const VType*type = peek_type();
      if (peek_refcnt_sequ_() > 0
              || (!type->can_be_packed() && dynamic_cast<const VTypeArray*>(type)))
	    decl.reg_flag = true;
      errors += decl.emit(out, peek_name());

      Expression*init_expr = peek_init_expr();
      if (init_expr) {
	    out << " = ";
	    init_expr->emit(out, ent, scope);
      }
      out << ";" << endl;
      return errors;
}

int Variable::emit(ostream&out, Entity*ent, ScopeBase*scope)
{
      int errors = 0;

      VType::decl_t decl;
      type_elaborate_(decl);
      decl.reg_flag = true;
      errors += decl.emit(out, peek_name());

      Expression*init_expr = peek_init_expr();
      if (init_expr) {
	    out << " = ";
	    init_expr->emit(out, ent, scope);
      }
      out << ";" << endl;
      return errors;
}

void Variable::write_to_stream(std::ostream&fd)
{
      fd << "variable " << peek_name() << " : ";
      peek_type()->write_to_stream(fd);
      fd << ";" << endl;
}
<commit_msg>vhdlpp: Emit init values for wires as a weak assignment.<commit_after>/*
 * Copyright (c) 2011 Stephen Williams (steve@icarus.com)
 * Copyright CERN 2014
 * @author Maciej Suminski (maciej.suminski@cern.ch)
 *
 *    This source code is free software; you can redistribute it
 *    and/or modify it in source code form under the terms of the GNU
 *    General Public License as published by the Free Software
 *    Foundation; either version 2 of the License, or (at your option)
 *    any later version.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU General Public License for more details.
 *
 *    You should have received a copy of the GNU General Public License
 *    along with this program; if not, write to the Free Software
 *    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */

# include  "vsignal.h"
# include  "expression.h"
# include  "vtype.h"
# include  <iostream>

using namespace std;

SigVarBase::SigVarBase(perm_string nam, const VType*typ, Expression*exp)
: name_(nam), type_(typ), init_expr_(exp), refcnt_sequ_(0)
{
}

SigVarBase::~SigVarBase()
{
}

void SigVarBase::elaborate(Entity*ent, ScopeBase*scope)
{
    if(init_expr_)
        init_expr_->elaborate_expr(ent, scope, peek_type());

    type_->elaborate(ent, scope);
}

void SigVarBase::type_elaborate_(VType::decl_t&decl)
{
      decl.type = type_;
}

int Signal::emit(ostream&out, Entity*ent, ScopeBase*scope)
{
      int errors = 0;

      VType::decl_t decl;
      type_elaborate_(decl);

      const VType*type = peek_type();
      if (peek_refcnt_sequ_() > 0
              || (!type->can_be_packed() && dynamic_cast<const VTypeArray*>(type)))
	    decl.reg_flag = true;
      errors += decl.emit(out, peek_name());

      Expression*init_expr = peek_init_expr();
      if (init_expr) {
            /* Emit initialization value for wires as a weak assignment */
            if(!decl.reg_flag)
                out << ";" << endl << "/*init*/ assign (weak1, weak0) " << peek_name();

            out << " = ";
            init_expr->emit(out, ent, scope);
      }
      out << ";" << endl;
      return errors;
}

int Variable::emit(ostream&out, Entity*ent, ScopeBase*scope)
{
      int errors = 0;

      VType::decl_t decl;
      type_elaborate_(decl);
      decl.reg_flag = true;
      errors += decl.emit(out, peek_name());

      Expression*init_expr = peek_init_expr();
      if (init_expr) {
	    out << " = ";
	    init_expr->emit(out, ent, scope);
      }
      out << ";" << endl;
      return errors;
}

void Variable::write_to_stream(std::ostream&fd)
{
      fd << "variable " << peek_name() << " : ";
      peek_type()->write_to_stream(fd);
      fd << ";" << endl;
}
<|endoftext|>


<commit_before>/* node.cc
 * libxml++ and this file are copyright (C) 2000 by Ari Johnson, and
 * are covered by the GNU Lesser General Public License, which should be
 * included with libxml++ as the file COPYING.
 */

#include <libxml++/nodes/element.h>
#include <libxml++/nodes/node.h>
#include <libxml++/nodes/entityreference.h>
#include <libxml++/nodes/textnode.h>
#include <libxml++/nodes/commentnode.h>
#include <libxml++/nodes/cdatanode.h>
#include <libxml++/nodes/processinginstructionnode.h>
#include <libxml++/exceptions/internal_error.h>
#include <libxml++/document.h>
#include <libxml/xpath.h>
#include <libxml/xpathInternals.h>
#include <libxml/tree.h>

#include <iostream>

namespace xmlpp
{

Node::Node(xmlNode* node)
  : impl_(node)
{
   impl_->_private = this;
}

Node::~Node()
{}

const Element* Node::get_parent() const
{
  return const_cast<Node*>(this)->get_parent();
}

Element* Node::get_parent()
{
  if(!(cobj()->parent && cobj()->parent->type == XML_ELEMENT_NODE))
    return 0;

  Node::create_wrapper(cobj()->parent);
  return static_cast<Element*>(cobj()->parent->_private);
}

const Node* Node::get_next_sibling() const
{
  return const_cast<Node*>(this)->get_next_sibling();
}

Node* Node::get_next_sibling()
{
  if(!cobj()->next)
    return 0;

  Node::create_wrapper(cobj()->next);
  return static_cast<Node*>(cobj()->next->_private);
}

const Node* Node::get_previous_sibling() const
{
  return const_cast<Node*>(this)->get_previous_sibling();
}

Node* Node::get_previous_sibling()
{
  if(!cobj()->prev)
    return 0;

  Node::create_wrapper(cobj()->prev);
  return static_cast<Node*>(cobj()->prev->_private);
}

Node::NodeList Node::get_children(const xmlpp::string& name)
{
   xmlNode* child = impl_->children;
   if(!child)
     return NodeList();

   NodeList children;
   do
   {
      if(name.empty() || name == (const char*)child->name)
      {
        Node::create_wrapper(child);
        children.push_back(reinterpret_cast<Node*>(child->_private));
      }
   }
   while((child = child->next));

   return children;
}

const Node::NodeList Node::get_children(const xmlpp::string& name) const
{
  return const_cast<Node*>(this)->get_children(name);
}

Element* Node::add_child(const xmlpp::string& name,
                         const xmlpp::string& ns_prefix)
{
  _xmlNode* child = create_new_child_node(name, ns_prefix);
  if(!child)
    return 0;

  _xmlNode* node = xmlAddChild(impl_, child);
  if(!node)
    return 0;

  Node::create_wrapper(node);
  return static_cast<Element*>(node->_private);
}

Element* Node::add_child(xmlpp::Node* previous_sibling,
                         const xmlpp::string& name,
                         const xmlpp::string& ns_prefix)
{
  if(!previous_sibling)
    return 0;

  _xmlNode* child = create_new_child_node(name, ns_prefix);
  if(!child)
    return 0;

  _xmlNode* node = xmlAddNextSibling(previous_sibling->cobj(), child);
  if(!node)
    return 0;

  Node::create_wrapper(node);
  return static_cast<Element*>(node->_private);
}

Element* Node::add_child_before(xmlpp::Node* next_sibling,
                         const xmlpp::string& name,
                         const xmlpp::string& ns_prefix)
{
  if(!next_sibling)
    return 0;

  _xmlNode* child = create_new_child_node(name, ns_prefix);
  if(!child)
    return 0;

  _xmlNode* node = xmlAddPrevSibling(next_sibling->cobj(), child);
  if(!node)
    return 0;

  Node::create_wrapper(node);
  return static_cast<Element*>(node->_private);
}

_xmlNode* Node::create_new_child_node(const xmlpp::string& name, const xmlpp::string& ns_prefix)
{
   xmlNs* ns = 0;

   if(impl_->type != XML_ELEMENT_NODE)
   {
      #ifdef LIBXMLCPP_EXCEPTIONS_ENABLED
      throw internal_error("You can only add child nodes to element nodes");
      #else
      return 0;
      #endif //LIBXMLCPP_EXCEPTIONS_ENABLED
   }

   //Ignore the namespace if none was specified:
   if(!ns_prefix.empty())
   {
     //Use the existing namespace if one exists:
     ns = xmlSearchNs(impl_->doc, impl_, (const xmlChar*)ns_prefix.c_str());
     if (!ns)
     {
       #ifdef LIBXMLCPP_EXCEPTIONS_ENABLED
       throw exception("The namespace prefix (" + ns_prefix + ") has not been declared.");
       #else
       return 0;
       #endif //LIBXMLCPP_EXCEPTIONS_ENABLED
     }
   }

   return xmlNewNode(ns, (const xmlChar*)name.c_str());
}

void Node::remove_child(Node* node)
{
  //TODO: Allow a node to be removed without deleting it, to allow it to be moved?
  //This would require a more complex memory management API.

  xmlNode* cnode = node->cobj();
  Node::free_wrappers(cnode); //This delete the C++ node (not this) itself.
  xmlUnlinkNode(cnode);
  xmlFreeNode(cnode);
}

Node* Node::import_node(const Node* node, bool recursive)
{
  //Create the node, by copying:
  xmlNode* imported_node = xmlDocCopyNode(const_cast<xmlNode*>(node->cobj()), impl_->doc, recursive);
  if (!imported_node)
  {
    #ifdef LIBXMLCPP_EXCEPTIONS_ENABLED
    throw exception("Unable to import node");
    #else
    return 0;
    #endif //LIBXMLCPP_EXCEPTIONS_ENABLED
  }

  //Add the node:
  xmlNode* added_node = xmlAddChild(this->cobj(),imported_node);
  if (!added_node)
  {
    Node::free_wrappers(imported_node);
    xmlFreeNode(imported_node);

    #ifdef LIBXMLCPP_EXCEPTIONS_ENABLED
    throw exception("Unable to add imported node to current node");
    #else
    return 0;
    #endif //LIBXMLCPP_EXCEPTIONS_ENABLED
  }

  Node::create_wrapper(imported_node);
  return static_cast<Node*>(imported_node->_private);
}

xmlpp::string Node::get_name() const
{
  return impl_->name ? (const char*)impl_->name : "";
}

void Node::set_name(const xmlpp::string& name)
{
  xmlNodeSetName( impl_, (const xmlChar *)name.c_str() );
}

int Node::get_line() const
{
   return XML_GET_LINE(impl_);
}

xmlNode* Node::cobj()
{
  return impl_;
}

const xmlNode* Node::cobj() const
{
  return impl_;
}

xmlpp::string Node::get_path() const
{
  xmlChar* path = xmlGetNodePath(impl_);
  xmlpp::string retn = path ? (char*)path : "";
  xmlFree(path);
  return retn;
}

static NodeSet find_impl(xmlXPathContext* ctxt, const xmlpp::string& xpath)
{
  xmlXPathObject* result = xmlXPathEval((const xmlChar*)xpath.c_str(), ctxt);

  if(!result)
  {
    xmlXPathFreeContext(ctxt);

    #ifdef LIBXMLCPP_EXCEPTIONS_ENABLED
    throw exception("Invalid XPath: " + xpath);
    #else
    return NodeSet();
    #endif //LIBXMLCPP_EXCEPTIONS_ENABLED
  }

  if(result->type != XPATH_NODESET)
  {
    xmlXPathFreeObject(result);
    xmlXPathFreeContext(ctxt);

    #ifdef LIBXMLCPP_EXCEPTIONS_ENABLE
    throw internal_error("Only nodeset result types are supported.");
    #else
    return NodeSet();
    #endif //LIBXMLCPP_EXCEPTIONS_ENABLED
  }

  xmlNodeSet* nodeset = result->nodesetval;
  NodeSet nodes;
  if( nodeset && !xmlXPathNodeSetIsEmpty(nodeset))
  {
    const int count = xmlXPathNodeSetGetLength(nodeset);
    nodes.reserve(count);
    for (int i = 0; i != count; ++i)
    {
      xmlNode* cnode = xmlXPathNodeSetItem(nodeset, i);
      if(cnode->type == XML_NAMESPACE_DECL)
      {
        //In this case we would cast it to a xmlNs*,
        //but this C++ method only returns Nodes.
        std::cerr << "Node::find_impl: ignoring an xmlNs object." << std::endl;
        continue;
      }

      //TODO: Check for other cnode->type values?

      Node::create_wrapper(cnode);
      Node* cppNode = static_cast<Node*>(cnode->_private);
      nodes.push_back(cppNode);
    }
  }
  else
  {
    // return empty set
  }

  xmlXPathFreeObject(result);
  xmlXPathFreeContext(ctxt);

  return nodes;
}

NodeSet Node::find(const xmlpp::string& xpath) const
{
  xmlXPathContext* ctxt = xmlXPathNewContext(impl_->doc);
  ctxt->node = impl_;

  return find_impl(ctxt, xpath);
}

NodeSet Node::find(const xmlpp::string& xpath,
		   const PrefixNsMap& namespaces) const
{
  xmlXPathContext* ctxt = xmlXPathNewContext(impl_->doc);
  ctxt->node = impl_;

  for (PrefixNsMap::const_iterator it=namespaces.begin();
       it != namespaces.end(); it++)
    xmlXPathRegisterNs(ctxt,
		       reinterpret_cast<const xmlChar*>(it->first.c_str()),
		       reinterpret_cast<const xmlChar*>(it->second.c_str()));

  return find_impl(ctxt, xpath);
}

xmlpp::string Node::get_namespace_prefix() const
{
  if(impl_->type == XML_DOCUMENT_NODE)
  {
    //impl_ is actually of type xmlDoc, instead of just xmlNode.
    //libxml does not always use GObject-style inheritance, so xmlDoc does not have all the same struct fields as xmlNode.
    //Therefore, a call to impl_->ns would be invalid.
    //This can be an issue when calling this method on a Node returned by Node::find().
    //See the TODO comment on Document, suggesting that Document should derived from Node.

    return xmlpp::string();
  }

  if(impl_ && impl_->ns && impl_->ns->prefix)
    return (char*)impl_->ns->prefix;
  else
    return xmlpp::string();
}

xmlpp::string Node::get_namespace_uri() const
{
  if(impl_->type == XML_DOCUMENT_NODE)
  {
    //impl_ is actually of type xmlDoc, instead of just xmlNode.
    //libxml does not always use GObject-style inheritance, so xmlDoc does not have all the same struct fields as xmlNode.
    //Therefore, a call to impl_->ns would be invalid.
    //This can be an issue when calling this method on a Node returned by Node::find().
    //See the TODO comment on Document, suggesting that Document should derived from Node.

    return xmlpp::string();
  }

  if(impl_ && impl_->ns && impl_->ns->href)
    return (char*)impl_->ns->href;
  else
    return xmlpp::string();
}

void Node::set_namespace(const xmlpp::string& ns_prefix)
{
  //Look for the existing namespace to use:
  xmlNs* ns = xmlSearchNs( cobj()->doc, cobj(), (xmlChar*)(ns_prefix.empty() ? 0 : ns_prefix.c_str()) );
  if(ns)
  {
      //Use it for this element:
      xmlSetNs(cobj(), ns);
  }
  else
  {
    #ifdef LIBXMLCPP_EXCEPTIONS_ENABLE
    throw exception("The namespace (" + ns_prefix + ") has not been declared.");
    #endif //LIBXMLCPP_EXCEPTIONS_ENABLE
  }
}

void Node::create_wrapper(xmlNode* node)
{
  if(node->_private)
  {
	  //Node already wrapped, skip
	  return;
  }

  switch (node->type)
  {
    case XML_ELEMENT_NODE:
    {
      node->_private = new xmlpp::Element(node);
      break;
    }
    case XML_ATTRIBUTE_NODE:
    {
      node->_private = new xmlpp::Attribute(node);
      break;
    }
    case XML_TEXT_NODE:
    {
      node->_private = new xmlpp::TextNode(node);
      break;
    }
    case XML_COMMENT_NODE:
    {
      node->_private = new xmlpp::CommentNode(node);
      break;
    }
    case XML_CDATA_SECTION_NODE:
    {
      node->_private = new xmlpp::CdataNode(node);
      break;
    }
    case XML_PI_NODE:
    {
      node->_private = new xmlpp::ProcessingInstructionNode(node);
      break;
    }
    case XML_DTD_NODE:
    {
      node->_private = new xmlpp::Dtd(reinterpret_cast<xmlDtd*>(node));
      break;
    }
    //case XML_ENTITY_NODE:
    //{
    //  assert(0 && "Warning: XML_ENTITY_NODE not implemented");
    //  //node->_private = new xmlpp::ProcessingInstructionNode(node);
    //  break;
    //}
    case XML_ENTITY_REF_NODE:
    {
      node->_private = new xmlpp::EntityReference(node);
      break;
    }
    case XML_DOCUMENT_NODE:
    {
      // do nothing. For Documents it's the wrapper that is the owner.
      break;
    }
    default:
    {
      // good default for release versions
      node->_private = new xmlpp::Node(node);
      std::cerr << G_STRFUNC << "Warning: new node of unknown type created: " << node->type << std::endl;
      break;
    }
  }
}

void Node::free_wrappers(xmlNode* node)
{
  if(!node)
    return;

  //Walk the children list
  for(xmlNode* child=node->children; child; child=child->next)
    free_wrappers(child);

  //Delete the local one
  switch(node->type)
  {
    //Node types that have no properties
    case XML_DTD_NODE:
      delete static_cast<Dtd*>(node->_private);
      node->_private = 0;
      return;
    case XML_ATTRIBUTE_NODE:
    case XML_ELEMENT_DECL:
    case XML_ATTRIBUTE_DECL:
    case XML_ENTITY_DECL:
      delete static_cast<Node*>(node->_private);
      node->_private = 0;
      return;
    case XML_DOCUMENT_NODE:
      //Do not free now. The Document is usually the one who owns the caller.
      return;
    default:
      delete static_cast<Node*>(node->_private);
      node->_private = 0;
      break;
  }

  //Walk the attributes list.
  //Note that some "derived" struct have a different layout, so
  //_xmlNode::properties would be a nonsense value, leading to crashes,
  //(and shown as valgrind warnings), so we return above, to avoid
  //checking it here.
  for(xmlAttr* attr = node->properties; attr; attr = attr->next)
    free_wrappers(reinterpret_cast<xmlNode*>(attr));
}

} //namespace xmlpp
<commit_msg>Removal of G_STRFUNC preprocessor macro<commit_after>/* node.cc
 * libxml++ and this file are copyright (C) 2000 by Ari Johnson, and
 * are covered by the GNU Lesser General Public License, which should be
 * included with libxml++ as the file COPYING.
 */

#include <libxml++/nodes/element.h>
#include <libxml++/nodes/node.h>
#include <libxml++/nodes/entityreference.h>
#include <libxml++/nodes/textnode.h>
#include <libxml++/nodes/commentnode.h>
#include <libxml++/nodes/cdatanode.h>
#include <libxml++/nodes/processinginstructionnode.h>
#include <libxml++/exceptions/internal_error.h>
#include <libxml++/document.h>
#include <libxml/xpath.h>
#include <libxml/xpathInternals.h>
#include <libxml/tree.h>

#include <iostream>

namespace xmlpp
{

Node::Node(xmlNode* node)
  : impl_(node)
{
   impl_->_private = this;
}

Node::~Node()
{}

const Element* Node::get_parent() const
{
  return const_cast<Node*>(this)->get_parent();
}

Element* Node::get_parent()
{
  if(!(cobj()->parent && cobj()->parent->type == XML_ELEMENT_NODE))
    return 0;

  Node::create_wrapper(cobj()->parent);
  return static_cast<Element*>(cobj()->parent->_private);
}

const Node* Node::get_next_sibling() const
{
  return const_cast<Node*>(this)->get_next_sibling();
}

Node* Node::get_next_sibling()
{
  if(!cobj()->next)
    return 0;

  Node::create_wrapper(cobj()->next);
  return static_cast<Node*>(cobj()->next->_private);
}

const Node* Node::get_previous_sibling() const
{
  return const_cast<Node*>(this)->get_previous_sibling();
}

Node* Node::get_previous_sibling()
{
  if(!cobj()->prev)
    return 0;

  Node::create_wrapper(cobj()->prev);
  return static_cast<Node*>(cobj()->prev->_private);
}

Node::NodeList Node::get_children(const xmlpp::string& name)
{
   xmlNode* child = impl_->children;
   if(!child)
     return NodeList();

   NodeList children;
   do
   {
      if(name.empty() || name == (const char*)child->name)
      {
        Node::create_wrapper(child);
        children.push_back(reinterpret_cast<Node*>(child->_private));
      }
   }
   while((child = child->next));

   return children;
}

const Node::NodeList Node::get_children(const xmlpp::string& name) const
{
  return const_cast<Node*>(this)->get_children(name);
}

Element* Node::add_child(const xmlpp::string& name,
                         const xmlpp::string& ns_prefix)
{
  _xmlNode* child = create_new_child_node(name, ns_prefix);
  if(!child)
    return 0;

  _xmlNode* node = xmlAddChild(impl_, child);
  if(!node)
    return 0;

  Node::create_wrapper(node);
  return static_cast<Element*>(node->_private);
}

Element* Node::add_child(xmlpp::Node* previous_sibling,
                         const xmlpp::string& name,
                         const xmlpp::string& ns_prefix)
{
  if(!previous_sibling)
    return 0;

  _xmlNode* child = create_new_child_node(name, ns_prefix);
  if(!child)
    return 0;

  _xmlNode* node = xmlAddNextSibling(previous_sibling->cobj(), child);
  if(!node)
    return 0;

  Node::create_wrapper(node);
  return static_cast<Element*>(node->_private);
}

Element* Node::add_child_before(xmlpp::Node* next_sibling,
                         const xmlpp::string& name,
                         const xmlpp::string& ns_prefix)
{
  if(!next_sibling)
    return 0;

  _xmlNode* child = create_new_child_node(name, ns_prefix);
  if(!child)
    return 0;

  _xmlNode* node = xmlAddPrevSibling(next_sibling->cobj(), child);
  if(!node)
    return 0;

  Node::create_wrapper(node);
  return static_cast<Element*>(node->_private);
}

_xmlNode* Node::create_new_child_node(const xmlpp::string& name, const xmlpp::string& ns_prefix)
{
   xmlNs* ns = 0;

   if(impl_->type != XML_ELEMENT_NODE)
   {
      #ifdef LIBXMLCPP_EXCEPTIONS_ENABLED
      throw internal_error("You can only add child nodes to element nodes");
      #else
      return 0;
      #endif //LIBXMLCPP_EXCEPTIONS_ENABLED
   }

   //Ignore the namespace if none was specified:
   if(!ns_prefix.empty())
   {
     //Use the existing namespace if one exists:
     ns = xmlSearchNs(impl_->doc, impl_, (const xmlChar*)ns_prefix.c_str());
     if (!ns)
     {
       #ifdef LIBXMLCPP_EXCEPTIONS_ENABLED
       throw exception("The namespace prefix (" + ns_prefix + ") has not been declared.");
       #else
       return 0;
       #endif //LIBXMLCPP_EXCEPTIONS_ENABLED
     }
   }

   return xmlNewNode(ns, (const xmlChar*)name.c_str());
}

void Node::remove_child(Node* node)
{
  //TODO: Allow a node to be removed without deleting it, to allow it to be moved?
  //This would require a more complex memory management API.

  xmlNode* cnode = node->cobj();
  Node::free_wrappers(cnode); //This delete the C++ node (not this) itself.
  xmlUnlinkNode(cnode);
  xmlFreeNode(cnode);
}

Node* Node::import_node(const Node* node, bool recursive)
{
  //Create the node, by copying:
  xmlNode* imported_node = xmlDocCopyNode(const_cast<xmlNode*>(node->cobj()), impl_->doc, recursive);
  if (!imported_node)
  {
    #ifdef LIBXMLCPP_EXCEPTIONS_ENABLED
    throw exception("Unable to import node");
    #else
    return 0;
    #endif //LIBXMLCPP_EXCEPTIONS_ENABLED
  }

  //Add the node:
  xmlNode* added_node = xmlAddChild(this->cobj(),imported_node);
  if (!added_node)
  {
    Node::free_wrappers(imported_node);
    xmlFreeNode(imported_node);

    #ifdef LIBXMLCPP_EXCEPTIONS_ENABLED
    throw exception("Unable to add imported node to current node");
    #else
    return 0;
    #endif //LIBXMLCPP_EXCEPTIONS_ENABLED
  }

  Node::create_wrapper(imported_node);
  return static_cast<Node*>(imported_node->_private);
}

xmlpp::string Node::get_name() const
{
  return impl_->name ? (const char*)impl_->name : "";
}

void Node::set_name(const xmlpp::string& name)
{
  xmlNodeSetName( impl_, (const xmlChar *)name.c_str() );
}

int Node::get_line() const
{
   return XML_GET_LINE(impl_);
}

xmlNode* Node::cobj()
{
  return impl_;
}

const xmlNode* Node::cobj() const
{
  return impl_;
}

xmlpp::string Node::get_path() const
{
  xmlChar* path = xmlGetNodePath(impl_);
  xmlpp::string retn = path ? (char*)path : "";
  xmlFree(path);
  return retn;
}

static NodeSet find_impl(xmlXPathContext* ctxt, const xmlpp::string& xpath)
{
  xmlXPathObject* result = xmlXPathEval((const xmlChar*)xpath.c_str(), ctxt);

  if(!result)
  {
    xmlXPathFreeContext(ctxt);

    #ifdef LIBXMLCPP_EXCEPTIONS_ENABLED
    throw exception("Invalid XPath: " + xpath);
    #else
    return NodeSet();
    #endif //LIBXMLCPP_EXCEPTIONS_ENABLED
  }

  if(result->type != XPATH_NODESET)
  {
    xmlXPathFreeObject(result);
    xmlXPathFreeContext(ctxt);

    #ifdef LIBXMLCPP_EXCEPTIONS_ENABLE
    throw internal_error("Only nodeset result types are supported.");
    #else
    return NodeSet();
    #endif //LIBXMLCPP_EXCEPTIONS_ENABLED
  }

  xmlNodeSet* nodeset = result->nodesetval;
  NodeSet nodes;
  if( nodeset && !xmlXPathNodeSetIsEmpty(nodeset))
  {
    const int count = xmlXPathNodeSetGetLength(nodeset);
    nodes.reserve(count);
    for (int i = 0; i != count; ++i)
    {
      xmlNode* cnode = xmlXPathNodeSetItem(nodeset, i);
      if(cnode->type == XML_NAMESPACE_DECL)
      {
        //In this case we would cast it to a xmlNs*,
        //but this C++ method only returns Nodes.
        std::cerr << "Node::find_impl: ignoring an xmlNs object." << std::endl;
        continue;
      }

      //TODO: Check for other cnode->type values?

      Node::create_wrapper(cnode);
      Node* cppNode = static_cast<Node*>(cnode->_private);
      nodes.push_back(cppNode);
    }
  }
  else
  {
    // return empty set
  }

  xmlXPathFreeObject(result);
  xmlXPathFreeContext(ctxt);

  return nodes;
}

NodeSet Node::find(const xmlpp::string& xpath) const
{
  xmlXPathContext* ctxt = xmlXPathNewContext(impl_->doc);
  ctxt->node = impl_;

  return find_impl(ctxt, xpath);
}

NodeSet Node::find(const xmlpp::string& xpath,
		   const PrefixNsMap& namespaces) const
{
  xmlXPathContext* ctxt = xmlXPathNewContext(impl_->doc);
  ctxt->node = impl_;

  for (PrefixNsMap::const_iterator it=namespaces.begin();
       it != namespaces.end(); it++)
    xmlXPathRegisterNs(ctxt,
		       reinterpret_cast<const xmlChar*>(it->first.c_str()),
		       reinterpret_cast<const xmlChar*>(it->second.c_str()));

  return find_impl(ctxt, xpath);
}

xmlpp::string Node::get_namespace_prefix() const
{
  if(impl_->type == XML_DOCUMENT_NODE)
  {
    //impl_ is actually of type xmlDoc, instead of just xmlNode.
    //libxml does not always use GObject-style inheritance, so xmlDoc does not have all the same struct fields as xmlNode.
    //Therefore, a call to impl_->ns would be invalid.
    //This can be an issue when calling this method on a Node returned by Node::find().
    //See the TODO comment on Document, suggesting that Document should derived from Node.

    return xmlpp::string();
  }

  if(impl_ && impl_->ns && impl_->ns->prefix)
    return (char*)impl_->ns->prefix;
  else
    return xmlpp::string();
}

xmlpp::string Node::get_namespace_uri() const
{
  if(impl_->type == XML_DOCUMENT_NODE)
  {
    //impl_ is actually of type xmlDoc, instead of just xmlNode.
    //libxml does not always use GObject-style inheritance, so xmlDoc does not have all the same struct fields as xmlNode.
    //Therefore, a call to impl_->ns would be invalid.
    //This can be an issue when calling this method on a Node returned by Node::find().
    //See the TODO comment on Document, suggesting that Document should derived from Node.

    return xmlpp::string();
  }

  if(impl_ && impl_->ns && impl_->ns->href)
    return (char*)impl_->ns->href;
  else
    return xmlpp::string();
}

void Node::set_namespace(const xmlpp::string& ns_prefix)
{
  //Look for the existing namespace to use:
  xmlNs* ns = xmlSearchNs( cobj()->doc, cobj(), (xmlChar*)(ns_prefix.empty() ? 0 : ns_prefix.c_str()) );
  if(ns)
  {
      //Use it for this element:
      xmlSetNs(cobj(), ns);
  }
  else
  {
    #ifdef LIBXMLCPP_EXCEPTIONS_ENABLE
    throw exception("The namespace (" + ns_prefix + ") has not been declared.");
    #endif //LIBXMLCPP_EXCEPTIONS_ENABLE
  }
}

void Node::create_wrapper(xmlNode* node)
{
  if(node->_private)
  {
	  //Node already wrapped, skip
	  return;
  }

  switch (node->type)
  {
    case XML_ELEMENT_NODE:
    {
      node->_private = new xmlpp::Element(node);
      break;
    }
    case XML_ATTRIBUTE_NODE:
    {
      node->_private = new xmlpp::Attribute(node);
      break;
    }
    case XML_TEXT_NODE:
    {
      node->_private = new xmlpp::TextNode(node);
      break;
    }
    case XML_COMMENT_NODE:
    {
      node->_private = new xmlpp::CommentNode(node);
      break;
    }
    case XML_CDATA_SECTION_NODE:
    {
      node->_private = new xmlpp::CdataNode(node);
      break;
    }
    case XML_PI_NODE:
    {
      node->_private = new xmlpp::ProcessingInstructionNode(node);
      break;
    }
    case XML_DTD_NODE:
    {
      node->_private = new xmlpp::Dtd(reinterpret_cast<xmlDtd*>(node));
      break;
    }
    //case XML_ENTITY_NODE:
    //{
    //  assert(0 && "Warning: XML_ENTITY_NODE not implemented");
    //  //node->_private = new xmlpp::ProcessingInstructionNode(node);
    //  break;
    //}
    case XML_ENTITY_REF_NODE:
    {
      node->_private = new xmlpp::EntityReference(node);
      break;
    }
    case XML_DOCUMENT_NODE:
    {
      // do nothing. For Documents it's the wrapper that is the owner.
      break;
    }
    default:
    {
      // good default for release versions
      node->_private = new xmlpp::Node(node);
      std::cerr << "Warning: new node of unknown type created: " << node->type << std::endl;
      break;
    }
  }
}

void Node::free_wrappers(xmlNode* node)
{
  if(!node)
    return;

  //Walk the children list
  for(xmlNode* child=node->children; child; child=child->next)
    free_wrappers(child);

  //Delete the local one
  switch(node->type)
  {
    //Node types that have no properties
    case XML_DTD_NODE:
      delete static_cast<Dtd*>(node->_private);
      node->_private = 0;
      return;
    case XML_ATTRIBUTE_NODE:
    case XML_ELEMENT_DECL:
    case XML_ATTRIBUTE_DECL:
    case XML_ENTITY_DECL:
      delete static_cast<Node*>(node->_private);
      node->_private = 0;
      return;
    case XML_DOCUMENT_NODE:
      //Do not free now. The Document is usually the one who owns the caller.
      return;
    default:
      delete static_cast<Node*>(node->_private);
      node->_private = 0;
      break;
  }

  //Walk the attributes list.
  //Note that some "derived" struct have a different layout, so
  //_xmlNode::properties would be a nonsense value, leading to crashes,
  //(and shown as valgrind warnings), so we return above, to avoid
  //checking it here.
  for(xmlAttr* attr = node->properties; attr; attr = attr->next)
    free_wrappers(reinterpret_cast<xmlNode*>(attr));
}

} //namespace xmlpp
<|endoftext|>


<commit_before>/***********************************************************************************
** MIT License                                                                    **
**                                                                                **
** Copyright (c) 2018 Victor DENIS (victordenis01@gmail.com)                      **
**                                                                                **
** Permission is hereby granted, free of charge, to any person obtaining a copy   **
** of this software and associated documentation files (the "Software"), to deal  **
** in the Software without restriction, including without limitation the rights   **
** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell      **
** copies of the Software, and to permit persons to whom the Software is          **
** furnished to do so, subject to the following conditions:                       **
**                                                                                **
** The above copyright notice and this permission notice shall be included in all **
** copies or substantial portions of the Software.                                **
**                                                                                **
** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR     **
** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,       **
** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE    **
** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER         **
** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,  **
** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE  **
** SOFTWARE.                                                                      **
***********************************************************************************/

#include "Widgets/Preferences/GeneralPage.hpp"

#include <QMessageBox>

#include <QFileInfo>
#include <QDir>

#include "Utils/DataPaths.hpp"
#include "Utils/Settings.hpp"

#include "Application.hpp"
#include "BrowserWindow.hpp"

namespace Sn {

GeneralPage::GeneralPage(QWidget* parent) :
		QWidget(parent)
{
	setAttribute(Qt::WA_DeleteOnClose);

	setupUI();

	loadSettings();

	connect(m_radioHPBlank, &QRadioButton::toggled, this, &GeneralPage::homePageActionChanged);

	connect(m_radioNTOpenCutomUrl, &QRadioButton::toggled, this, &GeneralPage::newTabActionChanged);

	connect(m_radioNSOpenSaved, &QRadioButton::toggled, this, &GeneralPage::startupActionChanged);
	connect(m_btnSaveCurrentSession, &QPushButton::clicked, this, &GeneralPage::saveCurrentSession);

};

GeneralPage::~GeneralPage()
{
	// Empty
}

void GeneralPage::loadSettings()
{
	Settings settings{};

	// Set new tab and new window option
	settings.beginGroup("Web-Settings");

	QUrl homePageUrl{settings.value(QLatin1String("homePage"), QUrl("https://doosearch.sielo.app/")).toUrl()};

	if (homePageUrl == QUrl("about:blank")) {
		m_radioHPBlank->setChecked(true);
		m_homePageUrl->setText("https://doosearch.sielo.app/");
		m_homePageUrl->setEnabled(false);
	}
	else {
		m_radioHPCustomUrl->setChecked(true);
		m_homePageUrl->setText(homePageUrl.toString());
	}

	QUrl newTabUrl{settings.value(QLatin1String("urlOnNewTab"), QUrl("https://doosearch.sielo.app/")).toUrl()};

	if (newTabUrl == QUrl("about:blank")) {
		m_radioNTOpenBlankPage->setChecked(true);
		m_newTabUrl->setText("https://www.google.com");
		m_newTabUrl->setEnabled(false);
	}
	else if (newTabUrl == homePageUrl) {
		m_radioNTOpenHomePage->setChecked(true);
		m_newTabUrl->setText(homePageUrl.toString());
		m_newTabUrl->setEnabled(false);
	}
	else {
		m_radioNTOpenCutomUrl->setChecked(true);
		m_newTabUrl->setText(newTabUrl.toString());
	}

	m_dontLoadTabsUntilSelect->setChecked(settings.value(QLatin1String("LoadTabsOnActivation"), true).toBool());

	settings.endGroup();

	// Set what to do on new session
	settings.beginGroup("Settings");

	Application::AfterLaunch afterLaunch = static_cast<Application::AfterLaunch>(settings.value(
			QLatin1String("afterLaunch"), Application::OpenHomePage).toInt());
	if (afterLaunch == Application::OpenBlankPage)
		m_radioNSOpenBlankPage->setChecked(true);
	else if (afterLaunch == Application::OpenHomePage)
		m_radioNSOpenHomePage->setChecked(true);
	else if (afterLaunch == Application::RestoreSession)
		m_radioNSRestoreSession->setChecked(true);
	else if (afterLaunch == Application::OpenSavedSession)
		m_radioNSOpenSaved->setChecked(true);

	m_btnSaveCurrentSession->setEnabled(afterLaunch == Application::OpenSavedSession);

	settings.endGroup();

	// Set language
	settings.beginGroup("Language");

	QString activeLanguage = Application::instance()->currentLanguage();

	if (!activeLanguage.isEmpty() && activeLanguage != QLatin1String("en_US"))
		m_languages->addItem(createLanguageItem(activeLanguage), activeLanguage);

	m_languages->addItem("English (en_US)");

	const QString translationPath = DataPaths::currentProfilePath() + "locale";
	QDir lanDir{translationPath};
	QStringList list = lanDir.entryList(QStringList("*.qm"));

			foreach (const QString& name, list) {
			if (name.startsWith(QLatin1String("qt_")))
				continue;

			QString loc{name};
			loc.remove(QLatin1String(".qm"));

			if (loc == activeLanguage)
				continue;

			m_languages->addItem(createLanguageItem(loc), loc);
		}

}

void GeneralPage::save()
{

	Settings settings{};

	// Save what to do on new session
	settings.beginGroup("Settings");

	if (m_radioNSOpenBlankPage->isChecked())
		settings.setValue(QLatin1String("afterLaunch"), Application::OpenBlankPage);
	else if (m_radioNSOpenHomePage->isChecked())
		settings.setValue(QLatin1String("afterLaunch"), Application::OpenHomePage);
	else if (m_radioNSRestoreSession->isChecked())
		settings.setValue(QLatin1String("afterLaunch"), Application::RestoreSession);
	else
		settings.setValue(QLatin1String("afterLaunch"), Application::OpenSavedSession);

	settings.endGroup();

	// Save what to do on new tab and new window
	settings.beginGroup("Web-Settings");

	settings.setValue(QLatin1String("homePage"),
					  m_radioHPBlank->isChecked() ? QUrl("about:blank") : QUrl(m_homePageUrl->text()));

	if (m_radioNTOpenBlankPage->isChecked()) {
		settings.setValue(QLatin1String("urlOnNewTab"), QUrl("about:blank"));
	}
	else if (m_radioNTOpenHomePage->isChecked()) {
		settings.setValue(QLatin1String("urlOnNewTab"),
						  m_radioHPBlank->isChecked() ? QUrl("about:blank") : QUrl(m_homePageUrl->text()));
	}
	else {
		settings.setValue(QLatin1String("urlOnNewTab"), QUrl(m_newTabUrl->text()));
	}

	// Save if we want to load tabs at startup
	settings.setValue(QLatin1String("LoadTabsOnActivation"), m_dontLoadTabsUntilSelect->isChecked());

	settings.endGroup();

	// Save language
	settings.beginGroup("Language");

	if (Application::instance()->currentLanguage() != m_languages->itemData(m_languages->currentIndex()).toString())
		QMessageBox::warning(this, tr("Warning"), tr("Some changes need Sielo restart to have effects"));

	settings.setValue("language", m_languages->itemData(m_languages->currentIndex()).toString());

	settings.endGroup();
}

void GeneralPage::homePageActionChanged(bool enabled)
{
	if (enabled)
		m_homePageUrl->setEnabled(false);
	else
		m_homePageUrl->setEnabled(true);
}

void GeneralPage::newTabActionChanged(bool enabled)
{
	if (enabled)
		m_newTabUrl->setEnabled(true);
	else
		m_newTabUrl->setEnabled(false);
}

void GeneralPage::startupActionChanged(bool isOpenSaved)
{
	m_btnSaveCurrentSession->setEnabled(isOpenSaved);

	if (!QFileInfo(DataPaths::currentProfilePath() + QLatin1String("/home-session.dat")).exists()
		&& isOpenSaved) {
		saveCurrentSession();
	}
}

void GeneralPage::saveCurrentSession()
{
	Application::instance()->saveSession(true);

	QMessageBox::information(this, tr("Saved"), tr("Your session will be restored at startup"));
}

void GeneralPage::setupUI()
{
	m_layout = new QVBoxLayout(this);
	m_layoutGroupHomePage = new QVBoxLayout();
	m_layoutGroupNewTab = new QVBoxLayout();
	m_layoutGroupNewSession = new QVBoxLayout();
	m_layoutGroupLanguage = new QVBoxLayout();

	m_groupHomePage = new QGroupBox(tr("Home page"), this);

	m_radioHPBlank = new QRadioButton(tr("Open blank page"), m_groupHomePage);
	m_radioHPCustomUrl = new QRadioButton(tr("Open custom url"), m_groupHomePage);

	m_radioHPCustomUrl->setChecked(true);

	m_homePageUrl = new QLineEdit(m_groupHomePage);
	m_homePageUrl->setPlaceholderText(tr("Home page url"));

	m_groupNewTab = new QGroupBox(tr("New tab"), this);

	m_radioNTOpenBlankPage = new QRadioButton(tr("Open blank page"), m_groupNewTab);
	m_radioNTOpenHomePage = new QRadioButton(tr("Open home page"), m_groupNewTab);
	m_radioNTOpenCutomUrl = new QRadioButton(tr("Open custom url"), m_groupNewTab);

	m_radioNTOpenHomePage->setChecked(true);

	m_newTabUrl = new QLineEdit(m_groupNewTab);
	m_newTabUrl->setPlaceholderText(tr("New tab url"));

	m_groupNewSession = new QGroupBox(tr("New Session"), this);

	m_radioNSOpenBlankPage = new QRadioButton(tr("Open blank page"), m_groupNewSession);
	m_radioNSOpenHomePage = new QRadioButton(tr("Open home page"), m_groupNewSession);
	m_radioNSRestoreSession = new QRadioButton(tr("Restore session"), m_groupNewSession);
	m_radioNSOpenSaved = new QRadioButton(tr("Open saved session"), m_groupNewSession);

	m_radioNSOpenHomePage->setChecked(true);

	m_btnSaveCurrentSession = new QPushButton(tr("Save current session"), m_groupNewSession);

	m_groupLanguage = new QGroupBox(tr("Language"), this);

	m_descLanguage = new QLabel(tr("Available translations:"), m_groupLanguage);
	m_languages = new QComboBox(m_groupLanguage);
	m_descLanguageNeedRestart = new QLabel(tr("In order to change language, you must restart your browser."),
										   m_groupLanguage);

	m_dontLoadTabsUntilSelect = new QCheckBox(tr("Don't load tabs until selected"), this);

	m_groupHomePage->setLayout(m_layoutGroupHomePage);

	m_layoutGroupHomePage->addWidget(m_radioHPBlank);
	m_layoutGroupHomePage->addWidget(m_radioHPCustomUrl);
	m_layoutGroupHomePage->addWidget(m_homePageUrl);

	m_groupNewTab->setLayout(m_layoutGroupNewTab);

	m_layoutGroupNewTab->addWidget(m_radioNTOpenBlankPage);
	m_layoutGroupNewTab->addWidget(m_radioNTOpenHomePage);
	m_layoutGroupNewTab->addWidget(m_radioNTOpenCutomUrl);
	m_layoutGroupNewTab->addWidget(m_newTabUrl);

	m_groupNewSession->setLayout(m_layoutGroupNewSession);

	m_layoutGroupNewSession->addWidget(m_radioNSOpenBlankPage);
	m_layoutGroupNewSession->addWidget(m_radioNSOpenHomePage);
	m_layoutGroupNewSession->addWidget(m_radioNSRestoreSession);
	m_layoutGroupNewSession->addWidget(m_radioNSOpenSaved);
	m_layoutGroupNewSession->addWidget(m_btnSaveCurrentSession);

	m_groupLanguage->setLayout(m_layoutGroupLanguage);

	m_layoutGroupLanguage->addWidget(m_descLanguage);
	m_layoutGroupLanguage->addWidget(m_languages);
	m_layoutGroupLanguage->addWidget(m_descLanguageNeedRestart);

	m_layout->addWidget(m_groupHomePage);
	m_layout->addWidget(m_groupNewTab);
	m_layout->addWidget(m_groupNewSession);
	m_layout->addWidget(m_groupLanguage);
	m_layout->addWidget(m_dontLoadTabsUntilSelect);
}

QString GeneralPage::createLanguageItem(const QString& lang)
{
	QLocale locale{lang};

	if (locale.language() == QLocale::C)
		return lang;

	QString country{QLocale::countryToString(locale.country())};
	QString language{QLocale::languageToString(locale.language())};

	return QString("%1, %2 (%3)").arg(language, country, lang);
}

}<commit_msg>[Fix] Language dialog issue in preferences with en_US<commit_after>/***********************************************************************************
** MIT License                                                                    **
**                                                                                **
** Copyright (c) 2018 Victor DENIS (victordenis01@gmail.com)                      **
**                                                                                **
** Permission is hereby granted, free of charge, to any person obtaining a copy   **
** of this software and associated documentation files (the "Software"), to deal  **
** in the Software without restriction, including without limitation the rights   **
** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell      **
** copies of the Software, and to permit persons to whom the Software is          **
** furnished to do so, subject to the following conditions:                       **
**                                                                                **
** The above copyright notice and this permission notice shall be included in all **
** copies or substantial portions of the Software.                                **
**                                                                                **
** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR     **
** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,       **
** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE    **
** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER         **
** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,  **
** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE  **
** SOFTWARE.                                                                      **
***********************************************************************************/

#include "Widgets/Preferences/GeneralPage.hpp"

#include <QMessageBox>

#include <QFileInfo>
#include <QDir>

#include "Utils/DataPaths.hpp"
#include "Utils/Settings.hpp"

#include "Application.hpp"
#include "BrowserWindow.hpp"

namespace Sn {

GeneralPage::GeneralPage(QWidget* parent) :
		QWidget(parent)
{
	setAttribute(Qt::WA_DeleteOnClose);

	setupUI();

	loadSettings();

	connect(m_radioHPBlank, &QRadioButton::toggled, this, &GeneralPage::homePageActionChanged);

	connect(m_radioNTOpenCutomUrl, &QRadioButton::toggled, this, &GeneralPage::newTabActionChanged);

	connect(m_radioNSOpenSaved, &QRadioButton::toggled, this, &GeneralPage::startupActionChanged);
	connect(m_btnSaveCurrentSession, &QPushButton::clicked, this, &GeneralPage::saveCurrentSession);

};

GeneralPage::~GeneralPage()
{
	// Empty
}

void GeneralPage::loadSettings()
{
	Settings settings{};

	// Set new tab and new window option
	settings.beginGroup("Web-Settings");

	QUrl homePageUrl{settings.value(QLatin1String("homePage"), QUrl("https://doosearch.sielo.app/")).toUrl()};

	if (homePageUrl == QUrl("about:blank")) {
		m_radioHPBlank->setChecked(true);
		m_homePageUrl->setText("https://doosearch.sielo.app/");
		m_homePageUrl->setEnabled(false);
	}
	else {
		m_radioHPCustomUrl->setChecked(true);
		m_homePageUrl->setText(homePageUrl.toString());
	}

	QUrl newTabUrl{settings.value(QLatin1String("urlOnNewTab"), QUrl("https://doosearch.sielo.app/")).toUrl()};

	if (newTabUrl == QUrl("about:blank")) {
		m_radioNTOpenBlankPage->setChecked(true);
		m_newTabUrl->setText("https://www.google.com");
		m_newTabUrl->setEnabled(false);
	}
	else if (newTabUrl == homePageUrl) {
		m_radioNTOpenHomePage->setChecked(true);
		m_newTabUrl->setText(homePageUrl.toString());
		m_newTabUrl->setEnabled(false);
	}
	else {
		m_radioNTOpenCutomUrl->setChecked(true);
		m_newTabUrl->setText(newTabUrl.toString());
	}

	m_dontLoadTabsUntilSelect->setChecked(settings.value(QLatin1String("LoadTabsOnActivation"), true).toBool());

	settings.endGroup();

	// Set what to do on new session
	settings.beginGroup("Settings");

	Application::AfterLaunch afterLaunch = static_cast<Application::AfterLaunch>(settings.value(
			QLatin1String("afterLaunch"), Application::OpenHomePage).toInt());
	if (afterLaunch == Application::OpenBlankPage)
		m_radioNSOpenBlankPage->setChecked(true);
	else if (afterLaunch == Application::OpenHomePage)
		m_radioNSOpenHomePage->setChecked(true);
	else if (afterLaunch == Application::RestoreSession)
		m_radioNSRestoreSession->setChecked(true);
	else if (afterLaunch == Application::OpenSavedSession)
		m_radioNSOpenSaved->setChecked(true);

	m_btnSaveCurrentSession->setEnabled(afterLaunch == Application::OpenSavedSession);

	settings.endGroup();

	// Set language
	settings.beginGroup("Language");

	QString activeLanguage = Application::instance()->currentLanguage();

	if (!activeLanguage.isEmpty() && activeLanguage != QLatin1String("en_US"))
		m_languages->addItem(createLanguageItem(activeLanguage), activeLanguage);

	m_languages->addItem("English (en_US)", "en_US");

	const QString translationPath = DataPaths::currentProfilePath() + "locale";
	QDir lanDir{translationPath};
	QStringList list = lanDir.entryList(QStringList("*.qm"));

			foreach (const QString& name, list) {
			if (name.startsWith(QLatin1String("qt_")))
				continue;

			QString loc{name};
			loc.remove(QLatin1String(".qm"));

			if (loc == activeLanguage)
				continue;

			m_languages->addItem(createLanguageItem(loc), loc);
		}

}

void GeneralPage::save()
{

	Settings settings{};

	// Save what to do on new session
	settings.beginGroup("Settings");

	if (m_radioNSOpenBlankPage->isChecked())
		settings.setValue(QLatin1String("afterLaunch"), Application::OpenBlankPage);
	else if (m_radioNSOpenHomePage->isChecked())
		settings.setValue(QLatin1String("afterLaunch"), Application::OpenHomePage);
	else if (m_radioNSRestoreSession->isChecked())
		settings.setValue(QLatin1String("afterLaunch"), Application::RestoreSession);
	else
		settings.setValue(QLatin1String("afterLaunch"), Application::OpenSavedSession);

	settings.endGroup();

	// Save what to do on new tab and new window
	settings.beginGroup("Web-Settings");

	settings.setValue(QLatin1String("homePage"),
					  m_radioHPBlank->isChecked() ? QUrl("about:blank") : QUrl(m_homePageUrl->text()));

	if (m_radioNTOpenBlankPage->isChecked()) {
		settings.setValue(QLatin1String("urlOnNewTab"), QUrl("about:blank"));
	}
	else if (m_radioNTOpenHomePage->isChecked()) {
		settings.setValue(QLatin1String("urlOnNewTab"),
						  m_radioHPBlank->isChecked() ? QUrl("about:blank") : QUrl(m_homePageUrl->text()));
	}
	else {
		settings.setValue(QLatin1String("urlOnNewTab"), QUrl(m_newTabUrl->text()));
	}

	// Save if we want to load tabs at startup
	settings.setValue(QLatin1String("LoadTabsOnActivation"), m_dontLoadTabsUntilSelect->isChecked());

	settings.endGroup();

	// Save language
	settings.beginGroup("Language");

	QString selectedLanguage{m_languages->itemData(m_languages->currentIndex()).toString()};

	if (Application::instance()->currentLanguage() != selectedLanguage)
		QMessageBox::warning(this, tr("Warning"), tr("Some changes need Sielo restart to have effects"));

	settings.setValue("language", m_languages->itemData(m_languages->currentIndex()).toString());

	settings.endGroup();
}

void GeneralPage::homePageActionChanged(bool enabled)
{
	if (enabled)
		m_homePageUrl->setEnabled(false);
	else
		m_homePageUrl->setEnabled(true);
}

void GeneralPage::newTabActionChanged(bool enabled)
{
	if (enabled)
		m_newTabUrl->setEnabled(true);
	else
		m_newTabUrl->setEnabled(false);
}

void GeneralPage::startupActionChanged(bool isOpenSaved)
{
	m_btnSaveCurrentSession->setEnabled(isOpenSaved);

	if (!QFileInfo(DataPaths::currentProfilePath() + QLatin1String("/home-session.dat")).exists()
		&& isOpenSaved) {
		saveCurrentSession();
	}
}

void GeneralPage::saveCurrentSession()
{
	Application::instance()->saveSession(true);

	QMessageBox::information(this, tr("Saved"), tr("Your session will be restored at startup"));
}

void GeneralPage::setupUI()
{
	m_layout = new QVBoxLayout(this);
	m_layoutGroupHomePage = new QVBoxLayout();
	m_layoutGroupNewTab = new QVBoxLayout();
	m_layoutGroupNewSession = new QVBoxLayout();
	m_layoutGroupLanguage = new QVBoxLayout();

	m_groupHomePage = new QGroupBox(tr("Home page"), this);

	m_radioHPBlank = new QRadioButton(tr("Open blank page"), m_groupHomePage);
	m_radioHPCustomUrl = new QRadioButton(tr("Open custom url"), m_groupHomePage);

	m_radioHPCustomUrl->setChecked(true);

	m_homePageUrl = new QLineEdit(m_groupHomePage);
	m_homePageUrl->setPlaceholderText(tr("Home page url"));

	m_groupNewTab = new QGroupBox(tr("New tab"), this);

	m_radioNTOpenBlankPage = new QRadioButton(tr("Open blank page"), m_groupNewTab);
	m_radioNTOpenHomePage = new QRadioButton(tr("Open home page"), m_groupNewTab);
	m_radioNTOpenCutomUrl = new QRadioButton(tr("Open custom url"), m_groupNewTab);

	m_radioNTOpenHomePage->setChecked(true);

	m_newTabUrl = new QLineEdit(m_groupNewTab);
	m_newTabUrl->setPlaceholderText(tr("New tab url"));

	m_groupNewSession = new QGroupBox(tr("New Session"), this);

	m_radioNSOpenBlankPage = new QRadioButton(tr("Open blank page"), m_groupNewSession);
	m_radioNSOpenHomePage = new QRadioButton(tr("Open home page"), m_groupNewSession);
	m_radioNSRestoreSession = new QRadioButton(tr("Restore session"), m_groupNewSession);
	m_radioNSOpenSaved = new QRadioButton(tr("Open saved session"), m_groupNewSession);

	m_radioNSOpenHomePage->setChecked(true);

	m_btnSaveCurrentSession = new QPushButton(tr("Save current session"), m_groupNewSession);

	m_groupLanguage = new QGroupBox(tr("Language"), this);

	m_descLanguage = new QLabel(tr("Available translations:"), m_groupLanguage);
	m_languages = new QComboBox(m_groupLanguage);
	m_descLanguageNeedRestart = new QLabel(tr("In order to change language, you must restart your browser."),
										   m_groupLanguage);

	m_dontLoadTabsUntilSelect = new QCheckBox(tr("Don't load tabs until selected"), this);

	m_groupHomePage->setLayout(m_layoutGroupHomePage);

	m_layoutGroupHomePage->addWidget(m_radioHPBlank);
	m_layoutGroupHomePage->addWidget(m_radioHPCustomUrl);
	m_layoutGroupHomePage->addWidget(m_homePageUrl);

	m_groupNewTab->setLayout(m_layoutGroupNewTab);

	m_layoutGroupNewTab->addWidget(m_radioNTOpenBlankPage);
	m_layoutGroupNewTab->addWidget(m_radioNTOpenHomePage);
	m_layoutGroupNewTab->addWidget(m_radioNTOpenCutomUrl);
	m_layoutGroupNewTab->addWidget(m_newTabUrl);

	m_groupNewSession->setLayout(m_layoutGroupNewSession);

	m_layoutGroupNewSession->addWidget(m_radioNSOpenBlankPage);
	m_layoutGroupNewSession->addWidget(m_radioNSOpenHomePage);
	m_layoutGroupNewSession->addWidget(m_radioNSRestoreSession);
	m_layoutGroupNewSession->addWidget(m_radioNSOpenSaved);
	m_layoutGroupNewSession->addWidget(m_btnSaveCurrentSession);

	m_groupLanguage->setLayout(m_layoutGroupLanguage);

	m_layoutGroupLanguage->addWidget(m_descLanguage);
	m_layoutGroupLanguage->addWidget(m_languages);
	m_layoutGroupLanguage->addWidget(m_descLanguageNeedRestart);

	m_layout->addWidget(m_groupHomePage);
	m_layout->addWidget(m_groupNewTab);
	m_layout->addWidget(m_groupNewSession);
	m_layout->addWidget(m_groupLanguage);
	m_layout->addWidget(m_dontLoadTabsUntilSelect);
}

QString GeneralPage::createLanguageItem(const QString& lang)
{
	QLocale locale{lang};

	if (locale.language() == QLocale::C)
		return lang;

	QString country{QLocale::countryToString(locale.country())};
	QString language{QLocale::languageToString(locale.language())};

	return QString("%1, %2 (%3)").arg(language, country, lang);
}

}<|endoftext|>