Mercurial > vim
comparison src/scriptfile.c @ 17861:0a5c615cd949 v8.1.1927
patch 8.1.1927: code for dealing with script files is spread out
Commit: https://github.com/vim/vim/commit/307c5a5bb77c3728dfab06c30e9f786309c63f74
Author: Bram Moolenaar <Bram@vim.org>
Date: Sun Aug 25 15:41:00 2019 +0200
patch 8.1.1927: code for dealing with script files is spread out
Problem: Code for dealing with script files is spread out.
Solution: Move the code to scriptfile.c. (Yegappan Lakshmanan, closes https://github.com/vim/vim/issues/4861)
author | Bram Moolenaar <Bram@vim.org> |
---|---|
date | Sun, 25 Aug 2019 15:45:04 +0200 |
parents | |
children | 4d63d47d87ef |
comparison
equal
deleted
inserted
replaced
17860:9ef94ab1528c | 17861:0a5c615cd949 |
---|---|
1 /* 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 * See README.txt for an overview of the Vim source code. | |
8 */ | |
9 | |
10 /* | |
11 * scriptfile.c: functions for dealing with the runtime directories/files | |
12 */ | |
13 | |
14 #include "vim.h" | |
15 | |
16 /* | |
17 * ":runtime [what] {name}" | |
18 */ | |
19 void | |
20 ex_runtime(exarg_T *eap) | |
21 { | |
22 char_u *arg = eap->arg; | |
23 char_u *p = skiptowhite(arg); | |
24 int len = (int)(p - arg); | |
25 int flags = eap->forceit ? DIP_ALL : 0; | |
26 | |
27 if (STRNCMP(arg, "START", len) == 0) | |
28 { | |
29 flags += DIP_START + DIP_NORTP; | |
30 arg = skipwhite(arg + len); | |
31 } | |
32 else if (STRNCMP(arg, "OPT", len) == 0) | |
33 { | |
34 flags += DIP_OPT + DIP_NORTP; | |
35 arg = skipwhite(arg + len); | |
36 } | |
37 else if (STRNCMP(arg, "PACK", len) == 0) | |
38 { | |
39 flags += DIP_START + DIP_OPT + DIP_NORTP; | |
40 arg = skipwhite(arg + len); | |
41 } | |
42 else if (STRNCMP(arg, "ALL", len) == 0) | |
43 { | |
44 flags += DIP_START + DIP_OPT; | |
45 arg = skipwhite(arg + len); | |
46 } | |
47 | |
48 source_runtime(arg, flags); | |
49 } | |
50 | |
51 static void | |
52 source_callback(char_u *fname, void *cookie UNUSED) | |
53 { | |
54 (void)do_source(fname, FALSE, DOSO_NONE); | |
55 } | |
56 | |
57 /* | |
58 * Find the file "name" in all directories in "path" and invoke | |
59 * "callback(fname, cookie)". | |
60 * "name" can contain wildcards. | |
61 * When "flags" has DIP_ALL: source all files, otherwise only the first one. | |
62 * When "flags" has DIP_DIR: find directories instead of files. | |
63 * When "flags" has DIP_ERR: give an error message if there is no match. | |
64 * | |
65 * return FAIL when no file could be sourced, OK otherwise. | |
66 */ | |
67 int | |
68 do_in_path( | |
69 char_u *path, | |
70 char_u *name, | |
71 int flags, | |
72 void (*callback)(char_u *fname, void *ck), | |
73 void *cookie) | |
74 { | |
75 char_u *rtp; | |
76 char_u *np; | |
77 char_u *buf; | |
78 char_u *rtp_copy; | |
79 char_u *tail; | |
80 int num_files; | |
81 char_u **files; | |
82 int i; | |
83 int did_one = FALSE; | |
84 #ifdef AMIGA | |
85 struct Process *proc = (struct Process *)FindTask(0L); | |
86 APTR save_winptr = proc->pr_WindowPtr; | |
87 | |
88 // Avoid a requester here for a volume that doesn't exist. | |
89 proc->pr_WindowPtr = (APTR)-1L; | |
90 #endif | |
91 | |
92 // Make a copy of 'runtimepath'. Invoking the callback may change the | |
93 // value. | |
94 rtp_copy = vim_strsave(path); | |
95 buf = alloc(MAXPATHL); | |
96 if (buf != NULL && rtp_copy != NULL) | |
97 { | |
98 if (p_verbose > 1 && name != NULL) | |
99 { | |
100 verbose_enter(); | |
101 smsg(_("Searching for \"%s\" in \"%s\""), | |
102 (char *)name, (char *)path); | |
103 verbose_leave(); | |
104 } | |
105 | |
106 // Loop over all entries in 'runtimepath'. | |
107 rtp = rtp_copy; | |
108 while (*rtp != NUL && ((flags & DIP_ALL) || !did_one)) | |
109 { | |
110 size_t buflen; | |
111 | |
112 // Copy the path from 'runtimepath' to buf[]. | |
113 copy_option_part(&rtp, buf, MAXPATHL, ","); | |
114 buflen = STRLEN(buf); | |
115 | |
116 // Skip after or non-after directories. | |
117 if (flags & (DIP_NOAFTER | DIP_AFTER)) | |
118 { | |
119 int is_after = buflen >= 5 | |
120 && STRCMP(buf + buflen - 5, "after") == 0; | |
121 | |
122 if ((is_after && (flags & DIP_NOAFTER)) | |
123 || (!is_after && (flags & DIP_AFTER))) | |
124 continue; | |
125 } | |
126 | |
127 if (name == NULL) | |
128 { | |
129 (*callback)(buf, (void *) &cookie); | |
130 if (!did_one) | |
131 did_one = (cookie == NULL); | |
132 } | |
133 else if (buflen + STRLEN(name) + 2 < MAXPATHL) | |
134 { | |
135 add_pathsep(buf); | |
136 tail = buf + STRLEN(buf); | |
137 | |
138 // Loop over all patterns in "name" | |
139 np = name; | |
140 while (*np != NUL && ((flags & DIP_ALL) || !did_one)) | |
141 { | |
142 // Append the pattern from "name" to buf[]. | |
143 copy_option_part(&np, tail, (int)(MAXPATHL - (tail - buf)), | |
144 "\t "); | |
145 | |
146 if (p_verbose > 2) | |
147 { | |
148 verbose_enter(); | |
149 smsg(_("Searching for \"%s\""), buf); | |
150 verbose_leave(); | |
151 } | |
152 | |
153 // Expand wildcards, invoke the callback for each match. | |
154 if (gen_expand_wildcards(1, &buf, &num_files, &files, | |
155 (flags & DIP_DIR) ? EW_DIR : EW_FILE) == OK) | |
156 { | |
157 for (i = 0; i < num_files; ++i) | |
158 { | |
159 (*callback)(files[i], cookie); | |
160 did_one = TRUE; | |
161 if (!(flags & DIP_ALL)) | |
162 break; | |
163 } | |
164 FreeWild(num_files, files); | |
165 } | |
166 } | |
167 } | |
168 } | |
169 } | |
170 vim_free(buf); | |
171 vim_free(rtp_copy); | |
172 if (!did_one && name != NULL) | |
173 { | |
174 char *basepath = path == p_rtp ? "runtimepath" : "packpath"; | |
175 | |
176 if (flags & DIP_ERR) | |
177 semsg(_(e_dirnotf), basepath, name); | |
178 else if (p_verbose > 0) | |
179 { | |
180 verbose_enter(); | |
181 smsg(_("not found in '%s': \"%s\""), basepath, name); | |
182 verbose_leave(); | |
183 } | |
184 } | |
185 | |
186 #ifdef AMIGA | |
187 proc->pr_WindowPtr = save_winptr; | |
188 #endif | |
189 | |
190 return did_one ? OK : FAIL; | |
191 } | |
192 | |
193 /* | |
194 * Find "name" in "path". When found, invoke the callback function for | |
195 * it: callback(fname, "cookie") | |
196 * When "flags" has DIP_ALL repeat for all matches, otherwise only the first | |
197 * one is used. | |
198 * Returns OK when at least one match found, FAIL otherwise. | |
199 * | |
200 * If "name" is NULL calls callback for each entry in "path". Cookie is | |
201 * passed by reference in this case, setting it to NULL indicates that callback | |
202 * has done its job. | |
203 */ | |
204 static int | |
205 do_in_path_and_pp( | |
206 char_u *path, | |
207 char_u *name, | |
208 int flags, | |
209 void (*callback)(char_u *fname, void *ck), | |
210 void *cookie) | |
211 { | |
212 int done = FAIL; | |
213 char_u *s; | |
214 int len; | |
215 char *start_dir = "pack/*/start/*/%s"; | |
216 char *opt_dir = "pack/*/opt/*/%s"; | |
217 | |
218 if ((flags & DIP_NORTP) == 0) | |
219 done = do_in_path(path, name, flags, callback, cookie); | |
220 | |
221 if ((done == FAIL || (flags & DIP_ALL)) && (flags & DIP_START)) | |
222 { | |
223 len = (int)(STRLEN(start_dir) + STRLEN(name)); | |
224 s = alloc(len); | |
225 if (s == NULL) | |
226 return FAIL; | |
227 vim_snprintf((char *)s, len, start_dir, name); | |
228 done = do_in_path(p_pp, s, flags, callback, cookie); | |
229 vim_free(s); | |
230 } | |
231 | |
232 if ((done == FAIL || (flags & DIP_ALL)) && (flags & DIP_OPT)) | |
233 { | |
234 len = (int)(STRLEN(opt_dir) + STRLEN(name)); | |
235 s = alloc(len); | |
236 if (s == NULL) | |
237 return FAIL; | |
238 vim_snprintf((char *)s, len, opt_dir, name); | |
239 done = do_in_path(p_pp, s, flags, callback, cookie); | |
240 vim_free(s); | |
241 } | |
242 | |
243 return done; | |
244 } | |
245 | |
246 /* | |
247 * Just like do_in_path_and_pp(), using 'runtimepath' for "path". | |
248 */ | |
249 int | |
250 do_in_runtimepath( | |
251 char_u *name, | |
252 int flags, | |
253 void (*callback)(char_u *fname, void *ck), | |
254 void *cookie) | |
255 { | |
256 return do_in_path_and_pp(p_rtp, name, flags, callback, cookie); | |
257 } | |
258 | |
259 /* | |
260 * Source the file "name" from all directories in 'runtimepath'. | |
261 * "name" can contain wildcards. | |
262 * When "flags" has DIP_ALL: source all files, otherwise only the first one. | |
263 * | |
264 * return FAIL when no file could be sourced, OK otherwise. | |
265 */ | |
266 int | |
267 source_runtime(char_u *name, int flags) | |
268 { | |
269 return source_in_path(p_rtp, name, flags); | |
270 } | |
271 | |
272 /* | |
273 * Just like source_runtime(), but use "path" instead of 'runtimepath'. | |
274 */ | |
275 int | |
276 source_in_path(char_u *path, char_u *name, int flags) | |
277 { | |
278 return do_in_path_and_pp(path, name, flags, source_callback, NULL); | |
279 } | |
280 | |
281 | |
282 #if defined(FEAT_EVAL) || defined(PROTO) | |
283 | |
284 /* | |
285 * Expand wildcards in "pat" and invoke do_source() for each match. | |
286 */ | |
287 static void | |
288 source_all_matches(char_u *pat) | |
289 { | |
290 int num_files; | |
291 char_u **files; | |
292 int i; | |
293 | |
294 if (gen_expand_wildcards(1, &pat, &num_files, &files, EW_FILE) == OK) | |
295 { | |
296 for (i = 0; i < num_files; ++i) | |
297 (void)do_source(files[i], FALSE, DOSO_NONE); | |
298 FreeWild(num_files, files); | |
299 } | |
300 } | |
301 | |
302 /* | |
303 * Add the package directory to 'runtimepath'. | |
304 */ | |
305 static int | |
306 add_pack_dir_to_rtp(char_u *fname) | |
307 { | |
308 char_u *p4, *p3, *p2, *p1, *p; | |
309 char_u *entry; | |
310 char_u *insp = NULL; | |
311 int c; | |
312 char_u *new_rtp; | |
313 int keep; | |
314 size_t oldlen; | |
315 size_t addlen; | |
316 size_t new_rtp_len; | |
317 char_u *afterdir = NULL; | |
318 size_t afterlen = 0; | |
319 char_u *after_insp = NULL; | |
320 char_u *ffname = NULL; | |
321 size_t fname_len; | |
322 char_u *buf = NULL; | |
323 char_u *rtp_ffname; | |
324 int match; | |
325 int retval = FAIL; | |
326 | |
327 p4 = p3 = p2 = p1 = get_past_head(fname); | |
328 for (p = p1; *p; MB_PTR_ADV(p)) | |
329 if (vim_ispathsep_nocolon(*p)) | |
330 { | |
331 p4 = p3; p3 = p2; p2 = p1; p1 = p; | |
332 } | |
333 | |
334 // now we have: | |
335 // rtp/pack/name/start/name | |
336 // p4 p3 p2 p1 | |
337 // | |
338 // find the part up to "pack" in 'runtimepath' | |
339 c = *++p4; // append pathsep in order to expand symlink | |
340 *p4 = NUL; | |
341 ffname = fix_fname(fname); | |
342 *p4 = c; | |
343 if (ffname == NULL) | |
344 return FAIL; | |
345 | |
346 // Find "ffname" in "p_rtp", ignoring '/' vs '\' differences. | |
347 // Also stop at the first "after" directory. | |
348 fname_len = STRLEN(ffname); | |
349 buf = alloc(MAXPATHL); | |
350 if (buf == NULL) | |
351 goto theend; | |
352 for (entry = p_rtp; *entry != NUL; ) | |
353 { | |
354 char_u *cur_entry = entry; | |
355 | |
356 copy_option_part(&entry, buf, MAXPATHL, ","); | |
357 if (insp == NULL) | |
358 { | |
359 add_pathsep(buf); | |
360 rtp_ffname = fix_fname(buf); | |
361 if (rtp_ffname == NULL) | |
362 goto theend; | |
363 match = vim_fnamencmp(rtp_ffname, ffname, fname_len) == 0; | |
364 vim_free(rtp_ffname); | |
365 if (match) | |
366 // Insert "ffname" after this entry (and comma). | |
367 insp = entry; | |
368 } | |
369 | |
370 if ((p = (char_u *)strstr((char *)buf, "after")) != NULL | |
371 && p > buf | |
372 && vim_ispathsep(p[-1]) | |
373 && (vim_ispathsep(p[5]) || p[5] == NUL || p[5] == ',')) | |
374 { | |
375 if (insp == NULL) | |
376 // Did not find "ffname" before the first "after" directory, | |
377 // insert it before this entry. | |
378 insp = cur_entry; | |
379 after_insp = cur_entry; | |
380 break; | |
381 } | |
382 } | |
383 | |
384 if (insp == NULL) | |
385 // Both "fname" and "after" not found, append at the end. | |
386 insp = p_rtp + STRLEN(p_rtp); | |
387 | |
388 // check if rtp/pack/name/start/name/after exists | |
389 afterdir = concat_fnames(fname, (char_u *)"after", TRUE); | |
390 if (afterdir != NULL && mch_isdir(afterdir)) | |
391 afterlen = STRLEN(afterdir) + 1; // add one for comma | |
392 | |
393 oldlen = STRLEN(p_rtp); | |
394 addlen = STRLEN(fname) + 1; // add one for comma | |
395 new_rtp = alloc(oldlen + addlen + afterlen + 1); // add one for NUL | |
396 if (new_rtp == NULL) | |
397 goto theend; | |
398 | |
399 // We now have 'rtp' parts: {keep}{keep_after}{rest}. | |
400 // Create new_rtp, first: {keep},{fname} | |
401 keep = (int)(insp - p_rtp); | |
402 mch_memmove(new_rtp, p_rtp, keep); | |
403 new_rtp_len = keep; | |
404 if (*insp == NUL) | |
405 new_rtp[new_rtp_len++] = ','; // add comma before | |
406 mch_memmove(new_rtp + new_rtp_len, fname, addlen - 1); | |
407 new_rtp_len += addlen - 1; | |
408 if (*insp != NUL) | |
409 new_rtp[new_rtp_len++] = ','; // add comma after | |
410 | |
411 if (afterlen > 0 && after_insp != NULL) | |
412 { | |
413 int keep_after = (int)(after_insp - p_rtp); | |
414 | |
415 // Add to new_rtp: {keep},{fname}{keep_after},{afterdir} | |
416 mch_memmove(new_rtp + new_rtp_len, p_rtp + keep, | |
417 keep_after - keep); | |
418 new_rtp_len += keep_after - keep; | |
419 mch_memmove(new_rtp + new_rtp_len, afterdir, afterlen - 1); | |
420 new_rtp_len += afterlen - 1; | |
421 new_rtp[new_rtp_len++] = ','; | |
422 keep = keep_after; | |
423 } | |
424 | |
425 if (p_rtp[keep] != NUL) | |
426 // Append rest: {keep},{fname}{keep_after},{afterdir}{rest} | |
427 mch_memmove(new_rtp + new_rtp_len, p_rtp + keep, oldlen - keep + 1); | |
428 else | |
429 new_rtp[new_rtp_len] = NUL; | |
430 | |
431 if (afterlen > 0 && after_insp == NULL) | |
432 { | |
433 // Append afterdir when "after" was not found: | |
434 // {keep},{fname}{rest},{afterdir} | |
435 STRCAT(new_rtp, ","); | |
436 STRCAT(new_rtp, afterdir); | |
437 } | |
438 | |
439 set_option_value((char_u *)"rtp", 0L, new_rtp, 0); | |
440 vim_free(new_rtp); | |
441 retval = OK; | |
442 | |
443 theend: | |
444 vim_free(buf); | |
445 vim_free(ffname); | |
446 vim_free(afterdir); | |
447 return retval; | |
448 } | |
449 | |
450 /* | |
451 * Load scripts in "plugin" and "ftdetect" directories of the package. | |
452 */ | |
453 static int | |
454 load_pack_plugin(char_u *fname) | |
455 { | |
456 static char *plugpat = "%s/plugin/**/*.vim"; | |
457 static char *ftpat = "%s/ftdetect/*.vim"; | |
458 int len; | |
459 char_u *ffname = fix_fname(fname); | |
460 char_u *pat = NULL; | |
461 int retval = FAIL; | |
462 | |
463 if (ffname == NULL) | |
464 return FAIL; | |
465 len = (int)STRLEN(ffname) + (int)STRLEN(ftpat); | |
466 pat = alloc(len); | |
467 if (pat == NULL) | |
468 goto theend; | |
469 vim_snprintf((char *)pat, len, plugpat, ffname); | |
470 source_all_matches(pat); | |
471 | |
472 { | |
473 char_u *cmd = vim_strsave((char_u *)"g:did_load_filetypes"); | |
474 | |
475 // If runtime/filetype.vim wasn't loaded yet, the scripts will be | |
476 // found when it loads. | |
477 if (cmd != NULL && eval_to_number(cmd) > 0) | |
478 { | |
479 do_cmdline_cmd((char_u *)"augroup filetypedetect"); | |
480 vim_snprintf((char *)pat, len, ftpat, ffname); | |
481 source_all_matches(pat); | |
482 do_cmdline_cmd((char_u *)"augroup END"); | |
483 } | |
484 vim_free(cmd); | |
485 } | |
486 vim_free(pat); | |
487 retval = OK; | |
488 | |
489 theend: | |
490 vim_free(ffname); | |
491 return retval; | |
492 } | |
493 | |
494 // used for "cookie" of add_pack_plugin() | |
495 static int APP_ADD_DIR; | |
496 static int APP_LOAD; | |
497 static int APP_BOTH; | |
498 | |
499 static void | |
500 add_pack_plugin(char_u *fname, void *cookie) | |
501 { | |
502 if (cookie != &APP_LOAD) | |
503 { | |
504 char_u *buf = alloc(MAXPATHL); | |
505 char_u *p; | |
506 int found = FALSE; | |
507 | |
508 if (buf == NULL) | |
509 return; | |
510 p = p_rtp; | |
511 while (*p != NUL) | |
512 { | |
513 copy_option_part(&p, buf, MAXPATHL, ","); | |
514 if (pathcmp((char *)buf, (char *)fname, -1) == 0) | |
515 { | |
516 found = TRUE; | |
517 break; | |
518 } | |
519 } | |
520 vim_free(buf); | |
521 if (!found) | |
522 // directory is not yet in 'runtimepath', add it | |
523 if (add_pack_dir_to_rtp(fname) == FAIL) | |
524 return; | |
525 } | |
526 | |
527 if (cookie != &APP_ADD_DIR) | |
528 load_pack_plugin(fname); | |
529 } | |
530 | |
531 /* | |
532 * Add all packages in the "start" directory to 'runtimepath'. | |
533 */ | |
534 void | |
535 add_pack_start_dirs(void) | |
536 { | |
537 do_in_path(p_pp, (char_u *)"pack/*/start/*", DIP_ALL + DIP_DIR, | |
538 add_pack_plugin, &APP_ADD_DIR); | |
539 } | |
540 | |
541 /* | |
542 * Load plugins from all packages in the "start" directory. | |
543 */ | |
544 void | |
545 load_start_packages(void) | |
546 { | |
547 did_source_packages = TRUE; | |
548 do_in_path(p_pp, (char_u *)"pack/*/start/*", DIP_ALL + DIP_DIR, | |
549 add_pack_plugin, &APP_LOAD); | |
550 } | |
551 | |
552 /* | |
553 * ":packloadall" | |
554 * Find plugins in the package directories and source them. | |
555 */ | |
556 void | |
557 ex_packloadall(exarg_T *eap) | |
558 { | |
559 if (!did_source_packages || eap->forceit) | |
560 { | |
561 // First do a round to add all directories to 'runtimepath', then load | |
562 // the plugins. This allows for plugins to use an autoload directory | |
563 // of another plugin. | |
564 add_pack_start_dirs(); | |
565 load_start_packages(); | |
566 } | |
567 } | |
568 | |
569 /* | |
570 * ":packadd[!] {name}" | |
571 */ | |
572 void | |
573 ex_packadd(exarg_T *eap) | |
574 { | |
575 static char *plugpat = "pack/*/%s/%s"; | |
576 int len; | |
577 char *pat; | |
578 int round; | |
579 int res = OK; | |
580 | |
581 // Round 1: use "start", round 2: use "opt". | |
582 for (round = 1; round <= 2; ++round) | |
583 { | |
584 // Only look under "start" when loading packages wasn't done yet. | |
585 if (round == 1 && did_source_packages) | |
586 continue; | |
587 | |
588 len = (int)STRLEN(plugpat) + (int)STRLEN(eap->arg) + 5; | |
589 pat = alloc(len); | |
590 if (pat == NULL) | |
591 return; | |
592 vim_snprintf(pat, len, plugpat, round == 1 ? "start" : "opt", eap->arg); | |
593 // The first round don't give a "not found" error, in the second round | |
594 // only when nothing was found in the first round. | |
595 res = do_in_path(p_pp, (char_u *)pat, | |
596 DIP_ALL + DIP_DIR + (round == 2 && res == FAIL ? DIP_ERR : 0), | |
597 add_pack_plugin, eap->forceit ? &APP_ADD_DIR : &APP_BOTH); | |
598 vim_free(pat); | |
599 } | |
600 } | |
601 #endif | |
602 | |
603 /* | |
604 * Expand color scheme, compiler or filetype names. | |
605 * Search from 'runtimepath': | |
606 * 'runtimepath'/{dirnames}/{pat}.vim | |
607 * When "flags" has DIP_START: search also from 'start' of 'packpath': | |
608 * 'packpath'/pack/ * /start/ * /{dirnames}/{pat}.vim | |
609 * When "flags" has DIP_OPT: search also from 'opt' of 'packpath': | |
610 * 'packpath'/pack/ * /opt/ * /{dirnames}/{pat}.vim | |
611 * "dirnames" is an array with one or more directory names. | |
612 */ | |
613 int | |
614 ExpandRTDir( | |
615 char_u *pat, | |
616 int flags, | |
617 int *num_file, | |
618 char_u ***file, | |
619 char *dirnames[]) | |
620 { | |
621 char_u *s; | |
622 char_u *e; | |
623 char_u *match; | |
624 garray_T ga; | |
625 int i; | |
626 int pat_len; | |
627 | |
628 *num_file = 0; | |
629 *file = NULL; | |
630 pat_len = (int)STRLEN(pat); | |
631 ga_init2(&ga, (int)sizeof(char *), 10); | |
632 | |
633 for (i = 0; dirnames[i] != NULL; ++i) | |
634 { | |
635 s = alloc(STRLEN(dirnames[i]) + pat_len + 7); | |
636 if (s == NULL) | |
637 { | |
638 ga_clear_strings(&ga); | |
639 return FAIL; | |
640 } | |
641 sprintf((char *)s, "%s/%s*.vim", dirnames[i], pat); | |
642 globpath(p_rtp, s, &ga, 0); | |
643 vim_free(s); | |
644 } | |
645 | |
646 if (flags & DIP_START) { | |
647 for (i = 0; dirnames[i] != NULL; ++i) | |
648 { | |
649 s = alloc(STRLEN(dirnames[i]) + pat_len + 22); | |
650 if (s == NULL) | |
651 { | |
652 ga_clear_strings(&ga); | |
653 return FAIL; | |
654 } | |
655 sprintf((char *)s, "pack/*/start/*/%s/%s*.vim", dirnames[i], pat); | |
656 globpath(p_pp, s, &ga, 0); | |
657 vim_free(s); | |
658 } | |
659 } | |
660 | |
661 if (flags & DIP_OPT) { | |
662 for (i = 0; dirnames[i] != NULL; ++i) | |
663 { | |
664 s = alloc(STRLEN(dirnames[i]) + pat_len + 20); | |
665 if (s == NULL) | |
666 { | |
667 ga_clear_strings(&ga); | |
668 return FAIL; | |
669 } | |
670 sprintf((char *)s, "pack/*/opt/*/%s/%s*.vim", dirnames[i], pat); | |
671 globpath(p_pp, s, &ga, 0); | |
672 vim_free(s); | |
673 } | |
674 } | |
675 | |
676 for (i = 0; i < ga.ga_len; ++i) | |
677 { | |
678 match = ((char_u **)ga.ga_data)[i]; | |
679 s = match; | |
680 e = s + STRLEN(s); | |
681 if (e - 4 > s && STRNICMP(e - 4, ".vim", 4) == 0) | |
682 { | |
683 e -= 4; | |
684 for (s = e; s > match; MB_PTR_BACK(match, s)) | |
685 if (s < match || vim_ispathsep(*s)) | |
686 break; | |
687 ++s; | |
688 *e = NUL; | |
689 mch_memmove(match, s, e - s + 1); | |
690 } | |
691 } | |
692 | |
693 if (ga.ga_len == 0) | |
694 return FAIL; | |
695 | |
696 // Sort and remove duplicates which can happen when specifying multiple | |
697 // directories in dirnames. | |
698 remove_duplicates(&ga); | |
699 | |
700 *file = ga.ga_data; | |
701 *num_file = ga.ga_len; | |
702 return OK; | |
703 } | |
704 | |
705 /* | |
706 * Expand loadplugin names: | |
707 * 'packpath'/pack/ * /opt/{pat} | |
708 */ | |
709 int | |
710 ExpandPackAddDir( | |
711 char_u *pat, | |
712 int *num_file, | |
713 char_u ***file) | |
714 { | |
715 char_u *s; | |
716 char_u *e; | |
717 char_u *match; | |
718 garray_T ga; | |
719 int i; | |
720 int pat_len; | |
721 | |
722 *num_file = 0; | |
723 *file = NULL; | |
724 pat_len = (int)STRLEN(pat); | |
725 ga_init2(&ga, (int)sizeof(char *), 10); | |
726 | |
727 s = alloc(pat_len + 26); | |
728 if (s == NULL) | |
729 { | |
730 ga_clear_strings(&ga); | |
731 return FAIL; | |
732 } | |
733 sprintf((char *)s, "pack/*/opt/%s*", pat); | |
734 globpath(p_pp, s, &ga, 0); | |
735 vim_free(s); | |
736 | |
737 for (i = 0; i < ga.ga_len; ++i) | |
738 { | |
739 match = ((char_u **)ga.ga_data)[i]; | |
740 s = gettail(match); | |
741 e = s + STRLEN(s); | |
742 mch_memmove(match, s, e - s + 1); | |
743 } | |
744 | |
745 if (ga.ga_len == 0) | |
746 return FAIL; | |
747 | |
748 // Sort and remove duplicates which can happen when specifying multiple | |
749 // directories in dirnames. | |
750 remove_duplicates(&ga); | |
751 | |
752 *file = ga.ga_data; | |
753 *num_file = ga.ga_len; | |
754 return OK; | |
755 } | |
756 | |
757 static void | |
758 cmd_source(char_u *fname, exarg_T *eap) | |
759 { | |
760 if (*fname == NUL) | |
761 emsg(_(e_argreq)); | |
762 | |
763 else if (eap != NULL && eap->forceit) | |
764 // ":source!": read Normal mode commands | |
765 // Need to execute the commands directly. This is required at least | |
766 // for: | |
767 // - ":g" command busy | |
768 // - after ":argdo", ":windo" or ":bufdo" | |
769 // - another command follows | |
770 // - inside a loop | |
771 openscript(fname, global_busy || listcmd_busy || eap->nextcmd != NULL | |
772 #ifdef FEAT_EVAL | |
773 || eap->cstack->cs_idx >= 0 | |
774 #endif | |
775 ); | |
776 | |
777 // ":source" read ex commands | |
778 else if (do_source(fname, FALSE, DOSO_NONE) == FAIL) | |
779 semsg(_(e_notopen), fname); | |
780 } | |
781 | |
782 /* | |
783 * ":source {fname}" | |
784 */ | |
785 void | |
786 ex_source(exarg_T *eap) | |
787 { | |
788 #ifdef FEAT_BROWSE | |
789 if (cmdmod.browse) | |
790 { | |
791 char_u *fname = NULL; | |
792 | |
793 fname = do_browse(0, (char_u *)_("Source Vim script"), eap->arg, | |
794 NULL, NULL, | |
795 (char_u *)_(BROWSE_FILTER_MACROS), NULL); | |
796 if (fname != NULL) | |
797 { | |
798 cmd_source(fname, eap); | |
799 vim_free(fname); | |
800 } | |
801 } | |
802 else | |
803 #endif | |
804 cmd_source(eap->arg, eap); | |
805 } | |
806 | |
807 #if defined(FEAT_EVAL) || defined(PROTO) | |
808 /* | |
809 * ":options" | |
810 */ | |
811 void | |
812 ex_options( | |
813 exarg_T *eap UNUSED) | |
814 { | |
815 vim_setenv((char_u *)"OPTWIN_CMD", | |
816 (char_u *)(cmdmod.tab ? "tab" | |
817 : (cmdmod.split & WSP_VERT) ? "vert" : "")); | |
818 cmd_source((char_u *)SYS_OPTWIN_FILE, NULL); | |
819 } | |
820 #endif | |
821 | |
822 /* | |
823 * ":source" and associated commands. | |
824 */ | |
825 /* | |
826 * Structure used to store info for each sourced file. | |
827 * It is shared between do_source() and getsourceline(). | |
828 * This is required, because it needs to be handed to do_cmdline() and | |
829 * sourcing can be done recursively. | |
830 */ | |
831 struct source_cookie | |
832 { | |
833 FILE *fp; // opened file for sourcing | |
834 char_u *nextline; // if not NULL: line that was read ahead | |
835 linenr_T sourcing_lnum; // line number of the source file | |
836 int finished; // ":finish" used | |
837 #ifdef USE_CRNL | |
838 int fileformat; // EOL_UNKNOWN, EOL_UNIX or EOL_DOS | |
839 int error; // TRUE if LF found after CR-LF | |
840 #endif | |
841 #ifdef FEAT_EVAL | |
842 linenr_T breakpoint; // next line with breakpoint or zero | |
843 char_u *fname; // name of sourced file | |
844 int dbg_tick; // debug_tick when breakpoint was set | |
845 int level; // top nesting level of sourced file | |
846 #endif | |
847 vimconv_T conv; // type of conversion | |
848 }; | |
849 | |
850 #ifdef FEAT_EVAL | |
851 /* | |
852 * Return the address holding the next breakpoint line for a source cookie. | |
853 */ | |
854 linenr_T * | |
855 source_breakpoint(void *cookie) | |
856 { | |
857 return &((struct source_cookie *)cookie)->breakpoint; | |
858 } | |
859 | |
860 /* | |
861 * Return the address holding the debug tick for a source cookie. | |
862 */ | |
863 int * | |
864 source_dbg_tick(void *cookie) | |
865 { | |
866 return &((struct source_cookie *)cookie)->dbg_tick; | |
867 } | |
868 | |
869 /* | |
870 * Return the nesting level for a source cookie. | |
871 */ | |
872 int | |
873 source_level(void *cookie) | |
874 { | |
875 return ((struct source_cookie *)cookie)->level; | |
876 } | |
877 #endif | |
878 | |
879 #if (defined(MSWIN) && defined(FEAT_CSCOPE)) || defined(HAVE_FD_CLOEXEC) | |
880 # define USE_FOPEN_NOINH | |
881 /* | |
882 * Special function to open a file without handle inheritance. | |
883 * When possible the handle is closed on exec(). | |
884 */ | |
885 static FILE * | |
886 fopen_noinh_readbin(char *filename) | |
887 { | |
888 # ifdef MSWIN | |
889 int fd_tmp = mch_open(filename, O_RDONLY | O_BINARY | O_NOINHERIT, 0); | |
890 # else | |
891 int fd_tmp = mch_open(filename, O_RDONLY, 0); | |
892 # endif | |
893 | |
894 if (fd_tmp == -1) | |
895 return NULL; | |
896 | |
897 # ifdef HAVE_FD_CLOEXEC | |
898 { | |
899 int fdflags = fcntl(fd_tmp, F_GETFD); | |
900 if (fdflags >= 0 && (fdflags & FD_CLOEXEC) == 0) | |
901 (void)fcntl(fd_tmp, F_SETFD, fdflags | FD_CLOEXEC); | |
902 } | |
903 # endif | |
904 | |
905 return fdopen(fd_tmp, READBIN); | |
906 } | |
907 #endif | |
908 | |
909 /* | |
910 * do_source: Read the file "fname" and execute its lines as EX commands. | |
911 * | |
912 * This function may be called recursively! | |
913 * | |
914 * return FAIL if file could not be opened, OK otherwise | |
915 */ | |
916 int | |
917 do_source( | |
918 char_u *fname, | |
919 int check_other, // check for .vimrc and _vimrc | |
920 int is_vimrc) // DOSO_ value | |
921 { | |
922 struct source_cookie cookie; | |
923 char_u *save_sourcing_name; | |
924 linenr_T save_sourcing_lnum; | |
925 char_u *p; | |
926 char_u *fname_exp; | |
927 char_u *firstline = NULL; | |
928 int retval = FAIL; | |
929 #ifdef FEAT_EVAL | |
930 sctx_T save_current_sctx; | |
931 static scid_T last_current_SID = 0; | |
932 static int last_current_SID_seq = 0; | |
933 funccal_entry_T funccalp_entry; | |
934 int save_debug_break_level = debug_break_level; | |
935 scriptitem_T *si = NULL; | |
936 # ifdef UNIX | |
937 stat_T st; | |
938 int stat_ok; | |
939 # endif | |
940 #endif | |
941 #ifdef STARTUPTIME | |
942 struct timeval tv_rel; | |
943 struct timeval tv_start; | |
944 #endif | |
945 #ifdef FEAT_PROFILE | |
946 proftime_T wait_start; | |
947 #endif | |
948 int trigger_source_post = FALSE; | |
949 | |
950 p = expand_env_save(fname); | |
951 if (p == NULL) | |
952 return retval; | |
953 fname_exp = fix_fname(p); | |
954 vim_free(p); | |
955 if (fname_exp == NULL) | |
956 return retval; | |
957 if (mch_isdir(fname_exp)) | |
958 { | |
959 smsg(_("Cannot source a directory: \"%s\""), fname); | |
960 goto theend; | |
961 } | |
962 | |
963 // Apply SourceCmd autocommands, they should get the file and source it. | |
964 if (has_autocmd(EVENT_SOURCECMD, fname_exp, NULL) | |
965 && apply_autocmds(EVENT_SOURCECMD, fname_exp, fname_exp, | |
966 FALSE, curbuf)) | |
967 { | |
968 #ifdef FEAT_EVAL | |
969 retval = aborting() ? FAIL : OK; | |
970 #else | |
971 retval = OK; | |
972 #endif | |
973 if (retval == OK) | |
974 // Apply SourcePost autocommands. | |
975 apply_autocmds(EVENT_SOURCEPOST, fname_exp, fname_exp, | |
976 FALSE, curbuf); | |
977 goto theend; | |
978 } | |
979 | |
980 // Apply SourcePre autocommands, they may get the file. | |
981 apply_autocmds(EVENT_SOURCEPRE, fname_exp, fname_exp, FALSE, curbuf); | |
982 | |
983 #ifdef USE_FOPEN_NOINH | |
984 cookie.fp = fopen_noinh_readbin((char *)fname_exp); | |
985 #else | |
986 cookie.fp = mch_fopen((char *)fname_exp, READBIN); | |
987 #endif | |
988 if (cookie.fp == NULL && check_other) | |
989 { | |
990 // Try again, replacing file name ".vimrc" by "_vimrc" or vice versa, | |
991 // and ".exrc" by "_exrc" or vice versa. | |
992 p = gettail(fname_exp); | |
993 if ((*p == '.' || *p == '_') | |
994 && (STRICMP(p + 1, "vimrc") == 0 | |
995 || STRICMP(p + 1, "gvimrc") == 0 | |
996 || STRICMP(p + 1, "exrc") == 0)) | |
997 { | |
998 if (*p == '_') | |
999 *p = '.'; | |
1000 else | |
1001 *p = '_'; | |
1002 #ifdef USE_FOPEN_NOINH | |
1003 cookie.fp = fopen_noinh_readbin((char *)fname_exp); | |
1004 #else | |
1005 cookie.fp = mch_fopen((char *)fname_exp, READBIN); | |
1006 #endif | |
1007 } | |
1008 } | |
1009 | |
1010 if (cookie.fp == NULL) | |
1011 { | |
1012 if (p_verbose > 0) | |
1013 { | |
1014 verbose_enter(); | |
1015 if (sourcing_name == NULL) | |
1016 smsg(_("could not source \"%s\""), fname); | |
1017 else | |
1018 smsg(_("line %ld: could not source \"%s\""), | |
1019 sourcing_lnum, fname); | |
1020 verbose_leave(); | |
1021 } | |
1022 goto theend; | |
1023 } | |
1024 | |
1025 // The file exists. | |
1026 // - In verbose mode, give a message. | |
1027 // - For a vimrc file, may want to set 'compatible', call vimrc_found(). | |
1028 if (p_verbose > 1) | |
1029 { | |
1030 verbose_enter(); | |
1031 if (sourcing_name == NULL) | |
1032 smsg(_("sourcing \"%s\""), fname); | |
1033 else | |
1034 smsg(_("line %ld: sourcing \"%s\""), | |
1035 sourcing_lnum, fname); | |
1036 verbose_leave(); | |
1037 } | |
1038 if (is_vimrc == DOSO_VIMRC) | |
1039 vimrc_found(fname_exp, (char_u *)"MYVIMRC"); | |
1040 else if (is_vimrc == DOSO_GVIMRC) | |
1041 vimrc_found(fname_exp, (char_u *)"MYGVIMRC"); | |
1042 | |
1043 #ifdef USE_CRNL | |
1044 // If no automatic file format: Set default to CR-NL. | |
1045 if (*p_ffs == NUL) | |
1046 cookie.fileformat = EOL_DOS; | |
1047 else | |
1048 cookie.fileformat = EOL_UNKNOWN; | |
1049 cookie.error = FALSE; | |
1050 #endif | |
1051 | |
1052 cookie.nextline = NULL; | |
1053 cookie.sourcing_lnum = 0; | |
1054 cookie.finished = FALSE; | |
1055 | |
1056 #ifdef FEAT_EVAL | |
1057 // Check if this script has a breakpoint. | |
1058 cookie.breakpoint = dbg_find_breakpoint(TRUE, fname_exp, (linenr_T)0); | |
1059 cookie.fname = fname_exp; | |
1060 cookie.dbg_tick = debug_tick; | |
1061 | |
1062 cookie.level = ex_nesting_level; | |
1063 #endif | |
1064 | |
1065 // Keep the sourcing name/lnum, for recursive calls. | |
1066 save_sourcing_name = sourcing_name; | |
1067 sourcing_name = fname_exp; | |
1068 save_sourcing_lnum = sourcing_lnum; | |
1069 sourcing_lnum = 0; | |
1070 | |
1071 #ifdef STARTUPTIME | |
1072 if (time_fd != NULL) | |
1073 time_push(&tv_rel, &tv_start); | |
1074 #endif | |
1075 | |
1076 #ifdef FEAT_EVAL | |
1077 # ifdef FEAT_PROFILE | |
1078 if (do_profiling == PROF_YES) | |
1079 prof_child_enter(&wait_start); // entering a child now | |
1080 # endif | |
1081 | |
1082 // Don't use local function variables, if called from a function. | |
1083 // Also starts profiling timer for nested script. | |
1084 save_funccal(&funccalp_entry); | |
1085 | |
1086 save_current_sctx = current_sctx; | |
1087 current_sctx.sc_lnum = 0; | |
1088 current_sctx.sc_version = 1; | |
1089 | |
1090 // Check if this script was sourced before to finds its SID. | |
1091 // If it's new, generate a new SID. | |
1092 // Always use a new sequence number. | |
1093 current_sctx.sc_seq = ++last_current_SID_seq; | |
1094 # ifdef UNIX | |
1095 stat_ok = (mch_stat((char *)fname_exp, &st) >= 0); | |
1096 # endif | |
1097 for (current_sctx.sc_sid = script_items.ga_len; current_sctx.sc_sid > 0; | |
1098 --current_sctx.sc_sid) | |
1099 { | |
1100 si = &SCRIPT_ITEM(current_sctx.sc_sid); | |
1101 if (si->sn_name != NULL | |
1102 && ( | |
1103 # ifdef UNIX | |
1104 // Compare dev/ino when possible, it catches symbolic | |
1105 // links. Also compare file names, the inode may change | |
1106 // when the file was edited. | |
1107 ((stat_ok && si->sn_dev_valid) | |
1108 && (si->sn_dev == st.st_dev | |
1109 && si->sn_ino == st.st_ino)) || | |
1110 # endif | |
1111 fnamecmp(si->sn_name, fname_exp) == 0)) | |
1112 break; | |
1113 } | |
1114 if (current_sctx.sc_sid == 0) | |
1115 { | |
1116 current_sctx.sc_sid = ++last_current_SID; | |
1117 if (ga_grow(&script_items, | |
1118 (int)(current_sctx.sc_sid - script_items.ga_len)) == FAIL) | |
1119 goto almosttheend; | |
1120 while (script_items.ga_len < current_sctx.sc_sid) | |
1121 { | |
1122 ++script_items.ga_len; | |
1123 SCRIPT_ITEM(script_items.ga_len).sn_name = NULL; | |
1124 # ifdef FEAT_PROFILE | |
1125 SCRIPT_ITEM(script_items.ga_len).sn_prof_on = FALSE; | |
1126 # endif | |
1127 } | |
1128 si = &SCRIPT_ITEM(current_sctx.sc_sid); | |
1129 si->sn_name = fname_exp; | |
1130 fname_exp = vim_strsave(si->sn_name); // used for autocmd | |
1131 # ifdef UNIX | |
1132 if (stat_ok) | |
1133 { | |
1134 si->sn_dev_valid = TRUE; | |
1135 si->sn_dev = st.st_dev; | |
1136 si->sn_ino = st.st_ino; | |
1137 } | |
1138 else | |
1139 si->sn_dev_valid = FALSE; | |
1140 # endif | |
1141 | |
1142 // Allocate the local script variables to use for this script. | |
1143 new_script_vars(current_sctx.sc_sid); | |
1144 } | |
1145 | |
1146 # ifdef FEAT_PROFILE | |
1147 if (do_profiling == PROF_YES) | |
1148 { | |
1149 int forceit; | |
1150 | |
1151 // Check if we do profiling for this script. | |
1152 if (!si->sn_prof_on && has_profiling(TRUE, si->sn_name, &forceit)) | |
1153 { | |
1154 script_do_profile(si); | |
1155 si->sn_pr_force = forceit; | |
1156 } | |
1157 if (si->sn_prof_on) | |
1158 { | |
1159 ++si->sn_pr_count; | |
1160 profile_start(&si->sn_pr_start); | |
1161 profile_zero(&si->sn_pr_children); | |
1162 } | |
1163 } | |
1164 # endif | |
1165 #endif | |
1166 | |
1167 cookie.conv.vc_type = CONV_NONE; // no conversion | |
1168 | |
1169 // Read the first line so we can check for a UTF-8 BOM. | |
1170 firstline = getsourceline(0, (void *)&cookie, 0, TRUE); | |
1171 if (firstline != NULL && STRLEN(firstline) >= 3 && firstline[0] == 0xef | |
1172 && firstline[1] == 0xbb && firstline[2] == 0xbf) | |
1173 { | |
1174 // Found BOM; setup conversion, skip over BOM and recode the line. | |
1175 convert_setup(&cookie.conv, (char_u *)"utf-8", p_enc); | |
1176 p = string_convert(&cookie.conv, firstline + 3, NULL); | |
1177 if (p == NULL) | |
1178 p = vim_strsave(firstline + 3); | |
1179 if (p != NULL) | |
1180 { | |
1181 vim_free(firstline); | |
1182 firstline = p; | |
1183 } | |
1184 } | |
1185 | |
1186 // Call do_cmdline, which will call getsourceline() to get the lines. | |
1187 do_cmdline(firstline, getsourceline, (void *)&cookie, | |
1188 DOCMD_VERBOSE|DOCMD_NOWAIT|DOCMD_REPEAT); | |
1189 retval = OK; | |
1190 | |
1191 #ifdef FEAT_PROFILE | |
1192 if (do_profiling == PROF_YES) | |
1193 { | |
1194 // Get "si" again, "script_items" may have been reallocated. | |
1195 si = &SCRIPT_ITEM(current_sctx.sc_sid); | |
1196 if (si->sn_prof_on) | |
1197 { | |
1198 profile_end(&si->sn_pr_start); | |
1199 profile_sub_wait(&wait_start, &si->sn_pr_start); | |
1200 profile_add(&si->sn_pr_total, &si->sn_pr_start); | |
1201 profile_self(&si->sn_pr_self, &si->sn_pr_start, | |
1202 &si->sn_pr_children); | |
1203 } | |
1204 } | |
1205 #endif | |
1206 | |
1207 if (got_int) | |
1208 emsg(_(e_interr)); | |
1209 sourcing_name = save_sourcing_name; | |
1210 sourcing_lnum = save_sourcing_lnum; | |
1211 if (p_verbose > 1) | |
1212 { | |
1213 verbose_enter(); | |
1214 smsg(_("finished sourcing %s"), fname); | |
1215 if (sourcing_name != NULL) | |
1216 smsg(_("continuing in %s"), sourcing_name); | |
1217 verbose_leave(); | |
1218 } | |
1219 #ifdef STARTUPTIME | |
1220 if (time_fd != NULL) | |
1221 { | |
1222 vim_snprintf((char *)IObuff, IOSIZE, "sourcing %s", fname); | |
1223 time_msg((char *)IObuff, &tv_start); | |
1224 time_pop(&tv_rel); | |
1225 } | |
1226 #endif | |
1227 | |
1228 if (!got_int) | |
1229 trigger_source_post = TRUE; | |
1230 | |
1231 #ifdef FEAT_EVAL | |
1232 // After a "finish" in debug mode, need to break at first command of next | |
1233 // sourced file. | |
1234 if (save_debug_break_level > ex_nesting_level | |
1235 && debug_break_level == ex_nesting_level) | |
1236 ++debug_break_level; | |
1237 #endif | |
1238 | |
1239 #ifdef FEAT_EVAL | |
1240 almosttheend: | |
1241 current_sctx = save_current_sctx; | |
1242 restore_funccal(); | |
1243 # ifdef FEAT_PROFILE | |
1244 if (do_profiling == PROF_YES) | |
1245 prof_child_exit(&wait_start); // leaving a child now | |
1246 # endif | |
1247 #endif | |
1248 fclose(cookie.fp); | |
1249 vim_free(cookie.nextline); | |
1250 vim_free(firstline); | |
1251 convert_setup(&cookie.conv, NULL, NULL); | |
1252 | |
1253 if (trigger_source_post) | |
1254 apply_autocmds(EVENT_SOURCEPOST, fname_exp, fname_exp, FALSE, curbuf); | |
1255 | |
1256 theend: | |
1257 vim_free(fname_exp); | |
1258 return retval; | |
1259 } | |
1260 | |
1261 #if defined(FEAT_EVAL) || defined(PROTO) | |
1262 | |
1263 /* | |
1264 * ":scriptnames" | |
1265 */ | |
1266 void | |
1267 ex_scriptnames(exarg_T *eap) | |
1268 { | |
1269 int i; | |
1270 | |
1271 if (eap->addr_count > 0) | |
1272 { | |
1273 // :script {scriptId}: edit the script | |
1274 if (eap->line2 < 1 || eap->line2 > script_items.ga_len) | |
1275 emsg(_(e_invarg)); | |
1276 else | |
1277 { | |
1278 eap->arg = SCRIPT_ITEM(eap->line2).sn_name; | |
1279 do_exedit(eap, NULL); | |
1280 } | |
1281 return; | |
1282 } | |
1283 | |
1284 for (i = 1; i <= script_items.ga_len && !got_int; ++i) | |
1285 if (SCRIPT_ITEM(i).sn_name != NULL) | |
1286 { | |
1287 home_replace(NULL, SCRIPT_ITEM(i).sn_name, | |
1288 NameBuff, MAXPATHL, TRUE); | |
1289 smsg("%3d: %s", i, NameBuff); | |
1290 } | |
1291 } | |
1292 | |
1293 # if defined(BACKSLASH_IN_FILENAME) || defined(PROTO) | |
1294 /* | |
1295 * Fix slashes in the list of script names for 'shellslash'. | |
1296 */ | |
1297 void | |
1298 scriptnames_slash_adjust(void) | |
1299 { | |
1300 int i; | |
1301 | |
1302 for (i = 1; i <= script_items.ga_len; ++i) | |
1303 if (SCRIPT_ITEM(i).sn_name != NULL) | |
1304 slash_adjust(SCRIPT_ITEM(i).sn_name); | |
1305 } | |
1306 # endif | |
1307 | |
1308 /* | |
1309 * Get a pointer to a script name. Used for ":verbose set". | |
1310 */ | |
1311 char_u * | |
1312 get_scriptname(scid_T id) | |
1313 { | |
1314 if (id == SID_MODELINE) | |
1315 return (char_u *)_("modeline"); | |
1316 if (id == SID_CMDARG) | |
1317 return (char_u *)_("--cmd argument"); | |
1318 if (id == SID_CARG) | |
1319 return (char_u *)_("-c argument"); | |
1320 if (id == SID_ENV) | |
1321 return (char_u *)_("environment variable"); | |
1322 if (id == SID_ERROR) | |
1323 return (char_u *)_("error handler"); | |
1324 return SCRIPT_ITEM(id).sn_name; | |
1325 } | |
1326 | |
1327 # if defined(EXITFREE) || defined(PROTO) | |
1328 void | |
1329 free_scriptnames(void) | |
1330 { | |
1331 int i; | |
1332 | |
1333 for (i = script_items.ga_len; i > 0; --i) | |
1334 vim_free(SCRIPT_ITEM(i).sn_name); | |
1335 ga_clear(&script_items); | |
1336 } | |
1337 # endif | |
1338 | |
1339 #endif | |
1340 | |
1341 linenr_T | |
1342 get_sourced_lnum(char_u *(*fgetline)(int, void *, int, int), void *cookie) | |
1343 { | |
1344 return fgetline == getsourceline | |
1345 ? ((struct source_cookie *)cookie)->sourcing_lnum | |
1346 : sourcing_lnum; | |
1347 } | |
1348 | |
1349 static char_u * | |
1350 get_one_sourceline(struct source_cookie *sp) | |
1351 { | |
1352 garray_T ga; | |
1353 int len; | |
1354 int c; | |
1355 char_u *buf; | |
1356 #ifdef USE_CRNL | |
1357 int has_cr; // CR-LF found | |
1358 #endif | |
1359 int have_read = FALSE; | |
1360 | |
1361 // use a growarray to store the sourced line | |
1362 ga_init2(&ga, 1, 250); | |
1363 | |
1364 // Loop until there is a finished line (or end-of-file). | |
1365 ++sp->sourcing_lnum; | |
1366 for (;;) | |
1367 { | |
1368 // make room to read at least 120 (more) characters | |
1369 if (ga_grow(&ga, 120) == FAIL) | |
1370 break; | |
1371 buf = (char_u *)ga.ga_data; | |
1372 | |
1373 if (fgets((char *)buf + ga.ga_len, ga.ga_maxlen - ga.ga_len, | |
1374 sp->fp) == NULL) | |
1375 break; | |
1376 len = ga.ga_len + (int)STRLEN(buf + ga.ga_len); | |
1377 #ifdef USE_CRNL | |
1378 // Ignore a trailing CTRL-Z, when in Dos mode. Only recognize the | |
1379 // CTRL-Z by its own, or after a NL. | |
1380 if ( (len == 1 || (len >= 2 && buf[len - 2] == '\n')) | |
1381 && sp->fileformat == EOL_DOS | |
1382 && buf[len - 1] == Ctrl_Z) | |
1383 { | |
1384 buf[len - 1] = NUL; | |
1385 break; | |
1386 } | |
1387 #endif | |
1388 | |
1389 have_read = TRUE; | |
1390 ga.ga_len = len; | |
1391 | |
1392 // If the line was longer than the buffer, read more. | |
1393 if (ga.ga_maxlen - ga.ga_len == 1 && buf[len - 1] != '\n') | |
1394 continue; | |
1395 | |
1396 if (len >= 1 && buf[len - 1] == '\n') // remove trailing NL | |
1397 { | |
1398 #ifdef USE_CRNL | |
1399 has_cr = (len >= 2 && buf[len - 2] == '\r'); | |
1400 if (sp->fileformat == EOL_UNKNOWN) | |
1401 { | |
1402 if (has_cr) | |
1403 sp->fileformat = EOL_DOS; | |
1404 else | |
1405 sp->fileformat = EOL_UNIX; | |
1406 } | |
1407 | |
1408 if (sp->fileformat == EOL_DOS) | |
1409 { | |
1410 if (has_cr) // replace trailing CR | |
1411 { | |
1412 buf[len - 2] = '\n'; | |
1413 --len; | |
1414 --ga.ga_len; | |
1415 } | |
1416 else // lines like ":map xx yy^M" will have failed | |
1417 { | |
1418 if (!sp->error) | |
1419 { | |
1420 msg_source(HL_ATTR(HLF_W)); | |
1421 emsg(_("W15: Warning: Wrong line separator, ^M may be missing")); | |
1422 } | |
1423 sp->error = TRUE; | |
1424 sp->fileformat = EOL_UNIX; | |
1425 } | |
1426 } | |
1427 #endif | |
1428 // The '\n' is escaped if there is an odd number of ^V's just | |
1429 // before it, first set "c" just before the 'V's and then check | |
1430 // len&c parities (is faster than ((len-c)%2 == 0)) -- Acevedo | |
1431 for (c = len - 2; c >= 0 && buf[c] == Ctrl_V; c--) | |
1432 ; | |
1433 if ((len & 1) != (c & 1)) // escaped NL, read more | |
1434 { | |
1435 ++sp->sourcing_lnum; | |
1436 continue; | |
1437 } | |
1438 | |
1439 buf[len - 1] = NUL; // remove the NL | |
1440 } | |
1441 | |
1442 // Check for ^C here now and then, so recursive :so can be broken. | |
1443 line_breakcheck(); | |
1444 break; | |
1445 } | |
1446 | |
1447 if (have_read) | |
1448 return (char_u *)ga.ga_data; | |
1449 | |
1450 vim_free(ga.ga_data); | |
1451 return NULL; | |
1452 } | |
1453 | |
1454 /* | |
1455 * Get one full line from a sourced file. | |
1456 * Called by do_cmdline() when it's called from do_source(). | |
1457 * | |
1458 * Return a pointer to the line in allocated memory. | |
1459 * Return NULL for end-of-file or some error. | |
1460 */ | |
1461 char_u * | |
1462 getsourceline(int c UNUSED, void *cookie, int indent UNUSED, int do_concat) | |
1463 { | |
1464 struct source_cookie *sp = (struct source_cookie *)cookie; | |
1465 char_u *line; | |
1466 char_u *p; | |
1467 | |
1468 #ifdef FEAT_EVAL | |
1469 // If breakpoints have been added/deleted need to check for it. | |
1470 if (sp->dbg_tick < debug_tick) | |
1471 { | |
1472 sp->breakpoint = dbg_find_breakpoint(TRUE, sp->fname, sourcing_lnum); | |
1473 sp->dbg_tick = debug_tick; | |
1474 } | |
1475 # ifdef FEAT_PROFILE | |
1476 if (do_profiling == PROF_YES) | |
1477 script_line_end(); | |
1478 # endif | |
1479 #endif | |
1480 | |
1481 // Set the current sourcing line number. | |
1482 sourcing_lnum = sp->sourcing_lnum + 1; | |
1483 | |
1484 // Get current line. If there is a read-ahead line, use it, otherwise get | |
1485 // one now. | |
1486 if (sp->finished) | |
1487 line = NULL; | |
1488 else if (sp->nextline == NULL) | |
1489 line = get_one_sourceline(sp); | |
1490 else | |
1491 { | |
1492 line = sp->nextline; | |
1493 sp->nextline = NULL; | |
1494 ++sp->sourcing_lnum; | |
1495 } | |
1496 #ifdef FEAT_PROFILE | |
1497 if (line != NULL && do_profiling == PROF_YES) | |
1498 script_line_start(); | |
1499 #endif | |
1500 | |
1501 // Only concatenate lines starting with a \ when 'cpoptions' doesn't | |
1502 // contain the 'C' flag. | |
1503 if (line != NULL && do_concat && vim_strchr(p_cpo, CPO_CONCAT) == NULL) | |
1504 { | |
1505 // compensate for the one line read-ahead | |
1506 --sp->sourcing_lnum; | |
1507 | |
1508 // Get the next line and concatenate it when it starts with a | |
1509 // backslash. We always need to read the next line, keep it in | |
1510 // sp->nextline. | |
1511 /* Also check for a comment in between continuation lines: "\ */ | |
1512 sp->nextline = get_one_sourceline(sp); | |
1513 if (sp->nextline != NULL | |
1514 && (*(p = skipwhite(sp->nextline)) == '\\' | |
1515 || (p[0] == '"' && p[1] == '\\' && p[2] == ' '))) | |
1516 { | |
1517 garray_T ga; | |
1518 | |
1519 ga_init2(&ga, (int)sizeof(char_u), 400); | |
1520 ga_concat(&ga, line); | |
1521 if (*p == '\\') | |
1522 ga_concat(&ga, p + 1); | |
1523 for (;;) | |
1524 { | |
1525 vim_free(sp->nextline); | |
1526 sp->nextline = get_one_sourceline(sp); | |
1527 if (sp->nextline == NULL) | |
1528 break; | |
1529 p = skipwhite(sp->nextline); | |
1530 if (*p == '\\') | |
1531 { | |
1532 // Adjust the growsize to the current length to speed up | |
1533 // concatenating many lines. | |
1534 if (ga.ga_len > 400) | |
1535 { | |
1536 if (ga.ga_len > 8000) | |
1537 ga.ga_growsize = 8000; | |
1538 else | |
1539 ga.ga_growsize = ga.ga_len; | |
1540 } | |
1541 ga_concat(&ga, p + 1); | |
1542 } | |
1543 else if (p[0] != '"' || p[1] != '\\' || p[2] != ' ') | |
1544 break; | |
1545 } | |
1546 ga_append(&ga, NUL); | |
1547 vim_free(line); | |
1548 line = ga.ga_data; | |
1549 } | |
1550 } | |
1551 | |
1552 if (line != NULL && sp->conv.vc_type != CONV_NONE) | |
1553 { | |
1554 char_u *s; | |
1555 | |
1556 // Convert the encoding of the script line. | |
1557 s = string_convert(&sp->conv, line, NULL); | |
1558 if (s != NULL) | |
1559 { | |
1560 vim_free(line); | |
1561 line = s; | |
1562 } | |
1563 } | |
1564 | |
1565 #ifdef FEAT_EVAL | |
1566 // Did we encounter a breakpoint? | |
1567 if (sp->breakpoint != 0 && sp->breakpoint <= sourcing_lnum) | |
1568 { | |
1569 dbg_breakpoint(sp->fname, sourcing_lnum); | |
1570 // Find next breakpoint. | |
1571 sp->breakpoint = dbg_find_breakpoint(TRUE, sp->fname, sourcing_lnum); | |
1572 sp->dbg_tick = debug_tick; | |
1573 } | |
1574 #endif | |
1575 | |
1576 return line; | |
1577 } | |
1578 | |
1579 /* | |
1580 * ":scriptencoding": Set encoding conversion for a sourced script. | |
1581 */ | |
1582 void | |
1583 ex_scriptencoding(exarg_T *eap) | |
1584 { | |
1585 struct source_cookie *sp; | |
1586 char_u *name; | |
1587 | |
1588 if (!getline_equal(eap->getline, eap->cookie, getsourceline)) | |
1589 { | |
1590 emsg(_("E167: :scriptencoding used outside of a sourced file")); | |
1591 return; | |
1592 } | |
1593 | |
1594 if (*eap->arg != NUL) | |
1595 { | |
1596 name = enc_canonize(eap->arg); | |
1597 if (name == NULL) // out of memory | |
1598 return; | |
1599 } | |
1600 else | |
1601 name = eap->arg; | |
1602 | |
1603 // Setup for conversion from the specified encoding to 'encoding'. | |
1604 sp = (struct source_cookie *)getline_cookie(eap->getline, eap->cookie); | |
1605 convert_setup(&sp->conv, name, p_enc); | |
1606 | |
1607 if (name != eap->arg) | |
1608 vim_free(name); | |
1609 } | |
1610 | |
1611 /* | |
1612 * ":scriptversion": Set Vim script version for a sourced script. | |
1613 */ | |
1614 void | |
1615 ex_scriptversion(exarg_T *eap UNUSED) | |
1616 { | |
1617 #ifdef FEAT_EVAL | |
1618 int nr; | |
1619 | |
1620 if (!getline_equal(eap->getline, eap->cookie, getsourceline)) | |
1621 { | |
1622 emsg(_("E984: :scriptversion used outside of a sourced file")); | |
1623 return; | |
1624 } | |
1625 | |
1626 nr = getdigits(&eap->arg); | |
1627 if (nr == 0 || *eap->arg != NUL) | |
1628 emsg(_(e_invarg)); | |
1629 else if (nr > 3) | |
1630 semsg(_("E999: scriptversion not supported: %d"), nr); | |
1631 else | |
1632 current_sctx.sc_version = nr; | |
1633 #endif | |
1634 } | |
1635 | |
1636 #if defined(FEAT_EVAL) || defined(PROTO) | |
1637 /* | |
1638 * ":finish": Mark a sourced file as finished. | |
1639 */ | |
1640 void | |
1641 ex_finish(exarg_T *eap) | |
1642 { | |
1643 if (getline_equal(eap->getline, eap->cookie, getsourceline)) | |
1644 do_finish(eap, FALSE); | |
1645 else | |
1646 emsg(_("E168: :finish used outside of a sourced file")); | |
1647 } | |
1648 | |
1649 /* | |
1650 * Mark a sourced file as finished. Possibly makes the ":finish" pending. | |
1651 * Also called for a pending finish at the ":endtry" or after returning from | |
1652 * an extra do_cmdline(). "reanimate" is used in the latter case. | |
1653 */ | |
1654 void | |
1655 do_finish(exarg_T *eap, int reanimate) | |
1656 { | |
1657 int idx; | |
1658 | |
1659 if (reanimate) | |
1660 ((struct source_cookie *)getline_cookie(eap->getline, | |
1661 eap->cookie))->finished = FALSE; | |
1662 | |
1663 // Cleanup (and inactivate) conditionals, but stop when a try conditional | |
1664 // not in its finally clause (which then is to be executed next) is found. | |
1665 // In this case, make the ":finish" pending for execution at the ":endtry". | |
1666 // Otherwise, finish normally. | |
1667 idx = cleanup_conditionals(eap->cstack, 0, TRUE); | |
1668 if (idx >= 0) | |
1669 { | |
1670 eap->cstack->cs_pending[idx] = CSTP_FINISH; | |
1671 report_make_pending(CSTP_FINISH, NULL); | |
1672 } | |
1673 else | |
1674 ((struct source_cookie *)getline_cookie(eap->getline, | |
1675 eap->cookie))->finished = TRUE; | |
1676 } | |
1677 | |
1678 | |
1679 /* | |
1680 * Return TRUE when a sourced file had the ":finish" command: Don't give error | |
1681 * message for missing ":endif". | |
1682 * Return FALSE when not sourcing a file. | |
1683 */ | |
1684 int | |
1685 source_finished( | |
1686 char_u *(*fgetline)(int, void *, int, int), | |
1687 void *cookie) | |
1688 { | |
1689 return (getline_equal(fgetline, cookie, getsourceline) | |
1690 && ((struct source_cookie *)getline_cookie( | |
1691 fgetline, cookie))->finished); | |
1692 } | |
1693 #endif |