<commit_before>/* -*- coding: utf-8; mode: c++; tab-width: 3; indent-tabs-mode: nil -*-

Copyright 2017 Raffaello D. Di Napoli

This file is part of Lofty.

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

Lofty 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 Lofty. If not, see
<http://www.gnu.org/licenses/>.
------------------------------------------------------------------------------------------------------------*/

#include <lofty.hxx>
#include <lofty/text.hxx>
#include <lofty/text/parsers/dynamic.hxx>
#include <lofty/text/parsers/regex.hxx>

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

namespace lofty { namespace text { namespace parsers {

regex::subexpression::subexpression() :
   first_state(nullptr),
   curr_alternative_first_state(nullptr),
   curr_state(nullptr) {
}

regex::subexpression::~subexpression() {
}

void regex::subexpression::push_alternative(dynamic_state * new_state) {
   if (curr_state) {
      alternative_last_states.push_back(curr_state);
      curr_alternative_first_state->set_alternative(new_state);
   } else {
      first_state = new_state;
   }
   curr_state = new_state;
   curr_alternative_first_state = new_state;
}

void regex::subexpression::push_next(dynamic_state * new_state) {
   if (curr_state) {
      curr_state->set_next(new_state);
   } else {
      first_state = new_state;
      curr_alternative_first_state = new_state;
   }
   curr_state = new_state;
}

void regex::subexpression::terminate_with_next_state(dynamic_state * next_state) {
   if (curr_state) {
      alternative_last_states.push_back(curr_state);
      curr_state = nullptr;
   }
   LOFTY_FOR_EACH(auto & state, alternative_last_states) {
      state->set_next(next_state);
   }
}

regex::regex(dynamic * parser_, str const & expr_) :
   parser(parser_),
   expr(expr_),
   expr_itr(expr.cbegin()),
   expr_end(expr.cend()),
   next_capture_index(0),
   enter_rep_group(false),
   end_subexpr(false),
   begin_alternative(false) {
   subexpr_stack.push_back(subexpression());
}

regex::~regex() {
}

void regex::insert_capture_group(dynamic_state const * first_state) {
   LOFTY_TRACE_FUNC(this, first_state);

   push_state(parser->create_capture_group(first_state));
}

int regex::parse_up_to_next_capture(regex_capture_format * capture_format, dynamic_state ** first_state) {
   LOFTY_TRACE_FUNC(this, capture_format, first_state);

   bool escape = false;
   while (expr_itr != expr_end) {
      char32_t cp = *expr_itr++;
      if (escape) {
         escape = false;
         goto escaped;
      }
      switch (cp) {
         case '.':
            push_state(parser->create_code_point_range_state(0x000000, 0xffffff));
            break;
         case '[':
            if (expr_itr >= expr_end) {
               throw_syntax_error(LOFTY_SL("unexpected end of bracket expression"));
            }
            subexpr_stack.push_back(subexpression());
            if (*expr_itr == '^') {
               if (++expr_itr >= expr_end) {
                  throw_syntax_error(LOFTY_SL("unexpected end of negative bracket expression"));
               }
               parse_negative_bracket_expression();
            } else {
               parse_positive_bracket_expression();
            }
            end_subexpr = true;
            break;
         case '\\':
            escape = true;
            break;
         case '(': {
            if (expr_itr >= expr_end) {
               throw_syntax_error(LOFTY_SL("unexpected end of group"));
            }
            if (*expr_itr != '?') {
               if (capture_format) {
                  extract_capture(capture_format);
                  return static_cast<int>(next_capture_index++);
               } else {
                  /* The caller is not interested in receiving the capture’s format; just report that we found
                  a capture. */
                  return 0;
               }
            }
            ++expr_itr;
            if (expr_itr >= expr_end) {
               throw_syntax_error(LOFTY_SL("unexpected end of non-capturing group"));
            }
            if (*expr_itr == ':') {
               ++expr_itr;
               enter_rep_group = true;
            } else {
               throw_syntax_error(LOFTY_SL("unsupported non-capturing group type"));
            }
            break;
         }
         case ')':
            end_subexpr = true;
            break;
         case '*':
            set_curr_state_repetitions(0, 0);
            break;
         case '+':
            set_curr_state_repetitions(1, 0);
            break;
         case '?':
            set_curr_state_repetitions(0, 1);
            break;
         case '{': {
            auto range(parse_repetition_range());
            set_curr_state_repetitions(_std::get<0>(range), _std::get<1>(range));
            break;
         }
         case '|':
            begin_alternative = true;
            break;
         case '^':
            push_state(parser->create_begin_state());
            break;
         case '$':
            push_state(parser->create_end_state());
            break;
         default:
         escaped:
            push_state(parser->create_code_point_state(cp));
            break;
      }
   }
   if (subexpr_stack.size() != 1) {
      throw_syntax_error(LOFTY_SL("mismatched parentheses"));
   }
   *first_state = subexpr_stack.front().first_state;
   // No need to terminate the last subexpression; it should be already nullptr-terminated.
   subexpr_stack.clear();
   return -1;
}

void regex::throw_syntax_error(str const & description) const {
   LOFTY_THROW(syntax_error, (
      // +1 because the first character is 1, to human beings.
      description, expr, static_cast<unsigned>(expr_itr - expr.cbegin() + 1)
   ));
}

void regex::extract_capture(regex_capture_format * format) {
   LOFTY_TRACE_FUNC(this, format);

   if (expr_itr != expr_end) {
      // The capture specifies a format expression.
      auto expr_begin(expr_itr);
      bool escape = false;
      for (; expr_itr != expr_end; ++expr_itr) {
         if (escape) {
            escape = false;
         } else {
            char32_t cp = *expr_itr;
            if (cp == '\\') {
               escape = true;
            } else if (cp == ')') {
               break;
            }
         }
      }
      if (expr_itr == expr_end) {
         throw_syntax_error(LOFTY_SL("unterminated capturing group"));
      }
      format->expr = str(external_buffer, expr_begin.ptr(), expr_itr.char_index() - expr_begin.char_index());
   } else {
      format->expr.clear();
   }

   if (expr_itr == expr_end || *expr_itr != ')') {
      throw_syntax_error(LOFTY_SL("unterminated capturing group"));
   }
   // Also consume the last parenthesis, so it won’t trigger end_subexpr in parse_up_to_next_capture().
   ++expr_itr;
}

void regex::parse_negative_bracket_expression() {
   LOFTY_TRACE_FUNC(this);

   char32_t next_range_begin = *expr_itr++ + 1;
   // Start with the first alternative.
   // TODO: this is not right if *expr_itr is NUL (rare).
   push_state(parser->create_code_point_range_state(0x000000, next_range_begin - 1));
   bool forming_range = false, escape = false;
   while (expr_itr != expr_end) {
      char32_t cp = *expr_itr++;
      if (cp == ']' && !escape) {
         if (forming_range) {
            // Turns out the dash/hyphen did not indicate a range; make two new states around it.
            auto & curr_subexpr = subexpr_stack.back();
            curr_subexpr.push_alternative(parser->create_code_point_range_state(next_range_begin, '-' - 1));
            curr_subexpr.push_alternative(parser->create_code_point_range_state('-' + 1, 0xffffff));
         }
         return;
      }
      if (forming_range) {
         forming_range = false;
         next_range_begin = cp + 1;
      } else {
         if (escape) {
            escape = false;
            goto escaped;
         }
         switch (cp) {
            case '-':
               forming_range = true;
               break;
            case '\\':
               escape = true;
               break;
            default:
            escaped:
               if (cp > next_range_begin) {
                  // Skipping one or more code points; make a new range for the gap.
                  auto & curr_subexpr = subexpr_stack.back();
                  curr_subexpr.push_alternative(parser->create_code_point_range_state(next_range_begin, cp - 1));
               }
               next_range_begin = cp + 1;
               break;
         }
      }
   }
   throw_syntax_error(LOFTY_SL("unexpected end of bracket expression"));
}

void regex::parse_positive_bracket_expression() {
   LOFTY_TRACE_FUNC(this);

   auto last_range_state = parser->create_code_point_state(*expr_itr++);
   push_state(last_range_state);
   bool forming_range = false, escape = false;
   // Start a series of alternatives.
   while (expr_itr != expr_end) {
      char32_t cp = *expr_itr++;
      if (cp == ']' && !escape) {
         if (forming_range) {
            // Turns out the dash/hyphen did not indicate a range; make a new state for it.
            subexpr_stack.back().push_alternative(parser->create_code_point_state('-'));
         }
         return;
      }
      if (forming_range) {
         forming_range = false;
         // Change the last code point state into a range.
         last_range_state->with_data<dynamic::_state_cp_range_data>()->last = cp;
      } else {
         if (escape) {
            escape = false;
            goto escaped;
         }
         switch (cp) {
            case '-':
               forming_range = true;
               break;
            case '\\':
               escape = true;
               break;
            default:
            escaped:
               // Individual code point: make a new state for it.
               last_range_state = parser->create_code_point_state(cp);
               subexpr_stack.back().push_alternative(last_range_state);
               break;
         }
      }
   }
   throw_syntax_error(LOFTY_SL("unexpected end of bracket expression"));
}

_std::tuple<std::uint16_t, std::uint16_t> regex::parse_repetition_range() {
   LOFTY_TRACE_FUNC(this);

   bool empty = true;
   std::uint16_t begin = 0, end = 0;
   while (expr_itr != expr_end) {
      char32_t cp = *expr_itr;
      if (cp < '0' || cp > '9') {
         break;
      }
      begin = static_cast<std::uint16_t>(begin * 10 + static_cast<std::uint16_t>(cp - '0'));
      empty = false;
      ++expr_itr;
   }
   if (expr_itr != expr_end && *expr_itr == ',') {
      while (++expr_itr != expr_end) {
         char32_t cp = *expr_itr;
         if (cp < '0' || cp > '9') {
            break;
         }
         begin = static_cast<std::uint16_t>(begin * 10 + static_cast<std::uint16_t>(cp - '0'));
         empty = false;
      }
   }
   if (expr_itr == expr_end || *expr_itr != '}' || empty) {
      throw_syntax_error(LOFTY_SL("malformed repetition range"));
   }
   // Consume the closing “}”.
   ++expr_itr;
   return _std::make_tuple(begin, end);
}

dynamic_state * regex::parse_with_no_captures() {
   dynamic_state * ret;
   if (parse_up_to_next_capture(nullptr, &ret) >= 0) {
      throw_syntax_error(LOFTY_SL("capturing groups not supported in this expression"));
   }
   return ret;
}

void regex::push_state(dynamic_state * next_state) {
   LOFTY_TRACE_FUNC(this, next_state);

   if (enter_rep_group) {
      auto repetition_group = parser->create_repetition_group(next_state, 1, 1);
      // Re-terminate the previous sub-expression with the newly-created group.
      prev_subexpr.terminate_with_next_state(repetition_group);
      auto & curr_subexpr = subexpr_stack.back();
      curr_subexpr.curr_state = repetition_group;
   } else if (begin_alternative) {
      begin_alternative = false;
      subexpr_stack.back().push_alternative(next_state);
   } else {
      if (end_subexpr) {
         /* Terminate the last sub-expression with the next state, popping it off the stack but tracking it as
         the previous sub-expression. */
         end_subexpr = false;
         prev_subexpr = subexpr_stack.pop_back();
         prev_subexpr.terminate_with_next_state(next_state);
      } else {
         // Stay in the same sub-expression, but track the previous state as its own sub-expression.
         prev_subexpr.curr_state = nullptr;
         prev_subexpr.alternative_last_states.clear();
         if (auto prev_state = subexpr_stack.back().curr_state) {
            prev_subexpr.push_next(prev_state);
            /*while (auto next_alternative = prev_state->alternative) {
               prev_subexpr.alternative_last_states.push_back(next_alternative);
               next_alternative = next_alternative->alternative;
            }*/
         }
      }
      subexpr_stack.back().push_next(next_state);
   }
}

void regex::set_curr_state_repetitions(std::uint16_t min, std::uint16_t max) {
   LOFTY_TRACE_FUNC(this, min, max);

   // Make sure we have a repetition group to apply the number of occurrences to.
   auto & curr_subexpr = subexpr_stack.back();
   if (!curr_subexpr.curr_state) {
      throw_syntax_error(LOFTY_SL("expression cannot start with ?*+{"));
   }
   if (curr_subexpr.curr_state->type == dynamic_state::_type::repetition_group) {
      // Already a group; just adjust the range.
      // TODO: block expressions like “a{1,3}{2,4}”, which in this implementation become “a{2,4}”.
      auto repetition_group = curr_subexpr.curr_state->with_data<dynamic::_state_repetition_group_data>();
      repetition_group->min = min;
      repetition_group->max = max;
   } else {
      auto repetition_group = parser->create_repetition_group(curr_subexpr.curr_state, min, max);
      // Re-terminate the previous sub-expression with the newly-created group.
      prev_subexpr.terminate_with_next_state(repetition_group);
      if (curr_subexpr.first_state == curr_subexpr.curr_state) {
         curr_subexpr.first_state = repetition_group;
      }
      curr_subexpr.curr_state = repetition_group;
   }
}

}}} //namespace lofty::text::parsers
<commit_msg>Sort methods<commit_after>/* -*- coding: utf-8; mode: c++; tab-width: 3; indent-tabs-mode: nil -*-

Copyright 2017 Raffaello D. Di Napoli

This file is part of Lofty.

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

Lofty 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 Lofty. If not, see
<http://www.gnu.org/licenses/>.
------------------------------------------------------------------------------------------------------------*/

