diff options
Diffstat (limited to 'apps')
133 files changed, 20432 insertions, 0 deletions
diff --git a/apps/Gateway/Gateway/Channel.cpp b/apps/Gateway/Gateway/Channel.cpp new file mode 100644 index 00000000000..f07b5a81978 --- /dev/null +++ b/apps/Gateway/Gateway/Channel.cpp @@ -0,0 +1,713 @@ +#include "ace/Log_Msg.h" +// @(#)Channel.cpp 1.1 10/18/96 + +#include "Routing_Entry.h" +#include "Channel_Connector.h" + +// Convenient short-hands. +#define CO CONDITION +#define MU MUTEX + +// = The total number of bytes sent/received on this channel. +size_t +Channel::total_bytes (void) +{ + return this->total_bytes_; +} + +void +Channel::total_bytes (size_t bytes) +{ + this->total_bytes_ += bytes; +} + +Channel::Channel (ROUTING_TABLE *rt, + Channel_Connector *cc, + ACE_Thread_Manager *thr_mgr, + int socket_queue_size) + : id_ (-1), + total_bytes_ (0), + state_ (Channel::IDLE), + routing_table_ (rt), + connector_ (cc), + timeout_ (1), + max_timeout_ (Channel::MAX_RETRY_TIMEOUT), + socket_queue_size_ (socket_queue_size), + ACE_Svc_Handler<CHANNEL_PEER_STREAM, SYNCH> (thr_mgr) +{ +} + +// Set the associated channel. + +void +Channel::active (int a) +{ + this->state (a == 0 ? Channel::IDLE : Channel::ESTABLISHED); +} + +// Get the associated channel. + +int +Channel::active (void) +{ + return this->state () == Channel::ESTABLISHED; +} + +// Set the direction. + +void +Channel::direction (char d) +{ + this->direction_ = d; +} + +// Get the direction. + +char +Channel::direction (void) +{ + return this->direction_; +} + +// Sets the timeout delay. + +void +Channel::timeout (int to) +{ + if (to > this->max_timeout_) + to = this->max_timeout_; + + this->timeout_ = to; +} + +// Recalculate the current retry timeout delay using exponential +// backoff. Returns the original timeout (i.e., before the +// recalculation). + +int +Channel::timeout (void) +{ + int old_timeout = this->timeout_; + this->timeout_ *= 2; + + if (this->timeout_ > this->max_timeout_) + this->timeout_ = this->max_timeout_; + + return old_timeout; +} + +// Sets the max timeout delay. + +void +Channel::max_timeout (int mto) +{ + this->max_timeout_ = mto; +} + +// Gets the max timeout delay. + +int +Channel::max_timeout (void) +{ + return this->max_timeout_; +} + +// Restart connection asynchronously when timeout occurs. + +int +Channel::handle_timeout (const ACE_Time_Value &, const void *) +{ + ACE_DEBUG ((LM_DEBUG, + "(%t) attempting to reconnect Channel %d with timeout = %d\n", + this->id (), this->timeout_)); + return this->connector_->initiate_connection (this, ACE_Synch_Options::asynch); +} + +// Restart connection (blocking_semantics dicates whether we +// restart synchronously or asynchronously). + +int +Channel::reinitiate_connection (void) +{ + // Skip over deactivated descriptors. + if (this->get_handle () != -1) + { + // Make sure to close down peer to reclaim descriptor. + this->peer ().close (); + +#if 0 +// if (this->state () == FAILED) +// { + // Reinitiate timeout to improve reconnection time. +// this->timeout (1); +#endif + + ACE_DEBUG ((LM_DEBUG, + "(%t) scheduling reinitiation of Channel %d\n", + this->id ())); + + // Reschedule ourselves to try and connect again. + if (ACE_Service_Config::reactor ()->schedule_timer (this, 0, + this->timeout ()) == -1) + ACE_ERROR_RETURN ((LM_ERROR, "(%t) %p\n", + "schedule_timer"), -1); + } + return 0; +} + +// Handle shutdown of the Channel object. + +int +Channel::handle_close (ACE_HANDLE, ACE_Reactor_Mask) +{ + ACE_DEBUG ((LM_DEBUG, "(%t) shutting down Channel %d on handle %d\n", + this->id (), this->get_handle ())); + + return this->reinitiate_connection (); +} + +// Set the state of the channel. + +void +Channel::state (Channel::State s) +{ + this->state_ = s; +} + +// Perform the first-time initiation of a connection to the peer. + +int +Channel::initialize_connection (void) +{ + this->state_ = Channel::ESTABLISHED; + + // Restart the timeout to 1. + this->timeout (1); + +#if defined (ASSIGN_ROUTING_ID) + // Action that sends the route id to the peerd. + + CONN_ID id = htons (this->id ()); + + ssize_t n = this->peer ().send ((const void *) &id, sizeof id); + + if (n != sizeof id) + ACE_ERROR_RETURN ((LM_ERROR, "(%t) %p\n", + n == 0 ? "gatewayd has closed down unexpectedly" : "send"), -1); +#endif /* ASSIGN_ROUTING_ID */ + return 0; +} + +// Set the size of the socket queue. + +void +Channel::socket_queue_size (void) +{ + if (this->socket_queue_size_ > 0) + { + int option = this->direction_ == 'I' ? SO_RCVBUF : SO_SNDBUF; + + if (this->peer ().set_option (SOL_SOCKET, option, + &this->socket_queue_size_, sizeof (int)) == -1) + ACE_ERROR ((LM_ERROR, "(%t) %p\n", "set_option")); + } +} + +// Upcall from the ACE_Acceptor::handle_input() that +// delegates control to our application-specific Channel. + +int +Channel::open (void *a) +{ + ACE_DEBUG ((LM_DEBUG, "(%t) Channel's fd = %d\n", this->peer ().get_handle ())); + + // Set the size of the socket queue. + this->socket_queue_size (); + + // Turn on non-blocking I/O. + if (this->peer ().enable (ACE_NONBLOCK) == -1) + ACE_ERROR_RETURN ((LM_ERROR, "(%t) %p\n", "enable"), -1); + + // Call down to the base class to activate and register this handler. + if (this->ACE_Svc_Handler<CHANNEL_PEER_STREAM, SYNCH>::open (a) == -1) + ACE_ERROR_RETURN ((LM_ERROR, "(%t) %p\n", "activate"), -1); + + return this->initialize_connection (); +} + +// Return the current state of the channel. + +Channel::State +Channel::state (void) +{ + return this->state_; +} + +void +Channel::id (CONN_ID id) +{ + this->id_ = id; +} + +CONN_ID +Channel::id (void) +{ + return this->id_; +} + +// Set the peer's address information. +int +Channel::bind (const ACE_INET_Addr &remote_addr, + const ACE_INET_Addr &local_addr, + CONN_ID id) +{ + this->remote_addr_ = remote_addr; + this->local_addr_ = local_addr; + this->id_ = id; + return 0; +} + +ACE_INET_Addr & +Channel::remote_addr (void) +{ + return this->remote_addr_; +} + +ACE_INET_Addr & +Channel::local_addr (void) +{ + return this->local_addr_; +} + +// Constructor sets the routing table pointer. + +Output_Channel::Output_Channel (ROUTING_TABLE *rt, + Channel_Connector *cc, + ACE_Thread_Manager *thr_mgr, + int socket_queue_size) + : Channel (rt, cc, thr_mgr, socket_queue_size) +{ + this->direction_ = 'O'; + this->msg_queue ()->high_water_mark (Output_Channel::QUEUE_SIZE); +} + +// This method should be called only when the peer shuts down +// unexpectedly. This method simply marks the Channel as +// having failed so that handle_close () can reconnect. + +int +Output_Channel::handle_input (ACE_HANDLE) +{ + char buf[1]; + + this->state (Channel::FAILED); + + switch (this->peer ().recv (buf, sizeof buf)) + { + case -1: + ACE_ERROR_RETURN ((LM_ERROR, + "(%t) Peer has failed unexpectedly for Output Channel %d\n", + this->id ()), -1); + /* NOTREACHED */ + case 0: + ACE_ERROR_RETURN ((LM_ERROR, + "(%t) Peer has shutdown unexpectedly for Output Channel %d\n", + this->id ()), -1); + /* NOTREACHED */ + default: + ACE_ERROR_RETURN ((LM_ERROR, + "(%t) Peer is sending input on Output Channel %d\n", + this->id ()), -1); + /* NOTREACHED */ + } +} + +int +Output_Channel::svc (void) +{ + ACE_ERROR_RETURN ((LM_ERROR, "(%t) svc should not be called on Output_Channel!\n"), -1); +} + +// Perform a non-blocking put() of message MB. If we are unable to +// send the entire message the remainder is re-queued at the *front* of +// the Message_List. + +int +Output_Channel::nonblk_put (ACE_Message_Block *mb) +{ + // Try to send the message. If we don't send it all (e.g., due to + // flow control), then re-queue the remainder at the head of the + // Message_List and ask the ACE_Reactor to inform us (via + // handle_output()) when it is possible to try again. + + ssize_t n; + + if ((n = this->send_peer (mb)) == -1) + { + // Things have gone wrong, let's try to close down and set up a new reconnection. + this->state (Channel::FAILED); + this->handle_close (); + return -1; + } + else if (errno == EWOULDBLOCK) // Didn't manage to send everything. + { + ACE_DEBUG ((LM_DEBUG, "(%t) queueing activated on handle %d to routing id %d\n", + this->get_handle (), this->id ())); + + // ACE_Queue in *front* of the list to preserve order. + if (this->msg_queue ()->enqueue_head (mb, (ACE_Time_Value *) &ACE_Time_Value::zero) == -1) + ACE_ERROR_RETURN ((LM_ERROR, "(%t) %p\n", "enqueue_head"), -1); + + // Tell ACE_Reactor to call us back when we can send again. + else if (ACE_Service_Config::reactor ()-> + schedule_wakeup (this, ACE_Event_Handler::WRITE_MASK) == -1) + ACE_ERROR_RETURN ((LM_ERROR, "(%t) %p\n", "schedule_wakeup"), -1); + return 0; + } + else + return n; +} + +int +Output_Channel::send_peer (ACE_Message_Block *mb) +{ + ssize_t n; + size_t len = mb->length (); + + if ((n = this->peer ().send (mb->rd_ptr (), len)) <= 0) + return errno == EWOULDBLOCK ? 0 : n; + else if (n < len) + // Re-adjust pointer to skip over the part we did send. + mb->rd_ptr (n); + else /* if (n == length) */ + { + // The whole message is sent, we can now safely deallocate the buffer. + // Note that this should decrement a reference count... + delete mb; + errno = 0; + } + this->total_bytes (n); + return n; +} + +// Finish sending a message when flow control conditions abate. +// This method is automatically called by the ACE_Reactor. + +int +Output_Channel::handle_output (ACE_HANDLE) +{ + ACE_Message_Block *mb = 0; + int status = 0; + + ACE_DEBUG ((LM_DEBUG, "(%t) in handle_output on handle %d\n", this->get_handle ())); + // The list had better not be empty, otherwise there's a bug! + + if (this->msg_queue ()->dequeue_head (mb, (ACE_Time_Value *) &ACE_Time_Value::zero) != -1) + { + switch (this->nonblk_put (mb)) + { + case 0: // Partial send. + ACE_ASSERT (errno == EWOULDBLOCK); + // Didn't write everything this time, come back later... + break; + + case -1: + // Caller is responsible for freeing a ACE_Message_Block if failures occur. + delete mb; + ACE_ERROR ((LM_ERROR, "(%t) %p\n", "transmission failure")); + + /* FALLTHROUGH */ + default: // Sent the whole thing. + + // If we succeed in writing the entire message (or we did not fail + // due to EWOULDBLOCK) then check if there are more messages on the Message_List. + // If there aren't, tell the ACE_Reactor not to notify us anymore (at least + // until there are new messages queued up). + + if (this->msg_queue ()->is_empty ()) + { + ACE_DEBUG ((LM_DEBUG, "(%t) queueing deactivated on handle %d to routing id %d\n", + this->get_handle (), this->id ())); + + + if (ACE_Service_Config::reactor ()-> + cancel_wakeup (this, ACE_Event_Handler::WRITE_MASK) == -1) + ACE_ERROR ((LM_ERROR, "(%t) %p\n", "cancel_wakeup")); + } + } + } + else + ACE_ERROR ((LM_ERROR, "(%t) %p\n", "dequeue_head")); + return 0; +} + +// Send a message to a peer (may queue if necessary). + +int +Output_Channel::put (ACE_Message_Block *mb, ACE_Time_Value *) +{ + if (this->msg_queue ()->is_empty ()) + // Try to send the message *without* blocking! + return this->nonblk_put (mb); + else + // If we have queued up messages due to flow control + // then just enqueue and return. + return this->msg_queue ()->enqueue_tail (mb, (ACE_Time_Value *) &ACE_Time_Value::zero); +} + +// Constructor sets the routing table pointer and the connector pointer. + +Input_Channel::Input_Channel (ROUTING_TABLE *rt, + Channel_Connector *cc, + ACE_Thread_Manager *thr_mgr, + int socket_queue_size) + : msg_frag_ (0), + Channel (rt, cc, thr_mgr, socket_queue_size) +{ + this->direction_ = 'I'; + this->msg_queue ()->high_water_mark (0); +} + +int +Input_Channel::put (ACE_Message_Block *, ACE_Time_Value *) +{ + ACE_ERROR_RETURN ((LM_ERROR, "(%t) put should not be called on Input_Channel!\n"), -1); +} + +int +Input_Channel::svc (void) +{ + ACE_ERROR_RETURN ((LM_ERROR, "(%t) svc should not be called on Input_Channel!\n"), -1); +} + +// Receive a Peer message from peerd. Handles fragmentation. +// +// The routing message returned from recv_peer consists of two parts: +// 1. The Address part, contains the virtual routing id. +// 2. The Data part, which contains the actual data to be routed. +// +// The reason for having two parts is to shield the higher layers +// of software from knowledge of the message structure. + +int +Input_Channel::recv_peer (ACE_Message_Block *&route_addr) +{ + Peer_Message *peer_msg; + size_t len; + ssize_t n = 0; + ssize_t m = 0; + size_t offset = 0; + + if (this->msg_frag_ == 0) + { + // No existing fragment... + ACE_NEW_RETURN (this->msg_frag_, + ACE_Message_Block (sizeof (Peer_Message)), + -1); + + if (this->msg_frag_ == 0) + ACE_ERROR_RETURN ((LM_ERROR, "(%t) out of memory\n"), -1); + + peer_msg = (Peer_Message *) this->msg_frag_->rd_ptr (); + + switch (n = this->peer ().recv (peer_msg, sizeof (Peer_Header))) + { + case sizeof (Peer_Header): + len = ntohl (peer_msg->header_.len_); + if (len <= sizeof peer_msg->buf_) + { + this->msg_frag_->wr_ptr (sizeof (Peer_Header)); + break; // The message is within the maximum size range. + } + else + ACE_ERROR ((LM_ERROR, "(%t) message too long = %d\n", len)); + /* FALLTHROUGH */ + default: + ACE_ERROR ((LM_ERROR, "(%t) invalid length = %d\n", n)); + n = -1; + /* FALLTHROUGH */ + case -1: + /* FALLTHROUGH */ + case 0: // Premature EOF. + // Make sure to free up memory on error returns. + delete this->msg_frag_; + this->msg_frag_ = 0; + return n; + } + } + else + { + // Figure out where we left off. + peer_msg = (Peer_Message *) this->msg_frag_->rd_ptr (); + offset = this->msg_frag_->length () - sizeof (Peer_Header); + len = peer_msg->header_.len_ - offset; + } + + // Try to receive the remainder of the message. + + switch (m = this->peer ().recv (peer_msg->buf_ + offset, len)) + { + case -1: + if (errno == EWOULDBLOCK) + { + // This shouldn't happen since the ACE_Reactor + // just triggered us to handle pending I/O! + ACE_DEBUG ((LM_DEBUG, "(%t) unexpected recv failure\n")); + errno = EWOULDBLOCK; + return -1; + } + else + /* FALLTHROUGH */; + + case 0: // Premature EOF. + delete this->msg_frag_; + this->msg_frag_ = 0; + return 0; + + default: + if (m != len) + // Re-adjust pointer to skip over the part we've read. + { + this->msg_frag_->wr_ptr (m); + errno = EWOULDBLOCK; + return -1; // Inform caller that we didn't get the whole message. + } + else + { + // Set the write pointer at 1 past the end of the message. + this->msg_frag_->wr_ptr (m); + + // Set the read pointer to the beginning of the message. + this->msg_frag_->rd_ptr (this->msg_frag_->base ()); + + // Allocate a routing message header and chain the data portion + // onto its continuation field. + ACE_NEW_RETURN (route_addr, + ACE_Message_Block (sizeof (Peer_Addr), + ACE_Message_Block::MB_PROTO, + this->msg_frag_), + -1); + + Peer_Addr peer_addr (this->id (), peer_msg->header_.routing_id_, 0); + // Copy the routing address from the Peer_Message into routing_addr. + route_addr->copy ((char *) &peer_addr, sizeof (Peer_Addr)); + + // Reset the pointer to indicate we've got an entire message. + this->msg_frag_ = 0; + } + this->total_bytes (m + n); +#if defined (VERBOSE) + ACE_DEBUG ((LM_DEBUG, "(%t) channel id = %d, route id = %d, len = %d, payload = %*s", + peer_addr.conn_id_, peer_msg->header_.routing_id_, peer_msg->header_.len_, + peer_msg->header_.len_, peer_msg->buf_)); +#else + ACE_DEBUG ((LM_DEBUG, "(%t) route id = %d, cur len = %d, total bytes read = %d\n", + peer_msg->header_.routing_id_, peer_msg->header_.len_, this->total_bytes ())); +#endif + return m + n; + } +} + +// Receive various types of input (e.g., Peer message from the +// gatewayd, as well as stdio). + +int +Input_Channel::handle_input (ACE_HANDLE) +{ + ACE_Message_Block *route_addr = 0; + + switch (this->recv_peer (route_addr)) + { + case 0: + // Note that a peer should never initiate a shutdown. + this->state (Channel::FAILED); + ACE_ERROR_RETURN ((LM_ERROR, + "(%t) Peer has closed down unexpectedly for Input Channel %d\n", + this->id ()), -1); + /* NOTREACHED */ + case -1: + if (errno == EWOULDBLOCK) + // A short-read, we'll come back and finish it up later on! + return 0; + else // A weird problem occurred, shut down and start again. + { + this->state (Channel::FAILED); + ACE_ERROR_RETURN ((LM_ERROR, "(%t) %p for Input Channel %d\n", + "Peer has failed unexpectedly", + this->id ()), -1); + } + /* NOTREACHED */ + default: + return this->route_message (route_addr); + } +} + +// Route a message to its appropriate destination. + +int +Input_Channel::route_message (ACE_Message_Block *route_addr) +{ + // We got a valid message, so determine its virtual routing id, + // which is stored in the first of the two message blocks chained together. + + Peer_Addr *routing_key = (Peer_Addr *) route_addr->rd_ptr (); + + // Skip over the address portion. + const ACE_Message_Block *const data = route_addr->cont (); + + // RE points to the routing entry located for this routing id. + Routing_Entry *re = 0; + + if (this->routing_table_->find (*routing_key, re) != -1) + { + // Check to see if there are any destinations. + if (re->destinations ()->size () == 0) + ACE_DEBUG ((LM_WARNING, + "there are no active destinations for this message currently\n")); + + else // There are destinations, so forward the message. + { + Routing_Entry::ENTRY_SET *esp = re->destinations (); + Routing_Entry::ENTRY_ITERATOR si (*esp); + + for (Channel **channel = 0; si.next (channel) != 0; si.advance ()) + { + // Only process active channels. + if ((*channel)->active ()) + { + // Clone the message portion (should be doing reference counting here...) + ACE_Message_Block *newmsg = data->clone (); + + ACE_DEBUG ((LM_DEBUG, "(%t) sending to peer %d\n", (*channel)->id ())); + + if ((*channel)->put (newmsg) == -1) + { + if (errno == EWOULDBLOCK) // The queue has filled up! + ACE_ERROR ((LM_ERROR, "(%t) %p\n", + "gateway is flow controlled, so we're dropping messages")); + else + ACE_ERROR ((LM_ERROR, "(%t) %p transmission error to route %d\n", + "put", (*channel)->id ())); + + // Caller is responsible for freeing a ACE_Message_Block if failures occur. + delete newmsg; + } + } + } + // Will become superfluous once we have reference counting... + delete route_addr; + return 0; + } + } + delete route_addr; + // Failure return. + ACE_ERROR ((LM_DEBUG, "(%t) find failed on conn id = %d, logical id = %d, payload = %d\n", + routing_key->conn_id_, routing_key->logical_id_, routing_key->payload_)); + return 0; +} + +#if defined (ACE_TEMPLATES_REQUIRE_SPECIALIZATION) +template class ACE_Map_Manager<Peer_Addr, Routing_Entry *, MUTEX>; +template class ACE_Map_Iterator<Peer_Addr, Routing_Entry *, MUTEX>; +template class ACE_Map_Entry<Peer_Addr, Routing_Entry *>; +#endif /* ACE_TEMPLATES_REQUIRE_SPECIALIZATION */ diff --git a/apps/Gateway/Gateway/Channel.h b/apps/Gateway/Gateway/Channel.h new file mode 100644 index 00000000000..9410d0121ae --- /dev/null +++ b/apps/Gateway/Gateway/Channel.h @@ -0,0 +1,281 @@ +/* -*- C++ -*- */ +// @(#)Channel.h 1.1 10/18/96 + + +// ============================================================================ +// +// = LIBRARY +// apps +// +// = FILENAME +// Channel.h +// +// = AUTHOR +// Doug Schmidt +// +// ============================================================================ + +#if !defined (_CHANNEL) +#define _CHANNEL + +#include "ace/Service_Config.h" +#include "ace/INET_Addr.h" +#include "ace/SOCK_Connector.h" +#include "ace/Svc_Handler.h" +#include "Routing_Table.h" +#include "Routing_Entry.h" +#include "Peer_Message.h" + +// The following typedefs are used in order to parameterize the +// synchronization policies without changing the source code! + +// If we don't have threads then use the single-threaded synchronization. +#if !defined (ACE_HAS_THREADS) +#define SYNCH ACE_NULL_SYNCH +typedef ACE_Null_Mutex MUTEX; +#define CHANNEL_PEER_STREAM ACE_SOCK_STREAM +#define CHANNEL_PEER_CONNECTOR ACE_SOCK_CONNECTOR +#else /* ACE_HAS_THREADS */ + +// Select communication mechanisms. +#if 0 // defined (ACE_HAS_TLI) +// Note that due to inconsistencies between the semantics of sockets +// and TLI with respect to establishing non-blocking connections it's +// not a good idea to use TLI... +#include "ace/TLI_Connector.h" +#define CHANNEL_PEER_STREAM ACE_TLI_STREAM +#define CHANNEL_PEER_CONNECTOR ACE_TLI_CONNECTOR +#else +#define CHANNEL_PEER_STREAM ACE_SOCK_STREAM +#define CHANNEL_PEER_CONNECTOR ACE_SOCK_CONNECTOR +#endif /* 0 */ + +// Note that we only need to make the ACE_Task thread-safe if we +// are using the multi-threaded Thr_Output_Channel... +#if defined (USE_OUTPUT_MT) +#define SYNCH ACE_MT_SYNCH +#else +#define SYNCH ACE_NULL_SYNCH +#endif /* USE_OUTPUT_MT || USE_INPUT_MT */ + +// Note that we only need to make the ACE_Map_Manager thread-safe if +// we are using the multi-threaded Thr_Input_Channel... +#if defined (USE_INPUT_MT) +typedef ACE_RW_Mutex MUTEX; +#else +typedef ACE_Null_Mutex MUTEX; +#endif /* USE_INPUT_MT */ +#endif /* ACE_HAS_THREADS */ + +// Typedef for the routing table. +typedef Routing_Table<Peer_Addr, Routing_Entry, MUTEX> + ROUTING_TABLE; + +// Forward declaration. +class Channel_Connector; + +class Channel : public ACE_Svc_Handler<CHANNEL_PEER_STREAM, SYNCH> + // = TITLE + // Channel contains info about connection state and addressing. + // + // = DESCRIPTION + // The Channel classes process messages sent from the peers to the + // gateway. These classes works as follows: + // + // 1. Channel_Connector creates a number of connections with the set of + // peers specified in a configuration file. + // + // 2. For each peer that connects successfully, Channel_Connector + // creates an Channel object. Each object assigns a unique routing + // id to its associated peer. The Channels are used by gatewayd + // that to receive, route, and forward messages from source peer(s) + // to destination peer(s). +{ +public: + Channel (ROUTING_TABLE *, + Channel_Connector *, + ACE_Thread_Manager * = 0, + int socket_queue_size = 0); + + virtual int open (void * = 0); + // Initialize and activate a single-threaded Channel (called by + // ACE_Connector::handle_output()). + + int bind (const ACE_INET_Addr &remote_addr, + const ACE_INET_Addr &local_addr, + CONN_ID); + // Set the peer's addressing and routing information. + + ACE_INET_Addr &remote_addr (void); + // Returns the peer's routing address. + + ACE_INET_Addr &local_addr (void); + // Returns our local address. + + // = Set/get routing id. + CONN_ID id (void); + void id (CONN_ID); + + // = Set/get the current state of the Channel. + enum State + { + IDLE = 1, // Prior to initialization. + CONNECTING, // During connection establishment. + ESTABLISHED, // Channel is established and active. + DISCONNECTING, // Channel is in the process of connecting. + FAILED // Channel has failed. + }; + + // = Set/get the current state. + State state (void); + void state (State); + + // = Set/get the current retry timeout delay. + int timeout (void); + void timeout (int); + + // = Set/get the maximum retry timeout delay. + int max_timeout (void); + void max_timeout (int); + + // = Set/get Channel activity status. + int active (void); + void active (int); + + // = Set/get direction (necessary for error checking). + char direction (void); + void direction (char); + + // = The total number of bytes sent/received on this channel. + size_t total_bytes (void); + void total_bytes (size_t bytes); + // Increment count by <bytes>. + + virtual int handle_timeout (const ACE_Time_Value &, const void *arg); + // Perform timer-based Channel reconnection. + +protected: + enum + { + MAX_RETRY_TIMEOUT = 300 // 5 minutes is the maximum timeout. + }; + + int initialize_connection (void); + // Perform the first-time initiation of a connection to the peer. + + int reinitiate_connection (void); + // Reinitiate a connection asynchronously when peers fail. + + void socket_queue_size (void); + // Set the socket queue size. + + virtual int handle_close (ACE_HANDLE = ACE_INVALID_HANDLE, + ACE_Reactor_Mask = ACE_Event_Handler::RWE_MASK); + // Perform Channel termination. + + ROUTING_TABLE *routing_table_; + // Pointer to table that maps a Peer_Addr + // to a Set of Channel *'s for output. + + ACE_INET_Addr remote_addr_; + // Address of peer. + + ACE_INET_Addr local_addr_; + // Address of us. + + CONN_ID id_; + // The assigned routing ID of this entry. + + size_t total_bytes_; + // The total number of bytes sent/received on this channel. + + State state_; + // The current state of the channel. + + Channel_Connector *connector_; + // Back pointer to Channel_Connector to reestablish broken + // connections. + + int timeout_; + // Amount of time to wait between reconnection attempts. + + int max_timeout_; + // Maximum amount of time to wait between reconnection attempts. + + char direction_; + // Indicates which direction data flows through the channel ('O' == + // output and 'I' == input). + + int socket_queue_size_; + // Size of the socket queue (0 means "use default"). +}; + +class Input_Channel : public Channel + // = TITLE + // Handle reception of Peer messages arriving as events. +{ +public: + Input_Channel (ROUTING_TABLE *, + Channel_Connector *, + ACE_Thread_Manager * = 0, + int socket_queue_size = 0); + // Constructor sets the routing table pointer. + + virtual int handle_input (ACE_HANDLE = ACE_INVALID_HANDLE); + // Receive and process peer messages. + +protected: + virtual int recv_peer (ACE_Message_Block *&); + // Receive a message from a peer. + + int route_message (ACE_Message_Block *); + // Action that receives messages from peerd. + + ACE_Message_Block *msg_frag_; + // Keep track of message fragment to handle non-blocking recv's from + // peers. + + virtual int svc (void); + // This method is not used since we are single-threaded. + +private: + virtual int put (ACE_Message_Block *, ACE_Time_Value *tv = 0); + // This methods should not be called to handle input. +}; + +class Output_Channel : public Channel + // = TITLE + // Handle transmission of messages to other Peers using a + // single-threaded approach. +{ +public: + Output_Channel (ROUTING_TABLE *, + Channel_Connector *, + ACE_Thread_Manager * = 0, + int socket_queue_size = 0); + + virtual int put (ACE_Message_Block *, ACE_Time_Value * = 0); + // Send a message to a gateway (may be queued if necessary). + +protected: + // = We'll allow up to 16 megabytes to be queued per-output + // channel. + enum {QUEUE_SIZE = 1024 * 1024 * 16}; + + virtual int handle_input (ACE_HANDLE); + // Receive and process shutdowns from peer. + + virtual int handle_output (ACE_HANDLE); + // Finish sending a message when flow control conditions abate. + + int nonblk_put (ACE_Message_Block *mb); + // Perform a non-blocking put(). + + virtual int send_peer (ACE_Message_Block *); + // Send a message to a peer. + + virtual int svc (void); + // This method is not used since we are single-threaded. +}; + +#endif /* _CHANNEL */ diff --git a/apps/Gateway/Gateway/Channel_Connector.cpp b/apps/Gateway/Gateway/Channel_Connector.cpp new file mode 100644 index 00000000000..429fa595df2 --- /dev/null +++ b/apps/Gateway/Gateway/Channel_Connector.cpp @@ -0,0 +1,92 @@ +#include "Channel_Connector.h" +// @(#)Channel_Connector.cpp 1.1 10/18/96 + + +Channel_Connector::Channel_Connector (void) +{ +} + +// Override the connection-failure method to add timer support. +// Note that these timers perform "expoential backoff" to +// avoid rapidly trying to reestablish connections when a link +// goes down. + +int +Channel_Connector::handle_close (ACE_HANDLE sd, ACE_Reactor_Mask) +{ + ACE_Connector<Channel, CHANNEL_PEER_CONNECTOR>::AST *stp = 0; + + // Locate the ACE_Svc_Handler corresponding to the socket descriptor. + if (this->handler_map_.find (sd, stp) == -1) + ACE_ERROR_RETURN ((LM_ERROR, "(%t) can't locate channel %d in map, %p\n", + sd, "find"), -1); + + Channel *channel = stp->svc_handler (); + + // Schedule a reconnection request at some point in the future + // (note that channel uses an exponential backoff scheme). + if (ACE_Service_Config::reactor ()->schedule_timer (channel, 0, + channel->timeout ()) == -1) + ACE_ERROR_RETURN ((LM_ERROR, "(%t) %p\n", + "schedule_timer"), -1); + return 0; +} + +// Initiate (or reinitiate) a connection to the Channel. + +int +Channel_Connector::initiate_connection (Channel *channel, + ACE_Synch_Options &synch_options) +{ + char buf[MAXHOSTNAMELEN]; + + // Mark ourselves as idle so that the various iterators + // will ignore us until we are reconnected. + channel->state (Channel::IDLE); + + if (channel->remote_addr ().addr_to_string (buf, sizeof buf) == -1 + || channel->local_addr ().addr_to_string (buf, sizeof buf) == -1) + ACE_ERROR_RETURN ((LM_ERROR, "(%t) %p\n", + "can't obtain peer's address"), -1); + + // Try to connect to the Peer. + + if (this->connect (channel, channel->remote_addr (), + synch_options, channel->local_addr ()) == -1) + { + if (errno != EWOULDBLOCK) + { + channel->state (Channel::FAILED); + ACE_DEBUG ((LM_DEBUG, "(%t) %p on address %s\n", + "connect", buf)); + + // Reschedule ourselves to try and connect again. + if (synch_options[ACE_Synch_Options::USE_REACTOR]) + { + if (ACE_Service_Config::reactor ()->schedule_timer + (channel, 0, channel->timeout ()) == 0) + ACE_ERROR_RETURN ((LM_ERROR, "(%t) %p\n", + "schedule_timer"), -1); + } + else + // Failures on synchronous connects are reported as errors + // so that the caller can decide how to proceed. + return -1; + } + else + { + channel->state (Channel::CONNECTING); + ACE_DEBUG ((LM_DEBUG, + "(%t) in the process of connecting %s to %s\n", + synch_options[ACE_Synch_Options::USE_REACTOR] + ? "asynchronously" : "synchronously", buf)); + } + } + else + { + channel->state (Channel::ESTABLISHED); + ACE_DEBUG ((LM_DEBUG, "(%t) connected to %s on %d\n", + buf, channel->get_handle ())); + } + return 0; +} diff --git a/apps/Gateway/Gateway/Channel_Connector.h b/apps/Gateway/Gateway/Channel_Connector.h new file mode 100644 index 00000000000..f15ced0e14d --- /dev/null +++ b/apps/Gateway/Gateway/Channel_Connector.h @@ -0,0 +1,41 @@ +/* -*- C++ -*- */ +// @(#)Channel_Connector.h 1.1 10/18/96 + + +// ============================================================================ +// +// = LIBRARY +// apps +// +// = FILENAME +// Channel_Connector.h +// +// = AUTHOR +// Doug Schmidt +// +// ============================================================================ + +#if !defined (_CHANNEL_CONNECTOR) +#define _CHANNEL_CONNECTOR + +#include "ace/Connector.h" +#include "Thr_Channel.h" + +class Channel_Connector : public ACE_Connector<Channel, CHANNEL_PEER_CONNECTOR> + // = TITLE + // A concrete factory class that setups connections to peerds + // and produces a new Channel object to do the dirty work... +{ +public: + Channel_Connector (void); + + // Initiate (or reinitiate) a connection on the Channel. + int initiate_connection (Channel *, + ACE_Synch_Options & = ACE_Synch_Options::synch); + +protected: + // Override the connection-failure method to add timer support. + virtual int handle_close (ACE_HANDLE sd, ACE_Reactor_Mask); +}; + +#endif /* _CHANNEL_CONNECTOR */ diff --git a/apps/Gateway/Gateway/Config_Files.cpp b/apps/Gateway/Gateway/Config_Files.cpp new file mode 100644 index 00000000000..5dddbf8cefa --- /dev/null +++ b/apps/Gateway/Gateway/Config_Files.cpp @@ -0,0 +1,170 @@ +#include "ace/OS.h" +// @(#)Config_Files.cpp 1.1 10/18/96 + +#include "Config_Files.h" + +// This fixes a nasty bug with cfront-based compilers (like +// Centerline). +typedef FP::Return_Type FP_RETURN_TYPE; + +FP_RETURN_TYPE +RT_Config_File_Parser::read_entry (RT_Config_File_Entry &entry, + int &line_number) +{ + FP_RETURN_TYPE read_result; + // increment the line count + line_number++; + + // Ignore comments, check for EOF and EOLINE + // if this succeeds, we have our connection id + while ((read_result = this->getint (entry.conn_id_)) != FP::SUCCESS) + { + if (read_result == FP::EOFILE) + return FP::EOFILE; + else if (read_result == FP::EOLINE + || read_result == FP::COMMENT) + { + // increment the line count + line_number++; + } + } + + // Get the logic id. + if ((read_result = this->getint (entry.logical_id_)) != FP::SUCCESS) + return read_result; + + // Get the payload type. + if ((read_result = this->getint (entry.payload_type_)) != FP::SUCCESS) + return read_result; + + // get all the destinations. + entry.total_destinations_ = 0; + + while ((read_result = this->getint (entry.destinations_[entry.total_destinations_])) + == FP::SUCCESS) + ++entry.total_destinations_; // do nothing + + if (read_result == FP::EOLINE || read_result == FP::EOFILE) + return FP::SUCCESS; + else + return read_result; +} + +FP_RETURN_TYPE +CC_Config_File_Parser::read_entry (CC_Config_File_Entry &entry, + int &line_number) +{ + char buf[BUFSIZ]; + FP_RETURN_TYPE read_result; + // increment the line count + line_number++; + + // Ignore comments, check for EOF and EOLINE + // if this succeeds, we have our connection id + while ((read_result = this->getint (entry.conn_id_)) != FP::SUCCESS) + { + if (read_result == FP::EOFILE) + return FP::EOFILE; + else if (read_result == FP::EOLINE || + read_result == FP::COMMENT) + { + // increment the line count + line_number++; + } + } + + // get the hostname + if ((read_result = this->getword (entry.host_)) != FP::SUCCESS) + return read_result; + + int port; + + // Get the port number. + if ((read_result = this->getint (port)) != FP::SUCCESS) + return read_result; + else + entry.remote_port_ = (u_short) port; + + // Get the direction. + if ((read_result = this->getword (buf)) != FP::SUCCESS) + return read_result; + else + entry.direction_ = buf[0]; + + // Get the max retry delay. + if ((read_result = this->getint (entry.max_retry_delay_)) != FP::SUCCESS) + return read_result; + + // Get the local port number. + if ((read_result = this->getint (port)) != FP::SUCCESS) + return read_result; + else + entry.local_port_ = (u_short) port; + + return FP::SUCCESS; +} + +#if defined (DEBUGGING) +int main (int argc, char *argv[]) +{ + if (argc != 4) { +// ACE_ERROR_RETURN ((LM_ERROR, "%s filename\n", argv[0]), -1); + cerr << argv[0] << " CCfilename RTfilename Mapfilename.\n"; + exit (1); + } + FP_RETURN_TYPE result; + CC_Config_File_Entry CCentry; + CC_Config_File_Parser CCfile; + + CCfile.open (argv[1]); + + int line_number = 0; + + printf ("ConnID\tHost\t\tRPort\tDir\tRetry\tLPort\n"); + + // Read config file line at a time. + while ((result = CCfile.read_entry (CCentry, line_number)) != EOF) + { + if (result != FP::SUCCESS) + // ACE_DEBUG ((LM_DEBUG, "Error line %d.\n", line_number)); + cerr << "Error at line " << line_number << endl; + else + printf ("%d\t%s\t%d\t%c\t%d\t%c\t%d\n", + CCentry.conn_id_, CCentry.host_, CCentry.remote_port_, CCentry.direction_, + CCentry.max_retry_delay_, CCentry.transform_, CCentry.local_port_); + } + CCfile.close(); + + RT_Config_File_Entry RTentry; + RT_Config_File_Parser RTfile; + + RTfile.open (argv[2]); + + line_number = 0; + + printf ("\nConnID\tLogic\tPayload\tDestinations\n"); + + // Read config file line at a time. + while ((result = RTfile.read_entry (RTentry, line_number)) != EOF) + { + if (result != FP::SUCCESS) + cerr << "Error at line " << line_number << endl; + else + { + printf ("%d\t%d\t%d\t%d\t", + RTentry.conn_id_, RTentry.logical_id_, RTentry.payload_type_); + while (--RTentry.total_destinations_ >= 0) + printf ("%d,", RTentry.destinations_[RTentry.total_destinations_]); + printf ("\n"); + } + } + RTfile.close(); + + return 0; +} +#endif /* DEBUGGING */ + +#if defined (ACE_TEMPLATES_REQUIRE_SPECIALIZATION) +template class File_Parser<CC_Config_File_Entry>; +template class File_Parser<RT_Config_File_Entry>; +#endif /* ACE_TEMPLATES_REQUIRE_SPECIALIZATION */ diff --git a/apps/Gateway/Gateway/Config_Files.h b/apps/Gateway/Gateway/Config_Files.h new file mode 100644 index 00000000000..67965fbff8b --- /dev/null +++ b/apps/Gateway/Gateway/Config_Files.h @@ -0,0 +1,91 @@ +/* -*- C++ -*- */ +// @(#)Config_Files.h 1.1 10/18/96 + + +// ============================================================================ +// +// = LIBRARY +// apps +// +// = FILENAME +// Config_Files.h +// +// = AUTHOR +// Doug Schmidt +// +// ============================================================================ + +#if !defined (_CONFIG_FILES) +#define _CONFIG_FILES + +#include "ace/OS.h" +#include "File_Parser.h" + +class CC_Config_File_Entry + // = TITLE + // Stores the information in a Channel Connection entry. +{ +public: + int conn_id_; + // Connection id for this Channel. + + char host_[BUFSIZ]; + // Host to connect with. + + u_short remote_port_; + // Port to connect with. + + char direction_; + // 'I' (input) or 'O' (output) + + int max_retry_delay_; + // Maximum amount of time to wait for reconnecting. + + u_short local_port_; + // Our local port number. +}; + +class CC_Config_File_Parser : public File_Parser<CC_Config_File_Entry> + // = TITLE + // Parser for the Channel Connection file. +{ +public: + virtual FP::Return_Type + read_entry (CC_Config_File_Entry &entry, int &line_number); +}; + +class RT_Config_File_Entry + // = TITLE + // Stores the information in a Routing Table entry. +{ +public: + enum { + MAX_DESTINATIONS = 1000 // Total number of multicast destinations. + }; + + int conn_id_; + // Connection id for this channel. + + int logical_id_; + // Logical routing id for this channel. + + int payload_type_; + // Type of payload in the message. + + int destinations_[MAX_DESTINATIONS]; + // Connection ids for destinations that we're routing to. + + int total_destinations_; + // Total number of these destinations. +}; + +class RT_Config_File_Parser : public File_Parser<RT_Config_File_Entry> + // = TITLE + // Parser for the Routing Table file. +{ +public: + virtual FP::Return_Type + read_entry (RT_Config_File_Entry &entry, int &line_number); +}; + +#endif /* _CONFIG_FILES */ diff --git a/apps/Gateway/Gateway/File_Parser.cpp b/apps/Gateway/Gateway/File_Parser.cpp new file mode 100644 index 00000000000..2fc52d7980a --- /dev/null +++ b/apps/Gateway/Gateway/File_Parser.cpp @@ -0,0 +1,142 @@ +#if !defined (FILE_PARSER_C) +// @(#)File_Parser.cpp 1.1 10/18/96 + +#define FILE_PARSER_C + +#include "ace/OS.h" +#include "File_Parser.h" + +// This fixes a nasty bug with cfront-based compilers (like +// Centerline). +typedef FP::Return_Type FP_RETURN_TYPE; + +// File_Parser stuff. + +template <class ENTRY> int +File_Parser<ENTRY>::open (const char filename[]) +{ + return (this->infile_ = ACE_OS::fopen (filename, "r")) == 0 ? -1 : 0; +} + +template <class ENTRY> int +File_Parser<ENTRY>::close (void) +{ + return ACE_OS::fclose (this->infile_); +} + +template <class ENTRY> FP_RETURN_TYPE +File_Parser<ENTRY>::getword (char buf[]) +{ + FP_RETURN_TYPE read_result = this->readword(buf); + if (read_result == FP::SUCCESS) + return FP::SUCCESS; + else + return read_result; +} + +// Get the next string from the file via this->readword() +// Check make sure the string forms a valid number. +template <class ENTRY> FP_RETURN_TYPE +File_Parser<ENTRY>::getint (int &value) +{ + char buf[BUFSIZ]; + FP_RETURN_TYPE read_result = this->readword(buf); + if (read_result == FP::SUCCESS) + { + // ptr is used for error checking with ACE_OS::strtol + char *ptr; + + // try to convert the buf to a decimal number + value = ACE_OS::strtol (buf, &ptr, 10); + + // check if the buf is a decimal or not + if ((value == 0) && (ptr == buf)) + return FP::ERROR; + else + return FP::SUCCESS; + } + else + return read_result; +} + + +template <class ENTRY> FP_RETURN_TYPE +File_Parser<ENTRY>::readword (char buf[]) +{ + int wordlength = 0; + int c; + + // Skip over leading delimiters and get word. + + while ((c = getc (this->infile_)) != EOF && c != '\n') + if (this->delimiter (c)) + { + // We've reached the end of a "word". + if (wordlength > 0) + break; + } + else + buf[wordlength++] = c; + + buf[wordlength] = '\0'; + + if (c == EOF) { + // If the EOF is just a dilimeter, don't return EOF so that the + // word gets processed + if (wordlength > 0) + { + ungetc (c, this->infile_); + return FP::SUCCESS; + } + else + // else return EOF so that read loops stop + return FP::EOFILE; + } + else if (c == '\n') + { + // if the EOLINE is just a dilimeter, don't return EOLINE + // so that the word gets processed + if (wordlength > 0) + ungetc (c, this->infile_); + else + return FP::EOLINE; + } + + // Skip comments. + if (this->comments (buf[0])) + { + if (this->skipline () == EOF) + return FP::EOFILE; + else + return FP::COMMENT; + } + else + return FP::SUCCESS; +} + +template <class ENTRY> int +File_Parser<ENTRY>::delimiter (char ch) +{ + return ch == ' ' || ch == ',' || ch == '\t'; +} + +template <class ENTRY> int +File_Parser<ENTRY>::comments (char ch) +{ + return ch == '#'; +} + +template <class ENTRY> int +File_Parser<ENTRY>::skipline (void) +{ + // Skip the remainder of the line. + + int c; + + while ((c = getc (this->infile_)) != '\n' && c != EOF) + continue; + + return c; +} + +#endif /* _FILE_PARSER_C */ diff --git a/apps/Gateway/Gateway/File_Parser.h b/apps/Gateway/Gateway/File_Parser.h new file mode 100644 index 00000000000..cddf3aa3055 --- /dev/null +++ b/apps/Gateway/Gateway/File_Parser.h @@ -0,0 +1,76 @@ +/* -*- C++ -*- */ +// @(#)File_Parser.h 1.1 10/18/96 + + +// ============================================================================ +// +// = LIBRARY +// apps +// +// = FILENAME +// File_Parser.h +// +// = AUTHOR +// Doug Schmidt +// +// ============================================================================ + +#if !defined (_FILE_PARSER) +#define _FILE_PARSER + +#include "ace/OS.h" + +class FP + // = TITLE + // This class serves as a namespace for the Return_Type +{ +public: + enum Return_Type + { + EOLINE, + EOFILE, + SUCCESS, + COMMENT, + ERROR + }; +}; + +template <class ENTRY> +class File_Parser + // = TITLE + // Class used to parse the configuration file for the routing + // table. +{ +public: + // = Open and Close the file specified + int open (const char filename[]); + int close (void); + + virtual FP::Return_Type read_entry (ENTRY &, int &line_number) = 0; + // Implementations use protected methods to fill in the entry. + +protected: + FP::Return_Type getword (char buf[]); + // Read the next ASCII word. + + FP::Return_Type getint (int &value); + // Read the next integer. + + FP::Return_Type readword (char buf[]); + int delimiter (char ch); + int endofline (char ch); + int comments (char ch); + int skipline (void); + + FILE *infile_; +}; + +#if defined (ACE_TEMPLATES_REQUIRE_SOURCE) +#include "File_Parser.cpp" +#endif /* ACE_TEMPLATES_REQUIRE_SOURCE */ + +#if defined (ACE_TEMPLATES_REQUIRE_PRAGMA) +#pragma implementation ("File_Parser.cpp") +#endif /* ACE_TEMPLATES_REQUIRE_PRAGMA */ + +#endif /* _FILE_PARSER */ diff --git a/apps/Gateway/Gateway/Gateway.cpp b/apps/Gateway/Gateway/Gateway.cpp new file mode 100644 index 00000000000..e46bfdd5a86 --- /dev/null +++ b/apps/Gateway/Gateway/Gateway.cpp @@ -0,0 +1,561 @@ +/* -*- C++ -*- */ +// @(#)Gateway.cpp 1.1 10/18/96 + + +#include "ace/Log_Msg.h" +#include "ace/Get_Opt.h" +#include "ace/Service_Config.h" +#include "Config_Files.h" +#include "Gateway.h" +#include "Channel_Connector.h" + +template <class INPUT_CHANNEL, class OUTPUT_CHANNEL> +class Gateway : public ACE_Service_Object +{ +public: + Gateway (ACE_Thread_Manager * = 0); + + virtual int init (int argc, char *argv[]); + // Perform initialization. + + virtual int fini (void); + // Perform termination. + +protected: + int handle_input (ACE_HANDLE); + + int handle_signal (int signum, siginfo_t * = 0, ucontext_t * = 0); + + typedef ACE_Map_Manager<CONN_ID, Channel *, MUTEX> CONFIG_TABLE; + typedef ACE_Map_Iterator<CONN_ID, Channel *, MUTEX> CONFIG_ITERATOR; + + CONFIG_TABLE config_table_; + // Table that maps Connection IDs to Channel *'s. + + ROUTING_TABLE routing_table_; + // Table that maps Peer addresses to a set of Channel *'s for output. + + virtual int info (char **, size_t) const; + // Return info about this service. + + int parse_args (int argc, char *argv[]); + // Parse gateway configuration arguments obtained from svc.conf file. + + int parse_cc_config_file (void); + // Parse the channel connection configuration file. + + int parse_rt_config_file (void); + // Parse the routing table configuration file. + + int initiate_connections (void); + // Initiate connections to the peers. + + virtual int handle_timeout (const ACE_Time_Value &, const void *arg); + // Perform timer-based performance profiling. + + const char *cc_config_file_; + // Name of the channel connection configuration file. + + const char *rt_config_file_; + // Name of the routing table configuration file. + + int performance_window_; + // Number of seconds after connection establishment to report throughput. + + int blocking_semantics_; + // 0 == blocking connects, ACE_NONBLOCK == non-blocking connects. + + int debug_; + // Are we debugging? + + Channel_Connector *connector_; + // This is used to establish the connections actively. + + int socket_queue_size_; + // Size of the socket queue (0 means "use default"). + + // = Manage output and input channel threads (if used.) + // if both input and output mt is used, they will share thr_mgr_, + // thr_mgr_ will always reference the thread manager being used + // regardless of whether input, output, or both channels are using mt. + ACE_Thread_Manager *thr_mgr_; + ACE_Thread_Manager *input_thr_mgr_; + ACE_Thread_Manager *output_thr_mgr_; +}; + +// Convenient shorthands. + +#define IC INPUT_CHANNEL +#define OC OUTPUT_CHANNEL + +template <class IC, class OC> int +Gateway<IC, OC>::handle_signal (int signum, siginfo_t *, ucontext_t *) +{ + if (signum > 0) + ACE_DEBUG ((LM_DEBUG, "(%t) %S\n", signum)); + + if (this->thr_mgr_ != 0) + { +#if defined (ACE_HAS_THREADS) + ACE_DEBUG ((LM_DEBUG, "(%t) suspending all threads\n")); + if (this->thr_mgr_->suspend_all () == -1) + ACE_ERROR_RETURN ((LM_ERROR, "(%t) %p\n", "suspend_all"), -1); +#endif /* ACE_HAS_THREADS */ + } + + // Shut down the main event loop. + ACE_Service_Config::end_reactor_event_loop (); + return 0; +} + +template <class IC, class OC> int +Gateway<IC, OC>::handle_input (ACE_HANDLE h) +{ + if (ACE_Service_Config::reactor ()->remove_handler (0, + ACE_Event_Handler::READ_MASK + | ACE_Event_Handler::DONT_CALL) == -1) + ACE_ERROR_RETURN ((LM_ERROR, "(%t) %p\n", "remove_handler"), -1); + char buf[BUFSIZ]; + // Consume the input... + ACE_OS::read (h, buf, sizeof (buf)); + return this->handle_signal (h); +} + +template <class IC, class OC> int +Gateway<IC, OC>::handle_timeout (const ACE_Time_Value &, const void *) +{ + ACE_DEBUG ((LM_DEBUG, "(%t) doing the performance timeout here...\n")); + CONFIG_ITERATOR cti (this->config_table_); + + // If we've got a ACE_Thread Manager then use it to suspend all + // the threads. This will enable us to get an accurate count. + + if (this->thr_mgr_ != 0) + { +#if defined (ACE_HAS_THREADS) + if (this->thr_mgr_->suspend_all () == -1) + ACE_ERROR_RETURN ((LM_ERROR, "(%t) %p\n", "suspend_all"), -1); + ACE_DEBUG ((LM_DEBUG, "(%t) suspending all threads...")); +#endif /* ACE_HAS_THREADS */ + } + + size_t total_bytes_in = 0; + size_t total_bytes_out = 0; + + // Iterate through the routing table connecting all the channels. + + for (ACE_Map_Entry <CONN_ID, Channel *> *me = 0; + cti.next (me) != 0; + cti.advance ()) + { + Channel *channel = me->int_id_; + if (channel->direction () == 'O') + total_bytes_out += channel->total_bytes (); + else + total_bytes_in += channel->total_bytes (); + } + +#if defined (ACE_NLOGGING) + ACE_OS::fprintf (stderr, "After %d seconds, \ntotal_bytes_in = %d\ntotal_bytes_out = %d\n", + performance_window_, + total_bytes_in, + total_bytes_out); + + ACE_OS::fprintf (stderr, "%f Mbits/sec received.\n", + (float) (total_bytes_in * 8 / (float) (1024*1024*this->performance_window_))); + + ACE_OS::fprintf (stderr, "%f Mbits/sec sent.\n", + (float) (total_bytes_out * 8 / (float) (1024*1024*this->performance_window_))); +#else + ACE_DEBUG ((LM_DEBUG, "(%t) after %d seconds, \ntotal_bytes_in = %d\ntotal_bytes_out = %d\n", + this->performance_window_, + total_bytes_in, + total_bytes_out)); + ACE_DEBUG ((LM_DEBUG, "(%t) %f Mbits/sec received.\n", + (float) (total_bytes_in * 8 / (float) (1024*1024*this->performance_window_)))); + ACE_DEBUG ((LM_DEBUG, "(%t) %f Mbits/sec sent.\n", + (float) (total_bytes_out * 8 / (float) (1024*1024*this->performance_window_)))); +#endif /* ACE_NLOGGING */ + // Resume all the threads again. + if (this->thr_mgr_ != 0) + { +#if defined (ACE_HAS_THREADS) + this->thr_mgr_->resume_all (); + ACE_DEBUG ((LM_DEBUG, "(%t) resuming all threads...")); +#endif /* ACE_HAS_THREADS */ + } + return 0; +} + +// Give default values to data members. + +template <class IC, class OC> +Gateway<IC, OC>::Gateway (ACE_Thread_Manager *thr_mgr) + : cc_config_file_ ("cc_config"), + rt_config_file_ ("rt_config"), + performance_window_ (0), + blocking_semantics_ (ACE_NONBLOCK), + debug_ (0), + connector_ (0), + thr_mgr_ (thr_mgr), + input_thr_mgr_ (thr_mgr), + output_thr_mgr_ (thr_mgr), + socket_queue_size_ (0) +{ +} + +// Parse the "command-line" arguments and set the corresponding flags. + +template <class IC, class OC> int +Gateway<IC, OC>::parse_args (int argc, char *argv[]) +{ + ACE_Get_Opt get_opt (argc, argv, "bc:dr:q:w:", 0); + + for (int c; (c = get_opt ()) != -1; ) + { + switch (c) + { + case 'b': // Use blocking connection establishment. + this->blocking_semantics_ = 0; + break; + case 'c': + this->cc_config_file_ = get_opt.optarg; + break; + case 'd': + this->debug_ = 1; + break; + case 'q': + this->socket_queue_size_ = ACE_OS::atoi (get_opt.optarg); + break; + case 'r': + this->rt_config_file_ = get_opt.optarg; + break; + case 'w': // Time performance for a designated amount of time. + this->performance_window_ = ACE_OS::atoi (get_opt.optarg); + // Use blocking connection semantics so that we get accurate + // timings (since all connections start at once). + this->blocking_semantics_ = 0; + break; + default: + break; + } + } + return 0; +} + +// Initiate connections with the peers. + +template <class IC, class OC> int +Gateway<IC, OC>::initiate_connections (void) +{ + CONFIG_ITERATOR cti (this->config_table_); + + // Iterate through the routing table connecting all the channels. + + for (ACE_Map_Entry <CONN_ID, Channel *> *me = 0; + cti.next (me) != 0; + cti.advance ()) + { + Channel *channel = me->int_id_; + if (this->connector_->initiate_connection + (channel, this->blocking_semantics_ == ACE_NONBLOCK + ? ACE_Synch_Options::asynch : ACE_Synch_Options::synch) == -1) + continue; + } + + return 0; +} + +// This method is automatically called when the gateway +// is shutdown. It gracefully shuts down all the Channels +// in the Channel connection Config_Table. + +template <class IC, class OC> int +Gateway<IC, OC>::fini (void) +{ + // Question: do we need to do anything special about the Routing_Table? + + CONFIG_ITERATOR cti (this->config_table_); + + for (ACE_Map_Entry <CONN_ID, Channel *> *me; + cti.next (me) != 0; + cti.advance ()) + { + Channel *channel = me->int_id_; + ACE_DEBUG ((LM_DEBUG, "(%t) closing down route %d\n", + channel->id ())); + if (channel->state () != Channel::IDLE) + // Mark channel as DISCONNECTING so we don't try to reconnect... + channel->state (Channel::DISCONNECTING); + + // Deallocate Channel resources. + channel->destroy (); // Will trigger a delete. + } + + // Free up the resources allocated dynamically by the ACE_Connector. + delete this->connector_; + delete this->thr_mgr_; + + return 0; +} + +template <class IC, class OC> int +Gateway<IC, OC>::init (int argc, char *argv[]) +{ + this->parse_args (argc, argv); + + ACE_NEW_RETURN (this->connector_, Channel_Connector (), -1); + + if (this->connector_ == 0) + ACE_ERROR_RETURN ((LM_ERROR, "(%t) %p\n", "out of memory"), -1); + + // Ignore SIPPIPE so each Output_Channel can handle it. + ACE_Sig_Action sig (ACE_SignalHandler (SIG_IGN), SIGPIPE); + + ACE_Sig_Set sig_set; + sig_set.sig_add (SIGINT); + sig_set.sig_add (SIGQUIT); + + // Register ourselves to receive SIGINT and SIGQUIT + // so we can shut down gracefully via signals. + + if (ACE_Service_Config::reactor ()->register_handler (sig_set, + this) == -1) + ACE_ERROR_RETURN ((LM_ERROR, "(%t) %p\n", "register_handler"), -1); + + if (ACE_Service_Config::reactor ()->register_handler (0, + this, + ACE_Event_Handler::READ_MASK) == -1) + ACE_ERROR_RETURN ((LM_ERROR, "(%t) %p\n", "register_handler"), -1); + + if (this->thr_mgr_ == 0) + // Create a thread manager if using some combination of multi-threaded channels. +#if defined (USE_OUTPUT_MT) && defined (USE_INPUT_MT) + this->thr_mgr_ = this->output_thr_mgr_ = + this->input_thr_mgr_ = ACE_Service_Config::thr_mgr (); +#elif defined (USE_OUTPUT_MT) + this->thr_mgr_ = this->output_thr_mgr_ = ACE_Service_Config::thr_mgr (); +#elif defined (USE_INPUT_MT) + this->thr_mgr_ = this->input_thr_mgr_ = ACE_Service_Config::thr_mgr (); +#endif + + // Parse the connection configuration file. + this->parse_cc_config_file (); + + // Parse the routing table config file and build the routing table. + this->parse_rt_config_file (); + + // Initiate connections with the peers. + this->initiate_connections (); + + // If this->performance_window_ > 0 start a timer. + + if (this->performance_window_ > 0) + { + if (ACE_Service_Config::reactor ()->schedule_timer (this, 0, + this->performance_window_) == -1) + ACE_ERROR ((LM_ERROR, "(%t) %p\n", "schedule_timer")); + else + ACE_DEBUG ((LM_DEBUG, "starting timer for %d seconds...\n", + this->performance_window_)); + } + + return 0; +} + +// Returns information on the currently active service. + +template <class IC, class OC> int +Gateway<IC, OC>::info (char **strp, size_t length) const +{ + char buf[BUFSIZ]; + + ACE_OS::sprintf (buf, "%s\t %s", "Gateway daemon", + "# Application-level gateway\n"); + + if (*strp == 0 && (*strp = ACE_OS::strdup (buf)) == 0) + return -1; + else + ACE_OS::strncpy (*strp, buf, length); + return ACE_OS::strlen (buf); +} + +// Parse and build the connection table. + +template <class IC, class OC> int +Gateway<IC, OC>::parse_cc_config_file (void) +{ + // File that contains the routing table configuration information. + CC_Config_File_Parser cc_file; + CC_Config_File_Entry entry; + int file_empty = 1; + int line_number = 0; + + if (cc_file.open (this->cc_config_file_) == -1) + ACE_ERROR_RETURN ((LM_ERROR, "(%t) %p\n", this->cc_config_file_), -1); + + // Read config file line at a time. + while (cc_file.read_entry (entry, line_number) != FP::EOFILE) + { + file_empty = 0; + + if (this->debug_) + ACE_DEBUG ((LM_DEBUG, "(%t) conn id = %d, host = %s, remote port = %d, " + "direction = %c, max retry timeout = %d, local port = %d\n", + entry.conn_id_, entry.host_, entry.remote_port_, entry.direction_, + entry.max_retry_delay_, entry.local_port_)); + + Channel *channel = 0; + + // The next few lines of code are dependent on whether we are making + // an Input_Channel or an Output_Channel. + + if (entry.direction_ == 'O') // Configure an output channel. + ACE_NEW_RETURN (channel, + OUTPUT_CHANNEL (&this->routing_table_, + this->connector_, + this->output_thr_mgr_, + this->socket_queue_size_), + -1); + else /* direction == 'I' */ // Configure an input channel. + ACE_NEW_RETURN (channel, + INPUT_CHANNEL (&this->routing_table_, + this->connector_, + this->input_thr_mgr_, + this->socket_queue_size_), + -1); + if (channel == 0) + ACE_ERROR_RETURN ((LM_ERROR, "(%t) out of memory\n"), -1); + + // The following code is common to both Input_ and Output_Channels. + + // Initialize the routing entry's peer addressing info. + channel->bind (ACE_INET_Addr (entry.remote_port_, entry.host_), + ACE_INET_Addr (entry.local_port_), entry.conn_id_); + + // Initialize max timeout. + channel->max_timeout (entry.max_retry_delay_); + + // Try to bind the new Channel to the connection ID. + switch (this->config_table_.bind (entry.conn_id_, channel)) + { + case -1: + ACE_ERROR_RETURN ((LM_ERROR, "(%t) bind failed for connection %d\n", + entry.conn_id_), -1); + /* NOTREACHED */ + case 1: // Oops, found a duplicate! + ACE_DEBUG ((LM_DEBUG, "(%t) duplicate connection %d, already bound\n", + entry.conn_id_)); + break; + case 0: + // Success. + break; + } + } + + if (file_empty) + ACE_ERROR ((LM_WARNING, + "warning: connection channel configuration file was empty\n")); + + return 0; +} + +template <class IC, class OC> int +Gateway<IC, OC>::parse_rt_config_file (void) +{ + // File that contains the routing table configuration information. + RT_Config_File_Parser rt_file; + RT_Config_File_Entry entry; + int file_empty = 1; + int line_number = 0; + + if (rt_file.open (this->rt_config_file_) == -1) + ACE_ERROR_RETURN ((LM_ERROR, "(%t) %p\n", this->rt_config_file_), -1); + + // Read config file line at a time. + while (rt_file.read_entry (entry, line_number) != FP::EOFILE) + { + file_empty = 0; + + if (this->debug_) + { + ACE_DEBUG ((LM_DEBUG, "(%t) conn id = %d, logical id = %d, payload = %d, " + "number of destinations = %d\n", + entry.conn_id_, entry.logical_id_, entry.payload_type_, + entry.total_destinations_)); + for (int i = 0; i < entry.total_destinations_; i++) + ACE_DEBUG ((LM_DEBUG, "(%t) destination[%d] = %d\n", + i, entry.destinations_[i])); + } + + Routing_Entry *re; + ACE_NEW_RETURN (re, Routing_Entry, -1); + Routing_Entry::ENTRY_SET *channel_set = new Routing_Entry::ENTRY_SET; + Peer_Addr peer_addr (entry.conn_id_, entry.logical_id_, + entry.payload_type_); + + // Add the destinations to the Routing Entry. + for (int i = 0; i < entry.total_destinations_; i++) + { + Channel *channel = 0; + + // Lookup destination and add to Routing_Entry set if found. + if (this->config_table_.find (entry.destinations_[i], + channel) != -1) + channel_set->insert (channel); + else + ACE_ERROR ((LM_ERROR, "(%t) not found: destination[%d] = %d\n", + i, entry.destinations_[i])); + } + + // Attach set of destination channels to routing entry. + re->destinations (channel_set); + + // Bind with routing table, keyed by peer address. + switch (this->routing_table_.bind (peer_addr, re)) + { + case -1: + ACE_ERROR_RETURN ((LM_ERROR, "(%t) bind failed for connection %d\n", + entry.conn_id_), -1); + /* NOTREACHED */ + case 1: // Oops, found a duplicate! + ACE_DEBUG ((LM_DEBUG, "(%t) duplicate routing table entry %d, " + "already bound\n", entry.conn_id_)); + break; + case 0: + // Success. + break; + } + } + + if (file_empty) + ACE_ERROR ((LM_WARNING, + "warning: routing table configuration file was empty\n")); + + return 0; +} + +#if defined (ACE_HAS_THREADS) && (defined (USE_OUTPUT_MT) || defined (USE_INPUT_MT)) +#if defined (USE_OUTPUT_MT) +typedef Thr_Output_Channel OUTPUT_CHANNEL; +#else +typedef Output_Channel OUTPUT_CHANNEL; +#endif /* USE_OUTPUT_MT */ + +#if defined (USE_INPUT_MT) +typedef Thr_Input_Channel INPUT_CHANNEL; +#else +typedef Input_Channel INPUT_CHANNEL; +#endif /* USE_INPUT_MT */ +#else +// Instantiate a non-multi-threaded Gateway. +typedef Input_Channel INPUT_CHANNEL; +typedef Output_Channel OUTPUT_CHANNEL; +#endif /* ACE_HAS_THREADS */ + +typedef Gateway<INPUT_CHANNEL, OUTPUT_CHANNEL> ACE_Gateway; + +// The following is a "Factory" used by the ACE_Service_Config and +// svc.conf file to dynamically initialize the state of the Gateway. + +ACE_SVC_FACTORY_DEFINE (ACE_Gateway) diff --git a/apps/Gateway/Gateway/Gateway.h b/apps/Gateway/Gateway/Gateway.h new file mode 100644 index 00000000000..b4269aa9d80 --- /dev/null +++ b/apps/Gateway/Gateway/Gateway.h @@ -0,0 +1,30 @@ +/* -*- C++ -*- */ +// @(#)Gateway.h 1.1 10/18/96 + + +// ============================================================================ +// +// = LIBRARY +// apps +// +// = FILENAME +// Gateway.h +// +// = AUTHOR +// Doug Schmidt +// +// ============================================================================ + +#if !defined (ACE_GATEWAY) +#define ACE_GATEWAY + +#include "ace/OS.h" + +ACE_SVC_FACTORY_DECLARE (ACE_Gateway) + +#endif /* ACE_GATEWAY */ + + + + + diff --git a/apps/Gateway/Gateway/Makefile b/apps/Gateway/Gateway/Makefile new file mode 100644 index 00000000000..768f774d9c4 --- /dev/null +++ b/apps/Gateway/Gateway/Makefile @@ -0,0 +1,505 @@ +#---------------------------------------------------------------------------- +# @(#)Makefile 1.1 10/18/96 +# +# Makefile for the Gateway prototype. +#---------------------------------------------------------------------------- + +#---------------------------------------------------------------------------- +# Local macros +#---------------------------------------------------------------------------- + +BIN = gatewayd +LIB = libGateway.a +SHLIB = libGateway.so + +FILES = Channel \ + Channel_Connector \ + Config_Files \ + File_Parser \ + Gateway \ + Routing_Entry \ + Routing_Table \ + Thr_Channel + +LSRC = $(addsuffix .cpp,$(FILES)) +LOBJ = $(addsuffix .o,$(FILES)) +SHOBJ = $(addsuffix .so,$(FILES)) + +LDLIBS = -lGateway +LIBS = -lACE + +VLDLIBS = $(LDLIBS:%=%$(VAR)) + +BUILD = $(VLIB) $(VSHLIB) $(SHLIBA) $(VBIN) + +#---------------------------------------------------------------------------- +# Include macros and targets +#---------------------------------------------------------------------------- + +include $(WRAPPER_ROOT)/include/makeinclude/wrapper_macros.GNU +include $(WRAPPER_ROOT)/include/makeinclude/macros.GNU +include $(WRAPPER_ROOT)/include/makeinclude/rules.common.GNU +include $(WRAPPER_ROOT)/include/makeinclude/rules.nonested.GNU +include $(WRAPPER_ROOT)/include/makeinclude/rules.lib.GNU +include $(WRAPPER_ROOT)/include/makeinclude/rules.bin.GNU +include $(WRAPPER_ROOT)/include/makeinclude/rules.local.GNU + +#---------------------------------------------------------------------------- +# Local targets +#---------------------------------------------------------------------------- + +# Default behavior is to use single-threading. See the README +# file for information on how to configure this with multiple +# strategies for threading the input and output channels. +DEFFLAGS += -DASSIGN_ROUTING_ID # -DUSE_OUTPUT_MT -DUSE_INPUT_MT + +#---------------------------------------------------------------------------- +# Dependencies +#---------------------------------------------------------------------------- + +# DO NOT DELETE THIS LINE -- g++dep uses it. +# DO NOT PUT ANYTHING AFTER THIS LINE, IT WILL GO AWAY. + +.obj/Channel.o .shobj/Channel.so: Channel.cpp \ + $(WRAPPER_ROOT)/ace/Log_Msg.h \ + $(WRAPPER_ROOT)/ace/Log_Record.h \ + $(WRAPPER_ROOT)/ace/ACE.h \ + $(WRAPPER_ROOT)/ace/OS.h \ + $(WRAPPER_ROOT)/ace/Time_Value.h \ + $(WRAPPER_ROOT)/ace/config.h \ + $(WRAPPER_ROOT)/ace/Trace.h \ + $(WRAPPER_ROOT)/ace/ACE.i \ + $(WRAPPER_ROOT)/ace/Log_Priority.h \ + $(WRAPPER_ROOT)/ace/Log_Record.i \ + Routing_Entry.h \ + $(WRAPPER_ROOT)/ace/Set.h \ + $(WRAPPER_ROOT)/ace/Set.cpp \ + $(WRAPPER_ROOT)/ace/Set.i \ + Channel_Connector.h \ + $(WRAPPER_ROOT)/ace/Connector.h \ + $(WRAPPER_ROOT)/ace/Service_Config.h \ + $(WRAPPER_ROOT)/ace/Service_Object.h \ + $(WRAPPER_ROOT)/ace/Shared_Object.h \ + $(WRAPPER_ROOT)/ace/Event_Handler.h \ + $(WRAPPER_ROOT)/ace/Thread_Manager.h \ + $(WRAPPER_ROOT)/ace/Thread.h \ + $(WRAPPER_ROOT)/ace/Synch.h \ + $(WRAPPER_ROOT)/ace/SV_Semaphore_Complex.h \ + $(WRAPPER_ROOT)/ace/SV_Semaphore_Simple.h \ + $(WRAPPER_ROOT)/ace/SV_Semaphore_Simple.i \ + $(WRAPPER_ROOT)/ace/SV_Semaphore_Complex.i \ + $(WRAPPER_ROOT)/ace/Synch_T.h \ + $(WRAPPER_ROOT)/ace/Synch_T.cpp \ + $(WRAPPER_ROOT)/ace/Synch_T.i \ + $(WRAPPER_ROOT)/ace/Proactor.h \ + $(WRAPPER_ROOT)/ace/Message_Block.h \ + $(WRAPPER_ROOT)/ace/Malloc.h \ + $(WRAPPER_ROOT)/ace/Malloc_T.h \ + $(WRAPPER_ROOT)/ace/Malloc_T.cpp \ + $(WRAPPER_ROOT)/ace/Malloc_T.i \ + $(WRAPPER_ROOT)/ace/Memory_Pool.h \ + $(WRAPPER_ROOT)/ace/Signal.h \ + $(WRAPPER_ROOT)/ace/Mem_Map.h \ + $(WRAPPER_ROOT)/ace/Timer_Queue.h \ + $(WRAPPER_ROOT)/ace/Timer_Queue.i \ + $(WRAPPER_ROOT)/ace/ReactorEx.h \ + $(WRAPPER_ROOT)/ace/Token.h \ + $(WRAPPER_ROOT)/ace/Reactor.h \ + $(WRAPPER_ROOT)/ace/Handle_Set.h \ + $(WRAPPER_ROOT)/ace/Pipe.h \ + $(WRAPPER_ROOT)/ace/Pipe.i \ + $(WRAPPER_ROOT)/ace/SOCK_Stream.h \ + $(WRAPPER_ROOT)/ace/SOCK_IO.h \ + $(WRAPPER_ROOT)/ace/SOCK.h \ + $(WRAPPER_ROOT)/ace/Addr.h \ + $(WRAPPER_ROOT)/ace/IPC_SAP.h \ + $(WRAPPER_ROOT)/ace/IPC_SAP.i \ + $(WRAPPER_ROOT)/ace/SOCK.i \ + $(WRAPPER_ROOT)/ace/SOCK_IO.i \ + $(WRAPPER_ROOT)/ace/INET_Addr.h \ + $(WRAPPER_ROOT)/ace/SOCK_Stream.i \ + $(WRAPPER_ROOT)/ace/Reactor.i \ + $(WRAPPER_ROOT)/ace/Svc_Conf_Tokens.h \ + $(WRAPPER_ROOT)/ace/Map_Manager.h \ + $(WRAPPER_ROOT)/ace/Map_Manager.cpp \ + $(WRAPPER_ROOT)/ace/Map_Manager.i \ + $(WRAPPER_ROOT)/ace/Svc_Handler.h \ + $(WRAPPER_ROOT)/ace/Synch_Options.h \ + $(WRAPPER_ROOT)/ace/Task.h \ + $(WRAPPER_ROOT)/ace/Message_Queue.h \ + $(WRAPPER_ROOT)/ace/IO_Cntl_Msg.h \ + $(WRAPPER_ROOT)/ace/Message_Queue.cpp \ + $(WRAPPER_ROOT)/ace/Message_Queue.i \ + $(WRAPPER_ROOT)/ace/Task.cpp \ + $(WRAPPER_ROOT)/ace/Module.h \ + $(WRAPPER_ROOT)/ace/Module.cpp \ + $(WRAPPER_ROOT)/ace/Stream_Modules.h \ + $(WRAPPER_ROOT)/ace/Stream_Modules.cpp \ + $(WRAPPER_ROOT)/ace/Stream_Modules.i \ + $(WRAPPER_ROOT)/ace/Module.i \ + $(WRAPPER_ROOT)/ace/Task.i \ + $(WRAPPER_ROOT)/ace/Svc_Handler.cpp \ + $(WRAPPER_ROOT)/ace/Dynamic.h \ + $(WRAPPER_ROOT)/ace/Svc_Handler.i \ + $(WRAPPER_ROOT)/ace/Strategies.h \ + $(WRAPPER_ROOT)/ace/Strategies.cpp \ + $(WRAPPER_ROOT)/ace/Connector.i \ + $(WRAPPER_ROOT)/ace/Connector.cpp \ + Thr_Channel.h Channel.h \ + $(WRAPPER_ROOT)/ace/SOCK_Connector.h \ + $(WRAPPER_ROOT)/ace/SOCK_Connector.i \ + Routing_Table.h Routing_Table.cpp Peer_Message.h +.obj/Channel_Connector.o .shobj/Channel_Connector.so: Channel_Connector.cpp Channel_Connector.h \ + $(WRAPPER_ROOT)/ace/Connector.h \ + $(WRAPPER_ROOT)/ace/Service_Config.h \ + $(WRAPPER_ROOT)/ace/Service_Object.h \ + $(WRAPPER_ROOT)/ace/Shared_Object.h \ + $(WRAPPER_ROOT)/ace/ACE.h \ + $(WRAPPER_ROOT)/ace/OS.h \ + $(WRAPPER_ROOT)/ace/Time_Value.h \ + $(WRAPPER_ROOT)/ace/config.h \ + $(WRAPPER_ROOT)/ace/Trace.h \ + $(WRAPPER_ROOT)/ace/ACE.i \ + $(WRAPPER_ROOT)/ace/Log_Msg.h \ + $(WRAPPER_ROOT)/ace/Log_Record.h \ + $(WRAPPER_ROOT)/ace/Log_Priority.h \ + $(WRAPPER_ROOT)/ace/Log_Record.i \ + $(WRAPPER_ROOT)/ace/Event_Handler.h \ + $(WRAPPER_ROOT)/ace/Thread_Manager.h \ + $(WRAPPER_ROOT)/ace/Thread.h \ + $(WRAPPER_ROOT)/ace/Synch.h \ + $(WRAPPER_ROOT)/ace/SV_Semaphore_Complex.h \ + $(WRAPPER_ROOT)/ace/SV_Semaphore_Simple.h \ + $(WRAPPER_ROOT)/ace/SV_Semaphore_Simple.i \ + $(WRAPPER_ROOT)/ace/SV_Semaphore_Complex.i \ + $(WRAPPER_ROOT)/ace/Synch_T.h \ + $(WRAPPER_ROOT)/ace/Synch_T.cpp \ + $(WRAPPER_ROOT)/ace/Synch_T.i \ + $(WRAPPER_ROOT)/ace/Set.h \ + $(WRAPPER_ROOT)/ace/Set.cpp \ + $(WRAPPER_ROOT)/ace/Set.i \ + $(WRAPPER_ROOT)/ace/Proactor.h \ + $(WRAPPER_ROOT)/ace/Message_Block.h \ + $(WRAPPER_ROOT)/ace/Malloc.h \ + $(WRAPPER_ROOT)/ace/Malloc_T.h \ + $(WRAPPER_ROOT)/ace/Malloc_T.cpp \ + $(WRAPPER_ROOT)/ace/Malloc_T.i \ + $(WRAPPER_ROOT)/ace/Memory_Pool.h \ + $(WRAPPER_ROOT)/ace/Signal.h \ + $(WRAPPER_ROOT)/ace/Mem_Map.h \ + $(WRAPPER_ROOT)/ace/Timer_Queue.h \ + $(WRAPPER_ROOT)/ace/Timer_Queue.i \ + $(WRAPPER_ROOT)/ace/ReactorEx.h \ + $(WRAPPER_ROOT)/ace/Token.h \ + $(WRAPPER_ROOT)/ace/Reactor.h \ + $(WRAPPER_ROOT)/ace/Handle_Set.h \ + $(WRAPPER_ROOT)/ace/Pipe.h \ + $(WRAPPER_ROOT)/ace/Pipe.i \ + $(WRAPPER_ROOT)/ace/SOCK_Stream.h \ + $(WRAPPER_ROOT)/ace/SOCK_IO.h \ + $(WRAPPER_ROOT)/ace/SOCK.h \ + $(WRAPPER_ROOT)/ace/Addr.h \ + $(WRAPPER_ROOT)/ace/IPC_SAP.h \ + $(WRAPPER_ROOT)/ace/IPC_SAP.i \ + $(WRAPPER_ROOT)/ace/SOCK.i \ + $(WRAPPER_ROOT)/ace/SOCK_IO.i \ + $(WRAPPER_ROOT)/ace/INET_Addr.h \ + $(WRAPPER_ROOT)/ace/SOCK_Stream.i \ + $(WRAPPER_ROOT)/ace/Reactor.i \ + $(WRAPPER_ROOT)/ace/Svc_Conf_Tokens.h \ + $(WRAPPER_ROOT)/ace/Map_Manager.h \ + $(WRAPPER_ROOT)/ace/Map_Manager.cpp \ + $(WRAPPER_ROOT)/ace/Map_Manager.i \ + $(WRAPPER_ROOT)/ace/Svc_Handler.h \ + $(WRAPPER_ROOT)/ace/Synch_Options.h \ + $(WRAPPER_ROOT)/ace/Task.h \ + $(WRAPPER_ROOT)/ace/Message_Queue.h \ + $(WRAPPER_ROOT)/ace/IO_Cntl_Msg.h \ + $(WRAPPER_ROOT)/ace/Message_Queue.cpp \ + $(WRAPPER_ROOT)/ace/Message_Queue.i \ + $(WRAPPER_ROOT)/ace/Task.cpp \ + $(WRAPPER_ROOT)/ace/Module.h \ + $(WRAPPER_ROOT)/ace/Module.cpp \ + $(WRAPPER_ROOT)/ace/Stream_Modules.h \ + $(WRAPPER_ROOT)/ace/Stream_Modules.cpp \ + $(WRAPPER_ROOT)/ace/Stream_Modules.i \ + $(WRAPPER_ROOT)/ace/Module.i \ + $(WRAPPER_ROOT)/ace/Task.i \ + $(WRAPPER_ROOT)/ace/Svc_Handler.cpp \ + $(WRAPPER_ROOT)/ace/Dynamic.h \ + $(WRAPPER_ROOT)/ace/Svc_Handler.i \ + $(WRAPPER_ROOT)/ace/Strategies.h \ + $(WRAPPER_ROOT)/ace/Strategies.cpp \ + $(WRAPPER_ROOT)/ace/Connector.i \ + $(WRAPPER_ROOT)/ace/Connector.cpp \ + Thr_Channel.h Channel.h \ + $(WRAPPER_ROOT)/ace/SOCK_Connector.h \ + $(WRAPPER_ROOT)/ace/SOCK_Connector.i \ + Routing_Table.h Routing_Table.cpp Routing_Entry.h Peer_Message.h +.obj/Config_Files.o .shobj/Config_Files.so: Config_Files.cpp \ + $(WRAPPER_ROOT)/ace/OS.h \ + $(WRAPPER_ROOT)/ace/Time_Value.h \ + $(WRAPPER_ROOT)/ace/config.h \ + $(WRAPPER_ROOT)/ace/Trace.h \ + Config_Files.h File_Parser.h File_Parser.cpp +.obj/File_Parser.o .shobj/File_Parser.so: File_Parser.cpp \ + $(WRAPPER_ROOT)/ace/OS.h \ + $(WRAPPER_ROOT)/ace/Time_Value.h \ + $(WRAPPER_ROOT)/ace/config.h \ + $(WRAPPER_ROOT)/ace/Trace.h \ + File_Parser.h File_Parser.cpp +.obj/Gateway.o .shobj/Gateway.so: Gateway.cpp \ + $(WRAPPER_ROOT)/ace/Log_Msg.h \ + $(WRAPPER_ROOT)/ace/Log_Record.h \ + $(WRAPPER_ROOT)/ace/ACE.h \ + $(WRAPPER_ROOT)/ace/OS.h \ + $(WRAPPER_ROOT)/ace/Time_Value.h \ + $(WRAPPER_ROOT)/ace/config.h \ + $(WRAPPER_ROOT)/ace/Trace.h \ + $(WRAPPER_ROOT)/ace/ACE.i \ + $(WRAPPER_ROOT)/ace/Log_Priority.h \ + $(WRAPPER_ROOT)/ace/Log_Record.i \ + $(WRAPPER_ROOT)/ace/Get_Opt.h \ + $(WRAPPER_ROOT)/ace/Service_Config.h \ + $(WRAPPER_ROOT)/ace/Service_Object.h \ + $(WRAPPER_ROOT)/ace/Shared_Object.h \ + $(WRAPPER_ROOT)/ace/Event_Handler.h \ + $(WRAPPER_ROOT)/ace/Thread_Manager.h \ + $(WRAPPER_ROOT)/ace/Thread.h \ + $(WRAPPER_ROOT)/ace/Synch.h \ + $(WRAPPER_ROOT)/ace/SV_Semaphore_Complex.h \ + $(WRAPPER_ROOT)/ace/SV_Semaphore_Simple.h \ + $(WRAPPER_ROOT)/ace/SV_Semaphore_Simple.i \ + $(WRAPPER_ROOT)/ace/SV_Semaphore_Complex.i \ + $(WRAPPER_ROOT)/ace/Synch_T.h \ + $(WRAPPER_ROOT)/ace/Synch_T.cpp \ + $(WRAPPER_ROOT)/ace/Synch_T.i \ + $(WRAPPER_ROOT)/ace/Set.h \ + $(WRAPPER_ROOT)/ace/Set.cpp \ + $(WRAPPER_ROOT)/ace/Set.i \ + $(WRAPPER_ROOT)/ace/Proactor.h \ + $(WRAPPER_ROOT)/ace/Message_Block.h \ + $(WRAPPER_ROOT)/ace/Malloc.h \ + $(WRAPPER_ROOT)/ace/Malloc_T.h \ + $(WRAPPER_ROOT)/ace/Malloc_T.cpp \ + $(WRAPPER_ROOT)/ace/Malloc_T.i \ + $(WRAPPER_ROOT)/ace/Memory_Pool.h \ + $(WRAPPER_ROOT)/ace/Signal.h \ + $(WRAPPER_ROOT)/ace/Mem_Map.h \ + $(WRAPPER_ROOT)/ace/Timer_Queue.h \ + $(WRAPPER_ROOT)/ace/Timer_Queue.i \ + $(WRAPPER_ROOT)/ace/ReactorEx.h \ + $(WRAPPER_ROOT)/ace/Token.h \ + $(WRAPPER_ROOT)/ace/Reactor.h \ + $(WRAPPER_ROOT)/ace/Handle_Set.h \ + $(WRAPPER_ROOT)/ace/Pipe.h \ + $(WRAPPER_ROOT)/ace/Pipe.i \ + $(WRAPPER_ROOT)/ace/SOCK_Stream.h \ + $(WRAPPER_ROOT)/ace/SOCK_IO.h \ + $(WRAPPER_ROOT)/ace/SOCK.h \ + $(WRAPPER_ROOT)/ace/Addr.h \ + $(WRAPPER_ROOT)/ace/IPC_SAP.h \ + $(WRAPPER_ROOT)/ace/IPC_SAP.i \ + $(WRAPPER_ROOT)/ace/SOCK.i \ + $(WRAPPER_ROOT)/ace/SOCK_IO.i \ + $(WRAPPER_ROOT)/ace/INET_Addr.h \ + $(WRAPPER_ROOT)/ace/SOCK_Stream.i \ + $(WRAPPER_ROOT)/ace/Reactor.i \ + $(WRAPPER_ROOT)/ace/Svc_Conf_Tokens.h \ + Config_Files.h File_Parser.h File_Parser.cpp Gateway.h \ + Channel_Connector.h \ + $(WRAPPER_ROOT)/ace/Connector.h \ + $(WRAPPER_ROOT)/ace/Map_Manager.h \ + $(WRAPPER_ROOT)/ace/Map_Manager.cpp \ + $(WRAPPER_ROOT)/ace/Map_Manager.i \ + $(WRAPPER_ROOT)/ace/Svc_Handler.h \ + $(WRAPPER_ROOT)/ace/Synch_Options.h \ + $(WRAPPER_ROOT)/ace/Task.h \ + $(WRAPPER_ROOT)/ace/Message_Queue.h \ + $(WRAPPER_ROOT)/ace/IO_Cntl_Msg.h \ + $(WRAPPER_ROOT)/ace/Message_Queue.cpp \ + $(WRAPPER_ROOT)/ace/Message_Queue.i \ + $(WRAPPER_ROOT)/ace/Task.cpp \ + $(WRAPPER_ROOT)/ace/Module.h \ + $(WRAPPER_ROOT)/ace/Module.cpp \ + $(WRAPPER_ROOT)/ace/Stream_Modules.h \ + $(WRAPPER_ROOT)/ace/Stream_Modules.cpp \ + $(WRAPPER_ROOT)/ace/Stream_Modules.i \ + $(WRAPPER_ROOT)/ace/Module.i \ + $(WRAPPER_ROOT)/ace/Task.i \ + $(WRAPPER_ROOT)/ace/Svc_Handler.cpp \ + $(WRAPPER_ROOT)/ace/Dynamic.h \ + $(WRAPPER_ROOT)/ace/Svc_Handler.i \ + $(WRAPPER_ROOT)/ace/Strategies.h \ + $(WRAPPER_ROOT)/ace/Strategies.cpp \ + $(WRAPPER_ROOT)/ace/Connector.i \ + $(WRAPPER_ROOT)/ace/Connector.cpp \ + Thr_Channel.h Channel.h \ + $(WRAPPER_ROOT)/ace/SOCK_Connector.h \ + $(WRAPPER_ROOT)/ace/SOCK_Connector.i \ + Routing_Table.h Routing_Table.cpp Routing_Entry.h Peer_Message.h +.obj/Routing_Entry.o .shobj/Routing_Entry.so: Routing_Entry.cpp Routing_Entry.h \ + $(WRAPPER_ROOT)/ace/Set.h \ + $(WRAPPER_ROOT)/ace/ACE.h \ + $(WRAPPER_ROOT)/ace/OS.h \ + $(WRAPPER_ROOT)/ace/Time_Value.h \ + $(WRAPPER_ROOT)/ace/config.h \ + $(WRAPPER_ROOT)/ace/Trace.h \ + $(WRAPPER_ROOT)/ace/ACE.i \ + $(WRAPPER_ROOT)/ace/Log_Msg.h \ + $(WRAPPER_ROOT)/ace/Log_Record.h \ + $(WRAPPER_ROOT)/ace/Log_Priority.h \ + $(WRAPPER_ROOT)/ace/Log_Record.i \ + $(WRAPPER_ROOT)/ace/Set.cpp \ + $(WRAPPER_ROOT)/ace/Set.i +.obj/Routing_Table.o .shobj/Routing_Table.so: Routing_Table.cpp \ + $(WRAPPER_ROOT)/ace/Log_Msg.h \ + $(WRAPPER_ROOT)/ace/Log_Record.h \ + $(WRAPPER_ROOT)/ace/ACE.h \ + $(WRAPPER_ROOT)/ace/OS.h \ + $(WRAPPER_ROOT)/ace/Time_Value.h \ + $(WRAPPER_ROOT)/ace/config.h \ + $(WRAPPER_ROOT)/ace/Trace.h \ + $(WRAPPER_ROOT)/ace/ACE.i \ + $(WRAPPER_ROOT)/ace/Log_Priority.h \ + $(WRAPPER_ROOT)/ace/Log_Record.i \ + Routing_Table.h \ + $(WRAPPER_ROOT)/ace/Map_Manager.h \ + $(WRAPPER_ROOT)/ace/Map_Manager.cpp \ + $(WRAPPER_ROOT)/ace/Synch.h \ + $(WRAPPER_ROOT)/ace/SV_Semaphore_Complex.h \ + $(WRAPPER_ROOT)/ace/SV_Semaphore_Simple.h \ + $(WRAPPER_ROOT)/ace/SV_Semaphore_Simple.i \ + $(WRAPPER_ROOT)/ace/SV_Semaphore_Complex.i \ + $(WRAPPER_ROOT)/ace/Synch_T.h \ + $(WRAPPER_ROOT)/ace/Synch_T.cpp \ + $(WRAPPER_ROOT)/ace/Thread.h \ + $(WRAPPER_ROOT)/ace/Synch_T.i \ + $(WRAPPER_ROOT)/ace/Malloc.h \ + $(WRAPPER_ROOT)/ace/Malloc_T.h \ + $(WRAPPER_ROOT)/ace/Malloc_T.cpp \ + $(WRAPPER_ROOT)/ace/Malloc_T.i \ + $(WRAPPER_ROOT)/ace/Memory_Pool.h \ + $(WRAPPER_ROOT)/ace/Event_Handler.h \ + $(WRAPPER_ROOT)/ace/Signal.h \ + $(WRAPPER_ROOT)/ace/Set.h \ + $(WRAPPER_ROOT)/ace/Set.cpp \ + $(WRAPPER_ROOT)/ace/Set.i \ + $(WRAPPER_ROOT)/ace/Mem_Map.h \ + $(WRAPPER_ROOT)/ace/Service_Config.h \ + $(WRAPPER_ROOT)/ace/Service_Object.h \ + $(WRAPPER_ROOT)/ace/Shared_Object.h \ + $(WRAPPER_ROOT)/ace/Thread_Manager.h \ + $(WRAPPER_ROOT)/ace/Proactor.h \ + $(WRAPPER_ROOT)/ace/Message_Block.h \ + $(WRAPPER_ROOT)/ace/Timer_Queue.h \ + $(WRAPPER_ROOT)/ace/Timer_Queue.i \ + $(WRAPPER_ROOT)/ace/ReactorEx.h \ + $(WRAPPER_ROOT)/ace/Token.h \ + $(WRAPPER_ROOT)/ace/Reactor.h \ + $(WRAPPER_ROOT)/ace/Handle_Set.h \ + $(WRAPPER_ROOT)/ace/Pipe.h \ + $(WRAPPER_ROOT)/ace/Pipe.i \ + $(WRAPPER_ROOT)/ace/SOCK_Stream.h \ + $(WRAPPER_ROOT)/ace/SOCK_IO.h \ + $(WRAPPER_ROOT)/ace/SOCK.h \ + $(WRAPPER_ROOT)/ace/Addr.h \ + $(WRAPPER_ROOT)/ace/IPC_SAP.h \ + $(WRAPPER_ROOT)/ace/IPC_SAP.i \ + $(WRAPPER_ROOT)/ace/SOCK.i \ + $(WRAPPER_ROOT)/ace/SOCK_IO.i \ + $(WRAPPER_ROOT)/ace/INET_Addr.h \ + $(WRAPPER_ROOT)/ace/SOCK_Stream.i \ + $(WRAPPER_ROOT)/ace/Reactor.i \ + $(WRAPPER_ROOT)/ace/Svc_Conf_Tokens.h \ + $(WRAPPER_ROOT)/ace/Map_Manager.i \ + Routing_Table.cpp +.obj/Thr_Channel.o .shobj/Thr_Channel.so: Thr_Channel.cpp Thr_Channel.h Channel.h \ + $(WRAPPER_ROOT)/ace/Service_Config.h \ + $(WRAPPER_ROOT)/ace/Service_Object.h \ + $(WRAPPER_ROOT)/ace/Shared_Object.h \ + $(WRAPPER_ROOT)/ace/ACE.h \ + $(WRAPPER_ROOT)/ace/OS.h \ + $(WRAPPER_ROOT)/ace/Time_Value.h \ + $(WRAPPER_ROOT)/ace/config.h \ + $(WRAPPER_ROOT)/ace/Trace.h \ + $(WRAPPER_ROOT)/ace/ACE.i \ + $(WRAPPER_ROOT)/ace/Log_Msg.h \ + $(WRAPPER_ROOT)/ace/Log_Record.h \ + $(WRAPPER_ROOT)/ace/Log_Priority.h \ + $(WRAPPER_ROOT)/ace/Log_Record.i \ + $(WRAPPER_ROOT)/ace/Event_Handler.h \ + $(WRAPPER_ROOT)/ace/Thread_Manager.h \ + $(WRAPPER_ROOT)/ace/Thread.h \ + $(WRAPPER_ROOT)/ace/Synch.h \ + $(WRAPPER_ROOT)/ace/SV_Semaphore_Complex.h \ + $(WRAPPER_ROOT)/ace/SV_Semaphore_Simple.h \ + $(WRAPPER_ROOT)/ace/SV_Semaphore_Simple.i \ + $(WRAPPER_ROOT)/ace/SV_Semaphore_Complex.i \ + $(WRAPPER_ROOT)/ace/Synch_T.h \ + $(WRAPPER_ROOT)/ace/Synch_T.cpp \ + $(WRAPPER_ROOT)/ace/Synch_T.i \ + $(WRAPPER_ROOT)/ace/Set.h \ + $(WRAPPER_ROOT)/ace/Set.cpp \ + $(WRAPPER_ROOT)/ace/Set.i \ + $(WRAPPER_ROOT)/ace/Proactor.h \ + $(WRAPPER_ROOT)/ace/Message_Block.h \ + $(WRAPPER_ROOT)/ace/Malloc.h \ + $(WRAPPER_ROOT)/ace/Malloc_T.h \ + $(WRAPPER_ROOT)/ace/Malloc_T.cpp \ + $(WRAPPER_ROOT)/ace/Malloc_T.i \ + $(WRAPPER_ROOT)/ace/Memory_Pool.h \ + $(WRAPPER_ROOT)/ace/Signal.h \ + $(WRAPPER_ROOT)/ace/Mem_Map.h \ + $(WRAPPER_ROOT)/ace/Timer_Queue.h \ + $(WRAPPER_ROOT)/ace/Timer_Queue.i \ + $(WRAPPER_ROOT)/ace/ReactorEx.h \ + $(WRAPPER_ROOT)/ace/Token.h \ + $(WRAPPER_ROOT)/ace/Reactor.h \ + $(WRAPPER_ROOT)/ace/Handle_Set.h \ + $(WRAPPER_ROOT)/ace/Pipe.h \ + $(WRAPPER_ROOT)/ace/Pipe.i \ + $(WRAPPER_ROOT)/ace/SOCK_Stream.h \ + $(WRAPPER_ROOT)/ace/SOCK_IO.h \ + $(WRAPPER_ROOT)/ace/SOCK.h \ + $(WRAPPER_ROOT)/ace/Addr.h \ + $(WRAPPER_ROOT)/ace/IPC_SAP.h \ + $(WRAPPER_ROOT)/ace/IPC_SAP.i \ + $(WRAPPER_ROOT)/ace/SOCK.i \ + $(WRAPPER_ROOT)/ace/SOCK_IO.i \ + $(WRAPPER_ROOT)/ace/INET_Addr.h \ + $(WRAPPER_ROOT)/ace/SOCK_Stream.i \ + $(WRAPPER_ROOT)/ace/Reactor.i \ + $(WRAPPER_ROOT)/ace/Svc_Conf_Tokens.h \ + $(WRAPPER_ROOT)/ace/SOCK_Connector.h \ + $(WRAPPER_ROOT)/ace/SOCK_Connector.i \ + $(WRAPPER_ROOT)/ace/Svc_Handler.h \ + $(WRAPPER_ROOT)/ace/Synch_Options.h \ + $(WRAPPER_ROOT)/ace/Task.h \ + $(WRAPPER_ROOT)/ace/Message_Queue.h \ + $(WRAPPER_ROOT)/ace/IO_Cntl_Msg.h \ + $(WRAPPER_ROOT)/ace/Message_Queue.cpp \ + $(WRAPPER_ROOT)/ace/Message_Queue.i \ + $(WRAPPER_ROOT)/ace/Task.cpp \ + $(WRAPPER_ROOT)/ace/Module.h \ + $(WRAPPER_ROOT)/ace/Module.cpp \ + $(WRAPPER_ROOT)/ace/Stream_Modules.h \ + $(WRAPPER_ROOT)/ace/Stream_Modules.cpp \ + $(WRAPPER_ROOT)/ace/Stream_Modules.i \ + $(WRAPPER_ROOT)/ace/Module.i \ + $(WRAPPER_ROOT)/ace/Task.i \ + $(WRAPPER_ROOT)/ace/Svc_Handler.cpp \ + $(WRAPPER_ROOT)/ace/Dynamic.h \ + $(WRAPPER_ROOT)/ace/Svc_Handler.i \ + Routing_Table.h \ + $(WRAPPER_ROOT)/ace/Map_Manager.h \ + $(WRAPPER_ROOT)/ace/Map_Manager.cpp \ + $(WRAPPER_ROOT)/ace/Map_Manager.i \ + Routing_Table.cpp Routing_Entry.h Peer_Message.h Channel_Connector.h \ + $(WRAPPER_ROOT)/ace/Connector.h \ + $(WRAPPER_ROOT)/ace/Strategies.h \ + $(WRAPPER_ROOT)/ace/Strategies.cpp \ + $(WRAPPER_ROOT)/ace/Connector.i \ + $(WRAPPER_ROOT)/ace/Connector.cpp + +# IF YOU PUT ANYTHING HERE IT WILL GO AWAY diff --git a/apps/Gateway/Gateway/Peer_Message.h b/apps/Gateway/Gateway/Peer_Message.h new file mode 100644 index 00000000000..d4041098e1b --- /dev/null +++ b/apps/Gateway/Gateway/Peer_Message.h @@ -0,0 +1,89 @@ +/* -*- C++ -*- */ +// @(#)Peer_Message.h 1.1 10/18/96 + + +// ============================================================================ +// +// = LIBRARY +// apps +// +// = FILENAME +// Peer_Message.h +// +// = AUTHOR +// Doug Schmidt +// +// ============================================================================ + +#if !defined (PEER_MESSAGE) +#define PEER_MESSAGE + +// This is the unique connection identifier that denotes a particular +// Channel in the Gateway. +typedef short CONN_ID; + +class Peer_Addr + // = TITLE + // Peer address is used to identify the source/destination of a + // routing message. +{ +public: + Peer_Addr (CONN_ID cid = -1, u_char lid = 0, u_char pay = 0) + : conn_id_ (cid), logical_id_ (lid), payload_ (pay) {} + + int operator== (const Peer_Addr &pa) const + { + return this->conn_id_ == pa.conn_id_ + && this->logical_id_ == pa.logical_id_ + && this->payload_ == pa.payload_; + } + + CONN_ID conn_id_; + // Unique connection identifier that denotes a particular Channel. + + u_char logical_id_; + // Logical ID. + + u_char payload_; + // Payload type. +}; + + +class Peer_Header + // = TITLE + // Fixed sized header. +{ +public: + typedef u_short ROUTING_ID; + // Type used to route messages from gatewayd. + + enum + { + INVALID_ID = -1 // No peer can validly use this number. + }; + + ROUTING_ID routing_id_; + // Source ID. + + size_t len_; + // Length of the message in bytes. +}; + +class Peer_Message + // = TITLE + // Variable-sized message (buf_ may be variable-sized between + // 0 and MAX_PAYLOAD_SIZE). +{ +public: + enum { MAX_PAYLOAD_SIZE = 1024 }; + // The maximum size of an Peer message (see Peer protocol specs for + // exact #). + + Peer_Header header_; + // Message header. + + char buf_[MAX_PAYLOAD_SIZE]; + // Message payload. +}; + +#endif /* PEER_MESSAGE */ diff --git a/apps/Gateway/Gateway/README b/apps/Gateway/Gateway/README new file mode 100644 index 00000000000..ceb17528d0d --- /dev/null +++ b/apps/Gateway/Gateway/README @@ -0,0 +1,22 @@ +This application illustrates an application-level Gateway which +routes messages between a set of Peers in a distributed environment. + +The default configuration is single-threaded, i.e., all Input_Channels +and Output_Channels are multiplexed via the Reactor on a single thread +of control. To obtain a version that multi-threads both input and +output simply set the following flag in the Makefile: + +DEFFLAGS += -DUSE_OUTPUT_MT -DUSE_INPUT_MT + +To get a version that uses single-threading for all Input_Channels, +but a separate thread per-Output_Channel set the following flag in the +Makefile: + +DEFFLAGS += -DUSE_OUTPUT_MT + +If you examine the source code, you'll see that very few changes are +required in the source code to switch between single-threading and +multi-threading. The ACE Task class is primarily responsible for +enabling the flexible modification of concurrency strategies with +little modification to the source code, design, and system +architecture. diff --git a/apps/Gateway/Gateway/Routing_Entry.cpp b/apps/Gateway/Gateway/Routing_Entry.cpp new file mode 100644 index 00000000000..a7333b73aea --- /dev/null +++ b/apps/Gateway/Gateway/Routing_Entry.cpp @@ -0,0 +1,47 @@ +// Defines an entry in the Routing Table. +// @(#)Routing_Entry.cpp 1.1 10/18/96 + +#include "Routing_Entry.h" + +Routing_Entry::Routing_Entry (int validity_interval) + : validity_interval_ (validity_interval) +{ + ACE_NEW (this->destinations_, Routing_Entry::ENTRY_SET); +} + +Routing_Entry::~Routing_Entry (void) +{ + delete this->destinations_; +} + +// Get the associated set of destinations. + +Routing_Entry::ENTRY_SET * +Routing_Entry::destinations (void) +{ + return this->destinations_; +} + +// Set the associated set of destinations. + +void +Routing_Entry::destinations (Routing_Entry::ENTRY_SET *s) +{ + this->destinations_ = s; +} + +// Get the current validity interval for this route. + +int +Routing_Entry::validity_interval (void) +{ + return this->validity_interval_; +} + +// Set the current validity interval for this route. + +void +Routing_Entry::validity_interval (int vi) +{ + this->validity_interval_ = vi; +} diff --git a/apps/Gateway/Gateway/Routing_Entry.h b/apps/Gateway/Gateway/Routing_Entry.h new file mode 100644 index 00000000000..3b281dd77e9 --- /dev/null +++ b/apps/Gateway/Gateway/Routing_Entry.h @@ -0,0 +1,53 @@ +/* -*- C++ -*- */ +// @(#)Routing_Entry.h 1.1 10/18/96 + + +// ============================================================================ +// +// = LIBRARY +// apps +// +// = FILENAME +// Routing_Entry.h +// +// = AUTHOR +// Doug Schmidt +// +// ============================================================================ + +#if !defined (_ROUTING_ENTRY) +#define _ROUTING_ENTRY + +#include "ace/Set.h" + +// Forward reference. +class Channel; + +class Routing_Entry +{ + // = TITLE + // Defines an entry in the Routing_Table. +public: + Routing_Entry (int validity_interval = 0); + ~Routing_Entry (void); + + typedef ACE_Unbounded_Set<Channel *> ENTRY_SET; + typedef ACE_Unbounded_Set_Iterator<Channel *> ENTRY_ITERATOR; + + // = Set/get the associated set of destinations. + ENTRY_SET *destinations (void); + void destinations (ENTRY_SET *); + + // = Set/get current validity interval for this routing entry. + int validity_interval (void); + void validity_interval (int); + +protected: + ENTRY_SET *destinations_; + // The set of destinations; + + int validity_interval_; + // The current validity interval of this link. +}; + +#endif /* _ROUTING_ENTRY */ diff --git a/apps/Gateway/Gateway/Routing_Table.cpp b/apps/Gateway/Gateway/Routing_Table.cpp new file mode 100644 index 00000000000..284febeafe1 --- /dev/null +++ b/apps/Gateway/Gateway/Routing_Table.cpp @@ -0,0 +1,69 @@ +/* -*- C++ -*- */ +// @(#)Routing_Table.cpp 1.1 10/18/96 + + +#if !defined (_ROUTING_TABLE_C) +#define _ROUTING_TABLE_C + +#include "ace/Log_Msg.h" +#include "Routing_Table.h" + +/* Bind the EXT_ID to the INT_ID. */ + +template <class EXT_ID, class INT_ID, class LOCK> ACE_INLINE int +Routing_Table<EXT_ID, INT_ID, LOCK>::bind (EXT_ID ext_id, INT_ID *int_id) +{ + return this->map_.bind (ext_id, int_id); +} + +/* Find the INT_ID corresponding to the EXT_ID. */ + +template <class EXT_ID, class INT_ID, class LOCK> ACE_INLINE int +Routing_Table<EXT_ID, INT_ID, LOCK>::find (EXT_ID ext_id, INT_ID *&int_id) +{ + return this->map_.find (ext_id, int_id); +} + +/* Unbind (remove) the EXT_ID from the map. */ + +template <class EXT_ID, class INT_ID, class LOCK> ACE_INLINE int +Routing_Table<EXT_ID, INT_ID, LOCK>::unbind (EXT_ID ext_id) +{ + return this->map_.unbind (ext_id); +} + +template <class EXT_ID, class INT_ID, class LOCK> ACE_INLINE +Routing_Iterator<EXT_ID, INT_ID, LOCK>::Routing_Iterator (Routing_Table<EXT_ID, + INT_ID, LOCK> &rt, + int ignore_inactive) + : map_iter_ (rt.map_), + ignore_inactive_ (ignore_inactive) +{ +} + +template <class EXT_ID, class INT_ID, class LOCK> ACE_INLINE int +Routing_Iterator<EXT_ID, INT_ID, LOCK>::next (INT_ID *&ss) +{ + // Loop in order to skip over inactive entries if necessary. + + for (ACE_Map_Entry<EXT_ID, INT_ID *> *temp = 0; + this->map_iter_.next (temp) != 0; + this->advance ()) + { + // Skip over inactive entries if necessary. + if (temp->int_id_->active () == 0 && this->ignore_inactive_) + continue; + + // Otherwise, return the next item. + ss = temp->int_id_; + return 1; + } + return 0; +} + +template <class EXT_ID, class INT_ID, class LOCK> ACE_INLINE int +Routing_Iterator<EXT_ID, INT_ID, LOCK>::advance (void) +{ + return this->map_iter_.advance (); +} +#endif /* _ROUTING_TABLE_C */ diff --git a/apps/Gateway/Gateway/Routing_Table.h b/apps/Gateway/Gateway/Routing_Table.h new file mode 100644 index 00000000000..884932c6da1 --- /dev/null +++ b/apps/Gateway/Gateway/Routing_Table.h @@ -0,0 +1,67 @@ +/* -*- C++ -*- */ +// @(#)Routing_Table.h 1.1 10/18/96 + + +// ============================================================================ +// +// = LIBRARY +// apps +// +// = FILENAME +// Routing_Table.h +// +// = AUTHOR +// Doug Schmidt +// +// ============================================================================ + +#if !defined (_ROUTING_TABLE_H) +#define _ROUTING_TABLE_H + +#include "ace/Map_Manager.h" + +template <class EXT_ID, class INT_ID, class LOCK> +class Routing_Table +{ + // = TITLE + // Define a generic routing table based on the ACE Map_Manager. + // + // = DESCRIPTION + // We need to have this table, rather than just using the Map_Manager + // directly in order to ignore "inactive" routing entries... +public: + int bind (EXT_ID ext_id, INT_ID *int_id); + // Associate EXT_ID with the INT_ID. + + int find (EXT_ID ext_id, INT_ID *&int_id); + // Break any association of EXID. + + int unbind (EXT_ID ext_id); + // Locate EXID and pass out parameter via INID. If found, + // return 0, else -1. + +public: + ACE_Map_Manager<EXT_ID, INT_ID *, LOCK> map_; + // Map external IDs to internal IDs. +}; + +template <class EXT_ID, class INT_ID, class LOCK> +class Routing_Iterator +{ + // = TITLE + // Define an iterator for the Routing Table. +public: + Routing_Iterator (Routing_Table<EXT_ID, INT_ID, LOCK> &mm, + int ignore_inactive = 1); + int next (INT_ID *&); + int advance (void); + +private: + ACE_Map_Iterator<EXT_ID, INT_ID *, LOCK> map_iter_; + int ignore_inactive_; +}; + +#if defined (ACE_TEMPLATES_REQUIRE_SOURCE) +#include "Routing_Table.cpp" +#endif /* ACE_TEMPLATES_REQUIRE_SOURCE */ +#endif /* _ROUTING_TABLE_H */ diff --git a/apps/Gateway/Gateway/Thr_Channel.cpp b/apps/Gateway/Gateway/Thr_Channel.cpp new file mode 100644 index 00000000000..bcabb66cc2b --- /dev/null +++ b/apps/Gateway/Gateway/Thr_Channel.cpp @@ -0,0 +1,204 @@ +#include "Thr_Channel.h" +// @(#)Thr_Channel.cpp 1.1 10/18/96 + +#include "Channel_Connector.h" + +#if defined (ACE_HAS_THREADS) +Thr_Output_Channel::Thr_Output_Channel (ROUTING_TABLE *rt, + Channel_Connector *cc, + ACE_Thread_Manager *thr_mgr, + int socket_queue_size) + : Output_Channel (rt, cc, thr_mgr, socket_queue_size) +{ +} + +// This method should be called only when the peer shuts down +// unexpectedly. This method marks the Channel as having failed and +// deactivates the ACE_Message_Queue (to wake up the thread blocked on +// <dequeue_head> in svc()). Thr_Output_Handler::handle_close () will +// eventually try to reconnect... + +int +Thr_Output_Channel::handle_input (ACE_HANDLE h) +{ + this->Output_Channel::handle_input (h); + ACE_Service_Config::reactor ()->remove_handler (h, + ACE_Event_Handler::RWE_MASK + | ACE_Event_Handler::DONT_CALL); + // Deactivate the queue while we try to get reconnected. + this->msg_queue ()->deactivate (); + return 0; +} + +// Initialize the threaded Output_Channel object and spawn a new +// thread. + +int +Thr_Output_Channel::open (void *) +{ + // Set the size of the socket queue. + this->socket_queue_size (); + + // Turn off non-blocking I/O. + if (this->peer ().disable (ACE_NONBLOCK) == -1) + ACE_ERROR_RETURN ((LM_ERROR, "(%t) %p\n", "enable"), -1); + + // Register ourselves to receive input events (which indicate that + // the Peer has shut down unexpectedly). + if (ACE_Service_Config::reactor ()->register_handler (this, + ACE_Event_Handler::READ_MASK) == -1) + ACE_ERROR_RETURN ((LM_ERROR, "(%t) %p\n", "register_handler"), -1); + + if (this->initialize_connection ()) + ACE_ERROR_RETURN ((LM_ERROR, "(%t) %p\n", + "initialize_connection"), -1); + + // Reactivate message queue. If it was active then this is the + // first time in and we need to spawn a thread, otherwise the queue + // was inactive due to some problem and we've already got a thread. + if (this->msg_queue ()->activate () == ACE_Message_Queue<SYNCH>::WAS_ACTIVE) + { + ACE_DEBUG ((LM_DEBUG, "(%t) spawning new thread\n")); + // Become an active object by spawning a new thread to transmit + // messages to peers. + return this->activate (THR_NEW_LWP | THR_DETACHED); + } + else + { + ACE_DEBUG ((LM_DEBUG, "(%t) reusing existing thread\n")); + return 0; + } +} + +// ACE_Queue up a message for transmission (must not block since all +// Input_Channels are single-threaded). + +int +Thr_Output_Channel::put (ACE_Message_Block *mb, ACE_Time_Value *) +{ + // Perform non-blocking enqueue. + return this->msg_queue ()->enqueue_tail (mb, (ACE_Time_Value *) &ACE_Time_Value::zero); +} + +// Transmit messages to the peer (note simplification resulting from +// threads...) + +int +Thr_Output_Channel::svc (void) +{ + for (;;) + { + ACE_DEBUG ((LM_DEBUG, "(%t) connected! Thr_Output_Channel's fd = %d\n", + this->peer ().get_handle ())); + + // Since this method runs in its own thread it is OK to block on + // output. + + for (ACE_Message_Block *mb = 0; + this->msg_queue ()->dequeue_head (mb) != -1; ) + if (this->send_peer (mb) == -1) + ACE_ERROR ((LM_ERROR, "(%t) %p\n", "send failed")); + + ACE_ASSERT (errno == ESHUTDOWN); + + ACE_DEBUG ((LM_DEBUG, "(%t) shutting down threaded Output_Channel %d on handle %d\n", + this->id (), this->get_handle ())); + + this->peer ().close (); + + for (this->timeout (1); + // Default is to reconnect synchronously. + this->connector_->initiate_connection (this) == -1; ) + { + ACE_Time_Value tv (this->timeout ()); + ACE_ERROR ((LM_ERROR, + "(%t) reattempting connection, sec = %d\n", + tv.sec ())); + ACE_OS::sleep (tv); + } + } + + return 0; +} + +Thr_Input_Channel::Thr_Input_Channel (ROUTING_TABLE *rt, + Channel_Connector *cc, + ACE_Thread_Manager *thr_mgr, + int socket_queue_size) + : Input_Channel (rt, cc, thr_mgr, socket_queue_size) +{ +} + +int +Thr_Input_Channel::open (void *) +{ + // Set the size of the socket queue. + this->socket_queue_size (); + + // Turn off non-blocking I/O. + if (this->peer ().disable (ACE_NONBLOCK) == -1) + ACE_ERROR_RETURN ((LM_ERROR, "(%t) %p\n", "enable"), -1); + + if (this->initialize_connection ()) + ACE_ERROR_RETURN ((LM_ERROR, "(%t) %p\n", + "initialize_connection"), -1); + + // Reactivate message queue. If it was active then this is the + // first time in and we need to spawn a thread, otherwise the queue + // was inactive due to some problem and we've already got a thread. + if (this->msg_queue ()->activate () == ACE_Message_Queue<SYNCH>::WAS_ACTIVE) + { + ACE_DEBUG ((LM_DEBUG, "(%t) spawning new thread\n")); + // Become an active object by spawning a new thread to transmit + // messages to peers. + return this->activate (THR_NEW_LWP | THR_DETACHED); + } + else + { + ACE_DEBUG ((LM_DEBUG, "(%t) reusing existing thread\n")); + return 0; + } +} + +// Receive messages from a Peer in a separate thread (note reuse of +// existing code!). + +int +Thr_Input_Channel::svc (void) +{ + for (;;) + { + ACE_DEBUG ((LM_DEBUG, "(%t) connected! Thr_Input_Channel's fd = %d\n", + this->peer ().get_handle ())); + + // Since this method runs in its own thread and processes + // messages for one connection it is OK to block on input and + // output. + + while (this->handle_input () != -1) + continue; + + ACE_DEBUG ((LM_DEBUG, + "(%t) shutting down threaded Input_Channel %d on handle %d\n", + this->id (), + this->get_handle ())); + + this->peer ().close (); + + // Deactivate the queue while we try to get reconnected. + this->msg_queue ()->deactivate (); + + for (this->timeout (1); + // Default is to reconnect synchronously. + this->connector_->initiate_connection (this) == -1; ) + { + ACE_Time_Value tv (this->timeout ()); + ACE_ERROR ((LM_ERROR, + "(%t) reattempting connection, sec = %d\n", tv.sec ())); + ACE_OS::sleep (tv); + } + } + return 0; +} + +#endif /* ACE_HAS_THREADS */ diff --git a/apps/Gateway/Gateway/Thr_Channel.h b/apps/Gateway/Gateway/Thr_Channel.h new file mode 100644 index 00000000000..796a9759d02 --- /dev/null +++ b/apps/Gateway/Gateway/Thr_Channel.h @@ -0,0 +1,65 @@ +/* -*- C++ -*- */ +// @(#)Thr_Channel.h 1.1 10/18/96 + + +// ============================================================================ +// +// = LIBRARY +// apps +// +// = FILENAME +// Thr_Channel.h +// +// = AUTHOR +// Doug Schmidt +// +// ============================================================================ + +#if !defined (_THR_CHANNEL) +#define _THR_CHANNEL + +#include "Channel.h" + +#if defined (ACE_HAS_THREADS) +class Thr_Output_Channel : public Output_Channel + // = TITLE + // Runs each Output Channel in a separate thread. +{ +public: + Thr_Output_Channel (ROUTING_TABLE *, + Channel_Connector *, + ACE_Thread_Manager *, + int socket_queue_size); + + virtual int open (void *); + // Initialize the threaded Output_Channel object and spawn a new + // thread. + + virtual int handle_input (ACE_HANDLE); + // Called when Peer shutdown unexpectedly. + + virtual int put (ACE_Message_Block *, ACE_Time_Value * = 0); + // Send a message to a peer. + + virtual int svc (void); + // Transmit peer messages. +}; + +class Thr_Input_Channel : public Input_Channel + // = TITLE + // Runs each Input Channel in a separate thread. +{ +public: + Thr_Input_Channel (ROUTING_TABLE *, + Channel_Connector *, + ACE_Thread_Manager *, + int socket_queue_size); + + virtual int open (void *); + // Initialize the object and spawn a new thread. + + virtual int svc (void); + // Transmit peer messages. +}; +#endif /* ACE_HAS_THREADS */ +#endif /* _THR_CHANNEL */ diff --git a/apps/Gateway/Gateway/cc_config b/apps/Gateway/Gateway/cc_config new file mode 100644 index 00000000000..96f9ebdedd7 --- /dev/null +++ b/apps/Gateway/Gateway/cc_config @@ -0,0 +1,10 @@ +# Conn ID Hostname Remote Port Direction Max Retry Delay Local Port +# ------- -------- ---- --------- --------------- ---------- + 1 tango.cs 10004 I 32 20000 +# 2 tango.cs 10004 O 32 + 3 merengue.cs 10004 O 32 20001 +# 4 mambo.cs 10004 O 32 20000 +# 5 lambada.cs 10004 O 32 20000 +# 6 tango.cs 10004 O 32 20000 +# 7 tango.cs 5001 I 32 +# 8 tango.cs 5002 O 32 diff --git a/apps/Gateway/Gateway/gatewayd.cpp b/apps/Gateway/Gateway/gatewayd.cpp new file mode 100644 index 00000000000..5897c69ca00 --- /dev/null +++ b/apps/Gateway/Gateway/gatewayd.cpp @@ -0,0 +1,34 @@ +// Main driver program for the Gateway. This file is completely +// @(#)gatewayd.cpp 1.1 10/18/96 + +// generic code due to the ACE Service Configurator framework! + +#include "ace/Service_Config.h" +#include "Gateway.h" + +int +main (int argc, char *argv[]) +{ + ACE_Service_Config daemon; + + if (daemon.open (argc, argv) == -1) + { + if (errno != ENOENT) + ACE_ERROR ((LM_ERROR, "%p\n%a", "open", 1)); + else // Use static binding. + { + static char *l_argv[3] = { "-d" }; + ACE_Service_Object *so = ACE_SVC_INVOKE (ACE_Gateway); + + if (so->init (1, l_argv) == -1) + ACE_ERROR ((LM_ERROR, "%p\n%a", "init", 1)); + } + } + + // Run forever, performing the configured services until we are shut + // down by a signal. + + ACE_Service_Config::run_reactor_event_loop (); + + return 0; +} diff --git a/apps/Gateway/Gateway/rt_config b/apps/Gateway/Gateway/rt_config new file mode 100644 index 00000000000..e951a0f09be --- /dev/null +++ b/apps/Gateway/Gateway/rt_config @@ -0,0 +1,7 @@ +# Conn ID Logical ID Payload Destinations +# ------- ---------- ------- ------------ +# 1 1 0 3,4,5 + 1 1 0 3 + 3 1 0 3 +# 4 1 0 4 +# 5 1 0 5 diff --git a/apps/Gateway/Gateway/svc.conf b/apps/Gateway/Gateway/svc.conf new file mode 100644 index 00000000000..8dfd56585b1 --- /dev/null +++ b/apps/Gateway/Gateway/svc.conf @@ -0,0 +1,3 @@ +#static Svc_Manager "-d -p 2913" +dynamic Gateway Service_Object *./libGateway.so:_make_ACE_Gateway() active "-d -c cc_config -f rt_config" + diff --git a/apps/Gateway/Makefile b/apps/Gateway/Makefile new file mode 100644 index 00000000000..b7abb07bf51 --- /dev/null +++ b/apps/Gateway/Makefile @@ -0,0 +1,26 @@ +#---------------------------------------------------------------------------- +# @(#)Makefile 1.1 10/18/96 +# +# Makefile for the Gateway application +#---------------------------------------------------------------------------- + +#---------------------------------------------------------------------------- +# Local macros +#---------------------------------------------------------------------------- + +INFO = README + +DIRS = Gateway \ + Peer + + +#---------------------------------------------------------------------------- +# Include macros and targets +#---------------------------------------------------------------------------- + +include $(WRAPPER_ROOT)/include/makeinclude/wrapper_macros.GNU +include $(WRAPPER_ROOT)/include/makeinclude/macros.GNU +include $(WRAPPER_ROOT)/include/makeinclude/rules.common.GNU +include $(WRAPPER_ROOT)/include/makeinclude/rules.nested.GNU +include $(WRAPPER_ROOT)/include/makeinclude/rules.nolocal.GNU + diff --git a/apps/Gateway/Peer/Gateway_Handler.cpp b/apps/Gateway/Peer/Gateway_Handler.cpp new file mode 100644 index 00000000000..84e3c796fe5 --- /dev/null +++ b/apps/Gateway/Peer/Gateway_Handler.cpp @@ -0,0 +1,653 @@ +#include "ace/Get_Opt.h" +// @(#)Gateway_Handler.cpp 1.1 10/18/96 + +#include "ace/Log_Msg.h" +#include "Gateway_Handler.h" + +Gateway_Handler::Gateway_Handler (ACE_Thread_Manager *) + : routing_id_ (0), + msg_frag_ (0), + total_bytes_ (0) +{ + this->msg_queue ()->high_water_mark (Gateway_Handler::QUEUE_SIZE); +} + +int +Gateway_Handler::handle_signal (int signum, siginfo_t *, ucontext_t *) +{ + ACE_DEBUG ((LM_DEBUG, "(%t) %S\n", signum)); + + // Shut down the main event loop. + ACE_Service_Config::end_reactor_event_loop (); + return 0; +} + +// Cache a binding to the HANDLER_MAP. + +void +Gateway_Handler::map (HANDLER_MAP *m) +{ + this->map_ = m; +} + +// Upcall from the ACE_Acceptor::handle_input() that turns control +// over to our application-specific Gateway handler. + +int +Gateway_Handler::open (void *a) +{ + ACE_DEBUG ((LM_DEBUG, "Gateway handler's fd = %d\n", + this->peer ().get_handle ())); + + // Call down to the base class to activate and register this + // handler. + if (this->inherited::open (a) == -1) + ACE_ERROR_RETURN ((LM_ERROR, "%p\n", "open"), -1); + + if (this->peer ().enable (ACE_NONBLOCK) == -1) + ACE_ERROR_RETURN ((LM_ERROR, "%p\n", "enable"), -1); + + Gateway_Handler *this_ = this; + + // Add ourselves to the map so we can be removed later on. + if (this->map_->bind (this->get_handle (), this_) == -1) + ACE_ERROR_RETURN ((LM_ERROR, "%p\n", "bind"), -1); + + char *to = ACE_OS::getenv ("TIMEOUT"); + int timeout = to == 0 ? 100000 : ACE_OS::atoi (to); + + // Schedule the time between disconnects. This should really be a + // "tunable" parameter. + if (ACE_Service_Config::reactor ()->schedule_timer (this, 0, timeout) == -1) + ACE_ERROR ((LM_ERROR, "%p\n", "schedule_timer")); + + // If there are messages left in the queue, make sure we + // enable the ACE_Reactor appropriately to get them sent out. + if (this->msg_queue ()->is_empty () == 0 + && ACE_Service_Config::reactor ()->schedule_wakeup (this, + ACE_Event_Handler::WRITE_MASK) == -1) + ACE_ERROR_RETURN ((LM_ERROR, "%p\n", "schedule_wakeup"), -1); + + // First action is to wait to be notified of our routing id. + this->do_action_ = &Gateway_Handler::await_route_id; + return 0; +} + +// Read messages from stdin and send them to the gatewayd. + +int +Gateway_Handler::xmit_stdin (void) +{ + if (this->routing_id_ != -1) + { + ssize_t n; + ACE_Message_Block *mb; + + ACE_NEW_RETURN (mb, + ACE_Message_Block (sizeof (Peer_Message)), + -1); + + Peer_Message *peer_msg = (Peer_Message *) mb->rd_ptr (); + peer_msg->header_.routing_id_ = this->routing_id_; + + n = ACE_OS::read (ACE_STDIN, peer_msg->buf_, sizeof peer_msg->buf_); + + switch (n) + { + case 0: + ACE_DEBUG ((LM_DEBUG, "stdin closing down\n")); + + // Take stdin out of the ACE_Reactor so we stop trying to + // send messages. + if (ACE_Service_Config::reactor ()->remove_handler + (0, ACE_Event_Handler::DONT_CALL | ACE_Event_Handler::READ_MASK) == -1) + ACE_ERROR ((LM_ERROR, "%p\n", "remove_handler")); + delete mb; + break; + case -1: + delete mb; + ACE_ERROR ((LM_ERROR, "%p\n", "read")); + break; + default: + peer_msg->header_.len_ = htonl (n); + mb->wr_ptr (sizeof (Peer_Header) + n); + + if (this->put (mb) == -1) + { + if (errno == EWOULDBLOCK) // The queue has filled up! + ACE_ERROR ((LM_ERROR, "%p\n", + "gateway is flow controlled, so we're dropping messages")); + else + ACE_ERROR ((LM_ERROR, "%p\n", "transmission failure in xmit_stdin")); + + // Caller is responsible for freeing a ACE_Message_Block + // if failures occur. + delete mb; + } + } + } + return 0; +} + +// Perform a non-blocking put() of message MB. If we are unable to +// send the entire message the remainder is re-Taskd at the *front* of +// the Message_List. + +int +Gateway_Handler::nonblk_put (ACE_Message_Block *mb) +{ + // Try to send the message. If we don't send it all (e.g., due to + // flow control), then re-ACE_Task the remainder at the head of the + // Message_List and ask the ACE_Reactor to inform us (via + // handle_output()) when it is possible to try again. + + ssize_t n; + + if ((n = this->send_peer (mb)) == -1) + return -1; + else if (errno == EWOULDBLOCK) // Didn't manage to send everything. + { + ACE_DEBUG ((LM_DEBUG, + "queueing activated on handle %d to routing id %d\n", + this->get_handle (), this->routing_id_)); + + // ACE_Queue in *front* of the list to preserve order. + if (this->msg_queue ()->enqueue_head + (mb, (ACE_Time_Value *) &ACE_Time_Value::zero) == -1) + ACE_ERROR_RETURN ((LM_ERROR, "%p\n", "enqueue_head"), -1); + + // Tell ACE_Reactor to call us back when we can send again. + if (ACE_Service_Config::reactor ()->schedule_wakeup + (this, ACE_Event_Handler::WRITE_MASK) == -1) + ACE_ERROR_RETURN ((LM_ERROR, "%p\n", "schedule_wakeup"), -1); + return 0; + } + else + return n; +} + +// Finish sending a message when flow control conditions abate. This +// method is automatically called by the ACE_Reactor. + +int +Gateway_Handler::handle_output (ACE_HANDLE) +{ + ACE_Message_Block *mb = 0; + int status = 0; + + ACE_DEBUG ((LM_DEBUG, "in handle_output\n")); + // The list had better not be empty, otherwise there's a bug! + + if (this->msg_queue ()->dequeue_head + (mb, (ACE_Time_Value *) &ACE_Time_Value::zero) != -1) + { + switch (this->nonblk_put (mb)) + { + case 0: // Partial send. + ACE_ASSERT (errno == EWOULDBLOCK); + // Didn't write everything this time, come back later... + break; + + case -1: + // Caller is responsible for freeing a ACE_Message_Block if + // failures occur. + delete mb; + ACE_ERROR ((LM_ERROR, "%p\n", + "transmission failure in handle_output")); + + /* FALLTHROUGH */ + default: // Sent the whole thing. + + // If we succeed in writing the entire message (or we did + // not fail due to EWOULDBLOCK) then check if there are more + // messages on the Message_List. If there aren't, tell the + // ACE_Reactor not to notify us anymore (at least until + // there are new messages queued up). + + if (this->msg_queue ()->is_empty ()) + { + ACE_DEBUG ((LM_DEBUG, + "queue now empty on handle %d to routing id %d\n", + this->get_handle (), + this->routing_id_)); + + if (ACE_Service_Config::reactor ()->cancel_wakeup + (this, ACE_Event_Handler::WRITE_MASK) == -1) + ACE_ERROR ((LM_ERROR, "%p\n", "cancel_wakeup")); + } + } + } + else + ACE_ERROR ((LM_ERROR, "%p\n", "dequeue_head")); + return 0; +} + +// Send a message to a peer (may ACE_Task if necessary). + +int +Gateway_Handler::put (ACE_Message_Block *mb, ACE_Time_Value *) +{ + if (this->msg_queue ()->is_empty ()) + // Try to send the message *without* blocking! + return this->nonblk_put (mb); + else + // If we have queued up messages due to flow control then just + // enqueue and return. + return this->msg_queue ()->enqueue_tail + (mb, (ACE_Time_Value *) &ACE_Time_Value::zero); +} + +// Send an Peer message to gatewayd. + +int +Gateway_Handler::send_peer (ACE_Message_Block *mb) +{ + ssize_t n; + size_t len = mb->length (); + + if ((n = this->peer ().send (mb->rd_ptr (), len)) <= 0) + return errno == EWOULDBLOCK ? 0 : n; + else if (n < len) + { + // Re-adjust pointer to skip over the part we did send. + mb->rd_ptr (n); + this->total_bytes_ += n; + } + else /* if (n == length) */ + { + // The whole message is sent, we can now safely deallocate the + // buffer. Note that this should decrement a reference count... + this->total_bytes_ += n; + delete mb; + errno = 0; + } + ACE_DEBUG ((LM_DEBUG, "sent %d bytes, total bytes sent = %d\n", + n, this->total_bytes_)); + return n; +} + +// Receive an Peer message from gatewayd. Handles fragmentation. + +int +Gateway_Handler::recv_peer (ACE_Message_Block *&mb) +{ + Peer_Message *peer_msg; + size_t len; + ssize_t n; + size_t offset = 0; + + if (this->msg_frag_ == 0) + { + ACE_NEW_RETURN (this->msg_frag_, + ACE_Message_Block (sizeof (Peer_Message)), + -1); + + // No existing fragment... + if (this->msg_frag_ == 0) + ACE_ERROR_RETURN ((LM_ERROR, "out of memory\n"), -1); + + peer_msg = (Peer_Message *) this->msg_frag_->rd_ptr (); + + switch (n = this->peer ().recv (peer_msg, sizeof (Peer_Header))) + { + case sizeof (Peer_Header): + len = ntohl (peer_msg->header_.len_); + if (len <= sizeof peer_msg->buf_) + { + this->msg_frag_->wr_ptr (sizeof (Peer_Header)); + break; // The message is within the maximum size range. + } + else + ACE_ERROR ((LM_ERROR, "message too long = %d\n", len)); + /* FALLTHROUGH */ + default: + ACE_ERROR ((LM_ERROR, "invalid length = %d\n", n)); + n = -1; + /* FALLTHROUGH */ + case -1: + /* FALLTHROUGH */ + case 0: + // Make sure to free up memory on error returns. + delete this->msg_frag_; + this->msg_frag_ = 0; + return n; + } + } + else + { + offset = this->msg_frag_->length () - sizeof (Peer_Header); + len = peer_msg->header_.len_ - offset; + } + + switch (n = this->peer ().recv (peer_msg->buf_ + offset, len)) + { + case -1: + if (errno == EWOULDBLOCK) + { + // This shouldn't happen since the ACE_Reactor + // just triggered us to handle pending I/O! + ACE_DEBUG ((LM_DEBUG, "unexpected recv failure\n")); + // Since ACE_DEBUG might change errno, we need to reset it + // here. + errno = EWOULDBLOCK; + return -1; + } + else + /* FALLTHROUGH */; + + case 0: // EOF. + delete this->msg_frag_; + this->msg_frag_ = 0; + return n; + + default: + if (n != len) + // Re-adjust pointer to skip over the part we've read. + { + this->msg_frag_->wr_ptr (n); + errno = EWOULDBLOCK; + // Inform caller that we didn't get the whole message. + return -1; + } + else + { + // Set the write pointer at 1 past the end of the message. + this->msg_frag_->wr_ptr (n); + + // Set the read pointer to the beginning of the message. + this->msg_frag_->rd_ptr (this->msg_frag_->base ()); + + mb = this->msg_frag_; + + // Reset the pointer to indicate we've got an entire + // message. + this->msg_frag_ = 0; + } + return n; + } +} + +// Receive various types of input (e.g., Peer message from the +// gatewayd, as well as stdio). + +int +Gateway_Handler::handle_input (ACE_HANDLE sd) +{ + ACE_DEBUG ((LM_DEBUG, "in handle_input, sd = %d\n", sd)); + if (sd == ACE_STDIN) // Handle message from stdin. + return this->xmit_stdin (); + else + // Perform the appropriate action depending on the state we are + // in. + return (this->*do_action_) (); +} + +// Action that receives the route id. + +int +Gateway_Handler::await_route_id (void) +{ + ssize_t n = this->peer ().recv (&this->routing_id_, + sizeof this->routing_id_); + + if (n != sizeof this->routing_id_) + { + if (n == 0) + ACE_ERROR_RETURN ((LM_ERROR, + "gatewayd has closed down unexpectedly\n"), -1); + else + ACE_ERROR_RETURN ((LM_ERROR, + "%p, bytes received on handle %d = %d\n", + "recv", this->get_handle (), n), -1); + } + else + ACE_DEBUG ((LM_DEBUG, "assigned routing id %d\n", + this->routing_id_)); + + // Transition to the action that waits for Peer messages. + this->do_action_ = &Gateway_Handler::await_messages; + + // Reset standard input. + ACE_OS::rewind (stdin); + + // Register this handler to receive test messages on stdin. + if (ACE_Service_Config::reactor ()->register_handler + (ACE_STDIN, this, ACE_Event_Handler::READ_MASK) == -1) + ACE_ERROR_RETURN ((LM_ERROR, "%p\n", "register_handler"), -1); + return 0; +} + +// Action that receives messages. + +int +Gateway_Handler::await_messages (void) +{ + ACE_Message_Block *mb = 0; + ssize_t n = this->recv_peer (mb); + + switch (n) + { + case 0: + ACE_ERROR_RETURN ((LM_ERROR, "gatewayd has closed down\n"), -1); + /* NOTREACHED */ + case -1: + if (errno == EWOULDBLOCK) + // A short-read, we'll come back and finish it up later on! + return 0; + else + ACE_ERROR_RETURN ((LM_ERROR, "%p\n", "recv_peer"), -1); + /* NOTREACHED */ + default: + { + // We got a valid message, so let's process it now! At the + // moment, we just print out the message contents... + + Peer_Message *peer_msg = (Peer_Message *) mb->rd_ptr (); + this->total_bytes_ += mb->length (); + +#if defined (VERBOSE) + ACE_DEBUG ((LM_DEBUG, + "route id = %d, len = %d, payload = %*s", + peer_msg->header_.routing_id_, peer_msg->header_.len_, + peer_msg->header_.len_, peer_msg->buf_)); +#else + ACE_DEBUG ((LM_DEBUG, + "route id = %d, cur len = %d, total len = %d\n", + peer_msg->header_.routing_id_, + peer_msg->header_.len_, + this->total_bytes_)); +#endif + delete mb; + return 0; + } + } +} + +// Periodically send messages via ACE_Reactor timer mechanism. + +int +Gateway_Handler::handle_timeout (const ACE_Time_Value &, const void *) +{ + // Skip over deactivated descriptors. + if (this->get_handle () != -1) + { + // Unbind ourselves from the map. + if (this->map_->unbind (this->get_handle ()) == -1) + ACE_ERROR ((LM_ERROR, "%p\n", "unbind")); + + // Shut down the handler. + this->handle_close (); + } + return 0; +} + +// Handle shutdown of the Gateway_Handler object. + +int +Gateway_Handler::handle_close (ACE_HANDLE, ACE_Reactor_Mask) +{ + if (this->get_handle () != ACE_INVALID_HANDLE) + { + ACE_DEBUG ((LM_DEBUG, "shutting down Gateway_Handler on handle %d\n", + this->get_handle ())); + + // Explicitly remove ourselves for handle 0 (the ACE_Reactor + // removes this->handle (), note that + // ACE_Event_Handler::DONT_CALL instructs the ACE_Reactor *not* + // to call this->handle_close(), which would otherwise lead to + // recursion!). + if (ACE_Service_Config::reactor ()->remove_handler + (0, ACE_Event_Handler::DONT_CALL | ACE_Event_Handler::READ_MASK) == -1) + ACE_ERROR ((LM_ERROR, "handle = %d: %p\n", + 0, "remove_handler")); + + // Deregister this handler with the ACE_Reactor. + if (ACE_Service_Config::reactor ()->remove_handler + (this, ACE_Event_Handler::DONT_CALL | ACE_Event_Handler::RWE_MASK) == -1) + ACE_ERROR_RETURN ((LM_ERROR, "handle = %d: %p\n", + this->get_handle (), "remove_handler"), -1); + + // Close down the peer. + this->peer ().close (); + return 0; + } +} + +Gateway_Acceptor::Gateway_Acceptor (Gateway_Handler *handler) + : gateway_handler_ (handler) +{ + this->gateway_handler_->map (&this->map_); +} + +// Note how this method just passes back the pre-allocated +// Gateway_Handler instead of having the ACE_Acceptor allocate a new +// one each time! + +Gateway_Handler * +Gateway_Acceptor::make_svc_handler (void) +{ + return this->gateway_handler_; +} + +int +Gateway_Acceptor::handle_signal (int signum, siginfo_t *, ucontext_t *) +{ + ACE_DEBUG ((LM_DEBUG, "signal %S occurred\n", signum)); + return 0; +} + +/* Returns information on the currently active service. */ + +int +Gateway_Acceptor::info (char **strp, size_t length) const +{ + char buf[BUFSIZ]; + char addr_str[BUFSIZ]; + + ACE_INET_Addr addr; + + if (this->acceptor ().get_local_addr (addr) == -1) + return -1; + else if (addr.addr_to_string (addr_str, sizeof addr) == -1) + return -1; + + ACE_OS::sprintf (buf, "%s\t %s/%s %s", + "Gateway peer daemon", addr_str, "tcp", + "# IRIDIUM SRP traffic generator and data sink\n"); + + if (*strp == 0 && (*strp = ACE_OS::strdup (buf)) == 0) + return -1; + else + ACE_OS::strncpy (*strp, buf, length); + return ACE_OS::strlen (buf); +} + +// Hook called by the explicit dynamic linking facility to terminate +// the peer. + +int +Gateway_Acceptor::fini (void) +{ + HANDLER_ITERATOR mi (this->map_); + + for (MAP_ENTRY *me = 0; + mi.next (me) != 0; + mi.advance ()) + { + if (me->int_id_->get_handle () != -1) + { + ACE_DEBUG ((LM_DEBUG, "closing down handle %d\n", + me->int_id_->get_handle ())); + me->int_id_->handle_close (); + } + else + ACE_DEBUG ((LM_DEBUG, "already closed %d\n")); + me->int_id_->destroy (); // Will trigger a delete. + } + + this->gateway_handler_->destroy (); // Will trigger a delete. + return inherited::fini (); +} + +// Hook called by the explicit dynamic linking facility to initialize +// the peer. + +int +Gateway_Acceptor::init (int argc, char *argv[]) +{ + ACE_Get_Opt get_opt (argc, argv, "dp:", 0); + ACE_INET_Addr addr; + + for (int c; (c = get_opt ()) != -1; ) + { + switch (c) + { + case 'p': + addr.set (ACE_OS::atoi (get_opt.optarg)); + break; + case 'd': + break; + default: + break; + } + } + + if (ACE_Service_Config::reactor ()->register_handler (SIGPIPE, this) == -1) + ACE_ERROR_RETURN ((LM_ERROR, "%p\n", "register_handler"), -1); + + if (this->open (addr) == -1) + ACE_ERROR_RETURN ((LM_ERROR, "%p\n", "open"), -1); + else if (ACE_Service_Config::reactor ()->register_handler + (this, ACE_Event_Handler::READ_MASK) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + "registering service with ACE_Reactor\n"), -1); + + ACE_Sig_Set sig_set; + sig_set.sig_add (SIGINT); + sig_set.sig_add (SIGQUIT); + + // Register ourselves to receive SIGINT and SIGQUIT so we can shut + // down gracefully via signals. + + if (ACE_Service_Config::reactor ()->register_handler (sig_set, + this) == -1) + ACE_ERROR_RETURN ((LM_ERROR, "(%t) %p\n", "register_handler"), -1); + return 0; +} + +// Dynamically linked factory function that dynamically allocates a +// new Gateway_Acceptor object. + +ACE_Service_Object * +_alloc_peerd (void) +{ + // This function illustrates how we can associate a ACE_Svc_Handler + // with the ACE_Acceptor at initialization time. + Gateway_Handler *handler; + + ACE_NEW_RETURN (handler, Gateway_Handler, 0); + ACE_Service_Object *temp; + + ACE_NEW_RETURN (temp, Gateway_Acceptor (handler), 0); + return temp; +} diff --git a/apps/Gateway/Peer/Gateway_Handler.h b/apps/Gateway/Peer/Gateway_Handler.h new file mode 100644 index 00000000000..d3156c47ce1 --- /dev/null +++ b/apps/Gateway/Peer/Gateway_Handler.h @@ -0,0 +1,154 @@ +/* -*- C++ -*- */ +// @(#)Gateway_Handler.h 1.1 10/18/96 + + +/* These Gateway handler classes process Peer messages sent from the + communication gateway daemon (gatewayd) to its various peers, e.g., + CF and ETS, (represented collectively in this prototype as peerd). + . These classes works as follows: + + 1. Gateway_Acceptor creates a listener endpoint and waits passively + for gatewayd to connect with it. + + 2. When gatewayd connects, Gateway_Acceptor creates an + Gateway_Handler object that sends/receives messages from + gatewayd. + + 3. Gateway_Handler waits for gatewayd to inform it of its routing + ID, which is prepended to all outgoing messages send from peerd. + + 4. Once the routing ID is set, peerd periodically sends messages to + gatewayd. Peerd also receives and "processes" messages + forwarded to it from gatewayd. In this program, peerd + "processes" messages by writing them to stdout. */ + +#if !defined (GATEWAY_HANDLER) +#define GATEWAY_HANDLER + +#include "ace/Service_Config.h" +#include "ace/Svc_Handler.h" +#include "ace/Acceptor.h" +#include "ace/SOCK_Stream.h" +#include "ace/SOCK_Acceptor.h" +#include "ace/INET_Addr.h" +#include "ace/Map_Manager.h" +#include "Peer_Message.h" + +// Forward declaration. +class Gateway_Handler; + +// Maps a ACE_HANDLE onto a Gateway_Handler *. +typedef ACE_Map_Manager <ACE_HANDLE, Gateway_Handler *, ACE_Null_Mutex> HANDLER_MAP; +typedef ACE_Map_Iterator<ACE_HANDLE, Gateway_Handler *, ACE_Null_Mutex> HANDLER_ITERATOR; +typedef ACE_Map_Entry <ACE_HANDLE, Gateway_Handler *> MAP_ENTRY; + +// Handle Peer messages arriving as events. + +class Gateway_Handler : public ACE_Svc_Handler<ACE_SOCK_STREAM, ACE_NULL_SYNCH> +{ +public: + Gateway_Handler (ACE_Thread_Manager * = 0); + + virtual int open (void * = 0); + // Initialize the handler (called by ACE_Acceptor::handle_input()) + + virtual int handle_input (ACE_HANDLE); + // Receive and process peer messages. + + virtual int put (ACE_Message_Block *, ACE_Time_Value *tv = 0); + // Send a message to a gateway (may be queued if necessary). + + virtual int handle_output (ACE_HANDLE); + // Finish sending a message when flow control conditions abate. + + virtual int handle_timeout (const ACE_Time_Value &, + const void *arg); + // Periodically send messages via ACE_Reactor timer mechanism. + + virtual int handle_close (ACE_HANDLE = ACE_INVALID_HANDLE, + ACE_Reactor_Mask = ACE_Event_Handler::RWE_MASK); + // Perform object termination. + + void map (HANDLER_MAP *); + // Cache a binding to the HANDLER_MAP. + +protected: + typedef ACE_Svc_Handler<ACE_SOCK_STREAM, ACE_NULL_SYNCH> inherited; + + // We'll allow up to 16 megabytes to be queued per-output + // channel!!!! This is clearly a policy in search of refinement... + enum { QUEUE_SIZE = 1024 * 1024 * 16 }; + + int handle_signal (int signum, siginfo_t *, ucontext_t *); + + Peer_Header::ROUTING_ID routing_id_; + // Routing ID of the peer (obtained from gatewayd). + + virtual int nonblk_put (ACE_Message_Block *mb); + // Perform a non-blocking put(). + + virtual int recv_peer (ACE_Message_Block *&); + // Receive an Peer message from gatewayd. + + virtual int send_peer (ACE_Message_Block *); + // Send an Peer message to gatewayd. + + int xmit_stdin (void); + // Receive a message from stdin and send it to the gateway. + + int (Gateway_Handler::*do_action_) (void); + // Pointer-to-member-function for the current action to run in this state. + + int await_route_id (void); + // Action that receives the route id. + + int await_messages (void); + // Action that receives messages. + + ACE_Message_Block *msg_frag_; + // Keep track of message fragment to handle non-blocking recv's from gateway. + + size_t total_bytes_; + // The total number of bytes sent/received to the gateway. + + HANDLER_MAP *map_; + // Maps the ACE_HANDLE onto the Gateway_Handler *. +}; + +// A factory class that accept connections from gatewayd and +// dynamically creates a new Gateway_Handler object to do the dirty work. + +class Gateway_Acceptor : public ACE_Acceptor<Gateway_Handler, ACE_SOCK_ACCEPTOR> +{ +public: + // = Initialization methods, called when dynamically linked. + Gateway_Acceptor (Gateway_Handler *handler); + virtual int init (int argc, char *argv[]); + // Initialize the acceptor. + + virtual int info (char **, size_t) const; + // Return info about this service. + + virtual int fini (void); + // Perform termination. + + virtual Gateway_Handler *make_svc_handler (void); + // Factory method that creates the Gateway_Handler once. + + virtual int handle_signal (int signum, siginfo_t *, ucontext_t *); + // Handle various signals (e.g., SIGPIPE) + + HANDLER_MAP map_; + // Maps the ACE_HANDLE onto the Gateway_Handler *. + + Gateway_Handler *gateway_handler_; + // Pointer to memory allocated exactly once. + + typedef ACE_Acceptor<Gateway_Handler, ACE_SOCK_ACCEPTOR> inherited; +}; + +// Factory function that allocates a new Peer daemon. +extern "C" ACE_Service_Object *_alloc_peerd (void); + +#endif /* GATEWAY_HANDLER */ + diff --git a/apps/Gateway/Peer/Makefile b/apps/Gateway/Peer/Makefile new file mode 100644 index 00000000000..38362c514a5 --- /dev/null +++ b/apps/Gateway/Peer/Makefile @@ -0,0 +1,137 @@ +#---------------------------------------------------------------------------- +# @(#)Makefile 1.1 10/18/96 +# +# Makefile for the peer portion of the communication gateway +#---------------------------------------------------------------------------- + +#---------------------------------------------------------------------------- +# Local macros +#---------------------------------------------------------------------------- + +BIN = peerd + +FILES = Gateway_Handler + +LSRC = $(addsuffix .cpp,$(FILES)) +LOBJ = $(addsuffix .o,$(FILES)) +VSHOBJS = $(LSRC:%.cpp=$(VSHDIR)%.so) + +LDLIBS = $(VSHOBJS) + +VLDLIBS = $(LDLIBS:%=%$(VAR)) + +BUILD = $(VBIN) + +#---------------------------------------------------------------------------- +# Include macros and targets +#---------------------------------------------------------------------------- + +include $(WRAPPER_ROOT)/include/makeinclude/wrapper_macros.GNU +include $(WRAPPER_ROOT)/include/makeinclude/macros.GNU +include $(WRAPPER_ROOT)/include/makeinclude/rules.common.GNU +include $(WRAPPER_ROOT)/include/makeinclude/rules.nonested.GNU +include $(WRAPPER_ROOT)/include/makeinclude/rules.lib.GNU +include $(WRAPPER_ROOT)/include/makeinclude/rules.bin.GNU +include $(WRAPPER_ROOT)/include/makeinclude/rules.local.GNU + +#---------------------------------------------------------------------------- +# Local targets +#---------------------------------------------------------------------------- + +#---------------------------------------------------------------------------- +# Dependencies +#---------------------------------------------------------------------------- + +# IF YOU PUT ANYTHING HERE IT WILL GO AWAY +# DO NOT DELETE THIS LINE -- g++dep uses it. +# DO NOT PUT ANYTHING AFTER THIS LINE, IT WILL GO AWAY. + +.obj/Gateway_Handler.o .shobj/Gateway_Handler.so: Gateway_Handler.cpp \ + $(WRAPPER_ROOT)/ace/Get_Opt.h \ + $(WRAPPER_ROOT)/ace/ACE.h \ + $(WRAPPER_ROOT)/ace/OS.h \ + $(WRAPPER_ROOT)/ace/Time_Value.h \ + $(WRAPPER_ROOT)/ace/config.h \ + $(WRAPPER_ROOT)/ace/Trace.h \ + $(WRAPPER_ROOT)/ace/ACE.i \ + $(WRAPPER_ROOT)/ace/Log_Msg.h \ + $(WRAPPER_ROOT)/ace/Log_Record.h \ + $(WRAPPER_ROOT)/ace/Log_Priority.h \ + $(WRAPPER_ROOT)/ace/Log_Record.i \ + Gateway_Handler.h \ + $(WRAPPER_ROOT)/ace/Service_Config.h \ + $(WRAPPER_ROOT)/ace/Service_Object.h \ + $(WRAPPER_ROOT)/ace/Shared_Object.h \ + $(WRAPPER_ROOT)/ace/Event_Handler.h \ + $(WRAPPER_ROOT)/ace/Thread_Manager.h \ + $(WRAPPER_ROOT)/ace/Thread.h \ + $(WRAPPER_ROOT)/ace/Synch.h \ + $(WRAPPER_ROOT)/ace/SV_Semaphore_Complex.h \ + $(WRAPPER_ROOT)/ace/SV_Semaphore_Simple.h \ + $(WRAPPER_ROOT)/ace/SV_Semaphore_Simple.i \ + $(WRAPPER_ROOT)/ace/SV_Semaphore_Complex.i \ + $(WRAPPER_ROOT)/ace/Synch_T.h \ + $(WRAPPER_ROOT)/ace/Synch_T.cpp \ + $(WRAPPER_ROOT)/ace/Synch_T.i \ + $(WRAPPER_ROOT)/ace/Set.h \ + $(WRAPPER_ROOT)/ace/Set.cpp \ + $(WRAPPER_ROOT)/ace/Set.i \ + $(WRAPPER_ROOT)/ace/Proactor.h \ + $(WRAPPER_ROOT)/ace/Message_Block.h \ + $(WRAPPER_ROOT)/ace/Malloc.h \ + $(WRAPPER_ROOT)/ace/Malloc_T.h \ + $(WRAPPER_ROOT)/ace/Malloc_T.cpp \ + $(WRAPPER_ROOT)/ace/Malloc_T.i \ + $(WRAPPER_ROOT)/ace/Memory_Pool.h \ + $(WRAPPER_ROOT)/ace/Signal.h \ + $(WRAPPER_ROOT)/ace/Mem_Map.h \ + $(WRAPPER_ROOT)/ace/Timer_Queue.h \ + $(WRAPPER_ROOT)/ace/Timer_Queue.i \ + $(WRAPPER_ROOT)/ace/ReactorEx.h \ + $(WRAPPER_ROOT)/ace/Token.h \ + $(WRAPPER_ROOT)/ace/Reactor.h \ + $(WRAPPER_ROOT)/ace/Handle_Set.h \ + $(WRAPPER_ROOT)/ace/Pipe.h \ + $(WRAPPER_ROOT)/ace/Pipe.i \ + $(WRAPPER_ROOT)/ace/SOCK_Stream.h \ + $(WRAPPER_ROOT)/ace/SOCK_IO.h \ + $(WRAPPER_ROOT)/ace/SOCK.h \ + $(WRAPPER_ROOT)/ace/Addr.h \ + $(WRAPPER_ROOT)/ace/IPC_SAP.h \ + $(WRAPPER_ROOT)/ace/IPC_SAP.i \ + $(WRAPPER_ROOT)/ace/SOCK.i \ + $(WRAPPER_ROOT)/ace/SOCK_IO.i \ + $(WRAPPER_ROOT)/ace/INET_Addr.h \ + $(WRAPPER_ROOT)/ace/SOCK_Stream.i \ + $(WRAPPER_ROOT)/ace/Reactor.i \ + $(WRAPPER_ROOT)/ace/Svc_Conf_Tokens.h \ + $(WRAPPER_ROOT)/ace/Svc_Handler.h \ + $(WRAPPER_ROOT)/ace/Synch_Options.h \ + $(WRAPPER_ROOT)/ace/Task.h \ + $(WRAPPER_ROOT)/ace/Message_Queue.h \ + $(WRAPPER_ROOT)/ace/IO_Cntl_Msg.h \ + $(WRAPPER_ROOT)/ace/Message_Queue.cpp \ + $(WRAPPER_ROOT)/ace/Message_Queue.i \ + $(WRAPPER_ROOT)/ace/Task.cpp \ + $(WRAPPER_ROOT)/ace/Module.h \ + $(WRAPPER_ROOT)/ace/Module.cpp \ + $(WRAPPER_ROOT)/ace/Stream_Modules.h \ + $(WRAPPER_ROOT)/ace/Stream_Modules.cpp \ + $(WRAPPER_ROOT)/ace/Stream_Modules.i \ + $(WRAPPER_ROOT)/ace/Module.i \ + $(WRAPPER_ROOT)/ace/Task.i \ + $(WRAPPER_ROOT)/ace/Svc_Handler.cpp \ + $(WRAPPER_ROOT)/ace/Dynamic.h \ + $(WRAPPER_ROOT)/ace/Svc_Handler.i \ + $(WRAPPER_ROOT)/ace/Acceptor.h \ + $(WRAPPER_ROOT)/ace/Strategies.h \ + $(WRAPPER_ROOT)/ace/Strategies.cpp \ + $(WRAPPER_ROOT)/ace/Acceptor.i \ + $(WRAPPER_ROOT)/ace/Acceptor.cpp \ + $(WRAPPER_ROOT)/ace/SOCK_Acceptor.h \ + $(WRAPPER_ROOT)/ace/Map_Manager.h \ + $(WRAPPER_ROOT)/ace/Map_Manager.cpp \ + $(WRAPPER_ROOT)/ace/Map_Manager.i \ + Peer_Message.h + +# IF YOU PUT ANYTHING HERE IT WILL GO AWAY diff --git a/apps/Gateway/Peer/Peer_Message.h b/apps/Gateway/Peer/Peer_Message.h new file mode 100644 index 00000000000..f6d636911c2 --- /dev/null +++ b/apps/Gateway/Peer/Peer_Message.h @@ -0,0 +1,44 @@ +/* -*- C++ -*- */ +// @(#)Peer_Message.h 1.1 10/18/96 + +// Define the Peer message schema (this may change). + +#if !defined (PEER_MESSAGE) +#define PEER_MESSAGE + +// Fixed sized header. + +class Peer_Header +{ +public: +// Type used to route messages from gatewayd. + typedef short ROUTING_ID; + + enum + { + INVALID_ID = -1 // No peer may use this number. + }; + + // Source ID. + ROUTING_ID routing_id_; + + // Length of the message in bytes. + size_t len_; +}; + +// Variable-sized message (buf_ may be variable-sized between +// 0 and MAX_PAYLOAD_SIZE). + +class Peer_Message +{ +public: + // The maximum size of an Peer message (see Peer protocol specs for exact #). + enum { MAX_PAYLOAD_SIZE = 1024 }; + + Peer_Header header_; + + // Message payload + char buf_[MAX_PAYLOAD_SIZE]; +}; + +#endif /* PEER_MESSAGE */ diff --git a/apps/Gateway/Peer/peerd.cpp b/apps/Gateway/Peer/peerd.cpp new file mode 100644 index 00000000000..a4c1b65a406 --- /dev/null +++ b/apps/Gateway/Peer/peerd.cpp @@ -0,0 +1,36 @@ +/* Driver for the peer daemon (peerd). Note that this +// @(#)peerd.cpp 1.1 10/18/96 + + is completely generic code due to the Service Configurator + framework! */ + +#include "ace/Service_Config.h" +#include "Gateway_Handler.h" + +int +main (int argc, char *argv[]) +{ + ACE_Service_Config daemon; + + if (daemon.open (argc, argv) == -1) + { + if (errno != ENOENT) + ACE_ERROR ((LM_ERROR, "%p\n%a", "open", 1)); + else // Use static binding. + { + static char *l_argv[3] = { "-d", "-p", "10002" }; + + ACE_Service_Object *so = _alloc_peerd (); + + + if (so->init (3, l_argv) == -1) + ACE_ERROR ((LM_ERROR, "%p\n%a", "init", 1)); + } + } + + /* Run forever, performing the configured services (until SIGINT/SIGQUIT occurs) */ + + daemon.run_reactor_event_loop (); + + return 0; +} diff --git a/apps/Gateway/Peer/svc.conf b/apps/Gateway/Peer/svc.conf new file mode 100644 index 00000000000..0f663260445 --- /dev/null +++ b/apps/Gateway/Peer/svc.conf @@ -0,0 +1,3 @@ +#static Svc_Manager "-d -p 291" +dynamic Peer1 Service_Object *.shobj/Gateway_Handler.so:_alloc_peerd() active "-p 10004" +#dynamic Peer2 Service_Object *.shobj/Gateway_Handler.so:_alloc_peerd() active "-p 10003" diff --git a/apps/Gateway/README b/apps/Gateway/README new file mode 100644 index 00000000000..7a198d9d07b --- /dev/null +++ b/apps/Gateway/README @@ -0,0 +1,80 @@ +OVERVIEW + +This directory contains source code for a prototype application-level +gateway implemented with ACE. This prototype was developed in my +cs422 grad OS class at Washington University. + +DIRECTORY STRUCTURE + +There are 2 directories: + +Gateway + + -- The application Gateway, which must be started *after* all + the Peers described below). This process reads the + cc_config and rt_config files. The cc_config file tells + the Gateway what connections to establish with which hosts + on which ports, etc. The rt_config file tells the Gateway + how to route data coming from "sources" to the appropriate + "destinations." + +Peer + + -- The test driver programs that must be started *before* the + Gateway. To do anything interesting you'll need at + least two Peers: one for supplying events and one for consuming + them. In the configuration files, these two types of Peers + are designated as follows: + + (1) Input Peers (designated by an "I" in the Gateway's + cc_config configuration file). These Peers are "sources" + of messages to the Gateway. + + (2) Output Peers (designated by an "O" in the Gateway's + cc_config file). These Peers are "destinations" of + messages routed by the Gateway (routing is based on + the settings in the rt_config configuration file). + +RUNNING THE TESTS + +To run the tests do the following: + +1. Compile everything (i.e., first compile the ACE libraries, then + compile the the Gateway directories). + +2. Edit the rt_config and cc_config files as discussed above. + +3. Start up the Peers (peerd). You can start up as many as you + like, as per the cc_config file, but you'll need at least + two (one for supplying input and one for consuming output). I + typically start up each peer in a different window on a different + machine. The peers should print out some diagnostic info and then + block awaiting connections from the Gateway. + +4. Start up the Gateway (gatewayd). This will print out + a bunch of messages as it reads the config files and connects + to all the Peers. Assuming everything works, then all the + Peers will be connected. If some of the Peers aren't set up + correctly then the Gateway will use an exponential backoff + algorithm to attempt to reestablish those connections. + +5. Once the Gateway has connected with all the Peers you can send + messages from Input Peers by typing commands in the Peer window. + This input will be sent to the Gateway, which will forward + the message to all Output Peers that have "subscribed" to receive + these messages. + + Note that if you type ^C in a Peer window the Peer will shutdown + its handlers and exit. The Gateway will detect this and will + start trying to reestablish the connection using the same + exponential backoff algorithm it used for the initial connection + establishment. + +7. When you want to terminate a Gateway, just type ^C + and the process will shut down gracefully. + +Please let me know if there are any questions. + + Doug + +schmidt@cs.wustl.edu diff --git a/apps/Makefile b/apps/Makefile new file mode 100644 index 00000000000..30fddeb5b26 --- /dev/null +++ b/apps/Makefile @@ -0,0 +1,30 @@ +#---------------------------------------------------------------------------- +# @(#)Makefile 1.1 10/18/96 +# +# Makefile for the apps directory +#---------------------------------------------------------------------------- + +#---------------------------------------------------------------------------- +# Local macros +#---------------------------------------------------------------------------- + +INFO = README + +DIRS = Synch-Benchmarks \ + Gateway + +# The following directory isn't compiled by default since haven't +# finished integrating it into ACE... +# +# Orbix-Examples + +#---------------------------------------------------------------------------- +# Include macros and targets +#---------------------------------------------------------------------------- + +include $(WRAPPER_ROOT)/include/makeinclude/wrapper_macros.GNU +include $(WRAPPER_ROOT)/include/makeinclude/macros.GNU +include $(WRAPPER_ROOT)/include/makeinclude/rules.common.GNU +include $(WRAPPER_ROOT)/include/makeinclude/rules.nested.GNU +include $(WRAPPER_ROOT)/include/makeinclude/rules.nolocal.GNU + diff --git a/apps/Orbix-Examples/Event_Comm/Consumer/Input_Handler.cpp b/apps/Orbix-Examples/Event_Comm/Consumer/Input_Handler.cpp new file mode 100644 index 00000000000..29d8b1218b2 --- /dev/null +++ b/apps/Orbix-Examples/Event_Comm/Consumer/Input_Handler.cpp @@ -0,0 +1,130 @@ +#include "Input_Handler.h" +// @(#)Input_Handler.cpp 1.1 10/18/96 + +#include "Notification_Receiver_Handler.h" + +#if defined (ACE_HAS_ORBIX) + +Input_Handler::~Input_Handler (void) +{ + ACE_DEBUG ((LM_DEBUG, "closing down Input_Handler::~Input_Handler\n")); + this->handle_close (); +} + +int +Input_Handler::consumer_initiated_shutdown (void) +{ + return this->consumer_initiated_shutdown_; +} + +void +Input_Handler::consumer_initiated_shutdown (int c) +{ + this->consumer_initiated_shutdown_ = c; +} + +ACE_HANDLE +Input_Handler::get_handle (void) const +{ + return this->handle_; +} + +int +Input_Handler::handle_close (ACE_HANDLE, ACE_Reactor_Mask) +{ + ACE_DEBUG ((LM_DEBUG, "closing down Consumer::Input_Handler\n")); + + Event_Comm::Notification_Receiver *receiver = this->receiver_handler_->receiver (); + Event_Comm::Notifier *notifier = this->receiver_handler_->notifier (); + + if (this->consumer_initiated_shutdown ()) + { + // Only try to unsubscribe if the Consumer initiated the + // shutdown. Otherwise, the Supplier initiated it and + // it has probably gone away by now! + TRY { + // Gracefully shutdown the Receiver by removing it + // from the Notifier's internal map. + + notifier->unsubscribe (receiver, "", IT_X); + } CATCHANY { + cerr << IT_X << endl; + } ENDTRY; + } + // Don't execute a callback here otherwise we'll recurse indefinitely! + if (ACE_Service_Config::reactor ()->remove_handler (this, ACE_Event_Handler::READ_MASK + | ACE_Event_Handler::DONT_CALL) == -1) + ACE_ERROR ((LM_ERROR, "%p\n", "remove_handler")); + + // *Must* be allocated dyanmically! + delete (void *) this; + return 0; +} + +Input_Handler::Input_Handler (Notification_Receiver_Handler *ch, + ACE_HANDLE handle) + : receiver_handler_ (ch), + handle_ (handle), + consumer_initiated_shutdown_ (0) +{ + if (ACE_Service_Config::reactor ()->register_handler (this, + ACE_Event_Handler::READ_MASK) == -1) + ACE_ERROR ((LM_ERROR, "Input_Handler::Input_Handler\n")); +} + +int +Input_Handler::handle_input (ACE_HANDLE h) +{ + char buf[BUFSIZ]; + ssize_t n; + + // Read up to BUFSIZ worth of data from ACE_HANDLE h. + + n = ACE_OS::read (h, buf, sizeof buf - 1); + + if (n > 0) + { + // Null terminate the buffer, replacing the '\n' with '\0'. + if (buf[n - 1] == '\n' || buf[n - 1] == EOF) + buf[n - 1] = '\0'; + else + buf[n] = '\0'; + ACE_DEBUG ((LM_DEBUG, "notifying for event %s\n", buf)); + + } + else + { + ACE_OS::strcpy (buf, "quit"); + ACE_DEBUG ((LM_DEBUG, "shutting down Input_Handler\n")); + } + + Event_Comm::Notifier *notifier = this->receiver_handler_->notifier (); + + ACE_ASSERT (notifier != 0); + + if (ACE_OS::strcmp (buf, "quit") == 0) + { + // Consumer wants to shutdown. + this->consumer_initiated_shutdown (1); + + // Tell the main event loop to shutdown. + ACE_Service_Config::end_reactor_event_loop (); + } + else + { + TRY { + Event_Comm::Notification notification; + + notification.tag_ = ACE_OS::strdup (buf); + + notifier->send_notification (notification, IT_X); + } + CATCHANY { + cerr << "Unexpected exception " << IT_X << endl; + } ENDTRY; + } + + /* NOTREACHED */ + return 0; +} +#endif /* ACE_HAS_ORBIX */ diff --git a/apps/Orbix-Examples/Event_Comm/Consumer/Input_Handler.h b/apps/Orbix-Examples/Event_Comm/Consumer/Input_Handler.h new file mode 100644 index 00000000000..d2b87d90f57 --- /dev/null +++ b/apps/Orbix-Examples/Event_Comm/Consumer/Input_Handler.h @@ -0,0 +1,71 @@ +/* -*- C++ -*- */ +// @(#)Input_Handler.h 1.1 10/18/96 + + +// ============================================================================ +// +// = LIBRARY +// EventComm +// +// = FILENAME +// Input_Handler.h +// +// = DESCRIPTION +// Subclass of ACE ACE_Service_Object that receives unsubscribes from +// the Notifier when input is received from the keyboard. +// +// = AUTHOR +// Douglas C. Schmidt (schmidt@cs.wustl.edu) +// +// ============================================================================ + +#if !defined (_INPUT_HANDLER_H) +#define _INPUT_HANDLER_ + +#include "ace/Service_Config.h" + +#if defined (ACE_HAS_ORBIX) +// Forward declaration. +class Notification_Receiver_Handler; + +class Input_Handler : public ACE_Service_Object + // = TITLE + // Handles input events generated from a keyboard. + // + // = DESCRIPTION +{ +public: + Input_Handler (Notification_Receiver_Handler *, ACE_HANDLE h = 0); + + virtual int handle_input (ACE_HANDLE); + // Dispatch the callback when events occur. + + virtual int handle_close (ACE_HANDLE = ACE_INVALID_HANDLE, + ACE_Reactor_Mask = ACE_Event_Handler::NULL_MASK); + // Close down the handler. + + int consumer_initiated_shutdown (void); + // Report whether the Consumer initiated the shutdown. + + void consumer_initiated_shutdown (int); + // Indicate that the Consumer initiated the shutdown. + +private: + ~Input_Handler (void); + // Ensure dynamic allocation. + + virtual ACE_HANDLE get_handle (void) const; + + ACE_HANDLE handle_; + // ACE_HANDLE where the input comes from. + + Notification_Receiver_Handler *receiver_handler_; + // Pointer to the <Notification_Receiver_Handler> that + // receives notifications from the <Event_Comm::Notifier>. + + int consumer_initiated_shutdown_; + // Keep track of whether the Consumer initiated the shutdown. +}; + +#endif /* ACE_HAS_ORBIX */ +#endif /* _INPUT_HANDLER_H */ diff --git a/apps/Orbix-Examples/Event_Comm/Consumer/Makefile b/apps/Orbix-Examples/Event_Comm/Consumer/Makefile new file mode 100644 index 00000000000..872b72a1880 --- /dev/null +++ b/apps/Orbix-Examples/Event_Comm/Consumer/Makefile @@ -0,0 +1,165 @@ +#---------------------------------------------------------------------------- +# @(#)Makefile 1.1 10/18/96 +# +# Makefile for the Consumer. +#---------------------------------------------------------------------------- + +#---------------------------------------------------------------------------- +# Local macros +#---------------------------------------------------------------------------- + +BIN = consumer + +FILES = Notification_Receiver_Handler \ + Input_Handler + +LSRC = $(addsuffix .cpp,$(FILES)) consumer.cpp +LOBJ = $(addsuffix .o,$(FILES)) +SHOBJ = $(addsuffix .so,$(FILES)) + +SRX = ../src/.obj + +LDLIBS = $(addprefix .shobj/,$(LOBJ)) ../src/libEvent_Comm.a +VLDLIBS = $(LDLIBS:%=%$(VAR)) + +BUILD = $(VBIN) + +#---------------------------------------------------------------------------- +# Include macros and targets +#---------------------------------------------------------------------------- + +include $(WRAPPER_ROOT)/include/makeinclude/wrapper_macros.GNU +include $(WRAPPER_ROOT)/include/makeinclude/macros.GNU +include $(WRAPPER_ROOT)/include/makeinclude/rules.common.GNU +include $(WRAPPER_ROOT)/include/makeinclude/rules.nonested.GNU +include $(WRAPPER_ROOT)/include/makeinclude/rules.lib.GNU +include $(WRAPPER_ROOT)/include/makeinclude/rules.bin.GNU +include $(WRAPPER_ROOT)/include/makeinclude/rules.local.GNU + +#---------------------------------------------------------------------------- +# Local targets +#---------------------------------------------------------------------------- + +CPPFLAGS += -I../include +VLDLIBS += -lgen + +#---------------------------------------------------------------------------- +# Dependencies +#---------------------------------------------------------------------------- + +# DO NOT DELETE THIS LINE -- g++dep uses it. +# DO NOT PUT ANYTHING AFTER THIS LINE, IT WILL GO AWAY. + +Notification_Receiver_Handler.o: Notification_Receiver_Handler.cpp \ + Notification_Receiver_Handler.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/CORBA_Handler.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Service_Config.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Reactor.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Handle_Set.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/sysincludes.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/config.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Handle_Set.i \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Timer_Queue.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Event_Handler.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Time_Value.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Synch.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Synch_T.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Timer_Queue.i \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Signal.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Set.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Thread.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Token.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Reactor.i \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Log_Msg.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Log_Record.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Log_Priority.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Log_Record.i \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Log_Msg.i \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Thread_Specific.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Thread_Specific.i \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Service_Object.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Shared_Object.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Service_Record.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Thread_Manager.h \ + ../include/Event_Comm_i.h ../include/Notification_Receiver_i.h \ + ../include/Notifier_i.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Map_Manager.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/SString.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/SString.i \ + ../include/Event_Comm.hh +Input_Handler.o: Input_Handler.cpp Input_Handler.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Service_Config.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Reactor.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Handle_Set.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/sysincludes.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/config.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Handle_Set.i \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Timer_Queue.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Event_Handler.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Time_Value.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Synch.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Synch_T.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Timer_Queue.i \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Signal.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Set.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Thread.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Token.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Reactor.i \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Log_Msg.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Log_Record.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Log_Priority.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Log_Record.i \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Log_Msg.i \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Thread_Specific.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Thread_Specific.i \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Service_Object.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Shared_Object.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Service_Record.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Thread_Manager.h \ + Notification_Receiver_Handler.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/CORBA_Handler.h \ + ../include/Event_Comm_i.h ../include/Notification_Receiver_i.h \ + ../include/Notifier_i.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Map_Manager.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/SString.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/SString.i \ + ../include/Event_Comm.hh +consumer.o: consumer.cpp \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Log_Msg.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Log_Record.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/sysincludes.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/config.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Log_Priority.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Log_Record.i \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Log_Msg.i \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Thread_Specific.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Synch.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Time_Value.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Synch_T.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Thread_Specific.i \ + Notification_Receiver_Handler.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/CORBA_Handler.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Service_Config.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Reactor.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Handle_Set.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Handle_Set.i \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Timer_Queue.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Event_Handler.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Timer_Queue.i \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Signal.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Set.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Thread.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Token.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Reactor.i \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Service_Object.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Shared_Object.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Service_Record.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Thread_Manager.h \ + ../include/Event_Comm_i.h ../include/Notification_Receiver_i.h \ + ../include/Notifier_i.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Map_Manager.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/SString.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/SString.i \ + ../include/Event_Comm.hh Input_Handler.h + +# IF YOU PUT ANYTHING HERE IT WILL GO AWAY diff --git a/apps/Orbix-Examples/Event_Comm/Consumer/Notification_Receiver_Handler.cpp b/apps/Orbix-Examples/Event_Comm/Consumer/Notification_Receiver_Handler.cpp new file mode 100644 index 00000000000..eccf499f6c0 --- /dev/null +++ b/apps/Orbix-Examples/Event_Comm/Consumer/Notification_Receiver_Handler.cpp @@ -0,0 +1,114 @@ +#include "Notification_Receiver_Handler.h" +// @(#)Notification_Receiver_Handler.cpp 1.1 10/18/96 + + +#if defined (ACE_HAS_ORBIX) + +#if defined (ACE_HAS_MT_ORBIX) +typedef ACE_MT_CORBA_Handler CORBA_HANDLER; +#else +typedef ACE_ST_CORBA_Handler CORBA_HANDLER; +#endif /* ACE_HAS_MT_ORBIX */ + +int +Notification_Receiver_Handler::handle_close (ACE_HANDLE, ACE_Reactor_Mask) +{ +// ACE_ST_CORBA_Handler::remove_service (Event_Comm_Notification_Receiver_IMPL); + + if (this->receiver_ != 0) + { + ACE_DEBUG ((LM_DEBUG, "closing down Notification_Receiver_Handler\n")); + CORBA_HANDLER::instance ()->deactivate_service (Event_Comm_Notification_Receiver_IMPL, + this->receiver_->_marker ()); + CORBA::release (this->receiver_); + this->receiver_ = 0; + CORBA::release (this->notifier_); + this->notifier_ = 0; + // *Must* be allocated dynamically in order to delete this! + delete this; + } + return 0; +} + +Notification_Receiver_Handler::Notification_Receiver_Handler (int argc, char *argv[]) + : notifier_ (0), + receiver_ (0) +{ + const char *server_name = Event_Comm_Notification_Receiver_IMPL; + char buf[BUFSIZ]; + char *receiver_marker = buf; + char *filtering_criteria; + char *host; + char *notifier_marker; + char *service_location = argv[0]; + + // First see if we have any environment variables. + filtering_criteria = ACE_OS::getenv ("FILTERING_CRITERIA"); + host = ACE_OS::getenv ("HOST"); + notifier_marker = ACE_OS::getenv ("NOTIFIER_MARKER"); + + // Then override these variables with command-line arguments. + filtering_criteria = argc > 1 ? argv[1] : ""; + host = argc > 2 ? argv[2] : "tango.cs"; + notifier_marker = argc > 3 ? argv[3] : "notifier:" Event_Comm_Notifier_IR; + + CORBA::Orbix.setDiagnostics (0); + + struct utsname name; + + // Make the marker name be the "/hostname/processid" + ACE_OS::uname (&name); + sprintf (buf, "/%s/%d", name.nodename, ACE_OS::getpid ()); + + CORBA_HANDLER::instance ()->activate_service (Event_Comm_Notification_Receiver_IMPL, + receiver_marker, service_location); + + // Create the receiver object. + this->receiver_ = new TIE_Event_Comm_Notification_Receiver (Notification_Receiver_i) + (new Notification_Receiver_i); + + this->receiver_->_marker (receiver_marker); + + ACE_ASSERT (this->receiver_); + + TRY { + // Get a binding to the notifier. + this->notifier_ = Event_Comm::Notifier::_bind (notifier_marker, host, IT_X); + + if (this->notifier_ != CORBA::OBJECT_NIL) + // Subscribe ourselves with the notifier's broker. + this->notifier_->subscribe (this->receiver_, + filtering_criteria, IT_X); + } CATCHANY { + cerr << "Unexpected exception " << IT_X << endl; + ACE_OS::exit (1); + } ENDTRY; + // Print out context. + + receiver_marker = (char *) this->receiver_->_marker (); + CORBA::BOA::activationMode mode = CORBA::Orbix.myActivationMode (); + ACE_DEBUG ((LM_DEBUG, "starting up a %spersistent server in mode %d with marker name %s\n", + mode == CORBA::BOA::persistentActivationMode ? "" : "non-", + mode, receiver_marker)); +} + +Event_Comm::Notification_Receiver * +Notification_Receiver_Handler::receiver (void) +{ + return this->receiver_; +} + +Event_Comm::Notifier * +Notification_Receiver_Handler::notifier (void) +{ + return this->notifier_; +} + +// Destroy a Receiver target object. + +Notification_Receiver_Handler::~Notification_Receiver_Handler (void) +{ + this->handle_close (-1, ACE_Event_Handler::RWE_MASK); +} + +#endif /* ACE_HAS_ORBIX */ diff --git a/apps/Orbix-Examples/Event_Comm/Consumer/Notification_Receiver_Handler.h b/apps/Orbix-Examples/Event_Comm/Consumer/Notification_Receiver_Handler.h new file mode 100644 index 00000000000..9ccd472ef1b --- /dev/null +++ b/apps/Orbix-Examples/Event_Comm/Consumer/Notification_Receiver_Handler.h @@ -0,0 +1,62 @@ +/* -*- C++ -*- */ +// @(#)Notification_Receiver_Handler.h 1.1 10/18/96 + + +// ============================================================================ +// +// = LIBRARY +// EventComm +// +// = FILENAME +// Notifier_Receiver_Handler.h +// +// = DESCRIPTION +// Subclass of Corba_Handler that sets up the Notification_Receiver handler +// for use with the ACE ACE_Reactor. +// +// = AUTHOR +// Douglas C. Schmidt (schmidt@cs.wustl.edu) +// +// ============================================================================ + +#if !defined (_NOTIFICATION_RECEIVER_HANDLER_H) +#define _NOTIFICATION_RECEIVER_HANDLER_H + +#include "ace/CORBA_Handler.h" +#include "Event_Comm_i.h" + +#if defined (ACE_HAS_ORBIX) + +class Notification_Receiver_Handler + // = TITLE + // Subclass of Corba_Handler that sets up the Notification Receiver handler + // for use with the ACE ACE_Reactor. + // + // = DESCRIPTION + // Note that this class doesn't inherit from ACE_ST_CORBA_Handler (unlike + // the Supplier's Notifier_Handler class). Instead, it uses an + // alternative interface that can be called directly. +{ +public: + Notification_Receiver_Handler (int argc, char *argv[]); + + Event_Comm::Notification_Receiver *receiver (void); + Event_Comm::Notifier *notifier (void); + + virtual int handle_close (ACE_HANDLE = ACE_INVALID_HANDLE, + ACE_Reactor_Mask = ACE_Event_Handler::NULL_MASK); + // Close down the handler. + +private: + ~Notification_Receiver_Handler (void); + // Ensure dynamic allocation. + + Event_Comm::Notification_Receiver *receiver_; + // Pointer to an IDL <Notification_Receiver> proxy object. + + Event_Comm::Notifier *notifier_; + // Pointer to an IDL <Notifier> proxy object. +}; + +#endif /* ACE_HAS_ORBIX */ +#endif /* _NOTIFICATION_RECEIVER_HANDLER_H */ diff --git a/apps/Orbix-Examples/Event_Comm/Consumer/consumer.cpp b/apps/Orbix-Examples/Event_Comm/Consumer/consumer.cpp new file mode 100644 index 00000000000..7133a8c8749 --- /dev/null +++ b/apps/Orbix-Examples/Event_Comm/Consumer/consumer.cpp @@ -0,0 +1,114 @@ +/* -*- C++ -*- */ +// @(#)consumer.cpp 1.1 10/18/96 + +// Consumer driver for the Orbix Notification example. + +#include "ace/Log_Msg.h" +#include "Notification_Receiver_Handler.h" +#include "Input_Handler.h" + +#if defined (ACE_HAS_ORBIX) + +class Consumer : public ACE_Event_Handler +{ +public: + Consumer (int argc, char *argv[]); + ~Consumer (void); + + void run (void); + // Execute the consumer; + +private: + virtual int handle_signal (int signum, siginfo_t *, ucontext_t *); + + virtual int handle_close (ACE_HANDLE, ACE_Reactor_Mask); + + Input_Handler *ih_; + // Handler for keyboard input. + + Notification_Receiver_Handler *nrh_; + // Handler for CORBA Consumer. + + ACE_Service_Config daemon_; + // ACE server event-loop mechanism. +}; + +int +Consumer::handle_close (ACE_HANDLE, ACE_Reactor_Mask) +{ + ACE_DEBUG ((LM_DEBUG, "closing down Consumer\n")); + return 0; +} + +int +Consumer::handle_signal (int signum, siginfo_t *, ucontext_t *) +{ + ACE_DEBUG ((LM_DEBUG, "%S\n", signum)); + + // Indicate that the consumer initiated the shutdown. + this->ih_->consumer_initiated_shutdown (1); + + // Shut down the event loop. + ACE_Service_Config::end_reactor_event_loop (); + return 0; +} + +// Run the event loop until someone calls +// calls ACE_Service_Config::end_reactor_event_loop(). + +void +Consumer::run (void) +{ + if (ACE_Service_Config::run_reactor_event_loop () == -1) + ACE_ERROR ((LM_ERROR, "%p\n", "run_reactor_event_loop")); +} + +Consumer::Consumer (int argc, char *argv[]) + : ih_ (0), + nrh_ (0) +{ + // Initialize the server. + if (this->daemon_.open (argc, argv) == -1) + { + if (errno == ENOENT) // There's no svc.conf file, so use static linking... + { + ACE_DEBUG ((LM_DEBUG, "no config file, using static binding\n")); + // The constructor registers the handlers... + this->nrh_ = new Notification_Receiver_Handler (argc, argv); + ACE_ASSERT (this->nrh_ != 0); + this->ih_ = new Input_Handler (this->nrh_); + ACE_ASSERT (this->ih_ != 0); + } + else + ACE_ERROR ((LM_ERROR, "%p\n%a", "open", 1)); + } + + if (ACE_Service_Config::reactor ()->register_handler (SIGINT, this) == -1) + ACE_ERROR ((LM_ERROR, "%p\n", "register_handler")); +} + +Consumer::~Consumer (void) +{ + // Free up the handlers if they were statically bound. + this->ih_->handle_close (); + this->nrh_->handle_close (); +} + +int +main (int argc, char *argv[]) +{ + // Initialize the supplier and consumer object references. + Consumer consumer (argc, argv); + + // Loop forever handling events. + consumer.run (); + + return 0; +} +#else /* !defined ACE_HAS_ORBIX */ +int +main (int argc, char *argv[]) +{ + ACE_ERROR_RETURN ((LM_ERROR, "you must have Orbix to run application %s\n", argv[0]), 1); +} +#endif /* ACE_HAS_ORBIX */ diff --git a/apps/Orbix-Examples/Event_Comm/Makefile b/apps/Orbix-Examples/Event_Comm/Makefile new file mode 100644 index 00000000000..3e0c8ae2dd8 --- /dev/null +++ b/apps/Orbix-Examples/Event_Comm/Makefile @@ -0,0 +1,26 @@ +#---------------------------------------------------------------------------- +# @(#)Makefile 1.1 10/18/96 +# +# Makefile for the consumer/supplier notification application +#---------------------------------------------------------------------------- + +#---------------------------------------------------------------------------- +# Local macros +#---------------------------------------------------------------------------- + +INFO = README + +DIRS = libsrc \ + Consumer \ + Supplier + +#---------------------------------------------------------------------------- +# Include macros and targets +#---------------------------------------------------------------------------- + +include $(WRAPPER_ROOT)/include/makeinclude/wrapper_macros.GNU +include $(WRAPPER_ROOT)/include/makeinclude/macros.GNU +include $(WRAPPER_ROOT)/include/makeinclude/rules.common.GNU +include $(WRAPPER_ROOT)/include/makeinclude/rules.nested.GNU +include $(WRAPPER_ROOT)/include/makeinclude/rules.nolocal.GNU + diff --git a/apps/Orbix-Examples/Event_Comm/README b/apps/Orbix-Examples/Event_Comm/README new file mode 100644 index 00000000000..1bd7b5d8c45 --- /dev/null +++ b/apps/Orbix-Examples/Event_Comm/README @@ -0,0 +1,109 @@ +OVERVIEW + +This directory contains source code for a prototype CORBA-based +distributed notification mechanism. This mechanism implements a +"publish/subscribe" communication protocol. It allows Suppliers to +pass messages containing object references to a dynamically managed +group of Consumers. This is similar to the OMG COSS Event Service, +though not as sophisticated. + +This example also illustrates how to integrate Orbix with the ACE +libraries. + +DIRECTORY STRUCTURE + +There are 4 directories: + +Supplier + + -- The supplier test driver, which must be started + first. It has an instance of an IDL Notifier + object. This object accepts subscriptions from Consumers + and forwards events sent to it either via Consumers or + via its standard input. + + The Supplier must be registered with the ORB using the + following command: + + % putit Event_Comm_Notifier <pathname>/supplier + +Consumer + + -- The consumer test driver, which must be started + after the Supplier. It has an instance of an + IDL Notification_Receiver object. This object is + used to receive notifications from the Notifier object + residing in the Supplier. When the Consumer starts up it + gets an object reference to the Supplier's Notifier. + It then subscribes its Notification_Receiver object with + the Supplier's Notifier by passing an object reference. + + In addition to passing an object reference to a + Notification_Receiver, the Consumer also may specify a + filtering criteria, which is a regular expression. If + the filtering criteria is the string "" then the Notifier + will send all Notifications to the Consumer (i.e., "" is + treated as a "wildcard"). Otherwise, the filtering + criteria is considered to be a regular expression, + and only those Notification tags that match the regular + expression will be forwarded to the Consumer. The regular + expressions are those used by ed(1) (see the regexp(5) + manual page for more info). + + The Consumer must be registered with the ORB + using the following command: + + % putit Event_Comm_Notification_Receiver <pathname>/consumer + +include + + -- This contains links to the appropriate header + files. + +libsrc + + -- This contains the IDL files and IDL implementation + classes that support the distributed notification scheme. + These are shared by the Consumer and Supplier. + +RUNNING THE TESTS + +To run the tests do the following: + +1. Compile everything. + +2. Start up the Orbix daemon (orbixd) if it's not already + running. + +3. Register the Consumer (i.e., Notification_Receiver) and Supplier + (i.e., Notifier) with the Orbix daemon (orbixd), as described + above. + +4. Start the Supplier/supplier executable. + +5. Start up as many copies of the Consumer/consumer as you'd like. + Typically, I run each one in its own window. If you'd like to use + different machines make sure that you start up the Orbix daemon on + each one and register the Consumer. + +6. Once the Consumers have subscribed you can send them info by typing + commands in the Supplier window. These will be sent to all the + Consumers who have subscribed. Likewise, you can send messages + from a Consumer to all other Consumers by typing messages in a + Consumer window. + + Note that if you type "quit", ^D, or ^C in a Consumer window the + Consumer will unsubscribe and shutdown its handlers and exit. + Likewise, if you type "quit", ^D, or ^C in the Supplier window + the Supplier will disconnect all of its Consumers and exit. + When a Consumer is disconnected from its Supplier it automatically + shuts itself down. + +7. When you want to terminate a Consumer or a Supplier, just type ^C + and the process will shut down gracefully. + +Please let me know if there are any questions. + + Doug + +schmidt@cs.wustl.edu diff --git a/apps/Orbix-Examples/Event_Comm/Supplier/Input_Handler.cpp b/apps/Orbix-Examples/Event_Comm/Supplier/Input_Handler.cpp new file mode 100644 index 00000000000..96a89f0ac6b --- /dev/null +++ b/apps/Orbix-Examples/Event_Comm/Supplier/Input_Handler.cpp @@ -0,0 +1,120 @@ +#include "Event_Comm.hh" +// @(#)Input_Handler.cpp 1.1 10/18/96 + +#include "Notifier_Handler.h" +#include "Input_Handler.h" + +#if defined (ACE_HAS_ORBIX) + +int +Input_Handler::handle_close (ACE_HANDLE, ACE_Reactor_Mask) +{ + ACE_DEBUG ((LM_DEBUG, "closing down Supplier::Input_Handler\n")); + + Event_Comm::Notifier *notifier = this->notifier_->notifier (); + ACE_ASSERT (notifier != 0); + + ACE_OS::fclose (this->fp_); + + TRY { + // Disconnect all the consumers gracefully. + notifier->send_disconnect ("quit", IT_X); + } CATCHANY { + cerr << IT_X << endl; + } ENDTRY; + + // Don't execute a callback here otherwise we'll recurse indefinitely! + if (ACE_Service_Config::reactor ()->remove_handler (this, ACE_Event_Handler::READ_MASK + | ACE_Event_Handler::DONT_CALL) == -1) + ACE_ERROR ((LM_ERROR, "%p\n", "remove_handler")); + + // *Must* be allocated dyanmically! + delete (void *) this; + return 0; +} + +Input_Handler::Input_Handler (Notifier_Handler *notifier, + ACE_HANDLE handle) // Use stdin by default. + : notifier_ (notifier), + handle_ (handle) +{ + // Register ourselves with the ACE_Reactor so that input events + // cause our handle_input() method to be dispatched automatically. + + if (ACE_Service_Config::reactor ()->register_handler (this, + ACE_Event_Handler::READ_MASK) == -1) + ACE_ERROR ((LM_ERROR, "%p\n", "register_handler")); + + this->fp_ = ACE_OS::fdopen (handle, "r"); + + if (this->fp_ == 0) + ACE_ERROR ((LM_ERROR, "%p\n", "fdopen")); +} + +Input_Handler::~Input_Handler (void) +{ + ACE_DEBUG ((LM_DEBUG, "closing down Input_Handler::~Input_Handler\n")); + this->handle_close (); +} + +ACE_HANDLE +Input_Handler::get_handle (void) const +{ + return this->handle_; +} + +// Frame input events and notify <Consumers>. + +int +Input_Handler::handle_input (ACE_HANDLE h) +{ + char buf[BUFSIZ]; + + // Read up to BUFSIZ worth of data from ACE_HANDLE h. + + if (ACE_OS::fgets (buf, sizeof buf - 1, this->fp_) == 0) + { + ACE_OS::strcpy (buf, "quit"); + ACE_DEBUG ((LM_DEBUG, "shutting down Input_Handler\n")); + } + else + { + size_t n = ACE_OS::strlen (buf); + + // Null terminate the buffer, replacing the '\n' with '\0'. + if (buf[n - 1] == '\n' || buf[n - 1] == EOF) + buf[n - 1] = '\0'; + else + buf[n] = '\0'; + ACE_DEBUG ((LM_DEBUG, "notifying for event %s\n", buf)); + } + + Event_Comm::Notifier *notifier = this->notifier_->notifier (); + ACE_ASSERT (notifier != 0); + + if (ACE_OS::strcmp (buf, "quit") == 0) + // Tell the main event loop to shutdown. + ACE_Service_Config::end_reactor_event_loop (); + else + { + // Use the notifier to notify Consumers. + TRY { + Event_Comm::Notification notification; + + // Pass the buf over in the tag field. + notification.tag_ = ACE_OS::strdup (buf); + + // This is where the "any" value goes or the object reference... + // notification.value_ = ... + + // Forward <Notification> to all <Notification_Receivers>. + notifier->send_notification (notification, IT_X); + } + CATCHANY { + cerr << "unexpected exception " << IT_X << endl; + } ENDTRY; + } + return 0; +} + +#endif /* ACE_HAS_ORBIX */ diff --git a/apps/Orbix-Examples/Event_Comm/Supplier/Input_Handler.h b/apps/Orbix-Examples/Event_Comm/Supplier/Input_Handler.h new file mode 100644 index 00000000000..7bd05bbb5db --- /dev/null +++ b/apps/Orbix-Examples/Event_Comm/Supplier/Input_Handler.h @@ -0,0 +1,70 @@ +/* -*- C++ -*- */ +// @(#)Input_Handler.h 1.1 10/18/96 + + +// ============================================================================ +// +// = LIBRARY +// EventComm +// +// = FILENAME +// Input_Handler.h +// +// = DESCRIPTION +// Handle input from the keyboard. +// +// = AUTHOR +// Douglas C. Schmidt (schmidt@cs.wustl.edu) +// +// ============================================================================ + +#if !defined (_INPUT_HANDLER_H) +#define _INPUT_HANDLER_H + +#include "ace/Service_Config.h" + +#if defined (ACE_HAS_ORBIX) + +// Forward declaration. +class Notifier_Handler; + +class Input_Handler : public ACE_Service_Object + // = TITLE + // Handles input events generated from a keyboard. + // + // = DESCRIPTION + // The events are currently framed and forwarded to + // all Consumers. In the future, we will need to + // be more selective and only send to those Consumers + // whose filtering criteria matches! +{ +public: + Input_Handler (Notifier_Handler *, ACE_HANDLE = 0); // Use stdin by default. + + virtual int handle_input (ACE_HANDLE); + // Frame input events and notify <Consumers>. + + virtual int handle_close (ACE_HANDLE = ACE_INVALID_HANDLE, + ACE_Reactor_Mask = ACE_Event_Handler::NULL_MASK); + // Close down the handler. + +protected: + virtual ACE_HANDLE get_handle (void) const; + + ACE_HANDLE handle_; + // ACE_HANDLE where the input comes from. + + Notifier_Handler *notifier_; + // Pointer to a <Notifier_Handler> that's used to inform + // Consumers that events of interest have occurred. + + FILE *fp_; + // Pointer to an input ACE_FILE. + +private: + ~Input_Handler (void); + // Ensure dynamic allocation. +}; + +#endif /* ACE_HAS_ORBIX */ +#endif /* _INPUT_HANDLER_H */ diff --git a/apps/Orbix-Examples/Event_Comm/Supplier/Makefile b/apps/Orbix-Examples/Event_Comm/Supplier/Makefile new file mode 100644 index 00000000000..4ded1a20e24 --- /dev/null +++ b/apps/Orbix-Examples/Event_Comm/Supplier/Makefile @@ -0,0 +1,164 @@ +#---------------------------------------------------------------------------- +# @(#)Makefile 1.1 10/18/96 +# +# Makefile for the Notifier. +#---------------------------------------------------------------------------- + +#---------------------------------------------------------------------------- +# Local macros +#---------------------------------------------------------------------------- + +BIN = supplier + +FILES = Input_Handler \ + Notifier_Handler + +LSRC = $(addsuffix .cpp,$(FILES)) supplier.cpp +LOBJ = $(addsuffix .o,$(FILES)) +SHOBJ = $(addsuffix .so,$(FILES)) + +SRX = ../src/.obj + +LDLIBS = $(addprefix .shobj/,$(LOBJ)) ../src/libEvent_Comm.a + +VLDLIBS = $(LDLIBS:%=%$(VAR)) + +BUILD = $(VBIN) + +#---------------------------------------------------------------------------- +# Include macros and targets +#---------------------------------------------------------------------------- + +include $(WRAPPER_ROOT)/include/makeinclude/wrapper_macros.GNU +include $(WRAPPER_ROOT)/include/makeinclude/macros.GNU +include $(WRAPPER_ROOT)/include/makeinclude/rules.common.GNU +include $(WRAPPER_ROOT)/include/makeinclude/rules.nonested.GNU +include $(WRAPPER_ROOT)/include/makeinclude/rules.lib.GNU +include $(WRAPPER_ROOT)/include/makeinclude/rules.bin.GNU +include $(WRAPPER_ROOT)/include/makeinclude/rules.local.GNU + +#---------------------------------------------------------------------------- +# Local targets +#---------------------------------------------------------------------------- + +CPPFLAGS += -I../include +VLDLIBS += -lgen + +#---------------------------------------------------------------------------- +# Dependencies +#---------------------------------------------------------------------------- + +# DO NOT DELETE THIS LINE -- g++dep uses it. +# DO NOT PUT ANYTHING AFTER THIS LINE, IT WILL GO AWAY. + +Input_Handler.o: Input_Handler.cpp ../include/Event_Comm.hh Notifier_Handler.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/CORBA_Handler.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Service_Config.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Reactor.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Handle_Set.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/sysincludes.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/config.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Handle_Set.i \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Timer_Queue.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Event_Handler.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Time_Value.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Synch.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Synch_T.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Timer_Queue.i \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Signal.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Set.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Thread.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Token.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Reactor.i \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Log_Msg.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Log_Record.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Log_Priority.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Log_Record.i \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Log_Msg.i \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Thread_Specific.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Thread_Specific.i \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Service_Object.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Shared_Object.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Service_Record.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Thread_Manager.h \ + ../include/Event_Comm_i.h ../include/Notification_Receiver_i.h \ + ../include/Notifier_i.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Map_Manager.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/SString.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/SString.i \ + Input_Handler.h +Notifier_Handler.o: Notifier_Handler.cpp Notifier_Handler.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/CORBA_Handler.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Service_Config.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Reactor.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Handle_Set.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/sysincludes.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/config.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Handle_Set.i \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Timer_Queue.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Event_Handler.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Time_Value.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Synch.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Synch_T.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Timer_Queue.i \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Signal.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Set.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Thread.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Token.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Reactor.i \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Log_Msg.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Log_Record.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Log_Priority.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Log_Record.i \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Log_Msg.i \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Thread_Specific.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Thread_Specific.i \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Service_Object.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Shared_Object.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Service_Record.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Thread_Manager.h \ + ../include/Event_Comm_i.h ../include/Notification_Receiver_i.h \ + ../include/Notifier_i.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Map_Manager.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/SString.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/SString.i \ + ../include/Event_Comm.hh +supplier.o: supplier.cpp \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Service_Config.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Reactor.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Handle_Set.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/sysincludes.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/config.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Handle_Set.i \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Timer_Queue.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Event_Handler.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Time_Value.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Synch.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Synch_T.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Timer_Queue.i \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Signal.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Set.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Thread.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Token.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Reactor.i \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Log_Msg.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Log_Record.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Log_Priority.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Log_Record.i \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Log_Msg.i \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Thread_Specific.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Thread_Specific.i \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Service_Object.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Shared_Object.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Service_Record.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Thread_Manager.h \ + Notifier_Handler.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/CORBA_Handler.h \ + ../include/Event_Comm_i.h ../include/Notification_Receiver_i.h \ + ../include/Notifier_i.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Map_Manager.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/SString.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/SString.i \ + ../include/Event_Comm.hh Input_Handler.h + +# IF YOU PUT ANYTHING HERE IT WILL GO AWAY diff --git a/apps/Orbix-Examples/Event_Comm/Supplier/Notifier_Handler.cpp b/apps/Orbix-Examples/Event_Comm/Supplier/Notifier_Handler.cpp new file mode 100644 index 00000000000..84e9b3380bf --- /dev/null +++ b/apps/Orbix-Examples/Event_Comm/Supplier/Notifier_Handler.cpp @@ -0,0 +1,66 @@ +#include "Notifier_Handler.h" +// @(#)Notifier_Handler.cpp 1.1 10/18/96 + + +#if defined (ACE_HAS_ORBIX) + +#if defined (ACE_HAS_MT_ORBIX) +typedef ACE_MT_CORBA_Handler CORBA_HANDLER; +#else +typedef ACE_ST_CORBA_Handler CORBA_HANDLER; +#endif /* ACE_HAS_MT_ORBIX */ + +int +Notifier_Handler::handle_close (ACE_HANDLE, ACE_Reactor_Mask) +{ + if (this->notifier_ != 0) + { + ACE_DEBUG ((LM_DEBUG, "closing down Notifier_Handler\n")); + CORBA_HANDLER::instance ()->deactivate_service (Event_Comm_Notifier_IMPL, + this->notifier_->_marker ()); + CORBA::release (this->notifier_); + this->notifier_ = 0; + // *Must* be allocated dyanmically! + delete this; + } + return 0; +} + +Event_Comm::Notifier * +Notifier_Handler::notifier (void) +{ + return this->notifier_; +} + +void +Notifier_Handler::notifier (Event_Comm::Notifier *notifier) +{ + if (this->notifier_ != notifier) + { + CORBA::release (this->notifier_); + this->notifier_ = notifier; + } +} + +// Create and initialize a Notifier target object. + +Notifier_Handler::Notifier_Handler (const char *service_location, + const char *marker, + int putit) +{ + CORBA_HANDLER::instance ()->activate_service (Event_Comm_Notifier_IMPL, + putit ? marker : 0, service_location); + + // Create a notifier object using the implementation class Notifier_i. + this->notifier_ = + new TIE_Event_Comm_Notifier (Notifier_i) (new Notifier_i, marker); +} + +// Destroy a Notifier target object. + +Notifier_Handler::~Notifier_Handler (void) +{ + this->handle_close (); +} + +#endif /* ACE_HAS_ORBIX */ diff --git a/apps/Orbix-Examples/Event_Comm/Supplier/Notifier_Handler.h b/apps/Orbix-Examples/Event_Comm/Supplier/Notifier_Handler.h new file mode 100644 index 00000000000..5d0be45924c --- /dev/null +++ b/apps/Orbix-Examples/Event_Comm/Supplier/Notifier_Handler.h @@ -0,0 +1,57 @@ +/* -*- C++ -*- */ +// @(#)Notifier_Handler.h 1.1 10/18/96 + + +// ============================================================================ +// +// = LIBRARY +// EventComm +// +// = FILENAME +// Notifier_Handler.h +// +// = DESCRIPTION +// Integrate CORBA with the ACE ACE_Reactor. +// +// = AUTHOR +// Douglas C. Schmidt (schmidt@cs.wustl.edu) +// +// ============================================================================ + +#if !defined (_NOTIFIER_HANDLER_H) +#define _NOTIFIER_HANDLER_H + +#include "ace/CORBA_Handler.h" +#include "Event_Comm_i.h" + +#if defined (ACE_HAS_ORBIX) + +class Notifier_Handler + // = TITLE + // Integrate CORBA with the ACE ACE_Reactor. + // + // = DESCRIPTION + // +{ +public: + Notifier_Handler (const char *service_location, + const char *marker = "notifier", + int putit = 1); // Default marker name. + + Event_Comm::Notifier *notifier (void); + void notifier (Event_Comm::Notifier *); + + virtual int handle_close (ACE_HANDLE = ACE_INVALID_HANDLE, + ACE_Reactor_Mask = ACE_Event_Handler::NULL_MASK); + // Close down the handler. + +private: + ~Notifier_Handler (void); + // Ensure dynamic allocation. + + Event_Comm::Notifier *notifier_; + // Pointer to an a <Event_Comm::Notifier> object. +}; + +#endif /* ACE_HAS_ORBIX */ +#endif /* _NOTIFIER_HANDLER_H */ diff --git a/apps/Orbix-Examples/Event_Comm/Supplier/supplier.cpp b/apps/Orbix-Examples/Event_Comm/Supplier/supplier.cpp new file mode 100644 index 00000000000..927ab73a022 --- /dev/null +++ b/apps/Orbix-Examples/Event_Comm/Supplier/supplier.cpp @@ -0,0 +1,116 @@ +/* -*- C++ -*- */ +// @(#)supplier.cpp 1.1 10/18/96 + +// Supplier driver for the Orbix Publish/Subscribe example. + +// The executable file generated from this code should be registered +// (under the name 'logger') using the 'putit' command. + +#include "ace/Service_Config.h" +#include "ace/Log_Msg.h" +#include "Notifier_Handler.h" +#include "Input_Handler.h" + +#if defined (ACE_HAS_ORBIX) + +class Supplier : public ACE_Event_Handler +{ +public: + Supplier (int argc, char *argv[]); + ~Supplier (void); + + void run (void); + // Execute the supplier. + +private: + virtual int handle_signal (int signum, siginfo_t *, ucontext_t *); + + virtual int handle_close (ACE_HANDLE, ACE_Reactor_Mask); + + Input_Handler *ih_; + // Handler for keyboard input. + + Notifier_Handler *nh_; + // Handler for CORBA Notifier. + + ACE_Service_Config daemon_; + // ACE server event-loop mechanism. +}; + +int +Supplier::handle_close (ACE_HANDLE, ACE_Reactor_Mask) +{ + ACE_DEBUG ((LM_DEBUG, "closing down Supplier\n")); + return 0; +} + +int +Supplier::handle_signal (int signum, siginfo_t *, ucontext_t *) +{ + ACE_DEBUG ((LM_DEBUG, "%S\n", signum)); + ACE_Service_Config::end_reactor_event_loop (); + return 0; +} + +void +Supplier::run (void) +{ + if (ACE_Service_Config::run_reactor_event_loop () == -1) + ACE_ERROR ((LM_ERROR, "%p\n", "run_reactor_event_loop")); +} + +Supplier::Supplier (int argc, char *argv[]) + : ih_ (0), + nh_ (0) +{ + // Initialize the server. + if (this->daemon_.open (argc, argv) == -1) + { + if (errno == ENOENT) // There's no svc.conf file, so use static linking... + { + ACE_DEBUG ((LM_DEBUG, "no config file, using static binding\n")); + // The constructor registers the handlers... + int putit = argc > 1 ? 1 : 0; + + // Pass in program exec name to use a service_location! + this->nh_ = new Notifier_Handler (argv[0], "notifier", putit); + ACE_ASSERT (this->nh_ != 0); + this->ih_ = new Input_Handler (this->nh_); + ACE_ASSERT (this->ih_ != 0); + } + else + ACE_ERROR ((LM_ERROR, "%p\n%a", "open", 1)); + } + + ACE_DEBUG ((LM_DEBUG, "starting up server %s\n", + CORBA::Orbix.myImplementationName ())); + + if (ACE_Service_Config::reactor ()->register_handler (SIGINT, this) == -1) + ACE_ERROR ((LM_ERROR, "%p\n", "register_handler")); +} + +Supplier::~Supplier (void) +{ + // Free up the handlers if they were statically bound. + this->ih_->handle_close (); + this->nh_->handle_close (); +} + +int +main (int argc, char *argv[]) +{ + // Initialize server daemon. + Supplier supplier (argc, argv); + + // Loop forever handling events. + supplier.run (); + + return 0; +} +#else /* !defined ACE_HAS_ORBIX */ +int +main (int argc, char *argv[]) +{ + ACE_ERROR_RETURN ((LM_ERROR, "you must have Orbix to run application %s\n", argv[0]), 1); +} +#endif /* ACE_HAS_ORBIX */ diff --git a/apps/Orbix-Examples/Event_Comm/include/Event_Comm.hh b/apps/Orbix-Examples/Event_Comm/include/Event_Comm.hh new file mode 100644 index 00000000000..85ad256da2a --- /dev/null +++ b/apps/Orbix-Examples/Event_Comm/include/Event_Comm.hh @@ -0,0 +1,887 @@ + +#ifndef Event_Comm_hh +#define Event_Comm_hh + +#include <CORBA.h> + +#include <string.h> + +class Event_Comm { +public: + +#ifndef Event_Comm_Notification_defined +#define Event_Comm_Notification_defined + + struct Notification { + CORBA::String_mgr tag_; + + void encodeOp (CORBA::Request &IT_r) const; + void decodeOp (CORBA::Request &IT_r); + void decodeInOutOp (CORBA::Request &IT_r); + static void* IT_anySupport (CORBA::Request &IT_r, + void *&, void*, const CORBA::Flags&); + static const void *IT_fn; + }; + + static const CORBA::TypeCode_ptr _tc_Notification; + +#ifndef Event_Comm_NotificationVarH +#define Event_Comm_NotificationVarH + +#ifndef Event_Comm_NotificationvPtr +#define Event_Comm_NotificationvPtr +typedef Notification* Notification_vPtr; +#endif + +class Notification_var : public CORBA::_var +{ + public: + + Notification_var () { + _ptr = NULL; + } + + Notification_var (Notification *IT_p) { + _ptr = IT_p; + } + + Notification_var (const Notification_var &IT_s) { + if (!IT_s._ptr) { + _ptr = IT_s._ptr; + return; + } + _ptr = new Notification (*(IT_s._ptr)); + } + + Notification_var &operator= (Notification *IT_p) { + if (_ptr != IT_p) { + delete _ptr; + } + _ptr = IT_p; + return (*this); + } + + Notification_var &operator= (const Notification_var &IT_s) { + if (_ptr != IT_s._ptr) { + delete _ptr; + } + _ptr = new Notification (*(IT_s._ptr)); + return (*this); + } + + ~Notification_var () { + delete _ptr; + } + + Notification* operator-> () { + return _ptr; + } + + operator const Notification_vPtr () const { return _ptr;} + operator Notification_vPtr& () { return _ptr;} + operator Notification& () const { return * _ptr;} + + protected: + Notification *_ptr; + private: + Notification_var &operator= (const CORBA::_var &IT_s); + Notification_var (const CORBA::_var &IT_s); +}; + +#endif + + +#endif + + +#ifndef _Event_Comm_Notification_Receiver_defined +#define _Event_Comm_Notification_Receiver_defined +class Notification_Receiver_dispatch : public virtual CORBA::PPTR { +public: + + Notification_Receiver_dispatch (void *IT_p, CORBA::Object* IT_o, const char *IT_m, + CORBA::LoaderClass *IT_l, char *IT_i, void* IT_im) + : CORBA::PPTR (IT_p,IT_o,IT_m,IT_l,IT_i,IT_im) {} + + + Notification_Receiver_dispatch (char *IT_OR, void *IT_p, CORBA::Object *IT_o) + : CORBA::PPTR (IT_OR,IT_p,IT_o) {} + + + Notification_Receiver_dispatch () {} + + Notification_Receiver_dispatch (ObjectReference *IT_OR, void *IT_p, CORBA::Object *IT_o) + : CORBA::PPTR (IT_OR,IT_p,IT_o) {} + + + Notification_Receiver_dispatch (void *IT_p, CORBA::Object *IT_o, const char *IT_m, + char *IT_i, CORBA::Object* IT_ob, void* IT_im) + : CORBA::PPTR (IT_p,IT_o,IT_m,IT_i,IT_ob,IT_im) {} + + + virtual unsigned char dispatch (CORBA::Request &IT_r, + unsigned char IT_isTarget, void* IT_pp=NULL); + + +}; + +class Notification_Receiver; + +#ifndef Event_Comm_Notification_ReceiverPtr +#define Event_Comm_Notification_ReceiverPtr + + typedef Notification_Receiver* Notification_Receiver_ptr; + + typedef Notification_Receiver* Notification_ReceiverRef; + +#endif + + +#ifndef Event_Comm_Notification_ReceiverForwH +#define Event_Comm_Notification_ReceiverForwH +static CORBA::ObjectRef Notification_Receiver_getBase (void *); +static void Notification_Receiver_release (Notification_Receiver *, CORBA::Environment &IT_env); +static void Notification_Receiver_release (Notification_Receiver_ptr); +static Notification_Receiver* Notification_Receiver_duplicate (Notification_Receiver_ptr, CORBA::Environment &IT_env); +static Notification_Receiver* Notification_Receiver_duplicate (Notification_Receiver_ptr ); +static Notification_Receiver_ptr Notification_Receiver_nil (CORBA::Environment &IT_env); +static Notification_Receiver_ptr Notification_Receiver_nil (); +#endif +#define Event_Comm_Notification_Receiver_IMPL "Event_Comm_Notification_Receiver" + + +class Notification_Receiver; + + typedef Notification_Receiver Notification_ReceiverProxy; +#define Event_Comm_Notification_Receiver_IR "Event_Comm_Notification_Receiver" +#define Event_Comm_Notification_Receiver_IMPL "Event_Comm_Notification_Receiver" + +#ifndef Event_Comm_Notification_ReceiverPtr +#define Event_Comm_Notification_ReceiverPtr + + typedef Notification_Receiver* Notification_Receiver_ptr; + + typedef Notification_Receiver* Notification_ReceiverRef; + +#endif + +class Notification_Receiver: public virtual CORBA::Object { +public: + Notification_Receiver (char *IT_OR); + Notification_Receiver (ObjectReference *IT_OR); + Notification_Receiver () : CORBA::Object (1) {} +protected: + Notification_Receiver_ptr __duplicate( + CORBA::Environment &IT_env=CORBA::IT_chooseDefaultEnv ()) { + CORBA::Object::__duplicate (IT_env); + return this; + } +public: + static Notification_Receiver_ptr _duplicate( + Notification_Receiver_ptr obj, + CORBA::Environment &IT_env=CORBA::IT_chooseDefaultEnv ()) { + CORBA::EnvExcRaiser IT_raise (&IT_env); + if (CORBA::is_nil(obj, IT_env)) { + IT_raise.maybeRaise (); + return (obj); + } + Notification_Receiver_ptr IT_obj = obj->__duplicate (IT_env); + IT_raise.maybeRaise(); + return IT_obj; + } +public: + static Notification_Receiver* _bind (const char* IT_markerServer, const char* host, + const CORBA::Context &IT_c, + CORBA::Environment &IT_env=CORBA::IT_chooseDefaultEnv ()); + static Notification_Receiver* _bind (CORBA::Environment &IT_env); + static Notification_Receiver* _bind (const char* IT_markerServer=NULL, const char* host=NULL, + CORBA::Environment &IT_env=CORBA::IT_chooseDefaultEnv ()); + static Notification_Receiver* _narrow (CORBA::Object* , CORBA::Environment &IT_env=CORBA::IT_chooseDefaultEnv ()); + static Notification_Receiver_ptr _nil (CORBA::Environment &IT_env=CORBA::IT_chooseDefaultEnv ()) { + CORBA::EnvExcRaiser IT_raise (&IT_env); + IT_raise.maybeRaise(); + return (Notification_Receiver_ptr) CORBA::OBJECT_NIL;} + virtual void receive_notification (const Event_Comm::Notification& notification, CORBA::Environment &IT_env=CORBA::IT_chooseDefaultEnv ()) throw (CORBA::SystemException); + virtual void disconnect (const char * reason, CORBA::Environment &IT_env=CORBA::IT_chooseDefaultEnv ()) throw (CORBA::SystemException); +}; + + static const CORBA::TypeCode_ptr _tc_Notification_Receiver; + + static const CORBA::TypeCode_ptr _tc_Notification_ReceiverRef; + +#ifndef Event_Comm_Notification_ReceiverVarH +#define Event_Comm_Notification_ReceiverVarH + +#ifndef Event_Comm_Notification_ReceivervPtr +#define Event_Comm_Notification_ReceivervPtr +typedef Notification_Receiver* Notification_Receiver_vPtr; +#endif + +class Notification_Receiver_var : public CORBA::_var +{ + public: + + Notification_Receiver_var () { + _ptr = Notification_Receiver_nil (); + } + + Notification_Receiver_var (Notification_Receiver *IT_p) { + _ptr = IT_p; + } + + Notification_Receiver_var (const Notification_Receiver_var &IT_s) { + _ptr = Notification_Receiver_duplicate (IT_s._ptr); + } + + Notification_Receiver_var &operator= (Notification_Receiver *IT_p) { + Notification_Receiver_release (_ptr); + _ptr = IT_p; + return (*this); + } + + Notification_Receiver_var &operator= (const Notification_Receiver_var &IT_s) { + Notification_Receiver_release (_ptr); + _ptr = Notification_Receiver_duplicate (IT_s._ptr); + return (*this); + } + + ~Notification_Receiver_var () { + Notification_Receiver_release (_ptr); + } + + Notification_Receiver* operator-> () { + return _ptr; + } + + operator const Notification_Receiver_vPtr () const { return _ptr;} + operator Notification_Receiver_vPtr& () { return _ptr;} + + protected: + Notification_Receiver *_ptr; + private: + Notification_Receiver_var &operator= (const CORBA::_var &IT_s); + Notification_Receiver_var (const CORBA::_var &IT_s); + Notification_Receiver_var &operator= (const CORBA::_mgr &IT_s); + Notification_Receiver_var &operator= (const CORBA::_SeqElem &IT_s); + Notification_Receiver_var (const CORBA::_mgr &IT_s); + Notification_Receiver_var (const CORBA::_SeqElem &IT_s); +}; + +#endif + + +#ifndef Event_Comm_Notification_ReceiverMgrH +#define Event_Comm_Notification_ReceiverMgrH + +class Notification_Receiver_mgr : public CORBA::_mgr +{ + public: + + Notification_Receiver_mgr () { + _ptr = Notification_Receiver_nil (); + _release = 1; + } + + Notification_Receiver_mgr (const Notification_Receiver_mgr &IT_s) { + _ptr = Notification_Receiver_duplicate (IT_s._ptr); + _release = 1; + } + + Notification_Receiver_mgr &operator= (Notification_Receiver *IT_p) { + if (_ptr && _release) + Notification_Receiver_release (_ptr); + _ptr = IT_p; + _release = 1; + return (*this); + } + + Notification_Receiver_mgr &operator= (const Notification_Receiver_mgr &IT_s) { + if (_ptr && _release) + Notification_Receiver_release (_ptr); + _ptr = Notification_Receiver_duplicate(IT_s._ptr); + _release = 1; + return (*this); + } + + Notification_Receiver_mgr &operator= (const Notification_Receiver_var &IT_s) { + if (_ptr && _release) + Notification_Receiver_release (_ptr); + _ptr = Notification_Receiver_duplicate(IT_s); + _release = 1; + return (*this); + } + + ~Notification_Receiver_mgr () { + if (_release) + Notification_Receiver_release (_ptr); + } + + unsigned char release () { + return _release; + } + + void release (unsigned char rel) { + _release = rel; + } + + operator int () const { + CORBA::Environment env; + CORBA::EnvExcRaiser IT_raise (&env); + return (!(CORBA::is_nil((CORBA::Object*) _ptr, env))); + } + + operator void* () const { + return _ptr; + } + + operator CORBA::Object * () const { + return (CORBA::Object *) _ptr; + } + + operator Notification_Receiver* () const { + return (Notification_Receiver*) _ptr; + } + + Notification_Receiver *_ptr; + + protected: + + unsigned char _release; +}; + +#endif + +#ifndef Event_Comm_Notification_ReceiverSeqElemH +#define Event_Comm_Notification_ReceiverSeqElemH + +class Notification_Receiver_SeqElem : public CORBA::_SeqElem +{ + public: + + Notification_Receiver_SeqElem (Event_Comm::Notification_Receiver_ptr* IT_p, unsigned char rel) { + _ptr = IT_p; + _release = rel; + } + + Notification_Receiver_SeqElem &operator= (Event_Comm::Notification_Receiver_ptr IT_p) { + if (!_ptr) + return (*this); + if (*(_ptr) && _release) + Notification_Receiver_release (*(_ptr)); + *(_ptr) = IT_p; + return (*this); + } + + Notification_Receiver_SeqElem &operator= (const Notification_Receiver_SeqElem &IT_s) { + if (!_ptr|| !IT_s._ptr) + return (*this); + if (*(_ptr) && _release) + Notification_Receiver_release (*(_ptr)); + *(_ptr) = Notification_Receiver_duplicate(*(IT_s._ptr)); + return (*this); + } + + operator Event_Comm::Notification_Receiver_ptr () const +{ + if (!_ptr) + return (Notification_Receiver_nil()); + return (Event_Comm::Notification_Receiver_ptr) (*_ptr); + } + + Notification_Receiver_ptr operator->() const { return *_ptr;} + + protected: + Event_Comm::Notification_Receiver_ptr *_ptr; + unsigned char _release; +}; + +#endif + + +#define TIE_Event_Comm_Notification_Receiver(X) Event_Comm_Notification_Receiver##X + +#define DEF_TIE_Event_Comm_Notification_Receiver(X) \ + class Event_Comm_Notification_Receiver##X : public virtual Event_Comm::Notification_Receiver { \ + X* m_obj; \ + public: \ + \ + Event_Comm_Notification_Receiver##X (X *objp, const char* m="", CORBA::LoaderClass *l=0)\ + : Event_Comm::Notification_Receiver(), CORBA::Object (), m_obj(objp) { \ + m_pptr = new Event_Comm::Notification_Receiver_dispatch \ + (( Event_Comm::Notification_Receiver*)this,(CORBA::Object*)this,m,l,Event_Comm_Notification_Receiver_IR,m_obj); \ + } \ + Event_Comm_Notification_Receiver##X (CORBA::Object *IT_p, const char* IT_m="", void *IT_q=0)\ + : Event_Comm::Notification_Receiver(), CORBA::Object () { \ + m_pptr = new Event_Comm::Notification_Receiver_dispatch \ + (( Event_Comm::Notification_Receiver*)this,(CORBA::Object*)this,IT_m,Event_Comm_Notification_Receiver_IR,IT_p,IT_q); \ + m_obj = (X*)(m_pptr->getImplObj ()); \ + } \ + \ + virtual ~Event_Comm_Notification_Receiver##X () { \ + if (_okToDeleteImpl ()) delete m_obj; } \ + \ + virtual void* _deref () { \ + return m_obj; } \ + \ + virtual void receive_notification (const Event_Comm::Notification& notification, CORBA::Environment &IT_env) throw (CORBA::SystemException){\ + m_obj->receive_notification ( notification,IT_env);\ +}\ + \ + virtual void disconnect (const char * reason, CORBA::Environment &IT_env) throw (CORBA::SystemException){\ + m_obj->disconnect ( reason,IT_env);\ +}\ + \ + }; + + +#define QUALS_Event_Comm_Notification_Receiver \ + virtual void receive_notification (const Event_Comm::Notification& notification, CORBA::Environment &IT_env) throw (CORBA::SystemException){\ + m_obj->receive_notification ( notification,IT_env);\ +}\ + \ + virtual void disconnect (const char * reason, CORBA::Environment &IT_env) throw (CORBA::SystemException){\ + m_obj->disconnect ( reason,IT_env);\ +}\ + + + + +class Notification_ReceiverProxyFactoryClass : public virtual CORBA::ObjectFactoryClass { +public: + Notification_ReceiverProxyFactoryClass (unsigned char IT_p=0) + : CORBA::ProxyFactory (Event_Comm_Notification_Receiver_IR, IT_p) {} + + virtual void* New (char *IT_OR, CORBA::Environment&); + + virtual void* New (ObjectReference *IT_OR, CORBA::Environment&); + + virtual void* New2 (); + + virtual void* IT_castUp (void *IT_p, char* IT_s); + + virtual CORBA::PPTR* pptr (void *IT_p); + + virtual void baseInterfaces (_IDL_SEQUENCE_string&); + + +}; + +static Notification_ReceiverProxyFactoryClass Notification_ReceiverProxyFactory; + + + +#endif + + +#ifndef _Event_Comm_Notifier_defined +#define _Event_Comm_Notifier_defined +class Notifier_dispatch : public virtual CORBA::PPTR { +public: + + Notifier_dispatch (void *IT_p, CORBA::Object* IT_o, const char *IT_m, + CORBA::LoaderClass *IT_l, char *IT_i, void* IT_im) + : CORBA::PPTR (IT_p,IT_o,IT_m,IT_l,IT_i,IT_im) {} + + + Notifier_dispatch (char *IT_OR, void *IT_p, CORBA::Object *IT_o) + : CORBA::PPTR (IT_OR,IT_p,IT_o) {} + + + Notifier_dispatch () {} + + Notifier_dispatch (ObjectReference *IT_OR, void *IT_p, CORBA::Object *IT_o) + : CORBA::PPTR (IT_OR,IT_p,IT_o) {} + + + Notifier_dispatch (void *IT_p, CORBA::Object *IT_o, const char *IT_m, + char *IT_i, CORBA::Object* IT_ob, void* IT_im) + : CORBA::PPTR (IT_p,IT_o,IT_m,IT_i,IT_ob,IT_im) {} + + + virtual unsigned char dispatch (CORBA::Request &IT_r, + unsigned char IT_isTarget, void* IT_pp=NULL); + + +}; + +class Notifier; + +#ifndef Event_Comm_NotifierPtr +#define Event_Comm_NotifierPtr + + typedef Notifier* Notifier_ptr; + + typedef Notifier* NotifierRef; + +#endif + + +#ifndef Event_Comm_NotifierForwH +#define Event_Comm_NotifierForwH +static CORBA::ObjectRef Notifier_getBase (void *); +static void Notifier_release (Notifier *, CORBA::Environment &IT_env); +static void Notifier_release (Notifier_ptr); +static Notifier* Notifier_duplicate (Notifier_ptr, CORBA::Environment &IT_env); +static Notifier* Notifier_duplicate (Notifier_ptr ); +static Notifier_ptr Notifier_nil (CORBA::Environment &IT_env); +static Notifier_ptr Notifier_nil (); +#endif +#define Event_Comm_Notifier_IMPL "Event_Comm_Notifier" + + +class Notifier; + + typedef Notifier NotifierProxy; +#define Event_Comm_Notifier_IR "Event_Comm_Notifier" +#define Event_Comm_Notifier_IMPL "Event_Comm_Notifier" + +#ifndef Event_Comm_NotifierPtr +#define Event_Comm_NotifierPtr + + typedef Notifier* Notifier_ptr; + + typedef Notifier* NotifierRef; + +#endif + +class Notifier: public virtual CORBA::Object { +public: + Notifier (char *IT_OR); + Notifier (ObjectReference *IT_OR); + Notifier () : CORBA::Object (1) {} +protected: + Notifier_ptr __duplicate( + CORBA::Environment &IT_env=CORBA::IT_chooseDefaultEnv ()) { + CORBA::Object::__duplicate (IT_env); + return this; + } +public: + static Notifier_ptr _duplicate( + Notifier_ptr obj, + CORBA::Environment &IT_env=CORBA::IT_chooseDefaultEnv ()) { + CORBA::EnvExcRaiser IT_raise (&IT_env); + if (CORBA::is_nil(obj, IT_env)) { + IT_raise.maybeRaise (); + return (obj); + } + Notifier_ptr IT_obj = obj->__duplicate (IT_env); + IT_raise.maybeRaise(); + return IT_obj; + } +public: + static Notifier* _bind (const char* IT_markerServer, const char* host, + const CORBA::Context &IT_c, + CORBA::Environment &IT_env=CORBA::IT_chooseDefaultEnv ()); + static Notifier* _bind (CORBA::Environment &IT_env); + static Notifier* _bind (const char* IT_markerServer=NULL, const char* host=NULL, + CORBA::Environment &IT_env=CORBA::IT_chooseDefaultEnv ()); + static Notifier* _narrow (CORBA::Object* , CORBA::Environment &IT_env=CORBA::IT_chooseDefaultEnv ()); + static Notifier_ptr _nil (CORBA::Environment &IT_env=CORBA::IT_chooseDefaultEnv ()) { + CORBA::EnvExcRaiser IT_raise (&IT_env); + IT_raise.maybeRaise(); + return (Notifier_ptr) CORBA::OBJECT_NIL;} + virtual void send_disconnect (const char * reason, CORBA::Environment &IT_env=CORBA::IT_chooseDefaultEnv ()) throw (CORBA::SystemException); + virtual void send_notification (const Event_Comm::Notification& notification, CORBA::Environment &IT_env=CORBA::IT_chooseDefaultEnv ()) throw (CORBA::SystemException); + virtual void subscribe (Event_Comm::Notification_Receiver_ptr notification_receiver, const char * filtering_criteria, CORBA::Environment &IT_env=CORBA::IT_chooseDefaultEnv ()) throw (CORBA::SystemException); + virtual void unsubscribe (Event_Comm::Notification_Receiver_ptr notification_receiver, const char * filtering_criteria, CORBA::Environment &IT_env=CORBA::IT_chooseDefaultEnv ()) throw (CORBA::SystemException); +}; + + static const CORBA::TypeCode_ptr _tc_Notifier; + + static const CORBA::TypeCode_ptr _tc_NotifierRef; + +#ifndef Event_Comm_NotifierVarH +#define Event_Comm_NotifierVarH + +#ifndef Event_Comm_NotifiervPtr +#define Event_Comm_NotifiervPtr +typedef Notifier* Notifier_vPtr; +#endif + +class Notifier_var : public CORBA::_var +{ + public: + + Notifier_var () { + _ptr = Notifier_nil (); + } + + Notifier_var (Notifier *IT_p) { + _ptr = IT_p; + } + + Notifier_var (const Notifier_var &IT_s) { + _ptr = Notifier_duplicate (IT_s._ptr); + } + + Notifier_var &operator= (Notifier *IT_p) { + Notifier_release (_ptr); + _ptr = IT_p; + return (*this); + } + + Notifier_var &operator= (const Notifier_var &IT_s) { + Notifier_release (_ptr); + _ptr = Notifier_duplicate (IT_s._ptr); + return (*this); + } + + ~Notifier_var () { + Notifier_release (_ptr); + } + + Notifier* operator-> () { + return _ptr; + } + + operator const Notifier_vPtr () const { return _ptr;} + operator Notifier_vPtr& () { return _ptr;} + + protected: + Notifier *_ptr; + private: + Notifier_var &operator= (const CORBA::_var &IT_s); + Notifier_var (const CORBA::_var &IT_s); + Notifier_var &operator= (const CORBA::_mgr &IT_s); + Notifier_var &operator= (const CORBA::_SeqElem &IT_s); + Notifier_var (const CORBA::_mgr &IT_s); + Notifier_var (const CORBA::_SeqElem &IT_s); +}; + +#endif + + +#ifndef Event_Comm_NotifierMgrH +#define Event_Comm_NotifierMgrH + +class Notifier_mgr : public CORBA::_mgr +{ + public: + + Notifier_mgr () { + _ptr = Notifier_nil (); + _release = 1; + } + + Notifier_mgr (const Notifier_mgr &IT_s) { + _ptr = Notifier_duplicate (IT_s._ptr); + _release = 1; + } + + Notifier_mgr &operator= (Notifier *IT_p) { + if (_ptr && _release) + Notifier_release (_ptr); + _ptr = IT_p; + _release = 1; + return (*this); + } + + Notifier_mgr &operator= (const Notifier_mgr &IT_s) { + if (_ptr && _release) + Notifier_release (_ptr); + _ptr = Notifier_duplicate(IT_s._ptr); + _release = 1; + return (*this); + } + + Notifier_mgr &operator= (const Notifier_var &IT_s) { + if (_ptr && _release) + Notifier_release (_ptr); + _ptr = Notifier_duplicate(IT_s); + _release = 1; + return (*this); + } + + ~Notifier_mgr () { + if (_release) + Notifier_release (_ptr); + } + + unsigned char release () { + return _release; + } + + void release (unsigned char rel) { + _release = rel; + } + + operator int () const { + CORBA::Environment env; + CORBA::EnvExcRaiser IT_raise (&env); + return (!(CORBA::is_nil((CORBA::Object*) _ptr, env))); + } + + operator void* () const { + return _ptr; + } + + operator CORBA::Object * () const { + return (CORBA::Object *) _ptr; + } + + operator Notifier* () const { + return (Notifier*) _ptr; + } + + Notifier *_ptr; + + protected: + + unsigned char _release; +}; + +#endif + +#ifndef Event_Comm_NotifierSeqElemH +#define Event_Comm_NotifierSeqElemH + +class Notifier_SeqElem : public CORBA::_SeqElem +{ + public: + + Notifier_SeqElem (Event_Comm::Notifier_ptr* IT_p, unsigned char rel) { + _ptr = IT_p; + _release = rel; + } + + Notifier_SeqElem &operator= (Event_Comm::Notifier_ptr IT_p) { + if (!_ptr) + return (*this); + if (*(_ptr) && _release) + Notifier_release (*(_ptr)); + *(_ptr) = IT_p; + return (*this); + } + + Notifier_SeqElem &operator= (const Notifier_SeqElem &IT_s) { + if (!_ptr|| !IT_s._ptr) + return (*this); + if (*(_ptr) && _release) + Notifier_release (*(_ptr)); + *(_ptr) = Notifier_duplicate(*(IT_s._ptr)); + return (*this); + } + + operator Event_Comm::Notifier_ptr () const +{ + if (!_ptr) + return (Notifier_nil()); + return (Event_Comm::Notifier_ptr) (*_ptr); + } + + Notifier_ptr operator->() const { return *_ptr;} + + protected: + Event_Comm::Notifier_ptr *_ptr; + unsigned char _release; +}; + +#endif + + +#define TIE_Event_Comm_Notifier(X) Event_Comm_Notifier##X + +#define DEF_TIE_Event_Comm_Notifier(X) \ + class Event_Comm_Notifier##X : public virtual Event_Comm::Notifier { \ + X* m_obj; \ + public: \ + \ + Event_Comm_Notifier##X (X *objp, const char* m="", CORBA::LoaderClass *l=0)\ + : Event_Comm::Notifier(), CORBA::Object (), m_obj(objp) { \ + m_pptr = new Event_Comm::Notifier_dispatch \ + (( Event_Comm::Notifier*)this,(CORBA::Object*)this,m,l,Event_Comm_Notifier_IR,m_obj); \ + } \ + Event_Comm_Notifier##X (CORBA::Object *IT_p, const char* IT_m="", void *IT_q=0)\ + : Event_Comm::Notifier(), CORBA::Object () { \ + m_pptr = new Event_Comm::Notifier_dispatch \ + (( Event_Comm::Notifier*)this,(CORBA::Object*)this,IT_m,Event_Comm_Notifier_IR,IT_p,IT_q); \ + m_obj = (X*)(m_pptr->getImplObj ()); \ + } \ + \ + virtual ~Event_Comm_Notifier##X () { \ + if (_okToDeleteImpl ()) delete m_obj; } \ + \ + virtual void* _deref () { \ + return m_obj; } \ + \ + virtual void send_disconnect (const char * reason, CORBA::Environment &IT_env) throw (CORBA::SystemException){\ + m_obj->send_disconnect ( reason,IT_env);\ +}\ + \ + virtual void send_notification (const Event_Comm::Notification& notification, CORBA::Environment &IT_env) throw (CORBA::SystemException){\ + m_obj->send_notification ( notification,IT_env);\ +}\ + \ + virtual void subscribe (Event_Comm::Notification_Receiver_ptr notification_receiver, const char * filtering_criteria, CORBA::Environment &IT_env) throw (CORBA::SystemException){\ + m_obj->subscribe ( notification_receiver, filtering_criteria,IT_env);\ +}\ + \ + virtual void unsubscribe (Event_Comm::Notification_Receiver_ptr notification_receiver, const char * filtering_criteria, CORBA::Environment &IT_env) throw (CORBA::SystemException){\ + m_obj->unsubscribe ( notification_receiver, filtering_criteria,IT_env);\ +}\ + \ + }; + + +#define QUALS_Event_Comm_Notifier \ + virtual void send_disconnect (const char * reason, CORBA::Environment &IT_env) throw (CORBA::SystemException){\ + m_obj->send_disconnect ( reason,IT_env);\ +}\ + \ + virtual void send_notification (const Event_Comm::Notification& notification, CORBA::Environment &IT_env) throw (CORBA::SystemException){\ + m_obj->send_notification ( notification,IT_env);\ +}\ + \ + virtual void subscribe (Event_Comm::Notification_Receiver_ptr notification_receiver, const char * filtering_criteria, CORBA::Environment &IT_env) throw (CORBA::SystemException){\ + m_obj->subscribe ( notification_receiver, filtering_criteria,IT_env);\ +}\ + \ + virtual void unsubscribe (Event_Comm::Notification_Receiver_ptr notification_receiver, const char * filtering_criteria, CORBA::Environment &IT_env) throw (CORBA::SystemException){\ + m_obj->unsubscribe ( notification_receiver, filtering_criteria,IT_env);\ +}\ + + + + +class NotifierProxyFactoryClass : public virtual CORBA::ObjectFactoryClass { +public: + NotifierProxyFactoryClass (unsigned char IT_p=0) + : CORBA::ProxyFactory (Event_Comm_Notifier_IR, IT_p) {} + + virtual void* New (char *IT_OR, CORBA::Environment&); + + virtual void* New (ObjectReference *IT_OR, CORBA::Environment&); + + virtual void* New2 (); + + virtual void* IT_castUp (void *IT_p, char* IT_s); + + virtual CORBA::PPTR* pptr (void *IT_p); + + virtual void baseInterfaces (_IDL_SEQUENCE_string&); + + +}; + +static NotifierProxyFactoryClass NotifierProxyFactory; + + + +#endif + +}; + + +void operator<<= (CORBA::any &IT_a, Event_Comm::Notification_Receiver_ptr IT_t); +CORBA::Boolean operator>>= (const CORBA::any &IT_a, Event_Comm::Notification_Receiver_ptr& IT_t); + + +void operator<<= (CORBA::any &IT_a, Event_Comm::Notifier_ptr IT_t); +CORBA::Boolean operator>>= (const CORBA::any &IT_a, Event_Comm::Notifier_ptr& IT_t); + + +void operator<<= (CORBA::any &IT_a, const Event_Comm::Notification& IT_t); +CORBA::Boolean operator>>= (const CORBA::any &IT_a, Event_Comm::Notification*& IT_t); + + +#endif diff --git a/apps/Orbix-Examples/Event_Comm/include/Event_Comm_i.h b/apps/Orbix-Examples/Event_Comm/include/Event_Comm_i.h new file mode 100644 index 00000000000..49673abec84 --- /dev/null +++ b/apps/Orbix-Examples/Event_Comm/include/Event_Comm_i.h @@ -0,0 +1,37 @@ +/* -*- C++ -*- */ +// @(#)Event_Comm_i.h 1.1 10/18/96 + + +// ============================================================================ +// +// = LIBRARY +// EventComm +// +// = FILENAME +// Event_Comm_i.h +// +// = DESCRIPTION +// Class interface for the implementation of the distributed +// event notification mechanism. +// +// = AUTHOR +// Douglas C. Schmidt (schmidt@cs.wustl.edu) +// +// ============================================================================ + +#if !defined (_EVENT_COMM_I_H) +#define _EVENT_COMM_I_H + +#include "Notification_Receiver_i.h" +#include "Notifier_i.h" + +#if defined (ACE_HAS_ORBIX) + +// Tie the Notification_Receiver and Notifier implementation classes +// together with the IDL interface. + +DEF_TIE_Event_Comm_Notification_Receiver (Notification_Receiver_i) +DEF_TIE_Event_Comm_Notifier (Notifier_i) + +#endif /* ACE_HAS_ORBIX */ +#endif /* _EVENT_COMM_I_H */ diff --git a/apps/Orbix-Examples/Event_Comm/include/Notification_Receiver_i.h b/apps/Orbix-Examples/Event_Comm/include/Notification_Receiver_i.h new file mode 100644 index 00000000000..4f0bcc980e4 --- /dev/null +++ b/apps/Orbix-Examples/Event_Comm/include/Notification_Receiver_i.h @@ -0,0 +1,48 @@ +/* -*- C++ -*- */ +// @(#)Notification_Receiver_i.h 1.1 10/18/96 + + +// ============================================================================ +// +// = LIBRARY +// EventComm +// +// = FILENAME +// Notification_Receiver__i.h +// +// = DESCRIPTION +// Class interface for the implementation of the <Notification_Receiver> +// +// = AUTHOR +// Douglas C. Schmidt (schmidt@cs.wustl.edu) +// +// ============================================================================ + +#if !defined (_Notification_Receiver_i_H) +#define _Notification_Receiver_i_H + +#if defined (ACE_HAS_ORBIX) +#include "Event_Comm.hh" + +class Notification_Receiver_i + // = TITLE + // Defines the implementation class for event <Notification_Receivers>. + // + // = DESCRIPTION +{ +public: + Notification_Receiver_i (void); + ~Notification_Receiver_i (void); + + virtual void receive_notification (const Event_Comm::Notification ¬ification, + CORBA::Environment &IT_env); + // Pass the <Notification> to the <Notification_Receiver>. + + virtual void disconnect (const char *reason, + CORBA::Environment &IT_env); + // Disconnect the <Notification_Receiver> from the <Notifier>, + // giving it the <reason>. +}; + +#endif /* ACE_HAS_ORBIX */ +#endif /* _Notification_Receiver_i_H */ diff --git a/apps/Orbix-Examples/Event_Comm/include/Notifier_i.h b/apps/Orbix-Examples/Event_Comm/include/Notifier_i.h new file mode 100644 index 00000000000..379f96b8097 --- /dev/null +++ b/apps/Orbix-Examples/Event_Comm/include/Notifier_i.h @@ -0,0 +1,82 @@ +/* -*- C++ -*- */ +// @(#)Notifier_i.h 1.1 10/18/96 + + +// ============================================================================ +// +// = LIBRARY +// EventComm +// +// = FILENAME +// Notifier_i.h +// +// = DESCRIPTION +// Class interface for the implementation of the <Notifier> +// +// = AUTHOR +// Douglas C. Schmidt (schmidt@cs.wustl.edu) +// +// ============================================================================ + +#if !defined (_Notifier_i_H) +#define _Notifier_i_H + +#include "ace/Map_Manager.h" +#include "ace/Synch.h" +#include "ace/SString.h" +#include "Event_Comm.hh" + +#if defined (ACE_HAS_ORBIX) + +// Forward reference. +class Notification_Receiver_Entry; + +class Notifier_i + // = TITLE + // Defines the implementation class for event <Notifiers>. + // + // = DESCRIPTION +{ +public: + enum + { + DEFAULT_SIZE = 1024 // Default max number of Event_Comm::Notification_Receivers. + }; + + Notifier_i (size_t size_hint = Notifier_i::DEFAULT_SIZE); + // Initialize a Notifier_i object with the specified size hint. + + void send_disconnect (const char *reason, + CORBA::Environment &IT_env); + // Disconnect all the receivers, giving them the <reason>. + + void send_notification (const Event_Comm::Notification ¬ification, + CORBA::Environment &IT_env); + // Send the <Notification> to all the consumers who + // have subscribed and who match the filtering criteria. + + void subscribe (Event_Comm::Notification_Receiver *notification_receiver, + const char *filtering_criteria, + CORBA::Environment &IT_env); + // Subscribe the <Notification_Receiver> to receive events that + // match <filtering_criteria> applied by the <Notifier>. + + void unsubscribe (Event_Comm::Notification_Receiver *notification_receiver, + const char *filtering_criteria, + CORBA::Environment &IT_env); + // Unsubscribe the <Notification_Receiver>. + +private: + // The following implementation should be replaced + // by a standard container class from STL... + + typedef ACE_Map_Manager <ACE_SString, Notification_Receiver_Entry *, ACE_Null_Mutex> MAP_MANAGER; + typedef ACE_Map_Iterator <ACE_SString, Notification_Receiver_Entry *, ACE_Null_Mutex> MAP_ITERATOR; + typedef ACE_Map_Entry <ACE_SString, Notification_Receiver_Entry *> MAP_ENTRY; + + MAP_MANAGER map_; + // Table that maps a <Event_Comm::Notification_Receiver *> to a <Notification_Receiver_Entry *>. +}; + +#endif /* ACE_HAS_ORBIX */ +#endif /* _Notifier_i_H */ diff --git a/apps/Orbix-Examples/Event_Comm/libsrc/Event_Comm.hh b/apps/Orbix-Examples/Event_Comm/libsrc/Event_Comm.hh new file mode 100644 index 00000000000..85ad256da2a --- /dev/null +++ b/apps/Orbix-Examples/Event_Comm/libsrc/Event_Comm.hh @@ -0,0 +1,887 @@ + +#ifndef Event_Comm_hh +#define Event_Comm_hh + +#include <CORBA.h> + +#include <string.h> + +class Event_Comm { +public: + +#ifndef Event_Comm_Notification_defined +#define Event_Comm_Notification_defined + + struct Notification { + CORBA::String_mgr tag_; + + void encodeOp (CORBA::Request &IT_r) const; + void decodeOp (CORBA::Request &IT_r); + void decodeInOutOp (CORBA::Request &IT_r); + static void* IT_anySupport (CORBA::Request &IT_r, + void *&, void*, const CORBA::Flags&); + static const void *IT_fn; + }; + + static const CORBA::TypeCode_ptr _tc_Notification; + +#ifndef Event_Comm_NotificationVarH +#define Event_Comm_NotificationVarH + +#ifndef Event_Comm_NotificationvPtr +#define Event_Comm_NotificationvPtr +typedef Notification* Notification_vPtr; +#endif + +class Notification_var : public CORBA::_var +{ + public: + + Notification_var () { + _ptr = NULL; + } + + Notification_var (Notification *IT_p) { + _ptr = IT_p; + } + + Notification_var (const Notification_var &IT_s) { + if (!IT_s._ptr) { + _ptr = IT_s._ptr; + return; + } + _ptr = new Notification (*(IT_s._ptr)); + } + + Notification_var &operator= (Notification *IT_p) { + if (_ptr != IT_p) { + delete _ptr; + } + _ptr = IT_p; + return (*this); + } + + Notification_var &operator= (const Notification_var &IT_s) { + if (_ptr != IT_s._ptr) { + delete _ptr; + } + _ptr = new Notification (*(IT_s._ptr)); + return (*this); + } + + ~Notification_var () { + delete _ptr; + } + + Notification* operator-> () { + return _ptr; + } + + operator const Notification_vPtr () const { return _ptr;} + operator Notification_vPtr& () { return _ptr;} + operator Notification& () const { return * _ptr;} + + protected: + Notification *_ptr; + private: + Notification_var &operator= (const CORBA::_var &IT_s); + Notification_var (const CORBA::_var &IT_s); +}; + +#endif + + +#endif + + +#ifndef _Event_Comm_Notification_Receiver_defined +#define _Event_Comm_Notification_Receiver_defined +class Notification_Receiver_dispatch : public virtual CORBA::PPTR { +public: + + Notification_Receiver_dispatch (void *IT_p, CORBA::Object* IT_o, const char *IT_m, + CORBA::LoaderClass *IT_l, char *IT_i, void* IT_im) + : CORBA::PPTR (IT_p,IT_o,IT_m,IT_l,IT_i,IT_im) {} + + + Notification_Receiver_dispatch (char *IT_OR, void *IT_p, CORBA::Object *IT_o) + : CORBA::PPTR (IT_OR,IT_p,IT_o) {} + + + Notification_Receiver_dispatch () {} + + Notification_Receiver_dispatch (ObjectReference *IT_OR, void *IT_p, CORBA::Object *IT_o) + : CORBA::PPTR (IT_OR,IT_p,IT_o) {} + + + Notification_Receiver_dispatch (void *IT_p, CORBA::Object *IT_o, const char *IT_m, + char *IT_i, CORBA::Object* IT_ob, void* IT_im) + : CORBA::PPTR (IT_p,IT_o,IT_m,IT_i,IT_ob,IT_im) {} + + + virtual unsigned char dispatch (CORBA::Request &IT_r, + unsigned char IT_isTarget, void* IT_pp=NULL); + + +}; + +class Notification_Receiver; + +#ifndef Event_Comm_Notification_ReceiverPtr +#define Event_Comm_Notification_ReceiverPtr + + typedef Notification_Receiver* Notification_Receiver_ptr; + + typedef Notification_Receiver* Notification_ReceiverRef; + +#endif + + +#ifndef Event_Comm_Notification_ReceiverForwH +#define Event_Comm_Notification_ReceiverForwH +static CORBA::ObjectRef Notification_Receiver_getBase (void *); +static void Notification_Receiver_release (Notification_Receiver *, CORBA::Environment &IT_env); +static void Notification_Receiver_release (Notification_Receiver_ptr); +static Notification_Receiver* Notification_Receiver_duplicate (Notification_Receiver_ptr, CORBA::Environment &IT_env); +static Notification_Receiver* Notification_Receiver_duplicate (Notification_Receiver_ptr ); +static Notification_Receiver_ptr Notification_Receiver_nil (CORBA::Environment &IT_env); +static Notification_Receiver_ptr Notification_Receiver_nil (); +#endif +#define Event_Comm_Notification_Receiver_IMPL "Event_Comm_Notification_Receiver" + + +class Notification_Receiver; + + typedef Notification_Receiver Notification_ReceiverProxy; +#define Event_Comm_Notification_Receiver_IR "Event_Comm_Notification_Receiver" +#define Event_Comm_Notification_Receiver_IMPL "Event_Comm_Notification_Receiver" + +#ifndef Event_Comm_Notification_ReceiverPtr +#define Event_Comm_Notification_ReceiverPtr + + typedef Notification_Receiver* Notification_Receiver_ptr; + + typedef Notification_Receiver* Notification_ReceiverRef; + +#endif + +class Notification_Receiver: public virtual CORBA::Object { +public: + Notification_Receiver (char *IT_OR); + Notification_Receiver (ObjectReference *IT_OR); + Notification_Receiver () : CORBA::Object (1) {} +protected: + Notification_Receiver_ptr __duplicate( + CORBA::Environment &IT_env=CORBA::IT_chooseDefaultEnv ()) { + CORBA::Object::__duplicate (IT_env); + return this; + } +public: + static Notification_Receiver_ptr _duplicate( + Notification_Receiver_ptr obj, + CORBA::Environment &IT_env=CORBA::IT_chooseDefaultEnv ()) { + CORBA::EnvExcRaiser IT_raise (&IT_env); + if (CORBA::is_nil(obj, IT_env)) { + IT_raise.maybeRaise (); + return (obj); + } + Notification_Receiver_ptr IT_obj = obj->__duplicate (IT_env); + IT_raise.maybeRaise(); + return IT_obj; + } +public: + static Notification_Receiver* _bind (const char* IT_markerServer, const char* host, + const CORBA::Context &IT_c, + CORBA::Environment &IT_env=CORBA::IT_chooseDefaultEnv ()); + static Notification_Receiver* _bind (CORBA::Environment &IT_env); + static Notification_Receiver* _bind (const char* IT_markerServer=NULL, const char* host=NULL, + CORBA::Environment &IT_env=CORBA::IT_chooseDefaultEnv ()); + static Notification_Receiver* _narrow (CORBA::Object* , CORBA::Environment &IT_env=CORBA::IT_chooseDefaultEnv ()); + static Notification_Receiver_ptr _nil (CORBA::Environment &IT_env=CORBA::IT_chooseDefaultEnv ()) { + CORBA::EnvExcRaiser IT_raise (&IT_env); + IT_raise.maybeRaise(); + return (Notification_Receiver_ptr) CORBA::OBJECT_NIL;} + virtual void receive_notification (const Event_Comm::Notification& notification, CORBA::Environment &IT_env=CORBA::IT_chooseDefaultEnv ()) throw (CORBA::SystemException); + virtual void disconnect (const char * reason, CORBA::Environment &IT_env=CORBA::IT_chooseDefaultEnv ()) throw (CORBA::SystemException); +}; + + static const CORBA::TypeCode_ptr _tc_Notification_Receiver; + + static const CORBA::TypeCode_ptr _tc_Notification_ReceiverRef; + +#ifndef Event_Comm_Notification_ReceiverVarH +#define Event_Comm_Notification_ReceiverVarH + +#ifndef Event_Comm_Notification_ReceivervPtr +#define Event_Comm_Notification_ReceivervPtr +typedef Notification_Receiver* Notification_Receiver_vPtr; +#endif + +class Notification_Receiver_var : public CORBA::_var +{ + public: + + Notification_Receiver_var () { + _ptr = Notification_Receiver_nil (); + } + + Notification_Receiver_var (Notification_Receiver *IT_p) { + _ptr = IT_p; + } + + Notification_Receiver_var (const Notification_Receiver_var &IT_s) { + _ptr = Notification_Receiver_duplicate (IT_s._ptr); + } + + Notification_Receiver_var &operator= (Notification_Receiver *IT_p) { + Notification_Receiver_release (_ptr); + _ptr = IT_p; + return (*this); + } + + Notification_Receiver_var &operator= (const Notification_Receiver_var &IT_s) { + Notification_Receiver_release (_ptr); + _ptr = Notification_Receiver_duplicate (IT_s._ptr); + return (*this); + } + + ~Notification_Receiver_var () { + Notification_Receiver_release (_ptr); + } + + Notification_Receiver* operator-> () { + return _ptr; + } + + operator const Notification_Receiver_vPtr () const { return _ptr;} + operator Notification_Receiver_vPtr& () { return _ptr;} + + protected: + Notification_Receiver *_ptr; + private: + Notification_Receiver_var &operator= (const CORBA::_var &IT_s); + Notification_Receiver_var (const CORBA::_var &IT_s); + Notification_Receiver_var &operator= (const CORBA::_mgr &IT_s); + Notification_Receiver_var &operator= (const CORBA::_SeqElem &IT_s); + Notification_Receiver_var (const CORBA::_mgr &IT_s); + Notification_Receiver_var (const CORBA::_SeqElem &IT_s); +}; + +#endif + + +#ifndef Event_Comm_Notification_ReceiverMgrH +#define Event_Comm_Notification_ReceiverMgrH + +class Notification_Receiver_mgr : public CORBA::_mgr +{ + public: + + Notification_Receiver_mgr () { + _ptr = Notification_Receiver_nil (); + _release = 1; + } + + Notification_Receiver_mgr (const Notification_Receiver_mgr &IT_s) { + _ptr = Notification_Receiver_duplicate (IT_s._ptr); + _release = 1; + } + + Notification_Receiver_mgr &operator= (Notification_Receiver *IT_p) { + if (_ptr && _release) + Notification_Receiver_release (_ptr); + _ptr = IT_p; + _release = 1; + return (*this); + } + + Notification_Receiver_mgr &operator= (const Notification_Receiver_mgr &IT_s) { + if (_ptr && _release) + Notification_Receiver_release (_ptr); + _ptr = Notification_Receiver_duplicate(IT_s._ptr); + _release = 1; + return (*this); + } + + Notification_Receiver_mgr &operator= (const Notification_Receiver_var &IT_s) { + if (_ptr && _release) + Notification_Receiver_release (_ptr); + _ptr = Notification_Receiver_duplicate(IT_s); + _release = 1; + return (*this); + } + + ~Notification_Receiver_mgr () { + if (_release) + Notification_Receiver_release (_ptr); + } + + unsigned char release () { + return _release; + } + + void release (unsigned char rel) { + _release = rel; + } + + operator int () const { + CORBA::Environment env; + CORBA::EnvExcRaiser IT_raise (&env); + return (!(CORBA::is_nil((CORBA::Object*) _ptr, env))); + } + + operator void* () const { + return _ptr; + } + + operator CORBA::Object * () const { + return (CORBA::Object *) _ptr; + } + + operator Notification_Receiver* () const { + return (Notification_Receiver*) _ptr; + } + + Notification_Receiver *_ptr; + + protected: + + unsigned char _release; +}; + +#endif + +#ifndef Event_Comm_Notification_ReceiverSeqElemH +#define Event_Comm_Notification_ReceiverSeqElemH + +class Notification_Receiver_SeqElem : public CORBA::_SeqElem +{ + public: + + Notification_Receiver_SeqElem (Event_Comm::Notification_Receiver_ptr* IT_p, unsigned char rel) { + _ptr = IT_p; + _release = rel; + } + + Notification_Receiver_SeqElem &operator= (Event_Comm::Notification_Receiver_ptr IT_p) { + if (!_ptr) + return (*this); + if (*(_ptr) && _release) + Notification_Receiver_release (*(_ptr)); + *(_ptr) = IT_p; + return (*this); + } + + Notification_Receiver_SeqElem &operator= (const Notification_Receiver_SeqElem &IT_s) { + if (!_ptr|| !IT_s._ptr) + return (*this); + if (*(_ptr) && _release) + Notification_Receiver_release (*(_ptr)); + *(_ptr) = Notification_Receiver_duplicate(*(IT_s._ptr)); + return (*this); + } + + operator Event_Comm::Notification_Receiver_ptr () const +{ + if (!_ptr) + return (Notification_Receiver_nil()); + return (Event_Comm::Notification_Receiver_ptr) (*_ptr); + } + + Notification_Receiver_ptr operator->() const { return *_ptr;} + + protected: + Event_Comm::Notification_Receiver_ptr *_ptr; + unsigned char _release; +}; + +#endif + + +#define TIE_Event_Comm_Notification_Receiver(X) Event_Comm_Notification_Receiver##X + +#define DEF_TIE_Event_Comm_Notification_Receiver(X) \ + class Event_Comm_Notification_Receiver##X : public virtual Event_Comm::Notification_Receiver { \ + X* m_obj; \ + public: \ + \ + Event_Comm_Notification_Receiver##X (X *objp, const char* m="", CORBA::LoaderClass *l=0)\ + : Event_Comm::Notification_Receiver(), CORBA::Object (), m_obj(objp) { \ + m_pptr = new Event_Comm::Notification_Receiver_dispatch \ + (( Event_Comm::Notification_Receiver*)this,(CORBA::Object*)this,m,l,Event_Comm_Notification_Receiver_IR,m_obj); \ + } \ + Event_Comm_Notification_Receiver##X (CORBA::Object *IT_p, const char* IT_m="", void *IT_q=0)\ + : Event_Comm::Notification_Receiver(), CORBA::Object () { \ + m_pptr = new Event_Comm::Notification_Receiver_dispatch \ + (( Event_Comm::Notification_Receiver*)this,(CORBA::Object*)this,IT_m,Event_Comm_Notification_Receiver_IR,IT_p,IT_q); \ + m_obj = (X*)(m_pptr->getImplObj ()); \ + } \ + \ + virtual ~Event_Comm_Notification_Receiver##X () { \ + if (_okToDeleteImpl ()) delete m_obj; } \ + \ + virtual void* _deref () { \ + return m_obj; } \ + \ + virtual void receive_notification (const Event_Comm::Notification& notification, CORBA::Environment &IT_env) throw (CORBA::SystemException){\ + m_obj->receive_notification ( notification,IT_env);\ +}\ + \ + virtual void disconnect (const char * reason, CORBA::Environment &IT_env) throw (CORBA::SystemException){\ + m_obj->disconnect ( reason,IT_env);\ +}\ + \ + }; + + +#define QUALS_Event_Comm_Notification_Receiver \ + virtual void receive_notification (const Event_Comm::Notification& notification, CORBA::Environment &IT_env) throw (CORBA::SystemException){\ + m_obj->receive_notification ( notification,IT_env);\ +}\ + \ + virtual void disconnect (const char * reason, CORBA::Environment &IT_env) throw (CORBA::SystemException){\ + m_obj->disconnect ( reason,IT_env);\ +}\ + + + + +class Notification_ReceiverProxyFactoryClass : public virtual CORBA::ObjectFactoryClass { +public: + Notification_ReceiverProxyFactoryClass (unsigned char IT_p=0) + : CORBA::ProxyFactory (Event_Comm_Notification_Receiver_IR, IT_p) {} + + virtual void* New (char *IT_OR, CORBA::Environment&); + + virtual void* New (ObjectReference *IT_OR, CORBA::Environment&); + + virtual void* New2 (); + + virtual void* IT_castUp (void *IT_p, char* IT_s); + + virtual CORBA::PPTR* pptr (void *IT_p); + + virtual void baseInterfaces (_IDL_SEQUENCE_string&); + + +}; + +static Notification_ReceiverProxyFactoryClass Notification_ReceiverProxyFactory; + + + +#endif + + +#ifndef _Event_Comm_Notifier_defined +#define _Event_Comm_Notifier_defined +class Notifier_dispatch : public virtual CORBA::PPTR { +public: + + Notifier_dispatch (void *IT_p, CORBA::Object* IT_o, const char *IT_m, + CORBA::LoaderClass *IT_l, char *IT_i, void* IT_im) + : CORBA::PPTR (IT_p,IT_o,IT_m,IT_l,IT_i,IT_im) {} + + + Notifier_dispatch (char *IT_OR, void *IT_p, CORBA::Object *IT_o) + : CORBA::PPTR (IT_OR,IT_p,IT_o) {} + + + Notifier_dispatch () {} + + Notifier_dispatch (ObjectReference *IT_OR, void *IT_p, CORBA::Object *IT_o) + : CORBA::PPTR (IT_OR,IT_p,IT_o) {} + + + Notifier_dispatch (void *IT_p, CORBA::Object *IT_o, const char *IT_m, + char *IT_i, CORBA::Object* IT_ob, void* IT_im) + : CORBA::PPTR (IT_p,IT_o,IT_m,IT_i,IT_ob,IT_im) {} + + + virtual unsigned char dispatch (CORBA::Request &IT_r, + unsigned char IT_isTarget, void* IT_pp=NULL); + + +}; + +class Notifier; + +#ifndef Event_Comm_NotifierPtr +#define Event_Comm_NotifierPtr + + typedef Notifier* Notifier_ptr; + + typedef Notifier* NotifierRef; + +#endif + + +#ifndef Event_Comm_NotifierForwH +#define Event_Comm_NotifierForwH +static CORBA::ObjectRef Notifier_getBase (void *); +static void Notifier_release (Notifier *, CORBA::Environment &IT_env); +static void Notifier_release (Notifier_ptr); +static Notifier* Notifier_duplicate (Notifier_ptr, CORBA::Environment &IT_env); +static Notifier* Notifier_duplicate (Notifier_ptr ); +static Notifier_ptr Notifier_nil (CORBA::Environment &IT_env); +static Notifier_ptr Notifier_nil (); +#endif +#define Event_Comm_Notifier_IMPL "Event_Comm_Notifier" + + +class Notifier; + + typedef Notifier NotifierProxy; +#define Event_Comm_Notifier_IR "Event_Comm_Notifier" +#define Event_Comm_Notifier_IMPL "Event_Comm_Notifier" + +#ifndef Event_Comm_NotifierPtr +#define Event_Comm_NotifierPtr + + typedef Notifier* Notifier_ptr; + + typedef Notifier* NotifierRef; + +#endif + +class Notifier: public virtual CORBA::Object { +public: + Notifier (char *IT_OR); + Notifier (ObjectReference *IT_OR); + Notifier () : CORBA::Object (1) {} +protected: + Notifier_ptr __duplicate( + CORBA::Environment &IT_env=CORBA::IT_chooseDefaultEnv ()) { + CORBA::Object::__duplicate (IT_env); + return this; + } +public: + static Notifier_ptr _duplicate( + Notifier_ptr obj, + CORBA::Environment &IT_env=CORBA::IT_chooseDefaultEnv ()) { + CORBA::EnvExcRaiser IT_raise (&IT_env); + if (CORBA::is_nil(obj, IT_env)) { + IT_raise.maybeRaise (); + return (obj); + } + Notifier_ptr IT_obj = obj->__duplicate (IT_env); + IT_raise.maybeRaise(); + return IT_obj; + } +public: + static Notifier* _bind (const char* IT_markerServer, const char* host, + const CORBA::Context &IT_c, + CORBA::Environment &IT_env=CORBA::IT_chooseDefaultEnv ()); + static Notifier* _bind (CORBA::Environment &IT_env); + static Notifier* _bind (const char* IT_markerServer=NULL, const char* host=NULL, + CORBA::Environment &IT_env=CORBA::IT_chooseDefaultEnv ()); + static Notifier* _narrow (CORBA::Object* , CORBA::Environment &IT_env=CORBA::IT_chooseDefaultEnv ()); + static Notifier_ptr _nil (CORBA::Environment &IT_env=CORBA::IT_chooseDefaultEnv ()) { + CORBA::EnvExcRaiser IT_raise (&IT_env); + IT_raise.maybeRaise(); + return (Notifier_ptr) CORBA::OBJECT_NIL;} + virtual void send_disconnect (const char * reason, CORBA::Environment &IT_env=CORBA::IT_chooseDefaultEnv ()) throw (CORBA::SystemException); + virtual void send_notification (const Event_Comm::Notification& notification, CORBA::Environment &IT_env=CORBA::IT_chooseDefaultEnv ()) throw (CORBA::SystemException); + virtual void subscribe (Event_Comm::Notification_Receiver_ptr notification_receiver, const char * filtering_criteria, CORBA::Environment &IT_env=CORBA::IT_chooseDefaultEnv ()) throw (CORBA::SystemException); + virtual void unsubscribe (Event_Comm::Notification_Receiver_ptr notification_receiver, const char * filtering_criteria, CORBA::Environment &IT_env=CORBA::IT_chooseDefaultEnv ()) throw (CORBA::SystemException); +}; + + static const CORBA::TypeCode_ptr _tc_Notifier; + + static const CORBA::TypeCode_ptr _tc_NotifierRef; + +#ifndef Event_Comm_NotifierVarH +#define Event_Comm_NotifierVarH + +#ifndef Event_Comm_NotifiervPtr +#define Event_Comm_NotifiervPtr +typedef Notifier* Notifier_vPtr; +#endif + +class Notifier_var : public CORBA::_var +{ + public: + + Notifier_var () { + _ptr = Notifier_nil (); + } + + Notifier_var (Notifier *IT_p) { + _ptr = IT_p; + } + + Notifier_var (const Notifier_var &IT_s) { + _ptr = Notifier_duplicate (IT_s._ptr); + } + + Notifier_var &operator= (Notifier *IT_p) { + Notifier_release (_ptr); + _ptr = IT_p; + return (*this); + } + + Notifier_var &operator= (const Notifier_var &IT_s) { + Notifier_release (_ptr); + _ptr = Notifier_duplicate (IT_s._ptr); + return (*this); + } + + ~Notifier_var () { + Notifier_release (_ptr); + } + + Notifier* operator-> () { + return _ptr; + } + + operator const Notifier_vPtr () const { return _ptr;} + operator Notifier_vPtr& () { return _ptr;} + + protected: + Notifier *_ptr; + private: + Notifier_var &operator= (const CORBA::_var &IT_s); + Notifier_var (const CORBA::_var &IT_s); + Notifier_var &operator= (const CORBA::_mgr &IT_s); + Notifier_var &operator= (const CORBA::_SeqElem &IT_s); + Notifier_var (const CORBA::_mgr &IT_s); + Notifier_var (const CORBA::_SeqElem &IT_s); +}; + +#endif + + +#ifndef Event_Comm_NotifierMgrH +#define Event_Comm_NotifierMgrH + +class Notifier_mgr : public CORBA::_mgr +{ + public: + + Notifier_mgr () { + _ptr = Notifier_nil (); + _release = 1; + } + + Notifier_mgr (const Notifier_mgr &IT_s) { + _ptr = Notifier_duplicate (IT_s._ptr); + _release = 1; + } + + Notifier_mgr &operator= (Notifier *IT_p) { + if (_ptr && _release) + Notifier_release (_ptr); + _ptr = IT_p; + _release = 1; + return (*this); + } + + Notifier_mgr &operator= (const Notifier_mgr &IT_s) { + if (_ptr && _release) + Notifier_release (_ptr); + _ptr = Notifier_duplicate(IT_s._ptr); + _release = 1; + return (*this); + } + + Notifier_mgr &operator= (const Notifier_var &IT_s) { + if (_ptr && _release) + Notifier_release (_ptr); + _ptr = Notifier_duplicate(IT_s); + _release = 1; + return (*this); + } + + ~Notifier_mgr () { + if (_release) + Notifier_release (_ptr); + } + + unsigned char release () { + return _release; + } + + void release (unsigned char rel) { + _release = rel; + } + + operator int () const { + CORBA::Environment env; + CORBA::EnvExcRaiser IT_raise (&env); + return (!(CORBA::is_nil((CORBA::Object*) _ptr, env))); + } + + operator void* () const { + return _ptr; + } + + operator CORBA::Object * () const { + return (CORBA::Object *) _ptr; + } + + operator Notifier* () const { + return (Notifier*) _ptr; + } + + Notifier *_ptr; + + protected: + + unsigned char _release; +}; + +#endif + +#ifndef Event_Comm_NotifierSeqElemH +#define Event_Comm_NotifierSeqElemH + +class Notifier_SeqElem : public CORBA::_SeqElem +{ + public: + + Notifier_SeqElem (Event_Comm::Notifier_ptr* IT_p, unsigned char rel) { + _ptr = IT_p; + _release = rel; + } + + Notifier_SeqElem &operator= (Event_Comm::Notifier_ptr IT_p) { + if (!_ptr) + return (*this); + if (*(_ptr) && _release) + Notifier_release (*(_ptr)); + *(_ptr) = IT_p; + return (*this); + } + + Notifier_SeqElem &operator= (const Notifier_SeqElem &IT_s) { + if (!_ptr|| !IT_s._ptr) + return (*this); + if (*(_ptr) && _release) + Notifier_release (*(_ptr)); + *(_ptr) = Notifier_duplicate(*(IT_s._ptr)); + return (*this); + } + + operator Event_Comm::Notifier_ptr () const +{ + if (!_ptr) + return (Notifier_nil()); + return (Event_Comm::Notifier_ptr) (*_ptr); + } + + Notifier_ptr operator->() const { return *_ptr;} + + protected: + Event_Comm::Notifier_ptr *_ptr; + unsigned char _release; +}; + +#endif + + +#define TIE_Event_Comm_Notifier(X) Event_Comm_Notifier##X + +#define DEF_TIE_Event_Comm_Notifier(X) \ + class Event_Comm_Notifier##X : public virtual Event_Comm::Notifier { \ + X* m_obj; \ + public: \ + \ + Event_Comm_Notifier##X (X *objp, const char* m="", CORBA::LoaderClass *l=0)\ + : Event_Comm::Notifier(), CORBA::Object (), m_obj(objp) { \ + m_pptr = new Event_Comm::Notifier_dispatch \ + (( Event_Comm::Notifier*)this,(CORBA::Object*)this,m,l,Event_Comm_Notifier_IR,m_obj); \ + } \ + Event_Comm_Notifier##X (CORBA::Object *IT_p, const char* IT_m="", void *IT_q=0)\ + : Event_Comm::Notifier(), CORBA::Object () { \ + m_pptr = new Event_Comm::Notifier_dispatch \ + (( Event_Comm::Notifier*)this,(CORBA::Object*)this,IT_m,Event_Comm_Notifier_IR,IT_p,IT_q); \ + m_obj = (X*)(m_pptr->getImplObj ()); \ + } \ + \ + virtual ~Event_Comm_Notifier##X () { \ + if (_okToDeleteImpl ()) delete m_obj; } \ + \ + virtual void* _deref () { \ + return m_obj; } \ + \ + virtual void send_disconnect (const char * reason, CORBA::Environment &IT_env) throw (CORBA::SystemException){\ + m_obj->send_disconnect ( reason,IT_env);\ +}\ + \ + virtual void send_notification (const Event_Comm::Notification& notification, CORBA::Environment &IT_env) throw (CORBA::SystemException){\ + m_obj->send_notification ( notification,IT_env);\ +}\ + \ + virtual void subscribe (Event_Comm::Notification_Receiver_ptr notification_receiver, const char * filtering_criteria, CORBA::Environment &IT_env) throw (CORBA::SystemException){\ + m_obj->subscribe ( notification_receiver, filtering_criteria,IT_env);\ +}\ + \ + virtual void unsubscribe (Event_Comm::Notification_Receiver_ptr notification_receiver, const char * filtering_criteria, CORBA::Environment &IT_env) throw (CORBA::SystemException){\ + m_obj->unsubscribe ( notification_receiver, filtering_criteria,IT_env);\ +}\ + \ + }; + + +#define QUALS_Event_Comm_Notifier \ + virtual void send_disconnect (const char * reason, CORBA::Environment &IT_env) throw (CORBA::SystemException){\ + m_obj->send_disconnect ( reason,IT_env);\ +}\ + \ + virtual void send_notification (const Event_Comm::Notification& notification, CORBA::Environment &IT_env) throw (CORBA::SystemException){\ + m_obj->send_notification ( notification,IT_env);\ +}\ + \ + virtual void subscribe (Event_Comm::Notification_Receiver_ptr notification_receiver, const char * filtering_criteria, CORBA::Environment &IT_env) throw (CORBA::SystemException){\ + m_obj->subscribe ( notification_receiver, filtering_criteria,IT_env);\ +}\ + \ + virtual void unsubscribe (Event_Comm::Notification_Receiver_ptr notification_receiver, const char * filtering_criteria, CORBA::Environment &IT_env) throw (CORBA::SystemException){\ + m_obj->unsubscribe ( notification_receiver, filtering_criteria,IT_env);\ +}\ + + + + +class NotifierProxyFactoryClass : public virtual CORBA::ObjectFactoryClass { +public: + NotifierProxyFactoryClass (unsigned char IT_p=0) + : CORBA::ProxyFactory (Event_Comm_Notifier_IR, IT_p) {} + + virtual void* New (char *IT_OR, CORBA::Environment&); + + virtual void* New (ObjectReference *IT_OR, CORBA::Environment&); + + virtual void* New2 (); + + virtual void* IT_castUp (void *IT_p, char* IT_s); + + virtual CORBA::PPTR* pptr (void *IT_p); + + virtual void baseInterfaces (_IDL_SEQUENCE_string&); + + +}; + +static NotifierProxyFactoryClass NotifierProxyFactory; + + + +#endif + +}; + + +void operator<<= (CORBA::any &IT_a, Event_Comm::Notification_Receiver_ptr IT_t); +CORBA::Boolean operator>>= (const CORBA::any &IT_a, Event_Comm::Notification_Receiver_ptr& IT_t); + + +void operator<<= (CORBA::any &IT_a, Event_Comm::Notifier_ptr IT_t); +CORBA::Boolean operator>>= (const CORBA::any &IT_a, Event_Comm::Notifier_ptr& IT_t); + + +void operator<<= (CORBA::any &IT_a, const Event_Comm::Notification& IT_t); +CORBA::Boolean operator>>= (const CORBA::any &IT_a, Event_Comm::Notification*& IT_t); + + +#endif diff --git a/apps/Orbix-Examples/Event_Comm/libsrc/Event_Comm.idl b/apps/Orbix-Examples/Event_Comm/libsrc/Event_Comm.idl new file mode 100644 index 00000000000..26890129d70 --- /dev/null +++ b/apps/Orbix-Examples/Event_Comm/libsrc/Event_Comm.idl @@ -0,0 +1,92 @@ +/* -*- C++ -*- */ +// @(#)Event_Comm.idl 1.1 10/18/96 + + +// ============================================================================ +// +// = LIBRARY +// EventComm +// +// = FILENAME +// Event_Comm.idl +// +// = DESCRIPTION +// The CORBA IDL module for distributed event notification. +// +// = AUTHOR +// Douglas C. Schmidt (schmidt@cs.wustl.edu) +// +// ============================================================================ + +#ifndef _EVENT_COMM_IDL +#define _EVENT_COMM_IDL + +module Event_Comm + // = TITLE + // The CORBA IDL module for distributed event notification. + // + // = DESCRIPTION +{ + struct Notification + // = TITLE + // Defines the interface for an event <Notification>. + // + // = This is the type passed by the Notifier to the Notification_Receiver. + // Since it contains an <any>, it can hold any values. Naturally, + // the consumer must understand how to interpret this! + { + string tag_; + // Tag for the notification. + + // any value_; + // A notification can contain anything. + +// Object object_ref_; + // Object reference for callbacks. + }; + + interface Notification_Receiver + // = TITLE + // Defines the interface for a <Notification_Receiver> of events. + // Note that all operations are <oneway> to avoid blocking. + // + // = DESCRIPTION + { + oneway void receive_notification (in Notification notification); + // Inform the <Notification_Receiver> that <event> has occurred. + + oneway void disconnect (in string reason); + // Disconnect the <Notification_Receiver> from the <Notifier>, + // giving it the <reason>. + }; + + interface Notifier + // = TITLE + // Defines the interface for a <Notifier> of events. + // + // = DESCRIPTION + { + oneway void send_disconnect (in string reason); + // Disconnect all the receivers, giving them the <reason>. + + oneway void send_notification (in Notification notification); + // Send the <Notification> to all the consumers who + // have subscribed and who match the filtering criteria. + + oneway void subscribe (in Notification_Receiver notification_receiver, + in string filtering_criteria); + // Subscribe the <Notification_Receiver> to receive events that + // match the regular expresssion <filtering_criteria> applied by + // the <Notifier>. If <filtering_criteria> is "" then all events + // are matched. + + oneway void unsubscribe (in Notification_Receiver notification_receiver, + in string filtering_criteria); + // Unsubscribe the <Notification_Receiver> that matches the + // filtering criteria. If <filtering_criteria> is "" then + // all <Notification_Receivers> with the matching object reference + // are removed. + }; +}; + +#endif /* _EVENT_COMM_IDL */ diff --git a/apps/Orbix-Examples/Event_Comm/libsrc/Event_CommC.cpp b/apps/Orbix-Examples/Event_Comm/libsrc/Event_CommC.cpp new file mode 100644 index 00000000000..eee25b11a72 --- /dev/null +++ b/apps/Orbix-Examples/Event_Comm/libsrc/Event_CommC.cpp @@ -0,0 +1,351 @@ + +// @(#)Event_CommC.cpp 1.1 10/18/96 + +#include "Event_Comm.hh" + + +#ifndef Event_Comm_Notification_Ops +#define Event_Comm_Notification_Ops + +void Event_Comm::Notification:: encodeOp (CORBA::Request &IT_r) const { + IT_r.encodeStringOp (tag_); +} + +void Event_Comm::Notification:: decodeOp (CORBA::Request &IT_r) { + IT_r.decodeStringOp(tag_); +} + +void Event_Comm::Notification:: decodeInOutOp (CORBA::Request &IT_r) { + IT_r.decodeInOutStrOp(tag_, 0); +} + +void* Event_Comm::Notification:: IT_anySupport (CORBA::Request &IT_r, + void *& IT_v, void *IT_to, const CORBA::Flags& IT_f) { + Event_Comm::Notification* IT_l = (Event_Comm::Notification*)IT_v; + + if (IT_f.isSetAll (CORBA::ARG_INOUT)) { + if (!IT_l) + IT_l = new Event_Comm::Notification(); + IT_l -> decodeInOutOp (IT_r); + IT_v = IT_l; + } + else if (IT_f.isSet (CORBA::ARG_IN)) { + IT_l -> encodeOp (IT_r); + } + else if (IT_f.isSet (CORBA::ARG_OUT)) { + if (!IT_l) + IT_l = new Event_Comm::Notification(); + IT_l -> decodeOp (IT_r); + IT_v = IT_l; + } + else if (IT_f.isSet (CORBA::_ANY_ASSIGN)) { + Event_Comm::Notification*IT_s = IT_to ? (Event_Comm::Notification*)IT_to : new Event_Comm::Notification; + *IT_s = *IT_l; return IT_s; + } + else if (IT_f.isSet (CORBA::_ANY_DELETE)) { + if (IT_to) IT_l->Event_Comm::Notification::~Notification(); + else delete IT_l; + return NULL; + } + else if (IT_f.isSet (CORBA::_ANY_SIZEOF)) { + return (void*) (sizeof (Event_Comm::Notification)); + } + else if (IT_f.isNil ()) { + if (!IT_l) + IT_l = new Event_Comm::Notification(); + IT_l -> decodeOp (IT_r); + IT_v = IT_l; + } + return NULL; +} + +const void *Event_Comm::Notification:: IT_fn = +CORBA::anyTable.record ("Event_Comm::Notification", &Event_Comm::Notification:: IT_anySupport); + +Event_Comm::Notification &Event_Comm::Notification:: operator= (const Event_Comm::IONANC_Notification& IT_p) { + this->operator= (*(Event_Comm::Notification*) &IT_p); + return (*this); +} + +Event_Comm::Notification:: operator Event_Comm::IONANC_Notification () { + Event_Comm::IONANC_Notification tmp; + memset (&tmp, 0, sizeof(tmp)); + ((Event_Comm::Notification *) &tmp)->operator= (*this); + return tmp; +} + +Event_Comm::Notification:: operator const Event_Comm::IONANC_Notification () const { + Event_Comm::IONANC_Notification tmp; + memset (&tmp, 0, sizeof(tmp)); + ((Event_Comm::Notification *) &tmp)->operator= (*this); + return tmp; +} + +Event_Comm::Notification::~Notification () { + if (tag_) delete [] tag_; +} + +Event_Comm::Notification:: Notification (const Event_Comm::Notification &IT_s) + { + if (IT_s.tag_) { + tag_=new char [strlen(IT_s.tag_)+1]; + strcpy (tag_, IT_s.tag_); + } + else { + tag_ = NULL; + } +} + +Event_Comm::Notification:: Notification () { + tag_ = NULL; +} + +Event_Comm::Notification &Event_Comm::Notification:: operator= (const Event_Comm::Notification& IT_s) { + if (this == &IT_s) return *this; + if (tag_) delete [] tag_; + if (IT_s.tag_) { + tag_=new char [strlen(IT_s.tag_)+1]; + strcpy (tag_, IT_s.tag_); + } + else { + tag_ = NULL; + } + return *this; +} + +Event_Comm::IONANC_Notification:: operator Event_Comm::Notification () { + return (*((Event_Comm::Notification *) this)); +} + +Event_Comm::IONANC_Notification:: operator const Event_Comm::Notification () const { + return (*((const Event_Comm::Notification *) this)); +} + + +#endif +Event_Comm::Notification_Receiver::Notification_Receiver (char *IT_OR) { + m_pptr = new Notification_Receiver_dispatch (IT_OR, this,(CORBA::Object*)this); +} + +#ifndef Event_Comm_Notification_ReceiverForwC +#define Event_Comm_Notification_ReceiverForwC +CORBA::ObjectRef Event_Comm::Notification_Receiver_getBase(void *IT_p){ + return (Event_Comm::Notification_Receiver*)IT_p;} + +void Event_Comm::Notification_Receiver_release (void *IT_p, CORBA::Environment &IT_env) { + ((Event_Comm::Notification_Receiver*)IT_p)->_release(IT_env);} + +Event_Comm::Notification_Receiver* Event_Comm::Notification_Receiver_duplicate (void *IT_p, CORBA::Environment &IT_env) { + return ((Event_Comm::Notification_Receiver*)IT_p)->_duplicate(IT_env); } +#endif + + + +Event_Comm::Notification_Receiver* Event_Comm::Notification_Receiver:: _bind (const char* IT_markerServer, const char* host, + const CORBA::Context &IT_c, + CORBA::Environment &IT_env) { + Notification_Receiver*IT_p = + (Notification_Receiver*)CORBA::Factory.New (IT_markerServer, IT_env, IT_c, host, + Event_Comm_Notification_Receiver_IMPL, Event_Comm_Notification_Receiver_IR); + return IT_p ? IT_p->_duplicate () : NULL; } + + + +Event_Comm::Notification_Receiver* Event_Comm::Notification_Receiver:: _bind (CORBA::Environment &IT_env) { + return _bind (NULL,NULL,CORBA::Context(), IT_env); } + + +Event_Comm::Notification_Receiver* Event_Comm::Notification_Receiver:: _bind (const char* IT_markerServer, const char* host, + CORBA::Environment &IT_env) { + return _bind (IT_markerServer, host, CORBA::Context (), IT_env); } +Event_Comm::Notification_Receiver* Event_Comm::Notification_Receiver::_narrow (CORBA::Object* IT_obj, CORBA::Environment &IT_env) { + Event_Comm::Notification_Receiver* IT_p = (Event_Comm::Notification_Receiver*)CORBA::Object::_castDown (IT_obj, Event_Comm_Notification_Receiver_IR, IT_env); + return IT_p ? IT_p->_duplicate(IT_env) : NULL; + } + +void* Event_Comm::Notification_ReceiverProxyFactoryClass::New (char *IT_OR, CORBA::Environment&) { + return new Notification_Receiver(IT_OR);} + +void* Event_Comm::Notification_ReceiverProxyFactoryClass::New2 () { + return new Notification_Receiver();} + +void* Event_Comm::Notification_ReceiverProxyFactoryClass::IT_castUp (void *IT_p, char* IT_s) { + void *IT_l; + if (!CORBA::_interfaceCmp (IT_s,Event_Comm_Notification_Receiver_IR)) + return IT_p; + else if (IT_l=CORBA::ObjectFactoryClass::IT_castUp((CORBA::Object*)((Event_Comm::Notification_Receiver*)IT_p),IT_s)) + return IT_l; + else return NULL; + } + + +CORBA::PPTR* Event_Comm::Notification_ReceiverProxyFactoryClass::pptr (void *IT_p) { + return ((Event_Comm::Notification_Receiver*)IT_p)->_pptr ();} + +void Event_Comm::Notification_ReceiverProxyFactoryClass::baseInterfaces (_IDL_SEQUENCE_string& seq) { + add (seq, Event_Comm_Notification_Receiver_IR); + CORBA::ObjectFactoryClass::baseInterfaces (seq); +} + + void Event_Comm::Notification_Receiver:: receive_notification(const Event_Comm::Notification& notification, CORBA::Environment &IT_env) { + + if (IT_env || m_isNull) return ; + CORBA::Request IT_r (this, "receive_notification",IT_env,1,1); + if (!IT_r.isException (IT_env)) { + notification.encodeOp (IT_r); + } + + IT_r.invoke (CORBA::Flags(CORBA::INV_NO_RESPONSE), IT_env); + } + + void Event_Comm::Notification_Receiver:: disconnect(const char * reason, CORBA::Environment &IT_env) { + + if (IT_env || m_isNull) return ; + CORBA::Request IT_r (this, "disconnect",IT_env,1,1); + if (!IT_r.isException (IT_env)) { + IT_r.encodeStringOp (reason); + } + + IT_r.invoke (CORBA::Flags(CORBA::INV_NO_RESPONSE), IT_env); + } + + +Event_Comm::Notification_ReceiverProxyFactoryClass Event_Comm::Notification_ReceiverProxyFactory(1); + + +#ifndef Event_Comm_Notification_Receiver_dispatch_impl + +unsigned char Event_Comm::Notification_Receiver_dispatch::dispatch (CORBA::Request &IT_r, + unsigned char, void *) { + IT_r.makeRuntimeException1 ("Event_Comm::Notification_Receiver"); + return 0; +} + +#endif + +Event_Comm::Notifier::Notifier (char *IT_OR) { + m_pptr = new Notifier_dispatch (IT_OR, this,(CORBA::Object*)this); +} + +#ifndef Event_Comm_NotifierForwC +#define Event_Comm_NotifierForwC +CORBA::ObjectRef Event_Comm::Notifier_getBase(void *IT_p){ + return (Event_Comm::Notifier*)IT_p;} + +void Event_Comm::Notifier_release (void *IT_p, CORBA::Environment &IT_env) { + ((Event_Comm::Notifier*)IT_p)->_release(IT_env);} + +Event_Comm::Notifier* Event_Comm::Notifier_duplicate (void *IT_p, CORBA::Environment &IT_env) { + return ((Event_Comm::Notifier*)IT_p)->_duplicate(IT_env); } +#endif + + + +Event_Comm::Notifier* Event_Comm::Notifier:: _bind (const char* IT_markerServer, const char* host, + const CORBA::Context &IT_c, + CORBA::Environment &IT_env) { + Notifier*IT_p = + (Notifier*)CORBA::Factory.New (IT_markerServer, IT_env, IT_c, host, + Event_Comm_Notifier_IMPL, Event_Comm_Notifier_IR); + return IT_p ? IT_p->_duplicate () : NULL; } + + + +Event_Comm::Notifier* Event_Comm::Notifier:: _bind (CORBA::Environment &IT_env) { + return _bind (NULL,NULL,CORBA::Context(), IT_env); } + + +Event_Comm::Notifier* Event_Comm::Notifier:: _bind (const char* IT_markerServer, const char* host, + CORBA::Environment &IT_env) { + return _bind (IT_markerServer, host, CORBA::Context (), IT_env); } +Event_Comm::Notifier* Event_Comm::Notifier::_narrow (CORBA::Object* IT_obj, CORBA::Environment &IT_env) { + Event_Comm::Notifier* IT_p = (Event_Comm::Notifier*)CORBA::Object::_castDown (IT_obj, Event_Comm_Notifier_IR, IT_env); + return IT_p ? IT_p->_duplicate(IT_env) : NULL; + } + +void* Event_Comm::NotifierProxyFactoryClass::New (char *IT_OR, CORBA::Environment&) { + return new Notifier(IT_OR);} + +void* Event_Comm::NotifierProxyFactoryClass::New2 () { + return new Notifier();} + +void* Event_Comm::NotifierProxyFactoryClass::IT_castUp (void *IT_p, char* IT_s) { + void *IT_l; + if (!CORBA::_interfaceCmp (IT_s,Event_Comm_Notifier_IR)) + return IT_p; + else if (IT_l=CORBA::ObjectFactoryClass::IT_castUp((CORBA::Object*)((Event_Comm::Notifier*)IT_p),IT_s)) + return IT_l; + else return NULL; + } + + +CORBA::PPTR* Event_Comm::NotifierProxyFactoryClass::pptr (void *IT_p) { + return ((Event_Comm::Notifier*)IT_p)->_pptr ();} + +void Event_Comm::NotifierProxyFactoryClass::baseInterfaces (_IDL_SEQUENCE_string& seq) { + add (seq, Event_Comm_Notifier_IR); + CORBA::ObjectFactoryClass::baseInterfaces (seq); +} + + void Event_Comm::Notifier:: send_disconnect(const char * reason, CORBA::Environment &IT_env) { + + if (IT_env || m_isNull) return ; + CORBA::Request IT_r (this, "send_disconnect",IT_env,1,1); + if (!IT_r.isException (IT_env)) { + IT_r.encodeStringOp (reason); + } + + IT_r.invoke (CORBA::Flags(CORBA::INV_NO_RESPONSE), IT_env); + } + + void Event_Comm::Notifier:: send_notification(const Event_Comm::Notification& notification, CORBA::Environment &IT_env) { + + if (IT_env || m_isNull) return ; + CORBA::Request IT_r (this, "send_notification",IT_env,1,1); + if (!IT_r.isException (IT_env)) { + notification.encodeOp (IT_r); + } + + IT_r.invoke (CORBA::Flags(CORBA::INV_NO_RESPONSE), IT_env); + } + + void Event_Comm::Notifier:: subscribe(Event_Comm::Notification_Receiver* notification_receiver, const char * filtering_criteria, CORBA::Environment &IT_env) { + + if (IT_env || m_isNull) return ; + CORBA::Request IT_r (this, "subscribe",IT_env,1,1); + if (!IT_r.isException (IT_env)) { + IT_r << (CORBA::Object*)notification_receiver; + + IT_r.encodeStringOp (filtering_criteria); + } + + IT_r.invoke (CORBA::Flags(CORBA::INV_NO_RESPONSE), IT_env); + } + + void Event_Comm::Notifier:: unsubscribe(Event_Comm::Notification_Receiver* notification_receiver, const char * filtering_criteria, CORBA::Environment &IT_env) { + + if (IT_env || m_isNull) return ; + CORBA::Request IT_r (this, "unsubscribe",IT_env,1,1); + if (!IT_r.isException (IT_env)) { + IT_r << (CORBA::Object*)notification_receiver; + + IT_r.encodeStringOp (filtering_criteria); + } + + IT_r.invoke (CORBA::Flags(CORBA::INV_NO_RESPONSE), IT_env); + } + + +Event_Comm::NotifierProxyFactoryClass Event_Comm::NotifierProxyFactory(1); + + +#ifndef Event_Comm_Notifier_dispatch_impl + +unsigned char Event_Comm::Notifier_dispatch::dispatch (CORBA::Request &IT_r, + unsigned char, void *) { + IT_r.makeRuntimeException1 ("Event_Comm::Notifier"); + return 0; +} + +#endif + diff --git a/apps/Orbix-Examples/Event_Comm/libsrc/Event_CommS.cpp b/apps/Orbix-Examples/Event_Comm/libsrc/Event_CommS.cpp new file mode 100644 index 00000000000..9adc4cb26d4 --- /dev/null +++ b/apps/Orbix-Examples/Event_Comm/libsrc/Event_CommS.cpp @@ -0,0 +1,166 @@ + +// @(#)Event_CommS.cpp 1.1 10/18/96 + +#include "Event_Comm.hh" + + +#define Event_Comm_Notification_Receiver_dispatch_impl + +unsigned char Event_Comm::Notification_Receiver_dispatch::dispatch (CORBA::Request &IT_r, + unsigned char IT_isTarget, void *IT_pp) { + if (!IT_pp) + IT_pp = m_obj; + const char *IT_s = IT_r.getOperation (); + if (!strcmp(IT_s,"receive_notification")) { + CORBA::Environment IT_env (IT_r); + CORBA::Filter* IT_f = CORBA::Orbix.getFilter (); + if (!IT_r.tcAssert ("\ +Ro~receive_notification~+notification{R~Event_Comm::Notification~tag_{0}},>{v},O{}\ +")) + return 1; + Event_Comm::Notification notification; + notification.decodeOp (IT_r); + + if (IT_f && !IT_r.isException (IT_env)) + IT_f->inRequestPostM (IT_r, IT_env); + if (!IT_r.isException (IT_env)) + ((Event_Comm::Notification_Receiver*)IT_pp)->receive_notification ( notification, IT_env); + + IT_r.replyNoResults (CORBA::Flags(CORBA::INV_NO_RESPONSE),IT_env); + return 1; + } + + else if (!strcmp(IT_s,"disconnect")) { + CORBA::Environment IT_env (IT_r); + CORBA::Filter* IT_f = CORBA::Orbix.getFilter (); + if (!IT_r.tcAssert ("\ +Ro~disconnect~+reason{0},>{v},O{}\ +")) + return 1; + char * reason; + IT_r.decodeStringOp(reason); + + if (IT_f && !IT_r.isException (IT_env)) + IT_f->inRequestPostM (IT_r, IT_env); + if (!IT_r.isException (IT_env)) + ((Event_Comm::Notification_Receiver*)IT_pp)->disconnect ( reason, IT_env); + + delete [] reason; + IT_r.replyNoResults (CORBA::Flags(CORBA::INV_NO_RESPONSE),IT_env); + return 1; + } + + else if (IT_isTarget) + IT_r.makeRuntimeException2 (); + + return 0; +} + +#define Event_Comm_Notifier_dispatch_impl + +unsigned char Event_Comm::Notifier_dispatch::dispatch (CORBA::Request &IT_r, + unsigned char IT_isTarget, void *IT_pp) { + if (!IT_pp) + IT_pp = m_obj; + const char *IT_s = IT_r.getOperation (); + if (!strcmp(IT_s,"send_disconnect")) { + CORBA::Environment IT_env (IT_r); + CORBA::Filter* IT_f = CORBA::Orbix.getFilter (); + if (!IT_r.tcAssert ("\ +Ro~send_disconnect~+reason{0},>{v},O{}\ +")) + return 1; + char * reason; + IT_r.decodeStringOp(reason); + + if (IT_f && !IT_r.isException (IT_env)) + IT_f->inRequestPostM (IT_r, IT_env); + if (!IT_r.isException (IT_env)) + ((Event_Comm::Notifier*)IT_pp)->send_disconnect ( reason, IT_env); + + delete [] reason; + IT_r.replyNoResults (CORBA::Flags(CORBA::INV_NO_RESPONSE),IT_env); + return 1; + } + + else if (!strcmp(IT_s,"send_notification")) { + CORBA::Environment IT_env (IT_r); + CORBA::Filter* IT_f = CORBA::Orbix.getFilter (); + if (!IT_r.tcAssert ("\ +Ro~send_notification~+notification{R~Event_Comm::Notification~tag_{0}},>{v},O{}\ +")) + return 1; + Event_Comm::Notification notification; + notification.decodeOp (IT_r); + + if (IT_f && !IT_r.isException (IT_env)) + IT_f->inRequestPostM (IT_r, IT_env); + if (!IT_r.isException (IT_env)) + ((Event_Comm::Notifier*)IT_pp)->send_notification ( notification, IT_env); + + IT_r.replyNoResults (CORBA::Flags(CORBA::INV_NO_RESPONSE),IT_env); + return 1; + } + + else if (!strcmp(IT_s,"subscribe")) { + CORBA::Environment IT_env (IT_r); + CORBA::Filter* IT_f = CORBA::Orbix.getFilter (); + if (!IT_r.tcAssert ("\ +Ro~subscribe~+notification_receiver{O~Event_Comm::Notification_Receiver},+filtering_criteria{0},>{v},O{}\ +")) + return 1; + Event_Comm::Notification_Receiver* notification_receiver; + notification_receiver = (Event_Comm::Notification_Receiver*) IT_r.decodeObjRef (Event_Comm_Notification_Receiver_IR); + if (notification_receiver) notification_receiver->_duplicate (); + + char * filtering_criteria; + IT_r.decodeStringOp(filtering_criteria); + + if (IT_f && !IT_r.isException (IT_env)) + IT_f->inRequestPostM (IT_r, IT_env); + if (!IT_r.isException (IT_env)) + ((Event_Comm::Notifier*)IT_pp)->subscribe ( notification_receiver, filtering_criteria, IT_env); + + if (notification_receiver) notification_receiver->_release (); + + delete [] filtering_criteria; + IT_r.replyNoResults (CORBA::Flags(CORBA::INV_NO_RESPONSE),IT_env); + + return 1; + } + + else if (!strcmp(IT_s,"unsubscribe")) { + CORBA::Environment IT_env (IT_r); + CORBA::Filter* IT_f = CORBA::Orbix.getFilter (); + if (!IT_r.tcAssert ("\ +Ro~unsubscribe~+notification_receiver{O~Event_Comm::Notification_Receiver},+filtering_criteria{0},>{v},O{}\ +")) + return 1; + Event_Comm::Notification_Receiver* notification_receiver; + notification_receiver = (Event_Comm::Notification_Receiver*) IT_r.decodeObjRef (Event_Comm_Notification_Receiver_IR); + if (notification_receiver) notification_receiver->_duplicate (); + + char * filtering_criteria; + IT_r.decodeStringOp(filtering_criteria); + + if (IT_f && !IT_r.isException (IT_env)) + IT_f->inRequestPostM (IT_r, IT_env); + if (!IT_r.isException (IT_env)) + ((Event_Comm::Notifier*)IT_pp)->unsubscribe ( notification_receiver, filtering_criteria, IT_env); + + if (notification_receiver) notification_receiver->_release (); + + delete [] filtering_criteria; + IT_r.replyNoResults (CORBA::Flags(CORBA::INV_NO_RESPONSE),IT_env); + + return 1; + } + + else if (IT_isTarget) + IT_r.makeRuntimeException2 (); + + return 0; +} + +#include "Event_CommC.cpp" + diff --git a/apps/Orbix-Examples/Event_Comm/libsrc/Event_Comm_i.h b/apps/Orbix-Examples/Event_Comm/libsrc/Event_Comm_i.h new file mode 100644 index 00000000000..2db73e2f616 --- /dev/null +++ b/apps/Orbix-Examples/Event_Comm/libsrc/Event_Comm_i.h @@ -0,0 +1,38 @@ +/* -*- C++ -*- */ +// @(#)Event_Comm_i.h 1.1 10/18/96 + + +// ============================================================================ +// +// = LIBRARY +// EventComm +// +// = FILENAME +// Event_Comm_i.h +// +// = DESCRIPTION +// Class interface for the implementation of the distributed +// event notification mechanism. +// +// = AUTHOR +// Douglas C. Schmidt (schmidt@cs.wustl.edu) +// +// ============================================================================ + +#if !defined (_EVENT_COMM_I_H) +#define _EVENT_COMM_I_H + +#include "Notification_Receiver_i.h" +#include "Notifier_i.h" + +#if defined (ACE_HAS_ORBIX) +#define nil 0 + +// Tie the Notification_Receiver and Notifier implementation classes +// together with the IDL interface. + +DEF_TIE_Event_Comm_Notification_Receiver (Notification_Receiver_i) +DEF_TIE_Event_Comm_Notifier (Notifier_i) + +#endif /* ACE_HAS_ORBIX */ +#endif /* _EVENT_COMM_I_H */ diff --git a/apps/Orbix-Examples/Event_Comm/libsrc/Makefile b/apps/Orbix-Examples/Event_Comm/libsrc/Makefile new file mode 100644 index 00000000000..add9fd31151 --- /dev/null +++ b/apps/Orbix-Examples/Event_Comm/libsrc/Makefile @@ -0,0 +1,113 @@ +#---------------------------------------------------------------------------- +# @(#)Makefile 1.1 10/18/96 +# +# Makefile for the Event Communications library +#---------------------------------------------------------------------------- + +#---------------------------------------------------------------------------- +# Local macros +#---------------------------------------------------------------------------- + +LIB = libEvent_Comm.a +SHLIB = libEvent_Comm.so + +FILES = Event_CommS \ + Event_CommC \ + Notifier_i \ + Notification_Receiver_i + +LSRC = $(addsuffix .cpp,$(FILES)) +LOBJ = $(addsuffix .o,$(FILES)) +SHOBJ = $(addsuffix .so,$(FILES)) + +VLDLIBS = $(LDLIBS:%=%$(VAR)) + +BUILD = $(VLIB) + +#---------------------------------------------------------------------------- +# Include macros and targets +#---------------------------------------------------------------------------- + +include $(WRAPPER_ROOT)/include/makeinclude/wrapper_macros.GNU +include $(WRAPPER_ROOT)/include/makeinclude/macros.GNU +include $(WRAPPER_ROOT)/include/makeinclude/rules.common.GNU +include $(WRAPPER_ROOT)/include/makeinclude/rules.nonested.GNU +include $(WRAPPER_ROOT)/include/makeinclude/rules.lib.GNU +include $(WRAPPER_ROOT)/include/makeinclude/rules.local.GNU + +#---------------------------------------------------------------------------- +# Orbix related macros and target settings. +#---------------------------------------------------------------------------- + +ORBIX_BINDIR = $(ORBIX_ROOT)/bin +ORBIX_LIBDIR = $(ORBIX_ROOT)/lib +ORBIX_INCDIR = $(ORBIX_ROOT)/include + +CPPFLAGS += -DEXCEPTIONS -I$(ORBIX_INCDIR) -DWANT_ORBIX_FDS +LDFLAGS += -L$(ORBIX_LIBDIR) -R $(ORBIX_LIBDIR) + +IDLFLAGS = -A -s S.cpp -c C.cpp + +#---------------------------------------------------------------------------- +# Local targets +#---------------------------------------------------------------------------- + +#---------------------------------------------------------------------------- +# Dependencies +#---------------------------------------------------------------------------- + +# DO NOT DELETE THIS LINE -- g++dep uses it. +# DO NOT PUT ANYTHING AFTER THIS LINE, IT WILL GO AWAY. + +Event_CommS.o: Event_CommS.cpp Event_Comm.hh Event_CommC.cpp +Event_CommC.o: Event_CommC.cpp Event_Comm.hh +Notifier_i.o: Notifier_i.cpp \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Log_Msg.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Log_Record.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/sysincludes.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/config.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Log_Priority.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Log_Record.i \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Log_Msg.i \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Thread_Specific.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Synch.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Time_Value.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Synch_T.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Thread_Specific.i \ + Notification_Receiver_i.h Notifier_i.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Map_Manager.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/SString.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/SString.i \ + Event_Comm.hh +Notification_Receiver_i.o: Notification_Receiver_i.cpp \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Log_Msg.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Log_Record.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/sysincludes.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/config.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Log_Priority.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Log_Record.i \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Log_Msg.i \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Thread_Specific.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Synch.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Time_Value.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Synch_T.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Thread_Specific.i \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Service_Config.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Reactor.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Handle_Set.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Handle_Set.i \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Timer_Queue.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Event_Handler.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Timer_Queue.i \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Signal.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Set.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Thread.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Token.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Reactor.i \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Service_Object.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Shared_Object.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Service_Record.h \ + /project/adaptive/ACE_wrappers/build/SunOS5.4/include/ace/Thread_Manager.h \ + Notification_Receiver_i.h + +# IF YOU PUT ANYTHING HERE IT WILL GO AWAY diff --git a/apps/Orbix-Examples/Event_Comm/libsrc/Notification.idl b/apps/Orbix-Examples/Event_Comm/libsrc/Notification.idl new file mode 100644 index 00000000000..ecfd5adb8e0 --- /dev/null +++ b/apps/Orbix-Examples/Event_Comm/libsrc/Notification.idl @@ -0,0 +1,42 @@ +/* -*- C++ -*- */ +// @(#)Notification.idl 1.1 10/18/96 + + +// ============================================================================ +// +// = LIBRARY +// EventComm +// +// = FILENAME +// Notification.idl +// +// = DESCRIPTION +// This is the CORBA IDL interface for the Event Communication <Notification>. +// +// = AUTHOR +// Douglas C. Schmidt (schmidt@cs.wustl.edu) +// +// ============================================================================ + +#ifndef _NOTIFICATION_IDL +#define _NOTIFICATION_IDL + +struct Notification + // = TITLE + // Defines the interface for an event <Notification>. + // + // = This is the type passed by the Notifier to the Notification_Receiver. + // Since it contains an <any>, it can hold any values. Naturally, + // the consumer must understand how to interpret this! +{ + string tag_; + // Tag for the notification. + +// any value_; + // A notification can contain anything. + + Object object_ref_; + // Object reference for callbacks. +}; + +#endif /* _NOTIFICATION_IDL */ diff --git a/apps/Orbix-Examples/Event_Comm/libsrc/Notification_Receiver.idl b/apps/Orbix-Examples/Event_Comm/libsrc/Notification_Receiver.idl new file mode 100644 index 00000000000..222f18782f7 --- /dev/null +++ b/apps/Orbix-Examples/Event_Comm/libsrc/Notification_Receiver.idl @@ -0,0 +1,42 @@ +/* -*- C++ -*- */ +// @(#)Notification_Receiver.idl 1.1 10/18/96 + + +// ============================================================================ +// +// = LIBRARY +// EventComm +// +// = FILENAME +// Notification_Receiver.idl +// +// = DESCRIPTION +// The CORBA IDL interface for the Event Communication +// <Notification_Receiver> component. +// +// = AUTHOR +// Douglas C. Schmidt (schmidt@cs.wustl.edu) +// +// ============================================================================ + +#include "Notification.idl" + +#ifndef _Notification_Receiver_iDL +#define _Notification_Receiver_iDL + +interface Notification_Receiver + // = TITLE + // Defines the interface for a <Notification_Receiver> of events. + // Note that all operations are <oneway> to avoid blocking. + // + // = DESCRIPTION +{ + oneway void receive_notification (in Notification notification); + // Inform the <Notification_Receiver> that <event> has occurred. + + oneway void disconnect (in string reason); + // Disconnect the <Notification_Receiver> from the <Notifier>, + // giving it the <reason>. +}; + +#endif /* _Notification_Receiver_iDL */ diff --git a/apps/Orbix-Examples/Event_Comm/libsrc/Notification_Receiver_i.cpp b/apps/Orbix-Examples/Event_Comm/libsrc/Notification_Receiver_i.cpp new file mode 100644 index 00000000000..337cf91e31e --- /dev/null +++ b/apps/Orbix-Examples/Event_Comm/libsrc/Notification_Receiver_i.cpp @@ -0,0 +1,39 @@ +#include "ace/Log_Msg.h" +// @(#)Notification_Receiver_i.cpp 1.1 10/18/96 + +#include "ace/Service_Config.h" +#include "Notification_Receiver_i.h" + +#if defined (ACE_HAS_ORBIX) + +Notification_Receiver_i::Notification_Receiver_i (void) +{ +} + +Notification_Receiver_i::~Notification_Receiver_i (void) +{ +} + +// Inform the <Event_Comm::Notification_Receiver> that <event> has occurred. + +void +Notification_Receiver_i::receive_notification + (const Event_Comm::Notification ¬ification, + CORBA::Environment &IT_env) +{ + const char *tmpstr = notification.tag_; + + ACE_DEBUG ((LM_DEBUG, "**** got notification = %s\n", tmpstr)); +} + +// Disconnect the <Event_Comm::Notification_Receiver> from the <Event_Comm::Notifier>. + +void +Notification_Receiver_i::disconnect (const char *reason, + CORBA::Environment &IT_env) +{ + ACE_DEBUG ((LM_DEBUG, "**** got disconnected due to %s\n", reason)); + ACE_Service_Config::end_reactor_event_loop (); +} + +#endif /* ACE_HAS_ORBIX */ diff --git a/apps/Orbix-Examples/Event_Comm/libsrc/Notification_Receiver_i.h b/apps/Orbix-Examples/Event_Comm/libsrc/Notification_Receiver_i.h new file mode 100644 index 00000000000..4f0bcc980e4 --- /dev/null +++ b/apps/Orbix-Examples/Event_Comm/libsrc/Notification_Receiver_i.h @@ -0,0 +1,48 @@ +/* -*- C++ -*- */ +// @(#)Notification_Receiver_i.h 1.1 10/18/96 + + +// ============================================================================ +// +// = LIBRARY +// EventComm +// +// = FILENAME +// Notification_Receiver__i.h +// +// = DESCRIPTION +// Class interface for the implementation of the <Notification_Receiver> +// +// = AUTHOR +// Douglas C. Schmidt (schmidt@cs.wustl.edu) +// +// ============================================================================ + +#if !defined (_Notification_Receiver_i_H) +#define _Notification_Receiver_i_H + +#if defined (ACE_HAS_ORBIX) +#include "Event_Comm.hh" + +class Notification_Receiver_i + // = TITLE + // Defines the implementation class for event <Notification_Receivers>. + // + // = DESCRIPTION +{ +public: + Notification_Receiver_i (void); + ~Notification_Receiver_i (void); + + virtual void receive_notification (const Event_Comm::Notification ¬ification, + CORBA::Environment &IT_env); + // Pass the <Notification> to the <Notification_Receiver>. + + virtual void disconnect (const char *reason, + CORBA::Environment &IT_env); + // Disconnect the <Notification_Receiver> from the <Notifier>, + // giving it the <reason>. +}; + +#endif /* ACE_HAS_ORBIX */ +#endif /* _Notification_Receiver_i_H */ diff --git a/apps/Orbix-Examples/Event_Comm/libsrc/Notifier.idl b/apps/Orbix-Examples/Event_Comm/libsrc/Notifier.idl new file mode 100644 index 00000000000..75e738e0627 --- /dev/null +++ b/apps/Orbix-Examples/Event_Comm/libsrc/Notifier.idl @@ -0,0 +1,49 @@ +/* -*- C++ -*- */ +// @(#)Notifier.idl 1.1 10/18/96 + + +// ============================================================================ +// +// = LIBRARY +// EventComm +// +// = FILENAME +// Notifier.idl +// +// = DESCRIPTION +// This is the CORBA IDL interface for the Event Communication <Notifier>. +// +// = AUTHOR +// Douglas C. Schmidt (schmidt@cs.wustl.edu) +// +// ============================================================================ + +#ifndef _Notifier_iDL +#define _Notifier_iDL + +#include "Notification.idl" +#include "Notification_Receiver.idl" + +interface Notifier + // = TITLE + // Defines the interface for a <Notifier> of events. + // + // = DESCRIPTION +{ + oneway void send_disconnect (in string reason); + // Disconnect all the receivers, giving them the <reason>. + + oneway void send_notification (in Notification notification); + // Send the <Notification> to all the consumers who + // have subscribed and who match the filtering criteria. + + oneway void subscribe (in Notification_Receiver notification_receiver, + in string filtering_criteria); + // Subscribe the <Notification_Receiver> to receive events that + // match <filtering_criteria> applied by the <Notifier>. + + oneway void unsubscribe (in Notification_Receiver notification_receiver); + // Unsubscribe the <Notification_Receiver>. +}; + +#endif /* _Notifier_iDL */ diff --git a/apps/Orbix-Examples/Event_Comm/libsrc/Notifier_i.cpp b/apps/Orbix-Examples/Event_Comm/libsrc/Notifier_i.cpp new file mode 100644 index 00000000000..1edefc82a29 --- /dev/null +++ b/apps/Orbix-Examples/Event_Comm/libsrc/Notifier_i.cpp @@ -0,0 +1,324 @@ +#include "ace/Log_Msg.h" +// @(#)Notifier_i.cpp 1.1 10/18/96 + +#include "Notification_Receiver_i.h" +#include "Notifier_i.h" + +#if defined (ACE_HAS_ORBIX) + +class Notification_Receiver_Entry + // = TITLE + // Keeps track of context information associated with + // a <Event_Comm::Notification_Receiver> entry. + // + // = DESCRIPTION + // +{ +public: + Notification_Receiver_Entry (Event_Comm::Notification_Receiver *notification_receiver, + const char *filtering_criteria); + ~Notification_Receiver_Entry (void); + + // = Set/get filtering criteria. + void criteria (const char *criteria); + const char *criteria (void); + + // = Set/get Event_Comm::Notification_Receiver object reference. + Event_Comm::Notification_Receiver *receiver (void); + void receiver (Event_Comm::Notification_Receiver *); + + // = Set/get the compiled regular expression buffer. + const char *regexp (void); + void regexp (char *); + +private: + const char *filtering_criteria_; + // String containing the filtering criteria. + + char *compiled_regexp_; + // Compiled representation of the regular expression (see + // regexpr(3g)). + + Event_Comm::Notification_Receiver *receiver_; + // Object reference for the Event_Comm::Notification_Receiver. +}; + +// = Set/get filtering criteria. + +void +Notification_Receiver_Entry::criteria (const char *criteria) +{ + ACE_OS::free (ACE_MALLOC_T (this->filtering_criteria_)); + this->filtering_criteria_ = ACE_OS::strdup (criteria); +} + +const char * +Notification_Receiver_Entry::criteria (void) +{ + return this->filtering_criteria_; +} + +// = Set/get Event_Comm::Notification_Receiver object reference. + +Event_Comm::Notification_Receiver * +Notification_Receiver_Entry::receiver (void) +{ + return this->receiver_; +} + +void +Notification_Receiver_Entry::receiver (Event_Comm::Notification_Receiver *receiver) +{ + this->receiver_ = receiver; +} + +const char * +Notification_Receiver_Entry::regexp (void) +{ + return this->compiled_regexp_; +} + +void +Notification_Receiver_Entry::regexp (char *regexp) +{ + ACE_OS::free (ACE_MALLOC_T (this->compiled_regexp_)); + this->compiled_regexp_ = regexp; +} + +Notification_Receiver_Entry::Notification_Receiver_Entry (Event_Comm::Notification_Receiver *receiver, + const char *filtering_criteria) + : receiver_ (receiver), + filtering_criteria_ (0), + compiled_regexp_ (0) +{ + char *compile_buffer = 0; + + this->criteria (filtering_criteria); + ACE_ASSERT (this->criteria ()); + + // Check for wildcard case first. + if (ACE_OS::strcmp (filtering_criteria, "") == 0) + compile_buffer = ACE_OS::strdup (""); + else // Compile the regular expression (the 0's cause ACE_OS::compile to allocate space). + compile_buffer = ACE_OS::compile (filtering_criteria, 0, 0); + + // Should throw an exception here! + ACE_ASSERT (compile_buffer != 0); + + this->regexp (compile_buffer); + ACE_ASSERT (this->regexp ()); + + // Increment the reference count since we are keeping a copy of + // this... + this->receiver_->_duplicate (this->receiver_); +} + +Notification_Receiver_Entry::~Notification_Receiver_Entry (void) +{ + ACE_OS::free (this->filtering_criteria_); + ACE_OS::free (this->compiled_regexp_); + // Decrement the object reference count. + CORBA::release (this->receiver_); +} + +Notifier_i::Notifier_i (size_t size) + : map_ (size) +{ +} + +// Add a new receiver to the table, being careful to check for +// duplicate entries. A receiver is considered a duplicate under +// the following circumstances: +// 1. It has the same marker name and the same filtering criteria +// 2. It has the same marker name and its filtering criteria is "" (the wild card). + +void +Notifier_i::subscribe (Event_Comm::Notification_Receiver *receiver_ref, + const char *filtering_criteria, + CORBA::Environment &IT_env) +{ + ACE_DEBUG ((LM_DEBUG, + "in Notifier_i::subscribe for %s with filtering criteria \"%s\"\n", + receiver_ref->_marker (), filtering_criteria)); + ACE_SString key (receiver_ref->_marker ()); + MAP_ITERATOR mi (this->map_); + + // Try to locate an entry using its marker name (which should be + // unique across the system). If we don't find the entry, or if the + // filtering criteria is different that is good news since we + // currently don't allow duplicates... In particular, if @@ Should + // duplicates be allowed? + + for (MAP_ENTRY *me = 0; mi.next (me) != 0; mi.advance ()) + { + Notification_Receiver_Entry *nr_entry = me->int_id_; + + // Check for a duplicate entry. + if (key == me->ext_id_ + && (ACE_OS::strcmp (filtering_criteria, "") == 0 + || ACE_OS::strcmp (filtering_criteria, nr_entry->criteria ()) == 0)) + { + // Inform the caller that the + // Event_Comm::Notification_Receiver * is already being + // used. + + errno = EADDRINUSE; + ACE_ERROR ((LM_ERROR, + "duplicate entry for receiver %s with criteria \"%s\"", + receiver_ref->_marker (), filtering_criteria)); + // Raise exception here??? + return; + } + } + + // If we get this far then we didn't find a duplicate, so add the + // new entry! + Notification_Receiver_Entry *nr_entry = + new Notification_Receiver_Entry (receiver_ref, filtering_criteria); + + if (nr_entry == 0) + { + errno = ENOMEM; + ACE_ERROR ((LM_ERROR, "%p\n", "new failed")); + // Raise exception here... + } + // Try to add new <Notification_Receiver_Entry> to the map. + else if (this->map_.bind (key, nr_entry) == -1) + { + // Prevent memory leaks. + delete nr_entry; + // Raise exception here... + ACE_ERROR ((LM_ERROR, "%p\n", "bind failed")); + } +} + +// Remove a receiver from the table. + +void +Notifier_i::unsubscribe (Event_Comm::Notification_Receiver *receiver_ref, + const char *filtering_criteria, + CORBA::Environment &IT_env) +{ + ACE_DEBUG ((LM_DEBUG, "in Notifier_i::unsubscribe for %s\n", + receiver_ref->_marker ())); + Notification_Receiver_Entry *nr_entry = 0; + ACE_SString key; + MAP_ITERATOR mi (this->map_); + int found = 0; + + // Don't make a copy since we are deleting... + key.rep ((char *) receiver_ref->_marker ()); + + // Locate <Notification_Receiver_Entry> and free up resources. @@ + // Note, we don't properly handle deallocation of KEYS! + + for (MAP_ENTRY *me = 0; mi.next (me) != 0; mi.advance ()) + { + if (key == me->ext_id_ + && (ACE_OS::strcmp (filtering_criteria, "") == 0 + || ACE_OS::strcmp (filtering_criteria, nr_entry->criteria ()) == 0)) + { + ACE_DEBUG ((LM_DEBUG, "removed entry %s with criteria \"%s\"\n", + receiver_ref->_marker (), filtering_criteria)); + found = 1; + // @@ This is a hack, we need a better approach! + if (this->map_.unbind (key, nr_entry) == -1) + ACE_ERROR ((LM_ERROR, "unbind failed for %s\n", + receiver_ref->_marker ())); + else + delete nr_entry; + } + } + + if (found == 0) + ACE_ERROR ((LM_ERROR, "entry %s with criteria \"%s\" not found\n", + receiver_ref->_marker (), filtering_criteria)); +} + +// Disconnect all the receivers, giving them the <reason>. + +void +Notifier_i::send_disconnect (const char *reason, + CORBA::Environment &IT_env) +{ + ACE_DEBUG ((LM_DEBUG, "in Notifier_i::send_disconnect = %s\n", reason)); + + MAP_ITERATOR mi (this->map_); + int count = 0; + + // Notify all the receivers, taking into account the filtering criteria. + + for (MAP_ENTRY *me = 0; mi.next (me) != 0; mi.advance ()) + { + Event_Comm::Notification_Receiver *receiver_ref = me->int_id_->receiver (); + ACE_ASSERT (receiver_ref->_marker () != 0); + ACE_DEBUG ((LM_DEBUG, "disconnecting client %s\n", receiver_ref->_marker ())); + TRY { + receiver_ref->disconnect (reason, IT_X); + } + CATCHANY { + cerr << "Unexpected exception " << IT_X << endl; + } + ENDTRY; + delete me->int_id_; + delete me->ext_id_.rep (); + count++; + } + + this->map_.close (); + if (count == 1) + ACE_DEBUG ((LM_DEBUG, "there was 1 receiver\n")); + else + ACE_DEBUG ((LM_DEBUG, "there were %d receivers\n", count)); +} + +// Notify all receivers whose filtering criteria match the event. + +void +Notifier_i::send_notification (const Event_Comm::Notification ¬ification, + CORBA::Environment &IT_env) +{ + ACE_DEBUG ((LM_DEBUG, "in Notifier_i::send_notification = %s\n", + notification.tag_)); + MAP_ITERATOR mi (this->map_); + int count = 0; + + // Notify all the receivers. + // @@ Later on we need to consider the filtering_criteria! + + for (MAP_ENTRY *me = 0; mi.next (me) != 0; mi.advance ()) + { + Event_Comm::Notification_Receiver *receiver_ref = me->int_id_->receiver (); + ACE_ASSERT (receiver_ref->_marker () != 0); + const char *regexp = me->int_id_->regexp (); + const char *criteria = me->int_id_->criteria (); + ACE_ASSERT (regexp); + ACE_ASSERT (criteria); + + // Do a regular expression comparison to determine matching. + if (ACE_OS::strcmp ("", criteria) == 0 // Everything matches the wildcard. +// || ACE_OS::strcmp (notification.tag_, regexp) == 0) + || ACE_OS::step (notification.tag_, regexp) != 0) + { + ACE_DEBUG ((LM_DEBUG, "string %s matched regexp \"%s\" for client %s\n", + notification.tag_, me->int_id_->criteria (), + receiver_ref->_marker ())); + TRY { + receiver_ref->receive_notification (notification, IT_X); + } + CATCHANY { + cerr << "Unexpected exception " << IT_X << endl; + continue; + } + ENDTRY; + count++; + } + } + + if (count == 1) + ACE_DEBUG ((LM_DEBUG, "there was 1 receiver\n")); + else + ACE_DEBUG ((LM_DEBUG, "there were %d receivers\n", count)); +} + +#endif /* ACE_HAS_ORBIX */ diff --git a/apps/Orbix-Examples/Event_Comm/libsrc/Notifier_i.h b/apps/Orbix-Examples/Event_Comm/libsrc/Notifier_i.h new file mode 100644 index 00000000000..379f96b8097 --- /dev/null +++ b/apps/Orbix-Examples/Event_Comm/libsrc/Notifier_i.h @@ -0,0 +1,82 @@ +/* -*- C++ -*- */ +// @(#)Notifier_i.h 1.1 10/18/96 + + +// ============================================================================ +// +// = LIBRARY +// EventComm +// +// = FILENAME +// Notifier_i.h +// +// = DESCRIPTION +// Class interface for the implementation of the <Notifier> +// +// = AUTHOR +// Douglas C. Schmidt (schmidt@cs.wustl.edu) +// +// ============================================================================ + +#if !defined (_Notifier_i_H) +#define _Notifier_i_H + +#include "ace/Map_Manager.h" +#include "ace/Synch.h" +#include "ace/SString.h" +#include "Event_Comm.hh" + +#if defined (ACE_HAS_ORBIX) + +// Forward reference. +class Notification_Receiver_Entry; + +class Notifier_i + // = TITLE + // Defines the implementation class for event <Notifiers>. + // + // = DESCRIPTION +{ +public: + enum + { + DEFAULT_SIZE = 1024 // Default max number of Event_Comm::Notification_Receivers. + }; + + Notifier_i (size_t size_hint = Notifier_i::DEFAULT_SIZE); + // Initialize a Notifier_i object with the specified size hint. + + void send_disconnect (const char *reason, + CORBA::Environment &IT_env); + // Disconnect all the receivers, giving them the <reason>. + + void send_notification (const Event_Comm::Notification ¬ification, + CORBA::Environment &IT_env); + // Send the <Notification> to all the consumers who + // have subscribed and who match the filtering criteria. + + void subscribe (Event_Comm::Notification_Receiver *notification_receiver, + const char *filtering_criteria, + CORBA::Environment &IT_env); + // Subscribe the <Notification_Receiver> to receive events that + // match <filtering_criteria> applied by the <Notifier>. + + void unsubscribe (Event_Comm::Notification_Receiver *notification_receiver, + const char *filtering_criteria, + CORBA::Environment &IT_env); + // Unsubscribe the <Notification_Receiver>. + +private: + // The following implementation should be replaced + // by a standard container class from STL... + + typedef ACE_Map_Manager <ACE_SString, Notification_Receiver_Entry *, ACE_Null_Mutex> MAP_MANAGER; + typedef ACE_Map_Iterator <ACE_SString, Notification_Receiver_Entry *, ACE_Null_Mutex> MAP_ITERATOR; + typedef ACE_Map_Entry <ACE_SString, Notification_Receiver_Entry *> MAP_ENTRY; + + MAP_MANAGER map_; + // Table that maps a <Event_Comm::Notification_Receiver *> to a <Notification_Receiver_Entry *>. +}; + +#endif /* ACE_HAS_ORBIX */ +#endif /* _Notifier_i_H */ diff --git a/apps/Orbix-Examples/Logger/Logger.cpp b/apps/Orbix-Examples/Logger/Logger.cpp new file mode 100644 index 00000000000..de05360f606 --- /dev/null +++ b/apps/Orbix-Examples/Logger/Logger.cpp @@ -0,0 +1,131 @@ +#include <iostream.h> +// @(#)Logger.cpp 1.1 10/18/96 + +#include "Logger.h" + +Logger::~Logger (void) +{ + // Release and free up the object reference. + this->logref_->_release (); + + // Must use free since we used strdup(3C). + ACE_OS::free (ACE_MALLOC_T (this->server_)); +} + +// Constructor takes the name of the host where the server +// is located. If server == 0, then use the locator service. + +Logger::Logger (char *server, size_t max_message_size) + : server_ (server == 0 ? 0 : ACE_OS::strdup (server)), + ip_ (0), + pid_ (ACE_OS::getpid ()) +{ + struct utsname name; + +#if 0 + // Could also use sysinfo(2)... + + ACE_OS::sysinfo (SI_HOSTNAME, clienthost, MAXHOSTNAMELEN); +#endif + + ACE_OS::uname (&name); + hostent *hp = ACE_OS::gethostbyname (name.nodename); + + if (hp != 0) + memcpy ((void *) &this->ip_, (void *) hp->h_addr, hp->h_length); + + TRY { + // First bind to the logger object. + // argv[1] has the hostname (if any) of the target logger object; + // The default is the local host: + this->logref_ = profile_logger::_bind ("", this->server_, IT_X); + } CATCHANY { + // an error occurred while trying to bind to the logger object. + cerr << "Bind to object failed" << endl; + cerr << "Unexpected exception " << IT_X << endl; + } ENDTRY; + // Pre-assign certain values that don't change. + this->log_msg_.app_id = this->pid_; + this->log_msg_.host_addr = this->ip_; + this->log_msg_.msg_data._maximum = max_message_size; +} + +// Transmit the message to the logging server. + +int +Logger::log (logger::Log_Priority priority, char message[], int length) +{ + // The following values change with every logging operation. + this->log_msg_.type = priority; + this->log_msg_.time = ACE_OS::time (0); + this->log_msg_.msg_data._length = length; + this->log_msg_.msg_data._buffer = message; + + TRY { + // Try to log a message. + this->logref_->log (this->log_msg_, IT_X); + } CATCHANY { + // an error occurred while trying to read the height and width: + cerr << "call to log failed" << endl; + cerr << "Unexpected exception " << IT_X << endl; + return -1; + } ENDTRY; + // success. + return 0; +} + +// Get the value of verbose. + +int +Logger::verbose (void) +{ + int verbosity = 0; + + TRY { + verbosity = this->logref_->verbose (); + } CATCHANY { + return -1; + } ENDTRY; + return verbosity; +} + +// Set the value of verbose. + +int +Logger::verbose (int value) +{ + int verbosity = 0; + + TRY { + this->logref_->verbose (value); + } CATCHANY { + return -1; + } ENDTRY; + return 0; +} + +// Activate the timer. + +int +Logger::start_timer (void) +{ + TRY { + this->logref_->start_timer (); + } CATCHANY { + return -1; + } ENDTRY; + return 0; +} + +// Deactivate the timer and return the elapsed time. + +int +Logger::stop_timer (profile_logger::Elapsed_Time &et) +{ + TRY { + this->logref_->stop_timer (et); + } CATCHANY { + return -1; + } ENDTRY; + return 0; +} diff --git a/apps/Orbix-Examples/Logger/Logger.h b/apps/Orbix-Examples/Logger/Logger.h new file mode 100644 index 00000000000..fecae83cbf6 --- /dev/null +++ b/apps/Orbix-Examples/Logger/Logger.h @@ -0,0 +1,56 @@ +/* -*- C++ -*- */ +// @(#)Logger.h 1.1 10/18/96 + + +#if !defined (_LOGGER_H) +#define _LOGGER_H + +#include "ace/OS.h" +#include "logger.hh" + +class Logger + // = TITLE + // Wrapper class that uses CORBA object reference + // as the transport mechanism to simplify implementation. +{ +public: + Logger (char *server, size_t max_message_size); + // Constructor takes the name of the host where the server + // is located. If server == 0, then use the locator service. + + ~Logger (void); + // Destructor releases the object reference. + + int log (logger::Log_Priority prio, char msg[], int len); + // Log a <msg> of length <len> with priority <prio>. + + int verbose (void); + // Report current level of verbosity. + + int verbose (int verbosity); + // Set the level of verbosity (0 == no verbose, > 0 == verbose). + + int start_timer (void); + // Activate the timer. + + int stop_timer (profile_logger::Elapsed_Time &et); + // Deactivate the timer and return the elapsed time. + +private: + profile_logger *logref_; + // CORBA object reference proxy. + + int pid_; + // Process ID. + + u_long ip_; + // IP address of self. + + logger::Log_Record log_msg_; + // Cache certain non-changing values to avoid recomputing them. + + char *server_; + // Name of server that we are bound to. +}; + +#endif /* _LOGGER_H */ diff --git a/apps/Orbix-Examples/Logger/Makefile b/apps/Orbix-Examples/Logger/Makefile new file mode 100644 index 00000000000..7193cee9945 --- /dev/null +++ b/apps/Orbix-Examples/Logger/Makefile @@ -0,0 +1,63 @@ +#---------------------------------------------------------------------------- +# @(#)Makefile 1.1 10/18/96 +# +# Makefile for the Logger. +#---------------------------------------------------------------------------- + +#---------------------------------------------------------------------------- +# Local macros +#---------------------------------------------------------------------------- + +SVR_OBJS = loggerS.o logger_i.o server.o +CLT_OBJS = loggerC.o client.o Logger.o + +LDLIBS = + +VLDLIBS = $(LDLIBS:%=%$(VAR)) + +#---------------------------------------------------------------------------- +# Include macros and targets +#---------------------------------------------------------------------------- + +include $(WRAPPER_ROOT)/include/makeinclude/wrapper_macros.GNU +include $(WRAPPER_ROOT)/include/makeinclude/macros.GNU +include $(WRAPPER_ROOT)/include/makeinclude/rules.common.GNU +include $(WRAPPER_ROOT)/include/makeinclude/rules.nonested.GNU +include $(WRAPPER_ROOT)/include/makeinclude/rules.lib.GNU +include $(WRAPPER_ROOT)/include/makeinclude/rules.bin.GNU +include $(WRAPPER_ROOT)/include/makeinclude/rules.local.GNU + +#---------------------------------------------------------------------------- +# Orbix related macros and target settings. +#---------------------------------------------------------------------------- + +ORBIX_BINDIR = $(ORBIX_ROOT)/bin +ORBIX_LIBDIR = $(ORBIX_ROOT)/lib +ORBIX_INCDIR = $(ORBIX_ROOT)/include + +CPPFLAGS += -DEXCEPTIONS -I$(ORBIX_INCDIR) -DWANT_ORBIX_FDS +LDFLAGS += -L$(ORBIX_LIBDIR) -R $(ORBIX_LIBDIR) + +IDLFLAGS = -s S.cpp -c C.cpp -B + +#---------------------------------------------------------------------------- +# Local targets +#---------------------------------------------------------------------------- + +all: client server + +client: $(addprefix $(VDIR),$(CLT_OBJS)) + $(LINK.cc) -o client $(addprefix $(VDIR),$(CLT_OBJS)) $(LDFLAGS) -lITsrvmt $(VLDLIBS) + +server: $(addprefix $(VDIR),$(SVR_OBJS)) + $(LINK.cc) -o server $(addprefix $(VDIR),$(SVR_OBJS)) $(LDFLAGS) -lITsrvmt $(VLDLIBS) + +#---------------------------------------------------------------------------- +# Dependencies +#---------------------------------------------------------------------------- +# DO NOT DELETE THIS LINE -- g++dep uses it. +# DO NOT PUT ANYTHING AFTER THIS LINE, IT WILL GO AWAY. + + + +# IF YOU PUT ANYTHING HERE IT WILL GO AWAY diff --git a/apps/Orbix-Examples/Logger/Orbix.hostgroups b/apps/Orbix-Examples/Logger/Orbix.hostgroups new file mode 100644 index 00000000000..013636e79c4 --- /dev/null +++ b/apps/Orbix-Examples/Logger/Orbix.hostgroups @@ -0,0 +1 @@ +all:tango diff --git a/apps/Orbix-Examples/Logger/Orbix.hosts b/apps/Orbix-Examples/Logger/Orbix.hosts new file mode 100644 index 00000000000..2e11d889bed --- /dev/null +++ b/apps/Orbix-Examples/Logger/Orbix.hosts @@ -0,0 +1,3 @@ +profile_logger:tango: +logger:tango: +IT_daemon:tango: diff --git a/apps/Orbix-Examples/Logger/README b/apps/Orbix-Examples/Logger/README new file mode 100644 index 00000000000..19b1db681f2 --- /dev/null +++ b/apps/Orbix-Examples/Logger/README @@ -0,0 +1,35 @@ +The directory contains the source code that implements an Orbix +version of the distributed Logger. Other ACE versions of this code +appear in the ./apps/Logger directory. It is interesting to compare +and contrast the alternative implementations. + +RUNNING: + +The client is run as follows: + +client -h host -m max_message_size + +The -h host is optional if the locator service is properly configured. +The -m specifies the maximum number of kilobytes to be sent per log. This +is useful when redirecting messages to stdin. + +TIMING: + +I recommend timing the log's by specifying a max_message_size and +redirecting /usr/dict/words. This will give you several trials from +which to take an average. + +CLIENT: + +While using the client and typing in messages manually, capital Q and V +must be used to quit and toggle verbose respectively. This allows you +to redirect /usr/dict/words without quiting at the q's!! + +SERVER: + +To turn off message reporting on the server side, do a + +setenv NO_MESSAGES + +in the enviroment where the server will be run. If this is done, the server +will only report that a message was received, but not display the messages. diff --git a/apps/Orbix-Examples/Logger/a1.tex b/apps/Orbix-Examples/Logger/a1.tex new file mode 100644 index 00000000000..5d10042e26e --- /dev/null +++ b/apps/Orbix-Examples/Logger/a1.tex @@ -0,0 +1,232 @@ +\documentstyle[times,11pt,moretext] {article} +\input macros +\input widen +\input psfig + +\begin{document} +\centerline{\Large Washington University} +\centerline{\Large Department of Computer Science} +\bigskip +\centerline{\large CS523: Distributed Operating Systems} +%\smallskip +%\centerline{\large Spring 1995} +\bigskip +\centerline{\large Programming Project} +% \centerline{\large Due Tuesday, January $31^{st}$, 1995} + +\section{Overview} + +In this assignment, you will implement a distributed logging service +shown in Figure~\ref{logenv}. Applications use this service to log +information (such as error notifications, debugging traces, and status +updates) in a distributed environment. In this service, CORBA remote +operations are used to send logging records to a central logging +server. The logging server outputs the logging records to a console, +a printer, a file, or a network management database, etc. + +\section{Design and Implementation Issues} + +The distributed logging service will be designed as a client/server +pair, containing the objects shown in Figure~\ref{simplog}. + +\subsection{CORBA IDL Specification} +The following CORBA IDL specification defines the logging interface: + +{ +\small +\ls{0.9} +\begin{verbatim} +// IDL schema definition +interface Logger +{ + // Types of logging messages. + enum Log_Priority { + LM_DEBUG, // Debugging messages + LM_WARNING, // Warning messages + LM_ERROR, // Errors + LM_EMERG // A panic condition + }; + + // Format of the logging record. + struct Log_Record { + Log_Priority type; // Type of logging message. + long time; // Time stamp at sender. + long app_id; // Process ID of sender. + long host_addr; // IP address of the sender. + sequence<char> msg_data; // Sender-specific logging message. + }; + + // Transmit a Log_Record to the logging server. + oneway void log (in Log_Record log_rec); + + // Toggle verbose formatting + attribute char verbose; +}; +\end{verbatim}} + +\begin{figure} +\center{\ \psfig{figure=graphics/logsimp.eps,width=13cm}\ } +\vspace{-0.12in} +\caption{Distributed Logging Service} +\label{logenv} +\end{figure} + +You will use a CORBA IDL compiler to translate this specification into +client-side {\em stubs} and server-side {\em skeletons}. The client +application (which you must write) will use the stubs as a {\em proxy} +to access the logging services provided by the server. You must also +write the implementation of the server, which provides the logging +service. + +\subsection{Client and Server Functionality} +For the purposes of the assignment, you can make the client driver +program very simple. The client can read a line from its standard +input and send it to the logging server. The server can then format +and print the line on its standard output. For example, if you type +this line to the client: + +\begin{verbatim} +To boldly go where no one has gone before +\end{verbatim} + +\noindent Then the server should output something like this: + +\begin{verbatim} +Jan 24 14:50:28 1995@tango.cs.wustl.edu@18352@LM_DEBUG +::To boldly go where no one has gone before +\end{verbatim} + +\noindent Note that the server has printed out the logging message +timestamp, sender's hostname and process id, and the message priority, +followed by the logging message data. + +\begin{figure} +\center{\ \psfig{figure=graphics/simplog.eps,width=13cm}\ } +\vspace{-0.12in} +\caption{CORBA-based Logger Design} +\label{simplog} +\end{figure} + +Note that in order to pass the client's IP address (which is +represented as a 4-byte {\tt long}) in the logging message, you'll +need to learn about several other UNIX routines. On the client-side +you'll need to use {\tt uname(2)} and {\tt gethostbyname(2)} to +determine the IP address of the client host. On the server-side, +you'll need to use the {\tt gethostbyaddr(2)} function to convert the +4-byte IP host address into an ASCII version of the host name. I +recommend that you check the manual pages and read Richard Steven's +book ``UNIX Network Programming'' for more details on using these +functions. + +\subsection{Invoking the Client and Server} +Once the client and server components are written, compiled, and +linked together you will use the {\tt putit} command to register the +server with the Orbix daemon. You'll then need to start up a copy of +{\tt orbixd} (if there isn't already one running). {\tt orbixd} +serves as the Object Request Broker for the local endpoint. + +A client will bind to the {\tt Logger} interface via the generated +{\tt Logger::\_bind} method. There are two general ways to use this +method. The first is to explicitly pass in the name of the server +where {\tt orbixd} is running (your client should accept a +command-line argument that is the name of the server, {\em e.g.,} +``tango.cs.wustl.edu''). + +The second method is to use the CORBA locator service to get an object +reference for the logging service. You'll need to read the Orbix +documentation to learn how to set up a location file. This file will +enable you to omit the name of the server in the call to {\tt +Logger::\_bind}. By using the locator server, your clients can bind +to object's implicitly. Make sure that your solution will work for +either implicit or explicit service location. + +Once the client application has bound (either explicitly or +implicitly) to an object reference for the {\tt Logger}, it can log +messages by calling the {\tt log} method via the object reference +proxy. + +\subsection{Performance Measurement} + +An important part of developing distributed systems is understanding +the performance implications of different design approaches. In order +to measure the performance overhead of using CORBA to build the +Logger, you will write a simple extension to the original {\tt Logger} +interface, as follows: + +{ +\small +\ls{0.9} +\begin{verbatim} +// IDL schema definition +interface Profile_Logger + : Logger // Profile_Logger IS-A Logger +{ + // Stores the amount of time that has elapsed. + struct Elapsed_Time + { + double real_time; + double user_time; + double system_time; + }; + + // Activate the timer. + void start_timer (void); + + // Deactivate the timer and return the elapsed time. + void stop_timer (out Elapsed_Time et); +}; +\end{verbatim}} + +\noindent You will need to modify your client program so that it can +time a series of {\tt Logger::log} operations for various sizes of +logging messages. This will help us understand the performance +overhead of CORBA. + +The main benchmarking should take place within a loop in your client +program. Basically, your client call {\tt +Profile\_Logger::start\_timer} just before sending the first of the +logging messages. After a suitable number of iterations (defined on +the command-line), you client will call {\tt +Profile\_Logger::stop\_timer} to determine and report the elapsed time +to the user. You should print out the ``real'' time, as well as the +``system $+$ user'' times. Make sure that you print out the +throughput in terms of megabits/sec (rather than bytes/sec or +kbytes/sec). Be sure to include the fixed-sized {\tt Log\_Record} +object, as well as the variable-sized {\tt msg\_data} portion in your +computations. + +The number of iterations and the size of the messages sent by the +client should be parameterizable on the command-line. Make sure that +your timing tests are run between processes on two different machines +(rather than processes on the same machine). If possible, try to run +the client and server processes on two machines on the same subnet. + +When you are finished with your timing test, you should explain the +timing results and indicate trends that you observed. + +\section{Learning and Using CORBA} + +To help you learn how CORBA works, I will be making copies of the +Orbix programmer's manual available for a small reproduction fee. +This manual explains how to program in CORBA. I will announce in +class where this will be available. + +We will be using IONA's Orbix CORBA Object Request Broker (ORB) +implementation. The libraries, executables, CORBA IDL compiler, and +example demo applications are located in {\tt +/project/adaptive/Orbix}. Please note that this is an automounted +directory, so you will need to {\tt cd} directly to it in order to see +the contents. To configure Orbix for your environment, copy the {\tt +/project/adaptive/Orbix/Orbix.cfg} file to your account. You'll need +to set the environment variable {\tt IT\_CONFIG\_PATH} to the complete +path where this file is located. + +\section{Concluding Remarks} +In office hours and in class, we will discuss how to use C++ and CORBA +in order to develop your solutions. Note that this assignment will +teach you many skills required to become adept at network programming. +However, it also will require a great deal of thought and planning. +Please make sure you start early, come to office hours, and ask lots +of questions. + +\end{document} diff --git a/apps/Orbix-Examples/Logger/client.cpp b/apps/Orbix-Examples/Logger/client.cpp new file mode 100644 index 00000000000..aea23379488 --- /dev/null +++ b/apps/Orbix-Examples/Logger/client.cpp @@ -0,0 +1,142 @@ +// A client for the distributed logger example. This program reads +// @(#)client.cpp 1.1 10/18/96 + +// from either stdin or from a redirected file and sends all the +// contents to the logging server. It also computes how long it takes +// to send this stuff. + +#include "ace/Log_Msg.h" +#include "Logger.h" + +// maximum message size +static size_t max_message_size = BUFSIZ; + +// Default behavior is to use the locator service. +static char *hostname = 0; + +// Should we prompt the user? +static int user_prompt; + +static void +parse_args (int argc, char *argv[]) +{ + extern char *optarg; + extern int optind; + int c; + + ACE_LOG_MSG->open (argv[0]); + + // If a file has been redirected, don't activate user prompts + if (ACE_OS::isatty (0)) + user_prompt = 1; + else + user_prompt = 0; + + while ((c = ACE_OS::getopt (argc, argv, "m:h:")) != -1) + switch (c) + { + case 'm': + max_message_size = ACE_OS::atoi (optarg) * BUFSIZ; + break; + case 'h': + hostname = optarg; + break; + default: + ACE_ERROR ((LM_ERROR, "%n: -h host -m max_message_size (in kbytes)\n%a", 1)); + /* NOTREACHED */ + } +} + +// Enable/disable verbose logging. + +static int +toggle_verbose (Logger &logger) +{ + int verbose_value; + + verbose_value = logger.verbose (); + logger.verbose (!verbose_value); + return 0; +} + +// Transmit messages to the server. + +int +transmit (Logger &logger, char buf[], ACE_HANDLE handle = 0) +{ + if (user_prompt) + cout << "\nEnter message ('Q':quit,'V':toggle verbose):\n" << flush; + + ssize_t nbytes = ACE_OS::read (handle, buf, max_message_size); + + if (nbytes <= 0) + return nbytes; // End of file or error. + buf[nbytes] = '\0'; + + if (user_prompt) + { + if (buf[0] == 'Q' || buf[0] == 'q') + return 0; + // toggle verbose? + else if (buf[0] == 'V' || buf[0] == 'v') + toggle_verbose (logger); + } + + // send the message to the logger + if (logger.log (logger::LM_DEBUG, buf, nbytes) == -1) + return -1; + else + return nbytes; +} + +// Print the results of the tests. + +void +report_results (profile_logger::Elapsed_Time &et, size_t total_bytes) +{ + ACE_DEBUG ((LM_DEBUG, + "real time = %8.2f\n" + "user time = %8.2f\n" + "sys time = %8.2f\n" + "mbits sec = %8.2f\n", + et.real_time, et.user_time, et.system_time, + (total_bytes / et.real_time) * 8.0 / 1024.0 / 1024.0)); +} + +int +main (int argc, char **argv) +{ + parse_args (argc,argv); + + // Pointer to the logger object that will be used. + Logger logger (hostname, max_message_size); + char *buf = new char [max_message_size]; + size_t total_bytes = 0; + size_t nbytes = 0; + + logger.start_timer (); + + // Transmit logging records until user quits. + + for (int done = 0; done == 0;) + switch (nbytes = transmit (logger, buf)) + { + case -1: + ACE_ERROR_RETURN ((LM_ERROR, "%p\n", "transmit"), -1); + /* NOTREACHED */ + case 0: + done = 1; + break; + default: + total_bytes += nbytes; + break; + } + + profile_logger::Elapsed_Time et; + + if (logger.stop_timer (et) == -1) + ACE_ERROR_RETURN ((LM_ERROR, "%p\n", "stop timer"), -1); + + report_results (et, total_bytes); + return 0; +} diff --git a/apps/Orbix-Examples/Logger/logger.hh b/apps/Orbix-Examples/Logger/logger.hh new file mode 100644 index 00000000000..0d0eeeca2f0 --- /dev/null +++ b/apps/Orbix-Examples/Logger/logger.hh @@ -0,0 +1,434 @@ + +#ifndef logger_hh +#define logger_hh + +#include <CORBA.h> + +#include <string.h> + + +#ifndef _IDL_SEQUENCE_char_defined +#define _IDL_SEQUENCE_char_defined + +struct IONANC__IDL_SEQUENCE_char; +struct _IDL_SEQUENCE_char { + unsigned long _maximum; + unsigned long _length; + char *_buffer; + + operator IONANC__IDL_SEQUENCE_char(); + operator const IONANC__IDL_SEQUENCE_char() const; + _IDL_SEQUENCE_char& operator= (const IONANC__IDL_SEQUENCE_char&); + + _IDL_SEQUENCE_char& operator= (const _IDL_SEQUENCE_char&); + _IDL_SEQUENCE_char (const _IDL_SEQUENCE_char&); + + _IDL_SEQUENCE_char (unsigned long IT_size = 0); + + ~_IDL_SEQUENCE_char () { if (_buffer) delete [] _buffer; } + + char& operator [] (unsigned long IT_i) const {return _buffer[IT_i]; } + + void encodeOp (CORBA::Request &IT_r) const; + void decodeOp (CORBA::Request &IT_r); + void decodeInOutOp (CORBA::Request &IT_r); +}; + +struct IONANC__IDL_SEQUENCE_char { + unsigned long _maximum; + unsigned long _length; + char *_buffer; + + char& operator [] (unsigned long IT_i) const; + + operator _IDL_SEQUENCE_char (); + + operator const _IDL_SEQUENCE_char () const; + +}; + + + +#endif + + +#ifndef _logger_defined +#define _logger_defined +class logger_dispatch : public virtual CORBA::PPTR { +public: + + logger_dispatch (void *IT_p, CORBA::Object* IT_o, const char *IT_m, + CORBA::LoaderClass *IT_l, char *IT_i, void* IT_im) + : CORBA::PPTR (IT_p,IT_o,IT_m,IT_l,IT_i,IT_im) {} + + + logger_dispatch (char *IT_OR, void *IT_p, CORBA::Object *IT_o) + : CORBA::PPTR (IT_OR,IT_p,IT_o) {} + + + logger_dispatch () {} + + logger_dispatch (void *IT_p, CORBA::Object *IT_o, const char *IT_m, + char *IT_i, CORBA::Object* IT_ob, void* IT_im) + : CORBA::PPTR (IT_p,IT_o,IT_m,IT_i,IT_ob,IT_im) {} + + + virtual unsigned char dispatch (CORBA::Request &IT_r, + unsigned char IT_isTarget, void* IT_pp=NULL); + + +}; + +class logger; + + +#ifndef loggerForwH +#define loggerForwH +CORBA::ObjectRef logger_getBase (void *); +void logger_release (void *, CORBA::Environment &IT_env=CORBA::default_environment); +logger* logger_duplicate (void *, CORBA::Environment &IT_env=CORBA::default_environment); +#endif +#define logger_IMPL "logger" + + +class logger; +#define logger_IR "logger" +#define logger_IMPL "logger" + +typedef logger* loggerRef; +typedef logger* logger_ptr; +class logger: public virtual CORBA::Object { +public: + logger (char *IT_OR); + logger () : CORBA::Object (1) {} + logger* _duplicate( + CORBA::Environment &IT_env=CORBA::default_environment) { + CORBA::Object::_duplicate (IT_env); return this; } + static logger* _bind (const char* IT_markerServer, const char* host, + const CORBA::Context &IT_c, + CORBA::Environment &IT_env=CORBA::default_environment); + static logger* _bind (CORBA::Environment &IT_env); + static logger* _bind (const char* IT_markerServer=NULL, const char* host=NULL, + CORBA::Environment &IT_env=CORBA::default_environment); + static logger* _narrow (CORBA::Object* , CORBA::Environment &IT_env=CORBA::default_environment); +enum Log_Priority {LM_MESSAGE,LM_DEBUG,LM_WARNING,LM_ERROR,LM_EMERG}; + +#ifndef logger_Log_Record_defined +#define logger_Log_Record_defined + +struct IONANC_Log_Record; +struct Log_Record { + logger::Log_Priority type; + long time; + long app_id; + long host_addr; + _IDL_SEQUENCE_char msg_data; + + void encodeOp (CORBA::Request &IT_r) const; + void decodeOp (CORBA::Request &IT_r); + void decodeInOutOp (CORBA::Request &IT_r); + Log_Record(const Log_Record &); + Log_Record(); + operator logger::IONANC_Log_Record(); + operator const logger::IONANC_Log_Record() const; + Log_Record& operator= (const IONANC_Log_Record&); + ~Log_Record(); + Log_Record& operator= (const Log_Record&); +}; + +struct IONANC_Log_Record { + logger::Log_Priority type; + long time; + long app_id; + long host_addr; + IONANC__IDL_SEQUENCE_char msg_data; + operator logger::Log_Record (); + operator const logger::Log_Record () const; + }; + + +#endif + + virtual void log (const logger::Log_Record& log_rec, CORBA::Environment &IT_env=CORBA::default_environment); + virtual void verbose (char verbose, CORBA::Environment &IT_env=CORBA::default_environment); + virtual char verbose (CORBA::Environment &IT_env=CORBA::default_environment); +}; + + +#define TIE_logger(X) logger##X + +#define DEF_TIE_logger(X) \ + class logger##X : public virtual logger { \ + X* m_obj; \ + public: \ + \ + logger##X (X *objp, const char* m="", CORBA::LoaderClass *l=nil)\ + : logger(), CORBA::Object (), m_obj(objp) { \ + m_pptr = new logger_dispatch \ + (( logger*)this,(CORBA::Object*)this,m,l,logger_IR,m_obj); \ + } \ + logger##X (CORBA::Object *IT_p, const char* IT_m="", void *IT_q=nil)\ + : logger(), CORBA::Object () { \ + m_pptr = new logger_dispatch \ + (( logger*)this,(CORBA::Object*)this,IT_m,logger_IR,IT_p,IT_q); \ + m_obj = (X*)(m_pptr->getImplObj ()); \ + } \ + \ + virtual ~logger##X () { \ + if (_okToDeleteImpl ()) delete m_obj; } \ + \ + virtual void* _deref () { \ + return m_obj; } \ + \ + virtual void log (const logger::Log_Record& log_rec, CORBA::Environment &IT_env) {\ +m_obj->log ( log_rec,IT_env);\ +}\ + \ +virtual void verbose (char verbose, CORBA::Environment &IT_env) {\ + m_obj->verbose(verbose,IT_env); }\ + \ +virtual char verbose (CORBA::Environment &IT_env) {\ +return m_obj->verbose(IT_env); }\ + \ + }; + + +#define QUALS_logger \ + virtual void log (const logger::Log_Record& log_rec, CORBA::Environment &IT_env) {\ +m_obj->log ( log_rec,IT_env);\ +}\ + \ +virtual void verbose (char verbose, CORBA::Environment &IT_env) {\ + m_obj->verbose(verbose,IT_env); }\ + \ +virtual char verbose (CORBA::Environment &IT_env) {\ +return m_obj->verbose(IT_env); }\ + + + + +class loggerProxyFactoryClass : public virtual CORBA::ObjectFactoryClass { +public: + loggerProxyFactoryClass (unsigned char IT_p=0) + : CORBA::ProxyFactory (logger_IR, IT_p) {} + + virtual void* New (char *IT_OR, CORBA::Environment&); + + virtual void* New2 (); + + virtual void* IT_castUp (void *IT_p, char* IT_s); + + virtual CORBA::PPTR* pptr (void *IT_p); + + virtual void baseInterfaces (_IDL_SEQUENCE_string&); + + +}; + +extern loggerProxyFactoryClass loggerProxyFactory; + + + +class loggerBOAImpl : public virtual logger { +public: + loggerBOAImpl (const char *m="", CORBA::LoaderClass *l=NULL) { + if (CORBA::PPTR::isOK (m_pptr, logger_IR)) + m_pptr = new logger_dispatch ( (logger*)this, + (CORBA::Object*)this, m, l, logger_IR, this); +} + + virtual void log (const logger::Log_Record& log_rec, CORBA::Environment &IT_env=CORBA::default_environment) =0; + virtual void verbose (char verbose, CORBA::Environment &IT_env=CORBA::default_environment)=0; + virtual char verbose (CORBA::Environment &IT_env=CORBA::default_environment)=0; +}; + + +#endif + + +#ifndef _profile_logger_defined +#define _profile_logger_defined +class profile_logger_dispatch : public virtual logger_dispatch { +public: + + profile_logger_dispatch (void *IT_p, CORBA::Object* IT_o, const char *IT_m, + CORBA::LoaderClass *IT_l, char *IT_i, void* IT_im) + : CORBA::PPTR (IT_p,IT_o,IT_m,IT_l,IT_i,IT_im) {} + + + profile_logger_dispatch (char *IT_OR, void *IT_p, CORBA::Object *IT_o) + : CORBA::PPTR (IT_OR,IT_p,IT_o) {} + + + profile_logger_dispatch () {} + + profile_logger_dispatch (void *IT_p, CORBA::Object *IT_o, const char *IT_m, + char *IT_i, CORBA::Object* IT_ob, void* IT_im) + : CORBA::PPTR (IT_p,IT_o,IT_m,IT_i,IT_ob,IT_im) {} + + + virtual unsigned char dispatch (CORBA::Request &IT_r, + unsigned char IT_isTarget, void* IT_pp=NULL); + + +}; + +class profile_logger; + + +#ifndef profile_loggerForwH +#define profile_loggerForwH +CORBA::ObjectRef profile_logger_getBase (void *); +void profile_logger_release (void *, CORBA::Environment &IT_env=CORBA::default_environment); +profile_logger* profile_logger_duplicate (void *, CORBA::Environment &IT_env=CORBA::default_environment); +#endif +#define profile_logger_IMPL "profile_logger" + + +class profile_logger; +#define profile_logger_IR "profile_logger" +#define profile_logger_IMPL "profile_logger" + +typedef profile_logger* profile_loggerRef; +typedef profile_logger* profile_logger_ptr; +class profile_logger: public virtual logger { +public: + profile_logger (char *IT_OR); + profile_logger () : CORBA::Object (1) {} + profile_logger* _duplicate( + CORBA::Environment &IT_env=CORBA::default_environment) { + CORBA::Object::_duplicate (IT_env); return this; } + static profile_logger* _bind (const char* IT_markerServer, const char* host, + const CORBA::Context &IT_c, + CORBA::Environment &IT_env=CORBA::default_environment); + static profile_logger* _bind (CORBA::Environment &IT_env); + static profile_logger* _bind (const char* IT_markerServer=NULL, const char* host=NULL, + CORBA::Environment &IT_env=CORBA::default_environment); + static profile_logger* _narrow (CORBA::Object* , CORBA::Environment &IT_env=CORBA::default_environment); + +#ifndef profile_logger_Elapsed_Time_defined +#define profile_logger_Elapsed_Time_defined + +struct Elapsed_Time { + double real_time; + double user_time; + double system_time; + + void encodeOp (CORBA::Request &IT_r) const; + void decodeOp (CORBA::Request &IT_r); + void decodeInOutOp (CORBA::Request &IT_r); +}; + + +#endif + + virtual void start_timer (CORBA::Environment &IT_env=CORBA::default_environment); + virtual void stop_timer (profile_logger::Elapsed_Time& et, CORBA::Environment &IT_env=CORBA::default_environment); +}; + + +#define TIE_profile_logger(X) profile_logger##X + +#define DEF_TIE_profile_logger(X) \ + class profile_logger##X : public virtual profile_logger { \ + X* m_obj; \ + public: \ + \ + profile_logger##X (X *objp, const char* m="", CORBA::LoaderClass *l=nil)\ + : profile_logger(), CORBA::Object (), m_obj(objp) { \ + m_pptr = new profile_logger_dispatch \ + (( profile_logger*)this,(CORBA::Object*)this,m,l,profile_logger_IR,m_obj); \ + } \ + profile_logger##X (CORBA::Object *IT_p, const char* IT_m="", void *IT_q=nil)\ + : profile_logger(), CORBA::Object () { \ + m_pptr = new profile_logger_dispatch \ + (( profile_logger*)this,(CORBA::Object*)this,IT_m,profile_logger_IR,IT_p,IT_q); \ + m_obj = (X*)(m_pptr->getImplObj ()); \ + } \ + \ + virtual ~profile_logger##X () { \ + if (_okToDeleteImpl ()) delete m_obj; } \ + \ + virtual void* _deref () { \ + return m_obj; } \ + \ + virtual void log (const logger::Log_Record& log_rec, CORBA::Environment &IT_env) {\ +m_obj->log ( log_rec,IT_env);\ +}\ + \ +virtual void verbose (char verbose, CORBA::Environment &IT_env) {\ + m_obj->verbose(verbose,IT_env); }\ + \ +virtual char verbose (CORBA::Environment &IT_env) {\ +return m_obj->verbose(IT_env); }\ + virtual void start_timer (CORBA::Environment &IT_env) {\ +m_obj->start_timer (IT_env);\ +}\ + \ + virtual void stop_timer (profile_logger::Elapsed_Time& et, CORBA::Environment &IT_env) {\ +m_obj->stop_timer ( et,IT_env);\ +}\ + \ + }; + + +#define QUALS_profile_logger \ + virtual void log (const logger::Log_Record& log_rec, CORBA::Environment &IT_env) {\ +m_obj->log ( log_rec,IT_env);\ +}\ + \ +virtual void verbose (char verbose, CORBA::Environment &IT_env) {\ + m_obj->verbose(verbose,IT_env); }\ + \ +virtual char verbose (CORBA::Environment &IT_env) {\ +return m_obj->verbose(IT_env); }\ + virtual void start_timer (CORBA::Environment &IT_env) {\ +m_obj->start_timer (IT_env);\ +}\ + \ + virtual void stop_timer (profile_logger::Elapsed_Time& et, CORBA::Environment &IT_env) {\ +m_obj->stop_timer ( et,IT_env);\ +}\ + + + + +class profile_loggerProxyFactoryClass : public virtual loggerProxyFactoryClass { +public: + profile_loggerProxyFactoryClass (unsigned char IT_p=0) + : CORBA::ProxyFactory (profile_logger_IR, IT_p) {} + + virtual void* New (char *IT_OR, CORBA::Environment&); + + virtual void* New2 (); + + virtual void* IT_castUp (void *IT_p, char* IT_s); + + virtual CORBA::PPTR* pptr (void *IT_p); + + virtual void baseInterfaces (_IDL_SEQUENCE_string&); + + +}; + +extern profile_loggerProxyFactoryClass profile_loggerProxyFactory; + + + +class profile_loggerBOAImpl : public virtual profile_logger { +public: + profile_loggerBOAImpl (const char *m="", CORBA::LoaderClass *l=NULL) { + if (CORBA::PPTR::isOK (m_pptr, profile_logger_IR)) + m_pptr = new profile_logger_dispatch ( (profile_logger*)this, + (CORBA::Object*)this, m, l, profile_logger_IR, this); +} + + virtual void start_timer (CORBA::Environment &IT_env=CORBA::default_environment) =0; + virtual void stop_timer (profile_logger::Elapsed_Time& et, CORBA::Environment &IT_env=CORBA::default_environment) =0; +}; + + +#endif + + +#endif diff --git a/apps/Orbix-Examples/Logger/logger.idl b/apps/Orbix-Examples/Logger/logger.idl new file mode 100644 index 00000000000..0fe673a84b9 --- /dev/null +++ b/apps/Orbix-Examples/Logger/logger.idl @@ -0,0 +1,56 @@ +/* -*- C++ -*- */ +// @(#)logger.idl 1.1 10/18/96 + +// logger.idl + +interface logger +// = TITLE +// This is the CORBA interface for the logger class. +{ + // = Types of logging messages. + enum Log_Priority + { + LM_MESSAGE, + LM_DEBUG, + LM_WARNING, + LM_ERROR, + LM_EMERG + }; + + // = Format of the logging record. + struct Log_Record + { + Log_Priority type; // Type of logging message. + long time; // Time stamp at sender. + long app_id; // Process ID of sender. + long host_addr; // IP address of the sender. + sequence<char> msg_data; // Sender-specific logging message. + }; + + oneway void log (in Log_Record log_rec); + // Transmit a Log_Record to the logging server. + + attribute char verbose; + // Toggle verbose formatting +}; + +interface profile_logger + : logger // Profile_Logger IS-A Logger +// = TITLE +// IDL Profile Logger definition that is used +// to compute statistics about the logging. +{ + // = Stores the amount of time that has elapsed. + struct Elapsed_Time + { + double real_time; + double user_time; + double system_time; + }; + + void start_timer (); + // Activate the timer. + + void stop_timer (out Elapsed_Time et); + // Deactivate the timer and return the elapsed time. +}; diff --git a/apps/Orbix-Examples/Logger/loggerS.cpp b/apps/Orbix-Examples/Logger/loggerS.cpp new file mode 100644 index 00000000000..b1210683886 --- /dev/null +++ b/apps/Orbix-Examples/Logger/loggerS.cpp @@ -0,0 +1,141 @@ + +// @(#)loggerS.cpp 1.1 10/18/96 + +#include "logger.hh" + + +#define logger_dispatch_impl + +unsigned char logger_dispatch::dispatch (CORBA::Request &IT_r, + unsigned char IT_isTarget, void *IT_pp) { + if (!IT_pp) + IT_pp = m_obj; + const char *IT_s = IT_r.getOperation (); + if (!strcmp(IT_s,"log")) { + CORBA::Environment IT_env (IT_r); + CORBA::Filter* IT_f = CORBA::Orbix.getFilter (); + if (!IT_r.tcAssert ("\ +Ro~log~+log_rec{R~logger::Log_Record~type{E~logger::Log_Priority~LM_MESSAGE,LM_DEBUG,LM_WARNING,LM_ERROR,LM_EMERG},time{l},app_id{l},host_addr{l},msg_data{S{c},0}},>{v},O{}\ +")) + return 1; + logger::Log_Record log_rec; + log_rec.decodeOp (IT_r); + + if (IT_f && !IT_r.isException (IT_env)) + IT_f->inRequestPostM (IT_r, IT_env); + if (!IT_r.isException (IT_env)) + ((logger*)IT_pp)->log ( log_rec, IT_env); + + IT_r.replyNoResults (CORBA::Flags(CORBA::INV_NO_RESPONSE),IT_env); + return 1; + } + + else if (!strcmp (IT_s,"_get_verbose")) { + char verbose; + CORBA::Environment IT_env (IT_r); + CORBA::Filter* IT_f = CORBA::Orbix.getFilter (); + if (!IT_r.tcAssert ("\ +Ro~_get_verbose~>{c},N{}\ +")) + return 1; + if (IT_f) + IT_f->inRequestPostM (IT_r, IT_env); + if (!IT_r.isException (IT_env)) + verbose = ((logger*)IT_pp)->verbose(IT_env); + + if (!IT_r.isException (IT_env)) { + if (!IT_r.convertToReply ("\ +c\ +", IT_env)) return 1; + IT_r << verbose; + } + else IT_r.makeSystemException (IT_env); + + return 1; + } + else if (!strcmp (IT_s,"_set_verbose")) { + CORBA::Environment IT_env (IT_r); + CORBA::Filter* IT_f = CORBA::Orbix.getFilter (); + if (IT_r.tcAssert ("\ +Ro~_set_verbose~+{c},>{v},N{}\ +")) { + char verbose; + IT_r >> verbose; + if (IT_f && !IT_r.isException (IT_env)) + IT_f->inRequestPostM (IT_r, IT_env); + if (!IT_r.isException (IT_env)) + ((logger*)IT_pp)->verbose(verbose, IT_env); + } + IT_r.replyNoResults (IT_env); + return 1; + } + + else if (IT_isTarget) + IT_r.makeRuntimeException2 (); + + return 0; +} + +#define profile_logger_dispatch_impl + +unsigned char profile_logger_dispatch::dispatch (CORBA::Request &IT_r, + unsigned char IT_isTarget, void *IT_pp) { + if (!IT_pp) + IT_pp = m_obj; + const char *IT_s = IT_r.getOperation (); + if (!strcmp(IT_s,"start_timer")) { + CORBA::Environment IT_env (IT_r); + CORBA::Filter* IT_f = CORBA::Orbix.getFilter (); + if (!IT_r.tcAssert ("\ +Ro~start_timer~>{v},N{}\ +")) + return 1; + + if (IT_f && !IT_r.isException (IT_env)) + IT_f->inRequestPostM (IT_r, IT_env); + if (!IT_r.isException (IT_env)) + ((profile_logger*)IT_pp)->start_timer (IT_env); + + IT_r.replyNoResults (IT_env); + return 1; + } + + else if (!strcmp(IT_s,"stop_timer")) { + CORBA::Environment IT_env (IT_r); + CORBA::Filter* IT_f = CORBA::Orbix.getFilter (); + if (!IT_r.tcAssert ("\ +Ro~stop_timer~-et{R~profile_logger::Elapsed_Time~real_time{d},user_time{d},system_time{d}},>{v},N{}\ +")) + return 1; + profile_logger::Elapsed_Time et; + + if (IT_f && !IT_r.isException (IT_env)) + IT_f->inRequestPostM (IT_r, IT_env); + if (!IT_r.isException (IT_env)) + ((profile_logger*)IT_pp)->stop_timer ( et, IT_env); + + + if (!IT_r.isException (IT_env)) { + if (!IT_r.convertToReply ("\ +v\ +", IT_env)) return 1; + et.encodeOp (IT_r); + } + + else IT_r.makeSystemException (IT_env); + return 1; + } + + else if (logger_dispatch::dispatch (IT_r, 0, + (logger*)((profile_logger*)IT_pp))) { + return 1; + } + + else if (IT_isTarget) + IT_r.makeRuntimeException2 (); + + return 0; +} + +#include "loggerC.cpp" + diff --git a/apps/Orbix-Examples/Logger/logger_i.cpp b/apps/Orbix-Examples/Logger/logger_i.cpp new file mode 100644 index 00000000000..cfd5a5b0d8b --- /dev/null +++ b/apps/Orbix-Examples/Logger/logger_i.cpp @@ -0,0 +1,120 @@ +// Implementation of the logger object. +// @(#)logger_i.cpp 1.1 10/18/96 + + +#include "ace/OS.h" +#include <iostream.h> +#include "logger_i.h" + +// Select non-verbose logging by default. + +logger_i::logger_i (int verbose) + : verbose_value_ (verbose) +{ + if (ACE_OS::getenv ("NO_MESSAGES") == 0) + this->verbose_message_ = 1; + else + this->verbose_message_ = 0; +} + +// Implement the log method. + +void +logger_i::log (const logger::Log_Record &log_rec, CORBA::Environment &IT_env) +{ + if (this->verbose_value_) // If verbose mode is on + { + char *tm; + + // Convert time + if ((tm = ACE_OS::ctime (&log_rec.time)) == 0) + cerr << "ctime failed" << endl; + else + { + hostent *hp; + + /* 01234567890123456789012345 */ + /* Wed Oct 18 14:25:36 1989n0 */ + tm[24] = '@'; + cout << tm; + + // Get host name of client + + if ((hp = gethostbyaddr((char *) &log_rec.host_addr, + sizeof log_rec.host_addr, AF_INET)) == NULL) + { + cerr << "server: error in calling gethostbyaddr" << endl; + cerr << "h_errno = " << h_errno << endl; + return; + } + else // Output client hostname. + cout << hp->h_name << "@"; + + // Output PID of client + cout << log_rec.app_id << "@"; + + // Output priority + + switch (log_rec.type) + { + case logger::LM_DEBUG: + cout << "LM_DEBUG"; + break; + case logger::LM_WARNING: + cout << "LM_WARNING"; + break; + case logger::LM_ERROR: + cout << "LM_ERROR"; + break; + case logger::LM_EMERG: + cout << "LM_EMERG"; + break; + } + } + } + if (this->verbose_message_) + { + cout << "::"; + // Output message + cout.write (log_rec.msg_data._buffer, log_rec.msg_data._length) << flush; + } +} + +// Enable/disable verbosity. + +void +logger_i::verbose (char verbose, CORBA::Environment &IT_env) +{ + this->verbose_value_ = verbose; +} + +// Report current verbosity level. + +char +logger_i::verbose (CORBA::Environment &IT_env) +{ + return this->verbose_value_; +} + +// Profile_Logger_i + +void +profile_logger_i::start_timer (CORBA::Environment &IT_env) +{ + this->pt_.start (); +} + +void +profile_logger_i::stop_timer (profile_logger::Elapsed_Time& et, + CORBA::Environment &IT_env) +{ + this->pt_.stop (); + + ACE_Profile_Timer::ACE_Elapsed_Time e; + + this->pt_.elapsed_time (e); + + et.real_time = e.real_time; + et.user_time = e.user_time; + et.system_time = e.system_time; +} diff --git a/apps/Orbix-Examples/Logger/logger_i.h b/apps/Orbix-Examples/Logger/logger_i.h new file mode 100644 index 00000000000..65253527370 --- /dev/null +++ b/apps/Orbix-Examples/Logger/logger_i.h @@ -0,0 +1,75 @@ +/* -*- C++ -*- */ +// @(#)logger_i.h 1.1 10/18/96 + + +#include "ace/Profile_Timer.h" +#define EXCEPTIONS +#include "logger.hh" + +class logger_i +#if defined (USE_BOA_IMPL) + : virtual public loggerBOAImpl +#endif /* USE_BOA_IMPL */ + // = TITLE + // Implementation of the logger interface. + // + // = DESCRIPTION + // Uses either the BOAImpl or the DEF_TIE approach, + // depending on the #ifdef +{ +public: + logger_i (int verbose = 0); + // Select non-verbose logging by default. + + virtual void log (const logger::Log_Record &log_rec, CORBA::Environment &IT_env); + // Implement the log method. + + virtual void verbose (char verbose, CORBA::Environment &IT_env); + // Enable/disable verbosity. + + virtual char verbose (CORBA::Environment &IT_env); + // Report current verbosity level. + +private: + unsigned char verbose_value_; + // Indicate if we are using verbose logging or not. + + unsigned char verbose_message_; + // Indicate if we outputting the messages (turn off if you + // want to conduct timing tests that just measure throughput). +}; + +class profile_logger_i : +#if defined (USE_BOA_IMPL) + public virtual profile_loggerBOAImpl, + public virtual Logger_i +#else /* USE_TIE */ + public logger_i +#endif /* USE_BOA_IMPL */ + // = TITLE + // Implementation of the profiler logger interface. + // + // = DESCRIPTION + // Uses the BOAImpl approach. +{ +public: + virtual void start_timer (CORBA::Environment &env); + // Activate the timer. + + virtual void stop_timer (profile_logger::Elapsed_Time &et, + CORBA::Environment &env); + // Deactivate the timer and return the elapsed time. + +private: + ACE_Profile_Timer pt_; + // Object that keeps track of the user and system execution time. +}; + +#if !defined (USE_BOA_IMPL) +// Indicate that the C++ classes logger_i and profile_logger_i implement +// the IDL interface logger and profile_logger, respectively: + +DEF_TIE_logger (logger_i) +DEF_TIE_profile_logger (profile_logger_i) + +#endif /* USE_BOA_IMPL */ diff --git a/apps/Orbix-Examples/Logger/server.cpp b/apps/Orbix-Examples/Logger/server.cpp new file mode 100644 index 00000000000..c41aa474dcf --- /dev/null +++ b/apps/Orbix-Examples/Logger/server.cpp @@ -0,0 +1,40 @@ +// server.C +// @(#)server.cpp 1.1 10/18/96 + + +// The server for the logger example. +// This uses the TRY,CATCHANY,ENDTRY macros for error testing. + +// The executable file generated from this code should be registered +// (under the name 'logger') using the 'putit' command. + +#include <iostream.h> +#include "logger_i.h" + +int +main (int, char *[]) +{ + // Tell the server not to hang up while clients are connected. + CORBA::Orbix.setNoHangup (1); + + // create a logger object - using the implementation class logger_i +#if defined (USE_BOA_IMPL) + profile_logger_i profile_logger; +#else + TIE_profile_logger (profile_logger_i) profile_logger (new profile_logger_i); +#endif /* USE_BOA_IMPL */ + + TRY { + // tell Orbix that we have completed the server's initialisation: + CORBA::Orbix.impl_is_ready (profile_logger_IMPL, IT_X); + } CATCHANY { + // an error occured calling impl_is_ready () - output the error. + cout << IT_X << endl; + } ENDTRY; + + // impl_is_ready() returns only when Orbix times-out an idle server + // (or an error occurs). + cerr << "server exiting" << endl; + + return 0; +} diff --git a/apps/Orbix-Examples/Makefile b/apps/Orbix-Examples/Makefile new file mode 100644 index 00000000000..723a4dce7c3 --- /dev/null +++ b/apps/Orbix-Examples/Makefile @@ -0,0 +1,25 @@ +#---------------------------------------------------------------------------- +# @(#)Makefile 1.1 10/18/96 +# +# Makefile for the Orbix applications +#---------------------------------------------------------------------------- + +#---------------------------------------------------------------------------- +# Local macros +#---------------------------------------------------------------------------- + +INFO = README + +DIRS = Event_Comm \ + Logger + +#---------------------------------------------------------------------------- +# Include macros and targets +#---------------------------------------------------------------------------- + +include $(WRAPPER_ROOT)/include/makeinclude/wrapper_macros.GNU +include $(WRAPPER_ROOT)/include/makeinclude/macros.GNU +include $(WRAPPER_ROOT)/include/makeinclude/rules.common.GNU +include $(WRAPPER_ROOT)/include/makeinclude/rules.nested.GNU +include $(WRAPPER_ROOT)/include/makeinclude/rules.nolocal.GNU + diff --git a/apps/README b/apps/README new file mode 100644 index 00000000000..348bf472216 --- /dev/null +++ b/apps/README @@ -0,0 +1,19 @@ +The subdirectories in this directory provide a number of complete +applications that utilize the ACE features. + + . Gateway -- Implements a connection-oriented application-level + gateway that uses source-based and destination-based routing + of messages between peers connected via TCP/IP networks. + + . Orbix-Examples -- Implements several applications that + integrate ACE and Orbix (which is IONA's implementation of + CORBA). + + . Synch-Benchmarks -- Implements a number of benchmarks + that test the performance of various synchronization + mechanisms. + + . TTCP -- Implements several variants of the TTCP benchmarking + test for TCP and UCP using C sockets, ACE C++ wrappers, and + several versions of CORBA (Orbix and ORBeline). + diff --git a/apps/gperf/COPYING b/apps/gperf/COPYING new file mode 100644 index 00000000000..9a170375811 --- /dev/null +++ b/apps/gperf/COPYING @@ -0,0 +1,249 @@ + + GNU GENERAL PUBLIC LICENSE + Version 1, February 1989 + + Copyright (C) 1989 Free Software Foundation, Inc. + 675 Mass Ave, Cambridge, MA 02139, USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The license agreements of most software companies try to keep users +at the mercy of those companies. By contrast, our General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. The +General Public License applies to the Free Software Foundation's +software and to any other program whose authors commit to using it. +You can use it for your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Specifically, the General Public License is designed to make +sure that you have the freedom to give away or sell copies of free +software, that you receive source code or can get it if you want it, +that you can change the software or use pieces of it in new free +programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of a such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must tell them their rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any program or other work which +contains a notice placed by the copyright holder saying it may be +distributed under the terms of this General Public License. The +"Program", below, refers to any such program or work, and a "work based +on the Program" means either the Program or any work containing the +Program or a portion of it, either verbatim or with modifications. Each +licensee is addressed as "you". + + 1. You may copy and distribute verbatim copies of the Program's source +code as you receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice and +disclaimer of warranty; keep intact all the notices that refer to this +General Public License and to the absence of any warranty; and give any +other recipients of the Program a copy of this General Public License +along with the Program. You may charge a fee for the physical act of +transferring a copy. + + 2. You may modify your copy or copies of the Program or any portion of +it, and copy and distribute such modifications under the terms of Paragraph +1 above, provided that you also do the following: + + a) cause the modified files to carry prominent notices stating that + you changed the files and the date of any change; and + + b) cause the whole of any work that you distribute or publish, that + in whole or in part contains the Program or any part thereof, either + with or without modifications, to be licensed at no charge to all + third parties under the terms of this General Public License (except + that you may choose to grant warranty protection to some or all + third parties, at your option). + + c) If the modified program normally reads commands interactively when + run, you must cause it, when started running for such interactive use + in the simplest and most usual way, to print or display an + announcement including an appropriate copyright notice and a notice + that there is no warranty (or else, saying that you provide a + warranty) and that users may redistribute the program under these + conditions, and telling the user how to view a copy of this General + Public License. + + d) You may charge a fee for the physical act of transferring a + copy, and you may at your option offer warranty protection in + exchange for a fee. + +Mere aggregation of another independent work with the Program (or its +derivative) on a volume of a storage or distribution medium does not bring +the other work under the scope of these terms. + + 3. You may copy and distribute the Program (or a portion or derivative of +it, under Paragraph 2) in object code or executable form under the terms of +Paragraphs 1 and 2 above provided that you also do one of the following: + + a) accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of + Paragraphs 1 and 2 above; or, + + b) accompany it with a written offer, valid for at least three + years, to give any third party free (except for a nominal charge + for the cost of distribution) a complete machine-readable copy of the + corresponding source code, to be distributed under the terms of + Paragraphs 1 and 2 above; or, + + c) accompany it with the information you received as to where the + corresponding source code may be obtained. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form alone.) + +Source code for a work means the preferred form of the work for making +modifications to it. For an executable file, complete source code means +all the source code for all modules it contains; but, as a special +exception, it need not include source code for modules which are standard +libraries that accompany the operating system on which the executable +file runs, or for standard header files or definitions files that +accompany that operating system. + + 4. You may not copy, modify, sublicense, distribute or transfer the +Program except as expressly provided under this General Public License. +Any attempt otherwise to copy, modify, sublicense, distribute or transfer +the Program is void, and will automatically terminate your rights to use +the Program under this License. However, parties who have received +copies, or rights to use copies, from you under this General Public +License will not have their licenses terminated so long as such parties +remain in full compliance. + + 5. By copying, distributing or modifying the Program (or any work based +on the Program) you indicate your acceptance of this license to do so, +and all its terms and conditions. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the original +licensor to copy, distribute or modify the Program subject to these +terms and conditions. You may not impose any further restrictions on the +recipients' exercise of the rights granted herein. + + 7. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of the license which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +the license, you may choose any version ever published by the Free Software +Foundation. + + 8. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 9. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 10. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + Appendix: How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to humanity, the best way to achieve this is to make it +free software which everyone can redistribute and change under these +terms. + + To do so, attach the following notices to the program. It is safest to +attach them to the start of each source file to most effectively convey +the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) 19yy <name of author> + + 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 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19xx name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the +appropriate parts of the General Public License. Of course, the +commands you use may be called something other than `show w' and `show +c'; they could even be mouse-clicks or menu items--whatever suits your +program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + program `Gnomovision' (a program to direct compilers to make passes + at assemblers) written by James Hacker. + + <signature of Ty Coon>, 1 April 1989 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/apps/gperf/ChangeLog b/apps/gperf/ChangeLog new file mode 100644 index 00000000000..d0e86c82103 --- /dev/null +++ b/apps/gperf/ChangeLog @@ -0,0 +1,1335 @@ +Sun Apr 14 14:31:10 1996 Douglas C. Schmidt (schmidt@tango.cs.wustl.edu) + + * src: Changed things so that there's no longer any use of the + Read_Line and Std_Err code. All of this has been pushed into + the ACE components, which is where it belongs... + + * src: Changed things so that there's no longer any use of the + pointless inheritance in the code. This was a result of my not + understanding inheritance back in 1989... ;-) + + * Began to integrate GNU gperf into the ACE release. Started off + by bringing the standard GNU version up to date wrt to the + changes I made back in 1991! + +Tue Oct 10 16:37:28 1995 Mike Stump <mrs@cygnus.com> + + * src/new.cc: Since malloc/delete are not paired, we cannot call + free. + +Wed Jan 4 12:40:14 1995 Per Bothner <bothner@kalessin.cygnus.com> + + * src/Makefile.in ($(TARGETPROG)): Link with $(LDFLAGS). + Patch from John Interrante <interran@uluru.stanford.edu>. + +Sat Nov 5 19:12:48 1994 Jason Merrill (jason@phydeaux.cygnus.com) + + * src/Makefile.in (LIBS): Remove. + +Tue Oct 18 17:51:14 1994 Per Bothner <bothner@kalessin.cygnus.com> + + * src/std-err.cc: Use stderror, instead of the non-standard + sys_nerr and sys_errlist. + +Sat Sep 17 22:02:13 1994 Per Bothner (bothner@kalessin.cygnus.com) + + * src/key-list.cc (output_hash_function): + Patch from William Bader <wbader@CSEE.Lehigh.Edu>. + +Fri Jul 15 09:38:11 1994 Per Bothner (bothner@cygnus.com) + + * src/std-err.cc: #include <errno.h>, and only declare + extern int errno if errno is not a macro. + +Mon May 30 17:29:34 1994 Per Bothner (bothner@kalessin.cygnus.com) + + * Makefile.in (src_all, install): Make sure to add '/' after + `pwd` in $rootme, as expected by FLAGS_TO_PASS. + +Wed May 11 00:47:22 1994 Jason Merrill (jason@deneb.cygnus.com) + + Make libg++ build with gcc -ansi -pedantic-errors + * src/options.h: Lose commas at end of enumerator lists. + +Sun Dec 5 19:16:40 1993 Brendan Kehoe (brendan@lisa.cygnus.com) + + * src/hash-table.cc (Hash_Table::~Hash_Table): Don't pass an + argument to fprintf, since it's not expecting one. + +Fri Nov 26 19:03:18 1993 Per Bothner (bothner@kalessin.cygnus.com) + + * src/list-node.cc: #undef index, for the sake of broken NeXT, + +Thu Nov 4 11:16:03 1993 Per Bothner (bothner@kalessin.cygnus.com) + + * Makefile.in (install): Use INSTALL_DATA for gperf.1. + +Mon Oct 25 18:40:51 1993 Per Bothner (bothner@kalessin.cygnus.com) + + * src/key-list.cc (Key_List::read_keys): Use POW macro + to increase hash table size to power of 2. + + * options.h (LARGE_STACK_ARRAYS): New flag. Defaults to zero. + * gen-perf.cc, key-list.cc, read-line.cc: + Only stack-allocate large arrays if LARGE_STACK_ARRAYS is set. + * main.cc (main): Only call setrlimit (RLIMIT_STACK, ...) + if LARGE_STACK_ARRAYS. + +Mon Oct 4 17:45:08 1993 Per Bothner (bothner@kalessin.cygnus.com) + + * src/gen-perf.cc: Always use ANSI rand/srand instead of BSDisms. + +Wed Aug 18 12:19:53 1993 Per Bothner (bothner@kalessin.cygnus.com) + + * Makefile.in (src_all): Make less verbose output. + +Fri May 28 14:01:18 1993 Per Bothner (bothner@rtl.cygnus.com) + + * src/gen-perf.cc (Gen_Perf::change): Don't use gcc-specific + 2-operand conditional expression. + * src/key-list.cc (Key_List::output_lookup_array): + Don't use variable-size stack arrays, unless compiled by g++. + +Tue May 4 14:08:44 1993 Per Bothner (bothner@cygnus.com) + + Changes (mostly from Peter Schauer) to permit compilation + using cfront 3.0 and otherwise be ARM-conforming. + * src/key-list.h: class Key_List must use public derivation + of base class Std_Err (because Gen_Perf::operator() in gen-perf.cc + calls Std_Err::report_error). + * src/gen-perf.cc (Gen_Perf::affects_prev), src/hash-table.cc + (Hash_Table::operator()): Don't use gcc-specific 2-operand + conditional expression. + * src/iterator.cc (Iterator::operator()): Don't use gcc-specific + range construct in case label. + * key-list.cc (Key_List::output_lookup_array, Key_List::read_keys), + src/gen-perf.cc (Gen_Perf::operator(), src/read-line.cc + (Read_Line::readln_aux): If not gcc, don't allocate + variable-sized arrays on stack. + * src/new.cc (operator new): Argument type should be size_t. + * key-list.cc (Key_List::output_lookup_array, Key_List::read_keys), + new/cc (::operator new): Don't use non-standard >?= operator. + +Tue Apr 27 20:11:30 1993 Per Bothner (bothner@cygnus.com) + + * src/Makefile.in: Define TARGETPROG, and use it. + +Mon Apr 19 00:29:18 1993 Per Bothner (bothner@cygnus.com) + + * Makefile.in, configure.in: Re-vamped configure scheme. + * gperf.texinfo: Renamed to gperf.texi. + * src/bool-array.{h,cc}: ANSIfy bzero->memset. + +Sat Jan 30 20:21:28 1993 Brendan Kehoe (brendan@lisa.cygnus.com) + + * tests/Makefile.in (mostlyclean): Also delete aout, cout, m3out, + pout, and preout. + +Tue Dec 29 08:58:17 1992 Ian Lance Taylor (ian@cygnus.com) + + * Makefile.in: pass $(FLAGS_TO_PASS) to all calls to make. + (FLAGS_TO_PASS): added INSTALL, INSTALL_DATA, INSTALL_PROGRAM. + +Mon Dec 21 18:46:46 1992 Per Bothner (bothner@rtl.cygnus.com) + + * tests/expected.* renamed to *.exp to fit in 14 chars. + * tests/Makefile.in: Update accordingly. + Also rename output.* to *.out. + * src/Makefile.in (clean): Remove gperf program. + +Wed Dec 9 14:33:34 1992 Per Bothner (bothner@cygnus.com) + + * src/hash-table.cc, src/bool-array.h: ANSIfy bzero->memset. + +Thu Dec 3 19:34:12 1992 Per Bothner (bothner@cygnus.com) + + * Makefile.in (distclean, realclean): Don't delete + Makefile before recursing. + +Fri Nov 6 13:41:49 1992 Per Bothner (bothner@rtl.cygnus.com) + + * key-list.{h,cc}: Remove MAX_INT (and similar) constant + fields from Key_List class, and use INT_MAX (etc) from limits.h. + * key-list.{h,cc}, options.{h,cc}, vectors.h: Removed all + uses of initialized const fields, as they are non-standard + - and their use was easy to do away with. Mostly, just + made the constants static non-fields in the .cc file. + +Mon Nov 2 13:10:11 1992 Per Bothner (bothner@cygnus.com) + + * tests/Makefile.in: When generating cinset.c, don't pass -C, + since -C assumes an ANSI compiler. Add the -C flag (with -a) + when generating test.out.3 instead. + * tests/expected.out.3: Update accordingly. + +Wed Aug 12 11:47:54 1992 Per Bothner (bothner@cygnus.com) + + * Makefile.in: Factor out common flags into $(FLAGS_TO_PASS). + * Makefile.in: 'install-info' depends on gperf.info. + +Mon Aug 10 11:39:52 1992 Ian Lance Taylor (ian@dumbest.cygnus.com) + + * Makefile.in, src/Makefile.in: always create installation + directories. + +Mon Jul 20 15:33:21 1992 Mike Stump (mrs@cygnus.com) + + * src/new.cc (operator new): Add cast from void * to char *, + since it is not a standard conversion. + +Wed Jun 17 16:25:30 1992 Per Bothner (bothner@rtl.cygnus.com) + + * src/gen-perf.cc: #include <_G_config.h> for _G_SYSV. + * src/key-list.cc: alloca() hair. + * src/main.cc (main): Only call getrlimit if _G_HAVE_SYS_RESOURCE. + * Makefile,in, {src,test}/Makefile.in: Fix *clean rules. + +Fri May 29 13:21:13 1992 Per Bothner (bothner@rtl.cygnus.com) + + * src/gen-perf.cc: Replace USG -> _G_SYSV. + +Thu May 14 13:58:36 1992 Per Bothner (bothner@rtl.cygnus.com) + + * src/Makefile.in: Don't pass obsolete flag -DUNLIMIT_STACK. + * tests/Makefile.in (clean): Fix. + +Sat Mar 7 00:03:56 1992 K. Richard Pixley (rich@rtl.cygnus.com) + + * gperf.texinfo: added menu item hook. + +Wed Feb 26 18:04:40 1992 K. Richard Pixley (rich@cygnus.com) + + * Makefile.in, configure.in: removed traces of namesubdir, + -subdirs, $(subdir), $(unsubdir), some rcs triggers. Forced + copyrights to '92, changed some from Cygnus to FSF. + +Sun Jan 26 19:21:58 1992 Per Bothner (bothner at cygnus.com) + + * tests/Makefile.in: Use re-directed stdin instead of file + name in argv. This allows us to remove the filename + from the output, the expected output, and hence the diffs. + (Note that the input file is in $(srcdir), which we cannot + place in the expected out files.) + * tests/expected.out.[1235]: Edit out input filename, + to match new output. + +Mon Nov 4 15:04:41 1991 Douglas C. Schmidt (schmidt at bastille.ics.uci.edu) + + * Need to do something about the end-of-line marker being + hard-coded to '\n'... + + * Need to do something about the comment character being + hard-coded to '#'... + +Fri Sep 27 09:30:15 1991 Douglas C. Schmidt (schmidt at net4.ics.uci.edu) + + * Fixed a stupid problem with printout out a local enum with the + -E option (I forgot to check for the case of 0 duplicates, so it + was saying 1 duplicate instead!). + +Mon Aug 19 00:39:40 1991 Douglas C. Schmidt (schmidt at javel.ics.uci.edu) + + * Yow, all finished making gperf run with cfront/Saber C++. Not + really all that hard, actually, though did need to remove some + GNU specific hacks, like dynamically sized arrays and + initializing class data members in their declarations, etc. + + * Bumped up the version # to reflect the recent changes. + +Sun Aug 18 22:25:32 1991 Douglas C. Schmidt (schmidt at javel.ics.uci.edu) + + * Changed passage of Options::usage function in Options.C to have + a leading `&' so that Saber C++ wouldn't complain... + + * Added a new header file called gperf.h that includes system-wide + info. + + * Hacked up the release to work with Saber C++! Changed all *.cc + files to *.C. + +Mon Aug 5 21:18:47 1991 Douglas C. Schmidt (schmidt at net1.ics.uci.edu) + + * Yow, hacked in the nifty changes to the Std_Err error handling + abstraction. This now adds format string support for printing + out signals and the name of the function when things go wrong. + Make changes throughout the source to make use of the new + facilities and also to make sure all previous uses of + Std_Err::report_error are now prefixed by the name of the class. + +Tue Jul 30 00:02:39 1991 Douglas C. Schmidt (schmidt at net4.ics.uci.edu) + + * Make sure to add 1 to the Key_List::total_duplicates value when + printing it out since any time we have more than zero duplicates + we really have two or more duplicates! + + * Added support for the -O (optimize option). This option + optimizes the generated lookup function by assuming that all + input keywords are members of the keyset from the keyfile. + + * Added #define DUPLICATES and #define HASH_VALUE_RANGE (and of + course the enum equivalent) to the generated output. Don't know + how useful this will be, but it allows us to determine at a + glance whether we've got a minimal perfect hash function (in + which case TOTAL_KEYWORDS = HASH_VALUE_RANGE, and DUPLICATES = + 0). + + * Fixed a small bug in the Key_List::output_keyword_table routine + that caused an extra newline to be printed if there where no + leading blank entries... (who cares, right?!) + +Mon Jul 29 22:05:40 1991 Douglas C. Schmidt (schmidt at net4.ics.uci.edu) + + * Modified the handling of the -E (emit enums rather than + #defines) option in conjunction with the -G option. Now, if -G + and -E are given the enums are generated outside the lookup + function, rather than within it! + + * Yow, as part of my Software Practice and Experience submission + writeup I realized I needed to make the # comment character work + correctly. Now if you put a backslash character ('\') in front + of the # it treats the first character as a #. Naturally, to + put a '\' character on the front of the line you need to escape + it also, i.e., + \\I'm a line that starts with only one \ + # I'm a comment line + \#define I'm walking a fine line... ;-) + +Wed Jun 26 11:21:02 1991 Douglas C. Schmidt (schmidt at bastille.ics.uci.edu) + + * Changed all uses of the identifier `iteration_number' to + `generation_number' (also updated the paper!). + +Tue Apr 9 07:59:42 1991 Doug Schmidt (schmidt at net4.ics.uci.edu) + + * Had to change a whole bunch of little thangs in key-list.cc and + list-node.cc to make the -I option work. + + * Changed an alloca statement in key-list.cc to reflect the + strncasecmp modification (i.e., we now need to be able to + allocate a longer buffer if the -I option is used). + +Mon Apr 8 18:17:04 1991 Doug Schmidt (schmidt at net4.ics.uci.edu) + + * Yucko, there was a bug in the handling of -c (and of course the + new -I command in key-list.cc). Apparently when I added the + super-duper hack that provided support for duplicate keys I + forgot to update the strcmp output... + + * Boy, it has been a *long* time since I hacked this puppy. Let's + see, I'm about to add long-overdue support for case-insensitive + string comparisons to gperf's generated output code. We are + going to employ the hitherto unused option -I to indicte this! + +Thu Jun 28 16:17:27 1990 Doug Schmidt (schmidt at brilliant) + + * Wow, first fix on the new job! There was a dumb error + in Key_List::output_lookup_function, where I printed the + string "&wordlist[key]" instead of the correct "&wordlist[index]". + + * Added a couple of #ifdefs for USG support. + +Sun Jun 3 17:16:36 1990 Doug Schmidt (schmidt at crimee.ics.uci.edu) + + * Updated the version number to 2.5 and sent to Doug Lea for release + with the latest GNU libg++. + + * Changed the error handling when a keyword file cannot be opened + (now calls perror). + +Wed May 30 14:49:40 1990 Doug Schmidt (schmidt at crimee.ics.uci.edu) + + * Instrumented the source code with trace statements automagically + inserted using my new automated trace instrumentation tool! + +Wed May 9 11:47:41 1990 Doug Schmidt (schmidt at siam.ics.uci.edu) + + * Really fixed the previous bug. Turns out that a small amount + of logic had to be duplicated to handle static links that occur + as part of dynamic link chains. What a pain!!! + +Tue May 8 23:11:44 1990 Doug Schmidt (schmidt at siam.ics.uci.edu) + + * Fixed a stupid bug in Key_List::output_lookup_array that was + causing incorrect counts to be generated when there were both + static and dynamic links occurring for the same hash value. + Also simplified the code that performs the logic in this routine. + +Mon Apr 30 17:37:24 1990 Doug Schmidt (schmidt at crimee.ics.uci.edu) + + * Fixed stupid bug in Key_List::output_lookup_array that was + making the generated lookup[] array contain `chars' even + when the values stored in the chars are greater than 127! + + * Changed the behavior of the -G (global table) option so that it + will output the `length[]' array in the global scope along with + the `word_list[]' array. + + * Fixed a stupid bug in Key_List::output_lookup_function that + would always output the complicated `duplicate-handling' lookup + logic, even when there were no duplicates in the input! + + * Yikes, had to modify a bunch of stuff in key-list.cc to correctly + handle duplicate entries. Changed the generated code so that + the MIN_HASH_VALUE is no longer subtracted off when calculating + the hash value for a keyword. This required changing some other + code by substituting MAX_HASH_VALUE for TOTAL_KEYS in several places. + Finally, this means that the generated tables may contain leading + null entries, but I suppose it is better to trade-off space to get + faster performance... + +Mon Mar 26 13:08:43 1990 Doug Schmidt (schmidt at crimee.ics.uci.edu) + + * Updated version number to 2.4 to reflect the latest changes. + + * Changed the main program so that it always prints out gperf's + execution timings to the generated output file. + +Sun Mar 25 12:39:30 1990 Doug Schmidt (schmidt at crimee.ics.uci.edu) + + * Added the -Z option so that users can specify the name of the + generated class explicitly. Updated documentation to reflect + this change. + + * Modified the generated C++ class interface so that the functions + are declared static (to remove the overhead of passing the `this' + pointer). This means that operator()() can no longer be used, + since it only works on non-static member functions. + Also changed things so that there is no constructor (why waste + the extra call, when it doesn't do anything, eh?) + + * Modified the behavior of Key_List::output when the -L C++ option + is enabled. Previously the code generated use const data members + to record MIN_WORD_LENGTH, MIN_HASH_VALUE, etc. However, as + pointed out by James Clark this may result in suboptimal behavior + on the part of C++ compilers that can't inline these values. + Therefore, the new behavior is identical to what happens with + -L C, i.e., either #defines or function-specific enums are used. + Why sacrifice speed for some abstract notion of `code purity?' ;-) + +Tue Mar 6 18:17:42 1990 Doug Schmidt (schmidt at crimee.ics.uci.edu) + + * Added the -E option that defines constant values using an enum + local to the lookup function rather than with #defines. This + also means that different lookup functions can reside in the + same file. Thanks to James Clark (jjc@ai.mit.edu). + +Sat Mar 3 20:19:00 1990 Doug Schmidt (schmidt at zola.ics.uci.edu) + + * Added a special case to key_list::output_switch that doesn't + generate extra comparisons when the `-S' is given an argument + of 1 (the normal case). This should speed up the generated + code output a tad... + +Fri Feb 23 14:21:28 1990 Doug Schmidt (schmidt at zola.ics.uci.edu) + + * Renamed all instances of member function get_keysig_size + to get_max_keysig_size, since this is more precise... + + * Changed all occurrences of charset to keysig (stands for ``key + signature'') to reflect the new naming convention used in the + USENIX paper. + +Thu Feb 22 11:28:36 1990 Doug Schmidt (schmidt at crimee.ics.uci.edu) + + * Changed the name of the generated associated values table from + asso_value to asso_values to reflect conventions in the USENIX + C++ paper. + +Thu Feb 15 23:29:03 1990 Doug Schmidt (schmidt at zola.ics.uci.edu) + + * Updated the gperf.texinfo file to fix some formatting problems + that had crept in since last time. + +Wed Feb 14 23:27:24 1990 Doug Schmidt (schmidt at zola.ics.uci.edu) + + * Fixed stupid bug in key-list.cc (get_special_input), wher + gperf replaced each '%' with the succeeding character. + + * Added support for multiple target language generation. Currently + handled languages are C and C++, with C as the default. Updated + documentation and option handler to reflect the changes. + + * Added a global destructor to new.cc and removed the #ifdef, since + the bloody thing now works with libg++. + +Mon Feb 14 13:00:00 1990 Doug Schmidt (schmidt at zola.ics.uci.edu) + + * Found out that my gperf paper was accepted at the upcoming + USENIX C++ Conference in San Francisco. Yow! + +Tue Jan 30 09:00:29 1990 Doug Schmidt (schmidt at zola.ics.uci.edu) + + * #ifdef'd out the new.cc memory allocator, since there are + problems with this and the libg++ stuff. + + * Changed key-list.h so that class Vectors is a public (rather + than private) base class for class Key_List. The previous + form was illegal C++, but wasn't being caught by the old + g++ compiler. Should work now... ;-) + +Sun Dec 10 14:08:23 1989 Doug Schmidt (schmidt at glacier.ics.uci.edu) + + * Added several changes from rfg@ics.uci.edu. These changes + help to automate the build process. + +Wed Nov 15 15:49:33 1989 Doug Schmidt (schmidt at zola.ics.uci.edu) + + * Removed conditional compilation for GATHER_STATISTICS. There's + really no good reason to avoid collecting this info at run-time, + since that section of code is *hardly* the bottleneck... ;-) + + * Simplified the C output routines in Key_List::set_output_types + and Key_List::output_keyword_table a bit in order to + speed-up and clean up the code generation. + + * Modified function Key_List::get_special_input so that it does + not try to `delete' a buffer that turned out to be too short. + This is important since the new memory management scheme + does not handle deletions. However, adding a small amount of + garbage won't hurt anything, since we generally don't do this + operation more than a couple times *at most*! + + * Created a new file (new.cc) which includes my own overloaded + operator new. This function should dramatically reduce the + number of calls to malloc since it grabs large chunks and + doles them out in small pieces. As a result of this change + the class-specific `operator new' was removed from class List_Node. + +Tue Nov 14 21:45:30 1989 Doug Schmidt (schmidt at glacier.ics.uci.edu) + + * Continued to refine the great hack. The latest trick is to + try and replace most uses of dynamic memory (i.e., calls to + new) with uses of gcc dynamic arrays (i.e., an alloca solution). + This makes life much easier for the overall process-size, since + it reduces the amount of overhead for memory management. As a + side-effect from this change there is no reason to have the + Bool_Array::dispose member function, so it's outta here! + + * Fixed a stupid bug that was an disaster waiting to happen... + Instead of making the boolean array large enough to index + max_hash_value it was only large enough to index max_hash_value + - 1. Once again, an off-by-one mistake in C/C++!!!! + +Mon Nov 13 19:38:27 1989 Doug Schmidt (schmidt at glacier.ics.uci.edu) + + * Added the final great hack! This allows us to generate hash tables + for near-perfect hash functions that contain duplicates, *without* + having to use switch statements! Since many compilers die on large + switch statements this feature is essential. Furthermore, it appears + that the generated code is often *smaller* than that put out by + compilers, even though a large, sparse array must be created. + Here's the general idea: + + a. Generate the wordlist as a contiguous block of keywords, + just as before when using a switch statement. This + wordlist *must* be sorted by hash value. + + b. Generate the lookup array, which is an array of signed + {chars,shorts,ints}, (which ever allows full coverage of + the wordlist dimensions). If the value v, where v = + lookup[hash(str,len)], is >= 0 and < TOTAL_KEYWORDS, then we + simply use this result as a direct access into the wordlist + array to snag the keyword for comparison. + + c. Otherwise, if v is < -TOTAL_KEYWORDS or > TOTAL_KEYWORDS + this is an indication that we'll need to search through + some number of duplicates hash values. Using a hash + linking scheme we'd then index into a different part of + the hash table that provides the starting index and total + length of the duplicate entries to find via linear search! + +Sun Nov 12 13:48:10 1989 Doug Schmidt (schmidt at zola.ics.uci.edu) + + * Simplified Key_List::output_min_max considerably by recognizing + that since the keyword list was already sorted by hash value finding + the min and max values is trivial! + + * Improved the debugging diagnostics considerably in classes Key_List, + Hash_Table, and Gen_Perf. + + * Modified the `-s' option so that a negative argument is now + interpreted to mean `allow the maximum associated value to be + about x times *smaller* than the number of input keys.' This + should help prevent massive explosion of generated hash table + size for large keysets. + +Sat Nov 11 11:31:13 1989 Doug Schmidt (schmidt at glacier.ics.uci.edu) + + * Added a field in class Key_List that counts the total number + of duplicate keywords, both static and dynamic. + + * Added a new member function Bool_Array that deletes the dynamic + memory allocated to Bool_Array::storage_array. This space may + be needed for subsequent options, so it made sense to free it as + soon as possible... + + * Renamed file/class Alpha_Vectors to Vectors, to avoid problems + with 14 character length filenames on SYSV. Also changed file + adapredefined.gperf to adadefs.gperf in the ./tests directory. + + * Modified class Options by changing the member function + Options::total_positions to Options::get_charset_size and + Options::set_charset_size. These two routines now either return + the total charset size *or* the length of the largest keyword + if the user specifies the -k'*' (ALLCHARS) option. This change + cleans up client code. + + * Merged all the cperf changes into gperf. + + * Made sure to explicitly initialize perfect.fewest_collisions to + 0. + + * Cleaned up some loose ends noticed by Nels Olson. + 1. Removed `if (collisions <= perfect.fewest_collisions)' + from Gen_Perf::affects_prev since it was superfluous. + 2. Removed the fields best_char_value and best_asso_value + from Gen_Perf. There were also unnecessary. + 3. Fixed a braino in the Bool_Array::bool_array_reset + function. Since iteration numbers can never be zero + the `if (bool_array.iteration_number++ == 0)' must be + `if (++bool_array.iteration_number == 0).' + 4. Modified Std_Err::report_error so that it correctly handles + "%%". + + * It is important to note that -D no longer enables -S. + There is a good reason for this change, which will become + manifested in the next release... (suspense!). + + * Made some subtle changes to Key_List::print_switch so that if finally + seems to work correctly. Needs more stress testing, however... + + * Made a major change to the Key_List::print_switch function. + The user can now specify the number of switch statements to generate + via an argument to the -S option, i.e., -S1 means `generate 1 + switch statement with all keywords in it,' -S2 means generate + 2 switch statements with 1/2 the elements in each one, etc. + Hopefully this will fix the problem with C compilers not being + able to generate code for giant switch statements (but don't + hold your breath!) + + * Changed Key_List::length function to Key_List::keyword_list_length. + + * Added a feature to main.c that prints out the starting wall-clock + time before the program begins and prints out the ending wall-clock + time when the program is finished. + + * Added the GATHER_STATISTICS code in hash-table.c so we can + keep track of how well double hashing is doing. Eventually, + GATHER_STATISTICS will be added so that all instrumentation + code can be conditionally compiled in. + + * Fixed a stupid bug in Key_List::print_switch routine. This + was necessary to make sure the generated switch statement worked + correctly when *both* `natural,' i.e., static links and dynamic + links, i.e., unresolved duplicates, hash to the same value. + + * Modified Bool_Array::~Bool_Array destructor so that + it now frees the bool_array.storage_array when it is no longer + needed. Since this array is generally very large it makes sense + to return the memory to the freelist when it is no longer in use. + + * Changed the interface to constructor Hash_Table::Hash_Table. This + constructor now passed a pointer to a power-of-two sized buffer that + serve as storage for the hash table. Although this weakens information + hiding a little bit it greatly reduces dynamic memory fragmentation, + since we can now obtain the memory via a call to alloca, rather + than malloc. This change modified Key_List::read_keys calling + interface. + + * Since alloca is now being used more aggressively a conditional + compilation section was added in main.c. Taken from GNU GCC, + this code gets rid of any avoidable limit on stack size so that + alloca does not fail. It is only used if the -DRLIMIT_STACK + symbol is defined when gperf is compiled. + + * Added warnings in option.c so that user's would be informed + that -r superceeds -i on the command-line. + + * Rewrote Gen_Perf::affects_prev. First, the code structure + was cleaned up considerably (removing the need for a dreaded + goto!). Secondly, a major change occurred so that Gen_Perf::affects_prev + returns FALSE (success) when fewest_hits gets down to whatever + it was after inserting the previous key (instead of waiting for + it to reach 0). In other words, it stops trying if it can + resolve the new collisions added by a key, even if there are + still other old, unresolved collisions. This modification was + suggested by Nels Olson and seems to *greatly* increase the + speed of gperf for large keyfiles. Thanks Nels! + + * In a similar vein, inside the Gen_Perf::change routine + the variable `perfect.fewest_collisions is no longer initialized + with the length of the keyword list. Instead it starts out at + 0 and is incremented by 1 every time change () is called. + The rationale for this behavior is that there are times when a + collision causes the number of duplicates (collisions) to + increase by a large amount when it would presumably just have + gone up by 1 if none of the asso_values were changed. That is, + at the beginning of change(), you could initialize fewest_hits + to 1+(previous value of fewest_hits) instead of to the number of + keys. Thanks again, Nels. + + * Replaced alloca with new in the Gen_Perf::change function. + This should eliminate some overhead at the expense of a little + extra memory that is never reclaimed. + + * Renamed Gen_Perf::merge_sets to Gen_Perf::compute_disjoint_union + to reflect the change in behavior. + + * Added the -e option so users can supply a string containing + the characters used to separate keywords from their attributes. + The default behavior is ",\n". + + * Removed the char *uniq_set field from LIST_NODE and modified + uses of uniq_set in perfect.c and keylist.c. Due to changes + to Gen_Perf::compute_disjoint_sets this field was no longer + necessary, and its removal makes the program smaller and + potentially faster. + + * Added lots of changes/fixes suggested by Nels Olson + (umls.UUCP!olson@mis.ucsf.edu). In particular: + 1. Changed Bool_Array so that it would dynamically create + an array of unsigned shorts rather than ints if the + LO_CAL symbol was defined during program compilation. + This cuts the amount of dynamic memory usage in half, + which is important for large keyfile input. + 2. Added some additional debugging statements that print extra + info to stderr when the -d option is enabled. + 3. Fixed a really stupid bug in Key_List::print_switch + A right paren was placed at the wrong location, which broke + strlen (). + 4. Fixed a subtle problem with printing case values when keylinks + appear. The logic failed to account for the fact that there + can be keylinks *and* regular node info also! + 5. Changed the behavior of Key_List::read_keys so that it would + honor -D unequivocally, i.e., it doesn't try to turn off dup + handling if the user requests it, even if there are no + immediate links in the keyfile input. + 6. Modified the -j option so that -j 0 means `try random values + when searching for a way to resolve collisions.' + 7. Added a field `num_done' to the Gen_Perf struct. This is used + to report information collected when trying to resolve + hash collisions. + 8. Modified the merge_sets algorithm to perform a disjoint + union of two multisets. This ensures that subsequent + processing in Gen_Perf::affect_prev doesn't + waste time trying to change an associated value that is + shared between two conflicting keywords. + 9. Modified Gen_Perf::affects_prev so that it doesn't try + random jump values unless the -j 0 option is enabled. + 10. Fixed a silly bug in Gen_Perf::change. This problem caused + gperf to seg fault when the -k* option was given and the + keyfile file had long keywords. + +Sun Oct 29 00:18:55 1989 Doug Schmidt (schmidt at siam.ics.uci.edu) + + * Modified class-specific new operations for Read_Line and + List_Node so they don't fail if SIZE is larger than twice + the previous buffer size. Note we double buffer size + everytime the previous buffer runs out, as a heuristic + to reduce future calls to malloc. + +Sun Oct 22 13:49:43 1989 Doug Schmidt (schmidt at glacier.ics.uci.edu) + + * Updated gperf version number to 2.0. Send to Doug Lea for + incorporation into the long-awaited `official' libg++ 1.36 + release! + + * Thanks to Nels Olson a silly bug in Gen_Perf::change () + was fixed. This problem caused gperf to seg fault when + the -k* option was given and the keyfile file had long + keywords. + + * Modified Key_List::print_hash_function so that it output + max_hash_value + 1 (rather than just max_hash_value) for + any associated value entries that don't correspond to + keyword charset characters. This should speed up rejection + of non-keyword strings a little in some cases. + +Sat Oct 21 19:28:36 1989 Doug Schmidt (schmidt at crimee.ics.uci.edu) + + * Fixed Key_List::print_hash_function so that it no longer output + things like `return 0 + ...' Although this probably gets + optimized away by even the worst C compilers there isn't any + point tempting fate... ;-) + + * Fixed class List_Node's constructor so that it wouldn't a priori + refuse to consider trying to hash keys whose length is less + than the smallest user-specified key position. It turns out + this is not a problem unless the user also specifies the -n + (NOLENGTH) option, in which case such keys most likely + don't have a prayer of being hashed correctly! + + * Changed the name of the generated lookup table from `Hash_Table' + to `asso_value' to be consistent with the gperf paper. + +Tue Oct 17 14:19:48 1989 Doug Schmidt (schmidt at glacier.ics.uci.edu) + + * Added a flag GATHER_STATISTICS in the Makefile. If defined + during compilation this turns on certain collection facilities + that track the performance of gperf during its execution. In + particular, I want to see how many collisions occur for the + double hashing Hash_Table. + + * Added a safety check so that we don't screw up if the total + number of `resets' of the Bool_Array exceeds MAX_INT. Since + this number is around 2^31 it is unlikely that this would ever + occur for most input, but why take the risk? + + * Changed the behavior for the -a (ANSI) option so that the + generated prototypes use int rather than size_t for the LEN + parameter. It was too ugly having to #include <stddef.h> all + over the place... + +Mon Oct 16 11:00:35 1989 Doug Schmidt (schmidt at crimee.ics.uci.edu) + + * Continued to work on the gperf paper for the USENIX C++ + conference. At some point this will be merged back into + the gperf documentation... + +Sat Oct 14 20:29:43 1989 Doug Schmidt (schmidt at siam.ics.uci.edu) + + * Added a majorly neat hack to Bool_Array, suggested by rfg. + The basic idea was to throw away the Ullman array technique. + The Ullman array was used to remove the need to reinitialize all + the Bool_Array elements to zero everytime we needed to determine + whether there were duplicate hash values in the keyword list. + The current trick uses an `iteration number' scheme, which takes + about 1/3 the space and reduces the overall program running a + time by about 20 percent for large input! The hack works as + follows: + + 1. Dynamically allocation 1 boolean array of size k. + 2. Initialize the boolean array to zeros, and consider the first + iteration to be iteration 1. + 2. Then on all subsequent iterations we `reset' the bool array by + kicking the iteration count by 1. + 3. When it comes time to check whether a hash value is currently + in the boolean array we simply check its index location. If + the value stored there is *not* equal to the current iteration + number then the item is clearly *not* in the set. In that + case we assign the iteration number to that array's index + location for future reference. Otherwise, if the item at + the index location *is* equal to the iteration number we've + found a duplicate. No muss, no fuss! + +Mon Oct 2 12:30:54 1989 Doug Schmidt (schmidt at glacier.ics.uci.edu) + + * Changed some consts in options.h to enumerals, since g++ + doesn't seem to like them at the moment! + +Sat Sep 30 12:55:24 1989 Doug Schmidt (schmidt at glacier.ics.uci.edu) + + * Fixed a stupid bug in Key_List::print_hash_function that manifested + itself if the `-k$' option was given (i.e., only use the key[length] + character in the hash function). + + * Added support for the -C option. This makes the contents of + all generated tables `readonly'. + + * Changed the handling of generated switches so that there is + only one call to str[n]?cmp. This *greatly* reduces the size of + the generated assembly code on all compilers I've seen. + + * Fixed a subtle bug that occurred when the -l and -S option + was given. Code produced looked something like: + + if (len != key_len || !strcmp (s1, resword->name)) return resword; + + which doesn't make any sense. Clearly, this should be: + + if (len == key_len && !strcmp (s1, resword->name)) return resword; + +Tue Sep 26 10:36:50 1989 Doug Schmidt (schmidt at glacier.ics.uci.edu) + + * Changed class Read_Line's definition so that it no longer + needs to know about the buffering scheme used to speed up + dynamic memory allocation of input keywords and their + associated attributes. This means that operator new is no longer + a friend of Read_Line. + +Mon Sep 25 23:17:10 1989 Doug Schmidt (schmidt at crimee.ics.uci.edu) + + * Decided that Obstacks had too much overhead, so they were + removed in favor of super-efficient, low-overhead buffered + storage allocation hacks in Read_Line and List_Node. + + * No longer try to inline functions that g++ complains about + (Key_List::Merge and Key_List::Merge_Sort). + +Sun Sep 24 13:11:24 1989 Doug Schmidt (schmidt at glacier.ics.uci.edu) + + * Changed classes Read_Line and List_Node to use Obstacks in order + to cache memory allocation for keyword strings and List_Nodes. + + * Continued to experiment with inheritance schemes. + + * Added a new file `alpha.h', that declares static data shared + (i.e., inherited) between classes List_Node and Key_List. + +Tue Sep 12 16:14:41 1989 Doug Schmidt (schmidt at glacier.ics.uci.edu) + + * Made numerous changes to incorporate multiple inheritance in + gperf. + +Wed Aug 16 23:04:08 1989 Doug Schmidt (schmidt at glacier.ics.uci.edu) + + * Added the -DCOMPILER_FIXED flag to the ./src/Makefile. This + implies that people trying to compile gperf need to have a + working version of the new g++ compiler (1.36.0). + + * Removed some extra spaces that were being added in the generated + C code. + +Mon Jul 24 17:09:46 1989 Doug Schmidt (schmidt at glacier.ics.uci.edu) + + * Fixed PRINT_HASH_FUNCTION and PRINT_LOOKUP_FUNCTION in keylist.c + so that the generated functions take an unsigned int length argument. + If -a is enabled the prototype is (const char *str, size_t len). + +Fri Jul 21 13:06:15 1989 Doug Schmidt (schmidt at zola.ics.uci.edu) + + * Fixed a typo in PRINT_KEYWORD_TABLE in keylist.cc that prevented + the indentation from working correctly. + + * Fixed a horrible typo in PRINT_KEYWORD_TABLE in keylist.cc + that prevented links from being printed correctly. + +Tue Jul 18 16:04:31 1989 Doug Schmidt (schmidt at zola.ics.uci.edu) + + * Fixed up readline.cc and readline.h so that they work OK + with g++ compilers that aren't completely up-to-date. + If symbol COMPILER_FIXED is defined then the behavior + that works on my more recent version of g++ is enabled. + +Sun Jul 9 17:53:28 1989 Doug Schmidt (schmidt at glacier.ics.uci.edu) + + * Changed the ./tests subdirectory Makefile so that it + uses $(CC) instead of gcc. + +Sun Jul 2 21:52:15 1989 Doug Schmidt (schmidt at glacier.ics.uci.edu) + + * Fixed a number of subtle bugs that occurred when -S was + combined with various and sundry options. + + * Added the -G option, that makes the generated keyword table + a global static variable, rather than hiding it inside + the lookup function. This allows other functions to directly + access the contents in this table. + + * Added the "#" feature, that allows comments inside the keyword + list from the input file. Comment handling takes place in readline.c. + This simplifies the code and reduces the number of malloc calls. + + * Also added the -H option (user can give the name of the hash + function) and the -T option (prevents the transfer of the type decl + to the output file, which is useful if the type is already defined + elsewhere). + +Thu Jun 22 20:39:39 1989 Doug Schmidt (schmidt at glacier.ics.uci.edu) + + * Modified many classes so that they would inherit Std_Err as + a base class. This makes things more abstract... + +Fri Jun 16 14:23:00 1989 Doug Schmidt (schmidt at zola.ics.uci.edu) + + * Modified the -f (FAST) option. This now takes an argument. + The argument corresponds to the number of iterations used + to resolve collisions. -f 0 uses the length of the + keyword list (which is what -f did before). This makes + life much easier when dealing with large keyword files. + +Tue Jun 6 17:53:27 1989 Doug Schmidt (schmidt at glacier.ics.uci.edu) + + * Added the -c (comparison) option. Enabling this + will use the strncmp function for string comparisons. + The default is to use strcmp. + + * Fixed a typo in key_list.cc (PRINT_SWITCH). This caused + faulty C code to be generated when the -D, -p, and -t + options were all enabled. + +Thu May 25 14:07:21 1989 Doug Schmidt (schmidt at siam.ics.uci.edu) + + * Once again, changed class Read_Line to overload global operator + new. Hopefully, this will work...! + +Sun May 21 01:51:45 1989 Doug Schmidt (schmidt at crimee.ics.uci.edu) + + * Modified Key_List::print_hash_function () so that it properly + formats the associated values in the hash table according to + the maximum number of digits required to represent the largest + value. + + * Removed the named return value from class Hash_Table's + operator (), since this causes a seg fault when -O is enabled. + No sense tripping subtle g++ bugs if we don't have to.... ;-) + + * Removed the operator new hack from Read_Line, since this seemed + to create horrible bus error problems. + + * Changed many class member functions and data members to be `static', + if they don't manipulate this! + +Fri May 12 23:06:56 1989 Doug Schmidt (schmidt at glacier.ics.uci.edu) + + * Changed class Std_Err to use static member functions, a la + Ada or Modula 2. This eliminates the need for an explicit + error-handler class object. + + * Added the ``named return value'' feature to Hash_Table::operator () + and Bool_Array::operator [], just for the heck of it.... ;-) + + * Changed the previous hack in Read_Line so that we now use + the overloaded global `new' instead of NEW_STRING! + +Wed May 3 17:36:55 1989 Doug Schmidt (schmidt at zola.ics.uci.edu) + + * Updated to version 1.7. This reflects the recent major changes + and the new C port. + + * Modified the GNU getopt.cc routine to have a class-based interface. + + * Fixed a typo in Perfect.cc ~Perfect that prevented the actual maximum + hash table size from being printed (maybe the stream classes + weren't so bad after all.... ;-). + + * Added support for the -f option. This generates the perfect + hash function ``fast.'' It reduces the execution time of + gperf, at the cost of minimizing the range of hash values. + +Tue May 2 16:23:29 1989 Doug Schmidt (schmidt at crimee.ics.uci.edu) + + * Added an efficiency hack to Read_Line. Instead of making + a call to operator NEW (a.k.a. malloc) for each input string + a new member function NEW_STRING stores a large buffer from + which new strings are carved out, growing the buffer if + necessary. It might be useful to add this throughout the + program.... + + * Removed all unnecessary calls to DELETE. If the program is about + to exit it is silly to waste time freeing memory. + + * Added the GNU getopt program to the distribution. This makes + GPERF portable to systems that don't include getopt in libc. + + * Added a strcspn member to class Key_List. This also increases + portability. + + * Added the get_include_src function from keylist.c as a member + function in class Key_List. Hopefully every function is + now associated with a class. This aids abstraction and + modularity. + + * Ported gperf to C. From now on both K&R C and GNU G++ versions + will be supported. There will be two ChangeLog files, one + for each version of the program. + +Mon May 1 16:41:45 1989 Doug Schmidt (schmidt at glacier.ics.uci.edu) + + * Fixed a bug with -k'*'. This now prints out *all* the cases + up to the length of the longest word in the keyword set. + +Sun Apr 30 12:15:25 1989 Doug Schmidt (schmidt at crimee.ics.uci.edu) + + * Removed all use of the stream classes. Too ugly, slow, and + not handled by the c++-mode formatter.... + + * Modified the handling of links (i.e., keywords that have + identical hash values as other keywords). This should + speed up hash function generation for keyword sets with + many duplicate entries. The trick is to treat duplicate + values as equivalence classes, so that each set of duplicate + values is represented only once in the main list processing. + + * Fixed some capitialization typos and indentations mistakes in + Key_List::print_hash_function. + +Sat Apr 29 12:04:03 1989 Doug Schmidt (schmidt at zola.ics.uci.edu) + + * Fixed a typo/logico in Key_List::print_switch that prevented + the last keyword in the keyword list to be print out. This + requires further examination..... + + * Fixed a stupid bug in List_Node::List_node. If the -k'*' option + was enabled the KEY_SET string wasn't getting terminated with + '\0'! + +Fri Apr 28 12:38:35 1989 Doug Schmidt (schmidt at glacier.ics.uci.edu) + + * Renamed strexp.h and strexp.cc to iterator.h and iterator.cc. + Also changed the strexp class to iterator. Continued to work + on style... + + * Updated the version number to 1.6. This reflects all the + recent changes. + +Thu Apr 27 00:14:51 1989 Doug Schmidt (schmidt at glacier.ics.uci.edu) + + * Added the -D option that properly handles keyword sets that + contain duplicate hash values. + + * Continued the stylistic changes. Added the #pragma once + directive to all the *.h files. Removed all #defines and + replaced them with static consts. Also moved the key_sort + routine from options.cc into the options class as a + member function. + +Mon Apr 3 13:26:55 1989 Doug Schmidt (schmidt at zola.ics.uci.edu) + + * Made massive stylistic changes to bring source code into + conformance with GNU style guidelines. + +Thu Mar 30 23:28:45 1989 Doug Schmidt (schmidt at crimee.ics.uci.edu) + + * Fixed up the output routines so that they generate code + corresponding to the GNU style guidelines. + +Sat Mar 11 13:12:37 1989 Doug Schmidt (schmidt at glacier.ics.uci.edu) + + * Fixed Stderr constructors so that they wouldn't try to + use the base class initializer syntax for the static + class variable Program_Name. G++ 1.34 is stricter in + enforcing the rules! + +Fri Mar 10 11:24:14 1989 Doug Schmidt (schmidt at glacier.ics.uci.edu) + + * Removed -v and ``| more'' from the Makefile to keep rfg happy... + +Thu Mar 2 12:37:30 1989 Doug Schmidt (schmidt at crimee.ics.uci.edu) + + * Sent latest GNU gperf version 1.5 to Doug Lea for inclusion + into libg++ 1.34. Note that there is a small bug with + the new %{ ... %} source inclusion facility, since it doesn't + understand comments and will barf if %{ or %} appear nested + inside the outermost delimiters. This is too trivial of + a defect to fix at the moment... + +Tue Feb 28 11:19:58 1989 Doug Schmidt (schmidt at glacier.ics.uci.edu) + + * Added the -K option, which allows the user to provide a + alternative name for the keyword structure component. + The default is still ``name.'' + + * Added the LEX and YACC-like ability to include arbitrary + text at the beginning of the generated C source code output. + This required two new functions Get_Special_Input, + Key_List::Save_Include_Src; + + * Fixed memory allocation bug in Key_List::Set_Types. + Variable Return_Type needs 1 additional location + to store the "*" if the -p option is used. + + * Added code to NULL terminate both Struct_Tag and Return_Type, + *after* the strncpy (stupid mistake). + +Mon Feb 27 14:39:51 1989 Doug Schmidt (schmidt at glacier.ics.uci.edu) + + * Added a new option -N. This allows the user to specify the + name to be used for the generated lookup function. The + default name is still ``in_word_set.'' This makes it + possible to completely automate the perfect hash function + generation process! + +Mon Feb 20 23:33:14 1989 Doug Schmidt (schmidt at glacier.ics.uci.edu) + + * Corrected the Hash_Table::operator () function so that + *it* is responsible for deciding when a new key has the + same signature as a previously seen key. The key length + information is now used internally to this function to + decide whether to add to the hash table those keys with + the same key sets, but different lengths. Before, this + was handled by the Key_List::Read_Keys function. However, + this failed to work for certain duplicate keys, since + they weren't being entered into the hash table properly. + +Sun Feb 19 16:02:51 1989 Doug Schmidt (schmidt at glacier.ics.uci.edu) + + * Modified class Options by moving the enum Option_Type out + of the class. This is to satisfy the new enumeration + scope rules in C++. + +Sun Jan 15 15:12:09 1989 Doug Schmidt (schmidt at crimee.ics.uci.edu) + + * Incremented the version number upto 1.4 to reflect the new + options that affect the generated code. Send the new + distribution off to Michael for use with g++ 1.33. + + * Added a fix to Key_List::Read_Keys so that it checks for links + properly when the -n option is used. Previously, it didn't + catch obvious links, which caused it to spend large amount + of time searching for a solution that could never occur! + + * Modified the Key_List data structure to record *both* the + minimum and the maximum key lengths. This information + is now computed in Key_List::Read_Keys, and thus + Key_List::Print_Min_Max doesn't need to bother. + + * Modifed the key position iterator scheme in options.cc to + eliminate the need for member function Options::Advance. + Now, the Options::Get function performs the advancement + automatically, obviating the need for an extra function call. + + * Added the new function Options::Print_Options, to print out + the user-specified command line options to generated C + output file. + + * Added a new function, Key_List::Print_Keylength_Table, + which creates a table of lengths for use in speeding + up the keyword search. This also meant that a new + option, -l (LENTABLE) is recognized. It controls + whether the length table is printed and the comparison + made in the generated function ``in_word_set.'' + + * Added a comment at the top of the generated C code + output file that tells what version of gperf was used. + Next, I'll also dump out the command line options + as a comment too. Thanks to Michael Tiemann for the + feedback on this. + + * Fixed the -n option to make it work correctly with + other parts of the program (most notably the Perfect::Hash + function and the computation of minimum and maximum lengths. + +Fri Jan 13 21:25:27 1989 Doug Schmidt (schmidt at siam.ics.uci.edu) + + * Realized the the need to add a test that will enable + optimziation of the generated C code in the ``hash'' function + by checking whether all the requested key positions are + guaranteed to exist due to the comparison in `in_word_set.'' + I'll put this in soon.... + +Thu Jan 12 20:09:21 1989 Doug Schmidt (schmidt at glacier.ics.uci.edu) + + * Added pascal, modula3, and modula2 tests inputs to the + Makefile + + * Recognised that there is a bug with the -n option. However + I'm too busy to fix it properly, right now. The problem + is that the generated #define end up being 0, since that's + my hack to make -n work. This needs complete rethinking! + +Tue Jan 10 00:08:16 1989 Doug Schmidt (schmidt at crimee.ics.uci.edu) + + * Added a new option, -n, that instructs gperf to not use the + length of an identifier when computing the hash functions. + I'm not sure how useful this is! + + * Retransmitted the distribution to rocky.oswego.edu. Hopefully, + this will work! + + * Began fixing the indentation and capitalization to conform + to the GNU coding guidelines. + +Mon Jan 9 22:23:18 1989 Doug Schmidt (schmidt at pompe.ics.uci.edu) + + * Fixed horrible bug in Read_Line::Readln_Aux. This was + a subtle and pernicous off-by-1 error, that overwrote + past the last character of the input string buffer. I + think this fault was killing the vax! + + * Yow, fixed an oversight in List_Node::List_Node, where the + pointer field Next was uninitialized. Luckily, the new routine + seems to return 0 filled objects the first time through! + +Sun Jan 8 13:43:14 1989 Doug Schmidt (schmidt at crimee.ics.uci.edu) + + * Modified the ``key linked'' diagnostic in Key_List::Read_Keys + to be more helpful and easy to read. + + * Fixed the List_Node::List_Node so that it would ignore trailing + fields if the -t option was not enabled. + + * Moved the List_Node declarations out of keylist.h and + into a file of its own, called listnode.cc and listnode.h + Made Set_Sort a member function of class List_Node. + + * Massively updated the documentation in the gperf.texinfo file. + + * Polished off the major revision to the print functions, + added a few new tests in the Makefile to check for the + validity of the program and ftp'ed the entire distribution + off to Doug Lea for libg++. ( changed it to + 1.3 to reflect the major changes with the generated + C code ). + + * Fixed Key_List::Print_Switch to deal with the -p and -t options. + This meant that the ``still-born'' function Key_List:: + Print_Type_Switch was superflous, so I removed it. + Also, removed the restriction in Option that the -p and + -t options couldn't be used simultaneously. + + * Modified List_Node::List_Node, to perform only 1 call to + ``new'' when dynamically allocating memory for the Key_Set + and the Uniq_Set. + +Sat Jan 7 14:10:51 1989 Doug Schmidt (schmidt at glacier.ics.uci.edu) + + * Fixed a big bug with the new policy of nesting the + wordlist inside of generated function ``in_word_set.'' + I'd forgotten to declare the wordlist array as static! + ( arrgh ). + + * Added a new function Key_List::Set_Types, that figures out + the return type for generated function ``in_word_set,'' + the user-defined ``struct tag,'' if one is used, and also + formates the array type for the static local array. + + * Changed the print routines to take advantage of the + new -p option. + + * Began adding the hooks to allow the return of a pointer + to a user defined struct location from the generated + ``in_word_set'' function instead of the current 0 or 1 + return value. Created function Key_List::Print_Type_Switch + and added option -p to class Option, allowing the user to + request generation of the aforementioned pointers returned + instead of booleans. + + * Put in checks in class Option to make sure that -S and -t + options are not used simultaneously. This restriction + will be removed in subsequent releases, once I decide on + a clean way to implement it. + + * Sent version 1.2 to Doug Lea for possible inclusion into + the libg++ distribution. + + * Moved the static word_list array inside the generated function + in_word_set. This supports better data hiding. + + * Added a texinfo file, gperf.texinfo + + * Revised the Makefile to cleanup the droppings from texinfo + and changed the name of gperf.cc and gperf.h to perfect.cc + and perfect.h. + +Fri Jan 6 13:04:45 1989 Doug Schmidt (schmidt at crimee.ics.uci.edu) + + * Implemented the switch statement output format. Much better + for large datasets in terms of space used. + + * Added new functions to break up the Key_List::Output function. + Functions added were Key_List::Print_Switch, + Key_List::Print_Min_Max, Key_List::Print_Keyword_Table, + Key_List::Print_Hash_Function, and + Key_List::Print_Lookup_Function. This simplifies the big mess + in Key_List::Output considerably! + + * Added switch statement option to Options, which potentially + trades time for space in the generated lookup code. + +Thu Jan 5 22:46:34 1989 Doug Schmidt (schmidt at siam.ics.uci.edu) + + * Released version 1.1 + + * Fixed a bug with Gperf::Merge_Set, it was skipping letters + shared between the Set_1 and Set_2. + + * Added the optimal min/max algorithm in Key_List::Output. This + runs in O (3n/2), rather than O (2n) time. + + * Changed Gperf::Sort_Set to use insertion sort, rather than + bubble sort. + + * Added a check in Key_List::Output for the special case where + the keys used are 1,$. It is possible to generate more + efficient C code in this case. diff --git a/apps/gperf/Makefile b/apps/gperf/Makefile new file mode 100644 index 00000000000..f9a0d4b9bd1 --- /dev/null +++ b/apps/gperf/Makefile @@ -0,0 +1,25 @@ +#---------------------------------------------------------------------------- +# @(#)Makefile 1.1 10/18/96 +# +# Makefile for the Orbix applications +#---------------------------------------------------------------------------- + +#---------------------------------------------------------------------------- +# Local macros +#---------------------------------------------------------------------------- + +INFO = README + +DIRS = src \ + tests + +#---------------------------------------------------------------------------- +# Include macros and targets +#---------------------------------------------------------------------------- + +include $(WRAPPER_ROOT)/include/makeinclude/wrapper_macros.GNU +include $(WRAPPER_ROOT)/include/makeinclude/macros.GNU +include $(WRAPPER_ROOT)/include/makeinclude/rules.common.GNU +include $(WRAPPER_ROOT)/include/makeinclude/rules.nested.GNU +include $(WRAPPER_ROOT)/include/makeinclude/rules.nolocal.GNU + diff --git a/apps/gperf/README b/apps/gperf/README new file mode 100644 index 00000000000..bd9d14ea680 --- /dev/null +++ b/apps/gperf/README @@ -0,0 +1,24 @@ +While teaching a data structures course at University of California, +Irvine, I developed a program called GPERF that generates perfect hash +functions for sets of key words. A perfect hash function is simply: + + A hash function and a data structure that allows + recognition of a key word in a set of words using + exactly 1 probe into the data structure. + +The gperf.texinfo file explains how the program works, the form of the +input, what options are available, and hints on choosing the best +options for particular key word sets. The texinfo file is readable +both via the GNU emacs `info' command, and is also suitable for +typesetting with TeX. + +The enclosed Makefile creates the executable program ``gperf'' and +also runs some tests. + +Output from the GPERF program is used to recognize reserved words in +the GNU C, GNU C++, and GNU Pascal compilers, as well as with the GNU +indent program. + +Happy hacking! + +Douglas C. Schmidt diff --git a/apps/gperf/gperf.1 b/apps/gperf/gperf.1 new file mode 100644 index 00000000000..5673c80062a --- /dev/null +++ b/apps/gperf/gperf.1 @@ -0,0 +1,23 @@ +.TH GPERF 1 "December 16, 1988 +.UC 4 +.SH NAME +gperf \- generate a perfect hash function from a key set +.SH SYNOPSIS +.B gperf +[ +.B \-adghijklnoprsStv +] [ +.I keyfile +] +.SH DESCRIPTION + +\fIgperf\fP reads a set of ``keys'' from \fIkeyfile\fP (or, by +default, from the standard input) and attempts to find a non-minimal +perfect hashing function that recognizes a member of the key set in +constant, i.e., O(1), time. If such a function is found the program +generates a pair of \fIC\fP source code routines that perform the +hashing and table lookup. All generated code is directed to the +standard output. + +Please refer to the \fIgperf.texinfo\fP file for more information. +This file is distributed with \fIgperf\fP release. diff --git a/apps/gperf/gperf.info b/apps/gperf/gperf.info new file mode 100644 index 00000000000..a0947230573 --- /dev/null +++ b/apps/gperf/gperf.info @@ -0,0 +1,1127 @@ +This is Info file gperf.info, produced by Makeinfo-1.55 from the input +file ./gperf.texi. + +START-INFO-DIR-ENTRY +* Gperf: (gperf). Perfect Hash Function Generator. +END-INFO-DIR-ENTRY + + This file documents the features of the GNU Perfect Hash Function +Generator + + Copyright (C) 1989 Free Software Foundation, Inc. + + Permission is granted to make and distribute verbatim copies of this +manual provided the copyright notice and this permission notice are +preserved on all copies. + + Permission is granted to copy and distribute modified versions of +this manual under the conditions for verbatim copying, provided also +that the section entitled "GNU General Public License" is included +exactly as in the original, and provided that the entire resulting +derived work is distributed under the terms of a permission notice +identical to this one. + + Permission is granted to copy and distribute translations of this +manual into another language, under the above conditions for modified +versions, except that the section entitled "GNU `gperf' General Public +License" an d this permission notice may be included in translations +approved by the Free Software Foundation instead of in the original +English. + + +File: gperf.info, Node: Top, Next: Copying, Prev: (dir), Up: (dir) + +Introduction +************ + + This manual documents the GNU `gperf' perfect hash function generator +utility, focusing on its features and how to use them, and how to report +bugs. + +* Menu: + +* Copying:: GNU `gperf' General Public License says + how you can copy and share `gperf'. +* Contributors:: People who have contributed to `gperf'. +* Motivation:: Static search structures and GNU GPERF. +* Search Structures:: Static search structures and GNU `gperf' +* Description:: High-level discussion of how GPERF functions. +* Options:: A description of options to the program. +* Bugs:: Known bugs and limitations with GPERF. +* Projects:: Things still left to do. +* Implementation:: Implementation Details for GNU GPERF. +* Bibliography:: Material Referenced in this Report. + + -- The Detailed Node Listing -- + +High-Level Description of GNU `gperf' + +* Input Format:: Input Format to `gperf' +* Output Format:: Output Format for Generated C Code with `gperf' + +Input Format to `gperf' + +* Declarations:: `struct' Declarations and C Code Inclusion. +* Keywords:: Format for Keyword Entries. +* Functions:: Including Additional C Functions. + + +File: gperf.info, Node: Copying, Next: Contributors, Prev: Top, Up: Top + +GNU GENERAL PUBLIC LICENSE +************************** + + Version 1, February 1989 + + Copyright (C) 1989 Free Software Foundation, Inc. + 675 Mass Ave, Cambridge, MA 02139, USA + + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +Preamble +======== + + The license agreements of most software companies try to keep users +at the mercy of those companies. By contrast, our General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. The +General Public License applies to the Free Software Foundation's +software and to any other program whose authors commit to using it. +You can use it for your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Specifically, the General Public License is designed to make +sure that you have the freedom to give away or sell copies of free +software, that you receive source code or can get it if you want it, +that you can change the software or use pieces of it in new free +programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of a such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must tell them their rights. + + We protect your rights with two steps: (1) copyright the software, +and (2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 1. This License Agreement applies to any program or other work which + contains a notice placed by the copyright holder saying it may be + distributed under the terms of this General Public License. The + "Program", below, refers to any such program or work, and a "work + based on the Program" means either the Program or any work + containing the Program or a portion of it, either verbatim or with + modifications. Each licensee is addressed as "you". + + 2. You may copy and distribute verbatim copies of the Program's source + code as you receive it, in any medium, provided that you + conspicuously and appropriately publish on each copy an + appropriate copyright notice and disclaimer of warranty; keep + intact all the notices that refer to this General Public License + and to the absence of any warranty; and give any other recipients + of the Program a copy of this General Public License along with + the Program. You may charge a fee for the physical act of + transferring a copy. + + 3. You may modify your copy or copies of the Program or any portion of + it, and copy and distribute such modifications under the terms of + Paragraph 1 above, provided that you also do the following: + + * cause the modified files to carry prominent notices stating + that you changed the files and the date of any change; and + + * cause the whole of any work that you distribute or publish, + that in whole or in part contains the Program or any part + thereof, either with or without modifications, to be licensed + at no charge to all third parties under the terms of this + General Public License (except that you may choose to grant + warranty protection to some or all third parties, at your + option). + + * If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the simplest and most usual way, to print + or display an announcement including an appropriate copyright + notice and a notice that there is no warranty (or else, + saying that you provide a warranty) and that users may + redistribute the program under these conditions, and telling + the user how to view a copy of this General Public License. + + * You may charge a fee for the physical act of transferring a + copy, and you may at your option offer warranty protection in + exchange for a fee. + + Mere aggregation of another independent work with the Program (or + its derivative) on a volume of a storage or distribution medium + does not bring the other work under the scope of these terms. + + 4. You may copy and distribute the Program (or a portion or + derivative of it, under Paragraph 2) in object code or executable + form under the terms of Paragraphs 1 and 2 above provided that you + also do one of the following: + + * accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of + Paragraphs 1 and 2 above; or, + + * accompany it with a written offer, valid for at least three + years, to give any third party free (except for a nominal + charge for the cost of distribution) a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Paragraphs 1 and 2 above; or, + + * accompany it with the information you received as to where the + corresponding source code may be obtained. (This alternative + is allowed only for noncommercial distribution and only if you + received the program in object code or executable form alone.) + + Source code for a work means the preferred form of the work for + making modifications to it. For an executable file, complete + source code means all the source code for all modules it contains; + but, as a special exception, it need not include source code for + modules which are standard libraries that accompany the operating + system on which the executable file runs, or for standard header + files or definitions files that accompany that operating system. + + 5. You may not copy, modify, sublicense, distribute or transfer the + Program except as expressly provided under this General Public + License. Any attempt otherwise to copy, modify, sublicense, + distribute or transfer the Program is void, and will automatically + terminate your rights to use the Program under this License. + However, parties who have received copies, or rights to use + copies, from you under this General Public License will not have + their licenses terminated so long as such parties remain in full + compliance. + + 6. By copying, distributing or modifying the Program (or any work + based on the Program) you indicate your acceptance of this license + to do so, and all its terms and conditions. + + 7. Each time you redistribute the Program (or any work based on the + Program), the recipient automatically receives a license from the + original licensor to copy, distribute or modify the Program + subject to these terms and conditions. You may not impose any + further restrictions on the recipients' exercise of the rights + granted herein. + + 8. The Free Software Foundation may publish revised and/or new + versions of the General Public License from time to time. Such + new versions will be similar in spirit to the present version, but + may differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the + Program specifies a version number of the license which applies to + it and "any later version", you have the option of following the + terms and conditions either of that version or of any later + version published by the Free Software Foundation. If the Program + does not specify a version number of the license, you may choose + any version ever published by the Free Software Foundation. + + 9. If you wish to incorporate parts of the Program into other free + programs whose distribution conditions are different, write to the + author to ask for permission. For software which is copyrighted + by the Free Software Foundation, write to the Free Software + Foundation; we sometimes make exceptions for this. Our decision + will be guided by the two goals of preserving the free status of + all derivatives of our free software and of promoting the sharing + and reuse of software generally. + + NO WARRANTY + + 10. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO + WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE + LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT + HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT + WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT + NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE + QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE + PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY + SERVICING, REPAIR OR CORRECTION. + + 11. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN + WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY + MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE + LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, + INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR + INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF + DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU + OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY + OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN + ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + +Appendix: How to Apply These Terms to Your New Programs +======================================================= + + If you develop a new program, and you want it to be of the greatest +possible use to humanity, the best way to achieve this is to make it +free software which everyone can redistribute and change under these +terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + ONE LINE TO GIVE THE PROGRAM'S NAME AND A BRIEF IDEA OF WHAT IT DOES. + Copyright (C) 19YY NAME OF AUTHOR + + 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 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Also add information on how to contact you by electronic and paper +mail. + + If the program is interactive, make it output a short notice like +this when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19YY NAME OF AUTHOR + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + + The hypothetical commands `show w' and `show c' should show the +appropriate parts of the General Public License. Of course, the +commands you use may be called something other than `show w' and `show +c'; they could even be mouse-clicks or menu items--whatever suits your +program. + + You should also get your employer (if you work as a programmer) or +your school, if any, to sign a "copyright disclaimer" for the program, +if necessary. Here a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + program `Gnomovision' (a program to direct compilers to make passes + at assemblers) written by James Hacker. + + SIGNATURE OF TY COON, 1 April 1989 + Ty Coon, President of Vice + + That's all there is to it! + + +File: gperf.info, Node: Contributors, Next: Motivation, Prev: Copying, Up: Top + +Contributors to GNU `gperf' Utility +*********************************** + + * The GNU `gperf' perfect hash function generator utility was + originally written in GNU C++ by Douglas C. Schmidt. It is now + also available in a highly-portable "old-style" C version. The + general idea for the perfect hash function generator was inspired + by Keith Bostic's algorithm written in C, and distributed to + net.sources around 1984. The current program is a heavily + modified, enhanced, and extended implementation of Keith's basic + idea, created at the University of California, Irvine. Bugs, + patches, and suggestions should be reported to schmidt at + ics.uci.edu. + + * Special thanks is extended to Michael Tiemann and Doug Lea, for + providing a useful compiler, and for giving me a forum to exhibit + my creation. + + In addition, Adam de Boor and Nels Olson provided many tips and + insights that greatly helped improve the quality and functionality + of `gperf'. + + +File: gperf.info, Node: Motivation, Next: Search Structures, Prev: Contributors, Up: Top + +Introduction +************ + + `gperf' is a perfect hash function generator written in C++. It +transforms an *n* element user-specified keyword set *W* into a perfect +hash function *F*. *F* uniquely maps keywords in *W* onto the range +0..*k*, where *k* >= *n*. If *k = n* then *F* is a *minimal* perfect +hash function. `gperf' generates a 0..*k* element static lookup table +and a pair of C functions. These functions determine whether a given +character string *s* occurs in *W*, using at most one probe into the +lookup table. + + `gperf' currently generates the reserved keyword recognizer for +lexical analyzers in several production and research compilers and +language processing tools, including GNU C, GNU C++, GNU Pascal, GNU +Modula 3, and GNU indent. Complete C++ source code for `gperf' is +available via anonymous ftp from ics.uci.edu. `gperf' also is +distributed along with the GNU libg++ library. A highly portable, +functionally equivalent K&R C version of `gperf' is archived in +comp.sources.unix, volume 20. Finally, a paper describing `gperf''s +design and implementation in greater detail is available in the Second +USENIX C++ Conference proceedings. + + +File: gperf.info, Node: Search Structures, Next: Description, Prev: Motivation, Up: Top + +Static search structures and GNU `gperf' +**************************************** + + A "static search structure" is an Abstract Data Type with certain +fundamental operations, *e.g.*, *initialize*, *insert*, and *retrieve*. +Conceptually, all insertions occur before any retrievals. In +practice, `gperf' generates a `static' array containing search set +keywords and any associated attributes specified by the user. Thus, +there is essentially no execution-time cost for the insertions. It is +a useful data structure for representing *static search sets*. Static +search sets occur frequently in software system applications. Typical +static search sets include compiler reserved words, assembler +instruction opcodes, and built-in shell interpreter commands. Search +set members, called "keywords", are inserted into the structure only +once, usually during program initialization, and are not generally +modified at run-time. + + Numerous static search structure implementations exist, *e.g.*, +arrays, linked lists, binary search trees, digital search tries, and +hash tables. Different approaches offer trade-offs between space +utilization and search time efficiency. For example, an *n* element +sorted array is space efficient, though the average-case time +complexity for retrieval operations using binary search is proportional +to log *n*. Conversely, hash table implementations often locate a +table entry in constant time, but typically impose additional memory +overhead and exhibit poor worst case performance. + + *Minimal perfect hash functions* provide an optimal solution for a +particular class of static search sets. A minimal perfect hash +function is defined by two properties: + + * It allows keyword recognition in a static search set using at most + *one* probe into the hash table. This represents the "perfect" + property. + + * The actual memory allocated to store the keywords is precisely + large enough for the keyword set, and *no larger*. This is the + "minimal" property. + + For most applications it is far easier to generate *perfect* hash +functions than *minimal perfect* hash functions. Moreover, non-minimal +perfect hash functions frequently execute faster than minimal ones in +practice. This phenomena occurs since searching a sparse keyword table +increases the probability of locating a "null" entry, thereby reducing +string comparisons. `gperf''s default behavior generates +*near-minimal* perfect hash functions for keyword sets. However, +`gperf' provides many options that permit user control over the degree +of minimality and perfection. + + Static search sets often exhibit relative stability over time. For +example, Ada's 63 reserved words have remained constant for nearly a +decade. It is therefore frequently worthwhile to expend concerted +effort building an optimal search structure *once*, if it subsequently +receives heavy use multiple times. `gperf' removes the drudgery +associated with constructing time- and space-efficient search +structures by hand. It has proven a useful and practical tool for +serious programming projects. Output from `gperf' is currently used in +several production and research compilers, including GNU C, GNU C++, +GNU Pascal, and GNU Modula 3. The latter two compilers are not yet +part of the official GNU distribution. Each compiler utilizes `gperf' +to automatically generate static search structures that efficiently +identify their respective reserved keywords. + + +File: gperf.info, Node: Description, Next: Options, Prev: Search Structures, Up: Top + +High-Level Description of GNU `gperf' +************************************* + +* Menu: + +* Input Format:: Input Format to `gperf' +* Output Format:: Output Format for Generated C Code with `gperf' + + The perfect hash function generator `gperf' reads a set of +"keywords" from a "keyfile" (or from the standard input by default). +It attempts to derive a perfect hashing function that recognizes a +member of the "static keyword set" with at most a single probe into the +lookup table. If `gperf' succeeds in generating such a function it +produces a pair of C source code routines that perform hashing and +table lookup recognition. All generated C code is directed to the +standard output. Command-line options described below allow you to +modify the input and output format to `gperf'. + + By default, `gperf' attempts to produce time-efficient code, with +less emphasis on efficient space utilization. However, several options +exist that permit trading-off execution time for storage space and vice +versa. In particular, expanding the generated table size produces a +sparse search structure, generally yielding faster searches. +Conversely, you can direct `gperf' to utilize a C `switch' statement +scheme that minimizes data space storage size. Furthermore, using a C +`switch' may actually speed up the keyword retrieval time somewhat. +Actual results depend on your C compiler, of course. + + In general, `gperf' assigns values to the characters it is using for +hashing until some set of values gives each keyword a unique value. A +helpful heuristic is that the larger the hash value range, the easier +it is for `gperf' to find and generate a perfect hash function. +Experimentation is the key to getting the most from `gperf'. + + +File: gperf.info, Node: Input Format, Next: Output Format, Prev: Description, Up: Description + +Input Format to `gperf' +======================= + + You can control the input keyfile format by varying certain +command-line arguments, in particular the `-t' option. The input's +appearance is similar to GNU utilities `flex' and `bison' (or UNIX +utilities `lex' and `yacc'). Here's an outline of the general format: + + declarations + %% + keywords + %% + functions + + *Unlike* `flex' or `bison', all sections of `gperf''s input are +optional. The following sections describe the input format for each +section. + +* Menu: + +* Declarations:: `struct' Declarations and C Code Inclusion. +* Keywords:: Format for Keyword Entries. +* Functions:: Including Additional C Functions. + + +File: gperf.info, Node: Declarations, Next: Keywords, Prev: Input Format, Up: Input Format + +`struct' Declarations and C Code Inclusion +------------------------------------------ + + The keyword input file optionally contains a section for including +arbitrary C declarations and definitions, as well as provisions for +providing a user-supplied `struct'. If the `-t' option *is* enabled, +you *must* provide a C `struct' as the last component in the +declaration section from the keyfile file. The first field in this +struct must be a `char *' identifier called "name," although it is +possible to modify this field's name with the `-K' option described +below. + + Here is simple example, using months of the year and their +attributes as input: + + struct months { char *name; int number; int days; int leap_days; }; + %% + january, 1, 31, 31 + february, 2, 28, 29 + march, 3, 31, 31 + april, 4, 30, 30 + may, 5, 31, 31 + june, 6, 30, 30 + july, 7, 31, 31 + august, 8, 31, 31 + september, 9, 30, 30 + october, 10, 31, 31 + november, 11, 30, 30 + december, 12, 31, 31 + + Separating the `struct' declaration from the list of key words and +other fields are a pair of consecutive percent signs, `%%', appearing +left justified in the first column, as in the UNIX utility `lex'. + + Using a syntax similar to GNU utilities `flex' and `bison', it is +possible to directly include C source text and comments verbatim into +the generated output file. This is accomplished by enclosing the region +inside left-justified surrounding `%{', `%}' pairs. Here is an input +fragment based on the previous example that illustrates this feature: + + %{ + #include <assert.h> + /* This section of code is inserted directly into the output. */ + int return_month_days (struct months *months, int is_leap_year); + %} + struct months { char *name; int number; int days; int leap_days; }; + %% + january, 1, 31, 31 + february, 2, 28, 29 + march, 3, 31, 31 + ... + + It is possible to omit the declaration section entirely. In this +case the keyfile begins directly with the first keyword line, *e.g.*: + + january, 1, 31, 31 + february, 2, 28, 29 + march, 3, 31, 31 + april, 4, 30, 30 + ... + + +File: gperf.info, Node: Keywords, Next: Functions, Prev: Declarations, Up: Input Format + +Format for Keyword Entries +-------------------------- + + The second keyfile format section contains lines of keywords and any +associated attributes you might supply. A line beginning with `#' in +the first column is considered a comment. Everything following the `#' +is ignored, up to and including the following newline. + + The first field of each non-comment line is always the key itself. +It should be given as a simple name, *i.e.*, without surrounding string +quotation marks, and be left-justified flush against the first column. +In this context, a "field" is considered to extend up to, but not +include, the first blank, comma, or newline. Here is a simple example +taken from a partial list of C reserved words: + + # These are a few C reserved words, see the c.`gperf' file + # for a complete list of ANSI C reserved words. + unsigned + sizeof + switch + signed + if + default + for + while + return + + Note that unlike `flex' or `bison' the first `%%' marker may be +elided if the declaration section is empty. + + Additional fields may optionally follow the leading keyword. Fields +should be separated by commas, and terminate at the end of line. What +these fields mean is entirely up to you; they are used to initialize the +elements of the user-defined `struct' provided by you in the +declaration section. If the `-t' option is *not* enabled these fields +are simply ignored. All previous examples except the last one contain +keyword attributes. + + +File: gperf.info, Node: Functions, Prev: Keywords, Up: Input Format + +Including Additional C Functions +-------------------------------- + + The optional third section also corresponds closely with conventions +found in `flex' and `bison'. All text in this section, starting at the +final `%%' and extending to the end of the input file, is included +verbatim into the generated output file. Naturally, it is your +responsibility to ensure that the code contained in this section is +valid C. + + +File: gperf.info, Node: Output Format, Prev: Input Format, Up: Description + +Output Format for Generated C Code with `gperf' +=============================================== + + Several options control how the generated C code appears on the +standard output. Two C function are generated. They are called `hash' +and `in_word_set', although you may modify the name for `in_word_set' +with a command-line option. Both functions require two arguments, a +string, `char *' STR, and a length parameter, `int' LEN. Their default +function prototypes are as follows: + + static int hash (char *str, int len); + int in_word_set (char *str, int len); + + By default, the generated `hash' function returns an integer value +created by adding LEN to several user-specified STR key positions +indexed into an "associated values" table stored in a local static +array. The associated values table is constructed internally by +`gperf' and later output as a static local C array called HASH_TABLE; +its meaning and properties are described below. *Note +Implementation::. The relevant key positions are specified via the `-k' +option when running `gperf', as detailed in the *Options* section +below. *Note Options::. + + Two options, `-g' (assume you are compiling with GNU C and its +`inline' feature) and `-a' (assume ANSI C-style function prototypes), +alter the content of both the generated `hash' and `in_word_set' +routines. However, function `in_word_set' may be modified more +extensively, in response to your option settings. The options that +affect the `in_word_set' structure are: + + `-p' + Have function `in_word_set' return a pointer rather than a + boolean. + + `-t' + Make use of the user-defined `struct'. + + `-S TOTAL SWITCH STATEMENTS' + Generate 1 or more C `switch' statement rather than use a + large, (and potentially sparse) static array. Although the + exact time and space savings of this approach vary according + to your C compiler's degree of optimization, this method + often results in smaller and faster code. + + If the `-t', `-S', and `-p' options are omitted the default action +is to generate a `char *' array containing the keys, together with +additional null strings used for padding the array. By experimenting +with the various input and output options, and timing the resulting C +code, you can determine the best option choices for different keyword +set characteristics. + + +File: gperf.info, Node: Options, Next: Bugs, Prev: Description, Up: Top + +Options to the `gperf' Utility +****************************** + + There are *many* options to `gperf'. They were added to make the +program more convenient for use with real applications. "On-line" help +is readily available via the `-h' option. Other options include: + + `-a' + Generate ANSI Standard C code using function prototypes. The + default is to use "classic" K&R C function declaration syntax. + + `-c' + Generates C code that uses the `strncmp' function to perform + string comparisons. The default action is to use `strcmp'. + + `-C' + Makes the contents of all generated lookup tables constant, + *i.e.*, "readonly." Many compilers can generate more + efficient code for this by putting the tables in readonly + memory. + + `-d' + Enables the debugging option. This produces verbose + diagnostics to "standard error" when `gperf' is executing. + It is useful both for maintaining the program and for + determining whether a given set of options is actually + speeding up the search for a solution. Some useful + information is dumped at the end of the program when the `-d' + option is enabled. + + `-D' + Handle keywords whose key position sets hash to duplicate + values. Duplicate hash values occur for two reasons: + + * Since `gperf' does not backtrack it is possible for it + to process all your input keywords without finding a + unique mapping for each word. However, frequently only + a very small number of duplicates occur, and the + majority of keys still require one probe into the table. + + * Sometimes a set of keys may have the same names, but + possess different attributes. With the -D option + `gperf' treats all these keys as part of an equivalence + class and generates a perfect hash function with multiple + comparisons for duplicate keys. It is up to you to + completely disambiguate the keywords by modifying the + generated C code. However, `gperf' helps you out by + organizing the output. + + Option `-D' is extremely useful for certain large or highly + redundant keyword sets, *i.e.*, assembler instruction opcodes. + Using this option usually means that the generated hash + function is no longer perfect. On the other hand, it permits + `gperf' to work on keyword sets that it otherwise could not + handle. + + `-e KEYWORD DELIMITER LIST' + Allows the user to provide a string containing delimiters + used to separate keywords from their attributes. The default + is ",\n". This option is essential if you want to use + keywords that have embedded commas or newlines. One useful + trick is to use -e'TAB', where TAB is the literal tab + character. + + `-E' + Define constant values using an enum local to the lookup + function rather than with #defines. This also means that + different lookup functions can reside in the same file. + Thanks to James Clark (jjc at ai.mit.edu). + + `-f ITERATION AMOUNT' + Generate the perfect hash function "fast." This decreases + `gperf''s running time at the cost of minimizing generated + table-size. The iteration amount represents the number of + times to iterate when resolving a collision. `0' means + `iterate by the number of keywords. This option is probably + most useful when used in conjunction with options `-D' and/or + `-S' for *large* keyword sets. + + `-g' + Assume a GNU compiler, *e.g.*, `g++' or `gcc'. This makes + all generated routines use the "inline" keyword to remove the + cost of function calls. Note that `-g' does *not* imply + `-a', since other non-ANSI C compilers may have provisions + for a function `inline' feature. + + `-G' + Generate the static table of keywords as a static global + variable, rather than hiding it inside of the lookup function + (which is the default behavior). + + `-h' + Prints a short summary on the meaning of each program option. + Aborts further program execution. + + `-H HASH FUNCTION NAME' + Allows you to specify the name for the generated hash + function. Default name is `hash.' This option permits the + use of two hash tables in the same file. + + `-i INITIAL VALUE' + Provides an initial VALUE for the associate values array. + Default is 0. Increasing the initial value helps inflate the + final table size, possibly leading to more time efficient + keyword lookups. Note that this option is not particularly + useful when `-S' is used. Also, `-i' is overriden when the + `-r' option is used. + + `-j JUMP VALUE' + Affects the "jump value," *i.e.*, how far to advance the + associated character value upon collisions. JUMP VALUE is + rounded up to an odd number, the default is 5. If the JUMP + VALUE is 0 `gper f' jumps by random amounts. + + `-k KEYS' + Allows selection of the character key positions used in the + keywords' hash function. The allowable choices range between + 1-126, inclusive. The positions are separated by commas, + *e.g.*, `-k 9,4,13,14'; ranges may be used, *e.g.*, `-k 2-7'; + and positions may occur in any order. Furthermore, the + meta-character '*' causes the generated hash function to + consider *all* character positions in each key, whereas '$' + instructs the hash function to use the "final character" of a + key (this is the only way to use a character position greater + than 126, incidentally). + + For instance, the option `-k 1,2,4,6-10,'$'' generates a hash + function that considers positions 1,2,4,6,7,8,9,10, plus the + last character in each key (which may differ for each key, + obviously). Keys with length less than the indicated key + positions work properly, since selected key positions + exceeding the key length are simply not referenced in the + hash function. + + `-K KEY NAME' + By default, the program assumes the structure component + identifier for the keyword is "name." This option allows an + arbitrary choice of identifier for this component, although + it still must occur as the first field in your supplied + `struct'. + + `-l' + Compare key lengths before trying a string comparison. This + might cut down on the number of string comparisons made + during the lookup, since keys with different lengths are + never compared via `strcmp'. However, using `-l' might + greatly increase the size of the generated C code if the + lookup table range is large (which implies that the switch + option `-S' is not enabled), since the length table contains + as many elements as there are entries in the lookup table. + + `-L GENERATED LANGUAGE NAME' + Instructs `gperf' to generate code in the language specified + by the option's argument. Languages handled are currently + C++ and C. The default is C. + + `-n' + Instructs the generator not to include the length of a + keyword when computing its hash value. This may save a few + assembly instructions in the generated lookup table. + + `-N LOOKUP FUNCTION NAME' + Allows you to specify the name for the generated lookup + function. Default name is `in_word_set.' This option + permits completely automatic generation of perfect hash + functions, especially when multiple generated hash functions + are used in the same application. + + `-o' + Reorders the keywords by sorting the keywords so that + frequently occuring key position set components appear first. + A second reordering pass follows so that keys with "already + determined values" are placed towards the front of the + keylist. This may decrease the time required to generate a + perfect hash function for many keyword sets, and also produce + more minimal perfect hash functions. The reason for this is + that the reordering helps prune the search time by handling + inevitable collisions early in the search process. On the + other hand, if the number of keywords is *very* large using + `-o' may *increase* `gperf''s execution time, since + collisions will begin earlier and continue throughout the + remainder of keyword processing. See Cichelli's paper from + the January 1980 Communications of the ACM for details. + + `-p' + Changes the return value of the generated function + `in_word_set' from boolean (*i.e.*, 0 or 1), to either type + "pointer to user-defined struct," (if the `-t' option is + enabled), or simply to `char *', if `-t' is not enabled. + This option is most useful when the `-t' option (allowing + user-defined structs) is used. For example, it is possible + to automatically generate the GNU C reserved word lookup + routine with the options `-p' and `-t'. + + `-r' + Utilizes randomness to initialize the associated values + table. This frequently generates solutions faster than using + deterministic initialization (which starts all associated + values at 0). Furthermore, using the randomization option + generally increases the size of the table. If `gperf' has + difficultly with a certain keyword set try using `-r' or `-D'. + + `-s SIZE-MULTIPLE' + Affects the size of the generated hash table. The numeric + argument for this option indicates "how many times larger or + smaller" the maximum associated value range should be, in + relationship to the number of keys. If the SIZE-MULTIPLE is + negative the maximum associated value is calculated by + *dividing* it into the total number of keys. For example, a + value of 3 means "allow the maximum associated value to be + about 3 times larger than the number of input keys." + + Conversely, a value of -3 means "allow the maximum associated + value to be about 3 times smaller than the number of input + keys." Negative values are useful for limiting the overall + size of the generated hash table, though this usually + increases the number of duplicate hash values. + + If `generate switch' option `-S' is *not* enabled, the maximum + associated value influences the static array table size, and + a larger table should decrease the time required for an + unsuccessful search, at the expense of extra table space. + + The default value is 1, thus the default maximum associated + value about the same size as the number of keys (for + efficiency, the maximum associated value is always rounded up + to a power of 2). The actual table size may vary somewhat, + since this technique is essentially a heuristic. In + particular, setting this value too high slows down `gperf''s + runtime, since it must search through a much larger range of + values. Judicious use of the `-f' option helps alleviate this + overhead, however. + + `-S TOTAL SWITCH STATEMENTS' + Causes the generated C code to use a `switch' statement + scheme, rather than an array lookup table. This can lead to + a reduction in both time and space requirements for some + keyfiles. The argument to this option determines how many + `switch' statements are generated. A value of 1 generates 1 + `switch' containing all the elements, a value of 2 generates + 2 tables with 1/2 the elements in each `switch', etc. This + is useful since many C compilers cannot correctly generate + code for large `switch' statements. This option was inspired + in part by Keith Bostic's original C program. + + `-t' + Allows you to include a `struct' type declaration for + generated code. Any text before a pair of consecutive %% is + consider part of the type declaration. Key words and + additional fields may follow this, one group of fields per + line. A set of examples for generating perfect hash tables + and functions for Ada, C, and G++, Pascal, and Modula 2 and 3 + reserved words are distributed with this release. + + `-T' + Prevents the transfer of the type declaration to the output + file. Use this option if the type is already defined + elsewhere. + + `-v' + Prints out the current version number. + + `-Z CLASS NAME' + Allow user to specify name of generated C++ class. Default + name is `Perfect_Hash'. + + +File: gperf.info, Node: Bugs, Next: Projects, Prev: Options, Up: Top + +Known Bugs and Limitations with `gperf' +*************************************** + + The following are some limitations with the current release of +`gperf': + + * The `gperf' utility is tuned to execute quickly, and works quickly + for small to medium size data sets (around 1000 keywords). It is + extremely useful for maintaining perfect hash functions for + compiler keyword sets. Several recent enhancements now enable + `gperf' to work efficiently on much larger keyword sets (over + 15,000 keywords). When processing large keyword sets it helps + greatly to have over 8 megs of RAM. + + However, since `gperf' does not backtrack no guaranteed solution + occurs on every run. On the other hand, it is usually easy to + obtain a solution by varying the option parameters. In + particular, try the `-r' option, and also try changing the default + arguments to the `-s' and `-j' options. To *guarantee* a + solution, use the `-D' and `-S' options, although the final + results are not likely to be a *perfect* hash function anymore! + Finally, use the `-f' option if you want `gperf' to generate the + perfect hash function *fast*, with less emphasis on making it + minimal. + + * The size of the generate static keyword array can get *extremely* + large if the input keyword file is large or if the keywords are + quite similar. This tends to slow down the compilation of the + generated C code, and *greatly* inflates the object code size. If + this situation occurs, consider using the `-S' option to reduce + data size, potentially increasing keyword recognition time a + negligible amount. Since many C compilers cannot correctly + generated code for large switch statements it is important to + qualify the -S option with an appropriate numerical argument that + controls the number of switch statements generated. + + * The maximum number of key positions selected for a given key has an + arbitrary limit of 126. This restriction should be removed, and if + anyone considers this a problem write me and let me know so I can + remove the constraint. + + * The C++ source code only compiles correctly with GNU G++, version + 1.36 (and hopefully later versions). Porting to AT&T cfront would + be tedious, but possible (and desirable). There is also a K&R C + version available now. This should compile without change on most + BSD systems, but may require a bit of work to run on SYSV, since + `gperf' uses ALLOCA in several places. Send mail to schmidt at + ics.uci.edu for information. + + +File: gperf.info, Node: Projects, Next: Implementation, Prev: Bugs, Up: Top + +Things Still Left to Do +*********************** + + It should be "relatively" easy to replace the current perfect hash +function algorithm with a more exhaustive approach; the perfect hash +module is essential independent from other program modules. Additional +worthwhile improvements include: + + * Make the algorithm more robust. At present, the program halts + with an error diagnostic if it can't find a direct solution and + the `-D' option is not enabled. A more comprehensive, albeit + computationally expensive, approach would employ backtracking or + enable alternative options and retry. It's not clear how helpful + this would be, in general, since most search sets are rather small + in practice. + + * Another useful extension involves modifying the program to generate + "minimal" perfect hash functions (under certain circumstances, the + current version can be rather extravagant in the generated table + size). Again, this is mostly of theoretical interest, since a + sparse table often produces faster lookups, and use of the `-S' + `switch' option can minimize the data size, at the expense of + slightly longer lookups (note that the gcc compiler generally + produces good code for `switch' statements, reducing the need for + more complex schemes). + + * In addition to improving the algorithm, it would also be useful to + generate a C++ class or Ada package as the code output, in + addition to the current C routines. + + +File: gperf.info, Node: Implementation, Next: Bibliography, Prev: Projects, Up: Top + +Implementation Details of GNU `gperf' +************************************* + + A paper describing the high-level description of the data structures +and algorithms used to implement `gperf' will soon be available. This +paper is useful not only from a maintenance and enhancement perspective, +but also because they demonstrate several clever and useful programming +techniques, *e.g.*, `Iteration Number' boolean arrays, double hashing, +a "safe" and efficient method for reading arbitrarily long input from a +file, and a provably optimal algorithm for simultaneously determining +both the minimum and maximum elements in a list. + + +File: gperf.info, Node: Bibliography, Prev: Implementation, Up: Top + +Bibliography +************ + + [1] Chang, C.C.: A Scheme for Constructing Ordered Minimal Perfect +Hashing Functions Information Sciences 39(1986), 187-195. + + [2] Cichelli, Richard J. Author's Response to "On Cichelli's Minimal +Perfec t Hash Functions Method" Communications of the ACM, 23, +12(December 1980), 729. + + [3] Cichelli, Richard J. Minimal Perfect Hash Functions Made Simple +Communications of the ACM, 23, 1(January 1980), 17-19. + + [4] Cook, C. R. and Oldehoeft, R.R. A Letter Oriented Minimal +Perfect Hashing Function SIGPLAN Notices, 17, 9(September 1982), 18-27. + + [5] Cormack, G. V. and Horspool, R. N. S. and Kaiserwerth, M. +Practical Perfect Hashing Computer Journal, 28, 1(January 1985), 54-58. + + [6] Jaeschke, G. Reciprocal Hashing: A Method for Generating Minimal +Perfect Hashing Functions Communications of the ACM, 24, 12(December +1981), 829-833. + + [7] Jaeschke, G. and Osterburg, G. On Cichelli's Minimal Perfect +Hash Functions Method Communications of the ACM, 23, 12(December 1980), +728-729. + + [8] Sager, Thomas J. A Polynomial Time Generator for Minimal Perfect +Hash Functions Communications of the ACM, 28, 5(December 1985), 523-532 + + [9] Schmidt, Douglas C. GPERF: A Perfect Hash Function Generator +Second USENIX C++ Conference Proceedings, April 1990. + + [10] Sebesta, R.W. and Taylor, M.A. Minimal Perfect Hash Functions +for Reserved Word Lists SIGPLAN Notices, 20, 12(September 1985), 47-53. + + [11] Sprugnoli, R. Perfect Hashing Functions: A Single Probe +Retrieving Method for Static Sets Communications of the ACM, 20 +11(November 1977), 841-850. + + [12] Stallman, Richard M. Using and Porting GNU CC Free Software +Foundation, 1988. + + [13] Stroustrup, Bjarne The C++ Programming Language. +Addison-Wesley, 1986. + + [14] Tiemann, Michael D. User's Guide to GNU C++ Free Software +Foundation, 1989. + + + +Tag Table: +Node: Top1218 +Node: Copying2456 +Node: Contributors15759 +Node: Motivation16859 +Node: Search Structures18126 +Node: Description21679 +Node: Input Format23499 +Node: Declarations24294 +Node: Keywords26601 +Node: Functions28192 +Node: Output Format28686 +Node: Options31156 +Node: Bugs44526 +Node: Projects47213 +Node: Implementation48790 +Node: Bibliography49509 + +End Tag Table diff --git a/apps/gperf/gperf.texi b/apps/gperf/gperf.texi new file mode 100644 index 00000000000..649d05f7ec6 --- /dev/null +++ b/apps/gperf/gperf.texi @@ -0,0 +1,1184 @@ +\input texinfo @c -*-texinfo-*- + +@settitle User's Guide to @code{gperf} +@setfilename gperf.info + +@ifinfo +@format +START-INFO-DIR-ENTRY +* Gperf: (gperf). Perfect Hash Function Generator. +END-INFO-DIR-ENTRY +@end format +@end ifinfo + +@ifinfo +This file documents the features of the GNU Perfect Hash Function Generator + +Copyright (C) 1989 Free Software Foundation, Inc. + +Permission is granted to make and distribute verbatim copies of +this manual provided the copyright notice and this permission notice +are preserved on all copies. + +@ignore +Permission is granted to process this file through @TeX{} and print the +results, provided the printed document carries copying permission +notice identical to this one except for the removal of this paragraph +(this paragraph not being relevant to the printed manual). + +@end ignore + +Permission is granted to copy and distribute modified versions of this +manual under the conditions for verbatim copying, provided also that the +section entitled ``GNU General Public License'' is included exactly as +in the original, and provided that the entire resulting derived work is +distributed under the terms of a permission notice identical to this one. + +Permission is granted to copy and distribute translations of this manual +into another language, under the above conditions for modified versions, +except that the section entitled ``GNU @code{gperf} General Public License'' an +d +this permission notice may be included in translations approved by the +Free Software Foundation instead of in the original English. +@end ifinfo + +@setchapternewpage odd + +@titlepage +@center @titlefont{User's Guide} +@sp 2 +@center @titlefont{for the} +@sp 2 +@center @titlefont{GNU GPERF Utility} +@sp 4 +@center Douglas C. Schmidt +@sp 3 +@center last updated 1 November 1989 +@sp 1 +@center for version 2.0 +@page +@vskip 0pt plus 1filll +Copyright @copyright{} 1989 Free Software Foundation, Inc. + + +Permission is granted to make and distribute verbatim copies of +this manual provided the copyright notice and this permission notice +are preserved on all copies. + +Permission is granted to copy and distribute modified versions of this +manual under the conditions for verbatim copying, provided also that the +section entitled ``GNU @code{gperf} General Public License'' is included exactl +y as +in the original, and provided that the entire resulting derived work is +distributed under the terms of a permission notice identical to this one. + +Permission is granted to copy and distribute translations of this manual +into another language, under the above conditions for modified versions, +except that the section entitled ``GNU @code{gperf} General Public License'' ma +y be +included in a translation approved by the author instead of in the original +English. +@end titlepage + +@ifinfo +@node Top, Copying, (dir), (dir) +@ichapter Introduction + +This manual documents the GNU @code{gperf} perfect hash function generator +utility, focusing on its features and how to use them, and how to report +bugs. + +@end ifinfo +@menu +* Copying:: GNU @code{gperf} General Public License says + how you can copy and share @code{gperf}. +* Contributors:: People who have contributed to @code{gperf}. +* Motivation:: Static search structures and GNU GPERF. +* Search Structures:: Static search structures and GNU @code{gperf} +* Description:: High-level discussion of how GPERF functions. +* Options:: A description of options to the program. +* Bugs:: Known bugs and limitations with GPERF. +* Projects:: Things still left to do. +* Implementation:: Implementation Details for GNU GPERF. +* Bibliography:: Material Referenced in this Report. + + --- The Detailed Node Listing --- + +High-Level Description of GNU @code{gperf} + +* Input Format:: Input Format to @code{gperf} +* Output Format:: Output Format for Generated C Code with @code{gperf} + +Input Format to @code{gperf} + +* Declarations:: @code{struct} Declarations and C Code Inclusion. +* Keywords:: Format for Keyword Entries. +* Functions:: Including Additional C Functions. +@end menu + +@node Copying, Contributors, Top, Top +@unnumbered GNU GENERAL PUBLIC LICENSE +@center Version 1, February 1989 + +@display +Copyright @copyright{} 1989 Free Software Foundation, Inc. +675 Mass Ave, Cambridge, MA 02139, USA + +Everyone is permitted to copy and distribute verbatim copies +of this license document, but changing it is not allowed. +@end display + +@unnumberedsec Preamble + + The license agreements of most software companies try to keep users +at the mercy of those companies. By contrast, our General Public +License is intended to guarantee your freedom to share and change free +software---to make sure the software is free for all its users. The +General Public License applies to the Free Software Foundation's +software and to any other program whose authors commit to using it. +You can use it for your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Specifically, the General Public License is designed to make +sure that you have the freedom to give away or sell copies of free +software, that you receive source code or can get it if you want it, +that you can change the software or use pieces of it in new free +programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of a such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must tell them their rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + The precise terms and conditions for copying, distribution and +modification follow. + +@iftex +@unnumberedsec TERMS AND CONDITIONS +@end iftex +@ifinfo +@center TERMS AND CONDITIONS +@end ifinfo + +@enumerate +@item +This License Agreement applies to any program or other work which +contains a notice placed by the copyright holder saying it may be +distributed under the terms of this General Public License. The +``Program'', below, refers to any such program or work, and a ``work based +on the Program'' means either the Program or any work containing the +Program or a portion of it, either verbatim or with modifications. Each +licensee is addressed as ``you''. + +@item +You may copy and distribute verbatim copies of the Program's source +code as you receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice and +disclaimer of warranty; keep intact all the notices that refer to this +General Public License and to the absence of any warranty; and give any +other recipients of the Program a copy of this General Public License +along with the Program. You may charge a fee for the physical act of +transferring a copy. + +@item +You may modify your copy or copies of the Program or any portion of +it, and copy and distribute such modifications under the terms of Paragraph +1 above, provided that you also do the following: + +@itemize @bullet +@item +cause the modified files to carry prominent notices stating that +you changed the files and the date of any change; and + +@item +cause the whole of any work that you distribute or publish, that +in whole or in part contains the Program or any part thereof, either +with or without modifications, to be licensed at no charge to all +third parties under the terms of this General Public License (except +that you may choose to grant warranty protection to some or all +third parties, at your option). + +@item +If the modified program normally reads commands interactively when +run, you must cause it, when started running for such interactive use +in the simplest and most usual way, to print or display an +announcement including an appropriate copyright notice and a notice +that there is no warranty (or else, saying that you provide a +warranty) and that users may redistribute the program under these +conditions, and telling the user how to view a copy of this General +Public License. + +@item +You may charge a fee for the physical act of transferring a +copy, and you may at your option offer warranty protection in +exchange for a fee. +@end itemize + +Mere aggregation of another independent work with the Program (or its +derivative) on a volume of a storage or distribution medium does not bring +the other work under the scope of these terms. + +@item +You may copy and distribute the Program (or a portion or derivative of +it, under Paragraph 2) in object code or executable form under the terms of +Paragraphs 1 and 2 above provided that you also do one of the following: + +@itemize @bullet +@item +accompany it with the complete corresponding machine-readable +source code, which must be distributed under the terms of +Paragraphs 1 and 2 above; or, + +@item +accompany it with a written offer, valid for at least three +years, to give any third party free (except for a nominal charge +for the cost of distribution) a complete machine-readable copy of the +corresponding source code, to be distributed under the terms of +Paragraphs 1 and 2 above; or, + +@item +accompany it with the information you received as to where the +corresponding source code may be obtained. (This alternative is +allowed only for noncommercial distribution and only if you +received the program in object code or executable form alone.) +@end itemize + +Source code for a work means the preferred form of the work for making +modifications to it. For an executable file, complete source code means +all the source code for all modules it contains; but, as a special +exception, it need not include source code for modules which are standard +libraries that accompany the operating system on which the executable +file runs, or for standard header files or definitions files that +accompany that operating system. + +@item +You may not copy, modify, sublicense, distribute or transfer the +Program except as expressly provided under this General Public License. +Any attempt otherwise to copy, modify, sublicense, distribute or transfer +the Program is void, and will automatically terminate your rights to use +the Program under this License. However, parties who have received +copies, or rights to use copies, from you under this General Public +License will not have their licenses terminated so long as such parties +remain in full compliance. + +@item +By copying, distributing or modifying the Program (or any work based +on the Program) you indicate your acceptance of this license to do so, +and all its terms and conditions. + +@item +Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the original +licensor to copy, distribute or modify the Program subject to these +terms and conditions. You may not impose any further restrictions on the +recipients' exercise of the rights granted herein. + +@item +The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of the license which applies to it and ``any +later version'', you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +the license, you may choose any version ever published by the Free Software +Foundation. + +@item +If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + +@iftex +@heading NO WARRANTY +@end iftex +@ifinfo +@center NO WARRANTY +@end ifinfo + +@item +BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM ``AS IS'' WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + +@item +IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL +ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES +ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT +LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES +SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE +WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN +ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. +@end enumerate + +@iftex +@heading END OF TERMS AND CONDITIONS +@end iftex +@ifinfo +@center END OF TERMS AND CONDITIONS +@end ifinfo + +@page +@unnumberedsec Appendix: How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to humanity, the best way to achieve this is to make it +free software which everyone can redistribute and change under these +terms. + + To do so, attach the following notices to the program. It is safest to +attach them to the start of each source file to most effectively convey +the exclusion of warranty; and each file should have at least the +``copyright'' line and a pointer to where the full notice is found. + +@smallexample +@var{one line to give the program's name and a brief idea of what it does.} +Copyright (C) 19@var{yy} @var{name of author} + +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 1, or (at your option) +any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +@end smallexample + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + +@smallexample +Gnomovision version 69, Copyright (C) 19@var{yy} @var{name of author} +Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. +This is free software, and you are welcome to redistribute it +under certain conditions; type `show c' for details. +@end smallexample + +The hypothetical commands `show w' and `show c' should show the +appropriate parts of the General Public License. Of course, the +commands you use may be called something other than `show w' and `show +c'; they could even be mouse-clicks or menu items---whatever suits your +program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a ``copyright disclaimer'' for the program, if +necessary. Here a sample; alter the names: + +@example +Yoyodyne, Inc., hereby disclaims all copyright interest in the +program `Gnomovision' (a program to direct compilers to make passes +at assemblers) written by James Hacker. + +@var{signature of Ty Coon}, 1 April 1989 +Ty Coon, President of Vice +@end example + +That's all there is to it! + +@node Contributors, Motivation, Copying, Top +@unnumbered Contributors to GNU @code{gperf} Utility + +@itemize @bullet +@item +The GNU @code{gperf} perfect hash function generator utility was +originally written in GNU C++ by Douglas C. Schmidt. It is now also +available in a highly-portable ``old-style'' C version. The general +idea for the perfect hash function generator was inspired by Keith +Bostic's algorithm written in C, and distributed to net.sources around +1984. The current program is a heavily modified, enhanced, and extended +implementation of Keith's basic idea, created at the University of +California, Irvine. Bugs, patches, and suggestions should be reported +to schmidt at ics.uci.edu. + +@item +Special thanks is extended to Michael Tiemann and Doug Lea, for +providing a useful compiler, and for giving me a forum to exhibit my +creation. + +In addition, Adam de Boor and Nels Olson provided many tips and insights +that greatly helped improve the quality and functionality of @code{gperf}. +@end itemize + +@node Motivation, Search Structures, Contributors, Top +@chapter Introduction + +@code{gperf} is a perfect hash function generator written in C++. It +transforms an @emph{n} element user-specified keyword set @emph{W} into +a perfect hash function @emph{F}. @emph{F} uniquely maps keywords in +@emph{W} onto the range 0..@emph{k}, where @emph{k} >= @emph{n}. If +@emph{k = n} then @emph{F} is a @emph{minimal} perfect hash function. +@code{gperf} generates a 0..@emph{k} element static lookup table and a +pair of C functions. These functions determine whether a given +character string @emph{s} occurs in @emph{W}, using at most one probe +into the lookup table. + +@code{gperf} currently generates the reserved keyword recognizer for +lexical analyzers in several production and research compilers and +language processing tools, including GNU C, GNU C++, GNU Pascal, GNU +Modula 3, and GNU indent. Complete C++ source code for @code{gperf} is +available via anonymous ftp from ics.uci.edu. @code{gperf} also is +distributed along with the GNU libg++ library. A highly portable, +functionally equivalent K&R C version of @code{gperf} is archived in +comp.sources.unix, volume 20. Finally, a paper describing +@code{gperf}'s design and implementation in greater detail is available +in the Second USENIX C++ Conference proceedings. + +@node Search Structures, Description, Motivation, Top +@chapter Static search structures and GNU @code{gperf} + +A @dfn{static search structure} is an Abstract Data Type with certain +fundamental operations, @emph{e.g.}, @emph{initialize}, @emph{insert}, +and @emph{retrieve}. Conceptually, all insertions occur before any +retrievals. In practice, @code{gperf} generates a @code{static} array +containing search set keywords and any associated attributes specified +by the user. Thus, there is essentially no execution-time cost for the +insertions. It is a useful data structure for representing @emph{static +search sets}. Static search sets occur frequently in software system +applications. Typical static search sets include compiler reserved +words, assembler instruction opcodes, and built-in shell interpreter +commands. Search set members, called @dfn{keywords}, are inserted into +the structure only once, usually during program initialization, and are +not generally modified at run-time. + +Numerous static search structure implementations exist, @emph{e.g.}, +arrays, linked lists, binary search trees, digital search tries, and +hash tables. Different approaches offer trade-offs between space +utilization and search time efficiency. For example, an @emph{n} element +sorted array is space efficient, though the average-case time +complexity for retrieval operations using binary search is +proportional to log @emph{n}. Conversely, hash table implementations +often locate a table entry in constant time, but typically impose +additional memory overhead and exhibit poor worst case performance. + + +@emph{Minimal perfect hash functions} provide an optimal solution for a +particular class of static search sets. A minimal perfect hash +function is defined by two properties: + +@itemize @bullet +@item +It allows keyword recognition in a static search set using at most +@emph{one} probe into the hash table. This represents the ``perfect'' +property. +@item +The actual memory allocated to store the keywords is precisely large +enough for the keyword set, and @emph{no larger}. This is the +``minimal'' property. +@end itemize + +For most applications it is far easier to generate @emph{perfect} hash +functions than @emph{minimal perfect} hash functions. Moreover, +non-minimal perfect hash functions frequently execute faster than +minimal ones in practice. This phenomena occurs since searching a +sparse keyword table increases the probability of locating a ``null'' +entry, thereby reducing string comparisons. @code{gperf}'s default +behavior generates @emph{near-minimal} perfect hash functions for +keyword sets. However, @code{gperf} provides many options that permit +user control over the degree of minimality and perfection. + +Static search sets often exhibit relative stability over time. For +example, Ada's 63 reserved words have remained constant for nearly a +decade. It is therefore frequently worthwhile to expend concerted +effort building an optimal search structure @emph{once}, if it +subsequently receives heavy use multiple times. @code{gperf} removes +the drudgery associated with constructing time- and space-efficient +search structures by hand. It has proven a useful and practical tool +for serious programming projects. Output from @code{gperf} is currently +used in several production and research compilers, including GNU C, GNU +C++, GNU Pascal, and GNU Modula 3. The latter two compilers are not yet +part of the official GNU distribution. Each compiler utilizes +@code{gperf} to automatically generate static search structures that +efficiently identify their respective reserved keywords. + +@node Description, Options, Search Structures, Top +@chapter High-Level Description of GNU @code{gperf} + +@menu +* Input Format:: Input Format to @code{gperf} +* Output Format:: Output Format for Generated C Code with @code{gperf} +@end menu + +The perfect hash function generator @code{gperf} reads a set of +``keywords'' from a @dfn{keyfile} (or from the standard input by +default). It attempts to derive a perfect hashing function that +recognizes a member of the @dfn{static keyword set} with at most a +single probe into the lookup table. If @code{gperf} succeeds in +generating such a function it produces a pair of C source code routines +that perform hashing and table lookup recognition. All generated C code +is directed to the standard output. Command-line options described +below allow you to modify the input and output format to @code{gperf}. + +By default, @code{gperf} attempts to produce time-efficient code, with +less emphasis on efficient space utilization. However, several options +exist that permit trading-off execution time for storage space and vice +versa. In particular, expanding the generated table size produces a +sparse search structure, generally yielding faster searches. +Conversely, you can direct @code{gperf} to utilize a C @code{switch} +statement scheme that minimizes data space storage size. Furthermore, +using a C @code{switch} may actually speed up the keyword retrieval time +somewhat. Actual results depend on your C compiler, of course. + +In general, @code{gperf} assigns values to the characters it is using +for hashing until some set of values gives each keyword a unique value. +A helpful heuristic is that the larger the hash value range, the easier +it is for @code{gperf} to find and generate a perfect hash function. +Experimentation is the key to getting the most from @code{gperf}. + +@node Input Format, Output Format, Description, Description +@section Input Format to @code{gperf} + +You can control the input keyfile format by varying certain command-line +arguments, in particular the @samp{-t} option. The input's appearance +is similar to GNU utilities @code{flex} and @code{bison} (or UNIX +utilities @code{lex} and @code{yacc}). Here's an outline of the general +format: + +@example +@group +declarations +%% +keywords +%% +functions +@end group +@end example + +@emph{Unlike} @code{flex} or @code{bison}, all sections of @code{gperf}'s input +are optional. The following sections describe the input format for each +section. + +@menu +* Declarations:: @code{struct} Declarations and C Code Inclusion. +* Keywords:: Format for Keyword Entries. +* Functions:: Including Additional C Functions. +@end menu + +@node Declarations, Keywords, Input Format, Input Format +@subsection @code{struct} Declarations and C Code Inclusion + +The keyword input file optionally contains a section for including +arbitrary C declarations and definitions, as well as provisions for +providing a user-supplied @code{struct}. If the @samp{-t} option +@emph{is} enabled, you @emph{must} provide a C @code{struct} as the last +component in the declaration section from the keyfile file. The first +field in this struct must be a @code{char *} identifier called ``name,'' +although it is possible to modify this field's name with the @samp{-K} +option described below. + +Here is simple example, using months of the year and their attributes as +input: + +@example +@group +struct months @{ char *name; int number; int days; int leap_days; @}; +%% +january, 1, 31, 31 +february, 2, 28, 29 +march, 3, 31, 31 +april, 4, 30, 30 +may, 5, 31, 31 +june, 6, 30, 30 +july, 7, 31, 31 +august, 8, 31, 31 +september, 9, 30, 30 +october, 10, 31, 31 +november, 11, 30, 30 +december, 12, 31, 31 +@end group +@end example + +Separating the @code{struct} declaration from the list of key words and +other fields are a pair of consecutive percent signs, @code{%%}, +appearing left justified in the first column, as in the UNIX utility +@code{lex}. + +Using a syntax similar to GNU utilities @code{flex} and @code{bison}, it +is possible to directly include C source text and comments verbatim into +the generated output file. This is accomplished by enclosing the region +inside left-justified surrounding @code{%@{}, @code{%@}} pairs. Here is +an input fragment based on the previous example that illustrates this +feature: + +@example +@group +%@{ +#include <assert.h> +/* This section of code is inserted directly into the output. */ +int return_month_days (struct months *months, int is_leap_year); +%@} +struct months @{ char *name; int number; int days; int leap_days; @}; +%% +january, 1, 31, 31 +february, 2, 28, 29 +march, 3, 31, 31 +... +@end group +@end example + +It is possible to omit the declaration section entirely. In this case +the keyfile begins directly with the first keyword line, @emph{e.g.}: + +@example +@group +january, 1, 31, 31 +february, 2, 28, 29 +march, 3, 31, 31 +april, 4, 30, 30 +... +@end group +@end example + +@node Keywords, Functions, Declarations, Input Format +@subsection Format for Keyword Entries + +The second keyfile format section contains lines of keywords and any +associated attributes you might supply. A line beginning with @samp{#} +in the first column is considered a comment. Everything following the +@samp{#} is ignored, up to and including the following newline. + +The first field of each non-comment line is always the key itself. It +should be given as a simple name, @emph{i.e.}, without surrounding +string quotation marks, and be left-justified flush against the first +column. In this context, a ``field'' is considered to extend up to, but +not include, the first blank, comma, or newline. Here is a simple +example taken from a partial list of C reserved words: + +@example +@group +# These are a few C reserved words, see the c.@code{gperf} file +# for a complete list of ANSI C reserved words. +unsigned +sizeof +switch +signed +if +default +for +while +return +@end group +@end example + +Note that unlike @code{flex} or @code{bison} the first @code{%%} marker +may be elided if the declaration section is empty. + +Additional fields may optionally follow the leading keyword. Fields +should be separated by commas, and terminate at the end of line. What +these fields mean is entirely up to you; they are used to initialize the +elements of the user-defined @code{struct} provided by you in the +declaration section. If the @samp{-t} option is @emph{not} enabled +these fields are simply ignored. All previous examples except the last +one contain keyword attributes. + +@node Functions, , Keywords, Input Format +@subsection Including Additional C Functions + +The optional third section also corresponds closely with conventions +found in @code{flex} and @code{bison}. All text in this section, +starting at the final @code{%%} and extending to the end of the input +file, is included verbatim into the generated output file. Naturally, +it is your responsibility to ensure that the code contained in this +section is valid C. + +@node Output Format, , Input Format, Description +@section Output Format for Generated C Code with @code{gperf} + +Several options control how the generated C code appears on the standard +output. Two C function are generated. They are called @code{hash} and +@code{in_word_set}, although you may modify the name for +@code{in_word_set} with a command-line option. Both functions require +two arguments, a string, @code{char *} @var{str}, and a length +parameter, @code{int} @var{len}. Their default function prototypes are +as follows: + +@example +@group +static int hash (char *str, int len); +int in_word_set (char *str, int len); +@end group +@end example + +By default, the generated @code{hash} function returns an integer value +created by adding @var{len} to several user-specified @var{str} key +positions indexed into an @dfn{associated values} table stored in a +local static array. The associated values table is constructed +internally by @code{gperf} and later output as a static local C array called +@var{hash_table}; its meaning and properties are described below. +@xref{Implementation}. The relevant key positions are specified via the +@samp{-k} option when running @code{gperf}, as detailed in the @emph{Options} +section below. @xref{Options}. + +Two options, @samp{-g} (assume you are compiling with GNU C and its +@code{inline} feature) and @samp{-a} (assume ANSI C-style function +prototypes), alter the content of both the generated @code{hash} and +@code{in_word_set} routines. However, function @code{in_word_set} may +be modified more extensively, in response to your option settings. The +options that affect the @code{in_word_set} structure are: + +@itemize @bullet +@table @samp +@item -p +Have function @code{in_word_set} return a pointer rather than a boolean. + +@item -t +Make use of the user-defined @code{struct}. + +@item -S @var{total switch statements} +Generate 1 or more C @code{switch} statement rather than use a large, +(and potentially sparse) static array. Although the exact time and +space savings of this approach vary according to your C compiler's +degree of optimization, this method often results in smaller and faster +code. +@end table +@end itemize + +If the @samp{-t}, @samp{-S}, and @samp{-p} options are omitted the +default action is to generate a @code{char *} array containing the keys, +together with additional null strings used for padding the array. By +experimenting with the various input and output options, and timing the +resulting C code, you can determine the best option choices for +different keyword set characteristics. + +@node Options, Bugs, Description, Top +@chapter Options to the @code{gperf} Utility + +There are @emph{many} options to @code{gperf}. They were added to make +the program more convenient for use with real applications. ``On-line'' +help is readily available via the @samp{-h} option. Other options +include: + +@itemize @bullet +@table @samp +@item -a +Generate ANSI Standard C code using function prototypes. The default is +to use ``classic'' K&R C function declaration syntax. + +@item -c +Generates C code that uses the @code{strncmp} function to perform +string comparisons. The default action is to use @code{strcmp}. + +@item -C +Makes the contents of all generated lookup tables constant, @emph{i.e.}, +``readonly.'' Many compilers can generate more efficient code for this +by putting the tables in readonly memory. + +@item -d +Enables the debugging option. This produces verbose diagnostics to +``standard error'' when @code{gperf} is executing. It is useful both for +maintaining the program and for determining whether a given set of +options is actually speeding up the search for a solution. Some useful +information is dumped at the end of the program when the @samp{-d} +option is enabled. + +@item -D +Handle keywords whose key position sets hash to duplicate values. +Duplicate hash values occur for two reasons: + +@itemize @bullet +@item +Since @code{gperf} does not backtrack it is possible for it to process +all your input keywords without finding a unique mapping for each word. +However, frequently only a very small number of duplicates occur, and +the majority of keys still require one probe into the table. +@item +Sometimes a set of keys may have the same names, but possess different +attributes. With the -D option @code{gperf} treats all these keys as part of +an equivalence class and generates a perfect hash function with multiple +comparisons for duplicate keys. It is up to you to completely +disambiguate the keywords by modifying the generated C code. However, +@code{gperf} helps you out by organizing the output. +@end itemize + +Option @samp{-D} is extremely useful for certain large or highly +redundant keyword sets, @emph{i.e.}, assembler instruction opcodes. +Using this option usually means that the generated hash function is no +longer perfect. On the other hand, it permits @code{gperf} to work on +keyword sets that it otherwise could not handle. + +@item -e @var{keyword delimiter list} +Allows the user to provide a string containing delimiters used to +separate keywords from their attributes. The default is ",\n". This +option is essential if you want to use keywords that have embedded +commas or newlines. One useful trick is to use -e'TAB', where TAB is +the literal tab character. + +@item -E +Define constant values using an enum local to the lookup function rather +than with #defines. This also means that different lookup functions can +reside in the same file. Thanks to James Clark (jjc at ai.mit.edu). + +@item -f @var{iteration amount} +Generate the perfect hash function ``fast.'' This decreases @code{gperf}'s +running time at the cost of minimizing generated table-size. The +iteration amount represents the number of times to iterate when +resolving a collision. `0' means `iterate by the number of keywords. +This option is probably most useful when used in conjunction with options +@samp{-D} and/or @samp{-S} for @emph{large} keyword sets. + +@item -g +Assume a GNU compiler, @emph{e.g.}, @code{g++} or @code{gcc}. This +makes all generated routines use the ``inline'' keyword to remove the +cost of function calls. Note that @samp{-g} does @emph{not} imply +@samp{-a}, since other non-ANSI C compilers may have provisions for a +function @code{inline} feature. + +@item -G +Generate the static table of keywords as a static global variable, +rather than hiding it inside of the lookup function (which is the +default behavior). + +@item -h +Prints a short summary on the meaning of each program option. Aborts +further program execution. + +@item -H @var{hash function name} +Allows you to specify the name for the generated hash function. Default +name is `hash.' This option permits the use of two hash tables in the +same file. + +@item -i @var{initial value} +Provides an initial @var{value} for the associate values array. Default +is 0. Increasing the initial value helps inflate the final table size, +possibly leading to more time efficient keyword lookups. Note that this +option is not particularly useful when @samp{-S} is used. Also, +@samp{-i} is overriden when the @samp{-r} option is used. + +@item -j @var{jump value} +Affects the ``jump value,'' @emph{i.e.}, how far to advance the +associated character value upon collisions. @var{Jump value} is rounded +up to an odd number, the default is 5. If the @var{jump value} is 0 @code{gper +f} +jumps by random amounts. + +@item -k @var{keys} +Allows selection of the character key positions used in the keywords' +hash function. The allowable choices range between 1-126, inclusive. +The positions are separated by commas, @emph{e.g.}, @samp{-k 9,4,13,14}; +ranges may be used, @emph{e.g.}, @samp{-k 2-7}; and positions may occur +in any order. Furthermore, the meta-character '*' causes the generated +hash function to consider @strong{all} character positions in each key, +whereas '$' instructs the hash function to use the ``final character'' +of a key (this is the only way to use a character position greater than +126, incidentally). + +For instance, the option @samp{-k 1,2,4,6-10,'$'} generates a hash +function that considers positions 1,2,4,6,7,8,9,10, plus the last +character in each key (which may differ for each key, obviously). Keys +with length less than the indicated key positions work properly, since +selected key positions exceeding the key length are simply not +referenced in the hash function. + +@item -K @var{key name} +By default, the program assumes the structure component identifier for +the keyword is ``name.'' This option allows an arbitrary choice of +identifier for this component, although it still must occur as the first +field in your supplied @code{struct}. + +@item -l +Compare key lengths before trying a string comparison. This might cut +down on the number of string comparisons made during the lookup, since +keys with different lengths are never compared via @code{strcmp}. +However, using @samp{-l} might greatly increase the size of the +generated C code if the lookup table range is large (which implies that +the switch option @samp{-S} is not enabled), since the length table +contains as many elements as there are entries in the lookup table. + +@item -L @var{generated language name} +Instructs @code{gperf} to generate code in the language specified by the +option's argument. Languages handled are currently C++ and C. The +default is C. + +@item -n +Instructs the generator not to include the length of a keyword when +computing its hash value. This may save a few assembly instructions in +the generated lookup table. + +@item -N @var{lookup function name} +Allows you to specify the name for the generated lookup function. +Default name is `in_word_set.' This option permits completely automatic +generation of perfect hash functions, especially when multiple generated +hash functions are used in the same application. + +@item -o +Reorders the keywords by sorting the keywords so that frequently +occuring key position set components appear first. A second reordering +pass follows so that keys with ``already determined values'' are placed +towards the front of the keylist. This may decrease the time required +to generate a perfect hash function for many keyword sets, and also +produce more minimal perfect hash functions. The reason for this is +that the reordering helps prune the search time by handling inevitable +collisions early in the search process. On the other hand, if the +number of keywords is @emph{very} large using @samp{-o} may +@emph{increase} @code{gperf}'s execution time, since collisions will begin +earlier and continue throughout the remainder of keyword processing. +See Cichelli's paper from the January 1980 Communications of the ACM for +details. + +@item -p +Changes the return value of the generated function @code{in_word_set} +from boolean (@emph{i.e.}, 0 or 1), to either type ``pointer to +user-defined struct,'' (if the @samp{-t} option is enabled), or simply +to @code{char *}, if @samp{-t} is not enabled. This option is most +useful when the @samp{-t} option (allowing user-defined structs) is +used. For example, it is possible to automatically generate the GNU C +reserved word lookup routine with the options @samp{-p} and @samp{-t}. + +@item -r +Utilizes randomness to initialize the associated values table. This +frequently generates solutions faster than using deterministic +initialization (which starts all associated values at 0). Furthermore, +using the randomization option generally increases the size of the +table. If @code{gperf} has difficultly with a certain keyword set try using +@samp{-r} or @samp{-D}. + +@item -s @var{size-multiple} +Affects the size of the generated hash table. The numeric argument for +this option indicates ``how many times larger or smaller'' the maximum +associated value range should be, in relationship to the number of keys. +If the @var{size-multiple} is negative the maximum associated value is +calculated by @emph{dividing} it into the total number of keys. For +example, a value of 3 means ``allow the maximum associated value to be +about 3 times larger than the number of input keys.'' + +Conversely, a value of -3 means ``allow the maximum associated value to +be about 3 times smaller than the number of input keys.'' Negative +values are useful for limiting the overall size of the generated hash +table, though this usually increases the number of duplicate hash +values. + +If `generate switch' option @samp{-S} is @emph{not} enabled, the maximum +associated value influences the static array table size, and a larger +table should decrease the time required for an unsuccessful search, at +the expense of extra table space. + +The default value is 1, thus the default maximum associated value about +the same size as the number of keys (for efficiency, the maximum +associated value is always rounded up to a power of 2). The actual +table size may vary somewhat, since this technique is essentially a +heuristic. In particular, setting this value too high slows down +@code{gperf}'s runtime, since it must search through a much larger range +of values. Judicious use of the @samp{-f} option helps alleviate this +overhead, however. + +@item -S @var{total switch statements} +Causes the generated C code to use a @code{switch} statement scheme, +rather than an array lookup table. This can lead to a reduction in both +time and space requirements for some keyfiles. The argument to this +option determines how many @code{switch} statements are generated. A +value of 1 generates 1 @code{switch} containing all the elements, a +value of 2 generates 2 tables with 1/2 the elements in each +@code{switch}, etc. This is useful since many C compilers cannot +correctly generate code for large @code{switch} statements. This option +was inspired in part by Keith Bostic's original C program. + +@item -t +Allows you to include a @code{struct} type declaration for generated +code. Any text before a pair of consecutive %% is consider part of the +type declaration. Key words and additional fields may follow this, one +group of fields per line. A set of examples for generating perfect hash +tables and functions for Ada, C, and G++, Pascal, and Modula 2 and 3 +reserved words are distributed with this release. + +@item -T +Prevents the transfer of the type declaration to the output file. Use +this option if the type is already defined elsewhere. + +@item -v +Prints out the current version number. + +@item -Z @var{class name} +Allow user to specify name of generated C++ class. Default name is +@code{Perfect_Hash}. +@end table +@end itemize + +@node Bugs, Projects, Options, Top +@chapter Known Bugs and Limitations with @code{gperf} + +The following are some limitations with the current release of +@code{gperf}: + +@itemize @bullet +@item +The @code{gperf} utility is tuned to execute quickly, and works quickly +for small to medium size data sets (around 1000 keywords). It is +extremely useful for maintaining perfect hash functions for compiler +keyword sets. Several recent enhancements now enable @code{gperf} to +work efficiently on much larger keyword sets (over 15,000 keywords). +When processing large keyword sets it helps greatly to have over 8 megs +of RAM. + +However, since @code{gperf} does not backtrack no guaranteed solution +occurs on every run. On the other hand, it is usually easy to obtain a +solution by varying the option parameters. In particular, try the +@samp{-r} option, and also try changing the default arguments to the +@samp{-s} and @samp{-j} options. To @emph{guarantee} a solution, use +the @samp{-D} and @samp{-S} options, although the final results are not +likely to be a @emph{perfect} hash function anymore! Finally, use the +@samp{-f} option if you want @code{gperf} to generate the perfect hash +function @emph{fast}, with less emphasis on making it minimal. + +@item +The size of the generate static keyword array can get @emph{extremely} +large if the input keyword file is large or if the keywords are quite +similar. This tends to slow down the compilation of the generated C +code, and @emph{greatly} inflates the object code size. If this +situation occurs, consider using the @samp{-S} option to reduce data +size, potentially increasing keyword recognition time a negligible +amount. Since many C compilers cannot correctly generated code for +large switch statements it is important to qualify the @var{-S} option +with an appropriate numerical argument that controls the number of +switch statements generated. + +@item +The maximum number of key positions selected for a given key has an +arbitrary limit of 126. This restriction should be removed, and if +anyone considers this a problem write me and let me know so I can remove +the constraint. + +@item +The C++ source code only compiles correctly with GNU G++, version 1.36 +(and hopefully later versions). Porting to AT&T cfront would be +tedious, but possible (and desirable). There is also a K&R C version +available now. This should compile without change on most BSD systems, +but may require a bit of work to run on SYSV, since @code{gperf} uses +@var{alloca} in several places. Send mail to schmidt at ics.uci.edu for +information. +@end itemize + +@node Projects, Implementation, Bugs, Top +@chapter Things Still Left to Do + +It should be ``relatively'' easy to replace the current perfect hash +function algorithm with a more exhaustive approach; the perfect hash +module is essential independent from other program modules. Additional +worthwhile improvements include: + +@itemize @bullet +@item +Make the algorithm more robust. At present, the program halts with an +error diagnostic if it can't find a direct solution and the @samp{-D} +option is not enabled. A more comprehensive, albeit computationally +expensive, approach would employ backtracking or enable alternative +options and retry. It's not clear how helpful this would be, in +general, since most search sets are rather small in practice. + +@item +Another useful extension involves modifying the program to generate +``minimal'' perfect hash functions (under certain circumstances, the +current version can be rather extravagant in the generated table size). +Again, this is mostly of theoretical interest, since a sparse table +often produces faster lookups, and use of the @samp{-S} @code{switch} +option can minimize the data size, at the expense of slightly longer +lookups (note that the gcc compiler generally produces good code for +@code{switch} statements, reducing the need for more complex schemes). + +@item +In addition to improving the algorithm, it would also be useful to +generate a C++ class or Ada package as the code output, in addition to +the current C routines. +@end itemize + +@node Implementation, Bibliography, Projects, Top +@chapter Implementation Details of GNU @code{gperf} + +A paper describing the high-level description of the data structures and +algorithms used to implement @code{gperf} will soon be available. This +paper is useful not only from a maintenance and enhancement perspective, +but also because they demonstrate several clever and useful programming +techniques, @emph{e.g.}, `Iteration Number' boolean arrays, double +hashing, a ``safe'' and efficient method for reading arbitrarily long +input from a file, and a provably optimal algorithm for simultaneously +determining both the minimum and maximum elements in a list. + +@page + +@node Bibliography, , Implementation, Top +@chapter Bibliography + +[1] Chang, C.C.: @i{A Scheme for Constructing Ordered Minimal Perfect +Hashing Functions} Information Sciences 39(1986), 187-195. + +[2] Cichelli, Richard J. @i{Author's Response to ``On Cichelli's Minimal Perfec +t Hash +Functions Method''} Communications of the ACM, 23, 12(December 1980), 729. + +[3] Cichelli, Richard J. @i{Minimal Perfect Hash Functions Made Simple} +Communications of the ACM, 23, 1(January 1980), 17-19. + +[4] Cook, C. R. and Oldehoeft, R.R. @i{A Letter Oriented Minimal +Perfect Hashing Function} SIGPLAN Notices, 17, 9(September 1982), 18-27. + +[5] Cormack, G. V. and Horspool, R. N. S. and Kaiserwerth, M. +@i{Practical Perfect Hashing} Computer Journal, 28, 1(January 1985), 54-58. + +[6] Jaeschke, G. @i{Reciprocal Hashing: A Method for Generating Minimal +Perfect Hashing Functions} Communications of the ACM, 24, 12(December +1981), 829-833. + +[7] Jaeschke, G. and Osterburg, G. @i{On Cichelli's Minimal Perfect +Hash Functions Method} Communications of the ACM, 23, 12(December 1980), +728-729. + +[8] Sager, Thomas J. @i{A Polynomial Time Generator for Minimal Perfect +Hash Functions} Communications of the ACM, 28, 5(December 1985), 523-532 + +[9] Schmidt, Douglas C. @i{GPERF: A Perfect Hash Function Generator} +Second USENIX C++ Conference Proceedings, April 1990. + +[10] Sebesta, R.W. and Taylor, M.A. @i{Minimal Perfect Hash Functions +for Reserved Word Lists} SIGPLAN Notices, 20, 12(September 1985), 47-53. + +[11] Sprugnoli, R. @i{Perfect Hashing Functions: A Single Probe +Retrieving Method for Static Sets} Communications of the ACM, 20 +11(November 1977), 841-850. + +[12] Stallman, Richard M. @i{Using and Porting GNU CC} Free Software Foundation, +1988. + +[13] Stroustrup, Bjarne @i{The C++ Programming Language.} Addison-Wesley, 1986. + +[14] Tiemann, Michael D. @i{User's Guide to GNU C++} Free Software +Foundation, 1989. + +@contents +@bye diff --git a/apps/gperf/src/Bool_Array.cpp b/apps/gperf/src/Bool_Array.cpp new file mode 100644 index 00000000000..e3243565f41 --- /dev/null +++ b/apps/gperf/src/Bool_Array.cpp @@ -0,0 +1,89 @@ +/* Fast lookup table abstraction implemented as an Iteration Number Array +// @(#)Bool_Array.cpp 1.1 10/18/96 + + Copyright (C) 1989 Free Software Foundation, Inc. + written by Douglas C. Schmidt (schmidt@ics.uci.edu) + +This file is part of GNU GPERF. + +GNU GPERF 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 1, or (at your option) +any later version. + +GNU GPERF is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU GPERF; see the file COPYING. If not, write to the Free +Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111, USA. */ + +#include "Bool_Array.h" + +// Prints out debugging diagnostics. + +Bool_Array::~Bool_Array (void) +{ + if (option[DEBUG]) + fprintf (stderr, "\ndumping boolean array information\n" + "size = %d\niteration number = %d\nend of array dump\n", + size, generation_number); +} + +Bool_Array::Bool_Array (void) + : storage_array (0), + generation_number (0), + size (0) +{ +} + +void +Bool_Array::init (STORAGE_TYPE *buffer, STORAGE_TYPE s) +{ + size = s; + generation_number = 1; + storage_array = buffer; + + memset (storage_array, 0, s * sizeof *storage_array); + + if (option[DEBUG]) + fprintf (stderr, "\nbool array size = %d, total bytes = %d\n", + size, size * sizeof *storage_array); +} + +int +Bool_Array::find (int index) +{ + if (storage_array[index] == generation_number) + return 1; + else + { + storage_array[index] = generation_number; + return 0; + } +} + +void +Bool_Array::reset (void) +{ + if (++generation_number == 0) + { + if (option[DEBUG]) + { + fprintf (stderr, "(re-initializing bool_array)..."); + fflush (stderr); + } + + generation_number = 1; + memset (storage_array, 0, size * sizeof *storage_array); + + if (option[DEBUG]) + { + fprintf (stderr, "done\n"); + fflush (stderr); + } + } +} + diff --git a/apps/gperf/src/Bool_Array.h b/apps/gperf/src/Bool_Array.h new file mode 100644 index 00000000000..d890484e485 --- /dev/null +++ b/apps/gperf/src/Bool_Array.h @@ -0,0 +1,65 @@ +/* -*- C++ -*- */ +// @(#)Bool_Array.h 1.1 10/18/96 + +/* This may look like C code, but it is really -*- C++ -*- */ + +/* Simple lookup table abstraction implemented as an Generation Number Array. + + Copyright (C) 1989 Free Software Foundation, Inc. + written by Douglas C. Schmidt (schmidt@ics.uci.edu) + +This file is part of GNU GPERF. + +GNU GPERF 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 1, or (at your option) +any later version. + +GNU GPERF is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU GPERF; see the file COPYING. If not, write to the Free +Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.*/ + +/* Define and implement a simple boolean array abstraction, + uses an Generation Numbering implementation to save on initialization time. */ + +#ifndef bool_array_h +#define bool_array_h 1 + +#include "Options.h" + +#ifdef LO_CAL +/* If we are on a memory diet then we'll only make these use a limited + amount of storage space. */ +typedef u_short STORAGE_TYPE; +#else +typedef int STORAGE_TYPE; +#endif + +class Bool_Array +{ +public: + Bool_Array (void); + ~Bool_Array (void); + + void init (STORAGE_TYPE *buffer, STORAGE_TYPE s); + int find (int hash_value); + void reset (void); + +private: + STORAGE_TYPE *storage_array; + // Initialization of the index space. + + STORAGE_TYPE generation_number; + // Keep track of the current Generation. + + int size; + // Keep track of array size. +}; + + +#endif diff --git a/apps/gperf/src/Gen_Perf.cpp b/apps/gperf/src/Gen_Perf.cpp new file mode 100644 index 00000000000..25c0299fd35 --- /dev/null +++ b/apps/gperf/src/Gen_Perf.cpp @@ -0,0 +1,345 @@ +/* Provides high-level routines to manipulate the keywork list +// @(#)Gen_Perf.cpp 1.1 10/18/96 + + structures the code generation output. + Copyright (C) 1989 Free Software Foundation, Inc. + written by Douglas C. Schmidt (schmidt@ics.uci.edu) + +This file is part of GNU GPERF. + +GNU GPERF 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 1, or (at your option) +any later version. + +GNU GPERF is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU GPERF; see the file COPYING. If not, write to the Free +Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111, USA. */ + +#include "Vectors.h" +#include "Gen_Perf.h" + +/* Current release version. */ +extern char *version_string; + +/* Reads input keys, possibly applies the reordering heuristic, sets + the maximum associated value size (rounded up to the nearest power + of 2), may initialize the associated values array, and determines + the maximum hash table size. Note: using the random numbers is + often helpful, though not as deterministic, of course! */ + +Gen_Perf::Gen_Perf (void) +{ + int asso_value_max; + int non_linked_length; + + this->key_list.read_keys (); + if (option[ORDER]) + this->key_list.reorder (); + asso_value_max = option.get_asso_max (); + non_linked_length = this->key_list.keyword_list_length (); + num_done = 1; + fewest_collisions = 0; + if (asso_value_max == 0) + asso_value_max = non_linked_length; + else if (asso_value_max > 0) + asso_value_max *= non_linked_length; + else /* if (asso_value_max < 0) */ + asso_value_max = non_linked_length / -asso_value_max; + option.set_asso_max (ACE_POW (asso_value_max)); + + if (option[RANDOM]) + { + srand (time (0)); + + for (int i = 0; i < ALPHA_SIZE; i++) + Vectors::asso_values[i] = (rand () & asso_value_max - 1); + } + else + { + int asso_value = option.initial_value (); + + if (asso_value) /* Initialize array if user requests non-zero default. */ + for (int i = ALPHA_SIZE - 1; i >= 0; i--) + Vectors::asso_values[i] = asso_value & option.get_asso_max () - 1; + } + max_hash_value = this->key_list.max_key_length () + option.get_asso_max () * + option.get_max_keysig_size (); + + printf ("/* "); + if (option[C]) + printf ("C"); + else if (option[CPLUSPLUS]) + printf ("C++"); + printf (" code produced by gperf version %s */\n", version_string); + Options::print_options (); + + if (option[DEBUG]) + fprintf (stderr, "total non-linked keys = %d\nmaximum associated value is %d" + "\nmaximum size of generated hash table is %d\n", + non_linked_length, asso_value_max, max_hash_value); +} + +/* Merge two disjoint hash key multisets to form the ordered disjoint union of the sets. + (In a multiset, an element can occur multiple times). + Precondition: both set_1 and set_2 must be ordered. Returns the length + of the combined set. */ + +inline int +Gen_Perf::compute_disjoint_union (char *set_1, char *set_2, char *set_3) +{ + char *base = set_3; + + while (*set_1 && *set_2) + if (*set_1 == *set_2) + set_1++, set_2++; + else + { + *set_3 = *set_1 < *set_2 ? *set_1++ : *set_2++; + if (set_3 == base || *set_3 != *(set_3-1)) set_3++; + } + + while (*set_1) + { + *set_3 = *set_1++; + if (set_3 == base || *set_3 != *(set_3-1)) set_3++; + } + + while (*set_2) + { + *set_3 = *set_2++; + if (set_3 == base || *set_3 != *(set_3-1)) set_3++; + } + *set_3 = '\0'; + return set_3 - base; +} + +/* Sort the UNION_SET in increasing frequency of occurrence. + This speeds up later processing since we may assume the resulting + set (Set_3, in this case), is ordered. Uses insertion sort, since + the UNION_SET is typically short. */ + +inline void +Gen_Perf::sort_set (char *union_set, int len) +{ + int i, j; + + for (i = 0, j = len - 1; i < j; i++) + { + char curr, tmp; + + for (curr = i + 1, tmp = union_set[curr]; + curr > 0 && Vectors::occurrences[tmp] < Vectors::occurrences[union_set[curr-1]]; + curr--) + union_set[curr] = union_set[curr - 1]; + + union_set[curr] = tmp; + } +} + +/* Generate a key set's hash value. */ + +inline int +Gen_Perf::hash (List_Node *key_node) +{ + int sum = option[NOLENGTH] ? 0 : key_node->length; + + for (char *ptr = key_node->char_set; *ptr; ptr++) + sum += Vectors::asso_values[*ptr]; + + return key_node->hash_value = sum; +} + +/* Find out how character value change affects successfully hashed + items. Returns FALSE if no other hash values are affected, else + returns TRUE. Note that because Option.Get_Asso_Max is a power of + two we can guarantee that all legal Vectors::Asso_Values are visited without + repetition since Option.Get_Jump was forced to be an odd value! */ + +inline int +Gen_Perf::affects_prev (char c, List_Node *curr) +{ + int original_char = Vectors::asso_values[c]; + int total_iterations = !option[FAST] + ? option.get_asso_max () : option.get_iterations () ? option.get_iterations () : this->key_list.keyword_list_length (); + + /* Try all legal associated values. */ + + for (int i = total_iterations - 1; i >= 0; i--) + { + int collisions = 0; + + Vectors::asso_values[c] = Vectors::asso_values[c] + (option.get_jump () ? option.get_jump () : rand ()) + & option.get_asso_max () - 1; + + /* Iteration Number array is a win, O(1) intialization time! */ + this->char_search.reset (); + + /* See how this asso_value change affects previous keywords. If + it does better than before we'll take it! */ + + for (List_Node *ptr = this->key_list.head; + !this->char_search.find (hash (ptr)) || ++collisions < fewest_collisions; + ptr = ptr->next) + if (ptr == curr) + { + fewest_collisions = collisions; + if (option[DEBUG]) + fprintf (stderr, "- resolved after %d iterations", total_iterations - i); + return 0; + } + } + + /* Restore original values, no more tries. */ + Vectors::asso_values[c] = original_char; + /* If we're this far it's time to try the next character.... */ + return 1; +} + +/* Change a character value, try least-used characters first. */ + +void +Gen_Perf::change (List_Node *prior, List_Node *curr) +{ + static char *union_set; + + if (!union_set) + union_set = new char [2 * option.get_max_keysig_size () + 1]; + + if (option[DEBUG]) + { + fprintf (stderr, "collision on keyword #%d, prior = \"%s\", curr = \"%s\" hash = %d\n", + num_done, prior->key, curr->key, curr->hash_value); + fflush (stderr); + } + sort_set (union_set, compute_disjoint_union (prior->char_set, curr->char_set, union_set)); + + /* Try changing some values, if change doesn't alter other values continue normal action. */ + fewest_collisions++; + + for (char *temp = union_set; *temp; temp++) + if (!affects_prev (*temp, curr)) + { + if (option[DEBUG]) + { + fprintf (stderr, " by changing asso_value['%c'] (char #%d) to %d\n", + *temp, temp - union_set + 1, Vectors::asso_values[*temp]); + fflush (stderr); + } + return; /* Good, doesn't affect previous hash values, we'll take it. */ + } + + for (List_Node *ptr = this->key_list.head; ptr != curr; ptr = ptr->next) + hash (ptr); + + hash (curr); + + if (option[DEBUG]) + { + fprintf (stderr, "** collision not resolved after %d iterations, %d duplicates remain, continuing...\n", + !option[FAST] ? option.get_asso_max () : option.get_iterations () ? option.get_iterations () : this->key_list.keyword_list_length (), + fewest_collisions + this->key_list.total_duplicates); + fflush (stderr); + } +} + +/* Does the hard stuff.... + Initializes the Iteration Number array, and attempts to find a perfect + function that will hash all the key words without getting any + duplications. This is made much easier since we aren't attempting + to generate *minimum* functions, only perfect ones. + If we can't generate a perfect function in one pass *and* the user + hasn't enabled the DUP option, we'll inform the user to try the + randomization option, use -D, or choose alternative key positions. + The alternatives (e.g., back-tracking) are too time-consuming, i.e, + exponential in the number of keys. */ + +int +Gen_Perf::generate (void) +{ +#if LARGE_STACK_ARRAYS + STORAGE_TYPE buffer[max_hash_value + 1]; +#else + // Note: we don't use new, because that invokes a custom operator new. + STORAGE_TYPE *buffer + = (STORAGE_TYPE*) malloc (sizeof(STORAGE_TYPE) * (max_hash_value + 1)); + if (buffer == NULL) + abort (); +#endif + + this->char_search.init (buffer, max_hash_value + 1); + + List_Node *curr; + + for (curr = this->key_list.head; + curr; + curr = curr->next) + { + hash (curr); + + for (List_Node *ptr = this->key_list.head; + ptr != curr; + ptr = ptr->next) + if (ptr->hash_value == curr->hash_value) + { + change (ptr, curr); + break; + } + num_done++; + } + + /* Make one final check, just to make sure nothing weird happened.... */ + + this->char_search.reset (); + + for (curr = this->key_list.head; + curr; + curr = curr->next) + if (this->char_search.find (hash (curr))) + if (option[DUP]) /* Keep track of this number... */ + this->key_list.total_duplicates++; + else /* Yow, big problems. we're outta here! */ + { + ACE_ERROR ((LM_ERROR, "\nInternal error, duplicate value %d:\n" + "try options -D or -r, or use new key positions.\n\n", hash (curr))); +#if !LARGE_STACK_ARRAYS + free (buffer); +#endif + return 1; + } + + /* Sorts the key word list by hash value, and then outputs the list. + The generated hash table code is only output if the early stage of + processing turned out O.K. */ + + this->key_list.sort (); + this->key_list.output (); +#if !LARGE_STACK_ARRAYS + free (buffer); +#endif + return 0; +} + +/* Prints out some diagnostics upon completion. */ + +Gen_Perf::~Gen_Perf (void) +{ + if (option[DEBUG]) + { + fprintf (stderr, "\ndumping occurrence and associated values tables\n"); + + for (int i = 0; i < ALPHA_SIZE; i++) + if (Vectors::occurrences[i]) + fprintf (stderr, "Vectors::asso_values[%c] = %6d, Vectors::occurrences[%c] = %6d\n", + i, Vectors::asso_values[i], i, Vectors::occurrences[i]); + + fprintf (stderr, "end table dumping\n"); + + } +} + diff --git a/apps/gperf/src/Gen_Perf.h b/apps/gperf/src/Gen_Perf.h new file mode 100644 index 00000000000..11817de4851 --- /dev/null +++ b/apps/gperf/src/Gen_Perf.h @@ -0,0 +1,65 @@ +/* -*- C++ -*- */ +// @(#)Gen_Perf.h 1.1 10/18/96 + +/* This may look like C code, but it is really -*- C++ -*- */ + +/* Provides high-level routines to manipulate the keyword list + structures the code generation output. + + Copyright (C) 1989 Free Software Foundation, Inc. + written by Douglas C. Schmidt (schmidt@ics.uci.edu) + +This file is part of GNU GPERF. + +GNU GPERF 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 1, or (at your option) +any later version. + +GNU GPERF is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU GPERF; see the file COPYING. If not, write to the Free +Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111, USA. */ + +#ifndef gen_perf_h +#define gen_perf_h 1 + +#include "Options.h" +#include "Key_List.h" +#include "Bool_Array.h" + +class Gen_Perf +{ +public: + Gen_Perf (void); + ~Gen_Perf (void); + int generate (void); + +private: + void change (List_Node *prior, List_Node *curr); + int affects_prev (char c, List_Node *curr); + static int hash (List_Node *key_node); + static int compute_disjoint_union (char *set_1, char *set_2, char *set_3); + static void sort_set (char *union_set, int len); + + int max_hash_value; + // Maximum possible hash value. + + int fewest_collisions; + // Records fewest # of collisions for asso value. + + int num_done; + // Number of keywords processed without a collision. + + Bool_Array char_search; + // Table that keeps track of key collisions. + + Key_List key_list; + // List of the keys we're trying to map into a perfect hash + // function. +}; +#endif diff --git a/apps/gperf/src/Hash_Table.cpp b/apps/gperf/src/Hash_Table.cpp new file mode 100644 index 00000000000..dfb008514ce --- /dev/null +++ b/apps/gperf/src/Hash_Table.cpp @@ -0,0 +1,84 @@ +/* Hash table for checking keyword links. Implemented using double hashing. +// @(#)Hash_Table.cpp 1.1 10/18/96 + + Copyright (C) 1989 Free Software Foundation, Inc. + written by Douglas C. Schmidt (schmidt@ics.uci.edu) + +This file is part of GNU GPERF. + +GNU GPERF 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 1, or (at your option) +any later version. + +GNU GPERF is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU GPERF; see the file COPYING. If not, write to the Free +Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111, USA. */ + +#include "ace/ACE.h" +#include "Hash_Table.h" + +#define NIL(TYPE) (TYPE *)0 + +// The size of the hash table is always the smallest power of 2 >= the +// size indicated by the user. This allows several optimizations, +// including the use of double hashing and elimination of the mod +// instruction. Note that the size had better be larger than the +// number of items in the hash table, else there's trouble!!! Note +// that the memory for the hash table is allocated *outside* the +// intialization routine. This compromises information hiding +// somewhat, but greatly reduces memory fragmentation, since we can +// now use alloca! + +Hash_Table::Hash_Table (List_Node **table_ptr, int s) + : collisions (0), + size (s), + table (table_ptr) +{ + memset ((char *) table, 0, size * sizeof *table); +} + +Hash_Table::~Hash_Table (void) +{ + if (option[DEBUG]) + { + int field_width = option.get_max_keysig_size (); + + fprintf (stderr, "\ndumping the hash table\ntotal available table slots = %d, total bytes = %d, total collisions = %d\n" + "location, %*s, keyword\n", size, size * sizeof *table, collisions, field_width, "keysig"); + + for (int i = size - 1; i >= 0; i--) + if (table[i]) + fprintf (stderr, "%8d, %*s, %s\n", + i, field_width, table[i]->char_set, table[i]->key); + + fprintf (stderr, "\nend dumping hash table\n\n"); + } +} + +// If the ITEM is already in the hash table return the item found in +// the table. Otherwise inserts the ITEM, and returns FALSE. Uses +// double hashing. + +List_Node * +Hash_Table::operator() (List_Node *item, int ignore_length) +{ + unsigned hash_val = ACE::hash_pjw (item->char_set); + int probe = hash_val & size - 1; + int increment = (hash_val ^ item->length | 1) & size - 1; + + while (table[probe] + && (strcmp (table[probe]->char_set, item->char_set) + || (!ignore_length && table[probe]->length != item->length))) + { + collisions++; + probe = probe + increment & size - 1; + } + + return table[probe] ? table[probe] : (table[probe] = item, NIL (List_Node)); +} diff --git a/apps/gperf/src/Hash_Table.h b/apps/gperf/src/Hash_Table.h new file mode 100644 index 00000000000..c7a77a1b37b --- /dev/null +++ b/apps/gperf/src/Hash_Table.h @@ -0,0 +1,50 @@ +/* -*- C++ -*- */ +// @(#)Hash_Table.h 1.1 10/18/96 + +/* This may look like C code, but it is really -*- C++ -*- */ + +/* Hash table used to check for duplicate keyword entries. + + Copyright (C) 1989 Free Software Foundation, Inc. + written by Douglas C. Schmidt (schmidt@ics.uci.edu) + +This file is part of GNU GPERF. + +GNU GPERF 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 1, or (at your option) +any later version. + +GNU GPERF is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU GPERF; see the file COPYING. If not, write to the Free +Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111, USA. */ + +#ifndef hash_table_h +#define hash_table_h 1 + +#include "Options.h" +#include "List_Node.h" + +class Hash_Table +{ +public: + Hash_Table (List_Node **t, int s); + ~Hash_Table (void); + List_Node *operator () (List_Node *item, int ignore_length); + +private: + List_Node **table; + // Vector of pointers to linked lists of List_Node's. + + int size; + // Size of the vector. + + int collisions; + // Find out how well our double hashing is working! +}; +#endif diff --git a/apps/gperf/src/Iterator.cpp b/apps/gperf/src/Iterator.cpp new file mode 100644 index 00000000000..2e5d37f8f00 --- /dev/null +++ b/apps/gperf/src/Iterator.cpp @@ -0,0 +1,90 @@ +/* Provides an Iterator for keyword characters. +// @(#)Iterator.cpp 1.1 10/18/96 + + Copyright (C) 1989 Free Software Foundation, Inc. + written by Douglas C. Schmidt (schmidt@ics.uci.edu) + +This file is part of GNU GPERF. + +GNU GPERF 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 1, or (at your option) +any later version. + +GNU GPERF is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU GPERF; see the file COPYING. If not, write to the Free +Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111, USA. */ + +#include "Iterator.h" + +// Constructor for Iterator. + +Iterator::Iterator (char *s, + int lo, + int hi, + int word_end, + int bad_val, + int key_end) + : end (key_end), + error_value (bad_val), + end_word (word_end), + str (s), + hi_bound (hi), + lo_bound (lo) +{ +} + +// Provide an Iterator, returning the ``next'' value from the list of +// valid values given in the constructor. + +int +Iterator::operator() (void) +{ + // Variables to record the Iterator's status when handling ranges, + // e.g., 3-12. + + static int size; + static int curr_value; + static int upper_bound; + + if (size) + { + if (++curr_value >= upper_bound) + size = 0; + return curr_value; + } + else + { + while (*str) + switch (*str) + { + default: return error_value; + case ',': str++; break; + case '$': str++; return end_word; + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + for (curr_value = 0; isdigit (*str); str++) + curr_value = curr_value * 10 + *str - '0'; + + if (*str == '-') + { + + for (size = 1, upper_bound = 0; + isdigit (*++str); + upper_bound = upper_bound * 10 + *str - '0'); + + if (upper_bound <= curr_value || upper_bound > hi_bound) + return error_value; + } + return curr_value >= lo_bound && curr_value <= hi_bound + ? curr_value : error_value; + } + + return end; + } +} diff --git a/apps/gperf/src/Iterator.h b/apps/gperf/src/Iterator.h new file mode 100644 index 00000000000..d2c81039b3f --- /dev/null +++ b/apps/gperf/src/Iterator.h @@ -0,0 +1,67 @@ +/* -*- C++ -*- */ +// @(#)Iterator.h 1.1 10/18/96 + +/* This may look like C code, but it is really -*- C++ -*- */ + +/* Provides an Iterator for keyword characters. + + Copyright (C) 1989 Free Software Foundation, Inc. + written by Douglas C. Schmidt (schmidt@ics.uci.edu) + +This file is part of GNU GPERF. + +GNU GPERF 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 1, or (at your option) +any later version. + +GNU GPERF is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU GPERF; see the file COPYING. If not, write to the Free +Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +/* Provides an Iterator that expands and decodes a control string + containing digits and ranges, returning an integer every time the + generator function is called. This is used to decode the user's + key position requests. For example: "-k 1,2,5-10,$" will return 1, + 2, 5, 6, 7, 8, 9, 10, and 0 ( representing the abstract ``last + character of the key'' on successive calls to the member function + operator (). No errors are handled in these routines, they are + passed back to the calling routines via a user-supplied Error_Value */ + +#ifndef iterator_h +#define iterator_h 1 + +#include "Options.h" + +class Iterator +{ +public: + Iterator (char *s, int lo, int hi, int word_end, int bad_val, int key_end); + int operator () (void); + +private: + char *str; + // A pointer to the string provided by the user. + + int end; + // Value returned after last key is processed. + + int end_word; + // A value marking the abstract ``end of word'' (usually '$'). + + int error_value; + // Error value returned when input is syntactically erroneous. + + int hi_bound; + // Greatest possible value, inclusive. + + int lo_bound; + // Smallest possible value, inclusive. +}; + +#endif diff --git a/apps/gperf/src/Key_List.cpp b/apps/gperf/src/Key_List.cpp new file mode 100644 index 00000000000..3a944b4b28b --- /dev/null +++ b/apps/gperf/src/Key_List.cpp @@ -0,0 +1,1345 @@ +/* Routines for building, ordering, and printing the keyword list. +// @(#)Key_List.cpp 1.1 10/18/96 + + Copyright (C) 1989 Free Software Foundation, Inc. + written by Douglas C. Schmidt (schmidt@ics.uci.edu) + +This file is part of GNU GPERF. + +GNU GPERF 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 1, or (at your option) +any later version. + +GNU GPERF is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU GPERF; see the file COPYING. If not, write to the Free +Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111, USA. */ + +#include "ace/Read_Buffer.h" +#include "Hash_Table.h" +#include "Vectors.h" +#include "Key_List.h" + +/* Make the hash table 10 times larger than the number of keyword entries. */ +static const int TABLE_MULTIPLE = 10; + +/* Default type for generated code. */ +static char *const default_array_type = "char *"; + +/* in_word_set return type, by default. */ +static char *const default_return_type = "char *"; + +/* How wide the printed field width must be to contain the maximum hash value. */ +static int field_width = 0; + +static int determined[ALPHA_SIZE]; + +/* Destructor dumps diagnostics during debugging. */ + +Key_List::~Key_List (void) +{ + if (option[DEBUG]) + { + fprintf (stderr, "\nDumping key list information:\ntotal non-static linked keywords = %d" + "\ntotal keywords = %d\ntotal duplicates = %d\nmaximum key length = %d\n", + list_len, total_keys, total_duplicates ? total_duplicates + 1 : 0, max_key_len); + dump (); + ACE_ERROR ((LM_ERROR, "End dumping list.\n\n")); + } +} + +/* Gathers the input stream into a buffer until one of two things occur: + + 1. We read a '%' followed by a '%' + 2. We read a '%' followed by a '}' + + The first symbolizes the beginning of the keyword list proper, + The second symbolizes the end of the C source code to be generated + verbatim in the output file. + + I assume that the keys are separated from the optional preceding struct + declaration by a consecutive % followed by either % or } starting in + the first column. The code below uses an expandible buffer to scan off + and return a pointer to all the code (if any) appearing before the delimiter. */ + +char * +Key_List::get_special_input (char delimiter) +{ + int size = 80; + char *buf = new char[size]; + int c, i; + + for (i = 0; (c = getchar ()) != EOF; i++) + { + if (c == '%') + { + if ((c = getchar ()) == delimiter) + { + + while ((c = getchar ()) != '\n') + ; /* discard newline */ + + if (i == 0) + return ""; + else + { + buf[delimiter == '%' && buf[i - 2] == ';' ? i - 2 : i - 1] = '\0'; + return buf; + } + } + else + buf[i++] = '%'; + } + else if (i >= size) /* Yikes, time to grow the buffer! */ + { + char *temp = new char[size *= 2]; + int j; + + for (j = 0; j < i; j++) + temp[j] = buf[j]; + + buf = temp; + } + buf[i] = c; + } + + return 0; /* Problem here. */ +} + +/* Stores any C text that must be included verbatim into the + generated code output. */ + +char * +Key_List::save_include_src (void) +{ + int c; + + if ((c = getchar ()) != '%') + ungetc (c, stdin); + else if ((c = getchar ()) != '{') + ACE_ERROR ((LM_ERROR, "internal error, %c != '{' on line %d in file %s%a", c, __LINE__, __FILE__, 1)); + else + return get_special_input ('}'); + return ""; +} + +/* Determines from the input file whether the user wants to build a table + from a user-defined struct, or whether the user is content to simply + use the default array of keys. */ + +char * +Key_List::get_array_type (void) +{ + return get_special_input ('%'); +} + +/* strcspn - find length of initial segment of S consisting entirely + ANSI string package, when GNU libc comes out I'll replace this...). */ + +inline int +Key_List::strcspn (const char *s, const char *reject) +{ + const char *scan; + const char *rej_scan; + int count = 0; + + for (scan = s; *scan; scan++) + { + + for (rej_scan = reject; *rej_scan; rej_scan++) + if (*scan == *rej_scan) + return count; + + count++; + } + + return count; +} + +/* Sets up the Return_Type, the Struct_Tag type and the Array_Type + based upon various user Options. */ + +void +Key_List::set_output_types (void) +{ + if (option[TYPE] && !(array_type = get_array_type ())) + return; /* Something's wrong, bug we'll catch it later on.... */ + else if (option[TYPE]) /* Yow, we've got a user-defined type... */ + { + int struct_tag_length = strcspn (array_type, "{\n\0"); + + if (option[POINTER]) /* And it must return a pointer... */ + { + return_type = new char[struct_tag_length + 2]; + strncpy (return_type, array_type, struct_tag_length); + return_type[struct_tag_length] = '*'; + return_type[struct_tag_length + 1] = '\0'; + } + + struct_tag = new char[struct_tag_length + 1]; + strncpy (struct_tag, array_type, struct_tag_length); + struct_tag[struct_tag_length] = '\0'; + } + else if (option[POINTER]) /* Return a char *. */ + return_type = default_array_type; +} + +/* Reads in all keys from standard input and creates a linked list pointed + to by Head. This list is then quickly checked for ``links,'' i.e., + unhashable elements possessing identical key sets and lengths. */ + +void +Key_List::read_keys (void) +{ + include_src = save_include_src (); + set_output_types (); + + ACE_Read_Buffer input (stdin); + + char *ptr = input.read ('\n'); + + if (ptr == 0) + // Oops, problem with the input file. + ACE_ERROR ((LM_ERROR, "No words in input file, did you forget to prepend %s" + " or use -t accidentally?\n%a", "%%", 1)); + + /* Read in all the keywords from the input file. */ + else + { + const char *delimiter = option.get_delimiter (); + List_Node *temp, *trail = 0; + + head = new List_Node (ptr, strcspn (ptr, delimiter)); + + for (temp = head; + (ptr = input.read ('\n')) && strcmp (ptr, "%%"); + temp = temp->next) + { + temp->next = new List_Node (ptr, strcspn (ptr, delimiter)); + total_keys++; + } + + /* See if any additional source code is included at end of this file. */ + if (ptr) + additional_code = 1; + + /* Hash table this number of times larger than keyword number. */ + int table_size = (list_len = total_keys) * TABLE_MULTIPLE; + +#if LARGE_STACK_ARRAYS + /* By allocating the memory here we save on dynamic allocation overhead. + Table must be a power of 2 for the hash function scheme to work. */ + List_Node *table[ACE_POW (table_size)]; +#else + // Note: we don't use new, because that invokes a custom operator new. + int malloc_size = ACE_POW (table_size) * sizeof(List_Node*); + if (malloc_size == 0) malloc_size = 1; + List_Node **table = (List_Node**)malloc(malloc_size); + if (table == NULL) + abort (); +#endif + + /* Make large hash table for efficiency. */ + Hash_Table found_link (table, table_size); + + /* Test whether there are any links and also set the maximum length of + an identifier in the keyword list. */ + + for (temp = head; temp; temp = temp->next) + { + List_Node *ptr = found_link (temp, option[NOLENGTH]); + + /* Check for links. We deal with these by building an equivalence class + of all duplicate values (i.e., links) so that only 1 keyword is + representative of the entire collection. This *greatly* simplifies + processing during later stages of the program. */ + + if (ptr) + { + total_duplicates++; + list_len--; + trail->next = temp->next; + temp->link = ptr->link; + ptr->link = temp; + + /* Complain if user hasn't enabled the duplicate option. */ + if (!option[DUP] || option[DEBUG]) + ACE_ERROR ((LM_ERROR, "Key link: \"%s\" = \"%s\", with key set \"%s\".\n", + temp->key, ptr->key, temp->char_set)); + } + else + trail = temp; + + /* Update minimum and maximum keyword length, if needed. */ + if (max_key_len < temp->length) + max_key_len = temp->length; + if (min_key_len > temp->length) + min_key_len = temp->length; + } + +#if !LARGE_STACK_ARRAYS + free (table); +#endif + + /* Exit program if links exists and option[DUP] not set, since we can't continue */ + if (total_duplicates) + ACE_ERROR ((LM_ERROR, option[DUP] + ? "%d input keys have identical hash values, examine output carefully...\n" + : "%d input keys have identical hash values,\ntry different key positions or use option -D.\n%a", total_duplicates, 1)); + if (option[ALLCHARS]) + option.set_keysig_size (max_key_len); + } +} + +/* Recursively merges two sorted lists together to form one sorted list. The + ordering criteria is by frequency of occurrence of elements in the key set + or by the hash value. This is a kludge, but permits nice sharing of + almost identical code without incurring the overhead of a function + call comparison. */ + +List_Node * +Key_List::merge (List_Node *list1, List_Node *list2) +{ + if (!list1) + return list2; + else if (!list2) + return list1; + else if (occurrence_sort && list1->occurrence < list2->occurrence + || hash_sort && list1->hash_value > list2->hash_value) + { + list2->next = merge (list2->next, list1); + return list2; + } + else + { + list1->next = merge (list1->next, list2); + return list1; + } +} + +/* Applies the merge sort algorithm to recursively sort the key list by + frequency of occurrence of elements in the key set. */ + +List_Node * +Key_List::merge_sort (List_Node *a_head) +{ + if (!a_head || !a_head->next) + return a_head; + else + { + List_Node *middle = a_head; + List_Node *temp = a_head->next->next; + + while (temp) + { + temp = temp->next; + middle = middle->next; + if (temp) + temp = temp->next; + } + + temp = middle->next; + middle->next = 0; + return merge (merge_sort (a_head), merge_sort (temp)); + } +} + +/* Returns the frequency of occurrence of elements in the key set. */ + +inline int +Key_List::get_occurrence (List_Node *ptr) +{ + int value = 0; + + for (char *temp = ptr->char_set; *temp; temp++) + value += Vectors::occurrences[*temp]; + + return value; +} + +/* Enables the index location of all key set elements that are now + determined. */ + +inline void +Key_List::set_determined (List_Node *ptr) +{ + for (char *temp = ptr->char_set; *temp; temp++) + determined[*temp] = 1; +} + +/* Returns TRUE if PTR's key set is already completely determined. */ + +inline int +Key_List::already_determined (List_Node *ptr) +{ + int is_determined = 1; + + for (char *temp = ptr->char_set; is_determined && *temp; temp++) + is_determined = determined[*temp]; + + return is_determined; +} + +/* Reorders the table by first sorting the list so that frequently occuring + keys appear first, and then the list is reorded so that keys whose values + are already determined will be placed towards the front of the list. This + helps prune the search time by handling inevitable collisions early in the + search process. See Cichelli's paper from Jan 1980 JACM for details.... */ + +void +Key_List::reorder (void) +{ + List_Node *ptr; + + for (ptr = head; ptr; ptr = ptr->next) + ptr->occurrence = get_occurrence (ptr); + + occurrence_sort = !(hash_sort = 0); /* Pretty gross, eh?! */ + + for (ptr = head = merge_sort (head); ptr->next; ptr = ptr->next) + { + set_determined (ptr); + + if (already_determined (ptr->next)) + continue; + else + { + List_Node *trail_ptr = ptr->next; + List_Node *run_ptr = trail_ptr->next; + + for (; run_ptr; run_ptr = trail_ptr->next) + { + + if (already_determined (run_ptr)) + { + trail_ptr->next = run_ptr->next; + run_ptr->next = ptr->next; + ptr = ptr->next = run_ptr; + } + else + trail_ptr = run_ptr; + } + } + } +} + +/* Outputs the maximum and minimum hash values. Since the + list is already sorted by hash value all we need to do is + find the final item! */ + +void +Key_List::output_min_max () +{ + List_Node *temp; + for (temp = head; temp->next; temp = temp->next) + ; + + min_hash_value = head->hash_value; + max_hash_value = temp->hash_value; + + if (!option[ENUM]) + printf ("\n#define TOTAL_KEYWORDS %d\n#define MIN_WORD_LENGTH %d" + "\n#define MAX_WORD_LENGTH %d\n#define MIN_HASH_VALUE %d" + "\n#define MAX_HASH_VALUE %d\n#define HASH_VALUE_RANGE %d" + "\n#define DUPLICATES %d\n\n", + total_keys, min_key_len, max_key_len, min_hash_value, + max_hash_value, max_hash_value - min_hash_value + 1, + total_duplicates ? total_duplicates + 1 : 0); + else if (option[GLOBAL]) + printf ("enum\n{\n" + " TOTAL_KEYWORDS = %d,\n" + " MIN_WORD_LENGTH = %d,\n" + " MAX_WORD_LENGTH = %d,\n" + " MIN_HASH_VALUE = %d,\n" + " MAX_HASH_VALUE = %d,\n" + " HASH_VALUE_RANGE = %d,\n" + " DUPLICATES = %d\n};\n\n", + total_keys, min_key_len, max_key_len, min_hash_value, + max_hash_value, max_hash_value - min_hash_value + 1, + total_duplicates ? total_duplicates + 1 : 0); +} + +/* Generates the output using a C switch. This trades increased + search time for decreased table space (potentially *much* less + space for sparse tables). It the user has specified their own + struct in the keyword file *and* they enable the POINTER option we + have extra work to do. The solution here is to maintain a local + static array of user defined struct's, as with the + Output_Lookup_Function. Then we use for switch statements to + perform either a strcmp or strncmp, returning 0 if the str fails to + match, and otherwise returning a pointer to appropriate index + location in the local static array. */ + +void +Key_List::output_switch (void) +{ + char *comp_buffer; + List_Node *curr = head; + int pointer_and_type_enabled = option[POINTER] && option[TYPE]; + int total_switches = option.get_total_switches (); + int switch_size = keyword_list_length () / total_switches; + + if (pointer_and_type_enabled) + { +#if defined (__GNUG__) + comp_buffer = (char *) alloca (strlen ("charmap[*str] == *resword->%s && !strncasecmp (str + 1, resword->%s + 1, len - 1)") + + 2 * strlen (option.get_key_name ()) + 1); +#else + comp_buffer = new char [strlen ("charmap[*str] == *resword->%s && !strncasecmp (str + 1, resword->%s + 1, len - 1)") + + 2 * strlen (option.get_key_name ()) + 1]; +#endif + if (option[COMP]) + sprintf (comp_buffer, "%s == *resword->%s && !%s (str + 1, resword->%s + 1, len - 1)", + option[STRCASECMP] ? "charmap[*str]" : "*str", option.get_key_name (), + option[STRCASECMP] ? "strncasecmp" : "strncmp", option.get_key_name ()); + else + sprintf (comp_buffer, "%s == *resword->%s && !%s (str + 1, resword->%s + 1)", + option[STRCASECMP] ? "charmap[*str]" : "*str", option.get_key_name (), + option[STRCASECMP] ? "strcasecmp" : "strcmp", option.get_key_name ()); + } + else + { + if (option[COMP]) + comp_buffer = option[STRCASECMP] + ? "charmap[*str] == *resword && !strncasecmp (str + 1, resword + 1, len - 1)" + : "*str == *resword && !strncmp (str + 1, resword + 1, len - 1)"; + else + comp_buffer = option[STRCASECMP] + ? "charmap[*str] == *resword && !strcasecmp (str + 1, resword + 1, len - 1)" + : "*str == *resword && !strcmp (str + 1, resword + 1, len - 1)"; + } + if (!option[OPTIMIZE]) + printf (" if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH)\n {\n"); + printf (" register int key = %s (str, len);\n\n", option.get_hash_name ()); + if (!option[OPTIMIZE]) + printf (" if (key <= MAX_HASH_VALUE && key >= MIN_HASH_VALUE)\n"); + + printf (" {\n"); + + /* Properly deal with user's who request multiple switch statements. */ + + while (curr) + { + List_Node *temp = curr; + int lowest_case_value = curr->hash_value; + int number_of_cases = 0; + + /* Figure out a good cut point to end this switch. */ + + for (; temp && ++number_of_cases < switch_size; temp = temp->next) + if (temp->next && temp->hash_value == temp->next->hash_value) + while (temp->next && temp->hash_value == temp->next->hash_value) + temp = temp->next; + + if (temp && total_switches != 1) + printf (" if (key <= %d)\n {\n", temp->hash_value); + else + printf (" {\n"); + + /* Output each keyword as part of a switch statement indexed by hash value. */ + + if (option[POINTER] || option[DUP]) + { + int i = 0; + + printf (" %s%s *resword; %s\n\n", + option[CONST] ? "const " : "", + pointer_and_type_enabled ? struct_tag : "char", + option[LENTABLE] && !option[DUP] ? "int key_len;" : ""); + if (total_switches == 1) + { + printf (" switch (key)\n {\n"); + lowest_case_value = 0; + } + else + printf (" switch (key - %d)\n {\n", lowest_case_value); + + for (temp = curr; temp && ++i <= number_of_cases; temp = temp->next) + { + printf (" case %*d:", field_width, temp->hash_value - lowest_case_value); + if (option[DEBUG]) + printf (" /* hash value = %4d, keyword = \"%s\" */", temp->hash_value, temp->key); + putchar ('\n'); + + /* Handle `natural links,' i.e., those that occur statically. */ + + if (temp->link) + { + List_Node *links; + + for (links = temp; links; links = links->link) + { + if (pointer_and_type_enabled) + printf (" resword = &wordlist[%d];\n", links->index); + else + printf (" resword = \"%s\";\n", links->key); + printf (" if (%s) return resword;\n", comp_buffer); + } + } + /* Handle unresolved duplicate hash values. These are guaranteed + to be adjacent since we sorted the keyword list by increasing + hash values. */ + if (temp->next && temp->hash_value == temp->next->hash_value) + { + + for ( ; temp->next && temp->hash_value == temp->next->hash_value; + temp = temp->next) + { + if (pointer_and_type_enabled) + printf (" resword = &wordlist[%d];\n", temp->index); + else + printf (" resword = \"%s\";\n", temp->key); + printf (" if (%s) return resword;\n", comp_buffer); + } + if (pointer_and_type_enabled) + printf (" resword = &wordlist[%d];\n", temp->index); + else + printf (" resword = \"%s\";\n", temp->key); + printf (" return %s ? resword : 0;\n", comp_buffer); + } + else if (temp->link) + printf (" return 0;\n"); + else + { + if (pointer_and_type_enabled) + printf (" resword = &wordlist[%d];", temp->index); + else + printf (" resword = \"%s\";", temp->key); + if (option[LENTABLE] && !option[DUP]) + printf (" key_len = %d;", temp->length); + printf (" break;\n"); + } + } + printf (" default: return 0;\n }\n"); + if (option[OPTIMIZE]) + printf (" return resword;\n"); + else + { + printf (option[LENTABLE] && !option[DUP] + ? " if (len == key_len && %s)\n return resword;\n" + : " if (%s)\n return resword;\n", comp_buffer); + printf (" return 0;\n"); + } + printf (" }\n"); + curr = temp; + } + else /* Nothing special required here. */ + { + int i = 0; + printf (" char *s;\n\n switch (key - %d)\n {\n", + lowest_case_value); + + for (temp = curr; temp && ++i <= number_of_cases; temp = temp->next) + if (option[LENTABLE]) + printf (" case %*d: if (len == %d) s = \"%s\"; else return 0; break;\n", + field_width, temp->hash_value - lowest_case_value, + temp->length, temp->key); + else + printf (" case %*d: s = \"%s\"; break;\n", + field_width, temp->hash_value - lowest_case_value, temp->key); + + printf (" default: return 0;\n }\n "); + if (option[COMP]) + printf ("return %s == *s && !%s;\n }\n", + option[STRCASECMP] ? "charmap[*str]" : "*str", + option[STRCASECMP] ? "strncasecmp (s + 1, str + 1, len - 1)" : "strcmp (s + 1, str + 1)"); + else + printf ("return %s == *s && !%s;\n }\n", + option[STRCASECMP] ? "charmap[*str]" : "*str", + option[STRCASECMP] ? "strcasecmp (s + 1, str + 1, len - 1)" : "strcmp (s + 1, str + 1)"); + curr = temp; + } + } + printf (" }\n %s\n}\n", option[OPTIMIZE] ? "" : "}\n return 0;"); +} + +/* Prints out a table of keyword lengths, for use with the + comparison code in generated function ``in_word_set.'' */ + +void +Key_List::output_keylength_table (void) +{ + const int max_column = 15; + int index = 0; + int column = 0; + char *indent = option[GLOBAL] ? "" : " "; + List_Node *temp; + + if (!option[DUP] && !option[SWITCH]) + { + printf ("\n%sstatic %sunsigned %s lengthtable[] =\n%s%s{\n ", + indent, option[CONST] ? "const " : "", + max_key_len <= UCHAR_MAX ? "char" : (max_key_len <= USHRT_MAX ? "short" : "long"), + indent, indent); + + for (temp = head; temp; temp = temp->next, index++) + { + + if (index < temp->hash_value) + for ( ; index < temp->hash_value; index++) + printf ("%3d,%s", 0, ++column % (max_column - 1) ? "" : "\n "); + + printf ("%3d,%s", temp->length, ++column % (max_column - 1 ) ? "" : "\n "); + } + + printf ("\n%s%s};\n", indent, indent); + } +} +/* Prints out the array containing the key words for the Gen_Perf + hash function. */ + +void +Key_List::output_keyword_table (void) +{ + char *l_brace = *head->rest ? "{" : ""; + char *r_brace = *head->rest ? "}," : ""; + char *indent = option[GLOBAL] ? "" : " "; + int index = 0; + List_Node *temp; + + printf ("%sstatic %s%swordlist[] =\n%s%s{\n", + indent, option[CONST] ? "const " : "", struct_tag, indent, indent); + + /* Skip over leading blank entries if there are no duplicates. */ + + if (0 < head->hash_value) + printf (" "); + for (int column = 1; index < head->hash_value; index++, column++) + printf ("%s\"\",%s %s", l_brace, r_brace, column % 9 ? "" : "\n "); + if (0 < head->hash_value && column % 10) + printf ("\n"); + + /* Generate an array of reserved words at appropriate locations. */ + + for (temp = head ; temp; temp = temp->next, index++) + { + temp->index = index; + + if (!option[SWITCH] && (total_duplicates == 0 || !option[DUP]) && index < temp->hash_value) + { + int column; + + printf (" "); + + for (column = 1; index < temp->hash_value; index++, column++) + printf ("%s\"\",%s %s", l_brace, r_brace, column % 9 ? "" : "\n "); + + if (column % 10) + printf ("\n"); + else + { + printf ("%s\"%s\", %s%s", l_brace, temp->key, temp->rest, r_brace); + if (option[DEBUG]) + printf (" /* hash value = %d, index = %d */", temp->hash_value, temp->index); + putchar ('\n'); + continue; + } + } + + printf (" %s\"%s\", %s%s", l_brace, temp->key, temp->rest, r_brace); + if (option[DEBUG]) + printf (" /* hash value = %d, index = %d */", temp->hash_value, temp->index); + putchar ('\n'); + + /* Deal with links specially. */ + if (temp->link) + for (List_Node *links = temp->link; links; links = links->link) + { + links->index = ++index; + printf (" %s\"%s\", %s%s", l_brace, links->key, links->rest, r_brace); + if (option[DEBUG]) + printf (" /* hash value = %d, index = %d */", links->hash_value, links->index); + putchar ('\n'); + } + } + printf ("%s%s};\n\n", indent, indent); +} + +/* Generates C code for the hash function that returns the + proper encoding for each key word. */ + +void +Key_List::output_hash_function (void) +{ + const int max_column = 10; + int count = max_hash_value; + + /* Calculate maximum number of digits required for MAX_HASH_VALUE. */ + + for (field_width = 2; (count /= 10) > 0; field_width++) + ; + + if (option[GNU]) + printf ("#ifdef __GNUC__\ninline\n#endif\n"); + + if (option[C]) + printf ("static "); + printf ("unsigned int\n"); + if (option[CPLUSPLUS]) + printf ("%s::", option.get_class_name ()); + + printf (option[ANSI] + ? "%s (register const char *str, register int len)\n{\n static %sunsigned %s asso_values[] =\n {" + : "%s (str, len)\n register char *str;\n register int unsigned len;\n{\n static %sunsigned %s asso_values[] =\n {", + option.get_hash_name (), option[CONST] ? "const " : "", + max_hash_value <= UCHAR_MAX ? "char" : (max_hash_value <= USHRT_MAX ? "short" : "int")); + + for (count = 0; count < ALPHA_SIZE; ++count) + { + if (!(count % max_column)) + printf ("\n "); + + printf ("%*d,", + field_width, + Vectors::occurrences[count] ? Vectors::asso_values[count] : max_hash_value + 1); + } + + /* Optimize special case of ``-k 1,$'' */ + if (option[DEFAULTCHARS]) + { + if (option[STRCASECMP]) + printf ("\n };\n return %sasso_values[charmap[str[len - 1]]] + asso_values[charmap[str[0]]];\n}\n\n", + option[NOLENGTH] ? "" : "len + "); + else + printf ("\n };\n return %sasso_values[str[len - 1]] + asso_values[str[0]];\n}\n\n", + option[NOLENGTH] ? "" : "len + "); + } + else + { + int key_pos; + + option.reset (); + + /* Get first (also highest) key position. */ + key_pos = option.get (); + + /* We can perform additional optimizations here. */ + if (!option[ALLCHARS] && key_pos <= min_key_len) + { + printf ("\n };\n return %s", option[NOLENGTH] ? "" : "len + "); + + for (; key_pos != WORD_END; ) + { + printf (option[STRCASECMP] ? "asso_values[charmap[str[%d]]]" : "asso_values[str[%d]]", key_pos - 1); + if ((key_pos = option.get ()) != EOS) + printf (" + "); + else + break; + } + + printf ("%s;\n}\n\n", key_pos == WORD_END + ? (option[STRCASECMP] ? "asso_values[charmap[str[len - 1]]]" : "asso_values[str[len - 1]]") + : ""); + } + + /* We've got to use the correct, but brute force, technique. */ + else + { + printf ("\n };\n register int hval = %s;\n\n switch (%s)\n {\n default:\n", + option[NOLENGTH] ? "0" : "len", option[NOLENGTH] ? "len" : "hval"); + + /* User wants *all* characters considered in hash. */ + if (option[ALLCHARS]) + { + int i; + + /* Break these options up for speed (gee, is this misplaced efficiency or what?! */ + if (option[STRCASECMP]) + + for (i = max_key_len; i > 0; i--) + printf (" case %d:\n hval += asso_values[charmap[str[%d]]];\n", i, i - 1); + + else + + for (i = max_key_len; i > 0; i--) + printf (" case %d:\n hval += asso_values[str[%d]];\n", i, i - 1); + + printf (" }\n return hval;\n}\n\n"); + } + else /* do the hard part... */ + { + count = key_pos + 1; + + do + { + + while (--count > key_pos) + printf (" case %d:\n", count); + + printf (option[STRCASECMP] + ? " case %d:\n hval += asso_values[charmap[str[%d]]];\n" + : " case %d:\n hval += asso_values[str[%d]];\n", + key_pos, key_pos - 1); + } + while ((key_pos = option.get ()) != EOS && key_pos != WORD_END); + + printf (" }\n return hval%s;\n}\n\n", + key_pos == WORD_END + ? (option[STRCASECMP] ? " + asso_values[charmap[str[len - 1]]]" : " + asso_values[str[len - 1]]") + : ""); + } + } + } +} + +/* Generates the large, sparse table that maps hash values into + the smaller, contiguous range of the keyword table. */ + +void +Key_List::output_lookup_array (void) +{ + if (total_duplicates > 0) + { + const int DEFAULT_VALUE = -1; + + struct duplicate_entry + { + int hash_value; /* Hash value for this particular duplicate set. */ + int index; /* Index into the main keyword storage array. */ + int count; /* Number of consecutive duplicates at this index. */ + }; +#if LARGE_STACK_ARRAYS + duplicate_entry duplicates[total_duplicates]; + int lookup_array[max_hash_value + 1]; +#else + // Note: we don't use new, because that invokes a custom operator new. + duplicate_entry *duplicates = (duplicate_entry*) + malloc (total_duplicates * sizeof(duplicate_entry)); + int *lookup_array = (int*)malloc(sizeof(int) * (max_hash_value + 1)); + if (duplicates == NULL || lookup_array == NULL) + abort(); +#endif + duplicate_entry *dup_ptr = duplicates; + int *lookup_ptr = lookup_array + max_hash_value + 1; + + while (lookup_ptr > lookup_array) + *--lookup_ptr = DEFAULT_VALUE; + + for (List_Node *temp = head; temp; temp = temp->next) + { + int hash_value = temp->hash_value; + lookup_array[hash_value] = temp->index; + if (option[DEBUG]) + fprintf (stderr, "keyword = %s, index = %d\n", temp->key, temp->index); + if (!temp->link && + (!temp->next || hash_value != temp->next->hash_value)) + continue; +#if LARGE_STACK_ARRAYS + *dup_ptr = (duplicate_entry) { hash_value, temp->index, 1 }; +#else + duplicate_entry _dups; + _dups.hash_value = hash_value; + _dups.index = temp->index; + _dups.count = 1; + *dup_ptr = _dups; +#endif + + for (List_Node *ptr = temp->link; ptr; ptr = ptr->link) + { + dup_ptr->count++; + if (option[DEBUG]) + fprintf (stderr, "static linked keyword = %s, index = %d\n", ptr->key, ptr->index); + } + + while (temp->next && hash_value == temp->next->hash_value) + { + temp = temp->next; + dup_ptr->count++; + if (option[DEBUG]) + fprintf (stderr, "dynamic linked keyword = %s, index = %d\n", temp->key, temp->index); + + for (List_Node *ptr = temp->link; ptr; ptr = ptr->link) + { + dup_ptr->count++; + if (option[DEBUG]) + fprintf (stderr, "static linked keyword = %s, index = %d\n", ptr->key, ptr->index); + } + } + dup_ptr++; + } + + while (--dup_ptr >= duplicates) + { + if (option[DEBUG]) + fprintf (stderr, "dup_ptr[%d]: hash_value = %d, index = %d, count = %d\n", + dup_ptr - duplicates, dup_ptr->hash_value, dup_ptr->index, dup_ptr->count); + + /* Start searching for available space towards the right part of the lookup array. */ + int i; + for (i = dup_ptr->hash_value; i < max_hash_value; i++) + if (lookup_array[i] == DEFAULT_VALUE && lookup_array[i + 1] == DEFAULT_VALUE) + { + lookup_array[i] = -dup_ptr->index; + lookup_array[i + 1] = -dup_ptr->count; + lookup_array[dup_ptr->hash_value] = max_hash_value + (i - dup_ptr->hash_value); + break; + } + + /* If we didn't find it to the right look to the left instead... */ + if (i == max_hash_value) + { + + for (i = dup_ptr->hash_value; i > 0; i--) + if (lookup_array[i] == DEFAULT_VALUE && lookup_array[i - 1] == DEFAULT_VALUE) + { + lookup_array[i - 1] = -dup_ptr->index; + lookup_array[i] = -dup_ptr->count; + lookup_array[dup_ptr->hash_value] = -(max_hash_value + (dup_ptr->hash_value - i + 1)); + break; + } + + /* We are in *big* trouble if this happens! */ + assert (i != 0); + } + } + + int max = INT_MIN; + lookup_ptr = lookup_array + max_hash_value + 1; + while (lookup_ptr > lookup_array) + { + int val = abs (*--lookup_ptr); + if (max < val) + max = val; + } + + char *indent = option[GLOBAL] ? "" : " "; + printf ("%sstatic %s%s lookup[] =\n%s%s{\n ", indent, option[CONST] ? "const " : "", + max <= SCHAR_MAX ? "char" : (max <= USHRT_MAX ? "short" : "int"), + indent, indent); + + int count = max; + + /* Calculate maximum number of digits required for MAX_HASH_VALUE. */ + + for (field_width = 2; (count /= 10) > 0; field_width++) + ; + + const int max_column = 15; + int column = 0; + + for (lookup_ptr = lookup_array; + lookup_ptr < lookup_array + max_hash_value + 1; + lookup_ptr++) + printf ("%*d,%s", field_width, *lookup_ptr, ++column % (max_column - 1) ? "" : "\n "); + + printf ("\n%s%s};\n\n", indent, indent); +#if !LARGE_STACK_ARRAYS + free (duplicates); + free (lookup_array); +#endif + } +} +/* Generates C code to perform the keyword lookup. */ + +void +Key_List::output_lookup_function (void) +{ + if (!option[OPTIMIZE]) + printf (" if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH)\n {\n"); + printf (" register int key = %s (str, len);\n\n", option.get_hash_name ()); + if (!option[OPTIMIZE]) + printf (" if (key <= MAX_HASH_VALUE && key >= MIN_HASH_VALUE)\n"); + printf (" {\n"); + + if (option[DUP] && total_duplicates > 0) + { + printf (" register int index = lookup[key];\n\n" + " if (index >= 0 && index < MAX_HASH_VALUE)\n"); + if (option[OPTIMIZE]) + printf (" return %swordlist[index];\n", option[TYPE] && option[POINTER] ? "&" : ""); + else + { + printf (" {\n" + " register %schar *s = wordlist[index]", option[CONST] ? "const " : ""); + if (array_type != default_array_type) + printf (".%s", option.get_key_name ()); + + printf (";\n\n if (%s%s == *s && !%s)\n return %s;\n }\n", + option[LENTABLE] ? "len == lengthtable[key]\n && " : "", + option[STRCASECMP] ? "charmap[*str]" : "*str", + option[COMP] ? (option[STRCASECMP] ? "strncasecmp (str + 1, s + 1, len - 1)" : "strncmp (str + 1, s + 1, len - 1)") + : (option[STRCASECMP] ? "strcasecmp (str + 1, s + 1)" : "strcmp (str + 1, s + 1)"), + option[TYPE] && option[POINTER] ? "&wordlist[index]" : "s"); + printf (" else if (index < 0 && index >= -MAX_HASH_VALUE)\n" + " return 0;\n"); + } + printf (" else\n {\n" + " register int offset = key + index + (index > 0 ? -MAX_HASH_VALUE : MAX_HASH_VALUE);\n" + " register %s%s*base = &wordlist[-lookup[offset]];\n" + " register %s%s*ptr = base + -lookup[offset + 1];\n\n" + " while (--ptr >= base)\n ", + option[CONST] ? "const " : "", struct_tag, + option[CONST] ? "const " : "", struct_tag); + if (array_type != default_array_type) + { + if (option[COMP]) + printf ("if (%s == *ptr->%s && !%s (str + 1, ptr->%s + 1, len - 1", + option[STRCASECMP] ? "charmap[*str]" : "*str", option.get_key_name (), + option[STRCASECMP] ? "strncasecmp" : "strncmp", option.get_key_name ()); + else + printf ("if (%s == *ptr->%s && !%s (str + 1, ptr->%s + 1", + option[STRCASECMP] ? "charmap[*str]" : "*str", option.get_key_name (), + option[STRCASECMP] ? "strcasecmp" : "strcmp", option.get_key_name ()); + } + else + printf (option[STRCASECMP] ? "if (charmap[*str] == **ptr && !%s" : "if (*str == **ptr && !%s", + option[COMP] + ? (option[STRCASECMP] ? "strncasecmp (str + 1, *ptr + 1, len - 1" : "strncmp (str + 1, *ptr + 1, len - 1") + : (option[STRCASECMP] ? "strcasecmp (str + 1, *ptr + 1" : "strcmp (str + 1, *ptr + 1")); + printf ("))\n return %sptr;" + "\n }\n }\n %s\n}\n", array_type == + default_array_type ? "*" : "", option[OPTIMIZE] ? "" : "}\n return 0;"); + } + else + { + if (option[OPTIMIZE]) + printf (" return %swordlist[key]", option[TYPE] && option[POINTER] ? "&" : ""); + else + { + printf (" register %schar *s = wordlist[key]", option[CONST] ? "const " : ""); + + if (array_type != default_array_type) + printf (".%s", option.get_key_name ()); + + printf (";\n\n if (%s%s == *s && !%s)\n return %s", + option[LENTABLE] ? "len == lengthtable[key]\n && " : "", + option[STRCASECMP] ? "charmap[*str]" : "*str", + option[COMP] + ? (option[STRCASECMP] ? "strncasecmp (str + 1, s + 1, len - 1)" : "strncmp (str + 1, s + 1, len - 1)") + : (option[STRCASECMP] ? "strcasecmp (str + 1, s + 1)" : "strcmp (str + 1, s + 1)"), + option[TYPE] && option[POINTER] ? "&wordlist[key]" : "s"); + } + printf (";\n }\n %s\n}\n", option[OPTIMIZE] ? "" : "}\n return 0;"); + } +} + +/* Output the table and the functions that map upper case into lower case! */ + +void +Key_List::output_strcasecmp (void) +{ + printf ("%s", + "/* This array is designed for mapping upper and lower case letter\n" + " * together for a case independent comparison. The mappings are\n" + " * based upon ascii character sequences.\n */" + "static char charmap[] = {\n" + " '\\000', '\\001', '\\002', '\\003', '\\004', '\\005', '\\006', '\\007',\n" + " '\\010', '\\011', '\\012', '\\013', '\\014', '\\015', '\\016', '\\017',\n" + " '\\020', '\\021', '\\022', '\\023', '\\024', '\\025', '\\026', '\\027',\n" + " '\\030', '\\031', '\\032', '\\033', '\\034', '\\035', '\\036', '\\037',\n" + " '\\040', '\\041', '\\042', '\\043', '\\044', '\\045', '\\046', '\\047',\n" + " '\\050', '\\051', '\\052', '\\053', '\\054', '\\055', '\\056', '\\057',\n" + " '\\060', '\\061', '\\062', '\\063', '\\064', '\\065', '\\066', '\\067',\n" + " '\\070', '\\071', '\\072', '\\073', '\\074', '\\075', '\\076', '\\077',\n" + " '\\100', '\\141', '\\142', '\\143', '\\144', '\\145', '\\146', '\\147',\n" + " '\\150', '\\151', '\\152', '\\153', '\\154', '\\155', '\\156', '\\157',\n" + " '\\160', '\\161', '\\162', '\\163', '\\164', '\\165', '\\166', '\\167',\n" + " '\\170', '\\171', '\\172', '\\133', '\\134', '\\135', '\\136', '\\137',\n" + " '\\140', '\\141', '\\142', '\\143', '\\144', '\\145', '\\146', '\\147',\n" + " '\\150', '\\151', '\\152', '\\153', '\\154', '\\155', '\\156', '\\157',\n" + " '\\160', '\\161', '\\162', '\\163', '\\164', '\\165', '\\166', '\\167',\n" + " '\\170', '\\171', '\\172', '\\173', '\\174', '\\175', '\\176', '\\177',\n" + " '\\200', '\\201', '\\202', '\\203', '\\204', '\\205', '\\206', '\\207',\n" + " '\\210', '\\211', '\\212', '\\213', '\\214', '\\215', '\\216', '\\217',\n" + " '\\220', '\\221', '\\222', '\\223', '\\224', '\\225', '\\226', '\\227',\n" + " '\\230', '\\231', '\\232', '\\233', '\\234', '\\235', '\\236', '\\237',\n" + " '\\240', '\\241', '\\242', '\\243', '\\244', '\\245', '\\246', '\\247',\n" + " '\\250', '\\251', '\\252', '\\253', '\\254', '\\255', '\\256', '\\257',\n" + " '\\260', '\\261', '\\262', '\\263', '\\264', '\\265', '\\266', '\\267',\n" + " '\\270', '\\271', '\\272', '\\273', '\\274', '\\275', '\\276', '\\277',\n" + " '\\300', '\\341', '\\342', '\\343', '\\344', '\\345', '\\346', '\\347',\n" + " '\\350', '\\351', '\\352', '\\353', '\\354', '\\355', '\\356', '\\357',\n" + " '\\360', '\\361', '\\362', '\\363', '\\364', '\\365', '\\366', '\\367',\n" + " '\\370', '\\371', '\\372', '\\333', '\\334', '\\335', '\\336', '\\337',\n" + " '\\340', '\\341', '\\342', '\\343', '\\344', '\\345', '\\346', '\\347',\n" + " '\\350', '\\351', '\\352', '\\353', '\\354', '\\355', '\\356', '\\357',\n" + " '\\360', '\\361', '\\362', '\\363', '\\364', '\\365', '\\366', '\\367',\n" + " '\\370', '\\371', '\\372', '\\373', '\\374', '\\375', '\\376', '\\377',\n};\n\nstatic int\n"); + if (option[COMP]) + { + printf ("%s", option[ANSI] + ? "strncasecmp (register char *s1, register char *s2, register int n)" + : "strncasecmp (s1, s2, n)\n register char *s1, *s2;\n register int n;"); + printf ("\n{\n register char *cm = charmap;\n\n while (--n >= 0 && cm[*s1] == cm[*s2++])\n" + " if (*s1++ == '\\0')\n return 0;\n" + "\n return n < 0 ? 0 : cm[*s1] - cm[*--s2];\n}\n\n"); + } + else + { + printf ("%s", option[ANSI] + ? "strcasecmp (register char *s1, register char *s2)" + : "strcasecmp (s1, s2)\n register char *s1, *s2;"); + printf ("\n{\n register char *cm = charmap;\n\n while (cm[*s1] == cm[*s2++])\n" + " if (*s1++ == '\\0')\n return 0;\n" + "\n return cm[*s1] - cm[*--s2];\n}\n\n"); + } +} + +/* Generates the hash function and the key word recognizer function + based upon the user's Options. */ + +void +Key_List::output (void) +{ + printf ("%s\n", include_src); + + if (option[TYPE] && !option[NOTYPE]) /* Output type declaration now, reference it later on.... */ + printf ("%s;\n", array_type); + + output_min_max (); + + if (option[STRCASECMP]) + output_strcasecmp (); + if (option[CPLUSPLUS]) + printf ("class %s\n{\nprivate:\n" + " static unsigned int hash (const char *str, int len);\npublic:\n" + " static %s%s%s (const char *str, int len);\n};\n\n", + option.get_class_name (), option[CONST] ? "const " : "", + return_type, option.get_function_name ()); + + output_hash_function (); + + if (option[GLOBAL]) + if (option[SWITCH]) + { + if (option[LENTABLE] && option[DUP]) + output_keylength_table (); + if (option[POINTER] && option[TYPE]) + output_keyword_table (); + } + else + { + if (option[LENTABLE]) + output_keylength_table (); + output_keyword_table (); + output_lookup_array (); + } + + if (option[GNU]) /* Use the inline keyword to remove function overhead. */ + printf ("#ifdef __GNUC__\ninline\n#endif\n"); + + printf ("%s%s\n", option[CONST] ? "const " : "", return_type); + if (option[CPLUSPLUS]) + printf ("%s::", option.get_class_name ()); + + printf (option[ANSI] + ? "%s (register const char *str, register int len)\n{\n" + : "%s (str, len)\n register char *str;\n register unsigned int len;\n{\n", + option.get_function_name ()); + + if (option[ENUM] && !option[GLOBAL]) + printf (" enum\n {\n" + " TOTAL_KEYWORDS = %d,\n" + " MIN_WORD_LENGTH = %d,\n" + " MAX_WORD_LENGTH = %d,\n" + " MIN_HASH_VALUE = %d,\n" + " MAX_HASH_VALUE = %d,\n" + " HASH_VALUE_RANGE = %d,\n" + " DUPLICATES = %d\n };\n\n", + total_keys, min_key_len, max_key_len, min_hash_value, + max_hash_value, max_hash_value - min_hash_value + 1, + total_duplicates ? total_duplicates + 1 : 0); + /* Use the switch in place of lookup table. */ + if (option[SWITCH]) + { + if (!option[GLOBAL]) + { + if (option[LENTABLE] && option[DUP]) + output_keylength_table (); + if (option[POINTER] && option[TYPE]) + output_keyword_table (); + } + output_switch (); + } + /* Use the lookup table, in place of switch. */ + else + { + if (!option[GLOBAL]) + { + if (option[LENTABLE]) + output_keylength_table (); + output_keyword_table (); + } + if (!option[GLOBAL]) + output_lookup_array (); + output_lookup_function (); + } + + if (additional_code) + { + for (;;) + { + int c = getchar (); + + if (c == EOF) + break; + else + putchar (c); + } + } + + fflush (stdout); +} + +/* Sorts the keys by hash value. */ + +void +Key_List::sort (void) +{ + hash_sort = 1; + occurrence_sort = 0; + + head = merge_sort (head); +} + +/* Dumps the key list to stderr stream. */ + +void +Key_List::dump () +{ + int field_width = option.get_max_keysig_size (); + + fprintf (stderr, "\nList contents are:\n(hash value, key length, index, %*s, keyword):\n", + field_width, "char_set"); + + for (List_Node *ptr = head; ptr; ptr = ptr->next) + fprintf (stderr, "%11d,%11d,%6d, %*s, %s\n", + ptr->hash_value, ptr->length, ptr->index, + field_width, ptr->char_set, ptr->key); +} + +/* Simple-minded constructor action here... */ + +Key_List::Key_List (void) +{ + total_keys = 1; + max_key_len = INT_MIN; + min_key_len = INT_MAX; + return_type = default_return_type; + array_type = struct_tag = default_array_type; + head = 0; + total_duplicates = 0; + additional_code = 0; +} + +/* Returns the length of entire key list. */ + +int +Key_List::keyword_list_length (void) +{ + return list_len; +} + +/* Returns length of longest key read. */ + +int +Key_List::max_key_length (void) +{ + return max_key_len; +} + diff --git a/apps/gperf/src/Key_List.h b/apps/gperf/src/Key_List.h new file mode 100644 index 00000000000..14276eb975d --- /dev/null +++ b/apps/gperf/src/Key_List.h @@ -0,0 +1,116 @@ +/* -*- C++ -*- */ +// @(#)Key_List.h 1.1 10/18/96 + +/* This may look like C code, but it is really -*- C++ -*- */ + +/* Data and function member declarations for the keyword list class. + + Copyright (C) 1989 Free Software Foundation, Inc. + written by Douglas C. Schmidt (schmidt@ics.uci.edu) + +This file is part of GNU GPERF. + +GNU GPERF 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 1, or (at your option) +any later version. + +GNU GPERF is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU GPERF; see the file COPYING. If not, write to the Free +Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111, USA. */ + +// The key word list is a useful abstraction that keeps track of +// various pieces of information that enable that fast generation of +// the Gen_Perf.hash function. A Key_List is a singly-linked list of +// List_Nodes. + +#ifndef key_list_h +#define key_list_h 1 + +#include "Options.h" +#include "List_Node.h" + +class Key_List +{ +public: + Key_List (void); + ~Key_List (void); + int keyword_list_length (void); + int max_key_length (void); + void reorder (void); + void sort (void); + void read_keys (void); + void output (void); + + List_Node *head; + // Points to the head of the linked list. + + int total_duplicates; + // Total number of duplicate hash values. + +private: + static int get_occurrence (List_Node *ptr); + static int strcspn (const char *s, const char *reject); + static int already_determined (List_Node *ptr); + static void set_determined (List_Node *ptr); + void output_min_max (void); + void output_switch (void); + void output_keyword_table (void); + void output_keylength_table (void); + void output_hash_function (void); + void output_lookup_function (void); + void output_lookup_array (void); + void output_strcasecmp (void); + void set_output_types (void); + void dump (void); + char *get_array_type (void); + char *save_include_src (void); + char *get_special_input (char delimiter); + List_Node *merge (List_Node *list1, List_Node *list2); + List_Node *merge_sort (List_Node *head); + + char *array_type; + // Pointer to the type for word list. + + char *return_type; + // Pointer to return type for lookup function. + + char *struct_tag; + // Shorthand for user-defined struct tag type. + + char *include_src; + // C source code to be included verbatim. + + int max_key_len; + // Maximum length of the longest keyword. + + int min_key_len; + // Minimum length of the shortest keyword. + + int min_hash_value; + // Minimum hash value for all keywords. + + int max_hash_value; + // Maximum hash value for all keywords. + + int occurrence_sort; + // True if sorting by occurrence. + + int hash_sort; + // True if sorting by hash value. + + int additional_code; + // True if any additional C code is included. + + int list_len; + // Length of head's Key_List, not counting duplicates. + + int total_keys; + // Total number of keys, counting duplicates. +}; +#endif diff --git a/apps/gperf/src/List_Node.cpp b/apps/gperf/src/List_Node.cpp new file mode 100644 index 00000000000..d72cc699c13 --- /dev/null +++ b/apps/gperf/src/List_Node.cpp @@ -0,0 +1,110 @@ +/* Creates and initializes a new list node. +// @(#)List_Node.cpp 1.1 10/18/96 + + Copyright (C) 1989 Free Software Foundation, Inc. + written by Douglas C. Schmidt (schmidt@ics.uci.edu) + +This file is part of GNU GPERF. + +GNU GPERF 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 1, or (at your option) +any later version. + +GNU GPERF is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU GPERF; see the file COPYING. If not, write to the Free +Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111, USA. */ + +#include "Vectors.h" +#include "List_Node.h" + +/* Defined as a macro in string.h on some systems, which causes + conflicts. */ +#undef index + +/* Sorts the key set alphabetically to speed up subsequent operations. + Uses insertion sort since the set is probably quite small. */ + +inline void +List_Node::set_sort (char *base, int len) +{ + int i, j; + + for (i = 0, j = len - 1; i < j; i++) + { + char curr, tmp; + + for (curr = i + 1, tmp = base[curr]; curr > 0 && tmp < base[curr-1]; curr--) + base[curr] = base[curr - 1]; + + base[curr] = tmp; + + } +} + +/* Initializes a List_Node. This requires obtaining memory for the + CHAR_SET initializing them using the information stored in the + KEY_POSITIONS array in Options, and checking for simple errors. + It's important to note that KEY and REST are both pointers to the + different offsets into the same block of dynamic memory pointed to + by parameter K. The data member REST is used to store any + additional fields of the input file (it is set to the "" string if + Option[TYPE] is not enabled). This is useful if the user wishes to + incorporate a lookup structure, rather than just an array of keys. + Finally, KEY_NUMBER contains a count of the total number of keys + seen so far. This is used to initialize the INDEX field to some + useful value. */ + +List_Node::List_Node (char *k, int len) + : key (k), + next (0), + index (0), + length (len), + link (0), + rest (option[TYPE] ? k + len + 1 : "") +{ + char *ptr = new char[(option[ALLCHARS] ? len : option.get_max_keysig_size ()) + 1]; + char_set = ptr; + k[len] = '\0'; /* Null terminate KEY to separate it from REST. */ + + /* Lower case if STRCASECMP option is enabled. */ + if (option[STRCASECMP]) + for (char *p = k; *p; p++) + if (isupper (*p)) + *p = tolower (*p); + + if (option[ALLCHARS]) /* Use all the character position in the KEY. */ + for (; *k; k++, ptr++) + ++Vectors::occurrences[*ptr = *k]; + else /* Only use those character positions specified by the user. */ + { + int i; + + /* Iterate thru the list of key_positions, initializing occurrences table + and char_set (via char * pointer ptr). */ + + for (option.reset (); (i = option.get ()) != EOS; ) + { + if (i == WORD_END) /* Special notation for last KEY position, i.e. '$'. */ + *ptr = key[len - 1]; + else if (i <= len) /* Within range of KEY length, so we'll keep it. */ + *ptr = key[i - 1]; + else /* Out of range of KEY length, so we'll just skip it. */ + continue; + ++Vectors::occurrences[*ptr++]; + } + + /* Didn't get any hits and user doesn't want to consider the + keylength, so there are essentially no usable hash positions! */ + if (ptr == char_set && option[NOLENGTH]) + ACE_ERROR ((LM_ERROR, "Can't hash keyword %s with chosen key positions.\n%a", key, 1)); + } + *ptr = '\0'; /* Terminate this bastard.... */ + /* Sort the KEY_SET items alphabetically. */ + set_sort (char_set, ptr - char_set); +} diff --git a/apps/gperf/src/List_Node.h b/apps/gperf/src/List_Node.h new file mode 100644 index 00000000000..0cb512f0894 --- /dev/null +++ b/apps/gperf/src/List_Node.h @@ -0,0 +1,65 @@ +/* -*- C++ -*- */ +// @(#)List_Node.h 1.1 10/18/96 + +/* This may look like C code, but it is really -*- C++ -*- */ + +/* Data and function members for defining values and operations of a list node. + + Copyright (C) 1989 Free Software Foundation, Inc. + written by Douglas C. Schmidt (schmidt@ics.uci.edu) + +This file is part of GNU GPERF. + +GNU GPERF 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 1, or (at your option) +any later version. + +GNU GPERF is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU GPERF; see the file COPYING. If not, write to the Free +Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111, USA. */ + +#ifndef list_node_h +#define list_node_h 1 + +#include "Options.h" + +struct List_Node +{ + List_Node (char *key, int len); + static void set_sort (char *base, int len); + + List_Node *link; + // TRUE if key has an identical KEY_SET as another key. + + List_Node *next; + // Points to next element on the list. + + char *key; + // Each keyword string stored here. + + char *rest; + // Additional information for building hash function. + + char *char_set; + // Set of characters to hash, specified by user. + + int length; + // Length of the key. + + int hash_value; + // Hash value for the key. + + int occurrence; + // A metric for frequency of key set occurrences. + + int index; + // Position of this node relative to other nodes. +}; + +#endif diff --git a/apps/gperf/src/Makefile b/apps/gperf/src/Makefile new file mode 100644 index 00000000000..ca6221f7156 --- /dev/null +++ b/apps/gperf/src/Makefile @@ -0,0 +1,155 @@ +#---------------------------------------------------------------------------- +# @(#)Makefile 1.1 10/18/96 +# +# Makefile for GPERF release +#---------------------------------------------------------------------------- + +BIN = gperf +LIB = libGperf.a + +FILES = new \ + Options \ + Iterator \ + Gen_Perf \ + Key_List \ + List_Node \ + Hash_Table \ + Bool_Array \ + Vectors \ + Version + +LSRC = $(addsuffix .cpp,$(FILES)) +LOBJ = $(addsuffix .o,$(FILES)) + +LDLIBS = -lGperf + +VLDLIBS = $(LDLIBS:%=%$(VAR)) + +BUILD = $(VLIB) $(VBIN) + +#---------------------------------------------------------------------------- +# Include macros and targets +#---------------------------------------------------------------------------- + +include $(WRAPPER_ROOT)/include/makeinclude/wrapper_macros.GNU +include $(WRAPPER_ROOT)/include/makeinclude/macros.GNU +include $(WRAPPER_ROOT)/include/makeinclude/rules.common.GNU +include $(WRAPPER_ROOT)/include/makeinclude/rules.nonested.GNU +include $(WRAPPER_ROOT)/include/makeinclude/rules.lib.GNU +include $(WRAPPER_ROOT)/include/makeinclude/rules.bin.GNU +include $(WRAPPER_ROOT)/include/makeinclude/rules.local.GNU + +# DO NOT DELETE THIS LINE -- g++dep uses it. +# DO NOT PUT ANYTHING AFTER THIS LINE, IT WILL GO AWAY. + +.obj/new.o .shobj/new.so: new.cpp Options.h \ + $(WRAPPER_ROOT)/ace/Log_Msg.h \ + $(WRAPPER_ROOT)/ace/Log_Record.h \ + $(WRAPPER_ROOT)/ace/ACE.h \ + $(WRAPPER_ROOT)/ace/OS.h \ + $(WRAPPER_ROOT)/ace/Time_Value.h \ + $(WRAPPER_ROOT)/ace/config.h \ + $(WRAPPER_ROOT)/ace/Trace.h \ + $(WRAPPER_ROOT)/ace/ACE.i \ + $(WRAPPER_ROOT)/ace/Log_Priority.h \ + $(WRAPPER_ROOT)/ace/Log_Record.i +.obj/Options.o .shobj/Options.so: Options.cpp \ + $(WRAPPER_ROOT)/ace/Get_Opt.h \ + $(WRAPPER_ROOT)/ace/ACE.h \ + $(WRAPPER_ROOT)/ace/OS.h \ + $(WRAPPER_ROOT)/ace/Time_Value.h \ + $(WRAPPER_ROOT)/ace/config.h \ + $(WRAPPER_ROOT)/ace/Trace.h \ + $(WRAPPER_ROOT)/ace/ACE.i \ + $(WRAPPER_ROOT)/ace/Log_Msg.h \ + $(WRAPPER_ROOT)/ace/Log_Record.h \ + $(WRAPPER_ROOT)/ace/Log_Priority.h \ + $(WRAPPER_ROOT)/ace/Log_Record.i \ + Options.h Iterator.h +.obj/Iterator.o .shobj/Iterator.so: Iterator.cpp Iterator.h Options.h \ + $(WRAPPER_ROOT)/ace/Log_Msg.h \ + $(WRAPPER_ROOT)/ace/Log_Record.h \ + $(WRAPPER_ROOT)/ace/ACE.h \ + $(WRAPPER_ROOT)/ace/OS.h \ + $(WRAPPER_ROOT)/ace/Time_Value.h \ + $(WRAPPER_ROOT)/ace/config.h \ + $(WRAPPER_ROOT)/ace/Trace.h \ + $(WRAPPER_ROOT)/ace/ACE.i \ + $(WRAPPER_ROOT)/ace/Log_Priority.h \ + $(WRAPPER_ROOT)/ace/Log_Record.i +.obj/Gen_Perf.o .shobj/Gen_Perf.so: Gen_Perf.cpp Gen_Perf.h Options.h \ + $(WRAPPER_ROOT)/ace/Log_Msg.h \ + $(WRAPPER_ROOT)/ace/Log_Record.h \ + $(WRAPPER_ROOT)/ace/ACE.h \ + $(WRAPPER_ROOT)/ace/OS.h \ + $(WRAPPER_ROOT)/ace/Time_Value.h \ + $(WRAPPER_ROOT)/ace/config.h \ + $(WRAPPER_ROOT)/ace/Trace.h \ + $(WRAPPER_ROOT)/ace/ACE.i \ + $(WRAPPER_ROOT)/ace/Log_Priority.h \ + $(WRAPPER_ROOT)/ace/Log_Record.i \ + Key_List.h List_Node.h Bool_Array.h +.obj/Key_List.o .shobj/Key_List.so: Key_List.cpp \ + $(WRAPPER_ROOT)/ace/Read_Buffer.h \ + $(WRAPPER_ROOT)/ace/ACE.h \ + $(WRAPPER_ROOT)/ace/OS.h \ + $(WRAPPER_ROOT)/ace/Time_Value.h \ + $(WRAPPER_ROOT)/ace/config.h \ + $(WRAPPER_ROOT)/ace/Trace.h \ + $(WRAPPER_ROOT)/ace/ACE.i \ + $(WRAPPER_ROOT)/ace/Log_Msg.h \ + $(WRAPPER_ROOT)/ace/Log_Record.h \ + $(WRAPPER_ROOT)/ace/Log_Priority.h \ + $(WRAPPER_ROOT)/ace/Log_Record.i \ + $(WRAPPER_ROOT)/ace/Malloc.h \ + $(WRAPPER_ROOT)/ace/Malloc_T.h \ + $(WRAPPER_ROOT)/ace/Synch.h \ + $(WRAPPER_ROOT)/ace/Synch_T.h \ + $(WRAPPER_ROOT)/ace/Memory_Pool.h \ + $(WRAPPER_ROOT)/ace/Event_Handler.h \ + $(WRAPPER_ROOT)/ace/Signal.h \ + $(WRAPPER_ROOT)/ace/Set.h \ + $(WRAPPER_ROOT)/ace/Mem_Map.h \ + $(WRAPPER_ROOT)/ace/SV_Semaphore_Complex.h \ + $(WRAPPER_ROOT)/ace/SV_Semaphore_Simple.h \ + $(WRAPPER_ROOT)/ace/SV_Semaphore_Simple.i \ + $(WRAPPER_ROOT)/ace/SV_Semaphore_Complex.i \ + $(WRAPPER_ROOT)/ace/Read_Buffer.i \ + Hash_Table.h Options.h List_Node.h Vectors.h Key_List.h +.obj/List_Node.o .shobj/List_Node.so: List_Node.cpp Vectors.h List_Node.h Options.h \ + $(WRAPPER_ROOT)/ace/Log_Msg.h \ + $(WRAPPER_ROOT)/ace/Log_Record.h \ + $(WRAPPER_ROOT)/ace/ACE.h \ + $(WRAPPER_ROOT)/ace/OS.h \ + $(WRAPPER_ROOT)/ace/Time_Value.h \ + $(WRAPPER_ROOT)/ace/config.h \ + $(WRAPPER_ROOT)/ace/Trace.h \ + $(WRAPPER_ROOT)/ace/ACE.i \ + $(WRAPPER_ROOT)/ace/Log_Priority.h \ + $(WRAPPER_ROOT)/ace/Log_Record.i +.obj/Hash_Table.o .shobj/Hash_Table.so: Hash_Table.cpp \ + $(WRAPPER_ROOT)/ace/ACE.h \ + $(WRAPPER_ROOT)/ace/OS.h \ + $(WRAPPER_ROOT)/ace/Time_Value.h \ + $(WRAPPER_ROOT)/ace/config.h \ + $(WRAPPER_ROOT)/ace/Trace.h \ + $(WRAPPER_ROOT)/ace/ACE.i \ + $(WRAPPER_ROOT)/ace/Log_Msg.h \ + $(WRAPPER_ROOT)/ace/Log_Record.h \ + $(WRAPPER_ROOT)/ace/Log_Priority.h \ + $(WRAPPER_ROOT)/ace/Log_Record.i \ + Hash_Table.h Options.h List_Node.h +.obj/Bool_Array.o .shobj/Bool_Array.so: Bool_Array.cpp Bool_Array.h Options.h \ + $(WRAPPER_ROOT)/ace/Log_Msg.h \ + $(WRAPPER_ROOT)/ace/Log_Record.h \ + $(WRAPPER_ROOT)/ace/ACE.h \ + $(WRAPPER_ROOT)/ace/OS.h \ + $(WRAPPER_ROOT)/ace/Time_Value.h \ + $(WRAPPER_ROOT)/ace/config.h \ + $(WRAPPER_ROOT)/ace/Trace.h \ + $(WRAPPER_ROOT)/ace/ACE.i \ + $(WRAPPER_ROOT)/ace/Log_Priority.h \ + $(WRAPPER_ROOT)/ace/Log_Record.i +.obj/Version.o .shobj/Version.so: Version.cpp + +# IF YOU PUT ANYTHING HERE IT WILL GO AWAY diff --git a/apps/gperf/src/Options.cpp b/apps/gperf/src/Options.cpp new file mode 100644 index 00000000000..184187b5a4a --- /dev/null +++ b/apps/gperf/src/Options.cpp @@ -0,0 +1,616 @@ +/* Handles parsing the Options provided to the user. +// @(#)Options.cpp 1.1 10/18/96 + + Copyright (C) 1989 Free Software Foundation, Inc. + written by Douglas C. Schmidt (schmidt@ics.uci.edu) + +This file is part of GNU GPERF. + +GNU GPERF 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 1, or (at your option) +any later version. + +GNU GPERF is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU GPERF; see the file COPYING. If not, write to the Free +Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111, USA. */ + +#include "ace/Get_Opt.h" +#include "Options.h" +#include "Iterator.h" + +/* Global option coordinator for the entire program. */ +Options option; + +/* Current program version. */ +extern char *version_string; + +/* Size to jump on a collision. */ +static const int DEFAULT_JUMP_VALUE = 5; + +/* Default name for generated lookup function. */ +static const char *const DEFAULT_NAME = "in_word_set"; + +/* Default name for the key component. */ +static const char *const DEFAULT_KEY = "name"; + +/* Default name for the generated class. */ +static const char *const DEFAULT_CLASS_NAME = "Perfect_Hash"; + +/* Default name for generated hash function. */ +static const char *const DEFAULT_HASH_NAME = "hash"; + +/* Default delimiters that separate keywords from their attributes. */ +static const char *const DEFAULT_DELIMITERS = ",\n"; + +int Options::option_word; +int Options::total_switches; +int Options::total_keysig_size; +int Options::size; +int Options::key_pos; +int Options::jump; +int Options::initial_asso_value; +int Options::argument_count; +int Options::iterations; +char **Options::argument_vector; +const char *Options::function_name; +const char *Options::key_name; +const char *Options::class_name; +const char *Options::hash_name; +const char *Options::delimiters; +char Options::key_positions[MAX_KEY_POS]; + +/* Prints program usage to standard error stream. */ + +inline void +Options::usage (void) +{ + ACE_ERROR ((LM_ERROR, "Usage: %n [-acCdDef[num]gGhH<hashname>i<init>Ijk<keys>K<keyname>lL<language>nN<function name>oOprs<size>S<switches>tTvZ<class name>].\n" + "(type %n -h for help)\n")); +} + +/* Output command-line Options. */ + +void +Options::print_options (void) +{ + int i; + + printf ("/* Command-line: "); + + for (i = 0; i < argument_count; i++) + printf ("%s ", argument_vector[i]); + + printf (" */"); +} + +/* Sorts the key positions *IN REVERSE ORDER!!* + This makes further routines more efficient. Especially when generating code. + Uses a simple Insertion Sort since the set is probably ordered. + Returns 1 if there are no duplicates, 0 otherwise. */ + +inline int +Options::key_sort (char *base, int len) +{ + int i, j; + + for (i = 0, j = len - 1; i < j; i++) + { + int curr, tmp; + + for (curr = i + 1,tmp = base[curr]; curr > 0 && tmp >= base[curr - 1]; curr--) + if ((base[curr] = base[curr - 1]) == tmp) /* oh no, a duplicate!!! */ + return 0; + + base[curr] = tmp; + } + + return 1; +} + +/* Sets the default Options. */ + +Options::Options (void) +{ + key_positions[0] = WORD_START; + key_positions[1] = WORD_END; + key_positions[2] = EOS; + total_keysig_size = 2; + delimiters = DEFAULT_DELIMITERS; + jump = DEFAULT_JUMP_VALUE; + option_word = DEFAULTCHARS | C; + function_name = DEFAULT_NAME; + key_name = DEFAULT_KEY; + hash_name = DEFAULT_HASH_NAME; + class_name = DEFAULT_CLASS_NAME; + total_switches = size = 1; + initial_asso_value = iterations = 0; +} + +/* Dumps option status when debug is set. */ + +Options::~Options (void) +{ + if (option_word & DEBUG) + { + char *ptr; + + fprintf (stderr, "\ndumping Options:\nDEBUG is.......: %s\nORDER is.......: %s" + "\nANSI is........: %s\nTYPE is........: %s\nGNU is.........: %s" + "\nRANDOM is......: %s\nDEFAULTCHARS is: %s\nSWITCH is......: %s" + "\nPOINTER is.....: %s\nNOLENGTH is....: %s\nLENTABLE is....: %s" + "\nDUP is.........: %s\nFAST is........: %s\nCOMP is.....: %s" + "\nNOTYPE is......: %s\nGLOBAL is......: %s\nCONST is....: %s" + "\nCPLUSPLUS is...: %s\nC is...........: %s\nENUM is.....: %s" + "\nSTRCASECMP is...: %s\nOPTIMIZE is...........: %s" + "\niterations = %d\nlookup function name = %s\nhash function name = %s" + "\nkey name = %s\njump value = %d\nmax associcated value = %d" + "\ninitial associated value = %d\ndelimiters = %s\nnumber of switch statements = %d\n", + option_word & DEBUG ? "enabled" : "disabled", + option_word & ORDER ? "enabled" : "disabled", + option_word & ANSI ? "enabled" : "disabled", + option_word & TYPE ? "enabled" : "disabled", + option_word & GNU ? "enabled" : "disabled", + option_word & RANDOM ? "enabled" : "disabled", + option_word & DEFAULTCHARS ? "enabled" : "disabled", + option_word & SWITCH ? "enabled" : "disabled", + option_word & POINTER ? "enabled" : "disabled", + option_word & NOLENGTH ? "enabled" : "disabled", + option_word & LENTABLE ? "enabled" : "disabled", + option_word & DUP ? "enabled" : "disabled", + option_word & FAST ? "enabled" : "disabled", + option_word & COMP ? "enabled" : "disabled", + option_word & NOTYPE ? "enabled" : "disabled", + option_word & GLOBAL ? "enabled" : "disabled", + option_word & CONST ? "enabled" : "disabled", + option_word & CPLUSPLUS ? "enabled" : "disabled", + option_word & C ? "enabled" : "disabled", + option_word & ENUM ? "enabled" : "disabled", + option_word & STRCASECMP ? "enabled" : "disabled", + option_word & OPTIMIZE ? "enabled" : "disabled", + iterations, function_name, hash_name, key_name, jump, size - 1, + initial_asso_value, delimiters, total_switches); + if (option_word & ALLCHARS) + fprintf (stderr, "all characters are used in the hash function\n"); + + fprintf (stderr, "maximum keysig size = %d\nkey positions are: \n", + total_keysig_size); + + for (ptr = key_positions; *ptr != EOS; ptr++) + if (*ptr == WORD_END) + fprintf (stderr, "$\n"); + else + fprintf (stderr, "%d\n", *ptr); + + fprintf (stderr, "finished dumping Options\n"); + } +} + + +/* Parses the command line Options and sets appropriate flags in option_word. */ + +void +Options::operator() (int argc, char *argv[]) +{ + ACE_LOG_MSG->open (argv[0]); + + ACE_Get_Opt getopt (argc, argv, "adcCDe:Ef:gGhH:i:Ij:k:K:lL:nN:oOprs:S:tTvZ:"); + int option_char; + + argument_count = argc; + argument_vector = argv; + + while ((option_char = getopt ()) != -1) + { + switch (option_char) + { + case 'a': /* Generated coded uses the ANSI prototype format. */ + { + option_word |= ANSI; + break; + } + case 'c': /* Generate strncmp rather than strcmp. */ + { + option_word |= COMP; + break; + } + case 'C': /* Make the generated tables readonly (const). */ + { + option_word |= CONST; + break; + } + case 'd': /* Enable debugging option. */ + { + option_word |= DEBUG; + ACE_ERROR ((LM_ERROR, "Starting program %n, version %s, with debuggin on.\n", + version_string)); + break; + } + case 'D': /* Enable duplicate option. */ + { + option_word |= DUP; + break; + } + case 'e': /* Allows user to provide keyword/attribute separator */ + { + option.delimiters = getopt.optarg; + break; + } + case 'E': + { + option_word |= ENUM; + break; + } + case 'f': /* Generate the hash table ``fast.'' */ + { + option_word |= FAST; + if ((iterations = atoi (getopt.optarg)) < 0) + { + ACE_ERROR ((LM_ERROR, "iterations value must not be negative, assuming 0\n")); + iterations = 0; + } + break; + } + case 'g': /* Use the ``inline'' keyword for generated sub-routines. */ + { + option_word |= GNU; + break; + } + case 'G': /* Make the keyword table a global variable. */ + { + option_word |= GLOBAL; + break; + } + case 'h': /* Displays a list of helpful Options to the user. */ + { + ACE_ERROR ((LM_ERROR, + "-a\tGenerate ANSI standard C output code, i.e., function prototypes.\n" + "-c\tGenerate comparison code using strncmp rather than strcmp.\n" + "-C\tMake the contents of generated lookup tables constant, i.e., readonly.\n" + "-d\tEnables the debugging option (produces verbose output to the standard error).\n" + "-D\tHandle keywords that hash to duplicate values. This is useful\n" + "\tfor certain highly redundant keyword sets. It enables the -S option.\n" + "-e\tAllow user to provide a string containing delimiters used to separate\n" + "\tkeywords from their attributes. Default is \",\\n\"\n" + "-E\tDefine constant values using an enum local to the lookup function\n" + "\trather than with defines\n" + "-f\tGenerate the gen-perf.hash function ``fast.'' This decreases GPERF's\n" + "\trunning time at the cost of minimizing generated table-size.\n" + "\tThe numeric argument represents the number of times to iterate when\n" + "\tresolving a collision. `0' means ``iterate by the number of keywords.''\n" + "-g\tAssume a GNU compiler, e.g., g++ or gcc. This makes all generated\n" + "\troutines use the ``inline'' keyword to remove cost of function calls.\n" + "-G\tGenerate the static table of keywords as a static global variable,\n" + "\trather than hiding it inside of the lookup function (which is the\n" + "\tdefault behavior).\n" + "-h\tPrints this mesage.\n" + "-H\tAllow user to specify name of generated hash function. Default\n" + "\tis `hash'.\n" + "-i\tProvide an initial value for the associate values array. Default is 0.\n" + "-I\tGenerate comparison code using case insensitive string comparison, e.g.,\n" + "\tstrncasecmp or strcasecmp.\n" + "\tSetting this value larger helps inflate the size of the final table.\n" + "-j\tAffects the ``jump value,'' i.e., how far to advance the associated\n" + "\tcharacter value upon collisions. Must be an odd number, default is %d.\n" + "-k\tAllows selection of the key positions used in the hash function.\n" + "\tThe allowable choices range between 1-%d, inclusive. The positions\n" + "\tare separated by commas, ranges may be used, and key positions may\n" + "\toccur in any order. Also, the meta-character '*' causes the generated\n" + "\thash function to consider ALL key positions, and $ indicates the\n" + "\t``final character'' of a key, e.g., $,1,2,4,6-10.\n" + "-K\tAllow use to select name of the keyword component in the keyword structure.\n" + "-l\tCompare key lengths before trying a string comparison. This helps\n" + "\tcut down on the number of string comparisons made during the lookup.\n" + "-L\tGenerates code in the language specified by the option's argument. Languages\n" + "\thandled are currently C++ and C. The default is C.\n" + "-n\tDo not include the length of the keyword when computing the hash function\n" + "-N\tAllow user to specify name of generated lookup function. Default\n" + "\tname is `in_word_set.'\n" + "-o\tReorders input keys by frequency of occurrence of the key sets.\n" + "\tThis should decrease the search time dramatically.\n" + "-O\tOptimize the generated lookup function by assuming that all input keywords \n" + "\tare members of the keyset from the keyfile.\n" + "-p\tChanges the return value of the generated function ``in_word_set''\n" + "\tfrom its default boolean value (i.e., 0 or 1), to type ``pointer\n" + "\tto wordlist array'' This is most useful when the -t option, allowing\n" + "\tuser-defined structs, is used.\n" + "-r\tUtilizes randomness to initialize the associated values table.\n" + "-s\tAffects the size of the generated hash table. The numeric argument\n" + "\tfor this option indicates ``how many times larger or smaller'' the associated\n" + "\tvalue range should be, in relationship to the number of keys, e.g. a value of 3\n" + "\tmeans ``allow the maximum associated value to be about 3 times larger than the\n" + "\tnumber of input keys.'' Conversely, a value of -3 means ``make the maximum\n" + "\tassociated value about 3 times smaller than the number of input keys.\n" + "\tA larger table should decrease the time required for an unsuccessful search,\n" + "\tat the expense of extra table space. Default value is 1.\n" + "-S\tCauses the generated C code to use a switch statement scheme, rather\n" + "\tthan an array lookup table. This can lead to a reduction in both\n" + "\ttime and space requirements for some keyfiles. The argument to\n" + "\tthis option determines how many switch statements are generated.\n" + "\tA value of 1 generates 1 switch containing all the elements, a value of 2\n" + "\tgenerates 2 tables with 1/2 the elements in each table, etc. This\n" + "\tis useful since many C compilers cannot correctly generate code for\n" + "\tlarge switch statements.\n" + "-t\tAllows the user to include a structured type declaration for \n" + "\tgenerated code. Any text before %%%% is consider part of the type\n" + "\tdeclaration. Key words and additional fields may follow this, one\n" + "\tgroup of fields per line.\n" + "-T\tPrevents the transfer of the type declaration to the output file.\n" + "\tUse this option if the type is already defined elsewhere.\n" + "-v\tPrints out the current version number\n" + "-Z\tAllow user to specify name of generated C++ class. Default\n" + "\tname is `Perfect_Hash.'\n%e%a", DEFAULT_JUMP_VALUE, (MAX_KEY_POS - 1), usage, 1)); + } + case 'H': /* Sets the name for the hash function */ + { + hash_name = getopt.optarg; + break; + } + case 'i': /* Sets the initial value for the associated values array. */ + { + if ((initial_asso_value = atoi (getopt.optarg)) < 0) + ACE_ERROR ((LM_ERROR, "Initial value %d should be non-zero, ignoring and continuing.\n", initial_asso_value)); + if (option[RANDOM]) + ACE_ERROR ((LM_ERROR, "warning, -r option superceeds -i, ignoring -i option and continuing\n")); + break; + } + case 'I': + { + option_word |= STRCASECMP; + break; + } + case 'j': /* Sets the jump value, must be odd for later algorithms. */ + { + if ((jump = atoi (getopt.optarg)) < 0) + ACE_ERROR ((LM_ERROR, "Jump value %d must be a positive number.\n%e%a", jump, usage, 1)); + else if (jump && ACE_EVEN (jump)) + ACE_ERROR ((LM_ERROR, "Jump value %d should be odd, adding 1 and continuing...\n", jump++)); + break; + } + case 'k': /* Sets key positions used for hash function. */ + { + const int BAD_VALUE = -1; + int value; + Iterator expand (getopt.optarg, 1, MAX_KEY_POS - 1, WORD_END, BAD_VALUE, EOS); + + if (*getopt.optarg == '*') /* Use all the characters for hashing!!!! */ + option_word = (option_word & ~DEFAULTCHARS) | ALLCHARS; + else + { + char *l_key_pos; + + for (l_key_pos = key_positions; (value = expand ()) != EOS; l_key_pos++) + if (value == BAD_VALUE) + ACE_ERROR ((LM_ERROR, "Illegal key value or range, use 1,2,3-%d,'$' or '*'.\n%e%a", + MAX_KEY_POS - 1, usage, 1)); + else + *l_key_pos = value;; + + *l_key_pos = EOS; + + if (! (total_keysig_size = (l_key_pos - key_positions))) + ACE_ERROR ((LM_ERROR, "No keys selected.\n%e%a", usage, 1)); + else if (! key_sort (key_positions, total_keysig_size)) + ACE_ERROR ((LM_ERROR, "Duplicate keys selected\n%e%a", usage, 1)); + + if (total_keysig_size != 2 + || (key_positions[0] != 1 || key_positions[1] != WORD_END)) + option_word &= ~DEFAULTCHARS; + } + break; + } + case 'K': /* Make this the keyname for the keyword component field. */ + { + key_name = getopt.optarg; + break; + } + case 'l': /* Create length table to avoid extra string compares. */ + { + option_word |= LENTABLE; + break; + } + case 'L': /* Deal with different generated languages. */ + { + option_word &= ~C; + if (!strcmp (getopt.optarg, "C++")) + option_word |= (CPLUSPLUS | ANSI); + else if (!strcmp (getopt.optarg, "C")) + option_word |= C; + else + { + ACE_ERROR ((LM_ERROR, "unsupported language option %s, defaulting to C\n", getopt.optarg)); + option_word |= C; + } + break; + } + case 'n': /* Don't include the length when computing hash function. */ + { + option_word |= NOLENGTH; + break; + } + case 'N': /* Make generated lookup function name be optarg */ + { + function_name = getopt.optarg; + break; + } + case 'o': /* Order input by frequency of key set occurrence. */ + { + option_word |= ORDER; + break; + } + case 'O': + { + option_word |= OPTIMIZE; + break; + } + case 'p': /* Generated lookup function now a pointer instead of int. */ + { + option_word |= POINTER; + break; + } + case 'r': /* Utilize randomness to initialize the associated values table. */ + { + option_word |= RANDOM; + if (option.initial_asso_value != 0) + ACE_ERROR ((LM_ERROR, "warning, -r option superceeds -i, disabling -i option and continuing\n")); + break; + } + case 's': /* Range of associated values, determines size of final table. */ + { + if (abs (size = atoi (getopt.optarg)) > 50) + ACE_ERROR ((LM_ERROR, "%d is excessive, did you really mean this?! (type %n -h for help)\n", size)); + break; + } + case 'S': /* Generate switch statement output, rather than lookup table. */ + { + option_word |= SWITCH; + if ((option.total_switches = atoi (getopt.optarg)) <= 0) + ACE_ERROR ((LM_ERROR, "number of switches %s must be a positive number\n%e%a", getopt.optarg, usage, 1)); + break; + } + case 't': /* Enable the TYPE mode, allowing arbitrary user structures. */ + { + option_word |= TYPE; + break; + } + case 'T': /* Don't print structure definition. */ + { + option_word |= NOTYPE; + break; + } + case 'v': /* Print out the version and quit. */ + ACE_ERROR ((LM_ERROR, "%n: version %s\n%e\n%a", version_string, usage, 1)); + case 'Z': /* Set the class name. */ + { + class_name = getopt.optarg; + break; + } + default: + ACE_ERROR ((LM_ERROR, "%e%a", usage, 1)); + } + + } + + if (argv[getopt.optind] && ! freopen (argv[getopt.optind], "r", stdin)) + ACE_ERROR ((LM_ERROR, "Cannot open keyword file %p\n%e%a", argv[getopt.optind], usage, 1)); + + if (++getopt.optind < argc) + ACE_ERROR ((LM_ERROR, "Extra trailing arguments to %n.\n%e%a", usage, 1)); +} + +int +Options::operator[] (Option_Type option) /* True if option enable, else false. */ +{ + return option_word & option; +} + +void +Options::operator = (enum Option_Type opt) /* Enables option OPT. */ +{ + option_word |= opt; +} + +void +Options::operator != (enum Option_Type opt) /* Disables option OPT. */ +{ + option_word &= ~opt; +} + +void +Options::reset (void) /* Initializes the key Iterator. */ +{ + key_pos = 0; +} + +int +Options::get (void) /* Returns current key_position and advanced index. */ +{ + return key_positions[key_pos++]; +} + +void +Options::set_asso_max (int r) /* Sets the size of the table size. */ +{ + size = r; +} + +int +Options::get_asso_max (void) /* Returns the size of the table size. */ +{ + return size; +} + +int +Options::get_max_keysig_size (void) /* Returns total distinct key positions. */ +{ + return total_keysig_size; +} + +void +Options::set_keysig_size (int a_size) /* Sets total distinct key positions. */ +{ + total_keysig_size = a_size; +} + +int +Options::get_jump (void) /* Returns the jump value. */ +{ + return jump; +} + +const char * +Options::get_function_name (void) /* Returns the generated function name. */ +{ + return function_name; +} + +const char * +Options::get_key_name (void) /* Returns the keyword key name. */ +{ + return key_name; +} + +const char * +Options::get_hash_name (void) /* Returns the hash function name. */ +{ + return hash_name; +} + +const char * +Options::get_class_name (void) /* Returns the generated class name. */ +{ + return class_name; +} + +int +Options::initial_value (void) /* Returns the initial associated character value. */ +{ + return initial_asso_value; +} + +int +Options::get_iterations (void) /* Returns the iterations value. */ +{ + return iterations; +} + +const char * +Options::get_delimiter () /* Returns the string used to delimit keywords from other attributes. */ +{ + return delimiters; +} + +int +Options::get_total_switches () /* Gets the total number of switch statements to generate. */ +{ + return total_switches; +} + + + + diff --git a/apps/gperf/src/Options.h b/apps/gperf/src/Options.h new file mode 100644 index 00000000000..2d67003d991 --- /dev/null +++ b/apps/gperf/src/Options.h @@ -0,0 +1,140 @@ +/* -*- C++ -*- */ +// @(#)Options.h 1.1 10/18/96 + +/* This may look like C code, but it is really -*- C++ -*- */ + +/* Handles parsing the Options provided to the user. + + Copyright (C) 1989 Free Software Foundation, Inc. + written by Douglas C. Schmidt (schmidt@ics.uci.edu) + +This file is part of GNU GPERF. + +GNU GPERF 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 1, or (at your option) +any later version. + +GNU GPERF is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU GPERF; see the file COPYING. If not, write to the Free +Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111, USA. */ + +/* This module provides a uniform interface to the various options + available to a user of the gperf hash function generator. In + addition to the run-time options, found in the Option_Type below, + there is also the hash table Size and the Keys to be used in the + hashing. The overall design of this module was an experiment in + using C++ classes as a mechanism to enhance centralization of + option and and error handling, which tend to get out of hand in a C + program. */ + +#ifndef options_h +#define options_h 1 + +#include "ace/Log_Msg.h" + +/* Enumerate the potential debugging Options. */ + +enum Option_Type +{ + DEBUG = 01, /* Enable debugging (prints diagnostics to stderr). */ + ORDER = 02, /* Apply ordering heuristic to speed-up search time. */ + ANSI = 04, /* Generate ANSI prototypes. */ + ALLCHARS = 010, /* Use all characters in hash function. */ + GNU = 020, /* Assume GNU extensions (primarily function inline). */ + TYPE = 040, /* Handle user-defined type structured keyword input. */ + RANDOM = 0100, /* Randomly initialize the associated values table. */ + DEFAULTCHARS = 0200, /* Make default char positions be 1,$ (end of keyword). */ + SWITCH = 0400, /* Generate switch output to save space. */ + POINTER = 01000, /* Have in_word_set function return pointer, not boolean. */ + NOLENGTH = 02000, /* Don't include keyword length in hash computations. */ + LENTABLE = 04000, /* Generate a length table for string comparison. */ + DUP = 010000, /* Handle duplicate hash values for keywords. */ + FAST = 020000, /* Generate the hash function ``fast.'' */ + NOTYPE = 040000, /* Don't include user-defined type definition in output -- it's already defined elsewhere. */ + COMP = 0100000, /* Generate strncmp rather than strcmp. */ + GLOBAL = 0200000, /* Make the keyword table a global variable. */ + CONST = 0400000, /* Make the generated tables readonly (const). */ + CPLUSPLUS = 01000000, /* Generate C++ code. */ + C = 02000000, /* Generate C code. */ + ENUM = 04000000, /* Use enum for constants. */ + STRCASECMP = 010000000, /* Use the case insensitive comparison. */ + OPTIMIZE = 020000000, /* Assume all input keywords are in the keyset. */ + ADA = 040000000 /* Generate Ada code. */ +}; + +/* Define some useful constants (these don't really belong here, but I'm + not sure where else to put them!). These should be consts, but g++ + doesn't seem to do the right thing with them at the moment... ;-( */ + +enum +{ + MAX_KEY_POS = 128 - 1, /* Max size of each word's key set. */ + WORD_START = 1, /* Signals the start of a word. */ + WORD_END = 0, /* Signals the end of a word. */ + EOS = MAX_KEY_POS /* Signals end of the key list. */ +}; + +/* Class manager for gperf program Options. */ + +class Options +{ +public: + Options (void); + ~Options (void); + int operator[] (Option_Type option); + void operator() (int argc, char *argv[]); + void operator= (enum Option_Type); + void operator!= (enum Option_Type); + static void print_options (void); + static void set_asso_max (int r); + static int get_asso_max (void); + static void reset (void); + static int get (void); + static int get_iterations (void); + static int get_max_keysig_size (void); + static void set_keysig_size (int); + static int get_jump (void); + static int initial_value (void); + static int get_total_switches (void); + static const char *get_function_name (void); + static const char *get_key_name (void); + static const char *get_class_name (void); + static const char *get_hash_name (void); + static const char *get_delimiter (void); + +private: + static int option_word; /* Holds the user-specified Options. */ + static int total_switches; /* Number of switch statements to generate. */ + static int total_keysig_size; /* Total number of distinct key_positions. */ + static int size; /* Range of the hash table. */ + static int key_pos; /* Tracks current key position for Iterator. */ + static int jump; /* Jump length when trying alternative values. */ + static int initial_asso_value; /* Initial value for asso_values table. */ + static int argument_count; /* Records count of command-line arguments. */ + static int iterations; /* Amount to iterate when a collision occurs. */ + static char **argument_vector; /* Stores a pointer to command-line vector. */ + static const char *function_name; /* Names used for generated lookup function. */ + static const char *key_name; /* Name used for keyword key. */ + static const char *class_name; /* Name used for generated C++ class. */ + static const char *hash_name; /* Name used for generated hash function. */ + static const char *delimiters; /* Separates keywords from other attributes. */ + static char key_positions[MAX_KEY_POS]; /* Contains user-specified key choices. */ + static int key_sort (char *base, int len); /* Sorts key positions in REVERSE order. */ + static void usage (void); /* Prints proper program usage. */ +}; + +/* Global option coordinator for the entire program. */ +extern Options option; + +/* Set to 1 if your want to stack-allocate some large arrays. */ +#ifndef LARGE_STACK_ARRAYS +#define LARGE_STACK_ARRAYS 0 +#endif + +#endif diff --git a/apps/gperf/src/Vectors.cpp b/apps/gperf/src/Vectors.cpp new file mode 100644 index 00000000000..761e08b2672 --- /dev/null +++ b/apps/gperf/src/Vectors.cpp @@ -0,0 +1,33 @@ +/* This may look like C code, but it is really -*- C++ -*- */ +// @(#)Vectors.cpp 1.1 10/18/96 + + +/* Static class data members that are shared between several classes via + inheritance. + + Copyright (C) 1989 Free Software Foundation, Inc. + written by Douglas C. Schmidt (schmidt@ics.uci.edu) + +This file is part of GNU GPERF. + +GNU GPERF 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 1, or (at your option) +any later version. + +GNU GPERF is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU GPERF; see the file COPYING. If not, write to the Free +Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111, USA. */ + +#include "Vectors.h" + +// Counts occurrences of each key set character. +int Vectors::occurrences[ALPHA_SIZE]; + +// Value associated with each character. +int Vectors::asso_values[ALPHA_SIZE]; diff --git a/apps/gperf/src/Vectors.h b/apps/gperf/src/Vectors.h new file mode 100644 index 00000000000..c01e9f27d8f --- /dev/null +++ b/apps/gperf/src/Vectors.h @@ -0,0 +1,44 @@ +/* -*- C++ -*- */ +// @(#)Vectors.h 1.1 10/18/96 + +#include <stdio.h> + +/* This may look like C code, but it is really -*- C++ -*- */ + +/* Static class data members that are shared between several classes via + inheritance. + + Copyright (C) 1989 Free Software Foundation, Inc. + written by Douglas C. Schmidt (schmidt@ics.uci.edu) + +This file is part of GNU GPERF. + +GNU GPERF 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 1, or (at your option) +any later version. + +GNU GPERF is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU GPERF; see the file COPYING. If not, write to the Free +Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111, USA. */ + +#ifndef vectors_h +#define vectors_h 1 + +static const int ALPHA_SIZE = 128; + +struct Vectors +{ + static int occurrences[ALPHA_SIZE]; + // Counts occurrences of each key set character. + + static int asso_values[ALPHA_SIZE]; + // Value associated with each character. +}; + +#endif diff --git a/apps/gperf/src/Version.cpp b/apps/gperf/src/Version.cpp new file mode 100644 index 00000000000..8fb0d398887 --- /dev/null +++ b/apps/gperf/src/Version.cpp @@ -0,0 +1,25 @@ +/* Current program version number. +// @(#)Version.cpp 1.1 10/18/96 + + + Copyright (C) 1989 Free Software Foundation, Inc. + written by Douglas C. Schmidt (schmidt@ics.uci.edu) + +This file is part of GNU GPERF. + +GNU GPERF 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 1, or (at your option) +any later version. + +GNU GPERF is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU GPERF; see the file COPYING. If not, write to the Free +Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111, +USA. */ + +char *version_string = "2.6 (GNU C++ version)"; diff --git a/apps/gperf/src/gperf.cpp b/apps/gperf/src/gperf.cpp new file mode 100644 index 00000000000..2e6aa2c6406 --- /dev/null +++ b/apps/gperf/src/gperf.cpp @@ -0,0 +1,66 @@ +/* Driver program for the Gen_Perf hash function generator Copyright +// @(#)gperf.cpp 1.1 10/18/96 + + (C) 1989 Free Software Foundation, Inc. written by Douglas + C. Schmidt (schmidt@ics.uci.edu) + +This file is part of GNU GPERF. + +GNU GPERF 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 1, or (at your option) +any later version. + +GNU GPERF is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU GPERF; see the file COPYING. If not, write to the Free +Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111, USA. */ + +// Simple driver program for the Gen_Perf.hash function generator. +// All the hard work is done in class Gen_Perf and its class methods. + +#include "Options.h" +#include "Gen_Perf.h" + +int +main (int argc, char *argv[]) +{ + + struct tm *tm; + time_t clock; + + time (&clock); + tm = localtime (&clock); + printf ("/* starting time is %d:%02d:%02d */\n", tm->tm_hour, tm->tm_min, tm->tm_sec); + +#if defined(RLIMIT_STACK) && LARGE_STACK_ARRAYS + /* Get rid of any avoidable limit on stack size. */ + { + struct rlimit rlim; + + /* Set the stack limit huge so that alloca does not fail. */ + getrlimit (RLIMIT_STACK, &rlim); + rlim.rlim_cur = rlim.rlim_max; + setrlimit (RLIMIT_STACK, &rlim); + } +#endif /* RLIMIT_STACK */ + + /* Sets the Options. */ + option (argc, argv); + + // Initializes the key word list. + Gen_Perf table; + + // Generates and prints the Gen_Perf hash table. Don't use exit + // here, it skips the destructors. + int status = table.generate (); + + time (&clock); + tm = localtime (&clock); + printf ("/* ending time is %d:%02d:%02d */\n", tm->tm_hour, tm->tm_min, tm->tm_sec); + return status; +} diff --git a/apps/gperf/src/new.cpp b/apps/gperf/src/new.cpp new file mode 100644 index 00000000000..ebaafa16917 --- /dev/null +++ b/apps/gperf/src/new.cpp @@ -0,0 +1,75 @@ +/* Defines a buffered memory allocation abstraction that reduces calls to +// @(#)new.cpp 1.1 10/18/96 + + malloc. + Copyright (C) 1989 Free Software Foundation, Inc. + written by Douglas C. Schmidt (schmidt@ics.uci.edu) + +This file is part of GNU GPERF. + +GNU GPERF 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 1, or (at your option) +any later version. + +GNU GPERF is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU GPERF; see the file COPYING. If not, write to the Free +Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111, USA. */ + +#include "Options.h" + +/* Determine default alignment. If your C++ compiler does not like + this then try something like #define DEFAULT_ALIGNMENT 8. */ +struct fooalign {char x; double d;}; +const int ALIGNMENT = ((char *)&((struct fooalign *) 0)->d - (char *)0); + +/* Provide an abstraction that cuts down on the number of calls to NEW + by buffering the memory pool from which strings are allocated. */ + +void * +operator new (size_t size) +{ + static char *buf_start = 0; /* Large array used to reduce calls to NEW. */ + static char *buf_end = 0; /* Indicates end of BUF_START. */ + static int buf_size = 4 * BUFSIZ; /* Size of buffer pointed to by BUF_START. */ + char *temp; + + /* Align this on correct boundaries, just to be safe... */ + size = ((size + ALIGNMENT - 1) / ALIGNMENT) * ALIGNMENT; + + /* If we are about to overflow our buffer we'll just grab another + chunk of memory. Since we never free the original memory it + doesn't matter that no one points to the beginning of that + chunk. Note we use a heuristic that grows the buffer either by + size of the request or by twice the previous size, whichever is + larger. */ + + if (buf_start + size >= buf_end) + { + buf_size *= 2; + if (buf_size < size) + buf_size = size; + if (buf_start = (char *)malloc (buf_size)) + buf_end = buf_start + buf_size; + else + ACE_ERROR ((LM_ERROR, "Virtual memory failed at %s, %s in function %s\n%a", __FILE__, __LINE__, "operator new", 1)); + } + + temp = buf_start; + buf_start += size; + return temp; +} + +/* We need this deletion operator in order to make the linker happy. */ + +void +operator delete (void *ptr) +{ + // We cannot call free here, as it doesn't match the mallocs. + // free ((char *) ptr); +} diff --git a/apps/gperf/tests/Makefile.in b/apps/gperf/tests/Makefile.in new file mode 100644 index 00000000000..f702fc804f2 --- /dev/null +++ b/apps/gperf/tests/Makefile.in @@ -0,0 +1,72 @@ +# Copyright (C) 1989, 1992, 1993 Free Software Foundation, Inc. +# written by Douglas C. Schmidt (schmidt@ics.uci.edu) +# +# This file is part of GNU GPERF. +# +# GNU GPERF 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 1, or (at your option) +# any later version. +# +# GNU GPERF is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU GPERF; see the file COPYING. If not, write to the Free +# Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111, USA. + +srcdir = . + +#### package, host, target, and site dependent Makefile fragments come in here. +## + +GPERF = ../src/gperf + +check: + @echo "performing some tests of the perfect hash generator" + $(CC) -c $(CFLAGS) $(srcdir)/test.c + $(GPERF) -p -c -l -S1 -o $(srcdir)/c.gperf > cinset.c + $(CC) $(CFLAGS) -o cout cinset.c test.o + @echo "testing ANSI C reserved words, all items should be found in the set" + ./cout -v < $(srcdir)/c.gperf > c.out + -diff -b $(srcdir)/c.exp c.out + $(GPERF) -k1,4,'$$' $(srcdir)/ada.gperf > adainset.c +# double '$$' is only there since make gets confused; programn wants only 1 '$' + $(CC) $(CFLAGS) -o aout adainset.c test.o + @echo "testing Ada reserved words, all items should be found in the set" + ./aout -v < $(srcdir)/ada.gperf > ada-res.out + -diff -b $(srcdir)/ada-res.exp ada-res.out + $(GPERF) -p -D -k1,'$$' -s 2 -o $(srcdir)/adadefs.gperf > preinset.c + $(CC) $(CFLAGS) -o preout preinset.c test.o + @echo "testing Ada predefined words, all items should be found in the set" + ./preout -v < $(srcdir)/adadefs.gperf > ada-pred.out + -diff -b $(srcdir)/ada-pred.exp ada-pred.out + $(GPERF) -k1,2,'$$' -o $(srcdir)/modula3.gperf > m3inset.c + $(CC) $(CFLAGS) -o m3out m3inset.c test.o + @echo "testing Modula3 reserved words, all items should be found in the set" + ./m3out -v < $(srcdir)/modula3.gperf > modula.out + -diff -b $(srcdir)/modula.exp modula.out + $(GPERF) -o -S2 -p < $(srcdir)/pascal.gperf > pinset.c + $(CC) $(CFLAGS) -o pout pinset.c test.o + @echo "testing Pascal reserved words, all items should be found in the set" + ./pout -v < $(srcdir)/pascal.gperf > pascal.out + -diff -b $(srcdir)/pascal.exp pascal.out +# these next 5 are demos that show off the generated code + $(GPERF) -p -j1 -g -o -t -N is_reserved_word -k1,3,'$$' < $(srcdir)/c-parse.gperf > test-1.out + -diff -b $(srcdir)/test-1.exp test-1.out + $(GPERF) -n -k1-8 -l <$(srcdir)/modula2.gperf > test-2.out + -diff -b $(srcdir)/test-2.exp test-2.out + $(GPERF) -p -j 1 -o -a -C -g -t -k1,4,$$ < $(srcdir)/gplus.gperf > test-3.out + -diff -b $(srcdir)/test-3.exp test-3.out + $(GPERF) -D -p -t < $(srcdir)/c-parse.gperf > test-4.out + -diff -b $(srcdir)/test-4.exp test-4.out + $(GPERF) -g -o -j1 -t -p -N is_reserved_word < $(srcdir)/gpc.gperf > test-5.out + -diff -b $(srcdir)/test-5.exp test-5.out +# prints out the help message + -$(GPERF) -h > test-6.out 2>&1 || [ a = a ] + -diff -b $(srcdir)/test-6.exp test-6.out + @echo "only if, do, for, case, goto, else, while, and return should be found " + ./aout -v < $(srcdir)/c.gperf > test-7.out + -diff -b $(srcdir)/test-7.exp test-7.out diff --git a/apps/gperf/tests/ada-pred.exp b/apps/gperf/tests/ada-pred.exp new file mode 100644 index 00000000000..33caaa32ea1 --- /dev/null +++ b/apps/gperf/tests/ada-pred.exp @@ -0,0 +1,54 @@ +in word set boolean +in word set character +in word set constraint_error +in word set false +in word set float +in word set integer +in word set natural +in word set numeric_error +in word set positive +in word set program_error +in word set storage_error +in word set string +in word set tasking_error +in word set true +in word set address +in word set aft +in word set base +in word set callable +in word set constrained +in word set count +in word set delta +in word set digits +in word set emax +in word set epsilon +in word set first +in word set firstbit +in word set fore +in word set image +in word set large +in word set last +in word set lastbit +in word set length +in word set machine_emax +in word set machine_emin +in word set machine_mantissa +in word set machine_overflows +in word set machine_radix +in word set machine_rounds +in word set mantissa +in word set pos +in word set position +in word set pred +in word set range +in word set safe_emax +in word set safe_large +in word set safe_small +in word set size +in word set small +in word set storage_size +in word set succ +in word set terminated +in word set val +in word set value +in word set width diff --git a/apps/gperf/tests/ada-res.exp b/apps/gperf/tests/ada-res.exp new file mode 100644 index 00000000000..8134fe861f5 --- /dev/null +++ b/apps/gperf/tests/ada-res.exp @@ -0,0 +1,63 @@ +in word set else +in word set exit +in word set terminate +in word set type +in word set raise +in word set range +in word set reverse +in word set declare +in word set end +in word set record +in word set exception +in word set not +in word set then +in word set return +in word set separate +in word set select +in word set digits +in word set renames +in word set subtype +in word set elsif +in word set function +in word set for +in word set package +in word set procedure +in word set private +in word set while +in word set when +in word set new +in word set entry +in word set delay +in word set case +in word set constant +in word set at +in word set abort +in word set accept +in word set and +in word set delta +in word set access +in word set abs +in word set pragma +in word set array +in word set use +in word set out +in word set do +in word set others +in word set of +in word set or +in word set all +in word set limited +in word set loop +in word set null +in word set task +in word set in +in word set is +in word set if +in word set rem +in word set mod +in word set begin +in word set body +in word set xor +in word set goto +in word set generic +in word set with diff --git a/apps/gperf/tests/ada.gperf b/apps/gperf/tests/ada.gperf new file mode 100644 index 00000000000..332bdc740ad --- /dev/null +++ b/apps/gperf/tests/ada.gperf @@ -0,0 +1,63 @@ +else +exit +terminate +type +raise +range +reverse +declare +end +record +exception +not +then +return +separate +select +digits +renames +subtype +elsif +function +for +package +procedure +private +while +when +new +entry +delay +case +constant +at +abort +accept +and +delta +access +abs +pragma +array +use +out +do +others +of +or +all +limited +loop +null +task +in +is +if +rem +mod +begin +body +xor +goto +generic +with diff --git a/apps/gperf/tests/adadefs.gperf b/apps/gperf/tests/adadefs.gperf new file mode 100644 index 00000000000..875be69abc9 --- /dev/null +++ b/apps/gperf/tests/adadefs.gperf @@ -0,0 +1,54 @@ +boolean +character +constraint_error +false +float +integer +natural +numeric_error +positive +program_error +storage_error +string +tasking_error +true +address +aft +base +callable +constrained +count +delta +digits +emax +epsilon +first +firstbit +fore +image +large +last +lastbit +length +machine_emax +machine_emin +machine_mantissa +machine_overflows +machine_radix +machine_rounds +mantissa +pos +position +pred +range +safe_emax +safe_large +safe_small +size +small +storage_size +succ +terminated +val +value +width diff --git a/apps/gperf/tests/c++.gperf b/apps/gperf/tests/c++.gperf new file mode 100644 index 00000000000..650d32d0edd --- /dev/null +++ b/apps/gperf/tests/c++.gperf @@ -0,0 +1,47 @@ +asm +auto +break +case +catch +char +class +const +continue +default +delete +do +double +else +enum +extern +float +for +friend +goto +if +inline +int +long +new +operator +overload +private +protected +public +register +return +short +signed +sizeof +static +struct +switch +template +this +typedef +union +unsigned +virtual +void +volatile +while diff --git a/apps/gperf/tests/c-parse.gperf b/apps/gperf/tests/c-parse.gperf new file mode 100644 index 00000000000..feef59babb0 --- /dev/null +++ b/apps/gperf/tests/c-parse.gperf @@ -0,0 +1,56 @@ +%{ +/* Command-line: gperf -p -j1 -i 1 -g -o -t -N is_reserved_word -k1,3,$ c-parse.gperf */ +%} +struct resword { char *name; short token; enum rid rid; }; +%% +__alignof, ALIGNOF, NORID +__alignof__, ALIGNOF, NORID +__asm, ASM, NORID +__asm__, ASM, NORID +__attribute, ATTRIBUTE, NORID +__attribute__, ATTRIBUTE, NORID +__const, TYPE_QUAL, RID_CONST +__const__, TYPE_QUAL, RID_CONST +__inline, SCSPEC, RID_INLINE +__inline__, SCSPEC, RID_INLINE +__signed, TYPESPEC, RID_SIGNED +__signed__, TYPESPEC, RID_SIGNED +__typeof, TYPEOF, NORID +__typeof__, TYPEOF, NORID +__volatile, TYPE_QUAL, RID_VOLATILE +__volatile__, TYPE_QUAL, RID_VOLATILE +asm, ASM, NORID +auto, SCSPEC, RID_AUTO +break, BREAK, NORID +case, CASE, NORID +char, TYPESPEC, RID_CHAR +const, TYPE_QUAL, RID_CONST +continue, CONTINUE, NORID +default, DEFAULT, NORID +do, DO, NORID +double, TYPESPEC, RID_DOUBLE +else, ELSE, NORID +enum, ENUM, NORID +extern, SCSPEC, RID_EXTERN +float, TYPESPEC, RID_FLOAT +for, FOR, NORID +goto, GOTO, NORID +if, IF, NORID +inline, SCSPEC, RID_INLINE +int, TYPESPEC, RID_INT +long, TYPESPEC, RID_LONG +register, SCSPEC, RID_REGISTER +return, RETURN, NORID +short, TYPESPEC, RID_SHORT +signed, TYPESPEC, RID_SIGNED +sizeof, SIZEOF, NORID +static, SCSPEC, RID_STATIC +struct, STRUCT, NORID +switch, SWITCH, NORID +typedef, SCSPEC, RID_TYPEDEF +typeof, TYPEOF, NORID +union, UNION, NORID +unsigned, TYPESPEC, RID_UNSIGNED +void, TYPESPEC, RID_VOID +volatile, TYPE_QUAL, RID_VOLATILE +while, WHILE, NORID diff --git a/apps/gperf/tests/c.exp b/apps/gperf/tests/c.exp new file mode 100644 index 00000000000..10c8b7f6116 --- /dev/null +++ b/apps/gperf/tests/c.exp @@ -0,0 +1,32 @@ +in word set if +in word set do +in word set int +in word set for +in word set case +in word set char +in word set auto +in word set goto +in word set else +in word set long +in word set void +in word set enum +in word set float +in word set short +in word set union +in word set break +in word set while +in word set const +in word set double +in word set static +in word set extern +in word set struct +in word set return +in word set sizeof +in word set switch +in word set signed +in word set typedef +in word set default +in word set unsigned +in word set continue +in word set register +in word set volatile diff --git a/apps/gperf/tests/c.gperf b/apps/gperf/tests/c.gperf new file mode 100644 index 00000000000..8672d6c25ed --- /dev/null +++ b/apps/gperf/tests/c.gperf @@ -0,0 +1,32 @@ +if +do +int +for +case +char +auto +goto +else +long +void +enum +float +short +union +break +while +const +double +static +extern +struct +return +sizeof +switch +signed +typedef +default +unsigned +continue +register +volatile diff --git a/apps/gperf/tests/configure.in b/apps/gperf/tests/configure.in new file mode 100644 index 00000000000..d93c7bb1840 --- /dev/null +++ b/apps/gperf/tests/configure.in @@ -0,0 +1,26 @@ +# This file is a shell script fragment that supplies the information +# necessary to tailor a template configure script into the configure +# script appropriate for this directory. For more information, check +# any existing configure script. + +configdirs="" +srctrigger=c-parse.gperf +srcname="test perfect hash function generator" + +target_makefile_frag=../../target-mkfrag +package_makefile_frag=Make.pack + +# per-host: + +# per-target: + +TOLIBGXX=../../ +ALL='$(NOTHING)' +CHECK=check +MOSTLYCLEAN='*.o \#* core *inset.c output.* *.out aout cout m3out pout preout' + +(. ${srcdir}/../../config.shared) >${package_makefile_frag} + +# post-target: + +rm -f ${package_makefile_frag} diff --git a/apps/gperf/tests/gpc.gperf b/apps/gperf/tests/gpc.gperf new file mode 100644 index 00000000000..8fb469e46bc --- /dev/null +++ b/apps/gperf/tests/gpc.gperf @@ -0,0 +1,48 @@ +%{ +/* ISO Pascal 7185 reserved words. + * + * For GNU Pascal compiler (GPC) by jtv@hut.fi + * + * run this through the Doug Schmidt's gperf program + * with command + * gperf -g -o -j1 -t -p -N is_reserved_word + * + */ +%} +struct resword { char *name; short token; short iclass;}; +%% +And, AND, PASCAL_ISO +Array, ARRAY, PASCAL_ISO +Begin, BEGIN_, PASCAL_ISO +Case, CASE, PASCAL_ISO +Const, CONST, PASCAL_ISO +Div, DIV, PASCAL_ISO +Do, DO, PASCAL_ISO +Downto, DOWNTO, PASCAL_ISO +Else, ELSE, PASCAL_ISO +End, END, PASCAL_ISO +File, FILE_, PASCAL_ISO +For, FOR, PASCAL_ISO +Function, FUNCTION, PASCAL_ISO +Goto, GOTO, PASCAL_ISO +If, IF, PASCAL_ISO +In, IN, PASCAL_ISO +Label, LABEL, PASCAL_ISO +Mod, MOD, PASCAL_ISO +Nil, NIL, PASCAL_ISO +Not, NOT, PASCAL_ISO +Of, OF, PASCAL_ISO +Or, OR, PASCAL_ISO +Packed, PACKED, PASCAL_ISO +Procedure, PROCEDURE, PASCAL_ISO +Program,PROGRAM,PASCAL_ISO +Record, RECORD, PASCAL_ISO +Repeat, REPEAT, PASCAL_ISO +Set, SET, PASCAL_ISO +Then, THEN, PASCAL_ISO +To, TO, PASCAL_ISO +Type, TYPE, PASCAL_ISO +Until, UNTIL, PASCAL_ISO +Var, VAR, PASCAL_ISO +While, WHILE, PASCAL_ISO +With, WITH, PASCAL_ISO diff --git a/apps/gperf/tests/gplus.gperf b/apps/gperf/tests/gplus.gperf new file mode 100644 index 00000000000..4a93315be52 --- /dev/null +++ b/apps/gperf/tests/gplus.gperf @@ -0,0 +1,76 @@ +%{ +/* Command-line: gperf -p -j1 -g -o -t -N is_reserved_word -k1,4,$ gplus.gperf */ +%} +struct resword { char *name; short token; enum rid rid;}; +%% +__alignof, ALIGNOF, NORID +__alignof__, ALIGNOF, NORID +__asm, ASM, NORID +__asm__, ASM, NORID +__attribute, ATTRIBUTE, NORID +__attribute__, ATTRIBUTE, NORID +__const, TYPE_QUAL, RID_CONST +__const__, TYPE_QUAL, RID_CONST +__inline, SCSPEC, RID_INLINE +__inline__, SCSPEC, RID_INLINE +__signed, TYPESPEC, RID_SIGNED +__signed__, TYPESPEC, RID_SIGNED +__typeof, TYPEOF, NORID +__typeof__, TYPEOF, NORID +__volatile, TYPE_QUAL, RID_VOLATILE +__volatile__, TYPE_QUAL, RID_VOLATILE +all, ALL, NORID /* Extension */, +except, EXCEPT, NORID /* Extension */, +exception, AGGR, RID_EXCEPTION /* Extension */, +raise, RAISE, NORID /* Extension */, +raises, RAISES, NORID /* Extension */, +reraise, RERAISE, NORID /* Extension */, +try, TRY, NORID /* Extension */, +asm, ASM, NORID, +auto, SCSPEC, RID_AUTO, +break, BREAK, NORID, +case, CASE, NORID, +catch, CATCH, NORID, +char, TYPESPEC, RID_CHAR, +class, AGGR, RID_CLASS, +const, TYPE_QUAL, RID_CONST, +continue, CONTINUE, NORID, +default, DEFAULT, NORID, +delete, DELETE, NORID, +do, DO, NORID, +double, TYPESPEC, RID_DOUBLE, +dynamic, DYNAMIC, NORID, +else, ELSE, NORID, +enum, ENUM, NORID, +extern, SCSPEC, RID_EXTERN, +float, TYPESPEC, RID_FLOAT, +for, FOR, NORID, +friend, SCSPEC, RID_FRIEND, +goto, GOTO, NORID, +if, IF, NORID, +inline, SCSPEC, RID_INLINE, +int, TYPESPEC, RID_INT, +long, TYPESPEC, RID_LONG, +new, NEW, NORID, +operator, OPERATOR, NORID, +overload, OVERLOAD, NORID, +private, PRIVATE, NORID, +protected, PROTECTED, NORID, +public, PUBLIC, NORID, +register, SCSPEC, RID_REGISTER, +return, RETURN, NORID, +short, TYPESPEC, RID_SHORT, +signed, TYPESPEC, RID_SIGNED, +sizeof, SIZEOF, NORID, +static, SCSPEC, RID_STATIC, +struct, AGGR, RID_RECORD, +switch, SWITCH, NORID, +this, THIS, NORID, +typedef, SCSPEC, RID_TYPEDEF, +typeof, TYPEOF, NORID, +union, AGGR, RID_UNION, +unsigned, TYPESPEC, RID_UNSIGNED, +virtual, SCSPEC, RID_VIRTUAL, +void, TYPESPEC, RID_VOID, +volatile, TYPE_QUAL, RID_VOLATILE, +while, WHILE, NORID, diff --git a/apps/gperf/tests/irc.gperf b/apps/gperf/tests/irc.gperf new file mode 100644 index 00000000000..afe53c59e7d --- /dev/null +++ b/apps/gperf/tests/irc.gperf @@ -0,0 +1,63 @@ +%{ +extern int m_text(), m_private(), m_who(), m_whois(), m_user(), m_list(); +extern int m_topic(), m_invite(), m_channel(), m_version(), m_quit(); +extern int m_server(), m_kill(), m_info(), m_links(), m_summon(), m_stats(); +extern int m_users(), m_nick(), m_error(), m_help(), m_whoreply(); +extern int m_squit(), m_restart(), m_away(), m_die(), m_connect(); +extern int m_ping(), m_pong(), m_oper(), m_pass(), m_wall(), m_trace(); +extern int m_time(), m_rehash(), m_names(), m_namreply(), m_admin(); +extern int m_linreply(), m_notice(), m_lusers(), m_voice(), m_grph(); +extern int m_xtra(), m_motd(); +%} +struct Message { + char *cmd; + int (* func)(); + int count; + int parameters; +}; +%% +NICK, m_nick, 0, 1 +MSG, m_text, 0, 1 +PRIVMSG, m_private, 0, 2 +WHO, m_who, 0, 1 +WHOIS, m_whois, 0, 4 +USER, m_user, 0, 4 +SERVER, m_server, 0, 2 +LIST, m_list, 0, 1 +TOPIC, m_topic, 0, 1 +INVITE, m_invite, 0, 2 +CHANNEL, m_channel, 0, 1 +VERSION, m_version, 0, 1 +QUIT, m_quit, 0, 2 +SQUIT, m_squit, 0, 2 +KILL, m_kill, 0, 2 +INFO, m_info, 0, 1 +LINKS, m_links, 0, 1 +SUMMON, m_summon, 0, 1 +STATS, m_stats, 0, 1 +USERS, m_users, 0, 1 +RESTART, m_restart, 0, 1 +WHOREPLY,m_whoreply, 0, 7 +HELP, m_help, 0, 2 +ERROR, m_error, 0, 1 +AWAY, m_away, 0, 1 +DIE, m_die, 0, 1 +CONNECT, m_connect, 0, 3 +PING, m_ping, 0, 2 +PONG, m_pong, 0, 3 +OPER, m_oper, 0, 3 +PASS, m_pass, 0, 2 +WALL, m_wall, 0, 1 +TIME, m_time, 0, 1 +REHASH, m_rehash, 0, 1 +NAMES, m_names, 0, 1 +NAMREPLY,m_namreply, 0, 3 +ADMIN, m_admin, 0, 1 +TRACE, m_trace, 0, 1 +LINREPLY,m_linreply, 0, 2 +NOTICE, m_notice, 0, 2 +LUSERS, m_lusers, 0, 1 +VOICE, m_voice, 0, 2 +GRPH, m_grph, 0, 2 +XTRA, m_xtra, 0, 2 +MOTD, m_motd, 0, 2 diff --git a/apps/gperf/tests/makeinfo.gperf b/apps/gperf/tests/makeinfo.gperf new file mode 100644 index 00000000000..1488b8e38fb --- /dev/null +++ b/apps/gperf/tests/makeinfo.gperf @@ -0,0 +1,116 @@ +COMMAND; +%% +!, cm_force_sentence_end, false +', insert_self, false +*, cm_asterisk, false +., cm_force_sentence_end, false +:, cm_force_abbreviated_whitespace, false +?, cm_force_sentence_end, false +@, insert_self, false +TeX, cm_TeX, true +`, insert_self, false +appendix, cm_appendix, false +appendixsec, cm_appendixsec, false +appendixsubsec, cm_appendixsubsec, false +asis, cm_asis, true +b, cm_bold, true +br, cm_br, false +bullet, cm_bullet, true +bye, cm_bye, false +c, cm_comment, false +center, cm_center, false +chapter, cm_chapter, false +cindex, cm_cindex, false +cite, cm_cite, true +code, cm_code, true +comment, cm_comment, false +contents, do_nothing, false +copyright, cm_copyright, true +ctrl, cm_ctrl, true +defcodeindex, cm_defindex, false +defindex, cm_defindex, false +dfn, cm_dfn, true +display, cm_display, false +dots, cm_dots, true +emph, cm_emph, true +end, cm_end, false +enumerate, cm_enumerate, false +equiv, cm_equiv, true +error, cm_error, true +example, cm_example, false +exdent, cm_exdent, false +expansion, cm_expansion, true +file, cm_file, true +findex, cm_findex, false +format, cm_format, false +group, cm_group, false +i, cm_italic, true +iappendix, cm_appendix, false +iappendixsec, cm_appendixsec, false +iappendixsubsec, cm_appendixsubsec, false +ichapter, cm_chapter, false +ifinfo, cm_ifinfo, false +iftex, cm_iftex, false +ignore, cm_ignore, false +include, cm_include, false +inforef, cm_inforef, true +input, cm_include, false +isection, cm_section, false +isubsection, cm_subsection, false +isubsubsection, cm_subsubsection, false +item, cm_item, false +itemize, cm_itemize, false +itemx, cm_itemx, false +iunnumbered, cm_unnumbered, false +iunnumberedsec, cm_unnumberedsec, false +iunnumberedsubsec, cm_unnumberedsubsec, false +kbd, cm_kbd, true +key, cm_key, true +kindex, cm_kindex, false +lisp, cm_lisp, false +menu, cm_menu +minus, cm_minus, true +need, cm_need, false +node, cm_node, false +noindent, cm_noindent, false +page, do_nothing, false +pindex, cm_pindex, false +point, cm_point, true +print, cm_print, true +printindex, cm_printindex, false +pxref, cm_pxref, true +quotation, cm_quotation, false +r, cm_roman, true +ref, cm_xref, true +refill, cm_refill, false +result, cm_result, true +samp, cm_samp, true +sc, cm_sc, true +section, cm_section, false +setchapternewpage, cm_setchapternewpage, false +setfilename, cm_setfilename, false +settitle, cm_settitle, false +smallexample, cm_smallexample, false +sp, cm_sp, false +strong, cm_strong, true +subsection, cm_subsection, false +subsubsection, cm_subsubsection, false +summarycontents, do_nothing, false +syncodeindex, cm_synindex, false +synindex, cm_synindex, false +t, cm_title, true +table, cm_table, false +tex, cm_tex, false +tindex, cm_tindex, false +titlepage, cm_titlepage, false +unnumbered, cm_unnumbered, false +unnumberedsec, cm_unnumberedsec, false +unnumberedsubsec, cm_unnumberedsubsec, false +var, cm_var, true +vindex, cm_vindex, false +w, cm_w, true +xref, cm_xref, true +{, insert_self, false +}, insert_self, false +infoinclude, cm_infoinclude, false +footnote, cm_footnote, false diff --git a/apps/gperf/tests/modula.exp b/apps/gperf/tests/modula.exp new file mode 100644 index 00000000000..cef7d5acad8 --- /dev/null +++ b/apps/gperf/tests/modula.exp @@ -0,0 +1,106 @@ +in word set AND +in word set ARRAY +in word set BEGIN +in word set BITS +in word set BY +in word set CASE +in word set CONST +in word set DIV +in word set DO +in word set ELSE +in word set ELSIF +in word set END +in word set EVAL +in word set EXCEPT +in word set EXCEPTION +in word set EXIT +in word set EXPORTS +in word set FINALLY +in word set FOR +in word set FROM +in word set IF +in word set IMPORT +in word set INTERFACE +in word set IN +in word set INLINE +in word set LOCK +in word set METHODS +in word set MOD +in word set MODULE +in word set NOT +in word set OBJECT +in word set OF +in word set OR +in word set PROCEDURE +in word set RAISES +in word set READONLY +in word set RECORD +in word set REF +in word set REPEAT +in word set RETURN +in word set SET +in word set THEN +in word set TO +in word set TRY +in word set TYPE +in word set TYPECASE +in word set UNSAFE +in word set UNTIL +in word set UNTRACED +in word set VALUE +in word set VAR +in word set WHILE +in word set WITH +in word set and +in word set array +in word set begin +in word set bits +in word set by +in word set case +in word set const +in word set div +in word set do +in word set else +in word set elsif +in word set end +in word set eval +in word set except +in word set exception +in word set exit +in word set exports +in word set finally +in word set for +in word set from +in word set if +in word set import +in word set interface +in word set in +in word set inline +in word set lock +in word set methods +in word set mod +in word set module +in word set not +in word set object +in word set of +in word set or +in word set procedure +in word set raises +in word set readonly +in word set record +in word set ref +in word set repeat +in word set return +in word set set +in word set then +in word set to +in word set try +in word set type +in word set typecase +in word set unsafe +in word set until +in word set untraced +in word set value +in word set var +in word set while +in word set with diff --git a/apps/gperf/tests/modula2.gperf b/apps/gperf/tests/modula2.gperf new file mode 100644 index 00000000000..5ef9c753835 --- /dev/null +++ b/apps/gperf/tests/modula2.gperf @@ -0,0 +1,40 @@ +AND +ARRAY +BEGIN +BY +CASE +CONST +DEFINITION +DIV +DO +ELSE +ELSIF +END +EXIT +EXPORT +FOR +FROM +IF +IMPLEMENTATION +IMPORT +IN +LOOP +MOD +MODULE +NOT +OF +OR +POINTER +PROCEDURE +QUALIFIED +RECORD +REPEAT +RETURN +SET +THEN +TO +TYPE +UNTIL +VAR +WHILE +WITH diff --git a/apps/gperf/tests/modula3.gperf b/apps/gperf/tests/modula3.gperf new file mode 100644 index 00000000000..d0243460d9b --- /dev/null +++ b/apps/gperf/tests/modula3.gperf @@ -0,0 +1,106 @@ +AND +ARRAY +BEGIN +BITS +BY +CASE +CONST +DIV +DO +ELSE +ELSIF +END +EVAL +EXCEPT +EXCEPTION +EXIT +EXPORTS +FINALLY +FOR +FROM +IF +IMPORT +INTERFACE +IN +INLINE +LOCK +METHODS +MOD +MODULE +NOT +OBJECT +OF +OR +PROCEDURE +RAISES +READONLY +RECORD +REF +REPEAT +RETURN +SET +THEN +TO +TRY +TYPE +TYPECASE +UNSAFE +UNTIL +UNTRACED +VALUE +VAR +WHILE +WITH +and +array +begin +bits +by +case +const +div +do +else +elsif +end +eval +except +exception +exit +exports +finally +for +from +if +import +interface +in +inline +lock +methods +mod +module +not +object +of +or +procedure +raises +readonly +record +ref +repeat +return +set +then +to +try +type +typecase +unsafe +until +untraced +value +var +while +with diff --git a/apps/gperf/tests/pascal.exp b/apps/gperf/tests/pascal.exp new file mode 100644 index 00000000000..765e44c6a0f --- /dev/null +++ b/apps/gperf/tests/pascal.exp @@ -0,0 +1,36 @@ +in word set with +in word set array +in word set and +in word set function +in word set case +in word set var +in word set const +in word set until +in word set then +in word set set +in word set record +in word set program +in word set procedure +in word set or +in word set packed +in word set not +in word set nil +in word set label +in word set in +in word set repeat +in word set of +in word set goto +in word set forward +in word set for +in word set while +in word set file +in word set else +in word set downto +in word set do +in word set div +in word set to +in word set type +in word set end +in word set mod +in word set begin +in word set if diff --git a/apps/gperf/tests/pascal.gperf b/apps/gperf/tests/pascal.gperf new file mode 100644 index 00000000000..fed3fbb30ea --- /dev/null +++ b/apps/gperf/tests/pascal.gperf @@ -0,0 +1,36 @@ +with +array +and +function +case +var +const +until +then +set +record +program +procedure +or +packed +not +nil +label +in +repeat +of +goto +forward +for +while +file +else +downto +do +div +to +type +end +mod +begin +if diff --git a/apps/gperf/tests/test-1.exp b/apps/gperf/tests/test-1.exp new file mode 100644 index 00000000000..5788cf7dfc3 --- /dev/null +++ b/apps/gperf/tests/test-1.exp @@ -0,0 +1,140 @@ +/* C code produced by gperf version 2.5 (GNU C++ version) */ +/* Command-line: ../src/gperf -p -j1 -g -o -t -N is_reserved_word -k1,3,$ */ +/* Command-line: gperf -p -j1 -i 1 -g -o -t -N is_reserved_word -k1,3,$ c-parse.gperf */ +struct resword { char *name; short token; enum rid rid; }; + +#define TOTAL_KEYWORDS 51 +#define MIN_WORD_LENGTH 2 +#define MAX_WORD_LENGTH 13 +#define MIN_HASH_VALUE 8 +#define MAX_HASH_VALUE 82 +/* maximum key range = 75, duplicates = 0 */ + +#ifdef __GNUC__ +inline +#endif +static unsigned int +hash (str, len) + register char *str; + register int unsigned len; +{ + static unsigned char asso_values[] = + { + 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, + 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, + 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, + 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, + 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, + 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, + 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, + 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, + 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, + 83, 83, 83, 83, 83, 0, 83, 1, 2, 34, + 19, 6, 11, 29, 0, 17, 83, 0, 23, 28, + 26, 30, 31, 83, 15, 1, 0, 28, 13, 4, + 83, 83, 5, 83, 83, 83, 83, 83, + }; + register int hval = len; + + switch (hval) + { + default: + case 3: + hval += asso_values[str[2]]; + case 2: + case 1: + hval += asso_values[str[0]]; + break; + } + return hval + asso_values[str[len - 1]]; +} + +#ifdef __GNUC__ +inline +#endif +struct resword * +is_reserved_word (str, len) + register char *str; + register unsigned int len; +{ + static struct resword wordlist[] = + { + {"",}, {"",}, {"",}, {"",}, {"",}, {"",}, {"",}, {"",}, + {"__asm__", ASM, NORID}, + {"",}, + {"__typeof__", TYPEOF, NORID}, + {"__signed__", TYPESPEC, RID_SIGNED}, + {"__alignof__", ALIGNOF, NORID}, + {"break", BREAK, NORID}, + {"__attribute__", ATTRIBUTE, NORID}, + {"",}, {"",}, + {"else", ELSE, NORID}, + {"__attribute", ATTRIBUTE, NORID}, + {"__typeof", TYPEOF, NORID}, + {"int", TYPESPEC, RID_INT}, + {"__alignof", ALIGNOF, NORID}, + {"struct", STRUCT, NORID}, + {"sizeof", SIZEOF, NORID}, + {"switch", SWITCH, NORID}, + {"__volatile__", TYPE_QUAL, RID_VOLATILE}, + {"",}, + {"__inline__", SCSPEC, RID_INLINE}, + {"__signed", TYPESPEC, RID_SIGNED}, + {"__volatile", TYPE_QUAL, RID_VOLATILE}, + {"if", IF, NORID}, + {"__inline", SCSPEC, RID_INLINE}, + {"while", WHILE, NORID}, + {"",}, + {"__asm", ASM, NORID}, + {"auto", SCSPEC, RID_AUTO}, + {"short", TYPESPEC, RID_SHORT}, + {"default", DEFAULT, NORID}, + {"extern", SCSPEC, RID_EXTERN}, + {"",}, {"",}, + {"__const", TYPE_QUAL, RID_CONST}, + {"static", SCSPEC, RID_STATIC}, + {"__const__", TYPE_QUAL, RID_CONST}, + {"for", FOR, NORID}, + {"case", CASE, NORID}, + {"float", TYPESPEC, RID_FLOAT}, + {"return", RETURN, NORID}, + {"typeof", TYPEOF, NORID}, + {"typedef", SCSPEC, RID_TYPEDEF}, + {"volatile", TYPE_QUAL, RID_VOLATILE}, + {"do", DO, NORID}, + {"inline", SCSPEC, RID_INLINE}, + {"void", TYPESPEC, RID_VOID}, + {"char", TYPESPEC, RID_CHAR}, + {"signed", TYPESPEC, RID_SIGNED}, + {"unsigned", TYPESPEC, RID_UNSIGNED}, + {"",}, {"",}, + {"double", TYPESPEC, RID_DOUBLE}, + {"asm", ASM, NORID}, + {"",}, {"",}, + {"goto", GOTO, NORID}, + {"",}, + {"const", TYPE_QUAL, RID_CONST}, + {"enum", ENUM, NORID}, + {"register", SCSPEC, RID_REGISTER}, + {"",}, {"",}, {"",}, {"",}, {"",}, {"",}, + {"continue", CONTINUE, NORID}, + {"",}, + {"union", UNION, NORID}, + {"",}, {"",}, {"",}, {"",}, {"",}, + {"long", TYPESPEC, RID_LONG}, + }; + + if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH) + { + register int key = hash (str, len); + + if (key <= MAX_HASH_VALUE && key >= 0) + { + register char *s = wordlist[key].name; + + if (*s == *str && !strcmp (str + 1, s + 1)) + return &wordlist[key]; + } + } + return 0; +} diff --git a/apps/gperf/tests/test-2.exp b/apps/gperf/tests/test-2.exp new file mode 100644 index 00000000000..f74124155eb --- /dev/null +++ b/apps/gperf/tests/test-2.exp @@ -0,0 +1,183 @@ +/* C code produced by gperf version 2.5 (GNU C++ version) */ +/* Command-line: ../src/gperf -n -k1-8 -l */ + +#define TOTAL_KEYWORDS 40 +#define MIN_WORD_LENGTH 2 +#define MAX_WORD_LENGTH 14 +#define MIN_HASH_VALUE 1 +#define MAX_HASH_VALUE 256 +/* maximum key range = 256, duplicates = 0 */ + +static unsigned int +hash (str, len) + register char *str; + register int unsigned len; +{ + static unsigned short asso_values[] = + { + 257, 257, 257, 257, 257, 257, 257, 257, 257, 257, + 257, 257, 257, 257, 257, 257, 257, 257, 257, 257, + 257, 257, 257, 257, 257, 257, 257, 257, 257, 257, + 257, 257, 257, 257, 257, 257, 257, 257, 257, 257, + 257, 257, 257, 257, 257, 257, 257, 257, 257, 257, + 257, 257, 257, 257, 257, 257, 257, 257, 257, 257, + 257, 257, 257, 257, 257, 25, 30, 35, 21, 0, + 30, 15, 30, 45, 257, 257, 0, 5, 45, 0, + 10, 0, 1, 20, 25, 15, 30, 40, 15, 5, + 257, 257, 257, 257, 257, 257, 257, 257, 257, 257, + 257, 257, 257, 257, 257, 257, 257, 257, 257, 257, + 257, 257, 257, 257, 257, 257, 257, 257, 257, 257, + 257, 257, 257, 257, 257, 257, 257, 257, + }; + register int hval = 0; + + switch (len) + { + default: + case 8: + hval += asso_values[str[7]]; + case 7: + hval += asso_values[str[6]]; + case 6: + hval += asso_values[str[5]]; + case 5: + hval += asso_values[str[4]]; + case 4: + hval += asso_values[str[3]]; + case 3: + hval += asso_values[str[2]]; + case 2: + hval += asso_values[str[1]]; + case 1: + hval += asso_values[str[0]]; + break; + } + return hval; +} + +char * +in_word_set (str, len) + register char *str; + register unsigned int len; +{ + + static unsigned char lengthtable[] = + { + 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 4, 2, 0, 0, 0, 2, 3, 0, + 0, 0, 2, 3, 0, 0, 0, 2, 4, 0, 0, 0, 4, 6, + 0, 0, 0, 3, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, + 3, 5, 6, 0, 0, 6, 0, 0, 0, 0, 3, 0, 0, 0, + 3, 0, 0, 0, 0, 2, 0, 0, 0, 0, 4, 0, 0, 9, + 0, 4, 6, 6, 0, 0, 2, 3, 0, 0, 0, 5, 3, 0, + 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 0, + 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, + 7, 0, 0, 0, 5, 0, 0, 0, 0, 5, 0, 0, 0, 0, + 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 10, + }; + static char *wordlist[] = + { + "", + "OR", + "", "", "", "", "", "", "", "", + "LOOP", + "", "", "", "", "", "", "", "", "", + "ELSE", + "DO", + "", "", "", + "TO", + "MOD", + "", "", "", + "OF", + "FOR", + "", "", "", + "BY", + "FROM", + "", "", "", + "TYPE", + "MODULE", + "", "", "", + "SET", + "", "", "", "", "", + "EXPORT", + "", "", "", "", + "VAR", + "ARRAY", + "RECORD", + "", "", + "REPEAT", + "", "", "", "", + "END", + "", "", "", + "NOT", + "", "", "", "", + "IF", + "", "", "", "", + "CASE", + "", "", + "PROCEDURE", + "", + "EXIT", + "IMPORT", + "RETURN", + "", "", + "IN", + "AND", + "", "", "", + "ELSIF", + "DIV", + "", "", "", + "THEN", + "", "", "", "", "", "", "", "", "", + "IMPLEMENTATION", + "", "", "", "", + "WHILE", + "", "", "", "", "", "", "", "", "", + "CONST", + "POINTER", + "", "", "", + "UNTIL", + "", "", "", "", + "BEGIN", + "", "", "", "", + "WITH", + "", "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", "", + "", "QUALIFIED", + "", "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", "", + "", "", "", "", "", + "DEFINITION", + }; + + if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH) + { + register int key = hash (str, len); + + if (key <= MAX_HASH_VALUE && key >= 0) + { + register char *s = wordlist[key]; + + if (len == lengthtable[key] + && *s == *str && !strcmp (str + 1, s + 1)) + return s; + } + } + return 0; +} diff --git a/apps/gperf/tests/test-3.exp b/apps/gperf/tests/test-3.exp new file mode 100644 index 00000000000..5e889020657 --- /dev/null +++ b/apps/gperf/tests/test-3.exp @@ -0,0 +1,169 @@ +/* C code produced by gperf version 2.5 (GNU C++ version) */ +/* Command-line: ../src/gperf -p -j 1 -o -a -C -g -t -k1,4,$ */ +/* Command-line: gperf -p -j1 -g -o -t -N is_reserved_word -k1,4,$ gplus.gperf */ +struct resword { char *name; short token; enum rid rid;}; + +#define TOTAL_KEYWORDS 71 +#define MIN_WORD_LENGTH 2 +#define MAX_WORD_LENGTH 13 +#define MIN_HASH_VALUE 4 +#define MAX_HASH_VALUE 147 +/* maximum key range = 144, duplicates = 0 */ + +#ifdef __GNUC__ +inline +#endif +static unsigned int +hash (register const char *str, register int len) +{ + static const unsigned char asso_values[] = + { + 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, + 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, + 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, + 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, + 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, + 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, + 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, + 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, + 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, + 148, 148, 148, 148, 148, 0, 148, 19, 6, 27, + 37, 0, 12, 1, 15, 63, 148, 4, 0, 56, + 20, 15, 42, 148, 31, 5, 26, 39, 32, 10, + 148, 40, 148, 148, 148, 148, 148, 148, + }; + register int hval = len; + + switch (hval) + { + default: + case 4: + hval += asso_values[str[3]]; + case 3: + case 2: + case 1: + hval += asso_values[str[0]]; + break; + } + return hval + asso_values[str[len - 1]]; +} + +#ifdef __GNUC__ +inline +#endif +const struct resword * +in_word_set (register const char *str, register int len) +{ + static const struct resword wordlist[] = + { + {"",}, {"",}, {"",}, {"",}, + {"else", ELSE, NORID,}, + {"",}, + {"long", TYPESPEC, RID_LONG,}, + {"",}, {"",}, {"",}, {"",}, + {"__alignof__", ALIGNOF, NORID}, + {"__asm__", ASM, NORID}, + {"",}, {"",}, + {"while", WHILE, NORID,}, + {"",}, {"",}, {"",}, {"",}, {"",}, + {"__alignof", ALIGNOF, NORID}, + {"all", ALL, NORID /* Extension */,}, + {"sizeof", SIZEOF, NORID,}, + {"__const__", TYPE_QUAL, RID_CONST}, + {"__volatile", TYPE_QUAL, RID_VOLATILE}, + {"extern", SCSPEC, RID_EXTERN,}, + {"__volatile__", TYPE_QUAL, RID_VOLATILE}, + {"__inline", SCSPEC, RID_INLINE}, + {"exception", AGGR, RID_EXCEPTION /* Extension */,}, + {"__inline__", SCSPEC, RID_INLINE}, + {"case", CASE, NORID,}, + {"except", EXCEPT, NORID /* Extension */,}, + {"new", NEW, NORID,}, + {"break", BREAK, NORID,}, + {"goto", GOTO, NORID,}, + {"",}, + {"__attribute", ATTRIBUTE, NORID}, + {"",}, + {"__attribute__", ATTRIBUTE, NORID}, + {"this", THIS, NORID,}, + {"raise", RAISE, NORID /* Extension */,}, + {"class", AGGR, RID_CLASS,}, + {"delete", DELETE, NORID,}, + {"typeof", TYPEOF, NORID,}, + {"typedef", SCSPEC, RID_TYPEDEF,}, + {"for", FOR, NORID,}, + {"raises", RAISES, NORID /* Extension */,}, + {"__const", TYPE_QUAL, RID_CONST}, + {"double", TYPESPEC, RID_DOUBLE,}, + {"__typeof__", TYPEOF, NORID}, + {"",}, + {"switch", SWITCH, NORID,}, + {"auto", SCSPEC, RID_AUTO,}, + {"do", DO, NORID,}, + {"friend", SCSPEC, RID_FRIEND,}, + {"",}, + {"reraise", RERAISE, NORID /* Extension */,}, + {"",}, + {"volatile", TYPE_QUAL, RID_VOLATILE,}, + {"__typeof", TYPEOF, NORID}, + {"continue", CONTINUE, NORID,}, + {"float", TYPESPEC, RID_FLOAT,}, + {"const", TYPE_QUAL, RID_CONST,}, + {"static", SCSPEC, RID_STATIC,}, + {"virtual", SCSPEC, RID_VIRTUAL,}, + {"__asm", ASM, NORID}, + {"short", TYPESPEC, RID_SHORT,}, + {"signed", TYPESPEC, RID_SIGNED,}, + {"try", TRY, NORID /* Extension */,}, + {"",}, {"",}, {"",}, + {"__signed__", TYPESPEC, RID_SIGNED}, + {"catch", CATCH, NORID,}, + {"public", PUBLIC, NORID,}, + {"struct", AGGR, RID_RECORD,}, + {"if", IF, NORID,}, + {"asm", ASM, NORID,}, + {"union", AGGR, RID_UNION,}, + {"",}, + {"private", PRIVATE, NORID,}, + {"",}, {"",}, {"",}, + {"operator", OPERATOR, NORID,}, + {"",}, {"",}, {"",}, + {"default", DEFAULT, NORID,}, + {"dynamic", DYNAMIC, NORID,}, + {"overload", OVERLOAD, NORID,}, + {"int", TYPESPEC, RID_INT,}, + {"char", TYPESPEC, RID_CHAR,}, + {"",}, {"",}, + {"return", RETURN, NORID,}, + {"",}, {"",}, {"",}, {"",}, {"",}, {"",}, {"",}, {"",}, {"",}, + {"",}, {"",}, + {"__signed", TYPESPEC, RID_SIGNED}, + {"",}, + {"void", TYPESPEC, RID_VOID,}, + {"",}, {"",}, {"",}, + {"protected", PROTECTED, NORID,}, + {"",}, + {"enum", ENUM, NORID,}, + {"",}, {"",}, {"",}, {"",}, {"",}, {"",}, {"",}, {"",}, {"",}, + {"",}, {"",}, {"",}, {"",}, {"",}, {"",}, + {"inline", SCSPEC, RID_INLINE,}, + {"register", SCSPEC, RID_REGISTER,}, + {"",}, {"",}, {"",}, {"",}, {"",}, {"",}, {"",}, {"",}, {"",}, + {"",}, {"",}, {"",}, {"",}, + {"unsigned", TYPESPEC, RID_UNSIGNED,}, + }; + + if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH) + { + register int key = hash (str, len); + + if (key <= MAX_HASH_VALUE && key >= 0) + { + register const char *s = wordlist[key].name; + + if (*s == *str && !strcmp (str + 1, s + 1)) + return &wordlist[key]; + } + } + return 0; +} diff --git a/apps/gperf/tests/test-4.exp b/apps/gperf/tests/test-4.exp new file mode 100644 index 00000000000..5238bf94d98 --- /dev/null +++ b/apps/gperf/tests/test-4.exp @@ -0,0 +1,138 @@ +/* C code produced by gperf version 2.5 (GNU C++ version) */ +/* Command-line: ../src/gperf -D -p -t */ +/* Command-line: gperf -p -j1 -i 1 -g -o -t -N is_reserved_word -k1,3,$ c-parse.gperf */ +struct resword { char *name; short token; enum rid rid; }; + +#define TOTAL_KEYWORDS 51 +#define MIN_WORD_LENGTH 2 +#define MAX_WORD_LENGTH 13 +#define MIN_HASH_VALUE 4 +#define MAX_HASH_VALUE 82 +/* maximum key range = 79, duplicates = 2 */ + +static unsigned int +hash (str, len) + register char *str; + register int unsigned len; +{ + static unsigned char asso_values[] = + { + 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, + 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, + 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, + 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, + 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, + 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, + 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, + 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, + 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, + 83, 83, 83, 83, 83, 0, 83, 40, 20, 50, + 25, 10, 30, 0, 0, 50, 83, 0, 15, 0, + 35, 0, 83, 83, 20, 0, 10, 40, 5, 15, + 83, 83, 83, 83, 83, 83, 83, 83, + }; + return len + asso_values[str[len - 1]] + asso_values[str[0]]; +} + +struct resword * +in_word_set (str, len) + register char *str; + register unsigned int len; +{ + static struct resword wordlist[] = + { + {"",}, {"",}, {"",}, {"",}, + {"goto", GOTO, NORID}, + {"__asm", ASM, NORID}, + {"switch", SWITCH, NORID}, + {"__asm__", ASM, NORID}, + {"__const__", TYPE_QUAL, RID_CONST}, + {"__inline__", SCSPEC, RID_INLINE}, + {"__typeof__", TYPEOF, NORID}, + {"__signed__", TYPESPEC, RID_SIGNED}, + {"__alignof__", ALIGNOF, NORID}, + {"__volatile__", TYPE_QUAL, RID_VOLATILE}, + {"__attribute__", ATTRIBUTE, NORID}, + {"enum", ENUM, NORID}, + {"short", TYPESPEC, RID_SHORT}, + {"struct", STRUCT, NORID}, + {"__const", TYPE_QUAL, RID_CONST}, + {"__inline", SCSPEC, RID_INLINE}, + {"long", TYPESPEC, RID_LONG}, + {"__volatile", TYPE_QUAL, RID_VOLATILE}, + {"__attribute", ATTRIBUTE, NORID}, + {"volatile", TYPE_QUAL, RID_VOLATILE}, + {"else", ELSE, NORID}, + {"break", BREAK, NORID}, + {"do", DO, NORID}, + {"while", WHILE, NORID}, + {"signed", TYPESPEC, RID_SIGNED}, + {"__signed", TYPESPEC, RID_SIGNED}, + {"void", TYPESPEC, RID_VOID}, + {"sizeof", SIZEOF, NORID}, + {"__typeof", TYPEOF, NORID}, + {"__alignof", ALIGNOF, NORID}, + {"double", TYPESPEC, RID_DOUBLE}, + {"default", DEFAULT, NORID}, + {"asm", ASM, NORID}, + {"auto", SCSPEC, RID_AUTO}, + {"float", TYPESPEC, RID_FLOAT}, + {"typeof", TYPEOF, NORID}, + {"typedef", SCSPEC, RID_TYPEDEF}, + {"register", SCSPEC, RID_REGISTER}, + {"extern", SCSPEC, RID_EXTERN}, + {"for", FOR, NORID}, + {"static", SCSPEC, RID_STATIC}, + {"return", RETURN, NORID}, + {"int", TYPESPEC, RID_INT}, + {"case", CASE, NORID}, + {"const", TYPE_QUAL, RID_CONST}, + {"inline", SCSPEC, RID_INLINE}, + {"continue", CONTINUE, NORID}, + {"unsigned", TYPESPEC, RID_UNSIGNED}, + {"char", TYPESPEC, RID_CHAR}, + {"union", UNION, NORID}, + {"if", IF, NORID}, + }; + + static char lookup[] = + { + -1, -1, -1, -1, 4, 5, 6, 7, -1, 8, 100, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, -1, 23, 24, 25, -1, 26, + -9, -3, 27, 28, -1, 29, 30, -1, 31, -1, 32, 33, -1, 34, + 35, 36, 37, 38, 39, 40, 41, -1, -1, 42, -1, 43, -1, -1, + 44, -1, -1, -1, -1, 45, -1, 46, 47, 48, 49, -1, 50, -1, + -1, -1, -1, 51, 52, -1, -1, -1, -1, -1, 53, -1, 54, + }; + + if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH) + { + register int key = hash (str, len); + + if (key <= MAX_HASH_VALUE && key >= 0) + { + register int index = lookup[key]; + + if (index >= 0 && index < MAX_HASH_VALUE) + { + register char *s = wordlist[index].name; + + if (*s == *str && !strcmp (str + 1, s + 1)) + return &wordlist[index]; + } + else if (index < 0 && index >= -MAX_HASH_VALUE) + return 0; + else + { + register int offset = key + index + (index > 0 ? -MAX_HASH_VALUE : MAX_HASH_VALUE); + register struct resword *base = &wordlist[-lookup[offset]]; + register struct resword *ptr = base + -lookup[offset + 1]; + + while (--ptr >= base) + if (*str == *ptr->name && !strcmp (str + 1, ptr->name + 1)) + return ptr; + } + } + } + return 0; +} diff --git a/apps/gperf/tests/test-5.exp b/apps/gperf/tests/test-5.exp new file mode 100644 index 00000000000..101e2798d40 --- /dev/null +++ b/apps/gperf/tests/test-5.exp @@ -0,0 +1,111 @@ +/* C code produced by gperf version 2.5 (GNU C++ version) */ +/* Command-line: ../src/gperf -g -o -j1 -t -p -N is_reserved_word */ +/* ISO Pascal 7185 reserved words. + * + * For GNU Pascal compiler (GPC) by jtv@hut.fi + * + * run this through the Doug Schmidt's gperf program + * with command + * gperf -g -o -j1 -t -p -N is_reserved_word + * + */ +struct resword { char *name; short token; short iclass;}; + +#define TOTAL_KEYWORDS 35 +#define MIN_WORD_LENGTH 2 +#define MAX_WORD_LENGTH 9 +#define MIN_HASH_VALUE 2 +#define MAX_HASH_VALUE 43 +/* maximum key range = 42, duplicates = 0 */ + +#ifdef __GNUC__ +inline +#endif +static unsigned int +hash (str, len) + register char *str; + register int unsigned len; +{ + static unsigned char asso_values[] = + { + 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, + 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, + 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, + 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, + 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, + 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, + 44, 44, 44, 44, 44, 18, 29, 14, 6, 7, + 10, 20, 44, 28, 44, 44, 28, 19, 22, 15, + 0, 44, 9, 23, 0, 23, 26, 2, 44, 44, + 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, + 0, 0, 13, 44, 30, 44, 44, 44, 0, 25, + 1, 0, 44, 44, 0, 44, 1, 44, 25, 44, + 44, 0, 44, 44, 44, 44, 44, 44, + }; + return len + asso_values[str[len - 1]] + asso_values[str[0]]; +} + +#ifdef __GNUC__ +inline +#endif +struct resword * +is_reserved_word (str, len) + register char *str; + register unsigned int len; +{ + static struct resword wordlist[] = + { + {"",}, {"",}, + {"To", TO, PASCAL_ISO}, + {"",}, + {"Type", TYPE, PASCAL_ISO}, + {"Then", THEN, PASCAL_ISO}, + {"Packed", PACKED, PASCAL_ISO}, + {"While", WHILE, PASCAL_ISO}, + {"Do", DO, PASCAL_ISO}, + {"Procedure", PROCEDURE, PASCAL_ISO}, + {"End", END, PASCAL_ISO}, + {"Else", ELSE, PASCAL_ISO}, + {"Downto", DOWNTO, PASCAL_ISO}, + {"For", FOR, PASCAL_ISO}, + {"File", FILE_, PASCAL_ISO}, + {"Record", RECORD, PASCAL_ISO}, + {"Repeat", REPEAT, PASCAL_ISO}, + {"Or", OR, PASCAL_ISO}, + {"Case", CASE, PASCAL_ISO}, + {"Function", FUNCTION, PASCAL_ISO}, + {"Const", CONST, PASCAL_ISO}, + {"And", AND, PASCAL_ISO}, + {"Mod", MOD, PASCAL_ISO}, + {"Array", ARRAY, PASCAL_ISO}, + {"Goto", GOTO, PASCAL_ISO}, + {"Nil", NIL, PASCAL_ISO}, + {"Not", NOT, PASCAL_ISO}, + {"Set", SET, PASCAL_ISO}, + {"Until", UNTIL, PASCAL_ISO}, + {"Var", VAR, PASCAL_ISO}, + {"Of", OF, PASCAL_ISO}, + {"In", IN, PASCAL_ISO}, + {"Program", PROGRAM,PASCAL_ISO}, + {"Label", LABEL, PASCAL_ISO}, + {"Div", DIV, PASCAL_ISO}, + {"Begin", BEGIN_, PASCAL_ISO}, + {"With", WITH, PASCAL_ISO}, + {"",}, {"",}, {"",}, {"",}, {"",}, {"",}, + {"If", IF, PASCAL_ISO}, + }; + + if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH) + { + register int key = hash (str, len); + + if (key <= MAX_HASH_VALUE && key >= 0) + { + register char *s = wordlist[key].name; + + if (*s == *str && !strcmp (str + 1, s + 1)) + return &wordlist[key]; + } + } + return 0; +} diff --git a/apps/gperf/tests/test-6.exp b/apps/gperf/tests/test-6.exp new file mode 100644 index 00000000000..eba6e3cac9a --- /dev/null +++ b/apps/gperf/tests/test-6.exp @@ -0,0 +1,74 @@ +-a Generate ANSI standard C output code, i.e., function prototypes. +-c Generate comparison code using strncmp rather than strcmp. +-C Make the contents of generated lookup tables constant, i.e., readonly. +-d Enables the debugging option (produces verbose output to the standard error). +-D Handle keywords that hash to duplicate values. This is useful + for certain highly redundant keyword sets. It enables the -S option. +-e Allow user to provide a string containing delimiters used to separate + keywords from their attributes. Default is ",\n" +-E Define constant values using an enum local to the lookup function + rather than with defines +-f Generate the gen-perf.hash function ``fast.'' This decreases GPERF's + running time at the cost of minimizing generated table-size. + The numeric argument represents the number of times to iterate when + resolving a collision. `0' means ``iterate by the number of keywords.'' +-g Assume a GNU compiler, e.g., g++ or gcc. This makes all generated + routines use the ``inline'' keyword to remove cost of function calls. +-G Generate the static table of keywords as a static global variable, + rather than hiding it inside of the lookup function (which is the + default behavior). +-h Prints this mesage. +-H Allow user to specify name of generated hash function. Default + is `hash'. +-i Provide an initial value for the associate values array. Default is 0. + Setting this value larger helps inflate the size of the final table. +-j Affects the ``jump value,'' i.e., how far to advance the associated + character value upon collisions. Must be an odd number, default is 5. +-k Allows selection of the key positions used in the hash function. + The allowable choices range between 1-126, inclusive. The positions + are separated by commas, ranges may be used, and key positions may + occur in any order. Also, the meta-character '*' causes the generated + hash function to consider ALL key positions, and $ indicates the + ``final character'' of a key, e.g., $,1,2,4,6-10. +-K Allow use to select name of the keyword component in the keyword structure. +-l Compare key lengths before trying a string comparison. This helps + cut down on the number of string comparisons made during the lookup. +-L Generates code in the language specified by the option's argument. Languages + handled are currently C++ and C. The default is C. +-n Do not include the length of the keyword when computing the hash function +-N Allow user to specify name of generated lookup function. Default + name is `in_word_set.' +-o Reorders input keys by frequency of occurrence of the key sets. + This should decrease the search time dramatically. +-p Changes the return value of the generated function ``in_word_set'' + from its default boolean value (i.e., 0 or 1), to type ``pointer + to wordlist array'' This is most useful when the -t option, allowing + user-defined structs, is used. +-r Utilizes randomness to initialize the associated values table. +-s Affects the size of the generated hash table. The numeric argument + for this option indicates ``how many times larger or smaller'' the associated + value range should be, in relationship to the number of keys, e.g. a value of 3 + means ``allow the maximum associated value to be about 3 times larger than the + number of input keys.'' Conversely, a value of -3 means ``make the maximum + associated value about 3 times smaller than the number of input keys. + A larger table should decrease the time required for an unsuccessful search, + at the expense of extra table space. Default value is 1. +-S Causes the generated C code to use a switch statement scheme, rather + than an array lookup table. This can lead to a reduction in both + time and space requirements for some keyfiles. The argument to + this option determines how many switch statements are generated. + A value of 1 generates 1 switch containing all the elements, a value of 2 + generates 2 tables with 1/2 the elements in each table, etc. This + is useful since many C compilers cannot correctly generate code for + large switch statements. +-t Allows the user to include a structured type declaration for + generated code. Any text before %% is consider part of the type + declaration. Key words and additional fields may follow this, one + group of fields per line. +-T Prevents the transfer of the type declaration to the output file. + Use this option if the type is already defined elsewhere. +-v Prints out the current version number +-Z Allow user to specify name of generated C++ class. Default + name is `Perfect_Hash.' +Usage: ../src/gperf [-acCdDef[num]gGhH<hashname>i<init>jk<keys>K<keyname>lL<language>nN<function name>oprs<size>S<switches>tTvZ<class name>]. +(type ../src/gperf -h for help) diff --git a/apps/gperf/tests/test-7.exp b/apps/gperf/tests/test-7.exp new file mode 100644 index 00000000000..c5c942c10d1 --- /dev/null +++ b/apps/gperf/tests/test-7.exp @@ -0,0 +1,32 @@ +in word set if +in word set do +NOT in word set int +in word set for +in word set case +NOT in word set char +NOT in word set auto +in word set goto +in word set else +NOT in word set long +NOT in word set void +NOT in word set enum +NOT in word set float +NOT in word set short +NOT in word set union +NOT in word set break +in word set while +NOT in word set const +NOT in word set double +NOT in word set static +NOT in word set extern +NOT in word set struct +in word set return +NOT in word set sizeof +NOT in word set switch +NOT in word set signed +NOT in word set typedef +NOT in word set default +NOT in word set unsigned +NOT in word set continue +NOT in word set register +NOT in word set volatile diff --git a/apps/gperf/tests/test.c b/apps/gperf/tests/test.c new file mode 100644 index 00000000000..35d9015bba7 --- /dev/null +++ b/apps/gperf/tests/test.c @@ -0,0 +1,28 @@ +/* +// @(#)test.c 1.1 10/18/96 + + Tests the generated perfect has function. + The -v option prints diagnostics as to whether a word is in + the set or not. Without -v the program is useful for timing. +*/ + +#include <stdio.h> + +#define MAX_LEN 80 + +int +main (argc, argv) + int argc; + char *argv[]; +{ + int verbose = argc > 1 ? 1 : 0; + char buf[MAX_LEN]; + + while (gets (buf)) + if (in_word_set (buf, strlen (buf)) && verbose) + printf ("in word set %s\n", buf); + else if (verbose) + printf ("NOT in word set %s\n", buf); + + return 0; +} |