#import "gdb.h"
#import "objc.h"
#import "objfiles.h"
#import "symfile.h"

#import <mach-o/ldsyms.h>
#import <stdio.h>
#import <stdlib.h>

#import <mach-o/loader.h>

#import <objc/hashtable.h>
#import <objc/maptable.h>
#import <objc/Protocol.h>

int forceRegionManager = 0;

#define currentManager (inferior_task ? (RelocManager *)regionManager : (RelocManager *)manager)
#define WARPG(p) \
((typeof(p))[currentManager pointerFor: (p) withSize: sizeof(*(p))])

extern RegionManager *regionManager;

typedef struct _ClassImp {
  char *className;
  IMP function;
  BOOL isFactory;
  struct _ClassImp *next;
} ClassImp;

static NXHashTable *selectorHash;
static NXMapTable *classMap;

typedef enum _OnlyOne {ANYTHING, INSTANCEONLY, CLASSONLY} OnlyOne;

static inline BOOL streq(str1, str2)
     char *str1, *str2;
{
  return ((*str1 == *str2) && (strcmp(str1, str2) == 0));
}

#include <sys/time.h>
#include <sys/resource.h>
#include <time.h>

/* Return time used so far, in microseconds.  */

int gettime ()
{
  struct rusage rusage;

  getrusage (0, &rusage);
  return (rusage.ru_utime.tv_sec * 1000000 + rusage.ru_utime.tv_usec
	  + rusage.ru_stime.tv_sec * 1000000 + rusage.ru_stime.tv_usec);
}

void print_time (str, total, tv)
     char *str;
     int total;
     struct timeval *tv;
{
  extern char **NXArgv;

  fprintf (stderr,
	   "time to `%s' for `%s':\n\t%7d.%06d (sys+usr)"
	   "\t%7d.%01d (real).\n", str, NXArgv[0],
	   total / 1000000, total % 1000000, 
	   tv->tv_sec, tv->tv_usec/100000);
}

static
ClassImp *findImp(manager, methodName, className, isFactory, methodLists, 
		  imps, goToEnd)
     SegmentManager *manager;
     char *methodName, *className;
     BOOL isFactory;
     struct objc_method_list **methodLists;
     ClassImp *imps;
     BOOL goToEnd;
{
  Method foundMethod = NULL, method;
  struct objc_method_list *methods; /* for use by goToEnd */
  int nMethods;
  ClassImp *imp;
  char *mName;

  if ( !methodLists || !*methodLists )
    return imps;

  methods = *methodLists;
  if (goToEnd) {
    while ( *methodLists != 0 && *methodLists != -1 ) {
      methods = *methodLists;
      methodLists++;
    }
  }
  methods = WARPG( methods );

  for (nMethods = methods->method_count, method = methods->method_list;
       !foundMethod && nMethods;
       nMethods--, method++) {
    mName = [currentManager pointerForString: (char *)method->method_name];
    if (mName && streq(mName, methodName))
      foundMethod = method;
  }
  if (foundMethod) {
    imp = xmalloc(sizeof(*imp));
    imp->className = className;
    imp->function = foundMethod->method_imp;
    imp->isFactory = isFactory;
    imp->next = imps;
    return imp;
  } else
    return imps;
}

#ifndef OBJC_NEXT_METHOD_LIST
#define CLS_METHOD_ARRAY 0x100
#endif

static struct objc_method_list **methodLists(manager, cls)
     SegmentManager *manager;
     Class cls;
{
  if ( cls->info & CLS_METHOD_ARRAY ) {
    return WARPG( cls->methodLists );  /* WARPG or WARP */
  }
  else {
    static struct objc_method_list* array[2];
    array[0] = cls->methodLists;
    array[1] = 0;
    return array;
  }
}

static ClassImp *
findClassImps(manager, methodName, className, onlyOne, pClass, nClasses, imps)
     SegmentManager *manager;
     char *methodName;
     char *className;
     OnlyOne onlyOne;
     Class *pClass;
     int nClasses;
     ClassImp *imps;
{
  Class class, metaClass;
  char *clsName;
  struct objc_method_list **methods;

  for (; nClasses; nClasses--, pClass++) {
    class = WARPG(*pClass);
    if (class) {
      clsName = [currentManager pointerForString: class->name];
      if (clsName) {
	if (!className || (strcmp(clsName, className) == 0)) {
	  if ((onlyOne != CLASSONLY)
	      && (methods = methodLists(manager, class)))
	    imps = findImp(manager, methodName, clsName, NO, methods,
			   imps, YES);
	  if ((onlyOne != INSTANCEONLY)
	      && (metaClass = WARPG(class->isa))
	      && (methods = methodLists(manager, metaClass)))
	    imps = findImp(manager, methodName, clsName, YES, methods,
			   imps, YES);
	  if (onlyOne && imps)
	    return imps;
	}
      }
    }
  }
  return imps;
}

