util.c - libgrapheme - unicode string library
(HTM) git clone git://git.suckless.org/libgrapheme
(DIR) Log
(DIR) Files
(DIR) Refs
(DIR) README
(DIR) LICENSE
---
util.c (21467B)
---
1 /* See LICENSE file for copyright and license details. */
2 #include <ctype.h>
3 #include <errno.h>
4 #include <inttypes.h>
5 #include <stdbool.h>
6 #include <stddef.h>
7 #include <stdint.h>
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <string.h>
11
12 #include "util.h"
13
14 struct range {
15 uint_least32_t lower;
16 uint_least32_t upper;
17 };
18
19 struct properties_payload {
20 struct properties *prop;
21 const struct property_spec *spec;
22 uint_least8_t speclen;
23 int (*set_value)(struct properties_payload *, uint_least32_t,
24 int_least64_t);
25 uint_least8_t (*handle_conflict)(uint_least32_t, uint_least8_t,
26 uint_least8_t);
27 };
28
29 struct break_test_payload {
30 struct break_test **test;
31 size_t *testlen;
32 };
33
34 static void *
35 reallocate_array(void *p, size_t len, size_t size)
36 {
37 if (len > 0 && size > SIZE_MAX / len) {
38 errno = ENOMEM;
39 return NULL;
40 }
41
42 return realloc(p, len * size);
43 }
44
45 int
46 hextocp(const char *str, size_t len, uint_least32_t *cp)
47 {
48 size_t i;
49 int off;
50 char relative;
51
52 /* the maximum valid codepoint is 0x10FFFF */
53 if (len > 6) {
54 fprintf(stderr, "hextocp: '%.*s' is too long.\n", (int)len,
55 str);
56 return 1;
57 }
58
59 for (i = 0, *cp = 0; i < len; i++) {
60 if (str[i] >= '0' && str[i] <= '9') {
61 relative = '0';
62 off = 0;
63 } else if (str[i] >= 'a' && str[i] <= 'f') {
64 relative = 'a';
65 off = 10;
66 } else if (str[i] >= 'A' && str[i] <= 'F') {
67 relative = 'A';
68 off = 10;
69 } else {
70 fprintf(stderr, "hextocp: '%.*s' is not hexadecimal.\n",
71 (int)len, str);
72 return 1;
73 }
74
75 *cp += ((uint_least32_t)1 << (4 * (len - i - 1))) *
76 (uint_least32_t)(str[i] - relative + off);
77 }
78
79 if (*cp > UINT32_C(0x10FFFF)) {
80 fprintf(stderr, "hextocp: '%.*s' is too large.\n", (int)len,
81 str);
82 return 1;
83 }
84
85 return 0;
86 }
87
88 int
89 parse_cp_list(const char *str, uint_least32_t **cp, size_t *cplen)
90 {
91 size_t count, i;
92 const char *tmp1 = NULL, *tmp2 = NULL;
93
94 if (strlen(str) == 0) {
95 *cp = NULL;
96 *cplen = 0;
97 return 0;
98 }
99
100 /* count the number of spaces in the string and infer list length */
101 for (count = 1, tmp1 = str; (tmp2 = strchr(tmp1, ' ')) != NULL;
102 count++, tmp1 = tmp2 + 1) {
103 ;
104 }
105
106 /* allocate resources */
107 if (!(*cp = calloc((*cplen = count), sizeof(**cp)))) {
108 fprintf(stderr, "calloc: %s\n", strerror(errno));
109 exit(1);
110 }
111
112 /* go through the string again, parsing the numbers */
113 for (i = 0, tmp1 = tmp2 = str; tmp2 != NULL; i++) {
114 tmp2 = strchr(tmp1, ' ');
115 if (hextocp(tmp1, tmp2 ? (size_t)(tmp2 - tmp1) : strlen(tmp1),
116 &((*cp)[i]))) {
117 return 1;
118 }
119 if (tmp2 != NULL) {
120 tmp1 = tmp2 + 1;
121 }
122 }
123
124 return 0;
125 }
126
127 static int
128 range_parse(const char *str, struct range *range)
129 {
130 char *p;
131
132 if ((p = strstr(str, "..")) == NULL) {
133 /* input has the form "XXXXXX" */
134 if (hextocp(str, strlen(str), &range->lower)) {
135 return 1;
136 }
137 range->upper = range->lower;
138 } else {
139 /* input has the form "XXXXXX..XXXXXX" */
140 if (hextocp(str, (size_t)(p - str), &range->lower) ||
141 hextocp(p + 2, strlen(p + 2), &range->upper)) {
142 return 1;
143 }
144 }
145
146 return 0;
147 }
148
149 static bool
150 get_line(char **buf, size_t *bufsize, FILE *fp, size_t *len)
151 {
152 int ret = EOF;
153
154 for (*len = 0;; (*len)++) {
155 if (*len > 0 && *buf != NULL && (*buf)[*len - 1] == '\n') {
156 /*
157 * if the previously read character was a newline,
158 * we fake an end-of-file so we NUL-terminate and
159 * are done.
160 */
161 ret = EOF;
162 } else {
163 ret = fgetc(fp);
164 }
165
166 if (*len >= *bufsize) {
167 /* the buffer needs to be expanded */
168 *bufsize += 512;
169 if ((*buf = realloc(*buf, *bufsize)) == NULL) {
170 fprintf(stderr, "get_line: Out of memory.\n");
171 exit(1);
172 }
173 }
174
175 if (ret != EOF) {
176 (*buf)[*len] = (char)ret;
177 } else {
178 (*buf)[*len] = '\0';
179 break;
180 }
181 }
182
183 return *len == 0 && (feof(fp) || ferror(fp));
184 }
185
186 void
187 parse_file_with_callback(const char *fname,
188 int (*callback)(const char *, char **, size_t, char *,
189 void *),
190 void *payload)
191 {
192 FILE *fp;
193 char *line = NULL, **field = NULL, *comment;
194 size_t linebufsize = 0, i, fieldbufsize = 0, j, nfields, len;
195
196 /* open file */
197 if (!(fp = fopen(fname, "r"))) {
198 fprintf(stderr, "parse_file_with_callback: fopen '%s': %s.\n",
199 fname, strerror(errno));
200 exit(1);
201 }
202
203 while (!get_line(&line, &linebufsize, fp, &len)) {
204 /* remove trailing newline */
205 if (len > 0 && line[len - 1] == '\n') {
206 line[len - 1] = '\0';
207 len--;
208 }
209
210 /* skip empty lines and comment lines */
211 if (len == 0 || line[0] == '#') {
212 continue;
213 }
214
215 /* tokenize line into fields */
216 for (i = 0, nfields = 0, comment = NULL; i < (size_t)len; i++) {
217 /* skip leading whitespace */
218 while (line[i] == ' ') {
219 i++;
220 }
221
222 /* check if we crashed into the comment */
223 if (line[i] != '#') {
224 /* extend field buffer, if necessary */
225 if (++nfields > fieldbufsize) {
226 if ((field = realloc(
227 field,
228 nfields *
229 sizeof(*field))) ==
230 NULL) {
231 fprintf(stderr,
232 "parse_file_with_"
233 "callback: realloc: "
234 "%s.\n",
235 strerror(errno));
236 exit(1);
237 }
238 fieldbufsize = nfields;
239 }
240
241 /* set current position as field start */
242 field[nfields - 1] = &line[i];
243
244 /* continue until we reach ';' or '#' or end */
245 while (line[i] != ';' && line[i] != '#' &&
246 line[i] != '\0') {
247 i++;
248 }
249 }
250
251 if (line[i] == '#') {
252 /* set comment-variable for later */
253 comment = &line[i + 1];
254 }
255
256 /* go back whitespace and terminate field there */
257 if (i > 0) {
258 for (j = i - 1; line[j] == ' '; j--) {
259 ;
260 }
261 line[j + 1] = '\0';
262 } else {
263 line[i] = '\0';
264 }
265
266 /* if comment is set, we are done */
267 if (comment != NULL) {
268 break;
269 }
270 }
271
272 /* skip leading whitespace in comment */
273 while (comment != NULL && comment[0] == ' ') {
274 comment++;
275 }
276
277 /* call callback function */
278 if (callback(fname, field, nfields, comment, payload)) {
279 fprintf(stderr, "parse_file_with_callback: "
280 "Malformed input.\n");
281 exit(1);
282 }
283 }
284
285 /* close file */
286 if (fclose(fp)) {
287 fprintf(stderr, "parse_file_with_callback: fclose '%s': %s.\n",
288 fname, strerror(errno));
289 exit(1);
290 }
291
292 /* cleanup */
293 free(line);
294 free(field);
295 }
296
297 static int
298 properties_callback(const char *file, char **field, size_t nfields,
299 char *comment, void *payload)
300 {
301 /* prop always has the length 0x110000 */
302 struct properties_payload *p = (struct properties_payload *)payload;
303 struct range r;
304 uint_least8_t i;
305 uint_least32_t cp;
306
307 (void)comment;
308
309 if (nfields < 2) {
310 return 1;
311 }
312
313 for (i = 0; i < p->speclen; i++) {
314 /* identify fitting file and identifier */
315 if (p->spec[i].file && !strcmp(p->spec[i].file, file) &&
316 (!strcmp(p->spec[i].ucdname, field[1]) ||
317 (comment != NULL &&
318 !strncmp(p->spec[i].ucdname, comment,
319 strlen(p->spec[i].ucdname)) &&
320 comment[strlen(p->spec[i].ucdname)] == ' ')) &&
321 (p->spec[i].ucdsubname == NULL ||
322 (nfields >= 3 &&
323 !strcmp(p->spec[i].ucdsubname, field[2])))) {
324 /* parse range in first field */
325 if (range_parse(field[0], &r)) {
326 return 1;
327 }
328
329 /* apply to all codepoints in the range */
330 for (cp = r.lower; cp <= r.upper; cp++) {
331 if (p->set_value(payload, cp, i)) {
332 exit(1);
333 }
334 }
335 break;
336 }
337 }
338
339 return 0;
340 }
341
342 void
343 properties_compress(const struct properties *prop,
344 struct properties_compressed *comp)
345 {
346 uint_least32_t cp, i;
347
348 /* initialization */
349 if (!(comp->offset = malloc((size_t)UINT32_C(0x110000) *
350 sizeof(*(comp->offset))))) {
351 fprintf(stderr, "malloc: %s\n", strerror(errno));
352 exit(1);
353 }
354 comp->data = NULL;
355 comp->datalen = 0;
356
357 for (cp = 0; cp < UINT32_C(0x110000); cp++) {
358 for (i = 0; i < comp->datalen; i++) {
359 if (!memcmp(&(prop[cp]), &(comp->data[i]),
360 sizeof(*prop))) {
361 /* found a match! */
362 comp->offset[cp] = i;
363 break;
364 }
365 }
366 if (i == comp->datalen) {
367 /*
368 * found no matching properties-struct, so
369 * add current properties to data and add the
370 * offset in the offset-table
371 */
372 if (!(comp->data = reallocate_array(
373 comp->data, ++(comp->datalen),
374 sizeof(*(comp->data))))) {
375 fprintf(stderr, "reallocate_array: %s\n",
376 strerror(errno));
377 exit(1);
378 }
379 memcpy(&(comp->data[comp->datalen - 1]), &(prop[cp]),
380 sizeof(*prop));
381 comp->offset[cp] = comp->datalen - 1;
382 }
383 }
384 }
385
386 double
387 properties_get_major_minor(const struct properties_compressed *comp,
388 struct properties_major_minor *mm)
389 {
390 size_t i, j, compression_count = 0;
391
392 /*
393 * we currently have an array comp->offset which maps the
394 * codepoints 0..0x110000 to offsets into comp->data.
395 * To improve cache-locality instead and allow a bit of
396 * compressing, instead of directly mapping a codepoint
397 * 0xAAAABB with comp->offset, we generate two arrays major
398 * and minor such that
399 * comp->offset(0xAAAABB) == minor[major[0xAAAA] + 0xBB]
400 * This yields a major-array of length 2^16 and a minor array
401 * of variable length depending on how many common subsequences
402 * can be filtered out.
403 */
404
405 /* initialize */
406 if (!(mm->major = malloc((size_t)0x1100 * sizeof(*(mm->major))))) {
407 fprintf(stderr, "malloc: %s\n", strerror(errno));
408 exit(1);
409 }
410 mm->minor = NULL;
411 mm->minorlen = 0;
412
413 for (i = 0; i < (size_t)0x1100; i++) {
414 /*
415 * we now look at the cp-range (i << 8)..(i << 8 + 0xFF)
416 * and check if its corresponding offset-data already
417 * exists in minor (because then we just point there
418 * and need less storage)
419 */
420 for (j = 0; j + 0xFF < mm->minorlen; j++) {
421 if (!memcmp(&(comp->offset[i << 8]), &(mm->minor[j]),
422 sizeof(*(comp->offset)) * 0x100)) {
423 break;
424 }
425 }
426 if (j + 0xFF < mm->minorlen) {
427 /* found an index */
428 compression_count++;
429 mm->major[i] = j;
430 } else {
431 /*
432 * add "new" sequence to minor and point to it
433 * in major
434 */
435 mm->minorlen += 0x100;
436 if (!(mm->minor =
437 reallocate_array(mm->minor, mm->minorlen,
438 sizeof(*(mm->minor))))) {
439 fprintf(stderr, "reallocate_array: %s\n",
440 strerror(errno));
441 exit(1);
442 }
443 memcpy(&(mm->minor[mm->minorlen - 0x100]),
444 &(comp->offset[i << 8]),
445 sizeof(*(mm->minor)) * 0x100);
446 mm->major[i] = mm->minorlen - 0x100;
447 }
448 }
449
450 /* return compression ratio */
451 return (double)compression_count / 0x1100 * 100;
452 }
453
454 void
455 properties_print_lookup_table(const char *name, const size_t *data,
456 size_t datalen)
457 {
458 const char *type;
459 size_t i, maxval;
460
461 for (i = 0, maxval = 0; i < datalen; i++) {
462 if (data[i] > maxval) {
463 maxval = data[i];
464 }
465 }
466
467 type = (maxval <= UINT_LEAST8_MAX) ? "uint_least8_t" :
468 (maxval <= UINT_LEAST16_MAX) ? "uint_least16_t" :
469 (maxval <= UINT_LEAST32_MAX) ? "uint_least32_t" :
470 "uint_least64_t";
471
472 printf("static const %s %s[] = {\n\t", type, name);
473 for (i = 0; i < datalen; i++) {
474 printf("%zu", data[i]);
475 if (i + 1 == datalen) {
476 printf("\n");
477 } else if ((i + 1) % 8 != 0) {
478 printf(", ");
479 } else {
480 printf(",\n\t");
481 }
482 }
483 printf("};\n");
484 }
485
486 void
487 properties_print_derived_lookup_table(
488 char *name, size_t *offset, size_t offsetlen,
489 int_least64_t (*get_value)(const struct properties *, size_t),
490 const void *payload)
491 {
492 const char *type;
493 size_t i;
494 int_least64_t minval, maxval;
495
496 for (i = 0, minval = INT_LEAST64_MAX, maxval = INT_LEAST64_MIN;
497 i < offsetlen; i++) {
498 if (get_value(payload, offset[i]) > maxval) {
499 maxval = get_value(payload, offset[i]);
500 } else if (get_value(payload, offset[i]) < minval) {
501 minval = get_value(payload, offset[i]);
502 }
503 }
504
505 if (minval < 0) {
506 /* we need a signed type */
507 type = (minval >= INT_LEAST8_MIN && maxval <= INT_LEAST8_MAX) ?
508 "int_least8_t" :
509 (minval >= INT_LEAST16_MIN &&
510 maxval <= INT_LEAST16_MAX) ?
511 "int_least16_t" :
512 (minval >= INT_LEAST32_MIN &&
513 maxval <= INT_LEAST32_MAX) ?
514 "int_least32_t" :
515 "int_least64_t";
516 } else {
517 /* we are fine with an unsigned type */
518 type = (maxval <= UINT_LEAST8_MAX) ? "uint_least8_t" :
519 (maxval <= UINT_LEAST16_MAX) ? "uint_least16_t" :
520 (maxval <= UINT_LEAST32_MAX) ? "uint_least32_t" :
521 "uint_least64_t";
522 }
523
524 printf("static const %s %s[] = {\n\t", type, name);
525 for (i = 0; i < offsetlen; i++) {
526 printf("%" PRIiLEAST64, get_value(payload, offset[i]));
527 if (i + 1 == offsetlen) {
528 printf("\n");
529 } else if ((i + 1) % 8 != 0) {
530 printf(", ");
531 } else {
532 printf(",\n\t");
533 }
534 }
535 printf("};\n");
536 }
537
538 static void
539 properties_print_enum(const struct property_spec *spec, size_t speclen,
540 const char *enumname, const char *enumprefix)
541 {
542 size_t i;
543
544 printf("enum %s {\n", enumname);
545 for (i = 0; i < speclen; i++) {
546 printf("\t%s_%s,\n", enumprefix, spec[i].enumname);
547 }
548 printf("\tNUM_%sS,\n};\n\n", enumprefix);
549 }
550
551 static int
552 set_value_bp(struct properties_payload *payload, uint_least32_t cp,
553 int_least64_t value)
554 {
555 if (payload->prop[cp].property != payload->speclen) {
556 if (payload->handle_conflict == NULL) {
557 fprintf(stderr,
558 "set_value_bp: "
559 "Unhandled character break property "
560 "overwrite for 0x%06X (%s <- %s).\n",
561 cp,
562 payload->spec[payload->prop[cp].property]
563 .enumname,
564 payload->spec[value].enumname);
565 return 1;
566 } else {
567 value = payload->handle_conflict(
568 cp, (uint_least8_t)payload->prop[cp].property,
569 (uint_least8_t)value);
570 }
571 }
572 payload->prop[cp].property = value;
573
574 return 0;
575 }
576
577 static int_least64_t
578 get_value_bp(const struct properties *prop, size_t offset)
579 {
580 return prop[offset].property;
581 }
582
583 void
584 properties_generate_break_property(
585 const struct property_spec *spec, uint_least8_t speclen,
586 uint_least8_t (*fill_missing)(uint_least32_t),
587 uint_least8_t (*handle_conflict)(uint_least32_t, uint_least8_t,
588 uint_least8_t),
589 void (*post_process)(struct properties *), const char *prefix,
590 const char *argv0)
591 {
592 struct properties_compressed comp;
593 struct properties_major_minor mm;
594 struct properties_payload payload;
595 struct properties *prop;
596 size_t i, j, prefixlen = strlen(prefix);
597 char buf1[64], prefix_uc[64], buf2[64], buf3[64], buf4[64];
598
599 /*
600 * allocate property buffer for all 0x110000 codepoints and
601 * initialize its entries to the known invalid value "speclen"
602 */
603 if (!(prop = calloc(UINT32_C(0x110000), sizeof(*prop)))) {
604 fprintf(stderr, "calloc: %s\n", strerror(errno));
605 exit(1);
606 }
607 for (i = 0; i < UINT32_C(0x110000); i++) {
608 prop[i].property = speclen;
609 }
610
611 /* generate data */
612 payload.prop = prop;
613 payload.spec = spec;
614 payload.speclen = speclen;
615 payload.set_value = set_value_bp;
616 payload.handle_conflict = handle_conflict;
617
618 /* parse each file exactly once and ignore NULL-fields */
619 for (i = 0; i < speclen; i++) {
620 for (j = 0; j < i; j++) {
621 if (spec[i].file && spec[j].file &&
622 !strcmp(spec[i].file, spec[j].file)) {
623 /* file has already been parsed */
624 break;
625 }
626 }
627 if (i == j && spec[i].file) {
628 /* file has not been processed yet */
629 parse_file_with_callback(spec[i].file,
630 properties_callback, &payload);
631 }
632 }
633
634 /* fill in the missing properties that weren't explicitly given */
635 for (i = 0; i < UINT32_C(0x110000); i++) {
636 if (payload.prop[i].property == speclen) {
637 if (fill_missing != NULL) {
638 payload.prop[i].property =
639 fill_missing((uint_least32_t)i);
640 } else {
641 payload.prop[i].property = 0;
642 }
643 }
644 }
645
646 /* post-processing */
647 if (post_process != NULL) {
648 post_process(payload.prop);
649 }
650
651 /* compress data */
652 printf("/* Automatically generated by %s */\n#include <stdint.h>\n\n",
653 argv0);
654 properties_compress(prop, &comp);
655
656 fprintf(stderr, "%s: %s-LUT compression-ratio: %.2f%%\n", argv0, prefix,
657 properties_get_major_minor(&comp, &mm));
658
659 /* prepare names */
660 if ((size_t)snprintf(buf1, LEN(buf1), "%s_property", prefix) >=
661 LEN(buf1)) {
662 fprintf(stderr, "snprintf: String truncated.\n");
663 exit(1);
664 }
665 if (LEN(prefix_uc) + 1 < prefixlen) {
666 fprintf(stderr, "snprintf: Buffer too small.\n");
667 exit(1);
668 }
669 for (i = 0; i < prefixlen; i++) {
670 prefix_uc[i] = (char)toupper(prefix[i]);
671 }
672 prefix_uc[prefixlen] = '\0';
673 if ((size_t)snprintf(buf2, LEN(buf2), "%s_PROP", prefix_uc) >=
674 LEN(buf2) ||
675 (size_t)snprintf(buf3, LEN(buf3), "%s_major", prefix) >=
676 LEN(buf3) ||
677 (size_t)snprintf(buf4, LEN(buf4), "%s_minor", prefix) >=
678 LEN(buf4)) {
679 fprintf(stderr, "snprintf: String truncated.\n");
680 exit(1);
681 }
682
683 /* print data */
684 properties_print_enum(spec, speclen, buf1, buf2);
685 properties_print_lookup_table(buf3, mm.major, 0x1100);
686 printf("\n");
687 properties_print_derived_lookup_table(buf4, mm.minor, mm.minorlen,
688 get_value_bp, comp.data);
689
690 /* free data */
691 free(prop);
692 free(comp.data);
693 free(comp.offset);
694 free(mm.major);
695 free(mm.minor);
696 }
697
698 static int
699 break_test_callback(const char *fname, char **field, size_t nfields,
700 char *comment, void *payload)
701 {
702 struct break_test *t,
703 **test = ((struct break_test_payload *)payload)->test;
704 size_t i, *testlen = ((struct break_test_payload *)payload)->testlen,
705 commentlen;
706 char *token;
707
708 (void)fname;
709
710 if (nfields < 1) {
711 return 1;
712 }
713
714 /* append new testcase and initialize with zeroes */
715 if ((*test = realloc(*test, ++(*testlen) * sizeof(**test))) == NULL) {
716 fprintf(stderr, "break_test_callback: realloc: %s.\n",
717 strerror(errno));
718 return 1;
719 }
720 t = &(*test)[*testlen - 1];
721 memset(t, 0, sizeof(*t));
722
723 /* parse testcase "<÷|×> <cp> <÷|×> ... <cp> <÷|×>" */
724 for (token = strtok(field[0], " "), i = 0; token != NULL;
725 i++, token = strtok(NULL, " ")) {
726 if (i % 2 == 0) {
727 /* delimiter or start of sequence */
728 if (i == 0 ||
729 !strncmp(token, "\xC3\xB7", 2)) { /* UTF-8 */
730 /*
731 * '÷' indicates a breakpoint,
732 * the current length is done; allocate
733 * a new length field and set it to 0
734 */
735 if ((t->len = realloc(
736 t->len,
737 ++t->lenlen * sizeof(*t->len))) ==
738 NULL) {
739 fprintf(stderr,
740 "break_test_"
741 "callback: realloc: %s.\n",
742 strerror(errno));
743 return 1;
744 }
745 t->len[t->lenlen - 1] = 0;
746 } else if (!strncmp(token, "\xC3\x97", 2)) { /* UTF-8 */
747 /* '×' indicates a non-breakpoint, do nothing */
748 } else {
749 fprintf(stderr,
750 "break_test_callback: "
751 "Malformed delimiter '%s'.\n",
752 token);
753 return 1;
754 }
755 } else {
756 /* add codepoint to cp-array */
757 if ((t->cp = realloc(t->cp,
758 ++t->cplen * sizeof(*t->cp))) ==
759 NULL) {
760 fprintf(stderr,
761 "break_test_callback: "
762 "realloc: %s.\n",
763 strerror(errno));
764 return 1;
765 }
766 if (hextocp(token, strlen(token),
767 &t->cp[t->cplen - 1])) {
768 return 1;
769 }
770 if (t->lenlen > 0) {
771 t->len[t->lenlen - 1]++;
772 }
773 }
774 }
775 if (t->lenlen > 0 && t->len[t->lenlen - 1] == 0) {
776 /*
777 * we allocated one more length than we needed because
778 * the breakpoint was at the end
779 */
780 t->lenlen--;
781 }
782
783 /* store comment */
784 if (comment != NULL) {
785 commentlen = strlen(comment) + 1;
786 if (((*test)[*testlen - 1].descr = malloc(commentlen)) ==
787 NULL) {
788 fprintf(stderr, "break_test_callback: malloc: %s.\n",
789 strerror(errno));
790 return 1;
791 }
792 memcpy((*test)[*testlen - 1].descr, comment, commentlen);
793 }
794
795 return 0;
796 }
797
798 void
799 break_test_list_parse(char *fname, struct break_test **test, size_t *testlen)
800 {
801 struct break_test_payload pl = {
802 .test = test,
803 .testlen = testlen,
804 };
805 *test = NULL;
806 *testlen = 0;
807
808 parse_file_with_callback(fname, break_test_callback, &pl);
809 }
810
811 void
812 break_test_list_print(const struct break_test *test, size_t testlen,
813 const char *identifier, const char *progname)
814 {
815 size_t i, j;
816
817 printf("/* Automatically generated by %s */\n"
818 "#include <stdint.h>\n#include <stddef.h>\n\n"
819 "#include \"../gen/types.h\"\n\n",
820 progname);
821
822 printf("static const struct break_test %s[] = {\n", identifier);
823 for (i = 0; i < testlen; i++) {
824 printf("\t{\n");
825
826 printf("\t\t.cp = (uint_least32_t[]){");
827 for (j = 0; j < test[i].cplen; j++) {
828 printf(" UINT32_C(0x%06X)", test[i].cp[j]);
829 if (j + 1 < test[i].cplen) {
830 putchar(',');
831 }
832 }
833 printf(" },\n");
834 printf("\t\t.cplen = %zu,\n", test[i].cplen);
835
836 printf("\t\t.len = (size_t[]){");
837 for (j = 0; j < test[i].lenlen; j++) {
838 printf(" %zu", test[i].len[j]);
839 if (j + 1 < test[i].lenlen) {
840 putchar(',');
841 }
842 }
843 printf(" },\n");
844 printf("\t\t.lenlen = %zu,\n", test[i].lenlen);
845
846 printf("\t\t.descr = \"%s\",\n", test[i].descr);
847
848 printf("\t},\n");
849 }
850 printf("};\n");
851 }
852
853 void
854 break_test_list_free(struct break_test *test, size_t testlen)
855 {
856 size_t i;
857
858 for (i = 0; i < testlen; i++) {
859 free(test[i].cp);
860 free(test[i].len);
861 free(test[i].descr);
862 }
863
864 free(test);
865 }