Logo Search packages:      
Sourcecode: ddd version File versions  Download package

session.C

// $Id$ -*- C++ -*-
// DDD session management

// Copyright (C) 1997 Technische Universitaet Braunschweig, Germany.
// Copyright (C) 2001 Universitaet des Saarlandes, Germany.
// Written by Andreas Zeller <zeller@gnu.org>.
// 
// This file is part of DDD.
// 
// DDD is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
// 
// DDD is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public
// License along with DDD -- see the file COPYING.
// If not, write to the Free Software Foundation, Inc.,
// 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
// 
// DDD is the data display debugger.
// For details, see the DDD World-Wide-Web page, 
// `http://www.gnu.org/software/ddd/',
// or send a mail to the DDD developers <ddd@gnu.org>.

char session_rcsid[] = 
    "$Id$";

#include "session.h"

#include "config.h"

#if XtSpecificationRelease >= 6
#include <X11/SM/SM.h>
#endif

#include <sys/types.h>
#include <stdlib.h>           // putenv(), getenv(), getuid()
#include <stdio.h>
#include <signal.h>           // kill()
#include <string.h>           // strerror()
#include <errno.h>
#include <fstream>
#include <pwd.h>

#include "AppData.h"
#include "DataDisp.h"
#include "Delay.h"
#include "DestroyCB.h"
#include "ExitCB.h"
#include "GDBAgent.h"
#include "HelpCB.h"
#include "MakeMenu.h"
#include "SmartC.h"
#include "SourceView.h"
#include "charsets.h"
#include "Command.h"
#include "basename.h"
#include "comm-manag.h"
#include "disp-read.h"
#include "ddd.h"
#include "cook.h"
#include "editing.h"
#include "exit.h"
#include "file.h"
#include "filetype.h"
#include "glob.h"
#include "history.h"
#include "home.h"
#include "hostname.h"
#include "mydialogs.h"
#include "options.h"
#include "post.h"
#include "question.h"
#include "settings.h"
#include "status.h"
#include "string-fun.h"
#include "tabs.h"
#include "verify.h"
#include "version.h"
#include "windows.h"
#include "wm.h"

#include <Xm/Xm.h>
#include <Xm/List.h>
#include <Xm/MessageB.h>
#include <Xm/SelectioB.h>
#include <Xm/PushB.h>
#include <Xm/ToggleB.h>
#include <Xm/Text.h>
#include <Xm/TextF.h>

extern "C" {
#include <sys/types.h>
#include <unistd.h>
#include <ctype.h>

#if HAVE_SYS_STAT_H
#include <sys/stat.h>         // mkdir()
#endif
}

#ifndef S_IRWXU
#define S_IRWXU 0700
#endif

#ifndef S_IRWXG
#define S_IRWXG 070
#endif

#ifndef S_IRWXO
#define S_IRWXO 07
#endif


// ---------------------------------------------------------------------------
// How to present a default session to the user
// ---------------------------------------------------------------------------

const string NO_SESSION = "[none]";


// ---------------------------------------------------------------------------
// Session files
// ---------------------------------------------------------------------------

string session_state_dir()
{
    const char *ddd_state = getenv(DDD_NAME "_STATE");
    if (ddd_state != 0)
      return ddd_state;
    else
      return string(gethome()) + "/." ddd_NAME;
}

static string session_base_dir()
{
    const char *ddd_sessions = getenv(DDD_NAME "_SESSIONS");
    if (ddd_sessions != 0)
      return ddd_sessions;
    else
      return session_state_dir() + "/sessions";
}

static string session_dir(const string& session)
{
    if (session == DEFAULT_SESSION)
      return session_state_dir();
    else
      return session_base_dir() + "/" + session;
}

string session_file(const string& session, const char *base)
{
    return session_dir(session) + "/" + base;
}


// ---------------------------------------------------------------------------
// Session file creation
// ---------------------------------------------------------------------------

00167 class StreamAction {
private:
    std::ostream& stream;
    string action;
    bool ok;

public:
    StreamAction(std::ostream& os, const string& c)
      : stream(os), action(c), ok(true)
    {
      stream << action << "...\n";
    }
    ~StreamAction()
    {
      if (ok)
          stream << action << "...done.\n";
    }
    void failed(const string& msg)
    {
      stream << action << " failed: " << msg << "\n";
      ok = false;
    }
};

static int makedir(const string& name, std::ostream& msg, bool user_only = false)
{
    StreamAction action(msg, "Creating " + quote(name + "/"));

    mode_t mask = umask(0);
    umask(mask);
    mode_t mode;
    if (user_only)
      mode = S_IRWXU & ~mask;
    else
      mode = (S_IRWXU | S_IRWXG | S_IRWXO) & ~mask;
    int ret = mkdir(name.chars(), mode);

    if (ret != 0)
      action.failed(strerror(errno));

    return ret;
}

