text:
// © 2016 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html
/*
**********************************************************************
*   Copyright (C) 2001-2006, International Business Machines
*   Corporation and others.  All Rights Reserved.
**********************************************************************
*/

#ifndef USTRFMT_H
#define USTRFMT_H

#include "unicode/utypes.h"

U_CAPI int32_t U_EXPORT2
uprv_itou (UChar * buffer, int32_t capacity, uint32_t i, uint32_t radix, int32_t minwidth);

#endif


text:
// © 2016 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html
/*
**********************************************************************
*   Copyright (C) 1999-2011, International Business Machines
*   Corporation and others.  All Rights Reserved.
**********************************************************************
*/

#include "unicode/chariter.h"

U_NAMESPACE_BEGIN

ForwardCharacterIterator::~ForwardCharacterIterator() {}
ForwardCharacterIterator::ForwardCharacterIterator()
: UObject()
{}
ForwardCharacterIterator::ForwardCharacterIterator(const ForwardCharacterIterator &other)
: UObject(other)
{}

CharacterIterator::CharacterIterator()
: textLength(0), pos(0), begin(0), end(0) {
}

CharacterIterator::CharacterIterator(int32_t length)
: textLength(length), pos(0), begin(0), end(length) {
    if(textLength < 0) {
        textLength = end = 0;
    }
}

CharacterIterator::CharacterIterator(int32_t length, int32_t position)
: textLength(length), pos(position), begin(0), end(length) {
    if(textLength < 0) {
        textLength = end = 0;
    }
    if(pos < 0) {
        pos = 0;
    } else if(pos > end) {
        pos = end;
    }
}

CharacterIterator::CharacterIterator(int32_t length, int32_t textBegin, int32_t textEnd, int32_t position)
: textLength(length), pos(position), begin(textBegin), end(textEnd) {
    if(textLength < 0) {
        textLength = 0;
    }
    if(begin < 0) {
        begin = 0;
    } else if(begin > textLength) {
        begin = textLength;
    }
    if(end < begin) {
        end = begin;
    } else if(end > textLength) {
        end = textLength;
    }
    if(pos < begin) {
        pos = begin;
    } else if(pos > end) {
        pos = end;
    }
}

CharacterIterator::~CharacterIterator() {}

CharacterIterator::CharacterIterator(const CharacterIterator &that) :
ForwardCharacterIterator(that),
textLength(that.textLength), pos(that.pos), begin(that.begin), end(that.end)
{
}

CharacterIterator &
CharacterIterator::operator=(const CharacterIterator &that) {
    ForwardCharacterIterator::operator=(that);
    textLength = that.textLength;
    pos = that.pos;
    begin = that.begin;
    end = that.end;
    return *this;
}

// implementing first[32]PostInc() directly in a subclass should be faster
// but these implementations make subclassing a little easier
UChar
CharacterIterator::firstPostInc(void) {
    setToStart();
    return nextPostInc();
}

UChar32
CharacterIterator::first32PostInc(void) {
    setToStart();
    return next32PostInc();
}

U_NAMESPACE_END

repo_language: C

mime_type: text/x-c


text:
// © 2016 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html
/*
*******************************************************************************
*
*   Copyright (C) 2001-2011, International Business Machines
*   Corporation and others.  All Rights Reserved.
*
*******************************************************************************
*   file name:  unormimp.h
*   encoding:   UTF-8
*   tab size:   8 (not used)
*   indentation:4
*
*   created on: 2001may25
*   created by: Markus W. Scherer
*/

#ifndef __UNORMIMP_H__
#define __UNORMIMP_H__

#include "unicode/utypes.h"

#if !UCONFIG_NO_NORMALIZATION

#include "udataswp.h"

/*
 * The 2001-2010 implementation of the normalization code loads its data from
 * unorm.icu, which is generated with the gennorm tool.
 * The format of that file is described at the end of this file.
 */

/* norm32 value constants */
enum {
    /* quick check flags 0..3 set mean "no" for their forms */
    _NORM_QC_NFC=0x11,          /* no|maybe */
    _NORM_QC_NFKC=0x22,         /* no|maybe */
    _NORM_QC_NFD=4,             /* no */
    _NORM_QC_NFKD=8,            /* no */

    _NORM_QC_ANY_NO=0xf,

    /* quick check flags 4..5 mean "maybe" for their forms; test flags>=_NORM_QC_MAYBE */
    _NORM_QC_MAYBE=0x10,
    _NORM_QC_ANY_MAYBE=0x30,

    _NORM_QC_MASK=0x3f,

    _NORM_COMBINES_FWD=0x40,
    _NORM_COMBINES_BACK=0x80,
    _NORM_COMBINES_ANY=0xc0,

    _NORM_CC_SHIFT=8,           /* UnicodeData.txt combining class in bits 15..8 */
    _NORM_CC_MASK=0xff00,

    _NORM_EXTRA_SHIFT=16,               /* 16 bits for the index to UChars and other extra data */
    _NORM_EXTRA_INDEX_TOP=0xfc00,       /* start of surrogate specials after shift */

    _NORM_EXTRA_SURROGATE_MASK=0x3ff,
    _NORM_EXTRA_SURROGATE_TOP=0x3f0,    /* hangul etc. */

    _NORM_EXTRA_HANGUL=_NORM_EXTRA_SURROGATE_TOP,
    _NORM_EXTRA_JAMO_L,
    _NORM_EXTRA_JAMO_V,
    _NORM_EXTRA_JAMO_T
};

/* norm32 value constants using >16 bits */
#define _NORM_MIN_SPECIAL       0xfc000000
#define _NORM_SURROGATES_TOP    0xfff00000
#define _NORM_MIN_HANGUL        0xfff00000
#define _NORM_MIN_JAMO_V        0xfff20000
#define _NORM_JAMO_V_TOP        0xfff30000

/* value constants for auxTrie */
enum {
    _NORM_AUX_COMP_EX_SHIFT=10,
    _NORM_AUX_UNSAFE_SHIFT=11,
    _NORM_AUX_NFC_SKIPPABLE_F_SHIFT=12
};

#define _NORM_AUX_MAX_FNC           ((int32_t)1<<_NORM_AUX_COMP_EX_SHIFT)

#define _NORM_AUX_FNC_MASK          (uint32_t)(_NORM_AUX_MAX_FNC-1)
#define _NORM_AUX_COMP_EX_MASK      ((uint32_t)1<<_NORM_AUX_COMP_EX_SHIFT)
#define _NORM_AUX_UNSAFE_MASK       ((uint32_t)1<<_NORM_AUX_UNSAFE_SHIFT)
#define _NORM_AUX_NFC_SKIP_F_MASK   ((uint32_t)1<<_NORM_AUX_NFC_SKIPPABLE_F_SHIFT)

/* canonStartSets[0..31] contains indexes for what is in the array */
enum {
    _NORM_SET_INDEX_CANON_SETS_LENGTH,      /* number of uint16_t in canonical starter sets */
    _NORM_SET_INDEX_CANON_BMP_TABLE_LENGTH, /* number of uint16_t in the BMP search table (contains pairs) */
    _NORM_SET_INDEX_CANON_SUPP_TABLE_LENGTH,/* number of uint16_t in the supplementary search table (contains triplets) */

    /* from formatVersion 2.3: */
    _NORM_SET_INDEX_NX_CJK_COMPAT_OFFSET,   /* uint16_t offset from canonStartSets[0] to the
                                               exclusion set for CJK compatibility characters */
    _NORM_SET_INDEX_NX_UNICODE32_OFFSET,    /* uint16_t offset from canonStartSets[0] to the
                                               exclusion set for Unicode 3.2 characters */
    _NORM_SET_INDEX_NX_RESERVED_OFFSET,     /* uint16_t offset from canonStartSets[0] to the
                                               end of the previous exclusion set */

    _NORM_SET_INDEX_TOP=32                  /* changing this requires a new formatVersion */
};

