Ejemplo n.º 1
0
/* Prints line onto a window highlighting it according to attrs, which should
 * specify 0-9 color groups for every character in line. */
static void
print_with_attrs(WINDOW *win, const char line[], const char attrs[],
		const cchar_t *default_attr)
{
	cchar_t attr = *default_attr;
	while(*line != '\0')
	{
		if(*attrs == '0')
		{
			attr = *default_attr;
		}
		else if(*attrs != ' ')
		{
			const int color = (USER1_COLOR + (*attrs - '1'));
			col_attr_t col = cfg.cs.color[STATUS_LINE_COLOR];
			cs_mix_colors(&col, &cfg.cs.color[color]);
			setcchar(&attr, L" ", col.attr, colmgr_get_pair(col.fg, col.bg), NULL);
		}

		const size_t len = utf8_chrw(line);
		char char_buf[len + 1];
		copy_str(char_buf, sizeof(char_buf), line);
		wprinta(win, char_buf, &attr, 0);

		line += len;
		attrs += utf8_chrsw(char_buf);
	}
}
Ejemplo n.º 2
0
/* Adds ellipsis to the string in buf not changing enlarging its length (at most
 * three first or last characters are replaced). */
static void
add_ellipsis(AlignType align, char buf[])
{
	const size_t len = get_width_on_screen(buf);
	const size_t dot_count = MIN(len, MAX_ELLIPSIS_DOT_COUNT);
	if(align == AT_LEFT)
	{
		const size_t width_limit = len - dot_count;
		const size_t pos = utf8_strsnlen(buf, width_limit);
		memset(buf + pos, '.', dot_count);
		buf[pos + dot_count] = '\0';
	}
	else
	{
		const char *new_beginning = buf;
		size_t skipped = 0;
		while(skipped < dot_count)
		{
			skipped += utf8_chrsw(new_beginning);
			new_beginning += utf8_chrw(new_beginning);
		}

		memmove(buf + dot_count, new_beginning, strlen(new_beginning) + 1);
		memset(buf, '.', dot_count);
	}
}
Ejemplo n.º 3
0
/* Returns number of characters at the beginning of the str which form one
 * logical symbol.  Takes UTF-8 encoding and terminal escape sequences into
 * account. */
static size_t
get_char_width_esc(const char str[])
{
	return (*str == '\033')
	     ? (size_t)(after_first(str, 'm') - str)
	     : utf8_chrw(str);
}
Ejemplo n.º 4
0
/* Corrects offset inside the line so that it points to the char after previous
 * character instead of the beginning of the current one. */
static size_t
correct_offset(const char line[], const int offsets[], size_t offset)
{
    assert(offset != 0U && "Offset has to be greater than zero.");
    const int prev_offset = offsets[offset - 1];
    const size_t char_width = utf8_chrw(line + prev_offset);
    return prev_offset + char_width;
}
Ejemplo n.º 5
0
/* Adds decorations like ellipsis to the output.  Returns actual align type used
 * for the column (might not match col->info.align). */
static AlignType
decorate_output(const column_t *col, char buf[], size_t max_line_width)
{
	const size_t len = get_width_on_screen(buf);
	const size_t max_col_width = calculate_max_width(col, len, max_line_width);
	const int too_long = len > max_col_width;
	AlignType result;

	if(!too_long)
	{
		return (col->info.align == AT_RIGHT ? AT_RIGHT : AT_LEFT);
	}

	if(col->info.align == AT_LEFT ||
			(col->info.align == AT_DYN && len <= max_col_width))
	{
		const size_t truncate_pos = utf8_strsnlen(buf, max_col_width);
		buf[truncate_pos] = '\0';
		result = AT_LEFT;
	}
	else
	{
		int extra_spaces;

		const size_t truncate_pos = utf8_strsnlen(buf, len - max_col_width);
		const char *new_beginning = buf + truncate_pos;

		extra_spaces = 0;
		while(get_width_on_screen(new_beginning) > max_col_width)
		{
			++extra_spaces;
			new_beginning += utf8_chrw(new_beginning);
		}

		memmove(buf + extra_spaces, new_beginning, strlen(new_beginning) + 1);
		if(extra_spaces != 0)
		{
			memset(buf, ' ', extra_spaces);
		}

		assert(get_width_on_screen(buf) == max_col_width && "Column isn't filled.");
		result = AT_RIGHT;
	}

	if(col->info.cropping == CT_ELLIPSIS)
	{
		add_ellipsis(result, buf);
	}

	return result;
}
Ejemplo n.º 6
0
/* Returns number of characters at the beginning of the str which form one
 * logical symbol.  Takes UTF-8 encoding and terminal escape sequences into
 * account. */
static size_t
get_char_width_esc(const char str[])
{
    if(*str != '\033')
    {
        return utf8_chrw(str);
    }
    else
    {
        const char *pos = strchr(str, 'm');
        pos = (pos == NULL) ? (str + strlen(str)) : (pos + 1);
        return pos - str;
    }
}
Ejemplo n.º 7
0
/* Converts the leading character of the str string to a printable string.  Puts
 * number of screen character positions taken by the resulting string
 * representation of a character into *screen_width.  Returns pointer to a
 * statically allocated buffer. */
TSTATIC const char *
strchar2str(const char str[], int pos, size_t *screen_width)
{
    static char buf[32];

    const size_t char_width = utf8_chrw(str);
    if(char_width != 1 || (unsigned char)str[0] >= (unsigned char)' ')
    {
        memcpy(buf, str, char_width);
        buf[char_width] = '\0';
        *screen_width = vifm_wcwidth(get_first_wchar(str));
    }
    else if(str[0] == '\n')
    {
        buf[0] = '\0';
        *screen_width = 0;
    }
    else if(str[0] == '\b')
    {
        strcpy(buf, "");
        *screen_width = 0;
    }
    else if(str[0] == '\r')
    {
        strcpy(buf, "<cr>");
        *screen_width = 4;
    }
    else if(str[0] == '\t')
    {
        const size_t space_count = cfg.tab_stop - pos%cfg.tab_stop;
        memset(buf, ' ', space_count);
        buf[space_count] = '\0';
        *screen_width = space_count;
    }
    else if(str[0] == '\033')
    {
        char *dst = buf;
        while(*str != 'm' && *str != '\0' && (size_t)(dst - buf) < sizeof(buf) - 2)
        {
            *dst++ = *str++;
        }
        if(*str != 'm')
        {
            buf[0] = '\0';
        }
        else
        {
            *dst++ = 'm';
            *dst = '\0';
        }
        *screen_width = 0;
    }
    else if((unsigned char)str[0] < (unsigned char)' ')
    {
        buf[0] = '^';
        buf[1] = ('A' - 1) + str[0];
        buf[2] = '\0';
        *screen_width = 2;
    }
    else
    {
        buf[0] = str[0];
        buf[1] = '\0';
        *screen_width = 1;
    }
    return buf;
}
Ejemplo n.º 8
0
/* Draws single menu item at position specified by line argument.  Non-zero
 * clear argument suppresses drawing current items in different color. */
