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