/* more constants for canonical starter sets */

/* 14 bit indexes to canonical USerializedSets */
#define _NORM_MAX_CANON_SETS            0x4000

/* single-code point BMP sets are encoded directly in the search table except if result=0x4000..0x7fff */
#define _NORM_CANON_SET_BMP_MASK        0xc000
#define _NORM_CANON_SET_BMP_IS_INDEX    0x4000

/* indexes[] value names */
enum {
    _NORM_INDEX_TRIE_SIZE,              /* number of bytes in normalization trie */
    _NORM_INDEX_UCHAR_COUNT,            /* number of UChars in extra data */

    _NORM_INDEX_COMBINE_DATA_COUNT,     /* number of uint16_t words for combining data */
    _NORM_INDEX_COMBINE_FWD_COUNT,      /* number of code points that combine forward */
    _NORM_INDEX_COMBINE_BOTH_COUNT,     /* number of code points that combine forward and backward */
    _NORM_INDEX_COMBINE_BACK_COUNT,     /* number of code points that combine backward */

    _NORM_INDEX_MIN_NFC_NO_MAYBE,       /* first code point with quick check NFC NO/MAYBE */
    _NORM_INDEX_MIN_NFKC_NO_MAYBE,      /* first code point with quick check NFKC NO/MAYBE */
    _NORM_INDEX_MIN_NFD_NO_MAYBE,       /* first code point with quick check NFD NO/MAYBE */
    _NORM_INDEX_MIN_NFKD_NO_MAYBE,      /* first code point with quick check NFKD NO/MAYBE */

    _NORM_INDEX_FCD_TRIE_SIZE,          /* number of bytes in FCD trie */

    _NORM_INDEX_AUX_TRIE_SIZE,          /* number of bytes in the auxiliary trie */
    _NORM_INDEX_CANON_SET_COUNT,        /* number of uint16_t in the array of serialized USet */

    _NORM_INDEX_TOP=32                  /* changing this requires a new formatVersion */
};

enum {
    /* FCD check: everything below this code point is known to have a 0 lead combining class */
    _NORM_MIN_WITH_LEAD_CC=0x300
};

enum {
    /**
     * Bit 7 of the length byte for a decomposition string in extra data is
     * a flag indicating whether the decomposition string is
     * preceded by a 16-bit word with the leading and trailing cc
     * of the decomposition (like for A-umlaut);
     * if not, then both cc's are zero (like for compatibility ideographs).
     */
    _NORM_DECOMP_FLAG_LENGTH_HAS_CC=0x80,
    /**
     * Bits 6..0 of the length byte contain the actual length.
     */
    _NORM_DECOMP_LENGTH_MASK=0x7f
};

/** Constants for options flags for normalization. */
enum {
    /** Options bit 0, do not decompose Hangul syllables. */
    UNORM_NX_HANGUL=1,
    /** Options bit 1, do not decompose CJK compatibility characters. */
    UNORM_NX_CJK_COMPAT=2
};

