blob: 5f8eef91b572601dfd855ef00f6e396aff783e6e [file] [log] [blame]
Bram Moolenaaredf3f972016-08-29 22:49:24 +02001/* vi:set ts=8 sts=4 sw=4 noet:
Bram Moolenaar071d4272004-06-13 20:20:40 +00002 *
3 * VIM - Vi IMproved by Bram Moolenaar
4 *
5 * Do ":help uganda" in Vim to read copying and usage conditions.
6 * Do ":help credits" in Vim to see a list of people who contributed.
7 * See README.txt for an overview of the Vim source code.
8 */
9
10/*
11 * ex_cmds2.c: some more functions for command line commands
12 */
13
Bram Moolenaar071d4272004-06-13 20:20:40 +000014#include "vim.h"
Bram Moolenaar071d4272004-06-13 20:20:40 +000015#include "version.h"
16
Bram Moolenaarf28dbce2016-01-29 22:03:47 +010017static void cmd_source(char_u *fname, exarg_T *eap);
Bram Moolenaar071d4272004-06-13 20:20:40 +000018
Bram Moolenaar05159a02005-02-26 23:04:13 +000019#ifdef FEAT_EVAL
Bram Moolenaar8cd06ca2005-02-28 22:44:58 +000020/* Growarray to store info about already sourced scripts.
Bram Moolenaar05159a02005-02-26 23:04:13 +000021 * For Unix also store the dev/ino, so that we don't have to stat() each
22 * script when going through the list. */
23typedef struct scriptitem_S
24{
25 char_u *sn_name;
26# ifdef UNIX
Bram Moolenaarbf0c4522009-05-16 19:16:33 +000027 int sn_dev_valid;
28 dev_t sn_dev;
Bram Moolenaar05159a02005-02-26 23:04:13 +000029 ino_t sn_ino;
30# endif
31# ifdef FEAT_PROFILE
32 int sn_prof_on; /* TRUE when script is/was profiled */
Bram Moolenaar8cd06ca2005-02-28 22:44:58 +000033 int sn_pr_force; /* forceit: profile functions in this script */
Bram Moolenaar05159a02005-02-26 23:04:13 +000034 proftime_T sn_pr_child; /* time set when going into first child */
35 int sn_pr_nest; /* nesting for sn_pr_child */
36 /* profiling the script as a whole */
37 int sn_pr_count; /* nr of times sourced */
Bram Moolenaar8c8de832008-06-24 22:58:06 +000038 proftime_T sn_pr_total; /* time spent in script + children */
39 proftime_T sn_pr_self; /* time spent in script itself */
Bram Moolenaar05159a02005-02-26 23:04:13 +000040 proftime_T sn_pr_start; /* time at script start */
41 proftime_T sn_pr_children; /* time in children after script start */
42 /* profiling the script per line */
43 garray_T sn_prl_ga; /* things stored for every line */
44 proftime_T sn_prl_start; /* start time for current line */
45 proftime_T sn_prl_children; /* time spent in children for this line */
46 proftime_T sn_prl_wait; /* wait start time for current line */
47 int sn_prl_idx; /* index of line being timed; -1 if none */
48 int sn_prl_execed; /* line being timed was executed */
49# endif
50} scriptitem_T;
51
52static garray_T script_items = {0, 0, sizeof(scriptitem_T), 4, NULL};
53#define SCRIPT_ITEM(id) (((scriptitem_T *)script_items.ga_data)[(id) - 1])
54
55# ifdef FEAT_PROFILE
56/* Struct used in sn_prl_ga for every line of a script. */
57typedef struct sn_prl_S
58{
59 int snp_count; /* nr of times line was executed */
Bram Moolenaar8c8de832008-06-24 22:58:06 +000060 proftime_T sn_prl_total; /* time spent in a line + children */
61 proftime_T sn_prl_self; /* time spent in a line itself */
Bram Moolenaar05159a02005-02-26 23:04:13 +000062} sn_prl_T;
63
64# define PRL_ITEM(si, idx) (((sn_prl_T *)(si)->sn_prl_ga.ga_data)[(idx)])
65# endif
66#endif
67
Bram Moolenaar071d4272004-06-13 20:20:40 +000068#if defined(FEAT_EVAL) || defined(PROTO)
Bram Moolenaar433f7c82006-03-21 21:29:36 +000069# if defined(FEAT_PROFILE) || defined(FEAT_RELTIME) || defined(PROTO)
Bram Moolenaar05159a02005-02-26 23:04:13 +000070/*
71 * Store the current time in "tm".
72 */
73 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +010074profile_start(proftime_T *tm)
Bram Moolenaar05159a02005-02-26 23:04:13 +000075{
Bram Moolenaar4f974752019-02-17 17:44:42 +010076# ifdef MSWIN
Bram Moolenaar8cd06ca2005-02-28 22:44:58 +000077 QueryPerformanceCounter(tm);
78# else
Bram Moolenaar05159a02005-02-26 23:04:13 +000079 gettimeofday(tm, NULL);
Bram Moolenaar8cd06ca2005-02-28 22:44:58 +000080# endif
Bram Moolenaar05159a02005-02-26 23:04:13 +000081}
82
83/*
84 * Compute the elapsed time from "tm" till now and store in "tm".
85 */
86 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +010087profile_end(proftime_T *tm)
Bram Moolenaar05159a02005-02-26 23:04:13 +000088{
89 proftime_T now;
90
Bram Moolenaar4f974752019-02-17 17:44:42 +010091# ifdef MSWIN
Bram Moolenaar8cd06ca2005-02-28 22:44:58 +000092 QueryPerformanceCounter(&now);
93 tm->QuadPart = now.QuadPart - tm->QuadPart;
94# else
Bram Moolenaar05159a02005-02-26 23:04:13 +000095 gettimeofday(&now, NULL);
96 tm->tv_usec = now.tv_usec - tm->tv_usec;
97 tm->tv_sec = now.tv_sec - tm->tv_sec;
98 if (tm->tv_usec < 0)
99 {
100 tm->tv_usec += 1000000;
101 --tm->tv_sec;
102 }
Bram Moolenaar8cd06ca2005-02-28 22:44:58 +0000103# endif
Bram Moolenaar05159a02005-02-26 23:04:13 +0000104}
105
106/*
107 * Subtract the time "tm2" from "tm".
108 */
109 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +0100110profile_sub(proftime_T *tm, proftime_T *tm2)
Bram Moolenaar05159a02005-02-26 23:04:13 +0000111{
Bram Moolenaar4f974752019-02-17 17:44:42 +0100112# ifdef MSWIN
Bram Moolenaar8cd06ca2005-02-28 22:44:58 +0000113 tm->QuadPart -= tm2->QuadPart;
114# else
Bram Moolenaar05159a02005-02-26 23:04:13 +0000115 tm->tv_usec -= tm2->tv_usec;
116 tm->tv_sec -= tm2->tv_sec;
117 if (tm->tv_usec < 0)
118 {
119 tm->tv_usec += 1000000;
120 --tm->tv_sec;
121 }
Bram Moolenaar8cd06ca2005-02-28 22:44:58 +0000122# endif
Bram Moolenaar05159a02005-02-26 23:04:13 +0000123}
124
125/*
Bram Moolenaar433f7c82006-03-21 21:29:36 +0000126 * Return a string that represents the time in "tm".
127 * Uses a static buffer!
128 */
129 char *
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +0100130profile_msg(proftime_T *tm)
Bram Moolenaar433f7c82006-03-21 21:29:36 +0000131{
132 static char buf[50];
133
Bram Moolenaar4f974752019-02-17 17:44:42 +0100134# ifdef MSWIN
Bram Moolenaar433f7c82006-03-21 21:29:36 +0000135 LARGE_INTEGER fr;
136
137 QueryPerformanceFrequency(&fr);
138 sprintf(buf, "%10.6lf", (double)tm->QuadPart / (double)fr.QuadPart);
139# else
140 sprintf(buf, "%3ld.%06ld", (long)tm->tv_sec, (long)tm->tv_usec);
Bram Moolenaar76929292008-01-06 19:07:36 +0000141# endif
Bram Moolenaar433f7c82006-03-21 21:29:36 +0000142 return buf;
143}
144
Bram Moolenaar79c2c882016-02-07 21:19:28 +0100145# if defined(FEAT_FLOAT) || defined(PROTO)
146/*
147 * Return a float that represents the time in "tm".
148 */
149 float_T
150profile_float(proftime_T *tm)
151{
Bram Moolenaar4f974752019-02-17 17:44:42 +0100152# ifdef MSWIN
Bram Moolenaar79c2c882016-02-07 21:19:28 +0100153 LARGE_INTEGER fr;
154
155 QueryPerformanceFrequency(&fr);
156 return (float_T)tm->QuadPart / (float_T)fr.QuadPart;
157# else
158 return (float_T)tm->tv_sec + (float_T)tm->tv_usec / 1000000.0;
159# endif
160}
161# endif
162
Bram Moolenaar433f7c82006-03-21 21:29:36 +0000163/*
Bram Moolenaar76929292008-01-06 19:07:36 +0000164 * Put the time "msec" past now in "tm".
Bram Moolenaar433f7c82006-03-21 21:29:36 +0000165 */
Bram Moolenaar76929292008-01-06 19:07:36 +0000166 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +0100167profile_setlimit(long msec, proftime_T *tm)
Bram Moolenaar76929292008-01-06 19:07:36 +0000168{
169 if (msec <= 0) /* no limit */
170 profile_zero(tm);
171 else
172 {
Bram Moolenaar4f974752019-02-17 17:44:42 +0100173# ifdef MSWIN
Bram Moolenaar76929292008-01-06 19:07:36 +0000174 LARGE_INTEGER fr;
175
176 QueryPerformanceCounter(tm);
177 QueryPerformanceFrequency(&fr);
Bram Moolenaarb3c70982008-01-18 10:40:55 +0000178 tm->QuadPart += (LONGLONG)((double)msec / 1000.0 * (double)fr.QuadPart);
Bram Moolenaar76929292008-01-06 19:07:36 +0000179# else
180 long usec;
181
182 gettimeofday(tm, NULL);
183 usec = (long)tm->tv_usec + (long)msec * 1000;
184 tm->tv_usec = usec % 1000000L;
185 tm->tv_sec += usec / 1000000L;
186# endif
187 }
188}
189
190/*
191 * Return TRUE if the current time is past "tm".
192 */
193 int
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +0100194profile_passed_limit(proftime_T *tm)
Bram Moolenaar76929292008-01-06 19:07:36 +0000195{
196 proftime_T now;
197
Bram Moolenaar4f974752019-02-17 17:44:42 +0100198# ifdef MSWIN
Bram Moolenaar76929292008-01-06 19:07:36 +0000199 if (tm->QuadPart == 0) /* timer was not set */
200 return FALSE;
201 QueryPerformanceCounter(&now);
202 return (now.QuadPart > tm->QuadPart);
203# else
204 if (tm->tv_sec == 0) /* timer was not set */
205 return FALSE;
206 gettimeofday(&now, NULL);
207 return (now.tv_sec > tm->tv_sec
208 || (now.tv_sec == tm->tv_sec && now.tv_usec > tm->tv_usec));
209# endif
210}
Bram Moolenaar433f7c82006-03-21 21:29:36 +0000211
212/*
213 * Set the time in "tm" to zero.
214 */
215 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +0100216profile_zero(proftime_T *tm)
Bram Moolenaar433f7c82006-03-21 21:29:36 +0000217{
Bram Moolenaar4f974752019-02-17 17:44:42 +0100218# ifdef MSWIN
Bram Moolenaar433f7c82006-03-21 21:29:36 +0000219 tm->QuadPart = 0;
220# else
221 tm->tv_usec = 0;
222 tm->tv_sec = 0;
223# endif
224}
225
Bram Moolenaar76929292008-01-06 19:07:36 +0000226# endif /* FEAT_PROFILE || FEAT_RELTIME */
227
Bram Moolenaar975b5272016-03-15 23:10:59 +0100228# if defined(FEAT_TIMERS) || defined(PROTO)
229static timer_T *first_timer = NULL;
Bram Moolenaar75537a92016-09-05 22:45:28 +0200230static long last_timer_id = 0;
Bram Moolenaar975b5272016-03-15 23:10:59 +0100231
Bram Moolenaar56bc8e22018-05-10 18:05:56 +0200232 long
Bram Moolenaar51b0f372017-11-18 18:52:04 +0100233proftime_time_left(proftime_T *due, proftime_T *now)
Bram Moolenaara8e93d62017-09-18 21:50:47 +0200234{
Bram Moolenaar4f974752019-02-17 17:44:42 +0100235# ifdef MSWIN
Bram Moolenaara8e93d62017-09-18 21:50:47 +0200236 LARGE_INTEGER fr;
237
Bram Moolenaar51b0f372017-11-18 18:52:04 +0100238 if (now->QuadPart > due->QuadPart)
Bram Moolenaara8e93d62017-09-18 21:50:47 +0200239 return 0;
240 QueryPerformanceFrequency(&fr);
Bram Moolenaar51b0f372017-11-18 18:52:04 +0100241 return (long)(((double)(due->QuadPart - now->QuadPart)
Bram Moolenaara8e93d62017-09-18 21:50:47 +0200242 / (double)fr.QuadPart) * 1000);
243# else
Bram Moolenaar51b0f372017-11-18 18:52:04 +0100244 if (now->tv_sec > due->tv_sec)
Bram Moolenaara8e93d62017-09-18 21:50:47 +0200245 return 0;
Bram Moolenaar51b0f372017-11-18 18:52:04 +0100246 return (due->tv_sec - now->tv_sec) * 1000
247 + (due->tv_usec - now->tv_usec) / 1000;
Bram Moolenaara8e93d62017-09-18 21:50:47 +0200248# endif
249}
Bram Moolenaar417ccd72016-09-01 21:26:20 +0200250
Bram Moolenaar975b5272016-03-15 23:10:59 +0100251/*
252 * Insert a timer in the list of timers.
253 */
254 static void
255insert_timer(timer_T *timer)
256{
257 timer->tr_next = first_timer;
258 timer->tr_prev = NULL;
259 if (first_timer != NULL)
260 first_timer->tr_prev = timer;
261 first_timer = timer;
Bram Moolenaar4231da42016-06-02 14:30:04 +0200262 did_add_timer = TRUE;
Bram Moolenaar975b5272016-03-15 23:10:59 +0100263}
264
265/*
266 * Take a timer out of the list of timers.
267 */
268 static void
269remove_timer(timer_T *timer)
270{
271 if (timer->tr_prev == NULL)
272 first_timer = timer->tr_next;
273 else
274 timer->tr_prev->tr_next = timer->tr_next;
275 if (timer->tr_next != NULL)
276 timer->tr_next->tr_prev = timer->tr_prev;
277}
278
279 static void
280free_timer(timer_T *timer)
281{
Bram Moolenaar75537a92016-09-05 22:45:28 +0200282 free_callback(timer->tr_callback, timer->tr_partial);
283 vim_free(timer);
Bram Moolenaar975b5272016-03-15 23:10:59 +0100284}
285
286/*
287 * Create a timer and return it. NULL if out of memory.
288 * Caller should set the callback.
289 */
290 timer_T *
291create_timer(long msec, int repeat)
292{
293 timer_T *timer = (timer_T *)alloc_clear(sizeof(timer_T));
Bram Moolenaaree39ef02016-09-10 19:17:42 +0200294 long prev_id = last_timer_id;
Bram Moolenaar975b5272016-03-15 23:10:59 +0100295
296 if (timer == NULL)
297 return NULL;
Bram Moolenaaree39ef02016-09-10 19:17:42 +0200298 if (++last_timer_id <= prev_id)
Bram Moolenaar75537a92016-09-05 22:45:28 +0200299 /* Overflow! Might cause duplicates... */
300 last_timer_id = 0;
301 timer->tr_id = last_timer_id;
Bram Moolenaar975b5272016-03-15 23:10:59 +0100302 insert_timer(timer);
303 if (repeat != 0)
Bram Moolenaar975b5272016-03-15 23:10:59 +0100304 timer->tr_repeat = repeat - 1;
Bram Moolenaar8e97bd72016-08-06 22:05:07 +0200305 timer->tr_interval = msec;
Bram Moolenaar975b5272016-03-15 23:10:59 +0100306
307 profile_setlimit(msec, &timer->tr_due);
308 return timer;
309}
310
311/*
312 * Invoke the callback of "timer".
313 */
314 static void
315timer_callback(timer_T *timer)
316{
317 typval_T rettv;
318 int dummy;
319 typval_T argv[2];
320
321 argv[0].v_type = VAR_NUMBER;
Bram Moolenaar75537a92016-09-05 22:45:28 +0200322 argv[0].vval.v_number = (varnumber_T)timer->tr_id;
Bram Moolenaar975b5272016-03-15 23:10:59 +0100323 argv[1].v_type = VAR_UNKNOWN;
324
325 call_func(timer->tr_callback, (int)STRLEN(timer->tr_callback),
Bram Moolenaardf48fb42016-07-22 21:50:18 +0200326 &rettv, 1, argv, NULL, 0L, 0L, &dummy, TRUE,
Bram Moolenaar975b5272016-03-15 23:10:59 +0100327 timer->tr_partial, NULL);
328 clear_tv(&rettv);
329}
330
331/*
332 * Call timers that are due.
333 * Return the time in msec until the next timer is due.
Bram Moolenaarc4f83382017-07-07 14:50:44 +0200334 * Returns -1 if there are no pending timers.
Bram Moolenaar975b5272016-03-15 23:10:59 +0100335 */
336 long
Bram Moolenaarcf089462016-06-12 21:18:43 +0200337check_due_timer(void)
Bram Moolenaar975b5272016-03-15 23:10:59 +0100338{
339 timer_T *timer;
Bram Moolenaar75537a92016-09-05 22:45:28 +0200340 timer_T *timer_next;
Bram Moolenaar975b5272016-03-15 23:10:59 +0100341 long this_due;
Bram Moolenaar597385a2016-03-16 23:24:43 +0100342 long next_due = -1;
Bram Moolenaar975b5272016-03-15 23:10:59 +0100343 proftime_T now;
344 int did_one = FALSE;
Bram Moolenaar02e177d2017-08-26 23:43:28 +0200345 int need_update_screen = FALSE;
Bram Moolenaar75537a92016-09-05 22:45:28 +0200346 long current_id = last_timer_id;
Bram Moolenaar975b5272016-03-15 23:10:59 +0100347
Bram Moolenaarc577d812017-07-08 22:37:34 +0200348 /* Don't run any timers while exiting or dealing with an error. */
349 if (exiting || aborting())
Bram Moolenaarc4f83382017-07-07 14:50:44 +0200350 return next_due;
351
Bram Moolenaar75537a92016-09-05 22:45:28 +0200352 profile_start(&now);
353 for (timer = first_timer; timer != NULL && !got_int; timer = timer_next)
Bram Moolenaar975b5272016-03-15 23:10:59 +0100354 {
Bram Moolenaar75537a92016-09-05 22:45:28 +0200355 timer_next = timer->tr_next;
Bram Moolenaar417ccd72016-09-01 21:26:20 +0200356
Bram Moolenaar75537a92016-09-05 22:45:28 +0200357 if (timer->tr_id == -1 || timer->tr_firing || timer->tr_paused)
358 continue;
Bram Moolenaar51b0f372017-11-18 18:52:04 +0100359 this_due = proftime_time_left(&timer->tr_due, &now);
Bram Moolenaar75537a92016-09-05 22:45:28 +0200360 if (this_due <= 1)
361 {
Bram Moolenaarb0f42ba2018-05-12 15:38:26 +0200362 /* Save and restore a lot of flags, because the timer fires while
363 * waiting for a character, which might be halfway a command. */
Bram Moolenaar1e8e1452017-06-24 16:03:06 +0200364 int save_timer_busy = timer_busy;
365 int save_vgetc_busy = vgetc_busy;
Bram Moolenaare723c422017-09-06 23:40:10 +0200366 int save_did_emsg = did_emsg;
367 int save_called_emsg = called_emsg;
Bram Moolenaar02e177d2017-08-26 23:43:28 +0200368 int save_must_redraw = must_redraw;
Bram Moolenaare723c422017-09-06 23:40:10 +0200369 int save_trylevel = trylevel;
370 int save_did_throw = did_throw;
Bram Moolenaarf5291f32017-09-14 22:55:37 +0200371 int save_ex_pressedreturn = get_pressedreturn();
Bram Moolenaare723c422017-09-06 23:40:10 +0200372 except_T *save_current_exception = current_exception;
Bram Moolenaarb0f42ba2018-05-12 15:38:26 +0200373 vimvars_save_T vvsave;
Bram Moolenaar1e8e1452017-06-24 16:03:06 +0200374
Bram Moolenaare723c422017-09-06 23:40:10 +0200375 /* Create a scope for running the timer callback, ignoring most of
376 * the current scope, such as being inside a try/catch. */
Bram Moolenaar1e8e1452017-06-24 16:03:06 +0200377 timer_busy = timer_busy > 0 || vgetc_busy > 0;
378 vgetc_busy = 0;
Bram Moolenaarc577d812017-07-08 22:37:34 +0200379 called_emsg = FALSE;
Bram Moolenaare723c422017-09-06 23:40:10 +0200380 did_emsg = FALSE;
381 did_uncaught_emsg = FALSE;
Bram Moolenaar02e177d2017-08-26 23:43:28 +0200382 must_redraw = 0;
Bram Moolenaare723c422017-09-06 23:40:10 +0200383 trylevel = 0;
384 did_throw = FALSE;
385 current_exception = NULL;
Bram Moolenaarb0f42ba2018-05-12 15:38:26 +0200386 save_vimvars(&vvsave);
Bram Moolenaar75537a92016-09-05 22:45:28 +0200387 timer->tr_firing = TRUE;
388 timer_callback(timer);
389 timer->tr_firing = FALSE;
Bram Moolenaare723c422017-09-06 23:40:10 +0200390
Bram Moolenaar75537a92016-09-05 22:45:28 +0200391 timer_next = timer->tr_next;
392 did_one = TRUE;
Bram Moolenaar1e8e1452017-06-24 16:03:06 +0200393 timer_busy = save_timer_busy;
394 vgetc_busy = save_vgetc_busy;
Bram Moolenaare723c422017-09-06 23:40:10 +0200395 if (did_uncaught_emsg)
Bram Moolenaarc577d812017-07-08 22:37:34 +0200396 ++timer->tr_emsg_count;
Bram Moolenaare723c422017-09-06 23:40:10 +0200397 did_emsg = save_did_emsg;
398 called_emsg = save_called_emsg;
399 trylevel = save_trylevel;
400 did_throw = save_did_throw;
401 current_exception = save_current_exception;
Bram Moolenaarb0f42ba2018-05-12 15:38:26 +0200402 restore_vimvars(&vvsave);
Bram Moolenaar02e177d2017-08-26 23:43:28 +0200403 if (must_redraw != 0)
404 need_update_screen = TRUE;
405 must_redraw = must_redraw > save_must_redraw
406 ? must_redraw : save_must_redraw;
Bram Moolenaarf5291f32017-09-14 22:55:37 +0200407 set_pressedreturn(save_ex_pressedreturn);
Bram Moolenaar75537a92016-09-05 22:45:28 +0200408
409 /* Only fire the timer again if it repeats and stop_timer() wasn't
410 * called while inside the callback (tr_id == -1). */
Bram Moolenaarc577d812017-07-08 22:37:34 +0200411 if (timer->tr_repeat != 0 && timer->tr_id != -1
412 && timer->tr_emsg_count < 3)
Bram Moolenaar75537a92016-09-05 22:45:28 +0200413 {
414 profile_setlimit(timer->tr_interval, &timer->tr_due);
Bram Moolenaar51b0f372017-11-18 18:52:04 +0100415 this_due = proftime_time_left(&timer->tr_due, &now);
Bram Moolenaar75537a92016-09-05 22:45:28 +0200416 if (this_due < 1)
417 this_due = 1;
418 if (timer->tr_repeat > 0)
419 --timer->tr_repeat;
Bram Moolenaar975b5272016-03-15 23:10:59 +0100420 }
Bram Moolenaar75537a92016-09-05 22:45:28 +0200421 else
422 {
423 this_due = -1;
424 remove_timer(timer);
425 free_timer(timer);
426 }
Bram Moolenaar975b5272016-03-15 23:10:59 +0100427 }
Bram Moolenaar75537a92016-09-05 22:45:28 +0200428 if (this_due > 0 && (next_due == -1 || next_due > this_due))
429 next_due = this_due;
Bram Moolenaar975b5272016-03-15 23:10:59 +0100430 }
431
432 if (did_one)
Bram Moolenaar02e177d2017-08-26 23:43:28 +0200433 redraw_after_callback(need_update_screen);
Bram Moolenaar975b5272016-03-15 23:10:59 +0100434
Bram Moolenaarc3719bd2017-11-18 22:13:31 +0100435#ifdef FEAT_BEVAL_TERM
Bram Moolenaar51b0f372017-11-18 18:52:04 +0100436 if (bevalexpr_due_set)
437 {
438 this_due = proftime_time_left(&bevalexpr_due, &now);
439 if (this_due <= 1)
440 {
441 bevalexpr_due_set = FALSE;
Bram Moolenaar51b0f372017-11-18 18:52:04 +0100442 if (balloonEval == NULL)
443 {
Bram Moolenaarca4b6132018-06-28 12:05:11 +0200444 balloonEval = (BalloonEval *)alloc_clear(sizeof(BalloonEval));
Bram Moolenaar51b0f372017-11-18 18:52:04 +0100445 balloonEvalForTerm = TRUE;
446 }
447 if (balloonEval != NULL)
448 general_beval_cb(balloonEval, 0);
449 }
Bram Moolenaar1c17ffa2018-04-24 15:19:04 +0200450 else if (next_due == -1 || next_due > this_due)
Bram Moolenaar51b0f372017-11-18 18:52:04 +0100451 next_due = this_due;
452 }
453#endif
Bram Moolenaar56bc8e22018-05-10 18:05:56 +0200454#ifdef FEAT_TERMINAL
455 /* Some terminal windows may need their buffer updated. */
456 next_due = term_check_timers(next_due, &now);
457#endif
Bram Moolenaar51b0f372017-11-18 18:52:04 +0100458
Bram Moolenaar75537a92016-09-05 22:45:28 +0200459 return current_id != last_timer_id ? 1 : next_due;
Bram Moolenaar975b5272016-03-15 23:10:59 +0100460}
461
462/*
463 * Find a timer by ID. Returns NULL if not found;
464 */
465 timer_T *
Bram Moolenaar75537a92016-09-05 22:45:28 +0200466find_timer(long id)
Bram Moolenaar975b5272016-03-15 23:10:59 +0100467{
468 timer_T *timer;
469
Bram Moolenaar75537a92016-09-05 22:45:28 +0200470 if (id >= 0)
471 {
472 for (timer = first_timer; timer != NULL; timer = timer->tr_next)
473 if (timer->tr_id == id)
474 return timer;
475 }
476 return NULL;
Bram Moolenaar975b5272016-03-15 23:10:59 +0100477}
478
479
480/*
481 * Stop a timer and delete it.
482 */
483 void
484stop_timer(timer_T *timer)
485{
Bram Moolenaar75537a92016-09-05 22:45:28 +0200486 if (timer->tr_firing)
487 /* Free the timer after the callback returns. */
488 timer->tr_id = -1;
489 else
490 {
491 remove_timer(timer);
492 free_timer(timer);
493 }
Bram Moolenaar975b5272016-03-15 23:10:59 +0100494}
Bram Moolenaare3188e22016-05-31 21:13:04 +0200495
Bram Moolenaar8e97bd72016-08-06 22:05:07 +0200496 void
Bram Moolenaarb73598e2016-08-07 18:22:53 +0200497stop_all_timers(void)
498{
Bram Moolenaar75537a92016-09-05 22:45:28 +0200499 timer_T *timer;
500 timer_T *timer_next;
501
502 for (timer = first_timer; timer != NULL; timer = timer_next)
503 {
504 timer_next = timer->tr_next;
505 stop_timer(timer);
506 }
Bram Moolenaarb73598e2016-08-07 18:22:53 +0200507}
508
509 void
Bram Moolenaar8e97bd72016-08-06 22:05:07 +0200510add_timer_info(typval_T *rettv, timer_T *timer)
511{
512 list_T *list = rettv->vval.v_list;
513 dict_T *dict = dict_alloc();
514 dictitem_T *di;
515 long remaining;
516 proftime_T now;
517
518 if (dict == NULL)
519 return;
520 list_append_dict(list, dict);
521
Bram Moolenaare0be1672018-07-08 16:50:37 +0200522 dict_add_number(dict, "id", timer->tr_id);
523 dict_add_number(dict, "time", (long)timer->tr_interval);
Bram Moolenaar8e97bd72016-08-06 22:05:07 +0200524
525 profile_start(&now);
Bram Moolenaar51b0f372017-11-18 18:52:04 +0100526 remaining = proftime_time_left(&timer->tr_due, &now);
Bram Moolenaare0be1672018-07-08 16:50:37 +0200527 dict_add_number(dict, "remaining", (long)remaining);
Bram Moolenaar8e97bd72016-08-06 22:05:07 +0200528
Bram Moolenaare0be1672018-07-08 16:50:37 +0200529 dict_add_number(dict, "repeat",
530 (long)(timer->tr_repeat < 0 ? -1 : timer->tr_repeat + 1));
531 dict_add_number(dict, "paused", (long)(timer->tr_paused));
Bram Moolenaar8e97bd72016-08-06 22:05:07 +0200532
533 di = dictitem_alloc((char_u *)"callback");
534 if (di != NULL)
535 {
536 if (dict_add(dict, di) == FAIL)
537 vim_free(di);
538 else if (timer->tr_partial != NULL)
539 {
540 di->di_tv.v_type = VAR_PARTIAL;
541 di->di_tv.vval.v_partial = timer->tr_partial;
542 ++timer->tr_partial->pt_refcount;
543 }
544 else
545 {
546 di->di_tv.v_type = VAR_FUNC;
547 di->di_tv.vval.v_string = vim_strsave(timer->tr_callback);
548 }
Bram Moolenaar8e97bd72016-08-06 22:05:07 +0200549 }
550}
551
552 void
553add_timer_info_all(typval_T *rettv)
554{
555 timer_T *timer;
556
557 for (timer = first_timer; timer != NULL; timer = timer->tr_next)
Bram Moolenaar75537a92016-09-05 22:45:28 +0200558 if (timer->tr_id != -1)
559 add_timer_info(rettv, timer);
Bram Moolenaar8e97bd72016-08-06 22:05:07 +0200560}
561
Bram Moolenaare3188e22016-05-31 21:13:04 +0200562/*
563 * Mark references in partials of timers.
564 */
565 int
566set_ref_in_timer(int copyID)
567{
568 int abort = FALSE;
569 timer_T *timer;
570 typval_T tv;
571
572 for (timer = first_timer; timer != NULL; timer = timer->tr_next)
573 {
Bram Moolenaar1e96d9b2016-07-29 22:15:09 +0200574 if (timer->tr_partial != NULL)
575 {
576 tv.v_type = VAR_PARTIAL;
577 tv.vval.v_partial = timer->tr_partial;
578 }
579 else
580 {
581 tv.v_type = VAR_FUNC;
582 tv.vval.v_string = timer->tr_callback;
583 }
Bram Moolenaare3188e22016-05-31 21:13:04 +0200584 abort = abort || set_ref_in_item(&tv, copyID, NULL, NULL);
585 }
586 return abort;
587}
Bram Moolenaar623e2632016-07-30 22:47:56 +0200588
589# if defined(EXITFREE) || defined(PROTO)
590 void
591timer_free_all()
592{
593 timer_T *timer;
594
595 while (first_timer != NULL)
596 {
597 timer = first_timer;
598 remove_timer(timer);
599 free_timer(timer);
600 }
601}
602# endif
Bram Moolenaar975b5272016-03-15 23:10:59 +0100603# endif
604
Bram Moolenaar113e1072019-01-20 15:30:40 +0100605#if defined(FEAT_SYN_HL) && defined(FEAT_RELTIME) && defined(FEAT_FLOAT) && defined(FEAT_PROFILE)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +0200606# if defined(HAVE_MATH_H)
607# include <math.h>
608# endif
609
610/*
611 * Divide the time "tm" by "count" and store in "tm2".
612 */
613 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +0100614profile_divide(proftime_T *tm, int count, proftime_T *tm2)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +0200615{
616 if (count == 0)
617 profile_zero(tm2);
618 else
619 {
Bram Moolenaar4f974752019-02-17 17:44:42 +0100620# ifdef MSWIN
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +0200621 tm2->QuadPart = tm->QuadPart / count;
622# else
623 double usec = (tm->tv_sec * 1000000.0 + tm->tv_usec) / count;
624
625 tm2->tv_sec = floor(usec / 1000000.0);
Bram Moolenaara2e14fc2013-06-10 20:10:44 +0200626 tm2->tv_usec = vim_round(usec - (tm2->tv_sec * 1000000.0));
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +0200627# endif
628 }
629}
630#endif
631
Bram Moolenaar76929292008-01-06 19:07:36 +0000632# if defined(FEAT_PROFILE) || defined(PROTO)
633/*
634 * Functions for profiling.
635 */
Bram Moolenaarf28dbce2016-01-29 22:03:47 +0100636static void script_dump_profile(FILE *fd);
Bram Moolenaar76929292008-01-06 19:07:36 +0000637static proftime_T prof_wait_time;
638
Bram Moolenaar433f7c82006-03-21 21:29:36 +0000639/*
Bram Moolenaar05159a02005-02-26 23:04:13 +0000640 * Add the time "tm2" to "tm".
641 */
642 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +0100643profile_add(proftime_T *tm, proftime_T *tm2)
Bram Moolenaar05159a02005-02-26 23:04:13 +0000644{
Bram Moolenaar4f974752019-02-17 17:44:42 +0100645# ifdef MSWIN
Bram Moolenaar8cd06ca2005-02-28 22:44:58 +0000646 tm->QuadPart += tm2->QuadPart;
647# else
Bram Moolenaar05159a02005-02-26 23:04:13 +0000648 tm->tv_usec += tm2->tv_usec;
649 tm->tv_sec += tm2->tv_sec;
650 if (tm->tv_usec >= 1000000)
651 {
652 tm->tv_usec -= 1000000;
653 ++tm->tv_sec;
654 }
Bram Moolenaar8cd06ca2005-02-28 22:44:58 +0000655# endif
Bram Moolenaar05159a02005-02-26 23:04:13 +0000656}
657
658/*
Bram Moolenaar1056d982006-03-09 22:37:52 +0000659 * Add the "self" time from the total time and the children's time.
660 */
661 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +0100662profile_self(proftime_T *self, proftime_T *total, proftime_T *children)
Bram Moolenaar1056d982006-03-09 22:37:52 +0000663{
664 /* Check that the result won't be negative. Can happen with recursive
665 * calls. */
Bram Moolenaar4f974752019-02-17 17:44:42 +0100666#ifdef MSWIN
Bram Moolenaar1056d982006-03-09 22:37:52 +0000667 if (total->QuadPart <= children->QuadPart)
668 return;
669#else
670 if (total->tv_sec < children->tv_sec
671 || (total->tv_sec == children->tv_sec
672 && total->tv_usec <= children->tv_usec))
673 return;
674#endif
675 profile_add(self, total);
676 profile_sub(self, children);
677}
678
679/*
Bram Moolenaar05159a02005-02-26 23:04:13 +0000680 * Get the current waittime.
681 */
682 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +0100683profile_get_wait(proftime_T *tm)
Bram Moolenaar05159a02005-02-26 23:04:13 +0000684{
685 *tm = prof_wait_time;
686}
687
688/*
689 * Subtract the passed waittime since "tm" from "tma".
690 */
691 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +0100692profile_sub_wait(proftime_T *tm, proftime_T *tma)
Bram Moolenaar05159a02005-02-26 23:04:13 +0000693{
694 proftime_T tm3 = prof_wait_time;
695
696 profile_sub(&tm3, tm);
697 profile_sub(tma, &tm3);
698}
699
700/*
701 * Return TRUE if "tm1" and "tm2" are equal.
702 */
703 int
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +0100704profile_equal(proftime_T *tm1, proftime_T *tm2)
Bram Moolenaar05159a02005-02-26 23:04:13 +0000705{
Bram Moolenaar4f974752019-02-17 17:44:42 +0100706# ifdef MSWIN
Bram Moolenaar8cd06ca2005-02-28 22:44:58 +0000707 return (tm1->QuadPart == tm2->QuadPart);
708# else
Bram Moolenaar05159a02005-02-26 23:04:13 +0000709 return (tm1->tv_usec == tm2->tv_usec && tm1->tv_sec == tm2->tv_sec);
Bram Moolenaar8cd06ca2005-02-28 22:44:58 +0000710# endif
711}
712
713/*
714 * Return <0, 0 or >0 if "tm1" < "tm2", "tm1" == "tm2" or "tm1" > "tm2"
715 */
716 int
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +0100717profile_cmp(const proftime_T *tm1, const proftime_T *tm2)
Bram Moolenaar8cd06ca2005-02-28 22:44:58 +0000718{
Bram Moolenaar4f974752019-02-17 17:44:42 +0100719# ifdef MSWIN
Bram Moolenaar8cd06ca2005-02-28 22:44:58 +0000720 return (int)(tm2->QuadPart - tm1->QuadPart);
721# else
722 if (tm1->tv_sec == tm2->tv_sec)
723 return tm2->tv_usec - tm1->tv_usec;
724 return tm2->tv_sec - tm1->tv_sec;
725# endif
Bram Moolenaar05159a02005-02-26 23:04:13 +0000726}
727
Bram Moolenaar05159a02005-02-26 23:04:13 +0000728static char_u *profile_fname = NULL;
Bram Moolenaar9b2200a2006-03-20 21:55:45 +0000729static proftime_T pause_time;
Bram Moolenaar05159a02005-02-26 23:04:13 +0000730
731/*
732 * ":profile cmd args"
733 */
734 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +0100735ex_profile(exarg_T *eap)
Bram Moolenaar05159a02005-02-26 23:04:13 +0000736{
737 char_u *e;
738 int len;
739
740 e = skiptowhite(eap->arg);
Bram Moolenaara93fa7e2006-04-17 22:14:47 +0000741 len = (int)(e - eap->arg);
Bram Moolenaar05159a02005-02-26 23:04:13 +0000742 e = skipwhite(e);
743
744 if (len == 5 && STRNCMP(eap->arg, "start", 5) == 0 && *e != NUL)
745 {
746 vim_free(profile_fname);
Bram Moolenaard94682f2015-04-13 15:37:56 +0200747 profile_fname = expand_env_save_opt(e, TRUE);
Bram Moolenaar9b2200a2006-03-20 21:55:45 +0000748 do_profiling = PROF_YES;
Bram Moolenaar05159a02005-02-26 23:04:13 +0000749 profile_zero(&prof_wait_time);
750 set_vim_var_nr(VV_PROFILING, 1L);
751 }
Bram Moolenaar9b2200a2006-03-20 21:55:45 +0000752 else if (do_profiling == PROF_NONE)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100753 emsg(_("E750: First use \":profile start {fname}\""));
Bram Moolenaar9b2200a2006-03-20 21:55:45 +0000754 else if (STRCMP(eap->arg, "pause") == 0)
755 {
756 if (do_profiling == PROF_YES)
757 profile_start(&pause_time);
758 do_profiling = PROF_PAUSED;
759 }
760 else if (STRCMP(eap->arg, "continue") == 0)
761 {
762 if (do_profiling == PROF_PAUSED)
763 {
764 profile_end(&pause_time);
765 profile_add(&prof_wait_time, &pause_time);
766 }
767 do_profiling = PROF_YES;
768 }
Bram Moolenaar05159a02005-02-26 23:04:13 +0000769 else
770 {
771 /* The rest is similar to ":breakadd". */
772 ex_breakadd(eap);
773 }
774}
775
Bram Moolenaarf86f26c2010-02-03 15:14:22 +0100776/* Command line expansion for :profile. */
777static enum
778{
779 PEXP_SUBCMD, /* expand :profile sub-commands */
Bram Moolenaar49789dc2011-02-25 14:46:09 +0100780 PEXP_FUNC /* expand :profile func {funcname} */
Bram Moolenaarf86f26c2010-02-03 15:14:22 +0100781} pexpand_what;
782
783static char *pexpand_cmds[] = {
784 "start",
785#define PROFCMD_START 0
786 "pause",
787#define PROFCMD_PAUSE 1
788 "continue",
789#define PROFCMD_CONTINUE 2
790 "func",
791#define PROFCMD_FUNC 3
792 "file",
793#define PROFCMD_FILE 4
794 NULL
795#define PROFCMD_LAST 5
796};
797
798/*
799 * Function given to ExpandGeneric() to obtain the profile command
800 * specific expansion.
801 */
802 char_u *
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +0100803get_profile_name(expand_T *xp UNUSED, int idx)
Bram Moolenaarf86f26c2010-02-03 15:14:22 +0100804{
805 switch (pexpand_what)
806 {
807 case PEXP_SUBCMD:
808 return (char_u *)pexpand_cmds[idx];
809 /* case PEXP_FUNC: TODO */
810 default:
811 return NULL;
812 }
813}
814
815/*
816 * Handle command line completion for :profile command.
817 */
818 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +0100819set_context_in_profile_cmd(expand_T *xp, char_u *arg)
Bram Moolenaarf86f26c2010-02-03 15:14:22 +0100820{
821 char_u *end_subcmd;
Bram Moolenaarf86f26c2010-02-03 15:14:22 +0100822
823 /* Default: expand subcommands. */
824 xp->xp_context = EXPAND_PROFILE;
825 pexpand_what = PEXP_SUBCMD;
826 xp->xp_pattern = arg;
827
828 end_subcmd = skiptowhite(arg);
829 if (*end_subcmd == NUL)
830 return;
831
Bram Moolenaar8b9c05f2010-03-02 17:54:33 +0100832 if (end_subcmd - arg == 5 && STRNCMP(arg, "start", 5) == 0)
Bram Moolenaarf86f26c2010-02-03 15:14:22 +0100833 {
834 xp->xp_context = EXPAND_FILES;
835 xp->xp_pattern = skipwhite(end_subcmd);
836 return;
837 }
838
839 /* TODO: expand function names after "func" */
840 xp->xp_context = EXPAND_NOTHING;
841}
842
Bram Moolenaar05159a02005-02-26 23:04:13 +0000843/*
844 * Dump the profiling info.
845 */
846 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +0100847profile_dump(void)
Bram Moolenaar05159a02005-02-26 23:04:13 +0000848{
849 FILE *fd;
850
851 if (profile_fname != NULL)
852 {
Bram Moolenaarbfd8fc02005-09-20 23:22:24 +0000853 fd = mch_fopen((char *)profile_fname, "w");
Bram Moolenaar05159a02005-02-26 23:04:13 +0000854 if (fd == NULL)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100855 semsg(_(e_notopen), profile_fname);
Bram Moolenaar05159a02005-02-26 23:04:13 +0000856 else
857 {
Bram Moolenaar05159a02005-02-26 23:04:13 +0000858 script_dump_profile(fd);
Bram Moolenaar8cd06ca2005-02-28 22:44:58 +0000859 func_dump_profile(fd);
Bram Moolenaar05159a02005-02-26 23:04:13 +0000860 fclose(fd);
861 }
862 }
863}
864
865/*
866 * Start profiling script "fp".
867 */
868 static void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +0100869script_do_profile(scriptitem_T *si)
Bram Moolenaar05159a02005-02-26 23:04:13 +0000870{
871 si->sn_pr_count = 0;
872 profile_zero(&si->sn_pr_total);
873 profile_zero(&si->sn_pr_self);
874
875 ga_init2(&si->sn_prl_ga, sizeof(sn_prl_T), 100);
876 si->sn_prl_idx = -1;
877 si->sn_prof_on = TRUE;
878 si->sn_pr_nest = 0;
879}
880
881/*
Bram Moolenaar67435d92017-10-19 21:04:37 +0200882 * Save time when starting to invoke another script or function.
Bram Moolenaar05159a02005-02-26 23:04:13 +0000883 */
884 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +0100885script_prof_save(
886 proftime_T *tm) /* place to store wait time */
Bram Moolenaar05159a02005-02-26 23:04:13 +0000887{
888 scriptitem_T *si;
889
Bram Moolenaarf29c1c62018-09-10 21:05:02 +0200890 if (current_sctx.sc_sid > 0 && current_sctx.sc_sid <= script_items.ga_len)
Bram Moolenaar05159a02005-02-26 23:04:13 +0000891 {
Bram Moolenaarf29c1c62018-09-10 21:05:02 +0200892 si = &SCRIPT_ITEM(current_sctx.sc_sid);
Bram Moolenaar05159a02005-02-26 23:04:13 +0000893 if (si->sn_prof_on && si->sn_pr_nest++ == 0)
894 profile_start(&si->sn_pr_child);
895 }
896 profile_get_wait(tm);
897}
898
899/*
900 * Count time spent in children after invoking another script or function.
901 */
902 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +0100903script_prof_restore(proftime_T *tm)
Bram Moolenaar05159a02005-02-26 23:04:13 +0000904{
905 scriptitem_T *si;
906
Bram Moolenaarf29c1c62018-09-10 21:05:02 +0200907 if (current_sctx.sc_sid > 0 && current_sctx.sc_sid <= script_items.ga_len)
Bram Moolenaar05159a02005-02-26 23:04:13 +0000908 {
Bram Moolenaarf29c1c62018-09-10 21:05:02 +0200909 si = &SCRIPT_ITEM(current_sctx.sc_sid);
Bram Moolenaar05159a02005-02-26 23:04:13 +0000910 if (si->sn_prof_on && --si->sn_pr_nest == 0)
911 {
912 profile_end(&si->sn_pr_child);
913 profile_sub_wait(tm, &si->sn_pr_child); /* don't count wait time */
914 profile_add(&si->sn_pr_children, &si->sn_pr_child);
915 profile_add(&si->sn_prl_children, &si->sn_pr_child);
916 }
917 }
918}
919
920static proftime_T inchar_time;
921
922/*
923 * Called when starting to wait for the user to type a character.
924 */
925 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +0100926prof_inchar_enter(void)
Bram Moolenaar05159a02005-02-26 23:04:13 +0000927{
928 profile_start(&inchar_time);
929}
930
931/*
932 * Called when finished waiting for the user to type a character.
933 */
934 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +0100935prof_inchar_exit(void)
Bram Moolenaar05159a02005-02-26 23:04:13 +0000936{
937 profile_end(&inchar_time);
938 profile_add(&prof_wait_time, &inchar_time);
939}
940
941/*
942 * Dump the profiling results for all scripts in file "fd".
943 */
944 static void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +0100945script_dump_profile(FILE *fd)
Bram Moolenaar05159a02005-02-26 23:04:13 +0000946{
947 int id;
948 scriptitem_T *si;
949 int i;
950 FILE *sfd;
951 sn_prl_T *pp;
952
953 for (id = 1; id <= script_items.ga_len; ++id)
954 {
955 si = &SCRIPT_ITEM(id);
956 if (si->sn_prof_on)
957 {
958 fprintf(fd, "SCRIPT %s\n", si->sn_name);
959 if (si->sn_pr_count == 1)
960 fprintf(fd, "Sourced 1 time\n");
961 else
962 fprintf(fd, "Sourced %d times\n", si->sn_pr_count);
963 fprintf(fd, "Total time: %s\n", profile_msg(&si->sn_pr_total));
964 fprintf(fd, " Self time: %s\n", profile_msg(&si->sn_pr_self));
965 fprintf(fd, "\n");
966 fprintf(fd, "count total (s) self (s)\n");
967
Bram Moolenaarbfd8fc02005-09-20 23:22:24 +0000968 sfd = mch_fopen((char *)si->sn_name, "r");
Bram Moolenaar05159a02005-02-26 23:04:13 +0000969 if (sfd == NULL)
970 fprintf(fd, "Cannot open file!\n");
971 else
972 {
Bram Moolenaar67435d92017-10-19 21:04:37 +0200973 /* Keep going till the end of file, so that trailing
974 * continuation lines are listed. */
975 for (i = 0; ; ++i)
Bram Moolenaar05159a02005-02-26 23:04:13 +0000976 {
977 if (vim_fgets(IObuff, IOSIZE, sfd))
978 break;
Bram Moolenaarac112f02017-12-05 16:46:28 +0100979 /* When a line has been truncated, append NL, taking care
980 * of multi-byte characters . */
981 if (IObuff[IOSIZE - 2] != NUL && IObuff[IOSIZE - 2] != NL)
982 {
983 int n = IOSIZE - 2;
Bram Moolenaar13505972019-01-24 15:04:48 +0100984
Bram Moolenaarac112f02017-12-05 16:46:28 +0100985 if (enc_utf8)
986 {
987 /* Move to the first byte of this char.
988 * utf_head_off() doesn't work, because it checks
989 * for a truncated character. */
990 while (n > 0 && (IObuff[n] & 0xc0) == 0x80)
991 --n;
992 }
993 else if (has_mbyte)
994 n -= mb_head_off(IObuff, IObuff + n);
Bram Moolenaarac112f02017-12-05 16:46:28 +0100995 IObuff[n] = NL;
996 IObuff[n + 1] = NUL;
997 }
Bram Moolenaar67435d92017-10-19 21:04:37 +0200998 if (i < si->sn_prl_ga.ga_len
999 && (pp = &PRL_ITEM(si, i))->snp_count > 0)
Bram Moolenaar05159a02005-02-26 23:04:13 +00001000 {
1001 fprintf(fd, "%5d ", pp->snp_count);
1002 if (profile_equal(&pp->sn_prl_total, &pp->sn_prl_self))
1003 fprintf(fd, " ");
1004 else
1005 fprintf(fd, "%s ", profile_msg(&pp->sn_prl_total));
1006 fprintf(fd, "%s ", profile_msg(&pp->sn_prl_self));
1007 }
1008 else
1009 fprintf(fd, " ");
1010 fprintf(fd, "%s", IObuff);
1011 }
1012 fclose(sfd);
1013 }
1014 fprintf(fd, "\n");
1015 }
1016 }
1017}
1018
1019/*
1020 * Return TRUE when a function defined in the current script should be
1021 * profiled.
1022 */
1023 int
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01001024prof_def_func(void)
Bram Moolenaar05159a02005-02-26 23:04:13 +00001025{
Bram Moolenaarf29c1c62018-09-10 21:05:02 +02001026 if (current_sctx.sc_sid > 0)
1027 return SCRIPT_ITEM(current_sctx.sc_sid).sn_pr_force;
Bram Moolenaar53180ce2005-07-05 21:48:14 +00001028 return FALSE;
Bram Moolenaar05159a02005-02-26 23:04:13 +00001029}
1030
1031# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001032#endif
1033
1034/*
1035 * If 'autowrite' option set, try to write the file.
1036 * Careful: autocommands may make "buf" invalid!
1037 *
1038 * return FAIL for failure, OK otherwise
1039 */
1040 int
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01001041autowrite(buf_T *buf, int forceit)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001042{
Bram Moolenaar373154b2007-02-13 05:19:30 +00001043 int r;
Bram Moolenaar7c0a2f32016-07-10 22:11:16 +02001044 bufref_T bufref;
Bram Moolenaar373154b2007-02-13 05:19:30 +00001045
Bram Moolenaar071d4272004-06-13 20:20:40 +00001046 if (!(p_aw || p_awa) || !p_write
1047#ifdef FEAT_QUICKFIX
Bram Moolenaar373154b2007-02-13 05:19:30 +00001048 /* never autowrite a "nofile" or "nowrite" buffer */
1049 || bt_dontwrite(buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001050#endif
Bram Moolenaar373154b2007-02-13 05:19:30 +00001051 || (!forceit && buf->b_p_ro) || buf->b_ffname == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001052 return FAIL;
Bram Moolenaar7c0a2f32016-07-10 22:11:16 +02001053 set_bufref(&bufref, buf);
Bram Moolenaar373154b2007-02-13 05:19:30 +00001054 r = buf_write_all(buf, forceit);
1055
1056 /* Writing may succeed but the buffer still changed, e.g., when there is a
1057 * conversion error. We do want to return FAIL then. */
Bram Moolenaar7c0a2f32016-07-10 22:11:16 +02001058 if (bufref_valid(&bufref) && bufIsChanged(buf))
Bram Moolenaar373154b2007-02-13 05:19:30 +00001059 r = FAIL;
1060 return r;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001061}
1062
1063/*
Bram Moolenaar8c9e7b02018-08-30 13:07:17 +02001064 * Flush all buffers, except the ones that are readonly or are never written.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001065 */
1066 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01001067autowrite_all(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001068{
1069 buf_T *buf;
1070
1071 if (!(p_aw || p_awa) || !p_write)
1072 return;
Bram Moolenaar29323592016-07-24 22:04:11 +02001073 FOR_ALL_BUFFERS(buf)
Bram Moolenaar8c9e7b02018-08-30 13:07:17 +02001074 if (bufIsChanged(buf) && !buf->b_p_ro && !bt_dontwrite(buf))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001075 {
Bram Moolenaar7c0a2f32016-07-10 22:11:16 +02001076 bufref_T bufref;
1077
1078 set_bufref(&bufref, buf);
Bram Moolenaarf2bd8ef2018-03-04 18:08:14 +01001079
Bram Moolenaar071d4272004-06-13 20:20:40 +00001080 (void)buf_write_all(buf, FALSE);
Bram Moolenaarf2bd8ef2018-03-04 18:08:14 +01001081
Bram Moolenaar071d4272004-06-13 20:20:40 +00001082 /* an autocommand may have deleted the buffer */
Bram Moolenaar7c0a2f32016-07-10 22:11:16 +02001083 if (!bufref_valid(&bufref))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001084 buf = firstbuf;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001085 }
1086}
1087
1088/*
Bram Moolenaar45d3b142013-11-09 03:31:51 +01001089 * Return TRUE if buffer was changed and cannot be abandoned.
1090 * For flags use the CCGD_ values.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001091 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001092 int
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01001093check_changed(buf_T *buf, int flags)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001094{
Bram Moolenaar7c0a2f32016-07-10 22:11:16 +02001095 int forceit = (flags & CCGD_FORCEIT);
Bram Moolenaar7c0a2f32016-07-10 22:11:16 +02001096 bufref_T bufref;
1097
1098 set_bufref(&bufref, buf);
Bram Moolenaar45d3b142013-11-09 03:31:51 +01001099
Bram Moolenaar071d4272004-06-13 20:20:40 +00001100 if ( !forceit
1101 && bufIsChanged(buf)
Bram Moolenaar45d3b142013-11-09 03:31:51 +01001102 && ((flags & CCGD_MULTWIN) || buf->b_nwindows <= 1)
1103 && (!(flags & CCGD_AW) || autowrite(buf, forceit) == FAIL))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001104 {
1105#if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG)
1106 if ((p_confirm || cmdmod.confirm) && p_write)
1107 {
1108 buf_T *buf2;
1109 int count = 0;
1110
Bram Moolenaar45d3b142013-11-09 03:31:51 +01001111 if (flags & CCGD_ALLBUF)
Bram Moolenaar29323592016-07-24 22:04:11 +02001112 FOR_ALL_BUFFERS(buf2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001113 if (bufIsChanged(buf2)
1114 && (buf2->b_ffname != NULL
1115# ifdef FEAT_BROWSE
1116 || cmdmod.browse
1117# endif
1118 ))
1119 ++count;
Bram Moolenaar7c0a2f32016-07-10 22:11:16 +02001120 if (!bufref_valid(&bufref))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001121 /* Autocommand deleted buffer, oops! It's not changed now. */
1122 return FALSE;
Bram Moolenaarf2bd8ef2018-03-04 18:08:14 +01001123
Bram Moolenaar071d4272004-06-13 20:20:40 +00001124 dialog_changed(buf, count > 1);
Bram Moolenaarf2bd8ef2018-03-04 18:08:14 +01001125
Bram Moolenaar7c0a2f32016-07-10 22:11:16 +02001126 if (!bufref_valid(&bufref))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001127 /* Autocommand deleted buffer, oops! It's not changed now. */
1128 return FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001129 return bufIsChanged(buf);
1130 }
1131#endif
Bram Moolenaar45d3b142013-11-09 03:31:51 +01001132 if (flags & CCGD_EXCMD)
Bram Moolenaarf5be7cd2017-08-17 16:55:13 +02001133 no_write_message();
Bram Moolenaar45d3b142013-11-09 03:31:51 +01001134 else
Bram Moolenaar7a760922018-02-19 23:10:02 +01001135 no_write_message_nobang(curbuf);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001136 return TRUE;
1137 }
1138 return FALSE;
1139}
1140
1141#if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG) || defined(PROTO)
1142
1143#if defined(FEAT_BROWSE) || defined(PROTO)
1144/*
1145 * When wanting to write a file without a file name, ask the user for a name.
1146 */
1147 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01001148browse_save_fname(buf_T *buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001149{
1150 if (buf->b_fname == NULL)
1151 {
1152 char_u *fname;
1153
Bram Moolenaar7b0294c2004-10-11 10:16:09 +00001154 fname = do_browse(BROWSE_SAVE, (char_u *)_("Save As"),
1155 NULL, NULL, NULL, NULL, buf);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001156 if (fname != NULL)
1157 {
1158 if (setfname(buf, fname, NULL, TRUE) == OK)
1159 buf->b_flags |= BF_NOTEDITED;
1160 vim_free(fname);
1161 }
1162 }
1163}
1164#endif
1165
1166/*
Bram Moolenaar9b486ca2011-05-19 18:26:40 +02001167 * Ask the user what to do when abandoning a changed buffer.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001168 * Must check 'write' option first!
1169 */
1170 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01001171dialog_changed(
1172 buf_T *buf,
1173 int checkall) /* may abandon all changed buffers */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001174{
Bram Moolenaard9462e32011-04-11 21:35:11 +02001175 char_u buff[DIALOG_MSG_SIZE];
Bram Moolenaar071d4272004-06-13 20:20:40 +00001176 int ret;
1177 buf_T *buf2;
Bram Moolenaar8218f602012-04-25 17:32:18 +02001178 exarg_T ea;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001179
Bram Moolenaar3f9a1ff2017-08-21 22:06:02 +02001180 dialog_msg(buff, _("Save changes to \"%s\"?"), buf->b_fname);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001181 if (checkall)
1182 ret = vim_dialog_yesnoallcancel(VIM_QUESTION, NULL, buff, 1);
1183 else
1184 ret = vim_dialog_yesnocancel(VIM_QUESTION, NULL, buff, 1);
1185
Bram Moolenaar8218f602012-04-25 17:32:18 +02001186 /* Init ea pseudo-structure, this is needed for the check_overwrite()
1187 * function. */
1188 ea.append = ea.forceit = FALSE;
1189
Bram Moolenaar071d4272004-06-13 20:20:40 +00001190 if (ret == VIM_YES)
1191 {
1192#ifdef FEAT_BROWSE
1193 /* May get file name, when there is none */
1194 browse_save_fname(buf);
1195#endif
Bram Moolenaar8218f602012-04-25 17:32:18 +02001196 if (buf->b_fname != NULL && check_overwrite(&ea, buf,
1197 buf->b_fname, buf->b_ffname, FALSE) == OK)
1198 /* didn't hit Cancel */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001199 (void)buf_write_all(buf, FALSE);
1200 }
1201 else if (ret == VIM_NO)
1202 {
1203 unchanged(buf, TRUE);
1204 }
1205 else if (ret == VIM_ALL)
1206 {
1207 /*
1208 * Write all modified files that can be written.
1209 * Skip readonly buffers, these need to be confirmed
1210 * individually.
1211 */
Bram Moolenaar29323592016-07-24 22:04:11 +02001212 FOR_ALL_BUFFERS(buf2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001213 {
1214 if (bufIsChanged(buf2)
1215 && (buf2->b_ffname != NULL
1216#ifdef FEAT_BROWSE
1217 || cmdmod.browse
1218#endif
1219 )
1220 && !buf2->b_p_ro)
1221 {
Bram Moolenaar7c0a2f32016-07-10 22:11:16 +02001222 bufref_T bufref;
1223
1224 set_bufref(&bufref, buf2);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001225#ifdef FEAT_BROWSE
1226 /* May get file name, when there is none */
1227 browse_save_fname(buf2);
1228#endif
Bram Moolenaar8218f602012-04-25 17:32:18 +02001229 if (buf2->b_fname != NULL && check_overwrite(&ea, buf2,
1230 buf2->b_fname, buf2->b_ffname, FALSE) == OK)
1231 /* didn't hit Cancel */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001232 (void)buf_write_all(buf2, FALSE);
Bram Moolenaarf2bd8ef2018-03-04 18:08:14 +01001233
Bram Moolenaar071d4272004-06-13 20:20:40 +00001234 /* an autocommand may have deleted the buffer */
Bram Moolenaar7c0a2f32016-07-10 22:11:16 +02001235 if (!bufref_valid(&bufref))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001236 buf2 = firstbuf;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001237 }
1238 }
1239 }
1240 else if (ret == VIM_DISCARDALL)
1241 {
1242 /*
1243 * mark all buffers as unchanged
1244 */
Bram Moolenaar29323592016-07-24 22:04:11 +02001245 FOR_ALL_BUFFERS(buf2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001246 unchanged(buf2, TRUE);
1247 }
1248}
1249#endif
1250
1251/*
1252 * Return TRUE if the buffer "buf" can be abandoned, either by making it
1253 * hidden, autowriting it or unloading it.
1254 */
1255 int
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01001256can_abandon(buf_T *buf, int forceit)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001257{
Bram Moolenaareb44a682017-08-03 22:44:55 +02001258 return ( buf_hide(buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001259 || !bufIsChanged(buf)
1260 || buf->b_nwindows > 1
1261 || autowrite(buf, forceit) == OK
1262 || forceit);
1263}
1264
Bram Moolenaar970a1b82012-03-23 18:39:18 +01001265/*
1266 * Add a buffer number to "bufnrs", unless it's already there.
1267 */
1268 static void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01001269add_bufnum(int *bufnrs, int *bufnump, int nr)
Bram Moolenaar970a1b82012-03-23 18:39:18 +01001270{
1271 int i;
1272
1273 for (i = 0; i < *bufnump; ++i)
1274 if (bufnrs[i] == nr)
1275 return;
1276 bufnrs[*bufnump] = nr;
1277 *bufnump = *bufnump + 1;
1278}
1279
Bram Moolenaar071d4272004-06-13 20:20:40 +00001280/*
1281 * Return TRUE if any buffer was changed and cannot be abandoned.
1282 * That changed buffer becomes the current buffer.
Bram Moolenaar25cdd9c2018-03-10 20:28:12 +01001283 * When "unload" is TRUE the current buffer is unloaded instead of making it
Bram Moolenaar027387f2016-01-02 22:25:52 +01001284 * hidden. This is used for ":q!".
Bram Moolenaar071d4272004-06-13 20:20:40 +00001285 */
1286 int
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01001287check_changed_any(
1288 int hidden, /* Only check hidden buffers */
1289 int unload)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001290{
Bram Moolenaar970a1b82012-03-23 18:39:18 +01001291 int ret = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001292 buf_T *buf;
1293 int save;
Bram Moolenaar970a1b82012-03-23 18:39:18 +01001294 int i;
1295 int bufnum = 0;
1296 int bufcount = 0;
1297 int *bufnrs;
Bram Moolenaar970a1b82012-03-23 18:39:18 +01001298 tabpage_T *tp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001299 win_T *wp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001300
Bram Moolenaar25cdd9c2018-03-10 20:28:12 +01001301 /* Make a list of all buffers, with the most important ones first. */
Bram Moolenaar29323592016-07-24 22:04:11 +02001302 FOR_ALL_BUFFERS(buf)
Bram Moolenaar970a1b82012-03-23 18:39:18 +01001303 ++bufcount;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001304
Bram Moolenaar970a1b82012-03-23 18:39:18 +01001305 if (bufcount == 0)
1306 return FALSE;
1307
1308 bufnrs = (int *)alloc(sizeof(int) * bufcount);
1309 if (bufnrs == NULL)
1310 return FALSE;
1311
1312 /* curbuf */
1313 bufnrs[bufnum++] = curbuf->b_fnum;
Bram Moolenaar25cdd9c2018-03-10 20:28:12 +01001314
1315 /* buffers in current tab */
Bram Moolenaar970a1b82012-03-23 18:39:18 +01001316 FOR_ALL_WINDOWS(wp)
1317 if (wp->w_buffer != curbuf)
1318 add_bufnum(bufnrs, &bufnum, wp->w_buffer->b_fnum);
1319
Bram Moolenaar25cdd9c2018-03-10 20:28:12 +01001320 /* buffers in other tabs */
Bram Moolenaar29323592016-07-24 22:04:11 +02001321 FOR_ALL_TABPAGES(tp)
Bram Moolenaar970a1b82012-03-23 18:39:18 +01001322 if (tp != curtab)
1323 for (wp = tp->tp_firstwin; wp != NULL; wp = wp->w_next)
1324 add_bufnum(bufnrs, &bufnum, wp->w_buffer->b_fnum);
Bram Moolenaar25cdd9c2018-03-10 20:28:12 +01001325
1326 /* any other buffer */
Bram Moolenaar29323592016-07-24 22:04:11 +02001327 FOR_ALL_BUFFERS(buf)
Bram Moolenaar970a1b82012-03-23 18:39:18 +01001328 add_bufnum(bufnrs, &bufnum, buf->b_fnum);
1329
1330 for (i = 0; i < bufnum; ++i)
1331 {
1332 buf = buflist_findnr(bufnrs[i]);
1333 if (buf == NULL)
1334 continue;
1335 if ((!hidden || buf->b_nwindows == 0) && bufIsChanged(buf))
1336 {
Bram Moolenaar7c0a2f32016-07-10 22:11:16 +02001337 bufref_T bufref;
1338
1339 set_bufref(&bufref, buf);
Bram Moolenaar25cdd9c2018-03-10 20:28:12 +01001340#ifdef FEAT_TERMINAL
1341 if (term_job_running(buf->b_term))
1342 {
1343 if (term_try_stop_job(buf) == FAIL)
1344 break;
1345 }
1346 else
1347#endif
Bram Moolenaar970a1b82012-03-23 18:39:18 +01001348 /* Try auto-writing the buffer. If this fails but the buffer no
Bram Moolenaar558ca4a2019-04-04 18:15:38 +02001349 * longer exists it's not changed, that's OK. */
Bram Moolenaar45d3b142013-11-09 03:31:51 +01001350 if (check_changed(buf, (p_awa ? CCGD_AW : 0)
1351 | CCGD_MULTWIN
Bram Moolenaar7c0a2f32016-07-10 22:11:16 +02001352 | CCGD_ALLBUF) && bufref_valid(&bufref))
Bram Moolenaar970a1b82012-03-23 18:39:18 +01001353 break; /* didn't save - still changes */
1354 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001355 }
1356
Bram Moolenaar970a1b82012-03-23 18:39:18 +01001357 if (i >= bufnum)
1358 goto theend;
1359
Bram Moolenaar25cdd9c2018-03-10 20:28:12 +01001360 /* Get here if "buf" cannot be abandoned. */
Bram Moolenaar970a1b82012-03-23 18:39:18 +01001361 ret = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001362 exiting = FALSE;
1363#if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG)
1364 /*
1365 * When ":confirm" used, don't give an error message.
1366 */
1367 if (!(p_confirm || cmdmod.confirm))
1368#endif
1369 {
1370 /* There must be a wait_return for this message, do_buffer()
1371 * may cause a redraw. But wait_return() is a no-op when vgetc()
1372 * is busy (Quit used from window menu), then make sure we don't
1373 * cause a scroll up. */
Bram Moolenaar61660ea2006-04-07 21:40:07 +00001374 if (vgetc_busy > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001375 {
1376 msg_row = cmdline_row;
1377 msg_col = 0;
1378 msg_didout = FALSE;
1379 }
Bram Moolenaareb44a682017-08-03 22:44:55 +02001380 if (
1381#ifdef FEAT_TERMINAL
1382 term_job_running(buf->b_term)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001383 ? semsg(_("E947: Job still running in buffer \"%s\""),
Bram Moolenaareb44a682017-08-03 22:44:55 +02001384 buf->b_fname)
1385 :
1386#endif
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001387 semsg(_("E162: No write since last change for buffer \"%s\""),
Bram Moolenaare1704ba2012-10-03 18:25:00 +02001388 buf_spname(buf) != NULL ? buf_spname(buf) : buf->b_fname))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001389 {
1390 save = no_wait_return;
1391 no_wait_return = FALSE;
1392 wait_return(FALSE);
1393 no_wait_return = save;
1394 }
1395 }
1396
Bram Moolenaar071d4272004-06-13 20:20:40 +00001397 /* Try to find a window that contains the buffer. */
1398 if (buf != curbuf)
Bram Moolenaar970a1b82012-03-23 18:39:18 +01001399 FOR_ALL_TAB_WINDOWS(tp, wp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001400 if (wp->w_buffer == buf)
1401 {
Bram Moolenaar7c0a2f32016-07-10 22:11:16 +02001402 bufref_T bufref;
1403
1404 set_bufref(&bufref, buf);
Bram Moolenaarf2bd8ef2018-03-04 18:08:14 +01001405
Bram Moolenaar970a1b82012-03-23 18:39:18 +01001406 goto_tabpage_win(tp, wp);
Bram Moolenaarf2bd8ef2018-03-04 18:08:14 +01001407
Bram Moolenaarbdace832019-03-02 10:13:42 +01001408 // Paranoia: did autocmd wipe out the buffer with changes?
Bram Moolenaar7c0a2f32016-07-10 22:11:16 +02001409 if (!bufref_valid(&bufref))
Bram Moolenaar970a1b82012-03-23 18:39:18 +01001410 goto theend;
Bram Moolenaar970a1b82012-03-23 18:39:18 +01001411 goto buf_found;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001412 }
Bram Moolenaar970a1b82012-03-23 18:39:18 +01001413buf_found:
Bram Moolenaar071d4272004-06-13 20:20:40 +00001414
1415 /* Open the changed buffer in the current window. */
1416 if (buf != curbuf)
Bram Moolenaar027387f2016-01-02 22:25:52 +01001417 set_curbuf(buf, unload ? DOBUF_UNLOAD : DOBUF_GOTO);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001418
Bram Moolenaar970a1b82012-03-23 18:39:18 +01001419theend:
1420 vim_free(bufnrs);
1421 return ret;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001422}
1423
1424/*
1425 * return FAIL if there is no file name, OK if there is one
1426 * give error message for FAIL
1427 */
1428 int
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01001429check_fname(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001430{
1431 if (curbuf->b_ffname == NULL)
1432 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001433 emsg(_(e_noname));
Bram Moolenaar071d4272004-06-13 20:20:40 +00001434 return FAIL;
1435 }
1436 return OK;
1437}
1438
1439/*
1440 * flush the contents of a buffer, unless it has no file name
1441 *
1442 * return FAIL for failure, OK otherwise
1443 */
1444 int
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01001445buf_write_all(buf_T *buf, int forceit)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001446{
1447 int retval;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001448 buf_T *old_curbuf = curbuf;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001449
1450 retval = (buf_write(buf, buf->b_ffname, buf->b_fname,
1451 (linenr_T)1, buf->b_ml.ml_line_count, NULL,
1452 FALSE, forceit, TRUE, FALSE));
Bram Moolenaar071d4272004-06-13 20:20:40 +00001453 if (curbuf != old_curbuf)
Bram Moolenaar2df6dcc2004-07-12 15:53:54 +00001454 {
Bram Moolenaar8820b482017-03-16 17:23:31 +01001455 msg_source(HL_ATTR(HLF_W));
Bram Moolenaar32526b32019-01-19 17:43:09 +01001456 msg(_("Warning: Entered other buffer unexpectedly (check autocommands)"));
Bram Moolenaar2df6dcc2004-07-12 15:53:54 +00001457 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001458 return retval;
1459}
1460
1461/*
1462 * Code to handle the argument list.
1463 */
1464
Bram Moolenaar32bbd002018-08-31 23:06:22 +02001465static int do_arglist(char_u *str, int what, int after, int will_edit);
Bram Moolenaarf28dbce2016-01-29 22:03:47 +01001466static void alist_check_arg_idx(void);
Bram Moolenaar32bbd002018-08-31 23:06:22 +02001467static void alist_add_list(int count, char_u **files, int after, int will_edit);
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001468#define AL_SET 1
1469#define AL_ADD 2
1470#define AL_DEL 3
1471
Bram Moolenaar071d4272004-06-13 20:20:40 +00001472/*
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001473 * Isolate one argument, taking backticks.
1474 * Changes the argument in-place, puts a NUL after it. Backticks remain.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001475 * Return a pointer to the start of the next argument.
1476 */
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001477 static char_u *
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01001478do_one_arg(char_u *str)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001479{
1480 char_u *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001481 int inbacktick;
1482
Bram Moolenaar071d4272004-06-13 20:20:40 +00001483 inbacktick = FALSE;
1484 for (p = str; *str; ++str)
1485 {
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001486 /* When the backslash is used for escaping the special meaning of a
1487 * character we need to keep it until wildcard expansion. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001488 if (rem_backslash(str))
1489 {
1490 *p++ = *str++;
1491 *p++ = *str;
1492 }
1493 else
1494 {
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001495 /* An item ends at a space not in backticks */
1496 if (!inbacktick && vim_isspace(*str))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001497 break;
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001498 if (*str == '`')
Bram Moolenaar071d4272004-06-13 20:20:40 +00001499 inbacktick ^= TRUE;
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00001500 *p++ = *str;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001501 }
1502 }
1503 str = skipwhite(str);
1504 *p = NUL;
1505
1506 return str;
1507}
1508
Bram Moolenaar86b68352004-12-27 21:59:20 +00001509/*
1510 * Separate the arguments in "str" and return a list of pointers in the
1511 * growarray "gap".
1512 */
Bram Moolenaar398ee732017-08-03 14:29:14 +02001513 static int
1514get_arglist(garray_T *gap, char_u *str, int escaped)
Bram Moolenaar86b68352004-12-27 21:59:20 +00001515{
1516 ga_init2(gap, (int)sizeof(char_u *), 20);
1517 while (*str != NUL)
1518 {
1519 if (ga_grow(gap, 1) == FAIL)
1520 {
1521 ga_clear(gap);
1522 return FAIL;
1523 }
1524 ((char_u **)gap->ga_data)[gap->ga_len++] = str;
1525
Bram Moolenaar398ee732017-08-03 14:29:14 +02001526 /* If str is escaped, don't handle backslashes or spaces */
1527 if (!escaped)
1528 return OK;
1529
Bram Moolenaar86b68352004-12-27 21:59:20 +00001530 /* Isolate one argument, change it in-place, put a NUL after it. */
1531 str = do_one_arg(str);
1532 }
1533 return OK;
1534}
1535
Bram Moolenaar7df351e2006-01-23 22:30:28 +00001536#if defined(FEAT_QUICKFIX) || defined(FEAT_SYN_HL) || defined(PROTO)
Bram Moolenaar13fcaaf2005-04-15 21:13:42 +00001537/*
1538 * Parse a list of arguments (file names), expand them and return in
Bram Moolenaar8f5c6f02012-06-29 12:57:06 +02001539 * "fnames[fcountp]". When "wig" is TRUE, removes files matching 'wildignore'.
Bram Moolenaar13fcaaf2005-04-15 21:13:42 +00001540 * Return FAIL or OK.
1541 */
1542 int
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01001543get_arglist_exp(
1544 char_u *str,
1545 int *fcountp,
1546 char_u ***fnamesp,
1547 int wig)
Bram Moolenaar13fcaaf2005-04-15 21:13:42 +00001548{
1549 garray_T ga;
1550 int i;
1551
Bram Moolenaar398ee732017-08-03 14:29:14 +02001552 if (get_arglist(&ga, str, TRUE) == FAIL)
Bram Moolenaar13fcaaf2005-04-15 21:13:42 +00001553 return FAIL;
Bram Moolenaar8f5c6f02012-06-29 12:57:06 +02001554 if (wig == TRUE)
1555 i = expand_wildcards(ga.ga_len, (char_u **)ga.ga_data,
1556 fcountp, fnamesp, EW_FILE|EW_NOTFOUND);
1557 else
1558 i = gen_expand_wildcards(ga.ga_len, (char_u **)ga.ga_data,
1559 fcountp, fnamesp, EW_FILE|EW_NOTFOUND);
1560
Bram Moolenaar13fcaaf2005-04-15 21:13:42 +00001561 ga_clear(&ga);
1562 return i;
1563}
1564#endif
1565
Bram Moolenaar071d4272004-06-13 20:20:40 +00001566/*
1567 * Redefine the argument list.
1568 */
1569 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01001570set_arglist(char_u *str)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001571{
Bram Moolenaar32bbd002018-08-31 23:06:22 +02001572 do_arglist(str, AL_SET, 0, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001573}
Bram Moolenaar071d4272004-06-13 20:20:40 +00001574
1575/*
1576 * "what" == AL_SET: Redefine the argument list to 'str'.
1577 * "what" == AL_ADD: add files in 'str' to the argument list after "after".
1578 * "what" == AL_DEL: remove files in 'str' from the argument list.
1579 *
1580 * Return FAIL for failure, OK otherwise.
1581 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001582 static int
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01001583do_arglist(
1584 char_u *str,
Bram Moolenaarf1d25012016-03-03 12:22:53 +01001585 int what,
Bram Moolenaar32bbd002018-08-31 23:06:22 +02001586 int after UNUSED, // 0 means before first one
1587 int will_edit) // will edit added argument
Bram Moolenaar071d4272004-06-13 20:20:40 +00001588{
1589 garray_T new_ga;
1590 int exp_count;
1591 char_u **exp_files;
1592 int i;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001593 char_u *p;
1594 int match;
Bram Moolenaar398ee732017-08-03 14:29:14 +02001595 int arg_escaped = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001596
1597 /*
Bram Moolenaar2faa29f2016-01-23 23:02:34 +01001598 * Set default argument for ":argadd" command.
1599 */
1600 if (what == AL_ADD && *str == NUL)
1601 {
1602 if (curbuf->b_ffname == NULL)
1603 return FAIL;
1604 str = curbuf->b_fname;
Bram Moolenaar398ee732017-08-03 14:29:14 +02001605 arg_escaped = FALSE;
Bram Moolenaar2faa29f2016-01-23 23:02:34 +01001606 }
1607
1608 /*
Bram Moolenaar071d4272004-06-13 20:20:40 +00001609 * Collect all file name arguments in "new_ga".
1610 */
Bram Moolenaar398ee732017-08-03 14:29:14 +02001611 if (get_arglist(&new_ga, str, arg_escaped) == FAIL)
Bram Moolenaar86b68352004-12-27 21:59:20 +00001612 return FAIL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001613
Bram Moolenaar071d4272004-06-13 20:20:40 +00001614 if (what == AL_DEL)
1615 {
1616 regmatch_T regmatch;
1617 int didone;
1618
1619 /*
1620 * Delete the items: use each item as a regexp and find a match in the
1621 * argument list.
1622 */
Bram Moolenaar71afbfe2013-03-19 16:49:16 +01001623 regmatch.rm_ic = p_fic; /* ignore case when 'fileignorecase' is set */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001624 for (i = 0; i < new_ga.ga_len && !got_int; ++i)
1625 {
1626 p = ((char_u **)new_ga.ga_data)[i];
1627 p = file_pat_to_reg_pat(p, NULL, NULL, FALSE);
1628 if (p == NULL)
1629 break;
1630 regmatch.regprog = vim_regcomp(p, p_magic ? RE_MAGIC : 0);
1631 if (regmatch.regprog == NULL)
1632 {
1633 vim_free(p);
1634 break;
1635 }
1636
1637 didone = FALSE;
1638 for (match = 0; match < ARGCOUNT; ++match)
1639 if (vim_regexec(&regmatch, alist_name(&ARGLIST[match]),
1640 (colnr_T)0))
1641 {
1642 didone = TRUE;
1643 vim_free(ARGLIST[match].ae_fname);
1644 mch_memmove(ARGLIST + match, ARGLIST + match + 1,
1645 (ARGCOUNT - match - 1) * sizeof(aentry_T));
1646 --ALIST(curwin)->al_ga.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001647 if (curwin->w_arg_idx > match)
1648 --curwin->w_arg_idx;
1649 --match;
1650 }
1651
Bram Moolenaar473de612013-06-08 18:19:48 +02001652 vim_regfree(regmatch.regprog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001653 vim_free(p);
1654 if (!didone)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001655 semsg(_(e_nomatch2), ((char_u **)new_ga.ga_data)[i]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001656 }
1657 ga_clear(&new_ga);
1658 }
1659 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00001660 {
1661 i = expand_wildcards(new_ga.ga_len, (char_u **)new_ga.ga_data,
1662 &exp_count, &exp_files, EW_DIR|EW_FILE|EW_ADDSLASH|EW_NOTFOUND);
1663 ga_clear(&new_ga);
Bram Moolenaar2db5c3b2016-01-16 22:49:34 +01001664 if (i == FAIL || exp_count == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001665 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001666 emsg(_(e_nomatch));
Bram Moolenaar071d4272004-06-13 20:20:40 +00001667 return FAIL;
1668 }
1669
Bram Moolenaar071d4272004-06-13 20:20:40 +00001670 if (what == AL_ADD)
1671 {
Bram Moolenaar32bbd002018-08-31 23:06:22 +02001672 alist_add_list(exp_count, exp_files, after, will_edit);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001673 vim_free(exp_files);
1674 }
1675 else /* what == AL_SET */
Bram Moolenaar32bbd002018-08-31 23:06:22 +02001676 alist_set(ALIST(curwin), exp_count, exp_files, will_edit, NULL, 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001677 }
1678
1679 alist_check_arg_idx();
1680
1681 return OK;
1682}
1683
1684/*
1685 * Check the validity of the arg_idx for each other window.
1686 */
1687 static void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01001688alist_check_arg_idx(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001689{
Bram Moolenaar071d4272004-06-13 20:20:40 +00001690 win_T *win;
Bram Moolenaarf740b292006-02-16 22:11:02 +00001691 tabpage_T *tp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001692
Bram Moolenaarf740b292006-02-16 22:11:02 +00001693 FOR_ALL_TAB_WINDOWS(tp, win)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001694 if (win->w_alist == curwin->w_alist)
1695 check_arg_idx(win);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001696}
1697
1698/*
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +01001699 * Return TRUE if window "win" is editing the file at the current argument
Bram Moolenaard4755bb2004-09-02 19:12:26 +00001700 * index.
1701 */
1702 static int
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01001703editing_arg_idx(win_T *win)
Bram Moolenaard4755bb2004-09-02 19:12:26 +00001704{
1705 return !(win->w_arg_idx >= WARGCOUNT(win)
1706 || (win->w_buffer->b_fnum
1707 != WARGLIST(win)[win->w_arg_idx].ae_fnum
1708 && (win->w_buffer->b_ffname == NULL
1709 || !(fullpathcmp(
1710 alist_name(&WARGLIST(win)[win->w_arg_idx]),
1711 win->w_buffer->b_ffname, TRUE) & FPC_SAME))));
1712}
1713
1714/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00001715 * Check if window "win" is editing the w_arg_idx file in its argument list.
1716 */
1717 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01001718check_arg_idx(win_T *win)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001719{
Bram Moolenaard4755bb2004-09-02 19:12:26 +00001720 if (WARGCOUNT(win) > 1 && !editing_arg_idx(win))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001721 {
1722 /* We are not editing the current entry in the argument list.
1723 * Set "arg_had_last" if we are editing the last one. */
1724 win->w_arg_idx_invalid = TRUE;
1725 if (win->w_arg_idx != WARGCOUNT(win) - 1
1726 && arg_had_last == FALSE
Bram Moolenaar071d4272004-06-13 20:20:40 +00001727 && ALIST(win) == &global_alist
Bram Moolenaar071d4272004-06-13 20:20:40 +00001728 && GARGCOUNT > 0
1729 && win->w_arg_idx < GARGCOUNT
1730 && (win->w_buffer->b_fnum == GARGLIST[GARGCOUNT - 1].ae_fnum
1731 || (win->w_buffer->b_ffname != NULL
1732 && (fullpathcmp(alist_name(&GARGLIST[GARGCOUNT - 1]),
1733 win->w_buffer->b_ffname, TRUE) & FPC_SAME))))
1734 arg_had_last = TRUE;
1735 }
1736 else
1737 {
1738 /* We are editing the current entry in the argument list.
1739 * Set "arg_had_last" if it's also the last one */
1740 win->w_arg_idx_invalid = FALSE;
1741 if (win->w_arg_idx == WARGCOUNT(win) - 1
Bram Moolenaar4033c552017-09-16 20:54:51 +02001742 && win->w_alist == &global_alist)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001743 arg_had_last = TRUE;
1744 }
1745}
1746
1747/*
1748 * ":args", ":argslocal" and ":argsglobal".
1749 */
1750 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01001751ex_args(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001752{
1753 int i;
1754
1755 if (eap->cmdidx != CMD_args)
1756 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00001757 alist_unlink(ALIST(curwin));
1758 if (eap->cmdidx == CMD_argglobal)
1759 ALIST(curwin) = &global_alist;
1760 else /* eap->cmdidx == CMD_arglocal */
1761 alist_new();
Bram Moolenaar071d4272004-06-13 20:20:40 +00001762 }
1763
Bram Moolenaar2ac372c2018-12-28 19:06:47 +01001764 if (*eap->arg != NUL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001765 {
1766 /*
1767 * ":args file ..": define new argument list, handle like ":next"
1768 * Also for ":argslocal file .." and ":argsglobal file ..".
1769 */
1770 ex_next(eap);
1771 }
Bram Moolenaar0c72fe42018-03-29 16:04:08 +02001772 else if (eap->cmdidx == CMD_args)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001773 {
1774 /*
1775 * ":args": list arguments.
1776 */
1777 if (ARGCOUNT > 0)
1778 {
Bram Moolenaar405dadb2018-04-20 22:48:58 +02001779 char_u **items = (char_u **)alloc(sizeof(char_u *) * ARGCOUNT);
Bram Moolenaar5d69da42018-04-20 22:01:41 +02001780
1781 if (items != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001782 {
Bram Moolenaar5d69da42018-04-20 22:01:41 +02001783 /* Overwrite the command, for a short list there is no
1784 * scrolling required and no wait_return(). */
1785 gotocmdline(TRUE);
1786
1787 for (i = 0; i < ARGCOUNT; ++i)
Bram Moolenaar405dadb2018-04-20 22:48:58 +02001788 items[i] = alist_name(&ARGLIST[i]);
Bram Moolenaar5d69da42018-04-20 22:01:41 +02001789 list_in_columns(items, ARGCOUNT, curwin->w_arg_idx);
1790 vim_free(items);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001791 }
1792 }
1793 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001794 else if (eap->cmdidx == CMD_arglocal)
1795 {
1796 garray_T *gap = &curwin->w_alist->al_ga;
1797
1798 /*
1799 * ":argslocal": make a local copy of the global argument list.
1800 */
1801 if (ga_grow(gap, GARGCOUNT) == OK)
1802 for (i = 0; i < GARGCOUNT; ++i)
1803 if (GARGLIST[i].ae_fname != NULL)
1804 {
1805 AARGLIST(curwin->w_alist)[gap->ga_len].ae_fname =
1806 vim_strsave(GARGLIST[i].ae_fname);
1807 AARGLIST(curwin->w_alist)[gap->ga_len].ae_fnum =
1808 GARGLIST[i].ae_fnum;
1809 ++gap->ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001810 }
1811 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001812}
1813
1814/*
1815 * ":previous", ":sprevious", ":Next" and ":sNext".
1816 */
1817 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01001818ex_previous(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001819{
1820 /* If past the last one already, go to the last one. */
1821 if (curwin->w_arg_idx - (int)eap->line2 >= ARGCOUNT)
1822 do_argfile(eap, ARGCOUNT - 1);
1823 else
1824 do_argfile(eap, curwin->w_arg_idx - (int)eap->line2);
1825}
1826
1827/*
1828 * ":rewind", ":first", ":sfirst" and ":srewind".
1829 */
1830 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01001831ex_rewind(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001832{
1833 do_argfile(eap, 0);
1834}
1835
1836/*
1837 * ":last" and ":slast".
1838 */
1839 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01001840ex_last(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001841{
1842 do_argfile(eap, ARGCOUNT - 1);
1843}
1844
1845/*
1846 * ":argument" and ":sargument".
1847 */
1848 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01001849ex_argument(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001850{
1851 int i;
1852
1853 if (eap->addr_count > 0)
1854 i = eap->line2 - 1;
1855 else
1856 i = curwin->w_arg_idx;
1857 do_argfile(eap, i);
1858}
1859
1860/*
1861 * Edit file "argn" of the argument lists.
1862 */
1863 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01001864do_argfile(exarg_T *eap, int argn)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001865{
1866 int other;
1867 char_u *p;
Bram Moolenaar34cdc3e2005-05-18 22:24:46 +00001868 int old_arg_idx = curwin->w_arg_idx;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001869
1870 if (argn < 0 || argn >= ARGCOUNT)
1871 {
1872 if (ARGCOUNT <= 1)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001873 emsg(_("E163: There is only one file to edit"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00001874 else if (argn < 0)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001875 emsg(_("E164: Cannot go before first file"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00001876 else
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001877 emsg(_("E165: Cannot go beyond last file"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00001878 }
1879 else
1880 {
1881 setpcmark();
1882#ifdef FEAT_GUI
1883 need_mouse_correct = TRUE;
1884#endif
1885
Bram Moolenaardf1bdc92006-02-23 21:32:16 +00001886 /* split window or create new tab page first */
1887 if (*eap->cmd == 's' || cmdmod.tab != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001888 {
1889 if (win_split(0, 0) == FAIL)
1890 return;
Bram Moolenaar3368ea22010-09-21 16:56:35 +02001891 RESET_BINDING(curwin);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001892 }
1893 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00001894 {
1895 /*
1896 * if 'hidden' set, only check for changed file when re-editing
1897 * the same buffer
1898 */
1899 other = TRUE;
Bram Moolenaareb44a682017-08-03 22:44:55 +02001900 if (buf_hide(curbuf))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001901 {
1902 p = fix_fname(alist_name(&ARGLIST[argn]));
1903 other = otherfile(p);
1904 vim_free(p);
1905 }
Bram Moolenaareb44a682017-08-03 22:44:55 +02001906 if ((!buf_hide(curbuf) || !other)
Bram Moolenaar45d3b142013-11-09 03:31:51 +01001907 && check_changed(curbuf, CCGD_AW
1908 | (other ? 0 : CCGD_MULTWIN)
1909 | (eap->forceit ? CCGD_FORCEIT : 0)
1910 | CCGD_EXCMD))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001911 return;
1912 }
1913
1914 curwin->w_arg_idx = argn;
Bram Moolenaar4033c552017-09-16 20:54:51 +02001915 if (argn == ARGCOUNT - 1 && curwin->w_alist == &global_alist)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001916 arg_had_last = TRUE;
1917
Bram Moolenaar34cdc3e2005-05-18 22:24:46 +00001918 /* Edit the file; always use the last known line number.
1919 * When it fails (e.g. Abort for already edited file) restore the
1920 * argument index. */
1921 if (do_ecmd(0, alist_name(&ARGLIST[curwin->w_arg_idx]), NULL,
Bram Moolenaar071d4272004-06-13 20:20:40 +00001922 eap, ECMD_LAST,
Bram Moolenaareb44a682017-08-03 22:44:55 +02001923 (buf_hide(curwin->w_buffer) ? ECMD_HIDE : 0)
Bram Moolenaar701f7af2008-11-15 13:12:07 +00001924 + (eap->forceit ? ECMD_FORCEIT : 0), curwin) == FAIL)
Bram Moolenaar34cdc3e2005-05-18 22:24:46 +00001925 curwin->w_arg_idx = old_arg_idx;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001926 /* like Vi: set the mark where the cursor is in the file. */
Bram Moolenaar34cdc3e2005-05-18 22:24:46 +00001927 else if (eap->cmdidx != CMD_argdo)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001928 setmark('\'');
1929 }
1930}
1931
1932/*
1933 * ":next", and commands that behave like it.
1934 */
1935 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01001936ex_next(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001937{
1938 int i;
1939
1940 /*
1941 * check for changed buffer now, if this fails the argument list is not
1942 * redefined.
1943 */
Bram Moolenaareb44a682017-08-03 22:44:55 +02001944 if ( buf_hide(curbuf)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001945 || eap->cmdidx == CMD_snext
Bram Moolenaar45d3b142013-11-09 03:31:51 +01001946 || !check_changed(curbuf, CCGD_AW
1947 | (eap->forceit ? CCGD_FORCEIT : 0)
1948 | CCGD_EXCMD))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001949 {
1950 if (*eap->arg != NUL) /* redefine file list */
1951 {
Bram Moolenaar32bbd002018-08-31 23:06:22 +02001952 if (do_arglist(eap->arg, AL_SET, 0, TRUE) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001953 return;
1954 i = 0;
1955 }
1956 else
1957 i = curwin->w_arg_idx + (int)eap->line2;
1958 do_argfile(eap, i);
1959 }
1960}
1961
Bram Moolenaar071d4272004-06-13 20:20:40 +00001962/*
1963 * ":argedit"
1964 */
1965 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01001966ex_argedit(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001967{
Bram Moolenaar90305c62017-07-16 15:31:17 +02001968 int i = eap->addr_count ? (int)eap->line2 : curwin->w_arg_idx + 1;
Bram Moolenaar46a53df2018-04-24 21:58:51 +02001969 // Whether curbuf will be reused, curbuf->b_ffname will be set.
1970 int curbuf_is_reusable = curbuf_reusable();
Bram Moolenaar071d4272004-06-13 20:20:40 +00001971
Bram Moolenaar32bbd002018-08-31 23:06:22 +02001972 if (do_arglist(eap->arg, AL_ADD, i, TRUE) == FAIL)
Bram Moolenaar90305c62017-07-16 15:31:17 +02001973 return;
1974#ifdef FEAT_TITLE
1975 maketitle();
1976#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001977
Bram Moolenaar46a53df2018-04-24 21:58:51 +02001978 if (curwin->w_arg_idx == 0
1979 && (curbuf->b_ml.ml_flags & ML_EMPTY)
1980 && (curbuf->b_ffname == NULL || curbuf_is_reusable))
Bram Moolenaar90305c62017-07-16 15:31:17 +02001981 i = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001982 /* Edit the argument. */
Bram Moolenaar90305c62017-07-16 15:31:17 +02001983 if (i < ARGCOUNT)
1984 do_argfile(eap, i);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001985}
1986
1987/*
1988 * ":argadd"
1989 */
1990 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01001991ex_argadd(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001992{
1993 do_arglist(eap->arg, AL_ADD,
Bram Moolenaar32bbd002018-08-31 23:06:22 +02001994 eap->addr_count > 0 ? (int)eap->line2 : curwin->w_arg_idx + 1,
1995 FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001996#ifdef FEAT_TITLE
1997 maketitle();
1998#endif
1999}
2000
2001/*
2002 * ":argdelete"
2003 */
2004 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01002005ex_argdelete(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002006{
2007 int i;
2008 int n;
2009
2010 if (eap->addr_count > 0)
2011 {
2012 /* ":1,4argdel": Delete all arguments in the range. */
2013 if (eap->line2 > ARGCOUNT)
2014 eap->line2 = ARGCOUNT;
2015 n = eap->line2 - eap->line1 + 1;
Bram Moolenaar69a92fb2017-03-09 15:58:30 +01002016 if (*eap->arg != NUL)
2017 /* Can't have both a range and an argument. */
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01002018 emsg(_(e_invarg));
Bram Moolenaar69a92fb2017-03-09 15:58:30 +01002019 else if (n <= 0)
2020 {
2021 /* Don't give an error for ":%argdel" if the list is empty. */
2022 if (eap->line1 != 1 || eap->line2 != 0)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01002023 emsg(_(e_invrange));
Bram Moolenaar69a92fb2017-03-09 15:58:30 +01002024 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002025 else
2026 {
2027 for (i = eap->line1; i <= eap->line2; ++i)
2028 vim_free(ARGLIST[i - 1].ae_fname);
2029 mch_memmove(ARGLIST + eap->line1 - 1, ARGLIST + eap->line2,
2030 (size_t)((ARGCOUNT - eap->line2) * sizeof(aentry_T)));
2031 ALIST(curwin)->al_ga.ga_len -= n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002032 if (curwin->w_arg_idx >= eap->line2)
2033 curwin->w_arg_idx -= n;
2034 else if (curwin->w_arg_idx > eap->line1)
2035 curwin->w_arg_idx = eap->line1;
Bram Moolenaar72defda2016-01-17 18:04:33 +01002036 if (ARGCOUNT == 0)
2037 curwin->w_arg_idx = 0;
2038 else if (curwin->w_arg_idx >= ARGCOUNT)
2039 curwin->w_arg_idx = ARGCOUNT - 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002040 }
2041 }
2042 else if (*eap->arg == NUL)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01002043 emsg(_(e_argreq));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002044 else
Bram Moolenaar32bbd002018-08-31 23:06:22 +02002045 do_arglist(eap->arg, AL_DEL, 0, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002046#ifdef FEAT_TITLE
2047 maketitle();
2048#endif
2049}
2050
2051/*
Bram Moolenaaraa23b372015-09-08 18:46:31 +02002052 * ":argdo", ":windo", ":bufdo", ":tabdo", ":cdo", ":ldo", ":cfdo" and ":lfdo"
Bram Moolenaar071d4272004-06-13 20:20:40 +00002053 */
2054 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01002055ex_listdo(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002056{
2057 int i;
Bram Moolenaar32466aa2006-02-24 23:53:04 +00002058 win_T *wp;
2059 tabpage_T *tp;
Bram Moolenaare25bb902015-02-27 20:33:37 +01002060 buf_T *buf = curbuf;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002061 int next_fnum = 0;
Bram Moolenaarf2bd8ef2018-03-04 18:08:14 +01002062#if defined(FEAT_SYN_HL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002063 char_u *save_ei = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002064#endif
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00002065 char_u *p_shm_save;
Bram Moolenaaraa23b372015-09-08 18:46:31 +02002066#ifdef FEAT_QUICKFIX
Bram Moolenaared84b762015-09-09 22:35:29 +02002067 int qf_size = 0;
Bram Moolenaaraa23b372015-09-08 18:46:31 +02002068 int qf_idx;
2069#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002070
Bram Moolenaar0106e3d2016-02-23 18:55:43 +01002071#ifndef FEAT_QUICKFIX
2072 if (eap->cmdidx == CMD_cdo || eap->cmdidx == CMD_ldo ||
2073 eap->cmdidx == CMD_cfdo || eap->cmdidx == CMD_lfdo)
2074 {
2075 ex_ni(eap);
2076 return;
2077 }
2078#endif
2079
Bram Moolenaarf2bd8ef2018-03-04 18:08:14 +01002080#if defined(FEAT_SYN_HL)
Bram Moolenaar910f66f2006-04-05 20:41:53 +00002081 if (eap->cmdidx != CMD_windo && eap->cmdidx != CMD_tabdo)
Bram Moolenaardcaf10e2005-01-21 11:55:25 +00002082 /* Don't do syntax HL autocommands. Skipping the syntax file is a
2083 * great speed improvement. */
2084 save_ei = au_event_disable(",Syntax");
Bram Moolenaar071d4272004-06-13 20:20:40 +00002085#endif
Bram Moolenaar6b1ee342014-08-06 18:17:11 +02002086#ifdef FEAT_CLIPBOARD
2087 start_global_changes();
2088#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002089
2090 if (eap->cmdidx == CMD_windo
Bram Moolenaar32466aa2006-02-24 23:53:04 +00002091 || eap->cmdidx == CMD_tabdo
Bram Moolenaareb44a682017-08-03 22:44:55 +02002092 || buf_hide(curbuf)
Bram Moolenaar45d3b142013-11-09 03:31:51 +01002093 || !check_changed(curbuf, CCGD_AW
2094 | (eap->forceit ? CCGD_FORCEIT : 0)
2095 | CCGD_EXCMD))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002096 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00002097 i = 0;
Bram Moolenaara162bc52015-01-07 16:54:21 +01002098 /* start at the eap->line1 argument/window/buffer */
Bram Moolenaar32466aa2006-02-24 23:53:04 +00002099 wp = firstwin;
2100 tp = first_tabpage;
Bram Moolenaara162bc52015-01-07 16:54:21 +01002101 switch (eap->cmdidx)
2102 {
Bram Moolenaara162bc52015-01-07 16:54:21 +01002103 case CMD_windo:
2104 for ( ; wp != NULL && i + 1 < eap->line1; wp = wp->w_next)
2105 i++;
2106 break;
2107 case CMD_tabdo:
2108 for( ; tp != NULL && i + 1 < eap->line1; tp = tp->tp_next)
2109 i++;
2110 break;
Bram Moolenaara162bc52015-01-07 16:54:21 +01002111 case CMD_argdo:
2112 i = eap->line1 - 1;
2113 break;
Bram Moolenaara162bc52015-01-07 16:54:21 +01002114 default:
2115 break;
2116 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002117 /* set pcmark now */
2118 if (eap->cmdidx == CMD_bufdo)
Bram Moolenaaraa23b372015-09-08 18:46:31 +02002119 {
Bram Moolenaare25bb902015-02-27 20:33:37 +01002120 /* Advance to the first listed buffer after "eap->line1". */
Bram Moolenaaraa23b372015-09-08 18:46:31 +02002121 for (buf = firstbuf; buf != NULL && (buf->b_fnum < eap->line1
Bram Moolenaare25bb902015-02-27 20:33:37 +01002122 || !buf->b_p_bl); buf = buf->b_next)
2123 if (buf->b_fnum > eap->line2)
2124 {
2125 buf = NULL;
2126 break;
2127 }
Bram Moolenaaraa23b372015-09-08 18:46:31 +02002128 if (buf != NULL)
Bram Moolenaare25bb902015-02-27 20:33:37 +01002129 goto_buffer(eap, DOBUF_FIRST, FORWARD, buf->b_fnum);
Bram Moolenaaraa23b372015-09-08 18:46:31 +02002130 }
2131#ifdef FEAT_QUICKFIX
2132 else if (eap->cmdidx == CMD_cdo || eap->cmdidx == CMD_ldo
2133 || eap->cmdidx == CMD_cfdo || eap->cmdidx == CMD_lfdo)
2134 {
2135 qf_size = qf_get_size(eap);
2136 if (qf_size <= 0 || eap->line1 > qf_size)
2137 buf = NULL;
2138 else
2139 {
2140 ex_cc(eap);
2141
2142 buf = curbuf;
2143 i = eap->line1 - 1;
2144 if (eap->addr_count <= 0)
2145 /* default is all the quickfix/location list entries */
2146 eap->line2 = qf_size;
2147 }
2148 }
2149#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002150 else
2151 setpcmark();
2152 listcmd_busy = TRUE; /* avoids setting pcmark below */
2153
Bram Moolenaare25bb902015-02-27 20:33:37 +01002154 while (!got_int && buf != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002155 {
2156 if (eap->cmdidx == CMD_argdo)
2157 {
2158 /* go to argument "i" */
2159 if (i == ARGCOUNT)
2160 break;
2161 /* Don't call do_argfile() when already there, it will try
2162 * reloading the file. */
Bram Moolenaard4755bb2004-09-02 19:12:26 +00002163 if (curwin->w_arg_idx != i || !editing_arg_idx(curwin))
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00002164 {
2165 /* Clear 'shm' to avoid that the file message overwrites
2166 * any output from the command. */
2167 p_shm_save = vim_strsave(p_shm);
2168 set_option_value((char_u *)"shm", 0L, (char_u *)"", 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002169 do_argfile(eap, i);
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00002170 set_option_value((char_u *)"shm", 0L, p_shm_save, 0);
2171 vim_free(p_shm_save);
2172 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002173 if (curwin->w_arg_idx != i)
2174 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002175 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002176 else if (eap->cmdidx == CMD_windo)
2177 {
Bram Moolenaar32466aa2006-02-24 23:53:04 +00002178 /* go to window "wp" */
2179 if (!win_valid(wp))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002180 break;
Bram Moolenaar32466aa2006-02-24 23:53:04 +00002181 win_goto(wp);
Bram Moolenaar41423242007-05-03 20:11:13 +00002182 if (curwin != wp)
2183 break; /* something must be wrong */
Bram Moolenaar32466aa2006-02-24 23:53:04 +00002184 wp = curwin->w_next;
2185 }
2186 else if (eap->cmdidx == CMD_tabdo)
2187 {
2188 /* go to window "tp" */
2189 if (!valid_tabpage(tp))
2190 break;
Bram Moolenaar49e649f2013-05-06 04:50:35 +02002191 goto_tabpage_tp(tp, TRUE, TRUE);
Bram Moolenaar32466aa2006-02-24 23:53:04 +00002192 tp = tp->tp_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002193 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002194 else if (eap->cmdidx == CMD_bufdo)
2195 {
2196 /* Remember the number of the next listed buffer, in case
2197 * ":bwipe" is used or autocommands do something strange. */
2198 next_fnum = -1;
2199 for (buf = curbuf->b_next; buf != NULL; buf = buf->b_next)
2200 if (buf->b_p_bl)
2201 {
2202 next_fnum = buf->b_fnum;
2203 break;
2204 }
2205 }
2206
Bram Moolenaara162bc52015-01-07 16:54:21 +01002207 ++i;
2208
Bram Moolenaar071d4272004-06-13 20:20:40 +00002209 /* execute the command */
2210 do_cmdline(eap->arg, eap->getline, eap->cookie,
2211 DOCMD_VERBOSE + DOCMD_NOWAIT);
2212
2213 if (eap->cmdidx == CMD_bufdo)
2214 {
2215 /* Done? */
Bram Moolenaara162bc52015-01-07 16:54:21 +01002216 if (next_fnum < 0 || next_fnum > eap->line2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002217 break;
2218 /* Check if the buffer still exists. */
Bram Moolenaar29323592016-07-24 22:04:11 +02002219 FOR_ALL_BUFFERS(buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002220 if (buf->b_fnum == next_fnum)
2221 break;
2222 if (buf == NULL)
2223 break;
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00002224
2225 /* Go to the next buffer. Clear 'shm' to avoid that the file
2226 * message overwrites any output from the command. */
2227 p_shm_save = vim_strsave(p_shm);
2228 set_option_value((char_u *)"shm", 0L, (char_u *)"", 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002229 goto_buffer(eap, DOBUF_FIRST, FORWARD, next_fnum);
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00002230 set_option_value((char_u *)"shm", 0L, p_shm_save, 0);
2231 vim_free(p_shm_save);
2232
Bram Moolenaaraa23b372015-09-08 18:46:31 +02002233 /* If autocommands took us elsewhere, quit here. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002234 if (curbuf->b_fnum != next_fnum)
2235 break;
2236 }
2237
Bram Moolenaaraa23b372015-09-08 18:46:31 +02002238#ifdef FEAT_QUICKFIX
2239 if (eap->cmdidx == CMD_cdo || eap->cmdidx == CMD_ldo
2240 || eap->cmdidx == CMD_cfdo || eap->cmdidx == CMD_lfdo)
2241 {
2242 if (i >= qf_size || i >= eap->line2)
2243 break;
2244
2245 qf_idx = qf_get_cur_idx(eap);
2246
2247 ex_cnext(eap);
2248
2249 /* If jumping to the next quickfix entry fails, quit here */
2250 if (qf_get_cur_idx(eap) == qf_idx)
2251 break;
2252 }
2253#endif
2254
Bram Moolenaar071d4272004-06-13 20:20:40 +00002255 if (eap->cmdidx == CMD_windo)
2256 {
2257 validate_cursor(); /* cursor may have moved */
Bram Moolenaar8a3bb562018-03-04 20:14:14 +01002258
Bram Moolenaar071d4272004-06-13 20:20:40 +00002259 /* required when 'scrollbind' has been set */
2260 if (curwin->w_p_scb)
2261 do_check_scrollbind(TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002262 }
Bram Moolenaara162bc52015-01-07 16:54:21 +01002263
Bram Moolenaara162bc52015-01-07 16:54:21 +01002264 if (eap->cmdidx == CMD_windo || eap->cmdidx == CMD_tabdo)
2265 if (i+1 > eap->line2)
2266 break;
Bram Moolenaara162bc52015-01-07 16:54:21 +01002267 if (eap->cmdidx == CMD_argdo && i >= eap->line2)
2268 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002269 }
2270 listcmd_busy = FALSE;
2271 }
2272
Bram Moolenaarf2bd8ef2018-03-04 18:08:14 +01002273#if defined(FEAT_SYN_HL)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00002274 if (save_ei != NULL)
2275 {
2276 au_event_restore(save_ei);
2277 apply_autocmds(EVENT_SYNTAX, curbuf->b_p_syn,
2278 curbuf->b_fname, TRUE, curbuf);
2279 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002280#endif
Bram Moolenaar6b1ee342014-08-06 18:17:11 +02002281#ifdef FEAT_CLIPBOARD
2282 end_global_changes();
2283#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002284}
2285
2286/*
2287 * Add files[count] to the arglist of the current window after arg "after".
2288 * The file names in files[count] must have been allocated and are taken over.
2289 * Files[] itself is not taken over.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002290 */
Bram Moolenaar32bbd002018-08-31 23:06:22 +02002291 static void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01002292alist_add_list(
2293 int count,
2294 char_u **files,
Bram Moolenaar32bbd002018-08-31 23:06:22 +02002295 int after, // where to add: 0 = before first one
2296 int will_edit) // will edit adding argument
Bram Moolenaar071d4272004-06-13 20:20:40 +00002297{
2298 int i;
Bram Moolenaara24f0a52016-01-17 19:39:00 +01002299 int old_argcount = ARGCOUNT;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002300
2301 if (ga_grow(&ALIST(curwin)->al_ga, count) == OK)
2302 {
2303 if (after < 0)
2304 after = 0;
2305 if (after > ARGCOUNT)
2306 after = ARGCOUNT;
2307 if (after < ARGCOUNT)
2308 mch_memmove(&(ARGLIST[after + count]), &(ARGLIST[after]),
2309 (ARGCOUNT - after) * sizeof(aentry_T));
2310 for (i = 0; i < count; ++i)
2311 {
Bram Moolenaar32bbd002018-08-31 23:06:22 +02002312 int flags = BLN_LISTED | (will_edit ? BLN_CURBUF : 0);
2313
Bram Moolenaar071d4272004-06-13 20:20:40 +00002314 ARGLIST[after + i].ae_fname = files[i];
Bram Moolenaar32bbd002018-08-31 23:06:22 +02002315 ARGLIST[after + i].ae_fnum = buflist_add(files[i], flags);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002316 }
2317 ALIST(curwin)->al_ga.ga_len += count;
Bram Moolenaara24f0a52016-01-17 19:39:00 +01002318 if (old_argcount > 0 && curwin->w_arg_idx >= after)
2319 curwin->w_arg_idx += count;
Bram Moolenaar32bbd002018-08-31 23:06:22 +02002320 return;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002321 }
2322
2323 for (i = 0; i < count; ++i)
2324 vim_free(files[i]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002325}
2326
Bram Moolenaarcd43eff2018-03-29 15:55:38 +02002327#if defined(FEAT_CMDL_COMPL) || defined(PROTO)
2328/*
2329 * Function given to ExpandGeneric() to obtain the possible arguments of the
2330 * argedit and argdelete commands.
2331 */
2332 char_u *
2333get_arglist_name(expand_T *xp UNUSED, int idx)
2334{
2335 if (idx >= ARGCOUNT)
2336 return NULL;
2337
2338 return alist_name(&ARGLIST[idx]);
2339}
2340#endif
2341
Bram Moolenaar0c72fe42018-03-29 16:04:08 +02002342
Bram Moolenaar071d4272004-06-13 20:20:40 +00002343#ifdef FEAT_EVAL
2344/*
2345 * ":compiler[!] {name}"
2346 */
2347 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01002348ex_compiler(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002349{
2350 char_u *buf;
2351 char_u *old_cur_comp = NULL;
2352 char_u *p;
2353
2354 if (*eap->arg == NUL)
2355 {
2356 /* List all compiler scripts. */
2357 do_cmdline_cmd((char_u *)"echo globpath(&rtp, 'compiler/*.vim')");
2358 /* ) keep the indenter happy... */
2359 }
2360 else
2361 {
2362 buf = alloc((unsigned)(STRLEN(eap->arg) + 14));
2363 if (buf != NULL)
2364 {
2365 if (eap->forceit)
2366 {
2367 /* ":compiler! {name}" sets global options */
2368 do_cmdline_cmd((char_u *)
2369 "command -nargs=* CompilerSet set <args>");
2370 }
2371 else
2372 {
2373 /* ":compiler! {name}" sets local options.
2374 * To remain backwards compatible "current_compiler" is always
2375 * used. A user's compiler plugin may set it, the distributed
2376 * plugin will then skip the settings. Afterwards set
Bram Moolenaar3d63e3f2010-01-19 16:13:50 +01002377 * "b:current_compiler" and restore "current_compiler".
2378 * Explicitly prepend "g:" to make it work in a function. */
2379 old_cur_comp = get_var_value((char_u *)"g:current_compiler");
Bram Moolenaar071d4272004-06-13 20:20:40 +00002380 if (old_cur_comp != NULL)
2381 old_cur_comp = vim_strsave(old_cur_comp);
2382 do_cmdline_cmd((char_u *)
2383 "command -nargs=* CompilerSet setlocal <args>");
2384 }
Bram Moolenaar3d63e3f2010-01-19 16:13:50 +01002385 do_unlet((char_u *)"g:current_compiler", TRUE);
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00002386 do_unlet((char_u *)"b:current_compiler", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002387
2388 sprintf((char *)buf, "compiler/%s.vim", eap->arg);
Bram Moolenaar7f8989d2016-03-12 22:11:39 +01002389 if (source_runtime(buf, DIP_ALL) == FAIL)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01002390 semsg(_("E666: compiler not supported: %s"), eap->arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002391 vim_free(buf);
2392
2393 do_cmdline_cmd((char_u *)":delcommand CompilerSet");
2394
2395 /* Set "b:current_compiler" from "current_compiler". */
Bram Moolenaar3d63e3f2010-01-19 16:13:50 +01002396 p = get_var_value((char_u *)"g:current_compiler");
Bram Moolenaar071d4272004-06-13 20:20:40 +00002397 if (p != NULL)
2398 set_internal_string_var((char_u *)"b:current_compiler", p);
2399
2400 /* Restore "current_compiler" for ":compiler {name}". */
2401 if (!eap->forceit)
2402 {
2403 if (old_cur_comp != NULL)
2404 {
Bram Moolenaar3d63e3f2010-01-19 16:13:50 +01002405 set_internal_string_var((char_u *)"g:current_compiler",
Bram Moolenaar071d4272004-06-13 20:20:40 +00002406 old_cur_comp);
2407 vim_free(old_cur_comp);
2408 }
2409 else
Bram Moolenaar3d63e3f2010-01-19 16:13:50 +01002410 do_unlet((char_u *)"g:current_compiler", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002411 }
2412 }
2413 }
2414}
2415#endif
2416
2417/*
Bram Moolenaar8dcf2592016-03-12 22:47:14 +01002418 * ":runtime [what] {name}"
Bram Moolenaar071d4272004-06-13 20:20:40 +00002419 */
2420 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01002421ex_runtime(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002422{
Bram Moolenaar8dcf2592016-03-12 22:47:14 +01002423 char_u *arg = eap->arg;
2424 char_u *p = skiptowhite(arg);
2425 int len = (int)(p - arg);
2426 int flags = eap->forceit ? DIP_ALL : 0;
2427
2428 if (STRNCMP(arg, "START", len) == 0)
2429 {
2430 flags += DIP_START + DIP_NORTP;
2431 arg = skipwhite(arg + len);
2432 }
2433 else if (STRNCMP(arg, "OPT", len) == 0)
2434 {
2435 flags += DIP_OPT + DIP_NORTP;
2436 arg = skipwhite(arg + len);
2437 }
2438 else if (STRNCMP(arg, "PACK", len) == 0)
2439 {
2440 flags += DIP_START + DIP_OPT + DIP_NORTP;
2441 arg = skipwhite(arg + len);
2442 }
2443 else if (STRNCMP(arg, "ALL", len) == 0)
2444 {
2445 flags += DIP_START + DIP_OPT;
2446 arg = skipwhite(arg + len);
2447 }
2448
2449 source_runtime(arg, flags);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002450}
2451
Bram Moolenaar071d4272004-06-13 20:20:40 +00002452 static void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01002453source_callback(char_u *fname, void *cookie UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002454{
Bram Moolenaar910f66f2006-04-05 20:41:53 +00002455 (void)do_source(fname, FALSE, DOSO_NONE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002456}
2457
2458/*
Bram Moolenaarbe82c252016-03-06 14:44:08 +01002459 * Find the file "name" in all directories in "path" and invoke
2460 * "callback(fname, cookie)".
2461 * "name" can contain wildcards.
2462 * When "flags" has DIP_ALL: source all files, otherwise only the first one.
2463 * When "flags" has DIP_DIR: find directories instead of files.
2464 * When "flags" has DIP_ERR: give an error message if there is no match.
2465 *
2466 * return FAIL when no file could be sourced, OK otherwise.
2467 */
Bram Moolenaar6bef5302016-03-12 21:28:26 +01002468 int
Bram Moolenaarf6fee0e2016-02-21 23:02:49 +01002469do_in_path(
2470 char_u *path,
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01002471 char_u *name,
Bram Moolenaar91715872016-03-03 17:13:03 +01002472 int flags,
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01002473 void (*callback)(char_u *fname, void *ck),
2474 void *cookie)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002475{
2476 char_u *rtp;
2477 char_u *np;
2478 char_u *buf;
2479 char_u *rtp_copy;
2480 char_u *tail;
2481 int num_files;
2482 char_u **files;
2483 int i;
2484 int did_one = FALSE;
2485#ifdef AMIGA
2486 struct Process *proc = (struct Process *)FindTask(0L);
2487 APTR save_winptr = proc->pr_WindowPtr;
2488
2489 /* Avoid a requester here for a volume that doesn't exist. */
2490 proc->pr_WindowPtr = (APTR)-1L;
2491#endif
2492
2493 /* Make a copy of 'runtimepath'. Invoking the callback may change the
2494 * value. */
Bram Moolenaarf6fee0e2016-02-21 23:02:49 +01002495 rtp_copy = vim_strsave(path);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002496 buf = alloc(MAXPATHL);
2497 if (buf != NULL && rtp_copy != NULL)
2498 {
Bram Moolenaarc09a6d62013-06-10 21:27:29 +02002499 if (p_verbose > 1 && name != NULL)
Bram Moolenaar54ee7752005-05-31 22:22:17 +00002500 {
2501 verbose_enter();
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01002502 smsg(_("Searching for \"%s\" in \"%s\""),
Bram Moolenaarf6fee0e2016-02-21 23:02:49 +01002503 (char *)name, (char *)path);
Bram Moolenaar54ee7752005-05-31 22:22:17 +00002504 verbose_leave();
2505 }
Bram Moolenaar34cdc3e2005-05-18 22:24:46 +00002506
Bram Moolenaar071d4272004-06-13 20:20:40 +00002507 /* Loop over all entries in 'runtimepath'. */
2508 rtp = rtp_copy;
Bram Moolenaar91715872016-03-03 17:13:03 +01002509 while (*rtp != NUL && ((flags & DIP_ALL) || !did_one))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002510 {
Bram Moolenaar66459b72016-08-06 19:01:55 +02002511 size_t buflen;
2512
Bram Moolenaar071d4272004-06-13 20:20:40 +00002513 /* Copy the path from 'runtimepath' to buf[]. */
2514 copy_option_part(&rtp, buf, MAXPATHL, ",");
Bram Moolenaar66459b72016-08-06 19:01:55 +02002515 buflen = STRLEN(buf);
2516
2517 /* Skip after or non-after directories. */
2518 if (flags & (DIP_NOAFTER | DIP_AFTER))
2519 {
2520 int is_after = buflen >= 5
2521 && STRCMP(buf + buflen - 5, "after") == 0;
2522
2523 if ((is_after && (flags & DIP_NOAFTER))
2524 || (!is_after && (flags & DIP_AFTER)))
2525 continue;
2526 }
2527
Bram Moolenaarc09a6d62013-06-10 21:27:29 +02002528 if (name == NULL)
2529 {
2530 (*callback)(buf, (void *) &cookie);
2531 if (!did_one)
2532 did_one = (cookie == NULL);
2533 }
Bram Moolenaar66459b72016-08-06 19:01:55 +02002534 else if (buflen + STRLEN(name) + 2 < MAXPATHL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002535 {
2536 add_pathsep(buf);
2537 tail = buf + STRLEN(buf);
2538
2539 /* Loop over all patterns in "name" */
2540 np = name;
Bram Moolenaar91715872016-03-03 17:13:03 +01002541 while (*np != NUL && ((flags & DIP_ALL) || !did_one))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002542 {
2543 /* Append the pattern from "name" to buf[]. */
2544 copy_option_part(&np, tail, (int)(MAXPATHL - (tail - buf)),
2545 "\t ");
2546
2547 if (p_verbose > 2)
Bram Moolenaar54ee7752005-05-31 22:22:17 +00002548 {
2549 verbose_enter();
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01002550 smsg(_("Searching for \"%s\""), buf);
Bram Moolenaar54ee7752005-05-31 22:22:17 +00002551 verbose_leave();
2552 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002553
2554 /* Expand wildcards, invoke the callback for each match. */
2555 if (gen_expand_wildcards(1, &buf, &num_files, &files,
Bram Moolenaar91715872016-03-03 17:13:03 +01002556 (flags & DIP_DIR) ? EW_DIR : EW_FILE) == OK)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002557 {
2558 for (i = 0; i < num_files; ++i)
2559 {
Bram Moolenaar13fcaaf2005-04-15 21:13:42 +00002560 (*callback)(files[i], cookie);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002561 did_one = TRUE;
Bram Moolenaar91715872016-03-03 17:13:03 +01002562 if (!(flags & DIP_ALL))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002563 break;
2564 }
2565 FreeWild(num_files, files);
2566 }
2567 }
2568 }
2569 }
2570 }
2571 vim_free(buf);
2572 vim_free(rtp_copy);
Bram Moolenaarbe82c252016-03-06 14:44:08 +01002573 if (!did_one && name != NULL)
Bram Moolenaar54ee7752005-05-31 22:22:17 +00002574 {
Bram Moolenaarbe82c252016-03-06 14:44:08 +01002575 char *basepath = path == p_rtp ? "runtimepath" : "packpath";
2576
2577 if (flags & DIP_ERR)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01002578 semsg(_(e_dirnotf), basepath, name);
Bram Moolenaarbe82c252016-03-06 14:44:08 +01002579 else if (p_verbose > 0)
2580 {
2581 verbose_enter();
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01002582 smsg(_("not found in '%s': \"%s\""), basepath, name);
Bram Moolenaarbe82c252016-03-06 14:44:08 +01002583 verbose_leave();
2584 }
Bram Moolenaar54ee7752005-05-31 22:22:17 +00002585 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002586
2587#ifdef AMIGA
2588 proc->pr_WindowPtr = save_winptr;
2589#endif
2590
2591 return did_one ? OK : FAIL;
2592}
2593
Bram Moolenaarf6fee0e2016-02-21 23:02:49 +01002594/*
Bram Moolenaar07ecfa62017-06-27 14:43:55 +02002595 * Find "name" in "path". When found, invoke the callback function for
Bram Moolenaarf6fee0e2016-02-21 23:02:49 +01002596 * it: callback(fname, "cookie")
Bram Moolenaar7f8989d2016-03-12 22:11:39 +01002597 * When "flags" has DIP_ALL repeat for all matches, otherwise only the first
2598 * one is used.
Bram Moolenaarf6fee0e2016-02-21 23:02:49 +01002599 * Returns OK when at least one match found, FAIL otherwise.
2600 *
Bram Moolenaar07ecfa62017-06-27 14:43:55 +02002601 * If "name" is NULL calls callback for each entry in "path". Cookie is
Bram Moolenaarf6fee0e2016-02-21 23:02:49 +01002602 * passed by reference in this case, setting it to NULL indicates that callback
2603 * has done its job.
2604 */
Bram Moolenaar07ecfa62017-06-27 14:43:55 +02002605 static int
2606do_in_path_and_pp(
2607 char_u *path,
Bram Moolenaarf6fee0e2016-02-21 23:02:49 +01002608 char_u *name,
Bram Moolenaar7f8989d2016-03-12 22:11:39 +01002609 int flags,
Bram Moolenaarf6fee0e2016-02-21 23:02:49 +01002610 void (*callback)(char_u *fname, void *ck),
2611 void *cookie)
2612{
Bram Moolenaar8dcf2592016-03-12 22:47:14 +01002613 int done = FAIL;
Bram Moolenaar7f8989d2016-03-12 22:11:39 +01002614 char_u *s;
2615 int len;
2616 char *start_dir = "pack/*/start/*/%s";
2617 char *opt_dir = "pack/*/opt/*/%s";
2618
Bram Moolenaar8dcf2592016-03-12 22:47:14 +01002619 if ((flags & DIP_NORTP) == 0)
Bram Moolenaar07ecfa62017-06-27 14:43:55 +02002620 done = do_in_path(path, name, flags, callback, cookie);
Bram Moolenaar7f8989d2016-03-12 22:11:39 +01002621
Bram Moolenaar8dcf2592016-03-12 22:47:14 +01002622 if ((done == FAIL || (flags & DIP_ALL)) && (flags & DIP_START))
Bram Moolenaar7f8989d2016-03-12 22:11:39 +01002623 {
Bram Moolenaar1c8b4ed2016-03-17 21:51:03 +01002624 len = (int)(STRLEN(start_dir) + STRLEN(name));
Bram Moolenaar7f8989d2016-03-12 22:11:39 +01002625 s = alloc(len);
2626 if (s == NULL)
2627 return FAIL;
2628 vim_snprintf((char *)s, len, start_dir, name);
2629 done = do_in_path(p_pp, s, flags, callback, cookie);
2630 vim_free(s);
2631 }
2632
Bram Moolenaar8dcf2592016-03-12 22:47:14 +01002633 if ((done == FAIL || (flags & DIP_ALL)) && (flags & DIP_OPT))
Bram Moolenaar7f8989d2016-03-12 22:11:39 +01002634 {
Bram Moolenaar1c8b4ed2016-03-17 21:51:03 +01002635 len = (int)(STRLEN(opt_dir) + STRLEN(name));
Bram Moolenaar7f8989d2016-03-12 22:11:39 +01002636 s = alloc(len);
2637 if (s == NULL)
2638 return FAIL;
2639 vim_snprintf((char *)s, len, opt_dir, name);
2640 done = do_in_path(p_pp, s, flags, callback, cookie);
2641 vim_free(s);
2642 }
2643
2644 return done;
Bram Moolenaarf6fee0e2016-02-21 23:02:49 +01002645}
2646
Bram Moolenaar1bdd4262016-03-03 14:23:10 +01002647/*
Bram Moolenaar07ecfa62017-06-27 14:43:55 +02002648 * Just like do_in_path_and_pp(), using 'runtimepath' for "path".
2649 */
2650 int
2651do_in_runtimepath(
2652 char_u *name,
2653 int flags,
2654 void (*callback)(char_u *fname, void *ck),
2655 void *cookie)
2656{
2657 return do_in_path_and_pp(p_rtp, name, flags, callback, cookie);
2658}
2659
2660/*
2661 * Source the file "name" from all directories in 'runtimepath'.
2662 * "name" can contain wildcards.
2663 * When "flags" has DIP_ALL: source all files, otherwise only the first one.
2664 *
2665 * return FAIL when no file could be sourced, OK otherwise.
2666 */
2667 int
2668source_runtime(char_u *name, int flags)
2669{
2670 return source_in_path(p_rtp, name, flags);
2671}
2672
2673/*
2674 * Just like source_runtime(), but use "path" instead of 'runtimepath'.
2675 */
2676 int
2677source_in_path(char_u *path, char_u *name, int flags)
2678{
2679 return do_in_path_and_pp(path, name, flags, source_callback, NULL);
2680}
2681
2682
Bram Moolenaarf2bd8ef2018-03-04 18:08:14 +01002683#if defined(FEAT_EVAL) || defined(PROTO)
2684
Bram Moolenaar07ecfa62017-06-27 14:43:55 +02002685/*
Bram Moolenaarf3654822016-03-04 22:12:23 +01002686 * Expand wildcards in "pat" and invoke do_source() for each match.
Bram Moolenaar1bdd4262016-03-03 14:23:10 +01002687 */
2688 static void
Bram Moolenaarf3654822016-03-04 22:12:23 +01002689source_all_matches(char_u *pat)
Bram Moolenaar1bdd4262016-03-03 14:23:10 +01002690{
Bram Moolenaarf3654822016-03-04 22:12:23 +01002691 int num_files;
2692 char_u **files;
2693 int i;
Bram Moolenaar1bdd4262016-03-03 14:23:10 +01002694
Bram Moolenaarf3654822016-03-04 22:12:23 +01002695 if (gen_expand_wildcards(1, &pat, &num_files, &files, EW_FILE) == OK)
Bram Moolenaar1bdd4262016-03-03 14:23:10 +01002696 {
Bram Moolenaarf3654822016-03-04 22:12:23 +01002697 for (i = 0; i < num_files; ++i)
2698 (void)do_source(files[i], FALSE, DOSO_NONE);
2699 FreeWild(num_files, files);
Bram Moolenaar1bdd4262016-03-03 14:23:10 +01002700 }
Bram Moolenaar1bdd4262016-03-03 14:23:10 +01002701}
2702
Bram Moolenaar2374faa2018-02-04 17:47:42 +01002703/*
2704 * Add the package directory to 'runtimepath'.
2705 */
2706 static int
2707add_pack_dir_to_rtp(char_u *fname)
Bram Moolenaarf6fee0e2016-02-21 23:02:49 +01002708{
Bram Moolenaarf3654822016-03-04 22:12:23 +01002709 char_u *p4, *p3, *p2, *p1, *p;
Bram Moolenaar99396d42018-09-08 18:21:16 +02002710 char_u *entry;
2711 char_u *insp = NULL;
Bram Moolenaar91715872016-03-03 17:13:03 +01002712 int c;
2713 char_u *new_rtp;
2714 int keep;
Bram Moolenaarb0550662016-05-31 21:37:36 +02002715 size_t oldlen;
2716 size_t addlen;
Bram Moolenaar99396d42018-09-08 18:21:16 +02002717 size_t new_rtp_len;
Bram Moolenaar2374faa2018-02-04 17:47:42 +01002718 char_u *afterdir = NULL;
Bram Moolenaarb0550662016-05-31 21:37:36 +02002719 size_t afterlen = 0;
Bram Moolenaar99396d42018-09-08 18:21:16 +02002720 char_u *after_insp = NULL;
Bram Moolenaar2374faa2018-02-04 17:47:42 +01002721 char_u *ffname = NULL;
Bram Moolenaarfef524b2016-07-02 22:07:22 +02002722 size_t fname_len;
Bram Moolenaar2f9e5752017-02-05 16:07:54 +01002723 char_u *buf = NULL;
2724 char_u *rtp_ffname;
2725 int match;
Bram Moolenaar2374faa2018-02-04 17:47:42 +01002726 int retval = FAIL;
Bram Moolenaarf6fee0e2016-02-21 23:02:49 +01002727
Bram Moolenaar2374faa2018-02-04 17:47:42 +01002728 p4 = p3 = p2 = p1 = get_past_head(fname);
2729 for (p = p1; *p; MB_PTR_ADV(p))
2730 if (vim_ispathsep_nocolon(*p))
2731 {
2732 p4 = p3; p3 = p2; p2 = p1; p1 = p;
2733 }
2734
2735 /* now we have:
2736 * rtp/pack/name/start/name
2737 * p4 p3 p2 p1
2738 *
2739 * find the part up to "pack" in 'runtimepath' */
2740 c = *++p4; /* append pathsep in order to expand symlink */
2741 *p4 = NUL;
2742 ffname = fix_fname(fname);
2743 *p4 = c;
Bram Moolenaar91715872016-03-03 17:13:03 +01002744 if (ffname == NULL)
Bram Moolenaar2374faa2018-02-04 17:47:42 +01002745 return FAIL;
2746
Bram Moolenaar99396d42018-09-08 18:21:16 +02002747 // Find "ffname" in "p_rtp", ignoring '/' vs '\' differences.
2748 // Also stop at the first "after" directory.
Bram Moolenaar2374faa2018-02-04 17:47:42 +01002749 fname_len = STRLEN(ffname);
Bram Moolenaar2374faa2018-02-04 17:47:42 +01002750 buf = alloc(MAXPATHL);
2751 if (buf == NULL)
2752 goto theend;
Bram Moolenaar99396d42018-09-08 18:21:16 +02002753 for (entry = p_rtp; *entry != NUL; )
Bram Moolenaarf6fee0e2016-02-21 23:02:49 +01002754 {
Bram Moolenaar99396d42018-09-08 18:21:16 +02002755 char_u *cur_entry = entry;
2756
2757 copy_option_part(&entry, buf, MAXPATHL, ",");
2758 if (insp == NULL)
2759 {
2760 add_pathsep(buf);
2761 rtp_ffname = fix_fname(buf);
2762 if (rtp_ffname == NULL)
2763 goto theend;
2764 match = vim_fnamencmp(rtp_ffname, ffname, fname_len) == 0;
2765 vim_free(rtp_ffname);
2766 if (match)
2767 // Insert "ffname" after this entry (and comma).
2768 insp = entry;
2769 }
2770
2771 if ((p = (char_u *)strstr((char *)buf, "after")) != NULL
2772 && p > buf
2773 && vim_ispathsep(p[-1])
2774 && (vim_ispathsep(p[5]) || p[5] == NUL || p[5] == ','))
2775 {
2776 if (insp == NULL)
2777 // Did not find "ffname" before the first "after" directory,
2778 // insert it before this entry.
2779 insp = cur_entry;
2780 after_insp = cur_entry;
Bram Moolenaar2374faa2018-02-04 17:47:42 +01002781 break;
Bram Moolenaar99396d42018-09-08 18:21:16 +02002782 }
Bram Moolenaarf6fee0e2016-02-21 23:02:49 +01002783 }
Bram Moolenaarf6fee0e2016-02-21 23:02:49 +01002784
Bram Moolenaar99396d42018-09-08 18:21:16 +02002785 if (insp == NULL)
2786 // Both "fname" and "after" not found, append at the end.
Bram Moolenaar2374faa2018-02-04 17:47:42 +01002787 insp = p_rtp + STRLEN(p_rtp);
Bram Moolenaar2374faa2018-02-04 17:47:42 +01002788
Bram Moolenaar99396d42018-09-08 18:21:16 +02002789 // check if rtp/pack/name/start/name/after exists
Bram Moolenaar2374faa2018-02-04 17:47:42 +01002790 afterdir = concat_fnames(fname, (char_u *)"after", TRUE);
2791 if (afterdir != NULL && mch_isdir(afterdir))
Bram Moolenaar99396d42018-09-08 18:21:16 +02002792 afterlen = STRLEN(afterdir) + 1; // add one for comma
Bram Moolenaar2374faa2018-02-04 17:47:42 +01002793
2794 oldlen = STRLEN(p_rtp);
Bram Moolenaar99396d42018-09-08 18:21:16 +02002795 addlen = STRLEN(fname) + 1; // add one for comma
2796 new_rtp = alloc((int)(oldlen + addlen + afterlen + 1)); // add one for NUL
Bram Moolenaar2374faa2018-02-04 17:47:42 +01002797 if (new_rtp == NULL)
2798 goto theend;
Bram Moolenaar99396d42018-09-08 18:21:16 +02002799
2800 // We now have 'rtp' parts: {keep}{keep_after}{rest}.
2801 // Create new_rtp, first: {keep},{fname}
Bram Moolenaar2374faa2018-02-04 17:47:42 +01002802 keep = (int)(insp - p_rtp);
2803 mch_memmove(new_rtp, p_rtp, keep);
Bram Moolenaar99396d42018-09-08 18:21:16 +02002804 new_rtp_len = keep;
2805 if (*insp == NUL)
2806 new_rtp[new_rtp_len++] = ','; // add comma before
2807 mch_memmove(new_rtp + new_rtp_len, fname, addlen - 1);
2808 new_rtp_len += addlen - 1;
2809 if (*insp != NUL)
2810 new_rtp[new_rtp_len++] = ','; // add comma after
2811
2812 if (afterlen > 0 && after_insp != NULL)
Bram Moolenaarf3654822016-03-04 22:12:23 +01002813 {
Bram Moolenaar99396d42018-09-08 18:21:16 +02002814 int keep_after = (int)(after_insp - p_rtp);
2815
2816 // Add to new_rtp: {keep},{fname}{keep_after},{afterdir}
2817 mch_memmove(new_rtp + new_rtp_len, p_rtp + keep,
2818 keep_after - keep);
2819 new_rtp_len += keep_after - keep;
2820 mch_memmove(new_rtp + new_rtp_len, afterdir, afterlen - 1);
2821 new_rtp_len += afterlen - 1;
2822 new_rtp[new_rtp_len++] = ',';
2823 keep = keep_after;
2824 }
2825
2826 if (p_rtp[keep] != NUL)
2827 // Append rest: {keep},{fname}{keep_after},{afterdir}{rest}
2828 mch_memmove(new_rtp + new_rtp_len, p_rtp + keep, oldlen - keep + 1);
2829 else
2830 new_rtp[new_rtp_len] = NUL;
2831
2832 if (afterlen > 0 && after_insp == NULL)
2833 {
2834 // Append afterdir when "after" was not found:
2835 // {keep},{fname}{rest},{afterdir}
Bram Moolenaar2374faa2018-02-04 17:47:42 +01002836 STRCAT(new_rtp, ",");
2837 STRCAT(new_rtp, afterdir);
Bram Moolenaarf3654822016-03-04 22:12:23 +01002838 }
Bram Moolenaar99396d42018-09-08 18:21:16 +02002839
Bram Moolenaar2374faa2018-02-04 17:47:42 +01002840 set_option_value((char_u *)"rtp", 0L, new_rtp, 0);
2841 vim_free(new_rtp);
2842 retval = OK;
Bram Moolenaarf3654822016-03-04 22:12:23 +01002843
2844theend:
Bram Moolenaar2f9e5752017-02-05 16:07:54 +01002845 vim_free(buf);
Bram Moolenaarf3654822016-03-04 22:12:23 +01002846 vim_free(ffname);
Bram Moolenaar2374faa2018-02-04 17:47:42 +01002847 vim_free(afterdir);
2848 return retval;
2849}
2850
2851/*
2852 * Load scripts in "plugin" and "ftdetect" directories of the package.
2853 */
2854 static int
2855load_pack_plugin(char_u *fname)
2856{
2857 static char *plugpat = "%s/plugin/**/*.vim";
2858 static char *ftpat = "%s/ftdetect/*.vim";
2859 int len;
2860 char_u *ffname = fix_fname(fname);
2861 char_u *pat = NULL;
2862 int retval = FAIL;
2863
2864 if (ffname == NULL)
2865 return FAIL;
2866 len = (int)STRLEN(ffname) + (int)STRLEN(ftpat);
2867 pat = alloc(len);
2868 if (pat == NULL)
2869 goto theend;
2870 vim_snprintf((char *)pat, len, plugpat, ffname);
2871 source_all_matches(pat);
2872
Bram Moolenaar2374faa2018-02-04 17:47:42 +01002873 {
2874 char_u *cmd = vim_strsave((char_u *)"g:did_load_filetypes");
2875
2876 /* If runtime/filetype.vim wasn't loaded yet, the scripts will be
2877 * found when it loads. */
2878 if (cmd != NULL && eval_to_number(cmd) > 0)
2879 {
2880 do_cmdline_cmd((char_u *)"augroup filetypedetect");
2881 vim_snprintf((char *)pat, len, ftpat, ffname);
2882 source_all_matches(pat);
2883 do_cmdline_cmd((char_u *)"augroup END");
2884 }
2885 vim_free(cmd);
2886 }
Bram Moolenaar2374faa2018-02-04 17:47:42 +01002887 vim_free(pat);
2888 retval = OK;
2889
2890theend:
2891 vim_free(ffname);
2892 return retval;
2893}
2894
2895/* used for "cookie" of add_pack_plugin() */
2896static int APP_ADD_DIR;
2897static int APP_LOAD;
2898static int APP_BOTH;
2899
2900 static void
2901add_pack_plugin(char_u *fname, void *cookie)
2902{
Bram Moolenaarf98a39c2018-04-18 22:18:23 +02002903 if (cookie != &APP_LOAD)
2904 {
2905 char_u *buf = alloc(MAXPATHL);
2906 char_u *p;
2907 int found = FALSE;
2908
2909 if (buf == NULL)
Bram Moolenaar2374faa2018-02-04 17:47:42 +01002910 return;
Bram Moolenaarf98a39c2018-04-18 22:18:23 +02002911 p = p_rtp;
2912 while (*p != NUL)
2913 {
2914 copy_option_part(&p, buf, MAXPATHL, ",");
2915 if (pathcmp((char *)buf, (char *)fname, -1) == 0)
2916 {
2917 found = TRUE;
2918 break;
2919 }
2920 }
2921 vim_free(buf);
2922 if (!found)
2923 /* directory is not yet in 'runtimepath', add it */
2924 if (add_pack_dir_to_rtp(fname) == FAIL)
2925 return;
2926 }
Bram Moolenaar2374faa2018-02-04 17:47:42 +01002927
2928 if (cookie != &APP_ADD_DIR)
2929 load_pack_plugin(fname);
Bram Moolenaarf6fee0e2016-02-21 23:02:49 +01002930}
2931
Bram Moolenaarce876aa2017-06-04 17:47:42 +02002932/*
2933 * Add all packages in the "start" directory to 'runtimepath'.
2934 */
2935 void
2936add_pack_start_dirs(void)
2937{
2938 do_in_path(p_pp, (char_u *)"pack/*/start/*", DIP_ALL + DIP_DIR,
2939 add_pack_plugin, &APP_ADD_DIR);
2940}
2941
2942/*
2943 * Load plugins from all packages in the "start" directory.
2944 */
2945 void
2946load_start_packages(void)
2947{
2948 did_source_packages = TRUE;
2949 do_in_path(p_pp, (char_u *)"pack/*/start/*", DIP_ALL + DIP_DIR,
2950 add_pack_plugin, &APP_LOAD);
2951}
Bram Moolenaar2d8f56a2016-03-12 20:34:27 +01002952
Bram Moolenaarf6fee0e2016-02-21 23:02:49 +01002953/*
Bram Moolenaar2d8f56a2016-03-12 20:34:27 +01002954 * ":packloadall"
Bram Moolenaarf3654822016-03-04 22:12:23 +01002955 * Find plugins in the package directories and source them.
Bram Moolenaarf6fee0e2016-02-21 23:02:49 +01002956 */
2957 void
Bram Moolenaar2d8f56a2016-03-12 20:34:27 +01002958ex_packloadall(exarg_T *eap)
Bram Moolenaarf6fee0e2016-02-21 23:02:49 +01002959{
Bram Moolenaarce876aa2017-06-04 17:47:42 +02002960 if (!did_source_packages || eap->forceit)
Bram Moolenaar2d8f56a2016-03-12 20:34:27 +01002961 {
Bram Moolenaar49b27322016-04-05 21:13:00 +02002962 /* First do a round to add all directories to 'runtimepath', then load
2963 * the plugins. This allows for plugins to use an autoload directory
2964 * of another plugin. */
Bram Moolenaarce876aa2017-06-04 17:47:42 +02002965 add_pack_start_dirs();
2966 load_start_packages();
Bram Moolenaar2d8f56a2016-03-12 20:34:27 +01002967 }
Bram Moolenaarf6fee0e2016-02-21 23:02:49 +01002968}
2969
2970/*
Bram Moolenaarf3654822016-03-04 22:12:23 +01002971 * ":packadd[!] {name}"
Bram Moolenaar91715872016-03-03 17:13:03 +01002972 */
2973 void
2974ex_packadd(exarg_T *eap)
2975{
Bram Moolenaar9e1d3992017-12-17 14:26:46 +01002976 static char *plugpat = "pack/*/%s/%s";
Bram Moolenaar91715872016-03-03 17:13:03 +01002977 int len;
2978 char *pat;
Bram Moolenaar9e1d3992017-12-17 14:26:46 +01002979 int round;
2980 int res = OK;
Bram Moolenaar91715872016-03-03 17:13:03 +01002981
Bram Moolenaar9e1d3992017-12-17 14:26:46 +01002982 /* Round 1: use "start", round 2: use "opt". */
2983 for (round = 1; round <= 2; ++round)
2984 {
2985 /* Only look under "start" when loading packages wasn't done yet. */
2986 if (round == 1 && did_source_packages)
2987 continue;
2988
2989 len = (int)STRLEN(plugpat) + (int)STRLEN(eap->arg) + 5;
2990 pat = (char *)alloc(len);
2991 if (pat == NULL)
2992 return;
2993 vim_snprintf(pat, len, plugpat, round == 1 ? "start" : "opt", eap->arg);
2994 /* The first round don't give a "not found" error, in the second round
2995 * only when nothing was found in the first round. */
2996 res = do_in_path(p_pp, (char_u *)pat,
2997 DIP_ALL + DIP_DIR + (round == 2 && res == FAIL ? DIP_ERR : 0),
2998 add_pack_plugin, eap->forceit ? &APP_ADD_DIR : &APP_BOTH);
2999 vim_free(pat);
3000 }
Bram Moolenaar91715872016-03-03 17:13:03 +01003001}
Bram Moolenaarf2bd8ef2018-03-04 18:08:14 +01003002#endif
Bram Moolenaar91715872016-03-03 17:13:03 +01003003
Bram Moolenaarf2bd8ef2018-03-04 18:08:14 +01003004#if defined(FEAT_EVAL) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003005/*
3006 * ":options"
3007 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003008 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01003009ex_options(
3010 exarg_T *eap UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003011{
Bram Moolenaarab6c8582017-08-11 17:15:09 +02003012 vim_setenv((char_u *)"OPTWIN_CMD", (char_u *)(cmdmod.tab ? "tab" : ""));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003013 cmd_source((char_u *)SYS_OPTWIN_FILE, NULL);
3014}
3015#endif
3016
Bram Moolenaarf42dd3c2017-01-28 16:06:38 +01003017#if defined(FEAT_PYTHON3) || defined(FEAT_PYTHON) || defined(PROTO)
3018
3019# if (defined(FEAT_PYTHON) && defined(FEAT_PYTHON3)) || defined(PROTO)
3020/*
3021 * Detect Python 3 or 2, and initialize 'pyxversion'.
3022 */
3023 void
3024init_pyxversion(void)
3025{
3026 if (p_pyx == 0)
3027 {
3028 if (python3_enabled(FALSE))
3029 p_pyx = 3;
3030 else if (python_enabled(FALSE))
3031 p_pyx = 2;
3032 }
3033}
3034# endif
3035
3036/*
3037 * Does a file contain one of the following strings at the beginning of any
3038 * line?
3039 * "#!(any string)python2" => returns 2
3040 * "#!(any string)python3" => returns 3
3041 * "# requires python 2.x" => returns 2
3042 * "# requires python 3.x" => returns 3
3043 * otherwise return 0.
3044 */
3045 static int
3046requires_py_version(char_u *filename)
3047{
3048 FILE *file;
3049 int requires_py_version = 0;
3050 int i, lines;
3051
3052 lines = (int)p_mls;
3053 if (lines < 0)
3054 lines = 5;
3055
3056 file = mch_fopen((char *)filename, "r");
3057 if (file != NULL)
3058 {
3059 for (i = 0; i < lines; i++)
3060 {
3061 if (vim_fgets(IObuff, IOSIZE, file))
3062 break;
3063 if (i == 0 && IObuff[0] == '#' && IObuff[1] == '!')
3064 {
3065 /* Check shebang. */
3066 if (strstr((char *)IObuff + 2, "python2") != NULL)
3067 {
3068 requires_py_version = 2;
3069 break;
3070 }
3071 if (strstr((char *)IObuff + 2, "python3") != NULL)
3072 {
3073 requires_py_version = 3;
3074 break;
3075 }
3076 }
3077 IObuff[21] = '\0';
3078 if (STRCMP("# requires python 2.x", IObuff) == 0)
3079 {
3080 requires_py_version = 2;
3081 break;
3082 }
3083 if (STRCMP("# requires python 3.x", IObuff) == 0)
3084 {
3085 requires_py_version = 3;
3086 break;
3087 }
3088 }
3089 fclose(file);
3090 }
3091 return requires_py_version;
3092}
3093
3094
3095/*
3096 * Source a python file using the requested python version.
3097 */
3098 static void
3099source_pyx_file(exarg_T *eap, char_u *fname)
3100{
3101 exarg_T ex;
3102 int v = requires_py_version(fname);
3103
3104# if defined(FEAT_PYTHON) && defined(FEAT_PYTHON3)
3105 init_pyxversion();
3106# endif
3107 if (v == 0)
3108 {
3109# if defined(FEAT_PYTHON) && defined(FEAT_PYTHON3)
3110 /* user didn't choose a preference, 'pyx' is used */
3111 v = p_pyx;
3112# elif defined(FEAT_PYTHON)
3113 v = 2;
3114# elif defined(FEAT_PYTHON3)
3115 v = 3;
3116# endif
3117 }
3118
3119 /*
3120 * now source, if required python version is not supported show
3121 * unobtrusive message.
3122 */
3123 if (eap == NULL)
3124 vim_memset(&ex, 0, sizeof(ex));
3125 else
3126 ex = *eap;
3127 ex.arg = fname;
3128 ex.cmd = (char_u *)(v == 2 ? "pyfile" : "pyfile3");
3129
3130 if (v == 2)
3131 {
3132# ifdef FEAT_PYTHON
3133 ex_pyfile(&ex);
3134# else
3135 vim_snprintf((char *)IObuff, IOSIZE,
3136 _("W20: Required python version 2.x not supported, ignoring file: %s"),
3137 fname);
Bram Moolenaar32526b32019-01-19 17:43:09 +01003138 msg((char *)IObuff);
Bram Moolenaarf42dd3c2017-01-28 16:06:38 +01003139# endif
3140 return;
3141 }
3142 else
3143 {
3144# ifdef FEAT_PYTHON3
3145 ex_py3file(&ex);
3146# else
3147 vim_snprintf((char *)IObuff, IOSIZE,
3148 _("W21: Required python version 3.x not supported, ignoring file: %s"),
3149 fname);
Bram Moolenaar32526b32019-01-19 17:43:09 +01003150 msg((char *)IObuff);
Bram Moolenaarf42dd3c2017-01-28 16:06:38 +01003151# endif
3152 return;
3153 }
3154}
3155
3156/*
3157 * ":pyxfile {fname}"
3158 */
3159 void
3160ex_pyxfile(exarg_T *eap)
3161{
3162 source_pyx_file(eap, eap->arg);
3163}
3164
3165/*
3166 * ":pyx"
3167 */
3168 void
3169ex_pyx(exarg_T *eap)
3170{
3171# if defined(FEAT_PYTHON) && defined(FEAT_PYTHON3)
3172 init_pyxversion();
3173 if (p_pyx == 2)
3174 ex_python(eap);
3175 else
3176 ex_py3(eap);
3177# elif defined(FEAT_PYTHON)
3178 ex_python(eap);
3179# elif defined(FEAT_PYTHON3)
3180 ex_py3(eap);
3181# endif
3182}
3183
3184/*
3185 * ":pyxdo"
3186 */
3187 void
3188ex_pyxdo(exarg_T *eap)
3189{
3190# if defined(FEAT_PYTHON) && defined(FEAT_PYTHON3)
3191 init_pyxversion();
3192 if (p_pyx == 2)
3193 ex_pydo(eap);
3194 else
3195 ex_py3do(eap);
3196# elif defined(FEAT_PYTHON)
3197 ex_pydo(eap);
3198# elif defined(FEAT_PYTHON3)
3199 ex_py3do(eap);
3200# endif
3201}
3202
3203#endif
3204
Bram Moolenaar071d4272004-06-13 20:20:40 +00003205/*
3206 * ":source {fname}"
3207 */
3208 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01003209ex_source(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003210{
3211#ifdef FEAT_BROWSE
3212 if (cmdmod.browse)
3213 {
3214 char_u *fname = NULL;
3215
Bram Moolenaar7b0294c2004-10-11 10:16:09 +00003216 fname = do_browse(0, (char_u *)_("Source Vim script"), eap->arg,
Bram Moolenaarc36651b2018-04-29 12:22:56 +02003217 NULL, NULL,
3218 (char_u *)_(BROWSE_FILTER_MACROS), NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003219 if (fname != NULL)
3220 {
3221 cmd_source(fname, eap);
3222 vim_free(fname);
3223 }
3224 }
3225 else
3226#endif
3227 cmd_source(eap->arg, eap);
3228}
3229
3230 static void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01003231cmd_source(char_u *fname, exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003232{
3233 if (*fname == NUL)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01003234 emsg(_(e_argreq));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003235
Bram Moolenaar071d4272004-06-13 20:20:40 +00003236 else if (eap != NULL && eap->forceit)
Bram Moolenaar84a05ac2013-05-06 04:24:17 +02003237 /* ":source!": read Normal mode commands
Bram Moolenaar1f35bf92006-03-07 22:38:47 +00003238 * Need to execute the commands directly. This is required at least
3239 * for:
Bram Moolenaar071d4272004-06-13 20:20:40 +00003240 * - ":g" command busy
3241 * - after ":argdo", ":windo" or ":bufdo"
3242 * - another command follows
3243 * - inside a loop
3244 */
3245 openscript(fname, global_busy || listcmd_busy || eap->nextcmd != NULL
3246#ifdef FEAT_EVAL
3247 || eap->cstack->cs_idx >= 0
3248#endif
3249 );
3250
3251 /* ":source" read ex commands */
Bram Moolenaar910f66f2006-04-05 20:41:53 +00003252 else if (do_source(fname, FALSE, DOSO_NONE) == FAIL)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01003253 semsg(_(e_notopen), fname);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003254}
3255
3256/*
3257 * ":source" and associated commands.
3258 */
3259/*
3260 * Structure used to store info for each sourced file.
3261 * It is shared between do_source() and getsourceline().
3262 * This is required, because it needs to be handed to do_cmdline() and
3263 * sourcing can be done recursively.
3264 */
3265struct source_cookie
3266{
3267 FILE *fp; /* opened file for sourcing */
3268 char_u *nextline; /* if not NULL: line that was read ahead */
3269 int finished; /* ":finish" used */
Bram Moolenaar00590742019-02-15 21:06:09 +01003270#ifdef USE_CRNL
Bram Moolenaar071d4272004-06-13 20:20:40 +00003271 int fileformat; /* EOL_UNKNOWN, EOL_UNIX or EOL_DOS */
3272 int error; /* TRUE if LF found after CR-LF */
3273#endif
3274#ifdef FEAT_EVAL
3275 linenr_T breakpoint; /* next line with breakpoint or zero */
3276 char_u *fname; /* name of sourced file */
3277 int dbg_tick; /* debug_tick when breakpoint was set */
3278 int level; /* top nesting level of sourced file */
3279#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003280 vimconv_T conv; /* type of conversion */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003281};
3282
3283#ifdef FEAT_EVAL
3284/*
3285 * Return the address holding the next breakpoint line for a source cookie.
3286 */
3287 linenr_T *
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01003288source_breakpoint(void *cookie)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003289{
3290 return &((struct source_cookie *)cookie)->breakpoint;
3291}
3292
3293/*
3294 * Return the address holding the debug tick for a source cookie.
3295 */
3296 int *
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01003297source_dbg_tick(void *cookie)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003298{
3299 return &((struct source_cookie *)cookie)->dbg_tick;
3300}
3301
3302/*
3303 * Return the nesting level for a source cookie.
3304 */
3305 int
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01003306source_level(void *cookie)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003307{
3308 return ((struct source_cookie *)cookie)->level;
3309}
3310#endif
3311
Bram Moolenaarf28dbce2016-01-29 22:03:47 +01003312static char_u *get_one_sourceline(struct source_cookie *sp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003313
Bram Moolenaar4f974752019-02-17 17:44:42 +01003314#if (defined(MSWIN) && defined(FEAT_CSCOPE)) || defined(HAVE_FD_CLOEXEC)
Bram Moolenaar6b29b0e2010-01-19 16:22:03 +01003315# define USE_FOPEN_NOINH
Bram Moolenaar071d4272004-06-13 20:20:40 +00003316/*
3317 * Special function to open a file without handle inheritance.
Bram Moolenaar6b29b0e2010-01-19 16:22:03 +01003318 * When possible the handle is closed on exec().
Bram Moolenaar071d4272004-06-13 20:20:40 +00003319 */
3320 static FILE *
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01003321fopen_noinh_readbin(char *filename)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003322{
Bram Moolenaar4f974752019-02-17 17:44:42 +01003323# ifdef MSWIN
Bram Moolenaar8d8ef0b2010-01-20 21:41:47 +01003324 int fd_tmp = mch_open(filename, O_RDONLY | O_BINARY | O_NOINHERIT, 0);
3325# else
3326 int fd_tmp = mch_open(filename, O_RDONLY, 0);
Bram Moolenaar6b29b0e2010-01-19 16:22:03 +01003327# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003328
3329 if (fd_tmp == -1)
3330 return NULL;
Bram Moolenaar6b29b0e2010-01-19 16:22:03 +01003331
3332# ifdef HAVE_FD_CLOEXEC
3333 {
3334 int fdflags = fcntl(fd_tmp, F_GETFD);
3335 if (fdflags >= 0 && (fdflags & FD_CLOEXEC) == 0)
Bram Moolenaarcde88542015-08-11 19:14:00 +02003336 (void)fcntl(fd_tmp, F_SETFD, fdflags | FD_CLOEXEC);
Bram Moolenaar6b29b0e2010-01-19 16:22:03 +01003337 }
3338# endif
3339
Bram Moolenaar071d4272004-06-13 20:20:40 +00003340 return fdopen(fd_tmp, READBIN);
3341}
3342#endif
3343
3344
3345/*
3346 * do_source: Read the file "fname" and execute its lines as EX commands.
3347 *
3348 * This function may be called recursively!
3349 *
3350 * return FAIL if file could not be opened, OK otherwise
3351 */
3352 int
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01003353do_source(
3354 char_u *fname,
3355 int check_other, /* check for .vimrc and _vimrc */
3356 int is_vimrc) /* DOSO_ value */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003357{
3358 struct source_cookie cookie;
3359 char_u *save_sourcing_name;
3360 linenr_T save_sourcing_lnum;
3361 char_u *p;
3362 char_u *fname_exp;
Bram Moolenaar73881402009-02-04 16:50:47 +00003363 char_u *firstline = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003364 int retval = FAIL;
3365#ifdef FEAT_EVAL
Bram Moolenaarf29c1c62018-09-10 21:05:02 +02003366 sctx_T save_current_sctx;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003367 static scid_T last_current_SID = 0;
Bram Moolenaarded5f1b2018-11-10 17:33:29 +01003368 static int last_current_SID_seq = 0;
Bram Moolenaar27e80c82018-10-14 21:41:01 +02003369 funccal_entry_T funccalp_entry;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003370 int save_debug_break_level = debug_break_level;
Bram Moolenaar05159a02005-02-26 23:04:13 +00003371 scriptitem_T *si = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003372# ifdef UNIX
Bram Moolenaar8767f522016-07-01 17:17:39 +02003373 stat_T st;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003374 int stat_ok;
3375# endif
3376#endif
3377#ifdef STARTUPTIME
3378 struct timeval tv_rel;
3379 struct timeval tv_start;
3380#endif
Bram Moolenaar05159a02005-02-26 23:04:13 +00003381#ifdef FEAT_PROFILE
3382 proftime_T wait_start;
3383#endif
Bram Moolenaar2b618522019-01-12 13:26:03 +01003384 int trigger_source_post = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003385
Bram Moolenaar071d4272004-06-13 20:20:40 +00003386 p = expand_env_save(fname);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003387 if (p == NULL)
3388 return retval;
3389 fname_exp = fix_fname(p);
3390 vim_free(p);
3391 if (fname_exp == NULL)
3392 return retval;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003393 if (mch_isdir(fname_exp))
3394 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01003395 smsg(_("Cannot source a directory: \"%s\""), fname);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003396 goto theend;
3397 }
3398
Bram Moolenaar8dd1aa52007-01-16 20:33:19 +00003399 /* Apply SourceCmd autocommands, they should get the file and source it. */
3400 if (has_autocmd(EVENT_SOURCECMD, fname_exp, NULL)
3401 && apply_autocmds(EVENT_SOURCECMD, fname_exp, fname_exp,
3402 FALSE, curbuf))
Bram Moolenaarf33943e2008-01-15 21:17:30 +00003403 {
Bram Moolenaarf2bd8ef2018-03-04 18:08:14 +01003404#ifdef FEAT_EVAL
Bram Moolenaarf33943e2008-01-15 21:17:30 +00003405 retval = aborting() ? FAIL : OK;
Bram Moolenaarf2bd8ef2018-03-04 18:08:14 +01003406#else
Bram Moolenaarf33943e2008-01-15 21:17:30 +00003407 retval = OK;
Bram Moolenaarf2bd8ef2018-03-04 18:08:14 +01003408#endif
Bram Moolenaar2b618522019-01-12 13:26:03 +01003409 if (retval == OK)
3410 // Apply SourcePost autocommands.
3411 apply_autocmds(EVENT_SOURCEPOST, fname_exp, fname_exp,
3412 FALSE, curbuf);
Bram Moolenaarf33943e2008-01-15 21:17:30 +00003413 goto theend;
3414 }
Bram Moolenaar8dd1aa52007-01-16 20:33:19 +00003415
3416 /* Apply SourcePre autocommands, they may get the file. */
Bram Moolenaar1f35bf92006-03-07 22:38:47 +00003417 apply_autocmds(EVENT_SOURCEPRE, fname_exp, fname_exp, FALSE, curbuf);
Bram Moolenaar1f35bf92006-03-07 22:38:47 +00003418
Bram Moolenaar6b29b0e2010-01-19 16:22:03 +01003419#ifdef USE_FOPEN_NOINH
Bram Moolenaar071d4272004-06-13 20:20:40 +00003420 cookie.fp = fopen_noinh_readbin((char *)fname_exp);
3421#else
3422 cookie.fp = mch_fopen((char *)fname_exp, READBIN);
3423#endif
3424 if (cookie.fp == NULL && check_other)
3425 {
3426 /*
3427 * Try again, replacing file name ".vimrc" by "_vimrc" or vice versa,
3428 * and ".exrc" by "_exrc" or vice versa.
3429 */
3430 p = gettail(fname_exp);
3431 if ((*p == '.' || *p == '_')
3432 && (STRICMP(p + 1, "vimrc") == 0
3433 || STRICMP(p + 1, "gvimrc") == 0
3434 || STRICMP(p + 1, "exrc") == 0))
3435 {
3436 if (*p == '_')
3437 *p = '.';
3438 else
3439 *p = '_';
Bram Moolenaar6b29b0e2010-01-19 16:22:03 +01003440#ifdef USE_FOPEN_NOINH
Bram Moolenaar071d4272004-06-13 20:20:40 +00003441 cookie.fp = fopen_noinh_readbin((char *)fname_exp);
3442#else
3443 cookie.fp = mch_fopen((char *)fname_exp, READBIN);
3444#endif
3445 }
3446 }
3447
3448 if (cookie.fp == NULL)
3449 {
3450 if (p_verbose > 0)
3451 {
Bram Moolenaar54ee7752005-05-31 22:22:17 +00003452 verbose_enter();
Bram Moolenaar071d4272004-06-13 20:20:40 +00003453 if (sourcing_name == NULL)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01003454 smsg(_("could not source \"%s\""), fname);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003455 else
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01003456 smsg(_("line %ld: could not source \"%s\""),
Bram Moolenaar555b2802005-05-19 21:08:39 +00003457 sourcing_lnum, fname);
Bram Moolenaar54ee7752005-05-31 22:22:17 +00003458 verbose_leave();
Bram Moolenaar071d4272004-06-13 20:20:40 +00003459 }
3460 goto theend;
3461 }
3462
3463 /*
3464 * The file exists.
3465 * - In verbose mode, give a message.
3466 * - For a vimrc file, may want to set 'compatible', call vimrc_found().
3467 */
3468 if (p_verbose > 1)
3469 {
Bram Moolenaar54ee7752005-05-31 22:22:17 +00003470 verbose_enter();
Bram Moolenaar071d4272004-06-13 20:20:40 +00003471 if (sourcing_name == NULL)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01003472 smsg(_("sourcing \"%s\""), fname);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003473 else
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01003474 smsg(_("line %ld: sourcing \"%s\""),
Bram Moolenaar555b2802005-05-19 21:08:39 +00003475 sourcing_lnum, fname);
Bram Moolenaar54ee7752005-05-31 22:22:17 +00003476 verbose_leave();
Bram Moolenaar071d4272004-06-13 20:20:40 +00003477 }
Bram Moolenaar910f66f2006-04-05 20:41:53 +00003478 if (is_vimrc == DOSO_VIMRC)
3479 vimrc_found(fname_exp, (char_u *)"MYVIMRC");
3480 else if (is_vimrc == DOSO_GVIMRC)
3481 vimrc_found(fname_exp, (char_u *)"MYGVIMRC");
Bram Moolenaar071d4272004-06-13 20:20:40 +00003482
3483#ifdef USE_CRNL
3484 /* If no automatic file format: Set default to CR-NL. */
3485 if (*p_ffs == NUL)
3486 cookie.fileformat = EOL_DOS;
3487 else
3488 cookie.fileformat = EOL_UNKNOWN;
3489 cookie.error = FALSE;
3490#endif
3491
Bram Moolenaar071d4272004-06-13 20:20:40 +00003492 cookie.nextline = NULL;
3493 cookie.finished = FALSE;
3494
3495#ifdef FEAT_EVAL
3496 /*
3497 * Check if this script has a breakpoint.
3498 */
3499 cookie.breakpoint = dbg_find_breakpoint(TRUE, fname_exp, (linenr_T)0);
3500 cookie.fname = fname_exp;
3501 cookie.dbg_tick = debug_tick;
3502
3503 cookie.level = ex_nesting_level;
3504#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003505
3506 /*
3507 * Keep the sourcing name/lnum, for recursive calls.
3508 */
3509 save_sourcing_name = sourcing_name;
3510 sourcing_name = fname_exp;
3511 save_sourcing_lnum = sourcing_lnum;
3512 sourcing_lnum = 0;
3513
3514#ifdef STARTUPTIME
Bram Moolenaarc4e41982010-01-19 16:31:47 +01003515 if (time_fd != NULL)
3516 time_push(&tv_rel, &tv_start);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003517#endif
3518
3519#ifdef FEAT_EVAL
Bram Moolenaar05159a02005-02-26 23:04:13 +00003520# ifdef FEAT_PROFILE
Bram Moolenaar9b2200a2006-03-20 21:55:45 +00003521 if (do_profiling == PROF_YES)
Bram Moolenaar05159a02005-02-26 23:04:13 +00003522 prof_child_enter(&wait_start); /* entering a child now */
3523# endif
3524
3525 /* Don't use local function variables, if called from a function.
3526 * Also starts profiling timer for nested script. */
Bram Moolenaar27e80c82018-10-14 21:41:01 +02003527 save_funccal(&funccalp_entry);
Bram Moolenaar05159a02005-02-26 23:04:13 +00003528
Bram Moolenaar558ca4a2019-04-04 18:15:38 +02003529 save_current_sctx = current_sctx;
3530 current_sctx.sc_lnum = 0;
3531 current_sctx.sc_version = 1;
3532
Bram Moolenaarded5f1b2018-11-10 17:33:29 +01003533 // Check if this script was sourced before to finds its SID.
3534 // If it's new, generate a new SID.
3535 // Always use a new sequence number.
Bram Moolenaarded5f1b2018-11-10 17:33:29 +01003536 current_sctx.sc_seq = ++last_current_SID_seq;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003537# ifdef UNIX
3538 stat_ok = (mch_stat((char *)fname_exp, &st) >= 0);
3539# endif
Bram Moolenaarf29c1c62018-09-10 21:05:02 +02003540 for (current_sctx.sc_sid = script_items.ga_len; current_sctx.sc_sid > 0;
3541 --current_sctx.sc_sid)
Bram Moolenaar05159a02005-02-26 23:04:13 +00003542 {
Bram Moolenaarf29c1c62018-09-10 21:05:02 +02003543 si = &SCRIPT_ITEM(current_sctx.sc_sid);
Bram Moolenaar05159a02005-02-26 23:04:13 +00003544 if (si->sn_name != NULL
Bram Moolenaar071d4272004-06-13 20:20:40 +00003545 && (
3546# ifdef UNIX
Bram Moolenaar7c626922005-02-07 22:01:03 +00003547 /* Compare dev/ino when possible, it catches symbolic
3548 * links. Also compare file names, the inode may change
3549 * when the file was edited. */
Bram Moolenaarbf0c4522009-05-16 19:16:33 +00003550 ((stat_ok && si->sn_dev_valid)
Bram Moolenaar05159a02005-02-26 23:04:13 +00003551 && (si->sn_dev == st.st_dev
3552 && si->sn_ino == st.st_ino)) ||
Bram Moolenaar071d4272004-06-13 20:20:40 +00003553# endif
Bram Moolenaar05159a02005-02-26 23:04:13 +00003554 fnamecmp(si->sn_name, fname_exp) == 0))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003555 break;
Bram Moolenaar05159a02005-02-26 23:04:13 +00003556 }
Bram Moolenaarf29c1c62018-09-10 21:05:02 +02003557 if (current_sctx.sc_sid == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003558 {
Bram Moolenaarf29c1c62018-09-10 21:05:02 +02003559 current_sctx.sc_sid = ++last_current_SID;
3560 if (ga_grow(&script_items,
3561 (int)(current_sctx.sc_sid - script_items.ga_len)) == FAIL)
Bram Moolenaar05159a02005-02-26 23:04:13 +00003562 goto almosttheend;
Bram Moolenaarf29c1c62018-09-10 21:05:02 +02003563 while (script_items.ga_len < current_sctx.sc_sid)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003564 {
Bram Moolenaar05159a02005-02-26 23:04:13 +00003565 ++script_items.ga_len;
3566 SCRIPT_ITEM(script_items.ga_len).sn_name = NULL;
3567# ifdef FEAT_PROFILE
3568 SCRIPT_ITEM(script_items.ga_len).sn_prof_on = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003569# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003570 }
Bram Moolenaarf29c1c62018-09-10 21:05:02 +02003571 si = &SCRIPT_ITEM(current_sctx.sc_sid);
Bram Moolenaar05159a02005-02-26 23:04:13 +00003572 si->sn_name = fname_exp;
Bram Moolenaarea56e162019-01-12 15:15:38 +01003573 fname_exp = vim_strsave(si->sn_name); // used for autocmd
Bram Moolenaar05159a02005-02-26 23:04:13 +00003574# ifdef UNIX
3575 if (stat_ok)
3576 {
Bram Moolenaarbf0c4522009-05-16 19:16:33 +00003577 si->sn_dev_valid = TRUE;
Bram Moolenaar05159a02005-02-26 23:04:13 +00003578 si->sn_dev = st.st_dev;
3579 si->sn_ino = st.st_ino;
3580 }
3581 else
Bram Moolenaarbf0c4522009-05-16 19:16:33 +00003582 si->sn_dev_valid = FALSE;
Bram Moolenaar05159a02005-02-26 23:04:13 +00003583# endif
3584
Bram Moolenaar071d4272004-06-13 20:20:40 +00003585 /* Allocate the local script variables to use for this script. */
Bram Moolenaarf29c1c62018-09-10 21:05:02 +02003586 new_script_vars(current_sctx.sc_sid);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003587 }
3588
Bram Moolenaar05159a02005-02-26 23:04:13 +00003589# ifdef FEAT_PROFILE
Bram Moolenaar9b2200a2006-03-20 21:55:45 +00003590 if (do_profiling == PROF_YES)
Bram Moolenaar05159a02005-02-26 23:04:13 +00003591 {
3592 int forceit;
3593
3594 /* Check if we do profiling for this script. */
3595 if (!si->sn_prof_on && has_profiling(TRUE, si->sn_name, &forceit))
3596 {
3597 script_do_profile(si);
3598 si->sn_pr_force = forceit;
3599 }
3600 if (si->sn_prof_on)
3601 {
3602 ++si->sn_pr_count;
3603 profile_start(&si->sn_pr_start);
3604 profile_zero(&si->sn_pr_children);
3605 }
3606 }
3607# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003608#endif
3609
Bram Moolenaar67435d92017-10-19 21:04:37 +02003610 cookie.conv.vc_type = CONV_NONE; /* no conversion */
3611
3612 /* Read the first line so we can check for a UTF-8 BOM. */
3613 firstline = getsourceline(0, (void *)&cookie, 0);
3614 if (firstline != NULL && STRLEN(firstline) >= 3 && firstline[0] == 0xef
3615 && firstline[1] == 0xbb && firstline[2] == 0xbf)
3616 {
3617 /* Found BOM; setup conversion, skip over BOM and recode the line. */
3618 convert_setup(&cookie.conv, (char_u *)"utf-8", p_enc);
3619 p = string_convert(&cookie.conv, firstline + 3, NULL);
3620 if (p == NULL)
3621 p = vim_strsave(firstline + 3);
3622 if (p != NULL)
3623 {
3624 vim_free(firstline);
3625 firstline = p;
3626 }
3627 }
Bram Moolenaar67435d92017-10-19 21:04:37 +02003628
Bram Moolenaar071d4272004-06-13 20:20:40 +00003629 /*
3630 * Call do_cmdline, which will call getsourceline() to get the lines.
3631 */
Bram Moolenaar73881402009-02-04 16:50:47 +00003632 do_cmdline(firstline, getsourceline, (void *)&cookie,
Bram Moolenaar071d4272004-06-13 20:20:40 +00003633 DOCMD_VERBOSE|DOCMD_NOWAIT|DOCMD_REPEAT);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003634 retval = OK;
Bram Moolenaar05159a02005-02-26 23:04:13 +00003635
3636#ifdef FEAT_PROFILE
Bram Moolenaar9b2200a2006-03-20 21:55:45 +00003637 if (do_profiling == PROF_YES)
Bram Moolenaar05159a02005-02-26 23:04:13 +00003638 {
3639 /* Get "si" again, "script_items" may have been reallocated. */
Bram Moolenaarf29c1c62018-09-10 21:05:02 +02003640 si = &SCRIPT_ITEM(current_sctx.sc_sid);
Bram Moolenaar05159a02005-02-26 23:04:13 +00003641 if (si->sn_prof_on)
3642 {
3643 profile_end(&si->sn_pr_start);
3644 profile_sub_wait(&wait_start, &si->sn_pr_start);
3645 profile_add(&si->sn_pr_total, &si->sn_pr_start);
Bram Moolenaar1056d982006-03-09 22:37:52 +00003646 profile_self(&si->sn_pr_self, &si->sn_pr_start,
3647 &si->sn_pr_children);
Bram Moolenaar05159a02005-02-26 23:04:13 +00003648 }
3649 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003650#endif
3651
3652 if (got_int)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01003653 emsg(_(e_interr));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003654 sourcing_name = save_sourcing_name;
3655 sourcing_lnum = save_sourcing_lnum;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003656 if (p_verbose > 1)
3657 {
Bram Moolenaar54ee7752005-05-31 22:22:17 +00003658 verbose_enter();
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01003659 smsg(_("finished sourcing %s"), fname);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003660 if (sourcing_name != NULL)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01003661 smsg(_("continuing in %s"), sourcing_name);
Bram Moolenaar54ee7752005-05-31 22:22:17 +00003662 verbose_leave();
Bram Moolenaar071d4272004-06-13 20:20:40 +00003663 }
3664#ifdef STARTUPTIME
Bram Moolenaarc4e41982010-01-19 16:31:47 +01003665 if (time_fd != NULL)
3666 {
3667 vim_snprintf((char *)IObuff, IOSIZE, "sourcing %s", fname);
3668 time_msg((char *)IObuff, &tv_start);
3669 time_pop(&tv_rel);
3670 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003671#endif
3672
Bram Moolenaar2b618522019-01-12 13:26:03 +01003673 if (!got_int)
3674 trigger_source_post = TRUE;
3675
Bram Moolenaar071d4272004-06-13 20:20:40 +00003676#ifdef FEAT_EVAL
3677 /*
3678 * After a "finish" in debug mode, need to break at first command of next
3679 * sourced file.
3680 */
3681 if (save_debug_break_level > ex_nesting_level
3682 && debug_break_level == ex_nesting_level)
3683 ++debug_break_level;
3684#endif
3685
Bram Moolenaar05159a02005-02-26 23:04:13 +00003686#ifdef FEAT_EVAL
3687almosttheend:
Bram Moolenaarf29c1c62018-09-10 21:05:02 +02003688 current_sctx = save_current_sctx;
Bram Moolenaar27e80c82018-10-14 21:41:01 +02003689 restore_funccal();
Bram Moolenaar05159a02005-02-26 23:04:13 +00003690# ifdef FEAT_PROFILE
Bram Moolenaar9b2200a2006-03-20 21:55:45 +00003691 if (do_profiling == PROF_YES)
Bram Moolenaar05159a02005-02-26 23:04:13 +00003692 prof_child_exit(&wait_start); /* leaving a child now */
3693# endif
3694#endif
3695 fclose(cookie.fp);
3696 vim_free(cookie.nextline);
Bram Moolenaar73881402009-02-04 16:50:47 +00003697 vim_free(firstline);
Bram Moolenaar05159a02005-02-26 23:04:13 +00003698 convert_setup(&cookie.conv, NULL, NULL);
Bram Moolenaar05159a02005-02-26 23:04:13 +00003699
Bram Moolenaar2b618522019-01-12 13:26:03 +01003700 if (trigger_source_post)
Bram Moolenaarea56e162019-01-12 15:15:38 +01003701 apply_autocmds(EVENT_SOURCEPOST, fname_exp, fname_exp, FALSE, curbuf);
Bram Moolenaar2b618522019-01-12 13:26:03 +01003702
Bram Moolenaar071d4272004-06-13 20:20:40 +00003703theend:
3704 vim_free(fname_exp);
3705 return retval;
3706}
3707
3708#if defined(FEAT_EVAL) || defined(PROTO)
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00003709
Bram Moolenaar071d4272004-06-13 20:20:40 +00003710/*
3711 * ":scriptnames"
3712 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003713 void
Bram Moolenaar07dc18f2018-11-30 22:48:32 +01003714ex_scriptnames(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003715{
3716 int i;
3717
Bram Moolenaar07dc18f2018-11-30 22:48:32 +01003718 if (eap->addr_count > 0)
3719 {
3720 // :script {scriptId}: edit the script
3721 if (eap->line2 < 1 || eap->line2 > script_items.ga_len)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01003722 emsg(_(e_invarg));
Bram Moolenaar07dc18f2018-11-30 22:48:32 +01003723 else
3724 {
3725 eap->arg = SCRIPT_ITEM(eap->line2).sn_name;
3726 do_exedit(eap, NULL);
3727 }
3728 return;
3729 }
3730
Bram Moolenaar05159a02005-02-26 23:04:13 +00003731 for (i = 1; i <= script_items.ga_len && !got_int; ++i)
3732 if (SCRIPT_ITEM(i).sn_name != NULL)
Bram Moolenaard58ea072011-06-26 04:25:30 +02003733 {
3734 home_replace(NULL, SCRIPT_ITEM(i).sn_name,
3735 NameBuff, MAXPATHL, TRUE);
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01003736 smsg("%3d: %s", i, NameBuff);
Bram Moolenaar970a1b82012-03-23 18:39:18 +01003737 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003738}
3739
3740# if defined(BACKSLASH_IN_FILENAME) || defined(PROTO)
3741/*
3742 * Fix slashes in the list of script names for 'shellslash'.
3743 */
3744 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01003745scriptnames_slash_adjust(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003746{
3747 int i;
3748
Bram Moolenaar05159a02005-02-26 23:04:13 +00003749 for (i = 1; i <= script_items.ga_len; ++i)
3750 if (SCRIPT_ITEM(i).sn_name != NULL)
3751 slash_adjust(SCRIPT_ITEM(i).sn_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003752}
3753# endif
3754
3755/*
3756 * Get a pointer to a script name. Used for ":verbose set".
3757 */
3758 char_u *
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01003759get_scriptname(scid_T id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003760{
3761 if (id == SID_MODELINE)
Bram Moolenaard1f56e62006-02-22 21:25:37 +00003762 return (char_u *)_("modeline");
Bram Moolenaar071d4272004-06-13 20:20:40 +00003763 if (id == SID_CMDARG)
Bram Moolenaard1f56e62006-02-22 21:25:37 +00003764 return (char_u *)_("--cmd argument");
Bram Moolenaar071d4272004-06-13 20:20:40 +00003765 if (id == SID_CARG)
Bram Moolenaard1f56e62006-02-22 21:25:37 +00003766 return (char_u *)_("-c argument");
Bram Moolenaar071d4272004-06-13 20:20:40 +00003767 if (id == SID_ENV)
Bram Moolenaard1f56e62006-02-22 21:25:37 +00003768 return (char_u *)_("environment variable");
3769 if (id == SID_ERROR)
3770 return (char_u *)_("error handler");
Bram Moolenaar05159a02005-02-26 23:04:13 +00003771 return SCRIPT_ITEM(id).sn_name;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003772}
Bram Moolenaar05159a02005-02-26 23:04:13 +00003773
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00003774# if defined(EXITFREE) || defined(PROTO)
3775 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01003776free_scriptnames(void)
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00003777{
3778 int i;
3779
3780 for (i = script_items.ga_len; i > 0; --i)
3781 vim_free(SCRIPT_ITEM(i).sn_name);
3782 ga_clear(&script_items);
3783}
3784# endif
3785
Bram Moolenaar071d4272004-06-13 20:20:40 +00003786#endif
3787
Bram Moolenaar071d4272004-06-13 20:20:40 +00003788/*
3789 * Get one full line from a sourced file.
3790 * Called by do_cmdline() when it's called from do_source().
3791 *
3792 * Return a pointer to the line in allocated memory.
3793 * Return NULL for end-of-file or some error.
3794 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003795 char_u *
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01003796getsourceline(int c UNUSED, void *cookie, int indent UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003797{
3798 struct source_cookie *sp = (struct source_cookie *)cookie;
3799 char_u *line;
Bram Moolenaarc047b9a2012-02-11 20:40:55 +01003800 char_u *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003801
3802#ifdef FEAT_EVAL
3803 /* If breakpoints have been added/deleted need to check for it. */
3804 if (sp->dbg_tick < debug_tick)
3805 {
3806 sp->breakpoint = dbg_find_breakpoint(TRUE, sp->fname, sourcing_lnum);
3807 sp->dbg_tick = debug_tick;
3808 }
Bram Moolenaar05159a02005-02-26 23:04:13 +00003809# ifdef FEAT_PROFILE
Bram Moolenaar9b2200a2006-03-20 21:55:45 +00003810 if (do_profiling == PROF_YES)
Bram Moolenaar05159a02005-02-26 23:04:13 +00003811 script_line_end();
3812# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003813#endif
3814 /*
3815 * Get current line. If there is a read-ahead line, use it, otherwise get
3816 * one now.
3817 */
3818 if (sp->finished)
3819 line = NULL;
3820 else if (sp->nextline == NULL)
3821 line = get_one_sourceline(sp);
3822 else
3823 {
3824 line = sp->nextline;
3825 sp->nextline = NULL;
3826 ++sourcing_lnum;
3827 }
Bram Moolenaare2cc9702005-03-15 22:43:58 +00003828#ifdef FEAT_PROFILE
Bram Moolenaar9b2200a2006-03-20 21:55:45 +00003829 if (line != NULL && do_profiling == PROF_YES)
Bram Moolenaare2cc9702005-03-15 22:43:58 +00003830 script_line_start();
3831#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003832
3833 /* Only concatenate lines starting with a \ when 'cpoptions' doesn't
3834 * contain the 'C' flag. */
3835 if (line != NULL && (vim_strchr(p_cpo, CPO_CONCAT) == NULL))
3836 {
3837 /* compensate for the one line read-ahead */
3838 --sourcing_lnum;
Bram Moolenaarb3a6bbc2012-02-05 23:10:30 +01003839
Bram Moolenaar67f8ab82018-09-11 22:37:29 +02003840 // Get the next line and concatenate it when it starts with a
3841 // backslash. We always need to read the next line, keep it in
3842 // sp->nextline.
3843 /* Also check for a comment in between continuation lines: "\ */
Bram Moolenaarb3a6bbc2012-02-05 23:10:30 +01003844 sp->nextline = get_one_sourceline(sp);
Bram Moolenaar67f8ab82018-09-11 22:37:29 +02003845 if (sp->nextline != NULL
3846 && (*(p = skipwhite(sp->nextline)) == '\\'
3847 || (p[0] == '"' && p[1] == '\\' && p[2] == ' ')))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003848 {
Bram Moolenaarb3a6bbc2012-02-05 23:10:30 +01003849 garray_T ga;
3850
Bram Moolenaarb549a732012-02-22 18:29:33 +01003851 ga_init2(&ga, (int)sizeof(char_u), 400);
Bram Moolenaarb3a6bbc2012-02-05 23:10:30 +01003852 ga_concat(&ga, line);
Bram Moolenaar67f8ab82018-09-11 22:37:29 +02003853 if (*p == '\\')
3854 ga_concat(&ga, p + 1);
Bram Moolenaarb3a6bbc2012-02-05 23:10:30 +01003855 for (;;)
3856 {
3857 vim_free(sp->nextline);
3858 sp->nextline = get_one_sourceline(sp);
3859 if (sp->nextline == NULL)
3860 break;
3861 p = skipwhite(sp->nextline);
Bram Moolenaar67f8ab82018-09-11 22:37:29 +02003862 if (*p == '\\')
Bram Moolenaarb549a732012-02-22 18:29:33 +01003863 {
Bram Moolenaar67f8ab82018-09-11 22:37:29 +02003864 // Adjust the growsize to the current length to speed up
3865 // concatenating many lines.
3866 if (ga.ga_len > 400)
3867 {
3868 if (ga.ga_len > 8000)
3869 ga.ga_growsize = 8000;
3870 else
3871 ga.ga_growsize = ga.ga_len;
3872 }
3873 ga_concat(&ga, p + 1);
Bram Moolenaarb549a732012-02-22 18:29:33 +01003874 }
Bram Moolenaar67f8ab82018-09-11 22:37:29 +02003875 else if (p[0] != '"' || p[1] != '\\' || p[2] != ' ')
3876 break;
Bram Moolenaarb3a6bbc2012-02-05 23:10:30 +01003877 }
3878 ga_append(&ga, NUL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003879 vim_free(line);
Bram Moolenaarb3a6bbc2012-02-05 23:10:30 +01003880 line = ga.ga_data;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003881 }
3882 }
3883
Bram Moolenaar071d4272004-06-13 20:20:40 +00003884 if (line != NULL && sp->conv.vc_type != CONV_NONE)
3885 {
Bram Moolenaarc047b9a2012-02-11 20:40:55 +01003886 char_u *s;
3887
Bram Moolenaar071d4272004-06-13 20:20:40 +00003888 /* Convert the encoding of the script line. */
3889 s = string_convert(&sp->conv, line, NULL);
3890 if (s != NULL)
3891 {
3892 vim_free(line);
3893 line = s;
3894 }
3895 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003896
3897#ifdef FEAT_EVAL
3898 /* Did we encounter a breakpoint? */
3899 if (sp->breakpoint != 0 && sp->breakpoint <= sourcing_lnum)
3900 {
3901 dbg_breakpoint(sp->fname, sourcing_lnum);
3902 /* Find next breakpoint. */
3903 sp->breakpoint = dbg_find_breakpoint(TRUE, sp->fname, sourcing_lnum);
3904 sp->dbg_tick = debug_tick;
3905 }
3906#endif
3907
3908 return line;
3909}
3910
3911 static char_u *
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01003912get_one_sourceline(struct source_cookie *sp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003913{
3914 garray_T ga;
3915 int len;
3916 int c;
3917 char_u *buf;
3918#ifdef USE_CRNL
3919 int has_cr; /* CR-LF found */
3920#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003921 int have_read = FALSE;
3922
3923 /* use a growarray to store the sourced line */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00003924 ga_init2(&ga, 1, 250);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003925
3926 /*
3927 * Loop until there is a finished line (or end-of-file).
3928 */
3929 sourcing_lnum++;
3930 for (;;)
3931 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00003932 /* make room to read at least 120 (more) characters */
3933 if (ga_grow(&ga, 120) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003934 break;
3935 buf = (char_u *)ga.ga_data;
3936
Bram Moolenaar00590742019-02-15 21:06:09 +01003937 if (fgets((char *)buf + ga.ga_len, ga.ga_maxlen - ga.ga_len,
Bram Moolenaar86b68352004-12-27 21:59:20 +00003938 sp->fp) == NULL)
Bram Moolenaar00590742019-02-15 21:06:09 +01003939 break;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00003940 len = ga.ga_len + (int)STRLEN(buf + ga.ga_len);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003941#ifdef USE_CRNL
3942 /* Ignore a trailing CTRL-Z, when in Dos mode. Only recognize the
3943 * CTRL-Z by its own, or after a NL. */
3944 if ( (len == 1 || (len >= 2 && buf[len - 2] == '\n'))
3945 && sp->fileformat == EOL_DOS
3946 && buf[len - 1] == Ctrl_Z)
3947 {
3948 buf[len - 1] = NUL;
3949 break;
3950 }
3951#endif
3952
Bram Moolenaar071d4272004-06-13 20:20:40 +00003953 have_read = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003954 ga.ga_len = len;
3955
3956 /* If the line was longer than the buffer, read more. */
Bram Moolenaar86b68352004-12-27 21:59:20 +00003957 if (ga.ga_maxlen - ga.ga_len == 1 && buf[len - 1] != '\n')
Bram Moolenaar071d4272004-06-13 20:20:40 +00003958 continue;
3959
3960 if (len >= 1 && buf[len - 1] == '\n') /* remove trailing NL */
3961 {
3962#ifdef USE_CRNL
3963 has_cr = (len >= 2 && buf[len - 2] == '\r');
3964 if (sp->fileformat == EOL_UNKNOWN)
3965 {
3966 if (has_cr)
3967 sp->fileformat = EOL_DOS;
3968 else
3969 sp->fileformat = EOL_UNIX;
3970 }
3971
3972 if (sp->fileformat == EOL_DOS)
3973 {
3974 if (has_cr) /* replace trailing CR */
3975 {
3976 buf[len - 2] = '\n';
3977 --len;
3978 --ga.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003979 }
3980 else /* lines like ":map xx yy^M" will have failed */
3981 {
3982 if (!sp->error)
Bram Moolenaar2df6dcc2004-07-12 15:53:54 +00003983 {
Bram Moolenaar8820b482017-03-16 17:23:31 +01003984 msg_source(HL_ATTR(HLF_W));
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01003985 emsg(_("W15: Warning: Wrong line separator, ^M may be missing"));
Bram Moolenaar2df6dcc2004-07-12 15:53:54 +00003986 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003987 sp->error = TRUE;
3988 sp->fileformat = EOL_UNIX;
3989 }
3990 }
3991#endif
3992 /* The '\n' is escaped if there is an odd number of ^V's just
3993 * before it, first set "c" just before the 'V's and then check
3994 * len&c parities (is faster than ((len-c)%2 == 0)) -- Acevedo */
3995 for (c = len - 2; c >= 0 && buf[c] == Ctrl_V; c--)
3996 ;
3997 if ((len & 1) != (c & 1)) /* escaped NL, read more */
3998 {
3999 sourcing_lnum++;
4000 continue;
4001 }
4002
4003 buf[len - 1] = NUL; /* remove the NL */
4004 }
4005
4006 /*
4007 * Check for ^C here now and then, so recursive :so can be broken.
4008 */
4009 line_breakcheck();
4010 break;
4011 }
4012
4013 if (have_read)
4014 return (char_u *)ga.ga_data;
4015
4016 vim_free(ga.ga_data);
4017 return NULL;
4018}
4019
Bram Moolenaar05159a02005-02-26 23:04:13 +00004020#if defined(FEAT_PROFILE) || defined(PROTO)
4021/*
4022 * Called when starting to read a script line.
4023 * "sourcing_lnum" must be correct!
4024 * When skipping lines it may not actually be executed, but we won't find out
4025 * until later and we need to store the time now.
4026 */
4027 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01004028script_line_start(void)
Bram Moolenaar05159a02005-02-26 23:04:13 +00004029{
4030 scriptitem_T *si;
4031 sn_prl_T *pp;
4032
Bram Moolenaarf29c1c62018-09-10 21:05:02 +02004033 if (current_sctx.sc_sid <= 0 || current_sctx.sc_sid > script_items.ga_len)
Bram Moolenaar05159a02005-02-26 23:04:13 +00004034 return;
Bram Moolenaarf29c1c62018-09-10 21:05:02 +02004035 si = &SCRIPT_ITEM(current_sctx.sc_sid);
Bram Moolenaar05159a02005-02-26 23:04:13 +00004036 if (si->sn_prof_on && sourcing_lnum >= 1)
4037 {
Bram Moolenaar8c8de832008-06-24 22:58:06 +00004038 /* Grow the array before starting the timer, so that the time spent
Bram Moolenaar05159a02005-02-26 23:04:13 +00004039 * here isn't counted. */
Bram Moolenaar67435d92017-10-19 21:04:37 +02004040 (void)ga_grow(&si->sn_prl_ga,
4041 (int)(sourcing_lnum - si->sn_prl_ga.ga_len));
Bram Moolenaar05159a02005-02-26 23:04:13 +00004042 si->sn_prl_idx = sourcing_lnum - 1;
4043 while (si->sn_prl_ga.ga_len <= si->sn_prl_idx
4044 && si->sn_prl_ga.ga_len < si->sn_prl_ga.ga_maxlen)
4045 {
4046 /* Zero counters for a line that was not used before. */
4047 pp = &PRL_ITEM(si, si->sn_prl_ga.ga_len);
4048 pp->snp_count = 0;
4049 profile_zero(&pp->sn_prl_total);
4050 profile_zero(&pp->sn_prl_self);
4051 ++si->sn_prl_ga.ga_len;
4052 }
4053 si->sn_prl_execed = FALSE;
4054 profile_start(&si->sn_prl_start);
4055 profile_zero(&si->sn_prl_children);
4056 profile_get_wait(&si->sn_prl_wait);
4057 }
4058}
4059
4060/*
4061 * Called when actually executing a function line.
4062 */
4063 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01004064script_line_exec(void)
Bram Moolenaar05159a02005-02-26 23:04:13 +00004065{
4066 scriptitem_T *si;
4067
Bram Moolenaarf29c1c62018-09-10 21:05:02 +02004068 if (current_sctx.sc_sid <= 0 || current_sctx.sc_sid > script_items.ga_len)
Bram Moolenaar05159a02005-02-26 23:04:13 +00004069 return;
Bram Moolenaarf29c1c62018-09-10 21:05:02 +02004070 si = &SCRIPT_ITEM(current_sctx.sc_sid);
Bram Moolenaar05159a02005-02-26 23:04:13 +00004071 if (si->sn_prof_on && si->sn_prl_idx >= 0)
4072 si->sn_prl_execed = TRUE;
4073}
4074
4075/*
Bram Moolenaar67435d92017-10-19 21:04:37 +02004076 * Called when done with a script line.
Bram Moolenaar05159a02005-02-26 23:04:13 +00004077 */
4078 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01004079script_line_end(void)
Bram Moolenaar05159a02005-02-26 23:04:13 +00004080{
4081 scriptitem_T *si;
4082 sn_prl_T *pp;
4083
Bram Moolenaarf29c1c62018-09-10 21:05:02 +02004084 if (current_sctx.sc_sid <= 0 || current_sctx.sc_sid > script_items.ga_len)
Bram Moolenaar05159a02005-02-26 23:04:13 +00004085 return;
Bram Moolenaarf29c1c62018-09-10 21:05:02 +02004086 si = &SCRIPT_ITEM(current_sctx.sc_sid);
Bram Moolenaar05159a02005-02-26 23:04:13 +00004087 if (si->sn_prof_on && si->sn_prl_idx >= 0
4088 && si->sn_prl_idx < si->sn_prl_ga.ga_len)
4089 {
4090 if (si->sn_prl_execed)
4091 {
4092 pp = &PRL_ITEM(si, si->sn_prl_idx);
4093 ++pp->snp_count;
4094 profile_end(&si->sn_prl_start);
4095 profile_sub_wait(&si->sn_prl_wait, &si->sn_prl_start);
Bram Moolenaar05159a02005-02-26 23:04:13 +00004096 profile_add(&pp->sn_prl_total, &si->sn_prl_start);
Bram Moolenaar1056d982006-03-09 22:37:52 +00004097 profile_self(&pp->sn_prl_self, &si->sn_prl_start,
4098 &si->sn_prl_children);
Bram Moolenaar05159a02005-02-26 23:04:13 +00004099 }
4100 si->sn_prl_idx = -1;
4101 }
4102}
4103#endif
4104
Bram Moolenaar071d4272004-06-13 20:20:40 +00004105/*
4106 * ":scriptencoding": Set encoding conversion for a sourced script.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004107 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004108 void
Bram Moolenaar558ca4a2019-04-04 18:15:38 +02004109ex_scriptencoding(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004110{
Bram Moolenaar071d4272004-06-13 20:20:40 +00004111 struct source_cookie *sp;
4112 char_u *name;
4113
4114 if (!getline_equal(eap->getline, eap->cookie, getsourceline))
4115 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01004116 emsg(_("E167: :scriptencoding used outside of a sourced file"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004117 return;
4118 }
4119
4120 if (*eap->arg != NUL)
4121 {
4122 name = enc_canonize(eap->arg);
4123 if (name == NULL) /* out of memory */
4124 return;
4125 }
4126 else
4127 name = eap->arg;
4128
4129 /* Setup for conversion from the specified encoding to 'encoding'. */
4130 sp = (struct source_cookie *)getline_cookie(eap->getline, eap->cookie);
4131 convert_setup(&sp->conv, name, p_enc);
4132
4133 if (name != eap->arg)
4134 vim_free(name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004135}
4136
Bram Moolenaar558ca4a2019-04-04 18:15:38 +02004137/*
4138 * ":scriptversion": Set Vim script version for a sourced script.
4139 */
4140 void
4141ex_scriptversion(exarg_T *eap UNUSED)
4142{
Bram Moolenaar1cd4dc42019-04-04 19:06:14 +02004143#ifdef FEAT_EVAL
Bram Moolenaar558ca4a2019-04-04 18:15:38 +02004144 int nr;
4145
4146 if (!getline_equal(eap->getline, eap->cookie, getsourceline))
4147 {
4148 emsg(_("E984: :scriptversion used outside of a sourced file"));
4149 return;
4150 }
4151
4152 nr = getdigits(&eap->arg);
4153 if (nr == 0 || *eap->arg != NUL)
4154 emsg(_(e_invarg));
Bram Moolenaard2e716e2019-04-20 14:39:52 +02004155 else if (nr > 3)
Bram Moolenaar558ca4a2019-04-04 18:15:38 +02004156 semsg(_("E999: scriptversion not supported: %d"), nr);
4157 else
4158 current_sctx.sc_version = nr;
Bram Moolenaar1cd4dc42019-04-04 19:06:14 +02004159#endif
Bram Moolenaar558ca4a2019-04-04 18:15:38 +02004160}
4161
Bram Moolenaar071d4272004-06-13 20:20:40 +00004162#if defined(FEAT_EVAL) || defined(PROTO)
4163/*
4164 * ":finish": Mark a sourced file as finished.
4165 */
4166 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01004167ex_finish(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004168{
4169 if (getline_equal(eap->getline, eap->cookie, getsourceline))
4170 do_finish(eap, FALSE);
4171 else
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01004172 emsg(_("E168: :finish used outside of a sourced file"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004173}
4174
4175/*
4176 * Mark a sourced file as finished. Possibly makes the ":finish" pending.
4177 * Also called for a pending finish at the ":endtry" or after returning from
4178 * an extra do_cmdline(). "reanimate" is used in the latter case.
4179 */
4180 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01004181do_finish(exarg_T *eap, int reanimate)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004182{
4183 int idx;
4184
4185 if (reanimate)
4186 ((struct source_cookie *)getline_cookie(eap->getline,
4187 eap->cookie))->finished = FALSE;
4188
4189 /*
4190 * Cleanup (and inactivate) conditionals, but stop when a try conditional
4191 * not in its finally clause (which then is to be executed next) is found.
4192 * In this case, make the ":finish" pending for execution at the ":endtry".
4193 * Otherwise, finish normally.
4194 */
4195 idx = cleanup_conditionals(eap->cstack, 0, TRUE);
4196 if (idx >= 0)
4197 {
4198 eap->cstack->cs_pending[idx] = CSTP_FINISH;
4199 report_make_pending(CSTP_FINISH, NULL);
4200 }
4201 else
4202 ((struct source_cookie *)getline_cookie(eap->getline,
4203 eap->cookie))->finished = TRUE;
4204}
4205
4206
4207/*
4208 * Return TRUE when a sourced file had the ":finish" command: Don't give error
4209 * message for missing ":endif".
4210 * Return FALSE when not sourcing a file.
4211 */
4212 int
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01004213source_finished(
4214 char_u *(*fgetline)(int, void *, int),
4215 void *cookie)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004216{
Bram Moolenaar89d40322006-08-29 15:30:07 +00004217 return (getline_equal(fgetline, cookie, getsourceline)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004218 && ((struct source_cookie *)getline_cookie(
Bram Moolenaar89d40322006-08-29 15:30:07 +00004219 fgetline, cookie))->finished);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004220}
4221#endif
4222
Bram Moolenaar071d4272004-06-13 20:20:40 +00004223/*
4224 * ":checktime [buffer]"
4225 */
4226 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01004227ex_checktime(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004228{
4229 buf_T *buf;
4230 int save_no_check_timestamps = no_check_timestamps;
4231
4232 no_check_timestamps = 0;
4233 if (eap->addr_count == 0) /* default is all buffers */
4234 check_timestamps(FALSE);
4235 else
4236 {
4237 buf = buflist_findnr((int)eap->line2);
4238 if (buf != NULL) /* cannot happen? */
4239 (void)buf_check_timestamp(buf, FALSE);
4240 }
4241 no_check_timestamps = save_no_check_timestamps;
4242}
Bram Moolenaar071d4272004-06-13 20:20:40 +00004243
Bram Moolenaar071d4272004-06-13 20:20:40 +00004244#if (defined(HAVE_LOCALE_H) || defined(X_LOCALE)) \
4245 && (defined(FEAT_EVAL) || defined(FEAT_MULTI_LANG))
Bram Moolenaar8765a4a2010-07-27 22:41:43 +02004246# define HAVE_GET_LOCALE_VAL
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01004247 static char_u *
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01004248get_locale_val(int what)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004249{
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01004250 char_u *loc;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004251
Bram Moolenaar48e330a2016-02-23 14:53:34 +01004252 /* Obtain the locale value from the libraries. */
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01004253 loc = (char_u *)setlocale(what, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004254
Bram Moolenaar4f974752019-02-17 17:44:42 +01004255# ifdef MSWIN
Bram Moolenaar071d4272004-06-13 20:20:40 +00004256 if (loc != NULL)
4257 {
4258 char_u *p;
4259
Bram Moolenaar61660ea2006-04-07 21:40:07 +00004260 /* setocale() returns something like "LC_COLLATE=<name>;LC_..." when
4261 * one of the values (e.g., LC_CTYPE) differs. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004262 p = vim_strchr(loc, '=');
4263 if (p != NULL)
4264 {
4265 loc = ++p;
4266 while (*p != NUL) /* remove trailing newline */
4267 {
Bram Moolenaar61660ea2006-04-07 21:40:07 +00004268 if (*p < ' ' || *p == ';')
Bram Moolenaar071d4272004-06-13 20:20:40 +00004269 {
4270 *p = NUL;
4271 break;
4272 }
4273 ++p;
4274 }
4275 }
4276 }
4277# endif
4278
4279 return loc;
4280}
4281#endif
4282
4283
Bram Moolenaar4f974752019-02-17 17:44:42 +01004284#ifdef MSWIN
Bram Moolenaar071d4272004-06-13 20:20:40 +00004285/*
4286 * On MS-Windows locale names are strings like "German_Germany.1252", but
4287 * gettext expects "de". Try to translate one into another here for a few
4288 * supported languages.
4289 */
4290 static char_u *
4291gettext_lang(char_u *name)
4292{
4293 int i;
4294 static char *(mtable[]) = {
4295 "afrikaans", "af",
4296 "czech", "cs",
4297 "dutch", "nl",
4298 "german", "de",
4299 "english_united kingdom", "en_GB",
4300 "spanish", "es",
4301 "french", "fr",
4302 "italian", "it",
4303 "japanese", "ja",
4304 "korean", "ko",
4305 "norwegian", "no",
4306 "polish", "pl",
4307 "russian", "ru",
4308 "slovak", "sk",
4309 "swedish", "sv",
4310 "ukrainian", "uk",
4311 "chinese_china", "zh_CN",
4312 "chinese_taiwan", "zh_TW",
4313 NULL};
4314
4315 for (i = 0; mtable[i] != NULL; i += 2)
4316 if (STRNICMP(mtable[i], name, STRLEN(mtable[i])) == 0)
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01004317 return (char_u *)mtable[i + 1];
Bram Moolenaar071d4272004-06-13 20:20:40 +00004318 return name;
4319}
4320#endif
4321
4322#if defined(FEAT_MULTI_LANG) || defined(PROTO)
4323/*
Bram Moolenaar389ab712018-11-05 20:25:52 +01004324 * Return TRUE when "lang" starts with a valid language name.
4325 * Rejects NULL, empty string, "C", "C.UTF-8" and others.
4326 */
4327 static int
4328is_valid_mess_lang(char_u *lang)
4329{
4330 return lang != NULL && ASCII_ISALPHA(lang[0]) && ASCII_ISALPHA(lang[1]);
4331}
4332
4333/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00004334 * Obtain the current messages language. Used to set the default for
4335 * 'helplang'. May return NULL or an empty string.
4336 */
4337 char_u *
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01004338get_mess_lang(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004339{
4340 char_u *p;
4341
Bram Moolenaar8765a4a2010-07-27 22:41:43 +02004342# ifdef HAVE_GET_LOCALE_VAL
Bram Moolenaar071d4272004-06-13 20:20:40 +00004343# if defined(LC_MESSAGES)
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01004344 p = get_locale_val(LC_MESSAGES);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004345# else
4346 /* This is necessary for Win32, where LC_MESSAGES is not defined and $LANG
Bram Moolenaar61660ea2006-04-07 21:40:07 +00004347 * may be set to the LCID number. LC_COLLATE is the best guess, LC_TIME
4348 * and LC_MONETARY may be set differently for a Japanese working in the
4349 * US. */
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01004350 p = get_locale_val(LC_COLLATE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004351# endif
4352# else
4353 p = mch_getenv((char_u *)"LC_ALL");
Bram Moolenaar389ab712018-11-05 20:25:52 +01004354 if (!is_valid_mess_lang(p))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004355 {
4356 p = mch_getenv((char_u *)"LC_MESSAGES");
Bram Moolenaar389ab712018-11-05 20:25:52 +01004357 if (!is_valid_mess_lang(p))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004358 p = mch_getenv((char_u *)"LANG");
4359 }
4360# endif
Bram Moolenaar4f974752019-02-17 17:44:42 +01004361# ifdef MSWIN
Bram Moolenaar071d4272004-06-13 20:20:40 +00004362 p = gettext_lang(p);
4363# endif
Bram Moolenaar389ab712018-11-05 20:25:52 +01004364 return is_valid_mess_lang(p) ? p : NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004365}
4366#endif
4367
Bram Moolenaardef9e822004-12-31 20:58:58 +00004368/* Complicated #if; matches with where get_mess_env() is used below. */
4369#if (defined(FEAT_EVAL) && !((defined(HAVE_LOCALE_H) || defined(X_LOCALE)) \
4370 && defined(LC_MESSAGES))) \
4371 || ((defined(HAVE_LOCALE_H) || defined(X_LOCALE)) \
Bram Moolenaardef9e822004-12-31 20:58:58 +00004372 && !defined(LC_MESSAGES))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004373/*
4374 * Get the language used for messages from the environment.
4375 */
4376 static char_u *
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01004377get_mess_env(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004378{
4379 char_u *p;
4380
4381 p = mch_getenv((char_u *)"LC_ALL");
4382 if (p == NULL || *p == NUL)
4383 {
4384 p = mch_getenv((char_u *)"LC_MESSAGES");
4385 if (p == NULL || *p == NUL)
4386 {
4387 p = mch_getenv((char_u *)"LANG");
4388 if (p != NULL && VIM_ISDIGIT(*p))
4389 p = NULL; /* ignore something like "1043" */
Bram Moolenaar8765a4a2010-07-27 22:41:43 +02004390# ifdef HAVE_GET_LOCALE_VAL
Bram Moolenaar071d4272004-06-13 20:20:40 +00004391 if (p == NULL || *p == NUL)
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01004392 p = get_locale_val(LC_CTYPE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004393# endif
4394 }
4395 }
4396 return p;
4397}
4398#endif
4399
4400#if defined(FEAT_EVAL) || defined(PROTO)
4401
4402/*
4403 * Set the "v:lang" variable according to the current locale setting.
4404 * Also do "v:lc_time"and "v:ctype".
4405 */
4406 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01004407set_lang_var(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004408{
4409 char_u *loc;
4410
Bram Moolenaar8765a4a2010-07-27 22:41:43 +02004411# ifdef HAVE_GET_LOCALE_VAL
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01004412 loc = get_locale_val(LC_CTYPE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004413# else
4414 /* setlocale() not supported: use the default value */
4415 loc = (char_u *)"C";
4416# endif
4417 set_vim_var_string(VV_CTYPE, loc, -1);
4418
4419 /* When LC_MESSAGES isn't defined use the value from $LC_MESSAGES, fall
4420 * back to LC_CTYPE if it's empty. */
Bram Moolenaar8765a4a2010-07-27 22:41:43 +02004421# if defined(HAVE_GET_LOCALE_VAL) && defined(LC_MESSAGES)
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01004422 loc = get_locale_val(LC_MESSAGES);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004423# else
4424 loc = get_mess_env();
4425# endif
4426 set_vim_var_string(VV_LANG, loc, -1);
4427
Bram Moolenaar8765a4a2010-07-27 22:41:43 +02004428# ifdef HAVE_GET_LOCALE_VAL
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01004429 loc = get_locale_val(LC_TIME);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004430# endif
4431 set_vim_var_string(VV_LC_TIME, loc, -1);
4432}
4433#endif
4434
Bram Moolenaarfc3abf42019-01-24 15:54:21 +01004435#if defined(HAVE_LOCALE_H) || defined(X_LOCALE) \
Bram Moolenaar071d4272004-06-13 20:20:40 +00004436/*
4437 * ":language": Set the language (locale).
4438 */
4439 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01004440ex_language(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004441{
4442 char *loc;
4443 char_u *p;
4444 char_u *name;
4445 int what = LC_ALL;
4446 char *whatstr = "";
4447#ifdef LC_MESSAGES
4448# define VIM_LC_MESSAGES LC_MESSAGES
4449#else
4450# define VIM_LC_MESSAGES 6789
4451#endif
4452
4453 name = eap->arg;
4454
4455 /* Check for "messages {name}", "ctype {name}" or "time {name}" argument.
4456 * Allow abbreviation, but require at least 3 characters to avoid
4457 * confusion with a two letter language name "me" or "ct". */
4458 p = skiptowhite(eap->arg);
Bram Moolenaar1c465442017-03-12 20:10:05 +01004459 if ((*p == NUL || VIM_ISWHITE(*p)) && p - eap->arg >= 3)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004460 {
4461 if (STRNICMP(eap->arg, "messages", p - eap->arg) == 0)
4462 {
4463 what = VIM_LC_MESSAGES;
4464 name = skipwhite(p);
4465 whatstr = "messages ";
4466 }
4467 else if (STRNICMP(eap->arg, "ctype", p - eap->arg) == 0)
4468 {
4469 what = LC_CTYPE;
4470 name = skipwhite(p);
4471 whatstr = "ctype ";
4472 }
4473 else if (STRNICMP(eap->arg, "time", p - eap->arg) == 0)
4474 {
4475 what = LC_TIME;
4476 name = skipwhite(p);
4477 whatstr = "time ";
4478 }
4479 }
4480
4481 if (*name == NUL)
4482 {
4483#ifndef LC_MESSAGES
4484 if (what == VIM_LC_MESSAGES)
4485 p = get_mess_env();
4486 else
4487#endif
4488 p = (char_u *)setlocale(what, NULL);
4489 if (p == NULL || *p == NUL)
4490 p = (char_u *)"Unknown";
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01004491 smsg(_("Current %slanguage: \"%s\""), whatstr, p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004492 }
4493 else
4494 {
4495#ifndef LC_MESSAGES
4496 if (what == VIM_LC_MESSAGES)
4497 loc = "";
4498 else
4499#endif
Bram Moolenaar8c8de832008-06-24 22:58:06 +00004500 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00004501 loc = setlocale(what, (char *)name);
Bram Moolenaar8c8de832008-06-24 22:58:06 +00004502#if defined(FEAT_FLOAT) && defined(LC_NUMERIC)
4503 /* Make sure strtod() uses a decimal point, not a comma. */
4504 setlocale(LC_NUMERIC, "C");
4505#endif
4506 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004507 if (loc == NULL)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01004508 semsg(_("E197: Cannot set language to \"%s\""), name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004509 else
4510 {
4511#ifdef HAVE_NL_MSG_CAT_CNTR
4512 /* Need to do this for GNU gettext, otherwise cached translations
4513 * will be used again. */
4514 extern int _nl_msg_cat_cntr;
4515
4516 ++_nl_msg_cat_cntr;
4517#endif
Bram Moolenaarb8017e72007-05-10 18:59:07 +00004518 /* Reset $LC_ALL, otherwise it would overrule everything. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004519 vim_setenv((char_u *)"LC_ALL", (char_u *)"");
4520
4521 if (what != LC_TIME)
4522 {
4523 /* Tell gettext() what to translate to. It apparently doesn't
4524 * use the currently effective locale. Also do this when
4525 * FEAT_GETTEXT isn't defined, so that shell commands use this
4526 * value. */
4527 if (what == LC_ALL)
Bram Moolenaar482aaeb2005-09-29 18:26:07 +00004528 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00004529 vim_setenv((char_u *)"LANG", name);
Bram Moolenaare3a0b532013-06-28 20:36:30 +02004530
4531 /* Clear $LANGUAGE because GNU gettext uses it. */
4532 vim_setenv((char_u *)"LANGUAGE", (char_u *)"");
Bram Moolenaar4f974752019-02-17 17:44:42 +01004533# ifdef MSWIN
Bram Moolenaar482aaeb2005-09-29 18:26:07 +00004534 /* Apparently MS-Windows printf() may cause a crash when
4535 * we give it 8-bit text while it's expecting text in the
4536 * current locale. This call avoids that. */
4537 setlocale(LC_CTYPE, "C");
4538# endif
4539 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004540 if (what != LC_CTYPE)
4541 {
4542 char_u *mname;
Bram Moolenaar4f974752019-02-17 17:44:42 +01004543#ifdef MSWIN
Bram Moolenaar071d4272004-06-13 20:20:40 +00004544 mname = gettext_lang(name);
4545#else
4546 mname = name;
4547#endif
4548 vim_setenv((char_u *)"LC_MESSAGES", mname);
4549#ifdef FEAT_MULTI_LANG
4550 set_helplang_default(mname);
4551#endif
4552 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004553 }
4554
4555# ifdef FEAT_EVAL
4556 /* Set v:lang, v:lc_time and v:ctype to the final result. */
4557 set_lang_var();
4558# endif
Bram Moolenaar6cc00c72011-10-20 21:41:09 +02004559# ifdef FEAT_TITLE
4560 maketitle();
4561# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004562 }
4563 }
4564}
4565
4566# if defined(FEAT_CMDL_COMPL) || defined(PROTO)
Bram Moolenaar9b486ca2011-05-19 18:26:40 +02004567
4568static char_u **locales = NULL; /* Array of all available locales */
Bram Moolenaarb8f7bd62017-01-12 20:28:25 +01004569
Bram Moolenaar4f974752019-02-17 17:44:42 +01004570# ifndef MSWIN
Bram Moolenaar9b486ca2011-05-19 18:26:40 +02004571static int did_init_locales = FALSE;
4572
Bram Moolenaar9b486ca2011-05-19 18:26:40 +02004573/* Return an array of strings for all available locales + NULL for the
4574 * last element. Return NULL in case of error. */
4575 static char_u **
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01004576find_locales(void)
Bram Moolenaar9b486ca2011-05-19 18:26:40 +02004577{
4578 garray_T locales_ga;
4579 char_u *loc;
4580
4581 /* Find all available locales by running command "locale -a". If this
4582 * doesn't work we won't have completion. */
4583 char_u *locale_a = get_cmd_output((char_u *)"locale -a",
Bram Moolenaar39c29ed2014-04-05 19:44:40 +02004584 NULL, SHELL_SILENT, NULL);
Bram Moolenaar9b486ca2011-05-19 18:26:40 +02004585 if (locale_a == NULL)
4586 return NULL;
4587 ga_init2(&locales_ga, sizeof(char_u *), 20);
4588
4589 /* Transform locale_a string where each locale is separated by "\n"
4590 * into an array of locale strings. */
4591 loc = (char_u *)strtok((char *)locale_a, "\n");
4592
4593 while (loc != NULL)
4594 {
4595 if (ga_grow(&locales_ga, 1) == FAIL)
4596 break;
4597 loc = vim_strsave(loc);
4598 if (loc == NULL)
4599 break;
4600
4601 ((char_u **)locales_ga.ga_data)[locales_ga.ga_len++] = loc;
4602 loc = (char_u *)strtok(NULL, "\n");
4603 }
4604 vim_free(locale_a);
4605 if (ga_grow(&locales_ga, 1) == FAIL)
4606 {
4607 ga_clear(&locales_ga);
4608 return NULL;
4609 }
4610 ((char_u **)locales_ga.ga_data)[locales_ga.ga_len] = NULL;
4611 return (char_u **)locales_ga.ga_data;
4612}
Bram Moolenaarb8f7bd62017-01-12 20:28:25 +01004613# endif
4614
4615/*
4616 * Lazy initialization of all available locales.
4617 */
4618 static void
4619init_locales(void)
4620{
Bram Moolenaar4f974752019-02-17 17:44:42 +01004621# ifndef MSWIN
Bram Moolenaarb8f7bd62017-01-12 20:28:25 +01004622 if (!did_init_locales)
4623 {
4624 did_init_locales = TRUE;
4625 locales = find_locales();
4626 }
4627# endif
4628}
Bram Moolenaar9b486ca2011-05-19 18:26:40 +02004629
4630# if defined(EXITFREE) || defined(PROTO)
4631 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01004632free_locales(void)
Bram Moolenaar9b486ca2011-05-19 18:26:40 +02004633{
4634 int i;
4635 if (locales != NULL)
4636 {
4637 for (i = 0; locales[i] != NULL; i++)
4638 vim_free(locales[i]);
Bram Moolenaard23a8232018-02-10 18:45:26 +01004639 VIM_CLEAR(locales);
Bram Moolenaar9b486ca2011-05-19 18:26:40 +02004640 }
4641}
4642# endif
4643
Bram Moolenaar071d4272004-06-13 20:20:40 +00004644/*
4645 * Function given to ExpandGeneric() to obtain the possible arguments of the
4646 * ":language" command.
4647 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004648 char_u *
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01004649get_lang_arg(expand_T *xp UNUSED, int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004650{
4651 if (idx == 0)
4652 return (char_u *)"messages";
4653 if (idx == 1)
4654 return (char_u *)"ctype";
4655 if (idx == 2)
4656 return (char_u *)"time";
Bram Moolenaar9b486ca2011-05-19 18:26:40 +02004657
4658 init_locales();
4659 if (locales == NULL)
4660 return NULL;
4661 return locales[idx - 3];
4662}
4663
4664/*
4665 * Function given to ExpandGeneric() to obtain the available locales.
4666 */
4667 char_u *
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01004668get_locales(expand_T *xp UNUSED, int idx)
Bram Moolenaar9b486ca2011-05-19 18:26:40 +02004669{
4670 init_locales();
4671 if (locales == NULL)
4672 return NULL;
4673 return locales[idx];
Bram Moolenaar071d4272004-06-13 20:20:40 +00004674}
4675# endif
4676
4677#endif