1/********************************************************************
2 * COPYRIGHT:
3 * Copyright (c) 1999-2007, International Business Machines Corporation and
4 * others. All Rights Reserved.
5 ********************************************************************/
6
7#include "unicode/utypes.h"
8#include "unicode/ucnv.h"
9#include "flagcb.h"
10#include <string.h>
11#include <stdlib.h>
12#include <stdio.h>
13
14#define DEBUG_TMI 0  /* set to 1 for Too Much Information (TMI) */
15
16U_CAPI FromUFLAGContext* U_EXPORT2  flagCB_fromU_openContext()
17{
18    FromUFLAGContext *ctx;
19
20    ctx = (FromUFLAGContext*) malloc(sizeof(FromUFLAGContext));
21
22    ctx->subCallback = NULL;
23    ctx->subContext  = NULL;
24    ctx->flag        = FALSE;
25
26    return ctx;
27}
28
29U_CAPI void U_EXPORT2 flagCB_fromU(
30                  const void *context,
31                  UConverterFromUnicodeArgs *fromUArgs,
32                  const UChar* codeUnits,
33                  int32_t length,
34                  UChar32 codePoint,
35                  UConverterCallbackReason reason,
36				  UErrorCode * err)
37{
38  /* First step - based on the reason code, take action */
39
40  if(reason == UCNV_UNASSIGNED) { /* whatever set should be trapped here */
41    ((FromUFLAGContext*)context)->flag = TRUE;
42  }
43
44  if(reason == UCNV_CLONE) {
45      /* The following is the recommended way to implement UCNV_CLONE
46         in a callback. */
47      UConverterFromUCallback   saveCallback;
48      const void *saveContext;
49      FromUFLAGContext *old, *cloned;
50      UErrorCode subErr = U_ZERO_ERROR;
51
52#if DEBUG_TMI
53      printf("*** FLAGCB: cloning %p ***\n", context);
54#endif
55      old = (FromUFLAGContext*)context;
56      cloned = flagCB_fromU_openContext();
57
58      memcpy(cloned, old, sizeof(FromUFLAGContext));
59
60#if DEBUG_TMI
61      printf("%p: my subcb=%p:%p\n", old, old->subCallback,
62             old->subContext);
63      printf("%p: cloned subcb=%p:%p\n", cloned, cloned->subCallback,
64             cloned->subContext);
65#endif
66
67      /* We need to get the sub CB to handle cloning,
68       * so we have to set up the following, temporarily:
69       *
70       *   - Set the callback+context to the sub of this (flag) cb
71       *   - preserve the current cb+context, it could be anything
72       *
73       *   Before:
74       *      CNV  ->   FLAG ->  subcb -> ...
75       *
76       *   After:
77       *      CNV  ->   subcb -> ...
78       *
79       *    The chain from 'something' on is saved, and will be restored
80       *   at the end of this block.
81       *
82       */
83
84      ucnv_setFromUCallBack(fromUArgs->converter,
85                            cloned->subCallback,
86                            cloned->subContext,
87                            &saveCallback,
88                            &saveContext,
89                            &subErr);
90
91      if( cloned->subCallback != NULL ) {
92          /* Now, call the sub callback if present */
93          cloned->subCallback(cloned->subContext, fromUArgs, codeUnits,
94                              length, codePoint, reason, err);
95      }
96
97      ucnv_setFromUCallBack(fromUArgs->converter,
98                            saveCallback,  /* Us */
99                            cloned,        /* new context */
100                            &cloned->subCallback,  /* IMPORTANT! Accept any change in CB or context */
101                            &cloned->subContext,
102                            &subErr);
103
104      if(U_FAILURE(subErr)) {
105          *err = subErr;
106      }
107  }
108
109  /* process other reasons here if need be */
110
111  /* Always call the subCallback if present */
112  if(((FromUFLAGContext*)context)->subCallback != NULL &&
113      reason != UCNV_CLONE) {
114      ((FromUFLAGContext*)context)->subCallback(  ((FromUFLAGContext*)context)->subContext,
115                                                  fromUArgs,
116                                                  codeUnits,
117                                                  length,
118                                                  codePoint,
119                                                  reason,
120                                                  err);
121  }
122
123  /* cleanup - free the memory AFTER calling the sub CB */
124  if(reason == UCNV_CLOSE) {
125      free((void*)context);
126  }
127}
128
129/* Debugging callback, just outputs what happens */
130
131/* Test safe clone callback */
132
133static uint32_t    debugCB_nextSerial()
134{
135    static uint32_t n = 1;
136
137    return (n++);
138}
139
140static void debugCB_print_log(debugCBContext *q, const char *name)
141{
142    if(q==NULL) {
143        printf("debugCBontext: %s is NULL!!\n", name);
144    } else {
145        if(q->magic != 0xC0FFEE) {
146            fprintf(stderr, "debugCBContext: %p:%d's magic is %x, supposed to be 0xC0FFEE\n",
147                    q,q->serial, q->magic);
148        }
149        printf("debugCBContext %p:%d=%s - magic %x\n",
150                    q, q->serial, name, q->magic);
151    }
152}
153
154static debugCBContext *debugCB_clone(debugCBContext *ctx)
155{
156    debugCBContext *newCtx;
157    newCtx = malloc(sizeof(debugCBContext));
158
159    newCtx->serial = debugCB_nextSerial();
160    newCtx->magic = 0xC0FFEE;
161
162    newCtx->subCallback = ctx->subCallback;
163    newCtx->subContext = ctx->subContext;
164
165#if DEBUG_TMI
166    printf("debugCB_clone: %p:%d -> new context %p:%d\n", ctx, ctx->serial, newCtx, newCtx->serial);
167#endif
168
169    return newCtx;
170}
171
172void debugCB_fromU(const void *context,
173                   UConverterFromUnicodeArgs *fromUArgs,
174                   const UChar* codeUnits,
175                   int32_t length,
176                   UChar32 codePoint,
177                   UConverterCallbackReason reason,
178                   UErrorCode * err)
179{
180    debugCBContext *ctx = (debugCBContext*)context;
181    /*UConverterFromUCallback junkFrom;*/
182
183#if DEBUG_TMI
184    printf("debugCB_fromU: Context %p:%d called, reason %d on cnv %p [err=%s]\n", ctx, ctx->serial, reason, fromUArgs->converter, u_errorName(*err));
185#endif
186
187    if(ctx->magic != 0xC0FFEE) {
188        fprintf(stderr, "debugCB_fromU: Context %p:%d magic is 0x%x should be 0xC0FFEE.\n", ctx,ctx->serial, ctx->magic);
189        return;
190    }
191
192    if(reason == UCNV_CLONE) {
193        /* see comments in above flagCB clone code */
194
195        UConverterFromUCallback   saveCallback;
196        const void *saveContext;
197        debugCBContext *cloned;
198        UErrorCode subErr = U_ZERO_ERROR;
199
200        /* "recreate" it */
201#if DEBUG_TMI
202        printf("debugCB_fromU: cloning..\n");
203#endif
204        cloned = debugCB_clone(ctx);
205
206        if(cloned == NULL) {
207            fprintf(stderr, "debugCB_fromU: internal clone failed on %p\n", ctx);
208            *err = U_MEMORY_ALLOCATION_ERROR;
209            return;
210        }
211
212        ucnv_setFromUCallBack(fromUArgs->converter,
213                              cloned->subCallback,
214                              cloned->subContext,
215                              &saveCallback,
216                              &saveContext,
217                              &subErr);
218
219        if( cloned->subCallback != NULL) {
220#if DEBUG_TMI
221            printf("debugCB_fromU:%p calling subCB %p\n", ctx, cloned->subCallback);
222#endif
223            /* call subCB if present */
224            cloned->subCallback(cloned->subContext, fromUArgs, codeUnits,
225                                length, codePoint, reason, err);
226        } else {
227            printf("debugCB_fromU:%p, NOT calling subCB, it's NULL\n", ctx);
228        }
229
230        /* set back callback */
231        ucnv_setFromUCallBack(fromUArgs->converter,
232                              saveCallback,  /* Us */
233                              cloned,        /* new context */
234                              &cloned->subCallback,  /* IMPORTANT! Accept any change in CB or context */
235                              &cloned->subContext,
236                              &subErr);
237
238        if(U_FAILURE(subErr)) {
239            *err = subErr;
240        }
241    }
242
243    /* process other reasons here */
244
245    /* always call subcb if present */
246    if(ctx->subCallback != NULL && reason != UCNV_CLONE) {
247        ctx->subCallback(ctx->subContext,
248                         fromUArgs,
249                         codeUnits,
250                         length,
251                         codePoint,
252                         reason,
253                         err);
254    }
255
256    if(reason == UCNV_CLOSE) {
257#if DEBUG_TMI
258        printf("debugCB_fromU: Context %p:%d closing\n", ctx, ctx->serial);
259#endif
260        free(ctx);
261    }
262
263#if DEBUG_TMI
264    printf("debugCB_fromU: leaving cnv %p, ctx %p: err %s\n", fromUArgs->converter, ctx, u_errorName(*err));
265#endif
266}
267
268debugCBContext *debugCB_openContext()
269{
270    debugCBContext *ctx;
271
272    ctx = malloc(sizeof(debugCBContext));
273
274    if(ctx != NULL) {
275        ctx->magic = 0xC0FFEE;
276        ctx->serial = debugCB_nextSerial();
277        ctx->subCallback = NULL;
278        ctx->subContext  = NULL;
279
280#if DEBUG_TMI
281        fprintf(stderr, "debugCB:openContext opened[%p] = serial #%d\n", ctx, ctx->serial);
282#endif
283
284    }
285
286
287    return ctx;
288}
289