#include <lofty.hxx>
#include <lofty/text.hxx>
#include <lofty/text/parsers/dynamic.hxx>
#include <lofty/text/parsers/regex.hxx>

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

namespace lofty { namespace text { namespace parsers {

regex::subexpression::subexpression() :
   first_state(nullptr),
   curr_alternative_first_state(nullptr),
   curr_state(nullptr) {
}

regex::subexpression::~subexpression() {
}

void regex::subexpression::push_alternative(dynamic_state * new_state) {
   if (curr_state) {
      alternative_last_states.push_back(curr_state);
      curr_alternative_first_state->set_alternative(new_state);
   } else {
      first_state = new_state;
   }
   curr_state = new_state;
   curr_alternative_first_state = new_state;
}

void regex::subexpression::push_next(dynamic_state * new_state) {
   if (curr_state) {
      curr_state->set_next(new_state);
   } else {
      first_state = new_state;
      curr_alternative_first_state = new_state;
   }
   curr_state = new_state;
}

void regex::subexpression::terminate_with_next_state(dynamic_state * next_state) {
   if (curr_state) {
      alternative_last_states.push_back(curr_state);
      curr_state = nullptr;
   }
   LOFTY_FOR_EACH(auto & state, alternative_last_states) {
      state->set_next(next_state);
   }
}

regex::regex(dynamic * parser_, str const & expr_) :
   parser(parser_),
   expr(expr_),
   expr_itr(expr.cbegin()),
   expr_end(expr.cend()),
   next_capture_index(0),
   enter_rep_group(false),
   end_subexpr(false),
   begin_alternative(false) {
   subexpr_stack.push_back(subexpression());
}

regex::~regex() {
}

void regex::insert_capture_group(dynamic_state const * first_state) {
   LOFTY_TRACE_FUNC(this, first_state);

   push_state(parser->create_capture_group(first_state));
}

void regex::extract_capture(regex_capture_format * format) {
   LOFTY_TRACE_FUNC(this, format);

   if (expr_itr != expr_end) {
      // The capture specifies a format expression.
      auto expr_begin(expr_itr);
      bool escape = false;
      for (; expr_itr != expr_end; ++expr_itr) {
         if (escape) {
            escape = false;
         } else {
            char32_t cp = *expr_itr;
            if (cp == '\\') {
               escape = true;
            } else if (cp == ')') {
               break;
            }
         }
      }
      if (expr_itr == expr_end) {
         throw_syntax_error(LOFTY_SL("unterminated capturing group"));
      }
      format->expr = str(external_buffer, expr_begin.ptr(), expr_itr.char_index() - expr_begin.char_index());
   } else {
      format->expr.clear();
   }

   if (expr_itr == expr_end || *expr_itr != ')') {
      throw_syntax_error(LOFTY_SL("unterminated capturing group"));
   }
   // Also consume the last parenthesis, so it won’t trigger end_subexpr in parse_up_to_next_capture().
   ++expr_itr;
}

void regex::parse_negative_bracket_expression() {
   LOFTY_TRACE_FUNC(this);

   char32_t next_range_begin = *expr_itr++ + 1;
   // Start with the first alternative.
   // TODO: this is not right if *expr_itr is NUL (rare).
   push_state(parser->create_code_point_range_state(0x000000, next_range_begin - 1));
   bool forming_range = false, escape = false;
   while (expr_itr != expr_end) {
      char32_t cp = *expr_itr++;
      if (cp == ']' && !escape) {
         if (forming_range) {
            // Turns out the dash/hyphen did not indicate a range; make two new states around it.
            auto & curr_subexpr = subexpr_stack.back();
            curr_subexpr.push_alternative(parser->create_code_point_range_state(next_range_begin, '-' - 1));
            curr_subexpr.push_alternative(parser->create_code_point_range_state('-' + 1, 0xffffff));
         }
         return;
      }
      if (forming_range) {
         forming_range = false;
         next_range_begin = cp + 1;
      } else {
         if (escape) {
            escape = false;
            goto escaped;
         }
         switch (cp) {
            case '-':
               forming_range = true;
               break;
            case '\\':
               escape = true;
               break;
            default:
            escaped:
               if (cp > next_range_begin) {
                  // Skipping one or more code points; make a new range for the gap.
                  auto & curr_subexpr = subexpr_stack.back();
                  curr_subexpr.push_alternative(parser->create_code_point_range_state(next_range_begin, cp - 1));
               }
               next_range_begin = cp + 1;
               break;
         }
      }
   }
   throw_syntax_error(LOFTY_SL("unexpected end of bracket expression"));
}

void regex::parse_positive_bracket_expression() {
   LOFTY_TRACE_FUNC(this);

   auto last_range_state = parser->create_code_point_state(*expr_itr++);
   push_state(last_range_state);
   bool forming_range = false, escape = false;
   // Start a series of alternatives.
   while (expr_itr != expr_end) {
      char32_t cp = *expr_itr++;
      if (cp == ']' && !escape) {
         if (forming_range) {
            // Turns out the dash/hyphen did not indicate a range; make a new state for it.
            subexpr_stack.back().push_alternative(parser->create_code_point_state('-'));
         }
         return;
      }
      if (forming_range) {
         forming_range = false;
         // Change the last code point state into a range.
         last_range_state->with_data<dynamic::_state_cp_range_data>()->last = cp;
      } else {
         if (escape) {
            escape = false;
            goto escaped;
         }
         switch (cp) {
            case '-':
               forming_range = true;
               break;
            case '\\':
               escape = true;
               break;
            default:
            escaped:
               // Individual code point: make a new state for it.
               last_range_state = parser->create_code_point_state(cp);
               subexpr_stack.back().push_alternative(last_range_state);
               break;
         }
      }
   }
   throw_syntax_error(LOFTY_SL("unexpected end of bracket expression"));
}

_std::tuple<std::uint16_t, std::uint16_t> regex::parse_repetition_range() {
   LOFTY_TRACE_FUNC(this);

   bool empty = true;
   std::uint16_t begin = 0, end = 0;
   while (expr_itr != expr_end) {
      char32_t cp = *expr_itr;
      if (cp < '0' || cp > '9') {
         break;
      }
      begin = static_cast<std::uint16_t>(begin * 10 + static_cast<std::uint16_t>(cp - '0'));
      empty = false;
      ++expr_itr;
   }
   if (expr_itr != expr_end && *expr_itr == ',') {
      while (++expr_itr != expr_end) {
         char32_t cp = *expr_itr;
         if (cp < '0' || cp > '9') {
            break;
         }
         begin = static_cast<std::uint16_t>(begin * 10 + static_cast<std::uint16_t>(cp - '0'));
         empty = false;
      }
   }
   if (expr_itr == expr_end || *expr_itr != '}' || empty) {
      throw_syntax_error(LOFTY_SL("malformed repetition range"));
   }
   // Consume the closing “}”.
   ++expr_itr;
   return _std::make_tuple(begin, end);
}

int regex::parse_up_to_next_capture(regex_capture_format * capture_format, dynamic_state ** first_state) {
   LOFTY_TRACE_FUNC(this, capture_format, first_state);

   bool escape = false;
   while (expr_itr != expr_end) {
      char32_t cp = *expr_itr++;
      if (escape) {
         escape = false;
         goto escaped;
      }
      switch (cp) {
         case '.':
            push_state(parser->create_code_point_range_state(0x000000, 0xffffff));
            break;
         case '[':
            if (expr_itr >= expr_end) {
               throw_syntax_error(LOFTY_SL("unexpected end of bracket expression"));
            }
            subexpr_stack.push_back(subexpression());
            if (*expr_itr == '^') {
               if (++expr_itr >= expr_end) {
                  throw_syntax_error(LOFTY_SL("unexpected end of negative bracket expression"));
               }
               parse_negative_bracket_expression();
            } else {
               parse_positive_bracket_expression();
            }
            end_subexpr = true;
            break;
         case '\\':
            escape = true;
            break;
         case '(': {
            if (expr_itr >= expr_end) {
               throw_syntax_error(LOFTY_SL("unexpected end of group"));
            }
            if (*expr_itr != '?') {
               if (capture_format) {
                  extract_capture(capture_format);
                  return static_cast<int>(next_capture_index++);
               } else {
                  /* The caller is not interested in receiving the capture’s format; just report that we found
                  a capture. */
                  return 0;
               }
            }
            ++expr_itr;
            if (expr_itr >= expr_end) {
               throw_syntax_error(LOFTY_SL("unexpected end of non-capturing group"));
            }
            if (*expr_itr == ':') {
               ++expr_itr;
               enter_rep_group = true;
            } else {
               throw_syntax_error(LOFTY_SL("unsupported non-capturing group type"));
            }
            break;
         }
         case ')':
            end_subexpr = true;
            break;
         case '*':
            set_curr_state_repetitions(0, 0);
            break;
         case '+':
            set_curr_state_repetitions(1, 0);
            break;
         case '?':
            set_curr_state_repetitions(0, 1);
            break;
         case '{': {
            auto range(parse_repetition_range());
            set_curr_state_repetitions(_std::get<0>(range), _std::get<1>(range));
            break;
         }
         case '|':
            begin_alternative = true;
            break;
         case '^':
            push_state(parser->create_begin_state());
            break;
         case '$':
            push_state(parser->create_end_state());
            break;
         default:
         escaped:
            push_state(parser->create_code_point_state(cp));
            break;
      }
   }
   if (subexpr_stack.size() != 1) {
      throw_syntax_error(LOFTY_SL("mismatched parentheses"));
   }
   *first_state = subexpr_stack.front().first_state;
   // No need to terminate the last subexpression; it should be already nullptr-terminated.
   subexpr_stack.clear();
   return -1;
}

dynamic_state * regex::parse_with_no_captures() {
   dynamic_state * ret;
   if (parse_up_to_next_capture(nullptr, &ret) >= 0) {
      throw_syntax_error(LOFTY_SL("capturing groups not supported in this expression"));
   }
   return ret;
}

void regex::push_state(dynamic_state * next_state) {
   LOFTY_TRACE_FUNC(this, next_state);

   if (enter_rep_group) {
      auto repetition_group = parser->create_repetition_group(next_state, 1, 1);
      // Re-terminate the previous sub-expression with the newly-created group.
      prev_subexpr.terminate_with_next_state(repetition_group);
      auto & curr_subexpr = subexpr_stack.back();
      curr_subexpr.curr_state = repetition_group;
   } else if (begin_alternative) {
      begin_alternative = false;
      subexpr_stack.back().push_alternative(next_state);
   } else {
      if (end_subexpr) {
         /* Terminate the last sub-expression with the next state, popping it off the stack but tracking it as
         the previous sub-expression. */
         end_subexpr = false;
         prev_subexpr = subexpr_stack.pop_back();
         prev_subexpr.terminate_with_next_state(next_state);
      } else {
         // Stay in the same sub-expression, but track the previous state as its own sub-expression.
         prev_subexpr.curr_state = nullptr;
         prev_subexpr.alternative_last_states.clear();
         if (auto prev_state = subexpr_stack.back().curr_state) {
            prev_subexpr.push_next(prev_state);
            /*while (auto next_alternative = prev_state->alternative) {
               prev_subexpr.alternative_last_states.push_back(next_alternative);
               next_alternative = next_alternative->alternative;
            }*/
         }
      }
      subexpr_stack.back().push_next(next_state);
   }
}

void regex::set_curr_state_repetitions(std::uint16_t min, std::uint16_t max) {
   LOFTY_TRACE_FUNC(this, min, max);

   // Make sure we have a repetition group to apply the number of occurrences to.
   auto & curr_subexpr = subexpr_stack.back();
   if (!curr_subexpr.curr_state) {
      throw_syntax_error(LOFTY_SL("expression cannot start with ?*+{"));
   }
   if (curr_subexpr.curr_state->type == dynamic_state::_type::repetition_group) {
      // Already a group; just adjust the range.
      // TODO: block expressions like “a{1,3}{2,4}”, which in this implementation become “a{2,4}”.
      auto repetition_group = curr_subexpr.curr_state->with_data<dynamic::_state_repetition_group_data>();
      repetition_group->min = min;
      repetition_group->max = max;
   } else {
      auto repetition_group = parser->create_repetition_group(curr_subexpr.curr_state, min, max);
      // Re-terminate the previous sub-expression with the newly-created group.
      prev_subexpr.terminate_with_next_state(repetition_group);
      if (curr_subexpr.first_state == curr_subexpr.curr_state) {
         curr_subexpr.first_state = repetition_group;
      }
      curr_subexpr.curr_state = repetition_group;
   }
}

void regex::throw_syntax_error(str const & description) const {
   LOFTY_THROW(syntax_error, (
      // +1 because the first character is 1, to human beings.
      description, expr, static_cast<unsigned>(expr_itr - expr.cbegin() + 1)
   ));
}

}}} //namespace lofty::text::parsers
<|endoftext|>


