<commit_before>
// (c) COPYRIGHT URI/MIT 1998
// Please read the full copyright statement in the file COPYRIGHT.
//
// Authors:
//      jhrg,jimg       James Gallagher (jgallagher@gso.uri.edu)

// This file contains various functions for use with/in constraint
// expressions.

// $Log: ce_functions.cc,v $
// Revision 1.1  1998/09/17 17:44:14  jimg
// Created
//

#include <iostream>
#include <strstream>
#include <string>
#include <algo.h>

#include <String.h>

#include "BaseType.h"
#include "DDS.h"
#include "date_proc.h"

static string
extract_argument(BaseType *arg)
{
#if 0
    if (arg->type() != dods_string_c)
	throw wrong_argument_type;
#endif

    // Use String until conversion of String to string is complete. 9/3/98
    // jhrg
    String *sp = 0;
    arg->buf2val((void **)&sp);
    string s = (const char *)(*sp);
    delete sp;

    cerr << "Argument value: `" << s << "'" << endl;

    return s;
}

// Parse the following notations: yyyy/ddd, yyyy/mm/dd, yyyymmdd, yyyyddd.
//
// Return the Julian day.

static long
get_julian_day(string date)
{
    int year, month, day;

    // Does the date have slashes?
    if (date.find("/") != string::npos) {
	istrstream iss(date.c_str());
	char slash;

	iss >> year;
	iss >> slash;
	iss >> month;		// might read month or day, check below
	iss >> slash;
	if (slash && iss) {	// must have month and days
	    iss >> day;
	}
	else {			// must have only year and day
	    day = month;
	    days_to_month_day(year, day, &month, &day);
	}

	return julian_day(year, month, day);
    }
    else {			// no slashes
	assert("Non-slash version of date not implemented" && false);
    }
}

// This function reads variables from the current instance of a sequence and,
// from them, computes the Julian day. That value is then used in date_func as
// part of the comparison with the user-supplied date or date range.

static long
read_julian_day(DDS &dds)
{
    BaseType *day_var = dds.var("JPL_Pathfinder.day");
    int day_number;
    int *day_number_p = &day_number;
    day_var->buf2val((void **)&day_number_p);

    BaseType *year_var = dds.var("JPL_Pathfinder.year");
    int year;
    int *year_p = &year;
    year_var->buf2val((void **)&year_p);

    int month;			// The jpl pathfinder catalog uses year/day.

    days_to_month_day(year, day_number, &month, &day_number);

    return julian_day(year, month, day_number);
}

// Select sequence elements based on date. The sequence is assumed to have
// year, month and day information accessible.
//
// Date uses the following notation: yyyyddd, yyyy/ddd, yyddd, yy/ddd,
// yyyymmdd, yyyy/mm/dd, yymmdd, yy/mm/dd.
//
// Semantics: One argument: return true for dates that match exactly.
// Two arguments: return true for dates that fall within the (inclusive)
// interval.
//
// Algorithm: Convert dates (arguments) to days since 1 AD and compare to
// dates from the sequence similarly converted.
//
// 9/3/98 jhrg

bool
func_date(int argc, BaseType *argv[], DDS &dds)
{
    string date1, date2 = "";
    long days1, days2 = 0;

#if 0
    if (argc < 1 || argc > 2)
	throw wrong_number_of_arguments;
#endif

    date1 = extract_argument(argv[0]);
    days1 = get_julian_day(date1);
    if (argc == 2) {
	date2 = extract_argument(argv[1]);
	days2 = get_julian_day(date2);
    }

    cerr << date1 << ": " << days1 << endl;
    if (argc == 2)
	cerr << date2 << ": " << days2 << endl;

    long jday = read_julian_day(dds);
    cerr << "jday: " << jday << endl;

    bool res;
    if (argc == 2)
	res = days2 >= jday && days1 <= jday;
    else
	res = days1 == jday;

    cerr << "Result: " << res << endl;
    return res;
}
<commit_msg>Recoded the projection and selection date functions for jdate. Added new functions for ymd dates and changed the overall organization a bit; it now tries to isolate the information specific to a particular dataset and it now uses the new DODS_Date objects for date conversions.<commit_after>
// (c) COPYRIGHT URI/MIT 1998
// Please read the full copyright statement in the file COPYRIGHT.
//
// Authors:
//      jhrg,jimg       James Gallagher (jgallagher@gso.uri.edu)

// This file contains various functions for use with/in constraint
// expressions.

// $Log: ce_functions.cc,v $
// Revision 1.2  1998/11/10 17:53:45  jimg
// Recoded the projection and selection date functions for jdate. Added new
// functions for ymd dates and changed the overall organization a bit; it now
// tries to isolate the information specific to a particular dataset and it now
// uses the new DODS_Date objects for date conversions.
//
// Revision 1.1  1998/09/17 17:44:14  jimg
// Created
//

#include <iostream>
#include <strstream>
#include <string>
#include <algo.h>

#include <String.h>

#include "BaseType.h"
#include "Str.h"
#include "Structure.h"
#include "Sequence.h"
#include "FFStr.h"
#include "DDS.h"
#include "Error.h"

#include "date_proc.h"
#include "DODS_Date.h"
#include "debug.h"

// Special for the JPL Pathfinder dataset. Read the sequence variables and
// build a DODS_Date object.

DODS_Date
DODS_Date_jpl_pathfinder(DDS &dds)
{
    BaseType *day_var = dds.var("JPL_Pathfinder.day");
    int day_number;
    int *day_number_p = &day_number;
    day_var->buf2val((void **)&day_number_p);

    BaseType *year_var = dds.var("JPL_Pathfinder.year");
    int year;
    int *year_p = &year;
    year_var->buf2val((void **)&year_p);

    DODS_Date d(year, day_number);

    return d;
}

// Select sequence elements based on date. The sequence is assumed to have
// year, month and day information accessible.
//
// Date uses the following notation: yyyy/ddd, yyyy/mm/dd.
//
// Semantics: One argument: return true for dates that match exactly.
// Two arguments: return true for dates that fall within the (inclusive)
// interval.
//
// Algorithm: Convert dates (arguments) to days since 1 AD and compare to
// dates from the sequence similarly converted.
//
// 9/3/98 jhrg

bool
func_date(int argc, BaseType *argv[], DDS &dds)
{
    if (argc < 1 || argc > 2)
	throw Error(malformed_expr,
		    "Wrong number of arguments to a projection function.");

    DODS_Date d1(argv[0]);
    DODS_Date d2;
    if (argc == 2) {
	d2.set_date(argv[1]);
    }

    DODS_Date current = DODS_Date_jpl_pathfinder(dds);

    bool res;
    if (argc == 2)
	res = d2 >= current && d1 <= current;
    else
	res = d1 == current;

    DBG(cerr << "Result: " << res << endl);
    return res;
}

bool
sel_dods_jdate(int argc, BaseType *argv[], DDS &dds)
{
    if (argc != 0)
	throw Error(malformed_expr,
		  "Wrong number of arguments to internal selection function.\n\
Please report this error.");

    DBG(cerr << "Entering" << endl);

    DODS_Date new_date = DODS_Date_jpl_pathfinder(dds);

    // Stuff the yyyy/ddd string into DODS_JDate.
    Str *dods_jdate = (Str*)dds.var("DODS_JDate");
    String s = new_date.yd_date().data();
    dods_jdate->val2buf(&s);

    DBG(dods_jdate->print_val(cerr, "    ", true));
    DBG(cerr << "Exiting (" << dods_jdate->name() << " [" \
	<< dods_jdate << "])" << endl);

    return true;
}

void
proj_dods_jdate(int argc, BaseType *argv[], DDS &dds)
{
    if (argc < 0 || argc > 1)
	throw Error(malformed_expr,
		  "Wrong number of arguments to projection function.\n\
Expected zero or one arguments.");

    // Create the new variable

    Str *dods_jdate = new FFStr("DODS_JDate");
    dods_jdate->set_read_p(true); // You must call this before ...
    dods_jdate->set_synthesized_p(true); // this! Look at BaseType.cc.

    // Add it to the DDS in the right place

    if (argc == 1 && argv[0]) {
	switch (argv[0]->type()) {
	  case dods_structure_c: {
	      Structure *sp = (Structure *)argv[0];
	      sp->add_var((BaseType *)dods_jdate);
	      break;
	  }

	  case dods_sequence_c: {
	      Sequence *sp = (Sequence *)argv[0];
	      sp->add_var((BaseType *)dods_jdate);
	      break;
	  }

	  default:
	throw Error(malformed_expr,
		  "You asked me to insert the synthesized variable in \n\
something that did not exist or was not a constructor \n\
type (e.g., a structure, sequence, ...).");
	    break;
	}
    }

    // Mark the variable as part of the current projection.

    dds.mark("DODS_JDate", true); // Don't just call set_send_p()!

    // Add the selection function to the CE

    dds.append_clause(sel_dods_jdate, 0); // 0 == no BaseType args
}

bool
sel_dods_date(int argc, BaseType *argv[], DDS &dds)
{
    if (argc != 0)
	throw Error(malformed_expr,
		  "Wrong number of arguments to internal selection function.\n\
Please report this error.");

    DBG(cerr << "Entering" << endl);

    DODS_Date new_date = DODS_Date_jpl_pathfinder(dds);

    // Stuff the yyyy/ddd string into DODS_JDate.
    Str *dods_date = (Str*)dds.var("DODS_Date");
    String s = new_date.ymd_date().data();
    dods_date->val2buf(&s);

    DBG(dods_date->print_val(cerr, "    ", true));
    DBG(cerr << "Exiting (" << dods_date->name() << " [" \
	<< dods_date << "])" << endl);

    return true;
}

