summaryrefslogtreecommitdiff
path: root/lisp/progmodes
diff options
context:
space:
mode:
authorRichard M. Stallman <rms@gnu.org>1994-02-07 05:39:56 +0000
committerRichard M. Stallman <rms@gnu.org>1994-02-07 05:39:56 +0000
commite697e5deb76f52bef05e8c98071be0ea4ccb8b1f (patch)
tree66449037f1110221e290f5632008728284081454 /lisp/progmodes
parente3768716803bc84aff1b87a44386d8fe8bbc6b37 (diff)
downloademacs-e697e5deb76f52bef05e8c98071be0ea4ccb8b1f.tar.gz
Properly check in the total rewrite installed months ago.
Diffstat (limited to 'lisp/progmodes')
-rw-r--r--lisp/progmodes/cmacexp.el507
1 files changed, 427 insertions, 80 deletions
diff --git a/lisp/progmodes/cmacexp.el b/lisp/progmodes/cmacexp.el
index dd2c6df2c67..8fa710c913f 100644
--- a/lisp/progmodes/cmacexp.el
+++ b/lisp/progmodes/cmacexp.el
@@ -1,8 +1,10 @@
-;;; cmacexp.el --- C preprocessor macro expansion
+;;; cmacexp.el --- expand C macros in a region
-;; Copyright (C) 1988 Free Software Foundation, Inc.
+;; Copyright (C) 1992 Free Software Foundation, Inc.
-;; Maintainer: FSF
+;; Author: Francesco Potorti` <pot@cnuce.cnr.it>
+;; Version: $Id: cmacexp.el 1.2 1992/09/15 11:34:56 pot Exp $
+;; Adapted-By: ESR
;; Keywords: c
;; This file is part of GNU Emacs.
@@ -23,85 +25,430 @@
;;; Commentary:
-;; This package gives you the ability to run the C macro preprocessor
-;; on the current region, expanding macros within it. This can be useful
-;; when you're not sure of the value or expansion of such macros and want
-;; to make sure they're doing what you think they're doing.
+;; In C mode C-M-x is bound to c-macro-expand. The result of the
+;; expansion is put in a separate buffer. The buffer is put in
+;; view-mode if the Inge Frick's view.el is installed. A user option
+;; allows the window displaying the buffer to be optimally sized.
;;
-;; This package supports the following option variables:
+;; When called with a C-u prefix, c-macro-expand replaces the selected
+;; region with the expansion. With two C-u's the user is offered to
+;; change the flags to the preprocessor (while the results of the
+;; expansion go to a separate buffer). Preprocessor arguments default
+;; to the last ones entered. Both the preprocessor name and the
+;; initial flag defaults can be set by the user. Setting
+;; c-macro-always-prompt to a non-nil value allows one to be always
+;; prompted for the flags, regardless of the prefix used.
+
+;; A c-macro-expansion function is provided for non-interactive use.
+;; A still experimental function c-macro-eval is provided. It aims at
+;; evaluating the contents of a region by using calc (by Dave
+;; Gillespie). Select a region and type C-x C-e (if you followed the
+;; suggestions in the INSTALLATION section) or type M-x c-ma RET v
+;; RET. If you have calc installed, the computed value of the
+;; expression will appear in the message area. If you give an
+;; interactive C-u prefix the computed value will be shown in signed,
+;; unsigned, hex and boolean representations. Two C-u's allow to
+;; change the preprocessor flags via prompt. c-macro-eval works well
+;; for constant expressions, but see the BUG section.
+
+;; A patch to calc 2.02 has been written by Dave Gillespie. It can
+;; be downloaded via anonymous ftp at fly.cnuce.cnr.it:pub/calc.diff.
+
+;; INSTALLATION ======================================================
+
+;; Put this file on your load-path, byte compile it for increased
+;; speed and put part or all of the following in your ~/.emacs file.
+
+;; To make a directory ~/emacs be in front of your load-path:
+;;(setq load-path (cons (expand-file-name "~/emacs") load-path))
+;;
+;; Suggested keybindings (work only in c-mode):
+;;(define-key c-mode-map "\C-\M-x" 'c-macro-expand)
+;;(define-key c-mode-map "\C-x\C-e" 'c-macro-eval)
+;;
+;; If you want the *Macroexpansion* window to be not higher than
+;; necessary:
+;;(setq c-macro-shrink-window-p t)
;;
-;; c-macro-preprocessor --- program to be used for macro expansion.
-;; c-macro-options --- command-line options to pass it.
+;; If you use a preprocessor other than /lib/cpp (be careful to set a
+;; -C option or equivalent in order to make the preprocessor not to
+;; strip the comments):
+;;(setq c-macro-preprocessor "gpp -C")
+;;
+;; If you often use a particular set of flags, and want them to be
+;; the default:
+;;(setq c-macro-default-cppflags "-I /usr/include/local -DDEBUG"
+;;
+;; If you always want the "Preprocessor arguments: " prompt,
+;; regardless of the arguments provided:
+;;(setq c-macro-always-prompt-p t)
+;;
+;; If you want to experiment with the C constant expressions
+;; evaluation feature:
+;;(autoload 'c-macro-eval "cmacexp"
+;; "C constant expressions evaluation. Requires calc. Experimental." t)
+
+;; BUG REPORTS =======================================================
+
+;; Please report bugs, suggestions, complaints and so on to
+;; pot@cnuce.cnr.it (Francesco Potorti`).
+
+;; IMPROVEMENTS OVER emacs 18.xx cmacexp.el ==========================
+
+;; - A lot of user visible changes. See above.
+;; - #line directives are inserted, so __LINE__ and __FILE__ are
+;; correctly expanded. Works even with START inside a string, a
+;; comment or a region #ifdef'd away by cpp. cpp is invoked with -C,
+;; making comments visible in the expansion.
+;; - All work is done in core memory, no need for temporary files.
+;; - The /lib/cpp process is run synchronously. This fixes an
+;; infinite loop bug on Motorola Delta (cpp waiting forever for
+;; end-of-file, don't know why). Fixes a similar intermittent
+;; problem on SunOS 4.1.
+
+;; ACKNOWLEDGEMENTS ==================================================
+
+;; A lot of thanks to Don Maszle who did a great work of testing, bug
+;; reporting and suggestion of new features, to Inge Fricks for her
+;; help with view.el and to Dave Gillespie for his suggestions on
+;; calc's use. This work has been partially inspired by Don Maszle
+;; and Jonathan Segal's.
+
+;; By the way, I recommend you Inge Frick's view.el. It works like
+;; the standard view, but *it is not recursive* and has some more
+;; commands. Moreover it is a minor mode, so you preserve all your
+;; major mode keybindings (well, not always :). Mail me to obtain a
+;; copy, or get it by anonymous ftp in fly.cnuce.cnr.it:pub/view.el.
+
+;; BUGS ==============================================================
+
+;; calc 2.02 does not handle the C operators "->", ".", "*" (as a
+;; prefix), the composite assignement operators "+=" etc. It cannot
+;; handle the "," operator and will be confused by ";". Almost all
+;; these can be defined as no-ops using the Calc's Syntax Tables
+;; feature. The built-in calc functions will cause problems in
+;; certain circumstances. c-macro-eval behaves correctly only on
+;; expressions not containing such operators. Does not distinguish
+;; among integer and real division.
+
+;; If the start point of the region is inside a macro definition the
+;; macro expansion is often inaccurate.
;;; Code:
-(defvar c-macro-preprocessor "/lib/cpp"
- "*Command to be used for C preprocessing.")
-
-(defvar c-macro-options nil
- "*List of options to use in C preprocessing.
-Each string in the list becomes a separate argument to the preprocessor.
-These arguments precede the filename.
-Use the `-I' option here to specify directories for header files.")
-
-(defun c-macro-expand (beg end)
- "Display the result of expanding all C macros occurring in the region.
-The expansion is entirely correct because it uses the C preprocessor.
-You can use the variables `c-macro-preprocessor' and `c-macro-options'
-to customize how preprocessing is done, or specify header file directories
-and macros to predefine."
- (interactive "r")
- (let ((outbuf (get-buffer-create "*Macroexpansion*"))
- (tempfile "%%macroexpand%%")
- expanded
- process
- last-needed)
- (setq expanded (expand-file-name tempfile))
- (save-excursion
- (set-buffer outbuf)
- (erase-buffer))
- (setq process (apply 'start-process "macros" outbuf c-macro-preprocessor
- c-macro-options))
- (set-process-sentinel process '(lambda (&rest x)))
- (save-restriction
- (widen)
- (save-excursion
- (goto-char beg)
- (beginning-of-line)
- (setq last-needed (point))
- (if (re-search-backward "^[ \t]*#" nil t)
- (progn
- ;; Skip continued lines.
- (while (progn (end-of-line) (= (preceding-char) ?\\))
- (forward-line 1))
- ;; Skip the last line of the macro definition we found.
- (forward-line 1)
- (setq last-needed (point)))))
- (write-region (point-min) last-needed expanded nil 'nomsg)
- ;; Output comment ender in case last #-directive is inside a comment.
- ;; Also, terminate any string that we are in.
- (write-region "*//*\"*/\n" nil expanded t 'nomsg)
- (write-region beg end (concat expanded "x") nil 'nomsg)
- (process-send-string process (concat "#include \"" tempfile "\"\n"))
- (process-send-string process "\n")
- (process-send-string process (concat "#include \"" tempfile "x\"\n"))
- (process-send-eof process)
- ;; HPUX seems to want two eofs.
- (process-send-eof process))
- (while (eq (process-status process) 'run)
- (accept-process-output))
- (delete-file expanded)
- (delete-file (concat expanded "x"))
- (display-buffer outbuf)
- (save-excursion
- (set-buffer outbuf)
- (goto-char (point-max))
- (forward-line -1)
- (delete-region (point) (point-max))
- (re-search-backward "\n# 1 ")
- (forward-line 2)
- (while (eolp) (delete-char 1))
- (delete-region (point-min) (point)))
- (display-buffer outbuf)))
-
-;;; cmacexp.el ends here
+(defvar c-macro-shrink-window-p nil
+ "*Non-nil means shrink the *Macroexpansion* window to fit its contents.")
+
+(defvar c-macro-always-prompt-p nil
+ "*Non-nil means always prompt for preprocessor arguments.")
+
+(defvar c-macro-preprocessor "/lib/cpp -C" "\
+The preprocessor used by the cmacexp package.
+
+If you change this, be sure to preserve the -C (don't strip comments)
+option, or to set an equivalent one.")
+
+(defvar c-macro-default-cppflags ""
+ "Default cpp flags used by c-macro-expand.")
+
+(defconst c-macro-buffer-name "*Macroexpansion*")
+
+(defun c-macro-expand (start end &optional flag) "\
+Expand all C macros occurring in the region using c-macro-preprocessor.
+Normally display output in temp buffer.
+Prefix arg means replace the region with it.
+Prompt for a string of arguments to the preprocessor, (e.g.
+-DDEBUG -I ./include) when prefixed with two C-u's.
+
+It is intended for interactive use only.
+For non interactive use, see the c-macro-expansion function."
+
+ (interactive "r\nP")
+ (let* ((subst (and flag (not (equal flag '(16)))))
+ (inbuf (current-buffer))
+ (displaybuf (if subst
+ (get-buffer c-macro-buffer-name)
+ (get-buffer-create c-macro-buffer-name)))
+ (expansion ""))
+ ;; Build the command string.
+ (if (or c-macro-always-prompt-p (equal flag '(16)))
+ (setq c-macro-default-cppflags
+ (read-string "Preprocessor arguments: "
+ c-macro-default-cppflags)))
+ ;; Decide where to display output.
+ (if (and subst
+ buffer-read-only
+ (not (eq inbuf displaybuf)))
+ (progn
+ (message
+ "Buffer is read only: displaying expansion in alternate window")
+ (sit-for 2)
+ (setq subst nil)
+ (or displaybuf
+ (setq displaybuf (get-buffer-create c-macro-buffer-name)))))
+ ;; Expand the macro and output it.
+ (if (interactive-p) (message (c-macro-default-message)))
+ (setq expansion
+ (c-macro-expansion start end
+ (concat c-macro-preprocessor " "
+ c-macro-default-cppflags)))
+ (message (concat (c-macro-default-message) "done"))
+ (if subst
+ (let ((exchange (= (point) start)))
+ (delete-region start end)
+ (insert expansion)
+ (if exchange
+ (exchange-point-and-mark)))
+ (set-buffer displaybuf)
+ (setq buffer-read-only nil)
+ (buffer-flush-undo displaybuf)
+ (erase-buffer)
+ (insert expansion)
+ (set-buffer-modified-p nil)
+ (if (string= "" expansion)
+ (message "Null expansion")
+ (c-macro-display-buffer inbuf))
+ (setq buffer-read-only t)
+ (bury-buffer displaybuf))))
+
+
+;; Display the current buffer in a window which is either just large
+;; enough to contain the entire buffer, or half the size of the
+;; screen, whichever is smaller. Put the current buffer in view-mode
+;; if the Inge Frick's view-mode is installed, with buffer to return
+;; to set to RETBUF (if sensible). Do not select the new window.
+;;
+;; Several factors influence window resizing so that the window is
+;; sized optimally if it is created anew, and so that it is messed
+;; with minimally if it has been created by the user. If the window
+;; chosen for display exists already but contains something else, the
+;; window is not re-sized. If the window already contains the current
+;; buffer, it is never shrunk, but possibly expanded. Finally, if the
+;; variable c-macro-shrink-window-p is nil the window size is *never*
+;; changed.
+(defun c-macro-display-buffer (retbuf)
+
+ (goto-char (point-min))
+ (c-mode)
+ (require 'view) ;load view.el
+ (let ((oldwinheight (window-height))
+ (alreadythere ;the window was already there
+ (get-buffer-window (current-buffer)))
+ (popped nil) ;the window popped changing the layout
+ (niceview ;is this Inge Fricks's view.el?
+ (boundp 'view-kill-when-finished)))
+
+ (or alreadythere
+ (progn
+ (display-buffer (current-buffer) t)
+ (setq popped (/= oldwinheight (window-height)))))
+ (if niceview
+ (view-mode 1)) ;set view mode
+ (if (and c-macro-shrink-window-p ;user wants fancy shrinking :\)
+ (or alreadythere popped))
+ ;; Enlarge up to half screen, or shrink properly.
+ (let ((oldwin (selected-window))
+ (minheight 0)
+ (maxheight 0))
+ (save-excursion
+ (select-window (get-buffer-window (current-buffer)))
+ (setq minheight (if alreadythere
+ (window-height)
+ window-min-height))
+ (setq maxheight (/ (screen-height) 2))
+ (enlarge-window (- (min maxheight
+ (max minheight
+ (+ 2 (vertical-motion 1000000))))
+ (window-height)))
+ (goto-char (point-min))
+ (select-window oldwin))))))
+
+
+(defun c-macro-expansion (start end cppcommand) "\
+Expands the region between START and END in the current buffer using
+the shell command CPPCOMMAND (e.g. \"/lib/cpp -C -DDEBUG\"). Be sure
+to use a -C (don't strip comments) or equivalent option.
+Returns the output as a string."
+
+;; Copy the current buffer's contents to a temporary hidden buffer.
+;; Delete from END to end of buffer. Insert a preprocessor #line
+;; directive at START and after each #endif following START that are
+;; not inside a comment or a string. Put all the strings thus
+;; inserted (without the "line" substring) in a list named linelist.
+;; If START is inside a comment, prepend "*/" and append "/*" to the
+;; #line directive. If inside a string, prepend and append "\"".
+;; Preprocess the buffer contents, then look for all the lines stored
+;; in linelist starting from end of buffer. The last line so found is
+;; where START was, so return the substring from point to end of
+;; buffer.
+ (let ((inbuf (current-buffer))
+ (outbuf (get-buffer-create " *C Macro Expansion*"))
+ (filename (if (and buffer-file-name
+ (string-match (regexp-quote default-directory)
+ buffer-file-name))
+ (substring buffer-file-name (match-end 0))
+ (buffer-name)))
+ (linenum 0)
+ (linelist ()))
+ (unwind-protect
+ (save-excursion
+ (save-restriction
+ (widen)
+ (set-buffer outbuf)
+ (setq buffer-read-only nil)
+ (erase-buffer)
+ (set-syntax-table c-mode-syntax-table)
+ (insert-buffer-substring inbuf 1 end))
+
+ ;; We have copied inbuf to outbuf. Point is at end of
+ ;; outbuf. Insert a space at the end, so cpp can correctly
+ ;; parse a token ending at END.
+
+ (insert " ")
+
+ ;; Now we insert the #line directives after all #endif or
+ ;; #else following START.
+ ;(switch-to-buffer outbuf) (debug) ;debugging instructions
+ (while (re-search-backward "\n#\\(endif\\|else\\)\\>" start 'move)
+ (if (equal (nthcdr 3 (parse-partial-sexp 1 (point)))
+ '(nil nil nil 0)) ;neither in string nor in
+ ;comment nor after quote
+ (progn
+ (goto-char (match-end 0))
+ (setq linenum (count-lines 1 (point)))
+ (setq linelist
+ (cons (format "\n# %d \"%s\"\n" linenum filename)
+ linelist))
+ (insert (car linelist))
+ (skip-chars-backward "^#")
+ (insert "line")
+ (goto-char (match-beginning 0)))))
+
+ ;; We are at START. Insert the first #line directive. This
+ ;; must work even inside a string or comment, or after a
+ ;; quote.
+ (setq linenum (+ (count-lines 1 (point))
+ (if (bolp) 1 0)))
+ (setq linelist
+ (cons
+ (let* ((startstat (parse-partial-sexp 1 start))
+ (startinstring (nth 3 startstat))
+ (startincomment (nth 4 startstat))
+ (startafterquote (nth 5 startstat)))
+ (concat (if startafterquote " ")
+ (cond (startinstring "\"") (startincomment "*/"))
+ (format "\n# %d \"%s\"\n" linenum filename)
+ (cond (startinstring "\"") (startincomment "/*"))
+ (if startafterquote "\\")))
+ linelist))
+ (insert (car linelist))
+ (skip-chars-backward "^#")
+ (insert "line")
+
+ ;; Call the preprocessor.
+ (call-process-region 1 (point-max) "sh" t t nil "-c"
+ (concat cppcommand " 2>/dev/null"))
+
+ ;; Look for the `# nn "file.c"' lines from the last to the first
+ ;; and delete them.
+ (setq linelist (reverse linelist))
+ (while (progn
+ (if (search-backward (car linelist) nil t)
+ (replace-match ""))
+ (setq linelist (cdr linelist))))
+
+ ;; Compute the return value, keeping in account the space
+ ;; inserted at the end of the buffer.
+ (buffer-substring (point) (max (point) (- (point-max) 1))))
+
+ ;; Cleanup.
+ (kill-buffer outbuf))))
+
+
+;; Experimental. With an argument, print signed, unsigned, hex and
+;; boolean representations.
+(defun c-macro-eval (start end &optional flag) "\
+Expand region using cpp and evaluate it using calc.
+Interactively print value in minibuffer and push it on the kill ring.
+With a C-u argument shows the evaluation in a variety of formats.
+With two C-u's prompts the user for a string of flags to the preprocessor.
+
+Non interactively returns value of region between START and END
+as a string. Several formats are used if optional FLAG is non-nil."
+
+ (interactive "r\nP")
+ (or (fboundp 'calc-eval)
+ (require 'calc))
+ (if (or c-macro-always-prompt-p (equal flag '(16)))
+ (setq c-macro-default-cppflags
+ (read-string "Preprocessor arguments: "
+ c-macro-default-cppflags)))
+
+ ;; Expand the region.
+ (if (interactive-p) (message (c-macro-default-message)))
+ (let ((evaluation
+ (c-macro-expansion start end
+ (concat c-macro-preprocessor " "
+ c-macro-default-cppflags)))
+ (evalbuf (get-buffer-create " *Macro Evaluation*")))
+ (unwind-protect
+ (save-excursion
+ (set-buffer evalbuf)
+ (setq buffer-read-only nil)
+ (erase-buffer)
+ (insert evaluation)
+
+ ;; Evaluate expression(s).
+ (if (interactive-p)
+ (message "Invoking calc..."))
+ (setq evaluation
+ (let ((calc-eval-error t))
+ (calc-eval (list (buffer-string) 'calc-language 'c))))
+ (erase-buffer)
+ (cond
+ (flag
+ (insert (calc-eval (list evaluation
+ 'calc-language 'c
+ 'calc-simplify-mode 'binary))
+ "(u)" " == "
+ (calc-eval (list evaluation
+ 'calc-language 'c
+ 'calc-word-size (- calc-word-size)
+ 'calc-simplify-mode 'binary))
+ "(d)" " == "
+ (calc-eval (list evaluation
+ 'calc-language 'c
+ 'calc-number-radix 16
+ 'calc-simplify-mode 'binary))
+ "(x)")
+ (save-excursion
+ (insert " == " (calc-eval (list evaluation
+ 'calc-language 'c
+ 'calc-number-radix 16
+ 'calc-simplify-mode 'binary))))
+ (while (re-search-forward "0x\\([^,]+\\)\\(, \\|\\'\\)" nil t)
+ (if (string= "0"
+ (buffer-substring (match-beginning 1)
+ (match-end 1)))
+ (replace-match "FALSE\\2")
+ (replace-match "TRUE\\2"))))
+ (t
+ (insert evaluation)))
+
+ ;; Output the evaluation.
+ (if (interactive-p)
+ (progn
+ (copy-region-as-kill 1 (point-max))
+ (message (buffer-string)))
+ (buffer-string)))
+ (kill-buffer evalbuf))))
+
+(defun c-macro-default-message ()
+ (format "Invoking %s%s%s on region..."
+ c-macro-preprocessor
+ (if (string= "" c-macro-default-cppflags) "" " ")
+ c-macro-default-cppflags))
+
+(provide 'cmacexp)
+
+;;; cmacexp.el ends here.