static ClassImp *
findCatImps(manager, methodName, className, onlyOne, pCat, nCats, imps)
     SegmentManager *manager;
     char *methodName;
     char *className;
     OnlyOne onlyOne;
     Category *pCat;
     int nCats;
     ClassImp *imps;
{
  Category cat;
  char *clsName;
  struct objc_method_list *methods[2];
  methods[1] = 0;

  for (; nCats; nCats--, pCat++) {
    cat = WARPG(*pCat);
    if (cat) {
      clsName = [currentManager pointerForString: cat->class_name];
      if (clsName) {
	if (!className || (strcmp(clsName, className) == 0)) {
	  if ((onlyOne != CLASSONLY)
	      && (methods[0] = cat->instance_methods))
	    imps = findImp(manager, methodName, clsName, NO, methods,
			   imps, NO);
	  if ((onlyOne != INSTANCEONLY)
	      && (methods[0] = cat->class_methods))
	    imps = findImp(manager, methodName, clsName, NO, methods,
			   imps, NO);
	  if (onlyOne && imps)
	    return imps;
	}
      }
    }
  }
  return imps;
}

static ClassImp *
findImps(methodName, className, onlyOne)
     char *methodName, *className;
     OnlyOne onlyOne;
{
  ClassImp *imps = NULL;
  struct objfile *objfile;

  for (objfile = object_files; objfile; objfile = objfile->next) {
    SegmentManager *manager = objfile->manager;
    Module mod;
    int nMods;
    Symtab symtab;
    int size;

    if (mod = [manager getSectData: "__OBJC" section: "__module_info"
	     size: &size]) {
      for (nMods = size / sizeof(struct objc_module);
	   nMods;
	   nMods--, mod++) {
	if (symtab = WARPG(mod->symtab)) {
	  imps = findClassImps(manager, methodName,
			       className,
			       onlyOne,
			       (Class *)symtab->defs, 
			       symtab->cls_def_cnt,
			       imps);
	  if (onlyOne && imps)
	    return imps;
	  imps = findCatImps(manager, methodName, className, onlyOne,
			     (Category *)symtab->defs
			     + symtab->cls_def_cnt,
			     symtab->cat_def_cnt,
			     imps);
	  if (onlyOne && imps)
	    return imps;
	}
      }
    }
  }
  return imps;
}

Class
lookup_objc_class(className)
     char *className;
{
  if (classMap && className && *className)
    return NXMapGet(classMap, className);
}

BOOL
isClass(class, allowMeta)
     Class class;
     BOOL allowMeta;
{
  struct objfile *objfile;
  const struct section *section;
  static Class stringClass = NULL;
  Class myClass;  

  for (objfile = object_files; objfile; objfile = objfile->next) {
    SegmentManager *manager = objfile->manager;
    if (section = [manager getSeg: "__OBJC" sect: "__class"]) {
      if ((section->addr <= (unsigned)class) 
	  && ((unsigned)class < (section->addr + section->size)))
	return YES;
    }
    if (allowMeta
	&& (section = [manager getSeg: "__OBJC" sect: "__meta_class"])
	&& (section->addr <= (unsigned)class) 
	&& ((unsigned)class < (section->addr + section->size)))
      return YES;
  }
  if (myClass = WARP(class)) {
    Class metaClass = myClass->isa;
    for (objfile = object_files; objfile; objfile = objfile->next) {
      SegmentManager *manager = objfile->manager;
      if ((section = [manager getSeg: "__OBJC" sect: "__meta_class"])
	  && (section->addr <= (unsigned)metaClass) 
	  && ((unsigned)metaClass < (section->addr + section->size)))
	return YES;
    }
  }

  if (!stringClass) {
    struct minimal_symbol *minSym
      = lookup_minimal_symbol("_NXConstantString_class", NULL, NULL);
    stringClass = minSym ? 
      (Class) SYMBOL_VALUE_ADDRESS(minSym) : (Class) 0xFFFFFFFF;
  }
  if (stringClass == class)
    return YES;
  return NO;
}

BOOL
isMetaClass(class)
     Class class;
{
  struct objfile *objfile;
  const struct section *section;

  for (objfile = object_files; objfile; objfile = objfile->next) {
    SegmentManager *manager = objfile->manager;
    const struct section *section
      = [manager getSeg: "__OBJC" sect: "__meta_class"];
    if (section) {
      if ((section->addr <= (unsigned)class) 
	  && ((unsigned)class < (section->addr + section->size)))
	return YES;
    }
  }
  return NO;
}

