1 /*
   2  * Copyright 2010 Nexenta Systems, Inc.  All rights reserved.
   3  * Copyright (c) 2001, 2003 Alexey Zelkin <phantom@FreeBSD.org>
   4  * All rights reserved.
   5  *
   6  * Redistribution and use in source and binary forms, with or without
   7  * modification, are permitted provided that the following conditions
   8  * are met:
   9  * 1. Redistributions of source code must retain the above copyright
  10  *    notice, this list of conditions and the following disclaimer.
  11  * 2. Redistributions in binary form must reproduce the above copyright
  12  *    notice, this list of conditions and the following disclaimer in the
  13  *    documentation and/or other materials provided with the distribution.
  14  *
  15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
  16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
  19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  25  * SUCH DAMAGE.
  26  */
  27 
  28 #include "lint.h"
  29 #include <langinfo.h>
  30 #include <limits.h>
  31 #include <locale.h>
  32 #include <stdlib.h>
  33 #include <string.h>
  34 
  35 #include "lnumeric.h"
  36 #include "lmessages.h"
  37 #include "lmonetary.h"
  38 #include "timelocal.h"
  39 
  40 #define _REL(BASE) ((int)item-BASE)
  41 
  42 #define MONETARY        (__get_current_monetary_locale())
  43 #define TIME            (__get_current_time_locale())
  44 #define MESSAGES        (__get_current_messages_locale())
  45 #define NUMERIC         (__get_current_numeric_locale())
  46 
  47 #pragma weak _nl_langinfo = nl_langinfo
  48 
  49 char *
  50 nl_langinfo(nl_item item)
  51 {
  52         char *ret, *s, *cs;
  53         static char *csym = NULL;
  54 
  55         switch (item) {
  56         case CODESET:
  57                 ret = "";
  58                 /*
  59                  * The codeset is the suffix of a locale, for most it will
  60                  * will be UTF-8, as in "en.UTF-8".  Short form locales are
  61                  * not supported.  Note also that although FreeBSD uses
  62                  * US-ASCII, Solaris historically has reported "646" for the
  63                  * C locale.
  64                  */
  65                 if ((s = setlocale(LC_CTYPE, NULL)) != NULL) {
  66                         if ((cs = strchr(s, '.')) != NULL)
  67                                 ret = cs + 1;
  68                         else if (strcmp(s, "C") == 0 || strcmp(s, "POSIX") == 0)
  69                                 ret = "646";
  70                 }
  71                 break;
  72         case D_T_FMT:
  73                 ret = (char *)TIME->c_fmt;
  74                 break;
  75         case D_FMT:
  76                 ret = (char *)TIME->x_fmt;
  77                 break;
  78         case T_FMT:
  79                 ret = (char *)TIME->X_fmt;
  80                 break;
  81         case T_FMT_AMPM:
  82                 ret = (char *)TIME->ampm_fmt;
  83                 break;
  84         case AM_STR:
  85                 ret = (char *)TIME->am;
  86                 break;
  87         case PM_STR:
  88                 ret = (char *)TIME->pm;
  89                 break;
  90         case DAY_1: case DAY_2: case DAY_3:
  91         case DAY_4: case DAY_5: case DAY_6: case DAY_7:
  92                 ret = (char *)TIME->weekday[_REL(DAY_1)];
  93                 break;
  94         case ABDAY_1: case ABDAY_2: case ABDAY_3:
  95         case ABDAY_4: case ABDAY_5: case ABDAY_6: case ABDAY_7:
  96                 ret = (char *)TIME->wday[_REL(ABDAY_1)];
  97                 break;
  98         case MON_1: case MON_2: case MON_3: case MON_4:
  99         case MON_5: case MON_6: case MON_7: case MON_8:
 100         case MON_9: case MON_10: case MON_11: case MON_12:
 101                 ret = (char *)TIME->month[_REL(MON_1)];
 102                 break;
 103         case ABMON_1: case ABMON_2: case ABMON_3: case ABMON_4:
 104         case ABMON_5: case ABMON_6: case ABMON_7: case ABMON_8:
 105         case ABMON_9: case ABMON_10: case ABMON_11: case ABMON_12:
 106                 ret = (char *)TIME->mon[_REL(ABMON_1)];
 107                 break;
 108         case ERA:
 109                 /* XXX: need to be implemented  */
 110                 ret = "";
 111                 break;
 112         case ERA_D_FMT:
 113                 /* XXX: need to be implemented  */
 114                 ret = "";
 115                 break;
 116         case ERA_D_T_FMT:
 117                 /* XXX: need to be implemented  */
 118                 ret = "";
 119                 break;
 120         case ERA_T_FMT:
 121                 /* XXX: need to be implemented  */
 122                 ret = "";
 123                 break;
 124         case ALT_DIGITS:
 125                 /* XXX: need to be implemented  */
 126                 ret = "";
 127                 break;
 128         case RADIXCHAR:
 129                 ret = (char *)NUMERIC->decimal_point;
 130                 break;
 131         case THOUSEP:
 132                 ret = (char *)NUMERIC->thousands_sep;
 133                 break;
 134         case YESEXPR:
 135                 ret = (char *)MESSAGES->yesexpr;
 136                 break;
 137         case NOEXPR:
 138                 ret = (char *)MESSAGES->noexpr;
 139                 break;
 140         /*
 141          * YESSTR and NOSTR items marked with LEGACY are available, but not
 142          * recomended by SUSv2 to be used in portable applications since
 143          * they're subject to remove in future specification editions.
 144          */
 145         case YESSTR:    /* LEGACY  */
 146                 ret = (char *)MESSAGES->yesstr;
 147                 break;
 148         case NOSTR:     /* LEGACY  */
 149                 ret = (char *)MESSAGES->nostr;
 150                 break;
 151         /*
 152          * SUSv2 special formatted currency string
 153          */
 154         case CRNCYSTR:
 155                 ret = "";
 156                 cs = (char *)MONETARY->currency_symbol;
 157                 if (*cs != '\0') {
 158                         char pos = localeconv()->p_cs_precedes;
 159 
 160                         if (pos == localeconv()->n_cs_precedes) {
 161                                 char psn = '\0';
 162 
 163                                 if (pos == CHAR_MAX) {
 164                                         if (strcmp(cs,
 165                                             MONETARY->mon_decimal_point) == 0)
 166                                                 psn = '.';
 167                                 } else
 168                                         psn = pos ? '-' : '+';
 169                                 if (psn != '\0') {
 170                                         int clen = strlen(cs);
 171                                         char *newc;
 172 
 173                                         newc = realloc(csym, clen + 2);
 174                                         if (newc != NULL) {
 175                                                 free(csym);
 176                                                 csym = newc;
 177                                                 *csym = psn;
 178                                                 (void) strcpy(csym + 1, cs);
 179                                                 ret = csym;
 180                                         }
 181                                 }
 182                         }
 183                 }
 184                 break;
 185         case _DATE_FMT:         /* Solaris specific extension */
 186                 ret = (char *)TIME->date_fmt;
 187                 break;
 188         /*
 189          * Note that FreeBSD also had a private D_MD_ORDER, but that appears
 190          * to have been specific to FreeBSD, so we have not included it here.
 191          */
 192         default:
 193                 ret = "";
 194         }
 195         return (ret);
 196 }