Use `called-interactively-p' instead of `interactive-p'.
[bpt/emacs.git] / lisp / progmodes / fortran.el
index a8d7807..02346c1 100644 (file)
@@ -1,8 +1,8 @@
 ;;; fortran.el --- Fortran mode for GNU Emacs
 
 ;; Copyright (C) 1986, 1993, 1994, 1995, 1997, 1998, 1999, 2000, 2001,
-;;               2002, 2003, 2004, 2005, 2006, 2007, 2008
-;;               Free Software Foundation, Inc.
+;;   2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009
+;;   Free Software Foundation, Inc.
 
 ;; Author: Michael D. Prange <prange@erl.mit.edu>
 ;; Maintainer: Glenn Morris <rgm@gnu.org>
 
 ;; This file is part of GNU Emacs.
 
-;; GNU Emacs is free software; you can redistribute it and/or modify
+;; GNU Emacs is free software: you can redistribute it and/or modify
 ;; it under the terms of the GNU General Public License as published by
-;; the Free Software Foundation; either version 3, or (at your option)
-;; any later version.
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
 
 ;; GNU Emacs is distributed in the hope that it will be useful,
 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -21,9 +21,7 @@
 ;; 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, Inc., 51 Franklin Street, Fifth Floor,
-;; Boston, MA 02110-1301, USA.
+;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
 
 ;;; Commentary:
 
@@ -85,8 +83,8 @@ A non-nil value specifies tab-digit style of continuation control.
 A value of nil specifies that continuation lines are marked
 with a character in column 6."
   :type  'boolean
+  :safe  'booleanp
   :group 'fortran-indent)
