qofstrftime.c

00001 /***************************************************************************
00002  *            qofstrftime.c
00003  *
00004  *  Sun May 21 15:59:32 2006
00005  *  Copyright (C) 1991-1999, 2000, 2001, 2003, 2004, 2005, 2006 
00006  *    Free Software Foundation, Inc. 
00007  *
00008  ****************************************************************************/
00009 /*
00010  * This program is free software; you can redistribute it and/or modify
00011  * it under the terms of the GNU General Public License as published by
00012  * the Free Software Foundation; either version 2 of the License, or
00013  * (at your option) any later version.
00014  * 
00015  * This program is distributed in the hope that it will be useful,
00016  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00017  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00018  * GNU General Public License for more details.
00019  * 
00020  * You should have received a copy of the GNU General Public License
00021  * along with this program; if not, write to the Free Software
00022  * Foundation, Inc., 51 Franklin Street, Fifth Floor Boston, MA 02110-1301,  USA
00023  */
00024 
00025 /* 
00026 Modified version of strftime from Debian coreutils package.
00027 
00028 (note that the GNU date command includes only strftime,
00029 the QOF strptime code comes direct from the GNU glibc.)
00030 
00031 1. Removed preprocessor directives that are always true or always false within QOF
00032 2. Extended variables to full 64bit ranges, even on 32bit platforms.
00033 3. Replaced time_t with qt_time to prevent overflow in 2038.
00034 4. Replaced struct tm with QofDate to prevent overflow.
00035 Neil Williams <linux@codehelp.co.uk>
00036 */
00037 
00038 #include "config.h"
00039 #include <stdio.h>
00040 #include <ctype.h>
00041 #include <sys/time.h>
00042 #include <time.h>
00043 #include <wchar.h>
00044 #include <limits.h>
00045 #include <stdlib.h>
00046 #include <string.h>
00047 #include <glib.h>
00048 #include "qof.h"
00049 #include "qofdate-p.h"
00050 
00051 static QofLogModule log_module = QOF_MOD_DATE;
00052 
00053 #define memset_space(P, Len) (memset (P, ' ', Len), (P) += (Len))
00054 #define memset_zero(P, Len) (memset (P, '0', Len), (P) += (Len))
00055 
00056 #define FPRINTFTIME 0
00057 
00058 /* Shift A right by B bits portably, by dividing A by 2**B and
00059 truncating towards minus infinity.  A and B should be free of side
00060 effects, and B should be in the range 0 <= B <= INT_BITS - 2, where
00061 INT_BITS is the number of useful bits in an int.  GNU code can
00062 assume that INT_BITS is at least 32.
00063 
00064 ISO C99 says that A >> B is implementation-defined if A < 0.  Some
00065 implementations (e.g., UNICOS 9.0 on a Cray Y-MP EL) don't shift
00066 right in the usual way when A < 0, so SHR falls back on division if
00067 ordinary A >> B doesn't seem to be the usual signed shift.  */
00068 #define SHR(a, b)       \
00069     (-1 >> 1 == -1      \
00070     ? (a) >> (b)        \
00071     : (a) / (1 << (b)) - ((a) % (1 << (b)) < 0))
00072 
00073 /* Bound on length of the string representing an integer 
00074 type or expression T. Subtract 1 for the sign bit if t is signed; 
00075 log10 (2.0) < 146/485; add 1 for integer division truncation; 
00076 add 1 more for a minus sign if needed.  */
00077 #define INT_strlen _BOUND(t) \
00078     ((sizeof (t) * CHAR_BIT - 1) * 146 / 485 + 2)
00079 
00080 /* IMPORTANT: QofDate does not use 1900 or 1970 as a base, all 
00081  years in QofDate are true values. */
00082 #define TM_YEAR_BASE 0
00083 
00084 #define add(n, f)                                       \
00085     do                                                  \
00086     {                                                   \
00087         gint _n = (n);                                  \
00088         gint _delta = width - _n;                       \
00089         gint _incr = _n + (_delta > 0 ? _delta : 0);    \
00090         if ((size_t) _incr >= maxsize - i)              \
00091             return 0;                                   \
00092         if (p)                                          \
00093         {                                               \
00094             if (digits == 0 && _delta > 0)              \
00095             {                                           \
00096                 if (pad == ('0'))                       \
00097                     memset_zero (p, _delta);            \
00098                 else                                    \
00099                     memset_space (p, _delta);           \
00100             }                                           \
00101             f;                                          \
00102             p += FPRINTFTIME ? 0 : _n;                  \
00103         }                                               \
00104         i += _incr;                                     \
00105     } while (0)
00106 
00107 # define add1(C) add (1, *p = C)
00108 
00109 # define cpy(n, s)                                  \
00110     add ((n),                                       \
00111      if (to_lowcase)                                \
00112        memcpy_lowcase (p, (s), _n);                 \
00113      else if (to_uppcase)                           \
00114        memcpy_uppcase (p, (s), _n);                 \
00115      else                                           \
00116        memcpy ((void *) p, (void const *) (s), _n))
00117 
00118 #define TOUPPER(Ch, L) (islower (Ch) ? toupper (Ch) : (Ch))
00119 #define TOLOWER(Ch, L) (isupper (Ch) ? tolower (Ch) : (Ch))
00120 
00121 /* We don't use `isdigit' here since the locale dependent
00122 interpretation is not what we want here. We only need to accept
00123 the arabic digits in the ASCII range.  One day there is perhaps a
00124 more reliable way to accept other sets of digits.  */
00125 #define ISDIGIT(Ch) ((guint) (Ch) - ('0') <= 9)
00126 /* The number of days from the first day of the first ISO week of this
00127 year to the year day YDAY with week day WDAY.  ISO weeks start on
00128 Monday; the first ISO week has the year's first Thursday.  YDAY may
00129 be as small as YDAY_MINIMUM.  */
00130 #define ISO_WEEK_START_WDAY 1   /* Monday */
00131 #define ISO_WEEK1_WDAY 4        /* Thursday */
00132 #define YDAY_MINIMUM (-366)
00133 
00134 static const mbstate_t mbstate_zero;
00135 const gchar *format_end = NULL;
00136 
00137 static gchar *
00138 memcpy_lowcase (gchar * dest, const gchar * src, size_t len)
00139 {
00140     while (len-- > 0)
00141         dest[len] = TOLOWER ((guchar) src[len], loc);
00142     return dest;
00143 }
00144 
00145 static gchar *
00146 memcpy_uppcase (gchar * dest, const gchar * src, size_t len)
00147 {
00148     while (len-- > 0)
00149         dest[len] = TOUPPER ((guchar) src[len], loc);
00150     return dest;
00151 }
00152 
00153 static gint
00154 iso_week_days (gint yday, gint wday)
00155 {
00156     /* Add enough to the first operand of % to make it nonnegative.  */
00157     gint big_enough_multiple_of_7 = (-YDAY_MINIMUM / 7 + 2) * 7;
00158     return (yday
00159         - (yday - wday + ISO_WEEK1_WDAY + big_enough_multiple_of_7) % 7
00160         + ISO_WEEK1_WDAY - ISO_WEEK_START_WDAY);
00161 }
00162 
00163 size_t
00164 strftime_case (gboolean upcase, gchar * s,
00165     size_t maxsize, const gchar *format, const QofDate *qd, 
00166     gint ut, glong ns)
00167 {
00168     const gchar *zone;
00169     gint hour12 = qd->qd_hour;
00170     size_t i = 0;
00171     gchar *p = s;
00172     const gchar *f;
00173     QofDate copy = *qd;
00174     qd = &copy;
00175     zone = (const gchar *) qd->qd_zone;
00176     if (ut)
00177     {
00178         if (!(zone && *zone))
00179             zone = "GMT";
00180     }
00181     else
00182     {
00183         /* POSIX.1 requires that local time zone information be used as
00184            though strftime called tzset.  */
00185         tzset ();
00186     }
00187 
00188     if (hour12 > 12)
00189         hour12 -= 12;
00190     else if (hour12 == 0)
00191         hour12 = 12;
00192 
00193     for (f = format; *f != '\0'; ++f)
00194     {
00195         gint pad = 0;           /* Padding for number ('-', '_', or 0).  */
00196         gint modifier;          /* Field modifier ('E', 'O', or 0).  */
00197         gint digits = 0;        /* Max digits for numeric format.  */
00198         glong number_value;     /* Numeric value to be printed.  */
00199         guint u_number_value;   /* (unsigned int) number_value.  */
00200         gboolean negative_number;   /* The number is negative.  */
00201         gboolean always_output_a_sign;  /* +/- should always be output.  */
00202         gint tz_colon_mask;     /* Bitmask of where ':' should appear.  */
00203         const gchar *subfmt;
00204         gchar sign_char;
00205         gchar *bufp;
00206         gchar buf[MAX_DATE_BUFFER];
00207         gint width = -1;
00208         gboolean to_lowcase = FALSE;
00209         gboolean to_uppcase = upcase;
00210         size_t colons;
00211         gboolean change_case = FALSE;
00212         gint format_char;
00213 
00214         switch (*f)
00215         {
00216         case ('%'):
00217             break;
00218 
00219         case ('\b'):
00220         case ('\t'):
00221         case ('\n'):
00222         case ('\v'):
00223         case ('\f'):
00224         case ('\r'):
00225         case (' '):
00226         case ('!'):
00227         case ('"'):
00228         case ('#'):
00229         case ('&'):
00230         case ('\''):
00231         case ('('):
00232         case (')'):
00233         case ('*'):
00234         case ('+'):
00235         case (','):
00236         case ('-'):
00237         case ('.'):
00238         case ('/'):
00239         case ('0'):
00240         case ('1'):
00241         case ('2'):
00242         case ('3'):
00243         case ('4'):
00244         case ('5'):
00245         case ('6'):
00246         case ('7'):
00247         case ('8'):
00248         case ('9'):
00249         case (':'):
00250         case (';'):
00251         case ('<'):
00252         case ('='):
00253         case ('>'):
00254         case ('?'):
00255         case ('A'):
00256         case ('B'):
00257         case ('C'):
00258         case ('D'):
00259         case ('E'):
00260         case ('F'):
00261         case ('G'):
00262         case ('H'):
00263         case ('I'):
00264         case ('J'):
00265         case ('K'):
00266         case ('L'):
00267         case ('M'):
00268         case ('N'):
00269         case ('O'):
00270         case ('P'):
00271         case ('Q'):
00272         case ('R'):
00273         case ('S'):
00274         case ('T'):
00275         case ('U'):
00276         case ('V'):
00277         case ('W'):
00278         case ('X'):
00279         case ('Y'):
00280         case ('Z'):
00281         case ('['):
00282         case ('\\'):
00283         case (']'):
00284         case ('^'):
00285         case ('_'):
00286         case ('a'):
00287         case ('b'):
00288         case ('c'):
00289         case ('d'):
00290         case ('e'):
00291         case ('f'):
00292         case ('g'):
00293         case ('h'):
00294         case ('i'):
00295         case ('j'):
00296         case ('k'):
00297         case ('l'):
00298         case ('m'):
00299         case ('n'):
00300         case ('o'):
00301         case ('p'):
00302         case ('q'):
00303         case ('r'):
00304         case ('s'):
00305         case ('t'):
00306         case ('u'):
00307         case ('v'):
00308         case ('w'):
00309         case ('x'):
00310         case ('y'):
00311         case ('z'):
00312         case ('{'):
00313         case ('|'):
00314         case ('}'):
00315         case ('~'):
00316             /* The C Standard requires these 98 characters (plus '%') 
00317             to be in the basic execution character set.  None of 
00318             these characters can start a multibyte sequence, so they 
00319             need not be analyzed further.  */
00320             add1 (*f);
00321             continue;
00322 
00323         default:
00324             /* Copy this multibyte sequence until we reach its end, 
00325             find an error, or come back to the initial shift state.
00326             */
00327             {
00328                 mbstate_t mbstate = mbstate_zero;
00329                 size_t len = 0;
00330                 size_t fsize;
00331 
00332                 if (!format_end)
00333                     format_end = f + strlen (f) + 1;
00334                 fsize = format_end - f;
00335 
00336                 do
00337                 {
00338                     size_t bytes = mbrlen (f + len, fsize - len, &mbstate);
00339 
00340                     if (bytes == 0)
00341                         break;
00342 
00343                     if (bytes == (size_t) - 2)
00344                     {
00345                         len += strlen (f + len);
00346                         break;
00347                     }
00348 
00349                     if (bytes == (size_t) - 1)
00350                     {
00351                         len++;
00352                         break;
00353                     }
00354 
00355                     len += bytes;
00356                 }
00357                 while (!mbsinit (&mbstate));
00358 
00359                 cpy (len, f);
00360                 f += len - 1;
00361                 continue;
00362             }
00363         }
00364 
00365         /* Check for flags that can modify a format.  */
00366         while (1)
00367         {
00368             switch (*++f)
00369             {
00370                 /* This influences the number formats.  */
00371             case ('_'):
00372             case ('-'):
00373             case ('0'):
00374                 pad = *f;
00375                 continue;
00376 
00377                 /* This changes textual output.  */
00378             case ('^'):
00379                 to_uppcase = TRUE;
00380                 continue;
00381             case ('#'):
00382                 change_case = TRUE;
00383                 continue;
00384 
00385             default:
00386                 break;
00387             }
00388             break;
00389         }
00390 
00391         /* As a GNU extension we allow to specify the field width.  */
00392         if (ISDIGIT (*f))
00393         {
00394             width = 0;
00395             do
00396             {
00397                 if (width > INT_MAX / 10
00398                     || (width == INT_MAX / 10
00399                         && *f - ('0') > INT_MAX % 10))
00400                     /* Avoid overflow.  */
00401                     width = INT_MAX;
00402                 else
00403                 {
00404                     width *= 10;
00405                     width += *f - ('0');
00406                 }
00407                 ++f;
00408             }
00409             while (ISDIGIT (*f));
00410         }
00411 
00412         /* Check for modifiers.  */
00413         switch (*f)
00414         {
00415         case ('E'):
00416         case ('O'):
00417             modifier = *f++;
00418             break;
00419 
00420         default:
00421             modifier = 0;
00422             break;
00423         }
00424 
00425         /* Now do the specified format.  */
00426         format_char = *f;
00427         switch (format_char)
00428         {
00429 #define DO_NUMBER(d, v) \
00430       digits = d;     \
00431       number_value = v; goto do_number
00432 #define DO_SIGNED_NUMBER(d, negative, v) \
00433       digits = d;       \
00434       negative_number = negative;  \
00435       u_number_value = v; goto do_signed_number
00436 
00437 /* The mask is not what you might think.
00438 When the ordinal i'th bit is set, insert a colon
00439 before the i'th digit of the time zone representation.  */
00440 #define DO_TZ_OFFSET(d, negative, mask, v)  \
00441     digits = d;                             \
00442     negative_number = negative;             \
00443     tz_colon_mask = mask;                   \
00444     u_number_value = v; goto do_tz_offset
00445 #define DO_NUMBER_SPACEPAD(d, v)            \
00446 digits = d;                                 \
00447     number_value = v; goto do_number_spacepad
00448 
00449         case ('%'):
00450             if (modifier != 0)
00451                 goto bad_format;
00452             add1 (*f);
00453             break;
00454 
00455         case ('a'):
00456             if (modifier != 0)
00457                 goto bad_format;
00458             if (change_case)
00459             {
00460                 to_uppcase = TRUE;
00461                 to_lowcase = FALSE;
00462             }
00463             goto underlying_strftime;
00464 
00465         case 'A':
00466             if (modifier != 0)
00467                 goto bad_format;
00468             if (change_case)
00469             {
00470                 to_uppcase = TRUE;
00471                 to_lowcase = FALSE;
00472             }
00473             goto underlying_strftime;
00474 
00475         case ('b'):
00476         case ('h'):
00477             if (change_case)
00478             {
00479                 to_uppcase = TRUE;
00480                 to_lowcase = FALSE;
00481             }
00482             if (modifier != 0)
00483                 goto bad_format;
00484             goto underlying_strftime;
00485 
00486         case ('B'):
00487             if (modifier != 0)
00488                 goto bad_format;
00489             if (change_case)
00490             {
00491                 to_uppcase = TRUE;
00492                 to_lowcase = FALSE;
00493             }
00494             goto underlying_strftime;
00495 
00496         case ('c'):
00497             if (modifier == ('O'))
00498                 goto bad_format;
00499             goto underlying_strftime;
00500 
00501             subformat:
00502             {
00503                 size_t len = strftime_case (to_uppcase,
00504                     NULL, ((size_t) - 1),
00505                     subfmt, qd, ut, ns);
00506                 add (len, strftime_case (to_uppcase, p,
00507                         (maxsize - i), subfmt, qd, ut, ns));
00508             }
00509             break;
00510 
00511           underlying_strftime:
00512             {
00513                 /* try to handle locale-specific formats */
00514                 gchar ufmt[5];
00515                 gchar *u = ufmt;
00516                 gchar ubuf[1024];   /* enough for any single format in practice */
00517                 size_t len;
00518                 /* Make sure we're calling the actual underlying strftime.
00519                 In some cases, config.h contains something like
00520                 "#define strftime rpl_strftime".  */
00521 # ifdef strftime
00522 #  undef strftime
00523                 size_t strftime ();
00524 #endif
00525 
00526                 /* The space helps distinguish strftime failure from 
00527                 empty output.  */
00528                 *u++ = ' ';
00529                 *u++ = '%';
00530                 if (modifier != 0)
00531                     *u++ = modifier;
00532                 *u++ = format_char;
00533                 *u = '\0';
00534                 {
00535                     glong nanosecs;
00536                     struct tm bad;
00537                     if(!qof_date_to_struct_tm ((QofDate*)qd, &bad, &nanosecs))
00538                     {
00539                         PERR (" locale format out of range.");
00540                         break;
00541                     }
00542                     len = strftime (ubuf, sizeof ubuf, ufmt, &bad);
00543                 }
00544                 if (len != 0)
00545                     cpy (len - 1, ubuf + 1);
00546             }
00547             break;
00548 
00549         case ('C'):
00550             if (modifier == ('O'))
00551                 goto bad_format;
00552             if (modifier == ('E'))
00553             {
00554                 goto underlying_strftime;
00555             }
00556 
00557             {
00558                 /* convert to use QofDate->qd_year which is 64bit */
00559                 gint century = qd->qd_year / 100 + TM_YEAR_BASE / 100;
00560                 century -= qd->qd_year % 100 < 0 && 0 < century;
00561                 DO_SIGNED_NUMBER (2, 
00562                     qd->qd_year < -TM_YEAR_BASE, century);
00563             }
00564 
00565         case ('x'):
00566             if (modifier == ('O'))
00567                 goto bad_format;
00568             goto underlying_strftime;
00569         case ('D'):
00570             if (modifier != 0)
00571                 goto bad_format;
00572             subfmt = ("%m/%d/%y");
00573             goto subformat;
00574 
00575         case ('d'):
00576             if (modifier == ('E'))
00577                 goto bad_format;
00578 
00579             DO_NUMBER (2, qd->qd_mday);
00580 
00581         case ('e'):
00582             if (modifier == ('E'))
00583                 goto bad_format;
00584 
00585             DO_NUMBER_SPACEPAD (2, qd->qd_mday);
00586 
00587             /* All numeric formats set DIGITS and NUMBER_VALUE (or U_NUMBER_VALUE)
00588             and then jump to one of these labels.  */
00589             do_tz_offset:
00590             always_output_a_sign = TRUE;
00591             goto do_number_body;
00592 
00593             do_number_spacepad:
00594             /* Force `_' flag unless overridden by `0' or `-' flag.  */
00595             if (pad != ('0') && pad != ('-'))
00596                 pad = ('_');
00597 
00598             do_number:
00599             /* Format NUMBER_VALUE according to the MODIFIER flag.  */
00600             negative_number = number_value < 0;
00601             u_number_value = number_value;
00602 
00603             do_signed_number:
00604             always_output_a_sign = FALSE;
00605             tz_colon_mask = 0;
00606 
00607             do_number_body:
00608             /* Format U_NUMBER_VALUE according to the MODIFIER flag.
00609             NEGATIVE_NUMBER is nonzero if the original number was
00610             negative; in this case it was converted directly to
00611             unsigned int (i.e., modulo (UINT_MAX + 1)) without
00612             negating it.  */
00613             if (modifier == ('O') && !negative_number)
00614             {
00615                 goto underlying_strftime;
00616             }
00617 
00618             bufp = buf + sizeof (buf) / sizeof (buf[0]);
00619 
00620             if (negative_number)
00621                 u_number_value = -u_number_value;
00622 
00623             do
00624             {
00625                 if (tz_colon_mask & 1)
00626                     *--bufp = ':';
00627                 tz_colon_mask >>= 1;
00628                 *--bufp = u_number_value % 10 + ('0');
00629                 u_number_value /= 10;
00630             }
00631             while (u_number_value != 0 || tz_colon_mask != 0);
00632 
00633           do_number_sign_and_padding:
00634             if (digits < width)
00635                 digits = width;
00636 
00637             sign_char = (negative_number ? ('-')
00638                 : always_output_a_sign ? ('+') : 0);
00639 
00640             if (pad == ('-'))
00641             {
00642                 if (sign_char)
00643                     add1 (sign_char);
00644             }
00645             else
00646             {
00647                 gint padding =
00648                     digits - (buf + (sizeof (buf) / sizeof (buf[0])) -
00649                     bufp) - !!sign_char;
00650 
00651                 if (padding > 0)
00652                 {
00653                     if (pad == ('_'))
00654                     {
00655                         if ((size_t) padding >= maxsize - i)
00656                             return 0;
00657 
00658                         if (p)
00659                             memset_space (p, padding);
00660                         i += padding;
00661                         width = width > padding ? width - padding : 0;
00662                         if (sign_char)
00663                             add1 (sign_char);
00664                     }
00665                     else
00666                     {
00667                         if ((size_t) digits >= maxsize - i)
00668                             return 0;
00669 
00670                         if (sign_char)
00671                             add1 (sign_char);
00672 
00673                         if (p)
00674                             memset_zero (p, padding);
00675                         i += padding;
00676                         width = 0;
00677                     }
00678                 }
00679                 else
00680                 {
00681                     if (sign_char)
00682                         add1 (sign_char);
00683                 }
00684             }
00685 
00686             cpy (buf + sizeof (buf) / sizeof (buf[0]) - bufp, bufp);
00687             break;
00688 
00689         case ('F'):
00690             if (modifier != 0)
00691                 goto bad_format;
00692             subfmt = ("%Y-%m-%d");
00693             goto subformat;
00694 
00695         case ('H'):
00696             if (modifier == ('E'))
00697                 goto bad_format;
00698 
00699             DO_NUMBER (2, qd->qd_hour);
00700 
00701         case ('I'):
00702             if (modifier == ('E'))
00703                 goto bad_format;
00704 
00705             DO_NUMBER (2, hour12);
00706 
00707         case ('k'):         /* GNU extension.  */
00708             if (modifier == ('E'))
00709                 goto bad_format;
00710 
00711             DO_NUMBER_SPACEPAD (2, qd->qd_hour);
00712 
00713         case ('l'):         /* GNU extension.  */
00714             if (modifier == ('E'))
00715                 goto bad_format;
00716 
00717             DO_NUMBER_SPACEPAD (2, hour12);
00718 
00719         case ('j'):
00720             if (modifier == ('E'))
00721                 goto bad_format;
00722 
00723             DO_SIGNED_NUMBER (3, qd->qd_yday < -1, qd->qd_yday + 1U);
00724 
00725         case ('M'):
00726             if (modifier == ('E'))
00727                 goto bad_format;
00728 
00729             DO_NUMBER (2, qd->qd_min);
00730 
00731         case ('m'):
00732             if (modifier == ('E'))
00733                 goto bad_format;
00734 
00735             DO_SIGNED_NUMBER (2, qd->qd_mon < -1, qd->qd_mon);
00736 
00737         case ('N'):         /* GNU extension.  */
00738             if (modifier == ('E'))
00739                 goto bad_format;
00740 
00741             number_value = ns;
00742             if (width == -1)
00743                 width = 9;
00744             else
00745             {
00746                 /* Take an explicit width less than 9 as a precision.  */
00747                 gint j;
00748                 for (j = width; j < 9; j++)
00749                     number_value /= 10;
00750             }
00751 
00752             DO_NUMBER (width, number_value);
00753 
00754         case ('n'):
00755             add1 (('\n'));
00756             break;
00757 
00758         case ('P'):
00759             to_lowcase = TRUE;
00760             format_char = ('p');
00761 
00762         case ('p'):
00763             if (change_case)
00764             {
00765                 to_uppcase = FALSE;
00766                 to_lowcase = TRUE;
00767             }
00768             goto underlying_strftime;
00769 
00770         case ('R'):
00771             subfmt = ("%H:%M");
00772             goto subformat;
00773 
00774         case ('r'):
00775             goto underlying_strftime;
00776 
00777         case ('S'):
00778             if (modifier == ('E'))
00779                 goto bad_format;
00780 
00781             DO_NUMBER (2, qd->qd_sec);
00782 
00783         case ('s'):         /* GNU extension.
00784         number of seconds since the epoch.
00785         basically QofTimeSecs as a string.
00786         */
00787             {
00788                 glong nanosecs;
00789                 QofTime *time;
00790                 QofTimeSecs t;
00791 
00792                 time = qof_date_to_qtime ((QofDate*)qd);
00793                 t = qof_time_get_secs (time);
00794                 nanosecs = qof_time_get_nanosecs (time);
00795 
00796                 /* Generate string value for T using time_t arithmetic;
00797                    this works even if sizeof (long) < sizeof (time_t).  */
00798 
00799                 bufp = buf + sizeof (buf) / sizeof (buf[0]);
00800                 negative_number = t < 0;
00801 
00802                 do
00803                 {
00804                     gint d = t % 10;
00805                     t /= 10;
00806                     *--bufp = (negative_number ? -d : d) + ('0');
00807                 }
00808                 while (t != 0);
00809 
00810                 digits = 1;
00811                 always_output_a_sign = FALSE;
00812                 goto do_number_sign_and_padding;
00813             }
00814 
00815         case ('X'):
00816             if (modifier == ('O'))
00817                 goto bad_format;
00818             goto underlying_strftime;
00819         case ('T'):
00820             subfmt = ("%H:%M:%S");
00821             goto subformat;
00822 
00823         case ('t'):
00824             add1 (('\t'));
00825             break;
00826 
00827         case ('u'):
00828             DO_NUMBER (1, (qd->qd_wday - 1 + 7) % 7 + 1);
00829 
00830         case ('U'):
00831             if (modifier == ('E'))
00832                 goto bad_format;
00833 
00834             DO_NUMBER (2, (qd->qd_yday - qd->qd_wday + 7) / 7);
00835 
00836         case ('V'):
00837         case ('g'):
00838         case ('G'):
00839             if (modifier == ('E'))
00840                 goto bad_format;
00841             {
00842                 gint year_adjust = 0;
00843                 gint days = iso_week_days (qd->qd_yday, qd->qd_wday);
00844 
00845                 if (days < 0)
00846                 {
00847                     /* This ISO week belongs to the previous year.  */
00848                     year_adjust = -1;
00849                     days =
00850                         iso_week_days (qd->qd_yday +
00851                         (365 + qof_date_isleap (qd->qd_year - 1)), 
00852                         qd->qd_wday);
00853                 }
00854                 else
00855                 {
00856                     gint d =
00857                         iso_week_days (qd->qd_yday - (365 +
00858                             qof_date_isleap (qd->qd_year)),
00859                         qd->qd_wday);
00860                     if (0 <= d)
00861                     {
00862                         /* This ISO week belongs to the next year.  */
00863                         year_adjust = 1;
00864                         days = d;
00865                     }
00866                 }
00867 
00868                 switch (*f)
00869                 {
00870                 case ('g'):
00871                     {
00872                         /* use QofDate->qd_year */
00873                         gint yy = (qd->qd_year % 100 + year_adjust) % 100;
00874                         DO_NUMBER (2, (0 <= yy
00875                                 ? yy : qd->qd_year <
00876                                 -TM_YEAR_BASE -
00877                                 year_adjust ? -yy : yy + 100));
00878                     }
00879 
00880                 case ('G'):
00881                     /* use QofDate->qd_year */
00882                     DO_SIGNED_NUMBER (4,
00883                         qd->qd_year <
00884                         -TM_YEAR_BASE - year_adjust,
00885                         (qd->qd_year + (guint) TM_YEAR_BASE +
00886                             year_adjust));
00887 
00888                 default:
00889                     DO_NUMBER (2, days / 7 + 1);
00890                 }
00891             }
00892 
00893         case ('W'):
00894             if (modifier == ('E'))
00895                 goto bad_format;
00896 
00897             DO_NUMBER (2,
00898                 (qd->qd_yday - (qd->qd_wday - 1 + 7) % 7 + 7) / 7);
00899 
00900         case ('w'):
00901             if (modifier == ('E'))
00902                 goto bad_format;
00903 
00904             DO_NUMBER (1, qd->qd_wday);
00905 
00906         case ('Y'):
00907             if (modifier == 'E')
00908             {
00909                 goto underlying_strftime;
00910             }
00911             if (modifier == ('O'))
00912                 goto bad_format;
00913             else
00914                 /* use QofDate->qd_year */
00915                 DO_SIGNED_NUMBER (4, qd->qd_year < -TM_YEAR_BASE,
00916                     qd->qd_year + TM_YEAR_BASE);
00917 
00918         case ('y'):
00919             if (modifier == ('E'))
00920             {
00921                 goto underlying_strftime;
00922             }
00923 
00924             {
00925                 gint64 yy = qd->qd_year % 100;
00926                 if (yy < 0)
00927                     yy = qd->qd_year < -TM_YEAR_BASE ? -yy : yy + 100;
00928                 DO_NUMBER (2, yy);
00929             }
00930 
00931         case ('Z'):
00932             if (change_case)
00933             {
00934                 to_uppcase = FALSE;
00935                 to_lowcase = TRUE;
00936             }
00937 
00938             /* The tzset() call might have changed the value.  */
00939             if (!(zone && *zone) && qd->qd_is_dst >= 0)
00940                 zone = tzname[qd->qd_is_dst != 0];
00941             if (!zone)
00942                 zone = "";
00943 
00944             cpy (strlen (zone), zone);
00945             break;
00946 
00947         case (':'):
00948             /* :, ::, and ::: are valid only just before 'z'.
00949                :::: etc. are rejected later.  */
00950             for (colons = 1; f[colons] == (':'); colons++)
00951                 continue;
00952             if (f[colons] != ('z'))
00953                 goto bad_format;
00954             f += colons;
00955             goto do_z_conversion;
00956 
00957         case ('z'):
00958             colons = 0;
00959 
00960           do_z_conversion:
00961             if (qd->qd_is_dst < 0)
00962                 break;
00963 
00964             {
00965                 gint diff;
00966                 gint hour_diff;
00967                 gint min_diff;
00968                 gint sec_diff;
00969                 diff = qd->qd_gmt_off;
00970                 hour_diff = diff / 60 / 60;
00971                 min_diff = diff / 60 % 60;
00972                 sec_diff = diff % 60;
00973 
00974                 switch (colons)
00975                 {
00976                 case 0:     /* +hhmm */
00977                     DO_TZ_OFFSET (5, diff < 0, 0,
00978                         hour_diff * 100 + min_diff);
00979 
00980                 case 1:
00981                   tz_hh_mm: /* +hh:mm */
00982                     DO_TZ_OFFSET (6, diff < 0, 04,
00983                         hour_diff * 100 + min_diff);
00984 
00985                 case 2:
00986                   tz_hh_mm_ss:  /* +hh:mm:ss */
00987                     DO_TZ_OFFSET (9, diff < 0, 024,
00988                         hour_diff * 10000 + min_diff * 100 + sec_diff);
00989 
00990                 case 3:     /* +hh if possible, else +hh:mm, else +hh:mm:ss */
00991                     if (sec_diff != 0)
00992                         goto tz_hh_mm_ss;
00993                     if (min_diff != 0)
00994                         goto tz_hh_mm;
00995                     DO_TZ_OFFSET (3, diff < 0, 0, hour_diff);
00996 
00997                 default:
00998                     goto bad_format;
00999                 }
01000             }
01001 
01002         case ('\0'):            /* GNU extension: % at end of format.  */
01003             --f;
01004             /* Fall through.  */
01005         default:
01006             /* Unknown format; output the format, including the '%',
01007                since this is most likely the right thing to do if a
01008                multibyte string has been misparsed.  */
01009           bad_format:
01010             {
01011                 gint flen;
01012                 for (flen = 1; f[1 - flen] != ('%'); flen++)
01013                     continue;
01014                 cpy (flen, &f[1 - flen]);
01015             }
01016             break;
01017         }
01018     }
01019     return i;
01020 }

Generated on Mon May 21 17:42:21 2007 for QOF by  doxygen 1.5.1