help-gnustep
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: objc patch to gdb


From: Jeff Teunissen
Subject: Re: objc patch to gdb
Date: Thu, 18 Apr 2002 16:10:20 -0400

Bob Gilson wrote:

> Jeff Teunissen wrote:
> 
> >Adam Fedor wrote:

[snip]

> > > I don't think there is an up-to-date patch available for gdb.
> > > Although the gdb maintainer himself inticated he might have time to
> > > integrate Apple's patch into the main gdb after 5.2 is released, so
> > > there is at least some hope.
> >
> > There is. I have one based on gdb 5.1.
> 
> Please share it with the rest of us.

Had to find it first. :)

-- 
| Jeff Teunissen  -=-  Pres., Dusk To Dawn Computing  -=-  deek @ d2dc.net
| GPG: 1024D/9840105A   7102 808A 7733 C2F3 097B  161B 9222 DAB8 9840 105A
| Core developer, The QuakeForge Project        http://www.quakeforge.net/
| Specializing in Debian GNU/Linux              http://www.d2dc.net/~deek/
diff -Naur gdb-5.0.cvs20011007/gdb/Makefile.in 
gdb-5.0.cvs20011007.new/gdb/Makefile.in
--- gdb-5.0.cvs20011007/gdb/Makefile.in Mon Jul 30 13:00:38 2001
+++ gdb-5.0.cvs20011007.new/gdb/Makefile.in     Sat Nov 24 22:50:52 2001
@@ -526,6 +526,7 @@
        jv-exp.y jv-lang.c jv-valprint.c jv-typeprint.c \
        m2-exp.y m2-lang.c m2-typeprint.c m2-valprint.c main.c maint.c \
        memattr.c mem-break.c minsyms.c mipsread.c nlmread.c objfiles.c \
+       objc-exp.y objc-lang.c \
        p-exp.y p-lang.c p-typeprint.c p-valprint.c parse.c \
        printcmd.c remote.c remote-nrom.c scm-exp.c scm-lang.c \
        scm-valprint.c signals.c source.c stabsread.c stack.c symfile.c \
@@ -638,6 +639,7 @@
        c-lang.h ch-lang.h f-lang.h \
        jv-lang.h \
        m2-lang.h  p-lang.h \
+       objc-lang.h \
        complaints.h valprint.h \
        29k-share/udi/udiids.h 29k-share/udi_soc nindy-share/b.out.h \
        nindy-share/block_io.h nindy-share/coff.h \
@@ -693,6 +695,7 @@
        varobj.o wrapper.o \
        jv-lang.o jv-valprint.o jv-typeprint.o \
        m2-lang.o p-lang.o p-typeprint.o p-valprint.o \
+       objc-lang.o \
        scm-exp.o scm-lang.o scm-valprint.o complaints.o typeprint.o \
        c-typeprint.o ch-typeprint.o f-typeprint.o m2-typeprint.o \
        c-valprint.o cp-valprint.o ch-valprint.o f-valprint.o m2-valprint.o \
@@ -713,7 +716,7 @@
        f-exp.tab.c m2-exp.tab.c p-exp.tab.c
 YYOBJ = c-exp.tab.o \
        jv-exp.tab.o \
-       f-exp.tab.o m2-exp.tab.o p-exp.tab.o
+       f-exp.tab.o m2-exp.tab.o p-exp.tab.o objc-exp.tab.o
 
 # Things which need to be built when making a distribution.
 
@@ -1062,6 +1065,19 @@
        -rm jv-exp.tmp
        mv jv-exp.new ./jv-exp.tab.c
 
+objc-exp.tab.o: objc-exp.tab.c
+objc-exp.tab.c: objc-exp.y
+       $(YACC) $(YFLAGS) $(srcdir)/objc-exp.y
+       -sed -e '/extern.*malloc/d' \
+            -e '/extern.*realloc/d' \
+            -e '/extern.*free/d' \
+            -e '/include.*malloc.h/d' \
+            -e 's/malloc/xmalloc/g' \
+            -e 's/realloc/xrealloc/g' \
+         < y.tab.c > objc-exp.new
+       -rm y.tab.c
+       mv objc-exp.new ./objc-exp.tab.c
+
 f-exp.tab.o: f-exp.tab.c
 f-exp.tab.c: f-exp.y c-exp.tab.c
        $(SHELL) $(YLWRAP) "$(YACC)" $(srcdir)/f-exp.y  y.tab.c f-exp.tmp -- 
$(YFLAGS) 
@@ -1242,6 +1258,9 @@
 c-valprint.o: c-valprint.c $(defs_h) $(expression_h) $(gdbtypes_h) \
        language.h $(symtab_h) valprint.h $(value_h) $(cp_abi_h)
 
+objc-lang.o: objc-lang.c objc-lang.h $(defs_h) $(expression_h) $(gdbtypes_h) \
+       language.h parser-defs.h $(symtab_h)
+
 f-lang.o: f-lang.c f-lang.h $(defs_h) $(expression_h) $(gdbtypes_h) \
        language.h parser-defs.h $(symtab_h) $(gdb_string_h)
 
@@ -2123,6 +2142,10 @@
        $(CC) -c $(INTERNAL_WARN_CFLAGS) $(NO_WERROR_CFLAGS) $<
 
 c-exp.tab.o: c-exp.tab.c c-lang.h $(defs_h) $(expression_h) \
+       $(gdbtypes_h) language.h parser-defs.h $(symtab_h) $(value_h) \
+       $(bfd_h) objfiles.h symfile.h
+
+objc-exp.tab.o: objc-exp.tab.c objc-lang.h $(defs_h) $(expression_h) \
        $(gdbtypes_h) language.h parser-defs.h $(symtab_h) $(value_h) \
        $(bfd_h) objfiles.h symfile.h
 
diff -Naur gdb-5.0.cvs20011007/gdb/breakpoint.c 
gdb-5.0.cvs20011007.new/gdb/breakpoint.c
--- gdb-5.0.cvs20011007/gdb/breakpoint.c        Thu Aug  2 20:19:43 2001
+++ gdb-5.0.cvs20011007.new/gdb/breakpoint.c    Sat Nov 24 22:50:52 2001
@@ -4682,9 +4682,11 @@
          current_source_symtab (which is decode_line_1's default).  This
          should produce the results we want almost all of the time while
          leaving default_breakpoint_* alone.  */
-      if (default_breakpoint_valid
-         && (!current_source_symtab
-             || (strchr ("+-", (*address)[0]) != NULL)))
+       char *arg = *address;
+       if (default_breakpoint_valid &&
+          (!current_source_symtab ||
+          (arg && (*arg == '+' || *arg == '-') && 
+           arg[1] != '[')))    /* address might be an objc method name */
        *sals = decode_line_1 (address, 1, default_breakpoint_symtab,
                               default_breakpoint_line, addr_string);
       else
diff -Naur gdb-5.0.cvs20011007/gdb/c-lang.c gdb-5.0.cvs20011007.new/gdb/c-lang.c
--- gdb-5.0.cvs20011007/gdb/c-lang.c    Tue Mar  6 03:21:06 2001
+++ gdb-5.0.cvs20011007.new/gdb/c-lang.c        Sat Nov 24 22:50:52 2001
@@ -29,13 +29,13 @@
 #include "valprint.h"
 
 extern void _initialize_c_language (void);
-static void c_emit_char (int c, struct ui_file * stream, int quoter);
+void c_emit_char (int c, struct ui_file * stream, int quoter);
 
 /* Print the character C on STREAM as part of the contents of a literal
    string whose delimiter is QUOTER.  Note that that format for printing
    characters and strings is language specific. */
 
-static void
+void
 c_emit_char (register int c, struct ui_file *stream, int quoter)
 {
   c &= 0xFF;                   /* Avoid sign bit follies */
diff -Naur gdb-5.0.cvs20011007/gdb/defs.h gdb-5.0.cvs20011007.new/gdb/defs.h
--- gdb-5.0.cvs20011007/gdb/defs.h      Thu Aug  2 20:19:46 2001
+++ gdb-5.0.cvs20011007.new/gdb/defs.h  Sat Nov 24 22:50:52 2001
@@ -213,6 +213,7 @@
     language_m2,               /* Modula-2 */
     language_asm,              /* Assembly language */
     language_scm,              /* Scheme / Guile */
+    language_objc,             /* Objective C */
     language_pascal            /* Pascal */
   };
 
@@ -502,6 +503,10 @@
 
 extern char *chill_demangle (const char *);
 
+/* From objc-lang.c, for the moment. (similarly FIXME) */
+
+extern char *objc_demangle PARAMS ((const char *));
+
 /* From utils.c */
 
 extern void initialize_utils (void);
@@ -600,6 +605,33 @@
 extern void wrap_here (char *);
 
 extern void reinitialize_more_filter (void);
+
+#if    0       // DR 08/23/00
+/* new */
+enum streamtype
+{
+  afile,
+  astring
+};
+
+/* new */
+typedef struct tui_stream
+{
+  enum streamtype ts_streamtype;
+  FILE *ts_filestream;
+  char *ts_strbuf;
+  int ts_buflen;
+} GDB_FILE;
+#endif
+
+extern struct ui_file *gdb_stdout;
+extern struct ui_file *gdb_stderr;
+
+#if 0
+typedef FILE GDB_FILE;
+#define gdb_stdout stdout
+#define gdb_stderr stderr
+#endif
 
 /* Normal results */
 extern struct ui_file *gdb_stdout;
diff -Naur gdb-5.0.cvs20011007/gdb/eval.c gdb-5.0.cvs20011007.new/gdb/eval.c
--- gdb-5.0.cvs20011007/gdb/eval.c      Thu Apr 26 20:19:09 2001
+++ gdb-5.0.cvs20011007.new/gdb/eval.c  Sat Nov 24 22:53:41 2001
@@ -30,6 +30,7 @@
 #include "frame.h"
 #include "language.h"          /* For CAST_IS_CONVERSION */
 #include "f-lang.h"            /* for array bound stuff */
+#include "objc-lang.h"
 #include "cp-abi.h"
 
 /* Defined in symtab.c */
@@ -475,6 +476,17 @@
       if (noside == EVAL_SKIP)
        goto nosideret;
       return value_bitstring (&exp->elts[pc + 2].string, tem);
+      /* NOTE: I think the above call is wrong, and that it should be 
+        value_string(..., tem + 1);  I can't easily test it, since 
+        while Fortran and Chill still use OP_STRING, C and C++ do not.
+        This needs to be verified.  Michael Snyder  */
+
+    case OP_NSSTRING:  /* Objective C Foundation Class NSString constant */
+      tem = longest_to_int (exp->elts[pc + 1].longconst);
+      (*pos) += 3 + BYTES_TO_EXP_ELEM (tem + 1);
+      if (noside == EVAL_SKIP)
+       goto nosideret;
+      return (value_ptr) value_nsstring (&exp->elts[pc + 2].string, tem + 1);
       break;
 
     case OP_ARRAY:
@@ -664,6 +676,158 @@
          return arg2;
        }
 
+    case OP_SELECTOR: {        /* Objective C @selector operator */
+      char *sel = &exp->elts[pc+2].string;
+      int   len = longest_to_int(exp->elts[pc+1].longconst);
+
+      (*pos) += 3 + BYTES_TO_EXP_ELEM (len + 1);
+      if (noside == EVAL_SKIP)
+       goto nosideret;
+
+      if (sel[len] != 0)
+       sel[len] = 0;           /* make sure terminated */
+      return value_from_longest(lookup_pointer_type(builtin_type_void), 
+                               lookup_child_selector(sel));
+    }
+
+    case OP_MSGCALL: {         /* Objective C message (method) call */
+                               /* User wants to call an objc method */
+      value_ptr find_function_in_inferior PARAMS((char *));
+      value_ptr ret;
+      struct symbol *sym;
+      value_ptr gnu_runtime = 0;
+
+      /* First evaluate the target (class or object) of the message */
+      (*pos) += 3;
+      nargs = (longest_to_int (exp->elts[pc + 2].longconst));
+      argvec = (value_ptr *) alloca (sizeof (value_ptr) * (nargs + 5));
+      argvec[1] = evaluate_subexp(0, exp, pos, noside);        /* target */
+
+      if (!value_as_long(argvec[1]))                   /* null target */
+       return value_from_longest(builtin_type_long, 0);
+
+      /* Now lookup the ObjC runtime message dispatcher */
+      if (lookup_minimal_symbol("objc_msgSend", 0, 0))
+       argvec[0] = find_function_in_inferior("objc_msgSend");
+      else     /* NeXT version not found, try GNU variant */
+       {
+         argvec[0] = find_function_in_inferior("objc_msg_lookup");
+         gnu_runtime = argvec[0];
+       }
+
+      /* Verify target responds to method selector */
+
+      if ((tem = lookup_child_selector("respondsTo:"))         == 0 &&
+         (tem = lookup_child_selector("respondsToSelector:")) == 0)
+       /* This logic needs work: not sure of GNU variant's name */
+       /* Must also account for new (NSObject) and old (Object) worlds */
+       error ("no 'respondsTo:' method");
+
+      /* call "respondsToSelector:" method, to make sure that 
+       * the target class implements the user's desired method selector
+       */
+      argvec[2] = value_from_longest(builtin_type_long, tem);
+      argvec[3] = value_from_longest(builtin_type_long, /* user's selector */
+                            exp->elts[pc + 1].longconst);
+      argvec[4] = 0;           /* see if target responds to selector */
+      ret = call_function_by_hand(argvec[0], 3, argvec + 1);
+      if (gnu_runtime)
+       {
+         /* Whereas the NeXT objc runtime function objc_msgSend actually
+          * calls the method, the GNU runtime function objc_msg_lookup
+          * only returns a pointer to it.  We still have to call it.
+          */
+         argvec[0] = ret;              /* prepare to call the method */
+         ret = call_function_by_hand(argvec[0], 3, argvec + 1);
+         argvec[0] = gnu_runtime;      /* prepare to call objc_msg_lookup */
+       }
+
+      if (!value_as_long(ret))                    /* no, it doesn't! */
+       error("Target does not respond to this message selector.");
+
+      /* Try to lookup the method implementation; see if we have a symbol */
+      if ((tem = lookup_child_selector("methodFor:"))         == 0 &&
+         (tem = lookup_child_selector("methodForSelector:")) == 0)
+       /* This logic needs work: not sure of GNU variant's name */
+       /* Must also account for new (NSObject) and old (Object) worlds */
+       error ("no 'methodFor:' method");
+
+      /* call "methodForSelector:" method, to get the address of a 
+       * function method that implements this selector for this class.
+       * If we can find a symbol at that address, then we know the 
+       * return type, parameter types etc.  (that's a good thing).
+       */
+      argvec[2] = value_from_longest(builtin_type_long, tem);
+      ret = call_function_by_hand(argvec[0], 3, argvec + 1);
+      if (gnu_runtime)
+       {
+         /* Whereas the NeXT objc runtime function objc_msgSend actually
+          * calls the method, the GNU runtime function objc_msg_lookup
+          * only returns a pointer to it.  We still have to call it.
+          */
+         argvec[0] = ret;              /* prepare to call the method */
+         ret = call_function_by_hand(argvec[0], 3, argvec + 1);
+         argvec[0] = gnu_runtime;      /* prepare to call objc_msg_lookup */
+       }
+
+      if (value_as_long(ret))          /* got an address */
+       {                               /* is it a high_level symbol? */
+         sym = find_pc_function(value_as_long(ret));
+         /* 
+          * Found a function symbol.  Now we will substitute its
+          * value in place of the message dispatcher (obj_msgSend),
+          * so that we call the method directly instead of thru
+          * the dispatcher.  The main reason for doing this is that
+          * we can now evaluate the return value and parameter values
+          * according to their known data types, in case we need to
+          * do things like promotion, dereferencing, special handling
+          * of structs and doubles, etc.
+          */
+         if (sym)                      /* yes; we will call it directly */
+           argvec[0] = value_of_variable(sym, 0);
+       }
+
+      /* So now we actually call the selector.  Move it's value into [2] */
+      argvec[2] = argvec[3];           /* selector */
+      /* Evaluate the method's user-supplied arguments */
+      for (tem=0; tem < nargs; tem++)  /* args, if any */
+       argvec[tem+3] = evaluate_subexp_with_coercion (exp, pos, noside);
+      argvec[tem+3] = 0;
+      /* Now depending on whether we found a symbol for the method, 
+       * we will either call the runtime dispatcher or the method directly.
+       */
+      if (noside == EVAL_SKIP)
+       goto nosideret;
+      if (noside == EVAL_AVOID_SIDE_EFFECTS)
+       {
+         /* If the return type doesn't look like a function type, call an
+            error.  This can happen if somebody tries to turn a variable into
+            a function call. This is here because people often want to
+            call, eg, strcmp, which gdb doesn't know is a function.  If
+            gdb isn't asked for it's opinion (ie. through "whatis"),
+            it won't offer it. */
+
+         struct type *ftype = TYPE_TARGET_TYPE (VALUE_TYPE (argvec[0]));
+
+         if (ftype)
+           return allocate_value (TYPE_TARGET_TYPE (VALUE_TYPE (argvec[0])));
+         else
+           error ("Expression of type other than \"Method returning ...\" used 
as a method");
+       }
+      ret = call_function_by_hand (argvec[0], nargs + 2, argvec + 1);
+      if (gnu_runtime && argvec[0] == gnu_runtime)
+       {
+         /* Whereas the NeXT objc runtime function objc_msgSend actually
+          * calls the method, the GNU runtime function objc_msg_lookup
+          * only returns a pointer to it.  We still have to call it.
+          */
+         argvec[0] = ret;              /* prepare to call the method */
+         return call_function_by_hand(argvec[0], nargs + 2, argvec + 1);
+       }
+      else
+       return ret;
+    } break;
+  
     case OP_FUNCALL:
       (*pos) += 2;
       op = exp->elts[*pos].opcode;
diff -Naur gdb-5.0.cvs20011007/gdb/expprint.c 
gdb-5.0.cvs20011007.new/gdb/expprint.c
--- gdb-5.0.cvs20011007/gdb/expprint.c  Tue Mar  6 03:21:07 2001
+++ gdb-5.0.cvs20011007.new/gdb/expprint.c      Sat Nov 24 22:50:52 2001
@@ -26,6 +26,7 @@
 #include "value.h"
 #include "language.h"
 #include "parser-defs.h"
+#include "target.h"
 
 #ifdef HAVE_CTYPE_H
 #include <ctype.h>
@@ -174,6 +175,50 @@
       fprintf_unfiltered (stream, "B'<unimplemented>'");
       return;
 
+    case OP_NSSTRING:  /* Objective C Foundation Class NSString constant */
+      nargs = longest_to_int (exp -> elts[pc + 1].longconst);
+      (*pos) += 3 + BYTES_TO_EXP_ELEM (nargs + 1);
+      fputs_filtered ("@\"", stream);
+      LA_PRINT_STRING (stream, &exp->elts[pc + 2].string, nargs, 0, 0);
+      fputs_filtered ("\"", stream);
+      return;
+
+    case OP_MSGCALL: { /* Objective C message (method) call */
+      char *selector;
+      (*pos) += 3;
+      nargs = longest_to_int (exp->elts[pc + 2].longconst);
+      fprintf_unfiltered (stream, "[");
+      print_subexp (exp, pos, stream, PREC_SUFFIX);
+      if (0 == target_read_string (exp->elts[pc + 1].longconst, 
+                                  &selector, 1024, NULL))
+       {
+         error("bad selector");
+         return;
+       }
+      /* NOTE: "selector" was malloc'd by target_read_string; must be freed */
+      if (nargs)
+        {
+         char *s, *nextS;
+         s = alloca(strlen(selector) + 1);
+         strcpy(s, selector);
+         for (tem = 0; tem < nargs; tem++)
+           {
+             nextS = strchr(s, ':');
+             *nextS = '\0';
+             fprintf_unfiltered (stream, " %s: ", s);
+             s = nextS + 1;
+             print_subexp (exp, pos, stream, PREC_ABOVE_COMMA);
+           }
+       }
+      else
+       {
+         fprintf_unfiltered (stream, " %s", selector);
+       }
+      fprintf_unfiltered (stream, "]");
+      free(selector);
+      return;
+    }
+
     case OP_ARRAY:
       (*pos) += 3;
       nargs = longest_to_int (exp->elts[pc + 2].longconst);
@@ -569,6 +614,8 @@
       return "BINOP_MIN";
     case BINOP_MAX:
       return "BINOP_MAX";
+    case BINOP_SCOPE:
+      return "BINOP_SCOPE";
     case STRUCTOP_MEMBER:
       return "STRUCTOP_MEMBER";
     case STRUCTOP_MPTR:
@@ -778,6 +825,7 @@
     case BINOP_EXP:
     case BINOP_MIN:
     case BINOP_MAX:
+    case BINOP_SCOPE:
     case BINOP_INTDIV:
     case BINOP_ASSIGN_MODIFY:
     case BINOP_VAL:
diff -Naur gdb-5.0.cvs20011007/gdb/expression.h 
gdb-5.0.cvs20011007.new/gdb/expression.h
--- gdb-5.0.cvs20011007/gdb/expression.h        Sat May 27 21:12:27 2000
+++ gdb-5.0.cvs20011007.new/gdb/expression.h    Sat Nov 24 22:50:52 2001
@@ -78,6 +78,7 @@
 
     BINOP_MIN,                 /* <? */
     BINOP_MAX,                 /* >? */
+    BINOP_SCOPE,               /* :: */
 
     /* STRUCTOP_MEMBER is used for pointer-to-member constructs.
        X . * Y translates into X STRUCTOP_MEMBER Y.  */
@@ -178,6 +179,12 @@
        making three exp_elements.  */
     OP_FUNCALL,
 
+    /* OP_MSGCALL is followed by a string in the next exp_element and then an
+       integer.  The string is the selector string.  The integer is the number
+       of arguments to the message call.  That many plus one values are used, 
+       the first one being the object pointer.  This is an Objective C message 
*/
+    OP_MSGCALL,
+
     /* This is EXACTLY like OP_FUNCALL but is semantically different.  
        In F77, array subscript expressions, substring expressions
        and function calls are  all exactly the same syntactically. They may 
@@ -275,6 +282,9 @@
        It just comes in a tight (OP_THIS, OP_THIS) pair.  */
     OP_THIS,
 
+    /* Objective C: "@selector" pseudo-operator */
+    OP_SELECTOR,
+  
     /* OP_SCOPE surrounds a type name and a field name.  The type
        name is encoded as one element, but the field name stays as
        a string, which, of course, is variable length.  */
@@ -299,7 +309,10 @@
     OP_NAME,
 
     /* An unparsed expression.  Used for Scheme (for now at least) */
-    OP_EXPRSTRING
+    OP_EXPRSTRING,
+
+    /* An Objective C Foundation Class NSString constant */
+    OP_NSSTRING,
   };
 
 union exp_element
