1  // COVTOOL -- test coverage analysis tool.
2  // Copyright (C) 2002, Lowell Boggs Jr.
3  // mailto:lowell.boggs@attbi.com
4 
5  // This file contains free software.  You can redistribute it
6  // and/or modify it under the terms of the GNU General Public License
7  // as published by the Free Software Foundation; either version 2, or
8  // (at your option) any later version.
9 
10  // This source code is distributed in the hope that it will be useful,
11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  // GNU General Public License for more details.
14 
15  // Write to the Free Software Foundation, 59 Temple Place - Suite 330,
16  // Boston, MA  02111-1307, USA for a copy of the GNU General Public License.
17  //
18 
19 
20  //
21  // This is the instrumentor for the covtool.exe program.  It parses a
22  // C++ source file that has already been through the C preprocessor and
23  // adds additional function calls and data declarations such that when your
24  // program runs it will make function calls that will create a database
25  // indicating which lines of code in your program were executed and which
26  // were not.  See covtoolhelper.c for implementations of the datastructures
27  // and functions injected by this program.
28  //
29  // The parser only recognizes the big picture parts of the C++ language.
30  // It is a recursive descent parser that knows about namespaces, classes,
31  // structs, unions, enums, variable declarations, functions, statements, etc.
32  // It does not know about expressions -- a statement is either one of
33  // the structured statements (if, while, do, for, switch, case) or it is
34  // a sequence of tokens ending in ;.
35  //
36  // The parser injects a call to CvT_record_line() before the beginning of
37  // every executable statement -- and one extra at the beginning of every
38  // function.  See function 'instrument_line()' below.
39  //
40  // Also, you can instruct the parser to skip all files beginning with a
41  // given prefix.  Normally the prefix will be just '/' because you will
42  // probably want to ignore the C++ source header files -- since they will
43  // detract from your code test coverage numbers and you don't want to have to
44  // write full tests for them.  See the -skip option.
45  //
46  #include <fstream>
47  #include <covstream.h>
48  #include <ctype.h>
49  #include <map>
50  #include <set>
51  #include <string>
52  #include <list>
53  #include <vector>
54  #include <stdlib.h>
55 
56  // the following block must not be separated, it defines the
57  // version string
58  char const*
59  #include "covtool_version"
60  ;
61  // do not separate this from the #include of covtool_version!
62 
63  typedef CovStream::iterator iterator;
64  typedef CovStream::token    token;
65 
66  int line_number=1;
67  int depth=0;
68 
69  char const *parse_state[1000];
70 
71 
72  #ifdef DEBUGGING
73      static char buffer[256];
74      static char *current_token = buffer;
75    #define DEBUG_FLUSH()  cout.flush()
76  #else
77    #define DEBUG_FLUSH()
78  #endif
79 
80  token get_token(iterator start, iterator end);
81 
82  // the names of runtime objects injected during instrumentation
83 
84  char const *rt_file_name_table    = "__CvT__file_name____";
85  char const *rt_record_line        = "__CvT__record_line____";
86  char const *rt_logged_lines_table = "__CvT__logged_lines___";
87  char const *rt_instrumented_lines = "__CvT__instrumented_lines___";
88 
89  char const *rt_module_info        =
90  "struct __CvT__module_info____                                   \n"
91  "{                                                               \n"
92  "    char const **file;                                          \n"
93  "    int files;                                                  \n"
94  "    void const **str;   // string version of file names above   \n"
95  "    int *  line;  // pairs of ints stored here                  \n"
96  "                  // the first is the index into file           \n"
97  "                  // the second is the line number in that file \n"
98  "    int lines;    // count of PAIRS not ints at line            \n"
99  "                                                                \n"
100  "    int struct_size;    // validates this struct                \n"
101  "    int * inst;   // counts of instrumented lines per file      \n"
102  "                                                                \n"
103  "    void construct();                                           \n"
104  "                                                                \n"
105  "    __CvT__module_info____(char const **f, int fs,              \n"
106  "                           int   *l, int ls,                    \n"
107  "                           int *i                               \n"
108  "                          )                                     \n"
109  "    : file(f), files(fs), str(0), line(l), lines(ls),           \n"
110  "      struct_size(sizeof(__CvT__module_info____)), inst(i)      \n"
111  "    {                                                           \n"
112  "      construct();                                              \n"
113  "    }                                                           \n"
114  "                                                                \n"
115  "    ~__CvT__module_info____();                                  \n"
116  "                                                                \n"
117  "};\n";
118 
119 
120  // The instrument time variables keeping track of files, lines, etc
121 
122  typedef map<string,int>          Files_t;
123  typedef Files_t::iterator        Files_Iterator_t;
124  typedef list<Files_t::iterator>  Ordered_Files_t;
125  typedef set< pair<int,int> >     Logged_Lines_t;
126 
127  bool                             instrumenting=false;
128  bool                             skipping=false;
129  string                           skip_prefix;  // one for the moment
130  list<string>                     skip_prefixes;
131  Files_t                          files;
132  Ordered_Files_t                  files_in_order;
133  token                            current_file_name;
134  string                           current_file_string;
135  int                              current_file_number;
136  Logged_Lines_t                   logged_lines;
137 
138  bool is_skipped(token filename)
139 +{
140 +  list<string>::iterator il;
141 
142 +  for(il = skip_prefixes.begin(); il != skip_prefixes.end(); ++il)
143    {
144 +    string const &skipped_path = *il;
145 
146 +    iterator               ifl = filename.first;
147 +    string::const_iterator ipn  = skipped_path.begin();
148 
149 +    while(*ifl == *ipn              &&
150           ifl != filename.second    &&
151           ipn != skipped_path.end()
152          )
153      {
154 +      ++ifl;
155 +      ++ipn;
156      }
157 
158 +    if(ipn == skipped_path.end())
159      {
160 +      return true;
161      }
162    }
163 
164 +  return false;
165 
166  }
167 
168 
169  void record_current_file_name() // records 'current_file_name'
170 +{
171 
172 +  iterator i = current_file_name.first;
173 
174 +  current_file_string.resize(0);
175 
176 +  while(i != current_file_name.second)
177    {
178 +    current_file_string += *i++;
179    }
180 
181    // cerr << "filename string is '" << current_file_string << endl;
182 
183 +  Files_Iterator_t tmp = files.find(current_file_string);
184 
185 +  if(tmp == files.end())
186    {
187      // file not already added
188 
189      //cerr << "it is new!" << endl;
190 
191 +    current_file_number = files.size();
192 
193 +    map<string,int>::value_type v(current_file_string, current_file_number);
194 
195 +    Files_Iterator_t tmp = files.insert(v).first;
196 
197 +    files_in_order.push_back(tmp);
198 
199    }
200    else
201    {
202      // cerr << "it is old!" << endl;
203 +    current_file_number = tmp->second;
204    }
205 
206 
207  }
208 
209 
210  void instrument_beginning()
211 +{
212 +  if(!instrumenting)
213 +    return;
214 
215 +  cout << "# 1 \"Instrumentation code beginning\"" << endl;
216 
217 +  cout << rt_module_info << endl;
218 
219 +  cout << "static __CvT__module_info____* __CvT_miT;" << endl;
220 
221 +  cout << "extern void "
222         << rt_record_line
223         << "(__CvT__module_info____ const*, int,int);"
224         << endl;
225 
226 +  cout.flush();
227  }
228 
229  void instrument_ending()
230 +{
231 +  if(!instrumenting)
232 +    return;
233 
234 +  cout << "# 1 \"Instrumentation code ending\"" << endl;
235 
236 +  cout << "static char const *" << rt_file_name_table << "[]={" << endl;
237 
238 +  int instrumented_files = files_in_order.size();
239 
240 +  if(instrumented_files == 0)
241 -    instrumented_files=1;
242 
243 +  vector<int> instrumented_lines(instrumented_files,0);
244 
245 +  Ordered_Files_t::iterator first, last;
246 
247 +  for(first = files_in_order.begin(),
248        last  = files_in_order.end();
249        first != last;
250        ++first
251       )
252    {
253 +    Files_Iterator_t i = *first;
254 
255 +    cout << CovStream::fullname(i->first) << "," << endl;
256    }
257 
258 +  cout << "0};" << endl;
259 
260 +  Logged_Lines_t::iterator ll_first, ll_last;
261 
262 +  ll_first = logged_lines.begin();
263 +  ll_last  = logged_lines.end();
264 
265 +  cout << "static int " << rt_logged_lines_table << "[] = {" << endl;
266 
267 +  while(ll_first != ll_last)
268    {
269 +    Logged_Lines_t::value_type const &r = *ll_first++;
270 
271 +    ++instrumented_lines[r.first];  // for each ref, increment the
272         			    // count of instrumented lines
273         			    // associated with the corresponding
274         			    // file
275 
276 +    cout << r.first << "," << r.second << "," << endl;
277 
278    }
279 
280 +  cout << "0};" << endl;
281 
282 +  cout << "static int " << rt_instrumented_lines << "[]={" << endl;
283    {
284 +    vector<int>::iterator i;
285 
286 +    for(i=instrumented_lines.begin();
287         i != instrumented_lines.end();
288         ++i
289         )
290      {
291 +      cout << *i << "," << endl;
292      }
293    }
294 +  cout << "0};" << endl;
295 
296 +  cout << "static __CvT__module_info____"
297         << endl
298         << "__CvT_miTBODY("
299         << endl
300         << "              "
301         <<  rt_file_name_table
302         <<    ","
303         << endl
304         <<     "              sizeof("
305         <<     rt_file_name_table
306         <<     ")/sizeof(char*) -1,"
307         << endl << "              "
308         <<  rt_logged_lines_table
309         <<    ","
310         <<    endl
311         <<    "              (sizeof("
312         <<    rt_logged_lines_table
313         <<    ")/sizeof(int) -1) / 2"
314         << endl
315         << "              ," << rt_instrumented_lines
316         << ");"
317         << endl;
318 
319 +  cout <<
320     "static __CvT__module_info____* __CvT_miTINIT = __CvT_miT = &__CvT_miTBODY;"
321     << endl;
322 
323 
324 +  cerr.flush();  // not sure why this needed but without these
325 +  cout.flush();  // too lines, icov.exe crashes in static destruction.
326         	 // Could be a bug in my code that will get fixed
327         	 // later, or could be a subtle issues in the
328         	 // destruction of objects in the covtoolhelper.c
329 
330  }
331 
332  void instrument_line()
333 +{
334 +  if(!instrumenting)
335 +    return;
336 
337 +  if(skipping)
338 -    return;
339 
340 +  pair<int,int> line_ref(current_file_number, line_number);
341 
342 +  Logged_Lines_t::iterator i = logged_lines.find(line_ref);
343 
344 +  if(i == logged_lines.end())
345    {
346 +    logged_lines.insert(line_ref);
347 
348 +    cout << rt_record_line
349          << "(__CvT_miT,"
350          << current_file_number
351          << ","
352          << line_number
353          << ");" ;
354    }
355 
356  }
357 
358  token process_declaration(token t, iterator end);
359 
360  int main(int argc, char **argv)
361 -{
362  #ifdef COVTOOL_INST
363 -  extern void CvT_StartRecording____();
364 -  CvT_StartRecording____();
365 
366  #endif
367 
368 +  --argc;
369 +  ++argv;
370 
371 +  string dash_instrument("-instrument");
372 +  string dash_skip("-skip");
373 +  string dash_v("-v");
374 
375 +  while(argc)
376    {
377 +    if(dash_v == *argv)
378      {
379 -      cout << covtool_version << endl;
380 -      exit(1);
381      }
382      else
383 +    if(dash_instrument == *argv)
384      {
385 +      instrumenting = true;
386      }
387      else
388 +    if(dash_skip == *argv)
389      {
390 +      --argc;
391 +      ++argv;
392 
393 +      skip_prefixes.push_back(string("\"") + *argv);
394 
395      }
396      else
397      {
398 -      cerr << "covtool:  invalid command line parameter '" << *argv << endl;
399 -      exit(1);
400      }
401 
402 +    if(argc)
403      {
404 +      --argc;
405 +      ++argv;
406      }
407    }
408 
409 +  instrument_beginning();
410 
411 +  CovStream s(stdin);
412 
413 +  iterator end  = s.end();
414 
415 +  parse_state[0] = "file";
416 
417 +  token t = get_token(s.begin(), s.end());
418 
419 +  do
420    {
421 +    t = process_declaration(t, end);
422    }
423    while(t.first != end);
424 
425 
426 +  cout << endl;
427 
428 +  instrument_ending();
429 
430 
431  }
432 
433  void output_token(iterator start, iterator end);
434 
435  token get_token(iterator start, iterator end)
436 +{
437    //
438    // parse tokens and handle default behaviors thereof.
439    // All parsing algorithms, except main(), assume that
440    // the first token to be parsed has already been gathered
441    // and is presented as the first thing to look at.  It will
442    // not have been copied to stdout yet though.
443    //
444 
445 +  token t(start,start);
446 
447 +  static int first_time=1;
448 
449 +  if(first_time)
450    {
451 +    first_time = 0;
452 
453 +    if(!instrumenting)
454      {
455 +      cout << "/*"
456            << parse_state[depth]
457            << "\t"
458            << 1
459            << "\t:"
460            << depth
461            << "\t*/";
462 
463        DEBUG_FLUSH();
464      }
465    }
466 
467 +  for(;;)
468    {
469 
470 +    t = CovStream::parse_token(t.second,end);
471 
472 
473  #ifdef DEBUGGING
474 
475      // so I can the stupid token in the debugger!
476 
477 
478      {
479         char *scan = buffer;
480         iterator input = t.first;
481 
482         while(input != t.second)
483           *scan++ = *input++;
484 
485         *scan = 0;
486 
487      }
488 
489 
490  #endif
491 
492 
493 +    if(t.first == end)
494 +      break;
495 
496 +    if(*t.first == '\n')
497      {
498 +      ++line_number;
499 
500 +      if(!instrumenting)
501        {
502 +       cout << endl
503              << "/*"
504              << parse_state[depth]
505              << "\t"
506              << line_number
507              << "\t:"
508              << depth
509              << "\t*/";
510 
511         DEBUG_FLUSH();
512        }
513        else
514 +       cout << endl;
515      }
516      else
517 +    if(*t.first == '#')
518      {
519 +      output_token(t.first, t.second);    // the #
520 
521 +      t = CovStream::parse_token(t.second,end);  // the line number
522 
523 +      if(t.first == end)
524 -       break;  // wierd stuff at end of file
525 
526 
527 
528 +      output_token(t.first, t.second);
529 
530 +      if( isdigit(*t.first) )
531        {
532         // this is a line number pragma
533 
534 +       char buffer[256];
535 
536 +       iterator scan = t.first;
537 
538 +       char *cp = buffer;
539 
540 +       while(scan != t.second)
541 +         *cp++ = *scan++;
542 
543 +       *cp=0;
544 
545 +       sscanf(buffer, "%d", &line_number);
546 
547 +       t = CovStream::parse_token(t.second, end);  // the filename
548 
549 +       if(t.first != end && *t.first != '\n')
550 +         output_token(t.first, t.second);
551 
552 +       current_file_name = t;
553 
554 +       record_current_file_name();
555 
556 +       skipping = is_skipped(t);
557 
558 
559 +       while(t.first != end && *t.first != '\n')
560         {
561 +         t = CovStream::parse_token(t.second, end);
562 
563 +         if(t.first != end && *t.first != '\n')
564 +           output_token(t.first, t.second);
565         }
566 
567 +       if(!instrumenting)
568         {
569 +         cout << endl
570                << "/*"
571                << parse_state[depth]
572                << "\t"
573                << line_number
574                << "\t:"
575                << depth
576                << "\t*/";
577           DEBUG_FLUSH();
578         }
579         else
580 +         cout << endl;
581 
582        }
583        else
584        {
585         // #pragma or other such nonesene
586 
587 +       do
588         {
589 +         t = get_token(t.second, end);
590 +         output_token(t.first, t.second);
591         }
592         while(t.first != end && *t.first != ';');
593 
594        }
595 
596      }
597      else
598      {
599 +      break;
600      }
601 
602    }
603 
604 
605 +  return t;
606 
607  }
608 
609  void output_token(iterator start, iterator end)
610 +{
611 +  while(start != end) cout << *start++;
612 +  cout << " ";
613 
614    DEBUG_FLUSH();
615 
616  }
617 
618  bool compare_token(iterator first, iterator last, char const *s)
619 +{
620 +  while(first != last && *s && *first == *s)
621    {
622 +    ++first;
623 +    ++s;
624    }
625 
626 +  if(first == last && *s == 0)
627 +    return true;
628 
629 +  return false;
630 
631  }
632 
633  token process_class     (token start, iterator end, char const *scope="class");
634  token process_enum      (token start, iterator end);
635  token process_template  (token start, iterator end);
636  token process_typedef   (token start, iterator end);
637  token process_function  (token start, iterator end);
638 
639  token process_parenthetical(token t, iterator end);
640  token process_statement    (token t, iterator end);
641 
642  token process_declaration(token t, iterator end)
643 +{
644    //
645    // process C++ declarations by copying them to stdout
646    // and add instrumentation as needed.
647 
648 +  if(t.first == end || *t.first == ';' || *t.first == '}')
649 +    output_token(t.first,t.second);
650    else
651    {
652 
653 +    if(compare_token(t.first, t.second, "extern"))
654      {
655 +      token save = t;
656 
657 +      t = get_token(t.second, end);
658 
659 +      if(t.first != end && *t.first == '"')
660        {
661 +       output_token(save.first, save.second);
662 +       output_token(t.first, t.second);
663 
664 +       t = get_token(t.second, end);
665 
666 +       if(t.first != end)
667         {
668 +         if(*t.first == '{')
669           {
670 +            ++depth; parse_state[depth]="extrn";
671 
672 +            output_token(t.first, t.second);
673 
674 +            t = get_token(t.second, end);
675 +            do
676              {
677 +              t = process_declaration(t, end);
678              }
679              while(t.first != end && *t.first != '}');
680 
681 +            output_token(t.first, t.second);
682 
683 +            --depth;
684 
685 +            t = get_token(t.second, end);
686 
687 +            return t;
688 
689 
690           }
691           else
692           {
693 +           return process_declaration(t,end);
694           }
695         }
696        }
697        else
698        {
699 +       output_token(save.first, save.second);
700 +       return process_declaration(t,end);
701        }
702 
703 
704      }
705      else
706 +    if(compare_token(t.first, t.second, "struct") ||
707         compare_token(t.first, t.second, "class")  ||
708         compare_token(t.first, t.second, "union")  ||
709         compare_token(t.first, t.second, "namespace")
710        )
711      {
712 +      char const *id="class";
713 
714 +      if(*t.first == 's')
715 +       id = "strct";
716        else
717 +      if(*t.first == 'u')
718 +       id = "union";
719        else
720 +      if(*t.first == 'n')
721 +       id = "namsp";
722 
723 +      return process_class(t, end,id);
724      }
725      else
726 +    if(compare_token(t.first, t.second, "enum") )
727      {
728 +      return process_enum(t, end);
729      }
730      else
731 +    if(
732         compare_token(t.first, t.second, "public")    ||
733         compare_token(t.first, t.second, "private")   ||
734         compare_token(t.first, t.second, "protected")
735        )
736      {
737        // eat the class section separators
738 
739 +      output_token(t.first, t.second);
740 +      t = get_token(t.second, end);
741 +      output_token(t.first, t.second);
742 +      return get_token(t.second, end);
743      }
744      else
745 +    if(compare_token(t.first, t.second, "template") )
746      {
747 +      return process_template(t, end);
748      }
749      else
750 +    if(compare_token(t.first, t.second, "namespace") )
751      {
752 -      return process_class(t, end, "namsp");
753      }
754      else
755 +    if(compare_token(t.first, t.second, "catch") )
756      {
757 +      output_token(t.first, t.second);
758 +      t = get_token(t.second, end);
759 
760 +      t = process_parenthetical(t, end);
761 
762 +      return process_statement(t,end);
763 
764      }
765      else
766 +    if(compare_token(t.first, t.second, "typedef") ||
767         compare_token(t.first, t.second, "using")
768        )
769      {
770 +      return process_typedef(t, end);
771      }
772      else
773 +    if(isalnum(*t.first) || *t.first == '_')
774      {
775 +      return process_function(t,end);
776      }
777      else
778      {
779 +      output_token(t.first,t.second);
780      }
781    }
782 
783 +  return get_token(t.second, end);
784  }
785 
786  token process_class(token t, iterator end, char const *scope)
787 +{
788 +  output_token(t.first, t.second);   // class, struct, union, etc
789 
790    // scan until the '{' or the ';'
791 
792 +  while(t.first != end)
793    {
794 +    t = get_token(t.second,end);
795 
796 +    if(t.first == end || *t.first == ';' || *t.first == '{')
797 +      break;
798 
799 +    output_token(t.first, t.second);
800    }
801 
802 +  if(t.first != end && *t.first != ';')
803    {
804      // process class body. Output the { and get the next token
805 
806 +    output_token(t.first, t.second);
807 +    ++depth; parse_state[depth] = scope;  // usually class but maybe namespace
808 
809 +    t = get_token(t.second,end);
810 
811 +    while(t.first != end && *t.first != '}')
812      {
813 
814 +      t = process_declaration(t, end);  // prints the trailing '}' if any
815 
816      }
817 
818 +    output_token(t.first, t.second);
819 +    --depth;
820 +    t = get_token(t.second, end);
821 
822 
823    }
824 
825 
826 +  return t;
827 
828  }
829 
830 
831  token process_enum(token t, iterator end)
832 +{
833 +  output_token(t.first, t.second);
834 
835 +  while(t.first != end)
836    {
837 +    t = get_token(t.second,end);
838 
839 +    output_token(t.first, t.second);
840 
841 +    if(t.first == end)
842 -      break;
843 
844 +    if(*t.first == ';' )
845 -      return get_token(t.second, end);
846 
847 +    if(*t.first == '{' )
848 +      break;
849 
850    }
851 
852 +  if(t.first != end)
853    {
854 +    ++depth; parse_state[depth]="enum";
855 
856 +    while(t.first != end)
857      {
858 +      t = get_token(t.second,end);
859 
860 +      output_token(t.first, t.second);
861 
862 +      if(t.first == end || *t.first == '}')
863        {
864 +       t = get_token(t.second, end);
865 +       break;
866        }
867      }
868 +    --depth;
869    }
870 
871 
872 +  return t;
873 
874  }
875 
876 
877  token process_template(token t, iterator end)
878 +{
879 +  output_token(t.first, t.second);
880 
881 +  int angledepth = 0;
882 
883 +  while(t.first != end)
884    {
885 +    t = get_token(t.second,end);
886 
887 +    output_token(t.first, t.second);
888 
889 +    if(t.first == end)
890 -      break;
891 
892 +    if(compare_token(t.first, t.second, "<>"))
893 +      return process_declaration(get_token(t.second, end), end);
894 
895 +    if(*t.first == '<')
896 +      ++angledepth;
897      else
898 +    if(*t.first == '>')
899      {
900 +      if(angledepth == 1)
901        {
902 +       token t =  process_declaration(get_token(t.second, end), end);
903 
904 +       return t;
905        }
906 +      --angledepth;
907      }
908 
909    }
910 
911 -  return token(end,end);
912 
913  }
914 
915  token process_till_semicolon(token t, iterator end)
916  //
917  // consume tokens until a semicolon is encountered -- return the
918  // token after the semicolon.  Do not walk past '}' -- I forgeet
919  // why -- but handle nested {} pairs.
920  //
921 +{
922 +  int braces=0;
923 
924 +  do
925    {
926 +    t = get_token(t.second, end);
927 
928 +    if(t.first == end)
929 -      return t;
930 
931 +    if(*t.first == '{')
932 +      ++braces;
933      else
934 +    if(*t.first == '}')
935 +      if(braces == 0)
936 -       return t;
937        else
938 +       --braces;
939 
940 +    output_token(t.first, t.second);
941    }
942    while(t.first != end && *t.first != ';');
943 
944 +  return get_token(t.second, end);
945 
946  }
947 
948  token process_typedef(token t, iterator end)
949 +{
950    // process typedef, namespace, and using statements at file scope
951 
952 +  output_token(t.first, t.second);
953 
954 +  t = get_token(t.second, end);
955 
956 +  if( compare_token(t.first, t.second, "struct") ||
957        compare_token(t.first, t.second, "class")  ||
958        compare_token(t.first, t.second, "union")
959      )
960      {
961 +      return process_class(t,end);
962      }
963 
964 +  output_token(t.first, t.second);
965 
966 +  return process_till_semicolon(t, end);
967 
968  }
969 
970 
971 
972  token process_function(token t, iterator end)
973 +{
974    // process function and variable declarations
975 
976    // copy the type info out and scarf the variable name
977    // up to the ()'s.
978 
979 +  output_token(t.first, t.second);
980 
981 +  do
982    {
983      // handle various wierd syntax cases including a pointer to a
984      // function declaration type (*name)(parms)
985      // so we have to handle multiple () groups before the 'const' or '='
986      // or { that defines what this darn thing is.
987 
988 +    t = get_token(t.second, end);
989 
990 +    if(t.first != end && compare_token(t.first, t.second, "operator") )
991      {
992        // oops, operators can be '=' and that will screw up the
993        // logic of detecting that this is a variable assignment not a
994        // function body.
995 
996 +      output_token(t.first, t.second);
997 
998 +      t = get_token(t.second, end);
999 
1000        // we now have the operator being defined but we don't
1001        // want it to be processed either
1002 
1003 +      output_token(t.first, t.second);
1004 
1005 +      t = get_token(t.second, end); // get the paren after the operator
1006 
1007 
1008      }
1009 
1010 
1011 +    if(t.first != end && *t.first == '(')
1012      {
1013 +      t = process_parenthetical(t, end);
1014 
1015 +      if(t.first != end &&
1016          compare_token(t.first, t.second, "const")
1017         )
1018        {
1019 +       output_token(t.first, t.second);
1020 +       t = get_token(t.second, end);
1021        }
1022 
1023 +      if(t.first != end &&
1024          compare_token(t.first, t.second, "try")
1025         )
1026        {
1027 +       output_token(t.first, t.second);
1028 +       t = get_token(t.second, end);
1029        }
1030 
1031 
1032      }
1033 
1034 +    output_token(t.first, t.second);
1035    }
1036    while(t.first != end   &&
1037         *t.first != ';'  &&
1038         *t.first != '='  &&
1039         *t.first != '{'
1040         );
1041 
1042 +  if(t.first == end || *t.first == ';')
1043 +    return get_token(t.second, end);
1044 
1045 +  if(*t.first == '=' )
1046 +    return  process_till_semicolon(t, end);
1047 
1048    // must be a '{'
1049 
1050 
1051 +  ++depth; parse_state[depth] = "func";
1052 
1053 +  instrument_line();
1054 
1055 
1056 +  t = get_token(t.second, end);
1057 
1058 +  while(t.first != end && *t.first != '}')
1059    {
1060 +    t = process_statement(t, end);
1061    }
1062 
1063 +  output_token(t.first, t.second);
1064 
1065 +  --depth;
1066 
1067 +  return get_token(t.second, end);
1068 
1069  }
1070 
1071  token process_if(token t, iterator end);
1072  token process_while(token t, iterator end);
1073  token process_do(token t, iterator end);
1074  token process_for(token t, iterator end);
1075  token process_switch(token t, iterator end);
1076  token process_try(token t, iterator end);
1077 
1078  token process_statement(token t, iterator end)
1079 +{
1080 +  if(t.first == end)
1081    {
1082 -    return t;
1083    }
1084 
1085 +  if(*t.first == ';')
1086    {
1087 +    output_token(t.first, t.second);
1088 +    return get_token(t.second, end);
1089    }
1090    else
1091 +  if(*t.first == '{')
1092    {
1093 +    ++depth; parse_state[depth] = "block";
1094 
1095 +    output_token(t.first, t.second);
1096 +    t = get_token(t.second,end);
1097 
1098 +    while( t.first != end && *t.first != '}' )
1099      {
1100 +      t = process_statement(t,end);
1101      }
1102 
1103 +    output_token(t.first, t.second);
1104 
1105 +    --depth;
1106 
1107 +    return get_token(t.second, end);
1108 
1109    }
1110    else
1111 +  if(compare_token(t.first, t.second, "if") )
1112    {
1113 +    instrument_line();
1114 +    return process_if(t, end);
1115    }
1116    else
1117 +  if(compare_token(t.first, t.second, "case") )
1118    {
1119 +    output_token(t.first, t.second);  // case
1120 
1121 +    do
1122      {
1123 +      t = get_token(t.second, end);
1124 
1125 +      output_token(t.first, t.second);  // label
1126      }
1127      while(t.first != end && !compare_token(t.first,t.second,":"));
1128 
1129 +    return process_statement(get_token(t.second, end), end);
1130    }
1131    else
1132 +  if(compare_token(t.first, t.second, "default") )
1133    {
1134 +    output_token(t.first, t.second);  // default
1135 
1136 +    t = get_token(t.second, end);
1137 
1138 +    output_token(t.first, t.second);  // :
1139 
1140 +    return process_statement(get_token(t.second, end), end);
1141    }
1142    else
1143 +  if(compare_token(t.first, t.second, "while") )
1144    {
1145 +    instrument_line();
1146 +    return process_while(t, end);
1147    }
1148    else
1149 +  if(compare_token(t.first, t.second, "do") )
1150    {
1151 +    instrument_line();
1152 +    return process_do(t, end);
1153    }
1154    else
1155 +  if(compare_token(t.first, t.second, "for") )
1156    {
1157 +    instrument_line();
1158 +    return process_for(t, end);
1159    }
1160    else
1161 +  if(compare_token(t.first, t.second, "switch") )
1162    {
1163 +    instrument_line();
1164 +    return process_switch(t, end);
1165    }
1166    else
1167 +  if(compare_token(t.first, t.second, "enum") )
1168    {
1169 -    instrument_line();
1170 -    return process_enum(t, end);
1171    }
1172    else
1173 +  if(compare_token(t.first, t.second, "try") )
1174    {
1175 +    instrument_line();
1176 +    return process_try(t, end);
1177    }
1178    else
1179 +  if(compare_token(t.first, t.second, "typedef") ||
1180       compare_token(t.first, t.second, "using")   ||
1181       compare_token(t.first, t.second, "namespace")
1182      )
1183    {
1184 +    return process_typedef(t, end);
1185    }
1186    else
1187 +  if(compare_token(t.first, t.second, "class") ||
1188       compare_token(t.first, t.second, "struct")||
1189       compare_token(t.first, t.second, "union")
1190      )
1191    {
1192 -    instrument_line();
1193 -    return process_declaration(t, end);
1194    }
1195    else
1196    {
1197 +    instrument_line();
1198 
1199 
1200  #if 0
1201      t = process_typedef(t, end);
1202  #else
1203 +    output_token(t.first, t.second);
1204 +    t = process_till_semicolon(t,end);
1205 
1206  #endif
1207 
1208 +    return t;
1209    }
1210  }
1211 
1212 
1213  token process_parenthetical(token t, iterator end)
1214 +{
1215    // scoop up a parenthesized expression and return the first
1216    // token after the trailing ).
1217 
1218 +  output_token(t.first, t.second);
1219 
1220 +  t = get_token(t.second, end);
1221 
1222 +  int pdepth=1;
1223 
1224 +  for(;;)
1225    {
1226 +    if(t.first == end)
1227 -      break;
1228 
1229 
1230 +    if(*t.first == ')')
1231      {
1232 +      --pdepth;
1233 
1234 +      if(pdepth == 0)
1235 +       break;
1236 
1237      }
1238      else
1239 +    if(*t.first == '(')
1240      {
1241 +      ++pdepth;
1242      }
1243 
1244 +    output_token(t.first, t.second);
1245 +    t = get_token(t.second, end);
1246 
1247    }
1248 
1249 +  output_token(t.first, t.second);
1250 +  return get_token(t.second, end);
1251 
1252  }
1253 
1254 
1255  token process_if(token t, iterator end)
1256 +{
1257    //handle 'if' statements and the else clause
1258 
1259 +  output_token(t.first, t.second);
1260 
1261 +  t = process_parenthetical(get_token(t.second, end),end);
1262 
1263 +  if(instrumenting) cout << "{";
1264 
1265 +  t = process_statement(t, end);
1266 
1267 +  if(instrumenting) cout << "}";
1268 
1269 +  if(compare_token(t.first, t.second, "else"))
1270    {
1271 +     output_token(t.first, t.second);
1272 
1273 +     if(instrumenting) cout << "{";
1274 +     t = process_statement(get_token(t.second, end), end);
1275 +     if(instrumenting) cout << "}";
1276    }
1277 
1278 +  return t;
1279 
1280  }
1281 
1282  token process_while(token t, iterator end)
1283 +{
1284    //handle 'while' statements
1285 
1286 +  output_token(t.first, t.second);
1287 
1288 +  t = process_parenthetical(get_token(t.second, end),end);
1289 
1290 +  if(instrumenting) cout << "{";
1291 +  t = process_statement(t, end);
1292 +  if(instrumenting) cout << "}";
1293 
1294 +  return t;
1295 
1296  }
1297 
1298  token process_do(token t, iterator end)
1299 +{
1300    //handle 'do' statements
1301 
1302 +  output_token(t.first, t.second);  // print do, get first token of statement
1303 
1304 +  if(instrumenting) cout << "{";
1305 +  t = process_statement(get_token(t.second,end), end);
1306 +  if(instrumenting) cout << "}";
1307 
1308    // t should be 'while' here
1309 
1310 +  output_token(t.first, t.second);
1311 
1312 +  t = process_parenthetical(get_token(t.second, end),end);
1313 +  return t;
1314 
1315  }
1316 
1317  token process_for(token t, iterator end)
1318 +{
1319    //handle for statements
1320 
1321 +  output_token(t.first, t.second);
1322 
1323 +  t = process_parenthetical( get_token(t.second, end), end);
1324 
1325 +  if(instrumenting) cout << "{";
1326 
1327 +  t =  process_statement(t, end);
1328 
1329 +  if(instrumenting) cout << "}";
1330 
1331 +  return t;
1332 
1333 
1334 
1335  }
1336 
1337  token process_switch(token t, iterator end)
1338 +{
1339    //handle switch statements
1340 
1341 +  output_token(t.first, t.second);
1342 
1343 +  t = process_parenthetical( get_token(t.second, end), end);
1344 
1345 +  return process_statement(t, end);
1346 
1347  }
1348 
1349 
1350  token process_try(token t, iterator end)
1351 +{
1352 +  output_token(t.first, t.second);
1353 
1354 +  t = process_statement(get_token(t.second, end), end);
1355 
1356 +  while(t.first != end && compare_token(t.first, t.second, "catch"))
1357    {
1358 +    output_token(t.first, t.second);  // catch
1359 
1360 +    t = process_parenthetical(get_token(t.second, end), end);
1361 
1362 +    t = process_statement(t, end);
1363 
1364    }
1365 
1366 +  return t;
1367 
1368  }
1369 
1370