1 #include "string_buffer.h"
2 #include "properties.h"
3 #include "wcwidth.h"
4 #include <assert.h>
5 #include <stddef.h>
6 #ifdef FT_HAVE_WCHAR
7 #include <wchar.h>
8 #endif
9 #if defined(FT_HAVE_UTF8)
10 #include "utf8.h"
11 #endif
12
str_iter_width(const char * beg,const char * end)13 static ptrdiff_t str_iter_width(const char *beg, const char *end)
14 {
15 assert(end >= beg);
16 return (end - beg);
17 }
18
19
20 #ifdef FT_HAVE_WCHAR
wcs_iter_width(const wchar_t * beg,const wchar_t * end)21 static ptrdiff_t wcs_iter_width(const wchar_t *beg, const wchar_t *end)
22 {
23 assert(end >= beg);
24 return mk_wcswidth(beg, (size_t)(end - beg));
25 }
26 #endif /* FT_HAVE_WCHAR */
27
28
buf_str_len(const f_string_buffer_t * buf)29 static size_t buf_str_len(const f_string_buffer_t *buf)
30 {
31 assert(buf);
32
33 switch (buf->type) {
34 case CHAR_BUF:
35 return strlen(buf->str.cstr);
36 #ifdef FT_HAVE_WCHAR
37 case W_CHAR_BUF:
38 return wcslen(buf->str.wstr);
39 #endif
40 #ifdef FT_HAVE_UTF8
41 case UTF8_BUF:
42 return utf8len(buf->str.u8str);
43 #endif
44 }
45
46 assert(0);
47 return 0;
48 }
49
50
51 FT_INTERNAL
strchr_count(const char * str,char ch)52 size_t strchr_count(const char *str, char ch)
53 {
54 if (str == NULL)
55 return 0;
56
57 size_t count = 0;
58 str = strchr(str, ch);
59 while (str) {
60 count++;
61 str++;
62 str = strchr(str, ch);
63 }
64 return count;
65 }
66
67 #ifdef FT_HAVE_WCHAR
68 FT_INTERNAL
wstrchr_count(const wchar_t * str,wchar_t ch)69 size_t wstrchr_count(const wchar_t *str, wchar_t ch)
70 {
71 if (str == NULL)
72 return 0;
73
74 size_t count = 0;
75 str = wcschr(str, ch);
76 while (str) {
77 count++;
78 str++;
79 str = wcschr(str, ch);
80 }
81 return count;
82 }
83 #endif
84
85
86 #if defined(FT_HAVE_UTF8)
87 /* todo: do something with code below!!! */
88 FT_INTERNAL
ut8next(const void * str)89 void *ut8next(const void *str)
90 {
91 utf8_int32_t out_codepoint;
92 return utf8codepoint(str, &out_codepoint);
93 }
94
95 FT_INTERNAL
utf8chr_count(const void * str,utf8_int32_t ch)96 size_t utf8chr_count(const void *str, utf8_int32_t ch)
97 {
98 if (str == NULL)
99 return 0;
100
101 size_t count = 0;
102 str = utf8chr(str, ch);
103 while (str) {
104 count++;
105 str = ut8next(str);
106 str = utf8chr(str, ch);
107 }
108 return count;
109 }
110 #endif /* FT_HAVE_UTF8 */
111
112
113 FT_INTERNAL
str_n_substring_beg(const char * str,char ch_separator,size_t n)114 const char *str_n_substring_beg(const char *str, char ch_separator, size_t n)
115 {
116 if (str == NULL)
117 return NULL;
118
119 if (n == 0)
120 return str;
121
122 str = strchr(str, ch_separator);
123 --n;
124 while (n > 0) {
125 if (str == NULL)
126 return NULL;
127 --n;
128 str++;
129 str = strchr(str, ch_separator);
130 }
131 return str ? (str + 1) : NULL;
132 }
133
134
135 #ifdef FT_HAVE_WCHAR
136 FT_INTERNAL
wstr_n_substring_beg(const wchar_t * str,wchar_t ch_separator,size_t n)137 const wchar_t *wstr_n_substring_beg(const wchar_t *str, wchar_t ch_separator, size_t n)
138 {
139 if (str == NULL)
140 return NULL;
141
142 if (n == 0)
143 return str;
144
145 str = wcschr(str, ch_separator);
146 --n;
147 while (n > 0) {
148 if (str == NULL)
149 return NULL;
150 --n;
151 str++;
152 str = wcschr(str, ch_separator);
153 }
154 return str ? (str + 1) : NULL;
155 }
156 #endif /* FT_HAVE_WCHAR */
157
158 #if defined(FT_HAVE_UTF8)
159 FT_INTERNAL
utf8_n_substring_beg(const void * str,utf8_int32_t ch_separator,size_t n)160 const void *utf8_n_substring_beg(const void *str, utf8_int32_t ch_separator, size_t n)
161 {
162 if (str == NULL)
163 return NULL;
164
165 if (n == 0)
166 return str;
167
168 str = utf8chr(str, ch_separator);
169 --n;
170 while (n > 0) {
171 if (str == NULL)
172 return NULL;
173 --n;
174 str = ut8next(str);
175 str = utf8chr(str, ch_separator);
176 }
177 return str ? (ut8next(str)) : NULL;
178 }
179 #endif
180
181
182 FT_INTERNAL
str_n_substring(const char * str,char ch_separator,size_t n,const char ** begin,const char ** end)183 void str_n_substring(const char *str, char ch_separator, size_t n, const char **begin, const char **end)
184 {
185 const char *beg = str_n_substring_beg(str, ch_separator, n);
186 if (beg == NULL) {
187 *begin = NULL;
188 *end = NULL;
189 return;
190 }
191
192 const char *en = strchr(beg, ch_separator);
193 if (en == NULL) {
194 en = str + strlen(str);
195 }
196
197 *begin = beg;
198 *end = en;
199 return;
200 }
201
202
203 #ifdef FT_HAVE_WCHAR
204 FT_INTERNAL
wstr_n_substring(const wchar_t * str,wchar_t ch_separator,size_t n,const wchar_t ** begin,const wchar_t ** end)205 void wstr_n_substring(const wchar_t *str, wchar_t ch_separator, size_t n, const wchar_t **begin, const wchar_t **end)
206 {
207 const wchar_t *beg = wstr_n_substring_beg(str, ch_separator, n);
208 if (beg == NULL) {
209 *begin = NULL;
210 *end = NULL;
211 return;
212 }
213
214 const wchar_t *en = wcschr(beg, ch_separator);
215 if (en == NULL) {
216 en = str + wcslen(str);
217 }
218
219 *begin = beg;
220 *end = en;
221 return;
222 }
223 #endif /* FT_HAVE_WCHAR */
224
225 #if defined(FT_HAVE_UTF8)
226 FT_INTERNAL
utf8_n_substring(const void * str,utf8_int32_t ch_separator,size_t n,const void ** begin,const void ** end)227 void utf8_n_substring(const void *str, utf8_int32_t ch_separator, size_t n, const void **begin, const void **end)
228 {
229 const char *beg = (const char *)utf8_n_substring_beg(str, ch_separator, n);
230 if (beg == NULL) {
231 *begin = NULL;
232 *end = NULL;
233 return;
234 }
235
236 const char *en = (const char *)utf8chr(beg, ch_separator);
237 if (en == NULL) {
238 en = (const char *)str + strlen((const char *)str);
239 }
240
241 *begin = beg;
242 *end = en;
243 return;
244 }
245 #endif /* FT_HAVE_UTF8 */
246
247
248
249 FT_INTERNAL
create_string_buffer(size_t n_chars,enum f_string_type type)250 f_string_buffer_t *create_string_buffer(size_t n_chars, enum f_string_type type)
251 {
252 size_t char_sz = 0;
253 switch (type) {
254 case CHAR_BUF:
255 char_sz = 1;
256 break;
257 #ifdef FT_HAVE_WCHAR
258 case W_CHAR_BUF:
259 char_sz = sizeof(wchar_t);
260 break;
261 #endif
262 #ifdef FT_HAVE_UTF8
263 case UTF8_BUF:
264 char_sz = 4;
265 break;
266 #endif
267 }
268
269 size_t sz = n_chars * char_sz;
270 f_string_buffer_t *result = (f_string_buffer_t *)F_MALLOC(sizeof(f_string_buffer_t));
271 if (result == NULL)
272 return NULL;
273 result->str.data = F_MALLOC(sz);
274 if (result->str.data == NULL) {
275 F_FREE(result);
276 return NULL;
277 }
278 result->data_sz = sz;
279 result->type = type;
280
281 if (sz) {
282 switch (type) {
283 case CHAR_BUF:
284 result->str.cstr[0] = '\0';
285 break;
286 #ifdef FT_HAVE_WCHAR
287 case W_CHAR_BUF:
288 result->str.wstr[0] = L'\0';
289 break;
290 #endif
291 #ifdef FT_HAVE_UTF8
292 case UTF8_BUF:
293 result->str.cstr[0] = '\0';
294 break;
295 #endif
296 }
297 }
298
299 return result;
300 }
301
302
303 FT_INTERNAL
destroy_string_buffer(f_string_buffer_t * buffer)304 void destroy_string_buffer(f_string_buffer_t *buffer)
305 {
306 if (buffer == NULL)
307 return;
308 F_FREE(buffer->str.data);
309 buffer->str.data = NULL;
310 F_FREE(buffer);
311 }
312
313 FT_INTERNAL
copy_string_buffer(const f_string_buffer_t * buffer)314 f_string_buffer_t *copy_string_buffer(const f_string_buffer_t *buffer)
315 {
316 assert(buffer);
317 f_string_buffer_t *result = create_string_buffer(buffer->data_sz, buffer->type);
318 if (result == NULL)
319 return NULL;
320 switch (buffer->type) {
321 case CHAR_BUF:
322 if (FT_IS_ERROR(fill_buffer_from_string(result, buffer->str.cstr))) {
323 destroy_string_buffer(result);
324 return NULL;
325 }
326 break;
327 #ifdef FT_HAVE_WCHAR
328 case W_CHAR_BUF:
329 if (FT_IS_ERROR(fill_buffer_from_wstring(result, buffer->str.wstr))) {
330 destroy_string_buffer(result);
331 return NULL;
332 }
333 break;
334 #endif /* FT_HAVE_WCHAR */
335 default:
336 destroy_string_buffer(result);
337 return NULL;
338 }
339 return result;
340 }
341
342 FT_INTERNAL
realloc_string_buffer_without_copy(f_string_buffer_t * buffer)343 f_status realloc_string_buffer_without_copy(f_string_buffer_t *buffer)
344 {
345 assert(buffer);
346 char *new_str = (char *)F_MALLOC(buffer->data_sz * 2);
347 if (new_str == NULL) {
348 return FT_MEMORY_ERROR;
349 }
350 F_FREE(buffer->str.data);
351 buffer->str.data = new_str;
352 buffer->data_sz *= 2;
353 return FT_SUCCESS;
354 }
355
356
357 FT_INTERNAL
fill_buffer_from_string(f_string_buffer_t * buffer,const char * str)358 f_status fill_buffer_from_string(f_string_buffer_t *buffer, const char *str)
359 {
360 assert(buffer);
361 assert(str);
362
363 char *copy = F_STRDUP(str);
364 if (copy == NULL)
365 return FT_MEMORY_ERROR;
366
367 F_FREE(buffer->str.data);
368 buffer->str.cstr = copy;
369 buffer->type = CHAR_BUF;
370
371 return FT_SUCCESS;
372 }
373
374
375 #ifdef FT_HAVE_WCHAR
376 FT_INTERNAL
fill_buffer_from_wstring(f_string_buffer_t * buffer,const wchar_t * str)377 f_status fill_buffer_from_wstring(f_string_buffer_t *buffer, const wchar_t *str)
378 {
379 assert(buffer);
380 assert(str);
381
382 wchar_t *copy = F_WCSDUP(str);
383 if (copy == NULL)
384 return FT_MEMORY_ERROR;
385
386 F_FREE(buffer->str.data);
387 buffer->str.wstr = copy;
388 buffer->type = W_CHAR_BUF;
389
390 return FT_SUCCESS;
391 }
392 #endif /* FT_HAVE_WCHAR */
393
394 #ifdef FT_HAVE_UTF8
395 FT_INTERNAL
fill_buffer_from_u8string(f_string_buffer_t * buffer,const void * str)396 f_status fill_buffer_from_u8string(f_string_buffer_t *buffer, const void *str)
397 {
398 assert(buffer);
399 assert(str);
400
401 void *copy = F_UTF8DUP(str);
402 if (copy == NULL)
403 return FT_MEMORY_ERROR;
404
405 F_FREE(buffer->str.u8str);
406 buffer->str.u8str = copy;
407 buffer->type = UTF8_BUF;
408
409 return FT_SUCCESS;
410 }
411 #endif /* FT_HAVE_UTF8 */
412
413 FT_INTERNAL
buffer_text_visible_height(const f_string_buffer_t * buffer)414 size_t buffer_text_visible_height(const f_string_buffer_t *buffer)
415 {
416 if (buffer == NULL || buffer->str.data == NULL || buf_str_len(buffer) == 0) {
417 return 0;
418 }
419 if (buffer->type == CHAR_BUF)
420 return 1 + strchr_count(buffer->str.cstr, '\n');
421 #ifdef FT_HAVE_WCHAR
422 else if (buffer->type == W_CHAR_BUF)
423 return 1 + wstrchr_count(buffer->str.wstr, L'\n');
424 #endif /* FT_HAVE_WCHAR */
425 #ifdef FT_HAVE_UTF8
426 else if (buffer->type == UTF8_BUF)
427 return 1 + utf8chr_count(buffer->str.u8str, '\n');
428 #endif /* FT_HAVE_WCHAR */
429
430 assert(0);
431 return 0;
432 }
433
434 FT_INTERNAL
string_buffer_cod_width_capacity(const f_string_buffer_t * buffer)435 size_t string_buffer_cod_width_capacity(const f_string_buffer_t *buffer)
436 {
437 return string_buffer_width_capacity(buffer);
438 }
439
440 FT_INTERNAL
string_buffer_raw_capacity(const f_string_buffer_t * buffer)441 size_t string_buffer_raw_capacity(const f_string_buffer_t *buffer)
442 {
443 return buffer->data_sz;
444 }
445
446 #ifdef FT_HAVE_UTF8
447 /* User provided function to compute utf8 string visible width */
448 static int (*_custom_u8strwid)(const void *beg, const void *end, size_t *width) = NULL;
449
450 FT_INTERNAL
buffer_set_u8strwid_func(int (* u8strwid)(const void * beg,const void * end,size_t * width))451 void buffer_set_u8strwid_func(int (*u8strwid)(const void *beg, const void *end, size_t *width))
452 {
453 _custom_u8strwid = u8strwid;
454 }
455
456 static
utf8_width(const void * beg,const void * end)457 size_t utf8_width(const void *beg, const void *end)
458 {
459 if (_custom_u8strwid) {
460 size_t width = 0;
461 if (!_custom_u8strwid(beg, end, &width))
462 return width;
463 }
464
465 size_t sz = (size_t)((const char *)end - (const char *)beg);
466 char *tmp = (char *)F_MALLOC(sizeof(char) * (sz + 1));
467 // @todo: add check to tmp
468 assert(tmp);
469
470 memcpy(tmp, beg, sz);
471 tmp[sz] = '\0';
472 size_t result = utf8width(tmp);
473 F_FREE(tmp);
474 return result;
475 }
476 #endif /* FT_HAVE_WCHAR */
477
478 FT_INTERNAL
buffer_text_visible_width(const f_string_buffer_t * buffer)479 size_t buffer_text_visible_width(const f_string_buffer_t *buffer)
480 {
481 size_t max_length = 0;
482 if (buffer->type == CHAR_BUF) {
483 size_t n = 0;
484 while (1) {
485 const char *beg = NULL;
486 const char *end = NULL;
487 str_n_substring(buffer->str.cstr, '\n', n, &beg, &end);
488 if (beg == NULL || end == NULL)
489 return max_length;
490
491 max_length = MAX(max_length, (size_t)(end - beg));
492 ++n;
493 }
494 #ifdef FT_HAVE_WCHAR
495 } else if (buffer->type == W_CHAR_BUF) {
496 size_t n = 0;
497 while (1) {
498 const wchar_t *beg = NULL;
499 const wchar_t *end = NULL;
500 wstr_n_substring(buffer->str.wstr, L'\n', n, &beg, &end);
501 if (beg == NULL || end == NULL)
502 return max_length;
503
504 int line_width = mk_wcswidth(beg, (size_t)(end - beg));
505 if (line_width < 0) /* For safety */
506 line_width = 0;
507 max_length = MAX(max_length, (size_t)line_width);
508
509 ++n;
510 }
511 #endif /* FT_HAVE_WCHAR */
512 #ifdef FT_HAVE_UTF8
513 } else if (buffer->type == UTF8_BUF) {
514 size_t n = 0;
515 while (1) {
516 const void *beg = NULL;
517 const void *end = NULL;
518 utf8_n_substring(buffer->str.u8str, '\n', n, &beg, &end);
519 if (beg == NULL || end == NULL)
520 return max_length;
521
522 max_length = MAX(max_length, (size_t)utf8_width(beg, end));
523 ++n;
524 }
525 #endif /* FT_HAVE_WCHAR */
526 }
527
528 return max_length; /* shouldn't be here */
529 }
530
531
532 static void
buffer_substring(const f_string_buffer_t * buffer,size_t buffer_row,const void ** begin,const void ** end,ptrdiff_t * str_it_width)533 buffer_substring(const f_string_buffer_t *buffer, size_t buffer_row, const void **begin, const void **end, ptrdiff_t *str_it_width)
534 {
535 switch (buffer->type) {
536 case CHAR_BUF:
537 str_n_substring(buffer->str.cstr, '\n', buffer_row, (const char **)begin, (const char **)end);
538 if ((*(const char **)begin) && (*(const char **)end))
539 *str_it_width = str_iter_width(*(const char **)begin, *(const char **)end);
540 break;
541 #ifdef FT_HAVE_WCHAR
542 case W_CHAR_BUF:
543 wstr_n_substring(buffer->str.wstr, L'\n', buffer_row, (const wchar_t **)begin, (const wchar_t **)end);
544 if ((*(const wchar_t **)begin) && (*(const wchar_t **)end))
545 *str_it_width = wcs_iter_width(*(const wchar_t **)begin, *(const wchar_t **)end);
546 break;
547 #endif /* FT_HAVE_WCHAR */
548 #ifdef FT_HAVE_UTF8
549 case UTF8_BUF:
550 utf8_n_substring(buffer->str.u8str, '\n', buffer_row, begin, end);
551 if ((*(const char **)begin) && (*(const char **)end))
552 *str_it_width = utf8_width(*begin, *end);
553 break;
554 #endif /* FT_HAVE_UTF8 */
555 default:
556 assert(0);
557 }
558 }
559
560
561 static int
buffer_print_range(f_conv_context_t * cntx,const void * beg,const void * end)562 buffer_print_range(f_conv_context_t *cntx, const void *beg, const void *end)
563 {
564 size_t len;
565 switch (cntx->b_type) {
566 case CHAR_BUF:
567 len = (size_t)((const char *)end - (const char *)beg);
568 return ft_nprint(cntx, (const char *)beg, len);
569 #ifdef FT_HAVE_WCHAR
570 case W_CHAR_BUF:
571 len = (size_t)((const wchar_t *)end - (const wchar_t *)beg);
572 return ft_nwprint(cntx, (const wchar_t *)beg, len);
573 #endif /* FT_HAVE_WCHAR */
574 #ifdef FT_HAVE_UTF8
575 case UTF8_BUF:
576 return ft_nu8print(cntx, beg, end);
577 #endif /* FT_HAVE_UTF8 */
578 default:
579 assert(0);
580 return -1;
581 }
582 }
583
584
585 FT_INTERNAL
buffer_printf(f_string_buffer_t * buffer,size_t buffer_row,f_conv_context_t * cntx,size_t vis_width,const char * content_style_tag,const char * reset_content_style_tag)586 int buffer_printf(f_string_buffer_t *buffer, size_t buffer_row, f_conv_context_t *cntx, size_t vis_width,
587 const char *content_style_tag, const char *reset_content_style_tag)
588 {
589 const f_context_t *context = cntx->cntx;
590 f_table_properties_t *props = context->table_properties;
591 size_t row = context->row;
592 size_t column = context->column;
593
594 if (buffer == NULL || buffer->str.data == NULL
595 || buffer_row >= buffer_text_visible_height(buffer)) {
596 return -1;
597 }
598
599 size_t content_width = buffer_text_visible_width(buffer);
600 if (vis_width < content_width)
601 return -1;
602
603 size_t left = 0;
604 size_t right = 0;
605 switch (get_cell_property_hierarchically(props, row, column, FT_CPROP_TEXT_ALIGN)) {
606 case FT_ALIGNED_LEFT:
607 left = 0;
608 right = (vis_width) - content_width;
609 break;
610 case FT_ALIGNED_CENTER:
611 left = ((vis_width) - content_width) / 2;
612 right = ((vis_width) - content_width) - left;
613 break;
614 case FT_ALIGNED_RIGHT:
615 left = (vis_width) - content_width;
616 right = 0;
617 break;
618 default:
619 assert(0);
620 break;
621 }
622
623 size_t written = 0;
624 int tmp = 0;
625 ptrdiff_t str_it_width = 0;
626 const void *beg = NULL;
627 const void *end = NULL;
628 buffer_substring(buffer, buffer_row, &beg, &end, &str_it_width);
629 if (beg == NULL || end == NULL)
630 return -1;
631 if (str_it_width < 0 || content_width < (size_t)str_it_width)
632 return -1;
633
634 size_t padding = content_width - (size_t)str_it_width;
635
636 CHCK_RSLT_ADD_TO_WRITTEN(print_n_strings(cntx, left, FT_SPACE));
637 CHCK_RSLT_ADD_TO_WRITTEN(print_n_strings(cntx, 1, content_style_tag));
638 CHCK_RSLT_ADD_TO_WRITTEN(buffer_print_range(cntx, beg, end));
639 CHCK_RSLT_ADD_TO_WRITTEN(print_n_strings(cntx, 1, reset_content_style_tag));
640 CHCK_RSLT_ADD_TO_WRITTEN(print_n_strings(cntx, padding, FT_SPACE));
641 CHCK_RSLT_ADD_TO_WRITTEN(print_n_strings(cntx, right, FT_SPACE));
642 return (int)written;
643
644 clear:
645 return -1;
646 }
647
648 FT_INTERNAL
string_buffer_width_capacity(const f_string_buffer_t * buffer)649 size_t string_buffer_width_capacity(const f_string_buffer_t *buffer)
650 {
651 assert(buffer);
652 switch (buffer->type) {
653 case CHAR_BUF:
654 return buffer->data_sz;
655 #ifdef FT_HAVE_WCHAR
656 case W_CHAR_BUF:
657 return buffer->data_sz / sizeof(wchar_t);
658 #endif
659 #ifdef FT_HAVE_UTF8
660 case UTF8_BUF:
661 return buffer->data_sz / 4;
662 #endif
663 default:
664 assert(0);
665 return 0;
666 }
667 }
668
669
670 FT_INTERNAL
buffer_get_data(f_string_buffer_t * buffer)671 void *buffer_get_data(f_string_buffer_t *buffer)
672 {
673 assert(buffer);
674 return buffer->str.data;
675 }
676
677 FT_INTERNAL
buffer_check_align(f_string_buffer_t * buffer)678 int buffer_check_align(f_string_buffer_t *buffer)
679 {
680 assert(buffer);
681 assert(buffer->str.data);
682
683 switch (buffer->type) {
684 case CHAR_BUF:
685 return 1;
686 #ifdef FT_HAVE_WCHAR
687 case W_CHAR_BUF:
688 return (((uintptr_t)buffer->str.data) & (sizeof(wchar_t) - 1)) == 0;
689 #endif
690 #ifdef FT_HAVE_UTF8
691 case UTF8_BUF:
692 return 1;
693 #endif
694 default:
695 assert(0);
696 return 0;
697 }
698 }
699