comparison src/undo.c @ 2214:f8222d1f9a73 vim73

Included patch for persistent undo. Lots of changes and added test.
author Bram Moolenaar <bram@vim.org>
date Sun, 23 May 2010 23:34:36 +0200
parents 8c6a66e2b3cc
children cccb71c2c5c1
comparison
equal deleted inserted replaced
2213:0e0e99d1092e 2214:f8222d1f9a73
97 static void u_add_time __ARGS((char_u *buf, size_t buflen, time_t tt)); 97 static void u_add_time __ARGS((char_u *buf, size_t buflen, time_t tt));
98 static void u_freeheader __ARGS((buf_T *buf, u_header_T *uhp, u_header_T **uhpp)); 98 static void u_freeheader __ARGS((buf_T *buf, u_header_T *uhp, u_header_T **uhpp));
99 static void u_freebranch __ARGS((buf_T *buf, u_header_T *uhp, u_header_T **uhpp)); 99 static void u_freebranch __ARGS((buf_T *buf, u_header_T *uhp, u_header_T **uhpp));
100 static void u_freeentries __ARGS((buf_T *buf, u_header_T *uhp, u_header_T **uhpp)); 100 static void u_freeentries __ARGS((buf_T *buf, u_header_T *uhp, u_header_T **uhpp));
101 static void u_freeentry __ARGS((u_entry_T *, long)); 101 static void u_freeentry __ARGS((u_entry_T *, long));
102 #ifdef FEAT_PERSISTENT_UNDO
103 static void unserialize_pos __ARGS((pos_T *pos, FILE *fp));
104 static void unserialize_visualinfo __ARGS((visualinfo_T *info, FILE *fp));
105 static char_u *u_get_undo_file_name __ARGS((char_u *, int reading));
106 static int serialize_uep __ARGS((u_entry_T *uep, FILE *fp));
107 static void serialize_pos __ARGS((pos_T pos, FILE *fp));
108 static void serialize_visualinfo __ARGS((visualinfo_T info, FILE *fp));
109 #endif
102 110
103 #ifdef U_USE_MALLOC 111 #ifdef U_USE_MALLOC
104 # define U_FREE_LINE(ptr) vim_free(ptr) 112 # define U_FREE_LINE(ptr) vim_free(ptr)
105 # define U_ALLOC_LINE(size) lalloc((long_u)((size) + 1), FALSE) 113 # define U_ALLOC_LINE(size) lalloc((long_u)((size) + 1), FALSE)
106 #else 114 #else
116 /* 124 /*
117 * When 'u' flag included in 'cpoptions', we behave like vi. Need to remember 125 * When 'u' flag included in 'cpoptions', we behave like vi. Need to remember
118 * the action that "u" should do. 126 * the action that "u" should do.
119 */ 127 */
120 static int undo_undoes = FALSE; 128 static int undo_undoes = FALSE;
129
130 static int lastmark = 0;
121 131
122 #ifdef U_DEBUG 132 #ifdef U_DEBUG
123 /* 133 /*
124 * Check the undo structures for being valid. Print a warning when something 134 * Check the undo structures for being valid. Print a warning when something
125 * looks wrong. 135 * looks wrong.
649 return OK; 659 return OK;
650 } 660 }
651 do_outofmem_msg((long_u)0); 661 do_outofmem_msg((long_u)0);
652 return FAIL; 662 return FAIL;
653 } 663 }
664
665 #ifdef FEAT_PERSISTENT_UNDO
666
667 # define UF_START_MAGIC 0xfeac /* magic at start of undofile */
668 # define UF_HEADER_MAGIC 0x5fd0 /* magic at start of header */
669 # define UF_END_MAGIC 0xe7aa /* magic after last header */
670 # define UF_VERSION 1 /* 2-byte undofile version number */
671
672 /*
673 * Compute the hash for the current buffer text into hash[UNDO_HASH_SIZE].
674 */
675 void
676 u_compute_hash(hash)
677 char_u *hash;
678 {
679 context_sha256_T ctx;
680 linenr_T lnum;
681 char_u *p;
682
683 sha256_start(&ctx);
684 for (lnum = 1; lnum < curbuf->b_ml.ml_line_count; ++lnum)
685 {
686 p = ml_get(lnum);
687 sha256_update(&ctx, p, STRLEN(p) + 1);
688 }
689 sha256_finish(&ctx, hash);
690 }
691
692 /*
693 * Unserialize the pos_T at the current position in fp.
694 */
695 static void
696 unserialize_pos(pos, fp)
697 pos_T *pos;
698 FILE *fp;
699 {
700 pos->lnum = get4c(fp);
701 pos->col = get4c(fp);
702 #ifdef FEAT_VIRTUALEDIT
703 pos->coladd = get4c(fp);
704 #else
705 (void)get4c(fp);
706 #endif
707 }
708
709 /*
710 * Unserialize the visualinfo_T at the current position in fp.
711 */
712 static void
713 unserialize_visualinfo(info, fp)
714 visualinfo_T *info;
715 FILE *fp;
716 {
717 unserialize_pos(&info->vi_start, fp);
718 unserialize_pos(&info->vi_end, fp);
719 info->vi_mode = get4c(fp);
720 info->vi_curswant = get4c(fp);
721 }
722
723 /*
724 * Return an allocated string of the full path of the target undofile.
725 * When "reading" is TRUE find the file to read, go over all directories in
726 * 'undodir'.
727 * When "reading" is FALSE use the first name where the directory exists.
728 */
729 static char_u *
730 u_get_undo_file_name(buf_ffname, reading)
731 char_u *buf_ffname;
732 int reading;
733 {
734 char_u *dirp;
735 char_u dir_name[IOSIZE + 1];
736 char_u *munged_name = NULL;
737 char_u *undo_file_name = NULL;
738 int dir_len;
739 char_u *p;
740 struct stat st;
741 char_u *ffname = buf_ffname;
742 #ifdef HAVE_READLINK
743 char_u fname_buf[MAXPATHL];
744 #endif
745
746 if (ffname == NULL)
747 return NULL;
748
749 #ifdef HAVE_READLINK
750 /* Expand symlink in the file name, so that we put the undo file with the
751 * actual file instead of with the symlink. */
752 if (resolve_symlink(ffname, fname_buf) == OK)
753 ffname = fname_buf;
754 #endif
755
756 /* Loop over 'undodir'. When reading find the first file that exists.
757 * When not reading use the first directory that exists or ".". */
758 dirp = p_udir;
759 while (*dirp != NUL)
760 {
761 dir_len = copy_option_part(&dirp, dir_name, IOSIZE, ",");
762 if (dir_len == 1 && dir_name[0] == '.')
763 {
764 /* Use same directory as the ffname,
765 * "dir/name" -> "dir/.name.un~" */
766 undo_file_name = vim_strnsave(ffname, STRLEN(ffname) + 5);
767 if (undo_file_name == NULL)
768 break;
769 p = gettail(undo_file_name);
770 mch_memmove(p + 1, p, STRLEN(p) + 1);
771 *p = '.';
772 STRCAT(p, ".un~");
773 }
774 else
775 {
776 dir_name[dir_len] = NUL;
777 if (mch_isdir(dir_name))
778 {
779 if (munged_name == NULL)
780 {
781 munged_name = vim_strsave(ffname);
782 if (munged_name == NULL)
783 return NULL;
784 for (p = munged_name; *p != NUL; mb_ptr_adv(p))
785 if (vim_ispathsep(*p))
786 *p = '%';
787 }
788 undo_file_name = concat_fnames(dir_name, munged_name, TRUE);
789 }
790 }
791
792 /* When reading check if the file exists. */
793 if (undo_file_name != NULL && (!reading
794 || mch_stat((char *)undo_file_name, &st) >= 0))
795 break;
796 vim_free(undo_file_name);
797 undo_file_name = NULL;
798 }
799
800 vim_free(munged_name);
801 return undo_file_name;
802 }
803
804 /*
805 * Load the undo tree from an undo file.
806 * If "name" is not NULL use it as the undo file name. This also means being
807 * a bit more verbose.
808 * Otherwise use curbuf->b_ffname to generate the undo file name.
809 * "hash[UNDO_HASH_SIZE]" must be the hash value of the buffer text.
810 */
811 void
812 u_read_undo(name, hash)
813 char_u *name;
814 char_u *hash;
815 {
816 char_u *file_name;
817 FILE *fp;
818 long magic, version, str_len;
819 char_u *line_ptr = NULL;
820 linenr_T line_lnum;
821 colnr_T line_colnr;
822 linenr_T line_count;
823 int uep_len;
824 int line_len;
825 int num_head;
826 long old_header_seq, new_header_seq, cur_header_seq;
827 long seq_last, seq_cur;
828 short old_idx = -1, new_idx = -1, cur_idx = -1;
829 long num_read_uhps = 0;
830 time_t seq_time;
831 int i, j;
832 int c;
833 short found_first_uep = 0;
834 char_u **array;
835 char_u *line;
836 u_entry_T *uep, *last_uep, *nuep;
837 u_header_T *uhp;
838 u_header_T **uhp_table = NULL;
839 char_u read_hash[UNDO_HASH_SIZE];
840
841 if (name == NULL)
842 {
843 file_name = u_get_undo_file_name(curbuf->b_ffname, TRUE);
844 if (file_name == NULL)
845 return;
846 }
847 else
848 file_name = name;
849
850 if (p_verbose > 0)
851 smsg((char_u *)_("Reading undo file: %s"), file_name);
852 fp = mch_fopen((char *)file_name, "r");
853 if (fp == NULL)
854 {
855 if (name != NULL || p_verbose > 0)
856 EMSG2(_("E822: Cannot open undo file for reading: %s"), file_name);
857 goto error;
858 }
859
860 /* Begin overall file information */
861 magic = get2c(fp);
862 if (magic != UF_START_MAGIC)
863 {
864 EMSG2(_("E823: Corrupted undo file: %s"), file_name);
865 goto error;
866 }
867 version = get2c(fp);
868 if (version != UF_VERSION)
869 {
870 EMSG2(_("E824: Incompatible undo file: %s"), file_name);
871 goto error;
872 }
873
874 fread(read_hash, UNDO_HASH_SIZE, 1, fp);
875 line_count = (linenr_T)get4c(fp);
876 if (memcmp(hash, read_hash, UNDO_HASH_SIZE) != 0
877 || line_count != curbuf->b_ml.ml_line_count)
878 {
879 if (p_verbose > 0 || name != NULL)
880 {
881 verbose_enter();
882 give_warning((char_u *)_("Undo file contents changed"), TRUE);
883 verbose_leave();
884 }
885 goto error;
886 }
887
888 /* Begin undo data for U */
889 str_len = get4c(fp);
890 if (str_len < 0)
891 goto error;
892 else if (str_len > 0)
893 {
894 if ((line_ptr = U_ALLOC_LINE(str_len)) == NULL)
895 goto error;
896 for (i = 0; i < str_len; i++)
897 line_ptr[i] = (char_u)getc(fp);
898 line_ptr[i] = NUL;
899 }
900 line_lnum = (linenr_T)get4c(fp);
901 line_colnr = (colnr_T)get4c(fp);
902
903 /* Begin general undo data */
904 old_header_seq = get4c(fp);
905 new_header_seq = get4c(fp);
906 cur_header_seq = get4c(fp);
907 num_head = get4c(fp);
908 seq_last = get4c(fp);
909 seq_cur = get4c(fp);
910 seq_time = get4c(fp);
911
912 /* uhp_table will store the freshly created undo headers we allocate
913 * until we insert them into curbuf. The table remains sorted by the
914 * sequence numbers of the headers. */
915 uhp_table = (u_header_T **)U_ALLOC_LINE(num_head * sizeof(u_header_T *));
916 if (uhp_table == NULL)
917 goto error;
918 vim_memset(uhp_table, 0, num_head * sizeof(u_header_T *));
919
920 c = get2c(fp);
921 while (c == UF_HEADER_MAGIC)
922 {
923 found_first_uep = 0;
924 uhp = (u_header_T *)U_ALLOC_LINE((unsigned)sizeof(u_header_T));
925 if (uhp == NULL)
926 goto error;
927 vim_memset(uhp, 0, sizeof(u_header_T));
928 /* We're not actually trying to store pointers here. We're just storing
929 * IDs so we can swizzle them into pointers later - hence the type
930 * cast. */
931 uhp->uh_next = (u_header_T *)(long)get4c(fp);
932 uhp->uh_prev = (u_header_T *)(long)get4c(fp);
933 uhp->uh_alt_next = (u_header_T *)(long)get4c(fp);
934 uhp->uh_alt_prev = (u_header_T *)(long)get4c(fp);
935 uhp->uh_seq = get4c(fp);
936 if (uhp->uh_seq <= 0)
937 {
938 EMSG2(_("E825: Undo file corruption: invalid uh_seq.: %s"),
939 file_name);
940 U_FREE_LINE(uhp);
941 goto error;
942 }
943 uhp->uh_walk = 0;
944 unserialize_pos(&uhp->uh_cursor, fp);
945 #ifdef FEAT_VIRTUALEDIT
946 uhp->uh_cursor_vcol = get4c(fp);
947 #else
948 (void)get4c(fp);
949 #endif
950 uhp->uh_flags = get2c(fp);
951 for (i = 0; i < NMARKS; ++i)
952 unserialize_pos(&uhp->uh_namedm[i], fp);
953 #ifdef FEAT_VISUAL
954 unserialize_visualinfo(&uhp->uh_visual, fp);
955 #else
956 {
957 visualinfo_T info;
958 unserialize_visualinfo(&info, fp);
959 }
960 #endif
961 uhp->uh_time = get4c(fp);
962
963 /* Unserialize uep list. The first 4 bytes is the length of the
964 * entire uep in bytes minus the length of the strings within.
965 * -1 is a sentinel value meaning no more ueps.*/
966 last_uep = NULL;
967 while ((uep_len = get4c(fp)) != -1)
968 {
969 uep = (u_entry_T *)U_ALLOC_LINE((unsigned)sizeof(u_entry_T));
970 vim_memset(uep, 0, sizeof(u_entry_T));
971 if (uep == NULL)
972 goto error;
973 uep->ue_top = get4c(fp);
974 uep->ue_bot = get4c(fp);
975 uep->ue_lcount = get4c(fp);
976 uep->ue_size = get4c(fp);
977 uep->ue_next = NULL;
978 array = (char_u **)U_ALLOC_LINE(
979 (unsigned)(sizeof(char_u *) * uep->ue_size));
980 for (i = 0; i < uep->ue_size; i++)
981 {
982 line_len = get4c(fp);
983 /* U_ALLOC_LINE provides an extra byte for the NUL terminator.*/
984 line = (char_u *)U_ALLOC_LINE(
985 (unsigned) (sizeof(char_u) * line_len));
986 if (line == NULL)
987 goto error;
988 for (j = 0; j < line_len; j++)
989 {
990 line[j] = getc(fp);
991 }
992 line[j] = '\0';
993 array[i] = line;
994 }
995 uep->ue_array = array;
996 if (found_first_uep == 0)
997 {
998 uhp->uh_entry = uep;
999 found_first_uep = 1;
1000 }
1001 else
1002 {
1003 last_uep->ue_next = uep;
1004 }
1005 last_uep = uep;
1006 }
1007
1008 /* Insertion sort the uhp into the table by its uh_seq. This is
1009 * required because, while the number of uhps is limited to
1010 * num_heads, and the uh_seq order is monotonic with respect to
1011 * creation time, the starting uh_seq can be > 0 if any undolevel
1012 * culling was done at undofile write time, and there can be uh_seq
1013 * gaps in the uhps.
1014 */
1015 for (i = num_read_uhps - 1; i >= -1; i--)
1016 {
1017 /* if i == -1, we've hit the leftmost side of the table, so insert
1018 * at uhp_table[0]. */
1019 if (i == -1 || uhp->uh_seq > uhp_table[i]->uh_seq)
1020 {
1021 /* If we've had to move from the rightmost side of the table,
1022 * we have to shift everything to the right by one spot. */
1023 if (i < num_read_uhps - 1)
1024 {
1025 memmove(uhp_table + i + 2, uhp_table + i + 1,
1026 (num_read_uhps - i) * sizeof(u_header_T *));
1027 }
1028 uhp_table[i + 1] = uhp;
1029 break;
1030 }
1031 else if (uhp->uh_seq == uhp_table[i]->uh_seq)
1032 {
1033 EMSG2(_("E826 Undo file corruption: duplicate uh_seq: %s"),
1034 file_name);
1035 goto error;
1036 }
1037 }
1038 num_read_uhps++;
1039 c = get2c(fp);
1040 }
1041
1042 if (c != UF_END_MAGIC)
1043 {
1044 EMSG2(_("E827: Undo file corruption; no end marker: %s"), file_name);
1045 goto error;
1046 }
1047
1048 /* We've organized all of the uhps into a table sorted by uh_seq. Now we
1049 * iterate through the table and swizzle each sequence number we've
1050 * stored in uh_foo into a pointer corresponding to the header with that
1051 * sequence number. Then free curbuf's old undo structure, give curbuf
1052 * the updated {old,new,cur}head pointers, and then free the table. */
1053 for (i = 0; i < num_head; i++)
1054 {
1055 uhp = uhp_table[i];
1056 if (uhp == NULL)
1057 continue;
1058 for (j = 0; j < num_head; j++)
1059 {
1060 if (uhp_table[j] == NULL)
1061 continue;
1062 if (uhp_table[j]->uh_seq == (long)uhp->uh_next)
1063 uhp->uh_next = uhp_table[j];
1064 if (uhp_table[j]->uh_seq == (long)uhp->uh_prev)
1065 uhp->uh_prev = uhp_table[j];
1066 if (uhp_table[j]->uh_seq == (long)uhp->uh_alt_next)
1067 uhp->uh_alt_next = uhp_table[j];
1068 if (uhp_table[j]->uh_seq == (long)uhp->uh_alt_prev)
1069 uhp->uh_alt_prev = uhp_table[j];
1070 }
1071 if (old_header_seq > 0 && old_idx < 0 && uhp->uh_seq == old_header_seq)
1072 old_idx = i;
1073 if (new_header_seq > 0 && new_idx < 0 && uhp->uh_seq == new_header_seq)
1074 new_idx = i;
1075 if (cur_header_seq > 0 && cur_idx < 0 && uhp->uh_seq == cur_header_seq)
1076 cur_idx = i;
1077 }
1078 u_blockfree(curbuf);
1079 curbuf->b_u_oldhead = old_idx < 0 ? 0 : uhp_table[old_idx];
1080 curbuf->b_u_newhead = new_idx < 0 ? 0 : uhp_table[new_idx];
1081 curbuf->b_u_curhead = cur_idx < 0 ? 0 : uhp_table[cur_idx];
1082 curbuf->b_u_line_ptr = line_ptr;
1083 curbuf->b_u_line_lnum = line_lnum;
1084 curbuf->b_u_line_colnr = line_colnr;
1085 curbuf->b_u_numhead = num_head;
1086 curbuf->b_u_seq_last = seq_last;
1087 curbuf->b_u_seq_cur = seq_cur;
1088 curbuf->b_u_seq_time = seq_time;
1089 U_FREE_LINE(uhp_table);
1090 #ifdef U_DEBUG
1091 u_check(TRUE);
1092 #endif
1093 if (name != NULL)
1094 smsg((char_u *)_("Finished reading undo file %s"), file_name);
1095 goto theend;
1096
1097 error:
1098 if (line_ptr != NULL)
1099 U_FREE_LINE(line_ptr);
1100 if (uhp_table != NULL)
1101 {
1102 for (i = 0; i < num_head; i++)
1103 {
1104 if (uhp_table[i] != NULL)
1105 {
1106 uep = uhp_table[i]->uh_entry;
1107 while (uep != NULL)
1108 {
1109 nuep = uep->ue_next;
1110 u_freeentry(uep, uep->ue_size);
1111 uep = nuep;
1112 }
1113 U_FREE_LINE(uhp_table[i]);
1114 }
1115 }
1116 U_FREE_LINE(uhp_table);
1117 }
1118
1119 theend:
1120 if (fp != NULL)
1121 fclose(fp);
1122 if (file_name != name)
1123 vim_free(file_name);
1124 return;
1125 }
1126
1127 /*
1128 * Serialize "uep" to "fp".
1129 */
1130 static int
1131 serialize_uep(uep, fp)
1132 u_entry_T *uep;
1133 FILE *fp;
1134 {
1135 int i;
1136 int uep_len;
1137 int *entry_lens;
1138
1139 if (uep->ue_size > 0)
1140 entry_lens = (int *)alloc(uep->ue_size * sizeof(int));
1141
1142 /* Define uep_len to be the size of the entire uep minus the size of its
1143 * component strings, in bytes. The sizes of the component strings
1144 * are written before each individual string.
1145 * We have 4 entries each of 4 bytes, plus ue_size * 4 bytes
1146 * of string size information. */
1147
1148 uep_len = uep->ue_size * 4;
1149 /* Collect sizing information for later serialization. */
1150 for (i = 0; i < uep->ue_size; i++)
1151 {
1152 entry_lens[i] = (int)STRLEN(uep->ue_array[i]);
1153 uep_len += entry_lens[i];
1154 }
1155 put_bytes(fp, (long_u)uep_len, 4);
1156 put_bytes(fp, (long_u)uep->ue_top, 4);
1157 put_bytes(fp, (long_u)uep->ue_bot, 4);
1158 put_bytes(fp, (long_u)uep->ue_lcount, 4);
1159 put_bytes(fp, (long_u)uep->ue_size, 4);
1160 for (i = 0; i < uep->ue_size; i++)
1161 {
1162 if (put_bytes(fp, (long_u)entry_lens[i], 4) == FAIL)
1163 return FAIL;
1164 fprintf(fp, "%s", uep->ue_array[i]);
1165 }
1166 if (uep->ue_size > 0)
1167 vim_free(entry_lens);
1168 return OK;
1169 }
1170
1171 /*
1172 * Serialize "pos" to "fp".
1173 */
1174 static void
1175 serialize_pos(pos, fp)
1176 pos_T pos;
1177 FILE *fp;
1178 {
1179 put_bytes(fp, (long_u)pos.lnum, 4);
1180 put_bytes(fp, (long_u)pos.col, 4);
1181 #ifdef FEAT_VIRTUALEDIT
1182 put_bytes(fp, (long_u)pos.coladd, 4);
1183 #else
1184 put_bytes(fp, (long_u)0, 4);
1185 #endif
1186 }
1187
1188 /*
1189 * Serialize "info" to "fp".
1190 */
1191 static void
1192 serialize_visualinfo(info, fp)
1193 visualinfo_T info;
1194 FILE *fp;
1195 {
1196 serialize_pos(info.vi_start, fp);
1197 serialize_pos(info.vi_end, fp);
1198 put_bytes(fp, (long_u)info.vi_mode, 4);
1199 put_bytes(fp, (long_u)info.vi_curswant, 4);
1200 }
1201
1202 static char_u e_not_open[] = N_("E828: Cannot open undo file for writing: %s");
1203
1204 /*
1205 * Write the undo tree in an undo file.
1206 * When "name" is not NULL, use it as the name of the undo file.
1207 * Otherwise use buf->b_ffname to generate the undo file name.
1208 * "buf" must never be null, buf->b_ffname is used to obtain the original file
1209 * permissions.
1210 * "forceit" is TRUE for ":wundo!", FALSE otherwise.
1211 * "hash[UNDO_HASH_SIZE]" must be the hash value of the buffer text.
1212 */
1213 void
1214 u_write_undo(name, forceit, buf, hash)
1215 char_u *name;
1216 int forceit;
1217 buf_T *buf;
1218 char_u *hash;
1219 {
1220 u_header_T *uhp;
1221 u_entry_T *uep;
1222 char_u *file_name;
1223 int str_len, i, uep_len, mark;
1224 int fd;
1225 FILE *fp = NULL;
1226 int perm;
1227 int write_ok = FALSE;
1228 #ifdef UNIX
1229 struct stat st_old;
1230 struct stat st_new;
1231 #endif
1232
1233 if (name == NULL)
1234 {
1235 file_name = u_get_undo_file_name(buf->b_ffname, FALSE);
1236 if (file_name == NULL)
1237 return;
1238 }
1239 else
1240 file_name = name;
1241
1242 #ifdef UNIX
1243 if (mch_stat((char *)buf->b_ffname, &st_old) >= 0)
1244 perm = st_old.st_mode;
1245 else
1246 perm = 0600;
1247 #else
1248 perm = mch_getperm(buf->b_ffname);
1249 if (perm < 0)
1250 perm = 0600;
1251 #endif
1252 /* set file protection same as original file, but strip s-bit */
1253 perm = perm & 0777;
1254
1255 /* If the undo file exists, verify that it actually is an undo file, and
1256 * delete it. */
1257 if (mch_getperm(file_name) >= 0)
1258 {
1259 if (name == NULL || !forceit)
1260 {
1261 /* Check we can read it and it's an undo file. */
1262 fd = mch_open((char *)file_name, O_RDONLY|O_EXTRA, 0);
1263 if (fd < 0)
1264 {
1265 if (name != NULL || p_verbose > 0)
1266 smsg((char_u *)_("Will not overwrite with undo file, cannot read: %s"),
1267 file_name);
1268 goto theend;
1269 }
1270 else
1271 {
1272 char_u buf[2];
1273
1274 vim_read(fd, buf, 2);
1275 close(fd);
1276 if ((buf[0] << 8) + buf[1] != UF_START_MAGIC)
1277 {
1278 if (name != NULL || p_verbose > 0)
1279 smsg((char_u *)_("Will not overwrite, this is not an undo file: %s"),
1280 file_name);
1281 goto theend;
1282 }
1283 }
1284 }
1285 mch_remove(file_name);
1286 }
1287
1288 fd = mch_open((char *)file_name,
1289 O_CREAT|O_EXTRA|O_WRONLY|O_EXCL|O_NOFOLLOW, perm);
1290 (void)mch_setperm(file_name, perm);
1291 if (fd < 0)
1292 {
1293 EMSG2(_(e_not_open), file_name);
1294 goto theend;
1295 }
1296 if (p_verbose > 0)
1297 smsg((char_u *)_("Writing undo file: %s"), file_name);
1298
1299 #ifdef UNIX
1300 /*
1301 * Try to set the group of the undo file same as the original file. If
1302 * this fails, set the protection bits for the group same as the
1303 * protection bits for others.
1304 */
1305 if (mch_stat((char *)file_name, &st_new) >= 0
1306 && st_new.st_gid != st_old.st_gid
1307 # ifdef HAVE_FCHOWN /* sequent-ptx lacks fchown() */
1308 && fchown(fd, (uid_t)-1, st_old.st_gid) != 0
1309 # endif
1310 )
1311 mch_setperm(file_name, (perm & 0707) | ((perm & 07) << 3));
1312 # ifdef HAVE_SELINUX
1313 mch_copy_sec(buf->b_ffname, file_name);
1314 # endif
1315 #endif
1316
1317 fp = fdopen(fd, "w");
1318 if (fp == NULL)
1319 {
1320 EMSG2(_(e_not_open), file_name);
1321 close(fd);
1322 mch_remove(file_name);
1323 goto theend;
1324 }
1325
1326 /* Start writing, first overall file information */
1327 put_bytes(fp, (long_u)UF_START_MAGIC, 2);
1328 put_bytes(fp, (long_u)UF_VERSION, 2);
1329
1330 /* Write a hash of the buffer text, so that we can verify it is still the
1331 * same when reading the buffer text. */
1332 if (fwrite(hash, (size_t)UNDO_HASH_SIZE, (size_t)1, fp) != 1)
1333 goto write_error;
1334 put_bytes(fp, (long_u)buf->b_ml.ml_line_count, 4);
1335
1336 /* Begin undo data for U */
1337 str_len = buf->b_u_line_ptr != NULL ? STRLEN(buf->b_u_line_ptr) : 0;
1338 put_bytes(fp, (long_u)str_len, 4);
1339 if (str_len > 0 && fwrite(buf->b_u_line_ptr, (size_t)str_len,
1340 (size_t)1, fp) != 1)
1341 goto write_error;
1342
1343 put_bytes(fp, (long_u)buf->b_u_line_lnum, 4);
1344 put_bytes(fp, (long_u)buf->b_u_line_colnr, 4);
1345
1346 /* Begin general undo data */
1347 uhp = buf->b_u_oldhead;
1348 put_bytes(fp, (long_u)(uhp != NULL ? uhp->uh_seq : 0), 4);
1349
1350 uhp = buf->b_u_newhead;
1351 put_bytes(fp, (long_u)(uhp != NULL ? uhp->uh_seq : 0), 4);
1352
1353 uhp = buf->b_u_curhead;
1354 put_bytes(fp, (long_u)(uhp != NULL ? uhp->uh_seq : 0), 4);
1355
1356 put_bytes(fp, (long_u)buf->b_u_numhead, 4);
1357 put_bytes(fp, (long_u)buf->b_u_seq_last, 4);
1358 put_bytes(fp, (long_u)buf->b_u_seq_cur, 4);
1359 put_bytes(fp, (long_u)buf->b_u_seq_time, 4);
1360
1361 /* Iteratively serialize UHPs and their UEPs from the top down. */
1362 mark = ++lastmark;
1363 uhp = buf->b_u_oldhead;
1364 while (uhp != NULL)
1365 {
1366 /* Serialize current UHP if we haven't seen it */
1367 if (uhp->uh_walk != mark)
1368 {
1369 if (put_bytes(fp, (long_u)UF_HEADER_MAGIC, 2) == FAIL)
1370 goto write_error;
1371
1372 put_bytes(fp, (long_u)((uhp->uh_next != NULL)
1373 ? uhp->uh_next->uh_seq : 0), 4);
1374 put_bytes(fp, (long_u)((uhp->uh_prev != NULL)
1375 ? uhp->uh_prev->uh_seq : 0), 4);
1376 put_bytes(fp, (long_u)((uhp->uh_alt_next != NULL)
1377 ? uhp->uh_alt_next->uh_seq : 0), 4);
1378 put_bytes(fp, (long_u)((uhp->uh_alt_prev != NULL)
1379 ? uhp->uh_alt_prev->uh_seq : 0), 4);
1380 put_bytes(fp, uhp->uh_seq, 4);
1381 serialize_pos(uhp->uh_cursor, fp);
1382 #ifdef FEAT_VIRTUALEDIT
1383 put_bytes(fp, (long_u)uhp->uh_cursor_vcol, 4);
1384 #else
1385 put_bytes(fp, (long_u)0, 4);
1386 #endif
1387 put_bytes(fp, (long_u)uhp->uh_flags, 2);
1388 /* Assume NMARKS will stay the same. */
1389 for (i = 0; i < NMARKS; ++i)
1390 {
1391 serialize_pos(uhp->uh_namedm[i], fp);
1392 }
1393 #ifdef FEAT_VISUAL
1394 serialize_visualinfo(uhp->uh_visual, fp);
1395 #endif
1396 put_bytes(fp, (long_u)uhp->uh_time, 4);
1397
1398 uep = uhp->uh_entry;
1399 while (uep != NULL)
1400 {
1401 if (serialize_uep(uep, fp) == FAIL)
1402 goto write_error;
1403 uep = uep->ue_next;
1404 }
1405 /* Sentinel value: no more ueps */
1406 uep_len = -1;
1407 put_bytes(fp, (long_u)uep_len, 4);
1408 uhp->uh_walk = mark;
1409 }
1410
1411 /* Now walk through the tree - algorithm from undo_time */
1412 if (uhp->uh_prev != NULL && uhp->uh_prev->uh_walk != mark)
1413 uhp = uhp->uh_prev;
1414 else if (uhp->uh_alt_next != NULL && uhp->uh_alt_next->uh_walk != mark)
1415 uhp = uhp->uh_alt_next;
1416 else if (uhp->uh_next != NULL && uhp->uh_alt_prev == NULL
1417 && uhp->uh_next->uh_walk != mark)
1418 uhp = uhp->uh_next;
1419 else if (uhp->uh_alt_prev != NULL)
1420 uhp = uhp->uh_alt_prev;
1421 else
1422 uhp = uhp->uh_next;
1423 }
1424
1425 if (put_bytes(fp, (long_u)UF_END_MAGIC, 2) == OK)
1426 write_ok = TRUE;
1427
1428 write_error:
1429 fclose(fp);
1430 if (!write_ok)
1431 EMSG2(_("E829: write error in undo file: %s"), file_name);
1432
1433 #if defined(MACOS_CLASSIC) || defined(WIN3264)
1434 (void)mch_copy_file_attribute(buf->b_ffname, file_name);
1435 #endif
1436 #ifdef HAVE_ACL
1437 {
1438 vim_acl_T acl;
1439
1440 /* For systems that support ACL: get the ACL from the original file. */
1441 acl = mch_get_acl(buf->b_ffname);
1442 mch_set_acl(file_name, acl);
1443 }
1444 #endif
1445
1446 theend:
1447 if (file_name != name)
1448 vim_free(file_name);
1449 }
1450
1451 #endif /* FEAT_PERSISTENT_UNDO */
1452
654 1453
655 /* 1454 /*
656 * If 'cpoptions' contains 'u': Undo the previous undo or redo (vi compatible). 1455 * If 'cpoptions' contains 'u': Undo the previous undo or redo (vi compatible).
657 * If 'cpoptions' does not contain 'u': Always undo. 1456 * If 'cpoptions' does not contain 'u': Always undo.
658 */ 1457 */
754 curbuf->b_u_curhead = curbuf->b_u_curhead->uh_prev; 1553 curbuf->b_u_curhead = curbuf->b_u_curhead->uh_prev;
755 } 1554 }
756 } 1555 }
757 u_undo_end(undo_undoes, FALSE); 1556 u_undo_end(undo_undoes, FALSE);
758 } 1557 }
759
760 static int lastmark = 0;
761 1558
762 /* 1559 /*
763 * Undo or redo over the timeline. 1560 * Undo or redo over the timeline.
764 * When "step" is negative go back in time, otherwise goes forward in time. 1561 * When "step" is negative go back in time, otherwise goes forward in time.
765 * When "sec" is FALSE make "step" steps, when "sec" is TRUE use "step" as 1562 * When "sec" is FALSE make "step" steps, when "sec" is TRUE use "step" as
925 if (uhp != NULL) /* found it */ 1722 if (uhp != NULL) /* found it */
926 break; 1723 break;
927 1724
928 if (absolute) 1725 if (absolute)
929 { 1726 {
930 EMSGN(_("Undo number %ld not found"), step); 1727 EMSGN(_("E830: Undo number %ld not found"), step);
931 return; 1728 return;
932 } 1729 }
933 1730
934 if (closest == closest_start) 1731 if (closest == closest_start)
935 { 1732 {