/**
 * Description of the format of unorm.icu version 2.3.
 *
 * Main change from version 1 to version 2:
 * Use of new, common UTrie instead of normalization-specific tries.
 * Change to version 2.1: add third/auxiliary trie with associated data.
 * Change to version 2.2: add skippable (f) flag data (_NORM_AUX_NFC_SKIP_F_MASK).
 * Change to version 2.3: add serialized sets for normalization exclusions
 *                        stored inside canonStartSets[]
 *
 * For more details of how to use the data structures see the code
 * in unorm.cpp (runtime normalization code) and
 * in gennorm.c and gennorm/store.c (build-time data generation).
 *
 * For the serialized format of UTrie see utrie.c/UTrieHeader.
 *
 * - Overall partition
 *
 * unorm.dat customarily begins with a UDataInfo structure, see udata.h and .c.
 * After that there are the following structures:
 *
 * int32_t indexes[_NORM_INDEX_TOP];            -- _NORM_INDEX_TOP=32, see enum in this file
 *
 * UTrie normTrie;                              -- size in bytes=indexes[_NORM_INDEX_TRIE_SIZE]
 *
 * uint16_t extraData[extraDataTop];            -- extraDataTop=indexes[_NORM_INDEX_UCHAR_COUNT]
 *                                                 extraData[0] contains the number of units for
 *                                                 FC_NFKC_Closure (formatVersion>=2.1)
 *
 * uint16_t combiningTable[combiningTableTop];  -- combiningTableTop=indexes[_NORM_INDEX_COMBINE_DATA_COUNT]
 *                                                 combiningTableTop may include one 16-bit padding unit
 *                                                 to make sure that fcdTrie is 32-bit-aligned
 *
 * UTrie fcdTrie;                               -- size in bytes=indexes[_NORM_INDEX_FCD_TRIE_SIZE]
 *
 * UTrie auxTrie;                               -- size in bytes=indexes[_NORM_INDEX_AUX_TRIE_SIZE]
 *
 * uint16_t canonStartSets[canonStartSetsTop]   -- canonStartSetsTop=indexes[_NORM_INDEX_CANON_SET_COUNT]
 *                                                 serialized USets and binary search tables, see below
 *
 *
 * The indexes array contains lengths and sizes of the following arrays and structures
 * as well as the following values:
 *  indexes[_NORM_INDEX_COMBINE_FWD_COUNT]=combineFwdTop
 *      -- one more than the highest combining index computed for forward-only-combining characters
 *  indexes[_NORM_INDEX_COMBINE_BOTH_COUNT]=combineBothTop-combineFwdTop
 *      -- number of combining indexes computed for both-ways-combining characters
 *  indexes[_NORM_INDEX_COMBINE_BACK_COUNT]=combineBackTop-combineBothTop
 *      -- number of combining indexes computed for backward-only-combining characters
 *
 *  indexes[_NORM_INDEX_MIN_NF*_NO_MAYBE] (where *={ C, D, KC, KD })
 *      -- first code point with a quick check NF* value of NO/MAYBE
 *
 *
 * - Tries
 *
 * The main structures are two UTrie tables ("compact arrays"),
 * each with one index array and one data array.
 * See utrie.h and utrie.c.
 *
 *
 * - Tries in unorm.dat
 *
 * The first trie (normTrie above)
 * provides data for the NF* quick checks and normalization.
 * The second trie (fcdTrie above) provides data just for FCD checks.
 *
 *
 * - norm32 data words from the first trie
 *
 * The norm32Table contains one 32-bit word "norm32" per code point.
 * It contains the following bit fields:
 * 31..16   extra data index, _NORM_EXTRA_SHIFT is used to shift this field down
 *          if this index is <_NORM_EXTRA_INDEX_TOP then it is an index into
 *              extraData[] where variable-length normalization data for this
 *              code point is found
 *          if this index is <_NORM_EXTRA_INDEX_TOP+_NORM_EXTRA_SURROGATE_TOP
 *              then this is a norm32 for a leading surrogate, and the index
 *              value is used together with the following trailing surrogate
 *              code unit in the second trie access
 *          if this index is >=_NORM_EXTRA_INDEX_TOP+_NORM_EXTRA_SURROGATE_TOP
 *              then this is a norm32 for a "special" character,
 *              i.e., the character is a Hangul syllable or a Jamo
 *              see _NORM_EXTRA_HANGUL etc.
 *          generally, instead of extracting this index from the norm32 and
 *              comparing it with the above constants,
 *              the normalization code compares the entire norm32 value
 *              with _NORM_MIN_SPECIAL, _NORM_SURROGATES_TOP, _NORM_MIN_HANGUL etc.
 *
 * 15..8    combining class (cc) according to UnicodeData.txt
 *
 *  7..6    _NORM_COMBINES_ANY flags, used in composition to see if a character
 *              combines with any following or preceding character(s)
 *              at all
 *     7    _NORM_COMBINES_BACK
 *     6    _NORM_COMBINES_FWD
 *
 *  5..0    quick check flags, set for "no" or "maybe", with separate flags for
 *              each normalization form
 *              the higher bits are "maybe" flags; for NF*D there are no such flags
 *              the lower bits are "no" flags for all forms, in the same order
 *              as the "maybe" flags,
 *              which is (MSB to LSB): NFKD NFD NFKC NFC
 *  5..4    _NORM_QC_ANY_MAYBE
 *  3..0    _NORM_QC_ANY_NO
 *              see further related constants
 *
 *
 * - Extra data per code point
 *
 * "Extra data" is referenced by the index in norm32.
 * It is variable-length data. It is only present, and only those parts
 * of it are, as needed for a given character.
 * The norm32 extra data index is added to the beginning of extraData[]
 * to get to a vector of 16-bit words with data at the following offsets:
 *
 * [-1]     Combining index for composition.
 *              Stored only if norm32&_NORM_COMBINES_ANY .
 * [0]      Lengths of the canonical and compatibility decomposition strings.
 *              Stored only if there are decompositions, i.e.,
 *              if norm32&(_NORM_QC_NFD|_NORM_QC_NFKD)
 *          High byte: length of NFKD, or 0 if none
 *          Low byte: length of NFD, or 0 if none
 *          Each length byte also has another flag:
 *              Bit 7 of a length byte is set if there are non-zero
 *              combining classes (cc's) associated with the respective
 *              decomposition. If this flag is set, then the decomposition
 *              is preceded by a 16-bit word that contains the
 *              leading and trailing cc's.
 *              Bits 6..0 of a length byte are the length of the
 *              decomposition string, not counting the cc word.
 * [1..n]   NFD
 * [n+1..]  NFKD
 *
 * Each of the two decompositions consists of up to two parts:
 * - The 16-bit words with the leading and trailing cc's.
 *   This is only stored if bit 7 of the corresponding length byte
 *   is set. In this case, at least one of the cc's is not zero.
 *   High byte: leading cc==cc of the first code point in the decomposition string
 *   Low byte: trailing cc==cc of the last code point in the decomposition string
 * - The decomposition string in UTF-16, with length code units.
 *
 *
 * - Combining indexes and combiningTable[]
 *
 * Combining indexes are stored at the [-1] offset of the extra data
 * if the character combines forward or backward with any other characters.
 * They are used for (re)composition in NF*C.
 * Values of combining indexes are arranged according to whether a character
 * combines forward, backward, or both ways:
 *    forward-only < both ways < backward-only
 *
 * The index values for forward-only and both-ways combining characters
 * are indexes into the combiningTable[].
 * The index values for backward-only combining characters are simply
 * incremented from the preceding index values to be unique.
 *
 * In the combiningTable[], a variable-length list
 * of variable-length (back-index, code point) pair entries is stored
 * for each forward-combining character.
 *
 * These back-indexes are the combining indexes of both-ways or backward-only
 * combining characters that the forward-combining character combines with.
 *
 * Each list is sorted in ascending order of back-indexes.
 * Each list is terminated with the last back-index having bit 15 set.
 *
 * Each pair (back-index, code point) takes up either 2 or 3
 * 16-bit words.
 * The first word of a list entry is the back-index, with its bit 15 set if
 * this is the last pair in the list.
 *
 * The second word contains flags in bits 15..13 that determine
 * if there is a third word and how the combined character is encoded:
 * 15   set if there is a third word in this list entry
 * 14   set if the result is a supplementary character
 * 13   set if the result itself combines forward
 *
 * According to these bits 15..14 of the second word,
 * the result character is encoded as follows:
 * 00 or 01 The result is <=0x1fff and stored in bits 12..0 of
 *          the second word.
 * 10       The result is 0x2000..0xffff and stored in the third word.
 *          Bits 12..0 of the second word are not used.
 * 11       The result is a supplementary character.
 *          Bits 9..0 of the leading surrogate are in bits 9..0 of
 *          the second word.
 *          Add 0xd800 to these bits to get the complete surrogate.
 *          Bits 12..10 of the second word are not used.
 *          The trailing surrogate is stored in the third word.
 *
 *
 * - FCD trie
 *
 * The FCD trie is very simple.
 * It is a folded trie with 16-bit data words.
 * In each word, the high byte contains the leading cc of the character,
 * and the low byte contains the trailing cc of the character.
 * These cc's are the cc's of the first and last code points in the
 * canonical decomposition of the character.
 *
 * Since all 16 bits are used for cc's, lead surrogates must be tested
 * by checking the code unit instead of the trie data.
 * This is done only if the 16-bit data word is not zero.
 * If the code unit is a leading surrogate and the data word is not zero,
 * then instead of cc's it contains the offset for the second trie lookup.
 *
 *
 * - Auxiliary trie and data
 *
 * The auxiliary 16-bit trie contains data for additional properties.
 * Bits
 * 15..13   reserved
 *     12   not NFC_Skippable (f) (formatVersion>=2.2)
 *     11   flag: not a safe starter for canonical closure
 *     10   composition exclusion
 *  9.. 0   index into extraData[] to FC_NFKC_Closure string
 *          (not for lead surrogate),
 *          or lead surrogate offset (for lead surrogate, if 9..0 not zero)
 *
 * - FC_NFKC_Closure strings in extraData[]
 *
 * Strings are either stored as a single code unit or as the length
 * followed by that many units.
 *   const UChar *s=extraData+(index from auxTrie data bits 9..0);
 *   int32_t length;
 *   if(*s<0xff00) {
 *     // s points to the single-unit string
 *     length=1;
 *   } else {
 *     length=*s&0xff;
 *     ++s;
 *   }
 *
 * Conditions for "NF* Skippable" from Mark Davis' com.ibm.text.UCD.NFSkippable:
 * (used in NormalizerTransliterator)
 *
 * A skippable character is
 * a) unassigned, or ALL of the following:
 * b) of combining class 0.
 * c) not decomposed by this normalization form.
 * AND if NFC or NFKC,
 * d) can never compose with a previous character.
 * e) can never compose with a following character.
 * f) can never change if another character is added.
 *    Example: a-breve might satisfy all but f, but if you
 *    add an ogonek it changes to a-ogonek + breve
 *
 * a)..e) must be tested from norm32.
 * Since f) is more complicated, the (not-)NFC_Skippable flag (f) is built
 * into the auxiliary trie.
 * The same bit is used for NFC and NFKC; (c) differs for them.
 * As usual, we build the "not skippable" flags so that unassigned
 * code points get a 0 bit.
 * This bit is only valid after (a)..(e) test FALSE; test NFD_NO before (f) as well.
 * Test Hangul LV syllables entirely in code.
 *
 *
 * - structure inside canonStartSets[]
 *
 * This array maps from code points c to sets of code points (USerializedSet).
 * The result sets are the code points whose canonical decompositions start
 * with c.
 *
 * canonStartSets[] contains the following sub-arrays:
 *
 * indexes[_NORM_SET_INDEX_TOP]
 *   - contains lengths of sub-arrays etc.
 *
 * startSets[indexes[_NORM_SET_INDEX_CANON_SETS_LENGTH]-_NORM_SET_INDEX_TOP]
 *   - contains serialized sets (USerializedSet) of canonical starters for
 *     enumerating canonically equivalent strings
 *     indexes[_NORM_SET_INDEX_CANON_SETS_LENGTH] includes _NORM_SET_INDEX_TOP
 *     for details about the structure see uset.c
 *
 * bmpTable[indexes[_NORM_SET_INDEX_CANON_BMP_TABLE_LENGTH]]
 *   - a sorted search table for BMP code points whose results are
 *     either indexes to USerializedSets or single code points for
 *     single-code point sets;
 *     each entry is a pair of { code point, result } with result=(binary) yy xxxxxx xxxxxxxx
 *     if yy==01 then there is a USerializedSet at canonStartSets+x
 *     else build a USerializedSet with result as the single code point
 *
 * suppTable[indexes[_NORM_SET_INDEX_CANON_SUPP_TABLE_LENGTH]]
 *   - a sorted search table for supplementary code points whose results are
 *     either indexes to USerializedSets or single code points for
 *     single-code point sets;
 *     each entry is a triplet of { high16(cp), low16(cp), result }
 *     each code point's high-word may contain extra data in bits 15..5:
 *     if the high word has bit 15 set, then build a set with a single code point
 *     which is (((high16(cp)&0x1f00)<<8)|result;
 *     else there is a USerializedSet at canonStartSets+result
 *
 * FormatVersion 2.3 adds 2 serialized sets for normalization exclusions.
 * They are stored in the data file so that the runtime normalization code need
 * not depend on other properties and their data and implementation files.
 * The _NORM_SET_INDEX_NX_..._OFFSET offsets in the canonStartSets index table
 * give the location for each set.
 * There is no set stored for UNORM_NX_HANGUL because it's trivial to create
 * without using properties.
 *
 * Set contents:
 *
 * _NORM_SET_INDEX_NX_CJK_COMPAT_OFFSET (for UNORM_NX_CJK_COMPAT)
 *     [[:Ideographic:]&[:NFD_QC=No:]]
 *     =[CJK Ideographs]&[has canonical decomposition]
 *
 * _NORM_SET_INDEX_NX_UNICODE32_OFFSET (for UNORM_UNICODE_3_2)
 *     [:^Age=3.2:]
 *     =set with all code points that were not designated by the specified Unicode version
 *
 * _NORM_SET_INDEX_NX_RESERVED_OFFSET
 *     This is an offset that points to where the next, future set would start.
 *     Currently it indicates where the previous set ends, and thus its length.
 *     The name for this enum constant may in the future be applied to different
 *     index slots. In order to get the limit of a set, use its index slot and
 *     the immediately following one regardless of that one's enum name.
 */

