strptime.c revision 74412
128019Sjoerg/* 228021Sjoerg * Powerdog Industries kindly requests feedback from anyone modifying 328021Sjoerg * this function: 428021Sjoerg * 528021Sjoerg * Date: Thu, 05 Jun 1997 23:17:17 -0400 628021Sjoerg * From: Kevin Ruddy <kevin.ruddy@powerdog.com> 728021Sjoerg * To: James FitzGibbon <james@nexis.net> 828021Sjoerg * Subject: Re: Use of your strptime(3) code (fwd) 928021Sjoerg * 1028021Sjoerg * The reason for the "no mod" clause was so that modifications would 1128021Sjoerg * come back and we could integrate them and reissue so that a wider 1228021Sjoerg * audience could use it (thereby spreading the wealth). This has 1328021Sjoerg * made it possible to get strptime to work on many operating systems. 1428021Sjoerg * I'm not sure why that's "plain unacceptable" to the FreeBSD team. 1528021Sjoerg * 1628021Sjoerg * Anyway, you can change it to "with or without modification" as 1728021Sjoerg * you see fit. Enjoy. 1828021Sjoerg * 1928021Sjoerg * Kevin Ruddy 2028021Sjoerg * Powerdog Industries, Inc. 2128021Sjoerg */ 2228021Sjoerg/* 2328019Sjoerg * Copyright (c) 1994 Powerdog Industries. All rights reserved. 2428019Sjoerg * 2528021Sjoerg * Redistribution and use in source and binary forms, with or without 2628019Sjoerg * modification, are permitted provided that the following conditions 2728019Sjoerg * are met: 2828019Sjoerg * 1. Redistributions of source code must retain the above copyright 2928019Sjoerg * notice, this list of conditions and the following disclaimer. 3028019Sjoerg * 2. Redistributions in binary form must reproduce the above copyright 3128019Sjoerg * notice, this list of conditions and the following disclaimer 3228019Sjoerg * in the documentation and/or other materials provided with the 3328019Sjoerg * distribution. 3428019Sjoerg * 3. All advertising materials mentioning features or use of this 3528019Sjoerg * software must display the following acknowledgement: 3628019Sjoerg * This product includes software developed by Powerdog Industries. 3728019Sjoerg * 4. The name of Powerdog Industries may not be used to endorse or 3828019Sjoerg * promote products derived from this software without specific prior 3928019Sjoerg * written permission. 4028019Sjoerg * 4128019Sjoerg * THIS SOFTWARE IS PROVIDED BY POWERDOG INDUSTRIES ``AS IS'' AND ANY 4228019Sjoerg * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 4328019Sjoerg * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 4428019Sjoerg * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE POWERDOG INDUSTRIES BE 4528019Sjoerg * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 4628019Sjoerg * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 4728019Sjoerg * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 4828019Sjoerg * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 4928019Sjoerg * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 5028019Sjoerg * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 5128019Sjoerg * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 5228019Sjoerg */ 5328019Sjoerg 5428021Sjoerg#ifdef LIBC_RCS 5528021Sjoergstatic const char rcsid[] = 5650476Speter "$FreeBSD: head/lib/libc/stdtime/strptime.c 74412 2001-03-18 11:58:15Z ache $"; 5728021Sjoerg#endif 5828021Sjoerg 5928019Sjoerg#ifndef lint 6028021Sjoerg#ifndef NOID 6128019Sjoergstatic char copyright[] = 6228019Sjoerg"@(#) Copyright (c) 1994 Powerdog Industries. All rights reserved."; 6328021Sjoergstatic char sccsid[] = "@(#)strptime.c 0.1 (Powerdog) 94/03/27"; 6428021Sjoerg#endif /* !defined NOID */ 6528019Sjoerg#endif /* not lint */ 6628019Sjoerg 6771579Sdeischen#include "namespace.h" 6828019Sjoerg#include <time.h> 6928019Sjoerg#include <ctype.h> 7028019Sjoerg#include <string.h> 7148614Sobrien#include <pthread.h> 7271579Sdeischen#include "un-namespace.h" 7371579Sdeischen#include "libc_private.h" 7428021Sjoerg#include "timelocal.h" 7528019Sjoerg 7648614Sobrienstatic char * _strptime(const char *, const char *, struct tm *); 7748614Sobrien 7871579Sdeischenstatic pthread_mutex_t gotgmt_mutex = PTHREAD_MUTEX_INITIALIZER; 7948614Sobrienstatic int got_GMT; 8048614Sobrien 8128021Sjoerg#define asizeof(a) (sizeof (a) / sizeof ((a)[0])) 8228019Sjoerg 8348614Sobrienstatic char * 8448614Sobrien_strptime(const char *buf, const char *fmt, struct tm *tm) 8528019Sjoerg{ 8628021Sjoerg char c; 8728021Sjoerg const char *ptr; 8828021Sjoerg int i, 8928021Sjoerg len; 9053941Sache int Ealternative, Oalternative; 9172168Sphantom struct lc_time_T *tptr = __get_current_time_locale(); 9228019Sjoerg 9328021Sjoerg ptr = fmt; 9428021Sjoerg while (*ptr != 0) { 9528021Sjoerg if (*buf == 0) 9628021Sjoerg break; 9728019Sjoerg 9828021Sjoerg c = *ptr++; 9928019Sjoerg 10028021Sjoerg if (c != '%') { 10128164Sache if (isspace((unsigned char)c)) 10228164Sache while (*buf != 0 && isspace((unsigned char)*buf)) 10328021Sjoerg buf++; 10428021Sjoerg else if (c != *buf++) 10528021Sjoerg return 0; 10628021Sjoerg continue; 10728021Sjoerg } 10828019Sjoerg 10953941Sache Ealternative = 0; 11053941Sache Oalternative = 0; 11153941Sachelabel: 11228021Sjoerg c = *ptr++; 11328021Sjoerg switch (c) { 11428021Sjoerg case 0: 11528021Sjoerg case '%': 11628021Sjoerg if (*buf++ != '%') 11728021Sjoerg return 0; 11828021Sjoerg break; 11928019Sjoerg 12053941Sache case '+': 12172168Sphantom buf = _strptime(buf, tptr->date_fmt, tm); 12228021Sjoerg if (buf == 0) 12328021Sjoerg return 0; 12428021Sjoerg break; 12528019Sjoerg 12653941Sache case 'C': 12753941Sache if (!isdigit((unsigned char)*buf)) 12853941Sache return 0; 12953941Sache 13054316Ssheldonh /* XXX This will break for 3-digit centuries. */ 13154316Ssheldonh len = 2; 13254316Ssheldonh for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) { 13353941Sache i *= 10; 13453941Sache i += *buf - '0'; 13554316Ssheldonh len--; 13653941Sache } 13753941Sache if (i < 19) 13853941Sache return 0; 13953941Sache 14053941Sache tm->tm_year = i * 100 - 1900; 14153941Sache break; 14253941Sache 14328021Sjoerg case 'c': 14472183Sache buf = _strptime(buf, tptr->c_fmt, tm); 14528021Sjoerg if (buf == 0) 14628021Sjoerg return 0; 14728021Sjoerg break; 14828019Sjoerg 14928021Sjoerg case 'D': 15048614Sobrien buf = _strptime(buf, "%m/%d/%y", tm); 15128021Sjoerg if (buf == 0) 15228021Sjoerg return 0; 15328021Sjoerg break; 15428019Sjoerg 15553941Sache case 'E': 15653960Sache if (Ealternative || Oalternative) 15753960Sache break; 15853941Sache Ealternative++; 15953941Sache goto label; 16053941Sache 16153941Sache case 'O': 16253960Sache if (Ealternative || Oalternative) 16353960Sache break; 16453941Sache Oalternative++; 16553941Sache goto label; 16653941Sache 16753960Sache case 'F': 16874412Sache if (!Ealternative) 16974412Sache buf = _strptime(buf, "%Y-%m-%d", tm); 17074412Sache else 17174412Sache buf = _strptime(buf, 17274412Sache *(tptr->md_order) == 'd' ? 17374412Sache "%e %B" : "%B %e", tm); 17474412Sache if (buf == 0) 17574412Sache return 0; 17674412Sache break; 17774412Sache 17853960Sache case 'f': 17953960Sache if (!Ealternative) 18053960Sache break; 18174412Sache buf = _strptime(buf, 18274412Sache *(tptr->md_order) == 'd' ? "%e %b" : "%b %e", 18374412Sache tm); 18453960Sache if (buf == 0) 18553960Sache return 0; 18653960Sache break; 18753960Sache 18828021Sjoerg case 'R': 18948614Sobrien buf = _strptime(buf, "%H:%M", tm); 19028021Sjoerg if (buf == 0) 19128021Sjoerg return 0; 19228021Sjoerg break; 19328019Sjoerg 19428021Sjoerg case 'r': 19573359Sache buf = _strptime(buf, tptr->ampm_fmt, tm); 19628021Sjoerg if (buf == 0) 19728021Sjoerg return 0; 19828021Sjoerg break; 19928019Sjoerg 20028021Sjoerg case 'T': 20148614Sobrien buf = _strptime(buf, "%H:%M:%S", tm); 20228021Sjoerg if (buf == 0) 20328021Sjoerg return 0; 20428021Sjoerg break; 20528019Sjoerg 20628021Sjoerg case 'X': 20772168Sphantom buf = _strptime(buf, tptr->X_fmt, tm); 20828021Sjoerg if (buf == 0) 20928021Sjoerg return 0; 21028021Sjoerg break; 21128019Sjoerg 21228021Sjoerg case 'x': 21372168Sphantom buf = _strptime(buf, tptr->x_fmt, tm); 21428021Sjoerg if (buf == 0) 21528021Sjoerg return 0; 21628021Sjoerg break; 21728019Sjoerg 21828021Sjoerg case 'j': 21928164Sache if (!isdigit((unsigned char)*buf)) 22028021Sjoerg return 0; 22128019Sjoerg 22254316Ssheldonh len = 3; 22354316Ssheldonh for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) { 22428021Sjoerg i *= 10; 22528021Sjoerg i += *buf - '0'; 22654316Ssheldonh len--; 22728021Sjoerg } 22853083Ssheldonh if (i < 1 || i > 366) 22928021Sjoerg return 0; 23028019Sjoerg 23153083Ssheldonh tm->tm_yday = i - 1; 23228021Sjoerg break; 23328019Sjoerg 23428021Sjoerg case 'M': 23528021Sjoerg case 'S': 23628164Sache if (*buf == 0 || isspace((unsigned char)*buf)) 23728021Sjoerg break; 23828019Sjoerg 23928164Sache if (!isdigit((unsigned char)*buf)) 24028021Sjoerg return 0; 24128019Sjoerg 24254316Ssheldonh len = 2; 24354316Ssheldonh for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) { 24428021Sjoerg i *= 10; 24528021Sjoerg i += *buf - '0'; 24654316Ssheldonh len--; 24728021Sjoerg } 24828019Sjoerg 24953083Ssheldonh if (c == 'M') { 25053083Ssheldonh if (i > 59) 25153083Ssheldonh return 0; 25228021Sjoerg tm->tm_min = i; 25353083Ssheldonh } else { 25453083Ssheldonh if (i > 60) 25553083Ssheldonh return 0; 25628021Sjoerg tm->tm_sec = i; 25753083Ssheldonh } 25828019Sjoerg 25928164Sache if (*buf != 0 && isspace((unsigned char)*buf)) 26028164Sache while (*ptr != 0 && !isspace((unsigned char)*ptr)) 26128021Sjoerg ptr++; 26228021Sjoerg break; 26328019Sjoerg 26428021Sjoerg case 'H': 26528021Sjoerg case 'I': 26628021Sjoerg case 'k': 26728021Sjoerg case 'l': 26854316Ssheldonh /* 26954316Ssheldonh * Of these, %l is the only specifier explicitly 27054316Ssheldonh * documented as not being zero-padded. However, 27154316Ssheldonh * there is no harm in allowing zero-padding. 27254316Ssheldonh * 27354316Ssheldonh * XXX The %l specifier may gobble one too many 27454316Ssheldonh * digits if used incorrectly. 27554316Ssheldonh */ 27628164Sache if (!isdigit((unsigned char)*buf)) 27728021Sjoerg return 0; 27828019Sjoerg 27954316Ssheldonh len = 2; 28054316Ssheldonh for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) { 28128021Sjoerg i *= 10; 28228021Sjoerg i += *buf - '0'; 28354316Ssheldonh len--; 28428021Sjoerg } 28528021Sjoerg if (c == 'H' || c == 'k') { 28628021Sjoerg if (i > 23) 28728021Sjoerg return 0; 28854301Ssheldonh } else if (i > 12) 28928021Sjoerg return 0; 29028019Sjoerg 29128021Sjoerg tm->tm_hour = i; 29228019Sjoerg 29328164Sache if (*buf != 0 && isspace((unsigned char)*buf)) 29428164Sache while (*ptr != 0 && !isspace((unsigned char)*ptr)) 29528021Sjoerg ptr++; 29628021Sjoerg break; 29728019Sjoerg 29828021Sjoerg case 'p': 29954316Ssheldonh /* 30054316Ssheldonh * XXX This is bogus if parsed before hour-related 30154316Ssheldonh * specifiers. 30254316Ssheldonh */ 30372168Sphantom len = strlen(tptr->am); 30472168Sphantom if (strncasecmp(buf, tptr->am, len) == 0) { 30528021Sjoerg if (tm->tm_hour > 12) 30628021Sjoerg return 0; 30728021Sjoerg if (tm->tm_hour == 12) 30828021Sjoerg tm->tm_hour = 0; 30928021Sjoerg buf += len; 31028021Sjoerg break; 31128021Sjoerg } 31228019Sjoerg 31372168Sphantom len = strlen(tptr->pm); 31472168Sphantom if (strncasecmp(buf, tptr->pm, len) == 0) { 31528021Sjoerg if (tm->tm_hour > 12) 31628021Sjoerg return 0; 31728021Sjoerg if (tm->tm_hour != 12) 31828021Sjoerg tm->tm_hour += 12; 31928021Sjoerg buf += len; 32028021Sjoerg break; 32128021Sjoerg } 32228019Sjoerg 32328021Sjoerg return 0; 32428019Sjoerg 32528021Sjoerg case 'A': 32628021Sjoerg case 'a': 32772168Sphantom for (i = 0; i < asizeof(tptr->weekday); i++) { 32874409Sache len = strlen(tptr->weekday[i]); 32974409Sache if (strncasecmp(buf, tptr->weekday[i], 33074409Sache len) == 0) 33174409Sache break; 33274409Sache len = strlen(tptr->wday[i]); 33374409Sache if (strncasecmp(buf, tptr->wday[i], 33474409Sache len) == 0) 33574409Sache break; 33628021Sjoerg } 33772168Sphantom if (i == asizeof(tptr->weekday)) 33828021Sjoerg return 0; 33928019Sjoerg 34028021Sjoerg tm->tm_wday = i; 34128021Sjoerg buf += len; 34228021Sjoerg break; 34328019Sjoerg 34453083Ssheldonh case 'U': 34553083Ssheldonh case 'W': 34653083Ssheldonh /* 34753083Ssheldonh * XXX This is bogus, as we can not assume any valid 34853083Ssheldonh * information present in the tm structure at this 34953083Ssheldonh * point to calculate a real value, so just check the 35053083Ssheldonh * range for now. 35153083Ssheldonh */ 35253083Ssheldonh if (!isdigit((unsigned char)*buf)) 35353083Ssheldonh return 0; 35453083Ssheldonh 35554316Ssheldonh len = 2; 35654316Ssheldonh for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) { 35753083Ssheldonh i *= 10; 35853083Ssheldonh i += *buf - '0'; 35954316Ssheldonh len--; 36053083Ssheldonh } 36153083Ssheldonh if (i > 53) 36253083Ssheldonh return 0; 36353083Ssheldonh 36453083Ssheldonh if (*buf != 0 && isspace((unsigned char)*buf)) 36553083Ssheldonh while (*ptr != 0 && !isspace((unsigned char)*ptr)) 36653083Ssheldonh ptr++; 36753083Ssheldonh break; 36853083Ssheldonh 36953083Ssheldonh case 'w': 37053083Ssheldonh if (!isdigit((unsigned char)*buf)) 37153083Ssheldonh return 0; 37253083Ssheldonh 37354316Ssheldonh i = *buf - '0'; 37453083Ssheldonh if (i > 6) 37553083Ssheldonh return 0; 37653083Ssheldonh 37753083Ssheldonh tm->tm_wday = i; 37853083Ssheldonh 37953083Ssheldonh if (*buf != 0 && isspace((unsigned char)*buf)) 38053083Ssheldonh while (*ptr != 0 && !isspace((unsigned char)*ptr)) 38153083Ssheldonh ptr++; 38253083Ssheldonh break; 38353083Ssheldonh 38428021Sjoerg case 'd': 38528021Sjoerg case 'e': 38654316Ssheldonh /* 38754316Ssheldonh * The %e specifier is explicitly documented as not 38854316Ssheldonh * being zero-padded but there is no harm in allowing 38954316Ssheldonh * such padding. 39054316Ssheldonh * 39154316Ssheldonh * XXX The %e specifier may gobble one too many 39254316Ssheldonh * digits if used incorrectly. 39354316Ssheldonh */ 39428164Sache if (!isdigit((unsigned char)*buf)) 39528021Sjoerg return 0; 39628019Sjoerg 39754316Ssheldonh len = 2; 39854316Ssheldonh for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) { 39928021Sjoerg i *= 10; 40028021Sjoerg i += *buf - '0'; 40154316Ssheldonh len--; 40228021Sjoerg } 40328021Sjoerg if (i > 31) 40428021Sjoerg return 0; 40528019Sjoerg 40628021Sjoerg tm->tm_mday = i; 40728019Sjoerg 40828164Sache if (*buf != 0 && isspace((unsigned char)*buf)) 40928164Sache while (*ptr != 0 && !isspace((unsigned char)*ptr)) 41028021Sjoerg ptr++; 41128021Sjoerg break; 41228019Sjoerg 41328021Sjoerg case 'B': 41428021Sjoerg case 'b': 41528021Sjoerg case 'h': 41672168Sphantom for (i = 0; i < asizeof(tptr->month); i++) { 41753941Sache if (Oalternative) { 41853941Sache if (c == 'B') { 41972168Sphantom len = strlen(tptr->alt_month[i]); 42053941Sache if (strncasecmp(buf, 42172168Sphantom tptr->alt_month[i], 42253941Sache len) == 0) 42353941Sache break; 42453941Sache } 42553941Sache } else { 42674409Sache len = strlen(tptr->month[i]); 42774409Sache if (strncasecmp(buf, tptr->month[i], 42874409Sache len) == 0) 42974409Sache break; 43074409Sache len = strlen(tptr->mon[i]); 43174409Sache if (strncasecmp(buf, tptr->mon[i], 43274409Sache len) == 0) 43374409Sache break; 43453941Sache } 43528021Sjoerg } 43672168Sphantom if (i == asizeof(tptr->month)) 43728021Sjoerg return 0; 43828019Sjoerg 43928021Sjoerg tm->tm_mon = i; 44028021Sjoerg buf += len; 44128021Sjoerg break; 44228019Sjoerg 44328021Sjoerg case 'm': 44428164Sache if (!isdigit((unsigned char)*buf)) 44528021Sjoerg return 0; 44628019Sjoerg 44754316Ssheldonh len = 2; 44854316Ssheldonh for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) { 44928021Sjoerg i *= 10; 45028021Sjoerg i += *buf - '0'; 45154316Ssheldonh len--; 45228021Sjoerg } 45328021Sjoerg if (i < 1 || i > 12) 45428021Sjoerg return 0; 45528019Sjoerg 45628021Sjoerg tm->tm_mon = i - 1; 45728019Sjoerg 45828164Sache if (*buf != 0 && isspace((unsigned char)*buf)) 45928164Sache while (*ptr != 0 && !isspace((unsigned char)*ptr)) 46028021Sjoerg ptr++; 46128021Sjoerg break; 46228019Sjoerg 46328021Sjoerg case 'Y': 46428021Sjoerg case 'y': 46528164Sache if (*buf == 0 || isspace((unsigned char)*buf)) 46628021Sjoerg break; 46728019Sjoerg 46828164Sache if (!isdigit((unsigned char)*buf)) 46928021Sjoerg return 0; 47028019Sjoerg 47154316Ssheldonh len = (c == 'Y') ? 4 : 2; 47254316Ssheldonh for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) { 47328021Sjoerg i *= 10; 47428021Sjoerg i += *buf - '0'; 47554316Ssheldonh len--; 47628021Sjoerg } 47728021Sjoerg if (c == 'Y') 47828021Sjoerg i -= 1900; 47946051Swes if (c == 'y' && i < 69) 48046042Swes i += 100; 48128021Sjoerg if (i < 0) 48228021Sjoerg return 0; 48328019Sjoerg 48428021Sjoerg tm->tm_year = i; 48528019Sjoerg 48628164Sache if (*buf != 0 && isspace((unsigned char)*buf)) 48728164Sache while (*ptr != 0 && !isspace((unsigned char)*ptr)) 48828021Sjoerg ptr++; 48928021Sjoerg break; 49048550Sobrien 49148550Sobrien case 'Z': 49248550Sobrien { 49348550Sobrien const char *cp; 49448550Sobrien char *zonestr; 49548550Sobrien 49652860Sache for (cp = buf; *cp && isupper((unsigned char)*cp); ++cp) {/*empty*/} 49748550Sobrien if (cp - buf) { 49848550Sobrien zonestr = alloca(cp - buf + 1); 49948550Sobrien strncpy(zonestr, buf, cp - buf); 50048550Sobrien zonestr[cp - buf] = '\0'; 50148550Sobrien tzset(); 50248550Sobrien if (0 == strcmp(zonestr, "GMT")) { 50348614Sobrien got_GMT = 1; 50448550Sobrien } else if (0 == strcmp(zonestr, tzname[0])) { 50548614Sobrien tm->tm_isdst = 0; 50648550Sobrien } else if (0 == strcmp(zonestr, tzname[1])) { 50748614Sobrien tm->tm_isdst = 1; 50848550Sobrien } else { 50948614Sobrien return 0; 51048550Sobrien } 51148550Sobrien buf += cp - buf; 51248550Sobrien } 51348550Sobrien } 51448550Sobrien break; 51528021Sjoerg } 51628021Sjoerg } 51748614Sobrien return (char *)buf; 51848614Sobrien} 51928019Sjoerg 52048614Sobrien 52148614Sobrienchar * 52248614Sobrienstrptime(const char *buf, const char *fmt, struct tm *tm) 52348614Sobrien{ 52448614Sobrien char *ret; 52548614Sobrien 52671579Sdeischen if (__isthreaded) 52771579Sdeischen _pthread_mutex_lock(&gotgmt_mutex); 52848614Sobrien 52948614Sobrien got_GMT = 0; 53048614Sobrien ret = _strptime(buf, fmt, tm); 53148614Sobrien if (ret && got_GMT) { 53248550Sobrien time_t t = timegm(tm); 53371579Sdeischen localtime_r(&t, tm); 53448614Sobrien got_GMT = 0; 53548550Sobrien } 53648614Sobrien 53771579Sdeischen if (__isthreaded) 53871579Sdeischen _pthread_mutex_unlock(&gotgmt_mutex); 53948614Sobrien 54048614Sobrien return ret; 54128019Sjoerg} 542