summaryrefslogtreecommitdiffstats
path: root/src/misc2.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/misc2.c')
-rw-r--r--src/misc2.c5556
1 files changed, 5556 insertions, 0 deletions
diff --git a/src/misc2.c b/src/misc2.c
new file mode 100644
index 0000000000..55c44a8651
--- /dev/null
+++ b/src/misc2.c
@@ -0,0 +1,5556 @@
+/* vi:set ts=8 sts=4 sw=4:
+ *
+ * VIM - Vi IMproved by Bram Moolenaar
+ *
+ * Do ":help uganda" in Vim to read copying and usage conditions.
+ * Do ":help credits" in Vim to see a list of people who contributed.
+ * See README.txt for an overview of the Vim source code.
+ */
+
+/*
+ * misc2.c: Various functions.
+ */
+#include "vim.h"
+
+#ifdef HAVE_FCNTL_H
+# include <fcntl.h> /* for chdir() */
+#endif
+
+#if defined(FEAT_VIRTUALEDIT) || defined(PROTO)
+static int coladvance2 __ARGS((pos_T *pos, int addspaces, int finetune, colnr_T wcol));
+
+/*
+ * Return TRUE if in the current mode we need to use virtual.
+ */
+ int
+virtual_active()
+{
+ /* While an operator is being executed we return "virtual_op", because
+ * VIsual_active has already been reset, thus we can't check for "block"
+ * being used. */
+ if (virtual_op != MAYBE)
+ return virtual_op;
+ return (ve_flags == VE_ALL
+# ifdef FEAT_VISUAL
+ || ((ve_flags & VE_BLOCK) && VIsual_active && VIsual_mode == Ctrl_V)
+# endif
+ || ((ve_flags & VE_INSERT) && (State & INSERT)));
+}
+
+/*
+ * Get the screen position of the cursor.
+ */
+ int
+getviscol()
+{
+ colnr_T x;
+
+ getvvcol(curwin, &curwin->w_cursor, &x, NULL, NULL);
+ return (int)x;
+}
+
+/*
+ * Get the screen position of character col with a coladd in the cursor line.
+ */
+ int
+getviscol2(col, coladd)
+ colnr_T col;
+ colnr_T coladd;
+{
+ colnr_T x;
+ pos_T pos;
+
+ pos.lnum = curwin->w_cursor.lnum;
+ pos.col = col;
+ pos.coladd = coladd;
+ getvvcol(curwin, &pos, &x, NULL, NULL);
+ return (int)x;
+}
+
+/*
+ * Go to column "wcol", and add/insert white space as neccessary to get the
+ * cursor in that column.
+ * The caller must have saved the cursor line for undo!
+ */
+ int
+coladvance_force(wcol)
+ colnr_T wcol;
+{
+ int rc = coladvance2(&curwin->w_cursor, TRUE, FALSE, wcol);
+
+ if (wcol == MAXCOL)
+ curwin->w_valid &= ~VALID_VIRTCOL;
+ else
+ {
+ /* Virtcol is valid */
+ curwin->w_valid |= VALID_VIRTCOL;
+ curwin->w_virtcol = wcol;
+ }
+ return rc;
+}
+#endif
+
+/*
+ * Try to advance the Cursor to the specified screen column.
+ * If virtual editing: fine tune the cursor position.
+ * Note that all virtual positions off the end of a line should share
+ * a curwin->w_cursor.col value (n.b. this is equal to STRLEN(line)),
+ * beginning at coladd 0.
+ *
+ * return OK if desired column is reached, FAIL if not
+ */
+ int
+coladvance(wcol)
+ colnr_T wcol;
+{
+ int rc = getvpos(&curwin->w_cursor, wcol);
+
+ if (wcol == MAXCOL || rc == FAIL)
+ curwin->w_valid &= ~VALID_VIRTCOL;
+ else
+ {
+ /* Virtcol is valid */
+ curwin->w_valid |= VALID_VIRTCOL;
+ curwin->w_virtcol = wcol;
+ }
+ return rc;
+}
+
+/*
+ * Return in "pos" the position of the cursor advanced to screen column "wcol".
+ * return OK if desired column is reached, FAIL if not
+ */
+ int
+getvpos(pos, wcol)
+ pos_T *pos;
+ colnr_T wcol;
+{
+#ifdef FEAT_VIRTUALEDIT
+ return coladvance2(pos, FALSE, virtual_active(), wcol);
+}
+
+ static int
+coladvance2(pos, addspaces, finetune, wcol)
+ pos_T *pos;
+ int addspaces; /* change the text to achieve our goal? */
+ int finetune; /* change char offset for the excact column */
+ colnr_T wcol; /* column to move to */
+{
+#endif
+ int idx;
+ char_u *ptr;
+ char_u *line;
+ colnr_T col = 0;
+ int csize = 0;
+ int one_more;
+#ifdef FEAT_LINEBREAK
+ int head = 0;
+#endif
+
+ one_more = (State & INSERT) || restart_edit != NUL
+#ifdef FEAT_VISUAL
+ || (VIsual_active && *p_sel != 'o')
+#endif
+ ;
+ line = ml_get_curline();
+
+ if (wcol >= MAXCOL)
+ {
+ idx = (int)STRLEN(line) - 1 + one_more;
+ col = wcol;
+
+#ifdef FEAT_VIRTUALEDIT
+ if ((addspaces || finetune) && !VIsual_active)
+ {
+ curwin->w_curswant = linetabsize(line) + one_more;
+ if (curwin->w_curswant > 0)
+ --curwin->w_curswant;
+ }
+#endif
+ }
+ else
+ {
+#ifdef FEAT_VIRTUALEDIT
+ int width = W_WIDTH(curwin) - win_col_off(curwin);
+
+ if ((addspaces || finetune)
+ && curwin->w_p_wrap
+# ifdef FEAT_VERTSPLIT
+ && curwin->w_width != 0
+# endif
+ && wcol >= (colnr_T)width)
+ {
+ csize = linetabsize(line);
+ if (csize > 0)
+ csize--;
+
+ if (wcol / width > (colnr_T)csize / width)
+ {
+ /* In case of line wrapping don't move the cursor beyond the
+ * right screen edge. */
+ wcol = (csize / width + 1) * width - 1;
+ }
+ }
+#endif
+
+ idx = -1;
+ ptr = line;
+ while (col <= wcol && *ptr != NUL)
+ {
+ /* Count a tab for what it's worth (if list mode not on) */
+#ifdef FEAT_LINEBREAK
+ csize = win_lbr_chartabsize(curwin, ptr, col, &head);
+# ifdef FEAT_MBYTE
+ if (has_mbyte)
+ ptr += (*mb_ptr2len_check)(ptr);
+ else
+# endif
+ ++ptr;
+#else
+ csize = lbr_chartabsize_adv(&ptr, col);
+#endif
+ col += csize;
+ }
+ idx = (int)(ptr - line);
+ /*
+ * Handle all the special cases. The virtual_active() check
+ * is needed to ensure that a virtual position off the end of
+ * a line has the correct indexing. The one_more comparison
+ * replaces an explicit add of one_more later on.
+ */
+ if (col > wcol || (!virtual_active() && one_more == 0))
+ {
+ idx -= 1;
+# ifdef FEAT_LINEBREAK
+ /* Don't count the chars from 'showbreak'. */
+ csize -= head;
+# endif
+ col -= csize;
+ }
+
+#ifdef FEAT_VIRTUALEDIT
+ if (virtual_active()
+ && addspaces
+ && ((col != wcol && col != wcol + 1) || csize > 1))
+ {
+ /* 'virtualedit' is set: The difference between wcol and col is
+ * filled with spaces. */
+
+ if (line[idx] == NUL)
+ {
+ /* Append spaces */
+ int correct = wcol - col;
+ char_u *newline = alloc(idx + correct + 1);
+ int t;
+
+ if (newline == NULL)
+ return FAIL;
+
+ for (t = 0; t < idx; ++t)
+ newline[t] = line[t];
+
+ for (t = 0; t < correct; ++t)
+ newline[t + idx] = ' ';
+
+ newline[idx + correct] = NUL;
+
+ ml_replace(pos->lnum, newline, FALSE);
+ changed_bytes(pos->lnum, (colnr_T)idx);
+ idx += correct;
+ col = wcol;
+ }
+ else
+ {
+ /* Break a tab */
+ int linelen = (int)STRLEN(line);
+ int correct = wcol - col - csize + 1; /* negative!! */
+ char_u *newline = alloc(linelen + csize);
+ int t, s = 0;
+ int v;
+
+ /*
+ * break a tab
+ */
+ if (newline == NULL || -correct > csize)
+ return FAIL;
+
+ for (t = 0; t < linelen; t++)
+ {
+ if (t != idx)
+ newline[s++] = line[t];
+ else
+ for (v = 0; v < csize; v++)
+ newline[s++] = ' ';
+ }
+
+ newline[linelen + csize - 1] = NUL;
+
+ ml_replace(pos->lnum, newline, FALSE);
+ changed_bytes(pos->lnum, idx);
+ idx += (csize - 1 + correct);
+ col += correct;
+ }
+ }
+#endif
+ }
+
+ if (idx < 0)
+ pos->col = 0;
+ else
+ pos->col = idx;
+
+#ifdef FEAT_VIRTUALEDIT
+ pos->coladd = 0;
+
+ if (finetune)
+ {
+ if (wcol == MAXCOL)
+ {
+ /* The width of the last character is used to set coladd. */
+ if (!one_more)
+ {
+ colnr_T scol, ecol;
+
+ getvcol(curwin, pos, &scol, NULL, &ecol);
+ pos->coladd = ecol - scol;
+ }
+ }
+ else
+ {
+ int b = (int)wcol - (int)col;
+
+ /* The difference between wcol and col is used to set coladd. */
+ if (b > 0 && b < (MAXCOL - 2 * W_WIDTH(curwin)))
+ pos->coladd = b;
+
+ col += b;
+ }
+ }
+#endif
+
+#ifdef FEAT_MBYTE
+ /* prevent cursor from moving on the trail byte */
+ if (has_mbyte)
+ mb_adjust_cursor();
+#endif
+
+ if (col < wcol)
+ return FAIL;
+ return OK;
+}
+
+/*
+ * inc(p)
+ *
+ * Increment the line pointer 'p' crossing line boundaries as necessary.
+ * Return 1 when going to the next line.
+ * Return 2 when moving forward onto a NUL at the end of the line).
+ * Return -1 when at the end of file.
+ * Return 0 otherwise.
+ */
+ int
+inc_cursor()
+{
+ return inc(&curwin->w_cursor);
+}
+
+ int
+inc(lp)
+ pos_T *lp;
+{
+ char_u *p = ml_get_pos(lp);
+
+ if (*p != NUL) /* still within line, move to next char (may be NUL) */
+ {
+#ifdef FEAT_MBYTE
+ if (has_mbyte)
+ {
+ int l = (*mb_ptr2len_check)(p);
+
+ lp->col += l;
+ return ((p[l] != NUL) ? 0 : 2);
+ }
+#endif
+ lp->col++;
+#ifdef FEAT_VIRTUALEDIT
+ lp->coladd = 0;
+#endif
+ return ((p[1] != NUL) ? 0 : 2);
+ }
+ if (lp->lnum != curbuf->b_ml.ml_line_count) /* there is a next line */
+ {
+ lp->col = 0;
+ lp->lnum++;
+#ifdef FEAT_VIRTUALEDIT
+ lp->coladd = 0;
+#endif
+ return 1;
+ }
+ return -1;
+}
+
+/*
+ * incl(lp): same as inc(), but skip the NUL at the end of non-empty lines
+ */
+ int
+incl(lp)
+ pos_T *lp;
+{
+ int r;
+
+ if ((r = inc(lp)) >= 1 && lp->col)
+ r = inc(lp);
+ return r;
+}
+
+/*
+ * dec(p)
+ *
+ * Decrement the line pointer 'p' crossing line boundaries as necessary.
+ * Return 1 when crossing a line, -1 when at start of file, 0 otherwise.
+ */
+ int
+dec_cursor()
+{
+ return dec(&curwin->w_cursor);
+}
+
+ int
+dec(lp)
+ pos_T *lp;
+{
+ char_u *p;
+
+#ifdef FEAT_VIRTUALEDIT
+ lp->coladd = 0;
+#endif
+ if (lp->col > 0) /* still within line */
+ {
+ lp->col--;
+#ifdef FEAT_MBYTE
+ if (has_mbyte)
+ {
+ p = ml_get(lp->lnum);
+ lp->col -= (*mb_head_off)(p, p + lp->col);
+ }
+#endif
+ return 0;
+ }
+ if (lp->lnum > 1) /* there is a prior line */
+ {
+ lp->lnum--;
+ p = ml_get(lp->lnum);
+ lp->col = (colnr_T)STRLEN(p);
+#ifdef FEAT_MBYTE
+ if (has_mbyte)
+ lp->col -= (*mb_head_off)(p, p + lp->col);
+#endif
+ return 1;
+ }
+ return -1; /* at start of file */
+}
+
+/*
+ * decl(lp): same as dec(), but skip the NUL at the end of non-empty lines
+ */
+ int
+decl(lp)
+ pos_T *lp;
+{
+ int r;
+
+ if ((r = dec(lp)) == 1 && lp->col)
+ r = dec(lp);
+ return r;
+}
+
+/*
+ * Make sure curwin->w_cursor.lnum is valid.
+ */
+ void
+check_cursor_lnum()
+{
+ if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count)
+ {
+#ifdef FEAT_FOLDING
+ /* If there is a closed fold at the end of the file, put the cursor in
+ * its first line. Otherwise in the last line. */
+ if (!hasFolding(curbuf->b_ml.ml_line_count,
+ &curwin->w_cursor.lnum, NULL))
+#endif
+ curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
+ }
+ if (curwin->w_cursor.lnum <= 0)
+ curwin->w_cursor.lnum = 1;
+}
+
+/*
+ * Make sure curwin->w_cursor.col is valid.
+ */
+ void
+check_cursor_col()
+{
+ colnr_T len;
+#ifdef FEAT_VIRTUALEDIT
+ colnr_T oldcol = curwin->w_cursor.col + curwin->w_cursor.coladd;
+#endif
+
+ len = (colnr_T)STRLEN(ml_get_curline());
+ if (len == 0)
+ curwin->w_cursor.col = 0;
+ else if (curwin->w_cursor.col >= len)
+ {
+ /* Allow cursor past end-of-line in Insert mode, restarting Insert
+ * mode or when in Visual mode and 'selection' isn't "old" */
+ if (State & INSERT || restart_edit
+#ifdef FEAT_VISUAL
+ || (VIsual_active && *p_sel != 'o')
+#endif
+ || virtual_active())
+ curwin->w_cursor.col = len;
+ else
+ curwin->w_cursor.col = len - 1;
+ }
+
+#ifdef FEAT_VIRTUALEDIT
+ /* If virtual editing is on, we can leave the cursor on the old position,
+ * only we must set it to virtual. But don't do it when at the end of the
+ * line. */
+ if (oldcol == MAXCOL)
+ curwin->w_cursor.coladd = 0;
+ else if (ve_flags == VE_ALL)
+ curwin->w_cursor.coladd = oldcol - curwin->w_cursor.col;
+#endif
+}
+
+/*
+ * make sure curwin->w_cursor in on a valid character
+ */
+ void
+check_cursor()
+{
+ check_cursor_lnum();
+ check_cursor_col();
+}
+
+#if defined(FEAT_TEXTOBJ) || defined(PROTO)
+/*
+ * Make sure curwin->w_cursor is not on the NUL at the end of the line.
+ * Allow it when in Visual mode and 'selection' is not "old".
+ */
+ void
+adjust_cursor_col()
+{
+ if (curwin->w_cursor.col > 0
+# ifdef FEAT_VISUAL
+ && (!VIsual_active || *p_sel == 'o')
+# endif
+ && gchar_cursor() == NUL)
+ --curwin->w_cursor.col;
+}
+#endif
+
+/*
+ * When curwin->w_leftcol has changed, adjust the cursor position.
+ * Return TRUE if the cursor was moved.
+ */
+ int
+leftcol_changed()
+{
+ long lastcol;
+ colnr_T s, e;
+ int retval = FALSE;
+
+ changed_cline_bef_curs();
+ lastcol = curwin->w_leftcol + W_WIDTH(curwin) - curwin_col_off() - 1;
+ validate_virtcol();
+
+ /*
+ * If the cursor is right or left of the screen, move it to last or first
+ * character.
+ */
+ if (curwin->w_virtcol > (colnr_T)(lastcol - p_siso))
+ {
+ retval = TRUE;
+ coladvance((colnr_T)(lastcol - p_siso));
+ }
+ else if (curwin->w_virtcol < curwin->w_leftcol + p_siso)
+ {
+ retval = TRUE;
+ (void)coladvance((colnr_T)(curwin->w_leftcol + p_siso));
+ }
+
+ /*
+ * If the start of the character under the cursor is not on the screen,
+ * advance the cursor one more char. If this fails (last char of the
+ * line) adjust the scrolling.
+ */
+ getvvcol(curwin, &curwin->w_cursor, &s, NULL, &e);
+ if (e > (colnr_T)lastcol)
+ {
+ retval = TRUE;
+ coladvance(s - 1);
+ }
+ else if (s < curwin->w_leftcol)
+ {
+ retval = TRUE;
+ if (coladvance(e + 1) == FAIL) /* there isn't another character */
+ {
+ curwin->w_leftcol = s; /* adjust w_leftcol instead */
+ changed_cline_bef_curs();
+ }
+ }
+
+ if (retval)
+ curwin->w_set_curswant = TRUE;
+ redraw_later(NOT_VALID);
+ return retval;
+}
+
+/**********************************************************************
+ * Various routines dealing with allocation and deallocation of memory.
+ */
+
+#if defined(MEM_PROFILE) || defined(PROTO)
+
+# define MEM_SIZES 8200
+static long_u mem_allocs[MEM_SIZES];
+static long_u mem_frees[MEM_SIZES];
+static long_u mem_allocated;
+static long_u mem_freed;
+static long_u mem_peak;
+static long_u num_alloc;
+static long_u num_freed;
+
+static void mem_pre_alloc_s __ARGS((size_t *sizep));
+static void mem_pre_alloc_l __ARGS((long_u *sizep));
+static void mem_post_alloc __ARGS((void **pp, size_t size));
+static void mem_pre_free __ARGS((void **pp));
+
+ static void
+mem_pre_alloc_s(sizep)
+ size_t *sizep;
+{
+ *sizep += sizeof(size_t);
+}
+
+ static void
+mem_pre_alloc_l(sizep)
+ long_u *sizep;
+{
+ *sizep += sizeof(size_t);
+}
+
+ static void
+mem_post_alloc(pp, size)
+ void **pp;
+ size_t size;
+{
+ if (*pp == NULL)
+ return;
+ size -= sizeof(size_t);
+ *(long_u *)*pp = size;
+ if (size <= MEM_SIZES-1)
+ mem_allocs[size-1]++;
+ else
+ mem_allocs[MEM_SIZES-1]++;
+ mem_allocated += size;
+ if (mem_allocated - mem_freed > mem_peak)
+ mem_peak = mem_allocated - mem_freed;
+ num_alloc++;
+ *pp = (void *)((char *)*pp + sizeof(size_t));
+}
+
+ static void
+mem_pre_free(pp)
+ void **pp;
+{
+ long_u size;
+
+ *pp = (void *)((char *)*pp - sizeof(size_t));
+ size = *(size_t *)*pp;
+ if (size <= MEM_SIZES-1)
+ mem_frees[size-1]++;
+ else
+ mem_frees[MEM_SIZES-1]++;
+ mem_freed += size;
+ num_freed++;
+}
+
+/*
+ * called on exit via atexit()
+ */
+ void
+vim_mem_profile_dump()
+{
+ int i, j;
+
+ printf("\r\n");
+ j = 0;
+ for (i = 0; i < MEM_SIZES - 1; i++)
+ {
+ if (mem_allocs[i] || mem_frees[i])
+ {
+ if (mem_frees[i] > mem_allocs[i])
+ printf("\r\n%s", _("ERROR: "));
+ printf("[%4d / %4lu-%-4lu] ", i + 1, mem_allocs[i], mem_frees[i]);
+ j++;
+ if (j > 3)
+ {
+ j = 0;
+ printf("\r\n");
+ }
+ }
+ }
+
+ i = MEM_SIZES - 1;
+ if (mem_allocs[i])
+ {
+ printf("\r\n");
+ if (mem_frees[i] > mem_allocs[i])
+ printf(_("ERROR: "));
+ printf("[>%d / %4lu-%-4lu]", i, mem_allocs[i], mem_frees[i]);
+ }
+
+ printf(_("\n[bytes] total alloc-freed %lu-%lu, in use %lu, peak use %lu\n"),
+ mem_allocated, mem_freed, mem_allocated - mem_freed, mem_peak);
+ printf(_("[calls] total re/malloc()'s %lu, total free()'s %lu\n\n"),
+ num_alloc, num_freed);
+}
+
+#endif /* MEM_PROFILE */
+
+/*
+ * Some memory is reserved for error messages and for being able to
+ * call mf_release_all(), which needs some memory for mf_trans_add().
+ */
+#if defined(MSDOS) && !defined(DJGPP)
+# define SMALL_MEM
+# define KEEP_ROOM 8192L
+#else
+# define KEEP_ROOM (2 * 8192L)
+#endif
+
+/*
+ * Note: if unsinged is 16 bits we can only allocate up to 64K with alloc().
+ * Use lalloc for larger blocks.
+ */
+ char_u *
+alloc(size)
+ unsigned size;
+{
+ return (lalloc((long_u)size, TRUE));
+}
+
+/*
+ * Allocate memory and set all bytes to zero.
+ */
+ char_u *
+alloc_clear(size)
+ unsigned size;
+{
+ char_u *p;
+
+ p = (lalloc((long_u)size, TRUE));
+ if (p != NULL)
+ (void)vim_memset(p, 0, (size_t)size);
+ return p;
+}
+
+/*
+ * alloc() with check for maximum line length
+ */
+ char_u *
+alloc_check(size)
+ unsigned size;
+{
+#if !defined(UNIX) && !defined(__EMX__)
+ if (sizeof(int) == 2 && size > 0x7fff)
+ {
+ /* Don't hide this message */
+ emsg_silent = 0;
+ EMSG(_("E340: Line is becoming too long"));
+ return NULL;
+ }
+#endif
+ return (lalloc((long_u)size, TRUE));
+}
+
+/*
+ * Allocate memory like lalloc() and set all bytes to zero.
+ */
+ char_u *
+lalloc_clear(size, message)
+ long_u size;
+ int message;
+{
+ char_u *p;
+
+ p = (lalloc(size, message));
+ if (p != NULL)
+ (void)vim_memset(p, 0, (size_t)size);
+ return p;
+}
+
+/*
+ * Low level memory allocation function.
+ * This is used often, KEEP IT FAST!
+ */
+ char_u *
+lalloc(size, message)
+ long_u size;
+ int message;
+{
+ char_u *p; /* pointer to new storage space */
+ static int releasing = FALSE; /* don't do mf_release_all() recursive */
+ int try_again;
+#if defined(HAVE_AVAIL_MEM) && !defined(SMALL_MEM)
+ static long_u allocated = 0; /* allocated since last avail check */
+#endif
+
+ /* Safety check for allocating zero bytes */
+ if (size == 0)
+ {
+ /* Don't hide this message */
+ emsg_silent = 0;
+ EMSGN(_("E341: Internal error: lalloc(%ld, )"), size);
+ return NULL;
+ }
+
+#ifdef MEM_PROFILE
+ mem_pre_alloc_l(&size);
+#endif
+
+#if defined(MSDOS) && !defined(DJGPP)
+ if (size >= 0xfff0) /* in MSDOS we can't deal with >64K blocks */
+ p = NULL;
+ else
+#endif
+
+ /*
+ * Loop when out of memory: Try to release some memfile blocks and
+ * if some blocks are released call malloc again.
+ */
+ for (;;)
+ {
+ /*
+ * Handle three kind of systems:
+ * 1. No check for available memory: Just return.
+ * 2. Slow check for available memory: call mch_avail_mem() after
+ * allocating KEEP_ROOM amount of memory.
+ * 3. Strict check for available memory: call mch_avail_mem()
+ */
+ if ((p = (char_u *)malloc((size_t)size)) != NULL)
+ {
+#ifndef HAVE_AVAIL_MEM
+ /* 1. No check for available memory: Just return. */
+ goto theend;
+#else
+# ifndef SMALL_MEM
+ /* 2. Slow check for available memory: call mch_avail_mem() after
+ * allocating (KEEP_ROOM / 2) amount of memory. */
+ allocated += size;
+ if (allocated < KEEP_ROOM / 2)
+ goto theend;
+ allocated = 0;
+# endif
+ /* 3. check for available memory: call mch_avail_mem() */
+ if (mch_avail_mem(TRUE) < KEEP_ROOM && !releasing)
+ {
+ vim_free((char *)p); /* System is low... no go! */
+ p = NULL;
+ }
+ else
+ goto theend;
+#endif
+ }
+ /*
+ * Remember that mf_release_all() is being called to avoid an endless
+ * loop, because mf_release_all() may call alloc() recursively.
+ */
+ if (releasing)
+ break;
+ releasing = TRUE;
+ try_again = mf_release_all();
+ releasing = FALSE;
+ if (!try_again)
+ break;
+ }
+
+ if (message && p == NULL)
+ do_outofmem_msg(size);
+
+theend:
+#ifdef MEM_PROFILE
+ mem_post_alloc((void **)&p, (size_t)size);
+#endif
+ return p;
+}
+
+#if defined(MEM_PROFILE) || defined(PROTO)
+/*
+ * realloc() with memory profiling.
+ */
+ void *
+mem_realloc(ptr, size)
+ void *ptr;
+ size_t size;
+{
+ void *p;
+
+ mem_pre_free(&ptr);
+ mem_pre_alloc_s(&size);
+
+ p = realloc(ptr, size);
+
+ mem_post_alloc(&p, size);
+
+ return p;
+}
+#endif
+
+/*
+* Avoid repeating the error message many times (they take 1 second each).
+* Did_outofmem_msg is reset when a character is read.
+*/
+ void
+do_outofmem_msg(size)
+ long_u size;
+{
+ if (!did_outofmem_msg)
+ {
+ /* Don't hide this message */
+ emsg_silent = 0;
+ EMSGN(_("E342: Out of memory! (allocating %lu bytes)"), size);
+ did_outofmem_msg = TRUE;
+ }
+}
+
+/*
+ * copy a string into newly allocated memory
+ */
+ char_u *
+vim_strsave(string)
+ char_u *string;
+{
+ char_u *p;
+ unsigned len;
+
+ len = (unsigned)STRLEN(string) + 1;
+ p = alloc(len);
+ if (p != NULL)
+ mch_memmove(p, string, (size_t)len);
+ return p;
+}
+
+ char_u *
+vim_strnsave(string, len)
+ char_u *string;
+ int len;
+{
+ char_u *p;
+
+ p = alloc((unsigned)(len + 1));
+ if (p != NULL)
+ {
+ STRNCPY(p, string, len);
+ p[len] = NUL;
+ }
+ return p;
+}
+
+#if 0 /* not used */
+/*
+ * like vim_strnsave(), but remove backslashes from the string.
+ */
+ char_u *
+vim_strnsave_esc(string, len)
+ char_u *string;
+ int len;
+{
+ char_u *p1, *p2;
+
+ p1 = alloc((unsigned) (len + 1));
+ if (p1 != NULL)
+ {
+ STRNCPY(p1, string, len);
+ p1[len] = NUL;
+ for (p2 = p1; *p2; ++p2)
+ if (*p2 == '\\' && *(p2 + 1) != NUL)
+ STRCPY(p2, p2 + 1);
+ }
+ return p1;
+}
+#endif
+
+/*
+ * Same as vim_strsave(), but any characters found in esc_chars are preceded
+ * by a backslash.
+ */
+ char_u *
+vim_strsave_escaped(string, esc_chars)
+ char_u *string;
+ char_u *esc_chars;
+{
+ return vim_strsave_escaped_ext(string, esc_chars, FALSE);
+}
+
+/*
+ * Same as vim_strsave_escaped(), but when "bsl" is TRUE also escape
+ * characters where rem_backslash() would remove the backslash.
+ */
+ char_u *
+vim_strsave_escaped_ext(string, esc_chars, bsl)
+ char_u *string;
+ char_u *esc_chars;
+ int bsl;
+{
+ char_u *p;
+ char_u *p2;
+ char_u *escaped_string;
+ unsigned length;
+#ifdef FEAT_MBYTE
+ int l;
+#endif
+
+ /*
+ * First count the number of backslashes required.
+ * Then allocate the memory and insert them.
+ */
+ length = 1; /* count the trailing NUL */
+ for (p = string; *p; p++)
+ {
+#ifdef FEAT_MBYTE
+ if (has_mbyte && (l = (*mb_ptr2len_check)(p)) > 1)
+ {
+ length += l; /* count a multibyte char */
+ p += l - 1;
+ continue;
+ }
+#endif
+ if (vim_strchr(esc_chars, *p) != NULL || (bsl && rem_backslash(p)))
+ ++length; /* count a backslash */
+ ++length; /* count an ordinary char */
+ }
+ escaped_string = alloc(length);
+ if (escaped_string != NULL)
+ {
+ p2 = escaped_string;
+ for (p = string; *p; p++)
+ {
+#ifdef FEAT_MBYTE
+ if (has_mbyte && (l = (*mb_ptr2len_check)(p)) > 1)
+ {
+ mch_memmove(p2, p, (size_t)l);
+ p2 += l;
+ p += l - 1; /* skip multibyte char */
+ continue;
+ }
+#endif
+ if (vim_strchr(esc_chars, *p) != NULL || (bsl && rem_backslash(p)))
+ *p2++ = '\\';
+ *p2++ = *p;
+ }
+ *p2 = NUL;
+ }
+ return escaped_string;
+}
+
+/*
+ * Like vim_strsave(), but make all characters uppercase.
+ * This uses ASCII lower-to-upper case translation, language independent.
+ */
+ char_u *
+vim_strsave_up(string)
+ char_u *string;
+{
+ char_u *p1;
+
+ p1 = vim_strsave(string);
+ vim_strup(p1);
+ return p1;
+}
+
+/*
+ * Like vim_strnsave(), but make all characters uppercase.
+ * This uses ASCII lower-to-upper case translation, language independent.
+ */
+ char_u *
+vim_strnsave_up(string, len)
+ char_u *string;
+ int len;
+{
+ char_u *p1;
+
+ p1 = vim_strnsave(string, len);
+ vim_strup(p1);
+ return p1;
+}
+
+/*
+ * ASCII lower-to-upper case translation, language independent.
+ */
+ void
+vim_strup(p)
+ char_u *p;
+{
+ char_u *p2;
+ int c;
+
+ if (p != NULL)
+ {
+ p2 = p;
+ while ((c = *p2) != NUL)
+#ifdef EBCDIC
+ *p2++ = isalpha(c) ? toupper(c) : c;
+#else
+ *p2++ = (c < 'a' || c > 'z') ? c : (c - 0x20);
+#endif
+ }
+}
+
+/*
+ * copy a space a number of times
+ */
+ void
+copy_spaces(ptr, count)
+ char_u *ptr;
+ size_t count;
+{
+ size_t i = count;
+ char_u *p = ptr;
+
+ while (i--)
+ *p++ = ' ';
+}
+
+#if defined(FEAT_VISUALEXTRA) || defined(PROTO)
+/*
+ * Copy a character a number of times.
+ * Does not work for multi-byte charactes!
+ */
+ void
+copy_chars(ptr, count, c)
+ char_u *ptr;
+ size_t count;
+ int c;
+{
+ size_t i = count;
+ char_u *p = ptr;
+
+ while (i--)
+ *p++ = c;
+}
+#endif
+
+/*
+ * delete spaces at the end of a string
+ */
+ void
+del_trailing_spaces(ptr)
+ char_u *ptr;
+{
+ char_u *q;
+
+ q = ptr + STRLEN(ptr);
+ while (--q > ptr && vim_iswhite(q[0]) && q[-1] != '\\' && q[-1] != Ctrl_V)
+ *q = NUL;
+}
+
+/*
+ * This is here because strncpy() does not guarantee successful results when
+ * the to and from strings overlap. It is only currently called from
+ * nextwild() which copies part of the command line to another part of the
+ * command line. This produced garbage when expanding files etc in the middle
+ * of the command line (on my terminal, anyway) -- webb.
+ * Note: strncpy() pads the remainder of the buffer with NUL bytes,
+ * vim_strncpy() doesn't do that.
+ */
+ void
+vim_strncpy(to, from, len)
+ char_u *to;
+ char_u *from;
+ int len;
+{
+ int i;
+
+ if (to <= from)
+ {
+ while (len-- && *from)
+ *to++ = *from++;
+ if (len >= 0)
+ *to = *from; /* Copy NUL */
+ }
+ else
+ {
+ for (i = 0; i < len; i++)
+ {
+ to++;
+ if (*from++ == NUL)
+ {
+ i++;
+ break;
+ }
+ }
+ for (; i > 0; i--)
+ *--to = *--from;
+ }
+}
+
+/*
+ * Isolate one part of a string option where parts are separated with
+ * "sep_chars".
+ * The part is copied into buf[maxlen].
+ * "*option" is advanced to the next part.
+ * The length is returned.
+ */
+ int
+copy_option_part(option, buf, maxlen, sep_chars)
+ char_u **option;
+ char_u *buf;
+ int maxlen;
+ char *sep_chars;
+{
+ int len = 0;
+ char_u *p = *option;
+
+ /* skip '.' at start of option part, for 'suffixes' */
+ if (*p == '.')
+ buf[len++] = *p++;
+ while (*p != NUL && vim_strchr((char_u *)sep_chars, *p) == NULL)
+ {
+ /*
+ * Skip backslash before a separator character and space.
+ */
+ if (p[0] == '\\' && vim_strchr((char_u *)sep_chars, p[1]) != NULL)
+ ++p;
+ if (len < maxlen - 1)
+ buf[len++] = *p;
+ ++p;
+ }
+ buf[len] = NUL;
+
+ if (*p != NUL && *p != ',') /* skip non-standard separator */
+ ++p;
+ p = skip_to_option_part(p); /* p points to next file name */
+
+ *option = p;
+ return len;
+}
+
+/*
+ * replacement for free() that ignores NULL pointers
+ */
+ void
+vim_free(x)
+ void *x;
+{
+ if (x != NULL)
+ {
+#ifdef MEM_PROFILE
+ mem_pre_free(&x);
+#endif
+ free(x);
+ }
+}
+
+#ifndef HAVE_MEMSET
+ void *
+vim_memset(ptr, c, size)
+ void *ptr;
+ int c;
+ size_t size;
+{
+ char *p = ptr;
+
+ while (size-- > 0)
+ *p++ = c;
+ return ptr;
+}
+#endif
+
+#ifdef VIM_MEMCMP
+/*
+ * Return zero when "b1" and "b2" are the same for "len" bytes.
+ * Return non-zero otherwise.
+ */
+ int
+vim_memcmp(b1, b2, len)
+ void *b1;
+ void *b2;
+ size_t len;
+{
+ char_u *p1 = (char_u *)b1, *p2 = (char_u *)b2;
+
+ for ( ; len > 0; --len)
+ {
+ if (*p1 != *p2)
+ return 1;
+ ++p1;
+ ++p2;
+ }
+ return 0;
+}
+#endif
+
+#ifdef VIM_MEMMOVE
+/*
+ * Version of memmove() that handles overlapping source and destination.
+ * For systems that don't have a function that is guaranteed to do that (SYSV).
+ */
+ void
+mch_memmove(dst_arg, src_arg, len)
+ void *src_arg, *dst_arg;
+ size_t len;
+{
+ /*
+ * A void doesn't have a size, we use char pointers.
+ */
+ char *dst = dst_arg, *src = src_arg;
+
+ /* overlap, copy backwards */
+ if (dst > src && dst < src + len)
+ {
+ src += len;
+ dst += len;
+ while (len-- > 0)
+ *--dst = *--src;
+ }
+ else /* copy forwards */
+ while (len-- > 0)
+ *dst++ = *src++;
+}
+#endif
+
+#if (!defined(HAVE_STRCASECMP) && !defined(HAVE_STRICMP)) || defined(PROTO)
+/*
+ * Compare two strings, ignoring case, using current locale.
+ * Doesn't work for multi-byte characters.
+ * return 0 for match, < 0 for smaller, > 0 for bigger
+ */
+ int
+vim_stricmp(s1, s2)
+ char *s1;
+ char *s2;
+{
+ int i;
+
+ for (;;)
+ {
+ i = (int)TOLOWER_LOC(*s1) - (int)TOLOWER_LOC(*s2);
+ if (i != 0)
+ return i; /* this character different */
+ if (*s1 == NUL)
+ break; /* strings match until NUL */
+ ++s1;
+ ++s2;
+ }
+ return 0; /* strings match */
+}
+#endif
+
+#if (!defined(HAVE_STRNCASECMP) && !defined(HAVE_STRNICMP)) || defined(PROTO)
+/*
+ * Compare two strings, for length "len", ignoring case, using current locale.
+ * Doesn't work for multi-byte characters.
+ * return 0 for match, < 0 for smaller, > 0 for bigger
+ */
+ int
+vim_strnicmp(s1, s2, len)
+ char *s1;
+ ch