xref: /reactos/dll/nls/normaliz/IdnToUnicode.c (revision 8a978a17)
1 
2 #define WIN32_NO_STATUS
3 #include <wine/unicode.h>
4 
5 #define NDEBUG
6 #include <debug.h>
7 
8 /* Taken from Wine kernel32/locale.c */
9 
10 enum {
11     BASE = 36,
12     TMIN = 1,
13     TMAX = 26,
14     SKEW = 38,
15     DAMP = 700,
16     INIT_BIAS = 72,
17     INIT_N = 128
18 };
19 
20 static inline INT adapt(INT delta, INT numpoints, BOOL firsttime)
21 {
22     INT k;
23 
24     delta /= (firsttime ? DAMP : 2);
25     delta += delta/numpoints;
26 
27     for(k=0; delta>((BASE-TMIN)*TMAX)/2; k+=BASE)
28         delta /= BASE-TMIN;
29     return k+((BASE-TMIN+1)*delta)/(delta+SKEW);
30 }
31 
32 static inline unsigned short get_table_entry( const unsigned short *table, WCHAR ch )
33 {
34     return table[table[table[ch >> 8] + ((ch >> 4) & 0x0f)] + (ch & 0xf)];
35 }
36 
37 INT WINAPI IdnToUnicode(DWORD dwFlags, LPCWSTR lpASCIICharStr, INT cchASCIIChar,
38                         LPWSTR lpUnicodeCharStr, INT cchUnicodeChar)
39 {
40     extern const unsigned short nameprep_char_type[];
41 
42     INT i, label_start, label_end, out_label, out = 0;
43     WCHAR ch;
44 
45     DPRINT("%x %p %d %p %d\n", dwFlags, lpASCIICharStr, cchASCIIChar,
46            lpUnicodeCharStr, cchUnicodeChar);
47 
48     for(label_start=0; label_start<cchASCIIChar;) {
49         INT n = INIT_N, pos = 0, old_pos, w, k, bias = INIT_BIAS, delim=0, digit, t;
50 
51         out_label = out;
52         for(i=label_start; i<cchASCIIChar; i++) {
53             ch = lpASCIICharStr[i];
54 
55             if(ch>0x7f || (i!=cchASCIIChar-1 && !ch)) {
56                 SetLastError(ERROR_INVALID_NAME);
57                 return 0;
58             }
59 
60             if(!ch || ch=='.')
61                 break;
62             if(ch == '-')
63                 delim = i;
64 
65             if((dwFlags&IDN_USE_STD3_ASCII_RULES) == 0)
66                 continue;
67             if((ch>='a' && ch<='z') || (ch>='A' && ch<='Z')
68                     || (ch>='0' && ch<='9') || ch=='-')
69                 continue;
70 
71             SetLastError(ERROR_INVALID_NAME);
72             return 0;
73         }
74         label_end = i;
75         /* last label may be empty */
76         if(label_start==label_end && ch) {
77             SetLastError(ERROR_INVALID_NAME);
78             return 0;
79         }
80 
81         if((dwFlags&IDN_USE_STD3_ASCII_RULES) && (lpASCIICharStr[label_start]=='-' ||
82                     lpASCIICharStr[label_end-1]=='-')) {
83             SetLastError(ERROR_INVALID_NAME);
84             return 0;
85         }
86         if(label_end-label_start > 63) {
87             SetLastError(ERROR_INVALID_NAME);
88             return 0;
89         }
90 
91         if(label_end-label_start<4 ||
92                 tolowerW(lpASCIICharStr[label_start])!='x' ||
93                 tolowerW(lpASCIICharStr[label_start+1])!='n' ||
94                 lpASCIICharStr[label_start+2]!='-' || lpASCIICharStr[label_start+3]!='-') {
95             if(label_end < cchASCIIChar)
96                 label_end++;
97 
98             if(!lpUnicodeCharStr) {
99                 out += label_end-label_start;
100             }else if(out+label_end-label_start <= cchUnicodeChar) {
101                 memcpy(lpUnicodeCharStr+out, lpASCIICharStr+label_start,
102                         (label_end-label_start)*sizeof(WCHAR));
103                 out += label_end-label_start;
104             }else {
105                 SetLastError(ERROR_INSUFFICIENT_BUFFER);
106                 return 0;
107             }
108 
109             label_start = label_end;
110             continue;
111         }
112 
113         if(delim == label_start+3)
114             delim++;
115         if(!lpUnicodeCharStr) {
116             out += delim-label_start-4;
117         }else if(out+delim-label_start-4 <= cchUnicodeChar) {
118             memcpy(lpUnicodeCharStr+out, lpASCIICharStr+label_start+4,
119                     (delim-label_start-4)*sizeof(WCHAR));
120             out += delim-label_start-4;
121         }else {
122             SetLastError(ERROR_INSUFFICIENT_BUFFER);
123             return 0;
124         }
125         if(out != out_label)
126             delim++;
127 
128         for(i=delim; i<label_end;) {
129             old_pos = pos;
130             w = 1;
131             for(k=BASE; ; k+=BASE) {
132                 ch = i<label_end ? tolowerW(lpASCIICharStr[i++]) : 0;
133                 if((ch<'a' || ch>'z') && (ch<'0' || ch>'9')) {
134                     SetLastError(ERROR_INVALID_NAME);
135                     return 0;
136                 }
137                 digit = ch<='9' ? ch-'0'+'z'-'a'+1 : ch-'a';
138                 pos += digit*w;
139                 t = k<=bias ? TMIN : k>=bias+TMAX ? TMAX : k-bias;
140                 if(digit < t)
141                     break;
142                 w *= BASE-t;
143             }
144             bias = adapt(pos-old_pos, out-out_label+1, old_pos==0);
145             n += pos/(out-out_label+1);
146             pos %= out-out_label+1;
147 
148             if((dwFlags&IDN_ALLOW_UNASSIGNED)==0 &&
149                     get_table_entry(nameprep_char_type, n)==1/*UNASSIGNED*/) {
150                 SetLastError(ERROR_INVALID_NAME);
151                 return 0;
152             }
153             if(!lpUnicodeCharStr) {
154                 out++;
155             }else if(out+1 <= cchASCIIChar) {
156                 memmove(lpUnicodeCharStr+out_label+pos+1,
157                         lpUnicodeCharStr+out_label+pos,
158                         (out-out_label-pos)*sizeof(WCHAR));
159                 lpUnicodeCharStr[out_label+pos] = n;
160                 out++;
161             }else {
162                 SetLastError(ERROR_INSUFFICIENT_BUFFER);
163                 return 0;
164             }
165             pos++;
166         }
167 
168         if(out-out_label > 63) {
169             SetLastError(ERROR_INVALID_NAME);
170             return 0;
171         }
172 
173         if(label_end < cchASCIIChar) {
174             if(!lpUnicodeCharStr) {
175                 out++;
176             }else if(out+1 <= cchUnicodeChar) {
177                 lpUnicodeCharStr[out++] = lpASCIICharStr[label_end];
178             }else {
179                 SetLastError(ERROR_INSUFFICIENT_BUFFER);
180                 return 0;
181             }
182         }
183         label_start = label_end+1;
184     }
185 
186     return out;
187 }
188