makemsg.cpp

Go to the documentation of this file.
00001 // $Id: makemsg.cpp 1333 2006-06-16 20:34:16Z alex $
00002 /* @@tag:xara-cn@@ DO NOT MODIFY THIS LINE
00003 ================================XARAHEADERSTART===========================
00004  
00005                Xara LX, a vector drawing and manipulation program.
00006                     Copyright (C) 1993-2006 Xara Group Ltd.
00007        Copyright on certain contributions may be held in joint with their
00008               respective authors. See AUTHORS file for details.
00009 
00010 LICENSE TO USE AND MODIFY SOFTWARE
00011 ----------------------------------
00012 
00013 This file is part of Xara LX.
00014 
00015 Xara LX is free software; you can redistribute it and/or modify it
00016 under the terms of the GNU General Public License version 2 as published
00017 by the Free Software Foundation.
00018 
00019 Xara LX and its component source files are distributed in the hope
00020 that it will be useful, but WITHOUT ANY WARRANTY; without even the
00021 implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
00022 See the GNU General Public License for more details.
00023 
00024 You should have received a copy of the GNU General Public License along
00025 with Xara LX (see the file GPL in the root directory of the
00026 distribution); if not, write to the Free Software Foundation, Inc., 51
00027 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
00028 
00029 
00030 ADDITIONAL RIGHTS
00031 -----------------
00032 
00033 Conditional upon your continuing compliance with the GNU General Public
00034 License described above, Xara Group Ltd grants to you certain additional
00035 rights. 
00036 
00037 The additional rights are to use, modify, and distribute the software
00038 together with the wxWidgets library, the wxXtra library, and the "CDraw"
00039 library and any other such library that any version of Xara LX relased
00040 by Xara Group Ltd requires in order to compile and execute, including
00041 the static linking of that library to XaraLX. In the case of the
00042 "CDraw" library, you may satisfy obligation under the GNU General Public
00043 License to provide source code by providing a binary copy of the library
00044 concerned and a copy of the license accompanying it.
00045 
00046 Nothing in this section restricts any of the rights you have under
00047 the GNU General Public License.
00048 
00049 
00050 SCOPE OF LICENSE
00051 ----------------
00052 
00053 This license applies to this program (XaraLX) and its constituent source
00054 files only, and does not necessarily apply to other Xara products which may
00055 in part share the same code base, and are subject to their own licensing
00056 terms.
00057 
00058 This license does not apply to files in the wxXtra directory, which
00059 are built into a separate library, and are subject to the wxWindows
00060 license contained within that directory in the file "WXXTRA-LICENSE".
00061 
00062 This license does not apply to the binary libraries (if any) within
00063 the "libs" directory, which are subject to a separate license contained
00064 within that directory in the file "LIBS-LICENSE".
00065 
00066 
00067 ARRANGEMENTS FOR CONTRIBUTION OF MODIFICATIONS
00068 ----------------------------------------------
00069 
00070 Subject to the terms of the GNU Public License (see above), you are
00071 free to do whatever you like with your modifications. However, you may
00072 (at your option) wish contribute them to Xara's source tree. You can
00073 find details of how to do this at:
00074   http://www.xaraxtreme.org/developers/
00075 
00076 Prior to contributing your modifications, you will need to complete our
00077 contributor agreement. This can be found at:
00078   http://www.xaraxtreme.org/developers/contribute/
00079 
00080 Please note that Xara will not accept modifications which modify any of
00081 the text between the start and end of this header (marked
00082 XARAHEADERSTART and XARAHEADEREND).
00083 
00084 
00085 MARKS
00086 -----
00087 
00088 Xara, Xara LX, Xara X, Xara X/Xtreme, Xara Xtreme, the Xtreme and Xara
00089 designs are registered or unregistered trademarks, design-marks, and/or
00090 service marks of Xara Group Ltd. All rights in these marks are reserved.
00091 
00092 
00093       Xara Group Ltd, Gaddesden Place, Hemel Hempstead, HP2 6EX, UK.
00094                         http://www.xara.com/
00095 
00096 =================================XARAHEADEREND============================
00097  */
00098 /*************************************************************************************
00099     
00100     
00101     Author: Justin_Flude (Xara Group Ltd) <camelotdev@xara.com>
00102     
00103     Definition of the member function StringBase::MakeMsg(), which extends the traditional
00104     sprintf() function to cope with arbitrarily ordered format specifications.
00105 **************************************************************************************/
00106 
00107 #include "camtypes.h"
00108 //#include "basestr.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00109 //#include "ensure.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00110 
00111 
00112 /**************************************************************************************
00113 >   INT32 __cdecl StringBase::MakeMsg(UINT32 resourceID ...)
00114 
00115     Author:     Justin_Flude (Xara Group Ltd) <camelotdev@xara.com>
00116     Created:    22nd April 1993
00117     Inputs:     A string resource identifier, and a variable number of other parameters,
00118                 which must match the format specifiers embedded in the string resource.
00119                 Note that floating point parameters are NOT supported.
00120     Outputs:    Changes "this" to become the formatted string, with the passed parameters
00121                 substituted into the string resource in the correct order.
00122     Returns:    The number of characters in the formatted string.  This will be zero if
00123                 a problem occurs, such as not being able to load the format string
00124                 resource.
00125     Purpose:    Internationally portable version of sprintf(...), eg.
00126                     StringBase s;
00127                     INT32 x, y, z;
00128                     s.MakeMsg(_R(IDS_COORDFORMAT), x, y, z);
00129                     TextOut(hdc, 20, 20, s, s.Length());
00130     Errors:     ENSURE failure if called for a String that hasn't been allocated.
00131 ***************************************************************************************/
00132 INT32 __cdecl StringBase::MakeMsg(UINT32 resID ...)
00133 {
00134 /*  if (IsUserName("JustinF"))
00135         TRACE( _T("MakeMsg called with resource ID %u\n"), resID);
00136 */
00137     ENSURE(text, "Call to StringBase::MakeMsg for unallocated String");
00138 
00139     va_list ap;
00140     va_start(ap, resID);
00141     
00142     INT32 n = 0;
00143     StringBase s;
00144     if (s.Alloc(MAX_STRING_RES_LENGTH) && s.Load(resID))
00145     {
00146         n = CCvsprintf(s.text, ap);
00147     }
00148 #ifdef _DEBUG
00149 /*  else
00150     {
00151         if (IsUserName("JustinF"))
00152             TRACE( _T("Failed to allocate or load the format string in MakeMsg()\n"));
00153     } */
00154 #endif
00155     
00156     va_end(ap);
00157     
00158 #if !defined(EXCLUDE_FROM_RALPH) && !defined(EXCLUDE_FROM_XARALX)
00159     // Remember the resource ID, in case the help system can't work out the message ID.
00160     SetNextMsgHelpContext(resID);
00161 #endif
00162     
00163     // Return the number of characters in the formatted string.
00164     return n;
00165 }
00166 
00167 
00168     
00169     
00170 /**************************************************************************************
00171 >   INT32 __cdecl StringBase::_MakeMsg(const TCHAR* fmt ...)
00172 
00173     Author:     Justin_Flude (Xara Group Ltd) <camelotdev@xara.com>
00174     Created:    22nd April 1993
00175     Inputs:     A constant character pointer, and a variable number of other parameters,
00176                 which must match the format specifiers embedded in the character pointer.
00177                 **** Note that floating point parameters are NOT supported ****
00178     Outputs:    Changes "this" to become the formatted string, with the passed parameters
00179                 substituted into the string resource in the correct order.
00180     Returns:    The number of characters in the formatted string.
00181     Purpose:    Internationally portable version of sprintf(...), eg.                   
00182                     UINT32 x;
00183                     s._MakeMsg("#1%lu", x);
00184                     TextOut(hdc, 20, 20, s, s.Length());
00185 ***************************************************************************************/
00186 INT32 __cdecl StringBase::_MakeMsg(const TCHAR* fmt ...)
00187 {
00188 /*  if (IsUserName("JustinF"))
00189         TRACE( _T("_MakeMsg called with format string %s\n"), fmt);
00190 */
00191     ENSURE(text, "Call to StringBase::_MakeMsg for an unallocated String");
00192     ENSURE(fmt, "Call to StringBase::_MakeMsg with a null format parameter");
00193     
00194     va_list ap;
00195     va_start(ap, fmt);
00196     
00197     INT32 n = CCvsprintf(fmt, ap);
00198     
00199     va_end(ap);
00200     return n;   
00201 }
00202 
00203 
00204 
00206 //
00207 // I M P L E M E N T A T I O N
00208 //
00209 // The types that can appear in a formatting string.  These have been documented,
00210 // but it's worth pointing out that the wsprintf(...) function will accept other
00211 // %specifiers, eg. %x, which are not documented.
00212 
00213 enum ArgType
00214 {
00215     LITERAL, CHAR_ARG, SIGNED_INT_ARG, SIGNED_INT32_ARG, UNSIGNED_INT_ARG,
00216     UNSIGNED_INT32_ARG, CHAR_POINTER_ARG, STRING_POINTER_ARG, UINT_PTR_ARG
00217 };
00218 
00219 
00220 
00221 // Private helper data structure - a linked list of strings & associated
00222 // type information.
00223 struct Item
00224 {
00225     TCHAR* str;         // pointer to literal / format specifier
00226     ArgType type;       // type of associated parameter
00227     INT32 pos;          // relative position in format string
00228     Item* next;         // next in list
00229         
00230     static Item* head;
00231     static Item* tail;
00232     
00233     Item(const TCHAR* s, INT32 ps, ArgType t);
00234     ~Item();
00235 };
00236     
00237     
00238 Item* Item::head = 0;
00239 Item* Item::tail = 0;
00240 
00241 
00242 // Create & join an Item.
00243 Item::Item(const TCHAR* s, INT32 ps, ArgType t)
00244 {
00245     camStrcpy(str = new TCHAR[camStrlen(s) + 1], s);
00246     pos = ps;
00247     type = t;
00248     next = 0;
00249     if (tail) tail = tail->next = this;
00250         else head = tail = this;
00251 }
00252 
00253 
00254     
00255     
00256 // Destroy an Item - tail recursively deletes every Item, if called by
00257 // delete Item::head;
00258 Item::~Item()
00259 {
00260     delete[] str;
00261     if (next) delete next;
00262         else head = tail = 0;
00263 }
00264 
00265 
00266     
00267     
00268 /**************************************************************************************
00269 >   INT32 StringBase::BuildList(const TCHAR* format)
00270 
00271     Author:     Justin_Flude (Xara Group Ltd) <camelotdev@xara.com>
00272     Created:    22nd April 1993
00273     Inputs:     A pointer to the format string to be analysed.
00274     Returns:    The number of format specifiers in the string, which should be the same
00275                 as the number of arguments to be formatted.
00276     Purpose:    This private helper function scans the layout string, breaking it up into
00277                 literal text and parameters, which are placed on an private list in the
00278                 order they are encountered, together with their type and which argument
00279                 passed to Format that they refer to.  The formatting must be done in two
00280                 passes as the order of the parameters is not known until the whole format
00281                 string has been scanned.
00282     Scope:      Private
00283 ***************************************************************************************/
00284 INT32 StringBase::BuildList(const TCHAR* format)
00285 {
00286 /*  if (IsUserName("JustinF"))
00287         TRACE( _T("\tBuildList called with format string %s\n"), format);
00288 */
00289     ENSURE(format, "Null parameter passed to StringBase::BuildList");
00290     const TCHAR* start = format;
00291     INT32 nArgs = 0;    
00292     while (*start)
00293     {
00294         const TCHAR* next = start;      
00295         // Look for a format specifier in the layout string
00296         if (*next == TEXT('#'))
00297             if (IsNumeric(*(++next)))
00298             {
00299                 INT32 ArgPos = *next - TEXT('0');       // MS approved method - yuck!!!
00300                 ENSURE(ArgPos >= 1 && ArgPos <= 9,
00301                         "Illegal format string passed to MakeMsg!");
00302 
00303                 if (*(++next) == TEXT('%'))
00304                 {
00305                     // Seem to have found the beginning of a specifier,
00306                     // so try parsing it, to extract the type information
00307                     if (*(++next) == TEXT('-')) next++;
00308                     if (*next == TEXT('#')) next++;
00309                     if (*next == TEXT('0')) next++;
00310                     while (IsNumeric(*next)) next++;
00311                     if (*next == TEXT('.'))
00312                         while (IsNumeric(*++next));
00313                     
00314                     // Ok, we have skipped the width, precision etc. and
00315                     // "next" should now be pointing at the type character(s)
00316                     ArgType kind;
00317                     if (*next == TEXT('l'))     // an INT32 something or another?
00318                         switch (*++next)
00319                         {
00320                             case TEXT('d'):  case TEXT('i'):
00321                                 kind = SIGNED_INT32_ARG;
00322                                 break;
00323                             case TEXT('u'):  case TEXT('x'):  case TEXT('X'):
00324                                 kind = UNSIGNED_INT32_ARG;
00325                                 break;
00326                             default:            // false alarm, can't be a specifier
00327                                 next = start + 1;
00328                                 goto not_format;
00329                         }
00330                     else
00331                         switch (*next)          // single type character
00332                         {    
00333                             case TEXT('c'):
00334                                 kind = CHAR_ARG;
00335                                 break;
00336                             case TEXT('d'):  case TEXT('i'):
00337                                 kind = SIGNED_INT_ARG;
00338                                 break;
00339                             case TEXT('p'): // a pointer
00340                                 kind = UINT_PTR_ARG;
00341                                 break;
00342                             case TEXT('u'): case TEXT('x'): case TEXT('X'):
00343                                 kind = UNSIGNED_INT_ARG;
00344                                 break;
00345                             case TEXT('s'):
00346                                 kind = CHAR_POINTER_ARG;
00347                                 break;
00348                             case TEXT('S'):
00349                                 kind = STRING_POINTER_ARG;                              
00350                                 break;
00351                             default:
00352                                 next = start + 1;
00353                                 goto not_format;            // boo hiss hooray!
00354                         }
00355                     
00356                     // Successfully parsed the format specifier, so add
00357                     // it to the Item list.  Skip the leading #n of the specifier,
00358                     // so (next) now points to the character following the '%X'
00359                     // format specifier.
00360                     next++;
00361 
00362                     // Extract the specifier.
00363                     TCHAR temp[64];
00364                     INT32 len = next - start - 2;
00365                     camStrncpy(temp, start + 2, len);
00366                     temp[len] = 0;
00367                     new Item(temp, ArgPos, kind);
00368                     start = next;
00369                     nArgs++;
00370                     
00371 /*                  if (IsUserName("JustinF"))
00372                     {
00373                         TRACE( _T("\tItem: (%d) at #%d  %s\n"), kind, ArgPos, temp);
00374                         TRACE( _T("\t\tRemaining: %s\n"), start);
00375                     }
00376 */
00377                     continue;
00378                 }
00379                 else
00380                     // Put back the previous character
00381                     next--;
00382             }
00383         
00384 not_format:     
00385         // Scan a literal up to the next '#' or null, whichever is sooner
00386         while (*next && *next != TEXT('#')) next++;
00387 
00388         TCHAR temp[256];
00389         INT32 len = next - start;
00390 
00391         camStrncpy(temp, start, len);
00392         temp[len] = 0;
00393         new Item(temp, 0, LITERAL);
00394         start = next;
00395 
00396 /*      if (IsUserName("JustinF"))
00397         {
00398             TRACE( _T("\tLiteral: %s\n"), temp);
00399         }
00400 */
00401     }
00402 
00403     return nArgs;       
00404 }
00405 
00406 
00407     
00408 
00409 /**************************************************************************************
00410 >   INT32 StringBase::CCvsprintf(const TCHAR* layout, va_list va)
00411 
00412     Author:     Justin_Flude (Xara Group Ltd) <camelotdev@xara.com>
00413     Created:    22nd April 1993
00414     Inputs:     A pointer to the format string, a variable-parameter pointer to the
00415                 arguments to be formatted.
00416     Returns:    The length of the StringBase once the arguments have been substituted into
00417                 it.
00418     Purpose:    This private helper function takes the list generated by
00419                 StringBase::BuildList and scans through it, formatting stack-frame
00420                 parameters according to their list entry.  This produces another list,
00421                 this time of formatted parameters, which is then concatenated to
00422                 produce the final output.
00423     Scope:      Private
00424 ***************************************************************************************/
00425 INT32 StringBase::CCvsprintf(const TCHAR* layout, va_list va)
00426 {
00427 /*  if (IsUserName("JustinF"))
00428         TRACE( _T("CCvsprintf called with format string %s\n"), layout);
00429 */
00430     INT32 n = BuildList(layout);
00431     for (INT32 i = 1; i <= n; i++)
00432     {
00433         for (Item* p = Item::head; p; p = p->next)
00434             if (p->pos != i)
00435                 continue;
00436             else
00437             {   // Found specifier i in the list, so grab from the stack
00438                 // and call wsprintf() to convert to text.  The converted
00439                 // text is put in the temp[] buffer - note the enormous
00440                 // size of this, in accordance with the C standard.
00441                 TCHAR temp[512];
00442                 switch (p->type)
00443                 {
00444                     // type 'c' - single character
00445                     case CHAR_ARG:
00446 #if defined(__WXMSW__)
00447                         camSnprintf( temp, 512, p->str, va_arg(va, TCHAR) );
00448 #else                   // TCHARs are promoted to INT32 when passed via ... under GCC
00449                         camSnprintf( temp, 512, p->str, va_arg(va, INT32) );
00450 #endif
00451                         break;
00452                     // type 'd' / 'i' - signed decimal integer
00453                     case SIGNED_INT_ARG:
00454                         camSnprintf(temp, 512, p->str, va_arg(va, INT32));
00455                         break;
00456                     // type 'ld' / 'li' - signed decimal INT32
00457                     case SIGNED_INT32_ARG:
00458                         camSnprintf(temp, 512, p->str, va_arg(va, INT32));
00459                         break;
00460                     // type 'u' - unsigned decimal integer
00461                     case UNSIGNED_INT_ARG:
00462                         camSnprintf(temp, 512, p->str, va_arg(va, UINT32));
00463                         break;
00464                     // type 'lu' / 'lx' / 'lX' - unsigned decimal INT32
00465                     case UNSIGNED_INT32_ARG:
00466                         camSnprintf(temp, 512, p->str, va_arg(va, UINT32));
00467                         break;
00468                     // type 's' - long pointer to array of (constant) char
00469                     case CHAR_POINTER_ARG:
00470                         camSnprintf(temp, 512, p->str, va_arg(va, LPCTSTR));
00471                         break;
00472                     // type 'p' a pointer
00473                     case UINT_PTR_ARG:
00474                         camSnprintf(temp, 512, p->str, va_arg(va, UINT_PTR));
00475                         break;
00476                     // type 'S' - a pointer to a StringBase.  First change the
00477                     // %S format specifier, which is our own, into a sprintf()
00478                     // compatible %s.  Note that the 'S' is always the last
00479                     // character of the format specifier.
00480                     case STRING_POINTER_ARG:
00481                         (p->str)[camStrlen(p->str) - 1] = TEXT('s');
00482                         camSnprintf(temp, 512, p->str, LPCTSTR(va_arg(va, const StringBase*)->text));
00483                         break;
00484                     default:
00485                         break;
00486                 }
00487                 // Replace the format specifier with the formatted text
00488                 delete[] p->str;
00489                 camStrcpy(p->str = new TCHAR[camStrlen(temp) + 1], temp);
00490                 break;
00491             }
00492     }
00493     
00494     // Concatenate every string in the list, tidy up, and return in "this" string
00495     *text = 0;
00496     UINT32 NextChar = 0;
00497     INT32 Index = 0;
00498     for (Item* p = Item::head; (p != NULL && NextChar < length) ; p = p->next)
00499     {
00500 /*      if (camStrlen(text) + camStrlen(p->str) < length)
00501             camStrcat(text, p->str);
00502         else
00503         {
00504             ENSURE(FALSE, "Call to String::MakeMsg will overflow - text has been truncated");
00505         }
00506 */
00507         for (Index = 0; p->str[Index] != 0 && NextChar < length; Index++)
00508             text[NextChar++] = p->str[Index];
00509     }
00510 
00511     if (NextChar > length)
00512         NextChar = length;
00513     text[NextChar] = TCHAR('\0');
00514 
00515     delete Item::head;
00516     return camStrlen(text);
00517 }

Generated on Sat Nov 10 03:48:38 2007 for Camelot by  doxygen 1.4.4