Mercurial > vim
annotate src/memline.c @ 8019:d685893d852e v7.4.1304
commit https://github.com/vim/vim/commit/7823a3bd2eed6ff9e544d201de96710bd5344aaf
Author: Bram Moolenaar <Bram@vim.org>
Date: Thu Feb 11 21:08:32 2016 +0100
patch 7.4.1304
Problem: Function names are difficult to read.
Solution: Rename jsonencode to json_encode, jsondecode to json_decode,
jsencode to js_encode and jsdecode to js_decode.
author | Christian Brabandt <cb@256bit.org> |
---|---|
date | Thu, 11 Feb 2016 21:15:05 +0100 |
parents | a7e58c6e4e9a |
children | 05b88224cea1 |
rev | line source |
---|---|
7 | 1 /* vi:set ts=8 sts=4 sw=4: |
2 * | |
3 * VIM - Vi IMproved by Bram Moolenaar | |
4 * | |
5 * Do ":help uganda" in Vim to read copying and usage conditions. | |
6 * Do ":help credits" in Vim to see a list of people who contributed. | |
7 * See README.txt for an overview of the Vim source code. | |
8 */ | |
9 | |
10 /* for debugging */ | |
11 /* #define CHECK(c, s) if (c) EMSG(s) */ | |
12 #define CHECK(c, s) | |
13 | |
14 /* | |
15 * memline.c: Contains the functions for appending, deleting and changing the | |
625 | 16 * text lines. The memfile functions are used to store the information in |
17 * blocks of memory, backed up by a file. The structure of the information is | |
18 * a tree. The root of the tree is a pointer block. The leaves of the tree | |
19 * are data blocks. In between may be several layers of pointer blocks, | |
20 * forming branches. | |
7 | 21 * |
22 * Three types of blocks are used: | |
23 * - Block nr 0 contains information for recovery | |
24 * - Pointer blocks contain list of pointers to other blocks. | |
25 * - Data blocks contain the actual text. | |
26 * | |
27 * Block nr 0 contains the block0 structure (see below). | |
28 * | |
29 * Block nr 1 is the first pointer block. It is the root of the tree. | |
30 * Other pointer blocks are branches. | |
31 * | |
32 * If a line is too big to fit in a single page, the block containing that | |
33 * line is made big enough to hold the line. It may span several pages. | |
34 * Otherwise all blocks are one page. | |
35 * | |
36 * A data block that was filled when starting to edit a file and was not | |
37 * changed since then, can have a negative block number. This means that it | |
38 * has not yet been assigned a place in the file. When recovering, the lines | |
39 * in this data block can be read from the original file. When the block is | |
40 * changed (lines appended/deleted/changed) or when it is flushed it gets a | |
41 * positive number. Use mf_trans_del() to get the new number, before calling | |
42 * mf_get(). | |
43 */ | |
44 | |
45 #include "vim.h" | |
46 | |
47 #ifndef UNIX /* it's in os_unix.h for Unix */ | |
48 # include <time.h> | |
49 #endif | |
50 | |
1030 | 51 #if defined(SASC) || defined(__amigaos4__) |
7 | 52 # include <proto/dos.h> /* for Open() and Close() */ |
53 #endif | |
54 | |
55 typedef struct block0 ZERO_BL; /* contents of the first block */ | |
56 typedef struct pointer_block PTR_BL; /* contents of a pointer block */ | |
57 typedef struct data_block DATA_BL; /* contents of a data block */ | |
58 typedef struct pointer_entry PTR_EN; /* block/line-count pair */ | |
59 | |
2267 | 60 #define DATA_ID (('d' << 8) + 'a') /* data block id */ |
61 #define PTR_ID (('p' << 8) + 't') /* pointer block id */ | |
62 #define BLOCK0_ID0 'b' /* block 0 id 0 */ | |
63 #define BLOCK0_ID1 '0' /* block 0 id 1 */ | |
64 #define BLOCK0_ID1_C0 'c' /* block 0 id 1 'cm' 0 */ | |
65 #define BLOCK0_ID1_C1 'C' /* block 0 id 1 'cm' 1 */ | |
6122 | 66 #define BLOCK0_ID1_C2 'd' /* block 0 id 1 'cm' 2 */ |
67 | |
68 #if defined(FEAT_CRYPT) | |
69 static int id1_codes[] = { | |
70 BLOCK0_ID1_C0, /* CRYPT_M_ZIP */ | |
71 BLOCK0_ID1_C1, /* CRYPT_M_BF */ | |
72 BLOCK0_ID1_C2, /* CRYPT_M_BF2 */ | |
73 }; | |
74 #endif | |
7 | 75 |
76 /* | |
77 * pointer to a block, used in a pointer block | |
78 */ | |
79 struct pointer_entry | |
80 { | |
81 blocknr_T pe_bnum; /* block number */ | |
82 linenr_T pe_line_count; /* number of lines in this branch */ | |
83 linenr_T pe_old_lnum; /* lnum for this block (for recovery) */ | |
84 int pe_page_count; /* number of pages in block pe_bnum */ | |
85 }; | |
86 | |
87 /* | |
88 * A pointer block contains a list of branches in the tree. | |
89 */ | |
90 struct pointer_block | |
91 { | |
92 short_u pb_id; /* ID for pointer block: PTR_ID */ | |
2240
6b4879aea261
Add test for gettabvar() and settabvar().
Bram Moolenaar <bram@vim.org>
parents:
2221
diff
changeset
|
93 short_u pb_count; /* number of pointers in this block */ |
7 | 94 short_u pb_count_max; /* maximum value for pb_count */ |
95 PTR_EN pb_pointer[1]; /* list of pointers to blocks (actually longer) | |
96 * followed by empty space until end of page */ | |
97 }; | |
98 | |
99 /* | |
100 * A data block is a leaf in the tree. | |
101 * | |
102 * The text of the lines is at the end of the block. The text of the first line | |
103 * in the block is put at the end, the text of the second line in front of it, | |
104 * etc. Thus the order of the lines is the opposite of the line number. | |
105 */ | |
106 struct data_block | |
107 { | |
108 short_u db_id; /* ID for data block: DATA_ID */ | |
109 unsigned db_free; /* free space available */ | |
110 unsigned db_txt_start; /* byte where text starts */ | |
111 unsigned db_txt_end; /* byte just after data block */ | |
112 linenr_T db_line_count; /* number of lines in this block */ | |
113 unsigned db_index[1]; /* index for start of line (actually bigger) | |
114 * followed by empty space upto db_txt_start | |
115 * followed by the text in the lines until | |
116 * end of page */ | |
117 }; | |
118 | |
119 /* | |
120 * The low bits of db_index hold the actual index. The topmost bit is | |
121 * used for the global command to be able to mark a line. | |
122 * This method is not clean, but otherwise there would be at least one extra | |
123 * byte used for each line. | |
124 * The mark has to be in this place to keep it with the correct line when other | |
125 * lines are inserted or deleted. | |
126 */ | |
127 #define DB_MARKED ((unsigned)1 << ((sizeof(unsigned) * 8) - 1)) | |
128 #define DB_INDEX_MASK (~DB_MARKED) | |
129 | |
130 #define INDEX_SIZE (sizeof(unsigned)) /* size of one db_index entry */ | |
131 #define HEADER_SIZE (sizeof(DATA_BL) - INDEX_SIZE) /* size of data block header */ | |
132 | |
39 | 133 #define B0_FNAME_SIZE_ORG 900 /* what it was in older versions */ |
2267 | 134 #define B0_FNAME_SIZE_NOCRYPT 898 /* 2 bytes used for other things */ |
135 #define B0_FNAME_SIZE_CRYPT 890 /* 10 bytes used for other things */ | |
39 | 136 #define B0_UNAME_SIZE 40 |
137 #define B0_HNAME_SIZE 40 | |
7 | 138 /* |
139 * Restrict the numbers to 32 bits, otherwise most compilers will complain. | |
140 * This won't detect a 64 bit machine that only swaps a byte in the top 32 | |
141 * bits, but that is crazy anyway. | |
142 */ | |
143 #define B0_MAGIC_LONG 0x30313233L | |
144 #define B0_MAGIC_INT 0x20212223L | |
145 #define B0_MAGIC_SHORT 0x10111213L | |
146 #define B0_MAGIC_CHAR 0x55 | |
147 | |
148 /* | |
149 * Block zero holds all info about the swap file. | |
150 * | |
151 * NOTE: DEFINITION OF BLOCK 0 SHOULD NOT CHANGE! It would make all existing | |
152 * swap files unusable! | |
153 * | |
154 * If size of block0 changes anyway, adjust MIN_SWAP_PAGE_SIZE in vim.h!! | |
155 * | |
1228 | 156 * This block is built up of single bytes, to make it portable across |
7 | 157 * different machines. b0_magic_* is used to check the byte order and size of |
158 * variables, because the rest of the swap file is not portable. | |
159 */ | |
160 struct block0 | |
161 { | |
2267 | 162 char_u b0_id[2]; /* id for block 0: BLOCK0_ID0 and BLOCK0_ID1, |
6122 | 163 * BLOCK0_ID1_C0, BLOCK0_ID1_C1, etc. */ |
7 | 164 char_u b0_version[10]; /* Vim version string */ |
165 char_u b0_page_size[4];/* number of bytes per page */ | |
166 char_u b0_mtime[4]; /* last modification time of file */ | |
167 char_u b0_ino[4]; /* inode of b0_fname */ | |
168 char_u b0_pid[4]; /* process id of creator (or 0) */ | |
169 char_u b0_uname[B0_UNAME_SIZE]; /* name of user (uid if no name) */ | |
170 char_u b0_hname[B0_HNAME_SIZE]; /* host name (if it has a name) */ | |
39 | 171 char_u b0_fname[B0_FNAME_SIZE_ORG]; /* name of file being edited */ |
7 | 172 long b0_magic_long; /* check for byte order of long */ |
173 int b0_magic_int; /* check for byte order of int */ | |
174 short b0_magic_short; /* check for byte order of short */ | |
175 char_u b0_magic_char; /* check for last char */ | |
176 }; | |
39 | 177 |
178 /* | |
625 | 179 * Note: b0_dirty and b0_flags are put at the end of the file name. For very |
39 | 180 * long file names in older versions of Vim they are invalid. |
181 * The 'fileencoding' comes before b0_flags, with a NUL in front. But only | |
182 * when there is room, for very long file names it's omitted. | |
183 */ | |
184 #define B0_DIRTY 0x55 | |
2267 | 185 #define b0_dirty b0_fname[B0_FNAME_SIZE_ORG - 1] |
39 | 186 |
187 /* | |
188 * The b0_flags field is new in Vim 7.0. | |
189 */ | |
2267 | 190 #define b0_flags b0_fname[B0_FNAME_SIZE_ORG - 2] |
191 | |
192 /* | |
193 * Crypt seed goes here, 8 bytes. New in Vim 7.3. | |
194 * Without encryption these bytes may be used for 'fenc'. | |
195 */ | |
196 #define b0_seed b0_fname[B0_FNAME_SIZE_ORG - 2 - MF_SEED_LEN] | |
39 | 197 |
198 /* The lowest two bits contain the fileformat. Zero means it's not set | |
199 * (compatible with Vim 6.x), otherwise it's EOL_UNIX + 1, EOL_DOS + 1 or | |
200 * EOL_MAC + 1. */ | |
201 #define B0_FF_MASK 3 | |
202 | |
203 /* Swap file is in directory of edited file. Used to find the file from | |
204 * different mount points. */ | |
205 #define B0_SAME_DIR 4 | |
206 | |
207 /* The 'fileencoding' is at the end of b0_fname[], with a NUL in front of it. | |
208 * When empty there is only the NUL. */ | |
209 #define B0_HAS_FENC 8 | |
7 | 210 |
211 #define STACK_INCR 5 /* nr of entries added to ml_stack at a time */ | |
212 | |
213 /* | |
214 * The line number where the first mark may be is remembered. | |
215 * If it is 0 there are no marks at all. | |
216 * (always used for the current buffer only, no buffer change possible while | |
217 * executing a global command). | |
218 */ | |
219 static linenr_T lowest_marked = 0; | |
220 | |
221 /* | |
222 * arguments for ml_find_line() | |
223 */ | |
224 #define ML_DELETE 0x11 /* delete line */ | |
225 #define ML_INSERT 0x12 /* insert line */ | |
226 #define ML_FIND 0x13 /* just find the line */ | |
227 #define ML_FLUSH 0x02 /* flush locked block */ | |
228 #define ML_SIMPLE(x) (x & 0x10) /* DEL, INS or FIND */ | |
229 | |
2267 | 230 /* argument for ml_upd_block0() */ |
231 typedef enum { | |
232 UB_FNAME = 0 /* update timestamp and filename */ | |
233 , UB_SAME_DIR /* update the B0_SAME_DIR flag */ | |
234 , UB_CRYPT /* update crypt key */ | |
235 } upd_block0_T; | |
236 | |
237 #ifdef FEAT_CRYPT | |
7803
37c929c4a073
commit https://github.com/vim/vim/commit/92b8b2d307e34117f146319872010b0ccc9d2713
Christian Brabandt <cb@256bit.org>
parents:
7410
diff
changeset
|
238 static void ml_set_mfp_crypt(buf_T *buf); |
37c929c4a073
commit https://github.com/vim/vim/commit/92b8b2d307e34117f146319872010b0ccc9d2713
Christian Brabandt <cb@256bit.org>
parents:
7410
diff
changeset
|
239 static void ml_set_b0_crypt(buf_T *buf, ZERO_BL *b0p); |
2267 | 240 #endif |
7803
37c929c4a073
commit https://github.com/vim/vim/commit/92b8b2d307e34117f146319872010b0ccc9d2713
Christian Brabandt <cb@256bit.org>
parents:
7410
diff
changeset
|
241 static int ml_check_b0_id(ZERO_BL *b0p); |
37c929c4a073
commit https://github.com/vim/vim/commit/92b8b2d307e34117f146319872010b0ccc9d2713
Christian Brabandt <cb@256bit.org>
parents:
7410
diff
changeset
|
242 static void ml_upd_block0(buf_T *buf, upd_block0_T what); |
37c929c4a073
commit https://github.com/vim/vim/commit/92b8b2d307e34117f146319872010b0ccc9d2713
Christian Brabandt <cb@256bit.org>
parents:
7410
diff
changeset
|
243 static void set_b0_fname(ZERO_BL *, buf_T *buf); |
37c929c4a073
commit https://github.com/vim/vim/commit/92b8b2d307e34117f146319872010b0ccc9d2713
Christian Brabandt <cb@256bit.org>
parents:
7410
diff
changeset
|
244 static void set_b0_dir_flag(ZERO_BL *b0p, buf_T *buf); |
39 | 245 #ifdef FEAT_MBYTE |
7803
37c929c4a073
commit https://github.com/vim/vim/commit/92b8b2d307e34117f146319872010b0ccc9d2713
Christian Brabandt <cb@256bit.org>
parents:
7410
diff
changeset
|
246 static void add_b0_fenc(ZERO_BL *b0p, buf_T *buf); |
39 | 247 #endif |
7803
37c929c4a073
commit https://github.com/vim/vim/commit/92b8b2d307e34117f146319872010b0ccc9d2713
Christian Brabandt <cb@256bit.org>
parents:
7410
diff
changeset
|
248 static time_t swapfile_info(char_u *); |
37c929c4a073
commit https://github.com/vim/vim/commit/92b8b2d307e34117f146319872010b0ccc9d2713
Christian Brabandt <cb@256bit.org>
parents:
7410
diff
changeset
|
249 static int recov_file_names(char_u **, char_u *, int prepend_dot); |
37c929c4a073
commit https://github.com/vim/vim/commit/92b8b2d307e34117f146319872010b0ccc9d2713
Christian Brabandt <cb@256bit.org>
parents:
7410
diff
changeset
|
250 static int ml_append_int(buf_T *, linenr_T, char_u *, colnr_T, int, int); |
37c929c4a073
commit https://github.com/vim/vim/commit/92b8b2d307e34117f146319872010b0ccc9d2713
Christian Brabandt <cb@256bit.org>
parents:
7410
diff
changeset
|
251 static int ml_delete_int(buf_T *, linenr_T, int); |
37c929c4a073
commit https://github.com/vim/vim/commit/92b8b2d307e34117f146319872010b0ccc9d2713
Christian Brabandt <cb@256bit.org>
parents:
7410
diff
changeset
|
252 static char_u *findswapname(buf_T *, char_u **, char_u *); |
37c929c4a073
commit https://github.com/vim/vim/commit/92b8b2d307e34117f146319872010b0ccc9d2713
Christian Brabandt <cb@256bit.org>
parents:
7410
diff
changeset
|
253 static void ml_flush_line(buf_T *); |
37c929c4a073
commit https://github.com/vim/vim/commit/92b8b2d307e34117f146319872010b0ccc9d2713
Christian Brabandt <cb@256bit.org>
parents:
7410
diff
changeset
|
254 static bhdr_T *ml_new_data(memfile_T *, int, int); |
37c929c4a073
commit https://github.com/vim/vim/commit/92b8b2d307e34117f146319872010b0ccc9d2713
Christian Brabandt <cb@256bit.org>
parents:
7410
diff
changeset
|
255 static bhdr_T *ml_new_ptr(memfile_T *); |
37c929c4a073
commit https://github.com/vim/vim/commit/92b8b2d307e34117f146319872010b0ccc9d2713
Christian Brabandt <cb@256bit.org>
parents:
7410
diff
changeset
|
256 static bhdr_T *ml_find_line(buf_T *, linenr_T, int); |
37c929c4a073
commit https://github.com/vim/vim/commit/92b8b2d307e34117f146319872010b0ccc9d2713
Christian Brabandt <cb@256bit.org>
parents:
7410
diff
changeset
|
257 static int ml_add_stack(buf_T *); |
37c929c4a073
commit https://github.com/vim/vim/commit/92b8b2d307e34117f146319872010b0ccc9d2713
Christian Brabandt <cb@256bit.org>
parents:
7410
diff
changeset
|
258 static void ml_lineadd(buf_T *, int); |
37c929c4a073
commit https://github.com/vim/vim/commit/92b8b2d307e34117f146319872010b0ccc9d2713
Christian Brabandt <cb@256bit.org>
parents:
7410
diff
changeset
|
259 static int b0_magic_wrong(ZERO_BL *); |
7 | 260 #ifdef CHECK_INODE |
7803
37c929c4a073
commit https://github.com/vim/vim/commit/92b8b2d307e34117f146319872010b0ccc9d2713
Christian Brabandt <cb@256bit.org>
parents:
7410
diff
changeset
|
261 static int fnamecmp_ino(char_u *, char_u *, long); |
7 | 262 #endif |
7803
37c929c4a073
commit https://github.com/vim/vim/commit/92b8b2d307e34117f146319872010b0ccc9d2713
Christian Brabandt <cb@256bit.org>
parents:
7410
diff
changeset
|
263 static void long_to_char(long, char_u *); |
37c929c4a073
commit https://github.com/vim/vim/commit/92b8b2d307e34117f146319872010b0ccc9d2713
Christian Brabandt <cb@256bit.org>
parents:
7410
diff
changeset
|
264 static long char_to_long(char_u *); |
7 | 265 #if defined(UNIX) || defined(WIN3264) |
7803
37c929c4a073
commit https://github.com/vim/vim/commit/92b8b2d307e34117f146319872010b0ccc9d2713
Christian Brabandt <cb@256bit.org>
parents:
7410
diff
changeset
|
266 static char_u *make_percent_swname(char_u *dir, char_u *name); |
7 | 267 #endif |
2267 | 268 #ifdef FEAT_CRYPT |
7803
37c929c4a073
commit https://github.com/vim/vim/commit/92b8b2d307e34117f146319872010b0ccc9d2713
Christian Brabandt <cb@256bit.org>
parents:
7410
diff
changeset
|
269 static cryptstate_T *ml_crypt_prepare(memfile_T *mfp, off_t offset, int reading); |
2267 | 270 #endif |
7 | 271 #ifdef FEAT_BYTEOFF |
7803
37c929c4a073
commit https://github.com/vim/vim/commit/92b8b2d307e34117f146319872010b0ccc9d2713
Christian Brabandt <cb@256bit.org>
parents:
7410
diff
changeset
|
272 static void ml_updatechunk(buf_T *buf, long line, long len, int updtype); |
7 | 273 #endif |
274 | |
275 /* | |
625 | 276 * Open a new memline for "buf". |
7 | 277 * |
625 | 278 * Return FAIL for failure, OK otherwise. |
7 | 279 */ |
280 int | |
7827
41789f16d6b2
commit https://github.com/vim/vim/commit/52ea13da0fe86df1abf34de52841e367035170c0
Christian Brabandt <cb@256bit.org>
parents:
7803
diff
changeset
|
281 ml_open(buf_T *buf) |
7 | 282 { |
283 memfile_T *mfp; | |
284 bhdr_T *hp = NULL; | |
285 ZERO_BL *b0p; | |
286 PTR_BL *pp; | |
287 DATA_BL *dp; | |
288 | |
625 | 289 /* |
290 * init fields in memline struct | |
291 */ | |
2267 | 292 buf->b_ml.ml_stack_size = 0; /* no stack yet */ |
625 | 293 buf->b_ml.ml_stack = NULL; /* no stack yet */ |
294 buf->b_ml.ml_stack_top = 0; /* nothing in the stack */ | |
295 buf->b_ml.ml_locked = NULL; /* no cached block */ | |
296 buf->b_ml.ml_line_lnum = 0; /* no cached line */ | |
7 | 297 #ifdef FEAT_BYTEOFF |
625 | 298 buf->b_ml.ml_chunksize = NULL; |
7 | 299 #endif |
300 | |
5737 | 301 if (cmdmod.noswapfile) |
302 buf->b_p_swf = FALSE; | |
303 | |
625 | 304 /* |
305 * When 'updatecount' is non-zero swap file may be opened later. | |
306 */ | |
307 if (p_uc && buf->b_p_swf) | |
308 buf->b_may_swap = TRUE; | |
7 | 309 else |
625 | 310 buf->b_may_swap = FALSE; |
311 | |
312 /* | |
313 * Open the memfile. No swap file is created yet. | |
314 */ | |
7 | 315 mfp = mf_open(NULL, 0); |
316 if (mfp == NULL) | |
317 goto error; | |
318 | |
625 | 319 buf->b_ml.ml_mfp = mfp; |
2267 | 320 #ifdef FEAT_CRYPT |
321 mfp->mf_buffer = buf; | |
322 #endif | |
625 | 323 buf->b_ml.ml_flags = ML_EMPTY; |
324 buf->b_ml.ml_line_count = 1; | |
13 | 325 #ifdef FEAT_LINEBREAK |
326 curwin->w_nrwidth_line_count = 0; | |
327 #endif | |
7 | 328 |
329 #if defined(MSDOS) && !defined(DJGPP) | |
330 /* for 16 bit MS-DOS create a swapfile now, because we run out of | |
331 * memory very quickly */ | |
332 if (p_uc != 0) | |
625 | 333 ml_open_file(buf); |
7 | 334 #endif |
335 | |
336 /* | |
337 * fill block0 struct and write page 0 | |
338 */ | |
339 if ((hp = mf_new(mfp, FALSE, 1)) == NULL) | |
340 goto error; | |
341 if (hp->bh_bnum != 0) | |
342 { | |
343 EMSG(_("E298: Didn't get block nr 0?")); | |
344 goto error; | |
345 } | |
346 b0p = (ZERO_BL *)(hp->bh_data); | |
347 | |
348 b0p->b0_id[0] = BLOCK0_ID0; | |
349 b0p->b0_id[1] = BLOCK0_ID1; | |
350 b0p->b0_magic_long = (long)B0_MAGIC_LONG; | |
351 b0p->b0_magic_int = (int)B0_MAGIC_INT; | |
352 b0p->b0_magic_short = (short)B0_MAGIC_SHORT; | |
353 b0p->b0_magic_char = B0_MAGIC_CHAR; | |
354 STRNCPY(b0p->b0_version, "VIM ", 4); | |
355 STRNCPY(b0p->b0_version + 4, Version, 6); | |
356 long_to_char((long)mfp->mf_page_size, b0p->b0_page_size); | |
625 | 357 |
800 | 358 #ifdef FEAT_SPELL |
359 if (!buf->b_spell) | |
360 #endif | |
625 | 361 { |
362 b0p->b0_dirty = buf->b_changed ? B0_DIRTY : 0; | |
363 b0p->b0_flags = get_fileformat(buf) + 1; | |
364 set_b0_fname(b0p, buf); | |
365 (void)get_user_name(b0p->b0_uname, B0_UNAME_SIZE); | |
366 b0p->b0_uname[B0_UNAME_SIZE - 1] = NUL; | |
367 mch_get_host_name(b0p->b0_hname, B0_HNAME_SIZE); | |
368 b0p->b0_hname[B0_HNAME_SIZE - 1] = NUL; | |
369 long_to_char(mch_get_pid(), b0p->b0_pid); | |
2267 | 370 #ifdef FEAT_CRYPT |
6122 | 371 ml_set_b0_crypt(buf, b0p); |
2267 | 372 #endif |
625 | 373 } |
7 | 374 |
375 /* | |
376 * Always sync block number 0 to disk, so we can check the file name in | |
2267 | 377 * the swap file in findswapname(). Don't do this for a help files or |
378 * a spell buffer though. | |
7 | 379 * Only works when there's a swapfile, otherwise it's done when the file |
380 * is created. | |
381 */ | |
382 mf_put(mfp, hp, TRUE, FALSE); | |
625 | 383 if (!buf->b_help && !B_SPELL(buf)) |
7 | 384 (void)mf_sync(mfp, 0); |
385 | |
625 | 386 /* |
387 * Fill in root pointer block and write page 1. | |
388 */ | |
7 | 389 if ((hp = ml_new_ptr(mfp)) == NULL) |
390 goto error; | |
391 if (hp->bh_bnum != 1) | |
392 { | |
393 EMSG(_("E298: Didn't get block nr 1?")); | |
394 goto error; | |
395 } | |
396 pp = (PTR_BL *)(hp->bh_data); | |
397 pp->pb_count = 1; | |
398 pp->pb_pointer[0].pe_bnum = 2; | |
399 pp->pb_pointer[0].pe_page_count = 1; | |
400 pp->pb_pointer[0].pe_old_lnum = 1; | |
401 pp->pb_pointer[0].pe_line_count = 1; /* line count after insertion */ | |
402 mf_put(mfp, hp, TRUE, FALSE); | |
403 | |
625 | 404 /* |
405 * Allocate first data block and create an empty line 1. | |
406 */ | |
7 | 407 if ((hp = ml_new_data(mfp, FALSE, 1)) == NULL) |
408 goto error; | |
409 if (hp->bh_bnum != 2) | |
410 { | |
411 EMSG(_("E298: Didn't get block nr 2?")); | |
412 goto error; | |
413 } | |
414 | |
415 dp = (DATA_BL *)(hp->bh_data); | |
416 dp->db_index[0] = --dp->db_txt_start; /* at end of block */ | |
417 dp->db_free -= 1 + INDEX_SIZE; | |
418 dp->db_line_count = 1; | |
2003 | 419 *((char_u *)dp + dp->db_txt_start) = NUL; /* empty line */ |
7 | 420 |
421 return OK; | |
422 | |
423 error: | |
424 if (mfp != NULL) | |
425 { | |
426 if (hp) | |
427 mf_put(mfp, hp, FALSE, FALSE); | |
428 mf_close(mfp, TRUE); /* will also free(mfp->mf_fname) */ | |
429 } | |
625 | 430 buf->b_ml.ml_mfp = NULL; |
7 | 431 return FAIL; |
432 } | |
433 | |
2267 | 434 #if defined(FEAT_CRYPT) || defined(PROTO) |
435 /* | |
6130 | 436 * Prepare encryption for "buf" for the current key and method. |
437 */ | |
438 static void | |
7827
41789f16d6b2
commit https://github.com/vim/vim/commit/52ea13da0fe86df1abf34de52841e367035170c0
Christian Brabandt <cb@256bit.org>
parents:
7803
diff
changeset
|
439 ml_set_mfp_crypt(buf_T *buf) |
6130 | 440 { |
441 if (*buf->b_p_key != NUL) | |
442 { | |
443 int method_nr = crypt_get_method_nr(buf); | |
444 | |
445 if (method_nr > CRYPT_M_ZIP) | |
446 { | |
447 /* Generate a seed and store it in the memfile. */ | |
448 sha2_seed(buf->b_ml.ml_mfp->mf_seed, MF_SEED_LEN, NULL, 0); | |
449 } | |
450 } | |
451 } | |
452 | |
453 /* | |
2267 | 454 * Prepare encryption for "buf" with block 0 "b0p". |
455 */ | |
456 static void | |
7827
41789f16d6b2
commit https://github.com/vim/vim/commit/52ea13da0fe86df1abf34de52841e367035170c0
Christian Brabandt <cb@256bit.org>
parents:
7803
diff
changeset
|
457 ml_set_b0_crypt(buf_T *buf, ZERO_BL *b0p) |
2267 | 458 { |
459 if (*buf->b_p_key == NUL) | |
460 b0p->b0_id[1] = BLOCK0_ID1; | |
461 else | |
462 { | |
6122 | 463 int method_nr = crypt_get_method_nr(buf); |
464 | |
465 b0p->b0_id[1] = id1_codes[method_nr]; | |
466 if (method_nr > CRYPT_M_ZIP) | |
2267 | 467 { |
468 /* Generate a seed and store it in block 0 and in the memfile. */ | |
469 sha2_seed(&b0p->b0_seed, MF_SEED_LEN, NULL, 0); | |
470 mch_memmove(buf->b_ml.ml_mfp->mf_seed, &b0p->b0_seed, MF_SEED_LEN); | |
471 } | |
472 } | |
473 } | |
474 | |
475 /* | |
476 * Called after the crypt key or 'cryptmethod' was changed for "buf". | |
477 * Will apply this to the swapfile. | |
478 * "old_key" is the previous key. It is equal to buf->b_p_key when | |
479 * 'cryptmethod' is changed. | |
2360
d8e4b27cef80
Change 'cryptmethod' from a number to a string option. Make it global-local.
Bram Moolenaar <bram@vim.org>
parents:
2283
diff
changeset
|
480 * "old_cm" is the previous 'cryptmethod'. It is equal to the current |
d8e4b27cef80
Change 'cryptmethod' from a number to a string option. Make it global-local.
Bram Moolenaar <bram@vim.org>
parents:
2283
diff
changeset
|
481 * 'cryptmethod' when 'key' is changed. |
2267 | 482 */ |
483 void | |
7827
41789f16d6b2
commit https://github.com/vim/vim/commit/52ea13da0fe86df1abf34de52841e367035170c0
Christian Brabandt <cb@256bit.org>
parents:
7803
diff
changeset
|
484 ml_set_crypt_key( |
41789f16d6b2
commit https://github.com/vim/vim/commit/52ea13da0fe86df1abf34de52841e367035170c0
Christian Brabandt <cb@256bit.org>
parents:
7803
diff
changeset
|
485 buf_T *buf, |
41789f16d6b2
commit https://github.com/vim/vim/commit/52ea13da0fe86df1abf34de52841e367035170c0
Christian Brabandt <cb@256bit.org>
parents:
7803
diff
changeset
|
486 char_u *old_key, |
41789f16d6b2
commit https://github.com/vim/vim/commit/52ea13da0fe86df1abf34de52841e367035170c0
Christian Brabandt <cb@256bit.org>
parents:
7803
diff
changeset
|
487 char_u *old_cm) |
2267 | 488 { |
489 memfile_T *mfp = buf->b_ml.ml_mfp; | |
490 bhdr_T *hp; | |
491 int page_count; | |
492 int idx; | |
493 long error; | |
494 infoptr_T *ip; | |
495 PTR_BL *pp; | |
496 DATA_BL *dp; | |
497 blocknr_T bnum; | |
498 int top; | |
6817 | 499 int old_method; |
2267 | 500 |
2483
7901306c5a34
Fix: when setting crypt key seed was not updated when the swap file wasn't
Bram Moolenaar <bram@vim.org>
parents:
2408
diff
changeset
|
501 if (mfp == NULL) |
2267 | 502 return; /* no memfile yet, nothing to do */ |
6817 | 503 old_method = crypt_method_nr_from_name(old_cm); |
504 | |
505 /* First make sure the swapfile is in a consistent state, using the old | |
506 * key and method. */ | |
507 { | |
508 char_u *new_key = buf->b_p_key; | |
509 char_u *new_buf_cm = buf->b_p_cm; | |
510 | |
511 buf->b_p_key = old_key; | |
512 buf->b_p_cm = old_cm; | |
513 ml_preserve(buf, FALSE); | |
514 buf->b_p_key = new_key; | |
515 buf->b_p_cm = new_buf_cm; | |
516 } | |
2267 | 517 |
518 /* Set the key, method and seed to be used for reading, these must be the | |
519 * old values. */ | |
520 mfp->mf_old_key = old_key; | |
6817 | 521 mfp->mf_old_cm = old_method; |
522 if (old_method > 0 && *old_key != NUL) | |
2267 | 523 mch_memmove(mfp->mf_old_seed, mfp->mf_seed, MF_SEED_LEN); |
524 | |
525 /* Update block 0 with the crypt flag and may set a new seed. */ | |
526 ml_upd_block0(buf, UB_CRYPT); | |
527 | |
528 if (mfp->mf_infile_count > 2) | |
529 { | |
530 /* | |
531 * Need to read back all data blocks from disk, decrypt them with the | |
532 * old key/method and mark them to be written. The algorithm is | |
533 * similar to what happens in ml_recover(), but we skip negative block | |
534 * numbers. | |
535 */ | |
536 ml_flush_line(buf); /* flush buffered line */ | |
537 (void)ml_find_line(buf, (linenr_T)0, ML_FLUSH); /* flush locked block */ | |
538 | |
539 hp = NULL; | |
540 bnum = 1; /* start with block 1 */ | |
541 page_count = 1; /* which is 1 page */ | |
542 idx = 0; /* start with first index in block 1 */ | |
543 error = 0; | |
544 buf->b_ml.ml_stack_top = 0; | |
2273
7f09ce7b4126
Fix a memory leak in encryption. (Dominique Pelle)
Bram Moolenaar <bram@vim.org>
parents:
2272
diff
changeset
|
545 vim_free(buf->b_ml.ml_stack); |
2267 | 546 buf->b_ml.ml_stack = NULL; |
547 buf->b_ml.ml_stack_size = 0; /* no stack yet */ | |
548 | |
549 for ( ; !got_int; line_breakcheck()) | |
550 { | |
551 if (hp != NULL) | |
552 mf_put(mfp, hp, FALSE, FALSE); /* release previous block */ | |
553 | |
554 /* get the block (pointer or data) */ | |
555 if ((hp = mf_get(mfp, (blocknr_T)bnum, page_count)) == NULL) | |
556 { | |
557 if (bnum == 1) | |
558 break; | |
559 ++error; | |
560 } | |
561 else | |
562 { | |
563 pp = (PTR_BL *)(hp->bh_data); | |
564 if (pp->pb_id == PTR_ID) /* it is a pointer block */ | |
565 { | |
566 if (pp->pb_count == 0) | |
567 { | |
568 /* empty block? */ | |
569 ++error; | |
570 } | |
571 else if (idx < (int)pp->pb_count) /* go a block deeper */ | |
572 { | |
573 if (pp->pb_pointer[idx].pe_bnum < 0) | |
574 { | |
6817 | 575 /* Skip data block with negative block number. |
576 * Should not happen, because of the ml_preserve() | |
577 * above. Get same block again for next index. */ | |
578 ++idx; | |
2267 | 579 continue; |
580 } | |
581 | |
582 /* going one block deeper in the tree, new entry in | |
583 * stack */ | |
584 if ((top = ml_add_stack(buf)) < 0) | |
585 { | |
586 ++error; | |
587 break; /* out of memory */ | |
588 } | |
589 ip = &(buf->b_ml.ml_stack[top]); | |
590 ip->ip_bnum = bnum; | |
591 ip->ip_index = idx; | |
592 | |
593 bnum = pp->pb_pointer[idx].pe_bnum; | |
594 page_count = pp->pb_pointer[idx].pe_page_count; | |
6817 | 595 idx = 0; |
2267 | 596 continue; |
597 } | |
598 } | |
599 else /* not a pointer block */ | |
600 { | |
601 dp = (DATA_BL *)(hp->bh_data); | |
602 if (dp->db_id != DATA_ID) /* block id wrong */ | |
603 ++error; | |
604 else | |
605 { | |
606 /* It is a data block, need to write it back to disk. */ | |
607 mf_put(mfp, hp, TRUE, FALSE); | |
608 hp = NULL; | |
609 } | |
610 } | |
611 } | |
612 | |
613 if (buf->b_ml.ml_stack_top == 0) /* finished */ | |
614 break; | |
615 | |
616 /* go one block up in the tree */ | |
617 ip = &(buf->b_ml.ml_stack[--(buf->b_ml.ml_stack_top)]); | |
618 bnum = ip->ip_bnum; | |
619 idx = ip->ip_index + 1; /* go to next index */ | |
620 page_count = 1; | |
621 } | |
6817 | 622 if (hp != NULL) |
623 mf_put(mfp, hp, FALSE, FALSE); /* release previous block */ | |
2657 | 624 |
625 if (error > 0) | |
626 EMSG(_("E843: Error while updating swap file crypt")); | |
2267 | 627 } |
628 | |
629 mfp->mf_old_key = NULL; | |
630 } | |
631 #endif | |
632 | |
7 | 633 /* |
634 * ml_setname() is called when the file name of "buf" has been changed. | |
635 * It may rename the swap file. | |
636 */ | |
637 void | |
7827
41789f16d6b2
commit https://github.com/vim/vim/commit/52ea13da0fe86df1abf34de52841e367035170c0
Christian Brabandt <cb@256bit.org>
parents:
7803
diff
changeset
|
638 ml_setname(buf_T *buf) |
7 | 639 { |
640 int success = FALSE; | |
641 memfile_T *mfp; | |
642 char_u *fname; | |
643 char_u *dirp; | |
644 #if defined(MSDOS) || defined(MSWIN) | |
645 char_u *p; | |
646 #endif | |
647 | |
648 mfp = buf->b_ml.ml_mfp; | |
649 if (mfp->mf_fd < 0) /* there is no swap file yet */ | |
650 { | |
651 /* | |
652 * When 'updatecount' is 0 and 'noswapfile' there is no swap file. | |
653 * For help files we will make a swap file now. | |
654 */ | |
5737 | 655 if (p_uc != 0 && !cmdmod.noswapfile) |
7 | 656 ml_open_file(buf); /* create a swap file */ |
657 return; | |
658 } | |
659 | |
660 /* | |
661 * Try all directories in the 'directory' option. | |
662 */ | |
663 dirp = p_dir; | |
664 for (;;) | |
665 { | |
666 if (*dirp == NUL) /* tried all directories, fail */ | |
667 break; | |
43 | 668 fname = findswapname(buf, &dirp, mfp->mf_fname); |
669 /* alloc's fname */ | |
3158 | 670 if (dirp == NULL) /* out of memory */ |
671 break; | |
7 | 672 if (fname == NULL) /* no file name found for this dir */ |
673 continue; | |
674 | |
675 #if defined(MSDOS) || defined(MSWIN) | |
676 /* | |
677 * Set full pathname for swap file now, because a ":!cd dir" may | |
678 * change directory without us knowing it. | |
679 */ | |
680 p = FullName_save(fname, FALSE); | |
681 vim_free(fname); | |
682 fname = p; | |
683 if (fname == NULL) | |
684 continue; | |
685 #endif | |
686 /* if the file name is the same we don't have to do anything */ | |
687 if (fnamecmp(fname, mfp->mf_fname) == 0) | |
688 { | |
689 vim_free(fname); | |
690 success = TRUE; | |
691 break; | |
692 } | |
693 /* need to close the swap file before renaming */ | |
694 if (mfp->mf_fd >= 0) | |
695 { | |
696 close(mfp->mf_fd); | |
697 mfp->mf_fd = -1; | |
698 } | |
699 | |
700 /* try to rename the swap file */ | |
701 if (vim_rename(mfp->mf_fname, fname) == 0) | |
702 { | |
703 success = TRUE; | |
704 vim_free(mfp->mf_fname); | |
705 mfp->mf_fname = fname; | |
706 vim_free(mfp->mf_ffname); | |
707 #if defined(MSDOS) || defined(MSWIN) | |
708 mfp->mf_ffname = NULL; /* mf_fname is full pathname already */ | |
709 #else | |
710 mf_set_ffname(mfp); | |
711 #endif | |
2267 | 712 ml_upd_block0(buf, UB_SAME_DIR); |
7 | 713 break; |
714 } | |
715 vim_free(fname); /* this fname didn't work, try another */ | |
716 } | |
717 | |
718 if (mfp->mf_fd == -1) /* need to (re)open the swap file */ | |
719 { | |
720 mfp->mf_fd = mch_open((char *)mfp->mf_fname, O_RDWR | O_EXTRA, 0); | |
721 if (mfp->mf_fd < 0) | |
722 { | |
723 /* could not (re)open the swap file, what can we do???? */ | |
724 EMSG(_("E301: Oops, lost the swap file!!!")); | |
725 return; | |
726 } | |
2003 | 727 #ifdef HAVE_FD_CLOEXEC |
728 { | |
729 int fdflags = fcntl(mfp->mf_fd, F_GETFD); | |
730 if (fdflags >= 0 && (fdflags & FD_CLOEXEC) == 0) | |
7961
a7e58c6e4e9a
commit https://github.com/vim/vim/commit/fbc4b4db3a9690906a96e16724350a6241cf32a5
Christian Brabandt <cb@256bit.org>
parents:
7881
diff
changeset
|
731 (void)fcntl(mfp->mf_fd, F_SETFD, fdflags | FD_CLOEXEC); |
2003 | 732 } |
733 #endif | |
7 | 734 } |
735 if (!success) | |
736 EMSG(_("E302: Could not rename swap file")); | |
737 } | |
738 | |
739 /* | |
740 * Open a file for the memfile for all buffers that are not readonly or have | |
741 * been modified. | |
742 * Used when 'updatecount' changes from zero to non-zero. | |
743 */ | |
744 void | |
7827
41789f16d6b2
commit https://github.com/vim/vim/commit/52ea13da0fe86df1abf34de52841e367035170c0
Christian Brabandt <cb@256bit.org>
parents:
7803
diff
changeset
|
745 ml_open_files(void) |
7 | 746 { |
747 buf_T *buf; | |
748 | |
749 for (buf = firstbuf; buf != NULL; buf = buf->b_next) | |
750 if (!buf->b_p_ro || buf->b_changed) | |
751 ml_open_file(buf); | |
752 } | |
753 | |
754 /* | |
755 * Open a swap file for an existing memfile, if there is no swap file yet. | |
756 * If we are unable to find a file name, mf_fname will be NULL | |
757 * and the memfile will be in memory only (no recovery possible). | |
758 */ | |
759 void | |
7827
41789f16d6b2
commit https://github.com/vim/vim/commit/52ea13da0fe86df1abf34de52841e367035170c0
Christian Brabandt <cb@256bit.org>
parents:
7803
diff
changeset
|
760 ml_open_file(buf_T *buf) |
7 | 761 { |
762 memfile_T *mfp; | |
763 char_u *fname; | |
764 char_u *dirp; | |
765 | |
766 mfp = buf->b_ml.ml_mfp; | |
5737 | 767 if (mfp == NULL || mfp->mf_fd >= 0 || !buf->b_p_swf || cmdmod.noswapfile) |
7 | 768 return; /* nothing to do */ |
769 | |
748 | 770 #ifdef FEAT_SPELL |
625 | 771 /* For a spell buffer use a temp file name. */ |
772 if (buf->b_spell) | |
773 { | |
6721 | 774 fname = vim_tempname('s', FALSE); |
625 | 775 if (fname != NULL) |
776 (void)mf_open_file(mfp, fname); /* consumes fname! */ | |
777 buf->b_may_swap = FALSE; | |
778 return; | |
779 } | |
780 #endif | |
781 | |
7 | 782 /* |
783 * Try all directories in 'directory' option. | |
784 */ | |
785 dirp = p_dir; | |
786 for (;;) | |
787 { | |
788 if (*dirp == NUL) | |
789 break; | |
2273
7f09ce7b4126
Fix a memory leak in encryption. (Dominique Pelle)
Bram Moolenaar <bram@vim.org>
parents:
2272
diff
changeset
|
790 /* There is a small chance that between choosing the swap file name |
7f09ce7b4126
Fix a memory leak in encryption. (Dominique Pelle)
Bram Moolenaar <bram@vim.org>
parents:
2272
diff
changeset
|
791 * and creating it, another Vim creates the file. In that case the |
7 | 792 * creation will fail and we will use another directory. */ |
43 | 793 fname = findswapname(buf, &dirp, NULL); /* allocates fname */ |
3158 | 794 if (dirp == NULL) |
795 break; /* out of memory */ | |
7 | 796 if (fname == NULL) |
797 continue; | |
798 if (mf_open_file(mfp, fname) == OK) /* consumes fname! */ | |
799 { | |
2823 | 800 #if defined(MSDOS) || defined(MSWIN) |
7 | 801 /* |
802 * set full pathname for swap file now, because a ":!cd dir" may | |
803 * change directory without us knowing it. | |
804 */ | |
805 mf_fullname(mfp); | |
806 #endif | |
2267 | 807 ml_upd_block0(buf, UB_SAME_DIR); |
39 | 808 |
7 | 809 /* Flush block zero, so others can read it */ |
810 if (mf_sync(mfp, MFS_ZERO) == OK) | |
630 | 811 { |
812 /* Mark all blocks that should be in the swapfile as dirty. | |
813 * Needed for when the 'swapfile' option was reset, so that | |
814 * the swap file was deleted, and then on again. */ | |
815 mf_set_dirty(mfp); | |
7 | 816 break; |
630 | 817 } |
7 | 818 /* Writing block 0 failed: close the file and try another dir */ |
819 mf_close_file(buf, FALSE); | |
820 } | |
821 } | |
822 | |
823 if (mfp->mf_fname == NULL) /* Failed! */ | |
824 { | |
825 need_wait_return = TRUE; /* call wait_return later */ | |
826 ++no_wait_return; | |
827 (void)EMSG2(_("E303: Unable to open swap file for \"%s\", recovery impossible"), | |
3839 | 828 buf_spname(buf) != NULL ? buf_spname(buf) : buf->b_fname); |
7 | 829 --no_wait_return; |
830 } | |
831 | |
832 /* don't try to open a swap file again */ | |
833 buf->b_may_swap = FALSE; | |
834 } | |
835 | |
836 /* | |
837 * If still need to create a swap file, and starting to edit a not-readonly | |
838 * file, or reading into an existing buffer, create a swap file now. | |
839 */ | |
840 void | |
7827
41789f16d6b2
commit https://github.com/vim/vim/commit/52ea13da0fe86df1abf34de52841e367035170c0
Christian Brabandt <cb@256bit.org>
parents:
7803
diff
changeset
|
841 check_need_swap( |
41789f16d6b2
commit https://github.com/vim/vim/commit/52ea13da0fe86df1abf34de52841e367035170c0
Christian Brabandt <cb@256bit.org>
parents:
7803
diff
changeset
|
842 int newfile) /* reading file into new buffer */ |
7 | 843 { |
844 if (curbuf->b_may_swap && (!curbuf->b_p_ro || !newfile)) | |
845 ml_open_file(curbuf); | |
846 } | |
847 | |
848 /* | |
849 * Close memline for buffer 'buf'. | |
850 * If 'del_file' is TRUE, delete the swap file | |
851 */ | |
852 void | |
7827
41789f16d6b2
commit https://github.com/vim/vim/commit/52ea13da0fe86df1abf34de52841e367035170c0
Christian Brabandt <cb@256bit.org>
parents:
7803
diff
changeset
|
853 ml_close(buf_T *buf, int del_file) |
7 | 854 { |
855 if (buf->b_ml.ml_mfp == NULL) /* not open */ | |
856 return; | |
857 mf_close(buf->b_ml.ml_mfp, del_file); /* close the .swp file */ | |
858 if (buf->b_ml.ml_line_lnum != 0 && (buf->b_ml.ml_flags & ML_LINE_DIRTY)) | |
859 vim_free(buf->b_ml.ml_line_ptr); | |
860 vim_free(buf->b_ml.ml_stack); | |
861 #ifdef FEAT_BYTEOFF | |
862 vim_free(buf->b_ml.ml_chunksize); | |
863 buf->b_ml.ml_chunksize = NULL; | |
864 #endif | |
865 buf->b_ml.ml_mfp = NULL; | |
866 | |
867 /* Reset the "recovered" flag, give the ATTENTION prompt the next time | |
868 * this buffer is loaded. */ | |
869 buf->b_flags &= ~BF_RECOVERED; | |
870 } | |
871 | |
872 /* | |
873 * Close all existing memlines and memfiles. | |
874 * Only used when exiting. | |
875 * When 'del_file' is TRUE, delete the memfiles. | |
165 | 876 * But don't delete files that were ":preserve"d when we are POSIX compatible. |
7 | 877 */ |
878 void | |
7827
41789f16d6b2
commit https://github.com/vim/vim/commit/52ea13da0fe86df1abf34de52841e367035170c0
Christian Brabandt <cb@256bit.org>
parents:
7803
diff
changeset
|
879 ml_close_all(int del_file) |
7 | 880 { |
881 buf_T *buf; | |
882 | |
883 for (buf = firstbuf; buf != NULL; buf = buf->b_next) | |
165 | 884 ml_close(buf, del_file && ((buf->b_flags & BF_PRESERVED) == 0 |
885 || vim_strchr(p_cpo, CPO_PRESERVE) == NULL)); | |
5519 | 886 #ifdef FEAT_SPELL |
887 spell_delete_wordlist(); /* delete the internal wordlist */ | |
888 #endif | |
7 | 889 #ifdef TEMPDIRNAMES |
5519 | 890 vim_deltempdir(); /* delete created temp directory */ |
7 | 891 #endif |
892 } | |
893 | |
894 /* | |
895 * Close all memfiles for not modified buffers. | |
896 * Only use just before exiting! | |
897 */ | |
898 void | |
7827
41789f16d6b2
commit https://github.com/vim/vim/commit/52ea13da0fe86df1abf34de52841e367035170c0
Christian Brabandt <cb@256bit.org>
parents:
7803
diff
changeset
|
899 ml_close_notmod(void) |
7 | 900 { |
901 buf_T *buf; | |
902 | |
903 for (buf = firstbuf; buf != NULL; buf = buf->b_next) | |
904 if (!bufIsChanged(buf)) | |
905 ml_close(buf, TRUE); /* close all not-modified buffers */ | |
906 } | |
907 | |
908 /* | |
909 * Update the timestamp in the .swp file. | |
910 * Used when the file has been written. | |
911 */ | |
912 void | |
7827
41789f16d6b2
commit https://github.com/vim/vim/commit/52ea13da0fe86df1abf34de52841e367035170c0
Christian Brabandt <cb@256bit.org>
parents:
7803
diff
changeset
|
913 ml_timestamp(buf_T *buf) |
7 | 914 { |
2267 | 915 ml_upd_block0(buf, UB_FNAME); |
916 } | |
917 | |
918 /* | |
919 * Return FAIL when the ID of "b0p" is wrong. | |
920 */ | |
921 static int | |
7827
41789f16d6b2
commit https://github.com/vim/vim/commit/52ea13da0fe86df1abf34de52841e367035170c0
Christian Brabandt <cb@256bit.org>
parents:
7803
diff
changeset
|
922 ml_check_b0_id(ZERO_BL *b0p) |
2267 | 923 { |
924 if (b0p->b0_id[0] != BLOCK0_ID0 | |
925 || (b0p->b0_id[1] != BLOCK0_ID1 | |
926 && b0p->b0_id[1] != BLOCK0_ID1_C0 | |
6122 | 927 && b0p->b0_id[1] != BLOCK0_ID1_C1 |
928 && b0p->b0_id[1] != BLOCK0_ID1_C2) | |
2267 | 929 ) |
930 return FAIL; | |
931 return OK; | |
39 | 932 } |
933 | |
934 /* | |
935 * Update the timestamp or the B0_SAME_DIR flag of the .swp file. | |
936 */ | |
937 static void | |
7827
41789f16d6b2
commit https://github.com/vim/vim/commit/52ea13da0fe86df1abf34de52841e367035170c0
Christian Brabandt <cb@256bit.org>
parents:
7803
diff
changeset
|
938 ml_upd_block0(buf_T *buf, upd_block0_T what) |
39 | 939 { |
7 | 940 memfile_T *mfp; |
941 bhdr_T *hp; | |
942 ZERO_BL *b0p; | |
943 | |
944 mfp = buf->b_ml.ml_mfp; | |
6130 | 945 if (mfp == NULL) |
7 | 946 return; |
6130 | 947 hp = mf_get(mfp, (blocknr_T)0, 1); |
948 if (hp == NULL) | |
949 { | |
950 #ifdef FEAT_CRYPT | |
951 /* Possibly update the seed in the memfile before there is a block0. */ | |
952 if (what == UB_CRYPT) | |
953 ml_set_mfp_crypt(buf); | |
954 #endif | |
955 return; | |
956 } | |
957 | |
7 | 958 b0p = (ZERO_BL *)(hp->bh_data); |
2267 | 959 if (ml_check_b0_id(b0p) == FAIL) |
39 | 960 EMSG(_("E304: ml_upd_block0(): Didn't get block 0??")); |
7 | 961 else |
39 | 962 { |
2267 | 963 if (what == UB_FNAME) |
39 | 964 set_b0_fname(b0p, buf); |
2267 | 965 #ifdef FEAT_CRYPT |
966 else if (what == UB_CRYPT) | |
967 ml_set_b0_crypt(buf, b0p); | |
968 #endif | |
969 else /* what == UB_SAME_DIR */ | |
39 | 970 set_b0_dir_flag(b0p, buf); |
971 } | |
7 | 972 mf_put(mfp, hp, TRUE, FALSE); |
973 } | |
974 | |
975 /* | |
976 * Write file name and timestamp into block 0 of a swap file. | |
977 * Also set buf->b_mtime. | |
978 * Don't use NameBuff[]!!! | |
979 */ | |
980 static void | |
7827
41789f16d6b2
commit https://github.com/vim/vim/commit/52ea13da0fe86df1abf34de52841e367035170c0
Christian Brabandt <cb@256bit.org>
parents:
7803
diff
changeset
|
981 set_b0_fname(ZERO_BL *b0p, buf_T *buf) |
7 | 982 { |
983 struct stat st; | |
984 | |
985 if (buf->b_ffname == NULL) | |
986 b0p->b0_fname[0] = NUL; | |
987 else | |
988 { | |
2823 | 989 #if defined(MSDOS) || defined(MSWIN) || defined(AMIGA) |
39 | 990 /* Systems that cannot translate "~user" back into a path: copy the |
991 * file name unmodified. Do use slashes instead of backslashes for | |
992 * portability. */ | |
2267 | 993 vim_strncpy(b0p->b0_fname, buf->b_ffname, B0_FNAME_SIZE_CRYPT - 1); |
39 | 994 # ifdef BACKSLASH_IN_FILENAME |
995 forward_slash(b0p->b0_fname); | |
996 # endif | |
7 | 997 #else |
998 size_t flen, ulen; | |
999 char_u uname[B0_UNAME_SIZE]; | |
1000 | |
1001 /* | |
1002 * For a file under the home directory of the current user, we try to | |
1003 * replace the home directory path with "~user". This helps when | |
1004 * editing the same file on different machines over a network. | |
1005 * First replace home dir path with "~/" with home_replace(). | |
1006 * Then insert the user name to get "~user/". | |
1007 */ | |
2267 | 1008 home_replace(NULL, buf->b_ffname, b0p->b0_fname, |
1009 B0_FNAME_SIZE_CRYPT, TRUE); | |
7 | 1010 if (b0p->b0_fname[0] == '~') |
1011 { | |
1012 flen = STRLEN(b0p->b0_fname); | |
1013 /* If there is no user name or it is too long, don't use "~/" */ | |
1014 if (get_user_name(uname, B0_UNAME_SIZE) == FAIL | |
2267 | 1015 || (ulen = STRLEN(uname)) + flen > B0_FNAME_SIZE_CRYPT - 1) |
1016 vim_strncpy(b0p->b0_fname, buf->b_ffname, | |
1017 B0_FNAME_SIZE_CRYPT - 1); | |
7 | 1018 else |
1019 { | |
1020 mch_memmove(b0p->b0_fname + ulen + 1, b0p->b0_fname + 1, flen); | |
1021 mch_memmove(b0p->b0_fname + 1, uname, ulen); | |
1022 } | |
1023 } | |
1024 #endif | |
1025 if (mch_stat((char *)buf->b_ffname, &st) >= 0) | |
1026 { | |
1027 long_to_char((long)st.st_mtime, b0p->b0_mtime); | |
1028 #ifdef CHECK_INODE | |
1029 long_to_char((long)st.st_ino, b0p->b0_ino); | |
1030 #endif | |
1031 buf_store_time(buf, &st, buf->b_ffname); | |
1032 buf->b_mtime_read = buf->b_mtime; | |
1033 } | |
1034 else | |
1035 { | |
1036 long_to_char(0L, b0p->b0_mtime); | |
1037 #ifdef CHECK_INODE | |
1038 long_to_char(0L, b0p->b0_ino); | |
1039 #endif | |
1040 buf->b_mtime = 0; | |
1041 buf->b_mtime_read = 0; | |
1042 buf->b_orig_size = 0; | |
1043 buf->b_orig_mode = 0; | |
1044 } | |
1045 } | |
39 | 1046 |
1047 #ifdef FEAT_MBYTE | |
1048 /* Also add the 'fileencoding' if there is room. */ | |
1049 add_b0_fenc(b0p, curbuf); | |
1050 #endif | |
7 | 1051 } |
1052 | |
1053 /* | |
39 | 1054 * Update the B0_SAME_DIR flag of the swap file. It's set if the file and the |
1055 * swapfile for "buf" are in the same directory. | |
1056 * This is fail safe: if we are not sure the directories are equal the flag is | |
1057 * not set. | |
1058 */ | |
1059 static void | |
7827
41789f16d6b2
commit https://github.com/vim/vim/commit/52ea13da0fe86df1abf34de52841e367035170c0
Christian Brabandt <cb@256bit.org>
parents:
7803
diff
changeset
|
1060 set_b0_dir_flag(ZERO_BL *b0p, buf_T *buf) |
39 | 1061 { |
1062 if (same_directory(buf->b_ml.ml_mfp->mf_fname, buf->b_ffname)) | |
1063 b0p->b0_flags |= B0_SAME_DIR; | |
1064 else | |
1065 b0p->b0_flags &= ~B0_SAME_DIR; | |
1066 } | |
1067 | |
1068 #ifdef FEAT_MBYTE | |
1069 /* | |
1070 * When there is room, add the 'fileencoding' to block zero. | |
1071 */ | |
1072 static void | |
7827
41789f16d6b2
commit https://github.com/vim/vim/commit/52ea13da0fe86df1abf34de52841e367035170c0
Christian Brabandt <cb@256bit.org>
parents:
7803
diff
changeset
|
1073 add_b0_fenc( |
41789f16d6b2
commit https://github.com/vim/vim/commit/52ea13da0fe86df1abf34de52841e367035170c0
Christian Brabandt <cb@256bit.org>
parents:
7803
diff
changeset
|
1074 ZERO_BL *b0p, |
41789f16d6b2
commit https://github.com/vim/vim/commit/52ea13da0fe86df1abf34de52841e367035170c0
Christian Brabandt <cb@256bit.org>
parents:
7803
diff
changeset
|
1075 buf_T *buf) |
39 | 1076 { |
1077 int n; | |
2267 | 1078 int size = B0_FNAME_SIZE_NOCRYPT; |
1079 | |
1080 # ifdef FEAT_CRYPT | |
1081 /* Without encryption use the same offset as in Vim 7.2 to be compatible. | |
1082 * With encryption it's OK to move elsewhere, the swap file is not | |
1083 * compatible anyway. */ | |
1084 if (*buf->b_p_key != NUL) | |
1085 size = B0_FNAME_SIZE_CRYPT; | |
1086 # endif | |
39 | 1087 |
835 | 1088 n = (int)STRLEN(buf->b_p_fenc); |
2267 | 1089 if ((int)STRLEN(b0p->b0_fname) + n + 1 > size) |
39 | 1090 b0p->b0_flags &= ~B0_HAS_FENC; |
1091 else | |
1092 { | |
2267 | 1093 mch_memmove((char *)b0p->b0_fname + size - n, |
39 | 1094 (char *)buf->b_p_fenc, (size_t)n); |
2267 | 1095 *(b0p->b0_fname + size - n - 1) = NUL; |
39 | 1096 b0p->b0_flags |= B0_HAS_FENC; |
1097 } | |
1098 } | |
1099 #endif | |
1100 | |
1101 | |
1102 /* | |
2267 | 1103 * Try to recover curbuf from the .swp file. |
7 | 1104 */ |
1105 void | |
7827
41789f16d6b2
commit https://github.com/vim/vim/commit/52ea13da0fe86df1abf34de52841e367035170c0
Christian Brabandt <cb@256bit.org>
parents:
7803
diff
changeset
|
1106 ml_recover(void) |
7 | 1107 { |
1108 buf_T *buf = NULL; | |
1109 memfile_T *mfp = NULL; | |
1110 char_u *fname; | |
2267 | 1111 char_u *fname_used = NULL; |
7 | 1112 bhdr_T *hp = NULL; |
1113 ZERO_BL *b0p; | |
39 | 1114 int b0_ff; |
1115 char_u *b0_fenc = NULL; | |
2267 | 1116 #ifdef FEAT_CRYPT |
1117 int b0_cm = -1; | |
1118 #endif | |
7 | 1119 PTR_BL *pp; |
1120 DATA_BL *dp; | |
1121 infoptr_T *ip; | |
1122 blocknr_T bnum; | |
1123 int page_count; | |
1124 struct stat org_stat, swp_stat; | |
1125 int len; | |
1126 int directly; | |
1127 linenr_T lnum; | |
1128 char_u *p; | |
1129 int i; | |
1130 long error; | |
1131 int cannot_open; | |
1132 linenr_T line_count; | |
1133 int has_error; | |
1134 int idx; | |
1135 int top; | |
1136 int txt_start; | |
1137 off_t size; | |
1138 int called_from_main; | |
1139 int serious_error = TRUE; | |
1140 long mtime; | |
1141 int attr; | |
2162
0527eb0f6918
After recovery check if the text changed. If it did mark the buffer as
Bram Moolenaar <bram@vim.org>
parents:
2145
diff
changeset
|
1142 int orig_file_status = NOTDONE; |
7 | 1143 |
1144 recoverymode = TRUE; | |
1145 called_from_main = (curbuf->b_ml.ml_mfp == NULL); | |
1146 attr = hl_attr(HLF_E); | |
1975 | 1147 |
1148 /* | |
1149 * If the file name ends in ".s[uvw][a-z]" we assume this is the swap file. | |
1150 * Otherwise a search is done to find the swap file(s). | |
1151 */ | |
7 | 1152 fname = curbuf->b_fname; |
1153 if (fname == NULL) /* When there is no file name */ | |
1154 fname = (char_u *)""; | |
1155 len = (int)STRLEN(fname); | |
1156 if (len >= 4 && | |
2823 | 1157 #if defined(VMS) |
1975 | 1158 STRNICMP(fname + len - 4, "_s" , 2) |
7 | 1159 #else |
1975 | 1160 STRNICMP(fname + len - 4, ".s" , 2) |
7 | 1161 #endif |
1975 | 1162 == 0 |
1163 && vim_strchr((char_u *)"UVWuvw", fname[len - 2]) != NULL | |
1164 && ASCII_ISALPHA(fname[len - 1])) | |
7 | 1165 { |
1166 directly = TRUE; | |
2267 | 1167 fname_used = vim_strsave(fname); /* make a copy for mf_open() */ |
7 | 1168 } |
1169 else | |
1170 { | |
1171 directly = FALSE; | |
1172 | |
1173 /* count the number of matching swap files */ | |
2267 | 1174 len = recover_names(fname, FALSE, 0, NULL); |
7 | 1175 if (len == 0) /* no swap files found */ |
1176 { | |
1177 EMSG2(_("E305: No swap file found for %s"), fname); | |
1178 goto theend; | |
1179 } | |
1180 if (len == 1) /* one swap file found, use it */ | |
1181 i = 1; | |
1182 else /* several swap files found, choose */ | |
1183 { | |
1184 /* list the names of the swap files */ | |
2267 | 1185 (void)recover_names(fname, TRUE, 0, NULL); |
7 | 1186 msg_putchar('\n'); |
1187 MSG_PUTS(_("Enter number of swap file to use (0 to quit): ")); | |
374 | 1188 i = get_number(FALSE, NULL); |
7 | 1189 if (i < 1 || i > len) |
1190 goto theend; | |
1191 } | |
1192 /* get the swap file name that will be used */ | |
2267 | 1193 (void)recover_names(fname, FALSE, i, &fname_used); |
7 | 1194 } |
2267 | 1195 if (fname_used == NULL) |
7 | 1196 goto theend; /* out of memory */ |
1197 | |
1198 /* When called from main() still need to initialize storage structure */ | |
625 | 1199 if (called_from_main && ml_open(curbuf) == FAIL) |
7 | 1200 getout(1); |
1201 | |
2267 | 1202 /* |
1203 * Allocate a buffer structure for the swap file that is used for recovery. | |
2407
6bc102a4bff8
Fix memory access to 'cryptmethod' during recovery. (Dominique Pelle)
Bram Moolenaar <bram@vim.org>
parents:
2394
diff
changeset
|
1204 * Only the memline and crypt information in it are really used. |
2267 | 1205 */ |
7 | 1206 buf = (buf_T *)alloc((unsigned)sizeof(buf_T)); |
1207 if (buf == NULL) | |
1208 goto theend; | |
2267 | 1209 |
1210 /* | |
1211 * init fields in memline struct | |
1212 */ | |
7 | 1213 buf->b_ml.ml_stack_size = 0; /* no stack yet */ |
1214 buf->b_ml.ml_stack = NULL; /* no stack yet */ | |
1215 buf->b_ml.ml_stack_top = 0; /* nothing in the stack */ | |
1216 buf->b_ml.ml_line_lnum = 0; /* no cached line */ | |
1217 buf->b_ml.ml_locked = NULL; /* no locked block */ | |
1218 buf->b_ml.ml_flags = 0; | |
2408
9e2e63af1641
Better fix for memory access in recovery. (Dominique Pelle)
Bram Moolenaar <bram@vim.org>
parents:
2407
diff
changeset
|
1219 #ifdef FEAT_CRYPT |
9e2e63af1641
Better fix for memory access in recovery. (Dominique Pelle)
Bram Moolenaar <bram@vim.org>
parents:
2407
diff
changeset
|
1220 buf->b_p_key = empty_option; |
9e2e63af1641
Better fix for memory access in recovery. (Dominique Pelle)
Bram Moolenaar <bram@vim.org>
parents:
2407
diff
changeset
|
1221 buf->b_p_cm = empty_option; |
9e2e63af1641
Better fix for memory access in recovery. (Dominique Pelle)
Bram Moolenaar <bram@vim.org>
parents:
2407
diff
changeset
|
1222 #endif |
7 | 1223 |
2267 | 1224 /* |
1225 * open the memfile from the old swap file | |
1226 */ | |
1227 p = vim_strsave(fname_used); /* save "fname_used" for the message: | |
1228 mf_open() will consume "fname_used"! */ | |
1229 mfp = mf_open(fname_used, O_RDONLY); | |
1230 fname_used = p; | |
7 | 1231 if (mfp == NULL || mfp->mf_fd < 0) |
1232 { | |
2267 | 1233 if (fname_used != NULL) |
1234 EMSG2(_("E306: Cannot open %s"), fname_used); | |
7 | 1235 goto theend; |
1236 } | |
1237 buf->b_ml.ml_mfp = mfp; | |
2267 | 1238 #ifdef FEAT_CRYPT |
1239 mfp->mf_buffer = buf; | |
1240 #endif | |
7 | 1241 |
1242 /* | |
1243 * The page size set in mf_open() might be different from the page size | |
1244 * used in the swap file, we must get it from block 0. But to read block | |
1245 * 0 we need a page size. Use the minimal size for block 0 here, it will | |
1246 * be set to the real value below. | |
1247 */ | |
1248 mfp->mf_page_size = MIN_SWAP_PAGE_SIZE; | |
1249 | |
2267 | 1250 /* |
1251 * try to read block 0 | |
1252 */ | |
7 | 1253 if ((hp = mf_get(mfp, (blocknr_T)0, 1)) == NULL) |
1254 { | |
1255 msg_start(); | |
1256 MSG_PUTS_ATTR(_("Unable to read block 0 from "), attr | MSG_HIST); | |
1257 msg_outtrans_attr(mfp->mf_fname, attr | MSG_HIST); | |
2267 | 1258 MSG_PUTS_ATTR(_("\nMaybe no changes were made or Vim did not update the swap file."), |
7 | 1259 attr | MSG_HIST); |
1260 msg_end(); | |
1261 goto theend; | |
1262 } | |
1263 b0p = (ZERO_BL *)(hp->bh_data); | |
1264 if (STRNCMP(b0p->b0_version, "VIM 3.0", 7) == 0) | |
1265 { | |
1266 msg_start(); | |
1267 msg_outtrans_attr(mfp->mf_fname, MSG_HIST); | |
1268 MSG_PUTS_ATTR(_(" cannot be used with this version of Vim.\n"), | |
1269 MSG_HIST); | |
1270 MSG_PUTS_ATTR(_("Use Vim version 3.0.\n"), MSG_HIST); | |
1271 msg_end(); | |
1272 goto theend; | |
1273 } | |
2267 | 1274 if (ml_check_b0_id(b0p) == FAIL) |
7 | 1275 { |
1276 EMSG2(_("E307: %s does not look like a Vim swap file"), mfp->mf_fname); | |
1277 goto theend; | |
1278 } | |
1279 if (b0_magic_wrong(b0p)) | |
1280 { | |
1281 msg_start(); | |
1282 msg_outtrans_attr(mfp->mf_fname, attr | MSG_HIST); | |
1283 #if defined(MSDOS) || defined(MSWIN) | |
1284 if (STRNCMP(b0p->b0_hname, "PC ", 3) == 0) | |
1285 MSG_PUTS_ATTR(_(" cannot be used with this version of Vim.\n"), | |
1286 attr | MSG_HIST); | |
1287 else | |
1288 #endif | |
1289 MSG_PUTS_ATTR(_(" cannot be used on this computer.\n"), | |
1290 attr | MSG_HIST); | |
1291 MSG_PUTS_ATTR(_("The file was created on "), attr | MSG_HIST); | |
2273
7f09ce7b4126
Fix a memory leak in encryption. (Dominique Pelle)
Bram Moolenaar <bram@vim.org>
parents:
2272
diff
changeset
|
1292 /* avoid going past the end of a corrupted hostname */ |
7 | 1293 b0p->b0_fname[0] = NUL; |
1294 MSG_PUTS_ATTR(b0p->b0_hname, attr | MSG_HIST); | |
1295 MSG_PUTS_ATTR(_(",\nor the file has been damaged."), attr | MSG_HIST); | |
1296 msg_end(); | |
1297 goto theend; | |
1298 } | |
1105 | 1299 |
2267 | 1300 #ifdef FEAT_CRYPT |
6122 | 1301 for (i = 0; i < (int)(sizeof(id1_codes) / sizeof(int)); ++i) |
1302 if (id1_codes[i] == b0p->b0_id[1]) | |
1303 b0_cm = i; | |
1304 if (b0_cm > 0) | |
2267 | 1305 mch_memmove(mfp->mf_seed, &b0p->b0_seed, MF_SEED_LEN); |
6122 | 1306 crypt_set_cm_option(buf, b0_cm < 0 ? 0 : b0_cm); |
2267 | 1307 #else |
1308 if (b0p->b0_id[1] != BLOCK0_ID1) | |
1309 { | |
2283
7e1bd501306d
Mainly documentation updates.
Bram Moolenaar <bram@vim.org>
parents:
2273
diff
changeset
|
1310 EMSG2(_("E833: %s is encrypted and this version of Vim does not support encryption"), mfp->mf_fname); |
2267 | 1311 goto theend; |
1312 } | |
1313 #endif | |
1314 | |
7 | 1315 /* |
1316 * If we guessed the wrong page size, we have to recalculate the | |
1317 * highest block number in the file. | |
1318 */ | |
1319 if (mfp->mf_page_size != (unsigned)char_to_long(b0p->b0_page_size)) | |
1320 { | |
1105 | 1321 unsigned previous_page_size = mfp->mf_page_size; |
1322 | |
7 | 1323 mf_new_page_size(mfp, (unsigned)char_to_long(b0p->b0_page_size)); |
1105 | 1324 if (mfp->mf_page_size < previous_page_size) |
1325 { | |
1326 msg_start(); | |
1327 msg_outtrans_attr(mfp->mf_fname, attr | MSG_HIST); | |
1328 MSG_PUTS_ATTR(_(" has been damaged (page size is smaller than minimum value).\n"), | |
1329 attr | MSG_HIST); | |
1330 msg_end(); | |
1331 goto theend; | |
1332 } | |
7 | 1333 if ((size = lseek(mfp->mf_fd, (off_t)0L, SEEK_END)) <= 0) |
1334 mfp->mf_blocknr_max = 0; /* no file or empty file */ | |
1335 else | |
1336 mfp->mf_blocknr_max = (blocknr_T)(size / mfp->mf_page_size); | |
1337 mfp->mf_infile_count = mfp->mf_blocknr_max; | |
1105 | 1338 |
1339 /* need to reallocate the memory used to store the data */ | |
1340 p = alloc(mfp->mf_page_size); | |
1341 if (p == NULL) | |
1342 goto theend; | |
1343 mch_memmove(p, hp->bh_data, previous_page_size); | |
1344 vim_free(hp->bh_data); | |
1345 hp->bh_data = p; | |
1346 b0p = (ZERO_BL *)(hp->bh_data); | |
7 | 1347 } |
1348 | |
2267 | 1349 /* |
1350 * If .swp file name given directly, use name from swap file for buffer. | |
1351 */ | |
7 | 1352 if (directly) |
1353 { | |
1354 expand_env(b0p->b0_fname, NameBuff, MAXPATHL); | |
1355 if (setfname(curbuf, NameBuff, NULL, TRUE) == FAIL) | |
1356 goto theend; | |
1357 } | |
1358 | |
1359 home_replace(NULL, mfp->mf_fname, NameBuff, MAXPATHL, TRUE); | |
274 | 1360 smsg((char_u *)_("Using swap file \"%s\""), NameBuff); |
7 | 1361 |
1362 if (buf_spname(curbuf) != NULL) | |
3839 | 1363 vim_strncpy(NameBuff, buf_spname(curbuf), MAXPATHL - 1); |
7 | 1364 else |
1365 home_replace(NULL, curbuf->b_ffname, NameBuff, MAXPATHL, TRUE); | |
274 | 1366 smsg((char_u *)_("Original file \"%s\""), NameBuff); |
7 | 1367 msg_putchar('\n'); |
1368 | |
2267 | 1369 /* |
1370 * check date of swap file and original file | |
1371 */ | |
7 | 1372 mtime = char_to_long(b0p->b0_mtime); |
1373 if (curbuf->b_ffname != NULL | |
1374 && mch_stat((char *)curbuf->b_ffname, &org_stat) != -1 | |
1375 && ((mch_stat((char *)mfp->mf_fname, &swp_stat) != -1 | |
1376 && org_stat.st_mtime > swp_stat.st_mtime) | |
1377 || org_stat.st_mtime != mtime)) | |
1378 { | |
1379 EMSG(_("E308: Warning: Original file may have been changed")); | |
1380 } | |
1381 out_flush(); | |
39 | 1382 |
1383 /* Get the 'fileformat' and 'fileencoding' from block zero. */ | |
1384 b0_ff = (b0p->b0_flags & B0_FF_MASK); | |
1385 if (b0p->b0_flags & B0_HAS_FENC) | |
1386 { | |
2271
2b33a7678e7b
Fix compiler warnings for shadowed variables. Make 'conceal' a long instead
Bram Moolenaar <bram@vim.org>
parents:
2267
diff
changeset
|
1387 int fnsize = B0_FNAME_SIZE_NOCRYPT; |
2267 | 1388 |
1389 #ifdef FEAT_CRYPT | |
1390 /* Use the same size as in add_b0_fenc(). */ | |
1391 if (b0p->b0_id[1] != BLOCK0_ID1) | |
2271
2b33a7678e7b
Fix compiler warnings for shadowed variables. Make 'conceal' a long instead
Bram Moolenaar <bram@vim.org>
parents:
2267
diff
changeset
|
1392 fnsize = B0_FNAME_SIZE_CRYPT; |
2267 | 1393 #endif |
2271
2b33a7678e7b
Fix compiler warnings for shadowed variables. Make 'conceal' a long instead
Bram Moolenaar <bram@vim.org>
parents:
2267
diff
changeset
|
1394 for (p = b0p->b0_fname + fnsize; p > b0p->b0_fname && p[-1] != NUL; --p) |
39 | 1395 ; |
2271
2b33a7678e7b
Fix compiler warnings for shadowed variables. Make 'conceal' a long instead
Bram Moolenaar <bram@vim.org>
parents:
2267
diff
changeset
|
1396 b0_fenc = vim_strnsave(p, (int)(b0p->b0_fname + fnsize - p)); |
39 | 1397 } |
1398 | |
7 | 1399 mf_put(mfp, hp, FALSE, FALSE); /* release block 0 */ |
1400 hp = NULL; | |
1401 | |
1402 /* | |
1403 * Now that we are sure that the file is going to be recovered, clear the | |
1404 * contents of the current buffer. | |
1405 */ | |
1406 while (!(curbuf->b_ml.ml_flags & ML_EMPTY)) | |
1407 ml_delete((linenr_T)1, FALSE); | |
1408 | |
1409 /* | |
1410 * Try reading the original file to obtain the values of 'fileformat', | |
1411 * 'fileencoding', etc. Ignore errors. The text itself is not used. | |
2267 | 1412 * When the file is encrypted the user is asked to enter the key. |
7 | 1413 */ |
1414 if (curbuf->b_ffname != NULL) | |
2162
0527eb0f6918
After recovery check if the text changed. If it did mark the buffer as
Bram Moolenaar <bram@vim.org>
parents:
2145
diff
changeset
|
1415 orig_file_status = readfile(curbuf->b_ffname, NULL, (linenr_T)0, |
7 | 1416 (linenr_T)0, (linenr_T)MAXLNUM, NULL, READ_NEW); |
1417 | |
2267 | 1418 #ifdef FEAT_CRYPT |
1419 if (b0_cm >= 0) | |
1420 { | |
1421 /* Need to ask the user for the crypt key. If this fails we continue | |
1422 * without a key, will probably get garbage text. */ | |
1423 if (*curbuf->b_p_key != NUL) | |
1424 { | |
1425 smsg((char_u *)_("Swap file is encrypted: \"%s\""), fname_used); | |
1426 MSG_PUTS(_("\nIf you entered a new crypt key but did not write the text file,")); | |
1427 MSG_PUTS(_("\nenter the new crypt key.")); | |
1428 MSG_PUTS(_("\nIf you wrote the text file after changing the crypt key press enter")); | |
1429 MSG_PUTS(_("\nto use the same key for text file and swap file")); | |
1430 } | |
1431 else | |
1432 smsg((char_u *)_(need_key_msg), fname_used); | |
6122 | 1433 buf->b_p_key = crypt_get_key(FALSE, FALSE); |
2267 | 1434 if (buf->b_p_key == NULL) |
1435 buf->b_p_key = curbuf->b_p_key; | |
1436 else if (*buf->b_p_key == NUL) | |
1437 { | |
1438 vim_free(buf->b_p_key); | |
1439 buf->b_p_key = curbuf->b_p_key; | |
1440 } | |
1441 if (buf->b_p_key == NULL) | |
1442 buf->b_p_key = empty_option; | |
2165
733f0dc510c3
Undo changes that are meant for the Vim 7.3 branch.
Bram Moolenaar <bram@vim.org>
parents:
2162
diff
changeset
|
1443 } |
2267 | 1444 #endif |
7 | 1445 |
39 | 1446 /* Use the 'fileformat' and 'fileencoding' as stored in the swap file. */ |
1447 if (b0_ff != 0) | |
1448 set_fileformat(b0_ff - 1, OPT_LOCAL); | |
1449 if (b0_fenc != NULL) | |
1450 { | |
1451 set_option_value((char_u *)"fenc", 0L, b0_fenc, OPT_LOCAL); | |
1452 vim_free(b0_fenc); | |
1453 } | |
1454 unchanged(curbuf, TRUE); | |
1455 | |
7 | 1456 bnum = 1; /* start with block 1 */ |
1457 page_count = 1; /* which is 1 page */ | |
1458 lnum = 0; /* append after line 0 in curbuf */ | |
1459 line_count = 0; | |
1460 idx = 0; /* start with first index in block 1 */ | |
1461 error = 0; | |
1462 buf->b_ml.ml_stack_top = 0; | |
1463 buf->b_ml.ml_stack = NULL; | |
1464 buf->b_ml.ml_stack_size = 0; /* no stack yet */ | |
1465 | |
1466 if (curbuf->b_ffname == NULL) | |
1467 cannot_open = TRUE; | |
1468 else | |
1469 cannot_open = FALSE; | |
1470 | |
1471 serious_error = FALSE; | |
1472 for ( ; !got_int; line_breakcheck()) | |
1473 { | |
1474 if (hp != NULL) | |
1475 mf_put(mfp, hp, FALSE, FALSE); /* release previous block */ | |
1476 | |
1477 /* | |
1478 * get block | |
1479 */ | |
1480 if ((hp = mf_get(mfp, (blocknr_T)bnum, page_count)) == NULL) | |
1481 { | |
1482 if (bnum == 1) | |
1483 { | |
1484 EMSG2(_("E309: Unable to read block 1 from %s"), mfp->mf_fname); | |
1485 goto theend; | |
1486 } | |
1487 ++error; | |
1488 ml_append(lnum++, (char_u *)_("???MANY LINES MISSING"), | |
1489 (colnr_T)0, TRUE); | |
1490 } | |
1491 else /* there is a block */ | |
1492 { | |
1493 pp = (PTR_BL *)(hp->bh_data); | |
1494 if (pp->pb_id == PTR_ID) /* it is a pointer block */ | |
1495 { | |
1496 /* check line count when using pointer block first time */ | |
1497 if (idx == 0 && line_count != 0) | |
1498 { | |
1499 for (i = 0; i < (int)pp->pb_count; ++i) | |
1500 line_count -= pp->pb_pointer[i].pe_line_count; | |
1501 if (line_count != 0) | |
1502 { | |
1503 ++error; | |
1504 ml_append(lnum++, (char_u *)_("???LINE COUNT WRONG"), | |
1505 (colnr_T)0, TRUE); | |
1506 } | |
1507 } | |
1508 | |
1509 if (pp->pb_count == 0) | |
1510 { | |
1511 ml_append(lnum++, (char_u *)_("???EMPTY BLOCK"), | |
1512 (colnr_T)0, TRUE); | |
1513 ++error; | |
1514 } | |
1515 else if (idx < (int)pp->pb_count) /* go a block deeper */ | |
1516 { | |
1517 if (pp->pb_pointer[idx].pe_bnum < 0) | |
1518 { | |
1519 /* | |
1520 * Data block with negative block number. | |
1521 * Try to read lines from the original file. | |
1522 * This is slow, but it works. | |
1523 */ | |
1524 if (!cannot_open) | |
1525 { | |
1526 line_count = pp->pb_pointer[idx].pe_line_count; | |
1527 if (readfile(curbuf->b_ffname, NULL, lnum, | |
1528 pp->pb_pointer[idx].pe_old_lnum - 1, | |
1529 line_count, NULL, 0) == FAIL) | |
1530 cannot_open = TRUE; | |
1531 else | |
1532 lnum += line_count; | |
1533 } | |
1534 if (cannot_open) | |
1535 { | |
1536 ++error; | |
1537 ml_append(lnum++, (char_u *)_("???LINES MISSING"), | |
1538 (colnr_T)0, TRUE); | |
1539 } | |
1540 ++idx; /* get same block again for next index */ | |
1541 continue; | |
1542 } | |
1543 | |
1544 /* | |
1545 * going one block deeper in the tree | |
1546 */ | |
1547 if ((top = ml_add_stack(buf)) < 0) /* new entry in stack */ | |
1548 { | |
1549 ++error; | |
1550 break; /* out of memory */ | |
1551 } | |
1552 ip = &(buf->b_ml.ml_stack[top]); | |
1553 ip->ip_bnum = bnum; | |
1554 ip->ip_index = idx; | |
1555 | |
1556 bnum = pp->pb_pointer[idx].pe_bnum; | |
1557 line_count = pp->pb_pointer[idx].pe_line_count; | |
1558 page_count = pp->pb_pointer[idx].pe_page_count; | |
2885 | 1559 idx = 0; |
7 | 1560 continue; |
1561 } | |
1562 } | |
1563 else /* not a pointer block */ | |
1564 { | |
1565 dp = (DATA_BL *)(hp->bh_data); | |
1566 if (dp->db_id != DATA_ID) /* block id wrong */ | |
1567 { | |
1568 if (bnum == 1) | |
1569 { | |
1570 EMSG2(_("E310: Block 1 ID wrong (%s not a .swp file?)"), | |
1571 mfp->mf_fname); | |
1572 goto theend; | |
1573 } | |
1574 ++error; | |
1575 ml_append(lnum++, (char_u *)_("???BLOCK MISSING"), | |
1576 (colnr_T)0, TRUE); | |
1577 } | |
1578 else | |
1579 { | |
1580 /* | |
1581 * it is a data block | |
1582 * Append all the lines in this block | |
1583 */ | |
1584 has_error = FALSE; | |
1585 /* | |
1586 * check length of block | |
1587 * if wrong, use length in pointer block | |
1588 */ | |
1589 if (page_count * mfp->mf_page_size != dp->db_txt_end) | |
1590 { | |
1591 ml_append(lnum++, (char_u *)_("??? from here until ???END lines may be messed up"), | |
1592 (colnr_T)0, TRUE); | |
1593 ++error; | |
1594 has_error = TRUE; | |
1595 dp->db_txt_end = page_count * mfp->mf_page_size; | |
1596 } | |
1597 | |
1598 /* make sure there is a NUL at the end of the block */ | |
1599 *((char_u *)dp + dp->db_txt_end - 1) = NUL; | |
1600 | |
1601 /* | |
1602 * check number of lines in block | |
1603 * if wrong, use count in data block | |
1604 */ | |
1605 if (line_count != dp->db_line_count) | |
1606 { | |
1607 ml_append(lnum++, (char_u *)_("??? from here until ???END lines may have been inserted/deleted"), | |
1608 (colnr_T)0, TRUE); | |
1609 ++error; | |
1610 has_error = TRUE; | |
1611 } | |
1612 | |
1613 for (i = 0; i < dp->db_line_count; ++i) | |
1614 { | |
1615 txt_start = (dp->db_index[i] & DB_INDEX_MASK); | |
1978 | 1616 if (txt_start <= (int)HEADER_SIZE |
7 | 1617 || txt_start >= (int)dp->db_txt_end) |
1618 { | |
1619 p = (char_u *)"???"; | |
1620 ++error; | |
1621 } | |
1622 else | |
1623 p = (char_u *)dp + txt_start; | |
1624 ml_append(lnum++, p, (colnr_T)0, TRUE); | |
1625 } | |
1626 if (has_error) | |
1978 | 1627 ml_append(lnum++, (char_u *)_("???END"), |
1628 (colnr_T)0, TRUE); | |
7 | 1629 } |
1630 } | |
1631 } | |
1632 | |
1633 if (buf->b_ml.ml_stack_top == 0) /* finished */ | |
1634 break; | |
1635 | |
1636 /* | |
1637 * go one block up in the tree | |
1638 */ | |
1639 ip = &(buf->b_ml.ml_stack[--(buf->b_ml.ml_stack_top)]); | |
1640 bnum = ip->ip_bnum; | |
1641 idx = ip->ip_index + 1; /* go to next index */ | |
1642 page_count = 1; | |
1643 } | |
1644 | |
1645 /* | |
2162
0527eb0f6918
After recovery check if the text changed. If it did mark the buffer as
Bram Moolenaar <bram@vim.org>
parents:
2145
diff
changeset
|
1646 * Compare the buffer contents with the original file. When they differ |
0527eb0f6918
After recovery check if the text changed. If it did mark the buffer as
Bram Moolenaar <bram@vim.org>
parents:
2145
diff
changeset
|
1647 * set the 'modified' flag. |
0527eb0f6918
After recovery check if the text changed. If it did mark the buffer as
Bram Moolenaar <bram@vim.org>
parents:
2145
diff
changeset
|
1648 * Lines 1 - lnum are the new contents. |
0527eb0f6918
After recovery check if the text changed. If it did mark the buffer as
Bram Moolenaar <bram@vim.org>
parents:
2145
diff
changeset
|
1649 * Lines lnum + 1 to ml_line_count are the original contents. |
0527eb0f6918
After recovery check if the text changed. If it did mark the buffer as
Bram Moolenaar <bram@vim.org>
parents:
2145
diff
changeset
|
1650 * Line ml_line_count + 1 in the dummy empty line. |
7 | 1651 */ |
2162
0527eb0f6918
After recovery check if the text changed. If it did mark the buffer as
Bram Moolenaar <bram@vim.org>
parents:
2145
diff
changeset
|
1652 if (orig_file_status != OK || curbuf->b_ml.ml_line_count != lnum * 2 + 1) |
0527eb0f6918
After recovery check if the text changed. If it did mark the buffer as
Bram Moolenaar <bram@vim.org>
parents:
2145
diff
changeset
|
1653 { |
0527eb0f6918
After recovery check if the text changed. If it did mark the buffer as
Bram Moolenaar <bram@vim.org>
parents:
2145
diff
changeset
|
1654 /* Recovering an empty file results in two lines and the first line is |
0527eb0f6918
After recovery check if the text changed. If it did mark the buffer as
Bram Moolenaar <bram@vim.org>
parents:
2145
diff
changeset
|
1655 * empty. Don't set the modified flag then. */ |
0527eb0f6918
After recovery check if the text changed. If it did mark the buffer as
Bram Moolenaar <bram@vim.org>
parents:
2145
diff
changeset
|
1656 if (!(curbuf->b_ml.ml_line_count == 2 && *ml_get(1) == NUL)) |
0527eb0f6918
After recovery check if the text changed. If it did mark the buffer as
Bram Moolenaar <bram@vim.org>
parents:
2145
diff
changeset
|
1657 { |
0527eb0f6918
After recovery check if the text changed. If it did mark the buffer as
Bram Moolenaar <bram@vim.org>
parents:
2145
diff
changeset
|
1658 changed_int(); |
0527eb0f6918
After recovery check if the text changed. If it did mark the buffer as
Bram Moolenaar <bram@vim.org>
parents:
2145
diff
changeset
|
1659 ++curbuf->b_changedtick; |
0527eb0f6918
After recovery check if the text changed. If it did mark the buffer as
Bram Moolenaar <bram@vim.org>
parents:
2145
diff
changeset
|
1660 } |
0527eb0f6918
After recovery check if the text changed. If it did mark the buffer as
Bram Moolenaar <bram@vim.org>
parents:
2145
diff
changeset
|
1661 } |
0527eb0f6918
After recovery check if the text changed. If it did mark the buffer as
Bram Moolenaar <bram@vim.org>
parents:
2145
diff
changeset
|
1662 else |
0527eb0f6918
After recovery check if the text changed. If it did mark the buffer as
Bram Moolenaar <bram@vim.org>
parents:
2145
diff
changeset
|
1663 { |
0527eb0f6918
After recovery check if the text changed. If it did mark the buffer as
Bram Moolenaar <bram@vim.org>
parents:
2145
diff
changeset
|
1664 for (idx = 1; idx <= lnum; ++idx) |
0527eb0f6918
After recovery check if the text changed. If it did mark the buffer as
Bram Moolenaar <bram@vim.org>
parents:
2145
diff
changeset
|
1665 { |
0527eb0f6918
After recovery check if the text changed. If it did mark the buffer as
Bram Moolenaar <bram@vim.org>
parents:
2145
diff
changeset
|
1666 /* Need to copy one line, fetching the other one may flush it. */ |
0527eb0f6918
After recovery check if the text changed. If it did mark the buffer as
Bram Moolenaar <bram@vim.org>
parents:
2145
diff
changeset
|
1667 p = vim_strsave(ml_get(idx)); |
0527eb0f6918
After recovery check if the text changed. If it did mark the buffer as
Bram Moolenaar <bram@vim.org>
parents:
2145
diff
changeset
|
1668 i = STRCMP(p, ml_get(idx + lnum)); |
0527eb0f6918
After recovery check if the text changed. If it did mark the buffer as
Bram Moolenaar <bram@vim.org>
parents:
2145
diff
changeset
|
1669 vim_free(p); |
0527eb0f6918
After recovery check if the text changed. If it did mark the buffer as
Bram Moolenaar <bram@vim.org>
parents:
2145
diff
changeset
|
1670 if (i != 0) |
0527eb0f6918
After recovery check if the text changed. If it did mark the buffer as
Bram Moolenaar <bram@vim.org>
parents:
2145
diff
changeset
|
1671 { |
0527eb0f6918
After recovery check if the text changed. If it did mark the buffer as
Bram Moolenaar <bram@vim.org>
parents:
2145
diff
changeset
|
1672 changed_int(); |
0527eb0f6918
After recovery check if the text changed. If it did mark the buffer as
Bram Moolenaar <bram@vim.org>
parents:
2145
diff
changeset
|
1673 ++curbuf->b_changedtick; |
0527eb0f6918
After recovery check if the text changed. If it did mark the buffer as
Bram Moolenaar <bram@vim.org>
parents:
2145
diff
changeset
|
1674 break; |
0527eb0f6918
After recovery check if the text changed. If it did mark the buffer as
Bram Moolenaar <bram@vim.org>
parents:
2145
diff
changeset
|
1675 } |
0527eb0f6918
After recovery check if the text changed. If it did mark the buffer as
Bram Moolenaar <bram@vim.org>
parents:
2145
diff
changeset
|
1676 } |
0527eb0f6918
After recovery check if the text changed. If it did mark the buffer as
Bram Moolenaar <bram@vim.org>
parents:
2145
diff
changeset
|
1677 } |
0527eb0f6918
After recovery check if the text changed. If it did mark the buffer as
Bram Moolenaar <bram@vim.org>
parents:
2145
diff
changeset
|
1678 |
0527eb0f6918
After recovery check if the text changed. If it did mark the buffer as
Bram Moolenaar <bram@vim.org>
parents:
2145
diff
changeset
|
1679 /* |
0527eb0f6918
After recovery check if the text changed. If it did mark the buffer as
Bram Moolenaar <bram@vim.org>
parents:
2145
diff
changeset
|
1680 * Delete the lines from the original file and the dummy line from the |
0527eb0f6918
After recovery check if the text changed. If it did mark the buffer as
Bram Moolenaar <bram@vim.org>
parents:
2145
diff
changeset
|
1681 * empty buffer. These will now be after the last line in the buffer. |
0527eb0f6918
After recovery check if the text changed. If it did mark the buffer as
Bram Moolenaar <bram@vim.org>
parents:
2145
diff
changeset
|
1682 */ |
0527eb0f6918
After recovery check if the text changed. If it did mark the buffer as
Bram Moolenaar <bram@vim.org>
parents:
2145
diff
changeset
|
1683 while (curbuf->b_ml.ml_line_count > lnum |
0527eb0f6918
After recovery check if the text changed. If it did mark the buffer as
Bram Moolenaar <bram@vim.org>
parents:
2145
diff
changeset
|
1684 && !(curbuf->b_ml.ml_flags & ML_EMPTY)) |
0527eb0f6918
After recovery check if the text changed. If it did mark the buffer as
Bram Moolenaar <bram@vim.org>
parents:
2145
diff
changeset
|
1685 ml_delete(curbuf->b_ml.ml_line_count, FALSE); |
7 | 1686 curbuf->b_flags |= BF_RECOVERED; |
1687 | |
1688 recoverymode = FALSE; | |
1689 if (got_int) | |
1690 EMSG(_("E311: Recovery Interrupted")); | |
1691 else if (error) | |
1692 { | |
1693 ++no_wait_return; | |
1694 MSG(">>>>>>>>>>>>>"); | |
1695 EMSG(_("E312: Errors detected while recovering; look for lines starting with ???")); | |
1696 --no_wait_return; | |
1697 MSG(_("See \":help E312\" for more information.")); | |
1698 MSG(">>>>>>>>>>>>>"); | |
1699 } | |
1700 else | |
1701 { | |
2162
0527eb0f6918
After recovery check if the text changed. If it did mark the buffer as
Bram Moolenaar <bram@vim.org>
parents:
2145
diff
changeset
|
1702 if (curbuf->b_changed) |
0527eb0f6918
After recovery check if the text changed. If it did mark the buffer as
Bram Moolenaar <bram@vim.org>
parents:
2145
diff
changeset
|
1703 { |
0527eb0f6918
After recovery check if the text changed. If it did mark the buffer as
Bram Moolenaar <bram@vim.org>
parents:
2145
diff
changeset
|
1704 MSG(_("Recovery completed. You should check if everything is OK.")); |
0527eb0f6918
After recovery check if the text changed. If it did mark the buffer as
Bram Moolenaar <bram@vim.org>
parents:
2145
diff
changeset
|
1705 MSG_PUTS(_("\n(You might want to write out this file under another name\n")); |
0527eb0f6918
After recovery check if the text changed. If it did mark the buffer as
Bram Moolenaar <bram@vim.org>
parents:
2145
diff
changeset
|
1706 MSG_PUTS(_("and run diff with the original file to check for changes)")); |
0527eb0f6918
After recovery check if the text changed. If it did mark the buffer as
Bram Moolenaar <bram@vim.org>
parents:
2145
diff
changeset
|
1707 } |
0527eb0f6918
After recovery check if the text changed. If it did mark the buffer as
Bram Moolenaar <bram@vim.org>
parents:
2145
diff
changeset
|
1708 else |
0527eb0f6918
After recovery check if the text changed. If it did mark the buffer as
Bram Moolenaar <bram@vim.org>
parents:
2145
diff
changeset
|
1709 MSG(_("Recovery completed. Buffer contents equals file contents.")); |
0527eb0f6918
After recovery check if the text changed. If it did mark the buffer as
Bram Moolenaar <bram@vim.org>
parents:
2145
diff
changeset
|
1710 MSG_PUTS(_("\nYou may want to delete the .swp file now.\n\n")); |
7 | 1711 cmdline_row = msg_row; |
1712 } | |
2267 | 1713 #ifdef FEAT_CRYPT |
1714 if (*buf->b_p_key != NUL && STRCMP(curbuf->b_p_key, buf->b_p_key) != 0) | |
1715 { | |
1716 MSG_PUTS(_("Using crypt key from swap file for the text file.\n")); | |
1717 set_option_value((char_u *)"key", 0L, buf->b_p_key, OPT_LOCAL); | |
1718 } | |
1719 #endif | |
7 | 1720 redraw_curbuf_later(NOT_VALID); |
1721 | |
1722 theend: | |
2267 | 1723 vim_free(fname_used); |
7 | 1724 recoverymode = FALSE; |
1725 if (mfp != NULL) | |
1726 { | |
1727 if (hp != NULL) | |
1728 mf_put(mfp, hp, FALSE, FALSE); | |
1729 mf_close(mfp, FALSE); /* will also vim_free(mfp->mf_fname) */ | |
1730 } | |
1053 | 1731 if (buf != NULL) |
1732 { | |
2267 | 1733 #ifdef FEAT_CRYPT |
1734 if (buf->b_p_key != curbuf->b_p_key) | |
1735 free_string_option(buf->b_p_key); | |
2407
6bc102a4bff8
Fix memory access to 'cryptmethod' during recovery. (Dominique Pelle)
Bram Moolenaar <bram@vim.org>
parents:
2394
diff
changeset
|
1736 free_string_option(buf->b_p_cm); |
2267 | 1737 #endif |
1053 | 1738 vim_free(buf->b_ml.ml_stack); |
1739 vim_free(buf); | |
1740 } | |
7 | 1741 if (serious_error && called_from_main) |
1742 ml_close(curbuf, TRUE); | |
1743 #ifdef FEAT_AUTOCMD | |
1744 else | |
1745 { | |
1746 apply_autocmds(EVENT_BUFREADPOST, NULL, curbuf->b_fname, FALSE, curbuf); | |
1747 apply_autocmds(EVENT_BUFWINENTER, NULL, curbuf->b_fname, FALSE, curbuf); | |
1748 } | |
1749 #endif | |
1750 return; | |
1751 } | |
1752 | |
1753 /* | |
1754 * Find the names of swap files in current directory and the directory given | |
1755 * with the 'directory' option. | |
1756 * | |
1757 * Used to: | |
1758 * - list the swap files for "vim -r" | |
1759 * - count the number of swap files when recovering | |
1760 * - list the swap files when recovering | |
1761 * - find the name of the n'th swap file when recovering | |
1762 */ | |
1763 int | |
7827
41789f16d6b2
commit https://github.com/vim/vim/commit/52ea13da0fe86df1abf34de52841e367035170c0
Christian Brabandt <cb@256bit.org>
parents:
7803
diff
changeset
|
1764 recover_names( |
41789f16d6b2
commit https://github.com/vim/vim/commit/52ea13da0fe86df1abf34de52841e367035170c0
Christian Brabandt <cb@256bit.org>
parents:
7803
diff
changeset
|
1765 char_u *fname, /* base for swap file name */ |
41789f16d6b2
commit https://github.com/vim/vim/commit/52ea13da0fe86df1abf34de52841e367035170c0
Christian Brabandt <cb@256bit.org>
parents:
7803
diff
changeset
|
1766 int list, /* when TRUE, list the swap file names */ |
41789f16d6b2
commit https://github.com/vim/vim/commit/52ea13da0fe86df1abf34de52841e367035170c0
Christian Brabandt <cb@256bit.org>
parents:
7803
diff
changeset
|
1767 int nr, /* when non-zero, return nr'th swap file name */ |
41789f16d6b2
commit https://github.com/vim/vim/commit/52ea13da0fe86df1abf34de52841e367035170c0
Christian Brabandt <cb@256bit.org>
parents:
7803
diff
changeset
|
1768 char_u **fname_out) /* result when "nr" > 0 */ |
7 | 1769 { |
1770 int num_names; | |
1771 char_u *(names[6]); | |
1772 char_u *tail; | |
1773 char_u *p; | |
1774 int num_files; | |
1775 int file_count = 0; | |
1776 char_u **files; | |
1777 int i; | |
1778 char_u *dirp; | |
1779 char_u *dir_name; | |
2175 | 1780 char_u *fname_res = NULL; |
2145
de0e7ca61893
updated for version 7.2.427
Bram Moolenaar <bram@zimbu.org>
parents:
2108
diff
changeset
|
1781 #ifdef HAVE_READLINK |
de0e7ca61893
updated for version 7.2.427
Bram Moolenaar <bram@zimbu.org>
parents:
2108
diff
changeset
|
1782 char_u fname_buf[MAXPATHL]; |
2175 | 1783 #endif |
1784 | |
1785 if (fname != NULL) | |
1786 { | |
1787 #ifdef HAVE_READLINK | |
2267 | 1788 /* Expand symlink in the file name, because the swap file is created |
1789 * with the actual file instead of with the symlink. */ | |
1790 if (resolve_symlink(fname, fname_buf) == OK) | |
1791 fname_res = fname_buf; | |
1792 else | |
2145
de0e7ca61893
updated for version 7.2.427
Bram Moolenaar <bram@zimbu.org>
parents:
2108
diff
changeset
|
1793 #endif |
2267 | 1794 fname_res = fname; |
2175 | 1795 } |
7 | 1796 |
1797 if (list) | |
1798 { | |
2145
de0e7ca61893
updated for version 7.2.427
Bram Moolenaar <bram@zimbu.org>
parents:
2108
diff
changeset
|
1799 /* use msg() to start the scrolling properly */ |
7 | 1800 msg((char_u *)_("Swap files found:")); |
1801 msg_putchar('\n'); | |
1802 } | |
1803 | |
1804 /* | |
1805 * Do the loop for every directory in 'directory'. | |
1806 * First allocate some memory to put the directory name in. | |
1807 */ | |
1808 dir_name = alloc((unsigned)STRLEN(p_dir) + 1); | |
1809 dirp = p_dir; | |
1810 while (dir_name != NULL && *dirp) | |
1811 { | |
1812 /* | |
1813 * Isolate a directory name from *dirp and put it in dir_name (we know | |
1814 * it is large enough, so use 31000 for length). | |
1815 * Advance dirp to next directory name. | |
1816 */ | |
1817 (void)copy_option_part(&dirp, dir_name, 31000, ","); | |
1818 | |
1819 if (dir_name[0] == '.' && dir_name[1] == NUL) /* check current dir */ | |
1820 { | |
2267 | 1821 if (fname == NULL) |
7 | 1822 { |
1823 #ifdef VMS | |
1824 names[0] = vim_strsave((char_u *)"*_sw%"); | |
1825 #else | |
1826 names[0] = vim_strsave((char_u *)"*.sw?"); | |
1827 #endif | |
1005 | 1828 #if defined(UNIX) || defined(WIN3264) |
1829 /* For Unix names starting with a dot are special. MS-Windows | |
1830 * supports this too, on some file systems. */ | |
7 | 1831 names[1] = vim_strsave((char_u *)".*.sw?"); |
1832 names[2] = vim_strsave((char_u *)".sw?"); | |
1833 num_names = 3; | |
1834 #else | |
1835 # ifdef VMS | |
1836 names[1] = vim_strsave((char_u *)".*_sw%"); | |
1837 num_names = 2; | |
1838 # else | |
1839 num_names = 1; | |
1840 # endif | |
1841 #endif | |
1842 } | |
1843 else | |
2145
de0e7ca61893
updated for version 7.2.427
Bram Moolenaar <bram@zimbu.org>
parents:
2108
diff
changeset
|
1844 num_names = recov_file_names(names, fname_res, TRUE); |
7 | 1845 } |
1846 else /* check directory dir_name */ | |
1847 { | |
2267 | 1848 if (fname == NULL) |
7 | 1849 { |
1850 #ifdef VMS | |
1851 names[0] = concat_fnames(dir_name, (char_u *)"*_sw%", TRUE); | |
1852 #else | |
1853 names[0] = concat_fnames(dir_name, (char_u *)"*.sw?", TRUE); | |
1854 #endif | |
1005 | 1855 #if defined(UNIX) || defined(WIN3264) |
1856 /* For Unix names starting with a dot are special. MS-Windows | |
1857 * supports this too, on some file systems. */ | |
7 | 1858 names[1] = concat_fnames(dir_name, (char_u *)".*.sw?", TRUE); |
1859 names[2] = concat_fnames(dir_name, (char_u *)".sw?", TRUE); | |
1860 num_names = 3; | |
1861 #else | |
1862 # ifdef VMS | |
1863 names[1] = concat_fnames(dir_name, (char_u *)".*_sw%", TRUE); | |
1864 num_names = 2; | |
1865 # else | |
1866 num_names = 1; | |
1867 # endif | |
1868 #endif | |
1869 } | |
1870 else | |
1871 { | |
1872 #if defined(UNIX) || defined(WIN3264) | |
1873 p = dir_name + STRLEN(dir_name); | |
39 | 1874 if (after_pathsep(dir_name, p) && p[-1] == p[-2]) |
7 | 1875 { |
1876 /* Ends with '//', Use Full path for swap name */ | |
2145
de0e7ca61893
updated for version 7.2.427
Bram Moolenaar <bram@zimbu.org>
parents:
2108
diff
changeset
|
1877 tail = make_percent_swname(dir_name, fname_res); |
7 | 1878 } |
1879 else | |
1880 #endif | |
1881 { | |
2145
de0e7ca61893
updated for version 7.2.427
Bram Moolenaar <bram@zimbu.org>
parents:
2108
diff
changeset
|
1882 tail = gettail(fname_res); |
7 | 1883 tail = concat_fnames(dir_name, tail, TRUE); |
1884 } | |
1885 if (tail == NULL) | |
1886 num_names = 0; | |
1887 else | |
1888 { | |
1889 num_names = recov_file_names(names, tail, FALSE); | |
1890 vim_free(tail); | |
1891 } | |
1892 } | |
1893 } | |
1894 | |
1895 /* check for out-of-memory */ | |
1896 for (i = 0; i < num_names; ++i) | |
1897 { | |
1898 if (names[i] == NULL) | |
1899 { | |
1900 for (i = 0; i < num_names; ++i) | |
1901 vim_free(names[i]); | |
1902 num_names = 0; | |
1903 } | |
1904 } | |
1905 if (num_names == 0) | |
1906 num_files = 0; | |
1907 else if (expand_wildcards(num_names, names, &num_files, &files, | |
1908 EW_KEEPALL|EW_FILE|EW_SILENT) == FAIL) | |
1909 num_files = 0; | |
1910 | |
1911 /* | |
1912 * When no swap file found, wildcard expansion might have failed (e.g. | |
1913 * not able to execute the shell). | |
1914 * Try finding a swap file by simply adding ".swp" to the file name. | |
1915 */ | |
2267 | 1916 if (*dirp == NUL && file_count + num_files == 0 && fname != NULL) |
7 | 1917 { |
1918 struct stat st; | |
1919 char_u *swapname; | |
1920 | |
2145
de0e7ca61893
updated for version 7.2.427
Bram Moolenaar <bram@zimbu.org>
parents:
2108
diff
changeset
|
1921 swapname = modname(fname_res, |
2823 | 1922 #if defined(VMS) |
2145
de0e7ca61893
updated for version 7.2.427
Bram Moolenaar <bram@zimbu.org>
parents:
2108
diff
changeset
|
1923 (char_u *)"_swp", FALSE |
7 | 1924 #else |
2145
de0e7ca61893
updated for version 7.2.427
Bram Moolenaar <bram@zimbu.org>
parents:
2108
diff
changeset
|
1925 (char_u *)".swp", TRUE |
7 | 1926 #endif |
2145
de0e7ca61893
updated for version 7.2.427
Bram Moolenaar <bram@zimbu.org>
parents:
2108
diff
changeset
|
1927 ); |
7 | 1928 if (swapname != NULL) |
1929 { | |
1930 if (mch_stat((char *)swapname, &st) != -1) /* It exists! */ | |
1931 { | |
1932 files = (char_u **)alloc((unsigned)sizeof(char_u *)); | |
1933 if (files != NULL) | |
1934 { | |
1935 files[0] = swapname; | |
1936 swapname = NULL; | |
1937 num_files = 1; | |
1938 } | |
1939 } | |
1940 vim_free(swapname); | |
1941 } | |
1942 } | |
1943 | |
1944 /* | |
1945 * remove swapfile name of the current buffer, it must be ignored | |
1946 */ | |
1947 if (curbuf->b_ml.ml_mfp != NULL | |
1948 && (p = curbuf->b_ml.ml_mfp->mf_fname) != NULL) | |
1949 { | |
1950 for (i = 0; i < num_files; ++i) | |
1951 if (fullpathcmp(p, files[i], TRUE) & FPC_SAME) | |
1952 { | |
1855 | 1953 /* Remove the name from files[i]. Move further entries |
1954 * down. When the array becomes empty free it here, since | |
1955 * FreeWild() won't be called below. */ | |
7 | 1956 vim_free(files[i]); |
1855 | 1957 if (--num_files == 0) |
1958 vim_free(files); | |
1959 else | |
1960 for ( ; i < num_files; ++i) | |
1961 files[i] = files[i + 1]; | |
7 | 1962 } |
1963 } | |
838 | 1964 if (nr > 0) |
7 | 1965 { |
1966 file_count += num_files; | |
1967 if (nr <= file_count) | |
1968 { | |
2267 | 1969 *fname_out = vim_strsave( |
1970 files[nr - 1 + num_files - file_count]); | |
7 | 1971 dirp = (char_u *)""; /* stop searching */ |
1972 } | |
1973 } | |
1974 else if (list) | |
1975 { | |
1976 if (dir_name[0] == '.' && dir_name[1] == NUL) | |
1977 { | |
2267 | 1978 if (fname == NULL) |
7 | 1979 MSG_PUTS(_(" In current directory:\n")); |
1980 else | |
1981 MSG_PUTS(_(" Using specified name:\n")); | |
1982 } | |
1983 else | |
1984 { | |
1985 MSG_PUTS(_(" In directory ")); | |
1986 msg_home_replace(dir_name); | |
1987 MSG_PUTS(":\n"); | |
1988 } | |
1989 | |
1990 if (num_files) | |
1991 { | |
1992 for (i = 0; i < num_files; ++i) | |
1993 { | |
1994 /* print the swap file name */ | |
1995 msg_outnum((long)++file_count); | |
1996 MSG_PUTS(". "); | |
1997 msg_puts(gettail(files[i])); | |
1998 msg_putchar('\n'); | |
1999 (void)swapfile_info(files[i]); | |
2000 } | |
2001 } | |
2002 else | |
2003 MSG_PUTS(_(" -- none --\n")); | |
2004 out_flush(); | |
2005 } | |
2006 else | |
2007 file_count += num_files; | |
2008 | |
2009 for (i = 0; i < num_names; ++i) | |
2010 vim_free(names[i]); | |
838 | 2011 if (num_files > 0) |
2012 FreeWild(num_files, files); | |
7 | 2013 } |
2014 vim_free(dir_name); | |
2015 return file_count; | |
2016 } | |
2017 | |
2018 #if defined(UNIX) || defined(WIN3264) /* Need _very_ long file names */ | |
2019 /* | |
2020 * Append the full path to name with path separators made into percent | |
2021 * signs, to dir. An unnamed buffer is handled as "" (<currentdir>/"") | |
2022 */ | |
2023 static char_u * | |
7827
41789f16d6b2
commit https://github.com/vim/vim/commit/52ea13da0fe86df1abf34de52841e367035170c0
Christian Brabandt <cb@256bit.org>
parents:
7803
diff
changeset
|
2024 make_percent_swname(char_u *dir, char_u *name) |
7 | 2025 { |
39 | 2026 char_u *d, *s, *f; |
7 | 2027 |
2028 f = fix_fname(name != NULL ? name : (char_u *) ""); | |
2029 d = NULL; | |
2030 if (f != NULL) | |
2031 { | |
2032 s = alloc((unsigned)(STRLEN(f) + 1)); | |
2033 if (s != NULL) | |
2034 { | |
39 | 2035 STRCPY(s, f); |
2036 for (d = s; *d != NUL; mb_ptr_adv(d)) | |
2037 if (vim_ispathsep(*d)) | |
2038 *d = '%'; | |
7 | 2039 d = concat_fnames(dir, s, TRUE); |
2040 vim_free(s); | |
2041 } | |
2042 vim_free(f); | |
2043 } | |
2044 return d; | |
2045 } | |
2046 #endif | |
2047 | |
2048 #if (defined(UNIX) || defined(__EMX__) || defined(VMS)) && (defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG)) | |
2049 static int process_still_running; | |
2050 #endif | |
2051 | |
2052 /* | |
580 | 2053 * Give information about an existing swap file. |
7 | 2054 * Returns timestamp (0 when unknown). |
2055 */ | |
2056 static time_t | |
7827
41789f16d6b2
commit https://github.com/vim/vim/commit/52ea13da0fe86df1abf34de52841e367035170c0
Christian Brabandt <cb@256bit.org>
parents:
7803
diff
changeset
|
2057 swapfile_info(char_u *fname) |
7 | 2058 { |
2059 struct stat st; | |
2060 int fd; | |
2061 struct block0 b0; | |
2062 time_t x = (time_t)0; | |
1001 | 2063 char *p; |
7 | 2064 #ifdef UNIX |
2065 char_u uname[B0_UNAME_SIZE]; | |
2066 #endif | |
2067 | |
2068 /* print the swap file date */ | |
2069 if (mch_stat((char *)fname, &st) != -1) | |
2070 { | |
2071 #ifdef UNIX | |
2072 /* print name of owner of the file */ | |
2073 if (mch_get_uname(st.st_uid, uname, B0_UNAME_SIZE) == OK) | |
2074 { | |
2075 MSG_PUTS(_(" owned by: ")); | |
2076 msg_outtrans(uname); | |
2077 MSG_PUTS(_(" dated: ")); | |
2078 } | |
2079 else | |
2080 #endif | |
2081 MSG_PUTS(_(" dated: ")); | |
2082 x = st.st_mtime; /* Manx C can't do &st.st_mtime */ | |
1001 | 2083 p = ctime(&x); /* includes '\n' */ |
2084 if (p == NULL) | |
2085 MSG_PUTS("(invalid)\n"); | |
2086 else | |
2087 MSG_PUTS(p); | |
7 | 2088 } |
2089 | |
2090 /* | |
2091 * print the original file name | |
2092 */ | |
2093 fd = mch_open((char *)fname, O_RDONLY | O_EXTRA, 0); | |
2094 if (fd >= 0) | |
2095 { | |
2664 | 2096 if (read_eintr(fd, &b0, sizeof(b0)) == sizeof(b0)) |
7 | 2097 { |
2098 if (STRNCMP(b0.b0_version, "VIM 3.0", 7) == 0) | |
2099 { | |
2100 MSG_PUTS(_(" [from Vim version 3.0]")); | |
2101 } | |
2267 | 2102 else if (ml_check_b0_id(&b0) == FAIL) |
7 | 2103 { |
2104 MSG_PUTS(_(" [does not look like a Vim swap file]")); | |
2105 } | |
2106 else | |
2107 { | |
2108 MSG_PUTS(_(" file name: ")); | |
2109 if (b0.b0_fname[0] == NUL) | |
9 | 2110 MSG_PUTS(_("[No Name]")); |
7 | 2111 else |
2112 msg_outtrans(b0.b0_fname); | |
2113 | |
2114 MSG_PUTS(_("\n modified: ")); | |
2115 MSG_PUTS(b0.b0_dirty ? _("YES") : _("no")); | |
2116 | |
2117 if (*(b0.b0_uname) != NUL) | |
2118 { | |
2119 MSG_PUTS(_("\n user name: ")); | |
2120 msg_outtrans(b0.b0_uname); | |
2121 } | |
2122 | |
2123 if (*(b0.b0_hname) != NUL) | |
2124 { | |
2125 if (*(b0.b0_uname) != NUL) | |
2126 MSG_PUTS(_(" host name: ")); | |
2127 else | |
2128 MSG_PUTS(_("\n host name: ")); | |
2129 msg_outtrans(b0.b0_hname); | |
2130 } | |
2131 | |
2132 if (char_to_long(b0.b0_pid) != 0L) | |
2133 { | |
2134 MSG_PUTS(_("\n process ID: ")); | |
2135 msg_outnum(char_to_long(b0.b0_pid)); | |
2136 #if defined(UNIX) || defined(__EMX__) | |
2137 /* EMX kill() not working correctly, it seems */ | |
2138 if (kill((pid_t)char_to_long(b0.b0_pid), 0) == 0) | |
2139 { | |
2140 MSG_PUTS(_(" (still running)")); | |
2141 # if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG) | |
2142 process_still_running = TRUE; | |
2143 # endif | |
2144 } | |
2145 #endif | |
2146 } | |
2147 | |
2148 if (b0_magic_wrong(&b0)) | |
2149 { | |
2150 #if defined(MSDOS) || defined(MSWIN) | |
2151 if (STRNCMP(b0.b0_hname, "PC ", 3) == 0) | |
2152 MSG_PUTS(_("\n [not usable with this version of Vim]")); | |
2153 else | |
2154 #endif | |
2155 MSG_PUTS(_("\n [not usable on this computer]")); | |
2156 } | |
2157 } | |
2158 } | |
2159 else | |
2160 MSG_PUTS(_(" [cannot be read]")); | |
2161 close(fd); | |
2162 } | |
2163 else | |
2164 MSG_PUTS(_(" [cannot be opened]")); | |
2165 msg_putchar('\n'); | |
2166 | |
2167 return x; | |
2168 } | |
2169 | |
2170 static int | |
7827
41789f16d6b2
commit https://github.com/vim/vim/commit/52ea13da0fe86df1abf34de52841e367035170c0
Christian Brabandt <cb@256bit.org>
parents:
7803
diff
changeset
|
2171 recov_file_names(char_u **names, char_u *path, int prepend_dot) |
7 | 2172 { |
2173 int num_names; | |
2174 | |
2175 #ifdef SHORT_FNAME | |
2176 /* | |
2177 * (MS-DOS) always short names | |
2178 */ | |
2179 names[0] = modname(path, (char_u *)".sw?", FALSE); | |
2180 num_names = 1; | |
2181 #else /* !SHORT_FNAME */ | |
2182 /* | |
2183 * (Win32 and Win64) never short names, but do prepend a dot. | |
2184 * (Not MS-DOS or Win32 or Win64) maybe short name, maybe not: Try both. | |
2185 * Only use the short name if it is different. | |
2186 */ | |
2187 char_u *p; | |
2188 int i; | |
2189 # ifndef WIN3264 | |
2190 int shortname = curbuf->b_shortname; | |
2191 | |
2192 curbuf->b_shortname = FALSE; | |
2193 # endif | |
2194 | |
2195 num_names = 0; | |
2196 | |
2197 /* | |
2198 * May also add the file name with a dot prepended, for swap file in same | |
2199 * dir as original file. | |
2200 */ | |
2201 if (prepend_dot) | |
2202 { | |
2203 names[num_names] = modname(path, (char_u *)".sw?", TRUE); | |
2204 if (names[num_names] == NULL) | |
2205 goto end; | |
2206 ++num_names; | |
2207 } | |
2208 | |
2209 /* | |
2210 * Form the normal swap file name pattern by appending ".sw?". | |
2211 */ | |
2212 #ifdef VMS | |
2213 names[num_names] = concat_fnames(path, (char_u *)"_sw%", FALSE); | |
2214 #else | |
2215 names[num_names] = concat_fnames(path, (char_u *)".sw?", FALSE); | |
2216 #endif | |
2217 if (names[num_names] == NULL) | |
2218 goto end; | |
2219 if (num_names >= 1) /* check if we have the same name twice */ | |
2220 { | |
2221 p = names[num_names - 1]; | |
2222 i = (int)STRLEN(names[num_names - 1]) - (int)STRLEN(names[num_names]); | |
2223 if (i > 0) | |
2224 p += i; /* file name has been expanded to full path */ | |
2225 | |
2226 if (STRCMP(p, names[num_names]) != 0) | |
2227 ++num_names; | |
2228 else | |
2229 vim_free(names[num_names]); | |
2230 } | |
2231 else | |
2232 ++num_names; | |
2233 | |
2234 # ifndef WIN3264 | |
2235 /* | |
2236 * Also try with 'shortname' set, in case the file is on a DOS filesystem. | |
2237 */ | |
2238 curbuf->b_shortname = TRUE; | |
2239 #ifdef VMS | |
2240 names[num_names] = modname(path, (char_u *)"_sw%", FALSE); | |
2241 #else | |
2242 names[num_names] = modname(path, (char_u *)".sw?", FALSE); | |
2243 #endif | |
2244 if (names[num_names] == NULL) | |
2245 goto end; | |
2246 | |
2247 /* | |
2248 * Remove the one from 'shortname', if it's the same as with 'noshortname'. | |
2249 */ | |
2250 p = names[num_names]; | |
2251 i = STRLEN(names[num_names]) - STRLEN(names[num_names - 1]); | |
2252 if (i > 0) | |
2253 p += i; /* file name has been expanded to full path */ | |
2254 if (STRCMP(names[num_names - 1], p) == 0) | |
2255 vim_free(names[num_names]); | |
2256 else | |
2257 ++num_names; | |
2258 # endif | |
2259 | |
2260 end: | |
2261 # ifndef WIN3264 | |
2262 curbuf->b_shortname = shortname; | |
2263 # endif | |
2264 | |
2265 #endif /* !SHORT_FNAME */ | |
2266 | |
2267 return num_names; | |
2268 } | |
2269 | |
2270 /* | |
2271 * sync all memlines | |
2272 * | |
2273 * If 'check_file' is TRUE, check if original file exists and was not changed. | |
2274 * If 'check_char' is TRUE, stop syncing when character becomes available, but | |
2275 * always sync at least one block. | |
2276 */ | |
2277 void | |
7827
41789f16d6b2
commit https://github.com/vim/vim/commit/52ea13da0fe86df1abf34de52841e367035170c0
Christian Brabandt <cb@256bit.org>
parents:
7803
diff
changeset
|
2278 ml_sync_all(int check_file, int check_char) |
7 | 2279 { |
2280 buf_T *buf; | |
2281 struct stat st; | |
2282 | |
2283 for (buf = firstbuf; buf != NULL; buf = buf->b_next) | |
2284 { | |
2285 if (buf->b_ml.ml_mfp == NULL || buf->b_ml.ml_mfp->mf_fname == NULL) | |
2286 continue; /* no file */ | |
2287 | |
2288 ml_flush_line(buf); /* flush buffered line */ | |
2289 /* flush locked block */ | |
2290 (void)ml_find_line(buf, (linenr_T)0, ML_FLUSH); | |
2291 if (bufIsChanged(buf) && check_file && mf_need_trans(buf->b_ml.ml_mfp) | |
2292 && buf->b_ffname != NULL) | |
2293 { | |
2294 /* | |
2295 * If the original file does not exist anymore or has been changed | |
2296 * call ml_preserve() to get rid of all negative numbered blocks. | |
2297 */ | |
2298 if (mch_stat((char *)buf->b_ffname, &st) == -1 | |
2299 || st.st_mtime != buf->b_mtime_read | |
2241
60da25e3aab7
Correct use of long instead of off_t for file size. (James Vega)
Bram Moolenaar <bram@vim.org>
parents:
2240
diff
changeset
|
2300 || st.st_size != buf->b_orig_size) |
7 | 2301 { |
2302 ml_preserve(buf, FALSE); | |
2303 did_check_timestamps = FALSE; | |
2304 need_check_timestamps = TRUE; /* give message later */ | |
2305 } | |
2306 } | |
2307 if (buf->b_ml.ml_mfp->mf_dirty) | |
2308 { | |
2309 (void)mf_sync(buf->b_ml.ml_mfp, (check_char ? MFS_STOP : 0) | |
2310 | (bufIsChanged(buf) ? MFS_FLUSH : 0)); | |
2311 if (check_char && ui_char_avail()) /* character available now */ | |
2312 break; | |
2313 } | |
2314 } | |
2315 } | |
2316 | |
2317 /* | |
2318 * sync one buffer, including negative blocks | |
2319 * | |
2320 * after this all the blocks are in the swap file | |
2321 * | |
2322 * Used for the :preserve command and when the original file has been | |
2323 * changed or deleted. | |
2324 * | |
2325 * when message is TRUE the success of preserving is reported | |
2326 */ | |
2327 void | |
7827
41789f16d6b2
commit https://github.com/vim/vim/commit/52ea13da0fe86df1abf34de52841e367035170c0
Christian Brabandt <cb@256bit.org>
parents:
7803
diff
changeset
|
2328 ml_preserve(buf_T *buf, int message) |
7 | 2329 { |
2330 bhdr_T *hp; | |
2331 linenr_T lnum; | |
2332 memfile_T *mfp = buf->b_ml.ml_mfp; | |
2333 int status; | |
2334 int got_int_save = got_int; | |
2335 | |
2336 if (mfp == NULL || mfp->mf_fname == NULL) | |
2337 { | |
2338 if (message) | |
2339 EMSG(_("E313: Cannot preserve, there is no swap file")); | |
2340 return; | |
2341 } | |
2342 | |
2343 /* We only want to stop when interrupted here, not when interrupted | |
2344 * before. */ | |
2345 got_int = FALSE; | |
2346 | |
2347 ml_flush_line(buf); /* flush buffered line */ | |
2348 (void)ml_find_line(buf, (linenr_T)0, ML_FLUSH); /* flush locked block */ | |
2349 status = mf_sync(mfp, MFS_ALL | MFS_FLUSH); | |
2350 | |
2351 /* stack is invalid after mf_sync(.., MFS_ALL) */ | |
2352 buf->b_ml.ml_stack_top = 0; | |
2353 | |
2354 /* | |
2355 * Some of the data blocks may have been changed from negative to | |
2356 * positive block number. In that case the pointer blocks need to be | |
2357 * updated. | |
2358 * | |
2359 * We don't know in which pointer block the references are, so we visit | |
2360 * all data blocks until there are no more translations to be done (or | |
2361 * we hit the end of the file, which can only happen in case a write fails, | |
2362 * e.g. when file system if full). | |
2363 * ml_find_line() does the work by translating the negative block numbers | |
2364 * when getting the first line of each data block. | |
2365 */ | |
2366 if (mf_need_trans(mfp) && !got_int) | |
2367 { | |
2368 lnum = 1; | |
2369 while (mf_need_trans(mfp) && lnum <= buf->b_ml.ml_line_count) | |
2370 { | |
2371 hp = ml_find_line(buf, lnum, ML_FIND); | |
2372 if (hp == NULL) | |
2373 { | |
2374 status = FAIL; | |
2375 goto theend; | |
2376 } | |
2377 CHECK(buf->b_ml.ml_locked_low != lnum, "low != lnum"); | |
2378 lnum = buf->b_ml.ml_locked_high + 1; | |
2379 } | |
2380 (void)ml_find_line(buf, (linenr_T)0, ML_FLUSH); /* flush locked block */ | |
2381 /* sync the updated pointer blocks */ | |
2382 if (mf_sync(mfp, MFS_ALL | MFS_FLUSH) == FAIL) | |
2383 status = FAIL; | |
2384 buf->b_ml.ml_stack_top = 0; /* stack is invalid now */ | |
2385 } | |
2386 theend: | |
2387 got_int |= got_int_save; | |
2388 | |
2389 if (message) | |
2390 { | |
2391 if (status == OK) | |
2392 MSG(_("File preserved")); | |
2393 else | |
2394 EMSG(_("E314: Preserve failed")); | |
2395 } | |
2396 } | |
2397 | |
2398 /* | |
2399 * NOTE: The pointer returned by the ml_get_*() functions only remains valid | |
2400 * until the next call! | |
2401 * line1 = ml_get(1); | |
2402 * line2 = ml_get(2); // line1 is now invalid! | |
2403 * Make a copy of the line if necessary. | |
2404 */ | |
2405 /* | |
2657 | 2406 * Return a pointer to a (read-only copy of a) line. |
7 | 2407 * |
2408 * On failure an error message is given and IObuff is returned (to avoid | |
2409 * having to check for error everywhere). | |
2410 */ | |
2411 char_u * | |
7827
41789f16d6b2
commit https://github.com/vim/vim/commit/52ea13da0fe86df1abf34de52841e367035170c0
Christian Brabandt <cb@256bit.org>
parents:
7803
diff
changeset
|
2412 ml_get(linenr_T lnum) |
7 | 2413 { |
2414 return ml_get_buf(curbuf, lnum, FALSE); | |
2415 } | |
2416 | |
2417 /* | |
2657 | 2418 * Return pointer to position "pos". |
7 | 2419 */ |
2420 char_u * | |
7827
41789f16d6b2
commit https://github.com/vim/vim/commit/52ea13da0fe86df1abf34de52841e367035170c0
Christian Brabandt <cb@256bit.org>
parents:
7803
diff
changeset
|
2421 ml_get_pos(pos_T *pos) |
7 | 2422 { |
2423 return (ml_get_buf(curbuf, pos->lnum, FALSE) + pos->col); | |
2424 } | |
2425 | |
2426 /* | |
2657 | 2427 * Return pointer to cursor line. |
7 | 2428 */ |
2429 char_u * | |
7827
41789f16d6b2
commit https://github.com/vim/vim/commit/52ea13da0fe86df1abf34de52841e367035170c0
Christian Brabandt <cb@256bit.org>
parents:
7803
diff
changeset
|
2430 ml_get_curline(void) |
7 | 2431 { |
2432 return ml_get_buf(curbuf, curwin->w_cursor.lnum, FALSE); | |
2433 } | |
2434 | |
2435 /* | |
2657 | 2436 * Return pointer to cursor position. |
7 | 2437 */ |
2438 char_u * | |
7827
41789f16d6b2
commit https://github.com/vim/vim/commit/52ea13da0fe86df1abf34de52841e367035170c0
Christian Brabandt <cb@256bit.org>
parents:
7803
diff
changeset
|
2439 ml_get_cursor(void) |
7 | 2440 { |
2441 return (ml_get_buf(curbuf, curwin->w_cursor.lnum, FALSE) + | |
2442 curwin->w_cursor.col); | |
2443 } | |
2444 | |
2445 /* | |
2657 | 2446 * Return a pointer to a line in a specific buffer |
7 | 2447 * |
2448 * "will_change": if TRUE mark the buffer dirty (chars in the line will be | |
2449 * changed) | |
2450 */ | |
2451 char_u * | |
7827
41789f16d6b2
commit https://github.com/vim/vim/commit/52ea13da0fe86df1abf34de52841e367035170c0
Christian Brabandt <cb@256bit.org>
parents:
7803
diff
changeset
|
2452 ml_get_buf( |
41789f16d6b2
commit https://github.com/vim/vim/commit/52ea13da0fe86df1abf34de52841e367035170c0
Christian Brabandt <cb@256bit.org>
parents:
7803
diff
changeset
|
2453 buf_T *buf, |
41789f16d6b2
commit https://github.com/vim/vim/commit/52ea13da0fe86df1abf34de52841e367035170c0
Christian Brabandt <cb@256bit.org>
parents:
7803
diff
changeset
|
2454 linenr_T lnum, |
41789f16d6b2
commit https://github.com/vim/vim/commit/52ea13da0fe86df1abf34de52841e367035170c0
Christian Brabandt <cb@256bit.org>
parents:
7803
diff
changeset
|
2455 int will_change) /* line will be changed */ |
7 | 2456 { |
1068 | 2457 bhdr_T *hp; |
2458 DATA_BL *dp; | |
2459 char_u *ptr; | |
2460 static int recursive = 0; | |
7 | 2461 |
2462 if (lnum > buf->b_ml.ml_line_count) /* invalid line number */ | |
2463 { | |
1068 | 2464 if (recursive == 0) |
2465 { | |
2466 /* Avoid giving this message for a recursive call, may happen when | |
2467 * the GUI redraws part of the text. */ | |
2468 ++recursive; | |
2469 EMSGN(_("E315: ml_get: invalid lnum: %ld"), lnum); | |
2470 --recursive; | |
2471 } | |
7 | 2472 errorret: |
2473 STRCPY(IObuff, "???"); | |
2474 return IObuff; | |
2475 } | |
2476 if (lnum <= 0) /* pretend line 0 is line 1 */ | |
2477 lnum = 1; | |
2478 | |
2479 if (buf->b_ml.ml_mfp == NULL) /* there are no lines */ | |
2480 return (char_u *)""; | |
2481 | |
2108
3cdf2a653e00
updated for version 7.2.391
Bram Moolenaar <bram@zimbu.org>
parents:
2075
diff
changeset
|
2482 /* |
3cdf2a653e00
updated for version 7.2.391
Bram Moolenaar <bram@zimbu.org>
parents:
2075
diff
changeset
|
2483 * See if it is the same line as requested last time. |
3cdf2a653e00
updated for version 7.2.391
Bram Moolenaar <bram@zimbu.org>
parents:
2075
diff
changeset
|
2484 * Otherwise may need to flush last used line. |
3cdf2a653e00
updated for version 7.2.391
Bram Moolenaar <bram@zimbu.org>
parents:
2075
diff
changeset
|
2485 * Don't use the last used line when 'swapfile' is reset, need to load all |
3cdf2a653e00
updated for version 7.2.391
Bram Moolenaar <bram@zimbu.org>
parents:
2075
diff
changeset
|
2486 * blocks. |
3cdf2a653e00
updated for version 7.2.391
Bram Moolenaar <bram@zimbu.org>
parents:
2075
diff
changeset
|
2487 */ |
1066 | 2488 if (buf->b_ml.ml_line_lnum != lnum || mf_dont_release) |
7 | 2489 { |
2490 ml_flush_line(buf); | |
2491 | |
2492 /* | |
2493 * Find the data block containing the line. | |
2494 * This also fills the stack with the blocks from the root to the data | |
2495 * block and releases any locked block. | |
2496 */ | |
2497 if ((hp = ml_find_line(buf, lnum, ML_FIND)) == NULL) | |
2498 { | |
1068 | 2499 if (recursive == 0) |
2500 { | |
2501 /* Avoid giving this message for a recursive call, may happen | |
2502 * when the GUI redraws part of the text. */ | |
2503 ++recursive; | |
2504 EMSGN(_("E316: ml_get: cannot find line %ld"), lnum); | |
2505 --recursive; | |
2506 } | |
7 | 2507 goto errorret; |
2508 } | |
2509 | |
2510 dp = (DATA_BL *)(hp->bh_data); | |
2511 | |
2512 ptr = (char_u *)dp + ((dp->db_index[lnum - buf->b_ml.ml_locked_low]) & DB_INDEX_MASK); | |
2513 buf->b_ml.ml_line_ptr = ptr; | |
2514 buf->b_ml.ml_line_lnum = lnum; | |
2515 buf->b_ml.ml_flags &= ~ML_LINE_DIRTY; | |
2516 } | |
2517 if (will_change) | |
2518 buf->b_ml.ml_flags |= (ML_LOCKED_DIRTY | ML_LOCKED_POS); | |
2519 | |
2520 return buf->b_ml.ml_line_ptr; | |
2521 } | |
2522 | |
2523 /* | |
2524 * Check if a line that was just obtained by a call to ml_get | |
2525 * is in allocated memory. | |
2526 */ | |
2527 int | |
7827
41789f16d6b2
commit https://github.com/vim/vim/commit/52ea13da0fe86df1abf34de52841e367035170c0
Christian Brabandt <cb@256bit.org>
parents:
7803
diff
changeset
|
2528 ml_line_alloced(void) |
7 | 2529 { |
2530 return (curbuf->b_ml.ml_flags & ML_LINE_DIRTY); | |
2531 } | |
2532 | |
2533 /* | |
2534 * Append a line after lnum (may be 0 to insert a line in front of the file). | |
2535 * "line" does not need to be allocated, but can't be another line in a | |
2536 * buffer, unlocking may make it invalid. | |
2537 * | |
2538 * newfile: TRUE when starting to edit a new file, meaning that pe_old_lnum | |
2539 * will be set for recovery | |
2540 * Check: The caller of this function should probably also call | |
2541 * appended_lines(). | |
2542 * | |
2543 * return FAIL for failure, OK otherwise | |
2544 */ | |
2545 int | |
7827
41789f16d6b2
commit https://github.com/vim/vim/commit/52ea13da0fe86df1abf34de52841e367035170c0
Christian Brabandt <cb@256bit.org>
parents:
7803
diff
changeset
|
2546 ml_append( |
41789f16d6b2
commit https://github.com/vim/vim/commit/52ea13da0fe86df1abf34de52841e367035170c0
Christian Brabandt <cb@256bit.org>
parents:
7803
diff
changeset
|
2547 linenr_T lnum, /* append after this line (can be 0) */ |
41789f16d6b2
commit https://github.com/vim/vim/commit/52ea13da0fe86df1abf34de52841e367035170c0
Christian Brabandt <cb@256bit.org>
parents:
7803
diff
changeset
|
2548 char_u *line, /* text of the new line */ |
41789f16d6b2
commit https://github.com/vim/vim/commit/52ea13da0fe86df1abf34de52841e367035170c0
Christian Brabandt <cb@256bit.org>
parents:
7803
diff
changeset
|
2549 colnr_T len, /* length of new line, including NUL, or 0 */ |
41789f16d6b2
commit https://github.com/vim/vim/commit/52ea13da0fe86df1abf34de52841e367035170c0
Christian Brabandt <cb@256bit.org>
parents:
7803
diff
changeset
|
2550 int newfile) /* flag, see above */ |
7 | 2551 { |
2552 /* When starting up, we might still need to create the memfile */ | |
2394
a3aca345aafa
Add the 'undoreload' option to be able to undo a file reload.
Bram Moolenaar <bram@vim.org>
parents:
2360
diff
changeset
|
2553 if (curbuf->b_ml.ml_mfp == NULL && open_buffer(FALSE, NULL, 0) == FAIL) |
7 | 2554 return FAIL; |
2555 | |
2556 if (curbuf->b_ml.ml_line_lnum != 0) | |
2557 ml_flush_line(curbuf); | |
2558 return ml_append_int(curbuf, lnum, line, len, newfile, FALSE); | |
2559 } | |
2560 | |
748 | 2561 #if defined(FEAT_SPELL) || defined(PROTO) |
625 | 2562 /* |
2563 * Like ml_append() but for an arbitrary buffer. The buffer must already have | |
2564 * a memline. | |
2565 */ | |
2566 int | |
7827
41789f16d6b2
commit https://github.com/vim/vim/commit/52ea13da0fe86df1abf34de52841e367035170c0
Christian Brabandt <cb@256bit.org>
parents:
7803
diff
changeset
|
2567 ml_append_buf( |
41789f16d6b2
commit https://github.com/vim/vim/commit/52ea13da0fe86df1abf34de52841e367035170c0
Christian Brabandt <cb@256bit.org>
parents:
7803
diff
changeset
|
2568 buf_T *buf, |
41789f16d6b2
commit https://github.com/vim/vim/commit/52ea13da0fe86df1abf34de52841e367035170c0
Christian Brabandt <cb@256bit.org>
parents:
7803
diff
changeset
|
2569 linenr_T lnum, /* append after this line (can be 0) */ |
41789f16d6b2
commit https://github.com/vim/vim/commit/52ea13da0fe86df1abf34de52841e367035170c0
Christian Brabandt <cb@256bit.org>
parents:
7803
diff
changeset
|
2570 char_u *line, /* text of the new line */ |
41789f16d6b2
commit https://github.com/vim/vim/commit/52ea13da0fe86df1abf34de52841e367035170c0
Christian Brabandt <cb@256bit.org>
parents:
7803
diff
changeset
|
2571 colnr_T len, /* length of new line, including NUL, or 0 */ |
41789f16d6b2
commit https://github.com/vim/vim/commit/52ea13da0fe86df1abf34de52841e367035170c0
Christian Brabandt <cb@256bit.org>
parents:
7803
diff
changeset
|
2572 int newfile) /* flag, see above */ |
625 | 2573 { |
2574 if (buf->b_ml.ml_mfp == NULL) | |
2575 return FAIL; | |
2576 | |
2577 if (buf->b_ml.ml_line_lnum != 0) | |
2578 ml_flush_line(buf); | |
2579 return ml_append_int(buf, lnum, line, len, newfile, FALSE); | |
2580 } | |
2581 #endif | |
2582 | |
7 | 2583 static int |
7827
41789f16d6b2
commit https://github.com/vim/vim/commit/52ea13da0fe86df1abf34de52841e367035170c0
Christian Brabandt <cb@256bit.org>
parents:
7803
diff
changeset
|
2584 ml_append_int( |
41789f16d6b2
commit https://github.com/vim/vim/commit/52ea13da0fe86df1abf34de52841e367035170c0
Christian Brabandt <cb@256bit.org>
parents:
7803
diff
changeset
|
2585 buf_T *buf, |
41789f16d6b2
commit https://github.com/vim/vim/commit/52ea13da0fe86df1abf34de52841e367035170c0
Christian Brabandt <cb@256bit.org>
parents:
7803
diff
changeset
|
2586 linenr_T lnum, /* append after this line (can be 0) */ |
41789f16d6b2
commit https://github.com/vim/vim/commit/52ea13da0fe86df1abf34de52841e367035170c0
Christian Brabandt <cb@256bit.org>
parents:
7803
diff
changeset
|
2587 char_u *line, /* text of the new line */ |
41789f16d6b2
commit https://github.com/vim/vim/commit/52ea13da0fe86df1abf34de52841e367035170c0
Christian Brabandt <cb@256bit.org>
parents:
7803
diff
changeset
|
2588 colnr_T len, /* length of line, including NUL, or 0 */ |
41789f16d6b2
commit https://github.com/vim/vim/commit/52ea13da0fe86df1abf34de52841e367035170c0
Christian Brabandt <cb@256bit.org>
parents:
7803
diff
changeset
|
2589 int newfile, /* flag, see above */ |
41789f16d6b2
commit https://github.com/vim/vim/commit/52ea13da0fe86df1abf34de52841e367035170c0
Christian Brabandt <cb@256bit.org>
parents:
7803
diff
changeset
|
2590 int mark) /* mark the new line */ |
7 | 2591 { |
2592 int i; | |
2593 int line_count; /* number of indexes in current block */ | |
2594 int offset; | |
2595 int from, to; | |
2596 int space_needed; /* space needed for new line */ | |
2597 int page_size; | |
2598 int page_count; | |
2599 int db_idx; /* index for lnum in data block */ | |
2600 bhdr_T *hp; | |
2601 memfile_T *mfp; | |
2602 DATA_BL *dp; | |
2603 PTR_BL *pp; | |
2604 infoptr_T *ip; | |
2605 | |
2606 /* lnum out of range */ | |
2607 if (lnum > buf->b_ml.ml_line_count || buf->b_ml.ml_mfp == NULL) | |
2608 return FAIL; | |
2609 | |
2610 if (lowest_marked && lowest_marked > lnum) | |
2611 lowest_marked = lnum + 1; | |
2612 | |
2613 if (len == 0) | |
2614 len = (colnr_T)STRLEN(line) + 1; /* space needed for the text */ | |
2615 space_needed = len + INDEX_SIZE; /* space needed for text + index */ | |
2616 | |
2617 mfp = buf->b_ml.ml_mfp; | |
2618 page_size = mfp->mf_page_size; | |
2619 | |
2620 /* | |
2621 * find the data block containing the previous line | |
2622 * This also fills the stack with the blocks from the root to the data block | |
2623 * This also releases any locked block. | |
2624 */ | |
2625 if ((hp = ml_find_line(buf, lnum == 0 ? (linenr_T)1 : lnum, | |
2626 ML_INSERT)) == NULL) | |
2627 return FAIL; | |
2628 | |
2629 buf->b_ml.ml_flags &= ~ML_EMPTY; | |
2630 | |
2631 if (lnum == 0) /* got line one instead, correct db_idx */ | |
2632 db_idx = -1; /* careful, it is negative! */ | |
2633 else | |
2634 db_idx = lnum - buf->b_ml.ml_locked_low; | |
2635 /* get line count before the insertion */ | |
2636 line_count = buf->b_ml.ml_locked_high - buf->b_ml.ml_locked_low; | |
2637 | |
2638 dp = (DATA_BL *)(hp->bh_data); | |
2639 | |
2640 /* | |
2641 * If | |
2642 * - there is not enough room in the current block | |
2643 * - appending to the last line in the block | |
2644 * - not appending to the last line in the file | |
2645 * insert in front of the next block. | |
2646 */ | |
2647 if ((int)dp->db_free < space_needed && db_idx == line_count - 1 | |
2648 && lnum < buf->b_ml.ml_line_count) | |
2649 { | |
2650 /* | |
2651 * Now that the line is not going to be inserted in the block that we | |
2652 * expected, the line count has to be adjusted in the pointer blocks | |
2653 * by using ml_locked_lineadd. | |
2654 */ | |
2655 --(buf->b_ml.ml_locked_lineadd); | |
2656 --(buf->b_ml.ml_locked_high); | |
2657 if ((hp = ml_find_line(buf, lnum + 1, ML_INSERT)) == NULL) | |
2658 return FAIL; | |
2659 | |
2660 db_idx = -1; /* careful, it is negative! */ | |
2661 /* get line count before the insertion */ | |
2662 line_count = buf->b_ml.ml_locked_high - buf->b_ml.ml_locked_low; | |
2663 CHECK(buf->b_ml.ml_locked_low != lnum + 1, "locked_low != lnum + 1"); | |
2664 | |
2665 dp = (DATA_BL *)(hp->bh_data); | |
2666 } | |
2667 | |
2668 ++buf->b_ml.ml_line_count; | |
2669 | |
2670 if ((int)dp->db_free >= space_needed) /* enough room in data block */ | |
2671 { | |
2672 /* | |
2673 * Insert new line in existing data block, or in data block allocated above. | |
2674 */ | |
2675 dp->db_txt_start -= len; | |
2676 dp->db_free -= space_needed; | |
2677 ++(dp->db_line_count); | |
2678 | |
2679 /* | |
2680 * move the text of the lines that follow to the front | |
2681 * adjust the indexes of the lines that follow | |
2682 */ | |
2683 if (line_count > db_idx + 1) /* if there are following lines */ | |
2684 { | |
2685 /* | |
2686 * Offset is the start of the previous line. | |
2687 * This will become the character just after the new line. | |
2688 */ | |
2689 if (db_idx < 0) | |
2690 offset = dp->db_txt_end; | |
2691 else | |
2692 offset = ((dp->db_index[db_idx]) & DB_INDEX_MASK); | |
2693 mch_memmove((char *)dp + dp->db_txt_start, | |
2694 (char *)dp + dp->db_txt_start + len, | |
2695 (size_t)(offset - (dp->db_txt_start + len))); | |
2696 for (i = line_count - 1; i > db_idx; --i) | |
2697 dp->db_index[i + 1] = dp->db_index[i] - len; | |
2698 dp->db_index[db_idx + 1] = offset - len; | |
2699 } | |
2700 else /* add line at the end */ | |
2701 dp->db_index[db_idx + 1] = dp->db_txt_start; | |
2702 | |
2703 /* | |
2704 * copy the text into the block | |
2705 */ | |
2706 mch_memmove((char *)dp + dp->db_index[db_idx + 1], line, (size_t)len); | |
2707 if (mark) | |
2708 dp->db_index[db_idx + 1] |= DB_MARKED; | |
2709 | |
2710 /* | |
2711 * Mark the block dirty. | |
2712 */ | |
2713 buf->b_ml.ml_flags |= ML_LOCKED_DIRTY; | |
2714 if (!newfile) | |
2715 buf->b_ml.ml_flags |= ML_LOCKED_POS; | |
2716 } | |
2717 else /* not enough space in data block */ | |
2718 { | |
2719 /* | |
2720 * If there is not enough room we have to create a new data block and copy some | |
2721 * lines into it. | |
2722 * Then we have to insert an entry in the pointer block. | |
2723 * If this pointer block also is full, we go up another block, and so on, up | |
2724 * to the root if necessary. | |
2725 * The line counts in the pointer blocks have already been adjusted by | |
2726 * ml_find_line(). | |
2727 */ | |
2728 long line_count_left, line_count_right; | |
2729 int page_count_left, page_count_right; | |
2730 bhdr_T *hp_left; | |
2731 bhdr_T *hp_right; | |
2732 bhdr_T *hp_new; | |
2733 int lines_moved; | |
2734 int data_moved = 0; /* init to shut up gcc */ | |
2735 int total_moved = 0; /* init to shut up gcc */ | |
2736 DATA_BL *dp_right, *dp_left; | |
2737 int stack_idx; | |
2738 int in_left; | |
2739 int lineadd; | |
2740 blocknr_T bnum_left, bnum_right; | |
2741 linenr_T lnum_left, lnum_right; | |
2742 int pb_idx; | |
2743 PTR_BL *pp_new; | |
2744 | |
2745 /* | |
2746 * We are going to allocate a new data block. Depending on the | |
2747 * situation it will be put to the left or right of the existing | |
2748 * block. If possible we put the new line in the left block and move | |
2749 * the lines after it to the right block. Otherwise the new line is | |
2750 * also put in the right block. This method is more efficient when | |
2751 * inserting a lot of lines at one place. | |
2752 */ | |
2753 if (db_idx < 0) /* left block is new, right block is existing */ | |
2754 { | |
2755 lines_moved = 0; | |
2756 in_left = TRUE; | |
2757 /* space_needed does not change */ | |
2758 } | |
2759 else /* left block is existing, right block is new */ | |
2760 { | |
2761 lines_moved = line_count - db_idx - 1; | |
2762 if (lines_moved == 0) | |
2763 in_left = FALSE; /* put new line in right block */ | |
2764 /* space_needed does not change */ | |
2765 else | |
2766 { | |
2767 data_moved = ((dp->db_index[db_idx]) & DB_INDEX_MASK) - | |
2768 dp->db_txt_start; | |
2769 total_moved = data_moved + lines_moved * INDEX_SIZE; | |
2770 if ((int)dp->db_free + total_moved >= space_needed) | |
2771 { | |
2772 in_left = TRUE; /* put new line in left block */ | |
2773 space_needed = total_moved; | |
2774 } | |
2775 else | |
2776 { | |
2777 in_left = FALSE; /* put new line in right block */ | |
2778 space_needed += total_moved; | |
2779 } | |
2780 } | |
2781 } | |
2782 | |
2783 page_count = ((space_needed + HEADER_SIZE) + page_size - 1) / page_size; | |
2784 if ((hp_new = ml_new_data(mfp, newfile, page_count)) == NULL) | |
2785 { | |
2786 /* correct line counts in pointer blocks */ | |
2787 --(buf->b_ml.ml_locked_lineadd); | |
2788 --(buf->b_ml.ml_locked_high); | |
2789 return FAIL; | |
2790 } | |
2791 if (db_idx < 0) /* left block is new */ | |
2792 { | |
2793 hp_left = hp_new; | |
2794 hp_right = hp; | |
2795 line_count_left = 0; | |
2796 line_count_right = line_count; | |
2797 } | |
2798 else /* right block is new */ | |
2799 { | |
2800 hp_left = hp; | |
2801 hp_right = hp_new; | |
2802 line_count_left = line_count; | |
2803 line_count_right = 0; | |
2804 } | |
2805 dp_right = (DATA_BL *)(hp_right->bh_data); | |
2806 dp_left = (DATA_BL *)(hp_left->bh_data); | |
2807 bnum_left = hp_left->bh_bnum; | |
2808 bnum_right = hp_right->bh_bnum; | |
2809 page_count_left = hp_left->bh_page_count; | |
2810 page_count_right = hp_right->bh_page_count; | |
2811 | |
2812 /* | |
2813 * May move the new line into the right/new block. | |
2814 */ | |
2815 if (!in_left) | |
2816 { | |
2817 dp_right->db_txt_start -= len; | |
2818 dp_right->db_free -= len + INDEX_SIZE; | |
2819 dp_right->db_index[0] = dp_right->db_txt_start; | |
2820 if (mark) | |
2821 dp_right->db_index[0] |= DB_MARKED; | |
2822 | |
2823 mch_memmove((char *)dp_right + dp_right->db_txt_start, | |
2824 line, (size_t)len); | |
2825 ++line_count_right; | |
2826 } | |
2827 /* | |
2828 * may move lines from the left/old block to the right/new one. | |
2829 */ | |
2830 if (lines_moved) | |
2831 { | |
2832 /* | |
2833 */ | |
2834 dp_right->db_txt_start -= data_moved; | |
2835 dp_right->db_free -= total_moved; | |
2836 mch_memmove((char *)dp_right + dp_right->db_txt_start, | |
2837 (char *)dp_left + dp_left->db_txt_start, | |
2838 (size_t)data_moved); | |
2839 offset = dp_right->db_txt_start - dp_left->db_txt_start; | |
2840 dp_left->db_txt_start += data_moved; | |
2841 dp_left->db_free += total_moved; | |
2842 | |
2843 /* | |
2844 * update indexes in the new block | |
2845 */ | |
2846 for (to = line_count_right, from = db_idx + 1; | |
2847 from < line_count_left; ++from, ++to) | |
2848 dp_right->db_index[to] = dp->db_index[from] + offset; | |
2849 line_count_right += lines_moved; | |
2850 line_count_left -= lines_moved; | |
2851 } | |
2852 | |
2853 /* | |
2854 * May move the new line into the left (old or new) block. | |
2855 */ | |
2856 if (in_left) | |
2857 { | |
2858 dp_left->db_txt_start -= len; | |
2859 dp_left->db_free -= len + INDEX_SIZE; | |
2860 dp_left->db_index[line_count_left] = dp_left->db_txt_start; | |
2861 if (mark) | |
2862 dp_left->db_index[line_count_left] |= DB_MARKED; | |
2863 mch_memmove((char *)dp_left + dp_left->db_txt_start, | |
2864 line, (size_t)len); | |
2865 ++line_count_left; | |
2866 } | |
2867 | |
2868 if (db_idx < 0) /* left block is new */ | |
2869 { | |
2870 lnum_left = lnum + 1; | |
2871 lnum_right = 0; | |
2872 } | |
2873 else /* right block is new */ | |
2874 { | |
2875 lnum_left = 0; | |
2876 if (in_left) | |
2877 lnum_right = lnum + 2; | |
2878 else | |
2879 lnum_right = lnum + 1; | |
2880 } | |
2881 dp_left->db_line_count = line_count_left; | |
2882 dp_right->db_line_count = line_count_right; | |
2883 | |
2884 /* | |
2885 * release the two data blocks | |
2886 * The new one (hp_new) already has a correct blocknumber. | |
2887 * The old one (hp, in ml_locked) gets a positive blocknumber if | |
2888 * we changed it and we are not editing a new file. | |
2889 */ | |
2890 if (lines_moved || in_left) | |
2891 buf->b_ml.ml_flags |= ML_LOCKED_DIRTY; | |
2892 if (!newfile && db_idx >= 0 && in_left) | |
2893 buf->b_ml.ml_flags |= ML_LOCKED_POS; | |
2894 mf_put(mfp, hp_new, TRUE, FALSE); | |
2895 | |
2896 /* | |
2897 * flush the old data block | |
2898 * set ml_locked_lineadd to 0, because the updating of the | |
2899 * pointer blocks is done below | |
2900 */ | |
2901 lineadd = buf->b_ml.ml_locked_lineadd; | |
2902 buf->b_ml.ml_locked_lineadd = 0; | |
2903 ml_find_line(buf, (linenr_T)0, ML_FLUSH); /* flush data block */ | |
2904 | |
2905 /* | |
2906 * update pointer blocks for the new data block | |
2907 */ | |
2908 for (stack_idx = buf->b_ml.ml_stack_top - 1; stack_idx >= 0; | |
2909 --stack_idx) | |
2910 { | |
2911 ip = &(buf->b_ml.ml_stack[stack_idx]); | |
2912 pb_idx = ip->ip_index; | |
2913 if ((hp = mf_get(mfp, ip->ip_bnum, 1)) == NULL) | |
2914 return FAIL; | |
2915 pp = (PTR_BL *)(hp->bh_data); /* must be pointer block */ | |
2916 if (pp->pb_id != PTR_ID) | |
2917 { | |
2918 EMSG(_("E317: pointer block id wrong 3")); | |
2919 mf_put(mfp, hp, FALSE, FALSE); | |
2920 return FAIL; | |
2921 } | |
2922 /* | |
2923 * TODO: If the pointer block is full and we are adding at the end | |
2924 * try to insert in front of the next block | |
2925 */ | |
2926 /* block not full, add one entry */ | |
2927 if (pp->pb_count < pp->pb_count_max) | |
2928 { | |
2929 if (pb_idx + 1 < (int)pp->pb_count) | |
2930 mch_memmove(&pp->pb_pointer[pb_idx + 2], | |
2931 &pp->pb_pointer[pb_idx + 1], | |
2932 (size_t)(pp->pb_count - pb_idx - 1) * sizeof(PTR_EN)); | |
2933 ++pp->pb_count; | |
2934 pp->pb_pointer[pb_idx].pe_line_count = line_count_left; | |
2935 pp->pb_pointer[pb_idx].pe_bnum = bnum_left; | |
2936 pp->pb_pointer[pb_idx].pe_page_count = page_count_left; | |
2937 pp->pb_pointer[pb_idx + 1].pe_line_count = line_count_right; | |
2938 pp->pb_pointer[pb_idx + 1].pe_bnum = bnum_right; | |
2939 pp->pb_pointer[pb_idx + 1].pe_page_count = page_count_right; | |
2940 | |
2941 if (lnum_left != 0) | |
2942 pp->pb_pointer[pb_idx].pe_old_lnum = lnum_left; | |
2943 if (lnum_right != 0) | |
2944 pp->pb_pointer[pb_idx + 1].pe_old_lnum = lnum_right; | |
2945 | |
2946 mf_put(mfp, hp, TRUE, FALSE); | |
2947 buf->b_ml.ml_stack_top = stack_idx + 1; /* truncate stack */ | |
2948 | |
2949 if (lineadd) | |
2950 { | |
2951 --(buf->b_ml.ml_stack_top); | |
1167 | 2952 /* fix line count for rest of blocks in the stack */ |
7 | 2953 ml_lineadd(buf, lineadd); |
2954 /* fix stack itself */ | |
2955 buf->b_ml.ml_stack[buf->b_ml.ml_stack_top].ip_high += | |
2956 lineadd; | |
2957 ++(buf->b_ml.ml_stack_top); | |
2958 } | |
2959 | |
2960 /* | |
2961 * We are finished, break the loop here. | |
2962 */ | |
2963 break; | |
2964 } | |
2965 else /* pointer block full */ | |
2966 { | |
2967 /* | |
2968 * split the pointer block | |
2969 * allocate a new pointer block | |
2970 * move some of the pointer into the new block | |
2971 * prepare for updating the parent block | |
2972 */ | |
2973 for (;;) /* do this twice when splitting block 1 */ | |
2974 { | |
2975 hp_new = ml_new_ptr(mfp); | |
2976 if (hp_new == NULL) /* TODO: try to fix tree */ | |
2977 return FAIL; | |
2978 pp_new = (PTR_BL *)(hp_new->bh_data); | |
2979 | |
2980 if (hp->bh_bnum != 1) | |
2981 break; | |
2982 | |
2983 /* | |
2984 * if block 1 becomes full the tree is given an extra level | |
2985 * The pointers from block 1 are moved into the new block. | |
2986 * block 1 is updated to point to the new block | |
2987 * then continue to split the new block | |
2988 */ | |
2989 mch_memmove(pp_new, pp, (size_t)page_size); | |
2990 pp->pb_count = 1; | |
2991 pp->pb_pointer[0].pe_bnum = hp_new->bh_bnum; | |
2992 pp->pb_pointer[0].pe_line_count = buf->b_ml.ml_line_count; | |
2993 pp->pb_pointer[0].pe_old_lnum = 1; | |
2994 pp->pb_pointer[0].pe_page_count = 1; | |
2995 mf_put(mfp, hp, TRUE, FALSE); /* release block 1 */ | |
2996 hp = hp_new; /* new block is to be split */ | |
2997 pp = pp_new; | |
2998 CHECK(stack_idx != 0, _("stack_idx should be 0")); | |
2999 ip->ip_index = 0; | |
3000 ++stack_idx; /* do block 1 again later */ | |
3001 } | |
3002 /* | |
3003 * move the pointers after the current one to the new block | |
3004 * If there are none, the new entry will be in the new block. | |
3005 */ | |
3006 total_moved = pp->pb_count - pb_idx - 1; | |
3007 if (total_moved) | |
3008 { | |
3009 mch_memmove(&pp_new->pb_pointer[0], | |
3010 &pp->pb_pointer[pb_idx + 1], | |
3011 (size_t)(total_moved) * sizeof(PTR_EN)); | |
3012 pp_new->pb_count = total_moved; | |
3013 pp->pb_count -= total_moved - 1; | |
3014 pp->pb_pointer[pb_idx + 1].pe_bnum = bnum_right; | |
3015 pp->pb_pointer[pb_idx + 1].pe_line_count = line_count_right; | |
3016 pp->pb_pointer[pb_idx + 1].pe_page_count = page_count_right; | |
3017 if (lnum_right) | |
3018 pp->pb_pointer[pb_idx + 1].pe_old_lnum = lnum_right; | |
3019 } | |
3020 else | |
3021 { | |
3022 pp_new->pb_count = 1; | |
3023 pp_new->pb_pointer[0].pe_bnum = bnum_right; | |
3024 pp_new->pb_pointer[0].pe_line_count = line_count_right; | |
3025 pp_new->pb_pointer[0].pe_page_count = page_count_right; | |
3026 pp_new->pb_pointer[0].pe_old_lnum = lnum_right; | |
3027 } | |
3028 pp->pb_pointer[pb_idx].pe_bnum = bnum_left; | |
3029 pp->pb_pointer[pb_idx].pe_line_count = line_count_left; | |
3030 pp->pb_pointer[pb_idx].pe_page_count = page_count_left; | |
3031 if (lnum_left) | |
3032 pp->pb_pointer[pb_idx].pe_old_lnum = lnum_left; | |
3033 lnum_left = 0; | |
3034 lnum_right = 0; | |
3035 | |
3036 /* | |
3037 * recompute line counts | |
3038 */ | |
3039 line_count_right = 0; | |
3040 for (i = 0; i < (int)pp_new->pb_count; ++i) | |
3041 line_count_right += pp_new->pb_pointer[i].pe_line_count; | |
3042 line_count_left = 0; | |
3043 for (i = 0; i < (int)pp->pb_count; ++i) | |
3044 line_count_left += pp->pb_pointer[i].pe_line_count; | |
3045 | |
3046 bnum_left = hp->bh_bnum; | |
3047 bnum_right = hp_new->bh_bnum; | |
3048 page_count_left = 1; | |
3049 page_count_right = 1; | |
3050 mf_put(mfp, hp, TRUE, FALSE); | |
3051 mf_put(mfp, hp_new, TRUE, FALSE); | |
3052 } | |
3053 } | |
3054 | |
3055 /* | |
3056 * Safety check: fallen out of for loop? | |
3057 */ | |
3058 if (stack_idx < 0) | |
3059 { | |
3060 EMSG(_("E318: Updated too many blocks?")); | |
3061 buf->b_ml.ml_stack_top = 0; /* invalidate stack */ | |
3062 } | |
3063 } | |
3064 | |
3065 #ifdef FEAT_BYTEOFF | |
3066 /* The line was inserted below 'lnum' */ | |
3067 ml_updatechunk(buf, lnum + 1, (long)len, ML_CHNK_ADDLINE); | |
3068 #endif | |
3069 #ifdef FEAT_NETBEANS_INTG | |
2210 | 3070 if (netbeans_active()) |
7 | 3071 { |
3072 if (STRLEN(line) > 0) | |
835 | 3073 netbeans_inserted(buf, lnum+1, (colnr_T)0, line, (int)STRLEN(line)); |
34 | 3074 netbeans_inserted(buf, lnum+1, (colnr_T)STRLEN(line), |
7 | 3075 (char_u *)"\n", 1); |
3076 } | |
3077 #endif | |
3078 return OK; | |
3079 } | |
3080 | |
3081 /* | |
625 | 3082 * Replace line lnum, with buffering, in current buffer. |
7 | 3083 * |
720 | 3084 * If "copy" is TRUE, make a copy of the line, otherwise the line has been |
7 | 3085 * copied to allocated memory already. |
3086 * | |
3087 * Check: The caller of this function should probably also call | |
3088 * changed_lines(), unless update_screen(NOT_VALID) is used. | |
3089 * | |
3090 * return FAIL for failure, OK otherwise | |
3091 */ | |
3092 int | |
7827
41789f16d6b2
commit https://github.com/vim/vim/commit/52ea13da0fe86df1abf34de52841e367035170c0
Christian Brabandt <cb@256bit.org>
parents:
7803
diff
changeset
|
3093 ml_replace(linenr_T lnum, char_u *line, int copy) |
7 | 3094 { |
3095 if (line == NULL) /* just checking... */ | |
3096 return FAIL; | |
3097 | |
3098 /* When starting up, we might still need to create the memfile */ | |
2394
a3aca345aafa
Add the 'undoreload' option to be able to undo a file reload.
Bram Moolenaar <bram@vim.org>
parents:
2360
diff
changeset
|
3099 if (curbuf->b_ml.ml_mfp == NULL && open_buffer(FALSE, NULL, 0) == FAIL) |
7 | 3100 return FAIL; |
3101 | |
3102 if (copy && (line = vim_strsave(line)) == NULL) /* allocate memory */ | |
3103 return FAIL; | |
3104 #ifdef FEAT_NETBEANS_INTG | |
2210 | 3105 if (netbeans_active()) |
7 | 3106 { |
3107 netbeans_removed(curbuf, lnum, 0, (long)STRLEN(ml_get(lnum))); | |
835 | 3108 netbeans_inserted(curbuf, lnum, 0, line, (int)STRLEN(line)); |
7 | 3109 } |
3110 #endif | |
3111 if (curbuf->b_ml.ml_line_lnum != lnum) /* other line buffered */ | |
3112 ml_flush_line(curbuf); /* flush it */ | |
3113 else if (curbuf->b_ml.ml_flags & ML_LINE_DIRTY) /* same line allocated */ | |
3114 vim_free(curbuf->b_ml.ml_line_ptr); /* free it */ | |
3115 curbuf->b_ml.ml_line_ptr = line; | |
3116 curbuf->b_ml.ml_line_lnum = lnum; | |
3117 curbuf->b_ml.ml_flags = (curbuf->b_ml.ml_flags | ML_LINE_DIRTY) & ~ML_EMPTY; | |
3118 | |
3119 return OK; | |
3120 } | |
3121 | |
3122 /* | |
625 | 3123 * Delete line 'lnum' in the current buffer. |
7 | 3124 * |
3125 * Check: The caller of this function should probably also call | |
3126 * deleted_lines() after this. | |
3127 * | |
3128 * return FAIL for failure, OK otherwise | |
3129 */ | |
3130 int | |
7827
41789f16d6b2
commit https://github.com/vim/vim/commit/52ea13da0fe86df1abf34de52841e367035170c0
Christian Brabandt <cb@256bit.org>
parents:
7803
diff
changeset
|
3131 ml_delete(linenr_T lnum, int message) |
7 | 3132 { |
3133 ml_flush_line(curbuf); | |
3134 return ml_delete_int(curbuf, lnum, message); | |
3135 } | |
3136 | |
3137 static int | |
7827
41789f16d6b2
commit https://github.com/vim/vim/commit/52ea13da0fe86df1abf34de52841e367035170c0
Christian Brabandt <cb@256bit.org>
parents:
7803
diff
changeset
|
3138 ml_delete_int(buf_T *buf, linenr_T lnum, int message) |
7 | 3139 { |
3140 bhdr_T *hp; | |
3141 memfile_T *mfp; | |
3142 DATA_BL *dp; | |
3143 PTR_BL *pp; | |
3144 infoptr_T *ip; | |
3145 int count; /* number of entries in block */ | |
3146 int idx; | |
3147 int stack_idx; | |
3148 int text_start; | |
3149 int line_start; | |
3150 long line_size; | |
3151 int i; | |
3152 | |
3153 if (lnum < 1 || lnum > buf->b_ml.ml_line_count) | |
3154 return FAIL; | |
3155 | |
3156 if (lowest_marked && lowest_marked > lnum) | |
3157 lowest_marked--; | |
3158 | |
3159 /* | |
3160 * If the file becomes empty the last line is replaced by an empty line. | |
3161 */ | |
3162 if (buf->b_ml.ml_line_count == 1) /* file becomes empty */ | |
3163 { | |
3164 if (message | |
3165 #ifdef FEAT_NETBEANS_INTG | |
3166 && !netbeansSuppressNoLines | |
3167 #endif | |
3168 ) | |
680 | 3169 set_keep_msg((char_u *)_(no_lines_msg), 0); |
3170 | |
4352 | 3171 /* FEAT_BYTEOFF already handled in there, don't worry 'bout it below */ |
7 | 3172 i = ml_replace((linenr_T)1, (char_u *)"", TRUE); |
3173 buf->b_ml.ml_flags |= ML_EMPTY; | |
3174 | |
3175 return i; | |
3176 } | |
3177 | |
3178 /* | |
3179 * find the data block containing the line | |
3180 * This also fills the stack with the blocks from the root to the data block | |
3181 * This also releases any locked block. | |
3182 */ | |
3183 mfp = buf->b_ml.ml_mfp; | |
3184 if (mfp == NULL) | |
3185 return FAIL; | |
3186 | |
3187 if ((hp = ml_find_line(buf, lnum, ML_DELETE)) == NULL) | |
3188 return FAIL; | |
3189 | |
3190 dp = (DATA_BL *)(hp->bh_data); | |
3191 /* compute line count before the delete */ | |
3192 count = (long)(buf->b_ml.ml_locked_high) | |
3193 - (long)(buf->b_ml.ml_locked_low) + 2; | |
3194 idx = lnum - buf->b_ml.ml_locked_low; | |
3195 | |
3196 --buf->b_ml.ml_line_count; | |
3197 | |
3198 line_start = ((dp->db_index[idx]) & DB_INDEX_MASK); | |
3199 if (idx == 0) /* first line in block, text at the end */ | |
3200 line_size = dp->db_txt_end - line_start; | |
3201 else | |
3202 line_size = ((dp->db_index[idx - 1]) & DB_INDEX_MASK) - line_start; | |
3203 | |
3204 #ifdef FEAT_NETBEANS_INTG | |
2210 | 3205 if (netbeans_active()) |
34 | 3206 netbeans_removed(buf, lnum, 0, (long)line_size); |
7 | 3207 #endif |
3208 | |
3209 /* | |
3210 * special case: If there is only one line in the data block it becomes empty. | |
3211 * Then we have to remove the entry, pointing to this data block, from the | |
3212 * pointer block. If this pointer block also becomes empty, we go up another | |
3213 * block, and so on, up to the root if necessary. | |
3214 * The line counts in the pointer blocks have already been adjusted by | |
3215 * ml_find_line(). | |
3216 */ | |
3217 if (count == 1) | |
3218 { | |
3219 mf_free(mfp, hp); /* free the data block */ | |
3220 buf->b_ml.ml_locked = NULL; | |
3221 | |
2823 | 3222 for (stack_idx = buf->b_ml.ml_stack_top - 1; stack_idx >= 0; |
3223 --stack_idx) | |
7 | 3224 { |
3225 buf->b_ml.ml_stack_top = 0; /* stack is invalid when failing */ | |
3226 ip = &(buf->b_ml.ml_stack[stack_idx]); | |
3227 idx = ip->ip_index; | |
3228 if ((hp = mf_get(mfp, ip->ip_bnum, 1)) == NULL) | |
3229 return FAIL; | |
3230 pp = (PTR_BL *)(hp->bh_data); /* must be pointer block */ | |
3231 if (pp->pb_id != PTR_ID) | |
3232 { | |
3233 EMSG(_("E317: pointer block id wrong 4")); | |
3234 mf_put(mfp, hp, FALSE, FALSE); | |
3235 return FAIL; | |
3236 } | |
3237 count = --(pp->pb_count); | |
3238 if (count == 0) /* the pointer block becomes empty! */ | |
3239 mf_free(mfp, hp); | |
3240 else | |
3241 { | |
3242 if (count != idx) /* move entries after the deleted one */ | |
3243 mch_memmove(&pp->pb_pointer[idx], &pp->pb_pointer[idx + 1], | |
3244 (size_t)(count - idx) * sizeof(PTR_EN)); | |
3245 mf_put(mfp, hp, TRUE, FALSE); | |
3246 | |
3247 buf->b_ml.ml_stack_top = stack_idx; /* truncate stack */ | |
1167 | 3248 /* fix line count for rest of blocks in the stack */ |
3249 if (buf->b_ml.ml_locked_lineadd != 0) | |
7 | 3250 { |
3251 ml_lineadd(buf, buf->b_ml.ml_locked_lineadd); | |
3252 buf->b_ml.ml_stack[buf->b_ml.ml_stack_top].ip_high += | |
1167 | 3253 buf->b_ml.ml_locked_lineadd; |
7 | 3254 } |
3255 ++(buf->b_ml.ml_stack_top); | |
3256 | |
3257 break; | |
3258 } | |
3259 } | |
3260 CHECK(stack_idx < 0, _("deleted block 1?")); | |
3261 } | |
3262 else | |
3263 { | |
3264 /* | |
3265 * delete the text by moving the next lines forwards | |
3266 */ | |
3267 text_start = dp->db_txt_start; | |
3268 mch_memmove((char *)dp + text_start + line_size, | |
3269 (char *)dp + text_start, (size_t)(line_start - text_start)); | |
3270 | |
3271 /* | |
3272 * delete the index by moving the next indexes backwards | |
3273 * Adjust the indexes for the text movement. | |
3274 */ | |
3275 for (i = idx; i < count - 1; ++i) | |
3276 dp->db_index[i] = dp->db_index[i + 1] + line_size; | |
3277 | |
3278 dp->db_free += line_size + INDEX_SIZE; | |
3279 dp->db_txt_start += line_size; | |
3280 --(dp->db_line_count); | |
3281 | |
3282 /* | |
3283 * mark the block dirty and make sure it is in the file (for recovery) | |
3284 */ | |
3285 buf->b_ml.ml_flags |= (ML_LOCKED_DIRTY | ML_LOCKED_POS); | |
3286 } | |
3287 | |
3288 #ifdef FEAT_BYTEOFF | |
3289 ml_updatechunk(buf, lnum, line_size, ML_CHNK_DELLINE); | |
3290 #endif | |
3291 return OK; | |
3292 } | |
3293 | |
3294 /* | |
3295 * set the B_MARKED flag for line 'lnum' | |
3296 */ | |
3297 void | |
7827
41789f16d6b2
commit https://github.com/vim/vim/commit/52ea13da0fe86df1abf34de52841e367035170c0
Christian Brabandt <cb@256bit.org>
parents:
7803
diff
changeset
|
3298 ml_setmarked(linenr_T lnum) |
7 | 3299 { |
3300 bhdr_T *hp; | |
3301 DATA_BL *dp; | |
3302 /* invalid line number */ | |
3303 if (lnum < 1 || lnum > curbuf->b_ml.ml_line_count | |
3304 || curbuf->b_ml.ml_mfp == NULL) | |
3305 return; /* give error message? */ | |
3306 | |
3307 if (lowest_marked == 0 || lowest_marked > lnum) | |
3308 lowest_marked = lnum; | |
3309 | |
3310 /* | |
3311 * find the data block containing the line | |
3312 * This also fills the stack with the blocks from the root to the data block | |
3313 * This also releases any locked block. | |
3314 */ | |
3315 if ((hp = ml_find_line(curbuf, lnum, ML_FIND)) == NULL) | |
3316 return; /* give error message? */ | |
3317 | |
3318 dp = (DATA_BL *)(hp->bh_data); | |
3319 dp->db_index[lnum - curbuf->b_ml.ml_locked_low] |= DB_MARKED; | |
3320 curbuf->b_ml.ml_flags |= ML_LOCKED_DIRTY; | |
3321 } | |
3322 | |
3323 /* | |
3324 * find the first line with its B_MARKED flag set | |
3325 */ | |
3326 linenr_T | |
7827
41789f16d6b2
commit https://github.com/vim/vim/commit/52ea13da0fe86df1abf34de52841e367035170c0
Christian Brabandt <cb@256bit.org>
parents:
7803
diff
changeset
|
3327 ml_firstmarked(void) |
7 | 3328 { |
3329 bhdr_T *hp; | |
3330 DATA_BL *dp; | |
3331 linenr_T lnum; | |
3332 int i; | |
3333 | |
3334 if (curbuf->b_ml.ml_mfp == NULL) | |
3335 return (linenr_T) 0; | |
3336 | |
3337 /* | |
3338 * The search starts with lowest_marked line. This is the last line where | |
3339 * a mark was found, adjusted by inserting/deleting lines. | |
3340 */ | |
3341 for (lnum = lowest_marked; lnum <= curbuf->b_ml.ml_line_count; ) | |
3342 { | |
3343 /* | |
3344 * Find the data block containing the line. | |
3345 * This also fills the stack with the blocks from the root to the data | |
3346 * block This also releases any locked block. | |
3347 */ | |
3348 if ((hp = ml_find_line(curbuf, lnum, ML_FIND)) == NULL) | |
3349 return (linenr_T)0; /* give error message? */ | |
3350 | |
3351 dp = (DATA_BL *)(hp->bh_data); | |
3352 | |
3353 for (i = lnum - curbuf->b_ml.ml_locked_low; | |
3354 lnum <= curbuf->b_ml.ml_locked_high; ++i, ++lnum) | |
3355 if ((dp->db_index[i]) & DB_MARKED) | |
3356 { | |
3357 (dp->db_index[i]) &= DB_INDEX_MASK; | |
3358 curbuf->b_ml.ml_flags |= ML_LOCKED_DIRTY; | |
3359 lowest_marked = lnum + 1; | |
3360 return lnum; | |
3361 } | |
3362 } | |
3363 | |
3364 return (linenr_T) 0; | |
3365 } | |
3366 | |
3367 /* | |
3368 * clear all DB_MARKED flags | |
3369 */ | |
3370 void | |
7827
41789f16d6b2
commit https://github.com/vim/vim/commit/52ea13da0fe86df1abf34de52841e367035170c0
Christian Brabandt <cb@256bit.org>
parents:
7803
diff
changeset
|
3371 ml_clearmarked(void) |
7 | 3372 { |
3373 bhdr_T *hp; | |
3374 DATA_BL *dp; | |
3375 linenr_T lnum; | |
3376 int i; | |
3377 | |
3378 if (curbuf->b_ml.ml_mfp == NULL) /* nothing to do */ | |
3379 return; | |
3380 | |
3381 /* | |
3382 * The search starts with line lowest_marked. | |
3383 */ | |
3384 for (lnum = lowest_marked; lnum <= curbuf->b_ml.ml_line_count; ) | |
3385 { | |
3386 /* | |
3387 * Find the data block containing the line. | |
3388 * This also fills the stack with the blocks from the root to the data | |
3389 * block and releases any locked block. | |
3390 */ | |
3391 if ((hp = ml_find_line(curbuf, lnum, ML_FIND)) == NULL) | |
3392 return; /* give error message? */ | |
3393 | |
3394 dp = (DATA_BL *)(hp->bh_data); | |
3395 | |
3396 for (i = lnum - curbuf->b_ml.ml_locked_low; | |
3397 lnum <= curbuf->b_ml.ml_locked_high; ++i, ++lnum) | |
3398 if ((dp->db_index[i]) & DB_MARKED) | |
3399 { | |
3400 (dp->db_index[i]) &= DB_INDEX_MASK; | |
3401 curbuf->b_ml.ml_flags |= ML_LOCKED_DIRTY; | |
3402 } | |
3403 } | |
3404 | |
3405 lowest_marked = 0; | |
3406 return; | |
3407 } | |
3408 | |
3409 /* | |
3410 * flush ml_line if necessary | |
3411 */ | |
3412 static void | |
7827
41789f16d6b2
commit https://github.com/vim/vim/commit/52ea13da0fe86df1abf34de52841e367035170c0
Christian Brabandt <cb@256bit.org>
parents:
7803
diff
changeset
|
3413 ml_flush_line(buf_T *buf) |
7 | 3414 { |
3415 bhdr_T *hp; | |
3416 DATA_BL *dp; | |
3417 linenr_T lnum; | |
3418 char_u *new_line; | |
3419 char_u *old_line; | |
3420 colnr_T new_len; | |
3421 int old_len; | |
3422 int extra; | |
3423 int idx; | |
3424 int start; | |
3425 int count; | |
3426 int i; | |
2075
903fcd726d90
updated for version 7.2.359
Bram Moolenaar <bram@zimbu.org>
parents:
2003
diff
changeset
|
3427 static int entered = FALSE; |
7 | 3428 |
3429 if (buf->b_ml.ml_line_lnum == 0 || buf->b_ml.ml_mfp == NULL) | |
3430 return; /* nothing to do */ | |
3431 | |
3432 if (buf->b_ml.ml_flags & ML_LINE_DIRTY) | |
3433 { | |
2075
903fcd726d90
updated for version 7.2.359
Bram Moolenaar <bram@zimbu.org>
parents:
2003
diff
changeset
|
3434 /* This code doesn't work recursively, but Netbeans may call back here |
903fcd726d90
updated for version 7.2.359
Bram Moolenaar <bram@zimbu.org>
parents:
2003
diff
changeset
|
3435 * when obtaining the cursor position. */ |
903fcd726d90
updated for version 7.2.359
Bram Moolenaar <bram@zimbu.org>
parents:
2003
diff
changeset
|
3436 if (entered) |
903fcd726d90
updated for version 7.2.359
Bram Moolenaar <bram@zimbu.org>
parents:
2003
diff
changeset
|
3437 return; |
903fcd726d90
updated for version 7.2.359
Bram Moolenaar <bram@zimbu.org>
parents:
2003
diff
changeset
|
3438 entered = TRUE; |
903fcd726d90
updated for version 7.2.359
Bram Moolenaar <bram@zimbu.org>
parents:
2003
diff
changeset
|
3439 |
7 | 3440 lnum = buf->b_ml.ml_line_lnum; |
3441 new_line = buf->b_ml.ml_line_ptr; | |
3442 | |
3443 hp = ml_find_line(buf, lnum, ML_FIND); | |
3444 if (hp == NULL) | |
3445 EMSGN(_("E320: Cannot find line %ld"), lnum); | |
3446 else | |
3447 { | |
3448 dp = (DATA_BL *)(hp->bh_data); | |
3449 idx = lnum - buf->b_ml.ml_locked_low; | |
3450 start = ((dp->db_index[idx]) & DB_INDEX_MASK); | |
3451 old_line = (char_u *)dp + start; | |
3452 if (idx == 0) /* line is last in block */ | |
3453 old_len = dp->db_txt_end - start; | |
3454 else /* text of previous line follows */ | |
3455 old_len = (dp->db_index[idx - 1] & DB_INDEX_MASK) - start; | |
3456 new_len = (colnr_T)STRLEN(new_line) + 1; | |
3457 extra = new_len - old_len; /* negative if lines gets smaller */ | |
3458 | |
3459 /* | |
3460 * if new line fits in data block, replace directly | |
3461 */ | |
3462 if ((int)dp->db_free >= extra) | |
3463 { | |
3464 /* if the length changes and there are following lines */ | |
3465 count = buf->b_ml.ml_locked_high - buf->b_ml.ml_locked_low + 1; | |
3466 if (extra != 0 && idx < count - 1) | |
3467 { | |
3468 /* move text of following lines */ | |
3469 mch_memmove((char *)dp + dp->db_txt_start - extra, | |
3470 (char *)dp + dp->db_txt_start, | |
3471 (size_t)(start - dp->db_txt_start)); | |
3472 | |
3473 /* adjust pointers of this and following lines */ | |
3474 for (i = idx + 1; i < count; ++i) | |
3475 dp->db_index[i] -= extra; | |
3476 } | |
3477 dp->db_index[idx] -= extra; | |
3478 | |
3479 /* adjust free space */ | |
3480 dp->db_free -= extra; | |
3481 dp->db_txt_start -= extra; | |
3482 | |
3483 /* copy new line into the data block */ | |
3484 mch_memmove(old_line - extra, new_line, (size_t)new_len); | |
3485 buf->b_ml.ml_flags |= (ML_LOCKED_DIRTY | ML_LOCKED_POS); | |
3486 #ifdef FEAT_BYTEOFF | |
3487 /* The else case is already covered by the insert and delete */ | |
3488 ml_updatechunk(buf, lnum, (long)extra, ML_CHNK_UPDLINE); | |
3489 #endif | |
3490 } | |
3491 else | |
3492 { | |
3493 /* | |
3494 * Cannot do it in one data block: Delete and append. | |
3495 * Append first, because ml_delete_int() cannot delete the | |
3496 * last line in a buffer, which causes trouble for a buffer | |
3497 * that has only one line. | |
3498 * Don't forget to copy the mark! | |
3499 */ | |
3500 /* How about handling errors??? */ | |
3501 (void)ml_append_int(buf, lnum, new_line, new_len, FALSE, | |
3502 (dp->db_index[idx] & DB_MARKED)); | |
3503 (void)ml_delete_int(buf, lnum, FALSE); | |
3504 } | |
3505 } | |
3506 vim_free(new_line); | |
2075
903fcd726d90
updated for version 7.2.359
Bram Moolenaar <bram@zimbu.org>
parents:
2003
diff
changeset
|
3507 |
903fcd726d90
updated for version 7.2.359
Bram Moolenaar <bram@zimbu.org>
parents:
2003
diff
changeset
|
3508 entered = FALSE; |
7 | 3509 } |
3510 | |
3511 buf->b_ml.ml_line_lnum = 0; | |
3512 } | |
3513 | |
3514 /* | |
3515 * create a new, empty, data block | |
3516 */ | |
3517 static bhdr_T * | |
7827
41789f16d6b2
commit https://github.com/vim/vim/commit/52ea13da0fe86df1abf34de52841e367035170c0
Christian Brabandt <cb@256bit.org>
parents:
7803
diff
changeset
|
3518 ml_new_data(memfile_T *mfp, int negative, int page_count) |
7 | 3519 { |
3520 bhdr_T *hp; | |
3521 DATA_BL *dp; | |
3522 | |
3523 if ((hp = mf_new(mfp, negative, page_count)) == NULL) | |
3524 return NULL; | |
3525 | |
3526 dp = (DATA_BL *)(hp->bh_data); | |
3527 dp->db_id = DATA_ID; | |
3528 dp->db_txt_start = dp->db_txt_end = page_count * mfp->mf_page_size; | |
3529 dp->db_free = dp->db_txt_start - HEADER_SIZE; | |
3530 dp->db_line_count = 0; | |
3531 | |
3532 return hp; | |
3533 } | |
3534 | |
3535 /* | |
3536 * create a new, empty, pointer block | |
3537 */ | |
3538 static bhdr_T * | |
7827
41789f16d6b2
commit https://github.com/vim/vim/commit/52ea13da0fe86df1abf34de52841e367035170c0
Christian Brabandt <cb@256bit.org>
parents:
7803
diff
changeset
|
3539 ml_new_ptr(memfile_T *mfp) |
7 | 3540 { |
3541 bhdr_T *hp; | |
3542 PTR_BL *pp; | |
3543 | |
3544 if ((hp = mf_new(mfp, FALSE, 1)) == NULL) | |
3545 return NULL; | |
3546 | |
3547 pp = (PTR_BL *)(hp->bh_data); | |
3548 pp->pb_id = PTR_ID; | |
3549 pp->pb_count = 0; | |
2240
6b4879aea261
Add test for gettabvar() and settabvar().
Bram Moolenaar <bram@vim.org>
parents:
2221
diff
changeset
|
3550 pp->pb_count_max = (short_u)((mfp->mf_page_size - sizeof(PTR_BL)) |
6b4879aea261
Add test for gettabvar() and settabvar().
Bram Moolenaar <bram@vim.org>
parents:
2221
diff
changeset
|
3551 / sizeof(PTR_EN) + 1); |
7 | 3552 |
3553 return hp; | |
3554 } | |
3555 | |
3556 /* | |
3557 * lookup line 'lnum' in a memline | |
3558 * | |
3559 * action: if ML_DELETE or ML_INSERT the line count is updated while searching | |
3560 * if ML_FLUSH only flush a locked block | |
3561 * if ML_FIND just find the line | |
3562 * | |
3563 * If the block was found it is locked and put in ml_locked. | |
3564 * The stack is updated to lead to the locked block. The ip_high field in | |
3565 * the stack is updated to reflect the last line in the block AFTER the | |
3566 * insert or delete, also if the pointer block has not been updated yet. But | |
1167 | 3567 * if ml_locked != NULL ml_locked_lineadd must be added to ip_high. |
7 | 3568 * |
3569 * return: NULL for failure, pointer to block header otherwise | |
3570 */ | |
3571 static bhdr_T * | |
7827
41789f16d6b2
commit https://github.com/vim/vim/commit/52ea13da0fe86df1abf34de52841e367035170c0
Christian Brabandt <cb@256bit.org>
parents:
7803
diff
changeset
|
3572 ml_find_line(buf_T *buf, linenr_T lnum, int action) |
7 | 3573 { |
3574 DATA_BL *dp; | |
3575 PTR_BL *pp; | |
3576 infoptr_T *ip; | |
3577 bhdr_T *hp; | |
3578 memfile_T *mfp; | |
3579 linenr_T t; | |
3580 blocknr_T bnum, bnum2; | |
3581 int dirty; | |
3582 linenr_T low, high; | |
3583 int top; | |
3584 int page_count; | |
3585 int idx; | |
3586 | |
3587 mfp = buf->b_ml.ml_mfp; | |
3588 | |
3589 /* | |
3590 * If there is a locked block check if the wanted line is in it. | |
3591 * If not, flush and release the locked block. | |
3592 * Don't do this for ML_INSERT_SAME, because the stack need to be updated. | |
3593 * Don't do this for ML_FLUSH, because we want to flush the locked block. | |
1066 | 3594 * Don't do this when 'swapfile' is reset, we want to load all the blocks. |
7 | 3595 */ |
3596 if (buf->b_ml.ml_locked) | |
3597 { | |
1066 | 3598 if (ML_SIMPLE(action) |
3599 && buf->b_ml.ml_locked_low <= lnum | |
3600 && buf->b_ml.ml_locked_high >= lnum | |
3601 && !mf_dont_release) | |
7 | 3602 { |
1066 | 3603 /* remember to update pointer blocks and stack later */ |
7 | 3604 if (action == ML_INSERT) |
3605 { | |
3606 ++(buf->b_ml.ml_locked_lineadd); | |
3607 ++(buf->b_ml.ml_locked_high); | |
3608 } | |
3609 else if (action == ML_DELETE) | |
3610 { | |
3611 --(buf->b_ml.ml_locked_lineadd); | |
3612 --(buf->b_ml.ml_locked_high); | |
3613 } | |
3614 return (buf->b_ml.ml_locked); | |
3615 } | |
3616 | |
3617 mf_put(mfp, buf->b_ml.ml_locked, buf->b_ml.ml_flags & ML_LOCKED_DIRTY, | |
3618 buf->b_ml.ml_flags & ML_LOCKED_POS); | |
3619 buf->b_ml.ml_locked = NULL; | |
3620 | |
1167 | 3621 /* |
3622 * If lines have been added or deleted in the locked block, need to | |
3623 * update the line count in pointer blocks. | |
3624 */ | |
3625 if (buf->b_ml.ml_locked_lineadd != 0) | |
7 | 3626 ml_lineadd(buf, buf->b_ml.ml_locked_lineadd); |
3627 } | |
3628 | |
3629 if (action == ML_FLUSH) /* nothing else to do */ | |
3630 return NULL; | |
3631 | |
3632 bnum = 1; /* start at the root of the tree */ | |
3633 page_count = 1; | |
3634 low = 1; | |
3635 high = buf->b_ml.ml_line_count; | |
3636 | |
3637 if (action == ML_FIND) /* first try stack entries */ | |
3638 { | |
3639 for (top = buf->b_ml.ml_stack_top - 1; top >= 0; --top) | |
3640 { | |
3641 ip = &(buf->b_ml.ml_stack[top]); | |
3642 if (ip->ip_low <= lnum && ip->ip_high >= lnum) | |
3643 { | |
3644 bnum = ip->ip_bnum; | |
3645 low = ip->ip_low; | |
3646 high = ip->ip_high; | |
3647 buf->b_ml.ml_stack_top = top; /* truncate stack at prev entry */ | |
3648 break; | |
3649 } | |
3650 } | |
3651 if (top < 0) | |
3652 buf->b_ml.ml_stack_top = 0; /* not found, start at the root */ | |
3653 } | |
3654 else /* ML_DELETE or ML_INSERT */ | |
3655 buf->b_ml.ml_stack_top = 0; /* start at the root */ | |
3656 | |
3657 /* | |
3658 * search downwards in the tree until a data block is found | |
3659 */ | |
3660 for (;;) | |
3661 { | |
3662 if ((hp = mf_get(mfp, bnum, page_count)) == NULL) | |
3663 goto error_noblock; | |
3664 | |
3665 /* | |
3666 * update high for insert/delete | |
3667 */ | |
3668 if (action == ML_INSERT) | |
3669 ++high; | |
3670 else if (action == ML_DELETE) | |
3671 --high; | |
3672 | |
3673 dp = (DATA_BL *)(hp->bh_data); | |
3674 if (dp->db_id == DATA_ID) /* data block */ | |
3675 { | |
3676 buf->b_ml.ml_locked = hp; | |
3677 buf->b_ml.ml_locked_low = low; | |
3678 buf->b_ml.ml_locked_high = high; | |
3679 buf->b_ml.ml_locked_lineadd = 0; | |
3680 buf->b_ml.ml_flags &= ~(ML_LOCKED_DIRTY | ML_LOCKED_POS); | |
3681 return hp; | |
3682 } | |
3683 | |
3684 pp = (PTR_BL *)(dp); /* must be pointer block */ | |
3685 if (pp->pb_id != PTR_ID) | |
3686 { | |
3687 EMSG(_("E317: pointer block id wrong")); | |
3688 goto error_block; | |
3689 } | |
3690 | |
3691 if ((top = ml_add_stack(buf)) < 0) /* add new entry to stack */ | |
3692 goto error_block; | |
3693 ip = &(buf->b_ml.ml_stack[top]); | |
3694 ip->ip_bnum = bnum; | |
3695 ip->ip_low = low; | |
3696 ip->ip_high = high; | |
3697 ip->ip_index = -1; /* index not known yet */ | |
3698 | |
3699 dirty = FALSE; | |
3700 for (idx = 0; idx < (int)pp->pb_count; ++idx) | |
3701 { | |
3702 t = pp->pb_pointer[idx].pe_line_count; | |
3703 CHECK(t == 0, _("pe_line_count is zero")); | |
3704 if ((low += t) > lnum) | |
3705 { | |
3706 ip->ip_index = idx; | |
3707 bnum = pp->pb_pointer[idx].pe_bnum; | |
3708 page_count = pp->pb_pointer[idx].pe_page_count; | |
3709 high = low - 1; | |
3710 low -= t; | |
3711 | |
3712 /* | |
3713 * a negative block number may have been changed | |
3714 */ | |
3715 if (bnum < 0) | |
3716 { | |
3717 bnum2 = mf_trans_del(mfp, bnum); | |
3718 if (bnum != bnum2) | |
3719 { | |
3720 bnum = bnum2; | |
3721 pp->pb_pointer[idx].pe_bnum = bnum; | |
3722 dirty = TRUE; | |
3723 } | |
3724 } | |
3725 | |
3726 break; | |
3727 } | |
3728 } | |
3729 if (idx >= (int)pp->pb_count) /* past the end: something wrong! */ | |
3730 { | |
3731 if (lnum > buf->b_ml.ml_line_count) | |
3732 EMSGN(_("E322: line number out of range: %ld past the end"), | |
3733 lnum - buf->b_ml.ml_line_count); | |
3734 | |
3735 else | |
3736 EMSGN(_("E323: line count wrong in block %ld"), bnum); | |
3737 goto error_block; | |
3738 } | |
3739 if (action == ML_DELETE) | |
3740 { | |
3741 pp->pb_pointer[idx].pe_line_count--; | |
3742 dirty = TRUE; | |
3743 } | |
3744 else if (action == ML_INSERT) | |
3745 { | |
3746 pp->pb_pointer[idx].pe_line_count++; | |
3747 dirty = TRUE; | |
3748 } | |
3749 mf_put(mfp, hp, dirty, FALSE); | |
3750 } | |
3751 | |
3752 error_block: | |
3753 mf_put(mfp, hp, FALSE, FALSE); | |
3754 error_noblock: | |
2267 | 3755 /* |
3756 * If action is ML_DELETE or ML_INSERT we have to correct the tree for | |
3757 * the incremented/decremented line counts, because there won't be a line | |
3758 * inserted/deleted after all. | |
3759 */ | |
7 | 3760 if (action == ML_DELETE) |
3761 ml_lineadd(buf, 1); | |
3762 else if (action == ML_INSERT) | |
3763 ml_lineadd(buf, -1); | |
3764 buf->b_ml.ml_stack_top = 0; | |
3765 return NULL; | |
3766 } | |
3767 | |
3768 /* | |
3769 * add an entry to the info pointer stack | |
3770 * | |
3771 * return -1 for failure, number of the new entry otherwise | |
3772 */ | |
3773 static int | |
7827
41789f16d6b2
commit https://github.com/vim/vim/commit/52ea13da0fe86df1abf34de52841e367035170c0
Christian Brabandt <cb@256bit.org>
parents:
7803
diff
changeset
|
3774 ml_add_stack(buf_T *buf) |
7 | 3775 { |
3776 int top; | |
3777 infoptr_T *newstack; | |
3778 | |
3779 top = buf->b_ml.ml_stack_top; | |
3780 | |
2267 | 3781 /* may have to increase the stack size */ |
7 | 3782 if (top == buf->b_ml.ml_stack_size) |
3783 { | |
2267 | 3784 CHECK(top > 0, _("Stack size increases")); /* more than 5 levels??? */ |
7 | 3785 |
3786 newstack = (infoptr_T *)alloc((unsigned)sizeof(infoptr_T) * | |
3787 (buf->b_ml.ml_stack_size + STACK_INCR)); | |
3788 if (newstack == NULL) | |
3789 return -1; | |
6989 | 3790 if (top > 0) |
3791 mch_memmove(newstack, buf->b_ml.ml_stack, | |
1624 | 3792 (size_t)top * sizeof(infoptr_T)); |
7 | 3793 vim_free(buf->b_ml.ml_stack); |
3794 buf->b_ml.ml_stack = newstack; | |
3795 buf->b_ml.ml_stack_size += STACK_INCR; | |
3796 } | |
3797 | |
3798 buf->b_ml.ml_stack_top++; | |
3799 return top; | |
3800 } | |
3801 | |
3802 /* | |
3803 * Update the pointer blocks on the stack for inserted/deleted lines. | |
3804 * The stack itself is also updated. | |
3805 * | |
3806 * When a insert/delete line action fails, the line is not inserted/deleted, | |
3807 * but the pointer blocks have already been updated. That is fixed here by | |
3808 * walking through the stack. | |
3809 * | |
3810 * Count is the number of lines added, negative if lines have been deleted. | |
3811 */ | |
3812 static void | |
7827
41789f16d6b2
commit https://github.com/vim/vim/commit/52ea13da0fe86df1abf34de52841e367035170c0
Christian Brabandt <cb@256bit.org>
parents:
7803
diff
changeset
|
3813 ml_lineadd(buf_T *buf, int count) |
7 | 3814 { |
3815 int idx; | |
3816 infoptr_T *ip; | |
3817 PTR_BL *pp; | |
3818 memfile_T *mfp = buf->b_ml.ml_mfp; | |
3819 bhdr_T *hp; | |
3820 | |
3821 for (idx = buf->b_ml.ml_stack_top - 1; idx >= 0; --idx) | |
3822 { | |
3823 ip = &(buf->b_ml.ml_stack[idx]); | |
3824 if ((hp = mf_get(mfp, ip->ip_bnum, 1)) == NULL) | |
3825 break; | |
3826 pp = (PTR_BL *)(hp->bh_data); /* must be pointer block */ | |
3827 if (pp->pb_id != PTR_ID) | |
3828 { | |
3829 mf_put(mfp, hp, FALSE, FALSE); | |
3830 EMSG(_("E317: pointer block id wrong 2")); | |
3831 break; | |
3832 } | |
3833 pp->pb_pointer[ip->ip_index].pe_line_count += count; | |
3834 ip->ip_high += count; | |
3835 mf_put(mfp, hp, TRUE, FALSE); | |
3836 } | |
3837 } | |
3838 | |
2214
f8222d1f9a73
Included patch for persistent undo. Lots of changes and added test.
Bram Moolenaar <bram@vim.org>
parents:
2210
diff
changeset
|
3839 #if defined(HAVE_READLINK) || defined(PROTO) |
594 | 3840 /* |
3841 * Resolve a symlink in the last component of a file name. | |
3842 * Note that f_resolve() does it for every part of the path, we don't do that | |
3843 * here. | |
3844 * If it worked returns OK and the resolved link in "buf[MAXPATHL]". | |
3845 * Otherwise returns FAIL. | |
3846 */ | |
2214
f8222d1f9a73
Included patch for persistent undo. Lots of changes and added test.
Bram Moolenaar <bram@vim.org>
parents:
2210
diff
changeset
|
3847 int |
7827
41789f16d6b2
commit https://github.com/vim/vim/commit/52ea13da0fe86df1abf34de52841e367035170c0
Christian Brabandt <cb@256bit.org>
parents:
7803
diff
changeset
|
3848 resolve_symlink(char_u *fname, char_u *buf) |
594 | 3849 { |
3850 char_u tmp[MAXPATHL]; | |
3851 int ret; | |
3852 int depth = 0; | |
3853 | |
3854 if (fname == NULL) | |
3855 return FAIL; | |
3856 | |
3857 /* Put the result so far in tmp[], starting with the original name. */ | |
3858 vim_strncpy(tmp, fname, MAXPATHL - 1); | |
3859 | |
3860 for (;;) | |
3861 { | |
3862 /* Limit symlink depth to 100, catch recursive loops. */ | |
3863 if (++depth == 100) | |
3864 { | |
3865 EMSG2(_("E773: Symlink loop for \"%s\""), fname); | |
3866 return FAIL; | |
3867 } | |
3868 | |
3869 ret = readlink((char *)tmp, (char *)buf, MAXPATHL - 1); | |
3870 if (ret <= 0) | |
3871 { | |
619 | 3872 if (errno == EINVAL || errno == ENOENT) |
594 | 3873 { |
619 | 3874 /* Found non-symlink or not existing file, stop here. |
1855 | 3875 * When at the first level use the unmodified name, skip the |
594 | 3876 * call to vim_FullName(). */ |
3877 if (depth == 1) | |
3878 return FAIL; | |
3879 | |
3880 /* Use the resolved name in tmp[]. */ | |
3881 break; | |
3882 } | |
3883 | |
3884 /* There must be some error reading links, use original name. */ | |
3885 return FAIL; | |
3886 } | |
3887 buf[ret] = NUL; | |
3888 | |
3889 /* | |
3890 * Check whether the symlink is relative or absolute. | |
3891 * If it's relative, build a new path based on the directory | |
3892 * portion of the filename (if any) and the path the symlink | |
3893 * points to. | |
3894 */ | |
3895 if (mch_isFullName(buf)) | |
3896 STRCPY(tmp, buf); | |
3897 else | |
3898 { | |
3899 char_u *tail; | |
3900 | |
3901 tail = gettail(tmp); | |
3902 if (STRLEN(tail) + STRLEN(buf) >= MAXPATHL) | |
3903 return FAIL; | |
3904 STRCPY(tail, buf); | |
3905 } | |
3906 } | |
3907 | |
3908 /* | |
3909 * Try to resolve the full name of the file so that the swapfile name will | |
3910 * be consistent even when opening a relative symlink from different | |
3911 * working directories. | |
3912 */ | |
3913 return vim_FullName(tmp, buf, MAXPATHL, TRUE); | |
3914 } | |
3915 #endif | |
3916 | |
7 | 3917 /* |
460 | 3918 * Make swap file name out of the file name and a directory name. |
3919 * Returns pointer to allocated memory or NULL. | |
7 | 3920 */ |
460 | 3921 char_u * |
7827
41789f16d6b2
commit https://github.com/vim/vim/commit/52ea13da0fe86df1abf34de52841e367035170c0
Christian Brabandt <cb@256bit.org>
parents:
7803
diff
changeset
|
3922 makeswapname( |
41789f16d6b2
commit https://github.com/vim/vim/commit/52ea13da0fe86df1abf34de52841e367035170c0
Christian Brabandt <cb@256bit.org>
parents:
7803
diff
changeset
|
3923 char_u *fname, |
41789f16d6b2
commit https://github.com/vim/vim/commit/52ea13da0fe86df1abf34de52841e367035170c0
Christian Brabandt <cb@256bit.org>
parents:
7803
diff
changeset
|
3924 char_u *ffname UNUSED, |
41789f16d6b2
commit https://github.com/vim/vim/commit/52ea13da0fe86df1abf34de52841e367035170c0
Christian Brabandt <cb@256bit.org>
parents:
7803
diff
changeset
|
3925 buf_T *buf, |
41789f16d6b2
commit https://github.com/vim/vim/commit/52ea13da0fe86df1abf34de52841e367035170c0
Christian Brabandt <cb@256bit.org>
parents:
7803
diff
changeset
|
3926 char_u *dir_name) |
7 | 3927 { |
3928 char_u *r, *s; | |
2145
de0e7ca61893
updated for version 7.2.427
Bram Moolenaar <bram@zimbu.org>
parents:
2108
diff
changeset
|
3929 char_u *fname_res = fname; |
594 | 3930 #ifdef HAVE_READLINK |
3931 char_u fname_buf[MAXPATHL]; | |
3932 #endif | |
7 | 3933 |
3934 #if defined(UNIX) || defined(WIN3264) /* Need _very_ long file names */ | |
3935 s = dir_name + STRLEN(dir_name); | |
39 | 3936 if (after_pathsep(dir_name, s) && s[-1] == s[-2]) |
7 | 3937 { /* Ends with '//', Use Full path */ |
3938 r = NULL; | |
460 | 3939 if ((s = make_percent_swname(dir_name, fname)) != NULL) |
7 | 3940 { |
3941 r = modname(s, (char_u *)".swp", FALSE); | |
3942 vim_free(s); | |
3943 } | |
3944 return r; | |
3945 } | |
3946 #endif | |
3947 | |
594 | 3948 #ifdef HAVE_READLINK |
3949 /* Expand symlink in the file name, so that we put the swap file with the | |
3950 * actual file instead of with the symlink. */ | |
3951 if (resolve_symlink(fname, fname_buf) == OK) | |
3952 fname_res = fname_buf; | |
3953 #endif | |
3954 | |
7 | 3955 r = buf_modname( |
3956 #ifdef SHORT_FNAME | |
3957 TRUE, | |
3958 #else | |
3959 (buf->b_p_sn || buf->b_shortname), | |
3960 #endif | |
594 | 3961 fname_res, |
7 | 3962 (char_u *) |
2823 | 3963 #if defined(VMS) |
7 | 3964 "_swp", |
3965 #else | |
3966 ".swp", | |
3967 #endif | |
3968 #ifdef SHORT_FNAME /* always 8.3 file name */ | |
3969 FALSE | |
3970 #else | |
3971 /* Prepend a '.' to the swap file name for the current directory. */ | |
3972 dir_name[0] == '.' && dir_name[1] == NUL | |
3973 #endif | |
3974 ); | |
3975 if (r == NULL) /* out of memory */ | |
3976 return NULL; | |
3977 | |
3978 s = get_file_in_dir(r, dir_name); | |
3979 vim_free(r); | |
3980 return s; | |
3981 } | |
3982 | |
3983 /* | |
3984 * Get file name to use for swap file or backup file. | |
3985 * Use the name of the edited file "fname" and an entry in the 'dir' or 'bdir' | |
3986 * option "dname". | |
3987 * - If "dname" is ".", return "fname" (swap file in dir of file). | |
3988 * - If "dname" starts with "./", insert "dname" in "fname" (swap file | |
3989 * relative to dir of file). | |
3990 * - Otherwise, prepend "dname" to the tail of "fname" (swap file in specific | |
3991 * dir). | |
3992 * | |
3993 * The return value is an allocated string and can be NULL. | |
3994 */ | |
3995 char_u * | |
7827
41789f16d6b2
commit https://github.com/vim/vim/commit/52ea13da0fe86df1abf34de52841e367035170c0
Christian Brabandt <cb@256bit.org>
parents:
7803
diff
changeset
|
3996 get_file_in_dir( |
41789f16d6b2
commit https://github.com/vim/vim/commit/52ea13da0fe86df1abf34de52841e367035170c0
Christian Brabandt <cb@256bit.org>
parents:
7803
diff
changeset
|
3997 char_u *fname, |
41789f16d6b2
commit https://github.com/vim/vim/commit/52ea13da0fe86df1abf34de52841e367035170c0
Christian Brabandt <cb@256bit.org>
parents:
7803
diff
changeset
|
3998 char_u *dname) /* don't use "dirname", it is a global for Alpha */ |
7 | 3999 { |
4000 char_u *t; | |
4001 char_u *tail; | |
4002 char_u *retval; | |
4003 int save_char; | |
4004 | |
4005 tail = gettail(fname); | |
4006 | |
4007 if (dname[0] == '.' && dname[1] == NUL) | |
4008 retval = vim_strsave(fname); | |
4009 else if (dname[0] == '.' && vim_ispathsep(dname[1])) | |
4010 { | |
4011 if (tail == fname) /* no path before file name */ | |
4012 retval = concat_fnames(dname + 2, tail, TRUE); | |
4013 else | |
4014 { | |
4015 save_char = *tail; | |
4016 *tail = NUL; | |
4017 t = concat_fnames(fname, dname + 2, TRUE); | |
4018 *tail = save_char; | |
4019 if (t == NULL) /* out of memory */ | |
4020 retval = NULL; | |
4021 else | |
4022 { | |
4023 retval = concat_fnames(t, tail, TRUE); | |
4024 vim_free(t); | |
4025 } | |
4026 } | |
4027 } | |
4028 else | |
4029 retval = concat_fnames(dname, tail, TRUE); | |
4030 | |
5432 | 4031 #ifdef WIN3264 |
4032 if (retval != NULL) | |
4033 for (t = gettail(retval); *t != NUL; mb_ptr_adv(t)) | |
4034 if (*t == ':') | |
4035 *t = '%'; | |
4036 #endif | |
4037 | |
7 | 4038 return retval; |
4039 } | |
4040 | |
7803
37c929c4a073
commit https://github.com/vim/vim/commit/92b8b2d307e34117f146319872010b0ccc9d2713
Christian Brabandt <cb@256bit.org>
parents:
7410
diff
changeset
|
4041 static void attention_message(buf_T *buf, char_u *fname); |
580 | 4042 |
4043 /* | |
4044 * Print the ATTENTION message: info about an existing swap file. | |
4045 */ | |
4046 static void | |
7827
41789f16d6b2
commit https://github.com/vim/vim/commit/52ea13da0fe86df1abf34de52841e367035170c0
Christian Brabandt <cb@256bit.org>
parents:
7803
diff
changeset
|
4047 attention_message( |
41789f16d6b2
commit https://github.com/vim/vim/commit/52ea13da0fe86df1abf34de52841e367035170c0
Christian Brabandt <cb@256bit.org>
parents:
7803
diff
changeset
|
4048 buf_T *buf, /* buffer being edited */ |
41789f16d6b2
commit https://github.com/vim/vim/commit/52ea13da0fe86df1abf34de52841e367035170c0
Christian Brabandt <cb@256bit.org>
parents:
7803
diff
changeset
|
4049 char_u *fname) /* swap file name */ |
580 | 4050 { |
4051 struct stat st; | |
4052 time_t x, sx; | |
1001 | 4053 char *p; |
580 | 4054 |
4055 ++no_wait_return; | |
4056 (void)EMSG(_("E325: ATTENTION")); | |
4057 MSG_PUTS(_("\nFound a swap file by the name \"")); | |
4058 msg_home_replace(fname); | |
4059 MSG_PUTS("\"\n"); | |
4060 sx = swapfile_info(fname); | |
4061 MSG_PUTS(_("While opening file \"")); | |
4062 msg_outtrans(buf->b_fname); | |
4063 MSG_PUTS("\"\n"); | |
4064 if (mch_stat((char *)buf->b_fname, &st) != -1) | |
4065 { | |
4066 MSG_PUTS(_(" dated: ")); | |
4067 x = st.st_mtime; /* Manx C can't do &st.st_mtime */ | |
1001 | 4068 p = ctime(&x); /* includes '\n' */ |
4069 if (p == NULL) | |
4070 MSG_PUTS("(invalid)\n"); | |
4071 else | |
4072 MSG_PUTS(p); | |
580 | 4073 if (sx != 0 && x > sx) |
4074 MSG_PUTS(_(" NEWER than swap file!\n")); | |
4075 } | |
4076 /* Some of these messages are long to allow translation to | |
4077 * other languages. */ | |
7881
e7afe45a202a
commit https://github.com/vim/vim/commit/d9ea9069f5ef5b8b9f9e0d0daecdd124e2dcd818
Christian Brabandt <cb@256bit.org>
parents:
7827
diff
changeset
|
4078 MSG_PUTS(_("\n(1) Another program may be editing the same file. If this is the case,\n be careful not to end up with two different instances of the same\n file when making changes. Quit, or continue with caution.\n")); |
2703 | 4079 MSG_PUTS(_("(2) An edit session for this file crashed.\n")); |
580 | 4080 MSG_PUTS(_(" If this is the case, use \":recover\" or \"vim -r ")); |
4081 msg_outtrans(buf->b_fname); | |
4082 MSG_PUTS(_("\"\n to recover the changes (see \":help recovery\").\n")); | |
4083 MSG_PUTS(_(" If you did this already, delete the swap file \"")); | |
4084 msg_outtrans(fname); | |
4085 MSG_PUTS(_("\"\n to avoid this message.\n")); | |
4086 cmdline_row = msg_row; | |
4087 --no_wait_return; | |
4088 } | |
4089 | |
4090 #ifdef FEAT_AUTOCMD | |
7803
37c929c4a073
commit https://github.com/vim/vim/commit/92b8b2d307e34117f146319872010b0ccc9d2713
Christian Brabandt <cb@256bit.org>
parents:
7410
diff
changeset
|
4091 static int do_swapexists(buf_T *buf, char_u *fname); |
580 | 4092 |
4093 /* | |
4094 * Trigger the SwapExists autocommands. | |
4095 * Returns a value for equivalent to do_dialog() (see below): | |
4096 * 0: still need to ask for a choice | |
4097 * 1: open read-only | |
4098 * 2: edit anyway | |
4099 * 3: recover | |
4100 * 4: delete it | |
4101 * 5: quit | |
4102 * 6: abort | |
4103 */ | |
4104 static int | |
7827
41789f16d6b2
commit https://github.com/vim/vim/commit/52ea13da0fe86df1abf34de52841e367035170c0
Christian Brabandt <cb@256bit.org>
parents:
7803
diff
changeset
|
4105 do_swapexists(buf_T *buf, char_u *fname) |
580 | 4106 { |
4107 set_vim_var_string(VV_SWAPNAME, fname, -1); | |
4108 set_vim_var_string(VV_SWAPCHOICE, NULL, -1); | |
4109 | |
4110 /* Trigger SwapExists autocommands with <afile> set to the file being | |
1856 | 4111 * edited. Disallow changing directory here. */ |
4112 ++allbuf_lock; | |
580 | 4113 apply_autocmds(EVENT_SWAPEXISTS, buf->b_fname, NULL, FALSE, NULL); |
1856 | 4114 --allbuf_lock; |
580 | 4115 |
4116 set_vim_var_string(VV_SWAPNAME, NULL, -1); | |
4117 | |
4118 switch (*get_vim_var_str(VV_SWAPCHOICE)) | |
4119 { | |
4120 case 'o': return 1; | |
4121 case 'e': return 2; | |
4122 case 'r': return 3; | |
4123 case 'd': return 4; | |
4124 case 'q': return 5; | |
4125 case 'a': return 6; | |
4126 } | |
4127 | |
4128 return 0; | |
4129 } | |
4130 #endif | |
4131 | |
7 | 4132 /* |
4133 * Find out what name to use for the swap file for buffer 'buf'. | |
4134 * | |
4135 * Several names are tried to find one that does not exist | |
460 | 4136 * Returns the name in allocated memory or NULL. |
3158 | 4137 * When out of memory "dirp" is set to NULL. |
7 | 4138 * |
4139 * Note: If BASENAMELEN is not correct, you will get error messages for | |
2214
f8222d1f9a73
Included patch for persistent undo. Lots of changes and added test.
Bram Moolenaar <bram@vim.org>
parents:
2210
diff
changeset
|
4140 * not being able to open the swap or undo file |
1856 | 4141 * Note: May trigger SwapExists autocmd, pointers may change! |
7 | 4142 */ |
4143 static char_u * | |
7827
41789f16d6b2
commit https://github.com/vim/vim/commit/52ea13da0fe86df1abf34de52841e367035170c0
Christian Brabandt <cb@256bit.org>
parents:
7803
diff
changeset
|
4144 findswapname( |
41789f16d6b2
commit https://github.com/vim/vim/commit/52ea13da0fe86df1abf34de52841e367035170c0
Christian Brabandt <cb@256bit.org>
parents:
7803
diff
changeset
|
4145 buf_T *buf, |
41789f16d6b2
commit https://github.com/vim/vim/commit/52ea13da0fe86df1abf34de52841e367035170c0
Christian Brabandt <cb@256bit.org>
parents:
7803
diff
changeset
|
4146 char_u **dirp, /* pointer to list of directories */ |
41789f16d6b2
commit https://github.com/vim/vim/commit/52ea13da0fe86df1abf34de52841e367035170c0
Christian Brabandt <cb@256bit.org>
parents:
7803
diff
changeset
|
4147 char_u *old_fname) /* don't give warning for this file name */ |
7 | 4148 { |
4149 char_u *fname; | |
4150 int n; | |
4151 char_u *dir_name; | |
4152 #ifdef AMIGA | |
4153 BPTR fh; | |
4154 #endif | |
4155 #ifndef SHORT_FNAME | |
4156 int r; | |
4157 #endif | |
5432 | 4158 char_u *buf_fname = buf->b_fname; |
7 | 4159 |
7410
08e62c4fc17d
commit https://github.com/vim/vim/commit/53076830fea6df737455523f7e235bfe4f79864d
Christian Brabandt <cb@256bit.org>
parents:
7408
diff
changeset
|
4160 #if !defined(SHORT_FNAME) && !defined(UNIX) |
7 | 4161 # define CREATE_DUMMY_FILE |
4162 FILE *dummyfd = NULL; | |
4163 | |
5432 | 4164 # ifdef WIN3264 |
4165 if (buf_fname != NULL && !mch_isFullName(buf_fname) | |
4166 && vim_strchr(gettail(buf_fname), ':')) | |
4167 { | |
4168 char_u *t; | |
4169 | |
4170 buf_fname = vim_strsave(buf_fname); | |
4171 if (buf_fname == NULL) | |
4172 buf_fname = buf->b_fname; | |
4173 else | |
4174 for (t = gettail(buf_fname); *t != NUL; mb_ptr_adv(t)) | |
4175 if (*t == ':') | |
4176 *t = '%'; | |
4177 } | |
4178 # endif | |
4179 | |
2214
f8222d1f9a73
Included patch for persistent undo. Lots of changes and added test.
Bram Moolenaar <bram@vim.org>
parents:
2210
diff
changeset
|
4180 /* |
f8222d1f9a73
Included patch for persistent undo. Lots of changes and added test.
Bram Moolenaar <bram@vim.org>
parents:
2210
diff
changeset
|
4181 * If we start editing a new file, e.g. "test.doc", which resides on an |
f8222d1f9a73
Included patch for persistent undo. Lots of changes and added test.
Bram Moolenaar <bram@vim.org>
parents:
2210
diff
changeset
|
4182 * MSDOS compatible filesystem, it is possible that the file |
f8222d1f9a73
Included patch for persistent undo. Lots of changes and added test.
Bram Moolenaar <bram@vim.org>
parents:
2210
diff
changeset
|
4183 * "test.doc.swp" which we create will be exactly the same file. To avoid |
f8222d1f9a73
Included patch for persistent undo. Lots of changes and added test.
Bram Moolenaar <bram@vim.org>
parents:
2210
diff
changeset
|
4184 * this problem we temporarily create "test.doc". Don't do this when the |
f8222d1f9a73
Included patch for persistent undo. Lots of changes and added test.
Bram Moolenaar <bram@vim.org>
parents:
2210
diff
changeset
|
4185 * check below for a 8.3 file name is used. |
f8222d1f9a73
Included patch for persistent undo. Lots of changes and added test.
Bram Moolenaar <bram@vim.org>
parents:
2210
diff
changeset
|
4186 */ |
5432 | 4187 if (!(buf->b_p_sn || buf->b_shortname) && buf_fname != NULL |
4188 && mch_getperm(buf_fname) < 0) | |
4189 dummyfd = mch_fopen((char *)buf_fname, "w"); | |
7 | 4190 #endif |
4191 | |
2214
f8222d1f9a73
Included patch for persistent undo. Lots of changes and added test.
Bram Moolenaar <bram@vim.org>
parents:
2210
diff
changeset
|
4192 /* |
f8222d1f9a73
Included patch for persistent undo. Lots of changes and added test.
Bram Moolenaar <bram@vim.org>
parents:
2210
diff
changeset
|
4193 * Isolate a directory name from *dirp and put it in dir_name. |
f8222d1f9a73
Included patch for persistent undo. Lots of changes and added test.
Bram Moolenaar <bram@vim.org>
parents:
2210
diff
changeset
|
4194 * First allocate some memory to put the directory name in. |
f8222d1f9a73
Included patch for persistent undo. Lots of changes and added test.
Bram Moolenaar <bram@vim.org>
parents:
2210
diff
changeset
|
4195 */ |
7 | 4196 dir_name = alloc((unsigned)STRLEN(*dirp) + 1); |
3158 | 4197 if (dir_name == NULL) |
4198 *dirp = NULL; | |
4199 else | |
7 | 4200 (void)copy_option_part(dirp, dir_name, 31000, ","); |
4201 | |
2214
f8222d1f9a73
Included patch for persistent undo. Lots of changes and added test.
Bram Moolenaar <bram@vim.org>
parents:
2210
diff
changeset
|
4202 /* |
f8222d1f9a73
Included patch for persistent undo. Lots of changes and added test.
Bram Moolenaar <bram@vim.org>
parents:
2210
diff
changeset
|
4203 * we try different names until we find one that does not exist yet |
f8222d1f9a73
Included patch for persistent undo. Lots of changes and added test.
Bram Moolenaar <bram@vim.org>
parents:
2210
diff
changeset
|
4204 */ |
7 | 4205 if (dir_name == NULL) /* out of memory */ |
4206 fname = NULL; | |
4207 else | |
5432 | 4208 fname = makeswapname(buf_fname, buf->b_ffname, buf, dir_name); |
7 | 4209 |
4210 for (;;) | |
4211 { | |
4212 if (fname == NULL) /* must be out of memory */ | |
4213 break; | |
4214 if ((n = (int)STRLEN(fname)) == 0) /* safety check */ | |
4215 { | |
4216 vim_free(fname); | |
4217 fname = NULL; | |
4218 break; | |
4219 } | |
7410
08e62c4fc17d
commit https://github.com/vim/vim/commit/53076830fea6df737455523f7e235bfe4f79864d
Christian Brabandt <cb@256bit.org>
parents:
7408
diff
changeset
|
4220 #if defined(UNIX) && !defined(SHORT_FNAME) |
7 | 4221 /* |
4222 * Some systems have a MS-DOS compatible filesystem that use 8.3 character | |
4223 * file names. If this is the first try and the swap file name does not fit in | |
4224 * 8.3, detect if this is the case, set shortname and try again. | |
4225 */ | |
4226 if (fname[n - 2] == 'w' && fname[n - 1] == 'p' | |
4227 && !(buf->b_p_sn || buf->b_shortname)) | |
4228 { | |
4229 char_u *tail; | |
4230 char_u *fname2; | |
4231 struct stat s1, s2; | |
4232 int f1, f2; | |
4233 int created1 = FALSE, created2 = FALSE; | |
4234 int same = FALSE; | |
4235 | |
4236 /* | |
4237 * Check if swapfile name does not fit in 8.3: | |
4238 * It either contains two dots, is longer than 8 chars, or starts | |
4239 * with a dot. | |
4240 */ | |
5432 | 4241 tail = gettail(buf_fname); |
7 | 4242 if ( vim_strchr(tail, '.') != NULL |
4243 || STRLEN(tail) > (size_t)8 | |
4244 || *gettail(fname) == '.') | |
4245 { | |
4246 fname2 = alloc(n + 2); | |
4247 if (fname2 != NULL) | |
4248 { | |
4249 STRCPY(fname2, fname); | |
4250 /* if fname == "xx.xx.swp", fname2 = "xx.xx.swx" | |
4251 * if fname == ".xx.swp", fname2 = ".xx.swpx" | |
4252 * if fname == "123456789.swp", fname2 = "12345678x.swp" | |
4253 */ | |
4254 if (vim_strchr(tail, '.') != NULL) | |
4255 fname2[n - 1] = 'x'; | |
4256 else if (*gettail(fname) == '.') | |
4257 { | |
4258 fname2[n] = 'x'; | |
4259 fname2[n + 1] = NUL; | |
4260 } | |
4261 else | |
4262 fname2[n - 5] += 1; | |
4263 /* | |
4264 * may need to create the files to be able to use mch_stat() | |
4265 */ | |
4266 f1 = mch_open((char *)fname, O_RDONLY | O_EXTRA, 0); | |
4267 if (f1 < 0) | |
4268 { | |
4269 f1 = mch_open_rw((char *)fname, | |
4270 O_RDWR|O_CREAT|O_EXCL|O_EXTRA); | |
4271 created1 = TRUE; | |
4272 } | |
4273 if (f1 >= 0) | |
4274 { | |
4275 f2 = mch_open((char *)fname2, O_RDONLY | O_EXTRA, 0); | |
4276 if (f2 < 0) | |
4277 { | |
4278 f2 = mch_open_rw((char *)fname2, | |
4279 O_RDWR|O_CREAT|O_EXCL|O_EXTRA); | |
4280 created2 = TRUE; | |
4281 } | |
4282 if (f2 >= 0) | |
4283 { | |
4284 /* | |
4285 * Both files exist now. If mch_stat() returns the | |
4286 * same device and inode they are the same file. | |
4287 */ | |
4288 if (mch_fstat(f1, &s1) != -1 | |
4289 && mch_fstat(f2, &s2) != -1 | |
4290 && s1.st_dev == s2.st_dev | |
4291 && s1.st_ino == s2.st_ino) | |
4292 same = TRUE; | |
4293 close(f2); | |
4294 if (created2) | |
4295 mch_remove(fname2); | |
4296 } | |
4297 close(f1); | |
4298 if (created1) | |
4299 mch_remove(fname); | |
4300 } | |
4301 vim_free(fname2); | |
4302 if (same) | |
4303 { | |
4304 buf->b_shortname = TRUE; | |
4305 vim_free(fname); | |
5432 | 4306 fname = makeswapname(buf_fname, buf->b_ffname, |
460 | 4307 buf, dir_name); |
7 | 4308 continue; /* try again with b_shortname set */ |
4309 } | |
4310 } | |
4311 } | |
4312 } | |
4313 #endif | |
4314 /* | |
4315 * check if the swapfile already exists | |
4316 */ | |
4317 if (mch_getperm(fname) < 0) /* it does not exist */ | |
4318 { | |
4319 #ifdef HAVE_LSTAT | |
4320 struct stat sb; | |
4321 | |
4322 /* | |
4323 * Extra security check: When a swap file is a symbolic link, this | |
4324 * is most likely a symlink attack. | |
4325 */ | |
4326 if (mch_lstat((char *)fname, &sb) < 0) | |
4327 #else | |
4328 # ifdef AMIGA | |
4329 fh = Open((UBYTE *)fname, (long)MODE_NEWFILE); | |
4330 /* | |
4331 * on the Amiga mch_getperm() will return -1 when the file exists | |
4332 * but is being used by another program. This happens if you edit | |
4333 * a file twice. | |
4334 */ | |
4335 if (fh != (BPTR)NULL) /* can open file, OK */ | |
4336 { | |
4337 Close(fh); | |
4338 mch_remove(fname); | |
4339 break; | |
4340 } | |
4341 if (IoErr() != ERROR_OBJECT_IN_USE | |
4342 && IoErr() != ERROR_OBJECT_EXISTS) | |
4343 # endif | |
4344 #endif | |
4345 break; | |
4346 } | |
4347 | |
4348 /* | |
4349 * A file name equal to old_fname is OK to use. | |
4350 */ | |
4351 if (old_fname != NULL && fnamecmp(fname, old_fname) == 0) | |
4352 break; | |
4353 | |
4354 /* | |
4355 * get here when file already exists | |
4356 */ | |
4357 if (fname[n - 2] == 'w' && fname[n - 1] == 'p') /* first try */ | |
4358 { | |
4359 #ifndef SHORT_FNAME | |
4360 /* | |
4361 * on MS-DOS compatible filesystems (e.g. messydos) file.doc.swp | |
4362 * and file.doc are the same file. To guess if this problem is | |
4363 * present try if file.doc.swx exists. If it does, we set | |
4364 * buf->b_shortname and try file_doc.swp (dots replaced by | |
4365 * underscores for this file), and try again. If it doesn't we | |
4366 * assume that "file.doc.swp" already exists. | |
4367 */ | |
4368 if (!(buf->b_p_sn || buf->b_shortname)) /* not tried yet */ | |
4369 { | |
4370 fname[n - 1] = 'x'; | |
4371 r = mch_getperm(fname); /* try "file.swx" */ | |
4372 fname[n - 1] = 'p'; | |
4373 if (r >= 0) /* "file.swx" seems to exist */ | |
4374 { | |
4375 buf->b_shortname = TRUE; | |
4376 vim_free(fname); | |
5432 | 4377 fname = makeswapname(buf_fname, buf->b_ffname, |
460 | 4378 buf, dir_name); |
7 | 4379 continue; /* try again with '.' replaced with '_' */ |
4380 } | |
4381 } | |
4382 #endif | |
4383 /* | |
4384 * If we get here the ".swp" file really exists. | |
4385 * Give an error message, unless recovering, no file name, we are | |
4386 * viewing a help file or when the path of the file is different | |
4387 * (happens when all .swp files are in one directory). | |
4388 */ | |
5432 | 4389 if (!recoverymode && buf_fname != NULL |
43 | 4390 && !buf->b_help && !(buf->b_flags & BF_DUMMY)) |
7 | 4391 { |
4392 int fd; | |
4393 struct block0 b0; | |
4394 int differ = FALSE; | |
4395 | |
4396 /* | |
4397 * Try to read block 0 from the swap file to get the original | |
4398 * file name (and inode number). | |
4399 */ | |
4400 fd = mch_open((char *)fname, O_RDONLY | O_EXTRA, 0); | |
4401 if (fd >= 0) | |
4402 { | |
2664 | 4403 if (read_eintr(fd, &b0, sizeof(b0)) == sizeof(b0)) |
7 | 4404 { |
4405 /* | |
39 | 4406 * If the swapfile has the same directory as the |
4407 * buffer don't compare the directory names, they can | |
4408 * have a different mountpoint. | |
7 | 4409 */ |
39 | 4410 if (b0.b0_flags & B0_SAME_DIR) |
4411 { | |
4412 if (fnamecmp(gettail(buf->b_ffname), | |
4413 gettail(b0.b0_fname)) != 0 | |
4414 || !same_directory(fname, buf->b_ffname)) | |
594 | 4415 { |
4416 #ifdef CHECK_INODE | |
4417 /* Symlinks may point to the same file even | |
4418 * when the name differs, need to check the | |
4419 * inode too. */ | |
4420 expand_env(b0.b0_fname, NameBuff, MAXPATHL); | |
4421 if (fnamecmp_ino(buf->b_ffname, NameBuff, | |
4422 char_to_long(b0.b0_ino))) | |
4423 #endif | |
4424 differ = TRUE; | |
4425 } | |
39 | 4426 } |
4427 else | |
4428 { | |
4429 /* | |
4430 * The name in the swap file may be | |
4431 * "~user/path/file". Expand it first. | |
4432 */ | |
4433 expand_env(b0.b0_fname, NameBuff, MAXPATHL); | |
7 | 4434 #ifdef CHECK_INODE |
39 | 4435 if (fnamecmp_ino(buf->b_ffname, NameBuff, |
594 | 4436 char_to_long(b0.b0_ino))) |
39 | 4437 differ = TRUE; |
7 | 4438 #else |
39 | 4439 if (fnamecmp(NameBuff, buf->b_ffname) != 0) |
4440 differ = TRUE; | |
7 | 4441 #endif |
39 | 4442 } |
7 | 4443 } |
4444 close(fd); | |
4445 } | |
4446 | |
4447 /* give the ATTENTION message when there is an old swap file | |
4448 * for the current file, and the buffer was not recovered. */ | |
4449 if (differ == FALSE && !(curbuf->b_flags & BF_RECOVERED) | |
4450 && vim_strchr(p_shm, SHM_ATTENTION) == NULL) | |
4451 { | |
580 | 4452 #if defined(HAS_SWAP_EXISTS_ACTION) |
4453 int choice = 0; | |
4454 #endif | |
7 | 4455 #ifdef CREATE_DUMMY_FILE |
4456 int did_use_dummy = FALSE; | |
4457 | |
4458 /* Avoid getting a warning for the file being created | |
4459 * outside of Vim, it was created at the start of this | |
4460 * function. Delete the file now, because Vim might exit | |
4461 * here if the window is closed. */ | |
4462 if (dummyfd != NULL) | |
4463 { | |
4464 fclose(dummyfd); | |
4465 dummyfd = NULL; | |
5432 | 4466 mch_remove(buf_fname); |
7 | 4467 did_use_dummy = TRUE; |
4468 } | |
4469 #endif | |
4470 | |
4471 #if (defined(UNIX) || defined(__EMX__) || defined(VMS)) && (defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG)) | |
4472 process_still_running = FALSE; | |
4473 #endif | |
580 | 4474 #ifdef FEAT_AUTOCMD |
4475 /* | |
4476 * If there is an SwapExists autocommand and we can handle | |
4477 * the response, trigger it. It may return 0 to ask the | |
4478 * user anyway. | |
4479 */ | |
4480 if (swap_exists_action != SEA_NONE | |
5432 | 4481 && has_autocmd(EVENT_SWAPEXISTS, buf_fname, buf)) |
580 | 4482 choice = do_swapexists(buf, fname); |
4483 | |
4484 if (choice == 0) | |
4485 #endif | |
7 | 4486 { |
580 | 4487 #ifdef FEAT_GUI |
4488 /* If we are supposed to start the GUI but it wasn't | |
4489 * completely started yet, start it now. This makes | |
4490 * the messages displayed in the Vim window when | |
4491 * loading a session from the .gvimrc file. */ | |
4492 if (gui.starting && !gui.in_use) | |
4493 gui_start(); | |
4494 #endif | |
4495 /* Show info about the existing swap file. */ | |
4496 attention_message(buf, fname); | |
4497 | |
4498 /* We don't want a 'q' typed at the more-prompt | |
4499 * interrupt loading a file. */ | |
4500 got_int = FALSE; | |
7 | 4501 } |
4502 | |
4503 #if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG) | |
580 | 4504 if (swap_exists_action != SEA_NONE && choice == 0) |
7 | 4505 { |
4506 char_u *name; | |
4507 | |
4508 name = alloc((unsigned)(STRLEN(fname) | |
4509 + STRLEN(_("Swap file \"")) | |
4510 + STRLEN(_("\" already exists!")) + 5)); | |
4511 if (name != NULL) | |
4512 { | |
4513 STRCPY(name, _("Swap file \"")); | |
4514 home_replace(NULL, fname, name + STRLEN(name), | |
4515 1000, TRUE); | |
4516 STRCAT(name, _("\" already exists!")); | |
4517 } | |
580 | 4518 choice = do_dialog(VIM_WARNING, |
7 | 4519 (char_u *)_("VIM - ATTENTION"), |
4520 name == NULL | |
4521 ? (char_u *)_("Swap file already exists!") | |
4522 : name, | |
4523 # if defined(UNIX) || defined(__EMX__) || defined(VMS) | |
4524 process_still_running | |
4525 ? (char_u *)_("&Open Read-Only\n&Edit anyway\n&Recover\n&Quit\n&Abort") : | |
4526 # endif | |
2684 | 4527 (char_u *)_("&Open Read-Only\n&Edit anyway\n&Recover\n&Delete it\n&Quit\n&Abort"), 1, NULL, FALSE); |
580 | 4528 |
4529 # if defined(UNIX) || defined(__EMX__) || defined(VMS) | |
4530 if (process_still_running && choice >= 4) | |
4531 choice++; /* Skip missing "Delete it" button */ | |
4532 # endif | |
4533 vim_free(name); | |
4534 | |
4535 /* pretend screen didn't scroll, need redraw anyway */ | |
4536 msg_scrolled = 0; | |
4537 redraw_all_later(NOT_VALID); | |
4538 } | |
4539 #endif | |
4540 | |
4541 #if defined(HAS_SWAP_EXISTS_ACTION) | |
4542 if (choice > 0) | |
4543 { | |
4544 switch (choice) | |
7 | 4545 { |
4546 case 1: | |
4547 buf->b_p_ro = TRUE; | |
4548 break; | |
4549 case 2: | |
4550 break; | |
4551 case 3: | |
4552 swap_exists_action = SEA_RECOVER; | |
4553 break; | |
4554 case 4: | |
580 | 4555 mch_remove(fname); |
4556 break; | |
4557 case 5: | |
7 | 4558 swap_exists_action = SEA_QUIT; |
4559 break; | |
580 | 4560 case 6: |
7 | 4561 swap_exists_action = SEA_QUIT; |
4562 got_int = TRUE; | |
4563 break; | |
4564 } | |
4565 | |
4566 /* If the file was deleted this fname can be used. */ | |
4567 if (mch_getperm(fname) < 0) | |
4568 break; | |
4569 } | |
4570 else | |
4571 #endif | |
4572 { | |
4573 MSG_PUTS("\n"); | |
625 | 4574 if (msg_silent == 0) |
4575 /* call wait_return() later */ | |
4576 need_wait_return = TRUE; | |
7 | 4577 } |
4578 | |
4579 #ifdef CREATE_DUMMY_FILE | |
4580 /* Going to try another name, need the dummy file again. */ | |
4581 if (did_use_dummy) | |
5432 | 4582 dummyfd = mch_fopen((char *)buf_fname, "w"); |
7 | 4583 #endif |
4584 } | |
4585 } | |
4586 } | |
4587 | |
4588 /* | |
4589 * Change the ".swp" extension to find another file that can be used. | |
4590 * First decrement the last char: ".swo", ".swn", etc. | |
4591 * If that still isn't enough decrement the last but one char: ".svz" | |
9 | 4592 * Can happen when editing many "No Name" buffers. |
7 | 4593 */ |
4594 if (fname[n - 1] == 'a') /* ".s?a" */ | |
4595 { | |
4596 if (fname[n - 2] == 'a') /* ".saa": tried enough, give up */ | |
4597 { | |
4598 EMSG(_("E326: Too many swap files found")); | |
4599 vim_free(fname); | |
4600 fname = NULL; | |
4601 break; | |
4602 } | |
4603 --fname[n - 2]; /* ".svz", ".suz", etc. */ | |
4604 fname[n - 1] = 'z' + 1; | |
4605 } | |
4606 --fname[n - 1]; /* ".swo", ".swn", etc. */ | |
4607 } | |
4608 | |
4609 vim_free(dir_name); | |
4610 #ifdef CREATE_DUMMY_FILE | |
4611 if (dummyfd != NULL) /* file has been created temporarily */ | |
4612 { | |
4613 fclose(dummyfd); | |
5432 | 4614 mch_remove(buf_fname); |
7 | 4615 } |
4616 #endif | |
5432 | 4617 #ifdef WIN3264 |
4618 if (buf_fname != buf->b_fname) | |
4619 vim_free(buf_fname); | |
4620 #endif | |
7 | 4621 return fname; |
4622 } | |
4623 | |
4624 static int | |
7827
41789f16d6b2
commit https://github.com/vim/vim/commit/52ea13da0fe86df1abf34de52841e367035170c0
Christian Brabandt <cb@256bit.org>
parents:
7803
diff
changeset
|
4625 b0_magic_wrong(ZERO_BL *b0p) |
7 | 4626 { |
4627 return (b0p->b0_magic_long != (long)B0_MAGIC_LONG | |
4628 || b0p->b0_magic_int != (int)B0_MAGIC_INT | |
4629 || b0p->b0_magic_short != (short)B0_MAGIC_SHORT | |
4630 || b0p->b0_magic_char != B0_MAGIC_CHAR); | |
4631 } | |
4632 | |
4633 #ifdef CHECK_INODE | |
4634 /* | |
4635 * Compare current file name with file name from swap file. | |
4636 * Try to use inode numbers when possible. | |
4637 * Return non-zero when files are different. | |
4638 * | |
4639 * When comparing file names a few things have to be taken into consideration: | |
4640 * - When working over a network the full path of a file depends on the host. | |
4641 * We check the inode number if possible. It is not 100% reliable though, | |
4642 * because the device number cannot be used over a network. | |
4643 * - When a file does not exist yet (editing a new file) there is no inode | |
4644 * number. | |
4645 * - The file name in a swap file may not be valid on the current host. The | |
4646 * "~user" form is used whenever possible to avoid this. | |
4647 * | |
4648 * This is getting complicated, let's make a table: | |
4649 * | |
4650 * ino_c ino_s fname_c fname_s differ = | |
4651 * | |
4652 * both files exist -> compare inode numbers: | |
4653 * != 0 != 0 X X ino_c != ino_s | |
4654 * | |
4655 * inode number(s) unknown, file names available -> compare file names | |
4656 * == 0 X OK OK fname_c != fname_s | |
4657 * X == 0 OK OK fname_c != fname_s | |
4658 * | |
4659 * current file doesn't exist, file for swap file exist, file name(s) not | |
4660 * available -> probably different | |
4661 * == 0 != 0 FAIL X TRUE | |
4662 * == 0 != 0 X FAIL TRUE | |
4663 * | |
4664 * current file exists, inode for swap unknown, file name(s) not | |
4665 * available -> probably different | |
4666 * != 0 == 0 FAIL X TRUE | |
4667 * != 0 == 0 X FAIL TRUE | |
4668 * | |
4669 * current file doesn't exist, inode for swap unknown, one file name not | |
4670 * available -> probably different | |
4671 * == 0 == 0 FAIL OK TRUE | |
4672 * == 0 == 0 OK FAIL TRUE | |
4673 * | |
4674 * current file doesn't exist, inode for swap unknown, both file names not | |
4675 * available -> probably same file | |
4676 * == 0 == 0 FAIL FAIL FALSE | |
4677 * | |
4678 * Note that when the ino_t is 64 bits, only the last 32 will be used. This | |
4679 * can't be changed without making the block 0 incompatible with 32 bit | |
4680 * versions. | |
4681 */ | |
4682 | |
4683 static int | |
7827
41789f16d6b2
commit https://github.com/vim/vim/commit/52ea13da0fe86df1abf34de52841e367035170c0
Christian Brabandt <cb@256bit.org>
parents:
7803
diff
changeset
|
4684 fnamecmp_ino( |
41789f16d6b2
commit https://github.com/vim/vim/commit/52ea13da0fe86df1abf34de52841e367035170c0
Christian Brabandt <cb@256bit.org>
parents:
7803
diff
changeset
|
4685 char_u *fname_c, /* current file name */ |
41789f16d6b2
commit https://github.com/vim/vim/commit/52ea13da0fe86df1abf34de52841e367035170c0
Christian Brabandt <cb@256bit.org>
parents:
7803
diff
changeset
|
4686 char_u *fname_s, /* file name from swap file */ |
41789f16d6b2
commit https://github.com/vim/vim/commit/52ea13da0fe86df1abf34de52841e367035170c0
Christian Brabandt <cb@256bit.org>
parents:
7803
diff
changeset
|
4687 long ino_block0) |
7 | 4688 { |
4689 struct stat st; | |
4690 ino_t ino_c = 0; /* ino of current file */ | |
4691 ino_t ino_s; /* ino of file from swap file */ | |
4692 char_u buf_c[MAXPATHL]; /* full path of fname_c */ | |
4693 char_u buf_s[MAXPATHL]; /* full path of fname_s */ | |
4694 int retval_c; /* flag: buf_c valid */ | |
4695 int retval_s; /* flag: buf_s valid */ | |
4696 | |
4697 if (mch_stat((char *)fname_c, &st) == 0) | |
4698 ino_c = (ino_t)st.st_ino; | |
4699 | |
4700 /* | |
4701 * First we try to get the inode from the file name, because the inode in | |
4702 * the swap file may be outdated. If that fails (e.g. this path is not | |
4703 * valid on this machine), use the inode from block 0. | |
4704 */ | |
4705 if (mch_stat((char *)fname_s, &st) == 0) | |
4706 ino_s = (ino_t)st.st_ino; | |
4707 else | |
4708 ino_s = (ino_t)ino_block0; | |
4709 | |
4710 if (ino_c && ino_s) | |
4711 return (ino_c != ino_s); | |
4712 | |
4713 /* | |
4714 * One of the inode numbers is unknown, try a forced vim_FullName() and | |
4715 * compare the file names. | |
4716 */ | |
4717 retval_c = vim_FullName(fname_c, buf_c, MAXPATHL, TRUE); | |
4718 retval_s = vim_FullName(fname_s, buf_s, MAXPATHL, TRUE); | |
4719 if (retval_c == OK && retval_s == OK) | |
4720 return (STRCMP(buf_c, buf_s) != 0); | |
4721 | |
4722 /* | |
4723 * Can't compare inodes or file names, guess that the files are different, | |
4724 * unless both appear not to exist at all. | |
4725 */ | |
4726 if (ino_s == 0 && ino_c == 0 && retval_c == FAIL && retval_s == FAIL) | |
4727 return FALSE; | |
4728 return TRUE; | |
4729 } | |
4730 #endif /* CHECK_INODE */ | |
4731 | |
4732 /* | |
4733 * Move a long integer into a four byte character array. | |
4734 * Used for machine independency in block zero. | |
4735 */ | |
4736 static void | |
7827
41789f16d6b2
commit https://github.com/vim/vim/commit/52ea13da0fe86df1abf34de52841e367035170c0
Christian Brabandt <cb@256bit.org>
parents:
7803
diff
changeset
|
4737 long_to_char(long n, char_u *s) |
7 | 4738 { |
4739 s[0] = (char_u)(n & 0xff); | |
4740 n = (unsigned)n >> 8; | |
4741 s[1] = (char_u)(n & 0xff); | |
4742 n = (unsigned)n >> 8; | |
4743 s[2] = (char_u)(n & 0xff); | |
4744 n = (unsigned)n >> 8; | |
4745 s[3] = (char_u)(n & 0xff); | |
4746 } | |
4747 | |
4748 static long | |
7827
41789f16d6b2
commit https://github.com/vim/vim/commit/52ea13da0fe86df1abf34de52841e367035170c0
Christian Brabandt <cb@256bit.org>
parents:
7803
diff
changeset
|
4749 char_to_long(char_u *s) |
7 | 4750 { |
4751 long retval; | |
4752 | |
4753 retval = s[3]; | |
4754 retval <<= 8; | |
4755 retval |= s[2]; | |
4756 retval <<= 8; | |
4757 retval |= s[1]; | |
4758 retval <<= 8; | |
4759 retval |= s[0]; | |
4760 | |
4761 return retval; | |
4762 } | |
4763 | |
39 | 4764 /* |
4765 * Set the flags in the first block of the swap file: | |
4766 * - file is modified or not: buf->b_changed | |
4767 * - 'fileformat' | |
4768 * - 'fileencoding' | |
4769 */ | |
7 | 4770 void |
7827
41789f16d6b2
commit https://github.com/vim/vim/commit/52ea13da0fe86df1abf34de52841e367035170c0
Christian Brabandt <cb@256bit.org>
parents:
7803
diff
changeset
|
4771 ml_setflags(buf_T *buf) |
7 | 4772 { |
4773 bhdr_T *hp; | |
4774 ZERO_BL *b0p; | |
4775 | |
4776 if (!buf->b_ml.ml_mfp) | |
4777 return; | |
4778 for (hp = buf->b_ml.ml_mfp->mf_used_last; hp != NULL; hp = hp->bh_prev) | |
4779 { | |
4780 if (hp->bh_bnum == 0) | |
4781 { | |
4782 b0p = (ZERO_BL *)(hp->bh_data); | |
39 | 4783 b0p->b0_dirty = buf->b_changed ? B0_DIRTY : 0; |
4784 b0p->b0_flags = (b0p->b0_flags & ~B0_FF_MASK) | |
4785 | (get_fileformat(buf) + 1); | |
4786 #ifdef FEAT_MBYTE | |
4787 add_b0_fenc(b0p, buf); | |
4788 #endif | |
7 | 4789 hp->bh_flags |= BH_DIRTY; |
4790 mf_sync(buf->b_ml.ml_mfp, MFS_ZERO); | |
4791 break; | |
4792 } | |
4793 } | |
4794 } | |
4795 | |
2267 | 4796 #if defined(FEAT_CRYPT) || defined(PROTO) |
4797 /* | |
4798 * If "data" points to a data block encrypt the text in it and return a copy | |
4799 * in allocated memory. Return NULL when out of memory. | |
4800 * Otherwise return "data". | |
4801 */ | |
4802 char_u * | |
7827
41789f16d6b2
commit https://github.com/vim/vim/commit/52ea13da0fe86df1abf34de52841e367035170c0
Christian Brabandt <cb@256bit.org>
parents:
7803
diff
changeset
|
4803 ml_encrypt_data( |
41789f16d6b2
commit https://github.com/vim/vim/commit/52ea13da0fe86df1abf34de52841e367035170c0
Christian Brabandt <cb@256bit.org>
parents:
7803
diff
changeset
|
4804 memfile_T *mfp, |
41789f16d6b2
commit https://github.com/vim/vim/commit/52ea13da0fe86df1abf34de52841e367035170c0
Christian Brabandt <cb@256bit.org>
parents:
7803
diff
changeset
|
4805 char_u *data, |
41789f16d6b2
commit https://github.com/vim/vim/commit/52ea13da0fe86df1abf34de52841e367035170c0
Christian Brabandt <cb@256bit.org>
parents:
7803
diff
changeset
|
4806 off_t offset, |
41789f16d6b2
commit https://github.com/vim/vim/commit/52ea13da0fe86df1abf34de52841e367035170c0
Christian Brabandt <cb@256bit.org>
parents:
7803
diff
changeset
|
4807 unsigned size) |
2267 | 4808 { |
4809 DATA_BL *dp = (DATA_BL *)data; | |
4810 char_u *head_end; | |
4811 char_u *text_start; | |
4812 char_u *new_data; | |
4813 int text_len; | |
6122 | 4814 cryptstate_T *state; |
2267 | 4815 |
4816 if (dp->db_id != DATA_ID) | |
4817 return data; | |
4818 | |
6817 | 4819 state = ml_crypt_prepare(mfp, offset, FALSE); |
4820 if (state == NULL) | |
4821 return data; | |
4822 | |
2267 | 4823 new_data = (char_u *)alloc(size); |
4824 if (new_data == NULL) | |
4825 return NULL; | |
4826 head_end = (char_u *)(&dp->db_index[dp->db_line_count]); | |
4827 text_start = (char_u *)dp + dp->db_txt_start; | |
4828 text_len = size - dp->db_txt_start; | |
4829 | |
4830 /* Copy the header and the text. */ | |
4831 mch_memmove(new_data, dp, head_end - (char_u *)dp); | |
4832 | |
4833 /* Encrypt the text. */ | |
6122 | 4834 crypt_encode(state, text_start, text_len, new_data + dp->db_txt_start); |
4835 crypt_free_state(state); | |
2267 | 4836 |
4837 /* Clear the gap. */ | |
4838 if (head_end < text_start) | |
4839 vim_memset(new_data + (head_end - data), 0, text_start - head_end); | |
4840 | |
4841 return new_data; | |
4842 } | |
4843 | |
4844 /* | |
6817 | 4845 * Decrypt the text in "data" if it points to an encrypted data block. |
2267 | 4846 */ |
4847 void | |
7827
41789f16d6b2
commit https://github.com/vim/vim/commit/52ea13da0fe86df1abf34de52841e367035170c0
Christian Brabandt <cb@256bit.org>
parents:
7803
diff
changeset
|
4848 ml_decrypt_data( |
41789f16d6b2
commit https://github.com/vim/vim/commit/52ea13da0fe86df1abf34de52841e367035170c0
Christian Brabandt <cb@256bit.org>
parents:
7803
diff
changeset
|
4849 memfile_T *mfp, |
41789f16d6b2
commit https://github.com/vim/vim/commit/52ea13da0fe86df1abf34de52841e367035170c0
Christian Brabandt <cb@256bit.org>
parents:
7803
diff
changeset
|
4850 char_u *data, |
41789f16d6b2
commit https://github.com/vim/vim/commit/52ea13da0fe86df1abf34de52841e367035170c0
Christian Brabandt <cb@256bit.org>
parents:
7803
diff
changeset
|
4851 off_t offset, |
41789f16d6b2
commit https://github.com/vim/vim/commit/52ea13da0fe86df1abf34de52841e367035170c0
Christian Brabandt <cb@256bit.org>
parents:
7803
diff
changeset
|
4852 unsigned size) |
2267 | 4853 { |
4854 DATA_BL *dp = (DATA_BL *)data; | |
4855 char_u *head_end; | |
4856 char_u *text_start; | |
4857 int text_len; | |
6122 | 4858 cryptstate_T *state; |
2267 | 4859 |
4860 if (dp->db_id == DATA_ID) | |
4861 { | |
4862 head_end = (char_u *)(&dp->db_index[dp->db_line_count]); | |
4863 text_start = (char_u *)dp + dp->db_txt_start; | |
4864 text_len = dp->db_txt_end - dp->db_txt_start; | |
4865 | |
4866 if (head_end > text_start || dp->db_txt_start > size | |
4867 || dp->db_txt_end > size) | |
4868 return; /* data was messed up */ | |
4869 | |
6122 | 4870 state = ml_crypt_prepare(mfp, offset, TRUE); |
6817 | 4871 if (state != NULL) |
4872 { | |
4873 /* Decrypt the text in place. */ | |
4874 crypt_decode_inplace(state, text_start, text_len); | |
4875 crypt_free_state(state); | |
4876 } | |
2267 | 4877 } |
4878 } | |
4879 | |
4880 /* | |
4881 * Prepare for encryption/decryption, using the key, seed and offset. | |
6122 | 4882 * Return an allocated cryptstate_T *. |
2267 | 4883 */ |
6122 | 4884 static cryptstate_T * |
7827
41789f16d6b2
commit https://github.com/vim/vim/commit/52ea13da0fe86df1abf34de52841e367035170c0
Christian Brabandt <cb@256bit.org>
parents:
7803
diff
changeset
|
4885 ml_crypt_prepare(memfile_T *mfp, off_t offset, int reading) |
2267 | 4886 { |
4887 buf_T *buf = mfp->mf_buffer; | |
4888 char_u salt[50]; | |
6122 | 4889 int method_nr; |
2267 | 4890 char_u *key; |
4891 char_u *seed; | |
4892 | |
4893 if (reading && mfp->mf_old_key != NULL) | |
4894 { | |
4895 /* Reading back blocks with the previous key/method/seed. */ | |
6122 | 4896 method_nr = mfp->mf_old_cm; |
2267 | 4897 key = mfp->mf_old_key; |
4898 seed = mfp->mf_old_seed; | |
4899 } | |
4900 else | |
4901 { | |
6122 | 4902 method_nr = crypt_get_method_nr(buf); |
2267 | 4903 key = buf->b_p_key; |
4904 seed = mfp->mf_seed; | |
4905 } | |
6817 | 4906 if (*key == NUL) |
4907 return NULL; | |
2267 | 4908 |
6122 | 4909 if (method_nr == CRYPT_M_ZIP) |
2267 | 4910 { |
6122 | 4911 /* For PKzip: Append the offset to the key, so that we use a different |
4912 * key for every block. */ | |
2267 | 4913 vim_snprintf((char *)salt, sizeof(salt), "%s%ld", key, (long)offset); |
6122 | 4914 return crypt_create(method_nr, salt, NULL, 0, NULL, 0); |
2267 | 4915 } |
6122 | 4916 |
4917 /* Using blowfish or better: add salt and seed. We use the byte offset | |
4918 * of the block for the salt. */ | |
4919 vim_snprintf((char *)salt, sizeof(salt), "%ld", (long)offset); | |
4920 return crypt_create(method_nr, key, salt, (int)STRLEN(salt), | |
4921 seed, MF_SEED_LEN); | |
2267 | 4922 } |
4923 | |
4924 #endif | |
4925 | |
4926 | |
7 | 4927 #if defined(FEAT_BYTEOFF) || defined(PROTO) |
4928 | |
4929 #define MLCS_MAXL 800 /* max no of lines in chunk */ | |
4930 #define MLCS_MINL 400 /* should be half of MLCS_MAXL */ | |
4931 | |
4932 /* | |
2407
6bc102a4bff8
Fix memory access to 'cryptmethod' during recovery. (Dominique Pelle)
Bram Moolenaar <bram@vim.org>
parents:
2394
diff
changeset
|
4933 * Keep information for finding byte offset of a line, updtype may be one of: |
7 | 4934 * ML_CHNK_ADDLINE: Add len to parent chunk, possibly splitting it |
4935 * Careful: ML_CHNK_ADDLINE may cause ml_find_line() to be called. | |
4936 * ML_CHNK_DELLINE: Subtract len from parent chunk, possibly deleting it | |
4937 * ML_CHNK_UPDLINE: Add len to parent chunk, as a signed entity. | |
4938 */ | |
4939 static void | |
7827
41789f16d6b2
commit https://github.com/vim/vim/commit/52ea13da0fe86df1abf34de52841e367035170c0
Christian Brabandt <cb@256bit.org>
parents:
7803
diff
changeset
|
4940 ml_updatechunk( |
41789f16d6b2
commit https://github.com/vim/vim/commit/52ea13da0fe86df1abf34de52841e367035170c0
Christian Brabandt <cb@256bit.org>
parents:
7803
diff
changeset
|
4941 buf_T *buf, |
41789f16d6b2
commit https://github.com/vim/vim/commit/52ea13da0fe86df1abf34de52841e367035170c0
Christian Brabandt <cb@256bit.org>
parents:
7803
diff
changeset
|
4942 linenr_T line, |
41789f16d6b2
commit https://github.com/vim/vim/commit/52ea13da0fe86df1abf34de52841e367035170c0
Christian Brabandt <cb@256bit.org>
parents:
7803
diff
changeset
|
4943 long len, |
41789f16d6b2
commit https://github.com/vim/vim/commit/52ea13da0fe86df1abf34de52841e367035170c0
Christian Brabandt <cb@256bit.org>
parents:
7803
diff
changeset
|
4944 int updtype) |
7 | 4945 { |
4946 static buf_T *ml_upd_lastbuf = NULL; | |
4947 static linenr_T ml_upd_lastline; | |
4948 static linenr_T ml_upd_lastcurline; | |
4949 static int ml_upd_lastcurix; | |
4950 | |
4951 linenr_T curline = ml_upd_lastcurline; | |
4952 int curix = ml_upd_lastcurix; | |
4953 long size; | |
4954 chunksize_T *curchnk; | |
4955 int rest; | |
4956 bhdr_T *hp; | |
4957 DATA_BL *dp; | |
4958 | |
4959 if (buf->b_ml.ml_usedchunks == -1 || len == 0) | |
4960 return; | |
4961 if (buf->b_ml.ml_chunksize == NULL) | |
4962 { | |
4963 buf->b_ml.ml_chunksize = (chunksize_T *) | |
4964 alloc((unsigned)sizeof(chunksize_T) * 100); | |
4965 if (buf->b_ml.ml_chunksize == NULL) | |
4966 { | |
4967 buf->b_ml.ml_usedchunks = -1; | |
4968 return; | |
4969 } | |
4970 buf->b_ml.ml_numchunks = 100; | |
4971 buf->b_ml.ml_usedchunks = 1; | |
4972 buf->b_ml.ml_chunksize[0].mlcs_numlines = 1; | |
4973 buf->b_ml.ml_chunksize[0].mlcs_totalsize = 1; | |
4974 } | |
4975 | |
4976 if (updtype == ML_CHNK_UPDLINE && buf->b_ml.ml_line_count == 1) | |
4977 { | |
4978 /* | |
4979 * First line in empty buffer from ml_flush_line() -- reset | |
4980 */ | |
4981 buf->b_ml.ml_usedchunks = 1; | |
4982 buf->b_ml.ml_chunksize[0].mlcs_numlines = 1; | |
4983 buf->b_ml.ml_chunksize[0].mlcs_totalsize = | |
4984 (long)STRLEN(buf->b_ml.ml_line_ptr) + 1; | |
4985 return; | |
4986 } | |
4987 | |
4988 /* | |
4989 * Find chunk that our line belongs to, curline will be at start of the | |
4990 * chunk. | |
4991 */ | |
4992 if (buf != ml_upd_lastbuf || line != ml_upd_lastline + 1 | |
4993 || updtype != ML_CHNK_ADDLINE) | |
4994 { | |
4995 for (curline = 1, curix = 0; | |
4996 curix < buf->b_ml.ml_usedchunks - 1 | |
4997 && line >= curline + buf->b_ml.ml_chunksize[curix].mlcs_numlines; | |
4998 curix++) | |
4999 { | |
5000 curline += buf->b_ml.ml_chunksize[curix].mlcs_numlines; | |
5001 } | |
5002 } | |
5003 else if (line >= curline + buf->b_ml.ml_chunksize[curix].mlcs_numlines | |
5004 && curix < buf->b_ml.ml_usedchunks - 1) | |
5005 { | |
5006 /* Adjust cached curix & curline */ | |
5007 curline += buf->b_ml.ml_chunksize[curix].mlcs_numlines; | |
5008 curix++; | |
5009 } | |
5010 curchnk = buf->b_ml.ml_chunksize + curix; | |
5011 | |
5012 if (updtype == ML_CHNK_DELLINE) | |
1030 | 5013 len = -len; |
7 | 5014 curchnk->mlcs_totalsize += len; |
5015 if (updtype == ML_CHNK_ADDLINE) | |
5016 { | |
5017 curchnk->mlcs_numlines++; | |
5018 | |
5019 /* May resize here so we don't have to do it in both cases below */ | |
5020 if (buf->b_ml.ml_usedchunks + 1 >= buf->b_ml.ml_numchunks) | |
5021 { | |
6596 | 5022 chunksize_T *t_chunksize = buf->b_ml.ml_chunksize; |
5023 | |
7 | 5024 buf->b_ml.ml_numchunks = buf->b_ml.ml_numchunks * 3 / 2; |
5025 buf->b_ml.ml_chunksize = (chunksize_T *) | |
5026 vim_realloc(buf->b_ml.ml_chunksize, | |
5027 sizeof(chunksize_T) * buf->b_ml.ml_numchunks); | |
5028 if (buf->b_ml.ml_chunksize == NULL) | |
5029 { | |
5030 /* Hmmmm, Give up on offset for this buffer */ | |
6596 | 5031 vim_free(t_chunksize); |
7 | 5032 buf->b_ml.ml_usedchunks = -1; |
5033 return; | |
5034 } | |
5035 } | |
5036 | |
5037 if (buf->b_ml.ml_chunksize[curix].mlcs_numlines >= MLCS_MAXL) | |
5038 { | |
5039 int count; /* number of entries in block */ | |
5040 int idx; | |
5041 int text_end; | |
5042 int linecnt; | |
5043 | |
5044 mch_memmove(buf->b_ml.ml_chunksize + curix + 1, | |
5045 buf->b_ml.ml_chunksize + curix, | |
5046 (buf->b_ml.ml_usedchunks - curix) * | |
5047 sizeof(chunksize_T)); | |
1855 | 5048 /* Compute length of first half of lines in the split chunk */ |
7 | 5049 size = 0; |
5050 linecnt = 0; | |
5051 while (curline < buf->b_ml.ml_line_count | |
5052 && linecnt < MLCS_MINL) | |
5053 { | |
5054 if ((hp = ml_find_line(buf, curline, ML_FIND)) == NULL) | |
5055 { | |
5056 buf->b_ml.ml_usedchunks = -1; | |
5057 return; | |
5058 } | |
5059 dp = (DATA_BL *)(hp->bh_data); | |
5060 count = (long)(buf->b_ml.ml_locked_high) - | |
5061 (long)(buf->b_ml.ml_locked_low) + 1; | |
5062 idx = curline - buf->b_ml.ml_locked_low; | |
5063 curline = buf->b_ml.ml_locked_high + 1; | |
5064 if (idx == 0)/* first line in block, text at the end */ | |
5065 text_end = dp->db_txt_end; | |
5066 else | |
5067 text_end = ((dp->db_index[idx - 1]) & DB_INDEX_MASK); | |
5068 /* Compute index of last line to use in this MEMLINE */ | |
5069 rest = count - idx; | |
5070 if (linecnt + rest > MLCS_MINL) | |
5071 { | |
5072 idx += MLCS_MINL - linecnt - 1; | |
5073 linecnt = MLCS_MINL; | |
5074 } | |
5075 else | |
5076 { | |
5077 idx = count - 1; | |
5078 linecnt += rest; | |
5079 } | |
5080 size += text_end - ((dp->db_index[idx]) & DB_INDEX_MASK); | |
5081 } | |
5082 buf->b_ml.ml_chunksize[curix].mlcs_numlines = linecnt; | |
5083 buf->b_ml.ml_chunksize[curix + 1].mlcs_numlines -= linecnt; | |
5084 buf->b_ml.ml_chunksize[curix].mlcs_totalsize = size; | |
5085 buf->b_ml.ml_chunksize[curix + 1].mlcs_totalsize -= size; | |
5086 buf->b_ml.ml_usedchunks++; | |
5087 ml_upd_lastbuf = NULL; /* Force recalc of curix & curline */ | |
5088 return; | |
5089 } | |
5090 else if (buf->b_ml.ml_chunksize[curix].mlcs_numlines >= MLCS_MINL | |
5091 && curix == buf->b_ml.ml_usedchunks - 1 | |
5092 && buf->b_ml.ml_line_count - line <= 1) | |
5093 { | |
5094 /* | |
5095 * We are in the last chunk and it is cheap to crate a new one | |
5096 * after this. Do it now to avoid the loop above later on | |
5097 */ | |
5098 curchnk = buf->b_ml.ml_chunksize + curix + 1; | |
5099 buf->b_ml.ml_usedchunks++; | |
5100 if (line == buf->b_ml.ml_line_count) | |
5101 { | |
5102 curchnk->mlcs_numlines = 0; | |
5103 curchnk->mlcs_totalsize = 0; | |
5104 } | |
5105 else | |
5106 { | |
5107 /* | |
5108 * Line is just prior to last, move count for last | |
5109 * This is the common case when loading a new file | |
5110 */ | |
5111 hp = ml_find_line(buf, buf->b_ml.ml_line_count, ML_FIND); | |
5112 if (hp == NULL) | |
5113 { | |
5114 buf->b_ml.ml_usedchunks = -1; | |
5115 return; | |
5116 } | |
5117 dp = (DATA_BL *)(hp->bh_data); | |
5118 if (dp->db_line_count == 1) | |
5119 rest = dp->db_txt_end - dp->db_txt_start; | |
5120 else | |
5121 rest = | |
5122 ((dp->db_index[dp->db_line_count - 2]) & DB_INDEX_MASK) | |
5123 - dp->db_txt_start; | |
5124 curchnk->mlcs_totalsize = rest; | |
5125 curchnk->mlcs_numlines = 1; | |
5126 curchnk[-1].mlcs_totalsize -= rest; | |
5127 curchnk[-1].mlcs_numlines -= 1; | |
5128 } | |
5129 } | |
5130 } | |
5131 else if (updtype == ML_CHNK_DELLINE) | |
5132 { | |
5133 curchnk->mlcs_numlines--; | |
5134 ml_upd_lastbuf = NULL; /* Force recalc of curix & curline */ | |
5135 if (curix < (buf->b_ml.ml_usedchunks - 1) | |
5136 && (curchnk->mlcs_numlines + curchnk[1].mlcs_numlines) | |
5137 <= MLCS_MINL) | |
5138 { | |
5139 curix++; | |
5140 curchnk = buf->b_ml.ml_chunksize + curix; | |
5141 } | |
5142 else if (curix == 0 && curchnk->mlcs_numlines <= 0) | |
5143 { | |
5144 buf->b_ml.ml_usedchunks--; | |
5145 mch_memmove(buf->b_ml.ml_chunksize, buf->b_ml.ml_chunksize + 1, | |
5146 buf->b_ml.ml_usedchunks * sizeof(chunksize_T)); | |
5147 return; | |
5148 } | |
5149 else if (curix == 0 || (curchnk->mlcs_numlines > 10 | |
5150 && (curchnk->mlcs_numlines + curchnk[-1].mlcs_numlines) | |
5151 > MLCS_MINL)) | |
5152 { | |
5153 return; | |
5154 } | |
5155 | |
5156 /* Collapse chunks */ | |
5157 curchnk[-1].mlcs_numlines += curchnk->mlcs_numlines; | |
5158 curchnk[-1].mlcs_totalsize += curchnk->mlcs_totalsize; | |
5159 buf->b_ml.ml_usedchunks--; | |
5160 if (curix < buf->b_ml.ml_usedchunks) | |
5161 { | |
5162 mch_memmove(buf->b_ml.ml_chunksize + curix, | |
5163 buf->b_ml.ml_chunksize + curix + 1, | |
5164 (buf->b_ml.ml_usedchunks - curix) * | |
5165 sizeof(chunksize_T)); | |
5166 } | |
5167 return; | |
5168 } | |
5169 ml_upd_lastbuf = buf; | |
5170 ml_upd_lastline = line; | |
5171 ml_upd_lastcurline = curline; | |
5172 ml_upd_lastcurix = curix; | |
5173 } | |
5174 | |
5175 /* | |
5176 * Find offset for line or line with offset. | |
169 | 5177 * Find line with offset if "lnum" is 0; return remaining offset in offp |
5178 * Find offset of line if "lnum" > 0 | |
7 | 5179 * return -1 if information is not available |
5180 */ | |
5181 long | |
7827
41789f16d6b2
commit https://github.com/vim/vim/commit/52ea13da0fe86df1abf34de52841e367035170c0
Christian Brabandt <cb@256bit.org>
parents:
7803
diff
changeset
|
5182 ml_find_line_or_offset(buf_T *buf, linenr_T lnum, long *offp) |
7 | 5183 { |
5184 linenr_T curline; | |
5185 int curix; | |
5186 long size; | |
5187 bhdr_T *hp; | |
5188 DATA_BL *dp; | |
5189 int count; /* number of entries in block */ | |
5190 int idx; | |
5191 int start_idx; | |
5192 int text_end; | |
5193 long offset; | |
5194 int len; | |
5195 int ffdos = (get_fileformat(buf) == EOL_DOS); | |
5196 int extra = 0; | |
5197 | |
169 | 5198 /* take care of cached line first */ |
5199 ml_flush_line(curbuf); | |
5200 | |
7 | 5201 if (buf->b_ml.ml_usedchunks == -1 |
5202 || buf->b_ml.ml_chunksize == NULL | |
169 | 5203 || lnum < 0) |
7 | 5204 return -1; |
5205 | |
5206 if (offp == NULL) | |
5207 offset = 0; | |
5208 else | |
5209 offset = *offp; | |
169 | 5210 if (lnum == 0 && offset <= 0) |
7 | 5211 return 1; /* Not a "find offset" and offset 0 _must_ be in line 1 */ |
5212 /* | |
5213 * Find the last chunk before the one containing our line. Last chunk is | |
5214 * special because it will never qualify | |
5215 */ | |
5216 curline = 1; | |
5217 curix = size = 0; | |
5218 while (curix < buf->b_ml.ml_usedchunks - 1 | |
169 | 5219 && ((lnum != 0 |
5220 && lnum >= curline + buf->b_ml.ml_chunksize[curix].mlcs_numlines) | |
7 | 5221 || (offset != 0 |
5222 && offset > size + buf->b_ml.ml_chunksize[curix].mlcs_totalsize | |
5223 + ffdos * buf->b_ml.ml_chunksize[curix].mlcs_numlines))) | |
5224 { | |
5225 curline += buf->b_ml.ml_chunksize[curix].mlcs_numlines; | |
5226 size += buf->b_ml.ml_chunksize[curix].mlcs_totalsize; | |
5227 if (offset && ffdos) | |
5228 size += buf->b_ml.ml_chunksize[curix].mlcs_numlines; | |
5229 curix++; | |
5230 } | |
5231 | |
169 | 5232 while ((lnum != 0 && curline < lnum) || (offset != 0 && size < offset)) |
7 | 5233 { |
5234 if (curline > buf->b_ml.ml_line_count | |
5235 || (hp = ml_find_line(buf, curline, ML_FIND)) == NULL) | |
5236 return -1; | |
5237 dp = (DATA_BL *)(hp->bh_data); | |
5238 count = (long)(buf->b_ml.ml_locked_high) - | |
5239 (long)(buf->b_ml.ml_locked_low) + 1; | |
5240 start_idx = idx = curline - buf->b_ml.ml_locked_low; | |
5241 if (idx == 0)/* first line in block, text at the end */ | |
5242 text_end = dp->db_txt_end; | |
5243 else | |
5244 text_end = ((dp->db_index[idx - 1]) & DB_INDEX_MASK); | |
5245 /* Compute index of last line to use in this MEMLINE */ | |
169 | 5246 if (lnum != 0) |
7 | 5247 { |
169 | 5248 if (curline + (count - idx) >= lnum) |
5249 idx += lnum - curline - 1; | |
7 | 5250 else |
5251 idx = count - 1; | |
5252 } | |
5253 else | |
5254 { | |
5255 extra = 0; | |
5256 while (offset >= size | |
5257 + text_end - (int)((dp->db_index[idx]) & DB_INDEX_MASK) | |
5258 + ffdos) | |
5259 { | |
5260 if (ffdos) | |
5261 size++; | |
5262 if (idx == count - 1) | |
5263 { | |
5264 extra = 1; | |
5265 break; | |
5266 } | |
5267 idx++; | |
5268 } | |
5269 } | |
5270 len = text_end - ((dp->db_index[idx]) & DB_INDEX_MASK); | |
5271 size += len; | |
5272 if (offset != 0 && size >= offset) | |
5273 { | |
5274 if (size + ffdos == offset) | |
5275 *offp = 0; | |
5276 else if (idx == start_idx) | |
5277 *offp = offset - size + len; | |
5278 else | |
5279 *offp = offset - size + len | |
5280 - (text_end - ((dp->db_index[idx - 1]) & DB_INDEX_MASK)); | |
5281 curline += idx - start_idx + extra; | |
5282 if (curline > buf->b_ml.ml_line_count) | |
5283 return -1; /* exactly one byte beyond the end */ | |
5284 return curline; | |
5285 } | |
5286 curline = buf->b_ml.ml_locked_high + 1; | |
5287 } | |
5288 | |
169 | 5289 if (lnum != 0) |
20 | 5290 { |
5291 /* Count extra CR characters. */ | |
5292 if (ffdos) | |
169 | 5293 size += lnum - 1; |
20 | 5294 |
6933 | 5295 /* Don't count the last line break if 'noeol' and ('bin' or |
5296 * 'nofixeol'). */ | |
5297 if ((!buf->b_p_fixeol || buf->b_p_bin) && !buf->b_p_eol | |
5298 && buf->b_ml.ml_line_count == lnum) | |
20 | 5299 size -= ffdos + 1; |
5300 } | |
5301 | |
7 | 5302 return size; |
5303 } | |
5304 | |
5305 /* | |
5306 * Goto byte in buffer with offset 'cnt'. | |
5307 */ | |
5308 void | |
7827
41789f16d6b2
commit https://github.com/vim/vim/commit/52ea13da0fe86df1abf34de52841e367035170c0
Christian Brabandt <cb@256bit.org>
parents:
7803
diff
changeset
|
5309 goto_byte(long cnt) |
7 | 5310 { |
5311 long boff = cnt; | |
5312 linenr_T lnum; | |
5313 | |
5314 ml_flush_line(curbuf); /* cached line may be dirty */ | |
5315 setpcmark(); | |
5316 if (boff) | |
5317 --boff; | |
5318 lnum = ml_find_line_or_offset(curbuf, (linenr_T)0, &boff); | |
5319 if (lnum < 1) /* past the end */ | |
5320 { | |
5321 curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count; | |
5322 curwin->w_curswant = MAXCOL; | |
5323 coladvance((colnr_T)MAXCOL); | |
5324 } | |
5325 else | |
5326 { | |
5327 curwin->w_cursor.lnum = lnum; | |
5328 curwin->w_cursor.col = (colnr_T)boff; | |
572 | 5329 # ifdef FEAT_VIRTUALEDIT |
5330 curwin->w_cursor.coladd = 0; | |
5331 # endif | |
7 | 5332 curwin->w_set_curswant = TRUE; |
5333 } | |
5334 check_cursor(); | |
5335 | |
5336 # ifdef FEAT_MBYTE | |
5337 /* Make sure the cursor is on the first byte of a multi-byte char. */ | |
5338 if (has_mbyte) | |
5339 mb_adjust_cursor(); | |
5340 # endif | |
5341 } | |
5342 #endif |