void
proj_dods_date(int argc, BaseType *argv[], DDS &dds)
{
    if (argc < 0 || argc > 1)
	throw Error(malformed_expr,
		  "Wrong number of arguments to projection function.\n\
Expected zero or one arguments.");

    // Create the new variable

    Str *dods_date = new FFStr("DODS_Date");
    dods_date->set_read_p(true); // You must call this before ...
    dods_date->set_synthesized_p(true); // this! Look at BaseType.cc.

    // Add it to the DDS in the right place

    if (argc == 1 && argv[0]) {
	switch (argv[0]->type()) {
	  case dods_structure_c: {
	      Structure *sp = (Structure *)argv[0];
	      sp->add_var((BaseType *)dods_date);
	      break;
	  }

	  case dods_sequence_c: {
	      Sequence *sp = (Sequence *)argv[0];
	      sp->add_var((BaseType *)dods_date);
	      break;
	  }

	  default:
	throw Error(malformed_expr,
		  "You asked me to insert the synthesized variable in \n\
something that did not exist or was not a constructor \n\
type (e.g., a structure, sequence, ...).");
	    break;
	}
    }

    // Mark the variable as part of the current projection.

    dds.mark("DODS_Date", true); // Don't just call set_send_p()!

    // Add the selection function to the CE

    dds.append_clause(sel_dods_date, 0); // 0 == no BaseType args
}
<|endoftext|>


<commit_before>#include <sstream>

#include "./cparse/shunting-yard.h"
#include "./cparse/shunting-yard-exceptions.h"

#include "./statements.h"
#include "./range.h"

namespace custom_features {

std::string parseName(const char** source, char end_char = '\0') {
  std::stringstream ss;
  const char* code = *source;

  // Parse the function name:
  if (isalpha(*code) || *code == '_') {
    ss << *code;
    ++code;

    while (isalnum(*code) || *code == '_') {
      ss << *code;
      ++code;
    }

    // Find the beggining of the non space character:
    while (isspace(*code) && *code != end_char) ++code;
  } else {
    throw syntax_error("Expected variable name!");
  }

  *source = code;

  return ss.str();
}

packToken new_operator(const packToken& left, const packToken& right, evaluationData* data) {
  TokenMap& parent = left.asMap();
  Tuple args;

  if (right->type == TUPLE) {
    args = right.asTuple();
  } else {
    args = Tuple(right);
  }

  // Create a new Map:
  packToken child = TokenMap(&parent);

  // Call its init function if it exists:
  packToken* init = parent.find("__init__");
  if (init) {
    Function* func = init->asFunc();
    Function::call(child, func, &args, data->scope);
  }

  // Return it:
  return child;
}

void new_parser(const char* expr, const char** rest, rpnBuilder* data) {

  // Ignore white spaces:
  while (isspace(*expr)) ++expr;
  const char* name_start = expr;

  // Parse the class reference as an expression:
  TokenQueue_t name_rpn = calculator::toRPN(expr, data->scope, "(", &expr);

  const char* name_end = expr;

  // Move `class_rpn` to the top of `data->rpn`:
  while (!name_rpn.empty()) {
    TokenBase* token = name_rpn.front();
    name_rpn.pop();
    data->handle_token(token);
  }

  // Add the new operator to the queue:
  data->handle_op("new_op");

  // Find the open bracket:
  while (*expr && *expr != '(') ++expr;

  if (*expr != '(') {
    // Expected '(', throw error:
    while (isspace(name_end[-1])) --name_end;
    std::string name(name_start, name_end - name_start);
    throw syntax_error("Expected '(' after `new " + name + "`");
  }

  // Save the '(' position:
  *rest = expr;
}

class CompiledFunc : public Function {
  FuncDeclaration func;

 public:
  CompiledFunc() : Function() {}
  CompiledFunc(const char* code, const char** rest = 0,
               TokenMap parent_scope = &TokenMap::empty)
              : Function(), func("", code, rest, parent_scope) {}
  virtual ~CompiledFunc() {}

 public:
  const std::string name() const { return ""; }
  const args_t args() const { return {}; }
  packToken exec(TokenMap scope) const {
    return func.asFunc();
  }

 public:
  TokenBase* clone() const {
    return new CompiledFunc(*this);
  }
};

void function_parser(const char* expr, const char** rest, rpnBuilder* data) {
  // Ignore white spaces:
  while (isspace(*expr)) ++expr;

  if (isalpha(*expr) || *expr == '_') {
    // Ignore the name:
    while (isalnum(*expr) || *expr == '_') ++expr;
    // Ignore white spaces:
    while (isspace(*expr)) ++expr;
  }

  data->handle_token(new CompiledFunc(expr, &expr, data->scope));

  data->handle_op("()");

  data->handle_token(new Tuple());

  *rest = expr;
}

struct Startup {
  Startup() {
    rWordMap_t& rwMap = calculator::default_rWordMap();
    rwMap["new"] = &new_parser;
    rwMap["function"] = &function_parser;

    OppMap_t& opp = calculator::default_opPrecedence();
    opp.add("new_op", 14);

    opMap_t& opMap = calculator::default_opMap();
    opMap.add({MAP, "new_op", TUPLE}, &new_operator);
  }
} Startup;

}  // namespace custom_features
<commit_msg>Add error check for `new(class)` instead of `new class()`<commit_after>#include <sstream>

#include "./cparse/shunting-yard.h"
#include "./cparse/shunting-yard-exceptions.h"

#include "./statements.h"
#include "./range.h"

namespace custom_features {

std::string parseName(const char** source, char end_char = '\0') {
  std::stringstream ss;
  const char* code = *source;

  // Parse the function name:
  if (isalpha(*code) || *code == '_') {
    ss << *code;
    ++code;

    while (isalnum(*code) || *code == '_') {
      ss << *code;
      ++code;
    }

    // Find the beggining of the non space character:
    while (isspace(*code) && *code != end_char) ++code;
  } else {
    throw syntax_error("Expected variable name!");
  }

  *source = code;

  return ss.str();
}

packToken new_operator(const packToken& left, const packToken& right, evaluationData* data) {
  TokenMap& parent = left.asMap();
  Tuple args;

  if (right->type == TUPLE) {
    args = right.asTuple();
  } else {
    args = Tuple(right);
  }

  // Create a new Map:
  packToken child = TokenMap(&parent);

  // Call its init function if it exists:
  packToken* init = parent.find("__init__");
  if (init) {
    Function* func = init->asFunc();
    Function::call(child, func, &args, data->scope);
  }

  // Return it:
  return child;
}

void new_parser(const char* expr, const char** rest, rpnBuilder* data) {

  // Ignore white spaces:
  while (isspace(*expr)) ++expr;
  const char* name_start = expr;

  if (*name_start == '(') {
    throw syntax_error("Expected a class name after `new` operator!");
  }

  // Parse the class reference as an expression:
  TokenQueue_t name_rpn = calculator::toRPN(expr, data->scope, "(", &expr);

  const char* name_end = expr;

  // Move `class_rpn` to the top of `data->rpn`:
  while (!name_rpn.empty()) {
    TokenBase* token = name_rpn.front();
    name_rpn.pop();
    data->handle_token(token);
  }

  // Add the new operator to the queue:
  data->handle_op("new_op");

  // Find the open bracket:
  while (*expr && *expr != '(') ++expr;

  if (*expr != '(') {
    // Expected '(', throw error:
    while (isspace(name_end[-1])) --name_end;
    std::string name(name_start, name_end - name_start);
    throw syntax_error("Expected '(' after `new " + name + "`");
  }

  // Save the '(' position:
  *rest = expr;
}

class CompiledFunc : public Function {
  FuncDeclaration func;

 public:
  CompiledFunc() : Function() {}
  CompiledFunc(const char* code, const char** rest = 0,
               TokenMap parent_scope = &TokenMap::empty)
              : Function(), func("", code, rest, parent_scope) {}
  virtual ~CompiledFunc() {}

 public:
  const std::string name() const { return ""; }
  const args_t args() const { return {}; }
  packToken exec(TokenMap scope) const {
    return func.asFunc();
  }

 public:
  TokenBase* clone() const {
    return new CompiledFunc(*this);
  }
};

void function_parser(const char* expr, const char** rest, rpnBuilder* data) {
  // Ignore white spaces:
  while (isspace(*expr)) ++expr;

  if (isalpha(*expr) || *expr == '_') {
    // Ignore the name:
    while (isalnum(*expr) || *expr == '_') ++expr;
    // Ignore white spaces:
    while (isspace(*expr)) ++expr;
  }

  data->handle_token(new CompiledFunc(expr, &expr, data->scope));

  data->handle_op("()");

  data->handle_token(new Tuple());

  *rest = expr;
}

struct Startup {
  Startup() {
    rWordMap_t& rwMap = calculator::default_rWordMap();
    rwMap["new"] = &new_parser;
    rwMap["function"] = &function_parser;

    OppMap_t& opp = calculator::default_opPrecedence();
    opp.add("new_op", 14);

    opMap_t& opMap = calculator::default_opMap();
    opMap.add({MAP, "new_op", TUPLE}, &new_operator);
  }
} Startup;

}  // namespace custom_features
<|endoftext|>


<commit_before>// Read an INI file into easy-to-access name/value pairs.

#include <algorithm>
#include <cctype>
#include <cstdlib>
#include "../ini.h"
#include "INIReader.h"

using std::string;

INIReader::INIReader(string filename)
{
    _error = ini_parse(filename.c_str(), ValueHandler, this);
}

int INIReader::ParseError()
{
    return _error;
}

string INIReader::Get(string section, string name, string default_value)
{
    string key = MakeKey(section, name);
    return _values.count(key) ? _values[key] : default_value;
}

