home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Windows Gam…ming Gurus (2nd Edition) / Disc2.iso / vc98 / crt / src / setlocal.c < prev    next >
C/C++ Source or Header  |  1998-06-17  |  17KB  |  555 lines

  1. /***
  2. *setlocal.c - Contains the setlocale function
  3. *
  4. *       Copyright (c) 1988-1998, Microsoft Corporation.  All rights reserved.
  5. *
  6. *Purpose:
  7. *       Contains the setlocale() function.
  8. *
  9. *******************************************************************************/
  10.  
  11. #include <locale.h>
  12.  
  13. #if !defined (_WIN32)
  14.  
  15. static char _clocalestr[] = "C";
  16.  
  17. #else  /* !defined (_WIN32) */
  18.  
  19. #include <cruntime.h>
  20. #include <mtdll.h>
  21. #include <malloc.h>
  22. #include <string.h>
  23. #include <stdarg.h>
  24. #include <stdlib.h> /* for strtol */
  25. #include <setlocal.h>
  26. #include <dbgint.h>
  27.  
  28. /* C locale */
  29. static char _clocalestr[] = "C";
  30.  
  31.  
  32.  
  33. __declspec(selectany) struct {
  34.         const char * catname;
  35.         char * locale;
  36.         int (* init)(void);
  37. } __lc_category[LC_MAX-LC_MIN+1] = {
  38.         /* code assumes locale initialization is "_clocalestr" */
  39.         { "LC_ALL", NULL,       __init_dummy /* never called */ },
  40.         { "LC_COLLATE", _clocalestr,    __init_collate  },
  41.         { "LC_CTYPE",   _clocalestr,    __init_ctype    },
  42.         { "LC_MONETARY",_clocalestr,    __init_monetary },
  43.         { "LC_NUMERIC", _clocalestr,    __init_numeric  },
  44.         { "LC_TIME",    _clocalestr,    __init_time }
  45. };
  46.  
  47. #ifdef _MT
  48. /*
  49.  * Flag indicating whether or not setlocale() is active. Its value is the
  50.  * number of setlocale() calls currently active.
  51.  */
  52. _CRTIMP int __setlc_active;
  53.  
  54. /*
  55.  * Flag indicating whether or not a function which references the locale
  56.  * without having locked it is active. Its value is the number of such
  57.  * functions.
  58.  */
  59. _CRTIMP int __unguarded_readlc_active;
  60. #endif  /* _MT */
  61.  
  62. /* helper function prototypes */
  63. char * _expandlocale(char *, char *, LC_ID *, UINT *, int);
  64. void _strcats(char *, int, ...);
  65. void __lc_lctostr(char *, const LC_STRINGS *);
  66. int __lc_strtolc(LC_STRINGS *, const char *);
  67. static char * __cdecl _setlocale_set_cat(int, const char *);
  68. static char * __cdecl _setlocale_get_all(void);
  69.  
  70. #endif  /* !defined (_WIN32) */
  71.  
  72.  
  73. /***
  74. *char * setlocale(int category, char *locale) - Set one or all locale categories
  75. *
  76. *Purpose:
  77. *       The setlocale() routine allows the user to set one or more of
  78. *       the locale categories to the specific locale selected by the
  79. *       user.  [ANSI]
  80. *
  81. *       NOTE: Under !_INTL, the C libraries only support the "C" locale.
  82. *       Attempts to change the locale will fail.
  83. *
  84. *Entry:
  85. *       int category = One of the locale categories defined in locale.h
  86. *       char *locale = String identifying a specific locale or NULL to
  87. *                  query the current locale.
  88. *
  89. *Exit:
  90. *       If supplied locale pointer == NULL:
  91. *
  92. *           Return pointer to current locale string and do NOT change
  93. *           the current locale.
  94. *
  95. *       If supplied locale pointer != NULL:
  96. *
  97. *           If locale string is '\0', set locale to default.
  98. *
  99. *           If desired setting can be honored, return a pointer to the
  100. *           locale string for the appropriate category.
  101. *
  102. *           If desired setting can NOT be honored, return NULL.
  103. *
  104. *Exceptions:
  105. *       Compound locale strings of the form "LC_COLLATE=xxx;LC_CTYPE=xxx;..."
  106. *       are allowed for the LC_ALL category.  This is to support the ability
  107. *       to restore locales with the returned string, as specified by ANSI.
  108. *       Setting the locale with a compound locale string will succeed unless
  109. *       *all* categories failed.  The returned string will reflect the current
  110. *       locale.  For example, if LC_CTYPE fails in the above string, setlocale
  111. *       will return "LC_COLLATE=xxx;LC_CTYPE=yyy;..." where yyy is the
  112. *       previous locale (or the C locale if restoring the previous locale
  113. *       also failed).  Unrecognized LC_* categories are ignored.
  114. *
  115. *******************************************************************************/
  116. char * __cdecl setlocale (
  117.         int _category,
  118.         const char *_locale
  119.         )
  120. #if !defined (_WIN32)
  121. {
  122.         if ( (_locale == NULL) ||
  123.              (_locale[0] == '\0') ||
  124.              ( (_locale[0]=='C') && (_locale[1]=='\0'))  )
  125.             return(_clocalestr);
  126.         else
  127.             return(NULL);
  128. }
  129. #else  /* !defined (_WIN32) */
  130. {
  131.         char * retval;
  132. #ifdef _MT
  133.         int local_lock_flag;
  134. #endif  /* _MT */
  135.  
  136.         /* Validate category */
  137.         if ((_category < LC_MIN) || (_category > LC_MAX))
  138.             return NULL;
  139.  
  140. #ifdef _MT
  141.         /*
  142.          * Assert setlocale lock
  143.          */
  144.         _mlock( _SETLOCALE_LOCK );
  145.  
  146.         local_lock_flag = 1;
  147.  
  148.         /*
  149.          * Increment flag that indicates a setlocale is active.
  150.          */
  151.         __setlc_active++;
  152.  
  153.         /*
  154.          * Wait until all unguarded locale read operations have completed.
  155.          */
  156.         while ( __unguarded_readlc_active )
  157.             Sleep( 1 );
  158.  
  159. #endif  /* _MT */
  160.  
  161.         /* Interpret locale */
  162.  
  163.         if (_category != LC_ALL)
  164.         {
  165.             retval = (_locale) ? _setlocale_set_cat(_category,_locale) :
  166.                 __lc_category[_category].locale;
  167.  
  168.         } else { /* LC_ALL */
  169.             char lctemp[MAX_LC_LEN];
  170.             int i;
  171.             int same = 1;
  172.             int fLocaleSet = 0; /* flag to indicate if anything successfully set */
  173.  
  174.             if (_locale != NULL)
  175.             {
  176.                 if ( (_locale[0]=='L') && (_locale[1]=='C') && (_locale[2]=='_') )
  177.                 {
  178.                     /* parse compound locale string */
  179.                     size_t len;
  180.                     const char * p = _locale;  /* start of string to parse */
  181.                     const char * s;
  182.  
  183.                     do {
  184.                         s = strpbrk(p,"=;");
  185.  
  186.                         if ((s==(char *)NULL) || (!(len=s-p)) || (*s==';'))
  187.                         {
  188. #ifdef _MT
  189.                             if ( local_lock_flag ) {
  190.                                 _munlock( _SETLOCALE_LOCK );
  191.                                 __setlc_active--;
  192.                             }
  193. #endif  /* _MT */
  194.                             return NULL;  /* syntax error */
  195.                         }
  196.  
  197.                         /* match with known LC_ strings, if possible, else ignore */
  198.                         for (i=LC_ALL+1; i<=LC_MAX; i++)
  199.                         {
  200.                             if ((!strncmp(__lc_category[i].catname,p,len))
  201.                                 && (len==strlen(__lc_category[i].catname)))
  202.                             {
  203.                                 break;  /* matched i */
  204.                             }
  205.                         } /* no match if (i>LC_MAX) -- just ignore */
  206.  
  207.                         if ((!(len = strcspn(++s,";"))) && (*s!=';'))
  208.                         {
  209. #ifdef _MT
  210.                             if ( local_lock_flag ) {
  211.                                 _munlock( _SETLOCALE_LOCK );
  212.                                 __setlc_active--;
  213.                             }
  214. #endif  /* _MT */
  215.                             return NULL;  /* syntax error */
  216.                         }
  217.  
  218.                         if (i<=LC_MAX)
  219.                         {
  220.                             strncpy(lctemp, s, len);
  221.                             lctemp[len]='\0';   /* null terminate string */
  222.  
  223.                             /* don't fail unless all categories fail */
  224.                             if (_setlocale_set_cat(i,lctemp))
  225.                                 fLocaleSet++;       /* record a success */
  226.                         }
  227.                         if (*(p = s+len)!='\0')
  228.                             p++;  /* skip ';', if present */
  229.  
  230.                     } while (*p);
  231.  
  232.                     retval = (fLocaleSet) ? _setlocale_get_all() : NULL;
  233.  
  234.                 } else { /* simple LC_ALL locale string */
  235.  
  236.                     /* confirm locale is supported, get expanded locale */
  237.                     if (retval = _expandlocale((char *)_locale, lctemp, NULL, NULL, _category))
  238.                     {
  239.                         for (i=LC_MIN; i<=LC_MAX; i++)
  240.                         {
  241.                             if (i!=LC_ALL)
  242.                             {
  243.                                 if (strcmp(lctemp, __lc_category[i].locale))
  244.                                 {
  245.                                     if (_setlocale_set_cat(i, lctemp))
  246.                                     {
  247.                                         fLocaleSet++;   /* record a success */
  248.                                     }
  249.                                     else
  250.                                     {
  251.                                         same = 0;       /* record a failure */
  252.                                     }
  253.                                 }
  254.                                 else
  255.                                     fLocaleSet++;   /* trivial succcess */
  256.                             }
  257.                         }
  258.                         if (same) /* needn't call setlocale_get_all() if all the same */
  259.                         {
  260.                             retval = _setlocale_get_all();
  261.                             /* retval set above */
  262.                             _free_crt(__lc_category[LC_ALL].locale);
  263.                             __lc_category[LC_ALL].locale = NULL;
  264.                         }
  265.                         else
  266.                             retval = (fLocaleSet) ? _setlocale_get_all() : NULL;
  267.                     }
  268.                 }
  269.             } else { /* LC_ALL & NULL */
  270.                 retval = _setlocale_get_all ();
  271.             }
  272.         }
  273.  
  274.         /* common exit point */
  275. #ifdef _MT
  276.         if ( local_lock_flag ) {
  277.             _munlock( _SETLOCALE_LOCK );
  278.             __setlc_active--;
  279.         }
  280. #endif  /* _MT */
  281.         return retval;
  282.  
  283. } /* setlocale */
  284.  
  285.  
  286. static char * __cdecl _setlocale_set_cat (
  287.         int category,
  288.         const char * locale
  289.         )
  290. {
  291.         char * oldlocale;
  292.         LCID oldhandle;
  293.         UINT oldcodepage;
  294.         LC_ID oldid;
  295.  
  296.         LC_ID idtemp;
  297.         UINT cptemp;
  298.         char lctemp[MAX_LC_LEN];
  299.         char * pch;
  300.  
  301.         if (!_expandlocale((char *)locale, lctemp, &idtemp, &cptemp, category))
  302.         {
  303.             return NULL;            /* unrecognized locale */
  304.         }
  305.  
  306.         if (!(pch = (char *)_malloc_crt(strlen(lctemp)+1)))
  307.         {
  308.             return NULL;  /* error if malloc fails */
  309.         }
  310.  
  311.         oldlocale = __lc_category[category].locale; /* save for possible restore*/
  312.         oldhandle = __lc_handle[category];
  313.         memcpy((void *)&oldid, (void *)&__lc_id[category], sizeof(oldid));
  314.         oldcodepage = __lc_codepage;
  315.  
  316.         /* update locale string */
  317.         __lc_category[category].locale = strcpy(pch,lctemp);
  318.         __lc_handle[category] = MAKELCID(idtemp.wLanguage, SORT_DEFAULT);
  319.         memcpy((void *)&__lc_id[category], (void *)&idtemp, sizeof(idtemp));
  320.  
  321.         if (category==LC_CTYPE)
  322.         __lc_codepage = cptemp;
  323.  
  324.         if ( category == LC_COLLATE )
  325.             __lc_collate_cp = cptemp;
  326.  
  327.         if (__lc_category[category].init())
  328.         {
  329.             /* restore previous state! */
  330.             __lc_category[category].locale = oldlocale;
  331.             _free_crt(pch);
  332.             __lc_handle[category] = oldhandle;
  333.             __lc_codepage = oldcodepage;
  334.  
  335.             return NULL; /* error if non-zero return */
  336.         }
  337.  
  338.         /* locale set up successfully */
  339.         /* Cleanup */
  340.         if ((oldlocale != _clocalestr)
  341.             )
  342.             _free_crt(oldlocale);
  343.  
  344.         return __lc_category[category].locale;
  345.  
  346. } /* _setlocale_set_cat */
  347.  
  348.  
  349.  
  350. static char * __cdecl _setlocale_get_all (
  351.         void
  352.         )
  353. {
  354.         int i;
  355.         int same = 1;
  356.         /* allocate memory if necessary */
  357.         if (!__lc_category[LC_ALL].locale)
  358.             __lc_category[LC_ALL].locale =
  359.                 _malloc_crt((MAX_LC_LEN+1) * (LC_MAX-LC_MIN+1) + CATNAMES_LEN);
  360.  
  361.         __lc_category[LC_ALL].locale[0] = '\0';
  362.         for (i=LC_MIN+1; ; i++)
  363.         {
  364.             _strcats(__lc_category[LC_ALL].locale, 3, __lc_category[i].catname,"=",__lc_category[i].locale);
  365.             if (i<LC_MAX)
  366.             {
  367.                 strcat(__lc_category[LC_ALL].locale,";");
  368.                 if (strcmp(__lc_category[i].locale, __lc_category[i+1].locale))
  369.                     same=0;
  370.             }
  371.             else
  372.             {
  373.                 if (!same)
  374.                     return __lc_category[LC_ALL].locale;
  375.                 else
  376.                 {
  377.                     _free_crt(__lc_category[LC_ALL].locale);
  378.                     __lc_category[LC_ALL].locale = (char *)NULL;
  379.                     return __lc_category[LC_CTYPE].locale;
  380.                 }
  381.             }
  382.         }
  383. } /* _setlocale_get_all */
  384.  
  385.  
  386. char * _expandlocale (
  387.         char *expr,
  388.         char * output,
  389.         LC_ID * id,
  390.         UINT * cp,
  391.         int category
  392.         )
  393. {
  394.         static  LC_ID   cacheid = {0, 0, 0};
  395.         static  UINT    cachecp = 0;
  396.         static  char cachein[MAX_LC_LEN] = "C";
  397.         static  char cacheout[MAX_LC_LEN] = "C";
  398.  
  399.         if (!expr)
  400.             return NULL; /* error if no input */
  401.  
  402.  
  403.         if (((*expr=='C') && (!expr[1]))
  404.             )  /* for "C" locale, just return */
  405.         {
  406.             *output = 'C';
  407.             output[1] = '\0';
  408.             if (id)
  409.             {
  410.                 id->wLanguage = 0;
  411.                 id->wCountry  = 0;
  412.                 id->wCodePage = 0;
  413.             }
  414.             if (cp)
  415.             {
  416.                 *cp = CP_ACP; /* return to ANSI code page */
  417.             }
  418.             return output; /* "C" */
  419.         }
  420.  
  421.         /* first, make sure we didn't just do this one */
  422.         if (strcmp(cacheout,expr) && strcmp(cachein,expr))
  423.         {
  424.             /* do some real work */
  425.             LC_STRINGS names;
  426.  
  427.             if (__lc_strtolc((LC_STRINGS *)&names, (const char *)expr))
  428.                 return NULL;  /* syntax error */
  429.  
  430.             if (!__get_qualified_locale((LPLC_STRINGS)&names,
  431.                 (LPLC_ID)&cacheid, (LPLC_STRINGS)&names))
  432.                 return NULL;    /* locale not recognized/supported */
  433.  
  434.             /* begin: cache atomic section */
  435.  
  436.             cachecp = cacheid.wCodePage;
  437.  
  438.             __lc_lctostr((char *)cacheout, &names);
  439.  
  440.             /* Don't cache "" empty string */
  441.             if (*expr)
  442.                 strcpy(cachein, expr);
  443.             else
  444.                 strcpy(cachein, cacheout);
  445.  
  446.             /* end: cache atomic section */
  447.         }
  448.         if (id)
  449.             memcpy((void *)id, (void *)&cacheid, sizeof(cacheid));   /* possibly return LC_ID */
  450.         if (cp)
  451.             memcpy((void *)cp, (void *)&cachecp, sizeof(cachecp));   /* possibly return cp */
  452.  
  453.         strcpy(output,cacheout);
  454.         return cacheout; /* return fully expanded locale string */
  455. }
  456.  
  457. /* helpers */
  458.  
  459. int __cdecl __init_dummy(void)  /* default routine for locale initializer */
  460. {
  461.         return 0;
  462. }
  463.  
  464. void _strcats
  465.         (
  466.         char *outstr,
  467.         int n,
  468.         ...
  469.         )
  470. {
  471.         int i;
  472.         va_list substr;
  473.  
  474.         va_start (substr, n);
  475.  
  476.         for (i =0; i<n; i++)
  477.         {
  478.             strcat(outstr, va_arg(substr, char *));
  479.         }
  480.         va_end(substr);
  481. }
  482.  
  483. int __lc_strtolc
  484.    (
  485.    LC_STRINGS *names,
  486.    const char *locale
  487.    )
  488. {
  489.         int i, len;
  490.         char ch;
  491.  
  492.         memset((void *)names, '\0', sizeof(LC_STRINGS));  /* clear out result */
  493.  
  494.         if (*locale=='\0')
  495.             return 0; /* trivial case */
  496.  
  497.         /* only code page is given */
  498.         if (locale[0] == '.' && locale[1] != '\0')
  499.         {
  500.             strcpy((char *)names->szCodePage, &locale[1]);
  501.             return 0;
  502.         }
  503.  
  504.         for (i=0; ; i++)
  505.         {
  506.             if (!(len=strcspn(locale,"_.,")))
  507.                 return -1;  /* syntax error */
  508.  
  509.             ch = locale[len];
  510.  
  511.             if ((i==0) && (len<MAX_LANG_LEN) && (ch!='.'))
  512.                 strncpy((char *)names->szLanguage, locale, len);
  513.  
  514.             else if ((i==1) && (len<MAX_CTRY_LEN) && (ch!='_'))
  515.                 strncpy((char *)names->szCountry, locale, len);
  516.  
  517.             else if ((i==2) && (ch=='\0' || ch==','))
  518.                 strncpy((char *)names->szCodePage, locale, len);
  519.  
  520.             else
  521.                 return -1;  /* error parsing locale string */
  522.  
  523.             if (ch==',')
  524.             {
  525.                 /* modifier not used in current implementation, but it
  526.                    must be parsed to for POSIX/XOpen conformance */
  527.             /*  strncpy(names->szModifier, locale, MAX_MODIFIER_LEN-1); */
  528.                 break;
  529.             }
  530.  
  531.             if (!ch)
  532.                 break;
  533.             locale+=(len+1);
  534.         }
  535.         return 0;
  536. }
  537.  
  538. void __lc_lctostr
  539. (
  540.         char *locale,
  541.         const LC_STRINGS *names
  542.         )
  543. {
  544.         strcpy(locale, (char *)names->szLanguage);
  545.         if (*(names->szCountry))
  546.             _strcats(locale, 2, "_", names->szCountry);
  547.         if (*(names->szCodePage))
  548.             _strcats(locale, 2, ".", names->szCodePage);
  549. /*      if (names->szModifier)
  550.         _strcats(locale, 2, ",", names->szModifier); */
  551. }
  552.  
  553.  
  554. #endif  /* !defined (_WIN32) */
  555.