1Python Scripting
2================
3
4LLDB has been structured from the beginning to be scriptable in two
5ways -- a Unix Python session can initiate/run a debug session
6non-interactively using LLDB; and within the LLDB debugger tool, Python
7scripts can be used to help with many tasks, including inspecting
8program data, iterating over containers and determining if a breakpoint
9should stop execution or continue. This document will show how to do
10some of these things by going through an example, explaining how to use
11Python scripting to find a bug in a program that searches for text in a
12large binary tree.
13
14.. contents::
15   :local:
16
17The Test Program and Input
18--------------------------
19
20We have a simple C program (dictionary.c) that reads in a text file,
21and stores all the words from the file in a Binary Search Tree, sorted
22alphabetically. It then enters a loop prompting the user for a word,
23searching for the word in the tree (using Binary Search), and reporting
24to the user whether or not it found the word in the tree.
25
26The input text file we are using to test our program contains the text
27for William Shakespeare's famous tragedy "Romeo and Juliet".
28
29The Bug
30-------
31
32When we try running our program, we find there is a problem. While it
33successfully finds some of the words we would expect to find, such as
34"love" or "sun", it fails to find the word "Romeo", which MUST be in
35the input text file:
36
37::
38
39   % ./dictionary Romeo-and-Juliet.txt
40   Dictionary loaded.
41   Enter search word: love
42   Yes!
43   Enter search word: sun
44   Yes!
45   Enter search word: Romeo
46   No!
47   Enter search word: ^D
48   %
49
50Using Depth First Search
51------------------------
52
53Our first job is to determine if the word "Romeo" actually got inserted
54into the tree or not. Since "Romeo and Juliet" has thousands of words,
55trying to examine our binary search tree by hand is completely
56impractical. Therefore we will write a Python script to search the tree
57for us. We will write a recursive Depth First Search function that
58traverses the entire tree searching for a word, and maintaining
59information about the path from the root of the tree to the current
60node. If it finds the word in the tree, it returns the path from the
61root to the node containing the word. This is what our DFS function in
62Python would look like, with line numbers added for easy reference in
63later explanations:
64
65::
66
67   1: def DFS (root, word, cur_path):
68   2:     root_word_ptr = root.GetChildMemberWithName ("word")
69   3:     left_child_ptr = root.GetChildMemberWithName ("left")
70   4:     right_child_ptr = root.GetChildMemberWithName ("right")
71   5:     root_word = root_word_ptr.GetSummary()
72   6:     end = len (root_word) - 1
73   7:     if root_word[0] == '"' and root_word[end] == '"':
74   8:         root_word = root_word[1:end]
75   9:     end = len (root_word) - 1
76   10:     if root_word[0] == '\'' and root_word[end] == '\'':
77   11:        root_word = root_word[1:end]
78   12:     if root_word == word:
79   13:         return cur_path
80   14:     elif word < root_word:
81   15:         if left_child_ptr.GetValue() == None:
82   16:             return ""
83   17:         else:
84   18:             cur_path = cur_path + "L"
85   19:             return DFS (left_child_ptr, word, cur_path)
86   20:     else:
87   21:         if right_child_ptr.GetValue() == None:
88   22:             return ""
89   23:         else:
90   24:             cur_path = cur_path + "R"
91   25:             return DFS (right_child_ptr, word, cur_path)
92
93
94Accessing & Manipulating Program Variables
95------------------------------------------
96
97Before we can call any Python function on any of our program's
98variables, we need to get the variable into a form that Python can
99access. To show you how to do this we will look at the parameters for
100the DFS function. The first parameter is going to be a node in our
101binary search tree, put into a Python variable. The second parameter is
102the word we are searching for (a string), and the third parameter is a
103string representing the path from the root of the tree to our current
104node.
105
106The most interesting parameter is the first one, the Python variable
107that needs to contain a node in our search tree. How can we take a
108variable out of our program and put it into a Python variable? What
109kind of Python variable will it be? The answers are to use the LLDB API
110functions, provided as part of the LLDB Python module. Running Python
111from inside LLDB, LLDB will automatically give us our current frame
112object as a Python variable, "lldb.frame". This variable has the type
113"SBFrame" (see the LLDB API for more information about SBFrame
114objects). One of the things we can do with a frame object, is to ask it
115to find and return its local variable. We will call the API function
116"FindVariable" on the lldb.frame object to give us our dictionary
117variable as a Python variable:
118
119::
120
121   root = lldb.frame.FindVariable ("dictionary")
122
123The line above, executed in the Python script interpreter in LLDB, asks the
124current frame to find the variable named "dictionary" and return it. We then
125store the returned value in the Python variable named "root". This answers the
126question of HOW to get the variable, but it still doesn't explain WHAT actually
127gets put into "root". If you examine the LLDB API, you will find that the
128SBFrame method "FindVariable" returns an object of type SBValue. SBValue
129objects are used, among other things, to wrap up program variables and values.
130There are many useful methods defined in the SBValue class to allow you to get
131information or children values out of SBValues. For complete information, see
132the header file SBValue.h. The SBValue methods that we use in our DFS function
133are ``GetChildMemberWithName()``, ``GetSummary()``, and ``GetValue()``.
134
135
136Explaining DFS Script in Detail
137-------------------------------
138
139Before diving into the details of this code, it would be best to give a
140high-level overview of what it does. The nodes in our binary search tree were
141defined to have type ``tree_node *``, which is defined as:
142
143::
144
145   typedef struct tree_node
146   {
147      const char *word;
148      struct tree_node *left;
149      struct tree_node *right;
150   } tree_node;
151
152Lines 2-11 of DFS are getting data out of the current tree node and getting
153ready to do the actual search; lines 12-25 are the actual depth-first search.
154Lines 2-4 of our DFS function get the word, left and right fields out of the
155current node and store them in Python variables. Since root_word_ptr is a
156pointer to our word, and we want the actual word, line 5 calls GetSummary() to
157get a string containing the value out of the pointer. Since GetSummary() adds
158quotes around its result, lines 6-11 strip surrounding quotes off the word.
159
160Line 12 checks to see if the word in the current node is the one we are
161searching for. If so, we are done, and line 13 returns the current path.
162Otherwise, line 14 checks to see if we should go left (search word comes before
163the current word). If we decide to go left, line 15 checks to see if the left
164pointer child is NULL ("None" is the Python equivalent of NULL). If the left
165pointer is NULL, then the word is not in this tree and we return an empty path
166(line 16). Otherwise, we add an "L" to the end of our current path string, to
167indicate we are going left (line 18), and then recurse on the left child (line
16819). Lines 20-25 are the same as lines 14-19, except for going right rather
169than going left.
170
171One other note: Typing something as long as our DFS function directly into the
172interpreter can be difficult, as making a single typing mistake means having to
173start all over. Therefore we recommend doing as we have done: Writing your
174longer, more complicated script functions in a separate file (in this case
175tree_utils.py) and then importing it into your LLDB Python interpreter.
176
177
178The DFS Script in Action
179------------------------
180
181At this point we are ready to use the DFS function to see if the word "Romeo"
182is in our tree or not. To actually use it in LLDB on our dictionary program,
183you would do something like this:
184
185::
186
187   % lldb
188   (lldb) process attach -n "dictionary"
189   Architecture set to: x86_64.
190   Process 521 stopped
191   * thread #1: tid = 0x2c03, 0x00007fff86c8bea0 libSystem.B.dylib`read$NOCANCEL + 8, stop reason = signal SIGSTOP
192   frame #0: 0x00007fff86c8bea0 libSystem.B.dylib`read$NOCANCEL + 8
193   (lldb) breakpoint set -n find_word
194   Breakpoint created: 1: name = 'find_word', locations = 1, resolved = 1
195   (lldb) continue
196   Process 521 resuming
197   Process 521 stopped
198   * thread #1: tid = 0x2c03, 0x0000000100001830 dictionary`find_word + 16
199   at dictionary.c:105, stop reason = breakpoint 1.1
200   frame #0: 0x0000000100001830 dictionary`find_word + 16 at dictionary.c:105
201   102 int
202   103 find_word (tree_node *dictionary, char *word)
203   104 {
204   -> 105 if (!word || !dictionary)
205   106 return 0;
206   107
207   108 int compare_value = strcmp (word, dictionary->word);
208   (lldb) script
209   Python Interactive Interpreter. To exit, type 'quit()', 'exit()' or Ctrl-D.
210   >>> import tree_utils
211   >>> root = lldb.frame.FindVariable ("dictionary")
212   >>> current_path = ""
213   >>> path = tree_utils.DFS (root, "Romeo", current_path)
214   >>> print path
215   LLRRL
216   >>> ^D
217   (lldb)
218
219The first bit of code above shows starting lldb, attaching to the dictionary
220program, and getting to the find_word function in LLDB. The interesting part
221(as far as this example is concerned) begins when we enter the script command
222and drop into the embedded interactive Python interpreter. We will go over this
223Python code line by line. The first line
224
225::
226
227   import tree_utils
228
229
230imports the file where we wrote our DFS function, tree_utils.py, into Python.
231Notice that to import the file we leave off the ".py" extension. We can now
232call any function in that file, giving it the prefix "tree_utils.", so that
233Python knows where to look for the function. The line
234
235::
236
237   root = lldb.frame.FindVariable ("dictionary")
238
239
240gets our program variable "dictionary" (which contains the binary search tree)
241and puts it into the Python variable "root". See Accessing & Manipulating
242Program Variables in Python above for more details about how this works. The
243next line is
244
245::
246
247   current_path = ""
248
249This line initializes the current_path from the root of the tree to our current
250node. Since we are starting at the root of the tree, our current path starts as
251an empty string. As we go right and left through the tree, the DFS function
252will append an 'R' or an 'L' to the current path, as appropriate. The line
253
254::
255
256   path = tree_utils.DFS (root, "Romeo", current_path)
257
258calls our DFS function (prefixing it with the module name so that Python can
259find it). We pass in our binary tree stored in the variable root, the word we
260are searching for, and our current path. We assign whatever path the DFS
261function returns to the Python variable path.
262
263Finally, we want to see if the word was found or not, and if so we want to see
264the path through the tree to the word. So we do
265
266::
267
268   print path
269
270From this we can see that the word "Romeo" was indeed found in the tree, and
271the path from the root of the tree to the node containing "Romeo" is
272left-left-right-right-left.
273
274Using Breakpoint Command Scripts
275--------------------------------
276
277We are halfway to figuring out what the problem is. We know the word we are
278looking for is in the binary tree, and we know exactly where it is in the
279binary tree. Now we need to figure out why our binary search algorithm is not
280finding the word. We will do this using breakpoint command scripts.
281
282The idea is as follows. The binary search algorithm has two main decision
283points: the decision to follow the right branch; and, the decision to follow
284the left branch. We will set a breakpoint at each of these decision points, and
285attach a Python breakpoint command script to each breakpoint. The breakpoint
286commands will use the global path Python variable that we got from our DFS
287function. Each time one of these decision breakpoints is hit, the script will
288compare the actual decision with the decision the front of the path variable
289says should be made (the first character of the path). If the actual decision
290and the path agree, then the front character is stripped off the path, and
291execution is resumed. In this case the user never even sees the breakpoint
292being hit. But if the decision differs from what the path says it should be,
293then the script prints out a message and does NOT resume execution, leaving the
294user sitting at the first point where a wrong decision is being made.
295
296Python Breakpoint Command Scripts Are Not What They Seem
297--------------------------------------------------------
298
299What do we mean by that? When you enter a Python breakpoint command in LLDB, it
300appears that you are entering one or more plain lines of Python. BUT LLDB then
301takes what you entered and wraps it into a Python FUNCTION (just like using the
302"def" Python command). It automatically gives the function an obscure, unique,
303hard-to-stumble-across function name, and gives it two parameters: frame and
304bp_loc. When the breakpoint gets hit, LLDB wraps up the frame object where the
305breakpoint was hit, and the breakpoint location object for the breakpoint that
306was hit, and puts them into Python variables for you. It then calls the Python
307function that was created for the breakpoint command, and passes in the frame
308and breakpoint location objects.
309
310So, being practical, what does this mean for you when you write your Python
311breakpoint commands? It means that there are two things you need to keep in
312mind: 1. If you want to access any Python variables created outside your
313script, you must declare such variables to be global. If you do not declare
314them as global, then the Python function will treat them as local variables,
315and you will get unexpected behavior. 2. All Python breakpoint command scripts
316automatically have a frame and a bp_loc variable. The variables are pre-loaded
317by LLDB with the correct context for the breakpoint. You do not have to use
318these variables, but they are there if you want them.
319
320The Decision Point Breakpoint Commands
321--------------------------------------
322
323This is what the Python breakpoint command script would look like for the
324decision to go right:
325
326::
327
328   global path
329   if path[0] == 'R':
330      path = path[1:]
331      thread = frame.GetThread()
332      process = thread.GetProcess()
333      process.Continue()
334   else:
335      print "Here is the problem; going right, should go left!"
336   Just as a reminder, LLDB is going to take this script and wrap it up in a function, like this:
337
338
339   def some_unique_and_obscure_function_name (frame, bp_loc):
340      global path
341      if path[0] == 'R':
342         path = path[1:]
343         thread = frame.GetThread()
344         process = thread.GetProcess()
345         process.Continue()
346      else:
347         print "Here is the problem; going right, should go left!"
348
349LLDB will call the function, passing in the correct frame and breakpoint
350location whenever the breakpoint gets hit. There are several things to notice
351about this function. The first one is that we are accessing and updating a
352piece of state (the path variable), and actually conditioning our behavior
353based upon this variable. Since the variable was defined outside of our script
354(and therefore outside of the corresponding function) we need to tell Python
355that we are accessing a global variable. That is what the first line of the
356script does. Next we check where the path says we should go and compare it to
357our decision (recall that we are at the breakpoint for the decision to go
358right). If the path agrees with our decision, then we strip the first character
359off of the path.
360
361Since the decision matched the path, we want to resume execution. To do this we
362make use of the frame parameter that LLDB guarantees will be there for us. We
363use LLDB API functions to get the current thread from the current frame, and
364then to get the process from the thread. Once we have the process, we tell it
365to resume execution (using the Continue() API function).
366
367If the decision to go right does not agree with the path, then we do not resume
368execution. We allow the breakpoint to remain stopped (by doing nothing), and we
369print an informational message telling the user we have found the problem, and
370what the problem is.
371
372Actually Using The Breakpoint Commands
373--------------------------------------
374
375Now we will look at what happens when we actually use these breakpoint commands
376on our program. Doing a source list -n find_word shows us the function
377containing our two decision points. Looking at the code below, we see that we
378want to set our breakpoints on lines 113 and 115:
379
380::
381
382   (lldb) source list -n find_word
383   File: /Volumes/Data/HD2/carolinetice/Desktop/LLDB-Web-Examples/dictionary.c.
384   101
385   102 int
386   103 find_word (tree_node *dictionary, char *word)
387   104 {
388   105   if (!word || !dictionary)
389   106     return 0;
390   107
391   108   int compare_value = strcmp (word, dictionary->word);
392   109
393   110   if (compare_value == 0)
394   111     return 1;
395   112   else if (compare_value < 0)
396   113     return find_word (dictionary->left, word);
397   114   else
398   115     return find_word (dictionary->right, word);
399   116 }
400   117
401
402
403So, we set our breakpoints, enter our breakpoint command scripts, and see what happens:
404
405::
406
407   (lldb) breakpoint set -l 113
408   Breakpoint created: 2: file ='dictionary.c', line = 113, locations = 1, resolved = 1
409   (lldb) breakpoint set -l 115
410   Breakpoint created: 3: file ='dictionary.c', line = 115, locations = 1, resolved = 1
411   (lldb) breakpoint command add -s python 2
412   Enter your Python command(s). Type 'DONE' to end.
413   > global path
414   > if (path[0] == 'L'):
415   >     path = path[1:]
416   >     thread = frame.GetThread()
417   >     process = thread.GetProcess()
418   >     process.Continue()
419   > else:
420   >     print "Here is the problem. Going left, should go right!"
421   > DONE
422   (lldb) breakpoint command add -s python 3
423   Enter your Python command(s). Type 'DONE' to end.
424   > global path
425   > if (path[0] == 'R'):
426   >     path = path[1:]
427   >     thread = frame.GetThread()
428   >     process = thread.GetProcess()
429   >     process.Continue()
430   > else:
431   >     print "Here is the problem. Going right, should go left!"
432   > DONE
433   (lldb) continue
434   Process 696 resuming
435   Here is the problem. Going right, should go left!
436   Process 696 stopped
437   * thread #1: tid = 0x2d03, 0x000000010000189f dictionary`find_word + 127 at dictionary.c:115, stop reason = breakpoint 3.1
438   frame #0: 0x000000010000189f dictionary`find_word + 127 at dictionary.c:115
439      112   else if (compare_value < 0)
440      113     return find_word (dictionary->left, word);
441      114   else
442   -> 115     return find_word (dictionary->right, word);
443      116 }
444      117
445      118 void
446   (lldb)
447
448
449After setting our breakpoints, adding our breakpoint commands and continuing,
450we run for a little bit and then hit one of our breakpoints, printing out the
451error message from the breakpoint command. Apparently at this point in the
452tree, our search algorithm decided to go right, but our path says the node we
453want is to the left. Examining the word at the node where we stopped, and our
454search word, we see:
455
456::
457
458   (lldb) expr dictionary->word
459   (const char *) $1 = 0x0000000100100080 "dramatis"
460   (lldb) expr word
461   (char *) $2 = 0x00007fff5fbff108 "romeo"
462
463So the word at our current node is "dramatis", and the word we are searching
464for is "romeo". "romeo" comes after "dramatis" alphabetically, so it seems like
465going right would be the correct decision. Let's ask Python what it thinks the
466path from the current node to our word is:
467
468::
469
470   (lldb) script print path
471   LLRRL
472
473According to Python we need to go left-left-right-right-left from our current
474node to find the word we are looking for. Let's double check our tree, and see
475what word it has at that node:
476
477::
478
479   (lldb) expr dictionary->left->left->right->right->left->word
480   (const char *) $4 = 0x0000000100100880 "Romeo"
481
482So the word we are searching for is "romeo" and the word at our DFS location is
483"Romeo". Aha! One is uppercase and the other is lowercase: We seem to have a
484case conversion problem somewhere in our program (we do).
485
486This is the end of our example on how you might use Python scripting in LLDB to
487help you find bugs in your program.
488
489Source Files for The Example
490----------------------------
491
492The complete code for the Dictionary program (with case-conversion bug), the
493DFS function and other Python script examples (tree_utils.py) used for this
494example are available below.
495
496tree_utils.py - Example Python functions using LLDB's API, including DFS
497
498::
499
500   """
501   # ===-- tree_utils.py ---------------------------------------*- Python -*-===//
502   #
503   #                     The LLVM Compiler Infrastructure
504   #
505   # This file is distributed under the University of Illinois Open Source
506   # License. See LICENSE.TXT for details.
507   #
508   # ===---------------------------------------------------------------------===//
509
510   tree_utils.py  - A set of functions for examining binary
511   search trees, based on the example search tree defined in
512   dictionary.c.  These functions contain calls to LLDB API
513   functions, and assume that the LLDB Python module has been
514   imported.
515
516   For a thorough explanation of how the DFS function works, and
517   for more information about dictionary.c go to
518   http://lldb.llvm.org/scripting.html
519   """
520
521
522   def DFS(root, word, cur_path):
523      """
524      Recursively traverse a binary search tree containing
525      words sorted alphabetically, searching for a particular
526      word in the tree.  Also maintains a string representing
527      the path from the root of the tree to the current node.
528      If the word is found in the tree, return the path string.
529      Otherwise return an empty string.
530
531      This function assumes the binary search tree is
532      the one defined in dictionary.c  It uses LLDB API
533      functions to examine and traverse the tree nodes.
534      """
535
536      # Get pointer field values out of node 'root'
537
538      root_word_ptr = root.GetChildMemberWithName("word")
539      left_child_ptr = root.GetChildMemberWithName("left")
540      right_child_ptr = root.GetChildMemberWithName("right")
541
542      # Get the word out of the word pointer and strip off
543      # surrounding quotes (added by call to GetSummary).
544
545      root_word = root_word_ptr.GetSummary()
546      end = len(root_word) - 1
547      if root_word[0] == '"' and root_word[end] == '"':
548         root_word = root_word[1:end]
549      end = len(root_word) - 1
550      if root_word[0] == '\'' and root_word[end] == '\'':
551         root_word = root_word[1:end]
552
553      # Main depth first search
554
555      if root_word == word:
556         return cur_path
557      elif word < root_word:
558
559         # Check to see if left child is NULL
560
561         if left_child_ptr.GetValue() is None:
562               return ""
563         else:
564               cur_path = cur_path + "L"
565               return DFS(left_child_ptr, word, cur_path)
566      else:
567
568         # Check to see if right child is NULL
569
570         if right_child_ptr.GetValue() is None:
571               return ""
572         else:
573               cur_path = cur_path + "R"
574               return DFS(right_child_ptr, word, cur_path)
575
576
577   def tree_size(root):
578      """
579      Recursively traverse a binary search tree, counting
580      the nodes in the tree.  Returns the final count.
581
582      This function assumes the binary search tree is
583      the one defined in dictionary.c  It uses LLDB API
584      functions to examine and traverse the tree nodes.
585      """
586      if (root.GetValue is None):
587         return 0
588
589      if (int(root.GetValue(), 16) == 0):
590         return 0
591
592      left_size = tree_size(root.GetChildAtIndex(1))
593      right_size = tree_size(root.GetChildAtIndex(2))
594
595      total_size = left_size + right_size + 1
596      return total_size
597
598
599   def print_tree(root):
600      """
601      Recursively traverse a binary search tree, printing out
602      the words at the nodes in alphabetical order (the
603      search order for the binary tree).
604
605      This function assumes the binary search tree is
606      the one defined in dictionary.c  It uses LLDB API
607      functions to examine and traverse the tree nodes.
608      """
609      if (root.GetChildAtIndex(1).GetValue() is not None) and (
610               int(root.GetChildAtIndex(1).GetValue(), 16) != 0):
611         print_tree(root.GetChildAtIndex(1))
612
613      print root.GetChildAtIndex(0).GetSummary()
614
615      if (root.GetChildAtIndex(2).GetValue() is not None) and (
616               int(root.GetChildAtIndex(2).GetValue(), 16) != 0):
617         print_tree(root.GetChildAtIndex(2))
618
619
620dictionary.c - Sample dictionary program, with bug
621
622::
623
624   //===-- dictionary.c ---------------------------------------------*- C -*-===//
625   //
626   //                     The LLVM Compiler Infrastructure
627   //
628   // This file is distributed under the University of Illinois Open Source
629   // License. See LICENSE.TXT for details.
630   //
631   //===---------------------------------------------------------------------===//
632   #include <ctype.h>
633   #include <stdio.h>
634   #include <stdlib.h>
635   #include <string.h>
636
637   typedef struct tree_node {
638   const char *word;
639   struct tree_node *left;
640   struct tree_node *right;
641   } tree_node;
642
643   /* Given a char*, returns a substring that starts at the first
644      alphabet character and ends at the last alphabet character, i.e. it
645      strips off beginning or ending quotes, punctuation, etc. */
646
647   char *strip(char **word) {
648   char *start = *word;
649   int len = strlen(start);
650   char *end = start + len - 1;
651
652   while ((start < end) && (!isalpha(start[0])))
653      start++;
654
655   while ((end > start) && (!isalpha(end[0])))
656      end--;
657
658   if (start > end)
659      return NULL;
660
661   end[1] = '\0';
662   *word = start;
663
664   return start;
665   }
666
667   /* Given a binary search tree (sorted alphabetically by the word at
668      each node), and a new word, inserts the word at the appropriate
669      place in the tree.  */
670
671   void insert(tree_node *root, char *word) {
672   if (root == NULL)
673      return;
674
675   int compare_value = strcmp(word, root->word);
676
677   if (compare_value == 0)
678      return;
679
680   if (compare_value < 0) {
681      if (root->left != NULL)
682         insert(root->left, word);
683      else {
684         tree_node *new_node = (tree_node *)malloc(sizeof(tree_node));
685         new_node->word = strdup(word);
686         new_node->left = NULL;
687         new_node->right = NULL;
688         root->left = new_node;
689      }
690   } else {
691      if (root->right != NULL)
692         insert(root->right, word);
693      else {
694         tree_node *new_node = (tree_node *)malloc(sizeof(tree_node));
695         new_node->word = strdup(word);
696         new_node->left = NULL;
697         new_node->right = NULL;
698         root->right = new_node;
699      }
700   }
701   }
702
703   /* Read in a text file and storea all the words from the file in a
704      binary search tree.  */
705
706   void populate_dictionary(tree_node **dictionary, char *filename) {
707   FILE *in_file;
708   char word[1024];
709
710   in_file = fopen(filename, "r");
711   if (in_file) {
712      while (fscanf(in_file, "%s", word) == 1) {
713         char *new_word = (strdup(word));
714         new_word = strip(&new_word);
715         if (*dictionary == NULL) {
716         tree_node *new_node = (tree_node *)malloc(sizeof(tree_node));
717         new_node->word = new_word;
718         new_node->left = NULL;
719         new_node->right = NULL;
720         *dictionary = new_node;
721         } else
722         insert(*dictionary, new_word);
723      }
724   }
725   }
726
727   /* Given a binary search tree and a word, search for the word
728      in the binary search tree.  */
729
730   int find_word(tree_node *dictionary, char *word) {
731   if (!word || !dictionary)
732      return 0;
733
734   int compare_value = strcmp(word, dictionary->word);
735
736   if (compare_value == 0)
737      return 1;
738   else if (compare_value < 0)
739      return find_word(dictionary->left, word);
740   else
741      return find_word(dictionary->right, word);
742   }
743
744   /* Print out the words in the binary search tree, in sorted order.  */
745
746   void print_tree(tree_node *dictionary) {
747   if (!dictionary)
748      return;
749
750   if (dictionary->left)
751      print_tree(dictionary->left);
752
753   printf("%s\n", dictionary->word);
754
755   if (dictionary->right)
756      print_tree(dictionary->right);
757   }
758
759   int main(int argc, char **argv) {
760   tree_node *dictionary = NULL;
761   char buffer[1024];
762   char *filename;
763   int done = 0;
764
765   if (argc == 2)
766      filename = argv[1];
767
768   if (!filename)
769      return -1;
770
771   populate_dictionary(&dictionary, filename);
772   fprintf(stdout, "Dictionary loaded.\nEnter search word: ");
773   while (!done && fgets(buffer, sizeof(buffer), stdin)) {
774      char *word = buffer;
775      int len = strlen(word);
776      int i;
777
778      for (i = 0; i < len; ++i)
779         word[i] = tolower(word[i]);
780
781      if ((len > 0) && (word[len - 1] == '\n')) {
782         word[len - 1] = '\0';
783         len = len - 1;
784      }
785
786      if (find_word(dictionary, word))
787         fprintf(stdout, "Yes!\n");
788      else
789         fprintf(stdout, "No!\n");
790
791      fprintf(stdout, "Enter search word: ");
792   }
793
794   fprintf(stdout, "\n");
795   return 0;
796   }
797
798
799The text for "Romeo and Juliet" can be obtained from the Gutenberg Project
800(http://www.gutenberg.org).
801
802