summaryrefslogtreecommitdiff
path: root/src/editfns.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/editfns.c')
-rw-r--r--src/editfns.c258
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;