static void copy(const string& from_name, const string& to_name, std::ostream& msg)
{
    FILE *from = fopen(from_name.chars(), "r");
    if (from == 0)
      return;                 // Don't care

    StreamAction action(msg, "Copying " + quote(from_name) 
                  + " to " + quote(to_name));

    FILE *to = fopen(to_name.chars(), "w");
    if (to == 0)
    {
      action.failed(strerror(errno));
      return;
    }

    int c;
    while ((c = getc(from)) != EOF)
      putc(c, to);

    fclose(from);
    if (fclose(to) == EOF)
    {
      action.failed(strerror(errno));
      unlink(to_name.chars());
    }
}

// True if SESSION is a saved session
bool is_saved_session(const string& session)
{
    return session == DEFAULT_SESSION || is_directory(session_dir(session));
}

// True if SESSION is temporary
bool is_temporary_session(const string& session)
{
    return is_saved_session(session) && 
      is_regular_file(session_tmp_flag(session));
}

// True if SESSION is temporary
void set_temporary_session(const string& session, bool temporary)
{
    if (temporary)
    {
        const string s1 = session_tmp_flag(session); 
      std::ofstream os(s1.chars());
      os << "This session will be deleted unless saved explicitly.\n";
    }
    else
    {
      const string s1 = session_tmp_flag(session.chars());
      unlink(s1.chars());
    }
}

// Create DDD state directory
static void create_session_state_dir(std::ostream& msg)
{
    // Create or find state directory
    if (!is_directory(session_state_dir()) && 
      makedir(session_state_dir(), msg, true) == 0)
    {
      // Check for DDD 2.1 `~/.dddinit' and `~/.ddd_history' files; 
      // copy them to new location if needed
      copy(string(gethome()) + "/.dddinit",
           session_state_file(DEFAULT_SESSION), msg);
      copy(string(gethome()) + "/.ddd_history",
           session_history_file(DEFAULT_SESSION), msg);
    }

    // Create session base directory
    if (!is_directory(session_base_dir()))
      makedir(session_base_dir(), msg);

    // Create themes directory
    if (!is_directory(session_themes_dir()))
      makedir(session_themes_dir(), msg);
}

// Create session directory
void create_session_dir(const string& session, std::ostream& msg)
{
    // Create state directory
    create_session_state_dir(msg);

    // Create session directory
    if (session != DEFAULT_SESSION 
      && is_directory(session_base_dir())
      && !is_directory(session_dir(session))
      && makedir(session_dir(session), msg) == 0)
    {
      // Check for default session state and history files;
      // copy them to new location
      copy(session_state_file(DEFAULT_SESSION), 
           session_state_file(session), msg);
      copy(session_history_file(DEFAULT_SESSION), 
           session_history_file(session), msg);
    }
}

// Same, but issue messages in status line
void create_session_dir(const string& session)
{
    std::ostringstream messages;
    create_session_dir(session, messages);
    string msg(messages);
    while (!msg.empty())
    {
      string line = msg.before('\n');
      set_status(line);
      msg = msg.after('\n');
    }
}


// ---------------------------------------------------------------------------
// Session locks
// ---------------------------------------------------------------------------

bool lock_session_dir(Display *display,
                  const string& session, 
                  LockInfo& info)
{
    info.pid = 0;
    string lock_file = session_lock_file(session);

    {
      std::ifstream is(lock_file.chars());
      if (!is.bad())
      {
          string version;
          is >> version;

          if (version.contains(DDD_NAME, 0))
          {
            // Lock already exists -- use contents as diagnostics
            is >> info.hostname
               >> info.pid 
               >> info.display 
               >> info.uid
               >> info.username;

            if (info.username.empty())
            {
                // DDD 3.0.90 and earlier do not save the user name
                info.username = itostring(info.uid);
            }

            if (info.hostname != fullhostname())
                return false; // Process running on remote host

            if (info.pid > 0 && kill(info.pid, 0) == 0)
                return false; // Process running on host
          }
      }
    }

    // Lock session dir, possibly overriding old lock
    {
      string username;
      struct passwd *pw = getpwuid(getuid());
      if (pw != 0)
          username = pw->pw_name;
      else
          username = itostring(getuid());

      std::ofstream os(lock_file.chars());
      os << DDD_NAME "-" DDD_VERSION
         << " " << fullhostname()
         << " " << getpid()
         << " " << XDisplayString(display)
         << " " << getuid()
         << " " << username
         << "\n";
    }

    return true;
}

bool unlock_session_dir(const string& session)
{
    string lock_file = session_lock_file(session);

    std::ifstream is(lock_file.chars());
    if (!is.bad())
    {
      // There is a lock -- check whether it's ours
      LockInfo info;
      string version;
      is >> version 
         >> info.hostname
         >> info.pid;

      if (info.hostname == fullhostname() && info.pid == getpid())
      {
          // It's ours -- remove it
          is.close();
          unlink(lock_file.chars());
          return true;
      }
      else
      {
          return false; // Mismatched lock
      }
    }

    return false;       // No lock
}


