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 / DispValue.C < prev    next >
C/C++ Source or Header  |  1998-11-26  |  36KB  |  1,559 lines

  1. // $Id: DispValue.C,v 1.107 1998/11/26 10:21:01 zeller Exp $
  2. // Read and store type and value of a displayed expression
  3.  
  4. // Copyright (C) 1995-1998 Technische Universitaet Braunschweig, Germany.
  5. // Written by Dorothea Luetkehaus <luetke@ips.cs.tu-bs.de>
  6. // and Andreas Zeller <zeller@ips.cs.tu-bs.de>.
  7. // 
  8. // This file is part of DDD.
  9. // 
  10. // DDD is free software; you can redistribute it and/or
  11. // modify it under the terms of the GNU General Public
  12. // License as published by the Free Software Foundation; either
  13. // version 2 of the License, or (at your option) any later version.
  14. // 
  15. // DDD is distributed in the hope that it will be useful,
  16. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  17. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  18. // See the GNU General Public License for more details.
  19. // 
  20. // You should have received a copy of the GNU General Public
  21. // License along with DDD -- see the file COPYING.
  22. // If not, write to the Free Software Foundation, Inc.,
  23. // 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  24. // 
  25. // DDD is the data display debugger.
  26. // For details, see the DDD World-Wide-Web page, 
  27. // `http://www.cs.tu-bs.de/softech/ddd/',
  28. // or send a mail to the DDD developers <ddd@ips.cs.tu-bs.de>.
  29.  
  30. char DispValue_rcsid[] =
  31.     "$Id: DispValue.C,v 1.107 1998/11/26 10:21:01 zeller Exp $";
  32.  
  33. #ifdef __GNUG__
  34. #pragma implementation
  35. #endif
  36.  
  37. #ifndef LOG_CREATE_VALUES
  38. #define LOG_CREATE_VALUES 0
  39. #endif
  40.  
  41.  
  42. //-----------------------------------------------------------------------------
  43. // A `DispValue' maintains type and value of a displayed expression
  44. //-----------------------------------------------------------------------------
  45.  
  46. #include "DispValue.h"
  47.  
  48. #include "AppData.h"
  49. #include "DispNode.h"
  50. #include "DispValueA.h"
  51. #include "DynArray.h"
  52. #include "GDBAgent.h"
  53. #include "PlotAgent.h"
  54. #include "assert.h"
  55. #include "cook.h"
  56. #include "ddd.h"
  57. #include "deref.h"
  58. #include "fonts.h"
  59. #include "isid.h"
  60. #include "misc.h"
  61. #include "plotter.h"
  62. #include "question.h"
  63. #include "regexps.h"
  64. #include "string-fun.h"
  65. #include "value-read.h"
  66.  
  67. #include <ctype.h>
  68.  
  69.  
  70. //-----------------------------------------------------------------------------
  71. // Helpers
  72. //-----------------------------------------------------------------------------
  73.  
  74. int DispValue::cached_box_tics = 0;
  75.  
  76. StringStringAssoc DispValue::type_cache;
  77.  
  78. // Get index base of expr EXPR in dimension DIM
  79. int DispValue::index_base(const string& expr, int dim)
  80. {
  81.     if (gdb->program_language() != LANGUAGE_FORTRAN)
  82.     return gdb->default_index_base();
  83.  
  84.     string base = expr;
  85.     if (base.contains('('))
  86.     base = base.before('(');
  87.     if (!type_cache.has(base))
  88.     type_cache[base] = gdb_question(gdb->whatis_command(base));
  89.     string type = type_cache[base];
  90.  
  91.     // GDB issues array information as `type = real*8 (0:9,2:12)'.
  92.     // However, the first dimension in the type output comes last in
  93.     // the printed array.
  94.     int colon = type.length();
  95.     while (colon >= 0 && dim-- >= 0)
  96.     colon = type.index(':', colon - type.length() - 1);
  97.     if (colon < 0)
  98.     return  gdb->default_index_base(); // Not found
  99.  
  100.     while (colon >= 0 && isdigit(type[colon - 1]))
  101.     colon--;
  102.  
  103.     return atoi((char *)type + colon);
  104. }
  105.  
  106. // In FORTRAN mode, GDB issues last dimensions first.  Insert new
  107. // dimension before first dimension and convert to FORTRAN
  108. // multi-dimension syntax.
  109. string DispValue::add_member_name(const string& base, 
  110.                   const string& member_name)
  111. {
  112.     if (gdb->program_language() == LANGUAGE_FORTRAN && 
  113.     member_name.contains('(', 0) &&    base.contains('('))
  114.     {
  115.     return base.before('(') + member_name.before(')') + ", " + 
  116.         base.after('(');
  117.     }
  118.  
  119.     return base + member_name;
  120. }
  121.  
  122. void DispValue::clear_type_cache()
  123. {
  124.     static StringStringAssoc empty;
  125.     type_cache = empty;
  126. }
  127.  
  128.  
  129. //-----------------------------------------------------------------------------
  130. // Data
  131. //-----------------------------------------------------------------------------
  132.  
  133. bool DispValue::expand_repeated_values = false;
  134. DispValue *(*DispValue::value_hook)(string& value) = 0;
  135.  
  136.  
  137. //-----------------------------------------------------------------------------
  138. // Function defs
  139. //-----------------------------------------------------------------------------
  140.  
  141. // Constructor
  142. DispValue::DispValue (DispValue* parent, 
  143.               int depth,
  144.               string& value,
  145.               const string& f_n, 
  146.               const string& p_n,
  147.               DispValueType given_type)
  148.     : mytype(UnknownType), myexpanded(true), myenabled(true),
  149.       myfull_name(f_n), print_name(p_n), changed(false), myrepeats(1),
  150.       _value(""), _dereferenced(false), _children(0),
  151.       _index_base(0), _have_index_base(false), _alignment(Horizontal),
  152.       _has_plot_alignment(false), _plotter(0), 
  153.       _cached_box(0), _cached_box_change(0),
  154.       _links(1)
  155. {
  156.     init(parent, depth, value, given_type);
  157.  
  158.     // A new display is not changed, but initialized
  159.     changed = false;
  160. }
  161.  
  162. // Duplicator
  163. DispValue::DispValue (const DispValue& dv)
  164.     : mytype(dv.mytype), myexpanded(dv.myexpanded), 
  165.       myenabled(dv.myenabled), myfull_name(dv.myfull_name),
  166.       print_name(dv.print_name), myaddr(dv.myaddr),
  167.       changed(false), myrepeats(dv.myrepeats),
  168.       _value(dv.value()), _dereferenced(false), _children(dv.nchildren()), 
  169.        _index_base(dv._index_base), 
  170.       _have_index_base(dv._have_index_base), _alignment(dv._alignment),
  171.       _has_plot_alignment(false), _plotter(0),
  172.       _cached_box(0), _cached_box_change(0),
  173.       _links(1)
  174. {
  175.     for (int i = 0; i < dv.nchildren(); i++)
  176.     {
  177.     _children += dv.child(i)->dup();
  178.     }
  179.  
  180.     if (dv.cached_box() != 0)
  181.     set_cached_box(dv.cached_box()->link());
  182. }
  183.  
  184.  
  185. // True if more sequence members are coming
  186. bool DispValue::sequence_pending(const string& value, 
  187.                  const DispValue *parent)
  188. {
  189.     if (parent != 0)
  190.     {
  191.     switch (parent->type())
  192.     {
  193.     case Sequence:
  194.     case List:
  195.     case Struct:
  196.     case Reference:
  197.     case Array:
  198.         // In a composite, we always read everything up to the
  199.         // final delimiter.
  200.         return false;
  201.  
  202.     case Simple:
  203.     case Pointer:
  204.     case Text:
  205.     case UnknownType:
  206.         break;
  207.     }
  208.     }
  209.  
  210.     string v = value;
  211.     strip_leading_space(v);
  212.  
  213.     if (v != "" && parent == 0)
  214.     return true;        // Still more to read
  215.  
  216.     if (!is_delimited(value))
  217.     return true;        // Not at delimiter - more stuff follows
  218.  
  219.     // Sequence is done
  220.     return false;
  221. }
  222.  
  223. // In Perl, replace `{$REF}' and `$REF' by `REF->'
  224. string DispValue::normalize_base(const string& base)
  225. {
  226. #if RUNTIME_REGEX
  227.     static regex rxsimple("([][a-zA-Z0-9_$@%().`]|->)*");
  228. #endif
  229.  
  230.     bool perl = gdb->program_language() == LANGUAGE_PERL;
  231.     string ref = base;
  232.  
  233.     // Fetch one-letter Perl prefix
  234.     string prefix;
  235.     if (perl && ref != "" && is_perl_prefix(ref[0]))
  236.     {
  237.     prefix = ref[0];
  238.     ref = ref.after(0);
  239.     }
  240.  
  241.     // Insert `->' operators
  242.     if (perl)
  243.     {
  244.     string r = ref;
  245.     if (r.contains("{", 0) && r.contains('}', -1))
  246.         r = unquote(r);
  247.  
  248.     if (r.contains('$', 0) && r.matches(rxsimple))
  249.     {
  250.         if (r.contains("}", -1) || r.contains("]", -1))
  251.         ref = r.after(0); // Array between braces is optional
  252.         else
  253.         ref = r.after(0) + "->";
  254.     }
  255.     }
  256.  
  257.     if (!perl)
  258.     {
  259.     // Place brackets around complex expressions
  260.     if (!ref.matches(rxsimple))
  261.         ref = "(" + ref + ")";
  262.     }
  263.  
  264.     return prefix + ref;
  265. }
  266.  
  267. // Parsing
  268. DispValue *DispValue::parse(DispValue *parent, 
  269.                 int        depth,
  270.                 string&    value,
  271.                 const string& full_name, 
  272.                 const string& print_name,
  273.                 DispValueType type)
  274. {
  275.     if (value_hook != 0)
  276.     {
  277.     DispValue *dv = (*value_hook)(value);
  278.     if (dv != 0)
  279.     {
  280.         // Just take values from given element
  281. #if LOG_CREATE_VALUES
  282.         clog << "External value " << quote(dv->full_name()) << "\n";
  283. #endif
  284.         return dv;
  285.     }
  286.     }
  287.  
  288.     return new DispValue(parent, depth, value, full_name, print_name, type);
  289. }
  290.  
  291. // Initialization
  292. void DispValue::init(DispValue *parent, int depth, string& value,
  293.              DispValueType given_type)
  294. {
  295. #if LOG_CREATE_VALUES
  296.     clog << "Building value from " << quote(value) << "\n";
  297. #endif
  298.  
  299.     // Be sure the value is not changed in memory
  300.     value.consuming(true);
  301.  
  302.     char *initial_value = value;
  303.  
  304.     static DispValueArray empty(0);
  305.     _children = empty;
  306.  
  307.     if (background(value.length()))
  308.     {
  309.     clear();
  310.  
  311.     mytype = Simple;
  312.     _value = "(Aborted)";
  313.     value  = "Aborted\n";
  314.     return;
  315.     }
  316.  
  317.     mytype = given_type;
  318.     if (mytype == UnknownType && 
  319.     (parent == 0 || parent->type() == List) && print_name == "")
  320.     mytype = Text;
  321.     if (mytype == UnknownType && parent == 0 && is_user_command(print_name))
  322.     mytype = List;
  323.     if (mytype == UnknownType)
  324.     mytype = determine_type(value);
  325.  
  326.     bool ignore_repeats = (parent != 0 && parent->type() == Array);
  327.  
  328.     char perl_type = '\0';
  329.  
  330.     switch (mytype)
  331.     {
  332.  
  333.     case Simple:
  334.     {
  335.     _value = read_simple_value(value, depth, ignore_repeats);
  336. #if LOG_CREATE_VALUES
  337.     clog << mytype << ": " << quote(_value) << "\n";
  338. #endif
  339.     perl_type = '$';
  340.     break;
  341.     }
  342.  
  343.     case Text:
  344.     {
  345.     // Read in entire text
  346.     _value = value;
  347.     value = value.from(int(value.length())); // assigns ""
  348. #if LOG_CREATE_VALUES
  349.     clog << mytype << ": " << quote(_value) << "\n";
  350. #endif
  351.     perl_type = '$';
  352.     break;
  353.     }
  354.  
  355.     case Pointer:
  356.     {
  357.     _value = read_pointer_value(value, ignore_repeats);
  358.     _dereferenced = false;
  359.  
  360. #if LOG_CREATE_VALUES
  361.     clog << mytype << ": " << quote(_value) << "\n";
  362. #endif
  363.     // Hide vtable pointers.
  364.     if (_value.contains("virtual table") || _value.contains("vtable"))
  365.         myexpanded = false;
  366.     perl_type = '$';
  367.  
  368.     // In Perl, pointers may be followed by indented `pointed to'
  369.     // info.  Skip this.
  370.     if (gdb->type() == PERL)
  371.     {
  372.         while (value.contains("\n  ", 0))
  373.         {
  374.         value = value.after("\n  ");
  375.         value = value.from("\n");
  376.         }
  377.     }        
  378.     break;
  379.     }
  380.  
  381.     case Array:
  382.     {
  383.     string base = normalize_base(myfull_name);
  384.  
  385.     _alignment = Vertical;
  386.  
  387. #if LOG_CREATE_VALUES
  388.     clog << mytype << ": " << "\n";
  389. #endif
  390.  
  391.     read_array_begin(value, myaddr);
  392.  
  393.     // Check for `vtable entries' prefix.
  394.     string vtable_entries = read_vtable_entries(value);
  395.     if (vtable_entries != "")
  396.     {
  397.         _children += parse_child(depth, vtable_entries, myfull_name);
  398.     }
  399.  
  400.     // Read the array elements.  Assume that the type is the
  401.     // same across all elements.
  402.     DispValueType member_type = UnknownType;
  403.     if (!_have_index_base)
  404.     {
  405.         _index_base = index_base(base, depth);
  406.         _have_index_base = true;
  407.     }
  408.     int array_index = _index_base;
  409.  
  410.     // The array has at least one element.  Otherwise, GDB
  411.     // would treat it as a pointer.
  412.     do {
  413.         char *repeated_value = value;
  414.         string member_name = 
  415.         gdb->index_expr("", itostring(array_index++));
  416.         DispValue *dv = parse_child(depth, value,
  417.                     add_member_name(base, member_name), 
  418.                     member_name, member_type);
  419.         member_type = dv->type();
  420.         _children += dv;
  421.  
  422.         int repeats = read_repeats(value);
  423.  
  424.         if (expand_repeated_values)
  425.         {
  426.         // Create one value per repeat
  427.         while (--repeats > 0)
  428.         {
  429.             member_name = 
  430.             gdb->index_expr("", itostring(array_index++));
  431.             string val = repeated_value;
  432.             DispValue *repeated_dv = 
  433.             parse_child(depth, val, 
  434.                     add_member_name(base, member_name),
  435.                     member_name, member_type);
  436.             _children += repeated_dv;
  437.         }
  438.         }
  439.         else
  440.         {
  441.         // Show repetition in member
  442.         if (repeats > 1)
  443.         {
  444.             array_index--;
  445.  
  446. #if 0
  447.             // We use the GDB `artificial array' notation here,
  448.             // since repeat recognition is supported in GDB only.
  449.             member_name += "@" + itostring(repeats);
  450.  
  451.             dv->full_name() = add_member_name(base, member_name);
  452.             dv->name()      = member_name;
  453. #endif
  454.             dv->repeats()   = repeats;
  455.  
  456.             array_index += repeats;
  457.         }
  458.         }
  459.  
  460.         if (background(value.length()))
  461.         {
  462.         init(parent, depth, value);
  463.         return;
  464.         }
  465.     } while (read_array_next(value));
  466.     read_array_end(value);
  467.  
  468.     // Expand only if at top-level.
  469.     myexpanded = (depth == 0 || nchildren() <= 1);
  470.  
  471. #if LOG_CREATE_VALUES
  472.     clog << mytype << " has " << nchildren() << " members\n";
  473. #endif
  474.     perl_type = '@';
  475.     break;
  476.     }
  477.  
  478.     case List:
  479.     // Some DBXes issue the local variables via a frame line, just
  480.     // like `set_date(d = 0x10003060, day_of_week = Sat, day = 24,
  481.     // month = 12, year = 1994)'.  Make this more readable.
  482.     munch_dump_line(value);
  483.  
  484.     // FALL THROUGH
  485.     case Struct:
  486.     {
  487.     bool found_struct_begin   = false;
  488.     bool read_multiple_values = false;
  489.     
  490. #if LOG_CREATE_VALUES
  491.     clog << mytype << " " << quote(myfull_name) << "\n";
  492. #endif
  493.     string member_prefix = myfull_name;
  494.     string member_suffix = "";
  495.     if (mytype == List)
  496.     {
  497.         member_prefix = "";
  498.         read_multiple_values = true;
  499.     }
  500.     else
  501.     {
  502.         // In C and Java, `*' binds tighter than `.'
  503.         if (member_prefix.contains('*', 0))
  504.         {
  505.         if (gdb->program_language() == LANGUAGE_C)
  506.         {
  507.             // Use the C `->' operator instead
  508.             member_prefix.del("*");
  509.             if (member_prefix.contains('(', 0) &&
  510.             member_prefix.contains(')', -1))
  511.             member_prefix = unquote(member_prefix);
  512.  
  513. #if RUNTIME_REGEX
  514.             static regex rxchain("[-a-zA-Z0-9::_>.`]+");
  515. #endif
  516.             if (member_prefix.matches(rxchain))
  517.             {
  518.             // Simple chain of identifiers - prepend `->'
  519.             member_prefix += "->";
  520.             }
  521.             else
  522.             {
  523.             member_prefix.prepend("(");
  524.             member_prefix += ")->";
  525.             }
  526.         }
  527.         else
  528.         {
  529.             member_prefix.prepend("(");
  530.             member_prefix += ").";
  531.         }
  532.         }
  533.         else if (gdb->program_language() == LANGUAGE_PERL)
  534.         {
  535.         // In Perl, members of A are accessed as A{...}
  536.         member_prefix = normalize_base(member_prefix) + "{";
  537.         member_suffix = "}";
  538.         }
  539.         else
  540.         {
  541.         // In all other languages, members are accessed as A.B
  542.         member_prefix = normalize_base(member_prefix) + ".";
  543.         }
  544.  
  545.         // In case we do not find a struct beginning, read only one value
  546.         found_struct_begin = read_struct_begin(value, myaddr);
  547.         read_multiple_values = found_struct_begin;
  548.     }
  549.  
  550.     bool more_values = true;
  551.     while (more_values)
  552.     {
  553.         string member_name = read_member_name(value);
  554.  
  555.         if (member_name == "")
  556.         {
  557.         // Some struct stuff that is not a member
  558.         char *old_value = value;
  559.  
  560.         DispValue *dv = parse_child(depth, value, myfull_name, "");
  561.  
  562.         bool consume = true;
  563.         if (value == old_value)
  564.         {
  565.             // Nothing consumed - stop here
  566.             consume = false;
  567.         }
  568.         else if (dv->type() == Simple && dv->value() == "")
  569.         {
  570.             // Empty value - stop here
  571.             consume = false;
  572.         }
  573.         else if (dv->type() == Struct)
  574.         {
  575.             // What's this - a struct within a struct?  Just
  576.             // adopt the members.
  577.             // (This happens when we finally found the struct
  578.             // after having read all the AIX DBX base classes.)
  579.             for (int i = 0; i < dv->nchildren(); i++)
  580.             {
  581.             DispValue *dv2 = dv->child(i)->link();
  582.             _children += dv2;
  583.             }
  584.             consume = false;
  585.         }
  586.  
  587.         if (!consume)
  588.         {
  589.             // Discard the value just read
  590.             dv->unlink();
  591.         }
  592.         else
  593.         {
  594.             _children += dv;
  595.         }
  596.  
  597.         more_values = read_multiple_values && read_struct_next(value);
  598.         }
  599.         else if (is_BaseClass_name(member_name))
  600.         {
  601.         // Base class member
  602.         DispValue *dv = 
  603.             parse_child(depth, value, myfull_name, member_name);
  604.         _children += dv;
  605.  
  606.         more_values = read_multiple_values && read_struct_next(value);
  607.  
  608.         // Skip a possible `members of CLASS:' prefix
  609.         read_members_prefix(value);
  610.  
  611.         // AIX DBX does not place a separator between base
  612.         // classes and the other members, so we always
  613.         // continue reading after having found a base
  614.         // class.  After all, the own class members are
  615.         // still missing.
  616.         if (mytype == Struct && !found_struct_begin)
  617.             more_values = true;
  618.         }
  619.         else
  620.         {
  621.         // Ordinary member
  622.         string full_name;
  623.  
  624.         if (member_name == " ")
  625.         {
  626.             // Anonymous union
  627.             full_name = myfull_name;
  628.         }
  629.         else if (member_name.contains('.') && gdb->has_quotes())
  630.         {
  631.             // The member name contains `.' => quote it.  This
  632.             // happens with vtable pointers on Linux (`_vptr.').
  633.             full_name = member_prefix + quote(member_name, '\'') + 
  634.             member_suffix;
  635.         }
  636.         else
  637.         {
  638.             // Ordinary member
  639.             full_name = member_prefix + member_name + member_suffix;
  640.         }
  641.  
  642.         _children += parse_child(depth, value, full_name, member_name);
  643.         more_values = read_multiple_values && read_struct_next(value);
  644.         }
  645.  
  646.         if (background(value.length()))
  647.         {
  648.         init(parent, depth, value);
  649.         return;
  650.         }
  651.     }
  652.  
  653.     if (mytype == List && value != "")
  654.     {
  655.         // Add remaining value as text
  656.         _children += parse_child(depth, value, "");
  657.     }
  658.  
  659.     if (found_struct_begin)
  660.     {
  661.         // Skip the remainder
  662.         read_struct_end(value);
  663.     }
  664.  
  665.     // Expand only if at top-level.
  666.     myexpanded = (depth == 0 || nchildren() <= 1);
  667.  
  668. #if LOG_CREATE_VALUES
  669.     clog << mytype << " "
  670.          << quote(myfull_name)
  671.          << " has " << nchildren() << " members\n";
  672. #endif
  673.  
  674.     perl_type = '%';
  675.     break;
  676.     }
  677.  
  678.     case Reference:
  679.     {
  680.     myexpanded = true;
  681.  
  682.     int sep = value.index('@');
  683.     sep = value.index(':', sep);
  684.  
  685.     string ref = value.before(sep);
  686.     value = value.after(sep);
  687.  
  688.     string addr = gdb->address_expr(myfull_name);
  689.  
  690.     _children += parse_child(depth, ref, addr, myfull_name, Pointer);
  691.     _children += parse_child(depth, value, myfull_name);
  692.  
  693.     if (background(value.length()))
  694.     {
  695.         init(parent, depth, value);
  696.         return;
  697.     }
  698.  
  699.     perl_type = '$';    // No such thing in Perl...
  700.     break;
  701.     }
  702.  
  703.     case Sequence:
  704.     case UnknownType:
  705.     assert(0);
  706.     abort();
  707.     }
  708.  
  709.     // Handle trailing stuff (`sequences')
  710.     if (parent == 0 || parent->type() != Sequence)
  711.     {
  712.     bool need_clear = true;
  713.     while (sequence_pending(value, parent))
  714.     {
  715.         if (need_clear)
  716.         {
  717. #if LOG_CREATE_VALUES
  718.         clog << "Sequence detected at " << quote(value) << "\n";
  719. #endif
  720.  
  721.         clear();
  722.         value = initial_value;
  723.  
  724.         mytype = Sequence;
  725.  
  726. #if LOG_CREATE_VALUES
  727.         clog << mytype << " " << quote(myfull_name) << "\n";
  728. #endif
  729.  
  730.         need_clear = false;
  731.         }
  732.         
  733.         char *old_value = value;
  734.  
  735.         DispValue *dv = parse_child(depth, value, myfull_name);
  736.  
  737.         if (value == old_value)
  738.         {
  739.         // Nothing consumed - stop here
  740.         dv->unlink();
  741.         break;
  742.         }
  743.         else if (dv->type() == Simple && dv->value() == "")
  744.         {
  745.         // Empty value - ignore
  746.         dv->unlink();
  747.         }
  748.         else
  749.         {
  750.         _children += dv;
  751.         }
  752.     }
  753.  
  754. #if LOG_CREATE_VALUES
  755.     if (!need_clear)
  756.     {
  757.         clog << mytype << " "
  758.          << quote(myfull_name)
  759.          << " has " << nchildren() << " members\n";
  760.     }
  761. #endif
  762.     }
  763.  
  764.     if (gdb->program_language() == LANGUAGE_PERL && is_perl_prefix(perl_type))
  765.     {
  766.     // Set new type
  767.     if (myfull_name != "" && is_perl_prefix(myfull_name[0]))
  768.         myfull_name[0] = perl_type;
  769.     }
  770.  
  771.     background(value.length());
  772.     changed = true;
  773. }
  774.  
  775. // Destructor helper
  776. void DispValue::clear()
  777. {
  778.     for (int i = 0; i < nchildren(); i++)
  779.     child(i)->unlink();
  780.  
  781.     static DispValueArray empty(0);
  782.     _children = empty;
  783.  
  784.     if (plotter() != 0)
  785.     {
  786.     plotter()->terminate();
  787.     _plotter = 0;
  788.     }
  789.  
  790.     clear_cached_box();
  791. }
  792.  
  793. //-----------------------------------------------------------------------------
  794. // Cache
  795. //-----------------------------------------------------------------------------
  796.  
  797. void DispValue::validate_box_cache()
  798. {
  799.     for (int i = 0; i < nchildren(); i++)
  800.     {
  801.     child(i)->validate_box_cache();
  802.  
  803.     if (child(i)->cached_box() == 0 ||
  804.         child(i)->_cached_box_change > _cached_box_change)
  805.     {
  806.         clear_cached_box();
  807.         break;
  808.     }
  809.     }
  810. }
  811.  
  812. //-----------------------------------------------------------------------------
  813. // Resources
  814. //-----------------------------------------------------------------------------
  815.  
  816. // Return dereferenced name.  Only if type() == Pointer.
  817. string DispValue::dereferenced_name() const
  818. {
  819.     switch (mytype)
  820.     {
  821.     case Pointer:
  822.     {
  823.     string f = full_name();
  824.     if (f.contains('/', 0))
  825.         f = f.from(2);    // Skip /FMT expressions
  826.  
  827.     return deref(f);
  828.     }
  829.  
  830.     case Simple:
  831.     case Text:
  832.     case Array:
  833.     case Sequence:
  834.     case List:
  835.     case Struct:
  836.     case Reference:
  837.     return "";
  838.  
  839.     case UnknownType:
  840.     assert(0);
  841.     abort();
  842.     }
  843.  
  844.     return "";
  845. }
  846.  
  847.  
  848. //-----------------------------------------------------------------------------
  849. // Modifiers
  850. //-----------------------------------------------------------------------------
  851.  
  852. // Expand.  Like expand(), but expand entire subtree
  853. void DispValue::expandAll(int depth)
  854. {
  855.     if (depth == 0)
  856.     return;
  857.  
  858.     _expand();
  859.  
  860.     for (int i = 0; i < nchildren(); i++)
  861.     {
  862.     child(i)->expandAll(depth - 1);
  863.     }
  864. }
  865.  
  866. // Collapse.  Like collapse(), but collapse entire subtree
  867. void DispValue::collapseAll(int depth)
  868. {
  869.     if (depth == 0)
  870.     return;
  871.  
  872.     _collapse();
  873.  
  874.     for (int i = 0; i < nchildren(); i++)
  875.     {
  876.     child(i)->collapseAll(depth - 1);
  877.     }
  878. }
  879.  
  880. // Count expanded nodes in tree
  881. int DispValue::expandedAll() const
  882. {
  883.     int count = 0;
  884.     if (expanded())
  885.     count++;
  886.     for (int i = 0; i < nchildren(); i++)
  887.     count += child(i)->expandedAll();
  888.  
  889.     return count;
  890. }
  891.  
  892. // Count collapsed nodes in tree
  893. int DispValue::collapsedAll() const
  894. {
  895.     int count = 0;
  896.     if (collapsed())
  897.     count++;
  898.     for (int i = 0; i < nchildren(); i++)
  899.     count += child(i)->collapsedAll();
  900.  
  901.     return count;
  902. }
  903.  
  904.  
  905. // Return height of entire tree
  906. int DispValue::height() const
  907. {
  908.     int d = 0;
  909.  
  910.     for (int i = 0; i < nchildren(); i++)
  911.     d = max(d, child(i)->height());
  912.  
  913.     return d + 1;
  914. }
  915.  
  916. // Return height of expanded tree
  917. int DispValue::heightExpanded() const
  918. {
  919.     if (collapsed())
  920.     return 0;
  921.  
  922.     int d = 0;
  923.  
  924.     for (int i = 0; i < nchildren(); i++)
  925.     {
  926.     if (child(i)->collapsed())
  927.         return 1;
  928.  
  929.     d = max(d, child(i)->heightExpanded());
  930.     }
  931.  
  932.     return d + 1;
  933. }
  934.  
  935. void DispValue::set_alignment(DispValueAlignment alignment)
  936. {
  937.     if (_alignment == alignment)
  938.     return;
  939.  
  940.     _alignment = alignment;
  941.     clear_cached_box();
  942.  
  943.     if (type() == Simple && plotter() != 0)
  944.     plot();
  945. }
  946.  
  947.  
  948. //-----------------------------------------------------------------------------
  949. // Update values
  950. //-----------------------------------------------------------------------------
  951.  
  952. // Update values from VALUE.  Set WAS_CHANGED iff value changed; Set
  953. // WAS_INITIALIZED iff type changed.  If TYPE is given, use TYPE as
  954. // type instead of inferring it.  Note: THIS can no more be referenced
  955. // after calling this function; use the returned value instead.
  956. DispValue *DispValue::update(string& value, 
  957.                  bool& was_changed, bool& was_initialized,
  958.                  DispValueType given_type)
  959. {
  960.     DispValue *source = parse(0, 0, value, 
  961.                   full_name(), name(), given_type);
  962.  
  963.     if (background(value.length()))
  964.     {
  965.     // Aborted while parsing - use SOURCE instead of original
  966.     DispValue *ret = source->link();
  967.     ret->changed = was_changed = was_initialized = true;
  968.     unlink();
  969.     return ret;
  970.     }
  971.  
  972.     DispValue *dv = update(source, was_changed, was_initialized);
  973.  
  974.     source->unlink();
  975.  
  976.     if (was_changed || was_initialized)
  977.     clear_cached_box();
  978.  
  979.     return dv;
  980. }
  981.  
  982.  
  983. // Update values from SOURCE.  Set WAS_CHANGED iff value changed; Set
  984. // WAS_INITIALIZED iff type changed.  Note: Neither THIS nor SOURCE
  985. // can be referenced after calling this function; use the returned
  986. // value instead.
  987. DispValue *DispValue::update(DispValue *source, 
  988.                  bool& was_changed, bool& was_initialized)
  989. {
  990.     bool was_plotted = (plotter() != 0);
  991.  
  992.     DispValue *dv = _update(source, was_changed, was_initialized);
  993.     if (was_plotted && was_changed)
  994.     dv->plot();
  995.  
  996.     return dv;
  997. }
  998.  
  999. DispValue *DispValue::_update(DispValue *source, 
  1000.                   bool& was_changed, bool& was_initialized)
  1001. {
  1002.     if (source == this)
  1003.     {
  1004.     // We're updated from ourselves -- ignore it all.  
  1005.     // This happens when a cluster is updated from the values of
  1006.     // the clustered dislays.
  1007.     if (descendant_changed())
  1008.         was_changed = true;
  1009.  
  1010.     return this;
  1011.     }
  1012.  
  1013.     if (changed)
  1014.     {
  1015.     // Clear `changed' flag
  1016.     changed = false;
  1017.     was_changed = true;
  1018.     }
  1019.  
  1020.     if (source->enabled() != enabled())
  1021.     {
  1022.     myenabled = source->enabled();
  1023.     was_changed = true;
  1024.  
  1025.     // We don't set CHANGED to true since enabled/disabled changes
  1026.     // are merely a change in the view, not a change in the data.
  1027.     }
  1028.  
  1029.     if (source->full_name() == full_name() && source->type() == type())
  1030.     {
  1031.     switch (type())
  1032.     {
  1033.     case Simple:
  1034.     case Text:
  1035.     case Pointer:
  1036.         // Atomic values
  1037.         if (_value != source->value())
  1038.         {
  1039.         _value = source->value();
  1040.         changed = was_changed = true;
  1041.         }
  1042.         return this;
  1043.  
  1044.     case Array:
  1045.         // Array.  Check for 1st element, too.
  1046.         if (_have_index_base != source->_have_index_base &&
  1047.         (_have_index_base && _index_base != source->_index_base))
  1048.         break;
  1049.  
  1050.         // FALL THROUGH
  1051.     case Reference:
  1052.     case Sequence:
  1053.         // Numbered children.  If size changed, we assume
  1054.         // the whole has been changed.
  1055.         if (nchildren() == source->nchildren())
  1056.         {
  1057.         for (int i = 0; i < nchildren(); i++)
  1058.         {
  1059.             // Update each child
  1060.             _children[i] = child(i)->update(source->child(i),
  1061.                             was_changed,
  1062.                             was_initialized);
  1063.         }
  1064.         return this;
  1065.         }
  1066.         break;
  1067.  
  1068.     case List:
  1069.     case Struct:
  1070.     {
  1071.         // Named children.  Check whether names are the same.
  1072.         bool same_members = (nchildren() == source->nchildren());
  1073.  
  1074.         for (int i = 0; same_members && i < nchildren(); i++)
  1075.         {
  1076.         if (child(i)->full_name() != source->child(i)->full_name())
  1077.             same_members = false;
  1078.         }
  1079.  
  1080.         if (same_members)
  1081.         {
  1082.         // Update each child
  1083.         for (int i = 0; i < nchildren(); i++)
  1084.         {
  1085.             _children[i] = child(i)->update(source->child(i),
  1086.                             was_changed,
  1087.                             was_initialized);
  1088.         }
  1089.         return this;
  1090.         }
  1091.  
  1092.         // Members have changed.
  1093.         // Be sure to mark only those members that actually have changed
  1094.         // (i.e. don't mark the entire struct and don't mark new members)
  1095.         // We do so by creating a new list of children.  `Old' children
  1096.         // that still are reported get updated; `new' children are added.
  1097.         DispValueArray new_children;
  1098.         DispValueArray processed_children;
  1099.         for (int j = 0; j < source->nchildren(); j++)
  1100.         {
  1101.         DispValue *c = 0;
  1102.         for (int i = 0; c == 0 && i < nchildren(); i++)
  1103.         {
  1104.             bool processed = false;
  1105.             for (int k = 0; k < processed_children.size(); k++)
  1106.             {
  1107.             if (child(i) == processed_children[k])
  1108.                 processed = true;
  1109.             }
  1110.             if (processed)
  1111.             continue;
  1112.  
  1113.             if (child(i)->full_name() == source->child(j)->full_name())
  1114.             {
  1115.             c = child(i)->update(source->child(j),
  1116.                          was_changed,
  1117.                          was_initialized);
  1118.             processed_children += child(i);
  1119.             }
  1120.         }
  1121.  
  1122.         if (c == 0)
  1123.         {
  1124.             // Child not found -- use source child
  1125.             c = source->child(j)->link();
  1126.         }
  1127.  
  1128.         new_children += c;
  1129.         }
  1130.         _children = new_children;
  1131.         was_changed = was_initialized = true;
  1132.         return this;
  1133.     }
  1134.  
  1135.     case UnknownType:
  1136.         assert(0);
  1137.         abort();
  1138.     }
  1139.     }
  1140.  
  1141.     // Type, name or structure have changed -- use SOURCE instead of original
  1142.     DispValue *ret = source->link();
  1143.     ret->changed = was_changed = was_initialized = true;
  1144.  
  1145.     // Copy the basic settings
  1146.     ret->myexpanded = expanded();
  1147.     ret->dereference(dereferenced());
  1148.     if (vertical_aligned())
  1149.     ret->align_vertical();
  1150.     if (horizontal_aligned())
  1151.     ret->align_horizontal();
  1152.  
  1153.     unlink();
  1154.     return ret;
  1155. }
  1156.  
  1157. // Return true iff this or some descendant changed
  1158. bool DispValue::descendant_changed() const
  1159. {
  1160.     if (changed)
  1161.     return true;
  1162.  
  1163.     for (int i = 0; i < nchildren(); i++)
  1164.     if (child(i)->descendant_changed())
  1165.         return true;
  1166.  
  1167.     return false;
  1168. }
  1169.  
  1170. //-----------------------------------------------------------------------------
  1171. // Find descendant
  1172. //-----------------------------------------------------------------------------
  1173.  
  1174. // Return true iff SOURCE and this are structurally equal.
  1175. // If SOURCE_DESCENDANT (a descendant of SOURCE) is set, 
  1176. // return its equivalent descendant of this in DESCENDANT.
  1177. bool DispValue::structurally_equal(const DispValue *source,
  1178.                    const DispValue *source_descendant,
  1179.                    const DispValue *&descendant) const
  1180. {
  1181.     if (source == source_descendant)
  1182.     descendant = this;
  1183.  
  1184.     if (type() != source->type())
  1185.     return false;        // Differing type
  1186.  
  1187.     switch (type())
  1188.     {
  1189.     case Simple:
  1190.     case Text:
  1191.     case Pointer:
  1192.         return true;    // Structurally equal
  1193.         
  1194.     case Array:
  1195.     {
  1196.         if (nchildren() != source->nchildren())
  1197.         return false;    // Differing size
  1198.  
  1199.         if (_have_index_base != source->_have_index_base)
  1200.         return false;    // Differing base
  1201.  
  1202.         if (_have_index_base && _index_base != source->_index_base)
  1203.         return false;    // Differing base
  1204.  
  1205.         for (int i = 0; i < nchildren(); i++)
  1206.         {
  1207.         DispValue *child = _children[i];
  1208.         DispValue *source_child = source->child(i);
  1209.         bool eq = child->structurally_equal(source_child, 
  1210.                             source_descendant,
  1211.                             descendant);
  1212.  
  1213.         if (!eq)
  1214.             return false;
  1215.         }
  1216.         return true;    // All children structurally equal
  1217.     }
  1218.  
  1219.     case List:
  1220.     case Struct:
  1221.     case Sequence:
  1222.     case Reference:
  1223.     {
  1224.         if (nchildren() != source->nchildren())
  1225.         return false;
  1226.  
  1227.         for (int i = 0; i < nchildren(); i++)
  1228.         {
  1229.         DispValue *child = _children[i];
  1230.         DispValue *source_child = source->child(i);
  1231.         bool eq = child->structurally_equal(source_child, 
  1232.                             source_descendant,
  1233.                             descendant);
  1234.  
  1235.         if (!eq)
  1236.             return false;
  1237.         }
  1238.         return true;    // All children structurally equal
  1239.     }
  1240.  
  1241.     case UnknownType:
  1242.         assert(0);
  1243.         abort();
  1244.     }
  1245.  
  1246.     return false;        // Not found
  1247. }
  1248.  
  1249. //-----------------------------------------------------------------------------
  1250. // Plotting
  1251. //-----------------------------------------------------------------------------
  1252.  
  1253. int DispValue::can_plot() const
  1254. {
  1255.     if (can_plot3d())
  1256.     return 3;
  1257.  
  1258.     if (can_plot2d())
  1259.     return 2;
  1260.  
  1261.     if (can_plot1d())
  1262.     return 1;
  1263.  
  1264.     // Search for plottable array children
  1265.     int ndim = 0;
  1266.     for (int i = 0; i < nchildren(); i++)
  1267.     ndim = max(ndim, child(i)->can_plot());
  1268.  
  1269.     return ndim;
  1270. }
  1271.  
  1272. bool DispValue::starts_number(char c)
  1273. {
  1274.     return c == '.' || c == '+' || c == '-' || isdigit(c);
  1275. }
  1276.  
  1277. bool DispValue::can_plot1d() const
  1278. {
  1279.     if (type() != Simple)
  1280.     return false;
  1281.  
  1282.     const string& v = value();
  1283.     if (v.length() == 0)
  1284.     return false;    // Empty value
  1285.     if (!starts_number(v[0]))
  1286.     return false;    // Not a numeric value
  1287.  
  1288.     return true;
  1289. }
  1290.  
  1291. bool DispValue::can_plot2d() const
  1292. {
  1293.     if (type() != Array)
  1294.     return false;
  1295.  
  1296.     for (int i = 0; i < nchildren(); i++)
  1297.     {
  1298.     if (!child(i)->can_plot1d())
  1299.         return false;
  1300.     }
  1301.  
  1302.     return true;
  1303. }
  1304.  
  1305. bool DispValue::can_plot3d() const
  1306. {
  1307.     if (type() != Array)
  1308.     return false;
  1309.  
  1310.     int grandchildren = -1;
  1311.     for (int i = 0; i < nchildren(); i++)
  1312.     {
  1313.     if (!child(i)->can_plot2d())
  1314.         return false;
  1315.  
  1316.     if (i == 0)
  1317.         grandchildren = child(i)->nchildren_with_repeats();
  1318.     else if (child(i)->nchildren_with_repeats() != grandchildren)
  1319.         return false;    // Differing number of grandchildren
  1320.     }
  1321.  
  1322.     return true;
  1323. }
  1324.  
  1325. int DispValue::nchildren_with_repeats() const
  1326. {
  1327.     int sum = 0;
  1328.     for (int i = 0; i < nchildren(); i++)
  1329.     sum += child(i)->repeats();
  1330.     return sum;
  1331. }
  1332.  
  1333. // Return a title for NAME
  1334. string DispValue::make_title(const string& name)
  1335. {
  1336.     if (!is_user_command(name))
  1337.     return name;
  1338.  
  1339.     string title = user_command(name);
  1340.     if (title.contains("graph ", 0))
  1341.     title = title.after("graph ");
  1342.     else if (title.contains("info ", 0))
  1343.     title = title.after("info ");
  1344.     else if (title.contains(" "))
  1345.     title = title.before(" ");
  1346.     if (title.length() > 0)
  1347.     title = toupper(title[0]) + title.after(0);
  1348.  
  1349.     return title;
  1350. }
  1351.  
  1352. void DispValue::plot() const
  1353. {
  1354.     int ndim = can_plot();
  1355.     if (ndim == 0)
  1356.     return;
  1357.  
  1358.     if (plotter() == 0)
  1359.     {
  1360.     string title = make_title(full_name());
  1361.     ((DispValue *)this)->_plotter = new_plotter(title, (DispValue *)this);
  1362.     if (plotter() == 0)
  1363.         return;
  1364.  
  1365.     plotter()->addHandler(Died, PlotterDiedHP, (void *)this);
  1366.     }
  1367.  
  1368.     plotter()->plot_2d_settings = app_data.plot_2d_settings;
  1369.     plotter()->plot_3d_settings = app_data.plot_3d_settings;
  1370.  
  1371.     _plot(plotter(), ndim);
  1372.  
  1373.     plotter()->flush();
  1374. }
  1375.  
  1376. void DispValue::_plot(PlotAgent *plotter, int ndim) const
  1377. {
  1378.     if (can_plot3d())
  1379.     {
  1380.     plot3d(plotter, ndim);
  1381.     return;
  1382.     }
  1383.  
  1384.     if (can_plot2d())
  1385.     {
  1386.     plot2d(plotter, ndim);
  1387.     return;
  1388.     }
  1389.  
  1390.     if (can_plot1d())
  1391.     {
  1392.     plot1d(plotter, ndim);
  1393.     return;
  1394.     }
  1395.  
  1396.     // Plot all array children into one window
  1397.     for (int i = 0; i < nchildren(); i++)
  1398.     child(i)->_plot(plotter, ndim);
  1399. }
  1400.  
  1401. void DispValue::replot() const
  1402. {
  1403.     if (plotter() != 0)
  1404.     plot();
  1405.  
  1406.     for (int i = 0; i < nchildren(); i++)
  1407.     child(i)->replot();
  1408. }
  1409.  
  1410. string DispValue::num_value() const
  1411. {
  1412.     const string& v = value();
  1413.     if (v.contains(rxdouble, 0))
  1414.     return v.through(rxdouble);
  1415.  
  1416.     if (v.contains(rxint, 0))
  1417.     return v.through(rxint);
  1418.  
  1419.     return "0";            // Illegal value
  1420. }
  1421.  
  1422. void DispValue::plot1d(PlotAgent *plotter, int ndim) const
  1423. {
  1424.     plotter->start_plot(full_name(), ndim);
  1425.  
  1426.     string val = num_value();
  1427.  
  1428.     if (!has_plot_alignment())
  1429.     {
  1430.     // Determine initial alignment.
  1431.     // By default, this is plotted horizontally.
  1432.     DispValueAlignment alignment = Horizontal;
  1433.  
  1434.     // But if this is an integral value that lies within the index
  1435.     // limits of a previously plotted array, plot it vertically.
  1436.     if (!val.contains('.'))
  1437.     {
  1438.         int v = atoi(val);
  1439.         if (plotter->min_x() < plotter->max_x() &&
  1440.         v >= plotter->min_x() && v <= plotter->max_x())
  1441.         {
  1442.         alignment = Vertical;
  1443.         }
  1444.     }
  1445.     
  1446.     ((DispValue *)this)->_alignment = alignment;
  1447.     ((DispValue *)this)->_has_plot_alignment = true;
  1448.     }
  1449.  
  1450.     plotter->add_point(val, horizontal_aligned() ? 0 : 1);
  1451.     plotter->end_plot();
  1452. }
  1453.  
  1454. void DispValue::plot2d(PlotAgent *plotter, int ndim) const
  1455. {
  1456.     plotter->start_plot(full_name(), ndim);
  1457.  
  1458.     int index;
  1459.     if (_have_index_base)
  1460.     index = _index_base;
  1461.     else
  1462.     index = gdb->default_index_base();
  1463.  
  1464.     for (int i = 0; i < nchildren(); i++)
  1465.     {
  1466.     DispValue *c = child(i);
  1467.     for (int ii = 0; ii < c->repeats(); ii++)
  1468.     {
  1469.         plotter->add_point(index++, c->num_value());
  1470.     }
  1471.     }
  1472.  
  1473.     plotter->end_plot();
  1474. }
  1475.  
  1476. void DispValue::plot3d(PlotAgent *plotter, int ndim) const
  1477. {
  1478.     plotter->start_plot(full_name(), ndim);
  1479.  
  1480.     int index;
  1481.     if (_have_index_base)
  1482.     index = _index_base;
  1483.     else
  1484.     index = gdb->default_index_base();
  1485.  
  1486.     for (int i = 0; i < nchildren(); i++)
  1487.     {
  1488.     DispValue *c = child(i);
  1489.     int c_index;
  1490.     if (c->_have_index_base)
  1491.         c_index = c->_index_base;
  1492.     else
  1493.         c_index = gdb->default_index_base();
  1494.  
  1495.     for (int ii = 0; ii < c->repeats(); ii++)
  1496.     {
  1497.         for (int j = 0; j < c->nchildren(); j++)
  1498.         {
  1499.         DispValue *cc = c->child(j);
  1500.         for (int jj = 0; jj < cc->repeats(); jj++)
  1501.             plotter->add_point(index, c_index++, cc->num_value());
  1502.         }
  1503.  
  1504.         index++;
  1505.     }
  1506.  
  1507.     plotter->add_break();
  1508.     }
  1509.     
  1510.     plotter->end_plot();
  1511. }
  1512.  
  1513. void DispValue::PlotterDiedHP(Agent *source, void *client_data, void *)
  1514. {
  1515.     (void) source;        // Use it
  1516.  
  1517.     DispValue *dv = (DispValue *)client_data;
  1518.  
  1519.     assert(source == dv->plotter());
  1520.  
  1521.     dv->plotter()->removeHandler(Died, PlotterDiedHP, (void *)dv);
  1522.     dv->_plotter = 0;
  1523. }
  1524.  
  1525. // Print plots to FILENAME
  1526. void DispValue::print_plots(const string& filename, 
  1527.                 const PrintGC& gc) const
  1528. {
  1529.     // Print self
  1530.     if (plotter() != 0)
  1531.     plotter()->print(filename, gc);
  1532.  
  1533.     // Print children
  1534.     for (int i = 0; i < nchildren(); i++)
  1535.     child(i)->print_plots(filename, gc);
  1536. }
  1537.  
  1538.  
  1539. // Show whether plot is active
  1540. void DispValue::set_plot_state(const string& state) const
  1541. {
  1542.     // Handle self
  1543.     if (plotter() != 0)
  1544.     plotter()->set_state(state);
  1545.  
  1546.     // Handle children
  1547.     for (int i = 0; i < nchildren(); i++)
  1548.     child(i)->set_plot_state(state);
  1549. }
  1550.  
  1551.  
  1552. //-----------------------------------------------------------------------------
  1553. // Background processing
  1554. //-----------------------------------------------------------------------------
  1555.  
  1556. static bool nop(int) { return false; }
  1557.  
  1558. bool (*DispValue::background)(int processed) = nop;
  1559.