uresbund.cpp 123 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462
  1. // © 2016 and later: Unicode, Inc. and others.
  2. // License & terms of use: http://www.unicode.org/copyright.html
  3. /*
  4. ******************************************************************************
  5. * Copyright (C) 1997-2016, International Business Machines Corporation and
  6. * others. All Rights Reserved.
  7. ******************************************************************************
  8. *
  9. * File uresbund.cpp
  10. *
  11. * Modification History:
  12. *
  13. * Date Name Description
  14. * 04/01/97 aliu Creation.
  15. * 06/14/99 stephen Removed functions taking a filename suffix.
  16. * 07/20/99 stephen Changed for UResourceBundle typedef'd to void*
  17. * 11/09/99 weiv Added ures_getLocale()
  18. * March 2000 weiv Total overhaul - using data in DLLs
  19. * 06/20/2000 helena OS/400 port changes; mostly typecast.
  20. * 06/24/02 weiv Added support for resource sharing
  21. ******************************************************************************
  22. */
  23. #include "unicode/ures.h"
  24. #include "unicode/ustring.h"
  25. #include "unicode/ucnv.h"
  26. #include "charstr.h"
  27. #include "uresimp.h"
  28. #include "ustr_imp.h"
  29. #include "cwchar.h"
  30. #include "ucln_cmn.h"
  31. #include "cmemory.h"
  32. #include "cstring.h"
  33. #include "mutex.h"
  34. #include "uhash.h"
  35. #include "unicode/uenum.h"
  36. #include "uenumimp.h"
  37. #include "ulocimp.h"
  38. #include "umutex.h"
  39. #include "putilimp.h"
  40. #include "uassert.h"
  41. #include "uresdata.h"
  42. using namespace icu;
  43. /*
  44. Static cache for already opened resource bundles - mostly for keeping fallback info
  45. TODO: This cache should probably be removed when the deprecated code is
  46. completely removed.
  47. */
  48. static UHashtable *cache = nullptr;
  49. static icu::UInitOnce gCacheInitOnce {};
  50. static UMutex resbMutex;
  51. /* INTERNAL: hashes an entry */
  52. static int32_t U_CALLCONV hashEntry(const UHashTok parm) {
  53. UResourceDataEntry *b = (UResourceDataEntry *)parm.pointer;
  54. UHashTok namekey, pathkey;
  55. namekey.pointer = b->fName;
  56. pathkey.pointer = b->fPath;
  57. return uhash_hashChars(namekey)+37u*uhash_hashChars(pathkey);
  58. }
  59. /* INTERNAL: compares two entries */
  60. static UBool U_CALLCONV compareEntries(const UHashTok p1, const UHashTok p2) {
  61. UResourceDataEntry *b1 = (UResourceDataEntry *)p1.pointer;
  62. UResourceDataEntry *b2 = (UResourceDataEntry *)p2.pointer;
  63. UHashTok name1, name2, path1, path2;
  64. name1.pointer = b1->fName;
  65. name2.pointer = b2->fName;
  66. path1.pointer = b1->fPath;
  67. path2.pointer = b2->fPath;
  68. return (UBool)(uhash_compareChars(name1, name2) &&
  69. uhash_compareChars(path1, path2));
  70. }
  71. /**
  72. * Internal function, gets parts of locale name according
  73. * to the position of '_' character
  74. */
  75. static UBool chopLocale(char *name) {
  76. char *i = uprv_strrchr(name, '_');
  77. if(i != nullptr) {
  78. *i = '\0';
  79. return true;
  80. }
  81. return false;
  82. }
  83. static UBool hasVariant(const char* localeID) {
  84. UErrorCode err = U_ZERO_ERROR;
  85. int32_t variantLength = uloc_getVariant(localeID, nullptr, 0, &err);
  86. return variantLength != 0;
  87. }
  88. // This file contains the tables for doing locale fallback, which are generated
  89. // by the CLDR-to-ICU process directly from the CLDR data. This file should only
  90. // ever be included from here.
  91. #define INCLUDED_FROM_URESBUND_CPP
  92. #include "localefallback_data.h"
  93. static const char* performFallbackLookup(const char* key,
  94. const char* keyStrs,
  95. const char* valueStrs,
  96. const int32_t* lookupTable,
  97. int32_t lookupTableLength) {
  98. const int32_t* bottom = lookupTable;
  99. const int32_t* top = lookupTable + lookupTableLength;
  100. while (bottom < top) {
  101. // Effectively, divide by 2 and round down to an even index
  102. const int32_t* middle = bottom + (((top - bottom) / 4) * 2);
  103. const char* entryKey = &(keyStrs[*middle]);
  104. int32_t strcmpResult = uprv_strcmp(key, entryKey);
  105. if (strcmpResult == 0) {
  106. return &(valueStrs[middle[1]]);
  107. } else if (strcmpResult < 0) {
  108. top = middle;
  109. } else {
  110. bottom = middle + 2;
  111. }
  112. }
  113. return nullptr;
  114. }
  115. static CharString getDefaultScript(const CharString& language, const CharString& region) {
  116. const char* defaultScript = nullptr;
  117. UErrorCode err = U_ZERO_ERROR;
  118. // the default script will be "Latn" if we don't find the locale ID in the tables
  119. CharString result("Latn", err);
  120. // if we were passed both language and region, make them into a locale ID and look that up in the default
  121. // script table
  122. if (!region.isEmpty()) {
  123. CharString localeID;
  124. localeID.append(language, err).append("_", err).append(region, err);
  125. if (U_FAILURE(err)) {
  126. return result;
  127. }
  128. defaultScript = performFallbackLookup(localeID.data(), dsLocaleIDChars, scriptCodeChars, defaultScriptTable, UPRV_LENGTHOF(defaultScriptTable));
  129. }
  130. // if we didn't find anything, look up just the language in the default script table
  131. if (defaultScript == nullptr) {
  132. defaultScript = performFallbackLookup(language.data(), dsLocaleIDChars, scriptCodeChars, defaultScriptTable, UPRV_LENGTHOF(defaultScriptTable));
  133. }
  134. // if either lookup above succeeded, copy the result from "defaultScript" into "result"; otherwise, return "Latn"
  135. if (defaultScript != nullptr) {
  136. result.clear();
  137. result.append(defaultScript, err);
  138. }
  139. return result;
  140. }
  141. enum UResOpenType {
  142. /**
  143. * Open a resource bundle for the locale;
  144. * if there is not even a base language bundle, then fall back to the default locale;
  145. * if there is no bundle for that either, then load the root bundle.
  146. *
  147. * This is the default bundle loading behavior.
  148. */
  149. URES_OPEN_LOCALE_DEFAULT_ROOT,
  150. // TODO: ICU ticket #11271 "consistent default locale across locale trees"
  151. // Add an option to look at the main locale tree for whether to
  152. // fall back to root directly (if the locale has main data) or
  153. // fall back to the default locale first (if the locale does not even have main data).
  154. /**
  155. * Open a resource bundle for the locale;
  156. * if there is not even a base language bundle, then load the root bundle;
  157. * never fall back to the default locale.
  158. *
  159. * This is used for algorithms that have good pan-Unicode default behavior,
  160. * such as case mappings, collation, and segmentation (BreakIterator).
  161. */
  162. URES_OPEN_LOCALE_ROOT,
  163. /**
  164. * Open a resource bundle for the exact bundle name as requested;
  165. * no fallbacks, do not load parent bundles.
  166. *
  167. * This is used for supplemental (non-locale) data.
  168. */
  169. URES_OPEN_DIRECT
  170. };
  171. typedef enum UResOpenType UResOpenType;
  172. /**
  173. * Internal function, determines the search path for resource bundle files.
  174. * Currently, this function is used only by findFirstExisting() to help search for resource bundle files when a bundle for the specified
  175. * locale doesn't exist. The code that supports inheritance of resources between existing resource bundle files continues to
  176. * use chopLocale() below.
  177. * @param name In-out parameter: On input, the locale ID to get a parent locale ID for (this is a locale's base name, without keywords); on output, the
  178. * requested parent locale ID.
  179. * @param origName The original locale ID the caller of findFirstExisting() requested. This is the same as `name` on the first call to this function,
  180. * but as findFirstExisting() ascends the resource bundle's parent tree, this parameter will continue to be the original locale ID requested.
  181. */
  182. static bool getParentLocaleID(char *name, const char *origName, UResOpenType openType) {
  183. // early out if the locale ID has a variant code or ends with _
  184. size_t nameLen = uprv_strlen(name);
  185. if (!nameLen || name[nameLen - 1] == '_' || hasVariant(name)) {
  186. return chopLocale(name);
  187. }
  188. UErrorCode err = U_ZERO_ERROR;
  189. const char* tempNamePtr = name;
  190. CharString language = ulocimp_getLanguage(tempNamePtr, &tempNamePtr, err);
  191. if (*tempNamePtr == '_') {
  192. ++tempNamePtr;
  193. }
  194. CharString script = ulocimp_getScript(tempNamePtr, &tempNamePtr, err);
  195. if (*tempNamePtr == '_') {
  196. ++tempNamePtr;
  197. }
  198. CharString region = ulocimp_getCountry(tempNamePtr, &tempNamePtr, err);
  199. CharString workingLocale;
  200. if (U_FAILURE(err)) {
  201. // hopefully this never happens...
  202. return chopLocale(name);
  203. }
  204. // if the open type is URES_OPEN_LOCALE_DEFAULT_ROOT, first look the locale ID up in the parent locale table;
  205. // if that table specifies a parent for it, return that (we don't do this for the other open types-- if we're not
  206. // falling back through the system default locale, we also want to do straight truncation fallback instead
  207. // of looking things up in the parent locale table-- see https://www.unicode.org/reports/tr35/tr35.html#Parent_Locales:
  208. // "Collation data, however, is an exception...")
  209. if (openType == URES_OPEN_LOCALE_DEFAULT_ROOT) {
  210. const char* parentID = performFallbackLookup(name, parentLocaleChars, parentLocaleChars, parentLocaleTable, UPRV_LENGTHOF(parentLocaleTable));
  211. if (parentID != nullptr) {
  212. uprv_strcpy(name, parentID);
  213. return true;
  214. }
  215. }
  216. // if it's not in the parent locale table, figure out the fallback script algorithmically
  217. // (see CLDR-15265 for an explanation of the algorithm)
  218. if (!script.isEmpty() && !region.isEmpty()) {
  219. // if "name" has both script and region, is the script the default script?
  220. // - if so, remove it and keep the region
  221. // - if not, remove the region and keep the script
  222. if (getDefaultScript(language, region) == script.toStringPiece()) {
  223. workingLocale.append(language, err).append("_", err).append(region, err);
  224. } else {
  225. workingLocale.append(language, err).append("_", err).append(script, err);
  226. }
  227. } else if (!region.isEmpty()) {
  228. // if "name" has region but not script, did the original locale ID specify a script?
  229. // - if yes, replace the region with the script from the original locale ID
  230. // - if no, replace the region with the default script for that language and region
  231. UErrorCode err = U_ZERO_ERROR;
  232. tempNamePtr = origName;
  233. CharString origNameLanguage = ulocimp_getLanguage(tempNamePtr, &tempNamePtr, err);
  234. if (*tempNamePtr == '_') {
  235. ++tempNamePtr;
  236. }
  237. CharString origNameScript = ulocimp_getScript(origName, nullptr, err);
  238. if (!origNameScript.isEmpty()) {
  239. workingLocale.append(language, err).append("_", err).append(origNameScript, err);
  240. } else {
  241. workingLocale.append(language, err).append("_", err).append(getDefaultScript(language, region), err);
  242. }
  243. } else if (!script.isEmpty()) {
  244. // if "name" has script but not region (and our open type if URES_OPEN_LOCALE_DEFAULT_ROOT), is the script
  245. // the default script for the language?
  246. // - if so, remove it from the locale ID
  247. // - if not, return false to continue up the chain
  248. // (we don't do this for other open types for the same reason we don't look things up in the parent
  249. // locale table for other open types-- see the reference to UTS #35 above)
  250. if (openType != URES_OPEN_LOCALE_DEFAULT_ROOT || getDefaultScript(language, CharString()) == script.toStringPiece()) {
  251. workingLocale.append(language, err);
  252. } else {
  253. return false;
  254. }
  255. } else {
  256. // if "name" just contains a language code, return false so the calling code falls back to "root"
  257. return false;
  258. }
  259. if (U_SUCCESS(err) && !workingLocale.isEmpty()) {
  260. uprv_strcpy(name, workingLocale.data());
  261. return true;
  262. } else {
  263. return false;
  264. }
  265. }
  266. /**
  267. * Called to check whether a name without '_' needs to be checked for a parent.
  268. * Some code had assumed that locale IDs with '_' could not have a non-root parent.
  269. * We may want a better way of doing this.
  270. */
  271. static UBool mayHaveParent(char *name) {
  272. return (name[0] != 0 && uprv_strstr("nb nn",name) != nullptr);
  273. }
  274. /**
  275. * Internal function
  276. */
  277. static void entryIncrease(UResourceDataEntry *entry) {
  278. Mutex lock(&resbMutex);
  279. entry->fCountExisting++;
  280. while(entry->fParent != nullptr) {
  281. entry = entry->fParent;
  282. entry->fCountExisting++;
  283. }
  284. }
  285. /**
  286. * Internal function. Tries to find a resource in given Resource
  287. * Bundle, as well as in its parents
  288. */
  289. static UResourceDataEntry *getFallbackData(
  290. const UResourceBundle *resBundle,
  291. const char **resTag, Resource *res, UErrorCode *status) {
  292. UResourceDataEntry *dataEntry = resBundle->fData;
  293. int32_t indexR = -1;
  294. int32_t i = 0;
  295. *res = RES_BOGUS;
  296. if(dataEntry == nullptr) {
  297. *status = U_MISSING_RESOURCE_ERROR;
  298. return nullptr;
  299. }
  300. if(dataEntry->fBogus == U_ZERO_ERROR) { /* if this resource is real, */
  301. *res = res_getTableItemByKey(&(dataEntry->fData), dataEntry->fData.rootRes, &indexR, resTag); /* try to get data from there */
  302. i++;
  303. }
  304. if(resBundle->fHasFallback) {
  305. // Otherwise, we'll look in parents.
  306. while(*res == RES_BOGUS && dataEntry->fParent != nullptr) {
  307. dataEntry = dataEntry->fParent;
  308. if(dataEntry->fBogus == U_ZERO_ERROR) {
  309. i++;
  310. *res = res_getTableItemByKey(&(dataEntry->fData), dataEntry->fData.rootRes, &indexR, resTag);
  311. }
  312. }
  313. }
  314. if(*res == RES_BOGUS) {
  315. // If the resource is not found, we need to give an error.
  316. *status = U_MISSING_RESOURCE_ERROR;
  317. return nullptr;
  318. }
  319. // If the resource is found in parents, we need to adjust the error.
  320. if(i>1) {
  321. if(uprv_strcmp(dataEntry->fName, uloc_getDefault())==0 || uprv_strcmp(dataEntry->fName, kRootLocaleName)==0) {
  322. *status = U_USING_DEFAULT_WARNING;
  323. } else {
  324. *status = U_USING_FALLBACK_WARNING;
  325. }
  326. }
  327. return dataEntry;
  328. }
  329. static void
  330. free_entry(UResourceDataEntry *entry) {
  331. UResourceDataEntry *alias;
  332. res_unload(&(entry->fData));
  333. if(entry->fName != nullptr && entry->fName != entry->fNameBuffer) {
  334. uprv_free(entry->fName);
  335. }
  336. if(entry->fPath != nullptr) {
  337. uprv_free(entry->fPath);
  338. }
  339. if(entry->fPool != nullptr) {
  340. --entry->fPool->fCountExisting;
  341. }
  342. alias = entry->fAlias;
  343. if(alias != nullptr) {
  344. while(alias->fAlias != nullptr) {
  345. alias = alias->fAlias;
  346. }
  347. --alias->fCountExisting;
  348. }
  349. uprv_free(entry);
  350. }
  351. /* Works just like ucnv_flushCache() */
  352. static int32_t ures_flushCache()
  353. {
  354. UResourceDataEntry *resB;
  355. int32_t pos;
  356. int32_t rbDeletedNum = 0;
  357. const UHashElement *e;
  358. UBool deletedMore;
  359. /*if shared data hasn't even been lazy evaluated yet
  360. * return 0
  361. */
  362. Mutex lock(&resbMutex);
  363. if (cache == nullptr) {
  364. return 0;
  365. }
  366. do {
  367. deletedMore = false;
  368. /*creates an enumeration to iterate through every element in the table */
  369. pos = UHASH_FIRST;
  370. while ((e = uhash_nextElement(cache, &pos)) != nullptr)
  371. {
  372. resB = (UResourceDataEntry *) e->value.pointer;
  373. /* Deletes only if reference counter == 0
  374. * Don't worry about the children of this node.
  375. * Those will eventually get deleted too, if not already.
  376. * Don't worry about the parents of this node.
  377. * Those will eventually get deleted too, if not already.
  378. */
  379. /* 04/05/2002 [weiv] fCountExisting should now be accurate. If it's not zero, that means that */
  380. /* some resource bundles are still open somewhere. */
  381. if (resB->fCountExisting == 0) {
  382. rbDeletedNum++;
  383. deletedMore = true;
  384. uhash_removeElement(cache, e);
  385. free_entry(resB);
  386. }
  387. }
  388. /*
  389. * Do it again to catch bundles (aliases, pool bundle) whose fCountExisting
  390. * got decremented by free_entry().
  391. */
  392. } while(deletedMore);
  393. return rbDeletedNum;
  394. }
  395. #ifdef URES_DEBUG
  396. #include <stdio.h>
  397. U_CAPI UBool U_EXPORT2 ures_dumpCacheContents() {
  398. UBool cacheNotEmpty = false;
  399. int32_t pos = UHASH_FIRST;
  400. const UHashElement *e;
  401. UResourceDataEntry *resB;
  402. Mutex lock(&resbMutex);
  403. if (cache == nullptr) {
  404. fprintf(stderr,"%s:%d: RB Cache is nullptr.\n", __FILE__, __LINE__);
  405. return false;
  406. }
  407. while ((e = uhash_nextElement(cache, &pos)) != nullptr) {
  408. cacheNotEmpty=true;
  409. resB = (UResourceDataEntry *) e->value.pointer;
  410. fprintf(stderr,"%s:%d: RB Cache: Entry @0x%p, refcount %d, name %s:%s. Pool 0x%p, alias 0x%p, parent 0x%p\n",
  411. __FILE__, __LINE__,
  412. (void*)resB, resB->fCountExisting,
  413. resB->fName?resB->fName:"nullptr",
  414. resB->fPath?resB->fPath:"nullptr",
  415. (void*)resB->fPool,
  416. (void*)resB->fAlias,
  417. (void*)resB->fParent);
  418. }
  419. fprintf(stderr,"%s:%d: RB Cache still contains %d items.\n", __FILE__, __LINE__, uhash_count(cache));
  420. return cacheNotEmpty;
  421. }
  422. #endif
  423. static UBool U_CALLCONV ures_cleanup()
  424. {
  425. if (cache != nullptr) {
  426. ures_flushCache();
  427. uhash_close(cache);
  428. cache = nullptr;
  429. }
  430. gCacheInitOnce.reset();
  431. return true;
  432. }
  433. /** INTERNAL: Initializes the cache for resources */
  434. static void U_CALLCONV createCache(UErrorCode &status) {
  435. U_ASSERT(cache == nullptr);
  436. cache = uhash_open(hashEntry, compareEntries, nullptr, &status);
  437. ucln_common_registerCleanup(UCLN_COMMON_URES, ures_cleanup);
  438. }
  439. static void initCache(UErrorCode *status) {
  440. umtx_initOnce(gCacheInitOnce, &createCache, *status);
  441. }
  442. /** INTERNAL: sets the name (locale) of the resource bundle to given name */
  443. static void setEntryName(UResourceDataEntry *res, const char *name, UErrorCode *status) {
  444. int32_t len = (int32_t)uprv_strlen(name);
  445. if(res->fName != nullptr && res->fName != res->fNameBuffer) {
  446. uprv_free(res->fName);
  447. }
  448. if (len < (int32_t)sizeof(res->fNameBuffer)) {
  449. res->fName = res->fNameBuffer;
  450. }
  451. else {
  452. res->fName = (char *)uprv_malloc(len+1);
  453. }
  454. if(res->fName == nullptr) {
  455. *status = U_MEMORY_ALLOCATION_ERROR;
  456. } else {
  457. uprv_strcpy(res->fName, name);
  458. }
  459. }
  460. static UResourceDataEntry *
  461. getPoolEntry(const char *path, UErrorCode *status);
  462. /**
  463. * INTERNAL: Inits and opens an entry from a data DLL.
  464. * CAUTION: resbMutex must be locked when calling this function.
  465. */
  466. static UResourceDataEntry *init_entry(const char *localeID, const char *path, UErrorCode *status) {
  467. UResourceDataEntry *r = nullptr;
  468. UResourceDataEntry find;
  469. /*int32_t hashValue;*/
  470. const char *name;
  471. char aliasName[100] = { 0 };
  472. int32_t aliasLen = 0;
  473. /*UBool isAlias = false;*/
  474. /*UHashTok hashkey; */
  475. if(U_FAILURE(*status)) {
  476. return nullptr;
  477. }
  478. /* here we try to deduce the right locale name */
  479. if(localeID == nullptr) { /* if localeID is nullptr, we're trying to open default locale */
  480. name = uloc_getDefault();
  481. } else if(*localeID == 0) { /* if localeID is "" then we try to open root locale */
  482. name = kRootLocaleName;
  483. } else { /* otherwise, we'll open what we're given */
  484. name = localeID;
  485. }
  486. find.fName = (char *)name;
  487. find.fPath = (char *)path;
  488. /* calculate the hash value of the entry */
  489. /*hashkey.pointer = (void *)&find;*/
  490. /*hashValue = hashEntry(hashkey);*/
  491. /* check to see if we already have this entry */
  492. r = (UResourceDataEntry *)uhash_get(cache, &find);
  493. if(r == nullptr) {
  494. /* if the entry is not yet in the hash table, we'll try to construct a new one */
  495. r = (UResourceDataEntry *) uprv_malloc(sizeof(UResourceDataEntry));
  496. if(r == nullptr) {
  497. *status = U_MEMORY_ALLOCATION_ERROR;
  498. return nullptr;
  499. }
  500. uprv_memset(r, 0, sizeof(UResourceDataEntry));
  501. /*r->fHashKey = hashValue;*/
  502. setEntryName(r, name, status);
  503. if (U_FAILURE(*status)) {
  504. uprv_free(r);
  505. return nullptr;
  506. }
  507. if(path != nullptr) {
  508. r->fPath = (char *)uprv_strdup(path);
  509. if(r->fPath == nullptr) {
  510. *status = U_MEMORY_ALLOCATION_ERROR;
  511. uprv_free(r);
  512. return nullptr;
  513. }
  514. }
  515. /* this is the actual loading */
  516. res_load(&(r->fData), r->fPath, r->fName, status);
  517. if (U_FAILURE(*status)) {
  518. /* if we failed to load due to an out-of-memory error, exit early. */
  519. if (*status == U_MEMORY_ALLOCATION_ERROR) {
  520. uprv_free(r);
  521. return nullptr;
  522. }
  523. /* we have no such entry in dll, so it will always use fallback */
  524. *status = U_USING_FALLBACK_WARNING;
  525. r->fBogus = U_USING_FALLBACK_WARNING;
  526. } else { /* if we have a regular entry */
  527. Resource aliasres;
  528. if (r->fData.usesPoolBundle) {
  529. r->fPool = getPoolEntry(r->fPath, status);
  530. if (U_SUCCESS(*status)) {
  531. const int32_t *poolIndexes = r->fPool->fData.pRoot + 1;
  532. if(r->fData.pRoot[1 + URES_INDEX_POOL_CHECKSUM] == poolIndexes[URES_INDEX_POOL_CHECKSUM]) {
  533. r->fData.poolBundleKeys = (const char *)(poolIndexes + (poolIndexes[URES_INDEX_LENGTH] & 0xff));
  534. r->fData.poolBundleStrings = r->fPool->fData.p16BitUnits;
  535. } else {
  536. r->fBogus = *status = U_INVALID_FORMAT_ERROR;
  537. }
  538. } else {
  539. r->fBogus = *status;
  540. }
  541. }
  542. if (U_SUCCESS(*status)) {
  543. /* handle the alias by trying to get out the %%Alias tag.*/
  544. /* We'll try to get alias string from the bundle */
  545. aliasres = res_getResource(&(r->fData), "%%ALIAS");
  546. if (aliasres != RES_BOGUS) {
  547. // No tracing: called during initial data loading
  548. const char16_t *alias = res_getStringNoTrace(&(r->fData), aliasres, &aliasLen);
  549. if(alias != nullptr && aliasLen > 0) { /* if there is actual alias - unload and load new data */
  550. u_UCharsToChars(alias, aliasName, aliasLen+1);
  551. r->fAlias = init_entry(aliasName, path, status);
  552. }
  553. }
  554. }
  555. }
  556. {
  557. UResourceDataEntry *oldR = nullptr;
  558. if((oldR = (UResourceDataEntry *)uhash_get(cache, r)) == nullptr) { /* if the data is not cached */
  559. /* just insert it in the cache */
  560. UErrorCode cacheStatus = U_ZERO_ERROR;
  561. uhash_put(cache, (void *)r, r, &cacheStatus);
  562. if (U_FAILURE(cacheStatus)) {
  563. *status = cacheStatus;
  564. free_entry(r);
  565. r = nullptr;
  566. }
  567. } else {
  568. /* somebody have already inserted it while we were working, discard newly opened data */
  569. /* Also, we could get here IF we opened an alias */
  570. free_entry(r);
  571. r = oldR;
  572. }
  573. }
  574. }
  575. if(r != nullptr) {
  576. /* return the real bundle */
  577. while(r->fAlias != nullptr) {
  578. r = r->fAlias;
  579. }
  580. r->fCountExisting++; /* we increase its reference count */
  581. /* if the resource has a warning */
  582. /* we don't want to overwrite a status with no error */
  583. if(r->fBogus != U_ZERO_ERROR && U_SUCCESS(*status)) {
  584. *status = r->fBogus; /* set the returning status */
  585. }
  586. }
  587. return r;
  588. }
  589. static UResourceDataEntry *
  590. getPoolEntry(const char *path, UErrorCode *status) {
  591. UResourceDataEntry *poolBundle = init_entry(kPoolBundleName, path, status);
  592. if( U_SUCCESS(*status) &&
  593. (poolBundle == nullptr || poolBundle->fBogus != U_ZERO_ERROR || !poolBundle->fData.isPoolBundle)
  594. ) {
  595. *status = U_INVALID_FORMAT_ERROR;
  596. }
  597. return poolBundle;
  598. }
  599. /* INTERNAL: */
  600. /* CAUTION: resbMutex must be locked when calling this function! */
  601. static UResourceDataEntry *
  602. findFirstExisting(const char* path, char* name, const char* defaultLocale, UResOpenType openType,
  603. UBool *isRoot, UBool *foundParent, UBool *isDefault, UErrorCode* status) {
  604. UResourceDataEntry *r = nullptr;
  605. UBool hasRealData = false;
  606. *foundParent = true; /* we're starting with a fresh name */
  607. char origName[ULOC_FULLNAME_CAPACITY];
  608. uprv_strcpy(origName, name);
  609. while(*foundParent && !hasRealData) {
  610. r = init_entry(name, path, status);
  611. /* Null pointer test */
  612. if (U_FAILURE(*status)) {
  613. return nullptr;
  614. }
  615. *isDefault = (UBool)(uprv_strncmp(name, defaultLocale, uprv_strlen(name)) == 0);
  616. hasRealData = (UBool)(r->fBogus == U_ZERO_ERROR);
  617. if(!hasRealData) {
  618. /* this entry is not real. We will discard it. */
  619. /* However, the parent line for this entry is */
  620. /* not to be used - as there might be parent */
  621. /* lines in cache from previous openings that */
  622. /* are not updated yet. */
  623. r->fCountExisting--;
  624. /*entryCloseInt(r);*/
  625. r = nullptr;
  626. *status = U_USING_FALLBACK_WARNING;
  627. } else {
  628. uprv_strcpy(name, r->fName); /* this is needed for supporting aliases */
  629. }
  630. *isRoot = (UBool)(uprv_strcmp(name, kRootLocaleName) == 0);
  631. /*Fallback data stuff*/
  632. if (!hasRealData) {
  633. *foundParent = getParentLocaleID(name, origName, openType);
  634. } else {
  635. // we've already found a real resource file; what we return to the caller is the parent
  636. // locale ID for inheritance, which should come from chopLocale(), not getParentLocaleID()
  637. *foundParent = chopLocale(name);
  638. }
  639. if (*foundParent && *name == '\0') {
  640. uprv_strcpy(name, "und");
  641. }
  642. }
  643. return r;
  644. }
  645. static void ures_setIsStackObject( UResourceBundle* resB, UBool state) {
  646. if(state) {
  647. resB->fMagic1 = 0;
  648. resB->fMagic2 = 0;
  649. } else {
  650. resB->fMagic1 = MAGIC1;
  651. resB->fMagic2 = MAGIC2;
  652. }
  653. }
  654. static UBool ures_isStackObject(const UResourceBundle* resB) {
  655. return((resB->fMagic1 == MAGIC1 && resB->fMagic2 == MAGIC2)?false:true);
  656. }
  657. U_CFUNC void ures_initStackObject(UResourceBundle* resB) {
  658. uprv_memset(resB, 0, sizeof(UResourceBundle));
  659. ures_setIsStackObject(resB, true);
  660. }
  661. U_NAMESPACE_BEGIN
  662. StackUResourceBundle::StackUResourceBundle() {
  663. ures_initStackObject(&bundle);
  664. }
  665. StackUResourceBundle::~StackUResourceBundle() {
  666. ures_close(&bundle);
  667. }
  668. U_NAMESPACE_END
  669. static UBool // returns U_SUCCESS(*status)
  670. loadParentsExceptRoot(UResourceDataEntry *&t1,
  671. char name[], int32_t nameCapacity,
  672. UBool usingUSRData, char usrDataPath[], UErrorCode *status) {
  673. if (U_FAILURE(*status)) { return false; }
  674. UBool checkParent = true;
  675. while (checkParent && t1->fParent == nullptr && !t1->fData.noFallback &&
  676. res_getResource(&t1->fData,"%%ParentIsRoot") == RES_BOGUS) {
  677. Resource parentRes = res_getResource(&t1->fData, "%%Parent");
  678. if (parentRes != RES_BOGUS) { // An explicit parent was found.
  679. int32_t parentLocaleLen = 0;
  680. // No tracing: called during initial data loading
  681. const char16_t *parentLocaleName = res_getStringNoTrace(&(t1->fData), parentRes, &parentLocaleLen);
  682. if(parentLocaleName != nullptr && 0 < parentLocaleLen && parentLocaleLen < nameCapacity) {
  683. u_UCharsToChars(parentLocaleName, name, parentLocaleLen + 1);
  684. if (uprv_strcmp(name, kRootLocaleName) == 0) {
  685. return true;
  686. }
  687. }
  688. }
  689. // Insert regular parents.
  690. UErrorCode parentStatus = U_ZERO_ERROR;
  691. UResourceDataEntry *t2 = init_entry(name, t1->fPath, &parentStatus);
  692. if (U_FAILURE(parentStatus)) {
  693. *status = parentStatus;
  694. return false;
  695. }
  696. UResourceDataEntry *u2 = nullptr;
  697. UErrorCode usrStatus = U_ZERO_ERROR;
  698. if (usingUSRData) { // This code inserts user override data into the inheritance chain.
  699. u2 = init_entry(name, usrDataPath, &usrStatus);
  700. // If we failed due to out-of-memory, report that to the caller and exit early.
  701. if (usrStatus == U_MEMORY_ALLOCATION_ERROR) {
  702. *status = usrStatus;
  703. return false;
  704. }
  705. }
  706. if (usingUSRData && U_SUCCESS(usrStatus) && u2->fBogus == U_ZERO_ERROR) {
  707. t1->fParent = u2;
  708. u2->fParent = t2;
  709. } else {
  710. t1->fParent = t2;
  711. if (usingUSRData) {
  712. // The USR override data wasn't found, set it to be deleted.
  713. u2->fCountExisting = 0;
  714. }
  715. }
  716. t1 = t2;
  717. checkParent = chopLocale(name) || mayHaveParent(name);
  718. }
  719. return true;
  720. }
  721. static UBool // returns U_SUCCESS(*status)
  722. insertRootBundle(UResourceDataEntry *&t1, UErrorCode *status) {
  723. if (U_FAILURE(*status)) { return false; }
  724. UErrorCode parentStatus = U_ZERO_ERROR;
  725. UResourceDataEntry *t2 = init_entry(kRootLocaleName, t1->fPath, &parentStatus);
  726. if (U_FAILURE(parentStatus)) {
  727. *status = parentStatus;
  728. return false;
  729. }
  730. t1->fParent = t2;
  731. t1 = t2;
  732. return true;
  733. }
  734. static UResourceDataEntry *entryOpen(const char* path, const char* localeID,
  735. UResOpenType openType, UErrorCode* status) {
  736. U_ASSERT(openType != URES_OPEN_DIRECT);
  737. UErrorCode intStatus = U_ZERO_ERROR;
  738. UResourceDataEntry *r = nullptr;
  739. UResourceDataEntry *t1 = nullptr;
  740. UBool isDefault = false;
  741. UBool isRoot = false;
  742. UBool hasRealData = false;
  743. UBool hasChopped = true;
  744. UBool usingUSRData = U_USE_USRDATA && ( path == nullptr || uprv_strncmp(path,U_ICUDATA_NAME,8) == 0);
  745. char name[ULOC_FULLNAME_CAPACITY];
  746. char usrDataPath[96];
  747. initCache(status);
  748. if(U_FAILURE(*status)) {
  749. return nullptr;
  750. }
  751. uprv_strncpy(name, localeID, sizeof(name) - 1);
  752. name[sizeof(name) - 1] = 0;
  753. if ( usingUSRData ) {
  754. if ( path == nullptr ) {
  755. uprv_strcpy(usrDataPath, U_USRDATA_NAME);
  756. } else {
  757. uprv_strncpy(usrDataPath, path, sizeof(usrDataPath) - 1);
  758. usrDataPath[0] = 'u';
  759. usrDataPath[1] = 's';
  760. usrDataPath[2] = 'r';
  761. usrDataPath[sizeof(usrDataPath) - 1] = 0;
  762. }
  763. }
  764. // Note: We need to query the default locale *before* locking resbMutex.
  765. const char *defaultLocale = uloc_getDefault();
  766. Mutex lock(&resbMutex); // Lock resbMutex until the end of this function.
  767. /* We're going to skip all the locales that do not have any data */
  768. r = findFirstExisting(path, name, defaultLocale, openType, &isRoot, &hasChopped, &isDefault, &intStatus);
  769. // If we failed due to out-of-memory, report the failure and exit early.
  770. if (intStatus == U_MEMORY_ALLOCATION_ERROR) {
  771. *status = intStatus;
  772. goto finish;
  773. }
  774. if(r != nullptr) { /* if there is one real locale, we can look for parents. */
  775. t1 = r;
  776. hasRealData = true;
  777. if ( usingUSRData ) { /* This code inserts user override data into the inheritance chain */
  778. UErrorCode usrStatus = U_ZERO_ERROR;
  779. UResourceDataEntry *u1 = init_entry(t1->fName, usrDataPath, &usrStatus);
  780. // If we failed due to out-of-memory, report the failure and exit early.
  781. if (intStatus == U_MEMORY_ALLOCATION_ERROR) {
  782. *status = intStatus;
  783. goto finish;
  784. }
  785. if ( u1 != nullptr ) {
  786. if(u1->fBogus == U_ZERO_ERROR) {
  787. u1->fParent = t1;
  788. r = u1;
  789. } else {
  790. /* the USR override data wasn't found, set it to be deleted */
  791. u1->fCountExisting = 0;
  792. }
  793. }
  794. }
  795. if ((hasChopped || mayHaveParent(name)) && !isRoot) {
  796. if (!loadParentsExceptRoot(t1, name, UPRV_LENGTHOF(name), usingUSRData, usrDataPath, status)) {
  797. goto finish;
  798. }
  799. }
  800. }
  801. /* we could have reached this point without having any real data */
  802. /* if that is the case, we need to chain in the default locale */
  803. if(r==nullptr && openType == URES_OPEN_LOCALE_DEFAULT_ROOT && !isDefault && !isRoot) {
  804. /* insert default locale */
  805. uprv_strcpy(name, defaultLocale);
  806. r = findFirstExisting(path, name, defaultLocale, openType, &isRoot, &hasChopped, &isDefault, &intStatus);
  807. // If we failed due to out-of-memory, report the failure and exit early.
  808. if (intStatus == U_MEMORY_ALLOCATION_ERROR) {
  809. *status = intStatus;
  810. goto finish;
  811. }
  812. intStatus = U_USING_DEFAULT_WARNING;
  813. if(r != nullptr) { /* the default locale exists */
  814. t1 = r;
  815. hasRealData = true;
  816. isDefault = true;
  817. // TODO: Why not if (usingUSRData) { ... } like in the non-default-locale code path?
  818. if ((hasChopped || mayHaveParent(name)) && !isRoot) {
  819. if (!loadParentsExceptRoot(t1, name, UPRV_LENGTHOF(name), usingUSRData, usrDataPath, status)) {
  820. goto finish;
  821. }
  822. }
  823. }
  824. }
  825. /* we could still have r == nullptr at this point - maybe even default locale is not */
  826. /* present */
  827. if(r == nullptr) {
  828. uprv_strcpy(name, kRootLocaleName);
  829. r = findFirstExisting(path, name, defaultLocale, openType, &isRoot, &hasChopped, &isDefault, &intStatus);
  830. // If we failed due to out-of-memory, report the failure and exit early.
  831. if (intStatus == U_MEMORY_ALLOCATION_ERROR) {
  832. *status = intStatus;
  833. goto finish;
  834. }
  835. if(r != nullptr) {
  836. t1 = r;
  837. intStatus = U_USING_DEFAULT_WARNING;
  838. hasRealData = true;
  839. } else { /* we don't even have the root locale */
  840. *status = U_MISSING_RESOURCE_ERROR;
  841. goto finish;
  842. }
  843. } else if(!isRoot && uprv_strcmp(t1->fName, kRootLocaleName) != 0 &&
  844. t1->fParent == nullptr && !r->fData.noFallback) {
  845. if (!insertRootBundle(t1, status)) {
  846. goto finish;
  847. }
  848. if(!hasRealData) {
  849. r->fBogus = U_USING_DEFAULT_WARNING;
  850. }
  851. }
  852. // TODO: Does this ever loop?
  853. while(r != nullptr && !isRoot && t1->fParent != nullptr) {
  854. t1->fParent->fCountExisting++;
  855. t1 = t1->fParent;
  856. }
  857. finish:
  858. if(U_SUCCESS(*status)) {
  859. if(intStatus != U_ZERO_ERROR) {
  860. *status = intStatus;
  861. }
  862. return r;
  863. } else {
  864. return nullptr;
  865. }
  866. }
  867. /**
  868. * Version of entryOpen() and findFirstExisting() for ures_openDirect(),
  869. * with no fallbacks.
  870. * Parent and root locale bundles are loaded if
  871. * the requested bundle does not have the "nofallback" flag.
  872. */
  873. static UResourceDataEntry *
  874. entryOpenDirect(const char* path, const char* localeID, UErrorCode* status) {
  875. initCache(status);
  876. if(U_FAILURE(*status)) {
  877. return nullptr;
  878. }
  879. // Note: We need to query the default locale *before* locking resbMutex.
  880. // If the localeID is nullptr, then we want to use the default locale.
  881. if (localeID == nullptr) {
  882. localeID = uloc_getDefault();
  883. } else if (*localeID == 0) {
  884. // If the localeID is "", then we want to use the root locale.
  885. localeID = kRootLocaleName;
  886. }
  887. Mutex lock(&resbMutex);
  888. // findFirstExisting() without fallbacks.
  889. UResourceDataEntry *r = init_entry(localeID, path, status);
  890. if(U_SUCCESS(*status)) {
  891. if(r->fBogus != U_ZERO_ERROR) {
  892. r->fCountExisting--;
  893. r = nullptr;
  894. }
  895. } else {
  896. r = nullptr;
  897. }
  898. // Some code depends on the ures_openDirect() bundle to have a parent bundle chain,
  899. // unless it is marked with "nofallback".
  900. UResourceDataEntry *t1 = r;
  901. if(r != nullptr && uprv_strcmp(localeID, kRootLocaleName) != 0 && // not root
  902. r->fParent == nullptr && !r->fData.noFallback &&
  903. uprv_strlen(localeID) < ULOC_FULLNAME_CAPACITY) {
  904. char name[ULOC_FULLNAME_CAPACITY];
  905. uprv_strcpy(name, localeID);
  906. if(!chopLocale(name) || uprv_strcmp(name, kRootLocaleName) == 0 ||
  907. loadParentsExceptRoot(t1, name, UPRV_LENGTHOF(name), false, nullptr, status)) {
  908. if(uprv_strcmp(t1->fName, kRootLocaleName) != 0 && t1->fParent == nullptr) {
  909. insertRootBundle(t1, status);
  910. }
  911. }
  912. if(U_FAILURE(*status)) {
  913. r = nullptr;
  914. }
  915. }
  916. if(r != nullptr) {
  917. // TODO: Does this ever loop?
  918. while(t1->fParent != nullptr) {
  919. t1->fParent->fCountExisting++;
  920. t1 = t1->fParent;
  921. }
  922. }
  923. return r;
  924. }
  925. /**
  926. * Functions to create and destroy resource bundles.
  927. * CAUTION: resbMutex must be locked when calling this function.
  928. */
  929. /* INTERNAL: */
  930. static void entryCloseInt(UResourceDataEntry *resB) {
  931. UResourceDataEntry *p = resB;
  932. while(resB != nullptr) {
  933. p = resB->fParent;
  934. resB->fCountExisting--;
  935. /* Entries are left in the cache. TODO: add ures_flushCache() to force a flush
  936. of the cache. */
  937. /*
  938. if(resB->fCountExisting <= 0) {
  939. uhash_remove(cache, resB);
  940. if(resB->fBogus == U_ZERO_ERROR) {
  941. res_unload(&(resB->fData));
  942. }
  943. if(resB->fName != nullptr) {
  944. uprv_free(resB->fName);
  945. }
  946. if(resB->fPath != nullptr) {
  947. uprv_free(resB->fPath);
  948. }
  949. uprv_free(resB);
  950. }
  951. */
  952. resB = p;
  953. }
  954. }
  955. /**
  956. * API: closes a resource bundle and cleans up.
  957. */
  958. static void entryClose(UResourceDataEntry *resB) {
  959. Mutex lock(&resbMutex);
  960. entryCloseInt(resB);
  961. }
  962. /*
  963. U_CFUNC void ures_setResPath(UResourceBundle *resB, const char* toAdd) {
  964. if(resB->fResPath == nullptr) {
  965. resB->fResPath = resB->fResBuf;
  966. *(resB->fResPath) = 0;
  967. }
  968. resB->fResPathLen = uprv_strlen(toAdd);
  969. if(RES_BUFSIZE <= resB->fResPathLen+1) {
  970. if(resB->fResPath == resB->fResBuf) {
  971. resB->fResPath = (char *)uprv_malloc((resB->fResPathLen+1)*sizeof(char));
  972. } else {
  973. resB->fResPath = (char *)uprv_realloc(resB->fResPath, (resB->fResPathLen+1)*sizeof(char));
  974. }
  975. }
  976. uprv_strcpy(resB->fResPath, toAdd);
  977. }
  978. */
  979. static void ures_appendResPath(UResourceBundle *resB, const char* toAdd, int32_t lenToAdd, UErrorCode *status) {
  980. int32_t resPathLenOrig = resB->fResPathLen;
  981. if(resB->fResPath == nullptr) {
  982. resB->fResPath = resB->fResBuf;
  983. *(resB->fResPath) = 0;
  984. resB->fResPathLen = 0;
  985. }
  986. resB->fResPathLen += lenToAdd;
  987. if(RES_BUFSIZE <= resB->fResPathLen+1) {
  988. if(resB->fResPath == resB->fResBuf) {
  989. resB->fResPath = (char *)uprv_malloc((resB->fResPathLen+1)*sizeof(char));
  990. /* Check that memory was allocated correctly. */
  991. if (resB->fResPath == nullptr) {
  992. *status = U_MEMORY_ALLOCATION_ERROR;
  993. return;
  994. }
  995. uprv_strcpy(resB->fResPath, resB->fResBuf);
  996. } else {
  997. char *temp = (char *)uprv_realloc(resB->fResPath, (resB->fResPathLen+1)*sizeof(char));
  998. /* Check that memory was reallocated correctly. */
  999. if (temp == nullptr) {
  1000. *status = U_MEMORY_ALLOCATION_ERROR;
  1001. return;
  1002. }
  1003. resB->fResPath = temp;
  1004. }
  1005. }
  1006. uprv_strcpy(resB->fResPath + resPathLenOrig, toAdd);
  1007. }
  1008. static void ures_freeResPath(UResourceBundle *resB) {
  1009. if (resB->fResPath && resB->fResPath != resB->fResBuf) {
  1010. uprv_free(resB->fResPath);
  1011. }
  1012. resB->fResPath = nullptr;
  1013. resB->fResPathLen = 0;
  1014. }
  1015. static void
  1016. ures_closeBundle(UResourceBundle* resB, UBool freeBundleObj)
  1017. {
  1018. if(resB != nullptr) {
  1019. if(resB->fData != nullptr) {
  1020. entryClose(resB->fData);
  1021. }
  1022. if(resB->fVersion != nullptr) {
  1023. uprv_free(resB->fVersion);
  1024. }
  1025. ures_freeResPath(resB);
  1026. if(ures_isStackObject(resB) == false && freeBundleObj) {
  1027. uprv_free(resB);
  1028. }
  1029. #if 0 /*U_DEBUG*/
  1030. else {
  1031. /* poison the data */
  1032. uprv_memset(resB, -1, sizeof(UResourceBundle));
  1033. }
  1034. #endif
  1035. }
  1036. }
  1037. U_CAPI void U_EXPORT2
  1038. ures_close(UResourceBundle* resB)
  1039. {
  1040. ures_closeBundle(resB, true);
  1041. }
  1042. namespace {
  1043. UResourceBundle *init_resb_result(
  1044. UResourceDataEntry *dataEntry, Resource r, const char *key, int32_t idx,
  1045. UResourceDataEntry *validLocaleDataEntry, const char *containerResPath,
  1046. int32_t recursionDepth,
  1047. UResourceBundle *resB, UErrorCode *status);
  1048. // TODO: Try to refactor further, so that we output a dataEntry + Resource + (optionally) resPath,
  1049. // rather than a UResourceBundle.
  1050. // May need to entryIncrease() the resulting dataEntry.
  1051. UResourceBundle *getAliasTargetAsResourceBundle(
  1052. const ResourceData &resData, Resource r, const char *key, int32_t idx,
  1053. UResourceDataEntry *validLocaleDataEntry, const char *containerResPath,
  1054. int32_t recursionDepth,
  1055. UResourceBundle *resB, UErrorCode *status) {
  1056. // TODO: When an error occurs: Should we return nullptr vs. resB?
  1057. if (U_FAILURE(*status)) { return resB; }
  1058. U_ASSERT(RES_GET_TYPE(r) == URES_ALIAS);
  1059. int32_t len = 0;
  1060. const char16_t *alias = res_getAlias(&resData, r, &len);
  1061. if(len <= 0) {
  1062. // bad alias
  1063. *status = U_ILLEGAL_ARGUMENT_ERROR;
  1064. return resB;
  1065. }
  1066. // Copy the UTF-16 alias string into an invariant-character string.
  1067. //
  1068. // We do this so that res_findResource() can modify the path,
  1069. // which allows us to remove redundant _res_findResource() variants
  1070. // in uresdata.c.
  1071. // res_findResource() now NUL-terminates each segment so that table keys
  1072. // can always be compared with strcmp() instead of strncmp().
  1073. // Saves code there and simplifies testing and code coverage.
  1074. //
  1075. // markus 2003oct17
  1076. CharString chAlias;
  1077. chAlias.appendInvariantChars(alias, len, *status);
  1078. if (U_FAILURE(*status)) {
  1079. return nullptr;
  1080. }
  1081. // We have an alias, now let's cut it up.
  1082. const char *path = nullptr, *locale = nullptr, *keyPath = nullptr;
  1083. if(chAlias[0] == RES_PATH_SEPARATOR) {
  1084. // There is a path included.
  1085. char *chAliasData = chAlias.data();
  1086. char *sep = chAliasData + 1;
  1087. path = sep;
  1088. sep = uprv_strchr(sep, RES_PATH_SEPARATOR);
  1089. if(sep != nullptr) {
  1090. *sep++ = 0;
  1091. }
  1092. if(uprv_strcmp(path, "LOCALE") == 0) {
  1093. // This is an XPath alias, starting with "/LOCALE/".
  1094. // It contains the path to a resource which should be looked up
  1095. // starting in the valid locale.
  1096. // TODO: Can/should we forbid a /LOCALE alias without key path?
  1097. // It seems weird to alias to the same path, just starting from the valid locale.
  1098. // That will often yield an infinite loop.
  1099. keyPath = sep;
  1100. // Read from the valid locale which we already have.
  1101. path = locale = nullptr;
  1102. } else {
  1103. if(uprv_strcmp(path, "ICUDATA") == 0) { /* want ICU data */
  1104. path = nullptr;
  1105. }
  1106. if (sep == nullptr) {
  1107. // TODO: This ends up using the root bundle. Can/should we forbid this?
  1108. locale = "";
  1109. } else {
  1110. locale = sep;
  1111. sep = uprv_strchr(sep, RES_PATH_SEPARATOR);
  1112. if(sep != nullptr) {
  1113. *sep++ = 0;
  1114. }
  1115. keyPath = sep;
  1116. }
  1117. }
  1118. } else {
  1119. // No path, start with a locale.
  1120. char *sep = chAlias.data();
  1121. locale = sep;
  1122. sep = uprv_strchr(sep, RES_PATH_SEPARATOR);
  1123. if(sep != nullptr) {
  1124. *sep++ = 0;
  1125. }
  1126. keyPath = sep;
  1127. path = validLocaleDataEntry->fPath;
  1128. }
  1129. // Got almost everything, let's try to open.
  1130. // First, open the bundle with real data.
  1131. LocalUResourceBundlePointer mainRes;
  1132. UResourceDataEntry *dataEntry;
  1133. if (locale == nullptr) {
  1134. // alias = /LOCALE/keyPath
  1135. // Read from the valid locale which we already have.
  1136. dataEntry = validLocaleDataEntry;
  1137. } else {
  1138. UErrorCode intStatus = U_ZERO_ERROR;
  1139. // TODO: Shouldn't we use ures_open() for locale data bundles (!noFallback)?
  1140. mainRes.adoptInstead(ures_openDirect(path, locale, &intStatus));
  1141. if(U_FAILURE(intStatus)) {
  1142. // We failed to open the resource bundle we're aliasing to.
  1143. *status = intStatus;
  1144. return resB;
  1145. }
  1146. dataEntry = mainRes->fData;
  1147. }
  1148. const char* temp = nullptr;
  1149. if(keyPath == nullptr) {
  1150. // No key path. This means that we are going to to use the corresponding resource from
  1151. // another bundle.
  1152. // TODO: Why the special code path?
  1153. // Why not put together a key path from containerResPath + key or idx,
  1154. // as a comment below suggests, and go into the regular code branch?
  1155. // First, we are going to get a corresponding container
  1156. // resource to the one we are searching.
  1157. r = dataEntry->fData.rootRes;
  1158. if(containerResPath) {
  1159. chAlias.clear().append(containerResPath, *status);
  1160. if (U_FAILURE(*status)) {
  1161. return nullptr;
  1162. }
  1163. char *aKey = chAlias.data();
  1164. // TODO: should res_findResource() return a new dataEntry, too?
  1165. r = res_findResource(&dataEntry->fData, r, &aKey, &temp);
  1166. }
  1167. if(key) {
  1168. // We need to make keyPath from the containerResPath and
  1169. // current key, if there is a key associated.
  1170. chAlias.clear().append(key, *status);
  1171. if (U_FAILURE(*status)) {
  1172. return nullptr;
  1173. }
  1174. char *aKey = chAlias.data();
  1175. r = res_findResource(&dataEntry->fData, r, &aKey, &temp);
  1176. } else if(idx != -1) {
  1177. // If there is no key, but there is an index, try to get by the index.
  1178. // Here we have either a table or an array, so get the element.
  1179. int32_t type = RES_GET_TYPE(r);
  1180. if(URES_IS_TABLE(type)) {
  1181. const char *aKey;
  1182. r = res_getTableItemByIndex(&dataEntry->fData, r, idx, &aKey);
  1183. } else { /* array */
  1184. r = res_getArrayItem(&dataEntry->fData, r, idx);
  1185. }
  1186. }
  1187. if(r != RES_BOGUS) {
  1188. resB = init_resb_result(
  1189. dataEntry, r, temp, -1, validLocaleDataEntry, nullptr, recursionDepth+1,
  1190. resB, status);
  1191. } else {
  1192. *status = U_MISSING_RESOURCE_ERROR;
  1193. }
  1194. } else {
  1195. // This one is a bit trickier.
  1196. // We start finding keys, but after we resolve one alias, the path might continue.
  1197. // Consider:
  1198. // aliastest:alias { "testtypes/anotheralias/Sequence" }
  1199. // anotheralias:alias { "/ICUDATA/sh/CollationElements" }
  1200. // aliastest resource should finally have the sequence, not collation elements.
  1201. CharString pathBuf(keyPath, *status);
  1202. if (U_FAILURE(*status)) {
  1203. return nullptr;
  1204. }
  1205. char *myPath = pathBuf.data();
  1206. containerResPath = nullptr;
  1207. // Now we have fallback following here.
  1208. for(;;) {
  1209. r = dataEntry->fData.rootRes;
  1210. // TODO: Move containerResPath = nullptr to here,
  1211. // consistent with restarting from the rootRes of another bundle?!
  1212. // This loop handles 'found' resources over several levels.
  1213. while(*myPath && U_SUCCESS(*status)) {
  1214. r = res_findResource(&(dataEntry->fData), r, &myPath, &temp);
  1215. if(r == RES_BOGUS) {
  1216. // No resource found, we don't really want to look anymore on this level.
  1217. break;
  1218. }
  1219. // Found a resource, but it might be an indirection.
  1220. resB = init_resb_result(
  1221. dataEntry, r, temp, -1,
  1222. validLocaleDataEntry, containerResPath, recursionDepth+1,
  1223. resB, status);
  1224. if (U_FAILURE(*status)) {
  1225. break;
  1226. }
  1227. if (temp == nullptr || uprv_strcmp(keyPath, temp) != 0) {
  1228. // The call to init_resb_result() above will set resB->fKeyPath to be
  1229. // the same as resB->fKey,
  1230. // throwing away any additional path elements if we had them --
  1231. // if the key path wasn't just a single resource ID, clear out
  1232. // the bundle's key path and re-set it to be equal to keyPath.
  1233. ures_freeResPath(resB);
  1234. ures_appendResPath(resB, keyPath, (int32_t)uprv_strlen(keyPath), status);
  1235. if(resB->fResPath[resB->fResPathLen-1] != RES_PATH_SEPARATOR) {
  1236. ures_appendResPath(resB, RES_PATH_SEPARATOR_S, 1, status);
  1237. }
  1238. if (U_FAILURE(*status)) {
  1239. break;
  1240. }
  1241. }
  1242. r = resB->fRes; /* switch to a new resource, possibly a new tree */
  1243. dataEntry = resB->fData;
  1244. containerResPath = resB->fResPath;
  1245. }
  1246. if (U_FAILURE(*status) || r != RES_BOGUS) {
  1247. break;
  1248. }
  1249. // Fall back to the parent bundle, if there is one.
  1250. dataEntry = dataEntry->fParent;
  1251. if (dataEntry == nullptr) {
  1252. *status = U_MISSING_RESOURCE_ERROR;
  1253. break;
  1254. }
  1255. // Copy the same keyPath again.
  1256. myPath = pathBuf.data();
  1257. uprv_strcpy(myPath, keyPath);
  1258. }
  1259. }
  1260. if(mainRes.getAlias() == resB) {
  1261. mainRes.orphan();
  1262. }
  1263. ResourceTracer(resB).maybeTrace("getalias");
  1264. return resB;
  1265. }
  1266. // Recursive function, should be called only by itself, by its simpler wrapper,
  1267. // or by getAliasTargetAsResourceBundle().
  1268. UResourceBundle *init_resb_result(
  1269. UResourceDataEntry *dataEntry, Resource r, const char *key, int32_t idx,
  1270. UResourceDataEntry *validLocaleDataEntry, const char *containerResPath,
  1271. int32_t recursionDepth,
  1272. UResourceBundle *resB, UErrorCode *status) {
  1273. // TODO: When an error occurs: Should we return nullptr vs. resB?
  1274. if(status == nullptr || U_FAILURE(*status)) {
  1275. return resB;
  1276. }
  1277. if (validLocaleDataEntry == nullptr) {
  1278. *status = U_ILLEGAL_ARGUMENT_ERROR;
  1279. return nullptr;
  1280. }
  1281. if(RES_GET_TYPE(r) == URES_ALIAS) {
  1282. // This is an alias, need to exchange with real data.
  1283. if(recursionDepth >= URES_MAX_ALIAS_LEVEL) {
  1284. *status = U_TOO_MANY_ALIASES_ERROR;
  1285. return resB;
  1286. }
  1287. return getAliasTargetAsResourceBundle(
  1288. dataEntry->fData, r, key, idx,
  1289. validLocaleDataEntry, containerResPath, recursionDepth, resB, status);
  1290. }
  1291. if(resB == nullptr) {
  1292. resB = (UResourceBundle *)uprv_malloc(sizeof(UResourceBundle));
  1293. if (resB == nullptr) {
  1294. *status = U_MEMORY_ALLOCATION_ERROR;
  1295. return nullptr;
  1296. }
  1297. ures_setIsStackObject(resB, false);
  1298. resB->fResPath = nullptr;
  1299. resB->fResPathLen = 0;
  1300. } else {
  1301. if(resB->fData != nullptr) {
  1302. entryClose(resB->fData);
  1303. }
  1304. if(resB->fVersion != nullptr) {
  1305. uprv_free(resB->fVersion);
  1306. }
  1307. /*
  1308. weiv: if stack object was passed in, it doesn't really need to be reinited,
  1309. since the purpose of initing is to remove stack junk. However, at this point
  1310. we would not do anything to an allocated object, so stack object should be
  1311. treated the same
  1312. */
  1313. /*
  1314. if(ures_isStackObject(resB) != false) {
  1315. ures_initStackObject(resB);
  1316. }
  1317. */
  1318. if(containerResPath != resB->fResPath) {
  1319. ures_freeResPath(resB);
  1320. }
  1321. }
  1322. resB->fData = dataEntry;
  1323. entryIncrease(resB->fData);
  1324. resB->fHasFallback = false;
  1325. resB->fIsTopLevel = false;
  1326. resB->fIndex = -1;
  1327. resB->fKey = key;
  1328. resB->fValidLocaleDataEntry = validLocaleDataEntry;
  1329. if(containerResPath != resB->fResPath) {
  1330. ures_appendResPath(
  1331. resB, containerResPath, static_cast<int32_t>(uprv_strlen(containerResPath)), status);
  1332. }
  1333. if(key != nullptr) {
  1334. ures_appendResPath(resB, key, (int32_t)uprv_strlen(key), status);
  1335. if(resB->fResPath[resB->fResPathLen-1] != RES_PATH_SEPARATOR) {
  1336. ures_appendResPath(resB, RES_PATH_SEPARATOR_S, 1, status);
  1337. }
  1338. } else if(idx >= 0) {
  1339. char buf[256];
  1340. int32_t len = T_CString_integerToString(buf, idx, 10);
  1341. ures_appendResPath(resB, buf, len, status);
  1342. if(resB->fResPath[resB->fResPathLen-1] != RES_PATH_SEPARATOR) {
  1343. ures_appendResPath(resB, RES_PATH_SEPARATOR_S, 1, status);
  1344. }
  1345. }
  1346. /* Make sure that Purify doesn't complain about uninitialized memory copies. */
  1347. {
  1348. int32_t usedLen = ((resB->fResBuf == resB->fResPath) ? resB->fResPathLen : 0);
  1349. uprv_memset(resB->fResBuf + usedLen, 0, sizeof(resB->fResBuf) - usedLen);
  1350. }
  1351. resB->fVersion = nullptr;
  1352. resB->fRes = r;
  1353. resB->fSize = res_countArrayItems(&resB->getResData(), resB->fRes);
  1354. ResourceTracer(resB).trace("get");
  1355. return resB;
  1356. }
  1357. UResourceBundle *init_resb_result(
  1358. UResourceDataEntry *dataEntry, Resource r, const char *key, int32_t idx,
  1359. // validLocaleDataEntry + containerResPath
  1360. const UResourceBundle *container,
  1361. UResourceBundle *resB, UErrorCode *status) {
  1362. return init_resb_result(
  1363. dataEntry, r, key, idx,
  1364. container->fValidLocaleDataEntry, container->fResPath, 0, resB, status);
  1365. }
  1366. } // namespace
  1367. UResourceBundle *ures_copyResb(UResourceBundle *r, const UResourceBundle *original, UErrorCode *status) {
  1368. UBool isStackObject;
  1369. if(U_FAILURE(*status) || r == original) {
  1370. return r;
  1371. }
  1372. if(original != nullptr) {
  1373. if(r == nullptr) {
  1374. isStackObject = false;
  1375. r = (UResourceBundle *)uprv_malloc(sizeof(UResourceBundle));
  1376. /* test for nullptr */
  1377. if (r == nullptr) {
  1378. *status = U_MEMORY_ALLOCATION_ERROR;
  1379. return nullptr;
  1380. }
  1381. } else {
  1382. isStackObject = ures_isStackObject(r);
  1383. ures_closeBundle(r, false);
  1384. }
  1385. uprv_memcpy(r, original, sizeof(UResourceBundle));
  1386. r->fResPath = nullptr;
  1387. r->fResPathLen = 0;
  1388. if(original->fResPath) {
  1389. ures_appendResPath(r, original->fResPath, original->fResPathLen, status);
  1390. }
  1391. ures_setIsStackObject(r, isStackObject);
  1392. if(r->fData != nullptr) {
  1393. entryIncrease(r->fData);
  1394. }
  1395. }
  1396. return r;
  1397. }
  1398. /**
  1399. * Functions to retrieve data from resource bundles.
  1400. */
  1401. U_CAPI const char16_t* U_EXPORT2 ures_getString(const UResourceBundle* resB, int32_t* len, UErrorCode* status) {
  1402. const char16_t *s;
  1403. if (status==nullptr || U_FAILURE(*status)) {
  1404. return nullptr;
  1405. }
  1406. if(resB == nullptr) {
  1407. *status = U_ILLEGAL_ARGUMENT_ERROR;
  1408. return nullptr;
  1409. }
  1410. s = res_getString({resB}, &resB->getResData(), resB->fRes, len);
  1411. if (s == nullptr) {
  1412. *status = U_RESOURCE_TYPE_MISMATCH;
  1413. }
  1414. return s;
  1415. }
  1416. static const char *
  1417. ures_toUTF8String(const char16_t *s16, int32_t length16,
  1418. char *dest, int32_t *pLength,
  1419. UBool forceCopy,
  1420. UErrorCode *status) {
  1421. int32_t capacity;
  1422. if (U_FAILURE(*status)) {
  1423. return nullptr;
  1424. }
  1425. if (pLength != nullptr) {
  1426. capacity = *pLength;
  1427. } else {
  1428. capacity = 0;
  1429. }
  1430. if (capacity < 0 || (capacity > 0 && dest == nullptr)) {
  1431. *status = U_ILLEGAL_ARGUMENT_ERROR;
  1432. return nullptr;
  1433. }
  1434. if (length16 == 0) {
  1435. /* empty string, return as read-only pointer */
  1436. if (pLength != nullptr) {
  1437. *pLength = 0;
  1438. }
  1439. if (forceCopy) {
  1440. u_terminateChars(dest, capacity, 0, status);
  1441. return dest;
  1442. } else {
  1443. return "";
  1444. }
  1445. } else {
  1446. /* We need to transform the string to the destination buffer. */
  1447. if (capacity < length16) {
  1448. /* No chance for the string to fit. Pure preflighting. */
  1449. return u_strToUTF8(nullptr, 0, pLength, s16, length16, status);
  1450. }
  1451. if (!forceCopy && (length16 <= 0x2aaaaaaa)) {
  1452. /*
  1453. * We know the string will fit into dest because each char16_t turns
  1454. * into at most three UTF-8 bytes. Fill the latter part of dest
  1455. * so that callers do not expect to use dest as a string pointer,
  1456. * hopefully leading to more robust code for when resource bundles
  1457. * may store UTF-8 natively.
  1458. * (In which case dest would not be used at all.)
  1459. *
  1460. * We do not do this if forceCopy=true because then the caller
  1461. * expects the string to start exactly at dest.
  1462. *
  1463. * The test above for <= 0x2aaaaaaa prevents overflows.
  1464. * The +1 is for the NUL terminator.
  1465. */
  1466. int32_t maxLength = 3 * length16 + 1;
  1467. if (capacity > maxLength) {
  1468. dest += capacity - maxLength;
  1469. capacity = maxLength;
  1470. }
  1471. }
  1472. return u_strToUTF8(dest, capacity, pLength, s16, length16, status);
  1473. }
  1474. }
  1475. U_CAPI const char * U_EXPORT2
  1476. ures_getUTF8String(const UResourceBundle *resB,
  1477. char *dest, int32_t *pLength,
  1478. UBool forceCopy,
  1479. UErrorCode *status) {
  1480. int32_t length16;
  1481. const char16_t *s16 = ures_getString(resB, &length16, status);
  1482. return ures_toUTF8String(s16, length16, dest, pLength, forceCopy, status);
  1483. }
  1484. U_CAPI const uint8_t* U_EXPORT2 ures_getBinary(const UResourceBundle* resB, int32_t* len,
  1485. UErrorCode* status) {
  1486. const uint8_t *p;
  1487. if (status==nullptr || U_FAILURE(*status)) {
  1488. return nullptr;
  1489. }
  1490. if(resB == nullptr) {
  1491. *status = U_ILLEGAL_ARGUMENT_ERROR;
  1492. return nullptr;
  1493. }
  1494. p = res_getBinary({resB}, &resB->getResData(), resB->fRes, len);
  1495. if (p == nullptr) {
  1496. *status = U_RESOURCE_TYPE_MISMATCH;
  1497. }
  1498. return p;
  1499. }
  1500. U_CAPI const int32_t* U_EXPORT2 ures_getIntVector(const UResourceBundle* resB, int32_t* len,
  1501. UErrorCode* status) {
  1502. const int32_t *p;
  1503. if (status==nullptr || U_FAILURE(*status)) {
  1504. return nullptr;
  1505. }
  1506. if(resB == nullptr) {
  1507. *status = U_ILLEGAL_ARGUMENT_ERROR;
  1508. return nullptr;
  1509. }
  1510. p = res_getIntVector({resB}, &resB->getResData(), resB->fRes, len);
  1511. if (p == nullptr) {
  1512. *status = U_RESOURCE_TYPE_MISMATCH;
  1513. }
  1514. return p;
  1515. }
  1516. /* this function returns a signed integer */
  1517. /* it performs sign extension */
  1518. U_CAPI int32_t U_EXPORT2 ures_getInt(const UResourceBundle* resB, UErrorCode *status) {
  1519. if (status==nullptr || U_FAILURE(*status)) {
  1520. return 0xffffffff;
  1521. }
  1522. if(resB == nullptr) {
  1523. *status = U_ILLEGAL_ARGUMENT_ERROR;
  1524. return 0xffffffff;
  1525. }
  1526. if(RES_GET_TYPE(resB->fRes) != URES_INT) {
  1527. *status = U_RESOURCE_TYPE_MISMATCH;
  1528. return 0xffffffff;
  1529. }
  1530. return res_getInt({resB}, resB->fRes);
  1531. }
  1532. U_CAPI uint32_t U_EXPORT2 ures_getUInt(const UResourceBundle* resB, UErrorCode *status) {
  1533. if (status==nullptr || U_FAILURE(*status)) {
  1534. return 0xffffffff;
  1535. }
  1536. if(resB == nullptr) {
  1537. *status = U_ILLEGAL_ARGUMENT_ERROR;
  1538. return 0xffffffff;
  1539. }
  1540. if(RES_GET_TYPE(resB->fRes) != URES_INT) {
  1541. *status = U_RESOURCE_TYPE_MISMATCH;
  1542. return 0xffffffff;
  1543. }
  1544. return res_getUInt({resB}, resB->fRes);
  1545. }
  1546. U_CAPI UResType U_EXPORT2 ures_getType(const UResourceBundle *resB) {
  1547. if(resB == nullptr) {
  1548. return URES_NONE;
  1549. }
  1550. return res_getPublicType(resB->fRes);
  1551. }
  1552. U_CAPI const char * U_EXPORT2 ures_getKey(const UResourceBundle *resB) {
  1553. //
  1554. // TODO: Trace ures_getKey? I guess not usually.
  1555. //
  1556. // We usually get the key string to decide whether we want the value, or to
  1557. // make a key-value pair. Tracing the value should suffice.
  1558. //
  1559. // However, I believe we have some data (e.g., in res_index) where the key
  1560. // strings are the data. Tracing the enclosing table should suffice.
  1561. //
  1562. if(resB == nullptr) {
  1563. return nullptr;
  1564. }
  1565. return(resB->fKey);
  1566. }
  1567. U_CAPI int32_t U_EXPORT2 ures_getSize(const UResourceBundle *resB) {
  1568. if(resB == nullptr) {
  1569. return 0;
  1570. }
  1571. return resB->fSize;
  1572. }
  1573. static const char16_t* ures_getStringWithAlias(const UResourceBundle *resB, Resource r, int32_t sIndex, int32_t *len, UErrorCode *status) {
  1574. if(RES_GET_TYPE(r) == URES_ALIAS) {
  1575. const char16_t* result = 0;
  1576. UResourceBundle *tempRes = ures_getByIndex(resB, sIndex, nullptr, status);
  1577. result = ures_getString(tempRes, len, status);
  1578. ures_close(tempRes);
  1579. return result;
  1580. } else {
  1581. return res_getString({resB, sIndex}, &resB->getResData(), r, len);
  1582. }
  1583. }
  1584. U_CAPI void U_EXPORT2 ures_resetIterator(UResourceBundle *resB){
  1585. if(resB == nullptr) {
  1586. return;
  1587. }
  1588. resB->fIndex = -1;
  1589. }
  1590. U_CAPI UBool U_EXPORT2 ures_hasNext(const UResourceBundle *resB) {
  1591. if(resB == nullptr) {
  1592. return false;
  1593. }
  1594. return (UBool)(resB->fIndex < resB->fSize-1);
  1595. }
  1596. U_CAPI const char16_t* U_EXPORT2 ures_getNextString(UResourceBundle *resB, int32_t* len, const char ** key, UErrorCode *status) {
  1597. Resource r = RES_BOGUS;
  1598. if (status==nullptr || U_FAILURE(*status)) {
  1599. return nullptr;
  1600. }
  1601. if(resB == nullptr) {
  1602. *status = U_ILLEGAL_ARGUMENT_ERROR;
  1603. return nullptr;
  1604. }
  1605. if(resB->fIndex == resB->fSize-1) {
  1606. *status = U_INDEX_OUTOFBOUNDS_ERROR;
  1607. } else {
  1608. resB->fIndex++;
  1609. switch(RES_GET_TYPE(resB->fRes)) {
  1610. case URES_STRING:
  1611. case URES_STRING_V2:
  1612. return res_getString({resB}, &resB->getResData(), resB->fRes, len);
  1613. case URES_TABLE:
  1614. case URES_TABLE16:
  1615. case URES_TABLE32:
  1616. r = res_getTableItemByIndex(&resB->getResData(), resB->fRes, resB->fIndex, key);
  1617. if(r == RES_BOGUS && resB->fHasFallback) {
  1618. /* TODO: do the fallback */
  1619. }
  1620. return ures_getStringWithAlias(resB, r, resB->fIndex, len, status);
  1621. case URES_ARRAY:
  1622. case URES_ARRAY16:
  1623. r = res_getArrayItem(&resB->getResData(), resB->fRes, resB->fIndex);
  1624. if(r == RES_BOGUS && resB->fHasFallback) {
  1625. /* TODO: do the fallback */
  1626. }
  1627. return ures_getStringWithAlias(resB, r, resB->fIndex, len, status);
  1628. case URES_ALIAS:
  1629. return ures_getStringWithAlias(resB, resB->fRes, resB->fIndex, len, status);
  1630. case URES_INT:
  1631. case URES_BINARY:
  1632. case URES_INT_VECTOR:
  1633. *status = U_RESOURCE_TYPE_MISMATCH;
  1634. U_FALLTHROUGH;
  1635. default:
  1636. return nullptr;
  1637. }
  1638. }
  1639. return nullptr;
  1640. }
  1641. U_CAPI UResourceBundle* U_EXPORT2 ures_getNextResource(UResourceBundle *resB, UResourceBundle *fillIn, UErrorCode *status) {
  1642. const char *key = nullptr;
  1643. Resource r = RES_BOGUS;
  1644. if (status==nullptr || U_FAILURE(*status)) {
  1645. /*return nullptr;*/
  1646. return fillIn;
  1647. }
  1648. if(resB == nullptr) {
  1649. *status = U_ILLEGAL_ARGUMENT_ERROR;
  1650. /*return nullptr;*/
  1651. return fillIn;
  1652. }
  1653. if(resB->fIndex == resB->fSize-1) {
  1654. *status = U_INDEX_OUTOFBOUNDS_ERROR;
  1655. /*return nullptr;*/
  1656. } else {
  1657. resB->fIndex++;
  1658. switch(RES_GET_TYPE(resB->fRes)) {
  1659. case URES_INT:
  1660. case URES_BINARY:
  1661. case URES_STRING:
  1662. case URES_STRING_V2:
  1663. case URES_INT_VECTOR:
  1664. return ures_copyResb(fillIn, resB, status);
  1665. case URES_TABLE:
  1666. case URES_TABLE16:
  1667. case URES_TABLE32:
  1668. r = res_getTableItemByIndex(&resB->getResData(), resB->fRes, resB->fIndex, &key);
  1669. if(r == RES_BOGUS && resB->fHasFallback) {
  1670. /* TODO: do the fallback */
  1671. }
  1672. return init_resb_result(resB->fData, r, key, resB->fIndex, resB, fillIn, status);
  1673. case URES_ARRAY:
  1674. case URES_ARRAY16:
  1675. r = res_getArrayItem(&resB->getResData(), resB->fRes, resB->fIndex);
  1676. if(r == RES_BOGUS && resB->fHasFallback) {
  1677. /* TODO: do the fallback */
  1678. }
  1679. return init_resb_result(resB->fData, r, key, resB->fIndex, resB, fillIn, status);
  1680. default:
  1681. /*return nullptr;*/
  1682. return fillIn;
  1683. }
  1684. }
  1685. /*return nullptr;*/
  1686. return fillIn;
  1687. }
  1688. U_CAPI UResourceBundle* U_EXPORT2 ures_getByIndex(const UResourceBundle *resB, int32_t indexR, UResourceBundle *fillIn, UErrorCode *status) {
  1689. const char* key = nullptr;
  1690. Resource r = RES_BOGUS;
  1691. if (status==nullptr || U_FAILURE(*status)) {
  1692. /*return nullptr;*/
  1693. return fillIn;
  1694. }
  1695. if(resB == nullptr) {
  1696. *status = U_ILLEGAL_ARGUMENT_ERROR;
  1697. /*return nullptr;*/
  1698. return fillIn;
  1699. }
  1700. if(indexR >= 0 && resB->fSize > indexR) {
  1701. switch(RES_GET_TYPE(resB->fRes)) {
  1702. case URES_INT:
  1703. case URES_BINARY:
  1704. case URES_STRING:
  1705. case URES_STRING_V2:
  1706. case URES_INT_VECTOR:
  1707. return ures_copyResb(fillIn, resB, status);
  1708. case URES_TABLE:
  1709. case URES_TABLE16:
  1710. case URES_TABLE32:
  1711. r = res_getTableItemByIndex(&resB->getResData(), resB->fRes, indexR, &key);
  1712. if(r == RES_BOGUS && resB->fHasFallback) {
  1713. /* TODO: do the fallback */
  1714. }
  1715. return init_resb_result(resB->fData, r, key, indexR, resB, fillIn, status);
  1716. case URES_ARRAY:
  1717. case URES_ARRAY16:
  1718. r = res_getArrayItem(&resB->getResData(), resB->fRes, indexR);
  1719. if(r == RES_BOGUS && resB->fHasFallback) {
  1720. /* TODO: do the fallback */
  1721. }
  1722. return init_resb_result(resB->fData, r, key, indexR, resB, fillIn, status);
  1723. default:
  1724. /*return nullptr;*/
  1725. return fillIn;
  1726. }
  1727. } else {
  1728. *status = U_MISSING_RESOURCE_ERROR;
  1729. }
  1730. /*return nullptr;*/
  1731. return fillIn;
  1732. }
  1733. U_CAPI const char16_t* U_EXPORT2 ures_getStringByIndex(const UResourceBundle *resB, int32_t indexS, int32_t* len, UErrorCode *status) {
  1734. const char* key = nullptr;
  1735. Resource r = RES_BOGUS;
  1736. if (status==nullptr || U_FAILURE(*status)) {
  1737. return nullptr;
  1738. }
  1739. if(resB == nullptr) {
  1740. *status = U_ILLEGAL_ARGUMENT_ERROR;
  1741. return nullptr;
  1742. }
  1743. if(indexS >= 0 && resB->fSize > indexS) {
  1744. switch(RES_GET_TYPE(resB->fRes)) {
  1745. case URES_STRING:
  1746. case URES_STRING_V2:
  1747. return res_getString({resB}, &resB->getResData(), resB->fRes, len);
  1748. case URES_TABLE:
  1749. case URES_TABLE16:
  1750. case URES_TABLE32:
  1751. r = res_getTableItemByIndex(&resB->getResData(), resB->fRes, indexS, &key);
  1752. if(r == RES_BOGUS && resB->fHasFallback) {
  1753. /* TODO: do the fallback */
  1754. }
  1755. return ures_getStringWithAlias(resB, r, indexS, len, status);
  1756. case URES_ARRAY:
  1757. case URES_ARRAY16:
  1758. r = res_getArrayItem(&resB->getResData(), resB->fRes, indexS);
  1759. if(r == RES_BOGUS && resB->fHasFallback) {
  1760. /* TODO: do the fallback */
  1761. }
  1762. return ures_getStringWithAlias(resB, r, indexS, len, status);
  1763. case URES_ALIAS:
  1764. return ures_getStringWithAlias(resB, resB->fRes, indexS, len, status);
  1765. case URES_INT:
  1766. case URES_BINARY:
  1767. case URES_INT_VECTOR:
  1768. *status = U_RESOURCE_TYPE_MISMATCH;
  1769. break;
  1770. default:
  1771. /* must not occur */
  1772. *status = U_INTERNAL_PROGRAM_ERROR;
  1773. break;
  1774. }
  1775. } else {
  1776. *status = U_MISSING_RESOURCE_ERROR;
  1777. }
  1778. return nullptr;
  1779. }
  1780. U_CAPI const char * U_EXPORT2
  1781. ures_getUTF8StringByIndex(const UResourceBundle *resB,
  1782. int32_t idx,
  1783. char *dest, int32_t *pLength,
  1784. UBool forceCopy,
  1785. UErrorCode *status) {
  1786. int32_t length16;
  1787. const char16_t *s16 = ures_getStringByIndex(resB, idx, &length16, status);
  1788. return ures_toUTF8String(s16, length16, dest, pLength, forceCopy, status);
  1789. }
  1790. /*U_CAPI const char *ures_getResPath(UResourceBundle *resB) {
  1791. return resB->fResPath;
  1792. }*/
  1793. U_CAPI UResourceBundle* U_EXPORT2
  1794. ures_findResource(const char* path, UResourceBundle *fillIn, UErrorCode *status)
  1795. {
  1796. UResourceBundle *first = nullptr;
  1797. UResourceBundle *result = fillIn;
  1798. char *packageName = nullptr;
  1799. char *pathToResource = nullptr, *save = nullptr;
  1800. char *locale = nullptr, *localeEnd = nullptr;
  1801. int32_t length;
  1802. if(status == nullptr || U_FAILURE(*status)) {
  1803. return result;
  1804. }
  1805. length = (int32_t)(uprv_strlen(path)+1);
  1806. save = pathToResource = (char *)uprv_malloc(length*sizeof(char));
  1807. /* test for nullptr */
  1808. if(pathToResource == nullptr) {
  1809. *status = U_MEMORY_ALLOCATION_ERROR;
  1810. return result;
  1811. }
  1812. uprv_memcpy(pathToResource, path, length);
  1813. locale = pathToResource;
  1814. if(*pathToResource == RES_PATH_SEPARATOR) { /* there is a path specification */
  1815. pathToResource++;
  1816. packageName = pathToResource;
  1817. pathToResource = uprv_strchr(pathToResource, RES_PATH_SEPARATOR);
  1818. if(pathToResource == nullptr) {
  1819. *status = U_ILLEGAL_ARGUMENT_ERROR;
  1820. } else {
  1821. *pathToResource = 0;
  1822. locale = pathToResource+1;
  1823. }
  1824. }
  1825. localeEnd = uprv_strchr(locale, RES_PATH_SEPARATOR);
  1826. if(localeEnd != nullptr) {
  1827. *localeEnd = 0;
  1828. }
  1829. first = ures_open(packageName, locale, status);
  1830. if(U_SUCCESS(*status)) {
  1831. if(localeEnd) {
  1832. result = ures_findSubResource(first, localeEnd+1, fillIn, status);
  1833. } else {
  1834. result = ures_copyResb(fillIn, first, status);
  1835. }
  1836. ures_close(first);
  1837. }
  1838. uprv_free(save);
  1839. return result;
  1840. }
  1841. U_CAPI UResourceBundle* U_EXPORT2
  1842. ures_findSubResource(const UResourceBundle *resB, char* path, UResourceBundle *fillIn, UErrorCode *status)
  1843. {
  1844. Resource res = RES_BOGUS;
  1845. UResourceBundle *result = fillIn;
  1846. const char *key;
  1847. if(status == nullptr || U_FAILURE(*status)) {
  1848. return result;
  1849. }
  1850. /* here we do looping and circular alias checking */
  1851. /* this loop is here because aliasing is resolved on this level, not on res level */
  1852. /* so, when we encounter an alias, it is not an aggregate resource, so we return */
  1853. do {
  1854. res = res_findResource(&resB->getResData(), resB->fRes, &path, &key);
  1855. if(res != RES_BOGUS) {
  1856. result = init_resb_result(resB->fData, res, key, -1, resB, fillIn, status);
  1857. resB = result;
  1858. } else {
  1859. *status = U_MISSING_RESOURCE_ERROR;
  1860. break;
  1861. }
  1862. } while(*path); /* there is more stuff in the path */
  1863. return result;
  1864. }
  1865. U_CAPI const char16_t* U_EXPORT2
  1866. ures_getStringByKeyWithFallback(const UResourceBundle *resB,
  1867. const char* inKey,
  1868. int32_t* len,
  1869. UErrorCode *status) {
  1870. UResourceBundle stack;
  1871. const char16_t* retVal = nullptr;
  1872. ures_initStackObject(&stack);
  1873. ures_getByKeyWithFallback(resB, inKey, &stack, status);
  1874. int32_t length;
  1875. retVal = ures_getString(&stack, &length, status);
  1876. ures_close(&stack);
  1877. if (U_FAILURE(*status)) {
  1878. return nullptr;
  1879. }
  1880. if (length == 3 && retVal[0] == EMPTY_SET && retVal[1] == EMPTY_SET && retVal[2] == EMPTY_SET ) {
  1881. retVal = nullptr;
  1882. length = 0;
  1883. *status = U_MISSING_RESOURCE_ERROR;
  1884. }
  1885. if (len != nullptr) {
  1886. *len = length;
  1887. }
  1888. return retVal;
  1889. }
  1890. /*
  1891. Like res_getTableItemByKey but accepts full paths like "NumberElements/latn/patternsShort".
  1892. */
  1893. static Resource getTableItemByKeyPath(const ResourceData *pResData, Resource table, const char *key) {
  1894. Resource resource = table; /* The current resource */
  1895. icu::CharString path;
  1896. UErrorCode errorCode = U_ZERO_ERROR;
  1897. path.append(key, errorCode);
  1898. if (U_FAILURE(errorCode)) { return RES_BOGUS; }
  1899. char *pathPart = path.data(); /* Path from current resource to desired resource */
  1900. UResType type = (UResType)RES_GET_TYPE(resource); /* the current resource type */
  1901. while (*pathPart && resource != RES_BOGUS && URES_IS_CONTAINER(type)) {
  1902. char *nextPathPart = uprv_strchr(pathPart, RES_PATH_SEPARATOR);
  1903. if (nextPathPart != nullptr) {
  1904. *nextPathPart = 0; /* Terminating null for this part of path. */
  1905. nextPathPart++;
  1906. } else {
  1907. nextPathPart = uprv_strchr(pathPart, 0);
  1908. }
  1909. int32_t t;
  1910. const char *pathP = pathPart;
  1911. resource = res_getTableItemByKey(pResData, resource, &t, &pathP);
  1912. type = (UResType)RES_GET_TYPE(resource);
  1913. pathPart = nextPathPart;
  1914. }
  1915. if (*pathPart) {
  1916. return RES_BOGUS;
  1917. }
  1918. return resource;
  1919. }
  1920. static void createPath(const char* origResPath,
  1921. int32_t origResPathLen,
  1922. const char* resPath,
  1923. int32_t resPathLen,
  1924. const char* inKey,
  1925. CharString& path,
  1926. UErrorCode* status) {
  1927. // This is a utility function used by ures_getByKeyWithFallback() below. This function builds a path from
  1928. // resPath and inKey, returning the result in `path`. Originally, this function just cleared `path` and
  1929. // appended resPath and inKey to it, but that caused problems for horizontal inheritance.
  1930. //
  1931. // In normal cases, resPath is the same as origResPath, but if ures_getByKeyWithFallback() has followed an
  1932. // alias, resPath may be different from origResPath. Not only may the existing path elements be different,
  1933. // but resPath may also have MORE path elements than origResPath did. If it does, those additional path
  1934. // elements SUPERSEDE the corresponding elements of inKey. So this code counts the number of elements in
  1935. // resPath and origResPath and, for each path element in resPath that doesn't have a counterpart in origResPath,
  1936. // deletes a path element from the beginning of inKey. The remainder of inKey is then appended to
  1937. // resPath to form the result. (We're not using uprv_strchr() here because resPath and origResPath may
  1938. // not be zero-terminated.)
  1939. path.clear();
  1940. const char* key = inKey;
  1941. if (resPathLen > 0) {
  1942. path.append(resPath, resPathLen, *status);
  1943. if (U_SUCCESS(*status)) {
  1944. const char* resPathLimit = resPath + resPathLen;
  1945. const char* origResPathLimit = origResPath + origResPathLen;
  1946. const char* resPathPtr = resPath;
  1947. const char* origResPathPtr = origResPath;
  1948. // Remove from the beginning of resPath the number of segments that are contained in origResPath.
  1949. // If origResPath has MORE segments than resPath, this will leave resPath as the empty string.
  1950. while (origResPathPtr < origResPathLimit && resPathPtr < resPathLimit) {
  1951. while (origResPathPtr < origResPathLimit && *origResPathPtr != RES_PATH_SEPARATOR) {
  1952. ++origResPathPtr;
  1953. }
  1954. if (origResPathPtr < origResPathLimit && *origResPathPtr == RES_PATH_SEPARATOR) {
  1955. ++origResPathPtr;
  1956. }
  1957. while (resPathPtr < resPathLimit && *resPathPtr != RES_PATH_SEPARATOR) {
  1958. ++resPathPtr;
  1959. }
  1960. if (resPathPtr < resPathLimit && *resPathPtr == RES_PATH_SEPARATOR) {
  1961. ++resPathPtr;
  1962. }
  1963. }
  1964. // New remove from the beginning of `key` the number of segments remaining in resPath.
  1965. // If resPath has more segments than `key` does, `key` will end up empty.
  1966. while (resPathPtr < resPathLimit && *key != '\0') {
  1967. while (resPathPtr < resPathLimit && *resPathPtr != RES_PATH_SEPARATOR) {
  1968. ++resPathPtr;
  1969. }
  1970. if (resPathPtr < resPathLimit && *resPathPtr == RES_PATH_SEPARATOR) {
  1971. ++resPathPtr;
  1972. }
  1973. while (*key != '\0' && *key != RES_PATH_SEPARATOR) {
  1974. ++key;
  1975. }
  1976. if (*key == RES_PATH_SEPARATOR) {
  1977. ++key;
  1978. }
  1979. }
  1980. }
  1981. // Finally, append what's left of `key` to `path`. What you end up with here is `resPath`, plus
  1982. // any pieces of `key` that aren't superseded by `resPath`.
  1983. // Or, to put it another way, calculate <#-segments-in-key> - (<#-segments-in-resPath> - <#-segments-in-origResPath>),
  1984. // and append that many segments from the end of `key` to `resPath` to produce the result.
  1985. path.append(key, *status);
  1986. } else {
  1987. path.append(inKey, *status);
  1988. }
  1989. }
  1990. U_CAPI UResourceBundle* U_EXPORT2
  1991. ures_getByKeyWithFallback(const UResourceBundle *resB,
  1992. const char* inKey,
  1993. UResourceBundle *fillIn,
  1994. UErrorCode *status) {
  1995. Resource res = RES_BOGUS, rootRes = RES_BOGUS;
  1996. UResourceBundle *helper = nullptr;
  1997. if (status==nullptr || U_FAILURE(*status)) {
  1998. return fillIn;
  1999. }
  2000. if(resB == nullptr) {
  2001. *status = U_ILLEGAL_ARGUMENT_ERROR;
  2002. return fillIn;
  2003. }
  2004. int32_t type = RES_GET_TYPE(resB->fRes);
  2005. if(URES_IS_TABLE(type)) {
  2006. const char* origResPath = resB->fResPath;
  2007. int32_t origResPathLen = resB->fResPathLen;
  2008. res = getTableItemByKeyPath(&resB->getResData(), resB->fRes, inKey);
  2009. const char* key = inKey;
  2010. bool didRootOnce = false;
  2011. if(res == RES_BOGUS) {
  2012. UResourceDataEntry *dataEntry = resB->fData;
  2013. CharString path;
  2014. char *myPath = nullptr;
  2015. const char* resPath = resB->fResPath;
  2016. int32_t len = resB->fResPathLen;
  2017. while(res == RES_BOGUS && (dataEntry->fParent != nullptr || !didRootOnce)) { /* Otherwise, we'll look in parents */
  2018. if (dataEntry->fParent != nullptr) {
  2019. dataEntry = dataEntry->fParent;
  2020. } else {
  2021. // We can't just stop when we get to a bundle whose fParent is nullptr. That'll work most of the time,
  2022. // but if the bundle that the caller passed to us was "root" (which happens in getAllItemsWithFallback(),
  2023. // this function will drop right out without doing anything if "root" doesn't contain the exact key path
  2024. // specified. In that case, we need one extra time through this loop to make sure we follow any
  2025. // applicable aliases at the root level.
  2026. didRootOnce = true;
  2027. }
  2028. rootRes = dataEntry->fData.rootRes;
  2029. if(dataEntry->fBogus == U_ZERO_ERROR) {
  2030. createPath(origResPath, origResPathLen, resPath, len, inKey, path, status);
  2031. if (U_FAILURE(*status)) {
  2032. ures_close(helper);
  2033. return fillIn;
  2034. }
  2035. myPath = path.data();
  2036. key = inKey;
  2037. do {
  2038. res = res_findResource(&(dataEntry->fData), rootRes, &myPath, &key);
  2039. if (RES_GET_TYPE(res) == URES_ALIAS && *myPath) {
  2040. /* We hit an alias, but we didn't finish following the path. */
  2041. helper = init_resb_result(dataEntry, res, nullptr, -1, resB, helper, status);
  2042. /*helper = init_resb_result(dataEntry, res, inKey, -1, resB, helper, status);*/
  2043. if(helper) {
  2044. dataEntry = helper->fData;
  2045. rootRes = helper->fRes;
  2046. resPath = helper->fResPath;
  2047. len = helper->fResPathLen;
  2048. } else {
  2049. break;
  2050. }
  2051. } else if (res == RES_BOGUS) {
  2052. break;
  2053. }
  2054. } while(*myPath); /* Continue until the whole path is consumed */
  2055. }
  2056. }
  2057. /*dataEntry = getFallbackData(resB, &key, &res, status);*/
  2058. if(res != RES_BOGUS) {
  2059. /* check if resB->fResPath gives the right name here */
  2060. if(uprv_strcmp(dataEntry->fName, uloc_getDefault())==0 || uprv_strcmp(dataEntry->fName, kRootLocaleName)==0) {
  2061. *status = U_USING_DEFAULT_WARNING;
  2062. } else {
  2063. *status = U_USING_FALLBACK_WARNING;
  2064. }
  2065. fillIn = init_resb_result(dataEntry, res, key, -1, resB, fillIn, status);
  2066. if (resPath != nullptr) {
  2067. createPath(origResPath, origResPathLen, resPath, len, inKey, path, status);
  2068. } else {
  2069. const char* separator = nullptr;
  2070. if (fillIn->fResPath != nullptr) {
  2071. separator = uprv_strchr(fillIn->fResPath, RES_PATH_SEPARATOR);
  2072. }
  2073. if (separator != nullptr && separator[1] != '\0') {
  2074. createPath(origResPath, origResPathLen, fillIn->fResPath,
  2075. static_cast<int32_t>(uprv_strlen(fillIn->fResPath)), inKey, path, status);
  2076. } else {
  2077. createPath(origResPath, origResPathLen, "", 0, inKey, path, status);
  2078. }
  2079. }
  2080. ures_freeResPath(fillIn);
  2081. ures_appendResPath(fillIn, path.data(), path.length(), status);
  2082. if(fillIn->fResPath[fillIn->fResPathLen-1] != RES_PATH_SEPARATOR) {
  2083. ures_appendResPath(fillIn, RES_PATH_SEPARATOR_S, 1, status);
  2084. }
  2085. } else {
  2086. *status = U_MISSING_RESOURCE_ERROR;
  2087. }
  2088. } else {
  2089. fillIn = init_resb_result(resB->fData, res, key, -1, resB, fillIn, status);
  2090. }
  2091. }
  2092. else {
  2093. *status = U_RESOURCE_TYPE_MISMATCH;
  2094. }
  2095. ures_close(helper);
  2096. return fillIn;
  2097. }
  2098. namespace {
  2099. void getAllItemsWithFallback(
  2100. const UResourceBundle *bundle, ResourceDataValue &value,
  2101. ResourceSink &sink, UErrorCode &errorCode) {
  2102. if (U_FAILURE(errorCode)) { return; }
  2103. // We recursively enumerate child-first,
  2104. // only storing parent items in the absence of child items.
  2105. // The sink needs to store a placeholder value for the no-fallback/no-inheritance marker
  2106. // to prevent a parent item from being stored.
  2107. //
  2108. // It would be possible to recursively enumerate parent-first,
  2109. // overriding parent items with child items.
  2110. // When the sink sees the no-fallback/no-inheritance marker,
  2111. // then it would remove the parent's item.
  2112. // We would deserialize parent values even though they are overridden in a child bundle.
  2113. value.setData(bundle->getResData());
  2114. value.setValidLocaleDataEntry(bundle->fValidLocaleDataEntry);
  2115. UResourceDataEntry *parentEntry = bundle->fData->fParent;
  2116. UBool hasParent = parentEntry != nullptr && U_SUCCESS(parentEntry->fBogus);
  2117. value.setResource(bundle->fRes, ResourceTracer(bundle));
  2118. sink.put(bundle->fKey, value, !hasParent, errorCode);
  2119. if (hasParent) {
  2120. // We might try to query the sink whether
  2121. // any fallback from the parent bundle is still possible.
  2122. // Turn the parent UResourceDataEntry into a UResourceBundle,
  2123. // much like in ures_openWithType().
  2124. // TODO: See if we can refactor ures_getByKeyWithFallback()
  2125. // and pull out an inner function that takes and returns a UResourceDataEntry
  2126. // so that we need not create UResourceBundle objects.
  2127. StackUResourceBundle parentBundle;
  2128. UResourceBundle &parentRef = parentBundle.ref();
  2129. parentRef.fData = parentEntry;
  2130. parentRef.fValidLocaleDataEntry = bundle->fValidLocaleDataEntry;
  2131. parentRef.fHasFallback = !parentRef.getResData().noFallback;
  2132. parentRef.fIsTopLevel = true;
  2133. parentRef.fRes = parentRef.getResData().rootRes;
  2134. parentRef.fSize = res_countArrayItems(&parentRef.getResData(), parentRef.fRes);
  2135. parentRef.fIndex = -1;
  2136. entryIncrease(parentEntry);
  2137. // Look up the container item in the parent bundle.
  2138. StackUResourceBundle containerBundle;
  2139. const UResourceBundle *rb;
  2140. UErrorCode pathErrorCode = U_ZERO_ERROR; // Ignore if parents up to root do not have this path.
  2141. if (bundle->fResPath == nullptr || *bundle->fResPath == 0) {
  2142. rb = parentBundle.getAlias();
  2143. } else {
  2144. rb = ures_getByKeyWithFallback(parentBundle.getAlias(), bundle->fResPath,
  2145. containerBundle.getAlias(), &pathErrorCode);
  2146. }
  2147. if (U_SUCCESS(pathErrorCode)) {
  2148. getAllItemsWithFallback(rb, value, sink, errorCode);
  2149. }
  2150. }
  2151. }
  2152. struct GetAllChildrenSink : public ResourceSink {
  2153. // Destination sink
  2154. ResourceSink& dest;
  2155. GetAllChildrenSink(ResourceSink& dest)
  2156. : dest(dest) {}
  2157. virtual ~GetAllChildrenSink() override;
  2158. virtual void put(const char *key, ResourceValue &value, UBool isRoot,
  2159. UErrorCode &errorCode) override {
  2160. ResourceTable itemsTable = value.getTable(errorCode);
  2161. if (U_FAILURE(errorCode)) { return; }
  2162. for (int32_t i = 0; itemsTable.getKeyAndValue(i, key, value); ++i) {
  2163. if (value.getType() == URES_ALIAS) {
  2164. ResourceDataValue& rdv = static_cast<ResourceDataValue&>(value);
  2165. StackUResourceBundle stackTempBundle;
  2166. UResourceBundle* aliasRB = getAliasTargetAsResourceBundle(rdv.getData(), rdv.getResource(), nullptr, -1,
  2167. rdv.getValidLocaleDataEntry(), nullptr, 0,
  2168. stackTempBundle.getAlias(), &errorCode);
  2169. if (U_SUCCESS(errorCode)) {
  2170. ResourceDataValue aliasedValue;
  2171. aliasedValue.setData(aliasRB->getResData());
  2172. aliasedValue.setValidLocaleDataEntry(aliasRB->fValidLocaleDataEntry);
  2173. aliasedValue.setResource(aliasRB->fRes, ResourceTracer(aliasRB));
  2174. dest.put(key, aliasedValue, isRoot, errorCode);
  2175. }
  2176. } else {
  2177. dest.put(key, value, isRoot, errorCode);
  2178. }
  2179. if (U_FAILURE(errorCode)) { return; }
  2180. }
  2181. }
  2182. };
  2183. // Virtual destructors must be defined out of line.
  2184. GetAllChildrenSink::~GetAllChildrenSink() {}
  2185. U_CAPI void U_EXPORT2
  2186. ures_getAllChildrenWithFallback(const UResourceBundle *bundle, const char *path,
  2187. icu::ResourceSink &sink, UErrorCode &errorCode) {
  2188. GetAllChildrenSink allChildrenSink(sink);
  2189. ures_getAllItemsWithFallback(bundle, path, allChildrenSink, errorCode);
  2190. }
  2191. } // namespace
  2192. // Requires a ResourceDataValue fill-in, so that we need not cast from a ResourceValue.
  2193. // Unfortunately, the caller must know which subclass to make and pass in.
  2194. // Alternatively, we could make it as polymorphic as in Java by
  2195. // returning a ResourceValue pointer (possibly wrapped into a LocalPointer)
  2196. // that the caller then owns.
  2197. //
  2198. // Also requires a UResourceBundle fill-in, so that the value's ResourceTracer
  2199. // can point to a non-local bundle.
  2200. // Without tracing, the child bundle could be a function-local object.
  2201. U_CAPI void U_EXPORT2
  2202. ures_getValueWithFallback(const UResourceBundle *bundle, const char *path,
  2203. UResourceBundle *tempFillIn,
  2204. ResourceDataValue &value, UErrorCode &errorCode) {
  2205. if (U_FAILURE(errorCode)) { return; }
  2206. if (path == nullptr) {
  2207. errorCode = U_ILLEGAL_ARGUMENT_ERROR;
  2208. return;
  2209. }
  2210. const UResourceBundle *rb;
  2211. if (*path == 0) {
  2212. // empty path
  2213. rb = bundle;
  2214. } else {
  2215. rb = ures_getByKeyWithFallback(bundle, path, tempFillIn, &errorCode);
  2216. if (U_FAILURE(errorCode)) {
  2217. return;
  2218. }
  2219. }
  2220. value.setData(rb->getResData());
  2221. value.setValidLocaleDataEntry(rb->fValidLocaleDataEntry);
  2222. value.setResource(rb->fRes, ResourceTracer(rb));
  2223. }
  2224. U_CAPI void U_EXPORT2
  2225. ures_getAllItemsWithFallback(const UResourceBundle *bundle, const char *path,
  2226. icu::ResourceSink &sink, UErrorCode &errorCode) {
  2227. if (U_FAILURE(errorCode)) { return; }
  2228. if (path == nullptr) {
  2229. errorCode = U_ILLEGAL_ARGUMENT_ERROR;
  2230. return;
  2231. }
  2232. StackUResourceBundle stackBundle;
  2233. const UResourceBundle *rb;
  2234. if (*path == 0) {
  2235. // empty path
  2236. rb = bundle;
  2237. } else {
  2238. rb = ures_getByKeyWithFallback(bundle, path, stackBundle.getAlias(), &errorCode);
  2239. if (U_FAILURE(errorCode)) {
  2240. return;
  2241. }
  2242. }
  2243. // Get all table items with fallback.
  2244. ResourceDataValue value;
  2245. getAllItemsWithFallback(rb, value, sink, errorCode);
  2246. }
  2247. U_CAPI UResourceBundle* U_EXPORT2 ures_getByKey(const UResourceBundle *resB, const char* inKey, UResourceBundle *fillIn, UErrorCode *status) {
  2248. Resource res = RES_BOGUS;
  2249. UResourceDataEntry *dataEntry = nullptr;
  2250. const char *key = inKey;
  2251. if (status==nullptr || U_FAILURE(*status)) {
  2252. return fillIn;
  2253. }
  2254. if(resB == nullptr) {
  2255. *status = U_ILLEGAL_ARGUMENT_ERROR;
  2256. return fillIn;
  2257. }
  2258. int32_t type = RES_GET_TYPE(resB->fRes);
  2259. if(URES_IS_TABLE(type)) {
  2260. int32_t t;
  2261. res = res_getTableItemByKey(&resB->getResData(), resB->fRes, &t, &key);
  2262. if(res == RES_BOGUS) {
  2263. key = inKey;
  2264. if(resB->fHasFallback) {
  2265. dataEntry = getFallbackData(resB, &key, &res, status);
  2266. if(U_SUCCESS(*status)) {
  2267. /* check if resB->fResPath gives the right name here */
  2268. return init_resb_result(dataEntry, res, key, -1, resB, fillIn, status);
  2269. } else {
  2270. *status = U_MISSING_RESOURCE_ERROR;
  2271. }
  2272. } else {
  2273. *status = U_MISSING_RESOURCE_ERROR;
  2274. }
  2275. } else {
  2276. return init_resb_result(resB->fData, res, key, -1, resB, fillIn, status);
  2277. }
  2278. }
  2279. #if 0
  2280. /* this is a kind of TODO item. If we have an array with an index table, we could do this. */
  2281. /* not currently */
  2282. else if(RES_GET_TYPE(resB->fRes) == URES_ARRAY && resB->fHasFallback == true) {
  2283. /* here should go a first attempt to locate the key using index table */
  2284. dataEntry = getFallbackData(resB, &key, &res, status);
  2285. if(U_SUCCESS(*status)) {
  2286. return init_resb_result(dataEntry, res, key, resB, fillIn, status);
  2287. } else {
  2288. *status = U_MISSING_RESOURCE_ERROR;
  2289. }
  2290. }
  2291. #endif
  2292. else {
  2293. *status = U_RESOURCE_TYPE_MISMATCH;
  2294. }
  2295. return fillIn;
  2296. }
  2297. U_CAPI const char16_t* U_EXPORT2 ures_getStringByKey(const UResourceBundle *resB, const char* inKey, int32_t* len, UErrorCode *status) {
  2298. Resource res = RES_BOGUS;
  2299. UResourceDataEntry *dataEntry = nullptr;
  2300. const char* key = inKey;
  2301. if (status==nullptr || U_FAILURE(*status)) {
  2302. return nullptr;
  2303. }
  2304. if(resB == nullptr) {
  2305. *status = U_ILLEGAL_ARGUMENT_ERROR;
  2306. return nullptr;
  2307. }
  2308. int32_t type = RES_GET_TYPE(resB->fRes);
  2309. if(URES_IS_TABLE(type)) {
  2310. int32_t t=0;
  2311. res = res_getTableItemByKey(&resB->getResData(), resB->fRes, &t, &key);
  2312. if(res == RES_BOGUS) {
  2313. key = inKey;
  2314. if(resB->fHasFallback) {
  2315. dataEntry = getFallbackData(resB, &key, &res, status);
  2316. if(U_SUCCESS(*status)) {
  2317. switch (RES_GET_TYPE(res)) {
  2318. case URES_STRING:
  2319. case URES_STRING_V2:
  2320. return res_getString({resB, key}, &dataEntry->fData, res, len);
  2321. case URES_ALIAS:
  2322. {
  2323. const char16_t* result = 0;
  2324. UResourceBundle *tempRes = ures_getByKey(resB, inKey, nullptr, status);
  2325. result = ures_getString(tempRes, len, status);
  2326. ures_close(tempRes);
  2327. return result;
  2328. }
  2329. default:
  2330. *status = U_RESOURCE_TYPE_MISMATCH;
  2331. }
  2332. } else {
  2333. *status = U_MISSING_RESOURCE_ERROR;
  2334. }
  2335. } else {
  2336. *status = U_MISSING_RESOURCE_ERROR;
  2337. }
  2338. } else {
  2339. switch (RES_GET_TYPE(res)) {
  2340. case URES_STRING:
  2341. case URES_STRING_V2:
  2342. return res_getString({resB, key}, &resB->getResData(), res, len);
  2343. case URES_ALIAS:
  2344. {
  2345. const char16_t* result = 0;
  2346. UResourceBundle *tempRes = ures_getByKey(resB, inKey, nullptr, status);
  2347. result = ures_getString(tempRes, len, status);
  2348. ures_close(tempRes);
  2349. return result;
  2350. }
  2351. default:
  2352. *status = U_RESOURCE_TYPE_MISMATCH;
  2353. }
  2354. }
  2355. }
  2356. #if 0
  2357. /* this is a kind of TODO item. If we have an array with an index table, we could do this. */
  2358. /* not currently */
  2359. else if(RES_GET_TYPE(resB->fRes) == URES_ARRAY && resB->fHasFallback == true) {
  2360. /* here should go a first attempt to locate the key using index table */
  2361. dataEntry = getFallbackData(resB, &key, &res, status);
  2362. if(U_SUCCESS(*status)) {
  2363. // TODO: Tracing
  2364. return res_getString(rd, res, len);
  2365. } else {
  2366. *status = U_MISSING_RESOURCE_ERROR;
  2367. }
  2368. }
  2369. #endif
  2370. else {
  2371. *status = U_RESOURCE_TYPE_MISMATCH;
  2372. }
  2373. return nullptr;
  2374. }
  2375. U_CAPI const char * U_EXPORT2
  2376. ures_getUTF8StringByKey(const UResourceBundle *resB,
  2377. const char *key,
  2378. char *dest, int32_t *pLength,
  2379. UBool forceCopy,
  2380. UErrorCode *status) {
  2381. int32_t length16;
  2382. const char16_t *s16 = ures_getStringByKey(resB, key, &length16, status);
  2383. return ures_toUTF8String(s16, length16, dest, pLength, forceCopy, status);
  2384. }
  2385. /* TODO: clean from here down */
  2386. /**
  2387. * INTERNAL: Get the name of the first real locale (not placeholder)
  2388. * that has resource bundle data.
  2389. */
  2390. U_CAPI const char* U_EXPORT2
  2391. ures_getLocaleInternal(const UResourceBundle* resourceBundle, UErrorCode* status)
  2392. {
  2393. if (status==nullptr || U_FAILURE(*status)) {
  2394. return nullptr;
  2395. }
  2396. if (!resourceBundle) {
  2397. *status = U_ILLEGAL_ARGUMENT_ERROR;
  2398. return nullptr;
  2399. } else {
  2400. return resourceBundle->fData->fName;
  2401. }
  2402. }
  2403. U_CAPI const char* U_EXPORT2
  2404. ures_getLocale(const UResourceBundle* resourceBundle,
  2405. UErrorCode* status)
  2406. {
  2407. return ures_getLocaleInternal(resourceBundle, status);
  2408. }
  2409. U_CAPI const char* U_EXPORT2
  2410. ures_getLocaleByType(const UResourceBundle* resourceBundle,
  2411. ULocDataLocaleType type,
  2412. UErrorCode* status) {
  2413. if (status==nullptr || U_FAILURE(*status)) {
  2414. return nullptr;
  2415. }
  2416. if (!resourceBundle) {
  2417. *status = U_ILLEGAL_ARGUMENT_ERROR;
  2418. return nullptr;
  2419. } else {
  2420. switch(type) {
  2421. case ULOC_ACTUAL_LOCALE:
  2422. return resourceBundle->fData->fName;
  2423. case ULOC_VALID_LOCALE:
  2424. return resourceBundle->fValidLocaleDataEntry->fName;
  2425. case ULOC_REQUESTED_LOCALE:
  2426. default:
  2427. *status = U_ILLEGAL_ARGUMENT_ERROR;
  2428. return nullptr;
  2429. }
  2430. }
  2431. }
  2432. U_CFUNC const char* ures_getName(const UResourceBundle* resB) {
  2433. if(resB == nullptr) {
  2434. return nullptr;
  2435. }
  2436. return resB->fData->fName;
  2437. }
  2438. #ifdef URES_DEBUG
  2439. U_CFUNC const char* ures_getPath(const UResourceBundle* resB) {
  2440. if(resB == nullptr) {
  2441. return nullptr;
  2442. }
  2443. return resB->fData->fPath;
  2444. }
  2445. #endif
  2446. static UResourceBundle*
  2447. ures_openWithType(UResourceBundle *r, const char* path, const char* localeID,
  2448. UResOpenType openType, UErrorCode* status) {
  2449. if(U_FAILURE(*status)) {
  2450. return nullptr;
  2451. }
  2452. UResourceDataEntry *entry;
  2453. if(openType != URES_OPEN_DIRECT) {
  2454. /* first "canonicalize" the locale ID */
  2455. char canonLocaleID[ULOC_FULLNAME_CAPACITY];
  2456. uloc_getBaseName(localeID, canonLocaleID, UPRV_LENGTHOF(canonLocaleID), status);
  2457. if(U_FAILURE(*status) || *status == U_STRING_NOT_TERMINATED_WARNING) {
  2458. *status = U_ILLEGAL_ARGUMENT_ERROR;
  2459. return nullptr;
  2460. }
  2461. entry = entryOpen(path, canonLocaleID, openType, status);
  2462. } else {
  2463. entry = entryOpenDirect(path, localeID, status);
  2464. }
  2465. if(U_FAILURE(*status)) {
  2466. return nullptr;
  2467. }
  2468. if(entry == nullptr) {
  2469. *status = U_MISSING_RESOURCE_ERROR;
  2470. return nullptr;
  2471. }
  2472. UBool isStackObject;
  2473. if(r == nullptr) {
  2474. r = (UResourceBundle *)uprv_malloc(sizeof(UResourceBundle));
  2475. if(r == nullptr) {
  2476. entryClose(entry);
  2477. *status = U_MEMORY_ALLOCATION_ERROR;
  2478. return nullptr;
  2479. }
  2480. isStackObject = false;
  2481. } else { // fill-in
  2482. isStackObject = ures_isStackObject(r);
  2483. ures_closeBundle(r, false);
  2484. }
  2485. uprv_memset(r, 0, sizeof(UResourceBundle));
  2486. ures_setIsStackObject(r, isStackObject);
  2487. r->fValidLocaleDataEntry = r->fData = entry;
  2488. r->fHasFallback = openType != URES_OPEN_DIRECT && !r->getResData().noFallback;
  2489. r->fIsTopLevel = true;
  2490. r->fRes = r->getResData().rootRes;
  2491. r->fSize = res_countArrayItems(&r->getResData(), r->fRes);
  2492. r->fIndex = -1;
  2493. ResourceTracer(r).traceOpen();
  2494. return r;
  2495. }
  2496. U_CAPI UResourceBundle* U_EXPORT2
  2497. ures_open(const char* path, const char* localeID, UErrorCode* status) {
  2498. return ures_openWithType(nullptr, path, localeID, URES_OPEN_LOCALE_DEFAULT_ROOT, status);
  2499. }
  2500. U_CAPI UResourceBundle* U_EXPORT2
  2501. ures_openNoDefault(const char* path, const char* localeID, UErrorCode* status) {
  2502. return ures_openWithType(nullptr, path, localeID, URES_OPEN_LOCALE_ROOT, status);
  2503. }
  2504. /**
  2505. * Opens a resource bundle without "canonicalizing" the locale name. No fallback will be performed
  2506. * or sought. However, alias substitution will happen!
  2507. */
  2508. U_CAPI UResourceBundle* U_EXPORT2
  2509. ures_openDirect(const char* path, const char* localeID, UErrorCode* status) {
  2510. return ures_openWithType(nullptr, path, localeID, URES_OPEN_DIRECT, status);
  2511. }
  2512. /**
  2513. * Internal API: This function is used to open a resource bundle
  2514. * proper fallback chaining is executed while initialization.
  2515. * The result is stored in cache for later fallback search.
  2516. *
  2517. * Same as ures_open(), but uses the fill-in parameter and does not allocate a new bundle.
  2518. */
  2519. U_CAPI void U_EXPORT2
  2520. ures_openFillIn(UResourceBundle *r, const char* path,
  2521. const char* localeID, UErrorCode* status) {
  2522. if(U_SUCCESS(*status) && r == nullptr) {
  2523. *status = U_ILLEGAL_ARGUMENT_ERROR;
  2524. return;
  2525. }
  2526. ures_openWithType(r, path, localeID, URES_OPEN_LOCALE_DEFAULT_ROOT, status);
  2527. }
  2528. /**
  2529. * Same as ures_openDirect(), but uses the fill-in parameter and does not allocate a new bundle.
  2530. */
  2531. U_CAPI void U_EXPORT2
  2532. ures_openDirectFillIn(UResourceBundle *r, const char* path, const char* localeID, UErrorCode* status) {
  2533. if(U_SUCCESS(*status) && r == nullptr) {
  2534. *status = U_ILLEGAL_ARGUMENT_ERROR;
  2535. return;
  2536. }
  2537. ures_openWithType(r, path, localeID, URES_OPEN_DIRECT, status);
  2538. }
  2539. /**
  2540. * API: Counts members. For arrays and tables, returns number of resources.
  2541. * For strings, returns 1.
  2542. */
  2543. U_CAPI int32_t U_EXPORT2
  2544. ures_countArrayItems(const UResourceBundle* resourceBundle,
  2545. const char* resourceKey,
  2546. UErrorCode* status)
  2547. {
  2548. UResourceBundle resData;
  2549. ures_initStackObject(&resData);
  2550. if (status==nullptr || U_FAILURE(*status)) {
  2551. return 0;
  2552. }
  2553. if(resourceBundle == nullptr) {
  2554. *status = U_ILLEGAL_ARGUMENT_ERROR;
  2555. return 0;
  2556. }
  2557. ures_getByKey(resourceBundle, resourceKey, &resData, status);
  2558. if(resData.getResData().data != nullptr) {
  2559. int32_t result = res_countArrayItems(&resData.getResData(), resData.fRes);
  2560. ures_close(&resData);
  2561. return result;
  2562. } else {
  2563. *status = U_MISSING_RESOURCE_ERROR;
  2564. ures_close(&resData);
  2565. return 0;
  2566. }
  2567. }
  2568. /**
  2569. * Internal function.
  2570. * Return the version number associated with this ResourceBundle as a string.
  2571. *
  2572. * @param resourceBundle The resource bundle for which the version is checked.
  2573. * @return A version number string as specified in the resource bundle or its parent.
  2574. * The caller does not own this string.
  2575. * @see ures_getVersion
  2576. * @internal
  2577. */
  2578. U_CAPI const char* U_EXPORT2
  2579. ures_getVersionNumberInternal(const UResourceBundle *resourceBundle)
  2580. {
  2581. if (!resourceBundle) return nullptr;
  2582. if(resourceBundle->fVersion == nullptr) {
  2583. /* If the version ID has not been built yet, then do so. Retrieve */
  2584. /* the minor version from the file. */
  2585. UErrorCode status = U_ZERO_ERROR;
  2586. int32_t minor_len = 0;
  2587. int32_t len;
  2588. const char16_t* minor_version = ures_getStringByKey(resourceBundle, kVersionTag, &minor_len, &status);
  2589. /* Determine the length of of the final version string. This is */
  2590. /* the length of the major part + the length of the separator */
  2591. /* (==1) + the length of the minor part (+ 1 for the zero byte at */
  2592. /* the end). */
  2593. len = (minor_len > 0) ? minor_len : 1;
  2594. /* Allocate the string, and build it up. */
  2595. /* + 1 for zero byte */
  2596. ((UResourceBundle *)resourceBundle)->fVersion = (char *)uprv_malloc(1 + len);
  2597. /* Check for null pointer. */
  2598. if (((UResourceBundle *)resourceBundle)->fVersion == nullptr) {
  2599. return nullptr;
  2600. }
  2601. if(minor_len > 0) {
  2602. u_UCharsToChars(minor_version, resourceBundle->fVersion , minor_len);
  2603. resourceBundle->fVersion[len] = '\0';
  2604. }
  2605. else {
  2606. uprv_strcpy(resourceBundle->fVersion, kDefaultMinorVersion);
  2607. }
  2608. }
  2609. return resourceBundle->fVersion;
  2610. }
  2611. U_CAPI const char* U_EXPORT2
  2612. ures_getVersionNumber(const UResourceBundle* resourceBundle)
  2613. {
  2614. return ures_getVersionNumberInternal(resourceBundle);
  2615. }
  2616. U_CAPI void U_EXPORT2 ures_getVersion(const UResourceBundle* resB, UVersionInfo versionInfo) {
  2617. if (!resB) return;
  2618. u_versionFromString(versionInfo, ures_getVersionNumberInternal(resB));
  2619. }
  2620. /** Tree support functions *******************************/
  2621. #define INDEX_LOCALE_NAME "res_index"
  2622. #define INDEX_TAG "InstalledLocales"
  2623. #define DEFAULT_TAG "default"
  2624. #if defined(URES_TREE_DEBUG)
  2625. #include <stdio.h>
  2626. #endif
  2627. typedef struct ULocalesContext {
  2628. UResourceBundle installed;
  2629. UResourceBundle curr;
  2630. } ULocalesContext;
  2631. static void U_CALLCONV
  2632. ures_loc_closeLocales(UEnumeration *enumerator) {
  2633. ULocalesContext *ctx = (ULocalesContext *)enumerator->context;
  2634. ures_close(&ctx->curr);
  2635. ures_close(&ctx->installed);
  2636. uprv_free(ctx);
  2637. uprv_free(enumerator);
  2638. }
  2639. static int32_t U_CALLCONV
  2640. ures_loc_countLocales(UEnumeration *en, UErrorCode * /*status*/) {
  2641. ULocalesContext *ctx = (ULocalesContext *)en->context;
  2642. return ures_getSize(&ctx->installed);
  2643. }
  2644. U_CDECL_BEGIN
  2645. static const char * U_CALLCONV
  2646. ures_loc_nextLocale(UEnumeration* en,
  2647. int32_t* resultLength,
  2648. UErrorCode* status) {
  2649. ULocalesContext *ctx = (ULocalesContext *)en->context;
  2650. UResourceBundle *res = &(ctx->installed);
  2651. UResourceBundle *k = nullptr;
  2652. const char *result = nullptr;
  2653. int32_t len = 0;
  2654. if(ures_hasNext(res) && (k = ures_getNextResource(res, &ctx->curr, status)) != 0) {
  2655. result = ures_getKey(k);
  2656. len = (int32_t)uprv_strlen(result);
  2657. }
  2658. if (resultLength) {
  2659. *resultLength = len;
  2660. }
  2661. return result;
  2662. }
  2663. static void U_CALLCONV
  2664. ures_loc_resetLocales(UEnumeration* en,
  2665. UErrorCode* /*status*/) {
  2666. UResourceBundle *res = &((ULocalesContext *)en->context)->installed;
  2667. ures_resetIterator(res);
  2668. }
  2669. U_CDECL_END
  2670. static const UEnumeration gLocalesEnum = {
  2671. nullptr,
  2672. nullptr,
  2673. ures_loc_closeLocales,
  2674. ures_loc_countLocales,
  2675. uenum_unextDefault,
  2676. ures_loc_nextLocale,
  2677. ures_loc_resetLocales
  2678. };
  2679. U_CAPI UEnumeration* U_EXPORT2
  2680. ures_openAvailableLocales(const char *path, UErrorCode *status)
  2681. {
  2682. UResourceBundle *idx = nullptr;
  2683. UEnumeration *en = nullptr;
  2684. ULocalesContext *myContext = nullptr;
  2685. if(U_FAILURE(*status)) {
  2686. return nullptr;
  2687. }
  2688. myContext = static_cast<ULocalesContext *>(uprv_malloc(sizeof(ULocalesContext)));
  2689. en = (UEnumeration *)uprv_malloc(sizeof(UEnumeration));
  2690. if(!en || !myContext) {
  2691. *status = U_MEMORY_ALLOCATION_ERROR;
  2692. uprv_free(en);
  2693. uprv_free(myContext);
  2694. return nullptr;
  2695. }
  2696. uprv_memcpy(en, &gLocalesEnum, sizeof(UEnumeration));
  2697. ures_initStackObject(&myContext->installed);
  2698. ures_initStackObject(&myContext->curr);
  2699. idx = ures_openDirect(path, INDEX_LOCALE_NAME, status);
  2700. ures_getByKey(idx, INDEX_TAG, &myContext->installed, status);
  2701. if(U_SUCCESS(*status)) {
  2702. #if defined(URES_TREE_DEBUG)
  2703. fprintf(stderr, "Got %s::%s::[%s] : %s\n",
  2704. path, INDEX_LOCALE_NAME, INDEX_TAG, ures_getKey(&myContext->installed));
  2705. #endif
  2706. en->context = myContext;
  2707. } else {
  2708. #if defined(URES_TREE_DEBUG)
  2709. fprintf(stderr, "%s open failed - %s\n", path, u_errorName(*status));
  2710. #endif
  2711. ures_close(&myContext->installed);
  2712. uprv_free(myContext);
  2713. uprv_free(en);
  2714. en = nullptr;
  2715. }
  2716. ures_close(idx);
  2717. return en;
  2718. }
  2719. static UBool isLocaleInList(UEnumeration *locEnum, const char *locToSearch, UErrorCode *status) {
  2720. const char *loc;
  2721. while ((loc = uenum_next(locEnum, nullptr, status)) != nullptr) {
  2722. if (uprv_strcmp(loc, locToSearch) == 0) {
  2723. return true;
  2724. }
  2725. }
  2726. return false;
  2727. }
  2728. U_CAPI int32_t U_EXPORT2
  2729. ures_getFunctionalEquivalent(char *result, int32_t resultCapacity,
  2730. const char *path, const char *resName, const char *keyword, const char *locid,
  2731. UBool *isAvailable, UBool omitDefault, UErrorCode *status)
  2732. {
  2733. char kwVal[1024] = ""; /* value of keyword 'keyword' */
  2734. char defVal[1024] = ""; /* default value for given locale */
  2735. char defLoc[1024] = ""; /* default value for given locale */
  2736. char base[1024] = ""; /* base locale */
  2737. char found[1024] = "";
  2738. char parent[1024] = "";
  2739. char full[1024] = "";
  2740. UResourceBundle bund1, bund2;
  2741. UResourceBundle *res = nullptr;
  2742. UErrorCode subStatus = U_ZERO_ERROR;
  2743. int32_t length = 0;
  2744. if(U_FAILURE(*status)) return 0;
  2745. uloc_getKeywordValue(locid, keyword, kwVal, 1024-1,&subStatus);
  2746. if(!uprv_strcmp(kwVal, DEFAULT_TAG)) {
  2747. kwVal[0]=0;
  2748. }
  2749. uloc_getBaseName(locid, base, 1024-1,&subStatus);
  2750. #if defined(URES_TREE_DEBUG)
  2751. fprintf(stderr, "getFunctionalEquivalent: \"%s\" [%s=%s] in %s - %s\n",
  2752. locid, keyword, kwVal, base, u_errorName(subStatus));
  2753. #endif
  2754. ures_initStackObject(&bund1);
  2755. ures_initStackObject(&bund2);
  2756. uprv_strcpy(parent, base);
  2757. uprv_strcpy(found, base);
  2758. if(isAvailable) {
  2759. UEnumeration *locEnum = ures_openAvailableLocales(path, &subStatus);
  2760. *isAvailable = true;
  2761. if (U_SUCCESS(subStatus)) {
  2762. *isAvailable = isLocaleInList(locEnum, parent, &subStatus);
  2763. }
  2764. uenum_close(locEnum);
  2765. }
  2766. if(U_FAILURE(subStatus)) {
  2767. *status = subStatus;
  2768. return 0;
  2769. }
  2770. do {
  2771. subStatus = U_ZERO_ERROR;
  2772. res = ures_open(path, parent, &subStatus);
  2773. if(((subStatus == U_USING_FALLBACK_WARNING) ||
  2774. (subStatus == U_USING_DEFAULT_WARNING)) && isAvailable)
  2775. {
  2776. *isAvailable = false;
  2777. }
  2778. isAvailable = nullptr; /* only want to set this the first time around */
  2779. #if defined(URES_TREE_DEBUG)
  2780. fprintf(stderr, "%s;%s -> %s [%s]\n", path?path:"ICUDATA", parent, u_errorName(subStatus), ures_getLocale(res, &subStatus));
  2781. #endif
  2782. if(U_FAILURE(subStatus)) {
  2783. *status = subStatus;
  2784. } else if(subStatus == U_ZERO_ERROR) {
  2785. ures_getByKey(res,resName,&bund1, &subStatus);
  2786. if(subStatus == U_ZERO_ERROR) {
  2787. const char16_t *defUstr;
  2788. int32_t defLen;
  2789. /* look for default item */
  2790. #if defined(URES_TREE_DEBUG)
  2791. fprintf(stderr, "%s;%s : loaded default -> %s\n",
  2792. path?path:"ICUDATA", parent, u_errorName(subStatus));
  2793. #endif
  2794. defUstr = ures_getStringByKey(&bund1, DEFAULT_TAG, &defLen, &subStatus);
  2795. if(U_SUCCESS(subStatus) && defLen) {
  2796. u_UCharsToChars(defUstr, defVal, u_strlen(defUstr));
  2797. #if defined(URES_TREE_DEBUG)
  2798. fprintf(stderr, "%s;%s -> default %s=%s, %s\n",
  2799. path?path:"ICUDATA", parent, keyword, defVal, u_errorName(subStatus));
  2800. #endif
  2801. uprv_strcpy(defLoc, parent);
  2802. if(kwVal[0]==0) {
  2803. uprv_strcpy(kwVal, defVal);
  2804. #if defined(URES_TREE_DEBUG)
  2805. fprintf(stderr, "%s;%s -> kwVal = %s\n",
  2806. path?path:"ICUDATA", parent, keyword, kwVal);
  2807. #endif
  2808. }
  2809. }
  2810. }
  2811. }
  2812. subStatus = U_ZERO_ERROR;
  2813. if (res != nullptr) {
  2814. uprv_strcpy(found, ures_getLocaleByType(res, ULOC_VALID_LOCALE, &subStatus));
  2815. }
  2816. uloc_getParent(found,parent,sizeof(parent),&subStatus);
  2817. ures_close(res);
  2818. } while(!defVal[0] && *found && uprv_strcmp(found, "root") != 0 && U_SUCCESS(*status));
  2819. /* Now, see if we can find the kwVal collator.. start the search over.. */
  2820. uprv_strcpy(parent, base);
  2821. uprv_strcpy(found, base);
  2822. do {
  2823. subStatus = U_ZERO_ERROR;
  2824. res = ures_open(path, parent, &subStatus);
  2825. if((subStatus == U_USING_FALLBACK_WARNING) && isAvailable) {
  2826. *isAvailable = false;
  2827. }
  2828. isAvailable = nullptr; /* only want to set this the first time around */
  2829. #if defined(URES_TREE_DEBUG)
  2830. fprintf(stderr, "%s;%s -> %s (looking for %s)\n",
  2831. path?path:"ICUDATA", parent, u_errorName(subStatus), kwVal);
  2832. #endif
  2833. if(U_FAILURE(subStatus)) {
  2834. *status = subStatus;
  2835. } else if(subStatus == U_ZERO_ERROR) {
  2836. ures_getByKey(res,resName,&bund1, &subStatus);
  2837. #if defined(URES_TREE_DEBUG)
  2838. /**/ fprintf(stderr,"@%d [%s] %s\n", __LINE__, resName, u_errorName(subStatus));
  2839. #endif
  2840. if(subStatus == U_ZERO_ERROR) {
  2841. ures_getByKey(&bund1, kwVal, &bund2, &subStatus);
  2842. #if defined(URES_TREE_DEBUG)
  2843. /**/ fprintf(stderr,"@%d [%s] %s\n", __LINE__, kwVal, u_errorName(subStatus));
  2844. #endif
  2845. if(subStatus == U_ZERO_ERROR) {
  2846. #if defined(URES_TREE_DEBUG)
  2847. fprintf(stderr, "%s;%s -> full0 %s=%s, %s\n",
  2848. path?path:"ICUDATA", parent, keyword, kwVal, u_errorName(subStatus));
  2849. #endif
  2850. uprv_strcpy(full, parent);
  2851. if(*full == 0) {
  2852. uprv_strcpy(full, "root");
  2853. }
  2854. /* now, recalculate default kw if need be */
  2855. if(uprv_strlen(defLoc) > uprv_strlen(full)) {
  2856. const char16_t *defUstr;
  2857. int32_t defLen;
  2858. /* look for default item */
  2859. #if defined(URES_TREE_DEBUG)
  2860. fprintf(stderr, "%s;%s -> recalculating Default0\n",
  2861. path?path:"ICUDATA", full);
  2862. #endif
  2863. defUstr = ures_getStringByKey(&bund1, DEFAULT_TAG, &defLen, &subStatus);
  2864. if(U_SUCCESS(subStatus) && defLen) {
  2865. u_UCharsToChars(defUstr, defVal, u_strlen(defUstr));
  2866. #if defined(URES_TREE_DEBUG)
  2867. fprintf(stderr, "%s;%s -> default0 %s=%s, %s\n",
  2868. path?path:"ICUDATA", full, keyword, defVal, u_errorName(subStatus));
  2869. #endif
  2870. uprv_strcpy(defLoc, full);
  2871. }
  2872. } /* end of recalculate default KW */
  2873. #if defined(URES_TREE_DEBUG)
  2874. else {
  2875. fprintf(stderr, "No trim0, %s <= %s\n", defLoc, full);
  2876. }
  2877. #endif
  2878. } else {
  2879. #if defined(URES_TREE_DEBUG)
  2880. fprintf(stderr, "err=%s in %s looking for %s\n",
  2881. u_errorName(subStatus), parent, kwVal);
  2882. #endif
  2883. }
  2884. }
  2885. }
  2886. subStatus = U_ZERO_ERROR;
  2887. uprv_strcpy(found, parent);
  2888. uloc_getParent(found,parent,1023,&subStatus);
  2889. ures_close(res);
  2890. } while(!full[0] && *found && U_SUCCESS(*status));
  2891. if((full[0]==0) && uprv_strcmp(kwVal, defVal)) {
  2892. #if defined(URES_TREE_DEBUG)
  2893. fprintf(stderr, "Failed to locate kw %s - try default %s\n", kwVal, defVal);
  2894. #endif
  2895. uprv_strcpy(kwVal, defVal);
  2896. uprv_strcpy(parent, base);
  2897. uprv_strcpy(found, base);
  2898. do { /* search for 'default' named item */
  2899. subStatus = U_ZERO_ERROR;
  2900. res = ures_open(path, parent, &subStatus);
  2901. if((subStatus == U_USING_FALLBACK_WARNING) && isAvailable) {
  2902. *isAvailable = false;
  2903. }
  2904. isAvailable = nullptr; /* only want to set this the first time around */
  2905. #if defined(URES_TREE_DEBUG)
  2906. fprintf(stderr, "%s;%s -> %s (looking for default %s)\n",
  2907. path?path:"ICUDATA", parent, u_errorName(subStatus), kwVal);
  2908. #endif
  2909. if(U_FAILURE(subStatus)) {
  2910. *status = subStatus;
  2911. } else if(subStatus == U_ZERO_ERROR) {
  2912. ures_getByKey(res,resName,&bund1, &subStatus);
  2913. if(subStatus == U_ZERO_ERROR) {
  2914. ures_getByKey(&bund1, kwVal, &bund2, &subStatus);
  2915. if(subStatus == U_ZERO_ERROR) {
  2916. #if defined(URES_TREE_DEBUG)
  2917. fprintf(stderr, "%s;%s -> full1 %s=%s, %s\n", path?path:"ICUDATA",
  2918. parent, keyword, kwVal, u_errorName(subStatus));
  2919. #endif
  2920. uprv_strcpy(full, parent);
  2921. if(*full == 0) {
  2922. uprv_strcpy(full, "root");
  2923. }
  2924. /* now, recalculate default kw if need be */
  2925. if(uprv_strlen(defLoc) > uprv_strlen(full)) {
  2926. const char16_t *defUstr;
  2927. int32_t defLen;
  2928. /* look for default item */
  2929. #if defined(URES_TREE_DEBUG)
  2930. fprintf(stderr, "%s;%s -> recalculating Default1\n",
  2931. path?path:"ICUDATA", full);
  2932. #endif
  2933. defUstr = ures_getStringByKey(&bund1, DEFAULT_TAG, &defLen, &subStatus);
  2934. if(U_SUCCESS(subStatus) && defLen) {
  2935. u_UCharsToChars(defUstr, defVal, u_strlen(defUstr));
  2936. #if defined(URES_TREE_DEBUG)
  2937. fprintf(stderr, "%s;%s -> default %s=%s, %s\n",
  2938. path?path:"ICUDATA", full, keyword, defVal, u_errorName(subStatus));
  2939. #endif
  2940. uprv_strcpy(defLoc, full);
  2941. }
  2942. } /* end of recalculate default KW */
  2943. #if defined(URES_TREE_DEBUG)
  2944. else {
  2945. fprintf(stderr, "No trim1, %s <= %s\n", defLoc, full);
  2946. }
  2947. #endif
  2948. }
  2949. }
  2950. }
  2951. subStatus = U_ZERO_ERROR;
  2952. uprv_strcpy(found, parent);
  2953. uloc_getParent(found,parent,1023,&subStatus);
  2954. ures_close(res);
  2955. } while(!full[0] && *found && U_SUCCESS(*status));
  2956. }
  2957. if(U_SUCCESS(*status)) {
  2958. if(!full[0]) {
  2959. #if defined(URES_TREE_DEBUG)
  2960. fprintf(stderr, "Still could not load keyword %s=%s\n", keyword, kwVal);
  2961. #endif
  2962. *status = U_MISSING_RESOURCE_ERROR;
  2963. } else if(omitDefault) {
  2964. #if defined(URES_TREE_DEBUG)
  2965. fprintf(stderr,"Trim? full=%s, defLoc=%s, found=%s\n", full, defLoc, found);
  2966. #endif
  2967. if(uprv_strlen(defLoc) <= uprv_strlen(full)) {
  2968. /* found the keyword in a *child* of where the default tag was present. */
  2969. if(!uprv_strcmp(kwVal, defVal)) { /* if the requested kw is default, */
  2970. /* and the default is in or in an ancestor of the current locale */
  2971. #if defined(URES_TREE_DEBUG)
  2972. fprintf(stderr, "Removing unneeded var %s=%s\n", keyword, kwVal);
  2973. #endif
  2974. kwVal[0]=0;
  2975. }
  2976. }
  2977. }
  2978. uprv_strcpy(found, full);
  2979. if(kwVal[0]) {
  2980. uprv_strcat(found, "@");
  2981. uprv_strcat(found, keyword);
  2982. uprv_strcat(found, "=");
  2983. uprv_strcat(found, kwVal);
  2984. } else if(!omitDefault) {
  2985. uprv_strcat(found, "@");
  2986. uprv_strcat(found, keyword);
  2987. uprv_strcat(found, "=");
  2988. uprv_strcat(found, defVal);
  2989. }
  2990. }
  2991. /* we found the default locale - no need to repeat it.*/
  2992. ures_close(&bund1);
  2993. ures_close(&bund2);
  2994. length = (int32_t)uprv_strlen(found);
  2995. if(U_SUCCESS(*status)) {
  2996. int32_t copyLength = uprv_min(length, resultCapacity);
  2997. if(copyLength>0) {
  2998. uprv_strncpy(result, found, copyLength);
  2999. }
  3000. if(length == 0) {
  3001. *status = U_MISSING_RESOURCE_ERROR;
  3002. }
  3003. } else {
  3004. length = 0;
  3005. result[0]=0;
  3006. }
  3007. return u_terminateChars(result, resultCapacity, length, status);
  3008. }
  3009. U_CAPI UEnumeration* U_EXPORT2
  3010. ures_getKeywordValues(const char *path, const char *keyword, UErrorCode *status)
  3011. {
  3012. #define VALUES_BUF_SIZE 2048
  3013. #define VALUES_LIST_SIZE 512
  3014. char valuesBuf[VALUES_BUF_SIZE];
  3015. int32_t valuesIndex = 0;
  3016. const char *valuesList[VALUES_LIST_SIZE];
  3017. int32_t valuesCount = 0;
  3018. const char *locale;
  3019. int32_t locLen;
  3020. UEnumeration *locs = nullptr;
  3021. UResourceBundle item;
  3022. UResourceBundle subItem;
  3023. ures_initStackObject(&item);
  3024. ures_initStackObject(&subItem);
  3025. locs = ures_openAvailableLocales(path, status);
  3026. if(U_FAILURE(*status)) {
  3027. ures_close(&item);
  3028. ures_close(&subItem);
  3029. return nullptr;
  3030. }
  3031. valuesBuf[0]=0;
  3032. valuesBuf[1]=0;
  3033. while((locale = uenum_next(locs, &locLen, status)) != 0) {
  3034. UResourceBundle *bund = nullptr;
  3035. UResourceBundle *subPtr = nullptr;
  3036. UErrorCode subStatus = U_ZERO_ERROR; /* don't fail if a bundle is unopenable */
  3037. bund = ures_open(path, locale, &subStatus);
  3038. #if defined(URES_TREE_DEBUG)
  3039. if(!bund || U_FAILURE(subStatus)) {
  3040. fprintf(stderr, "%s-%s values: Can't open %s locale - skipping. (%s)\n",
  3041. path?path:"<ICUDATA>", keyword, locale, u_errorName(subStatus));
  3042. }
  3043. #endif
  3044. ures_getByKey(bund, keyword, &item, &subStatus);
  3045. if(!bund || U_FAILURE(subStatus)) {
  3046. #if defined(URES_TREE_DEBUG)
  3047. fprintf(stderr, "%s-%s values: Can't find in %s - skipping. (%s)\n",
  3048. path?path:"<ICUDATA>", keyword, locale, u_errorName(subStatus));
  3049. #endif
  3050. ures_close(bund);
  3051. bund = nullptr;
  3052. continue;
  3053. }
  3054. while((subPtr = ures_getNextResource(&item,&subItem,&subStatus)) != 0
  3055. && U_SUCCESS(subStatus)) {
  3056. const char *k;
  3057. int32_t i;
  3058. k = ures_getKey(subPtr);
  3059. #if defined(URES_TREE_DEBUG)
  3060. /* fprintf(stderr, "%s | %s | %s | %s\n", path?path:"<ICUDATA>", keyword, locale, k); */
  3061. #endif
  3062. if(k == nullptr || *k == 0 ||
  3063. uprv_strcmp(k, DEFAULT_TAG) == 0 || uprv_strncmp(k, "private-", 8) == 0) {
  3064. // empty or "default" or unlisted type
  3065. continue;
  3066. }
  3067. for(i=0; i<valuesCount; i++) {
  3068. if(!uprv_strcmp(valuesList[i],k)) {
  3069. k = nullptr; /* found duplicate */
  3070. break;
  3071. }
  3072. }
  3073. if(k != nullptr) {
  3074. int32_t kLen = (int32_t)uprv_strlen(k);
  3075. if((valuesCount >= (VALUES_LIST_SIZE-1)) || /* no more space in list .. */
  3076. ((valuesIndex+kLen+1+1) >= VALUES_BUF_SIZE)) { /* no more space in buffer (string + 2 nulls) */
  3077. *status = U_ILLEGAL_ARGUMENT_ERROR; /* out of space.. */
  3078. } else {
  3079. uprv_strcpy(valuesBuf+valuesIndex, k);
  3080. valuesList[valuesCount++] = valuesBuf+valuesIndex;
  3081. valuesIndex += kLen;
  3082. #if defined(URES_TREE_DEBUG)
  3083. fprintf(stderr, "%s | %s | %s | [%s] (UNIQUE)\n",
  3084. path?path:"<ICUDATA>", keyword, locale, k);
  3085. #endif
  3086. valuesBuf[valuesIndex++] = 0; /* terminate */
  3087. }
  3088. }
  3089. }
  3090. ures_close(bund);
  3091. }
  3092. valuesBuf[valuesIndex++] = 0; /* terminate */
  3093. ures_close(&item);
  3094. ures_close(&subItem);
  3095. uenum_close(locs);
  3096. #if defined(URES_TREE_DEBUG)
  3097. fprintf(stderr, "%s: size %d, #%d\n", u_errorName(*status),
  3098. valuesIndex, valuesCount);
  3099. #endif
  3100. return uloc_openKeywordList(valuesBuf, valuesIndex, status);
  3101. }
  3102. #if 0
  3103. /* This code isn't needed, and given the documentation warnings the implementation is suspect */
  3104. U_CAPI UBool U_EXPORT2
  3105. ures_equal(const UResourceBundle* res1, const UResourceBundle* res2){
  3106. if(res1==nullptr || res2==nullptr){
  3107. return res1==res2; /* pointer comparison */
  3108. }
  3109. if(res1->fKey==nullptr|| res2->fKey==nullptr){
  3110. return (res1->fKey==res2->fKey);
  3111. }else{
  3112. if(uprv_strcmp(res1->fKey, res2->fKey)!=0){
  3113. return false;
  3114. }
  3115. }
  3116. if(uprv_strcmp(res1->fData->fName, res2->fData->fName)!=0){
  3117. return false;
  3118. }
  3119. if(res1->fData->fPath == nullptr|| res2->fData->fPath==nullptr){
  3120. return (res1->fData->fPath == res2->fData->fPath);
  3121. }else{
  3122. if(uprv_strcmp(res1->fData->fPath, res2->fData->fPath)!=0){
  3123. return false;
  3124. }
  3125. }
  3126. if(uprv_strcmp(res1->fData->fParent->fName, res2->fData->fParent->fName)!=0){
  3127. return false;
  3128. }
  3129. if(uprv_strcmp(res1->fData->fParent->fPath, res2->fData->fParent->fPath)!=0){
  3130. return false;
  3131. }
  3132. if(uprv_strncmp(res1->fResPath, res2->fResPath, res1->fResPathLen)!=0){
  3133. return false;
  3134. }
  3135. if(res1->fRes != res2->fRes){
  3136. return false;
  3137. }
  3138. return true;
  3139. }
  3140. U_CAPI UResourceBundle* U_EXPORT2
  3141. ures_clone(const UResourceBundle* res, UErrorCode* status){
  3142. UResourceBundle* bundle = nullptr;
  3143. UResourceBundle* ret = nullptr;
  3144. if(U_FAILURE(*status) || res == nullptr){
  3145. return nullptr;
  3146. }
  3147. bundle = ures_open(res->fData->fPath, res->fData->fName, status);
  3148. if(res->fResPath!=nullptr){
  3149. ret = ures_findSubResource(bundle, res->fResPath, nullptr, status);
  3150. ures_close(bundle);
  3151. }else{
  3152. ret = bundle;
  3153. }
  3154. return ret;
  3155. }
  3156. U_CAPI const UResourceBundle* U_EXPORT2
  3157. ures_getParentBundle(const UResourceBundle* res){
  3158. if(res==nullptr){
  3159. return nullptr;
  3160. }
  3161. return res->fParentRes;
  3162. }
  3163. #endif
  3164. U_CAPI void U_EXPORT2
  3165. ures_getVersionByKey(const UResourceBundle* res, const char *key, UVersionInfo ver, UErrorCode *status) {
  3166. const char16_t *str;
  3167. int32_t len;
  3168. str = ures_getStringByKey(res, key, &len, status);
  3169. if(U_SUCCESS(*status)) {
  3170. u_versionFromUString(ver, str);
  3171. }
  3172. }
  3173. /* eof */