diff options
Diffstat (limited to 'src/nvim/shada.c')
-rw-r--r-- | src/nvim/shada.c | 1005 |
1 files changed, 569 insertions, 436 deletions
diff --git a/src/nvim/shada.c b/src/nvim/shada.c index b5921eb810..8864301e4c 100644 --- a/src/nvim/shada.c +++ b/src/nvim/shada.c @@ -1,3 +1,6 @@ +// This is an open source non-commercial project. Dear PVS-Studio, please check +// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com + #include <stdlib.h> #include <stddef.h> #include <stdbool.h> @@ -13,12 +16,14 @@ #include "nvim/os/os.h" #include "nvim/os/time.h" #include "nvim/vim.h" +#include "nvim/pos.h" #include "nvim/ascii.h" #include "nvim/shada.h" #include "nvim/message.h" #include "nvim/globals.h" #include "nvim/memory.h" #include "nvim/mark.h" +#include "nvim/macros.h" #include "nvim/ops.h" #include "nvim/garray.h" #include "nvim/option.h" @@ -27,11 +32,10 @@ #include "nvim/api/private/helpers.h" #include "nvim/buffer.h" #include "nvim/buffer_defs.h" -#include "nvim/misc2.h" #include "nvim/ex_getln.h" #include "nvim/search.h" #include "nvim/regexp.h" -#include "nvim/eval_defs.h" +#include "nvim/eval/typval.h" #include "nvim/version.h" #include "nvim/path.h" #include "nvim/fileio.h" @@ -74,17 +78,10 @@ KHASH_SET_INIT_STR(strset) (vim_rename((char_u *)a, (char_u *)b)) #define mb_strnicmp(a, b, c) \ (mb_strnicmp((char_u *)a, (char_u *)b, c)) -#define has_non_ascii(a) (has_non_ascii((char_u *)a)) -#define string_convert(a, b, c) \ - ((char *)string_convert((vimconv_T *)a, (char_u *)b, c)) -#define path_shorten_fname_if_possible(b) \ - ((char *)path_shorten_fname_if_possible((char_u *)b)) +#define path_try_shorten_fname(b) \ + ((char *)path_try_shorten_fname((char_u *)b)) #define buflist_new(ffname, sfname, ...) \ (buflist_new((char_u *)ffname, (char_u *)sfname, __VA_ARGS__)) -#define convert_setup(vcp, from, to) \ - (convert_setup(vcp, (char_u *)from, (char_u *)to)) -#define os_getperm(f) \ - (os_getperm((char_u *) f)) #define os_isdir(f) (os_isdir((char_u *) f)) #define regtilde(s, m) ((char *) regtilde((char_u *) s, m)) #define path_tail_with_sep(f) ((char *) path_tail_with_sep((char_u *)f)) @@ -103,6 +100,7 @@ KHASH_SET_INIT_STR(strset) #define REG_KEY_TYPE "rt" #define REG_KEY_WIDTH "rw" #define REG_KEY_CONTENTS "rc" +#define REG_KEY_UNNAMED "ru" #define KEY_LNUM "l" #define KEY_COL "c" @@ -119,9 +117,10 @@ KHASH_SET_INIT_STR(strset) // E576: Missing '>' // E577: Illegal register name // E886: Can't rename viminfo file to %s! +// E929: Too many viminfo temp files, like %s! // Now only six of them are used: // E137: ShaDa file is not writeable (for pre-open checks) -// E138: All %s.tmp.X files exist, cannot write ShaDa file! +// E929: All %s.tmp.X files exist, cannot write ShaDa file! // RCERR (E576) for critical read errors. // RNERR (E136) for various errors when renaming. // RERR (E575) for various errors inside read ShaDa file. @@ -149,6 +148,9 @@ KHASH_SET_INIT_STR(strset) /// Common prefix for all ignorable “write” errors #define WERR "E574: " +/// Callback function for add_search_pattern +typedef void (*SearchPatternGetter)(SearchPattern *); + /// Flags for shada_read_file and children typedef enum { kShaDaWantInfo = 1, ///< Load non-mark information @@ -285,6 +287,7 @@ typedef struct { char name; MotionType type; char **contents; + bool is_unnamed; size_t contents_size; size_t width; dict_T *additional_data; @@ -374,7 +377,8 @@ KHASH_MAP_INIT_STR(file_marks, FileMarks) /// Before actually writing most of the data is read to this structure. typedef struct { HistoryMergerState hms[HIST_COUNT]; ///< Structures for history merging. - PossiblyFreedShadaEntry global_marks[NGLOBALMARKS]; ///< All global marks. + PossiblyFreedShadaEntry global_marks[NMARKS]; ///< Named global marks. + PossiblyFreedShadaEntry numbered_marks[EXTRA_MARKS]; ///< Numbered marks. PossiblyFreedShadaEntry registers[NUM_SAVED_REGISTERS]; ///< All registers. PossiblyFreedShadaEntry jumps[JUMPLISTSIZE]; ///< All dumped jumps. size_t jumps_size; ///< Number of jumps occupied. @@ -412,8 +416,6 @@ typedef struct sd_read_def { const char *error; ///< Error message in case of error. uintmax_t fpos; ///< Current position (amount of bytes read since ///< reader structure initialization). May overflow. - vimconv_T sd_conv; ///< Structure used for converting encodings of some - ///< items. } ShaDaReadDef; struct sd_write_def; @@ -434,8 +436,6 @@ typedef struct sd_write_def { ShaDaWriteCloser close; ///< Close function. void *cookie; ///< Data describing object written to. const char *error; ///< Error message in case of error. - vimconv_T sd_conv; ///< Structure used for converting encodings of some - ///< items. } ShaDaWriteDef; #ifdef INCLUDE_GENERATED_DECLARATIONS @@ -478,6 +478,7 @@ static const ShadaEntry sd_default_values[] = { .type = kMTCharWise, .contents = NULL, .contents_size = 0, + .is_unnamed = false, .width = 0, .additional_data = NULL), DEF_SDE(Variable, global_var, @@ -598,6 +599,7 @@ static inline void hmll_insert(HMLList *const hmll, if (hmll_entry == hmll->first) { hmll_entry = NULL; } + assert(hmll->first != NULL); hmll_remove(hmll, hmll->first); } HMLListEntry *target_entry; @@ -802,7 +804,7 @@ static int open_shada_file_for_reading(const char *const fname, return error; } - convert_setup(&sd_reader->sd_conv, "utf-8", p_enc); + assert(STRCMP(p_enc, "utf-8") == 0); return 0; } @@ -810,7 +812,7 @@ static int open_shada_file_for_reading(const char *const fname, /// Wrapper for closing file descriptors static void close_file(void *cookie) { - const int error = file_free(cookie); + const int error = file_free(cookie, !!p_fs); if (error != 0) { emsgf(_(SERR "System error while closing ShaDa file: %s"), os_strerror(error)); @@ -883,7 +885,7 @@ static int shada_read_file(const char *const file, const int flags) if (p_verbose > 0) { verbose_enter(); - smsg(_("Reading ShaDa file \"%s\"%s%s%s"), + smsg(_("Reading ShaDa file \"%s\"%s%s%s%s"), fname, (flags & kShaDaWantInfo) ? _(" info") : "", (flags & kShaDaWantMarks) ? _(" marks") : "", @@ -1182,8 +1184,7 @@ static void shada_read(ShaDaReadDef *const sd_reader, const int flags) list_T *oldfiles_list = get_vim_var_list(VV_OLDFILES); const bool force = flags & kShaDaForceit; const bool get_old_files = (flags & (kShaDaGetOldfiles | kShaDaForceit) - && (force || oldfiles_list == NULL - || oldfiles_list->lv_len == 0)); + && (force || tv_list_len(oldfiles_list) == 0)); const bool want_marks = flags & kShaDaWantMarks; const unsigned srni_flags = (unsigned) ( (flags & kShaDaWantInfo @@ -1220,7 +1221,7 @@ static void shada_read(ShaDaReadDef *const sd_reader, const int flags) khash_t(fnamebufs) fname_bufs = KHASH_EMPTY_TABLE(fnamebufs); khash_t(strset) oldfiles_set = KHASH_EMPTY_TABLE(strset); if (get_old_files && (oldfiles_list == NULL || force)) { - oldfiles_list = list_alloc(); + oldfiles_list = tv_list_alloc(kListLenUnknown); set_vim_var_list(VV_OLDFILES, oldfiles_list); } ShaDaReadResult srni_ret; @@ -1282,8 +1283,6 @@ static void shada_read(ShaDaReadDef *const sd_reader, const int flags) if (cur_entry.data.search_pattern.is_last_used) { set_last_used_pattern( cur_entry.data.search_pattern.is_substitute_pattern); - } - if (cur_entry.data.search_pattern.is_last_used) { SET_NO_HLSEARCH(!cur_entry.data.search_pattern.highlighted); } // Do not free shada entry: its allocated memory was saved above. @@ -1342,7 +1341,7 @@ static void shada_read(ShaDaReadDef *const sd_reader, const int flags) .y_width = (colnr_T) cur_entry.data.reg.width, .timestamp = cur_entry.timestamp, .additional_data = cur_entry.data.reg.additional_data, - })) { + }, cur_entry.data.reg.is_unnamed)) { shada_free_shada_entry(&cur_entry); } // Do not free shada entry: its allocated memory was saved above. @@ -1402,7 +1401,7 @@ static void shada_read(ShaDaReadDef *const sd_reader, const int flags) } case kSDItemBufferList: { for (size_t i = 0; i < cur_entry.data.buffer_list.size; i++) { - char *const sfname = path_shorten_fname_if_possible( + char *const sfname = path_try_shorten_fname( cur_entry.data.buffer_list.buffers[i].fname); buf_T *const buf = buflist_new( cur_entry.data.buffer_list.buffers[i].fname, sfname, 0, @@ -1432,8 +1431,8 @@ static void shada_read(ShaDaReadDef *const sd_reader, const int flags) fname = xstrdup(fname); } int kh_ret; - (void) kh_put(strset, &oldfiles_set, fname, &kh_ret); - list_append_allocated_string(oldfiles_list, fname); + (void)kh_put(strset, &oldfiles_set, fname, &kh_ret); + tv_list_append_allocated_string(oldfiles_list, fname); if (!want_marks) { // Avoid free because this string was already used. cur_entry.data.filemark.fname = NULL; @@ -1524,7 +1523,7 @@ static const char *shada_get_default_file(void) FUNC_ATTR_WARN_UNUSED_RESULT { if (default_shada_file == NULL) { - char *shada_dir = stdpaths_user_data_subpath("shada", 0); + char *shada_dir = stdpaths_user_data_subpath("shada", 0, false); default_shada_file = concat_fnames_realloc(shada_dir, "main.shada", true); } return default_shada_file; @@ -1570,13 +1569,17 @@ static char *shada_filename(const char *file) do { \ const String s_ = (s); \ msgpack_pack_str(spacker, s_.size); \ - msgpack_pack_str_body(spacker, s_.data, s_.size); \ + if (s_.size) { \ + msgpack_pack_str_body(spacker, s_.data, s_.size); \ + } \ } while (0) #define PACK_BIN(s) \ do { \ const String s_ = (s); \ msgpack_pack_bin(spacker, s_.size); \ - msgpack_pack_bin_body(spacker, s_.data, s_.size); \ + if (s_.size > 0) { \ + msgpack_pack_bin_body(spacker, s_.data, s_.size); \ + } \ } while (0) /// Write single ShaDa entry @@ -1599,13 +1602,13 @@ static ShaDaWriteResult shada_pack_entry(msgpack_packer *const packer, #define DUMP_ADDITIONAL_ELEMENTS(src, what) \ do { \ if ((src) != NULL) { \ - for (listitem_T *li = (src)->lv_first; li != NULL; li = li->li_next) { \ - if (encode_vim_to_msgpack(spacker, &li->li_tv, \ + TV_LIST_ITER((src), li, { \ + if (encode_vim_to_msgpack(spacker, TV_LIST_ITEM_TV(li), \ _("additional elements of ShaDa " what)) \ == FAIL) { \ goto shada_pack_entry_error; \ } \ - } \ + }); \ } \ } while (0) #define DUMP_ADDITIONAL_DATA(src, what) \ @@ -1616,10 +1619,10 @@ static ShaDaWriteResult shada_pack_entry(msgpack_packer *const packer, for (const hashitem_T *hi= d->dv_hashtab.ht_array; todo; hi++) { \ if (!HASHITEM_EMPTY(hi)) { \ todo--; \ - dictitem_T *const di = HI2DI(hi); \ - const size_t key_len = strlen((const char *) hi->hi_key); \ + dictitem_T *const di = TV_DICT_HI2DI(hi); \ + const size_t key_len = strlen((const char *)hi->hi_key); \ msgpack_pack_str(spacker, key_len); \ - msgpack_pack_str_body(spacker, (const char *) hi->hi_key, key_len); \ + msgpack_pack_str_body(spacker, (const char *)hi->hi_key, key_len); \ if (encode_vim_to_msgpack(spacker, &di->di_tv, \ _("additional data of ShaDa " what)) \ == FAIL) { \ @@ -1647,25 +1650,21 @@ static ShaDaWriteResult shada_pack_entry(msgpack_packer *const packer, case kSDItemHistoryEntry: { const bool is_hist_search = entry.data.history_item.histtype == HIST_SEARCH; - const size_t arr_size = 2 + (size_t) is_hist_search + (size_t) ( - entry.data.history_item.additional_elements == NULL - ? 0 - : entry.data.history_item.additional_elements->lv_len); + const size_t arr_size = 2 + (size_t)is_hist_search + (size_t)( + tv_list_len(entry.data.history_item.additional_elements)); msgpack_pack_array(spacker, arr_size); msgpack_pack_uint8(spacker, entry.data.history_item.histtype); PACK_BIN(cstr_as_string(entry.data.history_item.string)); if (is_hist_search) { - msgpack_pack_uint8(spacker, (uint8_t) entry.data.history_item.sep); + msgpack_pack_uint8(spacker, (uint8_t)entry.data.history_item.sep); } DUMP_ADDITIONAL_ELEMENTS(entry.data.history_item.additional_elements, "history entry item"); break; } case kSDItemVariable: { - const size_t arr_size = 2 + (size_t) ( - entry.data.global_var.additional_elements == NULL - ? 0 - : entry.data.global_var.additional_elements->lv_len); + const size_t arr_size = 2 + (size_t)( + tv_list_len(entry.data.global_var.additional_elements)); msgpack_pack_array(spacker, arr_size); const String varname = cstr_as_string(entry.data.global_var.name); PACK_BIN(varname); @@ -1684,10 +1683,8 @@ static ShaDaWriteResult shada_pack_entry(msgpack_packer *const packer, break; } case kSDItemSubString: { - const size_t arr_size = 1 + (size_t) ( - entry.data.sub_string.additional_elements == NULL - ? 0 - : entry.data.sub_string.additional_elements->lv_len); + const size_t arr_size = 1 + (size_t)( + tv_list_len(entry.data.sub_string.additional_elements)); msgpack_pack_array(spacker, arr_size); PACK_BIN(cstr_as_string(entry.data.sub_string.sub)); DUMP_ADDITIONAL_ELEMENTS(entry.data.sub_string.additional_elements, @@ -1783,6 +1780,7 @@ static ShaDaWriteResult shada_pack_entry(msgpack_packer *const packer, 2 // Register contents and name + ONE_IF_NOT_DEFAULT(entry, reg.type) + ONE_IF_NOT_DEFAULT(entry, reg.width) + + ONE_IF_NOT_DEFAULT(entry, reg.is_unnamed) // Additional entries, if any: + (size_t) (entry.data.reg.additional_data == NULL ? 0 @@ -1803,6 +1801,14 @@ static ShaDaWriteResult shada_pack_entry(msgpack_packer *const packer, PACK_STATIC_STR(REG_KEY_WIDTH); msgpack_pack_uint64(spacker, (uint64_t) entry.data.reg.width); } + if (!CHECK_DEFAULT(entry, reg.is_unnamed)) { + PACK_STATIC_STR(REG_KEY_UNNAMED); + if (entry.data.reg.is_unnamed) { + msgpack_pack_true(spacker); + } else { + msgpack_pack_false(spacker); + } + } DUMP_ADDITIONAL_DATA(entry.data.reg.additional_data, "register item"); break; } @@ -1894,127 +1900,24 @@ shada_pack_entry_error: } #undef PACK_STRING -/// Write single ShaDa entry, converting it if needed +/// Write single ShaDa entry and free it afterwards /// -/// @warning Frees entry after packing. +/// Will not free if entry could not be freed. /// /// @param[in] packer Packer used to write entry. -/// @param[in] sd_conv Conversion definitions. -/// @param[in] entry Entry written. If entry.can_free_entry is false then -/// it assumes that entry was not converted, otherwise it -/// is assumed that entry was already converted. +/// @param[in] entry Entry written. /// @param[in] max_kbyte Maximum size of an item in KiB. Zero means no /// restrictions. -static ShaDaWriteResult shada_pack_encoded_entry(msgpack_packer *const packer, - const vimconv_T *const sd_conv, - PossiblyFreedShadaEntry entry, - const size_t max_kbyte) - FUNC_ATTR_NONNULL_ALL +static inline ShaDaWriteResult shada_pack_pfreed_entry( + msgpack_packer *const packer, PossiblyFreedShadaEntry entry, + const size_t max_kbyte) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_ALWAYS_INLINE { ShaDaWriteResult ret = kSDWriteSuccessfull; + ret = shada_pack_entry(packer, entry.data, max_kbyte); if (entry.can_free_entry) { - ret = shada_pack_entry(packer, entry.data, max_kbyte); shada_free_shada_entry(&entry.data); - return ret; } -#define RUN_WITH_CONVERTED_STRING(cstr, code) \ - do { \ - bool did_convert = false; \ - if (sd_conv->vc_type != CONV_NONE && has_non_ascii((cstr))) { \ - char *const converted_string = string_convert(sd_conv, (cstr), NULL); \ - if (converted_string != NULL) { \ - (cstr) = converted_string; \ - did_convert = true; \ - } \ - } \ - code \ - if (did_convert) { \ - xfree((cstr)); \ - } \ - } while (0) - switch (entry.data.type) { - case kSDItemUnknown: - case kSDItemMissing: { - assert(false); - } - case kSDItemSearchPattern: { - RUN_WITH_CONVERTED_STRING(entry.data.data.search_pattern.pat, { - ret = shada_pack_entry(packer, entry.data, max_kbyte); - }); - break; - } - case kSDItemHistoryEntry: { - RUN_WITH_CONVERTED_STRING(entry.data.data.history_item.string, { - ret = shada_pack_entry(packer, entry.data, max_kbyte); - }); - break; - } - case kSDItemSubString: { - RUN_WITH_CONVERTED_STRING(entry.data.data.sub_string.sub, { - ret = shada_pack_entry(packer, entry.data, max_kbyte); - }); - break; - } - case kSDItemVariable: { - if (sd_conv->vc_type != CONV_NONE) { - typval_T tgttv; - var_item_copy(sd_conv, &entry.data.data.global_var.value, &tgttv, - true, 0); - clear_tv(&entry.data.data.global_var.value); - entry.data.data.global_var.value = tgttv; - } - ret = shada_pack_entry(packer, entry.data, max_kbyte); - break; - } - case kSDItemRegister: { - bool did_convert = false; - if (sd_conv->vc_type != CONV_NONE) { - size_t first_non_ascii = 0; - for (size_t i = 0; i < entry.data.data.reg.contents_size; i++) { - if (has_non_ascii(entry.data.data.reg.contents[i])) { - first_non_ascii = i; - did_convert = true; - break; - } - } - if (did_convert) { - entry.data.data.reg.contents = - xmemdup(entry.data.data.reg.contents, - (entry.data.data.reg.contents_size - * sizeof(entry.data.data.reg.contents))); - for (size_t i = 0; i < entry.data.data.reg.contents_size; i++) { - if (i >= first_non_ascii) { - entry.data.data.reg.contents[i] = get_converted_string( - sd_conv, - entry.data.data.reg.contents[i], - strlen(entry.data.data.reg.contents[i])); - } else { - entry.data.data.reg.contents[i] = - xstrdup(entry.data.data.reg.contents[i]); - } - } - } - } - ret = shada_pack_entry(packer, entry.data, max_kbyte); - if (did_convert) { - for (size_t i = 0; i < entry.data.data.reg.contents_size; i++) { - xfree(entry.data.data.reg.contents[i]); - } - xfree(entry.data.data.reg.contents); - } - break; - } - case kSDItemHeader: - case kSDItemGlobalMark: - case kSDItemJump: - case kSDItemBufferList: - case kSDItemLocalMark: - case kSDItemChange: { - ret = shada_pack_entry(packer, entry.data, max_kbyte); - break; - } - } -#undef RUN_WITH_CONVERTED_STRING return ret; } @@ -2121,6 +2024,113 @@ shada_parse_msgpack_extra_bytes: return ret; } +/// Format shada entry for debugging purposes +/// +/// @param[in] entry ShaDa entry to format. +/// +/// @return string representing ShaDa entry in a static buffer. +static const char *shada_format_entry(const ShadaEntry entry) + FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_UNUSED FUNC_ATTR_NONNULL_RET +{ + static char ret[1024]; + ret[0] = 0; + vim_snprintf(S_LEN(ret), "%s", "[ ] ts=%" PRIu64 " "); + // ^ Space for `can_free_entry` + switch (entry.type) { + case kSDItemMissing: { + vim_snprintf_add(S_LEN(ret), "Missing"); + break; + } + case kSDItemHeader: { + vim_snprintf_add(S_LEN(ret), "Header { TODO }"); + break; + } + case kSDItemBufferList: { + vim_snprintf_add(S_LEN(ret), "BufferList { TODO }"); + break; + } + case kSDItemUnknown: { + vim_snprintf_add(S_LEN(ret), "Unknown { TODO }"); + break; + } + case kSDItemSearchPattern: { + vim_snprintf_add(S_LEN(ret), "SearchPattern { TODO }"); + break; + } + case kSDItemSubString: { + vim_snprintf_add(S_LEN(ret), "SubString { TODO }"); + break; + } + case kSDItemHistoryEntry: { + vim_snprintf_add(S_LEN(ret), "HistoryEntry { TODO }"); + break; + } + case kSDItemRegister: { + vim_snprintf_add(S_LEN(ret), "Register { TODO }"); + break; + } + case kSDItemVariable: { + vim_snprintf_add(S_LEN(ret), "Variable { TODO }"); + break; + } +#define FORMAT_MARK_ENTRY(entry_name, name_fmt, name_fmt_arg) \ + do { \ + typval_T ad_tv = { \ + .v_type = VAR_DICT, \ + .vval.v_dict = entry.data.filemark.additional_data \ + }; \ + size_t ad_len; \ + char *const ad = encode_tv2string(&ad_tv, &ad_len); \ + vim_snprintf_add( \ + S_LEN(ret), \ + entry_name " {" name_fmt " file=[%zu]\"%.512s\", " \ + "pos={l=%" PRIdLINENR ",c=%" PRIdCOLNR ",a=%" PRIdCOLNR "}, " \ + "ad={%p:[%zu]%.64s} }", \ + name_fmt_arg, \ + strlen(entry.data.filemark.fname), \ + entry.data.filemark.fname, \ + entry.data.filemark.mark.lnum, \ + entry.data.filemark.mark.col, \ + entry.data.filemark.mark.coladd, \ + (void *)entry.data.filemark.additional_data, \ + ad_len, \ + ad); \ + } while (0) + case kSDItemGlobalMark: { + FORMAT_MARK_ENTRY("GlobalMark", " name='%c',", entry.data.filemark.name); + break; + } + case kSDItemChange: { + FORMAT_MARK_ENTRY("Change", "%s", ""); + break; + } + case kSDItemLocalMark: { + FORMAT_MARK_ENTRY("LocalMark", " name='%c',", entry.data.filemark.name); + break; + } + case kSDItemJump: { + FORMAT_MARK_ENTRY("Jump", "%s", ""); + break; + } +#undef FORMAT_MARK_ENTRY + } + return ret; +} + +/// Format possibly freed shada entry for debugging purposes +/// +/// @param[in] entry ShaDa entry to format. +/// +/// @return string representing ShaDa entry in a static buffer. +static const char *shada_format_pfreed_entry( + const PossiblyFreedShadaEntry pfs_entry) + FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_UNUSED FUNC_ATTR_NONNULL_RET +{ + char *ret = (char *)shada_format_entry(pfs_entry.data); + ret[1] = (pfs_entry.can_free_entry ? 'T' : 'F'); + return ret; +} + /// Read and merge in ShaDa file, used when writing /// /// @param[in] sd_reader Structure containing file reader definition. @@ -2151,7 +2161,7 @@ static inline ShaDaWriteResult shada_read_when_writing( } case kSDReadStatusNotShaDa: { ret = kSDWriteReadNotShada; - // fallthrough + FALLTHROUGH; } case kSDReadStatusReadError: { return ret; @@ -2172,9 +2182,12 @@ static inline ShaDaWriteResult shada_read_when_writing( shada_free_shada_entry(&wms_entry->data); \ } \ } \ - wms_entry->can_free_entry = true; \ - wms_entry->data = (entry); \ + *wms_entry = pfs_entry; \ } while (0) + const PossiblyFreedShadaEntry pfs_entry = { + .can_free_entry = true, + .data = entry, + }; switch (entry.type) { case kSDItemMissing: { break; @@ -2226,13 +2239,49 @@ static inline ShaDaWriteResult shada_read_when_writing( break; } case kSDItemGlobalMark: { - const int idx = mark_global_index(entry.data.filemark.name); - if (idx < 0) { - ret = shada_pack_entry(packer, entry, 0); - shada_free_shada_entry(&entry); - break; + if (ascii_isdigit(entry.data.filemark.name)) { + bool processed_mark = false; + // Completely ignore numbered mark names, make a list sorted by + // timestamp. + for (size_t i = ARRAY_SIZE(wms->numbered_marks); i > 0; i--) { + ShadaEntry wms_entry = wms->numbered_marks[i - 1].data; + if (wms_entry.type != kSDItemGlobalMark) { + continue; + } + // Ignore duplicates. + if (wms_entry.timestamp == entry.timestamp + && (wms_entry.data.filemark.additional_data == NULL + && entry.data.filemark.additional_data == NULL) + && marks_equal(wms_entry.data.filemark.mark, + entry.data.filemark.mark) + && strcmp(wms_entry.data.filemark.fname, + entry.data.filemark.fname) == 0) { + shada_free_shada_entry(&entry); + processed_mark = true; + break; + } + if (wms_entry.timestamp >= entry.timestamp) { + processed_mark = true; + if (i < ARRAY_SIZE(wms->numbered_marks)) { + replace_numbered_mark(wms, i, pfs_entry); + } else { + shada_free_shada_entry(&entry); + } + break; + } + } + if (!processed_mark) { + replace_numbered_mark(wms, 0, pfs_entry); + } + } else { + const int idx = mark_global_index(entry.data.filemark.name); + if (idx < 0) { + ret = shada_pack_entry(packer, entry, 0); + shada_free_shada_entry(&entry); + break; + } + COMPARE_WITH_ENTRY(&wms->global_marks[idx], entry); } - COMPARE_WITH_ENTRY(&wms->global_marks[idx], entry); break; } case kSDItemChange: @@ -2276,8 +2325,7 @@ static inline ShaDaWriteResult shada_read_when_writing( shada_free_shada_entry(&wms_entry->data); } } - wms_entry->can_free_entry = true; - wms_entry->data = entry; + *wms_entry = pfs_entry; } } else { #define FREE_POSSIBLY_FREED_SHADA_ENTRY(entry) \ @@ -2317,6 +2365,194 @@ static inline ShaDaWriteResult shada_read_when_writing( return ret; } +/// Check whether buffer should be ignored +/// +/// @param[in] buf buf_T* to check. +/// @param[in] removable_bufs Cache of buffers ignored due to their location. +/// +/// @return true or false. +static inline bool ignore_buf(const buf_T *const buf, + khash_t(bufset) *const removable_bufs) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_ALWAYS_INLINE +{ + return (buf->b_ffname == NULL || !buf->b_p_bl || bt_quickfix(buf) \ + || in_bufset(removable_bufs, buf)); +} + +/// Get list of buffers to write to the shada file +/// +/// @param[in] removable_bufs Buffers which are ignored +/// +/// @return ShadaEntry List of buffers to save, kSDItemBufferList entry. +static inline ShadaEntry shada_get_buflist( + khash_t(bufset) *const removable_bufs) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_ALWAYS_INLINE +{ + int max_bufs = get_shada_parameter('%'); + size_t buf_count = 0; + FOR_ALL_BUFFERS(buf) { + if (!ignore_buf(buf, removable_bufs) + && (max_bufs < 0 || buf_count < (size_t)max_bufs)) { + buf_count++; + } + } + + ShadaEntry buflist_entry = (ShadaEntry) { + .type = kSDItemBufferList, + .timestamp = os_time(), + .data = { + .buffer_list = { + .size = buf_count, + .buffers = xmalloc(buf_count + * sizeof(*buflist_entry.data.buffer_list.buffers)), + }, + }, + }; + size_t i = 0; + FOR_ALL_BUFFERS(buf) { + if (ignore_buf(buf, removable_bufs)) { + continue; + } + if (i >= buf_count) { + break; + } + buflist_entry.data.buffer_list.buffers[i] = (struct buffer_list_buffer) { + .pos = buf->b_last_cursor.mark, + .fname = (char *)buf->b_ffname, + .additional_data = buf->additional_data, + }; + i++; + } + + return buflist_entry; +} + +/// Save search pattern to PossiblyFreedShadaEntry +/// +/// @param[out] ret_pse Location where result will be saved. +/// @param[in] get_pattern Function used to get pattern. +/// @param[in] is_substitute_pattern True if pattern in question is substitute +/// pattern. Also controls whether some +/// fields should be initialized to default +/// or values from get_pattern. +/// @param[in] search_last_used Result of search_was_last_used(). +/// @param[in] search_highlighted True if search pattern was highlighted by +/// &hlsearch and this information should be +/// saved. +static inline void add_search_pattern(PossiblyFreedShadaEntry *const ret_pse, + const SearchPatternGetter get_pattern, + const bool is_substitute_pattern, + const bool search_last_used, + const bool search_highlighted) + FUNC_ATTR_ALWAYS_INLINE +{ + const ShadaEntry defaults = sd_default_values[kSDItemSearchPattern]; + SearchPattern pat; + get_pattern(&pat); + if (pat.pat != NULL) { + *ret_pse = (PossiblyFreedShadaEntry) { + .can_free_entry = false, + .data = { + .type = kSDItemSearchPattern, + .timestamp = pat.timestamp, + .data = { + .search_pattern = { + .magic = pat.magic, + .smartcase = !pat.no_scs, + .has_line_offset = (is_substitute_pattern + ? defaults.data.search_pattern.has_line_offset + : pat.off.line), + .place_cursor_at_end = ( + is_substitute_pattern + ? defaults.data.search_pattern.place_cursor_at_end + : pat.off.end), + .offset = (is_substitute_pattern + ? defaults.data.search_pattern.offset + : pat.off.off), + .is_last_used = (is_substitute_pattern ^ search_last_used), + .is_substitute_pattern = is_substitute_pattern, + .highlighted = ((is_substitute_pattern ^ search_last_used) + && search_highlighted), + .pat = (char *)pat.pat, + .additional_data = pat.additional_data, + .search_backward = (!is_substitute_pattern && pat.off.dir == '?'), + } + } + } + }; + } +} + +/// Initialize registers for writing to the ShaDa file +/// +/// @param[in] wms The WriteMergerState used when writing. +/// @param[in] max_reg_lines The maximum number of register lines. +static inline void shada_initialize_registers(WriteMergerState *const wms, + int max_reg_lines) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_ALWAYS_INLINE +{ + const void *reg_iter = NULL; + const bool limit_reg_lines = max_reg_lines >= 0; + do { + yankreg_T reg; + char name = NUL; + bool is_unnamed = false; + reg_iter = op_register_iter(reg_iter, &name, ®, &is_unnamed); + if (name == NUL) { + break; + } + if (limit_reg_lines && reg.y_size > (size_t)max_reg_lines) { + continue; + } + wms->registers[op_reg_index(name)] = (PossiblyFreedShadaEntry) { + .can_free_entry = false, + .data = { + .type = kSDItemRegister, + .timestamp = reg.timestamp, + .data = { + .reg = { + .contents = (char **)reg.y_array, + .contents_size = (size_t)reg.y_size, + .type = reg.y_type, + .width = (size_t)(reg.y_type == kMTBlockWise ? reg.y_width : 0), + .additional_data = reg.additional_data, + .name = name, + .is_unnamed = is_unnamed, + } + } + } + }; + } while (reg_iter != NULL); +} + +/// Replace numbered mark in WriteMergerState +/// +/// Frees the last mark, moves (including adjusting mark names) marks from idx +/// to the last-but-one one and saves the new mark at given index. +/// +/// @param[out] wms Merger state to adjust. +/// @param[in] idx Index at which new mark should be placed. +/// @param[in] entry New mark. +static inline void replace_numbered_mark(WriteMergerState *const wms, + const size_t idx, + const PossiblyFreedShadaEntry entry) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_ALWAYS_INLINE +{ + if (ARRAY_LAST_ENTRY(wms->numbered_marks).can_free_entry) { + shada_free_shada_entry(&ARRAY_LAST_ENTRY(wms->numbered_marks).data); + } + for (size_t i = idx; i < ARRAY_SIZE(wms->numbered_marks) - 1; i++) { + if (wms->numbered_marks[i].data.type == kSDItemGlobalMark) { + wms->numbered_marks[i].data.data.filemark.name = (char)('0' + (int)i + 1); + } + } + memmove(wms->numbered_marks + idx + 1, wms->numbered_marks + idx, + sizeof(wms->numbered_marks[0]) + * (ARRAY_SIZE(wms->numbered_marks) - 1 - idx)); + wms->numbered_marks[idx] = entry; + wms->numbered_marks[idx].data.data.filemark.name = (char)('0' + (int)idx); +} + /// Write ShaDa file /// /// @param[in] sd_writer Structure containing file writer definition. @@ -2343,7 +2579,6 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer, if (max_reg_lines < 0) { max_reg_lines = get_shada_parameter('"'); } - const bool limit_reg_lines = max_reg_lines >= 0; const bool dump_registers = (max_reg_lines != 0); khash_t(bufset) removable_bufs = KHASH_EMPTY_TABLE(bufset); const size_t max_kbyte = (size_t) max_kbyte_i; @@ -2423,46 +2658,13 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer, // Write buffer list if (find_shada_parameter('%') != NULL) { - size_t buf_count = 0; -#define IGNORE_BUF(buf)\ - (buf->b_ffname == NULL || !buf->b_p_bl || bt_quickfix(buf) \ - || in_bufset(&removable_bufs, buf)) - FOR_ALL_BUFFERS(buf) { - if (!IGNORE_BUF(buf)) { - buf_count++; - } - } - - ShadaEntry buflist_entry = (ShadaEntry) { - .type = kSDItemBufferList, - .timestamp = os_time(), - .data = { - .buffer_list = { - .size = buf_count, - .buffers = xmalloc(buf_count - * sizeof(*buflist_entry.data.buffer_list.buffers)), - }, - }, - }; - size_t i = 0; - FOR_ALL_BUFFERS(buf) { - if (IGNORE_BUF(buf)) { - continue; - } - buflist_entry.data.buffer_list.buffers[i] = (struct buffer_list_buffer) { - .pos = buf->b_last_cursor.mark, - .fname = (char *) buf->b_ffname, - .additional_data = buf->additional_data, - }; - i++; - } + ShadaEntry buflist_entry = shada_get_buflist(&removable_bufs); if (shada_pack_entry(packer, buflist_entry, 0) == kSDWriteFailed) { xfree(buflist_entry.data.buffer_list.buffers); ret = kSDWriteFailed; goto shada_write_exit; } xfree(buflist_entry.data.buffer_list.buffers); -#undef IGNORE_BUF } // Write some of the variables @@ -2477,11 +2679,7 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer, break; } typval_T tgttv; - if (sd_writer->sd_conv.vc_type != CONV_NONE) { - var_item_copy(&sd_writer->sd_conv, &vartv, &tgttv, true, 0); - } else { - copy_tv(&vartv, &tgttv); - } + tv_copy(&vartv, &tgttv); ShaDaWriteResult spe_ret; if ((spe_ret = shada_pack_entry(packer, (ShadaEntry) { .type = kSDItemVariable, @@ -2494,13 +2692,13 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer, } } }, max_kbyte)) == kSDWriteFailed) { - clear_tv(&vartv); - clear_tv(&tgttv); + tv_clear(&vartv); + tv_clear(&tgttv); ret = kSDWriteFailed; goto shada_write_exit; } - clear_tv(&vartv); - clear_tv(&tgttv); + tv_clear(&vartv); + tv_clear(&tgttv); if (spe_ret == kSDWriteSuccessfull) { int kh_ret; (void) kh_put(strset, &wms->dumped_variables, name, &kh_ret); @@ -2511,45 +2709,14 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer, const bool search_highlighted = !(no_hlsearch || find_shada_parameter('h') != NULL); const bool search_last_used = search_was_last_used(); -#define ADD_SEARCH_PAT(func, wms_attr, hlo, pcae, o, is_sub) \ - do { \ - SearchPattern pat; \ - func(&pat); \ - if (pat.pat != NULL) { \ - wms->wms_attr = (PossiblyFreedShadaEntry) { \ - .can_free_entry = false, \ - .data = { \ - .type = kSDItemSearchPattern, \ - .timestamp = pat.timestamp, \ - .data = { \ - .search_pattern = { \ - .magic = pat.magic, \ - .smartcase = !pat.no_scs, \ - .has_line_offset = hlo, \ - .place_cursor_at_end = pcae, \ - .offset = o, \ - .is_last_used = (is_sub ^ search_last_used), \ - .is_substitute_pattern = is_sub, \ - .highlighted = ((is_sub ^ search_last_used) \ - && search_highlighted), \ - .pat = (char *) pat.pat, \ - .additional_data = pat.additional_data, \ - .search_backward = (!is_sub && pat.off.dir == '?'), \ - } \ - } \ - } \ - }; \ - } \ - } while (0) // Initialize search pattern - ADD_SEARCH_PAT(get_search_pattern, search_pattern, pat.off.line, \ - pat.off.end, pat.off.off, false); + add_search_pattern(&wms->search_pattern, &get_search_pattern, false, + search_last_used, search_highlighted); // Initialize substitute search pattern - ADD_SEARCH_PAT(get_substitute_pattern, sub_search_pattern, false, false, 0, - true); -#undef ADD_SEARCH_PAT + add_search_pattern(&wms->sub_search_pattern, &get_substitute_pattern, true, + search_last_used, search_highlighted); // Initialize substitute replacement string { @@ -2572,10 +2739,18 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer, // Initialize jump list const void *jump_iter = NULL; + setpcmark(); + cleanup_jumplist(); do { xfmark_T fm; - cleanup_jumplist(); jump_iter = mark_jumplist_iter(jump_iter, curwin, &fm); + + if (fm.fmark.mark.lnum == 0) { + iemsgf("ShaDa: mark lnum zero (ji:%p, js:%p, len:%i)", + (void *)jump_iter, (void *)&curwin->w_jumplist[0], + curwin->w_jumplistlen); + continue; + } const buf_T *const buf = (fm.fmark.fnum == 0 ? NULL : buflist_findnr(fm.fmark.fnum)); @@ -2610,6 +2785,7 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer, // Initialize global marks if (dump_global_marks) { const void *global_mark_iter = NULL; + size_t digit_mark_idx = 0; do { char name = NUL; xfmark_T fm; @@ -2632,7 +2808,7 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer, } fname = (const char *) buf->b_ffname; } - wms->global_marks[mark_global_index(name)] = (PossiblyFreedShadaEntry) { + const PossiblyFreedShadaEntry pf_entry = { .can_free_entry = false, .data = { .type = kSDItemGlobalMark, @@ -2642,45 +2818,22 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer, .mark = fm.fmark.mark, .name = name, .additional_data = fm.fmark.additional_data, - .fname = (char *) fname, + .fname = (char *)fname, } } }, }; + if (ascii_isdigit(name)) { + replace_numbered_mark(wms, digit_mark_idx++, pf_entry); + } else { + wms->global_marks[mark_global_index(name)] = pf_entry; + } } while (global_mark_iter != NULL); } // Initialize registers if (dump_registers) { - const void *reg_iter = NULL; - do { - yankreg_T reg; - char name = NUL; - reg_iter = op_register_iter(reg_iter, &name, ®); - if (name == NUL) { - break; - } - if (limit_reg_lines && reg.y_size > (size_t)max_reg_lines) { - continue; - } - wms->registers[op_reg_index(name)] = (PossiblyFreedShadaEntry) { - .can_free_entry = false, - .data = { - .type = kSDItemRegister, - .timestamp = reg.timestamp, - .data = { - .reg = { - .contents = (char **) reg.y_array, - .contents_size = (size_t) reg.y_size, - .type = reg.y_type, - .width = (size_t) (reg.y_type == kMTBlockWise ? reg.y_width : 0), - .additional_data = reg.additional_data, - .name = name, - } - } - } - }; - } while (reg_iter != NULL); + shada_initialize_registers(wms, max_reg_lines); } // Initialize buffers @@ -2756,14 +2909,33 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer, } } + // Update numbered marks: '0' should be replaced with the current position, + // '9' should be removed and all other marks shifted. + if (!ignore_buf(curbuf, &removable_bufs) && curwin->w_cursor.lnum != 0) { + replace_numbered_mark(wms, 0, (PossiblyFreedShadaEntry) { + .can_free_entry = false, + .data = { + .type = kSDItemGlobalMark, + .timestamp = os_time(), + .data = { + .filemark = { + .mark = curwin->w_cursor, + .name = '0', + .additional_data = NULL, + .fname = (char *)curbuf->b_ffname, + } + } + }, + }); + } + // Write the rest #define PACK_WMS_ARRAY(wms_array) \ do { \ for (size_t i_ = 0; i_ < ARRAY_SIZE(wms_array); i_++) { \ if (wms_array[i_].data.type != kSDItemMissing) { \ - if (shada_pack_encoded_entry(packer, &sd_writer->sd_conv, \ - wms_array[i_], \ - max_kbyte) == kSDWriteFailed) { \ + if (shada_pack_pfreed_entry(packer, wms_array[i_], max_kbyte) \ + == kSDWriteFailed) { \ ret = kSDWriteFailed; \ goto shada_write_exit; \ } \ @@ -2771,10 +2943,11 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer, } \ } while (0) PACK_WMS_ARRAY(wms->global_marks); + PACK_WMS_ARRAY(wms->numbered_marks); PACK_WMS_ARRAY(wms->registers); for (size_t i = 0; i < wms->jumps_size; i++) { - if (shada_pack_encoded_entry(packer, &sd_writer->sd_conv, wms->jumps[i], - max_kbyte) == kSDWriteFailed) { + if (shada_pack_pfreed_entry(packer, wms->jumps[i], max_kbyte) + == kSDWriteFailed) { ret = kSDWriteFailed; goto shada_write_exit; } @@ -2782,8 +2955,8 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer, #define PACK_WMS_ENTRY(wms_entry) \ do { \ if (wms_entry.data.type != kSDItemMissing) { \ - if (shada_pack_encoded_entry(packer, &sd_writer->sd_conv, wms_entry, \ - max_kbyte) == kSDWriteFailed) { \ + if (shada_pack_pfreed_entry(packer, wms_entry, max_kbyte) \ + == kSDWriteFailed) { \ ret = kSDWriteFailed; \ goto shada_write_exit; \ } \ @@ -2810,9 +2983,8 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer, for (size_t i = 0; i < file_markss_to_dump; i++) { PACK_WMS_ARRAY(all_file_markss[i]->marks); for (size_t j = 0; j < all_file_markss[i]->changes_size; j++) { - if (shada_pack_encoded_entry(packer, &sd_writer->sd_conv, - all_file_markss[i]->changes[j], - max_kbyte) == kSDWriteFailed) { + if (shada_pack_pfreed_entry(packer, all_file_markss[i]->changes[j], + max_kbyte) == kSDWriteFailed) { ret = kSDWriteFailed; goto shada_write_exit; } @@ -2836,8 +3008,8 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer, if (dump_one_history[i]) { hms_insert_whole_neovim_history(&wms->hms[i]); HMS_ITER(&wms->hms[i], cur_entry, { - if (shada_pack_encoded_entry( - packer, &sd_writer->sd_conv, (PossiblyFreedShadaEntry) { + if (shada_pack_pfreed_entry( + packer, (PossiblyFreedShadaEntry) { .data = cur_entry->data, .can_free_entry = cur_entry->can_free_entry, }, max_kbyte) == kSDWriteFailed) { @@ -2866,6 +3038,7 @@ shada_write_exit: return ret; } +#undef IGNORE_BUF #undef PACK_STATIC_STR /// Write ShaDa file to a given location @@ -2976,7 +3149,7 @@ shada_write_file_nomerge: {} if (sd_writer.cookie == NULL) { xfree(fname); xfree(tempname); - if (sd_reader.close != NULL) { + if (sd_reader.cookie != NULL) { sd_reader.close(&sd_reader); } return FAIL; @@ -2988,8 +3161,6 @@ shada_write_file_nomerge: {} verbose_leave(); } - convert_setup(&sd_writer.sd_conv, p_enc, "utf-8"); - const ShaDaWriteResult sw_ret = shada_write(&sd_writer, (nomerge ? NULL : &sd_reader)); @@ -3037,7 +3208,7 @@ shada_write_file_nomerge: {} } else { if (sw_ret == kSDWriteReadNotShada) { EMSG3(_(RNERR "Did not rename %s because %s " - "does not looks like a ShaDa file"), tempname, fname); + "does not look like a ShaDa file"), tempname, fname); } else { EMSG3(_(RNERR "Did not rename %s to %s because there were errors " "during writing it"), tempname, fname); @@ -3104,17 +3275,17 @@ static void shada_free_shada_entry(ShadaEntry *const entry) case kSDItemJump: case kSDItemGlobalMark: case kSDItemLocalMark: { - dict_unref(entry->data.filemark.additional_data); + tv_dict_unref(entry->data.filemark.additional_data); xfree(entry->data.filemark.fname); break; } case kSDItemSearchPattern: { - dict_unref(entry->data.search_pattern.additional_data); + tv_dict_unref(entry->data.search_pattern.additional_data); xfree(entry->data.search_pattern.pat); break; } case kSDItemRegister: { - dict_unref(entry->data.reg.additional_data); + tv_dict_unref(entry->data.reg.additional_data); for (size_t i = 0; i < entry->data.reg.contents_size; i++) { xfree(entry->data.reg.contents[i]); } @@ -3122,25 +3293,25 @@ static void shada_free_shada_entry(ShadaEntry *const entry) break; } case kSDItemHistoryEntry: { - list_unref(entry->data.history_item.additional_elements); + tv_list_unref(entry->data.history_item.additional_elements); xfree(entry->data.history_item.string); break; } case kSDItemVariable: { - list_unref(entry->data.global_var.additional_elements); + tv_list_unref(entry->data.global_var.additional_elements); xfree(entry->data.global_var.name); - clear_tv(&entry->data.global_var.value); + tv_clear(&entry->data.global_var.value); break; } case kSDItemSubString: { - list_unref(entry->data.sub_string.additional_elements); + tv_list_unref(entry->data.sub_string.additional_elements); xfree(entry->data.sub_string.sub); break; } case kSDItemBufferList: { for (size_t i = 0; i < entry->data.buffer_list.size; i++) { xfree(entry->data.buffer_list.buffers[i].fname); - dict_unref(entry->data.buffer_list.buffers[i].additional_data); + tv_dict_unref(entry->data.buffer_list.buffers[i].additional_data); } xfree(entry->data.buffer_list.buffers); break; @@ -3277,29 +3448,6 @@ static ShaDaReadResult msgpack_read_uint64(ShaDaReadDef *const sd_reader, return kSDReadStatusSuccess; } -/// Convert or copy and return a string -/// -/// @param[in] sd_conv Conversion definition. -/// @param[in] str String to convert. -/// @param[in] len String length. -/// -/// @return [allocated] converted string or copy of the original string. -static inline char *get_converted_string(const vimconv_T *const sd_conv, - const char *const str, - const size_t len) - FUNC_ATTR_NONNULL_ALL FUNC_ATTR_MALLOC FUNC_ATTR_WARN_UNUSED_RESULT -{ - if (!has_non_ascii_len(str, len)) { - return xmemdupz(str, len); - } - size_t new_len = len; - char *const new_str = string_convert(sd_conv, str, &new_len); - if (new_str == NULL) { - return xmemdupz(str, len); - } - return new_str; -} - #define READERR(entry_name, error_desc) \ RERR "Error while reading ShaDa file: " \ entry_name " entry at position %" PRIu64 " " \ @@ -3328,59 +3476,58 @@ static inline char *get_converted_string(const vimconv_T *const sd_conv, } \ tgt = proc(obj.via.attr); \ } while (0) -#define CHECK_KEY_IS_STR(entry_name) \ - if (unpacked.data.via.map.ptr[i].key.type != MSGPACK_OBJECT_STR) { \ +#define CHECK_KEY_IS_STR(un, entry_name) \ + if (un.data.via.map.ptr[i].key.type != MSGPACK_OBJECT_STR) { \ emsgf(_(READERR(entry_name, "has key which is not a string")), \ initial_fpos); \ CLEAR_GA_AND_ERROR_OUT(ad_ga); \ - } else if (unpacked.data.via.map.ptr[i].key.via.str.size == 0) { \ + } else if (un.data.via.map.ptr[i].key.via.str.size == 0) { \ emsgf(_(READERR(entry_name, "has empty key")), initial_fpos); \ CLEAR_GA_AND_ERROR_OUT(ad_ga); \ } -#define CHECKED_KEY(entry_name, name, error_desc, tgt, condition, attr, proc) \ +#define CHECKED_KEY(un, entry_name, name, error_desc, tgt, condition, attr, \ + proc) \ else if (CHECK_KEY( /* NOLINT(readability/braces) */ \ - unpacked.data.via.map.ptr[i].key, name)) { \ + un.data.via.map.ptr[i].key, name)) { \ CHECKED_ENTRY( \ condition, "has " name " key value " error_desc, \ - entry_name, unpacked.data.via.map.ptr[i].val, \ + entry_name, un.data.via.map.ptr[i].val, \ tgt, attr, proc); \ } -#define TYPED_KEY(entry_name, name, type_name, tgt, objtype, attr, proc) \ +#define TYPED_KEY(un, entry_name, name, type_name, tgt, objtype, attr, proc) \ CHECKED_KEY( \ - entry_name, name, "which is not " type_name, tgt, \ - unpacked.data.via.map.ptr[i].val.type == MSGPACK_OBJECT_##objtype, \ + un, entry_name, name, "which is not " type_name, tgt, \ + un.data.via.map.ptr[i].val.type == MSGPACK_OBJECT_##objtype, \ attr, proc) -#define BOOLEAN_KEY(entry_name, name, tgt) \ - TYPED_KEY(entry_name, name, "a boolean", tgt, BOOLEAN, boolean, ID) -#define STRING_KEY(entry_name, name, tgt) \ - TYPED_KEY(entry_name, name, "a binary", tgt, BIN, bin, BINDUP) -#define CONVERTED_STRING_KEY(entry_name, name, tgt) \ - TYPED_KEY(entry_name, name, "a binary", tgt, BIN, bin, BIN_CONVERTED) -#define INT_KEY(entry_name, name, tgt, proc) \ +#define BOOLEAN_KEY(un, entry_name, name, tgt) \ + TYPED_KEY(un, entry_name, name, "a boolean", tgt, BOOLEAN, boolean, ID) +#define STRING_KEY(un, entry_name, name, tgt) \ + TYPED_KEY(un, entry_name, name, "a binary", tgt, BIN, bin, BINDUP) +#define CONVERTED_STRING_KEY(un, entry_name, name, tgt) \ + TYPED_KEY(un, entry_name, name, "a binary", tgt, BIN, bin, \ + BIN_CONVERTED) +#define INT_KEY(un, entry_name, name, tgt, proc) \ CHECKED_KEY( \ - entry_name, name, "which is not an integer", tgt, \ - ((unpacked.data.via.map.ptr[i].val.type \ + un, entry_name, name, "which is not an integer", tgt, \ + ((un.data.via.map.ptr[i].val.type \ == MSGPACK_OBJECT_POSITIVE_INTEGER) \ - || (unpacked.data.via.map.ptr[i].val.type \ + || (un.data.via.map.ptr[i].val.type \ == MSGPACK_OBJECT_NEGATIVE_INTEGER)), \ i64, proc) -#define INTEGER_KEY(entry_name, name, tgt) \ - INT_KEY(entry_name, name, tgt, TOINT) -#define LONG_KEY(entry_name, name, tgt) \ - INT_KEY(entry_name, name, tgt, TOLONG) -#define ADDITIONAL_KEY \ +#define INTEGER_KEY(un, entry_name, name, tgt) \ + INT_KEY(un, entry_name, name, tgt, TOINT) +#define LONG_KEY(un, entry_name, name, tgt) \ + INT_KEY(un, entry_name, name, tgt, TOLONG) +#define ADDITIONAL_KEY(un) \ else { /* NOLINT(readability/braces) */ \ ga_grow(&ad_ga, 1); \ - memcpy(((char *)ad_ga.ga_data) + ((size_t) ad_ga.ga_len \ - * sizeof(*unpacked.data.via.map.ptr)), \ - unpacked.data.via.map.ptr + i, \ - sizeof(*unpacked.data.via.map.ptr)); \ + memcpy(((char *)ad_ga.ga_data) + ((size_t)ad_ga.ga_len \ + * sizeof(*un.data.via.map.ptr)), \ + un.data.via.map.ptr + i, \ + sizeof(*un.data.via.map.ptr)); \ ad_ga.ga_len++; \ } -#define CONVERTED(str, len) ( \ - sd_reader->sd_conv.vc_type != CONV_NONE \ - ? get_converted_string(&sd_reader->sd_conv, (str), (len)) \ - : xmemdupz((str), (len))) +#define CONVERTED(str, len) (xmemdupz((str), (len))) #define BIN_CONVERTED(b) CONVERTED(b.ptr, b.size) #define SET_ADDITIONAL_DATA(tgt, name) \ do { \ @@ -3401,7 +3548,7 @@ static inline char *get_converted_string(const vimconv_T *const sd_conv, "cannot be converted to a VimL dictionary")), \ initial_fpos); \ ga_clear(&ad_ga); \ - clear_tv(&adtv); \ + tv_clear(&adtv); \ goto shada_read_next_item_error; \ } \ tgt = adtv.vval.v_dict; \ @@ -3424,7 +3571,7 @@ static inline char *get_converted_string(const vimconv_T *const sd_conv, if (msgpack_to_vim(obj, &aetv) == FAIL) { \ emsgf(_(READERR(name, "cannot be converted to a VimL list")), \ initial_fpos); \ - clear_tv(&aetv); \ + tv_clear(&aetv); \ goto shada_read_next_item_error; \ } \ assert(aetv.v_type == VAR_LIST); \ @@ -3483,8 +3630,16 @@ shada_read_next_item_start: return mru_ret; } - const size_t length = (size_t) length_u64; - entry->timestamp = (Timestamp) timestamp_u64; + if (length_u64 > PTRDIFF_MAX) { + emsgf(_(RCERR "Error while reading ShaDa file: " + "there is an item at position %" PRIu64 " " + "that is stated to be too long"), + initial_fpos); + return kSDReadStatusNotShaDa; + } + + const size_t length = (size_t)length_u64; + entry->timestamp = (Timestamp)timestamp_u64; if (type_u64 == 0) { // kSDItemUnknown cannot possibly pass that far because it is -1 and that @@ -3574,28 +3729,29 @@ shada_read_next_item_start: garray_T ad_ga; ga_init(&ad_ga, sizeof(*(unpacked.data.via.map.ptr)), 1); for (size_t i = 0; i < unpacked.data.via.map.size; i++) { - CHECK_KEY_IS_STR("search pattern") - BOOLEAN_KEY("search pattern", SEARCH_KEY_MAGIC, + CHECK_KEY_IS_STR(unpacked, "search pattern") + BOOLEAN_KEY(unpacked, "search pattern", SEARCH_KEY_MAGIC, entry->data.search_pattern.magic) - BOOLEAN_KEY("search pattern", SEARCH_KEY_SMARTCASE, + BOOLEAN_KEY(unpacked, "search pattern", SEARCH_KEY_SMARTCASE, entry->data.search_pattern.smartcase) - BOOLEAN_KEY("search pattern", SEARCH_KEY_HAS_LINE_OFFSET, + BOOLEAN_KEY(unpacked, "search pattern", SEARCH_KEY_HAS_LINE_OFFSET, entry->data.search_pattern.has_line_offset) - BOOLEAN_KEY("search pattern", SEARCH_KEY_PLACE_CURSOR_AT_END, + BOOLEAN_KEY(unpacked, "search pattern", SEARCH_KEY_PLACE_CURSOR_AT_END, entry->data.search_pattern.place_cursor_at_end) - BOOLEAN_KEY("search pattern", SEARCH_KEY_IS_LAST_USED, + BOOLEAN_KEY(unpacked, "search pattern", SEARCH_KEY_IS_LAST_USED, entry->data.search_pattern.is_last_used) - BOOLEAN_KEY("search pattern", SEARCH_KEY_IS_SUBSTITUTE_PATTERN, + BOOLEAN_KEY(unpacked, "search pattern", + SEARCH_KEY_IS_SUBSTITUTE_PATTERN, entry->data.search_pattern.is_substitute_pattern) - BOOLEAN_KEY("search pattern", SEARCH_KEY_HIGHLIGHTED, + BOOLEAN_KEY(unpacked, "search pattern", SEARCH_KEY_HIGHLIGHTED, entry->data.search_pattern.highlighted) - BOOLEAN_KEY("search pattern", SEARCH_KEY_BACKWARD, + BOOLEAN_KEY(unpacked, "search pattern", SEARCH_KEY_BACKWARD, entry->data.search_pattern.search_backward) - INTEGER_KEY("search pattern", SEARCH_KEY_OFFSET, + INTEGER_KEY(unpacked, "search pattern", SEARCH_KEY_OFFSET, entry->data.search_pattern.offset) - CONVERTED_STRING_KEY("search pattern", SEARCH_KEY_PAT, + CONVERTED_STRING_KEY(unpacked, "search pattern", SEARCH_KEY_PAT, entry->data.search_pattern.pat) - ADDITIONAL_KEY + ADDITIONAL_KEY(unpacked) } if (entry->data.search_pattern.pat == NULL) { emsgf(_(READERR("search pattern", "has no pattern")), initial_fpos); @@ -3616,7 +3772,7 @@ shada_read_next_item_start: garray_T ad_ga; ga_init(&ad_ga, sizeof(*(unpacked.data.via.map.ptr)), 1); for (size_t i = 0; i < unpacked.data.via.map.size; i++) { - CHECK_KEY_IS_STR("mark") + CHECK_KEY_IS_STR(unpacked, "mark") if (CHECK_KEY(unpacked.data.via.map.ptr[i].key, KEY_NAME_CHAR)) { if (type_u64 == kSDItemJump || type_u64 == kSDItemChange) { emsgf(_(READERR("mark", "has n key which is only valid for " @@ -3630,10 +3786,10 @@ shada_read_next_item_start: "mark", unpacked.data.via.map.ptr[i].val, entry->data.filemark.name, u64, TOCHAR); } - LONG_KEY("mark", KEY_LNUM, entry->data.filemark.mark.lnum) - INTEGER_KEY("mark", KEY_COL, entry->data.filemark.mark.col) - STRING_KEY("mark", KEY_FILE, entry->data.filemark.fname) - ADDITIONAL_KEY + LONG_KEY(unpacked, "mark", KEY_LNUM, entry->data.filemark.mark.lnum) + INTEGER_KEY(unpacked, "mark", KEY_COL, entry->data.filemark.mark.col) + STRING_KEY(unpacked, "mark", KEY_FILE, entry->data.filemark.fname) + ADDITIONAL_KEY(unpacked) } if (entry->data.filemark.fname == NULL) { emsgf(_(READERR("mark", "is missing file name")), initial_fpos); @@ -3658,7 +3814,7 @@ shada_read_next_item_start: garray_T ad_ga; ga_init(&ad_ga, sizeof(*(unpacked.data.via.map.ptr)), 1); for (size_t i = 0; i < unpacked.data.via.map.size; i++) { - CHECK_KEY_IS_STR("register") + CHECK_KEY_IS_STR(unpacked, "register") if (CHECK_KEY(unpacked.data.via.map.ptr[i].key, REG_KEY_CONTENTS)) { if (unpacked.data.via.map.ptr[i].val.type != MSGPACK_OBJECT_ARRAY) { @@ -3676,8 +3832,8 @@ shada_read_next_item_start: } const msgpack_object_array arr = unpacked.data.via.map.ptr[i].val.via.array; - for (size_t i = 0; i < arr.size; i++) { - if (arr.ptr[i].type != MSGPACK_OBJECT_BIN) { + for (size_t j = 0; j < arr.size; j++) { + if (arr.ptr[j].type != MSGPACK_OBJECT_BIN) { emsgf(_(READERR("register", "has " REG_KEY_CONTENTS " array " "with non-binary value")), initial_fpos); CLEAR_GA_AND_ERROR_OUT(ad_ga); @@ -3685,17 +3841,19 @@ shada_read_next_item_start: } entry->data.reg.contents_size = arr.size; entry->data.reg.contents = xmalloc(arr.size * sizeof(char *)); - for (size_t i = 0; i < arr.size; i++) { - entry->data.reg.contents[i] = BIN_CONVERTED(arr.ptr[i].via.bin); + for (size_t j = 0; j < arr.size; j++) { + entry->data.reg.contents[j] = BIN_CONVERTED(arr.ptr[j].via.bin); } } - TYPED_KEY("register", REG_KEY_TYPE, "an unsigned integer", + BOOLEAN_KEY(unpacked, "register", REG_KEY_UNNAMED, + entry->data.reg.is_unnamed) + TYPED_KEY(unpacked, "register", REG_KEY_TYPE, "an unsigned integer", entry->data.reg.type, POSITIVE_INTEGER, u64, TOU8) - TYPED_KEY("register", KEY_NAME_CHAR, "an unsigned integer", + TYPED_KEY(unpacked, "register", KEY_NAME_CHAR, "an unsigned integer", entry->data.reg.name, POSITIVE_INTEGER, u64, TOCHAR) - TYPED_KEY("register", REG_KEY_WIDTH, "an unsigned integer", + TYPED_KEY(unpacked, "register", REG_KEY_WIDTH, "an unsigned integer", entry->data.reg.width, POSITIVE_INTEGER, u64, TOSIZE) - ADDITIONAL_KEY + ADDITIONAL_KEY(unpacked) } if (entry->data.reg.contents == NULL) { emsgf(_(READERR("register", "has missing " REG_KEY_CONTENTS " array")), @@ -3753,30 +3911,14 @@ shada_read_next_item_start: (char) unpacked.data.via.array.ptr[2].via.u64; } size_t strsize; - if (sd_reader->sd_conv.vc_type == CONV_NONE - || !has_non_ascii_len(unpacked.data.via.array.ptr[1].via.bin.ptr, - unpacked.data.via.array.ptr[1].via.bin.size)) { -shada_read_next_item_hist_no_conv: - strsize = ( - unpacked.data.via.array.ptr[1].via.bin.size - + 1 // Zero byte - + 1); // Separator character - entry->data.history_item.string = xmalloc(strsize); - memcpy(entry->data.history_item.string, - unpacked.data.via.array.ptr[1].via.bin.ptr, - unpacked.data.via.array.ptr[1].via.bin.size); - } else { - size_t len = unpacked.data.via.array.ptr[1].via.bin.size; - char *const converted = string_convert( - &sd_reader->sd_conv, unpacked.data.via.array.ptr[1].via.bin.ptr, - &len); - if (converted != NULL) { - strsize = len + 2; - entry->data.history_item.string = xrealloc(converted, strsize); - } else { - goto shada_read_next_item_hist_no_conv; - } - } + strsize = ( + unpacked.data.via.array.ptr[1].via.bin.size + + 1 // Zero byte + + 1); // Separator character + entry->data.history_item.string = xmalloc(strsize); + memcpy(entry->data.history_item.string, + unpacked.data.via.array.ptr[1].via.bin.ptr, + unpacked.data.via.array.ptr[1].via.bin.size); entry->data.history_item.string[strsize - 2] = 0; entry->data.history_item.string[strsize - 1] = entry->data.history_item.sep; @@ -3809,16 +3951,6 @@ shada_read_next_item_hist_no_conv: "be converted to the VimL value")), initial_fpos); goto shada_read_next_item_error; } - if (sd_reader->sd_conv.vc_type != CONV_NONE) { - typval_T tgttv; - var_item_copy(&sd_reader->sd_conv, - &entry->data.global_var.value, - &tgttv, - true, - 0); - clear_tv(&entry->data.global_var.value); - entry->data.global_var.value = tgttv; - } SET_ADDITIONAL_ELEMENTS(unpacked.data.via.array, 2, entry->data.global_var.additional_elements, "variable"); @@ -3863,8 +3995,7 @@ shada_read_next_item_hist_no_conv: .data = unpacked.data.via.array.ptr[i], }; { - msgpack_unpacked unpacked = unpacked_2; - if (unpacked.data.type != MSGPACK_OBJECT_MAP) { + if (unpacked_2.data.type != MSGPACK_OBJECT_MAP) { emsgf(_(RERR "Error while reading ShaDa file: " "buffer list at position %" PRIu64 " " "contains entry that is not a dictionary"), @@ -3873,21 +4004,23 @@ shada_read_next_item_hist_no_conv: } entry->data.buffer_list.buffers[i].pos = default_pos; garray_T ad_ga; - ga_init(&ad_ga, sizeof(*(unpacked.data.via.map.ptr)), 1); + ga_init(&ad_ga, sizeof(*(unpacked_2.data.via.map.ptr)), 1); { + // XXX: Temporarily reassign `i` because the macros depend on it. const size_t j = i; { - for (size_t i = 0; i < unpacked.data.via.map.size; i++) { - CHECK_KEY_IS_STR("buffer list entry") - LONG_KEY("buffer list entry", KEY_LNUM, + for (i = 0; i < unpacked_2.data.via.map.size; i++) { // -V535 + CHECK_KEY_IS_STR(unpacked_2, "buffer list entry") + LONG_KEY(unpacked_2, "buffer list entry", KEY_LNUM, entry->data.buffer_list.buffers[j].pos.lnum) - INTEGER_KEY("buffer list entry", KEY_COL, + INTEGER_KEY(unpacked_2, "buffer list entry", KEY_COL, entry->data.buffer_list.buffers[j].pos.col) - STRING_KEY("buffer list entry", KEY_FILE, + STRING_KEY(unpacked_2, "buffer list entry", KEY_FILE, entry->data.buffer_list.buffers[j].fname) - ADDITIONAL_KEY + ADDITIONAL_KEY(unpacked_2) } } + i = j; // XXX: Restore `i`. } if (entry->data.buffer_list.buffers[i].pos.lnum <= 0) { emsgf(_(RERR "Error while reading ShaDa file: " |