Fix CFLAGS/LDFLAGS syntax
[platform/upstream/lrzsz.git] / lib / strftime.c
1 /* Copyright (C) 1991, 92, 93, 94, 95, 96 Free Software Foundation, Inc.
2
3    NOTE: The canonical source of this file is maintained with the GNU C
4    Library.  Bugs can be reported to bug-glibc@prep.ai.mit.edu.
5
6    This program is free software; you can redistribute it and/or modify it
7    under the terms of the GNU General Public License as published by the
8    Free Software Foundation; either version 2, or (at your option) any
9    later version.
10
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15
16    You should have received a copy of the GNU General Public License
17    along with this program; if not, write to the Free Software Foundation,
18    Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
19
20 #ifdef HAVE_CONFIG_H
21 # include <config.h>
22 #endif
23
24 #ifdef _LIBC
25 # define HAVE_LIMITS_H 1
26 # define HAVE_MBLEN 1
27 # define HAVE_MBRLEN 1
28 # define HAVE_STRUCT_ERA_ENTRY 1
29 # define HAVE_TM_GMTOFF 1
30 # define HAVE_TM_ZONE 1
31 # define MULTIBYTE_IS_FORMAT_SAFE 1
32 # define STDC_HEADERS 1
33 # include <ansidecl.h>
34 # include "../locale/localeinfo.h"
35 #endif
36
37 #include <sys/types.h>          /* Some systems define `time_t' here.  */
38
39 #ifdef TIME_WITH_SYS_TIME
40 # include <sys/time.h>
41 # include <time.h>
42 #else
43 # ifdef HAVE_SYS_TIME_H
44 #  include <sys/time.h>
45 # else
46 #  include <time.h>
47 # endif
48 #endif
49
50 #if HAVE_TZNAME
51 extern char *tzname[];
52 #endif
53
54 /* Do multibyte processing if multibytes are supported, unless
55    multibyte sequences are safe in formats.  Multibyte sequences are
56    safe if they cannot contain byte sequences that look like format
57    conversion specifications.  The GNU C Library uses UTF8 multibyte
58    encoding, which is safe for formats, but strftime.c can be used
59    with other C libraries that use unsafe encodings.  */
60 #define DO_MULTIBYTE (HAVE_MBLEN && ! MULTIBYTE_IS_FORMAT_SAFE)
61
62 #if DO_MULTIBYTE
63 # if HAVE_MBRLEN
64 #  include <wchar.h>
65 # else
66    /* Simulate mbrlen with mblen as best we can.  */
67 #  define mbstate_t int
68 #  define mbrlen(s, n, ps) mblen (s, n)
69 #  define mbsinit(ps) (*(ps) == 0)
70 # endif
71   static const mbstate_t mbstate_zero;
72 #endif
73
74 #if HAVE_LIMITS_H
75 # include <limits.h>
76 #endif
77
78 #if STDC_HEADERS
79 # include <stddef.h>
80 # include <stdlib.h>
81 # include <string.h>
82 #else
83 # define memcpy(d, s, n) bcopy (s, d, n)
84 #endif
85
86 #ifndef __P
87 #if defined (__GNUC__) || (defined (__STDC__) && __STDC__)
88 #define __P(args) args
89 #else
90 #define __P(args) ()
91 #endif  /* GCC.  */
92 #endif  /* Not __P.  */
93
94 #ifndef PTR
95 #ifdef __STDC__
96 #define PTR void *
97 #else
98 #define PTR char *
99 #endif
100 #endif
101
102 #ifndef CHAR_BIT
103 #define CHAR_BIT 8
104 #endif
105
106 #define TYPE_SIGNED(t) ((t) -1 < 0)
107
108 /* Bound on length of the string representing an integer value of type t.
109    Subtract one for the sign bit if t is signed;
110    302 / 1000 is log10 (2) rounded up;
111    add one for integer division truncation;
112    add one more for a minus sign if t is signed.  */
113 #define INT_STRLEN_BOUND(t) \
114   ((sizeof (t) * CHAR_BIT - TYPE_SIGNED (t)) * 302 / 100 + 1 + TYPE_SIGNED (t))
115
116 #define TM_YEAR_BASE 1900
117
118 #ifndef __isleap
119 /* Nonzero if YEAR is a leap year (every 4 years,
120    except every 100th isn't, and every 400th is).  */
121 #define __isleap(year)  \
122   ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
123 #endif
124
125
126 #ifdef _LIBC
127 # define gmtime_r __gmtime_r
128 # define localtime_r __localtime_r
129 #else
130 # if ! HAVE_LOCALTIME_R
131 #  if ! HAVE_TM_GMTOFF
132 /* Approximate gmtime_r as best we can in its absence.  */
133 #define gmtime_r my_gmtime_r
134 static struct tm *gmtime_r __P ((const time_t *, struct tm *));
135 static struct tm *
136 gmtime_r (t, tp)
137      const time_t *t;
138      struct tm *tp;
139 {
140   struct tm *l = gmtime (t);
141   if (! l)
142     return 0;
143   *tp = *l;
144   return tp;
145 }
146 #  endif /* ! HAVE_TM_GMTOFF */
147
148 /* Approximate localtime_r as best we can in its absence.  */
149 #define localtime_r my_localtime_r
150 static struct tm *localtime_r __P ((const time_t *, struct tm *));
151 static struct tm *
152 localtime_r (t, tp)
153      const time_t *t;
154      struct tm *tp;
155 {
156   struct tm *l = localtime (t);
157   if (! l)
158     return 0;
159   *tp = *l;
160   return tp;
161 }
162 # endif /* ! HAVE_LOCALTIME_R */
163 #endif /* ! defined (_LIBC) */
164
165
166 #define add(n, f)                                                             \
167   do                                                                          \
168     {                                                                         \
169       i += (n);                                                               \
170       if (i >= maxsize)                                                       \
171         return 0;                                                             \
172       else                                                                    \
173         if (p)                                                                \
174           {                                                                   \
175             f;                                                                \
176             p += (n);                                                         \
177           }                                                                   \
178     } while (0)
179 #define cpy(n, s)       add ((n), memcpy((PTR) p, (PTR) (s), (n)))
180
181 #if ! HAVE_TM_GMTOFF
182 /* Yield the difference between *A and *B,
183    measured in seconds, ignoring leap seconds.  */
184 static int tm_diff __P ((const struct tm *, const struct tm *));
185 static int
186 tm_diff (a, b)
187      const struct tm *a;
188      const struct tm *b;
189 {
190   /* Compute intervening leap days correctly even if year is negative.
191      Take care to avoid int overflow in leap day calculations,
192      but it's OK to assume that A and B are close to each other.  */
193   int a4 = (a->tm_year >> 2) + (TM_YEAR_BASE >> 2) - ! (a->tm_year & 3);
194   int b4 = (b->tm_year >> 2) + (TM_YEAR_BASE >> 2) - ! (b->tm_year & 3);
195   int a100 = a4 / 25 - (a4 % 25 < 0);
196   int b100 = b4 / 25 - (b4 % 25 < 0);
197   int a400 = a100 >> 2;
198   int b400 = b100 >> 2;
199   int intervening_leap_days = (a4 - b4) - (a100 - b100) + (a400 - b400);
200   int years = a->tm_year - b->tm_year;
201   int days = (365 * years + intervening_leap_days
202               + (a->tm_yday - b->tm_yday));
203   return (60 * (60 * (24 * days + (a->tm_hour - b->tm_hour))
204                 + (a->tm_min - b->tm_min))
205           + (a->tm_sec - b->tm_sec));
206 }
207 #endif /* ! HAVE_TM_GMTOFF */
208
209
210
211 /* The number of days from the first day of the first ISO week of this
212    year to the year day YDAY with week day WDAY.  ISO weeks start on
213    Monday; the first ISO week has the year's first Thursday.  YDAY may
214    be as small as YDAY_MINIMUM.  */
215 #define ISO_WEEK_START_WDAY 1 /* Monday */
216 #define ISO_WEEK1_WDAY 4 /* Thursday */
217 #define YDAY_MINIMUM (-366)
218 static int iso_week_days __P ((int, int));
219 #ifdef __GNUC__
220 inline
221 #endif
222 static int
223 iso_week_days (yday, wday)
224      int yday;
225      int wday;
226 {
227   /* Add enough to the first operand of % to make it nonnegative.  */
228   int big_enough_multiple_of_7 = (-YDAY_MINIMUM / 7 + 2) * 7;
229   return (yday
230           - (yday - wday + ISO_WEEK1_WDAY + big_enough_multiple_of_7) % 7
231           + ISO_WEEK1_WDAY - ISO_WEEK_START_WDAY);
232 }
233
234
235 #ifndef _NL_CURRENT
236 static char const weekday_name[][10] =
237   {
238     "Sunday", "Monday", "Tuesday", "Wednesday",
239     "Thursday", "Friday", "Saturday"
240   };
241 static char const month_name[][10] =
242   {
243     "January", "February", "March", "April", "May", "June",
244     "July", "August", "September", "October", "November", "December"
245   };
246 #endif
247
248 /* Write information from TP into S according to the format
249    string FORMAT, writing no more that MAXSIZE characters
250    (including the terminating '\0') and returning number of
251    characters written.  If S is NULL, nothing will be written
252    anywhere, so to determine how many characters would be
253    written, use NULL for S and (size_t) UINT_MAX for MAXSIZE.  */
254 size_t
255 strftime (s, maxsize, format, tp)
256       char *s;
257       size_t maxsize;
258       const char *format;
259       register const struct tm *tp;
260 {
261   int hour12 = tp->tm_hour;
262 #ifdef _NL_CURRENT
263   const char *const a_wkday = _NL_CURRENT (LC_TIME, ABDAY_1 + tp->tm_wday);
264   const char *const f_wkday = _NL_CURRENT (LC_TIME, DAY_1 + tp->tm_wday);
265   const char *const a_month = _NL_CURRENT (LC_TIME, ABMON_1 + tp->tm_mon);
266   const char *const f_month = _NL_CURRENT (LC_TIME, MON_1 + tp->tm_mon);
267   const char *const ampm = _NL_CURRENT (LC_TIME,
268                                         hour12 > 11 ? PM_STR : AM_STR);
269   size_t aw_len = strlen (a_wkday);
270   size_t am_len = strlen (a_month);
271   size_t ap_len = strlen (ampm);
272 #else
273   const char *const f_wkday = weekday_name[tp->tm_wday];
274   const char *const f_month = month_name[tp->tm_mon];
275   const char *const a_wkday = f_wkday;
276   const char *const a_month = f_month;
277   const char *const ampm = "AMPM" + 2 * (hour12 > 11);
278   size_t aw_len = 3;
279   size_t am_len = 3;
280   size_t ap_len = 2;
281 #endif
282   size_t wkday_len = strlen (f_wkday);
283   size_t month_len = strlen (f_month);
284   const char *zone;
285   size_t zonelen;
286   register size_t i = 0;
287   register char *p = s;
288   register const char *f;
289
290   zone = 0;
291 #if HAVE_TM_ZONE
292   zone = (const char *) tp->tm_zone;
293 #endif
294 #if HAVE_TZNAME
295   if (!(zone && *zone) && tp->tm_isdst >= 0)
296     zone = tzname[tp->tm_isdst];
297 #endif
298   if (! zone)
299     zone = "";          /* POSIX.2 requires the empty string here.  */
300
301   zonelen = strlen (zone);
302
303   if (hour12 > 12)
304     hour12 -= 12;
305   else
306     if (hour12 == 0) hour12 = 12;
307
308   for (f = format; *f != '\0'; ++f)
309     {
310       int pad;                  /* Padding for number ('-', '_', or 0).  */
311       int modifier;             /* Field modifier ('E', 'O', or 0).  */
312       int digits;               /* Max digits for numeric format.  */
313       int number_value;         /* Numeric value to be printed.  */
314       int negative_number;      /* 1 if the number is negative.  */
315       const char *subfmt;
316       char *bufp;
317       char buf[1 + (sizeof (int) < sizeof (time_t)
318                     ? INT_STRLEN_BOUND (time_t)
319                     : INT_STRLEN_BOUND (int))];
320
321 #if DO_MULTIBYTE
322
323        switch (*f)
324         {
325         case '%':
326           break;
327
328         case '\a': case '\b': case '\t': case '\n':
329         case '\v': case '\f': case '\r':
330         case ' ': case '!': case '"': case '#': case '&': case'\'':
331         case '(': case ')': case '*': case '+': case ',': case '-':
332         case '.': case '/': case '0': case '1': case '2': case '3':
333         case '4': case '5': case '6': case '7': case '8': case '9':
334         case ':': case ';': case '<': case '=': case '>': case '?':
335         case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
336         case 'G': case 'H': case 'I': case 'J': case 'K': case 'L':
337         case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R':
338         case 'S': case 'T': case 'U': case 'V': case 'W': case 'X':
339         case 'Y': case 'Z': case '[': case'\\': case ']': case '^':
340         case '_': case 'a': case 'b': case 'c': case 'd': case 'e':
341         case 'f': case 'g': case 'h': case 'i': case 'j': case 'k':
342         case 'l': case 'm': case 'n': case 'o': case 'p': case 'q':
343         case 'r': case 's': case 't': case 'u': case 'v': case 'w':
344         case 'x': case 'y': case 'z': case '{': case '|': case '}':
345         case '~':
346           /* The C Standard requires these 98 characters (plus '%') to
347              be in the basic execution character set.  None of these
348              characters can start a multibyte sequence, so they need
349              not be analyzed further.  */
350           add (1, *p = *f);
351           continue;
352
353         default:
354           /* Copy this multibyte sequence until we reach its end, find
355              an error, or come back to the initial shift state.  */
356           {
357             mbstate_t mbstate = mbstate_zero;
358             size_t len = 0;
359
360             do
361               {
362                 size_t bytes = mbrlen (f + len, (size_t) -1, &mbstate);
363
364                 if (bytes == 0)
365                   break;
366
367                 if (bytes == (size_t) -2 || bytes == (size_t) -1)
368                   {
369                     len++;
370                     break;
371                   }
372
373                 len += bytes;
374               }
375             while (! mbsinit (&mbstate));
376
377             cpy (len, f);
378             continue;
379           }
380         }
381
382 #else /* ! DO_MULTIBYTE */
383
384       /* Either multibyte encodings are not supported, or they are
385          safe for formats, so any non-'%' byte can be copied through.  */
386       if (*f != '%')
387         {
388           add (1, *p = *f);
389           continue;
390         }
391
392 #endif /* ! DO_MULTIBYTE */
393
394       /* Check for flags that can modify a number format.  */
395       ++f;
396       switch (*f)
397         {
398         case '_':
399         case '-':
400           pad = *f++;
401           break;
402
403         default:
404           pad = 0;
405           break;
406         }
407
408       /* Check for modifiers.  */
409       switch (*f)
410         {
411         case 'E':
412         case 'O':
413           modifier = *f++;
414           break;
415
416         default:
417           modifier = 0;
418           break;
419         }
420
421       /* Now do the specified format.  */
422       switch (*f)
423         {
424 #define DO_NUMBER(d, v) \
425           digits = d; number_value = v; goto do_number
426 #define DO_NUMBER_SPACEPAD(d, v) \
427           digits = d; number_value = v; goto do_number_spacepad
428
429         case '%':
430           if (modifier != 0)
431             goto bad_format;
432           add (1, *p = *f);
433           break;
434
435         case 'a':
436           if (modifier != 0)
437             goto bad_format;
438           cpy (aw_len, a_wkday);
439           break;
440
441         case 'A':
442           if (modifier != 0)
443             goto bad_format;
444           cpy (wkday_len, f_wkday);
445           break;
446
447         case 'b':
448         case 'h':               /* POSIX.2 extension.  */
449           if (modifier != 0)
450             goto bad_format;
451           cpy (am_len, a_month);
452           break;
453
454         case 'B':
455           if (modifier != 0)
456             goto bad_format;
457           cpy (month_len, f_month);
458           break;
459
460         case 'c':
461           if (modifier == 'O')
462             goto bad_format;
463 #ifdef _NL_CURRENT
464           if (! (modifier == 'E'
465                  && *(subfmt = _NL_CURRENT (LC_TIME, ERA_D_T_FMT)) != '\0'))
466             subfmt = _NL_CURRENT (LC_TIME, D_T_FMT);
467 #else
468           subfmt = "%a %b %e %H:%M:%S %Y";
469 #endif
470
471         subformat:
472           {
473             size_t len = strftime (p, maxsize - i, subfmt, tp);
474             if (len == 0 && *subfmt)
475               return 0;
476             add (len, ;);
477           }
478           break;
479
480         case 'C':               /* POSIX.2 extension.  */
481           if (modifier == 'O')
482             goto bad_format;
483 #if HAVE_STRUCT_ERA_ENTRY
484           if (modifier == 'E')
485             {
486               struct era_entry *era = _nl_get_era_entry (tp);
487               if (era)
488                 {
489                   size_t len = strlen (era->name_fmt);
490                   cpy (len, era->name_fmt);
491                   break;
492                 }
493             }
494 #endif
495           {
496             int year = tp->tm_year + TM_YEAR_BASE;
497             DO_NUMBER (1, year / 100 - (year % 100 < 0));
498           }
499
500         case 'x':
501           if (modifier == 'O')
502             goto bad_format;
503 #ifdef _NL_CURRENT
504           if (! (modifier == 'E'
505                  && *(subfmt = _NL_CURRENT (LC_TIME, ERA_D_FMT)) != '\0'))
506             subfmt = _NL_CURRENT (LC_TIME, D_FMT);
507           goto subformat;
508 #endif
509           /* Fall through.  */
510         case 'D':               /* POSIX.2 extension.  */
511           if (modifier != 0)
512             goto bad_format;
513           subfmt = "%m/%d/%y";
514           goto subformat;
515
516         case 'd':
517           if (modifier == 'E')
518             goto bad_format;
519
520           DO_NUMBER (2, tp->tm_mday);
521
522         case 'e':               /* POSIX.2 extension.  */
523           if (modifier == 'E')
524             goto bad_format;
525
526           DO_NUMBER_SPACEPAD (2, tp->tm_mday);
527
528           /* All numeric formats set DIGITS and NUMBER_VALUE and then
529              jump to one of these two labels.  */
530
531         do_number_spacepad:
532           /* Force `_' flag.  */
533           pad = '_';
534
535         do_number:
536           /* Format the number according to the MODIFIER flag.  */
537
538 #ifdef _NL_CURRENT
539           if (modifier == 'O' && 0 <= number_value)
540             {
541               /* Get the locale specific alternate representation of
542                  the number NUMBER_VALUE.  If none exist NULL is returned.  */
543               const char *cp = _nl_get_alt_digit (number_value);
544
545               if (cp != NULL)
546                 {
547                   size_t digitlen = strlen (cp);
548                   if (digitlen != 0)
549                     {
550                       cpy (digitlen, cp);
551                       break;
552                     }
553                 }
554             }
555 #endif
556           {
557             unsigned int u = number_value;
558
559             bufp = buf + sizeof (buf);
560             negative_number = number_value < 0;
561
562             if (negative_number)
563               u = -u;
564
565             do
566               *--bufp = u % 10 + '0';
567             while ((u /= 10) != 0);
568           }
569
570         do_number_sign_and_padding:
571           if (negative_number)
572             *--bufp = '-';
573
574           if (pad != '-')
575             {
576               int padding = digits - (buf + sizeof (buf) - bufp);
577
578               if (pad == '_')
579                 {
580                   while (0 < padding--)
581                     *--bufp = ' ';
582                 }
583               else
584                 {
585                   bufp += negative_number;
586                   while (0 < padding--)
587                     *--bufp = '0';
588                   if (negative_number)
589                     *--bufp = '-';
590                 }
591             }
592
593           cpy (buf + sizeof (buf) - bufp, bufp);
594           break;
595
596
597         case 'H':
598           if (modifier == 'E')
599             goto bad_format;
600
601           DO_NUMBER (2, tp->tm_hour);
602
603         case 'I':
604           if (modifier == 'E')
605             goto bad_format;
606
607           DO_NUMBER (2, hour12);
608
609         case 'k':               /* GNU extension.  */
610           if (modifier == 'E')
611             goto bad_format;
612
613           DO_NUMBER_SPACEPAD (2, tp->tm_hour);
614
615         case 'l':               /* GNU extension.  */
616           if (modifier == 'E')
617             goto bad_format;
618
619           DO_NUMBER_SPACEPAD (2, hour12);
620
621         case 'j':
622           if (modifier == 'E')
623             goto bad_format;
624
625           DO_NUMBER (3, 1 + tp->tm_yday);
626
627         case 'M':
628           if (modifier == 'E')
629             goto bad_format;
630
631           DO_NUMBER (2, tp->tm_min);
632
633         case 'm':
634           if (modifier == 'E')
635             goto bad_format;
636
637           DO_NUMBER (2, tp->tm_mon + 1);
638
639         case 'n':               /* POSIX.2 extension.  */
640           add (1, *p = '\n');
641           break;
642
643         case 'p':
644           cpy (ap_len, ampm);
645           break;
646
647         case 'R':               /* GNU extension.  */
648           subfmt = "%H:%M";
649           goto subformat;
650
651         case 'r':               /* POSIX.2 extension.  */
652 #ifdef _NL_CURRENT
653           if (*(subfmt = _NL_CURRENT (LC_TIME, T_FMT_AMPM)) == '\0')
654 #endif
655             subfmt = "%I:%M:%S %p";
656           goto subformat;
657
658         case 'S':
659           if (modifier == 'E')
660             goto bad_format;
661
662           DO_NUMBER (2, tp->tm_sec);
663
664         case 's':               /* GNU extension.  */
665           {
666             struct tm ltm;
667             time_t t;
668
669             ltm = *tp;
670             t = mktime (&ltm);
671
672             /* Generate string value for T using time_t arithmetic;
673                this works even if sizeof (long) < sizeof (time_t).  */
674
675             bufp = buf + sizeof (buf);
676             negative_number = t < 0;
677
678             do
679               {
680                 int d = t % 10;
681                 t /= 10;
682
683                 if (negative_number)
684                   {
685                     d = -d;
686
687                     /* Adjust if division truncates to minus infinity.  */
688                     if (0 < -1 % 10 && d < 0)
689                       {
690                         t++;
691                         d += 10;
692                       }
693                   }
694
695                 *--bufp = d + '0';
696               }
697             while (t != 0);
698
699             digits = 1;
700             goto do_number_sign_and_padding;
701           }
702
703         case 'X':
704           if (modifier == 'O')
705             goto bad_format;
706 #ifdef _NL_CURRENT
707           if (! (modifier == 'E'
708                  && *(subfmt = _NL_CURRENT (LC_TIME, ERA_T_FMT)) != '\0'))
709             subfmt = _NL_CURRENT (LC_TIME, T_FMT);
710           goto subformat;
711 #endif
712           /* Fall through.  */
713         case 'T':               /* POSIX.2 extension.  */
714           subfmt = "%H:%M:%S";
715           goto subformat;
716
717         case 't':               /* POSIX.2 extension.  */
718           add (1, *p = '\t');
719           break;
720
721         case 'u':               /* POSIX.2 extension.  */
722           DO_NUMBER (1, (tp->tm_wday - 1 + 7) % 7 + 1);
723
724         case 'U':
725           if (modifier == 'E')
726             goto bad_format;
727
728           DO_NUMBER (2, (tp->tm_yday - tp->tm_wday + 7) / 7);
729
730         case 'V':
731         case 'g':               /* GNU extension.  */
732         case 'G':               /* GNU extension.  */
733           if (modifier == 'E')
734             goto bad_format;
735           {
736             int year = tp->tm_year + TM_YEAR_BASE;
737             int days = iso_week_days (tp->tm_yday, tp->tm_wday);
738
739             if (days < 0)
740               {
741                 /* This ISO week belongs to the previous year.  */
742                 year--;
743                 days = iso_week_days (tp->tm_yday + (365 + __isleap (year)),
744                                       tp->tm_wday);
745               }
746             else
747               {
748                 int d = iso_week_days (tp->tm_yday - (365 + __isleap (year)),
749                                        tp->tm_wday);
750                 if (0 <= d)
751                   {
752                     /* This ISO week belongs to the next year.  */
753                     year++;
754                     days = d;
755                   }
756               }
757
758             switch (*f)
759               {
760               case 'g':
761                 DO_NUMBER (2, (year % 100 + 100) % 100);
762
763               case 'G':
764                 DO_NUMBER (1, year);
765
766               default:
767                 DO_NUMBER (2, days / 7 + 1);
768               }
769           }
770
771         case 'W':
772           if (modifier == 'E')
773             goto bad_format;
774
775           DO_NUMBER (2, (tp->tm_yday - (tp->tm_wday - 1 + 7) % 7 + 7) / 7);
776
777         case 'w':
778           if (modifier == 'E')
779             goto bad_format;
780
781           DO_NUMBER (1, tp->tm_wday);
782
783         case 'Y':
784 #if HAVE_STRUCT_ERA_ENTRY
785           if (modifier == 'E')
786             {
787               struct era_entry *era = _nl_get_era_entry (tp);
788               if (era)
789                 {
790                   subfmt = strchr (era->name_fmt, '\0') + 1;
791                   goto subformat;
792                 }
793             }
794 #endif
795           if (modifier == 'O')
796             goto bad_format;
797           else
798             DO_NUMBER (1, tp->tm_year + TM_YEAR_BASE);
799
800         case 'y':
801 #if HAVE_STRUCT_ERA_ENTRY
802           if (modifier == 'E')
803             {
804               struct era_entry *era = _nl_get_era_entry (tp);
805               if (era)
806                 {
807                   int delta = tp->tm_year - era->start_date[0];
808                   DO_NUMBER (1, (era->offset
809                                  + (era->direction == '-' ? -delta : delta)));
810                 }
811             }
812 #endif
813           DO_NUMBER (2, (tp->tm_year % 100 + 100) % 100);
814
815         case 'Z':
816           cpy (zonelen, zone);
817           break;
818
819         case 'z':               /* GNU extension.  */
820           if (tp->tm_isdst < 0)
821             break;
822
823           {
824             int diff;
825 #if HAVE_TM_GMTOFF
826             diff = tp->tm_gmtoff;
827 #else
828             struct tm gtm;
829             struct tm ltm;
830             time_t lt;
831
832             ltm = *tp;
833             lt = mktime (&ltm);
834
835             if (lt == (time_t) -1)
836               {
837                 /* mktime returns -1 for errors, but -1 is also a
838                    valid time_t value.  Check whether an error really
839                    occurred.  */
840                 struct tm tm;
841                 localtime_r (&lt, &tm);
842
843                 if ((ltm.tm_sec ^ tm.tm_sec)
844                     | (ltm.tm_min ^ tm.tm_min)
845                     | (ltm.tm_hour ^ tm.tm_hour)
846                     | (ltm.tm_mday ^ tm.tm_mday)
847                     | (ltm.tm_mon ^ tm.tm_mon)
848                     | (ltm.tm_year ^ tm.tm_year))
849                   break;
850               }
851
852             if (! gmtime_r (&lt, &gtm))
853               break;
854
855             diff = tm_diff (&ltm, &gtm);
856 #endif
857
858             if (diff < 0)
859               {
860                 add (1, *p = '-');
861                 diff = -diff;
862               }
863             else
864               add (1, *p = '+');
865
866             diff /= 60;
867             DO_NUMBER (4, (diff / 60) * 100 + diff % 60);
868           }
869
870         case '\0':              /* GNU extension: % at end of format.  */
871             --f;
872             /* Fall through.  */
873         default:
874           /* Unknown format; output the format, including the '%',
875              since this is most likely the right thing to do if a
876              multibyte string has been misparsed.  */
877         bad_format:
878           {
879             int flen;
880             for (flen = 1; f[1 - flen] != '%'; flen++)
881               continue;
882             cpy (flen, &f[1 - flen]);
883           }
884           break;
885         }
886     }
887
888   if (p)
889     *p = '\0';
890   return i;
891 }