strptime.c revision 92986
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 5428019Sjoerg#ifndef lint 5528021Sjoerg#ifndef NOID 5628019Sjoergstatic char copyright[] = 5728019Sjoerg"@(#) Copyright (c) 1994 Powerdog Industries. All rights reserved."; 5828021Sjoergstatic char sccsid[] = "@(#)strptime.c 0.1 (Powerdog) 94/03/27"; 5928021Sjoerg#endif /* !defined NOID */ 6028019Sjoerg#endif /* not lint */ 6192986Sobrien#include <sys/cdefs.h> 6292986Sobrien__FBSDID("$FreeBSD: head/lib/libc/stdtime/strptime.c 92986 2002-03-22 21:53:29Z obrien $"); 6328019Sjoerg 6471579Sdeischen#include "namespace.h" 6528019Sjoerg#include <time.h> 6628019Sjoerg#include <ctype.h> 6779664Sdd#include <limits.h> 6879664Sdd#include <stdlib.h> 6928019Sjoerg#include <string.h> 7048614Sobrien#include <pthread.h> 7171579Sdeischen#include "un-namespace.h" 7271579Sdeischen#include "libc_private.h" 7328021Sjoerg#include "timelocal.h" 7428019Sjoerg 7548614Sobrienstatic char * _strptime(const char *, const char *, struct tm *); 7648614Sobrien 7771579Sdeischenstatic pthread_mutex_t gotgmt_mutex = PTHREAD_MUTEX_INITIALIZER; 7848614Sobrienstatic int got_GMT; 7948614Sobrien 8028021Sjoerg#define asizeof(a) (sizeof (a) / sizeof ((a)[0])) 8128019Sjoerg 8248614Sobrienstatic char * 8348614Sobrien_strptime(const char *buf, const char *fmt, struct tm *tm) 8428019Sjoerg{ 8528021Sjoerg char c; 8628021Sjoerg const char *ptr; 8728021Sjoerg int i, 8828021Sjoerg len; 8953941Sache int Ealternative, Oalternative; 9072168Sphantom struct lc_time_T *tptr = __get_current_time_locale(); 9128019Sjoerg 9228021Sjoerg ptr = fmt; 9328021Sjoerg while (*ptr != 0) { 9428021Sjoerg if (*buf == 0) 9528021Sjoerg break; 9628019Sjoerg 9728021Sjoerg c = *ptr++; 9828019Sjoerg 9928021Sjoerg if (c != '%') { 10028164Sache if (isspace((unsigned char)c)) 10128164Sache while (*buf != 0 && isspace((unsigned char)*buf)) 10228021Sjoerg buf++; 10328021Sjoerg else if (c != *buf++) 10428021Sjoerg return 0; 10528021Sjoerg continue; 10628021Sjoerg } 10728019Sjoerg 10853941Sache Ealternative = 0; 10953941Sache Oalternative = 0; 11053941Sachelabel: 11128021Sjoerg c = *ptr++; 11228021Sjoerg switch (c) { 11328021Sjoerg case 0: 11428021Sjoerg case '%': 11528021Sjoerg if (*buf++ != '%') 11628021Sjoerg return 0; 11728021Sjoerg break; 11828019Sjoerg 11953941Sache case '+': 12072168Sphantom buf = _strptime(buf, tptr->date_fmt, tm); 12128021Sjoerg if (buf == 0) 12228021Sjoerg return 0; 12328021Sjoerg break; 12428019Sjoerg 12553941Sache case 'C': 12653941Sache if (!isdigit((unsigned char)*buf)) 12753941Sache return 0; 12853941Sache 12954316Ssheldonh /* XXX This will break for 3-digit centuries. */ 13054316Ssheldonh len = 2; 13154316Ssheldonh for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) { 13253941Sache i *= 10; 13353941Sache i += *buf - '0'; 13454316Ssheldonh len--; 13553941Sache } 13653941Sache if (i < 19) 13753941Sache return 0; 13853941Sache 13953941Sache tm->tm_year = i * 100 - 1900; 14053941Sache break; 14153941Sache 14228021Sjoerg case 'c': 14372183Sache buf = _strptime(buf, tptr->c_fmt, tm); 14428021Sjoerg if (buf == 0) 14528021Sjoerg return 0; 14628021Sjoerg break; 14728019Sjoerg 14828021Sjoerg case 'D': 14948614Sobrien buf = _strptime(buf, "%m/%d/%y", tm); 15028021Sjoerg if (buf == 0) 15128021Sjoerg return 0; 15228021Sjoerg break; 15328019Sjoerg 15453941Sache case 'E': 15553960Sache if (Ealternative || Oalternative) 15653960Sache break; 15753941Sache Ealternative++; 15853941Sache goto label; 15953941Sache 16053941Sache case 'O': 16153960Sache if (Ealternative || Oalternative) 16253960Sache break; 16353941Sache Oalternative++; 16453941Sache goto label; 16553941Sache 16653960Sache case 'F': 16774578Sache buf = _strptime(buf, "%Y-%m-%d", tm); 16874412Sache if (buf == 0) 16974412Sache return 0; 17074412Sache break; 17174412Sache 17228021Sjoerg case 'R': 17348614Sobrien buf = _strptime(buf, "%H:%M", tm); 17428021Sjoerg if (buf == 0) 17528021Sjoerg return 0; 17628021Sjoerg break; 17728019Sjoerg 17828021Sjoerg case 'r': 17973359Sache buf = _strptime(buf, tptr->ampm_fmt, tm); 18028021Sjoerg if (buf == 0) 18128021Sjoerg return 0; 18228021Sjoerg break; 18328019Sjoerg 18428021Sjoerg case 'T': 18548614Sobrien buf = _strptime(buf, "%H:%M:%S", tm); 18628021Sjoerg if (buf == 0) 18728021Sjoerg return 0; 18828021Sjoerg break; 18928019Sjoerg 19028021Sjoerg case 'X': 19172168Sphantom buf = _strptime(buf, tptr->X_fmt, tm); 19228021Sjoerg if (buf == 0) 19328021Sjoerg return 0; 19428021Sjoerg break; 19528019Sjoerg 19628021Sjoerg case 'x': 19772168Sphantom buf = _strptime(buf, tptr->x_fmt, tm); 19828021Sjoerg if (buf == 0) 19928021Sjoerg return 0; 20028021Sjoerg break; 20128019Sjoerg 20228021Sjoerg case 'j': 20328164Sache if (!isdigit((unsigned char)*buf)) 20428021Sjoerg return 0; 20528019Sjoerg 20654316Ssheldonh len = 3; 20754316Ssheldonh for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) { 20828021Sjoerg i *= 10; 20928021Sjoerg i += *buf - '0'; 21054316Ssheldonh len--; 21128021Sjoerg } 21253083Ssheldonh if (i < 1 || i > 366) 21328021Sjoerg return 0; 21428019Sjoerg 21553083Ssheldonh tm->tm_yday = i - 1; 21628021Sjoerg break; 21728019Sjoerg 21828021Sjoerg case 'M': 21928021Sjoerg case 'S': 22028164Sache if (*buf == 0 || isspace((unsigned char)*buf)) 22128021Sjoerg break; 22228019Sjoerg 22328164Sache if (!isdigit((unsigned char)*buf)) 22428021Sjoerg return 0; 22528019Sjoerg 22654316Ssheldonh len = 2; 22754316Ssheldonh for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) { 22828021Sjoerg i *= 10; 22928021Sjoerg i += *buf - '0'; 23054316Ssheldonh len--; 23128021Sjoerg } 23228019Sjoerg 23353083Ssheldonh if (c == 'M') { 23453083Ssheldonh if (i > 59) 23553083Ssheldonh return 0; 23628021Sjoerg tm->tm_min = i; 23753083Ssheldonh } else { 23853083Ssheldonh if (i > 60) 23953083Ssheldonh return 0; 24028021Sjoerg tm->tm_sec = i; 24153083Ssheldonh } 24228019Sjoerg 24328164Sache if (*buf != 0 && isspace((unsigned char)*buf)) 24428164Sache while (*ptr != 0 && !isspace((unsigned char)*ptr)) 24528021Sjoerg ptr++; 24628021Sjoerg break; 24728019Sjoerg 24828021Sjoerg case 'H': 24928021Sjoerg case 'I': 25028021Sjoerg case 'k': 25128021Sjoerg case 'l': 25254316Ssheldonh /* 25354316Ssheldonh * Of these, %l is the only specifier explicitly 25454316Ssheldonh * documented as not being zero-padded. However, 25554316Ssheldonh * there is no harm in allowing zero-padding. 25654316Ssheldonh * 25754316Ssheldonh * XXX The %l specifier may gobble one too many 25854316Ssheldonh * digits if used incorrectly. 25954316Ssheldonh */ 26028164Sache if (!isdigit((unsigned char)*buf)) 26128021Sjoerg return 0; 26228019Sjoerg 26354316Ssheldonh len = 2; 26454316Ssheldonh for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) { 26528021Sjoerg i *= 10; 26628021Sjoerg i += *buf - '0'; 26754316Ssheldonh len--; 26828021Sjoerg } 26928021Sjoerg if (c == 'H' || c == 'k') { 27028021Sjoerg if (i > 23) 27128021Sjoerg return 0; 27254301Ssheldonh } else if (i > 12) 27328021Sjoerg return 0; 27428019Sjoerg 27528021Sjoerg tm->tm_hour = i; 27628019Sjoerg 27728164Sache if (*buf != 0 && isspace((unsigned char)*buf)) 27828164Sache while (*ptr != 0 && !isspace((unsigned char)*ptr)) 27928021Sjoerg ptr++; 28028021Sjoerg break; 28128019Sjoerg 28228021Sjoerg case 'p': 28354316Ssheldonh /* 28454316Ssheldonh * XXX This is bogus if parsed before hour-related 28554316Ssheldonh * specifiers. 28654316Ssheldonh */ 28772168Sphantom len = strlen(tptr->am); 28872168Sphantom if (strncasecmp(buf, tptr->am, len) == 0) { 28928021Sjoerg if (tm->tm_hour > 12) 29028021Sjoerg return 0; 29128021Sjoerg if (tm->tm_hour == 12) 29228021Sjoerg tm->tm_hour = 0; 29328021Sjoerg buf += len; 29428021Sjoerg break; 29528021Sjoerg } 29628019Sjoerg 29772168Sphantom len = strlen(tptr->pm); 29872168Sphantom if (strncasecmp(buf, tptr->pm, len) == 0) { 29928021Sjoerg if (tm->tm_hour > 12) 30028021Sjoerg return 0; 30128021Sjoerg if (tm->tm_hour != 12) 30228021Sjoerg tm->tm_hour += 12; 30328021Sjoerg buf += len; 30428021Sjoerg break; 30528021Sjoerg } 30628019Sjoerg 30728021Sjoerg return 0; 30828019Sjoerg 30928021Sjoerg case 'A': 31028021Sjoerg case 'a': 31172168Sphantom for (i = 0; i < asizeof(tptr->weekday); i++) { 31274409Sache len = strlen(tptr->weekday[i]); 31374409Sache if (strncasecmp(buf, tptr->weekday[i], 31474409Sache len) == 0) 31574409Sache break; 31674409Sache len = strlen(tptr->wday[i]); 31774409Sache if (strncasecmp(buf, tptr->wday[i], 31874409Sache len) == 0) 31974409Sache break; 32028021Sjoerg } 32172168Sphantom if (i == asizeof(tptr->weekday)) 32228021Sjoerg return 0; 32328019Sjoerg 32428021Sjoerg tm->tm_wday = i; 32528021Sjoerg buf += len; 32628021Sjoerg break; 32728019Sjoerg 32853083Ssheldonh case 'U': 32953083Ssheldonh case 'W': 33053083Ssheldonh /* 33153083Ssheldonh * XXX This is bogus, as we can not assume any valid 33253083Ssheldonh * information present in the tm structure at this 33353083Ssheldonh * point to calculate a real value, so just check the 33453083Ssheldonh * range for now. 33553083Ssheldonh */ 33653083Ssheldonh if (!isdigit((unsigned char)*buf)) 33753083Ssheldonh return 0; 33853083Ssheldonh 33954316Ssheldonh len = 2; 34054316Ssheldonh for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) { 34153083Ssheldonh i *= 10; 34253083Ssheldonh i += *buf - '0'; 34354316Ssheldonh len--; 34453083Ssheldonh } 34553083Ssheldonh if (i > 53) 34653083Ssheldonh return 0; 34753083Ssheldonh 34853083Ssheldonh if (*buf != 0 && isspace((unsigned char)*buf)) 34953083Ssheldonh while (*ptr != 0 && !isspace((unsigned char)*ptr)) 35053083Ssheldonh ptr++; 35153083Ssheldonh break; 35253083Ssheldonh 35353083Ssheldonh case 'w': 35453083Ssheldonh if (!isdigit((unsigned char)*buf)) 35553083Ssheldonh return 0; 35653083Ssheldonh 35754316Ssheldonh i = *buf - '0'; 35853083Ssheldonh if (i > 6) 35953083Ssheldonh return 0; 36053083Ssheldonh 36153083Ssheldonh tm->tm_wday = i; 36253083Ssheldonh 36353083Ssheldonh if (*buf != 0 && isspace((unsigned char)*buf)) 36453083Ssheldonh while (*ptr != 0 && !isspace((unsigned char)*ptr)) 36553083Ssheldonh ptr++; 36653083Ssheldonh break; 36753083Ssheldonh 36828021Sjoerg case 'd': 36928021Sjoerg case 'e': 37054316Ssheldonh /* 37154316Ssheldonh * The %e specifier is explicitly documented as not 37254316Ssheldonh * being zero-padded but there is no harm in allowing 37354316Ssheldonh * such padding. 37454316Ssheldonh * 37554316Ssheldonh * XXX The %e specifier may gobble one too many 37654316Ssheldonh * digits if used incorrectly. 37754316Ssheldonh */ 37828164Sache if (!isdigit((unsigned char)*buf)) 37928021Sjoerg return 0; 38028019Sjoerg 38154316Ssheldonh len = 2; 38254316Ssheldonh for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) { 38328021Sjoerg i *= 10; 38428021Sjoerg i += *buf - '0'; 38554316Ssheldonh len--; 38628021Sjoerg } 38728021Sjoerg if (i > 31) 38828021Sjoerg return 0; 38928019Sjoerg 39028021Sjoerg tm->tm_mday = i; 39128019Sjoerg 39228164Sache if (*buf != 0 && isspace((unsigned char)*buf)) 39328164Sache while (*ptr != 0 && !isspace((unsigned char)*ptr)) 39428021Sjoerg ptr++; 39528021Sjoerg break; 39628019Sjoerg 39728021Sjoerg case 'B': 39828021Sjoerg case 'b': 39928021Sjoerg case 'h': 40072168Sphantom for (i = 0; i < asizeof(tptr->month); i++) { 40153941Sache if (Oalternative) { 40253941Sache if (c == 'B') { 40372168Sphantom len = strlen(tptr->alt_month[i]); 40453941Sache if (strncasecmp(buf, 40572168Sphantom tptr->alt_month[i], 40653941Sache len) == 0) 40753941Sache break; 40853941Sache } 40953941Sache } else { 41074409Sache len = strlen(tptr->month[i]); 41174409Sache if (strncasecmp(buf, tptr->month[i], 41274409Sache len) == 0) 41374409Sache break; 41474409Sache len = strlen(tptr->mon[i]); 41574409Sache if (strncasecmp(buf, tptr->mon[i], 41674409Sache len) == 0) 41774409Sache break; 41853941Sache } 41928021Sjoerg } 42072168Sphantom if (i == asizeof(tptr->month)) 42128021Sjoerg return 0; 42228019Sjoerg 42328021Sjoerg tm->tm_mon = i; 42428021Sjoerg buf += len; 42528021Sjoerg break; 42628019Sjoerg 42728021Sjoerg case 'm': 42828164Sache if (!isdigit((unsigned char)*buf)) 42928021Sjoerg return 0; 43028019Sjoerg 43154316Ssheldonh len = 2; 43254316Ssheldonh for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) { 43328021Sjoerg i *= 10; 43428021Sjoerg i += *buf - '0'; 43554316Ssheldonh len--; 43628021Sjoerg } 43728021Sjoerg if (i < 1 || i > 12) 43828021Sjoerg return 0; 43928019Sjoerg 44028021Sjoerg tm->tm_mon = i - 1; 44128019Sjoerg 44228164Sache if (*buf != 0 && isspace((unsigned char)*buf)) 44328164Sache while (*ptr != 0 && !isspace((unsigned char)*ptr)) 44428021Sjoerg ptr++; 44528021Sjoerg break; 44628019Sjoerg 44779664Sdd case 's': 44879664Sdd { 44979664Sdd char *cp; 45079664Sdd time_t t; 45179664Sdd 45279664Sdd t = strtol(buf, &cp, 10); 45379664Sdd if (t == LONG_MAX) 45479664Sdd return 0; 45579664Sdd buf = cp; 45679664Sdd gmtime_r(&t, tm); 45779664Sdd got_GMT = 1; 45879664Sdd } 45979664Sdd break; 46079664Sdd 46128021Sjoerg case 'Y': 46228021Sjoerg case 'y': 46328164Sache if (*buf == 0 || isspace((unsigned char)*buf)) 46428021Sjoerg break; 46528019Sjoerg 46628164Sache if (!isdigit((unsigned char)*buf)) 46728021Sjoerg return 0; 46828019Sjoerg 46954316Ssheldonh len = (c == 'Y') ? 4 : 2; 47054316Ssheldonh for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) { 47128021Sjoerg i *= 10; 47228021Sjoerg i += *buf - '0'; 47354316Ssheldonh len--; 47428021Sjoerg } 47528021Sjoerg if (c == 'Y') 47628021Sjoerg i -= 1900; 47746051Swes if (c == 'y' && i < 69) 47846042Swes i += 100; 47928021Sjoerg if (i < 0) 48028021Sjoerg return 0; 48128019Sjoerg 48228021Sjoerg tm->tm_year = i; 48328019Sjoerg 48428164Sache if (*buf != 0 && isspace((unsigned char)*buf)) 48528164Sache while (*ptr != 0 && !isspace((unsigned char)*ptr)) 48628021Sjoerg ptr++; 48728021Sjoerg break; 48848550Sobrien 48948550Sobrien case 'Z': 49048550Sobrien { 49148550Sobrien const char *cp; 49248550Sobrien char *zonestr; 49348550Sobrien 49452860Sache for (cp = buf; *cp && isupper((unsigned char)*cp); ++cp) {/*empty*/} 49548550Sobrien if (cp - buf) { 49648550Sobrien zonestr = alloca(cp - buf + 1); 49748550Sobrien strncpy(zonestr, buf, cp - buf); 49848550Sobrien zonestr[cp - buf] = '\0'; 49948550Sobrien tzset(); 50048550Sobrien if (0 == strcmp(zonestr, "GMT")) { 50148614Sobrien got_GMT = 1; 50248550Sobrien } else if (0 == strcmp(zonestr, tzname[0])) { 50348614Sobrien tm->tm_isdst = 0; 50448550Sobrien } else if (0 == strcmp(zonestr, tzname[1])) { 50548614Sobrien tm->tm_isdst = 1; 50648550Sobrien } else { 50748614Sobrien return 0; 50848550Sobrien } 50948550Sobrien buf += cp - buf; 51048550Sobrien } 51148550Sobrien } 51248550Sobrien break; 51328021Sjoerg } 51428021Sjoerg } 51548614Sobrien return (char *)buf; 51648614Sobrien} 51728019Sjoerg 51848614Sobrien 51948614Sobrienchar * 52048614Sobrienstrptime(const char *buf, const char *fmt, struct tm *tm) 52148614Sobrien{ 52248614Sobrien char *ret; 52348614Sobrien 52471579Sdeischen if (__isthreaded) 52571579Sdeischen _pthread_mutex_lock(&gotgmt_mutex); 52648614Sobrien 52748614Sobrien got_GMT = 0; 52848614Sobrien ret = _strptime(buf, fmt, tm); 52948614Sobrien if (ret && got_GMT) { 53048550Sobrien time_t t = timegm(tm); 53171579Sdeischen localtime_r(&t, tm); 53248614Sobrien got_GMT = 0; 53348550Sobrien } 53448614Sobrien 53571579Sdeischen if (__isthreaded) 53671579Sdeischen _pthread_mutex_unlock(&gotgmt_mutex); 53748614Sobrien 53848614Sobrien return ret; 53928019Sjoerg} 540