+
+
+class PHPDebuggerUI(BaseDebuggerUI):
+    DEFAULT_LISTENER_HOST     = "127.0.0.1"
+    DEFAULT_LISTENER_PORT     = 10001
+    DEFAULT_DBG_MOD_TIMEOUT   = 300
+    DEFAULT_DBG_MAX_EXEC_TIME = 240
+    dbgSessSeqId              = 1
+
+    def __init__(self, parent, id, command, service):
+        BaseDebuggerUI.__init__(self, parent, id)
+        #Note host and port need to come out of options or a pool.
+        self._dbgHost        = PHPDebuggerUI.DEFAULT_LISTENER_HOST
+        self._dbgPort        = PHPDebuggerUI.DEFAULT_LISTENER_PORT
+        self._dbgTimeout     = PHPDebuggerUI.DEFAULT_DBG_MOD_TIMEOUT
+        self._dbgMaxExecTime = PHPDebuggerUI.DEFAULT_DBG_MAX_EXEC_TIME
+        self._dbgSessId      = None
+        self._dbgSessParam   = None
+        self._dbgPhpIniFile  = None
+        self._callback       = PHPDebugger.PHPDebuggerCallback(self, service, self._dbgHost, self._dbgPort)
+        self._executor       = Executor(command, self)
+        self._service        = service
+        self._stopped        = False
+        self._allStopped     = False
+        
+        self._createPhpDbgSess()
+
+    def showErrorDialog(self, message, title):
+        wx.MessageBox(_(message), _(title))
+        return
+
+    def _createPhpDbgSess(self):
+        currTimeStr                = str(time.time())
+        (secStr, usecStr)          = currTimeStr.split('.')
+        secLongInt                 = long(secStr)
+        usecLongInt                = long(usecStr)
+        self._dbgSessId            = "%06ld%06ld%04d" % (secLongInt, usecLongInt, PHPDebuggerUI.dbgSessSeqId)
+        PHPDebuggerUI.dbgSessSeqId = PHPDebuggerUI.dbgSessSeqId + 1
+        self._dbgSessParam         = "DBGSESSID=%s@clienthost:%d" % (self._dbgSessId, self._dbgPort)
+
+        if _VERBOSE:
+            print "phpDbgParam=%s" % self._dbgSessParam
+
+        self._service.SetPhpDbgParam(self._dbgSessParam)
+
+    def _preparePhpIniFile(self):
+        success    = False
+
+        phpCgiExec = Executor.GetPHPExecutablePath()
+        phpExec    = phpCgiExec.replace("php-cgi", "php")
+        iniPath    = self._getPhpIniFromRunningPhp(phpExec)
+
+        try:
+            iniDbgPath = os.path.normpath(iniPath + ".ag_debug_enabled")
+            dbgFile    = open(iniDbgPath, "w")
+            oriFile    = open(iniPath, "r")
+    
+            while True:
+                oneOriLine = oriFile.readline()
+                if oneOriLine == '':
+                    break
+    
+                if not oneOriLine.startswith("debugger.") and not oneOriLine.startswith("max_execution_time="):
+                    dbgFile.write(oneOriLine)
+    
+            oriFile.close()
+    
+            if _WINDOWS:
+                dbgExtFile = "php_dbg.dll"
+            else:
+                dbgExtFile = "dbg.so"
+    
+            #
+            # TODO: we should make all of these options configurable.
+            #
+            configStr = "\n; ===============================================================\n; The followings are added by ActiveGrid IDE PHP Debugger Runtime\n; ===============================================================\n\n; As we are running with the dbg module, it takes a much longer time for each script to run.\nmax_execution_time=%d\n\n[debugger]\nextension=%s\ndebugger.enabled=On\ndebugger.JIT_enabled=On\ndebugger.JIT_host=%s\ndebugger.JIT_port=%d\ndebugger.fail_silently=Off\ndebugger.timeout_seconds=%d\ndebugger.ignore_nops=Off\ndebugger.enable_session_cookie=On\ndebugger.session_nocache=On\ndebugger.profiler_enabled=Off\n" % (self._dbgMaxExecTime, dbgExtFile, self._dbgHost, self._dbgPort, self._dbgTimeout)
+            dbgFile.write(configStr)
+            dbgFile.close()
+            success = True
+        except:
+            #TODO: print stack trace.
+            print "Caught exceptions while minipulating php.ini files"
+
+        if success:
+            self._dbgPhpIniFile = iniDbgPath
+        else:
+            self._dbgPhpIniFile = None
+
+    def _getPhpIniFromRunningPhp(self, phpExec):
+        phpIniPath = None
+
+        cmdEnv  = os.environ
+        if cmdEnv.has_key('PYTHONPATH'):
+            del cmdEnv['PYTHONPATH']
+
+        cmdLine = [phpExec, "-r", "phpinfo();"]
+        phpProc = subprocess.Popen(args=cmdLine, bufsize=0, executable=None, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, preexec_fn=None, close_fds=False, shell=False, cwd=None, env=os.environ, universal_newlines=False, startupinfo=None, creationflags=0)
+        phpOutput = phpProc.stdout
+
+        phpIniPattern = "Configuration File (php.ini) Path => "
+        while True:
+            oneLine = phpOutput.readline()
+            if oneLine == '':
+                break
+
+            if oneLine.startswith(phpIniPattern):
+                if oneLine.endswith("\n"):
+                    endIndex   = oneLine.index("\n")
+                    phpIniPath = oneLine[len(phpIniPattern):endIndex]
+                else:
+                    phpIniPath = oneLine[len(phpIniPattern):]
+
+                if phpIniPath and len(phpIniPath) > 0:
+                    phpIniPath = os.path.normpath(phpIniPath)
+                    break
+
+        phpOutput.close()
+
+        if _VERBOSE:
+            print "php.ini path is: %s" % repr(phpIniPath)
+
+        return phpIniPath
+
+    def Execute(self, initialArgs, startIn, environment, onWebServer = False):
+        self._preparePhpIniFile()
+        self._callback.Start()
+
+        if not onWebServer:
+            if self._dbgPhpIniFile:
+                args = '-c "' + self._dbgPhpIniFile + '" ' + initialArgs
+            else:
+                args = initialArgs
+
+            self._executor.Execute(args, startIn, environment)
+
+    def StopExecution(self, event):
+        # This is a general comment on shutdown for the running and debugged processes. Basically, the
+        # current state of this is the result of trial and error coding. The common problems were memory
+        # access violations and threads that would not exit. Making the OutputReaderThreads daemons seems
+        # to have side-stepped the hung thread issue. Being very careful not to touch things after calling
+        # process.py:ProcessOpen.kill() also seems to have fixed the memory access violations, but if there
+        # were more ugliness discovered I would not be surprised. If anyone has any help/advice, please send
+        # it on to mfryer@activegrid.com.
+        if not self._allStopped:
+            self._stopped = True
+            try:
+                self.DisableAfterStop()
+            except wx._core.PyDeadObjectError:
+                pass
+
+            try:
+                #
+                # If this is called by clicking the "Stop" button, we only stop
+                # the current running php script, and keep the listener
+                # running.
+                #
+                if event:
+                    self._callback.ShutdownServer(stopLsnr = False)
+                else:
+                    self._callback.ShutdownServer(stopLsnr = True)
+                    self._allStopped = True
+            except:
+                tp,val,tb = sys.exc_info()
+                traceback.print_exception(tp, val, tb)
+
+            try:
+                self.DeleteCurrentLineMarkers()
+            except:
+                pass
+
+            try:
+                if self._executor:
+                    self._executor.DoStopExecution()
+                    self._executor = None
+            except:
+                tp,val,tb = sys.exc_info()
+                traceback.print_exception(tp, val, tb)
+
+    def MakeFramesUI(self, parent, id, debugger):
+        return PHPFramesUI(parent, id, self)
+
+    def LoadPHPFramesList(self, stackList):
+        self.framesTab.LoadFramesList(stackList)
+
+    #
+    # TODO: this is a hack to overwrite BaseDebuggerUI's function.  The purpose
+    # is to always push breakpoints no matter if a php is running or not.  If
+    # no php is running, an exception will be thrown and handled like nothing
+    # happened.
+    #
+    def BreakPointChange(self):
+        self._callback.PushBreakpoints()
+        self.framesTab.PopulateBPList()
+
+
+class PythonDebuggerUI(BaseDebuggerUI):
+    debuggerPortList = None
+
+    def GetAvailablePort():
+        for index in range( 0, len(PythonDebuggerUI.debuggerPortList)):
+            port = PythonDebuggerUI.debuggerPortList[index]
+            if PythonDebuggerUI.PortAvailable(port):
+                PythonDebuggerUI.debuggerPortList.pop(index)
+                return port
+        wx.MessageBox(_("Out of ports for debugging!  Please restart the application builder.\nIf that does not work, check for and remove running instances of python."), _("Out of Ports"))
+        assert False, "Out of ports for debugger."
+
+    GetAvailablePort = staticmethod(GetAvailablePort)
+
+    def ReturnPortToPool(port):
+        config = wx.ConfigBase_Get()
+        startingPort = config.ReadInt("DebuggerStartingPort", DEFAULT_PORT)
+        val = int(startingPort) + int(PORT_COUNT)
+        if int(port) >= startingPort and (int(port) <= val):
+            PythonDebuggerUI.debuggerPortList.append(int(port))
+
+    ReturnPortToPool = staticmethod(ReturnPortToPool)
+
+    def PortAvailable(port):
+        config = wx.ConfigBase_Get()
+        hostname = config.Read("DebuggerHostName", DEFAULT_HOST)
+        try:
+            server = AGXMLRPCServer((hostname, port))
+            server.server_close()
+            if _VERBOSE: print "Port ", str(port), " available."
+            return True
+        except:
+            tp,val,tb = sys.exc_info()
+            if _VERBOSE: traceback.print_exception(tp, val, tb)
+            if _VERBOSE: print "Port ", str(port), " unavailable."
+            return False
+
+    PortAvailable = staticmethod(PortAvailable)
+
+    def NewPortRange():
+        config = wx.ConfigBase_Get()
+        startingPort = config.ReadInt("DebuggerStartingPort", DEFAULT_PORT)
+        PythonDebuggerUI.debuggerPortList = range(startingPort, startingPort + PORT_COUNT)
+    NewPortRange = staticmethod(NewPortRange)
+
+    def __init__(self, parent, id, command, service, autoContinue=True):
+        # Check for ports before creating the panel.
+        if not PythonDebuggerUI.debuggerPortList:
+            PythonDebuggerUI.NewPortRange()
+        self._debuggerPort = str(PythonDebuggerUI.GetAvailablePort())
+        self._guiPort = str(PythonDebuggerUI.GetAvailablePort())
+        self._debuggerBreakPort = str(PythonDebuggerUI.GetAvailablePort())
+        BaseDebuggerUI.__init__(self, parent, id)
+        self._command = command
+        self._service = service
+        config = wx.ConfigBase_Get()
+        self._debuggerHost = self._guiHost = config.Read("DebuggerHostName", DEFAULT_HOST)
+
+        url = 'http://' + self._debuggerHost + ':' + self._debuggerPort + '/'
+        self._breakURL = 'http://' + self._debuggerHost + ':' + self._debuggerBreakPort + '/'
+        self._callback = PythonDebuggerCallback(self._guiHost, self._guiPort, url, self._breakURL, self, autoContinue)
+        if DebuggerHarness.__file__.find('library.zip') > 0:
+            try:
+                fname = DebuggerHarness.__file__
+                parts = fname.split('library.zip')
+                path = os.path.join(parts[0],'activegrid', 'tool', 'DebuggerHarness.py')
+            except:
+                tp, val, tb = sys.exc_info()
+                traceback.print_exception(tp, val, tb)
+
+        else:
+            print "Starting debugger on these ports: %s, %s, %s" % (str(self._debuggerPort) , str(self._guiPort) , str(self._debuggerBreakPort))
+            path = DebuggerService.ExpandPath(DebuggerHarness.__file__)
+        self._executor = Executor(path, self, self._debuggerHost, \
+                                                self._debuggerPort, self._debuggerBreakPort, self._guiHost, self._guiPort, self._command, callbackOnExit=self.ExecutorFinished)
+
+        self._stopped = False
+
+    def LoadPythonFramesList(self, framesXML):
+        self.framesTab.LoadFramesList(framesXML)
+
+    def Execute(self, initialArgs, startIn, environment, onWebServer = False):
+        self._callback.Start()
+        self._executor.Execute(initialArgs, startIn, environment)
+        self._callback.WaitForRPC()
+
+
+    def StopExecution(self, event):
+        # This is a general comment on shutdown for the running and debugged processes. Basically, the
+        # current state of this is the result of trial and error coding. The common problems were memory
+        # access violations and threads that would not exit. Making the OutputReaderThreads daemons seems
+        # to have side-stepped the hung thread issue. Being very careful not to touch things after calling
+        # process.py:ProcessOpen.kill() also seems to have fixed the memory access violations, but if there
+        # were more ugliness discovered I would not be surprised. If anyone has any help/advice, please send
+        # it on to mfryer@activegrid.com.
+        if not self._stopped:
+            self._stopped = True
+            try:
+                self.DisableAfterStop()
+            except wx._core.PyDeadObjectError:
+                pass
+            try:
+                self._callback.ShutdownServer()
+            except:
+                tp,val,tb = sys.exc_info()
+                traceback.print_exception(tp, val, tb)
+
+            try:
+                self.DeleteCurrentLineMarkers()
+            except:
+                pass
+            try:
+                PythonDebuggerUI.ReturnPortToPool(self._debuggerPort)
+                PythonDebuggerUI.ReturnPortToPool(self._guiPort)
+                PythonDebuggerUI.ReturnPortToPool(self._debuggerBreakPort)
+            except:
+                pass
+            try:
+                if self._executor:
+                    self._executor.DoStopExecution()
+                    self._executor = None
+            except:
+                tp,val,tb = sys.exc_info()
+                traceback.print_exception(tp, val, tb)
+
+
+    def MakeFramesUI(self, parent, id, debugger):
+        panel = PythonFramesUI(parent, id, self)
+        return panel
+
+