From e4a1686d5ca41ad02672c6530588f94c34a1c678 Mon Sep 17 00:00:00 2001 From: Mattias Andrée Date: Thu, 2 Sep 2021 22:17:14 +0200 Subject: First commit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Mattias Andrée --- .gitignore | 12 ++ LICENSE | 15 ++ Makefile | 45 ++++ common.h | 8 + config.mk | 8 + libnumtext.h | 64 ++++++ libnumtext_num2text.c | 36 ++++ libnumtext_remove_separators.c | 29 +++ swedish.c | 478 +++++++++++++++++++++++++++++++++++++++++ 9 files changed, 695 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 Makefile create mode 100644 common.h create mode 100644 config.mk create mode 100644 libnumtext.h create mode 100644 libnumtext_num2text.c create mode 100644 libnumtext_remove_separators.c create mode 100644 swedish.c diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c9d4253 --- /dev/null +++ b/.gitignore @@ -0,0 +1,12 @@ +*\#* +*~ +*.o +*.a +*.lo +*.su +*.so +*.so.* +*.gch +*.gcov +*.gcno +*.gcda diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..c44b2d9 --- /dev/null +++ b/LICENSE @@ -0,0 +1,15 @@ +ISC License + +© 2021 Mattias Andrée + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..796d0cf --- /dev/null +++ b/Makefile @@ -0,0 +1,45 @@ +.POSIX: + +CONFIGFILE = config.mk +include $(CONFIGFILE) + +OBJ =\ + libnumtext_num2text.o\ + libnumtext_remove_separators.o\ + swedish.o + +HDR =\ + libnumtext.h\ + common.h + +all: libnumtext.a +$(OBJ): $(HDR) +$(OBJ:.o=.lo): $(HDR) + +.c.o: + $(CC) -c -o $@ $< $(CFLAGS) $(CPPFLAGS) + +.c.lo: + $(CC) -fPIC -c -o $@ $< $(CFLAGS) $(CPPFLAGS) + +libnumtext.a: $(OBJ) + @rm -f -- $@ + $(AR) rc $@ $(OBJ) + +install: libnumtext.a + mkdir -p -- "$(DESTDIR)$(PREFIX)/lib" + mkdir -p -- "$(DESTDIR)$(PREFIX)/include" + cp -- libnumtext.a "$(DESTDIR)$(PREFIX)/lib/" + cp -- libnumtext.h "$(DESTDIR)$(PREFIX)/include/" + +uninstall: + -rm -f -- "$(DESTDIR)$(PREFIX)/lib/libnumtext.a" + -rm -f -- "$(DESTDIR)$(PREFIX)/include/libnumtext.h" + +clean: + -rm -f -- *.o *.a *.lo *.su *.so *.so.* *.gch *.gcov *.gcno *.gcda + +.SUFFIXES: +.SUFFIXES: .lo .o .c + +.PHONY: all install uninstall clean diff --git a/common.h b/common.h new file mode 100644 index 0000000..2984213 --- /dev/null +++ b/common.h @@ -0,0 +1,8 @@ +/* See LICENSE file for copyright and license details. */ +#include "libnumtext.h" +#include +#include +#include + + +ssize_t libnumtext_num2text_swedish__(char outbuf[], size_t outbuf_size, const char *num, size_t num_len, uint32_t flags); diff --git a/config.mk b/config.mk new file mode 100644 index 0000000..9b16e1d --- /dev/null +++ b/config.mk @@ -0,0 +1,8 @@ +PREFIX = /usr +MANPREFIX = $(PREFIX)/share/man + +CC = cc + +CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_XOPEN_SOURCE=700 -D_GNU_SOURCE +CFLAGS = -std=c99 -Wall -g +LDFLAGS = diff --git a/libnumtext.h b/libnumtext.h new file mode 100644 index 0000000..21ad059 --- /dev/null +++ b/libnumtext.h @@ -0,0 +1,64 @@ +/* See LICENSE file for copyright and license details. */ +#ifndef LIBNUMTEXT_H +#define LIBNUMTEXT_H + +#include +#include +#include + + +enum libnumtext_language { + LIBNUMTEXT_SWEDISH +#define LIBNUMTEXT_SWEDISH LIBNUMTEXT_SWEDISH +}; + + +#define LIBNUMTEXT_N2T_SWEDISH_CARDINAL UINT32_C(0x00000000) /* ett/en, två, tre, fyra, fem, … */ +#define LIBNUMTEXT_N2T_SWEDISH_ORDINAL UINT32_C(0x00000001) /* första/-e, andra/-e, tredje, … */ + +#define LIBNUMTEXT_N2T_SWEDISH_NUMERATOR UINT32_C(0) +#define LIBNUMTEXT_N2T_SWEDISH_DENOMINATOR UINT32_C(0x00000002) /* hel, halv, tredjedel, … */ + +#define LIBNUMTEXT_N2T_SWEDISH_SINGULAR_FORM UINT32_C(0) +#define LIBNUMTEXT_N2T_SWEDISH_PLURAL_FORM UINT32_C(0x00000004) /* hela, halvor, tredjedelar, … [1] */ + +#define LIBNUMTEXT_N2T_SWEDISH_INDEFINITE_FORM UINT32_C(0) +#define LIBNUMTEXT_N2T_SWEDISH_DEFINITE_FORM UINT32_C(0x00000008) /* halan, halvan, trejdelen, … [1] */ + +#define LIBNUMTEXT_N2T_SWEDISH_COMMON_GENDER UINT32_C(0x00000000) /* ett/första, två/andra, … */ +#define LIBNUMTEXT_N2T_SWEDISH_NEUTER_GENDER UINT32_C(0x00000010) /* en/första, två/andra, … */ +#define LIBNUMTEXT_N2T_SWEDISH_MASCULINE_GENDER UINT32_C(0x00000020) /* en/förste, två/andre, … */ +#define LIBNUMTEXT_N2T_SWEDISH_FEMININE_GENDER UINT32_C(0x00000030) /* en/första, två/andra, … */ + +#define LIBNUMTEXT_N2T_SWEDISH_EXPLICIT_ONE UINT32_C(0x00000000) /* …, ettusen, … */ +#define LIBNUMTEXT_N2T_SWEDISH_IMPLICIT_ONE UINT32_C(0x00000040) /* …, tusen, … */ + +#define LIBNUMTEXT_N2T_SWEDISH_NOT_HYPHENATED UINT32_C(0) +#define LIBNUMTEXT_N2T_SWEDISH_HYPHENATED UINT32_C(0x00000080) /* …, femhundra-trettio-två, … */ + +#define LIBNUMTEXT_N2T_SWEDISH_LOWER_CASE UINT32_C(0) /* …, femhundratrettiotvå, … */ +#define LIBNUMTEXT_N2T_SWEDISH_PASCAL_CASE UINT32_C(0x00000100) /* …, FemhundraTrettioTvå, … */ +#define LIBNUMTEXT_N2T_SWEDISH_UPPER_CASE UINT32_C(0x00000200) /* …, FEMHUNDRATRETTIOTVÅ, … */ +#define LIBNUMTEXT_N2T_SWEDISH_SENTENCE_CASE UINT32_C(0x00000300) /* …, Femhundratrettiotvå, … */ + +#define LIBNUMTEXT_N2T_SWEDISH_NO_HYPHENATION UINT32_C(0) /* …, femhundratrettiotvå, … */ +#define LIBNUMTEXT_N2T_SWEDISH_COMPONENT_HYPHENATION UINT32_C(0x00000400) /* …, fem|hundra|trettio|två, … */ +#define LIBNUMTEXT_N2T_SWEDISH_SYLLABLE_HYPHENATION UINT32_C(0x00000800) /* …, fem|hun|dra|tret|tio|två, … */ +#define LIBNUMTEXT_N2T_SWEDISH_SECONDARY_HYPHENATION UINT32_C(0x00000C00) /* …, fem|hun¦dra|tret¦tio|två, … */ + +#define LIBNUMTEXT_N2T_SWEDISH_REDUCED_TRIPLETS UINT32_C(0) /* …, ettusen, … */ +#define LIBNUMTEXT_N2T_SWEDISH_EXPLICIT_TRIPLETS UINT32_C(0x00001000) /* …, etttusen, … (not acceptable Swedish) */ +#define LIBNUMTEXT_N2T_SWEDISH_LATEX_TRIPLETS UINT32_C(0x00002000) /* …, e"ttusen, … (for use in LaTeX) */ + +/* [1] Requires LIBNUMTEXT_N2T_SWEDISH_ORDINAL (no effect) or LIBNUMTEXT_N2T_SWEDISH_DENOMINATOR */ + + +/* input to libnumtext_num2str may not contain separators */ +ssize_t libnumtext_remove_separators(char outbuf[], size_t outbuf_size, const char *num, size_t num_len, + enum libnumtext_language lang); + +ssize_t libnumtext_num2text(char outbuf[], size_t outbuf_size, const char *num, size_t num_len, + enum libnumtext_language lang, uint32_t flags, ...); + + +#endif diff --git a/libnumtext_num2text.c b/libnumtext_num2text.c new file mode 100644 index 0000000..09605a7 --- /dev/null +++ b/libnumtext_num2text.c @@ -0,0 +1,36 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + +#define UNICODE_MINUS "−" + + +ssize_t +libnumtext_num2text(char outbuf[], size_t outbuf_size, const char *num, size_t num_len, + enum libnumtext_language lang, uint32_t flags, ...) +{ + size_t i; + + i = 0; + if (i < num_len) { + if (num[i] == '+' || num[i] == '-') + i += 1; + else if (!strncmp(&num[0], UNICODE_MINUS, sizeof(UNICODE_MINUS) - 1)) + i += sizeof(UNICODE_MINUS) - 1; + } + if (i == num_len) + goto einval; + for (; i < num_len; i++) + if (!isdigit(num[i])) + goto einval; + + switch (lang) { + + case LIBNUMTEXT_SWEDISH: + return libnumtext_num2text_swedish__(outbuf, outbuf_size, num, num_len, flags); + + default: + einval: + errno = EINVAL; + return -1; + } +} diff --git a/libnumtext_remove_separators.c b/libnumtext_remove_separators.c new file mode 100644 index 0000000..41b14ea --- /dev/null +++ b/libnumtext_remove_separators.c @@ -0,0 +1,29 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +ssize_t +libnumtext_remove_separators(char outbuf[], size_t outbuf_size, const char *num, size_t num_len, enum libnumtext_language lang) +{ + char *p = outbuf; + ssize_t len = 0; + + switch (lang) { + + case LIBNUMTEXT_SWEDISH: + for (; num_len--; num++) { + if (*num != ' ' && *num != '\'' && *num != '.') { + if (outbuf_size) { + *p++ = *num; + outbuf_size--; + } + len += 1; + } + } + return len; + + default: + errno = EINVAL; + return -1; + } +} diff --git a/swedish.c b/swedish.c new file mode 100644 index 0000000..9bfa486 --- /dev/null +++ b/swedish.c @@ -0,0 +1,478 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +static struct digit { + const char *cardinal_common; + const char *cardinal_other; + const char *ordinal_other; + const char *ordinal_masculine; + const char *ordinal_suffix; +} digits[] = { + {"Noll", NULL, "Noll¦te", NULL, NULL}, + {"Ett", "En", "Förs¦ta", "Förs¦te", NULL}, + {"Två", NULL, "An¦dra", "An¦dre", NULL}, + {"Tre", NULL, "Tre¦dje", NULL, NULL}, + {"Fy¦ra", NULL, "Fjär¦de", NULL, NULL}, + {"Fem", NULL, "Fem¦te", NULL, NULL}, + {"Sex", NULL, "Sjät¦te", NULL, NULL}, + {"Sju", NULL, "Sjun¦de", NULL, NULL}, + {"Åt¦ta", NULL, "Åt¦ton", NULL, "¦de"}, + {"Nio", NULL, "Ni¦on", NULL, "¦de"}, + {"Tio", NULL, "Ti¦on", NULL, "¦de"}, + {"El¦va", NULL, "Elf¦te", NULL, NULL}, + {"Tolv", NULL, "Tolf¦te", NULL, NULL}, + {"Tret|ton", NULL, "Tret|ton", NULL, "¦de"}, + {"Fjor|ton", NULL, "Fjor|ton", NULL, "¦de"}, + {"Fem|ton", NULL, "Fem|ton", NULL, "¦de"}, + {"Sex|ton", NULL, "Sex|ton", NULL, "¦de"}, + {"Sjut|ton", NULL, "Sjut|ton", NULL, "¦de"}, + {"Ar|ton", NULL, "Ar|ton", NULL, "¦de"}, + {"Nit|ton", NULL, "Nit|ton", NULL, "¦de"} +}; + +static struct ten { + const char *cardinal; + const char *ordinal; +} tens[] = { + {NULL}, + {NULL}, + {"Tju¦go", "Tju¦gon"}, + {"Tret|tio", "Tret|ti¦on"}, + {"Fyr|tio", "Fyr|ti¦on"}, + {"Fem|tio", "Fem|ti¦on"}, + {"Sex|tio", "Sex|ti¦on"}, + {"Sju|tio", "Sju|ti¦on"}, + {"Åt|tio", "Åt|ti¦on"}, + {"Nit|tio", "Nit|ti¦on"} +}; + +static const char *wholes_and_halves[][5] = { + {"Hel", "He¦la", "Hel¦an", "Hel¦or¦na", "Hel¦te"}, + {"Halv", "Hal¦va", "Halv¦an", "Halv¦or¦na", "Half¦te"} +}; + +static const char *great_suffixes[] = { + "il¦jon", + "il¦jard" +}; + +static const char *greats[][7] = { + {NULL, NULL, NULL, NULL, NULL, NULL, NULL}, + {"||m", "||un", "", "n", "||de¦ci", "nx*", "||cen¦ti"}, + {"||b", "||duo", "", "ms", "||vi|gin¦ti", "n", "||du|cen¦ti"}, + {"||tr", "||tre", "s*", "ns", "||tri|gin¦ta", "ns", "||tre|cen¦ti"}, + {"||kvad¦r", "||kvat¦tour", "", "ns", "||kvad¦ra|gin¦ta", "ns", "||kvad¦rin|gen¦ti"}, + {"||kvin¦t", "||kvin", "", "ns", "||kvin¦kva|gin¦ta", "ns", "||kvin|gen¦ti"}, + {"||sex¦t", "||se", "sx", "n", "||sex¦a|gin¦ta", "n", "||ses|cen¦ti"}, + {"||sep¦t", "||sep¦te", "mn", "n", "||sep¦tua|gin¦ta", "n", "||sep¦tin|gen¦ti"}, + {"||ok¦t", "||ok¦to", "", "mx*", "||ok¦to|gin¦ta", "mx*", "||ok¦tin|gen|ti"}, + {"||no¦n", "||no¦ve", "mn", "", "||no¦na|gin¦ta", "", "||non|gen¦ti"} +}; + + +static void +append(char outbuf[], size_t outbuf_size, size_t *lenp, const char *appendage, uint32_t flags) +{ + char hyphen[sizeof("¦x")], *p; + uint32_t hyphenation = flags & UINT32_C(0x00000C00); + int shift; + + if (appendage[0] == '|' && appendage[1] == '|') { + if (hyphenation == LIBNUMTEXT_N2T_SWEDISH_NO_HYPHENATION) + appendage = &appendage[2]; + else + appendage = &appendage[1]; + } else if (appendage[0] == '|') { + if (hyphenation == LIBNUMTEXT_N2T_SWEDISH_NO_HYPHENATION || + hyphenation == LIBNUMTEXT_N2T_SWEDISH_COMPONENT_HYPHENATION) { + appendage = &appendage[1]; + } + } else if (!strncmp(&appendage[appendage[0] == '<'], "¦", sizeof("¦") - 1)) { + shift = appendage[0] == '<'; + appendage = &appendage[shift]; + appendage = &appendage[sizeof("¦") - 1]; + if (hyphenation == LIBNUMTEXT_N2T_SWEDISH_SECONDARY_HYPHENATION) + p = stpcpy(hyphen, "¦"); + else if (hyphenation == LIBNUMTEXT_N2T_SWEDISH_SYLLABLE_HYPHENATION) + p = stpcpy(hyphen, "|"); + else + *hyphen = 0; + if (*hyphen) { + if (shift && *lenp <= outbuf_size) { + *p++ = outbuf[--*lenp]; + *p = '\0'; + } + for (p = hyphen; *p; p++) { + if (*lenp < outbuf_size) + outbuf[*lenp] = *p; + *lenp += 1; + } + } + } + + if (*lenp >= 2 && outbuf[*lenp - 2] == outbuf[*lenp - 1] && outbuf[*lenp - 1] == appendage[0]) { + if ((flags & UINT32_C(0x00002000)) == LIBNUMTEXT_N2T_SWEDISH_REDUCED_TRIPLETS) + *lenp -= 1; + else if ((flags & UINT32_C(0x00002000)) == LIBNUMTEXT_N2T_SWEDISH_LATEX_TRIPLETS) + outbuf[*lenp - 2] = '"'; + } + + if (*lenp && (flags & (LIBNUMTEXT_N2T_SWEDISH_HYPHENATED | hyphenation))) { + if (isupper(appendage[0]) || (appendage[0] == "Å"[0] && appendage[1] == "Å"[1])) { + if (*lenp < outbuf_size) + outbuf[*lenp] = '-'; + *lenp += 1; + } + } + + for (; *appendage; appendage++) { + if (*lenp < outbuf_size) + outbuf[*lenp] = *appendage; + *lenp += 1; + } +} + + +static void +suffix(char outbuf[], size_t outbuf_size, size_t *lenp, uint32_t flags) +{ + const char *appendage; + + if (flags & LIBNUMTEXT_N2T_SWEDISH_ORDINAL) { + if (flags & LIBNUMTEXT_N2T_SWEDISH_DENOMINATOR) { + appendage = "||delte"; + } else { + return; + } + } else if (flags & LIBNUMTEXT_N2T_SWEDISH_DENOMINATOR) { + if (flags & LIBNUMTEXT_N2T_SWEDISH_PLURAL_FORM) { + if (flags & LIBNUMTEXT_N2T_SWEDISH_DEFINITE_FORM) { + appendage = "||del¦ar¦na"; + } else { + appendage = "||del¦ar"; + } + } else { + if (flags & LIBNUMTEXT_N2T_SWEDISH_DEFINITE_FORM) { + appendage = "||del¦en"; + } else { + appendage = "||del"; + } + } + } else { + return; + } + + append(outbuf, outbuf_size, lenp, appendage, flags); +} + + +ssize_t +libnumtext_num2text_swedish__(char outbuf_[], size_t outbuf_size, const char *num, size_t num_len, uint32_t flags) +{ + char *outbuf = outbuf_; + size_t len = 0; + int first = 1, last; + int hundred_thousands, thousands, orig_thousands, hundreds, ones; + int32_t small_num; + const char *great_1, *great_1_suffix, *great_last; + const char *great_10, *great_10_prefix, *gsuffix; + const char *great_100, *great_100_prefix, *gprefix; + char affix[2] = {[1] = 0}; + size_t great_order, small_order, great_order_suffix; + size_t i, offset = 0; + const char *append_for_ordinal = NULL; + + if ((flags & (uint32_t)~UINT32_C(0x00003FFF)) || + ((flags & UINT32_C(0x00003000)) == UINT32_C(0x00003000))) + goto einval; + if (flags & (LIBNUMTEXT_N2T_SWEDISH_PLURAL_FORM | LIBNUMTEXT_N2T_SWEDISH_DEFINITE_FORM)) + if ((flags & (LIBNUMTEXT_N2T_SWEDISH_ORDINAL | LIBNUMTEXT_N2T_SWEDISH_DENOMINATOR)) == 0) + goto einval; + + if (!isdigit(num[0])) { + append(outbuf, outbuf_size, &len, num[0] == '+' ? "Plus " : "Min¦us ", flags); + offset = len; + if (offset > outbuf_size) + offset = outbuf_size; + outbuf += offset; + outbuf_size -= offset; + num++; + num_len--; + } + + while (num_len > 1 && num[0] == '0') { + num++; + num_len--; + } + + if (num_len == 1) { + if (num[0] == '0') { + append(outbuf, outbuf_size, &len, digits[0].cardinal_common, flags); + suffix(outbuf, outbuf_size, &len, flags); + goto out; + } else if (num[0] <= '2' && (flags & LIBNUMTEXT_N2T_SWEDISH_DENOMINATOR)) { + if (flags & LIBNUMTEXT_N2T_SWEDISH_ORDINAL) + i = 4; + else + i = (size_t)((flags / LIBNUMTEXT_N2T_SWEDISH_PLURAL_FORM) & 3); + append(outbuf, outbuf_size, &len, wholes_and_halves[num[0] - 1][i], flags); + goto out; + } + } + + while (num_len) { + num_len -= 1; + great_order = num_len / 6; + small_order = num_len % 6; + last = !num_len; + + great_order_suffix = 0; + hundred_thousands = thousands = hundreds = ones = 0; + small_num = 0; + + if (great_order && small_order >= 3) { + small_order -= 3; + great_order_suffix = 1; + } + + switch (small_order) { + case 5: + hundred_thousands = *num++ - '0'; + small_num = (int32_t)hundred_thousands; + num_len--; + if (hundred_thousands) { + if (first && hundred_thousands == 1 && (flags & LIBNUMTEXT_N2T_SWEDISH_IMPLICIT_ONE)) { + append(outbuf, outbuf_size, &len, "Hun¦dra", flags); + } else { + append(outbuf, outbuf_size, &len, digits[hundred_thousands].cardinal_common, flags); + append(outbuf, outbuf_size, &len, "||hun¦dra", flags); + } + append_for_ordinal = "¦de"; + first = 0; + } + /* fall through */ + + case 4: + thousands = *num++ - '0'; + orig_thousands = thousands; + num_len--; + if (tens[thousands].cardinal) { + append(outbuf, outbuf_size, &len, tens[thousands].cardinal, flags); + thousands = 0; + first = 0; + } else { + thousands *= 10; + } + /* fall through */ + + case 3: + small_num *= 10; + small_num += (int32_t)(*num - '0'); + thousands += *num++ - '0'; + num_len--; + if (thousands) { + if (first && thousands == 1 && (flags & LIBNUMTEXT_N2T_SWEDISH_IMPLICIT_ONE)) { + append(outbuf, outbuf_size, &len, "Tus¦en", flags); + } else { + append(outbuf, outbuf_size, &len, digits[thousands].cardinal_common, flags); + append(outbuf, outbuf_size, &len, "||tus¦en", flags); + } + append_for_ordinal = "¦de"; + first = 0; + } else if (hundred_thousands || orig_thousands) { + append(outbuf, outbuf_size, &len, "||tus¦en", flags); + append_for_ordinal = "¦de"; + first = 0; + } + /* fall through */ + + case 2: + small_num *= 10; + small_num += (int32_t)(*num - '0'); + hundreds = *num++ - '0'; + num_len--; + if (hundreds) { + if (first && hundred_thousands == 1 && (flags & LIBNUMTEXT_N2T_SWEDISH_IMPLICIT_ONE)) { + append(outbuf, outbuf_size, &len, "Hun¦dra", flags); + } else { + append(outbuf, outbuf_size, &len, digits[hundreds].cardinal_common, flags); + append(outbuf, outbuf_size, &len, "||hun¦dra", flags); + } + append_for_ordinal = "¦de"; + first = 0; + } + /* fall through */ + + case 1: + small_num *= 10; + small_num += (int32_t)(*num - '0'); + ones = *num++ - '0'; + num_len--; + if (tens[ones].cardinal) { + if (last && (flags & LIBNUMTEXT_N2T_SWEDISH_DENOMINATOR) && *num == '0') { + append(outbuf, outbuf_size, &len, tens[ones].ordinal, flags); + } else if (last && !great_order && *num == '0') { + append(outbuf, outbuf_size, &len, tens[ones].ordinal, flags); + append(outbuf, outbuf_size, &len, "¦de", flags); + } else { + append(outbuf, outbuf_size, &len, tens[ones].cardinal, flags); + } + append_for_ordinal = NULL; + ones = 0; + first = 0; + } else { + ones *= 10; + } + /* fall through */ + + case 0: + small_num *= 10; + small_num += (int32_t)(*num - '0'); + ones += *num++ - '0'; + if (ones) { + append_for_ordinal = NULL; + if (last && (flags & LIBNUMTEXT_N2T_SWEDISH_DENOMINATOR)) { + if ((flags & UINT32_C(0x00000030)) == LIBNUMTEXT_N2T_SWEDISH_MASCULINE_GENDER) + append(outbuf, outbuf_size, &len, digits[ones].ordinal_masculine, flags); + else + append(outbuf, outbuf_size, &len, digits[ones].ordinal_other, flags); + append_for_ordinal = digits[ones].ordinal_suffix; + } else if (!digits[ones].cardinal_other || + (last && (flags & UINT32_C(0x00000030)) == LIBNUMTEXT_N2T_SWEDISH_COMMON_GENDER)) { + append(outbuf, outbuf_size, &len, digits[ones].cardinal_common, flags); + } else { + append(outbuf, outbuf_size, &len, digits[ones].cardinal_other, flags); + } + first = 0; + } + break; + } + + if (great_order) { + if (great_order < 10) { + append(outbuf, outbuf_size, &len, greats[great_order - 1][0], flags); + } else if (great_order > 999) { + errno = EDOM; + return -1; + } else { + great_1 = greats[(great_order / 1) % 10][1]; + great_1_suffix = greats[(great_order / 1) % 10][2]; + great_10_prefix = greats[(great_order / 10) % 10][3]; + great_10 = greats[(great_order / 10) % 10][4]; + great_100_prefix = greats[(great_order / 100) % 10][5]; + great_100 = greats[(great_order / 100) % 10][6]; + great_last = NULL; + if (great_1) { + append(outbuf, outbuf_size, &len, great_1, flags); + great_last = great_1; + } + if (great_10) { + if (great_1_suffix && great_10_prefix) { + for (gprefix = great_10_prefix; *gprefix; gprefix++) { + for (gsuffix = great_1_suffix; *gsuffix; gsuffix++) + if (*gprefix == *gsuffix) + break; + if (*gsuffix) + break; + } + if (*gprefix && *gprefix == *gsuffix) { + affix[0] = *gprefix; + if (affix[0] == '*') + affix[0] = 's'; + append(outbuf, outbuf_size, &len, affix, flags); + } + } + append(outbuf, outbuf_size, &len, great_10, flags); + great_last = great_10; + great_1_suffix = NULL; + } + if (great_100) { + if (great_1_suffix && great_100_prefix) { + for (gprefix = great_100_prefix; *gprefix; gprefix++) { + for (gsuffix = great_1_suffix; *gsuffix; gsuffix++) + if (*gprefix == *gsuffix) + break; + if (*gsuffix) + break; + } + if (*gprefix && *gprefix == *gsuffix) { + affix[0] = *gprefix; + if (affix[0] == '*') + affix[0] = 's'; + append(outbuf, outbuf_size, &len, affix, flags); + } + } + append(outbuf, outbuf_size, &len, great_100, flags); + great_last = great_100; + } + while (great_last[1]) + great_last = &great_last[1]; + if (*great_last == 'a' || *great_last == 'e' || *great_last == 'i' || *great_last == 'o') + len -= 1; + } + append(outbuf, outbuf_size, &len, great_suffixes[great_order_suffix], flags); + append_for_ordinal = great_order_suffix == 0 ? "¦te" : "<¦e"; + if (small_num != 1) + if (!last || !(flags & (LIBNUMTEXT_N2T_SWEDISH_ORDINAL | LIBNUMTEXT_N2T_SWEDISH_DENOMINATOR))) + append(outbuf, outbuf_size, &len, "¦er", flags); + } + } + + if (flags & LIBNUMTEXT_N2T_SWEDISH_ORDINAL) + if (!(flags & LIBNUMTEXT_N2T_SWEDISH_DENOMINATOR)) + if (!append_for_ordinal) + append(outbuf, outbuf_size, &len, append_for_ordinal, flags); + suffix(outbuf, outbuf_size, &len, flags); + +out: + outbuf -= offset; + outbuf_size += offset; + + if ((size_t)len < outbuf_size) + outbuf[len] = '0'; + else if (outbuf_size) + outbuf[outbuf_size - 1] = '0'; + len += 1; + + if (!outbuf_size) + return (ssize_t)len; + + /* + * Å = \xc3\x85 + * å = \xc3\xa5 + * Ä = \xc3\x84 + * ä = \xc3\xa4 + * Ö = \xc3\x96 + * ö = \xc3\xb6 + * A-Z = 0x41 - 0x5a + * a-z = 0x61 - 0x7a + * | = 0x7c + * ¦ = \xc2\xa6 + * - = 0x2d + */ + i = 0; + if ((flags & UINT32_C(0x00000300)) == LIBNUMTEXT_N2T_SWEDISH_SENTENCE_CASE) { + i = 1; + while ((outbuf[i] & 0xC0) == 0x80) + i += 1; + goto lower_case; + + } else if ((flags & UINT32_C(0x00000300)) == LIBNUMTEXT_N2T_SWEDISH_UPPER_CASE) { + for (; outbuf[i]; i++) + if (isupper(outbuf[i]) || outbuf[i] == '\xa5' || outbuf[i] == '\xa4' || outbuf[i] == '\xb6') + outbuf[i] ^= 0x20; + + } else if ((flags & UINT32_C(0x00000300)) == LIBNUMTEXT_N2T_SWEDISH_LOWER_CASE) { + lower_case: + for (; outbuf[i]; i++) + if (islower(outbuf[i]) || outbuf[i] == '\x85') + outbuf[i] ^= 0x20; + } + + return (ssize_t)len; + +einval: + errno = EINVAL; + return -1; +} -- cgit v1.2.3-70-g09d2