diff options
Diffstat (limited to 'usr/src/lib/libbc/libc/gen/common/setlocale.c')
-rw-r--r-- | usr/src/lib/libbc/libc/gen/common/setlocale.c | 861 |
1 files changed, 861 insertions, 0 deletions
diff --git a/usr/src/lib/libbc/libc/gen/common/setlocale.c b/usr/src/lib/libbc/libc/gen/common/setlocale.c new file mode 100644 index 0000000000..9d4691198f --- /dev/null +++ b/usr/src/lib/libbc/libc/gen/common/setlocale.c @@ -0,0 +1,861 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 1995 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* Copyright (c) 1984 AT&T */ +/* All Rights Reserved */ + + +#pragma ident "%Z%%M% %I% %E% SMI" + +#if !defined(lint) && defined(SCCSIDS) +static char *sccsid = "%Z%%M% %I% %E% SMI"; +#endif + +#include <fcntl.h> +extern int open(); +#include <locale.h> +#include <stdlib.h> +#include "codeset.h" +#include <ctype.h> +#include <string.h> +#include <memory.h> +#include <malloc.h> +#include <sys/param.h> /* for MAXPATHLEN */ +#include <sys/stat.h> +#include <errno.h> +#include <limits.h> + +#define TRAILER ".ci" + +extern int stat(); +extern char *getenv(); + +struct _code_set_info _code_set_info = { + NULL, + CODESET_NONE, /* no codeset */ + NULL, /* not defined */ + 0, +}; + +/* tolower() and toupper() conversion table + * is hidden here to avoid being placed in the + * extern .sa file in the dynamic version of libc + */ + +char _ctype_ul[] = { 0, + +/* 0 1 2 3 4 5 6 7 */ + '\000', '\001', '\002', '\003', '\004', '\005', '\006', '\007', + '\010', '\011', '\012', '\013', '\014', '\015', '\016', '\017', + '\020', '\021', '\022', '\023', '\024', '\025', '\026', '\027', + '\030', '\031', '\032', '\033', '\034', '\035', '\036', '\037', + ' ', '!', '"', '#', '$', '%', '&', '\'', + '(', ')', '*', '+', ',', '-', '.', '/', + '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', ':', ';', '<', '=', '>', '?', + '@', 'a', 'b', 'c', 'd', 'e', 'f', 'g', + 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', + 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', + 'x', 'y', 'z', '[', '\\', ']', '^', '_', + '`', 'A', 'B', 'C', 'D', 'E', 'F', 'G', + 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', + 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', + 'X', 'Y', 'Z', '{', '|', '}', '~', '\177', + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, +}; + +/* following layout is: + * LC_NUMERIC LC_TIME LC_MONETARY LANGINFO LC_COLLATE LC_MESSAGES + */ +char _locales[MAXLOCALE - 1][MAXLOCALENAME + 1] ; + +char _my_time[MAXLOCALENAME + 1]; + +/* The array Default holds the systems notion of default locale. It is normally + * found in {LOCALE}/.default and moved to here. Note there is only one + * default locale spanning all categories + */ + +static char Default[MAXLOCALENAME+1]; + +struct langinfo _langinfo; +struct dtconv *_dtconv = NULL; + +static char *realmonths = NULL; +static char *realdays = NULL; +static char *realfmts = NULL; +static short lang_succ = ON; /* setlocale success */ + + +/* Set the values here to guarantee stdio use of the + decimal point + */ +static struct lconv lconv_arr = { + ".", "", "", "", "", + "", "", "", "", "", + CHAR_MAX, CHAR_MAX, CHAR_MAX, CHAR_MAX, + CHAR_MAX, CHAR_MAX, CHAR_MAX, CHAR_MAX +}; + +/* lconv is externally defined by ANSI C */ +struct lconv *lconv = &lconv_arr; + +static char *lconv_numeric_str = NULL; +static char *lconv_monetary_str = NULL; + +int getlocale_ctype(/*char *locale, char *ctypep, char *newlocale*/); +char *getlocale_numeric(/*char *locale, struct lconv *lconvp*/); +static char *getlocale_monetary(); +void init_statics(); + +static char *getstr(/*char *p, char **strp*/); +static char *getgrouping(/*char *p, char **groupingp*/); +static char *getnum(/*char *p, int *nump*/); +static char *getbool(/*char *p, int *boolp*/); +int openlocale(/*char *category, int cat_id, char *locale, char *newlocale */); +static int set_codeinfo(/*char */); +static int set_default(); + +char * +setlocale(category, locale) + int category; + char *locale; +{ + static char buf[MAXLOCALE*(MAXLOCALENAME + 1) + 1]; + /* buffer for current LC_ALL value */ + int nonuniform; + short ret; + char my_ctype[CTYPE_SIZE]; /* local copy */ + struct lconv my_lconv; /* local copy */ + char *my_lconv_numeric_str; + char *my_lconv_monetary_str; + register int i; + register char *p; + + + /* initialize my_lconv to lconv */ + memcpy(&my_lconv, lconv, sizeof(my_lconv)); + + /* + * Following code is to avoid static initialisation of + * strings which would otherwise blow up "xstr". + */ + if (_locales[0][0] == '\0') + init_statics(); + + if (locale == NULL) { + if (category == LC_ALL) { + /* + * Assume all locales are set to the same value. Then + * scan through the locales to see if any are + * different. If they are the same, return the common + * value; otherwise, construct a "composite" value. + */ + nonuniform = 0; /* assume all locales set the same */ + for (i = 0; i < MAXLOCALE - 2; i++) { + if (strcmp(_locales[i], _locales[i + 1]) != 0) { + nonuniform = 1; + break; + } + } + if (nonuniform) { + /* + * They're not all the same. Construct a list + * of all the locale values, in order, + * separated by slashes. Return that value. + */ + (void) strcpy(buf, _locales[0]); + for (i = 1; i < MAXLOCALE - 1; i++) { + (void) strcat(buf, "/"); + (void) strcat(buf, _locales[i]); + } + return (buf); + } else { + /* + * They're all the same; any one you return is + * OK. + */ + return (_locales[0]); + } + } else + return (_locales[category - 1]); + } + + switch (category) { + + case LC_ALL: + if (strchr(locale, '/') != NULL) { + /* + * Composite value; extract each category. + */ + if (strlen(locale) > sizeof buf - 1) + return (NULL); /* too long */ + (void) strcpy(buf, locale); + p = buf; + + /* + * LC_CTYPE and LC_NUMERIC are set here. + * Others locales won't be set here, + * they will be just marked. + */ + for (i = 0; i < MAXLOCALE - 1; i++) { + p = strtok(p, "/"); + if (p == NULL) + return (NULL); /* missing item */ + switch (i) { + + case LC_CTYPE - 1: + if (setlocale(LC_CTYPE,p) == NULL) + return NULL; + break; + case LC_NUMERIC - 1: + if (setlocale(LC_NUMERIC,p) == NULL) + return NULL; + break; + case LC_TIME - 1: + if (setlocale(LC_TIME,p) == NULL) + return NULL; + break; + case LC_MONETARY - 1: + if (setlocale(LC_MONETARY,p) == NULL) + return NULL; + break; + case LANGINFO - 1: + if (setlocale(LANGINFO,p) == NULL) + return NULL; + break; + case LC_COLLATE - 1: + if (setlocale(LC_COLLATE,p) == NULL) + return NULL; + break; + case LC_MESSAGES - 1: + if (setlocale(LC_MESSAGES,p) == NULL) + return NULL; + break; + } + p = NULL; + } + if (strtok((char *)NULL, "/") != NULL) + return (NULL); /* extra stuff at end */ + } + + /* If category = LC_ALL, Drop through to test each individual + * category, one at a time. Note default rules where env vars + * are not set + */ + + case LC_CTYPE: + if ((ret = getlocale_ctype(locale , my_ctype, + _locales[LC_CTYPE - 1])) < 0) + return (NULL); + if (ret) { + (void) memcpy(_ctype_, my_ctype, CTYPE_SIZE/2); + (void) memcpy(_ctype_ul, my_ctype+(CTYPE_SIZE/2), CTYPE_SIZE/2); + } + if (category != LC_ALL) + break; + + case LC_NUMERIC: + if ((my_lconv_numeric_str = + getlocale_numeric(locale, &my_lconv, + _locales[LC_NUMERIC - 1])) == NULL) + return (NULL); + if (*my_lconv_numeric_str) { + if (lconv_numeric_str != NULL) + free((malloc_t)lconv_numeric_str); + lconv_numeric_str = my_lconv_numeric_str; + memcpy(lconv, my_lconv, sizeof(my_lconv)); + } + if (category != LC_ALL) + break; + + case LC_TIME: + if ((ret = openlocale("LC_TIME", LC_TIME, locale, + _locales[LC_TIME -1])) < 0) + return (NULL); + if (ret) + (void) close(ret); + if (category != LC_ALL) + break; + + case LC_MONETARY: + if ((my_lconv_monetary_str = + getlocale_monetary(locale, &my_lconv, + _locales[LC_MONETARY - 1])) == NULL) + return (NULL); + if (*my_lconv_monetary_str) { + if (lconv_monetary_str != NULL) + free((malloc_t)lconv_monetary_str); + lconv_monetary_str = my_lconv_monetary_str; + memcpy(lconv, &my_lconv, sizeof(my_lconv)); + } + if (category != LC_ALL) + break; + + case LANGINFO: + if ((ret = openlocale("LANGINFO", LANGINFO, locale, + _locales[LANGINFO - 1])) < 0) { + lang_succ = OFF; + return (NULL); + } + if (ret) { + lang_succ = OFF; + (void) close(ret); + } + if (category != LC_ALL) + break; + + case LC_COLLATE: + if ((ret = openlocale("LC_COLLATE", LC_COLLATE, locale, + _locales[LC_COLLATE - 1])) < 0) + return (NULL); + if (ret) { + (void) close(ret); + } + if (category != LC_ALL) + break; + + case LC_MESSAGES: + if ((ret = openlocale("LC_MESSAGES", LC_MESSAGES, locale, + _locales[LC_MESSAGES - 1])) < 0) + return (NULL); + if (ret) { + (void) close(ret); + } + } + return (setlocale(category, (char *)NULL)); +} + +int +getlocale_ctype(locale, ctypep, newlocale) + char *locale; + char *ctypep; + char *newlocale; +{ + register int fd; + + if ((fd = openlocale("LC_CTYPE", LC_CTYPE, locale, newlocale)) > 0) { + if (read(fd, (char *)ctypep, CTYPE_SIZE) != CTYPE_SIZE) { + (void) close(fd); + fd = -1; + } + (void) close(fd); + } + return (fd); +} + +/* open and load the numeric information */ + +char * +getlocale_numeric(locale, lconvp, newlocale) + char *locale; + register struct lconv *lconvp; + char *newlocale; +{ + register int fd; + struct stat buf; + char *str; + register char *p; + + if ((fd = openlocale("LC_NUMERIC", LC_NUMERIC, locale, newlocale)) < 0) + return (NULL); + if (fd == 0) + return ""; + if ((fstat(fd, &buf)) != 0) + return (NULL); + if ((str = (char*)malloc((unsigned)buf.st_size + 2)) == NULL) + return (NULL); + + if ((read(fd, str, (int)buf.st_size)) != buf.st_size) { + free((malloc_t)str); + return (NULL); + } + + /* Set last character of str to '\0' */ + p = &str[buf.st_size]; + *p++ = '\n'; + *p = '\0'; + + /* p will "walk thru" str */ + p = str; + + p = getstr(p, &lconvp->decimal_point); + if (p == NULL) + goto fail; + p = getstr(p, &lconvp->thousands_sep); + if (p == NULL) + goto fail; + p = getgrouping(p, &lconvp->grouping); + if (p == NULL) + goto fail; + (void) close(fd); + + return (str); + +fail: + (void) close(fd); + free((malloc_t)str); + return (NULL); +} + + +static char * +getlocale_monetary(locale, lconvp, newlocale) + char *locale; + register struct lconv *lconvp; + char *newlocale; +{ + register int fd; + struct stat buf; + char *str; + register char *p; + + if ((fd = openlocale("LC_MONETARY", LC_MONETARY, locale, newlocale)) < 0) + return (NULL); + if (fd == 0) + return ""; + if ((fstat(fd, &buf)) != 0) + return (NULL); + if ((str = (char*)malloc((unsigned)buf.st_size + 2)) == NULL) + return (NULL); + + if ((read(fd, str, (int)buf.st_size)) != buf.st_size) { + free((malloc_t)str); + return (NULL); + } + + /* Set last character of str to '\0' */ + p = &str[buf.st_size]; + *p++ = '\n'; + *p = '\0'; + + /* p will "walk thru" str */ + p = str; + + p = getstr(p, &lconvp->int_curr_symbol); + if (p == NULL) + goto fail; + p = getstr(p, &lconvp->currency_symbol); + if (p == NULL) + goto fail; + p = getstr(p, &lconvp->mon_decimal_point); + if (p == NULL) + goto fail; + p = getstr(p, &lconvp->mon_thousands_sep); + if (p == NULL) + goto fail; + p = getgrouping(p, &lconvp->mon_grouping); + if (p == NULL) + goto fail; + p = getstr(p, &lconvp->positive_sign); + if (p == NULL) + goto fail; + p = getstr(p, &lconvp->negative_sign); + if (p == NULL) + goto fail; + p = getnum(p, &lconvp->frac_digits); + if (p == NULL) + goto fail; + p = getbool(p, &lconvp->p_cs_precedes); + if (p == NULL) + goto fail; + p = getbool(p, &lconvp->p_sep_by_space); + if (p == NULL) + goto fail; + p = getbool(p, &lconvp->n_cs_precedes); + if (p == NULL) + goto fail; + p = getbool(p, &lconvp->n_sep_by_space); + if (p == NULL) + goto fail; + p = getnum(p, &lconvp->p_sign_posn); + if (p == NULL) + goto fail; + p = getnum(p, &lconvp->n_sign_posn); + if (p == NULL) + goto fail; + (void) close(fd); + + return (str); + +fail: + (void) close(fd); + free((malloc_t)str); + return NULL; +} + +static char * +getstr(p, strp) + register char *p; + char **strp; +{ + *strp = p; + p = strchr(p, '\n'); + if (p == NULL) + return (NULL); /* no end-of-line */ + *p++ = '\0'; + return (p); +} + +static char * +getgrouping(p, groupingp) + register char *p; + char **groupingp; +{ + register int c; + + if (*p == '\0') + return (NULL); /* no grouping */ + *groupingp = p; + while ((c = *p) != '\n') { + if (c == '\0') + return (NULL); /* no end-of-line */ + if (c >= '0' && c <= '9') + *p++ = c - '0'; + else + *p++ = '\177'; + } + *p++ = '\0'; + return (p); +} + +static char * +getnum(p, nump) + register char *p; + char *nump; +{ + register int num; + register int c; + + if (*p == '\0') + return (NULL); /* no number */ + if (*p == '\n') + *nump = '\177'; /* blank line - no value */ + else { + num = 0; + while ((c = *p) != '\n') { + if (c < '0' || c > '9') + return (NULL); /* bad number */ + num = num*10 + c - '0'; + p++; + } + *nump = num; + } + *p++ = '\0'; + return (p); +} + +static char * +getbool(p, boolp) + register char *p; + char *boolp; +{ + + if (*p == '\0') + return (NULL); /* no number */ + if (*p == '\n') + *boolp = '\177'; /* blank line - no value */ + else { + switch (*p++) { + + case 'y': + case 'Y': + case 't': + case 'T': + *boolp = 1; /* true */ + break; + + case 'n': + case 'N': + case 'f': + case 'F': + *boolp = 0; /* false */ + break; + + default: + return (NULL); /* bad boolean */ + } + if (*p != '\n') + return (NULL); /* noise at end of line */ + } + *p++ = '\0'; + return (p); +} + +/* + * Open a locale file. First, check the value of "locale"; if it's a null + * string, first check the environment variable with the same name as the + * category, and then check the environment variable "LANG". If neither of + * them are set to non-null strings, use the LC_default env.var and if this + * has no meaning then assume we are running in the C locale. It is expected + * That LC_default is set across the whole system. If the resulting locale is + * longer than MAXLOCALENAME characters, reject it. Then, try looking in the + * per-machine locale directory for the file in question; if it's not found + * there, try looking in the shared locale directory. + * If there is no work to do, that is, the last setting of locales is equal + * to the current request, then we don't do anything, and exit with value 0. + * Copy the name of the locale used into "newlocale". + * Exit with positive value if we opened a file + * Exit with -1 if an error occured (invalid locale). + * Exit with 0 if there is no need to look at the disk file. + * (Assumption - there is always at least one fd open before setlocale + * is called) + */ +int +openlocale(category, cat_id, locale, newlocale) + char *category; + register int cat_id; + register char *locale; + char *newlocale; +{ + char pathname[MAXPATHLEN], *defp; + int fd, fd2; + struct _code_header code_header; + char *my_info; + + if (*locale == '\0') { + locale = getenv(category); + if (locale == NULL || *locale == '\0') { + locale = getenv("LANG"); + if (locale == NULL || *locale == '\0') { + if (*Default == '\0') { + defp = getenv("LC_default"); + if (defp == NULL || *defp == '\0') + strcpy(Default,"C"); + else + strcpy(Default, defp); + } + locale = Default; + } + } + } + if (strcmp(locale,_locales[cat_id-1]) == 0) { + (void) strcpy(newlocale, locale); + return 0; + } + if (strlen(locale) > MAXLOCALENAME) + return -1; + + (void) strcpy(pathname, PRIVATE_LOCALE_DIR); + (void) strcat(pathname, category); + (void) strcat(pathname, "/"); + (void) strcat(pathname, locale); + if ((fd = open(pathname, O_RDONLY)) < 0 && errno == ENOENT) { + (void) strcpy(pathname, LOCALE_DIR); + (void) strcat(pathname, category); + (void) strcat(pathname, "/"); + (void) strcat(pathname, locale); + fd = open(pathname, O_RDONLY); + } + if (fd >= 0) + (void) strcpy(newlocale, locale); + /* + * bug id 1072740; if by some chance the actual fd we're going to + * return is 0, change it to be some non-zero descriptor, because + * returning 0 means something different. If '0' is the only + * descriptor left, return an error. + */ + if (fd == 0) { + int dupfd; + + if ((dupfd = dup(fd)) < 1) { + (void) close(fd); + fd = -1; + } else { + (void) close(fd); + fd = dupfd; + } + } + + if (cat_id == LC_CTYPE) { + + /* Go and get the trailer file */ + + (void) strcat(pathname, TRAILER); + fd2 = open(pathname, O_RDONLY); + if ( fd2 == 0 ) { + fd2 = dup(fd2); + close(0); + } + + if (fd2 == -1) { + set_default(); + return fd; + } + + /* + * ctype trailer file exists - read it + */ + + if (read (fd2, (char *)&code_header, sizeof (code_header)) != + sizeof (code_header)) { + /* + * File format not correct + */ + set_default(); + close(fd2); + return -1; + } + /* + * set up trailer file + */ + strcpy(_code_set_info.code_name, code_header.code_name); + _code_set_info.code_id = code_header.code_id; + if (_code_set_info.code_info != NULL) + free (_code_set_info.code_info); + if (code_header.code_info_size > 0) { + my_info = malloc(code_header.code_info_size); + if (read (fd2, (char *)my_info, + code_header.code_info_size) != + code_header.code_info_size) { + close(fd2); + set_default(); + return -1; + } + _code_set_info.code_info = my_info; + } + else { + /* + * We have a corrupted file too + */ + _code_set_info.code_info = NULL; + close(fd2); + set_default(); + return -1; + } + close (fd2); + } + return fd; +} + +struct lconv * +localeconv() +{ + return (lconv); +} + +struct dtconv * +localdtconv() +{ + register char *p; + register short i; + + char *rawmonths = "Jan\nFeb\nMar\nApr\nMay\nJun\nJul\nAug\nSep\nOct\nNov\nDec\nJanuary\nFebruary\nMarch\nApril\nMay\nJune\nJuly\nAugust\nSeptember\nOctober\nNovember\nDecember"; + + char *rawdays = "Sun\nMon\nTue\nWed\nThu\nFri\nSat\nSunday\nMonday\nTuesday\nWednesday\nThursday\nFriday\nSaturday"; + +char *rawfmts = "%H:%M:%S\n%m/%d/%y\n%a %b %e %T %Z %Y\nAM\nPM\n%A, %B %e, %Y\n"; + + /* fix for bugid 1067574 ... robinson */ + (void)getlocale_time(); + + if (_dtconv == NULL) { + + /* We malloc both the space for the dtconv struct and the + * copy of the strings above because this program is later run + * through xstr and the resultant strings are put in read-only + * text segment. Therefore we cannot write to the original + * raw strings but we can to their copies. + */ + + _dtconv = (struct dtconv*)malloc(sizeof (struct dtconv)); + if (_dtconv == NULL) + return (NULL); + if ((realmonths = malloc(strlen(rawmonths)+1)) == NULL) + return (NULL); + strcpy(realmonths, rawmonths); + if ((realdays = malloc(strlen(rawdays)+1)) == NULL) + return (NULL); + strcpy(realdays, rawdays); + if ((realfmts = malloc(strlen(rawfmts)+1)) == NULL) + return (NULL); + strcpy(realfmts, rawfmts); + + /* p will "walk thru" str */ + + p = realmonths; + + for (i = 0; i < 12; i++) + p = getstr(p, &(_dtconv->abbrev_month_names[i])); + + for (i = 0; i < 12; i++) + p = getstr(p, &(_dtconv->month_names[i])); + p = realdays; + for (i= 0; i < 7; i++) + p = getstr(p, &(_dtconv->abbrev_weekday_names[i])); + for (i = 0; i < 7; i++) + p = getstr(p, &(_dtconv->weekday_names[i])); + p = realfmts; + p = getstr(p, &_dtconv->time_format); + p = getstr(p, &_dtconv->sdate_format); + p = getstr(p, &_dtconv->dtime_format); + p = getstr(p, &_dtconv->am_string); + p = getstr(p, &_dtconv->pm_string); + p = getstr(p, &_dtconv->ldate_format); + } + + return (_dtconv); +} + + +static int +set_default() +{ + + strcpy(_code_set_info.code_name, Default); + _code_set_info.code_id = CODESET_NONE; + if (_code_set_info.code_info != NULL) + free (_code_set_info.code_info); + _code_set_info.code_info = NULL; + _code_set_info.open_flag = 0; +} + +void init_statics() { + + short i; + + for (i=0; i<MAXLOCALE-1;i++) + strcpy(_locales[i],"C"); + strcpy(_code_set_info.code_name, "default"); + strcpy(_my_time,"C"); + _langinfo.yesstr = "yes"; + _langinfo.nostr = "no"; +} |