root/xdebug_handler_gdb.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. scan_cmd
  2. make_message
  3. send_message
  4. lookup_cmd_in_group
  5. lookup_cmd
  6. show_available_commands_in_group
  7. show_available_commands
  8. show_command_info
  9. return_printable_symbol
  10. dump_used_var
  11. dump_used_var_with_contents
  12. print_sourceline
  13. print_breakpoint
  14. print_stackframe
  15. xdebug_handle_backtrace
  16. xdebug_handle_breakpoint
  17. xdebug_handle_cont
  18. xdebug_handle_delete
  19. xdebug_handle_eval
  20. xdebug_handle_finish
  21. xdebug_handle_kill
  22. xdebug_handle_list
  23. xdebug_handle_next
  24. xdebug_handle_option
  25. xdebug_handle_print
  26. xdebug_handle_pwd
  27. xdebug_handle_quit
  28. xdebug_handle_run
  29. dump_class_breakpoint
  30. dump_function_breakpoint
  31. dump_line_breakpoint
  32. xdebug_handle_show_breakpoints
  33. show_local_vars
  34. xdebug_handle_show
  35. xdebug_handle_show_local
  36. xdebug_handle_step
  37. xdebug_gdb_parse_option
  38. xdebug_gdb_option_result
  39. xdebug_gdb_get_revision
  40. xdebug_gdb_init
  41. xdebug_gdb_deinit
  42. xdebug_gdb_error
  43. xdebug_gdb_breakpoint

/*
   +----------------------------------------------------------------------+
   | Xdebug                                                               |
   +----------------------------------------------------------------------+
   | Copyright (c) 2002, 2003, 2004, 2005, 2006 Derick Rethans            |
   +----------------------------------------------------------------------+
   | This source file is subject to version 1.0 of the Xdebug license,    |
   | that is bundled with this package in the file LICENSE, and is        |
   | available at through the world-wide-web at                           |
   | http://xdebug.derickrethans.nl/license.php                           |
   | If you did not receive a copy of the Xdebug license and are unable   |
   | to obtain it through the world-wide-web, please send a note to       |
   | xdebug@derickrethans.nl so we can mail you a copy immediately.       |
   +----------------------------------------------------------------------+
   | Authors:  Derick Rethans <derick@xdebug.org>                         |
   +----------------------------------------------------------------------+
 */

#include "php.h"
#include "ext/standard/php_string.h"
#include "ext/standard/url.h"
#include "TSRM.h"
#include "php_globals.h"
#include "php_xdebug.h"
#include "xdebug_private.h"
#include "xdebug_com.h"
#include "xdebug_llist.h"
#include "xdebug_handler_gdb.h"
#include "xdebug_var.h"

#ifdef PHP_WIN32
#include "win32/time.h"
#include <process.h>
#endif
#include <fcntl.h>

ZEND_EXTERN_MODULE_GLOBALS(xdebug)
/*****************************************************************************
** Prototypes for debug command handlers
*/

static char *xdebug_handle_backtrace(xdebug_con *context, xdebug_arg *args);
static char *xdebug_handle_breakpoint(xdebug_con *context, xdebug_arg *args);
static char *xdebug_handle_cont(xdebug_con *context, xdebug_arg *args);
static char *xdebug_handle_eval(xdebug_con *context, xdebug_arg *args);
static char *xdebug_handle_delete(xdebug_con *context, xdebug_arg *args);
static char *xdebug_handle_finish(xdebug_con *context, xdebug_arg *args);
static char *xdebug_handle_kill(xdebug_con *context, xdebug_arg *args);
static char *xdebug_handle_list(xdebug_con *context, xdebug_arg *args);
static char *xdebug_handle_next(xdebug_con *context, xdebug_arg *args);
static char *xdebug_handle_option(xdebug_con *context, xdebug_arg *args);
static char *xdebug_handle_print(xdebug_con *context, xdebug_arg *args);
static char *xdebug_handle_pwd(xdebug_con *context, xdebug_arg *args);
static char *xdebug_handle_quit(xdebug_con *context, xdebug_arg *args);
static char *xdebug_handle_run(xdebug_con *context, xdebug_arg *args);
static char *xdebug_handle_show(xdebug_con *context, xdebug_arg *args);
static char *xdebug_handle_show_breakpoints(xdebug_con *context, xdebug_arg *args);
static char *xdebug_handle_show_local(xdebug_con *context, xdebug_arg *args);
static char *xdebug_handle_step(xdebug_con *context, xdebug_arg *args);

/*****************************************************************************
** Dispatcher tables for supported debug commands
*/

static xdebug_gdb_cmd commands_init[] = {
        { "option", 2, "option [setting] [value]", xdebug_handle_option, 1, "Set a debug session option" },
        { "run",    0, "run",                      xdebug_handle_run,    1, "Start the script" },
        { NULL, 0, NULL, NULL, 0, NULL }
};

static xdebug_gdb_cmd commands_breakpoint[] = {
        { "break",      1, "bre(ak) [functionname|filename:linenumber]", xdebug_handle_breakpoint, 1,
                "Set breakpoint at specified line or function.\n"
                "             Argument may be filename and linenumber, function name or '{main}'\n"
                "             for the first PHP line."
        },
        { "bre",        1, "bre(ak) [functionname|filename:linenumber]", xdebug_handle_breakpoint, 0,
                "Set breakpoint at specified line or function.\n"
                "             Argument may be filename and linenumber, function name or '{main}'\n"
                "             for the first PHP line."
        },
        { "delete",     1, "del(ete) [functionname|filename:linenumber]", xdebug_handle_delete, 1,
                "Removed breakpoint at specified line or function.\n"
                "             Argument may be filename and linenumber, function name or '{main}'\n"
                "             for the first PHP line."
        },
        { "del",        1, "del(ete) [functionname|filename:linenumber]", xdebug_handle_delete, 0,
                "Removed breakpoint at specified line or function.\n"
                "             Argument may be filename and linenumber, function name or '{main}'\n"
                "             for the first PHP line."
        },
        { "show-breakpoints", 0, "show-breakpoints",                      xdebug_handle_show_breakpoints, 1,
                "Shows a list of all breakpoints and optional conditions."
        },
        { NULL, 0, NULL, NULL, 0, NULL }
};

static xdebug_gdb_cmd commands_data[] = {
        { "eval",       1, "eval [php code to execute]",                 xdebug_handle_eval,       1,
                "Evaluation PHP code"
        },
        { "show",       0, "show",                                       xdebug_handle_show,       1,
                "Show a list of all variables"
        },
        { "show-local", 0, "show-local",                                 xdebug_handle_show_local, 1,
                "Show a list of all variables including their contents"
        },
        { "print",      1, "print",                                      xdebug_handle_print,      1,
                "Prints the contents of the variable"
        },
        { NULL, 0, NULL, NULL, 0, NULL }
};

static xdebug_gdb_cmd commands_run[] = {
        { "kill",       0, "kill",                                       xdebug_handle_kill,       1, "Kill the script" },
        { "quit",       0, "quit",                                       xdebug_handle_quit,       1, "Close the debug session" },
        { NULL, 0, NULL, NULL, 0, NULL }
};

