home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 1999 mARCH / PCWK3A99.iso / Linux / DDD331 / DDD-3_1_.000 / DDD-3_1_ / ddd-3.1.1 / ddd / session.C < prev    next >
C/C++ Source or Header  |  1998-11-24  |  32KB  |  1,285 lines

  1. // $Id: session.C,v 1.48 1998/11/24 11:24:40 zeller Exp $ -*- C++ -*-
  2. // DDD session management
  3.  
  4. // Copyright (C) 1997 Technische Universitaet Braunschweig, Germany.
  5. // Written by Andreas Zeller <zeller@ips.cs.tu-bs.de>.
  6. // 
  7. // This file is part of DDD.
  8. // 
  9. // DDD is free software; you can redistribute it and/or
  10. // modify it under the terms of the GNU General Public
  11. // License as published by the Free Software Foundation; either
  12. // version 2 of the License, or (at your option) any later version.
  13. // 
  14. // DDD is distributed in the hope that it will be useful,
  15. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  17. // See the GNU General Public License for more details.
  18. // 
  19. // You should have received a copy of the GNU General Public
  20. // License along with DDD -- see the file COPYING.
  21. // If not, write to the Free Software Foundation, Inc.,
  22. // 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  23. // 
  24. // DDD is the data display debugger.
  25. // For details, see the DDD World-Wide-Web page, 
  26. // `http://www.cs.tu-bs.de/softech/ddd/',
  27. // or send a mail to the DDD developers <ddd@ips.cs.tu-bs.de>.
  28.  
  29. char session_rcsid[] = 
  30.     "$Id: session.C,v 1.48 1998/11/24 11:24:40 zeller Exp $";
  31.  
  32. #ifdef __GNUG__
  33. #pragma implementation
  34. #endif
  35.  
  36. #include "session.h"
  37.  
  38. #include "config.h"
  39.  
  40. #if XtSpecificationRelease >= 6
  41. #include <X11/SM/SM.h>
  42. #endif
  43.  
  44. #include <sys/types.h>
  45. #include <stdlib.h>        // putenv(), getenv(), getuid()
  46. #include <stdio.h>
  47. #include <signal.h>        // kill()
  48. #include <string.h>        // strerror()
  49. #include <errno.h>
  50. #include <fstream.h>
  51. #include <pwd.h>
  52.  
  53. #include "AppData.h"
  54. #include "DataDisp.h"
  55. #include "Delay.h"
  56. #include "DestroyCB.h"
  57. #include "ExitCB.h"
  58. #include "GDBAgent.h"
  59. #include "HelpCB.h"
  60. #include "MakeMenu.h"
  61. #include "SmartC.h"
  62. #include "SourceView.h"
  63. #include "charsets.h"
  64. #include "Command.h"
  65. #include "comm-manag.h"
  66. #include "disp-read.h"
  67. #include "ddd.h"
  68. #include "cook.h"
  69. #include "editing.h"
  70. #include "exit.h"
  71. #include "file.h"
  72. #include "filetype.h"
  73. #include "glob.h"
  74. #include "history.h"
  75. #include "home.h"
  76. #include "hostname.h"
  77. #include "mydialogs.h"
  78. #include "options.h"
  79. #include "post.h"
  80. #include "question.h"
  81. #include "settings.h"
  82. #include "status.h"
  83. #include "string-fun.h"
  84. #include "verify.h"
  85. #include "version.h"
  86. #include "windows.h"
  87. #include "wm.h"
  88.  
  89. #include <Xm/Xm.h>
  90. #include <Xm/List.h>
  91. #include <Xm/MessageB.h>
  92. #include <Xm/SelectioB.h>
  93. #include <Xm/PushB.h>
  94. #include <Xm/ToggleB.h>
  95. #include <Xm/Text.h>
  96. #include <Xm/TextF.h>
  97.  
  98. extern "C" {
  99. #include <sys/types.h>
  100. #include <unistd.h>
  101. #include <ctype.h>
  102.  
  103. #if HAVE_SYS_STAT_H
  104. #include <sys/stat.h>        // mkdir()
  105. #endif
  106. }
  107.  
  108. #ifndef S_IRWXU
  109. #define S_IRWXU 0700
  110. #endif
  111.  
  112. #ifndef S_IRWXG
  113. #define S_IRWXG 070
  114. #endif
  115.  
  116. #ifndef S_IRWXO
  117. #define S_IRWXO 07
  118. #endif
  119.  
  120.  
  121. // ---------------------------------------------------------------------------
  122. // How to present a default session to the user
  123. // ---------------------------------------------------------------------------
  124.  
  125. const string NO_SESSION = "[none]";
  126.  
  127.  
  128. // ---------------------------------------------------------------------------
  129. // Session files
  130. // ---------------------------------------------------------------------------
  131.  
  132. string session_state_dir()
  133. {
  134.     char *ddd_state = getenv(DDD_NAME "_STATE");
  135.     if (ddd_state != 0)
  136.     return ddd_state;
  137.     else
  138.     return string(gethome()) + "/." ddd_NAME;
  139. }
  140.  
  141. static string session_base_dir()
  142. {
  143.     char *ddd_sessions = getenv(DDD_NAME "_SESSIONS");
  144.     if (ddd_sessions != 0)
  145.     return ddd_sessions;
  146.     else
  147.     return session_state_dir() + "/sessions";
  148. }
  149.  
  150. static string session_dir(const string& session)
  151. {
  152.     if (session == DEFAULT_SESSION)
  153.     return session_state_dir();
  154.     else
  155.     return session_base_dir() + "/" + session;
  156. }
  157.  
  158. string session_file(const string& session, const string& base)
  159. {
  160.     return session_dir(session) + "/" + base;
  161. }
  162.  
  163.  
  164. // ---------------------------------------------------------------------------
  165. // Session file creation
  166. // ---------------------------------------------------------------------------
  167.  
  168. class StreamAction {
  169. private:
  170.     ostream& stream;
  171.     string action;
  172.     bool ok;
  173.  
  174. public:
  175.     StreamAction(ostream& os, const string& c)
  176.     : stream(os), action(c), ok(true)
  177.     {
  178.     stream << action << "...\n";
  179.     }
  180.     ~StreamAction()
  181.     {
  182.     if (ok)
  183.         stream << action << "...done.\n";
  184.     }
  185.     void failed(const string& msg)
  186.     {
  187.     stream << action << " failed: " << msg << "\n";
  188.     ok = false;
  189.     }
  190. };
  191.  
  192. static int makedir(string name, ostream& msg, bool user_only = false)
  193. {
  194.     StreamAction action(msg, "Creating " + quote(name + "/"));
  195.  
  196.     mode_t mask = umask(0);
  197.     umask(mask);
  198.     mode_t mode;
  199.     if (user_only)
  200.     mode = S_IRWXU & ~mask;
  201.     else
  202.     mode = (S_IRWXU | S_IRWXG | S_IRWXO) & ~mask;
  203.     int ret = mkdir(name, mode);
  204.  
  205.     if (ret != 0)
  206.     action.failed(strerror(errno));
  207.  
  208.     return ret;
  209. }
  210.  
  211. static void copy(const string& from_name, const string& to_name, ostream& msg)
  212. {
  213.     FILE *from = fopen(from_name, "r");
  214.     if (from == NULL)
  215.     return;            // Don't care
  216.  
  217.     StreamAction action(msg, "Copying " + quote(from_name) 
  218.             + " to " + quote(to_name));
  219.  
  220.     FILE *to = fopen(to_name, "w");
  221.     if (to == NULL)
  222.     {
  223.     action.failed(strerror(errno));
  224.     return;
  225.     }
  226.  
  227.     int c;
  228.     while ((c = getc(from)) != EOF)
  229.     putc(c, to);
  230.  
  231.     fclose(from);
  232.     if (fclose(to) == EOF)
  233.     {
  234.     action.failed(strerror(errno));
  235.     unlink(to_name);
  236.     }
  237. }
  238.  
  239. // Create DDD state directory
  240. static void create_session_state_dir(ostream& msg)
  241. {
  242.     // Create or find state directory
  243.     if (!is_directory(session_state_dir()) && 
  244.     makedir(session_state_dir(), msg, true) == 0)
  245.     {
  246.     // Check for DDD 2.1 `~/.dddinit' and `~/.ddd_history' files; 
  247.     // copy them to new location if needed
  248.     copy(string(gethome()) + "/.dddinit",
  249.          session_state_file(DEFAULT_SESSION), msg);
  250.     copy(string(gethome()) + "/.ddd_history",
  251.          session_history_file(DEFAULT_SESSION), msg);
  252.     }
  253.  
  254.     // Create session base directory
  255.     if (!is_directory(session_base_dir()))
  256.     makedir(session_base_dir(), msg);
  257. }
  258.  
  259. // Create session directory
  260. void create_session_dir(const string& session, ostream& msg)
  261. {
  262.     // Create state directory
  263.     create_session_state_dir(msg);
  264.  
  265.     // Create session directory
  266.     if (session != DEFAULT_SESSION 
  267.     && is_directory(session_base_dir())
  268.     && !is_directory(session_dir(session))
  269.     && makedir(session_dir(session), msg) == 0)
  270.     {
  271.     // Check for default session state and history files;
  272.     // copy them to new location
  273.     copy(session_state_file(DEFAULT_SESSION), 
  274.          session_state_file(session), msg);
  275.     copy(session_history_file(DEFAULT_SESSION), 
  276.          session_history_file(session), msg);
  277.     }
  278. }
  279.  
  280. // Same, but issue messages in status line
  281. void create_session_dir(const string& session)
  282. {
  283.     ostrstream messages;
  284.     create_session_dir(session, messages);
  285.     string msg(messages);
  286.     while (msg != "")
  287.     {
  288.     string line = msg.before('\n');
  289.     set_status(line);
  290.     msg = msg.after('\n');
  291.     }
  292. }
  293.  
  294.  
  295. // ---------------------------------------------------------------------------
  296. // Session locks
  297. // ---------------------------------------------------------------------------
  298.  
  299. bool lock_session_dir(Display *display,
  300.               const string& session, 
  301.               LockInfo& info)
  302. {
  303.     info.pid = 0;
  304.     string lock_file = session_lock_file(session);
  305.  
  306.     {
  307.     ifstream is(lock_file);
  308.     if (!is.bad())
  309.     {
  310.         string version;
  311.         is >> version;
  312.  
  313.         if (version.contains(DDD_NAME, 0))
  314.         {
  315.         // Lock already exists -- use contents as diagnostics
  316.         is >> info.hostname
  317.            >> info.pid 
  318.            >> info.display 
  319.            >> info.uid
  320.            >> info.username;
  321.  
  322.         if (info.username == "")
  323.         {
  324.             // DDD 3.0.90 and earlier do not save the user name
  325.             info.username = itostring(info.uid);
  326.         }
  327.  
  328.         if (info.hostname != fullhostname())
  329.             return false;    // Process running on remote host
  330.  
  331.         if (info.pid > 0 && kill(info.pid, 0) == 0)
  332.             return false;    // Process running on host
  333.         }
  334.     }
  335.     }
  336.  
  337.     // Lock session dir, possibly overriding old lock
  338.     {
  339.     string username;
  340.     struct passwd *pw = getpwuid(getuid());
  341.     if (pw != 0)
  342.         username = pw->pw_name;
  343.     else
  344.         username = itostring(getuid());
  345.  
  346.     ofstream os(lock_file);
  347.     os << DDD_NAME "-" DDD_VERSION
  348.        << " " << fullhostname()
  349.        << " " << getpid()
  350.        << " " << XDisplayString(display)
  351.        << " " << getuid()
  352.        << " " << username
  353.        << "\n";
  354.     }
  355.  
  356.     return true;
  357. }
  358.  
  359. bool unlock_session_dir(const string& session)
  360. {
  361.     string lock_file = session_lock_file(session);
  362.  
  363.     ifstream is(lock_file);
  364.     if (!is.bad())
  365.     {
  366.     // There is a lock -- check whether it's ours
  367.     LockInfo info;
  368.     string version;
  369.     is >> version 
  370.        >> info.hostname
  371.        >> info.pid;
  372.  
  373.     if (info.hostname == fullhostname() && info.pid == getpid())
  374.     {
  375.         // It's ours -- remove it
  376.         is.close();
  377.         unlink(lock_file);
  378.         return true;
  379.     }
  380.     else
  381.     {
  382.         return false;    // Mismatched lock
  383.     }
  384.     }
  385.  
  386.     return false;        // No lock
  387. }
  388.  
  389.  
  390. // ---------------------------------------------------------------------------
  391. // Session selection
  392. // ---------------------------------------------------------------------------
  393.  
  394. static void get_sessions(StringArray& arr)
  395. {
  396.     string mask = session_state_file("*");
  397.     char **files = glob_filename(mask);
  398.     if (files == (char **)0)
  399.     {
  400.     cerr << mask << ": glob failed\n";
  401.     }
  402.     else if (files == (char **)-1)
  403.     {
  404.     if (errno != 0)
  405.         post_error(string(mask) + ": " + strerror(errno), 
  406.                "no_sessions_error");
  407.     }
  408.     else
  409.     {
  410.     int count;
  411.     for (count = 0; files[count] != 0; count++)
  412.         ;
  413.     smart_sort(files, count);
  414.  
  415.     for (int i = 0; i < count; i++)
  416.     {
  417.         string file = files[i];
  418.         free(files[i]);
  419.         file = file.before('/', -1);
  420.         file = file.after('/', -1);
  421.         arr += file;
  422.     }
  423.     free((char *)files);
  424.     }
  425. }
  426.  
  427. // Update state of `delete' button
  428. static void update_delete(Widget dialog)
  429. {
  430.     Widget sessions = XmSelectionBoxGetChild(dialog, XmDIALOG_LIST);
  431.  
  432.     // Check for `Delete' button
  433.     XmStringTable selected_items;
  434.     int selected_items_count = 0;
  435.     XtVaGetValues(sessions,
  436.           XmNselectedItemCount, &selected_items_count,
  437.           XmNselectedItems, &selected_items,
  438.           NULL);
  439.  
  440.     bool sensitive = false;
  441.     for (int i = 0; i < selected_items_count; i++)
  442.     {
  443.     String _item;
  444.     XmStringGetLtoR(selected_items[i], LIST_CHARSET, &_item);
  445.     string item(_item);
  446.     XtFree(_item);
  447.  
  448.     if (item != NO_SESSION)
  449.     {
  450.         sensitive = true;
  451.         break;
  452.     }
  453.     }
  454.  
  455.     Widget delete_w = XmSelectionBoxGetChild(dialog, XmDIALOG_APPLY_BUTTON);
  456.     set_sensitive(delete_w, sensitive);
  457. }
  458.  
  459. // Update list of sessions
  460. static void update_sessions(Widget dialog)
  461. {
  462.     Widget sessions = XmSelectionBoxGetChild(dialog, XmDIALOG_LIST);
  463.  
  464.     StringArray session_list;
  465.     session_list += NO_SESSION;
  466.     get_sessions(session_list);
  467.     
  468.     bool *selected = new bool[session_list.size()];
  469.     int i;
  470.     for (i = 0; i < session_list.size(); i++)
  471.     {
  472.     selected[i] = (session_list[i] == app_data.session
  473.                || (session_list[i] == NO_SESSION 
  474.                && app_data.session == DEFAULT_SESSION));
  475.     }
  476.  
  477.     setLabelList(sessions, session_list.values(),
  478.          selected, session_list.size(), false, false);
  479.  
  480.     delete[] selected;
  481.  
  482.     update_delete(dialog);
  483. }
  484.  
  485. #if 0
  486. // Update list of sessions
  487. static void UpdateSessionsCB(Widget dialog, XtPointer, XtPointer)
  488. {
  489.     update_sessions(dialog);
  490. }
  491. #endif
  492.  
  493. // Set argument from selected list item
  494. static void SelectSessionCB(Widget sessions,
  495.                 XtPointer client_data, XtPointer call_data)
  496. {
  497.     Widget dialog = Widget(client_data);
  498.     XmListCallbackStruct *cbs = (XmListCallbackStruct *)call_data;
  499.  
  500.     // Update argument field
  501.     if (cbs->selected_item_count == 1)
  502.     {
  503.     int pos = cbs->item_position;
  504.     ListSetAndSelectPos(sessions, pos);
  505.  
  506.     String value_s;
  507.     XmStringGetLtoR(cbs->item, LIST_CHARSET, &value_s);
  508.     string value = value_s;
  509.     XtFree(value_s);
  510.  
  511.     if (value == NO_SESSION)
  512.         value = DEFAULT_SESSION;
  513.  
  514.     Widget text_w = XmSelectionBoxGetChild(dialog, XmDIALOG_TEXT);
  515.     XmTextSetString(text_w, (char *)value.chars());
  516.     }
  517.  
  518.     // Update delete button
  519.     update_delete(dialog);
  520. }
  521.  
  522. // Create custom session dialog
  523. static Widget create_session_panel(Widget parent, String name,
  524.                    XtCallbackProc ok,
  525.                    XtCallbackProc apply)
  526. {
  527.     Arg args[10];
  528.     int arg = 0;
  529.  
  530.     XtSetArg(args[arg], XmNautoUnmanage, False); arg++;
  531.     Widget dialog = 
  532.     verify(XmCreateSelectionDialog(find_shell(parent), name, args, arg));
  533.  
  534.     Delay::register_shell(dialog);
  535.  
  536.     Widget sessions = XmSelectionBoxGetChild(dialog, XmDIALOG_LIST);
  537.  
  538.     XtAddCallback(sessions, XmNsingleSelectionCallback,
  539.           SelectSessionCB, XtPointer(dialog));
  540.     XtAddCallback(sessions, XmNmultipleSelectionCallback,
  541.           SelectSessionCB, XtPointer(dialog));
  542.     XtAddCallback(sessions, XmNextendedSelectionCallback,
  543.           SelectSessionCB, XtPointer(dialog));
  544.     XtAddCallback(sessions, XmNbrowseSelectionCallback,
  545.           SelectSessionCB, XtPointer(dialog));
  546.  
  547.     XtAddCallback(dialog, XmNokCallback, 
  548.           ok, XtPointer(sessions));
  549.     XtAddCallback(dialog, XmNapplyCallback, 
  550.           apply, XtPointer(sessions));
  551.     XtAddCallback(dialog, XmNcancelCallback, 
  552.           UnmanageThisCB, XtPointer(dialog));
  553.     XtAddCallback(dialog, XmNhelpCallback, ImmediateHelpCB, 0);
  554.  
  555.     return dialog;
  556. }
  557.  
  558.  
  559.  
  560. // ---------------------------------------------------------------------------
  561. // Session deletion
  562. // ---------------------------------------------------------------------------
  563.  
  564. // Delete session SESSION
  565. void delete_session(const string& session, bool silent)
  566. {
  567.     if (session == DEFAULT_SESSION || session == NO_SESSION)
  568.     return;
  569.  
  570.     StatusDelay delay("Deleting session " + quote(session));
  571.     unlink(session_state_file(session));
  572.     unlink(session_core_file(session));
  573.     unlink(session_history_file(session));
  574.     unlink(session_lock_file(session));
  575.  
  576.     if (rmdir(session_dir(session)) && !silent)
  577.     {
  578.     post_error("Could not delete " + quote(session_dir(session)) + ": " 
  579.            + strerror(errno), "delete_session_error");
  580.     delay.outcome = strerror(errno);
  581.     }
  582.  
  583.     if (session == app_data.session)
  584.     {
  585.     // Current session is deleted
  586.     set_session(DEFAULT_SESSION);
  587.     }
  588. }
  589.  
  590. // Remove selected sessions
  591. static void DeleteSessionsCB(Widget dialog, XtPointer client_data, XtPointer)
  592. {
  593.     Widget sessions = Widget(client_data);
  594.  
  595.     XmStringTable selected_items;
  596.     int selected_items_count = 0;
  597.  
  598.     assert(XmIsList(sessions));
  599.  
  600.     XtVaGetValues(sessions,
  601.           XmNselectedItemCount, &selected_items_count,
  602.           XmNselectedItems,     &selected_items,
  603.           NULL);
  604.  
  605.     for (int i = 0; i < selected_items_count; i++)
  606.     {
  607.     String _item;
  608.     XmStringGetLtoR(selected_items[i], LIST_CHARSET, &_item);
  609.     string item(_item);
  610.     XtFree(_item);
  611.  
  612.     delete_session(item);
  613.     }
  614.  
  615.     update_sessions(dialog);
  616. }
  617.  
  618.  
  619.  
  620. // ---------------------------------------------------------------------------
  621. // Session save
  622. // ---------------------------------------------------------------------------
  623.  
  624. static string get_chosen_session(Widget dialog)
  625. {
  626.     Widget text     = XmSelectionBoxGetChild(dialog, XmDIALOG_TEXT);
  627.  
  628.     String v = 0;
  629.     if (XmIsText(text))
  630.     v = XmTextGetString(text);
  631.     else if (XmIsTextField(text))
  632.     v = XmTextFieldGetString(text);
  633.  
  634.     string value = v;
  635.     if (value == NO_SESSION)
  636.     value = DEFAULT_SESSION;
  637.     return value;
  638. }
  639.  
  640. // Set session to V
  641. void set_session(const string& v)
  642. {
  643.     static string value;
  644.     value = v;
  645.  
  646.     app_data.session = value;
  647.  
  648.     string session_name;
  649.     if (app_data.session == DEFAULT_SESSION)
  650.     session_name = NO_SESSION;
  651.     else
  652.     session_name = quote(app_data.session);
  653.  
  654.     set_status("Current session is " + session_name + ".");
  655.     create_session_dir(app_data.session);
  656. }
  657.  
  658. // Set the current session
  659. static void SetSessionCB(Widget dialog, XtPointer, XtPointer)
  660. {
  661.     set_session(get_chosen_session(dialog));
  662.     update_sessions(dialog);
  663.  
  664.     if (app_data.session == DEFAULT_SESSION)
  665.     {
  666.     XtUnmanageChild(dialog);
  667.     return;
  668.     }
  669.     else
  670.     {
  671.     StringArray session_list;
  672.     get_sessions(session_list);
  673.     for (int i = 0; i < session_list.size(); i++)
  674.         if (session_list[i] == app_data.session)
  675.         {
  676.         // Okay, proceed
  677.         XtUnmanageChild(dialog);
  678.         return;
  679.         }
  680.     }
  681. }
  682.  
  683. static Widget dump_core_w     = 0;
  684. static Widget may_kill_w      = 0;
  685. static Widget may_gcore_w     = 0;
  686. static Widget may_ptrace_w    = 0;
  687. static Widget gcore_methods_w = 0;
  688.  
  689. static void SetGCoreSensitivityCB(Widget = 0, XtPointer = 0, XtPointer = 0)
  690. {
  691.     bool set = 
  692.     XmToggleButtonGetState(dump_core_w) && XtIsSensitive(dump_core_w);
  693.  
  694.     set_sensitive(gcore_methods_w, set);
  695.  
  696.     set_sensitive(may_kill_w, set);
  697.     set_sensitive(may_gcore_w, set && gdb->type() == GDB &&
  698.            string(app_data.get_core_command) != "");
  699. #if HAVE_PTRACE_DUMPCORE
  700.     set_sensitive(may_ptrace_w, set && gdb->type() == GDB);
  701. #else
  702.     set_sensitive(may_ptrace_w, False);
  703. #endif
  704. }
  705.  
  706. static unsigned long gcore_method = 0;
  707.  
  708. static void SetGCoreMethodCB(Widget, XtPointer client_data, XtPointer)
  709. {
  710.     gcore_method = (unsigned long)client_data;
  711. }
  712.  
  713. static MMDesc gcore_methods[] =
  714. {
  715.     { "kill",   MMPush, 
  716.       { SetGCoreMethodCB, XtPointer(0) }, NULL, &may_kill_w, 0, 0 },
  717.     { "gcore",  MMPush, 
  718.       { SetGCoreMethodCB, XtPointer(MAY_GCORE) }, NULL, &may_gcore_w, 0, 0 },
  719.     { "ptrace", MMPush, 
  720.       { SetGCoreMethodCB, XtPointer(MAY_PTRACE) }, NULL, &may_ptrace_w, 0, 0 },
  721.     MMEnd
  722. };
  723.  
  724. static MMDesc gcore_items[] =
  725. {
  726.     { "dump",     MMToggle, 
  727.       { SetGCoreSensitivityCB, 0 }, NULL, &dump_core_w, 0, 0 },
  728.     { "method",   MMOptionMenu, 
  729.       { SetGCoreSensitivityCB, 0 }, gcore_methods, &gcore_methods_w, 0, 0 },
  730.     MMEnd
  731. };
  732.  
  733.  
  734. // OK pressed in `save session'
  735. static void SaveSessionCB(Widget w, XtPointer client_data, XtPointer call_data)
  736. {
  737.     SetSessionCB(w, client_data, call_data);
  738.  
  739.     if (app_data.session != DEFAULT_SESSION)
  740.     {
  741.     unsigned long flags = SAVE_SESSION | MAY_INTERACT;
  742.  
  743.     if (XmToggleButtonGetState(dump_core_w))
  744.         flags |= SAVE_CORE | gcore_method;
  745.  
  746.     DDDSaveOptionsCB(w, XtPointer(flags), call_data);
  747.     }
  748. }
  749.  
  750. // Save current session from a list of choices
  751. void SaveSessionAsCB(Widget w, XtPointer, XtPointer)
  752. {
  753.     static Widget dialog = 
  754.     create_session_panel(w, "sessions_to_save",
  755.                  SaveSessionCB, DeleteSessionsCB);
  756.  
  757.     if (dump_core_w == 0)
  758.     {
  759.     // Create panel
  760.     Widget panel = MMcreateButtonPanel(dialog, "panel", gcore_items);
  761.     XtVaSetValues(panel, 
  762.               XmNorientation, XmHORIZONTAL,
  763.               XmNborderWidth,  0,
  764.               XmNentryBorder,  0,
  765.               XmNspacing,      0,
  766.               XmNmarginWidth,  0,
  767.               XmNmarginHeight, 0,
  768.               NULL);
  769.     MMaddCallbacks(gcore_items);
  770.     MMaddHelpCallback(gcore_items, ImmediateHelpCB);
  771.  
  772.     // Initialize: use `kill debuggee' as default item
  773.     XtCallActionProc(may_kill_w, "ArmAndActivate", 
  774.              (XEvent *)0, (String *)0, 0);
  775.     }
  776.  
  777.     ProgramInfo info;
  778.     bool have_data = 
  779.     info.running || (info.core != NO_GDB_ANSWER && info.core != "");
  780.     XmToggleButtonSetState(dump_core_w, have_data, True);
  781.     set_sensitive(dump_core_w, info.running);
  782.     SetGCoreSensitivityCB();
  783.  
  784.     string name = "";
  785.     if (app_data.session == DEFAULT_SESSION)
  786.     {
  787.     // No current session - suggest a default name based on executable
  788.     if (info.file != NO_GDB_ANSWER)
  789.         name = info.file;
  790.  
  791.     name = SourceView::basename(name);
  792.     }
  793.     else
  794.     {
  795.     // Use current session name
  796.     name = app_data.session;
  797.     }
  798.  
  799.     MString text(name);
  800.     XtVaSetValues(dialog, XmNtextString, text.xmstring(), NULL);
  801.  
  802.     update_sessions(dialog);
  803.     manage_and_raise(dialog);
  804. }
  805.  
  806.  
  807.  
  808. // ---------------------------------------------------------------------------
  809. // Session open
  810. // ---------------------------------------------------------------------------
  811.  
  812. // Get a string resource from DB named NAME with class CLS
  813. static string get_resource(XrmDatabase db, string name, string cls)
  814. {
  815.     static string prefix = DDD_CLASS_NAME ".";
  816.     name.prepend(prefix);
  817.     cls.prepend(prefix);
  818.  
  819.     char *rtype = 0;
  820.     XrmValue value;
  821.     
  822.     XrmGetResource(db, name, cls, &rtype, &value);
  823.     if (value.addr != 0)
  824.     return string((char *)value.addr, value.size - 1);
  825.  
  826.     return "";        // Not found
  827. }
  828.  
  829. static Boolean done_if_idle(XtPointer data)
  830. {
  831.     if (emptyCommandQueue() && gdb->isReadyWithPrompt())
  832.     {
  833.     update_settings();    // Refresh settings and signals
  834.     update_signals();
  835.  
  836.     delete (Delay *)data;
  837.     return True;        // Remove from the list of work procs
  838.     }
  839.  
  840.     return False;        // Get called again
  841. }
  842.  
  843. static void done(const string&, void *data)
  844. {
  845.     XtAppAddWorkProc(XtWidgetToApplicationContext(command_shell),
  846.              done_if_idle, data);
  847. }
  848.  
  849. // Open the session given in SESSION
  850. static void open_session(const string& session)
  851. {
  852.     // Fetch init file for this session
  853.     string dbfile = session_state_file(session);
  854.     XrmDatabase db = XrmGetFileDatabase(dbfile);
  855.     if (db == 0)
  856.     {
  857.     post_error("Cannot open session.", "open_session_error");
  858.     return;
  859.     }
  860.  
  861.     StatusDelay *delay_ptr = 
  862.     new StatusDelay("Opening session " + quote(session));
  863.  
  864.     // Don't leave any more commands in the list; they'd refer to
  865.     // another session.
  866.     clearCommandQueue();
  867.  
  868.     ProgramInfo info;
  869.     // Kill or detach the debuggee.
  870.     Command c("set confirm off");
  871.     c.verbose  = false;
  872.     c.prompt   = false;
  873.     c.check    = false;
  874.     c.priority = COMMAND_PRIORITY_INIT;
  875.  
  876.     if (gdb->type() == GDB)
  877.     gdb_command(c);
  878.  
  879.     c.command = (info.attached ? "detach" : "kill");
  880.     gdb_command(c);
  881.  
  882.     // Clear all breakpoints and displays
  883.     source_view->reset();
  884.     data_disp->reset();
  885.  
  886.     // Discard current exec and core files
  887.     if (gdb->type() == GDB)
  888.     {
  889.     c.command  = "file";
  890.     gdb_command(c);
  891.     c.command  = "core";
  892.     gdb_command(c);
  893.     }
  894.  
  895.     // Clear debugger console
  896.     gdbClearWindowCB(0, 0, 0);
  897.  
  898.     // Load session-specific command history
  899.     load_history(session_history_file(session));
  900.  
  901.     // Remove settings and signals panel (such that GDB settings and
  902.     // signals will be updated from scratch)
  903.     reset_settings();
  904.     reset_signals();
  905.  
  906.  
  907.     // Set buttons and display shortcuts
  908.     static string data_buttons;
  909.     data_buttons = get_resource(db, XtNdataButtons, XtCButtons);
  910.     app_data.data_buttons = data_buttons;
  911.  
  912.     static string source_buttons;
  913.     source_buttons = get_resource(db, XtNsourceButtons, XtCButtons);
  914.     app_data.source_buttons = source_buttons;
  915.  
  916.     static string console_buttons;
  917.     console_buttons = get_resource(db, XtNconsoleButtons, XtCButtons);
  918.     app_data.console_buttons = console_buttons;
  919.  
  920.     static string display_shortcuts;
  921.  
  922.     String shortcuts = 0;
  923.     switch (gdb->type())
  924.     {
  925.     case GDB:  shortcuts = XtNgdbDisplayShortcuts;  break;
  926.     case DBX:  shortcuts = XtNdbxDisplayShortcuts;  break;
  927.     case XDB:  shortcuts = XtNxdbDisplayShortcuts;  break;
  928.     case JDB:  shortcuts = XtNjdbDisplayShortcuts;  break;
  929.     case PYDB: shortcuts = XtNpydbDisplayShortcuts; break;
  930.     case PERL: shortcuts = XtNperlDisplayShortcuts; break;
  931.     }
  932.  
  933.     display_shortcuts = get_resource(db, shortcuts, XtCDisplayShortcuts);
  934.  
  935.     switch (gdb->type())
  936.     {
  937.     case GDB:  app_data.gdb_display_shortcuts  = display_shortcuts; break;
  938.     case DBX:  app_data.dbx_display_shortcuts  = display_shortcuts; break;
  939.     case XDB:  app_data.xdb_display_shortcuts  = display_shortcuts; break;
  940.     case JDB:  app_data.jdb_display_shortcuts  = display_shortcuts; break;
  941.     case PYDB: app_data.pydb_display_shortcuts = display_shortcuts; break;
  942.     case PERL: app_data.perl_display_shortcuts = display_shortcuts; break;
  943.     }
  944.     update_user_buttons();
  945.  
  946.     // Set options
  947.     int tab_width = atoi(get_resource(db, XtNtabWidth, XtCTabWidth));
  948.     app_data.tab_width = tab_width ? tab_width : 8;
  949.  
  950.     update_options();
  951.  
  952.  
  953.     // Enqueue start-up commands
  954.     string restart = get_resource(db, XtNrestartCommands, XtCInitCommands);
  955.     string settings;
  956.     switch (gdb->type())
  957.     {
  958.     case GDB:
  959.     settings = get_resource(db, XtNgdbSettings, XtCSettings);
  960.     break;
  961.  
  962.     case DBX:
  963.     settings = get_resource(db, XtNdbxSettings, XtCSettings);
  964.     break;
  965.  
  966.     case XDB:
  967.     settings = get_resource(db, XtNxdbSettings, XtCSettings);
  968.     break;
  969.  
  970.     case JDB:
  971.     settings = get_resource(db, XtNjdbSettings, XtCSettings);
  972.     break;
  973.  
  974.     case PYDB:
  975.     settings = get_resource(db, XtNpydbSettings, XtCSettings);
  976.     break;
  977.  
  978.     case PERL:
  979.     settings = get_resource(db, XtNperlSettings, XtCSettings);
  980.     break;
  981.     }
  982.     init_session(restart, settings, app_data.source_init_commands);
  983.  
  984.     // One last command to clear the delay and set up breakpoints
  985.     c.command  = "# reset";
  986.     c.callback = done;
  987.     c.data     = (void *)(Delay *)delay_ptr;
  988.     c.priority = COMMAND_PRIORITY_BATCH;
  989.     c.verbose  = false;
  990.     c.prompt   = false;
  991.     c.check    = true;
  992.     gdb_command(c);
  993. }
  994.  
  995. // Restart GDB only.
  996. void RestartDebuggerCB(Widget, XtPointer, XtPointer)
  997. {
  998.     static string restart_commands;
  999.     unsigned long flags = 0;
  1000.  
  1001.     (void) get_restart_commands(restart_commands, flags);
  1002.     app_data.restart_commands = restart_commands;
  1003.  
  1004.     _gdb_out("Restarting " + gdb->title() + "\n");
  1005.  
  1006.     // Clear all breakpoints and displays
  1007.     source_view->reset();
  1008.     data_disp->reset();
  1009.  
  1010.     // Start GDB again
  1011.     start_gdb(false);
  1012. }
  1013.  
  1014.  
  1015. // OK pressed in `open session'
  1016. static void OpenThisSessionCB(Widget w, XtPointer client_data, 
  1017.                   XtPointer call_data)
  1018. {
  1019.     SetSessionCB(w, client_data, call_data);
  1020.     if (app_data.session != DEFAULT_SESSION)
  1021.     {
  1022.     open_session(app_data.session);
  1023.     }
  1024. }
  1025.  
  1026. // Load session from a list of choices
  1027. void OpenSessionCB(Widget w, XtPointer, XtPointer)
  1028. {
  1029.     static Widget dialog = 
  1030.     create_session_panel(w, "sessions_to_open",
  1031.                  OpenThisSessionCB, DeleteSessionsCB);
  1032.  
  1033.     // Suggest reloading current session
  1034.     if (app_data.session != DEFAULT_SESSION)
  1035.     {
  1036.     MString text(app_data.session);
  1037.     XtVaSetValues(dialog, XmNtextString, text.xmstring(), NULL);
  1038.     }
  1039.  
  1040.     update_sessions(dialog);
  1041.     manage_and_raise(dialog);
  1042. }
  1043.  
  1044. // Name of restart session
  1045. string restart_session()
  1046. {
  1047.     if (getenv(DDD_NAME "_SESSION") != 0)
  1048.     return getenv(DDD_NAME "_SESSION");
  1049.     return "";
  1050. }
  1051.  
  1052. void set_restart_session(const string& session)
  1053. {
  1054.     static string env;
  1055.     env = DDD_NAME "_SESSION=" + session;
  1056.     putenv(env);
  1057. }
  1058.  
  1059.  
  1060. // ---------------------------------------------------------------------------
  1061. // Core file
  1062. // ---------------------------------------------------------------------------
  1063.  
  1064. string session_core_file(const string& session)
  1065. {
  1066.     // Several UNIX systems have cron jobs that remove files named
  1067.     // `core' after a period of time.  So we use `dddcore' instead.
  1068.  
  1069.     string core = session_file(session, "core");
  1070.     if (is_core_file(core))
  1071.     return core;
  1072.  
  1073.     return session_file(session, ddd_NAME "core");
  1074. }
  1075.  
  1076.  
  1077. // ---------------------------------------------------------------------------
  1078. // X11R6 Session management
  1079. // ---------------------------------------------------------------------------
  1080.  
  1081. #if XtSpecificationRelease >= 6
  1082.  
  1083. // Realize X11R6 session management protocols.
  1084.  
  1085. static void AskSaveSmSessionCB    (Widget w, XtPointer, XtPointer call_data);
  1086. static void ConfirmSaveSmSessionCB(Widget w, XtPointer, XtPointer call_data);
  1087. static void CancelSaveSmSessionCB (Widget w, XtPointer, XtPointer call_data);
  1088.  
  1089. static void AskSmShutdownCB       (Widget w, XtPointer, XtPointer call_data);
  1090. static void ConfirmSmShutdownCB   (Widget w, XtPointer, XtPointer call_data);
  1091. static void CancelSmShutdownCB    (Widget w, XtPointer, XtPointer call_data);
  1092.  
  1093. static void ask(string text, String name,
  1094.         XtCheckpointToken token, Widget w,
  1095.         XtCallbackProc yes, XtCallbackProc no);
  1096.  
  1097. // 1. The user initiates a checkpoint.  Have DDD save its options.
  1098. void SaveSmSessionCB(Widget w, XtPointer, XtPointer call_data)
  1099. {
  1100.     XtCheckpointToken token = XtCheckpointToken(call_data);
  1101.  
  1102.     // Save session
  1103.     const bool interact = (token->interact_style == SmInteractStyleAny);
  1104.     const bool shutdown = token->shutdown;
  1105.  
  1106.     unsigned long flags = SAVE_SESSION | SAVE_GEOMETRY | SAVE_CORE;
  1107.     if (interact)
  1108.     flags |= MAY_INTERACT;
  1109.     if (shutdown)
  1110.     flags |= MAY_KILL;
  1111.  
  1112.     if (!shutdown && saving_options_kills_program(flags))
  1113.     {
  1114.     if (interact)
  1115.         XtAddCallback(w, XtNinteractCallback, AskSaveSmSessionCB, 0);
  1116.     else
  1117.         return;        // Cannot save state right now
  1118.     }
  1119.     else
  1120.     {
  1121.     // Save it
  1122.     token->save_success = save_options(flags);
  1123.     }
  1124.  
  1125.     if (shutdown)
  1126.     {
  1127.     // Prepare for shutdown
  1128.     if (interact)
  1129.         XtAddCallback(w, XtNinteractCallback, AskSmShutdownCB, 0);
  1130.     else
  1131.     {
  1132.         if (!gdb->isReadyWithPrompt())
  1133.         {
  1134.         // GDB is still busy - cannot shutdown right now
  1135.         token->request_cancel = true;
  1136.         }
  1137.     }
  1138.     }
  1139. }
  1140.  
  1141.  
  1142. // Save state
  1143.  
  1144. // 2. Request license to kill the program
  1145. static void AskSaveSmSessionCB(Widget w, XtPointer, XtPointer call_data)
  1146. {
  1147.     XtCheckpointToken token = XtCheckpointToken(call_data);
  1148.  
  1149.     ask("", "kill_to_save_dialog", token, w, 
  1150.     ConfirmSaveSmSessionCB, CancelSaveSmSessionCB);
  1151. }
  1152.  
  1153. // Ok, kill it
  1154. static void ConfirmSaveSmSessionCB(Widget, XtPointer client_data, XtPointer)
  1155. {
  1156.     XtCheckpointToken token = XtCheckpointToken(client_data);
  1157.  
  1158.     unsigned long flags = 
  1159.     SAVE_SESSION | SAVE_GEOMETRY | MAY_KILL | MAY_INTERACT;
  1160.  
  1161.     token->save_success = save_options(flags);
  1162.     XtSessionReturnToken(token);
  1163. }
  1164.  
  1165. // No, don't kill -- save everything else.
  1166. static void CancelSaveSmSessionCB(Widget, XtPointer client_data, XtPointer)
  1167. {
  1168.     XtCheckpointToken token = XtCheckpointToken(client_data);
  1169.  
  1170.     unsigned long flags = SAVE_SESSION | SAVE_GEOMETRY | MAY_INTERACT;
  1171.  
  1172.     token->save_success = save_options(flags);
  1173.     XtSessionReturnToken(token);
  1174. }
  1175.  
  1176.  
  1177. // Shutdown
  1178.  
  1179. // 2. If the checkpoint was part of a shutdown, make sure the
  1180. //     debugged program is killed properly.
  1181. static void AskSmShutdownCB(Widget w, XtPointer, XtPointer call_data)
  1182. {
  1183.     XtCheckpointToken token = XtCheckpointToken(call_data);
  1184.  
  1185.     if (token->cancel_shutdown || token->request_cancel)
  1186.     {
  1187.     XtSessionReturnToken(token);
  1188.     return;
  1189.     }
  1190.  
  1191.     if (gdb == 0 || !gdb->running())
  1192.     {
  1193.     XtSessionReturnToken(token);
  1194.     return;
  1195.     }
  1196.  
  1197.     if (!gdb->isReadyWithPrompt())
  1198.     {
  1199.     // Debugger is still running; request confirmation
  1200.     ask(gdb->title() + " is still busy.  Shutdown anyway (and kill it)?",
  1201.         "quit_dialog", token, w, ConfirmSmShutdownCB, 
  1202.         CancelSmShutdownCB);
  1203.     return;
  1204.     }
  1205.  
  1206.     ProgramInfo info;
  1207.     if (info.running)
  1208.     {
  1209.     // Program is still running; request confirmation
  1210.     ask("The program is running.  Shutdown anyway (and kill it)?",
  1211.         "shutdown_dialog", token, w, ConfirmSmShutdownCB, 
  1212.         CancelSmShutdownCB);
  1213.     return;
  1214.     }
  1215.  
  1216.     // Okay, go ahead.
  1217.     ConfirmSmShutdownCB(w, XtPointer(token), call_data);
  1218. }
  1219.  
  1220. // 3. Confirm or cancel shutdown.
  1221. static void ConfirmSmShutdownCB(Widget, XtPointer client_data, XtPointer)
  1222. {
  1223.     XtCheckpointToken token = XtCheckpointToken(client_data);
  1224.     XtSessionReturnToken(token);
  1225. }
  1226.  
  1227. static void CancelSmShutdownCB(Widget, XtPointer client_data, XtPointer)
  1228. {
  1229.     XtCheckpointToken token = XtCheckpointToken(client_data);
  1230.     token->request_cancel = true;
  1231.     XtSessionReturnToken(token);
  1232. }
  1233.  
  1234. // 4. Let DDD die.
  1235. void ShutdownSmSessionCB(Widget w, XtPointer, XtPointer call_data)
  1236. {
  1237.     if (gdb != 0 && gdb->isReadyWithPrompt())
  1238.     {
  1239.     if (gdb->type() == GDB)
  1240.         gdb_question("set confirm off");
  1241.  
  1242.     DDDExitCB(w, XtPointer(EXIT_SUCCESS), call_data);
  1243.     }
  1244.     else
  1245.     {
  1246.     _DDDExitCB(w, XtPointer(EXIT_SUCCESS), call_data);
  1247.     }
  1248. }
  1249.  
  1250. // Create a confirmation dialog
  1251. static void ask(string text, String name, XtCheckpointToken token, Widget w,
  1252.         XtCallbackProc yes, XtCallbackProc no)
  1253. {
  1254.     // Program is still running; request confirmation
  1255.     Arg args[10];
  1256.     int arg;
  1257.  
  1258.     static Widget dialog = 0;
  1259.     if (dialog)
  1260.     DestroyWhenIdle(dialog);
  1261.  
  1262.     arg = 0;
  1263.     MString msg = rm(text);
  1264.     if (text != "")
  1265.     {
  1266.     XtSetArg(args[arg], XmNmessageString, msg.xmstring()); arg++;
  1267.     }
  1268.     dialog = verify(XmCreateQuestionDialog(find_shell(w), name,
  1269.                        args, arg));
  1270.     Delay::register_shell(dialog);
  1271.     XtAddCallback(dialog, XmNokCallback,     yes, XtPointer(token));
  1272.     XtAddCallback(dialog, XmNcancelCallback, no, XtPointer(token));
  1273.     XtAddCallback(dialog, XmNhelpCallback,   ImmediateHelpCB, 0);
  1274.  
  1275.     manage_and_raise(dialog);
  1276. }
  1277.  
  1278. #else // XtSpecificationRelease < 6
  1279.  
  1280. // X11R5 and earlier: Nothing yet...
  1281. void SaveSmSessionCB(Widget, XtPointer, XtPointer) {}
  1282. void ShutdownSmSessionCB(Widget, XtPointer, XtPointer) {}
  1283.  
  1284. #endif // XtSpecificationRelease < 6
  1285.