1/*
2**********************************************************************
3* Copyright (C) 1998-2004, 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 const char *gPath = 0;
88static UResourceBundle *gBundle = NULL;
89
90U_STRING_DECL(gNoFormatting, " (UCONFIG_NO_FORMATTING see uconfig.h)", 38);
91
92U_CFUNC UResourceBundle *u_wmsg_setPath(const char *path, UErrorCode *err)
93{
94  if(U_FAILURE(*err))
95  {
96    return 0;
97  }
98
99  if(gBundle != NULL)
100  {
101    *err = U_ILLEGAL_ARGUMENT_ERROR;
102    return 0;
103  }
104  else
105  {
106    UResourceBundle *b = NULL;
107    b = ures_open(path, NULL, err);
108    if(U_FAILURE(*err))
109    {
110         return 0;
111    }
112
113    gPath = uprv_strdup(path);
114    gBundle = b;
115
116    U_STRING_INIT(gNoFormatting, " (UCONFIG_NO_FORMATTING see uconfig.h)", 38);
117  }
118
119  return gBundle;
120}
121
122/* Format a message and print it's output to fp */
123U_CFUNC int u_wmsg(FILE *fp, const char *tag, ... )
124{
125    const UChar *msg;
126    int32_t      msgLen;
127    UErrorCode  err = U_ZERO_ERROR;
128#if !UCONFIG_NO_FORMATTING
129    va_list ap;
130#endif
131    UChar   result[4096];
132    int32_t resultLength = LENGTHOF(result);
133
134    if(gBundle == NULL)
135    {
136#if 0
137        fprintf(stderr, "u_wmsg: No path set!!\n"); /* FIXME: codepage?? */
138#endif
139        return -1;
140    }
141
142    msg = ures_getStringByKey(gBundle, tag, &msgLen, &err);
143
144    if(U_FAILURE(err))
145    {
146#if 0
147        fprintf(stderr, "u_wmsg: failed to load tag [%s] [%s] [%s]!!\n", tag,  u_errorName(err), gPath);
148#endif
149        return -1;
150    }
151
152#if UCONFIG_NO_FORMATTING
153    resultLength = sizeof(gNoFormatting) / U_SIZEOF_UCHAR;
154    if((msgLen + resultLength) <= LENGTHOF(result)) {
155        memcpy(result, msg, msgLen * U_SIZEOF_UCHAR);
156        memcpy(result + msgLen, gNoFormatting, resultLength);
157        resultLength += msgLen;
158        uprint(result, resultLength, fp, &err);
159    } else {
160        uprint(msg,msgLen, fp, &err);
161    }
162#else
163    va_start(ap, tag);
164
165    resultLength = u_vformatMessage(uloc_getDefault(), msg, msgLen, result, resultLength, ap, &err);
166
167    va_end(ap);
168
169    if(U_FAILURE(err))
170    {
171#if 0
172        fprintf(stderr, "u_wmsg: failed to format %s:%s, err %s\n",
173            uloc_getDefault(),
174            tag,
175            u_errorName(err));
176#endif
177        err = U_ZERO_ERROR;
178        uprint(msg,msgLen, fp, &err);
179        return -1;
180    }
181
182    uprint(result, resultLength, fp, &err);
183#endif
184
185    if(U_FAILURE(err))
186    {
187#if 0
188        fprintf(stderr, "u_wmsg: failed to print %s: %s, err %s\n",
189            uloc_getDefault(),
190            tag,
191            u_errorName(err));
192#endif
193        return -1;
194    }
195
196    return 0;
197}
198
199/* these will break if the # of messages change. simply add or remove 0's .. */
200UChar **gInfoMessages = NULL;
201
202UChar **gErrMessages = NULL;
203
204static const UChar *fetchErrorName(UErrorCode err)
205{
206    if (!gInfoMessages) {
207        gInfoMessages = (UChar **)malloc((U_ERROR_WARNING_LIMIT-U_ERROR_WARNING_START)*sizeof(UChar*));
208        memset(gInfoMessages, 0, (U_ERROR_WARNING_LIMIT-U_ERROR_WARNING_START)*sizeof(UChar*));
209    }
210    if (!gErrMessages) {
211        gErrMessages = (UChar **)malloc(U_ERROR_LIMIT*sizeof(UChar*));
212        memset(gErrMessages, 0, U_ERROR_LIMIT*sizeof(UChar*));
213    }
214    if(err>=0)
215        return gErrMessages[err];
216    else
217        return gInfoMessages[err-U_ERROR_WARNING_START];
218}
219
220U_CFUNC const UChar *u_wmsg_errorName(UErrorCode err)
221{
222    UChar *msg;
223    int32_t msgLen;
224    UErrorCode subErr = U_ZERO_ERROR;
225    const char *textMsg = NULL;
226
227    /* try the cache */
228    msg = (UChar*)fetchErrorName(err);
229
230    if(msg)
231    {
232        return msg;
233    }
234
235    if(gBundle == NULL)
236    {
237        msg = NULL;
238    }
239    else
240    {
241        const char *errname = u_errorName(err);
242        if (errname) {
243            msg = (UChar*)ures_getStringByKey(gBundle, errname, &msgLen, &subErr);
244            if(U_FAILURE(subErr))
245            {
246                msg = NULL;
247            }
248        }
249    }
250
251    if(msg == NULL)  /* Couldn't find it anywhere.. */
252    {
253        char error[128];
254        textMsg = u_errorName(err);
255        if (!textMsg) {
256            sprintf(error, "UNDOCUMENTED ICU ERROR %d", err);
257            textMsg = error;
258        }
259        msg = (UChar*)malloc((strlen(textMsg)+1)*sizeof(msg[0]));
260        u_charsToUChars(textMsg, msg, (int32_t)(strlen(textMsg)+1));
261    }
262
263    if(err>=0)
264        gErrMessages[err] = msg;
265    else
266        gInfoMessages[err-U_ERROR_WARNING_START] = msg;
267
268    return msg;
269}
270