home *** CD-ROM | disk | FTP | other *** search
/ The C Users' Group Library 1994 August / wc-cdrom-cusersgrouplibrary-1994-08.iso / vol_300 / 329_01 / cflow.doc < prev    next >
Text File  |  1989-10-18  |  9KB  |  130 lines

  1. cFlow Operating Instructions
  2. (C) October 18  1989  Asaf Arkin
  3. All rights reserved
  4.  
  5.  
  6. C How Your Code Flows
  7.  
  8. The CUG library includes, amongst many, one cFlow utility. As Kenji Hino pointed out in his column, this utility is far from perfect -- far from the UNIX version, that is. He, therefore, made the proposition that one of CUJ's readers write a replica of the UNIX tool. I took that challange, and in this article am proud to present my implementation of the cFlow programming tool.
  9.  
  10.  
  11. Strictly speaking, what cFlow does is read a number of C source files and process them to produce a collective report of function dependencies. Put in plainer language, it shows you which function calls which and where you can find it, from a set of files you supply it with.
  12.  
  13. cFlow scans one or more source files, openning include files: from the listings it extracts the function definitions and invokations it requires. Having properly digested all files, cFlow dumps the dependency tree. The tree, like all structured material, is indented: a function's definition is followed by a list of all the functions it calls, slightly indented to the right; each called function is followed by a list of all the functions it calls, slightly further indented, and so on. To illustrate the point, observe listing 1, from which cFlow produces listing 2.
  14.  
  15. The functions a certain function calls are shown only once for that function, but may appear in the definition of others, including their own (what is termed recursion). For each definition, the filename, as provided by you or an #include directive, prints along with the line number. No filename or line number appear for undefined functions. In fact, functions lacking definition (and a declaration does not count as one) are not at all in the report; unless you order cFlow to, in which case undefined functions will print, and may even constitute a separate list (the -u option.)
  16.  
  17.  
  18. The Syntax Of cFlow
  19.  
  20. The syntax of cFlow is as follows
  21.  
  22.      cFlow  [<file> | -<options>]..
  23.  
  24. where options is
  25.  
  26.      <options>:   i | m <func> | o <file> | u
  27.  
  28. As you can see, cFlow accepts a number of filenames from the command line. Inbetween the filenames can come options. Their order of appearance has no relevance. Their effect is discussed next.
  29.  
  30. Specifying the -i option tells cFlow to open and read #include files referred to in the source code. This way you don't have to name them all at the command line. The -m option is followed by the name of a function: cFlow will start the dependency tree with that function, instead of main. The -o option directs all output to the named file or output destination. And the -u option, appearing once, requests that undefined functions be included in the dependency tree. Appearing twice, undefined functions will also constitute their own list.
  31.  
  32. cFlow will complain if it cannot open a source file, but still produce output from the files it is able to read. Two command line examples are
  33.  
  34.      cFlow myprog1.c -i myprog2.c myprog3.c -uu
  35.      cFlow graph.c  -m DrawObject -o flow.out
  36.  
  37.  
  38. cFlow Flaws
  39.  
  40. As much as I wanted to, I could not create a perfect cFlow. For that, the utility must understand C in full, making it too long and complex for a magazine article. For its simplicity, cFlow pays in three features:
  41.  
  42. --   cFlow does no macro expansion, or indeed any preprocessor activities, except for reading include files. If you define or invoke functions with macros, you will need a preprocessor to work on your code before passing it to cFlow: there are some public domain preprocessors available; some compilers come with an independent preprocessor for just such cases.
  43.  
  44. --   typedef is strange to cFlow, and so are such defined functions. Since handling typedef requires that cFlow master a large portion of C, I chose not to. If you typedef a function's parameters list, you've bad luck; if only the return value is typedefed, the output will come out correct.
  45.  
  46. --   cFlow gets confused with pointers to functions, and functions returning pointers to functions or arrays. This is because such definitions include two sets of parentheses, which cFlow cannot distinguish. Consider
  47.  
  48.      int (*FuncPtr)(a,b,c);
  49.      int (*FuncPtr(a,b,c))[];
  50.  
  51. In the second case, you may typedef the return value:
  52.  
  53.      typedef int  Array_Int[];
  54.      Array_Int  *FuncPtr(a,b,c);
  55.  
  56. N.B.: It is your obligation to assure the correctness of the source code: cFlow finds it difficult to handle faulty listings.
  57.  
  58.  
  59. Implementation Notes 1
  60.  
  61. To sustain the dependency tree in memory, cFlow must handle a multitude of memory blocks. The following paragraphs discuss them each.
  62.  
  63. FileBase/FileLen -- FileBase points to the start of a memory block FileLen is the length of. The File block holds the name of all source files so definitions can refer to them.
  64.  
  65. IDBase/IDLen -- cFlow gathers identifier names from the source code, stacking them in ID one after the other. Non-identifier characters cause ID to flush, or in case of an openning parentheses ('('), convince cFlow that ID contains the name of a function.
  66.  
  67. NameBase/NameLen -- The Name block holds successive names of all functions (whether invoked, declared or defined) found in the source code: a function's name is the last name on ID at the time of reading a '('. New names are appended to the end of Name; no function name is stored more than once.
  68.  
  69. ListBase/ListLen -- For each Name entry, there is a corresponding entry in List. A List entry is a structure of type strList, holding one offset and one pointer: the offset indexes the function's name within Name; the pointer designates a memory block of type Func.
  70.  
  71. When a function is encountered for the first time, its identifier appends to Name, and a matching entry appends to List. Also allocated is a memory block, holding a structure of type strFunc, to which the List entry points. The Func block contains information relevant to the function's definition: whether the function was defined; a pointer to the definition or declaration text (yet another memory block); the definition's line number and filename; and offsets to the invoked functions' List entry.
  72.  
  73.  
  74. Implementation Notes 2
  75.  
  76. The function responsible for reading source files and building the dependency tree is Process. Process accepts a filename, appending its name to File and openning it for reading. Characters read one at a time with GetChar: this function ignores \NL sequences, and treats whitespaces, line delimiters and comments as spaces. Process scans the source file, skipping literal strings and character constants, counting left and right braces and stacking ID with identifiers. The braces count (variable Level) can tell if a function is defined (Level equals zero) or invoked (more than zero).
  77.  
  78. Upon reaching a '(', cFlow assumes the contents of ID is a function name and calls DoFunc. DoFunc first checks the name is not a C token (eg, return (5);). If not, ScanFunc is called, returning a pointer to the function's Func block; if previously inexistent, ScanFunc will create the Func block, as well as Name and List entries. That done, DoFunc moves on to assemble the function's definition: the formal parameters are read and affixed to the return value located in ID.
  79.  
  80. If Level is zero, cFlow checks what follows the parameters list: a ';' or ',' signals a declaration -- the definition is accepted but the function is not considered defined; for any other character, cFlow assumes a definition. It sets the line number and filename, and skips characters up to the first '{' (or: skips the formal parameters of an unprototyped function.)
  81.  
  82. If Level is more than zero, the function is invoked: the offset to its List entry is added to the Calls list of the currently defined function. This list later serves to appoint branches and leaves to the dependency tree.
  83.  
  84. Once the dependency tree is complete, the printing phase commences: DumpFlow prints the definition of main (or any other particular function you specify), the definition of all other defined functions, and if the -u option appeared twice, a list of all undefined functions.
  85.  
  86. DumpFunc prints the function definitions: it prints all available information about a given function (name, definition, line number, filename) and then lists the functions it calls using itself (referring to the Calls list.) This recursion accounts for the tree's indentation.
  87.  
  88.  
  89. Ending Remarks
  90.  
  91. As you have seen, cFlow is quite a powerful tool. It can show you programs from a perspective not possible otherwise. By letting you inspect function dependencies, cFlow assists in developing, debugging and reading programs -- your own or other's -- saving you hours of paging to and fro through C listings.
  92.  
  93.  
  94.  
  95.  
  96.      void  main(void)
  97.      {
  98.        a();
  99.        b();
  100.      }
  101.  
  102.      void  a(void)
  103.      {
  104.        a();
  105.        b();
  106.      }
  107.  
  108.      void  b(void)
  109.      {
  110.        c();
  111.      }
  112.  
  113.      void  c(void)
  114.      {
  115.      }
  116.  
  117. Listing 1: Example of C source code
  118.  
  119.  
  120.      main: void (void),  <file1 1>
  121.        a: void (void),  <file1 7>
  122.          a: void (void),  <file1 7>
  123.          b: void (void),  <file1 13>
  124.            c: void (void),  <file1 18>
  125.        b: void (void),  <file1 13>
  126.  
  127. Listing 2: The report cFlow generates from listing 1
  128.  
  129.  
  130.