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