long INIReader::GetInteger(string section, string name, long default_value)
{
    string valstr = Get(section, name, "");
    const char* value = valstr.c_str();
    char* end;
    // This parses "1234" (decimal) and also "0x4D2" (hex)
    long n = strtol(value, &end, 0);
    return end > value ? n : default_value;
}

bool INIReader::GetBoolean(string section, string name, bool default_value)
{
    string valstr = Get(section, name, "");
    // Convert to lower case to make string comparisons case-insensitive
    std::transform(valstr.begin(), valstr.end(), valstr.begin(), ::tolower);
    if (valstr == "true" || valstr == "yes" || valstr == "on" || valstr == "1")
        return true;
    else if (valstr == "false" || valstr == "no" || valstr == "off" || valstr == "0")
        return false;
    else
        return default_value;
}

string INIReader::MakeKey(string section, string name)
{
    string key = section + "." + name;
    // Convert to lower case to make section/name lookups case-insensitive
    std::transform(key.begin(), key.end(), key.begin(), ::tolower);
    return key;
}

int INIReader::ValueHandler(void* user, const char* section, const char* name,
                            const char* value)
{
    INIReader* reader = (INIReader*)user;
    reader->_values[MakeKey(section, name)] = value;
    return 1;
}
<commit_msg>Issue 18: Fixed multi-line handling in C++ wrapper, per jeffhawke77.<commit_after>// Read an INI file into easy-to-access name/value pairs.

#include <algorithm>
#include <cctype>
#include <cstdlib>
#include "../ini.h"
#include "INIReader.h"

using std::string;

INIReader::INIReader(string filename)
{
    _error = ini_parse(filename.c_str(), ValueHandler, this);
}

int INIReader::ParseError()
{
    return _error;
}

string INIReader::Get(string section, string name, string default_value)
{
    string key = MakeKey(section, name);
    return _values.count(key) ? _values[key] : default_value;
}

long INIReader::GetInteger(string section, string name, long default_value)
{
    string valstr = Get(section, name, "");
    const char* value = valstr.c_str();
    char* end;
    // This parses "1234" (decimal) and also "0x4D2" (hex)
    long n = strtol(value, &end, 0);
    return end > value ? n : default_value;
}

bool INIReader::GetBoolean(string section, string name, bool default_value)
{
    string valstr = Get(section, name, "");
    // Convert to lower case to make string comparisons case-insensitive
    std::transform(valstr.begin(), valstr.end(), valstr.begin(), ::tolower);
    if (valstr == "true" || valstr == "yes" || valstr == "on" || valstr == "1")
        return true;
    else if (valstr == "false" || valstr == "no" || valstr == "off" || valstr == "0")
        return false;
    else
        return default_value;
}

string INIReader::MakeKey(string section, string name)
{
    string key = section + "." + name;
    // Convert to lower case to make section/name lookups case-insensitive
    std::transform(key.begin(), key.end(), key.begin(), ::tolower);
    return key;
}

int INIReader::ValueHandler(void* user, const char* section, const char* name,
                            const char* value)
{
    INIReader* reader = (INIReader*)user;
    string key = MakeKey(section, name);
    if (reader->_values[key].size() > 0)
        reader->_values[key] += "\n";
    reader->_values[key] += value;
    return 1;
}
<|endoftext|>


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

#include <algorithm>
#include <iostream>
#include <unordered_map>
#include <unordered_set>
#include <cstdio>
#include <cstdlib>
#include "FileUtil.h"
#include "MarcRecord.h"
#include "MarcReader.h"
#include "StringUtil.h"
#include "util.h"

void Usage() {
    std::cerr << "usage: " << ::progname << " marc_input subject1 [subject2 .. [subjectN]]\n\n"
              << "       where \"subject_list\" must contain LCSH's, one per line.\n";
    std::exit(EXIT_FAILURE);
}

/** Returns true if we match at least one subject in "subjects". */
inline bool Matched(const std::vector<std::string> &subjects,
                    const std::unordered_set<std::string> &loc_subject_headings)
{
    for (const auto &subject : subjects) {
        if (loc_subject_headings.find(subject) != loc_subject_headings.cend())
            return true;
    }

    return false;
}

void CollectStats(MarcReader * const marc_reader, const std::unordered_set<std::string> &loc_subject_headings,
                  std::unordered_map<std::string, unsigned> * const subjects_to_counts_map)
{
    unsigned total_count(0), matched_count(0);
    while (const MarcRecord record = marc_reader->read()) {
        ++total_count;

        std::vector<std::string> subjects;
        if (record.extractSubfield("650", 'a', &subjects) == 0)
            continue;

        for (auto &subject : subjects)
            StringUtil::RightTrim(" .", &subject);

        if (not Matched(subjects, loc_subject_headings))
            continue;

        ++matched_count;

        // Record our findings:
        for (const auto &subject : subjects) {
            auto subject_and_count(subjects_to_counts_map->find(subject));
            if (subject_and_count == subjects_to_counts_map->end())
                (*subjects_to_counts_map)[subject] = 1;
            else
                ++subject_and_count->second;
        }
    }

    std::cerr << "Processed a total of " << total_count << " record(s).\n";
    std::cerr << "Matched " << matched_count << " record(s).\n";
}

inline bool CompSubjectsAndSizes(const std::pair<std::string, unsigned> &subject_and_size1,
                                 const std::pair<std::string, unsigned> &subject_and_size2)
{
    return subject_and_size1.second > subject_and_size2.second;
}

void DisplayStats(const std::unordered_map<std::string, unsigned> &subjects_to_counts_map) {
    std::vector<std::pair<std::string, unsigned>> subjects_and_counts(subjects_to_counts_map.size());
    for (const auto &subject_and_count : subjects_to_counts_map)
        subjects_and_counts.emplace_back(subject_and_count);

    std::sort(subjects_and_counts.begin(), subjects_and_counts.end(), CompSubjectsAndSizes);

    for (const auto subject_and_count : subjects_and_counts)
        std::cout << subject_and_count.first << ' '
                  << StringUtil::ToString(subject_and_count.second * 100 / subjects_and_counts.size(), 3) << "%\n";
}

int main(int /*argc*/, char **argv) {
    ::progname = argv[0];
    ++argv;
    if (*argv == nullptr)
        Usage();

    try {
        std::unique_ptr<MarcReader> marc_reader(MarcReader::Factory(*argv++));
        if (*argv == nullptr)
            Usage();

        std::unordered_set<std::string> loc_subject_headings;
        while (*argv != nullptr)
            loc_subject_headings.insert(*argv++);

        std::unordered_map<std::string, unsigned> subjects_to_counts_map;
        CollectStats(marc_reader.get(), loc_subject_headings, &subjects_to_counts_map);
        DisplayStats(subjects_to_counts_map);
    } catch (const std::exception &x) {
        Error("caught exception: " + std::string(x.what()));
    }
}
<commit_msg>Fixed incorrect calculation.<commit_after>/** \brief A MARC-21 filter utility that selects records based on Library of Congress Subject Headings and
 *         counts LCSH frequencies in the selected set.
 *  \author Dr. Johannes Ruscheinski (johannes.ruscheinski@uni-tuebingen.de)
 *
 *  \copyright 2017 Universitätsbiblothek Tübingen.  All rights reserved.
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as
 *  published by the Free Software Foundation, either version 3 of the
 *  License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <algorithm>
#include <iostream>
#include <unordered_map>
#include <unordered_set>
#include <cstdio>
#include <cstdlib>
#include "FileUtil.h"
#include "MarcRecord.h"
#include "MarcReader.h"
#include "StringUtil.h"
#include "util.h"

void Usage() {
    std::cerr << "usage: " << ::progname << " marc_input subject1 [subject2 .. [subjectN]]\n\n"
              << "       where \"subject_list\" must contain LCSH's, one per line.\n";
    std::exit(EXIT_FAILURE);
}

/** Returns true if we match at least one subject in "subjects". */
inline bool Matched(const std::vector<std::string> &subjects,
                    const std::unordered_set<std::string> &loc_subject_headings)
{
    for (const auto &subject : subjects) {
        if (loc_subject_headings.find(subject) != loc_subject_headings.cend())
            return true;
    }

    return false;
}

void CollectStats(MarcReader * const marc_reader, const std::unordered_set<std::string> &loc_subject_headings,
                  std::unordered_map<std::string, unsigned> * const subjects_to_counts_map,
                  unsigned * const match_count)
{
    *match_count = 0;
    unsigned total_count(0);
    while (const MarcRecord record = marc_reader->read()) {
        ++total_count;

        std::vector<std::string> subjects;
        if (record.extractSubfield("650", 'a', &subjects) == 0)
            continue;

        for (auto &subject : subjects)
            StringUtil::RightTrim(" .", &subject);

        if (not Matched(subjects, loc_subject_headings))
            continue;

        ++*match_count;

        // Record our findings:
        for (const auto &subject : subjects) {
            auto subject_and_count(subjects_to_counts_map->find(subject));
            if (subject_and_count == subjects_to_counts_map->end())
                (*subjects_to_counts_map)[subject] = 1;
            else
                ++subject_and_count->second;
        }
    }

    std::cerr << "Processed a total of " << total_count << " record(s).\n";
    std::cerr << "Matched " << (*match_count) << " record(s).\n";
}

inline bool CompSubjectsAndSizes(const std::pair<std::string, unsigned> &subject_and_size1,
                                 const std::pair<std::string, unsigned> &subject_and_size2)
{
    return subject_and_size1.second > subject_and_size2.second;
}

