|
@@ -0,0 +1,398 @@
|
|
|
+
|
|
|
+This file is part of the GNU C Library.
|
|
|
+
|
|
|
+The GNU C Library is free software; you can redistribute it and/or
|
|
|
+modify it under the terms of the GNU Library General Public License as
|
|
|
+published by the Free Software Foundation; either version 2 of the
|
|
|
+License, or (at your option) any later version.
|
|
|
+
|
|
|
+The GNU C Library is distributed in the hope that it will be useful,
|
|
|
+but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
+Library General Public License for more details.
|
|
|
+
|
|
|
+You should have received a copy of the GNU Library General Public
|
|
|
+License along with the GNU C Library; see the file COPYING.LIB. If
|
|
|
+not, write to the Free Software Foundation, Inc., 675 Mass Ave,
|
|
|
+Cambridge, MA 02139, USA. */
|
|
|
+
|
|
|
+
|
|
|
+#include <stddef.h>
|
|
|
+#include <stdlib.h>
|
|
|
+#include <string.h>
|
|
|
+#include <stdio.h>
|
|
|
+#include <sys/types.h> /* Some systems define `time_t' here. */
|
|
|
+#include <sys/time.h>
|
|
|
+#include <time.h>
|
|
|
+#include <ctype.h>
|
|
|
+#include <limits.h>
|
|
|
+
|
|
|
+static unsigned int week (const struct tm *const, int, int);
|
|
|
+
|
|
|
+#define add(n, f) \
|
|
|
+ do \
|
|
|
+ { \
|
|
|
+ i += (n); \
|
|
|
+ if (i >= maxsize) \
|
|
|
+ return 0; \
|
|
|
+ else \
|
|
|
+ if (p) \
|
|
|
+ { \
|
|
|
+ f; \
|
|
|
+ p += (n); \
|
|
|
+ } \
|
|
|
+ } while (0)
|
|
|
+#define cpy(n, s) add((n), memcpy((void *) p, (void *) (s), (n)))
|
|
|
+
|
|
|
+#ifdef _LIBC
|
|
|
+#define fmt(n, args) add((n), if (sprintf args != (n)) return 0)
|
|
|
+#else
|
|
|
+#define fmt(n, args) add((n), sprintf args; if (strlen (p) != (n)) return 0)
|
|
|
+#endif
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ with weeks starting on STARTING_DAY. */
|
|
|
+static unsigned int week(const struct tm *const tp , int starting_day , int max_preceding )
|
|
|
+{
|
|
|
+ int wday, dl, base;
|
|
|
+
|
|
|
+ wday = tp->tm_wday - starting_day;
|
|
|
+ if (wday < 0)
|
|
|
+ wday += 7;
|
|
|
+
|
|
|
+
|
|
|
+ containing the day specified in TP. */
|
|
|
+ dl = tp->tm_yday - wday;
|
|
|
+
|
|
|
+
|
|
|
+ the week containing January 1st to 1 if this week has more than
|
|
|
+ MAX_PRECEDING days in the new year. For ISO 8601 this number is
|
|
|
+ 3, for the other representation it is 7 (i.e., not to be
|
|
|
+ fulfilled). */
|
|
|
+ base = ((dl + 7) % 7) > max_preceding ? 1 : 0;
|
|
|
+
|
|
|
+
|
|
|
+ compute it according ISO 8601. In this case we have to return 53
|
|
|
+ or 1 if the week containing January 1st has less than 4 days in
|
|
|
+ the new year or not. If DL is not negative we calculate the
|
|
|
+ number of complete weeks for our week (DL / 7) plus 1 (because
|
|
|
+ only for DL < 0 we are in week 0/53 and plus the number of the
|
|
|
+ first week computed in the last step. */
|
|
|
+ return dl < 0 ? (dl < -max_preceding ? 53 : base)
|
|
|
+ : base + 1 + dl / 7;
|
|
|
+}
|
|
|
+
|
|
|
+#ifndef _NL_CURRENT
|
|
|
+static char const weekday_name[][10] =
|
|
|
+ {
|
|
|
+ "Sunday", "Monday", "Tuesday", "Wednesday",
|
|
|
+ "Thursday", "Friday", "Saturday"
|
|
|
+ };
|
|
|
+static char const month_name[][10] =
|
|
|
+ {
|
|
|
+ "January", "February", "March", "April", "May", "June",
|
|
|
+ "July", "August", "September", "October", "November", "December"
|
|
|
+ };
|
|
|
+#endif
|
|
|
+
|
|
|
+
|
|
|
+ string FORMAT, writing no more that MAXSIZE characters
|
|
|
+ (including the terminating '\0') and returning number of
|
|
|
+ characters written. If S is NULL, nothing will be written
|
|
|
+ anywhere, so to determine how many characters would be
|
|
|
+ written, use NULL for S and (size_t) UINT_MAX for MAXSIZE. */
|
|
|
+size_t strftime( char *s , size_t maxsize , const char *format , register const struct tm *tp)
|
|
|
+{
|
|
|
+ int hour12 = tp->tm_hour;
|
|
|
+#ifdef _NL_CURRENT
|
|
|
+ const char *const a_wkday = _NL_CURRENT (LC_TIME, ABDAY_1 + tp->tm_wday);
|
|
|
+ const char *const f_wkday = _NL_CURRENT (LC_TIME, DAY_1 + tp->tm_wday);
|
|
|
+ const char *const a_month = _NL_CURRENT (LC_TIME, ABMON_1 + tp->tm_mon);
|
|
|
+ const char *const f_month = _NL_CURRENT (LC_TIME, MON_1 + tp->tm_mon);
|
|
|
+ const char *const ampm = _NL_CURRENT (LC_TIME,
|
|
|
+ hour12 > 11 ? PM_STR : AM_STR);
|
|
|
+ size_t aw_len = strlen(a_wkday);
|
|
|
+ size_t am_len = strlen(a_month);
|
|
|
+ size_t ap_len = strlen (ampm);
|
|
|
+#else
|
|
|
+ const char *const f_wkday = weekday_name[tp->tm_wday];
|
|
|
+ const char *const f_month = month_name[tp->tm_mon];
|
|
|
+ const char *const a_wkday = f_wkday;
|
|
|
+ const char *const a_month = f_month;
|
|
|
+ const char *const ampm = "AMPM" + 2 * (hour12 > 11);
|
|
|
+ size_t aw_len = 3;
|
|
|
+ size_t am_len = 3;
|
|
|
+ size_t ap_len = 2;
|
|
|
+#endif
|
|
|
+ size_t wkday_len = strlen(f_wkday);
|
|
|
+ size_t month_len = strlen(f_month);
|
|
|
+ const unsigned int y_week0 = week (tp, 0, 7);
|
|
|
+ const unsigned int y_week1 = week (tp, 1, 7);
|
|
|
+ const unsigned int y_week2 = week (tp, 1, 3);
|
|
|
+ const char *zone;
|
|
|
+ size_t zonelen;
|
|
|
+ register size_t i = 0;
|
|
|
+ register char *p = s;
|
|
|
+ register const char *f;
|
|
|
+ char number_fmt[5];
|
|
|
+
|
|
|
+
|
|
|
+ number_fmt[0] = '%';
|
|
|
+
|
|
|
+ zone = 0;
|
|
|
+#if HAVE_TM_ZONE
|
|
|
+ zone = (const char *) tp->tm_zone;
|
|
|
+#endif
|
|
|
+#if HAVE_TZNAME
|
|
|
+ if (!(zone && *zone) && tp->tm_isdst >= 0)
|
|
|
+ zone = tzname[tp->tm_isdst];
|
|
|
+#endif
|
|
|
+ if (!(zone && *zone))
|
|
|
+ zone = "???";
|
|
|
+
|
|
|
+ zonelen = strlen (zone);
|
|
|
+
|
|
|
+ if (hour12 > 12)
|
|
|
+ hour12 -= 12;
|
|
|
+ else
|
|
|
+ if (hour12 == 0) hour12 = 12;
|
|
|
+
|
|
|
+ for (f = format; *f != '\0'; ++f)
|
|
|
+ {
|
|
|
+ enum { pad_zero, pad_space, pad_none } pad;
|
|
|
+ unsigned int maxdigits;
|
|
|
+ unsigned int number_value;
|
|
|
+ const char *subfmt;
|
|
|
+
|
|
|
+#if HAVE_MBLEN
|
|
|
+ if (!isascii(*f))
|
|
|
+ {
|
|
|
+
|
|
|
+ int len = mblen(f, strlen(f));
|
|
|
+ if (len > 0)
|
|
|
+ {
|
|
|
+ cpy(len, f);
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ }
|
|
|
+#endif
|
|
|
+
|
|
|
+ if (*f != '%')
|
|
|
+ {
|
|
|
+ add(1, *p = *f);
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ ++f;
|
|
|
+ switch (*f)
|
|
|
+ {
|
|
|
+ case '_':
|
|
|
+ pad = pad_space;
|
|
|
+ ++f;
|
|
|
+ break;
|
|
|
+ case '-':
|
|
|
+ pad = pad_none;
|
|
|
+ ++f;
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ pad = pad_zero;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ switch (*f)
|
|
|
+ {
|
|
|
+ case '\0':
|
|
|
+ case '%':
|
|
|
+ add(1, *p = *f);
|
|
|
+ break;
|
|
|
+
|
|
|
+ case 'a':
|
|
|
+ cpy(aw_len, a_wkday);
|
|
|
+ break;
|
|
|
+
|
|
|
+ case 'A':
|
|
|
+ cpy(wkday_len, f_wkday);
|
|
|
+ break;
|
|
|
+
|
|
|
+ case 'b':
|
|
|
+ case 'h':
|
|
|
+ cpy(am_len, a_month);
|
|
|
+ break;
|
|
|
+
|
|
|
+ case 'B':
|
|
|
+ cpy(month_len, f_month);
|
|
|
+ break;
|
|
|
+
|
|
|
+ case 'c':
|
|
|
+#ifdef _NL_CURRENT
|
|
|
+ subfmt = _NL_CURRENT (LC_TIME, D_T_FMT);
|
|
|
+#else
|
|
|
+ subfmt = "%a %b %d %H:%M:%S %Z %Y";
|
|
|
+#endif
|
|
|
+ subformat:
|
|
|
+ {
|
|
|
+ size_t len = strftime (p, maxsize - i, subfmt, tp);
|
|
|
+ if (len == 0 && *subfmt)
|
|
|
+ return 0;
|
|
|
+ add(len, );
|
|
|
+ }
|
|
|
+ break;
|
|
|
+
|
|
|
+#define DO_NUMBER(digits, value) \
|
|
|
+ maxdigits = digits; number_value = value; goto do_number
|
|
|
+#define DO_NUMBER_NOPAD(digits, value) \
|
|
|
+ maxdigits = digits; number_value = value; goto do_number_nopad
|
|
|
+
|
|
|
+ case 'C':
|
|
|
+ DO_NUMBER (2, (1900 + tp->tm_year) / 100);
|
|
|
+
|
|
|
+ case 'x':
|
|
|
+#ifdef _NL_CURRENT
|
|
|
+ subfmt = _NL_CURRENT (LC_TIME, D_FMT);
|
|
|
+ goto subformat;
|
|
|
+#endif
|
|
|
+
|
|
|
+ case 'D':
|
|
|
+ subfmt = "%m/%d/%y";
|
|
|
+ goto subformat;
|
|
|
+
|
|
|
+ case 'd':
|
|
|
+ DO_NUMBER (2, tp->tm_mday);
|
|
|
+
|
|
|
+ case 'e':
|
|
|
+#if 0
|
|
|
+ DO_NUMBER_NOPAD (2, tp->tm_mday);
|
|
|
+#else
|
|
|
+ DO_NUMBER (2, tp->tm_mday);
|
|
|
+#endif
|
|
|
+
|
|
|
+
|
|
|
+ jump to one of these two labels. */
|
|
|
+
|
|
|
+ do_number_nopad:
|
|
|
+
|
|
|
+ pad = pad_none;
|
|
|
+
|
|
|
+ do_number:
|
|
|
+ {
|
|
|
+
|
|
|
+
|
|
|
+ register char *nf = &number_fmt[1];
|
|
|
+ int printed;
|
|
|
+
|
|
|
+ switch (pad)
|
|
|
+ {
|
|
|
+ case pad_zero:
|
|
|
+ *nf++ = '0';
|
|
|
+ case pad_space:
|
|
|
+ *nf++ = '0' + maxdigits;
|
|
|
+ case pad_none:
|
|
|
+ *nf++ = 'u';
|
|
|
+ *nf = '\0';
|
|
|
+ }
|
|
|
+
|
|
|
+#ifdef _LIBC
|
|
|
+ if (i + maxdigits >= maxsize)
|
|
|
+ return 0;
|
|
|
+ printed = sprintf (p, number_fmt, number_value);
|
|
|
+ i += printed;
|
|
|
+ p += printed;
|
|
|
+#else
|
|
|
+ add (maxdigits, sprintf (p, number_fmt, number_value);
|
|
|
+ printed = strlen (p));
|
|
|
+#endif
|
|
|
+
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ case 'H':
|
|
|
+ DO_NUMBER (2, tp->tm_hour);
|
|
|
+
|
|
|
+ case 'I':
|
|
|
+ DO_NUMBER (2, hour12);
|
|
|
+
|
|
|
+ case 'k':
|
|
|
+ DO_NUMBER_NOPAD (2, tp->tm_hour);
|
|
|
+
|
|
|
+ case 'l':
|
|
|
+ DO_NUMBER_NOPAD (2, hour12);
|
|
|
+
|
|
|
+ case 'j':
|
|
|
+ DO_NUMBER (3, 1 + tp->tm_yday);
|
|
|
+
|
|
|
+ case 'M':
|
|
|
+ DO_NUMBER (2, tp->tm_min);
|
|
|
+
|
|
|
+ case 'm':
|
|
|
+ DO_NUMBER (2, tp->tm_mon + 1);
|
|
|
+
|
|
|
+ case 'n':
|
|
|
+ add (1, *p = '\n');
|
|
|
+ break;
|
|
|
+
|
|
|
+ case 'p':
|
|
|
+ cpy(ap_len, ampm);
|
|
|
+ break;
|
|
|
+
|
|
|
+ case 'R':
|
|
|
+ subfmt = "%H:%M";
|
|
|
+ goto subformat;
|
|
|
+
|
|
|
+ case 'r':
|
|
|
+ subfmt = "%I:%M:%S %p";
|
|
|
+ goto subformat;
|
|
|
+
|
|
|
+ case 'S':
|
|
|
+ DO_NUMBER (2, tp->tm_sec);
|
|
|
+
|
|
|
+ case 'X':
|
|
|
+#ifdef _NL_CURRENT
|
|
|
+ subfmt = _NL_CURRENT (LC_TIME, T_FMT);
|
|
|
+ goto subformat;
|
|
|
+#endif
|
|
|
+
|
|
|
+ case 'T':
|
|
|
+ subfmt = "%H:%M:%S";
|
|
|
+ goto subformat;
|
|
|
+
|
|
|
+ case 't':
|
|
|
+ add (1, *p = '\t');
|
|
|
+ break;
|
|
|
+
|
|
|
+ case 'U':
|
|
|
+ DO_NUMBER (2, y_week0);
|
|
|
+
|
|
|
+ case 'V':
|
|
|
+ DO_NUMBER (2, y_week2);
|
|
|
+
|
|
|
+ case 'W':
|
|
|
+ DO_NUMBER (2, y_week1);
|
|
|
+
|
|
|
+ case 'w':
|
|
|
+ DO_NUMBER (1, tp->tm_wday);
|
|
|
+
|
|
|
+ case 'Y':
|
|
|
+ DO_NUMBER (4, 1900 + tp->tm_year);
|
|
|
+
|
|
|
+ case 'y':
|
|
|
+ DO_NUMBER (2, tp->tm_year % 100);
|
|
|
+
|
|
|
+ case 'Z':
|
|
|
+ cpy(zonelen, zone);
|
|
|
+ break;
|
|
|
+
|
|
|
+ default:
|
|
|
+
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (p)
|
|
|
+ *p = '\0';
|
|
|
+ return i;
|
|
|
+}
|