#endif /* #if !UCONFIG_NO_NORMALIZATION */

#endif


text:
// © 2016 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html
/*
*******************************************************************************
*
*   Copyright (C) 1997-2013, International Business Machines
*   Corporation and others.  All Rights Reserved.
*
*******************************************************************************
*   file name:  locavailable.cpp
*   encoding:   UTF-8
*   tab size:   8 (not used)
*   indentation:4
*
*   created on: 2010feb25
*   created by: Markus W. Scherer
*
*   Code for available locales, separated out from other .cpp files
*   that then do not depend on resource bundle code and res_index bundles.
*/

#include "unicode/errorcode.h"
#include "unicode/utypes.h"
#include "unicode/locid.h"
#include "unicode/uloc.h"
#include "unicode/ures.h"
#include "cmemory.h"
#include "cstring.h"
#include "ucln_cmn.h"
#include "uassert.h"
#include "umutex.h"
#include "uresimp.h"

// C++ API ----------------------------------------------------------------- ***

U_NAMESPACE_BEGIN

static icu::Locale*  availableLocaleList = NULL;
static int32_t  availableLocaleListCount;
static icu::UInitOnce gInitOnceLocale = U_INITONCE_INITIALIZER;

U_NAMESPACE_END

U_CDECL_BEGIN

static UBool U_CALLCONV locale_available_cleanup(void)
{
    U_NAMESPACE_USE

    if (availableLocaleList) {
        delete []availableLocaleList;
        availableLocaleList = NULL;
    }
    availableLocaleListCount = 0;
    gInitOnceLocale.reset();

    return TRUE;
}

U_CDECL_END

U_NAMESPACE_BEGIN

void U_CALLCONV locale_available_init() {
    // This function is a friend of class Locale.
    // This function is only invoked via umtx_initOnce().

    // for now, there is a hardcoded list, so just walk through that list and set it up.
    //  Note: this function is a friend of class Locale.
    availableLocaleListCount = uloc_countAvailable();
    if(availableLocaleListCount) {
       availableLocaleList = new Locale[availableLocaleListCount];
    }
    if (availableLocaleList == NULL) {
        availableLocaleListCount= 0;
    }
    for (int32_t locCount=availableLocaleListCount-1; locCount>=0; --locCount) {
        availableLocaleList[locCount].setFromPOSIXID(uloc_getAvailable(locCount));
    }
    ucln_common_registerCleanup(UCLN_COMMON_LOCALE_AVAILABLE, locale_available_cleanup);
}

const Locale* U_EXPORT2
Locale::getAvailableLocales(int32_t& count)
{
    umtx_initOnce(gInitOnceLocale, &locale_available_init);
    count = availableLocaleListCount;
    return availableLocaleList;
}

U_NAMESPACE_END

// C API ------------------------------------------------------------------- ***

U_NAMESPACE_USE

/* ### Constants **************************************************/

namespace {

// Enough capacity for the two lists in the res_index.res file
const char** gAvailableLocaleNames[2] = {};
int32_t gAvailableLocaleCounts[2] = {};
icu::UInitOnce ginstalledLocalesInitOnce = U_INITONCE_INITIALIZER;

class AvailableLocalesSink : public ResourceSink {
  public:
    void put(const char *key, ResourceValue &value, UBool /*noFallback*/, UErrorCode &status) U_OVERRIDE {
        ResourceTable resIndexTable = value.getTable(status);
        if (U_FAILURE(status)) {
            return;
        }
        for (int32_t i = 0; resIndexTable.getKeyAndValue(i, key, value); ++i) {
            ULocAvailableType type;
            if (uprv_strcmp(key, "InstalledLocales") == 0) {
                type = ULOC_AVAILABLE_DEFAULT;
            } else if (uprv_strcmp(key, "AliasLocales") == 0) {
                type = ULOC_AVAILABLE_ONLY_LEGACY_ALIASES;
            } else {
                // CLDRVersion, etc.
                continue;
            }
            ResourceTable availableLocalesTable = value.getTable(status);
            if (U_FAILURE(status)) {
                return;
            }
            gAvailableLocaleCounts[type] = availableLocalesTable.getSize();
            gAvailableLocaleNames[type] = static_cast<const char**>(
                uprv_malloc(gAvailableLocaleCounts[type] * sizeof(const char*)));
            if (gAvailableLocaleNames[type] == nullptr) {
                status = U_MEMORY_ALLOCATION_ERROR;
                return;
            }
            for (int32_t j = 0; availableLocalesTable.getKeyAndValue(j, key, value); ++j) {
                gAvailableLocaleNames[type][j] = key;
            }
        }
    }
};

class AvailableLocalesStringEnumeration : public StringEnumeration {
  public:
    AvailableLocalesStringEnumeration(ULocAvailableType type) : fType(type) {
    }

    const char* next(int32_t *resultLength, UErrorCode&) override {
        ULocAvailableType actualType = fType;
        int32_t actualIndex = fIndex++;

        // If the "combined" list was requested, resolve that now
        if (fType == ULOC_AVAILABLE_WITH_LEGACY_ALIASES) {
            int32_t defaultLocalesCount = gAvailableLocaleCounts[ULOC_AVAILABLE_DEFAULT];
            if (actualIndex < defaultLocalesCount) {
                actualType = ULOC_AVAILABLE_DEFAULT;
            } else {
                actualIndex -= defaultLocalesCount;
                actualType = ULOC_AVAILABLE_ONLY_LEGACY_ALIASES;
            }
        }

        // Return the requested string
        int32_t count = gAvailableLocaleCounts[actualType];
        const char* result;
        if (actualIndex < count) {
            result = gAvailableLocaleNames[actualType][actualIndex];
            if (resultLength != nullptr) {
                *resultLength = static_cast<int32_t>(uprv_strlen(result));
            }
        } else {
            result = nullptr;
            if (resultLength != nullptr) {
                *resultLength = 0;
            }
        }
        return result;
    }

    void reset(UErrorCode&) override {
        fIndex = 0;
    }

