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(¤t_default, base, sizeof current_default);
733aa83ff61Schristos style_set(&sy, ¤t_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, ¤t_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(¤t_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(¤t_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