static xdebug_gdb_cmd commands_runtime[] = {
        { "backtrace",  0, "backtrace [full]",                           xdebug_handle_backtrace,  0,
                "Print backtrace of all stack frames.\n"
                "             Use of the 'full' qualifier also prints the values of the local\n"
                "             variables."
        },
        { "bt",         0, "bt [full]",                                  xdebug_handle_backtrace,  1,
                "Print backtrace of all stack frames.\n"
                "             Use of the 'full' qualifier also prints the values of the local\n"
                "             variables."
        },

        { "cont",       0, "cont(inue)",                                 xdebug_handle_cont,       0,
                "Continue script being debugged, after error or breakpoint."
        },
        { "continue",   0, "cont(inue)",                                 xdebug_handle_cont,       1,
                "Continue script being debugged, after error or breakpoint."
        },

        { "finish",     0, "finish",                                     xdebug_handle_finish,     1,
                "Continues executing until the current function returned to the\n"
                "             calling function."
        },

        { "list",       0, "list [[file:]beginline] [endline]",          xdebug_handle_list,       1,
                "Lists specified line. With no arguments, lists ten more lines\n"
                "             after or before the previous listing. One argument specifies the\n"
                "             the line in a file to start, and then lines are listed around that\n"
                "             line. Two arguments specify starting and ending lines to list."
        },

        { "next",       0, "next",                                       xdebug_handle_next,       1,
                "Continues executing until the next statement in the same stack\n"
                "             frame. Is basically the same as 'step' but does not go into\n"
                "             function calls if they occur."
        },

        { "pwd",        0, "pwd",                                        xdebug_handle_pwd,        1,
                "Prints the current working directory."
        },

        { "step",       0, "step",                                       xdebug_handle_step,       1,
                "Continues executing until the next statement."
        },

        { NULL, 0, NULL, NULL, 0, NULL }
};

/*****************************************************************************
** Utility functions
*/

static xdebug_gdb_cmd* scan_cmd(xdebug_gdb_cmd *ptr, char *line)
{
        while (ptr->name) {
                if (strcmp(ptr->name, line) == 0) {
                        return ptr;
                }
                ptr++;
        }
        return NULL;
}


static char *make_message(xdebug_con *context, int error_code, char *message)
{
        xdebug_gdb_options *options = (xdebug_gdb_options*) context->options;
        char               *tmp;
        char               *ret;
        char               *type;
        int                 len;

        if ((error_code & XDEBUG_E) == XDEBUG_E) {
                type = "error";
        } else {
                type = "data";
        }
        
        switch (options->response_format) {
                case XDEBUG_RESPONSE_XML:
                        /* we ignore binary safety here */
                        tmp = xmlize(message, strlen(message), &len);
                        ret = xdebug_sprintf("<xdebug><%s><code>%d</code><message>%s</message></%s></xdebug>", type, error_code, tmp, type);
                        efree(tmp);
                        return ret;
                        break;

                case XDEBUG_RESPONSE_NORMAL:
                default:
                        return xdebug_sprintf("%d %s", error_code, message);
                        break;
        }
}

static void send_message(xdebug_con *context, int error_code, char *message)
{
        char *tmp;

        tmp = make_message(context, error_code, message);
        SENDMSG(context->socket, xdebug_sprintf("%s\n", tmp));
        xdfree(tmp);
}


/*****************************************************************************
** Helpers for looking up commands
*/

static inline xdebug_gdb_cmd* lookup_cmd_in_group(char *line, xdebug_gdb_cmd *group, int flag, int test_flag)
{
        xdebug_gdb_cmd *ptr;

        if (flag & test_flag) {
                ptr = scan_cmd(group, line);
                if (ptr) {
                        return (ptr);
                }
        }
        return NULL;
}

static xdebug_gdb_cmd* lookup_cmd(char *line, int flag)
{
        xdebug_gdb_cmd *ptr;
        
        if ((ptr = lookup_cmd_in_group(line, commands_init,       flag, XDEBUG_INIT)) != NULL)       return ptr;
        if ((ptr = lookup_cmd_in_group(line, commands_breakpoint, flag, XDEBUG_BREAKPOINT)) != NULL) return ptr;
        if ((ptr = lookup_cmd_in_group(line, commands_run,        flag, XDEBUG_RUN)) != NULL)        return ptr;
        if ((ptr = lookup_cmd_in_group(line, commands_runtime,    flag, XDEBUG_RUNTIME)) != NULL)    return ptr;
        if ((ptr = lookup_cmd_in_group(line, commands_data,       flag, XDEBUG_DATA)) != NULL)       return ptr;
#if 0
        if ((ptr = lookup_cmd_in_group(line, commands_status,     flag, XDEBUG_STATUS)) != NULL)     return ptr;
#endif
        return NULL;
}

/*****************************************************************************
** Helpers for the "help" command
*/

static inline void show_available_commands_in_group(xdebug_con *h, int fmt, int flag, int test_flag, xdebug_gdb_cmd *ptr)
{
        char *tmp;
        int   len;

        if (flag & test_flag ) {
        while (ptr->name) {
                        if (ptr->show && ptr->help) {
                                switch (fmt) {
                                        case XDEBUG_RESPONSE_XML:
                                                /* we ignore binary safety here */
                                                tmp = xmlize(ptr->help, strlen(ptr->help), &len);
                                                SENDMSG(h->socket, xdebug_sprintf("<command><name>%s</name><desc>%s</desc></command>", ptr->name, tmp));
                                                efree(tmp);
                                                break;
                                        default:
                                                SENDMSG(h->socket, xdebug_sprintf("%-12s %s\n", ptr->name, ptr->help));
                                                break;
                                }
                        }
                ptr++;
            }
        }
}

static void show_available_commands(xdebug_con *h, int flag)
{
        xdebug_gdb_options* o = (xdebug_gdb_options*) h->options;

        if (o->response_format == XDEBUG_RESPONSE_XML) {
                SENDMSG(h->socket, xdebug_sprintf("<xdebug><help>"));
        }
        show_available_commands_in_group(h, o->response_format, flag, XDEBUG_INIT,       commands_init);
        show_available_commands_in_group(h, o->response_format, flag, XDEBUG_BREAKPOINT, commands_breakpoint);
        show_available_commands_in_group(h, o->response_format, flag, XDEBUG_RUN,        commands_run);
        show_available_commands_in_group(h, o->response_format, flag, XDEBUG_RUNTIME,    commands_runtime);
        show_available_commands_in_group(h, o->response_format, flag, XDEBUG_DATA,       commands_data);
#if 0
        show_available_commands_in_group(h, o->response_format, flag, XDEBUG_STATUS,     commands_status);
#endif
        if (o->response_format == XDEBUG_RESPONSE_XML) {
                SENDMSG(h->socket, xdebug_sprintf("</help></xdebug>\n"));
        }
}

static void show_command_info(xdebug_con *h, xdebug_gdb_cmd* cmd)
{
        xdebug_gdb_options *o = (xdebug_gdb_options*) h->options;
        char               *t1, *t2;
        int                 len;

        if (cmd) {
                if (o->response_format == XDEBUG_RESPONSE_XML) {
                        /* we ignore binary safety here */
                        t1 = xmlize(cmd->description, strlen(cmd->description), &len);
                        t2 = xmlize(cmd->help, strlen(cmd->help), &len);
                        SENDMSG(h->socket, xdebug_sprintf("<xdebug><help><command><syntax>%s</syntax><desc>%s</desc></help</xdebug>\n", t1, t2));
                        efree(t1);
                        efree(t2);
                } else {
                        SENDMSG(h->socket, xdebug_sprintf("Syntax: %s\n%12s %s\n", cmd->description, " ", cmd->help));
                }
        } else {
                send_message(h, XDEBUG_E_NO_INFO, "No information available for this command.");
        }
}

