;;; CFLOW.EL --- C flow graph 

;; Copyright (C) 1997 Paul Barham

;; Author: Paul Barham Paul.Barham@cl.cam.ac.uk
;; Maintainer: Paul Barham Paul.Barham@cl.cam.ac.uk
;; Created: 19 Mar 1997
;; Version: 1.0
;; Keywords: C language cxref flow static call graph browser

 
;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 1, or (at your option)
;; any later version.

;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;; GNU General Public License for more details.

;; A copy of the GNU General Public License can be obtained from this
;; program's author (send electronic mail to Paul.Barham@cl.cam.ac.uk)
;; or from the Free Software Foundation, Inc., 675 Mass Ave,
;; Cambridge, MA 02139, USA.

;; LCD Archive Entry:
;; cflow|Paul Barham|Paul.Barham@cl.cam.ac.uk
;; |C flow graph 
;; |$Date$|$Revision$|~/packages/cflow.el

;;; Commentary:

;; This package is intended to be used in conjunction with cxref
;; package (http://www.gedanken.org.uk/software/cxref/)

;; It parses the cxref.function file generated by cxref and fires up 
;; a sort of folding mode which allows the hierarchical flow graph
;; to be recursively expanded.  
;;
;; Simply find the file cxref.function in a buffer and type M-x cflow
;;
;; Then you can use:
;;
;; SPC or RETURN   Expand the current line
;; x               Close up the flow graph under the current node
;; .               Find the function on the current line in another window
;; q               Quit
;;

;;; Change log:
;; $Log$

;;; Variables:

(defvar cflow-filenames t 
  "Show filenames where each function is declared")

(defvar cflow-main "main" 
  "The name of the function at the root of the flowgraph")

(defvar cflow-indent "|   " 
  "The indentation string added per-level of the flowgraph")

;;; Code:

;;;
;;; This reads in the output of cxref 
;;;
(defun cxref-readline ()
  (interactive)
  (let (file fn scope refs 
	(eol (progn (end-of-line) (point))))
    (beginning-of-line)
    (re-search-forward "\\([^ \t]+\\)[ \t]*" eol t) 
    (setq file (buffer-substring-no-properties 
		(match-beginning 1) (match-end 1)))
    (re-search-forward "\\([^ \t]+\\)[ \t]*" eol t) 
    (setq fn   (buffer-substring-no-properties 
		(match-beginning 1) (match-end 1)))
    (re-search-forward "\\([0-9]+\\)[ \t]*" eol t) 
    (setq scope (car (read-from-string
		      (buffer-substring-no-properties 
		       (match-beginning 1) (match-end 1)))))
    (setq refs nil)
    (while (re-search-forward "\\([^ \t]+\\)[ \t]*" eol t) 
      (setq refs (cons (buffer-substring-no-properties 
			(match-beginning 1) (match-end 1)) refs)))
    (list fn file scope (nreverse refs))
  ))

(defun cxref-parse ()
  (interactive)
  (let (fns)
    (save-excursion 
      (beginning-of-buffer)
      (while (< (point) (point-max))
	(setq fns (cons (cxref-readline) fns))
	(forward-line 1))
      )
    fns)
)

(defun cflow-insert (indent fname)
  (string-match "[&%]*\\(.*\\)" fname)
  (let* ((basename (substring fname (match-beginning 1)))
	 (fun (assoc basename cflow-fns)))
    (if fun 
	(insert (concat indent 
			fname
			(if cflow-filenames
			    (concat " (" (nth 1 fun) ")")
			  "")
			"\n"))
      (insert (concat indent fname "\n")))
    ))
  
(defun cflow-expand ()
  "Expand this node in the flow graph"
  (interactive)
  (let (name indent fun)
    (save-excursion
      (beginning-of-line)
      (re-search-forward "\\(^[| ]*\\)[&%]*\\([*a-zA-Z0-9_$]+\\)")
      (setq indent (concat (buffer-substring-no-properties 
			     (match-beginning 1)
			     (match-end 1)) cflow-indent))
      (setq name (buffer-substring-no-properties 
		 (match-beginning 2)
		 (match-end 2)))
      (setq fun (assoc name cflow-fns))
      (forward-line 1)
      (beginning-of-line)
      (or (looking-at indent)
	  (mapcar (lambda (fname) 
		    (cflow-insert indent fname))
		  (nth 3 fun)))

      ))
)
(defun cflow-contract ()
  "Close up this section of the flow graph"
  (interactive)
  (let (name indent fun)
    (save-excursion
      (beginning-of-line)
      (re-search-forward "\\(^[| ]*\\)")
      (setq indent (concat (buffer-substring-no-properties 
			     (match-beginning 1)
			     (match-end 1)) cflow-indent))
      (forward-line 1)
      (beginning-of-line)
      (while (looking-at indent)
	(kill-line 1))
      ))
)  

(defun cflow-tag ()
  "Find the function on the current line using TAGS"
  (interactive)
  (let (name indent fun)
    (save-excursion
      (beginning-of-line)
      (re-search-forward "\\(^[| ]*\\)[&%]*\\([*a-zA-Z0-9_$]+\\)")
      (setq indent (concat (buffer-substring-no-properties 
			     (match-beginning 1)
			     (match-end 1)) cflow-indent))
      (setq name (buffer-substring-no-properties 
		 (match-beginning 2)
		 (match-end 2)))
      (message "Finding %s" name)
      (find-tag-other-window name)))
)

(define-derived-mode cflow-mode fundamental-mode "CFlow"
  (setq cflow-mode t)
  ;; Linemenu simply highlights the current line
  ;  (linemenu-initialize) 
)

(define-key cflow-mode-map " " 'cflow-expand)    
(define-key cflow-mode-map "" 'cflow-expand)  
(define-key cflow-mode-map "x" 'cflow-contract)    
(define-key cflow-mode-map "." 'cflow-tag)    
(define-key cflow-mode-map "q" 'bury-buffer)    

(defun cflow ()
  (interactive)
  (let ((buf (get-buffer-create "*cflow*")))
    (setq cflow-fns (cxref-parse))
    (switch-to-buffer buf)
    (cflow-mode)
    (erase-buffer)
    ;; Don't understand this :-)
    ; (make-local-variable 'cflow-fns)
    (cflow-insert "" "$")
    (cflow-insert "" cflow-main))
)

;;; CFLOW.EL ends here
