Blame


1 65d9b3ca 2021-03-14 op /*
2 65d9b3ca 2021-03-14 op * Copyright (c) 2021 Omar Polo <op@omarpolo.com>
3 65d9b3ca 2021-03-14 op *
4 65d9b3ca 2021-03-14 op * Permission to use, copy, modify, and distribute this software for any
5 65d9b3ca 2021-03-14 op * purpose with or without fee is hereby granted, provided that the above
6 65d9b3ca 2021-03-14 op * copyright notice and this permission notice appear in all copies.
7 65d9b3ca 2021-03-14 op *
8 65d9b3ca 2021-03-14 op * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 65d9b3ca 2021-03-14 op * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 65d9b3ca 2021-03-14 op * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 65d9b3ca 2021-03-14 op * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 65d9b3ca 2021-03-14 op * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 65d9b3ca 2021-03-14 op * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 65d9b3ca 2021-03-14 op * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 65d9b3ca 2021-03-14 op */
16 65d9b3ca 2021-03-14 op
17 65d9b3ca 2021-03-14 op #include "telescope.h"
18 65d9b3ca 2021-03-14 op
19 65d9b3ca 2021-03-14 op #include <stdlib.h>
20 65d9b3ca 2021-03-14 op #include <string.h>
21 65d9b3ca 2021-03-14 op
22 65d9b3ca 2021-03-14 op /*
23 65d9b3ca 2021-03-14 op * Text wrapping
24 65d9b3ca 2021-03-14 op * =============
25 65d9b3ca 2021-03-14 op *
26 65d9b3ca 2021-03-14 op * There's a simple text wrapping algorithm.
27 65d9b3ca 2021-03-14 op *
28 65d9b3ca 2021-03-14 op * 1. if it's a line in a pre-formatted block:
29 65d9b3ca 2021-03-14 op * a. hard wrap.
30 65d9b3ca 2021-03-14 op * b. repeat
31 65d9b3ca 2021-03-14 op * 2. there is enough room for the next word?
32 65d9b3ca 2021-03-14 op * a. yes: render it
33 65d9b3ca 2021-03-14 op * b. no: break the current line.
34 65d9b3ca 2021-03-14 op * i. while there isn't enough space to draw the current
35 65d9b3ca 2021-03-14 op * word, hard-wrap it
36 65d9b3ca 2021-03-14 op * ii. draw the remainder of the current word (can be the
37 65d9b3ca 2021-03-14 op * the entirely)
38 65d9b3ca 2021-03-14 op * 3. render the spaces after the word
39 65d9b3ca 2021-03-14 op * a. but if there is not enough room break the line and
40 65d9b3ca 2021-03-14 op * forget them
41 65d9b3ca 2021-03-14 op * 4. repeat
42 65d9b3ca 2021-03-14 op *
43 65d9b3ca 2021-03-14 op */
44 65d9b3ca 2021-03-14 op
45 65d9b3ca 2021-03-14 op static int
46 65d9b3ca 2021-03-14 op push_line(struct tab *tab, const struct line *l, const char *buf, size_t len, int cont)
47 65d9b3ca 2021-03-14 op {
48 65d9b3ca 2021-03-14 op struct vline *vl;
49 65d9b3ca 2021-03-14 op
50 65d9b3ca 2021-03-14 op tab->s.line_max++;
51 65d9b3ca 2021-03-14 op
52 65d9b3ca 2021-03-14 op if ((vl = calloc(1, sizeof(*vl))) == NULL)
53 65d9b3ca 2021-03-14 op return 0;
54 65d9b3ca 2021-03-14 op
55 65d9b3ca 2021-03-14 op if (len != 0 && (vl->line = calloc(1, len+1)) == NULL) {
56 65d9b3ca 2021-03-14 op free(vl);
57 65d9b3ca 2021-03-14 op return 0;
58 65d9b3ca 2021-03-14 op }
59 65d9b3ca 2021-03-14 op
60 65d9b3ca 2021-03-14 op vl->parent = l;
61 65d9b3ca 2021-03-14 op if (len != 0)
62 65d9b3ca 2021-03-14 op memcpy(vl->line, buf, len);
63 65d9b3ca 2021-03-14 op vl->flags = cont;
64 65d9b3ca 2021-03-14 op
65 65d9b3ca 2021-03-14 op if (TAILQ_EMPTY(&tab->s.head))
66 65d9b3ca 2021-03-14 op TAILQ_INSERT_HEAD(&tab->s.head, vl, vlines);
67 65d9b3ca 2021-03-14 op else
68 65d9b3ca 2021-03-14 op TAILQ_INSERT_TAIL(&tab->s.head, vl, vlines);
69 65d9b3ca 2021-03-14 op return 1;
70 65d9b3ca 2021-03-14 op }
71 65d9b3ca 2021-03-14 op
72 65d9b3ca 2021-03-14 op /*
73 65d9b3ca 2021-03-14 op * Helper function for wrap_text. Find the end of the current word
74 65d9b3ca 2021-03-14 op * and the end of the separator after the word.
75 65d9b3ca 2021-03-14 op */
76 65d9b3ca 2021-03-14 op static int
77 65d9b3ca 2021-03-14 op word_boundaries(const char *s, const char *sep, const char **endword, const char **endspc)
78 65d9b3ca 2021-03-14 op {
79 65d9b3ca 2021-03-14 op *endword = s;
80 65d9b3ca 2021-03-14 op *endword = s;
81 65d9b3ca 2021-03-14 op
82 65d9b3ca 2021-03-14 op if (*s == '\0')
83 65d9b3ca 2021-03-14 op return 0;
84 65d9b3ca 2021-03-14 op
85 65d9b3ca 2021-03-14 op /* find the end of the current world */
86 65d9b3ca 2021-03-14 op for (; *s != '\0'; ++s) {
87 65d9b3ca 2021-03-14 op if (strchr(sep, *s) != NULL)
88 65d9b3ca 2021-03-14 op break;
89 65d9b3ca 2021-03-14 op }
90 65d9b3ca 2021-03-14 op
91 65d9b3ca 2021-03-14 op *endword = s;
92 65d9b3ca 2021-03-14 op
93 65d9b3ca 2021-03-14 op /* find the end of the separator */
94 65d9b3ca 2021-03-14 op for (; *s != '\0'; ++s) {
95 65d9b3ca 2021-03-14 op if (strchr(sep, *s) == NULL)
96 65d9b3ca 2021-03-14 op break;
97 65d9b3ca 2021-03-14 op }
98 65d9b3ca 2021-03-14 op
99 65d9b3ca 2021-03-14 op *endspc = s;
100 65d9b3ca 2021-03-14 op
101 65d9b3ca 2021-03-14 op return 1;
102 65d9b3ca 2021-03-14 op }
103 65d9b3ca 2021-03-14 op
104 65d9b3ca 2021-03-14 op static inline int
105 65d9b3ca 2021-03-14 op emitline(struct tab *tab, size_t zero, size_t *off, const struct line *l,
106 65d9b3ca 2021-03-14 op const char **line, int *cont)
107 65d9b3ca 2021-03-14 op {
108 65d9b3ca 2021-03-14 op if (!push_line(tab, l, *line, *off - zero, *cont))
109 65d9b3ca 2021-03-14 op return 0;
110 65d9b3ca 2021-03-14 op if (!*cont)
111 65d9b3ca 2021-03-14 op *cont = 1;
112 65d9b3ca 2021-03-14 op *line += *off - zero;
113 65d9b3ca 2021-03-14 op *off = zero;
114 65d9b3ca 2021-03-14 op return 1;
115 65d9b3ca 2021-03-14 op }
116 65d9b3ca 2021-03-14 op
117 65d9b3ca 2021-03-14 op /*
118 65d9b3ca 2021-03-14 op * Build a list of visual line by wrapping the given line, assuming
119 65d9b3ca 2021-03-14 op * that when printed will have a leading prefix prfx.
120 65d9b3ca 2021-03-14 op *
121 65d9b3ca 2021-03-14 op * TODO: it considers each byte one cell on the screen!
122 65d9b3ca 2021-03-14 op */
123 65d9b3ca 2021-03-14 op void
124 65d9b3ca 2021-03-14 op wrap_text(struct tab *tab, const char *prfx, struct line *l, size_t width)
125 65d9b3ca 2021-03-14 op {
126 65d9b3ca 2021-03-14 op size_t zero, off, len, split;
127 65d9b3ca 2021-03-14 op int cont = 0;
128 65d9b3ca 2021-03-14 op const char *endword, *endspc, *line, *linestart;
129 65d9b3ca 2021-03-14 op
130 65d9b3ca 2021-03-14 op zero = strlen(prfx);
131 65d9b3ca 2021-03-14 op off = zero;
132 65d9b3ca 2021-03-14 op line = l->line;
133 65d9b3ca 2021-03-14 op linestart = l->line;
134 65d9b3ca 2021-03-14 op
135 65d9b3ca 2021-03-14 op if (line == NULL) {
136 65d9b3ca 2021-03-14 op push_line(tab, l, NULL, 0, 0);
137 65d9b3ca 2021-03-14 op return;
138 65d9b3ca 2021-03-14 op }
139 65d9b3ca 2021-03-14 op
140 65d9b3ca 2021-03-14 op while (word_boundaries(line, " \t-", &endword, &endspc)) {
141 65d9b3ca 2021-03-14 op len = endword - line;
142 65d9b3ca 2021-03-14 op if (off + len >= width) {
143 65d9b3ca 2021-03-14 op emitline(tab, zero, &off, l, &linestart, &cont);
144 65d9b3ca 2021-03-14 op while (len >= width) {
145 65d9b3ca 2021-03-14 op /* hard wrap */
146 65d9b3ca 2021-03-14 op emitline(tab, zero, &off, l, &linestart, &cont);
147 65d9b3ca 2021-03-14 op len -= width-1;
148 65d9b3ca 2021-03-14 op line += width-1;
149 65d9b3ca 2021-03-14 op }
150 65d9b3ca 2021-03-14 op
151 65d9b3ca 2021-03-14 op if (len != 0)
152 65d9b3ca 2021-03-14 op off += len;
153 65d9b3ca 2021-03-14 op } else
154 65d9b3ca 2021-03-14 op off += len;
155 65d9b3ca 2021-03-14 op
156 65d9b3ca 2021-03-14 op /* print the spaces iff not at bol */
157 65d9b3ca 2021-03-14 op len = endspc - endword;
158 65d9b3ca 2021-03-14 op /* line = endspc; */
159 65d9b3ca 2021-03-14 op if (off != zero) {
160 65d9b3ca 2021-03-14 op if (off + len >= width) {
161 65d9b3ca 2021-03-14 op emitline(tab, zero, &off, l, &linestart, &cont);
162 65d9b3ca 2021-03-14 op linestart = endspc;
163 65d9b3ca 2021-03-14 op } else
164 65d9b3ca 2021-03-14 op off += len;
165 65d9b3ca 2021-03-14 op }
166 65d9b3ca 2021-03-14 op
167 65d9b3ca 2021-03-14 op line = endspc;
168 65d9b3ca 2021-03-14 op }
169 65d9b3ca 2021-03-14 op
170 65d9b3ca 2021-03-14 op emitline(tab, zero, &off, l, &linestart, &cont);
171 65d9b3ca 2021-03-14 op }
172 65d9b3ca 2021-03-14 op
173 65d9b3ca 2021-03-14 op int
174 65d9b3ca 2021-03-14 op hardwrap_text(struct tab *tab, struct line *l, size_t width)
175 65d9b3ca 2021-03-14 op {
176 65d9b3ca 2021-03-14 op size_t off, len;
177 65d9b3ca 2021-03-14 op int cont;
178 65d9b3ca 2021-03-14 op const char *linestart;
179 65d9b3ca 2021-03-14 op
180 166712b4 2021-03-14 op off = 0;
181 166712b4 2021-03-14 op linestart = l->line;
182 166712b4 2021-03-14 op
183 65d9b3ca 2021-03-14 op if (l->line == NULL)
184 65d9b3ca 2021-03-14 op return emitline(tab, 0, &off, l, &linestart, &cont);
185 65d9b3ca 2021-03-14 op
186 166712b4 2021-03-14 op len = strlen(l->line);
187 65d9b3ca 2021-03-14 op
188 65d9b3ca 2021-03-14 op while (len >= width) {
189 65d9b3ca 2021-03-14 op len -= width-1;
190 65d9b3ca 2021-03-14 op off = width-1;
191 65d9b3ca 2021-03-14 op if (!emitline(tab, 0, &off, l, &linestart, &cont))
192 65d9b3ca 2021-03-14 op return 0;
193 65d9b3ca 2021-03-14 op }
194 65d9b3ca 2021-03-14 op
195 65d9b3ca 2021-03-14 op if (len != 0)
196 65d9b3ca 2021-03-14 op return emitline(tab, 0, &len, l, &linestart, &cont);
197 65d9b3ca 2021-03-14 op
198 65d9b3ca 2021-03-14 op return 1;
199 65d9b3ca 2021-03-14 op }