1 // © 2016 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
3 /*
4 *******************************************************************************
5 *
6 *   Copyright (C) 1999-2012, International Business Machines
7 *   Corporation and others.  All Rights Reserved.
8 *
9 *******************************************************************************
10 *   file name:  umsg.cpp
11 *   encoding:   UTF-8
12 *   tab size:   8 (not used)
13 *   indentation:4
14 *
15 * This is a C wrapper to MessageFormat C++ API.
16 *
17 *   Change history:
18 *
19 *   08/5/2001  Ram         Added C wrappers for C++ API. Changed implementation of old API's
20 *                          Removed pattern parser.
21 *
22 */
23 
24 #include "unicode/utypes.h"
25 
26 #if !UCONFIG_NO_FORMATTING
27 
28 #include "unicode/umsg.h"
29 #include "unicode/ustring.h"
30 #include "unicode/fmtable.h"
31 #include "unicode/msgfmt.h"
32 #include "unicode/unistr.h"
33 #include "cpputils.h"
34 #include "uassert.h"
35 #include "ustr_imp.h"
36 
37 U_NAMESPACE_BEGIN
38 /**
39  * This class isolates our access to private internal methods of
40  * MessageFormat.  It is never instantiated; it exists only for C++
41  * access management.
42  */
43 class MessageFormatAdapter {
44 public:
45     static const Formattable::Type* getArgTypeList(const MessageFormat& m,
46                                                    int32_t& count);
hasArgTypeConflicts(const MessageFormat & m)47     static UBool hasArgTypeConflicts(const MessageFormat& m) {
48         return m.hasArgTypeConflicts;
49     }
50 };
51 const Formattable::Type*
getArgTypeList(const MessageFormat & m,int32_t & count)52 MessageFormatAdapter::getArgTypeList(const MessageFormat& m,
53                                      int32_t& count) {
54     return m.getArgTypeList(count);
55 }
56 U_NAMESPACE_END
57 
58 U_NAMESPACE_USE
59 
60 U_CAPI int32_t
u_formatMessage(const char * locale,const UChar * pattern,int32_t patternLength,UChar * result,int32_t resultLength,UErrorCode * status,...)61 u_formatMessage(const char  *locale,
62                 const UChar *pattern,
63                 int32_t     patternLength,
64                 UChar       *result,
65                 int32_t     resultLength,
66                 UErrorCode  *status,
67                 ...)
68 {
69     va_list    ap;
70     int32_t actLen;
71     //argument checking defered to subsequent method calls
72     // start vararg processing
73     va_start(ap, status);
74 
75     actLen = u_vformatMessage(locale,pattern,patternLength,result,resultLength,ap,status);
76     // end vararg processing
77     va_end(ap);
78 
79     return actLen;
80 }
81 
82 U_CAPI int32_t U_EXPORT2
u_vformatMessage(const char * locale,const UChar * pattern,int32_t patternLength,UChar * result,int32_t resultLength,va_list ap,UErrorCode * status)83 u_vformatMessage(   const char  *locale,
84                     const UChar *pattern,
85                     int32_t     patternLength,
86                     UChar       *result,
87                     int32_t     resultLength,
88                     va_list     ap,
89                     UErrorCode  *status)
90 
91 {
92     //argument checking defered to subsequent method calls
93     UMessageFormat *fmt = umsg_open(pattern,patternLength,locale,NULL,status);
94     int32_t retVal = umsg_vformat(fmt,result,resultLength,ap,status);
95     umsg_close(fmt);
96     return retVal;
97 }
98 
99 U_CAPI int32_t
u_formatMessageWithError(const char * locale,const UChar * pattern,int32_t patternLength,UChar * result,int32_t resultLength,UParseError * parseError,UErrorCode * status,...)100 u_formatMessageWithError(const char *locale,
101                         const UChar *pattern,
102                         int32_t     patternLength,
103                         UChar       *result,
104                         int32_t     resultLength,
105                         UParseError *parseError,
106                         UErrorCode  *status,
107                         ...)
108 {
109     va_list    ap;
110     int32_t actLen;
111     //argument checking defered to subsequent method calls
112     // start vararg processing
113     va_start(ap, status);
114 
115     actLen = u_vformatMessageWithError(locale,pattern,patternLength,result,resultLength,parseError,ap,status);
116 
117     // end vararg processing
118     va_end(ap);
119     return actLen;
120 }
121 
122 U_CAPI int32_t U_EXPORT2
u_vformatMessageWithError(const char * locale,const UChar * pattern,int32_t patternLength,UChar * result,int32_t resultLength,UParseError * parseError,va_list ap,UErrorCode * status)123 u_vformatMessageWithError(  const char  *locale,
124                             const UChar *pattern,
125                             int32_t     patternLength,
126                             UChar       *result,
127                             int32_t     resultLength,
128                             UParseError *parseError,
129                             va_list     ap,
130                             UErrorCode  *status)
131 
132 {
133     //argument checking defered to subsequent method calls
134     UMessageFormat *fmt = umsg_open(pattern,patternLength,locale,parseError,status);
135     int32_t retVal = umsg_vformat(fmt,result,resultLength,ap,status);
136     umsg_close(fmt);
137     return retVal;
138 }
139 
140 
141 // For parse, do the reverse of format:
142 //  1. Call through to the C++ APIs
143 //  2. Just assume the user passed in enough arguments.
144 //  3. Iterate through each formattable returned, and assign to the arguments
145 U_CAPI void
u_parseMessage(const char * locale,const UChar * pattern,int32_t patternLength,const UChar * source,int32_t sourceLength,UErrorCode * status,...)146 u_parseMessage( const char   *locale,
147                 const UChar  *pattern,
148                 int32_t      patternLength,
149                 const UChar  *source,
150                 int32_t      sourceLength,
151                 UErrorCode   *status,
152                 ...)
153 {
154     va_list    ap;
155     //argument checking defered to subsequent method calls
156 
157     // start vararg processing
158     va_start(ap, status);
159 
160     u_vparseMessage(locale,pattern,patternLength,source,sourceLength,ap,status);
161     // end vararg processing
162     va_end(ap);
163 }
164 
165 U_CAPI void U_EXPORT2
u_vparseMessage(const char * locale,const UChar * pattern,int32_t patternLength,const UChar * source,int32_t sourceLength,va_list ap,UErrorCode * status)166 u_vparseMessage(const char  *locale,
167                 const UChar *pattern,
168                 int32_t     patternLength,
169                 const UChar *source,
170                 int32_t     sourceLength,
171                 va_list     ap,
172                 UErrorCode  *status)
173 {
174     //argument checking defered to subsequent method calls
175     UMessageFormat *fmt = umsg_open(pattern,patternLength,locale,NULL,status);
176     int32_t count = 0;
177     umsg_vparse(fmt,source,sourceLength,&count,ap,status);
178     umsg_close(fmt);
179 }
180 
181 U_CAPI void
u_parseMessageWithError(const char * locale,const UChar * pattern,int32_t patternLength,const UChar * source,int32_t sourceLength,UParseError * error,UErrorCode * status,...)182 u_parseMessageWithError(const char  *locale,
183                         const UChar *pattern,
184                         int32_t     patternLength,
185                         const UChar *source,
186                         int32_t     sourceLength,
187                         UParseError *error,
188                         UErrorCode  *status,
189                         ...)
190 {
191     va_list    ap;
192 
193     //argument checking defered to subsequent method calls
194 
195     // start vararg processing
196     va_start(ap, status);
197 
198     u_vparseMessageWithError(locale,pattern,patternLength,source,sourceLength,ap,error,status);
199     // end vararg processing
200     va_end(ap);
201 }
202 U_CAPI void U_EXPORT2
u_vparseMessageWithError(const char * locale,const UChar * pattern,int32_t patternLength,const UChar * source,int32_t sourceLength,va_list ap,UParseError * error,UErrorCode * status)203 u_vparseMessageWithError(const char  *locale,
204                          const UChar *pattern,
205                          int32_t     patternLength,
206                          const UChar *source,
207                          int32_t     sourceLength,
208                          va_list     ap,
209                          UParseError *error,
210                          UErrorCode* status)
211 {
212     //argument checking defered to subsequent method calls
213     UMessageFormat *fmt = umsg_open(pattern,patternLength,locale,error,status);
214     int32_t count = 0;
215     umsg_vparse(fmt,source,sourceLength,&count,ap,status);
216     umsg_close(fmt);
217 }
218 //////////////////////////////////////////////////////////////////////////////////
219 //
220 //  Message format C API
221 //
222 /////////////////////////////////////////////////////////////////////////////////
223 
224 
225 U_CAPI UMessageFormat* U_EXPORT2
umsg_open(const UChar * pattern,int32_t patternLength,const char * locale,UParseError * parseError,UErrorCode * status)226 umsg_open(  const UChar     *pattern,
227             int32_t         patternLength,
228             const  char     *locale,
229             UParseError     *parseError,
230             UErrorCode      *status)
231 {
232     //check arguments
233     if(status==NULL || U_FAILURE(*status))
234     {
235       return 0;
236     }
237     if(pattern==NULL||patternLength<-1){
238         *status=U_ILLEGAL_ARGUMENT_ERROR;
239         return 0;
240     }
241 
242     UParseError tErr;
243     if(parseError==NULL)
244     {
245         parseError = &tErr;
246     }
247 
248     int32_t len = (patternLength == -1 ? u_strlen(pattern) : patternLength);
249     UnicodeString patString(patternLength == -1, pattern, len);
250 
251     MessageFormat* retVal = new MessageFormat(patString,Locale(locale),*parseError,*status);
252     if(retVal == NULL) {
253         *status = U_MEMORY_ALLOCATION_ERROR;
254         return NULL;
255     }
256     if (U_SUCCESS(*status) && MessageFormatAdapter::hasArgTypeConflicts(*retVal)) {
257         *status = U_ARGUMENT_TYPE_MISMATCH;
258     }
259     return (UMessageFormat*)retVal;
260 }
261 
262 U_CAPI void U_EXPORT2
umsg_close(UMessageFormat * format)263 umsg_close(UMessageFormat* format)
264 {
265     //check arguments
266     if(format==NULL){
267         return;
268     }
269     delete (MessageFormat*) format;
270 }
271 
272 U_CAPI UMessageFormat U_EXPORT2
umsg_clone(const UMessageFormat * fmt,UErrorCode * status)273 umsg_clone(const UMessageFormat *fmt,
274            UErrorCode *status)
275 {
276     //check arguments
277     if(status==NULL || U_FAILURE(*status)){
278         return NULL;
279     }
280     if(fmt==NULL){
281         *status = U_ILLEGAL_ARGUMENT_ERROR;
282         return NULL;
283     }
284     UMessageFormat retVal = (UMessageFormat)((MessageFormat*)fmt)->clone();
285     if(retVal == 0) {
286         *status = U_MEMORY_ALLOCATION_ERROR;
287         return 0;
288     }
289     return retVal;
290 }
291 
292 U_CAPI void  U_EXPORT2
umsg_setLocale(UMessageFormat * fmt,const char * locale)293 umsg_setLocale(UMessageFormat *fmt, const char* locale)
294 {
295     //check arguments
296     if(fmt==NULL){
297         return;
298     }
299     ((MessageFormat*)fmt)->setLocale(Locale(locale));
300 }
301 
302 U_CAPI const char*  U_EXPORT2
umsg_getLocale(const UMessageFormat * fmt)303 umsg_getLocale(const UMessageFormat *fmt)
304 {
305     //check arguments
306     if(fmt==NULL){
307         return "";
308     }
309     return ((const MessageFormat*)fmt)->getLocale().getName();
310 }
311 
312 U_CAPI void  U_EXPORT2
umsg_applyPattern(UMessageFormat * fmt,const UChar * pattern,int32_t patternLength,UParseError * parseError,UErrorCode * status)313 umsg_applyPattern(UMessageFormat *fmt,
314                            const UChar* pattern,
315                            int32_t patternLength,
316                            UParseError* parseError,
317                            UErrorCode* status)
318 {
319     //check arguments
320     UParseError tErr;
321     if(status ==NULL||U_FAILURE(*status)){
322         return ;
323     }
324     if(fmt==NULL || (pattern==NULL && patternLength!=0) || patternLength<-1) {
325         *status=U_ILLEGAL_ARGUMENT_ERROR;
326         return ;
327     }
328 
329     if(parseError==NULL){
330       parseError = &tErr;
331     }
332 
333     // UnicodeString(pattern, -1) calls u_strlen().
334     ((MessageFormat*)fmt)->applyPattern(UnicodeString(pattern,patternLength),*parseError,*status);
335 }
336 
337 U_CAPI int32_t  U_EXPORT2
umsg_toPattern(const UMessageFormat * fmt,UChar * result,int32_t resultLength,UErrorCode * status)338 umsg_toPattern(const UMessageFormat *fmt,
339                UChar* result,
340                int32_t resultLength,
341                UErrorCode* status)
342 {
343     //check arguments
344     if(status ==NULL||U_FAILURE(*status)){
345         return -1;
346     }
347     if(fmt==NULL||resultLength<0 || (resultLength>0 && result==0)){
348         *status=U_ILLEGAL_ARGUMENT_ERROR;
349         return -1;
350     }
351 
352 
353     UnicodeString res;
354     if(!(result==NULL && resultLength==0)) {
355         // NULL destination for pure preflighting: empty dummy string
356         // otherwise, alias the destination buffer
357         res.setTo(result, 0, resultLength);
358     }
359     ((const MessageFormat*)fmt)->toPattern(res);
360     return res.extract(result, resultLength, *status);
361 }
362 
363 U_CAPI int32_t
umsg_format(const UMessageFormat * fmt,UChar * result,int32_t resultLength,UErrorCode * status,...)364 umsg_format(    const UMessageFormat *fmt,
365                 UChar          *result,
366                 int32_t        resultLength,
367                 UErrorCode     *status,
368                 ...)
369 {
370     va_list    ap;
371     int32_t actLen;
372     //argument checking defered to last method call umsg_vformat which
373     //saves time when arguments are valid and we dont care when arguments are not
374     //since we return an error anyway
375 
376 
377     // start vararg processing
378     va_start(ap, status);
379 
380     actLen = umsg_vformat(fmt,result,resultLength,ap,status);
381 
382     // end vararg processing
383     va_end(ap);
384 
385     return actLen;
386 }
387 
388 U_CAPI int32_t U_EXPORT2
umsg_vformat(const UMessageFormat * fmt,UChar * result,int32_t resultLength,va_list ap,UErrorCode * status)389 umsg_vformat(   const UMessageFormat *fmt,
390                 UChar          *result,
391                 int32_t        resultLength,
392                 va_list        ap,
393                 UErrorCode     *status)
394 {
395     //check arguments
396     if(status==0 || U_FAILURE(*status))
397     {
398         return -1;
399     }
400     if(fmt==NULL||resultLength<0 || (resultLength>0 && result==0)) {
401         *status=U_ILLEGAL_ARGUMENT_ERROR;
402         return -1;
403     }
404 
405     int32_t count =0;
406     const Formattable::Type* argTypes =
407         MessageFormatAdapter::getArgTypeList(*(const MessageFormat*)fmt, count);
408     // Allocate at least one element.  Allocating an array of length
409     // zero causes problems on some platforms (e.g. Win32).
410     Formattable* args = new Formattable[count ? count : 1];
411 
412     // iterate through the vararg list, and get the arguments out
413     for(int32_t i = 0; i < count; ++i) {
414 
415         UChar *stringVal;
416         double tDouble=0;
417         int32_t tInt =0;
418         int64_t tInt64 = 0;
419         UDate tempDate = 0;
420         switch(argTypes[i]) {
421         case Formattable::kDate:
422             tempDate = va_arg(ap, UDate);
423             args[i].setDate(tempDate);
424             break;
425 
426         case Formattable::kDouble:
427             tDouble =va_arg(ap, double);
428             args[i].setDouble(tDouble);
429             break;
430 
431         case Formattable::kLong:
432             tInt = va_arg(ap, int32_t);
433             args[i].setLong(tInt);
434             break;
435 
436         case Formattable::kInt64:
437             tInt64 = va_arg(ap, int64_t);
438             args[i].setInt64(tInt64);
439             break;
440 
441         case Formattable::kString:
442             // For some reason, a temporary is needed
443             stringVal = va_arg(ap, UChar*);
444             if(stringVal){
445                 args[i].setString(UnicodeString(stringVal));
446             }else{
447                 *status=U_ILLEGAL_ARGUMENT_ERROR;
448             }
449             break;
450 
451         case Formattable::kArray:
452             // throw away this argument
453             // this is highly platform-dependent, and probably won't work
454             // so, if you try to skip arguments in the list (and not use them)
455             // you'll probably crash
456             va_arg(ap, int);
457             break;
458 
459         case Formattable::kObject:
460             // Unused argument number. Read and ignore a pointer argument.
461             va_arg(ap, void*);
462             break;
463 
464         default:
465             // Unknown/unsupported argument type.
466             UPRV_UNREACHABLE;
467         }
468     }
469     UnicodeString resultStr;
470     FieldPosition fieldPosition(FieldPosition::DONT_CARE);
471 
472     /* format the message */
473     ((const MessageFormat*)fmt)->format(args,count,resultStr,fieldPosition,*status);
474 
475     delete[] args;
476 
477     if(U_FAILURE(*status)){
478         return -1;
479     }
480 
481     return resultStr.extract(result, resultLength, *status);
482 }
483 
484 U_CAPI void
umsg_parse(const UMessageFormat * fmt,const UChar * source,int32_t sourceLength,int32_t * count,UErrorCode * status,...)485 umsg_parse( const UMessageFormat *fmt,
486             const UChar    *source,
487             int32_t        sourceLength,
488             int32_t        *count,
489             UErrorCode     *status,
490             ...)
491 {
492     va_list    ap;
493     //argument checking defered to last method call umsg_vparse which
494     //saves time when arguments are valid and we dont care when arguments are not
495     //since we return an error anyway
496 
497     // start vararg processing
498     va_start(ap, status);
499 
500     umsg_vparse(fmt,source,sourceLength,count,ap,status);
501 
502     // end vararg processing
503     va_end(ap);
504 }
505 
506 U_CAPI void U_EXPORT2
umsg_vparse(const UMessageFormat * fmt,const UChar * source,int32_t sourceLength,int32_t * count,va_list ap,UErrorCode * status)507 umsg_vparse(const UMessageFormat *fmt,
508             const UChar    *source,
509             int32_t        sourceLength,
510             int32_t        *count,
511             va_list        ap,
512             UErrorCode     *status)
513 {
514     //check arguments
515     if(status==NULL||U_FAILURE(*status))
516     {
517         return;
518     }
519     if(fmt==NULL||source==NULL || sourceLength<-1 || count==NULL){
520         *status=U_ILLEGAL_ARGUMENT_ERROR;
521         return;
522     }
523     if(sourceLength==-1){
524         sourceLength=u_strlen(source);
525     }
526 
527     UnicodeString srcString(source,sourceLength);
528     Formattable *args = ((const MessageFormat*)fmt)->parse(srcString,*count,*status);
529     UDate *aDate;
530     double *aDouble;
531     UChar *aString;
532     int32_t* aInt;
533     int64_t* aInt64;
534     UnicodeString temp;
535     int len =0;
536     // assign formattables to varargs
537     for(int32_t i = 0; i < *count; i++) {
538         switch(args[i].getType()) {
539 
540         case Formattable::kDate:
541             aDate = va_arg(ap, UDate*);
542             if(aDate){
543                 *aDate = args[i].getDate();
544             }else{
545                 *status=U_ILLEGAL_ARGUMENT_ERROR;
546             }
547             break;
548 
549         case Formattable::kDouble:
550             aDouble = va_arg(ap, double*);
551             if(aDouble){
552                 *aDouble = args[i].getDouble();
553             }else{
554                 *status=U_ILLEGAL_ARGUMENT_ERROR;
555             }
556             break;
557 
558         case Formattable::kLong:
559             aInt = va_arg(ap, int32_t*);
560             if(aInt){
561                 *aInt = (int32_t) args[i].getLong();
562             }else{
563                 *status=U_ILLEGAL_ARGUMENT_ERROR;
564             }
565             break;
566 
567         case Formattable::kInt64:
568             aInt64 = va_arg(ap, int64_t*);
569             if(aInt64){
570                 *aInt64 = args[i].getInt64();
571             }else{
572                 *status=U_ILLEGAL_ARGUMENT_ERROR;
573             }
574             break;
575 
576         case Formattable::kString:
577             aString = va_arg(ap, UChar*);
578             if(aString){
579                 args[i].getString(temp);
580                 len = temp.length();
581                 temp.extract(0,len,aString);
582                 aString[len]=0;
583             }else{
584                 *status= U_ILLEGAL_ARGUMENT_ERROR;
585             }
586             break;
587 
588         case Formattable::kObject:
589             // This will never happen because MessageFormat doesn't
590             // support kObject.  When MessageFormat is changed to
591             // understand MeasureFormats, modify this code to do the
592             // right thing. [alan]
593             UPRV_UNREACHABLE;
594 
595         // better not happen!
596         case Formattable::kArray:
597             UPRV_UNREACHABLE;
598         }
599     }
600 
601     // clean up
602     delete [] args;
603 }
604 
605 #define SINGLE_QUOTE      ((UChar)0x0027)
606 #define CURLY_BRACE_LEFT  ((UChar)0x007B)
607 #define CURLY_BRACE_RIGHT ((UChar)0x007D)
608 
609 #define STATE_INITIAL 0
610 #define STATE_SINGLE_QUOTE 1
611 #define STATE_IN_QUOTE 2
612 #define STATE_MSG_ELEMENT 3
613 
614 #define MAppend(c) if (len < destCapacity) dest[len++] = c; else len++
615 
umsg_autoQuoteApostrophe(const UChar * pattern,int32_t patternLength,UChar * dest,int32_t destCapacity,UErrorCode * ec)616 int32_t umsg_autoQuoteApostrophe(const UChar* pattern,
617                  int32_t patternLength,
618                  UChar* dest,
619                  int32_t destCapacity,
620                  UErrorCode* ec)
621 {
622     int32_t state = STATE_INITIAL;
623     int32_t braceCount = 0;
624     int32_t len = 0;
625 
626     if (ec == NULL || U_FAILURE(*ec)) {
627         return -1;
628     }
629 
630     if (pattern == NULL || patternLength < -1 || (dest == NULL && destCapacity > 0)) {
631         *ec = U_ILLEGAL_ARGUMENT_ERROR;
632         return -1;
633     }
634     U_ASSERT(destCapacity >= 0);
635 
636     if (patternLength == -1) {
637         patternLength = u_strlen(pattern);
638     }
639 
640     for (int i = 0; i < patternLength; ++i) {
641         UChar c = pattern[i];
642         switch (state) {
643         case STATE_INITIAL:
644             switch (c) {
645             case SINGLE_QUOTE:
646                 state = STATE_SINGLE_QUOTE;
647                 break;
648             case CURLY_BRACE_LEFT:
649                 state = STATE_MSG_ELEMENT;
650                 ++braceCount;
651                 break;
652             }
653             break;
654 
655         case STATE_SINGLE_QUOTE:
656             switch (c) {
657             case SINGLE_QUOTE:
658                 state = STATE_INITIAL;
659                 break;
660             case CURLY_BRACE_LEFT:
661             case CURLY_BRACE_RIGHT:
662                 state = STATE_IN_QUOTE;
663                 break;
664             default:
665                 MAppend(SINGLE_QUOTE);
666                 state = STATE_INITIAL;
667                 break;
668             }
669         break;
670 
671         case STATE_IN_QUOTE:
672             switch (c) {
673             case SINGLE_QUOTE:
674                 state = STATE_INITIAL;
675                 break;
676             }
677             break;
678 
679         case STATE_MSG_ELEMENT:
680             switch (c) {
681             case CURLY_BRACE_LEFT:
682                 ++braceCount;
683                 break;
684             case CURLY_BRACE_RIGHT:
685                 if (--braceCount == 0) {
686                     state = STATE_INITIAL;
687                 }
688                 break;
689             }
690             break;
691 
692         default: // Never happens.
693             break;
694         }
695 
696         U_ASSERT(len >= 0);
697         MAppend(c);
698     }
699 
700     // End of scan
701     if (state == STATE_SINGLE_QUOTE || state == STATE_IN_QUOTE) {
702         MAppend(SINGLE_QUOTE);
703     }
704 
705     return u_terminateUChars(dest, destCapacity, len, ec);
706 }
707 
708 #endif /* #if !UCONFIG_NO_FORMATTING */
709