Function: c-beginning-of-statement-1

c-beginning-of-statement-1 is a byte-compiled function defined in cc-engine.el.gz.

Signature

(c-beginning-of-statement-1 &optional LIM IGNORE-LABELS NOERROR COMMA-DELIM HIT-LIM)

Documentation

Move to the start of the current statement or declaration, or to the previous one if already at the beginning of one. Only statements/declarations on the same level are considered, i.e. don't move into or out of sexps (not even normal expression parentheses).

If point is already at the earliest statement within braces or parens, this function doesn't move back into any whitespace preceding it; it returns same in this case.

Stop at statement continuation tokens like "else", "catch",
"finally" and the "while" in "do ... while" if the start point
is within the continuation. If starting at such a token, move to the corresponding statement start. If at the beginning of a statement, move to the closest containing statement if there is any. This might also stop at a continuation clause.

Labels are treated as part of the following statements if IGNORE-LABELS is non-nil. (FIXME: Doesn't work if we stop at a known
statement start keyword.) Otherwise, each label is treated as a
separate statement.

Macros are ignored (i.e. skipped over) unless point is within one, in which case the content of the macro is treated as normal code. Aside from any normal statement starts found in it, stop at the first token of the content in the macro, i.e. the expression of an "#if" or the start of the definition in a "#define". Also stop at start of macros before leaving them.

Return:
label if stopped at a label or "case...:" or "default:";
same if stopped at the beginning of the current statement;
up if stepped to a containing statement;
previous if stepped to a preceding statement;
beginning if stepped from a statement continuation clause to
                its start clause;
macro if stepped to a macro start; or
nil if HIT-LIM is non-nil, and we hit the limit.
Note that same and not label is returned if stopped at the same label without crossing the colon character.

LIM may be given to limit the search. If the search hits the limit, point will be left at the closest following token, or at the start position if that is less. If HIT-LIM is non-nil, nil is returned in this case, otherwise same.

NOERROR turns off error logging to c-parsing-error.

Normally only ; and virtual semicolons are considered to delimit statements, but if COMMA-DELIM is non-nil then , is treated as a delimiter too.

Note that this function might do hidden buffer changes. See the comment at the start of cc-engine.el for more info.

Source Code