int
lookup_child_selector(methodName)
     char *methodName;
{
  value_ptr function, val, methodString;
  int methodSel;

  if (selectorHash
      && methodName && *methodName
      && NXHashMember(selectorHash, methodName)) {
    function = value_of_function("sel_getUid");
    if (function) {
      methodString = value_string(methodName, strlen(methodName) + 1);
      val = call_function_by_hand(function, 1, &methodString);
      if (value_logical_not(val))
	error("No method named %s.", methodName);
      else 
	return value_as_long(val);
    } else
      error("Couldn't find sel_getUid in inferior.");
  } else
    error("No method named %s.", methodName);
  return 0;
}

char *
lookup_child_selector_name(sel)
     SEL sel;
{
  value_ptr function, val, selector;
  char *methodName;

  function = value_of_function("sel_getName");
  if (function) {
    selector = value_from_longest(builtin_type_long, (LONGEST)sel);
    val = call_function_by_hand(function, 1, &selector);
    if (value_logical_not(val))
      error("No method named %s.", methodName);
    else 
      return WARPSTRING((char *)value_as_long(val));
  } else
    error("Couldn't find sel_getName in inferior.");
  return 0;
}

#ifdef GDB_SPARC
#define IS_ALIGNED(addr) (((unsigned int)(addr)) % 4 == 0)
#endif	/* MVS: protect sparc gdb from crashing on un-aligned objects */

Class
classForObject(id object)
{
  id myObject = WARP(object);
  Class class;

  if (myObject 
#ifdef GDB_SPARC
      && IS_ALIGNED(myObject)
#endif
      && isClass(class = myObject->isa, YES))
    return class;
  else
    return NULL;
}

Class
classForObjectOnly(object)
     id object;
{
  id myObject = WARP(object);
  Class class;

  if (myObject 
#ifdef GDB_SPARC
      && IS_ALIGNED(myObject)
#endif
      && isClass(class = myObject->isa, NO))
    return class;
  else
    return NULL;
}

CORE_ADDR
find_implementation_from_class(class, sel)
     Class class;
     SEL sel;
{
  Class myClass;
  Method method, foundMethod = NULL;
  struct objc_method_list **methods, *myMethods;
  int nMethods;
  for (myClass = WARP(class);
       !foundMethod && myClass;
       myClass = WARP(myClass->super_class)) {
    struct objc_method_list **localMethodList = 
      methodLists(regionManager, myClass);

    for (foundMethod = NULL;
	 !foundMethod
	 && *localMethodList != -1
	 && (myMethods = WARP(*localMethodList))
	 && (myMethods = [regionManager pointerFor: *localMethodList
		                          withSize: myMethods->method_count
			                    * sizeof(struct objc_method)]);
	 localMethodList++ ) {
      for (nMethods = myMethods->method_count,
	   method = myMethods->method_list;
	   !foundMethod && nMethods;
	   nMethods--, method++) {
	if (method->method_name == sel)
	  foundMethod = method;
      }
    }
  }
  if (foundMethod)
    return (CORE_ADDR)foundMethod->method_imp;
  else
    return 0;
}

CORE_ADDR
find_implementation(object, sel)
     id object;
     SEL sel;
{
  Class class;
  if (class = classForObject(object))
    return find_implementation_from_class(class, sel);
  else
    return 0;
}

int cmpImps(i1, i2)
     const void *i1, *i2;
{
  ClassImp * const *imp1 = i1, * const *imp2 = i2;
  return strcmp((*imp1)->className, (*imp2)->className);
}

int
lookup_method(argPtr, values)
     char **argPtr;
     struct symtabs_and_lines *values;
{
  int ret = 0, impIndex, i, size, j, maxLen = 0, maxNum, len, numPerLine;
  int num, numImps, numLines, numFullLines, line;
  char *p = *argPtr, *saveP, *methodName, *className;
  ClassImp *imps, *imp, **impArray;
  OnlyOne onlyOne = ANYTHING;
  struct minimal_symbol *minSym = NULL;
#define MAX_CHOICES	(100)
  CORE_ADDR		choices[MAX_CHOICES];
  int			num_choices = 0;

