home *** CD-ROM | disk | FTP | other *** search
/ Geek Gadgets 1 / ADE-1.bin / ade-dist / emacs-19.28-src.tgz / tar.out / fsf / emacs / lisp / cmacexp.el < prev    next >
Lisp/Scheme  |  1996-09-28  |  13KB  |  343 lines

  1. ;;; cmacexp.el --- expand C macros in a region
  2.  
  3. ;; Copyright (C) 1992, 1994 Free Software Foundation, Inc.
  4.  
  5. ;; Author: Francesco Potorti` <pot@cnuce.cnr.it>
  6. ;; Version: $Id: cmacexp.el,v 1.18 1994/09/05 04:33:23 rms Exp $
  7. ;; Adapted-By: ESR
  8. ;; Keywords: c
  9.  
  10. ;; This file is part of GNU Emacs.
  11.  
  12. ;; GNU Emacs is free software; you can redistribute it and/or modify
  13. ;; it under the terms of the GNU General Public License as published by
  14. ;; the Free Software Foundation; either version 2, or (at your option)
  15. ;; any later version.
  16.  
  17. ;; GNU Emacs is distributed in the hope that it will be useful,
  18. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  19. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  20. ;; GNU General Public License for more details.
  21.  
  22. ;; You should have received a copy of the GNU General Public License
  23. ;; along with GNU Emacs; see the file COPYING.  If not, write to
  24. ;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
  25.  
  26. ;; USAGE =============================================================
  27.  
  28. ;; In C mode C-C C-e is bound to c-macro-expand.  The result of the
  29. ;; expansion is put in a separate buffer.  A user option allows the
  30. ;; window displaying the buffer to be optimally sized.
  31. ;;
  32. ;; When called with a C-u prefix, c-macro-expand replaces the selected
  33. ;; region with the expansion.  Both the preprocessor name and the
  34. ;; initial flag can be set by the user.  If c-macro-prompt-flag is set
  35. ;; to a non-nil value the user is offered to change the options to the
  36. ;; preprocessor each time c-macro-expand is invoked.  Preprocessor
  37. ;; arguments default to the last ones entered.  If c-macro-prompt-flag
  38. ;; is nil, one must use M-x set-variable to set a different value for
  39. ;; c-macro-cppflags.
  40.  
  41. ;; A c-macro-expansion function is provided for non-interactive use.
  42.  
  43. ;; INSTALLATION ======================================================
  44.  
  45. ;; Put the following in your ~/.emacs file.
  46.  
  47. ;; If you want the *Macroexpansion* window to be not higher than
  48. ;; necessary: 
  49. ;;(setq c-macro-shrink-window-flag t)
  50. ;;
  51. ;; If you use a preprocessor other than /lib/cpp (be careful to set a
  52. ;; -C option or equivalent in order to make the preprocessor not to
  53. ;; strip the comments):
  54. ;;(setq c-macro-preprocessor "gpp -C")
  55. ;;
  56. ;; If you often use a particular set of flags:
  57. ;;(setq c-macro-cppflags "-I /usr/include/local -DDEBUG"
  58. ;;
  59. ;; If you want the "Preprocessor arguments: " prompt:
  60. ;;(setq c-macro-prompt-flag t)
  61.  
  62. ;; BUG REPORTS =======================================================
  63.  
  64. ;; Please report bugs, suggestions, complaints and so on to
  65. ;; pot@cnuce.cnr.it (Francesco Potorti`).
  66.  
  67. ;; IMPROVEMENTS OVER emacs 18.xx cmacexp.el ==========================
  68.  
  69. ;; - A lot of user and programmer visible changes.  See above.
  70. ;; - #line directives are inserted, so __LINE__ and __FILE__ are
  71. ;;   correctly expanded.  Works even with START inside a string, a
  72. ;;   comment or a region #ifdef'd away by cpp. cpp is invoked with -C,
  73. ;;   making comments visible in the expansion.
  74. ;; - All work is done in core memory, no need for temporary files.
  75.  
  76. ;; ACKNOWLEDGEMENTS ==================================================
  77.  
  78. ;; A lot of thanks to Don Maszle who did a great work of testing, bug
  79. ;; reporting and suggestion of new features.  This work has been
  80. ;; partially inspired by Don Maszle and Jonathan Segal's.
  81.  
  82. ;; BUGS ==============================================================
  83.  
  84. ;; If the start point of the region is inside a macro definition the
  85. ;; macro expansion is often inaccurate.
  86.  
  87.  
  88. (provide 'cmacexp)
  89.  
  90. (defvar c-macro-shrink-window-flag nil
  91.   "*Non-nil means shrink the *Macroexpansion* window to fit its contents.")
  92.  
  93. (defvar c-macro-prompt-flag nil
  94.   "*Non-nil makes `c-macro-expand' prompt for preprocessor arguments.")
  95.  
  96. (defvar c-macro-preprocessor "/lib/cpp -C"
  97.   "The preprocessor used by the cmacexp package.
  98.  
  99. If you change this, be sure to preserve the `-C' (don't strip comments)
  100. option, or to set an equivalent one.")
  101.  
  102. (defvar c-macro-cppflags ""
  103.   "*Preprocessor flags used by `c-macro-expand'.")
  104.  
  105. (defconst c-macro-buffer-name "*Macroexpansion*")
  106.  
  107. (defun c-macro-expand (start end subst)
  108.   "Expand C macros in the region, using the C preprocessor.
  109. Normally display output in temp buffer, but
  110. prefix arg means replace the region with it.
  111.  
  112. `c-macro-preprocessor' specifies the preprocessor to use.
  113. Prompt for arguments to the preprocessor \(e.g. `-DDEBUG -I ./include')
  114. if the user option `c-macro-prompt-flag' is non-nil.
  115.  
  116. Noninteractive args are START, END, SUBST.
  117. For use inside Lisp programs, see also `c-macro-expansion'."
  118.  
  119.   (interactive "r\nP")
  120.   (let ((inbuf (current-buffer))
  121.     (displaybuf (if subst
  122.             (get-buffer c-macro-buffer-name)
  123.               (get-buffer-create c-macro-buffer-name)))
  124.     (expansion ""))
  125.     ;; Build the command string.
  126.     (if c-macro-prompt-flag
  127.     (setq c-macro-cppflags
  128.           (read-string "Preprocessor arguments: "
  129.                c-macro-cppflags)))
  130.     ;; Decide where to display output.
  131.     (if (and subst
  132.          (and buffer-read-only (not inhibit-read-only))
  133.          (not (eq inbuf displaybuf)))
  134.     (progn
  135.       (message
  136.        "Buffer is read only: displaying expansion in alternate window")
  137.       (sit-for 2)
  138.       (setq subst nil)
  139.       (or displaybuf
  140.           (setq displaybuf (get-buffer-create c-macro-buffer-name)))))
  141.     ;; Expand the macro and output it.
  142.     (setq expansion (c-macro-expansion start end
  143.                        (concat c-macro-preprocessor " "
  144.                            c-macro-cppflags) t))
  145.     (if subst
  146.     (let ((exchange (= (point) start)))
  147.       (delete-region start end)
  148.       (insert expansion)
  149.       (if exchange
  150.           (exchange-point-and-mark)))
  151.       (set-buffer displaybuf)
  152.       (setq buffer-read-only nil)
  153.       (buffer-disable-undo displaybuf)
  154.       (erase-buffer)
  155.       (insert expansion)
  156.       (set-buffer-modified-p nil)
  157.       (if (string= "" expansion)
  158.       (message "Null expansion")
  159.     (c-macro-display-buffer))
  160.       (setq buffer-read-only t)
  161.       (setq buffer-auto-save-file-name nil)
  162.       (bury-buffer displaybuf))))
  163.  
  164.  
  165. ;; Display the current buffer in a window which is either just large
  166. ;; enough to contain the entire buffer, or half the size of the
  167. ;; screen, whichever is smaller.  Do not select the new
  168. ;; window.
  169. ;;
  170. ;; Several factors influence window resizing so that the window is
  171. ;; sized optimally if it is created anew, and so that it is messed
  172. ;; with minimally if it has been created by the user.  If the window
  173. ;; chosen for display exists already but contains something else, the
  174. ;; window is not re-sized.  If the window already contains the current
  175. ;; buffer, it is never shrunk, but possibly expanded.  Finally, if the
  176. ;; variable c-macro-shrink-window-flag is nil the window size is *never*
  177. ;; changed.
  178. (defun c-macro-display-buffer ()
  179.   (goto-char (point-min))
  180.   (c-mode)
  181.   (let ((oldwinheight (window-height))
  182.     (alreadythere            ;the window was already there
  183.      (get-buffer-window (current-buffer)))
  184.     (popped nil))            ;the window popped changing the layout 
  185.     (or alreadythere
  186.     (progn
  187.       (display-buffer (current-buffer) t)
  188.       (setq popped (/= oldwinheight (window-height)))))
  189.     (if (and c-macro-shrink-window-flag    ;user wants fancy shrinking :\)
  190.          (or alreadythere popped))
  191.     ;; Enlarge up to half screen, or shrink properly.
  192.     (let ((oldwin (selected-window))
  193.           (minheight 0)
  194.           (maxheight 0))
  195.       (save-excursion
  196.         (select-window (get-buffer-window (current-buffer)))
  197.         (setq minheight (if alreadythere
  198.                 (window-height)
  199.                   window-min-height))
  200.         (setq maxheight (/ (screen-height) 2))
  201.         (enlarge-window (- (min maxheight
  202.                     (max minheight
  203.                      (+ 2 (vertical-motion (point-max)))))
  204.                    (window-height)))
  205.         (goto-char (point-min))
  206.         (select-window oldwin))))))
  207.  
  208.  
  209. (defun c-macro-expansion (start end cppcommand &optional display)
  210.   "Run a preprocessor on region and return the output as a string.
  211. Expand the region between START and END in the current buffer using
  212. the shell command CPPCOMMAND (e.g. \"/lib/cpp -C -DDEBUG\").
  213. Be sure to use a -C (don't strip comments) or equivalent option.
  214. Optional arg DISPLAY non-nil means show messages in the echo area."
  215.  
  216. ;; Copy the current buffer's contents to a temporary hidden buffer.
  217. ;; Delete from END to end of buffer.  Insert a preprocessor #line
  218. ;; directive at START and after each #endif following START that are
  219. ;; not inside a comment or a string.  Put all the strings thus
  220. ;; inserted (without the "line" substring) in a list named linelist.
  221. ;; If START is inside a comment, prepend "*/" and append "/*" to the
  222. ;; #line directive.  If inside a string, prepend and append "\"".
  223. ;; Preprocess the buffer contents, then look for all the lines stored
  224. ;; in linelist starting from end of buffer.  The last line so found is
  225. ;; where START was, so return the substring from point to end of
  226. ;; buffer. 
  227.   (let ((inbuf (current-buffer))
  228.     (outbuf (get-buffer-create " *C Macro Expansion*"))
  229.     (filename (if (and buffer-file-name
  230.                (string-match (regexp-quote default-directory)
  231.                      buffer-file-name))
  232.               (substring buffer-file-name (match-end 0))
  233.             (buffer-name)))
  234.     (mymsg (format "Invoking %s%s%s on region..."
  235.                c-macro-preprocessor
  236.                (if (string= "" c-macro-cppflags) "" " ")
  237.                c-macro-cppflags))
  238.     (uniquestring "???!!!???!!! start of c-macro expansion ???!!!???!!!")
  239.     (startlinenum 0)
  240.     (linenum 0)
  241.     (startstat ())
  242.     (startmarker "")
  243.     (exit-status 0)
  244.     (tempname (make-temp-name "/tmp/")))
  245.     (unwind-protect
  246.     (save-excursion
  247.       (save-restriction
  248.         (widen)
  249.         (set-buffer outbuf)
  250.         (setq buffer-read-only nil)
  251.         (erase-buffer)
  252.         (set-syntax-table c-mode-syntax-table)
  253.         (insert-buffer-substring inbuf 1 end))
  254.  
  255.       ;; We have copied inbuf to outbuf.  Point is at end of
  256.       ;; outbuf.  Insert a space at the end, so cpp can correctly
  257.       ;; parse a token ending at END. 
  258.       (insert " ")
  259.  
  260.       ;; Save sexp status and line number at START.
  261.       (setq startstat (parse-partial-sexp 1 start))
  262.       (setq startlinenum (+ (count-lines 1 (point))
  263.                 (if (bolp) 1 0)))
  264.  
  265.       ;; Now we insert the #line directives after all #endif or
  266.       ;; #else following START going backward, so the lines we
  267.       ;; insert don't change the line numbers.
  268.       ;(switch-to-buffer outbuf) (debug)    ;debugging instructions
  269.       (goto-char (point-max))
  270.       (while (re-search-backward "\n#\\(endif\\|else\\)\\>" start 'move)
  271.         (if (equal (nthcdr 3 (parse-partial-sexp start (point)
  272.                              nil nil startstat))
  273.                '(nil nil nil 0 nil)) ;neither in string nor in
  274.                          ;comment nor after quote
  275.         (progn
  276.           (goto-char (match-end 0))
  277.           (setq linenum (+ startlinenum
  278.                    (count-lines start (point))))
  279.           (insert (format "\n#line %d \"%s\"\n" linenum filename))
  280.           (goto-char (match-beginning 0)))))
  281.  
  282.       ;; Now we are at START.  Insert the first #line directive.
  283.       ;; This must work even inside a string or comment, or after a
  284.       ;; quote.
  285.       (let* ((startinstring (nth 3 startstat))
  286.          (startincomment (nth 4 startstat))
  287.          (startafterquote (nth 5 startstat))
  288.          (startinbcomment (nth 7 startstat)))
  289.         (insert (if startafterquote " " "")
  290.             (cond (startinstring
  291.                (char-to-string startinstring))
  292.               (startincomment "*/")
  293.               (""))
  294.             (format "\n#line %d \"%s\"\n" startlinenum filename)
  295.             (setq startmarker
  296.               (concat uniquestring
  297.                   (cond (startinstring
  298.                      (char-to-string startinstring))
  299.                     (startincomment "/*")
  300.                     (startinbcomment "//"))
  301.                   (if startafterquote "\\")))))
  302.  
  303.       ;; Call the preprocessor.
  304.       (if display (message mymsg))
  305.       (setq exit-status
  306.         (call-process-region 1 (point-max) "sh" t t nil "-c"
  307.                      (concat cppcommand " 2>" tempname)))
  308.       (if display (message (concat mymsg "done")))
  309.       (if (= (buffer-size) 0)
  310.           ;; Empty output is normal after a fatal error.
  311.           (insert "\nPreprocessor produced no output\n")
  312.         ;; Find and delete the mark of the start of the expansion.
  313.         ;; Look for `# nn "file.c"' lines and delete them.
  314.         (goto-char (point-min))
  315.         (search-forward startmarker)
  316.         (delete-region 1 (point)))
  317.       (while (re-search-forward (concat "^# [0-9]+ \""
  318.                         (regexp-quote filename)
  319.                         "\"") nil t)
  320.         (beginning-of-line)
  321.         (let ((beg (point)))
  322.           (forward-line 1)
  323.           (delete-region beg (point))))
  324.  
  325.       ;; If CPP got errors, show them at the beginning.
  326.       (or (eq exit-status 0)
  327.           (progn
  328.         (goto-char (point-min))
  329.         (insert (format "Preprocessor terminated with status %s\n"
  330.                 exit-status))
  331.         (insert-file-contents tempname)
  332.         (insert "\n")))
  333.       (delete-file tempname)
  334.  
  335.       ;; Compute the return value, keeping in account the space
  336.       ;; inserted at the end of the buffer.
  337.       (buffer-substring 1 (max 1 (- (point-max) 1))))
  338.  
  339.       ;; Cleanup.
  340.       (kill-buffer outbuf))))
  341.  
  342. ;;; cmacexp.el ends here
  343.