<commit_before>/** \file    add_synonyms.cc
 *  \brief   Generic version for augmenting title data with synonyms found
             in the authority data
 *  \author  Johannes Riedl
 */

/*
    Copyright (C) 2016, Library of the University of Tübingen

    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/>.
*/

/*  We offer a list of tags and subfields where the primary data resides along
    with a list of tags and subfields where the synonym data is found and
    a list of unused fields in the title data where the synonyms can be stored
*/

#include <iostream>
#include <map>
#include <vector>
#include <cstdlib>
#include "Compiler.h"
#include "FileUtil.h"
#include "MarcReader.h"
#include "MarcRecord.h"
#include "MarcWriter.h"
#include "MediaTypeUtil.h"
#include "RegexMatcher.h"
#include "StringUtil.h"
#include "Subfields.h"
#include "util.h"

static unsigned modified_count(0);
static unsigned record_count(0);

void Usage() {
    std::cerr << "Usage: " << ::progname << " master_marc_input norm_data_marc_input marc_output\n";
    std::exit(EXIT_FAILURE);
}

std::string GetTag(const std::string &tag_and_subfields_spec) {
    return tag_and_subfields_spec.substr(0, 3);
}

std::string GetSubfieldCodes(const std::string &tag_and_subfields_spec) {
    return tag_and_subfields_spec.substr(3);
}

bool FilterPasses(const MarcRecord &record, const std::map<std::string, std::pair<std::string, std::string>> &filter_specs, const std::string &field_spec) {
      auto filter_spec(filter_specs.find(field_spec));
      if (filter_spec == filter_specs.cend())
          return true;

      auto rule(filter_spec->second);
      // We have field_spec in key and rule to match in value
      std::string subfield_value;
      std::string subfield_codes(GetSubfieldCodes(rule.first));
      if (subfield_codes.length() != 1)
         Error("Invalid subfield specification "  + subfield_codes + " for filter");

      if ((subfield_value = record.extractFirstSubfield(GetTag(rule.first), subfield_codes.c_str()[0])).empty())
          return false;

      return subfield_value == rule.second;
}

void ExtractSynonyms(MarcReader * const authority_reader,
                     const std::set<std::string> &primary_tags_and_subfield_codes,
                     const std::set<std::string> &synonym_tags_and_subfield_codes,
                     std::vector<std::map<std::string, std::string>> * const synonym_maps,
                     const std::map<std::string, std::pair<std::string, std::string>> &filter_spec)
{
    while (const MarcRecord record = authority_reader->read()) {
        std::set<std::string>::const_iterator primary;
        std::set<std::string>::const_iterator synonym;
        unsigned int i(0);
        for (primary = primary_tags_and_subfield_codes.begin(), synonym = synonym_tags_and_subfield_codes.begin();
            primary != primary_tags_and_subfield_codes.end();
            ++primary, ++synonym, ++i)
        {
            // Fill maps with synonyms
            std::vector<std::string> primary_values;
            std::vector<std::string> synonym_values;

            if (FilterPasses(record, filter_spec, *primary) and
                record.extractSubfields(GetTag(*primary), GetSubfieldCodes(*primary), &primary_values) and
                record.extractSubfields(GetTag(*synonym), GetSubfieldCodes(*synonym), &synonym_values))
                    (*synonym_maps)[i].emplace(StringUtil::Join(primary_values, ','),
                                               StringUtil::Join(synonym_values, ','));
        }
    }
}

inline std::string GetMapValueOrEmptyString(const std::map<std::string, std::string> &map,
                                            const std::string &searchterm)
{
    auto value(map.find(searchterm));
    return (value != map.cend()) ? value->second : "";
}

void ProcessRecord(MarcRecord * const record, const std::vector<std::map<std::string, std::string>> &synonym_maps,
                   const std::set<std::string> &primary_tags_and_subfield_codes,
                   const std::set<std::string> &output_tags_and_subfield_codes)
{
    std::set<std::string>::const_iterator primary;
    std::set<std::string>::const_iterator output;
    unsigned int i(0);

    if (primary_tags_and_subfield_codes.size() == output_tags_and_subfield_codes.size()) {
        for (primary = primary_tags_and_subfield_codes.begin(), output = output_tags_and_subfield_codes.begin();
            primary != primary_tags_and_subfield_codes.end();
            ++primary, ++output, ++i)
        {
            std::vector<std::string> primary_values;
            std::set<std::string> synonym_values;
            std::vector<size_t> field_indices;
            if (record->getFieldIndices(GetTag(*primary), &field_indices) != MarcRecord::FIELD_NOT_FOUND) {
                for (auto field_index : field_indices) {
                     primary_values.clear();
                     if (record->getSubfields(field_index).extractSubfields(GetSubfieldCodes(*primary), &primary_values)) {
                        std::string searchterm = StringUtil::Join(primary_values, ',');
                        // First case: Look up synonyms only in one category
                        if (i < synonym_maps.size()) {
                            const auto &synonym_map(synonym_maps[i]);
                            const auto &synonym(GetMapValueOrEmptyString(synonym_map, searchterm));
                            if (not synonym.empty())
                                synonym_values.insert(synonym);
                        }

                        // Second case: Look up synonyms in all categories
                        else {
                            for (auto &sm : synonym_maps) {
                                const auto &synonym(GetMapValueOrEmptyString(sm, searchterm));
                                if (not synonym.empty())
                                   synonym_values.insert(synonym);
                            }
                        }
                    }
                 }

                if (synonym_values.empty())
                     continue;

                const std::string synonyms(StringUtil::Join(synonym_values, ','));

                // Insert synonyms
                // Abort if field is already populated
                std::string tag(GetTag(*output));
                if (record->getFieldIndex(tag) != MarcRecord::FIELD_NOT_FOUND)
                    Error("Field with tag " + tag + " is not empty for PPN " + record->getControlNumber() + '\n');
                std::string subfield_spec = GetSubfieldCodes(*output);
                if (subfield_spec.size() != 1)
                    Error("We currently only support a single subfield and thus specifying " + subfield_spec
                          + " as output subfield is not valid\n");
                Subfields subfields(' ', ' '); // <- indicators must be set explicitly although empty
                subfields.addSubfield(subfield_spec.at(0), synonyms);
                if (not(record->insertField(tag, subfields.toString())))
                    Warning("Could not insert field " + tag + " for PPN " + record->getControlNumber() + '\n');
                ++modified_count;
            }
        }
    }
}

void InsertSynonyms(MarcReader * const marc_reader, MarcWriter * const marc_writer,
                    const std::set<std::string> &primary_tags_and_subfield_codes,
                    const std::set<std::string> &output_tags_and_subfield_codes,
                    std::vector<std::map<std::string, std::string>> &synonym_maps)
{
    while (MarcRecord record = marc_reader->read()) {
        ProcessRecord(&record, synonym_maps, primary_tags_and_subfield_codes, output_tags_and_subfield_codes);
        marc_writer->write(record);
        ++record_count;
    }

    std::cerr << "Modified " << modified_count << " of " << record_count << " record(s).\n";
}

int ParseSpec(std::string spec_str, std::set<std::string> * field_specs, std::map<std::string, std::pair<std::string, std::string>> * filter_specs = nullptr) {
    std::set<std::string>  raw_field_specs;

    if (unlikely(StringUtil::Split(spec_str, ":", &raw_field_specs) < 1)){
        Error("Need at least one field");
        return -1;
    }

    if (filter_specs == nullptr) {
        *field_specs = raw_field_specs;
	return 0;
    }

    // Iterate over all Field-specs and extract possible filters
    static RegexMatcher * const matcher(RegexMatcher::RegexMatcherFactory("(\\d{1,3}[a-z]+)\\[(\\d{1,3}[a-z])=(.*)\\]"));

    for (auto field_spec : raw_field_specs) {
        if (matcher->matched(field_spec)){
            filter_specs->emplace((*matcher)[1], std::make_pair((*matcher)[2], (*matcher)[3]));
            auto bracket = field_spec.find("[");
            field_spec = (bracket != std::string::npos) ? field_spec.erase(bracket, field_spec.length()) : field_spec;
        }
        field_specs->insert(field_spec);
    }
    return 0;
}

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

    if (argc != 4)
        Usage();

    const std::string marc_input_filename(argv[1]);
    const std::string authority_data_marc_input_filename(argv[2]);
    const std::string marc_output_filename(argv[3]);
    if (unlikely(marc_input_filename == marc_output_filename))
        Error("Title data input file name equals output file name!");
    if (unlikely(authority_data_marc_input_filename == marc_output_filename))
        Error("Authority data input file name equals output file name!");

    std::unique_ptr<MarcReader> marc_reader(MarcReader::Factory(marc_input_filename, MarcReader::BINARY));
    std::unique_ptr<MarcReader> authority_reader(MarcReader::Factory(authority_data_marc_input_filename,
                                                                     MarcReader::BINARY));
    std::unique_ptr<MarcWriter> marc_writer(MarcWriter::Factory(marc_output_filename, MarcWriter::BINARY));

    try {
        // Determine possible mappings
        // Values in square brackets specify a positive criterion for values to be taken into account
        const std::string AUTHORITY_DATA_PRIMARY_SPEC("100abcd[079v=piz]:110abcd:111abcd:130abcd:150abcd:151abcd");
        const std::string AUTHORITY_DATA_SYNONYM_SPEC("400abcd:410abcd:411abcd:430abcd:450abcd:451abcd");
        const std::string TITLE_DATA_PRIMARY_SPEC("600abcd:610abcd:611abcd:630abcd:650abcd:651abcd:689abcd");
        const std::string TITLE_DATA_UNUSED_FIELDS_FOR_SYNONYMS("180a:181a:182a:183a:184a:185a:186a");

        // Determine fields to handle
        std::set<std::string> primary_tags_and_subfield_codes;
        std::set<std::string> synonym_tags_and_subfield_codes;
        std::set<std::string> input_tags_and_subfield_codes;
        std::set<std::string> output_tags_and_subfield_codes;

        std::map<std::string, std::pair<std::string, std::string>> filter_specs;

        if (unlikely(ParseSpec(AUTHORITY_DATA_PRIMARY_SPEC, &primary_tags_and_subfield_codes, &filter_specs) < 0))
            Error("Could not properly parse " + AUTHORITY_DATA_PRIMARY_SPEC);

        if (unlikely(StringUtil::Split(AUTHORITY_DATA_SYNONYM_SPEC, ":", &synonym_tags_and_subfield_codes) < 1))
            Error("Need at least one synonym field");

        if (unlikely(StringUtil::Split(TITLE_DATA_PRIMARY_SPEC, ":", &input_tags_and_subfield_codes) < 1))
            Error("Need at least one input field");

        if (unlikely(StringUtil::Split(TITLE_DATA_UNUSED_FIELDS_FOR_SYNONYMS, ":", &output_tags_and_subfield_codes)
                     < 1))
            Error("Need at least one output field");

        unsigned num_of_authority_entries(primary_tags_and_subfield_codes.size());

        if (synonym_tags_and_subfield_codes.size() != num_of_authority_entries)
            Error("Number of authority primary specs must match number of synonym specs");
        if (input_tags_and_subfield_codes.size() != output_tags_and_subfield_codes.size())
            Error("Number of fields title entry specs must match number of output specs");

        std::vector<std::map<std::string, std::string>> synonym_maps(num_of_authority_entries,
                                                                     std::map<std::string, std::string>());

        // Extract the synonyms from authority data
        ExtractSynonyms(authority_reader.get(), primary_tags_and_subfield_codes, synonym_tags_and_subfield_codes,
                        &synonym_maps, filter_specs);

        // Iterate over the title data
        InsertSynonyms(marc_reader.get(), marc_writer.get(), input_tags_and_subfield_codes,
                       output_tags_and_subfield_codes, synonym_maps);

    } catch (const std::exception &x) {
        Error("caught exception: " + std::string(x.what()));
    }
}
<commit_msg>Required changes<commit_after>/** \file    add_synonyms.cc
 *  \brief   Generic version for augmenting title data with synonyms found
             in the authority data
 *  \author  Johannes Riedl
 */