<commit_before>#ifdef PIXELBOOST_PLATFORM_IOS

#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <ifaddrs.h>

#include "pixelboost/network/networkHelpers.h"

namespace pb
{

namespace NetworkHelpers
{

std::string GetWifiAddress()
{
#if TARGET_IPHONE_SIMULATOR
    return "127.0.0.1";
#else
    std::string address = "WiFi not enabled";
    struct ifaddrs *interfaces = NULL;
    struct ifaddrs *temp_addr = NULL;

    int success;
    success = getifaddrs(&interfaces);

    if (success == 0)
    {
        // Loop through linked list of interfaces
        temp_addr = interfaces;
        while(temp_addr != NULL)
        {
            if(temp_addr->ifa_addr->sa_family == AF_INET)
            {
                // en0 = iPhone WiFi connection
                if(strcmp(temp_addr->ifa_name, "en0") == 0)
                {
                    address = inet_ntoa(((struct sockaddr_in *)temp_addr->ifa_addr)->sin_addr);
                }
            }
            temp_addr = temp_addr->ifa_next;
        }
    }

    freeifaddrs(interfaces);
    return address;
#endif
}

}

}

#endif<commit_msg>Fix GetWifiAddress on Android<commit_after>#ifdef PIXELBOOST_PLATFORM_ANDROID

#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <ifaddrs.h>

#include "pixelboost/network/networkHelpers.h"

namespace pb
{

namespace NetworkHelpers
{

std::string GetWifiAddress()
{
    return "";
}

}

}

#endif<|endoftext|>


<commit_before>/*
 * This file is part of the µOS++ distribution.
 *   (https://github.com/micro-os-plus)
 * Copyright (c) 2015 Liviu Ionescu.
 *
 * µOS++ is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation, version 3.
 *
 * µOS++ 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 program. If not, see <http://www.gnu.org/licenses/>.
 */

#include "posix-io/CharDevicesRegistry.h"
#include "posix-io/CharDevice.h"

#include <cassert>
#include <cstring>
#include <cstdlib>
#if defined(OS_INCLUDE_TRACE_PRINTF)
#include "diag/Trace.h"
#endif

// ----------------------------------------------------------------------------

namespace os
{
  namespace posix
  {
    // ------------------------------------------------------------------------

    std::size_t CharDevicesRegistry::sfSize;

    CharDevice** CharDevicesRegistry::sfRegistryArray;

    // ------------------------------------------------------------------------

    CharDevicesRegistry::CharDevicesRegistry (std::size_t size)
    {
      assert(size > 0);

      sfSize = size;
      sfRegistryArray = new CharDevice*[size];

      for (std::size_t i = 0; i < size; ++i)
        {
          sfRegistryArray[i] = nullptr;
        }
    }

    CharDevicesRegistry::~CharDevicesRegistry ()
    {
      delete[] sfRegistryArray;
      sfSize = 0;
    }

    // ------------------------------------------------------------------------

    void
    CharDevicesRegistry::add (CharDevice* device)
    {
      bool found = false;
      for (std::size_t i = 0; i < sfSize; ++i)
        {
          if (sfRegistryArray[i] == nullptr)
            {
              sfRegistryArray[i] = device;
              found = true;
              continue;
            }

#if defined(DEBUG)
          // Validate the device name by checking duplicates.
          if (std::strcmp (device->getName (), sfRegistryArray[i]->getName ())
              == 0)
            {
              const char* msg = "Duplicate Device name. Abort.\n";
#if defined(OS_INCLUDE_TRACE_PRINTF)
              trace_printf(msg);
#else
              ::write (2, msg, strlen (msg));
#endif
              std::abort ();
            }
#endif
        }

      if (found)
        {
          return;
        }

      // TODO: call trace_printf() from the separate package, when available.
      const char* msg = "Max number of Devices reached. Abort.\n";
#if defined(OS_INCLUDE_TRACE_PRINTF)
      trace_printf(msg);
#else
      ::write (2, msg, strlen (msg));
#endif
      std::abort ();
    }

    void
    CharDevicesRegistry::remove (CharDevice* device)
    {
      for (std::size_t i = 0; i < sfSize; ++i)
        {
          if (sfRegistryArray[i] == device)
            {
              sfRegistryArray[i] = nullptr;
              return;
            }
        }

      // Not found... It would be good to tell.
    }

    /**
     * return pointer to device or nullptr if not found.
     */
    CharDevice*
    CharDevicesRegistry::identifyDevice (const char* path)
    {
      assert(path != nullptr);

      auto prefix = CharDevice::getDevicePrefix ();
      if (std::strncmp (prefix, path, std::strlen (prefix)) != 0)
        {
          // The device prefix does not match, not a device.
          return nullptr;
        }

      // The prefix was identified; try to match the rest of the path.
      auto name = path + std::strlen (prefix);
      for (std::size_t i = 0; i < sfSize; ++i)
        {
          if (sfRegistryArray[i] != nullptr
              && sfRegistryArray[i]->matchName (name))
            {
              // Return the first device that matches the path.
              return sfRegistryArray[i];
            }
        }

      // Not a known device.
      return nullptr;
    }

  } /* namespace posix */
} /* namespace os */

// ----------------------------------------------------------------------------
<commit_msg>CharDevicesRegistry::add() fix allocation<commit_after>/*
 * This file is part of the µOS++ distribution.
 *   (https://github.com/micro-os-plus)
 * Copyright (c) 2015 Liviu Ionescu.
 *
 * µOS++ is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation, version 3.
 *
 * µOS++ 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 program. If not, see <http://www.gnu.org/licenses/>.
 */

#include "posix-io/CharDevicesRegistry.h"
#include "posix-io/CharDevice.h"

#include <cassert>
#include <cstring>
#include <cstdlib>
#if defined(OS_INCLUDE_TRACE_PRINTF)
#include "diag/Trace.h"
#endif

// ----------------------------------------------------------------------------

namespace os
{
  namespace posix
  {
    // ------------------------------------------------------------------------

    std::size_t CharDevicesRegistry::sfSize;

    CharDevice** CharDevicesRegistry::sfRegistryArray;

    // ------------------------------------------------------------------------

    CharDevicesRegistry::CharDevicesRegistry (std::size_t size)
    {
      assert(size > 0);

      sfSize = size;
      sfRegistryArray = new CharDevice*[size];

      for (std::size_t i = 0; i < size; ++i)
        {
          sfRegistryArray[i] = nullptr;
        }
    }

    CharDevicesRegistry::~CharDevicesRegistry ()
    {
      delete[] sfRegistryArray;
      sfSize = 0;
    }

    // ------------------------------------------------------------------------

    void
    CharDevicesRegistry::add (CharDevice* device)
    {
#if defined(DEBUG)
      for (std::size_t i = 0; i < sfSize; ++i)
        {
          if (sfRegistryArray[i] == nullptr)
            {
              continue;
            }

          // Validate the device name by checking duplicates.
          if (std::strcmp (device->getName (), sfRegistryArray[i]->getName ())
              == 0)
            {
              const char* msg = "Duplicate Device name. Abort.\n";
#if defined(OS_INCLUDE_TRACE_PRINTF)
              trace_printf(msg);
#else
              ::write (2, msg, strlen (msg));
#endif
              std::abort ();
            }

        }
#endif // DEBUG

      for (std::size_t i = 0; i < sfSize; ++i)
        {
          if (sfRegistryArray[i] == nullptr)
            {
              sfRegistryArray[i] = device;
              return;
            }
        }

      // TODO: call trace_printf() from the separate package, when available.
      const char* msg = "Max number of Devices reached. Abort.\n";
#if defined(OS_INCLUDE_TRACE_PRINTF)
      trace_printf(msg);
#else
      ::write (2, msg, strlen (msg));
#endif
      std::abort ();
    }