// ---------------------------------------------------------------------------
// Session selection
// ---------------------------------------------------------------------------

static void get_sessions(StringArray& arr)
{
    string mask = session_state_file("*");
    char **files = glob_filename(mask.chars());
    if (files == (char **)0)
    {
      std::cerr << mask << ": glob failed\n";
    }
    else if (files == (char **)-1)
    {
      if (errno != 0)
          post_error(string(mask) + ": " + strerror(errno), 
                   "no_sessions_error");
    }
    else
    {
      int count;
      for (count = 0; files[count] != 0; count++)
          ;
      smart_sort(files, count);

      for (int i = 0; i < count; i++)
      {
          string file = files[i];
          free(files[i]);
          file = file.before('/', -1);
          file = file.after('/', -1);
          arr += file;
      }
      free((char *)files);
    }
}

// Update state of `delete' button
static void update_delete(Widget dialog)
{
    Widget sessions = XmSelectionBoxGetChild(dialog, XmDIALOG_LIST);

    // Check for `Delete' button
    XmStringTable selected_items;
    int selected_items_count = 0;
    XtVaGetValues(sessions,
              XmNselectedItemCount, &selected_items_count,
              XmNselectedItems, &selected_items,
              XtPointer(0));

    bool sensitive = false;
    for (int i = 0; i < selected_items_count; i++)
    {
      String _item;
      XmStringGetLtoR(selected_items[i], LIST_CHARSET, &_item);
      string item(_item);
      XtFree(_item);

      if (item != NO_SESSION)
      {
          sensitive = true;
          break;
      }
    }

    Widget delete_w = XmSelectionBoxGetChild(dialog, XmDIALOG_APPLY_BUTTON);
    set_sensitive(delete_w, sensitive);
}

// Update list of sessions
static void update_sessions(Widget dialog)
{
    Widget sessions = XmSelectionBoxGetChild(dialog, XmDIALOG_LIST);

    StringArray session_list;
    session_list += NO_SESSION;
    get_sessions(session_list);
      
    bool *selected = new bool[session_list.size()];
    int i;
    for (i = 0; i < session_list.size(); i++)
    {
      selected[i] = (session_list[i] == app_data.session
                   || (session_list[i] == NO_SESSION 
                     && app_data.session == DEFAULT_SESSION));
    }

    setLabelList(sessions, session_list.values(),
             selected, session_list.size(), false, false);

    delete[] selected;

    update_delete(dialog);
}

#if 0
// Update list of sessions
static void UpdateSessionsCB(Widget dialog, XtPointer, XtPointer)
{
    update_sessions(dialog);
}
#endif

// Set argument from selected list item
static void SelectSessionCB(Widget sessions,
                      XtPointer client_data, XtPointer call_data)
{
    Widget dialog = Widget(client_data);
    XmListCallbackStruct *cbs = (XmListCallbackStruct *)call_data;

    // Update argument field
    if (cbs->selected_item_count == 1)
    {
      int pos = cbs->item_position;
      ListSetAndSelectPos(sessions, pos);

      String value_s;
      XmStringGetLtoR(cbs->item, LIST_CHARSET, &value_s);
      string value = value_s;
      XtFree(value_s);

      if (value == NO_SESSION)
          value = DEFAULT_SESSION;

      Widget text_w = XmSelectionBoxGetChild(dialog, XmDIALOG_TEXT);
      XmTextSetString(text_w, XMST(value.chars()));
    }

    // Update delete button
    update_delete(dialog);
}

// Create custom session dialog
static Widget create_session_panel(Widget parent, const _XtString name,
                           XtCallbackProc ok,
                           XtCallbackProc apply)
{
    Arg args[10];
    int arg = 0;

    XtSetArg(args[arg], XmNautoUnmanage, False); arg++;
    Widget dialog = 
      verify(XmCreateSelectionDialog(find_shell(parent), XMST(name), args, arg));

    Delay::register_shell(dialog);

    Widget sessions = XmSelectionBoxGetChild(dialog, XmDIALOG_LIST);

    XtAddCallback(sessions, XmNsingleSelectionCallback,
              SelectSessionCB, XtPointer(dialog));
    XtAddCallback(sessions, XmNmultipleSelectionCallback,
              SelectSessionCB, XtPointer(dialog));
    XtAddCallback(sessions, XmNextendedSelectionCallback,
              SelectSessionCB, XtPointer(dialog));
    XtAddCallback(sessions, XmNbrowseSelectionCallback,
              SelectSessionCB, XtPointer(dialog));

    XtAddCallback(dialog, XmNokCallback, 
              ok, XtPointer(sessions));
    XtAddCallback(dialog, XmNapplyCallback, 
              apply, XtPointer(sessions));
    XtAddCallback(dialog, XmNcancelCallback, 
              UnmanageThisCB, XtPointer(dialog));
    XtAddCallback(dialog, XmNhelpCallback, ImmediateHelpCB, 0);

    return dialog;
}