/*
    Copyright (C) 2016, Library of the University of Tübingen

    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/>.
*/

/*  We offer a list of tags and subfields where the primary data resides along
    with a list of tags and subfields where the synonym data is found and
    a list of unused fields in the title data where the synonyms can be stored
*/

#include <iostream>
#include <map>
#include <vector>
#include <cstdlib>
#include "Compiler.h"
#include "FileUtil.h"
#include "MarcReader.h"
#include "MarcRecord.h"
#include "MarcWriter.h"
#include "MediaTypeUtil.h"
#include "RegexMatcher.h"
#include "StringUtil.h"
#include "Subfields.h"
#include "util.h"

static unsigned modified_count(0);
static unsigned record_count(0);

void Usage() {
    std::cerr << "Usage: " << ::progname << " master_marc_input norm_data_marc_input marc_output\n";
    std::exit(EXIT_FAILURE);
}

std::string GetTag(const std::string &tag_and_subfields_spec) {
    return tag_and_subfields_spec.substr(0, 3);
}

std::string GetSubfieldCodes(const std::string &tag_and_subfields_spec) {
    return tag_and_subfields_spec.substr(3);
}

bool FilterPasses(const MarcRecord &record, const std::map<std::string, std::pair<std::string, std::string>> &filter_specs, const std::string &field_spec) {
      auto filter_spec(filter_specs.find(field_spec));
      if (filter_spec == filter_specs.cend())
          return true;

      auto rule(filter_spec->second);
      // We have field_spec in key and rule to match in value
      std::string subfield_codes(GetSubfieldCodes(rule.first));
      if (subfield_codes.length() != 1)
         Error("Invalid subfield specification "  + subfield_codes + " for filter");

      std::string subfield_value;
      if ((subfield_value = record.extractFirstSubfield(GetTag(rule.first), subfield_codes.c_str()[0])).empty())
          return false;

      return subfield_value == rule.second;
}

void ExtractSynonyms(MarcReader * const authority_reader,
                     const std::set<std::string> &primary_tags_and_subfield_codes,
                     const std::set<std::string> &synonym_tags_and_subfield_codes,
                     std::vector<std::map<std::string, std::string>> * const synonym_maps,
                     const std::map<std::string, std::pair<std::string, std::string>> &filter_spec)
{
    while (const MarcRecord record = authority_reader->read()) {
        std::set<std::string>::const_iterator primary;
        std::set<std::string>::const_iterator synonym;
        unsigned int i(0);
        for (primary = primary_tags_and_subfield_codes.begin(), synonym = synonym_tags_and_subfield_codes.begin();
            primary != primary_tags_and_subfield_codes.end();
            ++primary, ++synonym, ++i)
        {
            // Fill maps with synonyms
            std::vector<std::string> primary_values;
            std::vector<std::string> synonym_values;

            if (FilterPasses(record, filter_spec, *primary) and
                record.extractSubfields(GetTag(*primary), GetSubfieldCodes(*primary), &primary_values) and
                record.extractSubfields(GetTag(*synonym), GetSubfieldCodes(*synonym), &synonym_values))
                    (*synonym_maps)[i].emplace(StringUtil::Join(primary_values, ','),
                                               StringUtil::Join(synonym_values, ','));
        }
    }
}

inline std::string GetMapValueOrEmptyString(const std::map<std::string, std::string> &map,
                                            const std::string &searchterm)
{
    auto value(map.find(searchterm));
    return (value != map.cend()) ? value->second : "";
}

void ProcessRecord(MarcRecord * const record, const std::vector<std::map<std::string, std::string>> &synonym_maps,
                   const std::set<std::string> &primary_tags_and_subfield_codes,
                   const std::set<std::string> &output_tags_and_subfield_codes)
{
    std::set<std::string>::const_iterator primary;
    std::set<std::string>::const_iterator output;
    unsigned int i(0);

    if (primary_tags_and_subfield_codes.size() == output_tags_and_subfield_codes.size()) {
        for (primary = primary_tags_and_subfield_codes.begin(), output = output_tags_and_subfield_codes.begin();
            primary != primary_tags_and_subfield_codes.end();
            ++primary, ++output, ++i)
        {
            std::vector<std::string> primary_values;
            std::set<std::string> synonym_values;
            std::vector<size_t> field_indices;
            if (record->getFieldIndices(GetTag(*primary), &field_indices) != MarcRecord::FIELD_NOT_FOUND) {
                for (auto field_index : field_indices) {
                     primary_values.clear();
                     if (record->getSubfields(field_index).extractSubfields(GetSubfieldCodes(*primary), &primary_values)) {
                        std::string searchterm = StringUtil::Join(primary_values, ',');
                        // First case: Look up synonyms only in one category
                        if (i < synonym_maps.size()) {
                            const auto &synonym_map(synonym_maps[i]);
                            const auto &synonym(GetMapValueOrEmptyString(synonym_map, searchterm));
                            if (not synonym.empty())
                                synonym_values.insert(synonym);
                        }

                        // Second case: Look up synonyms in all categories
                        else {
                            for (auto &sm : synonym_maps) {
                                const auto &synonym(GetMapValueOrEmptyString(sm, searchterm));
                                if (not synonym.empty())
                                   synonym_values.insert(synonym);
                            }
                        }
                    }
                 }

                if (synonym_values.empty())
                     continue;

                const std::string synonyms(StringUtil::Join(synonym_values, ','));

                // Insert synonyms
                // Abort if field is already populated
                std::string tag(GetTag(*output));
                if (record->getFieldIndex(tag) != MarcRecord::FIELD_NOT_FOUND)
                    Error("Field with tag " + tag + " is not empty for PPN " + record->getControlNumber() + '\n');
                std::string subfield_spec = GetSubfieldCodes(*output);
                if (subfield_spec.size() != 1)
                    Error("We currently only support a single subfield and thus specifying " + subfield_spec
                          + " as output subfield is not valid\n");
                Subfields subfields(' ', ' '); // <- indicators must be set explicitly although empty
                subfields.addSubfield(subfield_spec.at(0), synonyms);
                if (not(record->insertField(tag, subfields.toString())))
                    Warning("Could not insert field " + tag + " for PPN " + record->getControlNumber() + '\n');
                ++modified_count;
            }
        }
    }
}

void InsertSynonyms(MarcReader * const marc_reader, MarcWriter * const marc_writer,
                    const std::set<std::string> &primary_tags_and_subfield_codes,
                    const std::set<std::string> &output_tags_and_subfield_codes,
                    std::vector<std::map<std::string, std::string>> &synonym_maps)
{
    while (MarcRecord record = marc_reader->read()) {
        ProcessRecord(&record, synonym_maps, primary_tags_and_subfield_codes, output_tags_and_subfield_codes);
        marc_writer->write(record);
        ++record_count;
    }

    std::cerr << "Modified " << modified_count << " of " << record_count << " record(s).\n";
}

int ParseSpec(std::string spec_str, std::set<std::string> * field_specs, std::map<std::string, std::pair<std::string, std::string>> * filter_specs = nullptr) {
    std::set<std::string>  raw_field_specs;

    if (unlikely(StringUtil::Split(spec_str, ":", &raw_field_specs) < 1)){
        Error("Need at least one field");
        return -1;
    }

    if (filter_specs == nullptr) {
        *field_specs = raw_field_specs;
	return 0;
    }

    // Iterate over all Field-specs and extract possible filters
    static RegexMatcher * const matcher(RegexMatcher::RegexMatcherFactory("(\\d{1,3}[a-z]+)\\[(\\d{1,3}[a-z])=(.*)\\]"));

    for (auto field_spec : raw_field_specs) {
        if (matcher->matched(field_spec)){
            filter_specs->emplace((*matcher)[1], std::make_pair((*matcher)[2], (*matcher)[3]));
            auto bracket = field_spec.find("[");
            field_spec = (bracket != std::string::npos) ? field_spec.erase(bracket, field_spec.length()) : field_spec;
        }
        field_specs->insert(field_spec);
    }
    return 0;
}

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

    if (argc != 4)
        Usage();

    const std::string marc_input_filename(argv[1]);
    const std::string authority_data_marc_input_filename(argv[2]);
    const std::string marc_output_filename(argv[3]);
    if (unlikely(marc_input_filename == marc_output_filename))
        Error("Title data input file name equals output file name!");
    if (unlikely(authority_data_marc_input_filename == marc_output_filename))
        Error("Authority data input file name equals output file name!");

    std::unique_ptr<MarcReader> marc_reader(MarcReader::Factory(marc_input_filename, MarcReader::BINARY));
    std::unique_ptr<MarcReader> authority_reader(MarcReader::Factory(authority_data_marc_input_filename,
                                                                     MarcReader::BINARY));
    std::unique_ptr<MarcWriter> marc_writer(MarcWriter::Factory(marc_output_filename, MarcWriter::BINARY));

    try {
        // Determine possible mappings
        // Values in square brackets specify a positive criterion for values to be taken into account
        const std::string AUTHORITY_DATA_PRIMARY_SPEC("100abcd[079v=piz]:110abcd:111abcd:130abcd:150abcd:151abcd");
        const std::string AUTHORITY_DATA_SYNONYM_SPEC("400abcd:410abcd:411abcd:430abcd:450abcd:451abcd");
        const std::string TITLE_DATA_PRIMARY_SPEC("600abcd:610abcd:611abcd:630abcd:650abcd:651abcd:689abcd");
        const std::string TITLE_DATA_UNUSED_FIELDS_FOR_SYNONYMS("180a:181a:182a:183a:184a:185a:186a");

        // Determine fields to handle
        std::set<std::string> primary_tags_and_subfield_codes;
        std::set<std::string> synonym_tags_and_subfield_codes;
        std::set<std::string> input_tags_and_subfield_codes;
        std::set<std::string> output_tags_and_subfield_codes;

        std::map<std::string, std::pair<std::string, std::string>> filter_specs;

        if (unlikely(ParseSpec(AUTHORITY_DATA_PRIMARY_SPEC, &primary_tags_and_subfield_codes, &filter_specs) < 0))
            Error("Could not properly parse " + AUTHORITY_DATA_PRIMARY_SPEC);

        if (unlikely(StringUtil::Split(AUTHORITY_DATA_SYNONYM_SPEC, ":", &synonym_tags_and_subfield_codes) < 1))
            Error("Need at least one synonym field");

        if (unlikely(StringUtil::Split(TITLE_DATA_PRIMARY_SPEC, ":", &input_tags_and_subfield_codes) < 1))
            Error("Need at least one input field");

        if (unlikely(StringUtil::Split(TITLE_DATA_UNUSED_FIELDS_FOR_SYNONYMS, ":", &output_tags_and_subfield_codes)
                     < 1))
            Error("Need at least one output field");

        unsigned num_of_authority_entries(primary_tags_and_subfield_codes.size());

        if (synonym_tags_and_subfield_codes.size() != num_of_authority_entries)
            Error("Number of authority primary specs must match number of synonym specs");
        if (input_tags_and_subfield_codes.size() != output_tags_and_subfield_codes.size())
            Error("Number of fields title entry specs must match number of output specs");

        std::vector<std::map<std::string, std::string>> synonym_maps(num_of_authority_entries,
                                                                     std::map<std::string, std::string>());

        // Extract the synonyms from authority data
        ExtractSynonyms(authority_reader.get(), primary_tags_and_subfield_codes, synonym_tags_and_subfield_codes,
                        &synonym_maps, filter_specs);

        // Iterate over the title data
        InsertSynonyms(marc_reader.get(), marc_writer.get(), input_tags_and_subfield_codes,
                       output_tags_and_subfield_codes, synonym_maps);

    } catch (const std::exception &x) {
        Error("caught exception: " + std::string(x.what()));
    }
}
<|endoftext|>