  if (strchr("+-[", *p)) {
    switch (*p) {
    case '-':
      onlyOne = INSTANCEONLY;
      if (*(p + 1) == '[') 
	p += 2;
      else
	return 0;
      break;
	    case '+':
      onlyOne = CLASSONLY;
      if (*(p + 1) == '[') 
	p += 2;
      else
	return 0;
      break;
    case '[': default:
      p++;
      break;
    }

    /* Copy class name */
    for (; *p && *p == ' '; p++)
      ;
    for (saveP = p, size = 0; *p && (*p != ' '); p++, size++)
      ;
    className = alloca(size + 1);
    memcpy(className, saveP, size), className[size] = '\0';

    /* Copy method name */
    for (; *p && *p == ' '; p++)
      ;
    for (saveP = p, size = 0; *p && (*p != ' ') && (*p != ']'); p++, size++)
      ;
    methodName = alloca(size + 1);
    memcpy(methodName, saveP, size), methodName[size] = '\0';
    if (*p == ']')
      p++;
  } else {

    /* Copy method name */
    for (; *p && *p == ' '; p++);
    for (saveP = p, size = 0; *p && (*p != ' '); p++, size++);
    methodName = alloca(size + 1);
    memcpy(methodName, saveP, size), methodName[size] = '\0';

    className = NULL;
  }
  if (className && !lookup_objc_class(className))
    error("No class named %s.", className);
  else if (selectorHash && methodName && 
	   *methodName && NXHashMember(selectorHash, methodName) &&
	   (imps = findImps(methodName, className, onlyOne))) {
    if (!className && !onlyOne)
      minSym = lookup_minimal_symbol(methodName, NULL, NULL);
    if (imps->next || minSym) {
      for (imp = imps, numImps = 0; imp; imp = imp->next, numImps++);
      impArray = alloca(numImps * sizeof(*impArray));
      for (imp = imps, i = 0; imp; imp = imp->next, i++) {
	impArray[i] = imp;
	len = strlen(imp->className);
	if (len > maxLen)
	  maxLen = len;
      }
      qsort(impArray, numImps, sizeof(*impArray), cmpImps);
      for (maxNum = 1, num = 10; num <= numImps; maxNum++, num *= 10);
      numPerLine = 80 / (maxLen + maxNum + 4);
      if (numPerLine < 1) numPerLine = 1;
      if (numPerLine > i) numPerLine = numImps;
      maxLen += ((80 - (numPerLine * (maxLen + maxNum + 4))) / numPerLine);
      numLines = ((numImps - 1) / numPerLine) + 1;
      numFullLines = numImps - ((numImps / numPerLine) * numPerLine);
      if (0) {
	/* location for some old, decommissioned code. It used to 
	 * send the list of possible methods to a UI
	 */
      } else {
	char *nextBlank, *pBuf, buf[300];
	if (numImps == 1)
	  printf_filtered("The following class implements %s:\n",
			  methodName);
	else
	  printf_filtered("The following classes implement %s:\n",
			  methodName);
	for (i = 0, line = 0; i < numImps; line++) {	
	  for (j = 0; i < numImps && j < numPerLine; j++, i++) {
	    if (!numFullLines || j <= numFullLines)
	      impIndex = (j * numLines) + line;
	    else
	      impIndex = (numFullLines * numLines)
		+ ((j - numFullLines) * (numLines - 1)) + line;
	    imp = impArray[impIndex];
	    printf_filtered("%*d) %c%-*s ", maxNum, impIndex + 1,
			    imp->isFactory ? '+' : '-',
			    maxLen, imp->className);
	  }
	  printf_filtered("\n");
	}
	if (minSym) {
	  if (numImps > 1)
	    printf_filtered("\n");
	  printf_filtered("%*d) %s() as a function\n\n",
			  maxNum, numImps + 1, methodName);
	}
	if (!isatty (fileno (stdin))) {		/* reading from a pipe */
	  printf_filtered("Which one(s) do you want? ");
	  fflush(stdout);			/* no newline at end of prompt*/
	  fgets(buf, sizeof(buf), stdin);
	}
	else {
	  /* use readline for input (for editing and completion) */
	  if (pBuf = (char *) readline("Which one(s) do you want? ")) {
	    strcpy (buf, pBuf);
	    free (pBuf);
	  } else {	/* null input */
	    buf[0] = '\0';
	  }
	}
	for (pBuf = buf; pBuf && *pBuf; 
	     pBuf = nextBlank ? nextBlank + 1 : NULL) {
	  if (num_choices >= MAX_CHOICES) {
	    printf_filtered("You can only choose upto %d classes.\n",
			    MAX_CHOICES);
	    break;
	  }
	  while (*pBuf && (*pBuf == ' '))
	    pBuf++;
	  nextBlank = strpbrk(pBuf, " \n");
	  if (nextBlank)
	    *nextBlank = '\0';
	  if (isdigit(*pBuf)) {
	    impIndex = atoi(pBuf);
	    if ((1 <= impIndex) && (impIndex <= numImps))
	      choices[num_choices++] =

		(CORE_ADDR) impArray[impIndex - 1]->function;

	    else if (minSym && (impIndex == (numImps + 1)))
	      choices[num_choices++] =
		(CORE_ADDR) SYMBOL_VALUE_ADDRESS(minSym);
	  } else {
	    for (i = 0; i < numImps; i++) {
	      if (strcmp(pBuf, impArray[i]->className) == 0) {
		choices[num_choices++] = (CORE_ADDR) impArray[i]->function;
		break;
	      }
	    }
	  }
	} /* end foreach input line */
      }
      for (i = 0; i < numImps; i++)
	free(impArray[i]);
    } else
      choices[num_choices++] = (CORE_ADDR) imps->function;
    if (num_choices > 0) {
      int count = num_choices;
      values->sals = xmalloc(sizeof(*values->sals) * count);
      values->nelts = count;
      while (count--) {
	CORE_ADDR pc = choices[count];
	find_pc_symtab(pc);
	SKIP_PROLOGUE(pc);
	values->sals[count] = find_pc_line(pc, 0);
#warning Always using the start address, no matter what symtab says
#ifdef DONTTRUSTTHISPC
	if (!values->sals[count].symtab)
#endif
	  values->sals[count].pc = pc;
	ret = 1;
      }
    } else {
      error("No method chosen.");
    }
  }
  if (ret) {
    *argPtr = p;
    return ret;
  } else if (className) {
    if (methodName && *methodName) {
      error("%s responds to no %smethod named %s.",
	    className,
	    (onlyOne == CLASSONLY) ?
	    "class " : ((onlyOne == INSTANCEONLY) ? "instance " : ""), 
	    methodName);
    } else
      error ("No method name given.");
  } else
    return 0;
}