static void
draw_menu_item(menu_state_t *ms, int pos, int line, int clear)
{
	menu_data_t *const m = ms->d;
	int i;
	int off;
	char *item_tail;
	const int width = (curr_stats.load_stage == 0) ? 100 : getmaxx(menu_win) - 2;

	/* Calculate color for the line. */
	int attrs;
	col_attr_t col = cfg.cs.color[WIN_COLOR];
	if(cfg.hl_search && ms->search_highlight &&
			ms->matches != NULL && ms->matches[pos][0] >= 0)
	{
		cs_mix_colors(&col, &cfg.cs.color[SELECTED_COLOR]);
	}
	if(!clear && pos == m->pos)
	{
		cs_mix_colors(&col, &cfg.cs.color[CURR_LINE_COLOR]);
	}
	attrs = COLOR_PAIR(colmgr_get_pair(col.fg, col.bg)) | col.attr;

	/* Calculate offset of m->hor_pos's character in item text. */
	off = 0;
	i = m->hor_pos;
	while(i-- > 0 && m->items[pos][off] != '\0')
	{
		off += utf8_chrw(m->items[pos] + off);
	}

	item_tail = strdup(m->items[pos] + off);
	replace_char(item_tail, '\t', ' ');

	wattron(menu_win, attrs);

	/* Clear the area. */
	checked_wmove(menu_win, line, 1);
	if(curr_stats.load_stage != 0)
	{
		wprintw(menu_win, "%*s", width, "");
	}

	/* Draw visible part of item. */
	checked_wmove(menu_win, line, 2);
	if(utf8_strsw(item_tail) > (size_t)(width - 2))
	{
		void *p;
		const size_t len = utf8_nstrsnlen(item_tail, width - 3 - 2 + 1);
		memset(item_tail + len, ' ', strlen(item_tail) - len);
		p = realloc(item_tail, len + 4);
		if(p != NULL)
		{
			item_tail = p;
			strcpy(item_tail + len - 1, "...");
		}
		wprint(menu_win, item_tail);
	}
	else
	{
		const size_t len = utf8_nstrsnlen(item_tail, width - 2 + 1);
		item_tail[len] = '\0';
		wprint(menu_win, item_tail);
	}

	wattroff(menu_win, attrs);

	if(ms->search_highlight && ms->matches != NULL && ms->matches[pos][0] >= 0)
	{
		draw_search_match(item_tail, ms->matches[pos][0] - m->hor_pos,
				ms->matches[pos][1] - m->hor_pos, line, width, attrs);
	}

	free(item_tail);
}
Ejemplo n.º 9
0
/* Forms new line with highlights of matcher of the re regular expression using
 * escape sequences that invert colors.  Returns NULL when no match found or
 * memory allocation error occurred. */