    void
    CharDevicesRegistry::remove (CharDevice* device)
    {
      for (std::size_t i = 0; i < sfSize; ++i)
        {
          if (sfRegistryArray[i] == device)
            {
              sfRegistryArray[i] = nullptr;
              return;
            }
        }

      // Not found... It would be good to tell.
    }

    /**
     * return pointer to device or nullptr if not found.
     */
    CharDevice*
    CharDevicesRegistry::identifyDevice (const char* path)
    {
      assert(path != nullptr);

      auto prefix = CharDevice::getDevicePrefix ();
      if (std::strncmp (prefix, path, std::strlen (prefix)) != 0)
        {
          // The device prefix does not match, not a device.
          return nullptr;
        }

      // The prefix was identified; try to match the rest of the path.
      auto name = path + std::strlen (prefix);
      for (std::size_t i = 0; i < sfSize; ++i)
        {
          if (sfRegistryArray[i] != nullptr
              && sfRegistryArray[i]->matchName (name))
            {
              // Return the first device that matches the path.
              return sfRegistryArray[i];
            }
        }

      // Not a known device.
      return nullptr;
    }

  } /* namespace posix */
} /* namespace os */

// ----------------------------------------------------------------------------
<|endoftext|>


<commit_before> /*
  * jabbercontact.cpp  -  Base class for the Kopete Jabber protocol contact
  *
  * Copyright (c) 2002-2004 by Till Gerken <till@tantalo.net>
  * Copyright (c)      2006 by Olivier Goffart <ogoffart at kde.org>
  *
  * Kopete    (c) by the Kopete developers  <kopete-devel@kde.org>
  *
  * *************************************************************************
  * *                                                                       *
  * * This program is free software; you can redistribute it and/or modify  *
  * * it under the terms of the GNU General Public License as published by  *
  * * the Free Software Foundation; either version 2 of the License, or     *
  * * (at your option) any later version.                                   *
  * *                                                                       *
  * *************************************************************************
  */

#include <kdebug.h>
#include <klocale.h>
#include <kiconloader.h>
#include <qtimer.h>

#include <kopetegroup.h>
#include <kopetecontactlist.h>

#include "jabberbasecontact.h"

#include "xmpp_tasks.h"

#include "jabberprotocol.h"
#include "jabberaccount.h"
#include "jabberresource.h"
#include "jabberresourcepool.h"
#include "kopetemetacontact.h"
#include "kopetemessage.h"
#include "jabbertransport.h"

/**
 * JabberBaseContact constructor
 */
JabberBaseContact::JabberBaseContact (const XMPP::RosterItem &rosterItem, Kopete::Account *account, Kopete::MetaContact * mc, const QString &legacyId)
	: Kopete::Contact (account, legacyId.isEmpty() ? rosterItem.jid().full() : legacyId , mc )
{
	setDontSync ( false );

	JabberTransport *t=transport();
	m_account= t ? t->account() : static_cast<JabberAccount *>(Kopete::Contact::account());

	// take roster item and update display name
	updateContact ( rosterItem );

}

JabberProtocol *JabberBaseContact::protocol ()
{

	return static_cast<JabberProtocol *>(Kopete::Contact::protocol ());
}

JabberTransport * JabberBaseContact::transport( )
{
	return dynamic_cast<JabberTransport*>(Kopete::Contact::account());
}

/* Return if we are reachable (defaults to true because
   we can send on- and offline, only return false if the
   account itself is offline, too) */
bool JabberBaseContact::isReachable ()
{
	if (account()->isConnected())
		return true;

	return false;

}

void JabberBaseContact::updateContact ( const XMPP::RosterItem & item )
{
	kdDebug ( JABBER_DEBUG_GLOBAL ) << k_funcinfo << "Synchronizing local copy of " << contactId() << " with information received from server.  (name='" << item.name() << "' groups='" << item.groups() << "')"<< endl;

	mRosterItem = item;

	// if we don't have a meta contact yet, stop processing here
	if ( !metaContact () )
		return;

	/*
	 * We received the information from the server, as such,
	 * don't attempt to synch while we update our local copy.
	 */
	setDontSync ( true );

	// The myself contact is not in the roster on server, ignore this code
	// because the myself MetaContact displayname become the latest
	// Jabber acccount jid.
	if( metaContact() != Kopete::ContactList::self()->myself() )
	{
		// only update the alias if its not empty
		if ( !item.name().isEmpty () && item.name() != item.jid().bare() )
		{
			kdDebug ( JABBER_DEBUG_GLOBAL ) << k_funcinfo << "setting display name of " << contactId () << " to " << item.name() << endl;
			metaContact()->setDisplayName ( item.name () );
		}
	}

	/*
	 * Set the contact's subscription status
	 */
	switch ( item.subscription().type () )
	{
		case XMPP::Subscription::None:
			setProperty ( protocol()->propSubscriptionStatus,
						  i18n ( "You cannot see each others' status." ) );
			break;
		case XMPP::Subscription::To:
			setProperty ( protocol()->propSubscriptionStatus,
						  i18n ( "You can see this contact's status but they cannot see your status." ) );
			break;
		case XMPP::Subscription::From:
			setProperty ( protocol()->propSubscriptionStatus,
						  i18n ( "This contact can see your status but you cannot see their status." ) );
			break;
		case XMPP::Subscription::Both:
			setProperty ( protocol()->propSubscriptionStatus,
						  i18n ( "You can see each others' status." ) );
			break;
	}

	if( !metaContact()->isTemporary() )
	{
		/*
		* In this method, as opposed to KC::syncGroups(),
		* the group list from the server is authoritative.
		* As such, we need to find a list of all groups
		* that the meta contact resides in but does not
		* reside in on the server anymore, as well as all
		* groups that the meta contact does not reside in,
		* but resides in on the server.
		* Then, we'll have to synchronize the KMC using
		* that information.
		*/
		Kopete::GroupList groupsToRemoveFrom, groupsToAddTo;

		// find all groups our contact is in but that are not in the server side roster
		for ( unsigned i = 0; i < metaContact()->groups().count (); i++ )
		{
			if ( item.groups().find ( metaContact()->groups().at(i)->displayName () ) == item.groups().end () )
				groupsToRemoveFrom.append ( metaContact()->groups().at ( i ) );
		}

		// now find all groups that are in the server side roster but not in the local group
		for ( unsigned i = 0; i < item.groups().count (); i++ )
		{
			bool found = false;
			for ( unsigned j = 0; j < metaContact()->groups().count (); j++)
			{
				if ( metaContact()->groups().at(j)->displayName () == *item.groups().at(i) )
				{
					found = true;
					break;
				}
			}

			if ( !found )
			{
				groupsToAddTo.append ( Kopete::ContactList::self()->findGroup ( *item.groups().at(i) ) );
			}
		}

		/*
		* Special case: if we don't add the contact to any group and the
		* list of groups to remove from contains the top level group, we
		* risk removing the contact from the visible contact list. In this
		* case, we need to make sure at least the top level group stays.
		*/
		if ( ( groupsToAddTo.count () == 0 ) && ( groupsToRemoveFrom.contains ( Kopete::Group::topLevel () ) ) )
		{
			groupsToRemoveFrom.remove ( Kopete::Group::topLevel () );
		}

		for ( Kopete::Group *group = groupsToRemoveFrom.first (); group; group = groupsToRemoveFrom.next () )
		{
			kdDebug ( JABBER_DEBUG_GLOBAL ) << k_funcinfo << "Removing " << contactId() << " from group " << group->displayName () << endl;
			metaContact()->removeFromGroup ( group );
		}

		for ( Kopete::Group *group = groupsToAddTo.first (); group; group = groupsToAddTo.next () )
		{
			kdDebug ( JABBER_DEBUG_GLOBAL ) << k_funcinfo << "Adding " << contactId() << " to group " << group->displayName () << endl;
			metaContact()->addToGroup ( group );
		}
	}

	/*
	 * Enable updates for the server again.
	 */
	setDontSync ( false );

	//can't do it now because it's called from contructor at a point some virtual function are not available
	QTimer::singleShot(0, this, SLOT(reevaluateStatus()));

}

