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