void gdbEditSourceCB (Widget w, XtPointer, XtPointer) { string pos = source_view->file_of_cursor(); string file = pos.before(':'); string line = pos.after(':'); if (file == "" || line == "0") { post_error("No source.", "no_source_edit_error", w); return; } StatusDelay delay("Invoking editor for " + quote(file)); string cmd = app_data.edit_command; cmd.gsub("@FILE@", file); cmd.gsub("@LINE@", line); cmd = sh_command(cmd); // Invoke an editor in the background LiterateAgent *edit_agent = new LiterateAgent(XtWidgetToApplicationContext(w), cmd); output_buffer = ""; edit_agent->removeAllHandlers(Died); edit_agent->addHandler(InputEOF, gdbEditDoneHP); edit_agent->addHandler(Died, gdbEditDoneHP); edit_agent->addHandler(Input, gdbEditOutputHP); edit_agent->addHandler(Error, gdbEditOutputHP); edit_agent->start(); }
// Search for remote files and directories, using the command CMD static void searchRemote(Widget fs, XmFileSelectionBoxCallbackStruct *cbs, const _XtString cmd, bool search_dirs) { StatusDelay delay(delay_message); int nitems = 0; int size = 256; XmStringTable items = XmStringTable(XtMalloc(size * sizeof(XmString))); String mask; if (!XmStringGetLtoR(cbs->mask, MSTRING_DEFAULT_CHARSET, &mask)) { delay.outcome = "failed"; return; } String dir; if (!XmStringGetLtoR(cbs->dir, MSTRING_DEFAULT_CHARSET, &dir)) { delay.outcome = "failed"; return; } if (search_dirs) { string extra_dir = string(dir) + "."; items[nitems++] = XmStringCreateLtoR(XMST(extra_dir.chars()), MSTRING_DEFAULT_CHARSET); extra_dir = string(dir) + ".."; items[nitems++] = XmStringCreateLtoR(XMST(extra_dir.chars()), MSTRING_DEFAULT_CHARSET); } string command = cmd; command.gsub("@MASK@", mask); command = sh_command(command); Agent search(command); search.start(); FILE *fp = search.inputfp(); if (fp == 0) { delay.outcome = strerror(errno); return; } char buf[BUFSIZ]; while (fgets(buf, sizeof(buf), fp)) { if (buf[0] && buf[strlen(buf) - 1] == '\n') buf[strlen(buf) - 1] = '\0'; if (nitems >= size) { size += 256; items = XmStringTable(XtRealloc((char *)items, size * sizeof(XmString))); } items[nitems++] = XmStringCreateLtoR(buf, MSTRING_DEFAULT_CHARSET); if (nitems == 1 || nitems % 10 == 0) { std::ostringstream status; status << delay_message << "... (" << nitems << " processed)"; string s(status); set_status(s, true); } } if (search_dirs) { XtVaSetValues(fs, XmNdirListItems, items, XmNdirListItemCount, nitems, XmNdirectoryValid, True, XmNlistUpdated, True, XtPointer(0)); } else { if (nitems > 0) { XtVaSetValues(fs, XmNfileListItems, items, XmNfileListItemCount, nitems, XmNdirSpec, items[0], XmNlistUpdated, True, XtPointer(0)); } else { XtVaSetValues(fs, XmNfileListItems, 0, XmNfileListItemCount, 0, XmNlistUpdated, True, XtPointer(0)); } } }
// Create list of processes static void update_processes(Widget processes, bool keep_selection) { StatusDelay delay("Getting list of processes"); string cmd = sh_command(app_data.ps_command) + " 2>&1"; FILE *fp = popen(cmd.chars(), "r"); if (fp == 0) { delay.outcome = strerror(errno); return; } StringArray all_process_list; int c; string line = ""; bool first_line = true; while ((c = getc(fp)) != EOF) { if (c == '\n') { if (first_line || valid_ps_line(line, app_data.ps_command)) all_process_list += line; #if 0 else std::clog << "Excluded: " << line << "\n"; #endif if (first_line) { // Find first occurrence of `PID' title ps_pid_index = line.index(" PID "); if (ps_pid_index < 0) ps_pid_index = 0; } line = ""; first_line = false; } else { line += c; } } pclose(fp); sortProcesses(all_process_list); DynIntArray pids(all_process_list.size()); // If GDB cannot send a signal to the process, we cannot debug it. // Try a `kill -0' (via GDB, as it may be setuid) and filter out // all processes in the `kill' diagnostic -- that is, all // processes that `kill' could not send a signal. string kill = "kill -0"; if (gdb->has_handler_command()) kill = "/usr/bin/kill -0"; // Bypass built-in SUN DBX command int i; for (i = 0; i < all_process_list.size(); i++) { pids[i] = ps_pid(all_process_list[i]); if (pids[i]) kill += string(" ") + itostring(pids[i]); } #if defined(__sun) // bypass underlying debugger // Fix for Sun: use /usr/bin/kill string kill_result; { std::ostringstream os; kill += " 2>&1"; FILE *fp = popen(kill.chars(), "r"); if (fp != 0) { int c; while ((c = getc(fp)) != EOF) { os << (char)c; } pclose(fp); } kill_result = os; } #else string kill_result = gdb_question(gdb->shell_command(kill)); #endif i = 0; while (i >= 0) { i = kill_result.index(rxint, i); if (i >= 0) { int bad_pid = atoi(kill_result.chars() + i); for (int k = 0; k < all_process_list.size(); k++) { if (pids[k] != 0 && pids[k] == bad_pid) { #if 0 std::clog << "Excluded: " << all_process_list[k] << "\n"; #endif all_process_list[k] = NO_GDB_ANSWER; } } i++; } } StringArray process_list; for (i = 0; i < all_process_list.size(); i++) if (all_process_list[i] != NO_GDB_ANSWER) process_list += all_process_list[i]; // Now set the selection. bool *selected = new bool[process_list.size()]; for (i = 0; i < process_list.size(); i++) selected[i] = false; int pos = -1; if (keep_selection) { // Preserve old selection: each PID selected before will also be // selected after. IntArray selection; getPIDs(processes, selection); for (i = 0; i < selection.size(); i++) { for (int j = 0; j < process_list.size(); j++) if (selection[i] == ps_pid(process_list[j])) { if (pos < 0) pos = j; selected[j] = true; } } } if (pos < 0) { // Create new selection from current file and current pid. ProgramInfo info; // Check for current pid; if found, highlight it. for (i = 0; pos < 0 && i < process_list.size(); i++) { if (info.pid != 0 && ps_pid(process_list[i]) == info.pid) pos = i; } if (pos < 0) { // Not found? Try leftmost occurrence of process base name. string current_base = basename(info.file.chars()); int leftmost = INT_MAX; for (i = 0; i < process_list.size(); i++) { int occurrence = process_list[i].index(current_base); if (occurrence >= 0 && occurrence < leftmost && ps_pid(process_list[i]) > 0) { leftmost = occurrence; pos = i; } } } } if (pos >= 0) selected[pos] = true; setLabelList(processes, process_list.values(), selected, process_list.size(), true, false); if (pos >= 0) ListSetAndSelectPos(processes, pos + 1); delete[] selected; }
// Create a separate tty window; return its name in TTYNAME, its // process id in PID, its terminal type in TERM, and its window id in // WINDOWID. static void launch_separate_tty(string& ttyname, pid_t& pid, string& term, Window& windowid, Widget origin) { // If we're already running, all is done. if (pid > 0 && (remote_gdb() || kill(pid, 0) == 0)) return; string term_command = app_data.term_command; term_command.gsub("@FONT@", make_font(app_data, FixedWidthDDDFont)); static bool canceled; canceled = false; static Widget dialog = 0; if (dialog == 0) { Arg args[10]; Cardinal arg = 0; XtSetArg(args[arg], XmNdialogStyle, XmDIALOG_FULL_APPLICATION_MODAL); arg++; dialog = verify(XmCreateWorkingDialog(find_shell(origin), XMST("launch_tty_dialog"), args, arg)); XtUnmanageChild(XmMessageBoxGetChild(dialog, XmDIALOG_OK_BUTTON)); XtUnmanageChild(XmMessageBoxGetChild(dialog, XmDIALOG_HELP_BUTTON)); XtAddCallback(dialog, XmNcancelCallback, CancelTTYCB, XtPointer(&canceled)); } string base = term_command; if (base.contains(' ')) base = base.before(' '); MString msg = rm("Starting ") + tt(base) + rm("..."); XtVaSetValues(dialog, XmNmessageString, msg.xmstring(), XtPointer(0)); manage_and_raise(dialog); wait_until_mapped(dialog); StatusDelay delay("Starting execution window"); // Fill in defaults ttyname = ""; pid = -1; string command = // Set up a temporary file in TMP. "tmp=${TMPDIR-/tmp}/ddd$$; export tmp; " // Be sure to remove it when exiting... "trap \"rm -f $tmp\" 0; " // ... or being interrupted. "trap 'exit 1' 1 2 15; " // Now execute the xterm command + term_command + // which saves TTY, PID, TERM, and WINDOWID in TMP and goes to // sleep forever. Signal 2 (SIGINT) is blocked for two // reasons: first, we don't want ^C to kill the tty window; // second, later invocations will send us SIGINT to find out // whether we're still alive. " '" "echo `tty` $$ $TERM $WINDOWID >$tmp; " "trap \"\" 2; " "while true; do sleep 3600; done" "' " // The whole thing is redirected and in the background such // that rsh won't wait for us. ">/dev/null </dev/null 2>&1 & " // The main file waits for TMP to be created... "while test ! -s $tmp; do sleep 1; done; " // ...and sends TMP's contents to stdout, where DDD is waiting. "cat $tmp"; if (pid > 0 && remote_gdb()) { // We're already running. Don't start a new tty // if the old one is still running. std::ostringstream os; os << "kill -2 " << pid << " 2>/dev/null" << " || ( " << command << " )"; command = string(os); } command = sh_command(command); { XtAppContext app_context = XtWidgetToApplicationContext(dialog); LiterateAgent tty(app_context, command); string reply = ""; tty.addHandler(Input, GotReplyHP, (void *)&reply); tty.start(); while (!reply.contains('\n') && !canceled && tty.running()) XtAppProcessEvent(app_context, XtIMAll); if (reply.length() > 2) { std::istringstream is(reply.chars()); is >> ttyname >> pid >> term >> windowid; } tty.terminate(); }
GDBAgent *new_gdb(DebuggerType type, const AppData& app_data, XtAppContext app_context, int argc, char *argv[]) { // Build call static string gdb_call = app_data.debugger_command; if (app_data.play_log != 0) { gdb_call += string(" --PLAY ") + app_data.play_log; } else { switch(type) { case GDB: // Do not issue introductiory messages; output full file names. gdb_call += " -q -fullname"; break; case DBX: // Nothing special. (Anyway, every DBX has its own sets of // options, so there is not much we could do here.) break; case JDB: // Nothing special. break; case PERL: // Be sure to invoke the debugger. gdb_call += " -d"; break; case BASH: // Be sure to invoke the debugger. gdb_call += " --debugger"; break; case DBG: // Nothing special? break; case PYDB: // Nothing special. break; case XDB: // Enable line mode. gdb_call += " -L"; break; } } for (int i = 1; i < argc; i++) { string arg = argv[i]; gdb_call += " " + sh_quote(arg); } if (argc <= 1) { if (type == PERL) { // Invoked without args. Add a dummy `eval' arg. gdb_call += " -e 42"; } else if (type == BASH) { gdb_call += " -c ': type \\\"debug *script-name*\\\" to start your script.'"; } } GDBAgent *gdb; if (app_data.debugger_rhost == 0 || app_data.debugger_rhost[0] == '\0') { // Use direct invocation gdb_call = sh_command("exec " + gdb_call); gdb = new GDBAgent(app_context, gdb_call, type); } else { // Use interactive rsh gdb = new GDBAgent(app_context, sh_command(), type); gdb_call = "exec " + _sh_command("exec " + gdb_call, true, true) + "\n"; gdb->addHandler(Input, InvokeGDBFromShellHP, (void *)&gdb_call); } // Set up Agent resources switch (app_data.block_tty_input) { case On: gdb->block_tty_input(true); break; case Off: gdb->block_tty_input(false); break; case Auto: // Leave default setting unchanged break; } // Set up Agent resources switch (app_data.buffer_gdb_output) { case On: gdb->buffer_gdb_output(true); break; case Off: gdb->buffer_gdb_output(false); break; case Auto: // Tie buffering to existence of separate window gdb->buffer_gdb_output(app_data.separate_exec_window); break; } return gdb; }