diff -Naur gdb-5.0.cvs20011007/gdb/infrun.c gdb-5.0.cvs20011007.new/gdb/infrun.c
--- gdb-5.0.cvs20011007/gdb/infrun.c    Wed Jul 18 13:25:12 2001
+++ gdb-5.0.cvs20011007.new/gdb/infrun.c        Sat Nov 24 22:51:54 2001
@@ -2850,6 +2850,65 @@
            return;
          }
 
+       
+       /* Check for trampoline code pointing to objc_msgSend && friends.
+        * We must catch this and step into those functions, instead of 
+        * over them, so we can step into the method they eventually call.
+        */
+       if (ecs->stop_func_start != 0)  /* do we have a clue where we are? */
+       {
+           char      *name = ecs->stop_func_name;
+           CORE_ADDR start = ecs->stop_func_start;
+           CORE_ADDR end   = ecs->stop_func_end;
+           
+           if (tmp)    /* skip_trampoline_code scored */
+               find_pc_partial_function(tmp, &name, &start, &end);
+           
+           while (name && *name && *name == '_')
+               name++;
+           if (strncmp(name,   "objc_msg", 8) == 0 &&
+               (strncmp(name+8, "Send",     4) == 0 ||
+                strncmp(name+8, "Forward",  7) == 0))
+           {
+               /* OK: These are method dispatcher functions in the 
+                * ObjC runtime library.  This is how dynamic method
+                * binding happens.  Whenever a method is called, the
+                * call actually goes thru one of these functions, 
+                * which actually decides what method to call and
+                * then calls it.
+                *
+                * In order for GDB to step into the actual method, 
+                * we are going to treat the function that we have 
+                * just stepped into (objc_msgSend etc.) as an
+                * extension of the source line that we have been
+                * stepping in (by setting step_range_start and
+                * step_range_end).  That way, GDB will continue
+                * single-stepping until this function either returns,
+                * or calls into a function for which we have 
+                * line information (ie. the target method).
+                *
+                * NOTE: this scheme depends on the ObjC runtime library
+                * being built without debugging symbols.  Otherwise, 
+                * gdb will simply step into it like any other function
+                * (which might be what you want if you are debugging
+                * the runtime library, but not otherwise!) */
+               
+               if (tmp)
+                   write_pc(tmp);      /* jump directly to the function */
+               step_range_start = start;
+               step_range_end   = end;
+#if    0       // DR 08/22/00
+               goto keep_going;
+#else
+               keep_going (ecs);
+               return;
+#endif
+           }
+       }
+        
+#if    0       // DR 08/22/00
+       step_over_function:
+#endif
        step_over_function (ecs);
        keep_going (ecs);
        return;