void static
addMethods(manager, methods)
     SegmentManager *manager;
     struct objc_method_list *methods;
{
  Method method;
  int nMethods;

  if (methods) {
    for (nMethods = methods->method_count, method = methods->method_list;
	 nMethods; nMethods--, method++) {
      char *methodName = 
	[currentManager pointerForString: (char *)method->method_name];
      if (methodName)
	NXHashInsertIfAbsent( selectorHash, stringSave(methodName));
    }
  }
}

void static
addMethodDescriptions(manager, methods)
     SegmentManager *manager;
     struct objc_method_description_list *methods;
{
  struct objc_method_description *method;
  int nMethods;

  if (methods) {
    for (nMethods = methods->count, method = methods->list;
	 nMethods; nMethods--, method++) {
      char *methodName = 
	[currentManager pointerForString: (char *)method->name];
      if (methodName)
	NXHashInsertIfAbsent( selectorHash, stringSave(methodName));
    }
  }
}

void
objc_completion_list_hook (text)
     char *text;
{
  int nameLen;
  char firstChar, *selector;
  NXHashState state;

  if (!selectorHash)
    return;
  nameLen = strlen(text);
  if (nameLen) {
    nameLen--;
    firstChar = *text;
    text++;
    state = NXInitHashState(selectorHash);
    while (NXNextHashState(selectorHash, &state, (void **)&selector)) {
      if (firstChar == *selector && strncmp(text, selector + 1, nameLen) == 0)
	completion_list_add_symbol_no_check(selector);
    }
  }
}

struct type *
typeForObject(object, block)
     CORE_ADDR object;
     struct block *block;
{
  Class class, myClass;
  char *myClassName;
  struct type *structType;

  if (regionManager
      && (class = classForObjectOnly((id)object))
      && (myClass = WARP(class))
#ifdef GDB_SPARC
      && IS_ALIGNED(myClass)
#endif
      && (myClassName = WARPSTRING(myClass->name))
      && (structType = lookup_struct_no_error(myClassName, NULL)))
    return lookup_pointer_type(structType);
  else
    return NULL;
}

struct type *
typeForClassName(className)
     char *className;
{
  if (NXMapGet(classMap, className))
    return lookup_struct_no_error(className, 0);
  else
    return NULL;
}

int
isObjectType(type)
     struct type *type;
{
  struct type *target_type;
  char *name;

  return ((TYPE_CODE(type) == TYPE_CODE_PTR)
	  && (target_type = TYPE_TARGET_TYPE(type))
	  && (TYPE_CODE(target_type) == TYPE_CODE_STRUCT)
	  && (   (name = TYPE_NAME(target_type))
	      || (name = TYPE_TAG_NAME(target_type)))
	  && (strstr(name, "objc_object")
	      || ((strncmp(name, "struct ", 7) == 0)
		  && (lookup_objc_class(name + 7)))));
}

struct type *
objectType(object)
     CORE_ADDR object;
{
  Class class, myClass;
  char *myClassName;

  if (regionManager
      && (class = classForObject((id)object))
      && (!isMetaClass(class))
      && (myClass = WARP(class))
#ifdef GDB_SPARC
      && IS_ALIGNED(myClass)
#endif
      && (myClassName = WARPSTRING(myClass->name)))
    return lookup_struct_no_error(myClassName, 0);
  else
    return NULL;
}