-(put 'fortran-tab-mode-default 'safe-local-variable 'booleanp)
 
 ;; TODO add more detail of what tab mode is to doc string.
 (defcustom fortran-tab-mode-string
@@ -98,34 +96,35 @@ with a character in column 6."
                                           (interactive)
                                           (describe-variable
                                            'fortran-tab-mode-string))))
-  "String to appear in mode line in TAB format buffers."
+  "String to appear in mode line in TAB format buffers.
+See Info node `(emacs)ForIndent Cont'."
   :type  'string
+  :risky t
   :group 'fortran-indent)
-(put 'fortran-tab-mode-string 'risky-local-variable t)
 
 (defcustom fortran-do-indent 3
   "Extra indentation applied to DO blocks."
   :type  'integer
+  :safe  'integerp
   :group 'fortran-indent)
-(put 'fortran-do-indent 'safe-local-variable 'integerp)
 
 (defcustom fortran-if-indent 3
   "Extra indentation applied to IF, SELECT CASE and WHERE blocks."
   :type  'integer
+  :safe  'integerp
   :group 'fortran-indent)
-(put 'fortran-if-indent 'safe-local-variable 'integerp)
 
 (defcustom fortran-structure-indent 3
   "Extra indentation applied to STRUCTURE, UNION, MAP and INTERFACE blocks."
   :type  'integer
+  :safe  'integerp
   :group 'fortran-indent)
-(put 'fortran-structure-indent 'safe-local-variable 'integerp)
 
 (defcustom fortran-continuation-indent 5
   "Extra indentation applied to continuation lines."
   :type  'integer
+  :safe  'integerp
   :group 'fortran-indent)
-(put 'fortran-continuation-indent 'safe-local-variable 'integerp)
 
 (defcustom fortran-comment-indent-style 'fixed
   "How to indent comments.
@@ -136,24 +135,24 @@ nil forces comment lines not to be touched;
 `relative' indents to current Fortran indentation plus
   `fortran-comment-line-extra-indent'."
   :type  '(radio (const :tag "Untouched" nil) (const fixed) (const relative))
+  :safe  (lambda (value) (memq value '(nil fixed relative)))
   :group 'fortran-indent)
-(put 'fortran-comment-indent 'safe-local-variable
-     (lambda (value) (memq value '(nil fixed relative))))
 
 (defcustom fortran-comment-line-extra-indent 0
   "Amount of extra indentation for text within full-line comments."
   :type  'integer
+  :safe  'integerp
   :group 'fortran-indent
   :group 'fortran-comment)
-(put 'fortran-comment-line-extra-indent 'safe-local-variable 'integerp)
 
 (defcustom fortran-comment-line-start "C"
   "Delimiter inserted to start new full-line comment.
-You might want to change this to \"*\", for instance."
+You might want to change this to \"*\", for instance; or \"!\" to
+allow trailing comments on a line."
   :version "21.1"
   :type    'string
+  :safe    'stringp
   :group   'fortran-comment)
-(put 'fortran-comment-line-start 'safe-local-variable 'stringp)
 
 ;; This used to match preprocessor lines too, but that messes up
 ;; filling and doesn't seem to be necessary.
@@ -162,8 +161,8 @@ You might want to change this to \"*\", for instance."
   "Regexp to match the start of a full-line comment."
   :version "21.1"
   :type    'regexp
+  :safe    'stringp
   :group   'fortran-comment)
-(put 'fortran-comment-line-start-skip 'safe-local-variable 'stringp)
 
 (defcustom fortran-directive-re
   "^[ \t]*#.*"
@@ -172,20 +171,20 @@ The matching text will be fontified with `font-lock-keyword-face'.
 The matching line will be given zero indentation."
   :version "22.1"
   :type    'regexp
+  :safe    'stringp
   :group   'fortran-indent)
-(put 'fortran-directive-re 'safe-local-variable 'stringp)
 
 (defcustom fortran-minimum-statement-indent-fixed 6
   "Minimum statement indentation for fixed format continuation style."
   :type  'integer
+  :safe  'integerp
   :group 'fortran-indent)
-(put 'fortran-minimum-statement-indent-fixed 'safe-local-variable 'integerp)
 
 (defcustom fortran-minimum-statement-indent-tab (max tab-width 6)
   "Minimum statement indentation for TAB format continuation style."
   :type  'integer
+  :safe  'integerp
   :group 'fortran-indent)
-(put 'fortran-minimum-statement-indent-tab 'safe-local-variable 'integerp)
 
 ;; Note that this is documented in the v18 manuals as being a string
 ;; of length one rather than a single character.
@@ -194,31 +193,29 @@ The matching line will be given zero indentation."
   "Single-character string inserted for Fortran comment indentation.
 Normally a space."
   :type  'string
+  :safe  (lambda (value) (or (characterp value)
+                             (and (stringp value) (= (length value) 1))))
   :group 'fortran-comment)
-(put 'fortran-comment-indent-char 'safe-local-variable
-     (lambda (value) (or (characterp value)
-                         (and (stringp value)
-                              (= (length value) 1)))))
 
 (defcustom fortran-line-number-indent 1
   "Maximum indentation for Fortran line numbers.
 5 means right-justify them within their five-column field."
   :type  'integer
+  :safe  'integerp
   :group 'fortran-indent)
-(put 'fortran-line-number-indent 'safe-local-variable 'integerp)
 
 (defcustom fortran-check-all-num-for-matching-do nil
   "Non-nil causes all numbered lines to be treated as possible DO loop ends."
   :type  'boolean
+  :safe  'booleanp
   :group 'fortran)
-(put 'fortran-check-all-num-for-matching-do 'safe-local-variable 'booleanp)
 
 (defcustom fortran-blink-matching-if nil
   "Non-nil causes \\[fortran-indent-line] on ENDIF to blink on matching IF.
 Also, from an ENDDO statement blink on matching DO [WHILE] statement."
   :type  'boolean
+  :safe  'booleanp
   :group 'fortran)
-(put 'fortran-blink-matching-if 'safe-local-variable 'booleanp)
 
 (defcustom fortran-continuation-string "$"
   "Single-character string used for Fortran continuation lines.
@@ -226,25 +223,23 @@ In fixed format continuation style, this character is inserted in
 column 6 by \\[fortran-split-line] to begin a continuation line.
 Also, if \\[fortran-indent-line] finds this at the beginning of a
 line, it will convert the line into a continuation line of the
-appropriate style. Normally $."
+appropriate style.  Normally \"$\"."
   :type  'string
+  :safe  (lambda (value) (and (stringp value) (= (length value) 1)))
   :group 'fortran)
-(put 'fortran-continuation-string 'safe-local-variable
-     (lambda (value) (and (stringp value)
-                          (= (length value) 1))))
 
 (defcustom fortran-comment-region "c$$$"
   "String inserted by \\[fortran-comment-region] at start of each \
 line in region."
   :type  'string
+  :safe  'stringp
   :group 'fortran-comment)
-(put 'fortran-comment-region 'safe-local-variable 'stringp)
 
 (defcustom fortran-electric-line-number t
   "Non-nil causes line numbers to be moved to the correct column as typed."
   :type  'boolean
+  :safe  'booleanp
   :group 'fortran)
-(put 'fortran-electric-line-number 'safe-local-variable 'booleanp)
 
 ;; TODO use fortran-line-length, somehow.
 (defcustom fortran-column-ruler-fixed
@@ -256,8 +251,8 @@ line in region."
 This variable is used in fixed format mode.
 See the variable `fortran-column-ruler-tab' for TAB format mode."
   :type  'string
+  :safe  'stringp
   :group 'fortran)
-(put 'fortran-column-ruler-fixed 'safe-local-variable 'stringp)
 
 ;; TODO use fortran-line-length, somehow.
 (defcustom fortran-column-ruler-tab
@@ -269,21 +264,21 @@ See the variable `fortran-column-ruler-tab' for TAB format mode."
 This variable is used in TAB format mode.
 See the variable `fortran-column-ruler-fixed' for fixed format mode."
   :type  'string
+  :safe  'stringp
   :group 'fortran)
-(put 'fortran-column-ruler-tab 'safe-local-variable 'stringp)
 
 (defcustom fortran-analyze-depth 100
   "Number of lines to scan to identify fixed or TAB format style."
   :type  'integer
+  :safe  'integerp
   :group 'fortran)
-(put 'fortran-analyze-depth 'safe-local-variable 'integerp)
 
 (defcustom fortran-break-before-delimiters t
   "Non-nil causes filling to break lines before delimiters.
 Delimiters are characters matching the regexp `fortran-break-delimiters-re'."
   :type  'boolean
+  :safe  'booleanp
   :group 'fortran)
-(put 'fortran-break-before-delimiters 'safe-local-variable 'booleanp)
 
 ;; TODO 0 as no-limit, as per g77.
 (defcustom fortran-line-length 72
@@ -296,6 +291,7 @@ buffers and the default) or the function
 buffer).  This corresponds to the g77 compiler option
 `-ffixed-line-length-N'."
   :type 'integer
+  :safe 'integerp
   :initialize 'custom-initialize-default
   :set (lambda (symbol value)
          ;; Do all fortran buffers, and the default.
@@ -303,7 +299,6 @@ buffer).  This corresponds to the g77 compiler option
   :version "23.1"
   :group 'fortran)
 
-(put 'fortran-line-length 'safe-local-variable 'integerp)
 (make-variable-buffer-local 'fortran-line-length)
 
 (defcustom fortran-mode-hook nil
@@ -330,6 +325,13 @@ characters long.")
 (defconst fortran-if-start-re "\\(\\(\\sw\\|\\s_\\)+:[ \t]*\\)?if[ \t]*("
   "Regexp matching the start of an IF statement.")
 
+;; Note fortran-current-defun uses the subgroups.
+(defconst fortran-start-prog-re
+  "^[ \t]*\\(program\\|subroutine\\|function\
+\\|[ \ta-z0-9*()]*[ \t]+function\\|\
+\\(block[ \t]*data\\)\\)"
+  "Regexp matching the start of a subprogram, from the line start.")
+
 (defconst fortran-end-prog-re1
   "end\
 \\([ \t]*\\(program\\|subroutine\\|function\\|block[ \t]*data\\)\\>\
@@ -391,8 +393,8 @@ program\\|subroutine\\)\\>[ \t]*\\(\\sw+\\)?"
                           'paren) "\\>")
            ;; Builtin operators.
            (concat "\\." (regexp-opt
-                          '("and" "or" "not" "lt" "le" "eq" "ge"
-                            "gt" "ne" "true" "false")
+                          '("and" "eq" "eqv" "false" "ge" "gt" "le" "lt" "ne"
+                            "neqv" "not" "or" "true")
                           'paren) "\\.")
            ;; do/goto keywords and targets, and goto tags.
            '("\\<\\(do\\|go *to\\)\\>[ \t]*\\([0-9]+\\)?"
@@ -560,9 +562,9 @@ Used in the Fortran entry in `hs-special-modes-alist'.")
 \f
 (defvar fortran-mode-syntax-table
   (let ((table (make-syntax-table)))
-    ;; We might like `;' to be punctuation (g77 multi-statement
-    ;; lines), but that screws abbrevs.
-    (modify-syntax-entry ?\; "w"  table)
+    ;; Was a word-constituent (for abbrevs), now punctuation (g77
+    ;; multi-statement lines).
+    (modify-syntax-entry ?\; "."  table)
     (modify-syntax-entry ?\r " "  table)
     (modify-syntax-entry ?+  "."  table)
     (modify-syntax-entry ?-  "."  table)
@@ -619,14 +621,21 @@ Used in the Fortran entry in `hs-special-modes-alist'.")
 
     (easy-menu-define fortran-menu map "Menu for Fortran mode."
       `("Fortran"
-        ["Manual" (info "(emacs)Fortran")]
+        ["Manual" (info "(emacs)Fortran") :active t
+         :help "Read the Emacs manual chapter on Fortran mode"]
         ("Customization"
          ,(custom-menu-create 'fortran)
-         ["Set"  Custom-set t]
-         ["Save" Custom-save t]
-         ["Reset to Current" Custom-reset-current t]
-         ["Reset to Saved"   Custom-reset-saved t]
-         ["Reset to Standard Settings" Custom-reset-standard t]
+         ;; FIXME useless?
+         ["Set"  Custom-set :active t
+          :help "Set current value of all edited settings in the buffer"]
+         ["Save" Custom-save :active t
+          :help "Set and save all edited settings"]
+         ["Reset to Current" Custom-reset-current :active t
+          :help "Reset all edited settings to current"]
+         ["Reset to Saved" Custom-reset-saved :active t
+          :help "Reset all edited or set settings to saved"]
+         ["Reset to Standard Settings" Custom-reset-standard :active t
+          :help "Erase all cusomizations in buffer"]
          )
         "--"
         ["Comment Region" fortran-comment-region mark-active]
@@ -636,9 +645,12 @@ Used in the Fortran entry in `hs-special-modes-alist'.")
         ["Indent Region"     indent-region mark-active]
         ["Indent Subprogram" fortran-indent-subprogram t]
         "--"
-        ["Beginning of Subprogram" fortran-beginning-of-subprogram t]
-        ["End of Subprogram"       fortran-end-of-subprogram       t]
+        ["Beginning of Subprogram" fortran-beginning-of-subprogram :active t
+         :help "Move point to the start of the current subprogram"]
+        ["End of Subprogram" fortran-end-of-subprogram :active t
+         :help "Move point to the end of the current subprogram"]
         ("Mark"
+         :help "Mark a region of code"
          ["Subprogram" mark-defun      t]
          ["IF Block"   fortran-mark-if t]
          ["DO Block"   fortran-mark-do t]
@@ -646,105 +658,101 @@ Used in the Fortran entry in `hs-special-modes-alist'.")
         ["Narrow to Subprogram" narrow-to-defun t]
         ["Widen" widen t]
         "--"
-        ["Temporary column ruler" fortran-column-ruler  t]
+        ["Temporary Column Ruler" fortran-column-ruler :active t
+         :help "Briefly display Fortran column numbers"]
         ;; May not be '72', depending on fortran-line-length, but this
         ;; seems ok for a menu item.
-        ["72-column window"       fortran-window-create t]
+        ["72-column Window" fortran-window-create :active t
+         :help "Set window width to Fortran line length"]
         ["Full Width Window"
          (enlarge-window-horizontally (- (frame-width) (window-width)))
-         (not (window-full-width-p))]
-        ["Momentary 72-column window" fortran-window-create-momentarily t]
+         :active (not (window-full-width-p))
+         :help "Make window full width"]
+        ["Momentary 72-Column Window" fortran-window-create-momentarily
+         :active t :help "Briefly set window width to Fortran line length"]
         "--"
-        ["Break Line at Point"    fortran-split-line t]
-        ["Join Line"              fortran-join-line  t]
-        ["Fill Statement/Comment" fill-paragraph     t]
+        ["Break Line at Point" fortran-split-line :active t
+         :help "Break the current line at point"]
+        ["Join Line" fortran-join-line :active t
+         :help "Join the current line to the previous one"]
+        ["Fill Statement/Comment" fill-paragraph t]
         "--"
-        ["Toggle auto-fill"   auto-fill-mode :selected auto-fill-function
-         :style toggle]
-        ["Toggle abbrev-mode" abbrev-mode    :selected abbrev-mode
-         :style toggle]
-        ["Add imenu Menu" imenu-add-menubar-index
+        ["Toggle Auto Fill" auto-fill-mode :selected auto-fill-function
+         :style toggle
+         :help "Automatically fill text while typing in this buffer"]
+        ["Toggle Abbrev Mode" abbrev-mode :selected abbrev-mode
+         :style toggle :help "Expand abbreviations while typing in this buffer"]
+        ["Add Imenu Menu" imenu-add-menubar-index
          :active   (not (lookup-key (current-local-map) [menu-bar index]))
-         :included (fboundp 'imenu-add-to-menubar)]))
+         :included (fboundp 'imenu-add-to-menubar)
+         :help "Add an index menu to the menu-bar"]))
     map)
   "Keymap used in Fortran mode.")
 
 \f
-(defvar fortran-mode-abbrev-table
-  (progn
-    (define-abbrev-table 'fortran-mode-abbrev-table nil)
-    fortran-mode-abbrev-table)
-  "Abbrev table for Fortran mode.")
-
-(let (abbrevs-changed)
-  ;; Use the 6th arg (SYSTEM-FLAG) of define-abbrev if possible.
-  ;; Only use `apply' to quieten the byte-compiler.
-  (mapc
-   (function (lambda (element)
-               (condition-case nil
-                   (apply 'define-abbrev fortran-mode-abbrev-table
-                          (append element '(nil 0 t)))
-                 (wrong-number-of-arguments
-                  (apply 'define-abbrev fortran-mode-abbrev-table
-                         (append element '(nil 0)))))))
-   '((";au"   "automatic"         )
-     (";b"    "byte"              )
-     (";bd"   "block data"        )
-     (";ch"   "character"         )
-     (";cl"   "close"             )
-     (";c"    "continue"          )
-     (";cm"   "common"            )
-     (";cx"   "complex"           )
-     (";df"   "define"            )
-     (";di"   "dimension"         )
-     (";do"   "double"            )
-     (";dc"   "double complex"    )
-     (";dp"   "double precision"  )
-     (";dw"   "do while"          )
-     (";e"    "else"              )
-     (";ed"   "enddo"             )
-     (";el"   "elseif"            )
-     (";en"   "endif"             )
-     (";eq"   "equivalence"       )
-     (";ew"   "endwhere"          )
-     (";ex"   "external"          )
-     (";ey"   "entry"             )
-     (";f"    "format"            )
-     (";fa"   ".false."           )
-     (";fu"   "function"          )
-     (";g"    "goto"              )
-     (";im"   "implicit"          )
-     (";ib"   "implicit byte"     )
-     (";ic"   "implicit complex"  )
-     (";ich"  "implicit character")
-     (";ii"   "implicit integer"  )
-     (";il"   "implicit logical"  )
-     (";ir"   "implicit real"     )
-     (";inc"  "include"           )
-     (";in"   "integer"           )
-     (";intr" "intrinsic"         )
-     (";l"    "logical"           )
-     (";n"    "namelist"          )
-     (";o"    "open"              )     ; was ;op
-     (";pa"   "parameter"         )
-     (";pr"   "program"           )
-     (";ps"   "pause"             )
-     (";p"    "print"             )
-     (";rc"   "record"            )
-     (";re"   "real"              )
-     (";r"    "read"              )
-     (";rt"   "return"            )
-     (";rw"   "rewind"            )
-     (";s"    "stop"              )
-     (";sa"   "save"              )
-     (";st"   "structure"         )
-     (";sc"   "static"            )
-     (";su"   "subroutine"        )
-     (";tr"   ".true."            )
-     (";ty"   "type"              )
-     (";vo"   "volatile"          )
-     (";w"    "write"             )
-     (";wh"   "where"             ))))
+(define-abbrev-table 'fortran-mode-abbrev-table
+  (mapcar (lambda (e) (list (car e) (cdr e) nil :system t))
+          '((";au"   . "automatic"         )
+            (";b"    . "byte"              )
+            (";bd"   . "block data"        )
+            (";ch"   . "character"         )
+            (";cl"   . "close"             )
+            (";c"    . "continue"          )
+            (";cm"   . "common"            )
+            (";cx"   . "complex"           )
+            (";df"   . "define"            )
+            (";di"   . "dimension"         )
+            (";do"   . "double"            )
+            (";dc"   . "double complex"    )
+            (";dp"   . "double precision"  )
+            (";dw"   . "do while"          )
+            (";e"    . "else"              )
+            (";ed"   . "enddo"             )
+            (";el"   . "elseif"            )
+            (";en"   . "endif"             )
+            (";eq"   . "equivalence"       )
+            (";ew"   . "endwhere"          )
+            (";ex"   . "external"          )
+            (";ey"   . "entry"             )
+            (";f"    . "format"            )
+            (";fa"   . ".false."           )
+            (";fu"   . "function"          )
+            (";g"    . "goto"              )
+            (";im"   . "implicit"          )
+            (";ib"   . "implicit byte"     )
+            (";ic"   . "implicit complex"  )
+            (";ich"  . "implicit character")
+            (";ii"   . "implicit integer"  )
+            (";il"   . "implicit logical"  )
+            (";ir"   . "implicit real"     )
+            (";inc"  . "include"           )
+            (";in"   . "integer"           )
+            (";intr" . "intrinsic"         )
+            (";l"    . "logical"           )
+            (";n"    . "namelist"          )
+            (";o"    . "open"              ) ; was ;op
+            (";pa"   . "parameter"         )
+            (";pr"   . "program"           )
+            (";ps"   . "pause"             )
+            (";p"    . "print"             )
+            (";rc"   . "record"            )
+            (";re"   . "real"              )
+            (";r"    . "read"              )
+            (";rt"   . "return"            )
+            (";rw"   . "rewind"            )
+            (";s"    . "stop"              )
+            (";sa"   . "save"              )
+            (";st"   . "structure"         )
+            (";sc"   . "static"            )
+            (";su"   . "subroutine"        )
+            (";tr"   . ".true."            )
+            (";ty"   . "type"              )
+            (";vo"   . "volatile"          )
+            (";w"    . "write"             )
+            (";wh"   . "where"             )))
+  "Abbrev table for Fortran mode."
+  ;; Accept ; as the first char of an abbrev.  Also allow _ in abbrevs.
+  :regexp "\\(?:[^[:word:]_;]\\|^\\)\\(;?[[:word:]_]+\\)[^[:word:]_]*")
 
 \f
 ;;;###autoload
@@ -863,7 +871,7 @@ with no args, if that value is non-nil."
          nil t ((?/ . "$/") ("_$" . "w"))
          fortran-beginning-of-subprogram
          (font-lock-syntactic-keywords
-          . (fortran-font-lock-syntactic-keywords))))
+          . fortran-font-lock-syntactic-keywords)))
   (set (make-local-variable 'imenu-case-fold-search) t)
   (set (make-local-variable 'imenu-generic-expression)
        fortran-imenu-generic-expression)
@@ -1000,7 +1008,7 @@ With non-nil ARG, uncomments the region."
   "Typing ;\\[help-command] or ;? lists all the Fortran abbrevs.
 Any other key combination is executed normally."
   (interactive "*")
-  (insert last-command-char)
+  (insert last-command-event)
   (let* ((event (if (fboundp 'next-command-event) ; XEmacs
                     (next-command-event)
                   (read-event)))
@@ -1161,7 +1169,7 @@ Auto-indent does not happen if a numeric ARG is used."
             (looking-at "[0-9]"))       ; within a line number
         (self-insert-command (prefix-numeric-value arg))
       (skip-chars-backward " \t")
-      (insert last-command-char)
+      (insert last-command-event)
       (fortran-indent-line))))
 
 \f
@@ -1176,37 +1184,47 @@ Auto-indent does not happen if a numeric ARG is used."
                                          (+ fortran-line-length
                                             (line-beginning-position)))))))
 
-;; Note that you can't just check backwards for `subroutine' &c in
-;; case of un-marked main programs not at the start of the file.
+;; This is more complex than first expected because the beginning of a
+;; main program may be implicit (ie not marked by a PROGRAM statement).
+;; This would be fine (we could just go to bob in the absence of a match),
+;; except it need not even be the first subprogram in the file (eg it
+;; could follow a subroutine).  Hence we have to search for END
+;; statements instead.
+;; cf fortran-beginning-of-block, f90-beginning-of-subprogram
+;; Note that unlike the latter, we don't have to worry about nested
+;; subprograms (?).
+;; FIXME push-mark?
 (defun fortran-beginning-of-subprogram ()
   "Move point to the beginning of the current Fortran subprogram."
   (interactive)
-  (save-match-data
-    (let ((case-fold-search t))
-      (beginning-of-line -1)
-      (if (catch 'ok
-            (while (re-search-backward fortran-end-prog-re nil 'move)
-              (if (fortran-check-end-prog-re)
-                  (throw 'ok t))))
-          (forward-line)))))
-
+  (let ((case-fold-search t))
+    ;; If called already at the start of subprogram, go to the previous.
+    (beginning-of-line (if (bolp) 0 1))
+    (save-match-data
+      (or (looking-at fortran-start-prog-re)
+          ;; This leaves us at bob if before the first subprogram.
+          (eq (fortran-previous-statement) 'first-statement)
+          (if (or (catch 'ok
+                    (while (re-search-backward fortran-end-prog-re nil 'move)
+                      (if (fortran-check-end-prog-re) (throw 'ok t))))
+                  ;; If the search failed, must be at bob.
+                  ;; First code line is the start of the subprogram.
+                  ;; FIXME use a more rigorous test, cf fortran-next-statement?
+                  ;; Though that needs to handle continuations too.
+                  (not (looking-at "^\\([ \t]*[0-9]\\|[ \t]+[^!#]\\)")))
+              (fortran-next-statement))))))
+
+;; This is simpler than f-beginning-of-s because the end of a
+;; subprogram is never implicit.
 (defun fortran-end-of-subprogram ()
   "Move point to the end of the current Fortran subprogram."
   (interactive)
-  (save-match-data
-    (let ((case-fold-search t))
-      (if (save-excursion               ; on END
-            (beginning-of-line)
-            (and (looking-at fortran-end-prog-re)
-                 (fortran-check-end-prog-re)))
-          (forward-line)
-        (beginning-of-line 2)
-        (catch 'ok
-          (while (re-search-forward fortran-end-prog-re nil 'move)
-            (if (fortran-check-end-prog-re)
-                (throw 'ok t))))
-        (goto-char (match-beginning 0))
-        (forward-line)))))
+  (let ((case-fold-search t))
+    (beginning-of-line)
+    (save-match-data
+      (while (and (re-search-forward fortran-end-prog-re nil 'move)
+                  (not (fortran-check-end-prog-re))))
+      (forward-line))))
 
 (defun fortran-previous-statement ()
   "Move point to beginning of the previous Fortran statement.
@@ -1277,7 +1295,7 @@ If NUM is negative, go backward to the start of a block.  Does
 not check for consistency of block types.  Interactively, pushes
 mark before moving point."
   (interactive "p")
-  (if (interactive-p) (push-mark (point) t))
+  (if (called-interactively-p 'interactive) (push-mark (point) t))
   (and num (< num 0) (fortran-beginning-of-block (- num)))
   (let ((case-fold-search t)
         (count (or num 1)))
@@ -1310,7 +1328,7 @@ blocks.  If NUM is negative, go forward to the end of a block.
 Does not check for consistency of block types.  Interactively,
 pushes mark before moving point."
   (interactive "p")
-  (if (interactive-p) (push-mark (point) t))
+  (if (called-interactively-p 'interactive) (push-mark (point) t))
   (and num (< num 0) (fortran-end-of-block (- num)))
   (let ((case-fold-search t)
         (count (or num 1)))
@@ -1665,7 +1683,17 @@ Return point or nil."
               ((and (looking-at fortran-end-prog-re1)
                     (fortran-check-end-prog-re))
                ;; Previous END resets indent to minimum.
-               (setq icol fortran-minimum-statement-indent)))))
+               (setq icol fortran-minimum-statement-indent))
+              ;; Previous statement was a numbered DO loop without a
+              ;; closing CONTINUE or END DO, so we indented the
+              ;; terminator like the loop body.
+              ((and fortran-check-all-num-for-matching-do
+                    (not (looking-at "\\(continue\\|end[ \t]*do\\)\\>"))
+                    (progn
+                      (beginning-of-line)
+                      (and (looking-at "[ \t]*[0-9]+")
+                           (fortran-check-for-matching-do))))
+               (setq icol (- icol fortran-do-indent))))))
     (save-excursion
       (beginning-of-line)
       (cond ((looking-at "[ \t]*$"))
@@ -1690,8 +1718,12 @@ Return point or nil."
                        6
                      (+ icol fortran-continuation-indent))))
             (first-statement)
+            ;; The terminating statement is actually part of the
+            ;; loop body, so unless it is a CONTINUE or END DO, we
+            ;; indent it like the loop body (see above).
             ((and fortran-check-all-num-for-matching-do
-                  (looking-at "[ \t]*[0-9]+")
+                  (looking-at "[ \t]*[0-9]+[ \t]*\
+\\(continue\\|end[ \t]*do\\)\\>")
                   (fortran-check-for-matching-do))
              (setq icol (- icol fortran-do-indent)))
             (t
@@ -2117,19 +2149,16 @@ arg DO-SPACE prevents stripping the whitespace."
       (replace-match "" nil nil nil 1)
       (unless do-space (delete-horizontal-space)))))
 
-;; This code used to live in add-log.el, but this is a better place
-;; for it.
+;; This code used to live in add-log.el, but this is a better place for it.
 (defun fortran-current-defun ()
   "Function to use for `add-log-current-defun-function' in Fortran mode."
   (save-excursion
     ;; We must be inside function body for this to work.
     (fortran-beginning-of-subprogram)
-    (let ((case-fold-search t))         ; case-insensitive
+    (let ((case-fold-search t))
       ;; Search for fortran subprogram start.
       (if (re-search-forward
-           (concat "^[ \t]*\\(program\\|subroutine\\|function"
-                   "\\|[ \ta-z0-9*()]*[ \t]+function\\|"
-                   "\\(block[ \t]*data\\)\\)")
+           fortran-start-prog-re
            (save-excursion (fortran-end-of-subprogram)
                            (point))
            t)