<commit_before>#ifndef RETHREAD_POLL_HPP
#define RETHREAD_POLL_HPP

// Copyright (c) 2016, Boris Sazonov
//
// Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted,
// provided that the above copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
// IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
// WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

#include <rethread/cancellation_token.hpp>
#include <rethread/detail/utility.hpp>

#include <exception>

#include <poll.h>
#include <unistd.h>

#if !defined(RETHREAD_DISABLE_EVENTFD)
#include <sys/eventfd.h>
#endif

namespace rethread
{
	namespace detail
	{
#if !defined(RETHREAD_DISABLE_EVENTFD)
		struct poll_cancellation_handler : public cancellation_handler
		{
		private:
			int _eventfd;

		public:
			poll_cancellation_handler()
			{
				_eventfd = ::eventfd(0, 0);
				RETHREAD_CHECK(_eventfd != -1, std::system_error(errno, std::system_category(), "eventfd failed"));
			}

			virtual ~poll_cancellation_handler()
			{ RETHREAD_CHECK(::close(_eventfd) == 0, std::system_error(errno, std::system_category())); }

			poll_cancellation_handler(const poll_cancellation_handler&) = delete;
			poll_cancellation_handler& operator = (const poll_cancellation_handler&) = delete;

			void cancel() override
			{
				uint64_t val = 1;
				RETHREAD_CHECK(::write(_eventfd, &val, sizeof(val)) == sizeof(val), std::system_error(errno, std::system_category()));
			}

			void reset() override
			{
				uint64_t val;
				RETHREAD_CHECK(::read(_eventfd, &val, sizeof(val)) == sizeof(val), std::system_error(errno, std::system_category()));
			}

			int get_fd() const
			{ return _eventfd; }
		};
#else
		struct poll_cancellation_handler : public cancellation_handler
		{
		private:
			int _pipe[2];

		public:
			poll_cancellation_handler()
			{ RETHREAD_CHECK(::pipe(_pipe) == 0, std::system_error(errno, std::system_category())); }

			~poll_cancellation_handler()
			{
				RETHREAD_CHECK(::close(_pipe[0]) == 0, std::system_error(errno, std::system_category()));
				RETHREAD_CHECK(::close(_pipe[1]) == 0, std::system_error(errno, std::system_category()));
			}

			poll_cancellation_handler(const poll_cancellation_handler&) = delete;
			poll_cancellation_handler& operator = (const poll_cancellation_handler&) = delete;

			void cancel() override
			{
				char dummy = 0;
				RETHREAD_CHECK(::write(_pipe[1], &dummy, 1) == 1, std::system_error(errno, std::system_category()));
			}

			void reset() override
			{
				char dummy;
				RETHREAD_CHECK(::read(_pipe[0], &dummy, 1) == 1, std::system_error(errno, std::system_category()));
			}

			int get_fd() const
			{ return _pipe[0]; }
		};
#endif
	}

	inline short poll(int fd, short events, int timeoutMs, const cancellation_token& token)
	{
		detail::poll_cancellation_handler handler;
		cancellation_guard guard(token, handler);
		if (guard.is_cancelled())
			return 0;

		pollfd fds[2] = { };

		fds[0].fd = fd;
		fds[0].events = events;

		fds[1].fd = handler.get_fd();
		fds[1].events = POLLIN;

		RETHREAD_CHECK(::poll(fds, 2, timeoutMs) != -1, std::system_error(errno, std::system_category()));
		return fds[0].revents;
	}

	inline short poll(int fd, short events, const cancellation_token& token)
	{ return poll(fd, events, -1, token); }

	/// @brief   Cancellable version of POSIX read(...). Uses cancellable poll to implement cancellable waiting.
	/// @returns Zero if cancelled, otherwise read() result
	inline ssize_t read(int fd, void* buf, size_t nbyte, const cancellation_token& token)
	{
		if (poll(fd, POLLIN, token) != POLLIN)
			return 0;
		return ::read(fd, buf, nbyte);
	}
}

#endif
<commit_msg>Fixed fd leakage on fork<commit_after>#ifndef RETHREAD_POLL_HPP
#define RETHREAD_POLL_HPP

// Copyright (c) 2016, Boris Sazonov
//
// Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted,
// provided that the above copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
// IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
// WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

#include <rethread/cancellation_token.hpp>
#include <rethread/detail/utility.hpp>

#include <exception>

#include <poll.h>
#include <unistd.h>

#if !defined(RETHREAD_DISABLE_EVENTFD)
#include <sys/eventfd.h>
#endif

namespace rethread
{
	namespace detail
	{
#if !defined(RETHREAD_DISABLE_EVENTFD)
		struct poll_cancellation_handler : public cancellation_handler
		{
		private:
			int _eventfd;

		public:
			poll_cancellation_handler()
			{
				_eventfd = ::eventfd(0, EFD_CLOEXEC);
				RETHREAD_CHECK(_eventfd != -1, std::system_error(errno, std::system_category(), "eventfd failed"));
			}

			virtual ~poll_cancellation_handler()
			{ RETHREAD_CHECK(::close(_eventfd) == 0, std::system_error(errno, std::system_category())); }

			poll_cancellation_handler(const poll_cancellation_handler&) = delete;
			poll_cancellation_handler& operator = (const poll_cancellation_handler&) = delete;

			void cancel() override
			{
				uint64_t val = 1;
				RETHREAD_CHECK(::write(_eventfd, &val, sizeof(val)) == sizeof(val), std::system_error(errno, std::system_category()));
			}

			void reset() override
			{
				uint64_t val;
				RETHREAD_CHECK(::read(_eventfd, &val, sizeof(val)) == sizeof(val), std::system_error(errno, std::system_category()));
			}

			int get_fd() const
			{ return _eventfd; }
		};
#else
		struct poll_cancellation_handler : public cancellation_handler
		{
		private:
			int _pipe[2];

		public:
			poll_cancellation_handler()
			{ RETHREAD_CHECK(::pipe2(_pipe, O_CLOEXEC) == 0, std::system_error(errno, std::system_category())); }

			~poll_cancellation_handler()
			{
				RETHREAD_CHECK(::close(_pipe[0]) == 0, std::system_error(errno, std::system_category()));
				RETHREAD_CHECK(::close(_pipe[1]) == 0, std::system_error(errno, std::system_category()));
			}

			poll_cancellation_handler(const poll_cancellation_handler&) = delete;
			poll_cancellation_handler& operator = (const poll_cancellation_handler&) = delete;

			void cancel() override
			{
				char dummy = 0;
				RETHREAD_CHECK(::write(_pipe[1], &dummy, 1) == 1, std::system_error(errno, std::system_category()));
			}

			void reset() override
			{
				char dummy;
				RETHREAD_CHECK(::read(_pipe[0], &dummy, 1) == 1, std::system_error(errno, std::system_category()));
			}

			int get_fd() const
			{ return _pipe[0]; }
		};
#endif
	}

	inline short poll(int fd, short events, int timeoutMs, const cancellation_token& token)
	{
		detail::poll_cancellation_handler handler;
		cancellation_guard guard(token, handler);
		if (guard.is_cancelled())
			return 0;

		pollfd fds[2] = { };

		fds[0].fd = fd;
		fds[0].events = events;

		fds[1].fd = handler.get_fd();
		fds[1].events = POLLIN;

		RETHREAD_CHECK(::poll(fds, 2, timeoutMs) != -1, std::system_error(errno, std::system_category()));
		return fds[0].revents;
	}

	inline short poll(int fd, short events, const cancellation_token& token)
	{ return poll(fd, events, -1, token); }

	/// @brief   Cancellable version of POSIX read(...). Uses cancellable poll to implement cancellable waiting.
	/// @returns Zero if cancelled, otherwise read() result
	inline ssize_t read(int fd, void* buf, size_t nbyte, const cancellation_token& token)
	{
		if (poll(fd, POLLIN, token) != POLLIN)
			return 0;
		return ::read(fd, buf, nbyte);
	}
}

#endif
<|endoftext|>


<commit_before>#include "ride/wx.h"
#include "ride/settings.h"

#include <wx/filename.h>
#include <wx/stdpaths.h>
#include <fstream>

wxFileName GetConfigFile() {
  // wxStandardPaths::Get().UseAppInfo(wxStandardPaths::AppInfo_AppName | wxStandardPaths::AppInfo_VendorName);
  wxFileName folder(wxStandardPaths::Get().GetUserDataDir(), "settings", "data");
  return folder;
}

void LoadSettings(::ride::Settings& settings) {
  const wxFileName confPath = GetConfigFile();
  const wxString path = confPath.GetFullPath();
  if (confPath.IsFileReadable()) {
    std::fstream input(path.c_str().AsChar(), std::ios::in | std::ios::binary);
    const bool parse_result = settings.ParseFromIstream(&input);
  }
}

bool SaveSettings(::ride::Settings& settings) {
  const wxFileName confPath = GetConfigFile();
  const bool create_result = confPath.Mkdir(wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL);
  if (confPath.FileExists() && confPath.IsFileWritable() == false) {
    // abort if the file exist and isn't writable
    return false;
  }
  const wxString path = confPath.GetFullPath();
  std::fstream input(path.c_str().AsChar(), std::ios::out | std::ios::trunc | std::ios::binary);
  return settings.SerializeToOstream(&input);
}

wxColor C(const ride::Color& c) {
  return wxColor(c.r(), c.g(), c.b());
}

ride::Color C(const wxColor& c) {
  ride::Color r;
  r.set_r(c.Red());
  r.set_g(c.Green());
  r.set_b(c.Blue());
  return r;
}

/*
FoldFlags::FoldFlags()
  : LINEBEFORE_EXPANDED(false)
  , LINEBEFORE_CONTRACTED(false)
  , LINEAFTER_EXPANDED(false)
  , LINEAFTER_CONTRACTED(false)
  , LEVELNUMBERS(false)
{}

Style::Style(const wxFont& font, const wxColor& foreground, const wxColor& background)
  : font(font)
  , foreground(foreground)
  , background(background)
{}

Settings::Settings()
  : lineNumberEnable(true)
  , foldEnable(true)
  , displayEOLEnable(false)
  , indentGuideEnable(true)
  , whitespace(ViewWhitespace::HIDDEN)
  , wordWrap(WrapMode::NONE)
  , edgeStyle(EdgeStyle::NONE)
  , edgeColor(0,0,0)
  , edgeColumn(80)
  , tabWidth(4)
  , useTabs(false)
  , tabIndents(true)
  , backspaceUnindents(true)
  , foldComment(true)
  , foldCompact(true)
  , foldPreproc(true)
  , styling_within_preprocessor(false)
  , lexer_cpp_allow_dollars(false)
  , lexer_cpp_track_preprocessor(false)
  , lexer_cpp_update_preprocessor(false)
  , lexer_cpp_triplequoted_strings(false)
  , lexer_cpp_hashquoted_strings(false)
  , fold_cpp_syntax_based(true)
  , fold_cpp_comment_multiline(true)
  , fold_cpp_comment_explicit(true)
  , fold_cpp_explicit_anywhere(false)
  , fold_at_else(true)

{}

void Settings::load() {
}

void Settings::save() {
}

*/<commit_msg>Code cleanup<commit_after>#include "ride/wx.h"
#include "ride/settings.h"

#include <wx/filename.h>
#include <wx/stdpaths.h>
#include <fstream>

