xref: /netbsd-src/external/bsd/tmux/dist/format-draw.c (revision c23f9150cad51fdd442fa1806fac769ae26a1fdd)
1ef36e747Schristos /* $OpenBSD$ */
2ef36e747Schristos 
3ef36e747Schristos /*
4ef36e747Schristos  * Copyright (c) 2019 Nicholas Marriott <nicholas.marriott@gmail.com>
5ef36e747Schristos  *
6ef36e747Schristos  * Permission to use, copy, modify, and distribute this software for any
7ef36e747Schristos  * purpose with or without fee is hereby granted, provided that the above
8ef36e747Schristos  * copyright notice and this permission notice appear in all copies.
9ef36e747Schristos  *
10ef36e747Schristos  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11ef36e747Schristos  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12ef36e747Schristos  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13ef36e747Schristos  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14ef36e747Schristos  * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
15ef36e747Schristos  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
16ef36e747Schristos  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17ef36e747Schristos  */
18ef36e747Schristos 
19ef36e747Schristos #include <sys/types.h>
20ef36e747Schristos 
21ef36e747Schristos #include <stdlib.h>
22ef36e747Schristos #include <string.h>
23ef36e747Schristos 
24ef36e747Schristos #include "tmux.h"
25ef36e747Schristos 
26ef36e747Schristos /* Format range. */
27ef36e747Schristos struct format_range {
28ef36e747Schristos 	u_int				 index;
29ef36e747Schristos 	struct screen			*s;
30ef36e747Schristos 
31ef36e747Schristos 	u_int				 start;
32ef36e747Schristos 	u_int				 end;
33ef36e747Schristos 
34ef36e747Schristos 	enum style_range_type		 type;
35ef36e747Schristos 	u_int				 argument;
36*c23f9150Swiz 	char                             string[16];
37ef36e747Schristos 
38ef36e747Schristos 	TAILQ_ENTRY(format_range)	 entry;
39ef36e747Schristos };
40ef36e747Schristos TAILQ_HEAD(format_ranges, format_range);
41ef36e747Schristos 
42ef36e747Schristos /* Does this range match this style? */
43ef36e747Schristos static int
format_is_type(struct format_range * fr,struct style * sy)44ef36e747Schristos format_is_type(struct format_range *fr, struct style *sy)
45ef36e747Schristos {
46ef36e747Schristos 	if (fr->type != sy->range_type)
47ef36e747Schristos 		return (0);
48*c23f9150Swiz 	switch (fr->type) {
49*c23f9150Swiz 	case STYLE_RANGE_NONE:
50*c23f9150Swiz 	case STYLE_RANGE_LEFT:
51*c23f9150Swiz 	case STYLE_RANGE_RIGHT:
52*c23f9150Swiz 		return (1);
53*c23f9150Swiz 	case STYLE_RANGE_PANE:
54*c23f9150Swiz 	case STYLE_RANGE_WINDOW:
55*c23f9150Swiz 	case STYLE_RANGE_SESSION:
56*c23f9150Swiz 		return (fr->argument == sy->range_argument);
57*c23f9150Swiz 	case STYLE_RANGE_USER:
58*c23f9150Swiz 		return (strcmp(fr->string, sy->range_string) == 0);
59*c23f9150Swiz 	}
60ef36e747Schristos 	return (1);
61ef36e747Schristos }
62ef36e747Schristos 
63ef36e747Schristos /* Free a range. */
64ef36e747Schristos static void
format_free_range(struct format_ranges * frs,struct format_range * fr)65ef36e747Schristos format_free_range(struct format_ranges *frs, struct format_range *fr)
66ef36e747Schristos {
67ef36e747Schristos 	TAILQ_REMOVE(frs, fr, entry);
68ef36e747Schristos 	free(fr);
69ef36e747Schristos }
70ef36e747Schristos 
71ef36e747Schristos /* Fix range positions. */
72ef36e747Schristos static void
format_update_ranges(struct format_ranges * frs,struct screen * s,u_int offset,u_int start,u_int width)73ef36e747Schristos format_update_ranges(struct format_ranges *frs, struct screen *s, u_int offset,
74ef36e747Schristos     u_int start, u_int width)
75ef36e747Schristos {
76ef36e747Schristos 	struct format_range	*fr, *fr1;
77ef36e747Schristos 
78ef36e747Schristos 	if (frs == NULL)
79ef36e747Schristos 		return;
80ef36e747Schristos 
81ef36e747Schristos 	TAILQ_FOREACH_SAFE(fr, frs, entry, fr1) {
82ef36e747Schristos 		if (fr->s != s)
83ef36e747Schristos 			continue;
84ef36e747Schristos 
85ef36e747Schristos 		if (fr->end <= start || fr->start >= start + width) {
86ef36e747Schristos 			format_free_range(frs, fr);
87ef36e747Schristos 			continue;
88ef36e747Schristos 		}
89ef36e747Schristos 
90ef36e747Schristos 		if (fr->start < start)
91ef36e747Schristos 			fr->start = start;
92ef36e747Schristos 		if (fr->end > start + width)
93ef36e747Schristos 			fr->end = start + width;
94ef36e747Schristos 		if (fr->start == fr->end) {
95ef36e747Schristos 			format_free_range(frs, fr);
96ef36e747Schristos 			continue;
97ef36e747Schristos 		}
98ef36e747Schristos 
99ef36e747Schristos 		fr->start -= start;
100ef36e747Schristos 		fr->end -= start;
101ef36e747Schristos 
102ef36e747Schristos 		fr->start += offset;
103ef36e747Schristos 		fr->end += offset;
104ef36e747Schristos 	}
105ef36e747Schristos }
106ef36e747Schristos 
107ef36e747Schristos /* Draw a part of the format. */
108ef36e747Schristos static void
format_draw_put(struct screen_write_ctx * octx,u_int ocx,u_int ocy,struct screen * s,struct format_ranges * frs,u_int offset,u_int start,u_int width)109ef36e747Schristos format_draw_put(struct screen_write_ctx *octx, u_int ocx, u_int ocy,
110ef36e747Schristos     struct screen *s, struct format_ranges *frs, u_int offset, u_int start,
111ef36e747Schristos     u_int width)
112ef36e747Schristos {
113ef36e747Schristos 	/*
114ef36e747Schristos 	 * The offset is how far from the cursor on the target screen; start
115ef36e747Schristos 	 * and width how much to copy from the source screen.
116ef36e747Schristos 	 */
117ef36e747Schristos 	screen_write_cursormove(octx, ocx + offset, ocy, 0);
118ef36e747Schristos 	screen_write_fast_copy(octx, s, start, 0, width, 1);
119ef36e747Schristos 	format_update_ranges(frs, s, offset, start, width);
120ef36e747Schristos }
121ef36e747Schristos 
122ef36e747Schristos /* Draw list part of format. */
123ef36e747Schristos static void
format_draw_put_list(struct screen_write_ctx * octx,u_int ocx,u_int ocy,u_int offset,u_int width,struct screen * list,struct screen * list_left,struct screen * list_right,int focus_start,int focus_end,struct format_ranges * frs)124ef36e747Schristos format_draw_put_list(struct screen_write_ctx *octx,
125ef36e747Schristos     u_int ocx, u_int ocy, u_int offset, u_int width, struct screen *list,
126ef36e747Schristos     struct screen *list_left, struct screen *list_right, int focus_start,
127ef36e747Schristos     int focus_end, struct format_ranges *frs)
128ef36e747Schristos {
129ef36e747Schristos 	u_int	start, focus_centre;
130ef36e747Schristos 
131ef36e747Schristos 	/* If there is enough space for the list, draw it entirely. */
132ef36e747Schristos 	if (width >= list->cx) {
133ef36e747Schristos 		format_draw_put(octx, ocx, ocy, list, frs, offset, 0, width);
134ef36e747Schristos 		return;
135ef36e747Schristos 	}
136ef36e747Schristos 
137ef36e747Schristos 	/* The list needs to be trimmed. Try to keep the focus visible. */
138ef36e747Schristos 	focus_centre = focus_start + (focus_end - focus_start) / 2;
139ef36e747Schristos 	if (focus_centre < width / 2)
140ef36e747Schristos 		start = 0;
141ef36e747Schristos 	else
142ef36e747Schristos 		start = focus_centre - width / 2;
143ef36e747Schristos 	if (start + width > list->cx)
144ef36e747Schristos 		start = list->cx - width;
145ef36e747Schristos 
146ef36e747Schristos 	/* Draw <> markers at either side if needed. */
147ef36e747Schristos 	if (start != 0 && width > list_left->cx) {
148ef36e747Schristos 		screen_write_cursormove(octx, ocx + offset, ocy, 0);
149ef36e747Schristos 		screen_write_fast_copy(octx, list_left, 0, 0, list_left->cx, 1);
150ef36e747Schristos 		offset += list_left->cx;
151ef36e747Schristos 		start += list_left->cx;
152ef36e747Schristos 		width -= list_left->cx;
153ef36e747Schristos 	}
154ef36e747Schristos 	if (start + width < list->cx && width > list_right->cx) {
155aa83ff61Schristos 		screen_write_cursormove(octx, ocx + offset + width -
156aa83ff61Schristos 		    list_right->cx, ocy, 0);
157ef36e747Schristos 		screen_write_fast_copy(octx, list_right, 0, 0, list_right->cx,
158ef36e747Schristos 		    1);
159ef36e747Schristos 		width -= list_right->cx;
160ef36e747Schristos 	}
161ef36e747Schristos 
162ef36e747Schristos 	/* Draw the list screen itself. */
163ef36e747Schristos 	format_draw_put(octx, ocx, ocy, list, frs, offset, start, width);
164ef36e747Schristos }
165ef36e747Schristos 
166ef36e747Schristos /* Draw format with no list. */
167ef36e747Schristos static void
format_draw_none(struct screen_write_ctx * octx,u_int available,u_int ocx,u_int ocy,struct screen * left,struct screen * centre,struct screen * right,struct screen * abs_centre,struct format_ranges * frs)168ef36e747Schristos format_draw_none(struct screen_write_ctx *octx, u_int available, u_int ocx,
169ef36e747Schristos     u_int ocy, struct screen *left, struct screen *centre, struct screen *right,
1709fb66d81Schristos     struct screen *abs_centre, struct format_ranges *frs)
171ef36e747Schristos {
1729fb66d81Schristos 	u_int	width_left, width_centre, width_right, width_abs_centre;
173ef36e747Schristos 
174ef36e747Schristos 	width_left = left->cx;
175ef36e747Schristos 	width_centre = centre->cx;
176ef36e747Schristos 	width_right = right->cx;
1779fb66d81Schristos 	width_abs_centre = abs_centre->cx;
178ef36e747Schristos 
179ef36e747Schristos 	/*
180ef36e747Schristos 	 * Try to keep as much of the left and right as possible at the expense
181ef36e747Schristos 	 * of the centre.
182ef36e747Schristos 	 */
183ef36e747Schristos 	while (width_left + width_centre + width_right > available) {
184ef36e747Schristos 		if (width_centre > 0)
185ef36e747Schristos 			width_centre--;
186ef36e747Schristos 		else if (width_right > 0)
187ef36e747Schristos 			width_right--;
188ef36e747Schristos 		else
189ef36e747Schristos 			width_left--;
190ef36e747Schristos 	}
191ef36e747Schristos 
192ef36e747Schristos 	/* Write left. */
193ef36e747Schristos 	format_draw_put(octx, ocx, ocy, left, frs, 0, 0, width_left);
194ef36e747Schristos 
195ef36e747Schristos 	/* Write right at available - width_right. */
196ef36e747Schristos 	format_draw_put(octx, ocx, ocy, right, frs,
197ef36e747Schristos 	    available - width_right,
198ef36e747Schristos 	    right->cx - width_right,
199ef36e747Schristos 	    width_right);
200ef36e747Schristos 
201ef36e747Schristos 	/*
202ef36e747Schristos 	 * Write centre halfway between
203ef36e747Schristos 	 *     width_left
204ef36e747Schristos 	 * and
205ef36e747Schristos 	 *     available - width_right.
206ef36e747Schristos 	 */
207ef36e747Schristos 	format_draw_put(octx, ocx, ocy, centre, frs,
208ef36e747Schristos 	    width_left
209ef36e747Schristos 	    + ((available - width_right) - width_left) / 2
210ef36e747Schristos 	    - width_centre / 2,
211ef36e747Schristos 	    centre->cx / 2 - width_centre / 2,
212ef36e747Schristos 	    width_centre);
2139fb66d81Schristos 
2149fb66d81Schristos 	/*
2159fb66d81Schristos 	 * Write abs_centre in the perfect centre of all horizontal space.
2169fb66d81Schristos 	 */
2179fb66d81Schristos 	if (width_abs_centre > available)
2189fb66d81Schristos 		width_abs_centre = available;
2199fb66d81Schristos 	format_draw_put(octx, ocx, ocy, abs_centre, frs,
2209fb66d81Schristos 	    (available - width_abs_centre) / 2,
2219fb66d81Schristos 	    0,
2229fb66d81Schristos 	    width_abs_centre);
223ef36e747Schristos }
224ef36e747Schristos 
225ef36e747Schristos /* Draw format with list on the left. */
226ef36e747Schristos static void
format_draw_left(struct screen_write_ctx * octx,u_int available,u_int ocx,u_int ocy,struct screen * left,struct screen * centre,struct screen * right,struct screen * abs_centre,struct screen * list,struct screen * list_left,struct screen * list_right,struct screen * after,int focus_start,int focus_end,struct format_ranges * frs)227ef36e747Schristos format_draw_left(struct screen_write_ctx *octx, u_int available, u_int ocx,
228ef36e747Schristos     u_int ocy, struct screen *left, struct screen *centre, struct screen *right,
2299fb66d81Schristos     struct screen *abs_centre, struct screen *list, struct screen *list_left,
2309fb66d81Schristos     struct screen *list_right, struct screen *after, int focus_start,
2319fb66d81Schristos     int focus_end, struct format_ranges *frs)
232ef36e747Schristos {
233ef36e747Schristos 	u_int			width_left, width_centre, width_right;
2349fb66d81Schristos 	u_int			width_list, width_after, width_abs_centre;
235ef36e747Schristos 	struct screen_write_ctx	ctx;
236ef36e747Schristos 
237ef36e747Schristos 	width_left = left->cx;
238ef36e747Schristos 	width_centre = centre->cx;
239ef36e747Schristos 	width_right = right->cx;
2409fb66d81Schristos 	width_abs_centre = abs_centre->cx;
241ef36e747Schristos 	width_list = list->cx;
242ef36e747Schristos 	width_after = after->cx;
243ef36e747Schristos 
244ef36e747Schristos 	/*
245ef36e747Schristos 	 * Trim first the centre, then the list, then the right, then after the
246ef36e747Schristos 	 * list, then the left.
247ef36e747Schristos 	 */
248ef36e747Schristos 	while (width_left +
249ef36e747Schristos 	    width_centre +
250ef36e747Schristos 	    width_right +
251ef36e747Schristos 	    width_list +
252ef36e747Schristos 	    width_after > available) {
253ef36e747Schristos 		if (width_centre > 0)
254ef36e747Schristos 			width_centre--;
255ef36e747Schristos 		else if (width_list > 0)
256ef36e747Schristos 			width_list--;
257ef36e747Schristos 		else if (width_right > 0)
258ef36e747Schristos 			width_right--;
259ef36e747Schristos 		else if (width_after > 0)
260ef36e747Schristos 			width_after--;
261ef36e747Schristos 		else
262ef36e747Schristos 			width_left--;
263ef36e747Schristos 	}
264ef36e747Schristos 
265ef36e747Schristos 	/* If there is no list left, pass off to the no list function. */
266ef36e747Schristos 	if (width_list == 0) {
2679fb66d81Schristos 		screen_write_start(&ctx, left);
268ef36e747Schristos 		screen_write_fast_copy(&ctx, after, 0, 0, width_after, 1);
269ef36e747Schristos 		screen_write_stop(&ctx);
270ef36e747Schristos 
271ef36e747Schristos 		format_draw_none(octx, available, ocx, ocy, left, centre,
2729fb66d81Schristos 		    right, abs_centre, frs);
273ef36e747Schristos 		return;
274ef36e747Schristos 	}
275ef36e747Schristos 
276ef36e747Schristos 	/* Write left at 0. */
277ef36e747Schristos 	format_draw_put(octx, ocx, ocy, left, frs, 0, 0, width_left);
278ef36e747Schristos 
279ef36e747Schristos 	/* Write right at available - width_right. */
280ef36e747Schristos 	format_draw_put(octx, ocx, ocy, right, frs,
281ef36e747Schristos 	    available - width_right,
282ef36e747Schristos 	    right->cx - width_right,
283ef36e747Schristos 	    width_right);
284ef36e747Schristos 
285ef36e747Schristos 	/* Write after at width_left + width_list. */
286ef36e747Schristos 	format_draw_put(octx, ocx, ocy, after, frs,
287ef36e747Schristos 	    width_left + width_list,
288ef36e747Schristos 	    0,
289ef36e747Schristos 	    width_after);
290ef36e747Schristos 
291ef36e747Schristos 	/*
292ef36e747Schristos 	 * Write centre halfway between
293ef36e747Schristos 	 *     width_left + width_list + width_after
294ef36e747Schristos 	 * and
295ef36e747Schristos 	 *     available - width_right.
296ef36e747Schristos 	 */
297ef36e747Schristos 	format_draw_put(octx, ocx, ocy, centre, frs,
298ef36e747Schristos 	    (width_left + width_list + width_after)
299ef36e747Schristos 	    + ((available - width_right)
300ef36e747Schristos 		- (width_left + width_list + width_after)) / 2
301ef36e747Schristos 	    - width_centre / 2,
302ef36e747Schristos 	    centre->cx / 2 - width_centre / 2,
303ef36e747Schristos 	    width_centre);
304ef36e747Schristos 
305ef36e747Schristos 	/*
306ef36e747Schristos 	 * The list now goes from
307ef36e747Schristos 	 *     width_left
308ef36e747Schristos 	 * to
309ef36e747Schristos 	 *     width_left + width_list.
310ef36e747Schristos 	 * If there is no focus given, keep the left in focus.
311ef36e747Schristos 	 */
312ef36e747Schristos 	if (focus_start == -1 || focus_end == -1)
313ef36e747Schristos 		focus_start = focus_end = 0;
314ef36e747Schristos 	format_draw_put_list(octx, ocx, ocy, width_left, width_list, list,
315ef36e747Schristos 	    list_left, list_right, focus_start, focus_end, frs);
3169fb66d81Schristos 
3179fb66d81Schristos 	/*
3189fb66d81Schristos 	 * Write abs_centre in the perfect centre of all horizontal space.
3199fb66d81Schristos 	 */
3209fb66d81Schristos 	if (width_abs_centre > available)
3219fb66d81Schristos 		width_abs_centre = available;
3229fb66d81Schristos 	format_draw_put(octx, ocx, ocy, abs_centre, frs,
3239fb66d81Schristos 	    (available - width_abs_centre) / 2,
3249fb66d81Schristos 	    0,
3259fb66d81Schristos 	    width_abs_centre);
326ef36e747Schristos }
327ef36e747Schristos 
328ef36e747Schristos /* Draw format with list in the centre. */
329ef36e747Schristos static void
format_draw_centre(struct screen_write_ctx * octx,u_int available,u_int ocx,u_int ocy,struct screen * left,struct screen * centre,struct screen * right,struct screen * abs_centre,struct screen * list,struct screen * list_left,struct screen * list_right,struct screen * after,int focus_start,int focus_end,struct format_ranges * frs)330ef36e747Schristos format_draw_centre(struct screen_write_ctx *octx, u_int available, u_int ocx,
331ef36e747Schristos     u_int ocy, struct screen *left, struct screen *centre, struct screen *right,
3329fb66d81Schristos     struct screen *abs_centre, struct screen *list, struct screen *list_left,
3339fb66d81Schristos     struct screen *list_right, struct screen *after, int focus_start,
3349fb66d81Schristos     int focus_end, struct format_ranges *frs)
335ef36e747Schristos {
3369fb66d81Schristos 	u_int			width_left, width_centre, width_right, middle;
3379fb66d81Schristos 	u_int			width_list, width_after, width_abs_centre;
338ef36e747Schristos 	struct screen_write_ctx	ctx;
339ef36e747Schristos 
340ef36e747Schristos 	width_left = left->cx;
341ef36e747Schristos 	width_centre = centre->cx;
342ef36e747Schristos 	width_right = right->cx;
3439fb66d81Schristos 	width_abs_centre = abs_centre->cx;
344ef36e747Schristos 	width_list = list->cx;
345ef36e747Schristos 	width_after = after->cx;
346ef36e747Schristos 
347ef36e747Schristos 	/*
348ef36e747Schristos 	 * Trim first the list, then after the list, then the centre, then the
349ef36e747Schristos 	 * right, then the left.
350ef36e747Schristos 	 */
351ef36e747Schristos 	while (width_left +
352ef36e747Schristos 	    width_centre +
353ef36e747Schristos 	    width_right +
354ef36e747Schristos 	    width_list +
355ef36e747Schristos 	    width_after > available) {
356ef36e747Schristos 		if (width_list > 0)
357ef36e747Schristos 			width_list--;
358ef36e747Schristos 		else if (width_after > 0)
359ef36e747Schristos 			width_after--;
360ef36e747Schristos 		else if (width_centre > 0)
361ef36e747Schristos 			width_centre--;
362ef36e747Schristos 		else if (width_right > 0)
363ef36e747Schristos 			width_right--;
364ef36e747Schristos 		else
365ef36e747Schristos 			width_left--;
366ef36e747Schristos 	}
367ef36e747Schristos 
368ef36e747Schristos 	/* If there is no list left, pass off to the no list function. */
369ef36e747Schristos 	if (width_list == 0) {
3709fb66d81Schristos 		screen_write_start(&ctx, centre);
371ef36e747Schristos 		screen_write_fast_copy(&ctx, after, 0, 0, width_after, 1);
372ef36e747Schristos 		screen_write_stop(&ctx);
373ef36e747Schristos 
374ef36e747Schristos 		format_draw_none(octx, available, ocx, ocy, left, centre,
3759fb66d81Schristos 		    right, abs_centre, frs);
376ef36e747Schristos 		return;
377ef36e747Schristos 	}
378ef36e747Schristos 
379ef36e747Schristos 	/* Write left at 0. */
380ef36e747Schristos 	format_draw_put(octx, ocx, ocy, left, frs, 0, 0, width_left);
381ef36e747Schristos 
382ef36e747Schristos 	/* Write right at available - width_right. */
383ef36e747Schristos 	format_draw_put(octx, ocx, ocy, right, frs,
384ef36e747Schristos 	    available - width_right,
385ef36e747Schristos 	    right->cx - width_right,
386ef36e747Schristos 	    width_right);
387ef36e747Schristos 
388ef36e747Schristos 	/*
389ef36e747Schristos 	 * All three centre sections are offset from the middle of the
390ef36e747Schristos 	 * available space.
391ef36e747Schristos 	 */
392ef36e747Schristos 	middle = (width_left + ((available - width_right) - width_left) / 2);
393ef36e747Schristos 
394ef36e747Schristos 	/*
395ef36e747Schristos 	 * Write centre at
396ef36e747Schristos 	 *     middle - width_list / 2 - width_centre.
397ef36e747Schristos 	 */
398ef36e747Schristos 	format_draw_put(octx, ocx, ocy, centre, frs,
399ef36e747Schristos 	    middle - width_list / 2 - width_centre,
400ef36e747Schristos 	    0,
401ef36e747Schristos 	    width_centre);
402ef36e747Schristos 
403ef36e747Schristos 	/*
404ef36e747Schristos 	 * Write after at
4056483eba0Schristos 	 *     middle - width_list / 2 + width_list
406ef36e747Schristos 	 */
407ef36e747Schristos 	format_draw_put(octx, ocx, ocy, after, frs,
4086483eba0Schristos 	    middle - width_list / 2 + width_list,
409ef36e747Schristos 	    0,
410ef36e747Schristos 	    width_after);
411ef36e747Schristos 
412ef36e747Schristos 	/*
413ef36e747Schristos 	 * The list now goes from
414ef36e747Schristos 	 *     middle - width_list / 2
415ef36e747Schristos 	 * to
416ef36e747Schristos 	 *     middle + width_list / 2
417ef36e747Schristos 	 * If there is no focus given, keep the centre in focus.
418ef36e747Schristos 	 */
419ef36e747Schristos 	if (focus_start == -1 || focus_end == -1)
420ef36e747Schristos 		focus_start = focus_end = list->cx / 2;
421ef36e747Schristos 	format_draw_put_list(octx, ocx, ocy, middle - width_list / 2,
422ef36e747Schristos 	    width_list, list, list_left, list_right, focus_start, focus_end,
423ef36e747Schristos 	    frs);
4249fb66d81Schristos 
4259fb66d81Schristos 	/*
4269fb66d81Schristos 	 * Write abs_centre in the perfect centre of all horizontal space.
4279fb66d81Schristos 	 */
4289fb66d81Schristos 	if (width_abs_centre > available)
4299fb66d81Schristos 		width_abs_centre = available;
4309fb66d81Schristos 	format_draw_put(octx, ocx, ocy, abs_centre, frs,
4319fb66d81Schristos 	    (available - width_abs_centre) / 2,
4329fb66d81Schristos 	    0,
4339fb66d81Schristos 	    width_abs_centre);
434ef36e747Schristos }
435ef36e747Schristos 
436ef36e747Schristos /* Draw format with list on the right. */
437ef36e747Schristos static void
format_draw_right(struct screen_write_ctx * octx,u_int available,u_int ocx,u_int ocy,struct screen * left,struct screen * centre,struct screen * right,struct screen * abs_centre,struct screen * list,struct screen * list_left,struct screen * list_right,struct screen * after,int focus_start,int focus_end,struct format_ranges * frs)438ef36e747Schristos format_draw_right(struct screen_write_ctx *octx, u_int available, u_int ocx,
439ef36e747Schristos     u_int ocy, struct screen *left, struct screen *centre, struct screen *right,
4409fb66d81Schristos     struct screen *abs_centre,     struct screen *list,
4419fb66d81Schristos     struct screen *list_left, struct screen *list_right, struct screen *after,
4429fb66d81Schristos     int focus_start, int focus_end, struct format_ranges *frs)
443ef36e747Schristos {
444ef36e747Schristos 	u_int			width_left, width_centre, width_right;
4459fb66d81Schristos 	u_int			width_list, width_after, width_abs_centre;
446ef36e747Schristos 	struct screen_write_ctx	ctx;
447ef36e747Schristos 
448ef36e747Schristos 	width_left = left->cx;
449ef36e747Schristos 	width_centre = centre->cx;
450ef36e747Schristos 	width_right = right->cx;
4519fb66d81Schristos 	width_abs_centre = abs_centre->cx;
452ef36e747Schristos 	width_list = list->cx;
453ef36e747Schristos 	width_after = after->cx;
454ef36e747Schristos 
455ef36e747Schristos 	/*
456ef36e747Schristos 	 * Trim first the centre, then the list, then the right, then
457ef36e747Schristos 	 * after the list, then the left.
458ef36e747Schristos 	 */
459ef36e747Schristos 	while (width_left +
460ef36e747Schristos 	    width_centre +
461ef36e747Schristos 	    width_right +
462ef36e747Schristos 	    width_list +
463ef36e747Schristos 	    width_after > available) {
464ef36e747Schristos 		if (width_centre > 0)
465ef36e747Schristos 			width_centre--;
466ef36e747Schristos 		else if (width_list > 0)
467ef36e747Schristos 			width_list--;
468ef36e747Schristos 		else if (width_right > 0)
469ef36e747Schristos 			width_right--;
470ef36e747Schristos 		else if (width_after > 0)
471ef36e747Schristos 			width_after--;
472ef36e747Schristos 		else
473ef36e747Schristos 			width_left--;
474ef36e747Schristos 	}
475ef36e747Schristos 
476ef36e747Schristos 	/* If there is no list left, pass off to the no list function. */
477ef36e747Schristos 	if (width_list == 0) {
4789fb66d81Schristos 		screen_write_start(&ctx, right);
479ef36e747Schristos 		screen_write_fast_copy(&ctx, after, 0, 0, width_after, 1);
480ef36e747Schristos 		screen_write_stop(&ctx);
481ef36e747Schristos 
482ef36e747Schristos 		format_draw_none(octx, available, ocx, ocy, left, centre,
4839fb66d81Schristos 		    right, abs_centre, frs);
484ef36e747Schristos 		return;
485ef36e747Schristos 	}
486ef36e747Schristos 
487ef36e747Schristos 	/* Write left at 0. */
488ef36e747Schristos 	format_draw_put(octx, ocx, ocy, left, frs, 0, 0, width_left);
489ef36e747Schristos 
490ef36e747Schristos 	/* Write after at available - width_after. */
491ef36e747Schristos 	format_draw_put(octx, ocx, ocy, after, frs,
492ef36e747Schristos 	    available - width_after,
493ef36e747Schristos 	    after->cx - width_after,
494ef36e747Schristos 	    width_after);
495ef36e747Schristos 
496ef36e747Schristos 	/*
497ef36e747Schristos 	 * Write right at
498ef36e747Schristos 	 *     available - width_right - width_list - width_after.
499ef36e747Schristos 	 */
500ef36e747Schristos 	format_draw_put(octx, ocx, ocy, right, frs,
501ef36e747Schristos 	    available - width_right - width_list - width_after,
502ef36e747Schristos 	    0,
503ef36e747Schristos 	    width_right);
504ef36e747Schristos 
505ef36e747Schristos 	/*
506ef36e747Schristos 	 * Write centre halfway between
507ef36e747Schristos 	 *     width_left
508ef36e747Schristos 	 * and
509ef36e747Schristos 	 *     available - width_right - width_list - width_after.
510ef36e747Schristos 	 */
511ef36e747Schristos 	format_draw_put(octx, ocx, ocy, centre, frs,
512ef36e747Schristos 	    width_left
513ef36e747Schristos 	    + ((available - width_right - width_list - width_after)
514ef36e747Schristos 		- width_left) / 2
515ef36e747Schristos 	    - width_centre / 2,
516ef36e747Schristos 	    centre->cx / 2 - width_centre / 2,
517ef36e747Schristos 	    width_centre);
518ef36e747Schristos 
519ef36e747Schristos 	/*
520ef36e747Schristos 	 * The list now goes from
521ef36e747Schristos 	 *     available - width_list - width_after
522ef36e747Schristos 	 * to
523ef36e747Schristos 	 *     available - width_after
524ef36e747Schristos 	 * If there is no focus given, keep the right in focus.
525ef36e747Schristos 	 */
526ef36e747Schristos 	if (focus_start == -1 || focus_end == -1)
527ef36e747Schristos 		focus_start = focus_end = 0;
528ef36e747Schristos 	format_draw_put_list(octx, ocx, ocy, available - width_list -
529ef36e747Schristos 	    width_after, width_list, list, list_left, list_right, focus_start,
530ef36e747Schristos 	    focus_end, frs);
5319fb66d81Schristos 
5329fb66d81Schristos 	/*
5339fb66d81Schristos 	 * Write abs_centre in the perfect centre of all horizontal space.
5349fb66d81Schristos 	 */
5359fb66d81Schristos 	if (width_abs_centre > available)
5369fb66d81Schristos 		width_abs_centre = available;
5379fb66d81Schristos 	format_draw_put(octx, ocx, ocy, abs_centre, frs,
5389fb66d81Schristos 	    (available - width_abs_centre) / 2,
5399fb66d81Schristos 	    0,
5409fb66d81Schristos 	    width_abs_centre);
5419fb66d81Schristos }
5429fb66d81Schristos 
5439fb66d81Schristos static void
format_draw_absolute_centre(struct screen_write_ctx * octx,u_int available,u_int ocx,u_int ocy,struct screen * left,struct screen * centre,struct screen * right,struct screen * abs_centre,struct screen * list,struct screen * list_left,struct screen * list_right,struct screen * after,int focus_start,int focus_end,struct format_ranges * frs)5449fb66d81Schristos format_draw_absolute_centre(struct screen_write_ctx *octx, u_int available,
5459fb66d81Schristos     u_int ocx, u_int ocy, struct screen *left, struct screen *centre,
5469fb66d81Schristos     struct screen *right, struct screen *abs_centre, struct screen *list,
5479fb66d81Schristos     struct screen *list_left, struct screen *list_right, struct screen *after,
5489fb66d81Schristos     int focus_start, int focus_end, struct format_ranges *frs)
5499fb66d81Schristos {
5509fb66d81Schristos 	u_int	width_left, width_centre, width_right, width_abs_centre;
5519fb66d81Schristos 	u_int	width_list, width_after, middle, abs_centre_offset;
5529fb66d81Schristos 
5539fb66d81Schristos 	width_left = left->cx;
5549fb66d81Schristos 	width_centre = centre->cx;
5559fb66d81Schristos 	width_right = right->cx;
5569fb66d81Schristos 	width_abs_centre = abs_centre->cx;
5579fb66d81Schristos 	width_list = list->cx;
5589fb66d81Schristos 	width_after = after->cx;
5599fb66d81Schristos 
5609fb66d81Schristos 	/*
5619fb66d81Schristos 	 * Trim first centre, then the right, then the left.
5629fb66d81Schristos 	 */
5639fb66d81Schristos 	while (width_left +
5649fb66d81Schristos 	    width_centre +
5659fb66d81Schristos 	    width_right > available) {
5669fb66d81Schristos 		if (width_centre > 0)
5679fb66d81Schristos 			width_centre--;
5689fb66d81Schristos 		else if (width_right > 0)
5699fb66d81Schristos 			width_right--;
5709fb66d81Schristos 		else
5719fb66d81Schristos 			width_left--;
5729fb66d81Schristos 	}
5739fb66d81Schristos 
5749fb66d81Schristos 	/*
5759fb66d81Schristos 	 * We trim list after and abs_centre independently, as we are drawing
5769fb66d81Schristos 	 * them over the rest. Trim first the list, then after the list, then
5779fb66d81Schristos 	 * abs_centre.
5789fb66d81Schristos 	 */
5799fb66d81Schristos 	while (width_list + width_after + width_abs_centre > available) {
5809fb66d81Schristos 		if (width_list > 0)
5819fb66d81Schristos 			width_list--;
5829fb66d81Schristos 		else if (width_after > 0)
5839fb66d81Schristos 			width_after--;
5849fb66d81Schristos 		else
5859fb66d81Schristos 			width_abs_centre--;
5869fb66d81Schristos 	}
5879fb66d81Schristos 
5889fb66d81Schristos 	/* Write left at 0. */
5899fb66d81Schristos 	format_draw_put(octx, ocx, ocy, left, frs, 0, 0, width_left);
5909fb66d81Schristos 
5919fb66d81Schristos 	/* Write right at available - width_right. */
5929fb66d81Schristos 	format_draw_put(octx, ocx, ocy, right, frs,
5939fb66d81Schristos 	    available - width_right,
5949fb66d81Schristos 	    right->cx - width_right,
5959fb66d81Schristos 	    width_right);
5969fb66d81Schristos 
5979fb66d81Schristos 	/*
5989fb66d81Schristos 	 * Keep writing centre at the relative centre. Only the list is written
5999fb66d81Schristos 	 * in the absolute centre of the horizontal space.
6009fb66d81Schristos 	 */
6019fb66d81Schristos 	middle = (width_left + ((available - width_right) - width_left) / 2);
6029fb66d81Schristos 
6039fb66d81Schristos 	/*
6049fb66d81Schristos 	 * Write centre at
6059fb66d81Schristos 	 *     middle - width_centre.
6069fb66d81Schristos 	 */
6079fb66d81Schristos 	format_draw_put(octx, ocx, ocy, centre, frs,
6089fb66d81Schristos 		middle - width_centre,
6099fb66d81Schristos 		0,
6109fb66d81Schristos 		width_centre);
6119fb66d81Schristos 
6129fb66d81Schristos 	/*
6139fb66d81Schristos 	 * If there is no focus given, keep the centre in focus.
6149fb66d81Schristos 	 */
6159fb66d81Schristos 	if (focus_start == -1 || focus_end == -1)
6169fb66d81Schristos 		focus_start = focus_end = list->cx / 2;
6179fb66d81Schristos 
6189fb66d81Schristos 	/*
6199fb66d81Schristos 	 * We centre abs_centre and the list together, so their shared centre is
6209fb66d81Schristos 	 * in the perfect centre of horizontal space.
6219fb66d81Schristos 	 */
6229fb66d81Schristos 	abs_centre_offset = (available - width_list - width_abs_centre) / 2;
6239fb66d81Schristos 
6249fb66d81Schristos 	/*
6259fb66d81Schristos 	 * Write abs_centre before the list.
6269fb66d81Schristos 	 */
6279fb66d81Schristos 	format_draw_put(octx, ocx, ocy, abs_centre, frs, abs_centre_offset,
6289fb66d81Schristos 	    0, width_abs_centre);
6299fb66d81Schristos 	abs_centre_offset += width_abs_centre;
6309fb66d81Schristos 
6319fb66d81Schristos 	/*
6329fb66d81Schristos 	 * Draw the list in the absolute centre
6339fb66d81Schristos 	 */
6349fb66d81Schristos 	format_draw_put_list(octx, ocx, ocy, abs_centre_offset, width_list,
6359fb66d81Schristos 	    list, list_left, list_right, focus_start, focus_end, frs);
6369fb66d81Schristos 	abs_centre_offset += width_list;
6379fb66d81Schristos 
6389fb66d81Schristos 	/*
6399fb66d81Schristos 	 * Write after at the end of the centre
6409fb66d81Schristos 	 */
6419fb66d81Schristos 	format_draw_put(octx, ocx, ocy, after, frs, abs_centre_offset, 0,
6429fb66d81Schristos 	    width_after);
6439fb66d81Schristos }
6449fb66d81Schristos 
6456db26757Swiz /* Get width and count of any leading #s. */
6466db26757Swiz static const char *
format_leading_hashes(const char * cp,u_int * n,u_int * width)6476db26757Swiz format_leading_hashes(const char *cp, u_int *n, u_int *width)
6486db26757Swiz {
6496db26757Swiz 	for (*n = 0; cp[*n] == '#'; (*n)++)
6506db26757Swiz 		/* nothing */;
6516db26757Swiz 	if (*n == 0) {
6526db26757Swiz 		*width = 0;
6536db26757Swiz 		return (cp);
6546db26757Swiz 	}
6556db26757Swiz 	if (cp[*n] != '[') {
6566db26757Swiz 		if ((*n % 2) == 0)
6576db26757Swiz 			*width = (*n / 2);
6586db26757Swiz 		else
6596db26757Swiz 			*width = (*n / 2) + 1;
6606db26757Swiz 		return (cp + *n);
6616db26757Swiz 	}
6626db26757Swiz 	*width = (*n / 2);
6636db26757Swiz 	if ((*n % 2) == 0) {
6646db26757Swiz 		/*
6656db26757Swiz 		 * An even number of #s means that all #s are escaped, so not a
6666db26757Swiz 		 * style. The caller should not skip this. Return pointing to
6676db26757Swiz 		 * the [.
6686db26757Swiz 		 */
6696db26757Swiz 		return (cp + *n);
6706db26757Swiz 	}
6716db26757Swiz 	/* This is a style, so return pointing to the #. */
6726db26757Swiz 	return (cp + *n - 1);
6736db26757Swiz }
6746db26757Swiz 
6759fb66d81Schristos /* Draw multiple characters. */
6769fb66d81Schristos static void
format_draw_many(struct screen_write_ctx * ctx,struct style * sy,char ch,u_int n)6779fb66d81Schristos format_draw_many(struct screen_write_ctx *ctx, struct style *sy, char ch,
6789fb66d81Schristos     u_int n)
6799fb66d81Schristos {
6809fb66d81Schristos 	u_int	i;
6819fb66d81Schristos 
6829fb66d81Schristos 	utf8_set(&sy->gc.data, ch);
6839fb66d81Schristos 	for (i = 0; i < n; i++)
6849fb66d81Schristos 		screen_write_cell(ctx, &sy->gc);
685ef36e747Schristos }
686ef36e747Schristos 
687ef36e747Schristos /* Draw a format to a screen. */
688ef36e747Schristos void
format_draw(struct screen_write_ctx * octx,const struct grid_cell * base,u_int available,const char * expanded,struct style_ranges * srs,int default_colours)689ef36e747Schristos format_draw(struct screen_write_ctx *octx, const struct grid_cell *base,
6906db26757Swiz     u_int available, const char *expanded, struct style_ranges *srs,
6916db26757Swiz     int default_colours)
692ef36e747Schristos {
693ef36e747Schristos 	enum { LEFT,
694ef36e747Schristos 	       CENTRE,
695ef36e747Schristos 	       RIGHT,
6969fb66d81Schristos 	       ABSOLUTE_CENTRE,
697ef36e747Schristos 	       LIST,
698ef36e747Schristos 	       LIST_LEFT,
699ef36e747Schristos 	       LIST_RIGHT,
700ef36e747Schristos 	       AFTER,
701ef36e747Schristos 	       TOTAL } current = LEFT, last = LEFT;
702ef36e747Schristos 	const char	        *names[] = { "LEFT",
703ef36e747Schristos 					     "CENTRE",
704ef36e747Schristos 					     "RIGHT",
7059fb66d81Schristos 					     "ABSOLUTE_CENTRE",
706ef36e747Schristos 					     "LIST",
707ef36e747Schristos 					     "LIST_LEFT",
708ef36e747Schristos 					     "LIST_RIGHT",
709ef36e747Schristos 					     "AFTER" };
710ef36e747Schristos 	size_t			 size = strlen(expanded);
711ef36e747Schristos 	struct screen		*os = octx->s, s[TOTAL];
712ef36e747Schristos 	struct screen_write_ctx	 ctx[TOTAL];
7139fb66d81Schristos 	u_int			 ocx = os->cx, ocy = os->cy, n, i, width[TOTAL];
7149fb66d81Schristos 	u_int			 map[] = { LEFT,
7159fb66d81Schristos 					   LEFT,
7169fb66d81Schristos 					   CENTRE,
7179fb66d81Schristos 					   RIGHT,
7189fb66d81Schristos 					   ABSOLUTE_CENTRE };
719ef36e747Schristos 	int			 focus_start = -1, focus_end = -1;
7209fb66d81Schristos 	int			 list_state = -1, fill = -1, even;
721ef36e747Schristos 	enum style_align	 list_align = STYLE_ALIGN_DEFAULT;
722aa83ff61Schristos 	struct grid_cell	 gc, current_default;
723aa83ff61Schristos 	struct style		 sy, saved_sy;
724ef36e747Schristos 	struct utf8_data	*ud = &sy.gc.data;
725ef36e747Schristos 	const char		*cp, *end;
726ef36e747Schristos 	enum utf8_state		 more;
727ef36e747Schristos 	char			*tmp;
728ef36e747Schristos 	struct format_range	*fr = NULL, *fr1;
729ef36e747Schristos 	struct format_ranges	 frs;
730ef36e747Schristos 	struct style_range	*sr;
731ef36e747Schristos 
732aa83ff61Schristos 	memcpy(&current_default, base, sizeof current_default);
733aa83ff61Schristos 	style_set(&sy, &current_default);
734ef36e747Schristos 	TAILQ_INIT(&frs);
735ef36e747Schristos 	log_debug("%s: %s", __func__, expanded);
736ef36e747Schristos 
737ef36e747Schristos 	/*
738ef36e747Schristos 	 * We build three screens for left, right, centre alignment, one for
739ef36e747Schristos 	 * the list, one for anything after the list and two for the list left
740ef36e747Schristos 	 * and right markers.
741ef36e747Schristos 	 */
742ef36e747Schristos 	for (i = 0; i < TOTAL; i++) {
743ef36e747Schristos 		screen_init(&s[i], size, 1, 0);
7449fb66d81Schristos 		screen_write_start(&ctx[i], &s[i]);
745aa83ff61Schristos 		screen_write_clearendofline(&ctx[i], current_default.bg);
746ef36e747Schristos 		width[i] = 0;
747ef36e747Schristos 	}
748ef36e747Schristos 
749ef36e747Schristos 	/*
750ef36e747Schristos 	 * Walk the string and add to the corresponding screens,
751ef36e747Schristos 	 * parsing styles as we go.
752ef36e747Schristos 	 */
753ef36e747Schristos 	cp = expanded;
754ef36e747Schristos 	while (*cp != '\0') {
7559fb66d81Schristos 		/* Handle sequences of #. */
7569fb66d81Schristos 		if (cp[0] == '#' && cp[1] != '[' && cp[1] != '\0') {
7579fb66d81Schristos 			for (n = 1; cp[n] == '#'; n++)
7589fb66d81Schristos 				 /* nothing */;
7599fb66d81Schristos 			even = ((n % 2) == 0);
7609fb66d81Schristos 			if (cp[n] != '[') {
7619fb66d81Schristos 				cp += n;
7629fb66d81Schristos 				if (even)
7639fb66d81Schristos 					n = (n / 2);
7649fb66d81Schristos 				else
7659fb66d81Schristos 					n = (n / 2) + 1;
7669fb66d81Schristos 				width[current] += n;
7679fb66d81Schristos 				format_draw_many(&ctx[current], &sy, '#', n);
7689fb66d81Schristos 				continue;
7699fb66d81Schristos 			}
7709fb66d81Schristos 			if (even)
7719fb66d81Schristos 				cp += (n + 1);
7729fb66d81Schristos 			else
7739fb66d81Schristos 				cp += (n - 1);
7749fb66d81Schristos 			if (sy.ignore)
7759fb66d81Schristos 				continue;
7769fb66d81Schristos 			format_draw_many(&ctx[current], &sy, '#', n / 2);
7779fb66d81Schristos 			width[current] += (n / 2);
7789fb66d81Schristos 			if (even) {
7799fb66d81Schristos 				utf8_set(ud, '[');
7809fb66d81Schristos 				screen_write_cell(&ctx[current], &sy.gc);
7819fb66d81Schristos 				width[current]++;
7829fb66d81Schristos 			}
7839fb66d81Schristos 			continue;
7849fb66d81Schristos 		}
7859fb66d81Schristos 
7869fb66d81Schristos 		/* Is this not a style? */
7879fb66d81Schristos 		if (cp[0] != '#' || cp[1] != '[' || sy.ignore) {
788ef36e747Schristos 			/* See if this is a UTF-8 character. */
789ef36e747Schristos 			if ((more = utf8_open(ud, *cp)) == UTF8_MORE) {
790ef36e747Schristos 				while (*++cp != '\0' && more == UTF8_MORE)
791ef36e747Schristos 					more = utf8_append(ud, *cp);
792ef36e747Schristos 				if (more != UTF8_DONE)
793ef36e747Schristos 					cp -= ud->have;
794ef36e747Schristos 			}
795ef36e747Schristos 
796ef36e747Schristos 			/* Not a UTF-8 character - ASCII or not valid. */
797ef36e747Schristos 			if (more != UTF8_DONE) {
798ef36e747Schristos 				if (*cp < 0x20 || *cp > 0x7e) {
799ef36e747Schristos 					/* Ignore nonprintable characters. */
800ef36e747Schristos 					cp++;
801ef36e747Schristos 					continue;
802ef36e747Schristos 				}
803ef36e747Schristos 				utf8_set(ud, *cp);
804ef36e747Schristos 				cp++;
805ef36e747Schristos 			}
806ef36e747Schristos 
8076483eba0Schristos 			/* Draw the cell to the current screen. */
808ef36e747Schristos 			screen_write_cell(&ctx[current], &sy.gc);
809ef36e747Schristos 			width[current] += ud->width;
810ef36e747Schristos 			continue;
811ef36e747Schristos 		}
812ef36e747Schristos 
813ef36e747Schristos 		/* This is a style. Work out where the end is and parse it. */
814ef36e747Schristos 		end = format_skip(cp + 2, "]");
815ef36e747Schristos 		if (end == NULL) {
816ef36e747Schristos 			log_debug("%s: no terminating ] at '%s'", __func__,
817ef36e747Schristos 			    cp + 2);
818ef36e747Schristos 			TAILQ_FOREACH_SAFE(fr, &frs, entry, fr1)
819ef36e747Schristos 			    format_free_range(&frs, fr);
820ef36e747Schristos 			goto out;
821ef36e747Schristos 		}
822ef36e747Schristos 		tmp = xstrndup(cp + 2, end - (cp + 2));
823aa83ff61Schristos 		style_copy(&saved_sy, &sy);
824aa83ff61Schristos 		if (style_parse(&sy, &current_default, tmp) != 0) {
825ef36e747Schristos 			log_debug("%s: invalid style '%s'", __func__, tmp);
826ef36e747Schristos 			free(tmp);
827ef36e747Schristos 			cp = end + 1;
828ef36e747Schristos 			continue;
829ef36e747Schristos 		}
830ef36e747Schristos 		log_debug("%s: style '%s' -> '%s'", __func__, tmp,
831ef36e747Schristos 		    style_tostring(&sy));
832ef36e747Schristos 		free(tmp);
8336db26757Swiz 		if (default_colours) {
8346db26757Swiz 			sy.gc.bg = base->bg;
8356db26757Swiz 			sy.gc.fg = base->fg;
8366db26757Swiz 		}
837ef36e747Schristos 
8386483eba0Schristos 		/* If this style has a fill colour, store it for later. */
8396483eba0Schristos 		if (sy.fill != 8)
8406483eba0Schristos 			fill = sy.fill;
8416483eba0Schristos 
842aa83ff61Schristos 		/* If this style pushed or popped the default, update it. */
843aa83ff61Schristos 		if (sy.default_type == STYLE_DEFAULT_PUSH) {
8449fb66d81Schristos 			memcpy(&current_default, &saved_sy.gc,
8459fb66d81Schristos 			    sizeof current_default);
846aa83ff61Schristos 			sy.default_type = STYLE_DEFAULT_BASE;
847aa83ff61Schristos 		} else if (sy.default_type == STYLE_DEFAULT_POP) {
848aa83ff61Schristos 			memcpy(&current_default, base, sizeof current_default);
849aa83ff61Schristos 			sy.default_type = STYLE_DEFAULT_BASE;
850aa83ff61Schristos 		}
851aa83ff61Schristos 
852ef36e747Schristos 		/* Check the list state. */
853ef36e747Schristos 		switch (sy.list) {
854ef36e747Schristos 		case STYLE_LIST_ON:
855ef36e747Schristos 			/*
856ef36e747Schristos 			 * Entering the list, exiting a marker, or exiting the
857ef36e747Schristos 			 * focus.
858ef36e747Schristos 			 */
859ef36e747Schristos 			if (list_state != 0) {
860ef36e747Schristos 				if (fr != NULL) { /* abort any region */
861ef36e747Schristos 					free(fr);
862ef36e747Schristos 					fr = NULL;
863ef36e747Schristos 				}
864ef36e747Schristos 				list_state = 0;
865ef36e747Schristos 				list_align = sy.align;
866ef36e747Schristos 			}
867ef36e747Schristos 
868ef36e747Schristos 			/* End the focus if started. */
869ef36e747Schristos 			if (focus_start != -1 && focus_end == -1)
870ef36e747Schristos 				focus_end = s[LIST].cx;
871ef36e747Schristos 
872ef36e747Schristos 			current = LIST;
873ef36e747Schristos 			break;
874ef36e747Schristos 		case STYLE_LIST_FOCUS:
875ef36e747Schristos 			/* Entering the focus. */
876ef36e747Schristos 			if (list_state != 0) /* not inside the list */
877ef36e747Schristos 				break;
878ef36e747Schristos 			if (focus_start == -1) /* focus already started */
879ef36e747Schristos 				focus_start = s[LIST].cx;
880ef36e747Schristos 			break;
881ef36e747Schristos 		case STYLE_LIST_OFF:
882ef36e747Schristos 			/* Exiting or outside the list. */
883ef36e747Schristos 			if (list_state == 0) {
884ef36e747Schristos 				if (fr != NULL) { /* abort any region */
885ef36e747Schristos 					free(fr);
886ef36e747Schristos 					fr = NULL;
887ef36e747Schristos 				}
888ef36e747Schristos 				if (focus_start != -1 && focus_end == -1)
889ef36e747Schristos 					focus_end = s[LIST].cx;
890ef36e747Schristos 
891ef36e747Schristos 				map[list_align] = AFTER;
892ef36e747Schristos 				if (list_align == STYLE_ALIGN_LEFT)
893ef36e747Schristos 					map[STYLE_ALIGN_DEFAULT] = AFTER;
894ef36e747Schristos 				list_state = 1;
895ef36e747Schristos 			}
896ef36e747Schristos 			current = map[sy.align];
897ef36e747Schristos 			break;
898ef36e747Schristos 		case STYLE_LIST_LEFT_MARKER:
899ef36e747Schristos 			/* Entering left marker. */
900ef36e747Schristos 			if (list_state != 0) /* not inside the list */
901ef36e747Schristos 				break;
902ef36e747Schristos 			if (s[LIST_LEFT].cx != 0) /* already have marker */
903ef36e747Schristos 				break;
904ef36e747Schristos 			if (fr != NULL) { /* abort any region */
905ef36e747Schristos 				free(fr);
906ef36e747Schristos 				fr = NULL;
907ef36e747Schristos 			}
908ef36e747Schristos 			if (focus_start != -1 && focus_end == -1)
909ef36e747Schristos 				focus_start = focus_end = -1;
910ef36e747Schristos 			current = LIST_LEFT;
911ef36e747Schristos 			break;
912ef36e747Schristos 		case STYLE_LIST_RIGHT_MARKER:
913ef36e747Schristos 			/* Entering right marker. */
914ef36e747Schristos 			if (list_state != 0) /* not inside the list */
915ef36e747Schristos 				break;
916ef36e747Schristos 			if (s[LIST_RIGHT].cx != 0) /* already have marker */
917ef36e747Schristos 				break;
918ef36e747Schristos 			if (fr != NULL) { /* abort any region */
919ef36e747Schristos 				free(fr);
920ef36e747Schristos 				fr = NULL;
921ef36e747Schristos 			}
922ef36e747Schristos 			if (focus_start != -1 && focus_end == -1)
923ef36e747Schristos 				focus_start = focus_end = -1;
924ef36e747Schristos 			current = LIST_RIGHT;
925ef36e747Schristos 			break;
926ef36e747Schristos 		}
927ef36e747Schristos 		if (current != last) {
928ef36e747Schristos 			log_debug("%s: change %s -> %s", __func__,
929ef36e747Schristos 			    names[last], names[current]);
930ef36e747Schristos 			last = current;
931ef36e747Schristos 		}
932ef36e747Schristos 
933ef36e747Schristos 		/*
934ef36e747Schristos 		 * Check if the range style has changed and if so end the
935ef36e747Schristos 		 * current range and start a new one if needed.
936ef36e747Schristos 		 */
937ef36e747Schristos 		if (srs != NULL) {
938ef36e747Schristos 			if (fr != NULL && !format_is_type(fr, &sy)) {
939ef36e747Schristos 				if (s[current].cx != fr->start) {
940ef36e747Schristos 					fr->end = s[current].cx + 1;
941ef36e747Schristos 					TAILQ_INSERT_TAIL(&frs, fr, entry);
942ef36e747Schristos 				} else
943ef36e747Schristos 					free(fr);
944ef36e747Schristos 				fr = NULL;
945ef36e747Schristos 			}
946ef36e747Schristos 			if (fr == NULL && sy.range_type != STYLE_RANGE_NONE) {
947ef36e747Schristos 				fr = xcalloc(1, sizeof *fr);
948ef36e747Schristos 				fr->index = current;
949ef36e747Schristos 
950ef36e747Schristos 				fr->s = &s[current];
951ef36e747Schristos 				fr->start = s[current].cx;
952ef36e747Schristos 
953ef36e747Schristos 				fr->type = sy.range_type;
954ef36e747Schristos 				fr->argument = sy.range_argument;
955*c23f9150Swiz 				strlcpy(fr->string, sy.range_string,
956*c23f9150Swiz 				    sizeof fr->string);
957ef36e747Schristos 			}
958ef36e747Schristos 		}
959ef36e747Schristos 
960ef36e747Schristos 		cp = end + 1;
961ef36e747Schristos 	}
962ef36e747Schristos 	free(fr);
963ef36e747Schristos 
964ef36e747Schristos 	for (i = 0; i < TOTAL; i++) {
965ef36e747Schristos 		screen_write_stop(&ctx[i]);
966ef36e747Schristos 		log_debug("%s: width %s is %u", __func__, names[i], width[i]);
967ef36e747Schristos 	}
968ef36e747Schristos 	if (focus_start != -1 && focus_end != -1)
969ef36e747Schristos 		log_debug("%s: focus %d-%d", __func__, focus_start, focus_end);
970ef36e747Schristos 	TAILQ_FOREACH(fr, &frs, entry) {
971ef36e747Schristos 		log_debug("%s: range %d|%u is %s %u-%u", __func__, fr->type,
972ef36e747Schristos 		    fr->argument, names[fr->index], fr->start, fr->end);
973ef36e747Schristos 	}
974ef36e747Schristos 
9756483eba0Schristos 	/* Clear the available area. */
9766483eba0Schristos 	if (fill != -1) {
9776483eba0Schristos 		memcpy(&gc, &grid_default_cell, sizeof gc);
9786483eba0Schristos 		gc.bg = fill;
9796483eba0Schristos 		for (i = 0; i < available; i++)
9806483eba0Schristos 			screen_write_putc(octx, &gc, ' ');
9816483eba0Schristos 	}
9826483eba0Schristos 
983ef36e747Schristos 	/*
984ef36e747Schristos 	 * Draw the screens. How they are arranged depends on where the list
9859fb66d81Schristos 	 * appears.
986ef36e747Schristos 	 */
987ef36e747Schristos 	switch (list_align) {
988ef36e747Schristos 	case STYLE_ALIGN_DEFAULT:
989ef36e747Schristos 		/* No list. */
990ef36e747Schristos 		format_draw_none(octx, available, ocx, ocy, &s[LEFT],
9919fb66d81Schristos 		    &s[CENTRE], &s[RIGHT], &s[ABSOLUTE_CENTRE], &frs);
992ef36e747Schristos 		break;
993ef36e747Schristos 	case STYLE_ALIGN_LEFT:
994ef36e747Schristos 		/* List is part of the left. */
995ef36e747Schristos 		format_draw_left(octx, available, ocx, ocy, &s[LEFT],
9969fb66d81Schristos 		    &s[CENTRE], &s[RIGHT], &s[ABSOLUTE_CENTRE], &s[LIST],
9979fb66d81Schristos 		    &s[LIST_LEFT], &s[LIST_RIGHT], &s[AFTER],
9989fb66d81Schristos 		    focus_start, focus_end, &frs);
999ef36e747Schristos 		break;
1000ef36e747Schristos 	case STYLE_ALIGN_CENTRE:
1001ef36e747Schristos 		/* List is part of the centre. */
1002ef36e747Schristos 		format_draw_centre(octx, available, ocx, ocy, &s[LEFT],
10039fb66d81Schristos 		    &s[CENTRE], &s[RIGHT], &s[ABSOLUTE_CENTRE], &s[LIST],
10049fb66d81Schristos 		    &s[LIST_LEFT], &s[LIST_RIGHT], &s[AFTER],
10059fb66d81Schristos 		    focus_start, focus_end, &frs);
1006ef36e747Schristos 		break;
1007ef36e747Schristos 	case STYLE_ALIGN_RIGHT:
1008ef36e747Schristos 		/* List is part of the right. */
1009ef36e747Schristos 		format_draw_right(octx, available, ocx, ocy, &s[LEFT],
10109fb66d81Schristos 		    &s[CENTRE], &s[RIGHT], &s[ABSOLUTE_CENTRE], &s[LIST],
10119fb66d81Schristos 		    &s[LIST_LEFT], &s[LIST_RIGHT], &s[AFTER],
10129fb66d81Schristos 		    focus_start, focus_end, &frs);
10139fb66d81Schristos 		break;
10149fb66d81Schristos 	case STYLE_ALIGN_ABSOLUTE_CENTRE:
10159fb66d81Schristos 		/* List is in the centre of the entire horizontal space. */
10169fb66d81Schristos 		format_draw_absolute_centre(octx, available, ocx, ocy, &s[LEFT],
10179fb66d81Schristos 		    &s[CENTRE], &s[RIGHT], &s[ABSOLUTE_CENTRE], &s[LIST],
10189fb66d81Schristos 		    &s[LIST_LEFT], &s[LIST_RIGHT], &s[AFTER],
10199fb66d81Schristos 		    focus_start, focus_end, &frs);
1020ef36e747Schristos 		break;
1021ef36e747Schristos 	}
1022ef36e747Schristos 
1023ef36e747Schristos 	/* Create ranges to return. */
1024ef36e747Schristos 	TAILQ_FOREACH_SAFE(fr, &frs, entry, fr1) {
1025ef36e747Schristos 		sr = xcalloc(1, sizeof *sr);
1026ef36e747Schristos 		sr->type = fr->type;
1027ef36e747Schristos 		sr->argument = fr->argument;
1028*c23f9150Swiz 		strlcpy(sr->string, fr->string, sizeof sr->string);
1029ef36e747Schristos 		sr->start = fr->start;
1030ef36e747Schristos 		sr->end = fr->end;
1031ef36e747Schristos 		TAILQ_INSERT_TAIL(srs, sr, entry);
1032ef36e747Schristos 
1033*c23f9150Swiz 		switch (sr->type) {
1034*c23f9150Swiz 		case STYLE_RANGE_NONE:
1035*c23f9150Swiz 			break;
1036*c23f9150Swiz 		case STYLE_RANGE_LEFT:
1037*c23f9150Swiz 			log_debug("%s: range left at %u-%u", __func__,
1038*c23f9150Swiz 			    sr->start, sr->end);
1039*c23f9150Swiz 			break;
1040*c23f9150Swiz 		case STYLE_RANGE_RIGHT:
1041*c23f9150Swiz 			log_debug("%s: range right at %u-%u", __func__,
1042*c23f9150Swiz 			    sr->start, sr->end);
1043*c23f9150Swiz 			break;
1044*c23f9150Swiz 		case STYLE_RANGE_PANE:
1045*c23f9150Swiz 			log_debug("%s: range pane|%%%u at %u-%u", __func__,
1046ef36e747Schristos 			    sr->argument, sr->start, sr->end);
1047*c23f9150Swiz 			break;
1048*c23f9150Swiz 		case STYLE_RANGE_WINDOW:
1049*c23f9150Swiz 			log_debug("%s: range window|%u at %u-%u", __func__,
1050*c23f9150Swiz 			    sr->argument, sr->start, sr->end);
1051*c23f9150Swiz 			break;
1052*c23f9150Swiz 		case STYLE_RANGE_SESSION:
1053*c23f9150Swiz 			log_debug("%s: range session|$%u at %u-%u", __func__,
1054*c23f9150Swiz 			    sr->argument, sr->start, sr->end);
1055*c23f9150Swiz 			break;
1056*c23f9150Swiz 		case STYLE_RANGE_USER:
1057*c23f9150Swiz 			log_debug("%s: range user|%u at %u-%u", __func__,
1058*c23f9150Swiz 			    sr->argument, sr->start, sr->end);
1059*c23f9150Swiz 			break;
1060*c23f9150Swiz 		}
1061ef36e747Schristos 		format_free_range(&frs, fr);
1062ef36e747Schristos 	}
1063ef36e747Schristos 
1064ef36e747Schristos out:
1065ef36e747Schristos 	/* Free the screens. */
1066ef36e747Schristos 	for (i = 0; i < TOTAL; i++)
1067ef36e747Schristos 		screen_free(&s[i]);
1068ef36e747Schristos 
1069ef36e747Schristos 	/* Restore the original cursor position. */
1070ef36e747Schristos 	screen_write_cursormove(octx, ocx, ocy, 0);
1071ef36e747Schristos }
1072ef36e747Schristos 
1073ef36e747Schristos /* Get width, taking #[] into account. */
1074ef36e747Schristos u_int
format_width(const char * expanded)1075ef36e747Schristos format_width(const char *expanded)
1076ef36e747Schristos {
1077ef36e747Schristos 	const char		*cp, *end;
10786db26757Swiz 	u_int			 n, leading_width, width = 0;
1079ef36e747Schristos 	struct utf8_data	 ud;
1080ef36e747Schristos 	enum utf8_state		 more;
1081ef36e747Schristos 
1082ef36e747Schristos 	cp = expanded;
1083ef36e747Schristos 	while (*cp != '\0') {
10849fb66d81Schristos 		if (*cp == '#') {
10856db26757Swiz 			end = format_leading_hashes(cp, &n, &leading_width);
10866db26757Swiz 			width += leading_width;
10876db26757Swiz 			cp = end;
10886db26757Swiz 			if (*cp == '#') {
1089ef36e747Schristos 				end = format_skip(cp + 2, "]");
1090ef36e747Schristos 				if (end == NULL)
10919fb66d81Schristos 					return (0);
1092ef36e747Schristos 				cp = end + 1;
10936db26757Swiz 			}
1094ef36e747Schristos 		} else if ((more = utf8_open(&ud, *cp)) == UTF8_MORE) {
1095ef36e747Schristos 			while (*++cp != '\0' && more == UTF8_MORE)
1096ef36e747Schristos 				more = utf8_append(&ud, *cp);
1097ef36e747Schristos 			if (more == UTF8_DONE)
1098ef36e747Schristos 				width += ud.width;
1099ef36e747Schristos 			else
1100ef36e747Schristos 				cp -= ud.have;
1101ef36e747Schristos 		} else if (*cp > 0x1f && *cp < 0x7f) {
1102ef36e747Schristos 			width++;
1103ef36e747Schristos 			cp++;
11046483eba0Schristos 		} else
11056483eba0Schristos 			cp++;
1106ef36e747Schristos 	}
1107ef36e747Schristos 	return (width);
1108ef36e747Schristos }
1109ef36e747Schristos 
11109fb66d81Schristos /*
11119fb66d81Schristos  * Trim on the left, taking #[] into account.  Note, we copy the whole set of
11129fb66d81Schristos  * unescaped #s, but only add their escaped size to width. This is because the
11139fb66d81Schristos  * format_draw function will actually do the escaping when it runs
11149fb66d81Schristos  */
1115ef36e747Schristos char *
format_trim_left(const char * expanded,u_int limit)1116ef36e747Schristos format_trim_left(const char *expanded, u_int limit)
1117ef36e747Schristos {
1118ef36e747Schristos 	char			*copy, *out;
1119ef36e747Schristos 	const char		*cp = expanded, *end;
11206db26757Swiz 	u_int			 n, width = 0, leading_width;
1121ef36e747Schristos 	struct utf8_data	 ud;
1122ef36e747Schristos 	enum utf8_state		 more;
1123ef36e747Schristos 
1124*c23f9150Swiz 	out = copy = xcalloc(2, strlen(expanded) + 1);
1125ef36e747Schristos 	while (*cp != '\0') {
11269fb66d81Schristos 		if (width >= limit)
11279fb66d81Schristos 			break;
11289fb66d81Schristos 		if (*cp == '#') {
11296db26757Swiz 			end = format_leading_hashes(cp, &n, &leading_width);
11306db26757Swiz 			if (leading_width > limit - width)
11316db26757Swiz 				leading_width = limit - width;
11326db26757Swiz 			if (leading_width != 0) {
11336db26757Swiz 				if (n == 1)
11346db26757Swiz 					*out++ = '#';
11356db26757Swiz 				else {
11366db26757Swiz 					memset(out, '#', 2 * leading_width);
11376db26757Swiz 					out += 2 * leading_width;
11386db26757Swiz 				}
11396db26757Swiz 				width += leading_width;
11406db26757Swiz 			}
11419fb66d81Schristos 			cp = end;
11426db26757Swiz 			if (*cp == '#') {
1143ef36e747Schristos 				end = format_skip(cp + 2, "]");
1144ef36e747Schristos 				if (end == NULL)
1145ef36e747Schristos 					break;
1146ef36e747Schristos 				memcpy(out, cp, end + 1 - cp);
1147ef36e747Schristos 				out += (end + 1 - cp);
1148ef36e747Schristos 				cp = end + 1;
11496db26757Swiz 			}
1150ef36e747Schristos 		} else if ((more = utf8_open(&ud, *cp)) == UTF8_MORE) {
1151ef36e747Schristos 			while (*++cp != '\0' && more == UTF8_MORE)
1152ef36e747Schristos 				more = utf8_append(&ud, *cp);
1153ef36e747Schristos 			if (more == UTF8_DONE) {
1154ef36e747Schristos 				if (width + ud.width <= limit) {
1155ef36e747Schristos 					memcpy(out, ud.data, ud.size);
1156ef36e747Schristos 					out += ud.size;
1157ef36e747Schristos 				}
1158ef36e747Schristos 				width += ud.width;
1159aa83ff61Schristos 			} else {
1160ef36e747Schristos 				cp -= ud.have;
1161aa83ff61Schristos 				cp++;
1162aa83ff61Schristos 			}
1163ef36e747Schristos 		} else if (*cp > 0x1f && *cp < 0x7f) {
1164ef36e747Schristos 			if (width + 1 <= limit)
1165ef36e747Schristos 				*out++ = *cp;
1166ef36e747Schristos 			width++;
1167ef36e747Schristos 			cp++;
1168ef36e747Schristos 		} else
1169ef36e747Schristos 			cp++;
1170ef36e747Schristos 	}
1171ef36e747Schristos 	*out = '\0';
1172ef36e747Schristos 	return (copy);
1173ef36e747Schristos }
1174ef36e747Schristos 
1175ef36e747Schristos /* Trim on the right, taking #[] into account. */
1176ef36e747Schristos char *
format_trim_right(const char * expanded,u_int limit)1177ef36e747Schristos format_trim_right(const char *expanded, u_int limit)
1178ef36e747Schristos {
1179ef36e747Schristos 	char			*copy, *out;
1180ef36e747Schristos 	const char		*cp = expanded, *end;
11816db26757Swiz 	u_int			 width = 0, total_width, skip, n;
11826db26757Swiz 	u_int			 leading_width, copy_width;
1183ef36e747Schristos 	struct utf8_data	 ud;
1184ef36e747Schristos 	enum utf8_state		 more;
1185ef36e747Schristos 
1186ef36e747Schristos 	total_width = format_width(expanded);
1187ef36e747Schristos 	if (total_width <= limit)
1188ef36e747Schristos 		return (xstrdup(expanded));
1189ef36e747Schristos 	skip = total_width - limit;
1190ef36e747Schristos 
1191*c23f9150Swiz 	out = copy = xcalloc(2, strlen(expanded) + 1);
1192ef36e747Schristos 	while (*cp != '\0') {
11939fb66d81Schristos 		if (*cp == '#') {
11946db26757Swiz 			end = format_leading_hashes(cp, &n, &leading_width);
11956db26757Swiz 			copy_width = leading_width;
11969fb66d81Schristos 			if (width <= skip) {
11976db26757Swiz 				if (skip - width >= copy_width)
11986db26757Swiz 					copy_width = 0;
11999fb66d81Schristos 				else
12006db26757Swiz 					copy_width -= (skip - width);
12019fb66d81Schristos 			}
12026db26757Swiz 			if (copy_width != 0) {
12036db26757Swiz 				if (n == 1)
12046db26757Swiz 					*out++ = '#';
12056db26757Swiz 				else {
12066db26757Swiz 					memset(out, '#', 2 * copy_width);
12076db26757Swiz 					out += 2 * copy_width;
12089fb66d81Schristos 				}
12096db26757Swiz 			}
12106db26757Swiz 			width += leading_width;
12119fb66d81Schristos 			cp = end;
12126db26757Swiz 			if (*cp == '#') {
1213ef36e747Schristos 				end = format_skip(cp + 2, "]");
12146db26757Swiz 				if (end == NULL)
1215ef36e747Schristos 					break;
1216ef36e747Schristos 				memcpy(out, cp, end + 1 - cp);
1217ef36e747Schristos 				out += (end + 1 - cp);
1218ef36e747Schristos 				cp = end + 1;
12196db26757Swiz 			}
1220ef36e747Schristos 		} else if ((more = utf8_open(&ud, *cp)) == UTF8_MORE) {
1221ef36e747Schristos 			while (*++cp != '\0' && more == UTF8_MORE)
1222ef36e747Schristos 				more = utf8_append(&ud, *cp);
1223ef36e747Schristos 			if (more == UTF8_DONE) {
1224ef36e747Schristos 				if (width >= skip) {
1225ef36e747Schristos 					memcpy(out, ud.data, ud.size);
1226ef36e747Schristos 					out += ud.size;
1227ef36e747Schristos 				}
1228ef36e747Schristos 				width += ud.width;
1229aa83ff61Schristos 			} else {
1230ef36e747Schristos 				cp -= ud.have;
1231aa83ff61Schristos 				cp++;
1232aa83ff61Schristos 			}
1233ef36e747Schristos 		} else if (*cp > 0x1f && *cp < 0x7f) {
1234ef36e747Schristos 			if (width >= skip)
1235ef36e747Schristos 				*out++ = *cp;
1236ef36e747Schristos 			width++;
1237ef36e747Schristos 			cp++;
1238ef36e747Schristos 		} else
1239ef36e747Schristos 			cp++;
1240ef36e747Schristos 	}
1241ef36e747Schristos 	*out = '\0';
1242ef36e747Schristos 	return (copy);
1243ef36e747Schristos }
1244