Annotation of src/usr.bin/vim/memline.c, Revision 1.2
1.2 ! downsj 1: /* $OpenBSD: memline.c,v 1.1.1.1 1996/09/07 21:40:25 downsj Exp $ */
1.1 downsj 2: /* vi:set ts=4 sw=4:
3: *
4: * VIM - Vi IMproved by Bram Moolenaar
5: *
6: * Do ":help uganda" in Vim to read copying and usage conditions.
7: * Do ":help credits" in Vim to see a list of people who contributed.
8: */
9:
10: /* for debugging */
11: /* #define CHECK(c, s) if (c) EMSG(s) */
12: #define CHECK(c, s)
13:
14: /*
15: * memline.c: Contains the functions for appending, deleting and changing the
16: * text lines. The memfile functions are used to store the information in blocks
17: * of memory, backed up by a file. The structure of the information is a tree.
18: * The root of the tree is a pointer block. The leaves of the tree are data
19: * blocks. In between may be several layers of pointer blocks, forming branches.
20: *
21: * Three types of blocks are used:
22: * - Block nr 0 contains information for recovery
23: * - Pointer blocks contain list of pointers to other blocks.
24: * - Data blocks contain the actual text.
25: *
26: * Block nr 0 contains the block0 structure (see below).
27: *
28: * Block nr 1 is the first pointer block. It is the root of the tree.
29: * Other pointer blocks are branches.
30: *
31: * If a line is too big to fit in a single page, the block
32: * containing that line is made big enough to hold the line. It may span
33: * several pages. Otherwise all blocks are one page.
34: *
35: * A data block that was filled when starting to edit a file and was not
36: * changed since then, can have a negative block number. This means that it
37: * has not yet been assigned a place in the file. When recovering, the lines
38: * in this data block can be read from the original file. When the block is
39: * changed (lines appended/deleted/changed) or when it is flushed it gets
40: * a positive number. Use mf_trans_del() to get the new number, before
41: * calling mf_get().
42: */
43:
44: /*
45: * The following functions are available to work with a memline:
46: *
47: * ml_open() open a new memline for a buffer
48: * ml_setname() change the file name for a memline
49: * ml_close() close the memline for a buffer
50: * ml_close_all() close all memlines
51: * ml_recover() read a memline for recovery
52: * ml_sync_all() flush changed blocks to file for all files
53: * ml_preserve() flush everything into the file for one file
54: * ml_get() get a pointer to a line
55: * ml_get_pos() get a pointer to a position in a line
56: * ml_get_cursor() get a pointer to the char under the cursor
57: * ml_get_buf() get a pointer to a line in a specific buffer
58: * ml_line_alloced() return TRUE if line was allocated
59: * ml_append() append a new line
60: * ml_replace() replace a line
61: * ml_delete() delete a line
62: * ml_setmarked() set mark for a line (for :global command)
63: * ml_firstmarked() get first line with a mark (for :global command)
64: * ml_clearmarked() clear all line marks (for :global command)
65: */
66:
67: #if defined MSDOS || defined WIN32
68: # include <io.h>
69: #endif
70:
71: #include "vim.h"
72: #include "globals.h"
73: #include "proto.h"
74: #include "option.h"
75: #ifdef HAVE_FCNTL_H
76: # include <fcntl.h>
77: #endif
78: #ifndef UNIX /* it's in unix.h for Unix */
79: # include <time.h>
80: #endif
81:
82: #ifdef SASC
83: # include <proto/dos.h> /* for Open() and Close() */
84: #endif
85:
86: typedef struct block0 ZERO_BL; /* contents of the first block */
87: typedef struct pointer_block PTR_BL; /* contents of a pointer block */
88: typedef struct data_block DATA_BL; /* contents of a data block */
89: typedef struct pointer_entry PTR_EN; /* block/line-count pair */
90:
91: #define DATA_ID (('d' << 8) + 'a') /* data block id */
92: #define PTR_ID (('p' << 8) + 't') /* pointer block id */
93: #define BLOCK0_ID0 'b' /* block 0 id 0 */
94: #define BLOCK0_ID1 '0' /* block 0 id 1 */
95:
96: /*
97: * pointer to a block, used in a pointer block
98: */
99: struct pointer_entry
100: {
101: blocknr_t pe_bnum; /* block number */
102: linenr_t pe_line_count; /* number of lines in this branch */
103: linenr_t pe_old_lnum; /* lnum for this block (for recovery) */
104: int pe_page_count; /* number of pages in block pe_bnum */
105: };
106:
107: /*
108: * A pointer block contains a list of branches in the tree.
109: */
110: struct pointer_block
111: {
112: short_u pb_id; /* ID for pointer block: PTR_ID */
113: short_u pb_count; /* number of pointer in this block */
114: short_u pb_count_max; /* maximum value for pb_count */
115: PTR_EN pb_pointer[1]; /* list of pointers to blocks (actually longer)
116: * followed by empty space until end of page */
117: };
118:
119: /*
120: * A data block is a leaf in the tree.
121: *
122: * The text of the lines is at the end of the block. The text of the first line
123: * in the block is put at the end, the text of the second line in front of it,
124: * etc. Thus the order of the lines is the opposite of the line number.
125: */
126: struct data_block
127: {
128: short_u db_id; /* ID for data block: DATA_ID */
129: unsigned db_free; /* free space available */
130: unsigned db_txt_start; /* byte where text starts */
131: unsigned db_txt_end; /* byte just after data block */
132: linenr_t db_line_count; /* number of lines in this block */
133: unsigned db_index[1]; /* index for start of line (actually bigger)
134: * followed by empty space upto db_txt_start
135: * followed by the text in the lines until
136: * end of page */
137: };
138:
139: /*
140: * The low bits of db_index hold the actual index. The topmost bit is
141: * used for the global command to be able to mark a line.
142: * This method is not clean, but otherwise there would be at least one extra
143: * byte used for each line.
144: * The mark has to be in this place to keep it with the correct line when other
145: * lines are inserted or deleted.
146: */
147: #define DB_MARKED ((unsigned)1 << ((sizeof(unsigned) * 8) - 1))
148: #define DB_INDEX_MASK (~DB_MARKED)
149:
150: #define INDEX_SIZE (sizeof(unsigned)) /* size of one db_index entry */
151: #define HEADER_SIZE (sizeof(DATA_BL) - INDEX_SIZE) /* size of data block header */
152:
153: #define B0_FNAME_SIZE 900
154: #define B0_UNAME_SIZE 40
155: #define B0_HNAME_SIZE 40
156: /*
157: * Restrict the numbers to 32 bits, otherwise most compilers will complain.
158: * This won't detect a 64 bit machine that only swaps a byte in the top 32
159: * bits, but that is crazy anyway.
160: */
161: #define B0_MAGIC_LONG 0x30313233
162: #define B0_MAGIC_INT 0x20212223
163: #define B0_MAGIC_SHORT 0x10111213
164: #define B0_MAGIC_CHAR 0x55
165:
166: /*
167: * Block zero holds all info about the swap file.
168: *
169: * NOTE: DEFINITION OF BLOCK 0 SHOULD NOT CHANGE, it makes all existing swap
170: * files unusable!
171: *
172: * If size of block0 changes anyway, adjust minimal block size
173: * in mf_open()!!
174: *
175: * This block is built up of single bytes, to make it portable accros
176: * different machines. b0_magic_* is used to check the byte order and size of
177: * variables, because the rest of the swap is not portable.
178: */
179: struct block0
180: {
181: char_u b0_id[2]; /* id for block 0: BLOCK0_ID0 and BLOCK0_ID1 */
182: char_u b0_version[10]; /* Vim version string */
183: char_u b0_page_size[4];/* number of bytes per page */
184: char_u b0_mtime[4]; /* last modification time of file */
185: char_u b0_ino[4]; /* inode of b0_fname */
186: char_u b0_pid[4]; /* process id of creator (or 0) */
187: char_u b0_uname[B0_UNAME_SIZE]; /* name of user (uid if no name) */
188: char_u b0_hname[B0_HNAME_SIZE]; /* host name (if it has a name) */
189: char_u b0_fname[B0_FNAME_SIZE]; /* name of file being edited */
190: long b0_magic_long; /* check for byte order of long */
191: int b0_magic_int; /* check for byte order of int */
192: short b0_magic_short; /* check for byte order of short */
193: char_u b0_magic_char; /* check for last char */
194: };
195:
196: #define STACK_INCR 5 /* nr of entries added to ml_stack at a time */
197:
198: /*
199: * The line number where the first mark may be is remembered.
200: * If it is 0 there are no marks at all.
201: * (always used for the current buffer only, no buffer change possible while
202: * executing a global command).
203: */
204: static linenr_t lowest_marked = 0;
205:
206: /*
207: * arguments for ml_find_line()
208: */
209: #define ML_DELETE 0x11 /* delete line */
210: #define ML_INSERT 0x12 /* insert line */
211: #define ML_FIND 0x13 /* just find the line */
212: #define ML_FLUSH 0x02 /* flush locked block */
213: #define ML_SIMPLE(x) (x & 0x10) /* DEL, INS or FIND */
214:
215: static void ml_open_file __ARGS((BUF *));
216: static void set_b0_fname __ARGS((ZERO_BL *, BUF *buf));
217: static void swapfile_info __ARGS((char_u *));
218: static int get_names __ARGS((char_u **, char_u *));
219: static int ml_append_int __ARGS((BUF *, linenr_t, char_u *, colnr_t, int));
220: static int ml_delete_int __ARGS((BUF *, linenr_t, int));
221: static char_u *findswapname __ARGS((BUF *, char_u **, char_u *));
222: static void ml_flush_line __ARGS((BUF *));
223: static BHDR *ml_new_data __ARGS((MEMFILE *, int, int));
224: static BHDR *ml_new_ptr __ARGS((MEMFILE *));
225: static BHDR *ml_find_line __ARGS((BUF *, linenr_t, int));
226: static int ml_add_stack __ARGS((BUF *));
227: static char_u *makeswapname __ARGS((BUF *, char_u *));
228: static void ml_lineadd __ARGS((BUF *, int));
229: static int b0_magic_wrong __ARGS((ZERO_BL *));
230: #ifdef CHECK_INODE
231: static int fnamecmp_ino __ARGS((char_u *, char_u *, long));
232: #endif
233: static void long_to_char __ARGS((long, char_u *));
234: static long char_to_long __ARGS((char_u *));
235:
236: /*
237: * open a new memline for 'curbuf'
238: *
239: * return FAIL for failure, OK otherwise
240: */
241: int
242: ml_open()
243: {
244: MEMFILE *mfp;
245: char_u *fname;
246: BHDR *hp = NULL;
247: ZERO_BL *b0p;
248: PTR_BL *pp;
249: DATA_BL *dp;
250: char_u *dirp;
251:
252: /*
253: * init fields in memline struct
254: */
255: curbuf->b_ml.ml_stack_size = 0; /* no stack yet */
256: curbuf->b_ml.ml_stack = NULL; /* no stack yet */
257: curbuf->b_ml.ml_stack_top = 0; /* nothing in the stack */
258: curbuf->b_ml.ml_locked = NULL; /* no cached block */
259: curbuf->b_ml.ml_line_lnum = 0; /* no cached line */
260:
261: /*
262: * Make file name for swap file.
263: * If we are unable to find a file name, mf_fname will be NULL
264: * and the memfile will be in memory only (no recovery possible).
265: * When 'updatecount' is 0 and for help files on Unix there is never a swap
266: * file.
267: */
268: #if defined(UNIX) || defined(__EMX__)
269: /* why only on Unix an exception for help files??? */
270: if (p_uc == 0 || curbuf->b_help)
271: #else
272: if (p_uc == 0)
273: #endif
274: dirp = (char_u *)"";
275: else
276: dirp = p_dir;
277:
278: /*
279: * Open the memfile.
280: *
281: * If no swap file is wanted *dirp will be NUL, try with fname == NULL.
282: * Otherwise try all directories in 'directory' option. If that fails try
283: * with fname == NULL.
284: */
285: for (;;)
286: {
287: if (*dirp == NUL)
288: fname = NULL;
289: else
290: {
291: fname = findswapname(curbuf, &dirp, NULL);
292: if (fname == NULL)
293: continue;
294: }
295: mfp = mf_open(fname, TRUE);
296: /* stop when memfile opened or no other fname to try */
297: if (mfp != NULL || (fname == NULL && *dirp == NUL))
298: break;
299: vim_free(fname);
300: }
301:
302: if (mfp == NULL)
303: goto error;
304: curbuf->b_ml.ml_mfp = mfp;
305: curbuf->b_ml.ml_flags = ML_EMPTY;
306: curbuf->b_ml.ml_line_count = 1;
307:
308: if (!(p_uc == 0 || curbuf->b_help) && mfp->mf_fname == NULL)
309: {
310: need_wait_return = TRUE; /* call wait_return later */
311: ++no_wait_return;
312: (void)EMSG("Unable to open swap file, recovery impossible");
313: --no_wait_return;
314: }
315: #if defined(MSDOS) || defined(WIN32)
316: /*
317: * set full pathname for swap file now, because a ":!cd dir" may
318: * change directory without us knowing it.
319: */
320: mf_fullname(mfp);
321: #endif
322:
323: /*
324: * fill block0 struct and write page 0
325: */
326: if ((hp = mf_new(mfp, FALSE, 1)) == NULL)
327: goto error;
328: if (hp->bh_bnum != 0)
329: {
330: EMSG("didn't get block nr 0?");
331: goto error;
332: }
333: b0p = (ZERO_BL *)(hp->bh_data);
334:
335: b0p->b0_id[0] = BLOCK0_ID0;
336: b0p->b0_id[1] = BLOCK0_ID1;
337: b0p->b0_magic_long = (long)B0_MAGIC_LONG;
338: b0p->b0_magic_int = (int)B0_MAGIC_INT;
339: b0p->b0_magic_short = (short)B0_MAGIC_SHORT;
340: b0p->b0_magic_char = B0_MAGIC_CHAR;
341:
342: STRNCPY(b0p->b0_version, Version, 10);
343: set_b0_fname(b0p, curbuf);
344: long_to_char((long)mfp->mf_page_size, b0p->b0_page_size);
345: (void)mch_get_user_name(b0p->b0_uname, B0_UNAME_SIZE);
346: b0p->b0_uname[B0_UNAME_SIZE - 1] = NUL;
347: mch_get_host_name(b0p->b0_hname, B0_HNAME_SIZE);
348: b0p->b0_hname[B0_HNAME_SIZE - 1] = NUL;
349: long_to_char(mch_get_pid(), b0p->b0_pid);
350:
351: /*
352: * Always sync block number 0 to disk, so we can check the file name in
353: * the swap file in findswapname(). Don't do this for help files though.
354: */
355: mf_put(mfp, hp, TRUE, FALSE);
356: if (!curbuf->b_help)
357: mf_sync(mfp, FALSE, FALSE, FALSE);
358:
359: /*
360: * fill in root pointer block and write page 1
361: */
362: if ((hp = ml_new_ptr(mfp)) == NULL)
363: goto error;
364: if (hp->bh_bnum != 1)
365: {
366: EMSG("didn't get block nr 1?");
367: goto error;
368: }
369: pp = (PTR_BL *)(hp->bh_data);
370: pp->pb_count = 1;
371: pp->pb_pointer[0].pe_bnum = 2;
372: pp->pb_pointer[0].pe_page_count = 1;
373: pp->pb_pointer[0].pe_old_lnum = 1;
374: pp->pb_pointer[0].pe_line_count = 1; /* line count after insertion */
375: mf_put(mfp, hp, TRUE, FALSE);
376:
377: /*
378: * allocate first data block and create an empty line 1.
379: */
380: if ((hp = ml_new_data(mfp, FALSE, 1)) == NULL)
381: goto error;
382: if (hp->bh_bnum != 2)
383: {
384: EMSG("didn't get block nr 2?");
385: goto error;
386: }
387:
388: dp = (DATA_BL *)(hp->bh_data);
389: dp->db_index[0] = --dp->db_txt_start; /* at end of block */
390: dp->db_free -= 1 + INDEX_SIZE;
391: dp->db_line_count = 1;
392: *((char_u *)dp + dp->db_txt_start) = NUL; /* emtpy line */
393:
394: return OK;
395:
396: error:
397: if (mfp != NULL)
398: {
399: if (hp)
400: mf_put(mfp, hp, FALSE, FALSE);
401: mf_close(mfp, TRUE); /* will also free(mfp->mf_fname) */
402: }
403: else
404: vim_free(fname);
405: curbuf->b_ml.ml_mfp = NULL;
406: return FAIL;
407: }
408:
409: /*
410: * ml_setname() is called when the file name has been changed.
411: * It may rename the swap file.
412: */
413: void
414: ml_setname()
415: {
416: int success = FALSE;
417: MEMFILE *mfp;
418: char_u *fname;
419: char_u *dirp;
420:
421: /*
422: * When 'updatecount' is 0 there is never a swap file.
423: * For help files we will make a swap file now.
424: */
425: if (p_uc == 0)
426: return;
427:
428: mfp = curbuf->b_ml.ml_mfp;
429: if (mfp->mf_fd < 0) /* there is no swap file yet */
430: {
431: ml_open_file(curbuf); /* create a swap file */
432: return;
433: }
434:
435: /*
436: * Try all directories in the 'directory' option.
437: */
438: dirp = p_dir;
439: for (;;)
440: {
441: if (*dirp == NUL) /* tried all directories, fail */
442: break;
443: fname = findswapname(curbuf, &dirp, mfp->mf_fname);
444: if (fname == NULL) /* no file name found for this dir */
445: continue;
446:
447: #if defined(MSDOS) || defined(WIN32)
448: /*
449: * Set full pathname for swap file now, because a ":!cd dir" may
450: * change directory without us knowing it.
451: */
452: if (!did_cd)
453: {
454: char_u *p;
455:
456: p = FullName_save(fname);
457: vim_free(fname);
458: fname = p;
459: if (fname == NULL)
460: continue;
461: }
462: #endif
463: /* if the file name is the same we don't have to do anything */
464: if (fnamecmp(fname, mfp->mf_fname) == 0)
465: {
466: vim_free(fname);
467: success = TRUE;
468: break;
469: }
470: /* need to close the swap file before renaming */
471: if (mfp->mf_fd >= 0)
472: {
473: close(mfp->mf_fd);
474: mfp->mf_fd = -1;
475: }
476:
477: /* try to rename the swap file */
478: if (vim_rename(mfp->mf_fname, fname) == 0)
479: {
480: success = TRUE;
481: vim_free(mfp->mf_fname);
482: mfp->mf_fname = fname;
483: vim_free(mfp->mf_xfname);
484: mf_set_xfname(mfp);
485: break;
486: }
487: vim_free(fname); /* this fname didn't work, try another */
488: }
489:
490: if (mfp->mf_fd == -1) /* need to (re)open the swap file */
491: {
492: mfp->mf_fd = open((char *)mfp->mf_fname, O_RDWR | O_EXTRA);
493: if (mfp->mf_fd < 0)
494: {
495: /* could not (re)open the swap file, what can we do???? */
496: EMSG("Oops, lost the swap file!!!");
497: return;
498: }
499: }
500: if (!success)
501: EMSG("Could not rename swap file");
502: }
503:
504: /*
505: * Open a file for the memfile for all buffers.
506: * Used when 'updatecount' changes from zero to non-zero.
507: */
508: void
509: ml_open_files()
510: {
511: BUF *buf;
512:
513: for (buf = firstbuf; buf != NULL; buf = buf->b_next)
514: ml_open_file(buf);
515: }
516:
517: /*
518: * Open a swap file for an existing memfile, if there is non swap file yet.
519: */
520: static void
521: ml_open_file(buf)
522: BUF *buf;
523: {
524: MEMFILE *mfp;
525: char_u *fname;
526: char_u *dirp;
527:
528: mfp = buf->b_ml.ml_mfp;
529: if (mfp == NULL || mfp->mf_fd >= 0) /* nothing to do */
530: return;
531:
532: dirp = p_dir;
533: /*
534: * Try all directories in 'directory' option.
535: */
536: for (;;)
537: {
538: if (*dirp == NUL)
539: break;
540: fname = findswapname(buf, &dirp, NULL);
541: if (fname == NULL)
542: continue;
543: if (mf_open_file(mfp, fname) == OK)
544: break;
545: vim_free(fname);
546: }
547:
548: if (mfp->mf_fname == NULL) /* Failed! */
549: {
550: need_wait_return = TRUE; /* call wait_return later */
551: ++no_wait_return;
552: (void)EMSG2("Unable to open swap file for \"%s\", recovery impossible",
553: buf->b_xfilename == NULL ? (char_u *)"No File"
554: : buf->b_xfilename);
555: --no_wait_return;
556: }
557: #if defined(MSDOS) || defined(WIN32)
558: /*
559: * set full pathname for swap file now, because a ":!cd dir" may
560: * change directory without us knowing it.
561: */
562: else
563: mf_fullname(mfp);
564: #endif
565: }
566:
567: /*
568: * Close memline for buffer 'buf'.
569: * If 'del_file' is TRUE, delete the swap file
570: */
571: void
572: ml_close(buf, del_file)
573: BUF *buf;
574: int del_file;
575: {
576: if (buf->b_ml.ml_mfp == NULL) /* not open */
577: return;
578: mf_close(buf->b_ml.ml_mfp, del_file); /* close the .swp file */
579: if (buf->b_ml.ml_line_lnum != 0 && (buf->b_ml.ml_flags & ML_LINE_DIRTY))
580: vim_free(buf->b_ml.ml_line_ptr);
581: vim_free(buf->b_ml.ml_stack);
582: buf->b_ml.ml_mfp = NULL;
583: }
584:
585: /*
586: * Close all existing memlines and memfiles.
587: * Used when exiting.
588: * When 'del_file' is TRUE, delete the memfiles.
589: */
590: void
591: ml_close_all(del_file)
592: int del_file;
593: {
594: BUF *buf;
595:
596: for (buf = firstbuf; buf != NULL; buf = buf->b_next)
597: ml_close(buf, del_file);
598: }
599:
600: /*
601: * Close all memfiles for not modified buffers.
602: * Only use just before exiting!
603: */
604: void
605: ml_close_notmod()
606: {
607: BUF *buf;
608:
609: for (buf = firstbuf; buf != NULL; buf = buf->b_next)
610: if (!buf->b_changed)
611: ml_close(buf, TRUE); /* close all not-modified buffers */
612: }
613:
614: /*
615: * Update the timestamp in the .swp file.
616: * Used when the file has been written.
617: */
618: void
619: ml_timestamp(buf)
620: BUF *buf;
621: {
622: MEMFILE *mfp;
623: BHDR *hp;
624: ZERO_BL *b0p;
625:
626: mfp = buf->b_ml.ml_mfp;
627:
628: if (mfp == NULL || (hp = mf_get(mfp, (blocknr_t)0, 1)) == NULL)
629: return;
630: b0p = (ZERO_BL *)(hp->bh_data);
631: if (b0p->b0_id[0] != BLOCK0_ID0 || b0p->b0_id[1] != BLOCK0_ID1)
632: EMSG("ml_timestamp: Didn't get block 0??");
633: else
634: set_b0_fname(b0p, buf);
635: mf_put(mfp, hp, TRUE, FALSE);
636: }
637:
638: /*
639: * Write file name and timestamp into block 0 of a swap file.
640: * Also set buf->b_mtime.
641: * Don't use NameBuff[]!!!
642: */
643: static void
644: set_b0_fname(b0p, buf)
645: ZERO_BL *b0p;
646: BUF *buf;
647: {
648: struct stat st;
649: size_t flen, ulen;
650: char_u uname[B0_UNAME_SIZE];
651:
652: if (buf->b_filename == NULL)
653: b0p->b0_fname[0] = NUL;
654: else
655: {
656: /*
657: * For a file under the home directory of the current user, we try to
658: * replace the home directory path with "~user". This helps when
659: * editing the same file on different machines over a network.
660: * First replace home dir path with "~/" with home_replace().
661: * Then insert the user name to get "~user/".
662: */
663: home_replace(NULL, buf->b_filename, b0p->b0_fname, B0_FNAME_SIZE);
664: if (b0p->b0_fname[0] == '~')
665: {
666: flen = STRLEN(b0p->b0_fname);
667: /* If there is no user name or it is too long, don't use "~/" */
668: if (mch_get_user_name(uname, B0_UNAME_SIZE) == FAIL ||
669: (ulen = STRLEN(uname)) + flen > B0_FNAME_SIZE - 1)
670: STRNCPY(b0p->b0_fname, buf->b_filename, B0_FNAME_SIZE);
671: else
672: {
673: vim_memmove(b0p->b0_fname + ulen + 1, b0p->b0_fname + 1, flen);
674: vim_memmove(b0p->b0_fname + 1, uname, ulen);
675: }
676: }
677: if (stat((char *)buf->b_filename, &st) >= 0)
678: {
679: long_to_char((long)st.st_mtime, b0p->b0_mtime);
680: #ifdef CHECK_INODE
681: long_to_char((long)st.st_ino, b0p->b0_ino);
682: #endif
683: buf->b_mtime = st.st_mtime;
684: buf->b_mtime_read = st.st_mtime;
685: }
686: else
687: {
688: long_to_char(0L, b0p->b0_mtime);
689: #ifdef CHECK_INODE
690: long_to_char(0L, b0p->b0_ino);
691: #endif
692: buf->b_mtime = 0;
693: buf->b_mtime_read = 0;
694: }
695: }
696: }
697:
698: /*
699: * try to recover curbuf from the .swp file
700: */
701: void
702: ml_recover()
703: {
704: BUF *buf = NULL;
705: MEMFILE *mfp = NULL;
706: char_u *fname;
707: BHDR *hp = NULL;
708: ZERO_BL *b0p;
709: PTR_BL *pp;
710: DATA_BL *dp;
711: IPTR *ip;
712: blocknr_t bnum;
713: int page_count;
714: struct stat org_stat, swp_stat;
715: int len;
716: int directly;
717: linenr_t lnum;
718: char_u *p;
719: int i;
720: long error;
721: int cannot_open;
722: linenr_t line_count;
723: int has_error;
724: int idx;
725: int top;
726: int txt_start;
1.2 ! downsj 727: off_t size;
1.1 downsj 728: int called_from_main;
729: int serious_error = TRUE;
730: long mtime;
731:
732: called_from_main = (curbuf->b_ml.ml_mfp == NULL);
733: /*
734: * If the file name ends in ".sw?" we use it directly.
735: * Otherwise a search is done to find the swap file(s).
736: */
737: fname = curbuf->b_xfilename;
738: if (fname == NULL) /* When there is no file name */
739: fname = (char_u *)"";
740: len = STRLEN(fname);
741: if (len >= 4 && vim_strnicmp(fname + len - 4,
742: (char_u *)".sw", (size_t)3) == 0)
743: {
744: directly = TRUE;
745: fname = strsave(fname); /* make a copy for mf_open */
746: }
747: else
748: {
749: directly = FALSE;
750:
751: /* count the number of matching swap files */
752: len = recover_names(&fname, FALSE, 0);
753: if (len == 0) /* no swap files found */
754: {
755: EMSG2("No swap file found for %s", fname);
756: fname = NULL;
757: goto theend;
758: }
759: if (len == 1) /* one swap file found, use it */
760: i = 1;
761: else /* several swap files found, choose */
762: {
763: /* list the names of the swap files */
764: (void)recover_names(&fname, TRUE, 0);
765: msg_outchar('\n');
766: MSG_OUTSTR("Enter number of swap file to use (0 to quit): ");
767: i = get_number();
768: if (i < 1 || i > len)
769: {
770: fname = NULL;
771: goto theend;
772: }
773: }
774: /* get the swap file name that will be used */
775: (void)recover_names(&fname, FALSE, i);
776: }
777: if (fname == NULL)
778: goto theend; /* out of memory */
779:
780: /* When called from main() still need to initialize storage structure */
781: if (called_from_main && ml_open() == FAIL)
782: getout(1);
783:
784: /*
785: * allocate a buffer structure (only the memline in it is really used)
786: */
787: buf = (BUF *)alloc((unsigned)sizeof(BUF));
788: if (buf == NULL)
789: goto theend;
790:
791: /*
792: * init fields in memline struct
793: */
794: buf->b_ml.ml_stack_size = 0; /* no stack yet */
795: buf->b_ml.ml_stack = NULL; /* no stack yet */
796: buf->b_ml.ml_stack_top = 0; /* nothing in the stack */
797: buf->b_ml.ml_line_lnum = 0; /* no cached line */
798: buf->b_ml.ml_locked = NULL; /* no locked block */
799: buf->b_ml.ml_flags = 0;
800:
801: /*
802: * open the memfile from the old swap file
803: */
804: mfp = mf_open(fname, FALSE);
805: if (mfp == NULL || mfp->mf_fd < 0)
806: {
807: EMSG2("Cannot open %s", fname);
808: goto theend;
809: }
810: buf->b_ml.ml_mfp = mfp;
811: #if defined(MSDOS) || defined(WIN32)
812: /*
813: * set full pathname for swap file now, because a ":!cd dir" may
814: * change directory without us knowing it.
815: * Careful: May free fname!
816: */
817: mf_fullname(mfp);
818: #endif
819:
820: /*
821: * try to read block 0
822: */
823: if ((hp = mf_get(mfp, (blocknr_t)0, 1)) == NULL)
824: {
825: msg_start();
826: MSG_OUTSTR("Unable to read block 0 from ");
827: msg_outtrans(mfp->mf_fname);
828: MSG_OUTSTR("\nMaybe no changes were made or Vim did not update the swap file");
829: msg_end();
830: goto theend;
831: }
832: b0p = (ZERO_BL *)(hp->bh_data);
833: if (b0p->b0_id[0] != BLOCK0_ID0 || b0p->b0_id[1] != BLOCK0_ID1)
834: {
835: EMSG2("%s is not a swap file", mfp->mf_fname);
836: goto theend;
837: }
838: if (b0_magic_wrong(b0p))
839: {
840: msg_start();
841: MSG_OUTSTR("The file ");
842: msg_outtrans(mfp->mf_fname);
843: #if defined(MSDOS) || defined(WIN32)
844: if (STRNCMP(b0p->b0_hname, "PC ", 3) == 0)
845: MSG_OUTSTR(" cannot be used with this version of Vim.\n");
846: else
847: #endif
848: MSG_OUTSTR(" cannot be used on this computer.\n");
849: MSG_OUTSTR("The file was created on ");
850: /* avoid going past the end of currupted hostname */
851: b0p->b0_fname[0] = NUL;
852: MSG_OUTSTR(b0p->b0_hname);
853: MSG_OUTSTR(",\nor the file has been damaged.");
854: msg_end();
855: goto theend;
856: }
857: /*
858: * If we guessed the wrong page size, we have to recalculate the
859: * highest block number in the file.
860: */
861: if (mfp->mf_page_size != (unsigned)char_to_long(b0p->b0_page_size))
862: {
863: mfp->mf_page_size = (unsigned)char_to_long(b0p->b0_page_size);
864: if ((size = lseek(mfp->mf_fd, 0L, SEEK_END)) <= 0)
865: mfp->mf_blocknr_max = 0; /* no file or empty file */
866: else
867: mfp->mf_blocknr_max = size / mfp->mf_page_size;
868: mfp->mf_infile_count = mfp->mf_blocknr_max;
869: }
870:
871: /*
872: * If .swp file name given directly, use name from swap file for buffer.
873: */
874: if (directly)
875: {
876: expand_env(b0p->b0_fname, NameBuff, MAXPATHL);
877: if (setfname(NameBuff, NULL, TRUE) == FAIL)
878: goto theend;
879: }
880:
881: home_replace(NULL, mfp->mf_fname, NameBuff, MAXPATHL);
882: smsg((char_u *)"Using swap file \"%s\"", NameBuff);
883:
884: if (curbuf->b_filename == NULL)
885: STRCPY(NameBuff, "No File");
886: else
887: home_replace(NULL, curbuf->b_filename, NameBuff, MAXPATHL);
888: smsg((char_u *)"Original file \"%s\"", NameBuff);
889: msg_outchar((char_u)'\n');
890:
891: /*
892: * check date of swap file and original file
893: */
894: mtime = char_to_long(b0p->b0_mtime);
895: if (curbuf->b_filename != NULL &&
896: stat((char *)curbuf->b_filename, &org_stat) != -1 &&
897: ((stat((char *)mfp->mf_fname, &swp_stat) != -1 &&
898: org_stat.st_mtime > swp_stat.st_mtime) ||
899: org_stat.st_mtime != mtime))
900: {
901: EMSG("Warning: Original file may have been changed");
902: }
903: flushbuf();
904: mf_put(mfp, hp, FALSE, FALSE); /* release block 0 */
905: hp = NULL;
906:
907: /*
908: * Now that we are sure that the file is going to be recovered, clear the
909: * contents of the current buffer.
910: */
911: while (!(curbuf->b_ml.ml_flags & ML_EMPTY))
912: ml_delete((linenr_t)1, FALSE);
913:
914: bnum = 1; /* start with block 1 */
915: page_count = 1; /* which is 1 page */
916: lnum = 0; /* append after line 0 in curbuf */
917: line_count = 0;
918: idx = 0; /* start with first index in block 1 */
919: error = 0;
920: buf->b_ml.ml_stack_top = 0;
921: buf->b_ml.ml_stack = NULL;
922: buf->b_ml.ml_stack_size = 0; /* no stack yet */
923:
924: if (curbuf->b_filename == NULL)
925: cannot_open = TRUE;
926: else
927: cannot_open = FALSE;
928:
929: serious_error = FALSE;
930: for ( ; !got_int; line_breakcheck())
931: {
932: if (hp != NULL)
933: mf_put(mfp, hp, FALSE, FALSE); /* release previous block */
934:
935: /*
936: * get block
937: */
938: if ((hp = mf_get(mfp, (blocknr_t)bnum, page_count)) == NULL)
939: {
940: if (bnum == 1)
941: {
942: EMSG2("Unable to read block 1 from %s", mfp->mf_fname);
943: goto theend;
944: }
945: ++error;
946: ml_append(lnum++, (char_u *)"???MANY LINES MISSING", (colnr_t)0, TRUE);
947: }
948: else /* there is a block */
949: {
950: pp = (PTR_BL *)(hp->bh_data);
951: if (pp->pb_id == PTR_ID) /* it is a pointer block */
952: {
953: /* check line count when using pointer block first time */
954: if (idx == 0 && line_count != 0)
955: {
956: for (i = 0; i < (int)pp->pb_count; ++i)
957: line_count -= pp->pb_pointer[i].pe_line_count;
958: if (line_count != 0)
959: {
960: ++error;
961: ml_append(lnum++, (char_u *)"???LINE COUNT WRONG", (colnr_t)0, TRUE);
962: }
963: }
964:
965: if (pp->pb_count == 0)
966: {
967: ml_append(lnum++, (char_u *)"???EMPTY BLOCK", (colnr_t)0, TRUE);
968: ++error;
969: }
970: else if (idx < (int)pp->pb_count) /* go a block deeper */
971: {
972: if (pp->pb_pointer[idx].pe_bnum < 0)
973: {
974: /*
975: * Data block with negative block number.
976: * Try to read lines from the original file.
977: * This is slow, but it works.
978: */
979: if (!cannot_open)
980: {
981: line_count = pp->pb_pointer[idx].pe_line_count;
982: if (readfile(curbuf->b_filename, NULL, lnum, FALSE,
983: pp->pb_pointer[idx].pe_old_lnum - 1,
984: line_count, FALSE) == FAIL)
985: cannot_open = TRUE;
986: else
987: lnum += line_count;
988: }
989: if (cannot_open)
990: {
991: ++error;
992: ml_append(lnum++, (char_u *)"???LINES MISSING", (colnr_t)0, TRUE);
993: }
994: ++idx; /* get same block again for next index */
995: continue;
996: }
997:
998: /*
999: * going one block deeper in the tree
1000: */
1001: if ((top = ml_add_stack(buf)) < 0) /* new entry in stack */
1002: {
1003: ++error;
1004: break; /* out of memory */
1005: }
1006: ip = &(buf->b_ml.ml_stack[top]);
1007: ip->ip_bnum = bnum;
1008: ip->ip_index = idx;
1009:
1010: bnum = pp->pb_pointer[idx].pe_bnum;
1011: line_count = pp->pb_pointer[idx].pe_line_count;
1012: page_count = pp->pb_pointer[idx].pe_page_count;
1013: continue;
1014: }
1015: }
1016: else /* not a pointer block */
1017: {
1018: dp = (DATA_BL *)(hp->bh_data);
1019: if (dp->db_id != DATA_ID) /* block id wrong */
1020: {
1021: if (bnum == 1)
1022: {
1023: EMSG2("Block 1 ID wrong (%s not a .swp file?)",
1024: mfp->mf_fname);
1025: goto theend;
1026: }
1027: ++error;
1028: ml_append(lnum++, (char_u *)"???BLOCK MISSING", (colnr_t)0, TRUE);
1029: }
1030: else
1031: {
1032: /*
1033: * it is a data block
1034: * Append all the lines in this block
1035: */
1036: has_error = FALSE;
1037: /*
1038: * check length of block
1039: * if wrong, use length in pointer block
1040: */
1041: if (page_count * mfp->mf_page_size != dp->db_txt_end)
1042: {
1043: ml_append(lnum++, (char_u *)"??? from here until ???END lines may be messed up", (colnr_t)0, TRUE);
1044: ++error;
1045: has_error = TRUE;
1046: dp->db_txt_end = page_count * mfp->mf_page_size;
1047: }
1048:
1049: /* make sure there is a NUL at the end of the block */
1050: *((char_u *)dp + dp->db_txt_end - 1) = NUL;
1051:
1052: /*
1053: * check number of lines in block
1054: * if wrong, use count in data block
1055: */
1056: if (line_count != dp->db_line_count)
1057: {
1058: ml_append(lnum++, (char_u *)"??? from here until ???END lines may have been inserted/deleted", (colnr_t)0, TRUE);
1059: ++error;
1060: has_error = TRUE;
1061: }
1062:
1063: for (i = 0; i < dp->db_line_count; ++i)
1064: {
1065: txt_start = (dp->db_index[i] & DB_INDEX_MASK);
1066: if (txt_start <= HEADER_SIZE || txt_start >= (int)dp->db_txt_end)
1067: {
1068: p = (char_u *)"???";
1069: ++error;
1070: }
1071: else
1072: p = (char_u *)dp + txt_start;
1073: ml_append(lnum++, p, (colnr_t)0, TRUE);
1074: }
1075: if (has_error)
1076: ml_append(lnum++, (char_u *)"???END", (colnr_t)0, TRUE);
1077: }
1078: }
1079: }
1080:
1081: if (buf->b_ml.ml_stack_top == 0) /* finished */
1082: break;
1083:
1084: /*
1085: * go one block up in the tree
1086: */
1087: ip = &(buf->b_ml.ml_stack[--(buf->b_ml.ml_stack_top)]);
1088: bnum = ip->ip_bnum;
1089: idx = ip->ip_index + 1; /* go to next index */
1090: page_count = 1;
1091: }
1092:
1093: /*
1094: * The dummy line from the empty buffer will now be after the last line in
1095: * the buffer. Delete it.
1096: */
1097: ml_delete(curbuf->b_ml.ml_line_count, FALSE);
1098:
1099: recoverymode = FALSE;
1100: if (got_int)
1101: EMSG("Recovery Interrupted");
1102: else if (error)
1103: EMSG("Errors detected while recovering; look for lines starting with ???");
1104: else
1105: {
1106: MSG("Recovery completed. You should check if everything is OK.");
1107: MSG_OUTSTR("\n(You might want to write out this file under another name\n");
1108: MSG_OUTSTR("and run diff with the original file to check for changes)\n");
1109: MSG_OUTSTR("Delete the .swp file afterwards.\n\n");
1110: cmdline_row = msg_row;
1111: }
1112:
1113: theend:
1114: if (mfp != NULL)
1115: {
1116: if (hp != NULL)
1117: mf_put(mfp, hp, FALSE, FALSE);
1118: mf_close(mfp, FALSE); /* will also vim_free(mfp->mf_fname) */
1119: }
1120: else
1121: vim_free(fname);
1122: vim_free(buf);
1123: if (serious_error && called_from_main)
1124: ml_close(curbuf, TRUE);
1125: return;
1126: }
1127:
1128: /*
1129: * Find the names of swap files in current directory and the directory given
1130: * with the 'directory' option.
1131: *
1132: * Used to:
1133: * - list the swap files for "vim -r"
1134: * - count the number of swap files when recovering
1135: * - list the swap files when recovering
1136: * - find the name of the n'th swap file when recovering
1137: */
1138: int
1139: recover_names(fname, list, nr)
1140: char_u **fname; /* base for swap file name */
1141: int list; /* when TRUE, list the swap file names */
1142: int nr; /* when non-zero, return nr'th swap file name */
1143: {
1144: int num_names;
1145: char_u *(names[6]);
1146: char_u *tail;
1147: char_u *p;
1148: int num_files;
1149: int file_count = 0;
1150: char_u **files;
1151: int i;
1152: char_u *dirp;
1153: char_u *dir_name;
1154:
1155: if (list)
1156: {
1157: /* use msg() to start the scrolling properly */
1158: msg((char_u *)"Swap files found:");
1159: msg_outchar('\n');
1160: }
1161: expand_interactively = TRUE;
1.2 ! downsj 1162:
1.1 downsj 1163: /*
1164: * Do the loop for every directory in 'directory'.
1.2 ! downsj 1165: * First allocate some memory to put the directory name in.
1.1 downsj 1166: */
1.2 ! downsj 1167: dir_name = alloc((unsigned)STRLEN(p_dir) + 1);
1.1 downsj 1168: dirp = p_dir;
1.2 ! downsj 1169: while (dir_name != NULL && *dirp)
1.1 downsj 1170: {
1.2 ! downsj 1171: /*
! 1172: * Isolate a directory name from *dirp and put it in dir_name.
! 1173: * Advance dirp to next directory name.
! 1174: */
! 1175: (void)copy_option_part(&dirp, dir_name, 31000, ",");
! 1176:
! 1177: if (dir_name[0] == '.' && dir_name[1] == NUL) /* check current dir */
1.1 downsj 1178: {
1179: if (fname == NULL || *fname == NULL)
1180: {
1181: names[0] = strsave((char_u *)"*.sw?");
1182: #ifdef UNIX
1183: /* for Unix names starting with a dot are special */
1184: names[1] = strsave((char_u *)".*.sw?");
1185: names[2] = strsave((char_u *)".sw?");
1186: num_names = 3;
1187: #else
1188: num_names = 1;
1189: #endif
1190: }
1191: else
1192: num_names = get_names(names, *fname);
1193: }
1194: else /* check directory dir_name */
1195: {
1196: if (fname == NULL || *fname == NULL)
1197: {
1198: names[0] = concat_fnames(dir_name, (char_u *)"*.sw?", TRUE);
1199: #ifdef UNIX
1200: /* for Unix names starting with a dot are special */
1201: names[1] = concat_fnames(dir_name, (char_u *)".*.sw?", TRUE);
1202: names[2] = concat_fnames(dir_name, (char_u *)".sw?", TRUE);
1203: num_names = 3;
1204: #else
1205: num_names = 1;
1206: #endif
1207: }
1208: else
1209: {
1210: tail = gettail(*fname);
1211: tail = concat_fnames(dir_name, tail, TRUE);
1212: if (tail == NULL)
1213: num_names = 0;
1214: else
1215: {
1216: num_names = get_names(names, tail);
1217: vim_free(tail);
1218: }
1219: }
1220: }
1221:
1222: /* check for out-of-memory */
1223: for (i = 0; i < num_names; ++i)
1224: {
1225: if (names[i] == NULL)
1226: {
1227: for (i = 0; i < num_names; ++i)
1228: vim_free(names[i]);
1229: num_names = 0;
1230: }
1231: }
1232: if (num_names == 0)
1233: num_files = 0;
1234: else if (ExpandWildCards(num_names, names,
1235: &num_files, &files, TRUE, FALSE) == FAIL)
1236: {
1237: MSG_OUTSTR(files); /* print error message */
1238: num_files = 0;
1239: }
1240: /*
1241: * remove swapfile name of the current buffer, it must be ignored
1242: */
1243: if (curbuf->b_ml.ml_mfp != NULL &&
1244: (p = curbuf->b_ml.ml_mfp->mf_fname) != NULL)
1245: {
1246: for (i = 0; i < num_files; ++i)
1247: if (fullpathcmp(p, files[i]) == FPC_SAME)
1248: {
1249: vim_free(files[i]);
1250: --num_files;
1251: for ( ; i < num_files; ++i)
1252: files[i] = files[i + 1];
1253: break;
1254: }
1255: }
1256: if (nr)
1257: {
1258: file_count += num_files;
1259: if (nr <= file_count)
1260: {
1261: *fname = strsave(files[nr - 1 + num_files - file_count]);
1262: dirp = (char_u *)""; /* stop searching */
1263: }
1264: }
1265: else if (list)
1266: {
1.2 ! downsj 1267: if (dir_name[0] == '.' && dir_name[1] == NUL)
1.1 downsj 1268: {
1269: if (fname == NULL || *fname == NULL)
1270: MSG_OUTSTR(" In current directory:\n");
1271: else
1272: MSG_OUTSTR(" Using specified name:\n");
1273: }
1274: else
1275: {
1276: MSG_OUTSTR(" In directory ");
1277: msg_home_replace(dir_name);
1278: MSG_OUTSTR(":\n");
1279: }
1280:
1281: if (num_files)
1282: {
1283: for (i = 0; i < num_files; ++i)
1284: {
1285: /* print the swap file name */
1286: msg_outnum((long)++file_count);
1287: MSG_OUTSTR(". ");
1288: msg_outstr(gettail(files[i]));
1289: msg_outchar('\n');
1290: swapfile_info(files[i]);
1291: }
1292: }
1293: else
1294: MSG_OUTSTR(" -- none --\n");
1295: flushbuf();
1296: }
1297: else
1298: file_count += num_files;
1299:
1300: for (i = 0; i < num_names; ++i)
1301: vim_free(names[i]);
1302: FreeWild(num_files, files);
1303: }
1.2 ! downsj 1304: vim_free(dir_name);
1.1 downsj 1305: expand_interactively = FALSE;
1306: return file_count;
1307: }
1308:
1309: /*
1310: * Give information about an existing swap file
1311: */
1312: static void
1313: swapfile_info(fname)
1314: char_u *fname;
1315: {
1316: struct stat st;
1317: int fd;
1318: struct block0 b0;
1319: time_t x;
1320:
1321: /* print the swap file date */
1322: if (stat((char *)fname, &st) != -1)
1323: {
1324: MSG_OUTSTR(" dated: ");
1325: x = st.st_mtime; /* Manx C can't do &st.st_mtime */
1326: MSG_OUTSTR(ctime(&x));
1327: }
1328:
1329: /*
1330: * print the original file name
1331: */
1332: fd = open((char *)fname, O_RDONLY | O_EXTRA);
1333: if (fd >= 0)
1334: {
1335: if (read(fd, (char *)&b0, sizeof(b0)) == sizeof(b0))
1336: {
1337: if (b0.b0_id[0] != BLOCK0_ID0 ||
1338: b0.b0_id[1] != BLOCK0_ID1)
1339: MSG_OUTSTR(" [is not a swap file]");
1340: else
1341: {
1342: MSG_OUTSTR(" file name: ");
1343: msg_outstr(b0.b0_fname);
1344:
1345: if (*(b0.b0_hname) != NUL)
1346: {
1347: MSG_OUTSTR("\n host name: ");
1348: msg_outstr(b0.b0_hname);
1349: }
1350:
1351: if (*(b0.b0_uname) != NUL)
1352: {
1353: MSG_OUTSTR("\n user name: ");
1354: msg_outstr(b0.b0_uname);
1355: }
1356:
1357: if (char_to_long(b0.b0_pid) != 0L)
1358: {
1359: MSG_OUTSTR("\n process ID: ");
1360: msg_outnum(char_to_long(b0.b0_pid));
1361: #if defined(UNIX) || defined(__EMX__)
1362: /* EMX kill() not working correctly, it seems */
1363: if (kill(char_to_long(b0.b0_pid), 0) == 0)
1364: MSG_OUTSTR(" (still running)");
1365: #endif
1366: }
1367:
1368: if (b0_magic_wrong(&b0))
1369: {
1370: #if defined(MSDOS) || defined(WIN32)
1371: if (STRNCMP(b0.b0_hname, "PC ", 3) == 0)
1372: MSG_OUTSTR("\n [not usable with this version of Vim]");
1373: else
1374: #endif
1375: MSG_OUTSTR("\n [not usable on this computer]");
1376: }
1377:
1378: }
1379: }
1380: else
1381: MSG_OUTSTR(" [cannot be read]");
1382: close(fd);
1383: }
1384: else
1385: MSG_OUTSTR(" [cannot be opened]");
1386: msg_outchar('\n');
1387: }
1388:
1389: static int
1390: get_names(names, fname)
1391: char_u **names;
1392: char_u *fname;
1393: {
1394: int num_names;
1395:
1396: #ifdef SHORT_FNAME
1397: /*
1398: * (MS-DOS) always short names
1399: */
1400: names[0] = modname(fname, (char_u *)".sw?");
1401: num_names = 1;
1402: #else /* !SHORT_FNAME */
1403: # ifdef WIN32
1404: /*
1405: * (WIN32) never short names
1406: */
1407: num_names = 1;
1408: names[0] = concat_fnames(fname, (char_u *)".sw?", FALSE);
1409: # else /* !WIN32 */
1410: /*
1411: * (Not MS-DOS or WIN32) maybe short name, maybe not: try both.
1412: * Only use the short name if it is different.
1413: */
1414:
1415: int i;
1416:
1417: names[0] = concat_fnames(fname, (char_u *)".sw?", FALSE);
1418: i = curbuf->b_shortname;
1419: curbuf->b_shortname = TRUE;
1420: names[1] = modname(fname, (char_u *)".sw?");
1421: curbuf->b_shortname = i;
1422: if (STRCMP(names[0], names[1]) == 0)
1423: {
1424: vim_free(names[1]);
1425: num_names = 1;
1426: }
1427: else
1428: num_names = 2;
1429: # endif /* !WIN32 */
1430: #endif /* !SHORT_FNAME */
1431: return num_names;
1432: }
1433:
1434: /*
1435: * sync all memlines
1436: *
1437: * If 'check_file' is TRUE, check if original file exists and was not changed.
1438: * If 'check_char' is TRUE, stop syncing when character becomes available, but
1439: * always sync at least one block.
1440: */
1441: void
1442: ml_sync_all(check_file, check_char)
1443: int check_file;
1444: int check_char;
1445: {
1446: BUF *buf;
1447: struct stat st;
1448:
1449: for (buf = firstbuf; buf != NULL; buf = buf->b_next)
1450: {
1451: if (buf->b_ml.ml_mfp == NULL || buf->b_ml.ml_mfp->mf_fname == NULL)
1452: continue; /* no file */
1453:
1454: ml_flush_line(buf); /* flush buffered line */
1455: /* flush locked block */
1456: (void)ml_find_line(buf, (linenr_t)0, ML_FLUSH);
1457: if (buf->b_changed && check_file && mf_need_trans(buf->b_ml.ml_mfp) &&
1458: buf->b_filename != NULL)
1459: {
1460: /*
1461: * if original file does not exist anymore or has been changed
1462: * call ml_preserve to get rid of all negative numbered blocks
1463: */
1464: if (stat((char *)buf->b_filename, &st) == -1 ||
1465: st.st_mtime != buf->b_mtime_read)
1466: {
1467: ml_preserve(buf, FALSE);
1468: need_check_timestamps = TRUE; /* give message later */
1469: }
1470: }
1471: if (buf->b_ml.ml_mfp->mf_dirty)
1472: {
1473: mf_sync(buf->b_ml.ml_mfp, FALSE, check_char, buf->b_changed);
1474: if (check_char && mch_char_avail()) /* character available now */
1475: break;
1476: }
1477: }
1478: }
1479:
1480: /*
1481: * sync one buffer, including negative blocks
1482: *
1483: * after this all the blocks are in the swap file
1484: *
1485: * Used for the :preserve command and when the original file has been
1486: * changed or deleted.
1487: *
1488: * when message is TRUE the success of preserving is reported
1489: */
1490: void
1491: ml_preserve(buf, message)
1492: BUF *buf;
1493: int message;
1494: {
1495: BHDR *hp;
1496: linenr_t lnum;
1497: MEMFILE *mfp = buf->b_ml.ml_mfp;
1498: int status;
1499:
1500: if (mfp == NULL || mfp->mf_fname == NULL)
1501: {
1502: if (message)
1503: EMSG("Cannot preserve, there is no swap file");
1504: return;
1505: }
1506:
1507: ml_flush_line(buf); /* flush buffered line */
1508: (void)ml_find_line(buf, (linenr_t)0, ML_FLUSH); /* flush locked block */
1509: status = mf_sync(mfp, TRUE, FALSE, TRUE);
1510:
1511: /* stack is invalid after mf_sync(.., TRUE, ..) */
1512: buf->b_ml.ml_stack_top = 0;
1513:
1514: /*
1515: * Some of the data blocks may have been changed from negative to
1516: * positive block number. In that case the pointer blocks need to be
1517: * updated.
1518: *
1519: * We don't know in which pointer block the references are, so we visit
1520: * all data blocks until there are no more translations to be done (or
1521: * we hit the end of the file, which can only happen in case a write fails,
1522: * e.g. when file system if full).
1523: * ml_find_line() does the work by translating the negative block numbers
1524: * when getting the first line of each data block.
1525: */
1526: if (mf_need_trans(mfp))
1527: {
1528: lnum = 1;
1529: while (mf_need_trans(mfp) && lnum <= buf->b_ml.ml_line_count)
1530: {
1531: hp = ml_find_line(buf, lnum, ML_FIND);
1532: if (hp == NULL)
1533: {
1534: status = FAIL;
1535: goto theend;
1536: }
1537: CHECK(buf->b_ml.ml_locked_low != lnum, "low != lnum");
1538: lnum = buf->b_ml.ml_locked_high + 1;
1539: }
1540: (void)ml_find_line(buf, (linenr_t)0, ML_FLUSH); /* flush locked block */
1541: if (mf_sync(mfp, TRUE, FALSE, TRUE) == FAIL) /* sync the updated pointer blocks */
1542: status = FAIL;
1543: buf->b_ml.ml_stack_top = 0; /* stack is invalid now */
1544: }
1545: theend:
1546: if (message)
1547: {
1548: if (status == OK)
1549: MSG("File preserved");
1550: else
1551: EMSG("Preserve failed");
1552: }
1553: }
1554:
1555: /*
1556: * get a pointer to a (read-only copy of a) line
1557: *
1558: * On failure an error message is given and IObuff is returned (to avoid
1559: * having to check for error everywhere).
1560: */
1561: char_u *
1562: ml_get(lnum)
1563: linenr_t lnum;
1564: {
1565: return ml_get_buf(curbuf, lnum, FALSE);
1566: }
1567:
1568: /*
1569: * ml_get_pos: get pointer to position 'pos'
1570: */
1571: char_u *
1572: ml_get_pos(pos)
1573: FPOS *pos;
1574: {
1575: return (ml_get_buf(curbuf, pos->lnum, FALSE) + pos->col);
1576: }
1577:
1578: /*
1579: * ml_get_pos: get pointer to cursor line.
1580: */
1581: char_u *
1582: ml_get_curline()
1583: {
1584: return ml_get_buf(curbuf, curwin->w_cursor.lnum, FALSE);
1585: }
1586:
1587: /*
1588: * ml_get_pos: get pointer to cursor position
1589: */
1590: char_u *
1591: ml_get_cursor()
1592: {
1593: return (ml_get_buf(curbuf, curwin->w_cursor.lnum, FALSE) +
1594: curwin->w_cursor.col);
1595: }
1596:
1597: /*
1598: * get a pointer to a line in a specific buffer
1599: *
1600: * will_change: if TRUE mark the buffer dirty (chars in the line will be
1601: * changed)
1602: */
1603: char_u *
1604: ml_get_buf(buf, lnum, will_change)
1605: BUF *buf;
1606: linenr_t lnum;
1607: int will_change; /* line will be changed */
1608: {
1609: BHDR *hp;
1610: DATA_BL *dp;
1611: char_u *ptr;
1612:
1613: if (lnum > buf->b_ml.ml_line_count) /* invalid line number */
1614: {
1615: EMSGN("ml_get: invalid lnum: %ld", lnum);
1616: errorret:
1617: STRCPY(IObuff, "???");
1618: return IObuff;
1619: }
1620: if (lnum <= 0) /* pretend line 0 is line 1 */
1621: lnum = 1;
1622:
1623: if (buf->b_ml.ml_mfp == NULL) /* there are no lines */
1624: return (char_u *)"";
1625:
1626: /*
1627: * See if it is the same line as requested last time.
1628: * Otherwise may need to flush last used line.
1629: */
1630: if (buf->b_ml.ml_line_lnum != lnum)
1631: {
1632: ml_flush_line(buf);
1633:
1634: /*
1635: * find the data block containing the line
1636: * This also fills the stack with the blocks from the root to the data block
1637: * This also releases any locked block.
1638: */
1639: if ((hp = ml_find_line(buf, lnum, ML_FIND)) == NULL)
1640: {
1641: EMSGN("ml_get: cannot find line %ld", lnum);
1642: goto errorret;
1643: }
1644:
1645: dp = (DATA_BL *)(hp->bh_data);
1646:
1647: ptr = (char_u *)dp + ((dp->db_index[lnum - buf->b_ml.ml_locked_low]) & DB_INDEX_MASK);
1648: buf->b_ml.ml_line_ptr = ptr;
1649: buf->b_ml.ml_line_lnum = lnum;
1650: buf->b_ml.ml_flags &= ~ML_LINE_DIRTY;
1651: }
1652: if (will_change)
1653: buf->b_ml.ml_flags |= (ML_LOCKED_DIRTY | ML_LOCKED_POS);
1654:
1655: return buf->b_ml.ml_line_ptr;
1656: }
1657:
1658: /*
1659: * Check if a line that was just obtained by a call to ml_get
1660: * is in allocated memory.
1661: */
1662: int
1663: ml_line_alloced()
1664: {
1665: return (curbuf->b_ml.ml_flags & ML_LINE_DIRTY);
1666: }
1667:
1668: /*
1669: * append a line after lnum (may be 0 to insert a line in front of the file)
1670: *
1671: * newfile: TRUE when starting to edit a new file, meaning that pe_old_lnum
1672: * will be set for recovery
1673: *
1674: * return FAIL for failure, OK otherwise
1675: */
1676: int
1677: ml_append(lnum, line, len, newfile)
1678: linenr_t lnum; /* append after this line (can be 0) */
1679: char_u *line; /* text of the new line */
1680: colnr_t len; /* length of new line, including NUL, or 0 */
1681: int newfile; /* flag, see above */
1682: {
1683: if (curbuf->b_ml.ml_line_lnum != 0)
1684: ml_flush_line(curbuf);
1685: return ml_append_int(curbuf, lnum, line, len, newfile);
1686: }
1687:
1688: static int
1689: ml_append_int(buf, lnum, line, len, newfile)
1690: BUF *buf;
1691: linenr_t lnum; /* append after this line (can be 0) */
1692: char_u *line; /* text of the new line */
1693: colnr_t len; /* length of line, including NUL, or 0 */
1694: int newfile; /* flag, see above */
1695: {
1696: int i;
1697: int line_count; /* number of indexes in current block */
1698: int offset;
1699: int from, to;
1700: int space_needed; /* space needed for new line */
1701: int page_size;
1702: int page_count;
1703: int db_idx; /* index for lnum in data block */
1704: BHDR *hp;
1705: MEMFILE *mfp;
1706: DATA_BL *dp;
1707: PTR_BL *pp;
1708: IPTR *ip;
1709:
1710: /* lnum out of range */
1711: if (lnum > buf->b_ml.ml_line_count || buf->b_ml.ml_mfp == NULL)
1712: return FAIL;
1713:
1714: if (lowest_marked && lowest_marked > lnum)
1715: lowest_marked = lnum + 1;
1716:
1717: if (len == 0)
1718: len = STRLEN(line) + 1; /* space needed for the text */
1719: space_needed = len + INDEX_SIZE; /* space needed for text + index */
1720:
1721: mfp = buf->b_ml.ml_mfp;
1722: page_size = mfp->mf_page_size;
1723:
1724: /*
1725: * find the data block containing the previous line
1726: * This also fills the stack with the blocks from the root to the data block
1727: * This also releases any locked block.
1728: */
1729: if ((hp = ml_find_line(buf, lnum == 0 ? (linenr_t)1 : lnum,
1730: ML_INSERT)) == NULL)
1731: return FAIL;
1732:
1733: buf->b_ml.ml_flags &= ~ML_EMPTY;
1734:
1735: if (lnum == 0) /* got line one instead, correct db_idx */
1736: db_idx = -1; /* careful, it is negative! */
1737: else
1738: db_idx = lnum - buf->b_ml.ml_locked_low;
1739: /* get line count before the insertion */
1740: line_count = buf->b_ml.ml_locked_high - buf->b_ml.ml_locked_low;
1741:
1742: dp = (DATA_BL *)(hp->bh_data);
1743:
1744: /*
1745: * If
1746: * - there is not enough room in the current block
1747: * - appending to the last line in the block
1748: * - not appending to the last line in the file
1749: * insert in front of the next block.
1750: */
1751: if ((int)dp->db_free < space_needed && db_idx == line_count - 1 &&
1752: lnum < buf->b_ml.ml_line_count)
1753: {
1754: /*
1755: * Now that the line is not going to be inserted in the block that we
1756: * expected, the line count has to be adjusted in the pointer blocks
1757: * by using ml_locked_lineadd.
1758: */
1759: --(buf->b_ml.ml_locked_lineadd);
1760: --(buf->b_ml.ml_locked_high);
1761: if ((hp = ml_find_line(buf, lnum + 1, ML_INSERT)) == NULL)
1762: return FAIL;
1763:
1764: db_idx = -1; /* careful, it is negative! */
1765: /* get line count before the insertion */
1766: line_count = buf->b_ml.ml_locked_high - buf->b_ml.ml_locked_low;
1767: CHECK(buf->b_ml.ml_locked_low != lnum + 1, "locked_low != lnum + 1");
1768:
1769: dp = (DATA_BL *)(hp->bh_data);
1770: }
1771:
1772: ++buf->b_ml.ml_line_count;
1773:
1774: if ((int)dp->db_free >= space_needed) /* enough room in data block */
1775: {
1776: /*
1777: * Insert new line in existing data block, or in data block allocated above.
1778: */
1779: dp->db_txt_start -= len;
1780: dp->db_free -= space_needed;
1781: ++(dp->db_line_count);
1782:
1783: /*
1784: * move the text of the lines that follow to the front
1785: * adjust the indexes of the lines that follow
1786: */
1787: if (line_count > db_idx + 1) /* if there are following lines */
1788: {
1789: /*
1790: * Offset is the start of the previous line.
1791: * This will become the character just after the new line.
1792: */
1793: if (db_idx < 0)
1794: offset = dp->db_txt_end;
1795: else
1796: offset = ((dp->db_index[db_idx]) & DB_INDEX_MASK);
1797: vim_memmove((char *)dp + dp->db_txt_start,
1798: (char *)dp + dp->db_txt_start + len,
1799: (size_t)(offset - (dp->db_txt_start + len)));
1800: for (i = line_count - 1; i > db_idx; --i)
1801: dp->db_index[i + 1] = dp->db_index[i] - len;
1802: dp->db_index[db_idx + 1] = offset - len;
1803: }
1804: else /* add line at the end */
1805: dp->db_index[db_idx + 1] = dp->db_txt_start;
1806:
1807: /*
1808: * copy the text into the block
1809: */
1810: vim_memmove((char *)dp + dp->db_index[db_idx + 1], line, (size_t)len);
1811:
1812: /*
1813: * Mark the block dirty.
1814: */
1815: buf->b_ml.ml_flags |= ML_LOCKED_DIRTY;
1816: if (!newfile)
1817: buf->b_ml.ml_flags |= ML_LOCKED_POS;
1818: }
1819: else /* not enough space in data block */
1820: {
1821: /*
1822: * If there is not enough room we have to create a new data block and copy some
1823: * lines into it.
1824: * Then we have to insert an entry in the pointer block.
1825: * If this pointer block also is full, we go up another block, and so on, up
1826: * to the root if necessary.
1827: * The line counts in the pointer blocks have already been adjusted by
1828: * ml_find_line().
1829: */
1830: long line_count_left, line_count_right;
1831: int page_count_left, page_count_right;
1832: BHDR *hp_left;
1833: BHDR *hp_right;
1834: BHDR *hp_new;
1835: int lines_moved;
1836: int data_moved = 0; /* init to shut up gcc */
1837: int total_moved = 0; /* init to shut up gcc */
1838: DATA_BL *dp_right, *dp_left;
1839: int stack_idx;
1840: int in_left;
1841: int lineadd;
1842: blocknr_t bnum_left, bnum_right;
1843: linenr_t lnum_left, lnum_right;
1844: int pb_idx;
1845: PTR_BL *pp_new;
1846:
1847: /*
1848: * We are going to allocate a new data block. Depending on the
1849: * situation it will be put to the left or right of the existing
1850: * block. If possible we put the new line in the left block and move
1851: * the lines after it to the right block. Otherwise the new line is
1852: * also put in the right block. This method is more efficient when
1853: * inserting a lot of lines at one place.
1854: */
1855: if (db_idx < 0) /* left block is new, right block is existing */
1856: {
1857: lines_moved = 0;
1858: in_left = TRUE;
1859: /* space_needed does not change */
1860: }
1861: else /* left block is existing, right block is new */
1862: {
1863: lines_moved = line_count - db_idx - 1;
1864: if (lines_moved == 0)
1865: in_left = FALSE; /* put new line in right block */
1866: /* space_needed does not change */
1867: else
1868: {
1869: data_moved = ((dp->db_index[db_idx]) & DB_INDEX_MASK) -
1870: dp->db_txt_start;
1871: total_moved = data_moved + lines_moved * INDEX_SIZE;
1872: if ((int)dp->db_free + total_moved >= space_needed)
1873: {
1874: in_left = TRUE; /* put new line in left block */
1875: space_needed = total_moved;
1876: }
1877: else
1878: {
1879: in_left = FALSE; /* put new line in right block */
1880: space_needed += total_moved;
1881: }
1882: }
1883: }
1884:
1885: page_count = ((space_needed + HEADER_SIZE) + page_size - 1) / page_size;
1886: if ((hp_new = ml_new_data(mfp, newfile, page_count)) == NULL)
1887: {
1888: /* correct line counts in pointer blocks */
1889: --(buf->b_ml.ml_locked_lineadd);
1890: --(buf->b_ml.ml_locked_high);
1891: return FAIL;
1892: }
1893: if (db_idx < 0) /* left block is new */
1894: {
1895: hp_left = hp_new;
1896: hp_right = hp;
1897: line_count_left = 0;
1898: line_count_right = line_count;
1899: }
1900: else /* right block is new */
1901: {
1902: hp_left = hp;
1903: hp_right = hp_new;
1904: line_count_left = line_count;
1905: line_count_right = 0;
1906: }
1907: dp_right = (DATA_BL *)(hp_right->bh_data);
1908: dp_left = (DATA_BL *)(hp_left->bh_data);
1909: bnum_left = hp_left->bh_bnum;
1910: bnum_right = hp_right->bh_bnum;
1911: page_count_left = hp_left->bh_page_count;
1912: page_count_right = hp_right->bh_page_count;
1913:
1914: /*
1915: * May move the new line into the right/new block.
1916: */
1917: if (!in_left)
1918: {
1919: dp_right->db_txt_start -= len;
1920: dp_right->db_free -= len + INDEX_SIZE;
1921: dp_right->db_index[0] = dp_right->db_txt_start;
1922: vim_memmove((char *)dp_right + dp_right->db_txt_start,
1923: line, (size_t)len);
1924: ++line_count_right;
1925: }
1926: /*
1927: * may move lines from the left/old block to the right/new one.
1928: */
1929: if (lines_moved)
1930: {
1931: /*
1932: */
1933: dp_right->db_txt_start -= data_moved;
1934: dp_right->db_free -= total_moved;
1935: vim_memmove((char *)dp_right + dp_right->db_txt_start,
1936: (char *)dp_left + dp_left->db_txt_start,
1937: (size_t)data_moved);
1938: offset = dp_right->db_txt_start - dp_left->db_txt_start;
1939: dp_left->db_txt_start += data_moved;
1940: dp_left->db_free += total_moved;
1941:
1942: /*
1943: * update indexes in the new block
1944: */
1945: for (to = line_count_right, from = db_idx + 1;
1946: from < line_count_left; ++from, ++to)
1947: dp_right->db_index[to] = dp->db_index[from] + offset;
1948: line_count_right += lines_moved;
1949: line_count_left -= lines_moved;
1950: }
1951:
1952: /*
1953: * May move the new line into the left (old or new) block.
1954: */
1955: if (in_left)
1956: {
1957: dp_left->db_txt_start -= len;
1958: dp_left->db_free -= len + INDEX_SIZE;
1959: dp_left->db_index[line_count_left] = dp_left->db_txt_start;
1960: vim_memmove((char *)dp_left + dp_left->db_txt_start,
1961: line, (size_t)len);
1962: ++line_count_left;
1963: }
1964:
1965: if (db_idx < 0) /* left block is new */
1966: {
1967: lnum_left = lnum + 1;
1968: lnum_right = 0;
1969: }
1970: else /* right block is new */
1971: {
1972: lnum_left = 0;
1973: if (in_left)
1974: lnum_right = lnum + 2;
1975: else
1976: lnum_right = lnum + 1;
1977: }
1978: dp_left->db_line_count = line_count_left;
1979: dp_right->db_line_count = line_count_right;
1980:
1981: /*
1982: * release the two data blocks
1983: * The new one (hp_new) already has a correct blocknumber.
1984: * The old one (hp, in ml_locked) gets a positive blocknumber if
1985: * we changed it and we are not editing a new file.
1986: */
1987: if (lines_moved || in_left)
1988: buf->b_ml.ml_flags |= ML_LOCKED_DIRTY;
1989: if (!newfile && db_idx >= 0 && in_left)
1990: buf->b_ml.ml_flags |= ML_LOCKED_POS;
1991: mf_put(mfp, hp_new, TRUE, FALSE);
1992:
1993: /*
1994: * flush the old data block
1995: * set ml_locked_lineadd to 0, because the updating of the
1996: * pointer blocks is done below
1997: */
1998: lineadd = buf->b_ml.ml_locked_lineadd;
1999: buf->b_ml.ml_locked_lineadd = 0;
2000: ml_find_line(buf, (linenr_t)0, ML_FLUSH); /* flush data block */
2001:
2002: /*
2003: * update pointer blocks for the new data block
2004: */
2005: for (stack_idx = buf->b_ml.ml_stack_top - 1; stack_idx >= 0; --stack_idx)
2006: {
2007: ip = &(buf->b_ml.ml_stack[stack_idx]);
2008: pb_idx = ip->ip_index;
2009: if ((hp = mf_get(mfp, ip->ip_bnum, 1)) == NULL)
2010: return FAIL;
2011: pp = (PTR_BL *)(hp->bh_data); /* must be pointer block */
2012: if (pp->pb_id != PTR_ID)
2013: {
2014: EMSG("pointer block id wrong 3");
2015: mf_put(mfp, hp, FALSE, FALSE);
2016: return FAIL;
2017: }
2018: /*
2019: * TODO: If the pointer block is full and we are adding at the end
2020: * try to insert in front of the next block
2021: */
2022: if (pp->pb_count < pp->pb_count_max) /* block not full, add one entry */
2023: {
2024: if (pb_idx + 1 < (int)pp->pb_count)
2025: vim_memmove(&pp->pb_pointer[pb_idx + 2],
2026: &pp->pb_pointer[pb_idx + 1],
2027: (size_t)(pp->pb_count - pb_idx - 1) * sizeof(PTR_EN));
2028: ++pp->pb_count;
2029: pp->pb_pointer[pb_idx].pe_line_count = line_count_left;
2030: pp->pb_pointer[pb_idx].pe_bnum = bnum_left;
2031: pp->pb_pointer[pb_idx].pe_page_count = page_count_left;
2032: pp->pb_pointer[pb_idx + 1].pe_line_count = line_count_right;
2033: pp->pb_pointer[pb_idx + 1].pe_bnum = bnum_right;
2034: pp->pb_pointer[pb_idx + 1].pe_page_count = page_count_right;
2035:
2036: if (lnum_left != 0)
2037: pp->pb_pointer[pb_idx].pe_old_lnum = lnum_left;
2038: if (lnum_right != 0)
2039: pp->pb_pointer[pb_idx + 1].pe_old_lnum = lnum_right;
2040:
2041: mf_put(mfp, hp, TRUE, FALSE);
2042: buf->b_ml.ml_stack_top = stack_idx + 1; /* truncate stack */
2043:
2044: if (lineadd)
2045: {
2046: --(buf->b_ml.ml_stack_top);
2047: /* fix line count for rest of blocks in the stack */
2048: ml_lineadd(buf, lineadd);
2049: /* fix stack itself */
2050: buf->b_ml.ml_stack[buf->b_ml.ml_stack_top].ip_high +=
2051: lineadd;
2052: ++(buf->b_ml.ml_stack_top);
2053: }
2054:
2055: return OK;
2056: }
2057: else /* pointer block full */
2058: {
2059: /*
2060: * split the pointer block
2061: * allocate a new pointer block
2062: * move some of the pointer into the new block
2063: * prepare for updating the parent block
2064: */
2065: for (;;) /* do this twice when splitting block 1 */
2066: {
2067: hp_new = ml_new_ptr(mfp);
2068: if (hp_new == NULL) /* TODO: try to fix tree */
2069: return FAIL;
2070: pp_new = (PTR_BL *)(hp_new->bh_data);
2071:
2072: if (hp->bh_bnum != 1)
2073: break;
2074:
2075: /*
2076: * if block 1 becomes full the tree is given an extra level
2077: * The pointers from block 1 are moved into the new block.
2078: * block 1 is updated to point to the new block
2079: * then continue to split the new block
2080: */
2081: vim_memmove(pp_new, pp, (size_t)page_size);
2082: pp->pb_count = 1;
2083: pp->pb_pointer[0].pe_bnum = hp_new->bh_bnum;
2084: pp->pb_pointer[0].pe_line_count = buf->b_ml.ml_line_count;
2085: pp->pb_pointer[0].pe_old_lnum = 1;
2086: pp->pb_pointer[0].pe_page_count = 1;
2087: mf_put(mfp, hp, TRUE, FALSE); /* release block 1 */
2088: hp = hp_new; /* new block is to be split */
2089: pp = pp_new;
2090: CHECK(stack_idx != 0, "stack_idx should be 0");
2091: ip->ip_index = 0;
2092: ++stack_idx; /* do block 1 again later */
2093: }
2094: /*
2095: * move the pointers after the current one to the new block
2096: * If there are none, the new entry will be in the new block.
2097: */
2098: total_moved = pp->pb_count - pb_idx - 1;
2099: if (total_moved)
2100: {
2101: vim_memmove(&pp_new->pb_pointer[0],
2102: &pp->pb_pointer[pb_idx + 1],
2103: (size_t)(total_moved) * sizeof(PTR_EN));
2104: pp_new->pb_count = total_moved;
2105: pp->pb_count -= total_moved - 1;
2106: pp->pb_pointer[pb_idx + 1].pe_bnum = bnum_right;
2107: pp->pb_pointer[pb_idx + 1].pe_line_count = line_count_right;
2108: pp->pb_pointer[pb_idx + 1].pe_page_count = page_count_right;
2109: if (lnum_right)
2110: pp->pb_pointer[pb_idx + 1].pe_old_lnum = lnum_right;
2111: }
2112: else
2113: {
2114: pp_new->pb_count = 1;
2115: pp_new->pb_pointer[0].pe_bnum = bnum_right;
2116: pp_new->pb_pointer[0].pe_line_count = line_count_right;
2117: pp_new->pb_pointer[0].pe_page_count = page_count_right;
2118: pp_new->pb_pointer[0].pe_old_lnum = lnum_right;
2119: }
2120: pp->pb_pointer[pb_idx].pe_bnum = bnum_left;
2121: pp->pb_pointer[pb_idx].pe_line_count = line_count_left;
2122: pp->pb_pointer[pb_idx].pe_page_count = page_count_left;
2123: if (lnum_left)
2124: pp->pb_pointer[pb_idx].pe_old_lnum = lnum_left;
2125: lnum_left = 0;
2126: lnum_right = 0;
2127:
2128: /*
2129: * recompute line counts
2130: */
2131: line_count_right = 0;
2132: for (i = 0; i < (int)pp_new->pb_count; ++i)
2133: line_count_right += pp_new->pb_pointer[i].pe_line_count;
2134: line_count_left = 0;
2135: for (i = 0; i < (int)pp->pb_count; ++i)
2136: line_count_left += pp->pb_pointer[i].pe_line_count;
2137:
2138: bnum_left = hp->bh_bnum;
2139: bnum_right = hp_new->bh_bnum;
2140: page_count_left = 1;
2141: page_count_right = 1;
2142: mf_put(mfp, hp, TRUE, FALSE);
2143: mf_put(mfp, hp_new, TRUE, FALSE);
2144: }
2145: }
2146: EMSG("Updated too many blocks?");
2147: buf->b_ml.ml_stack_top = 0; /* invalidate stack */
2148: }
2149: return OK;
2150: }
2151:
2152: /*
2153: * replace line lnum, with buffering, in current buffer
2154: *
2155: * If copy is TRUE, make a copy of the line, otherwise the line has been
2156: * copied to allocated memory already.
2157: *
2158: * return FAIL for failure, OK otherwise
2159: */
2160: int
2161: ml_replace(lnum, line, copy)
2162: linenr_t lnum;
2163: char_u *line;
2164: int copy;
2165: {
2166: if (line == NULL) /* just checking... */
2167: return FAIL;
2168:
2169: if (curbuf->b_ml.ml_line_lnum != lnum) /* other line buffered */
2170: ml_flush_line(curbuf); /* flush it */
2171: else if (curbuf->b_ml.ml_flags & ML_LINE_DIRTY) /* same line allocated */
2172: vim_free(curbuf->b_ml.ml_line_ptr); /* free it */
2173: if (copy && (line = strsave(line)) == NULL) /* allocate memory */
2174: return FAIL;
2175: curbuf->b_ml.ml_line_ptr = line;
2176: curbuf->b_ml.ml_line_lnum = lnum;
2177: curbuf->b_ml.ml_flags = (curbuf->b_ml.ml_flags | ML_LINE_DIRTY) & ~ML_EMPTY;
2178:
2179: return OK;
2180: }
2181:
2182: /*
2183: * delete line 'lnum'
2184: *
2185: * return FAIL for failure, OK otherwise
2186: */
2187: int
2188: ml_delete(lnum, message)
2189: linenr_t lnum;
2190: int message;
2191: {
2192: ml_flush_line(curbuf);
2193: return ml_delete_int(curbuf, lnum, message);
2194: }
2195:
2196: static int
2197: ml_delete_int(buf, lnum, message)
2198: BUF *buf;
2199: linenr_t lnum;
2200: int message;
2201: {
2202: BHDR *hp;
2203: MEMFILE *mfp;
2204: DATA_BL *dp;
2205: PTR_BL *pp;
2206: IPTR *ip;
2207: int count; /* number of entries in block */
2208: int idx;
2209: int stack_idx;
2210: int text_start;
2211: int line_start;
2212: int line_size;
2213: int i;
2214:
2215: if (lnum < 1 || lnum > buf->b_ml.ml_line_count)
2216: return FAIL;
2217:
2218: if (lowest_marked && lowest_marked > lnum)
2219: lowest_marked--;
2220:
2221: /*
2222: * If the file becomes empty the last line is replaced by an empty line.
2223: */
2224: if (buf->b_ml.ml_line_count == 1) /* file becomes empty */
2225: {
2226: if (message)
2227: keep_msg = no_lines_msg;
2228: i = ml_replace((linenr_t)1, (char_u *)"", TRUE);
2229: buf->b_ml.ml_flags |= ML_EMPTY;
2230: return i;
2231: }
2232:
2233: /*
2234: * find the data block containing the line
2235: * This also fills the stack with the blocks from the root to the data block
2236: * This also releases any locked block.
2237: */
2238: mfp = buf->b_ml.ml_mfp;
2239: if (mfp == NULL)
2240: return FAIL;
2241:
2242: if ((hp = ml_find_line(buf, lnum, ML_DELETE)) == NULL)
2243: return FAIL;
2244:
2245: dp = (DATA_BL *)(hp->bh_data);
2246: /* compute line count before the delete */
2247: count = (long)(buf->b_ml.ml_locked_high) - (long)(buf->b_ml.ml_locked_low) + 2;
2248: idx = lnum - buf->b_ml.ml_locked_low;
2249:
2250: --buf->b_ml.ml_line_count;
2251:
2252: /*
2253: * special case: If there is only one line in the data block it becomes empty.
2254: * Then we have to remove the entry, pointing to this data block, from the
2255: * pointer block. If this pointer block also becomes empty, we go up another
2256: * block, and so on, up to the root if necessary.
2257: * The line counts in the pointer blocks have already been adjusted by
2258: * ml_find_line().
2259: */
2260: if (count == 1)
2261: {
2262: mf_free(mfp, hp); /* free the data block */
2263: buf->b_ml.ml_locked = NULL;
2264:
2265: for (stack_idx = buf->b_ml.ml_stack_top - 1; stack_idx >= 0; --stack_idx)
2266: {
2267: buf->b_ml.ml_stack_top = 0; /* stack is invalid when failing */
2268: ip = &(buf->b_ml.ml_stack[stack_idx]);
2269: idx = ip->ip_index;
2270: if ((hp = mf_get(mfp, ip->ip_bnum, 1)) == NULL)
2271: return FAIL;
2272: pp = (PTR_BL *)(hp->bh_data); /* must be pointer block */
2273: if (pp->pb_id != PTR_ID)
2274: {
2275: EMSG("pointer block id wrong 4");
2276: mf_put(mfp, hp, FALSE, FALSE);
2277: return FAIL;
2278: }
2279: count = --(pp->pb_count);
2280: if (count == 0) /* the pointer block becomes empty! */
2281: mf_free(mfp, hp);
2282: else
2283: {
2284: if (count != idx) /* move entries after the deleted one */
2285: vim_memmove(&pp->pb_pointer[idx], &pp->pb_pointer[idx + 1],
2286: (size_t)(count - idx) * sizeof(PTR_EN));
2287: mf_put(mfp, hp, TRUE, FALSE);
2288:
2289: buf->b_ml.ml_stack_top = stack_idx; /* truncate stack */
2290: /* fix line count for rest of blocks in the stack */
2291: if (buf->b_ml.ml_locked_lineadd)
2292: {
2293: ml_lineadd(buf, buf->b_ml.ml_locked_lineadd);
2294: buf->b_ml.ml_stack[buf->b_ml.ml_stack_top].ip_high +=
2295: buf->b_ml.ml_locked_lineadd;
2296: }
2297: ++(buf->b_ml.ml_stack_top);
2298:
2299: return OK;
2300: }
2301: }
2302: CHECK(1, "deleted block 1?");
2303:
2304: return OK;
2305: }
2306:
2307: /*
2308: * delete the text by moving the next lines forwards
2309: */
2310: text_start = dp->db_txt_start;
2311: line_start = ((dp->db_index[idx]) & DB_INDEX_MASK);
2312: if (idx == 0) /* first line in block, text at the end */
2313: line_size = dp->db_txt_end - line_start;
2314: else
2315: line_size = ((dp->db_index[idx - 1]) & DB_INDEX_MASK) - line_start;
2316: vim_memmove((char *)dp + text_start + line_size, (char *)dp + text_start,
2317: (size_t)(line_start - text_start));
2318:
2319: /*
2320: * delete the index by moving the next indexes backwards
2321: * Adjust the indexes for the text movement.
2322: */
2323: for (i = idx; i < count - 1; ++i)
2324: dp->db_index[i] = dp->db_index[i + 1] + line_size;
2325:
2326: dp->db_free += line_size + INDEX_SIZE;
2327: dp->db_txt_start += line_size;
2328: --(dp->db_line_count);
2329:
2330: /*
2331: * mark the block dirty and make sure it is in the file (for recovery)
2332: */
2333: buf->b_ml.ml_flags |= (ML_LOCKED_DIRTY | ML_LOCKED_POS);
2334:
2335: return OK;
2336: }
2337:
2338: /*
2339: * set the B_MARKED flag for line 'lnum'
2340: */
2341: void
2342: ml_setmarked(lnum)
2343: linenr_t lnum;
2344: {
2345: BHDR *hp;
2346: DATA_BL *dp;
2347: /* invalid line number */
2348: if (lnum < 1 || lnum > curbuf->b_ml.ml_line_count ||
2349: curbuf->b_ml.ml_mfp == NULL)
2350: return; /* give error message? */
2351:
2352: if (lowest_marked == 0 || lowest_marked > lnum)
2353: lowest_marked = lnum;
2354:
2355: /*
2356: * find the data block containing the line
2357: * This also fills the stack with the blocks from the root to the data block
2358: * This also releases any locked block.
2359: */
2360: if ((hp = ml_find_line(curbuf, lnum, ML_FIND)) == NULL)
2361: return; /* give error message? */
2362:
2363: dp = (DATA_BL *)(hp->bh_data);
2364: dp->db_index[lnum - curbuf->b_ml.ml_locked_low] |= DB_MARKED;
2365: curbuf->b_ml.ml_flags |= ML_LOCKED_DIRTY;
2366: }
2367:
2368: /*
2369: * find the first line with its B_MARKED flag set
2370: */
2371: linenr_t
2372: ml_firstmarked()
2373: {
2374: BHDR *hp;
2375: DATA_BL *dp;
2376: linenr_t lnum;
2377: int i;
2378:
2379: if (curbuf->b_ml.ml_mfp == NULL)
2380: return (linenr_t) 0;
2381:
2382: /*
2383: * The search starts with lowest_marked line. This is the last line where
2384: * a mark was found, adjusted by inserting/deleting lines.
2385: */
2386: for (lnum = lowest_marked; lnum <= curbuf->b_ml.ml_line_count; )
2387: {
2388: /*
2389: * Find the data block containing the line.
2390: * This also fills the stack with the blocks from the root to the data
2391: * block This also releases any locked block.
2392: */
2393: if ((hp = ml_find_line(curbuf, lnum, ML_FIND)) == NULL)
2394: return (linenr_t)0; /* give error message? */
2395:
2396: dp = (DATA_BL *)(hp->bh_data);
2397:
2398: for (i = lnum - curbuf->b_ml.ml_locked_low;
2399: lnum <= curbuf->b_ml.ml_locked_high; ++i, ++lnum)
2400: if ((dp->db_index[i]) & DB_MARKED)
2401: {
2402: (dp->db_index[i]) &= DB_INDEX_MASK;
2403: curbuf->b_ml.ml_flags |= ML_LOCKED_DIRTY;
2404: lowest_marked = lnum + 1;
2405: return lnum;
2406: }
2407: }
2408:
2409: return (linenr_t) 0;
2410: }
2411:
2412: /*
2413: * return TRUE if line 'lnum' has a mark
2414: */
2415: int
2416: ml_has_mark(lnum)
2417: linenr_t lnum;
2418: {
2419: BHDR *hp;
2420: DATA_BL *dp;
2421:
2422: if (curbuf->b_ml.ml_mfp == NULL ||
2423: (hp = ml_find_line(curbuf, lnum, ML_FIND)) == NULL)
2424: return FALSE;
2425:
2426: dp = (DATA_BL *)(hp->bh_data);
2427: return (int)((dp->db_index[lnum - curbuf->b_ml.ml_locked_low]) & DB_MARKED);
2428: }
2429:
2430: /*
2431: * clear all DB_MARKED flags
2432: */
2433: void
2434: ml_clearmarked()
2435: {
2436: BHDR *hp;
2437: DATA_BL *dp;
2438: linenr_t lnum;
2439: int i;
2440:
2441: if (curbuf->b_ml.ml_mfp == NULL) /* nothing to do */
2442: return;
2443:
2444: /*
2445: * The search starts with line lowest_marked.
2446: */
2447: for (lnum = lowest_marked; lnum <= curbuf->b_ml.ml_line_count; )
2448: {
2449: /*
2450: * Find the data block containing the line.
2451: * This also fills the stack with the blocks from the root to the data block
2452: * This also releases any locked block.
2453: */
2454: if ((hp = ml_find_line(curbuf, lnum, ML_FIND)) == NULL)
2455: return; /* give error message? */
2456:
2457: dp = (DATA_BL *)(hp->bh_data);
2458:
2459: for (i = lnum - curbuf->b_ml.ml_locked_low;
2460: lnum <= curbuf->b_ml.ml_locked_high; ++i, ++lnum)
2461: if ((dp->db_index[i]) & DB_MARKED)
2462: {
2463: (dp->db_index[i]) &= DB_INDEX_MASK;
2464: curbuf->b_ml.ml_flags |= ML_LOCKED_DIRTY;
2465: }
2466: }
2467:
2468: lowest_marked = 0;
2469: return;
2470: }
2471:
2472: /*
2473: * flush ml_line if necessary
2474: */
2475: static void
2476: ml_flush_line(buf)
2477: BUF *buf;
2478: {
2479: BHDR *hp;
2480: DATA_BL *dp;
2481: linenr_t lnum;
2482: char_u *new_line;
2483: char_u *old_line;
2484: colnr_t new_len;
2485: int old_len;
2486: int extra;
2487: int idx;
2488: int start;
2489: int count;
2490: int i;
2491:
2492: if (buf->b_ml.ml_line_lnum == 0 ||
2493: buf->b_ml.ml_mfp == NULL) /* nothing to do */
2494: return;
2495:
2496: if (buf->b_ml.ml_flags & ML_LINE_DIRTY)
2497: {
2498: lnum = buf->b_ml.ml_line_lnum;
2499: new_line = buf->b_ml.ml_line_ptr;
2500:
2501: hp = ml_find_line(buf, lnum, ML_FIND);
2502: if (hp == NULL)
2503: EMSGN("Cannot find line %ld", lnum);
2504: else
2505: {
2506: dp = (DATA_BL *)(hp->bh_data);
2507: idx = lnum - buf->b_ml.ml_locked_low;
2508: start = ((dp->db_index[idx]) & DB_INDEX_MASK);
2509: old_line = (char_u *)dp + start;
2510: if (idx == 0) /* line is last in block */
2511: old_len = dp->db_txt_end - start;
2512: else /* text of previous line follows */
2513: old_len = (dp->db_index[idx - 1] & DB_INDEX_MASK) - start;
2514: new_len = STRLEN(new_line) + 1;
2515: extra = new_len - old_len; /* negative if lines gets smaller */
2516:
2517: /*
2518: * if new line fits in data block, replace directly
2519: */
2520: if ((int)dp->db_free >= extra)
2521: {
2522: /* if the length changes and there are following lines */
2523: count = buf->b_ml.ml_locked_high - buf->b_ml.ml_locked_low + 1;
2524: if (extra != 0 && idx < count - 1)
2525: {
2526: /* move text of following lines */
2527: vim_memmove((char *)dp + dp->db_txt_start - extra,
2528: (char *)dp + dp->db_txt_start,
2529: (size_t)(start - dp->db_txt_start));
2530:
2531: /* adjust pointers of this and following lines */
2532: for (i = idx + 1; i < count; ++i)
2533: dp->db_index[i] -= extra;
2534: }
2535: dp->db_index[idx] -= extra;
2536:
2537: /* adjust free space */
2538: dp->db_free -= extra;
2539: dp->db_txt_start -= extra;
2540:
2541: /* copy new line into the data block */
2542: vim_memmove(old_line - extra, new_line, (size_t)new_len);
2543: buf->b_ml.ml_flags |= (ML_LOCKED_DIRTY | ML_LOCKED_POS);
2544: }
2545: else
2546: {
2547: /*
2548: * Cannot do it in one data block: delete and append.
2549: */
2550: /* How about handling errors??? */
2551: (void)ml_delete_int(buf, lnum, FALSE);
2552: (void)ml_append_int(buf, lnum - 1, new_line, new_len, FALSE);
2553: }
2554: }
2555: vim_free(new_line);
2556: }
2557:
2558: buf->b_ml.ml_line_lnum = 0;
2559: }
2560:
2561: /*
2562: * create a new, empty, data block
2563: */
2564: static BHDR *
2565: ml_new_data(mfp, negative, page_count)
2566: MEMFILE *mfp;
2567: int negative;
2568: int page_count;
2569: {
2570: BHDR *hp;
2571: DATA_BL *dp;
2572:
2573: if ((hp = mf_new(mfp, negative, page_count)) == NULL)
2574: return NULL;
2575:
2576: dp = (DATA_BL *)(hp->bh_data);
2577: dp->db_id = DATA_ID;
2578: dp->db_txt_start = dp->db_txt_end = page_count * mfp->mf_page_size;
2579: dp->db_free = dp->db_txt_start - HEADER_SIZE;
2580: dp->db_line_count = 0;
2581:
2582: return hp;
2583: }
2584:
2585: /*
2586: * create a new, empty, pointer block
2587: */
2588: static BHDR *
2589: ml_new_ptr(mfp)
2590: MEMFILE *mfp;
2591: {
2592: BHDR *hp;
2593: PTR_BL *pp;
2594:
2595: if ((hp = mf_new(mfp, FALSE, 1)) == NULL)
2596: return NULL;
2597:
2598: pp = (PTR_BL *)(hp->bh_data);
2599: pp->pb_id = PTR_ID;
2600: pp->pb_count = 0;
2601: pp->pb_count_max = (mfp->mf_page_size - sizeof(PTR_BL)) / sizeof(PTR_EN) + 1;
2602:
2603: return hp;
2604: }
2605:
2606: /*
2607: * lookup line 'lnum' in a memline
2608: *
2609: * action: if ML_DELETE or ML_INSERT the line count is updated while searching
2610: * if ML_FLUSH only flush a locked block
2611: * if ML_FIND just find the line
2612: *
2613: * If the block was found it is locked and put in ml_locked.
2614: * The stack is updated to lead to the locked block. The ip_high field in
2615: * the stack is updated to reflect the last line in the block AFTER the
2616: * insert or delete, also if the pointer block has not been updated yet. But
2617: * if if ml_locked != NULL ml_locked_lineadd must be added to ip_high.
2618: *
2619: * return: NULL for failure, pointer to block header otherwise
2620: */
2621: static BHDR *
2622: ml_find_line(buf, lnum, action)
2623: BUF *buf;
2624: linenr_t lnum;
2625: int action;
2626: {
2627: DATA_BL *dp;
2628: PTR_BL *pp;
2629: IPTR *ip;
2630: BHDR *hp;
2631: MEMFILE *mfp;
2632: linenr_t t;
2633: blocknr_t bnum, bnum2;
2634: int dirty;
2635: linenr_t low, high;
2636: int top;
2637: int page_count;
2638: int idx;
2639:
2640: mfp = buf->b_ml.ml_mfp;
2641:
2642: /*
2643: * If there is a locked block check if the wanted line is in it.
2644: * If not, flush and release the locked block.
2645: * Don't do this for ML_INSERT_SAME, because the stack need to be updated.
2646: * Don't do this for ML_FLUSH, because we want to flush the locked block.
2647: */
2648: if (buf->b_ml.ml_locked)
2649: {
2650: if (ML_SIMPLE(action) && buf->b_ml.ml_locked_low <= lnum &&
2651: buf->b_ml.ml_locked_high >= lnum)
2652: {
2653: /* remember to update pointer blocks and stack later */
2654: if (action == ML_INSERT)
2655: {
2656: ++(buf->b_ml.ml_locked_lineadd);
2657: ++(buf->b_ml.ml_locked_high);
2658: }
2659: else if (action == ML_DELETE)
2660: {
2661: --(buf->b_ml.ml_locked_lineadd);
2662: --(buf->b_ml.ml_locked_high);
2663: }
2664: return (buf->b_ml.ml_locked);
2665: }
2666:
2667: mf_put(mfp, buf->b_ml.ml_locked, buf->b_ml.ml_flags & ML_LOCKED_DIRTY,
2668: buf->b_ml.ml_flags & ML_LOCKED_POS);
2669: buf->b_ml.ml_locked = NULL;
2670:
2671: /*
2672: * if lines have been added or deleted in the locked block, need to
2673: * update the line count in pointer blocks
2674: */
2675: if (buf->b_ml.ml_locked_lineadd)
2676: ml_lineadd(buf, buf->b_ml.ml_locked_lineadd);
2677: }
2678:
2679: if (action == ML_FLUSH) /* nothing else to do */
2680: return NULL;
2681:
2682: bnum = 1; /* start at the root of the tree */
2683: page_count = 1;
2684: low = 1;
2685: high = buf->b_ml.ml_line_count;
2686:
2687: if (action == ML_FIND) /* first try stack entries */
2688: {
2689: for (top = buf->b_ml.ml_stack_top - 1; top >= 0; --top)
2690: {
2691: ip = &(buf->b_ml.ml_stack[top]);
2692: if (ip->ip_low <= lnum && ip->ip_high >= lnum)
2693: {
2694: bnum = ip->ip_bnum;
2695: low = ip->ip_low;
2696: high = ip->ip_high;
2697: buf->b_ml.ml_stack_top = top; /* truncate stack at prev entry */
2698: break;
2699: }
2700: }
2701: if (top < 0)
2702: buf->b_ml.ml_stack_top = 0; /* not found, start at the root */
2703: }
2704: else /* ML_DELETE or ML_INSERT */
2705: buf->b_ml.ml_stack_top = 0; /* start at the root */
2706:
2707: /*
2708: * search downwards in the tree until a data block is found
2709: */
2710: for (;;)
2711: {
2712: if ((hp = mf_get(mfp, bnum, page_count)) == NULL)
2713: goto error_noblock;
2714:
2715: /*
2716: * update high for insert/delete
2717: */
2718: if (action == ML_INSERT)
2719: ++high;
2720: else if (action == ML_DELETE)
2721: --high;
2722:
2723: dp = (DATA_BL *)(hp->bh_data);
2724: if (dp->db_id == DATA_ID) /* data block */
2725: {
2726: buf->b_ml.ml_locked = hp;
2727: buf->b_ml.ml_locked_low = low;
2728: buf->b_ml.ml_locked_high = high;
2729: buf->b_ml.ml_locked_lineadd = 0;
2730: buf->b_ml.ml_flags &= ~(ML_LOCKED_DIRTY | ML_LOCKED_POS);
2731: return hp;
2732: }
2733:
2734: pp = (PTR_BL *)(dp); /* must be pointer block */
2735: if (pp->pb_id != PTR_ID)
2736: {
2737: EMSG("pointer block id wrong");
2738: goto error_block;
2739: }
2740:
2741: if ((top = ml_add_stack(buf)) < 0) /* add new entry to stack */
2742: goto error_block;
2743: ip = &(buf->b_ml.ml_stack[top]);
2744: ip->ip_bnum = bnum;
2745: ip->ip_low = low;
2746: ip->ip_high = high;
2747: ip->ip_index = -1; /* index not known yet */
2748:
2749: dirty = FALSE;
2750: for (idx = 0; idx < (int)pp->pb_count; ++idx)
2751: {
2752: t = pp->pb_pointer[idx].pe_line_count;
2753: CHECK(t == 0, "pe_line_count is zero");
2754: if ((low += t) > lnum)
2755: {
2756: ip->ip_index = idx;
2757: bnum = pp->pb_pointer[idx].pe_bnum;
2758: page_count = pp->pb_pointer[idx].pe_page_count;
2759: high = low - 1;
2760: low -= t;
2761:
2762: /*
2763: * a negative block number may have been changed
2764: */
2765: if (bnum < 0)
2766: {
2767: bnum2 = mf_trans_del(mfp, bnum);
2768: if (bnum != bnum2)
2769: {
2770: bnum = bnum2;
2771: pp->pb_pointer[idx].pe_bnum = bnum;
2772: dirty = TRUE;
2773: }
2774: }
2775:
2776: break;
2777: }
2778: }
2779: if (idx >= (int)pp->pb_count) /* past the end: something wrong! */
2780: {
2781: if (lnum > buf->b_ml.ml_line_count)
2782: EMSGN("line number out of range: %ld past the end",
2783: lnum - buf->b_ml.ml_line_count);
2784:
2785: else
2786: EMSGN("line count wrong in block %ld", bnum);
2787: goto error_block;
2788: }
2789: if (action == ML_DELETE)
2790: {
2791: pp->pb_pointer[idx].pe_line_count--;
2792: dirty = TRUE;
2793: }
2794: else if (action == ML_INSERT)
2795: {
2796: pp->pb_pointer[idx].pe_line_count++;
2797: dirty = TRUE;
2798: }
2799: mf_put(mfp, hp, dirty, FALSE);
2800: }
2801:
2802: error_block:
2803: mf_put(mfp, hp, FALSE, FALSE);
2804: error_noblock:
2805: /*
2806: * If action is ML_DELETE or ML_INSERT we have to correct the tree for
2807: * the incremented/decremented line counts, because there won't be a line
2808: * inserted/deleted after all.
2809: */
2810: if (action == ML_DELETE)
2811: ml_lineadd(buf, 1);
2812: else if (action == ML_INSERT)
2813: ml_lineadd(buf, -1);
2814: buf->b_ml.ml_stack_top = 0;
2815: return NULL;
2816: }
2817:
2818: /*
2819: * add an entry to the info pointer stack
2820: *
2821: * return -1 for failure, number of the new entry otherwise
2822: */
2823: static int
2824: ml_add_stack(buf)
2825: BUF *buf;
2826: {
2827: int top;
2828: IPTR *newstack;
2829:
2830: top = buf->b_ml.ml_stack_top;
2831:
2832: /* may have to increase the stack size */
2833: if (top == buf->b_ml.ml_stack_size)
2834: {
2835: CHECK(top > 0, "Stack size increases"); /* more than 5 levels??? */
2836:
2837: newstack = (IPTR *)alloc((unsigned)sizeof(IPTR) *
2838: (buf->b_ml.ml_stack_size + STACK_INCR));
2839: if (newstack == NULL)
2840: return -1;
2841: vim_memmove(newstack, buf->b_ml.ml_stack, (size_t)top * sizeof(IPTR));
2842: vim_free(buf->b_ml.ml_stack);
2843: buf->b_ml.ml_stack = newstack;
2844: buf->b_ml.ml_stack_size += STACK_INCR;
2845: }
2846:
2847: buf->b_ml.ml_stack_top++;
2848: return top;
2849: }
2850:
2851: /*
2852: * Update the pointer blocks on the stack for inserted/deleted lines.
2853: * The stack itself is also updated.
2854: *
2855: * When a insert/delete line action fails, the line is not inserted/deleted,
2856: * but the pointer blocks have already been updated. That is fixed here by
2857: * walking through the stack.
2858: *
2859: * Count is the number of lines added, negative if lines have been deleted.
2860: */
2861: static void
2862: ml_lineadd(buf, count)
2863: BUF *buf;
2864: int count;
2865: {
2866: int idx;
2867: IPTR *ip;
2868: PTR_BL *pp;
2869: MEMFILE *mfp = buf->b_ml.ml_mfp;
2870: BHDR *hp;
2871:
2872: for (idx = buf->b_ml.ml_stack_top - 1; idx >= 0; --idx)
2873: {
2874: ip = &(buf->b_ml.ml_stack[idx]);
2875: if ((hp = mf_get(mfp, ip->ip_bnum, 1)) == NULL)
2876: break;
2877: pp = (PTR_BL *)(hp->bh_data); /* must be pointer block */
2878: if (pp->pb_id != PTR_ID)
2879: {
2880: mf_put(mfp, hp, FALSE, FALSE);
2881: EMSG("pointer block id wrong 2");
2882: break;
2883: }
2884: pp->pb_pointer[ip->ip_index].pe_line_count += count;
2885: ip->ip_high += count;
2886: mf_put(mfp, hp, TRUE, FALSE);
2887: }
2888: }
2889:
2890: /*
2891: * make swap file name out of the filename and a directory name
2892: */
2893: static char_u *
2894: makeswapname(buf, dir_name)
2895: BUF *buf;
2896: char_u *dir_name;
2897: {
1.2 ! downsj 2898: char_u *r, *s;
1.1 downsj 2899:
2900: #ifdef VMS
2901: r = modname(buf->b_xfilename, (char_u *)"_swp");
2902: #else
2903: r = modname(buf->b_xfilename, (char_u *)".swp");
2904: #endif
1.2 ! downsj 2905: if (r == NULL) /* out of memory */
! 2906: return NULL;
1.1 downsj 2907:
1.2 ! downsj 2908: s = get_file_in_dir(r, dir_name);
1.1 downsj 2909: vim_free(r);
2910: return s;
2911: }
2912:
2913: /*
1.2 ! downsj 2914: * Get file name to use for swap file or backup file.
! 2915: * Use the name of the edited file "fname" and an entry in the 'dir' or 'bdir'
! 2916: * option "dirname".
! 2917: * - If "dirname" is ".", return "fname".
! 2918: * - If "dirname" starts with "./", insert "dirname" in "fname".
! 2919: * - Otherwise, prepend "dirname" to the tail of "fname".
! 2920: *
! 2921: * The return value is an allocated string and can be NULL.
! 2922: */
! 2923: char_u *
! 2924: get_file_in_dir(fname, dirname)
! 2925: char_u *fname;
! 2926: char_u *dirname;
! 2927: {
! 2928: char_u *t;
! 2929: char_u *tail;
! 2930: char_u *retval;
! 2931: int save_char;
! 2932:
! 2933: tail = gettail(fname);
! 2934:
! 2935: if (dirname[0] == '.' && dirname[1] == NUL)
! 2936: retval = strsave(fname);
! 2937: else if (dirname[0] == '.' && ispathsep(dirname[1]))
! 2938: {
! 2939: if (tail == fname) /* no path before file name */
! 2940: retval = concat_fnames(dirname + 2, tail, TRUE);
! 2941: else
! 2942: {
! 2943: save_char = *tail;
! 2944: *tail = NUL;
! 2945: t = concat_fnames(fname, dirname + 2, TRUE);
! 2946: *tail = save_char;
! 2947: if (t == NULL) /* out of memory */
! 2948: retval = NULL;
! 2949: else
! 2950: {
! 2951: retval = concat_fnames(t, tail, TRUE);
! 2952: vim_free(t);
! 2953: }
! 2954: }
! 2955: }
! 2956: else
! 2957: retval = concat_fnames(dirname, tail, TRUE);
! 2958:
! 2959: return retval;
! 2960: }
! 2961:
! 2962: /*
1.1 downsj 2963: * Find out what name to use for the swap file for buffer 'buf'.
2964: *
2965: * Several names are tried to find one that does not exist
2966: *
1.2 ! downsj 2967: * Note: if BASENAMELEN is not correct, you will get error messages for
1.1 downsj 2968: * not being able to open the swapfile
2969: */
2970: static char_u *
2971: findswapname(buf, dirp, old_fname)
2972: BUF *buf;
2973: char_u **dirp; /* pointer to list of directories */
2974: char_u *old_fname; /* don't give warning for this filename */
2975: {
2976: char_u *fname;
2977: int n;
2978: time_t x;
2979: char_u *dir_name;
2980:
2981: #ifdef AMIGA
2982: BPTR fh;
2983: #endif
2984:
2985: #ifndef SHORT_FNAME
2986: int r;
2987: FILE *dummyfd = NULL;
2988:
2989: /*
2990: * If we start editing a new file, e.g. "test.doc", which resides on an MSDOS
2991: * compatible filesystem, it is possible that the file "test.doc.swp" which we
2992: * create will be exactly the same file. To avoid this problem we temporarily
2993: * create "test.doc".
2994: */
2995: if (!(buf->b_p_sn || buf->b_shortname) && buf->b_xfilename &&
2996: getperm(buf->b_xfilename) < 0)
2997: dummyfd = fopen((char *)buf->b_xfilename, "w");
2998: #endif
2999:
3000: /*
3001: * Isolate a directory name from *dirp and put it in dir_name.
1.2 ! downsj 3002: * First allocate some memory to put the directory name in.
1.1 downsj 3003: */
3004: dir_name = alloc((unsigned)STRLEN(*dirp) + 1);
3005: if (dir_name != NULL)
3006: (void)copy_option_part(dirp, dir_name, 31000, ",");
3007:
3008: /*
3009: * we try different names until we find one that does not exist yet
3010: */
3011: if (dir_name == NULL) /* out of memory */
3012: fname = NULL;
3013: else
3014: fname = makeswapname(buf, dir_name);
3015:
3016: for (;;)
3017: {
3018: if (fname == NULL) /* must be out of memory */
3019: break;
3020: if ((n = STRLEN(fname)) == 0) /* safety check */
3021: {
3022: vim_free(fname);
3023: fname = NULL;
3024: break;
3025: }
3026: #if (defined(UNIX) || defined(OS2)) && !defined(ARCHIE) && !defined(SHORT_FNAME)
3027: /*
3028: * Some systems have a MS-DOS compatible filesystem that use 8.3 character
3029: * file names. If this is the first try and the swap file name does not fit in
3030: * 8.3, detect if this is the case, set shortname and try again.
3031: */
3032: if (fname[n - 1] == 'p' && !(buf->b_p_sn || buf->b_shortname))
3033: {
3034: char_u *tail;
3035: char_u *fname2;
3036: struct stat s1, s2;
3037: int f1, f2;
3038: int created1 = FALSE, created2 = FALSE;
3039: int same = FALSE;
3040:
3041: /*
3042: * Check if swapfilename does not fit in 8.3:
3043: * It either contains two dots or it is longer than 8 chars.
3044: */
3045: tail = gettail(buf->b_xfilename);
3046: if (vim_strchr(tail, '.') != NULL || STRLEN(tail) > (size_t)8)
3047: {
3048: fname2 = alloc(n + 1);
3049: if (fname2 != NULL)
3050: {
3051: STRCPY(fname2, fname);
3052: if (vim_strchr(tail, '.') != NULL)
3053: fname2[n - 1] = 'x'; /* change ".swp" to ".swx" */
3054: else
3055: fname2[n - 5] += 1; /* change "x.swp" to "y.swp" */
3056: /*
3057: * may need to create the files to be able to use stat()
3058: */
3059: f1 = open((char *)fname, O_RDONLY | O_EXTRA);
3060: if (f1 < 0)
3061: {
3062: f1 = open((char *)fname, O_RDWR|O_CREAT|O_EXCL|O_EXTRA
3063: #ifdef AMIGA /* Amiga has no mode argument */
3064: );
3065: #endif
3066: #ifdef UNIX /* open in rw------- mode */
3067: , (mode_t)0600);
3068: #endif
3069: #if defined(MSDOS) || defined(WIN32) || defined(OS2) /* open read/write */
3070: , S_IREAD | S_IWRITE);
3071: #endif
3072: #if defined(OS2)
3073: if (f1 < 0 && errno == ENOENT)
3074: same = TRUE;
3075: #endif
3076: created1 = TRUE;
3077: }
3078: if (f1 >= 0)
3079: {
3080: f2 = open((char *)fname2, O_RDONLY | O_EXTRA);
3081: if (f2 < 0)
3082: {
3083: f2 = open((char *)fname2,
3084: O_RDWR|O_CREAT|O_EXCL|O_EXTRA
3085: #ifdef AMIGA /* Amiga has no mode argument */
3086: );
3087: #endif
3088: #ifdef UNIX /* open in rw------- mode */
3089: , (mode_t)0600);
3090: #endif
3091: #if defined(MSDOS) || defined(WIN32) || defined(OS2) /* open read/write */
3092: , S_IREAD | S_IWRITE);
3093: #endif
3094: created2 = TRUE;
3095: }
3096: if (f2 >= 0)
3097: {
3098: /*
3099: * Both files exist now. If stat() returns the
3100: * same device and inode they are the same file.
3101: */
3102: if (fstat(f1, &s1) != -1 &&
3103: fstat(f2, &s2) != -1 &&
3104: s1.st_dev == s2.st_dev &&
3105: s1.st_ino == s2.st_ino)
3106: same = TRUE;
3107: close(f2);
3108: if (created2)
3109: vim_remove(fname2);
3110: }
3111: close(f1);
3112: if (created1)
3113: vim_remove(fname);
3114: }
3115: vim_free(fname2);
3116: if (same)
3117: {
3118: buf->b_shortname = TRUE;
3119: vim_free(fname);
3120: fname = makeswapname(buf, dir_name);
3121: continue; /* try again with b_shortname set */
3122: }
3123: }
3124: }
3125: }
3126: #endif
3127: /*
3128: * check if the swapfile already exists
3129: */
3130: if (getperm(fname) < 0) /* it does not exist */
3131: {
3132: #ifdef AMIGA
3133: fh = Open((UBYTE *)fname, (long)MODE_NEWFILE);
3134: /*
3135: * on the Amiga getperm() will return -1 when the file exists but
3136: * is being used by another program. This happens if you edit
3137: * a file twice.
3138: */
3139: if (fh != (BPTR)NULL) /* can open file, OK */
3140: {
3141: Close(fh);
3142: break;
3143: }
3144: if (IoErr() != ERROR_OBJECT_IN_USE &&
3145: IoErr() != ERROR_OBJECT_EXISTS)
3146: #endif
3147: break;
3148: }
3149: /*
3150: * A file name equal to old_fname is OK to use.
3151: */
3152: if (old_fname != NULL && fnamecmp(fname, old_fname) == 0)
3153: break;
3154:
3155: /*
3156: * get here when file already exists
3157: */
3158: if (fname[n - 1] == 'p') /* first try */
3159: {
3160: #ifndef SHORT_FNAME
3161: /*
3162: * on MS-DOS compatible filesystems (e.g. messydos) file.doc.swp
3163: * and file.doc are the same file. To guess if this problem is
3164: * present try if file.doc.swx exists. If it does, we set
3165: * buf->b_shortname and try file_doc.swp (dots replaced by
3166: * underscores for this file), and try again. If it doesn't we
3167: * assume that "file.doc.swp" already exists.
3168: */
3169: if (!(buf->b_p_sn || buf->b_shortname)) /* not tried yet */
3170: {
3171: fname[n - 1] = 'x';
3172: r = getperm(fname); /* try "file.swx" */
3173: fname[n - 1] = 'p';
3174: if (r >= 0) /* "file.swx" seems to exist */
3175: {
3176: buf->b_shortname = TRUE;
3177: vim_free(fname);
3178: fname = makeswapname(buf, dir_name);
3179: continue; /* try again with '.' replaced by '_' */
3180: }
3181: }
3182: #endif
3183: /*
3184: * If we get here the ".swp" file really exists.
3185: * Give an error message, unless recovering, no file name, we are
3186: * viewing a help file or when the path of the file is different
3187: * (happens when all .swp files are in one directory).
3188: */
3189: if (!recoverymode && buf->b_xfilename != NULL && !buf->b_help)
3190: {
3191: int fd;
3192: struct block0 b0;
3193: int differ = FALSE;
3194:
3195: /*
3196: * Try to read block 0 from the swap file to get the original
3197: * file name (and inode number).
3198: */
3199: fd = open((char *)fname, O_RDONLY | O_EXTRA);
3200: if (fd >= 0)
3201: {
3202: if (read(fd, (char *)&b0, sizeof(b0)) == sizeof(b0))
3203: {
3204: /*
3205: * The name in the swap file may be "~user/path/file".
3206: * Expand it first.
3207: */
3208: expand_env(b0.b0_fname, NameBuff, MAXPATHL);
3209: #ifdef CHECK_INODE
3210: if (fnamecmp_ino(buf->b_filename, NameBuff,
3211: char_to_long(b0.b0_ino)))
3212: differ = TRUE;
3213: #else
3214: if (fnamecmp(NameBuff, buf->b_filename) != 0)
3215: differ = TRUE;
3216: #endif
3217: }
3218: close(fd);
3219: }
3220: if (differ == FALSE)
3221: {
3222: struct stat st;
3223:
3224: ++no_wait_return;
3225: #ifdef SLEEP_IN_EMSG
3226: ++dont_sleep;
3227: #endif
3228: (void)EMSG("ATTENTION");
3229: #ifdef SLEEP_IN_EMSG
3230: --dont_sleep;
3231: #endif
3232: MSG_OUTSTR("\nFound a swap file by the name \"");
3233: msg_home_replace(fname);
3234: MSG_OUTSTR("\"\n");
3235: swapfile_info(fname);
3236: MSG_OUTSTR("While opening file \"");
3237: msg_outtrans(buf->b_xfilename);
3238: MSG_OUTSTR("\"\n");
3239: if (stat((char *)buf->b_xfilename, &st) != -1)
3240: {
3241: MSG_OUTSTR(" dated: ");
3242: x = st.st_mtime; /* Manx C can't do &st.st_mtime */
3243: MSG_OUTSTR(ctime(&x));
3244: }
3245: MSG_OUTSTR("\n(1) Another program may be editing the same file.\n");
3246: MSG_OUTSTR(" If this is the case, quit this edit session to avoid having\n");
3247: MSG_OUTSTR(" two different instances of the same file when making changes.\n");
3248: MSG_OUTSTR("\n(2) An edit session for this file crashed.\n");
3249: MSG_OUTSTR(" If this is the case, use \":recover\" or \"vim -r ");
3250: msg_outtrans(buf->b_xfilename);
3251: MSG_OUTSTR("\"\n to recover the changes (see \":help recovery)\".\n");
3252: MSG_OUTSTR(" If you did this already, delete the swap file \"");
3253: msg_outtrans(fname);
3254: MSG_OUTSTR("\"\n to avoid this message.\n\n");
3255: cmdline_row = msg_row;
3256: --no_wait_return;
3257: need_wait_return = TRUE; /* call wait_return later */
3258: }
3259: }
3260: }
3261:
3262: if (fname[n - 1] == 'a') /* tried enough names, give up */
3263: {
3264: vim_free(fname);
3265: fname = NULL;
3266: break;
3267: }
3268: --fname[n - 1]; /* change last char of the name */
3269: }
3270:
3271: vim_free(dir_name);
3272: #ifndef SHORT_FNAME
3273: if (dummyfd) /* file has been created temporarily */
3274: {
3275: fclose(dummyfd);
3276: vim_remove(buf->b_xfilename);
3277: }
3278: #endif
3279: return fname;
3280: }
3281:
3282: static int
3283: b0_magic_wrong(b0p)
3284: ZERO_BL *b0p;
3285: {
3286: return (b0p->b0_magic_long != (long)B0_MAGIC_LONG ||
3287: b0p->b0_magic_int != (int)B0_MAGIC_INT ||
3288: b0p->b0_magic_short != (short)B0_MAGIC_SHORT ||
3289: b0p->b0_magic_char != B0_MAGIC_CHAR);
3290: }
3291:
3292: #ifdef CHECK_INODE
3293: /*
3294: * Compare current file name with file name from swap file.
3295: * Try to use inode numbers when possible.
3296: * Return non-zero when files are different.
3297: *
3298: * When comparing file names a few things have to be taken into consideration:
3299: * - When working over a network the full path of a file depends on the host.
3300: * We check the inode number if possible. It is not 100% reliable though,
3301: * because the device number cannot be used over a network.
3302: * - When a file does not exist yet (editing a new file) there is no inode
3303: * number.
3304: * - The file name in a swap file may not be valid on the current host. The
3305: * "~user" form is used whenever possible to avoid this.
3306: *
3307: * This is getting complicated, let's make a table:
3308: *
3309: * ino_c ino_s fname_c fname_s differ =
3310: *
3311: * both files exist -> compare inode numbers:
3312: * != 0 != 0 X X ino_c != ino_s
3313: *
3314: * inode number(s) unknown, file names available -> compare file names
3315: * == 0 X OK OK fname_c != fname_s
3316: * X == 0 OK OK fname_c != fname_s
3317: *
3318: * current file doesn't exist, file for swap file exist, file name(s) not
3319: * available -> probably different
3320: * == 0 != 0 FAIL X TRUE
3321: * == 0 != 0 X FAIL TRUE
3322: *
3323: * current file exists, inode for swap unknown, file name(s) not
3324: * available -> probably different
3325: * != 0 == 0 FAIL X TRUE
3326: * != 0 == 0 X FAIL TRUE
3327: *
3328: * current file doesn't exist, inode for swap unknown, one file name not
3329: * available -> probably different
3330: * == 0 == 0 FAIL OK TRUE
3331: * == 0 == 0 OK FAIL TRUE
3332: *
3333: * current file doesn't exist, inode for swap unknown, both file names not
3334: * available -> probably same file
3335: * == 0 == 0 FAIL FAIL FALSE
3336: */
3337:
3338: static int
3339: fnamecmp_ino(fname_c, fname_s, ino_block0)
3340: char_u *fname_c; /* current file name */
3341: char_u *fname_s; /* file name from swap file */
3342: long ino_block0;
3343: {
3344: struct stat st;
3345: long ino_c = 0; /* ino of current file */
3346: long ino_s; /* ino of file from swap file */
3347: char_u buf_c[MAXPATHL]; /* full path of fname_c */
3348: char_u buf_s[MAXPATHL]; /* full path of fname_s */
3349: int retval_c; /* flag: buf_c valid */
3350: int retval_s; /* flag: buf_s valid */
3351:
3352:
3353: if (stat((char *)fname_c, &st) == 0)
3354: ino_c = st.st_ino;
3355:
3356: /*
3357: * First we try to get the inode from the file name, because the inode in
3358: * the swap file may be outdated. If that fails (e.g. this path is not
3359: * valid on this machine), use the inode from block 0.
3360: */
3361: if (stat((char *)fname_s, &st) == 0)
3362: ino_s = st.st_ino;
3363: else
3364: ino_s = ino_block0;
3365:
3366: if (ino_c && ino_s)
3367: return (ino_c != ino_s);
3368:
3369: /*
3370: * One of the inode numbers is unknown, try a forced FullName() and
3371: * compare the file names.
3372: */
3373: retval_c = FullName(fname_c, buf_c, MAXPATHL, TRUE);
3374: retval_s = FullName(fname_s, buf_s, MAXPATHL, TRUE);
3375: if (retval_c == OK && retval_s == OK)
3376: return (STRCMP(buf_c, buf_s) != 0);
3377:
3378: /*
3379: * Can't compare inodes or filenames, guess that the files are different,
3380: * unless both appear not to exist at all.
3381: */
3382: if (ino_s == 0 && ino_c == 0 && retval_c == FAIL && retval_s == FAIL)
3383: return FALSE;
3384: return TRUE;
3385: }
3386: #endif /* CHECK_INODE */
3387:
3388: /*
3389: * Move a long integer into a four byte character array.
3390: * Used for machine independency in block zero.
3391: */
3392: static void
3393: long_to_char(n, s)
3394: long n;
3395: char_u *s;
3396: {
3397: s[0] = (n & 0xff);
3398: n >>= 8;
3399: s[1] = (n & 0xff);
3400: n >>= 8;
3401: s[2] = (n & 0xff);
3402: n >>= 8;
3403: s[3] = (n & 0xff);
3404: }
3405:
3406: static long
3407: char_to_long(s)
3408: char_u *s;
3409: {
3410: long retval;
3411:
3412: retval = s[3];
3413: retval <<= 8;
3414: retval += s[2];
3415: retval <<= 8;
3416: retval += s[1];
3417: retval <<= 8;
3418: retval += s[0];
3419:
3420: return retval;
3421: }