wxString GetConfigFolder() {
  return wxStandardPaths::Get().GetUserDataDir();
}

wxFileName GetConfigFile() {
  return wxFileName(GetConfigFolder(), "settings", "data");
}

void LoadSettings(::ride::Settings& settings) {
  const wxFileName confPath = GetConfigFile();
  const wxString path = confPath.GetFullPath();
  if (confPath.IsFileReadable()) {
    std::fstream input(path.c_str().AsChar(), std::ios::in | std::ios::binary);
    const bool parse_result = settings.ParseFromIstream(&input);
    if (false == parse_result) {
      wxMessageBox("Unable to parse settings file!", "Error", wxOK | wxICON_WARNING);
    }
  }
}

bool SaveSettings(::ride::Settings& settings) {
  const wxFileName config_file = GetConfigFile();
  const bool create_result = config_file.Mkdir(wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL);
  if (config_file.FileExists() && config_file.IsFileWritable() == false) {
    // abort if the file exist and isn't writable
    return false;
  }
  const wxString config_path = config_file.GetFullPath();
  std::fstream config_stream(config_path.c_str().AsChar(), std::ios::out | std::ios::trunc | std::ios::binary);
  return settings.SerializeToOstream(&config_stream);
}

wxColor C(const ride::Color& c) {
  return wxColor(c.r(), c.g(), c.b());
}

ride::Color C(const wxColor& c) {
  ride::Color r;
  r.set_r(c.Red());
  r.set_g(c.Green());
  r.set_b(c.Blue());
  return r;
}
<|endoftext|>


<commit_before>#include "tools/cabana/canmessages.h"

#include <QDebug>
#include <QSettings>

#include "tools/cabana/dbcmanager.h"

CANMessages *can = nullptr;

CANMessages::CANMessages(QObject *parent) : QObject(parent) {
  can = this;

  QObject::connect(this, &CANMessages::received, this, &CANMessages::process, Qt::QueuedConnection);
  QObject::connect(&settings, &Settings::changed, this, &CANMessages::settingChanged);
}

CANMessages::~CANMessages() {
  replay->stop();
}

static bool event_filter(const Event *e, void *opaque) {
  CANMessages *c = (CANMessages *)opaque;
  return c->eventFilter(e);
}

bool CANMessages::loadRoute(const QString &route, const QString &data_dir, bool use_qcam) {
  routeName = route;
  replay = new Replay(route, {"can", "roadEncodeIdx", "carParams"}, {}, nullptr, use_qcam ? REPLAY_FLAG_QCAMERA : 0, data_dir, this);
  replay->setSegmentCacheLimit(settings.cached_segment_limit);
  replay->installEventFilter(event_filter, this);
  QObject::connect(replay, &Replay::segmentsMerged, this, &CANMessages::segmentsMerged);
  if (replay->load()) {
    replay->start();
    return true;
  }
  return false;
}

QList<QPointF> CANMessages::findSignalValues(const QString &id, const Signal *signal, double value, FindFlags flag, int max_count) {
  auto evts = events();
  if (!evts) return {};

  auto l = id.split(':');
  int bus = l[0].toInt();
  uint32_t address = l[1].toUInt(nullptr, 16);

  QList<QPointF> ret;
  ret.reserve(max_count);
  for (auto &evt : *evts) {
    if (evt->which != cereal::Event::Which::CAN) continue;

    for (auto c : evt->event.getCan()) {
      if (bus == c.getSrc() && address == c.getAddress()) {
        double val = get_raw_value((uint8_t *)c.getDat().begin(), c.getDat().size(), *signal);
        if ((flag == EQ && val == value) || (flag == LT && val < value) || (flag == GT && val > value)) {
          ret.push_back({(evt->mono_time / (double)1e9) - can->routeStartTime(), val});
          if (ret.size() >= max_count)
            return ret;
        }
      }
    }
  }
  return ret;
}

void CANMessages::process(QHash<QString, std::deque<CanData>> *messages) {
  for (auto it = messages->begin(); it != messages->end(); ++it) {
    auto &msgs = can_msgs[it.key()];
    const auto &new_msgs = it.value();
    if (new_msgs.size() == settings.can_msg_log_size || msgs.empty()) {
      msgs = std::move(new_msgs);
    } else {
      msgs.insert(msgs.begin(), std::make_move_iterator(new_msgs.begin()), std::make_move_iterator(new_msgs.end()));
      while (msgs.size() >= settings.can_msg_log_size) {
        msgs.pop_back();
      }
    }
  }
  delete messages;

  if (current_sec < begin_sec || current_sec > end_sec) {
    // loop replay in selected range.
    seekTo(begin_sec);
  } else {
    emit updated();
  }
}

bool CANMessages::eventFilter(const Event *event) {
  static double prev_update_sec = 0;
  // drop packets when the GUI thread is calling seekTo. to make sure the current_sec is accurate.
  if (!seeking && event->which == cereal::Event::Which::CAN) {
    if (!received_msgs) {
      received_msgs.reset(new QHash<QString, std::deque<CanData>>);
      received_msgs->reserve(1000);
    }

    current_sec = (event->mono_time - replay->routeStartTime()) / (double)1e9;
    if (counters_begin_sec > current_sec) {
      // clear counters
      counters.clear();
      counters_begin_sec = current_sec;
    }

    auto can_events = event->event.getCan();
    for (const auto &c : can_events) {
      QString id = QString("%1:%2").arg(c.getSrc()).arg(c.getAddress(), 1, 16);
      auto &list = (*received_msgs)[id];
      while (list.size() >= settings.can_msg_log_size) {
        list.pop_back();
      }
      CanData &data = list.emplace_front();
      data.ts = current_sec;
      data.bus_time = c.getBusTime();
      data.dat.append((char *)c.getDat().begin(), c.getDat().size());

      auto &count = counters[id];
      data.count = ++count;
      if (double delta = (current_sec - counters_begin_sec); delta > 0) {
        data.freq = count / delta;
      }
    }

    if (current_sec < prev_update_sec || (current_sec - prev_update_sec) > 1.0 / settings.fps) {
      prev_update_sec = current_sec;
      // use pointer to avoid data copy in queued connection.
      emit received(received_msgs.release());
    }
  }
  return true;
}

void CANMessages::seekTo(double ts) {
  seeking = true;
  replay->seekTo(ts, false);
  seeking = false;
}

void CANMessages::setRange(double min, double max) {
  if (begin_sec != min || end_sec != max) {
    begin_sec = min;
    end_sec = max;
    is_zoomed = begin_sec != event_begin_sec || end_sec != event_end_sec;
    emit rangeChanged(min, max);
  }
}

void CANMessages::segmentsMerged() {
  auto events = replay->events();
  if (!events || events->empty()) return;

  auto it = std::find_if(events->begin(), events->end(), [=](const Event *e) { return e->which == cereal::Event::Which::CAN; });
  event_begin_sec = it == events->end() ? 0 : ((*it)->mono_time - replay->routeStartTime()) / (double)1e9;
  event_end_sec = double(events->back()->mono_time - replay->routeStartTime()) / 1e9;
  if (!is_zoomed) {
    begin_sec = event_begin_sec;
    end_sec = event_end_sec;
  }
  emit eventsMerged();
}

void CANMessages::resetRange() {
  setRange(event_begin_sec, event_end_sec);
}

