From af519a634878dd8e72f8276d82601b6562029107 Mon Sep 17 00:00:00 2001 From: Noam Postavsky Date: Tue, 25 Feb 2020 20:09:00 -0500 Subject: Define libgnutls-version properly * src/gnutls.c (syms_of_gnutls) : Define with DEFVAR_LISP and add docstring, so that this variable will accessible by help facilities. --- src/gnutls.c | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/gnutls.c b/src/gnutls.c index 31fcd37c0a6..70176c41cdd 100644 --- a/src/gnutls.c +++ b/src/gnutls.c @@ -2834,16 +2834,21 @@ Any GnuTLS extension with ID up to 100 void syms_of_gnutls (void) { - DEFSYM (Qlibgnutls_version, "libgnutls-version"); - Fset (Qlibgnutls_version, + DEFVAR_LISP ("libgnutls-version", Vlibgnutls_version, + doc: /* The version of libgnutls that Emacs was compiled with. +The version number is encoded as an integer with the major version in +the ten thousands place, minor version in the hundreds, and patch +level in the ones. For builds without libgnutls, the value is -1. */); + Vlibgnutls_version = make_fixnum #ifdef HAVE_GNUTLS - make_fixnum (GNUTLS_VERSION_MAJOR * 10000 - + GNUTLS_VERSION_MINOR * 100 - + GNUTLS_VERSION_PATCH) + (GNUTLS_VERSION_MAJOR * 10000 + + GNUTLS_VERSION_MINOR * 100 + + GNUTLS_VERSION_PATCH) #else - make_fixnum (-1) + (-1) #endif - ); + ; + #ifdef HAVE_GNUTLS gnutls_global_initialized = 0; PDUMPER_IGNORE (gnutls_global_initialized); -- cgit v1.3 From 999d75c0c1ce3dd19dfe376c0063951f9ba45900 Mon Sep 17 00:00:00 2001 From: Robert Pluim Date: Wed, 26 Feb 2020 19:18:54 +0100 Subject: Range-check width passed to define-fringe-bitmap This prevents a crash when attempting to create a zero-width bitmap. * src/fringe.c (Fdefine_fringe_bitmap): Check value of width, signal an error if outside documented range (Bug#39662). --- src/fringe.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/fringe.c b/src/fringe.c index 97aad843c2e..2a46e3c34f2 100644 --- a/src/fringe.c +++ b/src/fringe.c @@ -1500,7 +1500,8 @@ DEFUN ("define-fringe-bitmap", Fdefine_fringe_bitmap, Sdefine_fringe_bitmap, BITMAP is a symbol identifying the new fringe bitmap. BITS is either a string or a vector of integers. HEIGHT is height of bitmap. If HEIGHT is nil, use length of BITS. -WIDTH must be an integer between 1 and 16, or nil which defaults to 8. +WIDTH must be an integer from 1 to 16, or nil which defaults to 8. An +error is signaled if WIDTH is outside this range. Optional fifth arg ALIGN may be one of `top', `center', or `bottom', indicating the positioning of the bitmap relative to the rows where it is used; the default is to center the bitmap. Fifth arg may also be a @@ -1535,7 +1536,9 @@ If BITMAP already exists, the existing definition is replaced. */) else { CHECK_FIXNUM (width); - fb.width = max (0, min (XFIXNUM (width), 255)); + fb.width = max (1, min (XFIXNUM (width), 16)); + if (fb.width != XFIXNUM (width)) + args_out_of_range (width, build_string ("Width must be from 1 to 16")); } fb.period = 0; -- cgit v1.3 From 5cca73dd82cc18322c88721f311f8e6a081849fa Mon Sep 17 00:00:00 2001 From: Paul Eggert Date: Fri, 28 Feb 2020 12:58:28 -0800 Subject: * src/timefns.c (time_arith): Omit incorrect comment. --- src/timefns.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'src') diff --git a/src/timefns.c b/src/timefns.c index 46f9193d6a1..a08d3b816ff 100644 --- a/src/timefns.c +++ b/src/timefns.c @@ -1033,9 +1033,7 @@ lispint_arith (Lisp_Object a, Lisp_Object b, bool subtract) } /* Given Lisp operands A and B, add their values, and return the - result as a Lisp timestamp that is in (TICKS . HZ) form if either A - or B are in that form or are floats, (HI LO US PS) form otherwise. - Subtract instead of adding if SUBTRACT. */ + result as a Lisp timestamp. Subtract instead of adding if SUBTRACT. */ static Lisp_Object time_arith (Lisp_Object a, Lisp_Object b, bool subtract) { -- cgit v1.3 From fe1a447d52f548441d19af580ed11ef56d4459d2 Mon Sep 17 00:00:00 2001 From: Robert Pluim Date: Fri, 24 Jan 2020 14:11:44 +0100 Subject: Don't attempt to cache glyph metrics for FONT_INVALID_CODE This was causing massive slowdown in redisplay when eg #xfe0f (VARIATION SELECTOR-16) was present, as the cache ended up very large, unused, and being recreated on every call to font_fill_lglyph_metrics (Bug#39133). * src/composite.c (fill_gstring_body): Hoist FONT_OBJECT_P check out of loop. Calculate glyph code and check for FONT_INVALID_CODE before calling font_fill_lglyph_metrics. Pass glyph code to it. * src/font.c (font_fill_lglyph_metrics): Add code parameter, move glyph code calculation up the call stack into fill_gstring_body. * src/font.h: Adjust font_fill_lglyph_metrics prototype. --- src/composite.c | 18 ++++++++++++++---- src/font.c | 4 +--- src/font.h | 2 +- 3 files changed, 16 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/composite.c b/src/composite.c index 53e6930b5f2..364d5c9316e 100644 --- a/src/composite.c +++ b/src/composite.c @@ -818,6 +818,11 @@ fill_gstring_body (Lisp_Object gstring) Lisp_Object header = AREF (gstring, 0); ptrdiff_t len = LGSTRING_CHAR_LEN (gstring); ptrdiff_t i; + struct font *font = NULL; + unsigned int code; + + if (FONT_OBJECT_P (font_object)) + font = XFONT_OBJECT (font_object); for (i = 0; i < len; i++) { @@ -832,10 +837,15 @@ fill_gstring_body (Lisp_Object gstring) LGLYPH_SET_FROM (g, i); LGLYPH_SET_TO (g, i); LGLYPH_SET_CHAR (g, c); - if (FONT_OBJECT_P (font_object)) - { - font_fill_lglyph_metrics (g, font_object); - } + + if (font != NULL) + code = font->driver->encode_char (font, LGLYPH_CHAR (g)); + else + code = FONT_INVALID_CODE; + if (code != FONT_INVALID_CODE) + { + font_fill_lglyph_metrics (g, font, code); + } else { int width = XFIXNAT (CHAR_TABLE_REF (Vchar_width_table, c)); diff --git a/src/font.c b/src/font.c index 2b90903c909..39ec1b3562a 100644 --- a/src/font.c +++ b/src/font.c @@ -4416,10 +4416,8 @@ DEFUN ("clear-font-cache", Fclear_font_cache, Sclear_font_cache, 0, 0, 0, void -font_fill_lglyph_metrics (Lisp_Object glyph, Lisp_Object font_object) +font_fill_lglyph_metrics (Lisp_Object glyph, struct font *font, unsigned int code) { - struct font *font = XFONT_OBJECT (font_object); - unsigned code = font->driver->encode_char (font, LGLYPH_CHAR (glyph)); struct font_metrics metrics; LGLYPH_SET_CODE (glyph, code); diff --git a/src/font.h b/src/font.h index 633d92709c5..6f4792afe55 100644 --- a/src/font.h +++ b/src/font.h @@ -886,7 +886,7 @@ extern Lisp_Object font_update_drivers (struct frame *f, Lisp_Object list); extern Lisp_Object font_range (ptrdiff_t, ptrdiff_t, ptrdiff_t *, struct window *, struct face *, Lisp_Object); -extern void font_fill_lglyph_metrics (Lisp_Object, Lisp_Object); +extern void font_fill_lglyph_metrics (Lisp_Object, struct font *, unsigned int); extern Lisp_Object font_put_extra (Lisp_Object font, Lisp_Object prop, Lisp_Object val); -- cgit v1.3 From a4e4510ccd92da8ca17743c7dab9b32fc9d850e7 Mon Sep 17 00:00:00 2001 From: Eli Zaretskii Date: Tue, 3 Mar 2020 18:40:28 +0200 Subject: Fix handling MS-Windows keyboard input above the BMP * src/w32term.c (w32_read_socket): If we get a WM_UNICHAR message with a surrogate codepoint, assemble the corresponding character code above the BMP from its UTF-16 encoding, communicated in two consecutive WM_UNICHAR messages. --- src/w32term.c | 42 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/w32term.c b/src/w32term.c index 4eb5045fc5b..f515f5604d6 100644 --- a/src/w32term.c +++ b/src/w32term.c @@ -4701,6 +4701,10 @@ static short temp_buffer[100]; /* Temporarily store lead byte of DBCS input sequences. */ static char dbcs_lead = 0; +/* Temporarily store pending UTF-16 high surrogate unit and the modifiers. */ +static unsigned short utf16_high; +static DWORD utf16_high_modifiers; + /** mouse_or_wdesc_frame: When not dropping and the mouse was grabbed for DPYINFO, return the frame where the mouse was seen last. If @@ -4912,9 +4916,45 @@ w32_read_socket (struct terminal *terminal, XSETFRAME (inev.frame_or_window, f); inev.timestamp = msg.msg.time; + if (utf16_high + && (msg.msg.message != WM_UNICHAR + || UTF_16_HIGH_SURROGATE_P (msg.msg.wParam))) + { + /* Flush the pending high surrogate if the low one + isn't coming. (This should never happen, but I + have paranoia about this stuff.) */ + struct input_event inev1; + inev1.modifiers = utf16_high_modifiers; + inev1.code = utf16_high; + inev1.timestamp = inev.timestamp; + inev1.arg = Qnil; + kbd_buffer_store_event_hold (&inev1, hold_quit); + utf16_high = 0; + utf16_high_modifiers = 0; + } + if (msg.msg.message == WM_UNICHAR) { - inev.code = msg.msg.wParam; + /* Handle UTF-16 encoded codepoint above the BMP. + This is needed to support Emoji input from input + panel popped up by "Win+." shortcut. */ + if (UTF_16_HIGH_SURROGATE_P (msg.msg.wParam)) + { + utf16_high = msg.msg.wParam; + utf16_high_modifiers = inev.modifiers; + inev.kind = NO_EVENT; + break; + } + else if (UTF_16_LOW_SURROGATE_P (msg.msg.wParam) + && utf16_high) + { + inev.code = surrogates_to_codepoint (msg.msg.wParam, + utf16_high); + utf16_high = 0; + utf16_high_modifiers = 0; + } + else + inev.code = msg.msg.wParam; } else if (msg.msg.wParam < 256) { -- cgit v1.3 From add0610ec9327c15ee933f571731401212328810 Mon Sep 17 00:00:00 2001 From: Andreas Schwab Date: Wed, 15 Jan 2020 10:02:10 +0100 Subject: Fix implicit declaration of getenv and atol * src/gtkutil.c: Include . --- src/gtkutil.c | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/gtkutil.c b/src/gtkutil.c index 6308c38f164..5e7cf3d2114 100644 --- a/src/gtkutil.c +++ b/src/gtkutil.c @@ -22,6 +22,7 @@ along with GNU Emacs. If not, see . */ #ifdef USE_GTK #include #include +#include #include -- cgit v1.3 From d1bbd32dba392f2fb4548d892354e78ff8df4451 Mon Sep 17 00:00:00 2001 From: Alan Third Date: Wed, 4 Mar 2020 20:51:40 +0000 Subject: Fix more NS_DRAW_TO_BUFFER #ifdefs (bug#39883) * src/nsterm.m (ns_update_end): Make sure the frame is updated after drawing. (ns_focus): (ns_unfocus): Should be checking on NS_DRAW_TO_BUFFER rather than if it's Cocoa or GNUstep. --- src/nsterm.m | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/nsterm.m b/src/nsterm.m index 8e256149220..851a5617d7b 100644 --- a/src/nsterm.m +++ b/src/nsterm.m @@ -1141,6 +1141,7 @@ ns_update_end (struct frame *f) #ifdef NS_DRAW_TO_BUFFER [NSGraphicsContext setCurrentContext:nil]; + [view setNeedsDisplay:YES]; #else block_input (); @@ -1194,12 +1195,6 @@ ns_focus (struct frame *f, NSRect *r, int n) /* clipping */ if (r) { -#ifdef NS_IMPL_COCOA - int i; - for (i = 0 ; i < n ; i++) - [view setNeedsDisplayInRect:r[i]]; -#endif - [[NSGraphicsContext currentContext] saveGraphicsState]; if (n == 2) NSRectClipList (r, 2); @@ -1224,7 +1219,9 @@ ns_unfocus (struct frame *f) gsaved = NO; } -#ifdef NS_IMPL_GNUSTEP +#ifdef NS_DRAW_TO_BUFFER + [FRAME_NS_VIEW (f) setNeedsDisplay:YES]; +#else if (f != ns_updating_frame) { if (focus_view != NULL) -- cgit v1.3 From dc3006cf1419e5b22c35fa36d79ba029d0482df1 Mon Sep 17 00:00:00 2001 From: Paul Eggert Date: Wed, 4 Mar 2020 13:48:26 -0800 Subject: Pacify GCC 9.2.1 20190927 -O3 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Original problem report by N. Jackson in: https://lists.gnu.org/r/emacs-devel/2020-03/msg00047.html I found some other warnings when I used gcc, and fixed them with this patch. * lib-src/etags.c: Include verify.h. (xnmalloc, xnrealloc): Tell the compiler that NITEMS is nononnegative and ITEM_SIZE is positive. * src/conf_post.h (__has_attribute_returns_nonnull) (ATTRIBUTE_RETURNS_NONNULL): New macros. * src/editfns.c (Fuser_full_name): Don’t assume Fuser_login_name returns non-nil. * src/intervals.c (rotate_right, rotate_left, update_interval): * src/intervals.h (LENGTH, LEFT_TOTAL_LENGTH, RIGHT_TOTAL_LENGTH): Use TOTAL_LENGTH0 or equivalent on intervals that might be null. * src/intervals.h (TOTAL_LENGTH): Assume arg is nonnull. (TOTAL_LENGTH0): New macro, with the old TOTAL_LENGTH meaning. (make_interval, split_interval_right): Add ATTRIBUTE_RETURNS_NONNULL. * src/pdumper.c (dump_check_dump_off): Now returns void, since no caller uses the return value. Redo assert to pacify GCC. (decode_emacs_reloc): Add a seemingly-random eassume to pacify GCC. Ugly, and I suspect due to a bug in GCC. --- lib-src/etags.c | 5 +++++ src/conf_post.h | 7 +++++++ src/editfns.c | 19 +++++++++++-------- src/intervals.c | 12 ++++++------ src/intervals.h | 24 ++++++++++++++---------- src/pdumper.c | 9 ++++----- 6 files changed, 47 insertions(+), 29 deletions(-) (limited to 'src') diff --git a/lib-src/etags.c b/lib-src/etags.c index 174c33a7a5f..eee2c596262 100644 --- a/lib-src/etags.c +++ b/lib-src/etags.c @@ -124,6 +124,7 @@ University of California, as described above. */ #include #include #include +#include #include #include @@ -7310,6 +7311,8 @@ static void * xnmalloc (ptrdiff_t nitems, ptrdiff_t item_size) { ptrdiff_t nbytes; + assume (0 <= nitems); + assume (0 < item_size); if (INT_MULTIPLY_WRAPV (nitems, item_size, &nbytes)) memory_full (); return xmalloc (nbytes); @@ -7319,6 +7322,8 @@ static void * xnrealloc (void *pa, ptrdiff_t nitems, ptrdiff_t item_size) { ptrdiff_t nbytes; + assume (0 <= nitems); + assume (0 < item_size); if (INT_MULTIPLY_WRAPV (nitems, item_size, &nbytes) || SIZE_MAX < nbytes) memory_full (); void *result = realloc (pa, nbytes); diff --git a/src/conf_post.h b/src/conf_post.h index 2f8d19fdca8..eb8fb18c00c 100644 --- a/src/conf_post.h +++ b/src/conf_post.h @@ -78,6 +78,7 @@ typedef bool bool_bf; # define __has_attribute_no_address_safety_analysis false # define __has_attribute_no_sanitize_address GNUC_PREREQ (4, 8, 0) # define __has_attribute_no_sanitize_undefined GNUC_PREREQ (4, 9, 0) +# define __has_attribute_returns_nonnull GNUC_PREREQ (4, 9, 0) # define __has_attribute_warn_unused_result GNUC_PREREQ (3, 4, 0) #endif @@ -321,6 +322,12 @@ extern int emacs_setenv_TZ (char const *); #define ATTRIBUTE_MALLOC_SIZE(args) ATTRIBUTE_MALLOC ATTRIBUTE_ALLOC_SIZE (args) +#if __has_attribute (returns_nonnull) +# define ATTRIBUTE_RETURNS_NONNULL __attribute__ ((returns_nonnull)) +#else +# define ATTRIBUTE_RETURNS_NONNULL +#endif + /* Work around GCC bug 59600: when a function is inlined, the inlined code may have its addresses sanitized even if the function has the no_sanitize_address attribute. This bug is fixed in GCC 4.9.0 and diff --git a/src/editfns.c b/src/editfns.c index ddf190b1752..eb15566fb48 100644 --- a/src/editfns.c +++ b/src/editfns.c @@ -1262,14 +1262,17 @@ name, or nil if there is no such user. */) if (q) { Lisp_Object login = Fuser_login_name (INT_TO_INTEGER (pw->pw_uid)); - USE_SAFE_ALLOCA; - char *r = SAFE_ALLOCA (strlen (p) + SBYTES (login) + 1); - memcpy (r, p, q - p); - char *s = lispstpcpy (&r[q - p], login); - r[q - p] = upcase ((unsigned char) r[q - p]); - strcpy (s, q + 1); - full = build_string (r); - SAFE_FREE (); + if (!NILP (login)) + { + USE_SAFE_ALLOCA; + char *r = SAFE_ALLOCA (strlen (p) + SBYTES (login) + 1); + memcpy (r, p, q - p); + char *s = lispstpcpy (&r[q - p], login); + r[q - p] = upcase ((unsigned char) r[q - p]); + strcpy (s, q + 1); + full = build_string (r); + SAFE_FREE (); + } } #endif /* AMPERSAND_FULL_NAME */ diff --git a/src/intervals.c b/src/intervals.c index a66594ceea2..594d8924ebc 100644 --- a/src/intervals.c +++ b/src/intervals.c @@ -298,7 +298,7 @@ rotate_right (INTERVAL A) set_interval_parent (c, A); /* A's total length is decreased by the length of B and its left child. */ - A->total_length -= B->total_length - TOTAL_LENGTH (c); + A->total_length -= TOTAL_LENGTH (B) - TOTAL_LENGTH0 (c); eassert (TOTAL_LENGTH (A) > 0); eassert (LENGTH (A) > 0); @@ -349,7 +349,7 @@ rotate_left (INTERVAL A) set_interval_parent (c, A); /* A's total length is decreased by the length of B and its right child. */ - A->total_length -= B->total_length - TOTAL_LENGTH (c); + A->total_length -= TOTAL_LENGTH (B) - TOTAL_LENGTH0 (c); eassert (TOTAL_LENGTH (A) > 0); eassert (LENGTH (A) > 0); @@ -723,13 +723,13 @@ previous_interval (register INTERVAL interval) i->position - LEFT_TOTAL_LENGTH (i) \ - LENGTH (INTERVAL_PARENT (i)) -/* Find the interval containing POS, given some non-NULL INTERVAL in +/* Find the interval containing POS, given some interval I in the same tree. Note that we update interval->position in each interval we traverse, assuming it is already correctly set for the argument I. We don't assume that any other interval already has a correctly set ->position. */ INTERVAL -update_interval (register INTERVAL i, ptrdiff_t pos) +update_interval (INTERVAL i, ptrdiff_t pos) { if (!i) return NULL; @@ -739,7 +739,7 @@ update_interval (register INTERVAL i, ptrdiff_t pos) if (pos < i->position) { /* Move left. */ - if (pos >= i->position - TOTAL_LENGTH (i->left)) + if (pos >= i->position - LEFT_TOTAL_LENGTH (i)) { i->left->position = i->position - TOTAL_LENGTH (i->left) + LEFT_TOTAL_LENGTH (i->left); @@ -757,7 +757,7 @@ update_interval (register INTERVAL i, ptrdiff_t pos) else if (pos >= INTERVAL_LAST_POS (i)) { /* Move right. */ - if (pos < INTERVAL_LAST_POS (i) + TOTAL_LENGTH (i->right)) + if (pos < INTERVAL_LAST_POS (i) + RIGHT_TOTAL_LENGTH (i)) { i->right->position = INTERVAL_LAST_POS (i) + LEFT_TOTAL_LENGTH (i->right); diff --git a/src/intervals.h b/src/intervals.h index a93b10e9fff..9a7ba910a10 100644 --- a/src/intervals.h +++ b/src/intervals.h @@ -96,24 +96,27 @@ struct interval /* True if this interval has both left and right children. */ #define BOTH_KIDS_P(i) ((i)->left != NULL && (i)->right != NULL) -/* The total size of all text represented by this interval and all its - children in the tree. This is zero if the interval is null. */ -#define TOTAL_LENGTH(i) ((i) == NULL ? 0 : (i)->total_length) +/* The total size of all text represented by the nonnull interval I + and all its children in the tree. */ +#define TOTAL_LENGTH(i) ((i)->total_length) + +/* Likewise, but also defined to be zero if I is null. */ +#define TOTAL_LENGTH0(i) ((i) ? TOTAL_LENGTH (i) : 0) /* The size of text represented by this interval alone. */ -#define LENGTH(i) ((i)->total_length \ - - TOTAL_LENGTH ((i)->right) \ - - TOTAL_LENGTH ((i)->left)) +#define LENGTH(i) (TOTAL_LENGTH (i) \ + - RIGHT_TOTAL_LENGTH (i) \ + - LEFT_TOTAL_LENGTH (i)) /* The position of the character just past the end of I. Note that the position cache i->position must be valid for this to work. */ #define INTERVAL_LAST_POS(i) ((i)->position + LENGTH (i)) /* The total size of the left subtree of this interval. */ -#define LEFT_TOTAL_LENGTH(i) ((i)->left ? (i)->left->total_length : 0) +#define LEFT_TOTAL_LENGTH(i) TOTAL_LENGTH0 ((i)->left) /* The total size of the right subtree of this interval. */ -#define RIGHT_TOTAL_LENGTH(i) ((i)->right ? (i)->right->total_length : 0) +#define RIGHT_TOTAL_LENGTH(i) TOTAL_LENGTH0 ((i)->right) /* These macros are for dealing with the interval properties. */ @@ -234,7 +237,7 @@ set_interval_plist (INTERVAL i, Lisp_Object plist) /* Declared in alloc.c. */ -extern INTERVAL make_interval (void); +extern INTERVAL make_interval (void) ATTRIBUTE_RETURNS_NONNULL; /* Declared in intervals.c. */ @@ -246,7 +249,8 @@ extern void traverse_intervals (INTERVAL, ptrdiff_t, Lisp_Object); extern void traverse_intervals_noorder (INTERVAL, void (*) (INTERVAL, void *), void *); -extern INTERVAL split_interval_right (INTERVAL, ptrdiff_t); +extern INTERVAL split_interval_right (INTERVAL, ptrdiff_t) + ATTRIBUTE_RETURNS_NONNULL; extern INTERVAL split_interval_left (INTERVAL, ptrdiff_t); extern INTERVAL find_interval (INTERVAL, ptrdiff_t); extern INTERVAL next_interval (INTERVAL); diff --git a/src/pdumper.c b/src/pdumper.c index 4d81203f46f..e52163cdae2 100644 --- a/src/pdumper.c +++ b/src/pdumper.c @@ -3604,14 +3604,12 @@ dump_unwind_cleanup (void *data) Vprocess_environment = ctx->old_process_environment; } -/* Return DUMP_OFFSET, making sure it is within the heap. */ -static dump_off +/* Check that DUMP_OFFSET is within the heap. */ +static void dump_check_dump_off (struct dump_context *ctx, dump_off dump_offset) { eassert (dump_offset > 0); - if (ctx) - eassert (dump_offset < ctx->end_heap); - return dump_offset; + eassert (!ctx || dump_offset < ctx->end_heap); } static void @@ -3734,6 +3732,7 @@ decode_emacs_reloc (struct dump_context *ctx, Lisp_Object lreloc) } else { + eassume (ctx); /* Pacify GCC 9.2.1 -O3 -Wnull-dereference. */ eassert (!dump_object_emacs_ptr (target_value)); reloc.u.dump_offset = dump_recall_object (ctx, target_value); if (reloc.u.dump_offset <= 0) -- cgit v1.3 From 88c6db91961945e4ede53d66216c2484f0c5342b Mon Sep 17 00:00:00 2001 From: Eli Zaretskii Date: Thu, 5 Mar 2020 17:57:21 +0200 Subject: Avoid crashes when a fontset has strange entries * src/fontset.c (reorder_font_vector): Skip nil entries in the loop that assigns scores to rfont_def's. (fontset_compare_rfontdef): Cope with nil. This has the effect of moving any nil entries to the end of the font-group, and avoids crashing if an element other than the last in the font-group is nil. (Bug#39892) --- src/fontset.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/fontset.c b/src/fontset.c index bca9452418e..c2bb8b21f26 100644 --- a/src/fontset.c +++ b/src/fontset.c @@ -367,8 +367,14 @@ fontset_add (Lisp_Object fontset, Lisp_Object range, Lisp_Object elt, Lisp_Objec static int fontset_compare_rfontdef (const void *val1, const void *val2) { - return (RFONT_DEF_SCORE (*(Lisp_Object *) val1) - - RFONT_DEF_SCORE (*(Lisp_Object *) val2)); + Lisp_Object v1 = *(Lisp_Object *) val1, v2 = *(Lisp_Object *) val2; + if (NILP (v1) && NILP (v2)) + return 0; + else if (NILP (v1)) + return INT_MIN; + else if (NILP (v2)) + return INT_MAX; + return (RFONT_DEF_SCORE (v1) - RFONT_DEF_SCORE (v2)); } /* Update a cons cell which has this form: @@ -400,6 +406,8 @@ reorder_font_vector (Lisp_Object font_group, struct font *font) for (i = 0; i < size; i++) { Lisp_Object rfont_def = AREF (vec, i); + if (NILP (rfont_def)) + continue; Lisp_Object font_def = RFONT_DEF_FONT_DEF (rfont_def); Lisp_Object font_spec = FONT_DEF_SPEC (font_def); int score = RFONT_DEF_SCORE (rfont_def) & 0xFF; -- cgit v1.3 From c996fe1ec69de0082043397d4965d08cb94892fb Mon Sep 17 00:00:00 2001 From: Glenn Morris Date: Thu, 5 Mar 2020 17:11:51 -0800 Subject: Remove ancient OS X process-connection-type handling * src/process.c (init_process_emacs) [DARWIN_OS]: Remove process-connection-type special-casing for OS X < 10.3 (ie pre-2003). Ref https://lists.gnu.org/r/emacs-devel/2005-01/msg00741.html --- src/emacs.c | 1 - src/process.c | 13 ------------- 2 files changed, 14 deletions(-) (limited to 'src') diff --git a/src/emacs.c b/src/emacs.c index 8b27c63f731..ea9c4cd79dc 100644 --- a/src/emacs.c +++ b/src/emacs.c @@ -1964,7 +1964,6 @@ Using an Emacs configured with --with-x-toolkit=lucid does not have this problem /* This calls putenv and so must precede init_process_emacs. */ init_timefns (); - /* This sets Voperating_system_release, which init_process_emacs uses. */ init_editfns (); /* These two call putenv. */ diff --git a/src/process.c b/src/process.c index 91d426103d8..e4e5e57aeee 100644 --- a/src/process.c +++ b/src/process.c @@ -8277,19 +8277,6 @@ init_process_emacs (int sockfd) memset (datagram_address, 0, sizeof datagram_address); #endif -#if defined (DARWIN_OS) - /* PTYs are broken on Darwin < 6, but are sometimes useful for interactive - processes. As such, we only change the default value. */ - if (initialized) - { - char const *release = (STRINGP (Voperating_system_release) - ? SSDATA (Voperating_system_release) - : 0); - if (!release || !release[0] || (release[0] < '7' && release[1] == '.')) { - Vprocess_connection_type = Qnil; - } - } -#endif #endif /* subprocesses */ kbd_is_on_hold = 0; } -- cgit v1.3 From 33b31dc314cf71f3b1569f25756d69c6915168ff Mon Sep 17 00:00:00 2001 From: Eli Zaretskii Date: Fri, 6 Mar 2020 09:48:10 +0200 Subject: Attempt to avoid rare segfaults in show_mouse_face * src/xdisp.c (show_mouse_face): Don't display the active region if called on a frame different from the one recorded in HLINFO. (Bug#37671) --- src/xdisp.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src') diff --git a/src/xdisp.c b/src/xdisp.c index 3a8b5e3f1d0..a4de2698ca0 100644 --- a/src/xdisp.c +++ b/src/xdisp.c @@ -31454,6 +31454,10 @@ show_mouse_face (Mouse_HLInfo *hlinfo, enum draw_glyphs_face draw) struct window *w = XWINDOW (hlinfo->mouse_face_window); struct frame *f = XFRAME (WINDOW_FRAME (w)); + /* Don't bother doing anything if we are on a wrong frame. */ + if (f != hlinfo->mouse_face_mouse_frame) + return; + if (/* If window is in the process of being destroyed, don't bother to do anything. */ w->current_matrix != NULL -- cgit v1.3 From 5d4cf1fef85bc24bc4cd9705ebb14150263ad707 Mon Sep 17 00:00:00 2001 From: Paul Eggert Date: Sat, 7 Mar 2020 12:04:05 -0800 Subject: Add ‘nofollow’ flag to set-file-times MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is a companion to the recent set-file-modes patch. It adds support for a ‘nofollow’ flag to set-file-times (Bug#39773). Like the set-file-modes patch, it needs work in the w32 port. * admin/merge-gnulib (GNULIB_MODULES): Add futimens, utimensat. Remove utimens. * doc/lispref/files.texi (Changing Files): * etc/NEWS: Mention the change. * lib/gnulib.mk.in, m4/gnulib-comp.m4: Regenerate. * lisp/files.el (copy-directory): * lisp/gnus/gnus-cloud.el (gnus-cloud-replace-file): * lisp/net/tramp-adb.el (tramp-adb-handle-copy-file): * lisp/net/tramp-smb.el (tramp-smb-handle-copy-file): * lisp/tar-mode.el (tar-copy): * test/lisp/filenotify-tests.el (file-notify-test03-events): * test/lisp/files-tests.el: (files-tests-file-name-non-special-set-file-times): * test/lisp/net/tramp-tests.el (tramp-test22-file-times): When setting file times, avoid following symbolic links when the file is not supposed to be a symbolic link. * lib/futimens.c, lib/utimensat.c, m4/futimens.m4, m4/utimensat.m4: New files, copied from Gnulib. * lisp/gnus/gnus-cloud.el (gnus-cloud-replace-file): When creating a file that is not supposed to exist already, use the excl flag to check this. * lisp/net/tramp-adb.el (tramp-adb-handle-set-file-times): * lisp/net/tramp-sh.el (tramp-sh-handle-set-file-times): * lisp/net/tramp-sudoedit.el (tramp-sudoedit-handle-set-file-times): Accept an optional FLAG arg that is currently ignored, and add a FIXME comment for it. * lisp/net/tramp-gvfs.el (tramp-gvfs-handle-set-file-times): * src/fileio.c (Fset_file_times): Support an optional FLAG arg. * src/fileio.c (Fcopy_file): Use futimens instead of set_file_times, as it’s simpler and is a POSIX API. * src/sysdep.c (set_file_times): Move from here ... * src/w32.c (set_file_times): ... to here, and make it static, since it is now used only in w32.c. Presumably w32.c should also add support for futimens and utimensat (the POSIX APIs, which Emacs now uses) and it can remove fdutimens (the Gnulib API, which Emacs no longer uses). --- admin/merge-gnulib | 4 +- doc/lispref/files.texi | 10 ++- etc/NEWS | 4 +- lib/futimens.c | 37 ++++++++++ lib/gnulib.mk.in | 28 +++++++- lib/utimensat.c | 160 ++++++++++++++++++++++++++++++++++++++++++ lisp/files.el | 7 +- lisp/gnus/gnus-cloud.el | 4 +- lisp/net/tramp-adb.el | 6 +- lisp/net/tramp-gvfs.el | 4 +- lisp/net/tramp-sh.el | 3 +- lisp/net/tramp-smb.el | 3 +- lisp/net/tramp-sudoedit.el | 3 +- lisp/tar-mode.el | 2 +- m4/futimens.m4 | 65 +++++++++++++++++ m4/gnulib-comp.m4 | 38 +++++++++- m4/utimensat.m4 | 69 ++++++++++++++++++ src/fileio.c | 51 +++++++------- src/sysdep.c | 15 ---- src/systime.h | 3 - src/w32.c | 15 ++++ test/lisp/filenotify-tests.el | 8 +-- test/lisp/files-tests.el | 4 +- test/lisp/net/tramp-tests.el | 3 +- 24 files changed, 476 insertions(+), 70 deletions(-) create mode 100644 lib/futimens.c create mode 100644 lib/utimensat.c create mode 100644 m4/futimens.m4 create mode 100644 m4/utimensat.m4 (limited to 'src') diff --git a/admin/merge-gnulib b/admin/merge-gnulib index 557119441e4..768e5051f0b 100755 --- a/admin/merge-gnulib +++ b/admin/merge-gnulib @@ -34,7 +34,7 @@ GNULIB_MODULES=' d-type diffseq dosname double-slash-root dtoastr dtotimespec dup2 environ execinfo explicit_bzero faccessat fchmodat fcntl fcntl-h fdopendir - filemode filevercmp flexmember fpieee fstatat fsusage fsync + filemode filevercmp flexmember fpieee fstatat fsusage fsync futimens getloadavg getopt-gnu gettime gettimeofday gitlog-to-changelog ieee754-h ignore-value intprops largefile lstat manywarnings memmem-simple mempcpy memrchr minmax mkostemp mktime nstrftime @@ -43,7 +43,7 @@ GNULIB_MODULES=' sig2str socklen stat-time std-gnu11 stdalign stddef stdio stpcpy strnlen strtoimax symlink sys_stat sys_time tempname time time_r time_rz timegm timer-time timespec-add timespec-sub - update-copyright unlocked-io utimens + update-copyright unlocked-io utimensat vla warnings ' diff --git a/doc/lispref/files.texi b/doc/lispref/files.texi index a69a4e5dd38..b3ad9b99649 100644 --- a/doc/lispref/files.texi +++ b/doc/lispref/files.texi @@ -1909,11 +1909,19 @@ omitted or @code{nil}, it defaults to 0, i.e., no access rights at all. @end defun -@defun set-file-times filename &optional time +@defun set-file-times filename &optional time flag This function sets the access and modification times of @var{filename} to @var{time}. The return value is @code{t} if the times are successfully set, otherwise it is @code{nil}. @var{time} defaults to the current time and must be a time value (@pxref{Time of Day}). + +By default this function follows symbolic links. However, if the +optional argument @var{flag} is the symbol @code{nofollow}, this +function does not follow @var{filename} if it is a symbolic link; +this can help prevent inadvertently changing the times of a file +somewhere else. On platforms that do not support changing times +on a symbolic link, this function signals an error when @var{filename} +is a symbolic link and @var{flag} is @code{nofollow}. @end defun @defun set-file-extended-attributes filename attribute-alist diff --git a/etc/NEWS b/etc/NEWS index fcdf6dbe249..47b87afbc60 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -225,8 +225,8 @@ called when the function object is garbage-collected. Use 'set_function_finalizer' to set the finalizer and 'get_function_finalizer' to retrieve it. -** 'file-modes' and 'set-file-modes' now have an optional argument -specifying whether to follow symbolic links. +** 'file-modes', 'set-file-modes', and 'set-file-times' now have an +optional argument specifying whether to follow symbolic links. ** 'parse-time-string' can now parse ISO 8601 format strings, such as "2020-01-15T16:12:21-08:00". diff --git a/lib/futimens.c b/lib/futimens.c new file mode 100644 index 00000000000..83fb27cb6aa --- /dev/null +++ b/lib/futimens.c @@ -0,0 +1,37 @@ +/* Set the access and modification time of an open fd. + Copyright (C) 2009-2020 Free Software Foundation, Inc. + + This program 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. + + This program 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 this program. If not, see . */ + +/* written by Eric Blake */ + +#include + +#include + +#include "utimens.h" + +/* Set the access and modification timestamps of FD to be + TIMESPEC[0] and TIMESPEC[1], respectively. + Fail with ENOSYS on systems without futimes (or equivalent). + If TIMESPEC is null, set the timestamps to the current time. + Return 0 on success, -1 (setting errno) on failure. */ +int +futimens (int fd, struct timespec const times[2]) +{ + /* fdutimens also works around bugs in native futimens, when running + with glibc compiled against newer headers but on a Linux kernel + older than 2.6.32. */ + return fdutimens (fd, NULL, times); +} diff --git a/lib/gnulib.mk.in b/lib/gnulib.mk.in index d4dc6a3df33..e90d2e39049 100644 --- a/lib/gnulib.mk.in +++ b/lib/gnulib.mk.in @@ -106,6 +106,7 @@ # fstatat \ # fsusage \ # fsync \ +# futimens \ # getloadavg \ # getopt-gnu \ # gettime \ @@ -155,7 +156,7 @@ # timespec-sub \ # unlocked-io \ # update-copyright \ -# utimens \ +# utimensat \ # vla \ # warnings @@ -1087,6 +1088,7 @@ gl_GNULIB_ENABLED_lchmod = @gl_GNULIB_ENABLED_lchmod@ gl_GNULIB_ENABLED_malloca = @gl_GNULIB_ENABLED_malloca@ gl_GNULIB_ENABLED_open = @gl_GNULIB_ENABLED_open@ gl_GNULIB_ENABLED_strtoll = @gl_GNULIB_ENABLED_strtoll@ +gl_GNULIB_ENABLED_utimens = @gl_GNULIB_ENABLED_utimens@ gl_LIBOBJS = @gl_LIBOBJS@ gl_LTLIBOBJS = @gl_LTLIBOBJS@ gltests_LIBOBJS = @gltests_LIBOBJS@ @@ -1733,6 +1735,17 @@ EXTRA_libgnu_a_SOURCES += fsync.c endif ## end gnulib module fsync +## begin gnulib module futimens +ifeq (,$(OMIT_GNULIB_MODULE_futimens)) + + +EXTRA_DIST += futimens.c + +EXTRA_libgnu_a_SOURCES += futimens.c + +endif +## end gnulib module futimens + ## begin gnulib module getdtablesize ifeq (,$(OMIT_GNULIB_MODULE_getdtablesize)) @@ -3375,13 +3388,26 @@ endif ## begin gnulib module utimens ifeq (,$(OMIT_GNULIB_MODULE_utimens)) +ifneq (,$(gl_GNULIB_ENABLED_utimens)) libgnu_a_SOURCES += utimens.c +endif EXTRA_DIST += utimens.h endif ## end gnulib module utimens +## begin gnulib module utimensat +ifeq (,$(OMIT_GNULIB_MODULE_utimensat)) + + +EXTRA_DIST += at-func.c utimensat.c + +EXTRA_libgnu_a_SOURCES += at-func.c utimensat.c + +endif +## end gnulib module utimensat + ## begin gnulib module verify ifeq (,$(OMIT_GNULIB_MODULE_verify)) diff --git a/lib/utimensat.c b/lib/utimensat.c new file mode 100644 index 00000000000..63788d56480 --- /dev/null +++ b/lib/utimensat.c @@ -0,0 +1,160 @@ +/* Set the access and modification time of a file relative to directory fd. + Copyright (C) 2009-2020 Free Software Foundation, Inc. + + This program 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. + + This program 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 this program. If not, see . */ + +/* written by Eric Blake */ + +#include + +/* Specification. */ +#include + +#include +#include +#include + +#include "stat-time.h" +#include "timespec.h" +#include "utimens.h" + +#if HAVE_UTIMENSAT + +# undef utimensat + +/* If we have a native utimensat, but are compiling this file, then + utimensat was defined to rpl_utimensat by our replacement + sys/stat.h. We assume the native version might fail with ENOSYS, + or succeed without properly affecting ctime (as is the case when + using newer glibc but older Linux kernel). In this scenario, + rpl_utimensat checks whether the native version is usable, and + local_utimensat provides the fallback manipulation. */ + +static int local_utimensat (int, char const *, struct timespec const[2], int); +# define AT_FUNC_NAME local_utimensat + +/* Like utimensat, but work around native bugs. */ + +int +rpl_utimensat (int fd, char const *file, struct timespec const times[2], + int flag) +{ +# if defined __linux__ || defined __sun + struct timespec ts[2]; +# endif + + /* See comments in utimens.c for details. */ + static int utimensat_works_really; /* 0 = unknown, 1 = yes, -1 = no. */ + if (0 <= utimensat_works_really) + { + int result; +# if defined __linux__ || defined __sun + struct stat st; + /* As recently as Linux kernel 2.6.32 (Dec 2009), several file + systems (xfs, ntfs-3g) have bugs with a single UTIME_OMIT, + but work if both times are either explicitly specified or + UTIME_NOW. Work around it with a preparatory [l]stat prior + to calling utimensat; fortunately, there is not much timing + impact due to the extra syscall even on file systems where + UTIME_OMIT would have worked. + + The same bug occurs in Solaris 11.1 (Apr 2013). + + FIXME: Simplify this in 2024, when these file system bugs are + no longer common on Gnulib target platforms. */ + if (times && (times[0].tv_nsec == UTIME_OMIT + || times[1].tv_nsec == UTIME_OMIT)) + { + if (fstatat (fd, file, &st, flag)) + return -1; + if (times[0].tv_nsec == UTIME_OMIT && times[1].tv_nsec == UTIME_OMIT) + return 0; + if (times[0].tv_nsec == UTIME_OMIT) + ts[0] = get_stat_atime (&st); + else + ts[0] = times[0]; + if (times[1].tv_nsec == UTIME_OMIT) + ts[1] = get_stat_mtime (&st); + else + ts[1] = times[1]; + times = ts; + } +# ifdef __hppa__ + /* Linux kernel 2.6.22.19 on hppa does not reject invalid tv_nsec + values. */ + else if (times + && ((times[0].tv_nsec != UTIME_NOW + && ! (0 <= times[0].tv_nsec + && times[0].tv_nsec < TIMESPEC_HZ)) + || (times[1].tv_nsec != UTIME_NOW + && ! (0 <= times[1].tv_nsec + && times[1].tv_nsec < TIMESPEC_HZ)))) + { + errno = EINVAL; + return -1; + } +# endif +# endif + result = utimensat (fd, file, times, flag); + /* Linux kernel 2.6.25 has a bug where it returns EINVAL for + UTIME_NOW or UTIME_OMIT with non-zero tv_sec, which + local_utimensat works around. Meanwhile, EINVAL for a bad + flag is indeterminate whether the native utimensat works, but + local_utimensat will also reject it. */ + if (result == -1 && errno == EINVAL && (flag & ~AT_SYMLINK_NOFOLLOW)) + return result; + if (result == 0 || (errno != ENOSYS && errno != EINVAL)) + { + utimensat_works_really = 1; + return result; + } + } + /* No point in trying openat/futimens, since on Linux, futimens is + implemented with the same syscall as utimensat. Only avoid the + native utimensat due to an ENOSYS failure; an EINVAL error was + data-dependent, and the next caller may pass valid data. */ + if (0 <= utimensat_works_really && errno == ENOSYS) + utimensat_works_really = -1; + return local_utimensat (fd, file, times, flag); +} + +#else /* !HAVE_UTIMENSAT */ + +# define AT_FUNC_NAME utimensat + +#endif /* !HAVE_UTIMENSAT */ + +/* Set the access and modification timestamps of FILE to be + TIMESPEC[0] and TIMESPEC[1], respectively; relative to directory + FD. If flag is AT_SYMLINK_NOFOLLOW, change the times of a symlink, + or fail with ENOSYS if not possible. If TIMESPEC is null, set the + timestamps to the current time. If possible, do it without + changing the working directory. Otherwise, resort to using + save_cwd/fchdir, then utimens/restore_cwd. If either the save_cwd + or the restore_cwd fails, then give a diagnostic and exit nonzero. + Return 0 on success, -1 (setting errno) on failure. */ + +/* AT_FUNC_NAME is now utimensat or local_utimensat. */ +#define AT_FUNC_F1 lutimens +#define AT_FUNC_F2 utimens +#define AT_FUNC_USE_F1_COND AT_SYMLINK_NOFOLLOW +#define AT_FUNC_POST_FILE_PARAM_DECLS , struct timespec const ts[2], int flag +#define AT_FUNC_POST_FILE_ARGS , ts +#include "at-func.c" +#undef AT_FUNC_NAME +#undef AT_FUNC_F1 +#undef AT_FUNC_F2 +#undef AT_FUNC_USE_F1_COND +#undef AT_FUNC_POST_FILE_PARAM_DECLS +#undef AT_FUNC_POST_FILE_ARGS diff --git a/lisp/files.el b/lisp/files.el index 2e7694d7677..8ce0187f5b7 100644 --- a/lisp/files.el +++ b/lisp/files.el @@ -5944,9 +5944,10 @@ into NEWNAME instead." ;; Set directory attributes. (let ((modes (file-modes directory)) (times (and keep-time (file-attribute-modification-time - (file-attributes directory))))) - (if modes (set-file-modes newname modes (unless follow 'nofollow))) - (if times (set-file-times newname times)))))) + (file-attributes directory)))) + (follow-flag (unless follow 'nofollow))) + (if modes (set-file-modes newname modes follow-flag)) + (if times (set-file-times newname times follow-flag)))))) ;; At time of writing, only info uses this. diff --git a/lisp/gnus/gnus-cloud.el b/lisp/gnus/gnus-cloud.el index 4d8764bacca..da6231d7330 100644 --- a/lisp/gnus/gnus-cloud.el +++ b/lisp/gnus/gnus-cloud.el @@ -285,8 +285,8 @@ Use old data if FORCE-OLDER is not nil." (insert new-contents) (when (file-exists-p file-name) (rename-file file-name (car (find-backup-file-name file-name)))) - (write-region (point-min) (point-max) file-name) - (set-file-times file-name (parse-iso8601-time-string date)))) + (write-region (point-min) (point-max) file-name nil nil nil 'excl) + (set-file-times file-name (parse-iso8601-time-string date) 'nofollow))) (defun gnus-cloud-file-covered-p (file-name) (let ((matched nil)) diff --git a/lisp/net/tramp-adb.el b/lisp/net/tramp-adb.el index 2c9674fa36f..7ee740f93cb 100644 --- a/lisp/net/tramp-adb.el +++ b/lisp/net/tramp-adb.el @@ -674,8 +674,9 @@ But handle the case, if the \"test\" command is not available." (tramp-adb-send-command-and-check v (format "chmod %o %s" mode localname))))) -(defun tramp-adb-handle-set-file-times (filename &optional time) +(defun tramp-adb-handle-set-file-times (filename &optional time flag) "Like `set-file-times' for Tramp files." + flag ;; FIXME: Support 'nofollow'. (with-parsed-tramp-file-name filename nil (tramp-flush-file-properties v localname) (let ((time (if (or (null time) @@ -777,7 +778,8 @@ PRESERVE-UID-GID and PRESERVE-EXTENDED-ATTRIBUTES are completely ignored." (set-file-times newname (tramp-compat-file-attribute-modification-time - (file-attributes filename)))))) + (file-attributes filename)) + (unless ok-if-already-exists 'nofollow))))) (defun tramp-adb-handle-rename-file (filename newname &optional ok-if-already-exists) diff --git a/lisp/net/tramp-gvfs.el b/lisp/net/tramp-gvfs.el index 3ce7bbbd4a3..1ad57c59a5b 100644 --- a/lisp/net/tramp-gvfs.el +++ b/lisp/net/tramp-gvfs.el @@ -1571,7 +1571,7 @@ If FILE-SYSTEM is non-nil, return file system attributes." (tramp-gvfs-url-file-name (tramp-make-tramp-file-name v)) "unix::mode" (number-to-string mode)))) -(defun tramp-gvfs-handle-set-file-times (filename &optional time) +(defun tramp-gvfs-handle-set-file-times (filename &optional time flag) "Like `set-file-times' for Tramp files." (with-parsed-tramp-file-name filename nil (tramp-flush-file-properties v localname) @@ -1582,7 +1582,7 @@ If FILE-SYSTEM is non-nil, return file system attributes." (current-time) time))) (tramp-gvfs-send-command - v "gvfs-set-attribute" "-t" "uint64" + v "gvfs-set-attribute" (if flag "-nt" "-t") "uint64" (tramp-gvfs-url-file-name (tramp-make-tramp-file-name v)) "time::modified" (format-time-string "%s" time))))) diff --git a/lisp/net/tramp-sh.el b/lisp/net/tramp-sh.el index 84b8191bd3d..560941c4d5b 100644 --- a/lisp/net/tramp-sh.el +++ b/lisp/net/tramp-sh.el @@ -1495,11 +1495,12 @@ of." mode (tramp-shell-quote-argument localname)) "Error while changing file's mode %s" filename)))) -(defun tramp-sh-handle-set-file-times (filename &optional time) +(defun tramp-sh-handle-set-file-times (filename &optional time flag) "Like `set-file-times' for Tramp files." (with-parsed-tramp-file-name filename nil (when (tramp-get-remote-touch v) (tramp-flush-file-properties v localname) + flag ;; FIXME: Support 'nofollow'. (let ((time (if (or (null time) (tramp-compat-time-equal-p time tramp-time-doesnt-exist) diff --git a/lisp/net/tramp-smb.el b/lisp/net/tramp-smb.el index 42954cbda3d..d91362c879c 100644 --- a/lisp/net/tramp-smb.el +++ b/lisp/net/tramp-smb.el @@ -619,7 +619,8 @@ PRESERVE-UID-GID and PRESERVE-EXTENDED-ATTRIBUTES are completely ignored." (set-file-times newname (tramp-compat-file-attribute-modification-time - (file-attributes filename)))))) + (file-attributes filename)) + (unless ok-if-already-exists 'nofollow))))) (defun tramp-smb-handle-delete-directory (directory &optional recursive _trash) "Like `delete-directory' for Tramp files." diff --git a/lisp/net/tramp-sudoedit.el b/lisp/net/tramp-sudoedit.el index 7d8c8a90618..c054f405e3d 100644 --- a/lisp/net/tramp-sudoedit.el +++ b/lisp/net/tramp-sudoedit.el @@ -523,10 +523,11 @@ the result will be a local, non-Tramp, file name." (string-to-number (match-string 2))) (string-to-number (match-string 3))))))))) -(defun tramp-sudoedit-handle-set-file-times (filename &optional time) +(defun tramp-sudoedit-handle-set-file-times (filename &optional time flag) "Like `set-file-times' for Tramp files." (with-parsed-tramp-file-name filename nil (tramp-flush-file-properties v localname) + flag ;; FIXME: Support 'nofollow'. (let ((time (if (or (null time) (tramp-compat-time-equal-p time tramp-time-doesnt-exist) diff --git a/lisp/tar-mode.el b/lisp/tar-mode.el index 97d883eebd9..a3c1715b1e1 100644 --- a/lisp/tar-mode.el +++ b/lisp/tar-mode.el @@ -1056,7 +1056,7 @@ extracted file." (write-region start end to-file nil nil nil t)) (when (and tar-copy-preserve-time date) - (set-file-times to-file date))) + (set-file-times to-file date 'nofollow))) (message "Copied tar entry %s to %s" name to-file))) (defun tar-new-entry (filename &optional index) diff --git a/m4/futimens.m4 b/m4/futimens.m4 new file mode 100644 index 00000000000..dc5cfa94119 --- /dev/null +++ b/m4/futimens.m4 @@ -0,0 +1,65 @@ +# serial 8 +# See if we need to provide futimens replacement. + +dnl Copyright (C) 2009-2020 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. + +# Written by Eric Blake. + +AC_DEFUN([gl_FUNC_FUTIMENS], +[ + AC_REQUIRE([gl_SYS_STAT_H_DEFAULTS]) + AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles + AC_REQUIRE([gl_USE_SYSTEM_EXTENSIONS]) + AC_CHECK_FUNCS_ONCE([futimens]) + if test $ac_cv_func_futimens = no; then + HAVE_FUTIMENS=0 + else + AC_CACHE_CHECK([whether futimens works], + [gl_cv_func_futimens_works], + [AC_RUN_IFELSE([AC_LANG_PROGRAM([[ +#include +#include +#include +#include +]], [[struct timespec ts[2]; + int fd = creat ("conftest.file", 0600); + struct stat st; + if (fd < 0) return 1; + ts[0].tv_sec = 1; + ts[0].tv_nsec = UTIME_OMIT; + ts[1].tv_sec = 1; + ts[1].tv_nsec = UTIME_NOW; + errno = 0; + if (futimens (AT_FDCWD, NULL) == 0) return 2; + if (errno != EBADF) return 3; + if (futimens (fd, ts)) return 4; + sleep (1); + ts[0].tv_nsec = UTIME_NOW; + ts[1].tv_nsec = UTIME_OMIT; + if (futimens (fd, ts)) return 5; + if (fstat (fd, &st)) return 6; + if (st.st_ctime < st.st_atime) return 7; + ]])], + [gl_cv_func_futimens_works=yes], + [gl_cv_func_futimens_works=no], + [case "$host_os" in + # Guess no on glibc systems. + *-gnu* | gnu*) gl_cv_func_futimens_works="guessing no" ;; + # Guess no on musl systems. + *-musl*) gl_cv_func_futimens_works="guessing no" ;; + # Guess yes otherwise. + *) gl_cv_func_futimens_works="guessing yes" ;; + esac + ]) + rm -f conftest.file]) + case "$gl_cv_func_futimens_works" in + *yes) ;; + *) + REPLACE_FUTIMENS=1 + ;; + esac + fi +]) diff --git a/m4/gnulib-comp.m4 b/m4/gnulib-comp.m4 index 1465ce811b8..3228aa42b57 100644 --- a/m4/gnulib-comp.m4 +++ b/m4/gnulib-comp.m4 @@ -95,6 +95,7 @@ AC_DEFUN([gl_EARLY], # Code from module fstatat: # Code from module fsusage: # Code from module fsync: + # Code from module futimens: # Code from module getdtablesize: # Code from module getgroups: # Code from module getloadavg: @@ -179,6 +180,7 @@ AC_DEFUN([gl_EARLY], # Code from module unlocked-io: # Code from module update-copyright: # Code from module utimens: + # Code from module utimensat: # Code from module vararrays: # Code from module verify: # Code from module vla: @@ -297,6 +299,11 @@ AC_DEFUN([gl_INIT], gl_PREREQ_FSYNC fi gl_UNISTD_MODULE_INDICATOR([fsync]) + gl_FUNC_FUTIMENS + if test $HAVE_FUTIMENS = 0 || test $REPLACE_FUTIMENS = 1; then + AC_LIBOBJ([futimens]) + fi + gl_SYS_STAT_MODULE_INDICATOR([futimens]) gl_GETLOADAVG if test $HAVE_GETLOADAVG = 0; then AC_LIBOBJ([getloadavg]) @@ -466,7 +473,11 @@ AC_DEFUN([gl_INIT], gl_TIMESPEC gl_UNISTD_H gl_FUNC_GLIBC_UNLOCKED_IO - gl_UTIMENS + gl_FUNC_UTIMENSAT + if test $HAVE_UTIMENSAT = 0 || test $REPLACE_UTIMENSAT = 1; then + AC_LIBOBJ([utimensat]) + fi + gl_SYS_STAT_MODULE_INDICATOR([utimensat]) AC_C_VARARRAYS gl_gnulib_enabled_260941c0e5dc67ec9e87d1fb321c300b=false gl_gnulib_enabled_cloexec=false @@ -485,6 +496,7 @@ AC_DEFUN([gl_INIT], gl_gnulib_enabled_03e0aaad4cb89ca757653bd367a6ccb7=false gl_gnulib_enabled_6099e9737f757db36c47fa9d9f02e88c=false gl_gnulib_enabled_strtoll=false + gl_gnulib_enabled_utimens=false gl_gnulib_enabled_682e609604ccaac6be382e4ee3a4eaec=false func_gl_gnulib_m4code_260941c0e5dc67ec9e87d1fb321c300b () { @@ -663,6 +675,13 @@ AC_DEFUN([gl_INIT], gl_gnulib_enabled_strtoll=true fi } + func_gl_gnulib_m4code_utimens () + { + if ! $gl_gnulib_enabled_utimens; then + gl_UTIMENS + gl_gnulib_enabled_utimens=true + fi + } func_gl_gnulib_m4code_682e609604ccaac6be382e4ee3a4eaec () { if ! $gl_gnulib_enabled_682e609604ccaac6be382e4ee3a4eaec; then @@ -705,6 +724,9 @@ AC_DEFUN([gl_INIT], if test $HAVE_FSTATAT = 0 || test $REPLACE_FSTATAT = 1; then func_gl_gnulib_m4code_03e0aaad4cb89ca757653bd367a6ccb7 fi + if test $HAVE_FUTIMENS = 0 || test $REPLACE_FUTIMENS = 1; then + func_gl_gnulib_m4code_utimens + fi if test $REPLACE_GETOPT = 1; then func_gl_gnulib_m4code_be453cec5eecf5731a274f2de7f2db36 fi @@ -729,6 +751,15 @@ AC_DEFUN([gl_INIT], if test $HAVE_TIMEGM = 0 || test $REPLACE_TIMEGM = 1; then func_gl_gnulib_m4code_5264294aa0a5557541b53c8c741f7f31 fi + if test $HAVE_UTIMENSAT = 0 || test $REPLACE_UTIMENSAT = 1; then + func_gl_gnulib_m4code_260941c0e5dc67ec9e87d1fb321c300b + fi + if test $HAVE_UTIMENSAT = 0 || test $REPLACE_UTIMENSAT = 1; then + func_gl_gnulib_m4code_03e0aaad4cb89ca757653bd367a6ccb7 + fi + if test $HAVE_UTIMENSAT = 0 || test $REPLACE_UTIMENSAT = 1; then + func_gl_gnulib_m4code_utimens + fi m4_pattern_allow([^gl_GNULIB_ENABLED_]) AM_CONDITIONAL([gl_GNULIB_ENABLED_260941c0e5dc67ec9e87d1fb321c300b], [$gl_gnulib_enabled_260941c0e5dc67ec9e87d1fb321c300b]) AM_CONDITIONAL([gl_GNULIB_ENABLED_cloexec], [$gl_gnulib_enabled_cloexec]) @@ -747,6 +778,7 @@ AC_DEFUN([gl_INIT], AM_CONDITIONAL([gl_GNULIB_ENABLED_03e0aaad4cb89ca757653bd367a6ccb7], [$gl_gnulib_enabled_03e0aaad4cb89ca757653bd367a6ccb7]) AM_CONDITIONAL([gl_GNULIB_ENABLED_6099e9737f757db36c47fa9d9f02e88c], [$gl_gnulib_enabled_6099e9737f757db36c47fa9d9f02e88c]) AM_CONDITIONAL([gl_GNULIB_ENABLED_strtoll], [$gl_gnulib_enabled_strtoll]) + AM_CONDITIONAL([gl_GNULIB_ENABLED_utimens], [$gl_gnulib_enabled_utimens]) AM_CONDITIONAL([gl_GNULIB_ENABLED_682e609604ccaac6be382e4ee3a4eaec], [$gl_gnulib_enabled_682e609604ccaac6be382e4ee3a4eaec]) # End of code from modules m4_ifval(gl_LIBSOURCES_LIST, [ @@ -956,6 +988,7 @@ AC_DEFUN([gl_FILE_LIST], [ lib/fsync.c lib/ftoastr.c lib/ftoastr.h + lib/futimens.c lib/get-permissions.c lib/getdtablesize.c lib/getgroups.c @@ -1063,6 +1096,7 @@ AC_DEFUN([gl_FILE_LIST], [ lib/unlocked-io.h lib/utimens.c lib/utimens.h + lib/utimensat.c lib/verify.h lib/vla.h lib/warn-on-use.h @@ -1103,6 +1137,7 @@ AC_DEFUN([gl_FILE_LIST], [ m4/fstatat.m4 m4/fsusage.m4 m4/fsync.m4 + m4/futimens.m4 m4/getdtablesize.m4 m4/getgroups.m4 m4/getloadavg.m4 @@ -1184,6 +1219,7 @@ AC_DEFUN([gl_FILE_LIST], [ m4/unistd_h.m4 m4/unlocked-io.m4 m4/utimens.m4 + m4/utimensat.m4 m4/utimes.m4 m4/vararrays.m4 m4/warn-on-use.m4 diff --git a/m4/utimensat.m4 b/m4/utimensat.m4 new file mode 100644 index 00000000000..2bc1bfebb5d --- /dev/null +++ b/m4/utimensat.m4 @@ -0,0 +1,69 @@ +# serial 6 +# See if we need to provide utimensat replacement. + +dnl Copyright (C) 2009-2020 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. + +# Written by Eric Blake. + +AC_DEFUN([gl_FUNC_UTIMENSAT], +[ + AC_REQUIRE([gl_SYS_STAT_H_DEFAULTS]) + AC_REQUIRE([gl_USE_SYSTEM_EXTENSIONS]) + AC_CHECK_FUNCS_ONCE([utimensat]) + if test $ac_cv_func_utimensat = no; then + HAVE_UTIMENSAT=0 + else + AC_CACHE_CHECK([whether utimensat works], + [gl_cv_func_utimensat_works], + [AC_RUN_IFELSE( + [AC_LANG_PROGRAM([[ +#include +#include +#include +]], [[int result = 0; + const char *f = "conftest.file"; + if (close (creat (f, 0600))) + return 1; + /* Test whether the AT_SYMLINK_NOFOLLOW flag is supported. */ + { + if (utimensat (AT_FDCWD, f, NULL, AT_SYMLINK_NOFOLLOW)) + result |= 2; + } + /* Test whether UTIME_NOW and UTIME_OMIT work. */ + { + struct timespec ts[2]; + ts[0].tv_sec = 1; + ts[0].tv_nsec = UTIME_OMIT; + ts[1].tv_sec = 1; + ts[1].tv_nsec = UTIME_NOW; + if (utimensat (AT_FDCWD, f, ts, 0)) + result |= 4; + } + sleep (1); + { + struct stat st; + struct timespec ts[2]; + ts[0].tv_sec = 1; + ts[0].tv_nsec = UTIME_NOW; + ts[1].tv_sec = 1; + ts[1].tv_nsec = UTIME_OMIT; + if (utimensat (AT_FDCWD, f, ts, 0)) + result |= 8; + if (stat (f, &st)) + result |= 16; + else if (st.st_ctime < st.st_atime) + result |= 32; + } + return result; + ]])], + [gl_cv_func_utimensat_works=yes], + [gl_cv_func_utimensat_works=no], + [gl_cv_func_utimensat_works="guessing yes"])]) + if test "$gl_cv_func_utimensat_works" = no; then + REPLACE_UTIMENSAT=1 + fi + fi +]) diff --git a/src/fileio.c b/src/fileio.c index 2532f5233c4..82fd7989206 100644 --- a/src/fileio.c +++ b/src/fileio.c @@ -2253,9 +2253,8 @@ permissions. */) if (!NILP (keep_time)) { - struct timespec atime = get_stat_atime (&st); - struct timespec mtime = get_stat_mtime (&st); - if (set_file_times (ofd, SSDATA (encoded_newname), atime, mtime) != 0) + struct timespec ts[] = { get_stat_atime (&st), get_stat_mtime (&st) }; + if (futimens (ofd, ts) != 0) xsignal2 (Qfile_date_error, build_string ("Cannot set file date"), newname); } @@ -3430,39 +3429,41 @@ The value is an integer. */) } -DEFUN ("set-file-times", Fset_file_times, Sset_file_times, 1, 2, 0, +DEFUN ("set-file-times", Fset_file_times, Sset_file_times, 1, 3, 0, doc: /* Set times of file FILENAME to TIMESTAMP. -Set both access and modification times. -Return t on success, else nil. -Use the current time if TIMESTAMP is nil. TIMESTAMP is in the format of -`current-time'. */) - (Lisp_Object filename, Lisp_Object timestamp) +If optional FLAG is `nofollow', do not follow FILENAME if it is a +symbolic link. Set both access and modification times. Return t on +success, else nil. Use the current time if TIMESTAMP is nil. +TIMESTAMP is in the format of `current-time'. */) + (Lisp_Object filename, Lisp_Object timestamp, Lisp_Object flag) { - Lisp_Object absname, encoded_absname; - Lisp_Object handler; - struct timespec t = lisp_time_argument (timestamp); + int nofollow = symlink_nofollow_flag (flag); - absname = Fexpand_file_name (filename, BVAR (current_buffer, directory)); + struct timespec ts[2]; + if (!NILP (timestamp)) + ts[0] = ts[1] = lisp_time_argument (timestamp); + else + ts[0].tv_nsec = ts[1].tv_nsec = UTIME_NOW; /* If the file name has special constructs in it, call the corresponding file name handler. */ - handler = Ffind_file_name_handler (absname, Qset_file_times); + Lisp_Object + absname = Fexpand_file_name (filename, BVAR (current_buffer, directory)), + handler = Ffind_file_name_handler (absname, Qset_file_times); if (!NILP (handler)) - return call3 (handler, Qset_file_times, absname, timestamp); + return call4 (handler, Qset_file_times, absname, timestamp, flag); - encoded_absname = ENCODE_FILE (absname); + Lisp_Object encoded_absname = ENCODE_FILE (absname); - { - if (set_file_times (-1, SSDATA (encoded_absname), t, t) != 0) - { + if (utimensat (AT_FDCWD, SSDATA (encoded_absname), ts, nofollow) != 0) + { #ifdef MSDOS - /* Setting times on a directory always fails. */ - if (file_directory_p (encoded_absname)) - return Qnil; + /* Setting times on a directory always fails. */ + if (file_directory_p (encoded_absname)) + return Qnil; #endif - report_file_error ("Setting file times", absname); - } - } + report_file_error ("Setting file times", absname); + } return Qt; } diff --git a/src/sysdep.c b/src/sysdep.c index e8e8bbfb502..149d80f19ec 100644 --- a/src/sysdep.c +++ b/src/sysdep.c @@ -2752,21 +2752,6 @@ emacs_perror (char const *message) errno = err; } -/* Set the access and modification time stamps of FD (a.k.a. FILE) to be - ATIME and MTIME, respectively. - FD must be either negative -- in which case it is ignored -- - or a file descriptor that is open on FILE. - If FD is nonnegative, then FILE can be NULL. */ -int -set_file_times (int fd, const char *filename, - struct timespec atime, struct timespec mtime) -{ - struct timespec timespec[2]; - timespec[0] = atime; - timespec[1] = mtime; - return fdutimens (fd, filename, timespec); -} - /* Rename directory SRCFD's entry SRC to directory DSTFD's entry DST. This is like renameat except that it fails if DST already exists, or if this operation is not supported atomically. Return 0 if diff --git a/src/systime.h b/src/systime.h index 00ca4a1c58d..b59a3d1c690 100644 --- a/src/systime.h +++ b/src/systime.h @@ -67,9 +67,6 @@ timespec_valid_p (struct timespec t) return t.tv_nsec >= 0; } -/* defined in sysdep.c */ -extern int set_file_times (int, const char *, struct timespec, struct timespec); - /* defined in keyboard.c */ extern void set_waiting_for_input (struct timespec *); diff --git a/src/w32.c b/src/w32.c index cf1a3b37678..40f286ad6cf 100644 --- a/src/w32.c +++ b/src/w32.c @@ -3189,6 +3189,21 @@ fdutimens (int fd, char const *file, struct timespec const timespec[2]) } } +/* Set the access and modification time stamps of FD (a.k.a. FILE) to be + ATIME and MTIME, respectively. + FD must be either negative -- in which case it is ignored -- + or a file descriptor that is open on FILE. + If FD is nonnegative, then FILE can be NULL. */ +static int +set_file_times (int fd, const char *filename, + struct timespec atime, struct timespec mtime) +{ + struct timespec timespec[2]; + timespec[0] = atime; + timespec[1] = mtime; + return fdutimens (fd, filename, timespec); +} + /* ------------------------------------------------------------------------- */ /* IO support and wrapper functions for the Windows API. */ diff --git a/test/lisp/filenotify-tests.el b/test/lisp/filenotify-tests.el index 39156fbb5dc..a184fabb9ff 100644 --- a/test/lisp/filenotify-tests.el +++ b/test/lisp/filenotify-tests.el @@ -771,9 +771,9 @@ delivered." (copy-file file-notify--test-tmpfile file-notify--test-tmpfile1) ;; The next two events shall not be visible. (file-notify--test-read-event) - (set-file-modes file-notify--test-tmpfile 000) + (set-file-modes file-notify--test-tmpfile 000 'nofollow) (file-notify--test-read-event) - (set-file-times file-notify--test-tmpfile '(0 0)) + (set-file-times file-notify--test-tmpfile '(0 0) 'nofollow) (file-notify--test-read-event) (delete-directory file-notify--test-tmpdir 'recursive)) (file-notify-rm-watch file-notify--test-desc) @@ -864,9 +864,9 @@ delivered." (write-region "any text" nil file-notify--test-tmpfile nil 'no-message) (file-notify--test-read-event) - (set-file-modes file-notify--test-tmpfile 000) + (set-file-modes file-notify--test-tmpfile 000 'nofollow) (file-notify--test-read-event) - (set-file-times file-notify--test-tmpfile '(0 0)) + (set-file-times file-notify--test-tmpfile '(0 0) 'nofollow) (file-notify--test-read-event) (delete-file file-notify--test-tmpfile)) (file-notify-rm-watch file-notify--test-desc) diff --git a/test/lisp/files-tests.el b/test/lisp/files-tests.el index ac56a7732f2..05d9ceebf1d 100644 --- a/test/lisp/files-tests.el +++ b/test/lisp/files-tests.el @@ -1003,9 +1003,9 @@ unquoted file names." (ert-deftest files-tests-file-name-non-special-set-file-times () (files-tests--with-temp-non-special (tmpfile nospecial) - (set-file-times nospecial)) + (set-file-times nospecial nil 'nofollow)) (files-tests--with-temp-non-special-and-file-name-handler (tmpfile nospecial) - (should-error (set-file-times nospecial)))) + (should-error (set-file-times nospecial nil 'nofollow)))) (ert-deftest files-tests-file-name-non-special-set-visited-file-modtime () (files-tests--with-temp-non-special (tmpfile nospecial) diff --git a/test/lisp/net/tramp-tests.el b/test/lisp/net/tramp-tests.el index be0f418c943..dcf376e70b4 100644 --- a/test/lisp/net/tramp-tests.el +++ b/test/lisp/net/tramp-tests.el @@ -3743,7 +3743,8 @@ This tests also `make-symbolic-link', `file-truename' and `add-name-to-file'." (file-attributes tmp-name1)))) ;; Skip the test, if the remote handler is not able to set ;; the correct time. - (skip-unless (set-file-times tmp-name1 (seconds-to-time 1))) + (skip-unless (set-file-times tmp-name1 (seconds-to-time 1) + 'nofollow)) ;; Dumb remote shells without perl(1) or stat(1) are not ;; able to return the date correctly. They say "don't know". (unless (tramp-compat-time-equal-p -- cgit v1.3 From 0a3682a566d5563e3d57defe49359cee236e0274 Mon Sep 17 00:00:00 2001 From: Paul Eggert Date: Sun, 8 Mar 2020 00:16:17 -0800 Subject: * src/timefns.c: Add comments. --- src/timefns.c | 32 ++++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/timefns.c b/src/timefns.c index a08d3b816ff..8e2bb214ed8 100644 --- a/src/timefns.c +++ b/src/timefns.c @@ -491,11 +491,14 @@ timespec_mpz (struct timespec t) static Lisp_Object timespec_ticks (struct timespec t) { + /* For speed, use intmax_t arithmetic if it will do. */ intmax_t accum; if (FASTER_TIMEFNS && !INT_MULTIPLY_WRAPV (t.tv_sec, TIMESPEC_HZ, &accum) && !INT_ADD_WRAPV (t.tv_nsec, accum, &accum)) return make_int (accum); + + /* Fall back on bignum arithmetic. */ timespec_mpz (t); return make_integer_mpz (); } @@ -505,12 +508,17 @@ timespec_ticks (struct timespec t) static Lisp_Object lisp_time_hz_ticks (struct lisp_time t, Lisp_Object hz) { + /* For speed, just return TICKS if T is (TICKS . HZ). */ if (FASTER_TIMEFNS && EQ (t.hz, hz)) return t.ticks; + + /* Check HZ for validity. */ if (FIXNUMP (hz)) { if (XFIXNUM (hz) <= 0) invalid_hz (hz); + + /* For speed, use intmax_t arithmetic if it will do. */ intmax_t ticks; if (FASTER_TIMEFNS && FIXNUMP (t.ticks) && FIXNUMP (t.hz) && !INT_MULTIPLY_WRAPV (XFIXNUM (t.ticks), XFIXNUM (hz), &ticks)) @@ -520,6 +528,7 @@ lisp_time_hz_ticks (struct lisp_time t, Lisp_Object hz) else if (! (BIGNUMP (hz) && 0 < mpz_sgn (*xbignum_val (hz)))) invalid_hz (hz); + /* Fall back on bignum arithmetic. */ mpz_mul (mpz[0], *bignum_integer (&mpz[0], t.ticks), *bignum_integer (&mpz[1], hz)); @@ -533,9 +542,13 @@ lisp_time_seconds (struct lisp_time t) { if (!FASTER_TIMEFNS) return lisp_time_hz_ticks (t, make_fixnum (1)); + + /* For speed, use EMACS_INT arithmetic if it will do. */ if (FIXNUMP (t.ticks) && FIXNUMP (t.hz)) return make_fixnum (XFIXNUM (t.ticks) / XFIXNUM (t.hz) - (XFIXNUM (t.ticks) % XFIXNUM (t.hz) < 0)); + + /* For speed, inline what lisp_time_hz_ticks would do. */ mpz_fdiv_q (mpz[0], *bignum_integer (&mpz[0], t.ticks), *bignum_integer (&mpz[1], t.hz)); @@ -1116,21 +1129,22 @@ time_arith (Lisp_Object a, Lisp_Object b, bool subtract) (subtract ? mpz_submul : mpz_addmul) (*iticks, *fa, *nb); /* Normalize iticks/ihz by dividing both numerator and - denominator by ig = gcd (iticks, ihz). However, if that - would cause the denominator to become less than hzmin, - rescale the denominator upwards from its ordinary value by - multiplying numerator and denominator so that the denominator - becomes at least hzmin. This rescaling avoids returning a - timestamp that is less precise than both a and b, or a - timestamp that looks obsolete when that might be a problem. */ + denominator by ig = gcd (iticks, ihz). For speed, though, + skip this division if ihz = 1. */ mpz_t *ig = &mpz[3]; mpz_gcd (*ig, *iticks, *ihz); - if (!FASTER_TIMEFNS || mpz_cmp_ui (*ig, 1) > 0) { mpz_divexact (*iticks, *iticks, *ig); mpz_divexact (*ihz, *ihz, *ig); + /* However, if dividing the denominator by ig would cause the + denominator to become less than hzmin, rescale the denominator + upwards by multiplying the normalized numerator and denominator + so that the resulting denominator becomes at least hzmin. + This rescaling avoids returning a timestamp that is less precise + than both a and b, or a timestamp that looks obsolete when that + might be a problem. */ if (!FASTER_TIMEFNS || mpz_cmp (*ihz, *hzmin) < 0) { /* Rescale straightforwardly. Although this might not @@ -1144,6 +1158,8 @@ time_arith (Lisp_Object a, Lisp_Object b, bool subtract) mpz_mul (*ihz, *ihz, *rescale); } } + + /* mpz[0] and iticks now correspond to the (HZ . TICKS) pair. */ hz = make_integer_mpz (); mpz_swap (mpz[0], *iticks); ticks = make_integer_mpz (); -- cgit v1.3 From 66bc47d12aba72ff738a9f5575e0b93eefc641ba Mon Sep 17 00:00:00 2001 From: Eli Zaretskii Date: Sun, 8 Mar 2020 17:00:10 +0200 Subject: Fix the MinGW build as followup to recent "nofollow" changes * src/w32.c (fdutimens): Call utimensat instead of utime. (set_file_times): Function deleted. (convert_from_timespec): Renamed from convert_from_time_t and modified to accept 'struct timespec' argument instead of 'time_t'. (utimensat): Renamed from utime and modified to accept 'struct timespec [2]' argument and an additional argument FLAG. Emulate Posix 'utimensat'. Call 'convert_from_timespec'. (w32_copy_file): Call 'utimensat' instead of 'set_file_times'. * src/fileio.c (Fcopy_file) [WINDOWSNT]: Make the error message be identical to that on Posix platforms. * nt/inc/sys/stat.h (utimensat): Provide prototype. * nt/mingw-cfg.site (ac_cv_func_futimens) (gl_cv_func_futimens_works, ac_cv_func_utimensat) (gl_cv_func_utimensat_works): Override Gnulib tests. * nt/gnulib-cfg.mk (OMIT_GNULIB_MODULE_futimens) (OMIT_GNULIB_MODULE_utimensat): Disable these Gnulib modules. --- nt/gnulib-cfg.mk | 2 + nt/inc/sys/stat.h | 5 +++ nt/mingw-cfg.site | 4 ++ src/fileio.c | 2 +- src/w32.c | 121 ++++++++++++++++++++++++++++++++++-------------------- 5 files changed, 88 insertions(+), 46 deletions(-) (limited to 'src') diff --git a/nt/gnulib-cfg.mk b/nt/gnulib-cfg.mk index 1d120a973d1..e3b945720d6 100644 --- a/nt/gnulib-cfg.mk +++ b/nt/gnulib-cfg.mk @@ -65,3 +65,5 @@ OMIT_GNULIB_MODULE_unistd = true OMIT_GNULIB_MODULE_canonicalize-lgpl = true OMIT_GNULIB_MODULE_fchmodat = true OMIT_GNULIB_MODULE_lchmod = true +OMIT_GNULIB_MODULE_futimens = true +OMIT_GNULIB_MODULE_utimensat = true diff --git a/nt/inc/sys/stat.h b/nt/inc/sys/stat.h index 7bf780dbaa2..f58d5ab6573 100644 --- a/nt/inc/sys/stat.h +++ b/nt/inc/sys/stat.h @@ -164,4 +164,9 @@ int __cdecl __MINGW_NOTHROW fstatat (int, char const *, struct stat *, int); int __cdecl __MINGW_NOTHROW chmod (const char*, int); +/* Provide prototypes of library functions that are emulated on w32 + and whose prototypes are usually found in sys/stat.h on POSIX + platforms. */ +extern int utimensat (int, const char *, struct timespec const[2], int); + #endif /* INC_SYS_STAT_H_ */ diff --git a/nt/mingw-cfg.site b/nt/mingw-cfg.site index 5bd5b834634..2271eef98d6 100644 --- a/nt/mingw-cfg.site +++ b/nt/mingw-cfg.site @@ -105,6 +105,10 @@ gl_cv_func_fstatat_zero_flag=yes ac_cv_func_fchmodat=yes gl_cv_func_fchmodat_works="not-needed-so-yes" ac_cv_func_lchmod=yes +ac_cv_func_futimens=not-needed +gl_cv_func_futimens_works="not-needed-so-yes" +ac_cv_func_utimensat=yes +gl_cv_func_utimensat_works=yes # Aliased to _commit in ms-w32.h ac_cv_func_fsync=yes ac_cv_func_fdatasync=yes diff --git a/src/fileio.c b/src/fileio.c index 82fd7989206..ffe79559a3f 100644 --- a/src/fileio.c +++ b/src/fileio.c @@ -2077,7 +2077,7 @@ permissions. */) report_file_error ("Copying permissions from", file); case -3: xsignal2 (Qfile_date_error, - build_string ("Resetting file times"), newname); + build_string ("Cannot set file date"), newname); case -4: report_file_error ("Copying permissions to", newname); } diff --git a/src/w32.c b/src/w32.c index 40f286ad6cf..698e10e234e 100644 --- a/src/w32.c +++ b/src/w32.c @@ -3178,33 +3178,9 @@ fdutimens (int fd, char const *file, struct timespec const timespec[2]) return _futime (fd, &_ut); } else - { - struct utimbuf ut; - - ut.actime = timespec[0].tv_sec; - ut.modtime = timespec[1].tv_sec; - /* Call 'utime', which is implemented below, not the MS library - function, which fails on directories. */ - return utime (file, &ut); - } + return utimensat (fd, file, timespec, 0); } -/* Set the access and modification time stamps of FD (a.k.a. FILE) to be - ATIME and MTIME, respectively. - FD must be either negative -- in which case it is ignored -- - or a file descriptor that is open on FILE. - If FD is nonnegative, then FILE can be NULL. */ -static int -set_file_times (int fd, const char *filename, - struct timespec atime, struct timespec mtime) -{ - struct timespec timespec[2]; - timespec[0] = atime; - timespec[1] = mtime; - return fdutimens (fd, filename, timespec); -} - - /* ------------------------------------------------------------------------- */ /* IO support and wrapper functions for the Windows API. */ /* ------------------------------------------------------------------------- */ @@ -4985,7 +4961,7 @@ convert_time (FILETIME ft) } static void -convert_from_time_t (time_t time, FILETIME * pft) +convert_from_timespec (struct timespec time, FILETIME * pft) { ULARGE_INTEGER tmp; @@ -4996,7 +4972,8 @@ convert_from_time_t (time_t time, FILETIME * pft) } /* time in 100ns units since 1-Jan-1601 */ - tmp.QuadPart = (ULONGLONG) time * 10000000L + utc_base; + tmp.QuadPart = + (ULONGLONG) time.tv_sec * 10000000L + time.tv_nsec / 100 + utc_base; pft->dwHighDateTime = tmp.HighPart; pft->dwLowDateTime = tmp.LowPart; } @@ -5663,8 +5640,8 @@ fstatat (int fd, char const *name, struct stat *st, int flags) return stat_worker (name, st, ! (flags & AT_SYMLINK_NOFOLLOW)); } -/* Provide fstat and utime as well as stat for consistent handling of - file timestamps. */ +/* Provide fstat and utimensat as well as stat for consistent handling + of file timestamps. */ int fstat (int desc, struct stat * buf) { @@ -5775,23 +5752,65 @@ fstat (int desc, struct stat * buf) return 0; } -/* A version of 'utime' which handles directories as well as - files. */ +/* Emulate utimensat. */ int -utime (const char *name, struct utimbuf *times) +utimensat (int fd, const char *name, const struct timespec times[2], int flag) { - struct utimbuf deftime; + struct timespec ltimes[2]; HANDLE fh; FILETIME mtime; FILETIME atime; + DWORD flags_and_attrs = FILE_FLAG_BACKUP_SEMANTICS; + + /* Rely on a hack: an open directory is modeled as file descriptor 0. + This is good enough for the current usage in Emacs, but is fragile. + + FIXME: Add proper support for utimensat. + Gnulib does this and can serve as a model. */ + char fullname[MAX_UTF8_PATH]; + + if (fd != AT_FDCWD) + { + char lastc = dir_pathname[strlen (dir_pathname) - 1]; + + if (_snprintf (fullname, sizeof fullname, "%s%s%s", + dir_pathname, IS_DIRECTORY_SEP (lastc) ? "" : "/", name) + < 0) + { + errno = ENAMETOOLONG; + return -1; + } + name = fullname; + } if (times == NULL) { - deftime.modtime = deftime.actime = time (NULL); - times = &deftime; + memset (ltimes, 0, sizeof (ltimes)); + ltimes[0] = ltimes[1] = current_timespec (); + } + else + { + if (times[0].tv_nsec == UTIME_OMIT && times[1].tv_nsec == UTIME_OMIT) + return 0; /* nothing to do */ + if ((times[0].tv_nsec != UTIME_NOW && times[0].tv_nsec != UTIME_OMIT + && !(0 <= times[0].tv_nsec && times[0].tv_nsec < 1000000000)) + || (times[1].tv_nsec != UTIME_NOW && times[1].tv_nsec != UTIME_OMIT + && !(0 <= times[1].tv_nsec && times[1].tv_nsec < 1000000000))) + { + errno = EINVAL; /* reject invalid timespec values */ + return -1; + } + + memcpy (ltimes, times, sizeof (ltimes)); + if (ltimes[0].tv_nsec == UTIME_NOW) + ltimes[0] = current_timespec (); + if (ltimes[1].tv_nsec == UTIME_NOW) + ltimes[1] = current_timespec (); } + if (flag == AT_SYMLINK_NOFOLLOW) + flags_and_attrs |= FILE_FLAG_OPEN_REPARSE_POINT; if (w32_unicode_filenames) { wchar_t name_utf16[MAX_PATH]; @@ -5805,7 +5824,7 @@ utime (const char *name, struct utimbuf *times) allows other processes to delete files inside it, while we have the directory open. */ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, - 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); + 0, OPEN_EXISTING, flags_and_attrs, NULL); } else { @@ -5816,13 +5835,26 @@ utime (const char *name, struct utimbuf *times) fh = CreateFileA (name_ansi, FILE_WRITE_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, - 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); + 0, OPEN_EXISTING, flags_and_attrs, NULL); } if (fh != INVALID_HANDLE_VALUE) { - convert_from_time_t (times->actime, &atime); - convert_from_time_t (times->modtime, &mtime); - if (!SetFileTime (fh, NULL, &atime, &mtime)) + FILETIME *patime, *pmtime; + if (ltimes[0].tv_nsec == UTIME_OMIT) + patime = NULL; + else + { + convert_from_timespec (ltimes[0], &atime); + patime = &atime; + } + if (ltimes[1].tv_nsec == UTIME_OMIT) + pmtime = NULL; + else + { + convert_from_timespec (ltimes[1], &mtime); + pmtime = &mtime; + } + if (!SetFileTime (fh, NULL, patime, pmtime)) { CloseHandle (fh); errno = EACCES; @@ -6741,16 +6773,16 @@ w32_copy_file (const char *from, const char *to, FIXME? */ else if (!keep_time) { - struct timespec now; + struct timespec tnow[2]; DWORD attributes; + tnow[0] = tnow[1] = current_timespec (); if (w32_unicode_filenames) { /* Ensure file is writable while its times are set. */ attributes = GetFileAttributesW (to_w); SetFileAttributesW (to_w, attributes & ~FILE_ATTRIBUTE_READONLY); - now = current_timespec (); - if (set_file_times (-1, to, now, now)) + if (utimensat (AT_FDCWD, to, tnow, 0)) { /* Restore original attributes. */ SetFileAttributesW (to_w, attributes); @@ -6765,8 +6797,7 @@ w32_copy_file (const char *from, const char *to, { attributes = GetFileAttributesA (to_a); SetFileAttributesA (to_a, attributes & ~FILE_ATTRIBUTE_READONLY); - now = current_timespec (); - if (set_file_times (-1, to, now, now)) + if (utimensat (AT_FDCWD, to, tnow, 0)) { SetFileAttributesA (to_a, attributes); if (acl) -- cgit v1.3 From 20d3d3a9509ed24b4fb701919bf4a677c7d9e249 Mon Sep 17 00:00:00 2001 From: Paul Eggert Date: Sun, 8 Mar 2020 16:43:54 -0700 Subject: * src/timefns.c: Add comments. --- src/timefns.c | 49 ++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 42 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/timefns.c b/src/timefns.c index 8e2bb214ed8..4079358fc5d 100644 --- a/src/timefns.c +++ b/src/timefns.c @@ -421,6 +421,9 @@ decode_float_time (double t, struct lisp_time *result) else if (flt_radix_power_size <= scale) return isnan (t) ? EDOM : EOVERFLOW; + /* Compute TICKS, HZ such that TICKS / HZ exactly equals T, where HZ is + T's frequency or 1, whichever is greater. Here, “frequency” means + 1/precision. Cache HZ values in flt_radix_power. */ double scaled = scalbn (t, scale); eassert (trunc (scaled) == scaled); ticks = double_to_integer (scaled); @@ -442,6 +445,7 @@ decode_float_time (double t, struct lisp_time *result) static Lisp_Object ticks_hz_list4 (Lisp_Object ticks, Lisp_Object hz) { + /* mpz[0] = floor ((ticks * trillion) / hz). */ mpz_t const *zticks = bignum_integer (&mpz[0], ticks); #if FASTER_TIMEFNS && TRILLION <= ULONG_MAX mpz_mul_ui (mpz[0], *zticks, TRILLION); @@ -449,6 +453,9 @@ ticks_hz_list4 (Lisp_Object ticks, Lisp_Object hz) mpz_mul (mpz[0], *zticks, ztrillion); #endif mpz_fdiv_q (mpz[0], mpz[0], *bignum_integer (&mpz[1], hz)); + + /* mpz[0] = floor (mpz[0] / trillion), with US = the high six digits of the + 12-digit remainder, and PS = the low six digits. */ #if FASTER_TIMEFNS && TRILLION <= ULONG_MAX unsigned long int fullps = mpz_fdiv_q_ui (mpz[0], mpz[0], TRILLION); int us = fullps / 1000000; @@ -458,11 +465,14 @@ ticks_hz_list4 (Lisp_Object ticks, Lisp_Object hz) int ps = mpz_fdiv_q_ui (mpz[1], mpz[1], 1000000); int us = mpz_get_ui (mpz[1]); #endif + + /* mpz[0] = floor (mpz[0] / 1 << LO_TIME_BITS), with lo = remainder. */ unsigned long ulo = mpz_get_ui (mpz[0]); if (mpz_sgn (mpz[0]) < 0) ulo = -ulo; int lo = ulo & ((1 << LO_TIME_BITS) - 1); mpz_fdiv_q_2exp (mpz[0], mpz[0], LO_TIME_BITS); + return list4 (make_integer_mpz (), make_fixnum (lo), make_fixnum (us), make_fixnum (ps)); } @@ -482,6 +492,7 @@ mpz_set_time (mpz_t rop, time_t t) static void timespec_mpz (struct timespec t) { + /* mpz[0] = sec * TIMESPEC_HZ + nsec. */ mpz_set_ui (mpz[0], t.tv_nsec); mpz_set_time (mpz[1], t.tv_sec); mpz_addmul_ui (mpz[0], mpz[1], TIMESPEC_HZ); @@ -508,7 +519,9 @@ timespec_ticks (struct timespec t) static Lisp_Object lisp_time_hz_ticks (struct lisp_time t, Lisp_Object hz) { - /* For speed, just return TICKS if T is (TICKS . HZ). */ + /* The idea is to return the floor of ((T.ticks * HZ) / T.hz). */ + + /* For speed, just return T.ticks if T.hz == HZ. */ if (FASTER_TIMEFNS && EQ (t.hz, hz)) return t.ticks; @@ -540,6 +553,8 @@ lisp_time_hz_ticks (struct lisp_time t, Lisp_Object hz) static Lisp_Object lisp_time_seconds (struct lisp_time t) { + /* The idea is to return the floor of T.ticks / T.hz. */ + if (!FASTER_TIMEFNS) return lisp_time_hz_ticks (t, make_fixnum (1)); @@ -587,6 +602,7 @@ frac_to_double (Lisp_Object numerator, Lisp_Object denominator) && integer_to_intmax (numerator, &intmax_numerator)) return intmax_numerator; + /* Compute number of base-FLT_RADIX digits in numerator and denominator. */ mpz_t const *n = bignum_integer (&mpz[0], numerator); mpz_t const *d = bignum_integer (&mpz[1], denominator); ptrdiff_t nbits = mpz_sizeinbase (*n, 2); @@ -599,7 +615,8 @@ frac_to_double (Lisp_Object numerator, Lisp_Object denominator) /* Scale with SCALE when doing integer division. That is, compute (N * FLT_RADIX**SCALE) / D [or, if SCALE is negative, N / (D * FLT_RADIX**-SCALE)] as a bignum, convert the bignum to double, - then divide the double by FLT_RADIX**SCALE. */ + then divide the double by FLT_RADIX**SCALE. First scale N + N (or scale D, if SCALE is negative) ... */ ptrdiff_t scale = ddig - ndig + DBL_MANT_DIG + 1; if (scale < 0) { @@ -614,12 +631,12 @@ frac_to_double (Lisp_Object numerator, Lisp_Object denominator) mpz_mul_2exp (mpz[0], *n, scale * LOG2_FLT_RADIX); n = &mpz[0]; } - + /* ... and then divide, with quotient Q and remainder R. */ mpz_t *q = &mpz[2]; mpz_t *r = &mpz[3]; mpz_tdiv_qr (*q, *r, *n, *d); - /* The amount to add to the absolute value of *Q so that truncating + /* The amount to add to the absolute value of Q so that truncating it to double will round correctly. */ int incr; @@ -658,6 +675,7 @@ frac_to_double (Lisp_Object numerator, Lisp_Object denominator) if (!FASTER_TIMEFNS || incr != 0) (mpz_sgn (*n) < 0 ? mpz_sub_ui : mpz_add_ui) (*q, *q, incr); + /* Rescale the integer Q back to double. This step does not round. */ return scalbn (mpz_get_d (*q), -scale); } @@ -902,6 +920,10 @@ lisp_to_timespec (struct lisp_time t) mpz_t *q = &mpz[0]; mpz_t const *qt = q; + /* Floor-divide (T.ticks * TIMESPEC_HZ) by T.hz, + yielding quotient Q (tv_sec) and remainder NS (tv_nsec). + Return an invalid timespec if Q does not fit in time_t. + For speed, prefer fixnum arithmetic if it works. */ if (FASTER_TIMEFNS && EQ (t.hz, timespec_hz)) { if (FIXNUMP (t.ticks)) @@ -945,8 +967,8 @@ lisp_to_timespec (struct lisp_time t) ns = mpz_fdiv_q_ui (*q, *q, TIMESPEC_HZ); } - /* With some versions of MinGW, tv_sec is a 64-bit type, whereas - time_t is a 32-bit type. */ + /* Check that Q fits in time_t, not merely in T.tv_sec. With some versions + of MinGW, tv_sec is a 64-bit type, whereas time_t is a 32-bit type. */ time_t sec; if (mpz_time (*qt, &sec)) { @@ -1026,10 +1048,14 @@ lispint_arith (Lisp_Object a, Lisp_Object b, bool subtract) { if (EQ (b, make_fixnum (0))) return a; + + /* For speed, use EMACS_INT arithmetic if it will do. */ if (FIXNUMP (a)) return make_int (subtract ? XFIXNUM (a) - XFIXNUM (b) : XFIXNUM (a) + XFIXNUM (b)); + + /* For speed, use mpz_add_ui/mpz_sub_ui if it will do. */ if (eabs (XFIXNUM (b)) <= ULONG_MAX) { ((XFIXNUM (b) < 0) == subtract ? mpz_add_ui : mpz_sub_ui) @@ -1038,6 +1064,7 @@ lispint_arith (Lisp_Object a, Lisp_Object b, bool subtract) } } + /* Fall back on bignum arithmetic if necessary. */ if (!mpz_done) (subtract ? mpz_sub : mpz_add) (mpz[0], *bignum_integer (&mpz[0], a), @@ -1221,6 +1248,8 @@ time_cmp (Lisp_Object a, Lisp_Object b) if (EQ (a, b)) return 0; + /* Compare (ATICKS . AZ) to (BTICKS . BHZ) by comparing + ATICKS * BHZ to BTICKS * AHZ. */ struct lisp_time tb = lisp_time_struct (b, 0); mpz_t const *za = bignum_integer (&mpz[0], ta.ticks); mpz_t const *zb = bignum_integer (&mpz[1], tb.ticks); @@ -1498,6 +1527,7 @@ SEC is always an integer between 0 and 59.) usage: (decode-time &optional TIME ZONE FORM) */) (Lisp_Object specified_time, Lisp_Object zone, Lisp_Object form) { + /* Compute broken-down local time LOCAL_TM from SPECIFIED_TIME and ZONE. */ struct lisp_time lt = lisp_time_struct (specified_time, 0); struct timespec ts = lisp_to_timespec (lt); if (! timespec_valid_p (ts)) @@ -1512,6 +1542,7 @@ usage: (decode-time &optional TIME ZONE FORM) */) if (!tm) time_error (localtime_errno); + /* Let YEAR = LOCAL_TM.tm_year + TM_YEAR_BASE. */ Lisp_Object year; if (FASTER_TIMEFNS && MOST_NEGATIVE_FIXNUM - TM_YEAR_BASE <= local_tm.tm_year @@ -1528,12 +1559,15 @@ usage: (decode-time &optional TIME ZONE FORM) */) year = make_integer_mpz (); } + /* Compute SEC from LOCAL_TM.tm_sec and HZ. */ Lisp_Object hz = lt.hz, sec; if (EQ (hz, make_fixnum (1)) || !EQ (form, Qt)) sec = make_fixnum (local_tm.tm_sec); else { - Lisp_Object ticks; /* hz * tm_sec + mod (lt.ticks, hz) */ + /* Let TICKS = HZ * LOCAL_TM.tm_sec + mod (LT.ticks, HZ) + and SEC = (TICKS . HZ). */ + Lisp_Object ticks; intmax_t n; if (FASTER_TIMEFNS && FIXNUMP (lt.ticks) && FIXNUMP (hz) && !INT_MULTIPLY_WRAPV (XFIXNUM (hz), local_tm.tm_sec, &n) @@ -1663,6 +1697,7 @@ usage: (encode-time TIME &rest OBSOLESCENT-ARGUMENTS) */) yeararg = args[5]; } + /* Let SEC = floor (LT.ticks / HZ), with SUBSECTICKS the remainder. */ struct lisp_time lt; decode_lisp_time (secarg, 0, <, 0); Lisp_Object hz = lt.hz, sec, subsecticks; -- cgit v1.3 From cf223dc928119bb544c3370ad59fe3e175a8236e Mon Sep 17 00:00:00 2001 From: Paul Eggert Date: Sun, 8 Mar 2020 16:49:32 -0700 Subject: ; * src/timefns.c: Fix typo in previous change. --- src/timefns.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/timefns.c b/src/timefns.c index 4079358fc5d..553daf6e6a9 100644 --- a/src/timefns.c +++ b/src/timefns.c @@ -616,7 +616,7 @@ frac_to_double (Lisp_Object numerator, Lisp_Object denominator) (N * FLT_RADIX**SCALE) / D [or, if SCALE is negative, N / (D * FLT_RADIX**-SCALE)] as a bignum, convert the bignum to double, then divide the double by FLT_RADIX**SCALE. First scale N - N (or scale D, if SCALE is negative) ... */ + (or scale D, if SCALE is negative) ... */ ptrdiff_t scale = ddig - ndig + DBL_MANT_DIG + 1; if (scale < 0) { -- cgit v1.3