/*****************************************************************************
** Data printing functions
*/
static char *return_printable_symbol(xdebug_con *context, char *name, zval *val)
{
        xdebug_gdb_options* options = (xdebug_gdb_options*) context->options;
        char *str_rep, *ret;

        switch (options->response_format) {
                case XDEBUG_RESPONSE_NORMAL:
                        if (val) {
                                str_rep = get_zval_value(val, 0);
                        } else {
                                str_rep = xdstrdup("*uninitialized*");
                        }
                        if (name) {
                                ret = xdebug_sprintf("$%s = %s\n", name, str_rep);
                        } else {
                                ret = xdebug_sprintf("%s\n", str_rep);
                        }
                        xdfree(str_rep);
                        return ret;
                        break;

                case XDEBUG_RESPONSE_XML:
                default:
                        return get_zval_value_xml(name, val);
        }
}

static void dump_used_var(void *context, xdebug_hash_element* he)
{
        char               *name = (char*) he->ptr;
        xdebug_con         *h = (xdebug_con*) context;
        xdebug_gdb_options *options = (xdebug_gdb_options*) h->options;

        if (!options->dump_superglobals) {
                if ((strcmp(name, "GLOBALS") == 0) ||
                        (strcmp(name, "_GET") == 0) ||
                        (strcmp(name, "_POST") == 0) ||
                        (strcmp(name, "_COOKIE") == 0) ||
                        (strcmp(name, "_REQUEST") == 0) ||
                        (strcmp(name, "_SERVER") == 0) ||
                        (strcmp(name, "_ENV") == 0) ||
                        (strcmp(name, "_SESSION") == 0))
                {
                        return;
                }
        }
        if (options->response_format == XDEBUG_RESPONSE_XML) {
                SENDMSG(h->socket, xdebug_sprintf("<var name='%s'/>", name));
        } else {
                SENDMSG(h->socket, xdebug_sprintf("$%s\n", name));
        }
}


static void dump_used_var_with_contents(void *context, xdebug_hash_element* he)
{
        char               *name = (char*) he->ptr;
        xdebug_con         *h = (xdebug_con*) context;
        xdebug_gdb_options *options = (xdebug_gdb_options*) h->options;
        char               *contents;
        zval               *zval_var;

        if (!options->dump_superglobals) {
                if ((strcmp(name, "GLOBALS") == 0) ||
                        (strcmp(name, "_GET") == 0) ||
                        (strcmp(name, "_POST") == 0) ||
                        (strcmp(name, "_COOKIE") == 0) ||
                        (strcmp(name, "_REQUEST") == 0) ||
                        (strcmp(name, "_SERVER") == 0) ||
                        (strcmp(name, "_ENV") == 0) ||
                        (strcmp(name, "_SESSION") == 0))
                {
                        return;
                }
        }
        zval_var = xdebug_get_php_symbol(name, strlen(name) + 1);
        contents = return_printable_symbol(context, name, zval_var);
        if (contents) {
                if (options->response_format == XDEBUG_RESPONSE_XML) {
                        SENDMSG(h->socket, contents);
                } else {
                        SENDMSG(h->socket, xdebug_sprintf("%s", contents));
                        xdfree(contents);
                }
        } else {
                if (options->response_format == XDEBUG_RESPONSE_XML) {
                        SENDMSG(h->socket, xdebug_sprintf("<var name='%s'/>", name));
                } else {
                        SENDMSG(h->socket, xdebug_sprintf("$%s = *uninitialized*\n", name));
                }
        }
}

static void print_sourceline(xdebug_con *h, char *file, int begin, int end, int offset, int response_format TSRMLS_DC)
{
        int    fd;
        fd_buf fd_buffer = { NULL, 0 };
        int    i = begin;
        char  *line = NULL;
        int    update = 0, len;
        char  *tmp;

        if (i < 0) {
                begin = 0;
                i = 0;
        }

        /* Read until the "begin" line has been read */
        if ((fd = open(file, 0)) == -1) {
                SENDMSG(h->socket, xdebug_sprintf("The file '%s' could not be opened.\n", file));
                return;
        }
        
        while (i > 0) {
                if (line) {
                        free(line);
                        line = NULL;
                }
                line = fd_read_line(fd, &fd_buffer, FD_RL_FILE);
                i--;
        }
        /* Read until the "end" line has been read */
        do {
                if (line) {
                        update = 1;
                        if (response_format == XDEBUG_RESPONSE_XML) {
                                /* we ignore binary safety here */
                                tmp = xmlize(line, strlen(line), &len);
                                SENDMSG(h->socket, xdebug_sprintf("<line file='%s' no='%d'>%s</line>", file, begin + i, tmp));
                                efree(tmp);
                        } else {
                                SENDMSG(h->socket, xdebug_sprintf("%d\t%s\n", begin + i, line));
                        }
                        free(line);
                        line = NULL;
                }
                line = fd_read_line(fd, &fd_buffer, FD_RL_FILE);
                i++;
        } while (i < end + 1 - begin);

        /* Print last line */
        if (line) {
                update = 1;
                free(line);
                line = NULL;
        }

        /* Update "last" info */
        if (update) {
                if (XG(context).list.last_file && XG(context).list.last_file != file) {
                        xdfree(XG(context).list.last_file);
                }
                if (XG(context).list.last_file != file) {
                        XG(context).list.last_file = xdstrdup(file);
                }
                XG(context).list.last_line = end + 1 + offset;
        }
        close(fd);
}

static void print_breakpoint(xdebug_con *h, function_stack_entry *i, int response_format)
{
        int   c = 0; /* Comma flag */
        int   j = 0; /* Counter */
        char *tmp_fname;
        char *tmp;
        char *tmp_value;
        int   xml = (response_format == XDEBUG_RESPONSE_XML);
        int   len;
        TSRMLS_FETCH();
/*
* Breakpoint 2, xdebug_execute (op_array=0x82caf50)
*     at /dat/dev/php/xdebug/xdebug.c:361
*/
        tmp_fname = show_fname(i->function, 0, 0 TSRMLS_CC);
        if (xml) {
                SENDMSG(h->socket, xdebug_sprintf("<breakpoint><function><name>%s</name><params>", tmp_fname));
        } else {
                SENDMSG(h->socket, xdebug_sprintf("Breakpoint, %s(", tmp_fname));
        }
        xdfree(tmp_fname);

        /* Printing vars */
        for (j = 0; j < i->varc; j++) {
                if (c) {
                        SSEND(h->socket, ", ");
                } else {
                        c = 1;
                }

                if (i->var[j].name) {
                   SENDMSG(h->socket, xdebug_sprintf("$%s = ", i->var[j].name));
                }
                tmp_value = get_zval_value(i->var[j].addr, 0);
                /* we ignore binary safety here */
                tmp = xmlize(tmp_value, strlen(tmp_value), &len);
                SSENDL(h->socket, tmp, len);
                xdfree(tmp_value);
                efree(tmp);
        }

        if (xml) {
                SENDMSG(h->socket, xdebug_sprintf("</params></function><file>%s</file><line>%d</line></breakpoint>", i->filename, i->lineno));
        } else {
                SENDMSG(h->socket, xdebug_sprintf(")\n\tat %s:%d\n", i->filename, i->lineno));
        }
}