    int32_t count(UErrorCode&) const override {
        if (fType == ULOC_AVAILABLE_WITH_LEGACY_ALIASES) {
            return gAvailableLocaleCounts[ULOC_AVAILABLE_DEFAULT]
                + gAvailableLocaleCounts[ULOC_AVAILABLE_ONLY_LEGACY_ALIASES];
        } else {
            return gAvailableLocaleCounts[fType];
        }
    }

  private:
    ULocAvailableType fType;
    int32_t fIndex = 0;
};

/* ### Get available **************************************************/

static UBool U_CALLCONV uloc_cleanup(void) {
    for (int32_t i = 0; i < UPRV_LENGTHOF(gAvailableLocaleNames); i++) {
        uprv_free(gAvailableLocaleNames[i]);
        gAvailableLocaleNames[i] = nullptr;
        gAvailableLocaleCounts[i] = 0;
    }
    ginstalledLocalesInitOnce.reset();
    return TRUE;
}

// Load Installed Locales. This function will be called exactly once
//   via the initOnce mechanism.

static void U_CALLCONV loadInstalledLocales(UErrorCode& status) {
    ucln_common_registerCleanup(UCLN_COMMON_ULOC, uloc_cleanup);

    icu::LocalUResourceBundlePointer rb(ures_openDirect(NULL, "res_index", &status));
    AvailableLocalesSink sink;
    ures_getAllItemsWithFallback(rb.getAlias(), "", sink, status);
}

void _load_installedLocales(UErrorCode& status) {
    umtx_initOnce(ginstalledLocalesInitOnce, &loadInstalledLocales, status);
}

} // namespace

U_CAPI const char* U_EXPORT2
uloc_getAvailable(int32_t offset) {
    icu::ErrorCode status;
    _load_installedLocales(status);
    if (status.isFailure()) {
        return nullptr;
    }
    if (offset > gAvailableLocaleCounts[0]) {
        // *status = U_ILLEGAL_ARGUMENT_ERROR;
        return nullptr;
    }
    return gAvailableLocaleNames[0][offset];
}

U_CAPI int32_t  U_EXPORT2
uloc_countAvailable() {
    icu::ErrorCode status;
    _load_installedLocales(status);
    if (status.isFailure()) {
        return 0;
    }
    return gAvailableLocaleCounts[0];
}

U_CAPI UEnumeration* U_EXPORT2
uloc_openAvailableByType(ULocAvailableType type, UErrorCode* status) {
    if (U_FAILURE(*status)) {
        return nullptr;
    }
    if (type < 0 || type >= ULOC_AVAILABLE_COUNT) {
        *status = U_ILLEGAL_ARGUMENT_ERROR;
        return nullptr;
    }
    _load_installedLocales(*status);
    if (U_FAILURE(*status)) {
        return nullptr;
    }
    LocalPointer<AvailableLocalesStringEnumeration> result(
        new AvailableLocalesStringEnumeration(type), *status);
    if (U_FAILURE(*status)) {
        return nullptr;
    }
    return uenum_openFromStringEnumeration(result.orphan(), status);
}


text:
// © 2016 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html
/*
******************************************************************************
*
*   Copyright (C) 2009-2015, International Business Machines
*   Corporation and others.  All Rights Reserved.
*
******************************************************************************
*
*  FILE NAME : icuplug.c
*
*   Date         Name        Description
*   10/29/2009   sl          New.
******************************************************************************
*/

#include "unicode/icuplug.h"

#if UCONFIG_ENABLE_PLUGINS

#include "icuplugimp.h"
#include "cstring.h"
#include "cmemory.h"
#include "putilimp.h"
#include "ucln.h"
#include <stdio.h>
#ifdef __MVS__  /* defined by z/OS compiler */
#define _POSIX_SOURCE
#include <cics.h> /* 12 Nov 2011 JAM iscics() function */
#endif
#include "charstr.h"

using namespace icu;

#ifndef UPLUG_TRACE
#define UPLUG_TRACE 0
#endif

#if UPLUG_TRACE
#include <stdio.h>
#define DBG(x) fprintf(stderr, "%s:%d: ",__FILE__,__LINE__); fprintf x
#endif

/**
 * Internal structure of an ICU plugin.
 */

struct UPlugData {
  UPlugEntrypoint  *entrypoint; /**< plugin entrypoint */
  uint32_t structSize;    /**< initialized to the size of this structure */
  uint32_t token;         /**< must be U_PLUG_TOKEN */
  void *lib;              /**< plugin library, or NULL */
  char libName[UPLUG_NAME_MAX];   /**< library name */
  char sym[UPLUG_NAME_MAX];        /**< plugin symbol, or NULL */
  char config[UPLUG_NAME_MAX];     /**< configuration data */
  void *context;          /**< user context data */
  char name[UPLUG_NAME_MAX];   /**< name of plugin */
  UPlugLevel  level; /**< level of plugin */
  UBool   awaitingLoad; /**< TRUE if the plugin is awaiting a load call */
  UBool   dontUnload; /**< TRUE if plugin must stay resident (leak plugin and lib) */
  UErrorCode pluginStatus; /**< status code of plugin */
};

#define UPLUG_LIBRARY_INITIAL_COUNT 8
#define UPLUG_PLUGIN_INITIAL_COUNT 12

/**
 * Remove an item
 * @param list the full list
 * @param listSize the number of entries in the list
 * @param memberSize the size of one member
 * @param itemToRemove the item number of the member
 * @return the new listsize
 */
static int32_t uplug_removeEntryAt(void *list, int32_t listSize, int32_t memberSize, int32_t itemToRemove) {
  uint8_t *bytePtr = (uint8_t *)list;

  /* get rid of some bad cases first */
  if(listSize<1) {
    return listSize;
  }

  /* is there anything to move? */
  if(listSize > itemToRemove+1) {
    memmove(bytePtr+(itemToRemove*memberSize), bytePtr+((itemToRemove+1)*memberSize), memberSize);
  }

  return listSize-1;
}

#if U_ENABLE_DYLOAD
/**
 * Library management. Internal.
 * @internal
 */
struct UPlugLibrary;

/**
 * Library management. Internal.
 * @internal
 */
typedef struct UPlugLibrary {
  void *lib;                           /**< library ptr */
  char name[UPLUG_NAME_MAX]; /**< library name */
  uint32_t ref;                        /**< reference count */
} UPlugLibrary;

static UPlugLibrary   staticLibraryList[UPLUG_LIBRARY_INITIAL_COUNT];
static UPlugLibrary * libraryList = staticLibraryList;
static int32_t libraryCount = 0;
static int32_t libraryMax = UPLUG_LIBRARY_INITIAL_COUNT;

/**
 * Search for a library. Doesn't lock
 * @param libName libname to search for
 * @return the library's struct
 */
static int32_t searchForLibraryName(const char *libName) {
  int32_t i;

  for(i=0;i<libraryCount;i++) {
    if(!uprv_strcmp(libName, libraryList[i].name)) {
      return i;
    }
  }
  return -1;
}

static int32_t searchForLibrary(void *lib) {
  int32_t i;

  for(i=0;i<libraryCount;i++) {
    if(lib==libraryList[i].lib) {
      return i;
    }
  }
  return -1;
}

U_INTERNAL char * U_EXPORT2
uplug_findLibrary(void *lib, UErrorCode *status) {
  int32_t libEnt;
  char *ret = NULL;
  if(U_FAILURE(*status)) {
    return NULL;
  }
  libEnt = searchForLibrary(lib);
  if(libEnt!=-1) {
    ret = libraryList[libEnt].name;
  } else {
    *status = U_MISSING_RESOURCE_ERROR;
  }
  return ret;
}

