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 / exectty.C < prev    next >
C/C++ Source or Header  |  1998-12-06  |  23KB  |  948 lines

  1. // $Id: exectty.C,v 1.55.4.1 1998/12/06 15:18:02 zeller Exp $ -*- C++ -*-
  2. // Execution TTY
  3.  
  4. // Copyright (C) 1996-1998 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 exectty_rcsid[] = 
  30.     "$Id: exectty.C,v 1.55.4.1 1998/12/06 15:18:02 zeller Exp $";
  31.  
  32. #ifdef __GNUG__
  33. #pragma implementation
  34. #endif
  35.  
  36. #include "exectty.h"
  37.  
  38. #include "AppData.h"
  39. #include "Command.h"
  40. #include "Delay.h"
  41. #include "LiterateA.h"
  42. #include "MString.h"
  43. #include "SignalB.h"
  44. #include "charsets.h"
  45. #include "config.h"
  46. #include "cook.h"
  47. #include "ddd.h"
  48. #include "disp-read.h"
  49. #include "fonts.h"
  50. #include "logo.h"
  51. #include "post.h"
  52. #include "question.h"
  53. #include "regexps.h"
  54. #include "shell.h"
  55. #include "status.h"
  56. #include "string-fun.h"
  57. #include "verify.h"
  58. #include "windows.h"
  59. #include "wm.h"
  60.  
  61. #include <fstream.h>
  62. #include <signal.h>
  63. #include <unistd.h>
  64.  
  65. #include <X11/X.h>
  66. #include <X11/Xlib.h>
  67.  
  68. #include <Xm/Xm.h>
  69. #include <Xm/MessageB.h>
  70.  
  71.  
  72. extern "C" {
  73. // The GNU termcap declarations should also work for non-GNU termcap
  74. // implementations.  We do not include the system declaration files here
  75. // (i.e. <curses.h> or <term.h>), since these include lots of other
  76. // macros and declarations cluttering our namespace.
  77. #include <termcap/termcap.h>
  78. }
  79.  
  80. #if !HAVE_POPEN_DECL
  81. extern "C" FILE *popen(const char *command, const char *mode);
  82. #endif
  83. #if !HAVE_PCLOSE_DECL
  84. extern "C" int pclose(FILE *stream);
  85. #endif
  86.  
  87. //-----------------------------------------------------------------------------
  88. // Separate tty
  89. //-----------------------------------------------------------------------------
  90.  
  91. // TTY name
  92. static string separate_tty_name = "/dev/tty";
  93.  
  94. // TTY pid (0: not initialized, -1: failed)
  95. static pid_t separate_tty_pid   = 0;
  96.  
  97. // TTY terminal type
  98. static string separate_tty_term  = "dumb";
  99.  
  100. // TTY window
  101. static Window separate_tty_window = 0;
  102.  
  103. // GDB command redirection
  104. static string gdb_redirection = "";
  105.  
  106. // GDB run command
  107. static string gdb_run_command = "";
  108.  
  109. // GDB TTY
  110. string gdb_tty = "";
  111.  
  112. // True if the next 'Starting' line is to be displayed in the separate tty
  113. static bool show_starting_line_in_tty    = false;
  114.  
  115. pid_t exec_tty_pid()     { return separate_tty_pid; }
  116. Window exec_tty_window() { return separate_tty_window; }
  117.  
  118. static void CancelTTYCB(Widget, XtPointer client_data, XtPointer)
  119. {
  120.     bool *flag = (bool *)client_data;
  121.     *flag = true;
  122. }
  123.  
  124. static void GotReplyHP(Agent *, void *client_data, void *call_data)
  125. {
  126.     DataLength *d = (DataLength *)call_data;
  127.     string& reply = *(string *)client_data;
  128.     reply += string(d->data, d->length);
  129. }
  130.  
  131. // Create a separate tty window; return its name in TTYNAME, its
  132. // process id in PID, its terminal type in TERM, and its window id in
  133. // WINDOWID.
  134. static void launch_separate_tty(string& ttyname, pid_t& pid, string& term,
  135.                 Window& windowid, Widget origin)
  136. {
  137.     // If we're already running, all is done.
  138.     if (pid > 0 && (remote_gdb() || kill(pid, 0) == 0))
  139.     return;
  140.  
  141.     string term_command = app_data.term_command;
  142.     term_command.gsub("@FONT@", make_font(app_data, FixedWidthDDDFont));
  143.  
  144.     static bool canceled;
  145.     canceled = false;
  146.  
  147.     static Widget dialog = 0;
  148.     if (dialog == 0)
  149.     {
  150.     Arg args[10];
  151.     Cardinal arg = 0;
  152.     XtSetArg(args[arg], XmNdialogStyle, 
  153.          XmDIALOG_FULL_APPLICATION_MODAL); arg++;
  154.     dialog = verify(XmCreateWorkingDialog(find_shell(origin), 
  155.                           "launch_tty_dialog", args, arg));
  156.     XtUnmanageChild(XmMessageBoxGetChild(dialog, 
  157.                          XmDIALOG_OK_BUTTON));
  158.     XtUnmanageChild(XmMessageBoxGetChild(dialog, 
  159.                          XmDIALOG_HELP_BUTTON));
  160.     XtAddCallback(dialog, XmNcancelCallback, CancelTTYCB,
  161.               XtPointer(&canceled));
  162.     }
  163.  
  164.     string base = term_command;
  165.     if (base.contains(' '))
  166.     base = base.before(' ');
  167.     MString msg = rm("Starting ") + tt(base) + rm("...");
  168.     XtVaSetValues(dialog, XmNmessageString, msg.xmstring(), NULL);
  169.     manage_and_raise(dialog);
  170.     wait_until_mapped(dialog);
  171.  
  172.     StatusDelay delay("Starting execution window");
  173.  
  174.     // Fill in defaults
  175.     ttyname = "";
  176.     pid     = -1;
  177.  
  178.     string command = 
  179.     
  180.     // Set up a temporary file in TMP.
  181.     "tmp=${TMPDIR-/tmp}/ddd$$; export tmp; "
  182.  
  183.     // Be sure to remove it when exiting...
  184.     "trap \"rm -f $tmp\" 0; "
  185.  
  186.     // ... or being interrupted.
  187.     "trap 'exit 1' 1 2 15; "
  188.  
  189.     // Now execute the xterm command
  190.     + term_command +
  191.  
  192.     // which saves TTY, PID, TERM, and WINDOWID in TMP and goes to
  193.     // sleep forever.  Signal 2 (SIGINT) is blocked for two
  194.     // reasons: first, we don't want ^C to kill the tty window;
  195.     // second, later invocations will send us SIGINT to find out
  196.     // whether we're still alive.
  197.     " '"
  198.     "echo `tty` $$ $TERM $WINDOWID >$tmp; "
  199.     "trap \"\" 2; "
  200.     "while true; do sleep 3600; done"
  201.     "' "
  202.  
  203.     // The whole thing is redirected and in the background such
  204.     // that rsh won't wait for us.
  205.     ">/dev/null </dev/null 2>&1 & "
  206.  
  207.     // The main file waits for TMP to be created...
  208.     "while test ! -s $tmp; do sleep 1; done; "
  209.  
  210.     // ...and sends TMP's contents to stdout, where DDD is waiting.
  211.     "cat $tmp";
  212.  
  213.     if (pid > 0 && remote_gdb())
  214.     {
  215.     // We're already running.  Don't start a new tty
  216.     // if the old one is still running.
  217.     ostrstream os;
  218.     os << "kill -2 " << pid << " 2>/dev/null"
  219.        << " || ( " << command << " )";
  220.     command = string(os);
  221.     }
  222.  
  223.     command = sh_command(command);
  224.  
  225.     {
  226.     XtAppContext app_context = XtWidgetToApplicationContext(dialog);
  227.     LiterateAgent tty(app_context, command);
  228.  
  229.     string reply = "";
  230.     tty.addHandler(Input, GotReplyHP, (void *)&reply);
  231.     tty.start();
  232.  
  233.     while (!reply.contains('\n') && !canceled && tty.running())
  234.         XtAppProcessEvent(app_context, XtIMAll);
  235.  
  236.     if (reply.length() > 2)
  237.     {
  238.         istrstream is((const char *)reply);
  239.         is >> ttyname >> pid >> term >> windowid;
  240.     }
  241.  
  242.     tty.terminate();
  243.     }
  244.  
  245.     // Sanity check
  246.     if (ttyname == "" || ttyname[0] != '/')
  247.     pid = -1;
  248.  
  249.     // Waiting is over
  250.     XtUnmanageChild(dialog);
  251.  
  252.     if (pid < 0)
  253.     {
  254.     if (!canceled)
  255.     {
  256.         post_error("The execution window could not be started.", 
  257.                "tty_exec_error", origin);
  258.     }
  259.  
  260.     delay.outcome = (canceled ? "canceled" : "failed");
  261.     }
  262.  
  263.     // Set icon and group leader
  264.     if (windowid)
  265.     {
  266.     wm_set_icon(XtDisplay(command_shell), windowid,
  267.             iconlogo(command_shell), iconmask(command_shell));
  268.     }
  269.  
  270.     // Be sure to be notified when the TTY window is deleted
  271.     if (windowid)
  272.     {
  273.     XSelectInput(XtDisplay(gdb_w), windowid, StructureNotifyMask);
  274.     }
  275. }
  276.  
  277. static void get_args(const string& command, string& base, string& args)
  278. {
  279.     // Find (last) arguments to `run' command
  280.     base = command;
  281.     args = "";
  282.  
  283.     if (command.contains(rxwhite))
  284.     {
  285.     base = command.before(rxwhite);
  286.     args = command.after(rxwhite);
  287.     }
  288.  
  289.     if (args == "" && gdb->type() == GDB)
  290.     {
  291.     args = gdb_question("show args");
  292.  
  293.     // GDB 4.16 issues `Arguments list', GDB 4.17 `Argument list'.  Shhh.
  294.     if (!args.contains("Argument", 0))
  295.     {
  296.         args = "";
  297.     }
  298.     else
  299.     {
  300.         // Strip one pair of enclosing `"' characters
  301.         if (args.contains('"'))
  302.         {
  303.         args = args.after('"');
  304.         if (args.contains('"'))
  305.             args = args.before('"', -1);
  306.         }
  307.     }
  308.     }
  309.  
  310.     strip_leading_space(args);
  311. }
  312.  
  313.  
  314. // Set TERM environment variable, using CMD
  315. static int set_term_env(const string& cmd, const string& term_type,
  316.             Widget origin, bool silent)
  317. {
  318.     string reply = gdb_question(cmd);
  319.     if (reply == NO_GDB_ANSWER)
  320.     {
  321.     if (!silent)
  322.     {
  323.         post_warning(string("Cannot set terminal type to ") 
  324.              + quote(term_type), "tty_type_error", origin);
  325.     }
  326.     }
  327.     else if (reply != "")
  328.     {
  329.     if (!silent)
  330.         post_gdb_message(reply, true, origin);
  331.     return -1;
  332.     }
  333.  
  334.     return 0;
  335. }
  336.  
  337.  
  338. // Set debugger TTY to TTY_NAME and terminal type to TERM_TYPE.
  339. // Use without arguments to silently restore original TTY.
  340. static int gdb_set_tty(string tty_name = "",
  341.                string term_type = "dumb",
  342.                Widget origin = 0)
  343. {
  344.     bool silent = false;
  345.     if (tty_name == "")
  346.     {
  347.     tty_name = gdb->slave_tty();
  348.     silent = true;
  349.     }
  350.  
  351.     switch (gdb->type())
  352.     {
  353.     case GDB:
  354.     {
  355.     if (app_data.use_tty_command && tty_name != gdb_tty)
  356.     {
  357.         // Issue `tty' command to perform redirection
  358.         string tty_cmd = "tty " + tty_name;
  359.         string reply = gdb_question(tty_cmd);
  360.  
  361.         if (reply == NO_GDB_ANSWER)
  362.         {
  363.         if (!silent)
  364.             post_error("GDB I/O error: cannot send tty command", 
  365.                    "tty_command_error", origin);
  366.         return -1;
  367.         }
  368.         else if (reply != "")
  369.         {
  370.         if (!silent)
  371.             post_gdb_message(reply, true, origin);
  372.         return -1;
  373.         }
  374.         else
  375.         gdb_tty = tty_name;
  376.     }
  377.  
  378.     // Set remote terminal type
  379.     int ok = set_term_env("set environment TERM " + term_type, term_type,
  380.                   origin, silent);
  381.     if (!ok)
  382.         return ok;
  383.     }
  384.     break;
  385.  
  386.     case DBX:
  387.     {
  388.     if (app_data.use_tty_command && gdb->has_run_io_command())
  389.     {
  390.         if (tty_name != gdb_tty)
  391.         {
  392.         // Issue `dbxenv run_io pty' and `dbxenv run_pty' commands
  393.         // to perform redirection
  394.         string command = string("dbxenv run_io pty");
  395.         string reply = gdb_question(command);
  396.  
  397.         if (reply == NO_GDB_ANSWER)
  398.         {
  399.             if (!silent)
  400.             post_error("DBX I/O error: cannot send "
  401.                    "dbxenv run_io command",
  402.                    "tty_command_error", origin);
  403.             return -1;
  404.         }
  405.         else if (reply != "")
  406.         {
  407.             if (!silent)
  408.             post_gdb_message(reply, true, origin);
  409.             return -1;
  410.         }
  411.         else
  412.         {
  413.             command = "dbxenv run_pty " + tty_name;
  414.             reply = gdb_question(command);
  415.  
  416.             if (reply == NO_GDB_ANSWER)
  417.             {
  418.             if (!silent)
  419.                 post_error("DBX I/O error: cannot send "
  420.                        "dbxenv run_pty command",
  421.                        "tty_command_error", origin);
  422.             return -1;
  423.             }
  424.             else if (reply != "")
  425.             {
  426.             if (!silent)
  427.                 post_gdb_message(reply, true, origin);
  428.             return -1;
  429.             }
  430.             else
  431.             gdb_tty = tty_name;
  432.         }
  433.         }
  434.     }
  435.  
  436.     // Set remote terminal type
  437.     if (gdb->has_setenv_command())
  438.     {
  439.         int ok = set_term_env("setenv TERM " + term_type, term_type,
  440.                   origin, silent);
  441.         if (!ok)
  442.         return ok;
  443.     }
  444.     }
  445.     break;
  446.  
  447.     case PERL:
  448.     {
  449.     string cmd = "$ENV{'TERM'} = " + quote(term_type, '\'');
  450.     int ok = set_term_env(cmd, term_type, origin, silent);
  451.     if (!ok)
  452.         return ok;
  453.     }
  454.     break;
  455.  
  456.     case XDB:
  457.     case JDB:
  458.     case PYDB:    // for now
  459.     // No way to set environment variables
  460.     break;
  461.     }
  462.  
  463.     return 0;
  464. }
  465.  
  466. static bool has_redirection(const string& args, const string& redirection)
  467. {
  468.     return args.contains(redirection, 0) || args.contains(" " + redirection);
  469. }
  470.  
  471. // Add redirection commands for SH-like shell
  472. static void add_sh_redirection(string& gdb_redirection,
  473.                    const string& tty_name,
  474.                    const string& args)
  475. {
  476.     // sh, bsh, ksh, bash, zsh, sh5, ...
  477.     bool stdout_redirected = has_redirection(args, ">");
  478.     bool stderr_redirected = has_redirection(args, "2>");
  479.  
  480.     if (!stdout_redirected && !stderr_redirected)
  481.     {
  482.     gdb_redirection += " > " + tty_name;
  483.     gdb_redirection += " 2>&1";
  484.     }
  485.     else if (stderr_redirected)
  486.     {
  487.     gdb_redirection += " > " + tty_name;
  488.     }
  489.     else if (stdout_redirected)
  490.     {
  491.     gdb_redirection += " 2> " + tty_name;
  492.     }
  493. }
  494.  
  495. // Add redirection commands for RC shell
  496. static void add_rc_redirection(string& gdb_redirection,
  497.                    const string& tty_name,
  498.                    const string& args)
  499. {
  500.     // rc (from tim@pipex.net)
  501.     bool stdout_redirected = has_redirection(args, ">");
  502.     bool stderr_redirected = 
  503.     has_redirection(args, "2>") || has_redirection(args, ">[2");
  504.  
  505.     if (!stdout_redirected && !stderr_redirected)
  506.     {
  507.     gdb_redirection += " > " + tty_name;
  508.     gdb_redirection += " >[2=1]";
  509.     }
  510.     else if (stderr_redirected)
  511.     {
  512.     gdb_redirection += " > " + tty_name;
  513.     }
  514.     else if (stdout_redirected)
  515.     {
  516.     gdb_redirection += " 2> " + tty_name;
  517.     }
  518. }
  519.  
  520. // Redirect COMMAND to TTY_NAME
  521. static void redirect_process(string& command,
  522.                  const string& tty_name,
  523.                  Widget origin)
  524. {
  525.     // Try `tty' command to perform redirection
  526.     int ret = gdb_set_tty(tty_name, app_data.term_type, origin);
  527.     if (app_data.use_tty_command && ret == 0)
  528.     return;
  529.  
  530.     // Use redirection directives
  531.     string base;
  532.     string args;
  533.     get_args(command, base, args);
  534.  
  535.     if (gdb_redirection != "")
  536.     {
  537.     static string empty;
  538.     args.gsub(gdb_redirection, empty);
  539.     strip_space(args);
  540.     }
  541.  
  542.     gdb_redirection = "";
  543.     if (!has_redirection(args, "<"))
  544.     gdb_redirection = "< " + tty_name;
  545.  
  546.     switch (gdb->type())
  547.     {
  548.     case GDB:
  549.     {
  550.     // In GDB, COMMAND is interpreted by the user's shell.
  551.     static string shell;
  552.  
  553.     if (shell == "")
  554.     {
  555.         // The shell is determined only once, as it cannot change.
  556.         if (remote_gdb())
  557.         {
  558.         string sh = gdb_question(gdb->shell_command("echo $SHELL"));
  559.         if (sh != NO_GDB_ANSWER)
  560.             shell = sh.before('\n');
  561.         }
  562.         else
  563.         {
  564.         char *shell_s = getenv("SHELL");
  565.         if (shell_s == 0)
  566.             shell_s = "/bin/sh";
  567.         shell = shell_s;
  568.         }
  569.     }
  570.  
  571.     if (shell.contains("csh"))
  572.     {
  573.         // csh, tcsh
  574.         if (!has_redirection(args, ">"))
  575.         gdb_redirection += " >&! " + tty_name;
  576.     }
  577.     else if (shell.contains("rc"))
  578.     {
  579.         // rc (from tim@pipex.net)
  580.         add_rc_redirection(gdb_redirection, tty_name, args);
  581.     }
  582.     else if (shell.contains("sh"))
  583.     {
  584.         // sh, bsh, ksh, bash, zsh, sh5, ...
  585.         add_sh_redirection(gdb_redirection, tty_name, args);
  586.     }
  587.     else
  588.     {
  589.         // Unknown shell - play it safe and don't redirect stderr
  590.         if (!has_redirection(args, ">"))
  591.         gdb_redirection += " > " + tty_name;
  592.     }
  593.  
  594.     break;
  595.     }
  596.  
  597.     case DBX:
  598.     // DBX has its own parsing, in several variants.
  599.     if (gdb->has_regs_command())
  600.     {
  601.         // SUN DBX 4.0 has true ksh-style redirection.
  602.         add_sh_redirection(gdb_redirection, tty_name, args);
  603.     }
  604.     else if (gdb->has_print_r_option())
  605.     {
  606.         // SUN DBX 3.x interprets `COMMAND 2>&1' such that COMMAND
  607.         // runs in the background.  Use this kludge instead.
  608.         if (!has_redirection(args, "2>"))
  609.         gdb_redirection += " 2> " + tty_name;
  610.         if (!has_redirection(args, ">"))
  611.         gdb_redirection += " > " + tty_name;
  612.     }
  613.     else if (gdb->has_err_redirection())
  614.     {
  615.         // DEC DBX and AIX DBX use csh-style redirection.
  616.         if (!has_redirection(args, ">"))
  617.         gdb_redirection +=  " >& " + tty_name;
  618.     }
  619.     else
  620.     {
  621.         // SUN DBX 1.x does not allow to redirect stderr.  Play it safe.
  622.         if (!has_redirection(args, ">"))
  623.         gdb_redirection += " > " + tty_name;
  624.     }
  625.     break;
  626.  
  627.     case XDB:
  628.     // XDB uses ksh-style redirection.
  629.     add_sh_redirection(gdb_redirection, tty_name, args);
  630.     break;
  631.  
  632.     case JDB:
  633.     break;            // No redirection in JDB
  634.  
  635.     case PYDB:
  636.     break;            // No redirection in PYDB (for now)
  637.  
  638.     case PERL:
  639.     break;            // No redirection in Perl (for now)
  640.     }
  641.  
  642.     string new_args;
  643.     if (gdb_redirection != "" && !has_redirection(args, gdb_redirection))
  644.     {
  645.     if (args == "")
  646.         new_args = gdb_redirection;
  647.     else
  648.         new_args = gdb_redirection + " " + args;
  649.     }
  650.  
  651.     if (gdb_redirection != "")
  652.     gdb_out_ignore = " " + gdb_redirection;
  653.  
  654.     command = base + " " + new_args;
  655. }
  656.  
  657. static void unredirect_reply(const string& answer, void *)
  658. {
  659.     post_gdb_message(answer);
  660. }
  661.  
  662. // Restore original redirection
  663. static void unredirect_process(string& command,
  664.                    Widget origin = 0)
  665. {
  666.     if (gdb_redirection != "")
  667.     {
  668.     // Disable output redirection upon next run
  669.     string base;
  670.     string args;
  671.     get_args(command, base, args);
  672.     if (has_redirection(args, gdb_redirection) && gdb->type() == GDB)
  673.     {
  674.         static string empty;
  675.         args.gsub(gdb_redirection, empty);
  676.         strip_space(args);
  677.         gdb_command("set args " + args, origin, unredirect_reply);
  678.     }
  679.     }
  680.  
  681.     // Restore original tty
  682.     gdb_set_tty();
  683.  
  684.     gdb_redirection = "";
  685.     gdb_out_ignore  = "";
  686. }
  687.  
  688.  
  689. static inline void addcap(string& s, const char *cap, char *& b)
  690. {
  691.     const char *str = tgetstr(cap, &b);
  692.     if (str)
  693.     s += str;
  694. }
  695.  
  696. // Initialize execution TTY with an appropriate escape sequence
  697. static void initialize_tty(const string& tty_name, const string& tty_term)
  698. {
  699.     StatusDelay delay("Initializing execution window");
  700.  
  701. #ifdef SIGTTOU
  702.     // Christopher Vickery <vickery@babbage.cs.qc.edu> says: On SunOS
  703.     // 5.5, if I run ddd in the background (using the tcsh shell), ddd
  704.     // cannot complete initialization of the execution window because
  705.     // it tries to write to stdout, which causes it to block until the
  706.     // job is returned to the foreground.  Nothing actually appears in
  707.     // the terminal window when the ddd job is put in the foreground,
  708.     // but the execution window finishes initializing OK.
  709.  
  710.     // Solution: block SIGTTOU signals during this function.
  711.     SignalBlocker sb(SIGTTOU);
  712. #endif
  713.  
  714.     // Send initialization sequence
  715.     char buffer[2048];
  716.     string init;
  717.  
  718.     int success = tgetent(buffer, tty_term);
  719.     if (success > 0)
  720.     {
  721.     char caps[2048];
  722.     char *b = caps;
  723.  
  724.     addcap(init, "rs", b);    // Reset from strange mode
  725.     addcap(init, "is", b);    // Ordinary reset
  726.     addcap(init, "cl", b);    // Clear screen
  727.     }
  728.  
  729.     if (remote_gdb())
  730.     {
  731.     string command = sh_command("cat > " + tty_name);
  732.     FILE *fp = popen(command, "w");
  733.     if (fp != NULL)
  734.     {
  735.         fwrite((char *)init, init.length(), sizeof(char), fp);
  736.         pclose(fp);
  737.     }
  738.     }
  739.     else
  740.     {
  741.     ofstream tty(tty_name);
  742.     tty << init;
  743.     }
  744. }
  745.  
  746. // Set the title of TTY_WINDOW to MESSAGE
  747. static void set_tty_title(string message, Window tty_window)
  748. {
  749.     string init = "";
  750.  
  751.     string title = DDD_NAME ": Execution Window";
  752.     string icon  = title;
  753.  
  754.     message = message.after(": ");
  755.     static string empty;
  756.     if (gdb_out_ignore != "")
  757.     message.gsub(gdb_out_ignore, empty);
  758.  
  759.     string program = message;
  760.     if (program.contains(' '))
  761.     program = program.before(' ');
  762.  
  763.     if (program != "")
  764.     {
  765.     string program_base = program;
  766.     if (program_base.contains('/'))
  767.         program_base = program_base.after('/', -1);
  768.  
  769.     title = DDD_NAME ": " + message;
  770.     icon  = DDD_NAME ": " + program_base;
  771.     }
  772.  
  773.     if (tty_window)
  774.     wm_set_name(XtDisplay(command_shell), tty_window,
  775.             title, icon);
  776. }
  777.  
  778. // Handle rerun
  779. static void handle_rerun(string& command)
  780. {
  781.     if (!gdb->has_rerun_command())
  782.     return;
  783.     if (!is_running_cmd(command, gdb))
  784.     return;
  785.  
  786.     static string last_args = "";
  787.  
  788.     string base;
  789.     string args;
  790.     get_args(command, base, args);
  791.     bool rerun = base.contains("re", 0);
  792.  
  793.     if (rerun && !gdb->rerun_clears_args() && args == "")
  794.     {
  795.     // `rerun' without args - Use last arguments
  796.     command = base + " " + last_args;
  797.     }
  798.     else if (!rerun && gdb->rerun_clears_args() && args == "")
  799.     {
  800.     // `run' without args - Use last arguments
  801.     command = base + " " + last_args;
  802.     }
  803.     else
  804.     {
  805.     // Set last arguments
  806.     last_args = args;
  807.     }
  808. }
  809.  
  810. // Create TTY if required
  811. void handle_running_commands(string& command, Widget origin)
  812. {
  813.     // Make sure we see control messages such as `Starting program'
  814.     if (is_running_cmd(command, gdb))
  815.     show_next_line_in_status = true;
  816.  
  817.     if (is_run_cmd(command))
  818.     startup_exec_tty(command, origin);
  819. }
  820.  
  821. // Raise execution TTY.
  822. void startup_exec_tty()
  823. {
  824.     string dummy = "";
  825.     startup_exec_tty(dummy);
  826. }
  827.  
  828. // Raise execution TTY with command COMMAND.
  829. void startup_exec_tty(string& command, Widget origin)
  830. {
  831.     handle_rerun(command);
  832.  
  833.     bool started = false;
  834.  
  835.     if (app_data.separate_exec_window 
  836.     && separate_tty_pid >= 0
  837.     && gdb->isReadyWithPrompt()
  838.     && gdb->has_redirection())
  839.     {
  840.     // Launch separate tty if not running
  841.     launch_separate_tty(separate_tty_name, 
  842.                 separate_tty_pid,
  843.                 separate_tty_term,
  844.                 separate_tty_window,
  845.                 origin);
  846.  
  847.     if (separate_tty_pid >= 0)
  848.     {
  849.         // Initialize tty
  850.         initialize_tty(separate_tty_name, separate_tty_term);
  851.  
  852.         // Set title from `starting program...' message
  853.         show_starting_line_in_tty = true;
  854.  
  855.         // Tell GDB to redirect process I/O to this tty
  856.         redirect_process(command, separate_tty_name, origin);
  857.  
  858.         // Reflect setting in options
  859.         app_data.separate_exec_window = True;
  860.         update_options();
  861.  
  862.         started = true;
  863.     }
  864.     }
  865.  
  866.     if (!started)
  867.     {
  868.     // Close running tty
  869.     kill_exec_tty();
  870.  
  871.     // Tell GDB not to redirect its process I/O
  872.     unredirect_process(command, origin);
  873.     }
  874.  
  875.     if (command != "")
  876.     gdb_run_command = command;
  877. }
  878.  
  879. // Set TTY title to TEXT
  880. void set_tty_from_gdb(const string& text)
  881. {
  882.     if (private_gdb_input)
  883.     return;
  884.     if (!show_starting_line_in_tty)
  885.     return;
  886.     if (!text.contains("Starting program") && !text.contains("Running:"))
  887.     return;
  888.  
  889.     show_starting_line_in_tty = false;
  890.  
  891.     if (separate_tty_pid <= 0)
  892.     return;
  893.  
  894.     set_tty_title(text, separate_tty_window);
  895. }
  896.  
  897. // Some cleanup actions...
  898. void kill_exec_tty(bool killed)
  899. {
  900.     if (separate_tty_pid > 0)
  901.     {
  902.     StatusDelay delay(killed ?
  903.               "Execution window has been closed" :
  904.               "Closing execution window");
  905.  
  906.     if (remote_gdb())
  907.     {
  908.         ostrstream os;
  909.         os << "kill -" << SIGHUP << " " << separate_tty_pid 
  910.            << " >/dev/null </dev/null 2>&1 &";
  911.         Agent agent(sh_command(string(os)));
  912.         agent.start();
  913.         agent.wait();
  914.     }
  915.     else
  916.     {
  917.         kill(separate_tty_pid, SIGHUP);
  918.     }
  919.     }
  920.  
  921.     separate_tty_pid    = 0;
  922.     separate_tty_window = 0;
  923. }
  924.  
  925. // Verify if execution tty is still running
  926. void exec_tty_running()
  927. {
  928.     if (separate_tty_window)
  929.     {
  930.     XEvent event;
  931.  
  932.     if (XCheckTypedWindowEvent(XtDisplay(gdb_w), separate_tty_window,
  933.                    DestroyNotify, &event))
  934.     {
  935.         // TTY window has been killed - kill process as well
  936.         kill_exec_tty(true);
  937.  
  938.         // Restore original TTY for the time being
  939.         unredirect_process(gdb_run_command);
  940.  
  941.         // If the user closed the TTY window, s/he probably has a
  942.         // reason for doing so.  Reflect change in options.
  943.         app_data.separate_exec_window = False;
  944.         update_options();
  945.     }
  946.     }
  947. }
  948.