Browse Source

Initial version

ceriel 34 years ago
parent
commit
dbf9a060c2
53 changed files with 6156 additions and 0 deletions
  1. 141 0
      util/grind/Amakefile
  2. 40 0
      util/grind/LLgen.amk
  3. 6 0
      util/grind/PROBLEMS
  4. 3 0
      util/grind/READ_ME
  5. 5 0
      util/grind/ack-defs.amk
  6. 245 0
      util/grind/avl.cc
  7. 43 0
      util/grind/avl.h
  8. 43 0
      util/grind/cc_hh_tools.amk
  9. 71 0
      util/grind/char.ct
  10. 24 0
      util/grind/char_tools.amk
  11. 48 0
      util/grind/class.h
  12. 590 0
      util/grind/commands.g
  13. 679 0
      util/grind/dbx_string.g
  14. 194 0
      util/grind/dbxread.c
  15. 65 0
      util/grind/dump.c
  16. 13 0
      util/grind/expr.c
  17. 38 0
      util/grind/file.hh
  18. 15 0
      util/grind/idf.c
  19. 21 0
      util/grind/idf.h
  20. 163 0
      util/grind/itemlist.cc
  21. 47 0
      util/grind/langdep.cc
  22. 32 0
      util/grind/langdep.h
  23. 146 0
      util/grind/list.c
  24. 121 0
      util/grind/main.c
  25. 26 0
      util/grind/make.allocd
  26. 6 0
      util/grind/make.next
  27. 18 0
      util/grind/make.ops
  28. 36 0
      util/grind/make.tokcase
  29. 6 0
      util/grind/make.tokfile
  30. 59 0
      util/grind/message.h
  31. 61 0
      util/grind/modula-2.c
  32. 14 0
      util/grind/op_tools.amk
  33. 12 0
      util/grind/operator.h
  34. 24 0
      util/grind/operators.ot
  35. 196 0
      util/grind/position.c
  36. 57 0
      util/grind/position.h
  37. 322 0
      util/grind/print.c
  38. 134 0
      util/grind/rd.c
  39. 5 0
      util/grind/rd.h
  40. 523 0
      util/grind/run.c
  41. 131 0
      util/grind/scope.cc
  42. 54 0
      util/grind/scope.h
  43. 8 0
      util/grind/sizes.h
  44. 237 0
      util/grind/symbol.c
  45. 58 0
      util/grind/symbol.hh
  46. 15 0
      util/grind/tok_tools.amk
  47. 88 0
      util/grind/tokenname.c
  48. 17 0
      util/grind/tokenname.h
  49. 594 0
      util/grind/tree.c
  50. 32 0
      util/grind/tree.hh
  51. 387 0
      util/grind/type.c
  52. 118 0
      util/grind/type.hh
  53. 125 0
      util/grind/value.c

+ 141 - 0
util/grind/Amakefile

@@ -0,0 +1,141 @@
+AMAKELIB = { . , /usr/local/lib/amake } ;
+
+%include ack-defs.amk ;
+%include common.amk ;
+%include cc_hh_tools.amk ;
+%include tok_tools.amk ;
+%include op_tools.amk ;
+%include char_tools.amk ;
+%include LLgen.amk ;
+%include cc-c.amk ;
+%include loader.amk ;
+%include lint.amk ;
+
+%default grind ;
+
+TOKENNAMES = tokenname.c [
+	gen_tokens,
+	cc-dest = symbol2str.c,
+	LL-dest = tokenfile.g
+];
+
+DBS_LLTARGETS = {
+	dbx_string.c[type=C-src],
+	DBSpars.c[type=C-src],
+	DBSpars.h[type=C-incl]
+} ;
+
+DBS_LLSRC = {
+	dbx_string.g
+} ;
+
+CMD_LLTARGETS = {
+	tokenfile.c[type=C-src],
+	commands.c[type=C-src],
+	Lpars.c[type=C-src],
+	Lpars.h[type=C-incl]
+} ;
+
+CMD_LLSRC = {
+	tokenname.c,
+	commands.g
+} ;
+
+GENNEXTSRC = {
+	file.h[type=C-incl],
+	next.c[type=C-src]
+} ;
+
+CSRC = {
+	dbxread.c,
+	main.c,
+	list.c,
+	tree.c,
+	expr.c,
+	position.c,
+	idf.c,
+	run.c,
+	dump.c,
+	symbol.c,
+	print.c,
+	value.c,
+	type.c,
+	rd.c,
+	modula-2.c
+} ;
+
+HSRC = {
+	tokenname.h,
+	operator.h,
+	class.h,
+	position.h,
+	idf.h,
+	message.h,
+	avl.h,
+	scope.h,
+	langdep.h,
+	sizes.h,
+	rd.h
+} ;
+
+HHSRC = {
+	file.hh,
+	type.hh,
+	symbol.hh,
+	tree.hh,
+	avl.cc,
+	scope.cc,
+	itemlist.cc,
+	langdep.cc
+} ;
+
+LIBRARIES = {
+	$EMHOME/modules/lib/libassert.a,
+	$EMHOME/modules/lib/liballoc.a,
+	$EMHOME/modules/lib/malloc.o,
+	$EMHOME/modules/lib/libstring.a,
+	$EMHOME/modules/lib/libobject.a,
+	$EMHOME/modules/lib/libsystem.a
+} ;
+
+DBFLAGS = { -g, -DDEBUG } ;
+PROFFLAGS = { } ;
+
+LDFLAGS = {
+	-Bstatic,
+	$PROFFLAGS,
+	$DBFLAGS
+} ;
+
+INCLUDES = {
+	-I$EMHOME/modules/h,
+	-I$EMHOME/modules/pkg,
+	-I$EMHOME/h
+} ;
+
+CFLAGS = {
+	$INCLUDES,
+	$PROFFLAGS,
+	$DBFLAGS
+} ;
+
+LINTFLAGS = {
+	$INCLUDES
+} ;
+
+%cluster {
+	%targets $DBS_LLTARGETS ;
+	%sources $DBS_LLSRC ;
+	%use LLgen(prefix => DBS) ;
+} ;
+
+%cluster {
+	%targets lint.out[type = lint-output];
+	%sources $CMD_LLSRC + $CSRC + $DBS_LLTARGETS + $HHSRC + char.ct + operators.ot ;
+	%use lint(realdest => lint.out) ;
+} ;
+
+%cluster {
+	%targets grind[type = program];
+	%sources $CMD_LLSRC + $CSRC + $DBS_LLTARGETS + $HHSRC + char.ct + operators.ot ;
+} ;

+ 40 - 0
util/grind/LLgen.amk

@@ -0,0 +1,40 @@
+# LLgen:	LL(1) parser generator
+# variables:	LLGEN, LLFLAGS
+
+# tool definition for the new version of LLgen that allows for more than
+# one parser in one program. Unfortunately, for historical reasons there
+# is no proper default prefix for LLgen-generated files (LL.output versus
+# Lpars.[ch]). If LLgen would generate LLpars.[ch] instead of Lpars.[ch],
+# we could have a default value for prefix of 'LL', which would make
+# things a bit more simple.
+
+%instance deftypesuffix(LLgen-src, '%.g') ;
+
+%include ack-defs.amk;
+
+%if (%not defined(LLGEN), {
+    LLGEN = $EMHOME/bin/LLgen;
+});
+
+%if (%not defined(LLFLAGS), {
+    LLFLAGS = {};
+});
+
+%tool LLgen (
+    verbose: %boolean			  => %false;
+    flags:   %string %list		  => $LLFLAGS;
+    prefix:  %string			  => '';
+    src:     %in %list  [type = LLgen-src];
+    parser:  %out %list [type = C-src]
+	=> match($src) + if($prefix == '', Lpars.c, $prefix'pars.c');
+    tokens:  %out      [type = C-incl, compare]
+	=> if($prefix == '', Lpars.h, $prefix'pars.h');
+    diagn:   %out      [type = text]
+	=> if($prefix == '', LL.output, $prefix.output) %conform $verbose;
+    cmd:     %in      [type = command]    => $LLGEN;
+)
+{
+    exec($cmd, args => if($verbose, {'-vvv'}, {}) + $flags + $src);
+    echo({'LLgen ', $src, ' done'});
+};
+

+ 6 - 0
util/grind/PROBLEMS

@@ -0,0 +1,6 @@
+- front-end cannot generate DBX symbol table information for bit-fields,
+  because it does not know about byte-order.
+- single stepping on a line by line basis is difficult if you use breakpoints.
+  The problem is where to set the next breakpoint. One solution is to use
+  single-stepping until we are at a different line, but this is probably
+  extremely slow. Another solution is to adapt edb's method.

+ 3 - 0
util/grind/READ_ME

@@ -0,0 +1,3 @@
+This is GRIND (GRind Is Not Dbx). This program is still being developed,
+so behaviour may change without notice.
+

+ 5 - 0
util/grind/ack-defs.amk

@@ -0,0 +1,5 @@
+# definition of EMHOME
+
+%if (%not defined(EMHOME), {
+    EMHOME = /usr/proj/em/Work;
+});

+ 245 - 0
util/grind/avl.cc

@@ -0,0 +1,245 @@
+/* $Header$ */
+
+#include <alloc.h>
+
+/* Implementation of AVL-trees: trees in which the difference in depth
+   of the left branch and the right branch is at most one.
+   The difference in depth is indicated by a "balance" flag in each node:
+   this flag has one of three values:
+   .	indicating that the left branch has the same depth as the right branch,
+   +	indicating that the right branch is deeper,
+   -	indicating that the left branch is deeper.
+   So, a node has the following structure:
+*/
+
+struct avl_node {
+  struct avl_node
+		*left,
+		*right;		/* the left and right branches */
+  char		*info;		/* pointer to information in this node */
+  char		balance;	/* balance information described above */
+};
+
+/* create definitions for new_avl_node() and free_avl_node() */
+/* STATICALLOCDEF "avl_node" 10 */
+
+/* There is also a tree header, which contains the root of the tree and
+   the address of a user-supplied comparison routine:
+*/
+
+struct avl_tree {
+  struct avl_node
+		*root;		/* root of the avl tree */
+  int		(*cmp)();	/* address of comparison routine */
+};
+/* create definitions for new_avl_tree() and free_avl_tree() */
+/* STATICALLOCDEF "avl_tree" 2 */
+
+/* The next routine adds a node to an avl tree. It returns 1 if the
+   tree got deeper.
+*/
+static int
+balance_add(ppsc, n, cmp)
+  struct avl_node **ppsc;	/* address of root */
+  register char *n;		/* user-supplied information */
+  int (*cmp)();			/* user-supplied comparison routine */
+{
+  register struct avl_node *psc = *ppsc, *qsc, *ssc;
+
+  if (! psc) {
+	*ppsc = new_avl_node();
+	(*ppsc)->balance = '.';
+	(*ppsc)->info = n;
+	return 1;
+  }
+  if ((*cmp)(n, psc->info) < 0) {
+	if (balance_add(&(psc->left), n, cmp)) {
+		/* left hand side got deeper */
+		if (psc->balance == '+') {
+			/* but the right hand side was deeper */
+			psc->balance = '.';
+			return 0;
+		}
+		if (psc->balance == '.') {
+			/* but the right hand side was as deep */
+			psc->balance = '-';
+			return 1;
+		}
+		/* left hand side already was one deeper; re-organize */
+		qsc = psc->left;
+		if (qsc->balance == '-') {
+			/* if left-hand side of left node was deeper,
+			   this node becomes the new root
+			*/
+			psc->balance = '.';
+			qsc->balance = '.';
+			psc->left = qsc->right;
+			qsc->right = psc;
+			*ppsc = qsc;
+			return 0;
+		}
+		/* else the right node of the left node becomes the new root */
+		ssc = qsc->right;
+		psc->left = ssc->right;
+		qsc->right = ssc->left;
+		ssc->left = qsc;
+		ssc->right = psc;
+		*ppsc = ssc;
+		if (ssc->balance == '.') {
+			psc->balance = '.';
+			qsc->balance = '.';
+			return 0;
+		}
+		if (ssc->balance == '-') {
+			psc->balance = '+';
+			qsc->balance = '.';
+			ssc->balance = '.';
+			return 0;
+		}
+		psc->balance = '.';
+		qsc->balance = '-';
+	}
+	return 0;
+  }
+  if (balance_add(&(psc->right), n, cmp)) {
+	/* right hand side got deeper */
+	if (psc->balance == '-') {
+		/* but the left hand side was deeper */
+		psc->balance = '.';
+		return 0;
+	}
+	if (psc->balance == '.') {
+		/* but the left hand side as deep */
+		psc->balance = '+';
+		return 1;
+	}
+	/* right hand side already was one deeper; re-organize */
+	qsc = psc->right;
+	if (qsc->balance == '+') {
+		/* if right-hand side of left node was deeper,
+		   this node becomes the new root
+		*/
+		psc->balance = '.';
+		qsc->balance = '.';
+		psc->right = qsc->left;
+		qsc->left = psc;
+		*ppsc = qsc;
+		return 0;
+	}
+	/* else the left node of the right node becomes the new root */
+	ssc = qsc->left;
+	psc->right = ssc->left;
+	qsc->left = ssc->right;
+	ssc->right = qsc;
+	ssc->left = psc;
+	*ppsc = ssc;
+	if (ssc->balance == '.') {
+		psc->balance = '.';
+		qsc->balance = '.';
+		return 0;
+	}
+	if (ssc->balance == '+') {
+		psc->balance = '-';
+		qsc->balance = '.';
+		ssc->balance = '.';
+		return 0;
+	}
+	psc->balance = '.';
+	qsc->balance = '+';
+  }
+  return 0;
+}
+
+/* extern struct avl_tree *create_avl_tree(int (*cmp)());
+   Returns a fresh avl_tree structure.
+*/
+struct avl_tree *
+create_avl_tree(cmp)
+  int	(*cmp)();		/* comparison routine */
+{
+  register struct avl_tree *p = new_avl_tree();
+
+  p->cmp = cmp;
+  return p;
+}
+
+/* extern add_to_avl_tree(struct avl_tree *tree, char *n);
+   Adds the information indicated by 'n' to the avl_tree indicated by 'tree'
+*/
+add_to_avl_tree(tree, n)
+  struct avl_tree	*tree;	/* tree to be added to */
+  char			*n;	/* information */
+{
+  balance_add(&(tree->root), n, tree->cmp);
+}
+
+/* extern char *find_ngt(struct avl_tree *tree, char *n);
+   Returns the information in the largest node that still compares <= to 'n',
+   or 0 if not present.
+*/
+char *
+find_ngt(tree, n)
+  struct avl_tree	*tree;	/* tree to be searched in */
+  char			*n;	/* information to be compared with */
+{
+  register struct avl_node *nd = tree->root, *lastnd = 0;
+
+  for (;;) {
+  	while (nd && (*tree->cmp)(nd->info, n) > 0) {
+		nd = nd->left;
+	}
+  	while (nd && (*tree->cmp)(nd->info, n) <= 0) {
+		lastnd = nd;
+		nd = nd->right;
+	}
+	if (! nd) break;
+  }
+  return lastnd ? lastnd->info : (char *) 0;
+}
+
+/* extern char *find_nlt(struct avl_tree *tree, char *n);
+   Returns the information in the largest node that still compares >= to 'n',
+   or 0 if not present.
+*/
+char *
+find_nlt(tree, n)
+  struct avl_tree	*tree;	/* tree to be searched in */
+  char			*n;	/* information to be compared with */
+{
+  register struct avl_node *nd = tree->root, *lastnd = 0;
+
+  for (;;) {
+  	while (nd && (*tree->cmp)(nd->info, n) < 0) {
+		nd = nd->right;
+	}
+  	while (nd && (*tree->cmp)(nd->info, n) >= 0) {
+		lastnd = nd;
+		nd = nd->left;
+	}
+	if (! nd) break;
+  }
+  return lastnd ? lastnd->info : (char *) 0;
+}
+
+/* extern char *find_eq(struct avl_tree *tree, char *n);
+   Returns the information in the node that compares equal to 'n',
+   or 0 if not present.
+*/
+char *
+find_eq(tree, n)
+  struct avl_tree	*tree;	/* tree to be searched in */
+  char			*n;	/* information to be compared with */
+{
+  register struct avl_node *nd = tree->root;
+
+  for (;;) {
+  	while (nd && (*tree->cmp)(nd->info, n) < 0) {
+		nd = nd->right;
+	}
+  	while (nd && (*tree->cmp)(nd->info, n) > 0) {
+		nd = nd->left;
+	}
+	if (! nd) break;
+  }
+  return nd ? nd->info : (char *) 0;
+}

+ 43 - 0
util/grind/avl.h

@@ -0,0 +1,43 @@
+/* $Header$ */
+
+/* AVL-trees: trees in which the difference in depth
+   of the left branch and the right branch is at most one.
+   Information in the nodes is represented by a pointer, which is to
+   be supplied by the user. The user is also expected to supply a
+   comparison routine for each AVL tree. This routine is offered two
+   parameters, both pointers, and is expected to return:
+   a negative number	if the comparison result is <
+   0 			if the comparison result is =
+   a positive number	if the comparison result is >
+*/
+
+typedef struct avl_tree	*AVL_tree;
+
+/* extern AVL_tree create_avl_tree(int (*cmp)());
+   Returns a fresh avl_tree structure. 'cmp' will be used as comparison
+   routine for this tree.
+*/
+extern AVL_tree create_avl_tree();
+
+/* extern add_to_avl_tree(AVL_tree tree, char *n);
+   Adds the information indicated by 'n' to the avl_tree indicated by 'tree'.
+*/
+extern add_to_avl_tree();
+
+/* extern char *find_ngt(AVL_tree tree, char *n);
+   Returns the information in the largest node that still compares <= to 'n',
+   or 0 if not present.
+*/
+extern char *find_ngt();
+
+/* extern char *find_nlt(AVL_tree tree, char *n);
+   Returns the information in the largest node that still compares >= to 'n',
+   or 0 if not present.
+*/
+extern char *find_nlt();
+
+/* extern char *find_eq(AVL_tree tree, char *n);
+   Returns the information in the node that compares equal to 'n',
+   or 0 if not present.
+*/
+extern char *find_eq();

+ 43 - 0
util/grind/cc_hh_tools.amk

@@ -0,0 +1,43 @@
+%instance deftypesuffix(hh-src, '%.hh') ;
+%instance deftypesuffix(cc-src, '%.cc') ;
+
+ALLOCD = make.allocd;
+NEXT = make.next;
+
+%tool allochd (
+    hhsrc:	%in [type = hh-src, persistent];
+    hsrc:	%out [type = C-incl]	=> match($hhsrc);
+    prog:	%in [type = command]	=> $ALLOCD;
+)
+{
+    exec($prog, stdin => $hhsrc, stdout => $hsrc);
+    echo({$hsrc ,'created'});
+};
+
+
+%tool alloccd (
+    ccsrc:	%in [type = cc-src, persistent];
+    csrc:	%out [type = C-src]	=> match($ccsrc);
+    prog:	%in [type = command]	=> $ALLOCD;
+)
+{
+    exec($prog, stdin => $ccsrc, stdout => $csrc);
+    echo({$csrc ,'created'});
+};
+
+
+# Possibly there's only one type of { cc-src, hh-src } available,
+# so introduce a new attribute.
+
+%derive f[cc-or-hh-src] %when get($f, type) == cc-src
+			%or   get($f, type) == hh-src;
+
+%tool mknext (
+    cchhsrc:	%in %list [cc-or-hh-src];
+    next:	%out [type = C-src]	=> next.c;
+    prog:	%in [type = command]	=> $NEXT;
+)
+{
+    exec($prog, args => $cchhsrc, stdout => $next);
+    echo({$next ,'created'});
+};

+ 71 - 0
util/grind/char.ct

@@ -0,0 +1,71 @@
+% character tables for debugger
+% $Header$
+%S257
+%F	%s,
+%
+%	CHARACTER CLASSES
+%
+%iSTGARB
+STSKIP: \t\013\014\015
+STNL:;\012
+STIDF:a-zA-Z_$
+STSTR:"'
+STDOT:.
+STNUM:0-9
+STSIMP:,<>{}:`
+%T#include "class.h"
+%Tchar tkclass[] = {
+%p
+%T};
+%
+%	INIDF
+%
+%S129
+%C
+1:a-zA-Z0-9_$
+%Tchar inidf[] = {
+%F	%s,
+%p
+%T};
+%
+%	INEXT
+%
+%S129
+%C
+1:-#+{}~`@%^=|\\;:?/,a-zA-Z0-9_$.
+%Tchar inext[] = {
+%F	%s,
+%p
+%T};
+%
+%	ISDIG
+%
+%C
+1:0-9
+%Tchar isdig[] = {
+%p
+%T};
+%
+%	ISHEX
+%
+%C
+1:A-F0-9
+%Tchar ishex[] = {
+%p
+%T};
+%
+%	ISOCT
+%
+%C
+1:0-7
+%Tchar isoct[] = {
+%p
+%T};
+%
+%	ISTOKEN
+%
+%C
+1:-abcefiprstuvxAEFGLMPQSTVX,;:+=()*
+%T char istoken[] = {
+%p
+%T};

+ 24 - 0
util/grind/char_tools.amk

@@ -0,0 +1,24 @@
+# tabgen: tool definition for character table generator
+# variables:	TABGEN, CHTAB
+
+%include ack-defs.amk;
+
+%if (%not defined(TABGEN), {
+    TABGEN = $EMHOME/bin/tabgen;
+});
+
+%if (%not defined(CHTAB), {
+    CHTAB = chtab.c;
+});
+
+%instance deftypesuffix(char_tab, '%.ct');
+
+%tool gen_tab (
+    chtab:	%in [type = char_tab];
+    cfile:	%out [type = C-src]	=> $CHTAB;
+    mktab:	%in [type = command]	=> $TABGEN;
+)
+{
+    exec($mktab, args => '-f' $chtab, stdout => $cfile);
+    echo({$cfile, 'created'});
+};

+ 48 - 0
util/grind/class.h

@@ -0,0 +1,48 @@
+/*
+ * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands.
+ * See the copyright notice in the ACK home directory, in the file "Copyright".
+ *
+ * Author: Ceriel J.H. Jacobs
+ */
+
+/* U S E   O F   C H A R A C T E R   C L A S S E S */
+
+/* $Header$ */
+
+/*	As a starter, chars are divided into classes, according to which
+	token they can be the start of.
+	At present such a class number is supposed to fit in 4 bits.
+*/
+
+#define	class(ch)	(tkclass[ch])
+
+/*	Being the start of a token is, fortunately, a mutual exclusive
+	property, so, as there are less than 16 classes they can be
+	packed in 4 bits.
+*/
+
+#define	STSKIP	0	/* spaces and so on: skipped characters		*/
+#define	STNL	1	/* newline character(s): update linenumber etc.	*/
+#define	STGARB	2	/* garbage ascii character: not allowed		*/
+#define	STDOT	3	/* '.' can start a number, or be a separate token */
+#define	STCOMP	4	/* this one can start a compound token		*/
+#define	STIDF	5	/* being the initial character of an identifier	*/
+#define	STCHAR	6	/* the starter of a character constant		*/
+#define	STSTR	7	/* the starter of a string			*/
+#define	STNUM	8	/* the starter of a numeric constant		*/
+#define	STEOI	9	/* End-Of-Information mark			*/
+#define STSIMP  10      /* this character can occur as token            */
+
+/*	But occurring inside a token is not, so we need 1 bit for each
+	class.  This is implemented as a collection of tables to speed up
+	the decision whether a character has a special meaning.
+*/
+#define	in_idf(ch)	((unsigned)ch < 0177 && inidf[ch])
+#define	in_ext(ch)	((unsigned)ch < 0177 && inext[ch])
+#define	is_oct(ch)	((unsigned)ch < 0177 && isoct[ch])
+#define	is_dig(ch)	((unsigned)ch < 0177 && isdig[ch])
+#define	is_hex(ch)	((unsigned)ch < 0177 && ishex[ch])
+#define	is_token(ch)	((unsigned)ch < 0177 && istoken[ch])
+
+extern char tkclass[];
+extern char inidf[], isoct[], isdig[], ishex[], inext[], istoken[];

