/* * 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++; }
/* * 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); } }
/* 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; }
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; }
/* * 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; }
/* * 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; }
/* * 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; }
/// 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--; } }