| /************************************************************************** | 
 |  * © 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; | 
 | } |