static void print_stackframe(xdebug_con *h, int nr, function_stack_entry *i, int response_format, int flags)
{
        int c = 0; /* Comma flag */
        int j = 0; /* Counter */
        char *tmp_fname;
        char *tmp;
        char *tmp_value;
        int   len;
        TSRMLS_FETCH();
        
/*
* 0x4001af2e in xdebug_compile_file (file_handle=0xbffff960, type=2)
*     at /dat/dev/php/xdebug/xdebug.c:901
*         
*/
        tmp_fname = show_fname(i->function, 0, 0 TSRMLS_CC);
        if (response_format == XDEBUG_RESPONSE_XML) {
                if (nr) {
                        SENDMSG(h->socket, xdebug_sprintf("<stackframe><level>%d</level><function><name>%s</name><params>", nr, tmp_fname));
                } else {
                        SENDMSG(h->socket, xdebug_sprintf("<stackframe><function><name>%s</name><params>", tmp_fname));
                }
        } else {
                if (nr) {
                        SENDMSG(h->socket, xdebug_sprintf("#%-2d %s (", nr, tmp_fname));
                } else {
                        SENDMSG(h->socket, xdebug_sprintf("%s (", tmp_fname));
                }
        }
        xdfree(tmp_fname);

        /* Printing vars */
        for (j = 0; j < i->varc; j++) {
                if (c) {
                        SSEND(h->socket, ", ");
                } else {
                        c = 1;
                }

                if (i->var[j].name) {
                   SENDMSG(h->socket, xdebug_sprintf("$%s = ", i->var[j].name));
                }
                tmp_value = get_zval_value(i->var[j].addr, 0);
                /* we ignore binary safety here */
                tmp = xmlize(tmp_value, strlen(tmp_value), &len);
                SSENDL(h->socket, tmp, len);
                xdfree(tmp_value);
                efree(tmp);
        }

        XG(active_symbol_table) = i->symbol_table;
        if (response_format == XDEBUG_RESPONSE_XML) {
                SENDMSG(h->socket, xdebug_sprintf("</params></function><file>%s</file><line>%d</line>", i->filename, i->lineno));
                if (flags & XDEBUG_FRAME_FULL && i->used_vars) {
                        SSEND(h->socket, "<locals>");
                        xdebug_hash_apply(i->used_vars, (void *) h, dump_used_var_with_contents);
                        SSEND(h->socket, "</locals>");
                }
                SSEND(h->socket, "</stackframe>");
        } else {
                SENDMSG(h->socket, xdebug_sprintf(")\n    at %s:%d\n", i->filename, i->lineno));
                if (flags & XDEBUG_FRAME_FULL && i->used_vars) {
                        xdebug_hash_apply(i->used_vars, (void *) h, dump_used_var_with_contents);
                        SSEND(h->socket, "\n");
                }
        }
        XG(active_symbol_table) = NULL;
}

/*****************************************************************************
** Client command handlers
*/

char *xdebug_handle_backtrace(xdebug_con *context, xdebug_arg *args)
{
        xdebug_llist_element *le;
        int                   counter = 1;
        int                   full = XDEBUG_FRAME_NORMAL;
        xdebug_gdb_options   *options = (xdebug_gdb_options*) context->options;
        int                   xml = (options->response_format == XDEBUG_RESPONSE_XML);
        TSRMLS_FETCH();

        if (args->c == 1 && strcmp(args->args[0], "full") == 0) {
                full = XDEBUG_FRAME_FULL;
        }

        SSEND(context->socket, xml ? "<xdebug><backtrace>" : "");
        for (le = XDEBUG_LLIST_TAIL(XG(stack)); le != NULL; le = XDEBUG_LLIST_PREV(le)) {
                print_stackframe(context, counter++, XDEBUG_LLIST_VALP(le), options->response_format, full);
        }
        SSEND(context->socket, xml ? "</backtrace></xdebug>\n" : "\n");

        return NULL;
}

char *xdebug_handle_breakpoint(xdebug_con *context, xdebug_arg *args)
{
        xdebug_arg      *method = (xdebug_arg*) xdmalloc(sizeof(xdebug_arg));
        char            *tmp_name;
        xdebug_brk_info *extra_brk_info;
        xdebug_str       eval = {0, 0, NULL};
        int              i = 0;

        xdebug_arg_init(method);

        extra_brk_info = xdmalloc(sizeof(xdebug_brk_info));
        extra_brk_info->type = NULL;
        extra_brk_info->file = NULL;
        extra_brk_info->condition = NULL;
        extra_brk_info->classname = NULL;
        extra_brk_info->functionname = NULL;
        extra_brk_info->disabled = 0;
        extra_brk_info->temporary = 0;
        extra_brk_info->hit_count = 0;
        extra_brk_info->hit_value = 0;
        extra_brk_info->hit_condition = XDEBUG_HIT_DISABLED;
        extra_brk_info->function_break_type = XDEBUG_BRK_FUNC_CALL;

        if (strstr(args->args[0], "::")) { /* class::method */
                xdebug_explode("::", args->args[0], method, -1);
                extra_brk_info->classname = xdstrdup(method->args[0]);
                extra_brk_info->functionname = xdstrdup(method->args[1]);

                if (method->c != 2) {
                        xdebug_arg_dtor(method);
                        xdebug_brk_info_dtor(extra_brk_info);
                        return make_message(context, XDEBUG_E_INVALID_FORMAT, "Invalid format for class/method combination.");
                } else {
                        if (!xdebug_hash_add(context->class_breakpoints, args->args[0], strlen(args->args[0]), (void*) extra_brk_info)) {
                                xdebug_arg_dtor(method);
                                xdebug_brk_info_dtor(extra_brk_info);
                                return make_message(context, XDEBUG_E_BREAKPOINT_NOT_SET, "Breakpoint could not be set.");
                        } else {
                                send_message(context, XDEBUG_D_BREAKPOINT_SET, "Breakpoint set on class/method combination.");
                                xdebug_arg_dtor(method);
                        }
                }
        } else if (strstr(args->args[0], "->")) { /* class->method */
                xdebug_explode("->", args->args[0], method, -1);
                extra_brk_info->classname = xdstrdup(method->args[0]);
                extra_brk_info->functionname = xdstrdup(method->args[1]);

                if (method->c != 2) {
                        xdebug_arg_dtor(method);
                        xdebug_brk_info_dtor(extra_brk_info);
                        return make_message(context, XDEBUG_E_INVALID_FORMAT, "Invalid format for class/method combination.");
                } else {
                        if (!xdebug_hash_add(context->class_breakpoints, args->args[0], strlen(args->args[0]), (void*) extra_brk_info)) {
                                xdebug_arg_dtor(method);
                                xdebug_brk_info_dtor(extra_brk_info);
                                return make_message(context, XDEBUG_E_BREAKPOINT_NOT_SET, "Breakpoint could not be set.");
                        } else {
                                send_message(context, XDEBUG_D_BREAKPOINT_SET, "Breakpoint set on class/method combination.");
                                xdebug_arg_dtor(method);
                        }
                }
        } else if (strstr(args->args[0], ":")) { /* file:line */
                xdebug_explode(":", args->args[0], method, -1); /* 0 = filename, 1 = linenumer */
                if (method->c != 2) {
                        xdebug_arg_dtor(method);
                        return make_message(context, XDEBUG_E_INVALID_FORMAT, "Invalid format for file:line combination.");
                } else {
                        /* Make search key */
                        if (method->args[0][0] != '/') {
#if WIN32|WINNT
                                if (strlen(method->args[0]) > 3
                                        && method->args[0][1] != '|')
                                {
                                        tmp_name = xdebug_sprintf("/%s", method->args[0]);
                                } else {
                                        tmp_name = xdebug_sprintf("%s", method->args[0]);
                                }
#else
                                tmp_name = xdebug_sprintf("/%s", method->args[0]);
#endif
                        } else {
                                tmp_name = xdebug_sprintf("%s", method->args[0]);
                        }

                        /* Set line number in extra structure */
                        extra_brk_info->lineno = atoi(method->args[1]);
                        extra_brk_info->file = tmp_name;
                        extra_brk_info->file_len = strlen(tmp_name);

                        /* If there are more second parameters to the "break" command, then we
                         * concat them and use it as a conditional statement */
                        if (args->c > 1) {
                                xdebug_str_add(&eval, args->args[1], 0);

                                for (i = 2; i < args->c; i++) {
                                        xdebug_str_add(&eval, " ", 0);
                                        xdebug_str_add(&eval, args->args[i], 0);
                                }
                                extra_brk_info->condition = xdstrdup(eval.d);
                                xdebug_str_free(&eval);
                        }

                        /* Add breakpoint to the list */
                        xdebug_llist_insert_next(context->line_breakpoints, XDEBUG_LLIST_TAIL(context->line_breakpoints), (void*) extra_brk_info);
                        send_message(context, XDEBUG_D_BREAKPOINT_SET, "Breakpoint set on file:line combination.");
                        xdebug_arg_dtor(method);
                }
        } else { /* function */
                extra_brk_info->functionname = xdstrdup(args->args[0]);

                if (!xdebug_hash_add(context->function_breakpoints, args->args[0], strlen(args->args[0]), (void*) extra_brk_info)) {
                        xdebug_arg_dtor(method);
                        xdebug_brk_info_dtor(extra_brk_info);
                        return make_message(context, XDEBUG_E_BREAKPOINT_NOT_SET, "Breakpoint could not be set.");
                } else {
                        send_message(context, XDEBUG_D_BREAKPOINT_SET, "Breakpoint set on function.");
                        xdebug_arg_dtor(method);
                }
        }
        return NULL;
}