+ 590 - 0
util/grind/commands.g

@@ -0,0 +1,590 @@
+/* $Header$ */
+
+/* Command grammar */
+{
+#include	<stdio.h>
+#include	<alloc.h>
+#include	<setjmp.h>
+#include	<signal.h>
+
+#include	"ops.h"
+#include	"class.h"
+#include	"position.h"
+#include	"file.h"
+#include	"idf.h"
+#include	"symbol.h"
+#include	"tree.h"
+
+extern char	*Salloc();
+extern t_lineno	currline;
+extern FILE	*db_in;
+
+int		errorgiven;
+int		extended_charset = 0;
+jmp_buf		jmpbuf;
+
+static int	init_del();
+static int	skip_to_eol();
+
+static struct token {
+  int	tokno;
+  long	ival;
+  char	*str;
+  double fval;
+  struct idf *idf;
+} tok, aside;
+
+#define TOK	tok.tokno
+#define ASIDE	aside.tokno
+}
+%start Commands, commands;
+
+%lexical LLlex;
+
+commands
+  { p_tree com, lastcom = 0;
+  }
+:
+			{ if (! setjmp(jmpbuf)) {
+				init_del();
+			  }
+			  else {
+				skip_to_eol();
+				goto prmpt;
+			  }
+			}
+  [ %persistent command_line(&com)
+			{ if (com) {
+				if (errorgiven) {
+					freenode(com);
+					com = 0;
+				}
+				if (lastcom && !in_status(lastcom) &&
+				    lastcom != run_command) {
+					freenode(lastcom);
+					lastcom = 0;
+				}
+
+				if (com) {
+			  		if (repeatable(com)) {
+						lastcom = com;
+					}
+					eval(com);
+					if (! repeatable(com) &&
+					    ! in_status(com) &&
+					    com != run_command) {
+						freenode(com);
+					}
+				}
+			  } else if (lastcom && ! errorgiven) eval(lastcom);
+			}
+    [	'\n' 		{ prmpt: prompt(); }
+    |	';'
+    ]			{ errorgiven = 0; }
+  ]*
+			{ signal_child(SIGKILL); }
+;
+
+command_line(p_tree *p;)
+:
+  list_command(p)
+| file_command(p)
+| run_command(p)
+| stop_command(p)
+| when_command(p)
+| continue_command(p)
+| step_command(p)
+| next_command(p)
+| regs_command(p)
+| WHERE			{ *p = mknode(OP_WHERE); }
+| STATUS		{ *p = mknode(OP_STATUS); }
+| DUMP			{ *p = mknode(OP_DUMP); }
+| RESTORE INTEGER	{ *p = mknode(OP_RESTORE, tok.ival); }
+| delete_command(p)
+| print_command(p)
+| trace_command(p)
+|			{ *p = 0; }
+;
+
+list_command(p_tree *p;)
+  { p_tree t1 = 0, t2 = 0; }
+:
+  LIST
+  [
+  | lin_num(&t1)
+    [ ',' lin_num(&t2)
+    |			{ t2 = mknode(OP_INTEGER, t1->t_ival); }
+    ]
+  ]			{ *p = mknode(OP_LIST, t1, t2); }
+;
+
+file_command(p_tree *p;)
+:
+  XFILE			{ extended_charset = 1; }
+  [			{ *p = 0; }
+  | name(p)		{ (*p)->t_idf = str2idf((*p)->t_str, 0); }
+  ]			{ *p = mknode(OP_FILE, *p);
+			  extended_charset = 0;
+			}
+;
+
+run_command(p_tree *p;)
+:
+  RUN			{ extended_charset = 1; *p = 0; }
+  args(p)		{ *p = mknode(OP_RUN, *p);
+			  extended_charset = 0;
+			  freenode(run_command);
+			  run_command = *p;
+			}
+| RERUN			{ if (! run_command) {
+				error("no run command given yet");
+			  }
+			  else *p = run_command;
+			}
+;
+
+stop_command(p_tree *p;)
+  { p_tree whr = 0, cond = 0; }
+:
+  STOP
+  where(&whr)?
+  condition(&cond)?	{ if (! whr && ! cond) {
+				error("no position or condition");
+				*p = 0;
+			  }
+			  else *p = mknode(OP_STOP, whr, cond);
+			}
+;
+
+trace_command(p_tree *p;)
+  { p_tree whr = 0, cond = 0, exp = 0; }
+:
+  TRACE
+  [ ON expression(&exp) ]?
+  where(&whr)?
+  condition(&cond)?	{ *p = mknode(OP_TRACE, whr, cond, exp); }
+;
+
+continue_command(p_tree *p;)
+  { long l; p_tree pos = 0; }
+:
+  CONT
+  [ INTEGER		{ l = tok.ival; }
+  |			{ l = 1; }
+  ]
+  position(&pos)?
+  			{ *p = mknode(OP_CONT, mknode(OP_INTEGER, l), pos); }
+;
+
+when_command(p_tree *p;)
+  { p_tree	whr = 0, cond = 0; }
+:
+  WHEN
+  where(&whr)?
+  condition(&cond)?
+  '{' 
+  command_line(p)
+  [ ';'			{ *p = mknode(OP_LINK, *p, (p_tree) 0);
+			  p = &((*p)->t_args[1]);
+			}
+    command_line(p)
+  ]*
+  '}'
+			{ if (! whr && ! cond) {
+				error("no position or condition");
+				freenode(*p);
+				*p = 0;
+			  }
+			  else *p = mknode(OP_WHEN, whr, cond, *p);
+			}
+;
+
+step_command(p_tree *p;)
+  { long	l; }
+:
+  STEP
+  [ INTEGER		{ l = tok.ival; }
+  |			{ l = 1; }
+  ]			{ *p = mknode(OP_STEP, l); }
+;
+
+next_command(p_tree *p;)
+  { long	l; }
+:
+  NEXT
+  [ INTEGER		{ l = tok.ival; }
+  |			{ l = 1; }
+  ]			{ *p = mknode(OP_NEXT, l); }
+;
+
+regs_command(p_tree *p;)
+  { long	l; }
+:
+  REGS
+  [ INTEGER		{ l = tok.ival; }
+  |			{ l = 0; }
+  ]			{ *p = mknode(OP_REGS, l); }
+;
+
+delete_command(p_tree *p;)
+:
+  DELETE
+  INTEGER		{ *p = mknode(OP_DELETE, tok.ival); }
+;
+
+print_command(p_tree *p;)
+:
+  PRINT expression(p)	{ *p = mknode(OP_PRINT, *p); 
+			  p = &((*p)->t_args[0]);
+			}
+  [ ','			{ *p = mknode(OP_LINK, *p, (p_tree) 0);
+			  p = &((*p)->t_args[1]);
+			}
+    expression(p)
+  ]*
+;
+
+condition(p_tree *p;)
+:
+  IF expression(p)
+;
+
+where(p_tree *p;)
+:
+  IN qualified_name(p)	{ *p = mknode(OP_IN, *p); }
+|
+  position(p)
+;
+
+expression(p_tree *p;)
+:
+	qualified_name(p)
+;
+
+position(p_tree *p;)
+  { p_tree lin;
+    char *str;
+  }
+:
+  AT
+  [ STRING		{ str = tok.str; }
+    ':'
+  |			{ if (! currfile) str = 0;
+			  else str = currfile->sy_idf->id_text;
+			}
+  ]
+  lin_num(&lin)		{ *p = mknode(OP_AT, lin->t_ival, str);
+			  freenode(lin);
+			}
+;
+
+args(p_tree *p;)
+  { int first_time = 1; }
+:
+  [			{ if (! first_time) {
+				*p = mknode(OP_LINK, *p, (p_tree) 0);
+				p = &((*p)->t_args[1]);
+			  }
+			  first_time = 0;
+			}
+	arg(p)
+  ]*
+;
+
+arg(p_tree *p;)
+:
+  name(p)
+|
+  '>' name(p)		{ (*p)->t_oper = OP_OUTPUT; }
+|
+  '<' name(p)		{ (*p)->t_oper = OP_INPUT; }
+;
+
+lin_num(p_tree *p;)
+:
+  INTEGER		{ *p = mknode(OP_INTEGER, tok.ival); }
+;
+
+qualified_name(p_tree *p;)
+:
+  name(p)
+  [	'`'		{ *p = mknode(OP_SELECT, *p, (p_tree) 0); }
+	name(&((*p)->t_args[1]))
+  ]*
+;
+
+name(p_tree *p;)
+:
+  [ XFILE
+  | LIST
+  | RUN
+  | RERUN
+  | STOP
+  | WHEN
+  | AT
+  | IN
+  | IF
+  | NAME
+  | CONT
+  | STEP
+  | NEXT
+  | REGS
+  | WHERE
+  | STATUS
+  | PRINT
+  | DELETE
+  | DUMP
+  | RESTORE
+  | TRACE
+  | ON
+  ]			{ *p = mknode(OP_NAME, tok.idf, tok.str); }
+;
+
+{
+int
+LLlex()
+{
+  register int c;
+
+  if (ASIDE) {
+	tok = aside;
+	ASIDE = 0;
+	return TOK;
+  }
+  do {
+	c = getc(db_in);
+  } while (c != EOF && class(c) == STSKIP);
+  if (c == EOF) return c;
+  switch(class(c)) {
+  case STSTR:
+	TOK = get_string(c);
+	break;
+  case STIDF:
+	TOK = get_name(c);
+	break;
+  case STDOT:
+	c = getc(db_in);
+	if (c == EOF || class(c) != STNUM) {
+		ungetc(c,db_in);
+		TOK = '.';
+		break;
+	}
+	/* Fall through */
+  case STNUM:
+	TOK = get_number(c);
+	break;
+  case STNL:
+  case STSIMP:
+	TOK = c;
+	break;
+  default:
+	error("illegal character '\\0%o'", c);
+	return LLlex();
+  }
+  return TOK;
+}
+
+int
+get_name(c)
+  register int	c;
+{
+  char	buf[512+1];
+  register char	*p = &buf[0];
+  register struct idf *id;
+
+  do {
+	if (p - buf < 512) *p++ = c;
+	c = getc(db_in);
+  } while ((extended_charset && in_ext(c)) || in_idf(c));
+  ungetc(c, db_in);
+  *p = 0;
+  if (extended_charset) {
+	tok.idf = 0;
+	tok.str = Salloc(buf, (unsigned) (p - buf));
+	return NAME;
+  }
+  id = str2idf(buf, 1);
+  tok.idf = id;
+  tok.str = id->id_text;
+  return id->id_reserved ? id->id_reserved : NAME;
+}
+
+static int
+quoted(ch)
+  int	ch;
+{
+  /*	quoted() replaces an escaped character sequence by the
+	character meant.
+  */
+  /* first char after backslash already in ch */
+  if (!is_oct(ch)) {		/* a quoted char */
+	switch (ch) {
+	case 'n':
+		ch = '\n';
+		break;
+	case 't':
+		ch = '\t';
+		break;
+	case 'b':
+		ch = '\b';
+		break;
+	case 'r':
+		ch = '\r';
+		break;
+	case 'f':
+		ch = '\f';
+		break;
+	}
+  }
+  else {				/* a quoted octal */
+	register int oct = 0, cnt = 0;
+
+	do {
+		oct = oct*8 + (ch-'0');
+		ch = getc(db_in);
+	} while (is_oct(ch) && ++cnt < 3);
+	ungetc(ch, db_in);
+	ch = oct;
+  }
+  return ch&0377;
+
+}
+
+int get_string(c)
+  int	c;
+{
+  register int ch;
+  char buf[512];
+  register int len = 0;
+
+  while (ch = getc(db_in), ch != c) {
+	if (ch == '\n') {
+		error("newline in string");
+		break;
+	}
+	if (ch == '\\') {
+		ch = getc(db_in);
+		ch = quoted(ch);
+	}
+	buf[len++] = ch;
+  }
+  buf[len++] = 0;
+  tok.str = Salloc(buf, (unsigned) len);
+  return STRING;
+}
+
+static int
+val_in_base(c, base)
+  register int c;
+{
+  return is_dig(c) 
+	? c - '0'
+	: base != 16
+	  ? -1
+	  : is_hex(c)
+	    ? (c - 'a' + 10) & 017
+	    : -1;
+}
+
+int
+get_number(c)
+  register int	c;
+{
+  char buf[512+1];
+  register int base = 10;
+  register char *p = &buf[0];
+  register long val = 0;
+  register int val_c;
+
+  if (c == '0') {
+	/* check if next char is an 'x' or an 'X' */
+	c = getc(db_in);
+	if (c == 'x' || c == 'X') {
+		base = 16;
+		c = getc(db_in);
+	}
+	else	base = 8;
+  }
+  while (val_c = val_in_base(c, base), val_c >= 0) {
+	val = val * base + val_c;
+	if (p - buf < 512) *p++ = c;
+	c = getc(db_in);
+  }
+  if (base == 16 || !((c == '.' || c == 'e' || c == 'E'))) {
+	ungetc(c, db_in);
+	tok.ival = val;
+	return INTEGER;
+  }
+  if (c == '.') {
+	if (p - buf < 512) *p++ = c;
+	c = getc(db_in);
+  }
+  while (is_dig(c)) {
+	if (p - buf < 512) *p++ = c;
+	c = getc(db_in);
+  }
+  if (c == 'e' || c == 'E') {
+	if (p - buf < 512) *p++ = c;
+	c = getc(db_in);
+	if (c == '+' || c == '-') {
+		if (p - buf < 512) *p++ = c;
+		c = getc(db_in);
+	}
+	if (! is_dig(c)) {
+		error("malformed floating constant");
+	}
+	while (is_dig(c)) {
+		if (p - buf < 512) *p++ = c;
+		c = getc(db_in);
+	}
+  }
+  ungetc(c, db_in);
+  *p++ = 0;
+  if (p == &buf[512+1]) {
+	error("floating point constant too long");
+  }
+  return REAL;
+}
+
+extern char * symbol2str();
+
+LLmessage(t)
+{
+  if (t > 0) {
+  	if (! errorgiven) {
+		error("%s missing before %s", symbol2str(t), symbol2str(TOK));
+	}
+	aside = tok;
+  }
+  else if (t == 0) {
+  	if (! errorgiven) {
+		error("%s unexpected", symbol2str(TOK));
+	}
+  }
+  else if (! errorgiven) {
+	error("EOF expected");
+  }
+  errorgiven = 1;
+}
+
+static int
+catch_del()
+{
+  signal(SIGINT, catch_del);
+  signal_child(SIGEMT);
+  longjmp(jmpbuf, 1);
+}
+
+static int
+init_del()
+{
+  signal(SIGINT, catch_del);
+}
+
+static int
+skip_to_eol()
+{
+  while (TOK != '\n' && TOK > 0) LLlex();
+  wait_for_child("interrupted");
+}
+}

+ 679 - 0
util/grind/dbx_string.g

