Sat, 03 Jan 2015 20:18:00 +0100
Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.
michael@0 | 1 | /* |
michael@0 | 2 | ******************************************************************************* |
michael@0 | 3 | * |
michael@0 | 4 | * Copyright (C) 1997-2012, International Business Machines |
michael@0 | 5 | * Corporation and others. All Rights Reserved. |
michael@0 | 6 | * |
michael@0 | 7 | ******************************************************************************* |
michael@0 | 8 | * file name: loclikely.cpp |
michael@0 | 9 | * encoding: US-ASCII |
michael@0 | 10 | * tab size: 8 (not used) |
michael@0 | 11 | * indentation:4 |
michael@0 | 12 | * |
michael@0 | 13 | * created on: 2010feb25 |
michael@0 | 14 | * created by: Markus W. Scherer |
michael@0 | 15 | * |
michael@0 | 16 | * Code for likely and minimized locale subtags, separated out from other .cpp files |
michael@0 | 17 | * that then do not depend on resource bundle code and likely-subtags data. |
michael@0 | 18 | */ |
michael@0 | 19 | |
michael@0 | 20 | #include "unicode/utypes.h" |
michael@0 | 21 | #include "unicode/putil.h" |
michael@0 | 22 | #include "unicode/uloc.h" |
michael@0 | 23 | #include "unicode/ures.h" |
michael@0 | 24 | #include "cmemory.h" |
michael@0 | 25 | #include "cstring.h" |
michael@0 | 26 | #include "ulocimp.h" |
michael@0 | 27 | #include "ustr_imp.h" |
michael@0 | 28 | |
michael@0 | 29 | /** |
michael@0 | 30 | * This function looks for the localeID in the likelySubtags resource. |
michael@0 | 31 | * |
michael@0 | 32 | * @param localeID The tag to find. |
michael@0 | 33 | * @param buffer A buffer to hold the matching entry |
michael@0 | 34 | * @param bufferLength The length of the output buffer |
michael@0 | 35 | * @return A pointer to "buffer" if found, or a null pointer if not. |
michael@0 | 36 | */ |
michael@0 | 37 | static const char* U_CALLCONV |
michael@0 | 38 | findLikelySubtags(const char* localeID, |
michael@0 | 39 | char* buffer, |
michael@0 | 40 | int32_t bufferLength, |
michael@0 | 41 | UErrorCode* err) { |
michael@0 | 42 | const char* result = NULL; |
michael@0 | 43 | |
michael@0 | 44 | if (!U_FAILURE(*err)) { |
michael@0 | 45 | int32_t resLen = 0; |
michael@0 | 46 | const UChar* s = NULL; |
michael@0 | 47 | UErrorCode tmpErr = U_ZERO_ERROR; |
michael@0 | 48 | UResourceBundle* subtags = ures_openDirect(NULL, "likelySubtags", &tmpErr); |
michael@0 | 49 | if (U_SUCCESS(tmpErr)) { |
michael@0 | 50 | s = ures_getStringByKey(subtags, localeID, &resLen, &tmpErr); |
michael@0 | 51 | |
michael@0 | 52 | if (U_FAILURE(tmpErr)) { |
michael@0 | 53 | /* |
michael@0 | 54 | * If a resource is missing, it's not really an error, it's |
michael@0 | 55 | * just that we don't have any data for that particular locale ID. |
michael@0 | 56 | */ |
michael@0 | 57 | if (tmpErr != U_MISSING_RESOURCE_ERROR) { |
michael@0 | 58 | *err = tmpErr; |
michael@0 | 59 | } |
michael@0 | 60 | } |
michael@0 | 61 | else if (resLen >= bufferLength) { |
michael@0 | 62 | /* The buffer should never overflow. */ |
michael@0 | 63 | *err = U_INTERNAL_PROGRAM_ERROR; |
michael@0 | 64 | } |
michael@0 | 65 | else { |
michael@0 | 66 | u_UCharsToChars(s, buffer, resLen + 1); |
michael@0 | 67 | result = buffer; |
michael@0 | 68 | } |
michael@0 | 69 | |
michael@0 | 70 | ures_close(subtags); |
michael@0 | 71 | } else { |
michael@0 | 72 | *err = tmpErr; |
michael@0 | 73 | } |
michael@0 | 74 | } |
michael@0 | 75 | |
michael@0 | 76 | return result; |
michael@0 | 77 | } |
michael@0 | 78 | |
michael@0 | 79 | /** |
michael@0 | 80 | * Append a tag to a buffer, adding the separator if necessary. The buffer |
michael@0 | 81 | * must be large enough to contain the resulting tag plus any separator |
michael@0 | 82 | * necessary. The tag must not be a zero-length string. |
michael@0 | 83 | * |
michael@0 | 84 | * @param tag The tag to add. |
michael@0 | 85 | * @param tagLength The length of the tag. |
michael@0 | 86 | * @param buffer The output buffer. |
michael@0 | 87 | * @param bufferLength The length of the output buffer. This is an input/ouput parameter. |
michael@0 | 88 | **/ |
michael@0 | 89 | static void U_CALLCONV |
michael@0 | 90 | appendTag( |
michael@0 | 91 | const char* tag, |
michael@0 | 92 | int32_t tagLength, |
michael@0 | 93 | char* buffer, |
michael@0 | 94 | int32_t* bufferLength) { |
michael@0 | 95 | |
michael@0 | 96 | if (*bufferLength > 0) { |
michael@0 | 97 | buffer[*bufferLength] = '_'; |
michael@0 | 98 | ++(*bufferLength); |
michael@0 | 99 | } |
michael@0 | 100 | |
michael@0 | 101 | uprv_memmove( |
michael@0 | 102 | &buffer[*bufferLength], |
michael@0 | 103 | tag, |
michael@0 | 104 | tagLength); |
michael@0 | 105 | |
michael@0 | 106 | *bufferLength += tagLength; |
michael@0 | 107 | } |
michael@0 | 108 | |
michael@0 | 109 | /** |
michael@0 | 110 | * These are the canonical strings for unknown languages, scripts and regions. |
michael@0 | 111 | **/ |
michael@0 | 112 | static const char* const unknownLanguage = "und"; |
michael@0 | 113 | static const char* const unknownScript = "Zzzz"; |
michael@0 | 114 | static const char* const unknownRegion = "ZZ"; |
michael@0 | 115 | |
michael@0 | 116 | /** |
michael@0 | 117 | * Create a tag string from the supplied parameters. The lang, script and region |
michael@0 | 118 | * parameters may be NULL pointers. If they are, their corresponding length parameters |
michael@0 | 119 | * must be less than or equal to 0. |
michael@0 | 120 | * |
michael@0 | 121 | * If any of the language, script or region parameters are empty, and the alternateTags |
michael@0 | 122 | * parameter is not NULL, it will be parsed for potential language, script and region tags |
michael@0 | 123 | * to be used when constructing the new tag. If the alternateTags parameter is NULL, or |
michael@0 | 124 | * it contains no language tag, the default tag for the unknown language is used. |
michael@0 | 125 | * |
michael@0 | 126 | * If the length of the new string exceeds the capacity of the output buffer, |
michael@0 | 127 | * the function copies as many bytes to the output buffer as it can, and returns |
michael@0 | 128 | * the error U_BUFFER_OVERFLOW_ERROR. |
michael@0 | 129 | * |
michael@0 | 130 | * If an illegal argument is provided, the function returns the error |
michael@0 | 131 | * U_ILLEGAL_ARGUMENT_ERROR. |
michael@0 | 132 | * |
michael@0 | 133 | * Note that this function can return the warning U_STRING_NOT_TERMINATED_WARNING if |
michael@0 | 134 | * the tag string fits in the output buffer, but the null terminator doesn't. |
michael@0 | 135 | * |
michael@0 | 136 | * @param lang The language tag to use. |
michael@0 | 137 | * @param langLength The length of the language tag. |
michael@0 | 138 | * @param script The script tag to use. |
michael@0 | 139 | * @param scriptLength The length of the script tag. |
michael@0 | 140 | * @param region The region tag to use. |
michael@0 | 141 | * @param regionLength The length of the region tag. |
michael@0 | 142 | * @param trailing Any trailing data to append to the new tag. |
michael@0 | 143 | * @param trailingLength The length of the trailing data. |
michael@0 | 144 | * @param alternateTags A string containing any alternate tags. |
michael@0 | 145 | * @param tag The output buffer. |
michael@0 | 146 | * @param tagCapacity The capacity of the output buffer. |
michael@0 | 147 | * @param err A pointer to a UErrorCode for error reporting. |
michael@0 | 148 | * @return The length of the tag string, which may be greater than tagCapacity, or -1 on error. |
michael@0 | 149 | **/ |
michael@0 | 150 | static int32_t U_CALLCONV |
michael@0 | 151 | createTagStringWithAlternates( |
michael@0 | 152 | const char* lang, |
michael@0 | 153 | int32_t langLength, |
michael@0 | 154 | const char* script, |
michael@0 | 155 | int32_t scriptLength, |
michael@0 | 156 | const char* region, |
michael@0 | 157 | int32_t regionLength, |
michael@0 | 158 | const char* trailing, |
michael@0 | 159 | int32_t trailingLength, |
michael@0 | 160 | const char* alternateTags, |
michael@0 | 161 | char* tag, |
michael@0 | 162 | int32_t tagCapacity, |
michael@0 | 163 | UErrorCode* err) { |
michael@0 | 164 | |
michael@0 | 165 | if (U_FAILURE(*err)) { |
michael@0 | 166 | goto error; |
michael@0 | 167 | } |
michael@0 | 168 | else if (tag == NULL || |
michael@0 | 169 | tagCapacity <= 0 || |
michael@0 | 170 | langLength >= ULOC_LANG_CAPACITY || |
michael@0 | 171 | scriptLength >= ULOC_SCRIPT_CAPACITY || |
michael@0 | 172 | regionLength >= ULOC_COUNTRY_CAPACITY) { |
michael@0 | 173 | goto error; |
michael@0 | 174 | } |
michael@0 | 175 | else { |
michael@0 | 176 | /** |
michael@0 | 177 | * ULOC_FULLNAME_CAPACITY will provide enough capacity |
michael@0 | 178 | * that we can build a string that contains the language, |
michael@0 | 179 | * script and region code without worrying about overrunning |
michael@0 | 180 | * the user-supplied buffer. |
michael@0 | 181 | **/ |
michael@0 | 182 | char tagBuffer[ULOC_FULLNAME_CAPACITY]; |
michael@0 | 183 | int32_t tagLength = 0; |
michael@0 | 184 | int32_t capacityRemaining = tagCapacity; |
michael@0 | 185 | UBool regionAppended = FALSE; |
michael@0 | 186 | |
michael@0 | 187 | if (langLength > 0) { |
michael@0 | 188 | appendTag( |
michael@0 | 189 | lang, |
michael@0 | 190 | langLength, |
michael@0 | 191 | tagBuffer, |
michael@0 | 192 | &tagLength); |
michael@0 | 193 | } |
michael@0 | 194 | else if (alternateTags == NULL) { |
michael@0 | 195 | /* |
michael@0 | 196 | * Append the value for an unknown language, if |
michael@0 | 197 | * we found no language. |
michael@0 | 198 | */ |
michael@0 | 199 | appendTag( |
michael@0 | 200 | unknownLanguage, |
michael@0 | 201 | (int32_t)uprv_strlen(unknownLanguage), |
michael@0 | 202 | tagBuffer, |
michael@0 | 203 | &tagLength); |
michael@0 | 204 | } |
michael@0 | 205 | else { |
michael@0 | 206 | /* |
michael@0 | 207 | * Parse the alternateTags string for the language. |
michael@0 | 208 | */ |
michael@0 | 209 | char alternateLang[ULOC_LANG_CAPACITY]; |
michael@0 | 210 | int32_t alternateLangLength = sizeof(alternateLang); |
michael@0 | 211 | |
michael@0 | 212 | alternateLangLength = |
michael@0 | 213 | uloc_getLanguage( |
michael@0 | 214 | alternateTags, |
michael@0 | 215 | alternateLang, |
michael@0 | 216 | alternateLangLength, |
michael@0 | 217 | err); |
michael@0 | 218 | if(U_FAILURE(*err) || |
michael@0 | 219 | alternateLangLength >= ULOC_LANG_CAPACITY) { |
michael@0 | 220 | goto error; |
michael@0 | 221 | } |
michael@0 | 222 | else if (alternateLangLength == 0) { |
michael@0 | 223 | /* |
michael@0 | 224 | * Append the value for an unknown language, if |
michael@0 | 225 | * we found no language. |
michael@0 | 226 | */ |
michael@0 | 227 | appendTag( |
michael@0 | 228 | unknownLanguage, |
michael@0 | 229 | (int32_t)uprv_strlen(unknownLanguage), |
michael@0 | 230 | tagBuffer, |
michael@0 | 231 | &tagLength); |
michael@0 | 232 | } |
michael@0 | 233 | else { |
michael@0 | 234 | appendTag( |
michael@0 | 235 | alternateLang, |
michael@0 | 236 | alternateLangLength, |
michael@0 | 237 | tagBuffer, |
michael@0 | 238 | &tagLength); |
michael@0 | 239 | } |
michael@0 | 240 | } |
michael@0 | 241 | |
michael@0 | 242 | if (scriptLength > 0) { |
michael@0 | 243 | appendTag( |
michael@0 | 244 | script, |
michael@0 | 245 | scriptLength, |
michael@0 | 246 | tagBuffer, |
michael@0 | 247 | &tagLength); |
michael@0 | 248 | } |
michael@0 | 249 | else if (alternateTags != NULL) { |
michael@0 | 250 | /* |
michael@0 | 251 | * Parse the alternateTags string for the script. |
michael@0 | 252 | */ |
michael@0 | 253 | char alternateScript[ULOC_SCRIPT_CAPACITY]; |
michael@0 | 254 | |
michael@0 | 255 | const int32_t alternateScriptLength = |
michael@0 | 256 | uloc_getScript( |
michael@0 | 257 | alternateTags, |
michael@0 | 258 | alternateScript, |
michael@0 | 259 | sizeof(alternateScript), |
michael@0 | 260 | err); |
michael@0 | 261 | |
michael@0 | 262 | if (U_FAILURE(*err) || |
michael@0 | 263 | alternateScriptLength >= ULOC_SCRIPT_CAPACITY) { |
michael@0 | 264 | goto error; |
michael@0 | 265 | } |
michael@0 | 266 | else if (alternateScriptLength > 0) { |
michael@0 | 267 | appendTag( |
michael@0 | 268 | alternateScript, |
michael@0 | 269 | alternateScriptLength, |
michael@0 | 270 | tagBuffer, |
michael@0 | 271 | &tagLength); |
michael@0 | 272 | } |
michael@0 | 273 | } |
michael@0 | 274 | |
michael@0 | 275 | if (regionLength > 0) { |
michael@0 | 276 | appendTag( |
michael@0 | 277 | region, |
michael@0 | 278 | regionLength, |
michael@0 | 279 | tagBuffer, |
michael@0 | 280 | &tagLength); |
michael@0 | 281 | |
michael@0 | 282 | regionAppended = TRUE; |
michael@0 | 283 | } |
michael@0 | 284 | else if (alternateTags != NULL) { |
michael@0 | 285 | /* |
michael@0 | 286 | * Parse the alternateTags string for the region. |
michael@0 | 287 | */ |
michael@0 | 288 | char alternateRegion[ULOC_COUNTRY_CAPACITY]; |
michael@0 | 289 | |
michael@0 | 290 | const int32_t alternateRegionLength = |
michael@0 | 291 | uloc_getCountry( |
michael@0 | 292 | alternateTags, |
michael@0 | 293 | alternateRegion, |
michael@0 | 294 | sizeof(alternateRegion), |
michael@0 | 295 | err); |
michael@0 | 296 | if (U_FAILURE(*err) || |
michael@0 | 297 | alternateRegionLength >= ULOC_COUNTRY_CAPACITY) { |
michael@0 | 298 | goto error; |
michael@0 | 299 | } |
michael@0 | 300 | else if (alternateRegionLength > 0) { |
michael@0 | 301 | appendTag( |
michael@0 | 302 | alternateRegion, |
michael@0 | 303 | alternateRegionLength, |
michael@0 | 304 | tagBuffer, |
michael@0 | 305 | &tagLength); |
michael@0 | 306 | |
michael@0 | 307 | regionAppended = TRUE; |
michael@0 | 308 | } |
michael@0 | 309 | } |
michael@0 | 310 | |
michael@0 | 311 | { |
michael@0 | 312 | const int32_t toCopy = |
michael@0 | 313 | tagLength >= tagCapacity ? tagCapacity : tagLength; |
michael@0 | 314 | |
michael@0 | 315 | /** |
michael@0 | 316 | * Copy the partial tag from our internal buffer to the supplied |
michael@0 | 317 | * target. |
michael@0 | 318 | **/ |
michael@0 | 319 | uprv_memcpy( |
michael@0 | 320 | tag, |
michael@0 | 321 | tagBuffer, |
michael@0 | 322 | toCopy); |
michael@0 | 323 | |
michael@0 | 324 | capacityRemaining -= toCopy; |
michael@0 | 325 | } |
michael@0 | 326 | |
michael@0 | 327 | if (trailingLength > 0) { |
michael@0 | 328 | if (*trailing != '@' && capacityRemaining > 0) { |
michael@0 | 329 | tag[tagLength++] = '_'; |
michael@0 | 330 | --capacityRemaining; |
michael@0 | 331 | if (capacityRemaining > 0 && !regionAppended) { |
michael@0 | 332 | /* extra separator is required */ |
michael@0 | 333 | tag[tagLength++] = '_'; |
michael@0 | 334 | --capacityRemaining; |
michael@0 | 335 | } |
michael@0 | 336 | } |
michael@0 | 337 | |
michael@0 | 338 | if (capacityRemaining > 0) { |
michael@0 | 339 | /* |
michael@0 | 340 | * Copy the trailing data into the supplied buffer. Use uprv_memmove, since we |
michael@0 | 341 | * don't know if the user-supplied buffers overlap. |
michael@0 | 342 | */ |
michael@0 | 343 | const int32_t toCopy = |
michael@0 | 344 | trailingLength >= capacityRemaining ? capacityRemaining : trailingLength; |
michael@0 | 345 | |
michael@0 | 346 | uprv_memmove( |
michael@0 | 347 | &tag[tagLength], |
michael@0 | 348 | trailing, |
michael@0 | 349 | toCopy); |
michael@0 | 350 | } |
michael@0 | 351 | } |
michael@0 | 352 | |
michael@0 | 353 | tagLength += trailingLength; |
michael@0 | 354 | |
michael@0 | 355 | return u_terminateChars( |
michael@0 | 356 | tag, |
michael@0 | 357 | tagCapacity, |
michael@0 | 358 | tagLength, |
michael@0 | 359 | err); |
michael@0 | 360 | } |
michael@0 | 361 | |
michael@0 | 362 | error: |
michael@0 | 363 | |
michael@0 | 364 | /** |
michael@0 | 365 | * An overflow indicates the locale ID passed in |
michael@0 | 366 | * is ill-formed. If we got here, and there was |
michael@0 | 367 | * no previous error, it's an implicit overflow. |
michael@0 | 368 | **/ |
michael@0 | 369 | if (*err == U_BUFFER_OVERFLOW_ERROR || |
michael@0 | 370 | U_SUCCESS(*err)) { |
michael@0 | 371 | *err = U_ILLEGAL_ARGUMENT_ERROR; |
michael@0 | 372 | } |
michael@0 | 373 | |
michael@0 | 374 | return -1; |
michael@0 | 375 | } |
michael@0 | 376 | |
michael@0 | 377 | /** |
michael@0 | 378 | * Create a tag string from the supplied parameters. The lang, script and region |
michael@0 | 379 | * parameters may be NULL pointers. If they are, their corresponding length parameters |
michael@0 | 380 | * must be less than or equal to 0. If the lang parameter is an empty string, the |
michael@0 | 381 | * default value for an unknown language is written to the output buffer. |
michael@0 | 382 | * |
michael@0 | 383 | * If the length of the new string exceeds the capacity of the output buffer, |
michael@0 | 384 | * the function copies as many bytes to the output buffer as it can, and returns |
michael@0 | 385 | * the error U_BUFFER_OVERFLOW_ERROR. |
michael@0 | 386 | * |
michael@0 | 387 | * If an illegal argument is provided, the function returns the error |
michael@0 | 388 | * U_ILLEGAL_ARGUMENT_ERROR. |
michael@0 | 389 | * |
michael@0 | 390 | * @param lang The language tag to use. |
michael@0 | 391 | * @param langLength The length of the language tag. |
michael@0 | 392 | * @param script The script tag to use. |
michael@0 | 393 | * @param scriptLength The length of the script tag. |
michael@0 | 394 | * @param region The region tag to use. |
michael@0 | 395 | * @param regionLength The length of the region tag. |
michael@0 | 396 | * @param trailing Any trailing data to append to the new tag. |
michael@0 | 397 | * @param trailingLength The length of the trailing data. |
michael@0 | 398 | * @param tag The output buffer. |
michael@0 | 399 | * @param tagCapacity The capacity of the output buffer. |
michael@0 | 400 | * @param err A pointer to a UErrorCode for error reporting. |
michael@0 | 401 | * @return The length of the tag string, which may be greater than tagCapacity. |
michael@0 | 402 | **/ |
michael@0 | 403 | static int32_t U_CALLCONV |
michael@0 | 404 | createTagString( |
michael@0 | 405 | const char* lang, |
michael@0 | 406 | int32_t langLength, |
michael@0 | 407 | const char* script, |
michael@0 | 408 | int32_t scriptLength, |
michael@0 | 409 | const char* region, |
michael@0 | 410 | int32_t regionLength, |
michael@0 | 411 | const char* trailing, |
michael@0 | 412 | int32_t trailingLength, |
michael@0 | 413 | char* tag, |
michael@0 | 414 | int32_t tagCapacity, |
michael@0 | 415 | UErrorCode* err) |
michael@0 | 416 | { |
michael@0 | 417 | return createTagStringWithAlternates( |
michael@0 | 418 | lang, |
michael@0 | 419 | langLength, |
michael@0 | 420 | script, |
michael@0 | 421 | scriptLength, |
michael@0 | 422 | region, |
michael@0 | 423 | regionLength, |
michael@0 | 424 | trailing, |
michael@0 | 425 | trailingLength, |
michael@0 | 426 | NULL, |
michael@0 | 427 | tag, |
michael@0 | 428 | tagCapacity, |
michael@0 | 429 | err); |
michael@0 | 430 | } |
michael@0 | 431 | |
michael@0 | 432 | /** |
michael@0 | 433 | * Parse the language, script, and region subtags from a tag string, and copy the |
michael@0 | 434 | * results into the corresponding output parameters. The buffers are null-terminated, |
michael@0 | 435 | * unless overflow occurs. |
michael@0 | 436 | * |
michael@0 | 437 | * The langLength, scriptLength, and regionLength parameters are input/output |
michael@0 | 438 | * parameters, and must contain the capacity of their corresponding buffers on |
michael@0 | 439 | * input. On output, they will contain the actual length of the buffers, not |
michael@0 | 440 | * including the null terminator. |
michael@0 | 441 | * |
michael@0 | 442 | * If the length of any of the output subtags exceeds the capacity of the corresponding |
michael@0 | 443 | * buffer, the function copies as many bytes to the output buffer as it can, and returns |
michael@0 | 444 | * the error U_BUFFER_OVERFLOW_ERROR. It will not parse any more subtags once overflow |
michael@0 | 445 | * occurs. |
michael@0 | 446 | * |
michael@0 | 447 | * If an illegal argument is provided, the function returns the error |
michael@0 | 448 | * U_ILLEGAL_ARGUMENT_ERROR. |
michael@0 | 449 | * |
michael@0 | 450 | * @param localeID The locale ID to parse. |
michael@0 | 451 | * @param lang The language tag buffer. |
michael@0 | 452 | * @param langLength The length of the language tag. |
michael@0 | 453 | * @param script The script tag buffer. |
michael@0 | 454 | * @param scriptLength The length of the script tag. |
michael@0 | 455 | * @param region The region tag buffer. |
michael@0 | 456 | * @param regionLength The length of the region tag. |
michael@0 | 457 | * @param err A pointer to a UErrorCode for error reporting. |
michael@0 | 458 | * @return The number of chars of the localeID parameter consumed. |
michael@0 | 459 | **/ |
michael@0 | 460 | static int32_t U_CALLCONV |
michael@0 | 461 | parseTagString( |
michael@0 | 462 | const char* localeID, |
michael@0 | 463 | char* lang, |
michael@0 | 464 | int32_t* langLength, |
michael@0 | 465 | char* script, |
michael@0 | 466 | int32_t* scriptLength, |
michael@0 | 467 | char* region, |
michael@0 | 468 | int32_t* regionLength, |
michael@0 | 469 | UErrorCode* err) |
michael@0 | 470 | { |
michael@0 | 471 | const char* position = localeID; |
michael@0 | 472 | int32_t subtagLength = 0; |
michael@0 | 473 | |
michael@0 | 474 | if(U_FAILURE(*err) || |
michael@0 | 475 | localeID == NULL || |
michael@0 | 476 | lang == NULL || |
michael@0 | 477 | langLength == NULL || |
michael@0 | 478 | script == NULL || |
michael@0 | 479 | scriptLength == NULL || |
michael@0 | 480 | region == NULL || |
michael@0 | 481 | regionLength == NULL) { |
michael@0 | 482 | goto error; |
michael@0 | 483 | } |
michael@0 | 484 | |
michael@0 | 485 | subtagLength = ulocimp_getLanguage(position, lang, *langLength, &position); |
michael@0 | 486 | u_terminateChars(lang, *langLength, subtagLength, err); |
michael@0 | 487 | |
michael@0 | 488 | /* |
michael@0 | 489 | * Note that we explicit consider U_STRING_NOT_TERMINATED_WARNING |
michael@0 | 490 | * to be an error, because it indicates the user-supplied tag is |
michael@0 | 491 | * not well-formed. |
michael@0 | 492 | */ |
michael@0 | 493 | if(U_FAILURE(*err)) { |
michael@0 | 494 | goto error; |
michael@0 | 495 | } |
michael@0 | 496 | |
michael@0 | 497 | *langLength = subtagLength; |
michael@0 | 498 | |
michael@0 | 499 | /* |
michael@0 | 500 | * If no language was present, use the value of unknownLanguage |
michael@0 | 501 | * instead. Otherwise, move past any separator. |
michael@0 | 502 | */ |
michael@0 | 503 | if (*langLength == 0) { |
michael@0 | 504 | uprv_strcpy( |
michael@0 | 505 | lang, |
michael@0 | 506 | unknownLanguage); |
michael@0 | 507 | *langLength = (int32_t)uprv_strlen(lang); |
michael@0 | 508 | } |
michael@0 | 509 | else if (_isIDSeparator(*position)) { |
michael@0 | 510 | ++position; |
michael@0 | 511 | } |
michael@0 | 512 | |
michael@0 | 513 | subtagLength = ulocimp_getScript(position, script, *scriptLength, &position); |
michael@0 | 514 | u_terminateChars(script, *scriptLength, subtagLength, err); |
michael@0 | 515 | |
michael@0 | 516 | if(U_FAILURE(*err)) { |
michael@0 | 517 | goto error; |
michael@0 | 518 | } |
michael@0 | 519 | |
michael@0 | 520 | *scriptLength = subtagLength; |
michael@0 | 521 | |
michael@0 | 522 | if (*scriptLength > 0) { |
michael@0 | 523 | if (uprv_strnicmp(script, unknownScript, *scriptLength) == 0) { |
michael@0 | 524 | /** |
michael@0 | 525 | * If the script part is the "unknown" script, then don't return it. |
michael@0 | 526 | **/ |
michael@0 | 527 | *scriptLength = 0; |
michael@0 | 528 | } |
michael@0 | 529 | |
michael@0 | 530 | /* |
michael@0 | 531 | * Move past any separator. |
michael@0 | 532 | */ |
michael@0 | 533 | if (_isIDSeparator(*position)) { |
michael@0 | 534 | ++position; |
michael@0 | 535 | } |
michael@0 | 536 | } |
michael@0 | 537 | |
michael@0 | 538 | subtagLength = ulocimp_getCountry(position, region, *regionLength, &position); |
michael@0 | 539 | u_terminateChars(region, *regionLength, subtagLength, err); |
michael@0 | 540 | |
michael@0 | 541 | if(U_FAILURE(*err)) { |
michael@0 | 542 | goto error; |
michael@0 | 543 | } |
michael@0 | 544 | |
michael@0 | 545 | *regionLength = subtagLength; |
michael@0 | 546 | |
michael@0 | 547 | if (*regionLength > 0) { |
michael@0 | 548 | if (uprv_strnicmp(region, unknownRegion, *regionLength) == 0) { |
michael@0 | 549 | /** |
michael@0 | 550 | * If the region part is the "unknown" region, then don't return it. |
michael@0 | 551 | **/ |
michael@0 | 552 | *regionLength = 0; |
michael@0 | 553 | } |
michael@0 | 554 | } else if (*position != 0 && *position != '@') { |
michael@0 | 555 | /* back up over consumed trailing separator */ |
michael@0 | 556 | --position; |
michael@0 | 557 | } |
michael@0 | 558 | |
michael@0 | 559 | exit: |
michael@0 | 560 | |
michael@0 | 561 | return (int32_t)(position - localeID); |
michael@0 | 562 | |
michael@0 | 563 | error: |
michael@0 | 564 | |
michael@0 | 565 | /** |
michael@0 | 566 | * If we get here, we have no explicit error, it's the result of an |
michael@0 | 567 | * illegal argument. |
michael@0 | 568 | **/ |
michael@0 | 569 | if (!U_FAILURE(*err)) { |
michael@0 | 570 | *err = U_ILLEGAL_ARGUMENT_ERROR; |
michael@0 | 571 | } |
michael@0 | 572 | |
michael@0 | 573 | goto exit; |
michael@0 | 574 | } |
michael@0 | 575 | |
michael@0 | 576 | static int32_t U_CALLCONV |
michael@0 | 577 | createLikelySubtagsString( |
michael@0 | 578 | const char* lang, |
michael@0 | 579 | int32_t langLength, |
michael@0 | 580 | const char* script, |
michael@0 | 581 | int32_t scriptLength, |
michael@0 | 582 | const char* region, |
michael@0 | 583 | int32_t regionLength, |
michael@0 | 584 | const char* variants, |
michael@0 | 585 | int32_t variantsLength, |
michael@0 | 586 | char* tag, |
michael@0 | 587 | int32_t tagCapacity, |
michael@0 | 588 | UErrorCode* err) |
michael@0 | 589 | { |
michael@0 | 590 | /** |
michael@0 | 591 | * ULOC_FULLNAME_CAPACITY will provide enough capacity |
michael@0 | 592 | * that we can build a string that contains the language, |
michael@0 | 593 | * script and region code without worrying about overrunning |
michael@0 | 594 | * the user-supplied buffer. |
michael@0 | 595 | **/ |
michael@0 | 596 | char tagBuffer[ULOC_FULLNAME_CAPACITY]; |
michael@0 | 597 | char likelySubtagsBuffer[ULOC_FULLNAME_CAPACITY]; |
michael@0 | 598 | |
michael@0 | 599 | if(U_FAILURE(*err)) { |
michael@0 | 600 | goto error; |
michael@0 | 601 | } |
michael@0 | 602 | |
michael@0 | 603 | /** |
michael@0 | 604 | * Try the language with the script and region first. |
michael@0 | 605 | **/ |
michael@0 | 606 | if (scriptLength > 0 && regionLength > 0) { |
michael@0 | 607 | |
michael@0 | 608 | const char* likelySubtags = NULL; |
michael@0 | 609 | |
michael@0 | 610 | createTagString( |
michael@0 | 611 | lang, |
michael@0 | 612 | langLength, |
michael@0 | 613 | script, |
michael@0 | 614 | scriptLength, |
michael@0 | 615 | region, |
michael@0 | 616 | regionLength, |
michael@0 | 617 | NULL, |
michael@0 | 618 | 0, |
michael@0 | 619 | tagBuffer, |
michael@0 | 620 | sizeof(tagBuffer), |
michael@0 | 621 | err); |
michael@0 | 622 | if(U_FAILURE(*err)) { |
michael@0 | 623 | goto error; |
michael@0 | 624 | } |
michael@0 | 625 | |
michael@0 | 626 | likelySubtags = |
michael@0 | 627 | findLikelySubtags( |
michael@0 | 628 | tagBuffer, |
michael@0 | 629 | likelySubtagsBuffer, |
michael@0 | 630 | sizeof(likelySubtagsBuffer), |
michael@0 | 631 | err); |
michael@0 | 632 | if(U_FAILURE(*err)) { |
michael@0 | 633 | goto error; |
michael@0 | 634 | } |
michael@0 | 635 | |
michael@0 | 636 | if (likelySubtags != NULL) { |
michael@0 | 637 | /* Always use the language tag from the |
michael@0 | 638 | maximal string, since it may be more |
michael@0 | 639 | specific than the one provided. */ |
michael@0 | 640 | return createTagStringWithAlternates( |
michael@0 | 641 | NULL, |
michael@0 | 642 | 0, |
michael@0 | 643 | NULL, |
michael@0 | 644 | 0, |
michael@0 | 645 | NULL, |
michael@0 | 646 | 0, |
michael@0 | 647 | variants, |
michael@0 | 648 | variantsLength, |
michael@0 | 649 | likelySubtags, |
michael@0 | 650 | tag, |
michael@0 | 651 | tagCapacity, |
michael@0 | 652 | err); |
michael@0 | 653 | } |
michael@0 | 654 | } |
michael@0 | 655 | |
michael@0 | 656 | /** |
michael@0 | 657 | * Try the language with just the script. |
michael@0 | 658 | **/ |
michael@0 | 659 | if (scriptLength > 0) { |
michael@0 | 660 | |
michael@0 | 661 | const char* likelySubtags = NULL; |
michael@0 | 662 | |
michael@0 | 663 | createTagString( |
michael@0 | 664 | lang, |
michael@0 | 665 | langLength, |
michael@0 | 666 | script, |
michael@0 | 667 | scriptLength, |
michael@0 | 668 | NULL, |
michael@0 | 669 | 0, |
michael@0 | 670 | NULL, |
michael@0 | 671 | 0, |
michael@0 | 672 | tagBuffer, |
michael@0 | 673 | sizeof(tagBuffer), |
michael@0 | 674 | err); |
michael@0 | 675 | if(U_FAILURE(*err)) { |
michael@0 | 676 | goto error; |
michael@0 | 677 | } |
michael@0 | 678 | |
michael@0 | 679 | likelySubtags = |
michael@0 | 680 | findLikelySubtags( |
michael@0 | 681 | tagBuffer, |
michael@0 | 682 | likelySubtagsBuffer, |
michael@0 | 683 | sizeof(likelySubtagsBuffer), |
michael@0 | 684 | err); |
michael@0 | 685 | if(U_FAILURE(*err)) { |
michael@0 | 686 | goto error; |
michael@0 | 687 | } |
michael@0 | 688 | |
michael@0 | 689 | if (likelySubtags != NULL) { |
michael@0 | 690 | /* Always use the language tag from the |
michael@0 | 691 | maximal string, since it may be more |
michael@0 | 692 | specific than the one provided. */ |
michael@0 | 693 | return createTagStringWithAlternates( |
michael@0 | 694 | NULL, |
michael@0 | 695 | 0, |
michael@0 | 696 | NULL, |
michael@0 | 697 | 0, |
michael@0 | 698 | region, |
michael@0 | 699 | regionLength, |
michael@0 | 700 | variants, |
michael@0 | 701 | variantsLength, |
michael@0 | 702 | likelySubtags, |
michael@0 | 703 | tag, |
michael@0 | 704 | tagCapacity, |
michael@0 | 705 | err); |
michael@0 | 706 | } |
michael@0 | 707 | } |
michael@0 | 708 | |
michael@0 | 709 | /** |
michael@0 | 710 | * Try the language with just the region. |
michael@0 | 711 | **/ |
michael@0 | 712 | if (regionLength > 0) { |
michael@0 | 713 | |
michael@0 | 714 | const char* likelySubtags = NULL; |
michael@0 | 715 | |
michael@0 | 716 | createTagString( |
michael@0 | 717 | lang, |
michael@0 | 718 | langLength, |
michael@0 | 719 | NULL, |
michael@0 | 720 | 0, |
michael@0 | 721 | region, |
michael@0 | 722 | regionLength, |
michael@0 | 723 | NULL, |
michael@0 | 724 | 0, |
michael@0 | 725 | tagBuffer, |
michael@0 | 726 | sizeof(tagBuffer), |
michael@0 | 727 | err); |
michael@0 | 728 | if(U_FAILURE(*err)) { |
michael@0 | 729 | goto error; |
michael@0 | 730 | } |
michael@0 | 731 | |
michael@0 | 732 | likelySubtags = |
michael@0 | 733 | findLikelySubtags( |
michael@0 | 734 | tagBuffer, |
michael@0 | 735 | likelySubtagsBuffer, |
michael@0 | 736 | sizeof(likelySubtagsBuffer), |
michael@0 | 737 | err); |
michael@0 | 738 | if(U_FAILURE(*err)) { |
michael@0 | 739 | goto error; |
michael@0 | 740 | } |
michael@0 | 741 | |
michael@0 | 742 | if (likelySubtags != NULL) { |
michael@0 | 743 | /* Always use the language tag from the |
michael@0 | 744 | maximal string, since it may be more |
michael@0 | 745 | specific than the one provided. */ |
michael@0 | 746 | return createTagStringWithAlternates( |
michael@0 | 747 | NULL, |
michael@0 | 748 | 0, |
michael@0 | 749 | script, |
michael@0 | 750 | scriptLength, |
michael@0 | 751 | NULL, |
michael@0 | 752 | 0, |
michael@0 | 753 | variants, |
michael@0 | 754 | variantsLength, |
michael@0 | 755 | likelySubtags, |
michael@0 | 756 | tag, |
michael@0 | 757 | tagCapacity, |
michael@0 | 758 | err); |
michael@0 | 759 | } |
michael@0 | 760 | } |
michael@0 | 761 | |
michael@0 | 762 | /** |
michael@0 | 763 | * Finally, try just the language. |
michael@0 | 764 | **/ |
michael@0 | 765 | { |
michael@0 | 766 | const char* likelySubtags = NULL; |
michael@0 | 767 | |
michael@0 | 768 | createTagString( |
michael@0 | 769 | lang, |
michael@0 | 770 | langLength, |
michael@0 | 771 | NULL, |
michael@0 | 772 | 0, |
michael@0 | 773 | NULL, |
michael@0 | 774 | 0, |
michael@0 | 775 | NULL, |
michael@0 | 776 | 0, |
michael@0 | 777 | tagBuffer, |
michael@0 | 778 | sizeof(tagBuffer), |
michael@0 | 779 | err); |
michael@0 | 780 | if(U_FAILURE(*err)) { |
michael@0 | 781 | goto error; |
michael@0 | 782 | } |
michael@0 | 783 | |
michael@0 | 784 | likelySubtags = |
michael@0 | 785 | findLikelySubtags( |
michael@0 | 786 | tagBuffer, |
michael@0 | 787 | likelySubtagsBuffer, |
michael@0 | 788 | sizeof(likelySubtagsBuffer), |
michael@0 | 789 | err); |
michael@0 | 790 | if(U_FAILURE(*err)) { |
michael@0 | 791 | goto error; |
michael@0 | 792 | } |
michael@0 | 793 | |
michael@0 | 794 | if (likelySubtags != NULL) { |
michael@0 | 795 | /* Always use the language tag from the |
michael@0 | 796 | maximal string, since it may be more |
michael@0 | 797 | specific than the one provided. */ |
michael@0 | 798 | return createTagStringWithAlternates( |
michael@0 | 799 | NULL, |
michael@0 | 800 | 0, |
michael@0 | 801 | script, |
michael@0 | 802 | scriptLength, |
michael@0 | 803 | region, |
michael@0 | 804 | regionLength, |
michael@0 | 805 | variants, |
michael@0 | 806 | variantsLength, |
michael@0 | 807 | likelySubtags, |
michael@0 | 808 | tag, |
michael@0 | 809 | tagCapacity, |
michael@0 | 810 | err); |
michael@0 | 811 | } |
michael@0 | 812 | } |
michael@0 | 813 | |
michael@0 | 814 | return u_terminateChars( |
michael@0 | 815 | tag, |
michael@0 | 816 | tagCapacity, |
michael@0 | 817 | 0, |
michael@0 | 818 | err); |
michael@0 | 819 | |
michael@0 | 820 | error: |
michael@0 | 821 | |
michael@0 | 822 | if (!U_FAILURE(*err)) { |
michael@0 | 823 | *err = U_ILLEGAL_ARGUMENT_ERROR; |
michael@0 | 824 | } |
michael@0 | 825 | |
michael@0 | 826 | return -1; |
michael@0 | 827 | } |
michael@0 | 828 | |
michael@0 | 829 | #define CHECK_TRAILING_VARIANT_SIZE(trailing, trailingLength) \ |
michael@0 | 830 | { int32_t count = 0; \ |
michael@0 | 831 | int32_t i; \ |
michael@0 | 832 | for (i = 0; i < trailingLength; i++) { \ |
michael@0 | 833 | if (trailing[i] == '-' || trailing[i] == '_') { \ |
michael@0 | 834 | count = 0; \ |
michael@0 | 835 | if (count > 8) { \ |
michael@0 | 836 | goto error; \ |
michael@0 | 837 | } \ |
michael@0 | 838 | } else if (trailing[i] == '@') { \ |
michael@0 | 839 | break; \ |
michael@0 | 840 | } else if (count > 8) { \ |
michael@0 | 841 | goto error; \ |
michael@0 | 842 | } else { \ |
michael@0 | 843 | count++; \ |
michael@0 | 844 | } \ |
michael@0 | 845 | } \ |
michael@0 | 846 | } |
michael@0 | 847 | |
michael@0 | 848 | static int32_t |
michael@0 | 849 | _uloc_addLikelySubtags(const char* localeID, |
michael@0 | 850 | char* maximizedLocaleID, |
michael@0 | 851 | int32_t maximizedLocaleIDCapacity, |
michael@0 | 852 | UErrorCode* err) |
michael@0 | 853 | { |
michael@0 | 854 | char lang[ULOC_LANG_CAPACITY]; |
michael@0 | 855 | int32_t langLength = sizeof(lang); |
michael@0 | 856 | char script[ULOC_SCRIPT_CAPACITY]; |
michael@0 | 857 | int32_t scriptLength = sizeof(script); |
michael@0 | 858 | char region[ULOC_COUNTRY_CAPACITY]; |
michael@0 | 859 | int32_t regionLength = sizeof(region); |
michael@0 | 860 | const char* trailing = ""; |
michael@0 | 861 | int32_t trailingLength = 0; |
michael@0 | 862 | int32_t trailingIndex = 0; |
michael@0 | 863 | int32_t resultLength = 0; |
michael@0 | 864 | |
michael@0 | 865 | if(U_FAILURE(*err)) { |
michael@0 | 866 | goto error; |
michael@0 | 867 | } |
michael@0 | 868 | else if (localeID == NULL || |
michael@0 | 869 | maximizedLocaleID == NULL || |
michael@0 | 870 | maximizedLocaleIDCapacity <= 0) { |
michael@0 | 871 | goto error; |
michael@0 | 872 | } |
michael@0 | 873 | |
michael@0 | 874 | trailingIndex = parseTagString( |
michael@0 | 875 | localeID, |
michael@0 | 876 | lang, |
michael@0 | 877 | &langLength, |
michael@0 | 878 | script, |
michael@0 | 879 | &scriptLength, |
michael@0 | 880 | region, |
michael@0 | 881 | ®ionLength, |
michael@0 | 882 | err); |
michael@0 | 883 | if(U_FAILURE(*err)) { |
michael@0 | 884 | /* Overflow indicates an illegal argument error */ |
michael@0 | 885 | if (*err == U_BUFFER_OVERFLOW_ERROR) { |
michael@0 | 886 | *err = U_ILLEGAL_ARGUMENT_ERROR; |
michael@0 | 887 | } |
michael@0 | 888 | |
michael@0 | 889 | goto error; |
michael@0 | 890 | } |
michael@0 | 891 | |
michael@0 | 892 | /* Find the length of the trailing portion. */ |
michael@0 | 893 | while (_isIDSeparator(localeID[trailingIndex])) { |
michael@0 | 894 | trailingIndex++; |
michael@0 | 895 | } |
michael@0 | 896 | trailing = &localeID[trailingIndex]; |
michael@0 | 897 | trailingLength = (int32_t)uprv_strlen(trailing); |
michael@0 | 898 | |
michael@0 | 899 | CHECK_TRAILING_VARIANT_SIZE(trailing, trailingLength); |
michael@0 | 900 | |
michael@0 | 901 | resultLength = |
michael@0 | 902 | createLikelySubtagsString( |
michael@0 | 903 | lang, |
michael@0 | 904 | langLength, |
michael@0 | 905 | script, |
michael@0 | 906 | scriptLength, |
michael@0 | 907 | region, |
michael@0 | 908 | regionLength, |
michael@0 | 909 | trailing, |
michael@0 | 910 | trailingLength, |
michael@0 | 911 | maximizedLocaleID, |
michael@0 | 912 | maximizedLocaleIDCapacity, |
michael@0 | 913 | err); |
michael@0 | 914 | |
michael@0 | 915 | if (resultLength == 0) { |
michael@0 | 916 | const int32_t localIDLength = (int32_t)uprv_strlen(localeID); |
michael@0 | 917 | |
michael@0 | 918 | /* |
michael@0 | 919 | * If we get here, we need to return localeID. |
michael@0 | 920 | */ |
michael@0 | 921 | uprv_memcpy( |
michael@0 | 922 | maximizedLocaleID, |
michael@0 | 923 | localeID, |
michael@0 | 924 | localIDLength <= maximizedLocaleIDCapacity ? |
michael@0 | 925 | localIDLength : maximizedLocaleIDCapacity); |
michael@0 | 926 | |
michael@0 | 927 | resultLength = |
michael@0 | 928 | u_terminateChars( |
michael@0 | 929 | maximizedLocaleID, |
michael@0 | 930 | maximizedLocaleIDCapacity, |
michael@0 | 931 | localIDLength, |
michael@0 | 932 | err); |
michael@0 | 933 | } |
michael@0 | 934 | |
michael@0 | 935 | return resultLength; |
michael@0 | 936 | |
michael@0 | 937 | error: |
michael@0 | 938 | |
michael@0 | 939 | if (!U_FAILURE(*err)) { |
michael@0 | 940 | *err = U_ILLEGAL_ARGUMENT_ERROR; |
michael@0 | 941 | } |
michael@0 | 942 | |
michael@0 | 943 | return -1; |
michael@0 | 944 | } |
michael@0 | 945 | |
michael@0 | 946 | static int32_t |
michael@0 | 947 | _uloc_minimizeSubtags(const char* localeID, |
michael@0 | 948 | char* minimizedLocaleID, |
michael@0 | 949 | int32_t minimizedLocaleIDCapacity, |
michael@0 | 950 | UErrorCode* err) |
michael@0 | 951 | { |
michael@0 | 952 | /** |
michael@0 | 953 | * ULOC_FULLNAME_CAPACITY will provide enough capacity |
michael@0 | 954 | * that we can build a string that contains the language, |
michael@0 | 955 | * script and region code without worrying about overrunning |
michael@0 | 956 | * the user-supplied buffer. |
michael@0 | 957 | **/ |
michael@0 | 958 | char maximizedTagBuffer[ULOC_FULLNAME_CAPACITY]; |
michael@0 | 959 | int32_t maximizedTagBufferLength = sizeof(maximizedTagBuffer); |
michael@0 | 960 | |
michael@0 | 961 | char lang[ULOC_LANG_CAPACITY]; |
michael@0 | 962 | int32_t langLength = sizeof(lang); |
michael@0 | 963 | char script[ULOC_SCRIPT_CAPACITY]; |
michael@0 | 964 | int32_t scriptLength = sizeof(script); |
michael@0 | 965 | char region[ULOC_COUNTRY_CAPACITY]; |
michael@0 | 966 | int32_t regionLength = sizeof(region); |
michael@0 | 967 | const char* trailing = ""; |
michael@0 | 968 | int32_t trailingLength = 0; |
michael@0 | 969 | int32_t trailingIndex = 0; |
michael@0 | 970 | |
michael@0 | 971 | if(U_FAILURE(*err)) { |
michael@0 | 972 | goto error; |
michael@0 | 973 | } |
michael@0 | 974 | else if (localeID == NULL || |
michael@0 | 975 | minimizedLocaleID == NULL || |
michael@0 | 976 | minimizedLocaleIDCapacity <= 0) { |
michael@0 | 977 | goto error; |
michael@0 | 978 | } |
michael@0 | 979 | |
michael@0 | 980 | trailingIndex = |
michael@0 | 981 | parseTagString( |
michael@0 | 982 | localeID, |
michael@0 | 983 | lang, |
michael@0 | 984 | &langLength, |
michael@0 | 985 | script, |
michael@0 | 986 | &scriptLength, |
michael@0 | 987 | region, |
michael@0 | 988 | ®ionLength, |
michael@0 | 989 | err); |
michael@0 | 990 | if(U_FAILURE(*err)) { |
michael@0 | 991 | |
michael@0 | 992 | /* Overflow indicates an illegal argument error */ |
michael@0 | 993 | if (*err == U_BUFFER_OVERFLOW_ERROR) { |
michael@0 | 994 | *err = U_ILLEGAL_ARGUMENT_ERROR; |
michael@0 | 995 | } |
michael@0 | 996 | |
michael@0 | 997 | goto error; |
michael@0 | 998 | } |
michael@0 | 999 | |
michael@0 | 1000 | /* Find the spot where the variants or the keywords begin, if any. */ |
michael@0 | 1001 | while (_isIDSeparator(localeID[trailingIndex])) { |
michael@0 | 1002 | trailingIndex++; |
michael@0 | 1003 | } |
michael@0 | 1004 | trailing = &localeID[trailingIndex]; |
michael@0 | 1005 | trailingLength = (int32_t)uprv_strlen(trailing); |
michael@0 | 1006 | |
michael@0 | 1007 | CHECK_TRAILING_VARIANT_SIZE(trailing, trailingLength); |
michael@0 | 1008 | |
michael@0 | 1009 | createTagString( |
michael@0 | 1010 | lang, |
michael@0 | 1011 | langLength, |
michael@0 | 1012 | script, |
michael@0 | 1013 | scriptLength, |
michael@0 | 1014 | region, |
michael@0 | 1015 | regionLength, |
michael@0 | 1016 | NULL, |
michael@0 | 1017 | 0, |
michael@0 | 1018 | maximizedTagBuffer, |
michael@0 | 1019 | maximizedTagBufferLength, |
michael@0 | 1020 | err); |
michael@0 | 1021 | if(U_FAILURE(*err)) { |
michael@0 | 1022 | goto error; |
michael@0 | 1023 | } |
michael@0 | 1024 | |
michael@0 | 1025 | /** |
michael@0 | 1026 | * First, we need to first get the maximization |
michael@0 | 1027 | * from AddLikelySubtags. |
michael@0 | 1028 | **/ |
michael@0 | 1029 | maximizedTagBufferLength = |
michael@0 | 1030 | uloc_addLikelySubtags( |
michael@0 | 1031 | maximizedTagBuffer, |
michael@0 | 1032 | maximizedTagBuffer, |
michael@0 | 1033 | maximizedTagBufferLength, |
michael@0 | 1034 | err); |
michael@0 | 1035 | |
michael@0 | 1036 | if(U_FAILURE(*err)) { |
michael@0 | 1037 | goto error; |
michael@0 | 1038 | } |
michael@0 | 1039 | |
michael@0 | 1040 | /** |
michael@0 | 1041 | * Start first with just the language. |
michael@0 | 1042 | **/ |
michael@0 | 1043 | { |
michael@0 | 1044 | char tagBuffer[ULOC_FULLNAME_CAPACITY]; |
michael@0 | 1045 | |
michael@0 | 1046 | const int32_t tagBufferLength = |
michael@0 | 1047 | createLikelySubtagsString( |
michael@0 | 1048 | lang, |
michael@0 | 1049 | langLength, |
michael@0 | 1050 | NULL, |
michael@0 | 1051 | 0, |
michael@0 | 1052 | NULL, |
michael@0 | 1053 | 0, |
michael@0 | 1054 | NULL, |
michael@0 | 1055 | 0, |
michael@0 | 1056 | tagBuffer, |
michael@0 | 1057 | sizeof(tagBuffer), |
michael@0 | 1058 | err); |
michael@0 | 1059 | |
michael@0 | 1060 | if(U_FAILURE(*err)) { |
michael@0 | 1061 | goto error; |
michael@0 | 1062 | } |
michael@0 | 1063 | else if (uprv_strnicmp( |
michael@0 | 1064 | maximizedTagBuffer, |
michael@0 | 1065 | tagBuffer, |
michael@0 | 1066 | tagBufferLength) == 0) { |
michael@0 | 1067 | |
michael@0 | 1068 | return createTagString( |
michael@0 | 1069 | lang, |
michael@0 | 1070 | langLength, |
michael@0 | 1071 | NULL, |
michael@0 | 1072 | 0, |
michael@0 | 1073 | NULL, |
michael@0 | 1074 | 0, |
michael@0 | 1075 | trailing, |
michael@0 | 1076 | trailingLength, |
michael@0 | 1077 | minimizedLocaleID, |
michael@0 | 1078 | minimizedLocaleIDCapacity, |
michael@0 | 1079 | err); |
michael@0 | 1080 | } |
michael@0 | 1081 | } |
michael@0 | 1082 | |
michael@0 | 1083 | /** |
michael@0 | 1084 | * Next, try the language and region. |
michael@0 | 1085 | **/ |
michael@0 | 1086 | if (regionLength > 0) { |
michael@0 | 1087 | |
michael@0 | 1088 | char tagBuffer[ULOC_FULLNAME_CAPACITY]; |
michael@0 | 1089 | |
michael@0 | 1090 | const int32_t tagBufferLength = |
michael@0 | 1091 | createLikelySubtagsString( |
michael@0 | 1092 | lang, |
michael@0 | 1093 | langLength, |
michael@0 | 1094 | NULL, |
michael@0 | 1095 | 0, |
michael@0 | 1096 | region, |
michael@0 | 1097 | regionLength, |
michael@0 | 1098 | NULL, |
michael@0 | 1099 | 0, |
michael@0 | 1100 | tagBuffer, |
michael@0 | 1101 | sizeof(tagBuffer), |
michael@0 | 1102 | err); |
michael@0 | 1103 | |
michael@0 | 1104 | if(U_FAILURE(*err)) { |
michael@0 | 1105 | goto error; |
michael@0 | 1106 | } |
michael@0 | 1107 | else if (uprv_strnicmp( |
michael@0 | 1108 | maximizedTagBuffer, |
michael@0 | 1109 | tagBuffer, |
michael@0 | 1110 | tagBufferLength) == 0) { |
michael@0 | 1111 | |
michael@0 | 1112 | return createTagString( |
michael@0 | 1113 | lang, |
michael@0 | 1114 | langLength, |
michael@0 | 1115 | NULL, |
michael@0 | 1116 | 0, |
michael@0 | 1117 | region, |
michael@0 | 1118 | regionLength, |
michael@0 | 1119 | trailing, |
michael@0 | 1120 | trailingLength, |
michael@0 | 1121 | minimizedLocaleID, |
michael@0 | 1122 | minimizedLocaleIDCapacity, |
michael@0 | 1123 | err); |
michael@0 | 1124 | } |
michael@0 | 1125 | } |
michael@0 | 1126 | |
michael@0 | 1127 | /** |
michael@0 | 1128 | * Finally, try the language and script. This is our last chance, |
michael@0 | 1129 | * since trying with all three subtags would only yield the |
michael@0 | 1130 | * maximal version that we already have. |
michael@0 | 1131 | **/ |
michael@0 | 1132 | if (scriptLength > 0 && regionLength > 0) { |
michael@0 | 1133 | char tagBuffer[ULOC_FULLNAME_CAPACITY]; |
michael@0 | 1134 | |
michael@0 | 1135 | const int32_t tagBufferLength = |
michael@0 | 1136 | createLikelySubtagsString( |
michael@0 | 1137 | lang, |
michael@0 | 1138 | langLength, |
michael@0 | 1139 | script, |
michael@0 | 1140 | scriptLength, |
michael@0 | 1141 | NULL, |
michael@0 | 1142 | 0, |
michael@0 | 1143 | NULL, |
michael@0 | 1144 | 0, |
michael@0 | 1145 | tagBuffer, |
michael@0 | 1146 | sizeof(tagBuffer), |
michael@0 | 1147 | err); |
michael@0 | 1148 | |
michael@0 | 1149 | if(U_FAILURE(*err)) { |
michael@0 | 1150 | goto error; |
michael@0 | 1151 | } |
michael@0 | 1152 | else if (uprv_strnicmp( |
michael@0 | 1153 | maximizedTagBuffer, |
michael@0 | 1154 | tagBuffer, |
michael@0 | 1155 | tagBufferLength) == 0) { |
michael@0 | 1156 | |
michael@0 | 1157 | return createTagString( |
michael@0 | 1158 | lang, |
michael@0 | 1159 | langLength, |
michael@0 | 1160 | script, |
michael@0 | 1161 | scriptLength, |
michael@0 | 1162 | NULL, |
michael@0 | 1163 | 0, |
michael@0 | 1164 | trailing, |
michael@0 | 1165 | trailingLength, |
michael@0 | 1166 | minimizedLocaleID, |
michael@0 | 1167 | minimizedLocaleIDCapacity, |
michael@0 | 1168 | err); |
michael@0 | 1169 | } |
michael@0 | 1170 | } |
michael@0 | 1171 | |
michael@0 | 1172 | { |
michael@0 | 1173 | /** |
michael@0 | 1174 | * If we got here, return the locale ID parameter. |
michael@0 | 1175 | **/ |
michael@0 | 1176 | const int32_t localeIDLength = (int32_t)uprv_strlen(localeID); |
michael@0 | 1177 | |
michael@0 | 1178 | uprv_memcpy( |
michael@0 | 1179 | minimizedLocaleID, |
michael@0 | 1180 | localeID, |
michael@0 | 1181 | localeIDLength <= minimizedLocaleIDCapacity ? |
michael@0 | 1182 | localeIDLength : minimizedLocaleIDCapacity); |
michael@0 | 1183 | |
michael@0 | 1184 | return u_terminateChars( |
michael@0 | 1185 | minimizedLocaleID, |
michael@0 | 1186 | minimizedLocaleIDCapacity, |
michael@0 | 1187 | localeIDLength, |
michael@0 | 1188 | err); |
michael@0 | 1189 | } |
michael@0 | 1190 | |
michael@0 | 1191 | error: |
michael@0 | 1192 | |
michael@0 | 1193 | if (!U_FAILURE(*err)) { |
michael@0 | 1194 | *err = U_ILLEGAL_ARGUMENT_ERROR; |
michael@0 | 1195 | } |
michael@0 | 1196 | |
michael@0 | 1197 | return -1; |
michael@0 | 1198 | |
michael@0 | 1199 | |
michael@0 | 1200 | } |
michael@0 | 1201 | |
michael@0 | 1202 | static UBool |
michael@0 | 1203 | do_canonicalize(const char* localeID, |
michael@0 | 1204 | char* buffer, |
michael@0 | 1205 | int32_t bufferCapacity, |
michael@0 | 1206 | UErrorCode* err) |
michael@0 | 1207 | { |
michael@0 | 1208 | uloc_canonicalize( |
michael@0 | 1209 | localeID, |
michael@0 | 1210 | buffer, |
michael@0 | 1211 | bufferCapacity, |
michael@0 | 1212 | err); |
michael@0 | 1213 | |
michael@0 | 1214 | if (*err == U_STRING_NOT_TERMINATED_WARNING || |
michael@0 | 1215 | *err == U_BUFFER_OVERFLOW_ERROR) { |
michael@0 | 1216 | *err = U_ILLEGAL_ARGUMENT_ERROR; |
michael@0 | 1217 | |
michael@0 | 1218 | return FALSE; |
michael@0 | 1219 | } |
michael@0 | 1220 | else if (U_FAILURE(*err)) { |
michael@0 | 1221 | |
michael@0 | 1222 | return FALSE; |
michael@0 | 1223 | } |
michael@0 | 1224 | else { |
michael@0 | 1225 | return TRUE; |
michael@0 | 1226 | } |
michael@0 | 1227 | } |
michael@0 | 1228 | |
michael@0 | 1229 | U_CAPI int32_t U_EXPORT2 |
michael@0 | 1230 | uloc_addLikelySubtags(const char* localeID, |
michael@0 | 1231 | char* maximizedLocaleID, |
michael@0 | 1232 | int32_t maximizedLocaleIDCapacity, |
michael@0 | 1233 | UErrorCode* err) |
michael@0 | 1234 | { |
michael@0 | 1235 | char localeBuffer[ULOC_FULLNAME_CAPACITY]; |
michael@0 | 1236 | |
michael@0 | 1237 | if (!do_canonicalize( |
michael@0 | 1238 | localeID, |
michael@0 | 1239 | localeBuffer, |
michael@0 | 1240 | sizeof(localeBuffer), |
michael@0 | 1241 | err)) { |
michael@0 | 1242 | return -1; |
michael@0 | 1243 | } |
michael@0 | 1244 | else { |
michael@0 | 1245 | return _uloc_addLikelySubtags( |
michael@0 | 1246 | localeBuffer, |
michael@0 | 1247 | maximizedLocaleID, |
michael@0 | 1248 | maximizedLocaleIDCapacity, |
michael@0 | 1249 | err); |
michael@0 | 1250 | } |
michael@0 | 1251 | } |
michael@0 | 1252 | |
michael@0 | 1253 | U_CAPI int32_t U_EXPORT2 |
michael@0 | 1254 | uloc_minimizeSubtags(const char* localeID, |
michael@0 | 1255 | char* minimizedLocaleID, |
michael@0 | 1256 | int32_t minimizedLocaleIDCapacity, |
michael@0 | 1257 | UErrorCode* err) |
michael@0 | 1258 | { |
michael@0 | 1259 | char localeBuffer[ULOC_FULLNAME_CAPACITY]; |
michael@0 | 1260 | |
michael@0 | 1261 | if (!do_canonicalize( |
michael@0 | 1262 | localeID, |
michael@0 | 1263 | localeBuffer, |
michael@0 | 1264 | sizeof(localeBuffer), |
michael@0 | 1265 | err)) { |
michael@0 | 1266 | return -1; |
michael@0 | 1267 | } |
michael@0 | 1268 | else { |
michael@0 | 1269 | return _uloc_minimizeSubtags( |
michael@0 | 1270 | localeBuffer, |
michael@0 | 1271 | minimizedLocaleID, |
michael@0 | 1272 | minimizedLocaleIDCapacity, |
michael@0 | 1273 | err); |
michael@0 | 1274 | } |
michael@0 | 1275 | } |