char *xdebug_handle_cont(xdebug_con *context, xdebug_arg *args)
{
        SSEND(context->socket, "Continuing.\n");
        return NULL;
}

char *xdebug_handle_delete(xdebug_con *context, xdebug_arg *args)
{
        xdebug_arg      *method = (xdebug_arg*) xdmalloc(sizeof(xdebug_arg));
        char            *tmp_name;
        xdebug_llist_element *le;
        xdebug_brk_info      *brk;
        TSRMLS_FETCH();

        xdebug_arg_init(method);

        if (strstr(args->args[0], "::")) { /* class::method */
                xdebug_explode("::", args->args[0], method, -1);
                if (method->c != 2) {
                        xdebug_arg_dtor(method);
                        return make_message(context, XDEBUG_E_INVALID_FORMAT, "Invalid format for class/method combination.");
                } else {
                        if (!xdebug_hash_delete(context->class_breakpoints, args->args[0], strlen(args->args[0]))) {
                                xdebug_arg_dtor(method);
                                return make_message(context, XDEBUG_E_BREAKPOINT_NOT_REMOVED, "Breakpoint could not be removed.");
                        } else {
                                send_message(context, XDEBUG_D_BREAKPOINT_REMOVED, "Breakpoint removed.");
                                xdebug_arg_dtor(method);
                        }
                }
        } else if (strstr(args->args[0], "->")) { /* class->method */
                xdebug_explode("->", args->args[0], method, -1);
                if (method->c != 2) {
                        xdebug_arg_dtor(method);
                        return make_message(context, XDEBUG_E_INVALID_FORMAT, "Invalid format for class/method combination.");
                } else {
                        if (!xdebug_hash_delete(context->class_breakpoints, args->args[0], strlen(args->args[0]))) {
                                xdebug_arg_dtor(method);
                                return make_message(context, XDEBUG_E_BREAKPOINT_NOT_REMOVED, "Breakpoint could not be removed.");
                        } else {
                                send_message(context, XDEBUG_D_BREAKPOINT_REMOVED, "Breakpoint removed.");
                                xdebug_arg_dtor(method);
                        }
                }
        } else if (strstr(args->args[0], ":")) { /* file:line */
                xdebug_explode(":", args->args[0], method, -1); /* 0 = filename, 1 = linenumer */
                if (method->c != 2) {
                        xdebug_arg_dtor(method);
                        return make_message(context, XDEBUG_E_INVALID_FORMAT, "Invalid format for file:line combination.");
                } else {
                        /* Make search key */
                        if (method->args[0][0] != '/') {
#if WIN32|WINNT
                                if (strlen(method->args[0]) > 3
                                        && method->args[0][1] != '|')
                                {
                                        tmp_name = xdebug_sprintf("/%s", method->args[0]);
                                } else {
                                        tmp_name = xdebug_sprintf("%s", method->args[0]);
                                }
#else
                                tmp_name = xdebug_sprintf("/%s", method->args[0]);
#endif
                        } else {
                                tmp_name = xdebug_sprintf("%s", method->args[0]);
                        }

                        /* Add breakpoint to the list */
                        for (le = XDEBUG_LLIST_HEAD(XG(context).line_breakpoints); le != NULL; le = XDEBUG_LLIST_NEXT(le)) {
                                brk = XDEBUG_LLIST_VALP(le);

                                if (atoi(method->args[1]) == brk->lineno && strcmp(tmp_name, brk->file) == 0) {
                                        xdebug_llist_remove(context->line_breakpoints, le, NULL);
                                        send_message(context, XDEBUG_D_BREAKPOINT_REMOVED, "Breakpoint removed.");
                                        xdebug_arg_dtor(method);
                                        xdfree(tmp_name);
                                        return NULL;
                                }
                        }
                        xdfree(tmp_name);
                        xdebug_arg_dtor(method);
                }
        } else { /* function */
                if (!xdebug_hash_delete(context->function_breakpoints, args->args[0], strlen(args->args[0]))) {
                        xdebug_arg_dtor(method);
                        return make_message(context, XDEBUG_E_BREAKPOINT_NOT_REMOVED, "Breakpoint could not be removed.");
                } else {
                        send_message(context, XDEBUG_D_BREAKPOINT_REMOVED, "Breakpoint removed.");
                        xdebug_arg_dtor(method);
                }
        }
        return NULL;
}

char *xdebug_handle_eval(xdebug_con *context, xdebug_arg *args)
{
        int        i;
        xdebug_str buffer = {0, 0, NULL};
        zval       retval;
        char      *ret_value;
        int        old_error_reporting;
        TSRMLS_FETCH();

        /* Remember error reporting level */
        old_error_reporting = EG(error_reporting);
        EG(error_reporting) = 0;

        /* Concat all arguments back together */
        xdebug_str_add(&buffer, args->args[0], 0);

        for (i = 1; i < args->c; i++) {
                xdebug_str_add(&buffer, " ", 0);
                xdebug_str_add(&buffer, args->args[i], 0);
        }

        XG(breakpoints_allowed) = 0;
        if (zend_eval_string(buffer.d, &retval, "xdebug eval" TSRMLS_CC) == FAILURE) {
                xdebug_str_free(&buffer);
                EG(error_reporting) = old_error_reporting;
                XG(breakpoints_allowed) = 1;
                return make_message(context, XDEBUG_E_EVAL, "Error evaluating code");
        } else {
                xdebug_str_free(&buffer);
                EG(error_reporting) = old_error_reporting;
                ret_value = return_printable_symbol(context, NULL, &retval);
                SENDMSG(context->socket, xdebug_sprintf("%s\n", ret_value));
                zval_dtor(&retval);
                xdfree(ret_value);
                XG(breakpoints_allowed) = 1;
                return NULL;
        }
}