// ---------------------------------------------------------------------------
// Session deletion
// ---------------------------------------------------------------------------

// Delete session SESSION
void delete_session(const string& session, bool silent)
{
    if (session == DEFAULT_SESSION || session == NO_SESSION)
      return;

    StatusDelay delay("Deleting session " + quote(session));
    {
      const string s1 = session_state_file(session);
      unlink(s1.chars());
    }
    {
      const string s1 = session_core_file(session);
      unlink(s1.chars());
    }
    {
      const string s1 = session_history_file(session);
      unlink(s1.chars());
    }
    {
      const string s1 = session_lock_file(session);
      unlink(s1.chars());
    }
    {
      const string s1 = session_tmp_flag(session);
      unlink(s1.chars());
    }

    const string s2 = session_dir(session); 
    if (rmdir(s2.chars()) && !silent)
    {
      post_error("Could not delete " + quote(session_dir(session)) + ": " 
               + strerror(errno), "delete_session_error");
      delay.outcome = strerror(errno);
    }

    if (session == app_data.session)
    {
      // Current session is deleted
      set_session(DEFAULT_SESSION);
    }
}

// Remove selected sessions
static void DeleteSessionsCB(Widget dialog, XtPointer client_data, XtPointer)
{
    Widget sessions = Widget(client_data);

    XmStringTable selected_items;
    int selected_items_count = 0;

    assert(XmIsList(sessions));

    XtVaGetValues(sessions,
              XmNselectedItemCount, &selected_items_count,
              XmNselectedItems,     &selected_items,
              XtPointer(0));

    for (int i = 0; i < selected_items_count; i++)
    {
      String _item;
      XmStringGetLtoR(selected_items[i], LIST_CHARSET, &_item);
      string item(_item);
      XtFree(_item);

      delete_session(item);
    }

    update_sessions(dialog);
}



// ---------------------------------------------------------------------------
// Session save
// ---------------------------------------------------------------------------

static string get_chosen_session(Widget dialog)
{
    Widget text     = XmSelectionBoxGetChild(dialog, XmDIALOG_TEXT);

    String v = 0;
    if (XmIsText(text))
      v = XmTextGetString(text);
    else if (XmIsTextField(text))
      v = XmTextFieldGetString(text);

    string value = v;
    if (value == NO_SESSION)
      value = DEFAULT_SESSION;
    return value;
}

// Set session to V
void set_session(const string& v)
{
    static string value;
    value = v;

    app_data.session = value.chars();

    string session_name;
    if (app_data.session == DEFAULT_SESSION)
      session_name = NO_SESSION;
    else
      session_name = quote(app_data.session);

    set_status("Current session is " + session_name + ".");
    create_session_dir(app_data.session);
}

// Set the current session
static void SetSessionCB(Widget dialog, XtPointer, XtPointer)
{
    set_session(get_chosen_session(dialog));
    update_sessions(dialog);

    if (app_data.session == DEFAULT_SESSION)
    {
      XtUnmanageChild(dialog);
      return;
    }
    else
    {
      StringArray session_list;
      get_sessions(session_list);
      for (int i = 0; i < session_list.size(); i++)
          if (session_list[i] == app_data.session)
          {
            // Okay, proceed
            XtUnmanageChild(dialog);
            return;
          }
    }
}

static Widget dump_core_w     = 0;
static Widget may_kill_w      = 0;
static Widget may_gcore_w     = 0;
static Widget may_ptrace_w    = 0;
static Widget gcore_methods_w = 0;

static void SetGCoreSensitivityCB(Widget = 0, XtPointer = 0, XtPointer = 0)
{
    bool set = 
      XmToggleButtonGetState(dump_core_w) && XtIsSensitive(dump_core_w);

    set_sensitive(gcore_methods_w, set);

    set_sensitive(may_kill_w, set);
    set_sensitive(may_gcore_w, set && gdb->type() == GDB &&
               !string(app_data.get_core_command).empty());
#if HAVE_PTRACE_DUMPCORE
    set_sensitive(may_ptrace_w, set && gdb->type() == GDB);
#else
    set_sensitive(may_ptrace_w, false);
#endif
}

static unsigned long gcore_method = 0;

static void SetGCoreMethodCB(Widget, XtPointer client_data, XtPointer)
{
    gcore_method = (unsigned long)client_data;
}