@@ -0,0 +1,679 @@
+/* $Header$
+   Grammar of a string of a debugger symbol table entry.
+*/
+
+{
+#include	<out.h>
+#include	<alloc.h>
+
+#include	"type.h"
+#include	"symbol.h"
+#include	"scope.h"
+#include	"class.h"
+#include	"idf.h"
+
+extern char	*strindex();
+extern long	str2long();
+extern double	atof();
+extern int	saw_code;
+extern long	pointer_size;
+
+static char	*DbxPtr;		/* current pointer in DBX string */
+static int	AllowName;		/* set if NAME legal at this point */
+static long	ival;
+static double	fval;
+static char	*strval;
+static int	last_index[2];
+static struct outname	*currnam;
+
+static struct literal *get_literal_space();
+static struct fields *get_field_space();
+static end_field();
+static char *string_val();
+}
+
+%start DbxParser, debugger_string;
+
+%prefix DBS;
+
+%lexical DBSlex;
+
+%onerror DBSonerror;
+
+%token	INTEGER, REAL, STRING, NAME;
+
+debugger_string
+  { register p_symbol s;
+    char *str;
+    p_type tmp = 0;
+  }
+:
+  name(&str)
+  [ /* constant name */
+			{ s = NewSymbol(str, CurrentScope, CONST, currnam); }
+	'c' const_name(s)
+
+  | /* type name */
+			{ s = NewSymbol(str, CurrentScope, TYPE, currnam); }
+	't' type_name(&(s->sy_type))
+
+  | /* tag name (only C?) */
+			{ s = NewSymbol(str, CurrentScope, TAG, currnam); }
+	'T' tag_name(s)
+
+  | /* end scope */
+	'E' INTEGER
+			{ close_scope(); }
+
+  | /* module begin */
+			{ s = NewSymbol(str, CurrentScope, MODULE, currnam); }
+	'M' INTEGER
+			{ open_scope(s, 1);
+			  s->sy_name.nm_scope = CurrentScope;
+			  CurrentScope->sc_start = currnam->on_valu;
+			  CurrentScope->sc_proclevel = currnam->on_desc;
+			  add_scope_addr(CurrentScope);
+			}
+
+  | /* external procedure */
+			{ s = NewSymbol(str, PervasiveScope, PROC, currnam); }
+	'P' routine(s)
+
+  | /* private procedure */
+			{ s = NewSymbol(str, CurrentScope, PROC, currnam); }
+	'Q' routine(s)
+
+  | /* external function */
+			{ s = NewSymbol(str, PervasiveScope, FUNCTION, currnam); }
+	'F' function(s)
+
+  | /* private function */
+			{ s = NewSymbol(str, CurrentScope, FUNCTION, currnam); }
+	'f' function(s)
+
+  | /* global variable, external */
+				/* maybe we already know it; but we need
+				   the type information anyway for other
+				   types.
+				*/
+			{ s = Lookup(findidf(str), PervasiveScope, VAR);
+			  if (s) {
+				tmp = s->sy_type;
+			  } else s = NewSymbol(str, PervasiveScope, VAR, currnam);
+			}
+	'G' type(&(s->sy_type), (int *) 0)
+			{ if (tmp) s->sy_type = tmp; } 
+
+  | /* static variable */
+			{ s = NewSymbol(str, CurrentScope, VAR, currnam); }
+	'S' type(&(s->sy_type), (int *) 0)
+
+  | /* static variable, local scope */
+			{ s = NewSymbol(str, CurrentScope, VAR, currnam); }
+	'V' type(&(s->sy_type), (int *) 0)
+
+  | /* register variable */
+			{ s = NewSymbol(str, CurrentScope, REGVAR, currnam); }
+	'r' type(&(s->sy_type), (int *) 0)
+
+  | /* value parameter */
+			{ s = NewSymbol(str, CurrentScope, LOCVAR, currnam); }
+	'p' type(&(s->sy_type), (int *) 0)
+			{ add_param_type('p', s); }
+
+  | /* value parameter but address passed */
+			{ s = NewSymbol(str, CurrentScope, VARPAR, currnam); }
+	'i' type(&(s->sy_type), (int *) 0)
+			{ add_param_type('i', s); }
+
+  | /* variable parameter */
+			{ s = NewSymbol(str, CurrentScope, VARPAR, currnam); }
+	'v' type(&(s->sy_type), (int *) 0)
+			{ add_param_type('v', s); }
+
+  | /* local variable */
+			{ s = NewSymbol(str, CurrentScope, LOCVAR, currnam); }
+	type_name(&(s->sy_type))
+
+  | /* function result in Pascal; ignore ??? */
+			{ s = NewSymbol(str, CurrentScope, LOCVAR, currnam); }
+	'X' type_name(&(s->sy_type))
+  ]
+  ';'?
+;
+
+name(char **s;)
+:
+  /* anything up to a ':' */
+  NAME	{ *s = strval; }
+;
+
+const_name(p_symbol cst;)
+  { int type_index[2];
+    long iconst;
+    register char *p;
+  }
+:
+  '='
+  [
+	'b' integer_const(&(cst->sy_const.co_ival))	/* boolean */
+  |
+	'c' integer_const(&(cst->sy_const.co_ival))	/* character */
+				{ cst->sy_type = char_type; }
+  |
+	'i' integer_const(&(cst->sy_const.co_ival))	/* integer */
+				{ cst->sy_type = long_type; }
+  |
+  	'r' real_const(&(cst->sy_const.co_rval))	/* real */
+				{ cst->sy_type = double_type; }
+  |
+	's' string_const				/* string */
+				{ cst->sy_const.co_sval = string_val(strval);
+				  cst->sy_type = string_type;
+				}
+  |
+	'e' type_index(type_index) ',' integer_const(&(cst->sy_const.co_ival))
+				/* enumeration constant;
+				 * enumeration type, value
+				 */
+				{ cst->sy_type = *tp_lookup(type_index); }
+				
+  |
+	'S' type_index(type_index)
+				{ cst->sy_type = *tp_lookup(type_index);
+				  cst->sy_const.co_setval = p =
+				    Malloc((unsigned) cst->sy_type->ty_size);
+				}
+	[ ',' integer_const(&iconst)
+				{ *p++ = iconst; }
+	]+
+				/* set constant:
+				 *  settype, values of the bytes
+				 *  in the set.
+				 */
+  ]
+;
+
+integer_const(long *iconst;)
+  { int sign = 0; }
+:
+  [ '+' | '-' { sign = 1; } ]?
+  INTEGER			{ *iconst = sign ? -ival : ival; }
+;
+
+real_const(double *f;)
+  { int sign = 0; }
+:
+  [ '+' | '-' { sign = 1; } ]?
+  REAL				{ *f = sign ? fval : -fval; }
+;
+
+string_const
+:
+  STRING			/* has SINGLE quotes! */
+;
+
+type_name(p_type *t;)
+  { int type_index[2]; p_type *p; }
+:
+  type_index(type_index)
+  [
+	'='			
+	type(t, type_index)
+				{ p = tp_lookup(type_index);
+				  if (*p && *p != incomplete_type) {
+					if (!((*p)->ty_flags & T_CROSS))
+						error("Redefining (%d,%d) %d",
+						  type_index[0],
+						  type_index[1],
+						  (*p)->ty_class);
+					if (*t && *p != *t) free_type(*p);
+				  }
+				  if (*t) *p = *t; 
+				}
+  |
+				{ p = tp_lookup(type_index); }
+  ]
+				{ if (*p == 0) *p = incomplete_type;
+				  *t = *p;
+				}
+;
+
+type_index(int *type_index;)
+:
+[
+  INTEGER			{ type_index[0] = 0; type_index[1] = ival; }
+|
+  '(' INTEGER			{ type_index[0] = ival; }
+  ',' INTEGER			{ type_index[1] = ival; }
+  ')'
+]
+				{ last_index[0] = type_index[0];
+				  last_index[1] = type_index[1];
+				}
+;
+
+tag_name(p_symbol t;)
+  { int type_index[2]; p_type *p; }
+:
+  type_index(type_index)
+  '='				
+  type(&(t->sy_type), type_index)
+				{ p = tp_lookup(type_index);
+				  if (*p && *p != incomplete_type) {
+					if (!((*p)->ty_flags & T_CROSS))
+						error("Redefining (%d,%d) %d",
+						  type_index[0],
+						  type_index[1],
+						  (*p)->ty_class);
+					if (t->sy_type && *p != t->sy_type) {
+						free_type(*p);
+					}
+				  }
+				  if (t->sy_type) *p = t->sy_type; 
+				  if (*p == 0) *p = incomplete_type;
+				}
+;
+
+function(p_symbol p;)
+:
+  			{ p->sy_type = new_type();
+			  p->sy_type->ty_class = T_PROCEDURE;
+			  p->sy_type->ty_size = pointer_size;
+			}
+  type(&(p->sy_type->ty_retval), (int *) 0) 
+  			{ if (CurrentScope != FileScope &&
+			      saw_code) {
+				/* if saw_code is not set, it is a nested
+				   procedure
+				*/
+				close_scope();
+			  }
+			  saw_code = 0;
+			  open_scope(p, 1);
+			  p->sy_name.nm_scope = CurrentScope;
+			  CurrentScope->sc_start = currnam->on_valu;
+			  add_scope_addr(CurrentScope);
+			  CurrentScope->sc_proclevel = currnam->on_desc;
+			}
+;
+
+routine(p_symbol p;)
+:
+  			{ p->sy_type = new_type();
+			  p->sy_type->ty_class = T_PROCEDURE;
+			  p->sy_type->ty_size = pointer_size;
+			  if (CurrentScope != FileScope &&
+			      saw_code) {
+				/* if saw_code is not set, it is a nested
+				   procedure
+				*/
+				close_scope();
+			  }
+			  saw_code = 0;
+			  open_scope(p, 1);
+			  p->sy_name.nm_scope = CurrentScope;
+			  CurrentScope->sc_start = currnam->on_valu;
+			  add_scope_addr(CurrentScope);
+			  CurrentScope->sc_proclevel = currnam->on_desc;
+			}
+  INTEGER ';'
+  type(&(p->sy_type->ty_retval), (int *) 0) 
+;
+
+type(p_type *ptp; int *type_index;)
+  { register p_type tp = 0;
+    p_type t1, t2;
+    long ic1, ic2;
+    int A_used = 0;
+  }
+:			{ *ptp = 0; }
+  [
+	/* type cross reference */
+	/* these are used in C for references to a struct, union or
+	 * enum that has not been declared (yet)
+	 */
+  	'x'		{ tp = new_type(); tp->ty_flags = T_CROSS; }
+  	[ 's'	/* struct */
+			{ tp->ty_class = T_STRUCT; }
+  	| 'u'	/* union */
+			{ tp->ty_class = T_UNION; }
+  	| 'e'	/* enum */
+			{ tp->ty_class = T_ENUM; }
+  	]
+			{ AllowName = 1; }
+  	name(&(tp->ty_tag))
+  |
+  	/* subrange */
+  	/* the integer_const's represent the lower and the upper bound.
+   	 * A subrange type defined as subrange of itself is an integer type.
+   	 * If the second integer_const == 0, but the first is not, we
+   	 * have a floating point type with size equal to the first
+   	 * integer_const.
+   	 * Upperbound -1 means unsigned int or unsigned long.
+   	 */
+  	'r' type_name(&t1) ';'
+	[ 'A' integer_const(&ic1)	{ A_used = 1; }
+	| integer_const(&ic1)
+	]
+	';'
+	[ 'A' integer_const(&ic2)	{ A_used |= 2; }
+	| integer_const(&ic2)
+	]
+			{ *ptp = subrange_type(A_used,
+					       last_index,
+					       ic1,
+					       ic2,
+					       type_index);
+			}
+  |
+  	/* array; first type is bound type, next type
+   	 * is element type
+   	 */
+  	'a' type(&t1, (int *) 0) ';' type(&t2, (int *) 0)
+			{ *ptp = array_type(t1, t2); }
+  |
+  	/* structure type */
+  	's'		{ tp = new_type(); tp->ty_class = T_STRUCT; }
+	structure_type(tp)
+  |
+  	/* union type */
+  	'u'		{ tp = new_type(); tp->ty_class = T_UNION; }
+	structure_type(tp)
+  |
+  	/* enumeration type */
+  	'e'		{ tp = new_type(); tp->ty_class = T_ENUM; }
+	enum_type(tp)
+  |
+  	/* pointer type */
+  	'*'		{ tp = new_type(); tp->ty_class =T_POINTER;
+			  tp->ty_size = pointer_size;
+			}
+  	type(&(tp->ty_ptrto), (int *) 0)
+  |
+  	/* function type */
+  	'f'		{ tp = new_type(); tp->ty_class = T_PROCEDURE;
+			  tp->ty_size = pointer_size;
+			}
+  	type(&(tp->ty_retval), (int *) 0) 
+/*
+  	[ %prefer
+		',' param_list(tp)
+  	|
+  	]
+*/
+  |
+  	/* procedure type */
+  	'Q'		{ tp = new_type(); tp->ty_class = T_PROCEDURE;
+			  tp->ty_size = pointer_size;
+			}
+  	type(&(tp->ty_retval), (int *) 0) 
+	',' param_list(tp)
+  |
+  	/* another procedure type */
+  	'p'		{ tp = new_type(); tp->ty_class = T_PROCEDURE;
+			  tp->ty_size = pointer_size;
+			  tp->ty_retval = void_type;
+			}
+	param_list(tp)
+  |
+  	/* set type */
+  	/* the first integer_const represents the size in bytes,
+   	 * the second one represents the low bound
+   	 */
+  	'S'		{ tp = new_type(); tp->ty_class = T_SET; }
+	type(&(tp->ty_setbase), (int *) 0) ';'
+	[
+		integer_const(&(tp->ty_size)) ';'
+		integer_const(&(tp->ty_setlow)) ';'
+	|
+			{ set_bounds(tp); }
+	]
+  |
+	/* file type of Pascal */
+	'L'		{ tp = new_type(); tp->ty_class = T_FILE; }
+	type(&(tp->ty_fileof), (int *) 0)
+  |
+  	type_name(ptp)
+			{ if (type_index &&
+			      *ptp == incomplete_type &&
+			      type_index[0] == last_index[0] &&
+			      type_index[1] == last_index[1]) {
+				*ptp = void_type;
+			  }
+			}
+  ]
+			{ if (! *ptp) *ptp = tp; }
+;
+
+structure_type(register p_type tp;)
+  { register struct fields *fldp; }
+:
+  integer_const(&(tp->ty_size))		/* size in bytes */
+  [			{ fldp = get_field_space(tp); }
+	name(&(fldp->fld_name))
+	type(&(fldp->fld_type), (int *) 0) ','
+	integer_const(&(fldp->fld_pos)) ','	/* offset in bits */
+	integer_const(&(fldp->fld_bitsize)) ';'	/* size in bits */
+  ]*
+  ';'			{ end_field(tp); }
+;
+
+enum_type(register p_type tp;)
+  { register struct literal *litp;
+    long maxval = 0;
+  }
+:
+  [			{ litp = get_literal_space(tp);
+			}
+	name(&(litp->lit_name))
+	integer_const(&(litp->lit_val)) ',' 
+			{ if (maxval < litp->lit_val) maxval = litp->lit_val;
+			  AllowName = 1;
+			}
+  ]*
+  ';'			{ end_literal(tp, maxval); }
+;
+
+param_list(p_type t;)
+  { register struct param *p;
+    long iconst;
+  }
+:
+  integer_const(&iconst) ';'	/* number of parameters */
+			{ t->ty_nparams = iconst;
+			  t->ty_params = p = (struct param *)
+			    Malloc((unsigned)(t->ty_nparams * sizeof(struct param)));
+			}
+  [
+  	[	'p'	{ p->par_kind = 'p'; }
+  	|	'v'	{ p->par_kind = 'v'; }
+  	|	'i' 	{ p->par_kind = 'i'; }
+  	]
+  	type(&(p->par_type), (int *) 0) ';'
+			{ t->ty_nbparams += 
+				param_size(p->par_type, p->par_kind);
+			  p++;
+			}
+  ]*
+;
+
+{
+static char *dbx_string;
+static char *DbxOldPtr;
+
+struct outname *
+DbxString(n)
+  struct outname	*n;
+{
+  currnam = n;
+  DbxPtr = n->on_mptr;
+  dbx_string = DbxPtr;
+  AllowName = 1;
+  DbxParser();
+  return currnam;
+}
+
+/*ARGSUSED*/
+DBSmessage(n)
+{
+  fatal("error in Dbx string \"%s\", DbxPtr = \"%s\", DbxOldPtr = \"%s\"",
+	dbx_string,
+	DbxPtr,
+	DbxOldPtr);
+
+}
+
+DBSonerror(tk, p)
+  int	*p;
+{
+  DbxPtr = DbxOldPtr;
+/* ???  if (DBSsymb < 0) {
+	while (*p && *p != ';') p++;
+	if (*p) DbxPtr = ";";
+	return;
+  }
+*/
+  if (! tk) {
+	while (*p && *p != NAME) p++;
+	if (*p) {
+		AllowName = 1;
+	}
+  }
+  else if (tk == NAME) AllowName = 1;
+}
+
+DBSlex()
+{
+  register char *cp = DbxPtr;
+  int allow_name = AllowName;
+  register int c;
+
+  AllowName = 0;
+  DbxOldPtr = cp;
+  c = *cp;
+  if (c == '\\' && *(cp+1) == '\0') {
+	currnam++;
+	cp = currnam->on_mptr;
+	DbxOldPtr = cp;
+	c = *cp;
+  }
+  if (! c) {
+	DbxPtr = cp;
+	return -1;
+  }
+  if ((! allow_name && is_token(c)) || c == ';') {
+	DbxPtr = cp+1;
+	return c;
+  }
+  if (is_dig(c)) {
+	int retval = INTEGER;
+
+	while (++cp, is_dig(*cp)) /* nothing */;
+	c = *cp;
+	if (c == '.') {
+		retval = REAL;
+		while (++cp, is_dig(*cp)) /* nothing */;
+		c = *cp;
+	}
+	if (c == 'e' || c == 'E') {
+		char *oldcp = cp;
+
+		cp++;
+		c = *cp;
+		if (c == '-' || c == '+') {
+			cp++;
+			c = *cp;
+		}
+		if (is_dig(c)) {
+			retval = REAL;
+			while (++cp, is_dig(*cp)) /* nothing */;
+		}
+		else cp = oldcp;
+	}
+	c = *cp;
+	*cp = 0;
+	if (retval == INTEGER) {
+		ival = str2long(DbxOldPtr, 10);
+	}
+	else {
+		fval = atof(DbxOldPtr);
+	}
+	*cp = c;
+	DbxPtr = cp;
+	return retval;
+  }
+  if (c == '\'') {
+	cp++;
+	strval = cp;
+	while ((c = *cp) && c != '\'') {
+		if (c == '\\') cp++;	/* backslash escapes next character */
+		if (!(c =  *cp)) break;	/* but not a null byte */
+		cp++;
+	}
+	if (! c) DBSmessage(0);	/* no return */
+	*cp = 0;
+	DbxPtr = cp + 1;
+	return STRING;
+  }
+  strval = cp;
+  while ((c = *cp) && c != ':' && c != ',') cp++;
+  DbxPtr = *cp ? cp+1 : cp;
+  *cp = 0;
+  return NAME;
+}
+
+static struct fields *
+get_field_space(tp)
+  register p_type tp;
+{
+  if (! (tp->ty_nfields & 07)) {
+	tp->ty_fields = (struct fields *)
+		  Realloc((char *) tp->ty_fields,
+			    (tp->ty_nfields+8)*sizeof(struct fields));
+  }
+  return &tp->ty_fields[tp->ty_nfields++];
+}
+
+static
+end_field(tp)
+  register p_type tp;
+{
+  tp->ty_fields = (struct fields *)
+	Realloc((char *) tp->ty_fields,
+		tp->ty_nfields * sizeof(struct fields));
+}
+
+static struct literal *
+get_literal_space(tp)
+  register p_type tp;
+{
+  if (! (tp->ty_nenums & 07)) {
+	tp->ty_literals = (struct literal *)
+		Realloc((char *) tp->ty_literals,
+			(tp->ty_nenums+8)*sizeof(struct literal));
+  }
+  return &tp->ty_literals[tp->ty_nenums++];
+}
+
+static char *
+string_val(s)
+  char	*s;
+{
+  register char *ns = s, *os = s;
+  register unsigned int i = 1;
+
+  for (;;) {
+	if (!*os) break;
+	i++;
+	if (*os == '\\') {
+		os++;
+		*ns++ = *os++;
+	}
+	else *ns++ = *os++;
+  }
+  *ns = '\0';
+  return Salloc(s, i);
+}
+
+}

+ 194 - 0
util/grind/dbxread.c

@@ -0,0 +1,194 @@
+/* $Header$
+   Read the symbol table from an ACK a.out format file.
+*/
+
+#include <stb.h>
+#include <alloc.h>
+#include <assert.h>
+
+#include "position.h"
+#include "file.h"
+#include "symbol.h"
+#include "idf.h"
+#include "scope.h"
+#include "rd.h"
+
+extern char		*Malloc();
+extern char		*strindex();
+extern struct outname	*DbxString();
+
+int			saw_code = 0;
+
+static char		*AckStrings;	/* ACK a.out string table */
+static struct outname	*AckNames;	/* ACK a.out symbol table entries */
+static unsigned int	NAckNames;	/* Number of ACK symbol table entries */
+static struct outname	*EndAckNames;	/* &AckNames[NAckNames] */
+
+/* Read the symbol table from file 'f', which is supposed to be an
+   ACK a.out format file. Offer DBX strings to the DBX string parser.
+*/
+int
+DbxRead(f)
+  char	*f;
+{
+  struct outhead h;
+  register struct outname *n;
+  register struct outname *line_file = 0;
+  long OffsetStrings;
+  int had_lbrac = 0;
+
+  /* Open file, read header, and check magic word */
+  if (! rd_open(f)) {
+  	fatal("%s: not an ACK object file", f);
+  }
+  rd_ohead(&h);
+  if (BADMAGIC(h) && h.oh_magic != O_CONVERTED) {
+  	fatal("%s: not an ACK object file", f);
+  }
+
+  /* Allocate space for name table and read it */
+  AckNames = (struct outname *) 
+  		Malloc((unsigned)(sizeof(struct outname) * h.oh_nname));
+  AckStrings = Malloc((unsigned) h.oh_nchar);
+  rd_name(AckNames, h.oh_nname);
+  rd_string(AckStrings, h.oh_nchar);
+
+  /* Adjust file offsets in name table to point at strings */
+  OffsetStrings = OFF_CHAR(h);
+  NAckNames = h.oh_nname;
+  EndAckNames = &AckNames[h.oh_nname];
+  for (n = EndAckNames; --n >= AckNames;) {
+	if (n->on_foff) {
+		if ((unsigned)(n->on_foff - OffsetStrings) >= h.oh_nchar) {
+			fatal("%s: error in object file", f);
+		}
+		n->on_mptr = AckStrings + (n->on_foff - OffsetStrings);
+	}
+	else	n->on_mptr = 0;
+  }
+
+  /* Offer strings to the DBX string parser if they contain a ':'.
+     Also offer filename-line number information to add_position_addr().
+     Here, the order may be important.
+  */
+  for (n = &AckNames[0]; n < EndAckNames; n++) {
+	int tp = n->on_type >> 8;
+	register p_symbol sym;
+
+	if (tp & (S_STB >> 8)) {
+		switch(tp) {
+#ifdef N_BINCL
+		case N_BINCL:
+			n->on_valu = (long) line_file;
+			line_file = n;
+			break;
+		case N_EINCL:
+			if (line_file) {
+				line_file = (struct outname *) line_file->on_valu;
+			}
+			break;
+#endif
+		case N_SO:
+			if (n->on_mptr[strlen(n->on_mptr)-1] == '/') {
+				/* another N_SO follows ... */
+				break;
+			}
+			while (CurrentScope != PervasiveScope) {
+				close_scope();
+			}
+			saw_code = 0;
+			sym = add_file(n->on_mptr);
+
+			if (! currfile) newfile(sym->sy_idf);
+			open_scope(sym, 0);
+			sym->sy_file->f_scope = CurrentScope;
+			FileScope = CurrentScope;
+			clean_tp_tab();
+			/* fall through */
+		case N_SOL:
+			if (! line_file) line_file = n;
+			else line_file->on_mptr = n->on_mptr;
+			break;
+		case N_MAIN:
+			newfile(FileScope->sc_definedby->sy_idf);
+			break;
+		case N_SLINE:
+			assert(line_file);
+			if (! saw_code && !CurrentScope->sc_bp_opp) {
+			    CurrentScope->sc_bp_opp = n->on_valu;
+			    if (! CurrentScope->sc_start) {
+				CurrentScope->sc_start = n->on_valu;
+				if (CurrentScope->sc_has_activation_record) {
+					add_scope_addr(CurrentScope);
+				}
+			    }
+			}
+			saw_code = 1;
+			add_position_addr(line_file->on_mptr, n);
+			break;
+		case N_LBRAC:	/* block, desc = nesting level */
+			if (had_lbrac) {
+				open_scope((p_symbol) 0, 0);
+				saw_code = 0;
+			}
+			else {
+				register p_scope sc = 
+					get_scope_from_addr(n->on_valu);
+
+				if (!sc || sc->sc_bp_opp) {
+					had_lbrac = 1;
+				}
+				else CurrentScope = sc;
+			}
+			break;
+#ifdef N_SCOPE
+		case N_SCOPE:
+			if (n->on_mptr && strindex(n->on_mptr, ':')) {
+				n = DbxString(n);
+			}
+			break;
+#endif
+		case N_RBRAC:	/* end block, desc = nesting level */
+			had_lbrac = 0;
+			if (CurrentScope != FileScope) close_scope();
+			saw_code = 0;
+			break;
+		case N_FUN:	/* function, value = address */
+		case N_GSYM:	/* global variable */
+		case N_STSYM:	/* data, static, value = address */
+		case N_LCSYM:	/* bss, static, value = address */
+		case N_RSYM:	/* register var, value = reg number */
+		case N_SSYM:	/* struct/union el, value = offset */
+		case N_PSYM:	/* parameter, value = offset from AP */
+		case N_LSYM:	/* local sym, value = offset from FP */
+			if (had_lbrac) {
+				open_scope((p_symbol) 0, 0);
+				saw_code = 0;
+				had_lbrac = 0;
+			}
+			if (n->on_mptr && strindex(n->on_mptr, ':')) {
+				n = DbxString(n);
+			}
+			break;
+		default:
+/*
+			if (n->on_mptr && (n->on_type&S_TYP) >= S_MIN) {
+				struct idf *id = str2idf(n->on_mptr, 0);
+
+				sym = new_symbol();
+				sym->sy_next = id->id_def;
+				id->id_def = sym;
+				sym->sy_class = SYMENTRY;
+				sym->sy_onam = *n;
+				sym->sy_idf = id;
+			}
+*/
+			break;
+		}
+	}
+  }
+  close_scope();
+  add_position_addr((char *) 0, (struct outname *) 0);
+  rd_close();
+  return (h.oh_magic == O_CONVERTED);
+}

+ 65 - 0
util/grind/dump.c

@@ -0,0 +1,65 @@
+/* $Header$ */
+
+#include <assert.h>
+#include <alloc.h>
+
+#include "operator.h"
+#include "position.h"
+#include "tree.h"
+#include "message.h"
+
+extern long	pointer_size;
+extern p_tree	get_from_item_list();
+
+struct dump {
+  char	*globals, *stack;
+  struct message_hdr mglobal, mstack;
+};
+
+/* dumping and restoring of child process.
+*/
+do_dump(p)
+  p_tree	p;
+{
+  struct dump *d = (struct dump *) Malloc(sizeof(struct dump));
+
+  if (! get_dump(&d->mglobal, &d->globals, &d->mstack, &d->stack)) {
+	error("no debuggee");
+	free((char *) d);
+	return;
+  }
+  p->t_args[0] = (struct tree *) d;
+  p->t_address = (t_addr) BUFTOA(d->mglobal.m_buf+PC_OFF*pointer_size);
+  add_to_item_list(p);
+}
+
+/* dumping and restoring of child process.
+*/
+do_restore(p)
+  p_tree	p;
+{
+  struct dump *d;
+  
+  p = get_from_item_list((int) p->t_ival);
+  if (!p || p->t_oper != OP_DUMP) {
+	error("no such dump");
+	return;
+  }
+
+  d = (struct dump *) p->t_args[0];
+
+  if (! put_dump(&d->mglobal, d->globals, &d->mstack, d->stack)) {
+	error("no debuggee");
+  }
+  do_items();
+}
+
+free_dump(p)
+  p_tree	p;
+{
+  struct dump *d = (struct dump *) p->t_args[0];
+
+  free(d->globals);
+  free(d->stack);
+  free((char *) d);
+}

+ 13 - 0
util/grind/expr.c

@@ -0,0 +1,13 @@
+/* $Header$ */
+
+#include "position.h"
+#include "operator.h"
+#include "tree.h"
+
+int
+eval_cond(p)
+  p_tree	p;
+{
+  /* to be written !!! */
+  return 1;
+}

+ 38 - 0
util/grind/file.hh

@@ -0,0 +1,38 @@
+/* $Header$ */
+
+/* Structure for information about files. This information consists of three
+   parts:
+   - file name and directory
+   - mapping of line numbers to offsets in file
+   - mapping of object adresses to lines in file and vice versa
+*/
+
+#define LOGHSIZ		6		/* make sure HSIZ is a power of 2 */
+#define HSIZ		(1 << LOGHSIZ)
+#define	HASH(line)	((line) & (HSIZ-1))
+
+typedef struct file {
+	struct symbol	*f_sym;
+	char		*f_fullname;	/* name including directory */
+	struct scope	*f_scope;	/* reference to scope of this file */
+	t_lineno	f_nlines;	/* number of lines in file */
+	union {
+	  long		*ff_linepos;	/* positions of lines in file */
+	  struct file	*ff_next;	/* only for BINCL, EINCL */
+	} f_x;
+#define f_linepos	f_x.ff_linepos
+#define f_next		f_x.ff_next
+	struct outname	*f_start;
+	struct outname	*f_end;
+	struct outname	*f_line_addr[HSIZ];
+					/* hash table, mapping line numbers to
+					   outname structures. Collisions are
+					   resolved by chaining:
+					*/
+#define next_outname(n)		((struct outname *) ((n)->on_mptr))
+#define setnext_outname(n,m)	((n)->on_mptr = (char *) (m))
+
+	struct file	*f_nextmap;	/* next file in mapping */
+} t_file, *p_file;
+
+/* ALLOCDEF "file" 10 */

+ 15 - 0
util/grind/idf.c