U_INTERNAL void * U_EXPORT2
uplug_openLibrary(const char *libName, UErrorCode *status) {
  int32_t libEntry = -1;
  void *lib = NULL;

  if(U_FAILURE(*status)) return NULL;

  libEntry = searchForLibraryName(libName);
  if(libEntry == -1) {
    libEntry = libraryCount++;
    if(libraryCount >= libraryMax) {
      /* Ran out of library slots. Statically allocated because we can't depend on allocating memory.. */
      *status = U_MEMORY_ALLOCATION_ERROR;
#if UPLUG_TRACE
      DBG((stderr, "uplug_openLibrary() - out of library slots (max %d)\n", libraryMax));
#endif
      return NULL;
    }
    /* Some operating systems don't want
       DL operations from multiple threads. */
    libraryList[libEntry].lib = uprv_dl_open(libName, status);
#if UPLUG_TRACE
    DBG((stderr, "uplug_openLibrary(%s,%s) libEntry %d, lib %p\n", libName, u_errorName(*status), libEntry, lib));
#endif

    if(libraryList[libEntry].lib == NULL || U_FAILURE(*status)) {
      /* cleanup. */
      libraryList[libEntry].lib = NULL; /* failure with open */
      libraryList[libEntry].name[0] = 0;
#if UPLUG_TRACE
      DBG((stderr, "uplug_openLibrary(%s,%s) libEntry %d, lib %p\n", libName, u_errorName(*status), libEntry, lib));
#endif
      /* no need to free - just won't increase the count. */
      libraryCount--;
    } else { /* is it still there? */
      /* link it in */
      uprv_strncpy(libraryList[libEntry].name,libName,UPLUG_NAME_MAX);
      libraryList[libEntry].ref=1;
      lib = libraryList[libEntry].lib;
    }

  } else {
    lib = libraryList[libEntry].lib;
    libraryList[libEntry].ref++;
  }
  return lib;
}

U_INTERNAL void U_EXPORT2
uplug_closeLibrary(void *lib, UErrorCode *status) {
  int32_t i;

#if UPLUG_TRACE
  DBG((stderr, "uplug_closeLibrary(%p,%s) list %p\n", lib, u_errorName(*status), (void*)libraryList));
#endif
  if(U_FAILURE(*status)) return;

  for(i=0;i<libraryCount;i++) {
    if(lib==libraryList[i].lib) {
      if(--(libraryList[i].ref) == 0) {
        uprv_dl_close(libraryList[i].lib, status);
        libraryCount = uplug_removeEntryAt(libraryList, libraryCount, sizeof(*libraryList), i);
      }
      return;
    }
  }
  *status = U_INTERNAL_PROGRAM_ERROR; /* could not find the entry! */
}

#endif

static UPlugData pluginList[UPLUG_PLUGIN_INITIAL_COUNT];
static int32_t pluginCount = 0;

static int32_t uplug_pluginNumber(UPlugData* d) {
  UPlugData *pastPlug = &pluginList[pluginCount];
  if(d<=pluginList) {
    return 0;
  } else if(d>=pastPlug) {
    return pluginCount;
  } else {
    return (d-pluginList)/sizeof(pluginList[0]);
  }
}

U_CAPI UPlugData * U_EXPORT2
uplug_nextPlug(UPlugData *prior) {
  if(prior==NULL) {
    return pluginList;
  } else {
    UPlugData *nextPlug = &prior[1];
    UPlugData *pastPlug = &pluginList[pluginCount];

    if(nextPlug>=pastPlug) {
      return NULL;
    } else {
      return nextPlug;
    }
  }
}

/**
 * Call the plugin with some params
 */
static void uplug_callPlug(UPlugData *plug, UPlugReason reason, UErrorCode *status) {
  UPlugTokenReturn token;
  if(plug==NULL||U_FAILURE(*status)) {
    return;
  }
  token = (*(plug->entrypoint))(plug, reason, status);
  if(token!=UPLUG_TOKEN) {
    *status = U_INTERNAL_PROGRAM_ERROR;
  }
}

static void uplug_unloadPlug(UPlugData *plug, UErrorCode *status) {
  if(plug->awaitingLoad) {  /* shouldn't happen. Plugin hasn'tbeen loaded yet.*/
    *status = U_INTERNAL_PROGRAM_ERROR;
    return;
  }
  if(U_SUCCESS(plug->pluginStatus)) {
    /* Don't unload a plug which has a failing load status - means it didn't actually load. */
    uplug_callPlug(plug, UPLUG_REASON_UNLOAD, status);
  }
}

static void uplug_queryPlug(UPlugData *plug, UErrorCode *status) {
  if(!plug->awaitingLoad || !(plug->level == UPLUG_LEVEL_UNKNOWN) ) {  /* shouldn't happen. Plugin hasn'tbeen loaded yet.*/
    *status = U_INTERNAL_PROGRAM_ERROR;
    return;
  }
  plug->level = UPLUG_LEVEL_INVALID;
  uplug_callPlug(plug, UPLUG_REASON_QUERY, status);
  if(U_SUCCESS(*status)) {
    if(plug->level == UPLUG_LEVEL_INVALID) {
      plug->pluginStatus = U_PLUGIN_DIDNT_SET_LEVEL;
      plug->awaitingLoad = FALSE;
    }
  } else {
    plug->pluginStatus = U_INTERNAL_PROGRAM_ERROR;
    plug->awaitingLoad = FALSE;
  }
}

static void uplug_loadPlug(UPlugData *plug, UErrorCode *status) {
  if(U_FAILURE(*status)) {
    return;
  }
  if(!plug->awaitingLoad || (plug->level < UPLUG_LEVEL_LOW) ) {  /* shouldn't happen. Plugin hasn'tbeen loaded yet.*/
    *status = U_INTERNAL_PROGRAM_ERROR;
    return;
  }
  uplug_callPlug(plug, UPLUG_REASON_LOAD, status);
  plug->awaitingLoad = FALSE;
  if(!U_SUCCESS(*status)) {
    plug->pluginStatus = U_INTERNAL_PROGRAM_ERROR;
  }
}

static UPlugData *uplug_allocateEmptyPlug(UErrorCode *status)
{
  UPlugData *plug = NULL;

  if(U_FAILURE(*status)) {
    return NULL;
  }

  if(pluginCount == UPLUG_PLUGIN_INITIAL_COUNT) {
    *status = U_MEMORY_ALLOCATION_ERROR;
    return NULL;
  }

  plug = &pluginList[pluginCount++];

  plug->token = UPLUG_TOKEN;
  plug->structSize = sizeof(UPlugData);
  plug->name[0]=0;
  plug->level = UPLUG_LEVEL_UNKNOWN; /* initialize to null state */
  plug->awaitingLoad = TRUE;
  plug->dontUnload = FALSE;
  plug->pluginStatus = U_ZERO_ERROR;
  plug->libName[0] = 0;
  plug->config[0]=0;
  plug->sym[0]=0;
  plug->lib=NULL;
  plug->entrypoint=NULL;

  return plug;
}

static UPlugData *uplug_allocatePlug(UPlugEntrypoint *entrypoint, const char *config, void *lib, const char *symName,
                                     UErrorCode *status) {
  UPlugData *plug = uplug_allocateEmptyPlug(status);
  if(U_FAILURE(*status)) {
    return NULL;
  }

  if(config!=NULL) {
    uprv_strncpy(plug->config, config, UPLUG_NAME_MAX);
  } else {
    plug->config[0] = 0;
  }

  if(symName!=NULL) {
    uprv_strncpy(plug->sym, symName, UPLUG_NAME_MAX);
  } else {
    plug->sym[0] = 0;
  }

  plug->entrypoint = entrypoint;
  plug->lib = lib;
  uplug_queryPlug(plug, status);

  return plug;
}

static void uplug_deallocatePlug(UPlugData *plug, UErrorCode *status) {
  UErrorCode subStatus = U_ZERO_ERROR;
  if(!plug->dontUnload) {
#if U_ENABLE_DYLOAD
    uplug_closeLibrary(plug->lib, &subStatus);
#endif
  }
  plug->lib = NULL;
  if(U_SUCCESS(*status) && U_FAILURE(subStatus)) {
    *status = subStatus;
  }
  /* shift plugins up and decrement count. */
  if(U_SUCCESS(*status)) {
    /* all ok- remove. */
    pluginCount = uplug_removeEntryAt(pluginList, pluginCount, sizeof(plug[0]), uplug_pluginNumber(plug));
  } else {
    /* not ok- leave as a message. */
    plug->awaitingLoad=FALSE;
    plug->entrypoint=0;
    plug->dontUnload=TRUE;
  }
}