diff -Naur gdb-5.0.cvs20011007/gdb/language.c 
gdb-5.0.cvs20011007.new/gdb/language.c
--- gdb-5.0.cvs20011007/gdb/language.c  Tue May  8 17:19:43 2001
+++ gdb-5.0.cvs20011007.new/gdb/language.c      Sat Nov 24 22:51:54 2001
@@ -550,6 +550,7 @@
     {
     case language_c:
     case language_cplus:
+    case language_objc:
       if (TYPE_CODE (t1) == TYPE_CODE_FLT)
        return TYPE_CODE (t2) == TYPE_CODE_FLT && l2 > l1 ?
          VALUE_TYPE (v2) : VALUE_TYPE (v1);
@@ -862,6 +863,7 @@
     {
     case language_c:
     case language_cplus:
+    case language_objc:
       return (TYPE_CODE (type) != TYPE_CODE_INT) &&
        (TYPE_CODE (type) != TYPE_CODE_ENUM) ? 0 : 1;
     case language_m2:
@@ -904,6 +906,7 @@
 
     case language_c:
     case language_cplus:
+    case language_objc:
       return (TYPE_CODE (type) == TYPE_CODE_INT) &&
        TYPE_LENGTH (type) == sizeof (char)
       ? 1 : 0;
@@ -926,6 +929,7 @@
 
     case language_c:
     case language_cplus:
+    case language_objc:
       /* C does not have distinct string type. */
       return (0);
     default:
@@ -944,6 +948,7 @@
     {
     case language_c:
     case language_cplus:
+    case language_objc:
       /* Might be more cleanly handled by having a TYPE_CODE_INT_NOT_BOOL
          for CHILL and such languages, or a TYPE_CODE_INT_OR_BOOL for C.  */
       if (TYPE_CODE (type) == TYPE_CODE_INT)
@@ -979,6 +984,7 @@
     {
     case language_c:
     case language_cplus:
+    case language_objc:
       return (TYPE_CODE (type) == TYPE_CODE_STRUCT) ||
        (TYPE_CODE (type) == TYPE_CODE_UNION) ||
        (TYPE_CODE (type) == TYPE_CODE_ARRAY);
@@ -1018,8 +1024,10 @@
        }
       return builtin_type_f_logical_s2;
     case language_cplus:
+    case language_objc:
     case language_pascal:
-      if (current_language->la_language==language_cplus)
+      if (current_language->la_language==language_cplus ||
+        current_language->la_language==language_objc)
         {sym = lookup_symbol ("bool", NULL, VAR_NAMESPACE, NULL, NULL);}
       else
         {sym = lookup_symbol ("boolean", NULL, VAR_NAMESPACE, NULL, NULL);}
@@ -1197,6 +1205,7 @@
 #ifdef _LANG_c
        case language_c:
        case language_cplus:
+       case language_objc:
          switch (op)
            {
            case BINOP_DIV:
diff -Naur gdb-5.0.cvs20011007/gdb/language.h 
gdb-5.0.cvs20011007.new/gdb/language.h
--- gdb-5.0.cvs20011007/gdb/language.h  Tue Mar  6 03:21:09 2001
+++ gdb-5.0.cvs20011007.new/gdb/language.h      Sat Nov 24 22:51:54 2001
@@ -288,6 +288,7 @@
 /* "cast" really means conversion */
 /* FIXME -- should be a setting in language_defn */
 #define CAST_IS_CONVERSION (current_language->la_language == language_c  || \
+                           current_language->la_language == language_objc || \
                            current_language->la_language == language_cplus)
 
 extern void language_info (int);
diff -Naur gdb-5.0.cvs20011007/gdb/linespec.c 
gdb-5.0.cvs20011007.new/gdb/linespec.c
--- gdb-5.0.cvs20011007/gdb/linespec.c  Thu Apr 26 20:19:09 2001
+++ gdb-5.0.cvs20011007.new/gdb/linespec.c      Sat Nov 24 23:08:57 2001
@@ -29,6 +29,7 @@
 #include "demangle.h"
 #include "value.h"
 #include "completer.h"
+#include <ctype.h>
 #include "cp-abi.h"
 
 /* Prototype for one function in parser-defs.h,
@@ -314,9 +315,8 @@
       *canonical = canonical_arr;
     }
 
-  i = 0;
   printf_unfiltered ("[0] cancel\n[1] all\n");
-  while (i < nelts)
+  for (i = 0; i < nelts; i++)          /* handle full-debug symbols */
     {
       INIT_SAL (&return_values.sals[i]);       /* initialize to zeroes */
       INIT_SAL (&values.sals[i]);
@@ -330,8 +330,7 @@
                             values.sals[i].line);
        }
       else
-       printf_unfiltered ("?HERE\n");
-      i++;
+       printf_unfiltered ("?HERE?");
     }
 
   if ((prompt = getenv ("PS2")) == NULL)
@@ -476,7 +475,7 @@
   register struct minimal_symbol *msymbol;
   char *copy;
   struct symbol *sym_class;
-  int i1;
+  int i1, i2;
   int is_quoted;
   int is_quote_enclosed;
   int has_parens;
@@ -485,7 +484,6 @@
   struct symbol **sym_arr;
   struct type *t;
   char *saved_arg = *argptr;
-  extern char *gdb_completer_quote_characters;
 
   INIT_SAL (&val);             /* initialize to zeroes */
 
@@ -540,7 +538,8 @@
    *     (gdb) break c::f(int)
    */
 
-  /* Maybe arg is FILE : LINENUM or FILE : FUNCTION */
+  /* Maybe arg is FILE : LINENUM or FILE : FUNCTION
+     but it could also be an ObjC method name with embedded ':'s */
 
   is_quoted = (**argptr
               && strchr (get_gdb_completer_quote_characters (),
@@ -634,6 +633,38 @@
   if (has_comma)
     *ii = ',';
 
+#define        OBJC    1       // DR 12/31/99
+
+#if    OBJC
+  if (p && p[1] != ':')                /* Extract the file name (if any) 
FIRST! */
+    {
+      p1 = p;
+      while (p != *argptr && p[-1] == ' ') --p;
+      copy = (char *) alloca (p - *argptr + 1);
+      memcpy (copy, *argptr, p - *argptr);
+      copy[p - *argptr] = 0;
+
+      /* See if we've found a filename, and if so, find that file's data.  */
+      s = lookup_symtab (copy);
+      if (s == 0)
+       { /* No symtab found, must not be a source filename. */
+#if 0    /* Not an error!  Could be part of an ObjC selector name */
+         /* (In which case it will be picked up below) */
+         if (!have_full_symbols () && !have_partial_symbols ())
+           error (no_symtab_msg);
+         error ("No source file named %s.", copy);
+#endif
+       }
+      else  /* Found a source file: discard the file name from the arg.  */
+       {   /* (symtab "s" now limits the scope of further searching)    */
+         p = p1 + 1;
+         while (*p == ' ' || *p == '\t') p++;
+         *argptr = p;
+       }
+    }
+#endif // OBJC
+
+#if    !OBJC           // DR 12/31/99
   if ((p[0] == ':' || p[0] == '.') && !has_parens)
     {
       /*  C++ */
@@ -862,7 +893,9 @@
          sym = lookup_symbol (copy, 0, VAR_NAMESPACE, 0, &sym_symtab);
          s = (struct symtab *) 0;
          /* Prepare to jump: restore the " if (condition)" so outer layers see 
it */
-         /* Symbol was found --> jump to normal symbol processing.
+         if (has_if)
+            *ii = ' ';
+          /* Symbol was found --> jump to normal symbol processing.
             Code following "symbol_found" expects "copy" to have the
             symbol name, "sym" to have the symbol pointer, "s" to be
             a specified file's symtab, and sym_symtab to be the symbol's
@@ -957,10 +990,274 @@
          processing, etc. */
     }
 #endif
+#endif
 
   /* S is specified file's symtab, or 0 if no file specified.
-     arg no longer contains the file name.  */
-
+     arg no longer contains the file name (if any).  */
+  
+  /* we now have a big loop that scans the arg string for special chars */
+  for (p = *argptr; *p; p++)   /* scan input string */
+  {
+      if ((pp = strchr(get_gdb_completer_quote_characters, *p)))  /* skip 
quoted */
+      {
+         while (*++p && *p != *pp)
+             ;
+         if (!*p)
+             error ("unmatched quote char in command");
+         continue;
+      }
+      /* else... */
+      
+      if (p[0] == '.' && strchr (p, ':') == NULL) /* Java qualified method. */
+      {
+         /* Find the *last* '.', since the others are package qualifiers. */
+         for (p1 = p;  *p1;  p1++)
+         {
+             if (*p1 == '.')
+                 p = p1;
+         }
+         break;
+      }
+      switch (*p) {
+      case '<' :               /* possibly a C++ template spec */
+      {
+         int nesting = 1;      /* for "Vector<Vector<int>>::size" */
+         while (nesting > 0 && *++p)
+             switch (*p) {
+             case '>' : nesting--; break;
+             case '<' : nesting++; break;
+             default  : break;
+             }
+         if (!*p || nesting != 0)
+             error ("non-matching '<' and '>' in command");
+         continue;
+      }
+      case '(' :               /* possibly a C++ function with prototype */
+      {
+         int nesting = 1;      /* in case prototypes can have "()" in them */
+         while (nesting > 0 && *++p)
+             switch (*p) {
+             case ')' : nesting--; break;
+             case '(' : nesting++; break;
+             default  : break;
+             }
+         if (!*p || nesting != 0)
+             error ("non-matching '(' and ')' in command");
+         continue;
+      }
+      case '+':                /* possibly Objective C class/instance method 
spec */
+      case '-':
+         p1 = p;                       /* save position, scan off white space 
*/
+         while (*++p && (*p == ' ' || *p == '\t'))
+             ;
+         
+         if (isalpha(*p))
+             continue;         /* probably a [+/-] selector */
+         else if (*p != '[')   /* if NOT followed by LBRAC, */
+         {                     /*    take a word-break char (expect a num) */
+             p = p1;
+             break;
+         }
+         /* else fall thru to '[' case */
+         
+      case '[' :               /* Objective C method spec */
+         while (*++p && *p != ']')
+             /* scan off everything between '[' and ']': there may be
+                characters such as ':' and '(' that we don't want to analyze
+                (eg. they don't represent C++ stuff) */
+             ;
+         if (!*p)
+             error ("non-matching '[' and ']' in command");
+         continue;
+      case '.' :                /* dot, possibly Java qualified method      */
+      case ':' :               /* colon, possibly a C++ class::method spec */
+                               /* (also possibly an Objective C selector)  */
+                               /* double colon, possibly C++ class::method */
+         if (p[0] == '.' || p[1] ==':')
+         {
+             if (has_parens)
+             {
+                 p++;          /* skip */
+                 continue;
+             }
+             else
+             {
+                 /* Extract the class name (if it is one) */
+                 p1 = p;
+                 while (p != *argptr && p[-1] == ' ') --p;
+                 copy = (char *) alloca (p - *argptr + 1);
+                 memcpy (copy, *argptr, p - *argptr);
+                 copy[p - *argptr] = 0;
+                 
+                 p = p1 + (p1[0] == ':' ? 2 : 1);
+                 while (*p == ' ' || *p == '\t') p++;
+                 
+                 sym_class = lookup_symbol (copy, 0, STRUCT_NAMESPACE, 0,
+                                            (struct symtab **)NULL);
+                 if (sym_class &&
+                     (t = check_typedef (SYMBOL_TYPE (sym_class)),
+                      (TYPE_CODE (t) == TYPE_CODE_STRUCT
+                       || TYPE_CODE (t) == TYPE_CODE_UNION)))
+                 {
+                     /* Discard the class name from the arg.  */
+                     *argptr = p;
+                     
+                     if (**argptr &&
+                         strchr(get_gdb_completer_quote_characters, **argptr) 
!= NULL)
+                     {
+                         p = skip_quoted(*argptr);
+                         *argptr = *argptr + 1;
+                     }
+                     else
+                     {
+                         p = *argptr;
+                         while (*p && *p!=' ' && *p!='\t' && *p!=',' && 
*p!=':') p++;
+                     }
+/*
+                     q = operator_chars (*argptr, &q1);
+                     if (q1 - q)
+                     {
+                         char *opname;
+                         char *tmp = alloca (q1 - q + 1);
+                         memcpy (tmp, q, q1 - q);
+                         tmp[q1 - q] = '\0';
+                         opname = cplus_mangle_opname (tmp, DMGL_ANSI);
+                         if (opname == NULL)
+                         {
+                             error_begin ();
+                             printf_filtered ("no mangling for \"%s\"\n", tmp);
+                             cplusplus_hint (saved_arg);
+                             return_to_top_level (RETURN_ERROR);
+                         }
+                         copy = (char*) alloca (3 + strlen(opname));
+                         sprintf (copy, "__%s", opname);
+                         p = q1;
+                     }
+                     else
+*/
+                     {
+                         copy = (char *) alloca (p - *argptr + 1 );
+                         memcpy (copy, *argptr, p - *argptr);
+                         copy[p - *argptr] = '\0';
+                         if (p != *argptr
+                             && copy[p - *argptr - 1]
+                             && strchr (get_gdb_completer_quote_characters,
+                                        copy[p - *argptr - 1]) != NULL)
+                             copy[p - *argptr - 1] = '\0';
+                     }
+                     
+                     /* no line number may be specified */
+                     while (*p == ' ' || *p == '\t') p++;
+                     *argptr = p;
+                     
+                     sym = 0;
+                     i1 = 0;           /*  counter for the symbol array */
+                     sym_arr = (struct symbol **)
+                         alloca((total_number_of_methods (t) + 1)
+                                * sizeof(struct symbol *));
+                     
+                     if (destructor_name_p (copy, t))
+                     {
+                         /* Destructors are a special case.  */
+                         int m_index, f_index;
+                         
+                         if (get_destructor_fn_field (t, &m_index, &f_index))
+                         {
+                             struct fn_field *f = TYPE_FN_FIELDLIST1 (t, 
m_index);
+                             
+                             sym_arr[i1] =
+                                 lookup_symbol (TYPE_FN_FIELD_PHYSNAME (f, 
f_index),
+                                                NULL, VAR_NAMESPACE, (int *) 
NULL,
+                                                (struct symtab **)NULL);
+                             if (sym_arr[i1])
+                                 i1++;
+                         }
+                     }
+                     else
+                         i1 = find_methods (t, copy, sym_arr);
+                     if (i1 == 1)
+                     {
+                         /* There is exactly one field with that name.  */
+                         sym = sym_arr[0];
+                         
+                         if (sym && SYMBOL_CLASS (sym) == LOC_BLOCK)
+                         {
+                             values.sals = (struct symtab_and_line *)
+                                 xmalloc (sizeof (struct symtab_and_line));
+                             values.nelts = 1;
+                             values.sals[0] = find_function_start_sal (sym, 
+                                                                       
funfirstline);
+                         }
+                         else
+                         {
+                             values.nelts = 0;
+                         }
+                         return values;
+                     }
+                     if (i1 > 0)
+                     {
+                         /* There is more than one field with that name
+                            (overloaded).  Ask the user which one to use.  */
+                         return decode_line_2 (sym_arr, i1, funfirstline, 
canonical);
+                     }
+                     else
+                     {
+                         char *tmp;
+                         
+                         if (is_operator_name (copy))
+                         {
+                             tmp = (char *)alloca (strlen (copy+3) + 9);
+                             strcpy (tmp, "operator ");
+                             strcat (tmp, copy+3);
+                         }
+                         else
+                             tmp = copy;
+                         error_begin ();
+                         if (tmp[0] == '~')
+                             printf_filtered
+                                 ("the class `%s' does not have destructor 
defined\n",
+                                  SYMBOL_SOURCE_NAME(sym_class));
+                         else
+                             printf_filtered
+                                 ("the class %s does not have any method named 
%s\n",
+                                  SYMBOL_SOURCE_NAME(sym_class), tmp);
+                         cplusplus_hint (saved_arg);
+                         return_to_top_level (RETURN_ERROR);
+                     }
+                 }
+                 else
+                 {
+#if 0            /* Not an error!  Double-colon was parsed, and
+                    no class was matched, but it could still be part of
+                    an ObjC selector.  Just continue, we'll catch it
+                    further down, and if we still can't match it,
+                    then we'll report it. */
+ error_begin ();
+ /* The quotes are important if copy is empty.  */
+ printf_filtered
+     ("can't find class, struct, or union named \"%s\"\n",
+      copy);
+ cplusplus_hint (saved_arg);
+ return_to_top_level (RETURN_ERROR);
+#else
+ continue;     /* might be an ObjC selector */
+#endif
+                 }
+             }
+         }
+         /*  end of C++  */
+         continue;
+      default:         /* ordinary character to be scanned */
+         break;                /* fall out and check for word break character 
*/
+      }
+      if (strchr(get_gdb_completer_word_break_characters, *p))
+      {
+         while (*p == ' ' || *p == '\t')       /* scan off final whitespace */
+             p++;
+         break;        /* end scanning the input string */
+      }
+  }
+  
   /* Check whether arg is all digits (and sign) */
 
   q = *argptr;
@@ -1041,6 +1338,10 @@
       return values;
     }
 
+#if    OBJC    // DR 12/31/99
+  /* Arg token is not digits => try it as a variable name
+     Token has already been recognized by the for loop above */
+#else
   /* Arg token is not digits => try it as a variable name
      Find the next token (everything up to end or next whitespace).  */
 
@@ -1060,6 +1361,7 @@
     {
       p = skip_quoted (*argptr);
     }
+#endif
 
   copy = (char *) alloca (p - *argptr + 1);
   memcpy (copy, *argptr, p - *argptr);
@@ -1142,6 +1444,67 @@
       return values;
     }
 
+  /* here's where we recognise an Objective C Selector */
+  i1 = total_number_of_imps (copy, s); /* is it an objc selector? */
+  if (i1)                              /* input matches an objc selector */
+    {
+      /* NOTE: an Objective C selector may be implemented by more than
+        one class, therefore it may represent more than one method/function.
+        This gives us a situation somewhat analogous to C++ overloading.
+        If there's more than one method that could represent the selector,
+        then use some of the existing C++ code to let the user choose one. */
+
+      sym_arr = (struct symbol **) alloca (i1 * sizeof(struct symbol *) + 2);
+      /* see if input also matches a function */
+      sym = lookup_symbol (copy,
+                          (s ? BLOCKVECTOR_BLOCK (BLOCKVECTOR (s),
+                                                  STATIC_BLOCK)
+                           : get_selected_block ()),
+                          VAR_NAMESPACE, 0, &sym_symtab);
+      i1 = find_imps (copy, sym_arr, &i2, sym, s);
+
+      /* i1 now represents the TOTAL number of matches found...
+        i2 represents how many HIGH-LEVEL (struct symbol) matches,
+        which will come first in the sym_arr array.  Any low-level
+        (minimal_symbol) matches will follow those. */
+
+      if (i1 == 1)                     /* exactly one matching method */
+       {
+         extern int info_verbose;
+
+         if (i2 == 1)
+           sym = sym_arr[0];           /* already a struct symbol */
+         else                          /*   else a minimal_symbol */
+           sym = find_pc_function (SYMBOL_VALUE_ADDRESS (sym_arr[0]));
+
+         values.sals = (struct symtab_and_line *) xmalloc
+           (sizeof (struct symtab_and_line));
+         values.nelts = 1;
+
+         if (sym && SYMBOL_CLASS (sym) == LOC_BLOCK)
+           {
+             values.sals[0] = find_function_start_sal (sym, funfirstline);
+             /* canonicalize this, so it remains resolved for dylib loads */
+             build_canonical_line_spec (values.sals,
+                                        SYMBOL_SOURCE_NAME (sym), canonical);
+           }
+         else  /* the only match was a non-debuggable symbol */
+           { /* no high-level symbol */
+             values.sals[0].symtab = 0;
+             values.sals[0].line   = 0;
+             values.sals[0].end    = 0;
+             values.sals[0].pc     = SYMBOL_VALUE_ADDRESS (sym_arr[0]);
+           }
+         if (info_verbose)
+           printf_filtered ("(selector %s matched method %s)\n",
+                            copy, SYMBOL_SOURCE_NAME (sym_arr[0]));
+         return values;
+       }
+      else     /* more than one match -- user must choose one or more */
+       {
+         return decode_line_2 (sym_arr, i2, funfirstline, canonical);
+       }
+    }
 
   /* Look up that token as a variable.
      If file specified, use that file's per-file block to start with.  */
@@ -1228,6 +1591,13 @@
   if (!have_full_symbols () &&
       !have_partial_symbols () && !have_minimal_symbols ())
     error ("No symbol table is loaded.  Use the \"file\" command.");
+
+#ifdef v417    
+  if (s == NULL)
+  error ("Function \"%s\" not defined.", copy);
+  else
+    error ("Function \"%s\" not defined in file %s.", copy, s->filename);
+#endif
 
   error ("Function \"%s\" not defined.", copy);
   return values;               /* for lint */
diff -Naur gdb-5.0.cvs20011007/gdb/maint.c gdb-5.0.cvs20011007.new/gdb/maint.c
--- gdb-5.0.cvs20011007/gdb/maint.c     Tue Apr 10 21:01:04 2001
+++ gdb-5.0.cvs20011007.new/gdb/maint.c Sat Nov 24 22:51:54 2001
@@ -139,7 +139,18 @@
     }
   else
     {
-      demangled = cplus_demangle (args, DMGL_ANSI | DMGL_PARAMS);
+      switch (current_language->la_language) {
+      case language_objc:
+       demangled = objc_demangle (args);
+       break;
+      case language_chill:
+       demangled = chill_demangle (args);
+       break;
+      case language_cplus:
+      default:
+       demangled = cplus_demangle (args, DMGL_ANSI | DMGL_PARAMS);
+       break;
+      }
       if (demangled != NULL)
        {
          printf_unfiltered ("%s\n", demangled);
@@ -479,7 +490,7 @@
                  "Commands for use by GDB maintainers.\n\
 Includes commands to dump specific internal GDB structures in\n\
 a human readable form, to cause GDB to deliberately dump core,\n\
-to test internal functions such as the C++ demangler, etc.",
+to test internal functions such as the C++/Objc/Chill demangler, etc.",
                  &maintenancelist, "maintenance ", 0,
                  &cmdlist);
 
@@ -514,9 +525,9 @@
           &maintenancelist);
 
   add_cmd ("demangle", class_maintenance, maintenance_demangle,
-          "Demangle a C++ mangled name.\n\
-Call internal GDB demangler routine to demangle a C++ link name\n\
-and prints the result.",
+          "Demangle a C++/Objc/Chill mangled name.\n\
+Call internal GDB demangler routine to demangle a mangled link name\n\
+and print the result.",
           &maintenancelist);
 
   add_cmd ("time", class_maintenance, maintenance_time_display,
diff -Naur gdb-5.0.cvs20011007/gdb/objc-exp.y 
gdb-5.0.cvs20011007.new/gdb/objc-exp.y
--- gdb-5.0.cvs20011007/gdb/objc-exp.y  Wed Dec 31 19:00:00 1969
+++ gdb-5.0.cvs20011007.new/gdb/objc-exp.y      Sat Nov 24 22:51:54 2001
@@ -0,0 +1,1785 @@
+/* YACC parser for C expressions, for GDB.
+   Copyright (C) 1986, 1989, 1990, 1991, 1993, 1994
+   Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+
+/* Parse a C expression from text in a string,
+   and return the result as a  struct expression  pointer.
+   That structure contains arithmetic operations in reverse polish,
+   with constants represented by operations that are followed by special data.
+   See expression.h for the details of the format.
+   What is important here is that it can be built up sequentially
+   during the process of parsing; the lower levels of the tree always
+   come first in the result.
+
+   Note that malloc's and realloc's in this file are transformed to
+   xmalloc and xrealloc respectively by the same sed command in the
+   makefile that remaps any other malloc/realloc inserted by the parser
+   generator.  Doing this with #defines and trying to control the interaction
+   with include files (<malloc.h> and <stdlib.h> for example) just became
+   too messy, particularly when such includes can be inserted at random
+   times by the parser generator.  */
+   
+%{
+
+#include "defs.h"
+#include "gdb_string.h"
+#include <ctype.h>
+#include "expression.h"
+
+#include "objc-lang.h" /* for objc language constructs */
+
+#include "value.h"
+#include "parser-defs.h"
+#include "language.h"
+#include "c-lang.h"
+#include "bfd.h" /* Required by objfiles.h.  */
+#include "symfile.h" /* Required by objfiles.h.  */
+#include "objfiles.h" /* For have_full_symbols and have_partial_symbols */
+
+/* Remap normal yacc parser interface names (yyparse, yylex, yyerror, etc),
+   as well as gratuitiously global symbol names, so we can have multiple
+   yacc generated parsers in gdb.  Note that these are only the variables
+   produced by yacc.  If other parser generators (bison, byacc, etc) produce
+   additional global names that conflict at link time, then those parser
+   generators need to be fixed instead of adding those names to this list. */
+
+#define        yymaxdepth      objc_maxdepth
+#define        yyparse         objc_parse
+#define        yylex           objc_lex
+#define        yyerror         objc_error
+#define        yylval          objc_lval
+#define        yychar          objc_char
+#define        yydebug         objc_debug
+#define        yypact          objc_pact       
+#define        yyr1            objc_r1                 
+#define        yyr2            objc_r2                 
+#define        yydef           objc_def                
+#define        yychk           objc_chk                
+#define        yypgo           objc_pgo                
+#define        yyact           objc_act                
+#define        yyexca          objc_exca
+#define yyerrflag      objc_errflag
+#define yynerrs                objc_nerrs
+#define        yyps            objc_ps
+#define        yypv            objc_pv
+#define        yys             objc_s
+#define        yy_yys          objc_yys
+#define        yystate         objc_state
+#define        yytmp           objc_tmp
+#define        yyv             objc_v
+#define        yy_yyv          objc_yyv
+#define        yyval           objc_val
+#define        yylloc          objc_lloc
+#define yyreds         objc_reds               /* With YYDEBUG defined */
+#define yytoks         objc_toks               /* With YYDEBUG defined */
+#define yylhs          objc_yylhs
+#define yylen          objc_yylen
+#define yydefred       objc_yydefred
+#define yydgoto                objc_yydgoto
+#define yysindex       objc_yysindex
+#define yyrindex       objc_yyrindex
+#define yygindex       objc_yygindex
+#define yytable                objc_yytable
+#define yycheck                objc_yycheck
+
+#ifndef YYDEBUG
+#define        YYDEBUG 0               /* Default to no yydebug support */
+#endif
+
+int
+yyparse PARAMS ((void));
+
+static int
+yylex PARAMS ((void));
+
+void
+yyerror PARAMS ((char *));
+
+%}
+
+/* Although the yacc "value" of an expression is not used,
+   since the result is stored in the structure being created,
+   other node types do have values.  */
+
+%union
+  {
+    LONGEST lval;
+    struct {
+      LONGEST val;
+      struct type *type;
+    } typed_val_int;
+    struct {
+      DOUBLEST dval;
+      struct type *type;
+    } typed_val_float;
+    struct symbol *sym;
+    struct type *tval;
+    struct stoken sval;
+    struct ttype tsym;
+    struct symtoken ssym;
+    int voidval;
+    struct block *bval;
+    enum exp_opcode opcode;
+    struct internalvar *ivar;
+
+    struct type **tvec;
+    int *ivec;
+  }
+
+%{
+/* YYSTYPE gets defined by %union */
+static int
+parse_number PARAMS ((char *, int, int, YYSTYPE *));
+%}
+
+%type <voidval> exp exp1 type_exp start variable qualified_name lcurly
+%type <lval> rcurly
+%type <tval> type typebase
+%type <tvec> nonempty_typelist
+/* %type <bval> block */
+
+/* Fancy type parsing.  */
+%type <voidval> func_mod direct_abs_decl abs_decl
+%type <tval> ptype
+%type <lval> array_mod
+
+%token <typed_val_int> INT
+%token <typed_val_float> FLOAT
+
+/* Both NAME and TYPENAME tokens represent symbols in the input,
+   and both convey their data as strings.
+   But a TYPENAME is a string that happens to be defined as a typedef
+   or builtin type name (such as int or char)
+   and a NAME is any other symbol.
+   Contexts where this distinction is not important can use the
+   nonterminal "name", which matches either NAME or TYPENAME.  */
+
+%token <sval> STRING
+%token <sval> NSSTRING         /* NextStep ObjC "NSString" data type */
+%token <ssym> NAME /* BLOCKNAME defined below to give it higher precedence. */
+%token <tsym> TYPENAME
+%token <lval> CLASSNAME                /* ObjC Class name */
+%type <sval> name
+%type <ssym> name_not_typename
+%type <tsym> typename
+
+/* A NAME_OR_INT is a symbol which is not known in the symbol table,
+   but which would parse as a valid number in the current input radix.
+   E.g. "c" when input_radix==16.  Depending on the parse, it will be
+   turned into a name or into a number.  */
+
+%token <ssym> NAME_OR_INT 
+
+%token STRUCT CLASS UNION ENUM SIZEOF UNSIGNED COLONCOLON SELECTOR
+%token TEMPLATE
+%token ERROR
+
+/* Special type cases, put in to allow the parser to distinguish different
+   legal basetypes.  */
+%token SIGNED_KEYWORD LONG SHORT INT_KEYWORD CONST_KEYWORD VOLATILE_KEYWORD 
DOUBLE_KEYWORD
+
+%token <voidval> VARIABLE
+
+%token <opcode> ASSIGN_MODIFY
+
+/* C++ */
+%token THIS
+
+%left ','
+%left ABOVE_COMMA
+%right '=' ASSIGN_MODIFY
+%right '?'
+%left OROR
+%left ANDAND
+%left '|'
+%left '^'
+%left '&'
+%left EQUAL NOTEQUAL
+%left '<' '>' LEQ GEQ
+%left LSH RSH
+%left '@'
+%left '+' '-'
+%left '*' '/' '%'
+%right UNARY INCREMENT DECREMENT
+%right ARROW '.' '[' '('
+%token <ssym> BLOCKNAME 
+%type <bval> block
+%left COLONCOLON
+
+
+%%
+
+start   :      exp1
+       |       type_exp
+       ;
+
+type_exp:      type
+                       { write_exp_elt_opcode(OP_TYPE);
+                         write_exp_elt_type($1);
+                         write_exp_elt_opcode(OP_TYPE);}
+       ;
+
+/* Expressions, including the comma operator.  */
+exp1   :       exp
+       |       exp1 ',' exp
+                       { write_exp_elt_opcode (BINOP_COMMA); }
+       ;
+
+/* Expressions, not including the comma operator.  */
+exp    :       '*' exp    %prec UNARY
+                       { write_exp_elt_opcode (UNOP_IND); }
+
+exp    :       '&' exp    %prec UNARY
+                       { write_exp_elt_opcode (UNOP_ADDR); }
+
+exp    :       '-' exp    %prec UNARY
+                       { write_exp_elt_opcode (UNOP_NEG); }
+       ;
+
+exp    :       '!' exp    %prec UNARY
+                       { write_exp_elt_opcode (UNOP_LOGICAL_NOT); }
+       ;
+
+exp    :       '~' exp    %prec UNARY
+                       { write_exp_elt_opcode (UNOP_COMPLEMENT); }
+       ;
+
+exp    :       INCREMENT exp    %prec UNARY
+                       { write_exp_elt_opcode (UNOP_PREINCREMENT); }
+       ;
+
+exp    :       DECREMENT exp    %prec UNARY
+                       { write_exp_elt_opcode (UNOP_PREDECREMENT); }
+       ;
+
+exp    :       exp INCREMENT    %prec UNARY
+                       { write_exp_elt_opcode (UNOP_POSTINCREMENT); }
+       ;
+
+exp    :       exp DECREMENT    %prec UNARY
+                       { write_exp_elt_opcode (UNOP_POSTDECREMENT); }
+       ;
+
+exp    :       SIZEOF exp       %prec UNARY
+                       { write_exp_elt_opcode (UNOP_SIZEOF); }
+       ;
+
+exp    :       exp ARROW name
+                       { write_exp_elt_opcode (STRUCTOP_PTR);
+                         write_exp_string ($3);
+                         write_exp_elt_opcode (STRUCTOP_PTR); }
+       ;
+
+exp    :       exp ARROW qualified_name
+                       { /* exp->type::name becomes exp->*(&type::name) */
+                         /* Note: this doesn't work if name is a
+                            static member!  FIXME */
+                         write_exp_elt_opcode (UNOP_ADDR);
+                         write_exp_elt_opcode (STRUCTOP_MPTR); }
+       ;
+exp    :       exp ARROW '*' exp
+                       { write_exp_elt_opcode (STRUCTOP_MPTR); }
+       ;
+
+exp    :       exp '.' name
+                       { write_exp_elt_opcode (STRUCTOP_STRUCT);
+                         write_exp_string ($3);
+                         write_exp_elt_opcode (STRUCTOP_STRUCT); }
+       ;
+
+
+exp    :       exp '.' qualified_name
+                       { /* exp.type::name becomes exp.*(&type::name) */
+                         /* Note: this doesn't work if name is a
+                            static member!  FIXME */
+                         write_exp_elt_opcode (UNOP_ADDR);
+                         write_exp_elt_opcode (STRUCTOP_MEMBER); }
+       ;
+
+exp    :       exp '.' '*' exp
+                       { write_exp_elt_opcode (STRUCTOP_MEMBER); }
+       ;
+
+exp    :       exp '[' exp1 ']'
+                       { write_exp_elt_opcode (BINOP_SUBSCRIPT); }
+       ;
+/*
+ * The rules below parse ObjC message calls of the form:
+ *     '[' target selector {':' argument}* ']'
+ */
+
+exp    :       '[' CLASSNAME
+                       {
+                         write_exp_elt_opcode (OP_LONG);
+                         write_exp_elt_type (builtin_type_int);
+                         write_exp_elt_longcst ((LONGEST) $2);
+                         write_exp_elt_opcode (OP_LONG);
+                         start_msglist();
+                       }
+               msglist ']'
+                       { write_exp_elt_opcode (OP_MSGCALL);
+                         end_msglist();
+                         write_exp_elt_opcode (OP_MSGCALL); 
+                       }
+       ;
+
+exp    :       '[' exp
+                       { start_msglist(); }
+               msglist ']'
+                       { write_exp_elt_opcode (OP_MSGCALL);
+                         end_msglist();
+                         write_exp_elt_opcode (OP_MSGCALL); 
+                       }
+       ;
+
+msglist :      name
+                       { add_msglist(&$1, 0); }
+       |       msgarglist
+       ;
+
+msgarglist :   msgarg
+       |       msgarglist msgarg
+       ;
+
+msgarg :       name ':' exp
+                       { add_msglist(&$1, 1); }
+       |       ':' exp /* unnamed arg */
+                       { add_msglist(0, 1);   }
+       |       ',' exp /* variable number of args */
+                       { add_msglist(0, 0);   }
+       ;
+
+exp    :       exp '(' 
+                       /* This is to save the value of arglist_len
+                          being accumulated by an outer function call.  */
+                       { start_arglist (); }
+               arglist ')'     %prec ARROW
+                       { write_exp_elt_opcode (OP_FUNCALL);
+                         write_exp_elt_longcst ((LONGEST) end_arglist ());
+                         write_exp_elt_opcode (OP_FUNCALL); }
+       ;
+
+lcurly :       '{'
+                       { start_arglist (); }
+       ;
+
+arglist        :
+       ;
+
+arglist        :       exp
+                       { arglist_len = 1; }
+       ;
+
+arglist        :       arglist ',' exp   %prec ABOVE_COMMA
+                       { arglist_len++; }
+       ;
+
+rcurly :       '}'
+                       { $$ = end_arglist () - 1; }
+       ;
+exp    :       lcurly arglist rcurly   %prec ARROW
+                       { write_exp_elt_opcode (OP_ARRAY);
+                         write_exp_elt_longcst ((LONGEST) 0);
+                         write_exp_elt_longcst ((LONGEST) $3);
+                         write_exp_elt_opcode (OP_ARRAY); }
+       ;
+
+exp    :       lcurly type rcurly exp  %prec UNARY
+                       { write_exp_elt_opcode (UNOP_MEMVAL);
+                         write_exp_elt_type ($2);
+                         write_exp_elt_opcode (UNOP_MEMVAL); }
+       ;
+
+exp    :       '(' type ')' exp  %prec UNARY
+                       { write_exp_elt_opcode (UNOP_CAST);
+                         write_exp_elt_type ($2);
+                         write_exp_elt_opcode (UNOP_CAST); }
+       ;
+
+exp    :       '(' exp1 ')'
+                       { }
+       ;
+
+/* Binary operators in order of decreasing precedence.  */
+
+exp    :       exp '@' exp
+                       { write_exp_elt_opcode (BINOP_REPEAT); }
+       ;
+
+exp    :       exp '*' exp
+                       { write_exp_elt_opcode (BINOP_MUL); }
+       ;
+
+exp    :       exp '/' exp
+                       { write_exp_elt_opcode (BINOP_DIV); }
+       ;
+
+exp    :       exp '%' exp
+                       { write_exp_elt_opcode (BINOP_REM); }
+       ;
+
+exp    :       exp '+' exp
+                       { write_exp_elt_opcode (BINOP_ADD); }
+       ;
+
+exp    :       exp '-' exp
+                       { write_exp_elt_opcode (BINOP_SUB); }
+       ;
+
+exp    :       exp LSH exp
+                       { write_exp_elt_opcode (BINOP_LSH); }
+       ;
+
+exp    :       exp RSH exp
+                       { write_exp_elt_opcode (BINOP_RSH); }
+       ;
+
+exp    :       exp EQUAL exp
+                       { write_exp_elt_opcode (BINOP_EQUAL); }
+       ;
+
+exp    :       exp NOTEQUAL exp
+                       { write_exp_elt_opcode (BINOP_NOTEQUAL); }
+       ;
+
+exp    :       exp LEQ exp
+                       { write_exp_elt_opcode (BINOP_LEQ); }
+       ;
+
+exp    :       exp GEQ exp
+                       { write_exp_elt_opcode (BINOP_GEQ); }
+       ;
+
+exp    :       exp '<' exp
+                       { write_exp_elt_opcode (BINOP_LESS); }
+       ;
+
+exp    :       exp '>' exp
+                       { write_exp_elt_opcode (BINOP_GTR); }
+       ;
+
+exp    :       exp '&' exp
+                       { write_exp_elt_opcode (BINOP_BITWISE_AND); }
+       ;
+
+exp    :       exp '^' exp
+                       { write_exp_elt_opcode (BINOP_BITWISE_XOR); }
+       ;
+
+exp    :       exp '|' exp
+                       { write_exp_elt_opcode (BINOP_BITWISE_IOR); }
+       ;
+
+exp    :       exp ANDAND exp
+                       { write_exp_elt_opcode (BINOP_LOGICAL_AND); }
+       ;
+
+exp    :       exp OROR exp
+                       { write_exp_elt_opcode (BINOP_LOGICAL_OR); }
+       ;
+
+exp    :       exp '?' exp ':' exp     %prec '?'
+                       { write_exp_elt_opcode (TERNOP_COND); }
+       ;
+                         
+exp    :       exp '=' exp
+                       { write_exp_elt_opcode (BINOP_ASSIGN); }
+       ;
+
+exp    :       exp ASSIGN_MODIFY exp
+                       { write_exp_elt_opcode (BINOP_ASSIGN_MODIFY);
+                         write_exp_elt_opcode ($2);
+                         write_exp_elt_opcode (BINOP_ASSIGN_MODIFY); }
+       ;
+
+exp    :       INT
+                       { write_exp_elt_opcode (OP_LONG);
+                         write_exp_elt_type ($1.type);
+                         write_exp_elt_longcst ((LONGEST)($1.val));
+                         write_exp_elt_opcode (OP_LONG); }
+       ;
+
+exp    :       NAME_OR_INT
+                       { YYSTYPE val;
+                         parse_number ($1.stoken.ptr, $1.stoken.length, 0, 
&val);
+                         write_exp_elt_opcode (OP_LONG);
+                         write_exp_elt_type (val.typed_val_int.type);
+                         write_exp_elt_longcst 
((LONGEST)val.typed_val_int.val);
+                         write_exp_elt_opcode (OP_LONG);
+                       }
+       ;
+
+
+exp    :       FLOAT
+                       { write_exp_elt_opcode (OP_DOUBLE);
+                         write_exp_elt_type ($1.type);
+                         write_exp_elt_dblcst ($1.dval);
+                         write_exp_elt_opcode (OP_DOUBLE); }
+       ;
+
+exp    :       variable
+       ;
+
+exp    :       VARIABLE
+                       /* Already written by write_dollar_variable. */
+       ;
+
+exp    :       SELECTOR 
+                       {
+                         write_exp_elt_opcode (OP_SELECTOR);
+                         write_exp_string (yylval.sval);
+                         write_exp_elt_opcode (OP_SELECTOR); }
+
+exp    :       SIZEOF '(' type ')'     %prec UNARY
+                       { write_exp_elt_opcode (OP_LONG);
+                         write_exp_elt_type (builtin_type_int);
+                         CHECK_TYPEDEF ($3);
+                         write_exp_elt_longcst ((LONGEST) TYPE_LENGTH ($3));
+                         write_exp_elt_opcode (OP_LONG); }
+       ;
+
+exp    :       STRING
+                       { /* C strings are converted into array constants with
+                            an explicit null byte added at the end.  Thus
+                            the array upper bound is the string length.
+                            There is no such thing in C as a completely empty
+                            string. */
+                         char *sp = $1.ptr; int count = $1.length;
+                         while (count-- > 0)
+                           {
+                             write_exp_elt_opcode (OP_LONG);
+                             write_exp_elt_type (builtin_type_char);
+                             write_exp_elt_longcst ((LONGEST)(*sp++));
+                             write_exp_elt_opcode (OP_LONG);
+                           }
+                         write_exp_elt_opcode (OP_LONG);
+                         write_exp_elt_type (builtin_type_char);
+                         write_exp_elt_longcst ((LONGEST)'\0');
+                         write_exp_elt_opcode (OP_LONG);
+                         write_exp_elt_opcode (OP_ARRAY);
+                         write_exp_elt_longcst ((LONGEST) 0);
+                         write_exp_elt_longcst ((LONGEST) ($1.length));
+                         write_exp_elt_opcode (OP_ARRAY); }
+       ;
+
+exp     :      NSSTRING        /* ObjC NextStep NSString constant
+                                * of the form '@' '"' string '"'
+                                */
+                       { write_exp_elt_opcode (OP_NSSTRING);
+                         write_exp_string ($1);
+                         write_exp_elt_opcode (OP_NSSTRING); }
+       ;
+
+/* C++.  */
+exp    :       THIS
+                       { write_exp_elt_opcode (OP_THIS);
+                         write_exp_elt_opcode (OP_THIS); }
+       ;
+
+/* end of C++.  */
+
+block  :       BLOCKNAME
+                       {
+                         if ($1.sym != 0)
+                             $$ = SYMBOL_BLOCK_VALUE ($1.sym);
+                         else
+                           {
+                             struct symtab *tem =
+                                 lookup_symtab (copy_name ($1.stoken));
+                             if (tem)
+                               $$ = BLOCKVECTOR_BLOCK (BLOCKVECTOR (tem), 
STATIC_BLOCK);
+                             else
+                               error ("No file or function \"%s\".",
+                                      copy_name ($1.stoken));
+                           }
+                       }
+       ;
+
+block  :       block COLONCOLON name
+                       { struct symbol *tem
+                           = lookup_symbol (copy_name ($3), $1,
+                                            VAR_NAMESPACE, (int *) NULL,
+                                            (struct symtab **) NULL);
+                         if (!tem || SYMBOL_CLASS (tem) != LOC_BLOCK)
+                           error ("No function \"%s\" in specified context.",
+                                  copy_name ($3));
+                         $$ = SYMBOL_BLOCK_VALUE (tem); }
+       ;
+
+variable:      block COLONCOLON name
+                       { struct symbol *sym;
+                         sym = lookup_symbol (copy_name ($3), $1,
+                                              VAR_NAMESPACE, (int *) NULL,
+                                              (struct symtab **) NULL);
+                         if (sym == 0)
+                           error ("No symbol \"%s\" in specified context.",
+                                  copy_name ($3));
+
+                         write_exp_elt_opcode (OP_VAR_VALUE);
+                         /* block_found is set by lookup_symbol.  */
+                         write_exp_elt_block (block_found);
+                         write_exp_elt_sym (sym);
+                         write_exp_elt_opcode (OP_VAR_VALUE); }
+       ;
+
+qualified_name:        typebase COLONCOLON name
+                       {
+                         struct type *type = $1;
+                         if (TYPE_CODE (type) != TYPE_CODE_STRUCT
+                             && TYPE_CODE (type) != TYPE_CODE_UNION)
+                           error ("`%s' is not defined as an aggregate type.",
+                                  TYPE_NAME (type));
+
+                         write_exp_elt_opcode (OP_SCOPE);
+                         write_exp_elt_type (type);
+                         write_exp_string ($3);
+                         write_exp_elt_opcode (OP_SCOPE);
+                       }
+       |       typebase COLONCOLON '~' name
+                       {
+                         struct type *type = $1;
+                         struct stoken tmp_token;
+                         if (TYPE_CODE (type) != TYPE_CODE_STRUCT
+                             && TYPE_CODE (type) != TYPE_CODE_UNION)
+                           error ("`%s' is not defined as an aggregate type.",
+                                  TYPE_NAME (type));
+
+                         if (!STREQ (type_name_no_tag (type), $4.ptr))
+                           error ("invalid destructor `%s::~%s'",
+                                  type_name_no_tag (type), $4.ptr);
+
+                         tmp_token.ptr = (char*) alloca ($4.length + 2);
+                         tmp_token.length = $4.length + 1;
+                         tmp_token.ptr[0] = '~';
+                         memcpy (tmp_token.ptr+1, $4.ptr, $4.length);
+                         tmp_token.ptr[tmp_token.length] = 0;
+                         write_exp_elt_opcode (OP_SCOPE);
+                         write_exp_elt_type (type);
+                         write_exp_string (tmp_token);
+                         write_exp_elt_opcode (OP_SCOPE);
+                       }
+       ;
+
+variable:      qualified_name
+       |       COLONCOLON name
+                       {
+                         char *name = copy_name ($2);
+                         struct symbol *sym;
+                         struct minimal_symbol *msymbol;
+
+                         sym =
+                           lookup_symbol (name, (const struct block *) NULL,
+                                          VAR_NAMESPACE, (int *) NULL,
+                                          (struct symtab **) NULL);
+                         if (sym)
+                           {
+                             write_exp_elt_opcode (OP_VAR_VALUE);
+                             write_exp_elt_block (NULL);
+                             write_exp_elt_sym (sym);
+                             write_exp_elt_opcode (OP_VAR_VALUE);
+                             break;
+                           }
+
+                         msymbol = lookup_minimal_symbol (name, NULL, NULL);
+                         if (msymbol != NULL)
+                           {
+                             write_exp_msymbol (msymbol,
+                                                lookup_function_type 
(builtin_type_int),
+                                                builtin_type_int);
+                           }
+                         else
+                           if (!have_full_symbols () && !have_partial_symbols 
())
+                             error ("No symbol table is loaded.  Use the 
\"file\" command.");
+                           else
+                             error ("No symbol \"%s\" in current context.", 
name);
+                       }
+       ;
+
+variable:      name_not_typename
+                       { struct symbol *sym = $1.sym;
+                         struct objc_class *class;
+
+                         if (sym)
+                           {
+                             if (symbol_read_needs_frame (sym))
+                               {
+                                 if (innermost_block == 0 ||
+                                     contained_in (block_found, 
+                                                   innermost_block))
+                                   innermost_block = block_found;
+                               }
+
+                             write_exp_elt_opcode (OP_VAR_VALUE);
+                             /* We want to use the selected frame, not
+                                another more inner frame which happens to
+                                be in the same block.  */
+                             write_exp_elt_block (NULL);
+                             write_exp_elt_sym (sym);
+                             write_exp_elt_opcode (OP_VAR_VALUE);
+                           }
+                         else if ($1.is_a_field_of_this)
+                           {
+                             /* C++: it hangs off of `this'.  Must
+                                not inadvertently convert from a method call
+                                to data ref.  */
+                             if (innermost_block == 0 || 
+                                 contained_in (block_found, innermost_block))
+                               innermost_block = block_found;
+                             write_exp_elt_opcode (OP_THIS);
+                             write_exp_elt_opcode (OP_THIS);
+                             write_exp_elt_opcode (STRUCTOP_PTR);
+                             write_exp_string ($1.stoken);
+                             write_exp_elt_opcode (STRUCTOP_PTR);
+                           }
+                         else
+                           {
+                             struct minimal_symbol *msymbol;
+                             register char *arg = copy_name ($1.stoken);
+
+                             msymbol =
+                               lookup_minimal_symbol (arg, NULL, NULL);
+                             if (msymbol != NULL)
+                               {
+                                 write_exp_msymbol (msymbol,
+                                                    lookup_function_type 
(builtin_type_int),
+                                                    builtin_type_int);
+                               }
+                             else if (!have_full_symbols () && 
+                                      !have_partial_symbols ())
+                               error ("No symbol table is loaded.  Use the 
\"file\" command.");
+                             else
+                               error ("No symbol \"%s\" in current context.",
+                                      copy_name ($1.stoken));
+                           }
+                       }
+       ;
+
+
+ptype  :       typebase
+       /* "const" and "volatile" are curently ignored.  A type qualifier
+          before the type is currently handled in the typebase rule.
+          The reason for recognizing these here (shift/reduce conflicts)
+          might be obsolete now that some pointer to member rules have
+          been deleted.  */
+       |       typebase CONST_KEYWORD
+       |       typebase VOLATILE_KEYWORD
+       |       typebase abs_decl
+               { $$ = follow_types ($1); }
+       |       typebase CONST_KEYWORD abs_decl
+               { $$ = follow_types ($1); }
+       |       typebase VOLATILE_KEYWORD abs_decl
+               { $$ = follow_types ($1); }
+       ;
+
+abs_decl:      '*'
+                       { push_type (tp_pointer); $$ = 0; }
+       |       '*' abs_decl
+                       { push_type (tp_pointer); $$ = $2; }
+       |       '&'
+                       { push_type (tp_reference); $$ = 0; }
+       |       '&' abs_decl
+                       { push_type (tp_reference); $$ = $2; }
+       |       direct_abs_decl
+       ;
+
+direct_abs_decl: '(' abs_decl ')'
+                       { $$ = $2; }
+       |       direct_abs_decl array_mod
+                       {
+                         push_type_int ($2);
+                         push_type (tp_array);
+                       }
+       |       array_mod
+                       {
+                         push_type_int ($1);
+                         push_type (tp_array);
+                         $$ = 0;
+                       }
+
+       |       direct_abs_decl func_mod
+                       { push_type (tp_function); }
+       |       func_mod
+                       { push_type (tp_function); }
+       ;
+
+array_mod:     '[' ']'
+                       { $$ = -1; }
+       |       '[' INT ']'
+                       { $$ = $2.val; }
+       ;
+
+func_mod:      '(' ')'
+                       { $$ = 0; }
+       |       '(' nonempty_typelist ')'
+                       { free ((PTR)$2); $$ = 0; }
+       ;
+
+/* We used to try to recognize more pointer to member types here, but
+   that didn't work (shift/reduce conflicts meant that these rules never
+   got executed).  The problem is that
+     int (foo::bar::baz::bizzle)
+   is a function type but
+     int (foo::bar::baz::bizzle::*)
+   is a pointer to member type.  Stroustrup loses again!  */
+
+type   :       ptype
+       |       typebase COLONCOLON '*'
+                       { $$ = lookup_member_type (builtin_type_int, $1); }
+       ;
+
+typebase  /* Implements (approximately): (type-qualifier)* type-specifier */
+       :       TYPENAME
+                       { $$ = $1.type; }
+       |       INT_KEYWORD
+                       { $$ = builtin_type_int; }
+       |       LONG
+                       { $$ = builtin_type_long; }
+       |       SHORT
+                       { $$ = builtin_type_short; }
+       |       LONG INT_KEYWORD
+                       { $$ = builtin_type_long; }
+       |       UNSIGNED LONG INT_KEYWORD
+                       { $$ = builtin_type_unsigned_long; }
+       |       LONG LONG
+                       { $$ = builtin_type_long_long; }
+       |       LONG LONG INT_KEYWORD
+                       { $$ = builtin_type_long_long; }
+       |       UNSIGNED LONG LONG
+                       { $$ = builtin_type_unsigned_long_long; }
+       |       UNSIGNED LONG LONG INT_KEYWORD
+                       { $$ = builtin_type_unsigned_long_long; }
+       |       SHORT INT_KEYWORD
+                       { $$ = builtin_type_short; }
+       |       UNSIGNED SHORT INT_KEYWORD
+                       { $$ = builtin_type_unsigned_short; }
+       |       DOUBLE_KEYWORD
+                       { $$ = builtin_type_double; }
+       |       LONG DOUBLE_KEYWORD
+                       { $$ = builtin_type_long_double; }
+       |       STRUCT name
+                       { $$ = lookup_struct (copy_name ($2),
+                                             expression_context_block); }
+       |       CLASS name
+                       { $$ = lookup_struct (copy_name ($2),
+                                             expression_context_block); }
+       |       UNION name
+                       { $$ = lookup_union (copy_name ($2),
+                                            expression_context_block); }
+       |       ENUM name
+                       { $$ = lookup_enum (copy_name ($2),
+                                           expression_context_block); }
+       |       UNSIGNED typename
+                       { $$ = lookup_unsigned_typename (TYPE_NAME($2.type)); }
+       |       UNSIGNED
+                       { $$ = builtin_type_unsigned_int; }
+       |       SIGNED_KEYWORD typename
+                       { $$ = lookup_signed_typename (TYPE_NAME($2.type)); }
+       |       SIGNED_KEYWORD
+                       { $$ = builtin_type_int; }
+       |       TEMPLATE name '<' type '>'
+                       { $$ = lookup_template_type(copy_name($2), $4,
+                                                   expression_context_block);
+                       }
+       /* "const" and "volatile" are curently ignored.  A type qualifier
+          after the type is handled in the ptype rule.  I think these could
+          be too.  */
+       |       CONST_KEYWORD typebase { $$ = $2; }
+       |       VOLATILE_KEYWORD typebase { $$ = $2; }
+       ;
+
+typename:      TYPENAME
+       |       INT_KEYWORD
+               {
+                 $$.stoken.ptr = "int";
+                 $$.stoken.length = 3;
+                 $$.type = builtin_type_int;
+               }
+       |       LONG
+               {
+                 $$.stoken.ptr = "long";
+                 $$.stoken.length = 4;
+                 $$.type = builtin_type_long;
+               }
+       |       SHORT
+               {
+                 $$.stoken.ptr = "short";
+                 $$.stoken.length = 5;
+                 $$.type = builtin_type_short;
+               }
+       ;
+
+nonempty_typelist
+       :       type
+               { $$ = (struct type **) malloc (sizeof (struct type *) * 2);
+                 $<ivec>$[0] = 1;      /* Number of types in vector */
+                 $$[1] = $1;
+               }
+       |       nonempty_typelist ',' type
+               { int len = sizeof (struct type *) * (++($<ivec>1[0]) + 1);
+                 $$ = (struct type **) realloc ((char *) $1, len);
+                 $$[$<ivec>$[0]] = $3;
+               }
+       ;
+
+name   :       NAME { $$ = $1.stoken; }
+       |       BLOCKNAME { $$ = $1.stoken; }
+       |       TYPENAME { $$ = $1.stoken; }
+       |       NAME_OR_INT  { $$ = $1.stoken; }
+       ;
+
+name_not_typename :    NAME
+       |       BLOCKNAME
+/* These would be useful if name_not_typename was useful, but it is just
+   a fake for "variable", so these cause reduce/reduce conflicts because
+   the parser can't tell whether NAME_OR_INT is a name_not_typename (=variable,
+   =exp) or just an exp.  If name_not_typename was ever used in an lvalue
+   context where only a name could occur, this might be useful.
+       |       NAME_OR_INT
+ */
+       ;
+
+%%
+
+/* Take care of parsing a number (anything that starts with a digit).
+   Set yylval and return the token type; update lexptr.
+   LEN is the number of characters in it.  */
+
+/*** Needs some error checking for the float case ***/
+
+static int
+parse_number (p, len, parsed_float, putithere)
+     register char *p;
+     register int len;
+     int parsed_float;
+     YYSTYPE *putithere;
+{
+  /* FIXME: Shouldn't these be unsigned?  We don't deal with negative values
+     here, and we do kind of silly things like cast to unsigned.  */
+  register LONGEST n = 0;
+  register LONGEST prevn = 0;
+  unsigned LONGEST un;
+
+  register int i = 0;
+  register int c;
+  register int base = input_radix;
+  int unsigned_p = 0;
+
+  /* Number of "L" suffixes encountered.  */
+  int long_p = 0;
+
+  /* We have found a "L" or "U" suffix.  */
+  int found_suffix = 0;
+
+  unsigned LONGEST high_bit;
+  struct type *signed_type;
+  struct type *unsigned_type;
+
+  if (parsed_float)
+    {
+      char c;
+
+      /* It's a float since it contains a point or an exponent.  */
+
+      if (sizeof (putithere->typed_val_float.dval) <= sizeof (float))
+       sscanf (p, "%g", &putithere->typed_val_float.dval);
+      else if (sizeof (putithere->typed_val_float.dval) <= sizeof (double))
+       sscanf (p, "%lg", &putithere->typed_val_float.dval);
+      else
+       {
+#ifdef PRINTF_HAS_LONG_DOUBLE
+         sscanf (p, "%Lg", &putithere->typed_val_float.dval);
+#else
+         /* Scan it into a double, then assign it to the long double.
+            This at least wins with values representable in the range
+            of doubles. */
+         double temp;
+         sscanf (p, "%lg", &temp);
+         putithere->typed_val_float.dval = temp;
+#endif
+       }
+
+      /* See if it has `f' or `l' suffix (float or long double).  */
+
+      c = tolower (p[len - 1]);
+
+      if (c == 'f')
+       putithere->typed_val_float.type = builtin_type_float;
+      else if (c == 'l')
+       putithere->typed_val_float.type = builtin_type_long_double;
+      else if (isdigit (c) || c == '.')
+       putithere->typed_val_float.type = builtin_type_double;
+      else
+       return ERROR;
+
+      return FLOAT;
+    }
+
+  /* Handle base-switching prefixes 0x, 0t, 0d, 0 */
+  if (p[0] == '0')
+    switch (p[1])
+      {
+      case 'x':
+      case 'X':
+       if (len >= 3)
+         {
+           p += 2;
+           base = 16;
+           len -= 2;
+         }
+       break;
+
+      case 't':
+      case 'T':
+      case 'd':
+      case 'D':
+       if (len >= 3)
+         {
+           p += 2;
+           base = 10;
+           len -= 2;
+         }
+       break;
+
+      default:
+       base = 8;
+       break;
+      }
+
+  while (len-- > 0)
+    {
+      c = *p++;
+      if (c >= 'A' && c <= 'Z')
+       c += 'a' - 'A';
+      if (c != 'l' && c != 'u')
+       n *= base;
+      if (c >= '0' && c <= '9')
+       {
+         if (found_suffix)
+           return ERROR;
+         n += i = c - '0';
+       }
+      else
+       {
+         if (base > 10 && c >= 'a' && c <= 'f')
+           {
+             if (found_suffix)
+               return ERROR;
+             n += i = c - 'a' + 10;
+           }
+         else if (c == 'l')
+           {
+             ++long_p;
+             found_suffix = 1;
+           }
+         else if (c == 'u')
+           {
+             unsigned_p = 1;
+             found_suffix = 1;
+           }
+         else
+           return ERROR;       /* Char not a digit */
+       }
+      if (i >= base)
+       return ERROR;           /* Invalid digit in this base */
+
+      /* Portably test for overflow (only works for nonzero values, so make
+        a second check for zero).  FIXME: Can't we just make n and prevn
+        unsigned and avoid this?  */
+      if (c != 'l' && c != 'u' && (prevn >= n) && n != 0)
+       unsigned_p = 1;         /* Try something unsigned */
+
+      /* Portably test for unsigned overflow.
+        FIXME: This check is wrong; for example it doesn't find overflow
+        on 0x123456789 when LONGEST is 32 bits.  */
+      if (c != 'l' && c != 'u' && n != 0)
+       {       
+         if ((unsigned_p && (unsigned LONGEST) prevn >= (unsigned LONGEST) n))
+           error ("Numeric constant too large.");
+       }
+      prevn = n;
+    }
+
+  /* An integer constant is an int, a long, or a long long.  An L
+     suffix forces it to be long; an LL suffix forces it to be long
+     long.  If not forced to a larger size, it gets the first type of
+     the above that it fits in.  To figure out whether it fits, we
+     shift it right and see whether anything remains.  Note that we
+     can't shift sizeof (LONGEST) * HOST_CHAR_BIT bits or more in one
+     operation, because many compilers will warn about such a shift
+     (which always produces a zero result).  Sometimes TARGET_INT_BIT
+     or TARGET_LONG_BIT will be that big, sometimes not.  To deal with
+     the case where it is we just always shift the value more than
+     once, with fewer bits each time.  */
+
+  un = (unsigned LONGEST)n >> 2;
+  if (long_p == 0
+      && (un >> (TARGET_INT_BIT - 2)) == 0)
+    {
+      high_bit = ((unsigned LONGEST)1) << (TARGET_INT_BIT-1);
+
+      /* A large decimal (not hex or octal) constant (between INT_MAX
+        and UINT_MAX) is a long or unsigned long, according to ANSI,
+        never an unsigned int, but this code treats it as unsigned
+        int.  This probably should be fixed.  GCC gives a warning on
+        such constants.  */
+
+      unsigned_type = builtin_type_unsigned_int;
+      signed_type = builtin_type_int;
+    }
+  else if (long_p <= 1
+          && (un >> (TARGET_LONG_BIT - 2)) == 0)
+    {
+      high_bit = ((unsigned LONGEST)1) << (TARGET_LONG_BIT-1);
+      unsigned_type = builtin_type_unsigned_long;
+      signed_type = builtin_type_long;
+    }
+  else
+    {
+      high_bit = (((unsigned LONGEST)1)
+                 << (TARGET_LONG_LONG_BIT - 32 - 1)
+                 << 16
+                 << 16);
+      if (high_bit == 0)
+       /* A long long does not fit in a LONGEST.  */
+       high_bit =
+         (unsigned LONGEST)1 << (sizeof (LONGEST) * HOST_CHAR_BIT - 1);
+      unsigned_type = builtin_type_unsigned_long_long;
+      signed_type = builtin_type_long_long;
+    }
+
+   putithere->typed_val_int.val = n;
+
+   /* If the high bit of the worked out type is set then this number
+      has to be unsigned. */
+
+   if (unsigned_p || (n & high_bit)) 
+     {
+       putithere->typed_val_int.type = unsigned_type;
+     }
+   else 
+     {
+       putithere->typed_val_int.type = signed_type;
+     }
+
+   return INT;
+}
+
+struct token
+{
+  char *operator;
+  int token;
+  enum exp_opcode opcode;
+};
+
+static const struct token tokentab3[] =
+  {
+    {">>=", ASSIGN_MODIFY, BINOP_RSH},
+    {"<<=", ASSIGN_MODIFY, BINOP_LSH}
+  };
+
+static const struct token tokentab2[] =
+  {
+    {"+=", ASSIGN_MODIFY, BINOP_ADD},
+    {"-=", ASSIGN_MODIFY, BINOP_SUB},
+    {"*=", ASSIGN_MODIFY, BINOP_MUL},
+    {"/=", ASSIGN_MODIFY, BINOP_DIV},
+    {"%=", ASSIGN_MODIFY, BINOP_REM},
+    {"|=", ASSIGN_MODIFY, BINOP_BITWISE_IOR},
+    {"&=", ASSIGN_MODIFY, BINOP_BITWISE_AND},
+    {"^=", ASSIGN_MODIFY, BINOP_BITWISE_XOR},
+    {"++", INCREMENT, BINOP_END},
+    {"--", DECREMENT, BINOP_END},
+    {"->", ARROW, BINOP_END},
+    {"&&", ANDAND, BINOP_END},
+    {"||", OROR, BINOP_END},
+    {"::", COLONCOLON, BINOP_END},
+    {"<<", LSH, BINOP_END},
+    {">>", RSH, BINOP_END},
+    {"==", EQUAL, BINOP_END},
+    {"!=", NOTEQUAL, BINOP_END},
+    {"<=", LEQ, BINOP_END},
+    {">=", GEQ, BINOP_END}
+  };
+
+/* Read one token, getting characters through lexptr.  */
+
+static int
+yylex ()
+{
+  int c, tokchr;
+  int namelen;
+  unsigned int i;
+  char *tokstart;
+  char *tokptr;
+  int tempbufindex;
+  static char *tempbuf;
+  static int tempbufsize;
+  
+ retry:
+
+  tokstart = lexptr;
+  /* See if it is a special token of length 3.  */
+  for (i = 0; i < sizeof tokentab3 / sizeof tokentab3[0]; i++)
+    if (STREQN (tokstart, tokentab3[i].operator, 3))
+      {
+       lexptr += 3;
+       yylval.opcode = tokentab3[i].opcode;
+       return tokentab3[i].token;
+      }
+
+  /* See if it is a special token of length 2.  */
+  for (i = 0; i < sizeof tokentab2 / sizeof tokentab2[0]; i++)
+    if (STREQN (tokstart, tokentab2[i].operator, 2))
+      {
+       lexptr += 2;
+       yylval.opcode = tokentab2[i].opcode;
+       return tokentab2[i].token;
+      }
+
+  switch (tokchr = *tokstart)
+    {
+    case 0:
+      return 0;
+
+    case ' ':
+    case '\t':
+    case '\n':
+      lexptr++;
+      goto retry;
+
+    case '\'':
+      /* We either have a character constant ('0' or '\177' for example)
+        or we have a quoted symbol reference ('foo(int,int)' in C++
+        for example). */
+      lexptr++;
+      c = *lexptr++;
+      if (c == '\\')
+       c = parse_escape (&lexptr);
+      else if (c == '\'')
+       error ("Empty character constant.");
+
+      yylval.typed_val_int.val = c;
+      yylval.typed_val_int.type = builtin_type_char;
+
+      c = *lexptr++;
+      if (c != '\'')
+       {
+         namelen = skip_quoted (tokstart) - tokstart;
+         if (namelen > 2)
+           {
+             lexptr = tokstart + namelen;
+             if (lexptr[-1] != '\'')
+               error ("Unmatched single quote.");
+             namelen -= 2;
+             tokstart++;
+             goto tryname;
+           }
+         error ("Invalid character constant.");
+       }
+      return INT;
+
+    case '(':
+      paren_depth++;
+      lexptr++;
+      return '(';
+
+    case ')':
+      if (paren_depth == 0)
+       return 0;
+      paren_depth--;
+      lexptr++;
+      return ')';
+
+    case ',':
+      if (comma_terminates && paren_depth == 0)
+       return 0;
+      lexptr++;
+      return ',';
+
+    case '.':
+      /* Might be a floating point number.  */
+      if (lexptr[1] < '0' || lexptr[1] > '9')
+       goto symbol;            /* Nope, must be a symbol. */
+      /* FALL THRU into number case.  */
+
+    case '0':
+    case '1':
+    case '2':
+    case '3':
+    case '4':
+    case '5':
+    case '6':
+    case '7':
+    case '8':
+    case '9':
+      {
+       /* It's a number.  */
+       int got_dot = 0, got_e = 0, toktype = FLOAT;
+       /* initialize toktype to anything other than ERROR. */
+       register char *p = tokstart;
+       int hex = input_radix > 10;
+       int local_radix = input_radix;
+       if (tokchr == '0' && (p[1] == 'x' || p[1] == 'X'))
+         {
+           p += 2;
+           hex = 1;
+           local_radix = 16;
+         }
+       else if (tokchr == '0' && (p[1]=='t' || p[1]=='T' || p[1]=='d' || 
p[1]=='D'))
+         {
+           p += 2;
+           hex = 0;
+           local_radix = 10;
+         }
+
+       for (;; ++p)
+         {
+           /* This test includes !hex because 'e' is a valid hex digit
+              and thus does not indicate a floating point number when
+              the radix is hex.  */
+
+           if (!hex && (*p == 'e' || *p == 'E'))
+             if (got_e)
+               toktype = ERROR;        /* only one 'e' in a float */
+             else
+               got_e = 1;
+           /* This test does not include !hex, because a '.' always indicates
+              a decimal floating point number regardless of the radix.  */
+           else if (*p == '.')
+             if (got_dot)
+               toktype = ERROR;        /* only one '.' in a float */
+             else
+               got_dot = 1;
+           else if (got_e && (p[-1] == 'e' || p[-1] == 'E') &&
+                   (*p == '-' || *p == '+'))
+             /* This is the sign of the exponent, not the end of the
+                number.  */
+             continue;
+           /* Always take decimal digits; parse_number handles radix error */
+           else if (*p >= '0' && *p <= '9')
+             continue;
+           /* We will take letters only if hex is true, and only 
+              up to what the input radix would permit.  FSF was content
+              to rely on parse_number to validate; but it leaks. */
+           else if (*p >= 'a' && *p <= 'z') {
+             if (!hex || *p >= ('a' + local_radix - 10))
+               toktype = ERROR;
+           }
+           else if (*p >= 'A' && *p <= 'Z') {
+             if (!hex || *p >= ('A' + local_radix - 10))
+               toktype = ERROR;
+           }
+           else break;
+         }
+       if (toktype != ERROR)
+         toktype = parse_number (tokstart, p - tokstart, got_dot|got_e, 
&yylval);
+        if (toktype == ERROR)
+         {
+           char *err_copy = (char *) alloca (p - tokstart + 1);
+
+           memcpy (err_copy, tokstart, p - tokstart);
+           err_copy[p - tokstart] = 0;
+           error ("Invalid number \"%s\".", err_copy);
+         }
+       lexptr = p;
+       return toktype;
+      }
+
+    case '+':
+    case '-':
+    case '*':
+    case '/':
+    case '%':
+    case '|':
+    case '&':
+    case '^':
+    case '~':
+    case '!':
+#if 0
+    case '@':          /* moved out below */
+#endif
+    case '<':
+    case '>':
+    case '[':
+    case ']':
+    case '?':
+    case ':':
+    case '=':
+    case '{':
+    case '}':
+    symbol:
+      lexptr++;
+      return tokchr;
+
+    case '@':
+      if (strncmp(tokstart, "@selector", 9) == 0)
+       {
+         tokptr = strchr(tokstart, '(');
+         if (tokptr == NULL)
+           {
+             error ("Missing '(' in @SELECTOR(...)");
+           }
+         tempbufindex = 0;
+         tokptr++;     /* skip the '(' */
+         do {
+           /* Grow the static temp buffer if necessary, including allocating
+              the first one on demand. */
+           if (tempbufindex + 1 >= tempbufsize)
+             {
+               tempbuf = (char *) realloc (tempbuf, tempbufsize += 64);
+             }
+           tempbuf[tempbufindex++] = *tokptr++;
+         } while ((*tokptr != ')') && (*tokptr != '\0'));
+         if (*tokptr++ != ')')
+           {
+             error ("Missing ')' in @SELECTOR(...)");
+           }
+         tempbuf[tempbufindex] = '\0';
+         yylval.sval.ptr = tempbuf;
+         yylval.sval.length = tempbufindex;
+         lexptr = tokptr;
+         return SELECTOR;
+       }
+      if (tokstart[1] != '"')
+        {
+          lexptr++;
+          return tokchr;
+        }
+      /* ObjC NextStep NSString constant: fall thru and parse like STRING */
+      tokstart++;
+
+    case '"':
+
+      /* Build the gdb internal form of the input string in tempbuf,
+        translating any standard C escape forms seen.  Note that the
+        buffer is null byte terminated *only* for the convenience of
+        debugging gdb itself and printing the buffer contents when
+        the buffer contains no embedded nulls.  Gdb does not depend
+        upon the buffer being null byte terminated, it uses the length
+        string instead.  This allows gdb to handle C strings (as well
+        as strings in other languages) with embedded null bytes */
+
+      tokptr = ++tokstart;
+      tempbufindex = 0;
+
+      do {
+       /* Grow the static temp buffer if necessary, including allocating
+          the first one on demand. */
+       if (tempbufindex + 1 >= tempbufsize)
+         {
+           tempbuf = (char *) realloc (tempbuf, tempbufsize += 64);
+         }
+       switch (*tokptr)
+         {
+         case '\0':
+         case '"':
+           /* Do nothing, loop will terminate. */
+           break;
+         case '\\':
+           tokptr++;
+           c = parse_escape (&tokptr);
+           if (c == -1)
+             {
+               continue;
+             }
+           tempbuf[tempbufindex++] = c;
+           break;
+         default:
+           tempbuf[tempbufindex++] = *tokptr++;
+           break;
+         }
+      } while ((*tokptr != '"') && (*tokptr != '\0'));
+      if (*tokptr++ != '"')
+       {
+         error ("Unterminated string in expression.");
+       }
+      tempbuf[tempbufindex] = '\0';    /* See note above */
+      yylval.sval.ptr = tempbuf;
+      yylval.sval.length = tempbufindex;
+      lexptr = tokptr;
+      return (tokchr == '@' ? NSSTRING : STRING);
+    }
+
+  if (!(tokchr == '_' || tokchr == '$' || 
+       (tokchr >= 'a' && tokchr <= 'z') || (tokchr >= 'A' && tokchr <= 'Z')))
+    /* We must have come across a bad character (e.g. ';').  */
+    error ("Invalid character '%c' in expression.", c);
+
+  /* It's a name.  See how long it is.  */
+  namelen = 0;
+  for (c = tokstart[namelen];
+       (c == '_' || c == '$' || (c >= '0' && c <= '9')
+       || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '<');)
+    {
+       if (c == '<')
+        {
+          int i = namelen;
+          while (tokstart[++i] && tokstart[i] != '>');
+          if (tokstart[i] == '>')
+            namelen = i;
+         }
+       c = tokstart[++namelen];
+     }
+
+  /* The token "if" terminates the expression and is NOT 
+     removed from the input stream.  */
+  if (namelen == 2 && tokstart[0] == 'i' && tokstart[1] == 'f')
+    {
+      return 0;
+    }
+
+  lexptr += namelen;
+
+  tryname:
+
+  /* Catch specific keywords.  Should be done with a data structure.  */
+  switch (namelen)
+    {
+    case 8:
+      if (STREQN (tokstart, "unsigned", 8))
+       return UNSIGNED;
+      if (current_language->la_language == language_cplus
+         && STREQN (tokstart, "template", 8))
+       return TEMPLATE;
+      if (STREQN (tokstart, "volatile", 8))
+       return VOLATILE_KEYWORD;
+      break;
+    case 6:
+      if (STREQN (tokstart, "struct", 6))
+       return STRUCT;
+      if (STREQN (tokstart, "signed", 6))
+       return SIGNED_KEYWORD;
+      if (STREQN (tokstart, "sizeof", 6))      
+       return SIZEOF;
+      if (STREQN (tokstart, "double", 6))      
+       return DOUBLE_KEYWORD;
+      break;
+    case 5:
+      if ((current_language->la_language == language_cplus ||
+          current_language->la_language == language_objc)
+         && STREQN (tokstart, "class", 5))
+       return CLASS;
+      if (STREQN (tokstart, "union", 5))
+       return UNION;
+      if (STREQN (tokstart, "short", 5))
+       return SHORT;
+      if (STREQN (tokstart, "const", 5))
+       return CONST_KEYWORD;
+      break;
+    case 4:
+      if (STREQN (tokstart, "enum", 4))
+       return ENUM;
+      if (STREQN (tokstart, "long", 4))
+       return LONG;
+      if (current_language->la_language == language_cplus
+         && STREQN (tokstart, "this", 4))
+       {
+         static const char this_name[] =
+                                { CPLUS_MARKER, 't', 'h', 'i', 's', '\0' };
+
+         if (lookup_symbol (this_name, expression_context_block,
+                            VAR_NAMESPACE, (int *) NULL,
+                            (struct symtab **) NULL))
+           return THIS;
+       }
+      break;
+    case 3:
+      if (STREQN (tokstart, "int", 3))
+       return INT_KEYWORD;
+      break;
+    default:
+      break;
+    }
+
+  yylval.sval.ptr = tokstart;
+  yylval.sval.length = namelen;
+
+  if (*tokstart == '$')
+    {
+      write_dollar_variable (yylval.sval);
+      return VARIABLE;
+    }
+
+  /* Use token-type BLOCKNAME for symbols that happen to be defined as
+     functions or symtabs.  If this is not so, then ...
+     Use token-type TYPENAME for symbols that happen to be defined
+     currently as names of types; NAME for other symbols.
+     The caller is not constrained to care about the distinction.  */
+  {
+    char *tmp = copy_name (yylval.sval);
+    struct symbol *sym;
+    int is_a_field_of_this = 0, *need_this;
+    int hextype;
+
+    if (current_language->la_language == language_cplus ||
+       current_language->la_language == language_objc)
+      need_this = &is_a_field_of_this;
+    else
+      need_this = (int *) NULL;
+
+    sym = lookup_symbol (tmp, expression_context_block,
+                        VAR_NAMESPACE,
+                        need_this,
+                        (struct symtab **) NULL);
+    /* Call lookup_symtab, not lookup_partial_symtab, in case there are
+       no psymtabs (coff, xcoff, or some future change to blow away the
+       psymtabs once once symbols are read).  */
+    if ((sym && SYMBOL_CLASS (sym) == LOC_BLOCK) ||
+        lookup_symtab (tmp))
+      {
+       yylval.ssym.sym = sym;
+       yylval.ssym.is_a_field_of_this = is_a_field_of_this;
+       return BLOCKNAME;
+      }
+    if (sym && SYMBOL_CLASS (sym) == LOC_TYPEDEF)
+        {
+#if 1
+         /* Despite the following flaw, we need to keep this code enabled.
+            Because we can get called from check_stub_method, if we don't
+            handle nested types then it screws many operations in any
+            program which uses nested types.  */
+         /* In "A::x", if x is a member function of A and there happens
+            to be a type (nested or not, since the stabs don't make that
+            distinction) named x, then this code incorrectly thinks we
+            are dealing with nested types rather than a member function.  */
+
+         char *p;
+         char *namestart;
+         struct symbol *best_sym;
+
+         /* Look ahead to detect nested types.  This probably should be
+            done in the grammar, but trying seemed to introduce a lot
+            of shift/reduce and reduce/reduce conflicts.  It's possible
+            that it could be done, though.  Or perhaps a non-grammar, but
+            less ad hoc, approach would work well.  */
+
+         /* Since we do not currently have any way of distinguishing
+            a nested type from a non-nested one (the stabs don't tell
+            us whether a type is nested), we just ignore the
+            containing type.  */
+
+         p = lexptr;
+         best_sym = sym;
+         while (1)
+           {
+             /* Skip whitespace.  */
+             while (*p == ' ' || *p == '\t' || *p == '\n')
+               ++p;
+             if (*p == ':' && p[1] == ':')
+               {
+                 /* Skip the `::'.  */
+                 p += 2;
+                 /* Skip whitespace.  */
+                 while (*p == ' ' || *p == '\t' || *p == '\n')
+                   ++p;
+                 namestart = p;
+                 while (*p == '_' || *p == '$' || (*p >= '0' && *p <= '9')
+                        || (*p >= 'a' && *p <= 'z')
+                        || (*p >= 'A' && *p <= 'Z'))
+                   ++p;
+                 if (p != namestart)
+                   {
+                     struct symbol *cur_sym;
+                     /* As big as the whole rest of the expression, which is
+                        at least big enough.  */
+                     char *ncopy = alloca (strlen (tmp)+strlen (namestart)+3);
+                     char *tmp1;
+
+                     tmp1 = ncopy;
+                     memcpy (tmp1, tmp, strlen (tmp));
+                     tmp1 += strlen (tmp);
+                     memcpy (tmp1, "::", 2);
+                     tmp1 += 2;
+                     memcpy (tmp1, namestart, p - namestart);
+                     tmp1[p - namestart] = '\0';
+                     cur_sym = lookup_symbol (ncopy, expression_context_block,
+                                              VAR_NAMESPACE, (int *) NULL,
+                                              (struct symtab **) NULL);
+                     if (cur_sym)
+                       {
+                         if (SYMBOL_CLASS (cur_sym) == LOC_TYPEDEF)
+                           {
+                             best_sym = cur_sym;
+                             lexptr = p;
+                           }
+                         else
+                           break;
+                       }
+                     else
+                       break;
+                   }
+                 else
+                   break;
+               }
+             else
+               break;
+           }
+
+         yylval.tsym.type = SYMBOL_TYPE (best_sym);
+#else /* not 0 */
+         yylval.tsym.type = SYMBOL_TYPE (sym);
+#endif /* not 0 */
+         return TYPENAME;
+        }
+    if ((yylval.tsym.type = lookup_primitive_typename (tmp)) != 0)
+       return TYPENAME;
+
+    if (!sym)                  /* see if it's an ObjC classname */
+      {
+       CORE_ADDR Class = lookup_objc_class(tmp);
+       if (Class)
+         {
+           yylval.lval = Class;
+           return CLASSNAME;
+         }
+      }
+
+    /* Input names that aren't symbols but ARE valid hex numbers,
+       when the input radix permits them, can be names or numbers
+       depending on the parse.  Note we support radixes > 16 here.  */
+    if (!sym && 
+        ((tokstart[0] >= 'a' && tokstart[0] < 'a' + input_radix - 10) ||
+         (tokstart[0] >= 'A' && tokstart[0] < 'A' + input_radix - 10)))
+      {
+       YYSTYPE newlval;        /* Its value is ignored.  */
+       hextype = parse_number (tokstart, namelen, 0, &newlval);
+       if (hextype == INT)
+         {
+           yylval.ssym.sym = sym;
+           yylval.ssym.is_a_field_of_this = is_a_field_of_this;
+           return NAME_OR_INT;
+         }
+      }
+
+    /* Any other kind of symbol */
+    yylval.ssym.sym = sym;
+    yylval.ssym.is_a_field_of_this = is_a_field_of_this;
+    return NAME;
+  }
+}
+
+void
+yyerror (msg)
+     char *msg;
+{
+  if (*lexptr == '\0')
+    error("A %s near end of expression.",  (msg ? msg : "error"));
+  else
+    error ("A %s in expression, near `%s'.", (msg ? msg : "error"), lexptr);
+}
diff -Naur gdb-5.0.cvs20011007/gdb/objc-lang.c 
gdb-5.0.cvs20011007.new/gdb/objc-lang.c
--- gdb-5.0.cvs20011007/gdb/objc-lang.c Wed Dec 31 19:00:00 1969
+++ gdb-5.0.cvs20011007.new/gdb/objc-lang.c     Sat Nov 24 22:51:54 2001
@@ -0,0 +1,1228 @@
+/* Objective C language support routines for GDB, the GNU debugger.
+   Copyright 1996 NeXT Software, Inc.
+
+This file is part of GDB.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+
+#include "defs.h"
+#include "symtab.h"
+#include "gdbtypes.h"
+#include "expression.h"
+#include "parser-defs.h"
+#include "language.h"
+#include "objc-lang.h"
+#include "complaints.h"
+#include "value.h"
+#include "symfile.h"
+#include "objfiles.h"
+#include "string.h"
+#include "gnu-regex.h"
+#include "command.h"
+
+extern value_ptr find_function_in_inferior PARAMS((char *));
+
+/* Complaints about ObjC classes, selectors, etc.  */
+
+static struct complaint noclass_complaint = 
+  {"%s: class not found", 0, 0};
+
+static struct complaint noselector_complaint = 
+  {"%s: selector not found", 0, 0};
+
+
+/* test for a string of the form "+/-[...]" with balanced brackets.
+   If successful, return pointer to closing right bracket, else NULL */
+
+char *
+is_objc_demangled(name)
+     char *name;
+{
+  int nesting = 0;
+  char *temp;
+
+  if ((temp = strchr(name, '-')) ||    /* skip prefix (might be file name) */
+      (temp = strchr(name, '+')))
+    name = temp;
+
+  if ((*name == '-' || *name == '+') &&                /* must start with +/- 
*/
+      *++name == '[')                          /* followed by [       */
+    {
+      nesting = 1;
+      while (nesting && *++name)               /* scan for balanced [] */
+       if (*name == '[')
+         nesting++;
+       else if (*name == ']')
+         nesting--;
+      if (nesting == 0)
+       return name;                    /* success, matching ] found */
+    }
+  return NULL;                         /* nope, doesn't fit pattern */
+}
+
+
+
+/* Lookup a structure type named "struct NAME",
+   visible in lexical block BLOCK.  
+   If NOERR is nonzero, return zero if NAME is not suitably defined.  */
+
+static struct symbol *
+lookup_struct_typedef (name, block, noerr)
+     char *name;
+     struct block *block;
+     int noerr;
+{
+  register struct symbol *sym;
+
+  sym = lookup_symbol (name, block, STRUCT_NAMESPACE, 0,
+                      (struct symtab **) NULL);
+
+  if (sym == NULL)
+  {
+    if (noerr)
+      return 0;
+    else
+      error ("No struct type named %s.", name);
+  }
+  if (TYPE_CODE (SYMBOL_TYPE (sym)) != TYPE_CODE_STRUCT)
+  {
+    if (noerr)
+      return 0;
+    else
+      error ("This context has class, union or enum %s, not a struct.", name);
+  }
+  return sym;
+}
+
+#if 0
+struct symbol *
+lookup_objc_class(char *classname)
+{
+  struct symbol * sym = NULL;
+
+  if (current_language->la_language != language_objc &&
+      current_language->la_language != language_unknown)
+    return NULL;       /* no sense looking if this isn't ObjC */
+
+  if (!(sym = lookup_struct_typedef (classname, 
+                                    expression_context_block, 1))) {
+    complain (&noclass_complaint, classname);
+    return NULL;
+  }
+  if (SYMBOL_LANGUAGE (sym) != language_objc &&
+      SYMBOL_LANGUAGE (sym) != language_unknown)
+    return NULL;
+
+  /* FIXME: don't know how to distinguish a class from a struct! */
+  return sym;
+}
+#else
+CORE_ADDR 
+lookup_objc_class(classname)
+     char *classname;
+{
+  value_ptr function, classval;
+
+  if (lookup_minimal_symbol("objc_lookUpClass", 0, 0))
+    function = find_function_in_inferior("objc_lookUpClass");
+  else
+    function = find_function_in_inferior("objc_lookup_class");
+
+  classval = value_string(classname, strlen(classname) + 1);
+  classval = value_coerce_array(classval);
+  return (CORE_ADDR) value_as_long (call_function_by_hand (function,
+                                                          1, &classval));
+}
+#endif
+
+int
+lookup_child_selector(selname)
+     char *selname;
+{
+  value_ptr function, selstring;
+
+  if (lookup_minimal_symbol("sel_getUid", 0, 0))
+    function = find_function_in_inferior("sel_getUid");
+  else
+    function = find_function_in_inferior("sel_get_any_uid");
+
+  selstring = value_coerce_array(value_string(selname, strlen(selname) + 1));
+  return value_as_long(call_function_by_hand(function, 1, &selstring));
+}
+
+value_ptr 
+value_nsstring(ptr, len)
+     char *ptr;
+     int len;
+{
+  value_ptr stringValue = value_string(ptr, len);
+  value_ptr new_string, nsstringValue;
+  struct symbol *sym;
+  struct type *type;
+
+  /* _NSNewStringFromCString replaces "istr" after Lantern2A */
+  if (!(new_string = find_function_in_inferior("_NSNewStringFromCString")))
+    if (!(new_string = find_function_in_inferior("istr")))
+      error ("NSString: internal error -- can't create new NSString");
+
+
+  if (!(sym = lookup_struct_typedef("NSString", 0, 0)))
+    if (!(sym = lookup_struct_typedef("NXString", 0, 0)))
+      error ("NSString: no NSString type known.");
+
+  type = lookup_pointer_type(SYMBOL_TYPE (sym));
+  stringValue = value_coerce_array(stringValue);
+  nsstringValue = call_function_by_hand(new_string, 1, &stringValue);
+  VALUE_TYPE(nsstringValue) = type;
+  return nsstringValue;
+}
+    
+
+
+/* Objective C name demangling */
+
+char *
+objc_demangle (mangled)
+     const char *mangled;
+{
+  char *demangled, *cp;
+
+  if (mangled[0] == '_' &&
+     (mangled[1] == 'i' || mangled[1] == 'c') &&
+      mangled[2] == '_')
+    {
+      cp = demangled = xmalloc(strlen(mangled) + 2);
+
+      if (mangled[1] == 'i')
+       *cp++ = '-';            /* for instance method */
+      else
+       *cp++ = '+';            /* for class    method */
+
+      *cp++ = '[';             /* opening left brace  */
+      strcpy(cp, mangled+3);   /* tack on the rest of the mangled name */
+
+      while (*cp && *cp == '_')
+       cp++;                   /* skip any initial underbars in class name */
+
+      if (!(cp = strchr(cp, '_')))     /* find first non-initial underbar */
+       {
+         free(demangled);      /* not mangled name */
+         return NULL;
+       }
+      if (cp[1] == '_') {      /* easy case: no category name     */
+       *cp++ = ' ';            /* replace two '_' with one ' '    */
+       strcpy(cp, mangled + (cp - demangled) + 2);
+      }
+      else {
+       *cp++ = '(';            /* less easy case: category name */
+       if (!(cp = strchr(cp, '_')))
+         {
+           free(demangled);    /* not mangled name */
+           return NULL;
+         }
+       *cp++ = ')';
+       *cp++ = ' ';            /* overwriting 1st char of method name... */
+       strcpy(cp, mangled + (cp - demangled)); /* get it back */
+      }
+
+      while (*cp && *cp == '_')
+       cp++;                   /* skip any initial underbars in method name */
+
+      for (; *cp; cp++)
+       if (*cp == '_')
+         *cp = ':';            /* replace remaining '_' with ':' */
+
+      *cp++ = ']';             /* closing right brace */
+      *cp++ = 0;               /* string terminator */
+      return demangled;
+    }
+  else
+    return NULL;       /* not an objc mangled name */
+}
+
+/* Print the character C on STREAM as part of the contents of a literal
+   string whose delimiter is QUOTER.  Note that that format for printing
+   characters and strings is language specific. */
+
+static void
+emit_char (c, stream, quoter)
+     register int c;
+     struct ui_file *stream;
+     int quoter;
+{
+
+  c &= 0xFF;                   /* Avoid sign bit follies */
+
+  if (PRINT_LITERAL_FORM (c))
+    {
+      if (c == '\\' || c == quoter)
+       {
+         fputs_filtered ("\\", stream);
+       }
+      fprintf_filtered (stream, "%c", c);
+    }
+  else
+    {
+      switch (c)
+       {
+       case '\n':
+         fputs_filtered ("\\n", stream);
+         break;
+       case '\b':
+         fputs_filtered ("\\b", stream);
+         break;
+       case '\t':
+         fputs_filtered ("\\t", stream);
+         break;
+       case '\f':
+         fputs_filtered ("\\f", stream);
+         break;
+       case '\r':
+         fputs_filtered ("\\r", stream);
+         break;
+       case '\033':
+         fputs_filtered ("\\e", stream);
+         break;
+       case '\007':
+         fputs_filtered ("\\a", stream);
+         break;
+       default:
+         fprintf_filtered (stream, "\\%.3o", (unsigned int) c);
+         break;
+       }
+    }
+}
+
+static void
+objc_printchar (c, stream)
+     int c;
+     struct ui_file *stream;
+{
+  fputs_filtered ("'", stream);
+  emit_char (c, stream, '\'');
+  fputs_filtered ("'", stream);
+}
+
+/* Print the character string STRING, printing at most LENGTH characters.
+   Printing stops early if the number hits print_max; repeat counts
+   are printed as appropriate.  Print ellipses at the end if we
+   had to stop before printing LENGTH characters, or if FORCE_ELLIPSES.  */
+
+static void
+objc_printstr (stream, string, length, force_ellipses)
+     struct ui_file *stream;
+     char *string;
+     unsigned int length;
+     int force_ellipses;
+{
+  register unsigned int i;
+  unsigned int things_printed = 0;
+  int in_quotes = 0;
+  int need_comma = 0;
+  extern int inspect_it;
+  extern int repeat_count_threshold;
+  extern int print_max;
+
+  /* If the string was not truncated due to `set print elements', and
+     the last byte of it is a null, we don't print that, in traditional C
+     style.  */
+  if ((!force_ellipses) && length > 0 && string[length-1] == '\0')
+    length--;
+
+  if (length == 0)
+    {
+      fputs_filtered ("\"\"", stream);
+      return;
+    }
+
+  for (i = 0; i < length && things_printed < print_max; ++i)
+    {
+      /* Position of the character we are examining
+        to see whether it is repeated.  */
+      unsigned int rep1;
+      /* Number of repetitions we have detected so far.  */
+      unsigned int reps;
+
+      QUIT;
+
+      if (need_comma)
+       {
+         fputs_filtered (", ", stream);
+         need_comma = 0;
+       }
+
+      rep1 = i + 1;
+      reps = 1;
+      while (rep1 < length && string[rep1] == string[i])
+       {
+         ++rep1;
+         ++reps;
+       }
+
+      if (reps > repeat_count_threshold)
+       {
+         if (in_quotes)
+           {
+             if (inspect_it)
+               fputs_filtered ("\\\", ", stream);
+             else
+               fputs_filtered ("\", ", stream);
+             in_quotes = 0;
+           }
+         objc_printchar (string[i], stream);
+         fprintf_filtered (stream, " <repeats %u times>", reps);
+         i = rep1 - 1;
+         things_printed += repeat_count_threshold;
+         need_comma = 1;
+       }
+      else
+       {
+         if (!in_quotes)
+           {
+             if (inspect_it)
+               fputs_filtered ("\\\"", stream);
+             else
+               fputs_filtered ("\"", stream);
+             in_quotes = 1;
+           }
+         emit_char (string[i], stream, '"');
+         ++things_printed;
+       }
+    }
+
+  /* Terminate the quotes if necessary.  */
+  if (in_quotes)
+    {
+      if (inspect_it)
+       fputs_filtered ("\\\"", stream);
+      else
+       fputs_filtered ("\"", stream);
+    }
+
+  if (force_ellipses || i < length)
+    fputs_filtered ("...", stream);
+}
+
+/* Create a fundamental C type using default reasonable for the current
+   target machine.
+
+   Some object/debugging file formats (DWARF version 1, COFF, etc) do not
+   define fundamental types such as "int" or "double".  Others (stabs or
+   DWARF version 2, etc) do define fundamental types.  For the formats which
+   don't provide fundamental types, gdb can create such types using this
+   function.
+
+   FIXME:  Some compilers distinguish explicitly signed integral types
+   (signed short, signed int, signed long) from "regular" integral types
+   (short, int, long) in the debugging information.  There is some dis-
+   agreement as to how useful this feature is.  In particular, gcc does
+   not support this.  Also, only some debugging formats allow the
+   distinction to be passed on to a debugger.  For now, we always just
+   use "short", "int", or "long" as the type name, for both the implicit
+   and explicitly signed types.  This also makes life easier for the
+   gdb test suite since we don't have to account for the differences
+   in output depending upon what the compiler and debugging format
+   support.  We will probably have to re-examine the issue when gdb
+   starts taking it's fundamental type information directly from the
+   debugging information supplied by the compiler.  address@hidden */
+
+static struct type *
+objc_create_fundamental_type (objfile, typeid)
+     struct objfile *objfile;
+     int typeid;
+{
+  register struct type *type = NULL;
+
+  switch (typeid)
+    {
+      default:
+       /* FIXME:  For now, if we are asked to produce a type not in this
+          language, create the equivalent of a C integer type with the
+          name "<?type?>".  When all the dust settles from the type
+          reconstruction work, this should probably become an error. */
+       type = init_type (TYPE_CODE_INT,
+                         TARGET_INT_BIT / TARGET_CHAR_BIT,
+                         0, "<?type?>", objfile);
+        warning ("internal error: no C/C++ fundamental type %d", typeid);
+       break;
+      case FT_VOID:
+       type = init_type (TYPE_CODE_VOID,
+                         TARGET_CHAR_BIT / TARGET_CHAR_BIT,
+                         0, "void", objfile);
+       break;
+      case FT_CHAR:
+       type = init_type (TYPE_CODE_INT,
+                         TARGET_CHAR_BIT / TARGET_CHAR_BIT,
+                         0, "char", objfile);
+       break;
+      case FT_SIGNED_CHAR:
+       type = init_type (TYPE_CODE_INT,
+                         TARGET_CHAR_BIT / TARGET_CHAR_BIT,
+                         0, "signed char", objfile);
+       break;
+      case FT_UNSIGNED_CHAR:
+       type = init_type (TYPE_CODE_INT,
+                         TARGET_CHAR_BIT / TARGET_CHAR_BIT,
+                         TYPE_FLAG_UNSIGNED, "unsigned char", objfile);
+       break;
+      case FT_SHORT:
+       type = init_type (TYPE_CODE_INT,
+                         TARGET_SHORT_BIT / TARGET_CHAR_BIT,
+                         0, "short", objfile);
+       break;
+      case FT_SIGNED_SHORT:
+       type = init_type (TYPE_CODE_INT,
+                         TARGET_SHORT_BIT / TARGET_CHAR_BIT,
+                         0, "short", objfile); /* FIXME-fnf */
+       break;
+      case FT_UNSIGNED_SHORT:
+       type = init_type (TYPE_CODE_INT,
+                         TARGET_SHORT_BIT / TARGET_CHAR_BIT,
+                         TYPE_FLAG_UNSIGNED, "unsigned short", objfile);
+       break;
+      case FT_INTEGER:
+       type = init_type (TYPE_CODE_INT,
+                         TARGET_INT_BIT / TARGET_CHAR_BIT,
+                         0, "int", objfile);
+       break;
+      case FT_SIGNED_INTEGER:
+       type = init_type (TYPE_CODE_INT,
+                         TARGET_INT_BIT / TARGET_CHAR_BIT,
+                         0, "int", objfile); /* FIXME -fnf */
+       break;
+      case FT_UNSIGNED_INTEGER:
+       type = init_type (TYPE_CODE_INT,
+                         TARGET_INT_BIT / TARGET_CHAR_BIT,
+                         TYPE_FLAG_UNSIGNED, "unsigned int", objfile);
+       break;
+      case FT_LONG:
+       type = init_type (TYPE_CODE_INT,
+                         TARGET_LONG_BIT / TARGET_CHAR_BIT,
+                         0, "long", objfile);
+       break;
+      case FT_SIGNED_LONG:
+       type = init_type (TYPE_CODE_INT,
+                         TARGET_LONG_BIT / TARGET_CHAR_BIT,
+                         0, "long", objfile); /* FIXME -fnf */
+       break;
+      case FT_UNSIGNED_LONG:
+       type = init_type (TYPE_CODE_INT,
+                         TARGET_LONG_BIT / TARGET_CHAR_BIT,
+                         TYPE_FLAG_UNSIGNED, "unsigned long", objfile);
+       break;
+      case FT_LONG_LONG:
+       type = init_type (TYPE_CODE_INT,
+                         TARGET_LONG_LONG_BIT / TARGET_CHAR_BIT,
+                         0, "long long", objfile);
+       break;
+      case FT_SIGNED_LONG_LONG:
+       type = init_type (TYPE_CODE_INT,
+                         TARGET_LONG_LONG_BIT / TARGET_CHAR_BIT,
+                         0, "signed long long", objfile);
+       break;
+      case FT_UNSIGNED_LONG_LONG:
+       type = init_type (TYPE_CODE_INT,
+                         TARGET_LONG_LONG_BIT / TARGET_CHAR_BIT,
+                         TYPE_FLAG_UNSIGNED, "unsigned long long", objfile);
+       break;
+      case FT_FLOAT:
+       type = init_type (TYPE_CODE_FLT,
+                         TARGET_FLOAT_BIT / TARGET_CHAR_BIT,
+                         0, "float", objfile);
+       break;
+      case FT_DBL_PREC_FLOAT:
+       type = init_type (TYPE_CODE_FLT,
+                         TARGET_DOUBLE_BIT / TARGET_CHAR_BIT,
+                         0, "double", objfile);
+       break;
+      case FT_EXT_PREC_FLOAT:
+       type = init_type (TYPE_CODE_FLT,
+                         TARGET_LONG_DOUBLE_BIT / TARGET_CHAR_BIT,
+                         0, "long double", objfile);
+       break;
+      }
+  return (type);
+}
+
+
+/* Table mapping opcodes into strings for printing operators
+   and precedences of the operators.  */
+
+static const struct op_print objc_op_print_tab[] =
+  {
+    {",",  BINOP_COMMA, PREC_COMMA, 0},
+    {"=",  BINOP_ASSIGN, PREC_ASSIGN, 1},
+    {"||", BINOP_LOGICAL_OR, PREC_LOGICAL_OR, 0},
+    {"&&", BINOP_LOGICAL_AND, PREC_LOGICAL_AND, 0},
+    {"|",  BINOP_BITWISE_IOR, PREC_BITWISE_IOR, 0},
+    {"^",  BINOP_BITWISE_XOR, PREC_BITWISE_XOR, 0},
+    {"&",  BINOP_BITWISE_AND, PREC_BITWISE_AND, 0},
+    {"==", BINOP_EQUAL, PREC_EQUAL, 0},
+    {"!=", BINOP_NOTEQUAL, PREC_EQUAL, 0},
+    {"<=", BINOP_LEQ, PREC_ORDER, 0},
+    {">=", BINOP_GEQ, PREC_ORDER, 0},
+    {">",  BINOP_GTR, PREC_ORDER, 0},
+    {"<",  BINOP_LESS, PREC_ORDER, 0},
+    {">>", BINOP_RSH, PREC_SHIFT, 0},
+    {"<<", BINOP_LSH, PREC_SHIFT, 0},
+    {"+",  BINOP_ADD, PREC_ADD, 0},
+    {"-",  BINOP_SUB, PREC_ADD, 0},
+    {"*",  BINOP_MUL, PREC_MUL, 0},
+    {"/",  BINOP_DIV, PREC_MUL, 0},
+    {"%",  BINOP_REM, PREC_MUL, 0},
+    {"@",  BINOP_REPEAT, PREC_REPEAT, 0},
+    {"-",  UNOP_NEG, PREC_PREFIX, 0},
+    {"!",  UNOP_LOGICAL_NOT, PREC_PREFIX, 0},
+    {"~",  UNOP_COMPLEMENT, PREC_PREFIX, 0},
+    {"*",  UNOP_IND, PREC_PREFIX, 0},
+    {"&",  UNOP_ADDR, PREC_PREFIX, 0},
+    {"sizeof ", UNOP_SIZEOF, PREC_PREFIX, 0},
+    {"++", UNOP_PREINCREMENT, PREC_PREFIX, 0},
+    {"--", UNOP_PREDECREMENT, PREC_PREFIX, 0},
+    /* C++  */
+    {"::", BINOP_SCOPE, PREC_PREFIX, 0},
+    {NULL, 0, 0, 0}
+};
+
+struct type ** const (objc_builtin_types[]) = 
+{
+  &builtin_type_int,
+  &builtin_type_long,
+  &builtin_type_short,
+  &builtin_type_char,
+  &builtin_type_float,
+  &builtin_type_double,
+  &builtin_type_void,
+  &builtin_type_long_long,
+  &builtin_type_signed_char,
+  &builtin_type_unsigned_char,
+  &builtin_type_unsigned_short,
+  &builtin_type_unsigned_int,
+  &builtin_type_unsigned_long,
+  &builtin_type_unsigned_long_long,
+  &builtin_type_long_double,
+  &builtin_type_complex,
+  &builtin_type_double_complex,
+  0
+};
+
+const struct language_defn objc_language_defn = {
+  "objective-c",                               /* Language name */
+  language_objc,
+  objc_builtin_types,
+  range_check_off,
+  type_check_off,
+  case_sensitive_on,
+  objc_parse,
+  objc_error,
+  evaluate_subexp_standard,
+  objc_printchar,              /* Print a character constant */
+  objc_printstr,               /* Function to print string constant */
+  c_emit_char,
+  objc_create_fundamental_type,        /* Create fundamental type in this 
language */
+  c_print_type,                        /* Print a type using appropriate 
syntax */
+  c_val_print,                 /* Print a value using appropriate syntax */
+  c_value_print,               /* Print a top-level value */
+  {"",     "",    "",  ""},    /* Binary format info */
+  {"0%lo",  "0",   "o", ""},   /* Octal format info */
+  {"%ld",   "",    "d", ""},   /* Decimal format info */
+  {"0x%lx", "0x",  "x", ""},   /* Hex format info */
+  objc_op_print_tab,           /* expression operators for printing */
+  1,                           /* c-style arrays */
+  0,                           /* String lower bound */
+  &builtin_type_char,          /* Type of string elements */
+  LANG_MAGIC
+};
+
+/*
+ * ObjC:
+ * Following functions help construct Objective C message calls 
+ */
+
+struct selname         /* for parsing Objective C */
+  {
+    struct selname *next;
+    char *msglist_sel;
+    int msglist_len;
+  };
+
+static int msglist_len;
+static struct selname *selname_chain;
+static char *msglist_sel;
+
+void
+start_msglist()
+{
+  register struct selname *new = 
+    (struct selname *) xmalloc (sizeof (struct selname));
+
+  new->next = selname_chain;
+  new->msglist_len = msglist_len;
+  new->msglist_sel = msglist_sel;
+  msglist_len = 0;
+  msglist_sel = (char *)xmalloc(1);
+  *msglist_sel = 0;
+  selname_chain = new;
+}
+
+void
+add_msglist(str, addcolon)
+     struct stoken *str;
+     int addcolon;
+{
+  char *s, *p;
+  int len, plen;
+
+  if (str == 0) {              /* unnamed arg, or... */
+    if (addcolon == 0) {       /* variable number of args */
+      msglist_len++;
+      return;
+    }
+    p = "";
+    plen = 0;
+  } else {
+    p = str->ptr;
+    plen = str->length;
+  }
+  len = plen + strlen(msglist_sel) + 2;
+  s = (char *)xmalloc(len);
+  strcpy(s, msglist_sel);
+  strncat(s, p, plen);
+  free(msglist_sel);
+  msglist_sel = s;
+  if (addcolon) {
+    s[len-2] = ':';
+    s[len-1] = 0;
+    msglist_len++;
+  } else
+    s[len-2] = '\0';
+}
+
+int
+end_msglist()
+{
+  register int val = msglist_len;
+  register struct selname *sel = selname_chain;
+  register char *p = msglist_sel;
+  int selid;
+
+  selname_chain = sel->next;
+  msglist_len = sel->msglist_len;
+  msglist_sel = sel->msglist_sel;
+  selid = lookup_child_selector(p);
+  if (!selid)
+    error("Can't find selector \"%s\"", p);
+  write_exp_elt_longcst (selid);
+  free(p);
+  write_exp_elt_longcst (val);/* Number of args */
+  free(sel);
+
+  return val;
+}
+
+/*
+ * Function: specialcmp (char *a, char *b, char term)
+ *
+ * Special strcmp: treats ']' and ' ' as end-of-string.
+ * Used for qsorting lists of objc methods (either by class or selector) 
+ */
+
+int specialcmp(a, b)
+     char *a;
+     char *b;
+{
+  while (*a && *a != ' ' && *a != ']' && *b && *b != ' ' && *b != ']')
+    {
+      if (*a != *b)
+       return *a - *b;
+      a++, b++;
+    }
+  if (*a && *a != ' ' && *a != ']')
+    return  1;         /* a is longer therefore greater */
+  if (*b && *b != ' ' && *b != ']')
+    return -1;         /* a is shorter therefore lesser */
+  return    0;         /* a and b are identical */
+}
+
+/*
+ * Function: compare_selectors (void *, void *)
+ *
+ * Comparison function for use with qsort.  Arguments are symbols or msymbols
+ * Compares selector part of objc method name alphabetically.
+ */
+
+static int
+compare_selectors (a, b)
+     void *a;
+     void *b;
+{
+  char *aname, *bname;
+
+  if ((aname = SYMBOL_SOURCE_NAME (*(struct symbol **) a)) == NULL ||
+      (bname = SYMBOL_SOURCE_NAME (*(struct symbol **) b)) == NULL)
+    error ("internal: compare_selectors(1)");
+
+  if ((aname = strchr(aname, ' ')) == NULL ||
+      (bname = strchr(bname, ' ')) == NULL)
+    error ("internal: compare_selectors(2)");
+
+  return specialcmp (aname+1, bname+1);
+}
+
+/*
+ * Function: selectors_info (regexp, from_tty)
+ *
+ * Implements the "Info selectors" command.  Takes an optional regexp arg.
+ * Lists all objective c selectors that match the regexp.  Works by 
+ * grepping thru all symbols for objective c methods.  Output list is 
+ * sorted and uniqued. 
+ */
+
+static void
+selectors_info (regexp, from_tty)
+     char *regexp;
+     int   from_tty;
+{
+  struct objfile       *objfile;
+  struct minimal_symbol *msymbol;
+  char                  *name;
+  char                  *val;
+  int                    matches = 0;
+  int                    maxlen  = 0;
+  int                    ix;
+  char                   myregexp[2048];
+  char                   asel[256];
+  struct symbol        **sym_arr;
+  int                    plusminus = 0;
+
+  if (regexp == NULL)
+    strcpy(myregexp, ".*]");   /* null input, match all objc methods */
+  else
+    {
+      if (*regexp == '+' || *regexp == '-')    /* if regexp starts with +/- */
+       {          /* user wants only class methods or only instance methods */
+         plusminus = *regexp++;
+         while (*regexp == ' ' || *regexp == '\t')
+           regexp++;
+       }
+      if (*regexp == '\0')
+       strcpy(myregexp, ".*]");
+      else
+       {
+         strcpy(myregexp, regexp);
+         if (myregexp[strlen(myregexp) - 1] == '$') /* end of selector */
+           myregexp[strlen(myregexp) - 1] = ']';    /* end of method name */
+         else
+           strcat(myregexp, ".*]");
+       }
+    }
+
+  if (regexp != NULL)
+    if (0 != (val = re_comp (myregexp)))
+      error ("Invalid regexp (%s): %s", val, regexp);
+
+  /* first time thru is JUST to get max length and count */
+  ALL_MSYMBOLS (objfile, msymbol)
+    {
+      QUIT;
+      if ((name = SYMBOL_DEMANGLED_NAME (msymbol)) != NULL &&
+         (name[0] == '-' || name[0] == '+') &&
+         name[1] == '[')               /* got a method name */
+       {
+         if (plusminus && name[0] != plusminus)
+           continue;                   /* filter for class/instance methods */
+         name = (char *) strchr(name+2, ' ');  /* find selector part */
+         if (regexp == NULL || re_exec(++name) != 0)
+           { 
+             char *mystart = name;
+             char *myend   = (char *) strchr(mystart, ']');
+             
+             if (myend && (myend - mystart > maxlen))
+               maxlen = myend - mystart;       /* get longest selector */
+             matches++;
+           }
+       }
+    }
+  if (matches)
+    {
+      printf_filtered ("Selectors matching \"%s\":\n\n", 
+                      regexp ? regexp : "*");
+
+      sym_arr = alloca (matches * sizeof (struct symbol *));
+      matches = 0;
+      ALL_MSYMBOLS (objfile, msymbol)
+       {
+         QUIT;
+         if ((name = SYMBOL_DEMANGLED_NAME (msymbol)) != NULL &&
+             (name[0] == '-' || name[0] == '+') &&
+             name[1] == '[')           /* got a method name */
+           {
+             if (plusminus && name[0] != plusminus)
+               continue;               /* filter for class/instance methods */
+             name = (char *) strchr(name+2, ' ');     /* find selector part */
+             if (regexp == NULL || re_exec(++name) != 0)
+               sym_arr[matches++] = (struct symbol *) msymbol;
+           }
+       }
+
+      qsort (sym_arr, matches, sizeof (struct minimal_symbol *), 
+            compare_selectors);
+      asel[0] = 0;             /* to prevent compare on first iteration */
+      for (ix = 0; ix < matches; ix++) /* now do the output */
+       {
+         char *p = asel;
+
+         QUIT;
+         name = strchr (SYMBOL_DEMANGLED_NAME (sym_arr[ix]), ' ') + 1;
+         if (p[0] && specialcmp(name, p) == 0)
+           continue;           /* seen this one already (not unique) */
+
+         while (*name && *name != ']')         /* copy selector part */
+           *p++ = *name++;
+         *p++ = '\0';
+         puts_filtered_tabular(asel, maxlen + 1, 0);   /* print in columns */
+       }
+      begin_line();
+    }
+  else
+    printf_filtered ("No selectors matching \"%s\"\n", regexp ? regexp : "*");
+}
+
+/*
+ * Function: compare_classes (void *, void *)
+ *
+ * Comparison function for use with qsort.  Arguments are symbols or msymbols
+ * Compares class part of objc method name alphabetically.
+ */
+
+static int
+compare_classes (a, b)
+     void *a;
+     void *b;
+{
+  char *aname, *bname;
+
+  if ((aname = SYMBOL_SOURCE_NAME (*(struct symbol **) a)) == NULL ||
+      (bname = SYMBOL_SOURCE_NAME (*(struct symbol **) b)) == NULL)
+    error ("internal: compare_classes(1)");
+
+  return specialcmp (aname+1, bname+1);
+}
+
+/*
+ * Function: classes_info(regexp, from_tty)
+ *
+ * Implements the "info classes" command for objective c classes.
+ * Lists all objective c classes that match the optional regexp.
+ * Works by grepping thru the list of objective c methods.
+ * List will be sorted and uniqued (since one class may have many methods).
+ * BUGS: will not list a class that has no methods.
+ */
+
+static void
+classes_info (regexp, from_tty)
+     char *regexp;
+     int   from_tty;
+{
+  struct objfile       *objfile;
+  struct minimal_symbol *msymbol;
+  char                  *name;
+  char                  *val;
+  int                    matches = 0;
+  int                    maxlen  = 0;
+  int                    ix;
+  char                   myregexp[2048];
+  char                   aclass[256];
+  struct symbol        **sym_arr;
+
+  if (regexp == NULL)
+    strcpy(myregexp, ".* ");   /* null input: match all objc classes */
+  else
+    {
+      strcpy(myregexp, regexp);
+      if (myregexp[strlen(myregexp) - 1] == '$')
+       /* in the method name, the end of the class name is marked by ' ' */
+       myregexp[strlen(myregexp) - 1] = ' ';
+      else
+       strcat(myregexp, ".* ");
+    }
+
+  if (regexp != NULL)
+    if (0 != (val = re_comp (myregexp)))
+      error ("Invalid regexp (%s): %s", val, regexp);
+
+  /* first time thru is JUST to get max length and count */
+  ALL_MSYMBOLS (objfile, msymbol)
+    {
+      QUIT;
+      if ((name = SYMBOL_DEMANGLED_NAME (msymbol)) != NULL &&
+         (name[0] == '-' || name[0] == '+') &&
+         name[1] == '[')                               /* got a method name */
+       if (regexp == NULL || re_exec(name+2) != 0)
+         { 
+           char *mystart = name + 2;   /* compute length of classname part */
+           char *myend   = (char *) strchr(mystart, ' ');
+           
+           if (myend && (myend - mystart > maxlen))
+             maxlen = myend - mystart;
+           matches++;
+         }
+    }
+  if (matches)
+    {
+      printf_filtered ("Classes matching \"%s\":\n\n", 
+                      regexp ? regexp : "*");
+      sym_arr = alloca (matches * sizeof (struct symbol *));
+      matches = 0;
+      ALL_MSYMBOLS (objfile, msymbol)
+       {
+         QUIT;
+         if ((name = SYMBOL_DEMANGLED_NAME (msymbol)) != NULL &&
+             (name[0] == '-' || name[0] == '+') &&
+             name[1] == '[')                           /* got a method name */
+           if (regexp == NULL || re_exec(name+2) != 0)
+               sym_arr[matches++] = (struct symbol *) msymbol;
+       }
+
+      qsort (sym_arr, matches, sizeof (struct minimal_symbol *), 
+            compare_classes);
+      aclass[0] = 0;           /* to prevent compare on first iteration */
+      for (ix = 0; ix < matches; ix++) /* now do the output */
+       {
+         char *p = aclass;
+
+         QUIT;
+         name = SYMBOL_DEMANGLED_NAME (sym_arr[ix]) + 2;
+         if (p[0] && specialcmp(name, p) == 0)
+           continue;   /* seen this one already (not unique) */
+
+         while (*name && *name != ' ') /* copy class part of method name */
+           *p++ = *name++;
+         *p++ = '\0';
+         puts_filtered_tabular(aclass, maxlen + 1, 0); /* print in columns */
+       }
+      begin_line();
+    }
+  else
+    printf_filtered ("No classes matching \"%s\"\n", regexp ? regexp : "*");
+}
+
+/*
+ * Function: total_number_of_imps (char *selector);
+ *
+ * Input:  a string representing a selector 
+ * Output: number of methods that implement that selector.
+ *
+ * By analogy with function "total_number_of_methods", this allows 
+ * decode_line_1 (symtab.c) to detect if there are objective c methods
+ * matching the input, and to allocate an array of pointers to them 
+ * which can be manipulated by "decode_line_2" (also in symtab.c)
+ */
+
+int
+total_number_of_imps (selector, symtab)
+     char *selector;
+     struct symtab *symtab;
+{
+  struct objfile       *objfile;
+  struct minimal_symbol *msymbol;
+  char                  *name;
+  char                  *val;
+  int                    matches = 0;
+  char                   myregexp[2048];
+  char                   plusminus = 0;
+  struct block          *block = 0;
+
+  if (selector == NULL)                /* null input, match all selectors */
+    strcpy(myregexp, " .*]");  /* (this should never really happen) */
+  else
+    {
+      if (*selector == '-' || *selector == '+')
+       {
+         plusminus = *selector++;      /* user selects class/inst methods */
+         while (*selector == ' ' || *selector == '\t')
+           selector++;                 /* strip spaces/tabs */
+       }
+      sprintf(myregexp, " %s]", selector);
+    }
+
+  if (0 != (val = re_comp (myregexp)))
+    error ("Invalid selector (%s): %s", val, selector);
+
+  if (symtab)
+    block = BLOCKVECTOR_BLOCK (BLOCKVECTOR (symtab), STATIC_BLOCK);
+
+  ALL_MSYMBOLS (objfile, msymbol)
+    {
+      QUIT;
+      if (symtab &&            /* was a symtab (source file) specified? */
+        (SYMBOL_VALUE_ADDRESS (msymbol) <  block->startaddr ||
+         SYMBOL_VALUE_ADDRESS (msymbol) >= block->endaddr))
+       continue;               /* not in the specified symtab */
+
+      if ((name = SYMBOL_DEMANGLED_NAME (msymbol)) != NULL &&
+         (name[0] == '-' || name[0] == '+') &&
+         name[1] == '[')       /* got a method name */
+      {
+       if (plusminus && name[0] != plusminus)
+         continue;             /* filter for class/instance methods */
+       else if (re_exec (name+2) != 0)
+         matches++;
+      }
+    }
+  return matches;
+}
+
+/* 
+ * Function: find_imps (char *selector, struct symbol **sym_arr)
+ *
+ * Input:  a string representing a selector
+ *         a pointer to an array of symbol pointers
+ *         possibly a pointer to a symbol found by the caller.
+ *
+ * Output: number of methods that implement that selector.
+ * Side effects: The array of symbol pointers is filled with matching syms.
+ *
+ * By analogy with function "find_methods" (symtab.c), builds a list of
+ * symbols matching the ambiguous input, so that "decode_line_2" (symtab.c)
+ * can list them and ask the user to choose one or more.  In this case the
+ * matches are objective c methods ("implementations") matching an objective
+ * c selector.
+ *
+ * Note that it is possible for a normal (c-style) function to have the
+ * same name as an objective c selector.  To prevent the selector from
+ * eclipsing the function, we allow the caller (decode_line_1) to search
+ * for such a function first, and if it finds one, pass it in to us.  We
+ * will then integrate it into the list.  We also search for one here, 
+ * among the minsyms.
+ *
+ * NOTE: if NUM_DEBUGGABLE is non-zero, the sym_arr will be 
+ *       divided into two parts: debuggable (struct symbol) syms, 
+ *       and non_debuggable (struct minimal_symbol) syms.  
+ *       The debuggable ones will come first, before NUM_DEBUGGABLE
+ *       (which will thus be the index of the first non-debuggable one).
+ */
+
+int 
+find_imps (selector, sym_arr, num_debuggable, fn_match, symtab)
+     char           *selector;         /* the string to match */
+     struct symbol **sym_arr;          /* where to put matches */
+     int            *num_debuggable;   /* -g (source) matches */
+     struct symbol  *fn_match;         /* function with the same name */
+     struct symtab  *symtab;
+{
+  struct objfile       *objfile;
+  struct minimal_symbol *msymbol;
+  struct symbol         *sym;
+  char                  *name;
+  char                  *val;
+  int                    matches = 0;
+  int                    maxlen  = 0;
+  char                   myregexp[2048];
+  char                   animp[256];
+  int                    plusminus = 0;
+  struct block          *block = 0;
+
+  if (selector == NULL)
+    strcpy(myregexp, " .*]");
+  else
+    {
+      if (*selector == '-' || *selector == '+')
+       {
+         plusminus = *selector++;      /* user selects class/inst methods */
+         while (*selector == ' ' || *selector == '\t')
+           selector++;
+       }
+      sprintf(myregexp, " %s]", selector);
+    }
+
+  if (0 != (val = re_comp (myregexp)))
+    error ("Invalid selector (%s): %s", val, selector);
+
+  if (num_debuggable)     *num_debuggable = 0;
+
+  if (symtab)
+    block = BLOCKVECTOR_BLOCK (BLOCKVECTOR (symtab), STATIC_BLOCK);
+
+  if (fn_match)
+    { /* match found by caller -- assume this one is debuggable (i.e. "-g") */
+      sym_arr[matches++] = fn_match;
+      if (num_debuggable) (*num_debuggable)++;
+    }
+
+  ALL_MSYMBOLS (objfile, msymbol)
+    {
+      QUIT;
+      if (symtab &&            /* was a symtab (source file) specified? */
+        (SYMBOL_VALUE_ADDRESS (msymbol) <  block->startaddr ||
+         SYMBOL_VALUE_ADDRESS (msymbol) >= block->endaddr))
+       continue;               /* not in the specified symtab */
+
+      if ((name = SYMBOL_DEMANGLED_NAME (msymbol)) != NULL)
+      {
+       if ((name[0] == '-' || name[0] == '+') && name[1] == '[')
+         {                             /* found a method */
+           if (plusminus && name[0] != plusminus)
+             continue;                 /* filter for class/instance methods */
+           if (re_exec (name+2) != 0)  /* matching method */
+           {
+             if (num_debuggable &&
+                 (sym = find_pc_function (SYMBOL_VALUE_ADDRESS (msymbol))))
+               {       /* found a high-level method sym: swap it into the 
+                          lower part of sym_arr (below num_debuggable) */
+                 sym_arr[matches++] = sym_arr[*num_debuggable];
+                 sym_arr[(*num_debuggable)++] = sym;
+               }
+             else      /* found a non-debuggable method symbol */
+               sym_arr[matches++] = (struct symbol *) msymbol;
+           }
+         }
+       else if (!fn_match && strcmp(SYMBOL_NAME (msymbol), selector) == 0)
+         { /* found a matching function (we only want one) */
+           sym_arr[matches++] = fn_match = (struct symbol *) msymbol;
+         }
+      }
+    }
+
+  if (matches > 1)
+    {
+      if (num_debuggable)      /* sort two separate sections of sym_arr */
+       {
+         if (*num_debuggable > 1)              /* sort debuggable symbols */
+           qsort (sym_arr, *num_debuggable, 
+                  sizeof (struct minimal_symbol *), compare_classes);
+
+         if (matches - *num_debuggable > 1)    /* sort minimal_symbols */
+           qsort (&sym_arr[*num_debuggable], matches - *num_debuggable, 
+                  sizeof (struct minimal_symbol *), compare_classes);
+       }
+      else
+       qsort (sym_arr, matches,                /* sort everybody */
+              sizeof (struct minimal_symbol *), compare_classes);
+    }
+
+  sym_arr[matches] = 0;                /* terminate the sym_arr list */
+  return  matches;
+}
+
+void
+_initialize_objc_language ()
+{
+  add_language (&objc_language_defn);
+  add_info ("selectors", selectors_info,       /* INFO SELECTORS command */
+           "All Objective C selectors, or those matching REGEXP.");
+  add_info ("classes", classes_info,           /* INFO CLASSES   command */
+           "All Objective C classes, or those matching REGEXP.");
+}
diff -Naur gdb-5.0.cvs20011007/gdb/objc-lang.h 
gdb-5.0.cvs20011007.new/gdb/objc-lang.h
--- gdb-5.0.cvs20011007/gdb/objc-lang.h Wed Dec 31 19:00:00 1969
+++ gdb-5.0.cvs20011007.new/gdb/objc-lang.h     Sat Nov 24 22:51:54 2001
@@ -0,0 +1,46 @@
+/* Objective C language support definitions for GDB, the GNU debugger.
+   Copyright 1992 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+
+#ifdef __STDC__                /* Forward decls for prototypes */
+struct value;
+#endif
+
+extern int
+objc_parse PARAMS ((void));    /* Defined in c-exp.y */
+
+extern void
+objc_error PARAMS ((char *));  /* Defined in c-exp.y */
+
+extern void                    /* Defined in c-typeprint.c */
+c_print_type PARAMS ((struct type *, char *, struct ui_file *, int, int));
+
+extern int                     /* Defined in c-lang.c */
+c_val_print PARAMS ((struct type *, char *, int, CORE_ADDR, struct ui_file *, 
int, int,
+                    int, enum val_prettyprint));
+
+extern int                     /* Defined in c-lang.c */
+c_value_print PARAMS ((struct value *, struct ui_file *, int, 
+                      enum val_prettyprint));
+
+extern void                    /* Defined in c-lang.c */
+c_emit_char PARAMS ((int c, struct ui_file *stream, int quoter));
+
+extern CORE_ADDR lookup_objc_class     PARAMS ((char *classname));
+extern int       lookup_child_selector PARAMS ((char *methodname));
+
diff -Naur gdb-5.0.cvs20011007/gdb/parse.c gdb-5.0.cvs20011007.new/gdb/parse.c
--- gdb-5.0.cvs20011007/gdb/parse.c     Tue Mar 27 15:36:24 2001
+++ gdb-5.0.cvs20011007.new/gdb/parse.c Sat Nov 24 22:51:54 2001
@@ -877,6 +877,11 @@
       args = 1 + longest_to_int (expr->elts[endpos - 2].longconst);
       break;
 
+    case OP_MSGCALL:   /* Objective C message (method) call */
+      oplen = 4;
+      args = 1 + longest_to_int (expr->elts[endpos - 2].longconst);
+      break;
+
     case UNOP_MAX:
     case UNOP_MIN:
       oplen = 3;
@@ -908,6 +913,8 @@
       /* fall through */
     case OP_M2_STRING:
     case OP_STRING:
+    case OP_NSSTRING:  /* Objective C Foundation Class NSString constant */
+    case OP_SELECTOR:  /* Objective C "@selector" pseudo-op */
     case OP_NAME:
     case OP_EXPRSTRING:
       oplen = longest_to_int (expr->elts[endpos - 2].longconst);
@@ -1014,6 +1021,11 @@
       args = 1 + longest_to_int (inexpr->elts[inend - 2].longconst);
       break;
 
+    case OP_MSGCALL:   /* Objective C message (method) call */
+      oplen = 4;
+      args = 1 + longest_to_int (inexpr->elts[inend - 2].longconst);
+      break;
+
     case UNOP_MIN:
     case UNOP_MAX:
       oplen = 3;
@@ -1044,6 +1056,8 @@
       /* fall through */
     case OP_M2_STRING:
     case OP_STRING:
+    case OP_NSSTRING:  /* Objective C Foundation Class NSString constant */
+    case OP_SELECTOR:  /* Objective C "@selector" pseudo-op */
     case OP_NAME:
     case OP_EXPRSTRING:
       oplen = longest_to_int (inexpr->elts[inend - 2].longconst);
diff -Naur gdb-5.0.cvs20011007/gdb/parser-defs.h 
gdb-5.0.cvs20011007.new/gdb/parser-defs.h
--- gdb-5.0.cvs20011007/gdb/parser-defs.h       Tue Mar  6 03:21:11 2001
+++ gdb-5.0.cvs20011007.new/gdb/parser-defs.h   Sat Nov 24 22:51:54 2001
@@ -196,6 +196,11 @@
     int right_assoc;
   };
 