static MMDesc gcore_methods[] =
{
    { "kill",   MMPush, 
      { SetGCoreMethodCB, XtPointer(0) }, 0, &may_kill_w, 0, 0 },
    { "gcore",  MMPush, 
      { SetGCoreMethodCB, XtPointer(MAY_GCORE) }, 0, &may_gcore_w, 0, 0 },
    { "ptrace", MMPush, 
      { SetGCoreMethodCB, XtPointer(MAY_PTRACE) }, 0, &may_ptrace_w, 0, 0 },
    MMEnd
};

static MMDesc gcore_items[] =
{
    { "dump",     MMToggle, 
      { SetGCoreSensitivityCB, 0 }, 0, &dump_core_w, 0, 0 },
    { "method",   MMOptionMenu, 
      { SetGCoreSensitivityCB, 0 }, gcore_methods, &gcore_methods_w, 0, 0 },
    MMEnd
};


// OK pressed in `save session'
static void SaveSessionCB(Widget w, XtPointer client_data, XtPointer call_data)
{
    SetSessionCB(w, client_data, call_data);

    if (app_data.session != DEFAULT_SESSION)
    {
      unsigned long flags = SAVE_SESSION | MAY_INTERACT;

      if (XmToggleButtonGetState(dump_core_w))
          flags |= SAVE_CORE | gcore_method;

      DDDSaveOptionsCB(w, XtPointer(flags), call_data);
    }

    // Mark as `non-temporary'
    set_temporary_session(app_data.session, false);
}

// Save current session from a list of choices
void SaveSessionAsCB(Widget w, XtPointer, XtPointer)
{
    static Widget dialog = 
      create_session_panel(w, "sessions_to_save",
                       SaveSessionCB, DeleteSessionsCB);

    if (dump_core_w == 0)
    {
      // Create panel
      Widget panel = MMcreateButtonPanel(dialog, "panel", gcore_items);
      XtVaSetValues(panel, 
                  XmNorientation, XmHORIZONTAL,
                  XmNborderWidth,  0,
                  XmNentryBorder,  0,
                  XmNspacing,      0,
                  XmNmarginWidth,  0,
                  XmNmarginHeight, 0,
                  XtPointer(0));
      MMaddCallbacks(gcore_items);
      MMaddHelpCallback(gcore_items, ImmediateHelpCB);

      // Initialize: use `kill debuggee' as default item
      XtCallActionProc(may_kill_w, "ArmAndActivate", 
                   (XEvent *)0, (String *)0, 0);
    }

    ProgramInfo info;
    bool have_data = 
      info.running || (info.core != NO_GDB_ANSWER && !info.core.empty());
    XmToggleButtonSetState(dump_core_w, have_data, True);
    set_sensitive(dump_core_w, info.running);
    SetGCoreSensitivityCB();

    string name = "";
    if (app_data.session == DEFAULT_SESSION)
    {
      // No current session - suggest a default name based on executable
      if (info.file != NO_GDB_ANSWER)
          name = info.file;


      name = basename(name.chars());
    }
    else
    {
      // Use current session name
      name = app_data.session;
    }

    MString text(name);
    XtVaSetValues(dialog, XmNtextString, text.xmstring(), XtPointer(0));

    update_sessions(dialog);
    manage_and_raise(dialog);
}



// ---------------------------------------------------------------------------
// Session open
// ---------------------------------------------------------------------------

// Get a string resource from DB named NAME with class CLS
static string get_resource(XrmDatabase db, string name, string cls)
{
    static string prefix = DDD_CLASS_NAME ".";
    name.prepend(prefix);
    cls.prepend(prefix);

    char *rtype = 0;
    XrmValue value;
    
    XrmGetResource(db, name.chars(), cls.chars(), &rtype, &value);
    if (value.addr != 0)
      return string((char *)value.addr, value.size - 1);

    return "";          // Not found
}

static Boolean done_if_idle(XtPointer data)
{
    if (emptyCommandQueue() && can_do_gdb_command())
    {
      update_settings();      // Refresh settings and signals
      update_signals();

      delete (Delay *)data;
      return True;            // Remove from the list of work procs
    }

    return False;       // Get called again
}

static void done(const string&, void *data)
{
    XtAppAddWorkProc(XtWidgetToApplicationContext(command_shell),
                 done_if_idle, data);
}