char *xdebug_handle_finish(xdebug_con *context, xdebug_arg *args)
{
        xdebug_llist_element *le;
        function_stack_entry *fse;
        TSRMLS_FETCH();

        XG(context).do_next   = 0;
        XG(context).do_step   = 0;
        XG(context).do_finish = 1;

        if (XG(stack)) {
                le = XDEBUG_LLIST_TAIL(XG(stack));
                fse = XDEBUG_LLIST_VALP(le);
                XG(context).next_level = fse->level - 1;
        } else {
                XG(context).next_level = -1;
        }

        return NULL;
}

char *xdebug_handle_kill(xdebug_con *context, xdebug_arg *args)
{
        zend_bailout();
        return NULL;
}

char *xdebug_handle_list(xdebug_con *context, xdebug_arg *args)
{
        char               *tmp_file  = NULL;
        int                 tmp_begin = 0;
        int                 tmp_end   = 0;
        xdebug_arg         *parts = (xdebug_arg*) xdmalloc(sizeof(xdebug_arg));
        xdebug_gdb_options *options = (xdebug_gdb_options*) context->options;
        int                 xml = (options->response_format == XDEBUG_RESPONSE_XML);
        TSRMLS_FETCH();

        xdebug_arg_init(parts);

        switch (args->c) {
                case 0: 
                        if (XG(context).list.last_file) {
                                tmp_file  = XG(context).list.last_file;
                                tmp_begin = XG(context).list.last_line;
                        } else {
                                tmp_file  = XG(context).program_name;
                                tmp_begin = 1;
                        }
                        tmp_end = tmp_begin + 9;
                        break;
                case 1: 
                case 2: 
                        xdebug_explode(":", args->args[0], parts, -1);
                        tmp_begin = parts->c == 1 ? atoi(parts->args[0]) : atoi(parts->args[1]);
                        if (tmp_begin <= 0) {
                                tmp_begin = 1;
                        }

                        if (parts->c == 1) { /* Only line is given */
                                if (XG(context).list.last_file) {
                                        tmp_file = XG(context).list.last_file;
                                } else {
                                        tmp_file = XG(context).program_name;
                                }
                        } else if (parts->c == 2) { /* File:line is given */
                                tmp_file = parts->args[0];
                        }

                        if (args->c == 1) {
                                tmp_end = tmp_begin + 9;
                        } else {
                                tmp_end = atoi(args->args[1]);
                        }
                        break;
                default:
                        return make_message(context, XDEBUG_E_TOO_MANY_ARGUMENTS, "Too many arguments.");
                        break;
        }
        SSEND(context->socket, xml ? "<xdebug><list>" : "");
        print_sourceline(context, tmp_file, tmp_begin, tmp_end, 0, options->response_format TSRMLS_CC);
        SSEND(context->socket, xml ? "</list></xdebug>\n" : "\n");

        xdebug_arg_dtor(parts); 
        return NULL;
}

char *xdebug_handle_next(xdebug_con *context, xdebug_arg *args)
{
        xdebug_llist_element *le;
        function_stack_entry *fse;
        TSRMLS_FETCH();

        XG(context).do_next   = 1;
        XG(context).do_step   = 0;
        XG(context).do_finish = 0;

        if (XG(stack)) {
                le = XDEBUG_LLIST_TAIL(XG(stack));
                fse = XDEBUG_LLIST_VALP(le);
                XG(context).next_level = fse->level;
        } else {
                XG(context).next_level = 0;
        }

        return NULL;
}

char *xdebug_handle_option(xdebug_con *context, xdebug_arg *args)
{
        xdebug_gdb_options *options = (xdebug_gdb_options*)context->options;

        if (strcmp(args->args[0], "response_format") == 0) {
                options->response_format = atoi(args->args[1]);
        } else if (strcmp(args->args[0], "dump_superglobals") == 0) {
                options->dump_superglobals = atoi(args->args[1]);
        } else {
                return make_message(context, XDEBUG_E_UNKNOWN_OPTION, "Unknown option.");
        }

        return NULL;
}

char *xdebug_handle_print(xdebug_con *context, xdebug_arg *args)
{
        xdebug_gdb_options  *options = (xdebug_gdb_options*)context->options;
        int                  xml = (options->response_format == XDEBUG_RESPONSE_XML);
        char                *var_data;
        zval                *zvar;
        TSRMLS_FETCH();

        XG(active_symbol_table) = EG(active_symbol_table);
        zvar = xdebug_get_php_symbol(args->args[0], strlen(args->args[0]) + 1);
        if (zvar) {
                var_data = return_printable_symbol(context, args->args[0], zvar);
                XG(active_symbol_table) = NULL;

                if (var_data) {
                        SSEND(context->socket, xml ? "<xdebug><print>" : "");
                        SENDMSG(context->socket, var_data);
                        SSEND(context->socket, xml ? "</print></xdebug>\n" : "\n");
                        return NULL;
                }
        }

        return make_message(context, XDEBUG_E_SYMBOL_NOT_FOUND, "This symbol does not exist or is not yet initialized.");
}

char *xdebug_handle_pwd(xdebug_con *context, xdebug_arg *args)
{
        char                buffer[256 + 1];
        xdebug_gdb_options *options = (xdebug_gdb_options*) context->options;

        if (getcwd(buffer, 256)) {
                if (options->response_format == XDEBUG_RESPONSE_XML) {
                        SENDMSG(context->socket, xdebug_sprintf("<xdebug><pwd><directory>%s</directory></pwd></xdebug>\n", buffer));
                } else {
                        SENDMSG(context->socket, xdebug_sprintf("Working directory %s.\n", buffer));
                }
        }
        return NULL;
}

char *xdebug_handle_quit(xdebug_con *context, xdebug_arg *args)
{
        return NULL;
}

char *xdebug_handle_run(xdebug_con *context, xdebug_arg *args)
{
        xdebug_gdb_options          *options = (xdebug_gdb_options*) context->options;

        if (options->response_format == XDEBUG_RESPONSE_XML) {
                SENDMSG(context->socket, xdebug_sprintf("<xdebug><run><program>%s</program></run></xdebug>\n", context->program_name));
        } else {
                SENDMSG(context->socket, xdebug_sprintf("Starting program: %s\n", context->program_name));
        }
        return NULL;
}


static void dump_class_breakpoint(void *context, xdebug_hash_element* he)
{
        xdebug_con         *h = (xdebug_con*) context;
        xdebug_gdb_options *options = (xdebug_gdb_options*) h->options;
        xdebug_brk_info    *brk_info = (xdebug_brk_info*) he->ptr;

        if (options->response_format == XDEBUG_RESPONSE_XML) {
                SENDMSG(
                        h->socket,
                        xdebug_sprintf("<breakpoint type='class'><class>%s</class><method>%s</method></breakpoint>",
                        brk_info->classname, brk_info->functionname));
        } else {
                SENDMSG(
                        h->socket,
                        xdebug_sprintf("Method breakpoint: %s::%s\n",
                        brk_info->classname, brk_info->functionname));
        }
}

