Esempio n. 1
0
File: channel.c Progetto: mvladk/vim
/*
 * Add a new channel slot, return the index.
 * The channel isn't actually used into ch_fd is set >= 0;
 * Returns -1 if all channels are in use.
 */
    static int
add_channel(void)
{
    int		idx;
    channel_T	*ch;

    if (channels != NULL)
    {
	for (idx = 0; idx < channel_count; ++idx)
	    if (channels[idx].ch_fd < 0)
		/* re-use a closed channel slot */
		return idx;
	if (channel_count == MAX_OPEN_CHANNELS)
	    return -1;
    }
    else
    {
	channels = (channel_T *)alloc((int)sizeof(channel_T)
							 * MAX_OPEN_CHANNELS);
	if (channels == NULL)
	    return -1;
    }

    ch = &channels[channel_count];
    (void)vim_memset(ch, 0, sizeof(channel_T));

    ch->ch_fd = (sock_T)-1;
#ifdef FEAT_GUI_X11
    ch->ch_inputHandler = (XtInputId)NULL;
#endif
#ifdef FEAT_GUI_GTK
    ch->ch_inputHandler = 0;
#endif
#ifdef FEAT_GUI_W32
    ch->ch_inputHandler = -1;
#endif
    /* initialize circular queues */
    ch->ch_head.next = &ch->ch_head;
    ch->ch_head.prev = &ch->ch_head;
    ch->ch_cb_head.next = &ch->ch_cb_head;
    ch->ch_cb_head.prev = &ch->ch_cb_head;
    ch->ch_json_head.next = &ch->ch_json_head;
    ch->ch_json_head.prev = &ch->ch_json_head;

    ch->ch_timeout = 2000;

    return channel_count++;
}
Esempio n. 2
0
/*
 * Add a menu item to a menu
 */
    void
gui_mch_add_menu_item(
    vimmenu_T	*menu,
    int		idx)
{
    vimmenu_T	*parent = menu->parent;

    menu->id = s_menu_id++;
    menu->submenu_id = NULL;

#ifdef FEAT_TOOLBAR
    if (menu_is_toolbar(parent->name))
    {
	TBBUTTON newtb;

	vim_memset(&newtb, 0, sizeof(newtb));
	if (menu_is_separator(menu->name))
	{
	    newtb.iBitmap = 0;
	    newtb.fsStyle = TBSTYLE_SEP;
	}
	else
	{
	    if (menu->iconidx >= TOOLBAR_BITMAP_COUNT)
		newtb.iBitmap = -1;
	    else
		newtb.iBitmap = menu->iconidx;
	    newtb.fsStyle = TBSTYLE_BUTTON;
	}
	newtb.idCommand = menu->id;
	newtb.fsState = TBSTATE_ENABLED;
	SendMessage(s_toolbarhwnd, TB_INSERTBUTTON, (WPARAM)idx,
							     (LPARAM)&newtb);
	menu->submenu_id = (HMENU)-1;
    }
    else
#endif
    {
	InsertMenu(parent->submenu_id, (UINT)idx,
		(menu_is_separator(menu->name) ? MF_SEPARATOR : MF_STRING)
							      | MF_BYPOSITION,
		(UINT)menu->id, menu->name);
    }
}
Esempio n. 3
0
/* Basic Object handling procedures
 */
    GtkType
