diff options
| author | Florian Walch <fwalch@users.noreply.github.com> | 2015-04-09 00:02:09 +0300 | 
|---|---|---|
| committer | Florian Walch <fwalch@users.noreply.github.com> | 2015-04-09 00:02:09 +0300 | 
| commit | 04e09ba74279d286985333486c43f14a935a13c2 (patch) | |
| tree | 0f6dbc6ca1ddcb79f53d7c42b6adc18ba65f6727 /src | |
| parent | e1bac3b84090d07afa669a8b79816c28813c605e (diff) | |
| parent | dea2731184f23521f5eae03ab46cbe811b8825b7 (diff) | |
| download | rneovim-04e09ba74279d286985333486c43f14a935a13c2.tar.gz rneovim-04e09ba74279d286985333486c43f14a935a13c2.tar.bz2 rneovim-04e09ba74279d286985333486c43f14a935a13c2.zip  | |
Merge pull request #1805 from fwalch/vim-7.4.399
vim-patch: Port parts of Vim 7.4.399.
Diffstat (limited to 'src')
| -rw-r--r-- | src/nvim/buffer_defs.h | 5 | ||||
| -rw-r--r-- | src/nvim/memline.c | 25 | ||||
| -rw-r--r-- | src/nvim/misc2.c | 33 | ||||
| -rw-r--r-- | src/nvim/undo.c | 656 | ||||
| -rw-r--r-- | src/nvim/undo_defs.h | 7 | 
5 files changed, 417 insertions, 309 deletions
diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h index 35fa3978b6..9c8cdd41ae 100644 --- a/src/nvim/buffer_defs.h +++ b/src/nvim/buffer_defs.h @@ -6,6 +6,8 @@  // for FILE  #include <stdio.h> +typedef struct file_buffer buf_T; // Forward declaration +  // for garray_T  #include "nvim/garray.h"  // for pos_T, lpos_T and linenr_T @@ -16,7 +18,7 @@  #include "nvim/iconv.h"  // for jump list and tag stack sizes in a buffer and mark types  #include "nvim/mark_defs.h" -// for u_header_T +// for u_header_T; needs buf_T.  #include "nvim/undo_defs.h"  // for hashtab_T  #include "nvim/hashtab.h" @@ -80,7 +82,6 @@ typedef struct window_S win_T;  typedef struct wininfo_S wininfo_T;  typedef struct frame_S frame_T;  typedef int scid_T;                     /* script ID */ -typedef struct file_buffer buf_T;       /* forward declaration */  // for struct memline (it needs memfile_T)  #include "nvim/memline_defs.h" diff --git a/src/nvim/memline.c b/src/nvim/memline.c index 8b2ebfe554..93fbbd512f 100644 --- a/src/nvim/memline.c +++ b/src/nvim/memline.c @@ -91,8 +91,6 @@ typedef struct pointer_entry PTR_EN;        /* block/line-count pair */  #define PTR_ID         (('p' << 8) + 't')   /* pointer block id */  #define BLOCK0_ID0     'b'                  /* block 0 id 0 */  #define BLOCK0_ID1     '0'                  /* block 0 id 1 */ -#define BLOCK0_ID1_C0  'c'                  /* block 0 id 1 'cm' 0 */ -#define BLOCK0_ID1_C1  'C'                  /* block 0 id 1 'cm' 1 */  /*   * pointer to a block, used in a pointer block @@ -176,8 +174,7 @@ struct data_block {   * variables, because the rest of the swap file is not portable.   */  struct block0 { -  char_u b0_id[2];              /* id for block 0: BLOCK0_ID0 and BLOCK0_ID1, -                                 * BLOCK0_ID1_C0, BLOCK0_ID1_C1 */ +  char_u b0_id[2];              ///< ID for block 0: BLOCK0_ID0 and BLOCK0_ID1.    char_u b0_version[10];        /* Vim version string */    char_u b0_page_size[4];       /* number of bytes per page */    char_u b0_mtime[4];           /* last modification time of file */ @@ -619,22 +616,16 @@ void ml_timestamp(buf_T *buf)    ml_upd_block0(buf, UB_FNAME);  } -/* - * Return FAIL when the ID of "b0p" is wrong. - */ -static int ml_check_b0_id(ZERO_BL *b0p) +/// Checks whether the IDs in b0 are valid. +static bool ml_check_b0_id(ZERO_BL *b0p) +  FUNC_ATTR_NONNULL_ALL  { -  if (b0p->b0_id[0] != BLOCK0_ID0 -      || (b0p->b0_id[1] != BLOCK0_ID1 -          && b0p->b0_id[1] != BLOCK0_ID1_C0 -          && b0p->b0_id[1] != BLOCK0_ID1_C1) -      ) -    return FAIL; -  return OK; +  return b0p->b0_id[0] == BLOCK0_ID0 && b0p->b0_id[1] == BLOCK0_ID1;  } -/// Return true if all strings in b0 are correct (nul-terminated). -static bool ml_check_b0_strings(ZERO_BL *b0p) FUNC_ATTR_NONNULL_ALL +/// Checks whether all strings in b0 are valid (i.e. nul-terminated). +static bool ml_check_b0_strings(ZERO_BL *b0p) +  FUNC_ATTR_NONNULL_ALL  {    return (memchr(b0p->b0_version, NUL, 10)            && memchr(b0p->b0_uname, NUL, B0_UNAME_SIZE) diff --git a/src/nvim/misc2.c b/src/nvim/misc2.c index fafce66c5f..79b42cabf0 100644 --- a/src/nvim/misc2.c +++ b/src/nvim/misc2.c @@ -9,6 +9,7 @@  /*   * misc2.c: Various functions.   */ +#include <assert.h>  #include <errno.h>  #include <inttypes.h>  #include <string.h> @@ -29,6 +30,7 @@  #include "nvim/fileio.h"  #include "nvim/fold.h"  #include "nvim/getchar.h" +#include "nvim/macros.h"  #include "nvim/mark.h"  #include "nvim/mbyte.h"  #include "nvim/memfile.h" @@ -459,22 +461,33 @@ char *read_string(FILE *fd, size_t cnt)    return (char *)str;  } -/// Write a number to file "fd", MSB first, in "len" bytes. -/// @return OK/FAIL. -int put_bytes(FILE *fd, uintmax_t number, unsigned int len) +/// Writes a number to file "fd", most significant bit first, in "len" bytes. +/// @returns false in case of an error. +bool put_bytes(FILE *fd, uintmax_t number, size_t len)  { -  for (unsigned int i = len - 1; i < len; --i) -    if (putc((int)(number >> (i * 8)), fd) == EOF) -      return FAIL; -  return OK; +  assert(len > 0); +  for (size_t i = len - 1; i < len; i--) { +    if (putc((int)(number >> (i * 8)), fd) == EOF) { +      return false; +    } +  } +  return true;  } -/// Write time_t to file "fd" in 8 bytes. +/// Writes time_t to file "fd" in 8 bytes.  void put_time(FILE *fd, time_t time_)  { +  uint8_t buf[8]; +  time_to_bytes(time_, buf); +  fwrite(buf, sizeof(uint8_t), ARRAY_SIZE(buf), fd); +} + +/// Writes time_t to "buf[8]". +void time_to_bytes(time_t time_, uint8_t buf[8]) +{    // time_t can be up to 8 bytes in size, more than uintmax_t in 32 bits    // systems, thus we can't use put_bytes() here. -  for (unsigned int i = 7; i < 8; --i) { -    putc((int)((uint64_t)time_ >> (i * 8)), fd); +  for (size_t i = 7, bufi = 0; bufi < 8; i--, bufi++) { +    buf[bufi] = (uint8_t)((uint64_t)time_ >> (i * 8));    }  } diff --git a/src/nvim/undo.c b/src/nvim/undo.c index 67195235fe..4a721a0884 100644 --- a/src/nvim/undo.c +++ b/src/nvim/undo.c @@ -713,143 +713,159 @@ static void u_free_uhp(u_header_T *uhp)    free(uhp);  } -static int serialize_header(FILE *fp, buf_T *buf, char_u *hash) +/// Writes the header. +/// @returns false in case of an error. +static bool serialize_header(bufinfo_T *bi, char_u *hash) +  FUNC_ATTR_NONNULL_ALL  { -  /* Start writing, first the magic marker and undo info version. */ -  if (fwrite(UF_START_MAGIC, UF_START_MAGIC_LEN, 1, fp) != 1) -    return FAIL; +  buf_T *buf = bi->bi_buf; +  FILE *fp = bi->bi_fp; -  put_bytes(fp, UF_VERSION, 2); - -  /* Write a hash of the buffer text, so that we can verify it is still the -   * same when reading the buffer text. */ -  if (fwrite(hash, UNDO_HASH_SIZE, 1, fp) != 1) -    return FAIL; - -  /* buffer-specific data */ -  put_bytes(fp, (uintmax_t)buf->b_ml.ml_line_count, 4); -  size_t len = buf->b_u_line_ptr ? STRLEN(buf->b_u_line_ptr) : 0; -  put_bytes(fp, len, 4); -  if (len > 0 && fwrite(buf->b_u_line_ptr, len, 1, fp) != 1) -    return FAIL; -  put_bytes(fp, (uintmax_t)buf->b_u_line_lnum, 4); -  put_bytes(fp, (uintmax_t)buf->b_u_line_colnr, 4); - -  /* Undo structures header data */ -  put_header_ptr(fp, buf->b_u_oldhead); -  put_header_ptr(fp, buf->b_u_newhead); -  put_header_ptr(fp, buf->b_u_curhead); - -  put_bytes(fp, (uintmax_t)buf->b_u_numhead, 4); -  put_bytes(fp, (uintmax_t)buf->b_u_seq_last, 4); -  put_bytes(fp, (uintmax_t)buf->b_u_seq_cur, 4); -  put_time(fp, buf->b_u_time_cur); +  // Start writing, first the magic marker and undo info version. +  if (fwrite(UF_START_MAGIC, UF_START_MAGIC_LEN, 1, fp) != 1) { +    return false; +  } -  /* Optional fields. */ -  putc(4, fp); -  putc(UF_LAST_SAVE_NR, fp); -  put_bytes(fp, (uintmax_t)buf->b_u_save_nr_last, 4); +  undo_write_bytes(bi, UF_VERSION, 2); -  putc(0, fp);    /* end marker */ +  // Write a hash of the buffer text, so that we can verify it is +  // still the same when reading the buffer text. +  if (!undo_write(bi, hash, UNDO_HASH_SIZE)) { +    return false; +  } -  return OK; +  // Write buffer-specific data. +  undo_write_bytes(bi, (uintmax_t)buf->b_ml.ml_line_count, 4); +  size_t len = buf->b_u_line_ptr ? STRLEN(buf->b_u_line_ptr) : 0; +  undo_write_bytes(bi, len, 4); +  if (len > 0 && !undo_write(bi, buf->b_u_line_ptr, len)) { +    return false; +  } +  undo_write_bytes(bi, (uintmax_t)buf->b_u_line_lnum, 4); +  undo_write_bytes(bi, (uintmax_t)buf->b_u_line_colnr, 4); + +  // Write undo structures header data. +  put_header_ptr(bi, buf->b_u_oldhead); +  put_header_ptr(bi, buf->b_u_newhead); +  put_header_ptr(bi, buf->b_u_curhead); + +  undo_write_bytes(bi, (uintmax_t)buf->b_u_numhead, 4); +  undo_write_bytes(bi, (uintmax_t)buf->b_u_seq_last, 4); +  undo_write_bytes(bi, (uintmax_t)buf->b_u_seq_cur, 4); +  uint8_t time_buf[8]; +  time_to_bytes(buf->b_u_time_cur, time_buf); +  undo_write(bi, time_buf, sizeof(time_buf)); + +  // Write optional fields. +  undo_write_bytes(bi, 4, 1); +  undo_write_bytes(bi, UF_LAST_SAVE_NR, 1); +  undo_write_bytes(bi, (uintmax_t)buf->b_u_save_nr_last, 4); + +  // Write end marker. +  undo_write_bytes(bi, 0, 1); + +  return true;  } -static int serialize_uhp(FILE *fp, buf_T *buf, u_header_T *uhp) +static bool serialize_uhp(bufinfo_T *bi, u_header_T *uhp)  { -  if (put_bytes(fp, UF_HEADER_MAGIC, 2) == FAIL) -    return FAIL; - -  put_header_ptr(fp, uhp->uh_next.ptr); -  put_header_ptr(fp, uhp->uh_prev.ptr); -  put_header_ptr(fp, uhp->uh_alt_next.ptr); -  put_header_ptr(fp, uhp->uh_alt_prev.ptr); -  put_bytes(fp, (uintmax_t)uhp->uh_seq, 4); -  serialize_pos(uhp->uh_cursor, fp); -  put_bytes(fp, (uintmax_t)uhp->uh_cursor_vcol, 4); -  put_bytes(fp, (uintmax_t)uhp->uh_flags, 2); -  /* Assume NMARKS will stay the same. */ -  for (size_t i = 0; i < NMARKS; ++i) -    serialize_pos(uhp->uh_namedm[i], fp); -  serialize_visualinfo(&uhp->uh_visual, fp); -  put_time(fp, uhp->uh_time); - -  /* Optional fields. */ -  putc(4, fp); -  putc(UHP_SAVE_NR, fp); -  put_bytes(fp, (uintmax_t)uhp->uh_save_nr, 4); - -  putc(0, fp);    /* end marker */ - -  /* Write all the entries. */ +  if (!undo_write_bytes(bi, (uintmax_t)UF_HEADER_MAGIC, 2)) { +    return false; +  } + +  put_header_ptr(bi, uhp->uh_next.ptr); +  put_header_ptr(bi, uhp->uh_prev.ptr); +  put_header_ptr(bi, uhp->uh_alt_next.ptr); +  put_header_ptr(bi, uhp->uh_alt_prev.ptr); +  undo_write_bytes(bi, (uintmax_t)uhp->uh_seq, 4); +  serialize_pos(bi, uhp->uh_cursor); +  undo_write_bytes(bi, (uintmax_t)uhp->uh_cursor_vcol, 4); +  undo_write_bytes(bi, (uintmax_t)uhp->uh_flags, 2); +  // Assume NMARKS will stay the same. +  for (size_t i = 0; i < (size_t)NMARKS; i++) { +    serialize_pos(bi, uhp->uh_namedm[i]); +  } +  serialize_visualinfo(bi, &uhp->uh_visual); +  uint8_t time_buf[8]; +  time_to_bytes(uhp->uh_time, time_buf); +  undo_write(bi, time_buf, sizeof(time_buf)); + +  // Write optional fields. +  undo_write_bytes(bi, 4, 1); +  undo_write_bytes(bi, UHP_SAVE_NR, 1); +  undo_write_bytes(bi, (uintmax_t)uhp->uh_save_nr, 4); + +  // Write end marker. +  undo_write_bytes(bi, 0, 1); + +  // Write all the entries.    for (u_entry_T *uep = uhp->uh_entry; uep; uep = uep->ue_next) { -    put_bytes(fp, UF_ENTRY_MAGIC, 2); -    if (serialize_uep(fp, buf, uep) == FAIL) -      return FAIL; +    undo_write_bytes(bi, (uintmax_t)UF_ENTRY_MAGIC, 2); +    if (!serialize_uep(bi, uep)) { +      return false; +    }    } -  put_bytes(fp, UF_ENTRY_END_MAGIC, 2); -  return OK; +  undo_write_bytes(bi, (uintmax_t)UF_ENTRY_END_MAGIC, 2); +  return true;  } -static u_header_T *unserialize_uhp(FILE *fp, char_u *file_name) +static u_header_T *unserialize_uhp(bufinfo_T *bi, char_u *file_name)  { -  u_header_T  *uhp; -  int i; -  u_entry_T   *uep, *last_uep; -  int c; -  int error; - -  uhp = xmalloc(sizeof(u_header_T)); +  u_header_T *uhp = xmalloc(sizeof(u_header_T));    memset(uhp, 0, sizeof(u_header_T));  #ifdef U_DEBUG    uhp->uh_magic = UH_MAGIC;  #endif -  uhp->uh_next.seq = get4c(fp); -  uhp->uh_prev.seq = get4c(fp); -  uhp->uh_alt_next.seq = get4c(fp); -  uhp->uh_alt_prev.seq = get4c(fp); -  uhp->uh_seq = get4c(fp); +  uhp->uh_next.seq = undo_read_4c(bi); +  uhp->uh_prev.seq = undo_read_4c(bi); +  uhp->uh_alt_next.seq = undo_read_4c(bi); +  uhp->uh_alt_prev.seq = undo_read_4c(bi); +  uhp->uh_seq = undo_read_4c(bi);    if (uhp->uh_seq <= 0) {      corruption_error("uh_seq", file_name);      free(uhp);      return NULL;    } -  unserialize_pos(&uhp->uh_cursor, fp); -  uhp->uh_cursor_vcol = get4c(fp); -  uhp->uh_flags = get2c(fp); -  for (i = 0; i < NMARKS; ++i) -    unserialize_pos(&uhp->uh_namedm[i], fp); -  unserialize_visualinfo(&uhp->uh_visual, fp); -  uhp->uh_time = get8ctime(fp); +  unserialize_pos(bi, &uhp->uh_cursor); +  uhp->uh_cursor_vcol = undo_read_4c(bi); +  uhp->uh_flags = undo_read_2c(bi); +  for (size_t i = 0; i < (size_t)NMARKS; i++) { +    unserialize_pos(bi, &uhp->uh_namedm[i]); +  } +  unserialize_visualinfo(bi, &uhp->uh_visual); +  uhp->uh_time = undo_read_time(bi); -  /* Optional fields. */ +  // Unserialize optional fields.    for (;; ) { -    int len = getc(fp); -    int what; +    int len = undo_read_byte(bi); -    if (len == 0) +    if (len == 0) {        break; -    what = getc(fp); +    } +    int what = undo_read_byte(bi);      switch (what) {      case UHP_SAVE_NR: -      uhp->uh_save_nr = get4c(fp); +      uhp->uh_save_nr = undo_read_4c(bi);        break;      default: -      /* field not supported, skip */ -      while (--len >= 0) -        (void)getc(fp); +      // Field not supported, skip it. +      while (--len >= 0) { +        (void)undo_read_byte(bi); +      }      }    } -  /* Unserialize the uep list. */ -  last_uep = NULL; -  while ((c = get2c(fp)) == UF_ENTRY_MAGIC) { -    error = FALSE; -    uep = unserialize_uep(fp, &error, file_name); -    if (last_uep == NULL) +  // Unserialize the uep list. +  u_entry_T *last_uep = NULL; +  int c; +  while ((c = undo_read_2c(bi)) == UF_ENTRY_MAGIC) { +    bool error = false; +    u_entry_T *uep = unserialize_uep(bi, &error, file_name); +    if (last_uep == NULL) {        uhp->uh_entry = uep; -    else +    } else {        last_uep->ue_next = uep; +    }      last_uep = uep;      if (uep == NULL || error) {        u_free_uhp(uhp); @@ -865,59 +881,60 @@ static u_header_T *unserialize_uhp(FILE *fp, char_u *file_name)    return uhp;  } -/* - * Serialize "uep" to "fp". - */ -static int serialize_uep(FILE *fp, buf_T *buf, u_entry_T *uep) +/// Serializes "uep". +/// +/// @returns false in case of an error. +static bool serialize_uep(bufinfo_T *bi, u_entry_T *uep)  { -  put_bytes(fp, (uintmax_t)uep->ue_top, 4); -  put_bytes(fp, (uintmax_t)uep->ue_bot, 4); -  put_bytes(fp, (uintmax_t)uep->ue_lcount, 4); -  put_bytes(fp, (uintmax_t)uep->ue_size, 4); -  for (size_t i = 0; i < (size_t)uep->ue_size; ++i) { +  undo_write_bytes(bi, (uintmax_t)uep->ue_top, 4); +  undo_write_bytes(bi, (uintmax_t)uep->ue_bot, 4); +  undo_write_bytes(bi, (uintmax_t)uep->ue_lcount, 4); +  undo_write_bytes(bi, (uintmax_t)uep->ue_size, 4); + +  for (size_t i = 0; i < (size_t)uep->ue_size; i++) {      size_t len = STRLEN(uep->ue_array[i]); -    if (put_bytes(fp, len, 4) == FAIL) -      return FAIL; -    if (len > 0 && fwrite(uep->ue_array[i], len, 1, fp) != 1) -      return FAIL; +    if (!undo_write_bytes(bi, len, 4)) { +      return false; +    } +    if (len > 0 && !undo_write(bi, uep->ue_array[i], len)) { +      return false; +    }    } -  return OK; +  return true;  } -static u_entry_T *unserialize_uep(FILE *fp, int *error, char_u *file_name) +static u_entry_T *unserialize_uep(bufinfo_T * bi, bool *error, char_u *file_name)  { -  int i; -  u_entry_T   *uep; -  char_u      **array; -  char_u      *line; -  int line_len; - -  uep = xmalloc(sizeof(u_entry_T)); +  u_entry_T *uep = xmalloc(sizeof(u_entry_T));    memset(uep, 0, sizeof(u_entry_T));  #ifdef U_DEBUG    uep->ue_magic = UE_MAGIC;  #endif -  uep->ue_top = get4c(fp); -  uep->ue_bot = get4c(fp); -  uep->ue_lcount = get4c(fp); -  uep->ue_size = get4c(fp); +  uep->ue_top = undo_read_4c(bi); +  uep->ue_bot = undo_read_4c(bi); +  uep->ue_lcount = undo_read_4c(bi); +  uep->ue_size = undo_read_4c(bi); + +  char_u **array;    if (uep->ue_size > 0) {      array = xmalloc(sizeof(char_u *) * (size_t)uep->ue_size);      memset(array, 0, sizeof(char_u *) * (size_t)uep->ue_size); -  } else +  } else {      array = NULL; +  }    uep->ue_array = array; -  for (i = 0; i < uep->ue_size; ++i) { -    line_len = get4c(fp); -    if (line_len >= 0) -      line = READ_STRING(fp, line_len); -    else { +  for (size_t i = 0; i < (size_t)uep->ue_size; i++) { +    int line_len = undo_read_4c(bi); +    char_u *line; +    if (line_len >= 0) { +      line = undo_read_string(bi, (size_t)line_len); +    } else {        line = NULL;        corruption_error("line length", file_name);      }      if (line == NULL) { -      *error = TRUE; +      *error = true;        return uep;      }      array[i] = line; @@ -925,61 +942,47 @@ static u_entry_T *unserialize_uep(FILE *fp, int *error, char_u *file_name)    return uep;  } -/* - * Serialize "pos" to "fp". - */ -static void serialize_pos(pos_T pos, FILE *fp) +/// Serializes "pos". +static void serialize_pos(bufinfo_T *bi, pos_T pos)  { -  put_bytes(fp, (uintmax_t)pos.lnum, 4); -  put_bytes(fp, (uintmax_t)pos.col, 4); -  put_bytes(fp, (uintmax_t)pos.coladd, 4); +  undo_write_bytes(bi, (uintmax_t)pos.lnum, 4); +  undo_write_bytes(bi, (uintmax_t)pos.col, 4); +  undo_write_bytes(bi, (uintmax_t)pos.coladd, 4);  } -/* - * Unserialize the pos_T at the current position in fp. - */ -static void unserialize_pos(pos_T *pos, FILE *fp) +/// Unserializes the pos_T at the current position. +static void unserialize_pos(bufinfo_T *bi, pos_T *pos)  { -  pos->lnum = get4c(fp); -  if (pos->lnum < 0) +  pos->lnum = undo_read_4c(bi); +  if (pos->lnum < 0) {      pos->lnum = 0; -  pos->col = get4c(fp); -  if (pos->col < 0) +  } +  pos->col = undo_read_4c(bi); +  if (pos->col < 0) {      pos->col = 0; -  pos->coladd = get4c(fp); -  if (pos->coladd < 0) +  } +  pos->coladd = undo_read_4c(bi); +  if (pos->coladd < 0) {      pos->coladd = 0; +  }  } -/* - * Serialize "info" to "fp". - */ -static void serialize_visualinfo(visualinfo_T *info, FILE *fp) +/// Serializes "info". +static void serialize_visualinfo(bufinfo_T *bi, visualinfo_T *info)  { -  serialize_pos(info->vi_start, fp); -  serialize_pos(info->vi_end, fp); -  put_bytes(fp, (uintmax_t)info->vi_mode, 4); -  put_bytes(fp, (uintmax_t)info->vi_curswant, 4); +  serialize_pos(bi, info->vi_start); +  serialize_pos(bi, info->vi_end); +  undo_write_bytes(bi, (uintmax_t)info->vi_mode, 4); +  undo_write_bytes(bi, (uintmax_t)info->vi_curswant, 4);  } -/* - * Unserialize the visualinfo_T at the current position in fp. - */ -static void unserialize_visualinfo(visualinfo_T *info, FILE *fp) +/// Unserializes the visualinfo_T at the current position. +static void unserialize_visualinfo(bufinfo_T *bi, visualinfo_T *info)  { -  unserialize_pos(&info->vi_start, fp); -  unserialize_pos(&info->vi_end, fp); -  info->vi_mode = get4c(fp); -  info->vi_curswant = get4c(fp); -} - -/* - * Write the pointer to an undo header.  Instead of writing the pointer itself - * we use the sequence number of the header.  This is converted back to - * pointers when reading. */ -static void put_header_ptr(FILE *fp, u_header_T *uhp) -{ -  put_bytes(fp, (uintmax_t)(uhp != NULL ? uhp->uh_seq : 0), 4); +  unserialize_pos(bi, &info->vi_start); +  unserialize_pos(bi, &info->vi_end); +  info->vi_mode = undo_read_4c(bi); +  info->vi_curswant = undo_read_4c(bi);  }  /* @@ -1003,6 +1006,7 @@ void u_write_undo(char_u *name, int forceit, buf_T *buf, char_u *hash)    FILE        *fp = NULL;    int perm;    bool write_ok = false; +  bufinfo_T bi;    if (name == NULL) {      file_name = u_get_undo_file_name(buf->b_ffname, FALSE); @@ -1134,8 +1138,11 @@ void u_write_undo(char_u *name, int forceit, buf_T *buf, char_u *hash)    /*     * Write the header.     */ -  if (serialize_header(fp, buf, hash) == FAIL) +  bi.bi_buf = buf; +  bi.bi_fp = fp; +  if (!serialize_header(&bi, hash)) {      goto write_error; +  }    /*     * Iteratively serialize UHPs and their UEPs from the top down. @@ -1149,8 +1156,9 @@ void u_write_undo(char_u *name, int forceit, buf_T *buf, char_u *hash)  #ifdef U_DEBUG        ++headers_written;  #endif -      if (serialize_uhp(fp, buf, uhp) == FAIL) +      if (!serialize_uhp(&bi, uhp)) {          goto write_error; +      }      }      /* Now walk through the tree - algorithm from undo_time(). */ @@ -1168,8 +1176,9 @@ void u_write_undo(char_u *name, int forceit, buf_T *buf, char_u *hash)        uhp = uhp->uh_next.ptr;    } -  if (put_bytes(fp, UF_HEADER_END_MAGIC, 2) == OK) +  if (undo_write_bytes(&bi, (uintmax_t)UF_HEADER_END_MAGIC, 2)) {      write_ok = true; +  }  #ifdef U_DEBUG    if (headers_written != buf->b_u_numhead) {      EMSGN("Written %" PRId64 " headers, ...", headers_written); @@ -1198,47 +1207,27 @@ theend:      free(file_name);  } -/* - * Load the undo tree from an undo file. - * If "name" is not NULL use it as the undo file name.  This also means being - * a bit more verbose. - * Otherwise use curbuf->b_ffname to generate the undo file name. - * "hash[UNDO_HASH_SIZE]" must be the hash value of the buffer text. - */ +/// Loads the undo tree from an undo file. +/// If "name" is not NULL use it as the undo file name. This also means being +/// a bit more verbose. +/// Otherwise use curbuf->b_ffname to generate the undo file name. +/// "hash[UNDO_HASH_SIZE]" must be the hash value of the buffer text.  void u_read_undo(char_u *name, char_u *hash, char_u *orig_name) +  FUNC_ATTR_NONNULL_ARG(2)  { -  char_u      *file_name; -  FILE        *fp; -  long version, str_len; -  char_u      *line_ptr = NULL; -  linenr_T line_lnum; -  colnr_T line_colnr; -  linenr_T line_count; -  int num_head = 0; -  long old_header_seq, new_header_seq, cur_header_seq; -  long seq_last, seq_cur; -  long last_save_nr = 0; -  short old_idx = -1, new_idx = -1, cur_idx = -1; -  long num_read_uhps = 0; -  time_t seq_time; -  int i, j; -  int c; -  u_header_T  *uhp; -  u_header_T  **uhp_table = NULL; -  char_u read_hash[UNDO_HASH_SIZE]; -  char_u magic_buf[UF_START_MAGIC_LEN]; -#ifdef U_DEBUG -  int         *uhp_table_used; -#endif +  u_header_T **uhp_table = NULL; +  char_u *line_ptr = NULL; +  char_u *file_name;    if (name == NULL) {      file_name = u_get_undo_file_name(curbuf->b_ffname, TRUE); -    if (file_name == NULL) +    if (file_name == NULL) {        return; +    }  #ifdef UNIX -    /* For safety we only read an undo file if the owner is equal to the -     * owner of the text file or equal to the current user. */ +    // For safety we only read an undo file if the owner is equal to the +    // owner of the text file or equal to the current user.      FileInfo file_info_orig;      FileInfo file_info_undo;      if (os_fileinfo((char *)orig_name, &file_info_orig) @@ -1254,8 +1243,9 @@ void u_read_undo(char_u *name, char_u *hash, char_u *orig_name)        return;      }  #endif -  } else +  } else {      file_name = name; +  }    if (p_verbose > 0) {      verbose_enter(); @@ -1263,103 +1253,120 @@ void u_read_undo(char_u *name, char_u *hash, char_u *orig_name)      verbose_leave();    } -  fp = mch_fopen((char *)file_name, "r"); +  FILE *fp = mch_fopen((char *)file_name, "r");    if (fp == NULL) { -    if (name != NULL || p_verbose > 0) +    if (name != NULL || p_verbose > 0) {        EMSG2(_("E822: Cannot open undo file for reading: %s"), file_name); +    }      goto error;    } -  /* -   * Read the undo file header. -   */ +  bufinfo_T bi; +  bi.bi_buf = curbuf; +  bi.bi_fp = fp; + +  // Read the undo file header. +  char_u magic_buf[UF_START_MAGIC_LEN];    if (fread(magic_buf, UF_START_MAGIC_LEN, 1, fp) != 1        || memcmp(magic_buf, UF_START_MAGIC, UF_START_MAGIC_LEN) != 0) {      EMSG2(_("E823: Not an undo file: %s"), file_name);      goto error;    } -  version = get2c(fp); +  int version = get2c(fp);    if (version != UF_VERSION) {      EMSG2(_("E824: Incompatible undo file: %s"), file_name);      goto error;    } -  if (fread(read_hash, UNDO_HASH_SIZE, 1, fp) != 1) { +  char_u read_hash[UNDO_HASH_SIZE]; +  if (!undo_read(&bi, read_hash, UNDO_HASH_SIZE)) {      corruption_error("hash", file_name);      goto error;    } -  line_count = (linenr_T)get4c(fp); +  linenr_T line_count = (linenr_T)undo_read_4c(&bi);    if (memcmp(hash, read_hash, UNDO_HASH_SIZE) != 0        || line_count != curbuf->b_ml.ml_line_count) {      if (p_verbose > 0 || name != NULL) { -      if (name == NULL) +      if (name == NULL) {          verbose_enter(); +      }        give_warning((char_u *)            _("File contents changed, cannot use undo info"), true); -      if (name == NULL) +      if (name == NULL) {          verbose_leave(); +      }      }      goto error;    } -  /* Read undo data for "U" command. */ -  str_len = get4c(fp); -  if (str_len < 0) +  // Read undo data for "U" command. +  int str_len = undo_read_4c(&bi); +  if (str_len < 0) {      goto error; -  if (str_len > 0) -    line_ptr = READ_STRING(fp, str_len); -  line_lnum = (linenr_T)get4c(fp); -  line_colnr = (colnr_T)get4c(fp); +  } + +  if (str_len > 0) { +    line_ptr = undo_read_string(&bi, (size_t)str_len); +  } +  linenr_T line_lnum = (linenr_T)undo_read_4c(&bi); +  colnr_T line_colnr = (colnr_T)undo_read_4c(&bi);    if (line_lnum < 0 || line_colnr < 0) {      corruption_error("line lnum/col", file_name);      goto error;    } -  /* Begin general undo data */ -  old_header_seq = get4c(fp); -  new_header_seq = get4c(fp); -  cur_header_seq = get4c(fp); -  num_head = get4c(fp); -  seq_last = get4c(fp); -  seq_cur = get4c(fp); -  seq_time = get8ctime(fp); +  // Begin general undo data +  int old_header_seq = undo_read_4c(&bi); +  int new_header_seq = undo_read_4c(&bi); +  int cur_header_seq = undo_read_4c(&bi); +  int num_head = undo_read_4c(&bi); +  int seq_last = undo_read_4c(&bi); +  int seq_cur = undo_read_4c(&bi); +  time_t seq_time = undo_read_time(&bi); -  /* Optional header fields. */ +  // Optional header fields. +  long last_save_nr = 0;    for (;; ) { -    int len = getc(fp); -    int what; +    int len = undo_read_byte(&bi); -    if (len == 0 || len == EOF) +    if (len == 0 || len == EOF) {        break; -    what = getc(fp); +    } +    int what = undo_read_byte(&bi);      switch (what) { -    case UF_LAST_SAVE_NR: -      last_save_nr = get4c(fp); -      break; -    default: -      /* field not supported, skip */ -      while (--len >= 0) -        (void)getc(fp); +      case UF_LAST_SAVE_NR: +        last_save_nr = undo_read_4c(&bi); +        break; + +      default: +        // field not supported, skip +        while (--len >= 0) { +          (void)undo_read_byte(&bi); +        }      }    } -  /* uhp_table will store the freshly created undo headers we allocate -   * until we insert them into curbuf. The table remains sorted by the -   * sequence numbers of the headers. -   * When there are no headers uhp_table is NULL. */ +  // uhp_table will store the freshly created undo headers we allocate +  // until we insert them into curbuf. The table remains sorted by the +  // sequence numbers of the headers. +  // When there are no headers uhp_table is NULL.    if (num_head > 0) {      uhp_table = xmalloc((size_t)num_head * sizeof(u_header_T *));    } -  while ((c = get2c(fp)) == UF_HEADER_MAGIC) { +  long num_read_uhps = 0; + +  int c; +  while ((c = undo_read_2c(&bi)) == UF_HEADER_MAGIC) {      if (num_read_uhps >= num_head) {        corruption_error("num_head too small", file_name);        goto error;      } -    uhp = unserialize_uhp(fp, file_name); -    if (uhp == NULL) +    u_header_T *uhp = unserialize_uhp(&bi, file_name); +    if (uhp == NULL) {        goto error; +    }      uhp_table[num_read_uhps++] = uhp;    } @@ -1374,54 +1381,61 @@ void u_read_undo(char_u *name, char_u *hash, char_u *orig_name)  #ifdef U_DEBUG    size_t amount = num_head * sizeof(int) + 1; -  uhp_table_used = xmalloc(amount); +  int *uhp_table_used = xmalloc(amount);    memset(uhp_table_used, 0, amount);  # define SET_FLAG(j) ++ uhp_table_used[j]  #else  # define SET_FLAG(j)  #endif -  /* We have put all of the headers into a table. Now we iterate through the -   * table and swizzle each sequence number we have stored in uh_*_seq into -   * a pointer corresponding to the header with that sequence number. */ -  for (i = 0; i < num_head; i++) { -    uhp = uhp_table[i]; -    if (uhp == NULL) +  // We have put all of the headers into a table. Now we iterate through the +  // table and swizzle each sequence number we have stored in uh_*_seq into +  // a pointer corresponding to the header with that sequence number. +  short old_idx = -1, new_idx = -1, cur_idx = -1; +  for (int i = 0; i < num_head; i++) { +    u_header_T *uhp = uhp_table[i]; +    if (uhp == NULL) {        continue; -    for (j = 0; j < num_head; j++) +    } +    for (int j = 0; j < num_head; j++) {        if (uhp_table[j] != NULL && i != j            && uhp_table[i]->uh_seq == uhp_table[j]->uh_seq) {          corruption_error("duplicate uh_seq", file_name);          goto error;        } -    for (j = 0; j < num_head; j++) +    } +    for (int j = 0; j < num_head; j++) {        if (uhp_table[j] != NULL            && uhp_table[j]->uh_seq == uhp->uh_next.seq) {          uhp->uh_next.ptr = uhp_table[j];          SET_FLAG(j);          break;        } -    for (j = 0; j < num_head; j++) +    } +    for (int j = 0; j < num_head; j++) {        if (uhp_table[j] != NULL            && uhp_table[j]->uh_seq == uhp->uh_prev.seq) {          uhp->uh_prev.ptr = uhp_table[j];          SET_FLAG(j);          break;        } -    for (j = 0; j < num_head; j++) +    } +    for (int j = 0; j < num_head; j++) {        if (uhp_table[j] != NULL            && uhp_table[j]->uh_seq == uhp->uh_alt_next.seq) {          uhp->uh_alt_next.ptr = uhp_table[j];          SET_FLAG(j);          break;        } -    for (j = 0; j < num_head; j++) +    } +    for (int j = 0; j < num_head; j++) {        if (uhp_table[j] != NULL            && uhp_table[j]->uh_seq == uhp->uh_alt_prev.seq) {          uhp->uh_alt_prev.ptr = uhp_table[j];          SET_FLAG(j);          break;        } +    }      if (old_header_seq > 0 && old_idx < 0 && uhp->uh_seq == old_header_seq) {        assert(i <= SHRT_MAX);        old_idx = (short)i; @@ -1439,8 +1453,8 @@ void u_read_undo(char_u *name, char_u *hash, char_u *orig_name)      }    } -  /* Now that we have read the undo info successfully, free the current undo -   * info and use the info from the file. */ +  // Now that we have read the undo info successfully, free the current undo +  // info and use the info from the file.    u_blockfree(curbuf);    curbuf->b_u_oldhead = old_idx < 0 ? NULL : uhp_table[old_idx];    curbuf->b_u_newhead = new_idx < 0 ? NULL : uhp_table[new_idx]; @@ -1459,35 +1473,117 @@ void u_read_undo(char_u *name, char_u *hash, char_u *orig_name)    free(uhp_table);  #ifdef U_DEBUG -  for (i = 0; i < num_head; ++i) -    if (uhp_table_used[i] == 0) +  for (int i = 0; i < num_head; i++) { +    if (uhp_table_used[i] == 0) {        EMSGN("uhp_table entry %" PRId64 " not used, leaking memory", i); +    } +  }    free(uhp_table_used);    u_check(TRUE);  #endif -  if (name != NULL) +  if (name != NULL) {      smsg((char_u *)_("Finished reading undo file %s"), file_name); +  }    goto theend;  error:    free(line_ptr);    if (uhp_table != NULL) { -    for (i = 0; i < num_read_uhps; i++) -      if (uhp_table[i] != NULL) +    for (long i = 0; i < num_read_uhps; i++) +      if (uhp_table[i] != NULL) {          u_free_uhp(uhp_table[i]); +      }      free(uhp_table);    }  theend: -  if (fp != NULL) +  if (fp != NULL) {      fclose(fp); -  if (file_name != name) +  } +  if (file_name != name) {      free(file_name); -  return; +  } +} + +/// Writes a sequence of bytes to the undo file. +/// +/// @returns false in case of an error. +static bool undo_write(bufinfo_T *bi, uint8_t *ptr, size_t len) +  FUNC_ATTR_NONNULL_ARG(1) +{ +  return fwrite(ptr, len, 1, bi->bi_fp) == 1; +} + +/// Writes a number, most significant bit first, in "len" bytes. +/// +/// Must match with undo_read_?c() functions. +/// +/// @returns false in case of an error. +static bool undo_write_bytes(bufinfo_T *bi, uintmax_t nr, size_t len) +{ +  assert(len > 0); +  uint8_t buf[8]; +  for (size_t i = len - 1, bufi = 0; bufi < len; i--, bufi++) { +    buf[bufi] = (uint8_t)(nr >> (i * 8)); +  } +  return undo_write(bi, buf, len); +} + +/// Writes the pointer to an undo header. +/// +/// Instead of writing the pointer itself, we use the sequence +/// number of the header. This is converted back to pointers +/// when reading. +static void put_header_ptr(bufinfo_T *bi, u_header_T *uhp) +{ +  assert(uhp == NULL || uhp->uh_seq >= 0); +  undo_write_bytes(bi, (uint64_t)(uhp != NULL ? uhp->uh_seq : 0), 4); +} + +static int undo_read_4c(bufinfo_T *bi) +{ +  return get4c(bi->bi_fp);  } +static int undo_read_2c(bufinfo_T *bi) +{ +  return get2c(bi->bi_fp); +} +static int undo_read_byte(bufinfo_T *bi) +{ +  return getc(bi->bi_fp); +} + +static time_t undo_read_time(bufinfo_T *bi) +{ +  return get8ctime(bi->bi_fp); +} + +/// Reads "buffer[size]" from the undo file. +/// +/// @returns false in case of an error. +static bool undo_read(bufinfo_T *bi, uint8_t *buffer, size_t size) +  FUNC_ATTR_NONNULL_ARG(1) +{ +  return fread(buffer, size, 1, bi->bi_fp) == 1; +} + +/// Reads a string of length "len" from "bi->bi_fd" and appends a zero to it. +/// +/// @param len can be zero to allocate an empty line. +/// +/// @returns a pointer to allocated memory or NULL in case of an error. +static uint8_t *undo_read_string(bufinfo_T *bi, size_t len) +{ +  uint8_t *ptr = xmallocz(len); +  if (len > 0 && !undo_read(bi, ptr, len)) { +    free(ptr); +    return NULL; +  } +  return ptr; +}  /*   * If 'cpoptions' contains 'u': Undo the previous undo or redo (vi compatible). diff --git a/src/nvim/undo_defs.h b/src/nvim/undo_defs.h index 2579f13b93..610adb4367 100644 --- a/src/nvim/undo_defs.h +++ b/src/nvim/undo_defs.h @@ -4,6 +4,7 @@  #include <time.h>  // for time_t  #include "nvim/pos.h" +#include "nvim/buffer_defs.h"  /* Structure to store info about the Visual area. */  typedef struct { @@ -67,4 +68,10 @@ struct u_header {  #define UH_CHANGED  0x01        /* b_changed flag before undo/after redo */  #define UH_EMPTYBUF 0x02        /* buffer was empty */ +/// Structure passed around between undofile functions. +typedef struct { +  buf_T *bi_buf; +  FILE *bi_fp; +} bufinfo_T; +  #endif // NVIM_UNDO_DEFS_H  | 