static void dump_function_breakpoint(void *context, xdebug_hash_element* he)
{
        xdebug_con         *h = (xdebug_con*) context;
        xdebug_gdb_options *options = (xdebug_gdb_options*) h->options;
        xdebug_brk_info    *brk_info = (xdebug_brk_info*) he->ptr;

        if (options->response_format == XDEBUG_RESPONSE_XML) {
                SENDMSG(h->socket, xdebug_sprintf("<breakpoint type='function'><function>%s</function></breakpoint>", brk_info->functionname));
        } else {
                SENDMSG(h->socket, xdebug_sprintf("Function breakpoint: %s\n", brk_info->functionname));
        }
}

static void dump_line_breakpoint(xdebug_con *h, xdebug_gdb_options *options, xdebug_brk_info* brk_info)
{
        char *condition = NULL;
        int   len;

        if (options->response_format == XDEBUG_RESPONSE_XML) {
                if (condition) {
                        /* we ignore binary safety here */
                        condition = xmlize(brk_info->condition, strlen(brk_info->condition), &len);
                        SENDMSG(h->socket,
                                xdebug_sprintf("<breakpoint type='line' condition='%s'><file>%s</file><line>%d</line></breakpoint>",
                                condition,
                                brk_info->file,
                                brk_info->lineno));
                        xdfree(condition);
                } else {
                        SENDMSG(h->socket, xdebug_sprintf("<breakpoint type='line'><file>%s</file><line>%d</line></breakpoint>",
                                brk_info->file,
                                brk_info->lineno));
                }
        } else {
                if (condition) {
                        SENDMSG(h->socket,
                                xdebug_sprintf("Location breakpoint: %s:%d (condition= %s)\n",
                                brk_info->file,
                                brk_info->lineno,
                                brk_info->condition));
                } else {
                        SENDMSG(h->socket,
                                xdebug_sprintf("Location breakpoint: %s:%d\n",
                                brk_info->file,
                                brk_info->lineno));
                }
        }
}


char *xdebug_handle_show_breakpoints(xdebug_con *context, xdebug_arg *args)
{
        xdebug_gdb_options   *options = (xdebug_gdb_options*) context->options;
        xdebug_llist_element *le;

        if (options->response_format == XDEBUG_RESPONSE_XML) {
                SSEND(context->socket, "<breakpoints>");
        }

        xdebug_hash_apply(context->function_breakpoints, (void *) context, dump_function_breakpoint);
        xdebug_hash_apply(context->class_breakpoints, (void *) context, dump_class_breakpoint);

        for (le = XDEBUG_LLIST_TAIL(context->line_breakpoints); le != NULL; le = XDEBUG_LLIST_PREV(le)) {
                dump_line_breakpoint(context, options, XDEBUG_LLIST_VALP(le));
        }

        if (options->response_format == XDEBUG_RESPONSE_XML) {
                SSEND(context->socket, "</breakpoints>\n");
        }
        return NULL;
}


static char* show_local_vars(xdebug_con *context, xdebug_arg *args, void (*func)(void *, xdebug_hash_element*))
{
        function_stack_entry *i;
        xdebug_hash          *ht;
        xdebug_gdb_options   *options = (xdebug_gdb_options*) context->options;
        TSRMLS_FETCH();

        
        if (XDEBUG_LLIST_TAIL(XG(stack))) {
                i = XDEBUG_LLIST_VALP(XDEBUG_LLIST_TAIL(XG(stack)));
                ht = i->used_vars;

                /* Only show vars when they are scanned */
                if (ht) {
                        if (options->response_format == XDEBUG_RESPONSE_XML) {
                                SSEND(context->socket, "<xdebug><show>");
                        }

                        xdebug_hash_apply(ht, (void *) context, func);

                        if (options->response_format == XDEBUG_RESPONSE_XML) {
                                SSEND(context->socket, "</show></xdebug>\n");
                        }
                } else {
                        return make_message(context, XDEBUG_E_NOT_USER_DEFINED, "You can not show variables in functions not defined in your script.");
                }
        }
        
        return NULL;
}

char *xdebug_handle_show(xdebug_con *context, xdebug_arg *args)
{
        return show_local_vars(context, args, dump_used_var);
}


char *xdebug_handle_show_local(xdebug_con *context, xdebug_arg *args)
{
        char *tmp;
        TSRMLS_FETCH();
        
        XG(active_symbol_table) = EG(active_symbol_table);
        tmp = show_local_vars(context, args, dump_used_var_with_contents);
        XG(active_symbol_table) = NULL;
        return tmp;
}


char *xdebug_handle_step(xdebug_con *context, xdebug_arg *args)
{
        TSRMLS_FETCH();
        XG(context).do_step   = 1;
        XG(context).do_next   = 0;
        XG(context).do_finish = 0;
        
        return NULL;
}


/*****************************************************************************
** Parsing functions
*/

int xdebug_gdb_parse_option(xdebug_con *context, char* line, int flags, char *end_cmd, char **error)
{
        char *ptr;
        xdebug_gdb_cmd *cmd;
        int retval;
        char *ret_err = NULL;
        int i;
        
        xdebug_arg *args = (xdebug_arg*) xdmalloc(sizeof(xdebug_arg));
        xdebug_arg *endcmds = (xdebug_arg*) xdmalloc(sizeof(xdebug_arg));
        xdebug_arg_init(args);
        xdebug_arg_init(endcmds);

        xdebug_explode(",", end_cmd, endcmds, -1); 

        *error = NULL;

        /* Try to find command */
        ptr = strchr(line, ' ');
        if (!ptr) { /* No separator found */
                /* Check for the special case "help" */
                if (strcmp(line, "help") == 0) {
                        show_available_commands(context, flags);
                        retval = 0;
                        goto cleanup;
                }
                if (!(cmd = lookup_cmd(line, flags))) {
                        *error = make_message(context, XDEBUG_E_UNDEFINED_COMMAND, "Undefined command, try \"help\".");
                        retval = -1;
                        goto cleanup;
                }
        } else {
                char *tmp = (char*) xdmalloc(ptr - line + 1);
                memcpy(tmp, line, ptr - line);
                tmp[ptr - line] = '\0';

                /* Check for the special case "help [command]" */
                if (strcmp(tmp, "help") == 0) {
                        xdebug_explode(" ", ptr + 1, args, -1); 
                        if (args->c > 0) {
                                show_command_info(context, lookup_cmd(args->args[0], XDEBUG_ALL));
                                retval = 0;
                        } else {
                                *error = make_message(context, XDEBUG_E_UNDEFINED_COMMAND, "Undefined command, try \"help\".");
                                retval = -1;
                        }
                        xdfree(tmp);
                        goto cleanup;
                }

                /* Scan for valid commands */
                if ((cmd = lookup_cmd(tmp, flags))) {
                        xdfree(tmp);
                        xdebug_explode(" ", ptr + 1, args, -1); 
                } else {
                        *error = make_message(context, XDEBUG_E_UNDEFINED_COMMAND, "Undefined command, try \"help\".");
                        xdfree(tmp);
                        retval = -1;
                        goto cleanup;
                }
        }

        retval = 0;

        /* Default in continue mode */
        if (args->c >= cmd->args) {
                ret_err = cmd->handler(context, args);
                if (ret_err) {
                        *error = xdstrdup(ret_err);
                        xdfree(ret_err);
                        retval = -1;
                        goto cleanup;
                }
        } else {
                *error = xdstrdup(cmd->description);
                /* Oopsie, error */
                retval = -1;
                goto cleanup;
        }
        /* If the end command is reached, or the command is quit, set the return
         * value to 1 (continue) */
        for (i = 0; i < endcmds->c; i++) {
                if (strcmp(cmd->name, endcmds->args[i]) == 0) {
                        retval = 1;
                        goto cleanup;
                }
        }
cleanup:
        xdebug_arg_dtor(args);
        xdebug_arg_dtor(endcmds);
        return retval;
}