void DisplayStats(const std::unordered_map<std::string, unsigned> &subjects_to_counts_map,
                  const unsigned total_count)
{
    std::vector<std::pair<std::string, unsigned>> subjects_and_counts(subjects_to_counts_map.size());
    for (const auto &subject_and_count : subjects_to_counts_map)
        subjects_and_counts.emplace_back(subject_and_count);

    std::sort(subjects_and_counts.begin(), subjects_and_counts.end(), CompSubjectsAndSizes);

    for (const auto subject_and_count : subjects_and_counts)
        std::cout << subject_and_count.first << ' '
                  << StringUtil::ToString(subject_and_count.second * 100 / total_count, 5) << "%\n";
}

int main(int /*argc*/, char **argv) {
    ::progname = argv[0];
    ++argv;
    if (*argv == nullptr)
        Usage();

    try {
        std::unique_ptr<MarcReader> marc_reader(MarcReader::Factory(*argv++));
        if (*argv == nullptr)
            Usage();

        std::unordered_set<std::string> loc_subject_headings;
        while (*argv != nullptr)
            loc_subject_headings.insert(*argv++);

        std::unordered_map<std::string, unsigned> subjects_to_counts_map;
        unsigned match_count;
        CollectStats(marc_reader.get(), loc_subject_headings, &subjects_to_counts_map, &match_count);
        DisplayStats(subjects_to_counts_map, match_count);
    } catch (const std::exception &x) {
        Error("caught exception: " + std::string(x.what()));
    }
}
<|endoftext|>


<commit_before>#pragma once
#include "base.hpp"
#include <iostream>
#include <iomanip>
#include "leveldb/db.h"
#include "leveldb/filter_policy.h"

struct dumpIndexdLevel : public transform_t {
	leveldb::DB* ldb;
	std::atomic_ulong maxHeight;

	dumpIndexdLevel () {
		this->maxHeight = 0;
	}

	~dumpIndexdLevel () {
		if (this->ldb != nullptr) delete this->ldb;
	}

	bool initialize (const char* arg) {
		if (transform_t::initialize(arg)) return true;
		if (strncmp(arg, "-l", 2) == 0) {
			const auto folderName = std::string(arg + 2);

			leveldb::Options options;
			options.create_if_missing = true;
		// 	options.filter_policy = leveldb::NewBloomFilterPolicy(10); // TODO

			const auto status = leveldb::DB::Open(options, folderName, &this->ldb);
			assert(status.ok());

			std::cerr << "Opened leveldb at " << folderName << std::endl;
			return true;
		}

		return false;
	}

	// 0x00 \ SHA256(block)
	void putTip (const hash256_t& id) {
		StackSlice<1 + 32> data;
		{
			auto _data = data.drop(0);
			_data.write<uint8_t>(0x00);
			_data.writeN(id.begin(), 32);
			assert(_data.length() == 0);
		}

		this->put(data.take(1), data.drop(1));
	}

	// 0x01 | SHA256(SCRIPT) | HEIGHT<BE> | TX_HASH | VOUT
	void putScript (const hash256_t& scHash, uint32_t height, const hash256_t& txHash, uint32_t vout) {
		StackSlice<1 + 32 + 4 + 32 + 4> data;
		{
			auto _data = data.drop(0);
			_data.write<uint8_t>(0x01);
			_data.writeN(scHash.begin(), 32);
			_data.write<uint32_t, true>(height); // big-endian for indexing
			_data.writeN(txHash.begin(), 32);
			_data.write<uint32_t>(vout);
			assert(_data.length() == 0);
		}

		this->put(data.drop(0), data.take(0));
	}

	// 0x02 | PREV_TX_HASH | PREV_TX_VOUT \ TX_HASH | TX_VIN
	void putSpent (const Slice& prevTxHash, uint32_t vout, const hash256_t& txHash, uint32_t vin) {
		StackSlice<1 + 32 + 4 + 32 + 4> data;
		{
			auto _data = data.drop(0);
			_data.write<uint8_t>(0x02);
			_data.writeN(prevTxHash.begin(), 32);
			_data.write<uint32_t>(vout);
			_data.writeN(txHash.begin(), 32);
			_data.write<uint32_t>(vin);
			assert(_data.length() == 0);
		}

		this->put(data.take(37), data.drop(37));
	}

	// 0x03 | TX_HASH \ HEIGHT
	void putTx (const hash256_t& txHash, uint32_t height) {
		StackSlice<1 + 32 + 4> data;
		{
			auto _data = data.drop(0);
			_data.write<uint8_t>(0x03);
			_data.writeN(txHash.begin(), 32);
			_data.write<uint32_t>(height);
			assert(_data.length() == 0);
		}

		this->put(data.take(33), data.drop(33));
	}

	// 0x04 | TX_HASH | VOUT \ VALUE
	void putTxOut (const hash256_t& txHash, uint32_t vout, uint64_t value) {
		StackSlice<1 + 32 + 4 + 8> data;
		{
			auto _data = data.drop(0);
			_data.write<uint8_t>(0x04);
			_data.writeN(txHash.begin(), 32);
			_data.write<uint32_t>(vout);
			_data.write<uint64_t>(value);
			assert(_data.length() == 0);
		}

		this->put(data.take(37), data.drop(37));
	}

	void put (const Slice& key, const Slice& value) {
		this->ldb->Put(
			leveldb::WriteOptions(),
			leveldb::Slice(reinterpret_cast<const char*>(key.begin()), key.length()),
			leveldb::Slice(reinterpret_cast<const char*>(value.begin()), value.length())
		);
	}

	void operator() (const Block& block) {
		assert(this->ldb);
		assert(not this->whitelist.empty());

		hash256_t blockHash;
		uint32_t height = -1;
		if (this->shouldSkip(block, &blockHash, &height)) return;
		if (height >= this->maxHeight) {
			this->maxHeight = height;
			this->putTip(blockHash);
		}

		auto transactions = block.transactions();
		while (not transactions.empty()) {
			const auto& transaction = transactions.front();

			hash256_t txHash;
			hash256(txHash.begin(), transaction.data);

			this->putTx(txHash, height);

			uint32_t vin = 0;
			for (const auto& input : transaction.inputs) {
				this->putSpent(input.hash, input.vout, txHash, vin);
				++vin;
			}

			uint32_t vout = 0;
			for (const auto& output : transaction.outputs) {
				hash256_t scHash;
				hash256(scHash.begin(), output.script);

				this->putScript(scHash, height, txHash, vout);
				this->putTxOut(txHash, vout, output.value);
				++vout;
			}

			transactions.popFront();
		}
	}
};
<commit_msg>leveldb: move to batching<commit_after>#pragma once
#include "base.hpp"
#include <iostream>
#include <iomanip>
#include "leveldb/db.h"
#include "leveldb/filter_policy.h"
#include "leveldb/write_batch.h"

namespace {
	void write (leveldb::WriteBatch& batch, const Slice& key, const Slice& value) {
		batch.Put(
			leveldb::Slice(reinterpret_cast<const char*>(key.begin()), key.length()),
			leveldb::Slice(reinterpret_cast<const char*>(value.begin()), value.length())
		);
	}

	// 0x00 \ SHA256(block)
	void putTip (leveldb::WriteBatch& batch, const hash256_t& id) {
		StackSlice<1 + 32> data;
		{
			auto _data = data.drop(0);
			_data.write<uint8_t>(0x00);
			_data.writeN(id.begin(), 32);
			assert(_data.length() == 0);
		}

		write(batch, data.take(1), data.drop(1));
	}

	// 0x01 | SHA256(SCRIPT) | HEIGHT<BE> | TX_HASH | VOUT
	void putScript (leveldb::WriteBatch& batch, const hash256_t& scHash, uint32_t height, const hash256_t& txHash, uint32_t vout) {
		StackSlice<1 + 32 + 4 + 32 + 4> data;
		{
			auto _data = data.drop(0);
			_data.write<uint8_t>(0x01);
			_data.writeN(scHash.begin(), 32);
			_data.write<uint32_t, true>(height); // big-endian for indexing
			_data.writeN(txHash.begin(), 32);
			_data.write<uint32_t>(vout);
			assert(_data.length() == 0);
		}

		write(batch, data.drop(0), data.take(0));
	}

	// 0x02 | PREV_TX_HASH | PREV_TX_VOUT \ TX_HASH | TX_VIN
	void putSpent (leveldb::WriteBatch& batch, const Slice& prevTxHash, uint32_t vout, const hash256_t& txHash, uint32_t vin) {
		StackSlice<1 + 32 + 4 + 32 + 4> data;
		{
			auto _data = data.drop(0);
			_data.write<uint8_t>(0x02);
			_data.writeN(prevTxHash.begin(), 32);
			_data.write<uint32_t>(vout);
			_data.writeN(txHash.begin(), 32);
			_data.write<uint32_t>(vin);
			assert(_data.length() == 0);
		}

		write(batch, data.take(37), data.drop(37));
	}

	// 0x03 | TX_HASH \ HEIGHT
	void putTx (leveldb::WriteBatch& batch, const hash256_t& txHash, uint32_t height) {
		StackSlice<1 + 32 + 4> data;
		{
			auto _data = data.drop(0);
			_data.write<uint8_t>(0x03);
			_data.writeN(txHash.begin(), 32);
			_data.write<uint32_t>(height);
			assert(_data.length() == 0);
		}

		write(batch, data.take(33), data.drop(33));
	}

	// 0x04 | TX_HASH | VOUT \ VALUE
	void putTxOut (leveldb::WriteBatch& batch, const hash256_t& txHash, uint32_t vout, uint64_t value) {
		StackSlice<1 + 32 + 4 + 8> data;
		{
			auto _data = data.drop(0);
			_data.write<uint8_t>(0x04);
			_data.writeN(txHash.begin(), 32);
			_data.write<uint32_t>(vout);
			_data.write<uint64_t>(value);
			assert(_data.length() == 0);
		}

		write(batch, data.take(37), data.drop(37));
	}
}