static void uplug_doUnloadPlug(UPlugData *plugToRemove, UErrorCode *status) {
  if(plugToRemove != NULL) {
    uplug_unloadPlug(plugToRemove, status);
    uplug_deallocatePlug(plugToRemove, status);
  }
}

U_CAPI void U_EXPORT2
uplug_removePlug(UPlugData *plug, UErrorCode *status)  {
  UPlugData *cursor = NULL;
  UPlugData *plugToRemove = NULL;
  if(U_FAILURE(*status)) return;

  for(cursor=pluginList;cursor!=NULL;) {
    if(cursor==plug) {
      plugToRemove = plug;
      cursor=NULL;
    } else {
      cursor = uplug_nextPlug(cursor);
    }
  }

  uplug_doUnloadPlug(plugToRemove, status);
}

U_CAPI void U_EXPORT2
uplug_setPlugNoUnload(UPlugData *data, UBool dontUnload)
{
  data->dontUnload = dontUnload;
}

U_CAPI void U_EXPORT2
uplug_setPlugLevel(UPlugData *data, UPlugLevel level) {
  data->level = level;
}

U_CAPI UPlugLevel U_EXPORT2
uplug_getPlugLevel(UPlugData *data) {
  return data->level;
}

U_CAPI void U_EXPORT2
uplug_setPlugName(UPlugData *data, const char *name) {
  uprv_strncpy(data->name, name, UPLUG_NAME_MAX);
}

U_CAPI const char * U_EXPORT2
uplug_getPlugName(UPlugData *data) {
  return data->name;
}

U_CAPI const char * U_EXPORT2
uplug_getSymbolName(UPlugData *data) {
  return data->sym;
}

U_CAPI const char * U_EXPORT2
uplug_getLibraryName(UPlugData *data, UErrorCode *status) {
  if(data->libName[0]) {
    return data->libName;
  } else {
#if U_ENABLE_DYLOAD
    return uplug_findLibrary(data->lib, status);
#else
    return NULL;
#endif
  }
}

U_CAPI void * U_EXPORT2
uplug_getLibrary(UPlugData *data) {
  return data->lib;
}

U_CAPI void * U_EXPORT2
uplug_getContext(UPlugData *data) {
  return data->context;
}

U_CAPI void U_EXPORT2
uplug_setContext(UPlugData *data, void *context) {
  data->context = context;
}

U_CAPI const char* U_EXPORT2
uplug_getConfiguration(UPlugData *data) {
  return data->config;
}

U_INTERNAL UPlugData* U_EXPORT2
uplug_getPlugInternal(int32_t n) {
  if(n <0 || n >= pluginCount) {
    return NULL;
  } else {
    return &(pluginList[n]);
  }
}

U_CAPI UErrorCode U_EXPORT2
uplug_getPlugLoadStatus(UPlugData *plug) {
  return plug->pluginStatus;
}

/**
 * Initialize a plugin fron an entrypoint and library - but don't load it.
 */
static UPlugData* uplug_initPlugFromEntrypointAndLibrary(UPlugEntrypoint *entrypoint, const char *config, void *lib, const char *sym,
                                                         UErrorCode *status) {
  UPlugData *plug = NULL;

  plug = uplug_allocatePlug(entrypoint, config, lib, sym, status);

  if(U_SUCCESS(*status)) {
    return plug;
  } else {
    uplug_deallocatePlug(plug, status);
    return NULL;
  }
}

U_CAPI UPlugData* U_EXPORT2
uplug_loadPlugFromEntrypoint(UPlugEntrypoint *entrypoint, const char *config, UErrorCode *status) {
  UPlugData* plug = uplug_initPlugFromEntrypointAndLibrary(entrypoint, config, NULL, NULL, status);
  uplug_loadPlug(plug, status);
  return plug;
}

#if U_ENABLE_DYLOAD

static UPlugData*
uplug_initErrorPlug(const char *libName, const char *sym, const char *config, const char *nameOrError, UErrorCode loadStatus, UErrorCode *status)
{
  UPlugData *plug = uplug_allocateEmptyPlug(status);
  if(U_FAILURE(*status)) return NULL;

  plug->pluginStatus = loadStatus;
  plug->awaitingLoad = FALSE; /* Won't load. */
  plug->dontUnload = TRUE; /* cannot unload. */

  if(sym!=NULL) {
    uprv_strncpy(plug->sym, sym, UPLUG_NAME_MAX);
  }

  if(libName!=NULL) {
    uprv_strncpy(plug->libName, libName, UPLUG_NAME_MAX);
  }

  if(nameOrError!=NULL) {
    uprv_strncpy(plug->name, nameOrError, UPLUG_NAME_MAX);
  }

  if(config!=NULL) {
    uprv_strncpy(plug->config, config, UPLUG_NAME_MAX);
  }

  return plug;
}

/**
 * Fetch a plugin from DLL, and then initialize it from a library- but don't load it.
 */
static UPlugData*
uplug_initPlugFromLibrary(const char *libName, const char *sym, const char *config, UErrorCode *status) {
  void *lib = NULL;
  UPlugData *plug = NULL;
  if(U_FAILURE(*status)) { return NULL; }
  lib = uplug_openLibrary(libName, status);
  if(lib!=NULL && U_SUCCESS(*status)) {
    UPlugEntrypoint *entrypoint = NULL;
    entrypoint = (UPlugEntrypoint*)uprv_dlsym_func(lib, sym, status);

    if(entrypoint!=NULL&&U_SUCCESS(*status)) {
      plug = uplug_initPlugFromEntrypointAndLibrary(entrypoint, config, lib, sym, status);
      if(plug!=NULL&&U_SUCCESS(*status)) {
        plug->lib = lib; /* plug takes ownership of library */
        lib = NULL; /* library is now owned by plugin. */
      }
    } else {
      UErrorCode subStatus = U_ZERO_ERROR;
      plug = uplug_initErrorPlug(libName,sym,config,"ERROR: Could not load entrypoint",(lib==NULL)?U_MISSING_RESOURCE_ERROR:*status,&subStatus);
    }
    if(lib!=NULL) { /* still need to close the lib */
      UErrorCode subStatus = U_ZERO_ERROR;
      uplug_closeLibrary(lib, &subStatus); /* don't care here */
    }
  } else {
    UErrorCode subStatus = U_ZERO_ERROR;
    plug = uplug_initErrorPlug(libName,sym,config,"ERROR: could not load library",(lib==NULL)?U_MISSING_RESOURCE_ERROR:*status,&subStatus);
  }
  return plug;
}

U_CAPI UPlugData* U_EXPORT2
uplug_loadPlugFromLibrary(const char *libName, const char *sym, const char *config, UErrorCode *status) {
  UPlugData *plug = NULL;
  if(U_FAILURE(*status)) { return NULL; }
  plug = uplug_initPlugFromLibrary(libName, sym, config, status);
  uplug_loadPlug(plug, status);

  return plug;
}

#endif

static UPlugLevel gCurrentLevel = UPLUG_LEVEL_LOW;

U_CAPI UPlugLevel U_EXPORT2 uplug_getCurrentLevel() {
  return gCurrentLevel;
}

static UBool U_CALLCONV uplug_cleanup(void)
{
  int32_t i;

  UPlugData *pluginToRemove;
  /* cleanup plugs */
  for(i=0;i<pluginCount;i++) {
    UErrorCode subStatus = U_ZERO_ERROR;
    pluginToRemove = &pluginList[i];
    /* unload and deallocate */
    uplug_doUnloadPlug(pluginToRemove, &subStatus);
  }
  /* close other held libs? */
  gCurrentLevel = UPLUG_LEVEL_LOW;
  return TRUE;
}

