Copyright (C) IBM Corp. 2006
Author(s): Charles P. Wright (cpwright@us.ibm.com)

/*
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  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
 *
 *  Authors : Benjamin GAUTHIER - 24 Mar 2004
 *            Joseph BANINO
 *            Olivier JACQUES
 *            Richard GAYRAUD
 *            From Hewlett Packard Company.
 *            Guillaume Teissier from FTR&D
 *            Shriram Natarajan
 *            Peter Higginson
 *            Venkatesh
 *            Lee Ballard
 *            Wolfgang Beck
 *            Marc Van Diest from Belgacom
 *	      Charles P. Wright from IBM Research
 */
Index: trunk/call.cpp
===================================================================
--- trunk.orig/call.cpp
+++ trunk/call.cpp
@@ -1349,7 +1349,7 @@ bool call::next()
   /* Default without branching: use the next message */
   int new_msg_index = msg_index+1;
   /* If branch needed, overwrite this default */
-  if ( scenario[msg_index]->next && 
+  if ( (scenario[msg_index]->next >= 0) &&
        ((test == -1) ||
         (test <= maxVariableUsed && M_callVariableTable[test] != NULL && M_callVariableTable[test]->isSet()))
      ) {
@@ -1357,7 +1357,7 @@ bool call::next()
     int chance = scenario[msg_index]->chance;
     if ((chance <= 0) || (rand() > chance )) {
       /* Branch == overwrite with the 'next' attribute value */
-      new_msg_index = labelArray[scenario[msg_index]->next];
+      new_msg_index = scenario[msg_index]->next;
     }
   }
   msg_index=new_msg_index;
@@ -1415,10 +1415,10 @@ bool call::run()
     if((nb_retrans > (bInviteTransaction ? max_invite_retrans : max_non_invite_retrans)) ||
        (nb_retrans > max_udp_retrans)) {
       scenario[last_send_index] -> nb_timeout ++;
-      if (scenario[last_send_index]->on_timeout) {  // action on timeout
+      if (scenario[last_send_index]->on_timeout >= 0) {  // action on timeout
           WARNING_P3("Call-Id: %s, timeout on max UDP retrans for message %d, jumping to label %d ", 
                       id, msg_index, scenario[last_send_index]->on_timeout);
-          msg_index = labelArray[scenario[last_send_index]->on_timeout];
+          msg_index = scenario[last_send_index]->on_timeout;
           next_retrans = 0;
           recv_timeout = 0;
           if (msg_index < scenario_len) {
@@ -1642,7 +1642,7 @@ bool call::run()
       }
       recv_timeout = 0;
       ++scenario[msg_index]->nb_timeout;
-      if (scenario[msg_index]->on_timeout == 0) {
+      if (scenario[msg_index]->on_timeout < 0) {
         // if you set a timeout but not a label, the call is aborted 
         WARNING_P2("Call-Id: %s, receive timeout on message %d without label to jump to (ontimeout attribute): aborting call", 
                    id, msg_index);
@@ -1657,7 +1657,7 @@ bool call::run()
       }
       WARNING_P3("Call-Id: %s, receive timeout on message %d, jumping to label %d", 
                   id, msg_index, scenario[msg_index]->on_timeout);
-      msg_index = labelArray[scenario[msg_index]->on_timeout];
+      msg_index = scenario[msg_index]->on_timeout;
       recv_timeout = 0;
       if (msg_index < scenario_len) return true;
       // special case - the label points to the end - finish the call
Index: trunk/scenario.cpp
===================================================================
--- trunk.orig/scenario.cpp
+++ trunk/scenario.cpp
@@ -66,8 +66,8 @@ message::message()
   crlf = 0;
   test = 0;
   chance = 0;/* meaning always */
-  next = 0;
-  on_timeout = 0;
+  next = -1;
+  on_timeout = -1;
 
 /* 3pcc extended mode */
   peer_dest = NULL;
@@ -147,9 +147,13 @@ int           scenario_len = 0;
 char          scenario_name[255];
 int           toolMode  = MODE_CLIENT;
 unsigned long scenario_duration = 0;
-unsigned int  labelArray[MAX_LABELS];
 bool	      rtd_stopped[MAX_RTD_INFO_LENGTH];
 bool	      rtd_started[MAX_RTD_INFO_LENGTH];
+/* The mapping of labels to IDs. */
+var_map	      labelMap;
+/* The string label representations. */
+char	      *nextLabels[SCEN_MAX_MESSAGES];
+char	      *ontimeoutLabels[SCEN_MAX_MESSAGES];
 
 /*************** Helper functions for various types *****************/
 long get_long(const char *ptr, const char *what) {
@@ -517,6 +521,26 @@ void validate_variable_usage() {
   }
 }
 
+/* Apply the next and ontimeout labels according to our map. */
+void apply_labels() {
+  for (int i = 0; i <= scenario_len; i++) {
+    if (nextLabels[i]) {
+      var_map::iterator label_it = labelMap.find(nextLabels[i]);
+      if (label_it == labelMap.end()) {
+	ERROR_P2("The label '%s' was not defined (index %d, next attribute)\n", nextLabels[i], i);
+      }
+      scenario[i]->next = label_it->second;
+    }
+    if (ontimeoutLabels[i]) {
+      var_map::iterator label_it = labelMap.find(ontimeoutLabels[i]);
+      if (label_it == labelMap.end()) {
+	ERROR_P2("The label '%s' was not defined (index %d, ontimeout attribute)\n", ontimeoutLabels[i], i);
+      }
+      scenario[i]->on_timeout = label_it->second;
+    }
+  }
+}
+
 void init_rtds()
 {
   for (int i = 0; i < MAX_RTD_INFO_LENGTH; i++) {
@@ -539,6 +563,7 @@ int get_cr_number(char *src)
 
 void load_scenario(char * filename, int deflt)
 {
+  static int loaded = 0;
   char * elem;
   char method_list[METHOD_LIST_LENGTH]; // hopefully the method list wont be longer than this
   char method_list_length = 0;           // Enforce length, in case...
@@ -549,6 +574,11 @@ void load_scenario(char * filename, int 
   char * peer; 
   memset (method_list, 0, sizeof (method_list));
 
+  if (loaded) {
+    ERROR("You may only specify a single scenario!\n");
+  }
+  loaded++;
+
   if(filename) {
     if(!xp_set_xml_buffer_from_file(filename)) {
       ERROR_P1("Unable to load or parse '%s' xml scenario file", filename);
@@ -590,10 +620,10 @@ void load_scenario(char * filename, int 
       CStat::instance()->setRepartitionResponseTime(ptr);
     } else if(!strcmp(elem, "label")) {
       ptr = xp_get_value((char *)"id");
-      unsigned int labelNumber = get_long(ptr, "label identifier");
-      if (labelNumber < (sizeof(labelArray)/sizeof(labelArray[0]))) {
-       labelArray[labelNumber] = ::scenario_len;
+      if (labelMap.find(ptr) != labelMap.end()) {
+	ERROR_P1("The label name '%s' is used twice.", ptr);
       }
+      labelMap[ptr] = ::scenario_len;
     } else { /** Message Case */
       scenario[scenario_len]    = new message();
       scenario[scenario_len] -> content_length_flag = message::ContentLengthNoPresent;   // Initialize to No present
@@ -957,8 +987,8 @@ void load_scenario(char * filename, int 
         scenario[scenario_len] -> crlf = 1;
       }
 
-      if ( 0 != ( ptr = xp_get_value((char *)"next") ) ) {
-        scenario[scenario_len] -> next = get_long(ptr, "next jump");
+      if ((ptr = xp_get_value((char *)"next"))) {
+        nextLabels[scenario_len] = strdup(ptr);
 	scenario[scenario_len] -> test = xp_get_var("test", "test variable", -1);
 	if ( 0 != ( ptr = xp_get_value((char *)"chance") ) ) {
 	  float chance = get_double(ptr,"chance");
@@ -969,16 +999,12 @@ void load_scenario(char * filename, int 
 	  scenario[scenario_len] -> chance = (int)((1.0-chance)*RAND_MAX);
 	}
 	else {
-	  scenario[scenario_len] -> chance = 0;/* always */
+	  scenario[scenario_len] -> chance = 0; /* always */
 	}
-      } else {
-        scenario[scenario_len] -> next = 0;
       }
 
-      if (0 != (ptr = xp_get_value((char *)"ontimeout")) ) {
-        if ((::scenario[scenario_len]->on_timeout = get_long(ptr, "timeout jump")) >= MAX_LABELS) {
-            ERROR_P1("Ontimeout label larger than max supported %d", MAX_LABELS-1);
-        }
+      if ((ptr = xp_get_value((char *)"ontimeout"))) {
+	ontimeoutLabels[scenario_len] = ptr;
       }
      
       if (++scenario_len >= SCEN_MAX_MESSAGES) {
@@ -988,6 +1014,9 @@ void load_scenario(char * filename, int 
     xp_close_element();
   } // end while
 
+  /* Patch up the labels. */
+  apply_labels();
+
   /* Some post-scenario loading validation. */
   validate_rtds();
   if (scenario_len == 0) {
Index: trunk/scenario.hpp
===================================================================
--- trunk.orig/scenario.hpp
+++ trunk/scenario.hpp
@@ -66,7 +66,6 @@
 #define OPTIONAL_TRUE      1
 #define OPTIONAL_FALSE     0
 #define OPTIONAL_GLOBAL    2
-#define MAX_LABELS       100
 
 class message {
 
@@ -115,10 +114,10 @@ public:
   int		 counter;
   double         lost;
   int            crlf;
-  unsigned int   next;
+  int		 next;
   int            test;
   int            chance;/* 0=always, RAND_MAX+1=never (test rand() >= chance) */
-  unsigned int   on_timeout;
+  int		 on_timeout;
 
   /* Statistics */
   unsigned long   nb_sent;
@@ -195,7 +194,6 @@ void freeStringTable(char ** stringList,
 
 int find_scenario(const char *scenario);
 extern char * default_scenario[10];
-extern unsigned int  labelArray[MAX_LABELS];
 
 /* Useful utility functions for parsing integers, etc. */
 long get_long(const char *ptr, const char *what);
