cmacexp.el 14 KB

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