Don't die if fail to get root directory, from Ben Boeckel.
[tmux-openbsd.git] / layout-custom.c
blob8a4f08dcf17fea0f233de0fe544e84a12fa62384
1 /* $OpenBSD$ */
3 /*
4 * Copyright (c) 2010 Nicholas Marriott <nicm@users.sourceforge.net>
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
15 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
16 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 #include <sys/types.h>
21 #include <ctype.h>
22 #include <string.h>
24 #include "tmux.h"
26 struct layout_cell *layout_find_bottomright(struct layout_cell *);
27 u_short layout_checksum(const char *);
28 int layout_append(struct layout_cell *, char *, size_t);
29 struct layout_cell *layout_construct(struct layout_cell *, const char **);
30 void layout_assign(struct window_pane **, struct layout_cell *);
32 /* Find the bottom-right cell. */
33 struct layout_cell *
34 layout_find_bottomright(struct layout_cell *lc)
36 if (lc->type == LAYOUT_WINDOWPANE)
37 return (lc);
38 lc = TAILQ_LAST(&lc->cells, layout_cells);
39 return (layout_find_bottomright(lc));
42 /* Calculate layout checksum. */
43 u_short
44 layout_checksum(const char *layout)
46 u_short csum;
48 csum = 0;
49 for (; *layout != '\0'; layout++) {
50 csum = (csum >> 1) + ((csum & 1) << 15);
51 csum += *layout;
53 return (csum);
56 /* Dump layout as a string. */
57 char *
58 layout_dump(struct window *w)
60 char layout[BUFSIZ], *out;
62 *layout = '\0';
63 if (layout_append(w->layout_root, layout, sizeof layout) != 0)
64 return (NULL);
66 xasprintf(&out, "%4x,%s", layout_checksum(layout), layout);
67 return (out);
70 /* Append information for a single cell. */
71 int
72 layout_append(struct layout_cell *lc, char *buf, size_t len)
74 struct layout_cell *lcchild;
75 char tmp[64];
76 size_t tmplen;
77 const char *brackets = "][";
79 if (len == 0)
80 return (-1);
82 if (lc->wp != NULL) {
83 tmplen = xsnprintf(tmp, sizeof tmp, "%ux%u,%u,%u,%u",
84 lc->sx, lc->sy, lc->xoff, lc->yoff, lc->wp->id);
85 } else {
86 tmplen = xsnprintf(tmp, sizeof tmp, "%ux%u,%u,%u",
87 lc->sx, lc->sy, lc->xoff, lc->yoff);
89 if (tmplen > (sizeof tmp) - 1)
90 return (-1);
91 if (strlcat(buf, tmp, len) >= len)
92 return (-1);
94 switch (lc->type) {
95 case LAYOUT_LEFTRIGHT:
96 brackets = "}{";
97 /* FALLTHROUGH */
98 case LAYOUT_TOPBOTTOM:
99 if (strlcat(buf, &brackets[1], len) >= len)
100 return (-1);
101 TAILQ_FOREACH(lcchild, &lc->cells, entry) {
102 if (layout_append(lcchild, buf, len) != 0)
103 return (-1);
104 if (strlcat(buf, ",", len) >= len)
105 return (-1);
107 buf[strlen(buf) - 1] = brackets[0];
108 break;
109 case LAYOUT_WINDOWPANE:
110 break;
113 return (0);
116 /* Parse a layout string and arrange window as layout. */
118 layout_parse(struct window *w, const char *layout)
120 struct layout_cell *lc, *lcchild;
121 struct window_pane *wp;
122 u_int npanes, ncells, sx, sy;
123 u_short csum;
125 /* Check validity. */
126 if (sscanf(layout, "%hx,", &csum) != 1)
127 return (-1);
128 layout += 5;
129 if (csum != layout_checksum(layout))
130 return (-1);
132 /* Build the layout. */
133 lc = layout_construct(NULL, &layout);
134 if (lc == NULL)
135 return (-1);
136 if (*layout != '\0')
137 goto fail;
139 /* Check this window will fit into the layout. */
140 for (;;) {
141 npanes = window_count_panes(w);
142 ncells = layout_count_cells(lc);
143 if (npanes > ncells)
144 goto fail;
145 if (npanes == ncells)
146 break;
148 /* Fewer panes than cells - close the bottom right. */
149 lcchild = layout_find_bottomright(lc);
150 layout_destroy_cell(lcchild, &lc);
153 /* Save the old window size and resize to the layout size. */
154 sx = w->sx; sy = w->sy;
155 window_resize(w, lc->sx, lc->sy);
157 /* Destroy the old layout and swap to the new. */
158 layout_free_cell(w->layout_root);
159 w->layout_root = lc;
161 /* Assign the panes into the cells. */
162 wp = TAILQ_FIRST(&w->panes);
163 layout_assign(&wp, lc);
165 /* Update pane offsets and sizes. */
166 layout_fix_offsets(lc);
167 layout_fix_panes(w, lc->sx, lc->sy);
169 /* Then resize the layout back to the original window size. */
170 layout_resize(w, sx, sy);
171 window_resize(w, sx, sy);
173 layout_print_cell(lc, __func__, 0);
175 return (0);
177 fail:
178 layout_free_cell(lc);
179 return (-1);
182 /* Assign panes into cells. */
183 void
184 layout_assign(struct window_pane **wp, struct layout_cell *lc)
186 struct layout_cell *lcchild;
188 switch (lc->type) {
189 case LAYOUT_WINDOWPANE:
190 layout_make_leaf(lc, *wp);
191 *wp = TAILQ_NEXT(*wp, entry);
192 return;
193 case LAYOUT_LEFTRIGHT:
194 case LAYOUT_TOPBOTTOM:
195 TAILQ_FOREACH(lcchild, &lc->cells, entry)
196 layout_assign(wp, lcchild);
197 return;
201 /* Construct a cell from all or part of a layout tree. */
202 struct layout_cell *
203 layout_construct(struct layout_cell *lcparent, const char **layout)
205 struct layout_cell *lc, *lcchild;
206 u_int sx, sy, xoff, yoff;
208 if (!isdigit((u_char) **layout))
209 return (NULL);
210 if (sscanf(*layout, "%ux%u,%u,%u,%*u", &sx, &sy, &xoff, &yoff) != 4 &&
211 sscanf(*layout, "%ux%u,%u,%u", &sx, &sy, &xoff, &yoff) != 4)
212 return (NULL);
214 while (isdigit((u_char) **layout))
215 (*layout)++;
216 if (**layout != 'x')
217 return (NULL);
218 (*layout)++;
219 while (isdigit((u_char) **layout))
220 (*layout)++;
221 if (**layout != ',')
222 return (NULL);
223 (*layout)++;
224 while (isdigit((u_char) **layout))
225 (*layout)++;
226 if (**layout != ',')
227 return (NULL);
228 (*layout)++;
229 while (isdigit((u_char) **layout))
230 (*layout)++;
231 if (**layout == ',') {
232 (*layout)++;
233 while (isdigit((u_char) **layout))
234 (*layout)++;
237 lc = layout_create_cell(lcparent);
238 lc->sx = sx;
239 lc->sy = sy;
240 lc->xoff = xoff;
241 lc->yoff = yoff;
243 switch (**layout) {
244 case ',':
245 case '}':
246 case ']':
247 case '\0':
248 return (lc);
249 case '{':
250 lc->type = LAYOUT_LEFTRIGHT;
251 break;
252 case '[':
253 lc->type = LAYOUT_TOPBOTTOM;
254 break;
255 default:
256 goto fail;
259 do {
260 (*layout)++;
261 lcchild = layout_construct(lc, layout);
262 if (lcchild == NULL)
263 goto fail;
264 TAILQ_INSERT_TAIL(&lc->cells, lcchild, entry);
265 } while (**layout == ',');
267 switch (lc->type) {
268 case LAYOUT_LEFTRIGHT:
269 if (**layout != '}')
270 goto fail;
271 break;
272 case LAYOUT_TOPBOTTOM:
273 if (**layout != ']')
274 goto fail;
275 break;
276 default:
277 goto fail;
279 (*layout)++;
281 return (lc);
283 fail:
284 layout_free_cell(lc);
285 return (NULL);