void CANMessages::settingChanged() {
  replay->setSegmentCacheLimit(settings.cached_segment_limit);
}
<commit_msg>Cabana: use deque::resize() instead of pop_back in loop (#26209)<commit_after>#include "tools/cabana/canmessages.h"

#include <QDebug>
#include <QSettings>

#include "tools/cabana/dbcmanager.h"

CANMessages *can = nullptr;

CANMessages::CANMessages(QObject *parent) : QObject(parent) {
  can = this;

  QObject::connect(this, &CANMessages::received, this, &CANMessages::process, Qt::QueuedConnection);
  QObject::connect(&settings, &Settings::changed, this, &CANMessages::settingChanged);
}

CANMessages::~CANMessages() {
  replay->stop();
}

static bool event_filter(const Event *e, void *opaque) {
  CANMessages *c = (CANMessages *)opaque;
  return c->eventFilter(e);
}

bool CANMessages::loadRoute(const QString &route, const QString &data_dir, bool use_qcam) {
  routeName = route;
  replay = new Replay(route, {"can", "roadEncodeIdx", "carParams"}, {}, nullptr, use_qcam ? REPLAY_FLAG_QCAMERA : 0, data_dir, this);
  replay->setSegmentCacheLimit(settings.cached_segment_limit);
  replay->installEventFilter(event_filter, this);
  QObject::connect(replay, &Replay::segmentsMerged, this, &CANMessages::segmentsMerged);
  if (replay->load()) {
    replay->start();
    return true;
  }
  return false;
}

QList<QPointF> CANMessages::findSignalValues(const QString &id, const Signal *signal, double value, FindFlags flag, int max_count) {
  auto evts = events();
  if (!evts) return {};

  auto l = id.split(':');
  int bus = l[0].toInt();
  uint32_t address = l[1].toUInt(nullptr, 16);

  QList<QPointF> ret;
  ret.reserve(max_count);
  for (auto &evt : *evts) {
    if (evt->which != cereal::Event::Which::CAN) continue;

    for (auto c : evt->event.getCan()) {
      if (bus == c.getSrc() && address == c.getAddress()) {
        double val = get_raw_value((uint8_t *)c.getDat().begin(), c.getDat().size(), *signal);
        if ((flag == EQ && val == value) || (flag == LT && val < value) || (flag == GT && val > value)) {
          ret.push_back({(evt->mono_time / (double)1e9) - can->routeStartTime(), val});
          if (ret.size() >= max_count)
            return ret;
        }
      }
    }
  }
  return ret;
}

void CANMessages::process(QHash<QString, std::deque<CanData>> *messages) {
  for (auto it = messages->begin(); it != messages->end(); ++it) {
    auto &msgs = can_msgs[it.key()];
    const auto &new_msgs = it.value();
    if (new_msgs.size() == settings.can_msg_log_size || msgs.empty()) {
      msgs = std::move(new_msgs);
    } else {
      msgs.insert(msgs.begin(), std::make_move_iterator(new_msgs.begin()), std::make_move_iterator(new_msgs.end()));
      if (msgs.size() > settings.can_msg_log_size) {
        msgs.resize(settings.can_msg_log_size);
      }
    }
  }
  delete messages;

  if (current_sec < begin_sec || current_sec > end_sec) {
    // loop replay in selected range.
    seekTo(begin_sec);
  } else {
    emit updated();
  }
}

bool CANMessages::eventFilter(const Event *event) {
  static double prev_update_sec = 0;
  // drop packets when the GUI thread is calling seekTo. to make sure the current_sec is accurate.
  if (!seeking && event->which == cereal::Event::Which::CAN) {
    if (!received_msgs) {
      received_msgs.reset(new QHash<QString, std::deque<CanData>>);
      received_msgs->reserve(1000);
    }

    current_sec = (event->mono_time - replay->routeStartTime()) / (double)1e9;
    if (counters_begin_sec > current_sec) {
      // clear counters
      counters.clear();
      counters_begin_sec = current_sec;
    }

    auto can_events = event->event.getCan();
    for (const auto &c : can_events) {
      QString id = QString("%1:%2").arg(c.getSrc()).arg(c.getAddress(), 1, 16);
      auto &list = (*received_msgs)[id];
      while (list.size() >= settings.can_msg_log_size) {
        list.pop_back();
      }
      CanData &data = list.emplace_front();
      data.ts = current_sec;
      data.bus_time = c.getBusTime();
      data.dat.append((char *)c.getDat().begin(), c.getDat().size());

      auto &count = counters[id];
      data.count = ++count;
      if (double delta = (current_sec - counters_begin_sec); delta > 0) {
        data.freq = count / delta;
      }
    }

    if (current_sec < prev_update_sec || (current_sec - prev_update_sec) > 1.0 / settings.fps) {
      prev_update_sec = current_sec;
      // use pointer to avoid data copy in queued connection.
      emit received(received_msgs.release());
    }
  }
  return true;
}

void CANMessages::seekTo(double ts) {
  seeking = true;
  replay->seekTo(ts, false);
  seeking = false;
}

void CANMessages::setRange(double min, double max) {
  if (begin_sec != min || end_sec != max) {
    begin_sec = min;
    end_sec = max;
    is_zoomed = begin_sec != event_begin_sec || end_sec != event_end_sec;
    emit rangeChanged(min, max);
  }
}

void CANMessages::segmentsMerged() {
  auto events = replay->events();
  if (!events || events->empty()) return;

  auto it = std::find_if(events->begin(), events->end(), [=](const Event *e) { return e->which == cereal::Event::Which::CAN; });
  event_begin_sec = it == events->end() ? 0 : ((*it)->mono_time - replay->routeStartTime()) / (double)1e9;
  event_end_sec = double(events->back()->mono_time - replay->routeStartTime()) / 1e9;
  if (!is_zoomed) {
    begin_sec = event_begin_sec;
    end_sec = event_end_sec;
  }
  emit eventsMerged();
}

void CANMessages::resetRange() {
  setRange(event_begin_sec, event_end_sec);
}

void CANMessages::settingChanged() {
  replay->setSegmentCacheLimit(settings.cached_segment_limit);
}
<|endoftext|>


<commit_before>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: dlgeps.hxx,v $
 *
 *  $Revision: 1.7 $
 *
 *  last change: $Author: rt $ $Date: 2005-09-09 02:44:20 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    This library 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
 *    Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/

#ifndef _DLGEPS_HXX_
#define _DLGEPS_HXX_
#include <svtools/fltcall.hxx>
#include <vcl/dialog.hxx>
#include <vcl/button.hxx>
#include <vcl/fixed.hxx>
#include <vcl/field.hxx>
#include <vcl/lstbox.hxx>
#include <svtools/stdctrl.hxx>

/*************************************************************************
|*
|* Dialog zum Einstellen von Filteroptionen
|*
\************************************************************************/

class FilterConfigItem;
class ResMgr;

class DlgExportEPS : public ModalDialog
{
private:

    FltCallDialogParameter& rFltCallPara;

    FixedLine           aGrpPreview;
    CheckBox            aCBPreviewTiff;
    CheckBox            aCBPreviewEPSI;
    FixedLine           aGrpVersion;
    RadioButton         aRBLevel1;
    RadioButton         aRBLevel2;
    FixedLine           aGrpColor;
    RadioButton         aRBColor;
    RadioButton         aRBGrayscale;
    FixedLine           aGrpCompression;
    RadioButton         aRBCompressionLZW;
    RadioButton         aRBCompressionNone;
    OKButton            aBtnOK;
    CancelButton        aBtnCancel;
    HelpButton          aBtnHelp;

    FilterConfigItem*   pConfigItem;
    ResMgr*             pMgr;

    DECL_LINK( OK, void * );
    DECL_LINK( LEVEL1, void* );
    DECL_LINK( LEVEL2, void* );

public:
            DlgExportEPS( FltCallDialogParameter& rPara );
            ~DlgExportEPS();
};

#endif // _DLGEPS_HXX_
<commit_msg>INTEGRATION: CWS changefileheader (1.7.268); FILE MERGED 2008/03/31 13:39:36 rt 1.7.268.1: #i87441# Change license header to LPGL v3.<commit_after>/*************************************************************************
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * Copyright 2008 by Sun Microsystems, Inc.
 *
 * OpenOffice.org - a multi-platform office productivity suite
 *
 * $RCSfile: dlgeps.hxx,v $
 * $Revision: 1.8 $
 *
 * This file is part of OpenOffice.org.
 *
 * OpenOffice.org is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * OpenOffice.org 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 Lesser General Public License version 3 for more details
 * (a copy is included in the LICENSE file that accompanied this code).
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with OpenOffice.org.  If not, see
 * <http://www.openoffice.org/license.html>
 * for a copy of the LGPLv3 License.
 *
 ************************************************************************/

#ifndef _DLGEPS_HXX_
#define _DLGEPS_HXX_
#include <svtools/fltcall.hxx>
#include <vcl/dialog.hxx>
#include <vcl/button.hxx>
#include <vcl/fixed.hxx>
#include <vcl/field.hxx>
#include <vcl/lstbox.hxx>
#include <svtools/stdctrl.hxx>

/*************************************************************************
|*
|* Dialog zum Einstellen von Filteroptionen
|*
\************************************************************************/

class FilterConfigItem;
class ResMgr;

class DlgExportEPS : public ModalDialog
{
private:

    FltCallDialogParameter& rFltCallPara;

    FixedLine           aGrpPreview;
    CheckBox            aCBPreviewTiff;
    CheckBox            aCBPreviewEPSI;
    FixedLine           aGrpVersion;
    RadioButton         aRBLevel1;
    RadioButton         aRBLevel2;
    FixedLine           aGrpColor;
    RadioButton         aRBColor;
    RadioButton         aRBGrayscale;
    FixedLine           aGrpCompression;
    RadioButton         aRBCompressionLZW;
    RadioButton         aRBCompressionNone;
    OKButton            aBtnOK;
    CancelButton        aBtnCancel;
    HelpButton          aBtnHelp;

    FilterConfigItem*   pConfigItem;
    ResMgr*             pMgr;

    DECL_LINK( OK, void * );
    DECL_LINK( LEVEL1, void* );
    DECL_LINK( LEVEL2, void* );

public:
            DlgExportEPS( FltCallDialogParameter& rPara );
            ~DlgExportEPS();
};

#endif // _DLGEPS_HXX_
<|endoftext|>


<commit_before>// tankview.cpp : Defines the entry point for the application.
//

#include "tankview.h"
#include "Model.h"
#include "config.h"
#include "TextUtils.h"

// for the stupid debug openGLcount model uses
#ifdef DEBUG
int __beginendCount;
#endif

class Application : public SimpleDisplayEventCallbacks
{
public:
  Application();
  virtual void key ( int key, bool down, const ModiferKeys& mods );

  int run ( void );

protected:
  bool init ( void );
  void drawGridAndBounds ( void );
  void loadModels ( void );
  void drawModels ( void );

  SimpleDisplay display;
  SimpleDisplayCamera *camera;

  OBJModel base,turret,barrel,lTread,rTread;
  unsigned int red,green,blue,purple,black,current;

  bool moveKeysDown[4];
};

const char* convertPath ( const char* path )
{
  static std::string temp;
  if (!path)
    return NULL;

  temp = path;
#ifdef _WIN32
  temp = TextUtils::replace_all(temp,std::string("/"),std::string("\\"));
#endif

  return temp.c_str();
}

Application::Application()
{
  camera = NULL;

  for (int i = 0; i<4; i++)
    moveKeysDown[i] = false;
}

void Application::drawGridAndBounds ( void )
{
  glDisable(GL_TEXTURE_2D);
  glDisable(GL_LIGHTING);

  glBegin(GL_LINES);
  // axis markers
  glColor4f(1,0,0,0.25f);
  glVertex3f(0,0,0);
  glVertex3f(1,0,0);

  glColor4f(0,1,0,0.25f);
  glVertex3f(0,0,0);
  glVertex3f(0,1,0);

  glColor4f(0,0,1,0.25f);
  glVertex3f(0,0,0);
  glVertex3f(0,0,1);

  // grid
  glColor4f(1,1,1,0.125f);
  float grid = 10.0f;
  float increment = 0.25f;

  for( float i = -grid; i <= grid; i += increment )
  {
    glVertex3f(i,grid,0);
    glVertex3f(i,-grid,0);
    glVertex3f(grid,i,0);
    glVertex3f(-grid,i,0);
  }

  // tank bbox
  glColor4f(0,1,1,0.25f);
  float width = 1.4f;
  float height = 2.05f;
  float front = 4.94f;
  float back = -3.10f;

  // front
  glVertex3f(front,-width,0);
  glVertex3f(front,width,0);
  glVertex3f(front,width,0);
  glVertex3f(front,width,height);
  glVertex3f(front,-width,height);
  glVertex3f(front,width,height);
  glVertex3f(front,-width,0);
  glVertex3f(front,-width,height);

  // back
  glVertex3f(back,-width,0);
  glVertex3f(back,width,0);
  glVertex3f(back,width,0);
  glVertex3f(back,width,height);
  glVertex3f(back,-width,height);
  glVertex3f(back,width,height);
  glVertex3f(back,-width,0);
  glVertex3f(back,-width,height);

  // sides
  glVertex3f(back,-width,0);
  glVertex3f(front,-width,0);

  glVertex3f(back,width,0);
  glVertex3f(front,width,0);

  glVertex3f(back,-width,height);
  glVertex3f(front,-width,height);

  glVertex3f(back,width,height);
  glVertex3f(front,width,height);
  glEnd();

  glColor4f(1,1,1,1);
}

void Application::loadModels ( void )
{
  barrel.read(convertPath("./data/geometry/tank/std/barrel.obj"));
  base.read(convertPath("./data/geometry/tank/std/body.obj"));
  turret.read(convertPath("./data/geometry/tank/std/turret.obj"));
  lTread.read(convertPath("./data/geometry/tank/std/lcasing.obj"));
  rTread.read(convertPath("/.data/geometry/tank/std/rcasing.obj"));

  red = display.loadImage(convertPath("./data/skins/red/tank.png"));
  green = display.loadImage(convertPath("./data/skins/green/tank.png"));
  blue = display.loadImage(convertPath("./data/skins/blue/tank.png"));
  purple = display.loadImage(convertPath("./data/skins/purple/tank.png"));
  black = display.loadImage(convertPath("./data/skins/rogue/tank.png"));

  current = green;
}

void Application::drawModels ( void )
{
  base.draw();
  lTread.draw();
  rTread.draw();
  turret.draw();
  barrel.draw();
}

bool Application::init ( void )
{
  display.addEventCallback(this);

  camera = new SimpleDisplayCamera;

  // load up the models
  loadModels();

  camera->rotateGlob(-90,1,0,0);
  camera->moveLoc(0,0,-20);
  camera->moveLoc(0,1.5f,0);

  //setup light
  glEnable(GL_LIGHTING);
  glEnable(GL_LIGHT0);

  float v[4] = {1};
  v[0] = v[1] = v[2] = 0.125f;
  glLightfv (GL_LIGHT0, GL_AMBIENT,v);
  v[0] = v[1] = v[2] = 0.75f;
  glLightfv (GL_LIGHT0, GL_DIFFUSE,v);
  v[0] = v[1] = v[2] = 0;
  glLightfv (GL_LIGHT0, GL_SPECULAR,v);

  return true;
}

int Application::run ( void )
{
  if (!init())
    return -1;

  while (display.update())
  {
    display.clear();
    camera->applyView();

    drawGridAndBounds();

    glEnable(GL_TEXTURE_2D);
    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);

    float v[4] = {1};
    v[0] = v[1] = v[2] = 20.f;
    glLightfv (GL_LIGHT0, GL_POSITION,v);

    display.bindImage(current);
    drawModels();

    camera->removeView();
    display.flip();
    display.yeld(0.5f);
  }

  display.kill();

  return 0;
}

