Merge remote-tracking branch 'origin/master' into feature/android
This commit is contained in:
commit
79207a055f
25 changed files with 569 additions and 1096 deletions
|
@ -969,9 +969,15 @@ guaranteed to be writable.
|
|||
@end defmac
|
||||
|
||||
@defun access-file filename string
|
||||
@vindex remote-file-name-access-timeout
|
||||
If you can read @var{filename} this function returns @code{nil};
|
||||
otherwise it signals an error
|
||||
using @var{string} as the error message text.
|
||||
|
||||
If the user option @code{remote-file-name-access-timeout} is a number,
|
||||
the function signals an error when it doesn't finish after that time
|
||||
(in seconds). This applies only to remote files, and only when there
|
||||
is no additional time spent while reading passwords or alike.
|
||||
@end defun
|
||||
|
||||
@defun file-ownership-preserved-p filename &optional group
|
||||
|
|
|
@ -580,7 +580,14 @@ on every invocation of @code{todo-insert-item}.
|
|||
the highest or lowest priority in the category, if you do not
|
||||
explicitly assign it a priority on invoking @code{todo-insert-item}.
|
||||
By default, such new items are given highest priority, i.e., inserted
|
||||
at the top of the list.
|
||||
at the top of the list. In addition, when setting an item's priority
|
||||
you can use the minibuffer history to quickly call up the lowest or
|
||||
highest priority number in the minibuffer by typing @kbd{M-p} or
|
||||
@kbd{M-n}, and you can scroll through all priority numbers for the
|
||||
current category with these keys. For example, with the default
|
||||
setting of @code{todo-default-priority}, you can insert a new item as
|
||||
second to last in the category by typing @kbd{M-p M-p} at the prompt
|
||||
for setting the priority.
|
||||
|
||||
@item
|
||||
@code{todo-always-add-time-string} is for including or omitting the
|
||||
|
@ -983,7 +990,10 @@ category, i.e., gives it third highest priority; all lower priority
|
|||
items are pushed down by one. You can also pass the desired priority
|
||||
as a numeric prefix argument, e.g., @kbd{3 #} gives the item third
|
||||
highest priority without prompting. (Prefix arguments have no effect
|
||||
with @kbd{r} or @kbd{l}.)
|
||||
with @kbd{r} or @kbd{l}.) And you can type @kbd{M-p} and @kbd{M-n} in
|
||||
the minibuffer to scroll through all priority numbers for the current
|
||||
category. If you mistakenly choose the item's current priority, you
|
||||
will be prompted to choose a different priority.
|
||||
@end table
|
||||
|
||||
@node Moving and Deleting Items
|
||||
|
|
|
@ -5153,6 +5153,35 @@ In order to disable those optimizations, set user option
|
|||
@code{tramp-local-host-regexp} to @code{nil}.
|
||||
|
||||
|
||||
@item
|
||||
@value{tramp} blocks Emacs at startup
|
||||
|
||||
@vindex remote-file-name-access-timeout
|
||||
Some packages, like @file{desktop.el} or @file{recentf.el}, access
|
||||
remote files when loaded. If the respective file is not accessible,
|
||||
@value{tramp} could block. In order to check whether this could
|
||||
happen, add a test via @code{access-file} with a proper timeout prior
|
||||
loading these packages:
|
||||
|
||||
@lisp
|
||||
@group
|
||||
(let ((remote-file-name-access-timeout 10))
|
||||
(access-file "@file{@trampfn{method,user@@host,/path/to/file}}" "error"))
|
||||
@result{} nil
|
||||
@end group
|
||||
@end lisp
|
||||
|
||||
The result @code{nil} means success. If the file is not accessible,
|
||||
or if the underlying operations last too long, @code{access-file}
|
||||
returns with an error.
|
||||
|
||||
The value of the timeout (10 seconds in the example) depends on your
|
||||
preference and on the quality of the connection to the remote host.
|
||||
If the connection to the remote host isn't established yet, and if
|
||||
this requires an interactive password, the timeout check doesn't work
|
||||
properly.
|
||||
|
||||
|
||||
@item
|
||||
Does @value{tramp} support @acronym{SSH} security keys?
|
||||
|
||||
|
|
|
@ -183,7 +183,7 @@ Luckily, ERC now leverages a feature introduced in Emacs 27, "hook
|
|||
depth," to secure the positions of a few key members of
|
||||
'erc-insert-modify-hook' and 'erc-send-modify-hook'. So far, this
|
||||
includes the functions 'erc-button-add-buttons', 'erc-fill',
|
||||
'erc-add-timestamp', and 'erc-match-message', which now appear in that
|
||||
'erc-match-message', and 'erc-add-timestamp', which now appear in that
|
||||
order, when present, at depths beginning at 20 and ending below 80.
|
||||
Of most interest to module authors is the new relative positioning of
|
||||
the first two, 'erc-button-add-buttons' and 'erc-fill', which have
|
||||
|
|
13
etc/NEWS
13
etc/NEWS
|
@ -78,6 +78,11 @@ trash when deleting. Default is nil.
|
|||
If this user option is non-nil, 'auto-save-mode' will not auto-save
|
||||
remote buffers. The default is nil.
|
||||
|
||||
+++
|
||||
** New user option 'remote-file-name-access-timeout'.
|
||||
When a natural number, this option limits the call of 'access-file'
|
||||
for remote files to this number of seconds. Default is nil.
|
||||
|
||||
+++
|
||||
** New user option 'yes-or-no-prompt'.
|
||||
This allows the user to customize the prompt that is appended by
|
||||
|
@ -119,6 +124,11 @@ mistaken compositions, this will now work as well.
|
|||
This works like 'kill-matching-buffers', but without asking for
|
||||
confirmation.
|
||||
|
||||
---
|
||||
** New user option 'duplicate-region-final-position'.
|
||||
It controls the placement of point and the region after duplicating a
|
||||
region with 'duplicate-dwim'.
|
||||
|
||||
|
||||
* Changes in Specialized Modes and Packages in Emacs 30.1
|
||||
|
||||
|
@ -457,6 +467,9 @@ searching.
|
|||
CPerl mode fontifies subroutine signatures like variable declarations
|
||||
which makes them visually distinct from subroutine prototypes.
|
||||
|
||||
*** Syntax of Perl up to version 5.38 is supported.
|
||||
CPerl mode supports the new keywords for exception handling and the
|
||||
object oriented syntax which were added in Perl 5.36 and 5.38.
|
||||
|
||||
|
||||
* New Modes and Packages in Emacs 30.1
|
||||
|
|
|
@ -2646,16 +2646,26 @@ meaning to raise or lower the item's priority by one."
|
|||
(save-excursion
|
||||
(re-search-forward regexp1 nil t)
|
||||
(match-string-no-properties 1)))))))
|
||||
curnum
|
||||
(count 1)
|
||||
(curnum (save-excursion
|
||||
(let ((curstart
|
||||
;; If point is in done items section or not on an
|
||||
;; item, use position of first todo item to avoid
|
||||
;; the while-loop.
|
||||
(or (and (not (todo-done-item-section-p))
|
||||
(todo-item-start))
|
||||
(point-min))))
|
||||
(goto-char (point-min))
|
||||
(while (/= (point) curstart)
|
||||
(setq count (1+ count))
|
||||
(todo-forward-item))
|
||||
count)))
|
||||
(todo (cond ((or (memq arg '(raise lower))
|
||||
(eq major-mode 'todo-filtered-items-mode))
|
||||
(save-excursion
|
||||
(let ((curstart (todo-item-start))
|
||||
(count 0))
|
||||
(goto-char (point-min))
|
||||
(let ((count curnum))
|
||||
(while (looking-at todo-item-start)
|
||||
(setq count (1+ count))
|
||||
(when (= (point) curstart) (setq curnum count))
|
||||
(todo-forward-item))
|
||||
count)))
|
||||
((eq major-mode 'todo-mode)
|
||||
|
@ -2667,11 +2677,16 @@ meaning to raise or lower the item's priority by one."
|
|||
((and (eq arg 'raise) (>= curnum 1))
|
||||
(1- curnum))
|
||||
((and (eq arg 'lower) (<= curnum maxnum))
|
||||
(1+ curnum))))
|
||||
candidate)
|
||||
(1+ curnum)))))
|
||||
(and (called-interactively-p 'any)
|
||||
priority ; Check further only if arg or prefix arg was passed.
|
||||
(or (< priority 1) (> priority maxnum))
|
||||
(user-error (format "Priority must be an integer between 1 and %d"
|
||||
maxnum)))
|
||||
(unless (and priority
|
||||
(/= priority curnum)
|
||||
(or (and (eq arg 'raise) (zerop priority))
|
||||
(and (eq arg 'lower) (> priority maxnum))))
|
||||
(and (eq arg 'lower) (>= priority maxnum))))
|
||||
;; When moving item to another category, show the category before
|
||||
;; prompting for its priority.
|
||||
(unless (or arg (called-interactively-p 'any))
|
||||
|
@ -2687,16 +2702,34 @@ meaning to raise or lower the item's priority by one."
|
|||
;; while setting priority.
|
||||
(save-excursion (todo-category-select)))))
|
||||
;; Prompt for priority only when the category has at least one
|
||||
;; todo item.
|
||||
(when (> maxnum 1)
|
||||
(while (not priority)
|
||||
(setq candidate (read-number prompt
|
||||
(if (eq todo-default-priority 'first)
|
||||
1 maxnum)))
|
||||
(setq prompt (when (or (< candidate 1) (> candidate maxnum))
|
||||
(format "Priority must be an integer between 1 and %d.\n"
|
||||
maxnum)))
|
||||
(unless prompt (setq priority candidate))))
|
||||
;; todo item or when passing the current priority as prefix arg.
|
||||
(when (and (or (not priority) (= priority curnum))
|
||||
(> maxnum 1))
|
||||
(let* ((read-number-history (mapcar #'number-to-string
|
||||
(if (eq todo-default-priority
|
||||
'first)
|
||||
(number-sequence maxnum 1 -1)
|
||||
(number-sequence 1 maxnum))))
|
||||
(history-add-new-input nil)
|
||||
(candidate (or priority
|
||||
(read-number prompt
|
||||
(if (eq todo-default-priority
|
||||
'first)
|
||||
1 maxnum))))
|
||||
(success nil))
|
||||
(while (not success)
|
||||
(setq prompt
|
||||
(cond
|
||||
((and (= candidate curnum)
|
||||
;; Allow same priority in a different category
|
||||
;; (only possible when called non-interactively).
|
||||
(called-interactively-p 'any))
|
||||
"New priority must be different from current priority: ")
|
||||
(t (when (or (< candidate 1) (> candidate maxnum))
|
||||
(format "Priority must be an integer between 1 and %d: "
|
||||
maxnum)))))
|
||||
(when prompt (setq candidate (read-number prompt)))
|
||||
(unless prompt (setq priority candidate success t)))))
|
||||
;; In Top Priorities buffer, an item's priority can be changed
|
||||
;; wrt items in another category, but not wrt items in the same
|
||||
;; category.
|
||||
|
|
|
@ -52,7 +52,7 @@ they are hidden or highlighted. This is controlled via the variables
|
|||
`erc-current-nick-highlight-type'. For all these highlighting types,
|
||||
you can decide whether the entire message or only the sending nick is
|
||||
highlighted."
|
||||
((add-hook 'erc-insert-modify-hook #'erc-match-message 60)
|
||||
((add-hook 'erc-insert-modify-hook #'erc-match-message 50)
|
||||
(add-hook 'erc-mode-hook #'erc-match--modify-invisibility-spec)
|
||||
(unless erc--updating-modules-p
|
||||
(erc-buffer-do #'erc-match--modify-invisibility-spec))
|
||||
|
@ -237,10 +237,7 @@ for beeping to work."
|
|||
ERC calls members with the arguments (MATCH-TYPE NUH MESSAGE),
|
||||
where MATCH-TYPE is one of the symbols `current-nick', `keyword',
|
||||
`pal', `dangerous-host', `fool', and NUH is an `erc-response'
|
||||
sender, like bob!~bob@example.org. Users should keep in mind
|
||||
that MESSAGE may not include decorations, such as white space or
|
||||
time stamps, preceding the same text as inserted in the narrowed
|
||||
buffer."
|
||||
sender, like bob!~bob@example.org."
|
||||
:options '(erc-log-matches erc-hide-fools erc-beep-on-match)
|
||||
:type 'hook)
|
||||
|
||||
|
@ -462,19 +459,8 @@ In any of the following situations, MSG is directed at an entry FOOL:
|
|||
(erc-list-match fools-end msg))))
|
||||
|
||||
(defun erc-match-message ()
|
||||
"Add faces to matching text in inserted message."
|
||||
;; Exclude leading whitespace, stamps, etc.
|
||||
(let ((omin (point-min))
|
||||
(beg (or (and (not (get-text-property (point-min) 'erc-command))
|
||||
(next-single-property-change (point-min) 'erc-command))
|
||||
(point-min))))
|
||||
;; FIXME when ERC no longer supports 28, use `with-restriction'
|
||||
;; with `:label' here instead of passing `omin'.
|
||||
(save-restriction
|
||||
(narrow-to-region beg (point-max))
|
||||
(erc-match--message omin))))
|
||||
|
||||
(defun erc-match--message (unrestricted-point-min)
|
||||
"Mark certain keywords in a region.
|
||||
Use this defun with `erc-insert-modify-hook'."
|
||||
;; This needs some refactoring.
|
||||
(goto-char (point-min))
|
||||
(let* ((to-match-nick-dep '("pal" "fool" "dangerous-host"))
|
||||
|
@ -576,14 +562,12 @@ In any of the following situations, MSG is directed at an entry FOOL:
|
|||
'font-lock-face match-face)))
|
||||
;; Else twiddle your thumbs.
|
||||
(t nil))
|
||||
;; FIXME use `without-restriction' after dropping 28.
|
||||
(save-restriction
|
||||
(narrow-to-region unrestricted-point-min (point-max))
|
||||
(run-hook-with-args
|
||||
'erc-text-matched-hook (intern match-type)
|
||||
(or nickuserhost
|
||||
(concat "Server:" (erc-get-parsed-vector-type vector)))
|
||||
message)))))
|
||||
(run-hook-with-args
|
||||
'erc-text-matched-hook
|
||||
(intern match-type)
|
||||
(or nickuserhost
|
||||
(concat "Server:" (erc-get-parsed-vector-type vector)))
|
||||
message))))
|
||||
(if nickuserhost
|
||||
(append to-match-nick-dep to-match-nick-indep)
|
||||
to-match-nick-indep)))))
|
||||
|
|
|
@ -163,8 +163,8 @@ from entering them and instead jump over them."
|
|||
(define-erc-module stamp timestamp
|
||||
"This mode timestamps messages in the channel buffers."
|
||||
((add-hook 'erc-mode-hook #'erc-munge-invisibility-spec)
|
||||
(add-hook 'erc-insert-modify-hook #'erc-add-timestamp 50)
|
||||
(add-hook 'erc-send-modify-hook #'erc-add-timestamp 50)
|
||||
(add-hook 'erc-insert-modify-hook #'erc-add-timestamp 60)
|
||||
(add-hook 'erc-send-modify-hook #'erc-add-timestamp 60)
|
||||
(add-hook 'erc-mode-hook #'erc-stamp--recover-on-reconnect)
|
||||
(add-hook 'erc--pre-clear-functions #'erc-stamp--reset-on-clear)
|
||||
(unless erc--updating-modules-p
|
||||
|
|
|
@ -482,6 +482,7 @@ non-nil."
|
|||
"When nil, `auto-save-visited-mode' will auto-save remote files.
|
||||
Any other value means that it will not."
|
||||
:group 'auto-save
|
||||
:group 'tramp
|
||||
:type 'boolean
|
||||
:version "29.1")
|
||||
|
||||
|
@ -557,6 +558,7 @@ using a transform that puts the lock files on a local file system."
|
|||
(defcustom remote-file-name-inhibit-locks nil
|
||||
"Whether to create file locks for remote files."
|
||||
:group 'files
|
||||
:group 'tramp
|
||||
:version "28.1"
|
||||
:type 'boolean)
|
||||
|
||||
|
@ -1317,6 +1319,7 @@ consecutive checks. For example:
|
|||
(< 0 (file-attribute-size
|
||||
(file-attributes (file-chase-links file)))))))"
|
||||
:group 'files
|
||||
:group 'tramp
|
||||
:version "24.1"
|
||||
:type '(choice
|
||||
(const :tag "Do not inhibit file name cache" nil)
|
||||
|
@ -1325,6 +1328,22 @@ consecutive checks. For example:
|
|||
:format "Do not use file name cache older then %v seconds"
|
||||
:value 10)))
|
||||
|
||||
(defcustom remote-file-name-access-timeout nil
|
||||
"Timeout (in seconds) for `access-file'.
|
||||
This timeout limits the time to check, whether a remote file is
|
||||
accessible. `access-file' returns an error after that time. If
|
||||
the value is nil, no timeout is used.
|
||||
|
||||
This applies only when there isn't time spent for other actions,
|
||||
like reading passwords."
|
||||
:group 'files
|
||||
:group 'tramp
|
||||
:version "30.1"
|
||||
;;:type '(choice :tag "Timeout (seconds)" natnum (const nil)))
|
||||
:type '(choice
|
||||
(natnum :tag "Timeout (seconds)")
|
||||
(const :tag "Do not use timeout" nil)))
|
||||
|
||||
(defun file-local-name (file)
|
||||
"Return the local name component of FILE.
|
||||
This function removes from FILE the specification of the remote host
|
||||
|
@ -6391,6 +6410,8 @@ RECURSIVE if DIRECTORY is nonempty."
|
|||
"Whether remote files shall be moved to the Trash.
|
||||
This overrules any setting of `delete-by-moving-to-trash'."
|
||||
:version "30.1"
|
||||
:group 'files
|
||||
:group 'tramp
|
||||
:type 'boolean)
|
||||
|
||||
(defun file-equal-p (file1 file2)
|
||||
|
|
|
@ -114,7 +114,7 @@ When -1, put the region around the last copy."
|
|||
(const :tag "Put region around first copy" 1)
|
||||
(const :tag "Put region around last copy" -1))
|
||||
:group 'editing
|
||||
:version "29.1")
|
||||
:version "30.1")
|
||||
|
||||
(declare-function rectangle--duplicate-right "rect" (n displacement))
|
||||
|
||||
|
|
|
@ -2437,6 +2437,33 @@ without a visible progress reporter."
|
|||
(if tm (cancel-timer tm))
|
||||
(tramp-message ,vec ,level "%s...%s" ,message cookie)))))
|
||||
|
||||
(defmacro with-tramp-timeout (list &rest body)
|
||||
"Like `with-timeout', but allow SECONDS to be nil.
|
||||
|
||||
(fn (SECONDS TIMEOUT-FORMS...) BODY)"
|
||||
(declare (indent 1) (debug ((form body) body)))
|
||||
(let ((seconds (car list))
|
||||
(timeout-forms (cdr list)))
|
||||
`(if-let (((natnump ,seconds)))
|
||||
(with-timeout (,seconds ,@timeout-forms) ,@body)
|
||||
,@body)))
|
||||
|
||||
(defvar tramp-dont-suspend-timers nil
|
||||
"Don't suspend timers when checking reentrant calls.
|
||||
This shouldn't be changed globally, but let-bind where needed.")
|
||||
|
||||
(defmacro with-tramp-suspended-timers (&rest body)
|
||||
"Run BODY with suspended timers.
|
||||
Obey `tramp-dont-suspend-timers'."
|
||||
(declare (indent 0) (debug ((form body) body)))
|
||||
`(if tramp-dont-suspend-timers
|
||||
(progn ,@body)
|
||||
(let ((stimers (with-timeout-suspend))
|
||||
timer-list timer-idle-list)
|
||||
(unwind-protect
|
||||
(progn ,@body)
|
||||
(with-timeout-unsuspend stimers)))))
|
||||
|
||||
(defun tramp-drop-volume-letter (name)
|
||||
"Cut off unnecessary drive letter from file NAME.
|
||||
The functions `tramp-*-handle-expand-file-name' call `expand-file-name'
|
||||
|
@ -3962,19 +3989,30 @@ Let-bind it when necessary.")
|
|||
|
||||
(defun tramp-handle-access-file (filename string)
|
||||
"Like `access-file' for Tramp files."
|
||||
(setq filename (file-truename filename))
|
||||
(with-parsed-tramp-file-name filename v
|
||||
(if (file-exists-p filename)
|
||||
(unless
|
||||
(funcall
|
||||
(if (file-directory-p filename)
|
||||
#'file-accessible-directory-p #'file-readable-p)
|
||||
filename)
|
||||
(tramp-compat-permission-denied
|
||||
v (format "%s: Permission denied, %s" string filename)))
|
||||
(tramp-error
|
||||
v 'file-missing
|
||||
(format "%s: No such file or directory, %s" string filename)))))
|
||||
(let ((timeout
|
||||
(with-connection-local-variables
|
||||
;; This variable exists since Emacs 30.1.
|
||||
(bound-and-true-p remote-file-name-access-timeout)))
|
||||
;; We rely on timers, so don't suspend them.
|
||||
(tramp-dont-suspend-timers t))
|
||||
(with-parsed-tramp-file-name filename v
|
||||
(with-tramp-timeout
|
||||
(timeout
|
||||
(tramp-error
|
||||
v 'file-error
|
||||
(format "%s: Timeout %s second(s) accessing %s" string timeout filename)))
|
||||
(setq filename (file-truename filename))
|
||||
(if (file-exists-p filename)
|
||||
(unless
|
||||
(funcall
|
||||
(if (file-directory-p filename)
|
||||
#'file-accessible-directory-p #'file-readable-p)
|
||||
filename)
|
||||
(tramp-compat-permission-denied
|
||||
v (format "%s: Permission denied, %s" string filename)))
|
||||
(tramp-error
|
||||
v 'file-missing
|
||||
(format "%s: No such file or directory, %s" string filename)))))))
|
||||
|
||||
(defun tramp-handle-add-name-to-file
|
||||
(filename newname &optional ok-if-already-exists)
|
||||
|
@ -5684,26 +5722,24 @@ The terminal type can be configured with `tramp-terminal-type'."
|
|||
"Show the user a message for confirmation.
|
||||
Wait, until the connection buffer changes."
|
||||
(with-current-buffer (process-buffer proc)
|
||||
(let ((stimers (with-timeout-suspend))
|
||||
(cursor-in-echo-area t)
|
||||
set-message-function clear-message-function)
|
||||
;; Silence byte compiler.
|
||||
(ignore set-message-function clear-message-function)
|
||||
(tramp-message vec 6 "\n%s" (buffer-string))
|
||||
(tramp-check-for-regexp proc tramp-process-action-regexp)
|
||||
(with-temp-message
|
||||
(replace-regexp-in-string (rx (any "\r\n")) "" (match-string 0))
|
||||
;; Hide message in buffer.
|
||||
(narrow-to-region (point-max) (point-max))
|
||||
;; Wait for new output.
|
||||
(while (not (ignore-error file-error
|
||||
(tramp-wait-for-regexp
|
||||
proc 0.1 tramp-security-key-confirmed-regexp)))
|
||||
(when (tramp-check-for-regexp proc tramp-security-key-timeout-regexp)
|
||||
(throw 'tramp-action 'timeout))
|
||||
(redisplay 'force)))
|
||||
;; Reenable the timers.
|
||||
(with-timeout-unsuspend stimers)))
|
||||
(let ((cursor-in-echo-area t)
|
||||
set-message-function clear-message-function tramp-dont-suspend-timers)
|
||||
(with-tramp-suspended-timers
|
||||
;; Silence byte compiler.
|
||||
(ignore set-message-function clear-message-function)
|
||||
(tramp-message vec 6 "\n%s" (buffer-string))
|
||||
(tramp-check-for-regexp proc tramp-process-action-regexp)
|
||||
(with-temp-message
|
||||
(replace-regexp-in-string (rx (any "\r\n")) "" (match-string 0))
|
||||
;; Hide message in buffer.
|
||||
(narrow-to-region (point-max) (point-max))
|
||||
;; Wait for new output.
|
||||
(while (not (ignore-error file-error
|
||||
(tramp-wait-for-regexp
|
||||
proc 0.1 tramp-security-key-confirmed-regexp)))
|
||||
(when (tramp-check-for-regexp proc tramp-security-key-timeout-regexp)
|
||||
(throw 'tramp-action 'timeout))
|
||||
(redisplay 'force))))))
|
||||
t)
|
||||
|
||||
(defun tramp-action-process-alive (proc _vec)
|
||||
|
@ -5802,12 +5838,7 @@ performed successfully. Any other value means an error."
|
|||
proc 3 "Waiting for prompts from remote shell"
|
||||
(let ((enable-recursive-minibuffers t)
|
||||
exit)
|
||||
(if timeout
|
||||
(with-timeout (timeout (setq exit 'timeout))
|
||||
(while (not exit)
|
||||
(setq exit
|
||||
(catch 'tramp-action
|
||||
(tramp-process-one-action proc vec actions)))))
|
||||
(with-tramp-timeout (timeout (setq exit 'timeout))
|
||||
(while (not exit)
|
||||
(setq exit (catch 'tramp-action
|
||||
(tramp-process-one-action proc vec actions)))))
|
||||
|
@ -5863,14 +5894,12 @@ Mostly useful to protect BODY from being interrupted by timers."
|
|||
(throw 'non-essential 'non-essential)
|
||||
(tramp-error
|
||||
,proc 'remote-file-error "Forbidden reentrant call of Tramp"))
|
||||
(let ((stimers (with-timeout-suspend))
|
||||
timer-list timer-idle-list)
|
||||
(with-tramp-suspended-timers
|
||||
(unwind-protect
|
||||
(progn
|
||||
(tramp-set-connection-property ,proc "locked" t)
|
||||
,@body)
|
||||
(tramp-flush-connection-property ,proc "locked")
|
||||
(with-timeout-unsuspend stimers)))))
|
||||
(tramp-flush-connection-property ,proc "locked")))))
|
||||
|
||||
(defun tramp-accept-process-output (proc &optional _timeout)
|
||||
"Like `accept-process-output' for Tramp processes.
|
||||
|
@ -5963,21 +5992,13 @@ Expects the output of PROC to be sent to the current buffer. Returns
|
|||
the string that matched, or nil. Waits indefinitely if TIMEOUT is
|
||||
nil."
|
||||
(let ((found (tramp-check-for-regexp proc regexp)))
|
||||
(cond (timeout
|
||||
(with-timeout (timeout)
|
||||
(while (not found)
|
||||
(tramp-accept-process-output proc)
|
||||
(unless (process-live-p proc)
|
||||
(tramp-error-with-buffer
|
||||
nil proc 'file-error "Process has died"))
|
||||
(setq found (tramp-check-for-regexp proc regexp)))))
|
||||
(t
|
||||
(while (not found)
|
||||
(tramp-accept-process-output proc)
|
||||
(unless (process-live-p proc)
|
||||
(tramp-error-with-buffer
|
||||
nil proc 'file-error "Process has died"))
|
||||
(setq found (tramp-check-for-regexp proc regexp)))))
|
||||
(with-tramp-timeout (timeout)
|
||||
(while (not found)
|
||||
(tramp-accept-process-output proc)
|
||||
(unless (process-live-p proc)
|
||||
(tramp-error-with-buffer
|
||||
nil proc 'file-error "Process has died"))
|
||||
(setq found (tramp-check-for-regexp proc regexp))))
|
||||
;; The process could have timed out, for example due to session
|
||||
;; timeout of sudo. The process buffer does not exist any longer then.
|
||||
(ignore-errors
|
||||
|
@ -6759,9 +6780,7 @@ Consults the auth-source package."
|
|||
(auth-source-creation-prompts `((secret . ,pw-prompt)))
|
||||
;; Use connection-local value.
|
||||
(auth-sources (buffer-local-value 'auth-sources (process-buffer proc)))
|
||||
;; We suspend the timers while reading the password.
|
||||
(stimers (with-timeout-suspend))
|
||||
auth-info auth-passwd)
|
||||
auth-info auth-passwd tramp-dont-suspend-timers)
|
||||
|
||||
(unwind-protect
|
||||
;; We cannot use `with-parsed-tramp-file-name', because it
|
||||
|
@ -6786,7 +6805,7 @@ Consults the auth-source package."
|
|||
(tramp-compat-auth-info-password auth-info))))
|
||||
|
||||
;; Try the password cache.
|
||||
(progn
|
||||
(with-tramp-suspended-timers
|
||||
(setq auth-passwd (password-read pw-prompt key)
|
||||
tramp-password-save-function
|
||||
(lambda () (password-cache-add key auth-passwd)))
|
||||
|
@ -6796,25 +6815,20 @@ Consults the auth-source package."
|
|||
;; passwords. See discussion in Bug#50399.
|
||||
(when (tramp-string-empty-or-nil-p auth-passwd)
|
||||
(setq tramp-password-save-function nil))
|
||||
(tramp-set-connection-property vec "first-password-request" nil)
|
||||
|
||||
;; Reenable the timers.
|
||||
(with-timeout-unsuspend stimers))))
|
||||
(tramp-set-connection-property vec "first-password-request" nil))))
|
||||
|
||||
(put #'tramp-read-passwd 'tramp-suppress-trace t)
|
||||
|
||||
(defun tramp-read-passwd-without-cache (proc &optional prompt)
|
||||
"Read a password from user (compat function)."
|
||||
;; We suspend the timers while reading the password.
|
||||
(let ((stimers (with-timeout-suspend)))
|
||||
(unwind-protect
|
||||
(password-read
|
||||
(or prompt
|
||||
(with-current-buffer (process-buffer proc)
|
||||
(tramp-check-for-regexp proc tramp-password-prompt-regexp)
|
||||
(match-string 0))))
|
||||
;; Reenable the timers.
|
||||
(with-timeout-unsuspend stimers))))
|
||||
(let (tramp-dont-suspend-timers)
|
||||
(with-tramp-suspended-timers
|
||||
(password-read
|
||||
(or prompt
|
||||
(with-current-buffer (process-buffer proc)
|
||||
(tramp-check-for-regexp proc tramp-password-prompt-regexp)
|
||||
(match-string 0)))))))
|
||||
|
||||
(put #'tramp-read-passwd-without-cache 'tramp-suppress-trace t)
|
||||
|
||||
|
|
|
@ -705,7 +705,7 @@ voice);
|
|||
d) Has support for imenu, including:
|
||||
1) Separate unordered list of \"interesting places\";
|
||||
2) Separate TOC of POD sections;
|
||||
3) Separate list of packages;
|
||||
3) Separate list of packages/classes;
|
||||
4) Hierarchical view of methods in (sub)packages;
|
||||
5) and functions (by the full name - with package);
|
||||
e) Has an interface to INFO docs for Perl; The interface is
|
||||
|
@ -1311,7 +1311,7 @@ or \"${ foo }\" will not.")
|
|||
")")))
|
||||
"A regular expression for a single attribute, without leading colon.
|
||||
It may have parameters in parens, but parens within the
|
||||
parameter's value are not supported.. This regexp does not have
|
||||
parameter's value are not supported. This regexp does not have
|
||||
capture groups.")
|
||||
|
||||
(defconst cperl--attribute-list-rx
|
||||
|
@ -1368,14 +1368,14 @@ not be covered by regular expressions. This sequence captures
|
|||
enough to distinguish a signature from a prototype.")
|
||||
|
||||
(defconst cperl--package-rx
|
||||
`(sequence (group "package")
|
||||
`(sequence (group (or "package" "class"))
|
||||
,cperl--ws+-rx
|
||||
(group ,cperl--normal-identifier-rx)
|
||||
(optional (sequence ,cperl--ws+-rx
|
||||
(group (regexp ,cperl--version-regexp)))))
|
||||
"A regular expression for package NAME VERSION in Perl.
|
||||
Contains three groups for the keyword \"package\", for the
|
||||
package name and for the version.")
|
||||
"A regular expression for package|class NAME VERSION in Perl.
|
||||
Contains three groups for the initial keyword \"package\" or
|
||||
\"class\", for the package name and for the version.")
|
||||
|
||||
(defconst cperl--package-for-imenu-rx
|
||||
`(sequence symbol-start
|
||||
|
@ -1392,27 +1392,59 @@ NAME BLOCK\" and \"package NAME VERSION BLOCK.\" Contains three
|
|||
groups: One for the keyword \"package\", one for the package
|
||||
name, and one for the discovery of a following BLOCK.")
|
||||
|
||||
;; This gets a regexp of its own because classes allow attributes
|
||||
;; (e.g. ":isa(Parent)") while packages don't. We skip over it, but
|
||||
;; like for "package" we capture the following ";" or "{".
|
||||
(defconst cperl--class-for-imenu-rx
|
||||
`(sequence symbol-start
|
||||
(group-n 1 "class")
|
||||
,cperl--ws*-rx
|
||||
(group-n 2 ,cperl--normal-identifier-rx)
|
||||
(optional (sequence ,cperl--ws+-rx
|
||||
(regexp ,cperl--version-regexp)))
|
||||
(optional (sequence ,cperl--ws*-rx
|
||||
,cperl--attribute-list-rx))
|
||||
,cperl--ws*-rx
|
||||
(group-n 3 (or ";" "{")))
|
||||
"A regular expression to collect package names for `imenu'.
|
||||
Catches \"class NAME;\", \"class NAME VERSION;\", \"class NAME
|
||||
BLOCK\" and \"class NAME VERSION BLOCK\" and allows for
|
||||
attributes like \":isa(Parent)\". Contains three groups: One for
|
||||
the keyword \"package\", one for the package name, and one for
|
||||
the discovery of a following BLOCK.")
|
||||
|
||||
(defconst cperl--sub-name-for-imenu-rx
|
||||
`(sequence symbol-start
|
||||
(optional (sequence (group-n 3 (or "my" "state" "our"))
|
||||
,cperl--ws+-rx))
|
||||
(group-n 1 "sub")
|
||||
(group-n 1 (or "method" "sub"))
|
||||
,cperl--ws+-rx
|
||||
(group-n 2 ,cperl--normal-identifier-rx))
|
||||
"A regular expression to detect a subroutine start.
|
||||
Contains three groups: One to distinguish lexical from
|
||||
\"normal\" subroutines, for the keyword \"sub\", and one for the
|
||||
subroutine name.")
|
||||
"A regular expression to detect a subroutine or method start.
|
||||
Contains three groups: One to distinguish lexical from \"normal\"
|
||||
subroutines, for the keyword \"sub\" or \"method\", and one for
|
||||
the subroutine name.")
|
||||
|
||||
(defconst cperl--block-declaration-rx
|
||||
`(sequence
|
||||
(or "package" "sub") ; "class" and "method" coming soon
|
||||
(or "class" "method" "package" "sub")
|
||||
(1+ ,cperl--ws-or-comment-rx)
|
||||
,cperl--normal-identifier-rx)
|
||||
"A regular expression to find a declaration for a named block.
|
||||
Used for indentation. These declarations introduce a block which
|
||||
does not need a semicolon to terminate the statement.")
|
||||
|
||||
;;; Initializer blocks are not (yet) part of the Perl core.
|
||||
;; (defconst cperl--field-declaration-rx
|
||||
;; `(sequence
|
||||
;; "field"
|
||||
;; (1+ ,cperl--ws-or-comment-rx)
|
||||
;; ,cperl--basic-variable-rx)
|
||||
;; "A regular expression to find a declaration for a field.
|
||||
;; Used for indentation. These declarations allow an initializer
|
||||
;; block which does not need a semicolon to terminate the
|
||||
;; statement.")
|
||||
|
||||
(defconst cperl--pod-heading-rx
|
||||
`(sequence line-start
|
||||
(group-n 1 "=head")
|
||||
|
@ -1425,10 +1457,11 @@ heading text.")
|
|||
|
||||
(defconst cperl--imenu-entries-rx
|
||||
`(or ,cperl--package-for-imenu-rx
|
||||
,cperl--class-for-imenu-rx
|
||||
,cperl--sub-name-for-imenu-rx
|
||||
,cperl--pod-heading-rx)
|
||||
"A regular expression to collect stuff that goes into the `imenu' index.
|
||||
Covers packages, subroutines, and POD headings.")
|
||||
Covers packages and classes, subroutines and methods, and POD headings.")
|
||||
|
||||
;; end of eval-and-compiled stuff
|
||||
)
|
||||
|
@ -1534,7 +1567,7 @@ the last)."
|
|||
;; Tired of editing this in 8 places every time I remember that there
|
||||
;; is another method-defining keyword
|
||||
(defvar cperl-sub-keywords
|
||||
'("sub"))
|
||||
'("sub" "method"))
|
||||
|
||||
(defvar cperl-sub-regexp (regexp-opt cperl-sub-keywords))
|
||||
|
||||
|
@ -1832,7 +1865,8 @@ or as help on variables `cperl-tips', `cperl-problems',
|
|||
(rx (eval cperl--ws*-rx))
|
||||
(rx (optional (eval cperl--signature-rx)))
|
||||
"\\|" ; per toke.c
|
||||
"\\(BEGIN\\|UNITCHECK\\|CHECK\\|INIT\\|END\\|AUTOLOAD\\|DESTROY\\)"
|
||||
(rx (or "ADJUST" "AUTOLOAD" "BEGIN" "CHECK" "DESTROY"
|
||||
"END" "INIT" "UNITCHECK"))
|
||||
"\\)"
|
||||
cperl-maybe-white-and-comment-rex))
|
||||
(setq-local comment-indent-function #'cperl-comment-indent)
|
||||
|
@ -4853,7 +4887,7 @@ recursive calls in starting lines of here-documents."
|
|||
(setq tmpend tb))))
|
||||
((match-beginning 14) ; sub with prototype or attribute
|
||||
;; 1+6+2+1+1=11 extra () before this (sub with proto/attr):
|
||||
;; match-string 12: Keyword "sub"
|
||||
;; match-string 12: Keyword "sub" or "method"
|
||||
;; match-string 13: Name of the subroutine (optional)
|
||||
;; match-string 14: Indicator for proto/attr/signature
|
||||
;; match-string 15: Prototype
|
||||
|
@ -4862,7 +4896,7 @@ recursive calls in starting lines of here-documents."
|
|||
(setq b1 (match-beginning 13) e1 (match-end 13))
|
||||
(if (memq (char-after (1- b))
|
||||
'(?\$ ?\@ ?\% ?\& ?\*))
|
||||
nil ;; we found $sub or @sub etc
|
||||
nil ;; we found $sub or @method etc
|
||||
(goto-char b)
|
||||
(if (match-beginning 15) ; a complete prototype
|
||||
(progn
|
||||
|
@ -5006,7 +5040,11 @@ statement would start; thus the block in ${func()} does not count."
|
|||
(save-excursion
|
||||
(forward-sexp -1)
|
||||
;; else {} but not else::func {}
|
||||
(or (and (looking-at "\\(else\\|catch\\|try\\|continue\\|grep\\|map\\|BEGIN\\|END\\|UNITCHECK\\|CHECK\\|INIT\\)\\>")
|
||||
(or (and (looking-at (rx (or "else" "catch" "try"
|
||||
"finally" "defer"
|
||||
"continue" "grep" "map"
|
||||
"ADJUST" "BEGIN" "CHECK" "END"
|
||||
"INIT" "UNITCHECK")))
|
||||
(not (looking-at "\\(\\sw\\|_\\)+::")))
|
||||
;; sub f {}
|
||||
(progn
|
||||
|
@ -5168,18 +5206,16 @@ conditional/loop constructs."
|
|||
(if (eq (following-char) ?$ ) ; for my $var (list)
|
||||
(progn
|
||||
(forward-sexp -1)
|
||||
(if (looking-at "\\(state\\|my\\|local\\|our\\)\\>")
|
||||
(if (looking-at "\\(state\\|my\\|local\\|our\\|field\\)\\>")
|
||||
(forward-sexp -1))))
|
||||
(if (looking-at
|
||||
(concat "\\(elsif\\|if\\|unless\\|while\\|until"
|
||||
"\\|try\\|catch\\|finally\\|defer"
|
||||
"\\|for\\(each\\)?\\>\\(\\("
|
||||
cperl-maybe-white-and-comment-rex
|
||||
"\\(state\\|my\\|local\\|our\\)\\)?"
|
||||
"\\(state\\|my\\|local\\|our\\|field\\)\\)?"
|
||||
cperl-maybe-white-and-comment-rex
|
||||
(rx
|
||||
(sequence
|
||||
"$"
|
||||
(eval cperl--basic-identifier-rx)))
|
||||
(rx (eval cperl--basic-variable-rx))
|
||||
"\\)?\\)\\>"))
|
||||
(progn
|
||||
(goto-char top)
|
||||
|
@ -5296,6 +5332,7 @@ Returns some position at the last line."
|
|||
(opt (sequence "}" (0+ blank) ))
|
||||
symbol-start
|
||||
(or "else" "elsif" "continue" "if" "unless" "while" "until"
|
||||
"try" "catch" "finally" "defer"
|
||||
(sequence (or "for" "foreach")
|
||||
(opt
|
||||
(opt (sequence (1+ blank)
|
||||
|
@ -5625,6 +5662,8 @@ indentation and initial hashes. Behaves usually outside of comment."
|
|||
;; Previous space could have gone:
|
||||
(or (memq (preceding-char) '(?\s ?\t)) (insert " "))))))
|
||||
|
||||
;; The following lists are used for categorizing the entries found by
|
||||
;; `cperl-imenu--create-perl-index'.
|
||||
(defvar cperl-imenu-package-keywords '("package" "class" "role"))
|
||||
(defvar cperl-imenu-sub-keywords '("sub" "method" "function" "fun"))
|
||||
(defvar cperl-imenu-pod-keywords '("=head"))
|
||||
|
@ -5643,16 +5682,16 @@ comment, or POD."
|
|||
(index-pod-alist '())
|
||||
(index-sub-alist '())
|
||||
(index-unsorted-alist '())
|
||||
(package-stack '()) ; for package NAME BLOCK
|
||||
(current-package "(main)")
|
||||
(current-package-end (point-max))) ; end of package scope
|
||||
(namespace-stack '()) ; for package NAME BLOCK
|
||||
(current-namespace "(main)")
|
||||
(current-namespace-end (point-max))) ; end of package scope
|
||||
;; collect index entries
|
||||
(while (re-search-forward (rx (eval cperl--imenu-entries-rx)) nil t)
|
||||
;; First, check whether we have left the scope of previously
|
||||
;; recorded packages, and if so, eliminate them from the stack.
|
||||
(while (< current-package-end (point))
|
||||
(setq current-package (pop package-stack))
|
||||
(setq current-package-end (pop package-stack)))
|
||||
(while (< current-namespace-end (point))
|
||||
(setq current-namespace (pop namespace-stack))
|
||||
(setq current-namespace-end (pop namespace-stack)))
|
||||
(let ((state (syntax-ppss))
|
||||
(entry-type (match-string 1))
|
||||
name marker) ; for the "current" entry
|
||||
|
@ -5663,15 +5702,15 @@ comment, or POD."
|
|||
(setq name (match-string-no-properties 2)
|
||||
marker (copy-marker (match-end 2)))
|
||||
(if (string= (match-string 3) ";")
|
||||
(setq current-package name) ; package NAME;
|
||||
(setq current-namespace name) ; package NAME;
|
||||
;; No semicolon, therefore we have: package NAME BLOCK.
|
||||
;; Stash the current package, because we need to restore
|
||||
;; it after the end of BLOCK.
|
||||
(push current-package-end package-stack)
|
||||
(push current-package package-stack)
|
||||
(push current-namespace-end namespace-stack)
|
||||
(push current-namespace namespace-stack)
|
||||
;; record the current name and its scope
|
||||
(setq current-package name)
|
||||
(setq current-package-end (save-excursion
|
||||
(setq current-namespace name)
|
||||
(setq current-namespace-end (save-excursion
|
||||
(goto-char (match-beginning 3))
|
||||
(forward-sexp)
|
||||
(point))))
|
||||
|
@ -5682,14 +5721,14 @@ comment, or POD."
|
|||
(unless (nth 4 state) ; skip if in a comment
|
||||
(setq name (match-string-no-properties 2)
|
||||
marker (copy-marker (match-end 2)))
|
||||
;; Qualify the sub name with the package if it doesn't
|
||||
;; Qualify the sub name with the namespace if it doesn't
|
||||
;; already have one, and if it isn't lexically scoped.
|
||||
;; "my" and "state" subs are lexically scoped, but "our"
|
||||
;; are just lexical aliases to package subs.
|
||||
(if (and (null (string-match "::" name))
|
||||
(or (null (match-string 3))
|
||||
(string-equal (match-string 3) "our")))
|
||||
(setq name (concat current-package "::" name)))
|
||||
(setq name (concat current-namespace "::" name)))
|
||||
(let ((index (cons name marker)))
|
||||
(push index index-alist)
|
||||
(push index index-sub-alist)
|
||||
|
@ -5753,7 +5792,7 @@ comment, or POD."
|
|||
hier-list)
|
||||
index-alist)))
|
||||
(and index-package-alist
|
||||
(push (cons "+Packages+..."
|
||||
(push (cons "+Classes,Packages+..."
|
||||
(nreverse index-package-alist))
|
||||
index-alist))
|
||||
(and (or index-package-alist index-pod-alist
|
||||
|
@ -5846,13 +5885,17 @@ default function."
|
|||
'("if" "until" "while" "elsif" "else"
|
||||
"given" "when" "default" "break"
|
||||
"unless" "for"
|
||||
"try" "catch" "finally"
|
||||
"try" "catch" "defer" "finally"
|
||||
"foreach" "continue" "exit" "die" "last" "goto" "next"
|
||||
"redo" "return" "local" "exec"
|
||||
"do" "dump"
|
||||
"use" "our"
|
||||
"require" "package" "eval" "evalbytes" "my" "state"
|
||||
"BEGIN" "END" "CHECK" "INIT" "UNITCHECK"))) ; Flow control
|
||||
"class" "field" "method"
|
||||
"ADJUST" "BEGIN" "CHECK"
|
||||
"END" "INIT" "UNITCHECK"
|
||||
;; not in core, but per popular request
|
||||
"async" "await"))) ; Flow control
|
||||
"\\)\\>") 2) ; was "\\)[ \n\t;():,|&]"
|
||||
; In what follows we use `type' style
|
||||
; for overwritable builtins
|
||||
|
@ -5969,23 +6012,28 @@ default function."
|
|||
;; -------- anchored: Signature
|
||||
`(,(rx (sequence (in "(,")
|
||||
(eval cperl--ws*-rx)
|
||||
(group (or (eval cperl--basic-scalar-rx)
|
||||
(eval cperl--basic-array-rx)
|
||||
(eval cperl--basic-hash-rx)))))
|
||||
(group (eval cperl--basic-variable-rx))))
|
||||
(progn
|
||||
(goto-char (match-beginning 2)) ; pre-match: Back to sig
|
||||
(match-end 2))
|
||||
nil
|
||||
(1 font-lock-variable-name-face)))
|
||||
;; -------- various stuff calling for a package name
|
||||
;; (matcher subexp facespec)
|
||||
`(,(rx (sequence symbol-start
|
||||
(or "package" "require" "use" "import"
|
||||
"no" "bootstrap")
|
||||
(eval cperl--ws+-rx)
|
||||
(group-n 1 (eval cperl--normal-identifier-rx))
|
||||
(any " \t\n;"))) ; require A if B;
|
||||
1 font-lock-function-name-face)
|
||||
;; (matcher (subexp facespec) (subexp facespec))
|
||||
`(,(rx (sequence
|
||||
(or (sequence symbol-start
|
||||
(or "package" "require" "use" "import"
|
||||
"no" "bootstrap" "class")
|
||||
(eval cperl--ws+-rx))
|
||||
(sequence (group-n 2 (sequence ":"
|
||||
(eval cperl--ws*-rx)
|
||||
"isa"))
|
||||
"("
|
||||
(eval cperl--ws*-rx)))
|
||||
(group-n 1 (eval cperl--normal-identifier-rx))
|
||||
(any " \t\n;)"))) ; require A if B;
|
||||
(1 font-lock-function-name-face)
|
||||
(2 font-lock-constant-face t t))
|
||||
;; -------- formats
|
||||
;; (matcher subexp facespec)
|
||||
'("^[ \t]*format[ \t]+\\([a-zA-Z_][a-zA-Z_0-9:]*\\)[ \t]*=[ \t]*$"
|
||||
|
@ -6047,7 +6095,7 @@ default function."
|
|||
;; 1=my_etc, 2=white? 3=(+white? 4=white? 5=var
|
||||
;; -------- variable declarations
|
||||
;; (matcher (subexp facespec) ...
|
||||
`(,(rx (sequence (or "state" "my" "local" "our"))
|
||||
`(,(rx (sequence (or "state" "my" "local" "our" "field"))
|
||||
(eval cperl--ws*-rx)
|
||||
(opt (group (sequence "(" (eval cperl--ws*-rx))))
|
||||
(group
|
||||
|
@ -6959,7 +7007,9 @@ Does not move point."
|
|||
127
|
||||
(if (string-match "^package " (car elt))
|
||||
(substring (car elt) 8)
|
||||
(car elt) )
|
||||
(if (string-match "^class " (car elt))
|
||||
(substring (car elt) 6)
|
||||
(car elt)))
|
||||
1
|
||||
(number-to-string (elt elt 2)) ; Line
|
||||
","
|
||||
|
@ -7712,6 +7762,7 @@ __FILE__ Current (source) filename.
|
|||
__LINE__ Current line in current source.
|
||||
__PACKAGE__ Current package.
|
||||
__SUB__ Current sub.
|
||||
ADJUST {...} Callback for object creation
|
||||
ARGV Default multi-file input filehandle. <ARGV> is a synonym for <>.
|
||||
ARGVOUT Output filehandle with -i flag.
|
||||
BEGIN { ... } Immediately executed (during compilation) piece of code.
|
||||
|
@ -7722,7 +7773,9 @@ INIT { ... } Pseudo-subroutine executed before the script starts running.
|
|||
DATA Input filehandle for what follows after __END__ or __DATA__.
|
||||
accept(NEWSOCKET,GENERICSOCKET)
|
||||
alarm(SECONDS)
|
||||
async(SUB NAME {}|SUB {}) Mark function as potentially asynchronous
|
||||
atan2(X,Y)
|
||||
await(ASYNCEXPR) Yield result of Future
|
||||
bind(SOCKET,NAME)
|
||||
binmode(FILEHANDLE)
|
||||
break Break out of a given/when statement
|
||||
|
@ -7732,6 +7785,7 @@ chmod(LIST)
|
|||
chop[(LIST|VAR)]
|
||||
chown(LIST)
|
||||
chroot(FILENAME)
|
||||
class NAME Introduce a class.
|
||||
close(FILEHANDLE)
|
||||
closedir(DIRHANDLE)
|
||||
... cmp ... String compare.
|
||||
|
@ -7742,6 +7796,7 @@ crypt(PLAINTEXT,SALT)
|
|||
dbmclose(%HASH)
|
||||
dbmopen(%HASH,DBNAME,MODE)
|
||||
default { ... } default case for given/when block
|
||||
defer { ... } run this block after the containing block.
|
||||
defined(EXPR)
|
||||
delete($HASH{KEY})
|
||||
die(LIST)
|
||||
|
@ -7763,6 +7818,7 @@ exec([TRUENAME] ARGV0, ARGVs) or exec(SHELL_COMMAND_LINE)
|
|||
exit(EXPR)
|
||||
exp(EXPR)
|
||||
fcntl(FILEHANDLE,FUNCTION,SCALAR)
|
||||
field VAR [:param[(NAME)]] [=EXPR] declare an object attribute
|
||||
fileno(FILEHANDLE)
|
||||
flock(FILEHANDLE,OPERATION)
|
||||
for (EXPR;EXPR;EXPR) { ... }
|
||||
|
@ -7803,7 +7859,7 @@ hex(EXPR)
|
|||
if (EXPR) { ... } [ elsif (EXPR) { ... } ... ] [ else { ... } ] or EXPR if EXPR
|
||||
index(STR,SUBSTR[,OFFSET])
|
||||
int(EXPR)
|
||||
ioctl(FILEHANDLE,FUNCTION,SCALAR)
|
||||
ioctl(FILEHANDLE,FUNCTION,SCALA)R
|
||||
join(EXPR,LIST)
|
||||
keys(%HASH)
|
||||
kill(LIST)
|
||||
|
@ -7818,6 +7874,7 @@ log(EXPR)
|
|||
lstat(EXPR|FILEHANDLE|VAR)
|
||||
... lt ... String less than.
|
||||
m/PATTERN/iogsmx
|
||||
method [NAME [(signature)]] { BODY } method NAME;
|
||||
mkdir(FILENAME,MODE)
|
||||
msgctl(ID,CMD,ARG)
|
||||
msgget(KEY,FLAGS)
|
||||
|
@ -7956,7 +8013,7 @@ lc [ EXPR ] Returns lowercased EXPR.
|
|||
lcfirst [ EXPR ] Returns EXPR with lower-cased first letter.
|
||||
grep EXPR,LIST or grep {BLOCK} LIST Filters LIST via EXPR/BLOCK.
|
||||
map EXPR, LIST or map {BLOCK} LIST Applies EXPR/BLOCK to elts of LIST.
|
||||
no PACKAGE [SYMBOL1, ...] Partial reverse for `use'. Runs `unimport' method.
|
||||
no MODULE [SYMBOL1, ...] Partial reverse for `use'. Runs `unimport' method.
|
||||
not ... Low-precedence synonym for ! - negation.
|
||||
... or ... Low-precedence synonym for ||.
|
||||
pos STRING Set/Get end-position of the last match over this string, see \\G.
|
||||
|
@ -7967,12 +8024,12 @@ readline FH Synonym of <FH>.
|
|||
readpipe CMD Synonym of \\=`CMD\\=`.
|
||||
ref [ EXPR ] Type of EXPR when dereferenced.
|
||||
sysopen FH, FILENAME, MODE [, PERM] (MODE is numeric, see Fcntl.)
|
||||
tie VAR, PACKAGE, LIST Hide an object behind a simple Perl variable.
|
||||
tie VAR, CLASS, LIST Hide an object behind a simple Perl variable.
|
||||
tied Returns internal object for a tied data.
|
||||
uc [ EXPR ] Returns upcased EXPR.
|
||||
ucfirst [ EXPR ] Returns EXPR with upcased first letter.
|
||||
untie VAR Unlink an object from a simple Perl variable.
|
||||
use PACKAGE [SYMBOL1, ...] Compile-time `require' with consequent `import'.
|
||||
use MODULE [SYMBOL1, ...] Compile-time `require' with consequent `import'.
|
||||
... xor ... Low-precedence synonym for exclusive or.
|
||||
prototype \\&SUB Returns prototype of the function given a reference.
|
||||
=head1 Top-level heading.
|
||||
|
|
|
@ -9156,6 +9156,7 @@ presented."
|
|||
"When nil, `auto-save-mode' will auto-save remote files.
|
||||
Any other value means that it will not."
|
||||
:group 'auto-save
|
||||
:group 'tramp
|
||||
:type 'boolean
|
||||
:version "30.1")
|
||||
|
||||
|
|
|
@ -1,26 +0,0 @@
|
|||
@echo off
|
||||
rem ----------------------------------------------------------------------
|
||||
rem This was the old configuration script for MS Windows operating systems
|
||||
rem Copyright (C) 1999-2023 Free Software Foundation, Inc.
|
||||
|
||||
rem This file is part of GNU Emacs.
|
||||
|
||||
rem GNU Emacs is free software: you can redistribute it and/or modify
|
||||
rem it under the terms of the GNU General Public License as published by
|
||||
rem the Free Software Foundation, either version 3 of the License, or
|
||||
rem (at your option) any later version.
|
||||
|
||||
rem GNU Emacs is distributed in the hope that it will be useful,
|
||||
rem but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
rem MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
rem GNU General Public License for more details.
|
||||
|
||||
rem You should have received a copy of the GNU General Public License
|
||||
rem along with GNU Emacs. If not, see https://www.gnu.org/licenses/.
|
||||
|
||||
rem ----------------------------------------------------------------------
|
||||
echo ****************************************************************
|
||||
echo *** THIS METHOD OF BUILDING EMACS IS NO LONGER SUPPORTED. **
|
||||
echo *** INSTEAD, FOLLOW THE INSTRUCTIONS IN THE FILE INSTALL. **
|
||||
echo *** IN THE SAME DIRECTORY AS THIS BATCH FILE. **
|
||||
echo ****************************************************************
|
|
@ -1,24 +0,0 @@
|
|||
@echo off
|
||||
if (%1)==() echo Usage: %0 tracefile
|
||||
if (%1)==() goto done
|
||||
rem Need to fiddle with the dumped image so prep doesn't break it
|
||||
obj\i386\preprep ..\src\obj\i386\emacs.exe ..\src\obj\i386\pemacs.exe
|
||||
copy ..\src\obj\i386\temacs.map ..\src\obj\i386\pemacs.map
|
||||
rem -----------------------------------------------------------------
|
||||
rem Use this version to profile explicit commands only.
|
||||
rem prep /om /ft /sf _Fexecute_extended_command ..\src\obj\i386\pemacs
|
||||
rem -----------------------------------------------------------------
|
||||
rem Use this version to ignore startup code
|
||||
prep /om /ft /sf _command_loop_1 ..\src\obj\i386\pemacs
|
||||
rem -----------------------------------------------------------------
|
||||
rem Use this version to include startup code
|
||||
rem prep /om /ft ..\src\obj\i386\pemacs
|
||||
rem -----------------------------------------------------------------
|
||||
if errorlevel 1 goto done
|
||||
profile ..\src\obj\i386\pemacs %2 %3 %4 %5 %6 %7 %8 %9
|
||||
if errorlevel 1 goto done
|
||||
prep /m ..\src\obj\i386\pemacs
|
||||
if errorlevel 1 goto done
|
||||
plist ..\src\obj\i386\pemacs > %1
|
||||
:done
|
||||
|
24
nt/ftime.bat
24
nt/ftime.bat
|
@ -1,24 +0,0 @@
|
|||
@echo off
|
||||
if (%1)==() echo Usage: %0 tracefile
|
||||
if (%1)==() goto done
|
||||
rem Need to fiddle with the dumped image so prep doesn't break it
|
||||
obj\i386\preprep ..\src\obj\i386\emacs.exe ..\src\obj\i386\pemacs.exe
|
||||
copy ..\src\obj\i386\temacs.map ..\src\obj\i386\pemacs.map
|
||||
rem -----------------------------------------------------------------
|
||||
rem Use this version to profile explicit commands only.
|
||||
prep /om /ft /sf _Fexecute_extended_command ..\src\obj\i386\pemacs
|
||||
rem -----------------------------------------------------------------
|
||||
rem Use this version to ignore startup code
|
||||
rem prep /om /ft /sf _command_loop_1 ..\src\obj\i386\pemacs
|
||||
rem -----------------------------------------------------------------
|
||||
rem Use this version to include startup code
|
||||
rem prep /om /ft ..\src\obj\i386\pemacs
|
||||
rem -----------------------------------------------------------------
|
||||
if errorlevel 1 goto done
|
||||
profile ..\src\obj\i386\pemacs %2 %3 %4 %5 %6 %7 %8 %9
|
||||
if errorlevel 1 goto done
|
||||
prep /m ..\src\obj\i386\pemacs
|
||||
if errorlevel 1 goto done
|
||||
plist ..\src\obj\i386\pemacs > %1
|
||||
:done
|
||||
|
830
nt/preprep.c
830
nt/preprep.c
|
@ -1,830 +0,0 @@
|
|||
/* Pre-process emacs.exe for profiling by MSVC.
|
||||
Copyright (C) 1999, 2001-2023 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Emacs.
|
||||
|
||||
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 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
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
Andrew Innes <andrewi@harlequin.co.uk> 16-Jan-1999
|
||||
based on code from addsection.c
|
||||
*/
|
||||
|
||||
#define DEFER_MS_W32_H
|
||||
#include <config.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <fcntl.h>
|
||||
#include <time.h>
|
||||
#if defined(__GNUC__) && !defined(MINGW_W64)
|
||||
#define _ANONYMOUS_UNION
|
||||
#define _ANONYMOUS_STRUCT
|
||||
#endif
|
||||
#include <windows.h>
|
||||
|
||||
/* Include relevant definitions from IMAGEHLP.H, which can be found
|
||||
in \\win32sdk\mstools\samples\image\include\imagehlp.h. */
|
||||
|
||||
PIMAGE_NT_HEADERS (__stdcall * pfnCheckSumMappedFile) (LPVOID BaseAddress,
|
||||
DWORD_PTR FileLength,
|
||||
PDWORD_PTR HeaderSum,
|
||||
PDWORD_PTR CheckSum);
|
||||
|
||||
#undef min
|
||||
#undef max
|
||||
#define min(x, y) (((x) < (y)) ? (x) : (y))
|
||||
#define max(x, y) (((x) > (y)) ? (x) : (y))
|
||||
|
||||
|
||||
/* File handling. */
|
||||
|
||||
typedef struct file_data {
|
||||
const char *name;
|
||||
unsigned long size;
|
||||
HANDLE file;
|
||||
HANDLE file_mapping;
|
||||
unsigned char *file_base;
|
||||
} file_data;
|
||||
|
||||
int
|
||||
open_input_file (file_data *p_file, const char *filename)
|
||||
{
|
||||
HANDLE file;
|
||||
HANDLE file_mapping;
|
||||
void *file_base;
|
||||
unsigned long size, upper_size;
|
||||
|
||||
file = CreateFile (filename, GENERIC_READ, FILE_SHARE_READ, NULL,
|
||||
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
|
||||
if (file == INVALID_HANDLE_VALUE)
|
||||
return FALSE;
|
||||
|
||||
size = GetFileSize (file, &upper_size);
|
||||
file_mapping = CreateFileMapping (file, NULL, PAGE_READONLY,
|
||||
0, size, NULL);
|
||||
if (!file_mapping)
|
||||
return FALSE;
|
||||
|
||||
file_base = MapViewOfFile (file_mapping, FILE_MAP_READ, 0, 0, size);
|
||||
if (file_base == 0)
|
||||
return FALSE;
|
||||
|
||||
p_file->name = filename;
|
||||
p_file->size = size;
|
||||
p_file->file = file;
|
||||
p_file->file_mapping = file_mapping;
|
||||
p_file->file_base = file_base;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
int
|
||||
open_output_file (file_data *p_file, const char *filename, unsigned long size)
|
||||
{
|
||||
HANDLE file;
|
||||
HANDLE file_mapping;
|
||||
void *file_base;
|
||||
|
||||
file = CreateFile (filename, GENERIC_READ | GENERIC_WRITE, 0, NULL,
|
||||
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
|
||||
if (file == INVALID_HANDLE_VALUE)
|
||||
return FALSE;
|
||||
|
||||
file_mapping = CreateFileMapping (file, NULL, PAGE_READWRITE,
|
||||
0, size, NULL);
|
||||
if (!file_mapping)
|
||||
return FALSE;
|
||||
|
||||
file_base = MapViewOfFile (file_mapping, FILE_MAP_WRITE, 0, 0, size);
|
||||
if (file_base == 0)
|
||||
return FALSE;
|
||||
|
||||
p_file->name = filename;
|
||||
p_file->size = size;
|
||||
p_file->file = file;
|
||||
p_file->file_mapping = file_mapping;
|
||||
p_file->file_base = file_base;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
int
|
||||
open_inout_file (file_data *p_file, const char *filename)
|
||||
{
|
||||
HANDLE file;
|
||||
HANDLE file_mapping;
|
||||
void *file_base;
|
||||
unsigned long size, upper_size;
|
||||
|
||||
file = CreateFile (filename, GENERIC_READ | GENERIC_WRITE, 0, NULL,
|
||||
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
|
||||
if (file == INVALID_HANDLE_VALUE)
|
||||
return FALSE;
|
||||
|
||||
size = GetFileSize (file, &upper_size);
|
||||
file_mapping = CreateFileMapping (file, NULL, PAGE_READWRITE,
|
||||
0, size, NULL);
|
||||
if (!file_mapping)
|
||||
return FALSE;
|
||||
|
||||
file_base = MapViewOfFile (file_mapping, FILE_MAP_WRITE, 0, 0, size);
|
||||
if (file_base == 0)
|
||||
return FALSE;
|
||||
|
||||
p_file->name = filename;
|
||||
p_file->size = size;
|
||||
p_file->file = file;
|
||||
p_file->file_mapping = file_mapping;
|
||||
p_file->file_base = file_base;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* Close the system structures associated with the given file. */
|
||||
void
|
||||
close_file_data (file_data *p_file)
|
||||
{
|
||||
UnmapViewOfFile (p_file->file_base);
|
||||
CloseHandle (p_file->file_mapping);
|
||||
/* For the case of output files, set final size. */
|
||||
SetFilePointer (p_file->file, p_file->size, NULL, FILE_BEGIN);
|
||||
SetEndOfFile (p_file->file);
|
||||
CloseHandle (p_file->file);
|
||||
}
|
||||
|
||||
|
||||
/* Routines to manipulate NT executable file sections. */
|
||||
|
||||
unsigned long
|
||||
get_unrounded_section_size (PIMAGE_SECTION_HEADER p_section)
|
||||
{
|
||||
/* The true section size, before rounding, for an initialized data or
|
||||
code section. (Supposedly some linkers swap the meaning of these
|
||||
two values.) */
|
||||
return min (p_section->SizeOfRawData,
|
||||
p_section->Misc.VirtualSize);
|
||||
}
|
||||
|
||||
/* Return pointer to section header for named section. */
|
||||
IMAGE_SECTION_HEADER *
|
||||
find_section (const char *name, IMAGE_NT_HEADERS *nt_header)
|
||||
{
|
||||
PIMAGE_SECTION_HEADER section;
|
||||
int i;
|
||||
|
||||
section = IMAGE_FIRST_SECTION (nt_header);
|
||||
|
||||
for (i = 0; i < nt_header->FileHeader.NumberOfSections; i++)
|
||||
{
|
||||
if (strcmp (section->Name, name) == 0)
|
||||
return section;
|
||||
section++;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Return pointer to section header for section containing the given
|
||||
relative virtual address. */
|
||||
IMAGE_SECTION_HEADER *
|
||||
rva_to_section (DWORD_PTR rva, IMAGE_NT_HEADERS * nt_header)
|
||||
{
|
||||
PIMAGE_SECTION_HEADER section;
|
||||
int i;
|
||||
|
||||
section = IMAGE_FIRST_SECTION (nt_header);
|
||||
|
||||
for (i = 0; i < nt_header->FileHeader.NumberOfSections; i++)
|
||||
{
|
||||
/* Some linkers (eg. the NT SDK linker I believe) swapped the
|
||||
meaning of these two values - or rather, they ignored
|
||||
VirtualSize entirely and always set it to zero. This affects
|
||||
some very old exes (eg. gzip dated Dec 1993). Since
|
||||
w32_executable_type relies on this function to work reliably,
|
||||
we need to cope with this. */
|
||||
DWORD_PTR real_size = max (section->SizeOfRawData,
|
||||
section->Misc.VirtualSize);
|
||||
if (rva >= section->VirtualAddress
|
||||
&& rva < section->VirtualAddress + real_size)
|
||||
return section;
|
||||
section++;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Return pointer to section header for section containing the given
|
||||
offset in its raw data area. */
|
||||
IMAGE_SECTION_HEADER *
|
||||
offset_to_section (DWORD_PTR offset, IMAGE_NT_HEADERS * nt_header)
|
||||
{
|
||||
PIMAGE_SECTION_HEADER section;
|
||||
int i;
|
||||
|
||||
section = IMAGE_FIRST_SECTION (nt_header);
|
||||
|
||||
for (i = 0; i < nt_header->FileHeader.NumberOfSections; i++)
|
||||
{
|
||||
if (offset >= section->PointerToRawData
|
||||
&& offset < section->PointerToRawData + section->SizeOfRawData)
|
||||
return section;
|
||||
section++;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Return offset to an object in dst, given offset in src. We assume
|
||||
there is at least one section in both src and dst images, and that
|
||||
the some sections may have been added to dst (after sections in src). */
|
||||
static DWORD_PTR
|
||||
relocate_offset (DWORD_PTR offset,
|
||||
IMAGE_NT_HEADERS * src_nt_header,
|
||||
IMAGE_NT_HEADERS * dst_nt_header)
|
||||
{
|
||||
PIMAGE_SECTION_HEADER src_section = IMAGE_FIRST_SECTION (src_nt_header);
|
||||
PIMAGE_SECTION_HEADER dst_section = IMAGE_FIRST_SECTION (dst_nt_header);
|
||||
int i = 0;
|
||||
|
||||
while (offset >= src_section->PointerToRawData)
|
||||
{
|
||||
if (offset < src_section->PointerToRawData + src_section->SizeOfRawData)
|
||||
break;
|
||||
i++;
|
||||
if (i == src_nt_header->FileHeader.NumberOfSections)
|
||||
{
|
||||
/* Handle offsets after the last section. */
|
||||
dst_section = IMAGE_FIRST_SECTION (dst_nt_header);
|
||||
dst_section += dst_nt_header->FileHeader.NumberOfSections - 1;
|
||||
while (dst_section->PointerToRawData == 0)
|
||||
dst_section--;
|
||||
while (src_section->PointerToRawData == 0)
|
||||
src_section--;
|
||||
return offset
|
||||
+ (dst_section->PointerToRawData + dst_section->SizeOfRawData)
|
||||
- (src_section->PointerToRawData + src_section->SizeOfRawData);
|
||||
}
|
||||
src_section++;
|
||||
dst_section++;
|
||||
}
|
||||
return offset +
|
||||
(dst_section->PointerToRawData - src_section->PointerToRawData);
|
||||
}
|
||||
|
||||
#define OFFSET_TO_RVA(offset, section) \
|
||||
((section)->VirtualAddress + ((DWORD_PTR)(offset) - (section)->PointerToRawData))
|
||||
|
||||
#define RVA_TO_OFFSET(rva, section) \
|
||||
((section)->PointerToRawData + ((DWORD_PTR)(rva) - (section)->VirtualAddress))
|
||||
|
||||
#define RVA_TO_SECTION_OFFSET(rva, section) \
|
||||
((DWORD_PTR)(rva) - (section)->VirtualAddress)
|
||||
|
||||
#define RVA_TO_PTR(var,section,filedata) \
|
||||
((void *)((unsigned char *)(RVA_TO_OFFSET(var,section) + (filedata)->file_base)))
|
||||
|
||||
/* Convert address in executing image to RVA. */
|
||||
#define PTR_TO_RVA(ptr) ((DWORD_PTR)(ptr) - (DWORD_PTR) GetModuleHandle (NULL))
|
||||
|
||||
#define PTR_TO_OFFSET(ptr, pfile_data) \
|
||||
((unsigned const char *)(ptr) - (pfile_data)->file_base)
|
||||
|
||||
#define OFFSET_TO_PTR(offset, pfile_data) \
|
||||
((pfile_data)->file_base + (DWORD_PTR)(offset))
|
||||
|
||||
#define ROUND_UP(p, align) \
|
||||
(((DWORD_PTR)(p) + (align)-1) & ~((DWORD_PTR)(align)-1))
|
||||
#define ROUND_DOWN(p, align) ((DWORD_PTR)(p) & ~((DWORD_PTR)(align)-1))
|
||||
|
||||
|
||||
/* The MSVC prep program generates a ._xe file from .exe, where relevant
|
||||
function calls etc have been patched to go through thunks (generated
|
||||
by prep) that record timing/call information. Because the thunks
|
||||
need to make references to functions imported from profile.dll, the
|
||||
import table must be expanded; the end result is that all the
|
||||
sections following .rdata are relocated to higher RVAs (add a final
|
||||
code section is added holding all the thunks). The .reloc section is
|
||||
also expanded, so that the thunks themselves are relocatable.
|
||||
|
||||
It is this relocation which kills emacs._xe, because the dumped heap
|
||||
pointers aren't relocated, because there is no relocation data for
|
||||
either the relevant global/static variables or the heap section
|
||||
itself, both of which contain pointers into the heap. [Note that
|
||||
static variables which aren't initialized during linking may become
|
||||
initialized with heap pointers, or even pointers to other static
|
||||
variables, because of dumping.]
|
||||
|
||||
We could potentially generate the relocation data ourselves by making
|
||||
two versions of temacs, one with an extra dummy section before
|
||||
EMHEAP to offset it, and then compare the dumped executables from
|
||||
both. That is a lot of work though, and it doesn't solve the problem
|
||||
of dumped pointers to static variables, which also can be relocated.
|
||||
|
||||
A better solution is to pre-process emacs.exe so that the .rdata and
|
||||
.reloc sections are moved to the end of the section table, and thus
|
||||
prep won't relocate anything else. (Of course, we leave "dead"
|
||||
copies of these two sections in place, so that the virtual address of
|
||||
everything else is unaffected.) Relocating the .reloc data is
|
||||
trivial - we just update the IMAGE_BASE_RELOCATION address in the
|
||||
header (the data itself doesn't change). Relocating the import table
|
||||
is more complicated though, because the calls to imported functions
|
||||
must be patched up. That requires us to selectively apply the base
|
||||
relocations when we encounter references to imported functions (or
|
||||
variables) in other sections, but at least the base relocations are
|
||||
easy to parse. */
|
||||
|
||||
static void
|
||||
copy_executable_and_move_sections (file_data *p_infile,
|
||||
file_data *p_outfile)
|
||||
{
|
||||
unsigned char *dst;
|
||||
PIMAGE_DOS_HEADER dos_header;
|
||||
PIMAGE_NT_HEADERS nt_header;
|
||||
PIMAGE_NT_HEADERS dst_nt_header;
|
||||
PIMAGE_SECTION_HEADER section;
|
||||
PIMAGE_SECTION_HEADER dst_section;
|
||||
PIMAGE_SECTION_HEADER import_section;
|
||||
PIMAGE_SECTION_HEADER reloc_section;
|
||||
PIMAGE_DATA_DIRECTORY import_dir;
|
||||
PIMAGE_DATA_DIRECTORY reloc_dir;
|
||||
DWORD_PTR import_delta_rva;
|
||||
DWORD_PTR reloc_delta_rva;
|
||||
DWORD_PTR offset;
|
||||
int i;
|
||||
|
||||
#define COPY_CHUNK(message, src, size) \
|
||||
do { \
|
||||
unsigned const char *s = (void *)(src); \
|
||||
unsigned long count = (size); \
|
||||
printf ("%s\n", (message)); \
|
||||
printf ("\t0x%08x Offset in input file.\n", s - p_infile->file_base); \
|
||||
printf ("\t0x%08x Offset in output file.\n", dst - p_outfile->file_base); \
|
||||
printf ("\t0x%08x Size in bytes.\n", count); \
|
||||
memcpy (dst, s, count); \
|
||||
dst += count; \
|
||||
} while (0)
|
||||
|
||||
#define DST_TO_OFFSET() PTR_TO_OFFSET (dst, p_outfile)
|
||||
#define ROUND_UP_DST_AND_ZERO(align) \
|
||||
do { \
|
||||
unsigned char *newdst = p_outfile->file_base \
|
||||
+ ROUND_UP (DST_TO_OFFSET (), (align)); \
|
||||
/* Zero the alignment slop; it may actually initialize real data. */ \
|
||||
memset (dst, 0, newdst - dst); \
|
||||
dst = newdst; \
|
||||
} while (0)
|
||||
|
||||
/* Copy the source image sequentially, ie. section by section after
|
||||
copying the headers and section table, to simplify the process of
|
||||
relocating the .rdata and .reloc section table entries (which might
|
||||
force the raw section data to be relocated).
|
||||
|
||||
Note that dst is updated implicitly by each COPY_CHUNK. */
|
||||
|
||||
dos_header = (PIMAGE_DOS_HEADER) p_infile->file_base;
|
||||
nt_header = (PIMAGE_NT_HEADERS) (((unsigned char *) dos_header) +
|
||||
dos_header->e_lfanew);
|
||||
section = IMAGE_FIRST_SECTION (nt_header);
|
||||
|
||||
import_dir = &nt_header->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
|
||||
import_section = rva_to_section (import_dir->VirtualAddress, nt_header);
|
||||
|
||||
reloc_dir = &nt_header->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC];
|
||||
reloc_section = rva_to_section (reloc_dir->VirtualAddress, nt_header);
|
||||
if (!reloc_section)
|
||||
{
|
||||
printf ("No relocation data, cannot prepare for profile prepping.\n");
|
||||
exit (1);
|
||||
}
|
||||
|
||||
dst = (unsigned char *) p_outfile->file_base;
|
||||
|
||||
COPY_CHUNK ("Copying DOS header...", dos_header,
|
||||
(DWORD_PTR) nt_header - (DWORD_PTR) dos_header);
|
||||
dst_nt_header = (PIMAGE_NT_HEADERS) dst;
|
||||
COPY_CHUNK ("Copying NT header...", nt_header,
|
||||
(DWORD_PTR) section - (DWORD_PTR) nt_header);
|
||||
dst_section = (PIMAGE_SECTION_HEADER) dst;
|
||||
COPY_CHUNK ("Copying section table...", section,
|
||||
nt_header->FileHeader.NumberOfSections * sizeof (*section));
|
||||
|
||||
/* Leave room for extra section table entries; filled in below. */
|
||||
dst += 2 * sizeof (*section);
|
||||
|
||||
/* Align the first section's raw data area, and set the header size
|
||||
field accordingly. */
|
||||
ROUND_UP_DST_AND_ZERO (dst_nt_header->OptionalHeader.FileAlignment);
|
||||
dst_nt_header->OptionalHeader.SizeOfHeaders = DST_TO_OFFSET ();
|
||||
|
||||
for (i = 0; i < nt_header->FileHeader.NumberOfSections;
|
||||
i++, section++, dst_section++)
|
||||
{
|
||||
char msg[100];
|
||||
sprintf (msg, "Copying raw data for %s...", section->Name);
|
||||
|
||||
/* "Blank out" the two sections being relocated. */
|
||||
if (section == import_section || section == reloc_section)
|
||||
{
|
||||
dst_section->Name[0] = 'X';
|
||||
dst_section->Misc.VirtualSize =
|
||||
ROUND_UP (dst_section->Misc.VirtualSize,
|
||||
dst_nt_header->OptionalHeader.SectionAlignment);
|
||||
dst_section->PointerToRawData = 0;
|
||||
dst_section->SizeOfRawData = 0;
|
||||
dst_section->Characteristics &= ~IMAGE_SCN_CNT_INITIALIZED_DATA;
|
||||
dst_section->Characteristics |= IMAGE_SCN_CNT_UNINITIALIZED_DATA;
|
||||
dst_section->Characteristics &= ~IMAGE_SCN_MEM_WRITE;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Update the file-relative offset for this section's raw data (if
|
||||
it has any) in case things have been relocated; we will update
|
||||
the other offsets below once we know where everything is. */
|
||||
if (dst_section->PointerToRawData)
|
||||
dst_section->PointerToRawData = DST_TO_OFFSET ();
|
||||
|
||||
/* Copy the original raw data. */
|
||||
COPY_CHUNK
|
||||
(msg, OFFSET_TO_PTR (section->PointerToRawData, p_infile),
|
||||
section->SizeOfRawData);
|
||||
|
||||
/* Round up the raw data size to the new alignment. */
|
||||
dst_section->SizeOfRawData =
|
||||
ROUND_UP (dst_section->SizeOfRawData,
|
||||
dst_nt_header->OptionalHeader.FileAlignment);
|
||||
|
||||
/* Align the next section's raw data area. */
|
||||
ROUND_UP_DST_AND_ZERO (dst_nt_header->OptionalHeader.FileAlignment);
|
||||
}
|
||||
|
||||
/* Add the extra section entries, copying the raw data we skipped
|
||||
earlier. We'll patch up the data itself below. */
|
||||
if (import_section != NULL)
|
||||
{
|
||||
dst_nt_header->FileHeader.NumberOfSections++;
|
||||
dst_nt_header->OptionalHeader.SizeOfImage +=
|
||||
ROUND_UP (import_section->Misc.VirtualSize,
|
||||
dst_nt_header->OptionalHeader.SectionAlignment);
|
||||
*dst_section = *import_section;
|
||||
dst_section->VirtualAddress =
|
||||
dst_section[-1].VirtualAddress
|
||||
+ ROUND_UP (dst_section[-1].Misc.VirtualSize,
|
||||
dst_nt_header->OptionalHeader.SectionAlignment);
|
||||
dst_section->PointerToRawData = DST_TO_OFFSET ();
|
||||
/* Remember delta applied to import section. */
|
||||
import_delta_rva = dst_section->VirtualAddress - import_section->VirtualAddress;
|
||||
COPY_CHUNK
|
||||
("Relocating import directory",
|
||||
OFFSET_TO_PTR (import_section->PointerToRawData, p_infile),
|
||||
import_section->SizeOfRawData);
|
||||
ROUND_UP_DST_AND_ZERO (dst_nt_header->OptionalHeader.FileAlignment);
|
||||
dst_section++;
|
||||
}
|
||||
if (reloc_section != NULL)
|
||||
{
|
||||
dst_nt_header->FileHeader.NumberOfSections++;
|
||||
dst_nt_header->OptionalHeader.SizeOfImage +=
|
||||
ROUND_UP (reloc_section->Misc.VirtualSize,
|
||||
dst_nt_header->OptionalHeader.SectionAlignment);
|
||||
*dst_section = *reloc_section;
|
||||
dst_section->VirtualAddress =
|
||||
dst_section[-1].VirtualAddress
|
||||
+ ROUND_UP (dst_section[-1].Misc.VirtualSize,
|
||||
dst_nt_header->OptionalHeader.SectionAlignment);
|
||||
dst_section->PointerToRawData = DST_TO_OFFSET ();
|
||||
/* Remember delta applied to reloc section. */
|
||||
reloc_delta_rva = dst_section->VirtualAddress - reloc_section->VirtualAddress;
|
||||
COPY_CHUNK
|
||||
("Relocating base relocations directory",
|
||||
OFFSET_TO_PTR (reloc_section->PointerToRawData, p_infile),
|
||||
reloc_section->SizeOfRawData);
|
||||
ROUND_UP_DST_AND_ZERO (dst_nt_header->OptionalHeader.FileAlignment);
|
||||
reloc_dir = &dst_nt_header->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC];
|
||||
reloc_dir->VirtualAddress += reloc_delta_rva;
|
||||
dst_section++;
|
||||
}
|
||||
|
||||
/* Copy remainder of source image. */
|
||||
section--;
|
||||
offset = ROUND_UP (section->PointerToRawData + section->SizeOfRawData,
|
||||
nt_header->OptionalHeader.FileAlignment);
|
||||
COPY_CHUNK
|
||||
("Copying remainder of executable...",
|
||||
OFFSET_TO_PTR (offset, p_infile),
|
||||
p_infile->size - offset);
|
||||
|
||||
/* Final size for new image. */
|
||||
p_outfile->size = DST_TO_OFFSET ();
|
||||
|
||||
/* Now patch up remaining file-relative offsets. */
|
||||
printf ("Patching up raw data offsets...\n");
|
||||
|
||||
section = IMAGE_FIRST_SECTION (nt_header);
|
||||
dst_section = IMAGE_FIRST_SECTION (dst_nt_header);
|
||||
|
||||
#define ADJUST_OFFSET(var) \
|
||||
do { \
|
||||
if ((var) != 0) \
|
||||
(var) = relocate_offset ((var), nt_header, dst_nt_header); \
|
||||
} while (0)
|
||||
|
||||
#define ADJUST_IMPORT_RVA(var) \
|
||||
do { \
|
||||
if ((var) != 0) \
|
||||
*((DWORD_PTR *)&(var)) += import_delta_rva; \
|
||||
} while (0)
|
||||
|
||||
dst_nt_header->OptionalHeader.SizeOfInitializedData = 0;
|
||||
dst_nt_header->OptionalHeader.SizeOfUninitializedData = 0;
|
||||
for (i = 0; i < dst_nt_header->FileHeader.NumberOfSections; i++)
|
||||
{
|
||||
/* Recompute data sizes for completeness. */
|
||||
if (dst_section[i].Characteristics & IMAGE_SCN_CNT_INITIALIZED_DATA)
|
||||
dst_nt_header->OptionalHeader.SizeOfInitializedData +=
|
||||
ROUND_UP (dst_section[i].Misc.VirtualSize, dst_nt_header->OptionalHeader.FileAlignment);
|
||||
else if (dst_section[i].Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA)
|
||||
dst_nt_header->OptionalHeader.SizeOfUninitializedData +=
|
||||
ROUND_UP (dst_section[i].Misc.VirtualSize, dst_nt_header->OptionalHeader.FileAlignment);
|
||||
|
||||
ADJUST_OFFSET (dst_section[i].PointerToLinenumbers);
|
||||
}
|
||||
|
||||
ADJUST_OFFSET (dst_nt_header->FileHeader.PointerToSymbolTable);
|
||||
|
||||
/* Update offsets in debug directory entries. Note that the debug
|
||||
directory may be in the same section as the import table, so its
|
||||
RVA may need to be adjusted too. */
|
||||
{
|
||||
PIMAGE_DATA_DIRECTORY debug_dir =
|
||||
&dst_nt_header->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG];
|
||||
PIMAGE_DEBUG_DIRECTORY debug_entry;
|
||||
|
||||
/* Update debug_dir if part of import_section. */
|
||||
if (rva_to_section (debug_dir->VirtualAddress, nt_header) == import_section)
|
||||
debug_dir->VirtualAddress += import_delta_rva;
|
||||
|
||||
section = rva_to_section (debug_dir->VirtualAddress, dst_nt_header);
|
||||
if (section)
|
||||
{
|
||||
int size;
|
||||
|
||||
debug_entry = RVA_TO_PTR (debug_dir->VirtualAddress, section, p_outfile);
|
||||
size = debug_dir->Size / sizeof (IMAGE_DEBUG_DIRECTORY);
|
||||
|
||||
for (i = 0; i < size; i++, debug_entry++)
|
||||
{
|
||||
/* The debug data itself is normally not part of any
|
||||
section, but stored after all the raw section data. So
|
||||
let relocate_offset do the work. */
|
||||
ADJUST_OFFSET (debug_entry->PointerToRawData);
|
||||
ADJUST_IMPORT_RVA (debug_entry->AddressOfRawData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Update RVAs in import directory entries. */
|
||||
{
|
||||
PIMAGE_IMPORT_DESCRIPTOR imports;
|
||||
PIMAGE_THUNK_DATA import_thunks;
|
||||
|
||||
import_dir = &dst_nt_header->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
|
||||
import_dir->VirtualAddress += import_delta_rva;
|
||||
|
||||
section = rva_to_section (import_dir->VirtualAddress, dst_nt_header);
|
||||
imports = RVA_TO_PTR (import_dir->VirtualAddress, section, p_outfile);
|
||||
|
||||
for ( ; imports->Name != 0; imports++)
|
||||
{
|
||||
ADJUST_IMPORT_RVA (imports->OriginalFirstThunk);
|
||||
ADJUST_IMPORT_RVA (imports->FirstThunk);
|
||||
ADJUST_IMPORT_RVA (imports->Name);
|
||||
|
||||
for (import_thunks = RVA_TO_PTR (imports->OriginalFirstThunk, section, p_outfile);
|
||||
import_thunks->u1.Function != 0;
|
||||
import_thunks++)
|
||||
if ((import_thunks->u1.Ordinal >> 31) == 0)
|
||||
ADJUST_IMPORT_RVA (import_thunks->u1.Ordinal);
|
||||
|
||||
for (import_thunks = RVA_TO_PTR (imports->FirstThunk, section, p_outfile);
|
||||
import_thunks->u1.Function != 0;
|
||||
import_thunks++)
|
||||
if ((import_thunks->u1.Ordinal >> 31) == 0)
|
||||
ADJUST_IMPORT_RVA (import_thunks->u1.Ordinal);
|
||||
}
|
||||
|
||||
import_dir = &dst_nt_header->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT];
|
||||
import_dir->VirtualAddress += import_delta_rva;
|
||||
}
|
||||
|
||||
/* Fix up references to the import section. */
|
||||
printf ("Applying fixups to import references...\n");
|
||||
|
||||
{
|
||||
IMAGE_BASE_RELOCATION *relocs, *block, *start_block, *end_block;
|
||||
DWORD_PTR import_start = import_section->VirtualAddress + dst_nt_header->OptionalHeader.ImageBase;
|
||||
DWORD_PTR import_end = import_start + import_section->Misc.VirtualSize;
|
||||
DWORD_PTR len_import_relocs;
|
||||
DWORD_PTR len_remaining_relocs;
|
||||
int seen_high = 0;
|
||||
WORD * high_word;
|
||||
void * holder;
|
||||
|
||||
reloc_dir = &dst_nt_header->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC];
|
||||
reloc_section = rva_to_section (reloc_dir->VirtualAddress, dst_nt_header);
|
||||
relocs = RVA_TO_PTR (reloc_dir->VirtualAddress, reloc_section, p_outfile);
|
||||
|
||||
/* Move the base relocations for the import section, if there are
|
||||
any; the profiler needs to be able to patch RVAs in the import
|
||||
section itself. */
|
||||
for (block = relocs, start_block = 0;
|
||||
(DWORD_PTR) block - (DWORD_PTR) relocs < reloc_dir->Size;
|
||||
block = (void *)((DWORD_PTR) block + block->SizeOfBlock))
|
||||
{
|
||||
if (block->VirtualAddress >= import_section->VirtualAddress + import_section->Misc.VirtualSize)
|
||||
{
|
||||
end_block = block;
|
||||
break;
|
||||
}
|
||||
if (block->VirtualAddress >= import_section->VirtualAddress)
|
||||
{
|
||||
if (start_block == 0)
|
||||
start_block = block;
|
||||
block->VirtualAddress += import_delta_rva;
|
||||
}
|
||||
}
|
||||
if (start_block)
|
||||
{
|
||||
len_import_relocs = (DWORD_PTR) end_block - (DWORD_PTR) start_block;
|
||||
len_remaining_relocs = (DWORD_PTR) relocs + reloc_dir->Size - (DWORD_PTR) end_block;
|
||||
holder = malloc (len_import_relocs);
|
||||
if (holder == 0)
|
||||
abort ();
|
||||
memcpy (holder, start_block, len_import_relocs);
|
||||
memcpy (start_block, end_block, len_remaining_relocs);
|
||||
memcpy ((char *) start_block + len_remaining_relocs, holder, len_import_relocs);
|
||||
free (holder);
|
||||
}
|
||||
|
||||
/* Walk up the list of base relocations, checking for references
|
||||
to the old import section location, and patching them to
|
||||
reference the new location. */
|
||||
for (block = relocs;
|
||||
(DWORD_PTR) block - (DWORD_PTR) relocs < reloc_dir->Size;
|
||||
block = (void *)((DWORD_PTR) block + block->SizeOfBlock))
|
||||
{
|
||||
DWORD_PTR page_rva = block->VirtualAddress;
|
||||
DWORD_PTR page_offset;
|
||||
union {
|
||||
WORD word;
|
||||
DWORD_PTR dword;
|
||||
} * ploc;
|
||||
WORD *fixup;
|
||||
|
||||
section = rva_to_section (page_rva, dst_nt_header);
|
||||
/* Don't apply fixups to the blanked sections. */
|
||||
if (section->Name[0] == 'X')
|
||||
continue;
|
||||
|
||||
for (fixup = (WORD *) &block[1];
|
||||
(DWORD_PTR) fixup - (DWORD_PTR) block < block->SizeOfBlock;
|
||||
fixup++)
|
||||
{
|
||||
page_offset = (*fixup) & 0xfff;
|
||||
ploc = RVA_TO_PTR (page_rva + page_offset, section, p_outfile);
|
||||
|
||||
/* Unless our assumption is wrong, all low word fixups
|
||||
should immediately follow a high fixup. */
|
||||
if (seen_high && ((*fixup) >> 12) != IMAGE_REL_BASED_LOW)
|
||||
abort ();
|
||||
|
||||
switch ((*fixup) >> 12)
|
||||
{
|
||||
case IMAGE_REL_BASED_ABSOLUTE:
|
||||
break;
|
||||
case IMAGE_REL_BASED_HIGH:
|
||||
/* We must assume that high and low fixups occur in
|
||||
pairs, specifically a low fixup immediately follows a
|
||||
high fixup (normally separated by two bytes). We
|
||||
have to process the two fixups together, to find out
|
||||
the full pointer value and decide whether to apply
|
||||
the fixup. */
|
||||
seen_high = 1;
|
||||
high_word = &ploc->word;
|
||||
break;
|
||||
case IMAGE_REL_BASED_LOW:
|
||||
offset = (*high_word << 16) + ploc->word;
|
||||
if (offset >= import_start && offset < import_end)
|
||||
{
|
||||
(*high_word) += import_delta_rva >> 16;
|
||||
ploc->dword += import_delta_rva & 0xffff;
|
||||
}
|
||||
seen_high = 0;
|
||||
break;
|
||||
case IMAGE_REL_BASED_HIGHLOW:
|
||||
/* Docs imply two words in big-endian order, so perhaps
|
||||
this is only used on big-endian platforms, in which
|
||||
case the obvious code will work. */
|
||||
if (ploc->dword >= import_start && ploc->dword < import_end)
|
||||
ploc->dword += import_delta_rva;
|
||||
break;
|
||||
case IMAGE_REL_BASED_HIGHADJ:
|
||||
/* Docs don't say, but I guess this is the equivalent
|
||||
for little-endian platforms. */
|
||||
if (ploc->dword >= import_start && ploc->dword < import_end)
|
||||
ploc->dword += import_delta_rva;
|
||||
break;
|
||||
case IMAGE_REL_BASED_MIPS_JMPADDR:
|
||||
/* Don't know how to handle this; MIPS support has been
|
||||
dropped from NT4 anyway. */
|
||||
abort ();
|
||||
break;
|
||||
#ifdef IMAGE_REL_BASED_SECTION
|
||||
case IMAGE_REL_BASED_SECTION:
|
||||
case IMAGE_REL_BASED_REL32:
|
||||
/* Docs don't say what these values mean. */
|
||||
#endif
|
||||
default:
|
||||
abort ();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
main (int argc, char **argv)
|
||||
{
|
||||
PIMAGE_DOS_HEADER dos_header;
|
||||
PIMAGE_NT_HEADERS nt_header;
|
||||
file_data in_file, out_file;
|
||||
char out_filename[MAX_PATH], in_filename[MAX_PATH];
|
||||
|
||||
strcpy (in_filename, argv[1]);
|
||||
strcpy (out_filename, argv[2]);
|
||||
|
||||
printf ("Preparing %s for profile prepping\n", out_filename);
|
||||
|
||||
/* Open the original (dumped) executable file for reference. */
|
||||
if (!open_input_file (&in_file, in_filename))
|
||||
{
|
||||
printf ("Failed to open %s (%d)...bailing.\n",
|
||||
in_filename, GetLastError ());
|
||||
exit (1);
|
||||
}
|
||||
|
||||
/* Create a new image that can be prepped; we don't expect the size to
|
||||
change because we are only adding two new section table entries,
|
||||
which should fit in the alignment slop. */
|
||||
if (!open_output_file (&out_file, out_filename, in_file.size))
|
||||
{
|
||||
printf ("Failed to open %s (%d)...bailing.\n",
|
||||
out_filename, GetLastError ());
|
||||
exit (1);
|
||||
}
|
||||
|
||||
copy_executable_and_move_sections (&in_file, &out_file);
|
||||
|
||||
/* Patch up header fields; profiler is picky about this. */
|
||||
{
|
||||
HANDLE hImagehelp = LoadLibrary ("imagehlp.dll");
|
||||
DWORD_PTR headersum;
|
||||
DWORD_PTR checksum;
|
||||
|
||||
dos_header = (PIMAGE_DOS_HEADER) out_file.file_base;
|
||||
nt_header = (PIMAGE_NT_HEADERS) ((char *) dos_header + dos_header->e_lfanew);
|
||||
|
||||
nt_header->OptionalHeader.CheckSum = 0;
|
||||
// nt_header->FileHeader.TimeDateStamp = time (NULL);
|
||||
// dos_header->e_cp = size / 512;
|
||||
// nt_header->OptionalHeader.SizeOfImage = size;
|
||||
|
||||
pfnCheckSumMappedFile = (void *) GetProcAddress (hImagehelp, "CheckSumMappedFile");
|
||||
if (pfnCheckSumMappedFile)
|
||||
{
|
||||
// nt_header->FileHeader.TimeDateStamp = time (NULL);
|
||||
pfnCheckSumMappedFile (out_file.file_base,
|
||||
out_file.size,
|
||||
&headersum,
|
||||
&checksum);
|
||||
nt_header->OptionalHeader.CheckSum = checksum;
|
||||
}
|
||||
FreeLibrary (hImagehelp);
|
||||
}
|
||||
|
||||
close_file_data (&out_file);
|
||||
close_file_data (&in_file);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* eof */
|
|
@ -934,5 +934,70 @@ since all non-initial item lines must begin with whitespace."
|
|||
(insert (concat "\n" item1))
|
||||
(should-error (todo-edit-quit) :type 'user-error))))
|
||||
|
||||
(ert-deftest todo-test-item-insertion-with-priority-1 ()
|
||||
"Test inserting new item when point is not on a todo item.
|
||||
When point is on the empty line at the end of the todo items
|
||||
section, insertion with priority setting should succeed."
|
||||
(with-todo-test
|
||||
(todo-test--show 1)
|
||||
(goto-char (point-max))
|
||||
;; Now point should not be on a todo item.
|
||||
(should-not (todo-item-start))
|
||||
(let ((item "Point was on empty line at end of todo items section."))
|
||||
(todo-test--insert-item item 1)
|
||||
;; Move point to item that was just inserted.
|
||||
(goto-char (point-min))
|
||||
(re-search-forward (concat todo-date-string-start todo-date-pattern
|
||||
(regexp-quote todo-nondiary-end) " ")
|
||||
(pos-eol) t)
|
||||
(should (looking-at (regexp-quote item))))))
|
||||
|
||||
(ert-deftest todo-test-item-insertion-with-priority-2 ()
|
||||
"Test inserting new item when point is not on a todo item.
|
||||
When point is on the empty line at the end of the done items
|
||||
section, insertion with priority setting should succeed."
|
||||
(with-todo-test
|
||||
(todo-test--show 1)
|
||||
(goto-char (point-max))
|
||||
;; See comment about recentering in todo-test-raise-lower-priority.
|
||||
(set-window-buffer nil (current-buffer))
|
||||
(todo-toggle-view-done-items)
|
||||
(todo-next-item)
|
||||
(goto-char (point-max))
|
||||
;; Now point should be at end of done items section, so not be on a
|
||||
;; todo item.
|
||||
(should (todo-done-item-section-p))
|
||||
(should-not (todo-item-start))
|
||||
(let ((item "Point was on empty line at end of done items section."))
|
||||
(todo-test--insert-item item 1)
|
||||
;; Move point to item that was just inserted.
|
||||
(goto-char (point-min))
|
||||
(re-search-forward (concat todo-date-string-start todo-date-pattern
|
||||
(regexp-quote todo-nondiary-end) " ")
|
||||
(pos-eol) t)
|
||||
(should (looking-at (regexp-quote item))))))
|
||||
|
||||
(ert-deftest todo-test-item-insertion-with-priority-3 ()
|
||||
"Test inserting new item when point is not on a todo item.
|
||||
When point is on a done item, insertion with priority setting
|
||||
should succeed."
|
||||
(with-todo-test
|
||||
(todo-test--show 1)
|
||||
(goto-char (point-max))
|
||||
;; See comment about recentering in todo-test-raise-lower-priority.
|
||||
(set-window-buffer nil (current-buffer))
|
||||
(todo-toggle-view-done-items)
|
||||
(todo-next-item)
|
||||
;; Now point should be on first done item.
|
||||
(should (and (todo-item-start) (todo-done-item-section-p)))
|
||||
(let ((item "Point was on a done item."))
|
||||
(todo-test--insert-item item 1)
|
||||
;; Move point to item that was just inserted.
|
||||
(goto-char (point-min))
|
||||
(re-search-forward (concat todo-date-string-start todo-date-pattern
|
||||
(regexp-quote todo-nondiary-end) " ")
|
||||
(pos-eol) t)
|
||||
(should (looking-at (regexp-quote item))))))
|
||||
|
||||
(provide 'todo-mode-tests)
|
||||
;;; todo-mode-tests.el ends here
|
||||
|
|
|
@ -49,8 +49,9 @@
|
|||
:port port
|
||||
:full-name "tester"
|
||||
:nick "tester")
|
||||
(should (memq 'erc-match-message
|
||||
(memq 'erc-add-timestamp erc-insert-modify-hook)))
|
||||
;; Module `timestamp' follows `match' in insertion hooks.
|
||||
(should (memq 'erc-add-timestamp
|
||||
(memq 'erc-match-message erc-insert-modify-hook)))
|
||||
;; The "match type" is `current-nick'.
|
||||
(funcall expect 5 "tester")
|
||||
(should (eq (get-text-property (1- (point)) 'font-lock-face)
|
||||
|
@ -60,6 +61,7 @@
|
|||
;; some non-nil invisibility property spans the entire message.
|
||||
(ert-deftest erc-scenarios-match--stamp-left-fools-invisible ()
|
||||
:tags '(:expensive-test)
|
||||
(ert-skip "WIP: fix included in bug#64301")
|
||||
(erc-scenarios-common-with-cleanup
|
||||
((erc-scenarios-common-dialog "join/legacy")
|
||||
(dumb-server (erc-d-run "localhost" t 'foonet))
|
||||
|
@ -84,8 +86,9 @@
|
|||
:full-name "tester"
|
||||
:password "changeme"
|
||||
:nick "tester")
|
||||
(should (memq 'erc-match-message
|
||||
(memq 'erc-add-timestamp erc-insert-modify-hook)))
|
||||
;; Module `timestamp' follows `match' in insertion hooks.
|
||||
(should (memq 'erc-add-timestamp
|
||||
(memq 'erc-match-message erc-insert-modify-hook)))
|
||||
(funcall expect 5 "This server is in debug mode")))
|
||||
|
||||
(ert-info ("Ensure lines featuring \"bob\" are invisible")
|
||||
|
|
|
@ -1851,8 +1851,8 @@
|
|||
'( :erc-insert-modify-hook (erc-controls-highlight ; 0
|
||||
erc-button-add-buttons ; 30
|
||||
erc-fill ; 40
|
||||
erc-add-timestamp ; 50
|
||||
erc-match-message) ; 60
|
||||
erc-match-message ; 50
|
||||
erc-add-timestamp) ; 60
|
||||
|
||||
:erc-send-modify-hook ( erc-controls-highlight ; 0
|
||||
erc-button-add-buttons ; 30
|
||||
|
|
|
@ -81,6 +81,7 @@
|
|||
(defvar dired-copy-dereference)
|
||||
|
||||
;; Declared in Emacs 30.
|
||||
(defvar remote-file-name-access-timeout)
|
||||
(defvar remote-file-name-inhibit-delete-by-moving-to-trash)
|
||||
|
||||
;; `ert-resource-file' was introduced in Emacs 28.1.
|
||||
|
@ -3654,6 +3655,18 @@ This tests also `access-file', `file-readable-p',
|
|||
attr)
|
||||
(unwind-protect
|
||||
(progn
|
||||
(write-region "foo" nil tmp-name1)
|
||||
;; `access-file' returns nil in case of success.
|
||||
(should-not (access-file tmp-name1 "error"))
|
||||
;; `access-file' could use a timeout.
|
||||
(let ((remote-file-name-access-timeout 1))
|
||||
(cl-letf (((symbol-function #'file-exists-p)
|
||||
(lambda (_filename) (sleep-for 5))))
|
||||
(should-error
|
||||
(access-file tmp-name1 "error")
|
||||
:type 'file-error)))
|
||||
(delete-file tmp-name1)
|
||||
|
||||
;; A sticky bit could damage the `file-ownership-preserved-p' test.
|
||||
(when
|
||||
(and test-file-ownership-preserved-p
|
||||
|
|
|
@ -24,3 +24,58 @@ Name: cperl-indents1
|
|||
"";
|
||||
}
|
||||
=-=-=
|
||||
|
||||
Name: cperl-try-catch-finally
|
||||
|
||||
=-=
|
||||
{
|
||||
try {
|
||||
call_a_function();
|
||||
}
|
||||
catch ($e) {
|
||||
warn "Unable to call; $e";
|
||||
}
|
||||
finally {
|
||||
print "Finished\n";
|
||||
}
|
||||
}
|
||||
=-=-=
|
||||
|
||||
Name: cperl-defer
|
||||
|
||||
=-=
|
||||
use feature 'defer';
|
||||
|
||||
{
|
||||
say "This happens first";
|
||||
defer {
|
||||
say "This happens last";
|
||||
}
|
||||
|
||||
say "And this happens inbetween";
|
||||
}
|
||||
=-=-=
|
||||
|
||||
Name: cperl-feature-class
|
||||
|
||||
=-=
|
||||
use 5.038;
|
||||
use feature "class";
|
||||
no warnings "experimental";
|
||||
|
||||
class A {
|
||||
}
|
||||
|
||||
class C
|
||||
: isa(A)
|
||||
{
|
||||
method with_sig_and_attr
|
||||
: lvalue
|
||||
($top,$down)
|
||||
{
|
||||
return $top-$down;
|
||||
}
|
||||
}
|
||||
|
||||
say "done!";
|
||||
=-=-=
|
||||
|
|
|
@ -169,4 +169,29 @@ package Erdős::Number;
|
|||
}
|
||||
}
|
||||
|
||||
=head1 And now, for something completely different
|
||||
|
||||
Perl 5.38 supports classes with the same scope weirdness as packages.
|
||||
As long as this is experimental, CPAN tools don't play well with this,
|
||||
so some weird constructs are recommended to authors of CPAN modules.
|
||||
|
||||
=cut
|
||||
|
||||
package Class::Class;
|
||||
|
||||
our $VERSION = 0.01;
|
||||
|
||||
class Class::Class 0.01 {
|
||||
method init ($with,$signature) {
|
||||
...;
|
||||
}
|
||||
|
||||
class Class::Inner :isa(Class::Class);
|
||||
# This class comes without a block, so takes over until the rest
|
||||
# of the containing block.
|
||||
method init_again (@with_parameters) {
|
||||
...;
|
||||
}
|
||||
}
|
||||
|
||||
1;
|
||||
|
|
19
test/lisp/progmodes/cperl-mode-resources/perl-class.pl
Normal file
19
test/lisp/progmodes/cperl-mode-resources/perl-class.pl
Normal file
|
@ -0,0 +1,19 @@
|
|||
use 5.038;
|
||||
use feature 'class';
|
||||
no warnings 'experimental';
|
||||
|
||||
class A {
|
||||
}
|
||||
|
||||
class C
|
||||
: isa(A)
|
||||
{
|
||||
method with_sig_and_attr
|
||||
: lvalue
|
||||
($top,$down)
|
||||
{
|
||||
return $top-$down;
|
||||
}
|
||||
}
|
||||
|
||||
say "done!";
|
|
@ -213,6 +213,33 @@ attributes, prototypes and signatures."
|
|||
'font-lock-variable-name-face)))
|
||||
(goto-char end-of-sub))))))
|
||||
|
||||
(ert-deftest cperl-test-fontify-class ()
|
||||
"Test fontification of the various elements in a Perl class."
|
||||
(skip-unless (eq cperl-test-mode #'cperl-mode))
|
||||
(let ((file (ert-resource-file "perl-class.pl")))
|
||||
(with-temp-buffer
|
||||
(insert-file-contents file)
|
||||
(goto-char (point-min))
|
||||
(funcall cperl-test-mode)
|
||||
(font-lock-ensure)
|
||||
|
||||
;; The class name
|
||||
(while (search-forward-regexp "class " nil t)
|
||||
(should (equal (get-text-property (point) 'face)
|
||||
'font-lock-function-name-face)))
|
||||
;; The attributes (class and method)
|
||||
(while (search-forward-regexp " : " nil t)
|
||||
(should (equal (get-text-property (point) 'face)
|
||||
'font-lock-constant-face)))
|
||||
;; The signature
|
||||
(goto-char (point-min))
|
||||
(search-forward-regexp "\\(\$top\\),\\(\$down\\)")
|
||||
(should (equal (get-text-property (match-beginning 1) 'face)
|
||||
'font-lock-variable-name-face))
|
||||
(should (equal (get-text-property (match-beginning 1) 'face)
|
||||
'font-lock-variable-name-face))
|
||||
)))
|
||||
|
||||
(ert-deftest cperl-test-fontify-special-variables ()
|
||||
"Test fontification of variables like $^T or ${^ENCODING}.
|
||||
These can occur as \"local\" aliases."
|
||||
|
@ -408,7 +435,7 @@ the whole string."
|
|||
valid invalid)))
|
||||
|
||||
(ert-deftest cperl-test-package-regexp ()
|
||||
"Tests the regular expression of Perl package names with versions.
|
||||
"Tests the regular expression of Perl package and class names with versions.
|
||||
Also includes valid cases with whitespace in strange places."
|
||||
(skip-unless (eq cperl-test-mode #'cperl-mode))
|
||||
(let ((valid
|
||||
|
@ -416,13 +443,13 @@ Also includes valid cases with whitespace in strange places."
|
|||
"package Foo::Bar"
|
||||
"package Foo::Bar v1.2.3"
|
||||
"package Foo::Bar::Baz 1.1"
|
||||
"class O3D::Sphere" ; since Perl 5.38
|
||||
"package \nFoo::Bar\n 1.00"))
|
||||
(invalid
|
||||
'("package Foo;" ; semicolon must not be included
|
||||
"package Foo 1.1 {" ; nor the opening brace
|
||||
"packageFoo" ; not a package declaration
|
||||
"package Foo1.1" ; invalid package name
|
||||
"class O3D::Sphere"))) ; class not yet supported
|
||||
"package Foo1.1"))) ; invalid package name
|
||||
(cperl-test--validate-regexp (rx (eval cperl--package-rx))
|
||||
valid invalid)))
|
||||
|
||||
|
@ -784,7 +811,9 @@ created by CPerl mode, so skip it for Perl mode."
|
|||
"lexical"
|
||||
"Versioned::Block::signatured"
|
||||
"Package::in_package_again"
|
||||
"Erdős::Number::erdős_number")))
|
||||
"Erdős::Number::erdős_number"
|
||||
"Class::Class::init"
|
||||
"Class::Inner::init_again")))
|
||||
(dolist (sub expected)
|
||||
(should (assoc-string sub index)))))))
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue