Example #1
0
/**
   Add the specified filename if it matches the specified wildcard. 

   If the filename matches, first get the description of the specified
   filename. If this is a regular file, append the filesize to the
   description.

   \param list the list to add he completion to
   \param fullname the full filename of the file
   \param completion the completion part of the file name
   \param wc the wildcard to match against
   \param is_cmd whether we are performing command completion
*/
static void wildcard_completion_allocate( std::vector<completion_t> &list, 
					  const wcstring &fullname, 
					  const wcstring &completion,
					  const wchar_t *wc,
                      expand_flags_t expand_flags)
{
	struct stat buf, lbuf;
    wcstring sb;
	wcstring munged_completion;
    
	int flags = 0;
	int stat_res, lstat_res;
	int stat_errno=0;
	
	long long sz; 

	/*
	  If the file is a symlink, we need to stat both the file itself
	  _and_ the destination file. But we try to avoid this with
	  non-symlinks by first doing an lstat, and if the file is not a
	  link we copy the results over to the regular stat buffer.
	*/
	if( ( lstat_res = lwstat( fullname, &lbuf ) ) )
	{
        /* lstat failed! */
		sz=-1;
		stat_res = lstat_res;
	}
	else
	{
		if (S_ISLNK(lbuf.st_mode))
		{
			
			if( ( stat_res = wstat( fullname, &buf ) ) )
			{
				sz=-1;
			}
			else
			{
				sz = (long long)buf.st_size;
			}
			
			/*
			  In order to differentiate between e.g. rotten symlinks
			  and symlink loops, we also need to know the error status of wstat.
			*/
			stat_errno = errno;
		}
		else
		{
			stat_res = lstat_res;
			memcpy( &buf, &lbuf, sizeof( struct stat ) );
			sz = (long long)buf.st_size;
		}
	}
	
    
    bool wants_desc = ! (expand_flags & EXPAND_NO_DESCRIPTIONS);
	wcstring desc;
    if (wants_desc)
        desc = file_get_desc( fullname.c_str(), lstat_res, lbuf, stat_res, buf, stat_errno );
    
	if( sz >= 0 && S_ISDIR(buf.st_mode) )
	{
		flags = flags | COMPLETE_NO_SPACE;
        munged_completion = completion;
        munged_completion.push_back(L'/');
        if (wants_desc)
            sb.append(desc);
	}
	else
	{
        if (wants_desc)
        {
            if (! desc.empty())
            {
                sb.append(desc);
                sb.append(L", ");
            }
            sb.append(format_size(sz));
        }
	}
    
    const wcstring &completion_to_use = munged_completion.empty() ? completion : munged_completion;
	wildcard_complete(completion_to_use, wc, sb.c_str(), NULL, list, flags);
}
Example #2
0
/// Test if the given file is an executable (if EXECUTABLES_ONLY) or directory (if
/// DIRECTORIES_ONLY). If it matches, call wildcard_complete() with some description that we make
/// up. Note that the filename came from a readdir() call, so we know it exists.
static bool wildcard_test_flags_then_complete(const wcstring &filepath, const wcstring &filename,
                                              const wchar_t *wc, expand_flags_t expand_flags,
                                              std::vector<completion_t> *out) {
    // Check if it will match before stat().
    if (!wildcard_complete(filename, wc, {}, NULL, expand_flags, 0)) {
        return false;
    }

    struct stat lstat_buf = {}, stat_buf = {};
    int stat_res = -1;
    int stat_errno = 0;
    int lstat_res = lwstat(filepath, &lstat_buf);
    if (lstat_res >= 0) {
        if (S_ISLNK(lstat_buf.st_mode)) {
            stat_res = wstat(filepath, &stat_buf);

            if (stat_res < 0) {
                // In order to differentiate between e.g. rotten symlinks and symlink loops, we also
                // need to know the error status of wstat.
                stat_errno = errno;
            }
        } else {
            stat_buf = lstat_buf;
            stat_res = lstat_res;
        }
    }

    const long long file_size = stat_res == 0 ? stat_buf.st_size : 0;
    const bool is_directory = stat_res == 0 && S_ISDIR(stat_buf.st_mode);
    const bool is_executable = stat_res == 0 && S_ISREG(stat_buf.st_mode);

    const bool need_directory = expand_flags & DIRECTORIES_ONLY;
    if (need_directory && !is_directory) {
        return false;
    }

    const bool executables_only = expand_flags & EXECUTABLES_ONLY;
    if (executables_only && (!is_executable || waccess(filepath, X_OK) != 0)) {
        return false;
    }

    if (is_windows_subsystem_for_linux() &&
        string_suffixes_string_case_insensitive(L".dll", filename)) {
        return false;
    }

    // Compute the description.
    wcstring desc;
    if (!(expand_flags & EXPAND_NO_DESCRIPTIONS)) {
        desc = file_get_desc(filepath, lstat_res, lstat_buf, stat_res, stat_buf, stat_errno);

        if (file_size >= 0) {
            if (!desc.empty()) desc.append(L", ");
            desc.append(format_size(file_size));
        }
    }

    // Append a / if this is a directory. Note this requirement may be the only reason we have to
    // call stat() in some cases.
    auto desc_func = const_desc(desc);
    if (is_directory) {
        return wildcard_complete(filename + L'/', wc, desc_func, out, expand_flags,
                                 COMPLETE_NO_SPACE);
    }
    return wildcard_complete(filename, wc, desc_func, out, expand_flags, 0);
}