1/* Tree browser.
2   Copyright (C) 2002, 2003, 2004, 2007, 2008 Free Software Foundation, Inc.
3   Contributed by Sebastian Pop <s.pop@laposte.net>
4
5This file is part of GCC.
6
7GCC is free software; you can redistribute it and/or modify it under
8the terms of the GNU General Public License as published by the Free
9Software Foundation; either version 3, or (at your option) any later
10version.
11
12GCC is distributed in the hope that it will be useful, but WITHOUT ANY
13WARRANTY; without even the implied warranty of MERCHANTABILITY or
14FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
15for more details.
16
17You should have received a copy of the GNU General Public License
18along with GCC; see the file COPYING3.  If not see
19<http://www.gnu.org/licenses/>.  */
20
21#include "config.h"
22#include "system.h"
23#include "coretypes.h"
24#include "tm.h"
25#include "tree.h"
26#include "tree-inline.h"
27#include "diagnostic.h"
28#include "hashtab.h"
29
30
31#define TB_OUT_FILE stdout
32#define TB_IN_FILE stdin
33#define TB_NIY fprintf (TB_OUT_FILE, "Sorry this command is not yet implemented.\n")
34#define TB_WF fprintf (TB_OUT_FILE, "Warning, this command failed.\n")
35
36
37/* Structures for handling Tree Browser's commands.  */
38#define DEFTBCODE(COMMAND, STRING, HELP)   COMMAND,
39enum TB_Comm_code {
40#include "tree-browser.def"
41  TB_UNUSED_COMMAND
42};
43#undef DEFTBCODE
44typedef enum TB_Comm_code TB_CODE;
45
46struct tb_command {
47  const char *help_msg;
48  const char *comm_text;
49  size_t comm_len;
50  TB_CODE comm_code;
51};
52
53#define DEFTBCODE(code, str, help) { help, str, sizeof(str) - 1, code },
54static const struct tb_command tb_commands[] =
55{
56#include "tree-browser.def"
57};
58#undef DEFTBCODE
59
60#define TB_COMMAND_LEN(N) (tb_commands[N].comm_len)
61#define TB_COMMAND_TEXT(N) (tb_commands[N].comm_text)
62#define TB_COMMAND_CODE(N) (tb_commands[N].comm_code)
63#define TB_COMMAND_HELP(N) (tb_commands[N].help_msg)
64
65
66/* Next structure is for parsing TREE_CODEs.  */
67struct tb_tree_code {
68  enum tree_code code;
69  const char *code_string;
70  size_t code_string_len;
71};
72
73#define DEFTREECODE(SYM, STRING, TYPE, NARGS) { SYM, STRING, sizeof (STRING) - 1 },
74#define END_OF_BASE_TREE_CODES \
75  { LAST_AND_UNUSED_TREE_CODE, "@dummy", sizeof ("@dummy") - 1 },
76static const struct tb_tree_code tb_tree_codes[] =
77{
78#include "all-tree.def"
79};
80#undef DEFTREECODE
81#undef END_OF_BASE_TREE_CODES
82
83#define TB_TREE_CODE(N) (tb_tree_codes[N].code)
84#define TB_TREE_CODE_TEXT(N) (tb_tree_codes[N].code_string)
85#define TB_TREE_CODE_LEN(N) (tb_tree_codes[N].code_string_len)
86
87
88/* Function declarations.  */
89
90static long TB_getline (char **, long *, FILE *);
91static TB_CODE TB_get_command (char *);
92static enum tree_code TB_get_tree_code (char *);
93static tree find_node_with_code (tree *, int *, void *);
94static tree store_child_info (tree *, int *, void *);
95static void TB_update_up (tree);
96static tree TB_current_chain_node (tree);
97static tree TB_prev_expr (tree);
98static tree TB_next_expr (tree);
99static tree TB_up_expr (tree);
100static tree TB_first_in_bind (tree);
101static tree TB_last_in_bind (tree);
102static int  TB_parent_eq (const void *, const void *);
103static tree TB_history_prev (void);
104
105/* FIXME: To be declared in a .h file.  */
106void browse_tree (tree);
107
108/* Static variables.  */
109static htab_t TB_up_ht;
110static tree TB_history_stack = NULL_TREE;
111static int TB_verbose = 1;
112
113
114/* Entry point in the Tree Browser.  */
115
116void
117browse_tree (tree begin)
118{
119  tree head;
120  TB_CODE tbc = TB_UNUSED_COMMAND;
121  ssize_t rd;
122  char *input = NULL;
123  long input_size = 0;
124
125  fprintf (TB_OUT_FILE, "\nTree Browser\n");
126
127#define TB_SET_HEAD(N) do {                                           \
128  TB_history_stack = tree_cons (NULL_TREE, (N), TB_history_stack);    \
129  head = N;                                                           \
130  if (TB_verbose)                                                     \
131    if (head)                                                         \
132      {                                                               \
133	print_generic_expr (TB_OUT_FILE, head, 0);                    \
134	fprintf (TB_OUT_FILE, "\n");                                  \
135      }                                                               \
136} while (0)
137
138  TB_SET_HEAD (begin);
139
140  /* Store in a hashtable information about previous and upper statements.  */
141  {
142    TB_up_ht = htab_create (1023, htab_hash_pointer, &TB_parent_eq, NULL);
143    TB_update_up (head);
144  }
145
146  while (24)
147    {
148      fprintf (TB_OUT_FILE, "TB> ");
149      rd = TB_getline (&input, &input_size, TB_IN_FILE);
150
151      if (rd == -1)
152	/* EOF.  */
153	goto ret;
154
155      if (rd != 1)
156	/* Get a new command.  Otherwise the user just pressed enter, and thus
157	   she expects the last command to be reexecuted.  */
158	tbc = TB_get_command (input);
159
160      switch (tbc)
161	{
162	case TB_UPDATE_UP:
163	  TB_update_up (head);
164	  break;
165
166	case TB_MAX:
167	  if (head && (INTEGRAL_TYPE_P (head)
168		       || TREE_CODE (head) == REAL_TYPE
169		       || TREE_CODE (head) == FIXED_POINT_TYPE))
170	    TB_SET_HEAD (TYPE_MAX_VALUE (head));
171	  else
172	    TB_WF;
173	  break;
174
175	case TB_MIN:
176	  if (head && (INTEGRAL_TYPE_P (head)
177		       || TREE_CODE (head) == REAL_TYPE
178		       || TREE_CODE (head) == FIXED_POINT_TYPE))
179	    TB_SET_HEAD (TYPE_MIN_VALUE (head));
180	  else
181	    TB_WF;
182	  break;
183
184	case TB_ELT:
185	  if (head && TREE_CODE (head) == TREE_VEC)
186	    {
187	      /* This command takes another argument: the element number:
188		 for example "elt 1".  */
189	      TB_NIY;
190	    }
191	  else if (head && TREE_CODE (head) == VECTOR_CST)
192	    {
193	      /* This command takes another argument: the element number:
194                 for example "elt 1".  */
195              TB_NIY;
196	    }
197	  else
198	    TB_WF;
199	  break;
200
201	case TB_VALUE:
202	  if (head && TREE_CODE (head) == TREE_LIST)
203	    TB_SET_HEAD (TREE_VALUE (head));
204	  else
205	    TB_WF;
206	  break;
207
208	case TB_PURPOSE:
209	  if (head && TREE_CODE (head) == TREE_LIST)
210	    TB_SET_HEAD (TREE_PURPOSE (head));
211	  else
212	    TB_WF;
213	  break;
214
215	case TB_IMAG:
216	  if (head && TREE_CODE (head) == COMPLEX_CST)
217	    TB_SET_HEAD (TREE_IMAGPART (head));
218	  else
219	    TB_WF;
220	  break;
221
222	case TB_REAL:
223	  if (head && TREE_CODE (head) == COMPLEX_CST)
224	    TB_SET_HEAD (TREE_REALPART (head));
225	  else
226	    TB_WF;
227	  break;
228
229	case TB_BLOCK:
230	  if (head && TREE_CODE (head) == BIND_EXPR)
231	    TB_SET_HEAD (TREE_OPERAND (head, 2));
232	  else
233	    TB_WF;
234	  break;
235
236	case TB_SUBBLOCKS:
237	  if (head && TREE_CODE (head) == BLOCK)
238	    TB_SET_HEAD (BLOCK_SUBBLOCKS (head));
239	  else
240	    TB_WF;
241	  break;
242
243	case TB_SUPERCONTEXT:
244	  if (head && TREE_CODE (head) == BLOCK)
245	    TB_SET_HEAD (BLOCK_SUPERCONTEXT (head));
246	  else
247	    TB_WF;
248	  break;
249
250	case TB_VARS:
251	  if (head && TREE_CODE (head) == BLOCK)
252	    TB_SET_HEAD (BLOCK_VARS (head));
253	  else if (head && TREE_CODE (head) == BIND_EXPR)
254	    TB_SET_HEAD (TREE_OPERAND (head, 0));
255	  else
256	    TB_WF;
257	  break;
258
259	case TB_REFERENCE_TO_THIS:
260	  if (head && TYPE_P (head))
261	    TB_SET_HEAD (TYPE_REFERENCE_TO (head));
262	  else
263	    TB_WF;
264	  break;
265
266	case TB_POINTER_TO_THIS:
267	  if (head && TYPE_P (head))
268	    TB_SET_HEAD (TYPE_POINTER_TO (head));
269	  else
270	    TB_WF;
271	  break;
272
273	case TB_BASETYPE:
274	  if (head && TREE_CODE (head) == OFFSET_TYPE)
275	    TB_SET_HEAD (TYPE_OFFSET_BASETYPE (head));
276	  else
277	    TB_WF;
278	  break;
279
280	case TB_ARG_TYPES:
281	  if (head && (TREE_CODE (head) == FUNCTION_TYPE
282		       || TREE_CODE (head) == METHOD_TYPE))
283	    TB_SET_HEAD (TYPE_ARG_TYPES (head));
284	  else
285	    TB_WF;
286	  break;
287
288	case TB_METHOD_BASE_TYPE:
289	  if (head && (TREE_CODE (head) == FUNCTION_TYPE
290		       || TREE_CODE (head) == METHOD_TYPE)
291	      && TYPE_METHOD_BASETYPE (head))
292	    TB_SET_HEAD (TYPE_METHOD_BASETYPE (head));
293	  else
294	    TB_WF;
295	  break;
296
297	case TB_FIELDS:
298	  if (head && (TREE_CODE (head) == RECORD_TYPE
299		       || TREE_CODE (head) == UNION_TYPE
300		       || TREE_CODE (head) == QUAL_UNION_TYPE))
301	    TB_SET_HEAD (TYPE_FIELDS (head));
302	  else
303	    TB_WF;
304	  break;
305
306	case TB_DOMAIN:
307	  if (head && TREE_CODE (head) == ARRAY_TYPE)
308	    TB_SET_HEAD (TYPE_DOMAIN (head));
309	  else
310	    TB_WF;
311	  break;
312
313	case TB_VALUES:
314	  if (head && TREE_CODE (head) == ENUMERAL_TYPE)
315	    TB_SET_HEAD (TYPE_VALUES (head));
316	  else
317	    TB_WF;
318	  break;
319
320	case TB_ARG_TYPE:
321	  if (head && TREE_CODE (head) == PARM_DECL)
322	    TB_SET_HEAD (DECL_ARG_TYPE (head));
323	  else
324	    TB_WF;
325	  break;
326
327	case TB_INITIAL:
328	  if (head && DECL_P (head))
329	    TB_SET_HEAD (DECL_INITIAL (head));
330	  else
331	    TB_WF;
332	  break;
333
334	case TB_RESULT:
335	  if (head && DECL_P (head))
336	    TB_SET_HEAD (DECL_RESULT_FLD (head));
337	  else
338	    TB_WF;
339	  break;
340
341	case TB_ARGUMENTS:
342	  if (head && DECL_P (head))
343	    TB_SET_HEAD (DECL_ARGUMENTS (head));
344	  else
345	    TB_WF;
346	  break;
347
348	case TB_ABSTRACT_ORIGIN:
349	  if (head && DECL_P (head))
350	    TB_SET_HEAD (DECL_ABSTRACT_ORIGIN (head));
351	  else if (head && TREE_CODE (head) == BLOCK)
352	    TB_SET_HEAD (BLOCK_ABSTRACT_ORIGIN (head));
353	  else
354	    TB_WF;
355	  break;
356
357	case TB_ATTRIBUTES:
358	  if (head && DECL_P (head))
359	    TB_SET_HEAD (DECL_ATTRIBUTES (head));
360	  else if (head && TYPE_P (head))
361	    TB_SET_HEAD (TYPE_ATTRIBUTES (head));
362	  else
363	    TB_WF;
364	  break;
365
366	case TB_CONTEXT:
367	  if (head && DECL_P (head))
368	    TB_SET_HEAD (DECL_CONTEXT (head));
369	  else if (head && TYPE_P (head)
370		   && TYPE_CONTEXT (head))
371	    TB_SET_HEAD (TYPE_CONTEXT (head));
372	  else
373	    TB_WF;
374	  break;
375
376	case TB_OFFSET:
377	  if (head && TREE_CODE (head) == FIELD_DECL)
378	    TB_SET_HEAD (DECL_FIELD_OFFSET (head));
379	  else
380	    TB_WF;
381	  break;
382
383	case TB_BIT_OFFSET:
384	  if (head && TREE_CODE (head) == FIELD_DECL)
385	    TB_SET_HEAD (DECL_FIELD_BIT_OFFSET (head));
386	  else
387	    TB_WF;
388          break;
389
390	case TB_UNIT_SIZE:
391	  if (head && DECL_P (head))
392	    TB_SET_HEAD (DECL_SIZE_UNIT (head));
393	  else if (head && TYPE_P (head))
394	    TB_SET_HEAD (TYPE_SIZE_UNIT (head));
395	  else
396	    TB_WF;
397	  break;
398
399	case TB_SIZE:
400	  if (head && DECL_P (head))
401	    TB_SET_HEAD (DECL_SIZE (head));
402	  else if (head && TYPE_P (head))
403	    TB_SET_HEAD (TYPE_SIZE (head));
404	  else
405	    TB_WF;
406	  break;
407
408	case TB_TYPE:
409	  if (head && TREE_TYPE (head))
410	    TB_SET_HEAD (TREE_TYPE (head));
411	  else
412	    TB_WF;
413	  break;
414
415	case TB_DECL_SAVED_TREE:
416	  if (head && TREE_CODE (head) == FUNCTION_DECL
417	      && DECL_SAVED_TREE (head))
418	    TB_SET_HEAD (DECL_SAVED_TREE (head));
419	  else
420	    TB_WF;
421	  break;
422
423	case TB_BODY:
424	  if (head && TREE_CODE (head) == BIND_EXPR)
425	    TB_SET_HEAD (TREE_OPERAND (head, 1));
426	  else
427	    TB_WF;
428	  break;
429
430	case TB_CHILD_0:
431	  if (head && EXPR_P (head) && TREE_OPERAND (head, 0))
432	    TB_SET_HEAD (TREE_OPERAND (head, 0));
433	  else
434	    TB_WF;
435	  break;
436
437	case TB_CHILD_1:
438          if (head && EXPR_P (head) && TREE_OPERAND (head, 1))
439	    TB_SET_HEAD (TREE_OPERAND (head, 1));
440	  else
441	    TB_WF;
442          break;
443
444	case TB_CHILD_2:
445          if (head && EXPR_P (head) && TREE_OPERAND (head, 2))
446	    TB_SET_HEAD (TREE_OPERAND (head, 2));
447	  else
448	    TB_WF;
449	  break;
450
451	case TB_CHILD_3:
452	  if (head && EXPR_P (head) && TREE_OPERAND (head, 3))
453	    TB_SET_HEAD (TREE_OPERAND (head, 3));
454	  else
455	    TB_WF;
456          break;
457
458	case TB_PRINT:
459	  if (head)
460	    debug_tree (head);
461	  else
462	    TB_WF;
463	  break;
464
465	case TB_PRETTY_PRINT:
466	  if (head)
467	    {
468	      print_generic_stmt (TB_OUT_FILE, head, 0);
469	      fprintf (TB_OUT_FILE, "\n");
470	    }
471	  else
472	    TB_WF;
473	  break;
474
475	case TB_SEARCH_NAME:
476
477	  break;
478
479	case TB_SEARCH_CODE:
480	  {
481	    enum tree_code code;
482	    char *arg_text;
483
484	    arg_text = strchr (input, ' ');
485	    if (arg_text == NULL)
486	      {
487		fprintf (TB_OUT_FILE, "First argument is missing.  This isn't a valid search command.  \n");
488		break;
489	      }
490	    code = TB_get_tree_code (arg_text + 1);
491
492	    /* Search in the subtree a node with the given code.  */
493	    {
494	      tree res;
495
496	      res = walk_tree (&head, find_node_with_code, &code, NULL);
497	      if (res == NULL_TREE)
498		{
499		  fprintf (TB_OUT_FILE, "There's no node with this code (reachable via the walk_tree function from this node).\n");
500		}
501	      else
502		{
503		  fprintf (TB_OUT_FILE, "Achoo!  I got this node in the tree.\n");
504		  TB_SET_HEAD (res);
505		}
506	    }
507	    break;
508	  }
509
510#define TB_MOVE_HEAD(FCT) do {       \
511  if (head)                          \
512    {                                \
513      tree t;                        \
514      t = FCT (head);                \
515      if (t)                         \
516        TB_SET_HEAD (t);             \
517      else                           \
518	TB_WF;                       \
519    }                                \
520  else                               \
521    TB_WF;                           \
522} while (0)
523
524	case TB_FIRST:
525	  TB_MOVE_HEAD (TB_first_in_bind);
526          break;
527
528        case TB_LAST:
529          TB_MOVE_HEAD (TB_last_in_bind);
530          break;
531
532	case TB_UP:
533	  TB_MOVE_HEAD (TB_up_expr);
534	  break;
535
536	case TB_PREV:
537	  TB_MOVE_HEAD (TB_prev_expr);
538	  break;
539
540	case TB_NEXT:
541	  TB_MOVE_HEAD (TB_next_expr);
542	  break;
543
544	case TB_HPREV:
545	  /* This command is a little bit special, since it deals with history
546	     stack.  For this reason it should keep the "head = ..." statement
547	     and not use TB_MOVE_HEAD.  */
548	  if (head)
549	    {
550	      tree t;
551	      t = TB_history_prev ();
552	      if (t)
553		{
554		  head = t;
555		  if (TB_verbose)
556		    {
557		      print_generic_expr (TB_OUT_FILE, head, 0);
558		      fprintf (TB_OUT_FILE, "\n");
559		    }
560		}
561	      else
562		TB_WF;
563	    }
564	  else
565	    TB_WF;
566	  break;
567
568	case TB_CHAIN:
569	  /* Don't go further if it's the last node in this chain.  */
570	  if (head && TREE_CODE (head) == BLOCK)
571	    TB_SET_HEAD (BLOCK_CHAIN (head));
572	  else if (head && TREE_CHAIN (head))
573	    TB_SET_HEAD (TREE_CHAIN (head));
574	  else
575	    TB_WF;
576	  break;
577
578	case TB_FUN:
579	  /* Go up to the current function declaration.  */
580	  TB_SET_HEAD (current_function_decl);
581	  fprintf (TB_OUT_FILE, "Current function declaration.\n");
582	  break;
583
584	case TB_HELP:
585	  /* Display a help message.  */
586	  {
587	    int i;
588	    fprintf (TB_OUT_FILE, "Possible commands are:\n\n");
589	    for (i = 0; i < TB_UNUSED_COMMAND; i++)
590	      {
591		fprintf (TB_OUT_FILE, "%20s  -  %s\n", TB_COMMAND_TEXT (i), TB_COMMAND_HELP (i));
592	      }
593	  }
594	  break;
595
596	case TB_VERBOSE:
597	  if (TB_verbose == 0)
598	    {
599	      TB_verbose = 1;
600	      fprintf (TB_OUT_FILE, "Verbose on.\n");
601	    }
602	  else
603	    {
604	      TB_verbose = 0;
605	      fprintf (TB_OUT_FILE, "Verbose off.\n");
606	    }
607	  break;
608
609	case TB_EXIT:
610	case TB_QUIT:
611	  /* Just exit from this function.  */
612	  goto ret;
613
614	default:
615	  TB_NIY;
616	}
617    }
618
619 ret:;
620  htab_delete (TB_up_ht);
621  return;
622}
623
624
625/* Search the first node in this BIND_EXPR.  */
626
627static tree
628TB_first_in_bind (tree node)
629{
630  tree t;
631
632  if (node == NULL_TREE)
633    return NULL_TREE;
634
635  while ((t = TB_prev_expr (node)))
636    node = t;
637
638  return node;
639}
640
641/* Search the last node in this BIND_EXPR.  */
642
643static tree
644TB_last_in_bind (tree node)
645{
646  tree t;
647
648  if (node == NULL_TREE)
649    return NULL_TREE;
650
651  while ((t = TB_next_expr (node)))
652    node = t;
653
654  return node;
655}
656
657/* Search the parent expression for this node.  */
658
659static tree
660TB_up_expr (tree node)
661{
662  tree res;
663  if (node == NULL_TREE)
664    return NULL_TREE;
665
666  res = (tree) htab_find (TB_up_ht, node);
667  return res;
668}
669
670/* Search the previous expression in this BIND_EXPR.  */
671
672static tree
673TB_prev_expr (tree node)
674{
675  node = TB_current_chain_node (node);
676
677  if (node == NULL_TREE)
678    return NULL_TREE;
679
680  node = TB_up_expr (node);
681  if (node && TREE_CODE (node) == COMPOUND_EXPR)
682    return node;
683  else
684    return NULL_TREE;
685}
686
687/* Search the next expression in this BIND_EXPR.  */
688
689static tree
690TB_next_expr (tree node)
691{
692  node = TB_current_chain_node (node);
693
694  if (node == NULL_TREE)
695    return NULL_TREE;
696
697  node = TREE_OPERAND (node, 1);
698  return node;
699}
700
701static tree
702TB_current_chain_node (tree node)
703{
704  if (node == NULL_TREE)
705    return NULL_TREE;
706
707  if (TREE_CODE (node) == COMPOUND_EXPR)
708    return node;
709
710  node = TB_up_expr (node);
711  if (node)
712    {
713      if (TREE_CODE (node) == COMPOUND_EXPR)
714	return node;
715
716      node = TB_up_expr (node);
717      if (TREE_CODE (node) == COMPOUND_EXPR)
718	return node;
719    }
720
721  return NULL_TREE;
722}
723
724/* For each node store in its children nodes that the current node is their
725   parent.  This function is used by walk_tree.  */
726
727static tree
728store_child_info (tree *tp, int *walk_subtrees ATTRIBUTE_UNUSED,
729		  void *data ATTRIBUTE_UNUSED)
730{
731  tree node;
732  void **slot;
733
734  node = *tp;
735
736  /* 'node' is the parent of 'TREE_OPERAND (node, *)'.  */
737  if (EXPR_P (node))
738    {
739      int n = TREE_OPERAND_LENGTH (node);
740      int i;
741      for (i = 0; i < n; i++)
742	{
743	  tree op = TREE_OPERAND (node, i);
744	  slot = htab_find_slot (TB_up_ht, op, INSERT);
745	  *slot = (void *) node;
746	}
747    }
748
749  /* Never stop walk_tree.  */
750  return NULL_TREE;
751}
752
753/* Function used in TB_up_ht.  */
754
755static int
756TB_parent_eq (const void *p1, const void *p2)
757{
758  const_tree const node = (const_tree)p2;
759  const_tree const parent = (const_tree) p1;
760
761  if (p1 == NULL || p2 == NULL)
762    return 0;
763
764  if (EXPR_P (parent))
765    {
766      int n = TREE_OPERAND_LENGTH (parent);
767      int i;
768      for (i = 0; i < n; i++)
769	if (node == TREE_OPERAND (parent, i))
770	  return 1;
771    }
772  return 0;
773}
774
775/* Update information about upper expressions in the hash table.  */
776
777static void
778TB_update_up (tree node)
779{
780  while (node)
781    {
782      walk_tree (&node, store_child_info, NULL, NULL);
783
784      /* Walk function's body.  */
785      if (TREE_CODE (node) == FUNCTION_DECL)
786        if (DECL_SAVED_TREE (node))
787          walk_tree (&DECL_SAVED_TREE (node), store_child_info, NULL, NULL);
788
789      /* Walk rest of the chain.  */
790      node = TREE_CHAIN (node);
791    }
792  fprintf (TB_OUT_FILE, "Up/prev expressions updated.\n");
793}
794
795/* Parse the input string for determining the command the user asked for.  */
796
797static TB_CODE
798TB_get_command (char *input)
799{
800  unsigned int mn, size_tok;
801  int comp;
802  char *space;
803
804  space = strchr (input, ' ');
805  if (space != NULL)
806    size_tok = strlen (input) - strlen (space);
807  else
808    size_tok = strlen (input) - 1;
809
810  for (mn = 0; mn < TB_UNUSED_COMMAND; mn++)
811    {
812      if (size_tok != TB_COMMAND_LEN (mn))
813	continue;
814
815      comp = memcmp (input, TB_COMMAND_TEXT (mn), TB_COMMAND_LEN (mn));
816      if (comp == 0)
817	/* Here we just determined the command.  If this command takes
818	   an argument, then the argument is determined later.  */
819	return TB_COMMAND_CODE (mn);
820    }
821
822  /* Not a valid command.  */
823  return TB_UNUSED_COMMAND;
824}
825
826/* Parse the input string for determining the tree code.  */
827
828static enum tree_code
829TB_get_tree_code (char *input)
830{
831  unsigned int mn, size_tok;
832  int comp;
833  char *space;
834
835  space = strchr (input, ' ');
836  if (space != NULL)
837    size_tok = strlen (input) - strlen (space);
838  else
839    size_tok = strlen (input) - 1;
840
841  for (mn = 0; mn < LAST_AND_UNUSED_TREE_CODE; mn++)
842    {
843      if (size_tok != TB_TREE_CODE_LEN (mn))
844	continue;
845
846      comp = memcmp (input, TB_TREE_CODE_TEXT (mn), TB_TREE_CODE_LEN (mn));
847      if (comp == 0)
848	{
849	  fprintf (TB_OUT_FILE, "%s\n", TB_TREE_CODE_TEXT (mn));
850	  return TB_TREE_CODE (mn);
851	}
852    }
853
854  /* This isn't a valid code.  */
855  return LAST_AND_UNUSED_TREE_CODE;
856}
857
858/* Find a node with a given code.  This function is used as an argument to
859   walk_tree.  */
860
861static tree
862find_node_with_code (tree *tp, int *walk_subtrees ATTRIBUTE_UNUSED,
863		     void *data)
864{
865  enum tree_code *code;
866  code = (enum tree_code *) data;
867  if (*code == TREE_CODE (*tp))
868    return *tp;
869
870  return NULL_TREE;
871}
872
873/* Returns a pointer to the last visited node.  */
874
875static tree
876TB_history_prev (void)
877{
878  if (TB_history_stack)
879    {
880      TB_history_stack = TREE_CHAIN (TB_history_stack);
881      if (TB_history_stack)
882	return TREE_VALUE (TB_history_stack);
883    }
884  return NULL_TREE;
885}
886
887/* Read up to (and including) a '\n' from STREAM into *LINEPTR
888   (and null-terminate it). *LINEPTR is a pointer returned from malloc
889   (or NULL), pointing to *N characters of space.  It is realloc'd as
890   necessary.  Returns the number of characters read (not including the
891   null terminator), or -1 on error or EOF.
892   This function comes from sed (and is supposed to be a portable version
893   of getline).  */
894
895static long
896TB_getline (char **lineptr, long *n, FILE *stream)
897{
898  char *line, *p;
899  long size, copy;
900
901  if (lineptr == NULL || n == NULL)
902    {
903      errno = EINVAL;
904      return -1;
905    }
906
907  if (ferror (stream))
908    return -1;
909
910  /* Make sure we have a line buffer to start with.  */
911  if (*lineptr == NULL || *n < 2) /* !seen and no buf yet need 2 chars.  */
912    {
913#ifndef MAX_CANON
914#define MAX_CANON       256
915#endif
916      line = (char *) xrealloc (*lineptr, MAX_CANON);
917      if (line == NULL)
918        return -1;
919      *lineptr = line;
920      *n = MAX_CANON;
921    }
922
923  line = *lineptr;
924  size = *n;
925
926  copy = size;
927  p = line;
928
929  while (1)
930    {
931      long len;
932
933      while (--copy > 0)
934        {
935          register int c = getc (stream);
936          if (c == EOF)
937            goto lose;
938          else if ((*p++ = c) == '\n')
939            goto win;
940        }
941
942      /* Need to enlarge the line buffer.  */
943      len = p - line;
944      size *= 2;
945      line = (char *) xrealloc (line, size);
946      if (line == NULL)
947        goto lose;
948      *lineptr = line;
949      *n = size;
950      p = line + len;
951      copy = size - len;
952    }
953
954 lose:
955  if (p == *lineptr)
956    return -1;
957
958  /* Return a partial line since we got an error in the middle.  */
959 win:
960#if defined(WIN32) || defined(_WIN32) || defined(__CYGWIN__) || defined(MSDOS)
961  if (p - 2 >= *lineptr && p[-2] == '\r')
962    p[-2] = p[-1], --p;
963#endif
964  *p = '\0';
965  return p - *lineptr;
966}
967