struct dumpIndexdLevel : public transform_t {
	leveldb::DB* ldb;
	std::atomic_ulong maxHeight;

	dumpIndexdLevel () {
		this->maxHeight = 0;
	}

	~dumpIndexdLevel () {
		if (this->ldb != nullptr) delete this->ldb;
	}

	bool initialize (const char* arg) {
		if (transform_t::initialize(arg)) return true;
		if (strncmp(arg, "-l", 2) == 0) {
			const auto folderName = std::string(arg + 2);

			leveldb::Options options;
			options.create_if_missing = true;
		// 	options.filter_policy = leveldb::NewBloomFilterPolicy(10); // TODO

			const auto status = leveldb::DB::Open(options, folderName, &this->ldb);
			assert(status.ok());

			std::cerr << "Opened leveldb at " << folderName << std::endl;
			return true;
		}

		return false;
	}

	void write (leveldb::WriteBatch& batch) {
		this->ldb->Write(leveldb::WriteOptions(), &batch);
	}

	void operator() (const Block& block) {
		assert(this->ldb);
		assert(not this->whitelist.empty());

		hash256_t blockHash;
		uint32_t height = -1;
		if (this->shouldSkip(block, &blockHash, &height)) return;

		leveldb::WriteBatch batch;
		if (height >= this->maxHeight) {
			this->maxHeight = height;
			putTip(batch, blockHash);
		}

		auto transactions = block.transactions();
		while (not transactions.empty()) {
			const auto& transaction = transactions.front();

			hash256_t txHash;
			hash256(txHash.begin(), transaction.data);

			putTx(batch, txHash, height);

			uint32_t vin = 0;
			for (const auto& input : transaction.inputs) {
				putSpent(batch, input.hash, input.vout, txHash, vin);
				++vin;
			}

			uint32_t vout = 0;
			for (const auto& output : transaction.outputs) {
				hash256_t scHash;
				hash256(scHash.begin(), output.script);

				putScript(batch, scHash, height, txHash, vout);
				putTxOut(batch, txHash, vout, output.value);
				++vout;
			}

			transactions.popFront();
		}

		this->write(batch);
	}
};
<|endoftext|>


<commit_before>/*
	Flexisip, a flexible SIP proxy server with media capabilities.
	Copyright (C) 2010-2015  Belledonne Communications SARL, All rights reserved.

	This program is free software: you can redistribute it and/or modify
	it under the terms of the GNU Affero General Public License as
	published by the Free Software Foundation, either version 3 of the
	License, or (at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU Affero General Public License for more details.

	You should have received a copy of the GNU Affero General Public License
	along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "forkcallcontext.hh"
#include "common.hh"
#include <algorithm>
#include <sofia-sip/sip_status.h>

using namespace std;

template <typename T> static bool contains(const list<T> &l, T value) {
	return find(l.cbegin(), l.cend(), value) != l.cend();
}

ForkCallContext::ForkCallContext(Agent *agent, const shared_ptr<RequestSipEvent> &event,
								 shared_ptr<ForkContextConfig> cfg, ForkContextListener *listener)
	: ForkContext(agent, event, cfg, listener), mShortTimer(NULL), mPushTimer(NULL), mCancelled(false) {
	SLOGD << "New ForkCallContext " << this;
	mLog = event->getEventLog<CallLog>();
	mActivePushes = 0;
}

ForkCallContext::~ForkCallContext() {
	SLOGD << "Destroy ForkCallContext " << this;

	if (mShortTimer) {
		su_timer_destroy(mShortTimer);
		mShortTimer = NULL;
	}

	if (mPushTimer) {
		su_timer_destroy(mPushTimer);
		mPushTimer = NULL;
	}
}

void ForkCallContext::onCancel(const shared_ptr<RequestSipEvent> &ev) {
	mLog->setCancelled();
	mLog->setCompleted();
	mCancelled = true;
	cancelOthers(shared_ptr<BranchInfo>(), ev->getSip());
}

void ForkCallContext::cancelOthers(const shared_ptr<BranchInfo> &br, sip_t *received_cancel) {
	auto branches = getBranches();
	for (auto it = branches.begin(); it != branches.end(); ++it) {
		shared_ptr<BranchInfo> brit = *it;

		if (brit != br) {
			shared_ptr<OutgoingTransaction> tr = brit->mTransaction;

			if (tr && brit->getStatus() < 200) {
				if(received_cancel && received_cancel->sip_reason) {
					sip_reason_t *reason = sip_reason_dup(tr->getHome(), received_cancel->sip_reason);
					tr->cancelWithReason(reason);
				} else {
					tr->cancel();
				}
			}

			removeBranch(brit);
		}
	}
}

void ForkCallContext::cancelOthersWithStatus(const shared_ptr<BranchInfo> &br, FlexisipForkStatus status) {
	auto branches = getBranches();
	for (auto it = branches.begin(); it != branches.end(); ++it) {
		shared_ptr<BranchInfo> brit = *it;
		if (brit != br) {
			shared_ptr<OutgoingTransaction> tr = brit->mTransaction;

			if (tr && brit->getStatus() < 200) {
				if(status == FlexisipForkAcceptedElsewhere) {
					sip_reason_t* reason = sip_reason_make(tr->getHome(), "SIP;cause=200;text=\"Call completed elsewhere\"");
					tr->cancelWithReason(reason);
				} else if (status == FlexisipForkDeclineElsewhere) {
					sip_reason_t* reason = sip_reason_make(tr->getHome(), "SIP;cause=600;text=\"Busy Everywhere\"");
					tr->cancelWithReason(reason);
				} else {
					tr->cancel();
				}
			}

			removeBranch(brit);
		}
	}
}

const int ForkCallContext::sUrgentCodesWithout603[] = {401, 407, 415, 420, 484, 488, 606, 0};

const int *ForkCallContext::getUrgentCodes() {
	if (mCfg->mTreatAllErrorsAsUrgent)
		return ForkContext::sAllCodesUrgent;

	if (mCfg->mTreatDeclineAsUrgent)
		return ForkContext::sUrgentCodes;

	return sUrgentCodesWithout603;
}

void ForkCallContext::onResponse(const shared_ptr<BranchInfo> &br, const shared_ptr<ResponseSipEvent> &event) {
	const shared_ptr<MsgSip> &ms = event->getMsgSip();
	sip_t *sip = ms->getSip();
	int code = sip->sip_status->st_status;

	if (code >= 300) {
		/*in fork-late mode, we must not consider that 503 and 408 resonse codes (which are send by sofia in case of i/o
		 * error or timeouts) are branches that are answered)
		 * Instead we must wait for the duration of the fork for new registers*/
		if (allBranchesAnswered(mCfg->mForkLate)) {
			shared_ptr<BranchInfo> best = findBestBranch(getUrgentCodes(), mCfg->mForkLate);
			if (best)
				logResponse(forwardResponse(best));
			return;
		}

		if (isUrgent(code, getUrgentCodes()) && mShortTimer == NULL) {
			mShortTimer = su_timer_create(su_root_task(mAgent->getRoot()), 0);
			su_timer_set_interval(mShortTimer, &ForkCallContext::sOnShortTimer, this,
								  (su_duration_t)mCfg->mUrgentTimeout * 1000);
			return;
		}

		if (code >= 600) {
			/*6xx response are normally treated as global faillures */
			if (!mCfg->mForkNoGlobalDecline) {
				logResponse(forwardResponse(br));
				cancelOthersWithStatus(br, FlexisipForkDeclineElsewhere);
			}
		}
	} else if (code >= 200) {
		logResponse(forwardResponse(br));
		cancelOthersWithStatus(br, FlexisipForkAcceptedElsewhere);
	} else if (code >= 100) {
		logResponse(forwardResponse(br));
	}
}

// This is actually called when we want to simulate a ringing event, for example when a push notification is sent to a
// device.
void ForkCallContext::sendResponse(int code, char const *phrase) {
	if (code == 180) {
		int previousCode = getLastResponseCode();
		if (previousCode > 180 || !mIncoming)
			return;
	}

	shared_ptr<MsgSip> msgsip(mIncoming->createResponse(code, phrase));
	if (!msgsip)
		return;

	shared_ptr<ResponseSipEvent> ev(
		new ResponseSipEvent(dynamic_pointer_cast<OutgoingAgent>(mAgent->shared_from_this()), msgsip));

	// add a to tag, no set by sofia here.
	if (!mCfg->mRemoveToTag) {
		const char *totag = nta_agent_newtag(msgsip->getHome(), "%s", mAgent->getSofiaAgent());
		sip_to_tag(msgsip->getHome(), msgsip->getSip()->sip_to, totag);
	}

	if (mPushTimer)
		su_timer_destroy(mPushTimer), mPushTimer = NULL;

	if (mCfg->mPushResponseTimeout > 0) {
		mPushTimer = su_timer_create(su_root_task(mAgent->getRoot()), 0);
		su_timer_set_interval(mPushTimer, &ForkCallContext::sOnPushTimer, this, (su_duration_t)mCfg->mPushResponseTimeout * 1000);
	}

	forwardResponse(ev);
}

void ForkCallContext::logResponse(const shared_ptr<ResponseSipEvent> &ev) {
	if (ev) {
		sip_t *sip = ev->getMsgSip()->getSip();
		mLog->setStatusCode(sip->sip_status->st_status, sip->sip_status->st_phrase);

		if (sip->sip_status->st_status >= 200)
			mLog->setCompleted();

		ev->setEventLog(mLog);
	}
}

bool ForkCallContext::onNewRegister(const url_t *url, const string &uid) {
	if (isCompleted())
		return false;

	return ForkContext::onNewRegister(url, uid);
}

bool ForkCallContext::isCompleted() const {
	if (getLastResponseCode() >= 200 || mCancelled || mIncoming == NULL)
		return true;

	return false;
}

