# HG changeset patch # User Richard M. Stallman # Date 760599596 0 # Node ID c381f562cffcaf07f0960cb68289c0aaf8471c60 # Parent b1e5e6efed1d17b95dec205edb00739fe2d3f2a9 Properly check in the total rewrite installed months ago. diff -r b1e5e6efed1d -r c381f562cffc lisp/progmodes/cmacexp.el --- a/lisp/progmodes/cmacexp.el Mon Feb 07 05:08:36 1994 +0000 +++ b/lisp/progmodes/cmacexp.el Mon Feb 07 05:39:56 1994 +0000 @@ -1,8 +1,10 @@ -;;; cmacexp.el --- C preprocessor macro expansion +;;; cmacexp.el --- expand C macros in a region + +;; Copyright (C) 1992 Free Software Foundation, Inc. -;; Copyright (C) 1988 Free Software Foundation, Inc. - -;; Maintainer: FSF +;; Author: Francesco Potorti` +;; 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. +;; +;; 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) +;; +;; 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" ;; -;; This package supports the following option variables: +;; If you always want the "Preprocessor arguments: " prompt, +;; regardless of the arguments provided: +;;(setq c-macro-always-prompt-p t) ;; -;; c-macro-preprocessor --- program to be used for macro expansion. -;; c-macro-options --- command-line options to pass it. +;; 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-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." -(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.") + (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." -(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))) +;; 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)))) -;;; cmacexp.el ends here + ;; 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.