+/* for parsing Objective C */
+extern void start_msglist();
+extern void add_msglist PARAMS((struct stoken *str, int addcolon));
+extern int end_msglist();
+
 /* The generic method for targets to specify how their registers are
    named.  The mapping can be derived from three sources:
    REGISTER_NAME; std_regs; or a target specific alias hook. */
diff -Naur gdb-5.0.cvs20011007/gdb/printcmd.c 
gdb-5.0.cvs20011007.new/gdb/printcmd.c
--- gdb-5.0.cvs20011007/gdb/printcmd.c  Sun Oct  7 12:15:40 2001
+++ gdb-5.0.cvs20011007.new/gdb/printcmd.c      Sat Nov 24 22:51:54 2001
@@ -1121,7 +1121,11 @@
          printf_filtered ("Symbol \"");
          fprintf_symbol_filtered (gdb_stdout, exp,
                                   current_language->la_language, DMGL_ANSI);
-         printf_filtered ("\" is a field of the local class variable 
`this'\n");
+         printf_filtered ("\" is a field of the local class variable ");
+         if (current_language->la_language == language_objc)
+           printf_filtered ("'self'\n");     /* ObjC equivalent of "this" */
+         else
+           printf_filtered ("'this'\n");
          return;
        }
 
diff -Naur gdb-5.0.cvs20011007/gdb/source.c gdb-5.0.cvs20011007.new/gdb/source.c
--- gdb-5.0.cvs20011007/gdb/source.c    Wed Jul 18 13:25:13 2001
+++ gdb-5.0.cvs20011007.new/gdb/source.c        Sat Nov 24 22:51:54 2001
@@ -1140,8 +1140,9 @@
 
 /* Print a list of files and line numbers which a user may choose from
    in order to list a function which was specified ambiguously (as with
-   `list classname::overloadedfuncname', for example).  The vector in
-   SALS provides the filenames and line numbers.  */
+   `list classname::overloadedfuncname', or 'list objectiveCSelector:).
+   The vector in SALS provides the filenames and line numbers.
+   NOTE: some of the SALS may have no filename or line information! */
 
 static void
 ambiguous_line_spec (struct symtabs_and_lines *sals)
@@ -1149,8 +1150,11 @@
   int i;
 
   for (i = 0; i < sals->nelts; ++i)
-    printf_filtered ("file: \"%s\", line number: %d\n",
-                    sals->sals[i].symtab->filename, sals->sals[i].line);
+      if (sals->sals[i].symtab != 0)
+         printf_filtered("file: \"%s\", line number: %d\n",
+                         sals->sals[i].symtab->filename, sals->sals[i].line);
+      else
+         printf_filtered("No file and line information.\n");
 }
 
 static void
diff -Naur gdb-5.0.cvs20011007/gdb/symfile.c 
gdb-5.0.cvs20011007.new/gdb/symfile.c
--- gdb-5.0.cvs20011007/gdb/symfile.c   Wed Jul 18 13:25:13 2001
+++ gdb-5.0.cvs20011007.new/gdb/symfile.c       Sat Nov 24 22:51:54 2001
@@ -1879,12 +1879,14 @@
 {
   if (fl_table_size == 0)      /* protect against repetition */
     {
-      fl_table_size = 20;
+      fl_table_size = 22;
       fl_table_next = 0;
       filename_language_table =
        xmalloc (fl_table_size * sizeof (*filename_language_table));
       add_filename_language (".c", language_c);
       add_filename_language (".C", language_cplus);
+      add_filename_language (".m", language_objc);
+      add_filename_language (".M", language_objc);
       add_filename_language (".cc", language_cplus);
       add_filename_language (".cp", language_cplus);
       add_filename_language (".cpp", language_cplus);
diff -Naur gdb-5.0.cvs20011007/gdb/symtab.c gdb-5.0.cvs20011007.new/gdb/symtab.c
--- gdb-5.0.cvs20011007/gdb/symtab.c    Tue Jul 10 16:31:13 2001
+++ gdb-5.0.cvs20011007.new/gdb/symtab.c        Sat Nov 24 22:51:54 2001
@@ -669,8 +669,8 @@
     }
 
 
-  /* C++: If requested to do so by the caller, 
-     check to see if NAME is a field of `this'. */
+  /* C++ || ObjC: If requested to do so by the caller,
+     check to see if NAME is a field of `this' || 'self'. */
   if (is_a_field_of_this)
     {
       struct value *v = value_of_this (0);
@@ -985,7 +985,9 @@
          if (!(center < top))
            internal_error (__FILE__, __LINE__, "failed internal consistency 
check");
          if (!do_linear_search
-             && (SYMBOL_LANGUAGE (*center) == language_java))
+             && (SYMBOL_LANGUAGE (*center) == language_java ||
+                 SYMBOL_LANGUAGE (*center) == language_objc
+                ))
            {
              do_linear_search = 1;
            }
@@ -1174,8 +1176,8 @@
    a match we'll never find, since it will go pretty quick.  Once the
    binary search terminates, we drop through and do a straight linear
    search on the symbols.  Each symbol which is marked as being a C++
-   symbol (language_cplus set) has both the encoded and non-encoded names
-   tested for a match. */
+   symbol (language_cplus set) or an Objective C symbol (language_objc)
+   has both the encoded and non-encoded names tested for a match. */
 
 struct symbol *
 lookup_block_symbol (register const struct block *block, const char *name,
@@ -1210,7 +1212,10 @@
            }
          inc = (inc >> 1) + bot;
          sym = BLOCK_SYM (block, inc);
-         if (!do_linear_search && (SYMBOL_LANGUAGE (sym) == language_java))
+         if (!do_linear_search
+             && (SYMBOL_LANGUAGE (sym) == language_java ||
+                 SYMBOL_LANGUAGE (sym) == language_objc
+                ))
            {
              do_linear_search = 1;
            }
diff -Naur gdb-5.0.cvs20011007/gdb/symtab.h gdb-5.0.cvs20011007.new/gdb/symtab.h
--- gdb-5.0.cvs20011007/gdb/symtab.h    Tue Jul 10 16:31:13 2001
+++ gdb-5.0.cvs20011007.new/gdb/symtab.h        Sat Nov 24 22:51:54 2001
@@ -30,6 +30,8 @@
 #define obstack_chunk_free xfree
 #include "bcache.h"
 
+extern char *objc_demangle (const char *);
+
 /* Don't do this; it means that if some .o's are compiled with GNU C
    and some are not (easy to do accidentally the way we configure
    things; also it is a pain to have to "make clean" every time you
@@ -98,6 +100,11 @@
            char *demangled_name;
          }
        chill_specific;
+       struct objc_specific    /* For Objective C */
+       {
+         char *demangled_name;
+       }
+       objc_specific;
       }
     language_specific;
 
@@ -136,6 +143,12 @@
 #define SYMBOL_CPLUS_DEMANGLED_NAME(symbol)    \
   (symbol)->ginfo.language_specific.cplus_specific.demangled_name
 
+#define SYMBOL_CHILL_DEMANGLED_NAME(symbol)    \
+  (symbol)->ginfo.language_specific.chill_specific.demangled_name
+
+#define SYMBOL_OBJC_DEMANGLED_NAME(symbol)     \
+  (symbol)->ginfo.language_specific.objc_specific.demangled_name
+
 /* Macro that initializes the language dependent portion of a symbol
    depending upon the language for the symbol. */
 
@@ -152,6 +165,10 @@
       {                                                                        
\
        SYMBOL_CHILL_DEMANGLED_NAME (symbol) = NULL;                    \
       }                                                                        
\
+    else if (SYMBOL_LANGUAGE (symbol) == language_objc)                        
\
+      {                                                                        
\
+       SYMBOL_OBJC_DEMANGLED_NAME (symbol) = NULL;                     \
+      }                                                                        
\
     else                                                               \
       {                                                                        
\
        memset (&(symbol)->ginfo.language_specific, 0,                  \
@@ -208,8 +225,8 @@
          }                                                             \
       }                                                                        
\
     if (demangled == NULL                                              \
-       && (SYMBOL_LANGUAGE (symbol) == language_chill                  \
-           || SYMBOL_LANGUAGE (symbol) == language_auto))              \
+       && (SYMBOL_LANGUAGE (symbol) == language_chill  ||              \
+           SYMBOL_LANGUAGE (symbol) == language_auto))                 \
       {                                                                        
\
        demangled =                                                     \
          chill_demangle (SYMBOL_NAME (symbol));                        \
@@ -225,6 +242,41 @@
            SYMBOL_CHILL_DEMANGLED_NAME (symbol) = NULL;                \
          }                                                             \
       }                                                                        