void Application::key ( int key, bool down, const ModiferKeys& mods )
{
  if (!camera || !down)
    return;

  switch(key)
  {
  case SD_KEY_ESCAPE:
    display.quit();
    break;

  case SD_KEY_UP:
    if (mods.alt)
      camera->rotateLoc(1,1,0,0);
    else if (mods.ctl)
      camera->moveLoc(0,0,0.125f);
    else
      camera->moveLoc(0,0.125f,0);
    break;

  case SD_KEY_DOWN:
    if (mods.alt)
      camera->rotateLoc(-1,1,0,0);
    else if (mods.ctl)
      camera->moveLoc(0,0,-0.125f);
    else
      camera->moveLoc(0,-0.125f,0);
    break;

  case SD_KEY_LEFT:
    if (mods.alt)
      camera->rotateLoc(1,0,1,0);
    else
      camera->moveLoc(-0.125f,0,0);
    break;

  case SD_KEY_RIGHT:
    if (mods.alt)
      camera->rotateLoc(-1,0,1,0);
    else
      camera->moveLoc(0.125f,0,0);
    break;

  case SD_KEY_F1:
    current = red;
    break;
  case SD_KEY_F2:
    current = green;
    break;
  case SD_KEY_F3:
    current = blue;
    break;
  case SD_KEY_F4:
    current = purple;
    break;
  case SD_KEY_F5:
    current = black;
    break;
  }
}

#ifdef _WIN32
int WINAPI WinMain( HINSTANCE hInst, HINSTANCE hPInst, LPSTR lpCmdLine, int nShowCmd )
{
#else
int main ( int argc, char* argv[] )
{
#endif

  Application app;
  return app.run();
}

// Local Variables: ***
// mode:C++ ***
// tab-width: 8 ***
// c-basic-offset: 2 ***
// indent-tabs-mode: t ***
// End: ***
// ex: shiftwidth=2 tabstop=8<commit_msg>give the window a name. move faster. be more greedy.<commit_after>// tankview.cpp : Defines the entry point for the application.
//

#include "tankview.h"
#include "Model.h"
#include "config.h"
#include "TextUtils.h"

// for the stupid debug openGLcount model uses
#ifdef DEBUG
int __beginendCount;
#endif

class Application : public SimpleDisplayEventCallbacks
{
public:
  Application();
  virtual void key ( int key, bool down, const ModiferKeys& mods );

  int run ( void );

protected:
  bool init ( void );
  void drawGridAndBounds ( void );
  void loadModels ( void );
  void drawModels ( void );

  SimpleDisplay display;
  SimpleDisplayCamera *camera;

  OBJModel base,turret,barrel,lTread,rTread;
  unsigned int red,green,blue,purple,black,current;

  bool moveKeysDown[4];
};

const char* convertPath ( const char* path )
{
  static std::string temp;
  if (!path)
    return NULL;

  temp = path;
#ifdef _WIN32
  temp = TextUtils::replace_all(temp,std::string("/"),std::string("\\"));
#endif

  return temp.c_str();
}

Application::Application() : display(800,600,false,"tankview")
{
  camera = NULL;

  for (int i = 0; i<4; i++)
    moveKeysDown[i] = false;
}

void Application::drawGridAndBounds ( void )
{
  glDisable(GL_TEXTURE_2D);
  glDisable(GL_LIGHTING);

  glBegin(GL_LINES);
  // axis markers
  glColor4f(1,0,0,0.25f);
  glVertex3f(0,0,0);
  glVertex3f(1,0,0);

  glColor4f(0,1,0,0.25f);
  glVertex3f(0,0,0);
  glVertex3f(0,1,0);

  glColor4f(0,0,1,0.25f);
  glVertex3f(0,0,0);
  glVertex3f(0,0,1);

  // grid
  glColor4f(1,1,1,0.125f);
  float grid = 10.0f;
  float increment = 0.25f;

  for( float i = -grid; i <= grid; i += increment )
  {
    glVertex3f(i,grid,0);
    glVertex3f(i,-grid,0);
    glVertex3f(grid,i,0);
    glVertex3f(-grid,i,0);
  }

  // tank bbox
  glColor4f(0,1,1,0.25f);
  float width = 1.4f;
  float height = 2.05f;
  float front = 4.94f;
  float back = -3.10f;

  // front
  glVertex3f(front,-width,0);
  glVertex3f(front,width,0);
  glVertex3f(front,width,0);
  glVertex3f(front,width,height);
  glVertex3f(front,-width,height);
  glVertex3f(front,width,height);
  glVertex3f(front,-width,0);
  glVertex3f(front,-width,height);

  // back
  glVertex3f(back,-width,0);
  glVertex3f(back,width,0);
  glVertex3f(back,width,0);
  glVertex3f(back,width,height);
  glVertex3f(back,-width,height);
  glVertex3f(back,width,height);
  glVertex3f(back,-width,0);
  glVertex3f(back,-width,height);

  // sides
  glVertex3f(back,-width,0);
  glVertex3f(front,-width,0);

  glVertex3f(back,width,0);
  glVertex3f(front,width,0);

  glVertex3f(back,-width,height);
  glVertex3f(front,-width,height);

  glVertex3f(back,width,height);
  glVertex3f(front,width,height);
  glEnd();

  glColor4f(1,1,1,1);
}

void Application::loadModels ( void )
{
  barrel.read(convertPath("./data/geometry/tank/std/barrel.obj"));
  base.read(convertPath("./data/geometry/tank/std/body.obj"));
  turret.read(convertPath("./data/geometry/tank/std/turret.obj"));
  lTread.read(convertPath("./data/geometry/tank/std/lcasing.obj"));
  rTread.read(convertPath("/.data/geometry/tank/std/rcasing.obj"));

  red = display.loadImage(convertPath("./data/skins/red/tank.png"));
  green = display.loadImage(convertPath("./data/skins/green/tank.png"));
  blue = display.loadImage(convertPath("./data/skins/blue/tank.png"));
  purple = display.loadImage(convertPath("./data/skins/purple/tank.png"));
  black = display.loadImage(convertPath("./data/skins/rogue/tank.png"));

  current = green;
}

void Application::drawModels ( void )
{
  base.draw();
  lTread.draw();
  rTread.draw();
  turret.draw();
  barrel.draw();
}

bool Application::init ( void )
{
  display.addEventCallback(this);

  camera = new SimpleDisplayCamera;

  // load up the models
  loadModels();

  camera->rotateGlob(-90,1,0,0);
  camera->moveLoc(0,0,-20);
  camera->moveLoc(0,1.5f,0);

  //setup light
  glEnable(GL_LIGHTING);
  glEnable(GL_LIGHT0);

  float v[4] = {1};
  v[0] = v[1] = v[2] = 0.125f;
  glLightfv (GL_LIGHT0, GL_AMBIENT,v);
  v[0] = v[1] = v[2] = 0.75f;
  glLightfv (GL_LIGHT0, GL_DIFFUSE,v);
  v[0] = v[1] = v[2] = 0;
  glLightfv (GL_LIGHT0, GL_SPECULAR,v);

  return true;
}

int Application::run ( void )
{
  if (!init())
    return -1;

  while (display.update())
  {
    display.clear();
    camera->applyView();

    drawGridAndBounds();

    glEnable(GL_TEXTURE_2D);
    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);

    float v[4] = {1};
    v[0] = v[1] = v[2] = 20.f;
    glLightfv (GL_LIGHT0, GL_POSITION,v);

    display.bindImage(current);
    drawModels();

    camera->removeView();
    display.flip();
    //display.yeld(0.5f);
  }

  display.kill();

  return 0;
}

void Application::key ( int key, bool down, const ModiferKeys& mods )
{
  if (!camera || !down)
    return;

  switch(key)
  {
  case SD_KEY_ESCAPE:
    display.quit();
    break;

  case SD_KEY_UP:
    if (mods.alt)
      camera->rotateLoc(2.5f,1,0,0);
    else if (mods.ctl)
      camera->moveLoc(0,0,0.25f);
    else
      camera->moveLoc(0,0.25f,0);
    break;

  case SD_KEY_DOWN:
    if (mods.alt)
      camera->rotateLoc(-2.5f,1,0,0);
    else if (mods.ctl)
      camera->moveLoc(0,0,-0.25f);
    else
      camera->moveLoc(0,-0.25f,0);
    break;

  case SD_KEY_LEFT:
    if (mods.alt)
      camera->rotateLoc(2.5f,0,1,0);
    else
      camera->moveLoc(-0.25f,0,0);
    break;

  case SD_KEY_RIGHT:
    if (mods.alt)
      camera->rotateLoc(-2.5f,0,1,0);
    else
      camera->moveLoc(0.25f,0,0);
    break;

  case SD_KEY_F1:
    current = red;
    break;
  case SD_KEY_F2:
    current = green;
    break;
  case SD_KEY_F3:
    current = blue;
    break;
  case SD_KEY_F4:
    current = purple;
    break;
  case SD_KEY_F5:
    current = black;
    break;
  }
}