// Open the session given in SESSION
static void open_session(const string& session)
{
    // Fetch init file for this session
    string dbfile = session_state_file(session);
    XrmDatabase db = XrmGetFileDatabase(dbfile.chars());
    if (db == 0)
    {
      post_error("Cannot open session.", "open_session_error");
      return;
    }

    StatusDelay *delay_ptr = 
      new StatusDelay("Opening session " + quote(session));

    // Don't leave any more commands in the list; they'd refer to
    // another session.
    clearCommandQueue();

    ProgramInfo info;
    // Kill or detach the debuggee.
    Command c("set confirm off");
    c.verbose  = false;
    c.prompt   = false;
    c.check    = false;
    c.priority = COMMAND_PRIORITY_INIT;

    if (gdb->type() == GDB)
      gdb_command(c);

    c.command = (info.attached ? "detach" : "kill");
    gdb_command(c);

    // Clear all breakpoints and displays
    source_view->reset();
    data_disp->reset();

    // Discard current exec and core files
    if (gdb->type() == GDB)
    {
      c.command  = "file";
      gdb_command(c);
      c.command  = "core";
      gdb_command(c);
    }

    // Clear debugger console
    gdbClearWindowCB(0, 0, 0);

    // Load session-specific command history
    load_history(session_history_file(session));

    // Remove settings and signals panel (such that GDB settings and
    // signals will be updated from scratch)
    reset_settings();
    reset_signals();


    // Set buttons and display shortcuts
    static string data_buttons;
    data_buttons = get_resource(db, XtNdataButtons, XtCButtons);
    app_data.data_buttons = data_buttons.chars();

    static string source_buttons;
    source_buttons = get_resource(db, XtNsourceButtons, XtCButtons);
    app_data.source_buttons = source_buttons.chars();

    static string console_buttons;
    console_buttons = get_resource(db, XtNconsoleButtons, XtCButtons);
    app_data.console_buttons = console_buttons.chars();

    static string display_shortcuts;

    const _XtString shortcuts = 0;
    switch (gdb->type())
    {
    case BASH: shortcuts = XtNbashDisplayShortcuts; break;
    case DBG:  shortcuts = XtNdbgDisplayShortcuts;  break;
    case DBX:  shortcuts = XtNdbxDisplayShortcuts;  break;
    case GDB:  shortcuts = XtNgdbDisplayShortcuts;  break;
    case JDB:  shortcuts = XtNjdbDisplayShortcuts;  break;
    case PERL: shortcuts = XtNperlDisplayShortcuts; break;
    case PYDB: shortcuts = XtNpydbDisplayShortcuts; break;
    case XDB:  shortcuts = XtNxdbDisplayShortcuts;  break;
    }

    display_shortcuts = get_resource(db, shortcuts, XtCDisplayShortcuts);

    switch (gdb->type())
    {
    case BASH:
      app_data.bash_display_shortcuts = display_shortcuts.chars();
      break;
    case DBG:  
         app_data.dbg_display_shortcuts  = display_shortcuts.chars(); 
      break;
    case DBX:
      app_data.dbx_display_shortcuts  = display_shortcuts.chars();
      break;
    case GDB:
      app_data.gdb_display_shortcuts  = display_shortcuts.chars();
      break;
    case JDB:
      app_data.jdb_display_shortcuts  = display_shortcuts.chars();
      break;
    case PYDB:
      app_data.pydb_display_shortcuts = display_shortcuts.chars();
      break;
    case PERL:
      app_data.perl_display_shortcuts = display_shortcuts.chars();
      break;
    case XDB:
      app_data.xdb_display_shortcuts  = display_shortcuts.chars();
      break;
    }
    update_user_buttons();

    // Set options
    const string tab_width_s = get_resource(db, XtNtabWidth, XtCTabWidth);
    int tab_width = atoi(tab_width_s.chars());
    app_data.tab_width = tab_width ? tab_width : DEFAULT_TAB_WIDTH;

    update_options();


    // Enqueue start-up commands
    string restart = get_resource(db, XtNrestartCommands, XtCInitCommands);
    string settings;
    switch (gdb->type())
    {
    case BASH:
      settings = get_resource(db, XtNbashSettings, XtCSettings);
      break;

    case DBG:
      settings = get_resource(db, XtNdbgSettings, XtCSettings);
      break;

    case DBX:
      settings = get_resource(db, XtNdbxSettings, XtCSettings);
      break;

    case GDB:
      settings = get_resource(db, XtNgdbSettings, XtCSettings);
      break;

    case XDB:
      settings = get_resource(db, XtNxdbSettings, XtCSettings);
      break;

    case JDB:
      settings = get_resource(db, XtNjdbSettings, XtCSettings);
      break;

    case PYDB:
      settings = get_resource(db, XtNpydbSettings, XtCSettings);
      break;

    case PERL:
      settings = get_resource(db, XtNperlSettings, XtCSettings);
      break;

    }
    init_session(restart, settings, app_data.source_init_commands);

    // One last command to clear the delay and set up breakpoints
    c.command  = "# reset";
    c.callback = done;
    c.data     = (void *)(Delay *)delay_ptr;
    c.priority = COMMAND_PRIORITY_BATCH;
    c.verbose  = false;
    c.prompt   = false;
    c.check    = true;
    gdb_command(c);
}