bool ForkCallContext::isRingingSomewhere()const{
	for (const auto& br : getBranches()){
		int status = br->getStatus();

		if (status >= 180 && status < 200)
			return true;
	}

	return false;
}

void ForkCallContext::onShortTimer() {
	SLOGD << "ForkCallContext [" << this << "]: time to send urgent replies";

	/*first stop the timer, it has to be one shot*/
	su_timer_destroy(mShortTimer);
	mShortTimer = NULL;

	if (isRingingSomewhere())
		return; /*it's ringing somewhere*/

	auto br = findBestBranch(getUrgentCodes(), mCfg->mForkLate);

	if (br)
		logResponse(forwardResponse(br));
}

void ForkCallContext::onLateTimeout() {
	auto br = findBestBranch(getUrgentCodes(), mCfg->mForkLate);

	if (!br || br->getStatus() == 0 || br->getStatus() == 503) {
		shared_ptr<MsgSip> msgsip(mIncoming->createResponse(SIP_408_REQUEST_TIMEOUT));

		if (msgsip) {
			shared_ptr<ResponseSipEvent> ev(
				new ResponseSipEvent(dynamic_pointer_cast<OutgoingAgent>(mAgent->shared_from_this()), msgsip));
			logResponse(forwardResponse(ev));
		}
	} else {
		logResponse(forwardResponse(br));
	}

	/*cancel all possibly pending outgoing transactions*/
	cancelOthers(shared_ptr<BranchInfo>(), NULL);
}

void ForkCallContext::sOnShortTimer(su_root_magic_t *magic, su_timer_t *t, su_timer_arg_t *arg) {
	ForkCallContext *zis = static_cast<ForkCallContext *>(arg);
	zis->onShortTimer();
}

void ForkCallContext::onPushTimer() {
	if (!isCompleted() && getLastResponseCode() < 180) {
		SLOGD << "ForkCallContext [" << this << "] push timer : no uac response";
	}

	su_timer_destroy(mPushTimer);
	mPushTimer = NULL;
}

void ForkCallContext::sOnPushTimer(su_root_magic_t *magic, su_timer_t *t, su_timer_arg_t *arg) {
	ForkCallContext *zis = static_cast<ForkCallContext *>(arg);
	zis->onPushTimer();
}
void ForkCallContext::onPushInitiated(const string &key) {
	++mActivePushes;
}

void ForkCallContext::onPushError(const string &key, const string &errormsg) {
	--mActivePushes;

	if (mActivePushes != 0)
		return;

	SLOGD << "Early fail due to all push requests having failed";
	onPushTimer();
}
<commit_msg>Fix bug that caused aborted calls to not being logged in event logs<commit_after>/*
	Flexisip, a flexible SIP proxy server with media capabilities.
	Copyright (C) 2010-2015  Belledonne Communications SARL, All rights reserved.

	This program is free software: you can redistribute it and/or modify
	it under the terms of the GNU Affero General Public License as
	published by the Free Software Foundation, either version 3 of the
	License, or (at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU Affero General Public License for more details.

	You should have received a copy of the GNU Affero General Public License
	along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "forkcallcontext.hh"
#include "common.hh"
#include <algorithm>
#include <sofia-sip/sip_status.h>

using namespace std;

template <typename T> static bool contains(const list<T> &l, T value) {
	return find(l.cbegin(), l.cend(), value) != l.cend();
}

ForkCallContext::ForkCallContext(Agent *agent, const shared_ptr<RequestSipEvent> &event,
								 shared_ptr<ForkContextConfig> cfg, ForkContextListener *listener)
	: ForkContext(agent, event, cfg, listener), mShortTimer(NULL), mPushTimer(NULL), mCancelled(false) {
	SLOGD << "New ForkCallContext " << this;
	mLog = event->getEventLog<CallLog>();
	mActivePushes = 0;
}

ForkCallContext::~ForkCallContext() {
	SLOGD << "Destroy ForkCallContext " << this;

	if (mShortTimer) {
		su_timer_destroy(mShortTimer);
		mShortTimer = NULL;
	}

	if (mPushTimer) {
		su_timer_destroy(mPushTimer);
		mPushTimer = NULL;
	}
}

void ForkCallContext::onCancel(const shared_ptr<RequestSipEvent> &ev) {
	mLog->setCancelled();
	mLog->setCompleted();
	mCancelled = true;
	cancelOthers(shared_ptr<BranchInfo>(), ev->getSip());
	// The event log must be placed in a sip event in order to be written into DB.
	ev->setEventLog(mLog);
}

void ForkCallContext::cancelOthers(const shared_ptr<BranchInfo> &br, sip_t *received_cancel) {
	auto branches = getBranches();
	for (auto it = branches.begin(); it != branches.end(); ++it) {
		shared_ptr<BranchInfo> brit = *it;

		if (brit != br) {
			shared_ptr<OutgoingTransaction> tr = brit->mTransaction;

			if (tr && brit->getStatus() < 200) {
				if(received_cancel && received_cancel->sip_reason) {
					sip_reason_t *reason = sip_reason_dup(tr->getHome(), received_cancel->sip_reason);
					tr->cancelWithReason(reason);
				} else {
					tr->cancel();
				}
			}

			removeBranch(brit);
		}
	}
}

void ForkCallContext::cancelOthersWithStatus(const shared_ptr<BranchInfo> &br, FlexisipForkStatus status) {
	auto branches = getBranches();
	for (auto it = branches.begin(); it != branches.end(); ++it) {
		shared_ptr<BranchInfo> brit = *it;
		if (brit != br) {
			shared_ptr<OutgoingTransaction> tr = brit->mTransaction;

			if (tr && brit->getStatus() < 200) {
				if(status == FlexisipForkAcceptedElsewhere) {
					sip_reason_t* reason = sip_reason_make(tr->getHome(), "SIP;cause=200;text=\"Call completed elsewhere\"");
					tr->cancelWithReason(reason);
				} else if (status == FlexisipForkDeclineElsewhere) {
					sip_reason_t* reason = sip_reason_make(tr->getHome(), "SIP;cause=600;text=\"Busy Everywhere\"");
					tr->cancelWithReason(reason);
				} else {
					tr->cancel();
				}
			}

			removeBranch(brit);
		}
	}
}

const int ForkCallContext::sUrgentCodesWithout603[] = {401, 407, 415, 420, 484, 488, 606, 0};

const int *ForkCallContext::getUrgentCodes() {
	if (mCfg->mTreatAllErrorsAsUrgent)
		return ForkContext::sAllCodesUrgent;

	if (mCfg->mTreatDeclineAsUrgent)
		return ForkContext::sUrgentCodes;

	return sUrgentCodesWithout603;
}

void ForkCallContext::onResponse(const shared_ptr<BranchInfo> &br, const shared_ptr<ResponseSipEvent> &event) {
	const shared_ptr<MsgSip> &ms = event->getMsgSip();
	sip_t *sip = ms->getSip();
	int code = sip->sip_status->st_status;

	if (code >= 300) {
		/*in fork-late mode, we must not consider that 503 and 408 resonse codes (which are send by sofia in case of i/o
		 * error or timeouts) are branches that are answered)
		 * Instead we must wait for the duration of the fork for new registers*/
		if (allBranchesAnswered(mCfg->mForkLate)) {
			shared_ptr<BranchInfo> best = findBestBranch(getUrgentCodes(), mCfg->mForkLate);
			if (best)
				logResponse(forwardResponse(best));
			return;
		}

		if (isUrgent(code, getUrgentCodes()) && mShortTimer == NULL) {
			mShortTimer = su_timer_create(su_root_task(mAgent->getRoot()), 0);
			su_timer_set_interval(mShortTimer, &ForkCallContext::sOnShortTimer, this,
								  (su_duration_t)mCfg->mUrgentTimeout * 1000);
			return;
		}

		if (code >= 600) {
			/*6xx response are normally treated as global faillures */
			if (!mCfg->mForkNoGlobalDecline) {
				logResponse(forwardResponse(br));
				cancelOthersWithStatus(br, FlexisipForkDeclineElsewhere);
			}
		}
	} else if (code >= 200) {
		logResponse(forwardResponse(br));
		cancelOthersWithStatus(br, FlexisipForkAcceptedElsewhere);
	} else if (code >= 100) {
		logResponse(forwardResponse(br));
	}
}

// This is actually called when we want to simulate a ringing event, for example when a push notification is sent to a
// device.
void ForkCallContext::sendResponse(int code, char const *phrase) {
	if (code == 180) {
		int previousCode = getLastResponseCode();
		if (previousCode > 180 || !mIncoming)
			return;
	}

	shared_ptr<MsgSip> msgsip(mIncoming->createResponse(code, phrase));
	if (!msgsip)
		return;

	shared_ptr<ResponseSipEvent> ev(
		new ResponseSipEvent(dynamic_pointer_cast<OutgoingAgent>(mAgent->shared_from_this()), msgsip));

	// add a to tag, no set by sofia here.
	if (!mCfg->mRemoveToTag) {
		const char *totag = nta_agent_newtag(msgsip->getHome(), "%s", mAgent->getSofiaAgent());
		sip_to_tag(msgsip->getHome(), msgsip->getSip()->sip_to, totag);
	}

	if (mPushTimer)
		su_timer_destroy(mPushTimer), mPushTimer = NULL;

	if (mCfg->mPushResponseTimeout > 0) {
		mPushTimer = su_timer_create(su_root_task(mAgent->getRoot()), 0);
		su_timer_set_interval(mPushTimer, &ForkCallContext::sOnPushTimer, this, (su_duration_t)mCfg->mPushResponseTimeout * 1000);
	}

	forwardResponse(ev);
}