void JabberBaseContact::updateResourceList ()
{
	/*
	 * Set available resources.
	 * This is a bit more complicated: We need to generate
	 * all images dynamically from the KOS icons and store
	 * them into the mime factory, then plug them into
	 * the richtext.
	 */
	JabberResourcePool::ResourceList resourceList;
	account()->resourcePool()->findResources ( rosterItem().jid() , resourceList );

	if ( resourceList.isEmpty () )
	{
		removeProperty ( protocol()->propAvailableResources );
		return;
	}

	QString resourceListStr = "
";

	for ( JabberResourcePool::ResourceList::iterator it = resourceList.begin (); it != resourceList.end (); ++it )
	{
		// icon, resource name and priority
		resourceListStr += QString ( "
 %2 (Priority: %3)
" ).
						   arg ( protocol()->resourceToKOS((*it)->resource()).mimeSourceFor ( account () ),
								 (*it)->resource().name (), QString::number ( (*it)->resource().priority () ) );

		// client name, version, OS
		if ( !(*it)->clientName().isEmpty () )
		{
			resourceListStr += QString ( "
%1: %2 (%3)
" ).
							   arg ( i18n ( "Client" ), (*it)->clientName (), (*it)->clientSystem () );
		}

		// Supported features
#if 0  //disabled because it's just an ugly and long list of incomprehensible namespaces to the user
		QStringList supportedFeatures = (*it)->features().list();
		QStringList::ConstIterator featuresIt, featuresItEnd = supportedFeatures.constEnd();
		if( !supportedFeatures.empty() )
			resourceListStr += QString( "
Supported Features:" );
		for( featuresIt = supportedFeatures.constBegin(); featuresIt != featuresItEnd; ++featuresIt )
		{
			XMPP::Features tempFeature(*featuresIt);
			resourceListStr += QString("\n
");
			if ( tempFeature.id() > XMPP::Features::FID_None )
				resourceListStr += tempFeature.name() + QString(" (");
			resourceListStr += *featuresIt;
			if ( tempFeature.id() > Features::FID_None )
				resourceListStr += QString(")");
		}
		if( !supportedFeatures.empty() )
			resourceListStr += QString( "
" );
#endif

		// resource timestamp
		resourceListStr += QString ( "
%1: %2
" ).
						   arg ( i18n ( "Timestamp" ), KGlobal::locale()->formatDateTime ( (*it)->resource().status().timeStamp(), true, true ) );

		// message, if any
		if ( !(*it)->resource().status().status().stripWhiteSpace().isEmpty () )
		{
			resourceListStr += QString ( "
%1: %2
" ).
							   arg (
								i18n ( "Message" ),
								Kopete::Message::escape( (*it)->resource().status().status () )
								);
		}
	}

	resourceListStr += "
";

	setProperty ( protocol()->propAvailableResources, resourceListStr );
}

void JabberBaseContact::reevaluateStatus ()
{
	kdDebug (JABBER_DEBUG_GLOBAL) << k_funcinfo << "Determining new status for " << contactId () << endl;

	Kopete::OnlineStatus status;
	XMPP::Resource resource = account()->resourcePool()->bestResource ( mRosterItem.jid () );

	status = protocol()->resourceToKOS ( resource );

	/* Add some icon to show the subscription */
	if( ( mRosterItem.subscription().type() == XMPP::Subscription::None || mRosterItem.subscription().type() == XMPP::Subscription::From)
			 && inherits ( "JabberContact" ) && account()->myself() != this && account()->isConnected() )
	{
		status = Kopete::OnlineStatus(status.status() ,
									  status.weight() ,
									  protocol() ,
									  status.internalStatus() | 0x0100,
									  status.overlayIcons() + QStringList("status_unknown_overlay") , //FIXME: find better icon
									  status.description() );
	}

	updateResourceList ();

	kdDebug (JABBER_DEBUG_GLOBAL) << k_funcinfo << "New status for " << contactId () << " is " << status.description () << endl;
	setOnlineStatus ( status );

	/*
	 * Set away message property.
	 * We just need to read it from the current resource.
	 */
	if ( !resource.status ().status ().isEmpty () )
	{
		setProperty ( protocol()->propAwayMessage, resource.status().status () );
	}
	else
	{
		removeProperty ( protocol()->propAwayMessage );
	}

}

QString JabberBaseContact::fullAddress ()
{

	XMPP::Jid jid = rosterItem().jid();

	if ( jid.resource().isEmpty () )
	{
		jid.setResource ( account()->resourcePool()->bestResource ( jid ).name () );
	}

	return jid.full ();

}

XMPP::Jid JabberBaseContact::bestAddress ()
{

	// see if we are subscribed with a preselected resource
	if ( !mRosterItem.jid().resource().isEmpty () )
	{
		// we have a preselected resource, so return our default full address
		return mRosterItem.jid ();
	}

	// construct address out of user@host and current best resource
	XMPP::Jid jid = mRosterItem.jid ();
	jid.setResource ( account()->resourcePool()->bestResource( mRosterItem.jid() ).name () );

	return jid;

}

void JabberBaseContact::setDontSync ( bool flag )
{

	mDontSync = flag;

}

bool JabberBaseContact::dontSync ()
{

	return mDontSync;

}

void JabberBaseContact::serialize (QMap < QString, QString > &serializedData, QMap < QString, QString > & /* addressBookData */ )
{

	// Contact id and display name are already set for us, only add the rest
	serializedData["JID"] = mRosterItem.jid().full();

	serializedData["groups"] = mRosterItem.groups ().join (QString::fromLatin1 (","));
}

#include "jabberbasecontact.moc"

// vim: set noet ts=4 sts=4 sw=4:
<commit_msg>don't put the 'unknwon' icon on transport,  some transports doesn't authorize us, but it doesn't seems to be a problem<commit_after> /*
  * jabbercontact.cpp  -  Base class for the Kopete Jabber protocol contact
  *
  * Copyright (c) 2002-2004 by Till Gerken <till@tantalo.net>
  * Copyright (c)      2006 by Olivier Goffart <ogoffart at kde.org>
  *
  * Kopete    (c) by the Kopete developers  <kopete-devel@kde.org>
  *
  * *************************************************************************
  * *                                                                       *
  * * This program is free software; you can redistribute it and/or modify  *
  * * it under the terms of the GNU General Public License as published by  *
  * * the Free Software Foundation; either version 2 of the License, or     *
  * * (at your option) any later version.                                   *
  * *                                                                       *
  * *************************************************************************
  */

#include <kdebug.h>
#include <klocale.h>
#include <kiconloader.h>
#include <qtimer.h>

#include <kopetegroup.h>
#include <kopetecontactlist.h>

#include "jabberbasecontact.h"

#include "xmpp_tasks.h"

#include "jabberprotocol.h"
#include "jabberaccount.h"
#include "jabberresource.h"
#include "jabberresourcepool.h"
#include "kopetemetacontact.h"
#include "kopetemessage.h"
#include "jabbertransport.h"

/**
 * JabberBaseContact constructor
 */
JabberBaseContact::JabberBaseContact (const XMPP::RosterItem &rosterItem, Kopete::Account *account, Kopete::MetaContact * mc, const QString &legacyId)
	: Kopete::Contact (account, legacyId.isEmpty() ? rosterItem.jid().full() : legacyId , mc )
{
	setDontSync ( false );

	JabberTransport *t=transport();
	m_account= t ? t->account() : static_cast<JabberAccount *>(Kopete::Contact::account());

	// take roster item and update display name
	updateContact ( rosterItem );

}

JabberProtocol *JabberBaseContact::protocol ()
{

	return static_cast<JabberProtocol *>(Kopete::Contact::protocol ());
}

JabberTransport * JabberBaseContact::transport( )
{
	return dynamic_cast<JabberTransport*>(Kopete::Contact::account());
}

/* Return if we are reachable (defaults to true because
   we can send on- and offline, only return false if the
   account itself is offline, too) */
bool JabberBaseContact::isReachable ()
{
	if (account()->isConnected())
		return true;

	return false;

}

void JabberBaseContact::updateContact ( const XMPP::RosterItem & item )
{
	kdDebug ( JABBER_DEBUG_GLOBAL ) << k_funcinfo << "Synchronizing local copy of " << contactId() << " with information received from server.  (name='" << item.name() << "' groups='" << item.groups() << "')"<< endl;

	mRosterItem = item;

	// if we don't have a meta contact yet, stop processing here
	if ( !metaContact () )
		return;

	/*
	 * We received the information from the server, as such,
	 * don't attempt to synch while we update our local copy.
	 */
	setDontSync ( true );

	// The myself contact is not in the roster on server, ignore this code
	// because the myself MetaContact displayname become the latest
	// Jabber acccount jid.
	if( metaContact() != Kopete::ContactList::self()->myself() )
	{
		// only update the alias if its not empty
		if ( !item.name().isEmpty () && item.name() != item.jid().bare() )
		{
			kdDebug ( JABBER_DEBUG_GLOBAL ) << k_funcinfo << "setting display name of " << contactId () << " to " << item.name() << endl;
			metaContact()->setDisplayName ( item.name () );
		}
	}

	/*
	 * Set the contact's subscription status
	 */
	switch ( item.subscription().type () )
	{
		case XMPP::Subscription::None:
			setProperty ( protocol()->propSubscriptionStatus,
						  i18n ( "You cannot see each others' status." ) );
			break;
		case XMPP::Subscription::To:
			setProperty ( protocol()->propSubscriptionStatus,
						  i18n ( "You can see this contact's status but they cannot see your status." ) );
			break;
		case XMPP::Subscription::From:
			setProperty ( protocol()->propSubscriptionStatus,
						  i18n ( "This contact can see your status but you cannot see their status." ) );
			break;
		case XMPP::Subscription::Both:
			setProperty ( protocol()->propSubscriptionStatus,
						  i18n ( "You can see each others' status." ) );
			break;
	}

	if( !metaContact()->isTemporary() )
	{
		/*
		* In this method, as opposed to KC::syncGroups(),
		* the group list from the server is authoritative.
		* As such, we need to find a list of all groups
		* that the meta contact resides in but does not
		* reside in on the server anymore, as well as all
		* groups that the meta contact does not reside in,
		* but resides in on the server.
		* Then, we'll have to synchronize the KMC using
		* that information.
		*/
		Kopete::GroupList groupsToRemoveFrom, groupsToAddTo;

		// find all groups our contact is in but that are not in the server side roster
		for ( unsigned i = 0; i < metaContact()->groups().count (); i++ )
		{
			if ( item.groups().find ( metaContact()->groups().at(i)->displayName () ) == item.groups().end () )
				groupsToRemoveFrom.append ( metaContact()->groups().at ( i ) );
		}

		// now find all groups that are in the server side roster but not in the local group
		for ( unsigned i = 0; i < item.groups().count (); i++ )
		{
			bool found = false;
			for ( unsigned j = 0; j < metaContact()->groups().count (); j++)
			{
				if ( metaContact()->groups().at(j)->displayName () == *item.groups().at(i) )
				{
					found = true;
					break;
				}
			}

			if ( !found )
			{
				groupsToAddTo.append ( Kopete::ContactList::self()->findGroup ( *item.groups().at(i) ) );
			}
		}

		/*
		* Special case: if we don't add the contact to any group and the
		* list of groups to remove from contains the top level group, we
		* risk removing the contact from the visible contact list. In this
		* case, we need to make sure at least the top level group stays.
		*/
		if ( ( groupsToAddTo.count () == 0 ) && ( groupsToRemoveFrom.contains ( Kopete::Group::topLevel () ) ) )
		{
			groupsToRemoveFrom.remove ( Kopete::Group::topLevel () );
		}

		for ( Kopete::Group *group = groupsToRemoveFrom.first (); group; group = groupsToRemoveFrom.next () )
		{
			kdDebug ( JABBER_DEBUG_GLOBAL ) << k_funcinfo << "Removing " << contactId() << " from group " << group->displayName () << endl;
			metaContact()->removeFromGroup ( group );
		}

		for ( Kopete::Group *group = groupsToAddTo.first (); group; group = groupsToAddTo.next () )
		{
			kdDebug ( JABBER_DEBUG_GLOBAL ) << k_funcinfo << "Adding " << contactId() << " to group " << group->displayName () << endl;
			metaContact()->addToGroup ( group );
		}
	}

	/*
	 * Enable updates for the server again.
	 */
	setDontSync ( false );

	//can't do it now because it's called from contructor at a point some virtual function are not available
	QTimer::singleShot(0, this, SLOT(reevaluateStatus()));

}

void JabberBaseContact::updateResourceList ()
{
	/*
	 * Set available resources.
	 * This is a bit more complicated: We need to generate
	 * all images dynamically from the KOS icons and store
	 * them into the mime factory, then plug them into
	 * the richtext.
	 */
	JabberResourcePool::ResourceList resourceList;
	account()->resourcePool()->findResources ( rosterItem().jid() , resourceList );

	if ( resourceList.isEmpty () )
	{
		removeProperty ( protocol()->propAvailableResources );
		return;
	}

	QString resourceListStr = "
";

	for ( JabberResourcePool::ResourceList::iterator it = resourceList.begin (); it != resourceList.end (); ++it )
	{
		// icon, resource name and priority
		resourceListStr += QString ( "
 %2 (Priority: %3)
" ).
						   arg ( protocol()->resourceToKOS((*it)->resource()).mimeSourceFor ( account () ),
								 (*it)->resource().name (), QString::number ( (*it)->resource().priority () ) );

		// client name, version, OS
		if ( !(*it)->clientName().isEmpty () )
		{
			resourceListStr += QString ( "
%1: %2 (%3)
" ).
							   arg ( i18n ( "Client" ), (*it)->clientName (), (*it)->clientSystem () );
		}

		// Supported features
#if 0  //disabled because it's just an ugly and long list of incomprehensible namespaces to the user
		QStringList supportedFeatures = (*it)->features().list();
		QStringList::ConstIterator featuresIt, featuresItEnd = supportedFeatures.constEnd();
		if( !supportedFeatures.empty() )
			resourceListStr += QString( "
Supported Features:" );
		for( featuresIt = supportedFeatures.constBegin(); featuresIt != featuresItEnd; ++featuresIt )
		{
			XMPP::Features tempFeature(*featuresIt);
			resourceListStr += QString("\n
");
			if ( tempFeature.id() > XMPP::Features::FID_None )
				resourceListStr += tempFeature.name() + QString(" (");
			resourceListStr += *featuresIt;
			if ( tempFeature.id() > Features::FID_None )
				resourceListStr += QString(")");
		}
		if( !supportedFeatures.empty() )
			resourceListStr += QString( "
" );
#endif

		// resource timestamp
		resourceListStr += QString ( "
%1: %2
" ).
						   arg ( i18n ( "Timestamp" ), KGlobal::locale()->formatDateTime ( (*it)->resource().status().timeStamp(), true, true ) );

		// message, if any
		if ( !(*it)->resource().status().status().stripWhiteSpace().isEmpty () )
		{
			resourceListStr += QString ( "
%1: %2
" ).
							   arg (
								i18n ( "Message" ),
								Kopete::Message::escape( (*it)->resource().status().status () )
								);
		}
	}

	resourceListStr += "
";

	setProperty ( protocol()->propAvailableResources, resourceListStr );
}

void JabberBaseContact::reevaluateStatus ()
{
	kdDebug (JABBER_DEBUG_GLOBAL) << k_funcinfo << "Determining new status for " << contactId () << endl;

	Kopete::OnlineStatus status;
	XMPP::Resource resource = account()->resourcePool()->bestResource ( mRosterItem.jid () );

	status = protocol()->resourceToKOS ( resource );

	/* Add some icon to show the subscription */
	if( ( mRosterItem.subscription().type() == XMPP::Subscription::None || mRosterItem.subscription().type() == XMPP::Subscription::From)
			 && inherits ( "JabberContact" ) && metaContact() != Kopete::ContactList::self()->myself() && account()->isConnected() )
	{
		status = Kopete::OnlineStatus(status.status() ,
									  status.weight() ,
									  protocol() ,
									  status.internalStatus() | 0x0100,
									  status.overlayIcons() + QStringList("status_unknown_overlay") , //FIXME: find better icon
									  status.description() );
	}

	updateResourceList ();

	kdDebug (JABBER_DEBUG_GLOBAL) << k_funcinfo << "New status for " << contactId () << " is " << status.description () << endl;
	setOnlineStatus ( status );

	/*
	 * Set away message property.
	 * We just need to read it from the current resource.
	 */
	if ( !resource.status ().status ().isEmpty () )
	{
		setProperty ( protocol()->propAwayMessage, resource.status().status () );
	}
	else
	{
		removeProperty ( protocol()->propAwayMessage );
	}

}

QString JabberBaseContact::fullAddress ()
{

	XMPP::Jid jid = rosterItem().jid();

	if ( jid.resource().isEmpty () )
	{
		jid.setResource ( account()->resourcePool()->bestResource ( jid ).name () );
	}

	return jid.full ();

}

XMPP::Jid JabberBaseContact::bestAddress ()
{

	// see if we are subscribed with a preselected resource
	if ( !mRosterItem.jid().resource().isEmpty () )
	{
		// we have a preselected resource, so return our default full address
		return mRosterItem.jid ();
	}

	// construct address out of user@host and current best resource
	XMPP::Jid jid = mRosterItem.jid ();
	jid.setResource ( account()->resourcePool()->bestResource( mRosterItem.jid() ).name () );

	return jid;

}

void JabberBaseContact::setDontSync ( bool flag )
{

	mDontSync = flag;

}

bool JabberBaseContact::dontSync ()
{

	return mDontSync;

}

void JabberBaseContact::serialize (QMap < QString, QString > &serializedData, QMap < QString, QString > & /* addressBookData */ )
{

	// Contact id and display name are already set for us, only add the rest
	serializedData["JID"] = mRosterItem.jid().full();

	serializedData["groups"] = mRosterItem.groups ().join (QString::fromLatin1 (","));
}

#include "jabberbasecontact.moc"

// vim: set noet ts=4 sts=4 sw=4:
<|endoftext|>


<commit_before>// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include <stout/os.hpp>
#include <stout/strings.hpp>

#include <mesos/mesos.hpp>

#include "common/command_utils.hpp"

#include "slave/containerizer/mesos/provisioner/appc/fetcher.hpp"
#include "slave/containerizer/mesos/provisioner/appc/paths.hpp"

#include "uri/schemes/file.hpp"
#include "uri/schemes/http.hpp"

namespace http = process::http;

using std::string;

using process::Failure;
using process::Future;
using process::Owned;
using process::Shared;

namespace mesos {
namespace internal {
namespace slave {
namespace appc {

static const char EXT[] = "aci";
static const char LABEL_VERSION[] = "version";
static const char LABEL_OS[] = "os";
static const char LABEL_ARCH[] = "arch";

static Try<string> getSimpleDiscoveryImagePath(const Image::Appc& appc)
{
  CHECK(!appc.name().empty());

  hashmap<string, string> labels;
  foreach (const mesos::Label& label, appc.labels().labels()) {
    labels[label.key()] = label.value();
  }

  if (!labels.contains(LABEL_VERSION)) {
    labels.insert({LABEL_VERSION, "latest"});
  }

  if (!labels.contains(LABEL_OS)) {
    return Error(
        "Failed to form simple discovery url: label '" +
        string(LABEL_OS) + "' is missing");
  }

  if (!labels.contains(LABEL_ARCH)) {
    return Error(
        "Failed to form simple discovery url: label '" +
        string(LABEL_ARCH) + "' is missing");
  }

  return strings::format(
      "%s-%s-%s-%s.%s",
      appc.name(),              // Image name.
      labels.at(LABEL_VERSION), // Version label.
      labels.at(LABEL_OS),      // OS label.
      labels.at(LABEL_ARCH),    // ARCH label.
      EXT);                     // Extension.
}

static Try<URI> getUri(const string& prefix, const string& path)
{
  const string rawUrl = prefix + path;

  // TODO(jojy): Add parse URI function in URI namespace.

  // TODO(jojy): Add `Path::seperator` which abstracts the file
  // separator character.
  if (strings::startsWith(rawUrl, "/")) {
    return uri::file(rawUrl);
  }

  Try<http::URL> _url = http::URL::parse(rawUrl);
  if (_url.isError()) {
    return Error(
        "Failed to parse '" + rawUrl + "' as a valid URL: " + _url.error());
  }

  const http::URL& url = _url.get();

  if (url.domain.isNone() && url.ip.isNone()) {
    return Error(
        "Failed to parse host name from image url '" + rawUrl + "'");
  }

  // Get image server port.
  if (url.port.isNone()) {
    return Error(
        "Failed to parse port for image url '" + rawUrl + "'");
  }

  // Get image server host.
  const string host = url.domain.isSome()
    ? url.domain.get()
    : stringify(url.ip.get());

  int port = static_cast<int>(url.port.get());

  if (url.scheme.get() == "http") {
    return uri::http(host, url.path, port);
  }

  if (url.scheme.get() == "https") {
    return uri::https(host, url.path, port);
  }

  // TODO(jojy): Add support for hdfs.

  return Error("Unsupported scheme '" + url.scheme.get() + "'");
}

Try<Owned<Fetcher>> Fetcher::create(
    const Flags& flags,
    const Shared<uri::Fetcher>& fetcher)
{
  const string prefix = flags.appc_simple_discovery_uri_prefix;

  // TODO(jojy): Add support for hdfs.
  if (!strings::startsWith(prefix, "http") &&
      !strings::startsWith(prefix, "https") &&
      !strings::startsWith(prefix, "/")) {
    return Error("Invalid simple discovery uri prefix: " + prefix);
  }

  return new Fetcher(prefix, fetcher);
}

Fetcher::Fetcher(
    const string& _uriPrefix,
    const Shared<uri::Fetcher>& _fetcher)
  : uriPrefix(_uriPrefix),
    fetcher(_fetcher) {}

Future<Nothing> Fetcher::fetch(const Image::Appc& appc, const Path& directory)
{
  // TODO(jojy): Add more discovery implementations. We use simple
  // discovery now but in the future, this will be extended to use
  // multiple discoveries and try sequentially based on priority.

  if (appc.name().empty()) {
    return Failure("Image name cannot be empty");
  }

  Try<string> path = getSimpleDiscoveryImagePath(appc);
  if (path.isError()) {
    return Failure(
        "Failed to get discovery path for image '" +
        appc.name() + "': " + path.error());
  }

  // First construct a URI based on the scheme.
  Try<URI> uri = getUri(uriPrefix, path.get());
  if (uri.isError()) {
    return Failure(
        "Failed to get URI for image discovery path '" +
        path.get() + "': " + uri.error());
  }

  VLOG(1) << "Fetching image from URI '" << uri.get() << "'";

  // NOTE: URI fetcher will fetch the image into 'directory' with file
  // name as URI's basename.
  const Path aciBundle(path::join(
      directory,
      Path(uri->path()).basename()));

  return fetcher->fetch(uri.get(), directory)
    .then([=]() -> Future<Nothing> {
      // Change the extension to ".gz" as gzip utility expects it.
      const Path _aciBundle(aciBundle.string() + ".gz");

      Try<Nothing> rename = os::rename(aciBundle, _aciBundle);
      if (rename.isError()) {
        return Failure(
            "Failed to change extension to 'gz' for bundle '" +
            stringify(aciBundle) + "': " + rename.error());
      }

      return command::decompress(_aciBundle);
    })
    .then([=]() -> Future<string> {
      return command::sha512(aciBundle);
    })
    .then([=](const string& shasum) -> Future<Nothing> {
      const string imagePath(path::join(directory, "sha512-" + shasum));

      Try<Nothing> mkdir = os::mkdir(imagePath);
      if (mkdir.isError()) {
        return Failure(
            "Failed to create directory for untarring image '" +
            appc.name() + "': " + mkdir.error());
      }

      return command::untar(aciBundle, imagePath);
    })
    .then([=]() -> Future<Nothing> {
      // Remove the bundle file if everything goes well.
      Try<Nothing> remove = os::rm(aciBundle);
      if (remove.isError()) {
        return Failure(
            "Failed to remove aci bundle file '" + stringify(aciBundle) +
            "': " + remove.error());
      }

      return Nothing();
    });
}

} // namespace appc {
} // namespace slave {
} // namespace internal {
} // namespace mesos {
<commit_msg>Fixed garbled license header.<commit_after>// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include <stout/os.hpp>
#include <stout/strings.hpp>

#include <mesos/mesos.hpp>

#include "common/command_utils.hpp"

#include "slave/containerizer/mesos/provisioner/appc/fetcher.hpp"
#include "slave/containerizer/mesos/provisioner/appc/paths.hpp"

#include "uri/schemes/file.hpp"
#include "uri/schemes/http.hpp"

namespace http = process::http;

using std::string;

using process::Failure;
using process::Future;
using process::Owned;
using process::Shared;

namespace mesos {
namespace internal {
namespace slave {
namespace appc {

static const char EXT[] = "aci";
static const char LABEL_VERSION[] = "version";
static const char LABEL_OS[] = "os";
static const char LABEL_ARCH[] = "arch";

static Try<string> getSimpleDiscoveryImagePath(const Image::Appc& appc)
{
  CHECK(!appc.name().empty());

  hashmap<string, string> labels;
  foreach (const mesos::Label& label, appc.labels().labels()) {
    labels[label.key()] = label.value();
  }

  if (!labels.contains(LABEL_VERSION)) {
    labels.insert({LABEL_VERSION, "latest"});
  }

  if (!labels.contains(LABEL_OS)) {
    return Error(
        "Failed to form simple discovery url: label '" +
        string(LABEL_OS) + "' is missing");
  }

  if (!labels.contains(LABEL_ARCH)) {
    return Error(
        "Failed to form simple discovery url: label '" +
        string(LABEL_ARCH) + "' is missing");
  }

  return strings::format(
      "%s-%s-%s-%s.%s",
      appc.name(),              // Image name.
      labels.at(LABEL_VERSION), // Version label.
      labels.at(LABEL_OS),      // OS label.
      labels.at(LABEL_ARCH),    // ARCH label.
      EXT);                     // Extension.
}

static Try<URI> getUri(const string& prefix, const string& path)
{
  const string rawUrl = prefix + path;

  // TODO(jojy): Add parse URI function in URI namespace.

  // TODO(jojy): Add `Path::seperator` which abstracts the file
  // separator character.
  if (strings::startsWith(rawUrl, "/")) {
    return uri::file(rawUrl);
  }

  Try<http::URL> _url = http::URL::parse(rawUrl);
  if (_url.isError()) {
    return Error(
        "Failed to parse '" + rawUrl + "' as a valid URL: " + _url.error());
  }

  const http::URL& url = _url.get();

  if (url.domain.isNone() && url.ip.isNone()) {
    return Error(
        "Failed to parse host name from image url '" + rawUrl + "'");
  }

  // Get image server port.
  if (url.port.isNone()) {
    return Error(
        "Failed to parse port for image url '" + rawUrl + "'");
  }

  // Get image server host.
  const string host = url.domain.isSome()
    ? url.domain.get()
    : stringify(url.ip.get());

  int port = static_cast<int>(url.port.get());

  if (url.scheme.get() == "http") {
    return uri::http(host, url.path, port);
  }

  if (url.scheme.get() == "https") {
    return uri::https(host, url.path, port);
  }

  // TODO(jojy): Add support for hdfs.

  return Error("Unsupported scheme '" + url.scheme.get() + "'");
}

Try<Owned<Fetcher>> Fetcher::create(
    const Flags& flags,
    const Shared<uri::Fetcher>& fetcher)
{
  const string prefix = flags.appc_simple_discovery_uri_prefix;

  // TODO(jojy): Add support for hdfs.
  if (!strings::startsWith(prefix, "http") &&
      !strings::startsWith(prefix, "https") &&
      !strings::startsWith(prefix, "/")) {
    return Error("Invalid simple discovery uri prefix: " + prefix);
  }

  return new Fetcher(prefix, fetcher);
}

Fetcher::Fetcher(
    const string& _uriPrefix,
    const Shared<uri::Fetcher>& _fetcher)
  : uriPrefix(_uriPrefix),
    fetcher(_fetcher) {}

Future<Nothing> Fetcher::fetch(const Image::Appc& appc, const Path& directory)
{
  // TODO(jojy): Add more discovery implementations. We use simple
  // discovery now but in the future, this will be extended to use
  // multiple discoveries and try sequentially based on priority.

  if (appc.name().empty()) {
    return Failure("Image name cannot be empty");
  }

  Try<string> path = getSimpleDiscoveryImagePath(appc);
  if (path.isError()) {
    return Failure(
        "Failed to get discovery path for image '" +
        appc.name() + "': " + path.error());
  }

  // First construct a URI based on the scheme.
  Try<URI> uri = getUri(uriPrefix, path.get());
  if (uri.isError()) {
    return Failure(
        "Failed to get URI for image discovery path '" +
        path.get() + "': " + uri.error());
  }

  VLOG(1) << "Fetching image from URI '" << uri.get() << "'";

  // NOTE: URI fetcher will fetch the image into 'directory' with file
  // name as URI's basename.
  const Path aciBundle(path::join(
      directory,
      Path(uri->path()).basename()));

  return fetcher->fetch(uri.get(), directory)
    .then([=]() -> Future<Nothing> {
      // Change the extension to ".gz" as gzip utility expects it.
      const Path _aciBundle(aciBundle.string() + ".gz");

      Try<Nothing> rename = os::rename(aciBundle, _aciBundle);
      if (rename.isError()) {
        return Failure(
            "Failed to change extension to 'gz' for bundle '" +
            stringify(aciBundle) + "': " + rename.error());
      }

      return command::decompress(_aciBundle);
    })
    .then([=]() -> Future<string> {
      return command::sha512(aciBundle);
    })
    .then([=](const string& shasum) -> Future<Nothing> {
      const string imagePath(path::join(directory, "sha512-" + shasum));

      Try<Nothing> mkdir = os::mkdir(imagePath);
      if (mkdir.isError()) {
        return Failure(
            "Failed to create directory for untarring image '" +
            appc.name() + "': " + mkdir.error());
      }

      return command::untar(aciBundle, imagePath);
    })
    .then([=]() -> Future<Nothing> {
      // Remove the bundle file if everything goes well.
      Try<Nothing> remove = os::rm(aciBundle);
      if (remove.isError()) {
        return Failure(
            "Failed to remove aci bundle file '" + stringify(aciBundle) +
            "': " + remove.error());
      }

      return Nothing();
    });
}

} // namespace appc {
} // namespace slave {
} // namespace internal {
} // namespace mesos {
<|endoftext|>


<commit_before>// Copyright (C) 2010 - 2014 by Pedro Mendes, Virginia Tech Intellectual
// Properties, Inc., University of Heidelberg, and The University
// of Manchester.
// All rights reserved.

// Copyright (C) 2008 - 2009 by Pedro Mendes, Virginia Tech Intellectual
// Properties, Inc., EML Research, gGmbH, University of Heidelberg,
// and The University of Manchester.
// All rights reserved.

// Copyright (C) 2004 - 2007 by Pedro Mendes, Virginia Tech Intellectual
// Properties, Inc. and EML Research, gGmbH.
// All rights reserved.

#include <string>
#include <iostream>
#include "sbml/util/List.h"
#include "sbml/common/libsbml-config-common.h"

#include "ConverterASTNode.h"

#ifndef LIBSBML_USE_LEGACY_MATH

/**
 * Standard constructor
 */
ConverterASTNode::ConverterASTNode(): ASTNode()
{};

/**
 * Extension of the ASTNode class from libsbml. This class extends the original
 * ASTNode by some functions to manipulate the children of a node.
 */

/**
 * Sets the list of children to the one given as argument. The list is not
 * copied.
 */
void ConverterASTNode::setChildren(List* children)
{
  if (mNumber != NULL)
    {
      return; // cannot add children to a number type astnode
    }
  else if (mFunction != NULL)
    {
      unsigned int size = mFunction->getNumChildren();

      for (unsigned int i = mFunction->getNumChildren(); i > 0; i--)
        {
          mFunction->removeChild(i - 1);
        }

      for (unsigned int i = 0; i < children->getSize(); i++)
        {
          mFunction->addChild(static_cast<ASTNode*>(children->get(i)));
        }
    }
};

/**
 * Constructor that makes a ConverterASTNode from an ASTNode.
 */
ConverterASTNode::ConverterASTNode(const ASTNode &templ): ASTNode(templ.getType())
{
  if (this->getType() == AST_RATIONAL)
    {
      setValue(templ.getNumerator(), templ.getDenominator());
    }
  else if (this->getType() == AST_REAL)
    {
      setValue(templ.getReal());
    }
  else if (this->getType() == AST_REAL_E)
    {
      setValue(templ.getMantissa(), templ.getExponent());
    }

  if (this->getType() == AST_PLUS ||
      this->getType() == AST_MINUS ||
      this->getType() == AST_TIMES ||
      this->getType() == AST_DIVIDE ||
      this->getType() == AST_POWER)
    {
      this->mChar = templ.getCharacter();
    }
  else if (this->getType() == AST_INTEGER)
    {
      setValue(templ.getInteger());
    }

  if ((!this->isOperator()) && (!this->isNumber()) && !isConstant())
    {
      this->setName(templ.getName());
    }

  unsigned int counter;

  for (counter = 0; counter < templ.getNumChildren(); counter++)
    {
      this->addChild(new ConverterASTNode(*templ.getChild(counter)));
    }
};

/**
 * This function returns true if the node represents a user defined function.
 */
/*
bool ConverterASTNode::isUserDefinedFunction(){
    return (this->getType()==AST_FUNCTION);
};
 */

#ifdef COPASI_DEBUG
/**
 * Function to print an ASTNode and its children as a simple tree.
 * The arguments are the node to be printed and the number of spaces that this
 * node will be indented.
 */
void ConverterASTNode::printASTNode(const ASTNode* node, unsigned int indent)
{
  std::string spacer = "";
  unsigned int counter;

  for (counter = 0; counter < indent; counter++)
    {
      spacer += " ";
    }

  std::cout << spacer;

  if (node->isFunction() || node->isName())
    {
      if (node->getName() == NULL)
        std::cout << "emptyName";
      else
        std::cout << node->getName();
    }
  else if (node->isOperator())
    {
      std::cout << node->getCharacter();
    }
  else if (node->isInteger())
    {
      std::cout << node->getInteger();
    }
  else if (node->isReal())
    {
      std::cout << node->getReal();
    }
  else
    {
      std::cout << "[" << node->getType() << "]";
    }

  std::cout << std::endl;

  for (counter = 0; counter < node->getNumChildren(); counter++)
    {
      ConverterASTNode::printASTNode(node->getChild(counter), indent + 2);
    }
};
#endif // COPASI_DEBUG

ASTNode* ConverterASTNode::removeChild(unsigned int index)
{
  ASTNode * removedChild = getChild(index);

  this->ASTNode::removeChild(index);

  return removedChild;
};

/**
 * Constructor that makes a ConverterASTNode from an ASTNode.
 */
ASTNode* ConverterASTNode::shallowCopy(const ASTNode* pOrig)
{
  ConverterASTNode* pTmp = new ConverterASTNode(*pOrig);

  while (pTmp->getNumChildren() > 0)
    {
      delete pTmp->removeChild(0);
    }

  ASTNode* pCopy = pTmp->deepCopy();
  delete pTmp;
  return pCopy;
};

#else

/**
 * Standard constructor
 */
ConverterASTNode::ConverterASTNode(): ASTNode()
{};

/**
 * Extension of the ASTNode class from libsbml. This class extends the original
 * ASTNode by some functions to manipulate the children of a node.
 */

/**
 * Sets the list of children to the one given as argument. The list is not
 * copied.
 */
void ConverterASTNode::setChildren(List* children)
{
  this->mChildren = children;
};

/**
 * Constructor that makes a ConverterASTNode from an ASTNode.
 */
ConverterASTNode::ConverterASTNode(const ASTNode &templ): ASTNode(templ.getType())
{
  if (this->getType() == AST_RATIONAL)
    {
      this->mDenominator = templ.getDenominator();
      this->mInteger = templ.getNumerator();
    }
  else if (this->getType() == AST_REAL || this->getType() == AST_REAL_E)
    {
      this->mExponent = templ.getExponent();
      this->mReal = templ.getMantissa();
    }

  if (this->getType() == AST_PLUS || this->getType() == AST_MINUS || this->getType() == AST_TIMES || this->getType() == AST_DIVIDE || this->getType() == AST_POWER)
    {
      this->mChar = templ.getCharacter();
    }
  else if (this->getType() == AST_INTEGER)
    {
      this->mInteger = templ.getInteger();
    }

  if ((!this->isOperator()) && (!this->isNumber()))
    {
      this->setName(templ.getName());
    }

  unsigned int counter;

  for (counter = 0; counter < templ.getNumChildren(); counter++)
    {
      this->addChild(new ConverterASTNode(*templ.getChild(counter)));
    }
};

/**
 * This function returns true if the node represents a user defined function.
 */
/*
bool ConverterASTNode::isUserDefinedFunction(){
    return (this->getType()==AST_FUNCTION);
};
 */

#ifdef COPASI_DEBUG
/**
 * Function to print an ASTNode and its children as a simple tree.
 * The arguments are the node to be printed and the number of spaces that this
 * node will be indented.
 */
void ConverterASTNode::printASTNode(const ASTNode* node, unsigned int indent)
{
  std::string spacer = "";
  unsigned int counter;

  for (counter = 0; counter < indent; counter++)
    {
      spacer += " ";
    }

  std::cout << spacer;

  if (node->isFunction() || node->isName())
    {
      std::cout << node->getName();
    }
  else if (node->isOperator())
    {
      std::cout << node->getCharacter();
    }
  else if (node->isInteger())
    {
      std::cout << node->getInteger();
    }
  else if (node->isReal())
    {
      std::cout << node->getReal();
    }
  else
    {
      std::cout << "[" << node->getType() << "]";
    }

  std::cout << std::endl;

  for (counter = 0; counter < node->getNumChildren(); counter++)
    {
      ConverterASTNode::printASTNode(node->getChild(counter), indent + 2);
    }
};
#endif // COPASI_DEBUG

ASTNode* ConverterASTNode::removeChild(unsigned int index)
{
  return static_cast<ASTNode*>(this->mChildren->remove(index));
};

/**
 * Constructor that makes a ConverterASTNode from an ASTNode.
 */
ASTNode* ConverterASTNode::shallowCopy(const ASTNode* pOrig)
{
  ConverterASTNode* pTmp = new ConverterASTNode(*pOrig);

  while (pTmp->getNumChildren() > 0)
    {
      delete pTmp->removeChild(0);
    }

  ASTNode* pCopy = pTmp->deepCopy();
  delete pTmp;
  return pCopy;
};

#endif // LIBSBML_USE_LEGACY_MATH
<commit_msg>- fix bug 2159: in the case that copasi encounters an n-ary relational oparator, this one will be replaced by an AND of the binary relational operator.<commit_after>// Copyright (C) 2010 - 2015 by Pedro Mendes, Virginia Tech Intellectual
// Properties, Inc., University of Heidelberg, and The University
// of Manchester.
// All rights reserved.

// Copyright (C) 2008 - 2009 by Pedro Mendes, Virginia Tech Intellectual
// Properties, Inc., EML Research, gGmbH, University of Heidelberg,
// and The University of Manchester.
// All rights reserved.

// Copyright (C) 2004 - 2007 by Pedro Mendes, Virginia Tech Intellectual
// Properties, Inc. and EML Research, gGmbH.
// All rights reserved.

#include <string>
#include <iostream>
#include "sbml/util/List.h"
#include "sbml/common/libsbml-config-common.h"

#include "ConverterASTNode.h"

#ifndef LIBSBML_USE_LEGACY_MATH

/**
 * Standard constructor
 */
ConverterASTNode::ConverterASTNode(): ASTNode()
{};

/**
 * Extension of the ASTNode class from libsbml. This class extends the original
 * ASTNode by some functions to manipulate the children of a node.
 */

/**
 * Sets the list of children to the one given as argument. The list is not
 * copied.
 */
void ConverterASTNode::setChildren(List* children)
{
  if (mNumber != NULL)
    {
      return; // cannot add children to a number type astnode
    }
  else if (mFunction != NULL)
    {
      unsigned int size = mFunction->getNumChildren();

      for (unsigned int i = mFunction->getNumChildren(); i > 0; i--)
        {
          mFunction->removeChild(i - 1);
        }

      for (unsigned int i = 0; i < children->getSize(); i++)
        {
          mFunction->addChild(static_cast<ASTNode*>(children->get(i)));
        }
    }
};

/**
 * Constructor that makes a ConverterASTNode from an ASTNode.
 */
ConverterASTNode::ConverterASTNode(const ASTNode &templ): ASTNode(templ.getType())
{
  if (this->getType() == AST_RATIONAL)
    {
      setValue(templ.getNumerator(), templ.getDenominator());
    }
  else if (this->getType() == AST_REAL)
    {
      setValue(templ.getReal());
    }
  else if (this->getType() == AST_REAL_E)
    {
      setValue(templ.getMantissa(), templ.getExponent());
    }

  if (this->getType() == AST_PLUS ||
      this->getType() == AST_MINUS ||
      this->getType() == AST_TIMES ||
      this->getType() == AST_DIVIDE ||
      this->getType() == AST_POWER)
    {
      this->mChar = templ.getCharacter();
    }
  else if (this->getType() == AST_INTEGER)
    {
      setValue(templ.getInteger());
    }

  if ((!this->isOperator()) && (!this->isNumber()) && !isConstant())
    {
      this->setName(templ.getName());
    }

  unsigned int counter;

  for (counter = 0; counter < templ.getNumChildren(); counter++)
    {
      this->addChild(new ConverterASTNode(*templ.getChild(counter)));
    }
};

/**
 * This function returns true if the node represents a user defined function.
 */
/*
bool ConverterASTNode::isUserDefinedFunction(){
    return (this->getType()==AST_FUNCTION);
};
 */

#ifdef COPASI_DEBUG
/**
 * Function to print an ASTNode and its children as a simple tree.
 * The arguments are the node to be printed and the number of spaces that this
 * node will be indented.
 */
void ConverterASTNode::printASTNode(const ASTNode* node, unsigned int indent)
{
  std::string spacer = "";
  unsigned int counter;

  for (counter = 0; counter < indent; counter++)
    {
      spacer += " ";
    }

  std::cout << spacer;

  if (node->isFunction() || node->isName())
    {
      if (node->getName() == NULL)
        std::cout << "emptyName";
      else
        std::cout << node->getName();
    }
  else if (node->isOperator())
    {
      std::cout << node->getCharacter();
    }
  else if (node->isInteger())
    {
      std::cout << node->getInteger();
    }
  else if (node->isReal())
    {
      std::cout << node->getReal();
    }
  else
    {
      std::cout << "[" << node->getType() << "]";
    }

  std::cout << std::endl;

  for (counter = 0; counter < node->getNumChildren(); counter++)
    {
      ConverterASTNode::printASTNode(node->getChild(counter), indent + 2);
    }
};
#endif // COPASI_DEBUG

ASTNode* ConverterASTNode::removeChild(unsigned int index)
{
  ASTNode * removedChild = getChild(index);

  this->ASTNode::removeChild(index);

  return removedChild;
};

/**
 * Constructor that makes a ConverterASTNode from an ASTNode.
 */
ASTNode* ConverterASTNode::shallowCopy(const ASTNode* pOrig)
{
  ConverterASTNode* pTmp = new ConverterASTNode(*pOrig);

  while (pTmp->getNumChildren() > 0)
    {
      delete pTmp->removeChild(0);
    }

  ASTNode* pCopy = pTmp->deepCopy();
  delete pTmp;
  return pCopy;
};

#else

/**
 * Standard constructor
 */
ConverterASTNode::ConverterASTNode(): ASTNode()
{};

/**
 * Extension of the ASTNode class from libsbml. This class extends the original
 * ASTNode by some functions to manipulate the children of a node.
 */

/**
 * Sets the list of children to the one given as argument. The list is not
 * copied.
 */
void ConverterASTNode::setChildren(List* children)
{
  this->mChildren = children;
};

void fixNaryRelational(ASTNode& node)
{
  if (!node.isRelational()) return;

  if (node.getNumChildren() == 2) return;

  ASTNodeType_t type = node.getType();
  std::vector<ASTNode*> nodes;
  node.setType(AST_LOGICAL_AND);

  for (int i = 1; i < node.getNumChildren(); ++i)
    {
      ASTNode *current = new ASTNode(type);
      current->addChild(node.getChild(i - 1)->deepCopy());
      current->addChild(node.getChild(i)->deepCopy());
      nodes.push_back(current);
    }

  while (node.getNumChildren() > 0)
    node.removeChild(0);

  std::vector<ASTNode*>::iterator it = nodes.begin();

  while (it != nodes.end())
    {
      node.addChild(*it);
      ++it;
    }
}

/**
 * Constructor that makes a ConverterASTNode from an ASTNode.
 */
ConverterASTNode::ConverterASTNode(const ASTNode &templ): ASTNode(templ.getType())
{
  if (this->getType() == AST_RATIONAL)
    {
      this->mDenominator = templ.getDenominator();
      this->mInteger = templ.getNumerator();
    }
  else if (this->getType() == AST_REAL || this->getType() == AST_REAL_E)
    {
      this->mExponent = templ.getExponent();
      this->mReal = templ.getMantissa();
    }

  if (templ.getNumChildren() > 2 && (
        this->getType() == AST_RELATIONAL_EQ ||
        this->getType() == AST_RELATIONAL_NEQ ||
        this->getType() == AST_RELATIONAL_GEQ ||
        this->getType() == AST_RELATIONAL_GT ||
        this->getType() == AST_RELATIONAL_LEQ ||
        this->getType() == AST_RELATIONAL_LT))
    {
      // reduce to binary
      //const_cast<ASTNode&>(templ).reduceToBinary();
      fixNaryRelational(const_cast<ASTNode&>(templ));
      ASTNode::setType(AST_LOGICAL_AND);
    }

  if (this->getType() == AST_PLUS || this->getType() == AST_MINUS || this->getType() == AST_TIMES || this->getType() == AST_DIVIDE || this->getType() == AST_POWER)
    {
      this->mChar = templ.getCharacter();
    }
  else if (this->getType() == AST_INTEGER)
    {
      this->mInteger = templ.getInteger();
    }

  if ((!this->isOperator()) && (!this->isNumber()))
    {
      this->setName(templ.getName());
    }

  unsigned int counter;

  for (counter = 0; counter < templ.getNumChildren(); counter++)
    {
      this->addChild(new ConverterASTNode(*templ.getChild(counter)));
    }
};

/**
 * This function returns true if the node represents a user defined function.
 */
/*
bool ConverterASTNode::isUserDefinedFunction(){
    return (this->getType()==AST_FUNCTION);
};
 */

#ifdef COPASI_DEBUG
/**
 * Function to print an ASTNode and its children as a simple tree.
 * The arguments are the node to be printed and the number of spaces that this
 * node will be indented.
 */
void ConverterASTNode::printASTNode(const ASTNode* node, unsigned int indent)
{
  std::string spacer = "";
  unsigned int counter;

  for (counter = 0; counter < indent; counter++)
    {
      spacer += " ";
    }

  std::cout << spacer;

  if (node->isFunction() || node->isName())
    {
      std::cout << node->getName();
    }
  else if (node->isOperator())
    {
      std::cout << node->getCharacter();
    }
  else if (node->isInteger())
    {
      std::cout << node->getInteger();
    }
  else if (node->isReal())
    {
      std::cout << node->getReal();
    }
  else
    {
      std::cout << "[" << node->getType() << "]";
    }

  std::cout << std::endl;

  for (counter = 0; counter < node->getNumChildren(); counter++)
    {
      ConverterASTNode::printASTNode(node->getChild(counter), indent + 2);
    }
};
#endif // COPASI_DEBUG

ASTNode* ConverterASTNode::removeChild(unsigned int index)
{
  return static_cast<ASTNode*>(this->mChildren->remove(index));
};

/**
 * Constructor that makes a ConverterASTNode from an ASTNode.
 */
ASTNode* ConverterASTNode::shallowCopy(const ASTNode* pOrig)
{
  ConverterASTNode* pTmp = new ConverterASTNode(*pOrig);

  while (pTmp->getNumChildren() > 0)
    {
      delete pTmp->removeChild(0);
    }

  ASTNode* pCopy = pTmp->deepCopy();
  delete pTmp;
  return pCopy;
};

#endif // LIBSBML_USE_LEGACY_MATH
<|endoftext|>