static char *
add_pattern_highlights(const char line[], size_t len, const char no_esc[],
		const int offsets[], const regex_t *re)
{
	/* XXX: this might benefit from a rewrite, logic of when escape sequences are
	 *      copied is unclear (sometimes along with first matched character,
	 *      sometimes before the match). */

	regmatch_t match;
	char *next;
	char *processed;
	int no_esc_pos = 0;
	int overhead = 0;
	int offset;

	if(regexec(re, no_esc, 1, &match, 0) != 0)
	{
		return NULL;
	}
	if((processed = malloc(len + 1)) == NULL)
	{
		return NULL;
	}

	/* Before the first match. */
	if(match.rm_so != 0 && no_esc[match.rm_so] == '\0')
	{
		/* This is needed to handle possibility of immediate break from the loop
		 * below. */
		offset = correct_offset(line, offsets, match.rm_so);
	}
	else
	{
		offset = offsets[match.rm_so];
	}
	strncpy(processed, line, offset);
	next = processed + offset;

	/* All matches. */
	do
	{
		int so_offset;
		void *ptr;
		const int empty_match = (match.rm_so == match.rm_eo);

		match.rm_so += no_esc_pos;
		match.rm_eo += no_esc_pos;

		so_offset = offsets[match.rm_so];

		if(empty_match)
		{
			if(no_esc[match.rm_eo] == '\0')
			{
				no_esc_pos = match.rm_eo;
				break;
			}
		}

		/* Between matches. */
		if(no_esc_pos != 0)
		{
			const int corrected = correct_offset(line, offsets, no_esc_pos);
			strncpy(next, line + corrected, so_offset - corrected);
		}

		if(empty_match)
		{
			/* Copy single character after the match to advance forward. */

			/* Position inside the line string. */
			const int esc_pos = (no_esc_pos == 0)
			                  ? (size_t)(next - processed)
			                  : correct_offset(line, offsets, no_esc_pos);
			/* Number of characters to copy from the line string. */
			const int len = (match.rm_so == 0)
			              ? utf8_chrw(no_esc)
			              : correct_offset(line, offsets, match.rm_so + 1) - esc_pos;
			strncpy(next, line + esc_pos, len);
			next += len;
			no_esc_pos += utf8_chrw(&no_esc[no_esc_pos]);
		}
		else
		{
			size_t new_overhead;
			size_t match_len;

			new_overhead = INV_OVERHEAD*count_substr_chars(no_esc, &match);
			len += new_overhead;
			if((ptr = realloc(processed, len + 1)) == NULL)
			{
				free(processed);
				return NULL;
			}
			processed = ptr;

			match_len = correct_offset(line, offsets, match.rm_eo) - so_offset;
			next = processed + so_offset + overhead;
			next = add_highlighted_substr(line + so_offset, match_len, next);

			no_esc_pos = match.rm_eo;
			overhead += new_overhead;
		}
	}
	while(regexec(re, no_esc + no_esc_pos, 1, &match, 0) == 0);

	/* Abort if there were no non-empty matches. */
	if(overhead == 0)
	{
		free(processed);
		return 0;
	}

	/* After the last match. */
	strcpy(next, line +
			(no_esc_pos == 0 ? (size_t)(next - processed) :
			 correct_offset(line, offsets, no_esc_pos)));

	return processed;
}
Ejemplo n.º 10
0
int
flist_find_group(const FileView *view, int next)
{
	/* TODO: refactor/simplify this function (flist_find_group()). */

	const int correction = next ? -1 : 0;
	const int lb = correction;
	const int ub = view->list_rows + correction;
	const int inc = next ? +1 : -1;

	int pos = view->list_pos;
	dir_entry_t *pentry = &view->dir_entry[pos];
	const char *ext = get_last_ext(pentry->name);
	size_t char_width = utf8_chrw(pentry->name);
	wchar_t ch = towupper(get_first_wchar(pentry->name));
	const SortingKey sorting_key =
		flist_custom_active(view) && cv_compare(view->custom.type)
		? SK_BY_ID
		: abs(view->sort[0]);
	const int is_dir = fentry_is_dir(pentry);
	const char *const type_str = get_type_str(pentry->type);
	regmatch_t pmatch = { .rm_so = 0, .rm_eo = 0 };
#ifndef _WIN32
	char perms[16];
	get_perm_string(perms, sizeof(perms), pentry->mode);
#endif
	if(sorting_key == SK_BY_GROUPS)
	{
		pmatch = get_group_match(&view->primary_group, pentry->name);
	}
	while(pos > lb && pos < ub)
	{
		dir_entry_t *nentry;
		pos += inc;
		nentry = &view->dir_entry[pos];
		switch(sorting_key)
		{
			case SK_BY_FILEEXT:
				if(fentry_is_dir(nentry))
				{
					if(strncmp(pentry->name, nentry->name, char_width) != 0)
					{
						return pos;
					}
				}
				if(strcmp(get_last_ext(nentry->name), ext) != 0)
				{
					return pos;
				}
				break;
			case SK_BY_EXTENSION:
				if(strcmp(get_last_ext(nentry->name), ext) != 0)
					return pos;
				break;
			case SK_BY_GROUPS:
				{
					regmatch_t nmatch = get_group_match(&view->primary_group,
							nentry->name);

					if(pmatch.rm_eo - pmatch.rm_so != nmatch.rm_eo - nmatch.rm_so ||
							(pmatch.rm_eo != pmatch.rm_so &&
							 strncmp(pentry->name + pmatch.rm_so, nentry->name + nmatch.rm_so,
								 pmatch.rm_eo - pmatch.rm_so + 1U) != 0))
						return pos;
				}
				break;
			case SK_BY_TARGET:
				if((nentry->type == FT_LINK) != (pentry->type == FT_LINK))
				{
					/* One of the entries is not a link. */
					return pos;
				}
				if(nentry->type == FT_LINK)
				{
					/* Both entries are symbolic links. */
					char full_path[PATH_MAX];
					char nlink[PATH_MAX], plink[PATH_MAX];

					get_full_path_of(nentry, sizeof(full_path), full_path);
					if(get_link_target(full_path, nlink, sizeof(nlink)) != 0)
					{
						return pos;
					}
					get_full_path_of(pentry, sizeof(full_path), full_path);
					if(get_link_target(full_path, plink, sizeof(plink)) != 0)
					{
						return pos;
					}

					if(stroscmp(nlink, plink) != 0)
					{
						return pos;
					}
				}
				break;
			case SK_BY_NAME:
				if(strncmp(pentry->name, nentry->name, char_width) != 0)
					return pos;
				break;
			case SK_BY_INAME:
				if((wchar_t)towupper(get_first_wchar(nentry->name)) != ch)
					return pos;
				break;
			case SK_BY_SIZE:
				if(nentry->size != pentry->size)
					return pos;
				break;
			case SK_BY_NITEMS:
				if(entry_get_nitems(view, nentry) != entry_get_nitems(view, pentry))
					return pos;
				break;
			case SK_BY_TIME_ACCESSED:
				if(nentry->atime != pentry->atime)
					return pos;
				break;
			case SK_BY_TIME_CHANGED:
				if(nentry->ctime != pentry->ctime)
					return pos;
				break;
			case SK_BY_TIME_MODIFIED:
				if(nentry->mtime != pentry->mtime)
					return pos;
				break;
			case SK_BY_DIR:
				if(is_dir != fentry_is_dir(nentry))
				{
					return pos;
				}
				break;
			case SK_BY_TYPE:
				if(get_type_str(nentry->type) != type_str)
				{
					return pos;
				}
				break;
#ifndef _WIN32
			case SK_BY_GROUP_NAME:
			case SK_BY_GROUP_ID:
				if(nentry->gid != pentry->gid)
					return pos;
				break;
			case SK_BY_OWNER_NAME:
			case SK_BY_OWNER_ID:
				if(nentry->uid != pentry->uid)
					return pos;
				break;
			case SK_BY_MODE:
				if(nentry->mode != pentry->mode)
					return pos;
				break;
			case SK_BY_PERMISSIONS:
				{
					char nperms[16];
					get_perm_string(nperms, sizeof(nperms), nentry->mode);
					if(strcmp(nperms, perms) != 0)
					{
						return pos;
					}
					break;
				}
			case SK_BY_NLINKS:
				if(nentry->nlinks != pentry->nlinks)
				{
					return pos;
				}
				break;
#endif
		}
		/* Id sorting is builtin only and is defined outside SortingKey
		 * enumeration. */
		if((int)sorting_key == SK_BY_ID)
		{
			if(nentry->id != pentry->id)
			{
				return pos;
			}
		}
	}
	return pos;
}