void ForkCallContext::logResponse(const shared_ptr<ResponseSipEvent> &ev) {
	if (ev) {
		sip_t *sip = ev->getMsgSip()->getSip();
		mLog->setStatusCode(sip->sip_status->st_status, sip->sip_status->st_phrase);

		if (sip->sip_status->st_status >= 200)
			mLog->setCompleted();

		ev->setEventLog(mLog);
	}
}

bool ForkCallContext::onNewRegister(const url_t *url, const string &uid) {
	if (isCompleted())
		return false;

	return ForkContext::onNewRegister(url, uid);
}

bool ForkCallContext::isCompleted() const {
	if (getLastResponseCode() >= 200 || mCancelled || mIncoming == NULL)
		return true;

	return false;
}

bool ForkCallContext::isRingingSomewhere()const{
	for (const auto& br : getBranches()){
		int status = br->getStatus();

		if (status >= 180 && status < 200)
			return true;
	}

	return false;
}

void ForkCallContext::onShortTimer() {
	SLOGD << "ForkCallContext [" << this << "]: time to send urgent replies";

	/*first stop the timer, it has to be one shot*/
	su_timer_destroy(mShortTimer);
	mShortTimer = NULL;

	if (isRingingSomewhere())
		return; /*it's ringing somewhere*/

	auto br = findBestBranch(getUrgentCodes(), mCfg->mForkLate);

	if (br)
		logResponse(forwardResponse(br));
}

void ForkCallContext::onLateTimeout() {
	auto br = findBestBranch(getUrgentCodes(), mCfg->mForkLate);

	if (!br || br->getStatus() == 0 || br->getStatus() == 503) {
		shared_ptr<MsgSip> msgsip(mIncoming->createResponse(SIP_408_REQUEST_TIMEOUT));

		if (msgsip) {
			shared_ptr<ResponseSipEvent> ev(
				new ResponseSipEvent(dynamic_pointer_cast<OutgoingAgent>(mAgent->shared_from_this()), msgsip));
			logResponse(forwardResponse(ev));
		}
	} else {
		logResponse(forwardResponse(br));
	}

	/*cancel all possibly pending outgoing transactions*/
	cancelOthers(shared_ptr<BranchInfo>(), NULL);
}

void ForkCallContext::sOnShortTimer(su_root_magic_t *magic, su_timer_t *t, su_timer_arg_t *arg) {
	ForkCallContext *zis = static_cast<ForkCallContext *>(arg);
	zis->onShortTimer();
}

void ForkCallContext::onPushTimer() {
	if (!isCompleted() && getLastResponseCode() < 180) {
		SLOGD << "ForkCallContext [" << this << "] push timer : no uac response";
	}

	su_timer_destroy(mPushTimer);
	mPushTimer = NULL;
}

void ForkCallContext::sOnPushTimer(su_root_magic_t *magic, su_timer_t *t, su_timer_arg_t *arg) {
	ForkCallContext *zis = static_cast<ForkCallContext *>(arg);
	zis->onPushTimer();
}
void ForkCallContext::onPushInitiated(const string &key) {
	++mActivePushes;
}

void ForkCallContext::onPushError(const string &key, const string &errormsg) {
	--mActivePushes;

	if (mActivePushes != 0)
		return;

	SLOGD << "Early fail due to all push requests having failed";
	onPushTimer();
}
<|endoftext|>


<commit_before>// @(#)root/tree:$Name:  $:$Id: TSelector.cxx,v 1.30 2006/09/16 02:29:07 pcanal Exp $
// Author: Rene Brun   05/02/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.             *
 *************************************************************************/

////////////////////////////////////////////////////////////////////////////
//                                                                        //
// A TSelector object is used by the TTree::Draw, TTree::Scan,            //
// TTree::Loop, TTree::Process to navigate in a TTree and make            //
// selections. It contains the following main methods:                    //
//                                                                        //
// void TSelector::Init(TTree *t). Called every time a new TTree is       //
//    attached.                                                           //
// void TSelector::Begin(). This method is called before looping on the   //
//    events in the Tree. The user can create his histograms in this      //
//    function. When using PROOF Begin() is called on the client only.    //
//    Histogram creation should preferable be done in SlaveBegin() in     //
//    that case.                                                          //
// void TSelector::SlaveBegin(). This method is called on each PROOF      //
//    worker node. The user can create his histograms in this method.     //
//    In local mode this method is called on the client too.              //
//                                                                        //
// Bool_t TSelector::Notify(). This method is called at the first entry   //
//    of a new file in a chain.                                           //
//                                                                        //
// Bool_t TSelector::Process(Long64_t entry). This method is called       //
//    to process an event. It is the user's responsability to read        //
//    the corresponding entry in memory (may be just a partial read).     //
//    Once the entry is in memory one can apply a selection and if the    //
//    event is selected histograms can be filled. Processing stops        //
//    when this function returns kFALSE. This function combines the       //
//    next two functions in one, avoiding to have to maintain state       //
//    in the class to communicate between these two funtions.             //
//    See WARNING below about entry.                                      //
//    This method is used by PROOF.                                       //
// Bool_t TSelector::ProcessCut(Long64_t entry). This method is called    //
//    before processing entry. It is the user's responsability to read    //
//    the corresponding entry in memory (may be just a partial read).     //
//    The function returns kTRUE if the entry must be processed,          //
//    kFALSE otherwise. This method is obsolete, use Process().           //
//    See WARNING below about entry.                                      //
// void TSelector::ProcessFill(Long64_t entry). This method is called     //
//    for all selected events. User fills histograms in this function.    //
//    This method is obsolete, use Process().                             //
//    See WARNING below about entry.                                      //
// void TSelector::SlaveTerminate(). This method is called at the end of  //
//    the loop on all PROOF worker nodes. In local mode this method is    //
//    called on the client too.                                           //
// void TSelector::Terminate(). This method is called at the end of       //
//    the loop on all events. When using PROOF Terminate() is call on     //
//    the client only. Typically one performs the fits on the produced    //
//    histograms or write the histograms to file in this method.          //
//                                                                        //
// WARNING when a selector is used with a TChain:                         //
//    in the Process, ProcessCut, ProcessFill function, you must use      //
//    the pointer to the current Tree to call GetEntry(entry).            //
//    entry is always the local entry number in the current tree.         //
//    Assuming that fChain is the pointer to the TChain being processed,  //
//    use fChain->GetTree()->GetEntry(entry);                             //
//                                                                        //
////////////////////////////////////////////////////////////////////////////

#include "TROOT.h"
#include "TSystem.h"
#include "TTree.h"
#include "TError.h"
#include "TSelectorCint.h"
#include "Api.h"
#include "TClass.h"
#include "TInterpreter.h"

ClassImp(TSelector)

//______________________________________________________________________________
TSelector::TSelector() : TObject()
{
   // Default selector ctor.

   fStatus = 0;
   fAbort  = kContinue;
   fObject = 0;
   fInput  = 0;
   fOutput = new TSelectorList;
   fOutput->SetOwner();
}

//______________________________________________________________________________
TSelector::~TSelector()
{
   // Selector destructor.

   delete fOutput;
}

//______________________________________________________________________________
void TSelector::Abort(const char *why, EAbort what)
{
   // Abort processing. If what = kAbortProcess, the Process() loop will be
   // aborted. If what = kAbortFile, the current file in a chain will be
   // aborted and the processing will continue with the next file, if there
   // is no next file then Process() will be aborted. Abort() can also  be
   // called from Begin(), SlaveBegin(), Init() and Notify(). After abort
   // the SlaveTerminate() and Terminate() are always called. The abort flag
   // can be checked in these methods using GetAbort().

   fAbort = what;
   TString mess = "Abort";
   if (fAbort == kAbortProcess)
      mess = "AbortProcess";
   else if (fAbort == kAbortFile)
      mess = "AbortFile";

   Info(mess, why);
}

//______________________________________________________________________________
TSelector *TSelector::GetSelector(const char *filename)
{
   // The code in filename is loaded (interpreted or compiled, see below),
   // filename must contain a valid class implementation derived from TSelector.
   //
   // If filename is of the form file.C, the file will be interpreted.
   // If filename is of the form file.C++, the file file.C will be compiled
   // and dynamically loaded. The corresponding binary file and shared
   // library will be deleted at the end of the function.
   // If filename is of the form file.C+, the file file.C will be compiled
   // and dynamically loaded. At next call, if file.C is older than file.o
   // and file.so, the file.C is not compiled, only file.so is loaded.
   //
   // The static function returns a pointer to a TSelector object

   // If the filename does not contain "." assume class is compiled in
   TString localname;
   Bool_t fromFile = kFALSE;
   if (strchr(filename, '.') != 0) {
      //Interpret/compile filename via CINT
      localname  = ".L ";
      localname += filename;
      gROOT->ProcessLine(localname);
      fromFile = kTRUE;
   }

   //loop on all classes known to CINT to find the class on filename
   //that derives from TSelector
   const char *basename = gSystem->BaseName(filename);
   if (!basename) {
      ::Error("TSelector::GetSelector","unable to determine the classname for file %s", filename);
      return 0;
   }
   localname = basename;
   Bool_t isCompiled = !fromFile || localname.EndsWith("+");
   if (localname.Index(".") != kNPOS)
      localname.Remove(localname.Index("."));

   // make sure the class is loaded
   if (!fromFile && gInterpreter->AutoLoad(localname) != 1) {
      ::Error("TSelector::GetSelector", "class %s could not be loaded", filename);
      return 0;
   }

   G__ClassInfo cl;
   Bool_t ok = kFALSE;
   while (cl.Next()) {
      if (localname == cl.Name()) {
         if (cl.IsBase("TSelector")) ok = kTRUE;
         break;
      }
   }
   if (!ok) {
      if (fromFile) {
         ::Error("TSelector::GetSelector",
         "file %s does not have a valid class deriving from TSelector", filename);
      } else {
         ::Error("TSelector::GetSelector",
         "class %s does not exist or does not derive from TSelector", filename);
      }
      return 0;
   }

   // we can now create an instance of the class
   TSelector *selector = (TSelector*)cl.New();
   if (!selector || isCompiled) return selector;

   //interpreted selector: cannot be used as such
   //create a fake selector
   TSelectorCint *select = new TSelectorCint();
   select->Build(selector, &cl);

   return select;
}

