From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.org!.POSTED!not-for-mail From: Alan Mackenzie Newsgroups: gmane.emacs.devel Subject: Re: [Emacs-diffs] master 5b5f441: read_key_sequence: correct the handling of raw_keybuf in recursive calls Date: Tue, 28 Nov 2017 18:03:04 +0000 Message-ID: <20171128180304.GA14868@ACM> References: <20171120195918.GB3917@ACM> <20171120203004.GC3917@ACM> <20171122192522.GA4288@ACM> <20171122210426.GB4288@ACM> <20171123181017.GA5167@ACM> NNTP-Posting-Host: blaine.gmane.org Mime-Version: 1.0 Content-Type: text/plain; charset=us-ascii X-Trace: blaine.gmane.org 1511892512 3530 195.159.176.226 (28 Nov 2017 18:08:32 GMT) X-Complaints-To: usenet@blaine.gmane.org NNTP-Posting-Date: Tue, 28 Nov 2017 18:08:32 +0000 (UTC) User-Agent: Mutt/1.7.2 (2016-11-26) Cc: Stefan Monnier , emacs-devel@gnu.org To: Eli Zaretskii Original-X-From: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Tue Nov 28 19:08:26 2017 Return-path: Envelope-to: ged-emacs-devel@m.gmane.org Original-Received: from lists.gnu.org ([208.118.235.17]) by blaine.gmane.org with esmtp (Exim 4.84_2) (envelope-from ) id 1eJkIv-0000CG-95 for ged-emacs-devel@m.gmane.org; Tue, 28 Nov 2017 19:08:21 +0100 Original-Received: from localhost ([::1]:39442 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1eJkJ2-0005AG-EW for ged-emacs-devel@m.gmane.org; Tue, 28 Nov 2017 13:08:28 -0500 Original-Received: from eggs.gnu.org ([2001:4830:134:3::10]:40688) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1eJkIl-00055h-5v for emacs-devel@gnu.org; Tue, 28 Nov 2017 13:08:14 -0500 Original-Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1eJkIi-0001OZ-Bt for emacs-devel@gnu.org; Tue, 28 Nov 2017 13:08:11 -0500 Original-Received: from colin.muc.de ([193.149.48.1]:10530 helo=mail.muc.de) by eggs.gnu.org with smtp (Exim 4.71) (envelope-from ) id 1eJkIh-0001O0-UT for emacs-devel@gnu.org; Tue, 28 Nov 2017 13:08:08 -0500 Original-Received: (qmail 16789 invoked by uid 3782); 28 Nov 2017 18:08:05 -0000 Original-Received: from acm.muc.de (p548C73AE.dip0.t-ipconnect.de [84.140.115.174]) by colin.muc.de (tmda-ofmipd) with ESMTP; Tue, 28 Nov 2017 19:08:04 +0100 Original-Received: (qmail 17623 invoked by uid 1000); 28 Nov 2017 18:03:04 -0000 Content-Disposition: inline In-Reply-To: X-Delivery-Agent: TMDA/1.1.12 (Macallan) X-Primary-Address: acm@muc.de X-detected-operating-system: by eggs.gnu.org: FreeBSD 9.x [fuzzy] X-Received-From: 193.149.48.1 X-BeenThere: emacs-devel@gnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: "Emacs development discussions." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Original-Sender: "Emacs-devel" Xref: news.gmane.org gmane.emacs.devel:220507 Archived-At: Hello, Eli. On Thu, Nov 23, 2017 at 13:46:44 -0500, Stefan Monnier wrote: > >> Hmm... that might work, but I still can't imagine what comment I could > >> write next to the code to explain/prove that it's safe. > > It's as safe as the original, since the only buffer space used is the > > original raw_keybuf, indexed by raw_keybuf_count. The only change is to > > initialise raw_keybuf_count in different places, to allow the recursive > > call to read_key_sequence not to overwrite the current buffer contents. > I had misunderstood. Yes, that does look safe. No idea if it's correct > enough in practice, but it looks much better than what we have. > Thanks. What do you say? I would like to revert the previous commit that fixed this and install the one below. I don't know if you've been following this conversation, but Stefan was worried (and I agree with him) that the existing fix is dangerous, because read_key_sequence sets global variables to point into a buffer on the stack. If there's an unfortunate longjmp or unwind_protect, or something similar, the global variables might be left pointing at stale stack space. Neither of us can say for sure whether the actual code is safe or dangerous. My newer approach is to use only the static raw_keybuf as the buffer, but to be more discerning about where raw_keybuf_count gets initialised to zero. Instead of doing this at the start of read_key_sequence, I would do it in: command_loop_1 read_key_sequence_vs Fx_popup_menu . Fx_popup_menu will be split into the function itself (which initialises raw_keybuf_count) and x_popup_menu_1, this latter function not touching raw_keybuf_count, but otherwise containing the bulk of what was Fx_popup_menu. Stefan is happy enough about this new patch. What do you think? For some reason, I get lots of s in the listed "translation" strings, which I don't get with the current fix. This may have to be fixed somehow. Here's the patch (generated by $ git diff 5b5f441ff^ \ src/{keyboard,menu}.[ch]). diff --git a/src/keyboard.c b/src/keyboard.c index 6b7a6bfa74..a751861081 100644 --- a/src/keyboard.c +++ b/src/keyboard.c @@ -43,6 +43,7 @@ along with GNU Emacs. If not, see . */ #include "systime.h" #include "atimer.h" #include "process.h" +#include "menu.h" #include #ifdef HAVE_PTHREAD @@ -1365,6 +1366,7 @@ command_loop_1 (void) Vthis_command_keys_shift_translated = Qnil; /* Read next key sequence; i gets its length. */ + raw_keybuf_count = 0; i = read_key_sequence (keybuf, ARRAYELTS (keybuf), Qnil, 0, 1, 1, 0); @@ -8453,7 +8455,8 @@ read_char_x_menu_prompt (Lisp_Object map, /* Display the menu and get the selection. */ Lisp_Object value; - value = Fx_popup_menu (prev_event, get_keymap (map, 0, 1)); + /* value = Fx_popup_menu (prev_event, get_keymap (map, 0, 1)); */ + value = x_popup_menu_1 (prev_event, get_keymap (map, 0, 1)); if (CONSP (value)) { Lisp_Object tem; @@ -8863,6 +8866,11 @@ test_undefined (Lisp_Object binding) && EQ (Fcommand_remapping (binding, Qnil, Qnil), Qundefined))); } +void init_raw_keybuf_count (void) +{ + raw_keybuf_count = 0; +} + /* Read a sequence of keys that ends with a non prefix character, storing it in KEYBUF, a buffer of size BUFSIZE. Prompt with PROMPT. @@ -8973,7 +8981,11 @@ read_key_sequence (Lisp_Object *keybuf, int bufsize, Lisp_Object prompt, /* List of events for which a fake prefix key has been generated. */ Lisp_Object fake_prefixed_keys = Qnil; - raw_keybuf_count = 0; + /* raw_keybuf_count is now initialized in (most of) the callers of + read_key_sequence. This is so that in a recursive call (for + mouse menus) a spurious initialization doesn't erase the contents + of raw_keybuf created by the outer call. */ + /* raw_keybuf_count = 0; */ last_nonmenu_event = Qnil; @@ -9346,7 +9358,8 @@ read_key_sequence (Lisp_Object *keybuf, int bufsize, Lisp_Object prompt, && BUFFERP (XWINDOW (window)->contents) && XBUFFER (XWINDOW (window)->contents) != current_buffer) { - ASET (raw_keybuf, raw_keybuf_count, key); + GROW_RAW_KEYBUF; + ASET (raw_keybuf, raw_keybuf_count, key); raw_keybuf_count++; keybuf[t] = key; mock_input = t + 1; @@ -9840,6 +9853,7 @@ read_key_sequence_vs (Lisp_Object prompt, Lisp_Object continue_echo, cancel_hourglass (); #endif + raw_keybuf_count = 0; i = read_key_sequence (keybuf, ARRAYELTS (keybuf), prompt, ! NILP (dont_downcase_last), ! NILP (can_return_switch_frame), 0, 0); diff --git a/src/keyboard.h b/src/keyboard.h index 662d8e4a4f..c232e778e2 100644 --- a/src/keyboard.h +++ b/src/keyboard.h @@ -438,6 +438,7 @@ extern unsigned int timers_run; extern bool menu_separator_name_p (const char *); extern bool parse_menu_item (Lisp_Object, int); +extern void init_raw_keybuf_count (void); extern KBOARD *allocate_kboard (Lisp_Object); extern void delete_kboard (KBOARD *); extern void not_single_kboard_state (KBOARD *); diff --git a/src/menu.c b/src/menu.c index d569b4b29b..b40c2c04ce 100644 --- a/src/menu.c +++ b/src/menu.c @@ -1112,51 +1112,8 @@ into menu items. */) return Qnil; } - -DEFUN ("x-popup-menu", Fx_popup_menu, Sx_popup_menu, 2, 2, 0, - doc: /* Pop up a deck-of-cards menu and return user's selection. -POSITION is a position specification. This is either a mouse button event -or a list ((XOFFSET YOFFSET) WINDOW) -where XOFFSET and YOFFSET are positions in pixels from the top left -corner of WINDOW. (WINDOW may be a window or a frame object.) -This controls the position of the top left of the menu as a whole. -If POSITION is t, it means to use the current mouse position. - -MENU is a specifier for a menu. For the simplest case, MENU is a keymap. -The menu items come from key bindings that have a menu string as well as -a definition; actually, the "definition" in such a key binding looks like -\(STRING . REAL-DEFINITION). To give the menu a title, put a string into -the keymap as a top-level element. - -If REAL-DEFINITION is nil, that puts a nonselectable string in the menu. -Otherwise, REAL-DEFINITION should be a valid key binding definition. - -You can also use a list of keymaps as MENU. - Then each keymap makes a separate pane. - -When MENU is a keymap or a list of keymaps, the return value is the -list of events corresponding to the user's choice. Note that -`x-popup-menu' does not actually execute the command bound to that -sequence of events. - -Alternatively, you can specify a menu of multiple panes - with a list of the form (TITLE PANE1 PANE2...), -where each pane is a list of form (TITLE ITEM1 ITEM2...). -Each ITEM is normally a cons cell (STRING . VALUE); -but a string can appear as an item--that makes a nonselectable line -in the menu. -With this form of menu, the return value is VALUE from the chosen item. - -If POSITION is nil, don't display the menu at all, just precalculate the -cached information about equivalent key sequences. - -If the user gets rid of the menu without making a valid choice, for -instance by clicking the mouse away from a valid choice or by typing -keyboard input, then this normally results in a quit and -`x-popup-menu' does not return. But if POSITION is a mouse button -event (indicating that the user invoked the menu with the mouse) then -no quit occurs and `x-popup-menu' returns nil. */) - (Lisp_Object position, Lisp_Object menu) +Lisp_Object +x_popup_menu_1 (Lisp_Object position, Lisp_Object menu) { Lisp_Object keymap, tem, tem2; int xpos = 0, ypos = 0; @@ -1443,6 +1400,55 @@ no quit occurs and `x-popup-menu' returns nil. */) return selection; } +DEFUN ("x-popup-menu", Fx_popup_menu, Sx_popup_menu, 2, 2, 0, + doc: /* Pop up a deck-of-cards menu and return user's selection. +POSITION is a position specification. This is either a mouse button event +or a list ((XOFFSET YOFFSET) WINDOW) +where XOFFSET and YOFFSET are positions in pixels from the top left +corner of WINDOW. (WINDOW may be a window or a frame object.) +This controls the position of the top left of the menu as a whole. +If POSITION is t, it means to use the current mouse position. + +MENU is a specifier for a menu. For the simplest case, MENU is a keymap. +The menu items come from key bindings that have a menu string as well as +a definition; actually, the "definition" in such a key binding looks like +\(STRING . REAL-DEFINITION). To give the menu a title, put a string into +the keymap as a top-level element. + +If REAL-DEFINITION is nil, that puts a nonselectable string in the menu. +Otherwise, REAL-DEFINITION should be a valid key binding definition. + +You can also use a list of keymaps as MENU. + Then each keymap makes a separate pane. + +When MENU is a keymap or a list of keymaps, the return value is the +list of events corresponding to the user's choice. Note that +`x-popup-menu' does not actually execute the command bound to that +sequence of events. + +Alternatively, you can specify a menu of multiple panes + with a list of the form (TITLE PANE1 PANE2...), +where each pane is a list of form (TITLE ITEM1 ITEM2...). +Each ITEM is normally a cons cell (STRING . VALUE); +but a string can appear as an item--that makes a nonselectable line +in the menu. +With this form of menu, the return value is VALUE from the chosen item. + +If POSITION is nil, don't display the menu at all, just precalculate the +cached information about equivalent key sequences. + +If the user gets rid of the menu without making a valid choice, for +instance by clicking the mouse away from a valid choice or by typing +keyboard input, then this normally results in a quit and +`x-popup-menu' does not return. But if POSITION is a mouse button +event (indicating that the user invoked the menu with the mouse) then +no quit occurs and `x-popup-menu' returns nil. */) + (Lisp_Object position, Lisp_Object menu) +{ + init_raw_keybuf_count (); + return x_popup_menu_1 (position, menu); +} + /* If F's terminal is not capable of displaying a popup dialog, emulate it with a menu. */ diff --git a/src/menu.h b/src/menu.h index 1469cc87d9..3335616338 100644 --- a/src/menu.h +++ b/src/menu.h @@ -60,4 +60,5 @@ extern Lisp_Object ns_menu_show (struct frame *, int, int, int, extern Lisp_Object tty_menu_show (struct frame *, int, int, int, Lisp_Object, const char **); extern ptrdiff_t menu_item_width (const unsigned char *); +extern Lisp_Object x_popup_menu_1 (Lisp_Object position, Lisp_Object menu); #endif /* MENU_H */ > Stefan -- Alan Mackenzie (Nuremberg, Germany).