branch merge
authorCraig Dowell <craigdo@ee.washington.edu>
Wed, 26 Nov 2008 13:53:28 -0800
changeset 3948 a8e97cf0aae3
parent 3947 756887a9bbea (current diff)
parent 3934 745312072e11 (diff)
child 3949 016c554c4f6d
branch merge
--- a/bindings/python/callbacks_list.py	Wed Nov 26 13:48:53 2008 -0800
+++ b/bindings/python/callbacks_list.py	Wed Nov 26 13:53:28 2008 -0800
@@ -5,6 +5,7 @@
     ['void', 'ns3::Ptr<ns3::Socket>', 'unsigned int', 'ns3::empty', 'ns3::empty', 'ns3::empty', 'ns3::empty'],
     ['void', 'ns3::Ptr<ns3::Socket>', 'ns3::Address const&', 'ns3::empty', 'ns3::empty', 'ns3::empty', 'ns3::empty'],
     ['bool', 'ns3::Ptr<ns3::Socket>', 'ns3::Address const&', 'ns3::empty', 'ns3::empty', 'ns3::empty', 'ns3::empty'],
+    ['bool', 'std::string', 'ns3::empty', 'ns3::empty', 'ns3::empty', 'ns3::empty', 'ns3::empty'],
     ['bool', 'ns3::Ptr<ns3::NetDevice>', 'ns3::Ptr<ns3::Packet const>', 'unsigned short', 'ns3::Address const&', 'ns3::Address const&', 'ns3::NetDevice::PacketType'],
     ['bool', 'ns3::Ptr<ns3::NetDevice>', 'ns3::Ptr<ns3::Packet const>', 'unsigned short', 'ns3::Address const&', 'ns3::empty', 'ns3::empty'],
     ['void', 'ns3::Ptr<ns3::NetDevice>', 'ns3::Ptr<ns3::Packet const>', 'unsigned short', 'ns3::Address const&', 'ns3::Address const&', 'ns3::NetDevice::PacketType'],
--- a/bindings/python/ns3_module_core.py	Wed Nov 26 13:48:53 2008 -0800
+++ b/bindings/python/ns3_module_core.py	Wed Nov 26 13:53:28 2008 -0800
@@ -12,7 +12,7 @@
     ## callback.h: ns3::CallbackImplBase [class]
     module.add_class('CallbackImplBase', allow_subclassing=True, memory_policy=cppclass.ReferenceCountingMethodsPolicy(incref_method='Ref', decref_method='Unref', peekref_method='GetReferenceCount'))
     ## command-line.h: ns3::CommandLine [class]
-    module.add_class('CommandLine')
+    module.add_class('CommandLine', allow_subclassing=True)
     ## system-mutex.h: ns3::CriticalSection [class]
     module.add_class('CriticalSection')
     ## global-value.h: ns3::GlobalValue [class]
@@ -350,6 +350,10 @@
     cls.add_constructor([param('ns3::CommandLine const &', 'arg0')])
     ## command-line.h: ns3::CommandLine::CommandLine() [constructor]
     cls.add_constructor([])
+    ## command-line.h: void ns3::CommandLine::AddValue(std::string const & name, std::string const & help, ns3::Callback<bool, std::string, ns3::empty, ns3::empty, ns3::empty, ns3::empty, ns3::empty> callback) [member function]
+    cls.add_method('AddValue', 
+                   'void', 
+                   [param('std::string const &', 'name'), param('std::string const &', 'help'), param('ns3::Callback< bool, std::string, ns3::empty, ns3::empty, ns3::empty, ns3::empty, ns3::empty >', 'callback')])
     return
 
 def register_Ns3CriticalSection_methods(root_module, cls):
--- a/bindings/python/ns3module_helpers.cc	Wed Nov 26 13:48:53 2008 -0800
+++ b/bindings/python/ns3module_helpers.cc	Wed Nov 26 13:53:28 2008 -0800
@@ -1,3 +1,4 @@
+#include "ns3/ref-count-base.h"
 #include "ns3module.h"
 
 
@@ -220,3 +221,61 @@
     
     return (PyObject *) py_tid;
 }
+
+
+class CommandLinePythonValueSetter : public ns3::RefCountBase
+{
+    PyObject *m_namespace;
+    std::string m_variable;
+public:
+    CommandLinePythonValueSetter (PyObject *ns, std::string const &variable) {
+        Py_INCREF(ns);
+        m_namespace = ns;
+        m_variable = variable;
+    }
+    bool Parse (std::string value) {
+        PyObject *pyvalue = PyString_FromStringAndSize (value.data(), value.size());
+        PyObject_SetAttrString (m_namespace, m_variable.c_str(), pyvalue);
+        if (PyErr_Occurred()) {
+            PyErr_Print();
+            return false;
+        }
+        return true;
+    }
+    virtual ~CommandLinePythonValueSetter () {
+        Py_DECREF (m_namespace);
+        m_namespace = NULL;
+    }
+    
+};
+
+PyObject *
+_wrap_CommandLine_AddValue(PyNs3CommandLine *self, PyObject *args, PyObject *kwargs,
+                           PyObject **return_exception)
+{
+    const char *name, *help, *variable = NULL;
+    PyObject *py_namespace = NULL;
+    const char *keywords[] = {"name", "help", "variable", "namespace", NULL};
+    
+    if (!PyArg_ParseTupleAndKeywords(args, kwargs, (char *) "ss|sO", (char **) keywords, &name, &help, &variable, &py_namespace)) {
+        PyObject *exc_type, *traceback;
+        PyErr_Fetch(&exc_type, return_exception, &traceback);
+        Py_XDECREF(exc_type);
+        Py_XDECREF(traceback);
+        return NULL;
+    }
+    
+    if (variable == NULL) {
+        variable = name;
+    }
+    if (py_namespace == NULL) {
+        py_namespace = (PyObject *) self;
+    }
+
+    ns3::Ptr<CommandLinePythonValueSetter> setter = ns3::Create<CommandLinePythonValueSetter> (py_namespace, variable);
+    self->obj->AddValue (name, help, ns3::MakeCallback (&CommandLinePythonValueSetter::Parse, setter));
+
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
--- a/bindings/python/ns3modulegen_core_customizations.py	Wed Nov 26 13:48:53 2008 -0800
+++ b/bindings/python/ns3modulegen_core_customizations.py	Wed Nov 26 13:53:28 2008 -0800
@@ -292,6 +292,8 @@
     CommandLine = module['ns3::CommandLine']
     CommandLine.add_method('Parse', None, [ArgvParam(None, 'argv')],
                            is_static=False)
+    CommandLine.add_custom_method_wrapper("AddValue", "_wrap_CommandLine_AddValue",
+                                          flags=["METH_VARARGS", "METH_KEYWORDS"])
 
 
 def Object_customizations(module):
@@ -523,5 +525,5 @@
     TypeId = module['ns3::TypeId']
     TypeId.add_custom_method_wrapper("LookupByNameFailSafe", "_wrap_TypeId_LookupByNameFailSafe",
                                      flags=["METH_VARARGS", "METH_KEYWORDS", "METH_STATIC"])
-    
 
+
--- a/bindings/python/ns3modulescan.py	Wed Nov 26 13:48:53 2008 -0800
+++ b/bindings/python/ns3modulescan.py	Wed Nov 26 13:53:28 2008 -0800
@@ -56,6 +56,11 @@
         'automatic_type_narrowing': 'true',
         'allow_subclassing': 'false',
         },
+
+    '::ns3::CommandLine': {
+        'allow_subclassing': 'true', # needed so that AddValue is able to set attributes on the object
+        },
+
     'ns3::RandomVariable::RandomVariable(ns3::RandomVariableBase const & variable) [constructor]': {
         'ignore': None,
         },
