]> git.draconx.ca Git - dxcommon.git/blob - src/copysym.c
Add a dedicated function to emit the copyright symbol.
[dxcommon.git] / src / copysym.c
1 /*
2  * Copyright © 2023 Nick Bowler
3  *
4  * Helper function to output the copyright symbol in a specified encoding.
5  *
6  * License WTFPL2: Do What The Fuck You Want To Public License, version 2.
7  * This is free software: you are free to do what the fuck you want to.
8  * There is NO WARRANTY, to the extent permitted by law.
9  */
10
11 #if HAVE_CONFIG_H
12 #       include <config.h>
13 #endif
14
15 #if ENABLE_NLS
16
17 #include <stdlib.h>
18 #include <string.h>
19 #include <stdint.h>
20 #include "xtra.h"
21
22 static int compar_5arr(const void *key, const void *elem_)
23 {
24         const char (*elem)[5] = elem_;
25
26         return strncmp(key, *elem, sizeof *elem);
27 }
28
29 /*
30  * Return, as a multibyte string, the copyright symbol for the
31  * given character encoding, which is one of the strings returned
32  * by Gnulib's locale_charset function.  In particular, we are
33  * looking for one of the strings:
34  *
35  *     CP1129
36  *     CP1250
37  *     CP1251
38  *     CP1252
39  *     CP1253
40  *     CP1254
41  *     CP1256
42  *     CP1257
43  *     CP1258
44  *     CP775
45  *     CP850
46  *     CP856
47  *     CP857
48  *     CP869
49  *     CP922
50  *     GEORGIAN-PS
51  *     ISO-8859-1
52  *     ISO-8859-13
53  *     ISO-8859-14
54  *     ISO-8859-15
55  *     ISO-8859-7
56  *     ISO-8859-8
57  *     ISO-8859-9
58  *     PT154
59  *     EUC-JP
60  *     GB18030
61  *     KOI8-R
62  *     KOI8-T
63  *     KOI8-U
64  *     UTF-8
65  *
66  * All of these are ASCII supersets.  EBCDIC code pages like CP1122 are
67  * presently handled by returning (C), even if the character set does
68  * include the copyright symbol.
69  *
70  * To simplify the implementation, we allow some slop in the matching,
71  * as long as the result is valid for any actual encoding names.
72  *
73  * If NLS support is disabled, or if the character set does not
74  * include the copyright symbol, then the string (C) is returned
75  * in the C execution character set.
76  */
77 const char *copyright_symbol(const char *charset)
78 {
79         /* All known encodings of the copyright symbol */
80         static const char codes[] =
81                 "\xc2\xa9"         "\0"
82                 "\x97"             "\0"
83                 "\xa8"             "\0"
84                 "\xb8"             "\0"
85                 "\xbf"             "\0"
86                 "\x8f\xa2\xed"     "\0"
87                 "\x81\x30\x84\x38" "\0"
88                 "(C)";
89
90         /*
91          * We need the list below to be in lexicographic order in
92          * the C execution character encoding.
93          */
94 #if 'B'>'E' || 'C'>'E' || 'E'>'G' || 'G'>'I' || 'K'>'P' || 'P'>'U'
95 #  error this character encoding is unsupported, please report a bug.
96 #endif
97
98         /*
99          * For character sets that include the copyright symbol,
100          * the first 5 characters suffices to distinguish amongst
101          * all the different possible encodings.
102          */
103         static const char t1[][5] = {
104                 "CP112",
105                 "CP125",
106                 "CP775",
107                 "CP850",
108                 "CP856",
109                 "CP857",
110                 "CP869",
111                 "CP922",
112                 "EUC-J",
113                 "GB180",
114                 "GEORG",
115                 "ISO-8",
116                 "KOI8-",
117                 "PT154",
118                 "UTF-8"
119         };
120
121         /*
122          * Each nibble in the results value contains the offset in the
123          * codes array for the corresponding index in t1, except that
124          * ISO-8859 matches the special value '2' (handled below).
125          */
126         uint_least64_t results = 0x001921fb13777511;
127         const char (*m1)[sizeof *t1];
128         unsigned x, cindex;
129
130         if (!charset)
131                 goto no_conv;
132
133         m1 = bsearch(charset, t1, XTRA_ARRAYSIZE(t1), sizeof *t1, compar_5arr);
134         if (!m1)
135                 goto no_conv;
136         charset += 5;
137
138         x = m1-t1;
139         cindex = (results >> (x << 2)) & 0xf;
140
141         /*
142          * We now need to identify encodings that match one of the 5-character
143          * prefixes above but don't actually have the copyright symbol in their
144          * character set.  Specifically, these are:
145          *
146          *   CP1122 (does have it, but EBCDIC)
147          *   CP1124
148          *   CP1125
149          *   ISO-8859-10
150          *   ISO-8859-11
151          *   ISO-8859-2
152          *   ISO-8859-3
153          *   ISO-8859-4
154          *   ISO-8859-5
155          *   ISO-8859-6
156          */
157         if ((x == 0) != (*charset == '9')) {
158                 /* CP112x, x != '9', no copyright symbol. */
159                 goto no_conv;
160         } else if (cindex == 2) {
161                 /*
162                  * ISO-8859 special case.  Simply find and look at the final
163                  * two digits.  The set bits in the 'accept' value indicate
164                  * which encodings have the copyright symbol.
165                  */
166                 uint_least32_t accept  = 0x00380383;
167                 uint_least32_t collect = 0;
168                 char c;
169
170                 while ((c = *charset++)) {
171                         collect <<= 4;
172
173                         if (c != '-')
174                                 collect |= c - '0';
175                 }
176
177                 cindex = (accept >> (collect & 0x1f)) & 1;
178                 if (!cindex)
179                         goto no_conv;
180         }
181
182         return &codes[cindex];
183 no_conv:
184         return &codes[20];
185 }
186
187 #endif