|  | /************************************************************************** | 
|  | * © 2016 and later: Unicode, Inc. and others. | 
|  | * License & terms of use: http://www.unicode.org/copyright.html | 
|  | ************************************************************************** | 
|  | ************************************************************************** | 
|  | * COPYRIGHT: | 
|  | * Copyright (c) 1999-2007, International Business Machines Corporation and | 
|  | * others. All Rights Reserved. | 
|  | **************************************************************************/ | 
|  |  | 
|  | #include "unicode/utypes.h" | 
|  | #include "unicode/ucnv.h" | 
|  | #include "flagcb.h" | 
|  | #include <string.h> | 
|  | #include <stdlib.h> | 
|  | #include <stdio.h> | 
|  |  | 
|  | #define DEBUG_TMI 0  /* set to 1 for Too Much Information (TMI) */ | 
|  |  | 
|  | U_CAPI FromUFLAGContext* U_EXPORT2  flagCB_fromU_openContext() | 
|  | { | 
|  | FromUFLAGContext *ctx; | 
|  |  | 
|  | ctx = (FromUFLAGContext*) malloc(sizeof(FromUFLAGContext)); | 
|  |  | 
|  | ctx->subCallback = NULL; | 
|  | ctx->subContext  = NULL; | 
|  | ctx->flag        = false; | 
|  |  | 
|  | return ctx; | 
|  | } | 
|  |  | 
|  | U_CAPI void U_EXPORT2 flagCB_fromU( | 
|  | const void *context, | 
|  | UConverterFromUnicodeArgs *fromUArgs, | 
|  | const UChar* codeUnits, | 
|  | int32_t length, | 
|  | UChar32 codePoint, | 
|  | UConverterCallbackReason reason, | 
|  | UErrorCode * err) | 
|  | { | 
|  | /* First step - based on the reason code, take action */ | 
|  |  | 
|  | if(reason == UCNV_UNASSIGNED) { /* whatever set should be trapped here */ | 
|  | ((FromUFLAGContext*)context)->flag = true; | 
|  | } | 
|  |  | 
|  | if(reason == UCNV_CLONE) { | 
|  | /* The following is the recommended way to implement UCNV_CLONE | 
|  | in a callback. */ | 
|  | UConverterFromUCallback   saveCallback; | 
|  | const void *saveContext; | 
|  | FromUFLAGContext *old, *cloned; | 
|  | UErrorCode subErr = U_ZERO_ERROR; | 
|  |  | 
|  | #if DEBUG_TMI | 
|  | printf("*** FLAGCB: cloning %p ***\n", context); | 
|  | #endif | 
|  | old = (FromUFLAGContext*)context; | 
|  | cloned = flagCB_fromU_openContext(); | 
|  |  | 
|  | memcpy(cloned, old, sizeof(FromUFLAGContext)); | 
|  |  | 
|  | #if DEBUG_TMI | 
|  | printf("%p: my subcb=%p:%p\n", old, old->subCallback, | 
|  | old->subContext); | 
|  | printf("%p: cloned subcb=%p:%p\n", cloned, cloned->subCallback, | 
|  | cloned->subContext); | 
|  | #endif | 
|  |  | 
|  | /* We need to get the sub CB to handle cloning, | 
|  | * so we have to set up the following, temporarily: | 
|  | * | 
|  | *   - Set the callback+context to the sub of this (flag) cb | 
|  | *   - preserve the current cb+context, it could be anything | 
|  | * | 
|  | *   Before: | 
|  | *      CNV  ->   FLAG ->  subcb -> ... | 
|  | * | 
|  | *   After: | 
|  | *      CNV  ->   subcb -> ... | 
|  | * | 
|  | *    The chain from 'something' on is saved, and will be restored | 
|  | *   at the end of this block. | 
|  | * | 
|  | */ | 
|  |  | 
|  | ucnv_setFromUCallBack(fromUArgs->converter, | 
|  | cloned->subCallback, | 
|  | cloned->subContext, | 
|  | &saveCallback, | 
|  | &saveContext, | 
|  | &subErr); | 
|  |  | 
|  | if( cloned->subCallback != NULL ) { | 
|  | /* Now, call the sub callback if present */ | 
|  | cloned->subCallback(cloned->subContext, fromUArgs, codeUnits, | 
|  | length, codePoint, reason, err); | 
|  | } | 
|  |  | 
|  | ucnv_setFromUCallBack(fromUArgs->converter, | 
|  | saveCallback,  /* Us */ | 
|  | cloned,        /* new context */ | 
|  | &cloned->subCallback,  /* IMPORTANT! Accept any change in CB or context */ | 
|  | &cloned->subContext, | 
|  | &subErr); | 
|  |  | 
|  | if(U_FAILURE(subErr)) { | 
|  | *err = subErr; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* process other reasons here if need be */ | 
|  |  | 
|  | /* Always call the subCallback if present */ | 
|  | if(((FromUFLAGContext*)context)->subCallback != NULL && | 
|  | reason != UCNV_CLONE) { | 
|  | ((FromUFLAGContext*)context)->subCallback(  ((FromUFLAGContext*)context)->subContext, | 
|  | fromUArgs, | 
|  | codeUnits, | 
|  | length, | 
|  | codePoint, | 
|  | reason, | 
|  | err); | 
|  | } | 
|  |  | 
|  | /* cleanup - free the memory AFTER calling the sub CB */ | 
|  | if(reason == UCNV_CLOSE) { | 
|  | free((void*)context); | 
|  | } | 
|  | } | 
|  |  | 
|  | /* Debugging callback, just outputs what happens */ | 
|  |  | 
|  | /* Test safe clone callback */ | 
|  |  | 
|  | static uint32_t    debugCB_nextSerial() | 
|  | { | 
|  | static uint32_t n = 1; | 
|  |  | 
|  | return (n++); | 
|  | } | 
|  |  | 
|  | static void debugCB_print_log(debugCBContext *q, const char *name) | 
|  | { | 
|  | if(q==NULL) { | 
|  | printf("debugCBontext: %s is NULL!!\n", name); | 
|  | } else { | 
|  | if(q->magic != 0xC0FFEE) { | 
|  | fprintf(stderr, "debugCBContext: %p:%d's magic is %x, supposed to be 0xC0FFEE\n", | 
|  | q,q->serial, q->magic); | 
|  | } | 
|  | printf("debugCBContext %p:%d=%s - magic %x\n", | 
|  | q, q->serial, name, q->magic); | 
|  | } | 
|  | } | 
|  |  | 
|  | static debugCBContext *debugCB_clone(debugCBContext *ctx) | 
|  | { | 
|  | debugCBContext *newCtx; | 
|  | newCtx = malloc(sizeof(debugCBContext)); | 
|  |  | 
|  | newCtx->serial = debugCB_nextSerial(); | 
|  | newCtx->magic = 0xC0FFEE; | 
|  |  | 
|  | newCtx->subCallback = ctx->subCallback; | 
|  | newCtx->subContext = ctx->subContext; | 
|  |  | 
|  | #if DEBUG_TMI | 
|  | printf("debugCB_clone: %p:%d -> new context %p:%d\n", ctx, ctx->serial, newCtx, newCtx->serial); | 
|  | #endif | 
|  |  | 
|  | return newCtx; | 
|  | } | 
|  |  | 
|  | void debugCB_fromU(const void *context, | 
|  | UConverterFromUnicodeArgs *fromUArgs, | 
|  | const UChar* codeUnits, | 
|  | int32_t length, | 
|  | UChar32 codePoint, | 
|  | UConverterCallbackReason reason, | 
|  | UErrorCode * err) | 
|  | { | 
|  | debugCBContext *ctx = (debugCBContext*)context; | 
|  | /*UConverterFromUCallback junkFrom;*/ | 
|  |  | 
|  | #if DEBUG_TMI | 
|  | printf("debugCB_fromU: Context %p:%d called, reason %d on cnv %p [err=%s]\n", ctx, ctx->serial, reason, fromUArgs->converter, u_errorName(*err)); | 
|  | #endif | 
|  |  | 
|  | if(ctx->magic != 0xC0FFEE) { | 
|  | fprintf(stderr, "debugCB_fromU: Context %p:%d magic is 0x%x should be 0xC0FFEE.\n", ctx,ctx->serial, ctx->magic); | 
|  | return; | 
|  | } | 
|  |  | 
|  | if(reason == UCNV_CLONE) { | 
|  | /* see comments in above flagCB clone code */ | 
|  |  | 
|  | UConverterFromUCallback   saveCallback; | 
|  | const void *saveContext; | 
|  | debugCBContext *cloned; | 
|  | UErrorCode subErr = U_ZERO_ERROR; | 
|  |  | 
|  | /* "recreate" it */ | 
|  | #if DEBUG_TMI | 
|  | printf("debugCB_fromU: cloning..\n"); | 
|  | #endif | 
|  | cloned = debugCB_clone(ctx); | 
|  |  | 
|  | if(cloned == NULL) { | 
|  | fprintf(stderr, "debugCB_fromU: internal clone failed on %p\n", ctx); | 
|  | *err = U_MEMORY_ALLOCATION_ERROR; | 
|  | return; | 
|  | } | 
|  |  | 
|  | ucnv_setFromUCallBack(fromUArgs->converter, | 
|  | cloned->subCallback, | 
|  | cloned->subContext, | 
|  | &saveCallback, | 
|  | &saveContext, | 
|  | &subErr); | 
|  |  | 
|  | if( cloned->subCallback != NULL) { | 
|  | #if DEBUG_TMI | 
|  | printf("debugCB_fromU:%p calling subCB %p\n", ctx, cloned->subCallback); | 
|  | #endif | 
|  | /* call subCB if present */ | 
|  | cloned->subCallback(cloned->subContext, fromUArgs, codeUnits, | 
|  | length, codePoint, reason, err); | 
|  | } else { | 
|  | printf("debugCB_fromU:%p, NOT calling subCB, it's NULL\n", ctx); | 
|  | } | 
|  |  | 
|  | /* set back callback */ | 
|  | ucnv_setFromUCallBack(fromUArgs->converter, | 
|  | saveCallback,  /* Us */ | 
|  | cloned,        /* new context */ | 
|  | &cloned->subCallback,  /* IMPORTANT! Accept any change in CB or context */ | 
|  | &cloned->subContext, | 
|  | &subErr); | 
|  |  | 
|  | if(U_FAILURE(subErr)) { | 
|  | *err = subErr; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* process other reasons here */ | 
|  |  | 
|  | /* always call subcb if present */ | 
|  | if(ctx->subCallback != NULL && reason != UCNV_CLONE) { | 
|  | ctx->subCallback(ctx->subContext, | 
|  | fromUArgs, | 
|  | codeUnits, | 
|  | length, | 
|  | codePoint, | 
|  | reason, | 
|  | err); | 
|  | } | 
|  |  | 
|  | if(reason == UCNV_CLOSE) { | 
|  | #if DEBUG_TMI | 
|  | printf("debugCB_fromU: Context %p:%d closing\n", ctx, ctx->serial); | 
|  | #endif | 
|  | free(ctx); | 
|  | } | 
|  |  | 
|  | #if DEBUG_TMI | 
|  | printf("debugCB_fromU: leaving cnv %p, ctx %p: err %s\n", fromUArgs->converter, ctx, u_errorName(*err)); | 
|  | #endif | 
|  | } | 
|  |  | 
|  | debugCBContext *debugCB_openContext() | 
|  | { | 
|  | debugCBContext *ctx; | 
|  |  | 
|  | ctx = malloc(sizeof(debugCBContext)); | 
|  |  | 
|  | if(ctx != NULL) { | 
|  | ctx->magic = 0xC0FFEE; | 
|  | ctx->serial = debugCB_nextSerial(); | 
|  | ctx->subCallback = NULL; | 
|  | ctx->subContext  = NULL; | 
|  |  | 
|  | #if DEBUG_TMI | 
|  | fprintf(stderr, "debugCB:openContext opened[%p] = serial #%d\n", ctx, ctx->serial); | 
|  | #endif | 
|  |  | 
|  | } | 
|  |  | 
|  |  | 
|  | return ctx; | 
|  | } |