\
+    if (demangled == NULL &&                                           \
+       (SYMBOL_LANGUAGE (symbol) == language_objc ||                   \
+        SYMBOL_LANGUAGE (symbol) == language_auto))                    \
+      {                                                                        
\
+       demangled =                                                     \
+         objc_demangle (SYMBOL_NAME (symbol));                         \
+       if (demangled != NULL)                                          \
+         {                                                             \
+           SYMBOL_LANGUAGE (symbol) = language_objc;                   \
+           SYMBOL_OBJC_DEMANGLED_NAME (symbol) =                       \
+             obsavestring (demangled, strlen (demangled), (obstack));  \
+           xfree (demangled);                                          \
+         }                                                             \
+       else                                                            \
+         {                                                             \
+           SYMBOL_OBJC_DEMANGLED_NAME (symbol) = NULL;                 \
+         }                                                             \
+      }                                                                        
\
+    if (demangled &&                                                   \
+       SYMBOL_LANGUAGE (symbol) != language_objc &&                    \
+       (demangled = SYMBOL_NAME (symbol)) &&                           \
+       demangled[0] == '_' &&                                          \
+       (demangled[1] == 'i' || demangled[1] == 'c') &&                 \
+       demangled[2] == '_')                                            \
+      {        /* some other demangling succeeded, yet it looks like ObjC   */ 
\
+       demangled =                                                     \
+         objc_demangle (SYMBOL_NAME (symbol));                         \
+       if (demangled != NULL)  /*  yes, it was ObjC: let ObjC win   */ \
+         {                     /* (C++ demangling is too forgiving) */ \
+           SYMBOL_LANGUAGE (symbol) = language_objc;                   \
+           SYMBOL_OBJC_DEMANGLED_NAME (symbol) =                       \
+             obsavestring (demangled, strlen (demangled), (obstack));  \
+           xfree (demangled);                                          \
+         }                                                             \
+      }                                                                        
\
   } while (0)
 
 /* Macro that returns the demangled name for a symbol based on the language
@@ -236,7 +288,9 @@
    ? SYMBOL_CPLUS_DEMANGLED_NAME (symbol)                              \
    : (SYMBOL_LANGUAGE (symbol) == language_chill                       \
       ? SYMBOL_CHILL_DEMANGLED_NAME (symbol)                           \
-      : NULL))
+      : (SYMBOL_LANGUAGE (symbol) == language_objc                     \
+        ? SYMBOL_OBJC_DEMANGLED_NAME (symbol)                          \
+         : NULL)))
 
 #define SYMBOL_CHILL_DEMANGLED_NAME(symbol)                            \
   (symbol)->ginfo.language_specific.chill_specific.demangled_name
diff -Naur gdb-5.0.cvs20011007/gdb/utils.c gdb-5.0.cvs20011007.new/gdb/utils.c
--- gdb-5.0.cvs20011007/gdb/utils.c     Wed Jul 25 22:08:49 2001
+++ gdb-5.0.cvs20011007.new/gdb/utils.c Sat Nov 24 22:51:54 2001
@@ -1714,6 +1714,45 @@
     }
 }
 
+/* Print input string to gdb_stdout, filtered, with wrap, 
+   arranging strings in columns of n chars. String can be
+   right or left justified in the column.  Never prints 
+   trailing spaces.  String should never be longer than
+   width.  FIXME: this could be useful for the EXAMINE 
+   command, which currently doesn't tabulate very well */
+
+void
+puts_filtered_tabular (string, width, right)
+     char *string;     /* stuff to print */
+     int width;                /* width of tabular columns */
+     int right;                /* right/left justify */
+{
+  int spaces = 0;
+  int stringlen;
+  char *spacebuf;
+
+  if (((chars_printed - 1) / width + 2) * width >= chars_per_line)
+    fputs_filtered ("\n", gdb_stdout);
+
+  if (width >= chars_per_line)
+    width = chars_per_line - 1;
+
+  stringlen = strlen(string);
+
+  if (chars_printed > 0)
+    spaces = width - (chars_printed - 1) % width - 1;
+  if (right)
+    spaces += width - stringlen;
+
+  spacebuf = alloca(spaces + 1);
+  spacebuf[spaces] = '\0';
+  while (spaces--)
+    spacebuf[spaces] = ' ';
+
+  fputs_filtered (spacebuf, gdb_stdout);
+  fputs_filtered (string,   gdb_stdout);
+}
+
 /* Ensure that whatever gets printed next, using the filtered output
    commands, starts at the beginning of the line.  I.E. if there is
    any pending output for the current line, flush it and start a new
@@ -2174,6 +2213,9 @@
            case language_chill:
              demangled = chill_demangle (name);
              break;
+           case language_objc:
+             demangled = objc_demangle (name);
+             break;
            default:
              demangled = NULL;
              break;
diff -Naur gdb-5.0.cvs20011007/gdb/valops.c gdb-5.0.cvs20011007.new/gdb/valops.c
--- gdb-5.0.cvs20011007/gdb/valops.c    Wed Jul 11 14:29:47 2001
+++ gdb-5.0.cvs20011007.new/gdb/valops.c        Sat Nov 24 22:51:54 2001
@@ -112,7 +112,10 @@
        {
          struct type *type;
          CORE_ADDR maddr;
-         type = lookup_pointer_type (builtin_type_char);
+         /* Invent a type, function returning void*, for this minsym.
+            A void* return value can be cast to anything.  A char* return
+            type is dangerous, because gdb will attempt to print a string */
+         type = lookup_pointer_type (builtin_type_void);
          type = lookup_function_type (type);
          type = lookup_pointer_type (type);
          maddr = SYMBOL_VALUE_ADDRESS (msymbol);
