548 lines
14 KiB
C
548 lines
14 KiB
C
/*
|
|
* CDE - Common Desktop Environment
|
|
*
|
|
* Copyright (c) 1993-2012, The Open Group. All rights reserved.
|
|
*
|
|
* These libraries and programs are free software; you can
|
|
* redistribute them and/or modify them under the terms of the GNU
|
|
* Lesser General Public License as published by the Free Software
|
|
* Foundation; either version 2 of the License, or (at your option)
|
|
* any later version.
|
|
*
|
|
* These libraries and programs are distributed in the hope that
|
|
* they 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 these libraries and programs; if not, write
|
|
* to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
|
|
* Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
//%% (c) Copyright 1993, 1994 Hewlett-Packard Company
|
|
//%% (c) Copyright 1993, 1994 International Business Machines Corp.
|
|
//%% (c) Copyright 1993, 1994 Sun Microsystems, Inc.
|
|
//%% (c) Copyright 1993, 1994 Novell, Inc.
|
|
//%% $XConsortium: mp_s_pattern.C /main/3 1995/10/23 11:57:37 rswiston $
|
|
/*
|
|
* @(#)mp_s_pattern.C 1.27 93/09/07
|
|
*
|
|
* mp_s_pattern.cc
|
|
*
|
|
* Copyright (c) 1990, 1992 by Sun Microsystems, Inc.
|
|
*/
|
|
#include "mp_s_global.h"
|
|
#include "mp/mp_arg.h"
|
|
#include "mp_s_pat_context.h"
|
|
#include "mp/mp_mp.h"
|
|
#include "mp_s_mp.h"
|
|
#include "mp_s_pattern.h"
|
|
#include "mp_s_message.h"
|
|
#include "mp_signature.h"
|
|
#include "mp_observer.h"
|
|
#include "mp_ptype.h"
|
|
#include "mp_rpc_implement.h"
|
|
#include "mp_s_session.h"
|
|
#include "mp/mp_xdr_functions.h"
|
|
#include "util/tt_enumname.h"
|
|
#include "mp/mp_trace.h"
|
|
#include "util/tt_global_env.h"
|
|
#include "util/tt_assert.h"
|
|
|
|
|
|
//
|
|
// Creates a pattern from a signature object by extracting the relevant
|
|
// fields from the signature.
|
|
//
|
|
_Tt_s_pattern::
|
|
_Tt_s_pattern(const _Tt_signature_ptr &sig)
|
|
{
|
|
_timestamp = 0;
|
|
set_id(_tt_s_mp->initial_session->address_string());
|
|
set_category(sig->category());
|
|
add_scope(sig->scope());
|
|
add_op(sig->op());
|
|
if (sig->opnum() != -1) {
|
|
add_opnum(sig->opnum());
|
|
}
|
|
if (sig->category()!=TT_OBSERVE) {
|
|
add_handler_ptype(sig->ptid());
|
|
}
|
|
if (sig->otid().len() > 0) {
|
|
add_otype(sig->otid());
|
|
add_paradigm(TT_OTYPE);
|
|
add_paradigm(TT_OBJECT);
|
|
} else {
|
|
add_paradigm(TT_PROCEDURE);
|
|
}
|
|
add_reliability(sig->reliability());
|
|
add_message_class(sig->message_class());
|
|
|
|
// We share the arg list from the signature
|
|
// by just assigning the pointer and letting the ref-counting
|
|
// system do its thing. Conceivably, this might cause
|
|
// consternation if we ever let clients update patterns created
|
|
// from signatures, since updating the arg list would change
|
|
// the in-memory copy of the signature and every other pattern
|
|
// generated from the signature! However, the most likely
|
|
// way such updates would be generated would be to send a whole
|
|
// new pattern over from the client, so sharing the arg list
|
|
// wouldn\'t cause trouble.
|
|
|
|
_args = sig->args();
|
|
|
|
// We *cannot* share the context list from the signature as
|
|
// the signature contains plain _Tt_context entries, while
|
|
// the pattern should contain _Tt_pat_context entries.
|
|
|
|
_Tt_context_list_cursor c(sig->contexts());
|
|
|
|
while (c.next()) {
|
|
_Tt_pat_context_ptr nc = new _Tt_pat_context(**c);
|
|
add_context(nc);
|
|
}
|
|
}
|
|
|
|
|
|
_Tt_s_pattern::_Tt_s_pattern ()
|
|
{
|
|
_timestamp = 0;
|
|
}
|
|
|
|
_Tt_s_pattern::~_Tt_s_pattern ()
|
|
{
|
|
}
|
|
|
|
|
|
//
|
|
// Matches a pattern with a message and returns a number indicating how
|
|
// well the pattern matches the message. 0 indicates no match whereas a
|
|
// positive number indicates some level of matching. The greater the
|
|
// level, the "better" the pattern matches the message. A level of 1
|
|
// indicates all the pattern fields are wildcards so this is the minimum
|
|
// level of matching. Any level greater than 1 is controlled by the
|
|
// number returned from each field match. Thus twiddling these numbers
|
|
// gives some control as to what field matches are to be considered more
|
|
// important than others. Presently, the algorithm for selecting match
|
|
// numbers is that wildcard matches count as 0 whereas any specific match
|
|
// counts as 1. The total is added up for each field and returned as the
|
|
// value of the match. The variable tm below holds the total count of
|
|
// matches. It is initially 1 because wildcard matches don't increment
|
|
// it. In any submethod called to do matching the variable tm is passed
|
|
// to the method to be incremented.
|
|
//
|
|
int _Tt_s_pattern::
|
|
match(const _Tt_s_message &msg, const _Tt_msg_trace &trace) const
|
|
{
|
|
int tm = 1;
|
|
|
|
if (msg.is_handler_copy()) {
|
|
// message is the original
|
|
if ( (! _generating_ptype.is_null())
|
|
&& (category() == TT_OBSERVE))
|
|
{
|
|
//
|
|
// A static observer pattern can only match
|
|
// the message copy promised to it,
|
|
// and cannot match the original.
|
|
//
|
|
return 0;
|
|
}
|
|
} else {
|
|
// message is a copy promised to observer ptype
|
|
if ( (_generating_ptype.is_null())
|
|
|| ( msg.observer()->ptid()
|
|
!= _generating_ptype->ptid()))
|
|
{
|
|
//
|
|
// If this pattern is not owned by the
|
|
// promised ptype, or is owned by no
|
|
// ptype, then this pattern cannot
|
|
// fulfill the promise.
|
|
//
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
trace << "Tt_message & Tt_pattern {\n";
|
|
trace.incr_indent();
|
|
trace << "timestamp:\t" << _timestamp << "\n";
|
|
trace << *this;
|
|
trace.decr_indent();
|
|
trace << "} ";
|
|
|
|
// The code for this method is quite repetitive. For each
|
|
// field, the list is checked to see if there are any values. If
|
|
// there are and the appropiate match method returns 0 then the
|
|
// pattern fails to match. The only exception is match_scopes
|
|
// which defaults to not matching if there are no values specified.
|
|
|
|
|
|
// This function used to not match the op field of the
|
|
// message, because in the usual code path it is
|
|
// hashed on the basis of the op field so it would be
|
|
// redundant to check the op field here. However, when
|
|
// matching TT_HANDLER messages just to get callbacks run,
|
|
// this is not the case. To save a little time, we skip
|
|
// the op check if the message paradigm is not TT_HANDLER,
|
|
// even though that looks really funny here!
|
|
|
|
if (0 != _ops->count()) {
|
|
if (msg.paradigm() == TT_HANDLER &&
|
|
! match_field(msg.op(), _ops,
|
|
tm, trace, "op" ))
|
|
{
|
|
return(0);
|
|
}
|
|
tm++;
|
|
}
|
|
|
|
|
|
|
|
if (_reliabilities != 0) {
|
|
if (!(_reliabilities&(1<<msg.reliability()))) {
|
|
trace << "== 0; /* Tt_disposition */\n";
|
|
return(0);
|
|
}
|
|
tm++;
|
|
}
|
|
|
|
if (_states != 0) {
|
|
if (!(_states&(1<<msg.state()))) {
|
|
trace << "== 0; /* Tt_state */\n";
|
|
return(0);
|
|
}
|
|
tm++;
|
|
}
|
|
if (_classes != 0) {
|
|
if (!(_classes&(1<<TT_CLASS_UNDEFINED))
|
|
&& !(_classes&(1<< msg.message_class()))) {
|
|
trace << "== 0; /* Tt_class */\n";
|
|
return(0);
|
|
}
|
|
tm++;
|
|
}
|
|
if (_paradigms != 0) {
|
|
if (!(_paradigms&(1<<msg.paradigm()))) {
|
|
trace << "== 0; /* Tt_address */\n";
|
|
return(0);
|
|
}
|
|
tm++;
|
|
}
|
|
|
|
|
|
if (_handlers->count()) {
|
|
_Tt_string h;
|
|
|
|
if (! msg.handler().is_null()) {
|
|
h = msg.handler()->id();
|
|
}
|
|
if (! match_field(h, _handlers, tm, trace, "handler")) {
|
|
return(0);
|
|
}
|
|
}
|
|
|
|
if (_handler_ptypes->count() &&
|
|
! match_field(msg.handler_ptype(), _handler_ptypes,
|
|
tm, trace, "handler_ptype" ))
|
|
{
|
|
return(0);
|
|
}
|
|
|
|
|
|
if (! match_scopes(msg, tm, trace)) {
|
|
return(0);
|
|
}
|
|
if (msg.scope() == TT_SESSION) {
|
|
if (_files->count() &&
|
|
! match_field(msg.file(), _files,
|
|
tm, trace, "file"))
|
|
{
|
|
return(0);
|
|
}
|
|
}
|
|
if (_objects->count() &&
|
|
! match_field(msg.object(), _objects,
|
|
tm, trace, "object"))
|
|
{
|
|
return(0);
|
|
}
|
|
|
|
if (_otypes->count() &&
|
|
! match_field(msg.otype(), _otypes,
|
|
tm, trace, "otype"))
|
|
{
|
|
return(0);
|
|
}
|
|
if (_senders->count() &&
|
|
! match_field(msg.sender()->id(), _senders,
|
|
tm, trace, "sender"))
|
|
{
|
|
return(0);
|
|
}
|
|
|
|
if (_sender_ptypes->count() &&
|
|
! match_field(msg.sender_ptype(), _sender_ptypes,
|
|
tm, trace, "sender_ptype"))
|
|
{
|
|
return(0);
|
|
}
|
|
|
|
|
|
if (_args->count() && ! match_args(msg, tm, trace)) {
|
|
return(0);
|
|
}
|
|
|
|
if (! match_contexts(msg, tm, trace)) {
|
|
return(0);
|
|
}
|
|
|
|
trace << "== " << tm << ";\n";
|
|
return(tm);
|
|
}
|
|
|
|
|
|
//
|
|
// Generic matching function for lists of integers. If the given pattern
|
|
// values, pvals, is empty then 0 is returned indicating a mismatch.
|
|
// Otherwise, 1 is returned if "val" is in the list and the variable tm
|
|
// is incremented to update the matching score in _Tt_pattern::match.
|
|
//
|
|
int _Tt_s_pattern::
|
|
match_field(int val, const _Tt_int_rec_list_ptr &pvals, int &tm,
|
|
const _Tt_msg_trace &trace, const char *failure_note) const
|
|
{
|
|
_Tt_int_rec_list_cursor c(pvals);
|
|
|
|
while (c.next()) {
|
|
if (val == c->val) {
|
|
tm++;
|
|
return(1);
|
|
}
|
|
}
|
|
if (failure_note != 0) {
|
|
trace << "== 0; /* " << failure_note << " */\n";
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
|
|
//
|
|
// Generic matching function for lists of strings. If the given pattern
|
|
// values, pvals, is empty then 0 is returned indicating a mismatch.
|
|
// Otherwise, 1 is returned if "val" is in the list and the variable tm
|
|
// is incremented to update the matching score in _Tt_pattern::match.
|
|
//
|
|
int _Tt_s_pattern::
|
|
match_field(const _Tt_string &val, const _Tt_string_list_ptr &pvals,
|
|
int &tm, const _Tt_msg_trace &trace,
|
|
const char *failure_note) const
|
|
{
|
|
_Tt_string_list_cursor c(pvals);
|
|
|
|
if (val.len() != 0) {
|
|
while (c.next()) {
|
|
if (val == *c) {
|
|
tm++;
|
|
return(1);
|
|
}
|
|
}
|
|
}
|
|
if (failure_note != 0) {
|
|
trace << "== 0; /* " << failure_note << " */\n";
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
|
|
//
|
|
// Matches the scope of the message with the pattern scopes. For the
|
|
// different scopes, matching is defined differently as specified below.
|
|
//
|
|
int _Tt_s_pattern::
|
|
match_scopes(const _Tt_message &msg, int &tm,
|
|
const _Tt_msg_trace &trace) const
|
|
{
|
|
Tt_scope s = msg.scope();
|
|
int valid_scope_mask = 0;
|
|
int rval;
|
|
|
|
ASSERT(TT_SCOPE_NONE==0 && TT_SESSION==1 && TT_FILE==2 &&
|
|
TT_BOTH==3 && TT_FILE_IN_SESSION==4,
|
|
"Tt_scope enum values changed. This breaks the following "
|
|
"code, and also breaks binary compatibility for libtt users. "
|
|
"You probably don't want to do that, do you?")
|
|
|
|
static int valid_scope_masks[] = {
|
|
0, // TT_SCOPE_NONE
|
|
(1<<TT_SESSION) | (1<<TT_BOTH), // TT_SESSION
|
|
(1<<TT_FILE) | (1<<TT_BOTH), // TT_FILE
|
|
(1<<TT_SESSION) | (1<<TT_FILE) | (1<<TT_BOTH), // TT_BOTH
|
|
(1<<TT_FILE_IN_SESSION)}; // TT_FILE_IN_SESSION
|
|
|
|
if (! (_scopes & valid_scope_masks[s])) {
|
|
trace << "== 0; /* " << _tt_enumname(s) << " != Tt_scopes */\n";
|
|
return 0;
|
|
}
|
|
|
|
// if this is not a file-scoped message and the pattern
|
|
// doesn't contain a value for the _sessions list then we
|
|
// immediately fail the match.
|
|
if (s != TT_FILE &&
|
|
(msg.session().is_null() || _sessions->count() == 0)) {
|
|
trace << "== 0; /* pattern not joined to "
|
|
"tt_message_session() */\n";
|
|
return 0;
|
|
}
|
|
|
|
switch (s) {
|
|
case TT_FILE:
|
|
return match_field(msg.file(), _files,
|
|
tm, trace, "file" );
|
|
case TT_FILE_IN_SESSION:
|
|
if (msg.session()->has_id(_sessions)) {
|
|
return((_files->count() == 0)
|
|
|| match_field(msg.file(), _files,
|
|
tm, trace, "file"));
|
|
} else {
|
|
trace << "== 0; /* file in session */\n";
|
|
return(0);
|
|
}
|
|
case TT_SESSION:
|
|
|
|
// The session-scope case is so common that we avoid
|
|
// matching the session id by setting a flag on a
|
|
// pattern that we know is in the session (see
|
|
// _Tt_s_procid::add_pattern and
|
|
// _Tt_session::mod_session_id_in_patterns to see how
|
|
// the flag is set). This flag is turned on if this
|
|
// pattern is joined to the current session or not.
|
|
|
|
if (in_session()) {
|
|
return(1);
|
|
} else {
|
|
trace << "== 0; /* session */\n";
|
|
}
|
|
return(0);
|
|
|
|
case TT_BOTH:
|
|
rval = (msg.session()->has_id(_sessions) +
|
|
match_field(msg.file(), _files, tm, trace, 0));
|
|
if (rval == 0) {
|
|
trace << "== 0; /* file and session */\n";
|
|
}
|
|
return(rval);
|
|
|
|
case TT_SCOPE_NONE:
|
|
default:
|
|
trace << "== 0; /* Tt_scope */\n";
|
|
return(0);
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Returns 1 if the message's args match the pattern args. The
|
|
// _Tt_arg::is_match method is used to do the bulk of the work.
|
|
//
|
|
int _Tt_s_pattern::
|
|
match_args(const _Tt_message &msg, int &tm,
|
|
const _Tt_msg_trace &trace) const
|
|
{
|
|
if (msg.args()->count() == 0) {
|
|
// arg matching was specified but the
|
|
// message doesn't contain any args.
|
|
trace << "== 0; /* args */\n";
|
|
return(0);
|
|
}
|
|
|
|
_Tt_arg_list_cursor p_args(_args);
|
|
_Tt_arg_list_cursor m_args(msg.args());
|
|
int cumulative_args_score = 0;
|
|
|
|
while (p_args.next()) {
|
|
|
|
if (!m_args.next()) {
|
|
trace << "== 0; /* args */\n";
|
|
return(0);
|
|
}
|
|
|
|
int used_wildcard;
|
|
int score = p_args->match_score(*m_args, used_wildcard);
|
|
if (score <= 0) {
|
|
trace << "== 0; /* args */\n";
|
|
return(0);
|
|
}
|
|
cumulative_args_score += score;
|
|
}
|
|
tm += cumulative_args_score;
|
|
return(1);
|
|
}
|
|
|
|
//
|
|
// Returns 1 if the message's contexts match pattern's contexts.
|
|
//
|
|
int _Tt_s_pattern::
|
|
match_contexts(const _Tt_message &msg, int &tm,
|
|
const _Tt_msg_trace &trace) const
|
|
{
|
|
_Tt_pat_context_list_cursor cntxtC( _contexts );
|
|
while (cntxtC.next()) {
|
|
if (((_Tt_s_pat_context &)**cntxtC).matchVal( msg ) == 0) {
|
|
trace << "== 0; /* contexts */\n";
|
|
return 0;
|
|
}
|
|
}
|
|
tm++;
|
|
return 1;
|
|
}
|
|
|
|
|
|
|
|
// Routines to set and get the ptype that this pattern was generated
|
|
// from, and to test if it was so generate.
|
|
|
|
_Tt_ptype_ptr &_Tt_s_pattern::
|
|
generating_ptype(_Tt_ptype_ptr &pt)
|
|
{
|
|
_generating_ptype = pt;
|
|
return pt;
|
|
}
|
|
|
|
_Tt_ptype_ptr &_Tt_s_pattern::
|
|
generating_ptype()
|
|
{
|
|
return _generating_ptype;
|
|
}
|
|
|
|
int _Tt_s_pattern::
|
|
is_from_ptype()
|
|
{
|
|
return !_generating_ptype.is_null();
|
|
}
|
|
|
|
Tt_status _Tt_s_pattern::
|
|
join_context(const _Tt_msg_context &msgCntxt)
|
|
{
|
|
_Tt_pat_context_list_cursor contextC( _contexts );
|
|
while (contextC.next()) {
|
|
if (contextC->slotName() == msgCntxt.slotName()) {
|
|
return contextC->addValue( msgCntxt );
|
|
}
|
|
}
|
|
return TT_WRN_NOTFOUND;
|
|
}
|
|
|
|
Tt_status _Tt_s_pattern::
|
|
quit_context(const _Tt_msg_context &msgCntxt)
|
|
{
|
|
_Tt_pat_context_list_cursor contextC( _contexts );
|
|
while (contextC.next()) {
|
|
if (contextC->slotName() == msgCntxt.slotName()) {
|
|
return contextC->deleteValue( msgCntxt );
|
|
}
|
|
}
|
|
return TT_WRN_NOTFOUND;
|
|
}
|