gtk_form_get_type(void)
{
    static GtkType form_type = 0;

    if (!form_type)
    {
	GtkTypeInfo form_info;

	vim_memset(&form_info, 0, sizeof(form_info));
	form_info.type_name = "GtkForm";
	form_info.object_size = sizeof(GtkForm);
	form_info.class_size = sizeof(GtkFormClass);
	form_info.class_init_func = (GtkClassInitFunc)gtk_form_class_init;
	form_info.object_init_func = (GtkObjectInitFunc)gtk_form_init;

	form_type = gtk_type_unique(GTK_TYPE_CONTAINER, &form_info);
    }
    return form_type;
}
Esempio n. 4
0
int 
buf_init_chartab (
    buf_T *buf,
    int global                     /* FALSE: only set buf->b_chartab[] */
)
{
  int c;
  int c2;
  char_u      *p;
  int i;
  int tilde;
  int do_isalpha;

  if (global) {
    /*
     * Set the default size for printable characters:
     * From <Space> to '~' is 1 (printable), others are 2 (not printable).
     * This also inits all 'isident' and 'isfname' flags to FALSE.
     *
     * EBCDIC: all chars below ' ' are not printable, all others are
     * printable.
     */
    c = 0;
    while (c < ' ')
      chartab[c++] = (dy_flags & DY_UHEX) ? 4 : 2;
    while (c <= '~')
      chartab[c++] = 1 + CT_PRINT_CHAR;
    if (p_altkeymap) {
      while (c < YE)
        chartab[c++] = 1 + CT_PRINT_CHAR;
    }
    while (c < 256) {
      /* UTF-8: bytes 0xa0 - 0xff are printable (latin1) */
      if (enc_utf8 && c >= 0xa0)
        chartab[c++] = CT_PRINT_CHAR + 1;
      /* euc-jp characters starting with 0x8e are single width */
      else if (enc_dbcs == DBCS_JPNU && c == 0x8e)
        chartab[c++] = CT_PRINT_CHAR + 1;
      /* other double-byte chars can be printable AND double-width */
      else if (enc_dbcs != 0 && MB_BYTE2LEN(c) == 2)
        chartab[c++] = CT_PRINT_CHAR + 2;
      else
        /* the rest is unprintable by default */
        chartab[c++] = (dy_flags & DY_UHEX) ? 4 : 2;
    }

    /* Assume that every multi-byte char is a filename character. */
    for (c = 1; c < 256; ++c)
      if ((enc_dbcs != 0 && MB_BYTE2LEN(c) > 1)
          || (enc_dbcs == DBCS_JPNU && c == 0x8e)
          || (enc_utf8 && c >= 0xa0))
        chartab[c] |= CT_FNAME_CHAR;
  }

  /*
   * Init word char flags all to FALSE
   */
  vim_memset(buf->b_chartab, 0, (size_t)32);
  if (enc_dbcs != 0)
    for (c = 0; c < 256; ++c) {
      /* double-byte characters are probably word characters */
      if (MB_BYTE2LEN(c) == 2)
        SET_CHARTAB(buf, c);
    }

  /*
   * In lisp mode the '-' character is included in keywords.
   */
  if (buf->b_p_lisp)
    SET_CHARTAB(buf, '-');

  /* Walk through the 'isident', 'iskeyword', 'isfname' and 'isprint'
   * options Each option is a list of characters, character numbers or
   * ranges, separated by commas, e.g.: "200-210,x,#-178,-"
   */
  for (i = global ? 0 : 3; i <= 3; ++i) {
    if (i == 0)
      p = p_isi;                /* first round: 'isident' */
    else if (i == 1)
      p = p_isp;                /* second round: 'isprint' */
    else if (i == 2)
      p = p_isf;                /* third round: 'isfname' */
    else        /* i == 3 */
      p = buf->b_p_isk;         /* fourth round: 'iskeyword' */

    while (*p) {
      tilde = FALSE;
      do_isalpha = FALSE;
      if (*p == '^' && p[1] != NUL) {
        tilde = TRUE;
        ++p;
      }
      if (VIM_ISDIGIT(*p))
        c = getdigits(&p);
      else if (has_mbyte)
        c = mb_ptr2char_adv(&p);
      else
        c = *p++;
      c2 = -1;
      if (*p == '-' && p[1] != NUL) {
        ++p;
        if (VIM_ISDIGIT(*p))
          c2 = getdigits(&p);
        else if (has_mbyte)
          c2 = mb_ptr2char_adv(&p);
        else
          c2 = *p++;
      }
      if (c <= 0 || c >= 256 || (c2 < c && c2 != -1) || c2 >= 256
          || !(*p == NUL || *p == ','))
        return FAIL;

      if (c2 == -1) {           /* not a range */
        /*
         * A single '@' (not "@-@"):
         * Decide on letters being ID/printable/keyword chars with
         * standard function isalpha(). This takes care of locale for
         * single-byte characters).
         */
        if (c == '@') {
          do_isalpha = TRUE;
          c = 1;
          c2 = 255;
        } else
          c2 = c;
      }
      while (c <= c2) {
        /* Use the MB_ functions here, because isalpha() doesn't
         * work properly when 'encoding' is "latin1" and the locale is
         * "C".  */
        if (!do_isalpha || MB_ISLOWER(c) || MB_ISUPPER(c)
            || (p_altkeymap && (F_isalpha(c) || F_isdigit(c)))
            ) {
          if (i == 0) {                         /* (re)set ID flag */
            if (tilde)
              chartab[c] &= ~CT_ID_CHAR;
            else
              chartab[c] |= CT_ID_CHAR;
          } else if (i == 1)   {                /* (re)set printable */
            if ((c < ' '
                 || c > '~'
                 || (p_altkeymap
                     && (F_isalpha(c) || F_isdigit(c)))
                 )
                /* For double-byte we keep the cell width, so
                 * that we can detect it from the first byte. */
                && !(enc_dbcs && MB_BYTE2LEN(c) == 2)
                ) {
              if (tilde) {
                chartab[c] = (chartab[c] & ~CT_CELL_MASK)
                             + ((dy_flags & DY_UHEX) ? 4 : 2);
                chartab[c] &= ~CT_PRINT_CHAR;
              } else   {
                chartab[c] = (chartab[c] & ~CT_CELL_MASK) + 1;
                chartab[c] |= CT_PRINT_CHAR;
              }
            }
          } else if (i == 2)   {                /* (re)set fname flag */
            if (tilde)
              chartab[c] &= ~CT_FNAME_CHAR;
            else
              chartab[c] |= CT_FNAME_CHAR;
          } else   {     /* i == 3 */		/* (re)set keyword flag */
            if (tilde)
              RESET_CHARTAB(buf, c);
            else
              SET_CHARTAB(buf, c);
          }
        }
        ++c;
      }

      c = *p;
      p = skip_to_option_part(p);
      if (c == ',' && *p == NUL)
        /* Trailing comma is not allowed. */
        return FAIL;
    }
  }
  chartab_initialized = TRUE;
  return OK;
}
Esempio n. 5
0
/*
 * Shrink a hashtable when there is too much empty space.
 * Grow a hashtable when there is not enough empty space.
 * Returns OK or FAIL (out of memory).
 */
    static int