@@ -208,8 +211,9 @@
        }
     }
 
-  if (current_language->c_style_arrays
-      && TYPE_CODE (type2) == TYPE_CODE_ARRAY)
+  if (current_language->c_style_arrays &&
+     (TYPE_CODE (type2) == TYPE_CODE_ARRAY ||
+      TYPE_CODE (type2) == TYPE_CODE_STRING))  /* a string is an array */
     arg2 = value_coerce_array (arg2);
 
   if (TYPE_CODE (type2) == TYPE_CODE_FUNC)
@@ -3158,6 +3162,13 @@
 
 
 
+/* actually, I would think this would be a valuable function in general */
+static value_ptr value_of_local PARAMS ((char *name, int complain));
+
+/* Objective C: return the value of the class instance variable, if 
+   one exists.  Flag COMPLAIN signals an error if the request is 
+   made in an inappropriate context.  */
+
 /* C++: return the value of the class instance variable, if one exists.
    Flag COMPLAIN signals an error if the request is made in an
    inappropriate context.  */
@@ -3165,11 +3176,22 @@
 value_ptr
 value_of_this (int complain)
 {
+  if (current_language->la_language == language_objc)
+    return value_of_local("self", complain);
+  else
+    return value_of_local("this", complain);
+}
+
+static value_ptr
+value_of_local(name, complain)
+     char *name;
+     int   complain;
+{
   struct symbol *func, *sym;
   struct block *b;
   int i;
   static const char funny_this[] = "this";
-  value_ptr this;
+  value_ptr ret;
 
   if (selected_frame == 0)
     {
@@ -3183,7 +3205,7 @@
   if (!func)
     {
       if (complain)
-       error ("no `this' in nameless context");
+       error ("no %s in nameless context", name);
       else
        return 0;
     }
@@ -3193,14 +3215,14 @@
   if (i <= 0)
     {
       if (complain)
-       error ("no args, no `this'");
+         error ("no args, no %s", name);
       else
        return 0;
     }
 
   /* Calling lookup_block_symbol is necessary to get the LOC_REGISTER
      symbol instead of the LOC_ARG one (if both exist).  */
-  sym = lookup_block_symbol (b, funny_this, VAR_NAMESPACE);
+  sym = lookup_block_symbol (b, name, VAR_NAMESPACE);
   if (sym == NULL)
     {
       if (complain)
@@ -3209,10 +3231,10 @@
        return NULL;
     }
 
-  this = read_var_value (sym, selected_frame);
-  if (this == 0 && complain)
-    error ("`this' argument at unknown address");
-  return this;
+  ret = read_var_value (sym, selected_frame);
+  if (ret == 0 && complain)
+    error ("%s argument at unknown address", name);
+  return ret;
 }
 
 /* Create a slice (sub-string, sub-array) of ARRAY, that is LENGTH elements

reply via email to

[Prev in Thread] Current Thread [Next in Thread]