;; Defined in /usr/src/emacs/lisp/progmodes/cc-engine.el.gz
(defun c-beginning-of-statement-1 (&optional lim ignore-labels
					     noerror comma-delim hit-lim)
  "Move to the start of the current statement or declaration, or to
the previous one if already at the beginning of one.  Only
statements/declarations on the same level are considered, i.e. don't
move into or out of sexps (not even normal expression parentheses).

If point is already at the earliest statement within braces or parens,
this function doesn't move back into any whitespace preceding it; it
returns `same' in this case.

Stop at statement continuation tokens like \"else\", \"catch\",
\"finally\" and the \"while\" in \"do ... while\" if the start point
is within the continuation.  If starting at such a token, move to the
corresponding statement start.  If at the beginning of a statement,
move to the closest containing statement if there is any.  This might
also stop at a continuation clause.

Labels are treated as part of the following statements if
IGNORE-LABELS is non-nil.  (FIXME: Doesn't work if we stop at a known
statement start keyword.)  Otherwise, each label is treated as a
separate statement.

Macros are ignored (i.e. skipped over) unless point is within one, in
which case the content of the macro is treated as normal code.  Aside
from any normal statement starts found in it, stop at the first token
of the content in the macro, i.e. the expression of an \"#if\" or the
start of the definition in a \"#define\".  Also stop at start of
macros before leaving them.

Return:
`label'         if stopped at a label or \"case...:\" or \"default:\";
`same'          if stopped at the beginning of the current statement;
`up'            if stepped to a containing statement;
`previous'      if stepped to a preceding statement;
`beginning'     if stepped from a statement continuation clause to
                its start clause;
`macro'         if stepped to a macro start; or
nil             if HIT-LIM is non-nil, and we hit the limit.
Note that `same' and not `label' is returned if stopped at the same
label without crossing the colon character.

LIM may be given to limit the search.  If the search hits the limit,
point will be left at the closest following token, or at the start
position if that is less.  If HIT-LIM is non-nil, nil is returned in
this case, otherwise `same'.

NOERROR turns off error logging to `c-parsing-error'.

Normally only `;' and virtual semicolons are considered to delimit
statements, but if COMMA-DELIM is non-nil then `,' is treated
as a delimiter too.

Note that this function might do hidden buffer changes.  See the
comment at the start of cc-engine.el for more info."

  ;; The bulk of this function is a pushdown automaton that looks at statement
  ;; boundaries and the tokens (such as "while") in c-opt-block-stmt-key.  Its
  ;; purpose is to keep track of nested statements, ensuring that such
  ;; statements are skipped over in their entirety (somewhat akin to what C-M-p
  ;; does with nested braces/brackets/parentheses).
  ;;
  ;; Note: The position of a boundary is the following token.
  ;;
  ;; Beginning with the current token (the one following point), move back one
  ;; sexp at a time (where a sexp is, more or less, either a token or the
  ;; entire contents of a brace/bracket/paren pair).  Each time a statement
  ;; boundary is crossed or a "while"-like token is found, update the state of
  ;; the PDA.  Stop at the beginning of a statement when the stack (holding
  ;; nested statement info) is empty and the position has been moved.
  ;;
  ;; The following variables constitute the PDA:
  ;;
  ;; sym:    This is either the "while"-like token (e.g. 'for) we've just
  ;;         scanned back over, 'boundary if we've just gone back over a
  ;;         statement boundary, or nil otherwise.
  ;; state:  takes one of the values (nil else else-boundary while
  ;;         while-boundary catch catch-boundary).
  ;;         nil means "no "while"-like token yet scanned".
  ;;         'else, for example, means "just gone back over an else".
  ;;         'else-boundary means "just gone back over a statement boundary
  ;;         immediately after having gone back over an else".
  ;; saved-pos: A vector of either saved positions (tok ptok pptok, etc.) or
  ;;         of error reporting information.
  ;; stack:  The stack onto which the PDA pushes its state.  Each entry
  ;;         consists of a saved value of state and saved-pos.  An entry is
  ;;         pushed when we move back over a "continuation" token (e.g. else)
  ;;         and popped when we encounter the corresponding opening token
  ;;         (e.g. if).
  ;;
  ;;
  ;; The following diagram briefly outlines the PDA.
  ;;
  ;; Common state:
  ;;   "else": Push state, goto state `else'.
  ;;   "while": Push state, goto state `while'.
  ;;   "catch" or "finally": Push state, goto state `catch'.
  ;;   boundary: Pop state.
  ;;   other: Do nothing special.
  ;;
  ;; State `else':
  ;;   boundary: Goto state `else-boundary'.
  ;;   other: Error, pop state, retry token.
  ;;
  ;; State `else-boundary':
  ;;   "if": Pop state.
  ;;   boundary: Error, pop state.
  ;;   other: See common state.
  ;;
  ;; State `while':
  ;;   boundary: Save position, goto state `while-boundary'.
  ;;   other: Pop state, retry token.
  ;;
  ;; State `while-boundary':
  ;;   "do": Pop state.
  ;;   boundary: Restore position if it's not at start, pop state. [*see below]
  ;;   other: See common state.
  ;;
  ;; State `catch':
  ;;   boundary: Goto state `catch-boundary'.
  ;;   other: Error, pop state, retry token.
  ;;
  ;; State `catch-boundary':
  ;;   "try": Pop state.
  ;;   "catch": Goto state `catch'.
  ;;   boundary: Error, pop state.
  ;;   other: See common state.
  ;;
  ;; [*] In the `while-boundary' state, we had pushed a 'while state, and were
  ;; searching for a "do" which would have opened a do-while.  If we didn't
  ;; find it, we discard the analysis done since the "while", go back to this
  ;; token in the buffer and restart the scanning there, this time WITHOUT
  ;; pushing the 'while state onto the stack.
  ;;
  ;; In addition to the above there is some special handling of labels
  ;; and macros.

  (let ((case-fold-search nil)
	(start (point))
	macro-start
	(delims (if comma-delim '(?\; ?,) '(?\;)))
	(c-commas-bound-stmts (or c-commas-bound-stmts comma-delim))
	c-maybe-labelp after-case:-pos saved
	;; Current position.
	pos
	;; Position of last stmt boundary character (e.g. ;).
	boundary-pos
	;; Non-nil when a construct has been found which delimits the search
	;; for a statement start, e.g. an opening brace or a macro start, or a
	;; keyword like `if' when the PDA stack is empty.
	pre-stmt-found
	;; The position of the last sexp or bound that follows the
	;; first found colon, i.e. the start of the nonlabel part of
	;; the statement.  It's `start' if a colon is found just after
	;; the start.
	after-labels-pos
	;; Like `after-labels-pos', but the first such position inside
	;; a label, i.e. the start of the last label before the start
	;; of the nonlabel part of the statement.
	last-label-pos
	;; The last position where a label is possible provided the
	;; statement started there.  It's nil as long as no invalid
	;; label content has been found (according to
	;; `c-nonlabel-token-key').  It's `start' if no valid label
	;; content was found in the label.  Note that we might still
	;; regard it a label if it starts with `c-label-kwds'.
	label-good-pos
	;; Putative positions of the components of a bitfield declaration,
	;; e.g. "int foo : NUM_FOO_BITS ;"
	bitfield-type-pos bitfield-id-pos bitfield-size-pos
	;; Symbol just scanned back over (e.g. 'while or 'boundary).
	;; See above.
	sym
	;; Current state in the automaton.  See above.
	state
	;; Current saved positions.  See above.
	saved-pos
	;; Stack of conses (state . saved-pos).
	stack
	;; Regexp which matches "for", "if", etc.
	(cond-key (or c-opt-block-stmt-key
		      regexp-unmatchable))
	;; Return value.
	(ret 'same)
	;; Positions of the last three sexps or bounds we've stopped at.
	tok ptok pptok)

    (save-restriction
      (setq lim (if lim
		    (max lim (point-min))
		  (point-min)))
      (widen)

      (if (save-excursion
	    (and (c-beginning-of-macro)
		 (/= (point) start)))
	  (setq macro-start (point)))

      ;; Try to skip back over unary operator characters, to register
      ;; that we've moved.
      (while (progn
	       (setq pos (point))
	       (c-backward-syntactic-ws lim)
	       ;; Protect post-++/-- operators just before a virtual semicolon.
	       (and (not (c-at-vsemi-p))
		    (/= (skip-chars-backward "-+!*&~@`#") 0))))

      ;; Skip back over any semicolon here.  If it was a bare semicolon, we're
      ;; done.  Later on we ignore the boundaries for statements that don't
      ;; contain any sexp.  The only thing that is affected is that the error
      ;; checking is a little less strict, and we really don't bother.
      (if (and (memq (char-before) delims)
	       (progn (forward-char -1)
		      (setq saved (point))
		      (c-backward-syntactic-ws lim)
		      (or (memq (char-before) delims)
			  (memq (char-before) '(?: nil))
			  (eq (char-syntax (char-before)) ?\()
			  (c-at-vsemi-p))))
	  (setq ret 'previous
		pos saved)

	;; Begin at start and not pos to detect macros if we stand
	;; directly after the #.
	(goto-char start)
	(if (looking-at "\\<\\|\\W")
	    ;; Record this as the first token if not starting inside it.
	    (setq tok start))

	;; The following while loop goes back one sexp (balanced parens,
	;; etc. with contents, or symbol or suchlike) each iteration.  This
	;; movement is accomplished with a call to c-backward-sexp approx 170
	;; lines below.
	;;
	;; The loop is exited only by throwing nil to the (catch 'loop ...):
	;; 1. On reaching the start of a macro;
	;; 2. On having passed a stmt boundary with the PDA stack empty;
	;; 3. Going backwards past the search limit.
	;; 4. On reaching the start of an Objective C method def;
	;; 5. From macro `c-bos-pop-state'; when the stack is empty;
	;; 6. From macro `c-bos-pop-state-and-retry' when the stack is empty.
	(while
	    (catch 'loop ;; Throw nil to break, non-nil to continue.
	      (cond
	       ;; Are we in a macro, just after the opening #?
	       ((save-excursion
		  (and macro-start	; Always NIL for AWK.
		       (progn (skip-chars-backward " \t")
			      (eq (char-before) ?#))
		       (progn (setq saved (1- (point)))
			      (beginning-of-line)
			      (not (eq (char-before (1- (point))) ?\\)))
		       (looking-at c-opt-cpp-start)
		       (progn (skip-chars-forward " \t")
			      (eq (point) saved))))
		(goto-char saved)
		(if (and (c-forward-to-cpp-define-body)
			 (progn (c-forward-syntactic-ws start)
				(< (point) start)))
		    ;; Stop at the first token in the content of the macro.
		    (setq pos (point)
			  ignore-labels t) ; Avoid the label check on exit.
		  (setq pos saved
			ret 'macro
			ignore-labels t))
		(setq pre-stmt-found t)
		(throw 'loop nil))	; 1. Start of macro.

	       ;; Do a round through the automaton if we've just passed a
	       ;; statement boundary or passed a "while"-like token.
	       ((or sym
		    (and (looking-at cond-key)
			 (setq sym (intern (match-string 1)))))

		(when (and (< pos start) (null stack))
		  (setq pre-stmt-found t)
		  (throw 'loop nil))	; 2. Statement boundary.

		;; The PDA state handling.
                ;;
                ;; Refer to the description of the PDA in the opening
                ;; comments.  In the following OR form, the first leaf
                ;; attempts to handles one of the specific actions detailed
                ;; (e.g., finding token "if" whilst in state `else-boundary').
                ;; We drop through to the second leaf (which handles common
                ;; state) if no specific handler is found in the first cond.
                ;; If a parsing error is detected (e.g. an "else" with no
                ;; preceding "if"), we throw to the enclosing catch.
                ;;
                ;; Note that the (eq state 'else) means
		;; "we've just passed an else", NOT "we're looking for an
		;; else".
		(or (cond
		     ((eq state 'else)
		      (if (eq sym 'boundary)
			  (setq state 'else-boundary)
			(c-bos-report-error)
			(c-bos-pop-state-and-retry)))

		     ((eq state 'else-boundary)
		      (cond ((eq sym 'if)
			     (c-bos-pop-state (setq ret 'beginning)))
			    ((eq sym 'boundary)
			     (c-bos-report-error)
			     (c-bos-pop-state))))

		     ((eq state 'while)
		      (if (and (eq sym 'boundary)
			       ;; Since this can cause backtracking we do a
			       ;; little more careful analysis to avoid it:
			       ;; If there's a label in front of the while
			       ;; it can't be part of a do-while.
			       (not after-labels-pos))
			  (progn (c-bos-save-pos)
				 (setq state 'while-boundary))
			(c-bos-pop-state-and-retry))) ; Can't be a do-while

		     ((eq state 'while-boundary)
		      (cond ((eq sym 'do)
			     (c-bos-pop-state (setq ret 'beginning)))
			    ((eq sym 'boundary) ; isn't a do-while
			     (c-bos-restore-pos) ; the position of the while
			     (c-bos-pop-state)))) ; no longer searching for do.

		     ((eq state 'catch)
		      (if (eq sym 'boundary)
			  (setq state 'catch-boundary)
			(c-bos-report-error)
			(c-bos-pop-state-and-retry)))

		     ((eq state 'catch-boundary)
		      (cond
		       ((eq sym 'try)
			(c-bos-pop-state (setq ret 'beginning)))
		       ((eq sym 'catch)
			(setq state 'catch))
		       ((eq sym 'boundary)
			(c-bos-report-error)
			(c-bos-pop-state)))))

		    ;; This is state common.  We get here when the previous
		    ;; cond statement found no particular state handler.
		    (cond ((eq sym 'boundary)
			   ;; If we have a boundary at the start
			   ;; position we push a frame to go to the
			   ;; previous statement.
			   (if (>= pos start)
			       (c-bos-push-state)
			     (c-bos-pop-state)))
			  ((eq sym 'else)
			   (c-bos-push-state)
			   (c-bos-save-error-info 'if 'else)
			   (setq state 'else))
			  ((eq sym 'while)
			   ;; Is this a real while, or a do-while?
			   ;; The next `when' triggers unless we are SURE that
			   ;; the `while' is not the tail end of a `do-while'.
			   (when (or (not pptok)
				     (memq (char-after pptok) delims)
				     ;; The following kludge is to prevent
				     ;; infinite recursion when called from
				     ;; c-awk-after-if-for-while-condition-p,
				     ;; or the like.
				     (and (eq (point) start)
					  (c-vsemi-status-unknown-p))
				     (c-at-vsemi-p pptok))
			     ;; Since this can cause backtracking we do a
			     ;; little more careful analysis to avoid it: If
			     ;; the while isn't followed by a (possibly
			     ;; virtual) semicolon it can't be a do-while.
			     (c-bos-push-state)
			     (setq state 'while)))
			  ((memq sym '(catch finally))
			   (c-bos-push-state)
			   (c-bos-save-error-info 'try sym)
			   (setq state 'catch))))

		(when c-maybe-labelp
		  ;; We're either past a statement boundary or at the
		  ;; start of a statement, so throw away any label data
		  ;; for the previous one.
		  (setq after-labels-pos nil
			last-label-pos nil
			c-maybe-labelp nil))))

	      ;; Step to the previous sexp, but not if we crossed a
	      ;; boundary, since that doesn't consume an sexp.
	      (if (eq sym 'boundary)
		  (when (>= (point) lim)
		    (setq ret 'previous))

                ;; HERE IS THE SINGLE PLACE INSIDE THE PDA LOOP WHERE WE MOVE
		;; BACKWARDS THROUGH THE SOURCE.

		(c-backward-syntactic-ws lim)
		(let ((before-sws-pos (point))
		      ;; The end position of the area to search for statement
		      ;; barriers in this round.
		      (maybe-after-boundary-pos pos)
		      comma-delimited)

		  ;; Go back over exactly one logical sexp, taking proper
		  ;; account of macros and escaped EOLs.
		  (while
		      (and
		       (progn
			 (setq comma-delimited (and (not comma-delim)
						    (eq (char-before) ?\,)))
			 (unless (c-safe (c-backward-sexp) t)
			   ;; Give up if we hit an unbalanced block.  Since the
			   ;; stack won't be empty the code below will report a
			   ;; suitable error.
			   (setq pre-stmt-found t)
			   (throw 'loop nil))
			 ;; Handle C++'s `constexpr', etc.
			 (if (save-excursion
			       (and (looking-at c-block-stmt-hangon-key)
				    (progn
				      (c-backward-syntactic-ws lim)
				      (c-safe (c-backward-sexp) t))
				    (looking-at c-block-stmt-2-key)
				    (setq pos (point))))
			     (goto-char pos))
			 (cond
			  ;; Have we moved into a macro?
			  ((and (not macro-start)
				(c-beginning-of-macro))
			   (save-excursion
			     (c-backward-syntactic-ws lim)
			     (setq before-sws-pos (point)))
			   ;; Have we crossed a statement boundary?  If not,
			   ;; keep going back until we find one or a "real" sexp.
			   (and
			    (save-excursion
			      (c-end-of-macro)
			      (not (c-crosses-statement-barrier-p
				    (point) maybe-after-boundary-pos)))
			    (setq maybe-after-boundary-pos (point))))
			  ;; Have we just gone back over an escaped NL?  This
			  ;; doesn't count as a sexp.
			  ((looking-at "\\\\$"))))
		       (>= (point) lim)))

		  ;; Have we crossed a statement boundary?
		  (setq boundary-pos
			(cond
			 ;; Are we at a macro beginning?
			 ((and (not macro-start)
			       c-opt-cpp-prefix
			       (looking-at c-opt-cpp-prefix))
			  (save-excursion
			    (c-end-of-macro)
			    (c-crosses-statement-barrier-p
			     (point) maybe-after-boundary-pos)))
			 ;; Just gone back over a brace block?
			 ((and
			   (eq (char-after) ?{)
			   (not comma-delimited)
			   (not (c-looking-at-inexpr-block lim nil t))
			   (save-excursion
			     (c-backward-token-2 1 t nil)
			     (not (looking-at "=\\([^=]\\|$\\)")))
			   (or
			    (not c-opt-block-decls-with-vars-key)
			    (save-excursion
			      (c-backward-token-2 1 t nil)
			      (if (and (looking-at c-symbol-start)
				       (not (looking-at c-keywords-regexp)))
				  (c-backward-token-2 1 t nil))
			      (and
			       (not (looking-at
				     c-opt-block-decls-with-vars-key))
			       (or comma-delim
				   (not (eq (char-after) ?\,))))))
			   ;; Is the {..} followed by an operator which
			   ;; prevents it being a statement in its own right?
			   (save-excursion
			     (and
			      (c-go-list-forward)
			      (progn
				(c-forward-syntactic-ws)
				(or
				 (not (looking-at c-non-after-{}-ops-re))
				 (let
				     ((bad-op-len
				       (- (match-end 0) (match-beginning 0))))
				   (and
				    (looking-at c-operator-re)
				    (> (- (match-end 0) (match-beginning 0))
				       bad-op-len))))))))
			  (save-excursion
			    (c-forward-sexp) (point)))
			 ;; Just gone back over some paren block?
			 ((looking-at "\\s(")
			  (save-excursion
			    (goto-char (1+ (c-down-list-backward
					    before-sws-pos)))
			    (c-crosses-statement-barrier-p
			     (point) maybe-after-boundary-pos)))
			 ;; Just gone back over an ordinary symbol of some sort?
			 (t (c-crosses-statement-barrier-p
			     (point) maybe-after-boundary-pos))))

		  (when boundary-pos
		    (setq pptok ptok
			  ptok tok
			  tok boundary-pos
			  sym 'boundary)
		    ;; Like a C "continue".  Analyze the next sexp.
		    (throw 'loop t))))

	      ;; Have we gone past the limit?
	      (when (< (point) lim)
		(throw 'loop nil))	; 3. Gone back over the limit.

	      ;; ObjC method def?
	      (when (and c-opt-method-key
			 (setq saved (c-in-method-def-p)))
		(setq pos saved
		      pre-stmt-found t
		      ignore-labels t)	; Avoid the label check on exit.
		(throw 'loop nil))	; 4. ObjC method def.

	      ;; Might we have a bitfield declaration, "<type> <id> : <size>"?
	      (if c-has-bitfields
		  (cond
		   ;; The : <size> and <id> fields?
		   ((and (numberp c-maybe-labelp)
			 (not bitfield-size-pos)
			 (save-excursion
			   (goto-char (or tok start))
			   (not (looking-at c-keywords-regexp)))
			 (not (looking-at c-keywords-regexp))
			 (not (c-punctuation-in (point) c-maybe-labelp)))
		    (setq bitfield-size-pos (or tok start)
			  bitfield-id-pos (point)))
		   ;; The <type> field?
		   ((and bitfield-id-pos
			 (not bitfield-type-pos))
		    (if (and (looking-at c-symbol-key) ; Can only be an integer type.  :-)
			     (not (looking-at c-not-primitive-type-keywords-regexp))
			     (not (c-punctuation-in (point) tok)))
			(setq bitfield-type-pos (point))
		      (setq bitfield-size-pos nil
			    bitfield-id-pos nil)))))

	      ;; Handle labels.
	      (unless (eq ignore-labels t)
		(when (numberp c-maybe-labelp)
		  ;; `c-crosses-statement-barrier-p' has found a colon, so we
		  ;; might be in a label now.  Have we got a real label
		  ;; (including a case label) or something like C++'s "public:"?
		  ;; A case label might use an expression rather than a token.
		  (setq after-case:-pos (or tok start))
		  (if (or (looking-at c-nonlabel-nonparen-token-key)
					; e.g. "while" or "'a'"
			  ;; Catch C++'s inheritance construct "class foo : bar".
			  (save-excursion
			    (and
			     (c-safe (c-backward-sexp) t)
			     (looking-at c-nonlabel-token-2-key)))
			  ;; Catch C++'s "case a(1):"
			  (and (c-major-mode-is 'c++-mode)
			       (eq (char-after) ?\()
			       (save-excursion
				 (not (and
				       (zerop (c-backward-token-2 2))
				       (looking-at c-case-kwds-regexp))))))
		      (setq c-maybe-labelp nil)
		    (if after-labels-pos ; Have we already encountered a label?
			(if (not last-label-pos)
			    (setq last-label-pos (or tok start)))
		      (setq after-labels-pos (or tok start)))
		    (setq c-maybe-labelp t
			  label-good-pos nil))) ; bogus "label"

		(when (and (not label-good-pos)	; i.e. no invalid "label"'s yet
						; been found.
			   (looking-at c-nonlabel-token-key)) ; e.g. "while :"
		  ;; We're in a potential label and it's the first
		  ;; time we've found something that isn't allowed in
		  ;; one.
		  (setq label-good-pos (or tok start))))

	      ;; We've moved back by a sexp, so update the token positions.
	      (setq sym nil
		    pptok ptok
		    ptok tok
		    tok (point)
		    pos tok) ; always non-nil
	      )		     ; end of (catch 'loop ....)
	  )		     ; end of sexp-at-a-time (while ....)

	(when (and hit-lim
		   (or (not pre-stmt-found)
		       (< pos lim)
		       (>= pos start)))
	  (setq ret nil))

	;; If the stack isn't empty there might be errors to report.
	(while stack
	  (if (and (vectorp saved-pos) (eq (length saved-pos) 3))
	      (c-bos-report-error))
	  (setq saved-pos (cdr (car stack))
		stack (cdr stack)))

	(when (and (eq ret 'same)
		   (not (memq sym '(boundary ignore nil))))
	  ;; Need to investigate closer whether we've crossed
	  ;; between a substatement and its containing statement.
	  (if (setq saved
		    (cond ((and (looking-at c-block-stmt-1-2-key)
				(eq (char-after ptok) ?\())
			   pptok)
			  ((looking-at c-block-stmt-1-key)
			   ptok)
			  (t pptok)))
	      (cond ((> start saved) (setq pos saved))
		    ((= start saved) (setq ret 'up)))))

	(when (and (not ignore-labels)
		   (eq c-maybe-labelp t)
		   (not (eq ret 'beginning))
		   after-labels-pos
		   (not bitfield-type-pos) ; Bitfields take precedence over labels.
		   (or (not label-good-pos)
		       (<= label-good-pos pos)
		       (progn
			 (goto-char (if (and last-label-pos
					     (< last-label-pos start))
					last-label-pos
				      pos))
			 (looking-at c-label-kwds-regexp))))
	  ;; We're in a label.  Maybe we should step to the statement
	  ;; after it.
	  (if (< after-labels-pos start)
	      (setq pos after-labels-pos)
	    (setq ret 'label)
	    (if (and last-label-pos (< last-label-pos start))
		;; Might have jumped over several labels.  Go to the last one.
		(setq pos last-label-pos)))))

      ;; Have we got "case <expression>:"?
      (goto-char pos)
      (when (and after-case:-pos
		 (not (eq ret 'beginning))
		 (looking-at c-case-kwds-regexp))
	(if (< after-case:-pos start)
	    (setq pos after-case:-pos))
	(if (eq ret 'same)
	    (setq ret 'label)))

      ;; Skip over the unary operators that can start the statement.
      (while (and (> (point) lim)
		  (progn
		    (c-backward-syntactic-ws lim)
		    ;; protect AWK post-inc/decrement operators, etc.
		    (and (not (c-at-vsemi-p (point)))
			 (/= (skip-chars-backward "-.+!*&~@`#") 0))))
	(setq pos (point)))

      (goto-char pos)
      ret)))