From: Martin Baute Date: Tue, 29 Mar 2016 06:21:15 +0000 (+0200) Subject: Basic implementation of strftime(), untested. X-Git-Url: https://pd.if.org/git/?p=pdclib;a=commitdiff_plain;h=7517a992da74749b360ee20aa14febcc3cf314d5 Basic implementation of strftime(), untested. --- diff --git a/functions/time/strftime.c b/functions/time/strftime.c index 715a321..eb32a16 100644 --- a/functions/time/strftime.c +++ b/functions/time/strftime.c @@ -5,17 +5,20 @@ */ #include +#include +#include +#include #ifndef REGTEST +/* TODO: Alternative representations / numerals not supported. */ + +/* This implementation's code is highly repetitive, but I did not really + care for putting it into a number of macros / helper functions. +*/ + size_t strftime( char * _PDCLIB_restrict s, size_t maxsize, const char * _PDCLIB_restrict format, const struct tm * _PDCLIB_restrict timeptr ) { - /* - If the total number of resulting characters including the terminating null character is not - more than maxsize, the strftime function returns the number of characters placed - into the array pointed to by s not including the terminating null character. - (i.e., < maxsize) - */ size_t rc = 0; while ( rc < maxsize ) @@ -33,12 +36,12 @@ size_t strftime( char * _PDCLIB_restrict s, size_t maxsize, const char * _PDCLIB } else { - char flag = 0; + /* char flag = 0; */ switch ( *++format ) { case 'E': case 'O': - flag = *format++; + /* flag = *format++; */ break; default: /* EMPTY */ @@ -47,133 +50,477 @@ size_t strftime( char * _PDCLIB_restrict s, size_t maxsize, const char * _PDCLIB switch( *format++ ) { case 'a': - /* tm_wday abbreviated */ - break; + { + /* tm_wday abbreviated */ + const char * day = _PDCLIB_lconv.day_name_abbr[ timeptr->tm_wday ]; + size_t len = strlen( day ); + if ( rc < ( maxsize - len ) ) + { + strcpy( s, day ); + rc += len; + } + else + { + return 0; + } + break; + } case 'A': - /* tm_wday full */ - break; + { + /* tm_wday full */ + const char * day = _PDCLIB_lconv.day_name_full[ timeptr->tm_wday ]; + size_t len = strlen( day ); + if ( rc < ( maxsize - len ) ) + { + strcpy( s, day ); + rc += len; + } + else + { + return 0; + } + break; + } case 'b': case 'h': - /* tm_mon abbreviated */ - break; + { + /* tm_mon abbreviated */ + const char * month = _PDCLIB_lconv.month_name_abbr[ timeptr->tm_mon ]; + size_t len = strlen( month ); + if ( rc < ( maxsize - len ) ) + { + strcpy( s, month ); + rc += len; + } + else + { + return 0; + } + break; + } case 'B': - /* tm_mon full */ - break; + { + /* tm_mon full */ + const char * month = _PDCLIB_lconv.month_name_full[ timeptr->tm_mon ]; + size_t len = strlen( month ); + if ( rc < ( maxsize - len ) ) + { + strcpy( s, month ); + rc += len; + } + else + { + return 0; + } + break; + } case 'c': - /* locale's date / time representation, %a %b %e %T %Y for C locale */ - /* 'E' for locale's alternative representation */ - break; + { + /* locale's date / time representation, %a %b %e %T %Y for C locale */ + /* 'E' for locale's alternative representation */ + size_t count = strftime( s, maxsize - rc, _PDCLIB_lconv.date_time_format, timeptr ); + if ( count == 0 ) + { + return 0; + } + else + { + rc += count; + } + break; + } case 'C': - /* tm_year divided by 100, truncated to decimal (00-99) */ - /* 'E' for base year (period) in locale's alternative representation */ - break; + { + /* tm_year divided by 100, truncated to decimal (00-99) */ + /* 'E' for base year (period) in locale's alternative representation */ + if ( rc < ( maxsize - 2 ) ) + { + div_t period = div( ( timeptr->tm_year / 100 ), 10 ); + *s++ = '0' + period.quot; + *s++ = '0' + period.rem; + rc += 2; + } + else + { + return 0; + } + break; + } case 'd': - /* tm_mday as decimal (01-31) */ - /* 'O' for locale's alternative numeric symbols */ - break; + { + /* tm_mday as decimal (01-31) */ + /* 'O' for locale's alternative numeric symbols */ + if ( rc < ( maxsize - 2 ) ) + { + div_t day = div( timeptr->tm_mday, 10 ); + *s++ = '0' + day.quot; + *s++ = '0' + day.rem; + rc += 2; + } + else + { + return 0; + } + break; + } case 'D': - /* %m/%d/%y */ - break; + { + /* %m/%d/%y */ + size_t count = strftime( s, maxsize - rc, "%m/%d/%y", timeptr ); + if ( count == 0 ) + { + return 0; + } + else + { + rc += count; + } + break; + } case 'e': - /* tm_mday as decimal ( 1-31) */ - /* 'O' for locale's alternative numeric symbols */ - break; + { + /* tm_mday as decimal ( 1-31) */ + /* 'O' for locale's alternative numeric symbols */ + if ( rc < ( maxsize - 2 ) ) + { + div_t day = div( timeptr->tm_mday, 10 ); + *s++ = ( day.quot > 0 ) ? '0' + day.quot : ' '; + *s++ = '0' + day.rem; + rc += 2; + } + else + { + return 0; + } + break; + } case 'F': - /* %Y-%m-%d */ - break; + { + /* %Y-%m-%d */ + size_t count = strftime( s, maxsize - rc, "%Y-%m-%d", timeptr ); + if ( count == 0 ) + { + return 0; + } + else + { + rc += count; + } + break; + } case 'g': - /* last 2 digits of the week-based year as decimal (00-99) */ - break; + { + /* last 2 digits of the week-based year as decimal (00-99) */ + /* TODO: 'g' */ + break; + } case 'G': - /* week-based year as decimal (e.g. 1997) */ - break; + { + /* week-based year as decimal (e.g. 1997) */ + /* TODO: 'G' */ + break; + } case 'H': - /* tm_hour as 24h decimal (00-23) */ - /* 'O' for locale's alternative numeric symbols */ - break; + { + /* tm_hour as 24h decimal (00-23) */ + /* 'O' for locale's alternative numeric symbols */ + if ( rc < ( maxsize - 2 ) ) + { + div_t hour = div( timeptr->tm_hour, 10 ); + *s++ = '0' + hour.quot; + *s++ = '0' + hour.rem; + rc += 2; + } + else + { + return 0; + } + break; + } case 'I': - /* tm_hour as 12h decimal (01-12) */ - /* 'O' for locale's alternative numeric symbols */ - break; + { + /* tm_hour as 12h decimal (01-12) */ + /* 'O' for locale's alternative numeric symbols */ + if ( rc < ( maxsize - 2 ) ) + { + div_t hour = div( ( timeptr->tm_hour + 1 ) % 12, 10 ); + *s++ = '0' + hour.quot; + *s++ = '0' + hour.rem; + rc += 2; + } + else + { + return 0; + } + break; + } case 'j': - /* tm_yday as decimal (001-366) */ - break; + { + /* tm_yday as decimal (001-366) */ + if ( rc < ( maxsize - 3 ) ) + { + div_t yday = div( timeptr->tm_yday, 100 ); + *s++ = '0' + yday.quot; + *s++ = '0' + yday.rem / 10; + *s++ = '0' + yday.rem % 10; + rc += 3; + } + else + { + return 0; + } + break; + } case 'm': - /* tm_mon as decimal (01-12) */ - /* 'O' for locale's alternative numeric symbols */ - break; + { + /* tm_mon as decimal (01-12) */ + /* 'O' for locale's alternative numeric symbols */ + if ( rc < ( maxsize - 2 ) ) + { + div_t mon = div( timeptr->tm_mon + 1, 10 ); + *s++ = '0' + mon.quot; + *s++ = '0' + mon.rem; + rc += 2; + } + else + { + return 0; + } + break; + } case 'M': - /* tm_min as decimal (00-59) */ - /* 'O' for locale's alternative numeric symbols */ - break; + { + /* tm_min as decimal (00-59) */ + /* 'O' for locale's alternative numeric symbols */ + if ( rc < ( maxsize - 2 ) ) + { + div_t min = div( timeptr->tm_min + 1, 10 ); + *s++ = '0' + min.quot; + *s++ = '0' + min.rem; + rc += 2; + } + else + { + return 0; + } + break; + } case 'n': - /* newline */ - break; + { + /* newline */ + *s++ = '\n'; + ++rc; + break; + } case 'p': - /* tm_hour locale's AM/PM designations */ - break; + { + /* tm_hour locale's AM/PM designations */ + const char * designation = _PDCLIB_lconv.am_pm[ timeptr->tm_hour > 11 ]; + size_t len = strlen( designation ); + if ( rc < ( maxsize - len ) ) + { + strcpy( s, designation ); + rc += len; + } + else + { + return 0; + } + break; + } case 'r': - /* tm_hour / tm_min / tm_sec as locale's 12-hour clock time, %I:%M:%S %p for C locale */ - break; + { + /* tm_hour / tm_min / tm_sec as locale's 12-hour clock time, %I:%M:%S %p for C locale */ + size_t count = strftime( s, maxsize - rc, _PDCLIB_lconv.time_format_12h, timeptr ); + if ( count == 0 ) + { + return 0; + } + else + { + rc += count; + } + break; + } case 'R': - /* %H:%M */ - break; + { + /* %H:%M */ + size_t count = strftime( s, maxsize - rc, "%H:%M", timeptr ); + if ( count == 0 ) + { + return 0; + } + else + { + rc += count; + } + break; + } case 'S': - /* tm_sec as decimal (00-60) */ - /* 'O' for locale's alternative numeric symbols */ - break; + { + /* tm_sec as decimal (00-60) */ + /* 'O' for locale's alternative numeric symbols */ + if ( rc < ( maxsize - 2 ) ) + { + div_t sec = div( timeptr->tm_sec + 1, 10 ); + *s++ = '0' + sec.quot; + *s++ = '0' + sec.rem; + rc += 2; + } + else + { + return 0; + } + break; + } case 't': - /* tabulator */ - break; + { + /* tabulator */ + *s++ = '\t'; + ++rc; + break; + } case 'T': - /* %H:%M:%S */ - break; + { + /* %H:%M:%S */ + size_t count = strftime( s, maxsize - rc, "%H:%M:%S", timeptr ); + if ( count == 0 ) + { + return 0; + } + else + { + rc += count; + } + break; + } case 'u': - /* tm_wday as decimal (1-7) with Monday == 1 */ - /* 'O' for locale's alternative numeric symbols */ - break; + { + /* tm_wday as decimal (1-7) with Monday == 1 */ + /* 'O' for locale's alternative numeric symbols */ + *s++ = ( timeptr->tm_wday == 0 ) ? '7' : '0' + timeptr->tm_wday; + ++rc; + break; + } case 'U': - /* week number of the year (first Sunday as the first day of week 1) as decimal (00-53) */ - /* 'O' for locale's alternative numeric symbols */ - break; + { + /* week number of the year (first Sunday as the first day of week 1) as decimal (00-53) */ + /* 'O' for locale's alternative numeric symbols */ + /* TODO: 'U' */ + break; + } case 'V': - /* week number as decimal (01-53) */ - /* 'O' for locale's alternative numeric symbols */ - break; + { + /* week number as decimal (01-53) */ + /* 'O' for locale's alternative numeric symbols */ + /* TODO: 'V' */ + break; + } case 'w': - /* tm_wday as decimal number (0-6) with Sunday == 0 */ - /* 'O' for locale's alternative numeric symbols */ - break; + { + /* tm_wday as decimal number (0-6) with Sunday == 0 */ + /* 'O' for locale's alternative numeric symbols */ + *s++ = '0' + timeptr->tm_wday; + ++rc; + break; + } case 'W': - /* week number of the year (first Monday as the first day of week 1) as decimal (00-53) */ - /* 'O' for locale's alternative numeric symbols */ - break; + { + /* week number of the year (first Monday as the first day of week 1) as decimal (00-53) */ + /* 'O' for locale's alternative numeric symbols */ + /* TODO: 'W' */ + break; + } case 'x': - /* locale's date representation, %m/%d/%y for C locale */ - /* 'E' for locale's alternative representation */ - break; + { + /* locale's date representation, %m/%d/%y for C locale */ + /* 'E' for locale's alternative representation */ + size_t count = strftime( s, maxsize - rc, _PDCLIB_lconv.date_format, timeptr ); + if ( count == 0 ) + { + return 0; + } + else + { + rc += count; + } + break; + } case 'X': - /* locale's time representation, %T for C locale */ - /* 'E' for locale's alternative representation */ - break; + { + /* locale's time representation, %T for C locale */ + /* 'E' for locale's alternative representation */ + size_t count = strftime( s, maxsize - rc, _PDCLIB_lconv.time_format, timeptr ); + if ( count == 0 ) + { + return 0; + } + else + { + rc += count; + } + break; + } case 'y': - /* last 2 digits of tm_year as decimal (00-99) */ - /* 'E' for offset from %EC (year only) in locale's alternative representation */ - /* 'O' for locale's alternative numeric symbols */ - break; + { + /* last 2 digits of tm_year as decimal (00-99) */ + /* 'E' for offset from %EC (year only) in locale's alternative representation */ + /* 'O' for locale's alternative numeric symbols */ + if ( rc < ( maxsize - 2 ) ) + { + div_t year = div( ( timeptr->tm_year % 100 ), 10 ); + *s++ = '0' + year.quot; + *s++ = '0' + year.rem; + rc += 2; + } + else + { + return 0; + } + break; + } case 'Y': - /* tm_year as decimal (e.g. 1997) */ - /* 'E' for locale's alternative representation */ - break; + { + /* tm_year as decimal (e.g. 1997) */ + /* 'E' for locale's alternative representation */ + if ( rc < ( maxsize - 4 ) ) + { + int year = timeptr->tm_year; + + for ( int i = 3; i >= 0; --i ) + { + div_t digit = div( year, 10 ); + s[i] = '0' + digit.rem; + year = digit.quot; + } + + rc += 4; + } + else + { + return 0; + } + break; + } case 'z': - /* tm_isdst / UTC offset in ISO8601 format (e.g. -0430 meaning 4 hours 30 minutes behind Greenwich), or no characters */ - break; + { + /* tm_isdst / UTC offset in ISO8601 format (e.g. -0430 meaning 4 hours 30 minutes behind Greenwich), or no characters */ + /* TODO: 'z' */ + break; + } case 'Z': - /* tm_isdst / locale's time zone name or abbreviation, or no characters */ - break; + { + /* tm_isdst / locale's time zone name or abbreviation, or no characters */ + /* TODO: 'Z' */ + break; + } case '%': - /* '%' character */ - break; + { + /* '%' character */ + *s++ = '%'; + ++rc; + break; + } } } } diff --git a/includes/locale.h b/includes/locale.h index da91382..e2682ac 100644 --- a/includes/locale.h +++ b/includes/locale.h @@ -34,10 +34,15 @@ struct lconv { struct _PDCLIB_ctype_t * ctype; /* internal information */ char * _PDCLIB_errno_texts[_PDCLIB_ERRNO_MAX]; /* strerror() / perror() */ - char * _PDCLIB_month_name_abbr[12]; /* month names, abbreviated */ - char * _PDCLIB_month_name_full[12]; /* month names, full */ - char * _PDCLIB_day_name_abbr[7]; /* weekday names, abbreviated */ - char * _PDCLIB_day_name_full[7]; /* weekday names, full */ + char * month_name_abbr[12]; /* month names, abbreviated */ + char * month_name_full[12]; /* month names, full */ + char * day_name_abbr[7]; /* weekday names, abbreviated */ + char * day_name_full[7]; /* weekday names, full */ + char * date_time_format; /* date / time format for strftime( "%c" ) */ + char * time_format_12h; /* 12-hour time format for strftime( "%r" ) */ + char * date_format; /* date format for strftime( "%x" ) */ + char * time_format; /* time format for strftime( "%X" ) */ + char * am_pm[2]; /* AM / PM designation */ char * decimal_point; /* decimal point character */ char * thousands_sep; /* character for seperating groups of digits */ char * grouping; /* string indicating the size of digit groups */ diff --git a/platform/example/functions/_PDCLIB/_PDCLIB_stdinit.c b/platform/example/functions/_PDCLIB/_PDCLIB_stdinit.c index 9bcf455..f0ac4d3 100644 --- a/platform/example/functions/_PDCLIB/_PDCLIB_stdinit.c +++ b/platform/example/functions/_PDCLIB/_PDCLIB_stdinit.c @@ -361,6 +361,15 @@ struct lconv _PDCLIB_lconv = { (char *)"Friday", (char *)"Saturday" }, + /* date / time format */ (char *)"%a %b %e %T %Y", + /* 12h time format */ (char *)"%I:%M:%S %p", + /* date format */ (char *)"%m/%d/%y", + /* time format */ (char *)"%T", + /* AM / PM designation */ + { + (char *)"AM", + (char *)"PM" + }, /* decimal_point */ (char *)".", /* thousands_sep */ (char *)"", /* grouping */ (char *)"",