static void
addModSect(manager, section)
     SegmentManager *manager;
     const struct section *section;
{
  Class class, *pClass;
  int nClasses, nMods, nCats;
  Module mod;
  Symtab symtab;
  Category cat, *pCat;

  for (nMods = section->size / sizeof(struct objc_module),
       mod = [currentManager pointerFor: (void *)section->addr];
       nMods;
       nMods--, mod++) {
    if (mod) {
      symtab = WARPG(mod->symtab);
      if (symtab) {
	for (nClasses = symtab->cls_def_cnt,
	     pClass = (Class *)symtab->defs;
	     nClasses;
	     nClasses--, pClass++) {
	  class = WARPG(*pClass);
	  if (class) {
	    struct objc_method_list *methods = 
	      WARPG(*methodLists( manager, class ));
	    const char *className = 
	      [currentManager pointerForString: class->name];

	    if (className)	/* MVS: name to be inserted into classMap */
	      NXMapInsert(classMap, stringSave(className), *pClass);
	    if (methods) 	/* MVS: instance methods (I think) */
	      addMethods(manager, methods);
	    class = WARPG(class->isa);
	    if (class) {	/* MVS: class methods (I think) */
	      methods = WARPG( *methodLists(manager, class) );
	      if (methods)
		addMethods(manager, methods);
	    }
	  }
	}
	for (nCats = symtab->cat_def_cnt, pCat = (Category *)pClass;
	     nCats;
	     nCats--, pCat++) {
	  cat = WARPG(*pCat);
	  if (cat) {
	    addMethods(manager, WARPG(cat->instance_methods));
	    addMethods(manager, WARPG(cat->class_methods));
	  }
	}
      }
    }
  }
}

static void
addProtoSect(manager, section)
     SegmentManager *manager;
     const struct section *section;
{
  typedef struct { @defs(Protocol) } ProtocolID;
  ProtocolID *proto;
  int nProtos;

  for (nProtos = section->size / sizeof(ProtocolID),
       proto = [currentManager pointerFor: (void *)section->addr 
				 withSize: section->size];
       nProtos;
       nProtos--, proto++) {
    if (proto) {
      addMethodDescriptions(manager, WARPG(proto->instance_methods));
      addMethodDescriptions(manager, WARPG(proto->class_methods));
    }
  }
}

static void
addRefSect(manager, section)
     SegmentManager *manager;
     const struct section *section;
{
  SEL *sel;
  int nSels;
  for (nSels = section->size / sizeof(SEL),
       sel = [currentManager pointerFor: (void *)section->addr 
			       withSize: section->size];
       nSels;
       nSels--, sel++) {
    char *methodName = [currentManager pointerForString: (char *)*sel];
    if (methodName)
      NXHashInsertIfAbsent( selectorHash, stringSave(methodName));
  }
}

void
readObjC(manager)
     SegmentManager *manager;
{
  const struct section *modSect, *protoSect, *refSect;

  if (!selectorHash)
    selectorHash = NXCreateHashTable(NXStrPrototype, 0, NULL);
  if (!classMap)
    classMap = NXCreateMapTable(NXStrValueMapPrototype, 0);

  if (modSect = [manager getSeg: "__OBJC" sect: "__module_info"])
    addModSect(manager, modSect);
  if (protoSect = [manager getSeg: "__OBJC" sect: "__protocol"])
    addProtoSect(manager, protoSect);
  if (refSect = [manager getSeg: "__OBJC" sect: "__message_refs"])
    addRefSect(manager, refSect);
}

struct type *
objc_type(val, block)
     value_ptr val;
     struct block *block;
{
  struct type *oldType, *newType;

  oldType = VALUE_TYPE(val);
  if (isObjectType(oldType))
    return typeForObject(*(CORE_ADDR *)VALUE_CONTENTS(val), block);
  else
    return NULL;
}

void
cast_to_objc_type(val, block)
     value_ptr val;
     struct block *block;
{
  struct type *newType;

  if (newType = objc_type(val, block))
    VALUE_TYPE(val) = newType;
}

value_ptr
value_of_self (complain)
     int complain;
{
  extern struct frame_info * selected_frame;
  struct symbol *func, *sym;
  struct block *b;
  static char *savedSelf = NULL;

  if (selected_frame == 0)
    if (complain)
      error ("no frame selected");
    else return 0;

  func = get_frame_function (selected_frame);
  if (!func)
    return 0;

  b = SYMBOL_BLOCK_VALUE (func);
  if (BLOCK_NSYMS (b) <= 0)
    if (complain)
      error ("no args, no `self'");
    else return 0;

  sym = BLOCK_SYM (b, 0);

  if (!savedSelf)
    savedSelf = stringSave("self");

  if (savedSelf != SYMBOL_NAME (sym))
    if (complain)
      error ("current stack frame not in method");
    else return 0;

  sym = lookup_block_symbol(b, savedSelf, VAR_NAMESPACE);

  return read_var_value (sym, selected_frame);
}

static NXStream *hisStream = NULL;

