diff options
Diffstat (limited to 'src/editfns.c')
| -rw-r--r-- | src/editfns.c | 420 |
1 files changed, 204 insertions, 216 deletions
diff --git a/src/editfns.c b/src/editfns.c index a906aead09a..be1062dbbc5 100644 --- a/src/editfns.c +++ b/src/editfns.c | |||
| @@ -64,11 +64,17 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */ | |||
| 64 | extern Lisp_Object w32_get_internal_run_time (void); | 64 | extern Lisp_Object w32_get_internal_run_time (void); |
| 65 | #endif | 65 | #endif |
| 66 | 66 | ||
| 67 | static void set_time_zone_rule (char const *); | ||
| 67 | static Lisp_Object format_time_string (char const *, ptrdiff_t, struct timespec, | 68 | static Lisp_Object format_time_string (char const *, ptrdiff_t, struct timespec, |
| 68 | bool, struct tm *); | 69 | bool, struct tm *); |
| 70 | static long int tm_gmtoff (struct tm *); | ||
| 69 | static int tm_diff (struct tm *, struct tm *); | 71 | static int tm_diff (struct tm *, struct tm *); |
| 70 | static void update_buffer_properties (ptrdiff_t, ptrdiff_t); | 72 | static void update_buffer_properties (ptrdiff_t, ptrdiff_t); |
| 71 | 73 | ||
| 74 | #ifndef HAVE_TM_GMTOFF | ||
| 75 | # define HAVE_TM_GMTOFF false | ||
| 76 | #endif | ||
| 77 | |||
| 72 | static Lisp_Object Qbuffer_access_fontify_functions; | 78 | static Lisp_Object Qbuffer_access_fontify_functions; |
| 73 | 79 | ||
| 74 | /* Symbol for the text property used to mark fields. */ | 80 | /* Symbol for the text property used to mark fields. */ |
| @@ -79,15 +85,12 @@ Lisp_Object Qfield; | |||
| 79 | 85 | ||
| 80 | static Lisp_Object Qboundary; | 86 | static Lisp_Object Qboundary; |
| 81 | 87 | ||
| 82 | /* The startup value of the TZ environment variable so it can be | 88 | /* The startup value of the TZ environment variable; null if unset. */ |
| 83 | restored if the user calls set-time-zone-rule with a nil | ||
| 84 | argument. If null, the TZ environment variable was unset. */ | ||
| 85 | static char const *initial_tz; | 89 | static char const *initial_tz; |
| 86 | 90 | ||
| 87 | /* True if the static variable tzvalbuf (defined in | 91 | /* A valid but unlikely setting for the TZ environment variable. |
| 88 | set_time_zone_rule) is part of 'environ'. */ | 92 | It is OK (though a bit slower) if the user chooses this value. */ |
| 89 | static bool tzvalbuf_in_environ; | 93 | static char dump_tz_string[] = "TZ=UtC0"; |
| 90 | |||
| 91 | 94 | ||
| 92 | void | 95 | void |
| 93 | init_editfns (void) | 96 | init_editfns (void) |
| @@ -101,13 +104,38 @@ init_editfns (void) | |||
| 101 | init_system_name (); | 104 | init_system_name (); |
| 102 | 105 | ||
| 103 | #ifndef CANNOT_DUMP | 106 | #ifndef CANNOT_DUMP |
| 104 | /* Don't bother with this on initial start when just dumping out */ | 107 | /* When just dumping out, set the time zone to a known unlikely value |
| 108 | and skip the rest of this function. */ | ||
| 105 | if (!initialized) | 109 | if (!initialized) |
| 106 | return; | 110 | { |
| 107 | #endif /* not CANNOT_DUMP */ | 111 | # ifdef HAVE_TZSET |
| 112 | xputenv (dump_tz_string); | ||
| 113 | tzset (); | ||
| 114 | # endif | ||
| 115 | return; | ||
| 116 | } | ||
| 117 | #endif | ||
| 108 | 118 | ||
| 109 | initial_tz = getenv ("TZ"); | 119 | char *tz = getenv ("TZ"); |
| 110 | tzvalbuf_in_environ = 0; | 120 | initial_tz = tz; |
| 121 | |||
| 122 | #if !defined CANNOT_DUMP && defined HAVE_TZSET | ||
| 123 | /* If the execution TZ happens to be the same as the dump TZ, | ||
| 124 | change it to some other value and then change it back, | ||
| 125 | to force the underlying implementation to reload the TZ info. | ||
| 126 | This is needed on implementations that load TZ info from files, | ||
| 127 | since the TZ file contents may differ between dump and execution. */ | ||
| 128 | if (tz && strcmp (tz, &dump_tz_string[sizeof "TZ=" - 1]) == 0) | ||
| 129 | { | ||
| 130 | ++*tz; | ||
| 131 | tzset (); | ||
| 132 | --*tz; | ||
| 133 | } | ||
| 134 | #endif | ||
| 135 | |||
| 136 | /* Call set_time_zone_rule now, so that its call to putenv is done | ||
| 137 | before multiple threads are active. */ | ||
| 138 | set_time_zone_rule (tz); | ||
| 111 | 139 | ||
| 112 | pw = getpwuid (getuid ()); | 140 | pw = getpwuid (getuid ()); |
| 113 | #ifdef MSDOS | 141 | #ifdef MSDOS |
| @@ -376,13 +404,14 @@ at POSITION. */) | |||
| 376 | set_buffer_temp (XBUFFER (object)); | 404 | set_buffer_temp (XBUFFER (object)); |
| 377 | 405 | ||
| 378 | /* First try with room for 40 overlays. */ | 406 | /* First try with room for 40 overlays. */ |
| 379 | noverlays = 40; | 407 | Lisp_Object overlay_vecbuf[40]; |
| 380 | overlay_vec = alloca (noverlays * sizeof *overlay_vec); | 408 | noverlays = ARRAYELTS (overlay_vecbuf); |
| 409 | overlay_vec = overlay_vecbuf; | ||
| 381 | noverlays = overlays_around (posn, overlay_vec, noverlays); | 410 | noverlays = overlays_around (posn, overlay_vec, noverlays); |
| 382 | 411 | ||
| 383 | /* If there are more than 40, | 412 | /* If there are more than 40, |
| 384 | make enough space for all, and try again. */ | 413 | make enough space for all, and try again. */ |
| 385 | if (noverlays > 40) | 414 | if (ARRAYELTS (overlay_vecbuf) < noverlays) |
| 386 | { | 415 | { |
| 387 | SAFE_ALLOCA_LISP (overlay_vec, noverlays); | 416 | SAFE_ALLOCA_LISP (overlay_vec, noverlays); |
| 388 | noverlays = overlays_around (posn, overlay_vec, noverlays); | 417 | noverlays = overlays_around (posn, overlay_vec, noverlays); |
| @@ -1325,17 +1354,16 @@ name, or nil if there is no such user. */) | |||
| 1325 | /* Substitute the login name for the &, upcasing the first character. */ | 1354 | /* Substitute the login name for the &, upcasing the first character. */ |
| 1326 | if (q) | 1355 | if (q) |
| 1327 | { | 1356 | { |
| 1328 | register char *r; | 1357 | Lisp_Object login = Fuser_login_name (make_number (pw->pw_uid)); |
| 1329 | Lisp_Object login; | 1358 | USE_SAFE_ALLOCA; |
| 1330 | 1359 | char *r = SAFE_ALLOCA (strlen (p) + SBYTES (login) + 1); | |
| 1331 | login = Fuser_login_name (make_number (pw->pw_uid)); | ||
| 1332 | r = alloca (strlen (p) + SCHARS (login) + 1); | ||
| 1333 | memcpy (r, p, q - p); | 1360 | memcpy (r, p, q - p); |
| 1334 | r[q - p] = 0; | 1361 | r[q - p] = 0; |
| 1335 | strcat (r, SSDATA (login)); | 1362 | strcat (r, SSDATA (login)); |
| 1336 | r[q - p] = upcase ((unsigned char) r[q - p]); | 1363 | r[q - p] = upcase ((unsigned char) r[q - p]); |
| 1337 | strcat (r, q + 1); | 1364 | strcat (r, q + 1); |
| 1338 | full = build_string (r); | 1365 | full = build_string (r); |
| 1366 | SAFE_FREE (); | ||
| 1339 | } | 1367 | } |
| 1340 | #endif /* AMPERSAND_FULL_NAME */ | 1368 | #endif /* AMPERSAND_FULL_NAME */ |
| 1341 | 1369 | ||
| @@ -1373,6 +1401,30 @@ time_overflow (void) | |||
| 1373 | error ("Specified time is not representable"); | 1401 | error ("Specified time is not representable"); |
| 1374 | } | 1402 | } |
| 1375 | 1403 | ||
| 1404 | /* A substitute for mktime_z on platforms that lack it. It's not | ||
| 1405 | thread-safe, but should be good enough for Emacs in typical use. */ | ||
| 1406 | #ifndef HAVE_TZALLOC | ||
| 1407 | time_t | ||
| 1408 | mktime_z (timezone_t tz, struct tm *tm) | ||
| 1409 | { | ||
| 1410 | char *oldtz = getenv ("TZ"); | ||
| 1411 | USE_SAFE_ALLOCA; | ||
| 1412 | if (oldtz) | ||
| 1413 | { | ||
| 1414 | size_t oldtzsize = strlen (oldtz) + 1; | ||
| 1415 | char *oldtzcopy = SAFE_ALLOCA (oldtzsize); | ||
| 1416 | oldtz = strcpy (oldtzcopy, oldtz); | ||
| 1417 | } | ||
| 1418 | block_input (); | ||
| 1419 | set_time_zone_rule (tz); | ||
| 1420 | time_t t = mktime (tm); | ||
| 1421 | set_time_zone_rule (oldtz); | ||
| 1422 | unblock_input (); | ||
| 1423 | SAFE_FREE (); | ||
| 1424 | return t; | ||
| 1425 | } | ||
| 1426 | #endif | ||
| 1427 | |||
| 1376 | /* Return the upper part of the time T (everything but the bottom 16 bits). */ | 1428 | /* Return the upper part of the time T (everything but the bottom 16 bits). */ |
| 1377 | static EMACS_INT | 1429 | static EMACS_INT |
| 1378 | hi_time (time_t t) | 1430 | hi_time (time_t t) |
| @@ -1768,39 +1820,28 @@ format_time_string (char const *format, ptrdiff_t formatlen, | |||
| 1768 | size_t len; | 1820 | size_t len; |
| 1769 | Lisp_Object bufstring; | 1821 | Lisp_Object bufstring; |
| 1770 | int ns = t.tv_nsec; | 1822 | int ns = t.tv_nsec; |
| 1771 | struct tm *tm; | ||
| 1772 | USE_SAFE_ALLOCA; | 1823 | USE_SAFE_ALLOCA; |
| 1773 | 1824 | ||
| 1774 | while (1) | 1825 | tmp = ut ? gmtime_r (&t.tv_sec, tmp) : localtime_r (&t.tv_sec, tmp); |
| 1775 | { | 1826 | if (! tmp) |
| 1776 | time_t *taddr = &t.tv_sec; | 1827 | time_overflow (); |
| 1777 | block_input (); | 1828 | synchronize_system_time_locale (); |
| 1778 | |||
| 1779 | synchronize_system_time_locale (); | ||
| 1780 | |||
| 1781 | tm = ut ? gmtime (taddr) : localtime (taddr); | ||
| 1782 | if (! tm) | ||
| 1783 | { | ||
| 1784 | unblock_input (); | ||
| 1785 | time_overflow (); | ||
| 1786 | } | ||
| 1787 | *tmp = *tm; | ||
| 1788 | 1829 | ||
| 1830 | while (true) | ||
| 1831 | { | ||
| 1789 | buf[0] = '\1'; | 1832 | buf[0] = '\1'; |
| 1790 | len = emacs_nmemftime (buf, size, format, formatlen, tm, ut, ns); | 1833 | len = emacs_nmemftime (buf, size, format, formatlen, tmp, ut, ns); |
| 1791 | if ((0 < len && len < size) || (len == 0 && buf[0] == '\0')) | 1834 | if ((0 < len && len < size) || (len == 0 && buf[0] == '\0')) |
| 1792 | break; | 1835 | break; |
| 1793 | 1836 | ||
| 1794 | /* Buffer was too small, so make it bigger and try again. */ | 1837 | /* Buffer was too small, so make it bigger and try again. */ |
| 1795 | len = emacs_nmemftime (NULL, SIZE_MAX, format, formatlen, tm, ut, ns); | 1838 | len = emacs_nmemftime (NULL, SIZE_MAX, format, formatlen, tmp, ut, ns); |
| 1796 | unblock_input (); | ||
| 1797 | if (STRING_BYTES_BOUND <= len) | 1839 | if (STRING_BYTES_BOUND <= len) |
| 1798 | string_overflow (); | 1840 | string_overflow (); |
| 1799 | size = len + 1; | 1841 | size = len + 1; |
| 1800 | buf = SAFE_ALLOCA (size); | 1842 | buf = SAFE_ALLOCA (size); |
| 1801 | } | 1843 | } |
| 1802 | 1844 | ||
| 1803 | unblock_input (); | ||
| 1804 | bufstring = make_unibyte_string (buf, len); | 1845 | bufstring = make_unibyte_string (buf, len); |
| 1805 | SAFE_FREE (); | 1846 | SAFE_FREE (); |
| 1806 | return code_convert_string_norecord (bufstring, Vlocale_coding_system, 0); | 1847 | return code_convert_string_norecord (bufstring, Vlocale_coding_system, 0); |
| @@ -1824,38 +1865,30 @@ DOW and ZONE.) */) | |||
| 1824 | (Lisp_Object specified_time) | 1865 | (Lisp_Object specified_time) |
| 1825 | { | 1866 | { |
| 1826 | time_t time_spec = lisp_seconds_argument (specified_time); | 1867 | time_t time_spec = lisp_seconds_argument (specified_time); |
| 1827 | struct tm save_tm; | 1868 | struct tm local_tm, gmt_tm; |
| 1828 | struct tm *decoded_time; | ||
| 1829 | Lisp_Object list_args[9]; | ||
| 1830 | 1869 | ||
| 1831 | block_input (); | 1870 | if (! (localtime_r (&time_spec, &local_tm) |
| 1832 | decoded_time = localtime (&time_spec); | 1871 | && MOST_NEGATIVE_FIXNUM - TM_YEAR_BASE <= local_tm.tm_year |
| 1833 | if (decoded_time) | 1872 | && local_tm.tm_year <= MOST_POSITIVE_FIXNUM - TM_YEAR_BASE)) |
| 1834 | save_tm = *decoded_time; | ||
| 1835 | unblock_input (); | ||
| 1836 | if (! (decoded_time | ||
| 1837 | && MOST_NEGATIVE_FIXNUM - TM_YEAR_BASE <= save_tm.tm_year | ||
| 1838 | && save_tm.tm_year <= MOST_POSITIVE_FIXNUM - TM_YEAR_BASE)) | ||
| 1839 | time_overflow (); | 1873 | time_overflow (); |
| 1840 | XSETFASTINT (list_args[0], save_tm.tm_sec); | ||
| 1841 | XSETFASTINT (list_args[1], save_tm.tm_min); | ||
| 1842 | XSETFASTINT (list_args[2], save_tm.tm_hour); | ||
| 1843 | XSETFASTINT (list_args[3], save_tm.tm_mday); | ||
| 1844 | XSETFASTINT (list_args[4], save_tm.tm_mon + 1); | ||
| 1845 | /* On 64-bit machines an int is narrower than EMACS_INT, thus the | ||
| 1846 | cast below avoids overflow in int arithmetics. */ | ||
| 1847 | XSETINT (list_args[5], TM_YEAR_BASE + (EMACS_INT) save_tm.tm_year); | ||
| 1848 | XSETFASTINT (list_args[6], save_tm.tm_wday); | ||
| 1849 | list_args[7] = save_tm.tm_isdst ? Qt : Qnil; | ||
| 1850 | 1874 | ||
| 1851 | block_input (); | 1875 | /* Avoid overflow when INT_MAX < EMACS_INT_MAX. */ |
| 1852 | decoded_time = gmtime (&time_spec); | 1876 | EMACS_INT tm_year_base = TM_YEAR_BASE; |
| 1853 | if (decoded_time == 0) | 1877 | |
| 1854 | list_args[8] = Qnil; | 1878 | return Flist (9, ((Lisp_Object []) |
| 1855 | else | 1879 | {make_number (local_tm.tm_sec), |
| 1856 | XSETINT (list_args[8], tm_diff (&save_tm, decoded_time)); | 1880 | make_number (local_tm.tm_min), |
| 1857 | unblock_input (); | 1881 | make_number (local_tm.tm_hour), |
| 1858 | return Flist (9, list_args); | 1882 | make_number (local_tm.tm_mday), |
| 1883 | make_number (local_tm.tm_mon + 1), | ||
| 1884 | make_number (local_tm.tm_year + tm_year_base), | ||
| 1885 | make_number (local_tm.tm_wday), | ||
| 1886 | local_tm.tm_isdst ? Qt : Qnil, | ||
| 1887 | (HAVE_TM_GMTOFF | ||
| 1888 | ? make_number (tm_gmtoff (&local_tm)) | ||
| 1889 | : gmtime_r (&time_spec, &gmt_tm) | ||
| 1890 | ? make_number (tm_diff (&local_tm, &gmt_tm)) | ||
| 1891 | : Qnil)})); | ||
| 1859 | } | 1892 | } |
| 1860 | 1893 | ||
| 1861 | /* Return OBJ - OFFSET, checking that OBJ is a valid fixnum and that | 1894 | /* Return OBJ - OFFSET, checking that OBJ is a valid fixnum and that |
| @@ -1911,18 +1944,12 @@ usage: (encode-time SECOND MINUTE HOUR DAY MONTH YEAR &optional ZONE) */) | |||
| 1911 | if (CONSP (zone)) | 1944 | if (CONSP (zone)) |
| 1912 | zone = XCAR (zone); | 1945 | zone = XCAR (zone); |
| 1913 | if (NILP (zone)) | 1946 | if (NILP (zone)) |
| 1914 | { | 1947 | value = mktime (&tm); |
| 1915 | block_input (); | ||
| 1916 | value = mktime (&tm); | ||
| 1917 | unblock_input (); | ||
| 1918 | } | ||
| 1919 | else | 1948 | else |
| 1920 | { | 1949 | { |
| 1921 | static char const tzbuf_format[] = "XXX%s%"pI"d:%02d:%02d"; | 1950 | static char const tzbuf_format[] = "XXX%s%"pI"d:%02d:%02d"; |
| 1922 | char tzbuf[sizeof tzbuf_format + INT_STRLEN_BOUND (EMACS_INT)]; | 1951 | char tzbuf[sizeof tzbuf_format + INT_STRLEN_BOUND (EMACS_INT)]; |
| 1923 | char *old_tzstring; | ||
| 1924 | const char *tzstring; | 1952 | const char *tzstring; |
| 1925 | USE_SAFE_ALLOCA; | ||
| 1926 | 1953 | ||
| 1927 | if (EQ (zone, Qt)) | 1954 | if (EQ (zone, Qt)) |
| 1928 | tzstring = "UTC0"; | 1955 | tzstring = "UTC0"; |
| @@ -1939,29 +1966,13 @@ usage: (encode-time SECOND MINUTE HOUR DAY MONTH YEAR &optional ZONE) */) | |||
| 1939 | tzstring = tzbuf; | 1966 | tzstring = tzbuf; |
| 1940 | } | 1967 | } |
| 1941 | else | 1968 | else |
| 1942 | error ("Invalid time zone specification"); | 1969 | tzstring = 0; |
| 1943 | |||
| 1944 | old_tzstring = getenv ("TZ"); | ||
| 1945 | if (old_tzstring) | ||
| 1946 | { | ||
| 1947 | char *buf = SAFE_ALLOCA (strlen (old_tzstring) + 1); | ||
| 1948 | old_tzstring = strcpy (buf, old_tzstring); | ||
| 1949 | } | ||
| 1950 | |||
| 1951 | block_input (); | ||
| 1952 | |||
| 1953 | /* Set TZ before calling mktime; merely adjusting mktime's returned | ||
| 1954 | value doesn't suffice, since that would mishandle leap seconds. */ | ||
| 1955 | set_time_zone_rule (tzstring); | ||
| 1956 | |||
| 1957 | value = mktime (&tm); | ||
| 1958 | 1970 | ||
| 1959 | set_time_zone_rule (old_tzstring); | 1971 | timezone_t tz = tzstring ? tzalloc (tzstring) : 0; |
| 1960 | #ifdef LOCALTIME_CACHE | 1972 | if (! tz) |
| 1961 | tzset (); | 1973 | error ("Invalid time zone specification"); |
| 1962 | #endif | 1974 | value = mktime_z (tz, &tm); |
| 1963 | unblock_input (); | 1975 | tzfree (tz); |
| 1964 | SAFE_FREE (); | ||
| 1965 | } | 1976 | } |
| 1966 | 1977 | ||
| 1967 | if (value == (time_t) -1) | 1978 | if (value == (time_t) -1) |
| @@ -1987,34 +1998,27 @@ but this is considered obsolete. */) | |||
| 1987 | (Lisp_Object specified_time) | 1998 | (Lisp_Object specified_time) |
| 1988 | { | 1999 | { |
| 1989 | time_t value = lisp_seconds_argument (specified_time); | 2000 | time_t value = lisp_seconds_argument (specified_time); |
| 1990 | struct tm *tm; | ||
| 1991 | char buf[sizeof "Mon Apr 30 12:49:17 " + INT_STRLEN_BOUND (int) + 1]; | ||
| 1992 | int len IF_LINT (= 0); | ||
| 1993 | 2001 | ||
| 1994 | /* Convert to a string in ctime format, except without the trailing | 2002 | /* Convert to a string in ctime format, except without the trailing |
| 1995 | newline, and without the 4-digit year limit. Don't use asctime | 2003 | newline, and without the 4-digit year limit. Don't use asctime |
| 1996 | or ctime, as they might dump core if the year is outside the | 2004 | or ctime, as they might dump core if the year is outside the |
| 1997 | range -999 .. 9999. */ | 2005 | range -999 .. 9999. */ |
| 1998 | block_input (); | 2006 | struct tm tm; |
| 1999 | tm = localtime (&value); | 2007 | if (! localtime_r (&value, &tm)) |
| 2000 | if (tm) | ||
| 2001 | { | ||
| 2002 | static char const wday_name[][4] = | ||
| 2003 | { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; | ||
| 2004 | static char const mon_name[][4] = | ||
| 2005 | { "Jan", "Feb", "Mar", "Apr", "May", "Jun", | ||
| 2006 | "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; | ||
| 2007 | printmax_t year_base = TM_YEAR_BASE; | ||
| 2008 | |||
| 2009 | len = sprintf (buf, "%s %s%3d %02d:%02d:%02d %"pMd, | ||
| 2010 | wday_name[tm->tm_wday], mon_name[tm->tm_mon], tm->tm_mday, | ||
| 2011 | tm->tm_hour, tm->tm_min, tm->tm_sec, | ||
| 2012 | tm->tm_year + year_base); | ||
| 2013 | } | ||
| 2014 | unblock_input (); | ||
| 2015 | if (! tm) | ||
| 2016 | time_overflow (); | 2008 | time_overflow (); |
| 2017 | 2009 | ||
| 2010 | static char const wday_name[][4] = | ||
| 2011 | { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; | ||
| 2012 | static char const mon_name[][4] = | ||
| 2013 | { "Jan", "Feb", "Mar", "Apr", "May", "Jun", | ||
| 2014 | "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; | ||
| 2015 | printmax_t year_base = TM_YEAR_BASE; | ||
| 2016 | char buf[sizeof "Mon Apr 30 12:49:17 " + INT_STRLEN_BOUND (int) + 1]; | ||
| 2017 | int len = sprintf (buf, "%s %s%3d %02d:%02d:%02d %"pMd, | ||
| 2018 | wday_name[tm.tm_wday], mon_name[tm.tm_mon], tm.tm_mday, | ||
| 2019 | tm.tm_hour, tm.tm_min, tm.tm_sec, | ||
| 2020 | tm.tm_year + year_base); | ||
| 2021 | |||
| 2018 | return make_unibyte_string (buf, len); | 2022 | return make_unibyte_string (buf, len); |
| 2019 | } | 2023 | } |
| 2020 | 2024 | ||
| @@ -2041,6 +2045,17 @@ tm_diff (struct tm *a, struct tm *b) | |||
| 2041 | + (a->tm_sec - b->tm_sec)); | 2045 | + (a->tm_sec - b->tm_sec)); |
| 2042 | } | 2046 | } |
| 2043 | 2047 | ||
| 2048 | /* Yield A's UTC offset, or an unspecified value if unknown. */ | ||
| 2049 | static long int | ||
| 2050 | tm_gmtoff (struct tm *a) | ||
| 2051 | { | ||
| 2052 | #if HAVE_TM_GMTOFF | ||
| 2053 | return a->tm_gmtoff; | ||
| 2054 | #else | ||
| 2055 | return 0; | ||
| 2056 | #endif | ||
| 2057 | } | ||
| 2058 | |||
| 2044 | DEFUN ("current-time-zone", Fcurrent_time_zone, Scurrent_time_zone, 0, 1, 0, | 2059 | DEFUN ("current-time-zone", Fcurrent_time_zone, Scurrent_time_zone, 0, 1, 0, |
| 2045 | doc: /* Return the offset and name for the local time zone. | 2060 | doc: /* Return the offset and name for the local time zone. |
| 2046 | This returns a list of the form (OFFSET NAME). | 2061 | This returns a list of the form (OFFSET NAME). |
| @@ -2059,32 +2074,30 @@ the data it can't find. */) | |||
| 2059 | (Lisp_Object specified_time) | 2074 | (Lisp_Object specified_time) |
| 2060 | { | 2075 | { |
| 2061 | struct timespec value; | 2076 | struct timespec value; |
| 2062 | int offset; | 2077 | struct tm local_tm, gmt_tm; |
| 2063 | struct tm *t; | ||
| 2064 | struct tm localtm; | ||
| 2065 | Lisp_Object zone_offset, zone_name; | 2078 | Lisp_Object zone_offset, zone_name; |
| 2066 | 2079 | ||
| 2067 | zone_offset = Qnil; | 2080 | zone_offset = Qnil; |
| 2068 | value = make_timespec (lisp_seconds_argument (specified_time), 0); | 2081 | value = make_timespec (lisp_seconds_argument (specified_time), 0); |
| 2069 | zone_name = format_time_string ("%Z", sizeof "%Z" - 1, value, 0, &localtm); | 2082 | zone_name = format_time_string ("%Z", sizeof "%Z" - 1, value, 0, &local_tm); |
| 2070 | block_input (); | ||
| 2071 | t = gmtime (&value.tv_sec); | ||
| 2072 | if (t) | ||
| 2073 | offset = tm_diff (&localtm, t); | ||
| 2074 | unblock_input (); | ||
| 2075 | 2083 | ||
| 2076 | if (t) | 2084 | if (HAVE_TM_GMTOFF || gmtime_r (&value.tv_sec, &gmt_tm)) |
| 2077 | { | 2085 | { |
| 2086 | long int offset = (HAVE_TM_GMTOFF | ||
| 2087 | ? tm_gmtoff (&local_tm) | ||
| 2088 | : tm_diff (&local_tm, &gmt_tm)); | ||
| 2078 | zone_offset = make_number (offset); | 2089 | zone_offset = make_number (offset); |
| 2079 | if (SCHARS (zone_name) == 0) | 2090 | if (SCHARS (zone_name) == 0) |
| 2080 | { | 2091 | { |
| 2081 | /* No local time zone name is available; use "+-NNNN" instead. */ | 2092 | /* No local time zone name is available; use "+-NNNN" instead. */ |
| 2082 | int m = offset / 60; | 2093 | long int m = offset / 60; |
| 2083 | int am = offset < 0 ? - m : m; | 2094 | long int am = offset < 0 ? - m : m; |
| 2084 | char buf[sizeof "+00" + INT_STRLEN_BOUND (int)]; | 2095 | long int hour = am / 60; |
| 2085 | zone_name = make_formatted_string (buf, "%c%02d%02d", | 2096 | int min = am % 60; |
| 2097 | char buf[sizeof "+00" + INT_STRLEN_BOUND (long int)]; | ||
| 2098 | zone_name = make_formatted_string (buf, "%c%02ld%02d", | ||
| 2086 | (offset < 0 ? '-' : '+'), | 2099 | (offset < 0 ? '-' : '+'), |
| 2087 | am / 60, am % 60); | 2100 | hour, min); |
| 2088 | } | 2101 | } |
| 2089 | } | 2102 | } |
| 2090 | 2103 | ||
| @@ -2123,12 +2136,12 @@ only the former. */) | |||
| 2123 | 2136 | ||
| 2124 | /* Set the local time zone rule to TZSTRING. | 2137 | /* Set the local time zone rule to TZSTRING. |
| 2125 | 2138 | ||
| 2126 | This function is not thread-safe, partly because putenv, unsetenv | 2139 | This function is not thread-safe, in theory because putenv is not, |
| 2127 | and tzset are not, and partly because of the static storage it | 2140 | but mostly because of the static storage it updates. Other threads |
| 2128 | updates. Other threads that invoke localtime etc. may be adversely | 2141 | that invoke localtime etc. may be adversely affected while this |
| 2129 | affected while this function is executing. */ | 2142 | function is executing. */ |
| 2130 | 2143 | ||
| 2131 | void | 2144 | static void |
| 2132 | set_time_zone_rule (const char *tzstring) | 2145 | set_time_zone_rule (const char *tzstring) |
| 2133 | { | 2146 | { |
| 2134 | /* A buffer holding a string of the form "TZ=value", intended | 2147 | /* A buffer holding a string of the form "TZ=value", intended |
| @@ -2137,75 +2150,47 @@ set_time_zone_rule (const char *tzstring) | |||
| 2137 | static ptrdiff_t tzvalbufsize; | 2150 | static ptrdiff_t tzvalbufsize; |
| 2138 | 2151 | ||
| 2139 | int tzeqlen = sizeof "TZ=" - 1; | 2152 | int tzeqlen = sizeof "TZ=" - 1; |
| 2153 | ptrdiff_t tzstringlen = tzstring ? strlen (tzstring) : 0; | ||
| 2154 | char *tzval = tzvalbuf; | ||
| 2155 | bool new_tzvalbuf = tzvalbufsize <= tzeqlen + tzstringlen; | ||
| 2140 | 2156 | ||
| 2141 | #ifdef LOCALTIME_CACHE | 2157 | if (new_tzvalbuf) |
| 2142 | /* These two values are known to load tz files in buggy implementations, | 2158 | { |
| 2143 | i.e., Solaris 1 executables running under either Solaris 1 or Solaris 2. | 2159 | /* Do not attempt to free the old tzvalbuf, since another thread |
| 2144 | Their values shouldn't matter in non-buggy implementations. | 2160 | may be using it. In practice, the first allocation is large |
| 2145 | We don't use string literals for these strings, | 2161 | enough and memory does not leak. */ |
| 2146 | since if a string in the environment is in readonly | 2162 | tzval = xpalloc (NULL, &tzvalbufsize, |
| 2147 | storage, it runs afoul of bugs in SVR4 and Solaris 2.3. | 2163 | tzeqlen + tzstringlen - tzvalbufsize + 1, -1, 1); |
| 2148 | See Sun bugs 1113095 and 1114114, ``Timezone routines | 2164 | tzvalbuf = tzval; |
| 2149 | improperly modify environment''. */ | 2165 | tzval[1] = 'Z'; |
| 2150 | 2166 | tzval[2] = '='; | |
| 2151 | static char set_time_zone_rule_tz[][sizeof "TZ=GMT+0"] | 2167 | } |
| 2152 | = { "TZ=GMT+0", "TZ=GMT+1" }; | ||
| 2153 | |||
| 2154 | /* In SunOS 4.1.3_U1 and 4.1.4, if TZ has a value like | ||
| 2155 | "US/Pacific" that loads a tz file, then changes to a value like | ||
| 2156 | "XXX0" that does not load a tz file, and then changes back to | ||
| 2157 | its original value, the last change is (incorrectly) ignored. | ||
| 2158 | Also, if TZ changes twice in succession to values that do | ||
| 2159 | not load a tz file, tzset can dump core (see Sun bug#1225179). | ||
| 2160 | The following code works around these bugs. */ | ||
| 2161 | 2168 | ||
| 2162 | if (tzstring) | 2169 | if (tzstring) |
| 2163 | { | 2170 | { |
| 2164 | /* Temporarily set TZ to a value that loads a tz file | 2171 | /* Modify TZVAL in place. Although this is dicey in a |
| 2165 | and that differs from tzstring. */ | 2172 | multithreaded environment, we know of no portable alternative. |
| 2166 | bool eq0 = strcmp (tzstring, set_time_zone_rule_tz[0] + tzeqlen) == 0; | 2173 | Calling putenv or setenv could crash some other thread. */ |
| 2167 | xputenv (set_time_zone_rule_tz[eq0]); | 2174 | tzval[0] = 'T'; |
| 2175 | strcpy (tzval + tzeqlen, tzstring); | ||
| 2168 | } | 2176 | } |
| 2169 | else | 2177 | else |
| 2170 | { | 2178 | { |
| 2171 | /* The implied tzstring is unknown, so temporarily set TZ to | 2179 | /* Turn 'TZ=whatever' into an empty environment variable 'tZ='. |
| 2172 | two different values that each load a tz file. */ | 2180 | Although this is also dicey, calling unsetenv here can crash Emacs. |
| 2173 | xputenv (set_time_zone_rule_tz[0]); | 2181 | See Bug#8705. */ |
| 2174 | tzset (); | 2182 | tzval[0] = 't'; |
| 2175 | xputenv (set_time_zone_rule_tz[1]); | 2183 | tzval[tzeqlen] = 0; |
| 2176 | } | 2184 | } |
| 2177 | tzset (); | ||
| 2178 | tzvalbuf_in_environ = 0; | ||
| 2179 | #endif | ||
| 2180 | 2185 | ||
| 2181 | if (!tzstring) | 2186 | if (new_tzvalbuf) |
| 2182 | { | 2187 | { |
| 2183 | unsetenv ("TZ"); | 2188 | /* Although this is not thread-safe, in practice this runs only |
| 2184 | tzvalbuf_in_environ = 0; | 2189 | on startup when there is only one thread. */ |
| 2185 | } | 2190 | xputenv (tzval); |
| 2186 | else | ||
| 2187 | { | ||
| 2188 | ptrdiff_t tzstringlen = strlen (tzstring); | ||
| 2189 | |||
| 2190 | if (tzvalbufsize <= tzeqlen + tzstringlen) | ||
| 2191 | { | ||
| 2192 | unsetenv ("TZ"); | ||
| 2193 | tzvalbuf_in_environ = 0; | ||
| 2194 | tzvalbuf = xpalloc (tzvalbuf, &tzvalbufsize, | ||
| 2195 | tzeqlen + tzstringlen - tzvalbufsize + 1, -1, 1); | ||
| 2196 | memcpy (tzvalbuf, "TZ=", tzeqlen); | ||
| 2197 | } | ||
| 2198 | |||
| 2199 | strcpy (tzvalbuf + tzeqlen, tzstring); | ||
| 2200 | |||
| 2201 | if (!tzvalbuf_in_environ) | ||
| 2202 | { | ||
| 2203 | xputenv (tzvalbuf); | ||
| 2204 | tzvalbuf_in_environ = 1; | ||
| 2205 | } | ||
| 2206 | } | 2191 | } |
| 2207 | 2192 | ||
| 2208 | #ifdef LOCALTIME_CACHE | 2193 | #ifdef HAVE_TZSET |
| 2209 | tzset (); | 2194 | tzset (); |
| 2210 | #endif | 2195 | #endif |
| 2211 | } | 2196 | } |
| @@ -3012,8 +2997,12 @@ static Lisp_Object | |||
| 3012 | check_translation (ptrdiff_t pos, ptrdiff_t pos_byte, ptrdiff_t end, | 2997 | check_translation (ptrdiff_t pos, ptrdiff_t pos_byte, ptrdiff_t end, |
| 3013 | Lisp_Object val) | 2998 | Lisp_Object val) |
| 3014 | { | 2999 | { |
| 3015 | int buf_size = 16, buf_used = 0; | 3000 | int initial_buf[16]; |
| 3016 | int *buf = alloca (sizeof (int) * buf_size); | 3001 | int *buf = initial_buf; |
| 3002 | ptrdiff_t buf_size = ARRAYELTS (initial_buf); | ||
| 3003 | int *bufalloc = 0; | ||
| 3004 | ptrdiff_t buf_used = 0; | ||
| 3005 | Lisp_Object result = Qnil; | ||
| 3017 | 3006 | ||
| 3018 | for (; CONSP (val); val = XCDR (val)) | 3007 | for (; CONSP (val); val = XCDR (val)) |
| 3019 | { | 3008 | { |
| @@ -3038,12 +3027,11 @@ check_translation (ptrdiff_t pos, ptrdiff_t pos_byte, ptrdiff_t end, | |||
| 3038 | 3027 | ||
| 3039 | if (buf_used == buf_size) | 3028 | if (buf_used == buf_size) |
| 3040 | { | 3029 | { |
| 3041 | int *newbuf; | 3030 | bufalloc = xpalloc (bufalloc, &buf_size, 1, -1, |
| 3042 | 3031 | sizeof *bufalloc); | |
| 3043 | buf_size += 16; | 3032 | if (buf == initial_buf) |
| 3044 | newbuf = alloca (sizeof (int) * buf_size); | 3033 | memcpy (bufalloc, buf, sizeof initial_buf); |
| 3045 | memcpy (newbuf, buf, sizeof (int) * buf_used); | 3034 | buf = bufalloc; |
| 3046 | buf = newbuf; | ||
| 3047 | } | 3035 | } |
| 3048 | buf[buf_used++] = STRING_CHAR_AND_LENGTH (p, len1); | 3036 | buf[buf_used++] = STRING_CHAR_AND_LENGTH (p, len1); |
| 3049 | pos_byte += len1; | 3037 | pos_byte += len1; |
| @@ -3052,10 +3040,15 @@ check_translation (ptrdiff_t pos, ptrdiff_t pos_byte, ptrdiff_t end, | |||
| 3052 | break; | 3040 | break; |
| 3053 | } | 3041 | } |
| 3054 | if (i == len) | 3042 | if (i == len) |
| 3055 | return XCAR (val); | 3043 | { |
| 3044 | result = XCAR (val); | ||
| 3045 | break; | ||
| 3046 | } | ||
| 3056 | } | 3047 | } |
| 3057 | } | 3048 | } |
| 3058 | return Qnil; | 3049 | |
| 3050 | xfree (bufalloc); | ||
| 3051 | return result; | ||
| 3059 | } | 3052 | } |
| 3060 | 3053 | ||
| 3061 | 3054 | ||
| @@ -4354,11 +4347,8 @@ usage: (format STRING &rest OBJECTS) */) | |||
| 4354 | Lisp_Object | 4347 | Lisp_Object |
| 4355 | format2 (const char *string1, Lisp_Object arg0, Lisp_Object arg1) | 4348 | format2 (const char *string1, Lisp_Object arg0, Lisp_Object arg1) |
| 4356 | { | 4349 | { |
| 4357 | Lisp_Object args[3]; | 4350 | AUTO_STRING (format, string1); |
| 4358 | args[0] = build_string (string1); | 4351 | return Fformat (3, (Lisp_Object []) {format, arg0, arg1}); |
| 4359 | args[1] = arg0; | ||
| 4360 | args[2] = arg1; | ||
| 4361 | return Fformat (3, args); | ||
| 4362 | } | 4352 | } |
| 4363 | 4353 | ||
| 4364 | DEFUN ("char-equal", Fchar_equal, Schar_equal, 2, 2, 0, | 4354 | DEFUN ("char-equal", Fchar_equal, Schar_equal, 2, 2, 0, |
| @@ -4617,11 +4607,11 @@ Transposing beyond buffer boundaries is an error. */) | |||
| 4617 | if (tmp_interval3) | 4607 | if (tmp_interval3) |
| 4618 | set_text_properties_1 (startr1, endr2, Qnil, buf, tmp_interval3); | 4608 | set_text_properties_1 (startr1, endr2, Qnil, buf, tmp_interval3); |
| 4619 | 4609 | ||
| 4610 | USE_SAFE_ALLOCA; | ||
| 4611 | |||
| 4620 | /* First region smaller than second. */ | 4612 | /* First region smaller than second. */ |
| 4621 | if (len1_byte < len2_byte) | 4613 | if (len1_byte < len2_byte) |
| 4622 | { | 4614 | { |
| 4623 | USE_SAFE_ALLOCA; | ||
| 4624 | |||
| 4625 | temp = SAFE_ALLOCA (len2_byte); | 4615 | temp = SAFE_ALLOCA (len2_byte); |
| 4626 | 4616 | ||
| 4627 | /* Don't precompute these addresses. We have to compute them | 4617 | /* Don't precompute these addresses. We have to compute them |
| @@ -4633,21 +4623,19 @@ Transposing beyond buffer boundaries is an error. */) | |||
| 4633 | memcpy (temp, start2_addr, len2_byte); | 4623 | memcpy (temp, start2_addr, len2_byte); |
| 4634 | memcpy (start1_addr + len2_byte, start1_addr, len1_byte); | 4624 | memcpy (start1_addr + len2_byte, start1_addr, len1_byte); |
| 4635 | memcpy (start1_addr, temp, len2_byte); | 4625 | memcpy (start1_addr, temp, len2_byte); |
| 4636 | SAFE_FREE (); | ||
| 4637 | } | 4626 | } |
| 4638 | else | 4627 | else |
| 4639 | /* First region not smaller than second. */ | 4628 | /* First region not smaller than second. */ |
| 4640 | { | 4629 | { |
| 4641 | USE_SAFE_ALLOCA; | ||
| 4642 | |||
| 4643 | temp = SAFE_ALLOCA (len1_byte); | 4630 | temp = SAFE_ALLOCA (len1_byte); |
| 4644 | start1_addr = BYTE_POS_ADDR (start1_byte); | 4631 | start1_addr = BYTE_POS_ADDR (start1_byte); |
| 4645 | start2_addr = BYTE_POS_ADDR (start2_byte); | 4632 | start2_addr = BYTE_POS_ADDR (start2_byte); |
| 4646 | memcpy (temp, start1_addr, len1_byte); | 4633 | memcpy (temp, start1_addr, len1_byte); |
| 4647 | memcpy (start1_addr, start2_addr, len2_byte); | 4634 | memcpy (start1_addr, start2_addr, len2_byte); |
| 4648 | memcpy (start1_addr + len2_byte, temp, len1_byte); | 4635 | memcpy (start1_addr + len2_byte, temp, len1_byte); |
| 4649 | SAFE_FREE (); | ||
| 4650 | } | 4636 | } |
| 4637 | |||
| 4638 | SAFE_FREE (); | ||
| 4651 | graft_intervals_into_buffer (tmp_interval1, start1 + len2, | 4639 | graft_intervals_into_buffer (tmp_interval1, start1 + len2, |
| 4652 | len1, current_buffer, 0); | 4640 | len1, current_buffer, 0); |
| 4653 | graft_intervals_into_buffer (tmp_interval2, start1, | 4641 | graft_intervals_into_buffer (tmp_interval2, start1, |