#ifdef _WIN32
int WINAPI WinMain( HINSTANCE hInst, HINSTANCE hPInst, LPSTR lpCmdLine, int nShowCmd )
{
#else
int main ( int argc, char* argv[] )
{
#endif

  Application app;
  return app.run();
}

// Local Variables: ***
// mode:C++ ***
// tab-width: 8 ***
// c-basic-offset: 2 ***
// indent-tabs-mode: t ***
// End: ***
// ex: shiftwidth=2 tabstop=8<|endoftext|>


<commit_before>// @(#)root/rootd:$Name:  $:$Id: net.cxx,v 1.5 2000/10/02 11:10:51 rdm Exp $
// Author: Fons Rademakers   12/08/97

/*************************************************************************
 * Copyright (C) 1995-2000, Rene Brun and Fons Rademakers.               *
 * All rights reserved.                                                  *
 *                                                                       *
 * For the licensing terms see $ROOTSYS/LICENSE.                         *
 * For the list of contributors see $ROOTSYS/README/CREDITS.             *
 *************************************************************************/

//////////////////////////////////////////////////////////////////////////
//                                                                      //
// net                                                                  //
//                                                                      //
// Set of network routines for rootd daemon process.                    //
//                                                                      //
//////////////////////////////////////////////////////////////////////////

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <errno.h>

#if defined(linux)
#   include <features.h>
#   if __GNU_LIBRARY__ == 6
#      ifndef R__GLIBC
#         define R__GLIBC
#      endif
#   endif
#endif

#include "rootdp.h"

#if defined(R__GLIBC) || (defined(__FreeBSD__) && defined(__alpha__))
#   define USE_SOCKLEN_T
#endif

double  gBytesSent = 0;
double  gBytesRecv = 0;

static char openhost[256] = "????";

static int                tcp_srv_sock;
static struct sockaddr_in tcp_srv_addr;
static struct sockaddr_in tcp_cli_addr;

//______________________________________________________________________________
static void SigPipe(int)
{
   // After SO_KEEPALIVE times out we probably get a SIGPIPE.

   ErrorInfo("SigPipe: got a SIGPIPE");
   RootdClose();
   exit(1);
}

//______________________________________________________________________________
static int Sendn(int sock, const void *buffer, int length)
{
   // Send exactly length bytes from buffer.

   if (sock < 0) return -1;

   int n, nsent = 0;
   const char *buf = (const char *)buffer;

   for (n = 0; n < length; n += nsent) {
      if ((nsent = send(sock, buf+n, length-n, 0)) <= 0)
         return nsent;
   }

   gBytesSent += n;

   return n;
}

//______________________________________________________________________________
static int Recvn(int sock, void *buffer, int length)
{
   // Receive exactly length bytes into buffer. Returns number of bytes
   // received. Returns -1 in case of error.

   if (sock < 0) return -1;

   int n, nrecv = 0;
   char *buf = (char *)buffer;

   for (n = 0; n < length; n += nrecv) {
      while ((nrecv = recv(sock, buf+n, length-n, 0)) == -1 && GetErrno() == EINTR)
         ResetErrno();     // probably a SIGCLD that was caught
      if (nrecv < 0)
         return nrecv;
      else if (nrecv == 0)
         break;         // EOF
   }

   gBytesRecv += n;

   return n;
}

//______________________________________________________________________________
void NetInit(const char *service, int port)
{
   // Initialize the network connection for the server, when it has *not*
   // been invoked by inetd.

   // We weren't started by a master daemon.
   // We have to create a socket ourselves and bind our well-known
   // address to it.

   memset(&tcp_srv_addr, 0, sizeof(tcp_srv_addr));
   tcp_srv_addr.sin_family      = AF_INET;
   tcp_srv_addr.sin_addr.s_addr = htonl(INADDR_ANY);

   if (service) {

      if (port > 0)
         tcp_srv_addr.sin_port = htons(port);
      else {
         struct servent *sp;
         if ((sp = getservbyname(service, "tcp")) == 0)
            ErrorFatal(kErrFatal, "NetInit: unknown service: %s/tcp", service);
         tcp_srv_addr.sin_port = sp->s_port;
      }

   } else {

      if (port <= 0)
         ErrorFatal(kErrFatal, "NetInit: must specify either service or port");
      tcp_srv_addr.sin_port = htons(port);

   }

   // Create the socket and bind our local address so that any client can
   // send to us.

   if ((tcp_srv_sock = socket(AF_INET, SOCK_STREAM, 0)) < 0)
      ErrorSys(kErrFatal, "NetInit: can't create socket");

   if (bind(tcp_srv_sock, (struct sockaddr *) &tcp_srv_addr,
            sizeof(tcp_srv_addr)) < 0)
      ErrorSys(kErrFatal, "NetInit: can't bind local address");

   // And set the listen parameter, telling the system that we're
   // ready to accept incoming connection requests.

   listen(tcp_srv_sock, 5);

   if (gDebug > 0)
      ErrorInfo("NetInit: socket %d listening on port %d", tcp_srv_sock,
                ntohs(tcp_srv_addr.sin_port));
}

//______________________________________________________________________________
int NetOpen(int inetdflag)
{
   // Initialize the server's end.
   // We are passed a flag that says whether or not we are started
   // by a "master daemon" such as inetd. A master daemon will have
   // already waited for a message to arrive for us and will have
   // already set up the connection to the client. If we weren't
   // started by a master daemon, then we must wait for a client's
   // request to arrive.

   if (inetdflag) {

      // When we're fired up by inetd, file decriptors 0, 1 and 2
      // are sockets to the client.

      gSockFd = 0;

      if (gDebug > 0) {
#ifdef _AIX
         size_t clilen = sizeof(tcp_cli_addr);
#elif defined(USE_SOCKLEN_T)
         socklen_t clilen = sizeof(tcp_cli_addr);
#else
         int clilen = sizeof(tcp_cli_addr);
#endif
         if (!getpeername(gSockFd, (struct sockaddr *)&tcp_cli_addr, &clilen)) {
            struct hostent *hp;
            if ((hp = gethostbyaddr((const char *)&tcp_cli_addr.sin_addr,
                                    sizeof(tcp_cli_addr.sin_addr), AF_INET)))
               strcpy(openhost, hp->h_name);
            else {
               struct in_addr *host_addr = (struct in_addr*)&tcp_cli_addr.sin_addr;
               strcpy(openhost, inet_ntoa(*host_addr));
            }
         }
         ErrorInfo("NetOpen: accepted connection from host %s", openhost);

         ErrorInfo("NetOpen: connection established via socket %d", gSockFd);
      }

      return 0;

   }

   // For the concurrent server that's not initiated by inetd,
   // we have to wait for a connection request to arrive, then
   // fork a child to handle the client's request.
   // Beware that the accept() can be interrupted, such as by
   // a previously spawned child process that has terminated
   // (for which we caught the SIGCLD signal).

again:
#ifdef _AIX
   size_t clilen = sizeof(tcp_cli_addr);
#elif defined(USE_SOCKLEN_T)
   socklen_t clilen = sizeof(tcp_cli_addr);
#else
   int clilen = sizeof(tcp_cli_addr);
#endif
   int newsock = accept(tcp_srv_sock, (struct sockaddr *)&tcp_cli_addr, &clilen);
   if (newsock < 0) {
      if (GetErrno() == EINTR) {
         ResetErrno();
         goto again;   // probably a SIGCLD that was caught
      }
      ErrorSys(kErrFatal, "NetOpen: accept error");
   }

   if (gDebug > 0) {
      struct hostent *hp;
      if ((hp = gethostbyaddr((const char *)&tcp_cli_addr.sin_addr,
                              sizeof(tcp_cli_addr.sin_addr), AF_INET)))
         strcpy(openhost, hp->h_name);
      else {
         struct in_addr *host_addr = (struct in_addr*)&tcp_cli_addr.sin_addr;
         strcpy(openhost, inet_ntoa(*host_addr));
      }

      ErrorInfo("NetOpen: accepted connection from host %s", openhost);
   }

   // Fork a child process to handle the client's request.
   // The parent returns the child pid to the caller, which is
   // probably a concurrent server that'll call us again, to wait
   // for the next client request to this well-known port.

   int childpid;
   if ((childpid = fork()) < 0)
      ErrorSys(kErrFatal, "NetOpen: server can't fork");
   else if (childpid > 0) {    // parent
      close(newsock);
      return childpid;
   }

   // Child process continues here.
   // First close the original socket so that the parent
   // can accept any further requests that arrive there.
   // Then set "gSockFd" in our process to be the descriptor
   // that we are going to process.

   close(tcp_srv_sock);

   gSockFd = newsock;

   if (gDebug > 0)
      ErrorInfo("NetOpen: connection established via socket %d", gSockFd);

   return 0;
}

//______________________________________________________________________________
void NetClose()
{
   // Close the network connection.

   close(gSockFd);
   gSockFd = -1;

   if (gDebug > 0)
      ErrorInfo("NetClose: host = %s, fd = %d, file = %s", openhost, gSockFd,
                gFile);
}

//______________________________________________________________________________
void NetSetOptions()
{
   // Set some options for network socket.

   int val = 1;
   if (!setsockopt(gSockFd, IPPROTO_TCP, TCP_NODELAY, (char *)&val, sizeof(val))) {
       if (gDebug > 0) ErrorInfo("NetSetOptions: set TCP_NODELAY");
   }
   if (!setsockopt(gSockFd, SOL_SOCKET,  SO_KEEPALIVE, (char *)&val, sizeof(val))) {
      if (gDebug > 0) ErrorInfo("NetSetOptions: set SO_KEEPALIVE");
      signal(SIGPIPE, SigPipe);   // handle SO_KEEPALIVE failure
   }

   val = 65536;
   if (!setsockopt(gSockFd, SOL_SOCKET,  SO_SNDBUF,    (char *)&val, sizeof(val))) {
      if (gDebug > 0) ErrorInfo("NetSetOptions: set SO_SNDBUF %d", val);
   }
   if (!setsockopt(gSockFd, SOL_SOCKET,  SO_RCVBUF,    (char *)&val, sizeof(val))) {
      if (gDebug > 0) ErrorInfo("NetSetOptions: set SO_RCVBUF %d", val);
   }
}

//______________________________________________________________________________
int NetSendRaw(const void *buf, int len)
{
   // Send buffer of len bytes.

   if (gSockFd == -1) return -1;

   if (Sendn(gSockFd, buf, len) != len) {
      ErrorInfo("NetSendRaw: Sendn error");
      RootdClose();
      exit(1);
   }
   return len;
}

//______________________________________________________________________________
int NetSend(const void *buf, int len, EMessageTypes kind)
{
   // Send buffer of len bytes. Message will be of type "kind".

   int hdr[2];
   int hlen = sizeof(int) + len;
   hdr[0] = htonl(hlen);
   hdr[1] = htonl(kind);
   if (NetSendRaw(hdr, sizeof(hdr)) < 0)
      return -1;

   return NetSendRaw(buf, len);
}

//______________________________________________________________________________
int NetSend(int code, EMessageTypes kind)
{
   // Send integer. Message will be of type "kind".

   int hdr[3];
   int hlen = sizeof(int) + sizeof(int);
   hdr[0] = htonl(hlen);
   hdr[1] = htonl(kind);
   hdr[2] = htonl(code);
   return NetSendRaw(hdr, sizeof(hdr));
}

//______________________________________________________________________________
int NetSend(const char *msg, EMessageTypes kind)
{
   // Send a string. Message will be of type "kind".

   int len = 0;

   if (msg)
      len = strlen(msg)+1;

   return NetSend(msg, len, kind);
}

//______________________________________________________________________________
int NetSendAck()
{
   return NetSend(0, kROOTD_ACK);
}

//______________________________________________________________________________
int NetSendError(ERootdErrors err)
{
   return NetSend(err, kROOTD_ERR);
}

//______________________________________________________________________________
int NetRecvRaw(void *buf, int len)
{
   // Receive a buffer of maximum len bytes.

   if (gSockFd == -1) return -1;

   if (Recvn(gSockFd, buf, len) < 0) {
      ErrorInfo("NetRecvRaw: Recvn error");
      RootdClose();
      exit(1);
   }
   return len;
}

//______________________________________________________________________________
int NetRecv(void *&buf, int &len, EMessageTypes &kind)
{
   // Receive a buffer. Returns the newly allocated buffer, the length
   // of the buffer and message type in kind.

   int hdr[2];

   if (NetRecvRaw(hdr, sizeof(hdr)) < 0)
      return -1;

   len = ntohl(hdr[0]) - sizeof(int);
   kind = (EMessageTypes) ntohl(hdr[1]);
   if (len) {
      buf = new char* [len];
      return NetRecvRaw(buf, len);
   }
   buf = 0;
   return 0;
}

//______________________________________________________________________________
int NetRecv(char *msg, int len, EMessageTypes &kind)
{
   // Receive a string of maximum len length. Returns message type in kind.
   // Return value is msg length.

   int   mlen;
   char *buf;

   if (NetRecv((void *&)buf, mlen, kind) < 0)
      return -1;

   if (mlen == 0) {
      msg[0] = 0;
      return 0;
   } else if (mlen > len) {
      strncpy(msg, buf, len-1);
      msg[len-1] = 0;
      mlen = len;
   } else
      strcpy(msg, buf);

   delete [] buf;

   return mlen - 1;
}
<commit_msg>small mods for port to FreeBSD4.<commit_after>// @(#)root/rootd:$Name:  $:$Id: net.cxx,v 1.6 2000/12/01 14:22:26 rdm Exp $
// Author: Fons Rademakers   12/08/97

/*************************************************************************
 * Copyright (C) 1995-2000, Rene Brun and Fons Rademakers.               *
 * All rights reserved.                                                  *
 *                                                                       *
 * For the licensing terms see $ROOTSYS/LICENSE.                         *
 * For the list of contributors see $ROOTSYS/README/CREDITS.             *
 *************************************************************************/

//////////////////////////////////////////////////////////////////////////
//                                                                      //
// net                                                                  //
//                                                                      //
// Set of network routines for rootd daemon process.                    //
//                                                                      //
//////////////////////////////////////////////////////////////////////////

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <errno.h>

#if defined(linux)
#   include <features.h>
#   if __GNU_LIBRARY__ == 6
#      ifndef R__GLIBC
#         define R__GLIBC
#      endif
#   endif
#endif

#include "rootdp.h"

#if defined(__AIX) || (defined(__FreeBSD__) && !defined(__alpha__))
#   define USE_SIZE_T
#elif defined(R__GLIBC) || (defined(__FreeBSD__) && defined(__alpha__))
#   define USE_SOCKLEN_T
#endif

double  gBytesSent = 0;
double  gBytesRecv = 0;

static char openhost[256] = "????";

static int                tcp_srv_sock;
static struct sockaddr_in tcp_srv_addr;
static struct sockaddr_in tcp_cli_addr;

//______________________________________________________________________________
static void SigPipe(int)
{
   // After SO_KEEPALIVE times out we probably get a SIGPIPE.

   ErrorInfo("SigPipe: got a SIGPIPE");
   RootdClose();
   exit(1);
}

//______________________________________________________________________________
static int Sendn(int sock, const void *buffer, int length)
{
   // Send exactly length bytes from buffer.

   if (sock < 0) return -1;

   int n, nsent = 0;
   const char *buf = (const char *)buffer;

   for (n = 0; n < length; n += nsent) {
      if ((nsent = send(sock, buf+n, length-n, 0)) <= 0)
         return nsent;
   }

   gBytesSent += n;

   return n;
}

//______________________________________________________________________________
static int Recvn(int sock, void *buffer, int length)
{
   // Receive exactly length bytes into buffer. Returns number of bytes
   // received. Returns -1 in case of error.

   if (sock < 0) return -1;

   int n, nrecv = 0;
   char *buf = (char *)buffer;

   for (n = 0; n < length; n += nrecv) {
      while ((nrecv = recv(sock, buf+n, length-n, 0)) == -1 && GetErrno() == EINTR)
         ResetErrno();     // probably a SIGCLD that was caught
      if (nrecv < 0)
         return nrecv;
      else if (nrecv == 0)
         break;         // EOF
   }

   gBytesRecv += n;

   return n;
}

//______________________________________________________________________________
void NetInit(const char *service, int port)
{
   // Initialize the network connection for the server, when it has *not*
   // been invoked by inetd.

   // We weren't started by a master daemon.
   // We have to create a socket ourselves and bind our well-known
   // address to it.

   memset(&tcp_srv_addr, 0, sizeof(tcp_srv_addr));
   tcp_srv_addr.sin_family      = AF_INET;
   tcp_srv_addr.sin_addr.s_addr = htonl(INADDR_ANY);

   if (service) {

      if (port > 0)
         tcp_srv_addr.sin_port = htons(port);
      else {
         struct servent *sp;
         if ((sp = getservbyname(service, "tcp")) == 0)
            ErrorFatal(kErrFatal, "NetInit: unknown service: %s/tcp", service);
         tcp_srv_addr.sin_port = sp->s_port;
      }

   } else {

      if (port <= 0)
         ErrorFatal(kErrFatal, "NetInit: must specify either service or port");
      tcp_srv_addr.sin_port = htons(port);

   }

   // Create the socket and bind our local address so that any client can
   // send to us.

   if ((tcp_srv_sock = socket(AF_INET, SOCK_STREAM, 0)) < 0)
      ErrorSys(kErrFatal, "NetInit: can't create socket");

   if (bind(tcp_srv_sock, (struct sockaddr *) &tcp_srv_addr,
            sizeof(tcp_srv_addr)) < 0)
      ErrorSys(kErrFatal, "NetInit: can't bind local address");

   // And set the listen parameter, telling the system that we're
   // ready to accept incoming connection requests.

   listen(tcp_srv_sock, 5);

   if (gDebug > 0)
      ErrorInfo("NetInit: socket %d listening on port %d", tcp_srv_sock,
                ntohs(tcp_srv_addr.sin_port));
}

//______________________________________________________________________________
int NetOpen(int inetdflag)
{
   // Initialize the server's end.
   // We are passed a flag that says whether or not we are started
   // by a "master daemon" such as inetd. A master daemon will have
   // already waited for a message to arrive for us and will have
   // already set up the connection to the client. If we weren't
   // started by a master daemon, then we must wait for a client's
   // request to arrive.

   if (inetdflag) {

      // When we're fired up by inetd, file decriptors 0, 1 and 2
      // are sockets to the client.

      gSockFd = 0;

      if (gDebug > 0) {
#if defined(USE_SIZE_T)
         size_t clilen = sizeof(tcp_cli_addr);
#elif defined(USE_SOCKLEN_T)
         socklen_t clilen = sizeof(tcp_cli_addr);
#else
         int clilen = sizeof(tcp_cli_addr);
#endif
         if (!getpeername(gSockFd, (struct sockaddr *)&tcp_cli_addr, &clilen)) {
            struct hostent *hp;
            if ((hp = gethostbyaddr((const char *)&tcp_cli_addr.sin_addr,
                                    sizeof(tcp_cli_addr.sin_addr), AF_INET)))
               strcpy(openhost, hp->h_name);
            else {
               struct in_addr *host_addr = (struct in_addr*)&tcp_cli_addr.sin_addr;
               strcpy(openhost, inet_ntoa(*host_addr));
            }
         }
         ErrorInfo("NetOpen: accepted connection from host %s", openhost);

         ErrorInfo("NetOpen: connection established via socket %d", gSockFd);
      }

      return 0;

   }

   // For the concurrent server that's not initiated by inetd,
   // we have to wait for a connection request to arrive, then
   // fork a child to handle the client's request.
   // Beware that the accept() can be interrupted, such as by
   // a previously spawned child process that has terminated
   // (for which we caught the SIGCLD signal).

again:
#if defined(USE_SIZE_T)
   size_t clilen = sizeof(tcp_cli_addr);
#elif defined(USE_SOCKLEN_T)
   socklen_t clilen = sizeof(tcp_cli_addr);
#else
   int clilen = sizeof(tcp_cli_addr);
#endif
   int newsock = accept(tcp_srv_sock, (struct sockaddr *)&tcp_cli_addr, &clilen);
   if (newsock < 0) {
      if (GetErrno() == EINTR) {
         ResetErrno();
         goto again;   // probably a SIGCLD that was caught
      }
      ErrorSys(kErrFatal, "NetOpen: accept error");
   }

   if (gDebug > 0) {
      struct hostent *hp;
      if ((hp = gethostbyaddr((const char *)&tcp_cli_addr.sin_addr,
                              sizeof(tcp_cli_addr.sin_addr), AF_INET)))
         strcpy(openhost, hp->h_name);
      else {
         struct in_addr *host_addr = (struct in_addr*)&tcp_cli_addr.sin_addr;
         strcpy(openhost, inet_ntoa(*host_addr));
      }

      ErrorInfo("NetOpen: accepted connection from host %s", openhost);
   }

   // Fork a child process to handle the client's request.
   // The parent returns the child pid to the caller, which is
   // probably a concurrent server that'll call us again, to wait
   // for the next client request to this well-known port.

   int childpid;
   if ((childpid = fork()) < 0)
      ErrorSys(kErrFatal, "NetOpen: server can't fork");
   else if (childpid > 0) {    // parent
      close(newsock);
      return childpid;
   }

   // Child process continues here.
   // First close the original socket so that the parent
   // can accept any further requests that arrive there.
   // Then set "gSockFd" in our process to be the descriptor
   // that we are going to process.

   close(tcp_srv_sock);

   gSockFd = newsock;

   if (gDebug > 0)
      ErrorInfo("NetOpen: connection established via socket %d", gSockFd);

   return 0;
}

//______________________________________________________________________________
void NetClose()
{
   // Close the network connection.

   close(gSockFd);
   gSockFd = -1;

   if (gDebug > 0)
      ErrorInfo("NetClose: host = %s, fd = %d, file = %s", openhost, gSockFd,
                gFile);
}

//______________________________________________________________________________
void NetSetOptions()
{
   // Set some options for network socket.

   int val = 1;
   if (!setsockopt(gSockFd, IPPROTO_TCP, TCP_NODELAY, (char *)&val, sizeof(val))) {
       if (gDebug > 0) ErrorInfo("NetSetOptions: set TCP_NODELAY");
   }
   if (!setsockopt(gSockFd, SOL_SOCKET,  SO_KEEPALIVE, (char *)&val, sizeof(val))) {
      if (gDebug > 0) ErrorInfo("NetSetOptions: set SO_KEEPALIVE");
      signal(SIGPIPE, SigPipe);   // handle SO_KEEPALIVE failure
   }

   val = 65536;
   if (!setsockopt(gSockFd, SOL_SOCKET,  SO_SNDBUF,    (char *)&val, sizeof(val))) {
      if (gDebug > 0) ErrorInfo("NetSetOptions: set SO_SNDBUF %d", val);
   }
   if (!setsockopt(gSockFd, SOL_SOCKET,  SO_RCVBUF,    (char *)&val, sizeof(val))) {
      if (gDebug > 0) ErrorInfo("NetSetOptions: set SO_RCVBUF %d", val);
   }
}

//______________________________________________________________________________
int NetSendRaw(const void *buf, int len)
{
   // Send buffer of len bytes.

   if (gSockFd == -1) return -1;

   if (Sendn(gSockFd, buf, len) != len) {
      ErrorInfo("NetSendRaw: Sendn error");
      RootdClose();
      exit(1);
   }
   return len;
}

//______________________________________________________________________________
int NetSend(const void *buf, int len, EMessageTypes kind)
{
   // Send buffer of len bytes. Message will be of type "kind".

   int hdr[2];
   int hlen = sizeof(int) + len;
   hdr[0] = htonl(hlen);
   hdr[1] = htonl(kind);
   if (NetSendRaw(hdr, sizeof(hdr)) < 0)
      return -1;

   return NetSendRaw(buf, len);
}

//______________________________________________________________________________
int NetSend(int code, EMessageTypes kind)
{
   // Send integer. Message will be of type "kind".

   int hdr[3];
   int hlen = sizeof(int) + sizeof(int);
   hdr[0] = htonl(hlen);
   hdr[1] = htonl(kind);
   hdr[2] = htonl(code);
   return NetSendRaw(hdr, sizeof(hdr));
}

//______________________________________________________________________________
int NetSend(const char *msg, EMessageTypes kind)
{
   // Send a string. Message will be of type "kind".

   int len = 0;

   if (msg)
      len = strlen(msg)+1;

   return NetSend(msg, len, kind);
}

//______________________________________________________________________________
int NetSendAck()
{
   return NetSend(0, kROOTD_ACK);
}

//______________________________________________________________________________
int NetSendError(ERootdErrors err)
{
   return NetSend(err, kROOTD_ERR);
}

//______________________________________________________________________________
int NetRecvRaw(void *buf, int len)
{
   // Receive a buffer of maximum len bytes.

   if (gSockFd == -1) return -1;

   if (Recvn(gSockFd, buf, len) < 0) {
      ErrorInfo("NetRecvRaw: Recvn error");
      RootdClose();
      exit(1);
   }
   return len;
}

//______________________________________________________________________________
int NetRecv(void *&buf, int &len, EMessageTypes &kind)
{
   // Receive a buffer. Returns the newly allocated buffer, the length
   // of the buffer and message type in kind.

   int hdr[2];

   if (NetRecvRaw(hdr, sizeof(hdr)) < 0)
      return -1;

   len = ntohl(hdr[0]) - sizeof(int);
   kind = (EMessageTypes) ntohl(hdr[1]);
   if (len) {
      buf = new char* [len];
      return NetRecvRaw(buf, len);
   }
   buf = 0;
   return 0;
}

//______________________________________________________________________________
int NetRecv(char *msg, int len, EMessageTypes &kind)
{
   // Receive a string of maximum len length. Returns message type in kind.
   // Return value is msg length.

   int   mlen;
   char *buf;

   if (NetRecv((void *&)buf, mlen, kind) < 0)
      return -1;

   if (mlen == 0) {
      msg[0] = 0;
      return 0;
   } else if (mlen > len) {
      strncpy(msg, buf, len-1);
      msg[len-1] = 0;
      mlen = len;
   } else
      strcpy(msg, buf);

   delete [] buf;

   return mlen - 1;
}
<|endoftext|>