void clearPrintStream()
{
  hisStream = NULL;
}

value_ptr value_nsstring(ptr, len)
     char *ptr;
     int len;
{
  value_ptr stringValue = value_string(ptr, len), istrValue, nsstringValue;

  istrValue = value_of_function("_NSNewStringFromCString");
  /* _NSNewStringFromCString replaces "istr" after Lantern2A */
  if (!istrValue)
    istrValue = value_of_function("istr");
  if (istrValue) {
    struct type *type = lookup_struct_no_error("NSString", 0);

    nsstringValue = call_function_by_hand(istrValue, 1, &stringValue);
    if (!type)
      type = lookup_struct_no_error("NXString", 0);
    if (type) {
      type = lookup_pointer_type(type);
      VALUE_TYPE(nsstringValue) = type;
    }
    return nsstringValue;
  } else
    error ("NSString: internal error.  NSstring lib not linked?");
}
    
void printObjectCommand(args, from_tty)
     char *args;
     int from_tty;
{
  static value_ptr streamVal = NULL, zeroVal = NULL, printSelVal = NULL;
  static int printSel = 0;
  value_ptr  objectVal;
  id     hisObject;
  int    len;

  if (!args || !*args)		/* MVS: check for valid input */
    error("PrintObject requires an argument");

  if (!hisStream) {			/* open a stream in the inferior */
    value_ptr openFunc, Args[3];

    printSel = 0;	/* MVS: better force new lookup of print selector */
    if (!(zeroVal = value_from_longest(builtin_type_long, 0)))
      error("PrintObject: internal error 1");
    release_value(zeroVal);
    if ((openFunc = value_of_function("NXOpenMemory")) == 0)
      error("PrintObject: failed to find function NXOpenMemory");
    Args[0] = Args[1] = zeroVal;
    if (!(Args[2] = value_from_longest(builtin_type_long, NX_WRITEONLY)))
      error("PrintObject: internal error 2");
    if (!(streamVal = call_function_by_hand(openFunc, 3, Args)) ||
	!(hisStream = (NXStream *) value_as_long(streamVal)))
      error("failed to open stream for output");
    release_value(streamVal);
  } else {
    value_ptr seekFunc, Args[]   = {streamVal, zeroVal, zeroVal};

    if (!(seekFunc = value_of_function("NXSeek")))
      error("PrintObject: failed to find function NXSeek");
    call_function_by_hand(seekFunc, 3, Args);
  }

  if (!printSel || !printSelVal) {	/* get selector for printForDebugger */
    printSel    = lookup_child_selector("printForDebugger:");
    if (!(printSelVal = value_from_longest(builtin_type_long, 
					   (LONGEST) printSel)))
      error("PrintObject: internal error 3");
    release_value(printSelVal);
  }

  if (!(objectVal = parse_and_eval(args)))
    error("PrintObject: invalid expression <%s>", args);
  hisObject = (id)value_as_long(objectVal);
  if (hisObject) {
    unsigned imp;
    value_ptr printVal, Args[] = {objectVal, printSelVal, streamVal};
    NXStream *myStream;
    char *myBuf, *myString;

    if (!(imp = find_implementation(hisObject, (SEL)printSel))) 
      /* MVS: args not an object!? */
      error("%s is not an object, or does not respond to printForDebugger",
	    args);

    if (!(printVal = value_from_longest(builtin_type_long, (LONGEST)imp)))
      error("PrintObject: internal error 5");
    /* call printForDebugger */
    call_function_by_hand(printVal, 3, Args);
    if (!(myStream = WARP(hisStream)))	/* grab the output stream */
      error("PrintObject: internal error 6");
    len = myStream->buf_ptr - myStream->buf_base;
    myBuf = [regionManager pointerFor: myStream->buf_base withSize: len];
    if (!myBuf)
      error("PrintObject: internal error 7");
    fwrite(myBuf, 1, len, stdout);	/* send his output to our output */
    fputs("\n", stdout);
  }
  else
    error("<%s: nil object>", args);
}

#define    TRIM    0177            /* Mask to strip quote bit */

static int globMatch(s, p)
char *s, *p;
{
  int scc;
  int ok, lc;
  int c, cc;

  for (;;) {
    scc = *s++ & TRIM;
    switch (c = *p++) {
    case '[':
      ok = 0;
      lc = 077777;
      while (cc = *p++) {
	if (cc == ']') {
	  if (ok)
	    break;
	  return (0);
	}
	if (cc == '-') {
	  if (lc <= scc && scc <= *p++)
	    ok++;
	} else
	  if (scc == (lc = cc))
	    ok++;
      }
      if (cc == 0)
	return 0;
      continue;

    case '*':
      if (!*p)
	return (1);
      for (s--; *s; s++)
	if (globMatch(s, p))
	  return (1);
      return (0);

    case 0:
      return (scc == 0);

    case '?':
      if (scc == 0)
	return (0);
      continue;

    default:
      if ((c & TRIM) != scc)
	return (0);
      continue;
    }
  }
}

