2016-08-29 22:49:24 +02:00
/* vi:set ts=8 sts=4 sw=4 noet:
2004-06-13 20:20:40 +00:00
*
* VIM - Vi IMproved by Bram Moolenaar
*
* Do " :help uganda " in Vim to read copying and usage conditions .
* Do " :help credits " in Vim to see a list of people who contributed .
* See README . txt for an overview of the Vim source code .
*/
2019-12-04 21:57:43 +01:00
// for debugging
// #define CHECK(c, s) do { if (c) emsg((s)); } while (0)
2018-04-10 18:47:20 +02:00
# define CHECK(c, s) do { /**/ } while (0)
2004-06-13 20:20:40 +00:00
/*
* memline . c : Contains the functions for appending , deleting and changing the
2006-01-12 23:22:24 +00:00
* text lines . The memfile functions are used to store the information in
* blocks of memory , backed up by a file . The structure of the information is
* a tree . The root of the tree is a pointer block . The leaves of the tree
* are data blocks . In between may be several layers of pointer blocks ,
* forming branches .
2004-06-13 20:20:40 +00:00
*
* Three types of blocks are used :
* - Block nr 0 contains information for recovery
* - Pointer blocks contain list of pointers to other blocks .
* - Data blocks contain the actual text .
*
* Block nr 0 contains the block0 structure ( see below ) .
*
* Block nr 1 is the first pointer block . It is the root of the tree .
* Other pointer blocks are branches .
*
* If a line is too big to fit in a single page , the block containing that
* line is made big enough to hold the line . It may span several pages .
* Otherwise all blocks are one page .
*
* A data block that was filled when starting to edit a file and was not
* changed since then , can have a negative block number . This means that it
* has not yet been assigned a place in the file . When recovering , the lines
* in this data block can be read from the original file . When the block is
* changed ( lines appended / deleted / changed ) or when it is flushed it gets a
* positive number . Use mf_trans_del ( ) to get the new number , before calling
* mf_get ( ) .
*/
# include "vim.h"
2019-12-04 21:57:43 +01:00
# ifndef UNIX // it's in os_unix.h for Unix
2004-06-13 20:20:40 +00:00
# include <time.h>
# endif
2006-11-01 17:12:57 +00:00
# if defined(SASC) || defined(__amigaos4__)
2019-12-04 21:57:43 +01:00
# include <proto / dos.h> // for Open() and Close()
2004-06-13 20:20:40 +00:00
# endif
2019-12-04 21:57:43 +01:00
typedef struct block0 ZERO_BL ; // contents of the first block
typedef struct pointer_block PTR_BL ; // contents of a pointer block
typedef struct data_block DATA_BL ; // contents of a data block
typedef struct pointer_entry PTR_EN ; // block/line-count pair
2004-06-13 20:20:40 +00:00
2019-12-04 21:57:43 +01:00
# define DATA_ID (('d' << 8) + 'a') // data block id
# define PTR_ID (('p' << 8) + 't') // pointer block id
# define BLOCK0_ID0 'b' // block 0 id 0
# define BLOCK0_ID1 '0' // block 0 id 1
# define BLOCK0_ID1_C0 'c' // block 0 id 1 'cm' 0
# define BLOCK0_ID1_C1 'C' // block 0 id 1 'cm' 1
# define BLOCK0_ID1_C2 'd' // block 0 id 1 'cm' 2
2023-05-27 18:02:55 +01:00
// BLOCK0_ID1_C3 and BLOCK0_ID1_C4 are for libsodium encryption. However, for
2023-04-24 18:11:35 +01:00
// these the swapfile is disabled, thus they will not be used. Added for
// consistency anyway.
# define BLOCK0_ID1_C3 'S' // block 0 id 1 'cm' 3
# define BLOCK0_ID1_C4 's' // block 0 id 1 'cm' 4
2014-08-10 13:38:34 +02:00
# if defined(FEAT_CRYPT)
static int id1_codes [ ] = {
2019-12-04 21:57:43 +01:00
BLOCK0_ID1_C0 , // CRYPT_M_ZIP
BLOCK0_ID1_C1 , // CRYPT_M_BF
BLOCK0_ID1_C2 , // CRYPT_M_BF2
2021-06-20 14:02:16 +02:00
BLOCK0_ID1_C3 , // CRYPT_M_SOD - Unused!
2023-04-24 18:11:35 +01:00
BLOCK0_ID1_C4 , // CRYPT_M_SOD2 - Unused!
2014-08-10 13:38:34 +02:00
} ;
# endif
2004-06-13 20:20:40 +00:00
/*
* pointer to a block , used in a pointer block
*/
struct pointer_entry
{
2019-12-04 21:57:43 +01:00
blocknr_T pe_bnum ; // block number
linenr_T pe_line_count ; // number of lines in this branch
linenr_T pe_old_lnum ; // lnum for this block (for recovery)
int pe_page_count ; // number of pages in block pe_bnum
2004-06-13 20:20:40 +00:00
} ;
/*
* A pointer block contains a list of branches in the tree .
*/
struct pointer_block
{
2019-12-04 21:57:43 +01:00
short_u pb_id ; // ID for pointer block: PTR_ID
short_u pb_count ; // number of pointers in this block
short_u pb_count_max ; // maximum value for pb_count
PTR_EN pb_pointer [ 1 ] ; // list of pointers to blocks (actually longer)
// followed by empty space until end of page
2004-06-13 20:20:40 +00:00
} ;
2023-04-22 21:14:26 +01:00
// Value for pb_count_max.
# define PB_COUNT_MAX(mfp) (short_u)(((mfp)->mf_page_size - offsetof(PTR_BL, pb_pointer)) / sizeof(PTR_EN))
2004-06-13 20:20:40 +00:00
/*
* A data block is a leaf in the tree .
*
* The text of the lines is at the end of the block . The text of the first line
* in the block is put at the end , the text of the second line in front of it ,
* etc . Thus the order of the lines is the opposite of the line number .
*/
struct data_block
{
2019-12-04 21:57:43 +01:00
short_u db_id ; // ID for data block: DATA_ID
unsigned db_free ; // free space available
unsigned db_txt_start ; // byte where text starts
unsigned db_txt_end ; // byte just after data block
linenr_T db_line_count ; // number of lines in this block
unsigned db_index [ 1 ] ; // index for start of line (actually bigger)
2020-01-26 22:00:26 +01:00
// followed by empty space up to db_txt_start
2019-12-04 21:57:43 +01:00
// followed by the text in the lines until
// end of page
2004-06-13 20:20:40 +00:00
} ;
/*
* The low bits of db_index hold the actual index . The topmost bit is
* used for the global command to be able to mark a line .
* This method is not clean , but otherwise there would be at least one extra
* byte used for each line .
* The mark has to be in this place to keep it with the correct line when other
* lines are inserted or deleted .
*/
# define DB_MARKED ((unsigned)1 << ((sizeof(unsigned) * 8) - 1))
# define DB_INDEX_MASK (~DB_MARKED)
2019-12-04 21:57:43 +01:00
# define INDEX_SIZE (sizeof(unsigned)) // size of one db_index entry
2023-02-01 13:11:15 +00:00
# define HEADER_SIZE (offsetof(DATA_BL, db_index)) // size of data block header
2004-06-13 20:20:40 +00:00
2019-12-04 21:57:43 +01:00
# define B0_FNAME_SIZE_ORG 900 // what it was in older versions
# define B0_FNAME_SIZE_NOCRYPT 898 // 2 bytes used for other things
# define B0_FNAME_SIZE_CRYPT 890 // 10 bytes used for other things
2004-12-19 22:46:22 +00:00
# define B0_UNAME_SIZE 40
# define B0_HNAME_SIZE 40
2004-06-13 20:20:40 +00:00
/*
* Restrict the numbers to 32 bits , otherwise most compilers will complain .
* This won ' t detect a 64 bit machine that only swaps a byte in the top 32
* bits , but that is crazy anyway .
*/
# define B0_MAGIC_LONG 0x30313233L
# define B0_MAGIC_INT 0x20212223L
# define B0_MAGIC_SHORT 0x10111213L
# define B0_MAGIC_CHAR 0x55
/*
* Block zero holds all info about the swap file .
*
* NOTE : DEFINITION OF BLOCK 0 SHOULD NOT CHANGE ! It would make all existing
* swap files unusable !
*
* If size of block0 changes anyway , adjust MIN_SWAP_PAGE_SIZE in vim . h ! !
*
2007-05-10 19:30:25 +00:00
* This block is built up of single bytes , to make it portable across
2004-06-13 20:20:40 +00:00
* different machines . b0_magic_ * is used to check the byte order and size of
* variables , because the rest of the swap file is not portable .
*/
struct block0
{
2019-12-04 21:57:43 +01:00
char_u b0_id [ 2 ] ; // id for block 0: BLOCK0_ID0 and BLOCK0_ID1,
// BLOCK0_ID1_C0, BLOCK0_ID1_C1, etc.
char_u b0_version [ 10 ] ; // Vim version string
char_u b0_page_size [ 4 ] ; // number of bytes per page
char_u b0_mtime [ 4 ] ; // last modification time of file
char_u b0_ino [ 4 ] ; // inode of b0_fname
char_u b0_pid [ 4 ] ; // process id of creator (or 0)
char_u b0_uname [ B0_UNAME_SIZE ] ; // name of user (uid if no name)
char_u b0_hname [ B0_HNAME_SIZE ] ; // host name (if it has a name)
char_u b0_fname [ B0_FNAME_SIZE_ORG ] ; // name of file being edited
long b0_magic_long ; // check for byte order of long
int b0_magic_int ; // check for byte order of int
short b0_magic_short ; // check for byte order of short
char_u b0_magic_char ; // check for last char
2004-06-13 20:20:40 +00:00
} ;
2004-12-19 22:46:22 +00:00
/*
2006-01-12 23:22:24 +00:00
* Note : b0_dirty and b0_flags are put at the end of the file name . For very
2004-12-19 22:46:22 +00:00
* long file names in older versions of Vim they are invalid .
* The ' fileencoding ' comes before b0_flags , with a NUL in front . But only
* when there is room , for very long file names it ' s omitted .
*/
# define B0_DIRTY 0x55
2010-06-21 06:15:46 +02:00
# define b0_dirty b0_fname[B0_FNAME_SIZE_ORG - 1]
2004-12-19 22:46:22 +00:00
/*
* The b0_flags field is new in Vim 7.0 .
*/
2010-06-21 06:15:46 +02:00
# define b0_flags b0_fname[B0_FNAME_SIZE_ORG - 2]
/*
* Crypt seed goes here , 8 bytes . New in Vim 7.3 .
* Without encryption these bytes may be used for ' fenc ' .
*/
# define b0_seed b0_fname[B0_FNAME_SIZE_ORG - 2 - MF_SEED_LEN]
2004-12-19 22:46:22 +00:00
2019-12-04 21:57:43 +01:00
// The lowest two bits contain the fileformat. Zero means it's not set
// (compatible with Vim 6.x), otherwise it's EOL_UNIX + 1, EOL_DOS + 1 or
// EOL_MAC + 1.
2004-12-19 22:46:22 +00:00
# define B0_FF_MASK 3
2019-12-04 21:57:43 +01:00
// Swap file is in directory of edited file. Used to find the file from
// different mount points.
2004-12-19 22:46:22 +00:00
# define B0_SAME_DIR 4
2019-12-04 21:57:43 +01:00
// The 'fileencoding' is at the end of b0_fname[], with a NUL in front of it.
// When empty there is only the NUL.
2004-12-19 22:46:22 +00:00
# define B0_HAS_FENC 8
2004-06-13 20:20:40 +00:00
2019-12-04 21:57:43 +01:00
# define STACK_INCR 5 // nr of entries added to ml_stack at a time
2004-06-13 20:20:40 +00:00
/*
* The line number where the first mark may be is remembered .
* If it is 0 there are no marks at all .
* ( always used for the current buffer only , no buffer change possible while
* executing a global command ) .
*/
static linenr_T lowest_marked = 0 ;
/*
* arguments for ml_find_line ( )
*/
2019-12-04 21:57:43 +01:00
# define ML_DELETE 0x11 // delete line
# define ML_INSERT 0x12 // insert line
# define ML_FIND 0x13 // just find the line
# define ML_FLUSH 0x02 // flush locked block
2022-02-16 19:24:07 +00:00
# define ML_SIMPLE(x) ((x) & 0x10) // DEL, INS or FIND
2004-06-13 20:20:40 +00:00
2019-12-04 21:57:43 +01:00
// argument for ml_upd_block0()
2010-06-21 06:15:46 +02:00
typedef enum {
2019-12-04 21:57:43 +01:00
UB_FNAME = 0 // update timestamp and filename
, UB_SAME_DIR // update the B0_SAME_DIR flag
, UB_CRYPT // update crypt key
2010-06-21 06:15:46 +02:00
} upd_block0_T ;
# ifdef FEAT_CRYPT
2016-01-29 22:36:45 +01:00
static void ml_set_b0_crypt ( buf_T * buf , ZERO_BL * b0p ) ;
2010-06-21 06:15:46 +02:00
# endif
2016-01-29 22:36:45 +01:00
static void ml_upd_block0 ( buf_T * buf , upd_block0_T what ) ;
static void set_b0_fname ( ZERO_BL * , buf_T * buf ) ;
static void set_b0_dir_flag ( ZERO_BL * b0p , buf_T * buf ) ;
static void add_b0_fenc ( ZERO_BL * b0p , buf_T * buf ) ;
static time_t swapfile_info ( char_u * ) ;
static int recov_file_names ( char_u * * , char_u * , int prepend_dot ) ;
static char_u * findswapname ( buf_T * , char_u * * , char_u * ) ;
static void ml_flush_line ( buf_T * ) ;
static bhdr_T * ml_new_data ( memfile_T * , int , int ) ;
static bhdr_T * ml_new_ptr ( memfile_T * ) ;
static bhdr_T * ml_find_line ( buf_T * , linenr_T , int ) ;
static int ml_add_stack ( buf_T * ) ;
static void ml_lineadd ( buf_T * , int ) ;
static int b0_magic_wrong ( ZERO_BL * ) ;
2004-06-13 20:20:40 +00:00
# ifdef CHECK_INODE
2016-01-29 22:36:45 +01:00
static int fnamecmp_ino ( char_u * , char_u * , long ) ;
2004-06-13 20:20:40 +00:00
# endif
2016-01-29 22:36:45 +01:00
static void long_to_char ( long , char_u * ) ;
static long char_to_long ( char_u * ) ;
2010-06-21 06:15:46 +02:00
# ifdef FEAT_CRYPT
2016-07-01 17:17:39 +02:00
static cryptstate_T * ml_crypt_prepare ( memfile_T * mfp , off_T offset , int reading ) ;
2010-06-21 06:15:46 +02:00
# endif
2004-06-13 20:20:40 +00:00
# ifdef FEAT_BYTEOFF
2016-01-29 22:36:45 +01:00
static void ml_updatechunk ( buf_T * buf , long line , long len , int updtype ) ;
2004-06-13 20:20:40 +00:00
# endif
/*
2006-01-12 23:22:24 +00:00
* Open a new memline for " buf " .
2004-06-13 20:20:40 +00:00
*
2006-01-12 23:22:24 +00:00
* Return FAIL for failure , OK otherwise .
2004-06-13 20:20:40 +00:00
*/
int
2016-01-30 18:51:09 +01:00
ml_open ( buf_T * buf )
2004-06-13 20:20:40 +00:00
{
memfile_T * mfp ;
bhdr_T * hp = NULL ;
ZERO_BL * b0p ;
PTR_BL * pp ;
DATA_BL * dp ;
2006-01-12 23:22:24 +00:00
/*
* init fields in memline struct
*/
2019-12-04 21:57:43 +01:00
buf - > b_ml . ml_stack_size = 0 ; // no stack yet
buf - > b_ml . ml_stack = NULL ; // no stack yet
buf - > b_ml . ml_stack_top = 0 ; // nothing in the stack
buf - > b_ml . ml_locked = NULL ; // no cached block
buf - > b_ml . ml_line_lnum = 0 ; // no cached line
2004-06-13 20:20:40 +00:00
# ifdef FEAT_BYTEOFF
2006-01-12 23:22:24 +00:00
buf - > b_ml . ml_chunksize = NULL ;
2021-08-25 17:31:37 +02:00
buf - > b_ml . ml_usedchunks = 0 ;
2004-06-13 20:20:40 +00:00
# endif
2020-10-24 20:49:43 +02:00
if ( cmdmod . cmod_flags & CMOD_NOSWAPFILE )
2014-03-23 16:04:02 +01:00
buf - > b_p_swf = FALSE ;
2006-01-12 23:22:24 +00:00
/*
* When ' updatecount ' is non - zero swap file may be opened later .
*/
if ( p_uc & & buf - > b_p_swf )
buf - > b_may_swap = TRUE ;
2004-06-13 20:20:40 +00:00
else
2006-01-12 23:22:24 +00:00
buf - > b_may_swap = FALSE ;
2004-06-13 20:20:40 +00:00
2006-01-12 23:22:24 +00:00
/*
* Open the memfile . No swap file is created yet .
*/
2004-06-13 20:20:40 +00:00
mfp = mf_open ( NULL , 0 ) ;
if ( mfp = = NULL )
goto error ;
2006-01-12 23:22:24 +00:00
buf - > b_ml . ml_mfp = mfp ;
2010-06-21 06:15:46 +02:00
# ifdef FEAT_CRYPT
mfp - > mf_buffer = buf ;
# endif
2006-01-12 23:22:24 +00:00
buf - > b_ml . ml_flags = ML_EMPTY ;
buf - > b_ml . ml_line_count = 1 ;
2004-06-13 20:20:40 +00:00
/*
* fill block0 struct and write page 0
*/
if ( ( hp = mf_new ( mfp , FALSE , 1 ) ) = = NULL )
goto error ;
if ( hp - > bh_bnum ! = 0 )
{
2023-05-31 17:12:14 +01:00
iemsg ( e_didnt_get_block_nr_zero ) ;
2004-06-13 20:20:40 +00:00
goto error ;
}
b0p = ( ZERO_BL * ) ( hp - > bh_data ) ;
b0p - > b0_id [ 0 ] = BLOCK0_ID0 ;
b0p - > b0_id [ 1 ] = BLOCK0_ID1 ;
b0p - > b0_magic_long = ( long ) B0_MAGIC_LONG ;
b0p - > b0_magic_int = ( int ) B0_MAGIC_INT ;
b0p - > b0_magic_short = ( short ) B0_MAGIC_SHORT ;
b0p - > b0_magic_char = B0_MAGIC_CHAR ;
2018-05-26 17:35:27 +02:00
mch_memmove ( b0p - > b0_version , " VIM " , 4 ) ;
2004-06-13 20:20:40 +00:00
STRNCPY ( b0p - > b0_version + 4 , Version , 6 ) ;
long_to_char ( ( long ) mfp - > mf_page_size , b0p - > b0_page_size ) ;
2006-01-12 23:22:24 +00:00
2006-03-24 22:46:53 +00:00
# ifdef FEAT_SPELL
if ( ! buf - > b_spell )
# endif
2006-01-12 23:22:24 +00:00
{
b0p - > b0_dirty = buf - > b_changed ? B0_DIRTY : 0 ;
b0p - > b0_flags = get_fileformat ( buf ) + 1 ;
set_b0_fname ( b0p , buf ) ;
( void ) get_user_name ( b0p - > b0_uname , B0_UNAME_SIZE ) ;
b0p - > b0_uname [ B0_UNAME_SIZE - 1 ] = NUL ;
mch_get_host_name ( b0p - > b0_hname , B0_HNAME_SIZE ) ;
b0p - > b0_hname [ B0_HNAME_SIZE - 1 ] = NUL ;
long_to_char ( mch_get_pid ( ) , b0p - > b0_pid ) ;
2010-06-21 06:15:46 +02:00
# ifdef FEAT_CRYPT
2014-08-10 13:38:34 +02:00
ml_set_b0_crypt ( buf , b0p ) ;
2010-06-21 06:15:46 +02:00
# endif
2006-01-12 23:22:24 +00:00
}
2004-06-13 20:20:40 +00:00
/*
* Always sync block number 0 to disk , so we can check the file name in
2010-06-21 06:15:46 +02:00
* the swap file in findswapname ( ) . Don ' t do this for a help files or
* a spell buffer though .
2004-06-13 20:20:40 +00:00
* Only works when there ' s a swapfile , otherwise it ' s done when the file
* is created .
*/
mf_put ( mfp , hp , TRUE , FALSE ) ;
2006-01-12 23:22:24 +00:00
if ( ! buf - > b_help & & ! B_SPELL ( buf ) )
2004-06-13 20:20:40 +00:00
( void ) mf_sync ( mfp , 0 ) ;
2006-01-12 23:22:24 +00:00
/*
* Fill in root pointer block and write page 1.
*/
2004-06-13 20:20:40 +00:00
if ( ( hp = ml_new_ptr ( mfp ) ) = = NULL )
goto error ;
if ( hp - > bh_bnum ! = 1 )
{
2023-05-31 17:12:14 +01:00
iemsg ( e_didnt_get_block_nr_one ) ;
2004-06-13 20:20:40 +00:00
goto error ;
}
pp = ( PTR_BL * ) ( hp - > bh_data ) ;
pp - > pb_count = 1 ;
pp - > pb_pointer [ 0 ] . pe_bnum = 2 ;
pp - > pb_pointer [ 0 ] . pe_page_count = 1 ;
pp - > pb_pointer [ 0 ] . pe_old_lnum = 1 ;
2019-12-04 21:57:43 +01:00
pp - > pb_pointer [ 0 ] . pe_line_count = 1 ; // line count after insertion
2004-06-13 20:20:40 +00:00
mf_put ( mfp , hp , TRUE , FALSE ) ;
2006-01-12 23:22:24 +00:00
/*
* Allocate first data block and create an empty line 1.
*/
2004-06-13 20:20:40 +00:00
if ( ( hp = ml_new_data ( mfp , FALSE , 1 ) ) = = NULL )
goto error ;
if ( hp - > bh_bnum ! = 2 )
{
2023-05-31 17:12:14 +01:00
iemsg ( e_didnt_get_block_nr_two ) ;
2004-06-13 20:20:40 +00:00
goto error ;
}
dp = ( DATA_BL * ) ( hp - > bh_data ) ;
2019-12-04 21:57:43 +01:00
dp - > db_index [ 0 ] = - - dp - > db_txt_start ; // at end of block
2004-06-13 20:20:40 +00:00
dp - > db_free - = 1 + INDEX_SIZE ;
dp - > db_line_count = 1 ;
2019-12-04 21:57:43 +01:00
* ( ( char_u * ) dp + dp - > db_txt_start ) = NUL ; // empty line
2004-06-13 20:20:40 +00:00
return OK ;
error :
if ( mfp ! = NULL )
{
if ( hp )
mf_put ( mfp , hp , FALSE , FALSE ) ;
2019-12-04 21:57:43 +01:00
mf_close ( mfp , TRUE ) ; // will also free(mfp->mf_fname)
2004-06-13 20:20:40 +00:00
}
2006-01-12 23:22:24 +00:00
buf - > b_ml . ml_mfp = NULL ;
2004-06-13 20:20:40 +00:00
return FAIL ;
}
2010-06-21 06:15:46 +02:00
# if defined(FEAT_CRYPT) || defined(PROTO)
2023-06-27 18:57:10 +01:00
/*
* Swapfile encryption is not supported by XChaCha20 . If this crypt method is
* used then disable the swapfile , to avoid plain text being written to disk ,
* and return TRUE .
* Otherwise return FALSE .
*/
static int
crypt_may_close_swapfile ( buf_T * buf , char_u * key , int method )
{
if ( crypt_method_is_sodium ( method ) & & * key ! = NUL )
{
mf_close_file ( buf , TRUE ) ;
buf - > b_p_swf = FALSE ;
return TRUE ;
}
return FALSE ;
}
2014-08-13 21:58:28 +02:00
/*
* Prepare encryption for " buf " for the current key and method .
*/
static void
2016-01-30 18:51:09 +01:00
ml_set_mfp_crypt ( buf_T * buf )
2014-08-13 21:58:28 +02:00
{
2023-01-14 12:32:28 +00:00
if ( * buf - > b_p_key = = NUL )
return ;
2014-08-13 21:58:28 +02:00
2023-01-14 12:32:28 +00:00
int method_nr = crypt_get_method_nr ( buf ) ;
if ( method_nr > CRYPT_M_ZIP & & method_nr < CRYPT_M_SOD )
{
// Generate a seed and store it in the memfile.
sha2_seed ( buf - > b_ml . ml_mfp - > mf_seed , MF_SEED_LEN , NULL , 0 ) ;
2014-08-13 21:58:28 +02:00
}
2023-06-27 18:57:10 +01:00
# ifdef FEAT_SODIUM
2023-04-23 17:50:22 +01:00
else if ( crypt_method_is_sodium ( method_nr ) )
2023-06-27 18:57:10 +01:00
crypt_sodium_randombytes_buf ( buf - > b_ml . ml_mfp - > mf_seed , MF_SEED_LEN ) ;
# endif
2014-08-13 21:58:28 +02:00
}
2010-06-21 06:15:46 +02:00
/*
* Prepare encryption for " buf " with block 0 " b0p " .
2023-04-24 18:11:35 +01:00
* Note : should not be called with libsodium encryption , since xchacha20 does
* not support swapfile encryption .
2010-06-21 06:15:46 +02:00
*/
static void
2016-01-30 18:51:09 +01:00
ml_set_b0_crypt ( buf_T * buf , ZERO_BL * b0p )
2010-06-21 06:15:46 +02:00
{
if ( * buf - > b_p_key = = NUL )
b0p - > b0_id [ 1 ] = BLOCK0_ID1 ;
else
{
2014-08-10 13:38:34 +02:00
int method_nr = crypt_get_method_nr ( buf ) ;
b0p - > b0_id [ 1 ] = id1_codes [ method_nr ] ;
2021-06-20 14:02:16 +02:00
if ( method_nr > CRYPT_M_ZIP & & method_nr < CRYPT_M_SOD )
2010-06-21 06:15:46 +02:00
{
2019-12-04 21:57:43 +01:00
// Generate a seed and store it in block 0 and in the memfile.
2010-06-21 06:15:46 +02:00
sha2_seed ( & b0p - > b0_seed , MF_SEED_LEN , NULL , 0 ) ;
mch_memmove ( buf - > b_ml . ml_mfp - > mf_seed , & b0p - > b0_seed , MF_SEED_LEN ) ;
}
}
}
/*
* Called after the crypt key or ' cryptmethod ' was changed for " buf " .
* Will apply this to the swapfile .
* " old_key " is the previous key . It is equal to buf - > b_p_key when
* ' cryptmethod ' is changed .
2010-07-20 17:32:38 +02:00
* " old_cm " is the previous ' cryptmethod ' . It is equal to the current
* ' cryptmethod ' when ' key ' is changed .
2010-06-21 06:15:46 +02:00
*/
void
2016-01-30 18:51:09 +01:00
ml_set_crypt_key (
buf_T * buf ,
char_u * old_key ,
char_u * old_cm )
2010-06-21 06:15:46 +02:00
{
memfile_T * mfp = buf - > b_ml . ml_mfp ;
bhdr_T * hp ;
int page_count ;
int idx ;
long error ;
infoptr_T * ip ;
PTR_BL * pp ;
DATA_BL * dp ;
blocknr_T bnum ;
int top ;
2015-06-09 18:35:25 +02:00
int old_method ;
2010-06-21 06:15:46 +02:00
2021-06-20 14:02:16 +02:00
if ( mfp = = NULL | | mfp - > mf_fd < 0 )
2019-12-04 21:57:43 +01:00
return ; // no memfile yet, nothing to do
2015-06-09 18:35:25 +02:00
old_method = crypt_method_nr_from_name ( old_cm ) ;
2023-06-27 18:57:10 +01:00
# ifdef FEAT_CRYPT
if ( crypt_may_close_swapfile ( buf , buf - > b_p_key , crypt_get_method_nr ( buf ) ) )
2021-06-20 14:02:16 +02:00
return ;
2023-06-27 18:57:10 +01:00
# endif
2023-04-24 18:11:35 +01:00
2019-12-04 21:57:43 +01:00
// First make sure the swapfile is in a consistent state, using the old
// key and method.
2015-06-09 18:35:25 +02:00
{
char_u * new_key = buf - > b_p_key ;
char_u * new_buf_cm = buf - > b_p_cm ;
buf - > b_p_key = old_key ;
buf - > b_p_cm = old_cm ;
ml_preserve ( buf , FALSE ) ;
buf - > b_p_key = new_key ;
buf - > b_p_cm = new_buf_cm ;
}
2010-06-21 06:15:46 +02:00
2019-12-04 21:57:43 +01:00
// Set the key, method and seed to be used for reading, these must be the
// old values.
2010-06-21 06:15:46 +02:00
mfp - > mf_old_key = old_key ;
2015-06-09 18:35:25 +02:00
mfp - > mf_old_cm = old_method ;
if ( old_method > 0 & & * old_key ! = NUL )
2010-06-21 06:15:46 +02:00
mch_memmove ( mfp - > mf_old_seed , mfp - > mf_seed , MF_SEED_LEN ) ;
2019-12-04 21:57:43 +01:00
// Update block 0 with the crypt flag and may set a new seed.
2010-06-21 06:15:46 +02:00
ml_upd_block0 ( buf , UB_CRYPT ) ;
if ( mfp - > mf_infile_count > 2 )
{
/*
* Need to read back all data blocks from disk , decrypt them with the
* old key / method and mark them to be written . The algorithm is
* similar to what happens in ml_recover ( ) , but we skip negative block
* numbers .
*/
2019-12-04 21:57:43 +01:00
ml_flush_line ( buf ) ; // flush buffered line
( void ) ml_find_line ( buf , ( linenr_T ) 0 , ML_FLUSH ) ; // flush locked block
2010-06-21 06:15:46 +02:00
hp = NULL ;
2019-12-04 21:57:43 +01:00
bnum = 1 ; // start with block 1
page_count = 1 ; // which is 1 page
idx = 0 ; // start with first index in block 1
2010-06-21 06:15:46 +02:00
error = 0 ;
buf - > b_ml . ml_stack_top = 0 ;
2018-02-10 18:45:26 +01:00
VIM_CLEAR ( buf - > b_ml . ml_stack ) ;
2019-12-04 21:57:43 +01:00
buf - > b_ml . ml_stack_size = 0 ; // no stack yet
2010-06-21 06:15:46 +02:00
for ( ; ! got_int ; line_breakcheck ( ) )
{
if ( hp ! = NULL )
2019-12-04 21:57:43 +01:00
mf_put ( mfp , hp , FALSE , FALSE ) ; // release previous block
2010-06-21 06:15:46 +02:00
2019-12-04 21:57:43 +01:00
// get the block (pointer or data)
2022-01-28 15:28:04 +00:00
if ( ( hp = mf_get ( mfp , bnum , page_count ) ) = = NULL )
2010-06-21 06:15:46 +02:00
{
if ( bnum = = 1 )
break ;
+ + error ;
}
else
{
pp = ( PTR_BL * ) ( hp - > bh_data ) ;
2019-12-04 21:57:43 +01:00
if ( pp - > pb_id = = PTR_ID ) // it is a pointer block
2010-06-21 06:15:46 +02:00
{
if ( pp - > pb_count = = 0 )
{
2019-12-04 21:57:43 +01:00
// empty block?
2010-06-21 06:15:46 +02:00
+ + error ;
}
2019-12-04 21:57:43 +01:00
else if ( idx < ( int ) pp - > pb_count ) // go a block deeper
2010-06-21 06:15:46 +02:00
{
if ( pp - > pb_pointer [ idx ] . pe_bnum < 0 )
{
2019-12-04 21:57:43 +01:00
// Skip data block with negative block number.
// Should not happen, because of the ml_preserve()
// above. Get same block again for next index.
2019-01-03 21:55:32 +01:00
+ + idx ;
2010-06-21 06:15:46 +02:00
continue ;
}
2019-12-04 21:57:43 +01:00
// going one block deeper in the tree, new entry in
// stack
2010-06-21 06:15:46 +02:00
if ( ( top = ml_add_stack ( buf ) ) < 0 )
{
+ + error ;
2019-12-04 21:57:43 +01:00
break ; // out of memory
2010-06-21 06:15:46 +02:00
}
ip = & ( buf - > b_ml . ml_stack [ top ] ) ;
ip - > ip_bnum = bnum ;
ip - > ip_index = idx ;
bnum = pp - > pb_pointer [ idx ] . pe_bnum ;
page_count = pp - > pb_pointer [ idx ] . pe_page_count ;
2015-06-09 18:35:25 +02:00
idx = 0 ;
2010-06-21 06:15:46 +02:00
continue ;
}
}
2019-12-04 21:57:43 +01:00
else // not a pointer block
2010-06-21 06:15:46 +02:00
{
dp = ( DATA_BL * ) ( hp - > bh_data ) ;
2019-12-04 21:57:43 +01:00
if ( dp - > db_id ! = DATA_ID ) // block id wrong
2010-06-21 06:15:46 +02:00
+ + error ;
else
{
2019-12-04 21:57:43 +01:00
// It is a data block, need to write it back to disk.
2010-06-21 06:15:46 +02:00
mf_put ( mfp , hp , TRUE , FALSE ) ;
hp = NULL ;
}
}
}
2019-12-04 21:57:43 +01:00
if ( buf - > b_ml . ml_stack_top = = 0 ) // finished
2010-06-21 06:15:46 +02:00
break ;
2019-12-04 21:57:43 +01:00
// go one block up in the tree
2010-06-21 06:15:46 +02:00
ip = & ( buf - > b_ml . ml_stack [ - - ( buf - > b_ml . ml_stack_top ) ] ) ;
bnum = ip - > ip_bnum ;
2019-12-04 21:57:43 +01:00
idx = ip - > ip_index + 1 ; // go to next index
2010-06-21 06:15:46 +02:00
page_count = 1 ;
}
2015-06-09 18:35:25 +02:00
if ( hp ! = NULL )
2019-12-04 21:57:43 +01:00
mf_put ( mfp , hp , FALSE , FALSE ) ; // release previous block
2010-12-08 13:17:03 +01:00
if ( error > 0 )
2022-01-05 17:49:15 +00:00
emsg ( _ ( e_error_while_updating_swap_file_crypt ) ) ;
2010-06-21 06:15:46 +02:00
}
mfp - > mf_old_key = NULL ;
}
# endif
2004-06-13 20:20:40 +00:00
/*
* ml_setname ( ) is called when the file name of " buf " has been changed .
* It may rename the swap file .
*/
void
2016-01-30 18:51:09 +01:00
ml_setname ( buf_T * buf )
2004-06-13 20:20:40 +00:00
{
int success = FALSE ;
memfile_T * mfp ;
char_u * fname ;
char_u * dirp ;
2016-02-23 14:53:34 +01:00
# if defined(MSWIN)
2004-06-13 20:20:40 +00:00
char_u * p ;
# endif
mfp = buf - > b_ml . ml_mfp ;
2019-12-04 21:57:43 +01:00
if ( mfp - > mf_fd < 0 ) // there is no swap file yet
2004-06-13 20:20:40 +00:00
{
/*
* When ' updatecount ' is 0 and ' noswapfile ' there is no swap file .
* For help files we will make a swap file now .
*/
2020-10-24 20:49:43 +02:00
if ( p_uc ! = 0 & & ( cmdmod . cmod_flags & CMOD_NOSWAPFILE ) = = 0 )
2019-12-04 21:57:43 +01:00
ml_open_file ( buf ) ; // create a swap file
2004-06-13 20:20:40 +00:00
return ;
}
/*
* Try all directories in the ' directory ' option .
*/
dirp = p_dir ;
for ( ; ; )
{
2019-12-04 21:57:43 +01:00
if ( * dirp = = NUL ) // tried all directories, fail
2004-06-13 20:20:40 +00:00
break ;
2004-12-29 21:03:02 +00:00
fname = findswapname ( buf , & dirp , mfp - > mf_fname ) ;
2019-12-04 21:57:43 +01:00
// alloc's fname
if ( dirp = = NULL ) // out of memory
2011-10-26 11:44:18 +02:00
break ;
2019-12-04 21:57:43 +01:00
if ( fname = = NULL ) // no file name found for this dir
2004-06-13 20:20:40 +00:00
continue ;
2016-02-23 14:53:34 +01:00
# if defined(MSWIN)
2004-06-13 20:20:40 +00:00
/*
* Set full pathname for swap file now , because a " :!cd dir " may
* change directory without us knowing it .
*/
p = FullName_save ( fname , FALSE ) ;
vim_free ( fname ) ;
fname = p ;
if ( fname = = NULL )
continue ;
# endif
2019-12-04 21:57:43 +01:00
// if the file name is the same we don't have to do anything
2004-06-13 20:20:40 +00:00
if ( fnamecmp ( fname , mfp - > mf_fname ) = = 0 )
{
vim_free ( fname ) ;
success = TRUE ;
break ;
}
2019-12-04 21:57:43 +01:00
// need to close the swap file before renaming
2004-06-13 20:20:40 +00:00
if ( mfp - > mf_fd > = 0 )
{
close ( mfp - > mf_fd ) ;
mfp - > mf_fd = - 1 ;
}
2019-12-04 21:57:43 +01:00
// try to rename the swap file
2004-06-13 20:20:40 +00:00
if ( vim_rename ( mfp - > mf_fname , fname ) = = 0 )
{
success = TRUE ;
vim_free ( mfp - > mf_fname ) ;
mfp - > mf_fname = fname ;
vim_free ( mfp - > mf_ffname ) ;
2016-02-23 14:53:34 +01:00
# if defined(MSWIN)
2019-12-04 21:57:43 +01:00
mfp - > mf_ffname = NULL ; // mf_fname is full pathname already
2004-06-13 20:20:40 +00:00
# else
mf_set_ffname ( mfp ) ;
# endif
2010-06-21 06:15:46 +02:00
ml_upd_block0 ( buf , UB_SAME_DIR ) ;
2004-06-13 20:20:40 +00:00
break ;
}
2019-12-04 21:57:43 +01:00
vim_free ( fname ) ; // this fname didn't work, try another
2004-06-13 20:20:40 +00:00
}
2019-12-04 21:57:43 +01:00
if ( mfp - > mf_fd = = - 1 ) // need to (re)open the swap file
2004-06-13 20:20:40 +00:00
{
mfp - > mf_fd = mch_open ( ( char * ) mfp - > mf_fname , O_RDWR | O_EXTRA , 0 ) ;
if ( mfp - > mf_fd < 0 )
{
2019-12-04 21:57:43 +01:00
// could not (re)open the swap file, what can we do????
2022-01-02 17:00:40 +00:00
emsg ( _ ( e_oops_lost_the_swap_file ) ) ;
2004-06-13 20:20:40 +00:00
return ;
}
2009-11-17 16:13:15 +00:00
# ifdef HAVE_FD_CLOEXEC
{
int fdflags = fcntl ( mfp - > mf_fd , F_GETFD ) ;
if ( fdflags > = 0 & & ( fdflags & FD_CLOEXEC ) = = 0 )
2016-02-07 15:14:01 +01:00
( void ) fcntl ( mfp - > mf_fd , F_SETFD , fdflags | FD_CLOEXEC ) ;
2009-11-17 16:13:15 +00:00
}
# endif
2004-06-13 20:20:40 +00:00
}
if ( ! success )
2022-01-02 17:00:40 +00:00
emsg ( _ ( e_could_not_rename_swap_file ) ) ;
2004-06-13 20:20:40 +00:00
}
/*
* Open a file for the memfile for all buffers that are not readonly or have
* been modified .
* Used when ' updatecount ' changes from zero to non - zero .
*/
void
2016-01-30 18:51:09 +01:00
ml_open_files ( void )
2004-06-13 20:20:40 +00:00
{
buf_T * buf ;
2016-07-24 22:04:11 +02:00
FOR_ALL_BUFFERS ( buf )
2004-06-13 20:20:40 +00:00
if ( ! buf - > b_p_ro | | buf - > b_changed )
ml_open_file ( buf ) ;
}
/*
* Open a swap file for an existing memfile , if there is no swap file yet .
* If we are unable to find a file name , mf_fname will be NULL
* and the memfile will be in memory only ( no recovery possible ) .
*/
void
2016-01-30 18:51:09 +01:00
ml_open_file ( buf_T * buf )
2004-06-13 20:20:40 +00:00
{
memfile_T * mfp ;
char_u * fname ;
char_u * dirp ;
mfp = buf - > b_ml . ml_mfp ;
2020-10-24 20:49:43 +02:00
if ( mfp = = NULL | | mfp - > mf_fd > = 0 | | ! buf - > b_p_swf
| | ( cmdmod . cmod_flags & CMOD_NOSWAPFILE ) )
2019-12-04 21:57:43 +01:00
return ; // nothing to do
2004-06-13 20:20:40 +00:00
2006-03-12 22:18:00 +00:00
# ifdef FEAT_SPELL
2019-12-04 21:57:43 +01:00
// For a spell buffer use a temp file name.
2006-01-12 23:22:24 +00:00
if ( buf - > b_spell )
{
2015-03-31 13:33:08 +02:00
fname = vim_tempname ( ' s ' , FALSE ) ;
2006-01-12 23:22:24 +00:00
if ( fname ! = NULL )
2019-12-04 21:57:43 +01:00
( void ) mf_open_file ( mfp , fname ) ; // consumes fname!
2006-01-12 23:22:24 +00:00
buf - > b_may_swap = FALSE ;
return ;
}
# endif
2004-06-13 20:20:40 +00:00
/*
* Try all directories in ' directory ' option .
*/
dirp = p_dir ;
for ( ; ; )
{
if ( * dirp = = NUL )
break ;
2019-12-04 21:57:43 +01:00
// There is a small chance that between choosing the swap file name
// and creating it, another Vim creates the file. In that case the
// creation will fail and we will use another directory.
fname = findswapname ( buf , & dirp , NULL ) ; // allocates fname
2011-10-26 11:44:18 +02:00
if ( dirp = = NULL )
2019-12-04 21:57:43 +01:00
break ; // out of memory
2004-06-13 20:20:40 +00:00
if ( fname = = NULL )
continue ;
2019-12-04 21:57:43 +01:00
if ( mf_open_file ( mfp , fname ) = = OK ) // consumes fname!
2004-06-13 20:20:40 +00:00
{
2023-05-27 18:02:55 +01:00
// don't sync yet in ml_sync_all()
mfp - > mf_dirty = MF_DIRTY_YES_NOSYNC ;
2016-02-23 14:53:34 +01:00
# if defined(MSWIN)
2004-06-13 20:20:40 +00:00
/*
* set full pathname for swap file now , because a " :!cd dir " may
* change directory without us knowing it .
*/
mf_fullname ( mfp ) ;
# endif
2010-06-21 06:15:46 +02:00
ml_upd_block0 ( buf , UB_SAME_DIR ) ;
2004-12-19 22:46:22 +00:00
2019-12-04 21:57:43 +01:00
// Flush block zero, so others can read it
2004-06-13 20:20:40 +00:00
if ( mf_sync ( mfp , MFS_ZERO ) = = OK )
2006-01-14 21:23:38 +00:00
{
2019-12-04 21:57:43 +01:00
// Mark all blocks that should be in the swapfile as dirty.
// Needed for when the 'swapfile' option was reset, so that
// the swap file was deleted, and then on again.
2006-01-14 21:23:38 +00:00
mf_set_dirty ( mfp ) ;
2004-06-13 20:20:40 +00:00
break ;
2006-01-14 21:23:38 +00:00
}
2019-12-04 21:57:43 +01:00
// Writing block 0 failed: close the file and try another dir
2004-06-13 20:20:40 +00:00
mf_close_file ( buf , FALSE ) ;
}
}
2019-10-19 17:01:28 +02:00
if ( * p_dir ! = NUL & & mfp - > mf_fname = = NULL )
2004-06-13 20:20:40 +00:00
{
2022-08-29 15:06:50 +01:00
need_wait_return = TRUE ; // call wait_return() later
2004-06-13 20:20:40 +00:00
+ + no_wait_return ;
2022-01-02 17:00:40 +00:00
( void ) semsg ( _ ( e_unable_to_open_swap_file_for_str_recovery_impossible ) ,
2012-10-03 18:25:00 +02:00
buf_spname ( buf ) ! = NULL ? buf_spname ( buf ) : buf - > b_fname ) ;
2004-06-13 20:20:40 +00:00
- - no_wait_return ;
}
2019-12-04 21:57:43 +01:00
// don't try to open a swap file again
2004-06-13 20:20:40 +00:00
buf - > b_may_swap = FALSE ;
}
/*
* If still need to create a swap file , and starting to edit a not - readonly
* file , or reading into an existing buffer , create a swap file now .
*/
void
2016-01-30 18:51:09 +01:00
check_need_swap (
2018-08-21 18:50:18 +02:00
int newfile ) // reading file into new buffer
2004-06-13 20:20:40 +00:00
{
2018-08-21 18:50:18 +02:00
int old_msg_silent = msg_silent ; // might be reset by an E325 message
2004-06-13 20:20:40 +00:00
if ( curbuf - > b_may_swap & & ( ! curbuf - > b_p_ro | | ! newfile ) )
ml_open_file ( curbuf ) ;
2018-08-21 18:50:18 +02:00
msg_silent = old_msg_silent ;
2004-06-13 20:20:40 +00:00
}
/*
* Close memline for buffer ' buf ' .
* If ' del_file ' is TRUE , delete the swap file
*/
void
2016-01-30 18:51:09 +01:00
ml_close ( buf_T * buf , int del_file )
2004-06-13 20:20:40 +00:00
{
2019-12-04 21:57:43 +01:00
if ( buf - > b_ml . ml_mfp = = NULL ) // not open
2004-06-13 20:20:40 +00:00
return ;
2019-12-04 21:57:43 +01:00
mf_close ( buf - > b_ml . ml_mfp , del_file ) ; // close the .swp file
2022-06-30 22:13:59 +01:00
if ( buf - > b_ml . ml_line_lnum ! = 0
& & ( buf - > b_ml . ml_flags & ( ML_LINE_DIRTY | ML_ALLOCATED ) ) )
2004-06-13 20:20:40 +00:00
vim_free ( buf - > b_ml . ml_line_ptr ) ;
vim_free ( buf - > b_ml . ml_stack ) ;
# ifdef FEAT_BYTEOFF
2018-02-10 18:45:26 +01:00
VIM_CLEAR ( buf - > b_ml . ml_chunksize ) ;
2004-06-13 20:20:40 +00:00
# endif
buf - > b_ml . ml_mfp = NULL ;
2019-12-04 21:57:43 +01:00
// Reset the "recovered" flag, give the ATTENTION prompt the next time
// this buffer is loaded.
2004-06-13 20:20:40 +00:00
buf - > b_flags & = ~ BF_RECOVERED ;
}
/*
* Close all existing memlines and memfiles .
* Only used when exiting .
* When ' del_file ' is TRUE , delete the memfiles .
2005-02-12 14:31:42 +00:00
* But don ' t delete files that were " :preserve " d when we are POSIX compatible .
2004-06-13 20:20:40 +00:00
*/
void
2016-01-30 18:51:09 +01:00
ml_close_all ( int del_file )
2004-06-13 20:20:40 +00:00
{
buf_T * buf ;
2016-07-24 22:04:11 +02:00
FOR_ALL_BUFFERS ( buf )
2005-02-12 14:31:42 +00:00
ml_close ( buf , del_file & & ( ( buf - > b_flags & BF_PRESERVED ) = = 0
| | vim_strchr ( p_cpo , CPO_PRESERVE ) = = NULL ) ) ;
2013-11-28 17:41:46 +01:00
# ifdef FEAT_SPELL
2019-12-04 21:57:43 +01:00
spell_delete_wordlist ( ) ; // delete the internal wordlist
2013-11-28 17:41:46 +01:00
# endif
2004-06-13 20:20:40 +00:00
# ifdef TEMPDIRNAMES
2019-12-04 21:57:43 +01:00
vim_deltempdir ( ) ; // delete created temp directory
2004-06-13 20:20:40 +00:00
# endif
}
/*
* Close all memfiles for not modified buffers .
* Only use just before exiting !
*/
void
2016-01-30 18:51:09 +01:00
ml_close_notmod ( void )
2004-06-13 20:20:40 +00:00
{
buf_T * buf ;
2016-07-24 22:04:11 +02:00
FOR_ALL_BUFFERS ( buf )
2004-06-13 20:20:40 +00:00
if ( ! bufIsChanged ( buf ) )
2019-12-04 21:57:43 +01:00
ml_close ( buf , TRUE ) ; // close all not-modified buffers
2004-06-13 20:20:40 +00:00
}
/*
* Update the timestamp in the . swp file .
* Used when the file has been written .
*/
void
2016-01-30 18:51:09 +01:00
ml_timestamp ( buf_T * buf )
2004-12-19 22:46:22 +00:00
{
2010-06-21 06:15:46 +02:00
ml_upd_block0 ( buf , UB_FNAME ) ;
}
/*
* Return FAIL when the ID of " b0p " is wrong .
*/
static int
2016-01-30 18:51:09 +01:00
ml_check_b0_id ( ZERO_BL * b0p )
2010-06-21 06:15:46 +02:00
{
if ( b0p - > b0_id [ 0 ] ! = BLOCK0_ID0
| | ( b0p - > b0_id [ 1 ] ! = BLOCK0_ID1
& & b0p - > b0_id [ 1 ] ! = BLOCK0_ID1_C0
2014-08-10 13:38:34 +02:00
& & b0p - > b0_id [ 1 ] ! = BLOCK0_ID1_C1
2021-06-20 14:02:16 +02:00
& & b0p - > b0_id [ 1 ] ! = BLOCK0_ID1_C2
2023-05-27 18:02:55 +01:00
& & b0p - > b0_id [ 1 ] ! = BLOCK0_ID1_C3
& & b0p - > b0_id [ 1 ] ! = BLOCK0_ID1_C4 )
2010-06-21 06:15:46 +02:00
)
return FAIL ;
return OK ;
2004-12-19 22:46:22 +00:00
}
/*
* Update the timestamp or the B0_SAME_DIR flag of the . swp file .
*/
static void
2016-01-30 18:51:09 +01:00
ml_upd_block0 ( buf_T * buf , upd_block0_T what )
2004-06-13 20:20:40 +00:00
{
memfile_T * mfp ;
bhdr_T * hp ;
ZERO_BL * b0p ;
mfp = buf - > b_ml . ml_mfp ;
2014-08-13 21:58:28 +02:00
if ( mfp = = NULL )
return ;
hp = mf_get ( mfp , ( blocknr_T ) 0 , 1 ) ;
if ( hp = = NULL )
{
# ifdef FEAT_CRYPT
2019-12-04 21:57:43 +01:00
// Possibly update the seed in the memfile before there is a block0.
2014-08-13 21:58:28 +02:00
if ( what = = UB_CRYPT )
ml_set_mfp_crypt ( buf ) ;
# endif
2004-06-13 20:20:40 +00:00
return ;
2014-08-13 21:58:28 +02:00
}
2004-06-13 20:20:40 +00:00
b0p = ( ZERO_BL * ) ( hp - > bh_data ) ;
2010-06-21 06:15:46 +02:00
if ( ml_check_b0_id ( b0p ) = = FAIL )
2023-05-31 17:12:14 +01:00
iemsg ( e_ml_upd_block0_didnt_get_block_zero ) ;
2004-06-13 20:20:40 +00:00
else
2004-12-19 22:46:22 +00:00
{
2010-06-21 06:15:46 +02:00
if ( what = = UB_FNAME )
2004-12-19 22:46:22 +00:00
set_b0_fname ( b0p , buf ) ;
2010-06-21 06:15:46 +02:00
# ifdef FEAT_CRYPT
else if ( what = = UB_CRYPT )
ml_set_b0_crypt ( buf , b0p ) ;
# endif
2019-12-04 21:57:43 +01:00
else // what == UB_SAME_DIR
2004-12-19 22:46:22 +00:00
set_b0_dir_flag ( b0p , buf ) ;
}
2004-06-13 20:20:40 +00:00
mf_put ( mfp , hp , TRUE , FALSE ) ;
}
/*
* Write file name and timestamp into block 0 of a swap file .
* Also set buf - > b_mtime .
* Don ' t use NameBuff [ ] ! ! !
*/
static void
2016-01-30 18:51:09 +01:00
set_b0_fname ( ZERO_BL * b0p , buf_T * buf )
2004-06-13 20:20:40 +00:00
{
2016-07-01 17:17:39 +02:00
stat_T st ;
2004-06-13 20:20:40 +00:00
if ( buf - > b_ffname = = NULL )
b0p - > b0_fname [ 0 ] = NUL ;
else
{
2016-02-23 14:53:34 +01:00
# if defined(MSWIN) || defined(AMIGA)
2019-12-04 21:57:43 +01:00
// Systems that cannot translate "~user" back into a path: copy the
// file name unmodified. Do use slashes instead of backslashes for
// portability.
2010-06-21 06:15:46 +02:00
vim_strncpy ( b0p - > b0_fname , buf - > b_ffname , B0_FNAME_SIZE_CRYPT - 1 ) ;
2004-12-19 22:46:22 +00:00
# ifdef BACKSLASH_IN_FILENAME
forward_slash ( b0p - > b0_fname ) ;
# endif
2004-06-13 20:20:40 +00:00
# else
size_t flen , ulen ;
char_u uname [ B0_UNAME_SIZE ] ;
/*
* For a file under the home directory of the current user , we try to
* replace the home directory path with " ~user " . This helps when
* editing the same file on different machines over a network .
* First replace home dir path with " ~/ " with home_replace ( ) .
* Then insert the user name to get " ~user/ " .
*/
2010-06-21 06:15:46 +02:00
home_replace ( NULL , buf - > b_ffname , b0p - > b0_fname ,
B0_FNAME_SIZE_CRYPT , TRUE ) ;
2004-06-13 20:20:40 +00:00
if ( b0p - > b0_fname [ 0 ] = = ' ~ ' )
{
flen = STRLEN ( b0p - > b0_fname ) ;
2019-12-04 21:57:43 +01:00
// If there is no user name or it is too long, don't use "~/"
2004-06-13 20:20:40 +00:00
if ( get_user_name ( uname , B0_UNAME_SIZE ) = = FAIL
2010-06-21 06:15:46 +02:00
| | ( ulen = STRLEN ( uname ) ) + flen > B0_FNAME_SIZE_CRYPT - 1 )
vim_strncpy ( b0p - > b0_fname , buf - > b_ffname ,
B0_FNAME_SIZE_CRYPT - 1 ) ;
2004-06-13 20:20:40 +00:00
else
{
mch_memmove ( b0p - > b0_fname + ulen + 1 , b0p - > b0_fname + 1 , flen ) ;
mch_memmove ( b0p - > b0_fname + 1 , uname , ulen ) ;
}
}
# endif
if ( mch_stat ( ( char * ) buf - > b_ffname , & st ) > = 0 )
{
long_to_char ( ( long ) st . st_mtime , b0p - > b0_mtime ) ;
# ifdef CHECK_INODE
long_to_char ( ( long ) st . st_ino , b0p - > b0_ino ) ;
# endif
buf_store_time ( buf , & st , buf - > b_ffname ) ;
buf - > b_mtime_read = buf - > b_mtime ;
2021-10-14 21:27:55 +01:00
buf - > b_mtime_read_ns = buf - > b_mtime_ns ;
2004-06-13 20:20:40 +00:00
}
else
{
long_to_char ( 0L , b0p - > b0_mtime ) ;
# ifdef CHECK_INODE
long_to_char ( 0L , b0p - > b0_ino ) ;
# endif
buf - > b_mtime = 0 ;
2021-10-14 21:27:55 +01:00
buf - > b_mtime_ns = 0 ;
2004-06-13 20:20:40 +00:00
buf - > b_mtime_read = 0 ;
2021-10-14 21:27:55 +01:00
buf - > b_mtime_read_ns = 0 ;
2004-06-13 20:20:40 +00:00
buf - > b_orig_size = 0 ;
buf - > b_orig_mode = 0 ;
}
}
2004-12-19 22:46:22 +00:00
2019-12-04 21:57:43 +01:00
// Also add the 'fileencoding' if there is room.
2004-12-19 22:46:22 +00:00
add_b0_fenc ( b0p , curbuf ) ;
}
/*
* Update the B0_SAME_DIR flag of the swap file . It ' s set if the file and the
* swapfile for " buf " are in the same directory .
* This is fail safe : if we are not sure the directories are equal the flag is
* not set .
*/
static void
2016-01-30 18:51:09 +01:00
set_b0_dir_flag ( ZERO_BL * b0p , buf_T * buf )
2004-12-19 22:46:22 +00:00
{
if ( same_directory ( buf - > b_ml . ml_mfp - > mf_fname , buf - > b_ffname ) )
b0p - > b0_flags | = B0_SAME_DIR ;
else
b0p - > b0_flags & = ~ B0_SAME_DIR ;
}
/*
* When there is room , add the ' fileencoding ' to block zero .
*/
static void
2016-01-30 18:51:09 +01:00
add_b0_fenc (
ZERO_BL * b0p ,
buf_T * buf )
2004-12-19 22:46:22 +00:00
{
int n ;
2010-06-21 06:15:46 +02:00
int size = B0_FNAME_SIZE_NOCRYPT ;
2019-01-24 15:54:21 +01:00
# ifdef FEAT_CRYPT
2019-12-04 21:57:43 +01:00
// Without encryption use the same offset as in Vim 7.2 to be compatible.
// With encryption it's OK to move elsewhere, the swap file is not
// compatible anyway.
2010-06-21 06:15:46 +02:00
if ( * buf - > b_p_key ! = NUL )
size = B0_FNAME_SIZE_CRYPT ;
2019-01-24 15:54:21 +01:00
# endif
2004-12-19 22:46:22 +00:00
2006-04-17 22:14:47 +00:00
n = ( int ) STRLEN ( buf - > b_p_fenc ) ;
2010-06-21 06:15:46 +02:00
if ( ( int ) STRLEN ( b0p - > b0_fname ) + n + 1 > size )
2004-12-19 22:46:22 +00:00
b0p - > b0_flags & = ~ B0_HAS_FENC ;
else
{
2010-06-21 06:15:46 +02:00
mch_memmove ( ( char * ) b0p - > b0_fname + size - n ,
2004-12-19 22:46:22 +00:00
( char * ) buf - > b_p_fenc , ( size_t ) n ) ;
2010-06-21 06:15:46 +02:00
* ( b0p - > b0_fname + size - n - 1 ) = NUL ;
2004-12-19 22:46:22 +00:00
b0p - > b0_flags | = B0_HAS_FENC ;
}
2004-06-13 20:20:40 +00:00
}
2004-12-19 22:46:22 +00:00
2021-03-10 21:26:37 +01:00
# if defined(HAVE_SYS_SYSINFO_H) && defined(HAVE_SYSINFO_UPTIME)
# include <sys / sysinfo.h>
# endif
2021-07-06 20:15:46 +02:00
# if defined(UNIX) || defined(MSWIN)
2021-03-10 21:26:37 +01:00
/*
* Return TRUE if the process with number " b0p->b0_pid " is still running .
* " swap_fname " is the name of the swap file , if it ' s from before a reboot then
* the result is FALSE ;
*/
static int
swapfile_process_running ( ZERO_BL * b0p , char_u * swap_fname UNUSED )
{
2021-10-06 11:27:21 +01:00
# if defined(HAVE_SYSINFO) && defined(HAVE_SYSINFO_UPTIME)
2021-03-10 21:26:37 +01:00
stat_T st ;
struct sysinfo sinfo ;
// If the system rebooted after when the swap file was written then the
// process can't be running now.
if ( mch_stat ( ( char * ) swap_fname , & st ) ! = - 1
& & sysinfo ( & sinfo ) = = 0
2021-03-10 21:55:46 +01:00
& & st . st_mtime < time ( NULL ) - (
2021-07-06 20:15:46 +02:00
# ifdef FEAT_EVAL
2021-03-10 21:55:46 +01:00
override_sysinfo_uptime > = 0 ? override_sysinfo_uptime :
2021-07-06 20:15:46 +02:00
# endif
2021-03-10 21:55:46 +01:00
sinfo . uptime ) )
2021-03-10 21:26:37 +01:00
return FALSE ;
2021-07-06 20:15:46 +02:00
# endif
2021-03-10 21:26:37 +01:00
return mch_process_running ( char_to_long ( b0p - > b0_pid ) ) ;
}
2021-07-06 20:15:46 +02:00
# endif
2004-06-13 20:20:40 +00:00
/*
2010-06-21 06:15:46 +02:00
* Try to recover curbuf from the . swp file .
2019-05-23 21:35:48 +02:00
* If " checkext " is TRUE , check the extension and detect whether it is
* a swap file .
2004-06-13 20:20:40 +00:00
*/
void
2019-05-23 21:35:48 +02:00
ml_recover ( int checkext )
2004-06-13 20:20:40 +00:00
{
buf_T * buf = NULL ;
memfile_T * mfp = NULL ;
char_u * fname ;
2010-06-21 06:15:46 +02:00
char_u * fname_used = NULL ;
2004-06-13 20:20:40 +00:00
bhdr_T * hp = NULL ;
ZERO_BL * b0p ;
2004-12-19 22:46:22 +00:00
int b0_ff ;
char_u * b0_fenc = NULL ;
2010-06-21 06:15:46 +02:00
# ifdef FEAT_CRYPT
int b0_cm = - 1 ;
# endif
2004-06-13 20:20:40 +00:00
PTR_BL * pp ;
DATA_BL * dp ;
infoptr_T * ip ;
blocknr_T bnum ;
int page_count ;
2016-07-01 17:17:39 +02:00
stat_T org_stat , swp_stat ;
2004-06-13 20:20:40 +00:00
int len ;
int directly ;
linenr_T lnum ;
char_u * p ;
int i ;
long error ;
int cannot_open ;
linenr_T line_count ;
int has_error ;
int idx ;
int top ;
int txt_start ;
2016-07-01 17:17:39 +02:00
off_T size ;
2004-06-13 20:20:40 +00:00
int called_from_main ;
int serious_error = TRUE ;
long mtime ;
int attr ;
2010-05-15 17:06:53 +02:00
int orig_file_status = NOTDONE ;
2004-06-13 20:20:40 +00:00
recoverymode = TRUE ;
called_from_main = ( curbuf - > b_ml . ml_mfp = = NULL ) ;
2017-03-16 17:23:31 +01:00
attr = HL_ATTR ( HLF_E ) ;
2009-11-03 12:06:23 +00:00
/*
2017-12-02 15:11:22 +01:00
* If the file name ends in " .s[a-w][a-z] " we assume this is the swap file .
2009-11-03 12:06:23 +00:00
* Otherwise a search is done to find the swap file ( s ) .
*/
2004-06-13 20:20:40 +00:00
fname = curbuf - > b_fname ;
2019-12-04 21:57:43 +01:00
if ( fname = = NULL ) // When there is no file name
2004-06-13 20:20:40 +00:00
fname = ( char_u * ) " " ;
len = ( int ) STRLEN ( fname ) ;
2019-05-23 21:35:48 +02:00
if ( checkext & & len > = 4 & &
2011-05-10 16:41:25 +02:00
# if defined(VMS)
2017-02-17 16:31:35 +01:00
STRNICMP ( fname + len - 4 , " _s " , 2 )
2004-06-13 20:20:40 +00:00
# else
2017-02-17 16:31:35 +01:00
STRNICMP ( fname + len - 4 , " .s " , 2 )
2004-06-13 20:20:40 +00:00
# endif
2017-02-17 16:31:35 +01:00
= = 0
2017-12-02 15:11:22 +01:00
& & vim_strchr ( ( char_u * ) " abcdefghijklmnopqrstuvw " ,
TOLOWER_ASC ( fname [ len - 2 ] ) ) ! = NULL
2009-11-03 12:06:23 +00:00
& & ASCII_ISALPHA ( fname [ len - 1 ] ) )
2004-06-13 20:20:40 +00:00
{
directly = TRUE ;
2019-12-04 21:57:43 +01:00
fname_used = vim_strsave ( fname ) ; // make a copy for mf_open()
2004-06-13 20:20:40 +00:00
}
else
{
directly = FALSE ;
2019-12-04 21:57:43 +01:00
// count the number of matching swap files
2022-12-05 13:50:55 +00:00
len = recover_names ( fname , FALSE , NULL , 0 , NULL ) ;
2019-12-04 21:57:43 +01:00
if ( len = = 0 ) // no swap files found
2004-06-13 20:20:40 +00:00
{
2022-01-02 17:00:40 +00:00
semsg ( _ ( e_no_swap_file_found_for_str ) , fname ) ;
2004-06-13 20:20:40 +00:00
goto theend ;
}
2019-12-04 21:57:43 +01:00
if ( len = = 1 ) // one swap file found, use it
2004-06-13 20:20:40 +00:00
i = 1 ;
2019-12-04 21:57:43 +01:00
else // several swap files found, choose
2004-06-13 20:20:40 +00:00
{
2019-12-04 21:57:43 +01:00
// list the names of the swap files
2022-12-05 13:50:55 +00:00
( void ) recover_names ( fname , TRUE , NULL , 0 , NULL ) ;
2004-06-13 20:20:40 +00:00
msg_putchar ( ' \n ' ) ;
2019-01-19 17:43:09 +01:00
msg_puts ( _ ( " Enter number of swap file to use (0 to quit): " ) ) ;
2005-06-28 23:32:02 +00:00
i = get_number ( FALSE , NULL ) ;
2004-06-13 20:20:40 +00:00
if ( i < 1 | | i > len )
goto theend ;
}
2019-12-04 21:57:43 +01:00
// get the swap file name that will be used
2022-12-05 13:50:55 +00:00
( void ) recover_names ( fname , FALSE , NULL , i , & fname_used ) ;
2004-06-13 20:20:40 +00:00
}
2010-06-21 06:15:46 +02:00
if ( fname_used = = NULL )
2019-12-04 21:57:43 +01:00
goto theend ; // out of memory
2004-06-13 20:20:40 +00:00
2019-12-04 21:57:43 +01:00
// When called from main() still need to initialize storage structure
2006-01-12 23:22:24 +00:00
if ( called_from_main & & ml_open ( curbuf ) = = FAIL )
2004-06-13 20:20:40 +00:00
getout ( 1 ) ;
2010-06-21 06:15:46 +02:00
/*
* Allocate a buffer structure for the swap file that is used for recovery .
2010-07-25 14:00:46 +02:00
* Only the memline and crypt information in it are really used .
2010-06-21 06:15:46 +02:00
*/
2019-05-28 23:08:19 +02:00
buf = ALLOC_ONE ( buf_T ) ;
2004-06-13 20:20:40 +00:00
if ( buf = = NULL )
goto theend ;
2010-06-21 06:15:46 +02:00
/*
* init fields in memline struct
*/
2019-12-04 21:57:43 +01:00
buf - > b_ml . ml_stack_size = 0 ; // no stack yet
buf - > b_ml . ml_stack = NULL ; // no stack yet
buf - > b_ml . ml_stack_top = 0 ; // nothing in the stack
buf - > b_ml . ml_line_lnum = 0 ; // no cached line
buf - > b_ml . ml_locked = NULL ; // no locked block
2004-06-13 20:20:40 +00:00
buf - > b_ml . ml_flags = 0 ;
2010-07-25 15:11:11 +02:00
# ifdef FEAT_CRYPT
buf - > b_p_key = empty_option ;
buf - > b_p_cm = empty_option ;
# endif
2004-06-13 20:20:40 +00:00
2010-06-21 06:15:46 +02:00
/*
* open the memfile from the old swap file
*/
2019-12-04 21:57:43 +01:00
p = vim_strsave ( fname_used ) ; // save "fname_used" for the message:
// mf_open() will consume "fname_used"!
2010-06-21 06:15:46 +02:00
mfp = mf_open ( fname_used , O_RDONLY ) ;
fname_used = p ;
2004-06-13 20:20:40 +00:00
if ( mfp = = NULL | | mfp - > mf_fd < 0 )
{
2010-06-21 06:15:46 +02:00
if ( fname_used ! = NULL )
2022-01-02 17:00:40 +00:00
semsg ( _ ( e_cannot_open_str ) , fname_used ) ;
2004-06-13 20:20:40 +00:00
goto theend ;
}
buf - > b_ml . ml_mfp = mfp ;
2010-06-21 06:15:46 +02:00
# ifdef FEAT_CRYPT
mfp - > mf_buffer = buf ;
# endif
2004-06-13 20:20:40 +00:00
/*
* The page size set in mf_open ( ) might be different from the page size
* used in the swap file , we must get it from block 0. But to read block
* 0 we need a page size . Use the minimal size for block 0 here , it will
* be set to the real value below .
*/
mfp - > mf_page_size = MIN_SWAP_PAGE_SIZE ;
2010-06-21 06:15:46 +02:00
/*
* try to read block 0
*/
2004-06-13 20:20:40 +00:00
if ( ( hp = mf_get ( mfp , ( blocknr_T ) 0 , 1 ) ) = = NULL )
{
msg_start ( ) ;
2019-01-19 17:43:09 +01:00
msg_puts_attr ( _ ( " Unable to read block 0 from " ) , attr | MSG_HIST ) ;
2004-06-13 20:20:40 +00:00
msg_outtrans_attr ( mfp - > mf_fname , attr | MSG_HIST ) ;
2019-01-19 17:43:09 +01:00
msg_puts_attr ( _ ( " \n Maybe no changes were made or Vim did not update the swap file. " ) ,
2004-06-13 20:20:40 +00:00
attr | MSG_HIST ) ;
msg_end ( ) ;
goto theend ;
}
b0p = ( ZERO_BL * ) ( hp - > bh_data ) ;
if ( STRNCMP ( b0p - > b0_version , " VIM 3.0 " , 7 ) = = 0 )
{
msg_start ( ) ;
msg_outtrans_attr ( mfp - > mf_fname , MSG_HIST ) ;
2019-01-19 17:43:09 +01:00
msg_puts_attr ( _ ( " cannot be used with this version of Vim. \n " ) ,
2004-06-13 20:20:40 +00:00
MSG_HIST ) ;
2019-01-19 17:43:09 +01:00
msg_puts_attr ( _ ( " Use Vim version 3.0. \n " ) , MSG_HIST ) ;
2004-06-13 20:20:40 +00:00
msg_end ( ) ;
goto theend ;
}
2010-06-21 06:15:46 +02:00
if ( ml_check_b0_id ( b0p ) = = FAIL )
2004-06-13 20:20:40 +00:00
{
2022-01-02 17:00:40 +00:00
semsg ( _ ( e_str_does_not_look_like_vim_swap_file ) , mfp - > mf_fname ) ;
2004-06-13 20:20:40 +00:00
goto theend ;
}
if ( b0_magic_wrong ( b0p ) )
{
msg_start ( ) ;
msg_outtrans_attr ( mfp - > mf_fname , attr | MSG_HIST ) ;
2016-02-23 14:53:34 +01:00
# if defined(MSWIN)
2004-06-13 20:20:40 +00:00
if ( STRNCMP ( b0p - > b0_hname , " PC " , 3 ) = = 0 )
2019-01-19 17:43:09 +01:00
msg_puts_attr ( _ ( " cannot be used with this version of Vim. \n " ) ,
2004-06-13 20:20:40 +00:00
attr | MSG_HIST ) ;
else
# endif
2019-01-19 17:43:09 +01:00
msg_puts_attr ( _ ( " cannot be used on this computer. \n " ) ,
2004-06-13 20:20:40 +00:00
attr | MSG_HIST ) ;
2019-01-19 17:43:09 +01:00
msg_puts_attr ( _ ( " The file was created on " ) , attr | MSG_HIST ) ;
2019-12-04 21:57:43 +01:00
// avoid going past the end of a corrupted hostname
2004-06-13 20:20:40 +00:00
b0p - > b0_fname [ 0 ] = NUL ;
2019-01-19 17:43:09 +01:00
msg_puts_attr ( ( char * ) b0p - > b0_hname , attr | MSG_HIST ) ;
msg_puts_attr ( _ ( " , \n or the file has been damaged. " ) , attr | MSG_HIST ) ;
2004-06-13 20:20:40 +00:00
msg_end ( ) ;
goto theend ;
}
2007-04-26 15:21:56 +00:00
2010-06-21 06:15:46 +02:00
# ifdef FEAT_CRYPT
2021-06-02 13:28:16 +02:00
for ( i = 0 ; i < ( int ) ARRAY_LENGTH ( id1_codes ) ; + + i )
2014-08-10 13:38:34 +02:00
if ( id1_codes [ i ] = = b0p - > b0_id [ 1 ] )
b0_cm = i ;
if ( b0_cm > 0 )
2010-06-21 06:15:46 +02:00
mch_memmove ( mfp - > mf_seed , & b0p - > b0_seed , MF_SEED_LEN ) ;
2014-08-10 13:38:34 +02:00
crypt_set_cm_option ( buf , b0_cm < 0 ? 0 : b0_cm ) ;
2010-06-21 06:15:46 +02:00
# else
if ( b0p - > b0_id [ 1 ] ! = BLOCK0_ID1 )
{
2022-01-05 17:49:15 +00:00
semsg ( _ ( e_str_is_encrypted_and_this_version_of_vim_does_not_support_encryption ) , mfp - > mf_fname ) ;
2010-06-21 06:15:46 +02:00
goto theend ;
}
# endif
2004-06-13 20:20:40 +00:00
/*
* If we guessed the wrong page size , we have to recalculate the
* highest block number in the file .
*/
if ( mfp - > mf_page_size ! = ( unsigned ) char_to_long ( b0p - > b0_page_size ) )
{
2007-04-26 15:21:56 +00:00
unsigned previous_page_size = mfp - > mf_page_size ;
2004-06-13 20:20:40 +00:00
mf_new_page_size ( mfp , ( unsigned ) char_to_long ( b0p - > b0_page_size ) ) ;
2007-04-26 15:21:56 +00:00
if ( mfp - > mf_page_size < previous_page_size )
{
msg_start ( ) ;
msg_outtrans_attr ( mfp - > mf_fname , attr | MSG_HIST ) ;
2019-01-19 17:43:09 +01:00
msg_puts_attr ( _ ( " has been damaged (page size is smaller than minimum value). \n " ) ,
2007-04-26 15:21:56 +00:00
attr | MSG_HIST ) ;
msg_end ( ) ;
goto theend ;
}
2016-07-01 17:17:39 +02:00
if ( ( size = vim_lseek ( mfp - > mf_fd , ( off_T ) 0L , SEEK_END ) ) < = 0 )
2019-12-04 21:57:43 +01:00
mfp - > mf_blocknr_max = 0 ; // no file or empty file
2004-06-13 20:20:40 +00:00
else
mfp - > mf_blocknr_max = ( blocknr_T ) ( size / mfp - > mf_page_size ) ;
mfp - > mf_infile_count = mfp - > mf_blocknr_max ;
2007-04-26 15:21:56 +00:00
2019-12-04 21:57:43 +01:00
// need to reallocate the memory used to store the data
2007-04-26 15:21:56 +00:00
p = alloc ( mfp - > mf_page_size ) ;
if ( p = = NULL )
goto theend ;
mch_memmove ( p , hp - > bh_data , previous_page_size ) ;
vim_free ( hp - > bh_data ) ;
hp - > bh_data = p ;
b0p = ( ZERO_BL * ) ( hp - > bh_data ) ;
2004-06-13 20:20:40 +00:00
}
2010-06-21 06:15:46 +02:00
/*
* If . swp file name given directly , use name from swap file for buffer .
*/
2004-06-13 20:20:40 +00:00
if ( directly )
{
expand_env ( b0p - > b0_fname , NameBuff , MAXPATHL ) ;
if ( setfname ( curbuf , NameBuff , NULL , TRUE ) = = FAIL )
goto theend ;
}
home_replace ( NULL , mfp - > mf_fname , NameBuff , MAXPATHL , TRUE ) ;
2019-01-13 23:38:42 +01:00
smsg ( _ ( " Using swap file \" %s \" " ) , NameBuff ) ;
2004-06-13 20:20:40 +00:00
if ( buf_spname ( curbuf ) ! = NULL )
2012-10-03 18:25:00 +02:00
vim_strncpy ( NameBuff , buf_spname ( curbuf ) , MAXPATHL - 1 ) ;
2004-06-13 20:20:40 +00:00
else
home_replace ( NULL , curbuf - > b_ffname , NameBuff , MAXPATHL , TRUE ) ;
2019-01-13 23:38:42 +01:00
smsg ( _ ( " Original file \" %s \" " ) , NameBuff ) ;
2004-06-13 20:20:40 +00:00
msg_putchar ( ' \n ' ) ;
2010-06-21 06:15:46 +02:00
/*
* check date of swap file and original file
*/
2004-06-13 20:20:40 +00:00
mtime = char_to_long ( b0p - > b0_mtime ) ;
if ( curbuf - > b_ffname ! = NULL
& & mch_stat ( ( char * ) curbuf - > b_ffname , & org_stat ) ! = - 1
& & ( ( mch_stat ( ( char * ) mfp - > mf_fname , & swp_stat ) ! = - 1
& & org_stat . st_mtime > swp_stat . st_mtime )
| | org_stat . st_mtime ! = mtime ) )
2022-01-02 17:00:40 +00:00
emsg ( _ ( e_warning_original_file_may_have_been_changed ) ) ;
2004-06-13 20:20:40 +00:00
out_flush ( ) ;
2004-12-19 22:46:22 +00:00
2019-12-04 21:57:43 +01:00
// Get the 'fileformat' and 'fileencoding' from block zero.
2004-12-19 22:46:22 +00:00
b0_ff = ( b0p - > b0_flags & B0_FF_MASK ) ;
if ( b0p - > b0_flags & B0_HAS_FENC )
{
2010-06-22 06:28:58 +02:00
int fnsize = B0_FNAME_SIZE_NOCRYPT ;
2010-06-21 06:15:46 +02:00
# ifdef FEAT_CRYPT
2019-12-04 21:57:43 +01:00
// Use the same size as in add_b0_fenc().
2010-06-21 06:15:46 +02:00
if ( b0p - > b0_id [ 1 ] ! = BLOCK0_ID1 )
2010-06-22 06:28:58 +02:00
fnsize = B0_FNAME_SIZE_CRYPT ;
2010-06-21 06:15:46 +02:00
# endif
2010-06-22 06:28:58 +02:00
for ( p = b0p - > b0_fname + fnsize ; p > b0p - > b0_fname & & p [ - 1 ] ! = NUL ; - - p )
2004-12-19 22:46:22 +00:00
;
2020-06-12 22:59:11 +02:00
b0_fenc = vim_strnsave ( p , b0p - > b0_fname + fnsize - p ) ;
2004-12-19 22:46:22 +00:00
}
2019-12-04 21:57:43 +01:00
mf_put ( mfp , hp , FALSE , FALSE ) ; // release block 0
2004-06-13 20:20:40 +00:00
hp = NULL ;
/*
* Now that we are sure that the file is going to be recovered , clear the
* contents of the current buffer .
*/
while ( ! ( curbuf - > b_ml . ml_flags & ML_EMPTY ) )
2020-05-30 20:30:46 +02:00
ml_delete ( ( linenr_T ) 1 ) ;
2004-06-13 20:20:40 +00:00
/*
* Try reading the original file to obtain the values of ' fileformat ' ,
* ' fileencoding ' , etc . Ignore errors . The text itself is not used .
2010-06-21 06:15:46 +02:00
* When the file is encrypted the user is asked to enter the key .
2004-06-13 20:20:40 +00:00
*/
if ( curbuf - > b_ffname ! = NULL )
2010-05-15 17:06:53 +02:00
orig_file_status = readfile ( curbuf - > b_ffname , NULL , ( linenr_T ) 0 ,
2004-06-13 20:20:40 +00:00
( linenr_T ) 0 , ( linenr_T ) MAXLNUM , NULL , READ_NEW ) ;
2010-06-21 06:15:46 +02:00
# ifdef FEAT_CRYPT
if ( b0_cm > = 0 )
{
2019-12-04 21:57:43 +01:00
// Need to ask the user for the crypt key. If this fails we continue
// without a key, will probably get garbage text.
2010-06-21 06:15:46 +02:00
if ( * curbuf - > b_p_key ! = NUL )
{
2019-01-13 23:38:42 +01:00
smsg ( _ ( " Swap file is encrypted: \" %s \" " ) , fname_used ) ;
2019-01-19 17:43:09 +01:00
msg_puts ( _ ( " \n If you entered a new crypt key but did not write the text file, " ) ) ;
msg_puts ( _ ( " \n enter the new crypt key. " ) ) ;
msg_puts ( _ ( " \n If you wrote the text file after changing the crypt key press enter " ) ) ;
msg_puts ( _ ( " \n to use the same key for text file and swap file " ) ) ;
2010-06-21 06:15:46 +02:00
}
else
2019-01-13 23:38:42 +01:00
smsg ( _ ( need_key_msg ) , fname_used ) ;
2014-08-10 13:38:34 +02:00
buf - > b_p_key = crypt_get_key ( FALSE , FALSE ) ;
2010-06-21 06:15:46 +02:00
if ( buf - > b_p_key = = NULL )
buf - > b_p_key = curbuf - > b_p_key ;
else if ( * buf - > b_p_key = = NUL )
{
vim_free ( buf - > b_p_key ) ;
buf - > b_p_key = curbuf - > b_p_key ;
}
if ( buf - > b_p_key = = NULL )
buf - > b_p_key = empty_option ;
}
# endif
2019-12-04 21:57:43 +01:00
// Use the 'fileformat' and 'fileencoding' as stored in the swap file.
2004-12-19 22:46:22 +00:00
if ( b0_ff ! = 0 )
set_fileformat ( b0_ff - 1 , OPT_LOCAL ) ;
if ( b0_fenc ! = NULL )
{
2022-04-15 13:53:33 +01:00
set_option_value_give_err ( ( char_u * ) " fenc " , 0L , b0_fenc , OPT_LOCAL ) ;
2004-12-19 22:46:22 +00:00
vim_free ( b0_fenc ) ;
}
2019-06-08 18:07:21 +02:00
unchanged ( curbuf , TRUE , TRUE ) ;
2004-12-19 22:46:22 +00:00
2019-12-04 21:57:43 +01:00
bnum = 1 ; // start with block 1
page_count = 1 ; // which is 1 page
lnum = 0 ; // append after line 0 in curbuf
2004-06-13 20:20:40 +00:00
line_count = 0 ;
2019-12-04 21:57:43 +01:00
idx = 0 ; // start with first index in block 1
2004-06-13 20:20:40 +00:00
error = 0 ;
buf - > b_ml . ml_stack_top = 0 ;
buf - > b_ml . ml_stack = NULL ;
2019-12-04 21:57:43 +01:00
buf - > b_ml . ml_stack_size = 0 ; // no stack yet
2004-06-13 20:20:40 +00:00
if ( curbuf - > b_ffname = = NULL )
cannot_open = TRUE ;
else
cannot_open = FALSE ;
serious_error = FALSE ;
for ( ; ! got_int ; line_breakcheck ( ) )
{
if ( hp ! = NULL )
2019-12-04 21:57:43 +01:00
mf_put ( mfp , hp , FALSE , FALSE ) ; // release previous block
2004-06-13 20:20:40 +00:00
/*
* get block
*/
2022-01-28 15:28:04 +00:00
if ( ( hp = mf_get ( mfp , bnum , page_count ) ) = = NULL )
2004-06-13 20:20:40 +00:00
{
if ( bnum = = 1 )
{
2022-01-02 17:00:40 +00:00
semsg ( _ ( e_unable_to_read_block_one_from_str ) , mfp - > mf_fname ) ;
2004-06-13 20:20:40 +00:00
goto theend ;
}
+ + error ;
ml_append ( lnum + + , ( char_u * ) _ ( " ???MANY LINES MISSING " ) ,
( colnr_T ) 0 , TRUE ) ;
}
2019-12-04 21:57:43 +01:00
else // there is a block
2004-06-13 20:20:40 +00:00
{
pp = ( PTR_BL * ) ( hp - > bh_data ) ;
2019-12-04 21:57:43 +01:00
if ( pp - > pb_id = = PTR_ID ) // it is a pointer block
2004-06-13 20:20:40 +00:00
{
2023-04-22 21:14:26 +01:00
int ptr_block_error = FALSE ;
if ( pp - > pb_count_max ! = PB_COUNT_MAX ( mfp ) )
{
ptr_block_error = TRUE ;
pp - > pb_count_max = PB_COUNT_MAX ( mfp ) ;
}
if ( pp - > pb_count > pp - > pb_count_max )
{
ptr_block_error = TRUE ;
pp - > pb_count = pp - > pb_count_max ;
}
if ( ptr_block_error )
emsg ( _ ( e_warning_pointer_block_corrupted ) ) ;
2019-12-04 21:57:43 +01:00
// check line count when using pointer block first time
2004-06-13 20:20:40 +00:00
if ( idx = = 0 & & line_count ! = 0 )
{
for ( i = 0 ; i < ( int ) pp - > pb_count ; + + i )
line_count - = pp - > pb_pointer [ i ] . pe_line_count ;
if ( line_count ! = 0 )
{
+ + error ;
ml_append ( lnum + + , ( char_u * ) _ ( " ???LINE COUNT WRONG " ) ,
( colnr_T ) 0 , TRUE ) ;
}
}
if ( pp - > pb_count = = 0 )
{
ml_append ( lnum + + , ( char_u * ) _ ( " ???EMPTY BLOCK " ) ,
( colnr_T ) 0 , TRUE ) ;
+ + error ;
}
2019-12-04 21:57:43 +01:00
else if ( idx < ( int ) pp - > pb_count ) // go a block deeper
2004-06-13 20:20:40 +00:00
{
if ( pp - > pb_pointer [ idx ] . pe_bnum < 0 )
{
/*
* Data block with negative block number .
* Try to read lines from the original file .
* This is slow , but it works .
*/
if ( ! cannot_open )
{
line_count = pp - > pb_pointer [ idx ] . pe_line_count ;
if ( readfile ( curbuf - > b_ffname , NULL , lnum ,
pp - > pb_pointer [ idx ] . pe_old_lnum - 1 ,
2017-01-13 22:01:02 +01:00
line_count , NULL , 0 ) ! = OK )
2004-06-13 20:20:40 +00:00
cannot_open = TRUE ;
else
lnum + = line_count ;
}
if ( cannot_open )
{
+ + error ;
ml_append ( lnum + + , ( char_u * ) _ ( " ???LINES MISSING " ) ,
( colnr_T ) 0 , TRUE ) ;
}
2019-12-04 21:57:43 +01:00
+ + idx ; // get same block again for next index
2004-06-13 20:20:40 +00:00
continue ;
}
/*
* going one block deeper in the tree
*/
2019-12-04 21:57:43 +01:00
if ( ( top = ml_add_stack ( buf ) ) < 0 ) // new entry in stack
2004-06-13 20:20:40 +00:00
{
+ + error ;
2019-12-04 21:57:43 +01:00
break ; // out of memory
2004-06-13 20:20:40 +00:00
}
ip = & ( buf - > b_ml . ml_stack [ top ] ) ;
ip - > ip_bnum = bnum ;
ip - > ip_index = idx ;
bnum = pp - > pb_pointer [ idx ] . pe_bnum ;
line_count = pp - > pb_pointer [ idx ] . pe_line_count ;
page_count = pp - > pb_pointer [ idx ] . pe_page_count ;
2011-06-13 01:07:27 +02:00
idx = 0 ;
2004-06-13 20:20:40 +00:00
continue ;
}
}
2019-12-04 21:57:43 +01:00
else // not a pointer block
2004-06-13 20:20:40 +00:00
{
dp = ( DATA_BL * ) ( hp - > bh_data ) ;
2019-12-04 21:57:43 +01:00
if ( dp - > db_id ! = DATA_ID ) // block id wrong
2004-06-13 20:20:40 +00:00
{
if ( bnum = = 1 )
{
2022-01-02 17:00:40 +00:00
semsg ( _ ( e_block_one_id_wrong_str_not_swp_file ) ,
2004-06-13 20:20:40 +00:00
mfp - > mf_fname ) ;
goto theend ;
}
+ + error ;
ml_append ( lnum + + , ( char_u * ) _ ( " ???BLOCK MISSING " ) ,
( colnr_T ) 0 , TRUE ) ;
}
else
{
/*
2023-04-27 21:13:12 +01:00
* It is a data block .
* Append all the lines in this block .
2004-06-13 20:20:40 +00:00
*/
has_error = FALSE ;
2023-04-27 21:13:12 +01:00
// Check the length of the block.
// If wrong, use the length given in the pointer block.
2004-06-13 20:20:40 +00:00
if ( page_count * mfp - > mf_page_size ! = dp - > db_txt_end )
{
ml_append ( lnum + + , ( char_u * ) _ ( " ??? from here until ???END lines may be messed up " ) ,
( colnr_T ) 0 , TRUE ) ;
+ + error ;
has_error = TRUE ;
dp - > db_txt_end = page_count * mfp - > mf_page_size ;
}
2023-04-27 21:13:12 +01:00
// Make sure there is a NUL at the end of the block so we
// don't go over the end when copying text.
2004-06-13 20:20:40 +00:00
* ( ( char_u * ) dp + dp - > db_txt_end - 1 ) = NUL ;
2023-04-27 21:13:12 +01:00
// Check the number of lines in the block.
// If wrong, use the count in the data block.
2004-06-13 20:20:40 +00:00
if ( line_count ! = dp - > db_line_count )
{
ml_append ( lnum + + , ( char_u * ) _ ( " ??? from here until ???END lines may have been inserted/deleted " ) ,
( colnr_T ) 0 , TRUE ) ;
+ + error ;
has_error = TRUE ;
}
2023-04-27 21:13:12 +01:00
int did_questions = FALSE ;
2004-06-13 20:20:40 +00:00
for ( i = 0 ; i < dp - > db_line_count ; + + i )
{
2023-04-27 21:13:12 +01:00
if ( ( char_u * ) & ( dp - > db_index [ i ] )
> = ( char_u * ) dp + dp - > db_txt_start )
{
// line count must be wrong
+ + error ;
ml_append ( lnum + + ,
( char_u * ) _ ( " ??? lines may be missing " ) ,
( colnr_T ) 0 , TRUE ) ;
break ;
}
2004-06-13 20:20:40 +00:00
txt_start = ( dp - > db_index [ i ] & DB_INDEX_MASK ) ;
2009-11-03 14:33:17 +00:00
if ( txt_start < = ( int ) HEADER_SIZE
2004-06-13 20:20:40 +00:00
| | txt_start > = ( int ) dp - > db_txt_end )
{
+ + error ;
2023-04-27 21:13:12 +01:00
// avoid lots of lines with "???"
if ( did_questions )
continue ;
did_questions = TRUE ;
p = ( char_u * ) " ??? " ;
2004-06-13 20:20:40 +00:00
}
else
2023-04-27 21:13:12 +01:00
{
did_questions = FALSE ;
2004-06-13 20:20:40 +00:00
p = ( char_u * ) dp + txt_start ;
2023-04-27 21:13:12 +01:00
}
2004-06-13 20:20:40 +00:00
ml_append ( lnum + + , p , ( colnr_T ) 0 , TRUE ) ;
}
if ( has_error )
2009-11-03 14:33:17 +00:00
ml_append ( lnum + + , ( char_u * ) _ ( " ???END " ) ,
( colnr_T ) 0 , TRUE ) ;
2004-06-13 20:20:40 +00:00
}
}
}
2019-12-04 21:57:43 +01:00
if ( buf - > b_ml . ml_stack_top = = 0 ) // finished
2004-06-13 20:20:40 +00:00
break ;
/*
* go one block up in the tree
*/
ip = & ( buf - > b_ml . ml_stack [ - - ( buf - > b_ml . ml_stack_top ) ] ) ;
bnum = ip - > ip_bnum ;
2019-12-04 21:57:43 +01:00
idx = ip - > ip_index + 1 ; // go to next index
2004-06-13 20:20:40 +00:00
page_count = 1 ;
}
/*
2010-05-15 17:06:53 +02:00
* Compare the buffer contents with the original file . When they differ
* set the ' modified ' flag .
* Lines 1 - lnum are the new contents .
* Lines lnum + 1 to ml_line_count are the original contents .
* Line ml_line_count + 1 in the dummy empty line .
2004-06-13 20:20:40 +00:00
*/
2010-05-15 17:06:53 +02:00
if ( orig_file_status ! = OK | | curbuf - > b_ml . ml_line_count ! = lnum * 2 + 1 )
{
2019-12-04 21:57:43 +01:00
// Recovering an empty file results in two lines and the first line is
// empty. Don't set the modified flag then.
2010-05-15 17:06:53 +02:00
if ( ! ( curbuf - > b_ml . ml_line_count = = 2 & & * ml_get ( 1 ) = = NUL ) )
{
2019-05-11 18:36:34 +02:00
changed_internal ( ) ;
2017-02-25 14:59:34 +01:00
+ + CHANGEDTICK ( curbuf ) ;
2010-05-15 17:06:53 +02:00
}
}
else
{
for ( idx = 1 ; idx < = lnum ; + + idx )
{
2019-12-04 21:57:43 +01:00
// Need to copy one line, fetching the other one may flush it.
2024-02-26 21:21:17 +01:00
p = vim_strnsave ( ml_get ( idx ) , ml_get_len ( idx ) ) ;
2010-05-15 17:06:53 +02:00
i = STRCMP ( p , ml_get ( idx + lnum ) ) ;
vim_free ( p ) ;
if ( i ! = 0 )
{
2019-05-11 18:36:34 +02:00
changed_internal ( ) ;
2017-02-25 14:59:34 +01:00
+ + CHANGEDTICK ( curbuf ) ;
2010-05-15 17:06:53 +02:00
break ;
}
}
}
/*
* Delete the lines from the original file and the dummy line from the
* empty buffer . These will now be after the last line in the buffer .
*/
while ( curbuf - > b_ml . ml_line_count > lnum
& & ! ( curbuf - > b_ml . ml_flags & ML_EMPTY ) )
2020-05-30 20:30:46 +02:00
ml_delete ( curbuf - > b_ml . ml_line_count ) ;
2004-06-13 20:20:40 +00:00
curbuf - > b_flags | = BF_RECOVERED ;
2021-06-09 12:33:40 +02:00
check_cursor ( ) ;
2004-06-13 20:20:40 +00:00
recoverymode = FALSE ;
if ( got_int )
2022-01-02 17:00:40 +00:00
emsg ( _ ( e_recovery_interrupted ) ) ;
2004-06-13 20:20:40 +00:00
else if ( error )
{
+ + no_wait_return ;
2019-01-19 17:43:09 +01:00
msg ( " >>>>>>>>>>>>> " ) ;
2022-01-02 17:00:40 +00:00
emsg ( _ ( e_errors_detected_while_recovering_look_for_lines_starting_with_questions ) ) ;
2004-06-13 20:20:40 +00:00
- - no_wait_return ;
2019-01-19 17:43:09 +01:00
msg ( _ ( " See \" :help E312 \" for more information. " ) ) ;
msg ( " >>>>>>>>>>>>> " ) ;
2004-06-13 20:20:40 +00:00
}
else
{
2010-05-15 17:06:53 +02:00
if ( curbuf - > b_changed )
{
2019-01-19 17:43:09 +01:00
msg ( _ ( " Recovery completed. You should check if everything is OK. " ) ) ;
msg_puts ( _ ( " \n (You might want to write out this file under another name \n " ) ) ;
msg_puts ( _ ( " and run diff with the original file to check for changes) " ) ) ;
2010-05-15 17:06:53 +02:00
}
else
2019-01-19 17:43:09 +01:00
msg ( _ ( " Recovery completed. Buffer contents equals file contents. " ) ) ;
2020-11-09 21:04:17 +01:00
msg_puts ( _ ( " \n You may want to delete the .swp file now. " ) ) ;
# if defined(UNIX) || defined(MSWIN)
2021-03-10 21:26:37 +01:00
if ( swapfile_process_running ( b0p , fname_used ) )
2020-11-09 21:04:17 +01:00
{
// Warn there could be an active Vim on the same file, the user may
// want to kill it.
msg_puts ( _ ( " \n Note: process STILL RUNNING: " ) ) ;
msg_outnum ( char_to_long ( b0p - > b0_pid ) ) ;
}
# endif
msg_puts ( " \n \n " ) ;
2004-06-13 20:20:40 +00:00
cmdline_row = msg_row ;
}
2010-06-21 06:15:46 +02:00
# ifdef FEAT_CRYPT
if ( * buf - > b_p_key ! = NUL & & STRCMP ( curbuf - > b_p_key , buf - > b_p_key ) ! = 0 )
{
2019-01-19 17:43:09 +01:00
msg_puts ( _ ( " Using crypt key from swap file for the text file. \n " ) ) ;
2022-04-15 13:53:33 +01:00
set_option_value_give_err ( ( char_u * ) " key " , 0L , buf - > b_p_key , OPT_LOCAL ) ;
2010-06-21 06:15:46 +02:00
}
# endif
2022-08-14 14:17:45 +01:00
redraw_curbuf_later ( UPD_NOT_VALID ) ;
2004-06-13 20:20:40 +00:00
theend :
2010-06-21 06:15:46 +02:00
vim_free ( fname_used ) ;
2004-06-13 20:20:40 +00:00
recoverymode = FALSE ;
if ( mfp ! = NULL )
{
if ( hp ! = NULL )
mf_put ( mfp , hp , FALSE , FALSE ) ;
2019-12-04 21:57:43 +01:00
mf_close ( mfp , FALSE ) ; // will also vim_free(mfp->mf_fname)
2004-06-13 20:20:40 +00:00
}
2007-01-09 13:34:50 +00:00
if ( buf ! = NULL )
{
2010-06-21 06:15:46 +02:00
# ifdef FEAT_CRYPT
if ( buf - > b_p_key ! = curbuf - > b_p_key )
free_string_option ( buf - > b_p_key ) ;
2010-07-25 14:00:46 +02:00
free_string_option ( buf - > b_p_cm ) ;
2010-06-21 06:15:46 +02:00
# endif
2007-01-09 13:34:50 +00:00
vim_free ( buf - > b_ml . ml_stack ) ;
vim_free ( buf ) ;
}
2004-06-13 20:20:40 +00:00
if ( serious_error & & called_from_main )
ml_close ( curbuf , TRUE ) ;
else
{
apply_autocmds ( EVENT_BUFREADPOST , NULL , curbuf - > b_fname , FALSE , curbuf ) ;
apply_autocmds ( EVENT_BUFWINENTER , NULL , curbuf - > b_fname , FALSE , curbuf ) ;
}
}
/*
* Find the names of swap files in current directory and the directory given
* with the ' directory ' option .
*
* Used to :
* - list the swap files for " vim -r "
* - count the number of swap files when recovering
* - list the swap files when recovering
2022-12-05 13:50:55 +00:00
* - list the swap files for swapfilelist ( )
2004-06-13 20:20:40 +00:00
* - find the name of the n ' th swap file when recovering
*/
int
2016-01-30 18:51:09 +01:00
recover_names (
2019-12-04 21:57:43 +01:00
char_u * fname , // base for swap file name
2022-12-05 13:50:55 +00:00
int do_list , // when TRUE, list the swap file names
list_T * ret_list UNUSED , // when not NULL add file names to it
2019-12-04 21:57:43 +01:00
int nr , // when non-zero, return nr'th swap file name
char_u * * fname_out ) // result when "nr" > 0
2004-06-13 20:20:40 +00:00
{
int num_names ;
char_u * ( names [ 6 ] ) ;
char_u * tail ;
char_u * p ;
int num_files ;
int file_count = 0 ;
char_u * * files ;
char_u * dirp ;
char_u * dir_name ;
2010-05-25 21:37:17 +02:00
char_u * fname_res = NULL ;
2010-05-14 17:52:42 +02:00
# ifdef HAVE_READLINK
char_u fname_buf [ MAXPATHL ] ;
2010-05-25 21:37:17 +02:00
# endif
2010-05-14 17:52:42 +02:00
2010-05-25 21:37:17 +02:00
if ( fname ! = NULL )
{
# ifdef HAVE_READLINK
2019-12-04 21:57:43 +01:00
// Expand symlink in the file name, because the swap file is created
// with the actual file instead of with the symlink.
2010-06-21 06:15:46 +02:00
if ( resolve_symlink ( fname , fname_buf ) = = OK )
fname_res = fname_buf ;
else
2010-05-14 17:52:42 +02:00
# endif
2010-06-21 06:15:46 +02:00
fname_res = fname ;
2010-05-25 21:37:17 +02:00
}
2004-06-13 20:20:40 +00:00
2022-12-05 13:50:55 +00:00
if ( do_list )
2004-06-13 20:20:40 +00:00
{
2019-12-04 21:57:43 +01:00
// use msg() to start the scrolling properly
2019-01-19 17:43:09 +01:00
msg ( _ ( " Swap files found: " ) ) ;
2004-06-13 20:20:40 +00:00
msg_putchar ( ' \n ' ) ;
}
/*
* Do the loop for every directory in ' directory ' .
* First allocate some memory to put the directory name in .
*/
2019-05-24 18:54:09 +02:00
dir_name = alloc ( STRLEN ( p_dir ) + 1 ) ;
2004-06-13 20:20:40 +00:00
dirp = p_dir ;
while ( dir_name ! = NULL & & * dirp )
{
/*
* Isolate a directory name from * dirp and put it in dir_name ( we know
* it is large enough , so use 31000 for length ) .
* Advance dirp to next directory name .
*/
( void ) copy_option_part ( & dirp , dir_name , 31000 , " , " ) ;
2019-12-04 21:57:43 +01:00
if ( dir_name [ 0 ] = = ' . ' & & dir_name [ 1 ] = = NUL ) // check current dir
2004-06-13 20:20:40 +00:00
{
2010-06-21 06:15:46 +02:00
if ( fname = = NULL )
2004-06-13 20:20:40 +00:00
{
# ifdef VMS
names [ 0 ] = vim_strsave ( ( char_u * ) " *_sw% " ) ;
# else
names [ 0 ] = vim_strsave ( ( char_u * ) " *.sw? " ) ;
# endif
2019-02-17 17:44:42 +01:00
# if defined(UNIX) || defined(MSWIN)
2019-12-04 21:57:43 +01:00
// For Unix names starting with a dot are special. MS-Windows
// supports this too, on some file systems.
2004-06-13 20:20:40 +00:00
names [ 1 ] = vim_strsave ( ( char_u * ) " .*.sw? " ) ;
names [ 2 ] = vim_strsave ( ( char_u * ) " .sw? " ) ;
num_names = 3 ;
# else
# ifdef VMS
names [ 1 ] = vim_strsave ( ( char_u * ) " .*_sw% " ) ;
num_names = 2 ;
# else
num_names = 1 ;
# endif
# endif
}
else
2010-05-14 17:52:42 +02:00
num_names = recov_file_names ( names , fname_res , TRUE ) ;
2004-06-13 20:20:40 +00:00
}
2019-12-04 21:57:43 +01:00
else // check directory dir_name
2004-06-13 20:20:40 +00:00
{
2010-06-21 06:15:46 +02:00
if ( fname = = NULL )
2004-06-13 20:20:40 +00:00
{
# ifdef VMS
names [ 0 ] = concat_fnames ( dir_name , ( char_u * ) " *_sw% " , TRUE ) ;
# else
names [ 0 ] = concat_fnames ( dir_name , ( char_u * ) " *.sw? " , TRUE ) ;
# endif
2019-02-17 17:44:42 +01:00
# if defined(UNIX) || defined(MSWIN)
2019-12-04 21:57:43 +01:00
// For Unix names starting with a dot are special. MS-Windows
// supports this too, on some file systems.
2004-06-13 20:20:40 +00:00
names [ 1 ] = concat_fnames ( dir_name , ( char_u * ) " .*.sw? " , TRUE ) ;
names [ 2 ] = concat_fnames ( dir_name , ( char_u * ) " .sw? " , TRUE ) ;
num_names = 3 ;
# else
# ifdef VMS
names [ 1 ] = concat_fnames ( dir_name , ( char_u * ) " .*_sw% " , TRUE ) ;
num_names = 2 ;
# else
num_names = 1 ;
# endif
# endif
}
else
{
2019-02-17 17:44:42 +01:00
# if defined(UNIX) || defined(MSWIN)
2017-02-28 21:26:17 +01:00
int len = ( int ) STRLEN ( dir_name ) ;
2017-02-18 16:59:02 +01:00
p = dir_name + len ;
if ( after_pathsep ( dir_name , p ) & & len > 1 & & p [ - 1 ] = = p [ - 2 ] )
2004-06-13 20:20:40 +00:00
{
2019-12-04 21:57:43 +01:00
// Ends with '//', Use Full path for swap name
2024-07-25 20:58:42 +02:00
tail = make_percent_swname ( dir_name , p , fname_res ) ;
2004-06-13 20:20:40 +00:00
}
else
# endif
{
2010-05-14 17:52:42 +02:00
tail = gettail ( fname_res ) ;
2004-06-13 20:20:40 +00:00
tail = concat_fnames ( dir_name , tail , TRUE ) ;
}
if ( tail = = NULL )
num_names = 0 ;
else
{
num_names = recov_file_names ( names , tail , FALSE ) ;
vim_free ( tail ) ;
}
}
}
2019-05-18 13:05:18 +02:00
// check for out-of-memory
2022-12-05 13:50:55 +00:00
for ( int i = 0 ; i < num_names ; + + i )
2004-06-13 20:20:40 +00:00
{
if ( names [ i ] = = NULL )
{
for ( i = 0 ; i < num_names ; + + i )
vim_free ( names [ i ] ) ;
num_names = 0 ;
}
}
if ( num_names = = 0 )
num_files = 0 ;
else if ( expand_wildcards ( num_names , names , & num_files , & files ,
2019-05-23 21:35:48 +02:00
EW_NOTENV | EW_KEEPALL | EW_FILE | EW_SILENT ) = = FAIL )
2004-06-13 20:20:40 +00:00
num_files = 0 ;
/*
* When no swap file found , wildcard expansion might have failed ( e . g .
* not able to execute the shell ) .
* Try finding a swap file by simply adding " .swp " to the file name .
*/
2010-06-21 06:15:46 +02:00
if ( * dirp = = NUL & & file_count + num_files = = 0 & & fname ! = NULL )
2004-06-13 20:20:40 +00:00
{
2016-07-01 17:17:39 +02:00
stat_T st ;
2004-06-13 20:20:40 +00:00
char_u * swapname ;
2010-05-14 17:52:42 +02:00
swapname = modname ( fname_res ,
2011-05-10 16:41:25 +02:00
# if defined(VMS)
2010-05-14 17:52:42 +02:00
( char_u * ) " _swp " , FALSE
2004-06-13 20:20:40 +00:00
# else
2010-05-14 17:52:42 +02:00
( char_u * ) " .swp " , TRUE
2004-06-13 20:20:40 +00:00
# endif
2010-05-14 17:52:42 +02:00
) ;
2004-06-13 20:20:40 +00:00
if ( swapname ! = NULL )
{
2019-05-28 23:08:19 +02:00
if ( mch_stat ( ( char * ) swapname , & st ) ! = - 1 ) // It exists!
2004-06-13 20:20:40 +00:00
{
2019-05-28 23:08:19 +02:00
files = ALLOC_ONE ( char_u * ) ;
2004-06-13 20:20:40 +00:00
if ( files ! = NULL )
{
files [ 0 ] = swapname ;
swapname = NULL ;
num_files = 1 ;
}
}
vim_free ( swapname ) ;
}
}
/*
2022-12-05 13:50:55 +00:00
* Remove swapfile name of the current buffer , it must be ignored .
* But keep it for swapfilelist ( ) .
2004-06-13 20:20:40 +00:00
*/
if ( curbuf - > b_ml . ml_mfp ! = NULL
2022-12-05 13:50:55 +00:00
& & ( p = curbuf - > b_ml . ml_mfp - > mf_fname ) ! = NULL
& & ret_list = = NULL )
2004-06-13 20:20:40 +00:00
{
2022-12-05 13:50:55 +00:00
for ( int i = 0 ; i < num_files ; + + i )
2019-05-23 21:35:48 +02:00
// Do not expand wildcards, on windows would try to expand
// "%tmp%" in "%tmp%file".
if ( fullpathcmp ( p , files [ i ] , TRUE , FALSE ) & FPC_SAME )
2004-06-13 20:20:40 +00:00
{
2019-05-23 21:35:48 +02:00
// Remove the name from files[i]. Move further entries
// down. When the array becomes empty free it here, since
// FreeWild() won't be called below.
2004-06-13 20:20:40 +00:00
vim_free ( files [ i ] ) ;
2009-04-22 13:39:36 +00:00
if ( - - num_files = = 0 )
vim_free ( files ) ;
else
for ( ; i < num_files ; + + i )
files [ i ] = files [ i + 1 ] ;
2004-06-13 20:20:40 +00:00
}
}
2006-04-20 22:17:20 +00:00
if ( nr > 0 )
2004-06-13 20:20:40 +00:00
{
file_count + = num_files ;
if ( nr < = file_count )
{
2010-06-21 06:15:46 +02:00
* fname_out = vim_strsave (
files [ nr - 1 + num_files - file_count ] ) ;
2019-12-04 21:57:43 +01:00
dirp = ( char_u * ) " " ; // stop searching
2004-06-13 20:20:40 +00:00
}
}
2022-12-05 13:50:55 +00:00
else if ( do_list )
2004-06-13 20:20:40 +00:00
{
if ( dir_name [ 0 ] = = ' . ' & & dir_name [ 1 ] = = NUL )
{
2010-06-21 06:15:46 +02:00
if ( fname = = NULL )
2019-01-19 17:43:09 +01:00
msg_puts ( _ ( " In current directory: \n " ) ) ;
2004-06-13 20:20:40 +00:00
else
2019-01-19 17:43:09 +01:00
msg_puts ( _ ( " Using specified name: \n " ) ) ;
2004-06-13 20:20:40 +00:00
}
else
{
2019-01-19 17:43:09 +01:00
msg_puts ( _ ( " In directory " ) ) ;
2004-06-13 20:20:40 +00:00
msg_home_replace ( dir_name ) ;
2019-01-19 17:43:09 +01:00
msg_puts ( " : \n " ) ;
2004-06-13 20:20:40 +00:00
}
if ( num_files )
{
2022-12-05 13:50:55 +00:00
for ( int i = 0 ; i < num_files ; + + i )
2004-06-13 20:20:40 +00:00
{
2019-12-04 21:57:43 +01:00
// print the swap file name
2004-06-13 20:20:40 +00:00
msg_outnum ( ( long ) + + file_count ) ;
2019-01-19 17:43:09 +01:00
msg_puts ( " . " ) ;
msg_puts ( ( char * ) gettail ( files [ i ] ) ) ;
2004-06-13 20:20:40 +00:00
msg_putchar ( ' \n ' ) ;
( void ) swapfile_info ( files [ i ] ) ;
}
}
else
2019-01-19 17:43:09 +01:00
msg_puts ( _ ( " -- none -- \n " ) ) ;
2004-06-13 20:20:40 +00:00
out_flush ( ) ;
}
2022-12-05 13:50:55 +00:00
# ifdef FEAT_EVAL
else if ( ret_list ! = NULL )
{
for ( int i = 0 ; i < num_files ; + + i )
{
char_u * name = concat_fnames ( dir_name , files [ i ] , TRUE ) ;
if ( name ! = NULL )
{
list_append_string ( ret_list , name , - 1 ) ;
vim_free ( name ) ;
}
}
}
# endif
2004-06-13 20:20:40 +00:00
else
file_count + = num_files ;
2022-12-05 13:50:55 +00:00
for ( int i = 0 ; i < num_names ; + + i )
2004-06-13 20:20:40 +00:00
vim_free ( names [ i ] ) ;
2006-04-20 22:17:20 +00:00
if ( num_files > 0 )
FreeWild ( num_files , files ) ;
2004-06-13 20:20:40 +00:00
}
vim_free ( dir_name ) ;
return file_count ;
}
2019-02-17 17:44:42 +01:00
# if defined(UNIX) || defined(MSWIN) || defined(PROTO)
2004-06-13 20:20:40 +00:00
/*
2018-08-07 21:39:28 +02:00
* Need _very_ long file names .
2004-06-13 20:20:40 +00:00
* Append the full path to name with path separators made into percent
2021-10-19 22:12:25 +01:00
* signs , to " dir " . An unnamed buffer is handled as " " ( < currentdir > / " " )
* The last character in " dir " must be an extra slash or backslash , it is
* removed .
2004-06-13 20:20:40 +00:00
*/
2018-08-07 21:39:28 +02:00
char_u *
2024-07-25 20:58:42 +02:00
make_percent_swname ( char_u * dir , char_u * dir_end , char_u * name )
2004-06-13 20:20:40 +00:00
{
2018-08-07 21:39:28 +02:00
char_u * d = NULL , * s , * f ;
2004-06-13 20:20:40 +00:00
2018-08-07 21:39:28 +02:00
f = fix_fname ( name ! = NULL ? name : ( char_u * ) " " ) ;
2023-01-14 12:32:28 +00:00
if ( f = = NULL )
return NULL ;
2021-10-19 22:12:25 +01:00
2023-01-14 12:32:28 +00:00
s = alloc ( STRLEN ( f ) + 1 ) ;
if ( s ! = NULL )
{
STRCPY ( s , f ) ;
for ( d = s ; * d ! = NUL ; MB_PTR_ADV ( d ) )
if ( vim_ispathsep ( * d ) )
* d = ' % ' ;
2024-07-25 20:58:42 +02:00
dir_end [ - 1 ] = NUL ; // remove one trailing slash
2023-01-14 12:32:28 +00:00
d = concat_fnames ( dir , s , TRUE ) ;
vim_free ( s ) ;
2004-06-13 20:20:40 +00:00
}
2023-01-14 12:32:28 +00:00
vim_free ( f ) ;
2004-06-13 20:20:40 +00:00
return d ;
}
# endif
2019-04-28 22:50:40 +02:00
# if (defined(UNIX) || defined(VMS) || defined(MSWIN)) \
& & ( defined ( FEAT_GUI_DIALOG ) | | defined ( FEAT_CON_DIALOG ) )
# define HAVE_PROCESS_STILL_RUNNING
2004-06-13 20:20:40 +00:00
static int process_still_running ;
# endif
2018-08-21 21:09:07 +02:00
# if defined(FEAT_EVAL) || defined(PROTO)
2018-08-21 20:28:54 +02:00
/*
* Return information found in swapfile " fname " in dictionary " d " .
* This is used by the swapinfo ( ) function .
*/
void
get_b0_dict ( char_u * fname , dict_T * d )
{
int fd ;
struct block0 b0 ;
if ( ( fd = mch_open ( ( char * ) fname , O_RDONLY | O_EXTRA , 0 ) ) > = 0 )
{
if ( read_eintr ( fd , & b0 , sizeof ( b0 ) ) = = sizeof ( b0 ) )
{
2018-08-21 21:09:07 +02:00
if ( ml_check_b0_id ( & b0 ) = = FAIL )
2018-12-26 22:57:42 +01:00
dict_add_string ( d , " error " , ( char_u * ) " Not a swap file " ) ;
2018-08-21 21:09:07 +02:00
else if ( b0_magic_wrong ( & b0 ) )
2018-12-26 22:57:42 +01:00
dict_add_string ( d , " error " , ( char_u * ) " Magic number mismatch " ) ;
2018-08-21 20:28:54 +02:00
else
{
2019-12-04 21:57:43 +01:00
// we have swap information
2018-12-26 22:57:42 +01:00
dict_add_string_len ( d , " version " , b0 . b0_version , 10 ) ;
dict_add_string_len ( d , " user " , b0 . b0_uname , B0_UNAME_SIZE ) ;
dict_add_string_len ( d , " host " , b0 . b0_hname , B0_HNAME_SIZE ) ;
dict_add_string_len ( d , " fname " , b0 . b0_fname , B0_FNAME_SIZE_ORG ) ;
2018-08-21 20:28:54 +02:00
dict_add_number ( d , " pid " , char_to_long ( b0 . b0_pid ) ) ;
dict_add_number ( d , " mtime " , char_to_long ( b0 . b0_mtime ) ) ;
2018-08-21 21:09:07 +02:00
dict_add_number ( d , " dirty " , b0 . b0_dirty ? 1 : 0 ) ;
# ifdef CHECK_INODE
2018-08-21 20:28:54 +02:00
dict_add_number ( d , " inode " , char_to_long ( b0 . b0_ino ) ) ;
2018-08-21 21:09:07 +02:00
# endif
2018-08-21 20:28:54 +02:00
}
}
else
2018-12-26 22:57:42 +01:00
dict_add_string ( d , " error " , ( char_u * ) " Cannot read file " ) ;
2018-08-21 20:28:54 +02:00
close ( fd ) ;
}
else
2018-12-26 22:57:42 +01:00
dict_add_string ( d , " error " , ( char_u * ) " Cannot open file " ) ;
2018-08-21 20:28:54 +02:00
}
2018-08-21 21:09:07 +02:00
# endif
2018-08-21 20:28:54 +02:00
2004-06-13 20:20:40 +00:00
/*
2005-12-07 21:07:59 +00:00
* Give information about an existing swap file .
2004-06-13 20:20:40 +00:00
* Returns timestamp ( 0 when unknown ) .
*/
static time_t
2016-01-30 18:51:09 +01:00
swapfile_info ( char_u * fname )
2004-06-13 20:20:40 +00:00
{
2016-07-01 17:17:39 +02:00
stat_T st ;
2004-06-13 20:20:40 +00:00
int fd ;
struct block0 b0 ;
# ifdef UNIX
char_u uname [ B0_UNAME_SIZE ] ;
# endif
2019-05-10 21:28:38 +02:00
// print the swap file date
2004-06-13 20:20:40 +00:00
if ( mch_stat ( ( char * ) fname , & st ) ! = - 1 )
{
# ifdef UNIX
2019-05-10 21:28:38 +02:00
// print name of owner of the file
2004-06-13 20:20:40 +00:00
if ( mch_get_uname ( st . st_uid , uname , B0_UNAME_SIZE ) = = OK )
{
2019-01-19 17:43:09 +01:00
msg_puts ( _ ( " owned by: " ) ) ;
2004-06-13 20:20:40 +00:00
msg_outtrans ( uname ) ;
2019-01-19 17:43:09 +01:00
msg_puts ( _ ( " dated: " ) ) ;
2004-06-13 20:20:40 +00:00
}
else
# endif
2019-01-19 17:43:09 +01:00
msg_puts ( _ ( " dated: " ) ) ;
2019-05-10 21:28:38 +02:00
msg_puts ( get_ctime ( st . st_mtime , TRUE ) ) ;
2004-06-13 20:20:40 +00:00
}
2019-05-10 21:28:38 +02:00
else
st . st_mtime = 0 ;
2004-06-13 20:20:40 +00:00
/*
* print the original file name
*/
fd = mch_open ( ( char * ) fname , O_RDONLY | O_EXTRA , 0 ) ;
if ( fd > = 0 )
{
2010-12-17 16:27:16 +01:00
if ( read_eintr ( fd , & b0 , sizeof ( b0 ) ) = = sizeof ( b0 ) )
2004-06-13 20:20:40 +00:00
{
if ( STRNCMP ( b0 . b0_version , " VIM 3.0 " , 7 ) = = 0 )
{
2019-01-19 17:43:09 +01:00
msg_puts ( _ ( " [from Vim version 3.0] " ) ) ;
2004-06-13 20:20:40 +00:00
}
2010-06-21 06:15:46 +02:00
else if ( ml_check_b0_id ( & b0 ) = = FAIL )
2004-06-13 20:20:40 +00:00
{
2019-01-19 17:43:09 +01:00
msg_puts ( _ ( " [does not look like a Vim swap file] " ) ) ;
2004-06-13 20:20:40 +00:00
}
else
{
2019-01-19 17:43:09 +01:00
msg_puts ( _ ( " file name: " ) ) ;
2004-06-13 20:20:40 +00:00
if ( b0 . b0_fname [ 0 ] = = NUL )
2019-01-19 17:43:09 +01:00
msg_puts ( _ ( " [No Name] " ) ) ;
2004-06-13 20:20:40 +00:00
else
msg_outtrans ( b0 . b0_fname ) ;
2019-01-19 17:43:09 +01:00
msg_puts ( _ ( " \n modified: " ) ) ;
msg_puts ( b0 . b0_dirty ? _ ( " YES " ) : _ ( " no " ) ) ;
2004-06-13 20:20:40 +00:00
if ( * ( b0 . b0_uname ) ! = NUL )
{
2019-01-19 17:43:09 +01:00
msg_puts ( _ ( " \n user name: " ) ) ;
2004-06-13 20:20:40 +00:00
msg_outtrans ( b0 . b0_uname ) ;
}
if ( * ( b0 . b0_hname ) ! = NUL )
{
if ( * ( b0 . b0_uname ) ! = NUL )
2019-01-19 17:43:09 +01:00
msg_puts ( _ ( " host name: " ) ) ;
2004-06-13 20:20:40 +00:00
else
2019-01-19 17:43:09 +01:00
msg_puts ( _ ( " \n host name: " ) ) ;
2004-06-13 20:20:40 +00:00
msg_outtrans ( b0 . b0_hname ) ;
}
if ( char_to_long ( b0 . b0_pid ) ! = 0L )
{
2019-01-19 17:43:09 +01:00
msg_puts ( _ ( " \n process ID: " ) ) ;
2004-06-13 20:20:40 +00:00
msg_outnum ( char_to_long ( b0 . b0_pid ) ) ;
2019-04-28 22:25:38 +02:00
# if defined(UNIX) || defined(MSWIN)
2021-03-10 21:26:37 +01:00
if ( swapfile_process_running ( & b0 , fname ) )
2004-06-13 20:20:40 +00:00
{
2019-01-19 17:43:09 +01:00
msg_puts ( _ ( " (STILL RUNNING) " ) ) ;
2019-04-28 22:50:40 +02:00
# ifdef HAVE_PROCESS_STILL_RUNNING
2004-06-13 20:20:40 +00:00
process_still_running = TRUE ;
# endif
}
# endif
}
if ( b0_magic_wrong ( & b0 ) )
{
2016-02-23 14:53:34 +01:00
# if defined(MSWIN)
2004-06-13 20:20:40 +00:00
if ( STRNCMP ( b0 . b0_hname , " PC " , 3 ) = = 0 )
2019-01-19 17:43:09 +01:00
msg_puts ( _ ( " \n [not usable with this version of Vim] " ) ) ;
2004-06-13 20:20:40 +00:00
else
# endif
2019-01-19 17:43:09 +01:00
msg_puts ( _ ( " \n [not usable on this computer] " ) ) ;
2004-06-13 20:20:40 +00:00
}
}
}
else
2019-01-19 17:43:09 +01:00
msg_puts ( _ ( " [cannot be read] " ) ) ;
2004-06-13 20:20:40 +00:00
close ( fd ) ;
}
else
2019-01-19 17:43:09 +01:00
msg_puts ( _ ( " [cannot be opened] " ) ) ;
2004-06-13 20:20:40 +00:00
msg_putchar ( ' \n ' ) ;
2019-05-10 21:28:38 +02:00
return st . st_mtime ;
2004-06-13 20:20:40 +00:00
}
2019-04-28 22:25:38 +02:00
/*
* Return TRUE if the swap file looks OK and there are no changes , thus it can
* be safely deleted .
*/
2022-03-23 13:54:51 +00:00
static int
2019-04-28 22:25:38 +02:00
swapfile_unchanged ( char_u * fname )
{
stat_T st ;
int fd ;
struct block0 b0 ;
int ret = TRUE ;
// must be able to stat the swap file
if ( mch_stat ( ( char * ) fname , & st ) = = - 1 )
return FALSE ;
// must be able to read the first block
fd = mch_open ( ( char * ) fname , O_RDONLY | O_EXTRA , 0 ) ;
if ( fd < 0 )
return FALSE ;
if ( read_eintr ( fd , & b0 , sizeof ( b0 ) ) ! = sizeof ( b0 ) )
{
close ( fd ) ;
return FALSE ;
}
// the ID and magic number must be correct
if ( ml_check_b0_id ( & b0 ) = = FAIL | | b0_magic_wrong ( & b0 ) )
ret = FALSE ;
// must be unchanged
if ( b0 . b0_dirty )
ret = FALSE ;
# if defined(UNIX) || defined(MSWIN)
2020-11-09 21:04:17 +01:00
// Host name must be known and must equal the current host name, otherwise
// comparing pid is meaningless.
if ( * ( b0 . b0_hname ) = = NUL )
{
ret = FALSE ;
}
else
{
char_u hostname [ B0_HNAME_SIZE ] ;
mch_get_host_name ( hostname , B0_HNAME_SIZE ) ;
hostname [ B0_HNAME_SIZE - 1 ] = NUL ;
2020-11-21 13:51:16 +01:00
b0 . b0_hname [ B0_HNAME_SIZE - 1 ] = NUL ; // in case of corruption
2020-11-09 21:04:17 +01:00
if ( STRICMP ( b0 . b0_hname , hostname ) ! = 0 )
ret = FALSE ;
}
2019-11-02 22:54:41 +01:00
// process must be known and not be running
2021-03-10 21:26:37 +01:00
if ( char_to_long ( b0 . b0_pid ) = = 0L | | swapfile_process_running ( & b0 , fname ) )
2019-04-28 22:25:38 +02:00
ret = FALSE ;
# endif
2020-11-09 21:04:17 +01:00
// We do not check the user, it should be irrelevant for whether the swap
// file is still useful.
2019-04-28 22:25:38 +02:00
close ( fd ) ;
return ret ;
}
2004-06-13 20:20:40 +00:00
static int
2016-01-30 18:51:09 +01:00
recov_file_names ( char_u * * names , char_u * path , int prepend_dot )
2004-06-13 20:20:40 +00:00
{
int num_names ;
/*
* ( Win32 and Win64 ) never short names , but do prepend a dot .
* ( Not MS - DOS or Win32 or Win64 ) maybe short name , maybe not : Try both .
* Only use the short name if it is different .
*/
char_u * p ;
int i ;
2019-02-17 17:44:42 +01:00
# ifndef MSWIN
2004-06-13 20:20:40 +00:00
int shortname = curbuf - > b_shortname ;
curbuf - > b_shortname = FALSE ;
# endif
num_names = 0 ;
/*
* May also add the file name with a dot prepended , for swap file in same
* dir as original file .
*/
if ( prepend_dot )
{
names [ num_names ] = modname ( path , ( char_u * ) " .sw? " , TRUE ) ;
if ( names [ num_names ] = = NULL )
goto end ;
+ + num_names ;
}
/*
* Form the normal swap file name pattern by appending " .sw? " .
*/
# ifdef VMS
names [ num_names ] = concat_fnames ( path , ( char_u * ) " _sw% " , FALSE ) ;
# else
names [ num_names ] = concat_fnames ( path , ( char_u * ) " .sw? " , FALSE ) ;
# endif
if ( names [ num_names ] = = NULL )
goto end ;
2019-12-04 21:57:43 +01:00
if ( num_names > = 1 ) // check if we have the same name twice
2004-06-13 20:20:40 +00:00
{
p = names [ num_names - 1 ] ;
i = ( int ) STRLEN ( names [ num_names - 1 ] ) - ( int ) STRLEN ( names [ num_names ] ) ;
if ( i > 0 )
2019-12-04 21:57:43 +01:00
p + = i ; // file name has been expanded to full path
2004-06-13 20:20:40 +00:00
if ( STRCMP ( p , names [ num_names ] ) ! = 0 )
+ + num_names ;
else
vim_free ( names [ num_names ] ) ;
}
else
+ + num_names ;
2019-02-17 17:44:42 +01:00
# ifndef MSWIN
2004-06-13 20:20:40 +00:00
/*
* Also try with ' shortname ' set , in case the file is on a DOS filesystem .
*/
curbuf - > b_shortname = TRUE ;
# ifdef VMS
names [ num_names ] = modname ( path , ( char_u * ) " _sw% " , FALSE ) ;
# else
names [ num_names ] = modname ( path , ( char_u * ) " .sw? " , FALSE ) ;
# endif
if ( names [ num_names ] = = NULL )
goto end ;
/*
* Remove the one from ' shortname ' , if it ' s the same as with ' noshortname ' .
*/
p = names [ num_names ] ;
i = STRLEN ( names [ num_names ] ) - STRLEN ( names [ num_names - 1 ] ) ;
if ( i > 0 )
2019-12-04 21:57:43 +01:00
p + = i ; // file name has been expanded to full path
2004-06-13 20:20:40 +00:00
if ( STRCMP ( names [ num_names - 1 ] , p ) = = 0 )
vim_free ( names [ num_names ] ) ;
else
+ + num_names ;
# endif
end :
2019-02-17 17:44:42 +01:00
# ifndef MSWIN
2004-06-13 20:20:40 +00:00
curbuf - > b_shortname = shortname ;
# endif
return num_names ;
}
/*
* sync all memlines
*
* If ' check_file ' is TRUE , check if original file exists and was not changed .
* If ' check_char ' is TRUE , stop syncing when character becomes available , but
* always sync at least one block .
*/
void
2016-01-30 18:51:09 +01:00
ml_sync_all ( int check_file , int check_char )
2004-06-13 20:20:40 +00:00
{
buf_T * buf ;
2016-07-01 17:17:39 +02:00
stat_T st ;
2004-06-13 20:20:40 +00:00
2016-07-24 22:04:11 +02:00
FOR_ALL_BUFFERS ( buf )
2004-06-13 20:20:40 +00:00
{
2021-06-20 14:02:16 +02:00
if ( buf - > b_ml . ml_mfp = = NULL
| | buf - > b_ml . ml_mfp - > mf_fname = = NULL
| | buf - > b_ml . ml_mfp - > mf_fd < 0 )
2019-12-04 21:57:43 +01:00
continue ; // no file
2004-06-13 20:20:40 +00:00
2023-06-27 18:57:10 +01:00
# ifdef FEAT_CRYPT
if ( crypt_may_close_swapfile ( buf , buf - > b_p_key ,
crypt_get_method_nr ( buf ) ) )
continue ;
# endif
2019-12-04 21:57:43 +01:00
ml_flush_line ( buf ) ; // flush buffered line
// flush locked block
2004-06-13 20:20:40 +00:00
( void ) ml_find_line ( buf , ( linenr_T ) 0 , ML_FLUSH ) ;
if ( bufIsChanged ( buf ) & & check_file & & mf_need_trans ( buf - > b_ml . ml_mfp )
& & buf - > b_ffname ! = NULL )
{
/*
* If the original file does not exist anymore or has been changed
* call ml_preserve ( ) to get rid of all negative numbered blocks .
*/
if ( mch_stat ( ( char * ) buf - > b_ffname , & st ) = = - 1
| | st . st_mtime ! = buf - > b_mtime_read
2021-10-14 21:27:55 +01:00
# ifdef ST_MTIM_NSEC
| | st . ST_MTIM_NSEC ! = buf - > b_mtime_read_ns
# endif
2010-05-31 21:59:46 +02:00
| | st . st_size ! = buf - > b_orig_size )
2004-06-13 20:20:40 +00:00
{
ml_preserve ( buf , FALSE ) ;
did_check_timestamps = FALSE ;
2019-12-04 21:57:43 +01:00
need_check_timestamps = TRUE ; // give message later
2004-06-13 20:20:40 +00:00
}
}
2023-05-27 18:02:55 +01:00
if ( buf - > b_ml . ml_mfp - > mf_dirty = = MF_DIRTY_YES )
2004-06-13 20:20:40 +00:00
{
( void ) mf_sync ( buf - > b_ml . ml_mfp , ( check_char ? MFS_STOP : 0 )
| ( bufIsChanged ( buf ) ? MFS_FLUSH : 0 ) ) ;
2019-12-04 21:57:43 +01:00
if ( check_char & & ui_char_avail ( ) ) // character available now
2004-06-13 20:20:40 +00:00
break ;
}
}
}
/*
* sync one buffer , including negative blocks
*
* after this all the blocks are in the swap file
*
* Used for the : preserve command and when the original file has been
* changed or deleted .
*
* when message is TRUE the success of preserving is reported
*/
void
2016-01-30 18:51:09 +01:00
ml_preserve ( buf_T * buf , int message )
2004-06-13 20:20:40 +00:00
{
bhdr_T * hp ;
linenr_T lnum ;
memfile_T * mfp = buf - > b_ml . ml_mfp ;
int status ;
int got_int_save = got_int ;
if ( mfp = = NULL | | mfp - > mf_fname = = NULL )
{
if ( message )
2022-01-02 17:00:40 +00:00
emsg ( _ ( e_cannot_preserve_there_is_no_swap_file ) ) ;
2004-06-13 20:20:40 +00:00
return ;
}
2023-06-27 18:57:10 +01:00
# ifdef FEAT_CRYPT
if ( crypt_may_close_swapfile ( buf , buf - > b_p_key , crypt_get_method_nr ( buf ) ) )
return ;
# endif
2004-06-13 20:20:40 +00:00
2019-12-04 21:57:43 +01:00
// We only want to stop when interrupted here, not when interrupted
// before.
2004-06-13 20:20:40 +00:00
got_int = FALSE ;
2019-12-04 21:57:43 +01:00
ml_flush_line ( buf ) ; // flush buffered line
( void ) ml_find_line ( buf , ( linenr_T ) 0 , ML_FLUSH ) ; // flush locked block
2004-06-13 20:20:40 +00:00
status = mf_sync ( mfp , MFS_ALL | MFS_FLUSH ) ;
2019-12-04 21:57:43 +01:00
// stack is invalid after mf_sync(.., MFS_ALL)
2004-06-13 20:20:40 +00:00
buf - > b_ml . ml_stack_top = 0 ;
/*
* Some of the data blocks may have been changed from negative to
* positive block number . In that case the pointer blocks need to be
* updated .
*
* We don ' t know in which pointer block the references are , so we visit
* all data blocks until there are no more translations to be done ( or
* we hit the end of the file , which can only happen in case a write fails ,
* e . g . when file system if full ) .
* ml_find_line ( ) does the work by translating the negative block numbers
* when getting the first line of each data block .
*/
if ( mf_need_trans ( mfp ) & & ! got_int )
{
lnum = 1 ;
while ( mf_need_trans ( mfp ) & & lnum < = buf - > b_ml . ml_line_count )
{
hp = ml_find_line ( buf , lnum , ML_FIND ) ;
if ( hp = = NULL )
{
status = FAIL ;
goto theend ;
}
CHECK ( buf - > b_ml . ml_locked_low ! = lnum , " low != lnum " ) ;
lnum = buf - > b_ml . ml_locked_high + 1 ;
}
2019-12-04 21:57:43 +01:00
( void ) ml_find_line ( buf , ( linenr_T ) 0 , ML_FLUSH ) ; // flush locked block
// sync the updated pointer blocks
2004-06-13 20:20:40 +00:00
if ( mf_sync ( mfp , MFS_ALL | MFS_FLUSH ) = = FAIL )
status = FAIL ;
2019-12-04 21:57:43 +01:00
buf - > b_ml . ml_stack_top = 0 ; // stack is invalid now
2004-06-13 20:20:40 +00:00
}
theend :
got_int | = got_int_save ;
if ( message )
{
if ( status = = OK )
2019-01-19 17:43:09 +01:00
msg ( _ ( " File preserved " ) ) ;
2004-06-13 20:20:40 +00:00
else
2022-01-02 17:00:40 +00:00
emsg ( _ ( e_preserve_failed ) ) ;
2004-06-13 20:20:40 +00:00
}
}
/*
* NOTE : The pointer returned by the ml_get_ * ( ) functions only remains valid
* until the next call !
* line1 = ml_get ( 1 ) ;
* line2 = ml_get ( 2 ) ; // line1 is now invalid!
* Make a copy of the line if necessary .
*/
/*
2010-12-08 13:17:03 +01:00
* Return a pointer to a ( read - only copy of a ) line .
2004-06-13 20:20:40 +00:00
*
* On failure an error message is given and IObuff is returned ( to avoid
* having to check for error everywhere ) .
*/
char_u *
2016-01-30 18:51:09 +01:00
ml_get ( linenr_T lnum )
2004-06-13 20:20:40 +00:00
{
return ml_get_buf ( curbuf , lnum , FALSE ) ;
}
/*
2010-12-08 13:17:03 +01:00
* Return pointer to position " pos " .
2004-06-13 20:20:40 +00:00
*/
char_u *
2016-01-30 18:51:09 +01:00
ml_get_pos ( pos_T * pos )
2004-06-13 20:20:40 +00:00
{
return ( ml_get_buf ( curbuf , pos - > lnum , FALSE ) + pos - > col ) ;
}
/*
2010-12-08 13:17:03 +01:00
* Return pointer to cursor line .
2004-06-13 20:20:40 +00:00
*/
char_u *
2016-01-30 18:51:09 +01:00
ml_get_curline ( void )
2004-06-13 20:20:40 +00:00
{
return ml_get_buf ( curbuf , curwin - > w_cursor . lnum , FALSE ) ;
}
/*
2010-12-08 13:17:03 +01:00
* Return pointer to cursor position .
2004-06-13 20:20:40 +00:00
*/
char_u *
2016-01-30 18:51:09 +01:00
ml_get_cursor ( void )
2004-06-13 20:20:40 +00:00
{
return ( ml_get_buf ( curbuf , curwin - > w_cursor . lnum , FALSE ) +
curwin - > w_cursor . col ) ;
}
2024-02-26 21:21:17 +01:00
// return length (excluding the NUL) of the given line
colnr_T
ml_get_len ( linenr_T lnum )
{
return ml_get_buf_len ( curbuf , lnum ) ;
}
2024-03-12 21:50:32 +01:00
// return length (excluding the NUL) of the text after position "pos"
colnr_T
ml_get_pos_len ( pos_T * pos )
{
2024-03-13 20:42:26 +01:00
return ml_get_buf_len ( curbuf , pos - > lnum ) - pos - > col ;
2024-03-12 21:50:32 +01:00
}
2024-02-26 21:21:17 +01:00
// return length (excluding the NUL) of the cursor line
colnr_T
ml_get_curline_len ( void )
{
return ml_get_buf_len ( curbuf , curwin - > w_cursor . lnum ) ;
}
// return length (excluding the NUL) of the cursor position
colnr_T
ml_get_cursor_len ( void )
{
return ml_get_buf_len ( curbuf , curwin - > w_cursor . lnum ) - curwin - > w_cursor . col ;
}
// return length (excluding the NUL) of the given line in the given buffer
colnr_T
ml_get_buf_len ( buf_T * buf , linenr_T lnum )
{
2024-03-10 08:55:42 +01:00
char_u * line ;
if ( * ( line = ml_get_buf ( buf , lnum , FALSE ) ) = = NUL )
2024-07-14 10:20:20 +02:00
return 0 ;
2024-02-26 21:21:17 +01:00
2024-03-10 08:55:42 +01:00
if ( buf - > b_ml . ml_line_textlen < = 0 )
buf - > b_ml . ml_line_textlen = ( int ) STRLEN ( line ) + 1 ;
2024-03-05 20:43:25 +01:00
return buf - > b_ml . ml_line_textlen - 1 ;
2024-02-26 21:21:17 +01:00
}
2004-06-13 20:20:40 +00:00
/*
2010-12-08 13:17:03 +01:00
* Return a pointer to a line in a specific buffer
2004-06-13 20:20:40 +00:00
*
* " will_change " : if TRUE mark the buffer dirty ( chars in the line will be
* changed )
*/
char_u *
2016-01-30 18:51:09 +01:00
ml_get_buf (
buf_T * buf ,
linenr_T lnum ,
2019-12-04 21:57:43 +01:00
int will_change ) // line will be changed
2004-06-13 20:20:40 +00:00
{
2007-02-13 03:01:39 +00:00
bhdr_T * hp ;
DATA_BL * dp ;
static int recursive = 0 ;
2021-11-25 13:52:37 +00:00
static char_u questions [ 4 ] ;
2004-06-13 20:20:40 +00:00
2019-12-04 21:57:43 +01:00
if ( lnum > buf - > b_ml . ml_line_count ) // invalid line number
2004-06-13 20:20:40 +00:00
{
2007-02-13 03:01:39 +00:00
if ( recursive = = 0 )
{
2019-12-04 21:57:43 +01:00
// Avoid giving this message for a recursive call, may happen when
// the GUI redraws part of the text.
2007-02-13 03:01:39 +00:00
+ + recursive ;
2023-05-31 17:12:14 +01:00
siemsg ( e_ml_get_invalid_lnum_nr , lnum ) ;
2007-02-13 03:01:39 +00:00
- - recursive ;
}
2022-02-16 16:33:28 +00:00
ml_flush_line ( buf ) ;
2004-06-13 20:20:40 +00:00
errorret :
2021-11-25 13:52:37 +00:00
STRCPY ( questions , " ??? " ) ;
2019-05-25 22:11:45 +02:00
buf - > b_ml . ml_line_len = 4 ;
2024-03-05 20:43:25 +01:00
buf - > b_ml . ml_line_textlen = buf - > b_ml . ml_line_len ;
2022-02-16 16:33:28 +00:00
buf - > b_ml . ml_line_lnum = lnum ;
2021-11-25 13:52:37 +00:00
return questions ;
2004-06-13 20:20:40 +00:00
}
2019-05-25 22:11:45 +02:00
if ( lnum < = 0 ) // pretend line 0 is line 1
2004-06-13 20:20:40 +00:00
lnum = 1 ;
2019-05-25 22:11:45 +02:00
if ( buf - > b_ml . ml_mfp = = NULL ) // there are no lines
{
buf - > b_ml . ml_line_len = 1 ;
2024-03-05 20:43:25 +01:00
buf - > b_ml . ml_line_textlen = buf - > b_ml . ml_line_len ;
2004-06-13 20:20:40 +00:00
return ( char_u * ) " " ;
2019-05-25 22:11:45 +02:00
}
2004-06-13 20:20:40 +00:00
2010-03-10 14:46:26 +01:00
/*
* See if it is the same line as requested last time .
* Otherwise may need to flush last used line .
* Don ' t use the last used line when ' swapfile ' is reset , need to load all
* blocks .
*/
2007-02-07 02:41:57 +00:00
if ( buf - > b_ml . ml_line_lnum ! = lnum | | mf_dont_release )
2004-06-13 20:20:40 +00:00
{
2018-12-13 22:20:09 +01:00
unsigned start , end ;
int idx ;
2004-06-13 20:20:40 +00:00
ml_flush_line ( buf ) ;
/*
* Find the data block containing the line .
* This also fills the stack with the blocks from the root to the data
* block and releases any locked block .
*/
if ( ( hp = ml_find_line ( buf , lnum , ML_FIND ) ) = = NULL )
{
2007-02-13 03:01:39 +00:00
if ( recursive = = 0 )
{
2019-12-04 21:57:43 +01:00
// Avoid giving this message for a recursive call, may happen
// when the GUI redraws part of the text.
2007-02-13 03:01:39 +00:00
+ + recursive ;
2019-10-26 20:56:21 +02:00
get_trans_bufname ( buf ) ;
shorten_dir ( NameBuff ) ;
2023-05-31 17:12:14 +01:00
siemsg ( e_ml_get_cannot_find_line_nr_in_buffer_nr_str ,
2019-10-26 20:56:21 +02:00
lnum , buf - > b_fnum , NameBuff ) ;
2007-02-13 03:01:39 +00:00
- - recursive ;
}
2004-06-13 20:20:40 +00:00
goto errorret ;
}
dp = ( DATA_BL * ) ( hp - > bh_data ) ;
2018-12-13 22:20:09 +01:00
idx = lnum - buf - > b_ml . ml_locked_low ;
start = ( ( dp - > db_index [ idx ] ) & DB_INDEX_MASK ) ;
// The text ends where the previous line starts. The first line ends
// at the end of the block.
if ( idx = = 0 )
end = dp - > db_txt_end ;
else
end = ( ( dp - > db_index [ idx - 1 ] ) & DB_INDEX_MASK ) ;
buf - > b_ml . ml_line_ptr = ( char_u * ) dp + start ;
2024-02-26 21:21:17 +01:00
buf - > b_ml . ml_line_len = end - start ;
2024-03-05 20:43:25 +01:00
# if defined(FEAT_BYTEOFF) && defined(FEAT_PROP_POPUP)
2024-03-10 08:55:42 +01:00
// Text properties come after a NUL byte, so ml_line_len should be
// larger than the size of textprop_T if there is any.
if ( buf - > b_has_textprop
& & ( size_t ) buf - > b_ml . ml_line_len > sizeof ( textprop_T ) )
buf - > b_ml . ml_line_textlen = 0 ; // call STRLEN() later when needed
2024-03-05 20:43:25 +01:00
else
# endif
buf - > b_ml . ml_line_textlen = buf - > b_ml . ml_line_len ;
2004-06-13 20:20:40 +00:00
buf - > b_ml . ml_line_lnum = lnum ;
2022-06-30 22:13:59 +01:00
buf - > b_ml . ml_flags & = ~ ( ML_LINE_DIRTY | ML_ALLOCATED ) ;
2004-06-13 20:20:40 +00:00
}
if ( will_change )
2022-06-30 22:13:59 +01:00
{
2004-06-13 20:20:40 +00:00
buf - > b_ml . ml_flags | = ( ML_LOCKED_DIRTY | ML_LOCKED_POS ) ;
2022-06-30 22:13:59 +01:00
# ifdef FEAT_EVAL
if ( ml_get_alloc_lines & & ( buf - > b_ml . ml_flags & ML_ALLOCATED ) )
// can't make the change in the data block
buf - > b_ml . ml_flags | = ML_LINE_DIRTY ;
# endif
}
# ifdef FEAT_EVAL
if ( ml_get_alloc_lines
& & ( buf - > b_ml . ml_flags & ( ML_LINE_DIRTY | ML_ALLOCATED ) ) = = 0 )
{
char_u * p = alloc ( buf - > b_ml . ml_line_len ) ;
2004-06-13 20:20:40 +00:00
2022-06-30 22:13:59 +01:00
// make sure the text is in allocated memory
if ( p ! = NULL )
{
memmove ( p , buf - > b_ml . ml_line_ptr , buf - > b_ml . ml_line_len ) ;
buf - > b_ml . ml_line_ptr = p ;
buf - > b_ml . ml_flags | = ML_ALLOCATED ;
if ( will_change )
// can't make the change in the data block
buf - > b_ml . ml_flags | = ML_LINE_DIRTY ;
}
}
# endif
2004-06-13 20:20:40 +00:00
return buf - > b_ml . ml_line_ptr ;
}
/*
* Check if a line that was just obtained by a call to ml_get
* is in allocated memory .
2022-06-30 22:13:59 +01:00
* This ignores ML_ALLOCATED to get the same behavior as without the test
* override .
2004-06-13 20:20:40 +00:00
*/
int
2016-01-30 18:51:09 +01:00
ml_line_alloced ( void )
2004-06-13 20:20:40 +00:00
{
return ( curbuf - > b_ml . ml_flags & ML_LINE_DIRTY ) ;
}
2019-11-30 22:48:27 +01:00
# ifdef FEAT_PROP_POPUP
2020-05-30 14:46:52 +02:00
/*
* Add text properties that continue from the previous line .
*/
2018-12-28 23:22:40 +01:00
static void
add_text_props_for_append (
buf_T * buf ,
linenr_T lnum ,
char_u * * line ,
int * len ,
char_u * * tofree )
{
int round ;
int new_prop_count = 0 ;
int count ;
int n ;
char_u * props ;
2019-09-04 18:53:12 +02:00
int new_len = 0 ; // init for gcc
2020-09-12 21:04:23 +02:00
char_u * new_line = NULL ;
2018-12-28 23:22:40 +01:00
textprop_T prop ;
// Make two rounds:
// 1. calculate the extra space needed
// 2. allocate the space and fill it
for ( round = 1 ; round < = 2 ; + + round )
{
if ( round = = 2 )
{
if ( new_prop_count = = 0 )
return ; // nothing to do
new_len = * len + new_prop_count * sizeof ( textprop_T ) ;
2019-05-24 18:54:09 +02:00
new_line = alloc ( new_len ) ;
2018-12-28 23:22:40 +01:00
if ( new_line = = NULL )
return ;
mch_memmove ( new_line , * line , * len ) ;
new_prop_count = 0 ;
}
// Get the line above to find any props that continue in the next
// line.
count = get_text_props ( buf , lnum , & props , FALSE ) ;
for ( n = 0 ; n < count ; + + n )
{
2020-05-30 14:46:52 +02:00
mch_memmove ( & prop , props + n * sizeof ( textprop_T ) ,
sizeof ( textprop_T ) ) ;
2018-12-28 23:22:40 +01:00
if ( prop . tp_flags & TP_FLAG_CONT_NEXT )
{
if ( round = = 2 )
{
prop . tp_flags | = TP_FLAG_CONT_PREV ;
prop . tp_col = 1 ;
2020-05-30 14:46:52 +02:00
prop . tp_len = * len ; // not exactly the right length
mch_memmove ( new_line + * len + new_prop_count
* sizeof ( textprop_T ) , & prop , sizeof ( textprop_T ) ) ;
2018-12-28 23:22:40 +01:00
}
+ + new_prop_count ;
}
}
}
* line = new_line ;
* tofree = new_line ;
* len = new_len ;
}
2006-01-12 23:22:24 +00:00
# endif
2004-06-13 20:20:40 +00:00
static int
2016-01-30 18:51:09 +01:00
ml_append_int (
buf_T * buf ,
2018-12-13 22:20:09 +01:00
linenr_T lnum , // append after this line (can be 0)
2018-12-28 23:22:40 +01:00
char_u * line_arg , // text of the new line
2018-12-13 22:20:09 +01:00
colnr_T len_arg , // length of line, including NUL, or 0
2020-05-30 14:46:52 +02:00
int flags ) // ML_APPEND_ flags
2004-06-13 20:20:40 +00:00
{
2018-12-28 23:22:40 +01:00
char_u * line = line_arg ;
colnr_T len = len_arg ;
2004-06-13 20:20:40 +00:00
int i ;
2018-12-13 22:20:09 +01:00
int line_count ; // number of indexes in current block
2004-06-13 20:20:40 +00:00
int offset ;
int from , to ;
2018-12-13 22:20:09 +01:00
int space_needed ; // space needed for new line
2004-06-13 20:20:40 +00:00
int page_size ;
int page_count ;
2018-12-13 22:20:09 +01:00
int db_idx ; // index for lnum in data block
2004-06-13 20:20:40 +00:00
bhdr_T * hp ;
memfile_T * mfp ;
DATA_BL * dp ;
PTR_BL * pp ;
infoptr_T * ip ;
2019-11-30 22:48:27 +01:00
# ifdef FEAT_PROP_POPUP
2018-12-28 23:22:40 +01:00
char_u * tofree = NULL ;
2022-08-07 21:48:37 +01:00
# ifdef FEAT_BYTEOFF
colnr_T text_len = 0 ; // text len with NUL without text properties
# endif
2018-12-28 23:22:40 +01:00
# endif
int ret = FAIL ;
2004-06-13 20:20:40 +00:00
if ( lnum > buf - > b_ml . ml_line_count | | buf - > b_ml . ml_mfp = = NULL )
2018-12-28 23:22:40 +01:00
return FAIL ; // lnum out of range
2004-06-13 20:20:40 +00:00
if ( lowest_marked & & lowest_marked > lnum )
lowest_marked = lnum + 1 ;
if ( len = = 0 )
2022-08-07 21:48:37 +01:00
{
2018-12-13 22:20:09 +01:00
len = ( colnr_T ) STRLEN ( line ) + 1 ; // space needed for the text
2022-08-07 21:48:37 +01:00
# if defined(FEAT_PROP_POPUP) && defined(FEAT_BYTEOFF)
text_len = len ;
# endif
}
# if defined(FEAT_PROP_POPUP) && defined(FEAT_BYTEOFF)
else if ( curbuf - > b_has_textprop )
// "len" may include text properties, get the length of the text.
text_len = ( colnr_T ) STRLEN ( line ) + 1 ;
else
text_len = len ;
# endif
2018-12-28 23:22:40 +01:00
2019-11-30 22:48:27 +01:00
# ifdef FEAT_PROP_POPUP
2021-05-26 22:32:10 +02:00
if ( curbuf - > b_has_textprop & & lnum > 0
& & ! ( flags & ( ML_APPEND_UNDO | ML_APPEND_NOPROP ) ) )
2018-12-28 23:22:40 +01:00
// Add text properties that continue from the previous line.
add_text_props_for_append ( buf , lnum , & line , & len , & tofree ) ;
# endif
2018-12-13 22:20:09 +01:00
space_needed = len + INDEX_SIZE ; // space needed for text + index
2004-06-13 20:20:40 +00:00
mfp = buf - > b_ml . ml_mfp ;
page_size = mfp - > mf_page_size ;
/*
* find the data block containing the previous line
* This also fills the stack with the blocks from the root to the data block
* This also releases any locked block .
*/
if ( ( hp = ml_find_line ( buf , lnum = = 0 ? ( linenr_T ) 1 : lnum ,
ML_INSERT ) ) = = NULL )
2018-12-28 23:22:40 +01:00
goto theend ;
2004-06-13 20:20:40 +00:00
buf - > b_ml . ml_flags & = ~ ML_EMPTY ;
2019-12-04 21:57:43 +01:00
if ( lnum = = 0 ) // got line one instead, correct db_idx
db_idx = - 1 ; // careful, it is negative!
2004-06-13 20:20:40 +00:00
else
db_idx = lnum - buf - > b_ml . ml_locked_low ;
2019-12-04 21:57:43 +01:00
// get line count before the insertion
2004-06-13 20:20:40 +00:00
line_count = buf - > b_ml . ml_locked_high - buf - > b_ml . ml_locked_low ;
dp = ( DATA_BL * ) ( hp - > bh_data ) ;
/*
* If
* - there is not enough room in the current block
* - appending to the last line in the block
* - not appending to the last line in the file
* insert in front of the next block .
*/
if ( ( int ) dp - > db_free < space_needed & & db_idx = = line_count - 1
& & lnum < buf - > b_ml . ml_line_count )
{
/*
* Now that the line is not going to be inserted in the block that we
* expected , the line count has to be adjusted in the pointer blocks
* by using ml_locked_lineadd .
*/
- - ( buf - > b_ml . ml_locked_lineadd ) ;
- - ( buf - > b_ml . ml_locked_high ) ;
if ( ( hp = ml_find_line ( buf , lnum + 1 , ML_INSERT ) ) = = NULL )
2018-12-28 23:22:40 +01:00
goto theend ;
2004-06-13 20:20:40 +00:00
2019-12-04 21:57:43 +01:00
db_idx = - 1 ; // careful, it is negative!
// get line count before the insertion
2004-06-13 20:20:40 +00:00
line_count = buf - > b_ml . ml_locked_high - buf - > b_ml . ml_locked_low ;
CHECK ( buf - > b_ml . ml_locked_low ! = lnum + 1 , " locked_low != lnum + 1 " ) ;
dp = ( DATA_BL * ) ( hp - > bh_data ) ;
}
+ + buf - > b_ml . ml_line_count ;
2019-12-04 21:57:43 +01:00
if ( ( int ) dp - > db_free > = space_needed ) // enough room in data block
2004-06-13 20:20:40 +00:00
{
2018-12-28 23:22:40 +01:00
/*
* Insert the new line in an existing data block , or in the data block
* allocated above .
*/
2004-06-13 20:20:40 +00:00
dp - > db_txt_start - = len ;
dp - > db_free - = space_needed ;
+ + ( dp - > db_line_count ) ;
/*
* move the text of the lines that follow to the front
* adjust the indexes of the lines that follow
*/
2019-12-04 21:57:43 +01:00
if ( line_count > db_idx + 1 ) // if there are following lines
2004-06-13 20:20:40 +00:00
{
/*
* Offset is the start of the previous line .
* This will become the character just after the new line .
*/
if ( db_idx < 0 )
offset = dp - > db_txt_end ;
else
offset = ( ( dp - > db_index [ db_idx ] ) & DB_INDEX_MASK ) ;
mch_memmove ( ( char * ) dp + dp - > db_txt_start ,
( char * ) dp + dp - > db_txt_start + len ,
( size_t ) ( offset - ( dp - > db_txt_start + len ) ) ) ;
for ( i = line_count - 1 ; i > db_idx ; - - i )
dp - > db_index [ i + 1 ] = dp - > db_index [ i ] - len ;
dp - > db_index [ db_idx + 1 ] = offset - len ;
}
2018-12-13 22:20:09 +01:00
else
// add line at the end (which is the start of the text)
2004-06-13 20:20:40 +00:00
dp - > db_index [ db_idx + 1 ] = dp - > db_txt_start ;
/*
* copy the text into the block
*/
mch_memmove ( ( char * ) dp + dp - > db_index [ db_idx + 1 ] , line , ( size_t ) len ) ;
2020-05-30 14:46:52 +02:00
if ( flags & ML_APPEND_MARK )
2004-06-13 20:20:40 +00:00
dp - > db_index [ db_idx + 1 ] | = DB_MARKED ;
/*
* Mark the block dirty .
*/
buf - > b_ml . ml_flags | = ML_LOCKED_DIRTY ;
2020-05-30 14:46:52 +02:00
if ( ! ( flags & ML_APPEND_NEW ) )
2004-06-13 20:20:40 +00:00
buf - > b_ml . ml_flags | = ML_LOCKED_POS ;
}
2019-12-04 21:57:43 +01:00
else // not enough space in data block
2004-06-13 20:20:40 +00:00
{
long line_count_left , line_count_right ;
int page_count_left , page_count_right ;
bhdr_T * hp_left ;
bhdr_T * hp_right ;
bhdr_T * hp_new ;
int lines_moved ;
2019-12-04 21:57:43 +01:00
int data_moved = 0 ; // init to shut up gcc
int total_moved = 0 ; // init to shut up gcc
2004-06-13 20:20:40 +00:00
DATA_BL * dp_right , * dp_left ;
int stack_idx ;
int in_left ;
int lineadd ;
blocknr_T bnum_left , bnum_right ;
linenr_T lnum_left , lnum_right ;
int pb_idx ;
PTR_BL * pp_new ;
/*
2018-12-28 23:22:40 +01:00
* There is not enough room , we have to create a new data block and
* copy some lines into it .
* Then we have to insert an entry in the pointer block .
* If this pointer block also is full , we go up another block , and so
* on , up to the root if necessary .
* The line counts in the pointer blocks have already been adjusted by
* ml_find_line ( ) .
*
2004-06-13 20:20:40 +00:00
* We are going to allocate a new data block . Depending on the
* situation it will be put to the left or right of the existing
* block . If possible we put the new line in the left block and move
* the lines after it to the right block . Otherwise the new line is
* also put in the right block . This method is more efficient when
* inserting a lot of lines at one place .
*/
2019-12-04 21:57:43 +01:00
if ( db_idx < 0 ) // left block is new, right block is existing
2004-06-13 20:20:40 +00:00
{
lines_moved = 0 ;
in_left = TRUE ;
2019-12-04 21:57:43 +01:00
// space_needed does not change
2004-06-13 20:20:40 +00:00
}
2019-12-04 21:57:43 +01:00
else // left block is existing, right block is new
2004-06-13 20:20:40 +00:00
{
lines_moved = line_count - db_idx - 1 ;
if ( lines_moved = = 0 )
2019-12-04 21:57:43 +01:00
in_left = FALSE ; // put new line in right block
// space_needed does not change
2004-06-13 20:20:40 +00:00
else
{
data_moved = ( ( dp - > db_index [ db_idx ] ) & DB_INDEX_MASK ) -
dp - > db_txt_start ;
total_moved = data_moved + lines_moved * INDEX_SIZE ;
if ( ( int ) dp - > db_free + total_moved > = space_needed )
{
2019-12-04 21:57:43 +01:00
in_left = TRUE ; // put new line in left block
2004-06-13 20:20:40 +00:00
space_needed = total_moved ;
}
else
{
2019-12-04 21:57:43 +01:00
in_left = FALSE ; // put new line in right block
2004-06-13 20:20:40 +00:00
space_needed + = total_moved ;
}
}
}
page_count = ( ( space_needed + HEADER_SIZE ) + page_size - 1 ) / page_size ;
2020-05-30 14:46:52 +02:00
if ( ( hp_new = ml_new_data ( mfp , flags & ML_APPEND_NEW , page_count ) )
= = NULL )
2004-06-13 20:20:40 +00:00
{
2019-12-04 21:57:43 +01:00
// correct line counts in pointer blocks
2004-06-13 20:20:40 +00:00
- - ( buf - > b_ml . ml_locked_lineadd ) ;
- - ( buf - > b_ml . ml_locked_high ) ;
2018-12-28 23:22:40 +01:00
goto theend ;
2004-06-13 20:20:40 +00:00
}
2019-12-04 21:57:43 +01:00
if ( db_idx < 0 ) // left block is new
2004-06-13 20:20:40 +00:00
{
hp_left = hp_new ;
hp_right = hp ;
line_count_left = 0 ;
line_count_right = line_count ;
}
2019-12-04 21:57:43 +01:00
else // right block is new
2004-06-13 20:20:40 +00:00
{
hp_left = hp ;
hp_right = hp_new ;
line_count_left = line_count ;
line_count_right = 0 ;
}
dp_right = ( DATA_BL * ) ( hp_right - > bh_data ) ;
dp_left = ( DATA_BL * ) ( hp_left - > bh_data ) ;
bnum_left = hp_left - > bh_bnum ;
bnum_right = hp_right - > bh_bnum ;
page_count_left = hp_left - > bh_page_count ;
page_count_right = hp_right - > bh_page_count ;
/*
* May move the new line into the right / new block .
*/
if ( ! in_left )
{
dp_right - > db_txt_start - = len ;
dp_right - > db_free - = len + INDEX_SIZE ;
dp_right - > db_index [ 0 ] = dp_right - > db_txt_start ;
2020-05-30 14:46:52 +02:00
if ( flags & ML_APPEND_MARK )
2004-06-13 20:20:40 +00:00
dp_right - > db_index [ 0 ] | = DB_MARKED ;
mch_memmove ( ( char * ) dp_right + dp_right - > db_txt_start ,
line , ( size_t ) len ) ;
+ + line_count_right ;
}
/*
* may move lines from the left / old block to the right / new one .
*/
if ( lines_moved )
{
dp_right - > db_txt_start - = data_moved ;
dp_right - > db_free - = total_moved ;
mch_memmove ( ( char * ) dp_right + dp_right - > db_txt_start ,
( char * ) dp_left + dp_left - > db_txt_start ,
( size_t ) data_moved ) ;
offset = dp_right - > db_txt_start - dp_left - > db_txt_start ;
dp_left - > db_txt_start + = data_moved ;
dp_left - > db_free + = total_moved ;
/*
* update indexes in the new block
*/
for ( to = line_count_right , from = db_idx + 1 ;
from < line_count_left ; + + from , + + to )
dp_right - > db_index [ to ] = dp - > db_index [ from ] + offset ;
line_count_right + = lines_moved ;
line_count_left - = lines_moved ;
}
/*
* May move the new line into the left ( old or new ) block .
*/
if ( in_left )
{
dp_left - > db_txt_start - = len ;
dp_left - > db_free - = len + INDEX_SIZE ;
dp_left - > db_index [ line_count_left ] = dp_left - > db_txt_start ;
2020-05-30 14:46:52 +02:00
if ( flags & ML_APPEND_MARK )
2004-06-13 20:20:40 +00:00
dp_left - > db_index [ line_count_left ] | = DB_MARKED ;
mch_memmove ( ( char * ) dp_left + dp_left - > db_txt_start ,
line , ( size_t ) len ) ;
+ + line_count_left ;
}
2019-12-04 21:57:43 +01:00
if ( db_idx < 0 ) // left block is new
2004-06-13 20:20:40 +00:00
{
lnum_left = lnum + 1 ;
lnum_right = 0 ;
}
2019-12-04 21:57:43 +01:00
else // right block is new
2004-06-13 20:20:40 +00:00
{
lnum_left = 0 ;
if ( in_left )
lnum_right = lnum + 2 ;
else
lnum_right = lnum + 1 ;
}
dp_left - > db_line_count = line_count_left ;
dp_right - > db_line_count = line_count_right ;
/*
* release the two data blocks
* The new one ( hp_new ) already has a correct blocknumber .
* The old one ( hp , in ml_locked ) gets a positive blocknumber if
* we changed it and we are not editing a new file .
*/
if ( lines_moved | | in_left )
buf - > b_ml . ml_flags | = ML_LOCKED_DIRTY ;
2020-05-30 14:46:52 +02:00
if ( ! ( flags & ML_APPEND_NEW ) & & db_idx > = 0 & & in_left )
2004-06-13 20:20:40 +00:00
buf - > b_ml . ml_flags | = ML_LOCKED_POS ;
mf_put ( mfp , hp_new , TRUE , FALSE ) ;
/*
* flush the old data block
* set ml_locked_lineadd to 0 , because the updating of the
* pointer blocks is done below
*/
lineadd = buf - > b_ml . ml_locked_lineadd ;
buf - > b_ml . ml_locked_lineadd = 0 ;
2019-12-04 21:57:43 +01:00
ml_find_line ( buf , ( linenr_T ) 0 , ML_FLUSH ) ; // flush data block
2004-06-13 20:20:40 +00:00
/*
* update pointer blocks for the new data block
*/
for ( stack_idx = buf - > b_ml . ml_stack_top - 1 ; stack_idx > = 0 ;
- - stack_idx )
{
ip = & ( buf - > b_ml . ml_stack [ stack_idx ] ) ;
pb_idx = ip - > ip_index ;
if ( ( hp = mf_get ( mfp , ip - > ip_bnum , 1 ) ) = = NULL )
2018-12-28 23:22:40 +01:00
goto theend ;
2019-12-04 21:57:43 +01:00
pp = ( PTR_BL * ) ( hp - > bh_data ) ; // must be pointer block
2004-06-13 20:20:40 +00:00
if ( pp - > pb_id ! = PTR_ID )
{
2023-05-31 17:12:14 +01:00
iemsg ( e_pointer_block_id_wrong_three ) ;
2004-06-13 20:20:40 +00:00
mf_put ( mfp , hp , FALSE , FALSE ) ;
2018-12-28 23:22:40 +01:00
goto theend ;
2004-06-13 20:20:40 +00:00
}
/*
* TODO : If the pointer block is full and we are adding at the end
* try to insert in front of the next block
*/
2019-12-04 21:57:43 +01:00
// block not full, add one entry
2004-06-13 20:20:40 +00:00
if ( pp - > pb_count < pp - > pb_count_max )
{
if ( pb_idx + 1 < ( int ) pp - > pb_count )
mch_memmove ( & pp - > pb_pointer [ pb_idx + 2 ] ,
& pp - > pb_pointer [ pb_idx + 1 ] ,
( size_t ) ( pp - > pb_count - pb_idx - 1 ) * sizeof ( PTR_EN ) ) ;
+ + pp - > pb_count ;
pp - > pb_pointer [ pb_idx ] . pe_line_count = line_count_left ;
pp - > pb_pointer [ pb_idx ] . pe_bnum = bnum_left ;
pp - > pb_pointer [ pb_idx ] . pe_page_count = page_count_left ;
pp - > pb_pointer [ pb_idx + 1 ] . pe_line_count = line_count_right ;
pp - > pb_pointer [ pb_idx + 1 ] . pe_bnum = bnum_right ;
pp - > pb_pointer [ pb_idx + 1 ] . pe_page_count = page_count_right ;
if ( lnum_left ! = 0 )
pp - > pb_pointer [ pb_idx ] . pe_old_lnum = lnum_left ;
if ( lnum_right ! = 0 )
pp - > pb_pointer [ pb_idx + 1 ] . pe_old_lnum = lnum_right ;
mf_put ( mfp , hp , TRUE , FALSE ) ;
2019-12-04 21:57:43 +01:00
buf - > b_ml . ml_stack_top = stack_idx + 1 ; // truncate stack
2004-06-13 20:20:40 +00:00
if ( lineadd )
{
- - ( buf - > b_ml . ml_stack_top ) ;
2019-12-04 21:57:43 +01:00
// fix line count for rest of blocks in the stack
2004-06-13 20:20:40 +00:00
ml_lineadd ( buf , lineadd ) ;
2019-12-04 21:57:43 +01:00
// fix stack itself
2004-06-13 20:20:40 +00:00
buf - > b_ml . ml_stack [ buf - > b_ml . ml_stack_top ] . ip_high + =
lineadd ;
+ + ( buf - > b_ml . ml_stack_top ) ;
}
/*
* We are finished , break the loop here .
*/
break ;
}
2022-04-07 13:26:34 +01:00
// pointer block full
/*
* split the pointer block
* allocate a new pointer block
* move some of the pointer into the new block
* prepare for updating the parent block
*/
for ( ; ; ) // do this twice when splitting block 1
2004-06-13 20:20:40 +00:00
{
2022-04-07 13:26:34 +01:00
hp_new = ml_new_ptr ( mfp ) ;
if ( hp_new = = NULL ) // TODO: try to fix tree
goto theend ;
pp_new = ( PTR_BL * ) ( hp_new - > bh_data ) ;
2004-06-13 20:20:40 +00:00
2022-04-07 13:26:34 +01:00
if ( hp - > bh_bnum ! = 1 )
break ;
2004-06-13 20:20:40 +00:00
/*
2022-04-07 13:26:34 +01:00
* if block 1 becomes full the tree is given an extra level
* The pointers from block 1 are moved into the new block .
* block 1 is updated to point to the new block
* then continue to split the new block
2004-06-13 20:20:40 +00:00
*/
2022-04-07 13:26:34 +01:00
mch_memmove ( pp_new , pp , ( size_t ) page_size ) ;
pp - > pb_count = 1 ;
pp - > pb_pointer [ 0 ] . pe_bnum = hp_new - > bh_bnum ;
pp - > pb_pointer [ 0 ] . pe_line_count = buf - > b_ml . ml_line_count ;
pp - > pb_pointer [ 0 ] . pe_old_lnum = 1 ;
pp - > pb_pointer [ 0 ] . pe_page_count = 1 ;
mf_put ( mfp , hp , TRUE , FALSE ) ; // release block 1
hp = hp_new ; // new block is to be split
pp = pp_new ;
CHECK ( stack_idx ! = 0 , _ ( " stack_idx should be 0 " ) ) ;
ip - > ip_index = 0 ;
+ + stack_idx ; // do block 1 again later
}
/*
* move the pointers after the current one to the new block
* If there are none , the new entry will be in the new block .
*/
total_moved = pp - > pb_count - pb_idx - 1 ;
if ( total_moved )
{
mch_memmove ( & pp_new - > pb_pointer [ 0 ] ,
2004-06-13 20:20:40 +00:00
& pp - > pb_pointer [ pb_idx + 1 ] ,
( size_t ) ( total_moved ) * sizeof ( PTR_EN ) ) ;
2022-04-07 13:26:34 +01:00
pp_new - > pb_count = total_moved ;
pp - > pb_count - = total_moved - 1 ;
pp - > pb_pointer [ pb_idx + 1 ] . pe_bnum = bnum_right ;
pp - > pb_pointer [ pb_idx + 1 ] . pe_line_count = line_count_right ;
pp - > pb_pointer [ pb_idx + 1 ] . pe_page_count = page_count_right ;
if ( lnum_right )
pp - > pb_pointer [ pb_idx + 1 ] . pe_old_lnum = lnum_right ;
2004-06-13 20:20:40 +00:00
}
2022-04-07 13:26:34 +01:00
else
{
pp_new - > pb_count = 1 ;
pp_new - > pb_pointer [ 0 ] . pe_bnum = bnum_right ;
pp_new - > pb_pointer [ 0 ] . pe_line_count = line_count_right ;
pp_new - > pb_pointer [ 0 ] . pe_page_count = page_count_right ;
pp_new - > pb_pointer [ 0 ] . pe_old_lnum = lnum_right ;
}
pp - > pb_pointer [ pb_idx ] . pe_bnum = bnum_left ;
pp - > pb_pointer [ pb_idx ] . pe_line_count = line_count_left ;
pp - > pb_pointer [ pb_idx ] . pe_page_count = page_count_left ;
if ( lnum_left )
pp - > pb_pointer [ pb_idx ] . pe_old_lnum = lnum_left ;
lnum_left = 0 ;
lnum_right = 0 ;
/*
* recompute line counts
*/
line_count_right = 0 ;
for ( i = 0 ; i < ( int ) pp_new - > pb_count ; + + i )
line_count_right + = pp_new - > pb_pointer [ i ] . pe_line_count ;
line_count_left = 0 ;
for ( i = 0 ; i < ( int ) pp - > pb_count ; + + i )
line_count_left + = pp - > pb_pointer [ i ] . pe_line_count ;
bnum_left = hp - > bh_bnum ;
bnum_right = hp_new - > bh_bnum ;
page_count_left = 1 ;
page_count_right = 1 ;
mf_put ( mfp , hp , TRUE , FALSE ) ;
mf_put ( mfp , hp_new , TRUE , FALSE ) ;
2004-06-13 20:20:40 +00:00
}
/*
* Safety check : fallen out of for loop ?
*/
if ( stack_idx < 0 )
{
2023-05-31 17:12:14 +01:00
iemsg ( e_updated_too_many_blocks ) ;
2019-12-04 21:57:43 +01:00
buf - > b_ml . ml_stack_top = 0 ; // invalidate stack
2004-06-13 20:20:40 +00:00
}
}
# ifdef FEAT_BYTEOFF
2019-12-04 21:57:43 +01:00
// The line was inserted below 'lnum'
2022-08-07 21:48:37 +01:00
ml_updatechunk ( buf , lnum + 1 ,
# ifdef FEAT_PROP_POPUP
( long ) text_len
# else
( long ) len
2022-08-09 21:37:55 +01:00
# endif
2022-08-07 21:48:37 +01:00
, ML_CHNK_ADDLINE ) ;
2004-06-13 20:20:40 +00:00
# endif
2021-08-25 16:40:03 +02:00
2004-06-13 20:20:40 +00:00
# ifdef FEAT_NETBEANS_INTG
2010-05-22 21:34:09 +02:00
if ( netbeans_active ( ) )
2004-06-13 20:20:40 +00:00
{
2024-02-26 21:21:17 +01:00
int line_len = ( int ) STRLEN ( line ) ;
if ( line_len > 0 )
netbeans_inserted ( buf , lnum + 1 , ( colnr_T ) 0 , line , line_len ) ;
netbeans_inserted ( buf , lnum + 1 , ( colnr_T ) line_len , ( char_u * ) " \n " , 1 ) ;
2004-06-13 20:20:40 +00:00
}
# endif
2016-03-11 22:52:15 +01:00
# ifdef FEAT_JOB_CHANNEL
2016-03-06 20:22:25 +01:00
if ( buf - > b_write_to_channel )
channel_write_new_lines ( buf ) ;
# endif
2018-12-28 23:22:40 +01:00
ret = OK ;
2016-03-06 20:22:25 +01:00
2018-12-28 23:22:40 +01:00
theend :
2019-11-30 22:48:27 +01:00
# ifdef FEAT_PROP_POPUP
2018-12-28 23:22:40 +01:00
vim_free ( tofree ) ;
# endif
return ret ;
2004-06-13 20:20:40 +00:00
}
2019-07-15 23:02:14 +02:00
/*
* Flush any pending change and call ml_append_int ( )
*/
static int
ml_append_flush (
buf_T * buf ,
linenr_T lnum , // append after this line (can be 0)
char_u * line , // text of the new line
colnr_T len , // length of line, including NUL, or 0
2020-05-30 14:46:52 +02:00
int flags ) // ML_APPEND_ flags
2019-07-15 23:02:14 +02:00
{
if ( lnum > buf - > b_ml . ml_line_count )
return FAIL ; // lnum out of range
if ( buf - > b_ml . ml_line_lnum ! = 0 )
// This may also invoke ml_append_int().
ml_flush_line ( buf ) ;
# ifdef FEAT_EVAL
// When inserting above recorded changes: flush the changes before changing
// the text. Then flush the cached line, it may become invalid.
may_invoke_listeners ( buf , lnum + 1 , lnum + 1 , 1 ) ;
if ( buf - > b_ml . ml_line_lnum ! = 0 )
ml_flush_line ( buf ) ;
# endif
2020-05-30 14:46:52 +02:00
return ml_append_int ( buf , lnum , line , len , flags ) ;
2019-07-15 23:02:14 +02:00
}
/*
* Append a line after lnum ( may be 0 to insert a line in front of the file ) .
* " line " does not need to be allocated , but can ' t be another line in a
* buffer , unlocking may make it invalid .
*
2020-05-30 14:46:52 +02:00
* " newfile " : TRUE when starting to edit a new file , meaning that pe_old_lnum
2019-07-15 23:02:14 +02:00
* will be set for recovery
* Check : The caller of this function should probably also call
* appended_lines ( ) .
*
* return FAIL for failure , OK otherwise
*/
int
ml_append (
2019-12-04 21:57:43 +01:00
linenr_T lnum , // append after this line (can be 0)
char_u * line , // text of the new line
colnr_T len , // length of new line, including NUL, or 0
int newfile ) // flag, see above
2020-05-30 14:46:52 +02:00
{
return ml_append_flags ( lnum , line , len , newfile ? ML_APPEND_NEW : 0 ) ;
}
int
ml_append_flags (
linenr_T lnum , // append after this line (can be 0)
char_u * line , // text of the new line
colnr_T len , // length of new line, including NUL, or 0
int flags ) // ML_APPEND_ values
2019-07-15 23:02:14 +02:00
{
2019-12-04 21:57:43 +01:00
// When starting up, we might still need to create the memfile
2019-07-15 23:02:14 +02:00
if ( curbuf - > b_ml . ml_mfp = = NULL & & open_buffer ( FALSE , NULL , 0 ) = = FAIL )
return FAIL ;
2020-05-30 14:46:52 +02:00
return ml_append_flush ( curbuf , lnum , line , len , flags ) ;
2019-07-15 23:02:14 +02:00
}
2020-05-30 14:46:52 +02:00
2022-05-16 10:04:51 +01:00
# if defined(FEAT_SPELL) || defined(FEAT_QUICKFIX) || defined(FEAT_PROP_POPUP) \
| | defined ( PROTO )
2019-07-15 23:02:14 +02:00
/*
* Like ml_append ( ) but for an arbitrary buffer . The buffer must already have
* a memline .
*/
int
ml_append_buf (
buf_T * buf ,
2019-12-04 21:57:43 +01:00
linenr_T lnum , // append after this line (can be 0)
char_u * line , // text of the new line
colnr_T len , // length of new line, including NUL, or 0
int newfile ) // flag, see above
2019-07-15 23:02:14 +02:00
{
if ( buf - > b_ml . ml_mfp = = NULL )
return FAIL ;
2020-05-30 14:46:52 +02:00
return ml_append_flush ( buf , lnum , line , len , newfile ? ML_APPEND_NEW : 0 ) ;
2019-07-15 23:02:14 +02:00
}
# endif
2004-06-13 20:20:40 +00:00
/*
2020-07-15 15:30:05 +02:00
* Replace line " lnum " , with buffering , in current buffer .
2004-06-13 20:20:40 +00:00
*
2006-03-09 22:37:52 +00:00
* If " copy " is TRUE , make a copy of the line , otherwise the line has been
2004-06-13 20:20:40 +00:00
* copied to allocated memory already .
2020-07-15 15:30:05 +02:00
* If " copy " is FALSE the " line " may be freed to add text properties !
* Do not use it after calling ml_replace ( ) .
2004-06-13 20:20:40 +00:00
*
* Check : The caller of this function should probably also call
2022-08-14 14:17:45 +01:00
* changed_lines ( ) , unless update_screen ( UPD_NOT_VALID ) is used .
2004-06-13 20:20:40 +00:00
*
* return FAIL for failure , OK otherwise
*/
int
2016-01-30 18:51:09 +01:00
ml_replace ( linenr_T lnum , char_u * line , int copy )
2004-06-13 20:20:40 +00:00
{
2018-12-13 22:20:09 +01:00
colnr_T len = - 1 ;
if ( line ! = NULL )
2018-12-16 14:37:39 +01:00
len = ( colnr_T ) STRLEN ( line ) ;
2019-01-04 15:09:57 +01:00
return ml_replace_len ( lnum , line , len , FALSE , copy ) ;
2018-12-13 22:20:09 +01:00
}
2019-01-04 15:09:57 +01:00
/*
* Replace a line for the current buffer . Like ml_replace ( ) with :
* " len_arg " is the length of the text , excluding NUL .
* If " has_props " is TRUE then " line_arg " includes the text properties and
2024-03-13 20:42:26 +01:00
* " len_arg " includes the NUL of the text and text properties .
2022-06-30 22:13:59 +01:00
* When " copy " is TRUE copy the text into allocated memory , otherwise
* " line_arg " must be allocated and will be consumed here .
2019-01-04 15:09:57 +01:00
*/
2018-12-13 22:20:09 +01:00
int
2019-01-04 15:09:57 +01:00
ml_replace_len (
linenr_T lnum ,
char_u * line_arg ,
colnr_T len_arg ,
int has_props ,
int copy )
2018-12-13 22:20:09 +01:00
{
char_u * line = line_arg ;
colnr_T len = len_arg ;
2019-12-04 21:57:43 +01:00
if ( line = = NULL ) // just checking...
2004-06-13 20:20:40 +00:00
return FAIL ;
2019-12-04 21:57:43 +01:00
// When starting up, we might still need to create the memfile
2010-07-24 20:27:03 +02:00
if ( curbuf - > b_ml . ml_mfp = = NULL & & open_buffer ( FALSE , NULL , 0 ) = = FAIL )
2004-06-13 20:20:40 +00:00
return FAIL ;
2019-01-04 15:09:57 +01:00
if ( ! has_props )
+ + len ; // include the NUL after the text
if ( copy )
{
// copy the line to allocated memory
2019-11-30 22:48:27 +01:00
# ifdef FEAT_PROP_POPUP
2019-01-04 15:09:57 +01:00
if ( has_props )
line = vim_memsave ( line , len ) ;
else
# endif
line = vim_strnsave ( line , len - 1 ) ;
if ( line = = NULL )
return FAIL ;
}
2004-06-13 20:20:40 +00:00
# ifdef FEAT_NETBEANS_INTG
2010-05-22 21:34:09 +02:00
if ( netbeans_active ( ) )
2004-06-13 20:20:40 +00:00
{
2024-02-26 21:21:17 +01:00
netbeans_removed ( curbuf , lnum , 0 , ( long ) ml_get_len ( lnum ) ) ;
2006-04-17 22:14:47 +00:00
netbeans_inserted ( curbuf , lnum , 0 , line , ( int ) STRLEN ( line ) ) ;
2004-06-13 20:20:40 +00:00
}
# endif
2018-12-13 22:20:09 +01:00
if ( curbuf - > b_ml . ml_line_lnum ! = lnum )
{
// another line is buffered, flush it
ml_flush_line ( curbuf ) ;
2019-11-30 22:48:27 +01:00
# ifdef FEAT_PROP_POPUP
2019-01-04 15:09:57 +01:00
if ( curbuf - > b_has_textprop & & ! has_props )
2018-12-13 22:20:09 +01:00
// Need to fetch the old line to copy over any text properties.
ml_get_buf ( curbuf , lnum , TRUE ) ;
# endif
}
2019-11-30 22:48:27 +01:00
# ifdef FEAT_PROP_POPUP
2019-01-04 15:09:57 +01:00
if ( curbuf - > b_has_textprop & & ! has_props )
2018-12-13 22:20:09 +01:00
{
size_t oldtextlen = STRLEN ( curbuf - > b_ml . ml_line_ptr ) + 1 ;
if ( oldtextlen < ( size_t ) curbuf - > b_ml . ml_line_len )
{
char_u * newline ;
size_t textproplen = curbuf - > b_ml . ml_line_len - oldtextlen ;
// Need to copy over text properties, stored after the text.
2019-01-04 15:09:57 +01:00
newline = alloc ( len + ( int ) textproplen ) ;
2018-12-13 22:20:09 +01:00
if ( newline ! = NULL )
{
2019-01-04 15:09:57 +01:00
mch_memmove ( newline , line , len ) ;
2019-05-18 13:05:18 +02:00
mch_memmove ( newline + len , curbuf - > b_ml . ml_line_ptr
+ oldtextlen , textproplen ) ;
2018-12-13 22:20:09 +01:00
vim_free ( line ) ;
line = newline ;
2018-12-16 14:37:39 +01:00
len + = ( colnr_T ) textproplen ;
2018-12-13 22:20:09 +01:00
}
}
}
# endif
2022-06-30 22:13:59 +01:00
if ( curbuf - > b_ml . ml_flags & ( ML_LINE_DIRTY | ML_ALLOCATED ) )
vim_free ( curbuf - > b_ml . ml_line_ptr ) ; // free allocated line
2018-12-13 22:20:09 +01:00
2004-06-13 20:20:40 +00:00
curbuf - > b_ml . ml_line_ptr = line ;
2019-01-04 15:09:57 +01:00
curbuf - > b_ml . ml_line_len = len ;
2024-03-12 21:50:32 +01:00
curbuf - > b_ml . ml_line_textlen = ! has_props ? len_arg + 1 : 0 ;
2004-06-13 20:20:40 +00:00
curbuf - > b_ml . ml_line_lnum = lnum ;
curbuf - > b_ml . ml_flags = ( curbuf - > b_ml . ml_flags | ML_LINE_DIRTY ) & ~ ML_EMPTY ;
return OK ;
}
2019-11-30 22:48:27 +01:00
# ifdef FEAT_PROP_POPUP
2018-12-28 21:59:29 +01:00
/*
* Adjust text properties in line " lnum " for a deleted line .
2022-05-15 22:24:55 +01:00
* When " above " is true this is the line above the deleted line , otherwise this
* is the line below the deleted line .
* " del_props[del_props_len] " are the properties of the deleted line .
2018-12-28 21:59:29 +01:00
*/
static void
adjust_text_props_for_delete (
buf_T * buf ,
linenr_T lnum ,
char_u * del_props ,
int del_props_len ,
int above )
{
int did_get_line = FALSE ;
int done_del ;
int done_this ;
textprop_T prop_del ;
bhdr_T * hp ;
DATA_BL * dp ;
int idx ;
int line_start ;
long line_size ;
2023-06-26 18:48:09 +01:00
int this_props_len = 0 ;
2018-12-28 21:59:29 +01:00
char_u * text ;
size_t textlen ;
int found ;
for ( done_del = 0 ; done_del < del_props_len ; done_del + = sizeof ( textprop_T ) )
{
mch_memmove ( & prop_del , del_props + done_del , sizeof ( textprop_T ) ) ;
if ( ( above & & ( prop_del . tp_flags & TP_FLAG_CONT_PREV )
& & ! ( prop_del . tp_flags & TP_FLAG_CONT_NEXT ) )
| | ( ! above & & ( prop_del . tp_flags & TP_FLAG_CONT_NEXT )
& & ! ( prop_del . tp_flags & TP_FLAG_CONT_PREV ) ) )
{
if ( ! did_get_line )
{
did_get_line = TRUE ;
if ( ( hp = ml_find_line ( buf , lnum , ML_FIND ) ) = = NULL )
return ;
dp = ( DATA_BL * ) ( hp - > bh_data ) ;
idx = lnum - buf - > b_ml . ml_locked_low ;
line_start = ( ( dp - > db_index [ idx ] ) & DB_INDEX_MASK ) ;
if ( idx = = 0 ) // first line in block, text at the end
line_size = dp - > db_txt_end - line_start ;
else
2020-05-30 15:32:02 +02:00
line_size = ( ( dp - > db_index [ idx - 1 ] ) & DB_INDEX_MASK )
- line_start ;
2018-12-28 21:59:29 +01:00
text = ( char_u * ) dp + line_start ;
textlen = STRLEN ( text ) + 1 ;
if ( ( long ) textlen > = line_size )
{
if ( above )
internal_error ( " no text property above deleted line " ) ;
else
internal_error ( " no text property below deleted line " ) ;
return ;
}
2019-01-03 21:55:32 +01:00
this_props_len = line_size - ( int ) textlen ;
2018-12-28 21:59:29 +01:00
}
found = FALSE ;
2020-05-30 15:32:02 +02:00
for ( done_this = 0 ; done_this < this_props_len ;
done_this + = sizeof ( textprop_T ) )
2018-12-28 21:59:29 +01:00
{
2020-05-30 15:32:02 +02:00
int flag = above ? TP_FLAG_CONT_NEXT
: TP_FLAG_CONT_PREV ;
textprop_T prop_this ;
2022-05-15 22:24:55 +01:00
mch_memmove ( & prop_this , text + textlen + done_this ,
2020-05-30 15:32:02 +02:00
sizeof ( textprop_T ) ) ;
if ( ( prop_this . tp_flags & flag )
& & prop_del . tp_id = = prop_this . tp_id
2018-12-28 21:59:29 +01:00
& & prop_del . tp_type = = prop_this . tp_type )
{
found = TRUE ;
2020-05-30 15:32:02 +02:00
prop_this . tp_flags & = ~ flag ;
2022-05-15 22:24:55 +01:00
mch_memmove ( text + textlen + done_this , & prop_this ,
2020-05-30 15:32:02 +02:00
sizeof ( textprop_T ) ) ;
break ;
2018-12-28 21:59:29 +01:00
}
}
if ( ! found )
{
if ( above )
internal_error ( " text property above deleted line not found " ) ;
else
internal_error ( " text property below deleted line not found " ) ;
}
buf - > b_ml . ml_flags | = ( ML_LOCKED_DIRTY | ML_LOCKED_POS ) ;
}
}
}
# endif
2004-06-13 20:20:40 +00:00
/*
2017-09-16 20:54:51 +02:00
* Delete line " lnum " in the current buffer .
2020-05-30 14:46:52 +02:00
* When " flags " has ML_DEL_MESSAGE may give a " No lines in buffer " message .
* When " flags " has ML_DEL_UNDO this is called from undo .
2004-06-13 20:20:40 +00:00
*
* return FAIL for failure , OK otherwise
*/
static int
2020-05-30 14:46:52 +02:00
ml_delete_int ( buf_T * buf , linenr_T lnum , int flags )
2004-06-13 20:20:40 +00:00
{
bhdr_T * hp ;
memfile_T * mfp ;
DATA_BL * dp ;
PTR_BL * pp ;
infoptr_T * ip ;
2019-12-04 21:57:43 +01:00
int count ; // number of entries in block
2004-06-13 20:20:40 +00:00
int idx ;
int stack_idx ;
int text_start ;
int line_start ;
long line_size ;
int i ;
2018-12-28 21:59:29 +01:00
int ret = FAIL ;
2019-11-30 22:48:27 +01:00
# ifdef FEAT_PROP_POPUP
2018-12-28 21:59:29 +01:00
char_u * textprop_save = NULL ;
2022-08-09 21:37:55 +01:00
long textprop_len = 0 ;
2018-12-28 21:59:29 +01:00
# endif
2004-06-13 20:20:40 +00:00
if ( lowest_marked & & lowest_marked > lnum )
lowest_marked - - ;
/*
* If the file becomes empty the last line is replaced by an empty line .
*/
2019-12-04 21:57:43 +01:00
if ( buf - > b_ml . ml_line_count = = 1 ) // file becomes empty
2004-06-13 20:20:40 +00:00
{
2020-05-30 14:46:52 +02:00
if ( ( flags & ML_DEL_MESSAGE )
2004-06-13 20:20:40 +00:00
# ifdef FEAT_NETBEANS_INTG
& & ! netbeansSuppressNoLines
# endif
)
2006-02-21 22:12:05 +00:00
set_keep_msg ( ( char_u * ) _ ( no_lines_msg ) , 0 ) ;
2019-12-04 21:57:43 +01:00
// FEAT_BYTEOFF already handled in there, don't worry 'bout it below
2004-06-13 20:20:40 +00:00
i = ml_replace ( ( linenr_T ) 1 , ( char_u * ) " " , TRUE ) ;
buf - > b_ml . ml_flags | = ML_EMPTY ;
return i ;
}
/*
2018-12-28 21:59:29 +01:00
* Find the data block containing the line .
* This also fills the stack with the blocks from the root to the data block .
* This also releases any locked block . .
2004-06-13 20:20:40 +00:00
*/
mfp = buf - > b_ml . ml_mfp ;
if ( mfp = = NULL )
return FAIL ;
if ( ( hp = ml_find_line ( buf , lnum , ML_DELETE ) ) = = NULL )
return FAIL ;
dp = ( DATA_BL * ) ( hp - > bh_data ) ;
2019-12-04 21:57:43 +01:00
// compute line count before the delete
2004-06-13 20:20:40 +00:00
count = ( long ) ( buf - > b_ml . ml_locked_high )
- ( long ) ( buf - > b_ml . ml_locked_low ) + 2 ;
idx = lnum - buf - > b_ml . ml_locked_low ;
- - buf - > b_ml . ml_line_count ;
line_start = ( ( dp - > db_index [ idx ] ) & DB_INDEX_MASK ) ;
2019-12-04 21:57:43 +01:00
if ( idx = = 0 ) // first line in block, text at the end
2004-06-13 20:20:40 +00:00
line_size = dp - > db_txt_end - line_start ;
else
line_size = ( ( dp - > db_index [ idx - 1 ] ) & DB_INDEX_MASK ) - line_start ;
# ifdef FEAT_NETBEANS_INTG
2010-05-22 21:34:09 +02:00
if ( netbeans_active ( ) )
2022-01-28 15:28:04 +00:00
netbeans_removed ( buf , lnum , 0 , line_size ) ;
2004-06-13 20:20:40 +00:00
# endif
2019-11-30 22:48:27 +01:00
# ifdef FEAT_PROP_POPUP
2022-08-09 21:37:55 +01:00
// If there are text properties compute their byte length.
// if needed make a copy, so that we can update properties in preceding and
// following lines.
if ( buf - > b_has_textprop )
2018-12-28 21:59:29 +01:00
{
size_t textlen = STRLEN ( ( char_u * ) dp + line_start ) + 1 ;
2022-08-09 21:37:55 +01:00
textprop_len = line_size - ( long ) textlen ;
if ( ! ( flags & ( ML_DEL_UNDO | ML_DEL_NOPROP ) ) & & textprop_len > 0 )
2018-12-28 21:59:29 +01:00
textprop_save = vim_memsave ( ( char_u * ) dp + line_start + textlen ,
2022-08-09 21:37:55 +01:00
textprop_len ) ;
2018-12-28 21:59:29 +01:00
}
# endif
2004-06-13 20:20:40 +00:00
/*
* special case : If there is only one line in the data block it becomes empty .
* Then we have to remove the entry , pointing to this data block , from the
* pointer block . If this pointer block also becomes empty , we go up another
* block , and so on , up to the root if necessary .
* The line counts in the pointer blocks have already been adjusted by
* ml_find_line ( ) .
*/
if ( count = = 1 )
{
2019-12-04 21:57:43 +01:00
mf_free ( mfp , hp ) ; // free the data block
2004-06-13 20:20:40 +00:00
buf - > b_ml . ml_locked = NULL ;
2011-05-10 16:41:25 +02:00
for ( stack_idx = buf - > b_ml . ml_stack_top - 1 ; stack_idx > = 0 ;
- - stack_idx )
2004-06-13 20:20:40 +00:00
{
2019-12-04 21:57:43 +01:00
buf - > b_ml . ml_stack_top = 0 ; // stack is invalid when failing
2004-06-13 20:20:40 +00:00
ip = & ( buf - > b_ml . ml_stack [ stack_idx ] ) ;
idx = ip - > ip_index ;
if ( ( hp = mf_get ( mfp , ip - > ip_bnum , 1 ) ) = = NULL )
2018-12-28 21:59:29 +01:00
goto theend ;
2019-12-04 21:57:43 +01:00
pp = ( PTR_BL * ) ( hp - > bh_data ) ; // must be pointer block
2004-06-13 20:20:40 +00:00
if ( pp - > pb_id ! = PTR_ID )
{
2023-05-31 17:12:14 +01:00
iemsg ( e_pointer_block_id_wrong_four ) ;
2004-06-13 20:20:40 +00:00
mf_put ( mfp , hp , FALSE , FALSE ) ;
2018-12-28 21:59:29 +01:00
goto theend ;
2004-06-13 20:20:40 +00:00
}
count = - - ( pp - > pb_count ) ;
2019-12-04 21:57:43 +01:00
if ( count = = 0 ) // the pointer block becomes empty!
2004-06-13 20:20:40 +00:00
mf_free ( mfp , hp ) ;
else
{
2019-12-04 21:57:43 +01:00
if ( count ! = idx ) // move entries after the deleted one
2004-06-13 20:20:40 +00:00
mch_memmove ( & pp - > pb_pointer [ idx ] , & pp - > pb_pointer [ idx + 1 ] ,
( size_t ) ( count - idx ) * sizeof ( PTR_EN ) ) ;
mf_put ( mfp , hp , TRUE , FALSE ) ;
2019-12-04 21:57:43 +01:00
buf - > b_ml . ml_stack_top = stack_idx ; // truncate stack
// fix line count for rest of blocks in the stack
2007-05-06 14:25:46 +00:00
if ( buf - > b_ml . ml_locked_lineadd ! = 0 )
2004-06-13 20:20:40 +00:00
{
ml_lineadd ( buf , buf - > b_ml . ml_locked_lineadd ) ;
buf - > b_ml . ml_stack [ buf - > b_ml . ml_stack_top ] . ip_high + =
2007-05-06 14:25:46 +00:00
buf - > b_ml . ml_locked_lineadd ;
2004-06-13 20:20:40 +00:00
}
+ + ( buf - > b_ml . ml_stack_top ) ;
break ;
}
}
CHECK ( stack_idx < 0 , _ ( " deleted block 1? " ) ) ;
}
else
{
/*
* delete the text by moving the next lines forwards
*/
text_start = dp - > db_txt_start ;
mch_memmove ( ( char * ) dp + text_start + line_size ,
( char * ) dp + text_start , ( size_t ) ( line_start - text_start ) ) ;
/*
* delete the index by moving the next indexes backwards
* Adjust the indexes for the text movement .
*/
for ( i = idx ; i < count - 1 ; + + i )
dp - > db_index [ i ] = dp - > db_index [ i + 1 ] + line_size ;
dp - > db_free + = line_size + INDEX_SIZE ;
dp - > db_txt_start + = line_size ;
- - ( dp - > db_line_count ) ;
/*
* mark the block dirty and make sure it is in the file ( for recovery )
*/
buf - > b_ml . ml_flags | = ( ML_LOCKED_DIRTY | ML_LOCKED_POS ) ;
}
# ifdef FEAT_BYTEOFF
2021-08-25 16:40:03 +02:00
ml_updatechunk ( buf , lnum , line_size
# ifdef FEAT_PROP_POPUP
2022-08-09 21:37:55 +01:00
- textprop_len
2021-08-25 16:40:03 +02:00
# endif
2022-08-09 21:37:55 +01:00
, ML_CHNK_DELLINE ) ;
2004-06-13 20:20:40 +00:00
# endif
2018-12-28 21:59:29 +01:00
ret = OK ;
theend :
2019-11-30 22:48:27 +01:00
# ifdef FEAT_PROP_POPUP
2018-12-28 21:59:29 +01:00
if ( textprop_save ! = NULL )
{
// Adjust text properties in the line above and below.
if ( lnum > 1 )
2021-06-27 13:04:00 +02:00
adjust_text_props_for_delete ( buf , lnum - 1 , textprop_save ,
2022-08-09 21:37:55 +01:00
( int ) textprop_len , TRUE ) ;
2018-12-28 21:59:29 +01:00
if ( lnum < = buf - > b_ml . ml_line_count )
2021-06-27 13:04:00 +02:00
adjust_text_props_for_delete ( buf , lnum , textprop_save ,
2022-08-09 21:37:55 +01:00
( int ) textprop_len , FALSE ) ;
2018-12-28 21:59:29 +01:00
}
vim_free ( textprop_save ) ;
# endif
return ret ;
2004-06-13 20:20:40 +00:00
}
2020-05-30 14:46:52 +02:00
/*
* Delete line " lnum " in the current buffer .
* When " message " is TRUE may give a " No lines in buffer " message .
*
* Check : The caller of this function should probably also call
* deleted_lines ( ) after this .
*
* return FAIL for failure , OK otherwise
*/
int
2020-05-30 20:30:46 +02:00
ml_delete ( linenr_T lnum )
2020-05-30 14:46:52 +02:00
{
2020-05-30 20:30:46 +02:00
return ml_delete_flags ( lnum , 0 ) ;
2020-05-30 14:46:52 +02:00
}
/*
* Like ml_delete ( ) but using flags ( see ml_delete_int ( ) ) .
*/
int
ml_delete_flags ( linenr_T lnum , int flags )
{
ml_flush_line ( curbuf ) ;
if ( lnum < 1 | | lnum > curbuf - > b_ml . ml_line_count )
return FAIL ;
# ifdef FEAT_EVAL
// When inserting above recorded changes: flush the changes before changing
// the text.
may_invoke_listeners ( curbuf , lnum , lnum + 1 , - 1 ) ;
# endif
return ml_delete_int ( curbuf , lnum , flags ) ;
}
2004-06-13 20:20:40 +00:00
/*
2018-12-28 21:59:29 +01:00
* set the DB_MARKED flag for line ' lnum '
2004-06-13 20:20:40 +00:00
*/
void
2016-01-30 18:51:09 +01:00
ml_setmarked ( linenr_T lnum )
2004-06-13 20:20:40 +00:00
{
bhdr_T * hp ;
DATA_BL * dp ;
2019-12-04 21:57:43 +01:00
// invalid line number
2004-06-13 20:20:40 +00:00
if ( lnum < 1 | | lnum > curbuf - > b_ml . ml_line_count
| | curbuf - > b_ml . ml_mfp = = NULL )
2019-12-04 21:57:43 +01:00
return ; // give error message?
2004-06-13 20:20:40 +00:00
if ( lowest_marked = = 0 | | lowest_marked > lnum )
lowest_marked = lnum ;
/*
* find the data block containing the line
* This also fills the stack with the blocks from the root to the data block
* This also releases any locked block .
*/
if ( ( hp = ml_find_line ( curbuf , lnum , ML_FIND ) ) = = NULL )
2019-12-04 21:57:43 +01:00
return ; // give error message?
2004-06-13 20:20:40 +00:00
dp = ( DATA_BL * ) ( hp - > bh_data ) ;
dp - > db_index [ lnum - curbuf - > b_ml . ml_locked_low ] | = DB_MARKED ;
curbuf - > b_ml . ml_flags | = ML_LOCKED_DIRTY ;
}
/*
2018-12-28 21:59:29 +01:00
* find the first line with its DB_MARKED flag set
2004-06-13 20:20:40 +00:00
*/
linenr_T
2016-01-30 18:51:09 +01:00
ml_firstmarked ( void )
2004-06-13 20:20:40 +00:00
{
bhdr_T * hp ;
DATA_BL * dp ;
linenr_T lnum ;
int i ;
if ( curbuf - > b_ml . ml_mfp = = NULL )
return ( linenr_T ) 0 ;
/*
* The search starts with lowest_marked line . This is the last line where
* a mark was found , adjusted by inserting / deleting lines .
*/
for ( lnum = lowest_marked ; lnum < = curbuf - > b_ml . ml_line_count ; )
{
/*
* Find the data block containing the line .
* This also fills the stack with the blocks from the root to the data
* block This also releases any locked block .
*/
if ( ( hp = ml_find_line ( curbuf , lnum , ML_FIND ) ) = = NULL )
2019-12-04 21:57:43 +01:00
return ( linenr_T ) 0 ; // give error message?
2004-06-13 20:20:40 +00:00
dp = ( DATA_BL * ) ( hp - > bh_data ) ;
for ( i = lnum - curbuf - > b_ml . ml_locked_low ;
lnum < = curbuf - > b_ml . ml_locked_high ; + + i , + + lnum )
if ( ( dp - > db_index [ i ] ) & DB_MARKED )
{
( dp - > db_index [ i ] ) & = DB_INDEX_MASK ;
curbuf - > b_ml . ml_flags | = ML_LOCKED_DIRTY ;
lowest_marked = lnum + 1 ;
return lnum ;
}
}
return ( linenr_T ) 0 ;
}
/*
* clear all DB_MARKED flags
*/
void
2016-01-30 18:51:09 +01:00
ml_clearmarked ( void )
2004-06-13 20:20:40 +00:00
{
bhdr_T * hp ;
DATA_BL * dp ;
linenr_T lnum ;
int i ;
2019-12-04 21:57:43 +01:00
if ( curbuf - > b_ml . ml_mfp = = NULL ) // nothing to do
2004-06-13 20:20:40 +00:00
return ;
/*
* The search starts with line lowest_marked .
*/
for ( lnum = lowest_marked ; lnum < = curbuf - > b_ml . ml_line_count ; )
{
/*
* Find the data block containing the line .
* This also fills the stack with the blocks from the root to the data
* block and releases any locked block .
*/
if ( ( hp = ml_find_line ( curbuf , lnum , ML_FIND ) ) = = NULL )
2019-12-04 21:57:43 +01:00
return ; // give error message?
2004-06-13 20:20:40 +00:00
dp = ( DATA_BL * ) ( hp - > bh_data ) ;
for ( i = lnum - curbuf - > b_ml . ml_locked_low ;
lnum < = curbuf - > b_ml . ml_locked_high ; + + i , + + lnum )
if ( ( dp - > db_index [ i ] ) & DB_MARKED )
{
( dp - > db_index [ i ] ) & = DB_INDEX_MASK ;
curbuf - > b_ml . ml_flags | = ML_LOCKED_DIRTY ;
}
}
lowest_marked = 0 ;
}
/*
* flush ml_line if necessary
*/
static void
2016-01-30 18:51:09 +01:00
ml_flush_line ( buf_T * buf )
2004-06-13 20:20:40 +00:00
{
bhdr_T * hp ;
DATA_BL * dp ;
linenr_T lnum ;
char_u * new_line ;
char_u * old_line ;
colnr_T new_len ;
int old_len ;
int extra ;
int idx ;
int start ;
int count ;
int i ;
2010-02-11 18:54:43 +01:00
static int entered = FALSE ;
2004-06-13 20:20:40 +00:00
if ( buf - > b_ml . ml_line_lnum = = 0 | | buf - > b_ml . ml_mfp = = NULL )
2019-12-04 21:57:43 +01:00
return ; // nothing to do
2004-06-13 20:20:40 +00:00
if ( buf - > b_ml . ml_flags & ML_LINE_DIRTY )
{
2019-12-04 21:57:43 +01:00
// This code doesn't work recursively, but Netbeans may call back here
// when obtaining the cursor position.
2010-02-11 18:54:43 +01:00
if ( entered )
return ;
entered = TRUE ;
2004-06-13 20:20:40 +00:00
lnum = buf - > b_ml . ml_line_lnum ;
new_line = buf - > b_ml . ml_line_ptr ;
hp = ml_find_line ( buf , lnum , ML_FIND ) ;
if ( hp = = NULL )
2023-05-31 17:12:14 +01:00
siemsg ( e_cannot_find_line_nr , lnum ) ;
2004-06-13 20:20:40 +00:00
else
{
dp = ( DATA_BL * ) ( hp - > bh_data ) ;
idx = lnum - buf - > b_ml . ml_locked_low ;
start = ( ( dp - > db_index [ idx ] ) & DB_INDEX_MASK ) ;
old_line = ( char_u * ) dp + start ;
2019-12-04 21:57:43 +01:00
if ( idx = = 0 ) // line is last in block
2004-06-13 20:20:40 +00:00
old_len = dp - > db_txt_end - start ;
2019-12-04 21:57:43 +01:00
else // text of previous line follows
2004-06-13 20:20:40 +00:00
old_len = ( dp - > db_index [ idx - 1 ] & DB_INDEX_MASK ) - start ;
2018-12-13 22:20:09 +01:00
new_len = buf - > b_ml . ml_line_len ;
2019-12-04 21:57:43 +01:00
extra = new_len - old_len ; // negative if lines gets smaller
2004-06-13 20:20:40 +00:00
/*
* if new line fits in data block , replace directly
*/
if ( ( int ) dp - > db_free > = extra )
{
2021-08-15 22:18:04 +02:00
# if defined(FEAT_BYTEOFF) && defined(FEAT_PROP_POPUP)
2021-08-15 14:28:40 +02:00
int old_prop_len = 0 ;
2022-05-24 21:26:37 +01:00
if ( buf - > b_has_textprop )
old_prop_len = old_len - ( int ) STRLEN ( old_line ) - 1 ;
2021-08-15 14:28:40 +02:00
# endif
2019-12-04 21:57:43 +01:00
// if the length changes and there are following lines
2004-06-13 20:20:40 +00:00
count = buf - > b_ml . ml_locked_high - buf - > b_ml . ml_locked_low + 1 ;
if ( extra ! = 0 & & idx < count - 1 )
{
2019-12-04 21:57:43 +01:00
// move text of following lines
2004-06-13 20:20:40 +00:00
mch_memmove ( ( char * ) dp + dp - > db_txt_start - extra ,
( char * ) dp + dp - > db_txt_start ,
( size_t ) ( start - dp - > db_txt_start ) ) ;
2019-12-04 21:57:43 +01:00
// adjust pointers of this and following lines
2004-06-13 20:20:40 +00:00
for ( i = idx + 1 ; i < count ; + + i )
dp - > db_index [ i ] - = extra ;
}
dp - > db_index [ idx ] - = extra ;
2019-12-04 21:57:43 +01:00
// adjust free space
2004-06-13 20:20:40 +00:00
dp - > db_free - = extra ;
dp - > db_txt_start - = extra ;
2019-12-04 21:57:43 +01:00
// copy new line into the data block
2004-06-13 20:20:40 +00:00
mch_memmove ( old_line - extra , new_line , ( size_t ) new_len ) ;
buf - > b_ml . ml_flags | = ( ML_LOCKED_DIRTY | ML_LOCKED_POS ) ;
2021-08-15 22:18:04 +02:00
# if defined(FEAT_BYTEOFF) && defined(FEAT_PROP_POPUP)
2019-12-04 21:57:43 +01:00
// The else case is already covered by the insert and delete
2021-08-15 14:28:40 +02:00
if ( buf - > b_has_textprop )
{
// Do not count the size of any text properties.
extra + = old_prop_len ;
2021-08-16 21:15:32 +02:00
extra - = new_len - ( int ) STRLEN ( new_line ) - 1 ;
2021-08-15 14:28:40 +02:00
}
if ( extra ! = 0 )
ml_updatechunk ( buf , lnum , ( long ) extra , ML_CHNK_UPDLINE ) ;
2004-06-13 20:20:40 +00:00
# endif
}
else
{
/*
* Cannot do it in one data block : Delete and append .
* Append first , because ml_delete_int ( ) cannot delete the
* last line in a buffer , which causes trouble for a buffer
* that has only one line .
* Don ' t forget to copy the mark !
*/
2019-12-04 21:57:43 +01:00
// How about handling errors???
2020-05-30 14:46:52 +02:00
( void ) ml_append_int ( buf , lnum , new_line , new_len ,
2021-05-26 22:32:10 +02:00
( ( dp - > db_index [ idx ] & DB_MARKED ) ? ML_APPEND_MARK : 0 )
# ifdef FEAT_PROP_POPUP
| ML_APPEND_NOPROP
# endif
) ;
2021-06-27 13:04:00 +02:00
( void ) ml_delete_int ( buf , lnum , ML_DEL_NOPROP ) ;
2004-06-13 20:20:40 +00:00
}
}
vim_free ( new_line ) ;
2010-02-11 18:54:43 +01:00
entered = FALSE ;
2004-06-13 20:20:40 +00:00
}
2022-06-30 22:13:59 +01:00
else if ( buf - > b_ml . ml_flags & ML_ALLOCATED )
vim_free ( buf - > b_ml . ml_line_ptr ) ;
2004-06-13 20:20:40 +00:00
2022-06-30 22:13:59 +01:00
buf - > b_ml . ml_flags & = ~ ( ML_LINE_DIRTY | ML_ALLOCATED ) ;
2004-06-13 20:20:40 +00:00
buf - > b_ml . ml_line_lnum = 0 ;
}
/*
* create a new , empty , data block
*/
static bhdr_T *
2016-01-30 18:51:09 +01:00
ml_new_data ( memfile_T * mfp , int negative , int page_count )
2004-06-13 20:20:40 +00:00
{
bhdr_T * hp ;
DATA_BL * dp ;
if ( ( hp = mf_new ( mfp , negative , page_count ) ) = = NULL )
return NULL ;
dp = ( DATA_BL * ) ( hp - > bh_data ) ;
dp - > db_id = DATA_ID ;
dp - > db_txt_start = dp - > db_txt_end = page_count * mfp - > mf_page_size ;
dp - > db_free = dp - > db_txt_start - HEADER_SIZE ;
dp - > db_line_count = 0 ;
return hp ;
}
/*
* create a new , empty , pointer block
*/
static bhdr_T *
2016-01-30 18:51:09 +01:00
ml_new_ptr ( memfile_T * mfp )
2004-06-13 20:20:40 +00:00
{
bhdr_T * hp ;
PTR_BL * pp ;
if ( ( hp = mf_new ( mfp , FALSE , 1 ) ) = = NULL )
return NULL ;
pp = ( PTR_BL * ) ( hp - > bh_data ) ;
pp - > pb_id = PTR_ID ;
pp - > pb_count = 0 ;
2023-04-22 21:14:26 +01:00
pp - > pb_count_max = PB_COUNT_MAX ( mfp ) ;
2004-06-13 20:20:40 +00:00
return hp ;
}
/*
2018-12-28 21:59:29 +01:00
* Lookup line ' lnum ' in a memline .
2004-06-13 20:20:40 +00:00
*
* action : if ML_DELETE or ML_INSERT the line count is updated while searching
* if ML_FLUSH only flush a locked block
* if ML_FIND just find the line
*
* If the block was found it is locked and put in ml_locked .
* The stack is updated to lead to the locked block . The ip_high field in
* the stack is updated to reflect the last line in the block AFTER the
* insert or delete , also if the pointer block has not been updated yet . But
2007-05-06 14:25:46 +00:00
* if ml_locked ! = NULL ml_locked_lineadd must be added to ip_high .
2004-06-13 20:20:40 +00:00
*
* return : NULL for failure , pointer to block header otherwise
*/
static bhdr_T *
2016-01-30 18:51:09 +01:00
ml_find_line ( buf_T * buf , linenr_T lnum , int action )
2004-06-13 20:20:40 +00:00
{
DATA_BL * dp ;
PTR_BL * pp ;
infoptr_T * ip ;
bhdr_T * hp ;
memfile_T * mfp ;
linenr_T t ;
blocknr_T bnum , bnum2 ;
int dirty ;
linenr_T low , high ;
int top ;
int page_count ;
int idx ;
mfp = buf - > b_ml . ml_mfp ;
/*
* If there is a locked block check if the wanted line is in it .
* If not , flush and release the locked block .
* Don ' t do this for ML_INSERT_SAME , because the stack need to be updated .
* Don ' t do this for ML_FLUSH , because we want to flush the locked block .
2007-02-07 02:41:57 +00:00
* Don ' t do this when ' swapfile ' is reset , we want to load all the blocks .
2004-06-13 20:20:40 +00:00
*/
if ( buf - > b_ml . ml_locked )
{
2007-02-07 02:41:57 +00:00
if ( ML_SIMPLE ( action )
& & buf - > b_ml . ml_locked_low < = lnum
& & buf - > b_ml . ml_locked_high > = lnum
& & ! mf_dont_release )
2004-06-13 20:20:40 +00:00
{
2019-12-04 21:57:43 +01:00
// remember to update pointer blocks and stack later
2004-06-13 20:20:40 +00:00
if ( action = = ML_INSERT )
{
+ + ( buf - > b_ml . ml_locked_lineadd ) ;
+ + ( buf - > b_ml . ml_locked_high ) ;
}
else if ( action = = ML_DELETE )
{
- - ( buf - > b_ml . ml_locked_lineadd ) ;
- - ( buf - > b_ml . ml_locked_high ) ;
}
return ( buf - > b_ml . ml_locked ) ;
}
mf_put ( mfp , buf - > b_ml . ml_locked , buf - > b_ml . ml_flags & ML_LOCKED_DIRTY ,
buf - > b_ml . ml_flags & ML_LOCKED_POS ) ;
buf - > b_ml . ml_locked = NULL ;
2007-05-06 14:25:46 +00:00
/*
* If lines have been added or deleted in the locked block , need to
* update the line count in pointer blocks .
*/
if ( buf - > b_ml . ml_locked_lineadd ! = 0 )
2004-06-13 20:20:40 +00:00
ml_lineadd ( buf , buf - > b_ml . ml_locked_lineadd ) ;
}
2019-12-04 21:57:43 +01:00
if ( action = = ML_FLUSH ) // nothing else to do
2004-06-13 20:20:40 +00:00
return NULL ;
2019-12-04 21:57:43 +01:00
bnum = 1 ; // start at the root of the tree
2004-06-13 20:20:40 +00:00
page_count = 1 ;
low = 1 ;
high = buf - > b_ml . ml_line_count ;
2019-12-04 21:57:43 +01:00
if ( action = = ML_FIND ) // first try stack entries
2004-06-13 20:20:40 +00:00
{
for ( top = buf - > b_ml . ml_stack_top - 1 ; top > = 0 ; - - top )
{
ip = & ( buf - > b_ml . ml_stack [ top ] ) ;
if ( ip - > ip_low < = lnum & & ip - > ip_high > = lnum )
{
bnum = ip - > ip_bnum ;
low = ip - > ip_low ;
high = ip - > ip_high ;
2019-12-04 21:57:43 +01:00
buf - > b_ml . ml_stack_top = top ; // truncate stack at prev entry
2004-06-13 20:20:40 +00:00
break ;
}
}
if ( top < 0 )
2019-12-04 21:57:43 +01:00
buf - > b_ml . ml_stack_top = 0 ; // not found, start at the root
2004-06-13 20:20:40 +00:00
}
2019-12-04 21:57:43 +01:00
else // ML_DELETE or ML_INSERT
buf - > b_ml . ml_stack_top = 0 ; // start at the root
2004-06-13 20:20:40 +00:00
/*
* search downwards in the tree until a data block is found
*/
for ( ; ; )
{
if ( ( hp = mf_get ( mfp , bnum , page_count ) ) = = NULL )
goto error_noblock ;
/*
* update high for insert / delete
*/
if ( action = = ML_INSERT )
+ + high ;
else if ( action = = ML_DELETE )
- - high ;
dp = ( DATA_BL * ) ( hp - > bh_data ) ;
2019-12-04 21:57:43 +01:00
if ( dp - > db_id = = DATA_ID ) // data block
2004-06-13 20:20:40 +00:00
{
buf - > b_ml . ml_locked = hp ;
buf - > b_ml . ml_locked_low = low ;
buf - > b_ml . ml_locked_high = high ;
buf - > b_ml . ml_locked_lineadd = 0 ;
buf - > b_ml . ml_flags & = ~ ( ML_LOCKED_DIRTY | ML_LOCKED_POS ) ;
return hp ;
}
2019-12-04 21:57:43 +01:00
pp = ( PTR_BL * ) ( dp ) ; // must be pointer block
2004-06-13 20:20:40 +00:00
if ( pp - > pb_id ! = PTR_ID )
{
2023-05-31 17:12:14 +01:00
iemsg ( e_pointer_block_id_wrong ) ;
2004-06-13 20:20:40 +00:00
goto error_block ;
}
2019-12-04 21:57:43 +01:00
if ( ( top = ml_add_stack ( buf ) ) < 0 ) // add new entry to stack
2004-06-13 20:20:40 +00:00
goto error_block ;
ip = & ( buf - > b_ml . ml_stack [ top ] ) ;
ip - > ip_bnum = bnum ;
ip - > ip_low = low ;
ip - > ip_high = high ;
2019-12-04 21:57:43 +01:00
ip - > ip_index = - 1 ; // index not known yet
2004-06-13 20:20:40 +00:00
dirty = FALSE ;
for ( idx = 0 ; idx < ( int ) pp - > pb_count ; + + idx )
{
t = pp - > pb_pointer [ idx ] . pe_line_count ;
CHECK ( t = = 0 , _ ( " pe_line_count is zero " ) ) ;
if ( ( low + = t ) > lnum )
{
ip - > ip_index = idx ;
bnum = pp - > pb_pointer [ idx ] . pe_bnum ;
page_count = pp - > pb_pointer [ idx ] . pe_page_count ;
high = low - 1 ;
low - = t ;
/*
* a negative block number may have been changed
*/
if ( bnum < 0 )
{
bnum2 = mf_trans_del ( mfp , bnum ) ;
if ( bnum ! = bnum2 )
{
bnum = bnum2 ;
pp - > pb_pointer [ idx ] . pe_bnum = bnum ;
dirty = TRUE ;
}
}
break ;
}
}
2019-12-04 21:57:43 +01:00
if ( idx > = ( int ) pp - > pb_count ) // past the end: something wrong!
2004-06-13 20:20:40 +00:00
{
if ( lnum > buf - > b_ml . ml_line_count )
2023-05-31 17:12:14 +01:00
siemsg ( e_line_number_out_of_range_nr_past_the_end ,
2004-06-13 20:20:40 +00:00
lnum - buf - > b_ml . ml_line_count ) ;
else
2023-05-31 17:12:14 +01:00
siemsg ( e_line_count_wrong_in_block_nr , bnum ) ;
2004-06-13 20:20:40 +00:00
goto error_block ;
}
if ( action = = ML_DELETE )
{
pp - > pb_pointer [ idx ] . pe_line_count - - ;
dirty = TRUE ;
}
else if ( action = = ML_INSERT )
{
pp - > pb_pointer [ idx ] . pe_line_count + + ;
dirty = TRUE ;
}
mf_put ( mfp , hp , dirty , FALSE ) ;
}
error_block :
mf_put ( mfp , hp , FALSE , FALSE ) ;
error_noblock :
2010-06-21 06:15:46 +02:00
/*
* If action is ML_DELETE or ML_INSERT we have to correct the tree for
* the incremented / decremented line counts , because there won ' t be a line
* inserted / deleted after all .
*/
2004-06-13 20:20:40 +00:00
if ( action = = ML_DELETE )
ml_lineadd ( buf , 1 ) ;
else if ( action = = ML_INSERT )
ml_lineadd ( buf , - 1 ) ;
buf - > b_ml . ml_stack_top = 0 ;
return NULL ;
}
/*
* add an entry to the info pointer stack
*
* return - 1 for failure , number of the new entry otherwise
*/
static int
2016-01-30 18:51:09 +01:00
ml_add_stack ( buf_T * buf )
2004-06-13 20:20:40 +00:00
{
int top ;
infoptr_T * newstack ;
top = buf - > b_ml . ml_stack_top ;
2019-12-04 21:57:43 +01:00
// may have to increase the stack size
2004-06-13 20:20:40 +00:00
if ( top = = buf - > b_ml . ml_stack_size )
{
2019-12-04 21:57:43 +01:00
CHECK ( top > 0 , _ ( " Stack size increases " ) ) ; // more than 5 levels???
2004-06-13 20:20:40 +00:00
2019-05-28 23:08:19 +02:00
newstack = ALLOC_MULT ( infoptr_T , buf - > b_ml . ml_stack_size + STACK_INCR ) ;
2004-06-13 20:20:40 +00:00
if ( newstack = = NULL )
return - 1 ;
2015-08-08 18:23:46 +02:00
if ( top > 0 )
mch_memmove ( newstack , buf - > b_ml . ml_stack ,
2008-06-24 22:58:06 +00:00
( size_t ) top * sizeof ( infoptr_T ) ) ;
2004-06-13 20:20:40 +00:00
vim_free ( buf - > b_ml . ml_stack ) ;
buf - > b_ml . ml_stack = newstack ;
buf - > b_ml . ml_stack_size + = STACK_INCR ;
}
buf - > b_ml . ml_stack_top + + ;
return top ;
}
/*
* Update the pointer blocks on the stack for inserted / deleted lines .
* The stack itself is also updated .
*
* When a insert / delete line action fails , the line is not inserted / deleted ,
* but the pointer blocks have already been updated . That is fixed here by
* walking through the stack .
*
* Count is the number of lines added , negative if lines have been deleted .
*/
static void
2016-01-30 18:51:09 +01:00
ml_lineadd ( buf_T * buf , int count )
2004-06-13 20:20:40 +00:00
{
int idx ;
infoptr_T * ip ;
PTR_BL * pp ;
memfile_T * mfp = buf - > b_ml . ml_mfp ;
bhdr_T * hp ;
for ( idx = buf - > b_ml . ml_stack_top - 1 ; idx > = 0 ; - - idx )
{
ip = & ( buf - > b_ml . ml_stack [ idx ] ) ;
if ( ( hp = mf_get ( mfp , ip - > ip_bnum , 1 ) ) = = NULL )
break ;
2019-12-04 21:57:43 +01:00
pp = ( PTR_BL * ) ( hp - > bh_data ) ; // must be pointer block
2004-06-13 20:20:40 +00:00
if ( pp - > pb_id ! = PTR_ID )
{
mf_put ( mfp , hp , FALSE , FALSE ) ;
2023-05-31 17:12:14 +01:00
iemsg ( e_pointer_block_id_wrong_two ) ;
2004-06-13 20:20:40 +00:00
break ;
}
pp - > pb_pointer [ ip - > ip_index ] . pe_line_count + = count ;
ip - > ip_high + = count ;
mf_put ( mfp , hp , TRUE , FALSE ) ;
}
}
2010-05-23 23:34:36 +02:00
# if defined(HAVE_READLINK) || defined(PROTO)
2005-12-12 22:05:50 +00:00
/*
* Resolve a symlink in the last component of a file name .
* Note that f_resolve ( ) does it for every part of the path , we don ' t do that
* here .
* If it worked returns OK and the resolved link in " buf[MAXPATHL] " .
* Otherwise returns FAIL .
*/
2010-05-23 23:34:36 +02:00
int
2016-01-30 18:51:09 +01:00
resolve_symlink ( char_u * fname , char_u * buf )
2005-12-12 22:05:50 +00:00
{
char_u tmp [ MAXPATHL ] ;
int ret ;
int depth = 0 ;
if ( fname = = NULL )
return FAIL ;
2019-12-04 21:57:43 +01:00
// Put the result so far in tmp[], starting with the original name.
2005-12-12 22:05:50 +00:00
vim_strncpy ( tmp , fname , MAXPATHL - 1 ) ;
for ( ; ; )
{
2019-12-04 21:57:43 +01:00
// Limit symlink depth to 100, catch recursive loops.
2005-12-12 22:05:50 +00:00
if ( + + depth = = 100 )
{
2022-01-05 16:09:06 +00:00
semsg ( _ ( e_symlink_loop_for_str ) , fname ) ;
2005-12-12 22:05:50 +00:00
return FAIL ;
}
ret = readlink ( ( char * ) tmp , ( char * ) buf , MAXPATHL - 1 ) ;
if ( ret < = 0 )
{
2005-12-23 22:19:46 +00:00
if ( errno = = EINVAL | | errno = = ENOENT )
2005-12-12 22:05:50 +00:00
{
2019-12-04 21:57:43 +01:00
// Found non-symlink or not existing file, stop here.
// When at the first level use the unmodified name, skip the
// call to vim_FullName().
2005-12-12 22:05:50 +00:00
if ( depth = = 1 )
return FAIL ;
2019-12-04 21:57:43 +01:00
// Use the resolved name in tmp[].
2005-12-12 22:05:50 +00:00
break ;
}
2019-12-04 21:57:43 +01:00
// There must be some error reading links, use original name.
2005-12-12 22:05:50 +00:00
return FAIL ;
}
buf [ ret ] = NUL ;
/*
* Check whether the symlink is relative or absolute .
* If it ' s relative , build a new path based on the directory
* portion of the filename ( if any ) and the path the symlink
* points to .
*/
if ( mch_isFullName ( buf ) )
STRCPY ( tmp , buf ) ;
else
{
char_u * tail ;
tail = gettail ( tmp ) ;
if ( STRLEN ( tail ) + STRLEN ( buf ) > = MAXPATHL )
return FAIL ;
STRCPY ( tail , buf ) ;
}
}
/*
* Try to resolve the full name of the file so that the swapfile name will
* be consistent even when opening a relative symlink from different
* working directories .
*/
return vim_FullName ( tmp , buf , MAXPATHL , TRUE ) ;
}
# endif
2004-06-13 20:20:40 +00:00
/*
2005-08-01 22:02:32 +00:00
* Make swap file name out of the file name and a directory name .
* Returns pointer to allocated memory or NULL .
2004-06-13 20:20:40 +00:00
*/
2005-08-01 22:02:32 +00:00
char_u *
2016-01-30 18:51:09 +01:00
makeswapname (
char_u * fname ,
char_u * ffname UNUSED ,
buf_T * buf ,
char_u * dir_name )
2004-06-13 20:20:40 +00:00
{
char_u * r , * s ;
2010-05-14 17:52:42 +02:00
char_u * fname_res = fname ;
2005-12-12 22:05:50 +00:00
# ifdef HAVE_READLINK
char_u fname_buf [ MAXPATHL ] ;
2020-07-15 15:30:05 +02:00
// Expand symlink in the file name, so that we put the swap file with the
// actual file instead of with the symlink.
if ( resolve_symlink ( fname , fname_buf ) = = OK )
fname_res = fname_buf ;
2005-12-12 22:05:50 +00:00
# endif
2004-06-13 20:20:40 +00:00
2019-02-17 17:44:42 +01:00
# if defined(UNIX) || defined(MSWIN) // Need _very_ long file names
2017-02-28 21:26:17 +01:00
int len = ( int ) STRLEN ( dir_name ) ;
2017-02-18 16:59:02 +01:00
s = dir_name + len ;
if ( after_pathsep ( dir_name , s ) & & len > 1 & & s [ - 1 ] = = s [ - 2 ] )
2019-12-04 21:57:43 +01:00
{ // Ends with '//', Use Full path
2004-06-13 20:20:40 +00:00
r = NULL ;
2024-07-25 20:58:42 +02:00
if ( ( s = make_percent_swname ( dir_name , s , fname_res ) ) ! = NULL )
2004-06-13 20:20:40 +00:00
{
r = modname ( s , ( char_u * ) " .swp " , FALSE ) ;
vim_free ( s ) ;
}
return r ;
}
# endif
r = buf_modname (
( buf - > b_p_sn | | buf - > b_shortname ) ,
2005-12-12 22:05:50 +00:00
fname_res ,
2004-06-13 20:20:40 +00:00
( char_u * )
2011-05-10 16:41:25 +02:00
# if defined(VMS)
2004-06-13 20:20:40 +00:00
" _swp " ,
# else
" .swp " ,
# endif
2019-12-04 21:57:43 +01:00
// Prepend a '.' to the swap file name for the current directory.
2016-02-23 14:53:34 +01:00
dir_name [ 0 ] = = ' . ' & & dir_name [ 1 ] = = NUL ) ;
2019-12-04 21:57:43 +01:00
if ( r = = NULL ) // out of memory
2004-06-13 20:20:40 +00:00
return NULL ;
s = get_file_in_dir ( r , dir_name ) ;
vim_free ( r ) ;
return s ;
}
/*
* Get file name to use for swap file or backup file .
* Use the name of the edited file " fname " and an entry in the ' dir ' or ' bdir '
* option " dname " .
* - If " dname " is " . " , return " fname " ( swap file in dir of file ) .
* - If " dname " starts with " ./ " , insert " dname " in " fname " ( swap file
* relative to dir of file ) .
* - Otherwise , prepend " dname " to the tail of " fname " ( swap file in specific
* dir ) .
*
* The return value is an allocated string and can be NULL .
*/
char_u *
2016-01-30 18:51:09 +01:00
get_file_in_dir (
char_u * fname ,
2019-12-04 21:57:43 +01:00
char_u * dname ) // don't use "dirname", it is a global for Alpha
2004-06-13 20:20:40 +00:00
{
char_u * t ;
char_u * tail ;
char_u * retval ;
int save_char ;
tail = gettail ( fname ) ;
if ( dname [ 0 ] = = ' . ' & & dname [ 1 ] = = NUL )
retval = vim_strsave ( fname ) ;
else if ( dname [ 0 ] = = ' . ' & & vim_ispathsep ( dname [ 1 ] ) )
{
2019-12-04 21:57:43 +01:00
if ( tail = = fname ) // no path before file name
2004-06-13 20:20:40 +00:00
retval = concat_fnames ( dname + 2 , tail , TRUE ) ;
else
{
save_char = * tail ;
* tail = NUL ;
t = concat_fnames ( fname , dname + 2 , TRUE ) ;
* tail = save_char ;
2019-12-04 21:57:43 +01:00
if ( t = = NULL ) // out of memory
2004-06-13 20:20:40 +00:00
retval = NULL ;
else
{
retval = concat_fnames ( t , tail , TRUE ) ;
vim_free ( t ) ;
}
}
}
else
retval = concat_fnames ( dname , tail , TRUE ) ;
2019-02-17 17:44:42 +01:00
# ifdef MSWIN
2013-11-04 02:54:12 +01:00
if ( retval ! = NULL )
2017-03-12 19:22:36 +01:00
for ( t = gettail ( retval ) ; * t ! = NUL ; MB_PTR_ADV ( t ) )
2013-11-04 02:54:12 +01:00
if ( * t = = ' : ' )
* t = ' % ' ;
# endif
2004-06-13 20:20:40 +00:00
return retval ;
}
2005-12-07 21:07:59 +00:00
/*
* Print the ATTENTION message : info about an existing swap file .
*/
static void
2016-01-30 18:51:09 +01:00
attention_message (
2019-12-04 21:57:43 +01:00
buf_T * buf , // buffer being edited
char_u * fname ) // swap file name
2005-12-07 21:07:59 +00:00
{
2016-07-01 17:17:39 +02:00
stat_T st ;
2019-05-10 21:28:38 +02:00
time_t swap_mtime ;
2005-12-07 21:07:59 +00:00
+ + no_wait_return ;
2022-01-02 17:00:40 +00:00
( void ) emsg ( _ ( e_attention ) ) ;
2019-01-19 17:43:09 +01:00
msg_puts ( _ ( " \n Found a swap file by the name \" " ) ) ;
2005-12-07 21:07:59 +00:00
msg_home_replace ( fname ) ;
2019-01-19 17:43:09 +01:00
msg_puts ( " \" \n " ) ;
2019-05-10 21:28:38 +02:00
swap_mtime = swapfile_info ( fname ) ;
2019-01-19 17:43:09 +01:00
msg_puts ( _ ( " While opening file \" " ) ) ;
2005-12-07 21:07:59 +00:00
msg_outtrans ( buf - > b_fname ) ;
2019-01-19 17:43:09 +01:00
msg_puts ( " \" \n " ) ;
2018-10-13 19:06:27 +02:00
if ( mch_stat ( ( char * ) buf - > b_fname , & st ) = = - 1 )
{
2019-01-19 17:43:09 +01:00
msg_puts ( _ ( " CANNOT BE FOUND " ) ) ;
2018-10-13 19:06:27 +02:00
}
else
2005-12-07 21:07:59 +00:00
{
2019-01-19 17:43:09 +01:00
msg_puts ( _ ( " dated: " ) ) ;
2019-05-10 21:28:38 +02:00
msg_puts ( get_ctime ( st . st_mtime , TRUE ) ) ;
if ( swap_mtime ! = 0 & & st . st_mtime > swap_mtime )
2019-01-19 17:43:09 +01:00
msg_puts ( _ ( " NEWER than swap file! \n " ) ) ;
2005-12-07 21:07:59 +00:00
}
2019-12-04 21:57:43 +01:00
// Some of these messages are long to allow translation to
// other languages.
2019-01-19 17:43:09 +01:00
msg_puts ( _ ( " \n (1) Another program may be editing the same file. If this is the case, \n be careful not to end up with two different instances of the same \n file when making changes. Quit, or continue with caution. \n " ) ) ;
msg_puts ( _ ( " (2) An edit session for this file crashed. \n " ) ) ;
msg_puts ( _ ( " If this is the case, use \" :recover \" or \" vim -r " ) ) ;
2005-12-07 21:07:59 +00:00
msg_outtrans ( buf - > b_fname ) ;
2019-01-19 17:43:09 +01:00
msg_puts ( _ ( " \" \n to recover the changes (see \" :help recovery \" ). \n " ) ) ;
msg_puts ( _ ( " If you did this already, delete the swap file \" " ) ) ;
2005-12-07 21:07:59 +00:00
msg_outtrans ( fname ) ;
2019-01-19 17:43:09 +01:00
msg_puts ( _ ( " \" \n to avoid this message. \n " ) ) ;
2005-12-07 21:07:59 +00:00
cmdline_row = msg_row ;
- - no_wait_return ;
}
2022-04-04 16:57:21 +01:00
typedef enum {
SEA_CHOICE_NONE = 0 ,
SEA_CHOICE_READONLY = 1 ,
SEA_CHOICE_EDIT = 2 ,
SEA_CHOICE_RECOVER = 3 ,
SEA_CHOICE_DELETE = 4 ,
SEA_CHOICE_QUIT = 5 ,
SEA_CHOICE_ABORT = 6
} sea_choice_T ;
2018-03-04 18:08:14 +01:00
# if defined(FEAT_EVAL)
2005-12-07 21:07:59 +00:00
/*
* Trigger the SwapExists autocommands .
2022-04-04 16:57:21 +01:00
* Returns a value for equivalent to do_dialog ( ) .
2005-12-07 21:07:59 +00:00
*/
2022-04-04 16:57:21 +01:00
static sea_choice_T
2016-01-30 18:51:09 +01:00
do_swapexists ( buf_T * buf , char_u * fname )
2005-12-07 21:07:59 +00:00
{
set_vim_var_string ( VV_SWAPNAME , fname , - 1 ) ;
set_vim_var_string ( VV_SWAPCHOICE , NULL , - 1 ) ;
2019-12-04 21:57:43 +01:00
// Trigger SwapExists autocommands with <afile> set to the file being
// edited. Disallow changing directory here.
2009-04-22 13:58:46 +00:00
+ + allbuf_lock ;
2005-12-07 21:07:59 +00:00
apply_autocmds ( EVENT_SWAPEXISTS , buf - > b_fname , NULL , FALSE , NULL ) ;
2009-04-22 13:58:46 +00:00
- - allbuf_lock ;
2005-12-07 21:07:59 +00:00
set_vim_var_string ( VV_SWAPNAME , NULL , - 1 ) ;
switch ( * get_vim_var_str ( VV_SWAPCHOICE ) )
{
2022-04-04 16:57:21 +01:00
case ' o ' : return SEA_CHOICE_READONLY ;
case ' e ' : return SEA_CHOICE_EDIT ;
case ' r ' : return SEA_CHOICE_RECOVER ;
case ' d ' : return SEA_CHOICE_DELETE ;
case ' q ' : return SEA_CHOICE_QUIT ;
case ' a ' : return SEA_CHOICE_ABORT ;
2005-12-07 21:07:59 +00:00
}
2022-04-04 16:57:21 +01:00
return SEA_CHOICE_NONE ;
2005-12-07 21:07:59 +00:00
}
# endif
2004-06-13 20:20:40 +00:00
/*
* Find out what name to use for the swap file for buffer ' buf ' .
*
* Several names are tried to find one that does not exist
2005-08-01 22:02:32 +00:00
* Returns the name in allocated memory or NULL .
2011-10-26 11:44:18 +02:00
* When out of memory " dirp " is set to NULL .
2004-06-13 20:20:40 +00:00
*
* Note : If BASENAMELEN is not correct , you will get error messages for
2010-05-23 23:34:36 +02:00
* not being able to open the swap or undo file
2009-04-22 13:58:46 +00:00
* Note : May trigger SwapExists autocmd , pointers may change !
2004-06-13 20:20:40 +00:00
*/
static char_u *
2016-01-30 18:51:09 +01:00
findswapname (
buf_T * buf ,
2019-12-04 21:57:43 +01:00
char_u * * dirp , // pointer to list of directories
char_u * old_fname ) // don't give warning for this file name
2004-06-13 20:20:40 +00:00
{
char_u * fname ;
int n ;
char_u * dir_name ;
# ifdef AMIGA
BPTR fh ;
# endif
int r ;
2013-11-04 02:54:12 +01:00
char_u * buf_fname = buf - > b_fname ;
2004-06-13 20:20:40 +00:00
2016-02-23 14:53:34 +01:00
# if !defined(UNIX)
2004-06-13 20:20:40 +00:00
# define CREATE_DUMMY_FILE
FILE * dummyfd = NULL ;
2019-02-17 17:44:42 +01:00
# ifdef MSWIN
2013-11-04 02:54:12 +01:00
if ( buf_fname ! = NULL & & ! mch_isFullName ( buf_fname )
& & vim_strchr ( gettail ( buf_fname ) , ' : ' ) )
{
char_u * t ;
buf_fname = vim_strsave ( buf_fname ) ;
if ( buf_fname = = NULL )
buf_fname = buf - > b_fname ;
else
2017-03-12 19:22:36 +01:00
for ( t = gettail ( buf_fname ) ; * t ! = NUL ; MB_PTR_ADV ( t ) )
2013-11-04 02:54:12 +01:00
if ( * t = = ' : ' )
* t = ' % ' ;
}
# endif
2010-05-23 23:34:36 +02:00
/*
* If we start editing a new file , e . g . " test.doc " , which resides on an
* MSDOS compatible filesystem , it is possible that the file
* " test.doc.swp " which we create will be exactly the same file . To avoid
* this problem we temporarily create " test.doc " . Don ' t do this when the
2021-12-27 17:21:41 +00:00
* check below for an 8.3 file name is used .
2010-05-23 23:34:36 +02:00
*/
2013-11-04 02:54:12 +01:00
if ( ! ( buf - > b_p_sn | | buf - > b_shortname ) & & buf_fname ! = NULL
& & mch_getperm ( buf_fname ) < 0 )
dummyfd = mch_fopen ( ( char * ) buf_fname , " w " ) ;
2004-06-13 20:20:40 +00:00
# endif
2010-05-23 23:34:36 +02:00
/*
* Isolate a directory name from * dirp and put it in dir_name .
* First allocate some memory to put the directory name in .
*/
2019-05-24 18:54:09 +02:00
dir_name = alloc ( STRLEN ( * dirp ) + 1 ) ;
2011-10-26 11:44:18 +02:00
if ( dir_name = = NULL )
* dirp = NULL ;
else
2004-06-13 20:20:40 +00:00
( void ) copy_option_part ( dirp , dir_name , 31000 , " , " ) ;
2010-05-23 23:34:36 +02:00
/*
* we try different names until we find one that does not exist yet
*/
2019-12-04 21:57:43 +01:00
if ( dir_name = = NULL ) // out of memory
2004-06-13 20:20:40 +00:00
fname = NULL ;
else
2013-11-04 02:54:12 +01:00
fname = makeswapname ( buf_fname , buf - > b_ffname , buf , dir_name ) ;
2004-06-13 20:20:40 +00:00
for ( ; ; )
{
2019-12-04 21:57:43 +01:00
if ( fname = = NULL ) // must be out of memory
2004-06-13 20:20:40 +00:00
break ;
2019-12-04 21:57:43 +01:00
if ( ( n = ( int ) STRLEN ( fname ) ) = = 0 ) // safety check
2004-06-13 20:20:40 +00:00
{
2018-02-10 18:45:26 +01:00
VIM_CLEAR ( fname ) ;
2004-06-13 20:20:40 +00:00
break ;
}
2016-02-23 14:53:34 +01:00
# if defined(UNIX)
2004-06-13 20:20:40 +00:00
/*
* Some systems have a MS - DOS compatible filesystem that use 8.3 character
* file names . If this is the first try and the swap file name does not fit in
* 8.3 , detect if this is the case , set shortname and try again .
*/
if ( fname [ n - 2 ] = = ' w ' & & fname [ n - 1 ] = = ' p '
& & ! ( buf - > b_p_sn | | buf - > b_shortname ) )
{
char_u * tail ;
char_u * fname2 ;
2016-07-01 17:17:39 +02:00
stat_T s1 , s2 ;
2004-06-13 20:20:40 +00:00
int f1 , f2 ;
int created1 = FALSE , created2 = FALSE ;
int same = FALSE ;
/*
* Check if swapfile name does not fit in 8.3 :
* It either contains two dots , is longer than 8 chars , or starts
* with a dot .
*/
2013-11-04 02:54:12 +01:00
tail = gettail ( buf_fname ) ;
2004-06-13 20:20:40 +00:00
if ( vim_strchr ( tail , ' . ' ) ! = NULL
| | STRLEN ( tail ) > ( size_t ) 8
| | * gettail ( fname ) = = ' . ' )
{
fname2 = alloc ( n + 2 ) ;
if ( fname2 ! = NULL )
{
STRCPY ( fname2 , fname ) ;
2019-12-04 21:57:43 +01:00
// if fname == "xx.xx.swp", fname2 = "xx.xx.swx"
// if fname == ".xx.swp", fname2 = ".xx.swpx"
// if fname == "123456789.swp", fname2 = "12345678x.swp"
2004-06-13 20:20:40 +00:00
if ( vim_strchr ( tail , ' . ' ) ! = NULL )
fname2 [ n - 1 ] = ' x ' ;
else if ( * gettail ( fname ) = = ' . ' )
{
fname2 [ n ] = ' x ' ;
fname2 [ n + 1 ] = NUL ;
}
else
fname2 [ n - 5 ] + = 1 ;
/*
* may need to create the files to be able to use mch_stat ( )
*/
f1 = mch_open ( ( char * ) fname , O_RDONLY | O_EXTRA , 0 ) ;
if ( f1 < 0 )
{
f1 = mch_open_rw ( ( char * ) fname ,
O_RDWR | O_CREAT | O_EXCL | O_EXTRA ) ;
created1 = TRUE ;
}
if ( f1 > = 0 )
{
f2 = mch_open ( ( char * ) fname2 , O_RDONLY | O_EXTRA , 0 ) ;
if ( f2 < 0 )
{
f2 = mch_open_rw ( ( char * ) fname2 ,
O_RDWR | O_CREAT | O_EXCL | O_EXTRA ) ;
created2 = TRUE ;
}
if ( f2 > = 0 )
{
/*
* Both files exist now . If mch_stat ( ) returns the
* same device and inode they are the same file .
*/
if ( mch_fstat ( f1 , & s1 ) ! = - 1
& & mch_fstat ( f2 , & s2 ) ! = - 1
& & s1 . st_dev = = s2 . st_dev
& & s1 . st_ino = = s2 . st_ino )
same = TRUE ;
close ( f2 ) ;
if ( created2 )
mch_remove ( fname2 ) ;
}
close ( f1 ) ;
if ( created1 )
mch_remove ( fname ) ;
}
vim_free ( fname2 ) ;
if ( same )
{
buf - > b_shortname = TRUE ;
vim_free ( fname ) ;
2013-11-04 02:54:12 +01:00
fname = makeswapname ( buf_fname , buf - > b_ffname ,
2005-08-01 22:02:32 +00:00
buf , dir_name ) ;
2019-12-04 21:57:43 +01:00
continue ; // try again with b_shortname set
2004-06-13 20:20:40 +00:00
}
}
}
}
# endif
/*
* check if the swapfile already exists
*/
2019-12-04 21:57:43 +01:00
if ( mch_getperm ( fname ) < 0 ) // it does not exist
2004-06-13 20:20:40 +00:00
{
# ifdef HAVE_LSTAT
2016-07-01 17:17:39 +02:00
stat_T sb ;
2004-06-13 20:20:40 +00:00
/*
* Extra security check : When a swap file is a symbolic link , this
* is most likely a symlink attack .
*/
if ( mch_lstat ( ( char * ) fname , & sb ) < 0 )
# else
# ifdef AMIGA
fh = Open ( ( UBYTE * ) fname , ( long ) MODE_NEWFILE ) ;
/*
* on the Amiga mch_getperm ( ) will return - 1 when the file exists
* but is being used by another program . This happens if you edit
* a file twice .
*/
2019-12-04 21:57:43 +01:00
if ( fh ! = ( BPTR ) NULL ) // can open file, OK
2004-06-13 20:20:40 +00:00
{
Close ( fh ) ;
mch_remove ( fname ) ;
break ;
}
if ( IoErr ( ) ! = ERROR_OBJECT_IN_USE
& & IoErr ( ) ! = ERROR_OBJECT_EXISTS )
# endif
# endif
break ;
}
/*
* A file name equal to old_fname is OK to use .
*/
if ( old_fname ! = NULL & & fnamecmp ( fname , old_fname ) = = 0 )
break ;
/*
* get here when file already exists
*/
2019-12-04 21:57:43 +01:00
if ( fname [ n - 2 ] = = ' w ' & & fname [ n - 1 ] = = ' p ' ) // first try
2004-06-13 20:20:40 +00:00
{
/*
* on MS - DOS compatible filesystems ( e . g . messydos ) file . doc . swp
* and file . doc are the same file . To guess if this problem is
* present try if file . doc . swx exists . If it does , we set
* buf - > b_shortname and try file_doc . swp ( dots replaced by
* underscores for this file ) , and try again . If it doesn ' t we
* assume that " file.doc.swp " already exists .
*/
2019-12-04 21:57:43 +01:00
if ( ! ( buf - > b_p_sn | | buf - > b_shortname ) ) // not tried yet
2004-06-13 20:20:40 +00:00
{
fname [ n - 1 ] = ' x ' ;
2019-12-04 21:57:43 +01:00
r = mch_getperm ( fname ) ; // try "file.swx"
2004-06-13 20:20:40 +00:00
fname [ n - 1 ] = ' p ' ;
2019-12-04 21:57:43 +01:00
if ( r > = 0 ) // "file.swx" seems to exist
2004-06-13 20:20:40 +00:00
{
buf - > b_shortname = TRUE ;
vim_free ( fname ) ;
2013-11-04 02:54:12 +01:00
fname = makeswapname ( buf_fname , buf - > b_ffname ,
2005-08-01 22:02:32 +00:00
buf , dir_name ) ;
2019-12-04 21:57:43 +01:00
continue ; // try again with '.' replaced with '_'
2004-06-13 20:20:40 +00:00
}
}
/*
* If we get here the " .swp " file really exists .
* Give an error message , unless recovering , no file name , we are
* viewing a help file or when the path of the file is different
* ( happens when all . swp files are in one directory ) .
*/
2013-11-04 02:54:12 +01:00
if ( ! recoverymode & & buf_fname ! = NULL
2019-08-04 20:44:19 +02:00
& & ! buf - > b_help
& & ! ( buf - > b_flags & ( BF_DUMMY | BF_NO_SEA ) ) )
2004-06-13 20:20:40 +00:00
{
int fd ;
struct block0 b0 ;
int differ = FALSE ;
/*
* Try to read block 0 from the swap file to get the original
* file name ( and inode number ) .
*/
fd = mch_open ( ( char * ) fname , O_RDONLY | O_EXTRA , 0 ) ;
if ( fd > = 0 )
{
2010-12-17 16:27:16 +01:00
if ( read_eintr ( fd , & b0 , sizeof ( b0 ) ) = = sizeof ( b0 ) )
2004-06-13 20:20:40 +00:00
{
/*
2004-12-19 22:46:22 +00:00
* If the swapfile has the same directory as the
* buffer don ' t compare the directory names , they can
* have a different mountpoint .
2004-06-13 20:20:40 +00:00
*/
2004-12-19 22:46:22 +00:00
if ( b0 . b0_flags & B0_SAME_DIR )
{
if ( fnamecmp ( gettail ( buf - > b_ffname ) ,
gettail ( b0 . b0_fname ) ) ! = 0
| | ! same_directory ( fname , buf - > b_ffname ) )
2005-12-12 22:05:50 +00:00
{
# ifdef CHECK_INODE
2019-12-04 21:57:43 +01:00
// Symlinks may point to the same file even
// when the name differs, need to check the
// inode too.
2005-12-12 22:05:50 +00:00
expand_env ( b0 . b0_fname , NameBuff , MAXPATHL ) ;
if ( fnamecmp_ino ( buf - > b_ffname , NameBuff ,
char_to_long ( b0 . b0_ino ) ) )
# endif
differ = TRUE ;
}
2004-12-19 22:46:22 +00:00
}
else
{
/*
* The name in the swap file may be
* " ~user/path/file " . Expand it first .
*/
expand_env ( b0 . b0_fname , NameBuff , MAXPATHL ) ;
2004-06-13 20:20:40 +00:00
# ifdef CHECK_INODE
2004-12-19 22:46:22 +00:00
if ( fnamecmp_ino ( buf - > b_ffname , NameBuff ,
2005-12-12 22:05:50 +00:00
char_to_long ( b0 . b0_ino ) ) )
2004-12-19 22:46:22 +00:00
differ = TRUE ;
2004-06-13 20:20:40 +00:00
# else
2004-12-19 22:46:22 +00:00
if ( fnamecmp ( NameBuff , buf - > b_ffname ) ! = 0 )
differ = TRUE ;
2004-06-13 20:20:40 +00:00
# endif
2004-12-19 22:46:22 +00:00
}
2004-06-13 20:20:40 +00:00
}
close ( fd ) ;
}
2019-12-04 21:57:43 +01:00
// give the ATTENTION message when there is an old swap file
// for the current file, and the buffer was not recovered.
2004-06-13 20:20:40 +00:00
if ( differ = = FALSE & & ! ( curbuf - > b_flags & BF_RECOVERED )
& & vim_strchr ( p_shm , SHM_ATTENTION ) = = NULL )
{
2022-04-04 16:57:21 +01:00
sea_choice_T choice = SEA_CHOICE_NONE ;
stat_T st ;
2004-06-13 20:20:40 +00:00
# ifdef CREATE_DUMMY_FILE
2022-04-04 16:57:21 +01:00
int did_use_dummy = FALSE ;
2004-06-13 20:20:40 +00:00
2019-12-04 21:57:43 +01:00
// Avoid getting a warning for the file being created
// outside of Vim, it was created at the start of this
// function. Delete the file now, because Vim might exit
// here if the window is closed.
2004-06-13 20:20:40 +00:00
if ( dummyfd ! = NULL )
{
fclose ( dummyfd ) ;
dummyfd = NULL ;
2013-11-04 02:54:12 +01:00
mch_remove ( buf_fname ) ;
2004-06-13 20:20:40 +00:00
did_use_dummy = TRUE ;
}
# endif
2019-04-28 22:50:40 +02:00
# ifdef HAVE_PROCESS_STILL_RUNNING
2004-06-13 20:20:40 +00:00
process_still_running = FALSE ;
# endif
2019-04-28 22:25:38 +02:00
// It's safe to delete the swap file if all these are true:
// - the edited file exists
// - the swap file has no changes and looks OK
if ( mch_stat ( ( char * ) buf - > b_fname , & st ) = = 0
& & swapfile_unchanged ( fname ) )
{
2022-04-04 16:57:21 +01:00
choice = SEA_CHOICE_DELETE ;
2019-04-28 22:25:38 +02:00
if ( p_verbose > 0 )
verb_msg ( _ ( " Found a swap file that is not useful, deleting it " ) ) ;
}
2018-03-04 18:08:14 +01:00
# if defined(FEAT_EVAL)
2005-12-07 21:07:59 +00:00
/*
* If there is an SwapExists autocommand and we can handle
* the response , trigger it . It may return 0 to ask the
* user anyway .
*/
2022-04-04 16:57:21 +01:00
if ( choice = = SEA_CHOICE_NONE
2019-04-28 22:25:38 +02:00
& & swap_exists_action ! = SEA_NONE
2013-11-04 02:54:12 +01:00
& & has_autocmd ( EVENT_SWAPEXISTS , buf_fname , buf ) )
2005-12-07 21:07:59 +00:00
choice = do_swapexists ( buf , fname ) ;
# endif
2022-04-04 16:57:21 +01:00
if ( choice = = SEA_CHOICE_NONE
& & swap_exists_action = = SEA_READONLY )
{
// always open readonly.
choice = SEA_CHOICE_READONLY ;
}
if ( choice = = SEA_CHOICE_NONE )
2004-06-13 20:20:40 +00:00
{
2005-12-07 21:07:59 +00:00
# ifdef FEAT_GUI
2018-10-07 20:48:39 +02:00
// If we are supposed to start the GUI but it wasn't
// completely started yet, start it now. This makes
// the messages displayed in the Vim window when
// loading a session from the .gvimrc file.
2005-12-07 21:07:59 +00:00
if ( gui . starting & & ! gui . in_use )
2019-04-28 19:46:49 +02:00
gui_start ( NULL ) ;
2005-12-07 21:07:59 +00:00
# endif
2018-10-07 20:48:39 +02:00
// Show info about the existing swap file.
2005-12-07 21:07:59 +00:00
attention_message ( buf , fname ) ;
2018-10-07 20:48:39 +02:00
// We don't want a 'q' typed at the more-prompt
// interrupt loading a file.
2005-12-07 21:07:59 +00:00
got_int = FALSE ;
2018-10-07 20:48:39 +02:00
// If vimrc has "simalt ~x" we don't want it to
// interfere with the prompt here.
2018-10-07 23:16:36 +02:00
flush_buffers ( FLUSH_TYPEAHEAD ) ;
2004-06-13 20:20:40 +00:00
}
# if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG)
2022-04-04 16:57:21 +01:00
if ( swap_exists_action ! = SEA_NONE
& & choice = = SEA_CHOICE_NONE )
2004-06-13 20:20:40 +00:00
{
char_u * name ;
2022-04-04 16:57:21 +01:00
int dialog_result ;
2024-02-26 21:21:17 +01:00
size_t len = STRLEN ( _ ( " Swap file \" " ) ) ;
2004-06-13 20:20:40 +00:00
2019-05-24 18:54:09 +02:00
name = alloc ( STRLEN ( fname )
2024-02-26 21:21:17 +01:00
+ len
2019-05-24 18:54:09 +02:00
+ STRLEN ( _ ( " \" already exists! " ) ) + 5 ) ;
2004-06-13 20:20:40 +00:00
if ( name ! = NULL )
{
STRCPY ( name , _ ( " Swap file \" " ) ) ;
2024-02-26 21:21:17 +01:00
home_replace ( NULL , fname , name + len , 1000 , TRUE ) ;
2004-06-13 20:20:40 +00:00
STRCAT ( name , _ ( " \" already exists! " ) ) ;
}
2022-04-04 16:57:21 +01:00
dialog_result = do_dialog ( VIM_WARNING ,
2004-06-13 20:20:40 +00:00
( char_u * ) _ ( " VIM - ATTENTION " ) ,
name = = NULL
? ( char_u * ) _ ( " Swap file already exists! " )
: name ,
2019-04-28 22:50:40 +02:00
# ifdef HAVE_PROCESS_STILL_RUNNING
2004-06-13 20:20:40 +00:00
process_still_running
? ( char_u * ) _ ( " &Open Read-Only \n &Edit anyway \n &Recover \n &Quit \n &Abort " ) :
# endif
2011-01-17 20:08:11 +01:00
( char_u * ) _ ( " &Open Read-Only \n &Edit anyway \n &Recover \n &Delete it \n &Quit \n &Abort " ) , 1 , NULL , FALSE ) ;
2005-12-07 21:07:59 +00:00
2019-04-28 22:50:40 +02:00
# ifdef HAVE_PROCESS_STILL_RUNNING
2022-04-04 16:57:21 +01:00
if ( process_still_running & & dialog_result > = 4 )
// compensate for missing "Delete it" button
dialog_result + + ;
2005-12-07 21:07:59 +00:00
# endif
2022-04-04 16:57:21 +01:00
choice = dialog_result ;
2005-12-07 21:07:59 +00:00
vim_free ( name ) ;
2019-12-04 21:57:43 +01:00
// pretend screen didn't scroll, need redraw anyway
2005-12-07 21:07:59 +00:00
msg_scrolled = 0 ;
2022-08-14 14:17:45 +01:00
redraw_all_later ( UPD_NOT_VALID ) ;
2005-12-07 21:07:59 +00:00
}
# endif
2022-04-04 16:57:21 +01:00
switch ( choice )
2005-12-07 21:07:59 +00:00
{
2022-04-04 16:57:21 +01:00
case SEA_CHOICE_READONLY :
buf - > b_p_ro = TRUE ;
break ;
case SEA_CHOICE_EDIT :
break ;
case SEA_CHOICE_RECOVER :
swap_exists_action = SEA_RECOVER ;
break ;
case SEA_CHOICE_DELETE :
mch_remove ( fname ) ;
break ;
case SEA_CHOICE_QUIT :
swap_exists_action = SEA_QUIT ;
break ;
case SEA_CHOICE_ABORT :
swap_exists_action = SEA_QUIT ;
got_int = TRUE ;
break ;
case SEA_CHOICE_NONE :
msg_puts ( " \n " ) ;
if ( msg_silent = = 0 )
// call wait_return() later
need_wait_return = TRUE ;
2004-06-13 20:20:40 +00:00
break ;
}
2022-04-04 16:57:21 +01:00
// If the file was deleted this fname can be used.
if ( choice ! = SEA_CHOICE_NONE & & mch_getperm ( fname ) < 0 )
break ;
2004-06-13 20:20:40 +00:00
# ifdef CREATE_DUMMY_FILE
2019-12-04 21:57:43 +01:00
// Going to try another name, need the dummy file again.
2004-06-13 20:20:40 +00:00
if ( did_use_dummy )
2013-11-04 02:54:12 +01:00
dummyfd = mch_fopen ( ( char * ) buf_fname , " w " ) ;
2004-06-13 20:20:40 +00:00
# endif
}
}
}
/*
* Change the " .swp " extension to find another file that can be used .
* First decrement the last char : " .swo " , " .swn " , etc .
* If that still isn ' t enough decrement the last but one char : " .svz "
2004-06-20 12:51:53 +00:00
* Can happen when editing many " No Name " buffers .
2004-06-13 20:20:40 +00:00
*/
2019-12-04 21:57:43 +01:00
if ( fname [ n - 1 ] = = ' a ' ) // ".s?a"
2004-06-13 20:20:40 +00:00
{
2019-12-04 21:57:43 +01:00
if ( fname [ n - 2 ] = = ' a ' ) // ".saa": tried enough, give up
2004-06-13 20:20:40 +00:00
{
2022-01-02 17:00:40 +00:00
emsg ( _ ( e_too_many_swap_files_found ) ) ;
2018-02-10 18:45:26 +01:00
VIM_CLEAR ( fname ) ;
2004-06-13 20:20:40 +00:00
break ;
}
2019-12-04 21:57:43 +01:00
- - fname [ n - 2 ] ; // ".svz", ".suz", etc.
2004-06-13 20:20:40 +00:00
fname [ n - 1 ] = ' z ' + 1 ;
}
2019-12-04 21:57:43 +01:00
- - fname [ n - 1 ] ; // ".swo", ".swn", etc.
2004-06-13 20:20:40 +00:00
}
vim_free ( dir_name ) ;
# ifdef CREATE_DUMMY_FILE
2019-12-04 21:57:43 +01:00
if ( dummyfd ! = NULL ) // file has been created temporarily
2004-06-13 20:20:40 +00:00
{
fclose ( dummyfd ) ;
2013-11-04 02:54:12 +01:00
mch_remove ( buf_fname ) ;
2004-06-13 20:20:40 +00:00
}
2013-11-04 02:54:12 +01:00
# endif
2019-02-17 17:44:42 +01:00
# ifdef MSWIN
2013-11-04 02:54:12 +01:00
if ( buf_fname ! = buf - > b_fname )
vim_free ( buf_fname ) ;
2004-06-13 20:20:40 +00:00
# endif
return fname ;
}
static int
2016-01-30 18:51:09 +01:00
b0_magic_wrong ( ZERO_BL * b0p )
2004-06-13 20:20:40 +00:00
{
return ( b0p - > b0_magic_long ! = ( long ) B0_MAGIC_LONG
| | b0p - > b0_magic_int ! = ( int ) B0_MAGIC_INT
| | b0p - > b0_magic_short ! = ( short ) B0_MAGIC_SHORT
| | b0p - > b0_magic_char ! = B0_MAGIC_CHAR ) ;
}
# ifdef CHECK_INODE
/*
* Compare current file name with file name from swap file .
* Try to use inode numbers when possible .
* Return non - zero when files are different .
*
* When comparing file names a few things have to be taken into consideration :
* - When working over a network the full path of a file depends on the host .
* We check the inode number if possible . It is not 100 % reliable though ,
* because the device number cannot be used over a network .
* - When a file does not exist yet ( editing a new file ) there is no inode
* number .
* - The file name in a swap file may not be valid on the current host . The
* " ~user " form is used whenever possible to avoid this .
*
* This is getting complicated , let ' s make a table :
*
* ino_c ino_s fname_c fname_s differ =
*
* both files exist - > compare inode numbers :
* ! = 0 ! = 0 X X ino_c ! = ino_s
*
* inode number ( s ) unknown , file names available - > compare file names
* = = 0 X OK OK fname_c ! = fname_s
* X = = 0 OK OK fname_c ! = fname_s
*
* current file doesn ' t exist , file for swap file exist , file name ( s ) not
* available - > probably different
* = = 0 ! = 0 FAIL X TRUE
* = = 0 ! = 0 X FAIL TRUE
*
* current file exists , inode for swap unknown , file name ( s ) not
* available - > probably different
* ! = 0 = = 0 FAIL X TRUE
* ! = 0 = = 0 X FAIL TRUE
*
* current file doesn ' t exist , inode for swap unknown , one file name not
* available - > probably different
* = = 0 = = 0 FAIL OK TRUE
* = = 0 = = 0 OK FAIL TRUE
*
* current file doesn ' t exist , inode for swap unknown , both file names not
2018-05-12 17:04:12 +02:00
* available - > compare file names
* = = 0 = = 0 FAIL FAIL fname_c ! = fname_s
2004-06-13 20:20:40 +00:00
*
* Note that when the ino_t is 64 bits , only the last 32 will be used . This
* can ' t be changed without making the block 0 incompatible with 32 bit
* versions .
*/
static int
2016-01-30 18:51:09 +01:00
fnamecmp_ino (
2019-12-04 21:57:43 +01:00
char_u * fname_c , // current file name
char_u * fname_s , // file name from swap file
2016-01-30 18:51:09 +01:00
long ino_block0 )
2004-06-13 20:20:40 +00:00
{
2016-07-01 17:17:39 +02:00
stat_T st ;
2019-12-04 21:57:43 +01:00
ino_t ino_c = 0 ; // ino of current file
ino_t ino_s ; // ino of file from swap file
char_u buf_c [ MAXPATHL ] ; // full path of fname_c
char_u buf_s [ MAXPATHL ] ; // full path of fname_s
int retval_c ; // flag: buf_c valid
int retval_s ; // flag: buf_s valid
2004-06-13 20:20:40 +00:00
if ( mch_stat ( ( char * ) fname_c , & st ) = = 0 )
ino_c = ( ino_t ) st . st_ino ;
/*
* First we try to get the inode from the file name , because the inode in
* the swap file may be outdated . If that fails ( e . g . this path is not
* valid on this machine ) , use the inode from block 0.
*/
if ( mch_stat ( ( char * ) fname_s , & st ) = = 0 )
ino_s = ( ino_t ) st . st_ino ;
else
ino_s = ( ino_t ) ino_block0 ;
if ( ino_c & & ino_s )
return ( ino_c ! = ino_s ) ;
/*
* One of the inode numbers is unknown , try a forced vim_FullName ( ) and
* compare the file names .
*/
retval_c = vim_FullName ( fname_c , buf_c , MAXPATHL , TRUE ) ;
retval_s = vim_FullName ( fname_s , buf_s , MAXPATHL , TRUE ) ;
if ( retval_c = = OK & & retval_s = = OK )
2018-05-12 17:04:12 +02:00
return STRCMP ( buf_c , buf_s ) ! = 0 ;
2004-06-13 20:20:40 +00:00
/*
* Can ' t compare inodes or file names , guess that the files are different ,
2018-05-12 17:04:12 +02:00
* unless both appear not to exist at all , then compare with the file name
* in the swap file .
2004-06-13 20:20:40 +00:00
*/
if ( ino_s = = 0 & & ino_c = = 0 & & retval_c = = FAIL & & retval_s = = FAIL )
2018-05-12 17:04:12 +02:00
return STRCMP ( fname_c , fname_s ) ! = 0 ;
2004-06-13 20:20:40 +00:00
return TRUE ;
}
2019-12-04 21:57:43 +01:00
# endif // CHECK_INODE
2004-06-13 20:20:40 +00:00
/*
* Move a long integer into a four byte character array .
* Used for machine independency in block zero .
*/
static void
2016-01-30 18:51:09 +01:00
long_to_char ( long n , char_u * s )
2004-06-13 20:20:40 +00:00
{
s [ 0 ] = ( char_u ) ( n & 0xff ) ;
n = ( unsigned ) n > > 8 ;
s [ 1 ] = ( char_u ) ( n & 0xff ) ;
n = ( unsigned ) n > > 8 ;
s [ 2 ] = ( char_u ) ( n & 0xff ) ;
n = ( unsigned ) n > > 8 ;
s [ 3 ] = ( char_u ) ( n & 0xff ) ;
}
static long
2016-01-30 18:51:09 +01:00
char_to_long ( char_u * s )
2004-06-13 20:20:40 +00:00
{
long retval ;
retval = s [ 3 ] ;
retval < < = 8 ;
retval | = s [ 2 ] ;
retval < < = 8 ;
retval | = s [ 1 ] ;
retval < < = 8 ;
retval | = s [ 0 ] ;
return retval ;
}
2004-12-19 22:46:22 +00:00
/*
* Set the flags in the first block of the swap file :
* - file is modified or not : buf - > b_changed
* - ' fileformat '
* - ' fileencoding '
*/
2004-06-13 20:20:40 +00:00
void
2016-01-30 18:51:09 +01:00
ml_setflags ( buf_T * buf )
2004-06-13 20:20:40 +00:00
{
bhdr_T * hp ;
ZERO_BL * b0p ;
if ( ! buf - > b_ml . ml_mfp )
return ;
for ( hp = buf - > b_ml . ml_mfp - > mf_used_last ; hp ! = NULL ; hp = hp - > bh_prev )
{
if ( hp - > bh_bnum = = 0 )
{
b0p = ( ZERO_BL * ) ( hp - > bh_data ) ;
2004-12-19 22:46:22 +00:00
b0p - > b0_dirty = buf - > b_changed ? B0_DIRTY : 0 ;
b0p - > b0_flags = ( b0p - > b0_flags & ~ B0_FF_MASK )
| ( get_fileformat ( buf ) + 1 ) ;
add_b0_fenc ( b0p , buf ) ;
2004-06-13 20:20:40 +00:00
hp - > bh_flags | = BH_DIRTY ;
mf_sync ( buf - > b_ml . ml_mfp , MFS_ZERO ) ;
break ;
}
}
}
2010-06-21 06:15:46 +02:00
# if defined(FEAT_CRYPT) || defined(PROTO)
/*
* If " data " points to a data block encrypt the text in it and return a copy
* in allocated memory . Return NULL when out of memory .
* Otherwise return " data " .
*/
char_u *
2016-01-30 18:51:09 +01:00
ml_encrypt_data (
memfile_T * mfp ,
char_u * data ,
2016-07-01 17:17:39 +02:00
off_T offset ,
2016-01-30 18:51:09 +01:00
unsigned size )
2010-06-21 06:15:46 +02:00
{
DATA_BL * dp = ( DATA_BL * ) data ;
char_u * head_end ;
char_u * text_start ;
char_u * new_data ;
int text_len ;
2014-08-10 13:38:34 +02:00
cryptstate_T * state ;
2010-06-21 06:15:46 +02:00
if ( dp - > db_id ! = DATA_ID )
return data ;
2015-06-09 18:35:25 +02:00
state = ml_crypt_prepare ( mfp , offset , FALSE ) ;
if ( state = = NULL )
return data ;
2019-05-28 23:08:19 +02:00
new_data = alloc ( size ) ;
2010-06-21 06:15:46 +02:00
if ( new_data = = NULL )
2024-04-05 20:12:19 +02:00
{
crypt_free_state ( state ) ;
2010-06-21 06:15:46 +02:00
return NULL ;
2024-04-05 20:12:19 +02:00
}
2010-06-21 06:15:46 +02:00
head_end = ( char_u * ) ( & dp - > db_index [ dp - > db_line_count ] ) ;
text_start = ( char_u * ) dp + dp - > db_txt_start ;
text_len = size - dp - > db_txt_start ;
2019-12-04 21:57:43 +01:00
// Copy the header and the text.
2010-06-21 06:15:46 +02:00
mch_memmove ( new_data , dp , head_end - ( char_u * ) dp ) ;
2019-12-04 21:57:43 +01:00
// Encrypt the text.
2021-06-20 14:02:16 +02:00
crypt_encode ( state , text_start , text_len , new_data + dp - > db_txt_start ,
FALSE ) ;
2014-08-10 13:38:34 +02:00
crypt_free_state ( state ) ;
2010-06-21 06:15:46 +02:00
2019-12-04 21:57:43 +01:00
// Clear the gap.
2010-06-21 06:15:46 +02:00
if ( head_end < text_start )
vim_memset ( new_data + ( head_end - data ) , 0 , text_start - head_end ) ;
return new_data ;
}
/*
2015-06-09 18:35:25 +02:00
* Decrypt the text in " data " if it points to an encrypted data block .
2010-06-21 06:15:46 +02:00
*/
void
2016-01-30 18:51:09 +01:00
ml_decrypt_data (
memfile_T * mfp ,
char_u * data ,
2016-07-01 17:17:39 +02:00
off_T offset ,
2016-01-30 18:51:09 +01:00
unsigned size )
2010-06-21 06:15:46 +02:00
{
DATA_BL * dp = ( DATA_BL * ) data ;
char_u * head_end ;
char_u * text_start ;
int text_len ;
2014-08-10 13:38:34 +02:00
cryptstate_T * state ;
2010-06-21 06:15:46 +02:00
2023-01-14 12:32:28 +00:00
if ( dp - > db_id ! = DATA_ID )
return ;
head_end = ( char_u * ) ( & dp - > db_index [ dp - > db_line_count ] ) ;
text_start = ( char_u * ) dp + dp - > db_txt_start ;
text_len = dp - > db_txt_end - dp - > db_txt_start ;
2010-06-21 06:15:46 +02:00
2023-01-14 12:32:28 +00:00
if ( head_end > text_start | | dp - > db_txt_start > size
| | dp - > db_txt_end > size )
return ; // data was messed up
2010-06-21 06:15:46 +02:00
2023-01-14 12:32:28 +00:00
state = ml_crypt_prepare ( mfp , offset , TRUE ) ;
if ( state = = NULL )
return ;
// Decrypt the text in place.
crypt_decode_inplace ( state , text_start , text_len , FALSE ) ;
crypt_free_state ( state ) ;
2010-06-21 06:15:46 +02:00
}
/*
* Prepare for encryption / decryption , using the key , seed and offset .
2014-08-10 13:38:34 +02:00
* Return an allocated cryptstate_T * .
2023-04-23 17:50:22 +01:00
* Note : Encryption not supported for SODIUM
2010-06-21 06:15:46 +02:00
*/
2014-08-10 13:38:34 +02:00
static cryptstate_T *
2016-07-01 17:17:39 +02:00
ml_crypt_prepare ( memfile_T * mfp , off_T offset , int reading )
2010-06-21 06:15:46 +02:00
{
buf_T * buf = mfp - > mf_buffer ;
char_u salt [ 50 ] ;
2014-08-10 13:38:34 +02:00
int method_nr ;
2010-06-21 06:15:46 +02:00
char_u * key ;
2023-04-23 17:50:22 +01:00
crypt_arg_T arg ;
2010-06-21 06:15:46 +02:00
2023-04-23 17:50:22 +01:00
CLEAR_FIELD ( arg ) ;
2010-06-21 06:15:46 +02:00
if ( reading & & mfp - > mf_old_key ! = NULL )
{
2019-12-04 21:57:43 +01:00
// Reading back blocks with the previous key/method/seed.
2014-08-10 13:38:34 +02:00
method_nr = mfp - > mf_old_cm ;
2010-06-21 06:15:46 +02:00
key = mfp - > mf_old_key ;
2023-04-23 17:50:22 +01:00
arg . cat_seed = mfp - > mf_old_seed ;
2010-06-21 06:15:46 +02:00
}
else
{
2014-08-10 13:38:34 +02:00
method_nr = crypt_get_method_nr ( buf ) ;
2010-06-21 06:15:46 +02:00
key = buf - > b_p_key ;
2023-04-23 17:50:22 +01:00
arg . cat_seed = mfp - > mf_seed ;
2010-06-21 06:15:46 +02:00
}
2023-04-23 17:50:22 +01:00
2015-06-09 18:35:25 +02:00
if ( * key = = NUL )
return NULL ;
2010-06-21 06:15:46 +02:00
2023-06-27 18:57:10 +01:00
if ( crypt_may_close_swapfile ( buf , key , method_nr ) )
return NULL ;
2014-08-10 13:38:34 +02:00
if ( method_nr = = CRYPT_M_ZIP )
2010-06-21 06:15:46 +02:00
{
2019-12-04 21:57:43 +01:00
// For PKzip: Append the offset to the key, so that we use a different
// key for every block.
2010-06-21 06:15:46 +02:00
vim_snprintf ( ( char * ) salt , sizeof ( salt ) , " %s%ld " , key , ( long ) offset ) ;
2023-04-23 17:50:22 +01:00
arg . cat_seed = NULL ;
arg . cat_init_from_file = FALSE ;
return crypt_create ( method_nr , salt , & arg ) ;
2010-06-21 06:15:46 +02:00
}
2014-08-10 13:38:34 +02:00
2019-12-04 21:57:43 +01:00
// Using blowfish or better: add salt and seed. We use the byte offset
// of the block for the salt.
2014-08-10 13:38:34 +02:00
vim_snprintf ( ( char * ) salt , sizeof ( salt ) , " %ld " , ( long ) offset ) ;
2023-04-23 17:50:22 +01:00
arg . cat_salt = salt ;
arg . cat_salt_len = ( int ) STRLEN ( salt ) ;
arg . cat_seed_len = MF_SEED_LEN ;
arg . cat_add_len = 0 ;
arg . cat_add = NULL ;
arg . cat_init_from_file = FALSE ;
return crypt_create ( method_nr , key , & arg ) ;
2010-06-21 06:15:46 +02:00
}
# endif
2004-06-13 20:20:40 +00:00
# if defined(FEAT_BYTEOFF) || defined(PROTO)
2019-12-04 21:57:43 +01:00
# define MLCS_MAXL 800 // max no of lines in chunk
# define MLCS_MINL 400 // should be half of MLCS_MAXL
2004-06-13 20:20:40 +00:00
/*
2010-07-25 14:00:46 +02:00
* Keep information for finding byte offset of a line , updtype may be one of :
2004-06-13 20:20:40 +00:00
* ML_CHNK_ADDLINE : Add len to parent chunk , possibly splitting it
* Careful : ML_CHNK_ADDLINE may cause ml_find_line ( ) to be called .
* ML_CHNK_DELLINE : Subtract len from parent chunk , possibly deleting it
* ML_CHNK_UPDLINE : Add len to parent chunk , as a signed entity .
*/
static void
2016-01-30 18:51:09 +01:00
ml_updatechunk (
buf_T * buf ,
linenr_T line ,
long len ,
int updtype )
2004-06-13 20:20:40 +00:00
{
static buf_T * ml_upd_lastbuf = NULL ;
static linenr_T ml_upd_lastline ;
static linenr_T ml_upd_lastcurline ;
static int ml_upd_lastcurix ;
linenr_T curline = ml_upd_lastcurline ;
int curix = ml_upd_lastcurix ;
long size ;
chunksize_T * curchnk ;
int rest ;
bhdr_T * hp ;
DATA_BL * dp ;
if ( buf - > b_ml . ml_usedchunks = = - 1 | | len = = 0 )
return ;
if ( buf - > b_ml . ml_chunksize = = NULL )
{
2019-05-28 23:08:19 +02:00
buf - > b_ml . ml_chunksize = ALLOC_MULT ( chunksize_T , 100 ) ;
2004-06-13 20:20:40 +00:00
if ( buf - > b_ml . ml_chunksize = = NULL )
{
buf - > b_ml . ml_usedchunks = - 1 ;
return ;
}
buf - > b_ml . ml_numchunks = 100 ;
buf - > b_ml . ml_usedchunks = 1 ;
buf - > b_ml . ml_chunksize [ 0 ] . mlcs_numlines = 1 ;
buf - > b_ml . ml_chunksize [ 0 ] . mlcs_totalsize = 1 ;
}
if ( updtype = = ML_CHNK_UPDLINE & & buf - > b_ml . ml_line_count = = 1 )
{
/*
* First line in empty buffer from ml_flush_line ( ) - - reset
*/
buf - > b_ml . ml_usedchunks = 1 ;
buf - > b_ml . ml_chunksize [ 0 ] . mlcs_numlines = 1 ;
2018-12-13 22:20:09 +01:00
buf - > b_ml . ml_chunksize [ 0 ] . mlcs_totalsize = ( long ) buf - > b_ml . ml_line_len ;
2004-06-13 20:20:40 +00:00
return ;
}
/*
* Find chunk that our line belongs to , curline will be at start of the
* chunk .
*/
if ( buf ! = ml_upd_lastbuf | | line ! = ml_upd_lastline + 1
| | updtype ! = ML_CHNK_ADDLINE )
{
for ( curline = 1 , curix = 0 ;
curix < buf - > b_ml . ml_usedchunks - 1
& & line > = curline + buf - > b_ml . ml_chunksize [ curix ] . mlcs_numlines ;
curix + + )
curline + = buf - > b_ml . ml_chunksize [ curix ] . mlcs_numlines ;
}
2018-10-30 22:15:55 +01:00
else if ( curix < buf - > b_ml . ml_usedchunks - 1
& & line > = curline + buf - > b_ml . ml_chunksize [ curix ] . mlcs_numlines )
2004-06-13 20:20:40 +00:00
{
2019-12-04 21:57:43 +01:00
// Adjust cached curix & curline
2004-06-13 20:20:40 +00:00
curline + = buf - > b_ml . ml_chunksize [ curix ] . mlcs_numlines ;
curix + + ;
}
curchnk = buf - > b_ml . ml_chunksize + curix ;
if ( updtype = = ML_CHNK_DELLINE )
2006-11-01 17:12:57 +00:00
len = - len ;
2004-06-13 20:20:40 +00:00
curchnk - > mlcs_totalsize + = len ;
if ( updtype = = ML_CHNK_ADDLINE )
{
curchnk - > mlcs_numlines + + ;
2019-12-04 21:57:43 +01:00
// May resize here so we don't have to do it in both cases below
2004-06-13 20:20:40 +00:00
if ( buf - > b_ml . ml_usedchunks + 1 > = buf - > b_ml . ml_numchunks )
{
2015-02-10 18:34:01 +01:00
chunksize_T * t_chunksize = buf - > b_ml . ml_chunksize ;
2004-06-13 20:20:40 +00:00
buf - > b_ml . ml_numchunks = buf - > b_ml . ml_numchunks * 3 / 2 ;
2020-08-22 15:19:18 +02:00
buf - > b_ml . ml_chunksize = vim_realloc ( buf - > b_ml . ml_chunksize ,
2004-06-13 20:20:40 +00:00
sizeof ( chunksize_T ) * buf - > b_ml . ml_numchunks ) ;
if ( buf - > b_ml . ml_chunksize = = NULL )
{
2019-12-04 21:57:43 +01:00
// Hmmmm, Give up on offset for this buffer
2015-02-10 18:34:01 +01:00
vim_free ( t_chunksize ) ;
2004-06-13 20:20:40 +00:00
buf - > b_ml . ml_usedchunks = - 1 ;
return ;
}
}
if ( buf - > b_ml . ml_chunksize [ curix ] . mlcs_numlines > = MLCS_MAXL )
{
2019-12-04 21:57:43 +01:00
int count ; // number of entries in block
2004-06-13 20:20:40 +00:00
int idx ;
2018-12-25 23:15:46 +01:00
int end_idx ;
2004-06-13 20:20:40 +00:00
int text_end ;
int linecnt ;
mch_memmove ( buf - > b_ml . ml_chunksize + curix + 1 ,
buf - > b_ml . ml_chunksize + curix ,
( buf - > b_ml . ml_usedchunks - curix ) *
sizeof ( chunksize_T ) ) ;
2019-12-04 21:57:43 +01:00
// Compute length of first half of lines in the split chunk
2004-06-13 20:20:40 +00:00
size = 0 ;
linecnt = 0 ;
while ( curline < buf - > b_ml . ml_line_count
& & linecnt < MLCS_MINL )
{
if ( ( hp = ml_find_line ( buf , curline , ML_FIND ) ) = = NULL )
{
buf - > b_ml . ml_usedchunks = - 1 ;
return ;
}
dp = ( DATA_BL * ) ( hp - > bh_data ) ;
count = ( long ) ( buf - > b_ml . ml_locked_high ) -
( long ) ( buf - > b_ml . ml_locked_low ) + 1 ;
idx = curline - buf - > b_ml . ml_locked_low ;
curline = buf - > b_ml . ml_locked_high + 1 ;
2018-12-25 23:15:46 +01:00
// compute index of last line to use in this MEMLINE
2004-06-13 20:20:40 +00:00
rest = count - idx ;
if ( linecnt + rest > MLCS_MINL )
{
2018-12-25 23:15:46 +01:00
end_idx = idx + MLCS_MINL - linecnt - 1 ;
2004-06-13 20:20:40 +00:00
linecnt = MLCS_MINL ;
}
else
{
2018-12-25 23:15:46 +01:00
end_idx = count - 1 ;
2004-06-13 20:20:40 +00:00
linecnt + = rest ;
}
2019-11-30 22:48:27 +01:00
# ifdef FEAT_PROP_POPUP
2018-12-25 23:15:46 +01:00
if ( buf - > b_has_textprop )
{
int i ;
// We cannot use the text pointers to get the text length,
// the text prop info would also be counted. Go over the
// lines.
for ( i = end_idx ; i < idx ; + + i )
2022-08-09 21:37:55 +01:00
size + = ( int ) STRLEN ( ( char_u * ) dp
+ ( dp - > db_index [ i ] & DB_INDEX_MASK ) ) + 1 ;
2018-12-25 23:15:46 +01:00
}
else
# endif
{
2021-08-15 14:28:40 +02:00
if ( idx = = 0 ) // first line in block, text at the end
2018-12-25 23:15:46 +01:00
text_end = dp - > db_txt_end ;
else
text_end = ( ( dp - > db_index [ idx - 1 ] ) & DB_INDEX_MASK ) ;
2022-08-09 21:37:55 +01:00
size + = text_end
- ( ( dp - > db_index [ end_idx ] ) & DB_INDEX_MASK ) ;
2018-12-25 23:15:46 +01:00
}
2004-06-13 20:20:40 +00:00
}
buf - > b_ml . ml_chunksize [ curix ] . mlcs_numlines = linecnt ;
buf - > b_ml . ml_chunksize [ curix + 1 ] . mlcs_numlines - = linecnt ;
buf - > b_ml . ml_chunksize [ curix ] . mlcs_totalsize = size ;
buf - > b_ml . ml_chunksize [ curix + 1 ] . mlcs_totalsize - = size ;
buf - > b_ml . ml_usedchunks + + ;
2019-12-04 21:57:43 +01:00
ml_upd_lastbuf = NULL ; // Force recalc of curix & curline
2004-06-13 20:20:40 +00:00
return ;
}
else if ( buf - > b_ml . ml_chunksize [ curix ] . mlcs_numlines > = MLCS_MINL
& & curix = = buf - > b_ml . ml_usedchunks - 1
& & buf - > b_ml . ml_line_count - line < = 1 )
{
/*
2020-12-18 19:49:56 +01:00
* We are in the last chunk and it is cheap to create a new one
2004-06-13 20:20:40 +00:00
* after this . Do it now to avoid the loop above later on
*/
curchnk = buf - > b_ml . ml_chunksize + curix + 1 ;
buf - > b_ml . ml_usedchunks + + ;
if ( line = = buf - > b_ml . ml_line_count )
{
curchnk - > mlcs_numlines = 0 ;
curchnk - > mlcs_totalsize = 0 ;
}
else
{
/*
* Line is just prior to last , move count for last
* This is the common case when loading a new file
*/
hp = ml_find_line ( buf , buf - > b_ml . ml_line_count , ML_FIND ) ;
if ( hp = = NULL )
{
buf - > b_ml . ml_usedchunks = - 1 ;
return ;
}
dp = ( DATA_BL * ) ( hp - > bh_data ) ;
if ( dp - > db_line_count = = 1 )
rest = dp - > db_txt_end - dp - > db_txt_start ;
else
rest =
( ( dp - > db_index [ dp - > db_line_count - 2 ] ) & DB_INDEX_MASK )
- dp - > db_txt_start ;
curchnk - > mlcs_totalsize = rest ;
curchnk - > mlcs_numlines = 1 ;
curchnk [ - 1 ] . mlcs_totalsize - = rest ;
curchnk [ - 1 ] . mlcs_numlines - = 1 ;
}
}
}
else if ( updtype = = ML_CHNK_DELLINE )
{
curchnk - > mlcs_numlines - - ;
2019-12-04 21:57:43 +01:00
ml_upd_lastbuf = NULL ; // Force recalc of curix & curline
2022-08-09 21:37:55 +01:00
if ( curix < buf - > b_ml . ml_usedchunks - 1
& & curchnk - > mlcs_numlines + curchnk [ 1 ] . mlcs_numlines
< = MLCS_MINL )
2004-06-13 20:20:40 +00:00
{
curix + + ;
curchnk = buf - > b_ml . ml_chunksize + curix ;
}
else if ( curix = = 0 & & curchnk - > mlcs_numlines < = 0 )
{
buf - > b_ml . ml_usedchunks - - ;
mch_memmove ( buf - > b_ml . ml_chunksize , buf - > b_ml . ml_chunksize + 1 ,
buf - > b_ml . ml_usedchunks * sizeof ( chunksize_T ) ) ;
return ;
}
else if ( curix = = 0 | | ( curchnk - > mlcs_numlines > 10
2022-08-09 21:37:55 +01:00
& & curchnk - > mlcs_numlines + curchnk [ - 1 ] . mlcs_numlines
> MLCS_MINL ) )
2004-06-13 20:20:40 +00:00
{
return ;
}
2019-12-04 21:57:43 +01:00
// Collapse chunks
2004-06-13 20:20:40 +00:00
curchnk [ - 1 ] . mlcs_numlines + = curchnk - > mlcs_numlines ;
curchnk [ - 1 ] . mlcs_totalsize + = curchnk - > mlcs_totalsize ;
buf - > b_ml . ml_usedchunks - - ;
if ( curix < buf - > b_ml . ml_usedchunks )
mch_memmove ( buf - > b_ml . ml_chunksize + curix ,
buf - > b_ml . ml_chunksize + curix + 1 ,
( buf - > b_ml . ml_usedchunks - curix ) *
sizeof ( chunksize_T ) ) ;
return ;
}
ml_upd_lastbuf = buf ;
ml_upd_lastline = line ;
ml_upd_lastcurline = curline ;
ml_upd_lastcurix = curix ;
}
/*
* Find offset for line or line with offset .
2005-02-22 08:56:13 +00:00
* Find line with offset if " lnum " is 0 ; return remaining offset in offp
* Find offset of line if " lnum " > 0
2004-06-13 20:20:40 +00:00
* return - 1 if information is not available
*/
long
2016-01-30 18:51:09 +01:00
ml_find_line_or_offset ( buf_T * buf , linenr_T lnum , long * offp )
2004-06-13 20:20:40 +00:00
{
linenr_T curline ;
int curix ;
long size ;
bhdr_T * hp ;
DATA_BL * dp ;
2019-12-04 21:57:43 +01:00
int count ; // number of entries in block
2004-06-13 20:20:40 +00:00
int idx ;
int start_idx ;
int text_end ;
long offset ;
int len ;
int ffdos = ( get_fileformat ( buf ) = = EOL_DOS ) ;
int extra = 0 ;
2019-12-04 21:57:43 +01:00
// take care of cached line first
2005-02-22 08:56:13 +00:00
ml_flush_line ( curbuf ) ;
2004-06-13 20:20:40 +00:00
if ( buf - > b_ml . ml_usedchunks = = - 1
| | buf - > b_ml . ml_chunksize = = NULL
2005-02-22 08:56:13 +00:00
| | lnum < 0 )
2004-06-13 20:20:40 +00:00
return - 1 ;
if ( offp = = NULL )
offset = 0 ;
else
offset = * offp ;
2005-02-22 08:56:13 +00:00
if ( lnum = = 0 & & offset < = 0 )
2019-12-04 21:57:43 +01:00
return 1 ; // Not a "find offset" and offset 0 _must_ be in line 1
2004-06-13 20:20:40 +00:00
/*
* Find the last chunk before the one containing our line . Last chunk is
2021-08-15 14:28:40 +02:00
* special because it will never qualify .
2004-06-13 20:20:40 +00:00
*/
curline = 1 ;
curix = size = 0 ;
while ( curix < buf - > b_ml . ml_usedchunks - 1
2005-02-22 08:56:13 +00:00
& & ( ( lnum ! = 0
& & lnum > = curline + buf - > b_ml . ml_chunksize [ curix ] . mlcs_numlines )
2004-06-13 20:20:40 +00:00
| | ( offset ! = 0
& & offset > size + buf - > b_ml . ml_chunksize [ curix ] . mlcs_totalsize
2022-01-29 15:19:23 +00:00
+ ( long ) ffdos * buf - > b_ml . ml_chunksize [ curix ] . mlcs_numlines ) ) )
2004-06-13 20:20:40 +00:00
{
curline + = buf - > b_ml . ml_chunksize [ curix ] . mlcs_numlines ;
size + = buf - > b_ml . ml_chunksize [ curix ] . mlcs_totalsize ;
if ( offset & & ffdos )
size + = buf - > b_ml . ml_chunksize [ curix ] . mlcs_numlines ;
curix + + ;
}
2005-02-22 08:56:13 +00:00
while ( ( lnum ! = 0 & & curline < lnum ) | | ( offset ! = 0 & & size < offset ) )
2004-06-13 20:20:40 +00:00
{
2021-01-30 17:16:28 +01:00
# ifdef FEAT_PROP_POPUP
size_t textprop_total = 0 ;
# endif
2004-06-13 20:20:40 +00:00
if ( curline > buf - > b_ml . ml_line_count
| | ( hp = ml_find_line ( buf , curline , ML_FIND ) ) = = NULL )
return - 1 ;
dp = ( DATA_BL * ) ( hp - > bh_data ) ;
count = ( long ) ( buf - > b_ml . ml_locked_high ) -
( long ) ( buf - > b_ml . ml_locked_low ) + 1 ;
start_idx = idx = curline - buf - > b_ml . ml_locked_low ;
2020-01-13 20:40:51 +01:00
if ( idx = = 0 ) // first line in block, text at the end
2004-06-13 20:20:40 +00:00
text_end = dp - > db_txt_end ;
else
text_end = ( ( dp - > db_index [ idx - 1 ] ) & DB_INDEX_MASK ) ;
2019-12-04 21:57:43 +01:00
// Compute index of last line to use in this MEMLINE
2005-02-22 08:56:13 +00:00
if ( lnum ! = 0 )
2004-06-13 20:20:40 +00:00
{
2005-02-22 08:56:13 +00:00
if ( curline + ( count - idx ) > = lnum )
idx + = lnum - curline - 1 ;
2004-06-13 20:20:40 +00:00
else
idx = count - 1 ;
}
else
{
extra = 0 ;
2020-01-13 20:40:51 +01:00
for ( ; ; )
2004-06-13 20:20:40 +00:00
{
2020-01-13 20:40:51 +01:00
# ifdef FEAT_PROP_POPUP
2021-01-30 17:16:28 +01:00
size_t textprop_size = 0 ;
2020-01-13 20:40:51 +01:00
if ( buf - > b_has_textprop )
{
2021-01-30 17:16:28 +01:00
char_u * l1 , * l2 ;
2020-01-13 20:40:51 +01:00
// compensate for the extra bytes taken by textprops
l1 = ( char_u * ) dp + ( ( dp - > db_index [ idx ] ) & DB_INDEX_MASK ) ;
l2 = ( char_u * ) dp + ( idx = = 0 ? dp - > db_txt_end
: ( ( dp - > db_index [ idx - 1 ] ) & DB_INDEX_MASK ) ) ;
textprop_size = ( l2 - l1 ) - ( STRLEN ( l1 ) + 1 ) ;
}
# endif
if ( ! ( offset > = size
+ text_end - ( int ) ( ( dp - > db_index [ idx ] ) & DB_INDEX_MASK )
# ifdef FEAT_PROP_POPUP
2020-01-17 21:00:59 +01:00
- ( long ) ( textprop_total + textprop_size )
2020-01-13 20:40:51 +01:00
# endif
+ ffdos ) )
break ;
2004-06-13 20:20:40 +00:00
if ( ffdos )
size + + ;
2020-01-13 20:40:51 +01:00
# ifdef FEAT_PROP_POPUP
textprop_total + = textprop_size ;
# endif
2004-06-13 20:20:40 +00:00
if ( idx = = count - 1 )
{
extra = 1 ;
break ;
}
idx + + ;
}
}
2019-11-30 22:48:27 +01:00
# ifdef FEAT_PROP_POPUP
2021-01-30 17:16:28 +01:00
if ( buf - > b_has_textprop & & lnum ! = 0 )
2018-12-25 23:15:46 +01:00
{
int i ;
// cannot use the db_index pointer, need to get the actual text
// lengths.
len = 0 ;
for ( i = start_idx ; i < = idx ; + + i )
2021-01-30 17:16:28 +01:00
{
char_u * p = ( char_u * ) dp + ( ( dp - > db_index [ i ] ) & DB_INDEX_MASK ) ;
len + = ( int ) STRLEN ( p ) + 1 ;
}
2018-12-25 23:15:46 +01:00
}
else
# endif
2021-01-30 17:16:28 +01:00
len = text_end - ( ( dp - > db_index [ idx ] ) & DB_INDEX_MASK )
# ifdef FEAT_PROP_POPUP
- ( long ) textprop_total
# endif
;
2004-06-13 20:20:40 +00:00
size + = len ;
if ( offset ! = 0 & & size > = offset )
{
if ( size + ffdos = = offset )
* offp = 0 ;
else if ( idx = = start_idx )
* offp = offset - size + len ;
else
* offp = offset - size + len
2021-01-30 17:16:28 +01:00
- ( text_end - ( ( dp - > db_index [ idx - 1 ] ) & DB_INDEX_MASK ) )
# ifdef FEAT_PROP_POPUP
+ ( long ) textprop_total
# endif
;
2004-06-13 20:20:40 +00:00
curline + = idx - start_idx + extra ;
if ( curline > buf - > b_ml . ml_line_count )
2019-12-04 21:57:43 +01:00
return - 1 ; // exactly one byte beyond the end
2004-06-13 20:20:40 +00:00
return curline ;
}
curline = buf - > b_ml . ml_locked_high + 1 ;
}
2005-02-22 08:56:13 +00:00
if ( lnum ! = 0 )
2004-07-26 12:53:41 +00:00
{
2019-12-04 21:57:43 +01:00
// Count extra CR characters.
2004-07-26 12:53:41 +00:00
if ( ffdos )
2005-02-22 08:56:13 +00:00
size + = lnum - 1 ;
2004-07-26 12:53:41 +00:00
2019-12-04 21:57:43 +01:00
// Don't count the last line break if 'noeol' and ('bin' or
// 'nofixeol').
2015-07-17 14:18:08 +02:00
if ( ( ! buf - > b_p_fixeol | | buf - > b_p_bin ) & & ! buf - > b_p_eol
2018-08-20 22:53:04 +02:00
& & lnum > buf - > b_ml . ml_line_count )
2004-07-26 12:53:41 +00:00
size - = ffdos + 1 ;
}
2004-06-13 20:20:40 +00:00
return size ;
}
/*
* Goto byte in buffer with offset ' cnt ' .
*/
void
2016-01-30 18:51:09 +01:00
goto_byte ( long cnt )
2004-06-13 20:20:40 +00:00
{
long boff = cnt ;
linenr_T lnum ;
2019-12-04 21:57:43 +01:00
ml_flush_line ( curbuf ) ; // cached line may be dirty
2004-06-13 20:20:40 +00:00
setpcmark ( ) ;
if ( boff )
- - boff ;
lnum = ml_find_line_or_offset ( curbuf , ( linenr_T ) 0 , & boff ) ;
2019-12-04 21:57:43 +01:00
if ( lnum < 1 ) // past the end
2004-06-13 20:20:40 +00:00
{
curwin - > w_cursor . lnum = curbuf - > b_ml . ml_line_count ;
curwin - > w_curswant = MAXCOL ;
coladvance ( ( colnr_T ) MAXCOL ) ;
}
else
{
curwin - > w_cursor . lnum = lnum ;
curwin - > w_cursor . col = ( colnr_T ) boff ;
2005-12-02 00:50:49 +00:00
curwin - > w_cursor . coladd = 0 ;
2004-06-13 20:20:40 +00:00
curwin - > w_set_curswant = TRUE ;
}
check_cursor ( ) ;
2019-12-04 21:57:43 +01:00
// Make sure the cursor is on the first byte of a multi-byte char.
2004-06-13 20:20:40 +00:00
if ( has_mbyte )
mb_adjust_cursor ( ) ;
}
# endif