static void xdebug_gdb_option_result(xdebug_con *context, int ret, char *error)
{
        if (error || ret == -1) {
                SSEND(context->socket, "-ERROR");
                if (error) {
                        SSEND(context->socket, ": ");
                        SSEND(context->socket, error);
                }
                SSEND(context->socket, "\n");
        } else {
                SSEND(context->socket, "+OK\n");
        }
}

/*****************************************************************************
** Handlers for debug functions
*/

char *xdebug_gdb_get_revision(void)
{
        return "$Revision: 1.78 $";
}

int xdebug_gdb_init(xdebug_con *context, int mode)
{
        char *option;
        int   ret;
        char *error = NULL;
        xdebug_gdb_options *options;
        TSRMLS_FETCH();

        SENDMSG(context->socket, xdebug_sprintf("This is Xdebug version %s.\n", XDEBUG_VERSION));
        SSEND(context->socket, "Copyright 2002, 2003, 2004 by Derick Rethans,\n");
        context->buffer = xdmalloc(sizeof(fd_buf));
        context->buffer->buffer = NULL;
        context->buffer->buffer_size = 0;

        context->options = xdmalloc(sizeof(xdebug_gdb_options));
        options = (xdebug_gdb_options*) context->options;
        options->response_format = XDEBUG_RESPONSE_NORMAL;
        options->dump_superglobals = 1;

        /* Initialize auto globals in Zend Engine 2 */
#ifdef ZEND_ENGINE_2
        zend_is_auto_global("_ENV",     sizeof("_ENV")-1     TSRMLS_CC);
        zend_is_auto_global("_GET",     sizeof("_GET")-1     TSRMLS_CC);
        zend_is_auto_global("_POST",    sizeof("_POST")-1    TSRMLS_CC);
        zend_is_auto_global("_COOKIE",  sizeof("_COOKIE")-1  TSRMLS_CC);
        zend_is_auto_global("_REQUEST", sizeof("_REQUEST")-1 TSRMLS_CC);
        zend_is_auto_global("_FILES",   sizeof("_FILES")-1   TSRMLS_CC);
        zend_is_auto_global("_SERVER",  sizeof("_SERVER")-1  TSRMLS_CC);
#endif

        context->function_breakpoints = xdebug_hash_alloc(64, (xdebug_hash_dtor) xdebug_hash_brk_dtor);
        context->class_breakpoints = xdebug_hash_alloc(64, (xdebug_hash_dtor) xdebug_hash_brk_dtor);
        context->line_breakpoints = xdebug_llist_alloc((xdebug_llist_dtor) xdebug_llist_brk_dtor);
        do {
                SENDMSG(context->socket, xdebug_sprintf("?init %s\n", context->program_name));
                option = fd_read_line(context->socket, context->buffer, FD_RL_SOCKET);
                if (!option) {
                        return 0;
                }
                ret = xdebug_gdb_parse_option(context, option, XDEBUG_INIT | XDEBUG_DATA | XDEBUG_BREAKPOINT | XDEBUG_RUN | XDEBUG_STATUS, "run", (char**) &error);
                xdebug_gdb_option_result(context, ret, error);
                free(option);
        } while (1 != ret);

        return 1;
}

int xdebug_gdb_deinit(xdebug_con *context)
{
        xdfree(context->options);
        xdebug_hash_destroy(context->function_breakpoints);
        xdebug_hash_destroy(context->class_breakpoints);
        xdebug_llist_destroy(context->line_breakpoints, NULL);
        xdfree(context->buffer);

        return 1;
}

int xdebug_gdb_error(xdebug_con *context, int type, char *exception_type, char *message, const char *file, const uint lineno, xdebug_llist *stack)
{
        char               *errortype;
        int                 ret;
        char               *option;
        char               *error = NULL;
        int                 runtime_allowed;
        xdebug_gdb_options *options = (xdebug_gdb_options*) context->options;

        if (exception_type) {
                errortype = exception_type;
        } else {
                errortype = error_type(type);
        }

        runtime_allowed = (
                (type != E_ERROR) && 
                (type != E_CORE_ERROR) &&
                (type != E_COMPILE_ERROR) &&
                (type != E_USER_ERROR)
        ) ? XDEBUG_BREAKPOINT | XDEBUG_RUNTIME : 0;

        if (options->response_format == XDEBUG_RESPONSE_XML) {
                SENDMSG(context->socket, xdebug_sprintf("<xdebug><signal><code>%d</code><type>%s</type><message>%s</message><file>%s</file><line>%lu</line><stack>", type, errortype, message, file, lineno));
                print_stackframe(context, 0, XDEBUG_LLIST_VALP(XDEBUG_LLIST_TAIL(stack)), options->response_format, XDEBUG_FRAME_NORMAL);
                SENDMSG(context->socket, xdebug_sprintf("</stack></signal></xdebug>\n"));
        } else {
                SENDMSG(context->socket, xdebug_sprintf("\nProgram received signal %s: %s.\n", errortype, message));
                print_stackframe(context, 0, XDEBUG_LLIST_VALP(XDEBUG_LLIST_TAIL(stack)), options->response_format, XDEBUG_FRAME_NORMAL);
        }

        if (!exception_type) {
                xdfree(errortype);
        }

        do {
                SSEND(context->socket, "?cmd\n");
                option = fd_read_line(context->socket, context->buffer, FD_RL_SOCKET);
                if (!option) {
                        return 0;
                }
                ret = xdebug_gdb_parse_option(context, option, XDEBUG_DATA | XDEBUG_RUN | runtime_allowed | XDEBUG_STATUS, "cont,continue", (char**) &error);
                xdebug_gdb_option_result(context, ret, error);
                free(option);
        } while (1 != ret);

        return 1;
}

int xdebug_gdb_breakpoint(xdebug_con *context, xdebug_llist *stack, char *file, long lineno, int type)
{
        function_stack_entry *i;
        int    ret;
        char  *option;
        char  *error = NULL;
        xdebug_gdb_options *options = (xdebug_gdb_options*) context->options;
        int                 xml = (options->response_format == XDEBUG_RESPONSE_XML);
        TSRMLS_FETCH();

        i = XDEBUG_LLIST_VALP(XDEBUG_LLIST_TAIL(stack));

        SSEND(context->socket, xml ? "<xdebug><break>" : "");
        if (type == XDEBUG_BREAK) {
                print_breakpoint(context, i, options->response_format);
        }
        print_sourceline(context, file, lineno, lineno, -1, options->response_format TSRMLS_CC);
        SSEND(context->socket, xml ? "</break></xdebug>\n" : "\n");

        do {
                SSEND(context->socket, "?cmd\n");
                option = fd_read_line(context->socket, context->buffer, FD_RL_SOCKET);
                if (!option) {
                        return 0;
                }
                ret = xdebug_gdb_parse_option(context, option, XDEBUG_BREAKPOINT | XDEBUG_DATA | XDEBUG_RUN | XDEBUG_RUNTIME | XDEBUG_STATUS, "cont,continue,step,next,finish", (char**) &error);
                xdebug_gdb_option_result(context, ret, error);
                free(option);
        } while (1 != ret);

        return 1;
}

/* [<][>][^][v][top][bottom][index][help] */