#if U_ENABLE_DYLOAD

static void uplug_loadWaitingPlugs(UErrorCode *status) {
  int32_t i;
  UPlugLevel currentLevel = uplug_getCurrentLevel();

  if(U_FAILURE(*status)) {
    return;
  }
#if UPLUG_TRACE
  DBG((stderr,  "uplug_loadWaitingPlugs() Level: %d\n", currentLevel));
#endif
  /* pass #1: low level plugs */
  for(i=0;i<pluginCount;i++) {
    UErrorCode subStatus = U_ZERO_ERROR;
    UPlugData *pluginToLoad = &pluginList[i];
    if(pluginToLoad->awaitingLoad) {
      if(pluginToLoad->level == UPLUG_LEVEL_LOW) {
        if(currentLevel > UPLUG_LEVEL_LOW) {
          pluginToLoad->pluginStatus = U_PLUGIN_TOO_HIGH;
        } else {
          UPlugLevel newLevel;
          uplug_loadPlug(pluginToLoad, &subStatus);
          newLevel = uplug_getCurrentLevel();
          if(newLevel > currentLevel) {
            pluginToLoad->pluginStatus = U_PLUGIN_CHANGED_LEVEL_WARNING;
            currentLevel = newLevel;
          }
        }
        pluginToLoad->awaitingLoad = FALSE;
      }
    }
  }
  for(i=0;i<pluginCount;i++) {
    UErrorCode subStatus = U_ZERO_ERROR;
    UPlugData *pluginToLoad = &pluginList[i];

    if(pluginToLoad->awaitingLoad) {
      if(pluginToLoad->level == UPLUG_LEVEL_INVALID) {
        pluginToLoad->pluginStatus = U_PLUGIN_DIDNT_SET_LEVEL;
      } else if(pluginToLoad->level == UPLUG_LEVEL_UNKNOWN) {
        pluginToLoad->pluginStatus = U_INTERNAL_PROGRAM_ERROR;
      } else {
        uplug_loadPlug(pluginToLoad, &subStatus);
      }
      pluginToLoad->awaitingLoad = FALSE;
    }
  }

#if UPLUG_TRACE
  DBG((stderr,  " Done Loading Plugs. Level: %d\n", (int32_t)uplug_getCurrentLevel()));
#endif
}

/* Name of the plugin config file */
static char plugin_file[2048] = "";
#endif

U_INTERNAL const char* U_EXPORT2
uplug_getPluginFile() {
#if U_ENABLE_DYLOAD && !UCONFIG_NO_FILE_IO
  return plugin_file;
#else
  return NULL;
#endif
}

//  uplug_init()  is called first thing from u_init().

U_CAPI void U_EXPORT2
uplug_init(UErrorCode *status) {
#if !U_ENABLE_DYLOAD
  (void)status; /* unused */
#elif !UCONFIG_NO_FILE_IO
  CharString plugin_dir;
  const char *env = getenv("ICU_PLUGINS");

  if(U_FAILURE(*status)) return;
  if(env != NULL) {
    plugin_dir.append(env, -1, *status);
  }
  if(U_FAILURE(*status)) return;

#if defined(DEFAULT_ICU_PLUGINS)
  if(plugin_dir.isEmpty()) {
    plugin_dir.append(DEFAULT_ICU_PLUGINS, -1, *status);
  }
#endif

#if UPLUG_TRACE
  DBG((stderr, "ICU_PLUGINS=%s\n", plugin_dir.data()));
#endif

  if(!plugin_dir.isEmpty()) {
    FILE *f;

    CharString pluginFile;
#ifdef OS390BATCH
/* There are potentially a lot of ways to implement a plugin directory on OS390/zOS  */
/* Keeping in mind that unauthorized file access is logged, monitored, and enforced  */
/* I've chosen to open a DDNAME if BATCH and leave it alone for (presumably) UNIX    */
/* System Services.  Alternative techniques might be allocating a member in          */
/* SYS1.PARMLIB or setting an environment variable "ICU_PLUGIN_PATH" (?).  The       */
/* DDNAME can be connected to a file in the HFS if need be.                          */

    pluginFile.append("//DD:ICUPLUG", -1, *status);        /* JAM 20 Oct 2011 */
#else
    pluginFile.append(plugin_dir, *status);
    pluginFile.append(U_FILE_SEP_STRING, -1, *status);
    pluginFile.append("icuplugins", -1, *status);
    pluginFile.append(U_ICU_VERSION_SHORT, -1, *status);
    pluginFile.append(".txt", -1, *status);
#endif

#if UPLUG_TRACE
    DBG((stderr, "status=%s\n", u_errorName(*status)));
#endif

    if(U_FAILURE(*status)) {
      return;
    }
    if((size_t)pluginFile.length() > (sizeof(plugin_file)-1)) {
      *status = U_BUFFER_OVERFLOW_ERROR;
#if UPLUG_TRACE
      DBG((stderr, "status=%s\n", u_errorName(*status)));
#endif
      return;
    }

    /* plugin_file is not used for processing - it is only used
       so that uplug_getPluginFile() works (i.e. icuinfo)
    */
    uprv_strncpy(plugin_file, pluginFile.data(), sizeof(plugin_file));

#if UPLUG_TRACE
    DBG((stderr, "pluginfile= %s len %d/%d\n", plugin_file, (int)strlen(plugin_file), (int)sizeof(plugin_file)));
#endif

#ifdef __MVS__
    if (iscics()) /* 12 Nov 2011 JAM */
    {
        f = NULL;
    }
    else
#endif
    {
        f = fopen(pluginFile.data(), "r");
    }

    if(f != NULL) {
      char linebuf[1024];
      char *p, *libName=NULL, *symName=NULL, *config=NULL;
      int32_t line = 0;

      while(fgets(linebuf,1023,f)) {
        line++;

        if(!*linebuf || *linebuf=='#') {
          continue;
        } else {
          p = linebuf;
          while(*p&&isspace((int)*p))
            p++;
          if(!*p || *p=='#') continue;
          libName = p;
          while(*p&&!isspace((int)*p)) {
            p++;
          }
          if(!*p || *p=='#') continue; /* no tab after libname */
          *p=0; /* end of libname */
          p++;
          while(*p&&isspace((int)*p)) {
            p++;
          }
          if(!*p||*p=='#') continue; /* no symname after libname +tab */
          symName = p;
          while(*p&&!isspace((int)*p)) {
            p++;
          }

          if(*p) { /* has config */
            *p=0;
            ++p;
            while(*p&&isspace((int)*p)) {
              p++;
            }
            if(*p) {
              config = p;
            }
          }

          /* chop whitespace at the end of the config */
          if(config!=NULL&&*config!=0) {
            p = config+strlen(config);
            while(p>config&&isspace((int)*(--p))) {
              *p=0;
            }
          }

          /* OK, we're good. */
          {
            UErrorCode subStatus = U_ZERO_ERROR;
            UPlugData *plug = uplug_initPlugFromLibrary(libName, symName, config, &subStatus);
            if(U_FAILURE(subStatus) && U_SUCCESS(*status)) {
              *status = subStatus;
            }
#if UPLUG_TRACE
            DBG((stderr, "PLUGIN libName=[%s], sym=[%s], config=[%s]\n", libName, symName, config));
            DBG((stderr, " -> %p, %s\n", (void*)plug, u_errorName(subStatus)));
#else
            (void)plug; /* unused */
#endif
          }
        }
      }
      fclose(f);
    } else {
#if UPLUG_TRACE
      DBG((stderr, "Can't open plugin file %s\n", plugin_file));
#endif
    }
  }
  uplug_loadWaitingPlugs(status);
#endif /* U_ENABLE_DYLOAD */
  gCurrentLevel = UPLUG_LEVEL_HIGH;
  ucln_registerCleanup(UCLN_UPLUG, uplug_cleanup);
}

#endif