mirror of
				https://github.com/tmux/tmux.git
				synced 2025-11-04 00:56:10 +00:00 
			
		
		
		
	Change how escaping is processed for formats so that ## and # can be
used in styles. Also add a 'w' format modifier for the width. From Chas J Owens IV in GitHub issue 2389.
This commit is contained in:
		
							
								
								
									
										174
									
								
								format-draw.c
									
									
									
									
									
								
							
							
						
						
									
										174
									
								
								format-draw.c
									
									
									
									
									
								
							@@ -486,6 +486,18 @@ format_draw_right(struct screen_write_ctx *octx, u_int available, u_int ocx,
 | 
			
		||||
	    focus_end, frs);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Draw multiple characters. */
 | 
			
		||||
static void
 | 
			
		||||
format_draw_many(struct screen_write_ctx *ctx, struct style *sy, char ch,
 | 
			
		||||
    u_int n)
 | 
			
		||||
{
 | 
			
		||||
	u_int	i;
 | 
			
		||||
 | 
			
		||||
	utf8_set(&sy->gc.data, ch);
 | 
			
		||||
	for (i = 0; i < n; i++)
 | 
			
		||||
		screen_write_cell(ctx, &sy->gc);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Draw a format to a screen. */
 | 
			
		||||
void
 | 
			
		||||
format_draw(struct screen_write_ctx *octx, const struct grid_cell *base,
 | 
			
		||||
@@ -509,10 +521,10 @@ format_draw(struct screen_write_ctx *octx, const struct grid_cell *base,
 | 
			
		||||
	size_t			 size = strlen(expanded);
 | 
			
		||||
	struct screen		*os = octx->s, s[TOTAL];
 | 
			
		||||
	struct screen_write_ctx	 ctx[TOTAL];
 | 
			
		||||
	u_int			 ocx = os->cx, ocy = os->cy, i, width[TOTAL];
 | 
			
		||||
	u_int			 ocx = os->cx, ocy = os->cy, n, i, width[TOTAL];
 | 
			
		||||
	u_int			 map[] = { LEFT, LEFT, CENTRE, RIGHT };
 | 
			
		||||
	int			 focus_start = -1, focus_end = -1;
 | 
			
		||||
	int			 list_state = -1, fill = -1;
 | 
			
		||||
	int			 list_state = -1, fill = -1, even;
 | 
			
		||||
	enum style_align	 list_align = STYLE_ALIGN_DEFAULT;
 | 
			
		||||
	struct grid_cell	 gc, current_default;
 | 
			
		||||
	struct style		 sy, saved_sy;
 | 
			
		||||
@@ -547,6 +559,34 @@ format_draw(struct screen_write_ctx *octx, const struct grid_cell *base,
 | 
			
		||||
	 */
 | 
			
		||||
	cp = expanded;
 | 
			
		||||
	while (*cp != '\0') {
 | 
			
		||||
		/* Handle sequences of #. */
 | 
			
		||||
		if (cp[0] == '#' && cp[1] != '[' && cp[1] != '\0') {
 | 
			
		||||
			for (n = 1; cp[n] == '#'; n++)
 | 
			
		||||
				 /* nothing */;
 | 
			
		||||
			if (cp[n] != '[') {
 | 
			
		||||
				width[current] += n;
 | 
			
		||||
				cp += n;
 | 
			
		||||
				format_draw_many(&ctx[current], &sy, '#', n);
 | 
			
		||||
				continue;
 | 
			
		||||
			}
 | 
			
		||||
			even = ((n % 2) == 0);
 | 
			
		||||
			if (even)
 | 
			
		||||
				cp += (n + 1);
 | 
			
		||||
			else
 | 
			
		||||
				cp += (n - 1);
 | 
			
		||||
			if (sy.ignore)
 | 
			
		||||
				continue;
 | 
			
		||||
			format_draw_many(&ctx[current], &sy, '#', n / 2);
 | 
			
		||||
			width[current] += (n / 2);
 | 
			
		||||
			if (even) {
 | 
			
		||||
				utf8_set(ud, '[');
 | 
			
		||||
				screen_write_cell(&ctx[current], &sy.gc);
 | 
			
		||||
				width[current]++;
 | 
			
		||||
			}
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/* Is this not a style? */
 | 
			
		||||
		if (cp[0] != '#' || cp[1] != '[' || sy.ignore) {
 | 
			
		||||
			/* See if this is a UTF-8 character. */
 | 
			
		||||
			if ((more = utf8_open(ud, *cp)) == UTF8_MORE) {
 | 
			
		||||
@@ -796,13 +836,33 @@ u_int
 | 
			
		||||
format_width(const char *expanded)
 | 
			
		||||
{
 | 
			
		||||
	const char		*cp, *end;
 | 
			
		||||
	u_int			 width = 0;
 | 
			
		||||
	u_int			 n, width = 0;
 | 
			
		||||
	struct utf8_data	 ud;
 | 
			
		||||
	enum utf8_state		 more;
 | 
			
		||||
 | 
			
		||||
	cp = expanded;
 | 
			
		||||
	while (*cp != '\0') {
 | 
			
		||||
		if (cp[0] == '#' && cp[1] == '[') {
 | 
			
		||||
		if (*cp == '#') {
 | 
			
		||||
			for (n = 1; cp[n] == '#'; n++)
 | 
			
		||||
				/* nothing */;
 | 
			
		||||
			if (cp[n] != '[') {
 | 
			
		||||
				width += n;
 | 
			
		||||
				cp += n;
 | 
			
		||||
				continue;
 | 
			
		||||
			}
 | 
			
		||||
			width += (n / 2); /* one for each ## */
 | 
			
		||||
 | 
			
		||||
			if ((n % 2) == 0) {
 | 
			
		||||
				/*
 | 
			
		||||
				 * An even number of #s means that all #s are
 | 
			
		||||
				 * escaped, so not a style.
 | 
			
		||||
				 */
 | 
			
		||||
				width++; /* one for the [ */
 | 
			
		||||
				cp += (n + 1);
 | 
			
		||||
				continue;
 | 
			
		||||
			}
 | 
			
		||||
			cp += (n - 1); /* point to the [ */
 | 
			
		||||
 | 
			
		||||
			end = format_skip(cp + 2, "]");
 | 
			
		||||
			if (end == NULL)
 | 
			
		||||
				return (0);
 | 
			
		||||
@@ -823,19 +883,57 @@ format_width(const char *expanded)
 | 
			
		||||
	return (width);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Trim on the left, taking #[] into account. */
 | 
			
		||||
/*
 | 
			
		||||
 * Trim on the left, taking #[] into account.  Note, we copy the whole set of
 | 
			
		||||
 * unescaped #s, but only add their escaped size to width. This is because the
 | 
			
		||||
 * format_draw function will actually do the escaping when it runs
 | 
			
		||||
 */
 | 
			
		||||
char *
 | 
			
		||||
format_trim_left(const char *expanded, u_int limit)
 | 
			
		||||
{
 | 
			
		||||
	char			*copy, *out;
 | 
			
		||||
	const char		*cp = expanded, *end;
 | 
			
		||||
	u_int			 width = 0;
 | 
			
		||||
	u_int			 even, n, width = 0;
 | 
			
		||||
	struct utf8_data	 ud;
 | 
			
		||||
	enum utf8_state		 more;
 | 
			
		||||
 | 
			
		||||
	out = copy = xmalloc(strlen(expanded) + 1);
 | 
			
		||||
	out = copy = xcalloc(1, strlen(expanded) + 1);
 | 
			
		||||
	while (*cp != '\0') {
 | 
			
		||||
		if (cp[0] == '#' && cp[1] == '[') {
 | 
			
		||||
		if (width >= limit)
 | 
			
		||||
			break;
 | 
			
		||||
		if (*cp == '#') {
 | 
			
		||||
			for (end = cp + 1; *end == '#'; end++)
 | 
			
		||||
				/* nothing */;
 | 
			
		||||
			n = end - cp;
 | 
			
		||||
			if (*end != '[') {
 | 
			
		||||
				if (n > limit - width)
 | 
			
		||||
					n = limit - width;
 | 
			
		||||
				memcpy(out, cp, n);
 | 
			
		||||
				out += n;
 | 
			
		||||
				width += n;
 | 
			
		||||
				cp = end;
 | 
			
		||||
				continue;
 | 
			
		||||
			}
 | 
			
		||||
			even = ((n % 2) == 0);
 | 
			
		||||
 | 
			
		||||
			n /= 2;
 | 
			
		||||
			if (n > limit - width)
 | 
			
		||||
				n = limit - width;
 | 
			
		||||
			width += n;
 | 
			
		||||
			n *= 2;
 | 
			
		||||
			memcpy(out, cp, n);
 | 
			
		||||
			out += n;
 | 
			
		||||
 | 
			
		||||
			if (even) {
 | 
			
		||||
				if (width + 1 <= limit) {
 | 
			
		||||
					*out++ = '[';
 | 
			
		||||
					width++;
 | 
			
		||||
				}
 | 
			
		||||
				cp = end + 1;
 | 
			
		||||
				continue;
 | 
			
		||||
			}
 | 
			
		||||
			cp = end - 1;
 | 
			
		||||
 | 
			
		||||
			end = format_skip(cp + 2, "]");
 | 
			
		||||
			if (end == NULL)
 | 
			
		||||
				break;
 | 
			
		||||
@@ -873,7 +971,7 @@ format_trim_right(const char *expanded, u_int limit)
 | 
			
		||||
{
 | 
			
		||||
	char			*copy, *out;
 | 
			
		||||
	const char		*cp = expanded, *end;
 | 
			
		||||
	u_int			 width = 0, total_width, skip;
 | 
			
		||||
	u_int			 width = 0, total_width, skip, old_n, even, n;
 | 
			
		||||
	struct utf8_data	 ud;
 | 
			
		||||
	enum utf8_state		 more;
 | 
			
		||||
 | 
			
		||||
@@ -882,12 +980,64 @@ format_trim_right(const char *expanded, u_int limit)
 | 
			
		||||
		return (xstrdup(expanded));
 | 
			
		||||
	skip = total_width - limit;
 | 
			
		||||
 | 
			
		||||
	out = copy = xmalloc(strlen(expanded) + 1);
 | 
			
		||||
	out = copy = xcalloc(1, strlen(expanded) + 1);
 | 
			
		||||
	while (*cp != '\0') {
 | 
			
		||||
		if (cp[0] == '#' && cp[1] == '[') {
 | 
			
		||||
		if (*cp == '#') {
 | 
			
		||||
			for (end = cp + 1; *end == '#'; end++)
 | 
			
		||||
				/* nothing */;
 | 
			
		||||
			old_n = n = end - cp;
 | 
			
		||||
			if (*end != '[') {
 | 
			
		||||
				if (width <= skip) {
 | 
			
		||||
					if (skip - width >= n)
 | 
			
		||||
						n = 0;
 | 
			
		||||
					else
 | 
			
		||||
						n -= (skip - width);
 | 
			
		||||
				}
 | 
			
		||||
				if (n != 0) {
 | 
			
		||||
					memcpy(out, cp, n);
 | 
			
		||||
					out += n;
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				/*
 | 
			
		||||
				 * The width always increases by the full
 | 
			
		||||
				 * amount even if we can't copy anything yet.
 | 
			
		||||
				 */
 | 
			
		||||
				width += old_n;
 | 
			
		||||
				cp = end;
 | 
			
		||||
				continue;
 | 
			
		||||
			}
 | 
			
		||||
			even = ((n % 2) == 0);
 | 
			
		||||
 | 
			
		||||
			n /= 2;
 | 
			
		||||
			if (width <= skip) {
 | 
			
		||||
				if (skip - width >= n)
 | 
			
		||||
					n = 0;
 | 
			
		||||
				else
 | 
			
		||||
					n -= (skip - width);
 | 
			
		||||
			}
 | 
			
		||||
			if (n != 0) {
 | 
			
		||||
				/*
 | 
			
		||||
				 * Copy the full amount because it hasn't been
 | 
			
		||||
				 * escaped yet.
 | 
			
		||||
				 */
 | 
			
		||||
				memcpy(out, cp, old_n);
 | 
			
		||||
				out += old_n;
 | 
			
		||||
			}
 | 
			
		||||
			cp += old_n;
 | 
			
		||||
			width += (old_n / 2) - even;
 | 
			
		||||
 | 
			
		||||
			if (even) {
 | 
			
		||||
				if (width > skip)
 | 
			
		||||
					*out++ = '[';
 | 
			
		||||
				width++;
 | 
			
		||||
				continue;
 | 
			
		||||
			}
 | 
			
		||||
			cp = end - 1;
 | 
			
		||||
 | 
			
		||||
			end = format_skip(cp + 2, "]");
 | 
			
		||||
			if (end == NULL)
 | 
			
		||||
			if (end == NULL) {
 | 
			
		||||
				break;
 | 
			
		||||
			}
 | 
			
		||||
			memcpy(out, cp, end + 1 - cp);
 | 
			
		||||
			out += (end + 1 - cp);
 | 
			
		||||
			cp = end + 1;
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										40
									
								
								format.c
									
									
									
									
									
								
							
							
						
						
									
										40
									
								
								format.c
									
									
									
									
									
								
							@@ -98,6 +98,7 @@ format_job_cmp(struct format_job *fj1, struct format_job *fj2)
 | 
			
		||||
#define FORMAT_PANES 0x200
 | 
			
		||||
#define FORMAT_PRETTY 0x400
 | 
			
		||||
#define FORMAT_LENGTH 0x800
 | 
			
		||||
#define FORMAT_WIDTH 0x1000
 | 
			
		||||
 | 
			
		||||
/* Limit on recursion. */
 | 
			
		||||
#define FORMAT_LOOP_LIMIT 10
 | 
			
		||||
@@ -1671,7 +1672,7 @@ format_build_modifiers(struct format_expand_state *es, const char **s,
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Modifiers are a ; separated list of the forms:
 | 
			
		||||
	 *      l,m,C,b,d,n,t,q,E,T,S,W,P,<,>
 | 
			
		||||
	 *      l,m,C,b,d,n,t,w,q,E,T,S,W,P,<,>
 | 
			
		||||
	 *	=a
 | 
			
		||||
	 *	=/a
 | 
			
		||||
	 *      =/a/
 | 
			
		||||
@@ -1688,7 +1689,7 @@ format_build_modifiers(struct format_expand_state *es, const char **s,
 | 
			
		||||
			cp++;
 | 
			
		||||
 | 
			
		||||
		/* Check single character modifiers with no arguments. */
 | 
			
		||||
		if (strchr("lbdnqETSWP<>", cp[0]) != NULL &&
 | 
			
		||||
		if (strchr("lbdnqwETSWP<>", cp[0]) != NULL &&
 | 
			
		||||
		    format_is_end(cp[1])) {
 | 
			
		||||
			format_add_modifier(&list, count, cp, 1, NULL, 0);
 | 
			
		||||
			cp++;
 | 
			
		||||
@@ -2184,6 +2185,9 @@ format_replace(struct format_expand_state *es, const char *key, size_t keylen,
 | 
			
		||||
				if (errptr != NULL)
 | 
			
		||||
					width = 0;
 | 
			
		||||
				break;
 | 
			
		||||
			case 'w':
 | 
			
		||||
				modifiers |= FORMAT_WIDTH;
 | 
			
		||||
				break;
 | 
			
		||||
			case 'e':
 | 
			
		||||
				if (fm->argc < 1 || fm->argc > 3)
 | 
			
		||||
					break;
 | 
			
		||||
@@ -2456,13 +2460,19 @@ done:
 | 
			
		||||
		format_log(es, "applied padding width %d: %s", width, value);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Replace with the length if needed. */
 | 
			
		||||
	/* Replace with the length or width if needed. */
 | 
			
		||||
	if (modifiers & FORMAT_LENGTH) {
 | 
			
		||||
		xasprintf(&new, "%zu", strlen(value));
 | 
			
		||||
		free(value);
 | 
			
		||||
		value = new;
 | 
			
		||||
		format_log(es, "replacing with length: %s", new);
 | 
			
		||||
	}
 | 
			
		||||
	if (modifiers & FORMAT_WIDTH) {
 | 
			
		||||
		xasprintf(&new, "%u", format_width(value));
 | 
			
		||||
		free(value);
 | 
			
		||||
		value = new;
 | 
			
		||||
		format_log(es, "replacing with width: %s", new);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Expand the buffer and copy in the value. */
 | 
			
		||||
	valuelen = strlen(value);
 | 
			
		||||
@@ -2589,8 +2599,30 @@ format_expand1(struct format_expand_state *es, const char *fmt)
 | 
			
		||||
				break;
 | 
			
		||||
			fmt += n + 1;
 | 
			
		||||
			continue;
 | 
			
		||||
		case '}':
 | 
			
		||||
		case '#':
 | 
			
		||||
			/*
 | 
			
		||||
			 * If ##[ (with two or more #s), then it is a style and
 | 
			
		||||
			 * can be left for format_draw to handle.
 | 
			
		||||
			 */
 | 
			
		||||
			ptr = fmt;
 | 
			
		||||
			n = 2;
 | 
			
		||||
			while (*ptr == '#') {
 | 
			
		||||
				ptr++;
 | 
			
		||||
				n++;
 | 
			
		||||
			}
 | 
			
		||||
			if (*ptr == '[') {
 | 
			
		||||
				format_log(es, "found #*%zu[", n);
 | 
			
		||||
				while (len - off < n + 2) {
 | 
			
		||||
					buf = xreallocarray(buf, 2, len);
 | 
			
		||||
					len *= 2;
 | 
			
		||||
				}
 | 
			
		||||
				memcpy(buf + off, fmt - 2, n + 1);
 | 
			
		||||
				off += n + 1;
 | 
			
		||||
				fmt = ptr + 1;
 | 
			
		||||
				continue;
 | 
			
		||||
			}
 | 
			
		||||
			/* FALLTHROUGH */
 | 
			
		||||
		case '}':
 | 
			
		||||
		case ',':
 | 
			
		||||
			format_log(es, "found #%c", ch);
 | 
			
		||||
			while (len - off < 2) {
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										4
									
								
								tmux.1
									
									
									
									
									
								
							
							
						
						
									
										4
									
								
								tmux.1
									
									
									
									
									
								
							@@ -4591,7 +4591,9 @@ pads the string to a given width, for example
 | 
			
		||||
will result in a width of at least 10 characters.
 | 
			
		||||
A positive width pads on the left, a negative on the right.
 | 
			
		||||
.Ql n
 | 
			
		||||
expands to the length of the variable, for example
 | 
			
		||||
expands to the length of the variable and
 | 
			
		||||
.Ql w
 | 
			
		||||
to its width when displayed, for example
 | 
			
		||||
.Ql #{n:window_name} .
 | 
			
		||||
.Pp
 | 
			
		||||
Prefixing a time variable with
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user