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 / file.C < prev    next >
Encoding:
C/C++ Source or Header  |  1998-11-26  |  41.5 KB  |  1,780 lines

  1. // $Id: file.C,v 1.69 1998/11/26 13:28:43 zeller Exp $ -*- C++ -*-
  2. // DDD file functions
  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 file_rcsid[] = 
  30.     "$Id: file.C,v 1.69 1998/11/26 13:28:43 zeller Exp $";
  31.  
  32. #ifdef __GNUG__
  33. #pragma implementation
  34. #pragma implementation "DynArray"
  35. #pragma implementation "VarArray"
  36. #endif
  37.  
  38. #include "file.h"
  39.  
  40. #include "AppData.h"
  41. #include "GDBAgent.h"
  42. #include "MString.h"
  43. #include "Delay.h"
  44. #include "DestroyCB.h"
  45. #include "ExitCB.h"
  46. #include "HelpCB.h"
  47. #include "SmartC.h"
  48. #include "SourceView.h"
  49. #include "VarArray.h"
  50. #include "Command.h"
  51. #include "cook.h"
  52. #include "ddd.h"
  53. #include "filetype.h"
  54. #include "glob.h"
  55. #include "history.h"
  56. #include "java.h"
  57. #include "mydialogs.h"
  58. #include "post.h"
  59. #include "question.h"
  60. #include "regexps.h"
  61. #include "shell.h"
  62. #include "strclass.h"
  63. #include "status.h"
  64. #include "string-fun.h"
  65. #include "uniquify.h"
  66. #include "verify.h"
  67. #include "wm.h"
  68.  
  69. #include <limits.h>
  70. #include <string.h>        // strerror()
  71. #include <errno.h>
  72.  
  73. #include <Xm/Xm.h>
  74. #include <Xm/FileSB.h>
  75. #include <Xm/List.h>
  76. #include <Xm/SelectioB.h>
  77. #include <Xm/MessageB.h>
  78. #include <Xm/Text.h>
  79. #include <Xm/RowColumn.h>
  80. #include <Xm/TextF.h>
  81. #include <Xm/Label.h>
  82. #include <Xm/PushB.h>
  83.  
  84. // ANSI C++ doesn't like the XtIsRealized() macro
  85. #ifdef XtIsRealized
  86. #undef XtIsRealized
  87. #endif
  88.  
  89. #if !HAVE_POPEN_DECL
  90. extern "C" FILE *popen(const char *command, const char *mode);
  91. #endif
  92. #if !HAVE_PCLOSE_DECL
  93. extern "C" int pclose(FILE *stream);
  94. #endif
  95.  
  96.  
  97. //-----------------------------------------------------------------------------
  98. // Helpers
  99. //-----------------------------------------------------------------------------
  100.  
  101. // Don't rely on libiberty basename() because we don't want to depend
  102. // on libiberty include files
  103. static const char *file_basename(const char *name)
  104. {
  105.     const char *base = name;
  106.  
  107.     while (*name)
  108.     {
  109.     if (*name++ == '/')
  110.         base = name;
  111.     }
  112.  
  113.     return base;
  114. }
  115.  
  116. #define basename file_basename
  117.  
  118.  
  119. //-----------------------------------------------------------------------------
  120. // Opening files
  121. //-----------------------------------------------------------------------------
  122.  
  123. typedef void (*FileSearchProc)(Widget fs, 
  124.                    XmFileSelectionBoxCallbackStruct *cbs);
  125.  
  126. static WidgetArray file_filters;
  127. static WidgetArray file_dialogs;
  128.  
  129. static string current_file_filter = "";
  130.  
  131. // Make sure that every change in one filter is reflected in all others
  132. static void SyncFiltersCB(Widget dialog, XtPointer, XtPointer)
  133. {
  134.     static bool entered = false;
  135.  
  136.     if (entered)
  137.     return;
  138.  
  139.     entered = true;
  140.  
  141.     // clog << "widget = " << longName(text) << "\n";
  142.  
  143.     while (dialog != 0 && !XmIsFileSelectionBox(dialog))
  144.     dialog = XtParent(dialog);
  145.     
  146.     // clog << "dialog = " << longName(dialog) << "\n";
  147.  
  148.     Widget text = XmFileSelectionBoxGetChild(dialog, XmDIALOG_FILTER_TEXT);
  149.     String _current_file_filter = XmTextGetString(text);
  150.     current_file_filter = _current_file_filter;
  151.     XtFree(_current_file_filter);
  152.  
  153.     for (int i = 0; i < file_filters.size(); i++)
  154.     {
  155.     if (file_dialogs[i] != dialog)
  156.     {
  157.         // clog << "other dialog = " << longName(file_dialogs[i]) << "\n";
  158.         XmTextSetString(file_filters[i], current_file_filter);
  159.     }
  160.     }
  161.  
  162.     entered = false;
  163. }
  164.  
  165. // Make sure that every new filter call is performed in all other
  166. // dialogs as well
  167. static void FilterAllCB(Widget dialog, XtPointer client_data, 
  168.             XtPointer call_data)
  169. {
  170.     SyncFiltersCB(dialog, client_data, call_data);
  171.  
  172.     // clog << "widget = " << longName(dialog) << "\n";
  173.  
  174.     while (dialog != 0 && !XmIsFileSelectionBox(dialog))
  175.     dialog = XtParent(dialog);
  176.     
  177.     // clog << "dialog = " << longName(dialog) << "\n";
  178.  
  179.     for (int i = 0; i < file_dialogs.size(); i++)
  180.     {
  181.     if (file_dialogs[i] != dialog)
  182.     {
  183.         // clog << "other dialog = " << longName(file_dialogs[i]) << "\n";
  184.         XmFileSelectionDoSearch(file_dialogs[i], NULL);
  185.     }
  186.     }
  187. }
  188.  
  189. static void ClearStatusCB(Widget, XtPointer, XtPointer)
  190. {
  191.     set_status("");
  192. }
  193.  
  194. // Create a file dialog NAME with DO_SEARCH_FILES and DO_SEARCH_DIRS
  195. // as search procedures for files and directories, respectively, and
  196. // OK_CALLBACK as the procedure called when a file is selected.
  197. static Widget file_dialog(Widget w, const string& name,
  198.               FileSearchProc do_search_files = 0,
  199.               FileSearchProc do_search_dirs  = 0,
  200.               XtCallbackProc ok_callback     = 0)
  201. {
  202.     Delay delay(w);
  203.  
  204.     Arg args[10];
  205.     int arg = 0;
  206.  
  207.     string pwd;
  208.  
  209.     arg = 0;
  210.     if (do_search_files)
  211.     {
  212.     XtSetArg(args[arg], XmNfileSearchProc, do_search_files); arg++;
  213.     }
  214.     if (do_search_dirs)
  215.     {
  216.     XtSetArg(args[arg], XmNdirSearchProc, do_search_dirs); arg++;
  217.     }
  218.  
  219.     if (remote_gdb())
  220.     {
  221.     static MString xmpwd;
  222.     xmpwd = source_view->pwd();
  223.     XtSetArg(args[arg], XmNdirectory, xmpwd.xmstring()); arg++;
  224.     }
  225.  
  226.     Widget dialog = 
  227.     verify(XmCreateFileSelectionDialog(w, name, args, arg));
  228.     Delay::register_shell(dialog);
  229.  
  230.     if (ok_callback != 0)
  231.     XtAddCallback(dialog, XmNokCallback,     ok_callback, 0);
  232.  
  233.     XtAddCallback(dialog, XmNcancelCallback, UnmanageThisCB, 
  234.           XtPointer(dialog));
  235.     XtAddCallback(dialog, XmNhelpCallback,   ImmediateHelpCB, 0);
  236.  
  237.     Widget filter = XmFileSelectionBoxGetChild(dialog, XmDIALOG_FILTER_TEXT);
  238.     file_filters += filter;
  239.     if (current_file_filter != "")
  240.     XmTextSetString(filter, current_file_filter);
  241.     XtAddCallback(filter, XmNvalueChangedCallback, SyncFiltersCB, 0);
  242.  
  243.     Widget filter_button = 
  244.     XmFileSelectionBoxGetChild(dialog, XmDIALOG_APPLY_BUTTON);
  245.     XtAddCallback(filter_button, XmNactivateCallback, FilterAllCB, 0);
  246.     XtAddCallback(dialog, XmNunmapCallback, ClearStatusCB, 0);
  247.  
  248.     file_dialogs += dialog;
  249.  
  250.     return dialog;
  251. }
  252.  
  253. // Create various file dialogs
  254. static Widget create_file_dialog(Widget w, String name,
  255.                  FileSearchProc searchRemoteFiles       = 0,
  256.                  FileSearchProc searchRemoteDirectories = 0,
  257.                  FileSearchProc searchLocalFiles        = 0,
  258.                  FileSearchProc searchLocalDirectories  = 0,
  259.                  XtCallbackProc openDone = 0)
  260. {                 
  261.     if (remote_gdb())
  262.     return file_dialog(find_shell(w), name,
  263.                searchRemoteFiles, searchRemoteDirectories, 
  264.                openDone);
  265.     else if (app_data.filter_files)
  266.     return file_dialog(find_shell(w), name,
  267.                searchLocalFiles, searchLocalDirectories, 
  268.                openDone);
  269.     else
  270.     return file_dialog(find_shell(w), name, 0, 0, openDone);
  271. }
  272.  
  273.  
  274. // Synchronize file dialogs with current directory
  275. void process_cd(string pwd)
  276. {
  277.     current_file_filter = pwd + "/*";
  278.  
  279.     for (int i = 0; i < file_filters.size(); i++)
  280.     {
  281.     if (file_filters[i] != 0)
  282.     {
  283.         XmTextSetString(file_filters[i], current_file_filter);
  284.         break;
  285.     }
  286.     }
  287. }
  288.  
  289. static char delay_message[] = "Filtering files";
  290.  
  291. // Search for remote files and directories, using the command CMD
  292. static void searchRemote(Widget fs,
  293.              XmFileSelectionBoxCallbackStruct *cbs,
  294.              String cmd,
  295.              bool search_dirs)
  296. {
  297.     StatusDelay delay(delay_message);
  298.  
  299.     int nitems = 0;
  300.     int size = 256;
  301.     XmStringTable items = 
  302.     XmStringTable(XtMalloc(size * sizeof(XmString)));
  303.  
  304.     String mask;
  305.     if (!XmStringGetLtoR(cbs->mask, MSTRING_DEFAULT_CHARSET, &mask))
  306.     {
  307.     delay.outcome = "failed";
  308.     return;
  309.     }
  310.     String dir;
  311.     if (!XmStringGetLtoR(cbs->dir, MSTRING_DEFAULT_CHARSET, &dir))
  312.     {
  313.     delay.outcome = "failed";
  314.     return;
  315.     }
  316.  
  317.     if (search_dirs)
  318.     {
  319.     string extra_dir = string(dir) + ".";
  320.     items[nitems++] = 
  321.         XmStringCreateLtoR(extra_dir, MSTRING_DEFAULT_CHARSET);
  322.     extra_dir = string(dir) + "..";
  323.     items[nitems++] = 
  324.         XmStringCreateLtoR(extra_dir, MSTRING_DEFAULT_CHARSET);
  325.     }
  326.  
  327.     string command = cmd;
  328.     command.gsub("@MASK@", mask);
  329.     command = sh_command(command);
  330.  
  331.     Agent search(command);
  332.     search.start();
  333.  
  334.     FILE *fp = search.inputfp();
  335.     if (fp == 0)
  336.     {
  337.     delay.outcome = strerror(errno);
  338.     return;
  339.     }
  340.  
  341.     char buf[BUFSIZ];
  342.     while (fgets(buf, sizeof(buf), fp))
  343.     {
  344.     if (buf[0] && buf[strlen(buf) - 1] == '\n')
  345.         buf[strlen(buf) - 1] = '\0';
  346.  
  347.     if (nitems >= size)
  348.     {
  349.         size += 256;
  350.         items = XmStringTable(XtRealloc((char *)items,
  351.                         size * sizeof(XmString)));
  352.     }
  353.         
  354.     items[nitems++] = XmStringCreateLtoR(buf, MSTRING_DEFAULT_CHARSET);
  355.  
  356.     if (nitems == 1 || nitems % 10 == 0)
  357.     {
  358.         ostrstream status;
  359.         status << delay_message << "... ("
  360.            << nitems << " processed)";
  361.         string s(status);
  362.         set_status(s, true);
  363.     }
  364.     }
  365.  
  366.     if (search_dirs)
  367.     {
  368.     XtVaSetValues(fs,
  369.               XmNdirListItems,     items,
  370.               XmNdirListItemCount, nitems,
  371.               XmNdirectoryValid,   True,
  372.               XmNlistUpdated,      True,
  373.               NULL);
  374.     }
  375.     else
  376.     {
  377.     if (nitems > 0)
  378.     {
  379.         XtVaSetValues(fs,
  380.               XmNfileListItems,     items,
  381.               XmNfileListItemCount, nitems,
  382.               XmNdirSpec,           items[0],
  383.               XmNlistUpdated,       True,
  384.               NULL);
  385.     }
  386.     else
  387.     {
  388.         XtVaSetValues(fs,
  389.               XmNfileListItems,     0,
  390.               XmNfileListItemCount, 0,
  391.               XmNlistUpdated,       True,
  392.               NULL);
  393.     }
  394.     }
  395. }
  396.  
  397.  
  398. static void searchRemoteExecFiles(Widget fs,
  399.                   XmFileSelectionBoxCallbackStruct *cbs)
  400. {
  401.     searchRemote(fs, cbs, app_data.list_exec_command, false);
  402. }
  403.  
  404. static void searchRemoteCoreFiles(Widget fs,
  405.                   XmFileSelectionBoxCallbackStruct *cbs)
  406. {
  407.     searchRemote(fs, cbs, app_data.list_core_command, false);
  408. }
  409.  
  410. static void searchRemoteSourceFiles(Widget fs,
  411.                     XmFileSelectionBoxCallbackStruct *cbs)
  412. {
  413.     searchRemote(fs, cbs, app_data.list_source_command, false);
  414. }
  415.  
  416. static void searchRemoteDirectories(Widget fs,
  417.                     XmFileSelectionBoxCallbackStruct *cbs)
  418. {
  419.     searchRemote(fs, cbs, app_data.list_dir_command, true);
  420. }
  421.  
  422. // Search for local files and directories, using the predicate IS_OKAY
  423. static void searchLocal(Widget fs,
  424.             XmFileSelectionBoxCallbackStruct *cbs,
  425.             bool is_okay(const string& file_name))
  426. {
  427.     String mask;
  428.     if (!XmStringGetLtoR(cbs->mask, MSTRING_DEFAULT_CHARSET, &mask))
  429.     return;
  430.  
  431.     char **files = glob_filename(mask);
  432.     if (files == (char **)0)
  433.     {
  434.     cerr << mask << ": glob failed\n";
  435.     }
  436.     else if (files == (char **)-1)
  437.     {
  438.     post_error(string(mask) + ": " + strerror(errno));
  439.     }
  440.     else
  441.     {
  442.     StatusDelay delay(delay_message);
  443.  
  444.     int count;
  445.     for (count = 0; files[count] != 0; count++)
  446.         ;
  447.     smart_sort(files, count);
  448.  
  449.     XmStringTable items = 
  450.         XmStringTable(XtMalloc(count * sizeof(XmString)));
  451.  
  452.     int nitems = 0;
  453.     for (int i = 0; files[i] != 0; i++)
  454.     {
  455.         if (is_okay(files[i]))
  456.         items[nitems++] = XmStringCreateLtoR(files[i], 
  457.                              MSTRING_DEFAULT_CHARSET);
  458.         free(files[i]);
  459.  
  460.         int percent     = (i * 100) / count;
  461.         int old_percent = ((i - 1) * 100) / count;
  462.         if (percent % 10 == 0 && old_percent % 10 != 0)
  463.         {
  464.         ostrstream status;
  465.         status << delay_message << "... ("
  466.                << percent << "% processed)";
  467.         string s(status);
  468.         set_status(s, true);
  469.         }
  470.     }
  471.     free((char *)files);
  472.  
  473.     if (nitems > 0)
  474.     {
  475.         XtVaSetValues(fs,
  476.               XmNfileListItems,     items,
  477.               XmNfileListItemCount, nitems,
  478.               XmNdirSpec,           items[0],
  479.               XmNlistUpdated,       True,
  480.               NULL);
  481.  
  482.         XtFree((char *)items);
  483.         return;
  484.     }
  485.     }
  486.  
  487.     // Error or nothing found 
  488.     XtVaSetValues(fs,
  489.           XmNfileListItems,     0,
  490.           XmNfileListItemCount, 0,
  491.           XmNlistUpdated,       True,
  492.           NULL);
  493. }
  494.  
  495. static void searchLocalExecFiles(Widget fs,
  496.                  XmFileSelectionBoxCallbackStruct *cbs)
  497. {
  498.     switch (gdb->type())
  499.     {
  500.     case PYDB:
  501.     searchLocal(fs, cbs, is_python_file);
  502.     break;
  503.  
  504.     case PERL:
  505.     searchLocal(fs, cbs, is_perl_file);
  506.     break;
  507.  
  508.     default:
  509.     searchLocal(fs, cbs, is_exec_file);
  510.     }
  511. }
  512.  
  513. static void searchLocalCoreFiles(Widget fs,
  514.                  XmFileSelectionBoxCallbackStruct *cbs)
  515. {
  516.     searchLocal(fs, cbs, is_core_file);
  517. }
  518.  
  519. static void searchLocalSourceFiles(Widget fs,
  520.                    XmFileSelectionBoxCallbackStruct *cbs)
  521. {
  522.     switch (gdb->type())
  523.     {
  524.     case PYDB:
  525.     searchLocal(fs, cbs, is_python_file);
  526.     break;
  527.  
  528.     case PERL:
  529.     searchLocal(fs, cbs, is_perl_file);
  530.     break;
  531.  
  532.     default:
  533.     searchLocal(fs, cbs, is_source_file);
  534.     }
  535. }
  536.  
  537. // Get the file name from the file selection box W
  538. string get_file(Widget w, XtPointer, XtPointer call_data)
  539. {
  540.     XmFileSelectionBoxCallbackStruct *cbs = 
  541.     (XmFileSelectionBoxCallbackStruct *)call_data;
  542.  
  543.     String s;
  544.     if (!XmStringGetLtoR(cbs->value, MSTRING_DEFAULT_CHARSET, &s))
  545.     return NO_GDB_ANSWER;
  546.  
  547.     string filename = s;
  548.     XtFree(s);
  549.  
  550.     if (filename == "" || filename[0] != '/')
  551.     {
  552.     String dir;
  553.     if (!XmStringGetLtoR(cbs->dir, MSTRING_DEFAULT_CHARSET, &dir))
  554.         return NO_GDB_ANSWER;
  555.  
  556.     filename = string(dir) + "/" + filename;
  557.     XtFree(dir);
  558.     }
  559.  
  560.     if (is_directory(filename))
  561.     {
  562.     MString filter(filename);
  563.     XmFileSelectionDoSearch(w, filter.xmstring());
  564.     return "";
  565.     }
  566.  
  567.     return filename;
  568. }
  569.  
  570.  
  571.  
  572. //-----------------------------------------------------------------------------
  573. // OK pressed
  574. //-----------------------------------------------------------------------------
  575.  
  576. static void open_file(const string& filename)
  577. {
  578.     if (gdb->type() == GDB)
  579.     {
  580.     // GDB does not always detach processes upon opening new
  581.     // files, so we do it explicitly
  582.     ProgramInfo info;
  583.         if (info.attached)
  584.         gdb_command("detach");
  585.     }
  586.  
  587.     string cmd = gdb->debug_command(filename);
  588.  
  589.     if (gdb->type() == PERL)
  590.     cmd.gsub("perl", string(app_data.debugger_command));
  591.  
  592.     gdb_command(cmd);
  593. }
  594.  
  595. // OK pressed in `Open File'
  596. static void openFileDone(Widget w, XtPointer client_data, XtPointer call_data)
  597. {
  598.     string filename = get_file(w, client_data, call_data);
  599.     if (filename == "")
  600.     return;
  601.  
  602.     XtUnmanageChild(w);
  603.  
  604.     if (filename == NO_GDB_ANSWER)
  605.     return;
  606.  
  607.     open_file(filename);
  608. }
  609.  
  610.  
  611. // OK pressed in `Open Core'
  612. static void openCoreDone(Widget w, XtPointer client_data, XtPointer call_data)
  613. {
  614.     string corefile = get_file(w, client_data, call_data);
  615.     if (corefile == "")
  616.     return;
  617.  
  618.     ProgramInfo info;
  619.  
  620.     XtUnmanageChild(w);
  621.  
  622.     if (corefile != NO_GDB_ANSWER)
  623.     {
  624.     switch(gdb->type())
  625.     {
  626.     case GDB:
  627.         gdb_command("core-file " + corefile);
  628.         break;
  629.  
  630.     case DBX:
  631.         if (info.file != NO_GDB_ANSWER && info.file != "")
  632.         gdb_command(gdb->debug_command(info.file) + " " + info.core);
  633.         else
  634.         post_error("No program.", "no_program", w);
  635.         break;
  636.  
  637.     case XDB:
  638.     case JDB:
  639.     case PYDB:
  640.     case PERL:
  641.         break;        // FIXME
  642.     }
  643.     }
  644. }
  645.  
  646. // OK pressed in `Open Source'
  647. static void openSourceDone(Widget w, XtPointer client_data, 
  648.                XtPointer call_data)
  649. {
  650.     string filename = get_file(w, client_data, call_data);
  651.     if (filename == "")
  652.     return;
  653.  
  654.     XtUnmanageChild(w);
  655.     set_status("");
  656.  
  657.     // For PYDB, issue a 'file filename' command
  658.     if (gdb->type() == PYDB)
  659.     gdb_command(gdb->debug_command(filename));
  660.  
  661.     if (filename != NO_GDB_ANSWER)
  662.     source_view->read_file(filename);
  663. }
  664.  
  665.  
  666. //-----------------------------------------------------------------------------
  667. // Program Info
  668. //-----------------------------------------------------------------------------
  669.  
  670. // Get information on current debuggee
  671. ProgramInfo::ProgramInfo()
  672.     : file(NO_GDB_ANSWER),
  673.       core(NO_GDB_ANSWER),
  674.       pid(0),
  675.       attached(false),
  676.       running(false),
  677.       state()
  678. {
  679.     if (source_view->have_exec_pos())
  680.     {
  681.     state = "has stopped";
  682.     running = true;
  683.     }
  684.     else
  685.     {
  686.     state = "is not being run";
  687.     running = false;
  688.     }
  689.  
  690.     switch(gdb->type())
  691.     {
  692.     case GDB:
  693.     case PYDB:
  694.     {
  695.     string ans = gdb_question("info files");
  696.     if (ans == NO_GDB_ANSWER)
  697.         break;
  698.  
  699.     file = "";
  700.     if (ans.contains("Symbols from "))
  701.     {
  702.         file = ans.after("Symbols from ");
  703.         file = file.before(".\n");
  704.         file = unquote(file);
  705.     }
  706.  
  707.     core = "";
  708.     if (ans.contains("core dump"))
  709.     {
  710.         core = ans.after("core dump");
  711.         core = core.after('`');
  712.         core = core.before("',");
  713.     }
  714.  
  715.     if (ans.contains("process "))
  716.     {
  717.         string p = ans.after("process ");
  718.         pid = atoi(p.chars());
  719.     }
  720.  
  721.     attached = ans.contains("attached process ");
  722.  
  723.     ans = gdb_question("info program");
  724.     if (ans == NO_GDB_ANSWER)
  725.         break;
  726.  
  727.     if (ans.contains("not being run"))
  728.         running = false;
  729.     else if (ans.contains("\nIt stopped "))
  730.     {
  731.         state = ans.from("\nIt stopped ");
  732.         state = "has " + state.after("\nIt ");
  733.         state = state.before('.');
  734.         running = true;
  735.     }
  736.     break;
  737.     }
  738.  
  739.     case DBX:
  740.     {
  741.     string ans = gdb_question(gdb->debug_command());
  742.     if (ans != NO_GDB_ANSWER)
  743.     {
  744.         if (ans.contains("Debugging: ", 0))
  745.         ans = ans.after(": ");
  746.  
  747.         strip_space(ans);
  748.         if (!ans.contains(' ')) // Sanity check
  749.         file = ans;
  750.     }
  751.     break;
  752.     }
  753.  
  754.     case XDB:
  755.     break;            // FIXME
  756.  
  757.     case PERL:
  758.     // Just use the current file.
  759.     file = source_view->file_of_cursor();
  760.     file = file.before(":");
  761.     core = "";
  762.     break;
  763.  
  764.     case JDB:
  765.     // Just use the current class.
  766.     file = source_view->line_of_cursor();
  767.     file = file.before(":");
  768.     core = "";
  769.     break;
  770.     }
  771.  
  772.     if (file == NO_GDB_ANSWER)
  773.     {
  774.     // As a fallback, get core file and executable from argument list.
  775.     // Works only on local file system and is more a guess.
  776.     char **argv = saved_argv();
  777.     int argc = 0;
  778.     while (argv[argc] != 0)
  779.         argc++;
  780.  
  781.     for (int i = argc - 1; i > 0 && file == NO_GDB_ANSWER; i--)
  782.     {
  783.         // All debuggers supported by DDD have [EXEC [CORE]] as
  784.         // their last arguments.
  785.         string arg = argv[i];
  786.         if (is_core_file(arg))
  787.         core = arg;
  788.         else if (is_exec_file(arg))
  789.         file = arg;
  790.     }
  791.     }
  792.  
  793.     if (file != NO_GDB_ANSWER)
  794.     add_to_recent(file);
  795. }
  796.  
  797.  
  798. //-----------------------------------------------------------------------------
  799. // Processes
  800. //-----------------------------------------------------------------------------
  801.  
  802. // Process selection
  803. static int ps_pid_index = 0;
  804.  
  805. // Retrieve PID from PS output
  806. static int ps_pid(const string& line)
  807. {
  808.     const char *s = line.chars() + ps_pid_index;
  809.     while (s > line.chars() && isdigit(s[-1]))
  810.     --s;
  811.  
  812.     return atoi(s);
  813. }
  814.  
  815. // Fill the pids in DISP_NRS
  816. static void getPIDs(Widget selectionList, IntArray& disp_nrs)
  817. {
  818.     static IntArray empty;
  819.     disp_nrs = empty;
  820.  
  821.     XmStringTable selected_items;
  822.     int selected_items_count = 0;
  823.  
  824.     assert(XmIsList(selectionList));
  825.  
  826.     XtVaGetValues(selectionList,
  827.           XmNselectedItemCount, &selected_items_count,
  828.           XmNselectedItems, &selected_items,
  829.           NULL);
  830.  
  831.     for (int i = 0; i < selected_items_count; i++)
  832.     {
  833.     String _item;
  834.     XmStringGetLtoR(selected_items[i], LIST_CHARSET, &_item);
  835.     string item(_item);
  836.     XtFree(_item);
  837.  
  838.     int p = ps_pid(item);
  839.     if (p > 0)
  840.         disp_nrs += p;
  841.     }
  842. }
  843.  
  844. // Get the PID from the selection list in CLIENT_DATA
  845. static int get_pid(Widget, XtPointer client_data, XtPointer)
  846. {
  847.     IntArray pids;
  848.     Widget processes = Widget(client_data);
  849.     if (processes != 0)
  850.     getPIDs(processes, pids);
  851.  
  852.     if (pids.size() == 1)
  853.     return pids[0];
  854.     else
  855.     return 0;
  856. }
  857.  
  858. // Process selection
  859.  
  860. static void sortProcesses(StringArray& a)
  861. {
  862.     // Shell sort -- simple and fast
  863.     int h = 1;
  864.     do {
  865.     h = h * 3 + 1;
  866.     } while (h <= a.size());
  867.     do {
  868.     h /= 3;
  869.     for (int i = h; i < a.size(); i++)
  870.     {
  871.         string v = a[i];
  872.         int j;
  873.         for (j = i; j >= h && ps_pid(a[j - h]) > ps_pid(v); j -= h)
  874.         a[j] = a[j - h];
  875.         if (i != j)
  876.         a[j] = v;
  877.     }
  878.     } while (h != 1);
  879. }
  880.  
  881.  
  882. inline bool is_separator(char c)
  883. {
  884.     return c == ' ' || c == '\'' || c == '\"';
  885. }
  886.  
  887. // Check whether LINE is a valid PS line.  Exclude occurrences of PS_COMMAND.
  888. static bool valid_ps_line(const string& line, const string& ps_command)
  889. {
  890.     int pid = ps_pid(line);
  891.     if (pid == 0)
  892.     return false;        // No PID
  893.  
  894.     // You don't want to debug DDD, don't you?
  895.     if (!remote_gdb() && pid == getpid())
  896.     return false;
  897.  
  898.     // Neither should you debug GDB by itself.
  899.     if (pid == gdb->pid())
  900.     return false;
  901.  
  902.     // Don't issue lines containing `ps' (or whatever the first word
  903.     // in PS_COMMAND is).
  904.     string ps = ps_command;
  905.     if (ps.contains(' '))
  906.     ps = ps.before(' ');
  907.     ps = basename(ps);
  908.     int index = line.index(ps);
  909.     if (index > 0
  910.     && (line[index - 1] == '/' || is_separator(line[index - 1]))
  911.     && (line.length() == index + ps.length()
  912.         || is_separator(line[index + ps.length()])))
  913.     return false;
  914.  
  915.     // Okay, just leave it
  916.     return true;
  917. }
  918.  
  919.  
  920. // Create list of processes
  921. static void update_processes(Widget processes, bool keep_selection)
  922. {
  923.     StatusDelay delay("Getting list of processes");
  924.  
  925.     string cmd = sh_command(app_data.ps_command) + " 2>&1";
  926.     FILE *fp = popen(cmd.chars(), "r");
  927.     if (fp == 0)
  928.     {
  929.     delay.outcome = strerror(errno);
  930.     return;
  931.     }
  932.  
  933.     StringArray all_process_list;
  934.     int c;
  935.     string line = "";
  936.     bool first_line = true;
  937.  
  938.     while ((c = getc(fp)) != EOF)
  939.     {
  940.     if (c == '\n')
  941.     {
  942.         if (first_line || valid_ps_line(line, app_data.ps_command))
  943.         all_process_list += line;
  944. #if 0
  945.         else
  946.         clog << "Excluded: " << line << "\n";
  947. #endif
  948.  
  949.         if (first_line)
  950.         {
  951.         // Find first occurrence of `PID' title
  952.         ps_pid_index = line.index(" PID ");
  953.         if (ps_pid_index < 0)
  954.             ps_pid_index = 0;
  955.         }
  956.  
  957.         line = "";
  958.         first_line = false;
  959.     }
  960.     else
  961.     {
  962.         line += c;
  963.     }
  964.     }
  965.  
  966.     pclose(fp);
  967.     sortProcesses(all_process_list);
  968.     DynIntArray pids(all_process_list.size());
  969.  
  970.     // If GDB cannot send a signal to the process, we cannot debug it.
  971.     // Try a `kill -0' (via GDB, as it may be setuid) and filter out
  972.     // all processes in the `kill' diagnostic -- that is, all
  973.     // processes that `kill' could not send a signal.
  974.     string kill = "kill -0";
  975.     int i;
  976.     for (i = 0; i < all_process_list.size(); i++)
  977.     {
  978.     pids[i] = ps_pid(all_process_list[i]);
  979.     if (pids[i])
  980.         kill += string(" ") + itostring(pids[i]);
  981.     }
  982.     string kill_result = gdb_question(gdb->shell_command(kill));
  983.     i = 0;
  984.     while (i >= 0)
  985.     {
  986.     i = kill_result.index(rxint, i);
  987.     if (i >= 0)
  988.     {
  989.         int bad_pid = atoi((char *)kill_result + i);
  990.         for (int k = 0; k < all_process_list.size(); k++)
  991.         {
  992.         if (pids[k] != 0 && pids[k] == bad_pid)
  993.         {
  994. #if 0
  995.             clog << "Excluded: " << all_process_list[k] << "\n";
  996. #endif
  997.             all_process_list[k] = NO_GDB_ANSWER;
  998.         }
  999.         }
  1000.         i++;
  1001.     }
  1002.     }
  1003.  
  1004.     StringArray process_list;
  1005.     for (i = 0; i < all_process_list.size(); i++)
  1006.     if (all_process_list[i] != NO_GDB_ANSWER)
  1007.         process_list += all_process_list[i];
  1008.  
  1009.     // Now set the selection.
  1010.     bool *selected = new bool[process_list.size()];
  1011.     for (i = 0; i < process_list.size(); i++)
  1012.     selected[i] = false;
  1013.  
  1014.     int pos = -1;
  1015.     if (keep_selection)
  1016.     {
  1017.     // Preserve old selection: each PID selected before will also be
  1018.     // selected after.
  1019.     IntArray selection;
  1020.     getPIDs(processes, selection);
  1021.  
  1022.     for (i = 0; i < selection.size(); i++)
  1023.     {
  1024.         for (int j = 0; j < process_list.size(); j++)
  1025.         if (selection[i] == ps_pid(process_list[j]))
  1026.         {
  1027.             if (pos < 0)
  1028.             pos = j;
  1029.             selected[j] = true;
  1030.         }
  1031.     }
  1032.     }
  1033.  
  1034.     if (pos < 0)
  1035.     {
  1036.     // Create new selection from current file and current pid.
  1037.     ProgramInfo info;
  1038.  
  1039.     // Check for current pid; if found, highlight it.
  1040.     for (i = 0; pos < 0 && i < process_list.size(); i++)
  1041.     {
  1042.         if (info.pid != 0 && ps_pid(process_list[i]) == info.pid)
  1043.         pos = i;
  1044.     }
  1045.  
  1046.     if (pos < 0)
  1047.     {
  1048.         // Not found? Try leftmost occurrence of process base name.
  1049.         string current_base = basename(info.file.chars());
  1050.         int leftmost = INT_MAX;
  1051.         for (i = 0; i < process_list.size(); i++)
  1052.         {
  1053.         int occurrence = process_list[i].index(current_base);
  1054.         if (occurrence >= 0 && occurrence < leftmost 
  1055.             && ps_pid(process_list[i]) > 0)
  1056.         {
  1057.             leftmost = occurrence;
  1058.             pos = i;
  1059.         }
  1060.         }
  1061.     }
  1062.     }
  1063.  
  1064.     if (pos >= 0)
  1065.     selected[pos] = true;
  1066.  
  1067.     setLabelList(processes, process_list.values(),
  1068.          selected, process_list.size(), true, false);
  1069.  
  1070.     if (pos >= 0)
  1071.     ListSetAndSelectPos(processes, pos + 1);
  1072.  
  1073.     delete[] selected;
  1074. }
  1075.  
  1076.  
  1077. static void gdbUpdateProcessesCB(Widget, XtPointer client_data, XtPointer)
  1078. {
  1079.     Widget processes = Widget(client_data);
  1080.     update_processes(processes, true);
  1081. }
  1082.  
  1083. // Select a process
  1084. static void SelectProcessCB(Widget w, XtPointer client_data, 
  1085.                 XtPointer call_data)
  1086. {
  1087.     XmListCallbackStruct *cbs = (XmListCallbackStruct *)call_data;
  1088.     int pos = cbs->item_position;
  1089.     if (pos == 1)
  1090.     XmListDeselectAllItems(w); // Title selected
  1091.     else
  1092.     ListSetAndSelectPos(w, pos);
  1093.  
  1094.     int pid = get_pid(w, client_data, call_data);
  1095.     if (pid <= 0)
  1096.     set_status("");
  1097.     else
  1098.     set_status("Process " + itostring(pid));
  1099. }
  1100.  
  1101.  
  1102. // OK pressed in `Open Process'
  1103. static void openProcessDone(Widget w, XtPointer client_data, 
  1104.                 XtPointer call_data)
  1105. {
  1106.     int pid = get_pid(w, client_data, call_data);
  1107.     if (pid <= 0)
  1108.     {
  1109.     gdbUpdateProcessesCB(w, client_data, call_data);    
  1110.     return;
  1111.     }
  1112.  
  1113.     XtUnmanageChild(w);
  1114.  
  1115.     ProgramInfo info;
  1116.  
  1117.     if (pid == info.pid)
  1118.     {
  1119.     set_status("Already attached to process " + itostring(pid) + ".");
  1120.     return;
  1121.     }
  1122.  
  1123.     switch(gdb->type())
  1124.     {
  1125.     case GDB:
  1126.     // GDB does not always detach processes upon opening new
  1127.     // files, so we do it explicitly
  1128.     if (info.attached)
  1129.         gdb_command("detach");
  1130.  
  1131.     // Attach to new process
  1132.     gdb_command("attach " + itostring(pid));
  1133.     break;
  1134.     
  1135.     case DBX:
  1136.     // Attach to new process
  1137.     if (info.file != NO_GDB_ANSWER && info.file != "")
  1138.         gdb_command("debug " + info.file + " " + itostring(pid));
  1139.     else
  1140.         post_error("No program.", "no_program", w);
  1141.     break;
  1142.  
  1143.     case XDB:
  1144.     case JDB:
  1145.     case PYDB:
  1146.     case PERL:
  1147.     break;        // FIXME
  1148.     }
  1149. }
  1150.  
  1151. // When W is to be destroyed, remove all references in Widget(CLIENT_DATA)
  1152. static void RemoveCallbacksCB(Widget w, XtPointer client_data, XtPointer)
  1153. {
  1154.     Widget ref = Widget(client_data);
  1155.     XtRemoveCallback(ref, XmNokCallback,      UnmanageThisCB, XtPointer(w));
  1156.     XtRemoveCallback(ref, XmNcancelCallback,  UnmanageThisCB, XtPointer(w));
  1157.     XtRemoveCallback(ref, XmNdestroyCallback, RemoveCallbacksCB, XtPointer(w));
  1158. }
  1159.  
  1160. // If we don't have a current executable, issue a warning.
  1161. static void warn_if_no_program(Widget popdown)
  1162. {
  1163.     ProgramInfo info;
  1164.  
  1165.     if (info.file == "")
  1166.     {
  1167.     Widget warning = post_warning("Please open a program first.", 
  1168.                       "no_program", popdown);
  1169.  
  1170.     if (popdown != 0 && warning != 0)
  1171.     {
  1172.         // Tie the warning to the dialog - if one is popped down,
  1173.         // so is the other.
  1174.         XtAddCallback(warning, XmNokCallback, 
  1175.               UnmanageThisCB, XtPointer(popdown));
  1176.         XtAddCallback(warning, XmNcancelCallback, 
  1177.               UnmanageThisCB, XtPointer(popdown));
  1178.         XtAddCallback(popdown, XmNdestroyCallback,
  1179.               RemoveCallbacksCB, XtPointer(warning));
  1180.  
  1181.         XtAddCallback(popdown, XmNokCallback,
  1182.               UnmanageThisCB, XtPointer(warning));
  1183.         XtAddCallback(popdown, XmNcancelCallback,
  1184.               UnmanageThisCB, XtPointer(warning));
  1185.         XtAddCallback(warning, XmNdestroyCallback,
  1186.               RemoveCallbacksCB, XtPointer(popdown));
  1187.     }
  1188.     }
  1189. }
  1190.  
  1191.  
  1192.  
  1193. //-----------------------------------------------------------------------------
  1194. // Helpers for class and source selection
  1195. //-----------------------------------------------------------------------------
  1196.  
  1197. // Get the selected item ids
  1198. static void get_items(Widget selectionList, StringArray& itemids)
  1199. {
  1200.     static StringArray empty;
  1201.     itemids = empty;
  1202.  
  1203.     XmStringTable selected_items;
  1204.     int selected_items_count = 0;
  1205.  
  1206.     assert(XmIsList(selectionList));
  1207.  
  1208.     XtVaGetValues(selectionList,
  1209.           XmNselectedItemCount, &selected_items_count,
  1210.           XmNselectedItems, &selected_items,
  1211.           NULL);
  1212.  
  1213.     for (int i = 0; i < selected_items_count; i++)
  1214.     {
  1215.     String _item;
  1216.     XmStringGetLtoR(selected_items[i], LIST_CHARSET, &_item);
  1217.     string item(_item);
  1218.     XtFree(_item);
  1219.  
  1220.     itemids += item;
  1221.     }
  1222. }
  1223.  
  1224. // Get the item from the selection list in CLIENT_DATA
  1225. static string get_item(Widget, XtPointer client_data, XtPointer)
  1226. {
  1227.     StringArray itemids;
  1228.     Widget items = Widget(client_data);
  1229.     if (items != 0)
  1230.     get_items(items, itemids);
  1231.  
  1232.     if (itemids.size() == 1)
  1233.     return itemids[0];
  1234.  
  1235.     return "";
  1236. }
  1237.  
  1238.  
  1239. //-----------------------------------------------------------------------------
  1240. // Classes (JDB only)
  1241. //-----------------------------------------------------------------------------
  1242.  
  1243. // Select a class
  1244. static void SelectClassCB(Widget w, XtPointer client_data, 
  1245.               XtPointer call_data)
  1246. {
  1247.     XmListCallbackStruct *cbs = (XmListCallbackStruct *)call_data;
  1248.     int pos = cbs->item_position;
  1249.     ListSetAndSelectPos(w, pos);
  1250.  
  1251.     string cls = get_item(w, client_data, call_data);
  1252.     if (cls == "")
  1253.     set_status("");
  1254.     else
  1255.     set_status(source_view->full_path(java_class_file(cls)));
  1256. }
  1257.  
  1258. static void update_classes(Widget classes)
  1259. {
  1260.     StatusDelay delay("Getting list of classes");
  1261.     StringArray classes_list;
  1262.     get_java_classes(classes_list);
  1263.  
  1264.     // Now set the selection.
  1265.     bool *selected = new bool[classes_list.size()];
  1266.     for (int i = 0; i < classes_list.size(); i++)
  1267.     selected[i] = false;
  1268.  
  1269.     setLabelList(classes, classes_list.values(),
  1270.          selected, classes_list.size(), false, false);
  1271.  
  1272.     delete[] selected;
  1273. }
  1274.  
  1275. static void gdbUpdateClassesCB(Widget, XtPointer client_data, XtPointer)
  1276. {
  1277.     Widget classes = Widget(client_data);
  1278.     update_classes(classes);
  1279. }
  1280.  
  1281. // OK pressed in `Open Class'
  1282. static void openClassDone(Widget w, XtPointer client_data, 
  1283.               XtPointer call_data)
  1284. {
  1285.     string cls = get_item(w, client_data, call_data);
  1286.     if (cls == "")
  1287.     {
  1288.     gdbUpdateClassesCB(w, client_data, call_data);    
  1289.     return;
  1290.     }
  1291.  
  1292.     XtUnmanageChild(w);
  1293.  
  1294.     gdb_command(gdb->debug_command(cls));
  1295. }
  1296.  
  1297.  
  1298. //-----------------------------------------------------------------------------
  1299. // Lookup sources and functions (GDB only)
  1300. //-----------------------------------------------------------------------------
  1301.  
  1302. static StringArray all_sources;
  1303.  
  1304. // Select a source; show the full path name in the status line
  1305. static void SelectSourceCB(Widget w, XtPointer, XtPointer call_data)
  1306. {
  1307.     XmListCallbackStruct *cbs = (XmListCallbackStruct *)call_data;
  1308.     int pos = cbs->item_position;
  1309.     ListSetAndSelectPos(w, pos);
  1310.  
  1311.     pos--;
  1312.     if (pos < 0)
  1313.     pos = all_sources.size() - 1;
  1314.     set_status(all_sources[pos]);
  1315. }
  1316.  
  1317. // Get list of sources into SOURCES_LIST
  1318. void get_gdb_sources(StringArray& sources_list)
  1319. {
  1320.     static StringArray empty;
  1321.     sources_list = empty;
  1322.  
  1323.     // Load all shared libraries first.  Otherwise, their sources
  1324.     // won't show up in `info sources'.
  1325.     gdb_question("sharedlibrary");
  1326.  
  1327.     string ans = gdb_question("info sources");
  1328.     if (ans != NO_GDB_ANSWER)
  1329.     {
  1330.     // Create a newline-separated list of sources
  1331.     string new_ans;
  1332.     while (ans != "")
  1333.     {
  1334.         string line = ans.before('\n');
  1335.         ans = ans.after('\n');
  1336.  
  1337.         if (line == "" || line.contains(':', -1))
  1338.         continue;
  1339.  
  1340.         line.gsub(", ", "\n");
  1341.         new_ans += line + '\n';
  1342.     }
  1343.  
  1344.     ans = new_ans;
  1345.     while (ans != "")
  1346.     {
  1347.         string line = ans.before('\n');
  1348.         ans = ans.after('\n');
  1349.         
  1350.         sources_list += line;
  1351.     }
  1352.  
  1353.     smart_sort(sources_list);
  1354.     uniq(sources_list);
  1355.     }
  1356. }
  1357.  
  1358. // Remove adjacent duplicates in A1
  1359. static void uniq(StringArray& a1, StringArray& a2)
  1360. {
  1361.     StringArray b1;
  1362.     StringArray b2;
  1363.  
  1364.     for (int i = 0; i < a1.size(); i++)
  1365.     {
  1366.     if (i == 0 || a1[i - 1] != a1[i])
  1367.     {
  1368.         b1 += a1[i];
  1369.         b2 += a2[i];
  1370.     }
  1371.     }
  1372.     
  1373.     a1 = b1;
  1374.     a2 = b2;
  1375. }
  1376.  
  1377. // Sort A1 and A2 according to the values in A1
  1378. static void sort(StringArray& a1, StringArray& a2)
  1379. {
  1380.     assert(a1.size() == a2.size());
  1381.  
  1382.     // Shell sort -- simple and fast
  1383.     int h = 1;
  1384.     do {
  1385.     h = h * 3 + 1;
  1386.     } while (h <= a1.size());
  1387.     do {
  1388.     h /= 3;
  1389.     for (int i = h; i < a1.size(); i++)
  1390.     {
  1391.         string v1 = a1[i];
  1392.         string v2 = a2[i];
  1393.         int j;
  1394.         for (j = i; j >= h && smart_compare(a1[j - h], v1) > 0; j -= h)
  1395.         {
  1396.         a1[j] = a1[j - h];
  1397.         a2[j] = a2[j - h];
  1398.         }
  1399.         if (i != j)
  1400.         {
  1401.         a1[j] = v1;
  1402.         a2[j] = v2;
  1403.         }
  1404.     }
  1405.     } while (h != 1);
  1406. }
  1407.  
  1408. static void filter_sources(StringArray& labels, StringArray& sources,
  1409.                const string& pattern)
  1410. {
  1411.     assert(labels.size() == sources.size());
  1412.  
  1413.     StringArray new_labels;
  1414.     StringArray new_sources;
  1415.  
  1416.     for (int i = 0; i < labels.size(); i++)
  1417.     {
  1418.     if (glob_match(pattern, labels[i], 0) ||
  1419.         glob_match(pattern, sources[i], 0))
  1420.     {
  1421.         new_labels  += labels[i];
  1422.         new_sources += sources[i];
  1423.     }
  1424.     }
  1425.  
  1426.     labels  = new_labels;
  1427.     sources = new_sources;
  1428. }
  1429.  
  1430. static void update_sources(Widget sources, Widget filter)
  1431. {
  1432.     StatusDelay delay("Getting sources");
  1433.     get_gdb_sources(all_sources);
  1434.  
  1435.     String pattern_s = XmTextFieldGetString(filter);
  1436.     string pattern = pattern_s;
  1437.     XtFree(pattern_s);
  1438.  
  1439.     strip_space(pattern);
  1440.     if (pattern == "")
  1441.     pattern = "*";
  1442.     XmTextFieldSetString(filter, (char *)pattern);
  1443.  
  1444.     StringArray labels;
  1445.     uniquify(all_sources, labels);
  1446.  
  1447.     // Sort and remove duplicates
  1448.     sort(labels, all_sources);
  1449.     uniq(labels, all_sources);
  1450.  
  1451.     // Filter pattern
  1452.     filter_sources(labels, all_sources, pattern);
  1453.  
  1454.     // Now set the selection.
  1455.     bool *selected = new bool[labels.size()];
  1456.     for (int i = 0; i < labels.size(); i++)
  1457.     selected[i] = false;
  1458.  
  1459.     setLabelList(sources, labels.values(),
  1460.          selected, labels.size(), false, false);
  1461.  
  1462.     delete[] selected;
  1463. }
  1464.  
  1465. // OK pressed in `Lookup Source'
  1466. static void lookupSourceDone(Widget w,
  1467.                  XtPointer client_data, 
  1468.                  XtPointer call_data)
  1469. {
  1470.     Widget sources = Widget(client_data);
  1471.     XmSelectionBoxCallbackStruct *cbs = 
  1472.     (XmSelectionBoxCallbackStruct *)call_data;
  1473.  
  1474.     set_status("");
  1475.  
  1476.     string source = get_item(w, client_data, call_data);
  1477.  
  1478.     if (source.contains('/'))
  1479.     {
  1480.     // Expand to full path name
  1481.     int *position_list = 0;
  1482.     int position_count = 0;
  1483.     if (XmListGetSelectedPos(sources, &position_list, &position_count))
  1484.     {
  1485.         if (position_count == 1)
  1486.         {
  1487.         int pos = position_list[0];
  1488.         pos--;
  1489.         if (pos < 0)
  1490.             pos = all_sources.size() - 1;
  1491.         source = all_sources[pos];
  1492.         }
  1493.  
  1494.         XtFree((char *)position_list);
  1495.     }
  1496.     }
  1497.  
  1498.     if (source != "")
  1499.     {
  1500.     source_view->lookup(source + ":1");
  1501.  
  1502.     if (cbs != 0 && 
  1503.         cbs->reason != XmCR_APPLY && 
  1504.         cbs->reason != XmCR_ACTIVATE)
  1505.     {
  1506.         Widget scroll = XtParent(sources);
  1507.         Widget dialog = XtParent(scroll);
  1508.         XtUnmanageChild(dialog);
  1509.     }
  1510.     }
  1511. }
  1512.  
  1513. static void open_source_msg()
  1514. {
  1515.     set_status("Open Source is an idea whose time has finally come.  "
  1516.            "See http://www.opensource.org/.");
  1517. }
  1518.  
  1519.  
  1520.  
  1521. //-----------------------------------------------------------------------------
  1522. // Entry Points
  1523. //-----------------------------------------------------------------------------
  1524.  
  1525. void gdbOpenFileCB(Widget w, XtPointer, XtPointer)
  1526. {
  1527.     static Widget dialog = 
  1528.     create_file_dialog(w, "exec_files", 
  1529.                searchRemoteExecFiles, 
  1530.                searchRemoteDirectories,
  1531.                searchLocalExecFiles, 0,
  1532.                openFileDone);
  1533.     manage_and_raise(dialog);
  1534. }
  1535.  
  1536. void gdbOpenRecentCB(Widget, XtPointer client_data, XtPointer)
  1537. {
  1538.     int index = ((int)(long)client_data) - 1;
  1539.  
  1540.     StringArray recent_files;
  1541.     get_recent(recent_files);
  1542.  
  1543.     if (index >= 0 && index < recent_files.size())
  1544.     {
  1545.     string file = recent_files[index];
  1546.     open_file(file);
  1547.     // This is a kludge as I don't [yet] understand how to force the
  1548.     // reading of the source file automatically, as is done when an
  1549.     // compiled executable is opened.
  1550.     if (gdb->type() == PYDB)
  1551.         source_view->read_file(file);
  1552.     }
  1553. }
  1554.  
  1555. void gdbOpenCoreCB(Widget w, XtPointer, XtPointer)
  1556. {
  1557.     static Widget dialog = 
  1558.     create_file_dialog(w, "core_files", 
  1559.                searchRemoteCoreFiles, searchRemoteDirectories,
  1560.                searchLocalCoreFiles, 0,
  1561.                openCoreDone);
  1562.     manage_and_raise(dialog);
  1563.     warn_if_no_program(dialog);
  1564. }
  1565.  
  1566. void gdbOpenSourceCB(Widget w, XtPointer, XtPointer)
  1567. {
  1568.     static Widget dialog = 
  1569.     create_file_dialog(w, "source_files", 
  1570.                searchRemoteSourceFiles, searchRemoteDirectories,
  1571.                searchLocalSourceFiles, 0,
  1572.                openSourceDone);
  1573.     manage_and_raise(dialog);
  1574.  
  1575.     open_source_msg();
  1576.  
  1577.     if ((gdb->type() != JDB) && (gdb->type() != PYDB))
  1578.     {
  1579.     warn_if_no_program(dialog);
  1580.     }
  1581.     else
  1582.     {
  1583.     // JDB works well without executable
  1584.     // PYDB doesn't use an executable
  1585.     }
  1586. }
  1587.  
  1588. void gdbOpenProcessCB(Widget w, XtPointer, XtPointer)
  1589. {
  1590.     static Widget dialog = 0;
  1591.     static Widget processes = 0;
  1592.  
  1593.     if (dialog == 0)
  1594.     {
  1595.     Arg args[10];
  1596.     int arg = 0;
  1597.     
  1598.     XtSetArg(args[arg], XmNautoUnmanage, False); arg++;
  1599.     dialog = verify(XmCreateSelectionDialog(find_shell(w), 
  1600.                         "processes", args, arg));
  1601.  
  1602.     Delay::register_shell(dialog);
  1603.  
  1604.     XtUnmanageChild(XmSelectionBoxGetChild(dialog, 
  1605.                            XmDIALOG_SELECTION_LABEL));
  1606.     XtUnmanageChild(XmSelectionBoxGetChild(dialog, 
  1607.                            XmDIALOG_TEXT));
  1608.  
  1609.     processes = XmSelectionBoxGetChild(dialog, XmDIALOG_LIST);
  1610.  
  1611.     XtAddCallback(processes, XmNsingleSelectionCallback,
  1612.               SelectProcessCB, XtPointer(processes));
  1613.     XtAddCallback(processes, XmNmultipleSelectionCallback,
  1614.               SelectProcessCB, XtPointer(processes));
  1615.     XtAddCallback(processes, XmNextendedSelectionCallback,
  1616.               SelectProcessCB, XtPointer(processes));
  1617.     XtAddCallback(processes, XmNbrowseSelectionCallback,
  1618.               SelectProcessCB, XtPointer(processes));
  1619.  
  1620.     XtAddCallback(dialog, XmNokCallback, 
  1621.               openProcessDone, XtPointer(processes));
  1622.     XtAddCallback(dialog, XmNapplyCallback, 
  1623.               gdbUpdateProcessesCB, XtPointer(processes));
  1624.     XtAddCallback(dialog, XmNcancelCallback, 
  1625.               UnmanageThisCB, XtPointer(dialog));
  1626.     XtAddCallback(dialog, XmNhelpCallback, ImmediateHelpCB, 0);
  1627.     }
  1628.  
  1629.     update_processes(processes, false);
  1630.     manage_and_raise(dialog);
  1631.     warn_if_no_program(dialog);
  1632. }
  1633.  
  1634. void gdbOpenClassCB(Widget w, XtPointer, XtPointer)
  1635. {
  1636.     static Widget dialog = 0;
  1637.     static Widget classes = 0;
  1638.  
  1639.     if (dialog == 0)
  1640.     {
  1641.     Arg args[10];
  1642.     int arg = 0;
  1643.     
  1644.     XtSetArg(args[arg], XmNautoUnmanage, False); arg++;
  1645.     dialog = verify(XmCreateSelectionDialog(find_shell(w), 
  1646.                         "classes", args, arg));
  1647.  
  1648.     Delay::register_shell(dialog);
  1649.  
  1650.     XtUnmanageChild(XmSelectionBoxGetChild(dialog, 
  1651.                            XmDIALOG_SELECTION_LABEL));
  1652.     XtUnmanageChild(XmSelectionBoxGetChild(dialog, 
  1653.                            XmDIALOG_TEXT));
  1654.  
  1655.     classes = XmSelectionBoxGetChild(dialog, XmDIALOG_LIST);
  1656.  
  1657.     XtAddCallback(classes, XmNsingleSelectionCallback,
  1658.               SelectClassCB, XtPointer(classes));
  1659.     XtAddCallback(classes, XmNmultipleSelectionCallback,
  1660.               SelectClassCB, XtPointer(classes));
  1661.     XtAddCallback(classes, XmNextendedSelectionCallback,
  1662.               SelectClassCB, XtPointer(classes));
  1663.     XtAddCallback(classes, XmNbrowseSelectionCallback,
  1664.               SelectClassCB, XtPointer(classes));
  1665.  
  1666.     XtAddCallback(dialog, XmNokCallback, 
  1667.               openClassDone, XtPointer(classes));
  1668.     XtAddCallback(dialog, XmNapplyCallback, 
  1669.               gdbUpdateClassesCB, XtPointer(classes));
  1670.     XtAddCallback(dialog, XmNcancelCallback, 
  1671.               UnmanageThisCB, XtPointer(dialog));
  1672.     XtAddCallback(dialog, XmNhelpCallback, ImmediateHelpCB, 0);
  1673.     }
  1674.  
  1675.     update_classes(classes);
  1676.     manage_and_raise(dialog);
  1677. }
  1678.  
  1679. static Widget source_list   = 0;
  1680. static Widget source_filter = 0;
  1681.  
  1682. void update_sources()
  1683. {
  1684.     if (source_list != 0)
  1685.     update_sources(source_list, source_filter);
  1686. }
  1687.  
  1688. static void FilterSourcesCB(Widget, XtPointer, XtPointer)
  1689. {
  1690.     update_sources();
  1691. }
  1692.  
  1693. void gdbLookupSourceCB(Widget w, XtPointer client_data, XtPointer call_data)
  1694. {
  1695.     if (gdb->type() != GDB)
  1696.     {
  1697.     gdbOpenSourceCB(w, client_data, call_data);
  1698.     return;
  1699.     }
  1700.  
  1701.     static Widget dialog  = 0;
  1702.  
  1703.     if (dialog == 0)
  1704.     {
  1705.     Arg args[10];
  1706.     int arg = 0;
  1707.     
  1708.     XtSetArg(args[arg], XmNautoUnmanage, False); arg++;
  1709. #if XmVersion >= 1002
  1710.     XtSetArg(args[arg], XmNchildPlacement, XmPLACE_TOP); arg++;
  1711. #endif
  1712.     dialog = verify(XmCreateSelectionDialog(find_shell(w), 
  1713.                         "sources", args, arg));
  1714.  
  1715.     Delay::register_shell(dialog);
  1716.  
  1717.     XtUnmanageChild(XmSelectionBoxGetChild(dialog, 
  1718.                            XmDIALOG_SELECTION_LABEL));
  1719.     XtUnmanageChild(XmSelectionBoxGetChild(dialog, 
  1720.                            XmDIALOG_TEXT));
  1721.  
  1722.     arg = 0;
  1723.     XtSetArg(args[arg], XmNmarginWidth,     0);     arg++;
  1724.     XtSetArg(args[arg], XmNmarginHeight,    0);     arg++;
  1725.     XtSetArg(args[arg], XmNborderWidth,     0);     arg++;
  1726.     XtSetArg(args[arg], XmNadjustMargin,    False); arg++;
  1727.     XtSetArg(args[arg], XmNshadowThickness, 0);     arg++;
  1728.     XtSetArg(args[arg], XmNspacing,         0);     arg++;
  1729.     Widget box = XmCreateRowColumn(dialog, "box", args, arg);
  1730.     XtManageChild(box);
  1731.  
  1732.     arg = 0;
  1733.     Widget label = XmCreateLabel(box, "label", args, arg);
  1734.     XtManageChild(label);
  1735.  
  1736.     arg = 0;
  1737.     source_filter = XmCreateTextField(box, "filter", args, arg);
  1738.     XtManageChild(source_filter);
  1739.  
  1740. #if XmVersion >= 1002
  1741.     arg = 0;
  1742.     Widget lookup = XmCreatePushButton(dialog, "lookup", args, arg);
  1743.     XtManageChild(lookup);
  1744. #endif
  1745.  
  1746.     source_list = XmSelectionBoxGetChild(dialog, XmDIALOG_LIST);
  1747.  
  1748.     XtAddCallback(source_list, XmNsingleSelectionCallback,
  1749.               SelectSourceCB, XtPointer(source_list));
  1750.     XtAddCallback(source_list, XmNmultipleSelectionCallback,
  1751.               SelectSourceCB, XtPointer(source_list));
  1752.     XtAddCallback(source_list, XmNextendedSelectionCallback,
  1753.               SelectSourceCB, XtPointer(source_list));
  1754.     XtAddCallback(source_list, XmNbrowseSelectionCallback,
  1755.               SelectSourceCB, XtPointer(source_list));
  1756.  
  1757.     XtAddCallback(dialog, XmNokCallback, 
  1758.               lookupSourceDone, XtPointer(source_list));
  1759.     XtAddCallback(dialog, XmNapplyCallback, FilterSourcesCB, 0);
  1760.     XtAddCallback(dialog, XmNcancelCallback, 
  1761.               UnmanageThisCB, XtPointer(dialog));
  1762.     XtAddCallback(dialog, XmNunmapCallback, ClearStatusCB, 0);
  1763.     XtAddCallback(dialog, XmNhelpCallback, ImmediateHelpCB, 0);
  1764.  
  1765.     XtAddCallback(source_filter, XmNactivateCallback, 
  1766.               FilterSourcesCB, 0);
  1767.  
  1768. #if XmVersion >= 1002
  1769.     XtAddCallback(lookup, XmNactivateCallback, 
  1770.               lookupSourceDone, XtPointer(source_list));
  1771. #endif
  1772.     }
  1773.  
  1774.     update_sources(source_list, source_filter);
  1775.  
  1776.     open_source_msg();
  1777.     manage_and_raise(dialog);
  1778.     warn_if_no_program(dialog);
  1779. }
  1780.