// Restart GDB only.
void RestartDebuggerCB(Widget, XtPointer, XtPointer)
{
    static string restart_commands;
    static string settings;
    unsigned long flags = 0;

    if (gdb->type() == PERL)
      flags |= DONT_RELOAD_FILE;

    (void) get_restart_commands(restart_commands, flags);
    app_data.restart_commands = restart_commands.chars();

    settings = get_settings(gdb->type());
    switch (gdb->type())
    {
    case BASH:
      app_data.perl_settings = settings.chars();
      break;

    case DBG:
      app_data.dbg_settings = settings.chars();
      break;

    case DBX:
      app_data.dbx_settings = settings.chars();
      break;

    case GDB:
      app_data.gdb_settings = settings.chars();
      break;

    case JDB:
      app_data.jdb_settings = settings.chars();
      break;

    case PERL:
      app_data.perl_settings = settings.chars();
      break;

    case PYDB:
      app_data.pydb_settings = settings.chars();
      break;

    case XDB:
      app_data.xdb_settings = settings.chars();
      break;
    }

    _gdb_out("Restarting " + gdb->title() + "\n");

    // Clear all breakpoints and displays
    source_view->reset();
    data_disp->reset();

    // Abort old GDB (if still running)
    gdb->abort();

    // Start GDB again
    start_gdb(false);
}


// OK pressed in `open session'
static void OpenThisSessionCB(Widget w, XtPointer client_data, 
                        XtPointer call_data)
{
    SetSessionCB(w, client_data, call_data);
    if (app_data.session != DEFAULT_SESSION)
    {
      open_session(app_data.session);

      // Mark as `non-temporary'
      set_temporary_session(app_data.session, false);
    }
}

// Load session from a list of choices
void OpenSessionCB(Widget w, XtPointer, XtPointer)
{
    static Widget dialog = 
      create_session_panel(w, "sessions_to_open",
                       OpenThisSessionCB, DeleteSessionsCB);

    // Suggest reloading current session
    if (app_data.session != DEFAULT_SESSION)
    {
      MString text(app_data.session);
      XtVaSetValues(dialog, XmNtextString, text.xmstring(), XtPointer(0));
    }

    update_sessions(dialog);
    manage_and_raise(dialog);
}

// Name of restart session
string restart_session()
{
    if (getenv(DDD_NAME "_SESSION") != 0)
      return getenv(DDD_NAME "_SESSION");
    return "";
}

void set_restart_session(const string& session)
{
    static string env;
    env = DDD_NAME "_SESSION=" + session;
    putenv(CONST_CAST(char*,env.chars()));
}


// ---------------------------------------------------------------------------
// Core file
// ---------------------------------------------------------------------------

string session_core_file(const string& session)
{
    // Several UNIX systems have cron jobs that remove files named
    // `core' after a period of time.  So we use `dddcore' instead.

    string core = session_file(session, "core");
    if (is_core_file(core))
      return core;

    return session_file(session, ddd_NAME "core");
}


// ---------------------------------------------------------------------------
// X11R6 Session management
// ---------------------------------------------------------------------------

#if XtSpecificationRelease >= 6

// Realize X11R6 session management protocols.

static void AskSaveSmSessionCB    (Widget w, XtPointer, XtPointer call_data);
static void ConfirmSaveSmSessionCB(Widget w, XtPointer, XtPointer call_data);
static void CancelSaveSmSessionCB (Widget w, XtPointer, XtPointer call_data);

static void AskSmShutdownCB       (Widget w, XtPointer, XtPointer call_data);
static void ConfirmSmShutdownCB   (Widget w, XtPointer, XtPointer call_data);
static void CancelSmShutdownCB    (Widget w, XtPointer, XtPointer call_data);

static void ask(const string& text, const _XtString name,
            XtCheckpointToken token, Widget w,
            XtCallbackProc yes, XtCallbackProc no);

// 1. The user initiates a checkpoint.  Have DDD save its options.
void SaveSmSessionCB(Widget w, XtPointer, XtPointer call_data)
{
    XtCheckpointToken token = XtCheckpointToken(call_data);

    // Save session
    const bool interact = (token->interact_style == SmInteractStyleAny);
    const bool shutdown = token->shutdown;

    unsigned long flags = SAVE_SESSION | SAVE_GEOMETRY | SAVE_CORE;
    if (interact)
      flags |= MAY_INTERACT;
    if (shutdown)
      flags |= MAY_KILL;

    if (!shutdown && saving_options_kills_program(flags))
    {
      if (interact)
          XtAddCallback(w, XtNinteractCallback, AskSaveSmSessionCB, 0);
      else
          return;       // Cannot save state right now
    }
    else
    {
      // Save it
      token->save_success = save_options(flags);
    }

    if (shutdown)
    {
      // Prepare for shutdown
      if (interact)
          XtAddCallback(w, XtNinteractCallback, AskSmShutdownCB, 0);
      else
      {
          if (!can_do_gdb_command())
          {
            // GDB is still busy - cannot shutdown right now
            token->request_cancel = true;
          }
      }
    }
    else
    {
      // Ordinary checkpoint -- mark as temporary
      set_temporary_session(app_data.session, true);
    }
}