--- a/bindings/python/wscript	Wed Nov 26 13:48:53 2008 -0800
+++ b/bindings/python/wscript	Wed Nov 26 13:53:28 2008 -0800
@@ -309,6 +309,7 @@
    t1 > t2;
 }
 
+
 }
 """
     outfile.close()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/regression/tests/test-ns2-mob.py	Wed Nov 26 13:53:28 2008 -0800
@@ -0,0 +1,12 @@
+#! /usr/bin/env python
+
+"""Generic trace-comparison-type regression test."""
+
+import os
+import shutil
+import tracediff
+
+def run(verbose, generate):
+    """Execute a test."""
+    arguments = ["../../../samples/ns2-mob.tr", "out.tr"]
+    return tracediff.run_test(verbose, generate, "main-ns2-mob", arguments=arguments)
--- a/src/applications/v4ping/v4ping.cc	Wed Nov 26 13:48:53 2008 -0800
+++ b/src/applications/v4ping/v4ping.cc	Wed Nov 26 13:53:28 2008 -0800
@@ -149,6 +149,7 @@
 V4Ping::StopApplication (void)
 {
   NS_LOG_FUNCTION (this);
+  m_socket->Close ();
 }
 
 
--- a/src/common/pcap-writer.cc	Wed Nov 26 13:48:53 2008 -0800
+++ b/src/common/pcap-writer.cc	Wed Nov 26 13:53:28 2008 -0800
@@ -25,10 +25,14 @@
 
 #include <fstream>
 
+#include "ns3/log.h"
+#include "ns3/assert.h"
+#include "ns3/abort.h"
 #include "ns3/simulator.h"
 #include "pcap-writer.h"
 #include "packet.h"
 
+NS_LOG_COMPONENT_DEFINE ("PcapWriter");
 
 namespace ns3 {
 
@@ -41,53 +45,87 @@
 
 PcapWriter::PcapWriter ()
 {
+  NS_LOG_FUNCTION (this);
+  NS_LOG_LOGIC ("m_writer = 0");
   m_writer = 0;
 }
 
 PcapWriter::~PcapWriter ()
 {
+  NS_LOG_FUNCTION (this);
+
   if (m_writer != 0)
     {
-      m_writer->close ();
+      NS_LOG_LOGIC ("m_writer nonzero " << m_writer);
+      if (m_writer->is_open ())
+        {
+          NS_LOG_LOGIC ("m_writer open.  Closing " << m_writer);
+          m_writer->close ();
+        }
+
+      NS_LOG_LOGIC ("Deleting writer " << m_writer);
+      delete m_writer;
+
+      NS_LOG_LOGIC ("m_writer = 0");
+      m_writer = 0;
     }
-  delete m_writer;
-  m_writer = 0;
+  else
+    {
+      NS_LOG_LOGIC ("m_writer == 0");
+    }
 }
 
 void
 PcapWriter::Open (std::string const &name)
 {
+  NS_LOG_FUNCTION (this << name);
+  NS_ABORT_MSG_UNLESS (m_writer == 0, "PcapWriter::Open(): m_writer already allocated (std::ofstream leak detected)");
+
   m_writer = new std::ofstream ();
+  NS_ABORT_MSG_UNLESS (m_writer, "PcapWriter::Open(): Cannot allocate m_writer");
+
+  NS_LOG_LOGIC ("Created writer " << m_writer);
+
   m_writer->open (name.c_str ());
+  NS_ABORT_MSG_IF (m_writer->fail (), "PcapWriter::Open(): m_writer->open(" << name.c_str () << ") failed");
+
+  NS_ASSERT_MSG (m_writer->is_open (), "PcapWriter::Open(): m_writer not open");
+
+  NS_LOG_LOGIC ("Writer opened successfully");
 }
 
 void 
 PcapWriter::WriteEthernetHeader (void)
 {
+  NS_LOG_FUNCTION_NOARGS ();
   WriteHeader (PCAP_ETHERNET);
 }
 
 void 
 PcapWriter::WriteIpHeader (void)
 {
+  NS_LOG_FUNCTION_NOARGS ();
   WriteHeader (PCAP_RAW_IP);
 }
 
 void
 PcapWriter::WriteWifiHeader (void)
 {
+  NS_LOG_FUNCTION_NOARGS ();
   WriteHeader (PCAP_80211);
 }
 
 void 
 PcapWriter::WritePppHeader (void)
 {
+  NS_LOG_FUNCTION_NOARGS ();
   WriteHeader (PCAP_PPP);
 }
 
 void 
 PcapWriter::WriteHeader (uint32_t network)
 {
+  NS_LOG_FUNCTION (this << network);
   Write32 (0xa1b2c3d4);
   Write16 (2);
   Write16 (4);
--- a/src/core/command-line.cc	Wed Nov 26 13:48:53 2008 -0800
+++ b/src/core/command-line.cc	Wed Nov 26 13:53:28 2008 -0800
@@ -253,6 +253,27 @@
     }
 }
 
+bool
+CommandLine::CallbackItem::Parse (std::string value)
+{
+  NS_LOG_DEBUG ("CommandLine::CallbackItem::Parse \"" << value << "\"");
+  return m_callback (value);
+}
+
+void
+CommandLine::AddValue (const std::string &name,
+                       const std::string &help,
+                       Callback<bool, std::string> callback)
+{
+  NS_LOG_FUNCTION (this << name << help << "callback");
+  CallbackItem *item = new CallbackItem ();
+  item->m_name = name;
+  item->m_help = help;
+  item->m_callback = callback;
+  m_items.push_back (item);
+}
+
+
 } // namespace ns3
 
 #ifdef RUN_SELF_TESTS
--- a/src/core/command-line.h	Wed Nov 26 13:48:53 2008 -0800
+++ b/src/core/command-line.h	Wed Nov 26 13:53:28 2008 -0800
@@ -24,6 +24,8 @@
 #include <sstream>
 #include <list>
 
+#include "ns3/callback.h"
+
 namespace ns3 {
 
 /**
@@ -56,6 +58,17 @@
 		 const std::string &help,
 		 T &value);
 
+
+  /**
+   * \param name the name of the user-supplied argument
+   * \param help some help text used by --PrintHelp
+   * \param callback a callback function that will be invoked to parse
+   * and collect the value.  This normally used by language bindings.
+   */
+  void AddValue (const std::string &name,
+		 const std::string &help,
+                 Callback<bool, std::string> callback);
+
   /**
    * \param argc the 'argc' variable: number of arguments (including the
    *        main program name as first element).
@@ -82,6 +95,13 @@
     virtual bool Parse (std::string value);
     T *m_valuePtr;
   };
+  class CallbackItem : public Item
+  {
+  public:
+    virtual bool Parse (std::string value);
+    Callback<bool, std::string> m_callback;
+  };
+
   void HandleArgument (std::string name, std::string value) const;
   void PrintHelp (void) const;
   void PrintGlobals (void) const;
--- a/src/devices/wifi/mac-rx-middle.cc	Wed Nov 26 13:48:53 2008 -0800
+++ b/src/devices/wifi/mac-rx-middle.cc	Wed Nov 26 13:53:28 2008 -0800
@@ -260,56 +260,44 @@
 MacRxMiddle::Receive (Ptr<Packet> packet, WifiMacHeader const *hdr)
 {
   NS_LOG_FUNCTION (packet << hdr);
+  NS_ASSERT (hdr->IsData () || hdr->IsMgt ());
   OriginatorRxStatus *originator = Lookup (hdr);
-  if (hdr->IsData ()) 
+  /**
+   * The check below is really uneeded because it can fail in a lot of
+   * normal cases. Specifically, it is possible for sequence numbers to 
+   * loop back to zero once they reach 0xfff0 and to go up to 0xf7f0 in 
+   * which case the check below will report the two sequence numbers to 
+   * not have the correct order relationship.
+   * So, this check cannot be used to discard old duplicate frames. It is
+   * thus here only for documentation purposes.
+   */
+  if (!SequenceControlSmaller (originator->GetLastSequenceControl (), 
+                               hdr->GetSequenceControl ()))
     {
-      /**
-       * Note that the check below is not deterministic: it is possible
-       * for sequence numbers to loop back to zero once they reach 0xfff0
-       * and to go up to 0xf7f0 in which case the check below will report the
-       * two sequence numbers to not have the correct order relationship.
-       * This is why this check generates a warning only.
-       */
-      if (!SequenceControlSmaller (originator->GetLastSequenceControl (), 
-                                   hdr->GetSequenceControl ()))
-        {
-          NS_LOG_UNCOND ("Sequence numbers have looped back. last recorded="<<originator->GetLastSequenceControl ()<<
-                         " currently seen="<< hdr->GetSequenceControl ());
-        }
-      // filter duplicates.
-      if (IsDuplicate (hdr, originator)) 
-        {
-          NS_LOG_DEBUG ("duplicate from="<<hdr->GetAddr2 ()<<
-                 ", seq="<<hdr->GetSequenceNumber ()<<
-                 ", frag="<<hdr->GetFragmentNumber ());
-          return;
-        }
-      Ptr<Packet> agregate = HandleFragments (packet, hdr, originator);
-      if (agregate == 0) 
-        {
-          return;
-        }
-      NS_LOG_DEBUG ("forwarding data from="<<hdr->GetAddr2 ()<<
-             ", seq="<<hdr->GetSequenceNumber ()<<
-             ", frag="<<hdr->GetFragmentNumber ());
-      if (!hdr->GetAddr1 ().IsBroadcast ())
-        {
-          originator->SetSequenceControl (hdr->GetSequenceControl ());
-        }
-      m_callback (agregate, hdr);
-    } 
-  else 
+      NS_LOG_DEBUG ("Sequence numbers have looped back. last recorded="<<originator->GetLastSequenceControl ()<<
+                    " currently seen="<< hdr->GetSequenceControl ());
+    }
+  // filter duplicates.
+  if (IsDuplicate (hdr, originator)) 
     {
-      NS_LOG_DEBUG ("forwarding "<<hdr->GetTypeString ()<<
-             ", from="<<hdr->GetAddr2 ()<<
-             ", seq="<<hdr->GetSequenceNumber ()<<
-             ", frag="<<hdr->GetFragmentNumber ());
-      if (!hdr->GetAddr1 ().IsBroadcast ())
-        {
-          originator->SetSequenceControl (hdr->GetSequenceControl ());
-        }
-      m_callback (packet, hdr);
+      NS_LOG_DEBUG ("duplicate from="<<hdr->GetAddr2 ()<<
+                    ", seq="<<hdr->GetSequenceNumber ()<<
+                    ", frag="<<hdr->GetFragmentNumber ());
+      return;
     }
+  Ptr<Packet> agregate = HandleFragments (packet, hdr, originator);
+  if (agregate == 0) 
+    {
+      return;
+    }
+  NS_LOG_DEBUG ("forwarding data from="<<hdr->GetAddr2 ()<<
+                ", seq="<<hdr->GetSequenceNumber ()<<
+                ", frag="<<hdr->GetFragmentNumber ());
+  if (!hdr->GetAddr1 ().IsBroadcast ())
+    {
+      originator->SetSequenceControl (hdr->GetSequenceControl ());
+    }
+  m_callback (agregate, hdr);
 }
 
 } // namespace ns3
--- a/src/devices/wifi/nqsta-wifi-mac.cc	Wed Nov 26 13:48:53 2008 -0800
+++ b/src/devices/wifi/nqsta-wifi-mac.cc	Wed Nov 26 13:53:28 2008 -0800
@@ -475,10 +475,25 @@
   if (hdr->GetAddr1 () != GetAddress () &&
       !hdr->GetAddr1 ().IsBroadcast ()) 
     {
-      // packet is not for us
+      NS_LOG_LOGIC ("packet is not for us");
     } 
   else if (hdr->IsData ()) 
     {
+      if (!IsAssociated ())
+        {
+          NS_LOG_LOGIC ("Received data frame while not associated: ignore");
+          return;
+        }
+      if (!(hdr->IsFromDs () && !hdr->IsToDs ()))
+        {
+          NS_LOG_LOGIC ("Received data frame not from the DS: ignore");
+          return;
+        }
+      if (hdr->GetAddr2 () != GetBssid ())
+        {
+          NS_LOG_LOGIC ("Received data frame not from the the BSS we are associated with: ignore");
+          return;
+        }
       if (hdr->GetAddr3 () != GetAddress ())
         {
           ForwardUp (packet, hdr->GetAddr3 (), hdr->GetAddr1 ());
@@ -499,12 +514,16 @@
       if (GetSsid ().IsBroadcast () ||
           beacon.GetSsid ().IsEqual (GetSsid ()))
         {
+          goodBeacon = true;
+        }
+      if (IsAssociated () && hdr->GetAddr3 () != GetBssid ())
+        {
+          goodBeacon = false;
+        }
+      if (goodBeacon)
+        {
           Time delay = MicroSeconds (beacon.GetBeaconIntervalUs () * m_maxMissedBeacons);
           RestartBeaconWatchdog (delay);
-          goodBeacon = true;
-        }
-      if (goodBeacon) 
-        {
           SetBssid (hdr->GetAddr3 ());
         }
       if (goodBeacon && m_state == BEACON_MISSED) 
--- a/src/mobility/static-speed-helper.cc	Wed Nov 26 13:48:53 2008 -0800
+++ b/src/mobility/static-speed-helper.cc	Wed Nov 26 13:53:28 2008 -0800
@@ -24,9 +24,11 @@
 namespace ns3 {
 
 StaticSpeedHelper::StaticSpeedHelper ()
+  : m_paused (true)
 {}
 StaticSpeedHelper::StaticSpeedHelper (const Vector &position)
-  : m_position (position)
+  : m_position (position),
+    m_paused (true)
 {}
 StaticSpeedHelper::StaticSpeedHelper (const Vector &position,
 				      const Vector &vel)
--- a/src/node/application.cc	Wed Nov 26 13:48:53 2008 -0800
+++ b/src/node/application.cc	Wed Nov 26 13:53:28 2008 -0800
@@ -58,6 +58,7 @@
   m_node = 0;
   Simulator::Cancel(m_startEvent);
   Simulator::Cancel(m_stopEvent);
+  Object::DoDispose ();
 }  
    
 void Application::Start(const Time& startTime)
--- a/src/simulator/simulator.cc	Wed Nov 26 13:48:53 2008 -0800
+++ b/src/simulator/simulator.cc	Wed Nov 26 13:53:28 2008 -0800
@@ -58,7 +58,7 @@
 static void
 TimePrinter (std::ostream &os)
 {
-  os << Simulator::Now ();
+  os << Simulator::Now ().GetSeconds () << "s";
 }
 
 #endif /* NS3_LOG_ENABLE */
--- a/utils/python-unit-tests.py	Wed Nov 26 13:48:53 2008 -0800
+++ b/utils/python-unit-tests.py	Wed Nov 26 13:53:28 2008 -0800
@@ -123,5 +123,26 @@
         
         self.assertRaises(KeyError, ns3.TypeId.LookupByNameFailSafe, "__InvalidTypeName__")
 
+    def testCommandLine(self):
+        cmd = ns3.CommandLine()
+        cmd.AddValue("Test1", "this is a test option")
+        cmd.AddValue("Test2", "this is a test option")
+        cmd.AddValue("Test3", "this is a test option", variable="test_xxx")
+        cmd.Test1 = None
+        cmd.Test2 = None
+        cmd.test_xxx = None
+        class Foo:
+            pass
+        foo = Foo()
+        foo.test_foo = None
+        cmd.AddValue("Test4", "this is a test option", variable="test_foo", namespace=foo)
+        
+        cmd.Parse(["python", "--Test1=value1", "--Test2=value2", "--Test3=123", "--Test4=xpto"])
+
+        self.assertEqual(cmd.Test1, "value1")
+        self.assertEqual(cmd.Test2, "value2")
+        self.assertEqual(cmd.test_xxx, "123")
+        self.assertEqual(foo.test_foo, "xpto")
+
 if __name__ == '__main__':
     unittest.main()
--- a/wscript	Wed Nov 26 13:48:53 2008 -0800
+++ b/wscript	Wed Nov 26 13:53:28 2008 -0800
@@ -331,11 +331,13 @@
             obj.path = obj.path.find_dir('scratch')
             obj.find_sources_in_dirs(filename)
             obj.target = os.path.join(filename, filename)
+            obj.name = obj.target
         elif filename.endswith(".cc"):
             name = filename[:-len(".cc")]
             obj = bld.create_ns3_program(name, all_modules)
             obj.source = "scratch/%s" % filename
             obj.target = "scratch/%s" % name
+            obj.name = obj.target
 
 
 ##
@@ -445,16 +447,13 @@
 
     if Params.g_options.run:
         # Check that the requested program name is valid
-        try:
-            wutils.find_program(Params.g_options.run, env)
-        except ValueError, ex:
-            Params.fatal(str(ex))
-        
+        program_name, dummy_program_argv = wutils.get_run_program(Params.g_options.run, get_command_template())
+
         # When --run'ing a program, tell WAF to only build that program,
         # nothing more; this greatly speeds up compilation when all you
         # want to do is run a test program.
         if not Params.g_options.compile_targets:
-            Params.g_options.compile_targets = Params.g_options.run
+            Params.g_options.compile_targets = program_name
 
 
 
--- a/wutils.py	Wed Nov 26 13:48:53 2008 -0800
+++ b/wutils.py	Wed Nov 26 13:53:28 2008 -0800
@@ -94,13 +94,12 @@
         Params.fatal("Command %s exited with code %i" % (argv, retval))
     return retval
 
-def run_program(program_string, command_template=None):
+def get_run_program(program_string, command_template=None):
     """
-    if command_template is not None, then program_string == program
-    name and argv is given by command_template with %s replaced by the
-    full path to the program.  Else, program_string is interpreted as
-    a shell command with first name being the program name.
+    Return the program name and argv of the process that would be executed by
+    run_program(program_string, command_template).
     """
+    #print "get_run_program_argv(program_string=%r, command_template=%r)" % (program_string, command_template)
     env = Params.g_build.env_of_name('default')
 
     if command_template in (None, '%s'):
@@ -132,7 +131,16 @@
             Params.fatal("%s does not appear to be a program" % (program_name,))
 
         execvec = shlex.split(command_template % (program_node.abspath(env),))
+    return program_name, execvec
 
+def run_program(program_string, command_template=None):
+    """
+    if command_template is not None, then program_string == program
+    name and argv is given by command_template with %s replaced by the
+    full path to the program.  Else, program_string is interpreted as
+    a shell command with first name being the program name.
+    """
+    dummy_program_name, execvec = get_run_program(program_string, command_template)
     former_cwd = os.getcwd()
     if (Params.g_options.cwd_launch):
         os.chdir(Params.g_options.cwd_launch)