blob: d1751be547004b44cf902f7b9dd8386be9b813f6 [file] [log] [blame]
/*
*******************************************************************************
*
* Copyright (C) 2003, International Business Machines
* Corporation and others. All Rights Reserved.
*
*******************************************************************************
*
* File colprobe.cpp
*
* Modification History:
*
* Date Name Description
* 03/18/2003 weiv Creation.
*******************************************************************************
*/
#include "uoptions.h"
#include "unicode/ucol.h"
#include "unicode/ucoleitr.h"
#include "unicode/ures.h"
#include "unicode/uniset.h"
#include "unicode/usetiter.h"
#include "unicode/ustring.h"
#include "unicode/uchar.h"
#include "unicode/uscript.h"
#include "uprops.h"
#include "hash.h"
#include "ucol_imp.h"
#include "unicode/ustdio.h"
#include "unicode/utrans.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <io.h>
#include <fcntl.h>
#include "colprobe.h"
#ifdef WIN32
#include <windows.h>
#else
//
// Stubs for Windows API functions when building on UNIXes.
//
typedef int DWORD;
inline int CompareStringW(DWORD, DWORD, UChar *, int, UChar *, int) {return 0;};
#include <sys/time.h>
unsigned long timeGetTime() {
struct timeval t;
gettimeofday(&t, 0);
unsigned long val = t.tv_sec * 1000; // Let it overflow. Who cares.
val += t.tv_usec / 1000;
return val;
};
inline int LCMapStringW(DWORD, DWORD, UChar *, int, UChar *, int) {return 0;};
const int LCMAP_SORTKEY = 0;
#define MAKELCID(a,b) 0
const int SORT_DEFAULT = 0;
#endif
#include "line.h"
static UBool gVerbose = FALSE;
static UBool gDebug = FALSE;
static UBool gQuiet = FALSE;
static UBool gExemplar = FALSE;
DWORD gWinLCID;
int gCount;
Line **gICULines;
UCollator *gCol;
UCollator *gUCA;
Line source;
Line target;
Line *gSource = &source;
Line *gTarget = &target;
Hashtable gElements(FALSE);
Hashtable gExpansions(FALSE);
CompareFn gComparer;
const UChar separatorChar = 0x0030;
UFILE *out = NULL;
UFILE *err = NULL;
UFILE *log = NULL;
const char *progName = "colprobe";
const char *gLocale = NULL;
//char platform[256];
int32_t platformIndex = -1;
int32_t gPlatformNo = 0;
int32_t gPlatformIndexes[10];
int32_t gLocaleNo = 0;
const char* gLocales[100];
UBool gRulesStdin = FALSE;
enum {
HELP1,
HELP2,
VERBOSE,
QUIET,
VERSION,
ICUDATADIR,
COPYRIGHT,
LOCALE,
PLATFORM,
DEBUG,
EXEMPLAR,
RULESSTDIN
};
UOption options[]={
/*0*/ UOPTION_HELP_H,
/*1*/ UOPTION_HELP_QUESTION_MARK,
/*2*/ UOPTION_VERBOSE,
/*3*/ UOPTION_QUIET,
/*4*/ UOPTION_VERSION,
/*5*/ UOPTION_ICUDATADIR,
/*6*/ UOPTION_COPYRIGHT,
/*7*/ UOPTION_DEF("locale", 'l', UOPT_REQUIRES_ARG),
/*8*/ UOPTION_DEF("platform", 'p', UOPT_REQUIRES_ARG),
/*9*/ UOPTION_DEF("debug", 'D', UOPT_NO_ARG),
/*10*/ UOPTION_DEF("exemplar", 'E', UOPT_NO_ARG),
/*11*/ UOPTION_DEF("rulesstdin", 'R', UOPT_NO_ARG)
};
int Winstrcmp(const void *a, const void *b) {
gCount++;
int t;
t = CompareStringW(gWinLCID, 0,
(*(Line **)a)->name, (*(Line **)a)->len,
(*(Line **)b)->name, (*(Line **)b)->len);
return t-2;
}
int ICUstrcmp(const void *a, const void *b) {
gCount++;
UCollationResult t;
t = ucol_strcoll(gCol,
(*(Line **)a)->name, (*(Line **)a)->len,
(*(Line **)b)->name, (*(Line **)b)->len);
if (t == UCOL_LESS) return -1;
if (t == UCOL_GREATER) return +1;
return 0;
}
struct {
const char* name;
CompareFn comparer;
} platforms[] = {
{ "icu", ICUstrcmp },
{ "win", Winstrcmp}
};
void deleteLineElement(void *line) {
delete((Line *)line);
}
void stringToLower(char *string) {
uint32_t i = 0;
for(i = 0; i < strlen(string); i++) {
string[i] = tolower(string[i]);
}
}
void usage(const char *name) {
u_fprintf(out, "Usage: %s --locale loc_name --platform platform\n", name);
}
void listKnownPlatforms() {
int32_t i = 0;
u_fprintf(err, "Known platforms:\n");
for(i = 0; i < sizeof(platforms)/sizeof(platforms[0]); i++) {
u_fprintf(err, "\t%s\n", platforms[i]);
}
}
void addPlatform(const char *platform) {
int32_t i;
//stringToLower(platform);
int32_t oldPlatformNo = gPlatformNo;
for(i = 0; i < sizeof(platforms)/sizeof(platforms[0]); i++) {
if(strcmp(platform, platforms[i].name) == 0) {
gPlatformIndexes[gPlatformNo++] = i;
}
}
if(gPlatformNo == oldPlatformNo) {
u_fprintf(err, "Unknown platform %s\n", platform);
listKnownPlatforms();
}
}
void processArgs(int argc, char* argv[], UErrorCode &status)
{
int32_t i = 0;
U_MAIN_INIT_ARGS(argc, argv);
argc = u_parseArgs(argc, argv, (int32_t)(sizeof(options)/sizeof(options[0])), options);
if(argc < 0) {
u_fprintf(err, "Unknown option: %s\n", argv[-argc]);
usage(progName);
return;
}
if(options[0].doesOccur || options[1].doesOccur) {
usage(progName);
return;
}
if(options[VERBOSE].doesOccur) {
gVerbose = TRUE;
}
if(options[DEBUG].doesOccur) {
gDebug = TRUE;
gVerbose = TRUE;
}
if(options[EXEMPLAR].doesOccur) {
gExemplar = TRUE;
}
if(options[QUIET].doesOccur) {
gQuiet = TRUE;
}
/*
for(i = 8; i < 9; i++) {
if(!options[i].doesOccur) {
u_fprintf(err, "Option %s is required!\n", options[i].longName);
usage(progName);
status = U_ILLEGAL_ARGUMENT_ERROR;
}
if(options[i].value == NULL) {
u_fprintf(err, "Option %s needs an argument!\n", options[i].longName);
usage(progName);
status = U_ILLEGAL_ARGUMENT_ERROR;
}
}
*/
// ASCII based options specified on the command line
// this is for testing purposes, will allow to load
// up ICU rules and then poke through them.
// In that case, we test only ICU and don't need
// a locale.
if(options[RULESSTDIN].doesOccur) {
gRulesStdin = TRUE;
addPlatform("icu");
return;
}
if(options[LOCALE].doesOccur) {
gLocale = options[LOCALE].value;
} else {
for(i = 1; i < argc; i++) {
gLocales[gLocaleNo++] = argv[i];
}
}
if(options[PLATFORM].doesOccur) {
//strcpy(platform, options[PLATFORM].value);
//addPlatform("icu");
addPlatform(options[PLATFORM].value);
} else { // there is a list of platforms
u_fprintf(err, "Option %s is required!\n", options[i].longName);
usage(progName);
status = U_ILLEGAL_ARGUMENT_ERROR;
}
//
// Set up a Windows LCID
//
gWinLCID = uloc_getLCID(gLocale);
/*
if (gLocale != 0) {
gWinLCID = MAKELCID(gLocale, SORT_DEFAULT);
}
else {
gWinLCID = uloc_getLCID(gLocale);
}
*/
}
void printRules(const UChar *name, int32_t len, UFILE *file) {
// very rudimentary pretty rules print
int32_t i = 0;
UChar toPrint[16384];
int32_t toPrintIndex = 0;
for(i = 0; i < len; i++) {
if(name[i] == 0x0026) {
if(toPrintIndex) {
toPrint[toPrintIndex] = 0;
u_fprintf(file, "%U\n", toPrint);
toPrintIndex = 0;
toPrint[toPrintIndex++] = name[i];
} else {
toPrint[toPrintIndex++] = name[i];
}
} else {
toPrint[toPrintIndex++] = name[i];
}
}
if(toPrintIndex) {
toPrint[toPrintIndex] = 0;
u_fprintf(file, "%U\n", toPrint);
toPrintIndex = 0;
}
}
void escapeString(const UChar *name, int32_t len, UFILE *file) {
u_fprintf(file, "%U", name);
/*
int32_t j = 0;
for(j = 0; j < len; j++) {
if(name[j] >= 0x20 && name[j] < 0x80) {
u_fprintf(file, "%c", name[j]);
} else {
u_fprintf(file, "\\u%04X", name[j]);
}
}
*/
}
void escapeALine(Line *line, UFILE *file) {
escapeString(line->name, line->len, file);
}
void escapeExpansion(Line *line, UFILE *file) {
escapeString(line->expansionString, line->expLen, file);
}
void showNames(Line *line, UFILE *file) {
UErrorCode status = U_ZERO_ERROR;
int32_t j = 0;
char charName[256];
for(j = 0; j < line->len; j++) {
u_charName(line->name[j], U_EXTENDED_CHAR_NAME, charName, 256, &status);
u_fprintf(file, "%s ", charName);
}
}
void setArray(Line **array, Line *contents, int32_t size) {
int32_t i = 0;
for(i = 0; i < size; i++) {
array[i] = contents+i;
}
}
// set an array from a Hashtable
int32_t
setArray(Line **array, Hashtable *table = &gElements) {
int32_t size = table->count();
int32_t hashIndex = -1;
const UHashElement *hashElement = NULL;
int32_t count = 0;
while((hashElement = table->nextElement(hashIndex)) != NULL) {
array[count++] = (Line *)hashElement->value.pointer;
}
return size;
}
UBool trySwamped(Line **smaller, Line **greater, UChar chars[2], CompareFn comparer) {
u_strcpy(gSource->name, (*smaller)->name);
gSource->name[(*smaller)->len] = separatorChar;
gSource->name[(*smaller)->len+1] = chars[0];
gSource->name[(*smaller)->len+2] = 0;
gSource->len = (*smaller)->len+2;
u_strcpy(gTarget->name, (*greater)->name);
gTarget->name[(*greater)->len] = separatorChar;
gTarget->name[(*greater)->len+1] = chars[1];
gTarget->name[(*greater)->len+2] = 0;
gTarget->len = (*greater)->len+2;
if(comparer(&gSource, &gTarget) > 0) {
return TRUE;
} else {
return FALSE;
}
}
UBool trySwamps(Line **smaller, Line **greater, UChar chars[2], CompareFn comparer) {
gSource->name[0] = chars[0];
gSource->name[1] = separatorChar;
u_strcpy(gSource->name+2, (*smaller)->name);
gSource->len = (*smaller)->len+2;
gTarget->name[0] = chars[1];
gTarget->name[1] = separatorChar;
u_strcpy(gTarget->name+2, (*greater)->name);
gTarget->len = (*greater)->len+2;
if(comparer(&gSource, &gTarget) < 0) {
return TRUE;
} else {
return FALSE;
}
}
UColAttributeValue
probeStrength(Line** prevLine, Line **currLine, CompareFn comparer) {
// Primary swamps secondary
// have pairs where [0] 2> [1]
UChar primSwamps[][2] = {
{ 0x00E0, 0x0061 },
{ 0x0450, 0x0435 },
{ 0x31a3, 0x310d }
};
// Secondary swamps tertiary
// have pairs where [0] 3> [1]
UChar secSwamps[][2] = {
{ 0x0053, 0x0073 },
{ 0x0415, 0x0435 },
{ 0x31b6, 0x310e }
};
// Secondary is swamped by primary
// have pairs where [0] 1> [1]
UChar secSwamped[][2] = {
{ 0x0062, 0x0061 },
{ 0x0436, 0x0454 },
{ 0x310e, 0x310d }
};
// Tertiary is swamped by secondary
// have pairs where [0] 2> [1]
UChar terSwamped[][2] = {
{ 0x00E0, 0x0061 },
{ 0x0450, 0x0435 },
{ 0x31a3, 0x310d }
};
int32_t i = 0;
// Tertiary swamps equal?
int result = 0;
// Choose the pair
i = 0;
/*
if((*prevLine)->name[0] > 0xFF && (*currLine)->name[0] > 0xFF) {
i = 0;
} else if((*prevLine)->name[0] < 0x0400 && (*currLine)->name[0] < 0x0400) {
i = 1;
} else {
i = 2;
}
*/
// are they equal?
if((result = comparer(prevLine, currLine)) == 0) {
return UCOL_IDENTICAL;
} else if(result > 0) {
//fprintf(stderr, "lines should be ordered!");
return UCOL_OFF;
} else if(trySwamps(prevLine, currLine, primSwamps[i], comparer)) {
return UCOL_PRIMARY;
} else if(trySwamps(prevLine, currLine, secSwamps[i], comparer)) {
return UCOL_SECONDARY;
} else if(trySwamped(prevLine, currLine, terSwamped[i], comparer)) {
// is there a tertiary difference
return UCOL_TERTIARY;
} else {
//fprintf(stderr, "Unknown strength!\n");
return UCOL_ON;
}
}
// This function tries to probe the set of lines
// (already sorted by qsort) and deduct the strengths
void
analyzeStrength(Line **lines, int32_t size, CompareFn comparer) {
int32_t i = 0;
for(i = 1; i < size; i++) {
Line **prevLine = lines+i-1;
Line **currLine = lines+i;
(*currLine)->strength = probeStrength(prevLine, currLine, comparer);
(*currLine)->sortedIndex = i;
(*currLine)->previous = *prevLine;
(*prevLine)->next = *currLine;
}
}
void printStrength(UColAttributeValue strength, UFILE *file) {
u_fprintf(file, " ");
switch(strength) {
case UCOL_IDENTICAL:
u_fprintf(file, "=");
break;
case UCOL_TERTIARY:
//u_fprintf(file, "<3");
u_fprintf(file, "<<<");
break;
case UCOL_SECONDARY:
//u_fprintf(file, "<2");
u_fprintf(file, "<<");
break;
case UCOL_PRIMARY:
//u_fprintf(file, "<1");
u_fprintf(file, "<");
break;
case UCOL_OFF:
u_fprintf(file, ">?");
default:
u_fprintf(file, "?!");
break;
}
u_fprintf(file, " ");
}
void printStrength(Line *line, UFILE *file) {
printStrength(line->strength, file);
}
void printLine(Line *line, UFILE *file) {
escapeALine(line, file);
if(line->isExpansion) {
u_fprintf(file, "/");
escapeExpansion(line, file);
}
}
void printOrdering(Line **lines, int32_t size, UFILE *file, UBool useLinks = FALSE) {
int32_t i = 0;
//printLine(*lines);
//escapeALine(*lines); // Print first line
Line *line = NULL;
Line *previous = *lines;
if(previous->isReset) {
u_fprintf(file, "\n& ");
escapeALine(previous, file);
} else if(!previous->isRemoved) {
printLine(previous, file);
}
i = 1;
while(i < size && previous->next) {
if(useLinks) {
line = previous->next;
} else {
line = *(lines+i);
}
if(line->isReset) {
u_fprintf(file, "\n& ");
escapeALine(line, file);
} else if(!line->isRemoved) {
if(file == out) {
u_fprintf(file, "\n");
}
if(i > 0) {
printStrength(line, file);
}
printLine(line, file);
//escapeALine(line, file);
}
previous = line;
i++;
}
u_fprintf(file, "\n");
}
void setIndexes(Line **lines, int32_t size) {
int32_t i = 0;
(*lines)->sortedIndex = 0;
for(i = 1; i < size; i++) {
Line *line = *(lines+i);
Line *prev = *(lines+i-1);
line->previous = prev;
prev->next = line;
line->sortedIndex = i;
}
}
// this seems to be a dead end
void
noteExpansion(Line **gLines, Line *line, int32_t size, CompareFn comparer) {
UErrorCode status = U_ZERO_ERROR;
UnicodeString key(line->name, line->len);
//Line *toInsert = (Line *)gElements.get(key);
Line *toInsert = (Line *)gExpansions.get(key);
if(toInsert != NULL) {
toInsert->isExpansion = TRUE;
u_strcpy(toInsert->expansionString, line->expansionString);
toInsert->expLen = line->expLen;
toInsert->previous->next = toInsert->next;
toInsert->next->previous = toInsert->previous;
gElements.remove(key);
} else {
toInsert = new Line(*line);
toInsert->isExpansion = TRUE;
gElements.put(UnicodeString(toInsert->name, toInsert->len), toInsert, status);
}
int32_t i = 0;
Line testLine;
Line *l = &testLine;
for(i = 0; i < size; i++) {
u_strcpy(testLine.name, (*(gLines+i))->name);
u_strcat(testLine.name, line->expansionString);
testLine.len = (*(gLines+i))->len + line->expLen;
if(comparer(&l, &line) > 0) {
toInsert->previous = *(gLines+i-1);
toInsert->next = *(gLines+i);
toInsert->previous->next = toInsert;
toInsert->next->previous = toInsert;
break;
}
}
if(gVerbose) {
u_fprintf(log, "Adding expansion\n");
escapeALine(line, log);
u_fprintf(log, "/");
escapeExpansion(line, log);
u_fprintf(log, " ");
}
}
void
positionExpansions(Line **gLines, int32_t size, CompareFn comparer) {
int result = 0;
Line *line = NULL;
Line *toMove = NULL;
int32_t i = 0, j = 0;
Line **sortedExpansions = new Line*[gExpansions.count()];
int32_t sortedExpansionsSize = setArray(sortedExpansions, &gExpansions);
qsort(sortedExpansions, sortedExpansionsSize, sizeof(Line *), comparer);
// Make a list of things in the vincinity of expansion candidate
for(j = 0; j < sortedExpansionsSize; j++) {
line = *(sortedExpansions+j);
UnicodeString key(line->name, line->len);
toMove = (Line *)gElements.get(key);
int32_t i = 0;
Line testLine, prevTestLine;
Line *l = &testLine;
Line *prevL = &prevTestLine;
// This can be further optimized, since we now know that we have a
// sorted list of expansions, so current can start from toMove, since all
// the elements before it are already smaller. In the beggining it needs to
// be on gLines, though.
Line *current = *gLines;
while(current) {
if(current == toMove) {
// we are wading through a sorted list
// if we found ourselves, it means that we
// are already in a right place, so no moving
// is needed, but we need to make sure we have
// the right strength.
toMove->strength = probeStrength(&prevL, &toMove, comparer);
if(0) {
u_fprintf(log, "Positioned expansion without moving ");
printLine(toMove, log);
u_fprintf(log, " new ordering: \n");
printOrdering(gLines, size, log, TRUE);
}
break;
} else {
u_strcpy(testLine.name, current->name);
if(!current->isExpansion) {
u_strcat(testLine.name, line->expansionString);
testLine.len = current->len + line->expLen;
} else {
testLine.len = current->len;
}
if(comparer(&l, &line) > 0) {
// remove from chain
if(toMove->next) {
toMove->next->strength = probeStrength(&(toMove->previous), &(toMove->next), comparer);
toMove->next->previous = toMove->previous;
}
if(toMove->previous) {
toMove->previous->next = toMove->next;
}
// insert
toMove->previous = current->previous;
toMove->next = current;
if(current->previous) {
current->previous->next = toMove;
}
current->previous = toMove;
toMove->strength = probeStrength(&prevL, &toMove, comparer);
toMove->next->strength = probeStrength(&toMove, &l, comparer);
if(0) {
u_fprintf(log, "Positioned expansion ");
printLine(toMove, log);
u_fprintf(log, " new ordering: \n");
printOrdering(gLines, size, log, TRUE);
}
if(toMove->strength == UCOL_IDENTICAL) {
// check for craziness such as s = ss/s
// such line would consist of previous (or next) concatenated with the expansion value
// make a test
UChar fullString[256];
u_strcpy(fullString, toMove->previous->name);
u_strcat(fullString, toMove->expansionString);
if(u_strcmp(fullString, toMove->name) == 0) {
toMove->previous->next = toMove->next;
toMove->next->previous = toMove->previous;
toMove->isRemoved = TRUE;
u_fprintf(log, "Removed: ");
printLine(toMove, log);
u_fprintf(log, "\n");
}
} else if(toMove->next->strength == UCOL_IDENTICAL) {
UChar fullString[256];
u_strcpy(fullString, toMove->next->name);
u_strcat(fullString, toMove->expansionString);
if(u_strcmp(fullString, toMove->name) == 0) {
toMove->next->strength = toMove->strength;
toMove->previous->next = toMove->next;
toMove->next->previous = toMove->previous;
toMove->isRemoved = TRUE;
u_fprintf(log, "Removed because of back: ");
printLine(toMove, log);
u_fprintf(log, "\n");
}
}
break;
}
prevTestLine = testLine;
}
current = current->next;
}
}
delete[] sortedExpansions;
}
void
noteExpansion(Line *line) {
UErrorCode status = U_ZERO_ERROR;
UnicodeString key(line->name, line->len);
Line *el = (Line *)gElements.get(key);
if(el != NULL) {
el->isExpansion = TRUE;
u_strcpy(el->expansionString, line->expansionString);
el->expLen = line->expLen;
} else {
Line *toInsert = new Line(*line);
toInsert->isExpansion = TRUE;
gElements.put(UnicodeString(line->name, line->len), toInsert, status);
}
Line *el2 = (Line *)gExpansions.get(key);
el2->isExpansion = TRUE;
u_strcpy(el2->expansionString, line->expansionString);
el2->expLen = line->expLen;
if(gDebug) {
u_fprintf(log, "Adding expansion\n");
printLine(line, log);
u_fprintf(log, "\n");
}
}
void
noteContraction(Line *line) {
UErrorCode status = U_ZERO_ERROR;
Line *toInsert = new Line(*line);
toInsert->isContraction = TRUE;
gElements.put(UnicodeString(line->name, line->len), toInsert, status);
if(gVerbose) {
u_fprintf(log, "Adding contraction\n");
escapeALine(line, log);
u_fprintf(log, " ");
}
}
void
noteElement(Line *line) {
UErrorCode status = U_ZERO_ERROR;
Line *toInsert = new Line(*line);
gElements.put(UnicodeString(line->name, line->len), toInsert, status);
if(0) { //if(gDebug)
escapeALine(line, log);
u_fprintf(log, " ");
}
}
// This function checks if a combination of characters has changed place with the
// adjacent elements. If so, these are most probably contractions.
// However, it still needs to be checked if these contractions are fake - the
// test is simple - if xy is suspected contraction, if we get that x/y is expansion, then
// xy is a fake contraction.
int32_t
analyzeContractions(Line** lines, int32_t size, CompareFn comparer) {
int32_t i = 0, j = 0;
int32_t outOfOrder = 0;
UColAttributeValue strength = UCOL_OFF;
UColAttributeValue currStrength = UCOL_OFF;
Line **prevLine = lines;
Line **currLine = NULL;
Line **backupLine = NULL;
UBool prevIsContraction = FALSE, currIsContraction = FALSE;
// Problem here is detecting a contraction that is at the very end of the sorted list
for(i = 1; i < size; i++) {
currLine = lines+i;
strength = probeStrength(prevLine, currLine, comparer);
if(strength == UCOL_OFF || strength != (*currLine)->strength) {
prevIsContraction = FALSE;
currIsContraction = FALSE;
if(!outOfOrder) {
if(gVerbose) {
u_fprintf(log, "Possible contractions: ");
}
}
// now we have two elements that are different. The question is,
// which one of them is the contraction - which one has moved.
// Could be the previous, but could also be the current.
outOfOrder++;
// First, lets check whether the previous has jumped back
j = i+1;
// skip all the nexts that have smaller strength, they don't have an effect
while(j < size && (*(lines+j))->strength > (*currLine)->strength) {
j++;
}
// check if there are other elements of same or greater strength
while(j < size &&
(strength = probeStrength(prevLine, (backupLine = lines+j), comparer)) == UCOL_OFF) {
j++;
// if we skipped more than one, it might be in fact a contraction
prevIsContraction = TRUE;
}
if(prevIsContraction) {
noteContraction(*prevLine);
j = i-2;
// add all the previous elements with smaller strength, since they also
// will jump over and are contractions
while(j >= 0 && (*(lines+j+1))->strength > (*currLine)->strength) {
strength = probeStrength(lines+j, currLine, comparer);
if(strength == UCOL_OFF) {
noteContraction(*(lines+j));
}
j--;
}
}
// now we check if the current element is jumping forward,
// the dance steps are analogous to above.
j = i - 2;
while(j >= 0 && (*(lines+j+1))->strength > (*currLine)->strength) {
j--;
}
while(j >= 0 &&
(strength = probeStrength((backupLine = lines+j), currLine, comparer)) == UCOL_OFF) {
j--;
currIsContraction = TRUE;
}
if(currIsContraction) {
if(gVerbose) {
escapeALine(*currLine, log);
u_fprintf(log, " ");
}
j = i+1;
while(j < size && (*(lines+j))->strength > (*currLine)->strength) {
strength = probeStrength(prevLine, lines+j, comparer);
if(strength == UCOL_OFF) {
noteContraction(*(lines+j));
}
j++;
}
}
// Not sure about either. List both and then check
if(!(prevIsContraction || currIsContraction)) {
noteContraction(*prevLine);
noteContraction(*currLine);
}
}
prevLine = currLine;
}
if(outOfOrder) {
if(gVerbose) {
u_fprintf(log, "\n");
}
}
return outOfOrder;
}
int32_t
detectContractions(Line **gLines, Line *lines, int32_t size, CompareFn comparer) {
int32_t i = 0, j = 0;
int32_t noContractions = 0;
// Create and compare doubles:
Line *backupLines = new Line[size];
Line::copyArray(backupLines, lines, size);
// detect contractions
Line **gLinesBackup = NULL; //new Line*[size];
for(i = 0; i < size; i++) {
// preserve index and previous
Line::copyArray(lines, backupLines, size);
for(j = 0; j < size; j++) {
u_strcpy(lines[j].name, backupLines[i].name);
u_strcat(lines[j].name, backupLines[j].name);
lines[j].len = backupLines[i].len+backupLines[j].len;
}
if((noContractions += analyzeContractions(gLines, size, comparer)) && gDebug) {
if(gLinesBackup == NULL) {
gLinesBackup = new Line*[size];
}
// Show the sorted doubles, for debugging
setArray(gLinesBackup, lines, size);
qsort(gLinesBackup, size, sizeof(Line *), comparer);
//setIndexes(gLinesBackup, size);
analyzeStrength(gLinesBackup, size, comparer);
printOrdering(gLinesBackup, size, log);
}
if(!gQuiet) {
u_fprintf(log, ".");
}
}
if(!gQuiet) {
u_fprintf(log, "\n");
}
delete[] backupLines;
if(gLinesBackup) {
delete[] gLinesBackup;
}
return noContractions;
}
// gLines in this function is an array of sorted pointers.
// Contractions are already included.
int32_t
detectExpansions(Line **gLines, int32_t size, CompareFn comparer) {
UErrorCode status = U_ZERO_ERROR;
// detect expansions
UColAttributeValue startStrength = UCOL_OFF, endStrength = UCOL_OFF,
strength = UCOL_OFF, previousStrength = UCOL_OFF;
Line start, end, src;
Line *startP = &start, *endP = &end, *srcP = &src;
Line *current = NULL;
memset(startP, 0, sizeof(Line));
memset(endP, 0, sizeof(Line));
memset(srcP, 0, sizeof(Line));
int32_t srcLen;
int32_t i = 0, j = 0, k = 0;
for(i = 0; i < size; i++) {
u_strcpy(start.name, (*(gLines+i))->name);
u_strcpy(end.name, (*(gLines+i))->name);
srcLen = (*(gLines+i))->len;
u_strcpy(start.name+srcLen, (*(gLines))->name);
start.len = srcLen + (*(gLines))->len;
u_strcpy(end.name+srcLen, (*(gLines+size-1))->name);
end.len = srcLen + (*(gLines+size-1))->len;
for(k = 0; k < size; k++) { // k is index of a thing that is not doubled
current = *(gLines+k);
// see if we have moved to front
// has it moved to the very beggining
if((startStrength = probeStrength((gLines+k), &startP, comparer)) != UCOL_OFF) {
continue; // this one is in the front
}
// has it moved to the very end?
if((endStrength = probeStrength(&endP, (gLines+k), comparer)) != UCOL_OFF) {
continue; // this one is in the back
}
// Potential Expansion
if(gDebug) { //gVerbose
u_fprintf(log, "Possible expansion: ");
escapeALine(*(gLines+k), log);
u_fprintf(log, " ");
}
// Now we have to make sure that this is really an expansion
// First, we have to find it
u_strcpy(src.name, (*(gLines+i))->name);
for(j = 0; j < size; j++) {
u_strcpy(src.name+srcLen, (*(gLines+j))->name);
src.len = srcLen + (*(gLines+j))->len;
if((strength = probeStrength(&srcP, (gLines+k), comparer)) == UCOL_OFF) {
strength = probeStrength((gLines+k), &srcP, comparer);
// we found it *(gLines+j-1) is the element that is interesting
// since gLines+j-1 < gLines+k < gLines+j
if(gDebug) { //gVerbose
u_fprintf(log, "i = %i, k = %i, j = %i ", i, k, j);
escapeALine(*(gLines+i), log);
escapeALine(*(gLines+j-1), log);
printStrength(previousStrength, log);
escapeALine(current, log);
printStrength(strength, log);
escapeALine(*(gLines+i), log);
escapeALine(*(gLines+j), log);
u_fprintf(log, "\n");
}
// check whether it is a contraction that is the same as an expansion
// or a multi character that doesn't do anything
current->addExpansionHit(i, j);
current->isExpansion = TRUE;
current->expIndex = k;
// cache expansion
gExpansions.put(UnicodeString(current->name, current->len), current, status); //new Line(*current)
break;
}
previousStrength = strength;
}
}
if(!gQuiet) {
u_fprintf(log, ".");
}
}
if(!gQuiet) {
u_fprintf(log, "\n");
}
// now we have identified possible expansions. We need to find out how do they expand.
// Let's iterate over expansions cache - it's easier.
const UHashElement *el = NULL;
int32_t hashIndex = -1;
Line *doubles = new Line[size*10];
Line **sorter = new Line*[size*10];
int32_t currSize = 0;
int32_t newSize = 0;
Line *prev = NULL;
Line *next = NULL;
Line *origin = NULL;
int result = 0;
// Make a list of things in the vincinity of expansion candidate
// in expansionPrefixes and expansionAfter we have stored the
// prefixes of stuff that caused the detection of an expansion
// and a position where the expansion was.
// For example (icu, de__PHONEBOOK), we had:
// aE <<< \u00E4 < af
// AD < \u00E4 <<< Ae
// From that we will construct the following sequence:
// AD < aE <<< \u00E4/ <<< Ae < af
// then we will take the vincinity of \u00E4:
// aE <<< \u00E4/ <<< Ae
// then we will choose the smallest expansion to be the expansion
// part: 'e'.
// if there is equality, we choose the equal part:
// (win32, de__PHONEBOOK):
// AD < \u00E4/ = ae <<< aE <<< Ae
// we choose 'e'.
while((el = gExpansions.nextElement(hashIndex)) != NULL) {
newSize = 0;
current = (Line *)el->value.pointer;
currSize = size*current->expansionPrefixesSize;
if(gDebug) {
escapeALine(current, log);
u_fprintf(log, " Number: %i\n", current->expansionPrefixesSize);
}
// construct the doubles
for(i = 0; i < current->expansionPrefixesSize; i++) {
doubles[newSize].suffix = current->expansionAfter[i]-1;
doubles[newSize++].setToConcat(*(gLines+current->expansionPrefixes[i]), *(gLines+current->expansionAfter[i]-1));
doubles[newSize].suffix = current->expansionAfter[i];
doubles[newSize++].setToConcat(*(gLines+current->expansionPrefixes[i]), *(gLines+current->expansionAfter[i]));
}
// add the expansion we're observing
doubles[newSize++] = *current;
setArray(sorter, doubles, newSize);
qsort(sorter, newSize, sizeof(Line*), comparer);
analyzeStrength(sorter, newSize, comparer);
if(gDebug) {
printOrdering(sorter, newSize, log);
}
i = 0;
while(**(sorter+i) != *current) {
i++;
}
// find the two additions
if((*(sorter+i))->strength == UCOL_IDENTICAL) {
// if we ae id
origin = *(gLines+((*(sorter+i-1))->suffix));
u_strcpy(current->expansionString, origin->name);
current->expLen = origin->len;
} else if(i < newSize-1 && (*(sorter+i+1))->strength == UCOL_IDENTICAL) {
origin = *(gLines+((*(sorter+i+1))->suffix));
u_strcpy(current->expansionString, origin->name);
current->expLen = origin->len;
} else {
if(i > 0) {
prev = *(gLines+(*(sorter+i-1))->suffix);
if(i < newSize-1) {
next = *(gLines+(*(sorter+i+1))->suffix);
result = comparer(&prev, &next);
if(result <= 0) {
u_strcpy(current->expansionString, prev->name);
current->expLen = prev->len;
} else {
u_strcpy(current->expansionString, next->name);
current->expLen = next->len;
}
}
}
if(0) { //if(gDebug)
u_fprintf(log, "Expansion is: ");
escapeALine(current, log);
u_fprintf(log, "/");
escapeExpansion(current, log);
u_fprintf(log, "\n");
}
}
noteExpansion(current);
//noteExpansion(gLines, current, size, comparer);
if(!gQuiet) {
u_fprintf(log, ".");
}
}
if(!gQuiet) {
u_fprintf(log, "\n");
}
delete[] doubles;
delete[] sorter;
return gExpansions.count();
}
UBool
isTailored(Line *line, UErrorCode &status) {
UBool result = FALSE;
UCollationElements *tailoring = ucol_openElements(gCol, line->name, line->len, &status);
UCollationElements *uca = ucol_openElements(gUCA, line->name, line->len, &status);
int32_t tailElement = UCOL_NULLORDER;
int32_t ucaElement = UCOL_NULLORDER;
do {
do {
tailElement = ucol_next(tailoring, &status);
} while(tailElement == 0);
do {
ucaElement = ucol_next(uca, &status);
} while(ucaElement == 0);
if(tailElement != ucaElement) {
result = TRUE;
break;
}
} while (tailElement != UCOL_NULLORDER && ucaElement != UCOL_NULLORDER);
ucol_closeElements(tailoring);
ucol_closeElements(uca);
return result;
}
void
reduceUntailored(Line **gLines, int32_t size){
UErrorCode status = U_ZERO_ERROR;
Line *current = *(gLines);
Line *previous = NULL;
while(current) {
// if the current line is not tailored according to the UCA
if(!isTailored(current, status)) {
// we remove it
current->isRemoved = TRUE;
} else {
// if it's tailored
if(current->previous && current->previous->isRemoved == TRUE) {
previous = current->previous;
while(previous && (previous->strength > current->strength || previous->isExpansion || previous->isContraction) && previous->isRemoved) {
if(previous->previous && previous->previous->isRemoved) {
previous = previous->previous;
} else {
break;
}
}
if(previous) {
previous->isReset = TRUE;
} else {
(*(gLines))->isReset = TRUE;
}
}
}
current = current->next;
}
}
void
constructAndAnalyze(Line **gLines, Line *lines, int32_t size, CompareFn comparer) {
int32_t i = 0, j = 0, k = 0;
// setup our compare arrays to point to single set.
// For contractions we need a block of data
setArray(gLines, lines, size);
//size = setArray(gLines);
qsort(gLines, size, sizeof(Line *), comparer);
// Establish who is previous according to the sort order
//setIndexes(gLines, size);
analyzeStrength(gLines, size, comparer);
if(gVerbose) {
u_fprintf(log, "Ordering:\n");
printOrdering(gLines, size, log);
}
//showDifferences(exemplarSetSize);
//dumpData(exemplarSetSize);
if(!gQuiet) {
u_fprintf(log, "Detecting contractions?\n");
}
int32_t noContractions = 0;
noContractions = detectContractions(gLines, lines, size, comparer);
if(!gQuiet) {
u_fprintf(log, "Detected %i contractions\n", noContractions);
}
// now we have suspected contractions in the table
// we have to re-sort the things
size = setArray(gLines);
qsort(gLines, size, sizeof(Line *), comparer);
analyzeStrength(gLines, size, comparer);
if(!gQuiet) {
u_fprintf(log, "Detecting expansions\n");
}
int32_t noExpansions = detectExpansions(gLines, size, comparer);
if(!gQuiet) {
u_fprintf(log, "Detected %i expansions\n", noExpansions);
}
positionExpansions(gLines, size, comparer);
if(gVerbose) {
u_fprintf(log, "After positioning expansions:\n");
printOrdering(gLines, size, log, TRUE);
}
//reduceUntailored(gLines, size);
if(!gQuiet) {
u_fprintf(out, "Final result\n");
}
printOrdering(gLines, size, out, TRUE);
printOrdering(gLines, size, log, TRUE);
}
// Check whether upper case comes before lower case or vice-versa
int32_t
checkCaseOrdering(void) {
UChar stuff[][3] = {
{ 0x0061, separatorChar, 0x0061}, //"aa",
{ 0x0061, separatorChar, 0x0041 }, //"a\\u00E0",
{ 0x0041, separatorChar, 0x0061 }, //"\\u00E0a",
{ 0x0041, separatorChar, 0x0041 }, //"\\u00E0a",
//{ 0x00E0, separatorChar, 0x00E0 } //"\\u00E0\\u00E0"
};
const int32_t size = sizeof(stuff)/sizeof(stuff[0]);
Line **sortedLines = new Line*[size];
Line lines[size];
int32_t i = 0;
int32_t ordered = 0, reversed = 0;
for(i = 0; i < size; i++) {
lines[i].setName(stuff[i], 3);
}
setArray(sortedLines, lines, size);
qsort(sortedLines, size, sizeof(Line*), gComparer);
for(i = 0; i < size; i++) {
if(*(sortedLines+i) == &lines[i]) {
ordered++;
}
if(*(sortedLines+i) == &lines[size-i-1]) {
reversed++;
}
}
delete[] sortedLines;
if(ordered == size) {
return 0; // in normal order
} else if(reversed == size) {
return 1; // in reversed order
} else {
return -1; // unknown order
}
}
// Check whether the secondaries are in the straight or reversed order
int32_t
checkSecondaryOrdering(void) {
UChar stuff[][5] = {
{ 0x0061, separatorChar, 0x0061, separatorChar, 0x00E0 }, //"aa",
{ 0x0061, separatorChar, 0x00E0, separatorChar, 0x0061 }, //"a\\u00E0",
{ 0x00E0, separatorChar, 0x0061, separatorChar, 0x0061 }, //"\\u00E0a",
//{ 0x00E0, separatorChar, 0x00E0 } //"\\u00E0\\u00E0"
};
const int32_t size = sizeof(stuff)/sizeof(stuff[0]);
Line **sortedLines = new Line*[size];
Line lines[size];
int32_t i = 0;
int32_t ordered = 0, reversed = 0;
for(i = 0; i < size; i++) {
lines[i].setName(stuff[i], 5);
}
setArray(sortedLines, lines, size);
qsort(sortedLines, size, sizeof(Line*), gComparer);
for(i = 0; i < size; i++) {
if(*(sortedLines+i) == &lines[i]) {
ordered++;
}
if(*(sortedLines+i) == &lines[size-i-1]) {
reversed++;
}
}
delete[] sortedLines;
if(ordered == size) {
return 0; // in normal order
} else if(reversed == size) {
return 1; // in reversed order
} else {
return -1; // unknown order
}
}
// We have to remove ignorable characters from the exemplar set,
// otherwise, we get messed up results
void removeIgnorableChars(UnicodeSet &exemplarUSet, CompareFn comparer, UErrorCode &status) {
UnicodeSet ignorables, primaryIgnorables;
UnicodeSetIterator exemplarUSetIter(exemplarUSet);
exemplarUSetIter.reset();
Line empty;
Line *emptyP = &empty;
Line current;
Line *currLine = &current;
UColAttributeValue strength = UCOL_OFF;
while(exemplarUSetIter.next()) {
if(exemplarUSetIter.isString()) { // process a string
u_memcpy(currLine->name, exemplarUSetIter.getString().getBuffer(), exemplarUSetIter.getString().length());
currLine->len = exemplarUSetIter.getString().length();
strength = probeStrength(&emptyP, &currLine, comparer);
if(strength == UCOL_IDENTICAL) {
ignorables.add(exemplarUSetIter.getString());
} else if(strength > UCOL_PRIMARY) {
primaryIgnorables.add(exemplarUSetIter.getString());
}
} else { // process code point
UBool isError = FALSE;
UChar32 codePoint = exemplarUSetIter.getCodepoint();
currLine->len = 0;
U16_APPEND(currLine->name, currLine->len, 25, codePoint, isError);
strength = probeStrength(&emptyP, &currLine, comparer);
if(strength == UCOL_IDENTICAL) {
ignorables.add(codePoint);
} else if(strength > UCOL_PRIMARY) {
primaryIgnorables.add(codePoint);
}
}
}
exemplarUSet.removeAll(ignorables);
exemplarUSet.removeAll(primaryIgnorables);
UnicodeString removedPattern;
if(ignorables.size()) {
u_fprintf(log, "Ignorables:\n");
ignorables.toPattern(removedPattern, TRUE);
removedPattern.setCharAt(removedPattern.length(), 0);
escapeString(removedPattern.getBuffer(), removedPattern.length(), log);
u_fprintf(log, "\n");
}
if(primaryIgnorables.size()) {
u_fprintf(log, "Primary ignorables:\n");
primaryIgnorables.toPattern(removedPattern, TRUE);
removedPattern.setCharAt(removedPattern.length(), 0);
escapeString(removedPattern.getBuffer(), removedPattern.length(), log);
u_fprintf(log, "\n");
}
}
// TODO: develop logic for choosing boundary characters - right now it is hardcoded
// It should be a function of used scripts. Also, check whether we need to save
// used script names
void addUtilityChars(UnicodeSet &exemplarUSet, UErrorCode &status) {
// in order to get nice rules, we need to add some characters to the
// starting set. These are mostly parts of compatibity composed characters,
// such as L-middle dot (middle dot is 0x00B7). If we don't add these, we would
// get a reset at a funky character, such as L-middle dot. This list will probably
// grow.
exemplarUSet.add(0x00B7);
// these things represent a script before the target script and
// a script after. More logic should be added so that these characters are
// chosen automatically
exemplarUSet.add(0x0038);
exemplarUSet.add(0x0039);
//exemplarUSet.add(0x0433);
//exemplarUSet.add(0x0436);
exemplarUSet.add(0xfa29);
exemplarUSet.add(0xfa28);
}
void
getExemplars(const char *locale, UnicodeSet &exemplars, UErrorCode &status) {
// first we fill out structures with exemplar characters.
UResourceBundle *res = ures_open(NULL, locale, &status);
int32_t exemplarLength = 0;
UnicodeString exemplarString = ures_getUnicodeStringByKey(res, "ExemplarCharacters", &status);
exemplars.clear();
exemplars.applyPattern(exemplarString, status);
ures_close(res);
}
void
prepareStartingSet(UnicodeSet &exemplarUSet, CompareFn comparer, UErrorCode &status) {
int32_t i = 0;
UnicodeString exemplarString;
exemplarUSet.toPattern(exemplarString);
// Produce case closure of exemplar characters
// Then we want to figure out what is the script of the exemplar characters
// just pick several and see their script
const char* usedScriptNames[USCRIPT_CODE_LIMIT];
int32_t numberOfUsedScripts = 0;
char scriptSetPattern[256];
UnicodeString pattern; // for debugging
UChar32 exChar = -1;
while(exemplarUSet.size() != 0 && (exChar = exemplarUSet.charAt(0)) != -1) {
int32_t scriptNo = u_getIntPropertyValue(exChar, UCHAR_SCRIPT);
usedScriptNames[numberOfUsedScripts] = u_getPropertyValueName(UCHAR_SCRIPT, scriptNo, U_SHORT_PROPERTY_NAME);
sprintf(scriptSetPattern, "[:%s:]", usedScriptNames[numberOfUsedScripts]);
numberOfUsedScripts++;
UnicodeSet scriptSet(UnicodeString(scriptSetPattern, ""), status);
exemplarUSet.removeAll(scriptSet);
exemplarUSet.toPattern(pattern, TRUE);
}
exemplarUSet.clear();
// always add ASCII
//exemplarUSet.addAll(UnicodeSet(UnicodeString("[\\u0020-\\u007f]", ""), status));
exemplarUSet.addAll(UnicodeSet(UnicodeString("[\\u0041-\\u005b]", ""), status));
if(gExemplar) {
exemplarUSet.applyPattern(exemplarString, status);
exemplarUSet.closeOver(USET_CASE);
if(!gQuiet) {
u_fprintf(out, "ICU exemplar characters:\n");
escapeString(exemplarString.getBuffer(), exemplarString.length(), out);
u_fprintf(out, "\n");
}
} else {
if(!gQuiet) {
u_fprintf(out, "Using scripts:\n");
}
// add interesting scripts
for(i = 0; i < numberOfUsedScripts; i++) {
sprintf(scriptSetPattern, "[:%s:]", usedScriptNames[i]);
exemplarUSet.addAll(UnicodeSet(UnicodeString(scriptSetPattern, ""), status));
if(!gQuiet) {
u_fprintf(out, "%s\n", scriptSetPattern);
}
}
}
removeIgnorableChars(exemplarUSet, comparer, status);
addUtilityChars(exemplarUSet, status);
/*
// try to check whether tailored set and exemplar characters match.
USet *tailored = ucol_getTailoredSet(gCol, &status);
UBool tailoredContained = exemplarUSet.containsAll(*((UnicodeSet *)tailored));
if(!tailoredContained) {
((UnicodeSet *)tailored)->removeAll(exemplarUSet);
UnicodeString pattern;
((UnicodeSet *)tailored)->toPattern(pattern, TRUE);
}
uset_close(tailored);
*/
//return exemplarUSet;
}
void
setOutputFile(const char *name, UErrorCode &status) {
int32_t i = 0;
char filename[256];
strcpy(filename, name);
for(i = 0; i < gPlatformNo; i++) {
strcat(filename, "_");
strcat(filename, platforms[gPlatformIndexes[i]].name);
}
if(gExemplar) {
strcat(filename, "_exemplar");
} else {
strcat(filename, "_script");
}
strcat(filename, ".utf16.txt");
out = u_fopen(filename, "wb", "en", "utf-16");
}
void
processCollator(UCollator *col, UErrorCode &status) {
int32_t i = 0;
gCol = col;
UChar ruleString[16384];
int32_t ruleStringLength = ucol_getRulesEx(gCol, UCOL_TAILORING_ONLY, ruleString, 16384);
if(!gQuiet) {
u_fprintf(out, "ICU rules:\n");
printRules(ruleString, ruleStringLength, out);
printRules(ruleString, ruleStringLength, log);
//escapeString(ruleString, ruleStringLength, out);
u_fprintf(out, "\n");
}
const char *locale = ucol_getLocale(gCol, ULOC_REQUESTED_LOCALE, &status);
UnicodeSet exemplarUSet;
if(locale) {
getExemplars(locale, exemplarUSet, status);
} else {
exemplarUSet = *((UnicodeSet *)ucol_getTailoredSet(gCol, &status));
}
for(i = 0; i < gPlatformNo; i++) {
u_fprintf(out, "\nGenerating order for platform: %s\n", platforms[gPlatformIndexes[i]].name);
gComparer = platforms[gPlatformIndexes[i]].comparer;
prepareStartingSet(exemplarUSet, gComparer, status);
int32_t itemLen = 0;
// get the number of all the items from the set (both codepoints and strings)
int32_t exemplarSetSize = exemplarUSet.size();
UnicodeSetIterator exemplarUSetIter(exemplarUSet);
// allocate ICU lines
gICULines = new Line*[exemplarSetSize*5];
int32_t j = 0;
int32_t linesCount = 0;
Line *lines = new Line[exemplarSetSize];
int32_t reversedSecondary = checkSecondaryOrdering();
if(reversedSecondary == 0) {
u_fprintf(out, "Secondaries do not seem to be reversed\n");
} else if(reversedSecondary == 1) {
u_fprintf(out, "Secondaries are reversed\n");
if(gComparer == ICUstrcmp) {
ucol_setAttribute(gCol, UCOL_FRENCH_COLLATION, UCOL_OFF, &status);
}
} else {
u_fprintf(out, "Cannot conclude if secondaries are reversed\n");
}
int32_t reversedCase = checkCaseOrdering();
if(reversedCase == 0) {
u_fprintf(out, "Case does not seem to be reversed\n");
} else if(reversedCase == 1) {
u_fprintf(out, "Case is reversed\n");
if(gComparer == ICUstrcmp) {
ucol_setAttribute(gCol, UCOL_CASE_FIRST, UCOL_OFF, &status);
}
} else {
u_fprintf(out, "Cannot conclude if case is reversed\n");
}
exemplarUSetIter.reset();
gElements.removeAll();
gExpansions.removeAll();
linesCount = 0;
while(exemplarUSetIter.next()) {
Line *currLine = lines+linesCount;
if(exemplarUSetIter.isString()) { // process a string
u_memcpy(currLine->name, exemplarUSetIter.getString().getBuffer(), exemplarUSetIter.getString().length());
currLine->len = exemplarUSetIter.getString().length();
} else { // process code point
UBool isError = FALSE;
currLine->len = 0;
U16_APPEND(currLine->name, currLine->len, 25, exemplarUSetIter.getCodepoint(), isError);
}
currLine->name[currLine->len] = 0; // zero terminate, for our evil ways
currLine->index = linesCount;
linesCount++;
noteElement(currLine);
}
constructAndAnalyze(gICULines, lines, exemplarSetSize, gComparer);
delete[] lines;
}
// cleanup globals
delete[] gICULines;
u_fflush(out);
u_fclose(out);
ucol_close(gCol);
}
void
processLocale(const char *locale, UErrorCode &status) {
gWinLCID = uloc_getLCID(locale);
UCollator *col = ucol_open(locale, &status);
setOutputFile(locale, status);
u_fprintf(out, "Locale %s (LCID:%06X)\n", locale, gWinLCID);
processCollator(col, status);
}
UBool
hasCollationElements(const char *locName) {
UErrorCode status = U_ZERO_ERROR;
UResourceBundle *ColEl = NULL;
UResourceBundle *loc = ures_open(NULL, locName, &status);;
if(U_SUCCESS(status)) {
status = U_ZERO_ERROR;
ColEl = ures_getByKey(loc, "CollationElements", ColEl, &status);
if(status == U_ZERO_ERROR) { /* do the test - there are real elements */
ures_close(ColEl);
ures_close(loc);
return TRUE;
}
ures_close(ColEl);
ures_close(loc);
}
return FALSE;
}
int
main(int argc,
char* argv[])
{
UErrorCode status = U_ZERO_ERROR;
err = u_finit(stderr, "en", "latin-1");
log = u_finit(stdout, "en", "latin-1");
/*
USet *wsp = uprv_openRuleWhiteSpaceSet(&status);
uset_add(wsp, 0x0041);
uset_remove(wsp, 0x0041);
UnicodeString pat;
((UnicodeSet *)wsp)->toPattern(pat, TRUE);
pat.setCharAt(pat.length(), 0);
escapeString(pat.getBuffer(), pat.length(), log);
u_fflush(log);
*/
UTransliterator *anyHex = utrans_open("[^\\u000a\\u0020-\\u007f] Any-Hex/Java", UTRANS_FORWARD, NULL, 0, NULL, &status);
u_fsettransliterator(log, U_WRITE, anyHex, &status);
processArgs(argc, argv, status);
int32_t i = 0;
gElements.setValueDeleter(deleteLineElement);
if(U_FAILURE(status) || gPlatformNo == 0) {
return -1;
}
gUCA = ucol_open("root", &status);
if(gRulesStdin) {
char buffer[1024];
UChar ruleBuffer[16384];
UChar *rules = ruleBuffer;
int32_t maxRuleLen = 16384;
int32_t rLen = 0;
while(gets(buffer)) {
if(buffer[0] != '/' && buffer[1] != '/') {
rLen = u_unescape(buffer, rules, maxRuleLen);
rules += rLen;
maxRuleLen -= rLen;
}
}
UParseError parseError;
//escapeString(ruleBuffer, rules-ruleBuffer, log);//
u_fprintf(log, "%U\n", ruleBuffer);
UCollator *col = ucol_openRules(ruleBuffer, rules-ruleBuffer, UCOL_DEFAULT, UCOL_DEFAULT, &parseError, &status);
if(U_SUCCESS(status)) {
setOutputFile("stdinRules", status);
processCollator(col, status);
} else {
u_fprintf(err, "Error %s\n", u_errorName(status));
}
} else {
if(gLocale) {
processLocale(gLocale, status);
} else if(gLocaleNo) {
for(i = 0; i < gLocaleNo; i++) {
processLocale(gLocales[i], status);
}
} else { // do the loop through all the locales
int32_t noOfLoc = uloc_countAvailable();
const char *locName = NULL;
for(i = 0; i<noOfLoc; i++) {
status = U_ZERO_ERROR;
locName = uloc_getAvailable(i);
if(hasCollationElements(locName)) {
processLocale(locName, status);
}
}
}
}
ucol_close(gUCA);
u_fflush(log);
u_fclose(log);
u_fflush(err);
u_fclose(err);
return 0;
}