diff options
| author | Andrea Corallo <akrl@sdf.org> | 2020-03-09 07:49:33 +0000 |
|---|---|---|
| committer | Andrea Corallo <akrl@sdf.org> | 2020-03-09 07:49:33 +0000 |
| commit | 87ee6ff4eb6df369965f37fba073e3ef1bb5d0bd (patch) | |
| tree | 2af79516bca28e875879e01cb45b16fa4525a905 /src | |
| parent | 9838ee7ed870844470703b2648f8b59c0575bd46 (diff) | |
| parent | a461baae79af3cea8780e9d9a845a1e859e96e5e (diff) | |
Merge remote-tracking branch 'savannah/master' into HEAD
Diffstat (limited to 'src')
| -rw-r--r-- | src/composite.c | 18 | ||||
| -rw-r--r-- | src/conf_post.h | 7 | ||||
| -rw-r--r-- | src/editfns.c | 19 | ||||
| -rw-r--r-- | src/emacs.c | 1 | ||||
| -rw-r--r-- | src/fileio.c | 53 | ||||
| -rw-r--r-- | src/font.c | 4 | ||||
| -rw-r--r-- | src/font.h | 2 | ||||
| -rw-r--r-- | src/fontset.c | 12 | ||||
| -rw-r--r-- | src/fringe.c | 7 | ||||
| -rw-r--r-- | src/gnutls.c | 19 | ||||
| -rw-r--r-- | src/intervals.c | 12 | ||||
| -rw-r--r-- | src/intervals.h | 24 | ||||
| -rw-r--r-- | src/nsterm.m | 11 | ||||
| -rw-r--r-- | src/pdumper.c | 9 | ||||
| -rw-r--r-- | src/process.c | 13 | ||||
| -rw-r--r-- | src/sysdep.c | 15 | ||||
| -rw-r--r-- | src/systime.h | 3 | ||||
| -rw-r--r-- | src/timefns.c | 83 | ||||
| -rw-r--r-- | src/w32.c | 106 | ||||
| -rw-r--r-- | src/w32term.c | 42 | ||||
| -rw-r--r-- | src/xdisp.c | 4 |
21 files changed, 303 insertions, 161 deletions
diff --git a/src/composite.c b/src/composite.c index 05365cfb65e..84de334ce0d 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/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/emacs.c b/src/emacs.c index ce1c9edc2dc..fcc02a3a874 100644 --- a/src/emacs.c +++ b/src/emacs.c @@ -1974,7 +1974,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/fileio.c b/src/fileio.c index 2532f5233c4..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); } @@ -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/font.c b/src/font.c index bb39aef92d5..2a456300619 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 0561e3c83f5..8614e7fa10a 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); 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; 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; 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); 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/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) diff --git a/src/pdumper.c b/src/pdumper.c index 4ecdea14538..2e2220a9b29 100644 --- a/src/pdumper.c +++ b/src/pdumper.c @@ -3692,14 +3692,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 @@ -3822,6 +3820,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) 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; } 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/timefns.c b/src/timefns.c index 0aa8775f9ff..404ce4973b7 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); @@ -491,11 +502,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 +519,19 @@ timespec_ticks (struct timespec t) static Lisp_Object lisp_time_hz_ticks (struct lisp_time t, Lisp_Object 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; + + /* 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 +541,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)); @@ -531,11 +553,17 @@ 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)); + + /* 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)); @@ -577,6 +605,7 @@ frac_to_double (Lisp_Object numerator, Lisp_Object denominator) && intmax_numerator % intmax_denominator == 0) return intmax_numerator / intmax_denominator; + /* 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 ndig = mpz_sizeinbase (*n, FLT_RADIX); @@ -588,7 +617,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 + (or scale D, if SCALE is negative) ... */ ptrdiff_t scale = ddig - ndig + DBL_MANT_DIG; if (scale < 0) { @@ -603,12 +633,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; @@ -647,6 +677,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); } @@ -895,6 +926,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)) @@ -938,8 +973,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)) { @@ -1019,10 +1054,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) @@ -1031,6 +1070,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), @@ -1039,9 +1079,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) { @@ -1124,21 +1162,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 @@ -1152,6 +1191,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 (); @@ -1213,6 +1254,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); @@ -1490,6 +1533,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)) @@ -1504,6 +1548,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 @@ -1520,12 +1565,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) @@ -1655,6 +1703,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; diff --git a/src/w32.c b/src/w32.c index cf1a3b37678..698e10e234e 100644 --- a/src/w32.c +++ b/src/w32.c @@ -3178,18 +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); } - /* ------------------------------------------------------------------------- */ /* IO support and wrapper functions for the Windows API. */ /* ------------------------------------------------------------------------- */ @@ -4970,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; @@ -4981,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; } @@ -5648,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) { @@ -5760,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]; @@ -5790,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 { @@ -5801,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; @@ -6726,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); @@ -6750,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) 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) { diff --git a/src/xdisp.c b/src/xdisp.c index c2aa314c1ad..3c594b54add 100644 --- a/src/xdisp.c +++ b/src/xdisp.c @@ -31441,6 +31441,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 |
