summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rwxr-xr-xsrc/auto/configure94
-rw-r--r--src/config.h.in1
-rw-r--r--src/configure.ac40
-rw-r--r--src/drawscreen.c8
-rw-r--r--src/errors.h12
-rw-r--r--src/evalfunc.c18
-rw-r--r--src/ex_cmds.c10
-rw-r--r--src/ex_getln.c8
-rw-r--r--src/match.c16
-rw-r--r--src/os_mac.h52
-rw-r--r--src/os_macosx.m176
-rw-r--r--src/os_unix.c214
-rw-r--r--src/os_win32.c86
-rw-r--r--src/proto/os_unix.pro4
-rw-r--r--src/proto/os_win32.pro4
-rw-r--r--src/proto/regexp.pro4
-rw-r--r--src/quickfix.c2
-rw-r--r--src/regexp.c31
-rw-r--r--src/regexp.h2
-rw-r--r--src/regexp_bt.c54
-rw-r--r--src/regexp_nfa.c46
-rw-r--r--src/screen.c5
-rw-r--r--src/search.c82
-rw-r--r--src/structs.h5
-rw-r--r--src/syntax.c25
-rw-r--r--src/testdir/test_hlsearch.vim11
-rw-r--r--src/testdir/test_search.vim26
-rw-r--r--src/testdir/test_syntax.vim15
-rw-r--r--src/version.c2
29 files changed, 811 insertions, 242 deletions
diff --git a/src/auto/configure b/src/auto/configure
index 8ed46c85f4..b261631ef4 100755
--- a/src/auto/configure
+++ b/src/auto/configure
@@ -762,7 +762,6 @@ infodir
docdir
oldincludedir
includedir
-runstatedir
localstatedir
sharedstatedir
sysconfdir
@@ -898,7 +897,6 @@ datadir='${datarootdir}'
sysconfdir='${prefix}/etc'
sharedstatedir='${prefix}/com'
localstatedir='${prefix}/var'
-runstatedir='${localstatedir}/run'
includedir='${prefix}/include'
oldincludedir='/usr/include'
docdir='${datarootdir}/doc/${PACKAGE}'
@@ -1151,15 +1149,6 @@ do
| -silent | --silent | --silen | --sile | --sil)
silent=yes ;;
- -runstatedir | --runstatedir | --runstatedi | --runstated \
- | --runstate | --runstat | --runsta | --runst | --runs \
- | --run | --ru | --r)
- ac_prev=runstatedir ;;
- -runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \
- | --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \
- | --run=* | --ru=* | --r=*)
- runstatedir=$ac_optarg ;;
-
-sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
ac_prev=sbindir ;;
-sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
@@ -1297,7 +1286,7 @@ fi
for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \
datadir sysconfdir sharedstatedir localstatedir includedir \
oldincludedir docdir infodir htmldir dvidir pdfdir psdir \
- libdir localedir mandir runstatedir
+ libdir localedir mandir
do
eval ac_val=\$$ac_var
# Remove trailing slashes.
@@ -1450,7 +1439,6 @@ Fine tuning of the installation directories:
--sysconfdir=DIR read-only single-machine data [PREFIX/etc]
--sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com]
--localstatedir=DIR modifiable single-machine data [PREFIX/var]
- --runstatedir=DIR modifiable per-process data [LOCALSTATEDIR/run]
--libdir=DIR object code libraries [EPREFIX/lib]
--includedir=DIR C header files [PREFIX/include]
--oldincludedir=DIR C header files for non-gcc [/usr/include]
@@ -12737,7 +12725,7 @@ else
We can't simply define LARGE_OFF_T to be 9223372036854775807,
since some C++ compilers masquerading as C compilers
incorrectly reject 9223372036854775807. */
-#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31))
+#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
&& LARGE_OFF_T % 2147483647 == 1)
? 1 : -1];
@@ -12783,7 +12771,7 @@ else
We can't simply define LARGE_OFF_T to be 9223372036854775807,
since some C++ compilers masquerading as C compilers
incorrectly reject 9223372036854775807. */
-#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31))
+#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
&& LARGE_OFF_T % 2147483647 == 1)
? 1 : -1];
@@ -12807,7 +12795,7 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
We can't simply define LARGE_OFF_T to be 9223372036854775807,
since some C++ compilers masquerading as C compilers
incorrectly reject 9223372036854775807. */
-#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31))
+#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
&& LARGE_OFF_T % 2147483647 == 1)
? 1 : -1];
@@ -12852,7 +12840,7 @@ else
We can't simply define LARGE_OFF_T to be 9223372036854775807,
since some C++ compilers masquerading as C compilers
incorrectly reject 9223372036854775807. */
-#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31))
+#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
&& LARGE_OFF_T % 2147483647 == 1)
? 1 : -1];
@@ -12876,7 +12864,7 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
We can't simply define LARGE_OFF_T to be 9223372036854775807,
since some C++ compilers masquerading as C compilers
incorrectly reject 9223372036854775807. */
-#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31))
+#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
&& LARGE_OFF_T % 2147483647 == 1)
? 1 : -1];
@@ -13080,6 +13068,76 @@ $as_echo "no" >&6; }
fi
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for timer_create" >&5
+$as_echo_n "checking for timer_create... " >&6; }
+save_LIBS="$LIBS"
+LIBS="$LIBS -lrt"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+#include<signal.h>
+#include<time.h>
+static void set_flag(union sigval) {}
+
+int
+main ()
+{
+
+ struct timespec ts;
+ struct sigevent action = {0};
+ timer_t timer_id;
+
+ action.sigev_notify = SIGEV_THREAD;
+ action.sigev_notify_function = set_flag;
+ timer_create(CLOCK_REALTIME, &action, &timer_id);
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes; with -lrt" >&5
+$as_echo "yes; with -lrt" >&6; }; $as_echo "#define HAVE_TIMER_CREATE 1" >>confdefs.h
+
+else
+ LIBS="$save_LIBS"
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+#include<signal.h>
+#include<time.h>
+static void set_flag(union sigval) {}
+
+int
+main ()
+{
+
+ struct timespec ts;
+ struct sigevent action = {0};
+ timer_t timer_id;
+
+ action.sigev_notify = SIGEV_THREAD;
+ action.sigev_notify_function = set_flag;
+ timer_create(CLOCK_REALTIME, &action, &timer_id);
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }; $as_echo "#define HAVE_TIMER_CREATE 1" >>confdefs.h
+
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether stat() ignores a trailing slash" >&5
$as_echo_n "checking whether stat() ignores a trailing slash... " >&6; }
if ${vim_cv_stat_ignores_slash+:} false; then :
diff --git a/src/config.h.in b/src/config.h.in
index 2653e8dda5..1786f2ff96 100644
--- a/src/config.h.in
+++ b/src/config.h.in
@@ -231,6 +231,7 @@
#undef HAVE_UTIME
#undef HAVE_BIND_TEXTDOMAIN_CODESET
#undef HAVE_MBLEN
+#undef HAVE_TIMER_CREATE
/* Define, if needed, for accessing large files. */
#undef _LARGE_FILES
diff --git a/src/configure.ac b/src/configure.ac
index 913129f665..a6a926e681 100644
--- a/src/configure.ac
+++ b/src/configure.ac
@@ -1,7 +1,8 @@
dnl configure.ac: autoconf script for Vim
-dnl Process this file with autoconf 2.12 or 2.13 to produce "configure".
-dnl Should also work with autoconf 2.54 and later.
+dnl Process this file with autoconf 2.69 to produce "configure".
+dnl This should also work with other versions of autoconf, but 2.70 and later
+dnl generate lots of hard to fix "obsolete" warnings.
AC_INIT(vim.h)
AC_CONFIG_HEADER(auto/config.h:config.h.in)
@@ -3812,6 +3813,41 @@ AC_TRY_COMPILE(
AC_MSG_RESULT(yes); AC_DEFINE(HAVE_ST_BLKSIZE),
AC_MSG_RESULT(no))
+dnl Check for timer_create. It probably requires the 'rt' library.
+AC_MSG_CHECKING([for timer_create])
+save_LIBS="$LIBS"
+LIBS="$LIBS -lrt"
+AC_TRY_LINK([
+#include<signal.h>
+#include<time.h>
+static void set_flag(union sigval) {}
+], [
+ struct timespec ts;
+ struct sigevent action = {0};
+ timer_t timer_id;
+
+ action.sigev_notify = SIGEV_THREAD;
+ action.sigev_notify_function = set_flag;
+ timer_create(CLOCK_REALTIME, &action, &timer_id);
+ ],
+ AC_MSG_RESULT(yes; with -lrt); AC_DEFINE(HAVE_TIMER_CREATE),
+ LIBS="$save_LIBS"
+ AC_TRY_LINK([
+#include<signal.h>
+#include<time.h>
+static void set_flag(union sigval) {}
+ ], [
+ struct timespec ts;
+ struct sigevent action = {0};
+ timer_t timer_id;
+
+ action.sigev_notify = SIGEV_THREAD;
+ action.sigev_notify_function = set_flag;
+ timer_create(CLOCK_REALTIME, &action, &timer_id);
+ ],
+ AC_MSG_RESULT(yes); AC_DEFINE(HAVE_TIMER_CREATE),
+ AC_MSG_RESULT(no)))
+
AC_CACHE_CHECK([whether stat() ignores a trailing slash], [vim_cv_stat_ignores_slash],
[
AC_RUN_IFELSE([AC_LANG_SOURCE([[
diff --git a/src/drawscreen.c b/src/drawscreen.c
index ed8927127e..2c184bde0f 100644
--- a/src/drawscreen.c
+++ b/src/drawscreen.c
@@ -1474,9 +1474,6 @@ win_update(win_T *wp)
#if defined(FEAT_SYN_HL) || defined(FEAT_SEARCH_EXTRA)
int save_got_int;
#endif
-#ifdef SYN_TIME_LIMIT
- proftime_T syntax_tm;
-#endif
#if defined(FEAT_SEARCH_EXTRA) || defined(FEAT_CLIPBOARD)
// This needs to be done only for the first window when update_screen() is
@@ -2182,8 +2179,7 @@ win_update(win_T *wp)
#endif
#ifdef SYN_TIME_LIMIT
// Set the time limit to 'redrawtime'.
- profile_setlimit(p_rdt, &syntax_tm);
- syn_set_timeout(&syntax_tm);
+ init_regexp_timeout(p_rdt);
#endif
#ifdef FEAT_FOLDING
win_foldinfo.fi_level = 0;
@@ -2695,7 +2691,7 @@ win_update(win_T *wp)
}
#ifdef SYN_TIME_LIMIT
- syn_set_timeout(NULL);
+ disable_regexp_timeout();
#endif
// Reset the type of redrawing required, the window has been updated.
diff --git a/src/errors.h b/src/errors.h
index 8ca2a3e2f7..109d9e8348 100644
--- a/src/errors.h
+++ b/src/errors.h
@@ -3288,3 +3288,15 @@ EXTERN char e_bitshift_ops_must_be_postive[]
EXTERN char e_argument_1_list_item_nr_dictionary_required[]
INIT(= N_("E1284: Argument 1, list item %d: Dictionary required"));
#endif
+#ifdef FEAT_RELTIME
+EXTERN char e_could_not_clear_timeout_str[]
+ INIT(= N_("E1285: Could not clear timeout: %s"));
+EXTERN char e_could_not_set_timeout_str[]
+ INIT(= N_("E1286: Could not set timeout: %s"));
+EXTERN char e_could_not_set_handler_for_timeout_str[]
+ INIT(= N_("E1287: Could not set handler for timeout: %s"));
+EXTERN char e_could_not_reset_handler_for_timeout_str[]
+ INIT(= N_("E1288: Could not reset handler for timeout: %s"));
+EXTERN char e_could_not_check_for_pending_sigalrm_str[]
+ INIT(= N_("E1289: Could not check for pending SIGALRM: %s"));
+#endif
diff --git a/src/evalfunc.c b/src/evalfunc.c
index 75c3456a8f..ff30756dda 100644
--- a/src/evalfunc.c
+++ b/src/evalfunc.c
@@ -8439,7 +8439,6 @@ search_cmn(typval_T *argvars, pos_T *match_pos, int *flagsp)
int retval = 0; // default: FAIL
long lnum_stop = 0;
#ifdef FEAT_RELTIME
- proftime_T tm;
long time_limit = 0;
#endif
int options = SEARCH_KEEP;
@@ -8486,11 +8485,6 @@ search_cmn(typval_T *argvars, pos_T *match_pos, int *flagsp)
}
}
-#ifdef FEAT_RELTIME
- // Set the time limit, if there is one.
- profile_setlimit(time_limit, &tm);
-#endif
-
/*
* This function does not accept SP_REPEAT and SP_RETCOUNT flags.
* Check to make sure only those flags are set.
@@ -8509,7 +8503,7 @@ search_cmn(typval_T *argvars, pos_T *match_pos, int *flagsp)
CLEAR_FIELD(sia);
sia.sa_stop_lnum = (linenr_T)lnum_stop;
#ifdef FEAT_RELTIME
- sia.sa_tm = &tm;
+ sia.sa_tm = time_limit;
#endif
// Repeat until {skip} returns FALSE.
@@ -8955,19 +8949,11 @@ do_searchpair(
int use_skip = FALSE;
int err;
int options = SEARCH_KEEP;
-#ifdef FEAT_RELTIME
- proftime_T tm;
-#endif
// Make 'cpoptions' empty, the 'l' flag should not be used here.
save_cpo = p_cpo;
p_cpo = empty_option;
-#ifdef FEAT_RELTIME
- // Set the time limit, if there is one.
- profile_setlimit(time_limit, &tm);
-#endif
-
// Make two search patterns: start/end (pat2, for in nested pairs) and
// start/middle/end (pat3, for the top pair).
pat2 = alloc(STRLEN(spat) + STRLEN(epat) + 17);
@@ -8998,7 +8984,7 @@ do_searchpair(
CLEAR_FIELD(sia);
sia.sa_stop_lnum = lnum_stop;
#ifdef FEAT_RELTIME
- sia.sa_tm = &tm;
+ sia.sa_tm = time_limit;
#endif
n = searchit(curwin, curbuf, &pos, NULL, dir, pat, 1L,
options, RE_SEARCH, &sia);
diff --git a/src/ex_cmds.c b/src/ex_cmds.c
index e01d067275..34a740da24 100644
--- a/src/ex_cmds.c
+++ b/src/ex_cmds.c
@@ -4006,7 +4006,7 @@ ex_substitute(exarg_T *eap)
); ++lnum)
{
nmatch = vim_regexec_multi(&regmatch, curwin, curbuf, lnum,
- (colnr_T)0, NULL, NULL);
+ (colnr_T)0, NULL);
if (nmatch)
{
colnr_T copycol;
@@ -4663,7 +4663,7 @@ skip:
|| nmatch_tl > 0
|| (nmatch = vim_regexec_multi(&regmatch, curwin,
curbuf, sub_firstlnum,
- matchcol, NULL, NULL)) == 0
+ matchcol, NULL)) == 0
|| regmatch.startpos[0].lnum > 0)
{
if (new_start != NULL)
@@ -4728,7 +4728,7 @@ skip:
}
if (nmatch == -1 && !lastone)
nmatch = vim_regexec_multi(&regmatch, curwin, curbuf,
- sub_firstlnum, matchcol, NULL, NULL);
+ sub_firstlnum, matchcol, NULL);
/*
* 5. break if there isn't another match in this line
@@ -4992,7 +4992,7 @@ ex_global(exarg_T *eap)
{
lnum = curwin->w_cursor.lnum;
match = vim_regexec_multi(&regmatch, curwin, curbuf, lnum,
- (colnr_T)0, NULL, NULL);
+ (colnr_T)0, NULL);
if ((type == 'g' && match) || (type == 'v' && !match))
global_exe_one(cmd, lnum);
}
@@ -5005,7 +5005,7 @@ ex_global(exarg_T *eap)
{
// a match on this line?
match = vim_regexec_multi(&regmatch, curwin, curbuf, lnum,
- (colnr_T)0, NULL, NULL);
+ (colnr_T)0, NULL);
if (regmatch.regprog == NULL)
break; // re-compiling regprog failed
if ((type == 'g' && match) || (type == 'v' && !match))
diff --git a/src/ex_getln.c b/src/ex_getln.c
index f75d8c7a96..20d9520ed1 100644
--- a/src/ex_getln.c
+++ b/src/ex_getln.c
@@ -417,7 +417,6 @@ may_do_incsearch_highlighting(
int found; // do_search() result
pos_T end_pos;
#ifdef FEAT_RELTIME
- proftime_T tm;
searchit_arg_T sia;
#endif
int next_char;
@@ -484,10 +483,6 @@ may_do_incsearch_highlighting(
cursor_off(); // so the user knows we're busy
out_flush();
++emsg_off; // so it doesn't beep if bad expr
-#ifdef FEAT_RELTIME
- // Set the time limit to half a second.
- profile_setlimit(500L, &tm);
-#endif
if (!p_hls)
search_flags += SEARCH_KEEP;
if (search_first_line != 0)
@@ -495,7 +490,8 @@ may_do_incsearch_highlighting(
ccline.cmdbuff[skiplen + patlen] = NUL;
#ifdef FEAT_RELTIME
CLEAR_FIELD(sia);
- sia.sa_tm = &tm;
+ // Set the time limit to half a second.
+ sia.sa_tm = 500;
#endif
found = do_search(NULL, firstc == ':' ? '/' : firstc, search_delim,
ccline.cmdbuff + skiplen, count, search_flags,
diff --git a/src/match.c b/src/match.c
index 8280da7d57..72276527d3 100644
--- a/src/match.c
+++ b/src/match.c
@@ -330,10 +330,6 @@ init_search_hl(win_T *wp, match_T *search_hl)
cur->hl.buf = wp->w_buffer;
cur->hl.lnum = 0;
cur->hl.first_lnum = 0;
-# ifdef FEAT_RELTIME
- // Set the time limit to 'redrawtime'.
- profile_setlimit(p_rdt, &(cur->hl.tm));
-# endif
cur = cur->next;
}
search_hl->buf = wp->w_buffer;
@@ -424,6 +420,7 @@ next_search_hl(
colnr_T matchcol;
long nmatched;
int called_emsg_before = called_emsg;
+ int timed_out = FALSE;
// for :{range}s/pat only highlight inside the range
if ((lnum < search_first_line || lnum > search_last_line) && cur == NULL)
@@ -451,7 +448,7 @@ next_search_hl(
{
# ifdef FEAT_RELTIME
// Stop searching after passing the time limit.
- if (profile_passed_limit(&(shl->tm)))
+ if (timed_out)
{
shl->lnum = 0; // no match found in time
break;
@@ -494,16 +491,9 @@ next_search_hl(
int regprog_is_copy = (shl != search_hl && cur != NULL
&& shl == &cur->hl
&& cur->match.regprog == cur->hl.rm.regprog);
- int timed_out = FALSE;
nmatched = vim_regexec_multi(&shl->rm, win, shl->buf, lnum,
- matchcol,
-#ifdef FEAT_RELTIME
- &(shl->tm), &timed_out
-#else
- NULL, NULL
-#endif
- );
+ matchcol, &timed_out);
// Copy the regprog, in case it got freed and recompiled.
if (regprog_is_copy)
cur->match.regprog = cur->hl.rm.regprog;
diff --git a/src/os_mac.h b/src/os_mac.h
index dbc0086e0d..f6ed1216ca 100644
--- a/src/os_mac.h
+++ b/src/os_mac.h
@@ -6,6 +6,9 @@
* Do ":help credits" in Vim to see a list of people who contributed.
*/
+#ifndef OS_MAC__H
+#define OS_MAC__H
+
// Before Including the MacOS specific files,
// let's set the OPAQUE_TOOLBOX_STRUCTS to 0 so we
// can access the internal structures.
@@ -266,3 +269,52 @@
// A Mac constant causing big problem to syntax highlighting
#define UNKNOWN_CREATOR '\?\?\?\?'
+
+#ifdef FEAT_RELTIME
+
+# include <dispatch/dispatch.h>
+
+# if !defined(MAC_OS_X_VERSION_10_12) || \
+ (MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_12)
+typedef int clockid_t;
+# endif
+# ifndef CLOCK_REALTIME
+# define CLOCK_REALTIME 0
+# endif
+# ifndef CLOCK_MONOTONIC
+# define CLOCK_MONOTONIC 1
+# endif
+
+struct itimerspec
+{
+ struct timespec it_interval; // timer period
+ struct timespec it_value; // initial expiration
+};
+
+struct sigevent;
+
+struct macos_timer
+{
+ dispatch_queue_t tim_queue;
+ dispatch_source_t tim_timer;
+ void (*tim_func)(union sigval);
+ void *tim_arg;
+};
+
+typedef struct macos_timer *timer_t;
+
+extern int timer_create(
+ clockid_t clockid,
+ struct sigevent *sevp,
+ timer_t *timerid);
+
+extern int timer_delete(timer_t timerid);
+
+extern int timer_settime(
+ timer_t timerid, int flags,
+ const struct itimerspec *new_value,
+ struct itimerspec *unused);
+
+#endif // FEAT_RELTIME
+
+#endif // OS_MAC__H
diff --git a/src/os_macosx.m b/src/os_macosx.m
index 0299fe4134..1fdfbc70dd 100644
--- a/src/os_macosx.m
+++ b/src/os_macosx.m
@@ -23,6 +23,13 @@
* X11 header files. */
#define NO_X11_INCLUDES
+#include <stdbool.h>
+#include <mach/boolean.h>
+#include <sys/errno.h>
+#include <stdlib.h>
+
+#include <dispatch/dispatch.h>
+
#include "vim.h"
#import <AppKit/AppKit.h>
@@ -208,6 +215,175 @@ releasepool:
#endif /* FEAT_CLIPBOARD */
+#ifdef FEAT_RELTIME
+/*
+ * The following timer code is based on a Gist by Jorgen Lundman:
+ *
+ * https://gist.github.com/lundman
+ */
+
+typedef struct macos_timer macos_timer_T;
+
+ static void
+_timer_cancel(void *arg UNUSED)
+{
+ // This is not currently used, but it might be useful in the future and
+ // it is non-trivial enough to provide as usable implementation.
+# if 0
+ macos_timer_T *timerid = (macos_timer_T *)arg;
+
+ dispatch_release(timerid->tim_timer);
+ dispatch_release(timerid->tim_queue);
+ timerid->tim_timer = NULL;
+ timerid->tim_queue = NULL;
+ free(timerid);
+# endif
+}
+
+ static void
+_timer_handler(void *arg)
+{
+ macos_timer_T *timerid = (macos_timer_T *)arg;
+ union sigval sv;
+
+ sv.sival_ptr = timerid->tim_arg;
+
+ if (timerid->tim_func != NULL)
+ timerid->tim_func(sv);
+}
+
+ static uint64_t
+itime_to_ns(const struct timespec *it)
+{
+ time_t sec = it->tv_sec;
+ long nsec = it->tv_nsec;
+ uint64_t ns = NSEC_PER_SEC * sec + nsec;
+
+ return ns == 0 ? DISPATCH_TIME_FOREVER : ns;
+}
+
+/*
+ * A partial emulation of the POSIX timer_create function.
+ *
+ * The limitations and differences include:
+ *
+ * - Only CLOCK_REALTIME and CLOCK_MONOTONIC are supported as clockid
+ * values.
+ * - Even if CLOCK_REALTIME is specified, internally the mach_absolute_time
+ * source is used internally.
+ * - The only notification method supported is SIGEV_THREAD.
+ */
+ inline int
+timer_create(clockid_t clockid, struct sigevent *sevp, timer_t *timerid)
+{
+ macos_timer_T *timer = NULL;
+
+ // We only support real time and monotonic clocks; and SIGEV_THREAD
+ // notification. In practice, there is no difference between the two
+ // types of clocks on MacOS - we always use the mach_machine_time
+ // source.
+ if ( (clockid != CLOCK_REALTIME && clockid != CLOCK_MONOTONIC)
+ || sevp->sigev_notify != SIGEV_THREAD)
+ {
+ semsg("clockid: %d %d", clockid, CLOCK_REALTIME);
+ semsg("notify: %d %d", sevp->sigev_notify, SIGEV_THREAD);
+ errno = ENOTSUP;
+ return -1;
+ }
+
+ timer = (macos_timer_T *)malloc(sizeof(macos_timer_T));
+ if (timer == NULL)
+ {
+ errno = ENOMEM;
+ return -1;
+ }
+ *timerid = timer;
+
+ timer->tim_queue = dispatch_queue_create(
+ "org.vim.timerqueue", NULL);
+ if (timer->tim_queue == NULL)
+ {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ timer->tim_timer = dispatch_source_create(
+ DISPATCH_SOURCE_TYPE_TIMER, 0, 0, timer->tim_queue);
+ if (timer->tim_timer == NULL)
+ {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ timer->tim_func = sevp->sigev_notify_function;
+ timer->tim_arg = sevp->sigev_value.sival_ptr;
+
+ dispatch_set_context(timer->tim_timer, timer);
+ dispatch_source_set_event_handler_f(timer->tim_timer, _timer_handler);
+ dispatch_source_set_cancel_handler_f(timer->tim_timer, _timer_cancel);
+
+ dispatch_resume(timer->tim_timer);
+
+ return 0;
+}
+
+/*
+ * A partial emulation of the POSIX timer_settime function.
+ *
+ * The limitations and differences include:
+ *
+ * - The flags argument is ignored. The supplied new_value is therfore
+ * always treated as a relative time.
+ * - The old_value argument is ignored.
+ */
+ int
+timer_settime(
+ timer_t timerid,
+ int unused_flags UNUSED,
+ const struct itimerspec *new_value,
+ struct itimerspec *old_value UNUSED)
+{
+ uint64_t first_shot = itime_to_ns(&new_value->it_value);
+
+ if (timerid == NULL)
+ return 0;
+
+ if (first_shot == DISPATCH_TIME_FOREVER)
+ {
+ dispatch_source_set_timer(
+ timerid->tim_timer, first_shot, first_shot, 0);
+ }
+ else
+ {
+ uint64_t interval = itime_to_ns(&new_value->it_interval);
+
+ dispatch_time_t start = dispatch_time(DISPATCH_TIME_NOW, first_shot);
+ dispatch_source_set_timer(timerid->tim_timer, start, interval, 0);
+ }
+
+ return 0;
+}
+
+/*
+ * An emulation of the POSIX timer_delete function.
+ *
+ * Disabled because it is not currently used, but an implemented provided
+ * for completeness and possible future use.
+ */
+#if 0
+ int
+timer_delete(timer_t timerid)
+{
+ /* Calls _timer_cancel() */
+ if (timerid != NULL)
+ dispatch_source_cancel(timerid->tim_timer);
+
+ return 0;
+}
+#endif
+
+#endif /* FEAT_RELTIME */
+
/* Lift the compiler warning suppression. */
#if defined(__clang__) && defined(__STRICT_ANSI__)
# pragma clang diagnostic pop
diff --git a/src/os_unix.c b/src/os_unix.c
index b786f8df07..28113fcb3b 100644
--- a/src/os_unix.c
+++ b/src/os_unix.c
@@ -8256,3 +8256,217 @@ xsmp_close(void)
}
}
#endif // USE_XSMP
+
+#if defined(FEAT_RELTIME) || defined(PROTO)
+# if defined(HAVE_TIMER_CREATE) || defined(MACOS_X)
+/*
+ * Implement timeout with timer_create() and timer_settime().
+ */
+static int timeout_flag = FALSE;
+static timer_t timer_id;
+static int timer_created = FALSE;
+
+/*
+ * Callback for when the timer expires.
+ */
+ static void
+set_flag(union sigval _unused UNUSED)
+{
+ timeout_flag = TRUE;
+}
+
+/*
+ * Stop any active timeout.
+ */
+ void
+stop_timeout(void)
+{
+ static struct itimerspec disarm = {{0, 0}, {0, 0}};
+
+ if (timer_created)
+ {
+ int ret = timer_settime(timer_id, 0, &disarm, NULL);
+
+ if (ret < 0)
+ semsg(_(e_could_not_clear_timeout_str), strerror(errno));
+ }
+
+ // Clear the current timeout flag; any previous timeout should be
+ // considered _not_ triggered.
+ timeout_flag = FALSE;
+}
+
+/*
+ * Start the timeout timer.
+ *
+ * The return value is a pointer to a flag that is initialised to FALSE. If the
+ * timeout expires, the flag is set to TRUE. This will only return pointers to
+ * static memory; i.e. any pointer returned by this function may always be
+ * safely dereferenced.
+ *
+ * This function is not expected to fail, but if it does it will still return a
+ * valid flag pointer; the flag will remain stuck as FALSE .
+ */
+ const int *
+start_timeout(long msec)
+{
+ struct itimerspec interval = {
+ {0, 0}, // Do not repeat.
+ {msec / 1000, (msec % 1000) * 1000000}}; // Timeout interval
+ int ret;
+
+ // This is really the caller's responsibility, but let's make sure the
+ // previous timer has been stopped.
+ stop_timeout();
+ timeout_flag = FALSE;
+
+ if (!timer_created)
+ {
+ struct sigevent action = {0};
+
+ action.sigev_notify = SIGEV_THREAD;
+ action.sigev_notify_function = set_flag;
+ ret = timer_create(CLOCK_MONOTONIC, &action, &timer_id);
+ if (ret < 0)
+ {
+ semsg(_(e_could_not_set_timeout_str), strerror(errno));
+ return &timeout_flag;
+ }
+ timer_created = TRUE;
+ }
+
+ ret = timer_settime(timer_id, 0, &interval, NULL);
+ if (ret < 0)
+ semsg(_(e_could_not_set_timeout_str), strerror(errno));
+
+ return &timeout_flag;
+}
+
+# else
+
+/*
+ * Implement timeout with setitimer()
+ */
+static struct itimerval prev_interval;
+static struct sigaction prev_sigaction;
+static int timeout_flag = FALSE;
+static int timer_active = FALSE;
+static int timer_handler_active = FALSE;
+static int alarm_pending = FALSE;
+
+/*
+ * Handle SIGALRM for a timeout.
+ */
+ static RETSIGTYPE
+set_flag SIGDEFARG(sigarg)
+{
+ if (alarm_pending)
+ alarm_pending = FALSE;
+ else
+ timeout_flag = TRUE;
+}
+
+/*
+ * Stop any active timeout.
+ */
+ void
+stop_timeout(void)
+{
+ static struct itimerval disarm = {{0, 0}, {0, 0}};
+ int ret;
+
+ if (timer_active)
+ {
+ timer_active = FALSE;
+ ret = setitimer(ITIMER_REAL, &disarm, &prev_interval);
+ if (ret < 0)
+ // Should only get here as a result of coding errors.
+ semsg(_(e_could_not_clear_timeout_str), strerror(errno));
+ }
+
+ if (timer_handler_active)
+ {
+ timer_handler_active = FALSE;
+ ret = sigaction(SIGALRM, &prev_sigaction, NULL);
+ if (ret < 0)
+ // Should only get here as a result of coding errors.
+ semsg(_(e_could_not_reset_handler_for_timeout_str),
+ strerror(errno));
+ }
+ timeout_flag = 0;
+}
+
+/*
+ * Start the timeout timer.
+ *
+ * The return value is a pointer to a flag that is initialised to FALSE. If the
+ * timeout expires, the flag is set to TRUE. This will only return pointers to
+ * static memory; i.e. any pointer returned by this function may always be
+ * safely dereferenced.
+ *
+ * This funct