//______________________________________________________________________________
Bool_t TSelector::IsStandardDraw(const char *selec)
{
   // Find out if this is a standard selection used for Draw actions
   // (either TSelectorDraw, TProofDraw or deriving from them).

   // Make sure we have a name
   if (!selec) {
      ::Info("TSelector::IsStandardDraw",
             "selector name undefined - do nothing");
      return kFALSE;
   }

   Bool_t stdselec = kFALSE;
   if (!strchr(selec, '.')) {
      if (strstr(selec, "TSelectorDraw")) {
         stdselec = kTRUE;
      } else {
         TClass *cl = TClass::GetClass(selec);
         if (cl && (cl->InheritsFrom("TProofDraw") ||
                    cl->InheritsFrom("TSelectorDraw")))
            stdselec = kTRUE;
      }
   }

   // We are done
   return stdselec;
}
<commit_msg>From Gerri: implement a better way of autoloading in GetSelector(). Macro's don't have a dict in the gClassTable so don't fail in that case.<commit_after>// @(#)root/tree:$Name:  $:$Id: TSelector.cxx,v 1.31 2007/07/10 08:40:12 rdm Exp $
// Author: Rene Brun   05/02/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.             *
 *************************************************************************/

////////////////////////////////////////////////////////////////////////////
//                                                                        //
// A TSelector object is used by the TTree::Draw, TTree::Scan,            //
// TTree::Loop, TTree::Process to navigate in a TTree and make            //
// selections. It contains the following main methods:                    //
//                                                                        //
// void TSelector::Init(TTree *t). Called every time a new TTree is       //
//    attached.                                                           //
// void TSelector::Begin(). This method is called before looping on the   //
//    events in the Tree. The user can create his histograms in this      //
//    function. When using PROOF Begin() is called on the client only.    //
//    Histogram creation should preferable be done in SlaveBegin() in     //
//    that case.                                                          //
// void TSelector::SlaveBegin(). This method is called on each PROOF      //
//    worker node. The user can create his histograms in this method.     //
//    In local mode this method is called on the client too.              //
//                                                                        //
// Bool_t TSelector::Notify(). This method is called at the first entry   //
//    of a new file in a chain.                                           //
//                                                                        //
// Bool_t TSelector::Process(Long64_t entry). This method is called       //
//    to process an event. It is the user's responsability to read        //
//    the corresponding entry in memory (may be just a partial read).     //
//    Once the entry is in memory one can apply a selection and if the    //
//    event is selected histograms can be filled. Processing stops        //
//    when this function returns kFALSE. This function combines the       //
//    next two functions in one, avoiding to have to maintain state       //
//    in the class to communicate between these two funtions.             //
//    See WARNING below about entry.                                      //
//    This method is used by PROOF.                                       //
// Bool_t TSelector::ProcessCut(Long64_t entry). This method is called    //
//    before processing entry. It is the user's responsability to read    //
//    the corresponding entry in memory (may be just a partial read).     //
//    The function returns kTRUE if the entry must be processed,          //
//    kFALSE otherwise. This method is obsolete, use Process().           //
//    See WARNING below about entry.                                      //
// void TSelector::ProcessFill(Long64_t entry). This method is called     //
//    for all selected events. User fills histograms in this function.    //
//    This method is obsolete, use Process().                             //
//    See WARNING below about entry.                                      //
// void TSelector::SlaveTerminate(). This method is called at the end of  //
//    the loop on all PROOF worker nodes. In local mode this method is    //
//    called on the client too.                                           //
// void TSelector::Terminate(). This method is called at the end of       //
//    the loop on all events. When using PROOF Terminate() is call on     //
//    the client only. Typically one performs the fits on the produced    //
//    histograms or write the histograms to file in this method.          //
//                                                                        //
// WARNING when a selector is used with a TChain:                         //
//    in the Process, ProcessCut, ProcessFill function, you must use      //
//    the pointer to the current Tree to call GetEntry(entry).            //
//    entry is always the local entry number in the current tree.         //
//    Assuming that fChain is the pointer to the TChain being processed,  //
//    use fChain->GetTree()->GetEntry(entry);                             //
//                                                                        //
////////////////////////////////////////////////////////////////////////////

#include "TROOT.h"
#include "TSystem.h"
#include "TTree.h"
#include "TError.h"
#include "TSelectorCint.h"
#include "Api.h"
#include "TClass.h"
#include "TInterpreter.h"

ClassImp(TSelector)

//______________________________________________________________________________
TSelector::TSelector() : TObject()
{
   // Default selector ctor.

   fStatus = 0;
   fAbort  = kContinue;
   fObject = 0;
   fInput  = 0;
   fOutput = new TSelectorList;
   fOutput->SetOwner();
}

//______________________________________________________________________________
TSelector::~TSelector()
{
   // Selector destructor.

   delete fOutput;
}

//______________________________________________________________________________
void TSelector::Abort(const char *why, EAbort what)
{
   // Abort processing. If what = kAbortProcess, the Process() loop will be
   // aborted. If what = kAbortFile, the current file in a chain will be
   // aborted and the processing will continue with the next file, if there
   // is no next file then Process() will be aborted. Abort() can also  be
   // called from Begin(), SlaveBegin(), Init() and Notify(). After abort
   // the SlaveTerminate() and Terminate() are always called. The abort flag
   // can be checked in these methods using GetAbort().

   fAbort = what;
   TString mess = "Abort";
   if (fAbort == kAbortProcess)
      mess = "AbortProcess";
   else if (fAbort == kAbortFile)
      mess = "AbortFile";

   Info(mess, why);
}

//______________________________________________________________________________
TSelector *TSelector::GetSelector(const char *filename)
{
   // The code in filename is loaded (interpreted or compiled, see below),
   // filename must contain a valid class implementation derived from TSelector.
   //
   // If filename is of the form file.C, the file will be interpreted.
   // If filename is of the form file.C++, the file file.C will be compiled
   // and dynamically loaded. The corresponding binary file and shared
   // library will be deleted at the end of the function.
   // If filename is of the form file.C+, the file file.C will be compiled
   // and dynamically loaded. At next call, if file.C is older than file.o
   // and file.so, the file.C is not compiled, only file.so is loaded.
   //
   // The static function returns a pointer to a TSelector object

   // If the filename does not contain "." assume class is compiled in
   TString localname;
   Bool_t fromFile = kFALSE;
   if (strchr(filename, '.') != 0) {
      //Interpret/compile filename via CINT
      localname  = ".L ";
      localname += filename;
      gROOT->ProcessLine(localname);
      fromFile = kTRUE;
   }

   //loop on all classes known to CINT to find the class on filename
   //that derives from TSelector
   const char *basename = gSystem->BaseName(filename);
   if (!basename) {
      ::Error("TSelector::GetSelector","unable to determine the classname for file %s", filename);
      return 0;
   }
   localname = basename;
   Bool_t isCompiled = !fromFile || localname.EndsWith("+");
   if (localname.Index(".") != kNPOS)
      localname.Remove(localname.Index("."));

   // if a file was not specified, try to load the class via the interpreter;
   // this returns 0 (== failure) in the case the class is already in memory
   // but does not have a dictionary, so we just raise a flag for better
   // diagnostic in the case the class is not found in the G__ClassInfo table.
   Bool_t autoloaderr = kFALSE;
   if (!fromFile && gInterpreter->AutoLoad(localname) != 1)
      autoloaderr = kTRUE;

   G__ClassInfo cl;
   Bool_t ok = kFALSE;
   while (cl.Next()) {
      if (localname == cl.Name()) {
         if (cl.IsBase("TSelector")) ok = kTRUE;
         break;
      }
   }
   if (!ok) {
      if (fromFile) {
         ::Error("TSelector::GetSelector",
         "file %s does not have a valid class deriving from TSelector", filename);
      } else {
         if (autoloaderr)
            ::Error("TSelector::GetSelector", "class %s could not be loaded", filename);
         else
            ::Error("TSelector::GetSelector",
                    "class %s does not exist or does not derive from TSelector", filename);
      }
      return 0;
   }

   // we can now create an instance of the class
   TSelector *selector = (TSelector*)cl.New();
   if (!selector || isCompiled) return selector;

   //interpreted selector: cannot be used as such
   //create a fake selector
   TSelectorCint *select = new TSelectorCint();
   select->Build(selector, &cl);

   return select;
}

//______________________________________________________________________________
Bool_t TSelector::IsStandardDraw(const char *selec)
{
   // Find out if this is a standard selection used for Draw actions
   // (either TSelectorDraw, TProofDraw or deriving from them).

   // Make sure we have a name
   if (!selec) {
      ::Info("TSelector::IsStandardDraw",
             "selector name undefined - do nothing");
      return kFALSE;
   }

   Bool_t stdselec = kFALSE;
   if (!strchr(selec, '.')) {
      if (strstr(selec, "TSelectorDraw")) {
         stdselec = kTRUE;
      } else {
         TClass *cl = TClass::GetClass(selec);
         if (cl && (cl->InheritsFrom("TProofDraw") ||
                    cl->InheritsFrom("TSelectorDraw")))
            stdselec = kTRUE;
      }
   }

   // We are done
   return stdselec;
}
<|endoftext|>