/* Finds pointer to the beginning of the last extension of the file name.
 * Returns the pointer, which might point to the NUL byte if there are no
 * extensions. */
static const char *
get_last_ext(const char name[])
{
	const char *const ext = strrchr(name, '.');
	return (ext == NULL) ? (name + strlen(name)) : (ext + 1);
}

int
flist_find_dir_group(const FileView *view, int next)
{
	const int correction = next ? -1 : 0;
	const int lb = correction;
	const int ub = view->list_rows + correction;
	const int inc = next ? +1 : -1;

	int pos = curr_view->list_pos;
	dir_entry_t *pentry = &curr_view->dir_entry[pos];
	const int is_dir = fentry_is_dir(pentry);
	while(pos > lb && pos < ub)
	{
		dir_entry_t *nentry;
		pos += inc;
		nentry = &curr_view->dir_entry[pos];
		if(is_dir != fentry_is_dir(nentry))
		{
			break;
		}
	}
	return pos;
}

int
flist_first_sibling(const FileView *view)
{
	const int parent = view->list_pos - view->dir_entry[view->list_pos].child_pos;
	return (parent == view->list_pos ? 0 : parent + 1);
}

int
flist_last_sibling(const FileView *view)
{
	int pos = view->list_pos - view->dir_entry[view->list_pos].child_pos;
	if(pos == view->list_pos)
	{
		/* For top-level entry, find the last top-level entry. */
		pos = view->list_rows - 1;
		while(view->dir_entry[pos].child_pos != 0)
		{
			pos -= view->dir_entry[pos].child_pos;
		}
	}
	else
	{
		/* For non-top-level entry, go to last tree item and go up until our
		 * child. */
		const int parent = pos;
		pos = parent + view->dir_entry[parent].child_count;
		while(pos - view->dir_entry[pos].child_pos != parent)
		{
			pos -= view->dir_entry[pos].child_pos;
		}
	}
	return pos;
}