1 | // This is an open source non-commercial project. Dear PVS-Studio, please check |
2 | // it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com |
3 | |
4 | // Various routines dealing with allocation and deallocation of memory. |
5 | |
6 | #include <assert.h> |
7 | #include <inttypes.h> |
8 | #include <string.h> |
9 | #include <stdbool.h> |
10 | |
11 | #include "nvim/vim.h" |
12 | #include "nvim/context.h" |
13 | #include "nvim/eval.h" |
14 | #include "nvim/highlight.h" |
15 | #include "nvim/memfile.h" |
16 | #include "nvim/memory.h" |
17 | #include "nvim/message.h" |
18 | #include "nvim/misc1.h" |
19 | #include "nvim/ui.h" |
20 | #include "nvim/sign.h" |
21 | #include "nvim/api/vim.h" |
22 | |
23 | #ifdef UNIT_TESTING |
24 | # define malloc(size) mem_malloc(size) |
25 | # define calloc(count, size) mem_calloc(count, size) |
26 | # define realloc(ptr, size) mem_realloc(ptr, size) |
27 | # define free(ptr) mem_free(ptr) |
28 | MemMalloc mem_malloc = &malloc; |
29 | MemFree mem_free = &free; |
30 | MemCalloc mem_calloc = &calloc; |
31 | MemRealloc mem_realloc = &realloc; |
32 | #endif |
33 | |
34 | #ifdef INCLUDE_GENERATED_DECLARATIONS |
35 | # include "memory.c.generated.h" |
36 | #endif |
37 | |
38 | #ifdef EXITFREE |
39 | bool entered_free_all_mem = false; |
40 | #endif |
41 | |
42 | /// Try to free memory. Used when trying to recover from out of memory errors. |
43 | /// @see {xmalloc} |
44 | void try_to_free_memory(void) |
45 | { |
46 | static bool trying_to_free = false; |
47 | // avoid recursive calls |
48 | if (trying_to_free) |
49 | return; |
50 | trying_to_free = true; |
51 | |
52 | // free any scrollback text |
53 | clear_sb_text(true); |
54 | // Try to save all buffers and release as many blocks as possible |
55 | mf_release_all(); |
56 | |
57 | trying_to_free = false; |
58 | } |
59 | |
60 | /// malloc() wrapper |
61 | /// |
62 | /// try_malloc() is a malloc() wrapper that tries to free some memory before |
63 | /// trying again. |
64 | /// |
65 | /// @see {try_to_free_memory} |
66 | /// @param size |
67 | /// @return pointer to allocated space. NULL if out of memory |
68 | void *try_malloc(size_t size) FUNC_ATTR_MALLOC FUNC_ATTR_ALLOC_SIZE(1) |
69 | { |
70 | size_t allocated_size = size ? size : 1; |
71 | void *ret = malloc(allocated_size); |
72 | if (!ret) { |
73 | try_to_free_memory(); |
74 | ret = malloc(allocated_size); |
75 | } |
76 | return ret; |
77 | } |
78 | |
79 | /// try_malloc() wrapper that shows an out-of-memory error message to the user |
80 | /// before returning NULL |
81 | /// |
82 | /// @see {try_malloc} |
83 | /// @param size |
84 | /// @return pointer to allocated space. NULL if out of memory |
85 | void *verbose_try_malloc(size_t size) FUNC_ATTR_MALLOC FUNC_ATTR_ALLOC_SIZE(1) |
86 | { |
87 | void *ret = try_malloc(size); |
88 | if (!ret) { |
89 | do_outofmem_msg(size); |
90 | } |
91 | return ret; |
92 | } |
93 | |
94 | /// malloc() wrapper that never returns NULL |
95 | /// |
96 | /// xmalloc() succeeds or gracefully aborts when out of memory. |
97 | /// Before aborting try to free some memory and call malloc again. |
98 | /// |
99 | /// @see {try_to_free_memory} |
100 | /// @param size |
101 | /// @return pointer to allocated space. Never NULL |
102 | void *xmalloc(size_t size) |
103 | FUNC_ATTR_MALLOC FUNC_ATTR_ALLOC_SIZE(1) FUNC_ATTR_NONNULL_RET |
104 | { |
105 | void *ret = try_malloc(size); |
106 | if (!ret) { |
107 | mch_errmsg(e_outofmem); |
108 | mch_errmsg("\n" ); |
109 | preserve_exit(); |
110 | } |
111 | return ret; |
112 | } |
113 | |
114 | /// free() wrapper that delegates to the backing memory manager |
115 | /// |
116 | /// @note Use XFREE_CLEAR() instead, if possible. |
117 | void xfree(void *ptr) |
118 | { |
119 | free(ptr); |
120 | } |
121 | |
122 | /// calloc() wrapper |
123 | /// |
124 | /// @see {xmalloc} |
125 | /// @param count |
126 | /// @param size |
127 | /// @return pointer to allocated space. Never NULL |
128 | void *xcalloc(size_t count, size_t size) |
129 | FUNC_ATTR_MALLOC FUNC_ATTR_ALLOC_SIZE_PROD(1, 2) FUNC_ATTR_NONNULL_RET |
130 | { |
131 | size_t allocated_count = count && size ? count : 1; |
132 | size_t allocated_size = count && size ? size : 1; |
133 | void *ret = calloc(allocated_count, allocated_size); |
134 | if (!ret) { |
135 | try_to_free_memory(); |
136 | ret = calloc(allocated_count, allocated_size); |
137 | if (!ret) { |
138 | mch_errmsg(e_outofmem); |
139 | mch_errmsg("\n" ); |
140 | preserve_exit(); |
141 | } |
142 | } |
143 | return ret; |
144 | } |
145 | |
146 | /// realloc() wrapper |
147 | /// |
148 | /// @see {xmalloc} |
149 | /// @param size |
150 | /// @return pointer to reallocated space. Never NULL |
151 | void *xrealloc(void *ptr, size_t size) |
152 | FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_ALLOC_SIZE(2) FUNC_ATTR_NONNULL_RET |
153 | { |
154 | size_t allocated_size = size ? size : 1; |
155 | void *ret = realloc(ptr, allocated_size); |
156 | if (!ret) { |
157 | try_to_free_memory(); |
158 | ret = realloc(ptr, allocated_size); |
159 | if (!ret) { |
160 | mch_errmsg(e_outofmem); |
161 | mch_errmsg("\n" ); |
162 | preserve_exit(); |
163 | } |
164 | } |
165 | return ret; |
166 | } |
167 | |
168 | /// xmalloc() wrapper that allocates size + 1 bytes and zeroes the last byte |
169 | /// |
170 | /// @see {xmalloc} |
171 | /// @param size |
172 | /// @return pointer to allocated space. Never NULL |
173 | void *xmallocz(size_t size) |
174 | FUNC_ATTR_MALLOC FUNC_ATTR_NONNULL_RET FUNC_ATTR_WARN_UNUSED_RESULT |
175 | { |
176 | size_t total_size = size + 1; |
177 | if (total_size < size) { |
178 | mch_errmsg(_("Vim: Data too large to fit into virtual memory space\n" )); |
179 | preserve_exit(); |
180 | } |
181 | |
182 | void *ret = xmalloc(total_size); |
183 | ((char*)ret)[size] = 0; |
184 | |
185 | return ret; |
186 | } |
187 | |
188 | /// Allocates (len + 1) bytes of memory, duplicates `len` bytes of |
189 | /// `data` to the allocated memory, zero terminates the allocated memory, |
190 | /// and returns a pointer to the allocated memory. If the allocation fails, |
191 | /// the program dies. |
192 | /// |
193 | /// @see {xmalloc} |
194 | /// @param data Pointer to the data that will be copied |
195 | /// @param len number of bytes that will be copied |
196 | void *xmemdupz(const void *data, size_t len) |
197 | FUNC_ATTR_MALLOC FUNC_ATTR_NONNULL_RET FUNC_ATTR_WARN_UNUSED_RESULT |
198 | FUNC_ATTR_NONNULL_ALL |
199 | { |
200 | return memcpy(xmallocz(len), data, len); |
201 | } |
202 | |
203 | /// A version of strchr() that returns a pointer to the terminating NUL if it |
204 | /// doesn't find `c`. |
205 | /// |
206 | /// @param str The string to search. |
207 | /// @param c The char to look for. |
208 | /// @returns a pointer to the first instance of `c`, or to the NUL terminator |
209 | /// if not found. |
210 | char *xstrchrnul(const char *str, char c) |
211 | FUNC_ATTR_NONNULL_RET FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE |
212 | { |
213 | char *p = strchr(str, c); |
214 | return p ? p : (char *)(str + strlen(str)); |
215 | } |
216 | |
217 | /// A version of memchr() that returns a pointer one past the end |
218 | /// if it doesn't find `c`. |
219 | /// |
220 | /// @param addr The address of the memory object. |
221 | /// @param c The char to look for. |
222 | /// @param size The size of the memory object. |
223 | /// @returns a pointer to the first instance of `c`, or one past the end if not |
224 | /// found. |
225 | void *xmemscan(const void *addr, char c, size_t size) |
226 | FUNC_ATTR_NONNULL_RET FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE |
227 | { |
228 | char *p = memchr(addr, c, size); |
229 | return p ? p : (char *)addr + size; |
230 | } |
231 | |
232 | /// Replaces every instance of `c` with `x`. |
233 | /// |
234 | /// @warning Will read past `str + strlen(str)` if `c == NUL`. |
235 | /// |
236 | /// @param str A NUL-terminated string. |
237 | /// @param c The unwanted byte. |
238 | /// @param x The replacement. |
239 | void strchrsub(char *str, char c, char x) |
240 | FUNC_ATTR_NONNULL_ALL |
241 | { |
242 | assert(c != '\0'); |
243 | while ((str = strchr(str, c))) { |
244 | *str++ = x; |
245 | } |
246 | } |
247 | |
248 | /// Replaces every instance of `c` with `x`. |
249 | /// |
250 | /// @param data An object in memory. May contain NULs. |
251 | /// @param c The unwanted byte. |
252 | /// @param x The replacement. |
253 | /// @param len The length of data. |
254 | void memchrsub(void *data, char c, char x, size_t len) |
255 | FUNC_ATTR_NONNULL_ALL |
256 | { |
257 | char *p = data, *end = (char *)data + len; |
258 | while ((p = memchr(p, c, (size_t)(end - p)))) { |
259 | *p++ = x; |
260 | } |
261 | } |
262 | |
263 | /// Counts the number of occurrences of `c` in `str`. |
264 | /// |
265 | /// @warning Unsafe if `c == NUL`. |
266 | /// |
267 | /// @param str Pointer to the string to search. |
268 | /// @param c The byte to search for. |
269 | /// @returns the number of occurrences of `c` in `str`. |
270 | size_t strcnt(const char *str, char c) |
271 | FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE |
272 | { |
273 | assert(c != 0); |
274 | size_t cnt = 0; |
275 | while ((str = strchr(str, c))) { |
276 | cnt++; |
277 | str++; // Skip the instance of c. |
278 | } |
279 | return cnt; |
280 | } |
281 | |
282 | /// Counts the number of occurrences of byte `c` in `data[len]`. |
283 | /// |
284 | /// @param data Pointer to the data to search. |
285 | /// @param c The byte to search for. |
286 | /// @param len The length of `data`. |
287 | /// @returns the number of occurrences of `c` in `data[len]`. |
288 | size_t memcnt(const void *data, char c, size_t len) |
289 | FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE |
290 | { |
291 | size_t cnt = 0; |
292 | const char *ptr = data, *end = ptr + len; |
293 | while ((ptr = memchr(ptr, c, (size_t)(end - ptr))) != NULL) { |
294 | cnt++; |
295 | ptr++; // Skip the instance of c. |
296 | } |
297 | return cnt; |
298 | } |
299 | |
300 | /// Copies the string pointed to by src (including the terminating NUL |
301 | /// character) into the array pointed to by dst. |
302 | /// |
303 | /// @returns pointer to the terminating NUL char copied into the dst buffer. |
304 | /// This is the only difference with strcpy(), which returns dst. |
305 | /// |
306 | /// WARNING: If copying takes place between objects that overlap, the behavior |
307 | /// is undefined. |
308 | /// |
309 | /// Nvim version of POSIX 2008 stpcpy(3). We do not require POSIX 2008, so |
310 | /// implement our own version. |
311 | /// |
312 | /// @param dst |
313 | /// @param src |
314 | char *xstpcpy(char *restrict dst, const char *restrict src) |
315 | FUNC_ATTR_NONNULL_RET FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL |
316 | { |
317 | const size_t len = strlen(src); |
318 | return (char *)memcpy(dst, src, len + 1) + len; |
319 | } |
320 | |
321 | /// Copies not more than n bytes (bytes that follow a NUL character are not |
322 | /// copied) from the array pointed to by src to the array pointed to by dst. |
323 | /// |
324 | /// If a NUL character is written to the destination, xstpncpy() returns the |
325 | /// address of the first such NUL character. Otherwise, it shall return |
326 | /// &dst[maxlen]. |
327 | /// |
328 | /// WARNING: If copying takes place between objects that overlap, the behavior |
329 | /// is undefined. |
330 | /// |
331 | /// WARNING: xstpncpy will ALWAYS write maxlen bytes. If src is shorter than |
332 | /// maxlen, zeroes will be written to the remaining bytes. |
333 | /// |
334 | /// @param dst |
335 | /// @param src |
336 | /// @param maxlen |
337 | char *xstpncpy(char *restrict dst, const char *restrict src, size_t maxlen) |
338 | FUNC_ATTR_NONNULL_RET FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL |
339 | { |
340 | const char *p = memchr(src, '\0', maxlen); |
341 | if (p) { |
342 | size_t srclen = (size_t)(p - src); |
343 | memcpy(dst, src, srclen); |
344 | memset(dst + srclen, 0, maxlen - srclen); |
345 | return dst + srclen; |
346 | } else { |
347 | memcpy(dst, src, maxlen); |
348 | return dst + maxlen; |
349 | } |
350 | } |
351 | |
352 | /// xstrlcpy - Copy a NUL-terminated string into a sized buffer |
353 | /// |
354 | /// Compatible with *BSD strlcpy: the result is always a valid NUL-terminated |
355 | /// string that fits in the buffer (unless, of course, the buffer size is |
356 | /// zero). It does not pad out the result like strncpy() does. |
357 | /// |
358 | /// @param[out] dst Buffer to store the result. |
359 | /// @param[in] src String to be copied. |
360 | /// @param[in] dsize Size of `dst`. |
361 | /// |
362 | /// @return Length of `src`. May be greater than `dsize - 1`, which would mean |
363 | /// that string was truncated. |
364 | size_t xstrlcpy(char *restrict dst, const char *restrict src, size_t dsize) |
365 | FUNC_ATTR_NONNULL_ALL |
366 | { |
367 | size_t slen = strlen(src); |
368 | |
369 | if (dsize) { |
370 | size_t len = MIN(slen, dsize - 1); |
371 | memcpy(dst, src, len); |
372 | dst[len] = '\0'; |
373 | } |
374 | |
375 | return slen; // Does not include NUL. |
376 | } |
377 | |
378 | /// Appends `src` to string `dst` of size `dsize` (unlike strncat, dsize is the |
379 | /// full size of `dst`, not space left). At most dsize-1 characters |
380 | /// will be copied. Always NUL terminates. `src` and `dst` may overlap. |
381 | /// |
382 | /// @see vim_strcat from Vim. |
383 | /// @see strlcat from OpenBSD. |
384 | /// |
385 | /// @param[in,out] dst Buffer to be appended-to. Must have a NUL byte. |
386 | /// @param[in] src String to put at the end of `dst`. |
387 | /// @param[in] dsize Size of `dst` including NUL byte. Must be greater than 0. |
388 | /// |
389 | /// @return Length of the resulting string as if destination size was #SIZE_MAX. |
390 | /// May be greater than `dsize - 1`, which would mean that string was |
391 | /// truncated. |
392 | size_t xstrlcat(char *const dst, const char *const src, const size_t dsize) |
393 | FUNC_ATTR_NONNULL_ALL |
394 | { |
395 | assert(dsize > 0); |
396 | const size_t dlen = strlen(dst); |
397 | assert(dlen < dsize); |
398 | const size_t slen = strlen(src); |
399 | |
400 | if (slen > dsize - dlen - 1) { |
401 | memmove(dst + dlen, src, dsize - dlen - 1); |
402 | dst[dsize - 1] = '\0'; |
403 | } else { |
404 | memmove(dst + dlen, src, slen + 1); |
405 | } |
406 | |
407 | return slen + dlen; // Does not include NUL. |
408 | } |
409 | |
410 | /// strdup() wrapper |
411 | /// |
412 | /// @see {xmalloc} |
413 | /// @param str 0-terminated string that will be copied |
414 | /// @return pointer to a copy of the string |
415 | char *xstrdup(const char *str) |
416 | FUNC_ATTR_MALLOC FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_RET |
417 | FUNC_ATTR_NONNULL_ALL |
418 | { |
419 | return xmemdupz(str, strlen(str)); |
420 | } |
421 | |
422 | /// strdup() wrapper |
423 | /// |
424 | /// Unlike xstrdup() allocates a new empty string if it receives NULL. |
425 | char *xstrdupnul(const char *const str) |
426 | FUNC_ATTR_MALLOC FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_RET |
427 | { |
428 | if (str == NULL) { |
429 | return xmallocz(0); |
430 | } else { |
431 | return xstrdup(str); |
432 | } |
433 | } |
434 | |
435 | /// A version of memchr that starts the search at `src + len`. |
436 | /// |
437 | /// Based on glibc's memrchr. |
438 | /// |
439 | /// @param src The source memory object. |
440 | /// @param c The byte to search for. |
441 | /// @param len The length of the memory object. |
442 | /// @returns a pointer to the found byte in src[len], or NULL. |
443 | void *xmemrchr(const void *src, uint8_t c, size_t len) |
444 | FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE |
445 | { |
446 | while (len--) { |
447 | if (((uint8_t *)src)[len] == c) { |
448 | return (uint8_t *) src + len; |
449 | } |
450 | } |
451 | return NULL; |
452 | } |
453 | |
454 | /// strndup() wrapper |
455 | /// |
456 | /// @see {xmalloc} |
457 | /// @param str 0-terminated string that will be copied |
458 | /// @return pointer to a copy of the string |
459 | char *xstrndup(const char *str, size_t len) |
460 | FUNC_ATTR_MALLOC FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_RET |
461 | FUNC_ATTR_NONNULL_ALL |
462 | { |
463 | char *p = memchr(str, '\0', len); |
464 | return xmemdupz(str, p ? (size_t)(p - str) : len); |
465 | } |
466 | |
467 | /// Duplicates a chunk of memory using xmalloc |
468 | /// |
469 | /// @see {xmalloc} |
470 | /// @param data pointer to the chunk |
471 | /// @param len size of the chunk |
472 | /// @return a pointer |
473 | void *xmemdup(const void *data, size_t len) |
474 | FUNC_ATTR_MALLOC FUNC_ATTR_ALLOC_SIZE(2) FUNC_ATTR_NONNULL_RET |
475 | FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL |
476 | { |
477 | return memcpy(xmalloc(len), data, len); |
478 | } |
479 | |
480 | /// Returns true if strings `a` and `b` are equal. Arguments may be NULL. |
481 | bool strequal(const char *a, const char *b) |
482 | FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT |
483 | { |
484 | return (a == NULL && b == NULL) || (a && b && strcmp(a, b) == 0); |
485 | } |
486 | |
487 | /// Case-insensitive `strequal`. |
488 | bool striequal(const char *a, const char *b) |
489 | FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT |
490 | { |
491 | return (a == NULL && b == NULL) || (a && b && STRICMP(a, b) == 0); |
492 | } |
493 | |
494 | /* |
495 | * Avoid repeating the error message many times (they take 1 second each). |
496 | * Did_outofmem_msg is reset when a character is read. |
497 | */ |
498 | void do_outofmem_msg(size_t size) |
499 | { |
500 | if (!did_outofmem_msg) { |
501 | /* Don't hide this message */ |
502 | emsg_silent = 0; |
503 | |
504 | /* Must come first to avoid coming back here when printing the error |
505 | * message fails, e.g. when setting v:errmsg. */ |
506 | did_outofmem_msg = true; |
507 | |
508 | EMSGU(_("E342: Out of memory! (allocating %" PRIu64 " bytes)" ), size); |
509 | } |
510 | } |
511 | |
512 | /// Writes time_t to "buf[8]". |
513 | void time_to_bytes(time_t time_, uint8_t buf[8]) |
514 | { |
515 | // time_t can be up to 8 bytes in size, more than uintmax_t in 32 bits |
516 | // systems, thus we can't use put_bytes() here. |
517 | for (size_t i = 7, bufi = 0; bufi < 8; i--, bufi++) { |
518 | buf[bufi] = (uint8_t)((uint64_t)time_ >> (i * 8)); |
519 | } |
520 | } |
521 | |
522 | #if defined(EXITFREE) |
523 | |
524 | #include "nvim/file_search.h" |
525 | #include "nvim/buffer.h" |
526 | #include "nvim/charset.h" |
527 | #include "nvim/diff.h" |
528 | #include "nvim/edit.h" |
529 | #include "nvim/ex_cmds.h" |
530 | #include "nvim/ex_docmd.h" |
531 | #include "nvim/ex_getln.h" |
532 | #include "nvim/fileio.h" |
533 | #include "nvim/fold.h" |
534 | #include "nvim/getchar.h" |
535 | #include "nvim/mark.h" |
536 | #include "nvim/mbyte.h" |
537 | #include "nvim/memline.h" |
538 | #include "nvim/move.h" |
539 | #include "nvim/option.h" |
540 | #include "nvim/ops.h" |
541 | #include "nvim/os_unix.h" |
542 | #include "nvim/path.h" |
543 | #include "nvim/quickfix.h" |
544 | #include "nvim/regexp.h" |
545 | #include "nvim/screen.h" |
546 | #include "nvim/search.h" |
547 | #include "nvim/spell.h" |
548 | #include "nvim/syntax.h" |
549 | #include "nvim/tag.h" |
550 | #include "nvim/window.h" |
551 | #include "nvim/os/os.h" |
552 | #include "nvim/eval/typval.h" |
553 | |
554 | /* |
555 | * Free everything that we allocated. |
556 | * Can be used to detect memory leaks, e.g., with ccmalloc. |
557 | * NOTE: This is tricky! Things are freed that functions depend on. Don't be |
558 | * surprised if Vim crashes... |
559 | * Some things can't be freed, esp. things local to a library function. |
560 | */ |
561 | void free_all_mem(void) |
562 | { |
563 | buf_T *buf, *nextbuf; |
564 | |
565 | // When we cause a crash here it is caught and Vim tries to exit cleanly. |
566 | // Don't try freeing everything again. |
567 | if (entered_free_all_mem) { |
568 | return; |
569 | } |
570 | entered_free_all_mem = true; |
571 | |
572 | // Don't want to trigger autocommands from here on. |
573 | block_autocmds(); |
574 | |
575 | /* Close all tabs and windows. Reset 'equalalways' to avoid redraws. */ |
576 | p_ea = false; |
577 | if (first_tabpage->tp_next != NULL) |
578 | do_cmdline_cmd("tabonly!" ); |
579 | |
580 | if (!ONE_WINDOW) { |
581 | // to keep things simple, don't perform this |
582 | // ritual inside a float |
583 | curwin = firstwin; |
584 | do_cmdline_cmd("only!" ); |
585 | } |
586 | |
587 | /* Free all spell info. */ |
588 | spell_free_all(); |
589 | |
590 | /* Clear user commands (before deleting buffers). */ |
591 | ex_comclear(NULL); |
592 | |
593 | /* Clear menus. */ |
594 | do_cmdline_cmd("aunmenu *" ); |
595 | do_cmdline_cmd("menutranslate clear" ); |
596 | |
597 | /* Clear mappings, abbreviations, breakpoints. */ |
598 | do_cmdline_cmd("lmapclear" ); |
599 | do_cmdline_cmd("xmapclear" ); |
600 | do_cmdline_cmd("mapclear" ); |
601 | do_cmdline_cmd("mapclear!" ); |
602 | do_cmdline_cmd("abclear" ); |
603 | do_cmdline_cmd("breakdel *" ); |
604 | do_cmdline_cmd("profdel *" ); |
605 | do_cmdline_cmd("set keymap=" ); |
606 | |
607 | free_titles(); |
608 | free_findfile(); |
609 | |
610 | /* Obviously named calls. */ |
611 | free_all_autocmds(); |
612 | free_all_marks(); |
613 | alist_clear(&global_alist); |
614 | free_homedir(); |
615 | free_users(); |
616 | free_search_patterns(); |
617 | free_old_sub(); |
618 | free_last_insert(); |
619 | free_prev_shellcmd(); |
620 | free_regexp_stuff(); |
621 | free_tag_stuff(); |
622 | free_cd_dir(); |
623 | free_signs(); |
624 | set_expr_line(NULL); |
625 | diff_clear(curtab); |
626 | clear_sb_text(true); // free any scrollback text |
627 | |
628 | /* Free some global vars. */ |
629 | xfree(last_cmdline); |
630 | xfree(new_last_cmdline); |
631 | set_keep_msg(NULL, 0); |
632 | |
633 | /* Clear cmdline history. */ |
634 | p_hi = 0; |
635 | init_history(); |
636 | |
637 | qf_free_all(NULL); |
638 | /* Free all location lists */ |
639 | FOR_ALL_TAB_WINDOWS(tab, win) { |
640 | qf_free_all(win); |
641 | } |
642 | |
643 | /* Close all script inputs. */ |
644 | close_all_scripts(); |
645 | |
646 | /* Destroy all windows. Must come before freeing buffers. */ |
647 | win_free_all(); |
648 | |
649 | // Free all option values. Must come after closing windows. |
650 | free_all_options(); |
651 | |
652 | free_arshape_buf(); |
653 | |
654 | /* Clear registers. */ |
655 | clear_registers(); |
656 | ResetRedobuff(); |
657 | ResetRedobuff(); |
658 | |
659 | |
660 | /* highlight info */ |
661 | free_highlight(); |
662 | |
663 | reset_last_sourcing(); |
664 | |
665 | free_tabpage(first_tabpage); |
666 | first_tabpage = NULL; |
667 | |
668 | /* message history */ |
669 | for (;; ) |
670 | if (delete_first_msg() == FAIL) |
671 | break; |
672 | |
673 | eval_clear(); |
674 | api_vim_free_all_mem(); |
675 | ctx_free_all(); |
676 | |
677 | // Free all buffers. Reset 'autochdir' to avoid accessing things that |
678 | // were freed already. |
679 | // Must be after eval_clear to avoid it trying to access b:changedtick after |
680 | // freeing it. |
681 | p_acd = false; |
682 | for (buf = firstbuf; buf != NULL; ) { |
683 | bufref_T bufref; |
684 | set_bufref(&bufref, buf); |
685 | nextbuf = buf->b_next; |
686 | close_buffer(NULL, buf, DOBUF_WIPE, false); |
687 | // Didn't work, try next one. |
688 | buf = bufref_valid(&bufref) ? nextbuf : firstbuf; |
689 | } |
690 | |
691 | // free screenlines (can't display anything now!) |
692 | screen_free_all_mem(); |
693 | |
694 | clear_hl_tables(false); |
695 | list_free_log(); |
696 | } |
697 | |
698 | #endif |
699 | |
700 | |