diff options
Diffstat (limited to 'src/editfns.c')
-rw-r--r-- | src/editfns.c | 258 |
1 files changed, 146 insertions, 112 deletions
diff --git a/src/editfns.c b/src/editfns.c index 5cc4a67ab19..ccc78e12758 100644 --- a/src/editfns.c +++ b/src/editfns.c @@ -49,6 +49,7 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */ #include <limits.h> #include <intprops.h> +#include <stdlib.h> #include <strftime.h> #include <verify.h> @@ -86,10 +87,6 @@ static timezone_t local_tz; static timezone_t wall_clock_tz; static timezone_t const utc_tz = 0; -/* A valid but unlikely setting for the TZ environment variable. - It is OK (though a bit slower) if the user chooses this value. */ -static char dump_tz_string[] = "TZ=UtC0"; - /* The cached value of Vsystem_name. This is used only to compare it to Vsystem_name, so it need not be visible to the GC. */ static Lisp_Object cached_system_name; @@ -146,8 +143,9 @@ xtzfree (timezone_t tz) static timezone_t tzlookup (Lisp_Object zone, bool settz) { - static char const tzbuf_format[] = "XXX%s%"pI"d:%02d:%02d"; - char tzbuf[sizeof tzbuf_format + INT_STRLEN_BOUND (EMACS_INT)]; + static char const tzbuf_format[] = "<%+.*"pI"d>%s%"pI"d:%02d:%02d"; + char const *trailing_tzbuf_format = tzbuf_format + sizeof "<%+.*"pI"d" - 1; + char tzbuf[sizeof tzbuf_format + 2 * INT_STRLEN_BOUND (EMACS_INT)]; char const *zone_string; timezone_t new_tz; @@ -160,16 +158,50 @@ tzlookup (Lisp_Object zone, bool settz) } else { + bool plain_integer = INTEGERP (zone); + if (EQ (zone, Qwall)) zone_string = 0; else if (STRINGP (zone)) - zone_string = SSDATA (zone); - else if (INTEGERP (zone)) + zone_string = SSDATA (ENCODE_SYSTEM (zone)); + else if (plain_integer || (CONSP (zone) && INTEGERP (XCAR (zone)) + && CONSP (XCDR (zone)))) { + Lisp_Object abbr; + if (!plain_integer) + { + abbr = XCAR (XCDR (zone)); + zone = XCAR (zone); + } + EMACS_INT abszone = eabs (XINT (zone)), hour = abszone / (60 * 60); - int min = (abszone / 60) % 60, sec = abszone % 60; - sprintf (tzbuf, tzbuf_format, &"-"[XINT (zone) < 0], hour, min, sec); - zone_string = tzbuf; + int hour_remainder = abszone % (60 * 60); + int min = hour_remainder / 60, sec = hour_remainder % 60; + + if (plain_integer) + { + int prec = 2; + EMACS_INT numzone = hour; + if (hour_remainder != 0) + { + prec += 2, numzone = 100 * numzone + min; + if (sec != 0) + prec += 2, numzone = 100 * numzone + sec; + } + sprintf (tzbuf, tzbuf_format, prec, numzone, + &"-"[XINT (zone) < 0], hour, min, sec); + zone_string = tzbuf; + } + else + { + AUTO_STRING (leading, "<"); + AUTO_STRING_WITH_LEN (trailing, tzbuf, + sprintf (tzbuf, trailing_tzbuf_format, + &"-"[XINT (zone) < 0], + hour, min, sec)); + zone_string = SSDATA (concat3 (leading, ENCODE_SYSTEM (abbr), + trailing)); + } } else xsignal2 (Qerror, build_string ("Invalid time zone specification"), @@ -181,6 +213,7 @@ tzlookup (Lisp_Object zone, bool settz) { block_input (); emacs_setenv_TZ (zone_string); + tzset (); timezone_t old_tz = local_tz; local_tz = new_tz; tzfree (old_tz); @@ -193,6 +226,12 @@ tzlookup (Lisp_Object zone, bool settz) void init_editfns (bool dumping) { +#if !defined CANNOT_DUMP && defined HAVE_TZSET + /* A valid but unlikely setting for the TZ environment variable. + It is OK (though a bit slower) if the user chooses this value. */ + static char dump_tz_string[] = "TZ=UtC0"; +#endif + const char *user_name; register char *p; struct passwd *pw; /* password entry for the current user */ @@ -1487,17 +1526,8 @@ static EMACS_INT hi_time (time_t t) { time_t hi = t >> LO_TIME_BITS; - - /* Check for overflow, helping the compiler for common cases where - no runtime check is needed, and taking care not to convert - negative numbers to unsigned before comparing them. */ - if (! ((! TYPE_SIGNED (time_t) - || MOST_NEGATIVE_FIXNUM <= TIME_T_MIN >> LO_TIME_BITS - || MOST_NEGATIVE_FIXNUM <= hi) - && (TIME_T_MAX >> LO_TIME_BITS <= MOST_POSITIVE_FIXNUM - || hi <= MOST_POSITIVE_FIXNUM))) + if (FIXNUM_OVERFLOW_P (hi)) time_overflow (); - return hi; } @@ -1559,7 +1589,7 @@ time_arith (Lisp_Object a, Lisp_Object b, struct lisp_time ta = lisp_time_struct (a, &alen); struct lisp_time tb = lisp_time_struct (b, &blen); struct lisp_time t = op (ta, tb); - if (! (MOST_NEGATIVE_FIXNUM <= t.hi && t.hi <= MOST_POSITIVE_FIXNUM)) + if (FIXNUM_OVERFLOW_P (t.hi)) time_overflow (); Lisp_Object val = Qnil; @@ -1824,7 +1854,7 @@ decode_time_components (Lisp_Object high, Lisp_Object low, Lisp_Object usec, if (result) { - if (! (MOST_NEGATIVE_FIXNUM <= hi && hi <= MOST_POSITIVE_FIXNUM)) + if (FIXNUM_OVERFLOW_P (hi)) return -1; result->hi = hi; result->lo = lo; @@ -1982,12 +2012,15 @@ emacs_nmemftime (char *s, size_t maxsize, const char *format, DEFUN ("format-time-string", Fformat_time_string, Sformat_time_string, 1, 3, 0, doc: /* Use FORMAT-STRING to format the time TIME, or now if omitted or nil. TIME is specified as (HIGH LOW USEC PSEC), as returned by -`current-time' or `file-attributes'. -It can also be a single integer number of seconds since the epoch. -The obsolete form (HIGH . LOW) is also still accepted. -The optional ZONE is omitted or nil for Emacs local time, -t for Universal Time, `wall' for system wall clock time, -or a string as in the TZ environment variable. +`current-time' or `file-attributes'. It can also be a single integer +number of seconds since the epoch. The obsolete form (HIGH . LOW) is +also still accepted. + +The optional ZONE is omitted or nil for Emacs local time, t for +Universal Time, `wall' for system wall clock time, or a string as in +the TZ environment variable. It can also be a list (as from +`current-time-zone') or an integer (as from `decode-time') applied +without consideration for daylight saving time. The value is a copy of FORMAT-STRING, but with certain constructs replaced by text that describes the specified date and time in TIME: @@ -2007,6 +2040,7 @@ by text that describes the specified date and time in TIME: %H is the hour on a 24-hour clock, %I is on a 12-hour clock, %k is like %H only blank-padded, %l is like %I blank-padded. %p is the locale's equivalent of either AM or PM. +%q is the calendar quarter (1–4). %M is the minute. %S is the second. %N is the nanosecond, %6N the microsecond, %3N the millisecond, etc. @@ -2058,7 +2092,6 @@ format_time_string (char const *format, ptrdiff_t formatlen, char *buf = buffer; ptrdiff_t size = sizeof buffer; size_t len; - Lisp_Object bufstring; int ns = t.tv_nsec; USE_SAFE_ALLOCA; @@ -2094,21 +2127,25 @@ format_time_string (char const *format, ptrdiff_t formatlen, } xtzfree (tz); - bufstring = make_unibyte_string (buf, len); + AUTO_STRING_WITH_LEN (bufstring, buf, len); + Lisp_Object result = code_convert_string_norecord (bufstring, + Vlocale_coding_system, 0); SAFE_FREE (); - return code_convert_string_norecord (bufstring, Vlocale_coding_system, 0); + return result; } DEFUN ("decode-time", Fdecode_time, Sdecode_time, 0, 2, 0, doc: /* Decode a time value as (SEC MINUTE HOUR DAY MONTH YEAR DOW DST UTCOFF). -The optional SPECIFIED-TIME should be a list of (HIGH LOW . IGNORED), +The optional TIME should be a list of (HIGH LOW . IGNORED), as from `current-time' and `file-attributes', or nil to use the -current time. -It can also be a single integer number of seconds since the epoch. -The obsolete form (HIGH . LOW) is also still accepted. +current time. It can also be a single integer number of seconds since +the epoch. The obsolete form (HIGH . LOW) is also still accepted. + The optional ZONE is omitted or nil for Emacs local time, t for Universal Time, `wall' for system wall clock time, or a string as in -the TZ environment variable. +the TZ environment variable. It can also be a list (as from +`current-time-zone') or an integer (as from `decode-time') applied +without consideration for daylight saving time. The list has the following nine members: SEC is an integer between 0 and 60; SEC is 60 for a leap second, which only some operating systems @@ -2155,22 +2192,22 @@ usage: (decode-time &optional TIME ZONE) */) } /* Return OBJ - OFFSET, checking that OBJ is a valid fixnum and that - the result is representable as an int. Assume OFFSET is small and - nonnegative. */ + the result is representable as an int. */ static int check_tm_member (Lisp_Object obj, int offset) { - EMACS_INT n; CHECK_NUMBER (obj); - n = XINT (obj); - if (! (INT_MIN + offset <= n && n - offset <= INT_MAX)) + EMACS_INT n = XINT (obj); + int result; + if (INT_SUBTRACT_WRAPV (n, offset, &result)) time_overflow (); - return n - offset; + return result; } DEFUN ("encode-time", Fencode_time, Sencode_time, 6, MANY, 0, doc: /* Convert SECOND, MINUTE, HOUR, DAY, MONTH, YEAR and ZONE to internal time. This is the reverse operation of `decode-time', which see. + The optional ZONE is omitted or nil for Emacs local time, t for Universal Time, `wall' for system wall clock time, or a string as in the TZ environment variable. It can also be a list (as from @@ -2205,8 +2242,6 @@ usage: (encode-time SECOND MINUTE HOUR DAY MONTH YEAR &optional ZONE) */) tm.tm_year = check_tm_member (args[5], TM_YEAR_BASE); tm.tm_isdst = -1; - if (CONSP (zone)) - zone = XCAR (zone); timezone_t tz = tzlookup (zone, false); value = emacs_mktime_z (tz, &tm); xtzfree (tz); @@ -2230,14 +2265,15 @@ which provide a much more powerful and general facility. If SPECIFIED-TIME is given, it is a time to format instead of the current time. The argument should have the form (HIGH LOW . IGNORED). Thus, you can use times obtained from `current-time' and from -`file-attributes'. SPECIFIED-TIME can also be a single integer -number of seconds since the epoch. -SPECIFIED-TIME can also have the form (HIGH . LOW), but this is -considered obsolete. +`file-attributes'. SPECIFIED-TIME can also be a single integer number +of seconds since the epoch. The obsolete form (HIGH . LOW) is also +still accepted. The optional ZONE is omitted or nil for Emacs local time, t for Universal Time, `wall' for system wall clock time, or a string as in -the TZ environment variable. */) +the TZ environment variable. It can also be a list (as from +`current-time-zone') or an integer (as from `decode-time') applied +without consideration for daylight saving time. */) (Lisp_Object specified_time, Lisp_Object zone) { time_t value = lisp_seconds_argument (specified_time); @@ -2312,10 +2348,14 @@ If SPECIFIED-TIME is given, the time zone offset is determined from it instead of using the current time. The argument should have the form \(HIGH LOW . IGNORED). Thus, you can use times obtained from `current-time' and from `file-attributes'. SPECIFIED-TIME can also be -a single integer number of seconds since the epoch. SPECIFIED-TIME can -also have the form (HIGH . LOW), but this is considered obsolete. -Optional second arg ZONE is omitted or nil for the local time zone, or -a string as in the TZ environment variable. +a single integer number of seconds since the epoch. The obsolete form +(HIGH . LOW) is also still accepted. + +The optional ZONE is omitted or nil for Emacs local time, t for +Universal Time, `wall' for system wall clock time, or a string as in +the TZ environment variable. It can also be a list (as from +`current-time-zone') or an integer (as from `decode-time') applied +without consideration for daylight saving time. Some operating systems cannot provide all this information to Emacs; in this case, `current-time-zone' returns a list containing nil for @@ -2342,15 +2382,18 @@ the data it can't find. */) zone_offset = make_number (offset); if (SCHARS (zone_name) == 0) { - /* No local time zone name is available; use "+-NNNN" instead. */ - long int m = offset / 60; - long int am = offset < 0 ? - m : m; - long int hour = am / 60; - int min = am % 60; - char buf[sizeof "+00" + INT_STRLEN_BOUND (long int)]; - zone_name = make_formatted_string (buf, "%c%02ld%02d", + /* No local time zone name is available; use numeric zone instead. */ + long int hour = offset / 3600; + int min_sec = offset % 3600; + int amin_sec = min_sec < 0 ? - min_sec : min_sec; + int min = amin_sec / 60; + int sec = amin_sec % 60; + int min_prec = min_sec ? 2 : 0; + int sec_prec = sec ? 2 : 0; + char buf[sizeof "+0000" + INT_STRLEN_BOUND (long int)]; + zone_name = make_formatted_string (buf, "%c%.2ld%.*d%.*d", (offset < 0 ? '-' : '+'), - hour, min); + hour, min_prec, min, sec_prec, sec); } } @@ -2359,11 +2402,11 @@ the data it can't find. */) DEFUN ("set-time-zone-rule", Fset_time_zone_rule, Sset_time_zone_rule, 1, 1, 0, doc: /* Set the Emacs local time zone using TZ, a string specifying a time zone rule. - If TZ is nil or `wall', use system wall clock time; this differs from the usual Emacs convention where nil means current local time. If TZ -is t, use Universal Time. If TZ is an integer, treat it as in -`encode-time'. +is t, use Universal Time. If TZ is a list (as from +`current-time-zone') or an integer (as from `decode-time'), use the +specified time zone without consideration for daylight saving time. Instead of calling this function, you typically want something else. To temporarily use a different time zone rule for just one invocation @@ -2436,23 +2479,24 @@ emacs_setenv_TZ (const char *tzstring) tzval[tzeqlen] = 0; } - if (new_tzvalbuf -#ifdef WINDOWSNT - /* MS-Windows implementation of 'putenv' copies the argument - string into a block it allocates, so modifying tzval string - does not change the environment. OTOH, the other threads run - by Emacs on MS-Windows never call 'xputenv' or 'putenv' or - 'unsetenv', so the original cause for the dicey in-place - modification technique doesn't exist there in the first - place. */ - || 1 + +#ifndef WINDOWSNT + /* Modifying *TZVAL merely requires calling tzset (which is the + caller's responsibility). However, modifying TZVAL requires + calling putenv; although this is not thread-safe, in practice this + runs only on startup when there is only one thread. */ + bool need_putenv = new_tzvalbuf; +#else + /* MS-Windows 'putenv' copies the argument string into a block it + allocates, so modifying *TZVAL will not change the environment. + However, the other threads run by Emacs on MS-Windows never call + 'xputenv' or 'putenv' or 'unsetenv', so the original cause for the + dicey in-place modification technique doesn't exist there in the + first place. */ + bool need_putenv = true; #endif - ) - { - /* Although this is not thread-safe, in practice this runs only - on startup when there is only one thread. */ - xputenv (tzval); - } + if (need_putenv) + xputenv (tzval); return 0; } @@ -3344,7 +3388,7 @@ It returns the number of characters changed. */) ptrdiff_t size; /* Size of translate table. */ ptrdiff_t pos, pos_byte, end_pos; bool multibyte = !NILP (BVAR (current_buffer, enable_multibyte_characters)); - bool string_multibyte IF_LINT (= 0); + bool string_multibyte UNINIT; validate_region (&start, &end); if (CHAR_TABLE_P (table)) @@ -3866,6 +3910,9 @@ precision specifier says how many decimal places to show; if zero, the decimal point itself is omitted. For %s and %S, the precision specifier truncates the string to the given width. +Text properties, if any, are copied from the format-string to the +produced text. + usage: (format STRING &rest OBJECTS) */) (ptrdiff_t nargs, Lisp_Object *args) { @@ -3877,10 +3924,9 @@ DEFUN ("format-message", Fformat_message, Sformat_message, 1, MANY, 0, The first argument is a format control string. The other arguments are substituted into it to make the result, a string. -This acts like `format', except it also replaces each left single -quotation mark (\\=‘) and grave accent (\\=`) by a left quote, and each -right single quotation mark (\\=’) and apostrophe (\\=') by a right quote. -The left and right quote replacement characters are specified by +This acts like `format', except it also replaces each grave accent (\\=`) +by a left quote, and each apostrophe (\\=') by a right quote. The left +and right quote replacement characters are specified by `text-quoting-style'. usage: (format-message STRING &rest OBJECTS) */) @@ -3900,7 +3946,7 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message) ptrdiff_t bufsize = sizeof initial_buffer; ptrdiff_t max_bufsize = STRING_BYTES_BOUND + 1; char *p; - ptrdiff_t buf_save_value_index IF_LINT (= 0); + ptrdiff_t buf_save_value_index UNINIT; char *format, *end; ptrdiff_t nchars; /* When we make a multibyte string, we must pay attention to the @@ -4159,6 +4205,7 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message) p += padding; nchars += padding; } + info[n].start = nchars; if (p > buf && multibyte @@ -4171,9 +4218,7 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message) nbytes, STRING_MULTIBYTE (args[n]), multibyte); - info[n].start = nchars; nchars += nchars_string; - info[n].end = nchars; if (minus_flag) { @@ -4181,6 +4226,7 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message) p += padding; nchars += padding; } + info[n].end = nchars; /* If this argument has text properties, record where in the result string it appears. */ @@ -4398,6 +4444,7 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message) exponent_bytes = src + sprintf_bytes - e; } + info[n].start = nchars; if (! minus_flag) { memset (p, ' ', padding); @@ -4420,9 +4467,7 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message) memcpy (p, src, exponent_bytes); p += exponent_bytes; - info[n].start = nchars; nchars += leading_zeros + sprintf_bytes + trailing_zeros; - info[n].end = nchars; if (minus_flag) { @@ -4430,6 +4475,7 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message) p += padding; nchars += padding; } + info[n].end = nchars; continue; } @@ -4437,14 +4483,6 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message) } else { - /* Named constants for the UTF-8 encodings of U+2018 LEFT SINGLE - QUOTATION MARK and U+2019 RIGHT SINGLE QUOTATION MARK. */ - enum - { - uLSQM0 = 0xE2, uLSQM1 = 0x80, uLSQM2 = 0x98, - /* uRSQM0 = 0xE2, uRSQM1 = 0x80, */ uRSQM2 = 0x99 - }; - unsigned char str[MAX_MULTIBYTE_LENGTH]; if ((format_char == '`' || format_char == '\'') @@ -4460,18 +4498,6 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message) } else if (format_char == '`' && quoting_style == STRAIGHT_QUOTING_STYLE) convsrc = "'"; - else if (format_char == uLSQM0 && CURVE_QUOTING_STYLE < quoting_style - && multibyte_format - && (unsigned char) format[0] == uLSQM1 - && ((unsigned char) format[1] == uLSQM2 - || (unsigned char) format[1] == uRSQM2)) - { - convsrc = (((unsigned char) format[1] == uLSQM2 - && quoting_style == GRAVE_QUOTING_STYLE) - ? "`" : "'"); - format += 2; - memset (&discarded[format0 + 1 - format_start], 2, 2); - } else { /* Copy a single character from format to buf. */ @@ -4629,7 +4655,7 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message) len = make_number (SCHARS (args[i])); Lisp_Object new_len = make_number (info[i].end - info[i].start); props = text_property_list (args[i], make_number (0), len, Qnil); - props = extend_property_ranges (props, new_len); + props = extend_property_ranges (props, len, new_len); /* If successive arguments have properties, be sure that the value of `composition' property be the copy. */ if (1 < i && info[i - 1].end) @@ -5056,6 +5082,14 @@ Transposing beyond buffer boundaries is an error. */) start2_byte, start2_byte + len2_byte); fix_start_end_in_overlays (start1, end2); } + else + { + /* The character positions of the markers remain intact, but we + still need to update their byte positions, because the + transposed regions might include multibyte sequences which + make some original byte positions of the markers invalid. */ + adjust_markers_bytepos (start1, start1_byte, end2, end2_byte, 0); + } signal_after_change (start1, end2 - start1, end2 - start1); return Qnil; |