Update FSF's address.
[bpt/emacs.git] / lisp / progmodes / c-mode.el
index fc4f4fa..22d598b 100644 (file)
@@ -1,6 +1,6 @@
 ;;; c-mode.el --- C code editing commands for Emacs
 
-;; Copyright (C) 1985, 1986, 1987, 1992 Free Software Foundation, Inc.
+;; Copyright (C) 1985, 86, 87, 92, 94, 95 Free Software Foundation, Inc.
 
 ;; Maintainer: FSF
 ;; Keywords: c
 ;; GNU General Public License for more details.
 
 ;; You should have received a copy of the GNU General Public License
-;; along with GNU Emacs; see the file COPYING.  If not, write to
-;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+;; along with GNU Emacs; see the file COPYING.  If not, write to the
+;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+;; Boston, MA 02111-1307, USA.
+
+;;; Commentary:
+
+;; A smart editing mode for C code.  It knows a lot about C syntax and tries
+;; to position the cursor according to C layout conventions.  You can
+;; change the details of the layout style with option variables.  Load it
+;; and do M-x describe-mode for details.
 
 ;;; Code:
 
   "Abbrev table in use in C mode.")
 (define-abbrev-table 'c-mode-abbrev-table ())
 
-(defvar c-mode-map ()
+(defvar c-mode-map (make-sparse-keymap)
   "Keymap used in C mode.")
-(if c-mode-map
-    ()
-  (setq c-mode-map (make-sparse-keymap))
-  (define-key c-mode-map "{" 'electric-c-brace)
-  (define-key c-mode-map "}" 'electric-c-brace)
-  (define-key c-mode-map ";" 'electric-c-semi)
-  (define-key c-mode-map "#" 'electric-c-sharp-sign)
-  (define-key c-mode-map ":" 'electric-c-terminator)
-  (define-key c-mode-map "\e\C-h" 'mark-c-function)
-  (define-key c-mode-map "\e\C-q" 'indent-c-exp)
-  (define-key c-mode-map "\ea" 'c-beginning-of-statement)
-  (define-key c-mode-map "\ee" 'c-end-of-statement)
-  (define-key c-mode-map "\eq" 'c-fill-paragraph)
-  (define-key c-mode-map "\177" 'backward-delete-char-untabify)
-  (define-key c-mode-map "\t" 'c-indent-command))
 
-;; cmacexp is lame because it uses no preprocessor symbols.
-;; It isn't very extensible either -- hardcodes /lib/cpp.
+(define-key c-mode-map "{" 'electric-c-brace)
+(define-key c-mode-map "}" 'electric-c-brace)
+(define-key c-mode-map ";" 'electric-c-semi)
+(define-key c-mode-map "#" 'electric-c-sharp-sign)
+(define-key c-mode-map ":" 'electric-c-terminator)
+(define-key c-mode-map "\e\C-h" 'mark-c-function)
+(define-key c-mode-map "\e\C-q" 'indent-c-exp)
+(define-key c-mode-map "\ea" 'c-beginning-of-statement)
+(define-key c-mode-map "\ee" 'c-end-of-statement)
+(define-key c-mode-map "\C-c\C-n" 'c-forward-conditional)
+(define-key c-mode-map "\C-c\C-p" 'c-backward-conditional)
+(define-key c-mode-map "\C-c\C-u" 'c-up-conditional)
+(define-key c-mode-map "\177" 'backward-delete-char-untabify)
+(define-key c-mode-map "\t" 'c-indent-command)
+
+(define-key c-mode-map [menu-bar] (make-sparse-keymap))
+
+;; "C-mode" is not strictly the right punctuation--it should be "C
+;; mode"--but that would look like two menu items.  "C-mode" is the
+;; best alternative I can think of.
+(define-key c-mode-map [menu-bar c]
+  (cons "C-mode" (make-sparse-keymap "C-mode")))
+
+(define-key c-mode-map [menu-bar c comment-region]
+  '("Comment Out Region" . comment-region))
+(define-key c-mode-map [menu-bar c c-macro-expand]
+  '("Macro Expand Region" . c-macro-expand))
+(define-key c-mode-map [menu-bar c c-backslash-region]
+  '("Backslashify" . c-backslash-region))
+(define-key c-mode-map [menu-bar c indent-exp]
+  '("Indent Expression" . indent-c-exp))
+(define-key c-mode-map [menu-bar c indent-line]
+  '("Indent Line" . c-indent-command))
+(define-key c-mode-map [menu-bar c fill]
+  '("Fill Comment Paragraph" . c-fill-paragraph))
+(define-key c-mode-map [menu-bar c cpp-highlight-buffer]
+  '("Highlight Conditionals" . cpp-highlight-buffer))
+(define-key c-mode-map [menu-bar c up]
+  '("Up Conditional" . c-up-conditional))
+(define-key c-mode-map [menu-bar c backward]
+  '("Backward Conditional" . c-backward-conditional))
+(define-key c-mode-map [menu-bar c forward]
+  '("Forward Conditional" . c-forward-conditional))
+(define-key c-mode-map [menu-bar c backward-stmt]
+  '("Backward Statement" . c-beginning-of-statement))
+(define-key c-mode-map [menu-bar c forward-stmt]
+  '("Forward Statement" . c-end-of-statement))
+
+(put 'comment-region 'menu-enable 'mark-active)
+(put 'c-macro-expand 'menu-enable 'mark-active)
+(put 'c-backslash-region 'menu-enable 'mark-active)
+
 (autoload 'c-macro-expand "cmacexp"
   "Display the result of expanding all C macros occurring in the region.
 The expansion is entirely correct because it uses the C preprocessor."
@@ -85,37 +128,42 @@ The expansion is entirely correct because it uses the C preprocessor."
   "*Extra indent for lines not starting new statements.")
 (defconst c-continued-brace-offset 0
   "*Extra indent for substatements that start with open-braces.
-This is in addition to c-continued-statement-offset.")
+This is in addition to `c-continued-statement-offset'.")
 (defconst c-style-alist
   '(("GNU"
      (c-indent-level               .  2)
      (c-argdecl-indent             .  5)
      (c-brace-offset               .  0)
+     (c-continued-brace-offset     .  0)
      (c-label-offset               . -2)
      (c-continued-statement-offset .  2))
     ("K&R"
      (c-indent-level               .  5)
      (c-argdecl-indent             .  0)
-     (c-brace-offset               . -5)
+     (c-brace-offset               .  0)
+     (c-continued-brace-offset     . -5)
      (c-label-offset               . -5)
      (c-continued-statement-offset .  5))
     ("BSD"
      (c-indent-level               .  4)
      (c-argdecl-indent             .  4)
-     (c-brace-offset               . -4)
+     (c-brace-offset               .  0)
+     (c-continued-brace-offset     . -4)
      (c-label-offset               . -4)
      (c-continued-statement-offset .  4))
-    (C++
-     (c-indent-level               . 4)
-     (c-continued-statement-offset . 4)
-     (c-brace-offset               . -4)
-     (c-argdecl-indent             . 0)
+    ("C++"
+     (c-indent-level               .  4)
+     (c-argdecl-indent             .  0)
+     (c-brace-offset               .  0)
+     (c-continued-brace-offset     . -4)
      (c-label-offset               . -4)
-     (c-auto-newline               . t))
+     (c-continued-statement-offset .  4)
+     (c-auto-newline               .  t))
     ("Whitesmith"
      (c-indent-level               .  4)
      (c-argdecl-indent             .  4)
      (c-brace-offset               .  0)
+     (c-continued-brace-offset     .  0)
      (c-label-offset               . -4)
      (c-continued-statement-offset .  4))))
 
@@ -123,11 +171,112 @@ This is in addition to c-continued-statement-offset.")
   "*Non-nil means automatically newline before and after braces,
 and after colons and semicolons, inserted in C code.
 If you do not want a leading newline before braces then use:
-  (define-key c-mode-map "{" 'electric-c-semi)")
+  (define-key c-mode-map \"{\" 'electric-c-semi)")
 
 (defconst c-tab-always-indent t
   "*Non-nil means TAB in C mode should always reindent the current line,
 regardless of where in the line point is when the TAB command is used.")
+
+;;; Regular expression used internally to recognize labels in switch
+;;; statements.
+(defconst c-switch-label-regexp "case[ \t'/(]\\|default[ \t]*:")
+
+;; This is actually the expression for C++ mode, but it's used for C too.
+(defvar c-imenu-generic-expression
+  (` 
+   ((nil
+     (, 
+      (concat
+       "^"                               ; beginning of line is required
+       "\\(template[ \t]*<[^>]+>[ \t]*\\)?" ; there may be a "template <...>"
+       "\\([a-zA-Z0-9_:]+[ \t]+\\)?"     ; type specs; there can be no
+       "\\([a-zA-Z0-9_:]+[ \t]+\\)?"     ; more than 3 tokens, right?
+       
+       "\\("                             ; last type spec including */&
+       "[a-zA-Z0-9_:]+"
+       "\\([ \t]*[*&]+[ \t]*\\|[ \t]+\\)"        ; either pointer/ref sign or whitespace
+       "\\)?"                            ; if there is a last type spec
+       "\\("                         ; name; take that into the imenu entry
+       "[a-zA-Z0-9_:~]+"                     ; member function, ctor or dtor...
+                                       ; (may not contain * because then 
+                                       ; "a::operator char*" would become "char*"!)
+       "\\|"
+       "\\([a-zA-Z0-9_:~]*::\\)?operator"
+       "[^a-zA-Z1-9_][^(]*"          ; ...or operator
+       " \\)"
+       "[ \t]*([^)]*)[ \t\n]*[^              ;]" ; require something other than a ; after
+                                       ; the (...) to avoid prototypes.  Can't
+                                       ; catch cases with () inside the parentheses
+                                       ; surrounding the parameters
+                                       ; (like "int foo(int a=bar()) {...}"
+       
+       )) 6)    
+    ("Class" 
+     (, (concat 
+        "^"                               ; beginning of line is required
+        "\\(template[ \t]*<[^>]+>[ \t]*\\)?" ; there may be a "template <...>"
+        "class[ \t]+"
+        "\\([a-zA-Z0-9_]+\\)"                ; this is the string we want to get
+        "[ \t]*[:{]"
+        )) 2)
+;; Example of generic expression for finding prototypes, structs, unions, enums.
+;; Uncomment if you want to find these too.  It will be a bit slower gathering
+;; the indexes.
+;    ("Prototypes"
+;     (, 
+;      (concat
+;       "^"                              ; beginning of line is required
+;       "\\(template[ \t]*<[^>]+>[ \t]*\\)?" ; there may be a "template <...>"
+;       "\\([a-zA-Z0-9_:]+[ \t]+\\)?"    ; type specs; there can be no
+;       "\\([a-zA-Z0-9_:]+[ \t]+\\)?"    ; more than 3 tokens, right?
+       
+;       "\\("                            ; last type spec including */&
+;       "[a-zA-Z0-9_:]+"
+;       "\\([ \t]*[*&]+[ \t]*\\|[ \t]+\\)"       ; either pointer/ref sign or whitespace
+;       "\\)?"                           ; if there is a last type spec
+;       "\\("                        ; name; take that into the imenu entry
+;       "[a-zA-Z0-9_:~]+"                    ; member function, ctor or dtor...
+;                                      ; (may not contain * because then 
+;                                      ; "a::operator char*" would become "char*"!)
+;       "\\|"
+;       "\\([a-zA-Z0-9_:~]*::\\)?operator"
+;       "[^a-zA-Z1-9_][^(]*"         ; ...or operator
+;       " \\)"
+;       "[ \t]*([^)]*)[ \t\n]*;"       ; require ';' after
+;                                      ; the (...) Can't
+;                                      ; catch cases with () inside the parentheses
+;                                      ; surrounding the parameters
+;                                      ; (like "int foo(int a=bar());"       
+;       )) 6)    
+;    ("Struct"
+;     (, (concat
+;       "^"                            ; beginning of line is required
+;       "\\(static[ \t]+\\)?"          ; there may be static or const.
+;       "\\(const[ \t]+\\)?"
+;       "struct[ \t]+"
+;       "\\([a-zA-Z0-9_]+\\)"          ; this is the string we want to get
+;       "[ \t]*[{]"
+;       )) 3)
+;    ("Enum"
+;     (, (concat
+;       "^"                            ; beginning of line is required
+;       "\\(static[ \t]+\\)?"          ; there may be static or const.
+;       "\\(const[ \t]+\\)?"
+;       "enum[ \t]+"
+;       "\\([a-zA-Z0-9_]+\\)"          ; this is the string we want to get
+;       "[ \t]*[{]"
+;       )) 3)
+;    ("Union"
+;     (, (concat
+;       "^"                            ; beginning of line is required
+;       "\\(static[ \t]+\\)?"          ; there may be static or const.
+;       "\\(const[ \t]+\\)?"
+;       "union[ \t]+"
+;       "\\([a-zA-Z0-9_]+\\)"          ; this is the string we want to get
+;       "[ \t]*[{]"
+;       )) 3)
+    ))
+  "Imenu generic expression for C mode.  See `imenu-generic-expression'.")
 \f
 (defun c-mode ()
   "Major mode for editing C code.
@@ -181,17 +330,23 @@ if that value is non-nil."
   (setq local-abbrev-table c-mode-abbrev-table)
   (set-syntax-table c-mode-syntax-table)
   (make-local-variable 'paragraph-start)
-  (setq paragraph-start (concat "^$\\|" page-delimiter))
+  (setq paragraph-start (concat "$\\|" page-delimiter))
   (make-local-variable 'paragraph-separate)
   (setq paragraph-separate paragraph-start)
   (make-local-variable 'paragraph-ignore-fill-prefix)
   (setq paragraph-ignore-fill-prefix t)
+  (make-local-variable 'fill-paragraph-function)
+  (setq fill-paragraph-function 'c-fill-paragraph)
   (make-local-variable 'indent-line-function)
   (setq indent-line-function 'c-indent-line)
   (make-local-variable 'indent-region-function)
   (setq indent-region-function 'c-indent-region)
   (make-local-variable 'require-final-newline)
   (setq require-final-newline t)
+  (make-local-variable 'outline-regexp)
+  (setq outline-regexp "[^#\n\^M]")
+  (make-local-variable 'outline-level)
+  (setq outline-level 'c-outline-level)
   (make-local-variable 'comment-start)
   (setq comment-start "/* ")
   (make-local-variable 'comment-end)
@@ -200,11 +355,20 @@ if that value is non-nil."
   (setq comment-column 32)
   (make-local-variable 'comment-start-skip)
   (setq comment-start-skip "/\\*+ *")
-  (make-local-variable 'comment-indent-hook)
-  (setq comment-indent-hook 'c-comment-indent)
+  (make-local-variable 'comment-indent-function)
+  (setq comment-indent-function 'c-comment-indent)
+  (make-local-variable 'comment-multi-line)
+  (setq comment-multi-line t)
   (make-local-variable 'parse-sexp-ignore-comments)
   (setq parse-sexp-ignore-comments t)
+  (make-local-variable 'imenu-generic-expression)
+  (setq imenu-generic-expression c-imenu-generic-expression)
   (run-hooks 'c-mode-hook))
+
+(defun c-outline-level ()
+  (save-excursion
+    (skip-chars-forward "\t ")
+    (current-column)))
 \f
 ;; This is used by indent-for-comment
 ;; to decide how much to indent a comment in C code
@@ -236,65 +400,200 @@ if that value is non-nil."
 
 (defun c-fill-paragraph (&optional arg)
   "Like \\[fill-paragraph] but handle C comments.
-If point is inside a comment, the current paragraph of the comment
-is filled, preserving the comment indentation or line-starting decorations."
+If any of the current line is a comment or within a comment,
+fill the comment or the paragraph of it that point is in,
+preserving the comment indentation or line-starting decorations."
   (interactive "P")
-  (let ((first-line
-        ;; Check for obvious entry to comment.
-        (save-excursion
-          (beginning-of-line)
-          (skip-chars-forward " \t")
-          (looking-at comment-start-skip))))
-    (if (or first-line
-           ;; t if we enter a comment between start of function and this line.
-           (eq (calculate-c-indent) t)
-           ;; See if we enter a comment between beg-of-line and here.
-           (nth 4 (parse-partial-sexp (save-excursion (beginning-of-line) (point))
-                                      (point) 0)))
-       ;; Inside a comment: fill one comment paragraph.
-       (let ((fill-prefix
-              ;; The prefix for each line of this paragraph
-              ;; is the appropriate part of the start of this line,
-              ;; up to the column at which text should be indented.
-              (save-excursion
-                (beginning-of-line)
-                (if (looking-at "[ \t]*/\\*.*\\*/")
-                    (progn (re-search-forward comment-start-skip)
-                           (make-string (current-column) ?\ ))
-                  (if first-line (forward-line 1))
-                  (buffer-substring (point)
-                                    (progn
-                                      (move-to-column
-                                       (calculate-c-indent-within-comment t)
-                                       t)
-                                      (point))))))
+  (let* (comment-start-place
+        (first-line
+         ;; Check for obvious entry to comment.
+         (save-excursion
+           (beginning-of-line)
+           (skip-chars-forward " \t\n")
+           (and (looking-at comment-start-skip)
+                (setq comment-start-place (point))))))
+    (if (and (eq major-mode 'c++-mode)
+            (save-excursion
+              (beginning-of-line)
+              (looking-at ".*//")))
+       (let (fill-prefix
              (paragraph-start
               ;; Lines containing just a comment start or just an end
               ;; should not be filled into paragraphs they are next to.
-              (concat paragraph-start
-                      "\\|^[ \t]*/\\*[ \t]*$\\|^[ \t]*\\*/[ \t]*$\\|^[^ \t/*]"))
+              (concat 
+               paragraph-start
+               "\\|[ \t]*/\\*[ \t]*$\\|[ \t]*\\*/[ \t]*$\\|[ \t/*]*$"))
              (paragraph-separate
-              (concat paragraph-separate
-                      "\\|^[ \t]*/\\*[ \t]*$\\|^[ \t]*\\*/[ \t]*$\\|^[^ \t/*]")))
-         (save-restriction
-  (recursive-edit)
-           ;; Don't fill the comment together with the code following it.
-           (narrow-to-region (save-excursion
-                               (search-backward "/*")
-                               (beginning-of-line)
-                               (point))
-                             (save-excursion
-                               (search-forward "*/" nil 'move)
-                               (forward-line 1)
-                               (point)))
-           (fill-paragraph arg)
-           (save-excursion
-             (search-forward "*/")
-             (beginning-of-line)
-             (if (looking-at "[ \t]*\\*/")
-                 (delete-indentation)))))
-      ;; Outside of comments: do ordinary filling.
-      (fill-paragraph arg))))
+              (concat
+               paragraph-separate
+               "\\|[ \t]*/\\*[ \t]*$\\|[ \t]*\\*/[ \t]*$\\|[ \t/*]*$")))
+         (save-excursion
+           (beginning-of-line)
+           ;; Move up to first line of this comment.
+           (while (and (not (bobp)) (looking-at "[ \t]*//"))
+             (forward-line -1))
+           (if (not (looking-at ".*//"))
+               (forward-line 1))
+           ;; Find the comment start in this line.
+           (re-search-forward "[ \t]*//[ \t]*")
+           ;; Set the fill-prefix to be what all lines except the first
+           ;; should start with.
+           (let ((endcol (current-column)))
+             (skip-chars-backward " \t")
+             (setq fill-prefix
+                   (concat (make-string (- (current-column) 2) ?\ )
+                           "//"
+                           (make-string (- endcol (current-column)) ?\ ))))
+           (save-restriction
+             ;; Narrow down to just the lines of this comment.
+             (narrow-to-region (point)
+                               (save-excursion
+                                 (forward-line 1)
+                                 (while (looking-at "[ \t]*//")
+                                   (forward-line 1))
+                                 (point)))
+             (insert fill-prefix)
+             (fill-paragraph arg)
+             (delete-region (point-min)
+                            (+ (point-min) (length fill-prefix))))))
+      (if (or first-line
+             ;; t if we enter a comment between start of function and this line.
+             (eq (calculate-c-indent) t)
+             ;; t if this line contains a comment starter.
+             (setq first-line
+                   (save-excursion
+                     (beginning-of-line)
+                     (prog1
+                         (re-search-forward comment-start-skip
+                                            (save-excursion (end-of-line)
+                                                            (point))
+                                            t)
+                       (setq comment-start-place (point))))))
+         ;; Inside a comment: fill one comment paragraph.
+         (let ((fill-prefix
+                ;; The prefix for each line of this paragraph
+                ;; is the appropriate part of the start of this line,
+                ;; up to the column at which text should be indented.
+                (save-excursion
+                  (beginning-of-line)
+                  (if (looking-at "[ \t]*/\\*.*\\*/")
+                      (progn (re-search-forward comment-start-skip)
+                             (make-string (current-column) ?\ ))
+                    (if first-line (forward-line 1))
+
+                    (let ((line-width (progn (end-of-line) (current-column))))
+                      (beginning-of-line)
+                      (prog1
+                          (buffer-substring
+                           (point)
+
+                           ;; How shall we decide where the end of the
+                           ;; fill-prefix is?
+                           ;; calculate-c-indent-within-comment bases its value
+                           ;; on the indentation of previous lines; if they're
+                           ;; indented specially, it could return a column
+                           ;; that's well into the current line's text.  So
+                           ;; we'll take at most that many space, tab, or *
+                           ;; characters, and use that as our fill prefix.
+                           (let ((max-prefix-end
+                                  (progn
+                                    (move-to-column
+                                     (calculate-c-indent-within-comment t)
+                                     t)
+                                    (point))))
+                             (beginning-of-line)
+                             (skip-chars-forward " \t*" max-prefix-end)
+                             ;; Don't include part of comment terminator
+                             ;; in the fill-prefix.
+                             (and (eq (following-char) ?/)
+                                  (eq (preceding-char) ?*)
+                                  (backward-char 1))
+                             (point)))
+
+                        ;; If the comment is only one line followed by a blank
+                        ;; line, calling move-to-column above may have added
+                        ;; some spaces and tabs to the end of the line; the
+                        ;; fill-paragraph function will then delete it and the
+                        ;; newline following it, so we'll lose a blank line
+                        ;; when we shouldn't.  So delete anything
+                        ;; move-to-column added to the end of the line.  We
+                        ;; record the line width instead of the position of the
+                        ;; old line end because move-to-column might break a
+                        ;; tab into spaces, and the new characters introduced
+                        ;; there shouldn't be deleted.
+
+                        ;; If you can see a better way to do this, please make
+                        ;; the change.  This seems very messy to me.
+                        (delete-region (progn (move-to-column line-width)
+                                              (point))
+                                       (progn (end-of-line) (point))))))))
+
+               (paragraph-start
+                ;; Lines containing just a comment start or just an end
+                ;; should not be filled into paragraphs they are next to.
+                (concat 
+                 paragraph-start
+                 "\\|[ \t]*/\\*[ \t]*$\\|[ \t]*\\*/[ \t]*$\\|[ \t/*]*$"))
+               (paragraph-separate
+                (concat
+                 paragraph-separate
+                 "\\|[ \t]*/\\*[ \t]*$\\|[ \t]*\\*/[ \t]*$\\|[ \t/*]*$"))
+               (chars-to-delete 0))
+           (save-restriction
+             ;; Don't fill the comment together with the code following it.
+             ;; So temporarily exclude everything before the comment start,
+             ;; and everything after the line where the comment ends.
+             ;; If comment-start-place is non-nil, the comment starter is there.
+             ;; Otherwise, point is inside the comment.
+             (narrow-to-region (save-excursion
+                                 (if comment-start-place
+                                     (goto-char comment-start-place)
+                                   (search-backward "/*"))
+                                 ;; Protect text before the comment start 
+                                 ;; by excluding it.  Add spaces to bring back 
+                                 ;; proper indentation of that point.
+                                 (let ((column (current-column)))
+                                   (prog1 (point)
+                                     (setq chars-to-delete column)
+                                     (insert-char ?\  column))))
+                               (save-excursion
+                                 (if comment-start-place
+                                     (goto-char (+ comment-start-place 2)))
+                                 (search-forward "*/" nil 'move)
+                                 (forward-line 1)
+                                 (point)))
+             (save-excursion
+               (goto-char (point-max))
+               (forward-line -1)
+               ;; And comment terminator was on a separate line before,
+               ;; keep it that way.
+               ;; This also avoids another problem:
+               ;; if the fill-prefix ends in a *, it could eat up
+               ;; the * of the comment terminator.
+               (if (looking-at "[ \t]*\\*/")
+                   (narrow-to-region (point-min) (point))))
+             (fill-paragraph arg)
+             (save-excursion
+               ;; Delete the chars we inserted to avoid clobbering
+               ;; the stuff before the comment start.
+               (goto-char (point-min))
+               (if (> chars-to-delete 0)
+                   (delete-region (point) (+ (point) chars-to-delete)))
+               ;; Find the comment ender (should be on last line of buffer,
+               ;; given the narrowing) and don't leave it on its own line.
+               ;; Do this with a fill command, so as to preserve sentence
+               ;; boundaries.
+               (goto-char (point-max))
+               (forward-line -1)
+               (search-forward "*/" nil 'move)
+               (beginning-of-line)
+               (if (looking-at "[ \t]*\\*/")
+                   (let ((fill-column (+ fill-column 9999)))
+                     (forward-line -1)
+                     (fill-region-as-paragraph (point) (point-max)))))))
+       ;; Outside of comments: do ordinary filling.
+       (fill-paragraph arg)))
+    t))
 
 (defun electric-c-brace (arg)
   "Insert character and correct line's indentation."
@@ -354,7 +653,7 @@ is filled, preserving the comment indentation or line-starting decorations."
                        ;; So quickly rule out most other uses of colon
                        ;; and do no indentation for them.
                        (and (eq last-command-char ?:)
-                            (not (looking-at "case[ \t'/(]\\|default\\>"))
+                            (not (looking-at c-switch-label-regexp))
                             (save-excursion
                               (skip-chars-forward "a-zA-Z0-9_$")
                               (skip-chars-forward " \t")
@@ -446,7 +745,7 @@ Return the amount the indentation changed by."
          (t
           (skip-chars-forward " \t")
           (if (listp indent) (setq indent (car indent)))
-          (cond ((or (looking-at "case[ \t'/(]\\|default\\>")
+          (cond ((or (looking-at c-switch-label-regexp)
                      (and (looking-at "[A-Za-z]")
                           (save-excursion
                             (forward-sexp 1)
@@ -457,12 +756,15 @@ Return the amount the indentation changed by."
                  (setq indent (save-excursion
                                 (c-backward-to-start-of-if)
                                 (current-indentation))))
-                ((looking-at "}[ \t]*else")
+                ((and (looking-at "}[ \t]*else\\b")
+                      (not (looking-at "}[ \t]*else\\s_")))
                  (setq indent (save-excursion
                                 (forward-char)
                                 (backward-sexp)
+                                (c-backward-to-start-of-if)
                                 (current-indentation))))
                 ((and (looking-at "while\\b")
+                      (not (looking-at "while\\s_"))
                       (save-excursion
                         (c-backward-to-start-of-do)))
                  ;; This is a `while' that ends a do-while.
@@ -527,31 +829,42 @@ Returns nil if line starts inside a string, t if in a comment."
                          ;; Recognize the DEFUN macro in Emacs.
                          (if (save-excursion
                                ;; Move down to the (putative) argnames line.
-                               (while (not (looking-at " *("))
+                               (while (and (not (eobp))
+                                           (not (looking-at " *[({}#/]")))
                                  (forward-line 1))
                                ;; Go back to the DEFUN, if it is one.
                                (condition-case nil
                                    (backward-sexp 1)
                                  (error))
                                (beginning-of-line)
-                               (setq tem (point))
                                (looking-at "DEFUN\\b"))
                              c-argdecl-indent
                            (if (and (looking-at "\\sw\\|\\s_")
-                                    (looking-at "[^\"\n=]*(")
+                                    ;; This is careful to stop at the first
+                                    ;; paren if we have
+                                    ;; int foo Proto ((int, int));
+                                    (looking-at "[^\"\n=(]*(")
                                     (progn
                                       (goto-char (1- (match-end 0)))
-                                      (setq lim (point))
-                                      (condition-case nil
-                                          (forward-sexp 1)
-                                        (error))
-                                      (skip-chars-forward " \t\f")
+                                      ;; Skip any number of paren-groups.
+                                      ;; Consider typedef int (*fcn) (int);
+                                      (while (= (following-char) ?\()
+                                        (setq lim (point))
+                                        (condition-case nil
+                                            (forward-sexp 1)
+                                          (error))
+                                        (skip-chars-forward " \t\f"))
+                                      ;; Have we reached something
+                                      ;; that shows this isn't a function
+                                      ;; definition?
                                       (and (< (point) indent-point)
                                            (not (memq (following-char)
                                                       '(?\, ?\;)))))
                                     ;; Make sure the "function decl" we found
                                     ;; is not inside a comment.
                                     (progn
+                                      ;; Move back to the `(' starting arglist
+                                      (goto-char lim)
                                       (beginning-of-line)
                                       (while (and (not comment)
                                                   (search-forward "/*" lim t))
@@ -594,23 +907,28 @@ Returns nil if line starts inside a string, t if in a comment."
               (beginning-of-line)
               (c-backward-to-noncomment containing-sexp))
             ;; Check for a preprocessor statement or its continuation lines.
-            ;; Move back to end of previous non-preprocessor line.
+            ;; Move back to end of previous non-preprocessor line,
+            ;; or possibly beginning of buffer.
             (let ((found (point)) stop)
               (while (not stop)
-                (cond ((save-excursion (end-of-line 0)
+                (beginning-of-line)
+                (cond ((bobp)
+                       (setq found (point)
+                             stop t))
+                      ((save-excursion (forward-char -1)
                                        (= (preceding-char) ?\\))
-                       (end-of-line 0))
+                       (forward-char -1))
                       ;; This line is not preceded by a backslash.
                       ;; So either it starts a preprocessor command
                       ;; or any following continuation lines
                       ;; should not be skipped.
-                      ((progn (beginning-of-line) (= (following-char) ?#))
-                       (end-of-line 0)
+                      ((= (following-char) ?#)
+                       (forward-char -1)
                        (setq found (point)))
                       (t (setq stop t))))
               (goto-char found))
             ;; Now we get the answer.
-            (if (and (not (memq (preceding-char) '(nil ?\, ?\; ?\} ?\{)))
+            (if (and (not (memq (preceding-char) '(0 ?\, ?\; ?\} ?\{)))
                      ;; But don't treat a line with a close-brace
                      ;; as a continuation.  It is probably the
                      ;; end of an enum type declaration.
@@ -664,27 +982,32 @@ Returns nil if line starts inside a string, t if in a comment."
                           (if (= (following-char) ?\{) c-brace-offset 0)))))
                 ;; If no previous statement,
                 ;; indent it relative to line brace is on.
-                ;; For open brace in column zero, don't let statement
-                ;; start there too.  If c-indent-level is zero,
-                ;; use c-brace-offset + c-continued-statement-offset instead.
-                ;; For open-braces not the first thing in a line,
-                ;; add in c-brace-imaginary-offset.
-                (+ (if (and (bolp) (zerop c-indent-level))
-                       (+ c-brace-offset c-continued-statement-offset)
-                     c-indent-level)
-                   ;; Move back over whitespace before the openbrace.
-                   ;; If openbrace is not first nonwhite thing on the line,
-                   ;; add the c-brace-imaginary-offset.
-                   (progn (skip-chars-backward " \t")
-                          (if (bolp) 0 c-brace-imaginary-offset))
-                   ;; If the openbrace is preceded by a parenthesized exp,
-                   ;; move to the beginning of that;
-                   ;; possibly a different line
-                   (progn
-                     (if (eq (preceding-char) ?\))
-                         (forward-sexp -1))
-                     ;; Get initial indentation of the line we are on.
-                     (current-indentation))))))))))
+                (calculate-c-indent-after-brace))))))))
+
+(defun calculate-c-indent-after-brace ()
+  "Return the proper C indent for the first line after an open-brace.
+This function is called with point before the brace."
+  ;; For open brace in column zero, don't let statement
+  ;; start there too.  If c-indent-level is zero,
+  ;; use c-brace-offset + c-continued-statement-offset instead.
+  ;; For open-braces not the first thing in a line,
+  ;; add in c-brace-imaginary-offset.
+  (+ (if (and (bolp) (zerop c-indent-level))
+        (+ c-brace-offset c-continued-statement-offset)
+       c-indent-level)
+     ;; Move back over whitespace before the openbrace.
+     ;; If openbrace is not first nonwhite thing on the line,
+     ;; add the c-brace-imaginary-offset.
+     (progn (skip-chars-backward " \t")
+           (if (bolp) 0 c-brace-imaginary-offset))
+     ;; If the openbrace is preceded by a parenthesized exp,
+     ;; move to the beginning of that;
+     ;; possibly a different line
+     (progn
+       (if (eq (preceding-char) ?\))
+          (forward-sexp -1))
+       ;; Get initial indentation of the line we are on.
+       (current-indentation))))
 
 (defun calculate-c-indent-within-comment (&optional after-star)
   "Return the indentation amount for line inside a block comment.
@@ -743,9 +1066,11 @@ return the indentation of the text that would follow this star."
        (case-fold-search nil))
     (while (and (not (bobp)) (not (zerop if-level)))
       (backward-sexp 1)
-      (cond ((looking-at "else\\b")
+      (cond ((and (looking-at "else\\b")
+                 (not (looking-at "else\\s_")))
             (setq if-level (1+ if-level)))
-           ((looking-at "if\\b")
+           ((and (looking-at "if\\b")
+                 (not (looking-at "if\\s_")))
             (setq if-level (1- if-level)))
            ((< (point) limit)
             (setq if-level 0)
@@ -794,14 +1119,19 @@ Otherwise return nil and don't move point."
 (defun c-beginning-of-statement (count)
   "Go to the beginning of the innermost C statement.
 With prefix arg, go back N - 1 statements.  If already at the beginning of a
-statement then go to the beginning of the preceeding one.
-If within a string or comment, move by sentences instead of statements."
+statement then go to the beginning of the preceding one.
+If within a string or comment, or next to a comment (only whitespace between),
+move by sentences instead of statements."
   (interactive "p")
   (let ((here (point)) state)
     (save-excursion
       (beginning-of-defun)
       (setq state (parse-partial-sexp (point) here nil nil)))
-    (if (or (nth 3 state) (nth 4 state))
+    (if (or (nth 3 state) (nth 4 state)
+           (looking-at (concat "[ \t]*" comment-start-skip))
+           (save-excursion (skip-chars-backward " \t")
+                           (goto-char (- (point) 2))
+                           (looking-at "\\*/")))
        (forward-sentence (- count))
       (while (> count 0)
        (c-beginning-of-statement-1)
@@ -856,14 +1186,14 @@ If within a string or comment, move by sentences instead of statements."
   (interactive)
   (push-mark (point))
   (end-of-defun)
-  (push-mark (point))
+  (push-mark (point) nil t)
   (beginning-of-defun)
   (backward-paragraph))
 \f
+;; Idea of ENDPOS is, indent each line, stopping when
+;; ENDPOS is encountered.  But it's too much of a pain to make that work.
 (defun indent-c-exp (&optional endpos)
-  "Indent each line of the C grouping following point.
-If optional arg ENDPOS is given, indent each line, stopping when
-ENDPOS is encountered."
+  "Indent each line of the C grouping following point."
   (interactive)
   (let* ((indent-stack (list nil))
         (opoint (point))  ;; May be altered below.
@@ -874,13 +1204,15 @@ ENDPOS is encountered."
                      (save-excursion (forward-char 1)
                                      (beginning-of-defun)
                                      (setq funbeg (point)))
+                     (setq opoint funbeg)
                      ;; Try to find containing open,
                      ;; but don't scan past that fcn-start.
                      (save-restriction
                        (narrow-to-region funbeg (point))
                        (condition-case nil
                            (save-excursion
-                             (backward-up-list 1) (point))
+                             (backward-up-list 1)
+                             (point))
                          ;; We gave up: must be between fcns.
                          ;; Set opoint to beg of prev fcn
                          ;; since otherwise calculate-c-indent
@@ -892,7 +1224,7 @@ ENDPOS is encountered."
         restart outer-loop-done inner-loop-done state ostate
         this-indent last-sexp
         at-else at-brace at-while
-        last-depth
+        last-depth this-point
         (next-depth 0))
     ;; If the braces don't match, get an error right away.
     (save-excursion
@@ -903,6 +1235,12 @@ ENDPOS is encountered."
        (and (re-search-forward
              comment-start-skip
              (save-excursion (end-of-line) (point)) t)
+            ;; Make sure this isn't a comment alone on a line
+            ;; (which should be indented like code instead).
+            (save-excursion
+              (goto-char (match-beginning 0))
+              (skip-chars-backward " \t")
+              (not (bolp)))
             ;; Make sure the comment starter we found
             ;; is not actually in a string or quoted.
             (let ((new-state
@@ -930,9 +1268,12 @@ ENDPOS is encountered."
          (if (and (car (cdr (cdr state)))
                   (>= (car (cdr (cdr state))) 0))
              (setq last-sexp (car (cdr (cdr state)))))
-         (if (or (nth 4 ostate))
+         ;; If this line started within a comment, indent it as such.
+         (if (or (nth 4 ostate) (nth 7 ostate))
              (c-indent-line))
-         (if (or (nth 3 state))
+         ;; If it ends outside of comments or strings, exit the inner loop.
+         ;; Otherwise move on to next line.
+         (if (or (nth 3 state) (nth 4 state) (nth 7 state))
              (forward-line 1)
            (setq inner-loop-done t)))
        (and endpos
@@ -965,49 +1306,75 @@ ENDPOS is encountered."
                                                        (point)))))
          (forward-line 1)
          (skip-chars-forward " \t")
-         (if (eolp)
+         ;; Don't really reindent if the line is just whitespace,
+         ;; or if it is past the endpos.
+         ;; (The exit test in the outer while
+         ;; does not exit until we have passed the first line
+         ;; past the region.)
+         (if (or (eolp) (and endpos (>= (point) endpos)))
              nil
+           ;; Is this line in a new nesting level?
+           ;; In other words, is this the first line that
+           ;; starts in the new level?
            (if (and (car indent-stack)
                     (>= (car indent-stack) 0))
-               ;; Line is on an existing nesting level.
-               ;; Lines inside parens are handled specially.
-               (if (/= (char-after (car contain-stack)) ?{)
-                   (setq this-indent (car indent-stack))
-                 ;; Line is at statement level.
-                 ;; Is it a new statement?  Is it an else?
-                 ;; Find last non-comment character before this line
-                 (save-excursion
-                   (setq at-else (looking-at "else\\W"))
-                   (setq at-brace (= (following-char) ?{))
-                   (setq at-while (looking-at "while\\b"))
-                   (c-backward-to-noncomment opoint)
-                   (if (not (memq (preceding-char) '(nil ?\, ?\; ?} ?: ?{)))
-                       ;; Preceding line did not end in comma or semi;
-                       ;; indent this line  c-continued-statement-offset
-                       ;; more than previous.
-                       (progn
-                         (c-backward-to-start-of-continued-exp (car contain-stack))
-                         (setq this-indent
-                               (+ c-continued-statement-offset (current-column)
-                                  (if at-brace c-continued-brace-offset 0))))
-                     ;; Preceding line ended in comma or semi;
-                     ;; use the standard indent for this level.
-                     (cond (at-else (progn (c-backward-to-start-of-if opoint)
-                                           (setq this-indent
-                                                 (current-indentation))))
-                           ((and at-while (c-backward-to-start-of-do opoint))
-                            (setq this-indent (current-indentation)))
-                           (t (setq this-indent (car indent-stack)))))))
-             ;; Just started a new nesting level.
+               nil
+             ;; Yes.
              ;; Compute the standard indent for this level.
-             (let ((val (calculate-c-indent
-                          (if (car indent-stack)
-                              (- (car indent-stack))
-                            opoint))))
-               (setcar indent-stack
-                       (setq this-indent val))))
+             (let (val)
+               (if (= (char-after (car contain-stack)) ?{)
+                   (save-excursion
+                     (goto-char (car contain-stack))
+                     (setq val (calculate-c-indent-after-brace)))
+                 (setq val (calculate-c-indent
+                            (if (car indent-stack)
+                                (- (car indent-stack))
+                              opoint))))
+               ;; t means we are in a block comment and should
+               ;; calculate accordingly.
+               (if (eq val t)
+                   (setq val (calculate-c-indent-within-comment)))
+               (setcar indent-stack val)))
+           ;; Adjust indent of this individual line
+           ;; based on its predecessor.
+           ;; Handle continuation lines, if, else, while, and so on.
+           (if (/= (char-after (car contain-stack)) ?{)
+               (setq this-indent (car indent-stack))
+             ;; Line is at statement level.
+             ;; Is it a new statement?  Is it an else?
+             ;; Find last non-comment character before this line
+             (save-excursion
+               (setq this-point (point))
+               (setq at-else (and (looking-at "else\\b")
+                                  (not (looking-at "else\\s_"))))
+               (setq at-brace (= (following-char) ?{))
+               (setq at-while (and (looking-at "while\\b")
+                                   (not (looking-at "while\\s_"))))
+               (if (= (following-char) ?})
+                   (setq this-indent (car indent-stack))
+                 (c-backward-to-noncomment opoint)
+                 (if (not (memq (preceding-char) '(0 ?\, ?\; ?} ?: ?{)))
+                     ;; Preceding line did not end in comma or semi;
+                     ;; indent this line  c-continued-statement-offset
+                     ;; more than previous.
+                     (progn
+                       (c-backward-to-start-of-continued-exp (car contain-stack))
+                       (setq this-indent
+                             (+ c-continued-statement-offset (current-column)
+                                (if at-brace c-continued-brace-offset 0))))
+                   ;; Preceding line ended in comma or semi;
+                   ;; use the standard indent for this level.
+                   (cond (at-else (progn (c-backward-to-start-of-if opoint)
+                                         (setq this-indent
+                                               (current-indentation))))
+                         ((and at-while (c-backward-to-start-of-do opoint))
+                          (setq this-indent (current-indentation)))
+                         ((eq (preceding-char) ?\,)
+                          (goto-char this-point)
+                          (setq this-indent (calculate-c-indent)))
+                         (t (setq this-indent (car indent-stack))))))))
            ;; Adjust line indentation according to its contents
-           (if (or (looking-at "case[ \t'/(]\\|default\\>")
+           (if (or (looking-at c-switch-label-regexp)
                    (and (looking-at "[A-Za-z]")
                         (save-excursion
                           (forward-sexp 1)
@@ -1016,7 +1383,16 @@ ENDPOS is encountered."
            (if (= (following-char) ?})
                (setq this-indent (- this-indent c-indent-level)))
            (if (= (following-char) ?{)
-               (setq this-indent (+ this-indent c-brace-offset)))
+               ;; Don't move an open-brace in column 0.
+               ;; This is good when constructs such as
+               ;; `extern "C" {' surround a function definition
+               ;; that should be indented as usual.
+               ;; It is also good for nested functions.
+               ;; It is bad when an open-brace is indented at column 0
+               ;; and you want to fix that, but we can't win 'em all.
+               (if (zerop (current-column))
+                   (setq this-indent 0)
+                 (setq this-indent (+ this-indent c-brace-offset))))
            ;; Don't leave indentation in empty lines.
            (if (eolp) (setq this-indent 0))
            ;; Put chosen indentation into effect.
@@ -1027,17 +1403,18 @@ ENDPOS is encountered."
                  (indent-to this-indent)))
            ;; Indent any comment following the text.
            (or (looking-at comment-start-skip)
-               (let ((beg (point)))
-                 (and (re-search-forward
-                       comment-start-skip
-                       (save-excursion (end-of-line) (point)) t)
-                      ;; Make sure the comment starter we found
-                      ;; is not actually in a string or quoted.
-                      (let ((new-state
-                             (parse-partial-sexp beg (point)
-                                                 nil nil state)))
-                        (and (not (nth 3 new-state)) (not (nth 5 new-state))))
-                     (progn (indent-for-comment) (beginning-of-line)))))))))))
+               (save-excursion
+                 (let ((beg (point)))
+                   (and (re-search-forward
+                         comment-start-skip
+                         (save-excursion (end-of-line) (point)) t)
+                        ;; Make sure the comment starter we found
+                        ;; is not actually in a string or quoted.
+                        (let ((new-state
+                               (parse-partial-sexp beg (point)
+                                                   nil nil state)))
+                          (and (not (nth 3 new-state)) (not (nth 5 new-state))))
+                        (indent-for-comment)))))))))))
 
 ;; Look at all comment-start strings in the current line after point.
 ;; Return t if one of them starts a real comment.
@@ -1061,10 +1438,48 @@ ENDPOS is encountered."
 (defun c-indent-region (start end)
   (save-excursion
     (goto-char start)
-    (let ((endmark (copy-marker end)))
-      (and (bolp) (not (eolp))
-          (c-indent-line))
-      (indent-c-exp endmark)
+    ;; Advance to first nonblank line.
+    (skip-chars-forward " \t\n")
+    (beginning-of-line)
+    (let ((endmark (copy-marker end))
+         (c-tab-always-indent t))
+      (while (and (bolp) (not (eobp)) (< (point) endmark))
+       ;; Indent one line as with TAB.
+       (let ((shift-amt (c-indent-line))
+             nextline sexpbeg sexpend)
+         (if (save-excursion (beginning-of-line) (looking-at "[ \t]*#"))
+             (forward-line 1)
+           (save-excursion
+             ;; Find beginning of following line.
+             (save-excursion
+               (forward-line 1) (setq nextline (point)))
+             ;; Find first beginning-of-sexp for sexp extending past this line.
+             (beginning-of-line)
+             (while (< (point) nextline)
+               (condition-case nil
+                   (progn
+                     (forward-sexp 1)
+                     (setq sexpend (point-marker)))
+                 (error (setq sexpend nil)
+                        (goto-char nextline)))
+               (skip-chars-forward " \t\n"))
+             (if sexpend
+                 (progn
+                   ;; Make sure the sexp we found really starts on the
+                   ;; current line and extends past it.
+                   (goto-char sexpend)
+                   (backward-sexp 1)
+                   (setq sexpbeg (point)))))
+           ;; If that sexp ends within the region,
+           ;; indent it all at once, fast.
+           (if (and sexpend (> sexpend nextline) (<= sexpend endmark)
+                    (< sexpbeg nextline))
+               (progn
+                 (indent-c-exp)
+                 (goto-char sexpend)))
+           ;; Move to following line and try again.
+           (and sexpend (set-marker sexpend nil))
+           (forward-line 1))))
       (set-marker endmark nil))))
 \f
 (defun set-c-style (style &optional global)
@@ -1073,8 +1488,9 @@ The arguments are a string representing the desired style
 and a flag which, if non-nil, means to set the style globally.
 \(Interactively, the flag comes from the prefix argument.)
 Available styles are GNU, K&R, BSD and Whitesmith."
-  (interactive (list (completing-read "Use which C indentation style? "
-                                      c-style-alist nil t)
+  (interactive (list (let ((completion-ignore-case t))
+                      (completing-read "Use which C indentation style? "
+                                       c-style-alist nil t))
                     current-prefix-arg))
   (let ((vars (cdr (assoc style c-style-alist))))
     (or vars
@@ -1148,10 +1564,12 @@ definition and conveniently use this command."
 
 (defun c-delete-backslash ()
   (end-of-line)
-  (forward-char -1)
-  (if (looking-at "\\\\")
-      (delete-region (1+ (point))
-                    (progn (skip-chars-backward " \t") (point)))))
+  (or (bolp)
+      (progn
+       (forward-char -1)
+       (if (looking-at "\\\\")
+           (delete-region (1+ (point))
+                          (progn (skip-chars-backward " \t") (point)))))))
 \f
 (defun c-up-conditional (count)
   "Move back to the containing preprocessor conditional, leaving mark behind.
@@ -1160,15 +1578,28 @@ move forward to the end of the containing preprocessor conditional.
 When going backwards, `#elif' is treated like `#else' followed by `#if'.
 When going forwards, `#elif' is ignored."
   (interactive "p")
-  (let* ((forward (< count 0))
+  (c-forward-conditional (- count) t))
+
+(defun c-backward-conditional (count &optional up-flag)
+  "Move back across a preprocessor conditional, leaving mark behind.
+A prefix argument acts as a repeat count.  With a negative argument,
+move forward across a preprocessor conditional."
+  (interactive "p")
+  (c-forward-conditional (- count) up-flag))
+
+(defun c-forward-conditional (count &optional up-flag)
+  "Move forward across a preprocessor conditional, leaving mark behind.
+A prefix argument acts as a repeat count.  With a negative argument,
+move backward across a preprocessor conditional."
+  (interactive "p")
+  (let* ((forward (> count 0))
         (increment (if forward -1 1))
         (search-function (if forward 're-search-forward 're-search-backward))
         (opoint (point))
         (new))
     (save-excursion
       (while (/= count 0)
-       (if forward (end-of-line))
-       (let ((depth 0) found)
+       (let ((depth (if up-flag 0 -1)) found)
          (save-excursion
            ;; Find the "next" significant line in the proper direction.
            (while (and (not found)
@@ -1180,28 +1611,37 @@ When going forwards, `#elif' is ignored."
                        ;; the regexp matcher.
                        (funcall search-function
                                 "#[ \t]*\\(if\\|elif\\|endif\\)"
-                                nil t)
-                       (progn
-                         (beginning-of-line)
-                         (looking-at "^[ \t]*#[ \t]*\\(if\\|elif\\|endif\\)")))
-             ;; Update depth according to what we found.
+                                nil t))
              (beginning-of-line)
-             (cond ((looking-at "[ \t]*#[ \t]*endif")
-                    (setq depth (+ depth increment)))
-                   ((looking-at "[ \t]*#[ \t]*elif")
-                    (if (and forward (= depth 0))
-                        (setq found (point))))
-                   (t (setq depth (- depth increment))))
-             ;; If this line exits a level of conditional, exit inner loop.
-             (if (< depth 0)
-                 (setq found (point)))
-             ;; When searching forward, start from end of line
-             ;; so that we don't find the same line again.
-             (if forward (end-of-line))))
+             ;; Now verify it is really a preproc line.
+             (if (looking-at "^[ \t]*#[ \t]*\\(if\\|elif\\|endif\\)")
+                 (let ((prev depth))
+                   ;; Update depth according to what we found.
+                   (beginning-of-line)
+                   (cond ((looking-at "[ \t]*#[ \t]*endif")
+                          (setq depth (+ depth increment)))
+                         ((looking-at "[ \t]*#[ \t]*elif")
+                          (if (and forward (= depth 0))
+                              (setq found (point))))
+                         (t (setq depth (- depth increment))))
+                   ;; If we are trying to move across, and we find
+                   ;; an end before we find a beginning, get an error.
+                   (if (and (< prev 0) (< depth prev))
+                       (error (if forward
+                                  "No following conditional at this level"
+                                "No previous conditional at this level")))
+                   ;; When searching forward, start from next line
+                   ;; so that we don't find the same line again.
+                   (if forward (forward-line 1))
+                   ;; If this line exits a level of conditional, exit inner loop.
+                   (if (< depth 0)
+                       (setq found (point))))
+               ;; If the line is not really a conditional, skip past it.
+               (if forward (end-of-line)))))
          (or found
              (error "No containing preprocessor conditional"))
          (goto-char (setq new found)))
-       (setq count (- count increment))))
+       (setq count (+ count increment))))
     (push-mark)
     (goto-char new)))