hash_may_resize(
    hashtab_T	*ht,
    int		minitems)		/* minimal number of items */
{
    hashitem_T	temparray[HT_INIT_SIZE];
    hashitem_T	*oldarray, *newarray;
    hashitem_T	*olditem, *newitem;
    unsigned	newi;
    int		todo;
    long_u	oldsize, newsize;
    long_u	minsize;
    long_u	newmask;
    hash_T	perturb;

    /* Don't resize a locked table. */
    if (ht->ht_locked > 0)
	return OK;

#ifdef HT_DEBUG
    if (ht->ht_used > ht->ht_filled)
	EMSG("hash_may_resize(): more used than filled");
    if (ht->ht_filled >= ht->ht_mask + 1)
	EMSG("hash_may_resize(): table completely filled");
#endif

    if (minitems == 0)
    {
	/* Return quickly for small tables with at least two NULL items.  NULL
	 * items are required for the lookup to decide a key isn't there. */
	if (ht->ht_filled < HT_INIT_SIZE - 1
					 && ht->ht_array == ht->ht_smallarray)
	    return OK;

	/*
	 * Grow or refill the array when it's more than 2/3 full (including
	 * removed items, so that they get cleaned up).
	 * Shrink the array when it's less than 1/5 full.  When growing it is
	 * at least 1/4 full (avoids repeated grow-shrink operations)
	 */
	oldsize = ht->ht_mask + 1;
	if (ht->ht_filled * 3 < oldsize * 2 && ht->ht_used > oldsize / 5)
	    return OK;

	if (ht->ht_used > 1000)
	    minsize = ht->ht_used * 2;  /* it's big, don't make too much room */
	else
	    minsize = ht->ht_used * 4;  /* make plenty of room */
    }
    else
    {
	/* Use specified size. */
	if ((long_u)minitems < ht->ht_used)	/* just in case... */
	    minitems = (int)ht->ht_used;
	minsize = minitems * 3 / 2;	/* array is up to 2/3 full */
    }

    newsize = HT_INIT_SIZE;
    while (newsize < minsize)
    {
	newsize <<= 1;		/* make sure it's always a power of 2 */
	if (newsize == 0)
	    return FAIL;	/* overflow */
    }

    if (newsize == HT_INIT_SIZE)
    {
	/* Use the small array inside the hashdict structure. */
	newarray = ht->ht_smallarray;
	if (ht->ht_array == newarray)
	{
	    /* Moving from ht_smallarray to ht_smallarray!  Happens when there
	     * are many removed items.  Copy the items to be able to clean up
	     * removed items. */
	    mch_memmove(temparray, newarray, sizeof(temparray));
	    oldarray = temparray;
	}
	else
	    oldarray = ht->ht_array;
    }
    else
    {
	/* Allocate an array. */
	newarray = (hashitem_T *)alloc((unsigned)
					      (sizeof(hashitem_T) * newsize));
	if (newarray == NULL)
	{
	    /* Out of memory.  When there are NULL items still return OK.
	     * Otherwise set ht_error, because lookup may result in a hang if
	     * we add another item. */
	    if (ht->ht_filled < ht->ht_mask)
		return OK;
	    ht->ht_error = TRUE;
	    return FAIL;
	}
	oldarray = ht->ht_array;
    }
    vim_memset(newarray, 0, (size_t)(sizeof(hashitem_T) * newsize));

    /*
     * Move all the items from the old array to the new one, placing them in
     * the right spot.  The new array won't have any removed items, thus this
     * is also a cleanup action.
     */
    newmask = newsize - 1;
    todo = (int)ht->ht_used;
    for (olditem = oldarray; todo > 0; ++olditem)
	if (!HASHITEM_EMPTY(olditem))
	{
	    /*
	     * The algorithm to find the spot to add the item is identical to
	     * the algorithm to find an item in hash_lookup().  But we only
	     * need to search for a NULL key, thus it's simpler.
	     */
	    newi = (unsigned)(olditem->hi_hash & newmask);
	    newitem = &newarray[newi];

	    if (newitem->hi_key != NULL)
		for (perturb = olditem->hi_hash; ; perturb >>= PERTURB_SHIFT)
		{
		    newi = (unsigned)((newi << 2U) + newi + perturb + 1U);
		    newitem = &newarray[newi & newmask];
		    if (newitem->hi_key == NULL)
			break;
		}
	    *newitem = *olditem;
	    --todo;
	}
Esempio n. 6
0
/*
 * get a new block
 *
 *   negative: TRUE if negative block number desired (data block)
 */
    bhdr_T *
mf_new(memfile_T *mfp, int negative, int page_count)
{
    bhdr_T	*hp;	/* new bhdr_T */
    bhdr_T	*freep;	/* first block in free list */
    char_u	*p;

    /*
     * If we reached the maximum size for the used memory blocks, release one
     * If a bhdr_T is returned, use it and adjust the page_count if necessary.
     */
    hp = mf_release(mfp, page_count);

/*
 * Decide on the number to use:
 * If there is a free block, use its number.
 * Otherwise use mf_block_min for a negative number, mf_block_max for
 * a positive number.
 */
    freep = mfp->mf_free_first;
    if (!negative && freep != NULL && freep->bh_page_count >= page_count)
    {
	/*
	 * If the block in the free list has more pages, take only the number
	 * of pages needed and allocate a new bhdr_T with data
	 *
	 * If the number of pages matches and mf_release() did not return a
	 * bhdr_T, use the bhdr_T from the free list and allocate the data
	 *
	 * If the number of pages matches and mf_release() returned a bhdr_T,
	 * just use the number and free the bhdr_T from the free list
	 */
	if (freep->bh_page_count > page_count)
	{
	    if (hp == NULL && (hp = mf_alloc_bhdr(mfp, page_count)) == NULL)
		return NULL;
	    hp->bh_bnum = freep->bh_bnum;
	    freep->bh_bnum += page_count;
	    freep->bh_page_count -= page_count;
	}
	else if (hp == NULL)	    /* need to allocate memory for this block */
	{
	    if ((p = (char_u *)alloc(mfp->mf_page_size * page_count)) == NULL)
		return NULL;
	    hp = mf_rem_free(mfp);
	    hp->bh_data = p;
	}
	else		    /* use the number, remove entry from free list */
	{
	    freep = mf_rem_free(mfp);
	    hp->bh_bnum = freep->bh_bnum;
	    vim_free(freep);
	}
    }
    else	/* get a new number */
    {
	if (hp == NULL && (hp = mf_alloc_bhdr(mfp, page_count)) == NULL)
	    return NULL;
	if (negative)
	{
	    hp->bh_bnum = mfp->mf_blocknr_min--;
	    mfp->mf_neg_count++;
	}
	else
	{
	    hp->bh_bnum = mfp->mf_blocknr_max;
	    mfp->mf_blocknr_max += page_count;
	}
    }
    hp->bh_flags = BH_LOCKED | BH_DIRTY;	/* new block is always dirty */
    mfp->mf_dirty = TRUE;
    hp->bh_page_count = page_count;
    mf_ins_used(mfp, hp);
    mf_ins_hash(mfp, hp);

    /*
     * Init the data to all zero, to avoid reading uninitialized data.
     * This also avoids that the passwd file ends up in the swap file!
     */
    (void)vim_memset((char *)(hp->bh_data), 0,
				      (size_t)mfp->mf_page_size * page_count);

    return hp;
}
Esempio n. 7
0
/*
 * Initialization routine for vim_findfile().
 *
 * Returns the newly allocated search context or NULL if an error occurred.
 *
 * Don't forget to clean up by calling vim_findfile_cleanup() if you are done
 * with the search context.
 *
 * Find the file 'filename' in the directory 'path'.
 * The parameter 'path' may contain wildcards. If so only search 'level'
 * directories deep. The parameter 'level' is the absolute maximum and is
 * not related to restricts given to the '**' wildcard. If 'level' is 100
 * and you use '**200' vim_findfile() will stop after 100 levels.
 *
 * 'filename' cannot contain wildcards!  It is used as-is, no backslashes to
 * escape special characters.
 *
 * If 'stopdirs' is not NULL and nothing is found downward, the search is
 * restarted on the next higher directory level. This is repeated until the
 * start-directory of a search is contained in 'stopdirs'. 'stopdirs' has the
 * format ";*<dirname>*\(;<dirname>\)*;\=$".
 *
 * If the 'path' is relative, the starting dir for the search is either VIM's
 * current dir or if the path starts with "./" the current files dir.
 * If the 'path' is absolute, the starting dir is that part of the path before
 * the first wildcard.
 *
 * Upward search is only done on the starting dir.
 *
 * If 'free_visited' is TRUE the list of already visited files/directories is
 * cleared. Set this to FALSE if you just want to search from another
 * directory, but want to be sure that no directory from a previous search is
 * searched again. This is useful if you search for a file at different places.
 * The list of visited files/dirs can also be cleared with the function
 * vim_findfile_free_visited().
 *
 * Set the parameter 'find_what' to FINDFILE_DIR if you want to search for
 * directories only, FINDFILE_FILE for files only, FINDFILE_BOTH for both.
 *
 * A search context returned by a previous call to vim_findfile_init() can be
 * passed in the parameter "search_ctx_arg".  This context is reused and
 * reinitialized with the new parameters.  The list of already visited
 * directories from this context is only deleted if the parameter
 * "free_visited" is true.  Be aware that the passed "search_ctx_arg" is freed
 * if the reinitialization fails.
 *
 * If you don't have a search context from a previous call "search_ctx_arg"
 * must be NULL.
 *
 * This function silently ignores a few errors, vim_findfile() will have
 * limited functionality then.
 */
void *
vim_findfile_init (
    char_u *path,
    char_u *filename,
    char_u *stopdirs,
    int level,
    int free_visited,
    int find_what,
    void *search_ctx_arg,
    int tagfile,                    /* expanding names of tags files */
    char_u *rel_fname         /* file name to use for "." */
)
{
  char_u              *wc_part;
  ff_stack_T          *sptr;
  ff_search_ctx_T     *search_ctx;

  /* If a search context is given by the caller, reuse it, else allocate a
   * new one.
   */
  if (search_ctx_arg != NULL)
    search_ctx = search_ctx_arg;
  else {
    search_ctx = (ff_search_ctx_T*)alloc((unsigned)sizeof(ff_search_ctx_T));
    if (search_ctx == NULL)
      goto error_return;
    vim_memset(search_ctx, 0, sizeof(ff_search_ctx_T));
  }
  search_ctx->ffsc_find_what = find_what;
  search_ctx->ffsc_tagfile = tagfile;

  /* clear the search context, but NOT the visited lists */
  ff_clear(search_ctx);

  /* clear visited list if wanted */
  if (free_visited == TRUE)
    vim_findfile_free_visited(search_ctx);
  else {
    /* Reuse old visited lists. Get the visited list for the given
     * filename. If no list for the current filename exists, creates a new
     * one. */
    search_ctx->ffsc_visited_list = ff_get_visited_list(filename,
        &search_ctx->ffsc_visited_lists_list);
    if (search_ctx->ffsc_visited_list == NULL)
      goto error_return;
    search_ctx->ffsc_dir_visited_list = ff_get_visited_list(filename,
        &search_ctx->ffsc_dir_visited_lists_list);
    if (search_ctx->ffsc_dir_visited_list == NULL)
      goto error_return;
  }

  if (ff_expand_buffer == NULL) {
    ff_expand_buffer = (char_u*)alloc(MAXPATHL);
    if (ff_expand_buffer == NULL)
      goto error_return;
  }

  /* Store information on starting dir now if path is relative.
   * If path is absolute, we do that later.  */
  if (path[0] == '.'
      && (vim_ispathsep(path[1]) || path[1] == NUL)
      && (!tagfile || vim_strchr(p_cpo, CPO_DOTTAG) == NULL)
      && rel_fname != NULL) {
    int len = (int)(gettail(rel_fname) - rel_fname);

    if (!vim_isAbsName(rel_fname) && len + 1 < MAXPATHL) {
      /* Make the start dir an absolute path name. */
      vim_strncpy(ff_expand_buffer, rel_fname, len);
      search_ctx->ffsc_start_dir = FullName_save(ff_expand_buffer, FALSE);
    } else
      search_ctx->ffsc_start_dir = vim_strnsave(rel_fname, len);
    if (search_ctx->ffsc_start_dir == NULL)
      goto error_return;
    if (*++path != NUL)
      ++path;
  } else if (*path == NUL || !vim_isAbsName(path))   {
#ifdef BACKSLASH_IN_FILENAME
    /* "c:dir" needs "c:" to be expanded, otherwise use current dir */
    if (*path != NUL && path[1] == ':') {
      char_u drive[3];

      drive[0] = path[0];
      drive[1] = ':';
      drive[2] = NUL;
      if (vim_FullName(drive, ff_expand_buffer, MAXPATHL, TRUE) == FAIL)
        goto error_return;
      path += 2;
    } else
#endif
    if (mch_dirname(ff_expand_buffer, MAXPATHL) == FAIL)
      goto error_return;

    search_ctx->ffsc_start_dir = vim_strsave(ff_expand_buffer);
    if (search_ctx->ffsc_start_dir == NULL)
      goto error_return;

#ifdef BACKSLASH_IN_FILENAME
    /* A path that starts with "/dir" is relative to the drive, not to the
     * directory (but not for "//machine/dir").  Only use the drive name. */
    if ((*path == '/' || *path == '\\')
        && path[1] != path[0]
        && search_ctx->ffsc_start_dir[1] == ':')
      search_ctx->ffsc_start_dir[2] = NUL;
#endif
  }

  /*
   * If stopdirs are given, split them into an array of pointers.
   * If this fails (mem allocation), there is no upward search at all or a
   * stop directory is not recognized -> continue silently.
   * If stopdirs just contains a ";" or is empty,
   * search_ctx->ffsc_stopdirs_v will only contain a  NULL pointer. This
   * is handled as unlimited upward search.  See function
   * ff_path_in_stoplist() for details.
   */
  if (stopdirs != NULL) {
    char_u  *walker = stopdirs;
    int dircount;

    while (*walker == ';')
      walker++;

    dircount = 1;
    search_ctx->ffsc_stopdirs_v =
      (char_u **)alloc((unsigned)sizeof(char_u *));

    if (search_ctx->ffsc_stopdirs_v != NULL) {
      do {
        char_u  *helper;
        void    *ptr;

        helper = walker;
        ptr = vim_realloc(search_ctx->ffsc_stopdirs_v,
            (dircount + 1) * sizeof(char_u *));
        if (ptr)
          search_ctx->ffsc_stopdirs_v = ptr;
        else
          /* ignore, keep what we have and continue */
          break;
        walker = vim_strchr(walker, ';');
        if (walker) {
          search_ctx->ffsc_stopdirs_v[dircount-1] =
            vim_strnsave(helper, (int)(walker - helper));
          walker++;
        } else
          /* this might be "", which means ascent till top
           * of directory tree.
           */
          search_ctx->ffsc_stopdirs_v[dircount-1] =
            vim_strsave(helper);

        dircount++;

      } while (walker != NULL);
      search_ctx->ffsc_stopdirs_v[dircount-1] = NULL;
    }
  }

  search_ctx->ffsc_level = level;

  /* split into:
   *  -fix path
   *  -wildcard_stuff (might be NULL)
   */
  wc_part = vim_strchr(path, '*');
  if (wc_part != NULL) {
    int llevel;
    int len;
    char    *errpt;

    /* save the fix part of the path */
    search_ctx->ffsc_fix_path = vim_strnsave(path, (int)(wc_part - path));

    /*
     * copy wc_path and add restricts to the '**' wildcard.
     * The octet after a '**' is used as a (binary) counter.
     * So '**3' is transposed to '**^C' ('^C' is ASCII value 3)
     * or '**76' is transposed to '**N'( 'N' is ASCII value 76).
     * For EBCDIC you get different character values.
     * If no restrict is given after '**' the default is used.
     * Due to this technique the path looks awful if you print it as a
     * string.
     */
    len = 0;
    while (*wc_part != NUL) {
      if (len + 5 >= MAXPATHL) {
        EMSG(_(e_pathtoolong));
        break;
      }
      if (STRNCMP(wc_part, "**", 2) == 0) {
        ff_expand_buffer[len++] = *wc_part++;
        ff_expand_buffer[len++] = *wc_part++;

        llevel = strtol((char *)wc_part, &errpt, 10);
        if ((char_u *)errpt != wc_part && llevel > 0 && llevel < 255)
          ff_expand_buffer[len++] = llevel;
        else if ((char_u *)errpt != wc_part && llevel == 0)
          /* restrict is 0 -> remove already added '**' */
          len -= 2;
        else
          ff_expand_buffer[len++] = FF_MAX_STAR_STAR_EXPAND;
        wc_part = (char_u *)errpt;
        if (*wc_part != NUL && !vim_ispathsep(*wc_part)) {
          EMSG2(_(
                  "E343: Invalid path: '**[number]' must be at the end of the path or be followed by '%s'."),
              PATHSEPSTR);
          goto error_return;
        }
      } else
        ff_expand_buffer[len++] = *wc_part++;
    }
    ff_expand_buffer[len] = NUL;
    search_ctx->ffsc_wc_path = vim_strsave(ff_expand_buffer);

    if (search_ctx->ffsc_wc_path == NULL)
      goto error_return;
  } else
    search_ctx->ffsc_fix_path = vim_strsave(path);

  if (search_ctx->ffsc_start_dir == NULL) {
    /* store the fix part as startdir.
     * This is needed if the parameter path is fully qualified.
     */
    search_ctx->ffsc_start_dir = vim_strsave(search_ctx->ffsc_fix_path);
    if (search_ctx->ffsc_start_dir == NULL)
      goto error_return;
    search_ctx->ffsc_fix_path[0] = NUL;
  }

  /* create an absolute path */
  if (STRLEN(search_ctx->ffsc_start_dir)
      + STRLEN(search_ctx->ffsc_fix_path) + 3 >= MAXPATHL) {
    EMSG(_(e_pathtoolong));
    goto error_return;
  }
  STRCPY(ff_expand_buffer, search_ctx->ffsc_start_dir);
  add_pathsep(ff_expand_buffer);
  {
    int eb_len = (int)STRLEN(ff_expand_buffer);
    char_u *buf = alloc(eb_len
        + (int)STRLEN(search_ctx->ffsc_fix_path) + 1);

    STRCPY(buf, ff_expand_buffer);
    STRCPY(buf + eb_len, search_ctx->ffsc_fix_path);
    if (mch_isdir(buf)) {
      STRCAT(ff_expand_buffer, search_ctx->ffsc_fix_path);
      add_pathsep(ff_expand_buffer);
    } else   {
      char_u *p =  gettail(search_ctx->ffsc_fix_path);
      char_u *wc_path = NULL;
      char_u *temp = NULL;
      int len = 0;

      if (p > search_ctx->ffsc_fix_path) {
        len = (int)(p - search_ctx->ffsc_fix_path) - 1;
        STRNCAT(ff_expand_buffer, search_ctx->ffsc_fix_path, len);
        add_pathsep(ff_expand_buffer);
      } else
        len = (int)STRLEN(search_ctx->ffsc_fix_path);

      if (search_ctx->ffsc_wc_path != NULL) {
        wc_path = vim_strsave(search_ctx->ffsc_wc_path);
        temp = alloc((int)(STRLEN(search_ctx->ffsc_wc_path)
                           + STRLEN(search_ctx->ffsc_fix_path + len)
                           + 1));
      }

      if (temp == NULL || wc_path == NULL) {
        vim_free(buf);
        vim_free(temp);
        vim_free(wc_path);
        goto error_return;
      }

      STRCPY(temp, search_ctx->ffsc_fix_path + len);
      STRCAT(temp, search_ctx->ffsc_wc_path);
      vim_free(search_ctx->ffsc_wc_path);
      vim_free(wc_path);
      search_ctx->ffsc_wc_path = temp;
    }
    vim_free(buf);
  }

  sptr = ff_create_stack_element(ff_expand_buffer,
      search_ctx->ffsc_wc_path,
      level, 0);

  if (sptr == NULL)
    goto error_return;

  ff_push(search_ctx, sptr);

  search_ctx->ffsc_file_to_search = vim_strsave(filename);
  if (search_ctx->ffsc_file_to_search == NULL)
    goto error_return;

  return search_ctx;

error_return:
  /*
   * We clear the search context now!
   * Even when the caller gave us a (perhaps valid) context we free it here,
   * as we might have already destroyed it.
   */
  vim_findfile_cleanup(search_ctx);
  return NULL;
}
Esempio n. 8
0
/// Shrink a hashtable when there is too much empty space.
/// Grow a hashtable when there is not enough empty space.
///
/// @param ht
/// @param minitems minimal number of items
///
/// @returns OK or FAIL (out of memory).
static int hash_may_resize(hashtab_T *ht, int minitems)
{
  hashitem_T temparray[HT_INIT_SIZE];
  hashitem_T *oldarray, *newarray;
  hashitem_T *olditem, *newitem;
  unsigned newi;
  int todo;
  long_u oldsize, newsize;
  long_u minsize;
  long_u newmask;
  hash_T perturb;

  // Don't resize a locked table.
  if (ht->ht_locked > 0) {
    return OK;
  }

#ifdef HT_DEBUG
  if (ht->ht_used > ht->ht_filled) {
    EMSG("hash_may_resize(): more used than filled");
  }

  if (ht->ht_filled >= ht->ht_mask + 1) {
    EMSG("hash_may_resize(): table completely filled");
  }
#endif  // ifdef HT_DEBUG

  if (minitems == 0) {
    // Return quickly for small tables with at least two NULL items.  NULL
    // items are required for the lookup to decide a key isn't there.
    if ((ht->ht_filled < HT_INIT_SIZE - 1)
        && (ht->ht_array == ht->ht_smallarray)) {
      return OK;
    }

    // Grow or refill the array when it's more than 2/3 full (including
    // removed items, so that they get cleaned up).
    // Shrink the array when it's less than 1/5 full.  When growing it is
    // at least 1/4 full (avoids repeated grow-shrink operations)
    oldsize = ht->ht_mask + 1;
    if ((ht->ht_filled * 3 < oldsize * 2) && (ht->ht_used > oldsize / 5)) {
      return OK;
    }

    if (ht->ht_used > 1000) {
      // it's big, don't make too much room
      minsize = ht->ht_used * 2;
    } else {
      // make plenty of room
      minsize = ht->ht_used * 4;
    }
  } else {
    // Use specified size.
    if ((long_u)minitems < ht->ht_used) {
      // just in case...
      minitems = (int)ht->ht_used;
    }
    // array is up to 2/3 full
    minsize = minitems * 3 / 2;
  }

  newsize = HT_INIT_SIZE;

  while (newsize < minsize) {
    // make sure it's always a power of 2
    newsize <<= 1;
    if (newsize == 0) {
      // overflow
      return FAIL;
    }
  }

  if (newsize == HT_INIT_SIZE) {
    // Use the small array inside the hashdict structure.
    newarray = ht->ht_smallarray;
    if (ht->ht_array == newarray) {
      // Moving from ht_smallarray to ht_smallarray!  Happens when there
      // are many removed items.  Copy the items to be able to clean up
      // removed items.
      mch_memmove(temparray, newarray, sizeof(temparray));
      oldarray = temparray;
    } else {
      oldarray = ht->ht_array;
    }
  } else {
    // Allocate an array.
    newarray = (hashitem_T *)alloc((unsigned)(sizeof(hashitem_T) * newsize));

    if (newarray == NULL) {
      // Out of memory.  When there are NULL items still return OK.
      // Otherwise set ht_error, because lookup may result in a hang if
      // we add another item.
      if (ht->ht_filled < ht->ht_mask) {
        return OK;
      }
      ht->ht_error = TRUE;
      return FAIL;
    }
    oldarray = ht->ht_array;
  }
  vim_memset(newarray, 0, (size_t)(sizeof(hashitem_T) * newsize));

  // Move all the items from the old array to the new one, placing them in
  // the right spot.  The new array won't have any removed items, thus this
  // is also a cleanup action.
  newmask = newsize - 1;
  todo = (int)ht->ht_used;

  for (olditem = oldarray; todo > 0; ++olditem) {
    if (!HASHITEM_EMPTY(olditem)) {
      // The algorithm to find the spot to add the item is identical to
      // the algorithm to find an item in hash_lookup().  But we only
      // need to search for a NULL key, thus it's simpler.
      newi = (unsigned)(olditem->hi_hash & newmask);
      newitem = &newarray[newi];
      if (newitem->hi_key != NULL) {
        for (perturb = olditem->hi_hash;; perturb >>= PERTURB_SHIFT) {
          newi = (unsigned)((newi << 2U) + newi + perturb + 1U);
          newitem = &newarray[newi & newmask];
          if (newitem->hi_key == NULL) {
            break;
          }
        }
      }
      *newitem = *olditem;
      todo--;
    }
  }