static int
pstrcmp(ps1, ps2)
     const void *ps1, *ps2;
{
  return strcmp(*(char **)ps1, *(char **)ps2);
}

static void
printNames(found, numFound)
     char **found;
     int numFound;
{
  int i, maxLen = 0, len, numPerLine, j;

  for (i = 0; i < numFound; i++) {
    QUIT;
    len = strlen(found[i]);
    if (len > maxLen)
      maxLen = len;
  }
  numPerLine = 80 / (maxLen + 1);
  if (numPerLine < 1) numPerLine = 1;
  if (numPerLine > numFound) numPerLine = numFound;
  maxLen += ((80 - (numPerLine * (maxLen + 1))) / numPerLine);
  qsort(found, numFound, sizeof(*found), pstrcmp);

  for (i = 0; i < numFound;) {
    QUIT;
    for (j = 0; (i < numFound) && (j < (numPerLine-1)); j++) {
      printf("%-*s ", maxLen, found[i++]);
    }
    printf("%s\n", i < numFound ? found[i++] : "");
  }
}

static void
infoClassesCommand(args, from_tty)
     char *args;
     int from_tty;
{
  if (classMap) {
    int numAlloced = 24, numFound = 0, i, maxLen = 0;
    int len, numPerLine, j, extra;
    char **found = malloc(numAlloced * sizeof(*found));
    struct cleanup *cleanups = make_cleanup(free, found);
    NXMapState state;
    Class class;
    char *className;

    if (!args)
      args = "*";

    state = NXInitMapState(classMap);
    while (NXNextMapState(classMap, &state,
			  (void **)  &className, (void **) &class)) {
      QUIT;
      if (globMatch(className, args)) {
	if (numFound == numAlloced) {
	  numAlloced *= 2;
	  found = realloc(found, numAlloced * sizeof(*found));
	}
	found[numFound++] = className;
      }
    }
    if (numFound) {
      printf("The following classes match %s.\n", args); 
      printNames(found, numFound);
    } else
      printf("No classes match %s.\n", args);
    do_cleanups(cleanups);
  }
}

static void
infoSelectorsCommand(args, from_tty)
     char *args;
     int from_tty;
{
  if (selectorHash) {
    int numAlloced = 24, numFound = 0, i, maxLen = 0, len, numPerLine, j;
    char **found = malloc(numAlloced * sizeof(*found)), *selName;
    struct cleanup *cleanups = make_cleanup(free, found);
    NXHashState state;

    if (!args)
      args = "*";

    state = NXInitHashState(selectorHash);
    while (NXNextHashState(selectorHash, &state, (void **)&selName)) {
      QUIT;
      if (globMatch(selName, args)) {
	if (numFound == numAlloced) {
	  numAlloced *= 2;
	  found = realloc(found, numAlloced * sizeof(*found));
	}
	found[numFound++] = selName;
      }
    }
    if (numFound) {
      printf("The following selectors match %s.\n", args); 
      printNames(found, numFound);
    } else
      printf("No selectors match %s.\n", args);
    do_cleanups(cleanups);
  }
}

static NXMapTable *
removeRangeFromMap(mapTable, start, size)
     NXMapTable *mapTable;
     void *start;
     unsigned size;
{
  NXMapState state = NXInitMapState(mapTable);
  void *key, *value;

  while (NXNextMapState(mapTable, &state, &key, &value)) {
    if (start <= value && value < (start + size))
      NXMapRemove(mapTable, key);
  }
  return mapTable;
}

static NXHashTable *
removeRangeFromHash(hashTable, start, size)
     NXHashTable *hashTable;
     void *start;
     unsigned size;
{
  NXHashTable *newHash = NXCreateHashTable(NXStrPrototype, 
					   NXCountHashTable(hashTable),
					   0);
  NXHashState state = NXInitHashState(hashTable);
  void *key;

  while (NXNextHashState(hashTable, &state, &key)) {
    if (!(start <= key && key < (start + size)))
      NXHashInsert(newHash, key);
  }
  return newHash;
}

void
removeObjC(manager)
     SegmentManager *manager;
{
  if (classMap)
    classMap = removeRangeFromMap(classMap,
				  manager->images->header,
				  (unsigned)manager->images->size);
}

void _initialize_objc()
{
  add_com("print-object", class_vars, printObjectCommand,
	  "Print object by sending \"printForDebugger:\" to it.\n");
  add_com_alias("po", "print-object", class_vars, 2);
  add_info("classes", infoClassesCommand, "All Objective C classes");
  add_info("selectors", infoSelectorsCommand, "All Objective C selectors");
}
