1 // $Id: makemsg.cpp 1333 2006-06-16 20:34:16Z alex $
2 /* @@tag:xara-cn@@ DO NOT MODIFY THIS LINE
3 ================================XARAHEADERSTART===========================
5 Xara LX, a vector drawing and manipulation program.
6 Copyright (C) 1993-2006 Xara Group Ltd.
7 Copyright on certain contributions may be held in joint with their
8 respective authors. See AUTHORS file for details.
10 LICENSE TO USE AND MODIFY SOFTWARE
11 ----------------------------------
13 This file is part of Xara LX.
15 Xara LX is free software; you can redistribute it and/or modify it
16 under the terms of the GNU General Public License version 2 as published
17 by the Free Software Foundation.
19 Xara LX and its component source files are distributed in the hope
20 that it will be useful, but WITHOUT ANY WARRANTY; without even the
21 implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
22 See the GNU General Public License for more details.
24 You should have received a copy of the GNU General Public License along
25 with Xara LX (see the file GPL in the root directory of the
26 distribution); if not, write to the Free Software Foundation, Inc., 51
27 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
33 Conditional upon your continuing compliance with the GNU General Public
34 License described above, Xara Group Ltd grants to you certain additional
37 The additional rights are to use, modify, and distribute the software
38 together with the wxWidgets library, the wxXtra library, and the "CDraw"
39 library and any other such library that any version of Xara LX relased
40 by Xara Group Ltd requires in order to compile and execute, including
41 the static linking of that library to XaraLX. In the case of the
42 "CDraw" library, you may satisfy obligation under the GNU General Public
43 License to provide source code by providing a binary copy of the library
44 concerned and a copy of the license accompanying it.
46 Nothing in this section restricts any of the rights you have under
47 the GNU General Public License.
53 This license applies to this program (XaraLX) and its constituent source
54 files only, and does not necessarily apply to other Xara products which may
55 in part share the same code base, and are subject to their own licensing
58 This license does not apply to files in the wxXtra directory, which
59 are built into a separate library, and are subject to the wxWindows
60 license contained within that directory in the file "WXXTRA-LICENSE".
62 This license does not apply to the binary libraries (if any) within
63 the "libs" directory, which are subject to a separate license contained
64 within that directory in the file "LIBS-LICENSE".
67 ARRANGEMENTS FOR CONTRIBUTION OF MODIFICATIONS
68 ----------------------------------------------
70 Subject to the terms of the GNU Public License (see above), you are
71 free to do whatever you like with your modifications. However, you may
72 (at your option) wish contribute them to Xara's source tree. You can
73 find details of how to do this at:
74 http://www.xaraxtreme.org/developers/
76 Prior to contributing your modifications, you will need to complete our
77 contributor agreement. This can be found at:
78 http://www.xaraxtreme.org/developers/contribute/
80 Please note that Xara will not accept modifications which modify any of
81 the text between the start and end of this header (marked
82 XARAHEADERSTART and XARAHEADEREND).
88 Xara, Xara LX, Xara X, Xara X/Xtreme, Xara Xtreme, the Xtreme and Xara
89 designs are registered or unregistered trademarks, design-marks, and/or
90 service marks of Xara Group Ltd. All rights in these marks are reserved.
93 Xara Group Ltd, Gaddesden Place, Hemel Hempstead, HP2 6EX, UK.
96 =================================XARAHEADEREND============================
98 /*************************************************************************************
101 Author: Justin_Flude (Xara Group Ltd) <camelotdev@xara.com>
103 Definition of the member function StringBase::MakeMsg(), which extends the traditional
104 sprintf() function to cope with arbitrarily ordered format specifications.
105 **************************************************************************************/
107 #include "camtypes.h"
108 //#include "basestr.h" - in camtypes.h [AUTOMATICALLY REMOVED]
109 //#include "ensure.h" - in camtypes.h [AUTOMATICALLY REMOVED]
112 /**************************************************************************************
113 > INT32 __cdecl StringBase::MakeMsg(UINT32 resourceID ...)
115 Author: Justin_Flude (Xara Group Ltd) <camelotdev@xara.com>
116 Created: 22nd April 1993
117 Inputs: A string resource identifier, and a variable number of other parameters,
118 which must match the format specifiers embedded in the string resource.
119 Note that floating point parameters are NOT supported.
120 Outputs: Changes "this" to become the formatted string, with the passed parameters
121 substituted into the string resource in the correct order.
122 Returns: The number of characters in the formatted string. This will be zero if
123 a problem occurs, such as not being able to load the format string
125 Purpose: Internationally portable version of sprintf(...), eg.
128 s.MakeMsg(_R(IDS_COORDFORMAT), x, y, z);
129 TextOut(hdc, 20, 20, s, s.Length());
130 Errors: ENSURE failure if called for a String that hasn't been allocated.
131 ***************************************************************************************/
132 INT32 __cdecl
StringBase::MakeMsg(UINT32 resID
...)
134 /* if (IsUserName("JustinF"))
135 TRACE( _T("MakeMsg called with resource ID %u\n"), resID);
137 ENSURE(text
, "Call to StringBase::MakeMsg for unallocated String");
144 if (s
.Alloc(MAX_STRING_RES_LENGTH
) && s
.Load(resID
))
146 n
= CCvsprintf(s
.text
, ap
);
151 if (IsUserName("JustinF"))
152 TRACE( _T("Failed to allocate or load the format string in MakeMsg()\n"));
158 #if !defined(EXCLUDE_FROM_RALPH) && !defined(EXCLUDE_FROM_XARALX)
159 // Remember the resource ID, in case the help system can't work out the message ID.
160 SetNextMsgHelpContext(resID
);
163 // Return the number of characters in the formatted string.
170 /**************************************************************************************
171 > INT32 __cdecl StringBase::_MakeMsg(const TCHAR* fmt ...)
173 Author: Justin_Flude (Xara Group Ltd) <camelotdev@xara.com>
174 Created: 22nd April 1993
175 Inputs: A constant character pointer, and a variable number of other parameters,
176 which must match the format specifiers embedded in the character pointer.
177 **** Note that floating point parameters are NOT supported ****
178 Outputs: Changes "this" to become the formatted string, with the passed parameters
179 substituted into the string resource in the correct order.
180 Returns: The number of characters in the formatted string.
181 Purpose: Internationally portable version of sprintf(...), eg.
183 s._MakeMsg("#1%lu", x);
184 TextOut(hdc, 20, 20, s, s.Length());
185 ***************************************************************************************/
186 INT32 __cdecl
StringBase::_MakeMsg(const TCHAR
* fmt
...)
188 /* if (IsUserName("JustinF"))
189 TRACE( _T("_MakeMsg called with format string %s\n"), fmt);
191 ENSURE(text
, "Call to StringBase::_MakeMsg for an unallocated String");
192 ENSURE(fmt
, "Call to StringBase::_MakeMsg with a null format parameter");
197 INT32 n
= CCvsprintf(fmt
, ap
);
205 /////////////////////////////////////////////////////////////////////////////////////////
207 // I M P L E M E N T A T I O N
209 // The types that can appear in a formatting string. These have been documented,
210 // but it's worth pointing out that the wsprintf(...) function will accept other
211 // %specifiers, eg. %x, which are not documented.
215 LITERAL
, CHAR_ARG
, SIGNED_INT_ARG
, SIGNED_INT32_ARG
, UNSIGNED_INT_ARG
,
216 UNSIGNED_INT32_ARG
, CHAR_POINTER_ARG
, STRING_POINTER_ARG
, UINT_PTR_ARG
221 // Private helper data structure - a linked list of strings & associated
225 TCHAR
* str
; // pointer to literal / format specifier
226 ArgType type
; // type of associated parameter
227 INT32 pos
; // relative position in format string
228 Item
* next
; // next in list
233 Item(const TCHAR
* s
, INT32 ps
, ArgType t
);
238 Item
* Item::head
= 0;
239 Item
* Item::tail
= 0;
242 // Create & join an Item.
243 Item::Item(const TCHAR
* s
, INT32 ps
, ArgType t
)
245 camStrcpy(str
= new TCHAR
[camStrlen(s
) + 1], s
);
249 if (tail
) tail
= tail
->next
= this;
250 else head
= tail
= this;
256 // Destroy an Item - tail recursively deletes every Item, if called by
257 // delete Item::head;
261 if (next
) delete next
;
262 else head
= tail
= 0;
268 /**************************************************************************************
269 > INT32 StringBase::BuildList(const TCHAR* format)
271 Author: Justin_Flude (Xara Group Ltd) <camelotdev@xara.com>
272 Created: 22nd April 1993
273 Inputs: A pointer to the format string to be analysed.
274 Returns: The number of format specifiers in the string, which should be the same
275 as the number of arguments to be formatted.
276 Purpose: This private helper function scans the layout string, breaking it up into
277 literal text and parameters, which are placed on an private list in the
278 order they are encountered, together with their type and which argument
279 passed to Format that they refer to. The formatting must be done in two
280 passes as the order of the parameters is not known until the whole format
281 string has been scanned.
283 ***************************************************************************************/
284 INT32
StringBase::BuildList(const TCHAR
* format
)
286 /* if (IsUserName("JustinF"))
287 TRACE( _T("\tBuildList called with format string %s\n"), format);
289 ENSURE(format
, "Null parameter passed to StringBase::BuildList");
290 const TCHAR
* start
= format
;
294 const TCHAR
* next
= start
;
295 // Look for a format specifier in the layout string
296 if (*next
== TEXT('#'))
297 if (IsNumeric(*(++next
)))
299 INT32 ArgPos
= *next
- TEXT('0'); // MS approved method - yuck!!!
300 ENSURE(ArgPos
>= 1 && ArgPos
<= 9,
301 "Illegal format string passed to MakeMsg!");
303 if (*(++next
) == TEXT('%'))
305 // Seem to have found the beginning of a specifier,
306 // so try parsing it, to extract the type information
307 if (*(++next
) == TEXT('-')) next
++;
308 if (*next
== TEXT('#')) next
++;
309 if (*next
== TEXT('0')) next
++;
310 while (IsNumeric(*next
)) next
++;
311 if (*next
== TEXT('.'))
312 while (IsNumeric(*++next
));
314 // Ok, we have skipped the width, precision etc. and
315 // "next" should now be pointing at the type character(s)
317 if (*next
== TEXT('l')) // an INT32 something or another?
320 case TEXT('d'): case TEXT('i'):
321 kind
= SIGNED_INT32_ARG
;
323 case TEXT('u'): case TEXT('x'): case TEXT('X'):
324 kind
= UNSIGNED_INT32_ARG
;
326 default: // false alarm, can't be a specifier
331 switch (*next
) // single type character
336 case TEXT('d'): case TEXT('i'):
337 kind
= SIGNED_INT_ARG
;
339 case TEXT('p'): // a pointer
342 case TEXT('u'): case TEXT('x'): case TEXT('X'):
343 kind
= UNSIGNED_INT_ARG
;
346 kind
= CHAR_POINTER_ARG
;
349 kind
= STRING_POINTER_ARG
;
353 goto not_format
; // boo hiss hooray!
356 // Successfully parsed the format specifier, so add
357 // it to the Item list. Skip the leading #n of the specifier,
358 // so (next) now points to the character following the '%X'
362 // Extract the specifier.
364 INT32 len
= next
- start
- 2;
365 camStrncpy(temp
, start
+ 2, len
);
367 new Item(temp
, ArgPos
, kind
);
371 /* if (IsUserName("JustinF"))
373 TRACE( _T("\tItem: (%d) at #%d %s\n"), kind, ArgPos, temp);
374 TRACE( _T("\t\tRemaining: %s\n"), start);
380 // Put back the previous character
385 // Scan a literal up to the next '#' or null, whichever is sooner
386 while (*next
&& *next
!= TEXT('#')) next
++;
389 INT32 len
= next
- start
;
391 camStrncpy(temp
, start
, len
);
393 new Item(temp
, 0, LITERAL
);
396 /* if (IsUserName("JustinF"))
398 TRACE( _T("\tLiteral: %s\n"), temp);
409 /**************************************************************************************
410 > INT32 StringBase::CCvsprintf(const TCHAR* layout, va_list va)
412 Author: Justin_Flude (Xara Group Ltd) <camelotdev@xara.com>
413 Created: 22nd April 1993
414 Inputs: A pointer to the format string, a variable-parameter pointer to the
415 arguments to be formatted.
416 Returns: The length of the StringBase once the arguments have been substituted into
418 Purpose: This private helper function takes the list generated by
419 StringBase::BuildList and scans through it, formatting stack-frame
420 parameters according to their list entry. This produces another list,
421 this time of formatted parameters, which is then concatenated to
422 produce the final output.
424 ***************************************************************************************/
425 INT32
StringBase::CCvsprintf(const TCHAR
* layout
, va_list va
)
427 /* if (IsUserName("JustinF"))
428 TRACE( _T("CCvsprintf called with format string %s\n"), layout);
430 INT32 n
= BuildList(layout
);
431 for (INT32 i
= 1; i
<= n
; i
++)
433 for (Item
* p
= Item::head
; p
; p
= p
->next
)
437 { // Found specifier i in the list, so grab from the stack
438 // and call wsprintf() to convert to text. The converted
439 // text is put in the temp[] buffer - note the enormous
440 // size of this, in accordance with the C standard.
444 // type 'c' - single character
446 #if defined(__WXMSW__)
447 camSnprintf( temp
, 512, p
->str
, va_arg(va
, TCHAR
) );
448 #else // TCHARs are promoted to INT32 when passed via ... under GCC
449 camSnprintf( temp
, 512, p
->str
, va_arg(va
, INT32
) );
452 // type 'd' / 'i' - signed decimal integer
454 camSnprintf(temp
, 512, p
->str
, va_arg(va
, INT32
));
456 // type 'ld' / 'li' - signed decimal INT32
457 case SIGNED_INT32_ARG
:
458 camSnprintf(temp
, 512, p
->str
, va_arg(va
, INT32
));
460 // type 'u' - unsigned decimal integer
461 case UNSIGNED_INT_ARG
:
462 camSnprintf(temp
, 512, p
->str
, va_arg(va
, UINT32
));
464 // type 'lu' / 'lx' / 'lX' - unsigned decimal INT32
465 case UNSIGNED_INT32_ARG
:
466 camSnprintf(temp
, 512, p
->str
, va_arg(va
, UINT32
));
468 // type 's' - long pointer to array of (constant) char
469 case CHAR_POINTER_ARG
:
470 camSnprintf(temp
, 512, p
->str
, va_arg(va
, LPCTSTR
));
472 // type 'p' a pointer
474 camSnprintf(temp
, 512, p
->str
, va_arg(va
, UINT_PTR
));
476 // type 'S' - a pointer to a StringBase. First change the
477 // %S format specifier, which is our own, into a sprintf()
478 // compatible %s. Note that the 'S' is always the last
479 // character of the format specifier.
480 case STRING_POINTER_ARG
:
481 (p
->str
)[camStrlen(p
->str
) - 1] = TEXT('s');
482 camSnprintf(temp
, 512, p
->str
, LPCTSTR(va_arg(va
, const StringBase
*)->text
));
487 // Replace the format specifier with the formatted text
489 camStrcpy(p
->str
= new TCHAR
[camStrlen(temp
) + 1], temp
);
494 // Concatenate every string in the list, tidy up, and return in "this" string
498 for (Item
* p
= Item::head
; (p
!= NULL
&& NextChar
< length
) ; p
= p
->next
)
500 /* if (camStrlen(text) + camStrlen(p->str) < length)
501 camStrcat(text, p->str);
504 ENSURE(FALSE, "Call to String::MakeMsg will overflow - text has been truncated");
507 for (Index
= 0; p
->str
[Index
] != 0 && NextChar
< length
; Index
++)
508 text
[NextChar
++] = p
->str
[Index
];
511 if (NextChar
> length
)
513 text
[NextChar
] = TCHAR('\0');
516 return camStrlen(text
);