This is buffer.c in view mode; [Download] [Up]
/* vi:ts=4:sw=4 * * VIM - Vi IMproved by Bram Moolenaar * * Read the file "credits.txt" for a list of people who contributed. * Read the file "uganda.txt" for copying and usage conditions. */ /* * buffer.c: functions for dealing with the buffer structure */ /* * The buffer list is a double linked list of all buffers. * Each buffer can be in one of these states: * never loaded: b_neverloaded == TRUE, only the file name is valid * not loaded: b_ml.ml_mfp == NULL, no memfile allocated * hidden: b_nwindows == 0, loaded but not displayed in a window * normal: loaded and displayed in a window * * Instead of storing file names all over the place, each file name is * stored in the buffer list. It can be referenced by a number. * * The current implementation remembers all file names ever used. */ #include "vim.h" #include "globals.h" #include "proto.h" #include "param.h" static void enter_buffer __ARGS((BUF *)); static BUF *buflist_findname __ARGS((char_u *)); static BUF *buflist_findnr __ARGS((int)); static void buflist_setlnum __ARGS((BUF *, linenr_t)); static linenr_t buflist_findlnum __ARGS((BUF *)); /* * Open current buffer, that is: open the memfile and read the file into memory * return FAIL for failure, OK otherwise */ int open_buffer() { if (readonlymode && curbuf->b_filename != NULL) curbuf->b_p_ro = TRUE; if (ml_open() == FAIL) { /* * There MUST be a memfile, otherwise we can't do anything * If we can't create one for the current buffer, take another buffer */ close_buffer(curbuf, FALSE, FALSE); for (curbuf = firstbuf; curbuf != NULL; curbuf = curbuf->b_next) if (curbuf->b_ml.ml_mfp != NULL) break; /* * if there is no memfile at all, exit * This is OK, since there are no changes to loose. */ if (curbuf == NULL) { EMSG("Cannot allocate buffer, exiting..."); getout(2); } EMSG("Cannot allocate buffer, using other one..."); enter_buffer(curbuf); return FAIL; } if (curbuf->b_filename != NULL) { if (readfile(curbuf->b_filename, curbuf->b_sfilename, (linenr_t)0, TRUE, (linenr_t)0, MAXLNUM) == FAIL) return FAIL; } else MSG("Empty Buffer"); UNCHANGED(curbuf); curbuf->b_neverloaded = FALSE; return OK; } /* * Close the link to a buffer. If "free_buf" is TRUE free the buffer if it * becomes unreferenced. The caller should get a new buffer very soon! * if 'remove' is TRUE, remove the buffer from the buffer list. */ void close_buffer(buf, free_buf, remove) BUF *buf; int free_buf; int remove; { if (buf->b_nwindows > 0) --buf->b_nwindows; if (buf->b_nwindows > 0 || !free_buf) { if (buf == curbuf) u_sync(); /* sync undo before going to another buffer */ return; } buf_freeall(buf); /* free all things allocated for this buffer */ /* * If there is no file name, remove the buffer from the list */ if (buf->b_filename == NULL || remove) { free(buf->b_filename); free(buf->b_sfilename); if (buf->b_prev == NULL) firstbuf = buf->b_next; else buf->b_prev->b_next = buf->b_next; if (buf->b_next == NULL) lastbuf = buf->b_prev; else buf->b_next->b_prev = buf->b_prev; free(buf); } else buf_clear(buf); } /* * buf_clear() - make buffer empty */ void buf_clear(buf) BUF *buf; { buf->b_ml.ml_line_count = 1; buf->b_changed = FALSE; #ifndef MSDOS buf->b_shortname = FALSE; #endif buf->b_p_eol = TRUE; buf->b_ml.ml_mfp = NULL; buf->b_ml.ml_flags = ML_EMPTY; /* empty buffer */ } /* * buf_freeall() - free all things allocated for the buffer */ void buf_freeall(buf) BUF *buf; { u_blockfree(buf); /* free the memory allocated for undo */ ml_close(buf); /* close and delete the memline/memfile */ buf->b_ml.ml_line_count = 0; /* no lines in buffer */ u_clearall(buf); /* reset all undo information */ } /* * Implementation of the command for the buffer list */ int do_buffer(action, start, dir, count, forceit) int action; /* 0 = normal, 1 = split window, 2 = unload, 3 = delete */ int start; /* 0 = current, 1 = first, 2 = last, 3 = modified */ int dir; /* FORWARD or BACKWARD */ int count; /* buffer number or number of buffers */ int forceit; /* TRUE for :bdelete! */ { BUF *buf; int retval; switch (start) { case 0: buf = curbuf; break; case 1: buf = firstbuf; break; case 2: buf = curbuf; while (buf->b_next != NULL) buf = buf->b_next; break; default: buf = curbuf; break; } if (start == 3) /* find next modified buffer */ { while (count-- > 0) { do { buf = buf->b_next; if (buf == NULL) buf = firstbuf; } while (buf != curbuf && !buf->b_changed); } if (!buf->b_changed) { EMSG("No modified buffer found"); return FAIL; } } else if (start == 1 && count) /* find specified buffer number */ { while (buf != NULL && buf->b_fnum != count) buf = buf->b_next; } else { while (buf != NULL && count-- > 0) { if (dir == FORWARD) buf = buf->b_next; else buf = buf->b_prev; } } if (buf == NULL) /* could not find it */ { if (start == 1) EMSG2("Cannot go to buffer %ld", (char_u *)count); else if (dir == FORWARD) EMSG("Cannot go beyond last buffer"); else EMSG("Cannot go before first buffer"); return FAIL; } /* * delete buffer buf from memory and/or the list */ if (action == 2 || action == 3) { if (buf->b_nwindows > 1 || (buf != curbuf && buf->b_nwindows != 0)) { EMSG2("Other window editing buffer %ld", (char_u *)buf->b_fnum); return FAIL; } if (!forceit && buf->b_changed) { EMSG2("No write since last change for buffer %ld (use ! to override)", (char_u *)buf->b_fnum); return FAIL; } /* * if deleting last buffer, make it empty */ if (firstbuf->b_next == NULL) { buf = curbuf; retval = doecmd(NULL, NULL, NULL, FALSE, (linenr_t)1); /* the doecmd() may create a new buffer, then we have to * delete the old one */ if (action == 3 && buf != curbuf) close_buffer(buf, TRUE, action == 3); return retval; } /* * If deleted buffer is not current one, delete it here. * Otherwise find buffer to go to and delete it below. */ { if (buf != curbuf) { close_buffer(buf, TRUE, action == 3); return OK; } if (buf->b_next != NULL) buf = buf->b_next; else buf = buf->b_prev; } } /* * make buf current buffer */ if (action == 1) /* split window first */ { if (win_split(0L, FALSE) == FAIL) return FAIL; } buflist_altlnum(); /* remember curpos.lnum */ close_buffer(curbuf, action == 2 || action == 3, action == 3); enter_buffer(buf); return OK; } /* * enter a new current buffer. * (old curbuf must have been freed already) */ static void enter_buffer(buf) BUF *buf; { int need_fileinfo = TRUE; if (buf->b_neverloaded) { buf_copy_options(curbuf, buf); buf->b_neverloaded = FALSE; } curwin->w_buffer = buf; curbuf = buf; ++curbuf->b_nwindows; if (curbuf->b_ml.ml_mfp == NULL) /* need to load the file */ { open_buffer(); need_fileinfo = FALSE; } buflist_getlnum(); /* restore curpos.lnum */ maketitle(); updateScreen(NOT_VALID); if (need_fileinfo) fileinfo(did_cd); } /* * functions for dealing with the buffer list */ /* * Add a file name to the buffer list. Return a pointer to the buffer. * If the same file name already exists return a pointer to that buffer. * If it does not exist, or if fname == NULL, a new entry is created. * If use_curbuf is TRUE, may use current buffer. * This is the ONLY way to create a new buffer. */ BUF * buflist_new(fname, sfname, lnum, use_curbuf) char_u *fname; char_u *sfname; linenr_t lnum; int use_curbuf; { static int top_file_num = 1; /* highest file number */ BUF *buf; fname_expand(&fname, &sfname); /* * If file name already exists in the list, update the entry */ if (fname != NULL && (buf = buflist_findname(fname)) != NULL) { if (lnum != 0) buflist_setlnum(buf, lnum); if (buf->b_neverloaded && curbuf != NULL && buf != curbuf) buf_copy_options(curbuf, buf); return buf; } /* * If the current buffer has no name and no contents, use the current buffer. * Otherwise: Need to allocate a new buffer structure. * * This is the ONLY place where a new buffer structure is allocated! */ if (use_curbuf && curbuf != NULL && curbuf->b_filename == NULL && curbuf->b_nwindows <= 1 && (curbuf->b_ml.ml_mfp == NULL || curbuf->b_ml.ml_flags == ML_EMPTY)) buf = curbuf; else { buf = (BUF *)alloc((unsigned)sizeof(BUF)); if (buf == NULL) return NULL; memset((char *)buf, 0, sizeof(BUF)); } if (fname != NULL) { buf->b_filename = strsave(fname); buf->b_sfilename = strsave(sfname); } if (buf->b_winlnum == NULL) buf->b_winlnum = (WINLNUM *)alloc((unsigned)sizeof(WINLNUM)); if ((fname != NULL && (buf->b_filename == NULL || buf->b_sfilename == NULL)) || buf->b_winlnum == NULL) { free(buf->b_filename); buf->b_filename = NULL; free(buf->b_sfilename); buf->b_sfilename = NULL; if (buf != curbuf) { free(buf->b_winlnum); free(buf); } return NULL; } if (buf == curbuf) { buf_freeall(buf); /* free all things allocated for this buffer */ buf->b_nwindows = 0; } else { if (curbuf != NULL) /* don't do this for first buffer */ buf_copy_options(curbuf, buf); /* * put new buffer at the end of the buffer list */ buf->b_next = NULL; if (firstbuf == NULL) /* buffer list is empty */ { buf->b_prev = NULL; firstbuf = buf; } else /* append new buffer at end of the list */ { lastbuf->b_next = buf; buf->b_prev = lastbuf; } lastbuf = buf; buf->b_fnum = top_file_num++; if (top_file_num < 0) /* wrap around (may cause duplicates) */ { EMSG("Warning: List of file names overflow"); sleep(3); /* make sure it is noticed */ top_file_num = 1; } buf->b_winlnum->wl_lnum = lnum; buf->b_winlnum->wl_next = NULL; buf->b_winlnum->wl_prev = NULL; buf->b_winlnum->wl_win = curwin; } if (did_cd) buf->b_xfilename = buf->b_filename; else buf->b_xfilename = buf->b_sfilename; buf->b_u_synced = TRUE; buf->b_neverloaded = TRUE; buf_clear(buf); clrallmarks(buf); /* clear marks */ return buf; } /* * get alternate file n * set linenr to lnum or altlnum if lnum == 0 * if (setpm) setpcmark * return FAIL for failure, OK for success */ int buflist_getfile(n, lnum, setpm) int n; linenr_t lnum; int setpm; { BUF *buf; buf = buflist_findnr(n); if (buf == NULL) { emsg(e_noalt); return FAIL; } if (lnum == 0) lnum = buflist_findlnum(buf); /* altlnum may be changed by getfile() */ RedrawingDisabled = TRUE; if (getfile(buf->b_filename, buf->b_sfilename, setpm, lnum) <= 0) { RedrawingDisabled = FALSE; return OK; } RedrawingDisabled = FALSE; return FAIL; } /* * go to the last know line number for the current buffer */ void buflist_getlnum() { linenr_t lnum; curwin->w_cursor.lnum = 1; curwin->w_cursor.col = 0; lnum = buflist_findlnum(curbuf); if (lnum != 0 && lnum <= curbuf->b_ml.ml_line_count) curwin->w_cursor.lnum = lnum; } /* * find file in buffer list by name (it has to be for the current window) * 'fname' must have a full path. */ static BUF * buflist_findname(fname) char_u *fname; { BUF *buf; for (buf = firstbuf; buf != NULL; buf = buf->b_next) if (buf->b_filename != NULL && fnamecmp(fname, buf->b_filename) == 0) return (buf); return NULL; } /* * find file in buffer name list by number */ static BUF * buflist_findnr(nr) int nr; { BUF *buf; if (nr == 0) nr = curwin->w_alt_fnum; for (buf = firstbuf; buf != NULL; buf = buf->b_next) if (buf->b_fnum == nr) return (buf); return NULL; } /* * get name of file 'n' in the buffer list */ char_u * buflist_nr2name(n) int n; { BUF *buf; char_u *fname; buf = buflist_findnr(n); if (buf == NULL) return NULL; fname = did_cd ? buf->b_filename : buf->b_sfilename; home_replace(fname, NameBuff, MAXPATHL); return NameBuff; } /* * set the lnum for the buffer 'buf' and the current window */ static void buflist_setlnum(buf, lnum) BUF *buf; linenr_t lnum; { WINLNUM *wlp; for (wlp = buf->b_winlnum; wlp != NULL; wlp = wlp->wl_next) if (wlp->wl_win == curwin) break; if (wlp == NULL) /* make new entry */ { wlp = (WINLNUM *)alloc((unsigned)sizeof(WINLNUM)); if (wlp == NULL) return; wlp->wl_win = curwin; } else /* remove entry from list */ { if (wlp->wl_prev) wlp->wl_prev->wl_next = wlp->wl_next; else buf->b_winlnum = wlp->wl_next; if (wlp->wl_next) wlp->wl_next->wl_prev = wlp->wl_prev; } wlp->wl_lnum = lnum; /* * insert entry in front of the list */ wlp->wl_next = buf->b_winlnum; buf->b_winlnum = wlp; wlp->wl_prev = NULL; if (wlp->wl_next) wlp->wl_next->wl_prev = wlp; return; } /* * find the lnum for the buffer 'buf' for the current window */ static linenr_t buflist_findlnum(buf) BUF *buf; { WINLNUM *wlp; for (wlp = buf->b_winlnum; wlp != NULL; wlp = wlp->wl_next) if (wlp->wl_win == curwin) break; if (wlp == NULL) /* if no lnum for curwin, use the first in the list */ wlp = buf->b_winlnum; if (wlp) return wlp->wl_lnum; else return (linenr_t)1; } /* * list all know file names (for :files and :buffers command) */ void buflist_list() { BUF *buf; int len; gotocmdline(TRUE, NUL); for (buf = firstbuf; buf != NULL && !got_int; buf = buf->b_next) { if (buf != firstbuf) msg_outchar('\n'); if (buf->b_xfilename == NULL) STRCPY(NameBuff, "No File"); else /* careful: home_replace calls vimgetenv(), which uses IObuff! */ home_replace(buf->b_xfilename, NameBuff, MAXPATHL); sprintf((char *)IObuff, "%3d %c%c%c \"", buf->b_fnum, buf == curbuf ? '%' : (curwin->w_alt_fnum == buf->b_fnum ? '#' : ' '), buf->b_ml.ml_mfp == NULL ? '-' : (buf->b_nwindows == 0 ? 'h' : ' '), buf->b_changed ? '+' : ' '); len = STRLEN(IObuff); STRNCPY(IObuff + len, NameBuff, (size_t)IOSIZE - 20 - len); len = STRLEN(IObuff); IObuff[len++] = '"'; /* * try to put the "line" strings in column 40 */ do { IObuff[len++] = ' '; } while (len < 40 && len < IOSIZE - 18); sprintf((char *)IObuff + len, "line %ld", buf == curbuf ? curwin->w_cursor.lnum : (long)buflist_findlnum(buf)); msg_outstr(IObuff); flushbuf(); /* output one line at a time */ breakcheck(); } msg_end(); } /* * get file name and line number for file 'fnum' * used by DoOneCmd() for translating '%' and '#' * return FAIL if not found, OK for success */ int buflist_name_nr(fnum, fname, lnum) int fnum; char_u **fname; linenr_t *lnum; { BUF *buf; buf = buflist_findnr(fnum); if (buf == NULL || buf->b_filename == NULL) return FAIL; if (did_cd) *fname = buf->b_filename; else *fname = buf->b_sfilename; *lnum = buflist_findlnum(buf); return OK; } /* * Set the current file name to 's', short file name to 'ss'. * The file name with the full path is also remembered, for when :cd is used. * Returns FAIL for failure (file name already in use by other buffer) * OK otherwise. */ int setfname(fname, sfname, message) char_u *fname, *sfname; int message; { BUF *buf; if (fname == NULL || *fname == NUL) { curbuf->b_filename = NULL; curbuf->b_sfilename = NULL; } else { fname_expand(&fname, &sfname); /* * if the file name is already used in another buffer: * - if the buffer is loaded, fail * - if the buffer is not loaded, delete it from the list */ buf = buflist_findname(fname); if (buf != NULL && buf != curbuf) { if (buf->b_ml.ml_mfp != NULL) /* it's loaded, fail */ { if (message) EMSG("Buffer with this name already exists"); return FAIL; } close_buffer(buf, TRUE, TRUE); /* delete from the list */ } fname = strsave(fname); sfname = strsave(sfname); if (fname == NULL || sfname == NULL) { free(sfname); free(fname); return FAIL; } free(curbuf->b_filename); free(curbuf->b_sfilename); curbuf->b_filename = fname; curbuf->b_sfilename = sfname; } if (did_cd) curbuf->b_xfilename = curbuf->b_filename; else curbuf->b_xfilename = curbuf->b_sfilename; #ifndef MSDOS curbuf->b_shortname = FALSE; #endif return OK; } /* * set alternate file name for current window * * used by dowrite() and doecmd() */ void setaltfname(fname, sfname, lnum) char_u *fname; char_u *sfname; linenr_t lnum; { BUF *buf; buf = buflist_new(fname, sfname, lnum, FALSE); if (buf != NULL) curwin->w_alt_fnum = buf->b_fnum; } /* * add a file name to the buflist and return its number * * used by qf_init(), main() and doarglist() */ int buflist_add(fname) char_u *fname; { BUF *buf; buf = buflist_new(fname, NULL, (linenr_t)0, FALSE); if (buf != NULL) return buf->b_fnum; return 0; } /* * set alternate lnum for current window */ void buflist_altlnum() { buflist_setlnum(curbuf, curwin->w_cursor.lnum); } /* * return nonzero if 'fname' is not the same file as current file * fname must have a full path (expanded by FullName) */ int otherfile(fname) char_u *fname; { /* no name is different */ if (fname == NULL || *fname == NUL || curbuf->b_filename == NULL) return TRUE; return fnamecmp(fname, curbuf->b_filename); } void fileinfo(fullname) int fullname; { char_u *name; #if 0 /* this message is quite useless */ if (bufempty()) { MSG("Buffer Empty"); return; } #endif if (curbuf->b_filename == NULL) STRCPY(IObuff, "\"No File"); else { if (!fullname && curbuf->b_sfilename != NULL) name = curbuf->b_sfilename; else name = curbuf->b_filename; /* careful: home_replace cals vimgetenv(), which also uses IObuff! */ home_replace(name, IObuff + 1, IOSIZE - 1); IObuff[0] = '"'; } sprintf((char *)IObuff + STRLEN(IObuff), "\"%s%s%s line %ld of %ld --%d%%-- col %d", curbuf->b_changed ? " [Modified]" : "", curbuf->b_notedited ? " [Not edited]" : "", curbuf->b_p_ro ? " [readonly]" : "", (long)curwin->w_cursor.lnum, (long)curbuf->b_ml.ml_line_count, (int)(((long)curwin->w_cursor.lnum * 100L) / (long)curbuf->b_ml.ml_line_count), (int)curwin->w_cursor.col + 1); if (arg_count > 1) sprintf((char *)IObuff + STRLEN(IObuff), " (file %d of %d)", curwin->w_arg_idx + 1, arg_count); msg(IObuff); } /* * put filename in title bar of window and in icon title */ static char_u *lasttitle = NULL; static char_u *lasticon = NULL; void maketitle() { char_u *t; char_u *i; if (!p_title && !p_icon) return; if (curbuf->b_filename == NULL) { t = (char_u *)""; i = (char_u *)"No File"; } else { home_replace(curbuf->b_filename, IObuff, IOSIZE); if (arg_count > 1) sprintf((char *)IObuff + STRLEN(IObuff), " (%d of %d)", curwin->w_arg_idx + 1, arg_count); t = IObuff; i = gettail(curbuf->b_filename); /* use filename only for icon */ } free(lasttitle); if (p_title) lasttitle = alloc((unsigned)(STRLEN(t) + 7)); else lasttitle = NULL; if (lasttitle != NULL) sprintf((char *)lasttitle, "VIM - %s", (char *)t); free(lasticon); if (p_icon) lasticon = strsave(i); else lasticon = NULL; resettitle(); } void resettitle() { mch_settitle(lasttitle, lasticon); } /* * If fname is not a full path, make it a full path */ char_u * fix_fname(fname) char_u *fname; { if (fname != NameBuff) /* if not already expanded */ { if (!isFullName(fname)) { (void)FullName(fname, NameBuff, MAXPATHL); fname = NameBuff; } #ifdef AMIGA else { STRNCPY(NameBuff, fname, (size_t)MAXPATHL); /* make copy so we can change it */ fname = NameBuff; fname_case(fname); /* set correct case for filename */ } #endif } return fname; } /* * make fname a full file name, set sfname to fname if not NULL */ void fname_expand(fname, sfname) char_u **fname; char_u **sfname; { if (*fname == NULL) /* if no file name given, nothing to do */ return; if (*sfname == NULL) /* if no short file name given, use fname */ *sfname = *fname; *fname = fix_fname(*fname); /* expand to full path */ } /* * do_arg_all: open a window for each argument */ void do_arg_all() { int win_count; int i; if (arg_count <= 1) { EMSG("Argument list contains less than 2 files"); return; } /* * 1. close all but first window * 2. make the desired number of windows * 3. start editing the first window (hide the current window contents) * 4. stuff commands to fill the other windows */ close_others(FALSE); curwin->w_arg_idx = 0; win_count = make_windows(arg_count); for (i = 0; i < win_count; ++i) { /* edit file i */ (void)doecmd(arg_files[i], NULL, NULL, TRUE, (linenr_t)1); curwin->w_arg_idx = i; if (curwin->w_next == NULL) /* just checking */ break; win_enter(curwin->w_next, FALSE); } win_enter(firstwin, FALSE); /* back to first window */ } /* * do_arg_all: open a window for each buffer * * when 'all' is TRUE, also load inactive buffers */ void do_buffer_all(all) int all; { int win_count; BUF *buf; int i; /* * count number of desired windows */ win_count = 0; for (buf = firstbuf; buf != NULL; buf = buf->b_next) if (all || buf->b_ml.ml_mfp != NULL) ++win_count; if (win_count == 0) /* Cannot happen? */ { EMSG("No relevant entries in buffer list"); return; } /* * 1. close all but first window * 2. make the desired number of windows * 3. stuff commands to fill the windows */ close_others(FALSE); curwin->w_arg_idx = 0; win_count = make_windows(win_count); buf = firstbuf; for (i = 0; i < win_count; ++i) { for ( ; buf != NULL; buf = buf->b_next) if (all || buf->b_ml.ml_mfp != NULL) break; if (buf == NULL) /* Cannot happen? */ break; if (i != 0) stuffReadbuff((char_u *)"\n\027\027:"); /* CTRL-W CTRL-W */ stuffReadbuff((char_u *)":buf "); /* edit Nth buffer */ stuffnumReadbuff((long)buf->b_fnum); buf = buf->b_next; } stuffReadbuff((char_u *)"\n100\027k"); /* back to first window */ }
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.