diff options
-rw-r--r-- | ChangeLog | 18 | ||||
-rw-r--r-- | include/stdio.h | 1 | ||||
-rw-r--r-- | include/time.h | 3 | ||||
-rw-r--r-- | libio/ftello.c | 3 | ||||
-rw-r--r-- | nscd/nscd_getgr_r.c | 40 | ||||
-rw-r--r-- | time/tzfile.c | 259 | ||||
-rw-r--r-- | time/tzset.c | 230 | ||||
-rw-r--r-- | timezone/tst-timezone.c | 3 |
8 files changed, 311 insertions, 246 deletions
@@ -1,5 +1,23 @@ 2007-10-14 Ulrich Drepper <drepper@redhat.com> + * include/time.h: Declare __tzset_parse_tz and __tzset_compute. + * time/tzset.c (tzset_internal): Break TZ string parsing out into + __tzset_parse_tz and updating of daylight, timezone, tzname into + update_vars. + (__tz_compute): Renamed from tz_compute. Take additional parameters. + (__tz_convert): Updating of tm_isdst, tm_zone, and tm_gmtoff now + happens in __tz_compute. + * time/tzfile.c (__tzfile_read): Also read TZ string. + (find_transition): Fold into __tzfile_compute. + (__tzfile_compute): For times beyond the last transition try to + use the TZ string. + * timezone/tst-timezone.c: Information in daylight and tzname does + change for Asia/Tokyo timezone with more concrete information. + Remove the test. + + * include/stdio.h: Add libc_hidden_proto for ftello. + * libio/ftello.c: Add libc_hidden_def. + [BZ #1140] * time/tzfile.c (__tzfile_compute): Compute tzname[] values based on the specified time and not the last entries in the file. Move diff --git a/include/stdio.h b/include/stdio.h index 084c02ea1e..3c11037490 100644 --- a/include/stdio.h +++ b/include/stdio.h @@ -138,6 +138,7 @@ libc_hidden_proto (rewind) libc_hidden_proto (fileno) libc_hidden_proto (fwrite) libc_hidden_proto (fseek) +libc_hidden_proto (ftello) libc_hidden_proto (fflush_unlocked) libc_hidden_proto (fread_unlocked) libc_hidden_proto (fwrite_unlocked) diff --git a/include/time.h b/include/time.h index ed6cb3669f..e896406db4 100644 --- a/include/time.h +++ b/include/time.h @@ -46,6 +46,9 @@ extern void __tzfile_compute (time_t timer, int use_localtime, struct tm *tp); extern void __tzfile_default (const char *std, const char *dst, long int stdoff, long int dstoff); +extern void __tzset_parse_tz (const char *tz); +extern void __tz_compute (time_t timer, struct tm *tm, int use_localtime) + __THROW internal_function; /* Subroutine of `mktime'. Return the `time_t' representation of TP and normalize TP, given that a `struct tm *' maps to a `time_t' as performed diff --git a/libio/ftello.c b/libio/ftello.c index e58daacad4..d250e55c04 100644 --- a/libio/ftello.c +++ b/libio/ftello.c @@ -1,4 +1,4 @@ -/* Copyright (C) 1993, 1995-2001, 2002, 2003, 2004 +/* Copyright (C) 1993, 1995-2001, 2002, 2003, 2004, 2007 Free Software Foundation, Inc. This file is part of the GNU C Library. @@ -63,3 +63,4 @@ ftello (fp) } return pos; } +libc_hidden_def (ftello) diff --git a/nscd/nscd_getgr_r.c b/nscd/nscd_getgr_r.c index a277a1d355..afb4d20435 100644 --- a/nscd/nscd_getgr_r.c +++ b/nscd/nscd_getgr_r.c @@ -264,9 +264,9 @@ nscd_getgr_r (const char *key, size_t keylen, request_type type, retval = 0; /* If there are no group members TOTAL_LEN is zero. */ - if (total_len > 0) + if (gr_name == NULL) { - if (gr_name == NULL) + if (total_len > 0) { size_t n = __readall (sock, resultbuf->gr_mem[0], total_len); if (__builtin_expect (n != total_len, 0)) @@ -278,26 +278,26 @@ nscd_getgr_r (const char *key, size_t keylen, request_type type, else *result = resultbuf; } - else + } + else + { + /* Copy the group member names. */ + memcpy (resultbuf->gr_mem[0], gr_name + gr_name_len, total_len); + + /* Try to detect corrupt databases. */ + if (resultbuf->gr_name[gr_name_len - 1] != '\0' + || resultbuf->gr_passwd[gr_resp.gr_passwd_len - 1] != '\0' + || ({for (cnt = 0; cnt < gr_resp.gr_mem_cnt; ++cnt) + if (resultbuf->gr_mem[cnt][len[cnt] - 1] != '\0') + break; + cnt < gr_resp.gr_mem_cnt; })) { - /* Copy the group member names. */ - memcpy (resultbuf->gr_mem[0], gr_name + gr_name_len, total_len); - - /* Try to detect corrupt databases. */ - if (resultbuf->gr_name[gr_name_len - 1] != '\0' - || resultbuf->gr_passwd[gr_resp.gr_passwd_len - 1] != '\0' - || ({for (cnt = 0; cnt < gr_resp.gr_mem_cnt; ++cnt) - if (resultbuf->gr_mem[cnt][len[cnt] - 1] != '\0') - break; - cnt < gr_resp.gr_mem_cnt; })) - { - /* We cannot use the database. */ - retval = mapped->head->gc_cycle != gc_cycle ? -2 : -1; - goto out_close; - } - - *result = resultbuf; + /* We cannot use the database. */ + retval = mapped->head->gc_cycle != gc_cycle ? -2 : -1; + goto out_close; } + + *result = resultbuf; } } else diff --git a/time/tzfile.c b/time/tzfile.c index a872e283a2..44d6614771 100644 --- a/time/tzfile.c +++ b/time/tzfile.c @@ -62,6 +62,7 @@ static long int rule_stdoff; static long int rule_dstoff; static size_t num_leaps; static struct leap *leaps; +static char *tzspec; #include <endian.h> #include <byteswap.h> @@ -113,6 +114,7 @@ __tzfile_read (const char *file, size_t extra, char **extrap) size_t leaps_idx; int was_using_tzfile = __use_tzfile; int trans_width = 4; + size_t tzspec_len; if (sizeof (time_t) != 4 && sizeof (time_t) != 8) abort (); @@ -241,10 +243,18 @@ __tzfile_read (const char *file, size_t extra, char **extrap) & ~(__alignof__ (struct leap) - 1)); leaps_idx = total_size; total_size += num_leaps * sizeof (struct leap); + tzspec_len = (trans_width == 8 + ? st.st_size - (ftello (f) + + num_transitions * (8 + 1) + + num_types * 6 + + chars + + num_leaps * 8 + + num_isstd + + num_isgmt) - 1 : 0); /* Allocate enough memory including the extra block requested by the caller. */ - transitions = (time_t *) malloc (total_size + extra); + transitions = (time_t *) malloc (total_size + tzspec_len + extra); if (transitions == NULL) goto lose; @@ -253,6 +263,10 @@ __tzfile_read (const char *file, size_t extra, char **extrap) types = (struct ttinfo *) ((char *) transitions + types_idx); zone_names = (char *) types + num_types * sizeof (struct ttinfo); leaps = (struct leap *) ((char *) transitions + leaps_idx); + if (trans_width == 8) + tzspec = (char *) leaps + num_leaps * sizeof (struct leap); + else + tzspec = NULL; if (extra > 0) *extrap = (char *) &leaps[num_leaps]; @@ -356,11 +370,16 @@ __tzfile_read (const char *file, size_t extra, char **extrap) while (i < num_types) types[i++].isgmt = 0; - /* XXX When a version 2 file is available it can contain a POSIX TZ-style - formatted string which specifies how times past the last one specified - are supposed to be handled. We might want to handle this at some - point. But it might be overhead since most/all? files have an - open-ended last entry. */ + /* Read the POSIX TZ-style information if possible. */ + if (tzspec != NULL) + { + /* Skip over the newline first. */ + if (getc_unlocked (f) != '\n' + || fread_unlocked (tzspec, 1, tzspec_len - 1, f) != tzspec_len - 1) + tzspec = NULL; + else + tzspec[tzspec_len - 1] = '\0'; + } fclose (f); @@ -530,138 +549,148 @@ __tzfile_default (const char *std, const char *dst, compute_tzname_max (stdlen + dstlen); } -static struct ttinfo * -internal_function -find_transition (time_t timer) +void +__tzfile_compute (time_t timer, int use_localtime, + long int *leap_correct, int *leap_hit, + struct tm *tp) { - size_t i; - - __tzname[0] = NULL; - __tzname[1] = NULL; + register size_t i; - if (num_transitions == 0 || timer < transitions[0]) + if (use_localtime) { - /* TIMER is before any transition (or there are no transitions). - Choose the first non-DST type - (or the first if they're all DST types). */ - i = 0; - while (i < num_types && types[i].isdst) + __tzname[0] = NULL; + __tzname[1] = NULL; + + if (num_transitions == 0 || timer < transitions[0]) { - if (__tzname[1] == NULL) - __tzname[1] = __tzstring (&zone_names[types[i].idx]); + /* TIMER is before any transition (or there are no transitions). + Choose the first non-DST type + (or the first if they're all DST types). */ + i = 0; + while (i < num_types && types[i].isdst) + { + if (__tzname[1] == NULL) + __tzname[1] = __tzstring (&zone_names[types[i].idx]); - ++i; - } + ++i; + } - if (i == num_types) - i = 0; - __tzname[0] = __tzstring (&zone_names[types[i].idx]); - if (__tzname[1] == NULL) + if (i == num_types) + i = 0; + __tzname[0] = __tzstring (&zone_names[types[i].idx]); + if (__tzname[1] == NULL) + { + size_t j = i; + while (j < num_types) + if (types[j].isdst) + { + __tzname[1] = __tzstring (&zone_names[types[j].idx]); + break; + } + else + ++j; + } + } + else if (timer >= transitions[num_transitions - 1]) { - size_t j = i; - while (j < num_types) - if (types[j].isdst) - { - __tzname[1] = __tzstring (&zone_names[types[j].idx]); - break; - } - else - ++j; + if (tzspec == NULL) + { + use_last: + i = num_transitions - 1; + goto found; + } + + /* Parse the POSIX TZ-style string. */ + __tzset_parse_tz (tzspec); + + /* Convert to broken down structure. If this fails do not + use the string. */ + if (! __offtime (&timer, 0, tp)) + goto use_last; + + /* Use the rules from the TZ string to compute the change. */ + __tz_compute (timer, tp, 1); + + *leap_correct = 0L; + *leap_hit = 0; + return; } - } - else if (timer >= transitions[num_transitions - 1]) - { - i = num_transitions - 1; - goto found; - } - else - { - /* Find the first transition after TIMER, and - then pick the type of the transition before it. */ - size_t lo = 0; - size_t hi = num_transitions - 1; - /* Assume that DST is changing twice a year and guess initial - search spot from it. - Half of a gregorian year has on average 365.2425 * 86400 / 2 - = 15778476 seconds. */ - i = (transitions[num_transitions - 1] - timer) / 15778476; - if (i < num_transitions) + else { - i = num_transitions - 1 - i; - if (timer < transitions[i]) + /* Find the first transition after TIMER, and + then pick the type of the transition before it. */ + size_t lo = 0; + size_t hi = num_transitions - 1; + /* Assume that DST is changing twice a year and guess initial + search spot from it. + Half of a gregorian year has on average 365.2425 * 86400 / 2 + = 15778476 seconds. */ + i = (transitions[num_transitions - 1] - timer) / 15778476; + if (i < num_transitions) { - if (i < 10 || timer >= transitions[i - 10]) + i = num_transitions - 1 - i; + if (timer < transitions[i]) { - /* Linear search. */ - while (timer < transitions[i - 1]) - --i; - goto found; + if (i < 10 || timer >= transitions[i - 10]) + { + /* Linear search. */ + while (timer < transitions[i - 1]) + --i; + goto found; + } + hi = i - 10; } - hi = i - 10; - } - else - { - if (i + 10 >= num_transitions || timer < transitions[i + 10]) + else { - /* Linear search. */ - while (timer >= transitions[i]) - ++i; - goto found; + if (i + 10 >= num_transitions || timer < transitions[i + 10]) + { + /* Linear search. */ + while (timer >= transitions[i]) + ++i; + goto found; + } + lo = i + 10; } - lo = i + 10; } - } - /* Binary search. */ - /* assert (timer >= transitions[lo] && timer < transitions[hi]); */ - while (lo + 1 < hi) - { - i = (lo + hi) / 2; - if (timer < transitions[i]) - hi = i; - else - lo = i; - } - i = hi; - - found: - /* assert (timer >= transitions[i - 1] && timer < transitions[i]); */ - __tzname[types[type_idxs[i - 1]].isdst] - = __tzstring (&zone_names[types[type_idxs[i - 1]].idx]); - size_t j = i; - while (j < num_transitions) - { - int type = type_idxs[j]; - int dst = types[type].isdst; - int idx = types[type].idx; + /* Binary search. */ + /* assert (timer >= transitions[lo] && timer < transitions[hi]); */ + while (lo + 1 < hi) + { + i = (lo + hi) / 2; + if (timer < transitions[i]) + hi = i; + else + lo = i; + } + i = hi; - if (__tzname[dst] == NULL) + found: + /* assert (timer >= transitions[i - 1] && timer < transitions[i]); */ + __tzname[types[type_idxs[i - 1]].isdst] + = __tzstring (&zone_names[types[type_idxs[i - 1]].idx]); + size_t j = i; + while (j < num_transitions) { - __tzname[dst] = __tzstring (&zone_names[idx]); + int type = type_idxs[j]; + int dst = types[type].isdst; + int idx = types[type].idx; - if (__tzname[1 - dst] != NULL) - break; - } + if (__tzname[dst] == NULL) + { + __tzname[dst] = __tzstring (&zone_names[idx]); - ++j; - } + if (__tzname[1 - dst] != NULL) + break; + } - i = type_idxs[i - 1]; - } + ++j; + } - return &types[i]; -} - -void -__tzfile_compute (time_t timer, int use_localtime, - long int *leap_correct, int *leap_hit, - struct tm *tp) -{ - register size_t i; + i = type_idxs[i - 1]; + } - if (use_localtime) - { - struct ttinfo *info = find_transition (timer); + struct ttinfo *info = &types[i]; __daylight = rule_stdoff != rule_dstoff; __timezone = -rule_stdoff; diff --git a/time/tzset.c b/time/tzset.c index 0479abb38a..27efef0f7a 100644 --- a/time/tzset.c +++ b/time/tzset.c @@ -1,4 +1,4 @@ -/* Copyright (C) 1991-2002,2003,2004 Free Software Foundation, Inc. +/* Copyright (C) 1991-2002,2003,2004,2007 Free Software Foundation, Inc. This file is part of the GNU C Library. The GNU C Library is free software; you can redistribute it and/or @@ -70,7 +70,6 @@ static tz_rule tz_rules[2]; static void compute_change (tz_rule *rule, int year) __THROW internal_function; -static void tz_compute (const struct tm *tm) __THROW internal_function; static void tzset_internal (int always, int explicit) __THROW internal_function; @@ -92,7 +91,7 @@ __tzstring (const char *s) { char *p; struct tzstring_l *t, *u, *new; - size_t len = strlen(s); + size_t len = strlen (s); /* Walk the list and look for a match. If this string is the same as the end of an already-allocated string, it can share space. */ @@ -140,80 +139,34 @@ __tzname_max () static char *old_tz; -/* Interpret the TZ envariable. */ static void internal_function -tzset_internal (always, explicit) - int always; - int explicit; +update_vars (void) +{ + __daylight = tz_rules[0].offset != tz_rules[1].offset; + __timezone = -tz_rules[0].offset; + __tzname[0] = (char *) tz_rules[0].name; + __tzname[1] = (char *) tz_rules[1].name; + + /* Keep __tzname_cur_max up to date. */ + size_t len0 = strlen (__tzname[0]); + size_t len1 = strlen (__tzname[1]); + if (len0 > __tzname_cur_max) + __tzname_cur_max = len0; + if (len1 > __tzname_cur_max) + __tzname_cur_max = len1; +} + +/* Parse the POSIX TZ-style string. */ +void +__tzset_parse_tz (tz) + const char *tz; { - static int is_initialized; - register const char *tz; register size_t l; char *tzbuf; unsigned short int hh, mm, ss; unsigned short int whichrule; - if (is_initialized && !always) - return; - is_initialized = 1; - - /* Examine the TZ environment variable. */ - tz = getenv ("TZ"); - if (tz == NULL && !explicit) - /* Use the site-wide default. This is a file name which means we - would not see changes to the file if we compare only the file - name for change. We want to notice file changes if tzset() has - been called explicitly. Leave TZ as NULL in this case. */ - tz = TZDEFAULT; - if (tz && *tz == '\0') - /* User specified the empty string; use UTC explicitly. */ - tz = "Universal"; - - /* A leading colon means "implementation defined syntax". - We ignore the colon and always use the same algorithm: - try a data file, and if none exists parse the 1003.1 syntax. */ - if (tz && *tz == ':') - ++tz; - - /* Check whether the value changes since the last run. */ - if (old_tz != NULL && tz != NULL && strcmp (tz, old_tz) == 0) - /* No change, simply return. */ - return; - - if (tz == NULL) - /* No user specification; use the site-wide default. */ - tz = TZDEFAULT; - - tz_rules[0].name = NULL; - tz_rules[1].name = NULL; - - /* Save the value of `tz'. */ - if (old_tz != NULL) - free (old_tz); - old_tz = tz ? __strdup (tz) : NULL; - - /* Try to read a data file. */ - __tzfile_read (tz, 0, NULL); - if (__use_tzfile) - return; - - /* No data file found. Default to UTC if nothing specified. */ - - if (tz == NULL || *tz == '\0' - || (TZDEFAULT != NULL && strcmp (tz, TZDEFAULT) == 0)) - { - tz_rules[0].name = tz_rules[1].name = "UTC"; - tz_rules[0].type = tz_rules[1].type = J0; - tz_rules[0].m = tz_rules[0].n = tz_rules[0].d = 0; - tz_rules[1].m = tz_rules[1].n = tz_rules[1].d = 0; - tz_rules[0].secs = tz_rules[1].secs = 0; - tz_rules[0].offset = tz_rules[1].offset = 0L; - tz_rules[0].change = tz_rules[1].change = (time_t) -1; - tz_rules[0].computed_for = tz_rules[1].computed_for = 0; - goto out; - } - /* Clear out old state and reset to unnamed UTC. */ memset (tz_rules, 0, sizeof tz_rules); tz_rules[0].name = tz_rules[1].name = ""; @@ -413,20 +366,81 @@ tzset_internal (always, explicit) } out: - __daylight = tz_rules[0].offset != tz_rules[1].offset; - __timezone = -tz_rules[0].offset; - __tzname[0] = (char *) tz_rules[0].name; - __tzname[1] = (char *) tz_rules[1].name; + update_vars (); +} - { - /* Keep __tzname_cur_max up to date. */ - size_t len0 = strlen (__tzname[0]); - size_t len1 = strlen (__tzname[1]); - if (len0 > __tzname_cur_max) - __tzname_cur_max = len0; - if (len1 > __tzname_cur_max) - __tzname_cur_max = len1; - } +/* Interpret the TZ envariable. */ +static void +internal_function +tzset_internal (always, explicit) + int always; + int explicit; +{ + static int is_initialized; + register const char *tz; + + if (is_initialized && !always) + return; + is_initialized = 1; + + /* Examine the TZ environment variable. */ + tz = getenv ("TZ"); + if (tz == NULL && !explicit) + /* Use the site-wide default. This is a file name which means we + would not see changes to the file if we compare only the file + name for change. We want to notice file changes if tzset() has + been called explicitly. Leave TZ as NULL in this case. */ + tz = TZDEFAULT; + if (tz && *tz == '\0') + /* User specified the empty string; use UTC explicitly. */ + tz = "Universal"; + + /* A leading colon means "implementation defined syntax". + We ignore the colon and always use the same algorithm: + try a data file, and if none exists parse the 1003.1 syntax. */ + if (tz && *tz == ':') + ++tz; + + /* Check whether the value changes since the last run. */ + if (old_tz != NULL && tz != NULL && strcmp (tz, old_tz) == 0) + /* No change, simply return. */ + return; + + if (tz == NULL) + /* No user specification; use the site-wide default. */ + tz = TZDEFAULT; + + tz_rules[0].name = NULL; + tz_rules[1].name = NULL; + + /* Save the value of `tz'. */ + if (old_tz != NULL) + free (old_tz); + old_tz = tz ? __strdup (tz) : NULL; + + /* Try to read a data file. */ + __tzfile_read (tz, 0, NULL); + if (__use_tzfile) + return; + + /* No data file found. Default to UTC if nothing specified. */ + + if (tz == NULL || *tz == '\0' + || (TZDEFAULT != NULL && strcmp (tz, TZDEFAULT) == 0)) + { + tz_rules[0].name = tz_rules[1].name = "UTC"; + tz_rules[0].type = tz_rules[1].type = J0; + tz_rules[0].m = tz_rules[0].n = tz_rules[0].d = 0; + tz_rules[1].m = tz_rules[1].n = tz_rules[1].d = 0; + tz_rules[0].secs = tz_rules[1].secs = 0; + tz_rules[0].offset = tz_rules[1].offset = 0L; + tz_rules[0].change = tz_rules[1].change = (time_t) -1; + tz_rules[0].computed_for = tz_rules[1].computed_for = 0; + update_vars (); + return; + } + + __tzset_parse_tz (tz); } /* Figure out the exact time (as a time_t) in YEAR @@ -523,13 +537,34 @@ compute_change (rule, year) /* Figure out the correct timezone for TM and set `__tzname', `__timezone', and `__daylight' accordingly. */ -static void +void internal_function -tz_compute (tm) - const struct tm *tm; +__tz_compute (timer, tm, use_localtime) + time_t timer; + struct tm *tm; + int use_localtime; { compute_change (&tz_rules[0], 1900 + tm->tm_year); compute_change (&tz_rules[1], 1900 + tm->tm_year); + + if (use_localtime) + { + int isdst; + + /* We have to distinguish between northern and southern + hemisphere. For the latter the daylight saving time + ends in the next year. */ + if (__builtin_expect (tz_rules[0].change + > tz_rules[1].change, 0)) + isdst = (timer < tz_rules[1].change + || timer >= tz_rules[0].change); + else + isdst = (timer >= tz_rules[0].change + && timer < tz_rules[1].change); + tm->tm_isdst = isdst; + tm->tm_zone = __tzname[isdst]; + tm->tm_gmtoff = tz_rules[isdst].offset; + } } /* Reinterpret the TZ environment variable and set `tzname'. */ @@ -583,35 +618,14 @@ __tz_convert (const time_t *timer, int use_localtime, struct tm *tp) if (! __offtime (timer, 0, tp)) tp = NULL; else - tz_compute (tp); + __tz_compute (*timer, tp, use_localtime); leap_correction = 0L; leap_extra_secs = 0; } if (tp) { - if (use_localtime) - { - if (!__use_tzfile) - { - int isdst; - - /* We have to distinguish between northern and southern - hemisphere. For the latter the daylight saving time - ends in the next year. */ - if (__builtin_expect (tz_rules[0].change - > tz_rules[1].change, 0)) - isdst = (*timer < tz_rules[1].change - || *timer >= tz_rules[0].change); - else - isdst = (*timer >= tz_rules[0].change - && *timer < tz_rules[1].change); - tp->tm_isdst = isdst; - tp->tm_zone = __tzname[isdst]; - tp->tm_gmtoff = tz_rules[isdst].offset; - } - } - else + if (! use_localtime) { tp->tm_isdst = 0; tp->tm_zone = "GMT"; diff --git a/timezone/tst-timezone.c b/timezone/tst-timezone.c index 4c879163cf..00f899b851 100644 --- a/timezone/tst-timezone.c +++ b/timezone/tst-timezone.c @@ -1,4 +1,4 @@ -/* Copyright (C) 1998, 1999, 2000, 2005 Free Software Foundation, Inc. +/* Copyright (C) 1998, 1999, 2000, 2005, 2007 Free Software Foundation, Inc. This file is part of the GNU C Library. Contributed by Andreas Jaeger <aj@suse.de>, 1998. @@ -44,7 +44,6 @@ static const struct test_times tests[] = { "America/Chicago", 1, 21600, {"CST", "CDT" }}, { "America/Indiana/Indianapolis", 1, 18000, {"EST", "EDT" }}, { "America/Los_Angeles", 1, 28800, {"PST", "PDT" }}, - { "Asia/Tokyo", 1, -32400, {"JST", "JDT" }}, { "Pacific/Auckland", 1, -43200, { "NZST", "NZDT" }}, { NULL, 0, 0 } }; |