@@ -0,0 +1,15 @@
+/*
+ * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands.
+ * See the copyright notice in the ACK home directory, in the file "Copyright".
+ *
+ * Author: Ceriel J.H. Jacobs
+ */
+
+/* I N S T A N T I A T I O N   O F   I D F   P A C K A G E */
+
+/* $Header$ */
+
+#include	"position.h"
+#include	"file.h"
+#include	"idf.h"
+#include	<idf_pkg.body>

+ 21 - 0
util/grind/idf.h

@@ -0,0 +1,21 @@
+/*
+ * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands.
+ * See the copyright notice in the ACK home directory, in the file "Copyright".
+ *
+ * Author: Ceriel J.H. Jacobs
+ */
+
+/* U S E R   D E C L A R E D   P A R T   O F   I D F */
+
+/* $Header$ */
+
+struct id_u {
+	int id_res;
+	struct symbol *id_df;
+};
+
+#define IDF_TYPE	struct id_u
+#define id_reserved	id_user.id_res
+#define id_def		id_user.id_df
+
+#include	<idf_pkg.spec>

+ 163 - 0
util/grind/itemlist.cc

@@ -0,0 +1,163 @@
+/* $Header$ */
+
+#include <alloc.h>
+#include <stdio.h>
+#include <assert.h>
+
+#include "position.h"
+#include "tree.h"
+#include "operator.h"
+
+extern FILE	*db_out;
+extern int	db_ss;
+
+typedef struct item {
+  struct item		*i_next;
+  struct tree		*i_node;
+} t_item, *p_item;
+
+/* STATICALLOCDEF "item" 10 */
+
+struct itemlist {
+  p_item	il_first, il_last;
+  int		il_count;
+};
+
+static struct itemlist	item_list;
+
+int
+in_item_list(p)
+  p_tree	p;
+{
+  register p_item i = item_list.il_first;
+
+  while (i) {
+	if (i->i_node == p) return 1;
+	i = i->i_next;
+  }
+  return 0;
+}
+
+int
+item_addr_actions(a)
+  t_addr	a;
+{
+  /* Perform actions associated with position 'a', and return 1 if we must stop
+     there, and 0 if not.
+  */
+  register p_item i = item_list.il_first;
+  int stopping = 0;
+
+  while (i) {
+	register p_tree	p = i->i_node;
+
+	if (p->t_address == a || p->t_address == NO_ADDR) {
+		switch(p->t_oper) {
+		case OP_TRACE:
+		case OP_WHEN:
+			if (! p->t_args[1] ||
+			    eval_cond(p->t_args[1])) {
+				perform(p, a);
+			}
+			break;
+		case OP_STOP:
+			if (! p->t_args[1] ||
+			    eval_cond(p->t_args[1])) stopping = 1;
+			break;
+		case OP_DUMP:
+			break;
+		default:
+			assert(0);
+		}
+	}
+	i = i->i_next;
+  }
+  return stopping;
+}
+
+add_to_item_list(p)
+  p_tree	p;
+{
+  p_item i;
+  
+  if (in_item_list(p)) return 1;
+
+  i = new_item();
+  i->i_node = p;
+  if (p->t_address == NO_ADDR &&
+      (p->t_oper != OP_TRACE || ! p->t_args[0])) db_ss++;
+  if (item_list.il_first == 0) {
+	item_list.il_first = i;
+  }
+  else {
+	item_list.il_last->i_next = i;
+  }
+  p->t_itemno = ++item_list.il_count;
+  item_list.il_last = i;
+  pr_item(p);
+  return 1;
+}
+
+p_tree
+remove_from_item_list(n)
+  int	n;
+{
+  register p_item i = item_list.il_first, prev = 0;
+  p_tree	p = 0;
+
+  while (i) {
+	if (i->i_node->t_itemno == n) break;
+	prev = i;
+	i = i->i_next;
+  }
+  if (i) {
+	if (prev) {
+		prev->i_next = i->i_next;
+	}
+	else item_list.il_first = i->i_next;
+	if (i == item_list.il_last) item_list.il_last = prev;
+	p = i->i_node;
+	if (p->t_address == NO_ADDR &&
+	    (p->t_oper != OP_TRACE || ! p->t_args[0])) db_ss--;
+	free_item(i);
+  }
+  return p;
+}
+
+p_tree
+get_from_item_list(n)
+  int	n;
+{
+  register p_item i = item_list.il_first;
+
+  while (i) {
+	if (i->i_node->t_itemno == n) return i->i_node;
+	i = i->i_next;
+  }
+  return 0;
+}
+
+print_items()
+{
+  register p_item i = item_list.il_first;
+
+  for (; i; i = i->i_next) {
+	pr_item(i->i_node);
+  }
+}
+
+pr_item(p)
+  p_tree	p;
+{
+  fprintf(db_out, "(%d)\t", p->t_itemno);
+  print_node(p, 1);
+}
+
+do_items()
+{
+  register p_item i = item_list.il_first;
+
+  for (; i; i = i->i_next) {
+	if (i->i_node->t_oper != OP_DUMP) eval(i->i_node);
+  }
+}

+ 47 - 0
util/grind/langdep.cc

@@ -0,0 +1,47 @@
+/* $Header$ */
+
+#include "langdep.h"
+
+struct langlist {
+  struct langlist	*l_next;
+  struct langdep	*l_lang;
+  char			*l_suff;
+};
+
+/* STATICALLOCDEF "langlist" 5 */
+
+static struct langlist *list;
+
+struct langdep	*currlang;
+
+static int
+add_language(suff, lang)
+  char	*suff;
+  struct langdep *lang;
+{
+  struct langlist *p = new_langlist();
+
+  p->l_next = list;
+  p->l_suff = suff;
+  p->l_lang = lang;
+  list = p;
+}
+
+int
+init_languages()
+{
+  add_language(".mod", m2_dep);
+}
+
+int
+find_language(suff)
+  char	*suff;
+{
+  register struct langlist *p = list;
+
+  while (p) {
+	currlang = p->l_lang;
+	if (! strcmp(p->l_suff, suff)) break;
+	p = p->l_next;
+  }
+}

+ 32 - 0
util/grind/langdep.h

@@ -0,0 +1,32 @@
+/* $Header$ */
+
+/* language-dependent routines and formats, together in one structure: */
+
+struct langdep {
+  /* formats (for fprintf): */
+  char	*decint_fmt;		/* decimal ints (format for long) */
+  char	*octint_fmt;		/* octal ints (format for long) */
+  char	*hexint_fmt;		/* hexadecimal ints (format for long) */
+  char	*uns_fmt;		/* unsigneds (format for long) */
+  char	*addr_fmt;		/* address (format for long) */
+  char	*real_fmt;		/* real (format for double) */
+  char	*char_fmt;		/* character (format for int) */
+
+  /* display openers and closers: */
+  char	*open_array_display;
+  char	*close_array_display;
+  char	*open_struct_display;
+  char	*close_struct_display;
+  char	*open_set_display;
+  char	*close_set_display;
+
+  /* language dependant routines: */
+  int	(*printstring)();
+  long	(*arrayelsize)();
+};
+
+extern struct langdep	*m2_dep, *currlang;
+
+extern int find_language();
+
+extern int init_languages();

+ 146 - 0
util/grind/list.c

@@ -0,0 +1,146 @@
+/* $Header$ */
+
+#include <stdio.h>
+#include <alloc.h>
+
+#include "position.h"
+#include "idf.h"
+#include "file.h"
+#include "symbol.h"
+
+static	line_positions();
+extern char	*dirs[];
+extern FILE	*fopen();
+extern FILE	*db_out;
+#define	window_size	21
+
+static int
+mk_filnm(dir, file, newname)
+  char	*dir;
+  char	*file;
+  char	**newname;
+{
+  register char	*dst = Malloc((unsigned) (strlen(dir) + strlen(file) + 2));
+
+  *newname = dst;
+  if (*dir) {
+	while (*dst++ = *dir++) /* nothing */;
+	*(dst - 1) = '/';
+  }
+  while (*dst++ = *file++) /* nothing */;
+}
+
+static FILE *
+open_file(fn, mode, ffn)
+  char	*fn;
+  char	*mode;
+  char	**ffn;
+{
+  FILE	*f;
+  char	**p;
+
+  if (fn[0] == '/') {
+	*ffn = fn;
+	return fopen(fn, mode);
+  }
+  p = dirs;
+  while (*p) {	
+	mk_filnm(*p++, fn, ffn);
+	if ((f = fopen(*ffn, mode)) != NULL) {
+		return f;
+	}
+	free(*ffn);
+  }
+  return NULL;
+}
+
+/*	Print a window of window_size lines around line "line" of
+	file "file".
+*/
+window(file, line)
+  p_file	file;
+  int		line;
+{
+  lines(file,
+	line + ((window_size >> 1) - window_size), line + (window_size >> 1));
+}
+
+lines(file, l1, l2)
+  register p_file file;
+  int		l1, l2;
+{
+  static p_file last_file;
+  static FILE *last_f;
+  register FILE	*f;
+  register int	n;
+
+  if (last_file != file) {
+	if (last_f) fclose(last_f);
+	last_f = 0;
+  	if (!(f = open_file(file->f_sym->sy_idf->id_text, 
+			    "r",
+			    &file->f_fullname))) {
+		error("could not open %s", file->f_sym->sy_idf->id_text);
+		return;
+	}
+	printf("filedesc = %d\n", fileno(f));
+	last_file = file;
+	last_f = f;
+	if (! file->f_linepos) {
+		line_positions(file, f);
+	}
+  }
+  else f = last_f;
+
+  if (l1 < 1) l1 = 1;
+  if (l2 > file->f_nlines) l2 = file->f_nlines;
+  if (l1 > l2) {
+	error("%s has only %d lines", file->f_sym->sy_idf->id_text, file->f_nlines);
+	return;
+  }
+
+  fseek(f, *(file->f_linepos+(l1-1)), 0);
+  for (n = l1; n <= l2; n++) {
+	register int	c;
+
+	fprintf(db_out, "%6d  ", n);
+	do {
+		c = getc(f);
+		if (c != EOF) putc(c, db_out);
+	} while (c != '\n' && c != EOF);
+	if (c == EOF) break;
+  }
+  clearerr(f);
+}
+
+static
+line_positions(file, f)
+  p_file	file;
+  register FILE	*f;
+{
+  int		nl;
+  unsigned int	n_alloc = 256;
+  register long	cnt = 0;
+  register int	c;
+
+  file->f_linepos = (long *) Malloc(n_alloc * sizeof(long));
+  file->f_linepos[0] = 0;
+  nl = 1;
+  while ((c = getc(f)) != EOF) {
+	cnt++;
+	if (c == '\n') {
+		if (nl == n_alloc) {
+			n_alloc <<= 1;
+			file->f_linepos =
+				(long *) Realloc((char *)(file->f_linepos),
+						 n_alloc * sizeof(long));
+		}
+		file->f_linepos[nl++] = cnt;
+	}
+  }
+  if (cnt == file->f_linepos[nl-1]) nl--;
+  file->f_linepos = (long *) Realloc((char *)(file->f_linepos),
+					(unsigned)nl * sizeof(long));
+  file->f_nlines = nl;
+  clearerr(f);
+}

+ 121 - 0
util/grind/main.c

@@ -0,0 +1,121 @@
+#include <stdio.h>
+#include <varargs.h>
+
+#include "tokenname.h"
+#include "position.h"
+#include "file.h"
+#include "symbol.h"
+#include "scope.h"
+
+static char	*usage = "Usage: %s [-d] [<ack.out>] [<a.out>]";
+static char	*progname;
+char		*AckObj;
+char		*AObj;
+char		*dirs[] = { "", 0 };
+FILE		*db_out;
+FILE		*db_in;
+t_lineno	currline;
+int		debug;
+extern struct tokenname tkidf[];
+extern char	*strindex();
+
+main(argc, argv)
+  char	*argv[];
+{
+  char	*p;
+
+  db_out = stdout;
+  db_in = stdin;
+  progname = argv[0];
+  while (p = strindex(progname, '/')) {
+	progname = p + 1;
+  }
+  if (argv[1][0] == '-') {
+	switch(argv[1][1]) {
+	case 'd':
+		debug++;
+		break;
+	default:
+		fatal(usage, progname);
+	}
+	argv++;
+	argc--;
+  }
+  if (argc > 3) {
+	fatal(usage, progname);
+  }
+  AckObj = argv[1] ? argv[1] : "a.out";
+  if (argc == 3) AObj = argv[2];
+  init_idf();
+  init_types();
+  init_scope();
+  init_languages();
+  if (DbxRead(AckObj) && AObj == 0) AObj = AckObj;
+  else if (AObj == 0) AObj = "a.out";
+  reserve(tkidf);
+  if (currfile) CurrentScope = currfile->sy_file->f_scope;
+  if (! init_run()) {
+	fatal("something wrong with file descriptors");
+  }
+  prompt();
+  Commands();
+  fputc( '\n', db_out);
+  exit(0);
+}
+
+prompt()
+{
+  if (isatty(fileno(db_in))) {
+	fprintf(db_out, "%s -> ", progname);
+	fflush(db_out);
+  }
+}
+
+/*VARARGS1*/
+fatal(va_alist)
+  va_dcl
+{
+  va_list ap;
+  char *fmt;
+
+  va_start(ap);
+  {
+	fmt = va_arg(ap, char *);
+	fprintf(stderr, "%s: ", progname);
+	vfprintf(stderr, fmt, ap);
+	fprintf(stderr, "\n");
+  }
+  va_end(ap);
+  abort();
+  exit(1);
+}
+
+extern int errorgiven;
+
+/*VARARGS1*/
+error(va_alist)
+  va_dcl
+{
+  va_list ap;
+  char *fmt;
+
+  va_start(ap);
+  {
+	fmt = va_arg(ap, char *);
+	fprintf(stderr, "%s: ", progname);
+	vfprintf(stderr, fmt, ap);
+	fprintf(stderr, "\n");
+  }
+  va_end(ap);
+  errorgiven = 1;
+}
+
+rd_fatal()
+{
+  fatal("read error in %s", AckObj);
+}
+
+No_Mem()
+{
+  fatal("out of memory");
+}

+ 26 - 0
util/grind/make.allocd

@@ -0,0 +1,26 @@
+sed -e '
+s:^.*[ 	]ALLOCDEF[ 	].*"\(.*\)"[ 	]*\([0-9][0-9]*\).*$:\
+/* allocation definitions of struct \1 */\
+extern char *st_alloc();\
+extern struct \1 *h_\1;\
+#ifdef DEBUG\
+extern int cnt_\1;\
+extern char *std_alloc();\
+#define	new_\1() ((struct \1 *) std_alloc((char **)\&h_\1, sizeof(struct \1), \2, \&cnt_\1))\
+#else\
+#define	new_\1() ((struct \1 *) st_alloc((char **)\&h_\1, sizeof(struct \1), \2))\
+#endif\
+#define	free_\1(p) st_free(p, \&h_\1, sizeof(struct \1))\
+:' -e '
+s:^.*[ 	]STATICALLOCDEF[ 	].*"\(.*\)"[ 	]*\([0-9][0-9]*\).*$:\
+/* allocation definitions of struct \1 */\
+extern char *st_alloc();\
+struct \1 *h_\1;\
+#ifdef DEBUG\
+int cnt_\1;\
+#define	new_\1() ((struct \1 *) std_alloc((char **)\&h_\1, sizeof(struct \1), \2, \&cnt_\1))\
+#else\
+#define	new_\1() ((struct \1 *) st_alloc((char **)\&h_\1, sizeof(struct \1), \2))\
+#endif\
+#define	free_\1(p) st_free(p, \&h_\1, sizeof(struct \1))\
+:'

+ 6 - 0
util/grind/make.next

@@ -0,0 +1,6 @@
+sed -n '
+s:^.*[ 	]ALLOCDEF[ 	].*"\(.*\)".*$:struct \1 *h_\1 = 0;\
+#ifdef DEBUG\
+int cnt_\1 = 0;\
+#endif:p
+' $*

+ 18 - 0
util/grind/make.ops

@@ -0,0 +1,18 @@
+awk '
+BEGIN	{ n = 0 }
+	{ print "#define " $1 " " n; n++
+	  if ($3 !~ /0/) print "extern int " $3 "();" ;
+	}
+' < $1 > ops.h
+
+cat > ops.c <<'EOF'
+#include "operator.h"
+#include "ops.h"
+
+t_operator operators[] = {
+EOF
+awk '	{ print "{ " $2 ", " $3 "}, /* " $1 " */" }' < $1 >> ops.c
+cat >> ops.c <<'EOF'
+{ 0, 0 }
+};
+EOF

+ 36 - 0
util/grind/make.tokcase

