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