1/*
2**********************************************************************
3* Copyright (C) 1998-2012, International Business Machines Corporation
4* and others.  All Rights Reserved.
5**********************************************************************
6*
7* File uwmsg.c
8*
9* Modification History:
10*
11*   Date        Name        Description
12*   06/14/99    stephen     Creation.
13*******************************************************************************
14*/
15
16#include "unicode/ucnv.h"
17#include "unicode/ustring.h"
18#include "unicode/umsg.h"
19#include "unicode/uwmsg.h"
20#include "unicode/ures.h"
21#include "unicode/putil.h"
22#include "cstring.h"
23
24#include <stdlib.h>
25#include <stdarg.h>
26#include <stdio.h>
27#include <string.h>
28
29#define LENGTHOF(array) (sizeof(array)/sizeof((array)[0]))
30
31#define BUF_SIZE 128
32
33/* Print a ustring to the specified FILE* in the default codepage */
34static void
35uprint(const UChar *s,
36       int32_t sourceLen,
37       FILE *f,
38       UErrorCode *status)
39{
40    /* converter */
41    UConverter *converter;
42    char buf [BUF_SIZE];
43    const UChar *mySource;
44    const UChar *mySourceEnd;
45    char *myTarget;
46    int32_t arraySize;
47
48    if(s == 0) return;
49
50    /* set up the conversion parameters */
51    mySource     = s;
52    mySourceEnd  = mySource + sourceLen;
53    myTarget     = buf;
54    arraySize    = BUF_SIZE;
55
56    /* open a default converter */
57    converter = ucnv_open(0, status);
58
59    /* if we failed, clean up and exit */
60    if(U_FAILURE(*status)) goto finish;
61
62    /* perform the conversion */
63    do {
64        /* reset the error code */
65        *status = U_ZERO_ERROR;
66
67        /* perform the conversion */
68        ucnv_fromUnicode(converter, &myTarget,  myTarget + arraySize,
69            &mySource, mySourceEnd, NULL,
70            TRUE, status);
71
72        /* Write the converted data to the FILE* */
73        fwrite(buf, sizeof(char), myTarget - buf, f);
74
75        /* update the conversion parameters*/
76        myTarget     = buf;
77        arraySize    = BUF_SIZE;
78    }
79    while(*status == U_BUFFER_OVERFLOW_ERROR);
80
81finish:
82
83    /* close the converter */
84    ucnv_close(converter);
85}
86
87static UResourceBundle *gBundle = NULL;
88
89U_STRING_DECL(gNoFormatting, " (UCONFIG_NO_FORMATTING see uconfig.h)", 38);
90
91U_CFUNC UResourceBundle *u_wmsg_setPath(const char *path, UErrorCode *err)
92{
93  if(U_FAILURE(*err))
94  {
95    return 0;
96  }
97
98  if(gBundle != NULL)
99  {
100    *err = U_ILLEGAL_ARGUMENT_ERROR;
101    return 0;
102  }
103  else
104  {
105    UResourceBundle *b = NULL;
106    b = ures_open(path, NULL, err);
107    if(U_FAILURE(*err))
108    {
109         return 0;
110    }
111
112    gBundle = b;
113
114    U_STRING_INIT(gNoFormatting, " (UCONFIG_NO_FORMATTING see uconfig.h)", 38);
115  }
116
117  return gBundle;
118}
119
120/* Format a message and print it's output to fp */
121U_CFUNC int u_wmsg(FILE *fp, const char *tag, ... )
122{
123    const UChar *msg;
124    int32_t      msgLen;
125    UErrorCode  err = U_ZERO_ERROR;
126#if !UCONFIG_NO_FORMATTING
127    va_list ap;
128#endif
129    UChar   result[4096];
130    int32_t resultLength = LENGTHOF(result);
131
132    if(gBundle == NULL)
133    {
134#if 0
135        fprintf(stderr, "u_wmsg: No path set!!\n"); /* FIXME: codepage?? */
136#endif
137        return -1;
138    }
139
140    msg = ures_getStringByKey(gBundle, tag, &msgLen, &err);
141
142    if(U_FAILURE(err))
143    {
144        return -1;
145    }
146
147#if UCONFIG_NO_FORMATTING
148    resultLength = sizeof(gNoFormatting) / U_SIZEOF_UCHAR;
149    if((msgLen + resultLength) <= LENGTHOF(result)) {
150        memcpy(result, msg, msgLen * U_SIZEOF_UCHAR);
151        memcpy(result + msgLen, gNoFormatting, resultLength);
152        resultLength += msgLen;
153        uprint(result, resultLength, fp, &err);
154    } else {
155        uprint(msg,msgLen, fp, &err);
156    }
157#else
158    va_start(ap, tag);
159
160    resultLength = u_vformatMessage(uloc_getDefault(), msg, msgLen, result, resultLength, ap, &err);
161
162    va_end(ap);
163
164    if(U_FAILURE(err))
165    {
166#if 0
167        fprintf(stderr, "u_wmsg: failed to format %s:%s, err %s\n",
168            uloc_getDefault(),
169            tag,
170            u_errorName(err));
171#endif
172        err = U_ZERO_ERROR;
173        uprint(msg,msgLen, fp, &err);
174        return -1;
175    }
176
177    uprint(result, resultLength, fp, &err);
178#endif
179
180    if(U_FAILURE(err))
181    {
182#if 0
183        fprintf(stderr, "u_wmsg: failed to print %s: %s, err %s\n",
184            uloc_getDefault(),
185            tag,
186            u_errorName(err));
187#endif
188        return -1;
189    }
190
191    return 0;
192}
193
194/* these will break if the # of messages change. simply add or remove 0's .. */
195UChar **gInfoMessages = NULL;
196
197UChar **gErrMessages = NULL;
198
199static const UChar *fetchErrorName(UErrorCode err)
200{
201    if (!gInfoMessages) {
202        gInfoMessages = (UChar **)malloc((U_ERROR_WARNING_LIMIT-U_ERROR_WARNING_START)*sizeof(UChar*));
203        memset(gInfoMessages, 0, (U_ERROR_WARNING_LIMIT-U_ERROR_WARNING_START)*sizeof(UChar*));
204    }
205    if (!gErrMessages) {
206        gErrMessages = (UChar **)malloc(U_ERROR_LIMIT*sizeof(UChar*));
207        memset(gErrMessages, 0, U_ERROR_LIMIT*sizeof(UChar*));
208    }
209    if(err>=0)
210        return gErrMessages[err];
211    else
212        return gInfoMessages[err-U_ERROR_WARNING_START];
213}
214
215U_CFUNC const UChar *u_wmsg_errorName(UErrorCode err)
216{
217    UChar *msg;
218    int32_t msgLen;
219    UErrorCode subErr = U_ZERO_ERROR;
220    const char *textMsg = NULL;
221
222    /* try the cache */
223    msg = (UChar*)fetchErrorName(err);
224
225    if(msg)
226    {
227        return msg;
228    }
229
230    if(gBundle == NULL)
231    {
232        msg = NULL;
233    }
234    else
235    {
236        const char *errname = u_errorName(err);
237        if (errname) {
238            msg = (UChar*)ures_getStringByKey(gBundle, errname, &msgLen, &subErr);
239            if(U_FAILURE(subErr))
240            {
241                msg = NULL;
242            }
243        }
244    }
245
246    if(msg == NULL)  /* Couldn't find it anywhere.. */
247    {
248        char error[128];
249        textMsg = u_errorName(err);
250        if (!textMsg) {
251            sprintf(error, "UNDOCUMENTED ICU ERROR %d", err);
252            textMsg = error;
253        }
254        msg = (UChar*)malloc((strlen(textMsg)+1)*sizeof(msg[0]));
255        u_charsToUChars(textMsg, msg, (int32_t)(strlen(textMsg)+1));
256    }
257
258    if(err>=0)
259        gErrMessages[err] = msg;
260    else
261        gInfoMessages[err-U_ERROR_WARNING_START] = msg;
262
263    return msg;
264}
265