@@ -0,0 +1,36 @@
+cat <<'--EOT--'
+/* Generated by make.tokcase */
+/* $Header$ */
+#include "Lpars.h"
+
+char *
+symbol2str(tok)
+	int tok;
+{
+#define SIZBUF	8
+	/* allow for a few invocations in f.i. an argument list */
+	static char buf[SIZBUF] = { '\'', 0, '\'', 0, '\'', 0, '\'', 0};
+	static int index = 1;
+
+	switch (tok) {
+--EOT--
+
+sed '
+/{[A-Z]/!d
+s/.*{\(.*\),.*\(".*"\).*$/	case \1 :\
+		return \2;/
+'
+
+cat <<'--EOT--'
+	default:
+		if (tok <= 0) return "end of file";
+		if (tok == '\n') return "<newline>";
+		if (tok < 040 || tok >= 0177) {
+			return "bad token";
+		}
+		index = (index+4) & (SIZBUF-1);
+		buf[index] = tok;
+		return &buf[index-1];
+	}
+}
+--EOT--

+ 6 - 0
util/grind/make.tokfile

@@ -0,0 +1,6 @@
+sed '
+/{[A-Z]/!d
+s/.*{//
+s/,.*//
+s/.*/%token	&;/
+'

+ 59 - 0
util/grind/message.h

@@ -0,0 +1,59 @@
+/* $Header$ */
+
+#define BUFLEN	24	/* size of buffer in message header */
+
+struct message_hdr {
+  int	m_type;
+/* Possible values of m_type: */
+#define DB_RUN	020000	/* set for commands that cause child to run */
+#define	SETBP	 0	/* set breakpoint at address in m_size */
+#define	CLRBP	 1	/* clear breakpoint at address in m_size */
+#define	SETSS	 (2|DB_RUN)	/* set single stepping, # of steps in m_size */
+#define SETSSF	 (3|DB_RUN)	/* set single stepping, counting calls as one step */
+#define	GETEMREGS 4	/* get EM registers, m_size contains level */
+#define	GETBYTES 5	/* get data; m_size contains size, m_buf contains address */
+#define GETSTR	 6	/* get string; m_buf contains address */
+#define SETBYTES 7	/* set data; m_buf contains address, m_size contains size */
+#define CALL	 8	/* call function; 
+			   m_size contains size of parameter buffer,
+			   m_buf contains address + size of function result
+			*/
+#define CONT	 (9|DB_RUN)	/* continue */
+#define	SETEMREGS 10	/* set EM registers, m_size contains level
+			   Actually, only the program counter is set.
+			*/
+#define DB_SS	040000	/* debugger wants single stepping (to be orred with
+			   SETSS(F) or CONT
+			*/
+#define CLRSS	12	/* clear single stepping */
+#define DUMP	13	/* dump command */
+#define DGLOB	14	/* data area */
+#define DSTACK	15	/* stack area */
+#define SETTRACE 16	/* start tracing; range in m_mes */
+#define CLRTRACE 17	/* end tracing */
+
+#define	OK	50	/* answer of child to most messages */
+#define FAIL	51	/* answer of child when something goes wrong */
+#define DATA	52	/* answer of child when data requested */
+#define END_SS	53	/* when stopped because of user single stepping */
+  long	m_size;		/* size */
+  char	m_buf[BUFLEN];	/* some of the data required included in message */
+};
+
+#define	LB_OFF	0
+#define AB_OFF	1
+#define PC_OFF	2
+#define HP_OFF	3
+#define SP_OFF	4
+
+#define IN_FD	3
+#define OUT_FD	6
+
+#define BUFTOL(c)	(*((long *) (c)))
+#define LTOBUF(c,l)	(*((long *) (c)) = (l))
+#define BUFTOA(c)	(*((char **) (c)))
+#define ATOBUF(c,p)	(*((char **) (c)) = (p))
+#define BUFTOS(c)	(*((short *) (c)))
+#define BUFTOI(c)	(*((int *) (c)))
+#define BUFTOF(c)	(*((float *) (c)))
+#define BUFTOD(c)	(*((double *) (c)))

+ 61 - 0
util/grind/modula-2.c

@@ -0,0 +1,61 @@
+/* $Header$ */
+
+/* Language dependant support; this one is for Modula-2 */
+
+#include <stdio.h>
+
+#include "langdep.h"
+
+extern FILE *db_out;
+
+static int
+	print_string();
+
+static long
+	array_elsize();
+
+static struct langdep m2 = {
+	"%ld",
+	"%loB",
+	"%lXH",
+	"%lu",
+	"%lXH",
+	"%g",
+	"%oC",
+
+	"[",
+	"]",
+	"(",
+	")",
+	"{",
+	"}",
+
+	print_string,
+	array_elsize
+};
+
+struct langdep *m2_dep = &m2;
+
+static int
+print_string(s)
+  char	*s;
+{
+  register char	*str = s;
+  int delim = '\'';
+
+  while (*str) {
+	if (*str++ == '\'') delim = '"';
+  }
+  fprintf(db_out, "%c%s%c", delim, s, delim);
+}
+
+extern long	int_size;
+
+static long
+array_elsize(size)
+  long	size;
+{
+  if (! (int_size % size)) return size;
+  if (! (size % int_size)) return size;
+  return ((size + int_size - 1) / int_size) * int_size;
+}

+ 14 - 0
util/grind/op_tools.amk

@@ -0,0 +1,14 @@
+MAKE_OPS = make.ops;
+
+%instance deftypesuffix(op_tab, '%.ot');
+
+%tool gen_ops (
+    ops:	%in  [type = op_tab];
+    cfile:	%out [type = C-src]	=> ops.c;
+    hfile:	%out [type = C-incl]	=> ops.h;
+    mkops:	%in  [type = command]	=> $MAKE_OPS;
+)
+{
+    exec($mkops, args => $ops);
+    echo({$cfile, 'and', $hfile, 'created'});
+};

+ 12 - 0
util/grind/operator.h

@@ -0,0 +1,12 @@
+/* $Header$ */
+
+#include "ops.h"
+
+typedef struct operator {
+	int	op_nargs;
+	int	(*op_fun)();
+} t_operator, *p_operator;
+
+extern t_operator operators[];
+
+#define nargs(n)	(operators[(n)].op_nargs)

+ 24 - 0
util/grind/operators.ot

@@ -0,0 +1,24 @@
+OP_LIST		2	do_list
+OP_FILE		1	do_file
+OP_LINK		2	0
+OP_RUN		1	start_child
+OP_INPUT	1	0
+OP_OUTPUT	1	0
+OP_INTEGER	0	0
+OP_NAME		0	0
+OP_AT		0	0
+OP_IN		1	0
+OP_STOP		2	do_stop
+OP_WHEN		3	do_stop
+OP_CONT		2	do_continue
+OP_STEP		0	do_step
+OP_NEXT		0	do_next
+OP_REGS		0	do_regs
+OP_WHERE	0	do_where
+OP_STATUS	0	do_status
+OP_DELETE	0	do_delete
+OP_SELECT	2	0
+OP_PRINT	1	do_print
+OP_DUMP		0	do_dump
+OP_RESTORE	0	do_restore
+OP_TRACE	3	do_trace

+ 196 - 0
util/grind/position.c

@@ -0,0 +1,196 @@
+/* $Header$ */
+
+#include	<stdio.h>
+#include	<assert.h>
+#include	<alloc.h>
+#include	<out.h>
+#include	<stb.h>
+
+#include	"position.h"
+#include	"scope.h"
+#include	"file.h"
+#include	"idf.h"
+#include	"symbol.h"
+
+extern FILE	*db_out;
+
+static p_file	mapping;
+static int	nfiles = 0;
+
+/* static p_file get_map_from_addr(t_addr t);
+   Returns the file entry that contains the code at the address 't',
+   or 0 if there is no information available, or 't' represents an address
+   below the start address of the first file.
+*/
+static p_file
+get_map_from_addr(t)
+  t_addr t;
+{
+  register p_file p = mapping, oldp = 0;
+
+  /* linear search is probably acceptable here */
+  while (p && p->f_start->on_valu <= t) {
+	oldp = p;
+	p = p->f_nextmap;
+  }
+  return oldp ? oldp : p->f_start->on_valu <= t ? p : 0;
+}
+
+/* extern char *get_filename_from_addr(t_addr t);
+   Returns the source filename that contains the code at the address 't',
+   or 0 if there is no information available, or 't' represents an address
+   below the start address of the first file.
+*/
+char *
+get_filename_from_addr(t)
+  t_addr t;
+{
+  register p_file map = get_map_from_addr(t);
+
+  if (! map) return 0;
+  return map->f_sym->sy_idf->id_text;
+}
+
+/* extern t_lineno get_lineno_from_addr(t_addr t);
+   Returns the source line number of the line that contains the code at address
+   't'.  0 is returned if no source line number could be found.
+*/
+t_lineno
+get_lineno_from_addr(t)
+  t_addr t;
+{
+  p_position p;
+
+  p = get_position_from_addr(t);
+  return p == 0 ? 0 : p->lineno;
+}
+
+/* extern p_position get_position_from_addr(t_addr t);
+   Returns a pointer to a structure containing the source position of the code
+   at address 't'.  0 is returned if no source position could be found.
+*/
+p_position
+get_position_from_addr(t)
+  t_addr t;
+{
+  register p_file map = get_map_from_addr(t);
+  static t_position retval;
+  register int i,j,m;
+
+  if (! map) return 0;
+  i = 0;
+  j = map->f_end - map->f_start;
+  do {
+	m = ((i + j) >> 1) + ((i + j) & 1);
+	while ((map->f_start[m].on_type >> 8) != N_SLINE) m++;
+	assert(m <= j);
+	if (map->f_start[m].on_valu > t) {
+		j = m - 1;
+		while (j > i && (map->f_start[j].on_type >> 8) != N_SLINE) j--;
+	}
+	else	i = m;
+  } while (i < j);
+  retval.filename = map->f_sym->sy_idf->id_text;
+  retval.lineno = map->f_start[j].on_desc;
+  return &retval;
+}
+
+/* extern t_addr get_addr_from_position(p_position p);
+   Returns the address of the code at position 'p', or ILL_ADDR if it could
+   not be found. If there is no symbolic information for the filename in
+   position 'p', an error message will be given.
+*/
+t_addr
+get_addr_from_position(p)
+  p_position p;
+{
+  register p_symbol sym = Lookup(findidf(p->filename), PervasiveScope, FILESYM);
+
+  if (sym) {
+	register unsigned int i;
+	register p_file map = sym->sy_file;
+
+	for (i = p->lineno; i > 0; i--) {
+		register struct outname *n = map->f_line_addr[HASH(i)];
+
+		while (n) {
+			if (n->on_desc == i) return (t_addr) n->on_valu;
+			n = next_outname(n);
+		}
+	}
+	return ILL_ADDR;
+  }
+  error("no symbolic information for file %s", p->filename);
+  return ILL_ADDR;
+}
+
+/* extern add_position_addr(char *filename, struct outname *n);
+   Adds the ('filename','lineno'),'t' pair to the mapping information.
+*/
+add_position_addr(filename, n)
+  char *filename;
+  register struct outname *n;
+{
+  static char *lastfile = 0;
+  static p_file lastmap = 0;
+  register p_file map = lastmap;
+
+  if (filename != lastfile) {	/* new file ... */
+	register p_symbol sym;
+
+	nfiles++;
+	lastfile = filename;
+	if (! filename) {	/* last call */
+		return;
+	}
+	sym = Lookup(findidf(filename), PervasiveScope, FILESYM);
+	if (sym) map = sym->sy_file; 
+	else {
+		sym = add_file(filename);
+		map = sym->sy_file;
+		map->f_scope = FileScope;
+	}
+	if (! mapping) mapping = map;
+	else lastmap->f_nextmap = map;
+	lastmap = map;
+	map->f_start = n;
+  }
+  else map = lastmap;
+  map->f_end = n;
+  setnext_outname(n, map->f_line_addr[HASH(n->on_desc)]);
+  map->f_line_addr[HASH(n->on_desc)] = n;
+}
+
+/* extern struct scope  *get_scope_from_position(p_position p);
+   Returns the scope of the code at position 'p', or 0 if it could not be found.
+*/
+struct scope *
+get_scope_from_position(p)
+  p_position p;
+{
+  t_addr a = get_addr_from_position(p);
+
+  if (a != ILL_ADDR) {
+	return get_scope_from_addr(a);
+  }
+  return 0;
+}
+
+/* extern p_position print_position(t_addr a, int print_function);
+   Prints position 'a' and returns it. If 'print_function' is set,
+   an attempt is made to print the function name as well.
+*/
+p_position
+print_position(a, print_function)
+  t_addr	a;
+  int		print_function;
+{
+  register p_scope	sc = base_scope(get_scope_from_addr(a));
+  register p_position	pos = get_position_from_addr(a);
+
+  if (sc && print_function) {
+	fprintf(db_out, "in %s ", sc->sc_definedby->sy_idf->id_text);
+  }
+  if (pos) fprintf(db_out, "at \"%s\":%u", pos->filename, pos->lineno);
+  return pos;
+}

+ 57 - 0
util/grind/position.h

@@ -0,0 +1,57 @@
+/* $Header$ */
+
+/* maps from address to filename-lineno pair and reverse,
+   maps from filename-lineno pair or address to scope.
+*/
+
+typedef unsigned int	t_lineno;
+typedef long		t_addr;
+#define ILL_ADDR	((t_addr) -1)
+#define NO_ADDR		((t_addr) -3)
+
+typedef struct pos {
+  t_lineno	lineno;
+  char		*filename;
+} t_position, *p_position;
+
+/* extern char *get_filename_from_addr(t_addr t);
+   Returns the source filename that contains the code at the address 't',
+   or 0 if there is no information available, or 't' represents an address
+   below the start address of the first file.
+*/
+extern char		*get_filename_from_addr();
+
+/* extern t_lineno	get_lineno_from_addr(t_addr t);
+   Returns the source line number of the line that contains the code at address
+   't'.  0 is returned if no source line number could be found.
+*/
+extern t_lineno		get_lineno_from_addr();
+
+/* extern p_position	get_position_from_addr(t_addr t);
+   Returns a pointer to a structure containing the source position of the code
+   at address 't'.  0 is returned if no source position could be found.
+*/
+extern p_position	get_position_from_addr();
+
+/* extern t_addr	get_addr_from_position(p_position p);
+   Returns the address of the code at position 'p', or ILL_ADDR if it could
+   not be found. If there is no symbolic information for the filename in
+   position 'p', an error message will be given.
+*/
+extern t_addr		get_addr_from_position();
+
+/* extern	add_position_addr(char *filename, struct outname *n);
+   Adds the ('filename','n'->on_desc),'n'->on_valu pair to the mapping information.
+*/
+extern 			add_position_addr();
+
+/* extern struct scope	*get_scope_from_position(p_position p);
+   Returns the scope of the code at position 'p', or 0 if it could not be found.
+*/
+extern struct scope	*get_scope_from_position();
+
+/* extern p_position print_position(t_addr a, int print_function);
+   Prints position 'a' and returns it. If 'print_function' is set,
+   an attempt is made to print the function name as well.
+*/
+extern p_position	print_position();

+ 322 - 0
util/grind/print.c

@@ -0,0 +1,322 @@
+/* $Header$ */
+
+#include <alloc.h>
+#include <assert.h>
+#include <stdio.h>
+
+#include "type.h"
+#include "message.h"
+#include "langdep.h"
+#include "scope.h"
+#include "symbol.h"
+#include "position.h"
+#include "idf.h"
+
+extern FILE *db_out;
+extern long float_size, pointer_size, int_size;
+
+static
+print_literal(tp, v)
+  p_type	tp;
+  int		v;
+{
+  register struct literal *lit = tp->ty_literals;
+  register int i;
+
+  for (i = tp->ty_nenums; i; i--, lit++) {
+	if (lit->lit_val == v) {
+		fprintf(db_out, lit->lit_name);
+		break;
+	}
+  }
+  if (! i) {
+	fprintf(db_out, "unknown enumeration value %d", v);
+  }
+}
+
+static
+print_unsigned(tp, v)
+  p_type	tp;
+  long		v;
+{
+  if (tp == uchar_type) {
+	fprintf(db_out, currlang->char_fmt, (int) v);
+  }
+  else	fprintf(db_out, currlang->uns_fmt, v);
+}
+
+static
+print_integer(tp, v)
+  p_type	tp;
+  long		v;
+{
+  if (tp == char_type) {
+	fprintf(db_out, currlang->char_fmt, (int) v);
+  }
+  else	fprintf(db_out, currlang->decint_fmt, v);
+}
+
+print_params(tp, AB, static_link)
+  p_type	tp;
+  t_addr	AB;
+{
+  char *param_bytes;
+  register char *p;
+  register int i;
+  register struct param *par;
+  long size;
+
+  if (! tp) return;
+  assert(tp->ty_class == T_PROCEDURE);
+
+  if ((i = tp->ty_nparams) == 0) return;
+
+  /* get parameter bytes */
+  par = tp->ty_params;
+  size = tp->ty_nbparams;
+  if (static_link) size += pointer_size;
+  param_bytes = p = Malloc((unsigned)size);
+  if (static_link) p += pointer_size;
+  if (! get_bytes(size, AB, param_bytes)) {
+	error("no debuggee");
+	free(param_bytes);
+	return;
+  }
+
+  while (i--) {
+	if (par->par_kind == 'v' || par->par_kind == 'i') {
+		/* call by reference parameter, or
+		   call by value parameter, but address is passed;
+		   try and get value.
+		*/
+		char	*q;
+
+		if ((size = par->par_type->ty_size) == 0) {
+			size = compute_size(par->par_type, param_bytes);
+		}
+		q = Malloc((unsigned) size);
+		if (! get_bytes(size, (t_addr) BUFTOA(p), q)) {
+			fprintf(db_out, currlang->addr_fmt, BUFTOA(p));
+		}
+		else {
+			print_val(par->par_type, q, 1, 0, param_bytes);
+		}
+		free(q);
+	}
+	else print_val(par->par_type, p, 1, 0, param_bytes);
+	if (i) fputs(", ", db_out);
+	p += param_size(par->par_type, par->par_kind);
+	par++;
+  }
+  free(param_bytes);
+}
+
+print_val(tp, addr, compressed, indent, AB)
+  p_type	tp;		/* type of value to be printed */
+  char		*addr;		/* address to get value from */
+  int		compressed;	/* for parameter lists */
+  int		indent;		/* indentation */
+  char		*AB;		/* argument base for dynamic subranges */
+{
+  long sz;
+  register int i;
+  long elsize;
+
+  if (indent == 0) indent = 4;
+  switch(tp->ty_class) {
+  case T_SUBRANGE:
+	print_val(tp->ty_base, addr, compressed, indent, AB);
+	break;
+  case T_ARRAY:
+	if (tp->ty_elements == char_type ||
+	    tp->ty_elements == uchar_type) {
+		print_val(string_type, addr, compressed, indent, AB);
+		break;
+	}
+	if ((sz = tp->ty_size) == 0) sz = compute_size(tp, AB);
+	if (compressed) {
+		fprintf(db_out, currlang->open_array_display);
+	}
+	else {
+		fprintf(db_out, "\n%*c%s%*c",
+			indent,
+			' ',
+			currlang->open_array_display,
+			4-strlen(currlang->open_array_display), ' ');
+	}
+	indent += 4;
+	elsize = (*currlang->arrayelsize)(tp->ty_elements->ty_size);
+	for (i = sz/elsize; i; i--) {
+		print_val(tp->ty_elements, addr, compressed, indent, AB);
+		addr += elsize;
+		if (compressed && i > 1) {
+			fprintf(db_out, ", ...");
+			break;
+		} 
+		if (i > 1) {
+			fputc(',', db_out);
+		}
+		fprintf(db_out, "\n%*c", i > 1 ? indent : indent - 4, ' ');
+	}
+	fprintf(db_out, currlang->close_array_display);
+	indent -= 4;
+	break;
+  case T_STRUCT: {
+	register struct fields *fld = tp->ty_fields;
+
+	if (compressed) {
+		fprintf(db_out, currlang->open_struct_display);
+	}
+	else {
+		fprintf(db_out, "\n%*c%s%*c",
+			indent,
+			' ',
+			currlang->open_struct_display,
+			4-strlen(currlang->open_struct_display), ' ');
+	}
+	indent += 4;
+	for (i = tp->ty_nfields; i; i--, fld++) {
+		if (! compressed) fprintf(db_out, "%s = ", fld->fld_name);
+		if (fld->fld_bitsize != fld->fld_type->ty_size << 3) {
+			/* apparently a bit field */
+			/* ??? */
+			fprintf(db_out, "<bitfield, %d, %d>", fld->fld_bitsize, fld->fld_type->ty_size);
+		}
+		else print_val(fld->fld_type, addr+(fld->fld_pos>>3), compressed, indent, AB);
+		if (compressed && i > 1) {
+			fprintf(db_out, ", ...");
+			break;
+		} 
+		if (i > 1) {
+			fputc(',', db_out);
+		}
+		fprintf(db_out, "\n%*c", i > 1 ? indent : indent - 4, ' ');
+	}
+	indent -= 4;
+	fprintf(db_out, currlang->close_struct_display);
+	break;
+	}
+  case T_UNION:
+	fprintf(db_out, "<union>");
+	break;
+  case T_ENUM:
+	print_literal(tp,  tp->ty_size == 1 
+			   ? (*addr & 0xFF)
+			   : tp->ty_size == 2
+			      ? (BUFTOS(addr) & 0xFFFF)
+			      : (int) BUFTOL(addr));
+	break;
+  case T_PROCEDURE: {
+	register p_scope sc = get_scope_from_addr((t_addr) BUFTOA(addr));
+
+	if (sc && sc->sc_definedby) {
+		fprintf(db_out, sc->sc_definedby->sy_idf->id_text);
+		break;
+	}
+	}
+	/* Fall through */
+  case T_POINTER:
+	fprintf(db_out, currlang->addr_fmt, (long) BUFTOA(addr));
+	break;
+  case T_FILE:
+	fprintf(db_out, "<file>");
+	break;
+  case T_SET: {
+	long	val = tp->ty_setlow;
+	p_type	base = tp->ty_setbase;
+	long	nelements = tp->ty_size << 3;
+	int	count = 0;
+	int	rsft = 3 + (int_size == 2 ? 1 : 2);
+	long	mask = int_size == 2 ? 0xFFFF : 0xFFFFFFFF;
+
+	if (base->ty_class == T_SUBRANGE) base = base->ty_base;
+	if (compressed) {
+		fprintf(db_out, currlang->open_set_display);
+	}
+	else {
+		fprintf(db_out, "\n%*c%s%*c",
+			indent,
+			' ',
+			currlang->open_set_display,
+			4-strlen(currlang->open_set_display), ' ');
+	}
+	indent += 4;
+	for (i = 0; i < nelements; i++) {
+		if (*((int *) addr + (i >> rsft)) & (1 << (i & mask))) {
+			count++;
+			if (count > 1) {
+				if (compressed) {
+					fprintf(db_out, ", ...");
+					break;
+				}
+				fprintf(db_out, ",\n%*c", indent , ' ');
+			}
+			switch(base->ty_class) {
+			case T_INTEGER:
+				print_integer(base, val+i);
+				break;
+			case T_UNSIGNED:
+				print_unsigned(base, val+i);
+				break;
+			case T_ENUM:
+				print_literal(base, (int)val+i);
+				break;
+			default:
+				assert(0);
+			}
+		} 
+	}
+	if (! compressed) {
+		fprintf(db_out, "\n%*c", indent-4 , ' ');
+	}
+	indent -= 4;
+	fprintf(db_out, currlang->close_set_display);
+  	}
+	break;
+  case T_REAL: {
+	double val = tp->ty_size == float_size
+		? BUFTOF(addr)
+		: BUFTOD(addr);
+	fprintf(db_out, currlang->real_fmt, val);
+	break;
+	}
+  case T_UNSIGNED:
+	print_unsigned(tp, tp->ty_size == 1 
+				? (*addr & 0xFF)
+				: tp->ty_size == 2
+			  	    ? (BUFTOS(addr) & 0xFFFF)
+			  	    : BUFTOL(addr));
+	break;
+  case T_INTEGER:
+	print_integer(tp, tp->ty_size == 1 
+				? *addr
+				: tp->ty_size == 2
+			  	    ? BUFTOS(addr)
+			  	    : BUFTOL(addr));
+	break;
+  case T_STRING:
+	(*currlang->printstring)(addr);
+	break;
+  default:
+	assert(0);
+	break;
+  }
+}
+
+int
+print_sym(sym)
+  p_symbol	sym;
+{
+  char		*buf;
+  char		*AB;
+
+  if (get_value(sym, &buf, &AB)) {
+	fputs(" = ", db_out);
+	print_val(sym->sy_type, buf, 0, 0, AB);
+	if (buf) free(buf);
+	if (AB) free(AB);
+	fputs("\n", db_out);
+	return 1;
+  }
+  return 0;
+}

+ 134 - 0
util/grind/rd.c

@@ -0,0 +1,134 @@
+/* $Header$ */
+
+/* a.out file reading ... */
+
+#include "rd.h"
+
+#if defined(sun) && defined(mc68020)
+
+#include <a.out.h>
+#include <stdio.h>
+
+static FILE *inf;
+static struct exec bh;
+static long seg_strings;
+static struct outhead hh;
+
+#define readf(a, b, c)	(fread((char *)(a), (b), (int)(c), inf))
+
+int
+rd_open(f)
+  char	*f;
+{
+  if ((inf = fopen(f, "r")) == NULL) return 0;
+  return 1;
+}
+
+rd_ohead(h)
+  struct outhead	*h;
+{
+  if (! readf(&bh, sizeof(struct exec), 1)) rd_fatal();
+  if (N_BADMAG(bh)) rd_fatal();
+
+  h->oh_magic = O_CONVERTED;
+  h->oh_stamp = 0;
+  h->oh_nsect = 4;
+  h->oh_nname = 3 + bh.a_syms / sizeof(struct nlist);
+  h->oh_nrelo = (bh.a_trsize + bh.a_drsize) / sizeof(struct reloc_info_68k);
+  h->oh_flags = h->oh_nrelo ? HF_LINK : 0;
+  if (bh.a_magic == ZMAGIC) bh.a_text -= sizeof(struct exec);
+  h->oh_nemit = bh.a_text + bh.a_data;
+  if (bh.a_magic == ZMAGIC) bh.a_text += sizeof(struct exec);
+  fseek(inf, N_STROFF(bh), 0);
+  h->oh_nchar = getw(inf) + 6 + 6 + 5 - 4; /* ".text", ".data", ".bss",
+					      minus the size word */
+  seg_strings = h->oh_nchar - 17;
+  if (bh.a_magic == ZMAGIC) bh.a_text -= sizeof(struct exec);
+  fseek(inf, sizeof(struct exec) + bh.a_text + bh.a_data, 0);
+  hh = *h;
+}
+
+/*ARGSUSED1*/
+rd_name(names, count)
+  register struct outname	*names;
+  unsigned int		count;	/* ignored; complete namelist is read */
+{
+  names->on_valu = 0; names->on_foff = seg_strings + OFF_CHAR(hh);
+  names->on_desc = 0; names->on_type = S_MIN | S_SCT;
+  names++;
+  names->on_valu = 0; names->on_foff = seg_strings + OFF_CHAR(hh) + 6;
+  names->on_desc = 0; names->on_type = (S_MIN+2) | S_SCT;
+  names++;
+  names->on_valu = 0; names->on_foff = seg_strings + OFF_CHAR(hh) + 12;
+  names->on_desc = 0; names->on_type = (S_MIN+3) | S_SCT;
+  names++;
+  count = bh.a_syms / sizeof(struct nlist);
+  while (count > 0) {
+	struct nlist n;
+
+	if (! readf(&n, sizeof(struct nlist), 1)) rd_fatal();
+	count--;
+	names->on_desc = n.n_desc;
+	if (n.n_un.n_strx - 4 < 0) names->on_foff = 0;
+	else names->on_foff = OFF_CHAR(hh) - 4 + n.n_un.n_strx;
+	names->on_valu = n.n_value;
+
+	if (n.n_type & N_STAB) {
+		names->on_type = n.n_type << 8;
+		names++;
+		continue;
+	}
+	switch(n.n_type & ~N_EXT) {
+	case N_ABS:
+		names->on_type = S_ABS;
+		break;
+	case N_TEXT:
+		names->on_type = S_MIN;
+		break;
+	case N_DATA:
+		names->on_type = S_MIN + 2;
+		names->on_valu -= bh.a_text;
+		break;
+	case N_BSS:
+		names->on_type = S_MIN + 3;
+		names->on_valu -= bh.a_text + bh.a_data;
+		break;
+	case N_UNDF:
+		if (! names->on_valu) {
+			names->on_type = S_UND;
+			break;
+		}
+		names->on_type = (S_MIN + 3) | S_COM;
+		break;
+	case N_FN:
+		names->on_type = S_FIL;
+		break;
+	default:
+		rd_fatal();
+	}
+	if (n.n_type & N_EXT) names->on_type |= S_EXT;
+	names++;
+  }
+}
+
+extern char	*strcpy();
+
+rd_string(strings, count)
+  register char	*strings;
+  long	count;
+{
+  if (bh.a_magic == ZMAGIC) bh.a_text += sizeof(struct exec);
+  fseek(inf, N_STROFF(bh)+4, 0);
+  if (! readf(strings, (int)count-17, 1)) rd_fatal();
+  strings += count-17;
+  strcpy(strings, ".text"); strings += 6;
+  strcpy(strings, ".data"); strings += 6;
+  strcpy(strings, ".bss");
+}
+
+rd_close()
+{
+  fclose(inf);
+}
+
+#endif

+ 5 - 0
util/grind/rd.h

@@ -0,0 +1,5 @@
+/* $Header$ */
+
+#include	<out.h>
+
+#define O_CONVERTED	0x202

+ 523 - 0
util/grind/run.c

@@ -0,0 +1,523 @@
+/* $Header$ */
+
+/* Running a process and communication */
+
+#include <signal.h>
+#include <stdio.h>
+#include <assert.h>
+#include <alloc.h>
+
+#include "ops.h"
+#include "message.h"
+#include "position.h"
+#include "tree.h"
+#include "file.h"
+#include "symbol.h"
+#include "idf.h"
+#include "scope.h"
+
+#define MAXARG	128
+
+extern char	*strncpy();
+extern char	*AObj;
+extern t_lineno	currline;
+extern FILE	*db_out;
+extern int	debug;
+extern struct idf *str2idf();
+extern long	pointer_size;
+
+static int	child_pid;		/* process id of child */
+static int	to_child, from_child;	/* file descriptors for communication */
+static int	child_status;
+static int	restoring;
+
+int		db_ss;
+
+static int	catch_sigpipe();
+static int	stopped();
+static int	uputm(), ugetm();
+static int	fild1[2], fild2[2];	/* pipe file descriptors */
+
+int
+init_run()
+{
+  /* take file descriptors so that listing cannot take them */
+  int i;
+
+  for (i = IN_FD; i <= OUT_FD; i++) close(i);
+  if (pipe(fild1) < 0 ||
+      pipe(fild2) < 0 ||
+      fild1[0] != IN_FD ||
+      fild2[1] != OUT_FD) {
+	return 0;
+  }
+  to_child = fild1[1];
+  from_child = fild2[0];
+  return 1;
+}
+
+int
+start_child(p)
+  p_tree	p;
+{
+  /* start up the process to be debugged and set up communication */
+
+  char *argp[MAXARG];				/* argument list */
+  register p_tree pt = p->t_args[0], pt1;
+  unsigned int	nargs = 1;			/* #args */
+  char	*in_redirect = 0;			/* standard input redirected */
+  char	*out_redirect = 0;			/* standard output redirected */
+
+  signal_child(SIGKILL); /* like families in China, this debugger is only
+			    allowed one child
+			 */
+
+  /* first check arguments and redirections and build argument list */
+  while (pt) {
+  	switch(pt->t_oper) {
+	case OP_LINK:
+		pt1 = pt->t_args[1];
+		pt = pt->t_args[0];
+		continue;
+	case OP_NAME:
+		if (nargs < (MAXARG-1)) {
+			argp[nargs++] = pt->t_str;
+		}
+		else {
+			error("too many arguments");
+			return 0;
+		}
+		break;
+	case OP_INPUT:
+		if (in_redirect) {
+			error("input redirected twice?");
+			return 0;
+		}
+		in_redirect = pt->t_str;
+		break;
+	case OP_OUTPUT:
+		if (out_redirect) {
+			error("output redirected twice?");
+			return 0;
+		}
+		out_redirect = pt->t_str;
+		break;
+  	}
+	if (pt != pt1) pt = pt1;
+	else break;
+  }
+  argp[0] = AObj;
+  argp[nargs] = 0;
+
+  /* create child process */
+  child_pid = fork();
+  if (child_pid < 0) {
+	error("could not create child");
+	return 0;
+  }
+  if (child_pid == 0) {
+	/* this is the child process */
+	close(fild1[1]);
+	close(fild2[0]);
+
+	signal(SIGINT, SIG_IGN);
+
+	/* I/O redirection */
+	if (in_redirect) {
+		int fd;
+		close(0);
+		if ((fd = open(in_redirect, 0)) < 0) {
+			error("could not open input file");
+			exit(-1);
+		}
+		if (fd != 0) {
+			dup2(fd, 0);
+			close(fd);
+		}
+	}
+	if (out_redirect) {
+		int fd;
+		close(1);
+		if ((fd = creat(in_redirect, 0666)) < 0) {
+			error("could not open output file");
+			exit(-1);
+		}
+		if (fd != 1) {
+			dup2(fd, 1);
+			close(fd);
+		}
+	}
+
+	/* and run process to be debugged */
+	execv(AObj, argp);
+	error("could not exec %s", AObj);
+	exit(-1);
+  }
+
+  /* debugger */
+  close(fild1[0]);
+  close(fild2[1]);
+
+  pipe(fild1);		/* to occupy file descriptors */
+  signal(SIGPIPE, catch_sigpipe);
+  if (! wait_for_child((char *) 0)) {
+	error("child not responding");
+	return 0;
+  }
+  do_items();
+  if (! restoring) send_cont(1);
+  return 1;
+}
+
+int
+wait_for_child(s)
+  char	*s;		/* to pass on to 'stopped' */
+{
+  struct message_hdr m;
+
+  if (child_pid) {
+  	if (ugetm(&m)) {
+		return stopped(s, (t_addr) m.m_size);
+  	}
+  	return 0;
+  }
+  return 1;
+}
+
+signal_child(sig)
+{
+  if (child_pid) {
+	kill(child_pid, sig);
+	if (sig == SIGKILL) {
+		wait(&child_status);
+		init_run();
+	}
+  }
+}
+
+static int
+catch_sigpipe()
+{
+  child_pid = 0;
+}
+
+
+static int
+ureceive(p, c)
+  char	*p;
+  long	c;
+{
+  int	i;
+
+  if (! child_pid) return 0;
+
+  while (c >= 0x1000) {
+	i = read(from_child, p, 0x1000);
+	if (i <= 0) {
+		if (i == 0) child_pid = 0;
+		return 0;
+	}
+	p += i;
+	c -= i;
+  }
+  while (c > 0) {
+	i = read(from_child, p, (int)c);
+	if (i <= 0) {
+		if (i == 0) child_pid = 0;
+		return 0;
+	}
+	p += i;
+	c -= i;
+  }
+  return c == 0;
+}
+
+static int
+usend(p, c)
+  char	*p;
+  long	c;
+{
+  int	i;
+
+  while (c >= 0x1000) {
+	i = write(to_child, p, 0x1000);
+	if (i < 0) return 0;
+	p += i;
+	c -= i;
+  }
+  while (c > 0) {
+	i = write(to_child, p, (int)c);
+	if (i < 0) return 0;
+	p += i;
+	c -= i;
+  }
+  return 1;
+}
+
+static int
+ugetm(message)
+  struct message_hdr *message;
+{
+  if (! ureceive((char *) message, (long) sizeof(struct message_hdr))) {
+  	return 0;
+  }
+  if (debug) printf("Got %d\n", message->m_type);
+  return 1;
+}
+
+static int
+uputm(message)
+  struct message_hdr *message;
+{
+  if (! usend((char *) message, (long) sizeof(struct message_hdr))) {
+  	return 0;
+  }
+  if (debug) printf("Sent %d\n", message->m_type);
+  return 1;
+}
+
+static struct message_hdr	answer;
+static int	single_stepping;
+
+static int
+stopped(s, a)
+  char	*s;	/* stop message */
+  t_addr a;	/* address where stopped */
+{
+  p_position pos;
+
+  if (s) {
+	fprintf(db_out, "%s ", s);
+	pos = print_position((t_addr) a, 1);
+	newfile(str2idf(pos->filename, 1));
+	currline = pos->lineno;
+	fputs("\n", db_out);
+	lines(currfile->sy_file, (int)currline, (int)currline);
+  }
+  return 1;
+}
+
+static int
+could_send(m, stop_message)
+  struct message_hdr	*m;
+{
+  int	type;
+  t_addr a;
+  for (;;) {
+  	if (child_pid) {
+		if (! uputm(m) ||
+		    ! ugetm(&answer)) {
+			if (child_pid) {
+				error("something wrong!");
+				return 1;
+			}
+			wait(&child_status);
+			init_run();
+			if (child_status & 0177) {
+				fprintf(db_out,
+					"Child died with signal %d\n",
+					child_status & 0177);
+			}
+			else {
+				fprintf(db_out,
+					"Child terminated, exit status %d\n",
+					child_status >> 8);
+			}
+			return 1;
+		}
+		a = answer.m_size;
+		type = answer.m_type;
+		if (m->m_type & DB_RUN) {
+			/* run command */
+			CurrentScope = get_scope_from_addr((t_addr) a);
+		    	if (! item_addr_actions(a) &&
+		            ( type == DB_SS || type == OK)) {
+				/* no explicit breakpoints at this position.
+				   Also, child did not stop because of
+				   SETSS or SETSSF, otherwise we would
+				   have gotten END_SS.
+				   So, continue.
+				*/
+				if ((m->m_type & ~ DB_SS) != CONT) {
+					m->m_type = CONT | (m->m_type & DB_SS);
+				}
+				continue;
+			}
+			if (type != END_SS && single_stepping) {
+				m->m_type = CLRSS;
+				uputm(m) && ugetm(&answer);
+			}
+			single_stepping = 0;
+		}
+		if (stop_message) stopped("stopped", a);
+		return 1;
+	}
+	return 0;
+  }
+  /*NOTREACHED*/
+}
+
+int
+get_bytes(size, from, to)
+  long	size;
+  t_addr from;
+  char	*to;
+{
+  struct message_hdr	m;
+
+  m.m_type = GETBYTES;
+  m.m_size = size;
+  ATOBUF(m.m_buf, (char *) from);
+
+  if (! could_send(&m, 0)) {
+	return 0;
+  }
+
+  assert(answer.m_type == DATA && answer.m_size == m.m_size);
+
+  return ureceive(to, answer.m_size);
+}
+
+int
+get_dump(globmessage, globbuf, stackmessage, stackbuf)
+  struct message_hdr *globmessage, *stackmessage;
+  char **globbuf, **stackbuf;
+{
+  struct message_hdr	m;
+
+  m.m_type = DUMP;
+  if (! could_send(&m, 0)) {
+	return 0;
+  }
+  assert(answer.m_type == DGLOB);
+  *globmessage = answer;
+  *globbuf = Malloc((unsigned) answer.m_size);
+  if (! ureceive(*globbuf, answer.m_size) || ! ugetm(stackmessage)) {
+	free(*globbuf);
+	return 0;
+  }
+  assert(stackmessage->m_type == DSTACK);
+  *stackbuf = Malloc((unsigned) stackmessage->m_size);
+  if (! ureceive(*stackbuf, stackmessage->m_size)) {
+	free(*globbuf);
+	free(*stackbuf);
+	return 0;
+  }
+  ATOBUF(globmessage->m_buf+SP_OFF*pointer_size,
+	 BUFTOA(stackmessage->m_buf+SP_OFF*pointer_size));
+  return 1;
+}
+
+int
+put_dump(globmessage, globbuf, stackmessage, stackbuf)
+  struct message_hdr *globmessage, *stackmessage;
+  char *globbuf, *stackbuf;
+{
+  struct message_hdr m;
+
+  if (! child_pid) {
+	restoring = 1;
+	start_child(run_command);
+	restoring = 0;
+  }
+  return	uputm(globmessage) &&
+		usend(globbuf, globmessage->m_size) &&
+		uputm(stackmessage) &&
+		usend(stackbuf, stackmessage->m_size) &&
+		ugetm(&m) && stopped("restored", m.m_size);
+}
+
+t_addr *
+get_EM_regs(level)
+  int	level;
+{
+  struct message_hdr	m;
+  static t_addr buf[5];
+  register t_addr *to = &buf[0];
+
+  m.m_type = GETEMREGS;
+  m.m_size = level;
+
+  if (! could_send(&m, 0)) {
+	return 0;
+  }
+  *to++ = (t_addr) BUFTOA(answer.m_buf);
+  *to++ = (t_addr) BUFTOA(answer.m_buf+pointer_size);
+  *to++ = (t_addr) BUFTOA(answer.m_buf+2*pointer_size);
+  *to++ = (t_addr) BUFTOA(answer.m_buf+3*pointer_size);
+  *to++ = (t_addr) BUFTOA(answer.m_buf+4*pointer_size);
+  return buf;
+}
+
+int
+set_pc(PC)
+  t_addr	PC;
+{
+  struct message_hdr	m;
+
+  m.m_type = SETEMREGS;
+  m.m_size = 0;
+  ATOBUF(m.m_buf+PC_OFF*pointer_size, (char *)PC);
+  return could_send(&m, 0);
+}
+
+int
+send_cont(stop_message)
+  int	stop_message;
+{
+  struct message_hdr	m;
+
+  m.m_type = (CONT | (db_ss ? DB_SS : 0));
+  m.m_size = 0;
+  return could_send(&m, stop_message);
+}
+
+int
+do_single_step(type, count)
+  int	type;
+  long	count;
+{
+  struct message_hdr	m;
+
+  m.m_type = type | (db_ss ? DB_SS : 0);
+  m.m_size = count;
+  single_stepping = 1;
+  if (could_send(&m, 1)) {
+	return 1;
+  }
+  single_stepping = 0;
+  return 0;
+}
+
+int
+set_or_clear_breakpoint(a, type)
+  t_addr	a;
+  int	type;
+{
+  struct message_hdr m;
+
+  if (a == ILL_ADDR || a == NO_ADDR) return 0;
+
+  m.m_type = type;
+  m.m_size = a;
+  if (debug) printf("%s breakpoint at 0x%lx\n", type == SETBP ? "setting" : "clearing", (long) a);
+  if (! could_send(&m, 0)) { }
+
+  return 1;
+}
+
+int
+set_or_clear_trace(start, end, type)
+  t_addr start, end;
+  int	type;
+{
+  struct message_hdr m;
+
+  m.m_type = type;
+  ATOBUF(m.m_buf, (char *) start);
+  ATOBUF(m.m_buf+pointer_size, (char *) end);
+  if (debug) printf("%s trace at [0x%lx,0x%lx]\n", type == SETTRACE ? "setting" : "clearing", (long) start, (long) end);
+  if (! could_send(&m, 0)) { }
+
+  return 1;
+}

+ 131 - 0
util/grind/scope.cc

@@ -0,0 +1,131 @@
+/* Scope mechanism */
+
+/* $Header$ */
+
+#include	<assert.h>
+#include	<alloc.h>
+#include	<out.h>
+
+#include	"position.h"
+#include	"file.h"
+#include	"idf.h"
+#include	"type.h"
+#include	"symbol.h"
+#include	"scope.h"
+#include	"avl.h"
+
+p_scope PervasiveScope, CurrentScope, FileScope;
+
+/* STATICALLOCDEF "scope" 10 */
+
+static AVL_tree	ScopeTree;
+
+static int
+cmp_starts(s1, s2)
+  char	*s1, *s2;
+{
+  register p_scope c1 = (p_scope)s1, c2 = (p_scope)s2;
+
+  return c1->sc_start < c2->sc_start
+	 ? -1
+	 : c1->sc_start == c2->sc_start
+	   ? 0
+	   : 1;
+}
+
+/*ARGSUSED*/
+open_scope(name, has_activation)
+  p_symbol name;
+  int has_activation;
+{
+  register p_scope sc = new_scope();
+
+  sc->sc_has_activation_record = has_activation;
+  sc->sc_static_encl = CurrentScope;
+  sc->sc_definedby = name;
+  sc->sc_proclevel = CurrentScope->sc_proclevel;
+			/* sc_proclevel possibly reset by caller */
+  CurrentScope = sc;
+}
+
+init_scope()
+{
+  register p_scope sc = new_scope();
+
+  PervasiveScope = sc;
+  CurrentScope = sc;
+  open_scope((p_symbol) 0, 0);		/* this one will be closed at the
+					   first N_SO
+					*/
+  ScopeTree = create_avl_tree(cmp_starts);
+}
+
+close_scope()
+{
+  register p_scope sc = CurrentScope;
+
+  assert(sc != 0);
+  CurrentScope = sc->sc_static_encl;
+}
+
+add_scope_addr(scope)
+  p_scope	scope;
+{
+  add_to_avl_tree(ScopeTree, (char *)scope);
+}
+
+/* extern p_scope	get_scope_from_addr(t_addr a);
+   Returns the scope of the code at address 'a', or 0 if it could not be found.
+*/
+p_scope
+get_scope_from_addr(a)
+  t_addr a;
+{
+  t_scope sc;
+
+  sc.sc_start = a;
+  return (p_scope) find_ngt(ScopeTree, (char *) &sc);
+}
+
+/* extern p_scope	get_next_scope_from_addr(t_addr a);
+   Returns the scope following the one of the code at address 'a',
+   and that has an activation record,
+   or 0 if it could not be found.
+*/
+p_scope
+get_next_scope_from_addr(a)
+  t_addr a;
+{
+  t_scope sc;
+
+  sc.sc_start = a;
+  for (;;) {
+  	p_scope psc = (p_scope) find_nlt(ScopeTree, (char *) &sc);
+	if (! psc || psc->sc_has_activation_record) return psc;
+	sc.sc_start = psc->sc_start+1;
+  }
+  /*NOTREACHED*/
+}
+
+/* extern int	has_static_link(p_scope sc);
+   Returns 1 if the procedure of this scope takes a static link.
+*/
+int
+has_static_link(sc)
+  register p_scope	sc;
+{
+  return sc->sc_proclevel > 1;
+}
+
+/* extern p_scope	base_scope(p_scope sc);
+   Returns the closest enclosing scope of 'sc' that has an activation record.
+*/
+p_scope
+base_scope(sc)
+  register p_scope	sc;
+{
+  while (sc && ! sc->sc_has_activation_record) {
+	sc = sc->sc_static_encl;
+  }
+  return sc;
+}

+ 54 - 0
util/grind/scope.h

@@ -0,0 +1,54 @@
+/* scope structure */
+
+/* $Header$ */
+
+typedef struct scope {
+  struct scope	*sc_static_encl;	/* linked list of enclosing scopes */
+  struct symbol *sc_symbs;		/* symbols defined in this scope */
+  struct symbol *sc_definedby;		/* symbol defining this scope */
+  long		sc_start;		/* start address of code of this scope */
+  long		sc_bp_opp;		/* first breakpoint opportunity */
+  short		sc_proclevel;		/* proc level of this scope */
+  char		sc_has_activation_record;
+} t_scope, *p_scope;
+
+extern p_scope PervasiveScope, CurrentScope, FileScope;
+
+/* extern	init_scope();
+   Initializes the scope routines.
+*/
+extern	init_scope();
+
+/* extern	open_scope(struct symbol *name, int has_activation);
+   Opens a new scope and assigns it to CurrentScope; The new scope is defined
+   by 'name' and if 'has_activation' is set, it has an activation record.
+*/
+extern	open_scope();
+
+/* extern	close_scope();
+   Closes the current scope; CurrentScope becomes the statically enclosing
+   scope.
+*/
+extern	close_scope();
+
+/* extern	add_scope_addr(p_scope sc);
+   Adds scope 'sc' to the list of scopes that have an address at runtime.
+*/
+extern	add_scope_addr();
+
+/* extern p_scope	get_scope_from_addr(t_addr a);
+   Returns the scope of the code at address 'a', or 0 if it could not be found.
+*/
+extern p_scope	get_scope_from_addr();
+
+/* extern p_scope	get_next_scope_from_addr(t_addr a);
+   Returns the scope following the one of the code at address 'a',
+   and that has an activation record,
+   or 0 if it could not be found.
+*/
+extern p_scope	get_next_scope_from_addr();
+
+/* extern p_scope	base_scope(p_scope sc);
+   Returns the closest enclosing scope of 'sc' that has an activation record.
+*/
+extern p_scope	base_scope();

+ 8 - 0
util/grind/sizes.h

@@ -0,0 +1,8 @@
+/* For the time being ... */
+
+#define SZ_INT		4
+#define SZ_SHORT	2
+#define SZ_POINTER	4
+#define SZ_LONG		4
+#define SZ_FLOAT	4
+#define SZ_DOUBLE	8

+ 237 - 0
util/grind/symbol.c

@@ -0,0 +1,237 @@
+/* $Header$ */
+
+/* Symbol handling */
+
+#include	<alloc.h>
+#include	<out.h>
+#include	<stb.h>
+#include	<assert.h>
+
+#include	"position.h"
+#include	"file.h"
+#include	"idf.h"
+#include	"type.h"
+#include	"symbol.h"
+#include	"scope.h"
+#include	"tree.h"
+#include	"operator.h"
+
+p_symbol	currfile;
+
+p_symbol
+NewSymbol(s, scope, class, nam)
+  char	*s;
+  register p_scope scope;
+  struct outname *nam;
+{
+  register p_symbol sym;
+  
+  sym = new_symbol();
+  sym->sy_idf = str2idf(s, 0);
+  sym->sy_scope = scope;
+  sym->sy_prev_sc = scope->sc_symbs;
+  scope->sc_symbs = sym;
+  sym->sy_next = sym->sy_idf->id_def;
+  sym->sy_idf->id_def = sym;
+  sym->sy_class = class;
+  switch(class) {
+  case MODULE:
+  case PROC:
+  case FUNCTION:
+  case VAR:
+  case REGVAR:
+  case LOCVAR:
+  case VARPAR:
+	sym->sy_name.nm_value = nam->on_valu;
+	break;
+  default:
+	break;
+  }
+  return sym;
+}
+
+/* Lookup a definition for 'id' in scope 'scope' with class in the 'class'
+   bitset.
+*/
+p_symbol
+Lookup(id, scope, class)
+  struct idf *id;
+  p_scope scope;
+  int	class;
+{
+  register p_symbol p = id ? id->id_def : 0;
+
+  while (p) {
+	if (p->sy_scope == scope && (p->sy_class & class)) {
+		return p;
+	}
+	p = p->sy_next;
+  }
+  return (p_symbol) 0;
+}
+
+/* Lookup a definition for 'id' with class in the 'class' bitset,
+   starting in scope 'sc' and also looking in enclosing scopes.
+*/
+p_symbol
+Lookfromscope(id, class, sc)
+  register struct idf *id;
+  int	class;
+  register p_scope	sc;
+{
+  if (! id) return (p_symbol) 0;
+
+  while (sc) {
+	register p_symbol sym = id->id_def;
+	while (sym) {
+		if (sym->sy_scope == sc && (sym->sy_class & class)) {
+			return sym;
+		}
+		sym = sym->sy_next;
+	}
+	sc = sc->sc_static_encl;
+  }
+  return (p_symbol) 0;
+}
+
+/* Lookup a definition for 'id' with class in the 'class' bitset,
+   starting in scope 'CurrentScope' and also looking in enclosing scopes.
+*/
+p_symbol
+Lookfor(id, class)
+  register struct idf *id;
+  int	class;
+{
+  return Lookfromscope(id, class, CurrentScope);
+}
+
+extern char *strrindex();
+
+p_symbol
+add_file(s)
+  char	*s;
+{
+  register p_symbol sym = NewSymbol(s,
+				    PervasiveScope,
+				    FILESYM,
+				    (struct outname *) 0);
+  register char *p;
+
+  sym->sy_file = new_file();
+  sym->sy_file->f_sym = sym;
+  p = strrindex(s, '.');
+  if (p) {
+	char c = *p;
+	p_symbol sym1;
+
+	*p = 0;
+	sym1 = NewSymbol(Salloc(s, (unsigned) strlen(s)+1),
+		  	 PervasiveScope,
+		 	 FILELINK,
+			 (struct outname *) 0);
+	*p = c;
+	sym1->sy_filelink = sym;
+  }
+  return sym;
+}
+
+/* Determine if the OP_SELECT tree indicated by 'p' could lead to scope 'sc'.
+*/
+static int
+consistent(p, sc)
+  p_tree	p;
+  p_scope	sc;
+{
+  p_tree	arg;
+  p_symbol	sym;
+
+  assert(p->t_oper == OP_SELECT);
+  sc = sc->sc_static_encl;
+  if (!sc) return 0;
+
+  p = p->t_args[0];
+
+  switch(p->t_oper) {
+  case OP_NAME:
+	sym = Lookfromscope(p->t_idf, FILELINK|FILESYM|PROC|MODULE, sc);
+	return sym != 0;
+
+  case OP_SELECT:
+	arg = p->t_args[1];
+	sym = Lookfromscope(arg->t_idf, FILELINK|FILESYM|PROC|MODULE, sc);
+	if (sym == 0) return 0;
+	return consistent(p, sym->sy_scope);
+
+  default:
+	assert(0);
+  }
+  return 0;	/* notreached? */
+}
+
+/* Try to find the name referred to in the node indicated by 'p', and
+   try to be just a little bit intelligent about it.
+*/
+p_symbol
+identify(p, class_set)
+  p_tree	p;
+  int		class_set;
+{
+  p_symbol	sym = 0;
+  register p_symbol s;
+  p_tree	arg;
+
+  switch(p->t_oper) {
+  case OP_NAME:
+	if (! p->t_sc) p->t_sc = CurrentScope;
+	sym = Lookfromscope(p->t_idf, class_set, p->t_sc);
+	if (sym) {
+		/* Found it. */
+		break;
+	}
+
+	/* We could not find it using scope p->t_sc; now we try to identify
+	   it using class_set. If this results in only one definition, we
+	   take this one.
+	*/
+	s = p->t_idf->id_def;
+	while (s) {
+		if (s->sy_class & class_set) {
+			if (sym) {
+				error("could not identify \"%s\"", p->t_str);
+				sym = 0;
+				break;
+			}
+			sym = s;
+		}
+		s = s->sy_next;
+	}
+	if (!sym && !s) {
+		error("could not find \"%s\"", p->t_str);
+	}
+	break;
+
+  case OP_SELECT:
+	arg = p->t_args[1];
+	assert(arg->t_oper == OP_NAME);
+	s = arg->t_idf->id_def;
+	sym = 0;
+	while (s) {
+		if ((s->sy_class & class_set) && consistent(p, s->sy_scope)) {
+			if (sym) {
+				error("could not identify \"%s\"", arg->t_str);
+				sym = 0;
+			}
+			sym = s;
+		}
+		s = s->sy_next;
+	}
+	if (!sym && !s) {
+		error("could not find \"%s\"", arg->t_str);
+	}
+	break;
+
+  default:
+	assert(0);
+  }
+  return sym;
+}

+ 58 - 0
util/grind/symbol.hh

@@ -0,0 +1,58 @@
+/* $Header$
+   Symbol table data structure.
+   Each identifier structure refers to a list of possible meanings of this
+   identifier. Each of these meanings is represented by a "symbol" structure.
+*/
+
+typedef union constant {	/* depends on type */
+  long	co_ival;
+  double co_rval;
+  char *co_sval;
+  char *co_setval;
+} t_const, *p_const;
+
+typedef struct name {
+  long	nm_value;		/* address or offset */
+  struct scope *nm_scope;	/* for names that define a scope */
+} t_name, *p_name;
+
+typedef struct symbol {
+  struct symbol	*sy_next;	/* link to next meaning */
+  struct symbol	*sy_prev_sc;	/* link to previous decl in scope */
+  struct type	*sy_type;	/* type of symbol */
+  int		sy_class;
+#define CONST		0x0001
+#define TYPE		0x0002
+#define TAG		0x0004
+#define MODULE		0x0008
+#define PROC		0x0010
+#define FUNCTION	0x0020
+#define VAR		0x0040
+#define REGVAR		0x0080
+#define LOCVAR		0x0100
+#define VARPAR		0x0200
+/* #define SYMENTRY	0x0400	/* a non-dbx entry */
+#define FILESYM		0x0800	/* a filename */
+#define FILELINK	0x1000	/* a filename without its suffix */
+  struct idf	*sy_idf;	/* reference back to its idf structure */
+  struct scope	*sy_scope;	/* scope in which this symbol resides */
+  union {
+	t_const	syv_const;	/* CONST */
+	t_name	syv_name;
+/*	struct outname syv_onam;	/* for non-dbx entries */
+	struct file *syv_file;		/* for FILESYM */
+	struct symbol *syv_fllink;	/* for FILELINK */
+  }	sy_v;
+#define sy_const	sy_v.syv_const
+#define sy_name		sy_v.syv_name
+#define sy_onam		sy_v.syv_onam
+#define sy_file		sy_v.syv_file
+#define sy_filelink	sy_v.syv_fllink
+} t_symbol, *p_symbol;
+
+/* ALLOCDEF "symbol" 50 */
+
+extern p_symbol	NewSymbol(), Lookup(), Lookfor(), Lookfromscope(), add_file();
+extern p_symbol identify();
+
+extern p_symbol	currfile;

+ 15 - 0
util/grind/tok_tools.amk

@@ -0,0 +1,15 @@
+MAKE_TOKFILE = make.tokfile;
+MAKE_TOKCASE = make.tokcase;
+
+%tool gen_tokens (
+    csrc:	%in  [type = C-src, gen_tokens, persistent];
+    tokfile:	%out [type = LLgen-src]	=> get($csrc, LL-dest);
+    symbols:	%out [type = C-src, b]	=> get($csrc, cc-dest);
+    mktok:	%in  [type = command]	=> $MAKE_TOKFILE;
+    mkcase:	%in  [type = command]	=> $MAKE_TOKCASE;
+)
+{
+    exec($mktok,  stdin => $csrc, stdout => $tokfile);
+    exec($mkcase, stdin => $csrc, stdout => $symbols);
+    echo({$tokfile, 'and', $symbols, 'created'});
+};

+ 88 - 0
util/grind/tokenname.c

@@ -0,0 +1,88 @@
+/*
+ * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands.
+ * See the copyright notice in the ACK home directory, in the file "Copyright".
+ *
+ * Author: Ceriel J.H. Jacobs
+ */
+
+/* T O K E N   D E F I N I T I O N S */
+
+/* $Header$ */
+
+#include	"tokenname.h"
+#include	"Lpars.h"
+#include	"position.h"
+#include	"file.h"
+#include	"idf.h"
+
+/*	To centralize the declaration of %tokens, their presence in this
+	file is taken as their declaration. The Makefile will produce
+	a grammar file (tokenfile.g) from this file. This scheme ensures
+	that all tokens have a printable name.
+	Also, the "token2str.c" file is produced from this file.
+*/
+
+#if 0
+struct tokenname tkspec[] =	{	/* the names of the special tokens */
+	{NAME, "name"},
+	{STRING, "string"},
+	{INTEGER, "number"},
+	{REAL, "real"},
+	{CHAR, "char"},
+	{0, ""}
+};
+#endif
+
+struct tokenname tkidf[] =	{	/* names of the identifier tokens */
+	{LIST, "list"},
+	{XFILE, "file"},
+	{RUN, "run"},
+	{RERUN, "rerun"},
+	{STOP, "stop"},
+	{WHEN, "when"},
+	{AT, "at"},
+	{IN, "in"},
+	{ON, "on"},
+	{IF, "if"},
+	{CONT, "cont"},
+	{STEP, "step"},
+	{NEXT, "next"},
+	{REGS, "regs"},
+	{WHERE, "where"},
+	{STATUS, "status"},
+	{DELETE, "delete"},
+	{PRINT, "print"},
+	{DUMP, "dump"},
+	{RESTORE, "restore"},
+	{TRACE, "trace"},
+	{-1, "quit"},
+	{0, ""}
+};
+
+#if 0
+struct tokenname tkinternal[] = {	/* internal keywords	*/
+	{0, "0"}
+};
+
+struct tokenname tkstandard[] =	{	/* standard identifiers */
+	{0, ""}
+};
+#endif
+
+/* Some routines to handle tokennames */
+
+reserve(resv)
+	register struct tokenname *resv;
+{
+	/*	The names of the tokens described in resv are entered
+		as reserved words.
+	*/
+	register struct idf *p;
+
+	while (resv->tn_symbol)	{
+		p = str2idf(resv->tn_name, 0);
+		if (!p) fatal("out of Memory");
+		p->id_reserved = resv->tn_symbol;
+		resv++;
+	}
+}

+ 17 - 0
util/grind/tokenname.h

@@ -0,0 +1,17 @@
+/*
+ * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands.
+ * See the copyright notice in the ACK home directory, in the file "Copyright".
+ *
+ * Author: Ceriel J.H. Jacobs
+ */
+
+/* T O K E N N A M E   S T R U C T U R E */
+
+/* $Header$ */
+
+struct tokenname	{	/*	Used for defining the name of a
+					token as identified by its symbol
+				*/
+	int tn_symbol;
+	char *tn_name;
+};

+ 594 - 0
util/grind/tree.c

@@ -0,0 +1,594 @@
+/* $Header$ */
+
+#include	<stdio.h>
+#include	<varargs.h>
+#include	<assert.h>
+#include	<alloc.h>
+#include	<out.h>
+
+#include	"operator.h"
+#include	"position.h"
+#include	"file.h"
+#include	"idf.h"
+#include	"tree.h"
+#include	"message.h"
+#include	"scope.h"
+#include	"symbol.h"
+#include	"langdep.h"
+
+extern FILE	*db_out;
+extern t_lineno	currline;
+extern long	pointer_size;
+extern char	*strrindex();
+
+p_tree		run_command;
+
+/*VARARGS1*/
+p_tree
+mknode(va_alist)
+  va_dcl
+{
+  va_list ap;
+  register p_tree p = new_tree();
+
+  va_start(ap);
+  {
+	register int i, na;
+
+	p->t_oper = va_arg(ap, int);
+	switch(p->t_oper) {
+	case OP_NAME:
+		p->t_idf = va_arg(ap, struct idf *);
+		p->t_str = va_arg(ap, char *);
+		break;
+	case OP_INTEGER:
+		p->t_ival = va_arg(ap, long);
+		break;
+	case OP_AT:
+		p->t_lino = va_arg(ap, long);
+		p->t_filename = va_arg(ap, char *);
+		break;
+	case OP_NEXT:
+	case OP_STEP:
+	case OP_REGS:
+	case OP_DELETE:
+	case OP_RESTORE:
+		p->t_ival = va_arg(ap, long);
+		break;
+	default:
+		na = nargs(p->t_oper);
+		assert(na <= MAXARGS);
+		for (i = 0; i < na; i++) {
+			p->t_args[i] = va_arg(ap, p_tree);
+		}
+		break;
+	}
+  }
+  va_end(ap);
+  return p;
+}
+
+freenode(p)
+  register p_tree	p;
+{
+  register int na, i;
+
+  if (! p) return;
+  switch(p->t_oper) {
+  case OP_NAME:
+  case OP_INTEGER:
+  case OP_AT:
+  case OP_CONT:
+  case OP_NEXT:
+  case OP_STEP:
+  case OP_REGS:
+  case OP_DELETE:
+	break;
+  default:
+	na = nargs(p->t_oper);
+	assert(na <= MAXARGS);
+	for (i = 0; i < na; i++) {
+		freenode(p->t_args[i]);
+	}
+	break;
+  }
+  free_tree(p);
+}
+
+print_node(p, top_level)
+  register p_tree	p;
+{
+  if (!p) return;
+  switch(p->t_oper) {
+  case OP_LIST:
+	fputs("list ", db_out);
+	if (p->t_args[0]) {
+		print_node(p->t_args[0], 0);
+		if (p->t_args[1]) {
+			fputs(", ", db_out);
+			print_node(p->t_args[1], 0);
+		}
+	}
+	break;
+  case OP_PRINT:
+	fputs("print ", db_out);
+	print_node(p->t_args[0], 0);
+	break;
+  case OP_FILE:
+	fputs("file ", db_out);
+	print_node(p->t_args[0], 0);
+	break;
+  case OP_DELETE:
+	fprintf(db_out, "delete %d", p->t_ival);
+	break;
+  case OP_REGS:
+	fprintf(db_out, "regs %d", p->t_ival);
+	break;
+  case OP_NEXT:
+	fprintf(db_out, "next %d", p->t_ival);
+	break;
+  case OP_STEP:
+	fprintf(db_out, "step %d", p->t_ival);
+	break;
+  case OP_STATUS:
+	fputs("status", db_out);
+	break;
+  case OP_DUMP:
+	fputs("dump ", db_out);
+	print_position(p->t_address, 1);
+	break;
+  case OP_RESTORE:
+	fprintf(db_out, "restore %d", p->t_ival);
+	break;
+  case OP_WHERE:
+	fputs("where", db_out);
+	break;
+  case OP_CONT:
+	fputs("cont", db_out);
+	if (p->t_args[0]) {
+		fprintf(db_out, " %d", p->t_args[0]->t_ival);
+	}
+	if (p->t_args[1]) {
+		fputs(" ", db_out);
+		print_node(p->t_args[1], 0);
+	}
+	break;
+
+  case OP_WHEN:
+	fputs("when ", db_out);
+	if (p->t_address != NO_ADDR) {
+		print_position(p->t_address, 1);
+	}
+	else print_node(p->t_args[0], 0);
+	if (p->t_args[1]) {
+		fputs(" if ", db_out);
+		print_node(p->t_args[1], 0);
+	}
+	p = p->t_args[2];
+	fputs(" { ", db_out);
+	while (p->t_oper == OP_LINK) {
+		print_node(p->t_args[0], 0);
+		fputs("; ", db_out);
+		p = p->t_args[1];
+	}
+	print_node(p, 0);
+	fputs(" }", db_out);
+	break;
+  case OP_STOP:
+	fputs("stop ", db_out);
+	if (p->t_address != NO_ADDR) {
+		print_position(p->t_address, 1);
+	}
+	else print_node(p->t_args[0], 0);
+	if (p->t_args[1]) {
+		fputs(" if ", db_out);
+		print_node(p->t_args[1], 0);
+	}
+	break;
+  case OP_TRACE:
+	fputs("trace ", db_out);
+	if (p->t_args[2]) {
+		fputs("on ", db_out);
+		print_node(p->t_args[2], 0);
+		fputs(" ", db_out);
+	}
+	if (p->t_address != NO_ADDR) {
+		print_position(p->t_address, 1);
+	}
+	else print_node(p->t_args[0], 0);
+	if (p->t_args[1]) {
+		fputs(" if ", db_out);
+		print_node(p->t_args[1], 0);
+	}
+	break;
+  case OP_AT:
+	fprintf(db_out, "at \"%s\":%ld", p->t_filename, p->t_lino);
+	break;
+  case OP_IN:
+	fputs("in ", db_out);
+	print_node(p->t_args[0], 0);
+	break;
+  case OP_SELECT:
+	print_node(p->t_args[0], 0);
+	fputs("`", db_out);
+	print_node(p->t_args[1], 0);
+	break;
+  case OP_NAME:
+	fputs(p->t_str, db_out);
+	break;
+  case OP_INTEGER:
+	fprintf(db_out, "%d", p->t_ival);
+	break;
+  }
+  if (top_level) fputs("\n", db_out);
+}
+
+int
+repeatable(com)
+  p_tree	com;
+{
+  switch(com->t_oper) {
+  case OP_CONT:
+  case OP_NEXT:
+  case OP_STEP:
+  case OP_LIST:
+  case OP_STATUS:
+  case OP_PRINT:
+	return 1;
+  }
+  return 0;
+}
+
+int
+in_status(com)
+  p_tree	com;
+{
+  switch(com->t_oper) {
+  case OP_STOP:
+  case OP_WHEN:
+  case OP_TRACE:
+  case OP_DUMP:
+	return 1;
+  }
+  return 0;
+}
+
+eval(p)
+  p_tree	p;
+{
+  if (p) (*operators[p->t_oper].op_fun)(p);
+}
+
+do_list(p)
+  p_tree	p;
+{
+  if (currfile) {
+	lines(currfile->sy_file,
+	      p->t_args[0] ? (int) p->t_args[0]->t_ival : (int) currline,
+	      p->t_args[1] ? (int) p->t_args[1]->t_ival : (int) currline+9);
+	currline = p->t_args[1] ? p->t_args[1]->t_ival + 1 : currline + 10;
+  }
+  else fprintf(db_out, "no current file\n");
+}
+
+do_file(p)
+  p_tree	p;
+{
+  if (p->t_args[0]) {
+	newfile(p->t_args[0]->t_idf);
+  }
+  else if (currfile) fprintf(db_out, "%s\n", currfile->sy_idf->id_text);
+  else fprintf(db_out, "no current file\n");
+}
+
+newfile(id)
+  register struct idf	*id;
+{
+  register p_symbol sym = Lookup(id, PervasiveScope, FILESYM);
+
+  if (currfile != sym) currline = 1;
+  currfile = sym;
+  if (! currfile) {
+	currline = 1;
+	currfile = add_file(id->id_text);
+	currfile->sy_file->f_scope = FileScope;
+  }
+  find_language(strrindex(id->id_text, '.'));
+}
+
+static t_addr
+get_pos(p)
+  p_tree	p;
+{
+  t_addr	a = ILL_ADDR;
+  register p_symbol sym;
+
+  if (! p) return NO_ADDR;
+  if (p->t_address != 0) return p->t_address;
+  switch(p->t_oper) {
+  case OP_AT:
+	if (! p->t_filename &&
+	    (! currfile || ! (p->t_filename = currfile->sy_idf->id_text))) {
+		error("no current file");
+		break;
+	}
+	a = get_addr_from_position(&(p->t_pos));
+	if (a == ILL_ADDR) {
+		error("could not determine address of \"%s\":%d",
+			p->t_filename, p->t_lino);
+		break;
+	}
+	p->t_address = a;
+	break;
+	
+  case OP_IN:
+	a =  get_pos(p->t_args[0]);
+	p->t_address = a;
+	break;
+
+  case OP_NAME:
+  case OP_SELECT:
+	sym = identify(p, PROC|MODULE);
+	if (! sym) {
+		break;
+	}
+	if (! sym->sy_name.nm_scope || ! sym->sy_name.nm_scope->sc_bp_opp) {
+		error("could not determine address of \"%s\"", p->t_str);
+		break;
+	}
+	a = sym->sy_name.nm_scope->sc_bp_opp;
+	break;
+
+  default:
+	assert(0);
+  }
+  return a;
+}
+
+do_stop(p)
+  p_tree	p;
+{
+  t_addr	a = get_pos(p->t_args[0]);
+
+  if (a == ILL_ADDR) {
+	return;
+  }
+
+  p->t_address = a;
+  add_to_item_list(p);
+  if (a != NO_ADDR) {
+	if (! set_or_clear_breakpoint(a, SETBP)) {
+		error("could not set breakpoint");
+	}
+  }
+}
+
+do_trace(p)
+  p_tree	p;
+{
+  t_addr a;
+  t_addr e;
+
+  p->t_address = NO_ADDR;
+  if (p->t_args[0]) {
+	a = get_pos(p->t_args[0]);
+	if (a == ILL_ADDR) return;
+	if (p->t_args[0]->t_oper == OP_AT) {
+		e = a;
+		p->t_address = a;
+	}
+	else {
+		p_scope sc = get_next_scope_from_addr(a+1);
+
+		if (sc) e = sc->sc_start - 1;
+		else e = 0xffffffff;
+	}
+	if (! set_or_clear_trace(a, e, SETTRACE)) {
+		error("could not set trace");
+	}
+  }
+  add_to_item_list(p);
+}
+
+do_continue(p)
+  p_tree	p;
+{
+  int count;
+
+  if (p) {
+	count = p->t_args[0]->t_ival;
+	if (p->t_args[1]) {
+		t_addr	a = get_addr_from_position(&(p->t_args[1]->t_pos));
+		p_scope sc = get_scope_from_addr(a);
+
+		if (a == ILL_ADDR || base_scope(sc) != base_scope(CurrentScope) ||
+		    ! set_pc(a)) {
+			error("cannot continue at line %d",
+			      p->t_args[1]->t_lino);
+			return;
+		}
+	}
+  }
+  else count = 1;
+  while (count--) {
+	if (! send_cont(count==0)) {
+		error("no debuggee");
+		break;
+	}
+  }
+}
+
+do_step(p)
+  p_tree	p;
+{
+  if (! do_single_step(SETSS, p->t_ival)) {
+	error("no debuggee");
+  }
+}
+
+do_next(p)
+  p_tree	p;
+{
+
+  if (! do_single_step(SETSSF, p->t_ival)) {
+	error("no debuggee");
+  }
+}
+
+extern t_addr	*get_EM_regs();
+
+do_regs(p)
+  p_tree	p;
+{
+  t_addr	*buf;
+  int		n = p->t_ival;
+
+  if (! (buf = get_EM_regs(n))) {
+	error("no debuggee");
+	return;
+  }
+  fprintf(db_out, "EM registers %d levels back:\n", n);
+  fprintf(db_out, "\tLocalBase =\t0x%lx\n\tArgumentBase =\t0x%lx\n", 
+		(long) buf[LB_OFF], (long) buf[AB_OFF]);
+  fprintf(db_out, "\tProgramCounter=\t0x%lx\n\tHeapPointer = \t0x%lx\n",
+		(long) buf[PC_OFF],
+		(long) buf[HP_OFF]);
+  fprintf(db_out, "\tStackPointer =\t0x%lx\n", (long) buf[SP_OFF]);
+}
+
+/*ARGSUSED*/
+do_where(p)
+  p_tree	p;
+{
+  int i = 0;
+
+  for (;;) {
+	t_addr AB;
+	t_addr PC;
+	p_scope sc;
+	t_addr *buf;
+
+	if (! (buf = get_EM_regs(i++))) {
+		error("no debuggee");
+		return;
+	}
+	AB = buf[AB_OFF];
+	PC = buf[PC_OFF];
+	if (! AB) break;
+	sc = base_scope(get_scope_from_addr(PC));
+	if (! sc || sc->sc_start > PC) break;
+	fprintf(db_out, "%s(", sc->sc_definedby->sy_idf->id_text);
+	print_params(sc->sc_definedby->sy_type, AB, has_static_link(sc));
+	fputs(") ", db_out);
+	print_position(PC, 0);
+	fputs("\n", db_out);
+  }
+}
+
+/*ARGSUSED*/
+do_status(p)
+  p_tree	p;
+{
+  print_items();
+}
+
+extern p_tree	remove_from_item_list();
+
+do_delete(p)
+  p_tree	p;
+{
+  p = remove_from_item_list((int) p->t_ival);
+
+  if (p) switch(p->t_oper) {
+  case OP_WHEN:
+  case OP_STOP: {
+	t_addr a = get_pos(p->t_args[0]);
+
+	if (a != ILL_ADDR && a != NO_ADDR) {
+		set_or_clear_breakpoint(a, CLRBP);
+	}
+	break;
+	}
+  case OP_TRACE: {
+	t_addr a = get_pos(p->t_args[0]);
+	
+	if (a != ILL_ADDR && a != NO_ADDR) {
+		t_addr e;
+		if (p->t_args[0]->t_oper == OP_AT) {
+			e = a;
+		}
+		else {
+			p_scope sc = get_next_scope_from_addr(a+1);
+
+			if (sc) e = sc->sc_start - 1;
+			else e = 0xffffffff;
+		}
+		set_or_clear_trace(a, e, CLRTRACE);
+	}
+	break;
+	}
+  case OP_DUMP:
+	free_dump(p);
+  }
+  freenode(p);
+}
+
+do_print(p)
+  p_tree	p;
+{
+  p_symbol sym;
+
+  switch(p->t_oper) {
+  case OP_PRINT:
+	do_print(p->t_args[0]);
+	break;
+  case OP_LINK:
+	do_print(p->t_args[0]);
+	do_print(p->t_args[1]);
+	break;
+  case OP_NAME:
+  case OP_SELECT:
+	sym = identify(p, VAR|REGVAR|LOCVAR|VARPAR);
+	if (! sym) return;
+	print_node(p, 0);
+	if (! print_sym(sym)) {
+		fputs(" currently not available\n", db_out);
+		break;
+	}
+  }
+}
+
+perform(p, a)
+  register p_tree	p;
+  t_addr		a;
+{
+  switch(p->t_oper) {
+  case OP_WHEN:
+	p = p->t_args[2];
+	while (p->t_oper == OP_LINK) {
+		eval(p->t_args[0]);
+		p = p->t_args[1];
+	}
+	eval(p);
+	break;
+  case OP_TRACE:
+	if (p->t_args[0] && p->t_args[0]->t_oper == OP_IN) {
+		register p_scope sc = base_scope(CurrentScope);
+	
+		if (sc != get_scope_from_addr(p->t_args[0]->t_address)) {
+			break;
+		}
+	}
+	{
+		p_position pos = get_position_from_addr(a);
+
+		newfile(str2idf(pos->filename, 1));
+		currline = pos->lineno;
+		lines(currfile->sy_file, (int)currline, (int)currline);
+		if (p->t_args[2]) do_print(p->t_args[2]);
+	}
+	break;
+  default:
+	assert(0);
+  }
+}

+ 32 - 0
util/grind/tree.hh

@@ -0,0 +1,32 @@
+/* $Header$ */
+
+#define MAXARGS	3
+
+typedef struct tree {
+  int	t_oper;		/* operator */
+  t_addr t_address;	/* some operators use an address */
+  int	t_itemno;	/* item number in status list */
+  union {
+	long tt_ival;
+	struct {
+		struct idf *tt_idf;
+		char *tt_str;
+		struct scope *tt_scope;	
+	} tt_x;
+	struct tree *tt_args[MAXARGS];
+	t_position tt_pos;
+  } t_xxxx;
+#define t_ival	t_xxxx.tt_ival
+#define t_idf	t_xxxx.tt_x.tt_idf
+#define t_str	t_xxxx.tt_x.tt_str
+#define t_sc	t_xxxx.tt_x.tt_scope
+#define t_args	t_xxxx.tt_args
+#define t_lino t_xxxx.tt_pos.lineno
+#define t_filename t_xxxx.tt_pos.filename
+#define t_pos	t_xxxx.tt_pos
+} t_tree, *p_tree;
+
+/* ALLOCDEF "tree" 100 */
+
+extern p_tree	mknode();
+extern p_tree	run_command;

+ 387 - 0
util/grind/type.c

@@ -0,0 +1,387 @@
+/* $Header$ */
+
+/* Routines to create type structures */
+
+#include <alloc.h>
+#include <assert.h>
+
+#include "type.h"
+#include "sizes.h"
+#include "symbol.h"
+#include "scope.h"
+#include "message.h"
+#include "langdep.h"
+
+p_type	int_type, char_type, short_type, long_type;
+p_type	uint_type, uchar_type, ushort_type, ulong_type;
+p_type	void_type, incomplete_type;
+p_type	float_type, double_type;
+p_type	string_type;
+
+long	int_size = SZ_INT,
+	char_size = 1,
+	short_size = SZ_SHORT,
+	long_size = SZ_LONG,
+	pointer_size = SZ_POINTER;
+
+long	float_size = SZ_FLOAT,
+	double_size = SZ_DOUBLE;
+
+struct bounds {
+	long low, high;
+};
+
+static struct bounds ibounds[2] = {
+	{ -128, 127 },
+	{ -32768, 32767 }
+};
+
+static struct bounds ubounds[2] = {
+	{ 0, 255 },
+	{ 0, 65535 }
+};
+
+static long max_int[8], max_uns[8];
+
+struct integer_types {
+	long	maxval;
+	p_type	type;
+};
+
+static struct integer_types i_types[4];
+static struct integer_types u_types[5];
+
+#define ufit(n, nb)	Xfit(n, nb, ubounds)
+#define ifit(n, nb)	Xfit(n, nb, ibounds)
+#define Xfit(n, nb, b)	((n) >= (b)[(nb)-1].low && (n) <= (b)[(nb)-1].high)
+
+/* Create a subrange type, but is it really a subrange? */
+p_type
+subrange_type(A, base_index, c1, c2, result_index)
+  int *base_index, *result_index;
+  long c1, c2;
+{
+  int itself = 0;
+  register p_type p;
+  p_type base_type;
+
+  if (!A) {
+	/* Subrange of itself is a special case ... */
+	if (result_index &&
+ 	   result_index[0] == base_index[0] &&
+ 	   result_index[1] == base_index[1]) {
+
+		/* c1 = 0 and c2 = 0 -> void */
+		if (c1 == 0 && c2 == 0) {
+			return void_type;
+		}
+
+		/* c1 = 0 and c2 = 127 -> char ??? */
+		if (c1 == 0 && c2 == 127) {
+			return char_type;
+		}
+		itself = 1;
+	}
+  }
+
+  if (itself) base_type = int_type; else base_type = *(tp_lookup(base_index));
+
+  if (! A) {
+	/* c2 = 0 and c1 > 0 -> real */
+	if (c2 == 0 && c1 > 0) {
+		if (c1 == float_size) return float_type;
+		return double_type;
+	}
+
+	/* c1 = 0 and base_index indicates int_type or itself -> unsigned,
+	   c1 = -c2 - 1 and base_index indicates int_type or itself -> integer
+	*/
+	if (itself || base_type == int_type) {
+		register struct integer_types *ip = 0;
+		if (c1 == 0) {
+			ip = &u_types[0];
+		}
+		else if (c1 == -c2 - 1) {
+			ip = &i_types[0];
+		}
+		if (ip) {
+			while (ip->maxval != 0 && ip->maxval != c2) ip++;
+			if (ip->maxval) return ip->type;
+		}
+	}
+  }
+  /* if we get here, it actually is a subrange type */
+  p = new_type();
+  p->ty_class = T_SUBRANGE;
+  p->ty_low = c1;
+  p->ty_up = c2;
+  p->ty_base = base_type;
+  p->ty_A = A;
+
+  /* determine size of subrange type */
+  p->ty_size = base_type->ty_size;
+  if (!A && p->ty_base == uint_type) {
+  	if (ufit(p->ty_up, 1)) {
+		p->ty_size = 1;
+  	}
+  	else if (ufit(p->ty_up, (int)short_size)) {
+		p->ty_size = short_size;
+	}
+  }
+  if (!A && p->ty_base == int_type) {
+  	if (ifit(p->ty_up, 1) && ifit(p->ty_low, 1)) {
+		p->ty_size = 1;
+  	}
+  	else if (ifit(p->ty_up, (int)short_size) &&
+		 ifit(p->ty_low, (int)short_size)) {
+		p->ty_size = short_size;
+	}
+  }
+
+  return p;
+}
+
+static long
+nel(tp)
+  register p_type tp;
+{
+  switch(tp->ty_class) {
+  case T_SUBRANGE:
+	if (tp->ty_A) return 0;
+	if (tp->ty_low <= tp->ty_up) return tp->ty_up - tp->ty_low + 1;
+	return tp->ty_low - tp->ty_up + 1;
+  case T_UNSIGNED:
+  case T_INTEGER:
+	if (tp->ty_size == 1) return 256;
+	if (tp->ty_size == 2) return 65536L;
+	assert(0);
+	break;
+  case T_ENUM:
+	return tp->ty_nenums;
+  default:
+	assert(0);
+	break;
+  }
+  return 0;
+}
+
+p_type
+array_type(bound_type, el_type)
+  p_type bound_type, el_type;
+{
+  register p_type tp = new_type();
+
+  tp->ty_class = T_ARRAY;
+  tp->ty_index = bound_type;
+  tp->ty_elements = el_type;
+  tp->ty_size = (*currlang->arrayelsize)(el_type->ty_size) * nel(bound_type);
+  return tp;
+}
+
+p_type
+basic_type(fund, size)
+  int	fund;
+  long	size;
+{
+  register p_type	p = new_type();
+
+  p->ty_class = fund;
+  p->ty_size = size;
+  return p;
+}
+
+set_bounds(tp)
+  register p_type	tp;
+{
+  /* Determine the size and low of a set type */
+  register p_type base = tp->ty_setbase;
+
+  if (base->ty_class == T_SUBRANGE) {
+	tp->ty_size = (base->ty_up - base->ty_low + 7) >> 3;
+	tp->ty_setlow = base->ty_low;
+  }
+  else if (base->ty_class == T_INTEGER) {
+	tp->ty_size = (max_int[(int)base->ty_size] + 1) >>  2;
+	tp->ty_setlow = -max_int[(int)base->ty_size] - 1;
+  }
+  else {
+	assert(base->ty_class == T_UNSIGNED);
+	tp->ty_size = (max_uns[(int)base->ty_size] + 1) >>  3;
+	tp->ty_setlow = 0;
+  }
+}
+
+init_types()
+{
+  register int i = 0;
+  register long x = 0;
+
+  while (x >= 0) {
+	i++;
+	x = (x << 8) + 0377;
+	max_uns[i] = x;
+	max_int[i] = x & ~(1L << (8*i - 1));
+  }
+  int_type = basic_type(T_INTEGER, int_size);
+  long_type = basic_type(T_INTEGER, long_size);
+  short_type = basic_type(T_INTEGER, short_size);
+  char_type = basic_type(T_INTEGER, char_size);
+  uint_type = basic_type(T_UNSIGNED, int_size);
+  ulong_type = basic_type(T_UNSIGNED, long_size);
+  ushort_type = basic_type(T_UNSIGNED, short_size);
+  uchar_type = basic_type(T_UNSIGNED, char_size);
+  string_type = basic_type(T_STRING, 0L);
+  void_type = basic_type(T_VOID, 0L);
+  incomplete_type = basic_type(T_INCOMPLETE, 0L);
+  float_type = basic_type(T_REAL, float_size);
+  double_type = basic_type(T_REAL, double_size);
+
+  i_types[0].maxval = max_int[(int)int_size]; i_types[0].type = int_type;
+  i_types[1].maxval = max_int[(int)short_size]; i_types[1].type = short_type;
+  i_types[2].maxval = max_int[(int)long_size]; i_types[2].type = long_type;
+  u_types[0].maxval = max_uns[(int)int_size]; u_types[0].type = uint_type;
+  u_types[1].maxval = max_uns[(int)short_size]; u_types[1].type = ushort_type;
+  u_types[2].maxval = max_uns[(int)long_size]; u_types[2].type = ulong_type;
+  u_types[3].maxval = max_uns[1]; u_types[3].type = uchar_type;
+}
+
+/*
+ * Some code to handle type indices, which are pairs of integers.
+ * What we need is a two-dimensional array, but we don't know how large
+ * it is going to be, so we use a list of rows instead.
+ */
+static struct tp_index {
+  unsigned	len;
+  p_type	*row;
+} *list_row;
+static unsigned list_len;
+
+#define NINCR 10
+  
+p_type *
+tp_lookup(type_index)
+  int *type_index;
+{
+  register int i;
+  register struct tp_index *p;
+
+  while (type_index[0] >= list_len) {
+	if (list_len) {
+		list_row = (struct tp_index *) Realloc((char *) list_row,
+				(list_len += NINCR) * sizeof(struct tp_index));
+	}
+	else	list_row = (struct tp_index *)
+			Malloc((list_len = NINCR) * sizeof(struct tp_index));
+	for (i = NINCR; i > 0; i--) {
+		list_row[list_len - i].len = 0;
+	}
+  }
+  p = &list_row[type_index[0]];
+  while (type_index[1] >= p->len) {
+	if (p->len) {
+		p->row = (p_type *) Realloc((char *) p->row,
+				(p->len += NINCR) * sizeof(p_type));
+	}
+	else	p->row = (p_type *) Malloc((p->len = NINCR) * sizeof(p_type));
+	for (i = NINCR; i > 0; i--) {
+		p->row[p->len - i] = 0;
+	}
+  }
+  return &(p->row[type_index[1]]);
+}
+
+clean_tp_tab()
+{
+  if (list_len) {
+  	register int i = list_len;
+
+  	while (--i >= 0) {
+		register int j = list_row[i].len;
+		if (j) {
+			while (--j > 0) {
+				p_type p = list_row[i].row[j];
+				if (p == incomplete_type) {
+					error("incomplete type (%d,%d) 0x%x", i, j, &list_row[i].row[j]);
+				}
+			}
+			free((char *) list_row[i].row);
+		}
+	}
+	free((char *) list_row);
+	list_len = 0;
+	list_row = 0;
+  }
+}
+
+end_literal(tp, maxval)
+  register p_type tp;
+  long maxval;
+{
+  tp->ty_literals = (struct literal *)
+	Realloc((char *) tp->ty_literals,
+		tp->ty_nenums * sizeof(struct literal));
+  if (ufit(maxval, 1)) tp->ty_size = 1;
+  else if (ufit(maxval, (int)short_size)) tp->ty_size = short_size;
+  else tp->ty_size = int_size;
+}
+
+long
+param_size(t, v)
+  int	v;
+  p_type t;
+{
+  if (v == 'i' || v == 'v') {
+	/* addresss; only exception is a conformant array, which also
+	   takes a descriptor.
+	*/
+	if (t->ty_class == T_ARRAY &&
+	    t->ty_index->ty_class == T_SUBRANGE &&
+	    t->ty_index->ty_A) {
+		return pointer_size + 3 * int_size;
+	}
+	return pointer_size;
+  }
+  return ((t->ty_size + int_size - 1) / int_size) * int_size;
+}
+
+add_param_type(v, s)
+  int	v;		/* 'v' or 'i' for address, 'p' for value */
+  p_symbol s;		/* parameter itself */
+{
+  register p_scope sc = base_scope(s->sy_scope);
+  register p_type prc_type;
+
+  if (! sc) return;
+  prc_type = sc->sc_definedby->sy_type;
+  assert(prc_type->ty_class == T_PROCEDURE);
+
+  prc_type->ty_nparams++;
+  prc_type->ty_params = (struct param *) Realloc((char *) prc_type->ty_params, 
+				(unsigned)prc_type->ty_nparams * sizeof(struct param));
+  prc_type->ty_params[prc_type->ty_nparams - 1].par_type = s->sy_type;
+  prc_type->ty_params[prc_type->ty_nparams - 1].par_kind = v;
+  prc_type->ty_nbparams += param_size(s->sy_type, v);
+}
+
+/* Compute the size of a parameter of dynamic size
+*/
+
+long
+compute_size(tp, AB)
+  p_type	tp;
+  char		*AB;
+{
+  long	low, high;
+
+  assert(tp->ty_class == T_ARRAY);
+  assert(tp->ty_index->ty_class == T_SUBRANGE);
+  assert(tp->ty_index->ty_A != 0);
+
+  if (tp->ty_index->ty_A & 1) {
+	low = BUFTOI(AB+tp->ty_index->ty_low);
+  } else low = tp->ty_index->ty_low;
+  if (tp->ty_index->ty_A & 2) {
+	high = BUFTOI(AB+tp->ty_index->ty_up);
+  } else high = tp->ty_index->ty_up;
+  return (high - low + 1) * tp->ty_elements->ty_size;
+}

+ 118 - 0
util/grind/type.hh

@@ -0,0 +1,118 @@
+/* $Header$ */
+
+/* internal type representation */
+
+/* structure for struct/union elements */
+struct fields {
+  long fld_pos;			/* position of field */
+  long fld_bitsize;		/* size in bits */
+  struct type *fld_type;	/* type of field */
+  char *fld_name;		/* name of field */
+};
+
+/* structure for enumeration literals */
+struct literal {
+  long lit_val;			/* value of literal */
+  char *lit_name;		/* name of literal */
+};
+
+/* structure for parameters */
+struct param {
+  struct type *par_type;	/* type of parameter */
+  char par_kind;		/* kind of parameter ('p', 'i', or 'v') */
+};
+
+typedef struct type {
+  short		ty_class;
+#define T_SUBRANGE	 1
+#define T_ARRAY		 2
+#define T_STRUCT	 3
+#define T_UNION		 4
+#define T_ENUM		 5
+#define T_POINTER	 6
+#define T_FILE		 7
+#define T_PROCEDURE	 8
+#define T_SET		 9
+#define T_REAL		10
+#define T_INTEGER	11
+#define T_VOID		12
+#define T_UNSIGNED	13
+#define T_STRING	14	/* only for string constants ... */
+#define T_INCOMPLETE   100
+  short		ty_flags;
+#define T_CROSS		0x0001
+  long		ty_size;
+  union {
+     /* cross references */
+     char	    *typ_tag;
+#define ty_tag		ty_v.typ_tag
+     /* procedures/functions: */
+     struct {
+	int	    typ_nparams;
+	struct type *typ_retval;
+	struct param *typ_params;
+	long	    typ_nbparams;
+     } ty_proc;
+#define ty_nparams	ty_v.ty_proc.typ_nparams
+#define ty_retval	ty_v.ty_proc.typ_retval
+#define ty_params	ty_v.ty_proc.typ_params
+#define ty_nbparams	ty_v.ty_proc.typ_nbparams
+     /* pointers, files: */
+     struct type *typ_ptrto;
+#define ty_ptrto	ty_v.typ_ptrto
+#define ty_fileof	ty_v.typ_ptrto
+     /* arrays: */
+     struct {
+	struct type *typ_index;
+	struct type *typ_elements;
+     } ty_array;
+#define ty_index	ty_v.ty_array.typ_index
+#define ty_elements	ty_v.ty_array.typ_elements
+     /* subranges: */
+     struct {
+	long typ_low, typ_up;
+	int typ_A;
+	struct type *typ_base;
+     } ty_subrange;
+#define ty_A		ty_v.ty_subrange.typ_A
+#define ty_low		ty_v.ty_subrange.typ_low
+#define ty_up		ty_v.ty_subrange.typ_up
+#define ty_base		ty_v.ty_subrange.typ_base
+     /* structures/unions: */
+     struct {
+	unsigned typ_nfields;		/* number of field structures */
+	struct fields *typ_fields;
+     } ty_struct;
+#define ty_nfields	ty_v.ty_struct.typ_nfields
+#define ty_fields	ty_v.ty_struct.typ_fields
+     /* enumerations: */
+     struct {
+	unsigned typ_nenums;		/* number of enumeration literals */
+	struct literal *typ_literals;
+     } ty_enum;
+#define ty_nenums	ty_v.ty_enum.typ_nenums
+#define ty_literals	ty_v.ty_enum.typ_literals
+     /* bit sets: */
+     struct {
+	struct type *typ_setbase;	/* base type of set elements */
+	long typ_setlow;		/* low bound */
+     } ty_set;
+#define ty_setbase	ty_v.ty_set.typ_setbase
+#define ty_setlow	ty_v.ty_set.typ_setlow
+  } ty_v;
+} t_type, *p_type;
+
+/* ALLOCDEF "type" 50 */
+
+extern p_type
+	subrange_type(),
+	array_type(),
+	*tp_lookup();
+extern long
+	param_size(),
+	compute_size();
+
+extern p_type	char_type, uchar_type,
+		long_type, double_type, string_type;
+extern p_type	void_type, incomplete_type;
+

+ 125 - 0
util/grind/value.c

@@ -0,0 +1,125 @@
+/* $Header$ */
+
+#include <alloc.h>
+
+#include "position.h"
+#include "scope.h"
+#include "symbol.h"
+#include "type.h"
+#include "message.h"
+
+int stack_offset;		/* for up and down commands */
+
+extern long pointer_size;
+extern t_addr *get_EM_regs();
+
+/* Get the value of the symbol indicated by sym.
+   Return 0 on failure,
+	  1 on success.
+   On success, 'buf' contains the value, and 'AB' may contain the parameters
+   of the procedure invocation containing sym.
+   For both of these, storage is allocated by Malloc; this storage must
+   be freed by caller (I don't like this any more than you do, but caller
+   does not know sizes).
+*/
+int
+get_value(sym, buf, AB)
+  register p_symbol	sym;
+  char	**buf, **AB;
+{
+  p_type	tp = sym->sy_type;
+  long		size = tp->ty_size;
+  int		retval = 0;
+  t_addr	*EM_regs;
+  int		i;
+  p_scope	sc, symsc;
+
+  *buf = 0;
+  *AB = 0;
+  switch(sym->sy_class) {
+  case VAR:
+	/* exists if child exists; nm_value contains addres */
+	*buf = Malloc((unsigned) size);
+	if (get_bytes(size, (t_addr) sym->sy_name.nm_value, *buf)) {
+		retval = 1;
+	}
+	break;
+
+  case VARPAR:
+  case LOCVAR:
+	/* first find the stack frame in which it resides */
+	symsc = base_scope(sym->sy_scope);
+
+	/* now symsc contains the scope where the storage for sym is
+	   allocated. Now find it on the stack of child.
+	*/
+	i = stack_offset;
+	for (;;) {
+		sc = 0;
+		if (! (EM_regs = get_EM_regs(i++))) {
+			/* no child? */
+			break;
+		}
+		if (! EM_regs[AB_OFF]) {
+			/* no more frames */
+			break;
+		}
+		sc = base_scope(get_scope_from_addr(EM_regs[PC_OFF]));
+		if (! sc || sc->sc_start > EM_regs[PC_OFF]) {
+			sc = 0;
+			break;
+		}
+		if (sc == symsc) break;		/* found it */
+	}
+
+	if (! sc) break;	/* not found */
+
+	if (sym->sy_class == LOCVAR) {
+		/* Either local variable or value parameter */
+		*buf = Malloc((unsigned) size);
+		if (get_bytes(size,
+			      EM_regs[sym->sy_name.nm_value < 0 
+					? LB_OFF 
+					: AB_OFF
+				     ] +
+				  (t_addr) sym->sy_name.nm_value,
+			      *buf)) {
+			retval = 1;
+		}
+		break;
+	}
+
+	/* If we get here, we have a var parameter. Get the parameters
+	   of the current procedure invocation.
+	*/
+	{
+		p_type proctype = sc->sc_definedby->sy_type;
+
+		size = proctype->ty_nbparams;
+		if (has_static_link(sc)) size += pointer_size;
+		*AB = Malloc((unsigned) size);
+		if (! get_bytes(size, EM_regs[AB_OFF], *AB)) {
+			break;
+		}
+		if ((size = tp->ty_size) == 0) {
+			size = compute_size(tp, *AB);
+		}
+	}
+	*buf = Malloc((unsigned) size);
+	if (get_bytes(size,
+		      (t_addr) BUFTOA(*AB+sym->sy_name.nm_value),
+		      *buf)) {
+		retval = 1;
+	}
+	break;
+  }
+
+  if (retval == 0) {
+	if (*buf) free(*buf);
+	if (*AB) free(*AB);
+	*buf = 0;
+	*AB = 0;
+  }
+
+  return retval;
+}