// Save state

// 2. Request license to kill the program
static void AskSaveSmSessionCB(Widget w, XtPointer, XtPointer call_data)
{
    XtCheckpointToken token = XtCheckpointToken(call_data);

    ask("", "kill_to_save_dialog", token, w, 
      ConfirmSaveSmSessionCB, CancelSaveSmSessionCB);
}

// Ok, kill it
static void ConfirmSaveSmSessionCB(Widget, XtPointer client_data, XtPointer)
{
    XtCheckpointToken token = XtCheckpointToken(client_data);

    unsigned long flags = 
      SAVE_SESSION | SAVE_GEOMETRY | MAY_KILL | MAY_INTERACT;

    token->save_success = save_options(flags);
    XtSessionReturnToken(token);
}

// No, don't kill -- save everything else.
static void CancelSaveSmSessionCB(Widget, XtPointer client_data, XtPointer)
{
    XtCheckpointToken token = XtCheckpointToken(client_data);

    unsigned long flags = SAVE_SESSION | SAVE_GEOMETRY | MAY_INTERACT;

    token->save_success = save_options(flags);
    XtSessionReturnToken(token);
}


// Shutdown

// 2. If the checkpoint was part of a shutdown, make sure the
//     debugged program is killed properly.
static void AskSmShutdownCB(Widget w, XtPointer, XtPointer call_data)
{
    XtCheckpointToken token = XtCheckpointToken(call_data);

    if (token->cancel_shutdown || token->request_cancel)
    {
      XtSessionReturnToken(token);
      return;
    }

    if (gdb == 0 || !gdb->running())
    {
      XtSessionReturnToken(token);
      return;
    }

    if (!can_do_gdb_command())
    {
      // Debugger is still running; request confirmation
      ask(gdb->title() + " is still busy.  Shutdown anyway (and kill it)?",
          "quit_dialog", token, w, ConfirmSmShutdownCB, 
          CancelSmShutdownCB);
      return;
    }

    ProgramInfo info;
    if (info.running)
    {
      // Program is still running; request confirmation
      ask("The program is running.  Shutdown anyway (and kill it)?",
          "shutdown_dialog", token, w, ConfirmSmShutdownCB, 
          CancelSmShutdownCB);
      return;
    }

    // Okay, go ahead.
    ConfirmSmShutdownCB(w, XtPointer(token), call_data);
}

// 3. Confirm or cancel shutdown.
static void ConfirmSmShutdownCB(Widget, XtPointer client_data, XtPointer)
{
    XtCheckpointToken token = XtCheckpointToken(client_data);
    XtSessionReturnToken(token);
}

static void CancelSmShutdownCB(Widget, XtPointer client_data, XtPointer)
{
    XtCheckpointToken token = XtCheckpointToken(client_data);
    token->request_cancel = true;
    XtSessionReturnToken(token);
}

// 4. Let DDD die.  This leaves the session as temporary.
void ShutdownSmSessionCB(Widget w, XtPointer, XtPointer call_data)
{
    if (gdb != 0 && can_do_gdb_command())
    {
      if (gdb->type() == GDB)
          gdb_question("set confirm off");
    }

    ddd_is_restarting    = false;
    ddd_is_shutting_down = true;
    _DDDExitCB(w, XtPointer(EXIT_SUCCESS), call_data);
}

// Create a confirmation dialog
static void ask(const string& text, const _XtString name, XtCheckpointToken token, Widget w,
            XtCallbackProc yes, XtCallbackProc no)
{
    // Program is still running; request confirmation
    Arg args[10];
    int arg;

    static Widget dialog = 0;
    if (dialog)
      DestroyWhenIdle(dialog);

    arg = 0;
    MString msg = rm(text);
    if (!text.empty())
    {
      XtSetArg(args[arg], XmNmessageString, msg.xmstring()); arg++;
    }
    dialog = verify(XmCreateQuestionDialog(find_shell(w), XMST(name),
                                 args, arg));
    Delay::register_shell(dialog);
    XtAddCallback(dialog, XmNokCallback,     yes, XtPointer(token));
    XtAddCallback(dialog, XmNcancelCallback, no, XtPointer(token));
    XtAddCallback(dialog, XmNhelpCallback,   ImmediateHelpCB, 0);

    manage_and_raise(dialog);
}

#else // XtSpecificationRelease < 6

// X11R5 and earlier: Nothing yet...
void SaveSmSessionCB(Widget, XtPointer, XtPointer) {}
void ShutdownSmSessionCB(Widget, XtPointer, XtPointer) {}

#endif // XtSpecificationRelease < 6

Generated by  Doxygen 1.6.0   Back to index