blob: 7cce61181b33dbdf6969bc80fbafbc0bcbadf903 [file] [log] [blame]
Bram Moolenaar8b5866d2020-09-05 15:48:51 +02001/* vi:set ts=8 sts=4 sw=4 noet:
2 *
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 */
8
9/*
10 * Implements starting jobs and controlling them.
11 */
12
13#include "vim.h"
14
15#if defined(FEAT_JOB_CHANNEL) || defined(PROTO)
16
17#define FOR_ALL_JOBS(job) \
18 for ((job) = first_job; (job) != NULL; (job) = (job)->jv_next)
19
20 static int
21handle_mode(typval_T *item, jobopt_T *opt, ch_mode_T *modep, int jo)
22{
23 char_u *val = tv_get_string(item);
24
25 opt->jo_set |= jo;
26 if (STRCMP(val, "nl") == 0)
Bram Moolenaarac4174e2022-05-07 16:38:24 +010027 *modep = CH_MODE_NL;
Bram Moolenaar8b5866d2020-09-05 15:48:51 +020028 else if (STRCMP(val, "raw") == 0)
Bram Moolenaarac4174e2022-05-07 16:38:24 +010029 *modep = CH_MODE_RAW;
Bram Moolenaar8b5866d2020-09-05 15:48:51 +020030 else if (STRCMP(val, "js") == 0)
Bram Moolenaarac4174e2022-05-07 16:38:24 +010031 *modep = CH_MODE_JS;
Bram Moolenaar8b5866d2020-09-05 15:48:51 +020032 else if (STRCMP(val, "json") == 0)
Bram Moolenaarac4174e2022-05-07 16:38:24 +010033 *modep = CH_MODE_JSON;
Yegappan Lakshmanan9247a222022-03-30 10:16:05 +010034 else if (STRCMP(val, "lsp") == 0)
Bram Moolenaarac4174e2022-05-07 16:38:24 +010035 *modep = CH_MODE_LSP;
Bram Moolenaar8b5866d2020-09-05 15:48:51 +020036 else
37 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +000038 semsg(_(e_invalid_argument_str), val);
Bram Moolenaar8b5866d2020-09-05 15:48:51 +020039 return FAIL;
40 }
41 return OK;
42}
43
44 static int
45handle_io(typval_T *item, ch_part_T part, jobopt_T *opt)
46{
47 char_u *val = tv_get_string(item);
48
49 opt->jo_set |= JO_OUT_IO << (part - PART_OUT);
50 if (STRCMP(val, "null") == 0)
51 opt->jo_io[part] = JIO_NULL;
52 else if (STRCMP(val, "pipe") == 0)
53 opt->jo_io[part] = JIO_PIPE;
54 else if (STRCMP(val, "file") == 0)
55 opt->jo_io[part] = JIO_FILE;
56 else if (STRCMP(val, "buffer") == 0)
57 opt->jo_io[part] = JIO_BUFFER;
58 else if (STRCMP(val, "out") == 0 && part == PART_ERR)
59 opt->jo_io[part] = JIO_OUT;
60 else
61 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +000062 semsg(_(e_invalid_argument_str), val);
Bram Moolenaar8b5866d2020-09-05 15:48:51 +020063 return FAIL;
64 }
65 return OK;
66}
67
68/*
69 * Clear a jobopt_T before using it.
70 */
71 void
72clear_job_options(jobopt_T *opt)
73{
74 CLEAR_POINTER(opt);
75}
76
77/*
78 * Free any members of a jobopt_T.
79 */
80 void
81free_job_options(jobopt_T *opt)
82{
83 if (opt->jo_callback.cb_partial != NULL)
84 partial_unref(opt->jo_callback.cb_partial);
85 else if (opt->jo_callback.cb_name != NULL)
86 func_unref(opt->jo_callback.cb_name);
87 if (opt->jo_out_cb.cb_partial != NULL)
88 partial_unref(opt->jo_out_cb.cb_partial);
89 else if (opt->jo_out_cb.cb_name != NULL)
90 func_unref(opt->jo_out_cb.cb_name);
91 if (opt->jo_err_cb.cb_partial != NULL)
92 partial_unref(opt->jo_err_cb.cb_partial);
93 else if (opt->jo_err_cb.cb_name != NULL)
94 func_unref(opt->jo_err_cb.cb_name);
95 if (opt->jo_close_cb.cb_partial != NULL)
96 partial_unref(opt->jo_close_cb.cb_partial);
97 else if (opt->jo_close_cb.cb_name != NULL)
98 func_unref(opt->jo_close_cb.cb_name);
99 if (opt->jo_exit_cb.cb_partial != NULL)
100 partial_unref(opt->jo_exit_cb.cb_partial);
101 else if (opt->jo_exit_cb.cb_name != NULL)
102 func_unref(opt->jo_exit_cb.cb_name);
103 if (opt->jo_env != NULL)
104 dict_unref(opt->jo_env);
105}
106
107/*
108 * Get the PART_ number from the first character of an option name.
109 */
110 static int
111part_from_char(int c)
112{
113 return c == 'i' ? PART_IN : c == 'o' ? PART_OUT: PART_ERR;
114}
115
116/*
117 * Get the option entries from the dict in "tv", parse them and put the result
118 * in "opt".
119 * Only accept JO_ options in "supported" and JO2_ options in "supported2".
120 * If an option value is invalid return FAIL.
121 */
122 int
123get_job_options(typval_T *tv, jobopt_T *opt, int supported, int supported2)
124{
125 typval_T *item;
126 char_u *val;
127 dict_T *dict;
128 int todo;
129 hashitem_T *hi;
130 ch_part_T part;
131
132 if (tv->v_type == VAR_UNKNOWN)
133 return OK;
134 if (tv->v_type != VAR_DICT)
135 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +0000136 emsg(_(e_dictionary_required));
Bram Moolenaar8b5866d2020-09-05 15:48:51 +0200137 return FAIL;
138 }
139 dict = tv->vval.v_dict;
140 if (dict == NULL)
141 return OK;
142
143 todo = (int)dict->dv_hashtab.ht_used;
144 for (hi = dict->dv_hashtab.ht_array; todo > 0; ++hi)
145 if (!HASHITEM_EMPTY(hi))
146 {
147 item = &dict_lookup(hi)->di_tv;
148
149 if (STRCMP(hi->hi_key, "mode") == 0)
150 {
151 if (!(supported & JO_MODE))
152 break;
153 if (handle_mode(item, opt, &opt->jo_mode, JO_MODE) == FAIL)
154 return FAIL;
155 }
156 else if (STRCMP(hi->hi_key, "in_mode") == 0)
157 {
158 if (!(supported & JO_IN_MODE))
159 break;
160 if (handle_mode(item, opt, &opt->jo_in_mode, JO_IN_MODE)
161 == FAIL)
162 return FAIL;
163 }
164 else if (STRCMP(hi->hi_key, "out_mode") == 0)
165 {
166 if (!(supported & JO_OUT_MODE))
167 break;
168 if (handle_mode(item, opt, &opt->jo_out_mode, JO_OUT_MODE)
169 == FAIL)
170 return FAIL;
171 }
172 else if (STRCMP(hi->hi_key, "err_mode") == 0)
173 {
174 if (!(supported & JO_ERR_MODE))
175 break;
176 if (handle_mode(item, opt, &opt->jo_err_mode, JO_ERR_MODE)
177 == FAIL)
178 return FAIL;
179 }
180 else if (STRCMP(hi->hi_key, "noblock") == 0)
181 {
182 if (!(supported & JO_MODE))
183 break;
184 opt->jo_noblock = tv_get_bool(item);
185 }
186 else if (STRCMP(hi->hi_key, "in_io") == 0
187 || STRCMP(hi->hi_key, "out_io") == 0
188 || STRCMP(hi->hi_key, "err_io") == 0)
189 {
190 if (!(supported & JO_OUT_IO))
191 break;
192 if (handle_io(item, part_from_char(*hi->hi_key), opt) == FAIL)
193 return FAIL;
194 }
195 else if (STRCMP(hi->hi_key, "in_name") == 0
196 || STRCMP(hi->hi_key, "out_name") == 0
197 || STRCMP(hi->hi_key, "err_name") == 0)
198 {
199 part = part_from_char(*hi->hi_key);
200
201 if (!(supported & JO_OUT_IO))
202 break;
203 opt->jo_set |= JO_OUT_NAME << (part - PART_OUT);
204 opt->jo_io_name[part] = tv_get_string_buf_chk(item,
205 opt->jo_io_name_buf[part]);
206 }
207 else if (STRCMP(hi->hi_key, "pty") == 0)
208 {
209 if (!(supported & JO_MODE))
210 break;
211 opt->jo_pty = tv_get_bool(item);
212 }
213 else if (STRCMP(hi->hi_key, "in_buf") == 0
214 || STRCMP(hi->hi_key, "out_buf") == 0
215 || STRCMP(hi->hi_key, "err_buf") == 0)
216 {
217 part = part_from_char(*hi->hi_key);
218
219 if (!(supported & JO_OUT_IO))
220 break;
221 opt->jo_set |= JO_OUT_BUF << (part - PART_OUT);
222 opt->jo_io_buf[part] = tv_get_number(item);
223 if (opt->jo_io_buf[part] <= 0)
224 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +0000225 semsg(_(e_invalid_value_for_argument_str_str), hi->hi_key, tv_get_string(item));
Bram Moolenaar8b5866d2020-09-05 15:48:51 +0200226 return FAIL;
227 }
228 if (buflist_findnr(opt->jo_io_buf[part]) == NULL)
229 {
Bram Moolenaar40bcec12021-12-05 22:19:27 +0000230 semsg(_(e_buffer_nr_does_not_exist),
231 (long)opt->jo_io_buf[part]);
Bram Moolenaar8b5866d2020-09-05 15:48:51 +0200232 return FAIL;
233 }
234 }
235 else if (STRCMP(hi->hi_key, "out_modifiable") == 0
236 || STRCMP(hi->hi_key, "err_modifiable") == 0)
237 {
238 part = part_from_char(*hi->hi_key);
239
240 if (!(supported & JO_OUT_IO))
241 break;
242 opt->jo_set |= JO_OUT_MODIFIABLE << (part - PART_OUT);
243 opt->jo_modifiable[part] = tv_get_bool(item);
244 }
245 else if (STRCMP(hi->hi_key, "out_msg") == 0
246 || STRCMP(hi->hi_key, "err_msg") == 0)
247 {
248 part = part_from_char(*hi->hi_key);
249
250 if (!(supported & JO_OUT_IO))
251 break;
252 opt->jo_set2 |= JO2_OUT_MSG << (part - PART_OUT);
253 opt->jo_message[part] = tv_get_bool(item);
254 }
255 else if (STRCMP(hi->hi_key, "in_top") == 0
256 || STRCMP(hi->hi_key, "in_bot") == 0)
257 {
258 linenr_T *lp;
259
260 if (!(supported & JO_OUT_IO))
261 break;
262 if (hi->hi_key[3] == 't')
263 {
264 lp = &opt->jo_in_top;
265 opt->jo_set |= JO_IN_TOP;
266 }
267 else
268 {
269 lp = &opt->jo_in_bot;
270 opt->jo_set |= JO_IN_BOT;
271 }
272 *lp = tv_get_number(item);
273 if (*lp < 0)
274 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +0000275 semsg(_(e_invalid_value_for_argument_str_str), hi->hi_key, tv_get_string(item));
Bram Moolenaar8b5866d2020-09-05 15:48:51 +0200276 return FAIL;
277 }
278 }
279 else if (STRCMP(hi->hi_key, "channel") == 0)
280 {
281 if (!(supported & JO_OUT_IO))
282 break;
283 opt->jo_set |= JO_CHANNEL;
284 if (item->v_type != VAR_CHANNEL)
285 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +0000286 semsg(_(e_invalid_value_for_argument_str), "channel");
Bram Moolenaar8b5866d2020-09-05 15:48:51 +0200287 return FAIL;
288 }
289 opt->jo_channel = item->vval.v_channel;
290 }
291 else if (STRCMP(hi->hi_key, "callback") == 0)
292 {
293 if (!(supported & JO_CALLBACK))
294 break;
295 opt->jo_set |= JO_CALLBACK;
296 opt->jo_callback = get_callback(item);
297 if (opt->jo_callback.cb_name == NULL)
298 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +0000299 semsg(_(e_invalid_value_for_argument_str), "callback");
Bram Moolenaar8b5866d2020-09-05 15:48:51 +0200300 return FAIL;
301 }
302 }
303 else if (STRCMP(hi->hi_key, "out_cb") == 0)
304 {
305 if (!(supported & JO_OUT_CALLBACK))
306 break;
307 opt->jo_set |= JO_OUT_CALLBACK;
308 opt->jo_out_cb = get_callback(item);
309 if (opt->jo_out_cb.cb_name == NULL)
310 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +0000311 semsg(_(e_invalid_value_for_argument_str), "out_cb");
Bram Moolenaar8b5866d2020-09-05 15:48:51 +0200312 return FAIL;
313 }
314 }
315 else if (STRCMP(hi->hi_key, "err_cb") == 0)
316 {
317 if (!(supported & JO_ERR_CALLBACK))
318 break;
319 opt->jo_set |= JO_ERR_CALLBACK;
320 opt->jo_err_cb = get_callback(item);
321 if (opt->jo_err_cb.cb_name == NULL)
322 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +0000323 semsg(_(e_invalid_value_for_argument_str), "err_cb");
Bram Moolenaar8b5866d2020-09-05 15:48:51 +0200324 return FAIL;
325 }
326 }
327 else if (STRCMP(hi->hi_key, "close_cb") == 0)
328 {
329 if (!(supported & JO_CLOSE_CALLBACK))
330 break;
331 opt->jo_set |= JO_CLOSE_CALLBACK;
332 opt->jo_close_cb = get_callback(item);
333 if (opt->jo_close_cb.cb_name == NULL)
334 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +0000335 semsg(_(e_invalid_value_for_argument_str), "close_cb");
Bram Moolenaar8b5866d2020-09-05 15:48:51 +0200336 return FAIL;
337 }
338 }
339 else if (STRCMP(hi->hi_key, "drop") == 0)
340 {
341 int never = FALSE;
342 val = tv_get_string(item);
343
344 if (STRCMP(val, "never") == 0)
345 never = TRUE;
346 else if (STRCMP(val, "auto") != 0)
347 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +0000348 semsg(_(e_invalid_value_for_argument_str_str), "drop", val);
Bram Moolenaar8b5866d2020-09-05 15:48:51 +0200349 return FAIL;
350 }
351 opt->jo_drop_never = never;
352 }
353 else if (STRCMP(hi->hi_key, "exit_cb") == 0)
354 {
355 if (!(supported & JO_EXIT_CB))
356 break;
357 opt->jo_set |= JO_EXIT_CB;
358 opt->jo_exit_cb = get_callback(item);
359 if (opt->jo_exit_cb.cb_name == NULL)
360 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +0000361 semsg(_(e_invalid_value_for_argument_str), "exit_cb");
Bram Moolenaar8b5866d2020-09-05 15:48:51 +0200362 return FAIL;
363 }
364 }
365#ifdef FEAT_TERMINAL
366 else if (STRCMP(hi->hi_key, "term_name") == 0)
367 {
368 if (!(supported2 & JO2_TERM_NAME))
369 break;
370 opt->jo_set2 |= JO2_TERM_NAME;
371 opt->jo_term_name = tv_get_string_buf_chk(item,
372 opt->jo_term_name_buf);
373 if (opt->jo_term_name == NULL)
374 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +0000375 semsg(_(e_invalid_value_for_argument_str), "term_name");
Bram Moolenaar8b5866d2020-09-05 15:48:51 +0200376 return FAIL;
377 }
378 }
379 else if (STRCMP(hi->hi_key, "term_finish") == 0)
380 {
381 if (!(supported2 & JO2_TERM_FINISH))
382 break;
383 val = tv_get_string(item);
384 if (STRCMP(val, "open") != 0 && STRCMP(val, "close") != 0)
385 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +0000386 semsg(_(e_invalid_value_for_argument_str_str), "term_finish", val);
Bram Moolenaar8b5866d2020-09-05 15:48:51 +0200387 return FAIL;
388 }
389 opt->jo_set2 |= JO2_TERM_FINISH;
390 opt->jo_term_finish = *val;
391 }
392 else if (STRCMP(hi->hi_key, "term_opencmd") == 0)
393 {
394 char_u *p;
395
396 if (!(supported2 & JO2_TERM_OPENCMD))
397 break;
398 opt->jo_set2 |= JO2_TERM_OPENCMD;
399 p = opt->jo_term_opencmd = tv_get_string_buf_chk(item,
400 opt->jo_term_opencmd_buf);
401 if (p != NULL)
402 {
403 // Must have %d and no other %.
404 p = vim_strchr(p, '%');
405 if (p != NULL && (p[1] != 'd'
406 || vim_strchr(p + 2, '%') != NULL))
407 p = NULL;
408 }
409 if (p == NULL)
410 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +0000411 semsg(_(e_invalid_value_for_argument_str), "term_opencmd");
Bram Moolenaar8b5866d2020-09-05 15:48:51 +0200412 return FAIL;
413 }
414 }
415 else if (STRCMP(hi->hi_key, "eof_chars") == 0)
416 {
417 if (!(supported2 & JO2_EOF_CHARS))
418 break;
419 opt->jo_set2 |= JO2_EOF_CHARS;
420 opt->jo_eof_chars = tv_get_string_buf_chk(item,
421 opt->jo_eof_chars_buf);
422 if (opt->jo_eof_chars == NULL)
423 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +0000424 semsg(_(e_invalid_value_for_argument_str), "eof_chars");
Bram Moolenaar8b5866d2020-09-05 15:48:51 +0200425 return FAIL;
426 }
427 }
428 else if (STRCMP(hi->hi_key, "term_rows") == 0)
429 {
Bram Moolenaar88137392021-11-12 16:01:15 +0000430 int error = FALSE;
431
Bram Moolenaar8b5866d2020-09-05 15:48:51 +0200432 if (!(supported2 & JO2_TERM_ROWS))
433 break;
434 opt->jo_set2 |= JO2_TERM_ROWS;
Bram Moolenaar88137392021-11-12 16:01:15 +0000435 opt->jo_term_rows = tv_get_number_chk(item, &error);
436 if (error)
437 return FAIL;
Bram Moolenaar5300be62021-11-13 10:27:40 +0000438 if (opt->jo_term_rows < 0 || opt->jo_term_rows > 1000)
439 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +0000440 semsg(_(e_invalid_value_for_argument_str), "term_rows");
Bram Moolenaar5300be62021-11-13 10:27:40 +0000441 return FAIL;
442 }
Bram Moolenaar8b5866d2020-09-05 15:48:51 +0200443 }
444 else if (STRCMP(hi->hi_key, "term_cols") == 0)
445 {
446 if (!(supported2 & JO2_TERM_COLS))
447 break;
448 opt->jo_set2 |= JO2_TERM_COLS;
449 opt->jo_term_cols = tv_get_number(item);
450 }
451 else if (STRCMP(hi->hi_key, "vertical") == 0)
452 {
453 if (!(supported2 & JO2_VERTICAL))
454 break;
455 opt->jo_set2 |= JO2_VERTICAL;
456 opt->jo_vertical = tv_get_bool(item);
457 }
458 else if (STRCMP(hi->hi_key, "curwin") == 0)
459 {
460 if (!(supported2 & JO2_CURWIN))
461 break;
462 opt->jo_set2 |= JO2_CURWIN;
Bram Moolenaarad304702020-09-06 18:22:53 +0200463 opt->jo_curwin = tv_get_bool(item);
Bram Moolenaar8b5866d2020-09-05 15:48:51 +0200464 }
465 else if (STRCMP(hi->hi_key, "bufnr") == 0)
466 {
467 int nr;
468
469 if (!(supported2 & JO2_CURWIN))
470 break;
471 opt->jo_set2 |= JO2_BUFNR;
472 nr = tv_get_number(item);
473 if (nr <= 0)
474 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +0000475 semsg(_(e_invalid_value_for_argument_str_str), hi->hi_key, tv_get_string(item));
Bram Moolenaar8b5866d2020-09-05 15:48:51 +0200476 return FAIL;
477 }
478 opt->jo_bufnr_buf = buflist_findnr(nr);
479 if (opt->jo_bufnr_buf == NULL)
480 {
Bram Moolenaar40bcec12021-12-05 22:19:27 +0000481 semsg(_(e_buffer_nr_does_not_exist), (long)nr);
Bram Moolenaar8b5866d2020-09-05 15:48:51 +0200482 return FAIL;
483 }
484 if (opt->jo_bufnr_buf->b_nwindows == 0
485 || opt->jo_bufnr_buf->b_term == NULL)
486 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +0000487 semsg(_(e_invalid_argument_str), "bufnr");
Bram Moolenaar8b5866d2020-09-05 15:48:51 +0200488 return FAIL;
489 }
490 }
491 else if (STRCMP(hi->hi_key, "hidden") == 0)
492 {
493 if (!(supported2 & JO2_HIDDEN))
494 break;
495 opt->jo_set2 |= JO2_HIDDEN;
496 opt->jo_hidden = tv_get_bool(item);
497 }
498 else if (STRCMP(hi->hi_key, "norestore") == 0)
499 {
500 if (!(supported2 & JO2_NORESTORE))
501 break;
502 opt->jo_set2 |= JO2_NORESTORE;
503 opt->jo_term_norestore = tv_get_bool(item);
504 }
505 else if (STRCMP(hi->hi_key, "term_kill") == 0)
506 {
507 if (!(supported2 & JO2_TERM_KILL))
508 break;
509 opt->jo_set2 |= JO2_TERM_KILL;
510 opt->jo_term_kill = tv_get_string_buf_chk(item,
511 opt->jo_term_kill_buf);
512 if (opt->jo_term_kill == NULL)
513 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +0000514 semsg(_(e_invalid_value_for_argument_str), "term_kill");
Bram Moolenaar8b5866d2020-09-05 15:48:51 +0200515 return FAIL;
516 }
517 }
518 else if (STRCMP(hi->hi_key, "tty_type") == 0)
519 {
520 char_u *p;
521
522 if (!(supported2 & JO2_TTY_TYPE))
523 break;
524 opt->jo_set2 |= JO2_TTY_TYPE;
525 p = tv_get_string_chk(item);
526 if (p == NULL)
527 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +0000528 semsg(_(e_invalid_value_for_argument_str), "tty_type");
Bram Moolenaar8b5866d2020-09-05 15:48:51 +0200529 return FAIL;
530 }
531 // Allow empty string, "winpty", "conpty".
532 if (!(*p == NUL || STRCMP(p, "winpty") == 0
Bram Moolenaar6ed545e2022-05-09 20:09:23 +0100533 || STRCMP(p, "conpty") == 0))
Bram Moolenaar8b5866d2020-09-05 15:48:51 +0200534 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +0000535 semsg(_(e_invalid_value_for_argument_str), "tty_type");
Bram Moolenaar8b5866d2020-09-05 15:48:51 +0200536 return FAIL;
537 }
538 opt->jo_tty_type = p[0];
539 }
540# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
541 else if (STRCMP(hi->hi_key, "ansi_colors") == 0)
542 {
543 int n = 0;
544 listitem_T *li;
545 long_u rgb[16];
546
547 if (!(supported2 & JO2_ANSI_COLORS))
548 break;
549
550 if (item == NULL || item->v_type != VAR_LIST
LemonBoyb2b3acb2022-05-20 10:10:34 +0100551 || item->vval.v_list == NULL
552 || item->vval.v_list->lv_first == &range_list_item)
Bram Moolenaar8b5866d2020-09-05 15:48:51 +0200553 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +0000554 semsg(_(e_invalid_value_for_argument_str), "ansi_colors");
Bram Moolenaar8b5866d2020-09-05 15:48:51 +0200555 return FAIL;
556 }
557
Bram Moolenaar8b5866d2020-09-05 15:48:51 +0200558 li = item->vval.v_list->lv_first;
559 for (; li != NULL && n < 16; li = li->li_next, n++)
560 {
561 char_u *color_name;
562 guicolor_T guicolor;
563 int called_emsg_before = called_emsg;
564
565 color_name = tv_get_string_chk(&li->li_tv);
566 if (color_name == NULL)
567 return FAIL;
568
569 guicolor = GUI_GET_COLOR(color_name);
570 if (guicolor == INVALCOLOR)
571 {
572 if (called_emsg_before == called_emsg)
573 // may not get the error if the GUI didn't start
Drew Vogele30d1022021-10-24 20:35:07 +0100574 semsg(_(e_cannot_allocate_color_str), color_name);
Bram Moolenaar8b5866d2020-09-05 15:48:51 +0200575 return FAIL;
576 }
577
578 rgb[n] = GUI_MCH_GET_RGB(guicolor);
579 }
580
581 if (n != 16 || li != NULL)
582 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +0000583 semsg(_(e_invalid_value_for_argument_str), "ansi_colors");
Bram Moolenaar8b5866d2020-09-05 15:48:51 +0200584 return FAIL;
585 }
586
587 opt->jo_set2 |= JO2_ANSI_COLORS;
588 memcpy(opt->jo_ansi_colors, rgb, sizeof(rgb));
589 }
590# endif
591 else if (STRCMP(hi->hi_key, "term_highlight") == 0)
592 {
593 char_u *p;
594
595 if (!(supported2 & JO2_TERM_HIGHLIGHT))
596 break;
597 opt->jo_set2 |= JO2_TERM_HIGHLIGHT;
598 p = tv_get_string_buf_chk(item, opt->jo_term_highlight_buf);
599 if (p == NULL || *p == NUL)
600 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +0000601 semsg(_(e_invalid_value_for_argument_str), "term_highlight");
Bram Moolenaar8b5866d2020-09-05 15:48:51 +0200602 return FAIL;
603 }
604 opt->jo_term_highlight = p;
605 }
606 else if (STRCMP(hi->hi_key, "term_api") == 0)
607 {
608 if (!(supported2 & JO2_TERM_API))
609 break;
610 opt->jo_set2 |= JO2_TERM_API;
611 opt->jo_term_api = tv_get_string_buf_chk(item,
612 opt->jo_term_api_buf);
613 if (opt->jo_term_api == NULL)
614 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +0000615 semsg(_(e_invalid_value_for_argument_str), "term_api");
Bram Moolenaar8b5866d2020-09-05 15:48:51 +0200616 return FAIL;
617 }
618 }
619#endif
620 else if (STRCMP(hi->hi_key, "env") == 0)
621 {
622 if (!(supported2 & JO2_ENV))
623 break;
624 if (item->v_type != VAR_DICT)
625 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +0000626 semsg(_(e_invalid_value_for_argument_str), "env");
Bram Moolenaar8b5866d2020-09-05 15:48:51 +0200627 return FAIL;
628 }
629 opt->jo_set2 |= JO2_ENV;
630 opt->jo_env = item->vval.v_dict;
631 if (opt->jo_env != NULL)
632 ++opt->jo_env->dv_refcount;
633 }
634 else if (STRCMP(hi->hi_key, "cwd") == 0)
635 {
636 if (!(supported2 & JO2_CWD))
637 break;
638 opt->jo_cwd = tv_get_string_buf_chk(item, opt->jo_cwd_buf);
639 if (opt->jo_cwd == NULL || !mch_isdir(opt->jo_cwd)
640#ifndef MSWIN // Win32 directories don't have the concept of "executable"
641 || mch_access((char *)opt->jo_cwd, X_OK) != 0
642#endif
643 )
644 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +0000645 semsg(_(e_invalid_value_for_argument_str), "cwd");
Bram Moolenaar8b5866d2020-09-05 15:48:51 +0200646 return FAIL;
647 }
648 opt->jo_set2 |= JO2_CWD;
649 }
650 else if (STRCMP(hi->hi_key, "waittime") == 0)
651 {
652 if (!(supported & JO_WAITTIME))
653 break;
654 opt->jo_set |= JO_WAITTIME;
655 opt->jo_waittime = tv_get_number(item);
656 }
657 else if (STRCMP(hi->hi_key, "timeout") == 0)
658 {
659 if (!(supported & JO_TIMEOUT))
660 break;
661 opt->jo_set |= JO_TIMEOUT;
662 opt->jo_timeout = tv_get_number(item);
663 }
664 else if (STRCMP(hi->hi_key, "out_timeout") == 0)
665 {
666 if (!(supported & JO_OUT_TIMEOUT))
667 break;
668 opt->jo_set |= JO_OUT_TIMEOUT;
669 opt->jo_out_timeout = tv_get_number(item);
670 }
671 else if (STRCMP(hi->hi_key, "err_timeout") == 0)
672 {
673 if (!(supported & JO_ERR_TIMEOUT))
674 break;
675 opt->jo_set |= JO_ERR_TIMEOUT;
676 opt->jo_err_timeout = tv_get_number(item);
677 }
678 else if (STRCMP(hi->hi_key, "part") == 0)
679 {
680 if (!(supported & JO_PART))
681 break;
682 opt->jo_set |= JO_PART;
683 val = tv_get_string(item);
684 if (STRCMP(val, "err") == 0)
685 opt->jo_part = PART_ERR;
686 else if (STRCMP(val, "out") == 0)
687 opt->jo_part = PART_OUT;
688 else
689 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +0000690 semsg(_(e_invalid_value_for_argument_str_str), "part", val);
Bram Moolenaar8b5866d2020-09-05 15:48:51 +0200691 return FAIL;
692 }
693 }
694 else if (STRCMP(hi->hi_key, "id") == 0)
695 {
696 if (!(supported & JO_ID))
697 break;
698 opt->jo_set |= JO_ID;
699 opt->jo_id = tv_get_number(item);
700 }
701 else if (STRCMP(hi->hi_key, "stoponexit") == 0)
702 {
703 if (!(supported & JO_STOPONEXIT))
704 break;
705 opt->jo_set |= JO_STOPONEXIT;
706 opt->jo_stoponexit = tv_get_string_buf_chk(item,
707 opt->jo_stoponexit_buf);
708 if (opt->jo_stoponexit == NULL)
709 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +0000710 semsg(_(e_invalid_value_for_argument_str), "stoponexit");
Bram Moolenaar8b5866d2020-09-05 15:48:51 +0200711 return FAIL;
712 }
713 }
714 else if (STRCMP(hi->hi_key, "block_write") == 0)
715 {
716 if (!(supported & JO_BLOCK_WRITE))
717 break;
718 opt->jo_set |= JO_BLOCK_WRITE;
719 opt->jo_block_write = tv_get_number(item);
720 }
721 else
722 break;
723 --todo;
724 }
725 if (todo > 0)
726 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +0000727 semsg(_(e_invalid_argument_str), hi->hi_key);
Bram Moolenaar8b5866d2020-09-05 15:48:51 +0200728 return FAIL;
729 }
730
731 return OK;
732}
733
734static job_T *first_job = NULL;
735
736 static void
737job_free_contents(job_T *job)
738{
739 int i;
740
741 ch_log(job->jv_channel, "Freeing job");
742 if (job->jv_channel != NULL)
743 {
744 // The link from the channel to the job doesn't count as a reference,
745 // thus don't decrement the refcount of the job. The reference from
746 // the job to the channel does count the reference, decrement it and
747 // NULL the reference. We don't set ch_job_killed, unreferencing the
748 // job doesn't mean it stops running.
749 job->jv_channel->ch_job = NULL;
750 channel_unref(job->jv_channel);
751 }
752 mch_clear_job(job);
753
754 vim_free(job->jv_tty_in);
755 vim_free(job->jv_tty_out);
756 vim_free(job->jv_stoponexit);
757#ifdef UNIX
758 vim_free(job->jv_termsig);
759#endif
760#ifdef MSWIN
761 vim_free(job->jv_tty_type);
762#endif
763 free_callback(&job->jv_exit_cb);
764 if (job->jv_argv != NULL)
765 {
766 for (i = 0; job->jv_argv[i] != NULL; i++)
767 vim_free(job->jv_argv[i]);
768 vim_free(job->jv_argv);
769 }
770}
771
772/*
773 * Remove "job" from the list of jobs.
774 */
775 static void
776job_unlink(job_T *job)
777{
778 if (job->jv_next != NULL)
779 job->jv_next->jv_prev = job->jv_prev;
780 if (job->jv_prev == NULL)
781 first_job = job->jv_next;
782 else
783 job->jv_prev->jv_next = job->jv_next;
784}
785
786 static void
787job_free_job(job_T *job)
788{
789 job_unlink(job);
790 vim_free(job);
791}
792
793 static void
794job_free(job_T *job)
795{
796 if (!in_free_unref_items)
797 {
798 job_free_contents(job);
799 job_free_job(job);
800 }
801}
802
803static job_T *jobs_to_free = NULL;
804
805/*
806 * Put "job" in a list to be freed later, when it's no longer referenced.
807 */
808 static void
809job_free_later(job_T *job)
810{
811 job_unlink(job);
812 job->jv_next = jobs_to_free;
813 jobs_to_free = job;
814}
815
816 static void
817free_jobs_to_free_later(void)
818{
819 job_T *job;
820
821 while (jobs_to_free != NULL)
822 {
823 job = jobs_to_free;
824 jobs_to_free = job->jv_next;
825 job_free_contents(job);
826 vim_free(job);
827 }
828}
829
830#if defined(EXITFREE) || defined(PROTO)
831 void
832job_free_all(void)
833{
834 while (first_job != NULL)
835 job_free(first_job);
836 free_jobs_to_free_later();
837
838# ifdef FEAT_TERMINAL
839 free_unused_terminals();
840# endif
841}
842#endif
843
844/*
845 * Return TRUE if we need to check if the process of "job" has ended.
846 */
847 static int
848job_need_end_check(job_T *job)
849{
850 return job->jv_status == JOB_STARTED
851 && (job->jv_stoponexit != NULL || job->jv_exit_cb.cb_name != NULL);
852}
853
854/*
855 * Return TRUE if the channel of "job" is still useful.
856 */
857 static int
858job_channel_still_useful(job_T *job)
859{
860 return job->jv_channel != NULL && channel_still_useful(job->jv_channel);
861}
862
863/*
864 * Return TRUE if the channel of "job" is closeable.
865 */
866 static int
867job_channel_can_close(job_T *job)
868{
869 return job->jv_channel != NULL && channel_can_close(job->jv_channel);
870}
871
872/*
873 * Return TRUE if the job should not be freed yet. Do not free the job when
874 * it has not ended yet and there is a "stoponexit" flag, an exit callback
875 * or when the associated channel will do something with the job output.
876 */
877 static int
878job_still_useful(job_T *job)
879{
880 return job_need_end_check(job) || job_channel_still_useful(job);
881}
882
883#if defined(GUI_MAY_FORK) || defined(GUI_MAY_SPAWN) || defined(PROTO)
884/*
885 * Return TRUE when there is any running job that we care about.
886 */
887 int
888job_any_running()
889{
890 job_T *job;
891
892 FOR_ALL_JOBS(job)
893 if (job_still_useful(job))
894 {
895 ch_log(NULL, "GUI not forking because a job is running");
896 return TRUE;
897 }
898 return FALSE;
899}
900#endif
901
Bram Moolenaarf46bf522020-12-09 13:16:13 +0100902// Unix uses argv[] for the command, other systems use a string.
903#if defined(UNIX)
904# define USE_ARGV
905#endif
906
Bram Moolenaar8b5866d2020-09-05 15:48:51 +0200907#if !defined(USE_ARGV) || defined(PROTO)
908/*
909 * Escape one argument for an external command.
910 * Returns the escaped string in allocated memory. NULL when out of memory.
911 */
912 static char_u *
913win32_escape_arg(char_u *arg)
914{
915 int slen, dlen;
916 int escaping = 0;
917 int i;
918 char_u *s, *d;
919 char_u *escaped_arg;
920 int has_spaces = FALSE;
921
922 // First count the number of extra bytes required.
923 slen = (int)STRLEN(arg);
924 dlen = slen;
925 for (s = arg; *s != NUL; MB_PTR_ADV(s))
926 {
927 if (*s == '"' || *s == '\\')
928 ++dlen;
929 if (*s == ' ' || *s == '\t')
930 has_spaces = TRUE;
931 }
932
933 if (has_spaces)
934 dlen += 2;
935
936 if (dlen == slen)
937 return vim_strsave(arg);
938
939 // Allocate memory for the result and fill it.
940 escaped_arg = alloc(dlen + 1);
941 if (escaped_arg == NULL)
942 return NULL;
943 memset(escaped_arg, 0, dlen+1);
944
945 d = escaped_arg;
946
947 if (has_spaces)
948 *d++ = '"';
949
950 for (s = arg; *s != NUL;)
951 {
952 switch (*s)
953 {
954 case '"':
955 for (i = 0; i < escaping; i++)
956 *d++ = '\\';
957 escaping = 0;
958 *d++ = '\\';
959 *d++ = *s++;
960 break;
961 case '\\':
962 escaping++;
963 *d++ = *s++;
964 break;
965 default:
966 escaping = 0;
967 MB_COPY_CHAR(s, d);
968 break;
969 }
970 }
971
972 // add terminating quote and finish with a NUL
973 if (has_spaces)
974 {
975 for (i = 0; i < escaping; i++)
976 *d++ = '\\';
977 *d++ = '"';
978 }
979 *d = NUL;
980
981 return escaped_arg;
982}
983
984/*
985 * Build a command line from a list, taking care of escaping.
986 * The result is put in gap->ga_data.
987 * Returns FAIL when out of memory.
988 */
989 int
990win32_build_cmd(list_T *l, garray_T *gap)
991{
992 listitem_T *li;
993 char_u *s;
994
995 CHECK_LIST_MATERIALIZE(l);
996 FOR_ALL_LIST_ITEMS(l, li)
997 {
998 s = tv_get_string_chk(&li->li_tv);
999 if (s == NULL)
1000 return FAIL;
1001 s = win32_escape_arg(s);
1002 if (s == NULL)
1003 return FAIL;
1004 ga_concat(gap, s);
1005 vim_free(s);
1006 if (li->li_next != NULL)
1007 ga_append(gap, ' ');
1008 }
1009 return OK;
1010}
1011#endif
1012
1013/*
1014 * NOTE: Must call job_cleanup() only once right after the status of "job"
1015 * changed to JOB_ENDED (i.e. after job_status() returned "dead" first or
1016 * mch_detect_ended_job() returned non-NULL).
1017 * If the job is no longer used it will be removed from the list of jobs, and
1018 * deleted a bit later.
1019 */
1020 void
1021job_cleanup(job_T *job)
1022{
1023 if (job->jv_status != JOB_ENDED)
1024 return;
1025
1026 // Ready to cleanup the job.
1027 job->jv_status = JOB_FINISHED;
1028
1029 // When only channel-in is kept open, close explicitly.
1030 if (job->jv_channel != NULL)
1031 ch_close_part(job->jv_channel, PART_IN);
1032
1033 if (job->jv_exit_cb.cb_name != NULL)
1034 {
1035 typval_T argv[3];
1036 typval_T rettv;
1037
1038 // Invoke the exit callback. Make sure the refcount is > 0.
1039 ch_log(job->jv_channel, "Invoking exit callback %s",
1040 job->jv_exit_cb.cb_name);
1041 ++job->jv_refcount;
1042 argv[0].v_type = VAR_JOB;
1043 argv[0].vval.v_job = job;
1044 argv[1].v_type = VAR_NUMBER;
1045 argv[1].vval.v_number = job->jv_exitval;
1046 call_callback(&job->jv_exit_cb, -1, &rettv, 2, argv);
1047 clear_tv(&rettv);
1048 --job->jv_refcount;
1049 channel_need_redraw = TRUE;
1050 }
1051
1052 if (job->jv_channel != NULL && job->jv_channel->ch_anonymous_pipe)
1053 job->jv_channel->ch_killing = TRUE;
1054
1055 // Do not free the job in case the close callback of the associated channel
1056 // isn't invoked yet and may get information by job_info().
1057 if (job->jv_refcount == 0 && !job_channel_still_useful(job))
1058 // The job was already unreferenced and the associated channel was
1059 // detached, now that it ended it can be freed. However, a caller might
1060 // still use it, thus free it a bit later.
1061 job_free_later(job);
1062}
1063
1064/*
1065 * Mark references in jobs that are still useful.
1066 */
1067 int
1068set_ref_in_job(int copyID)
1069{
1070 int abort = FALSE;
1071 job_T *job;
1072 typval_T tv;
1073
1074 for (job = first_job; !abort && job != NULL; job = job->jv_next)
1075 if (job_still_useful(job))
1076 {
1077 tv.v_type = VAR_JOB;
1078 tv.vval.v_job = job;
1079 abort = abort || set_ref_in_item(&tv, copyID, NULL, NULL);
1080 }
1081 return abort;
1082}
1083
1084/*
1085 * Dereference "job". Note that after this "job" may have been freed.
1086 */
1087 void
1088job_unref(job_T *job)
1089{
1090 if (job != NULL && --job->jv_refcount <= 0)
1091 {
1092 // Do not free the job if there is a channel where the close callback
1093 // may get the job info.
1094 if (!job_channel_still_useful(job))
1095 {
1096 // Do not free the job when it has not ended yet and there is a
1097 // "stoponexit" flag or an exit callback.
1098 if (!job_need_end_check(job))
1099 {
1100 job_free(job);
1101 }
1102 else if (job->jv_channel != NULL)
1103 {
1104 // Do remove the link to the channel, otherwise it hangs
1105 // around until Vim exits. See job_free() for refcount.
1106 ch_log(job->jv_channel, "detaching channel from job");
1107 job->jv_channel->ch_job = NULL;
1108 channel_unref(job->jv_channel);
1109 job->jv_channel = NULL;
1110 }
1111 }
1112 }
1113}
1114
1115 int
1116free_unused_jobs_contents(int copyID, int mask)
1117{
1118 int did_free = FALSE;
1119 job_T *job;
1120
1121 FOR_ALL_JOBS(job)
1122 if ((job->jv_copyID & mask) != (copyID & mask)
1123 && !job_still_useful(job))
1124 {
1125 // Free the channel and ordinary items it contains, but don't
1126 // recurse into Lists, Dictionaries etc.
1127 job_free_contents(job);
1128 did_free = TRUE;
1129 }
1130 return did_free;
1131}
1132
1133 void
1134free_unused_jobs(int copyID, int mask)
1135{
1136 job_T *job;
1137 job_T *job_next;
1138
1139 for (job = first_job; job != NULL; job = job_next)
1140 {
1141 job_next = job->jv_next;
1142 if ((job->jv_copyID & mask) != (copyID & mask)
1143 && !job_still_useful(job))
1144 {
1145 // Free the job struct itself.
1146 job_free_job(job);
1147 }
1148 }
1149}
1150
1151/*
1152 * Allocate a job. Sets the refcount to one and sets options default.
1153 */
1154 job_T *
1155job_alloc(void)
1156{
1157 job_T *job;
1158
1159 job = ALLOC_CLEAR_ONE(job_T);
1160 if (job != NULL)
1161 {
1162 job->jv_refcount = 1;
1163 job->jv_stoponexit = vim_strsave((char_u *)"term");
1164
1165 if (first_job != NULL)
1166 {
1167 first_job->jv_prev = job;
1168 job->jv_next = first_job;
1169 }
1170 first_job = job;
1171 }
1172 return job;
1173}
1174
1175 void
1176job_set_options(job_T *job, jobopt_T *opt)
1177{
1178 if (opt->jo_set & JO_STOPONEXIT)
1179 {
1180 vim_free(job->jv_stoponexit);
1181 if (opt->jo_stoponexit == NULL || *opt->jo_stoponexit == NUL)
1182 job->jv_stoponexit = NULL;
1183 else
1184 job->jv_stoponexit = vim_strsave(opt->jo_stoponexit);
1185 }
1186 if (opt->jo_set & JO_EXIT_CB)
1187 {
1188 free_callback(&job->jv_exit_cb);
1189 if (opt->jo_exit_cb.cb_name == NULL || *opt->jo_exit_cb.cb_name == NUL)
1190 {
1191 job->jv_exit_cb.cb_name = NULL;
1192 job->jv_exit_cb.cb_partial = NULL;
1193 }
1194 else
1195 copy_callback(&job->jv_exit_cb, &opt->jo_exit_cb);
1196 }
1197}
1198
1199/*
1200 * Called when Vim is exiting: kill all jobs that have the "stoponexit" flag.
1201 */
1202 void
1203job_stop_on_exit(void)
1204{
1205 job_T *job;
1206
1207 FOR_ALL_JOBS(job)
1208 if (job->jv_status == JOB_STARTED && job->jv_stoponexit != NULL)
1209 mch_signal_job(job, job->jv_stoponexit);
1210}
1211
1212/*
1213 * Return TRUE when there is any job that has an exit callback and might exit,
1214 * which means job_check_ended() should be called more often.
1215 */
1216 int
1217has_pending_job(void)
1218{
1219 job_T *job;
1220
1221 FOR_ALL_JOBS(job)
1222 // Only should check if the channel has been closed, if the channel is
1223 // open the job won't exit.
1224 if ((job->jv_status == JOB_STARTED && !job_channel_still_useful(job))
1225 || (job->jv_status == JOB_FINISHED
1226 && job_channel_can_close(job)))
1227 return TRUE;
1228 return FALSE;
1229}
1230
1231#define MAX_CHECK_ENDED 8
1232
1233/*
1234 * Called once in a while: check if any jobs that seem useful have ended.
1235 * Returns TRUE if a job did end.
1236 */
1237 int
1238job_check_ended(void)
1239{
1240 int i;
1241 int did_end = FALSE;
1242
1243 // be quick if there are no jobs to check
1244 if (first_job == NULL)
1245 return did_end;
1246
1247 for (i = 0; i < MAX_CHECK_ENDED; ++i)
1248 {
1249 // NOTE: mch_detect_ended_job() must only return a job of which the
1250 // status was just set to JOB_ENDED.
1251 job_T *job = mch_detect_ended_job(first_job);
1252
1253 if (job == NULL)
1254 break;
1255 did_end = TRUE;
1256 job_cleanup(job); // may add "job" to jobs_to_free
1257 }
1258
1259 // Actually free jobs that were cleaned up.
1260 free_jobs_to_free_later();
1261
1262 if (channel_need_redraw)
1263 {
1264 channel_need_redraw = FALSE;
Bram Moolenaare5050712021-12-09 10:51:05 +00001265 redraw_after_callback(TRUE, FALSE);
Bram Moolenaar8b5866d2020-09-05 15:48:51 +02001266 }
1267 return did_end;
1268}
1269
1270/*
1271 * Create a job and return it. Implements job_start().
1272 * "argv_arg" is only for Unix.
1273 * When "argv_arg" is NULL then "argvars" is used.
1274 * The returned job has a refcount of one.
1275 * Returns NULL when out of memory.
1276 */
1277 job_T *
1278job_start(
1279 typval_T *argvars,
1280 char **argv_arg UNUSED,
1281 jobopt_T *opt_arg,
1282 job_T **term_job)
1283{
1284 job_T *job;
1285 char_u *cmd = NULL;
1286 char **argv = NULL;
1287 int argc = 0;
1288 int i;
Bram Moolenaarf46bf522020-12-09 13:16:13 +01001289#ifndef USE_ARGV
Bram Moolenaar8b5866d2020-09-05 15:48:51 +02001290 garray_T ga;
1291#endif
1292 jobopt_T opt;
1293 ch_part_T part;
1294
1295 job = job_alloc();
1296 if (job == NULL)
1297 return NULL;
1298
1299 job->jv_status = JOB_FAILED;
1300#ifndef USE_ARGV
Bram Moolenaar04935fb2022-01-08 16:19:22 +00001301 ga_init2(&ga, sizeof(char*), 20);
Bram Moolenaar8b5866d2020-09-05 15:48:51 +02001302#endif
1303
1304 if (opt_arg != NULL)
1305 opt = *opt_arg;
1306 else
1307 {
1308 // Default mode is NL.
1309 clear_job_options(&opt);
Bram Moolenaarac4174e2022-05-07 16:38:24 +01001310 opt.jo_mode = CH_MODE_NL;
Bram Moolenaar8b5866d2020-09-05 15:48:51 +02001311 if (get_job_options(&argvars[1], &opt,
1312 JO_MODE_ALL + JO_CB_ALL + JO_TIMEOUT_ALL + JO_STOPONEXIT
1313 + JO_EXIT_CB + JO_OUT_IO + JO_BLOCK_WRITE,
1314 JO2_ENV + JO2_CWD) == FAIL)
1315 goto theend;
1316 }
1317
1318 // Check that when io is "file" that there is a file name.
1319 for (part = PART_OUT; part < PART_COUNT; ++part)
1320 if ((opt.jo_set & (JO_OUT_IO << (part - PART_OUT)))
1321 && opt.jo_io[part] == JIO_FILE
1322 && (!(opt.jo_set & (JO_OUT_NAME << (part - PART_OUT)))
1323 || *opt.jo_io_name[part] == NUL))
1324 {
Bram Moolenaard82a47d2022-01-05 20:24:39 +00001325 emsg(_(e_io_file_requires_name_to_be_set));
Bram Moolenaar8b5866d2020-09-05 15:48:51 +02001326 goto theend;
1327 }
1328
1329 if ((opt.jo_set & JO_IN_IO) && opt.jo_io[PART_IN] == JIO_BUFFER)
1330 {
1331 buf_T *buf = NULL;
1332
1333 // check that we can find the buffer before starting the job
1334 if (opt.jo_set & JO_IN_BUF)
1335 {
1336 buf = buflist_findnr(opt.jo_io_buf[PART_IN]);
1337 if (buf == NULL)
Bram Moolenaar40bcec12021-12-05 22:19:27 +00001338 semsg(_(e_buffer_nr_does_not_exist),
1339 (long)opt.jo_io_buf[PART_IN]);
Bram Moolenaar8b5866d2020-09-05 15:48:51 +02001340 }
1341 else if (!(opt.jo_set & JO_IN_NAME))
1342 {
Bram Moolenaard82a47d2022-01-05 20:24:39 +00001343 emsg(_(e_in_io_buffer_requires_in_buf_or_in_name_to_be_set));
Bram Moolenaar8b5866d2020-09-05 15:48:51 +02001344 }
1345 else
1346 buf = buflist_find_by_name(opt.jo_io_name[PART_IN], FALSE);
1347 if (buf == NULL)
1348 goto theend;
1349 if (buf->b_ml.ml_mfp == NULL)
1350 {
1351 char_u numbuf[NUMBUFLEN];
1352 char_u *s;
1353
1354 if (opt.jo_set & JO_IN_BUF)
1355 {
1356 sprintf((char *)numbuf, "%d", opt.jo_io_buf[PART_IN]);
1357 s = numbuf;
1358 }
1359 else
1360 s = opt.jo_io_name[PART_IN];
Bram Moolenaard82a47d2022-01-05 20:24:39 +00001361 semsg(_(e_buffer_must_be_loaded_str), s);
Bram Moolenaar8b5866d2020-09-05 15:48:51 +02001362 goto theend;
1363 }
1364 job->jv_in_buf = buf;
1365 }
1366
1367 job_set_options(job, &opt);
1368
1369#ifdef USE_ARGV
1370 if (argv_arg != NULL)
1371 {
1372 // Make a copy of argv_arg for job->jv_argv.
1373 for (i = 0; argv_arg[i] != NULL; i++)
1374 argc++;
1375 argv = ALLOC_MULT(char *, argc + 1);
1376 if (argv == NULL)
1377 goto theend;
1378 for (i = 0; i < argc; i++)
1379 argv[i] = (char *)vim_strsave((char_u *)argv_arg[i]);
1380 argv[argc] = NULL;
1381 }
1382 else
1383#endif
1384 if (argvars[0].v_type == VAR_STRING)
1385 {
1386 // Command is a string.
1387 cmd = argvars[0].vval.v_string;
1388 if (cmd == NULL || *skipwhite(cmd) == NUL)
1389 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00001390 emsg(_(e_invalid_argument));
Bram Moolenaar8b5866d2020-09-05 15:48:51 +02001391 goto theend;
1392 }
1393
1394 if (build_argv_from_string(cmd, &argv, &argc) == FAIL)
1395 goto theend;
1396 }
1397 else if (argvars[0].v_type != VAR_LIST
1398 || argvars[0].vval.v_list == NULL
1399 || argvars[0].vval.v_list->lv_len < 1)
1400 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00001401 emsg(_(e_invalid_argument));
Bram Moolenaar8b5866d2020-09-05 15:48:51 +02001402 goto theend;
1403 }
1404 else
1405 {
1406 list_T *l = argvars[0].vval.v_list;
1407
1408 if (build_argv_from_list(l, &argv, &argc) == FAIL)
1409 goto theend;
1410
1411 // Empty command is invalid.
1412 if (argc == 0 || *skipwhite((char_u *)argv[0]) == NUL)
1413 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00001414 emsg(_(e_invalid_argument));
Bram Moolenaar8b5866d2020-09-05 15:48:51 +02001415 goto theend;
1416 }
1417#ifndef USE_ARGV
1418 if (win32_build_cmd(l, &ga) == FAIL)
1419 goto theend;
1420 cmd = ga.ga_data;
1421 if (cmd == NULL || *skipwhite(cmd) == NUL)
1422 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00001423 emsg(_(e_invalid_argument));
Bram Moolenaar8b5866d2020-09-05 15:48:51 +02001424 goto theend;
1425 }
1426#endif
1427 }
1428
1429 // Save the command used to start the job.
1430 job->jv_argv = argv;
1431
1432 if (term_job != NULL)
1433 *term_job = job;
1434
1435#ifdef USE_ARGV
1436 if (ch_log_active())
1437 {
1438 garray_T ga;
1439
Bram Moolenaar04935fb2022-01-08 16:19:22 +00001440 ga_init2(&ga, sizeof(char), 200);
Bram Moolenaar8b5866d2020-09-05 15:48:51 +02001441 for (i = 0; i < argc; ++i)
1442 {
1443 if (i > 0)
1444 ga_concat(&ga, (char_u *)" ");
1445 ga_concat(&ga, (char_u *)argv[i]);
1446 }
1447 ga_append(&ga, NUL);
1448 ch_log(NULL, "Starting job: %s", (char *)ga.ga_data);
1449 ga_clear(&ga);
1450 }
1451 mch_job_start(argv, job, &opt, term_job != NULL);
1452#else
1453 ch_log(NULL, "Starting job: %s", (char *)cmd);
1454 mch_job_start((char *)cmd, job, &opt);
1455#endif
1456
1457 // If the channel is reading from a buffer, write lines now.
1458 if (job->jv_channel != NULL)
1459 channel_write_in(job->jv_channel);
1460
1461theend:
1462#ifndef USE_ARGV
1463 vim_free(ga.ga_data);
1464#endif
1465 if (argv != NULL && argv != job->jv_argv)
1466 {
1467 for (i = 0; argv[i] != NULL; i++)
1468 vim_free(argv[i]);
1469 vim_free(argv);
1470 }
1471 free_job_options(&opt);
1472 return job;
1473}
1474
1475/*
1476 * Get the status of "job" and invoke the exit callback when needed.
1477 * The returned string is not allocated.
1478 */
1479 char *
1480job_status(job_T *job)
1481{
1482 char *result;
1483
1484 if (job->jv_status >= JOB_ENDED)
1485 // No need to check, dead is dead.
1486 result = "dead";
1487 else if (job->jv_status == JOB_FAILED)
1488 result = "fail";
1489 else
1490 {
1491 result = mch_job_status(job);
1492 if (job->jv_status == JOB_ENDED)
1493 job_cleanup(job);
1494 }
1495 return result;
1496}
1497
1498/*
1499 * Send a signal to "job". Implements job_stop().
1500 * When "type" is not NULL use this for the type.
1501 * Otherwise use argvars[1] for the type.
1502 */
1503 int
1504job_stop(job_T *job, typval_T *argvars, char *type)
1505{
1506 char_u *arg;
1507
1508 if (type != NULL)
1509 arg = (char_u *)type;
1510 else if (argvars[1].v_type == VAR_UNKNOWN)
1511 arg = (char_u *)"";
1512 else
1513 {
1514 arg = tv_get_string_chk(&argvars[1]);
1515 if (arg == NULL)
1516 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00001517 emsg(_(e_invalid_argument));
Bram Moolenaar8b5866d2020-09-05 15:48:51 +02001518 return 0;
1519 }
1520 }
1521 if (job->jv_status == JOB_FAILED)
1522 {
1523 ch_log(job->jv_channel, "Job failed to start, job_stop() skipped");
1524 return 0;
1525 }
1526 if (job->jv_status == JOB_ENDED)
1527 {
1528 ch_log(job->jv_channel, "Job has already ended, job_stop() skipped");
1529 return 0;
1530 }
1531 ch_log(job->jv_channel, "Stopping job with '%s'", (char *)arg);
1532 if (mch_signal_job(job, arg) == FAIL)
1533 return 0;
1534
1535 // Assume that only "kill" will kill the job.
1536 if (job->jv_channel != NULL && STRCMP(arg, "kill") == 0)
1537 job->jv_channel->ch_job_killed = TRUE;
1538
1539 // We don't try freeing the job, obviously the caller still has a
1540 // reference to it.
1541 return 1;
1542}
1543
1544 void
1545invoke_prompt_callback(void)
1546{
1547 typval_T rettv;
1548 typval_T argv[2];
1549 char_u *text;
1550 char_u *prompt;
1551 linenr_T lnum = curbuf->b_ml.ml_line_count;
1552
1553 // Add a new line for the prompt before invoking the callback, so that
1554 // text can always be inserted above the last line.
1555 ml_append(lnum, (char_u *)"", 0, FALSE);
1556 curwin->w_cursor.lnum = lnum + 1;
1557 curwin->w_cursor.col = 0;
1558
1559 if (curbuf->b_prompt_callback.cb_name == NULL
1560 || *curbuf->b_prompt_callback.cb_name == NUL)
1561 return;
1562 text = ml_get(lnum);
1563 prompt = prompt_text();
1564 if (STRLEN(text) >= STRLEN(prompt))
1565 text += STRLEN(prompt);
1566 argv[0].v_type = VAR_STRING;
1567 argv[0].vval.v_string = vim_strsave(text);
1568 argv[1].v_type = VAR_UNKNOWN;
1569
1570 call_callback(&curbuf->b_prompt_callback, -1, &rettv, 1, argv);
1571 clear_tv(&argv[0]);
1572 clear_tv(&rettv);
1573}
1574
1575/*
1576 * Return TRUE when the interrupt callback was invoked.
1577 */
1578 int
1579invoke_prompt_interrupt(void)
1580{
1581 typval_T rettv;
1582 typval_T argv[1];
Yegappan Lakshmanan4dc24eb2021-12-07 12:23:57 +00001583 int ret;
Bram Moolenaar8b5866d2020-09-05 15:48:51 +02001584
1585 if (curbuf->b_prompt_interrupt.cb_name == NULL
1586 || *curbuf->b_prompt_interrupt.cb_name == NUL)
1587 return FALSE;
1588 argv[0].v_type = VAR_UNKNOWN;
1589
1590 got_int = FALSE; // don't skip executing commands
Yegappan Lakshmanan4dc24eb2021-12-07 12:23:57 +00001591 ret = call_callback(&curbuf->b_prompt_interrupt, -1, &rettv, 0, argv);
Bram Moolenaar8b5866d2020-09-05 15:48:51 +02001592 clear_tv(&rettv);
Yegappan Lakshmanan4dc24eb2021-12-07 12:23:57 +00001593 return ret == FAIL ? FALSE : TRUE;
Bram Moolenaar8b5866d2020-09-05 15:48:51 +02001594}
1595
1596/*
1597 * Return the effective prompt for the specified buffer.
1598 */
Yegappan Lakshmanan8ee52af2021-08-09 19:59:06 +02001599 static char_u *
Bram Moolenaar8b5866d2020-09-05 15:48:51 +02001600buf_prompt_text(buf_T* buf)
1601{
1602 if (buf->b_prompt_text == NULL)
1603 return (char_u *)"% ";
1604 return buf->b_prompt_text;
1605}
1606
1607/*
1608 * Return the effective prompt for the current buffer.
1609 */
1610 char_u *
1611prompt_text(void)
1612{
1613 return buf_prompt_text(curbuf);
1614}
1615
1616
1617/*
1618 * Prepare for prompt mode: Make sure the last line has the prompt text.
1619 * Move the cursor to this line.
1620 */
1621 void
1622init_prompt(int cmdchar_todo)
1623{
1624 char_u *prompt = prompt_text();
1625 char_u *text;
1626
1627 curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
1628 text = ml_get_curline();
1629 if (STRNCMP(text, prompt, STRLEN(prompt)) != 0)
1630 {
1631 // prompt is missing, insert it or append a line with it
1632 if (*text == NUL)
1633 ml_replace(curbuf->b_ml.ml_line_count, prompt, TRUE);
1634 else
1635 ml_append(curbuf->b_ml.ml_line_count, prompt, 0, FALSE);
1636 curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
1637 coladvance((colnr_T)MAXCOL);
1638 changed_bytes(curbuf->b_ml.ml_line_count, 0);
1639 }
1640
1641 // Insert always starts after the prompt, allow editing text after it.
1642 if (Insstart_orig.lnum != curwin->w_cursor.lnum
1643 || Insstart_orig.col != (int)STRLEN(prompt))
1644 set_insstart(curwin->w_cursor.lnum, (int)STRLEN(prompt));
1645
1646 if (cmdchar_todo == 'A')
1647 coladvance((colnr_T)MAXCOL);
Bram Moolenaaree8b7872020-11-19 18:46:25 +01001648 if (curwin->w_cursor.col < (int)STRLEN(prompt))
Bram Moolenaar8b5866d2020-09-05 15:48:51 +02001649 curwin->w_cursor.col = (int)STRLEN(prompt);
1650 // Make sure the cursor is in a valid position.
1651 check_cursor();
1652}
1653
1654/*
1655 * Return TRUE if the cursor is in the editable position of the prompt line.
1656 */
1657 int
1658prompt_curpos_editable()
1659{
1660 return curwin->w_cursor.lnum == curbuf->b_ml.ml_line_count
1661 && curwin->w_cursor.col >= (int)STRLEN(prompt_text());
1662}
1663
1664/*
1665 * "prompt_setcallback({buffer}, {callback})" function
1666 */
1667 void
1668f_prompt_setcallback(typval_T *argvars, typval_T *rettv UNUSED)
1669{
1670 buf_T *buf;
1671 callback_T callback;
1672
1673 if (check_secure())
1674 return;
Yegappan Lakshmanan7973de32021-07-24 16:16:15 +02001675
1676 if (in_vim9script() && check_for_buffer_arg(argvars, 0) == FAIL)
1677 return;
1678
Bram Moolenaar8b5866d2020-09-05 15:48:51 +02001679 buf = tv_get_buf(&argvars[0], FALSE);
1680 if (buf == NULL)
1681 return;
1682
1683 callback = get_callback(&argvars[1]);
1684 if (callback.cb_name == NULL)
1685 return;
1686
1687 free_callback(&buf->b_prompt_callback);
1688 set_callback(&buf->b_prompt_callback, &callback);
1689}
1690
1691/*
1692 * "prompt_setinterrupt({buffer}, {callback})" function
1693 */
1694 void
1695f_prompt_setinterrupt(typval_T *argvars, typval_T *rettv UNUSED)
1696{
1697 buf_T *buf;
1698 callback_T callback;
1699
1700 if (check_secure())
1701 return;
Yegappan Lakshmanan7973de32021-07-24 16:16:15 +02001702
1703 if (in_vim9script() && check_for_buffer_arg(argvars, 0) == FAIL)
1704 return;
1705
Bram Moolenaar8b5866d2020-09-05 15:48:51 +02001706 buf = tv_get_buf(&argvars[0], FALSE);
1707 if (buf == NULL)
1708 return;
1709
1710 callback = get_callback(&argvars[1]);
1711 if (callback.cb_name == NULL)
1712 return;
1713
1714 free_callback(&buf->b_prompt_interrupt);
1715 set_callback(&buf->b_prompt_interrupt, &callback);
1716}
1717
1718
1719/*
1720 * "prompt_getprompt({buffer})" function
1721 */
1722 void
1723f_prompt_getprompt(typval_T *argvars, typval_T *rettv)
1724{
1725 buf_T *buf;
1726
1727 // return an empty string by default, e.g. it's not a prompt buffer
1728 rettv->v_type = VAR_STRING;
1729 rettv->vval.v_string = NULL;
1730
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02001731 if (in_vim9script() && check_for_buffer_arg(argvars, 0) == FAIL)
1732 return;
1733
Bram Moolenaar8b5866d2020-09-05 15:48:51 +02001734 buf = tv_get_buf_from_arg(&argvars[0]);
1735 if (buf == NULL)
1736 return;
1737
1738 if (!bt_prompt(buf))
1739 return;
1740
1741 rettv->vval.v_string = vim_strsave(buf_prompt_text(buf));
1742}
1743
1744/*
1745 * "prompt_setprompt({buffer}, {text})" function
1746 */
1747 void
1748f_prompt_setprompt(typval_T *argvars, typval_T *rettv UNUSED)
1749{
1750 buf_T *buf;
1751 char_u *text;
1752
Yegappan Lakshmanana9a7c0c2021-07-17 19:11:07 +02001753 if (in_vim9script()
Yegappan Lakshmanancd917202021-07-21 19:09:09 +02001754 && (check_for_buffer_arg(argvars, 0) == FAIL
Yegappan Lakshmanana9a7c0c2021-07-17 19:11:07 +02001755 || check_for_string_arg(argvars, 1) == FAIL))
1756 return;
1757
Bram Moolenaar8b5866d2020-09-05 15:48:51 +02001758 if (check_secure())
1759 return;
1760 buf = tv_get_buf(&argvars[0], FALSE);
1761 if (buf == NULL)
1762 return;
1763
1764 text = tv_get_string(&argvars[1]);
1765 vim_free(buf->b_prompt_text);
1766 buf->b_prompt_text = vim_strsave(text);
1767}
1768
1769/*
1770 * Get the job from the argument.
1771 * Returns NULL if the job is invalid.
1772 */
1773 static job_T *
1774get_job_arg(typval_T *tv)
1775{
1776 job_T *job;
1777
1778 if (tv->v_type != VAR_JOB)
1779 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00001780 semsg(_(e_invalid_argument_str), tv_get_string(tv));
Bram Moolenaar8b5866d2020-09-05 15:48:51 +02001781 return NULL;
1782 }
1783 job = tv->vval.v_job;
1784
1785 if (job == NULL)
Bram Moolenaard82a47d2022-01-05 20:24:39 +00001786 emsg(_(e_not_valid_job));
Bram Moolenaar8b5866d2020-09-05 15:48:51 +02001787 return job;
1788}
1789
1790/*
1791 * "job_getchannel()" function
1792 */
1793 void
1794f_job_getchannel(typval_T *argvars, typval_T *rettv)
1795{
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02001796 job_T *job;
Bram Moolenaar8b5866d2020-09-05 15:48:51 +02001797
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02001798 if (in_vim9script() && check_for_job_arg(argvars, 0) == FAIL)
1799 return;
1800
1801 job = get_job_arg(&argvars[0]);
Bram Moolenaar8b5866d2020-09-05 15:48:51 +02001802 if (job != NULL)
1803 {
1804 rettv->v_type = VAR_CHANNEL;
1805 rettv->vval.v_channel = job->jv_channel;
1806 if (job->jv_channel != NULL)
1807 ++job->jv_channel->ch_refcount;
1808 }
1809}
1810
1811/*
1812 * Implementation of job_info().
1813 */
1814 static void
1815job_info(job_T *job, dict_T *dict)
1816{
1817 dictitem_T *item;
1818 varnumber_T nr;
1819 list_T *l;
1820 int i;
1821
1822 dict_add_string(dict, "status", (char_u *)job_status(job));
1823
1824 item = dictitem_alloc((char_u *)"channel");
1825 if (item == NULL)
1826 return;
1827 item->di_tv.v_type = VAR_CHANNEL;
1828 item->di_tv.vval.v_channel = job->jv_channel;
1829 if (job->jv_channel != NULL)
1830 ++job->jv_channel->ch_refcount;
1831 if (dict_add(dict, item) == FAIL)
1832 dictitem_free(item);
1833
1834#ifdef UNIX
1835 nr = job->jv_pid;
1836#else
1837 nr = job->jv_proc_info.dwProcessId;
1838#endif
1839 dict_add_number(dict, "process", nr);
1840 dict_add_string(dict, "tty_in", job->jv_tty_in);
1841 dict_add_string(dict, "tty_out", job->jv_tty_out);
1842
1843 dict_add_number(dict, "exitval", job->jv_exitval);
1844 dict_add_string(dict, "exit_cb", job->jv_exit_cb.cb_name);
1845 dict_add_string(dict, "stoponexit", job->jv_stoponexit);
1846#ifdef UNIX
1847 dict_add_string(dict, "termsig", job->jv_termsig);
1848#endif
1849#ifdef MSWIN
1850 dict_add_string(dict, "tty_type", job->jv_tty_type);
1851#endif
1852
1853 l = list_alloc();
1854 if (l != NULL)
1855 {
1856 dict_add_list(dict, "cmd", l);
1857 if (job->jv_argv != NULL)
1858 for (i = 0; job->jv_argv[i] != NULL; i++)
1859 list_append_string(l, (char_u *)job->jv_argv[i], -1);
1860 }
1861}
1862
1863/*
1864 * Implementation of job_info() to return info for all jobs.
1865 */
1866 static void
1867job_info_all(list_T *l)
1868{
1869 job_T *job;
1870 typval_T tv;
1871
1872 FOR_ALL_JOBS(job)
1873 {
1874 tv.v_type = VAR_JOB;
1875 tv.vval.v_job = job;
1876
1877 if (list_append_tv(l, &tv) != OK)
1878 return;
1879 }
1880}
1881
1882/*
1883 * "job_info()" function
1884 */
1885 void
1886f_job_info(typval_T *argvars, typval_T *rettv)
1887{
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02001888 if (in_vim9script() && check_for_opt_job_arg(argvars, 0) == FAIL)
1889 return;
1890
Bram Moolenaar8b5866d2020-09-05 15:48:51 +02001891 if (argvars[0].v_type != VAR_UNKNOWN)
1892 {
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02001893 job_T *job;
Bram Moolenaar8b5866d2020-09-05 15:48:51 +02001894
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02001895 job = get_job_arg(&argvars[0]);
Bram Moolenaar8b5866d2020-09-05 15:48:51 +02001896 if (job != NULL && rettv_dict_alloc(rettv) != FAIL)
1897 job_info(job, rettv->vval.v_dict);
1898 }
1899 else if (rettv_list_alloc(rettv) == OK)
1900 job_info_all(rettv->vval.v_list);
1901}
1902
1903/*
1904 * "job_setoptions()" function
1905 */
1906 void
1907f_job_setoptions(typval_T *argvars, typval_T *rettv UNUSED)
1908{
Yegappan Lakshmanan83494b42021-07-20 17:51:51 +02001909 job_T *job;
Bram Moolenaar8b5866d2020-09-05 15:48:51 +02001910 jobopt_T opt;
1911
Yegappan Lakshmanan83494b42021-07-20 17:51:51 +02001912 if (in_vim9script()
1913 && (check_for_job_arg(argvars, 0) == FAIL
1914 || check_for_dict_arg(argvars, 1) == FAIL))
1915 return;
1916
1917 job = get_job_arg(&argvars[0]);
Bram Moolenaar8b5866d2020-09-05 15:48:51 +02001918 if (job == NULL)
1919 return;
1920 clear_job_options(&opt);
1921 if (get_job_options(&argvars[1], &opt, JO_STOPONEXIT + JO_EXIT_CB, 0) == OK)
1922 job_set_options(job, &opt);
1923 free_job_options(&opt);
1924}
1925
1926/*
1927 * "job_start()" function
1928 */
1929 void
1930f_job_start(typval_T *argvars, typval_T *rettv)
1931{
1932 rettv->v_type = VAR_JOB;
1933 if (check_restricted() || check_secure())
1934 return;
Yegappan Lakshmanan0ad871d2021-07-23 20:37:56 +02001935
1936 if (in_vim9script()
1937 && (check_for_string_or_list_arg(argvars, 0) == FAIL
1938 || check_for_opt_dict_arg(argvars, 1) == FAIL))
1939 return;
1940
Bram Moolenaar8b5866d2020-09-05 15:48:51 +02001941 rettv->vval.v_job = job_start(argvars, NULL, NULL, NULL);
1942}
1943
1944/*
1945 * "job_status()" function
1946 */
1947 void
1948f_job_status(typval_T *argvars, typval_T *rettv)
1949{
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02001950 if (in_vim9script() && check_for_job_arg(argvars, 0) == FAIL)
1951 return;
1952
Bram Moolenaar218450a2020-10-17 18:51:52 +02001953 if (argvars[0].v_type == VAR_JOB && argvars[0].vval.v_job == NULL)
Bram Moolenaar8b5866d2020-09-05 15:48:51 +02001954 {
Bram Moolenaar218450a2020-10-17 18:51:52 +02001955 // A job that never started returns "fail".
Bram Moolenaar8b5866d2020-09-05 15:48:51 +02001956 rettv->v_type = VAR_STRING;
Bram Moolenaar218450a2020-10-17 18:51:52 +02001957 rettv->vval.v_string = vim_strsave((char_u *)"fail");
1958 }
1959 else
1960 {
1961 job_T *job = get_job_arg(&argvars[0]);
1962
1963 if (job != NULL)
1964 {
1965 rettv->v_type = VAR_STRING;
1966 rettv->vval.v_string = vim_strsave((char_u *)job_status(job));
1967 }
Bram Moolenaar8b5866d2020-09-05 15:48:51 +02001968 }
1969}
1970
1971/*
1972 * "job_stop()" function
1973 */
1974 void
1975f_job_stop(typval_T *argvars, typval_T *rettv)
1976{
Yegappan Lakshmanan83494b42021-07-20 17:51:51 +02001977 job_T *job;
Bram Moolenaar8b5866d2020-09-05 15:48:51 +02001978
Yegappan Lakshmanan83494b42021-07-20 17:51:51 +02001979 if (in_vim9script()
1980 && (check_for_job_arg(argvars, 0) == FAIL
1981 || check_for_opt_string_or_number_arg(argvars, 1) == FAIL))
1982 return;
1983
1984 job = get_job_arg(&argvars[0]);
Bram Moolenaar8b5866d2020-09-05 15:48:51 +02001985 if (job != NULL)
1986 rettv->vval.v_number = job_stop(job, argvars, NULL);
1987}
1988
Bram Moolenaar1328bde2021-06-05 20:51:38 +02001989/*
1990 * Get a string with information about the job in "varp" in "buf".
1991 * "buf" must be at least NUMBUFLEN long.
1992 */
1993 char_u *
1994job_to_string_buf(typval_T *varp, char_u *buf)
1995{
1996 job_T *job = varp->vval.v_job;
1997 char *status;
1998
1999 if (job == NULL)
Bram Moolenaar271906b2021-08-28 12:30:12 +02002000 {
2001 vim_snprintf((char *)buf, NUMBUFLEN, "no process");
2002 return buf;
2003 }
Bram Moolenaar1328bde2021-06-05 20:51:38 +02002004 status = job->jv_status == JOB_FAILED ? "fail"
2005 : job->jv_status >= JOB_ENDED ? "dead"
2006 : "run";
2007# ifdef UNIX
2008 vim_snprintf((char *)buf, NUMBUFLEN,
2009 "process %ld %s", (long)job->jv_pid, status);
2010# elif defined(MSWIN)
2011 vim_snprintf((char *)buf, NUMBUFLEN,
2012 "process %ld %s",
2013 (long)job->jv_proc_info.dwProcessId,
2014 status);
2015# else
2016 // fall-back
2017 vim_snprintf((char *)buf, NUMBUFLEN, "process ? %s", status);
2018# endif
2019 return buf;
2020}
2021
Bram Moolenaar8b5866d2020-09-05 15:48:51 +02002022#endif // FEAT_JOB_CHANNEL