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         {
00180             setenv ("TZ", "GMT", 1);
00181             zone = "GMT";
00182         }
00183     }
00184     else
00185     {
00186         /* POSIX.1 requires that local time zone information be 
00187         used as though strftime called tzset.  */
00188         tzset ();
00189     }
00190 
00191     if (hour12 > 12)
00192         hour12 -= 12;
00193     else if (hour12 == 0)
00194         hour12 = 12;
00195 
00196     for (f = format; *f != '\0'; ++f)
00197     {
00198         gint pad = 0;           /* Padding for number ('-', '_', or 0).  */
00199         gint modifier;          /* Field modifier ('E', 'O', or 0).  */
00200         gint digits = 0;        /* Max digits for numeric format.  */
00201         glong number_value;     /* Numeric value to be printed.  */
00202         guint u_number_value;   /* (unsigned int) number_value.  */
00203         gboolean negative_number;   /* The number is negative.  */
00204         gboolean always_output_a_sign;  /* +/- should always be output.  */
00205         gint tz_colon_mask;     /* Bitmask of where ':' should appear.  */
00206         const gchar *subfmt;
00207         gchar sign_char;
00208         gchar *bufp;
00209         gchar buf[MAX_DATE_BUFFER];
00210         gint width = -1;
00211         gboolean to_lowcase = FALSE;
00212         gboolean to_uppcase = upcase;
00213         size_t colons;
00214         gboolean change_case = FALSE;
00215         gint format_char;
00216 
00217         switch (*f)
00218         {
00219         case ('%'):
00220             break;
00221 
00222         case ('\b'):
00223         case ('\t'):
00224         case ('\n'):
00225         case ('\v'):
00226         case ('\f'):
00227         case ('\r'):
00228         case (' '):
00229         case ('!'):
00230         case ('"'):
00231         case ('#'):
00232         case ('&'):
00233         case ('\''):
00234         case ('('):
00235         case (')'):
00236         case ('*'):
00237         case ('+'):
00238         case (','):
00239         case ('-'):
00240         case ('.'):
00241         case ('/'):
00242         case ('0'):
00243         case ('1'):
00244         case ('2'):
00245         case ('3'):
00246         case ('4'):
00247         case ('5'):
00248         case ('6'):
00249         case ('7'):
00250         case ('8'):
00251         case ('9'):
00252         case (':'):
00253         case (';'):
00254         case ('<'):
00255         case ('='):
00256         case ('>'):
00257         case ('?'):
00258         case ('A'):
00259         case ('B'):
00260         case ('C'):
00261         case ('D'):
00262         case ('E'):
00263         case ('F'):
00264         case ('G'):
00265         case ('H'):
00266         case ('I'):
00267         case ('J'):
00268         case ('K'):
00269         case ('L'):
00270         case ('M'):
00271         case ('N'):
00272         case ('O'):
00273         case ('P'):
00274         case ('Q'):
00275         case ('R'):
00276         case ('S'):
00277         case ('T'):
00278         case ('U'):
00279         case ('V'):
00280         case ('W'):
00281         case ('X'):
00282         case ('Y'):
00283         case ('Z'):
00284         case ('['):
00285         case ('\\'):
00286         case (']'):
00287         case ('^'):
00288         case ('_'):
00289         case ('a'):
00290         case ('b'):
00291         case ('c'):
00292         case ('d'):
00293         case ('e'):
00294         case ('f'):
00295         case ('g'):
00296         case ('h'):
00297         case ('i'):
00298         case ('j'):
00299         case ('k'):
00300         case ('l'):
00301         case ('m'):
00302         case ('n'):
00303         case ('o'):
00304         case ('p'):
00305         case ('q'):
00306         case ('r'):
00307         case ('s'):
00308         case ('t'):
00309         case ('u'):
00310         case ('v'):
00311         case ('w'):
00312         case ('x'):
00313         case ('y'):
00314         case ('z'):
00315         case ('{'):
00316         case ('|'):
00317         case ('}'):
00318         case ('~'):
00319             /* The C Standard requires these 98 characters (plus '%') 
00320             to be in the basic execution character set.  None of 
00321             these characters can start a multibyte sequence, so they 
00322             need not be analyzed further.  */
00323             add1 (*f);
00324             continue;
00325 
00326         default:
00327             /* Copy this multibyte sequence until we reach its end, 
00328             find an error, or come back to the initial shift state.
00329             */
00330             {
00331                 mbstate_t mbstate = mbstate_zero;
00332                 size_t len = 0;
00333                 size_t fsize;
00334 
00335                 if (!format_end)
00336                     format_end = f + strlen (f) + 1;
00337                 fsize = format_end - f;
00338 
00339                 do
00340                 {
00341                     size_t bytes = mbrlen (f + len, fsize - len, &mbstate);
00342 
00343                     if (bytes == 0)
00344                         break;
00345 
00346                     if (bytes == (size_t) - 2)
00347                     {
00348                         len += strlen (f + len);
00349                         break;
00350                     }
00351 
00352                     if (bytes == (size_t) - 1)
00353                     {
00354                         len++;
00355                         break;
00356                     }
00357 
00358                     len += bytes;
00359                 }
00360                 while (!mbsinit (&mbstate));
00361 
00362                 cpy (len, f);
00363                 f += len - 1;
00364                 continue;
00365             }
00366         }
00367 
00368         /* Check for flags that can modify a format.  */
00369         while (1)
00370         {
00371             switch (*++f)
00372             {
00373                 /* This influences the number formats.  */
00374             case ('_'):
00375             case ('-'):
00376             case ('0'):
00377                 pad = *f;
00378                 continue;
00379 
00380                 /* This changes textual output.  */
00381             case ('^'):
00382                 to_uppcase = TRUE;
00383                 continue;
00384             case ('#'):
00385                 change_case = TRUE;
00386                 continue;
00387 
00388             default:
00389                 break;
00390             }
00391             break;
00392         }
00393 
00394         /* As a GNU extension we allow to specify the field width.  */
00395         if (ISDIGIT (*f))
00396         {
00397             width = 0;
00398             do
00399             {
00400                 if (width > INT_MAX / 10
00401                     || (width == INT_MAX / 10
00402                         && *f - ('0') > INT_MAX % 10))
00403                     /* Avoid overflow.  */
00404                     width = INT_MAX;
00405                 else
00406                 {
00407                     width *= 10;
00408                     width += *f - ('0');
00409                 }
00410                 ++f;
00411             }
00412             while (ISDIGIT (*f));
00413         }
00414 
00415         /* Check for modifiers.  */
00416         switch (*f)
00417         {
00418         case ('E'):
00419         case ('O'):
00420             modifier = *f++;
00421             break;
00422 
00423         default:
00424             modifier = 0;
00425             break;
00426         }
00427 
00428         /* Now do the specified format.  */
00429         format_char = *f;
00430         switch (format_char)
00431         {
00432 #define DO_NUMBER(d, v) \
00433       digits = d;     \
00434       number_value = v; goto do_number
00435 #define DO_SIGNED_NUMBER(d, negative, v) \
00436       digits = d;       \
00437       negative_number = negative;  \
00438       u_number_value = v; goto do_signed_number
00439 
00440 /* The mask is not what you might think.
00441 When the ordinal i'th bit is set, insert a colon
00442 before the i'th digit of the time zone representation.  */
00443 #define DO_TZ_OFFSET(d, negative, mask, v)  \
00444     digits = d;                             \
00445     negative_number = negative;             \
00446     tz_colon_mask = mask;                   \
00447     u_number_value = v; goto do_tz_offset
00448 #define DO_NUMBER_SPACEPAD(d, v)            \
00449 digits = d;                                 \
00450     number_value = v; goto do_number_spacepad
00451 
00452         case ('%'):
00453             if (modifier != 0)
00454                 goto bad_format;
00455             add1 (*f);
00456             break;
00457 
00458         case ('a'):
00459             if (modifier != 0)
00460                 goto bad_format;
00461             if (change_case)
00462             {
00463                 to_uppcase = TRUE;
00464                 to_lowcase = FALSE;
00465             }
00466             goto underlying_strftime;
00467 
00468         case 'A':
00469             if (modifier != 0)
00470                 goto bad_format;
00471             if (change_case)
00472             {
00473                 to_uppcase = TRUE;
00474                 to_lowcase = FALSE;
00475             }
00476             goto underlying_strftime;
00477 
00478         case ('b'):
00479         case ('h'):
00480             if (change_case)
00481             {
00482                 to_uppcase = TRUE;
00483                 to_lowcase = FALSE;
00484             }
00485             if (modifier != 0)
00486                 goto bad_format;
00487             goto underlying_strftime;
00488 
00489         case ('B'):
00490             if (modifier != 0)
00491                 goto bad_format;
00492             if (change_case)
00493             {
00494                 to_uppcase = TRUE;
00495                 to_lowcase = FALSE;
00496             }
00497             goto underlying_strftime;
00498 
00499         case ('c'):
00500             if (modifier == ('O'))
00501                 goto bad_format;
00502             goto underlying_strftime;
00503 
00504             subformat:
00505             {
00506                 size_t len = strftime_case (to_uppcase,
00507                     NULL, ((size_t) - 1),
00508                     subfmt, qd, ut, ns);
00509                 add (len, strftime_case (to_uppcase, p,
00510                         (maxsize - i), subfmt, qd, ut, ns));
00511             }
00512             break;
00513 
00514           underlying_strftime:
00515             {
00516                 /* try to handle locale-specific formats */
00517                 gchar ufmt[5];
00518                 gchar *u = ufmt;
00519                 gchar ubuf[1024];   /* enough for any single format in practice */
00520                 size_t len;
00521                 /* Make sure we're calling the actual underlying strftime.
00522                 In some cases, config.h contains something like
00523                 "#define strftime rpl_strftime".  */
00524 # ifdef strftime
00525 #  undef strftime
00526                 size_t strftime ();
00527 #endif
00528 
00529                 /* The space helps distinguish strftime failure from 
00530                 empty output.  */
00531                 *u++ = ' ';
00532                 *u++ = '%';
00533                 if (modifier != 0)
00534                     *u++ = modifier;
00535                 *u++ = format_char;
00536                 *u = '\0';
00537                 {
00538                     glong nanosecs;
00539                     struct tm bad;
00540                     if(!qof_date_to_struct_tm ((QofDate*)qd, &bad, &nanosecs))
00541                     {
00542                         PERR (" locale format out of range.");
00543                         break;
00544                     }
00545                     len = strftime (ubuf, sizeof ubuf, ufmt, &bad);
00546                 }
00547                 if (len != 0)
00548                     cpy (len - 1, ubuf + 1);
00549             }
00550             break;
00551 
00552         case ('C'):
00553             if (modifier == ('O'))
00554                 goto bad_format;
00555             if (modifier == ('E'))
00556             {
00557                 goto underlying_strftime;
00558             }
00559 
00560             {
00561                 /* convert to use QofDate->qd_year which is 64bit */
00562                 gint century = qd->qd_year / 100 + TM_YEAR_BASE / 100;
00563                 century -= qd->qd_year % 100 < 0 && 0 < century;
00564                 DO_SIGNED_NUMBER (2, 
00565                     qd->qd_year < -TM_YEAR_BASE, century);
00566             }
00567 
00568         case ('x'):
00569             if (modifier == ('O'))
00570                 goto bad_format;
00571             goto underlying_strftime;
00572         case ('D'):
00573             if (modifier != 0)
00574                 goto bad_format;
00575             subfmt = ("%m/%d/%y");
00576             goto subformat;
00577 
00578         case ('d'):
00579             if (modifier == ('E'))
00580                 goto bad_format;
00581 
00582             DO_NUMBER (2, qd->qd_mday);
00583 
00584         case ('e'):
00585             if (modifier == ('E'))
00586                 goto bad_format;
00587 
00588             DO_NUMBER_SPACEPAD (2, qd->qd_mday);
00589 
00590             /* All numeric formats set DIGITS and NUMBER_VALUE (or U_NUMBER_VALUE)
00591             and then jump to one of these labels.  */
00592             do_tz_offset:
00593             always_output_a_sign = TRUE;
00594             goto do_number_body;
00595 
00596             do_number_spacepad:
00597             /* Force `_' flag unless overridden by `0' or `-' flag.  */
00598             if (pad != ('0') && pad != ('-'))
00599                 pad = ('_');
00600 
00601             do_number:
00602             /* Format NUMBER_VALUE according to the MODIFIER flag.  */
00603             negative_number = number_value < 0;
00604             u_number_value = number_value;
00605 
00606             do_signed_number:
00607             always_output_a_sign = FALSE;
00608             tz_colon_mask = 0;
00609 
00610             do_number_body:
00611             /* Format U_NUMBER_VALUE according to the MODIFIER flag.
00612             NEGATIVE_NUMBER is nonzero if the original number was
00613             negative; in this case it was converted directly to
00614             unsigned int (i.e., modulo (UINT_MAX + 1)) without
00615             negating it.  */
00616             if (modifier == ('O') && !negative_number)
00617             {
00618                 goto underlying_strftime;
00619             }
00620 
00621             bufp = buf + sizeof (buf) / sizeof (buf[0]);
00622 
00623             if (negative_number)
00624                 u_number_value = -u_number_value;
00625 
00626             do
00627             {
00628                 if (tz_colon_mask & 1)
00629                     *--bufp = ':';
00630                 tz_colon_mask >>= 1;
00631                 *--bufp = u_number_value % 10 + ('0');
00632                 u_number_value /= 10;
00633             }
00634             while (u_number_value != 0 || tz_colon_mask != 0);
00635 
00636           do_number_sign_and_padding:
00637             if (digits < width)
00638                 digits = width;
00639 
00640             sign_char = (negative_number ? ('-')
00641                 : always_output_a_sign ? ('+') : 0);
00642 
00643             if (pad == ('-'))
00644             {
00645                 if (sign_char)
00646                     add1 (sign_char);
00647             }
00648             else
00649             {
00650                 gint padding =
00651                     digits - (buf + (sizeof (buf) / sizeof (buf[0])) -
00652                     bufp) - !!sign_char;
00653 
00654                 if (padding > 0)
00655                 {
00656                     if (pad == ('_'))
00657                     {
00658                         if ((size_t) padding >= maxsize - i)
00659                             return 0;
00660 
00661                         if (p)
00662                             memset_space (p, padding);
00663                         i += padding;
00664                         width = width > padding ? width - padding : 0;
00665                         if (sign_char)
00666                             add1 (sign_char);
00667                     }
00668                     else
00669                     {
00670                         if ((size_t) digits >= maxsize - i)
00671                             return 0;
00672 
00673                         if (sign_char)
00674                             add1 (sign_char);
00675 
00676                         if (p)
00677                             memset_zero (p, padding);
00678                         i += padding;
00679                         width = 0;
00680                     }
00681                 }
00682                 else
00683                 {
00684                     if (sign_char)
00685                         add1 (sign_char);
00686                 }
00687             }
00688 
00689             cpy (buf + sizeof (buf) / sizeof (buf[0]) - bufp, bufp);
00690             break;
00691 
00692         case ('F'):
00693             if (modifier != 0)
00694                 goto bad_format;
00695             subfmt = ("%Y-%m-%d");
00696             goto subformat;
00697 
00698         case ('H'):
00699             if (modifier == ('E'))
00700                 goto bad_format;
00701 
00702             DO_NUMBER (2, qd->qd_hour);
00703 
00704         case ('I'):
00705             if (modifier == ('E'))
00706                 goto bad_format;
00707 
00708             DO_NUMBER (2, hour12);
00709 
00710         case ('k'):         /* GNU extension.  */
00711             if (modifier == ('E'))
00712                 goto bad_format;
00713 
00714             DO_NUMBER_SPACEPAD (2, qd->qd_hour);
00715 
00716         case ('l'):         /* GNU extension.  */
00717             if (modifier == ('E'))
00718                 goto bad_format;
00719 
00720             DO_NUMBER_SPACEPAD (2, hour12);
00721 
00722         case ('j'):
00723             if (modifier == ('E'))
00724                 goto bad_format;
00725 
00726             DO_SIGNED_NUMBER (3, qd->qd_yday < -1, qd->qd_yday + 1U);
00727 
00728         case ('M'):
00729             if (modifier == ('E'))
00730                 goto bad_format;
00731 
00732             DO_NUMBER (2, qd->qd_min);
00733 
00734         case ('m'):
00735             if (modifier == ('E'))
00736                 goto bad_format;
00737 
00738             DO_SIGNED_NUMBER (2, qd->qd_mon < -1, qd->qd_mon);
00739 
00740         case ('N'):         /* GNU extension.  */
00741             if (modifier == ('E'))
00742                 goto bad_format;
00743 
00744             number_value = ns;
00745             if (width == -1)
00746                 width = 9;
00747             else
00748             {
00749                 /* Take an explicit width less than 9 as a precision.  */
00750                 gint j;
00751                 for (j = width; j < 9; j++)
00752                     number_value /= 10;
00753             }
00754 
00755             DO_NUMBER (width, number_value);
00756 
00757         case ('n'):
00758             add1 (('\n'));
00759             break;
00760 
00761         case ('P'):
00762             to_lowcase = TRUE;
00763             format_char = ('p');
00764 
00765         case ('p'):
00766             if (change_case)
00767             {
00768                 to_uppcase = FALSE;
00769                 to_lowcase = TRUE;
00770             }
00771             goto underlying_strftime;
00772 
00773         case ('R'):
00774             subfmt = ("%H:%M");
00775             goto subformat;
00776 
00777         case ('r'):
00778             goto underlying_strftime;
00779 
00780         case ('S'):
00781             if (modifier == ('E'))
00782                 goto bad_format;
00783 
00784             DO_NUMBER (2, qd->qd_sec);
00785 
00786         case ('s'):         /* GNU extension.
00787         number of seconds since the epoch.
00788         basically QofTimeSecs as a string.
00789         */
00790             {
00791                 glong nanosecs;
00792                 QofTime *time;
00793                 QofTimeSecs t;
00794 
00795                 time = qof_date_to_qtime ((QofDate*)qd);
00796                 t = qof_time_get_secs (time);
00797                 nanosecs = qof_time_get_nanosecs (time);
00798 
00799                 /* Generate string value for T using time_t arithmetic;
00800                    this works even if sizeof (long) < sizeof (time_t).  */
00801 
00802                 bufp = buf + sizeof (buf) / sizeof (buf[0]);
00803                 negative_number = t < 0;
00804 
00805                 do
00806                 {
00807                     gint d = t % 10;
00808                     t /= 10;
00809                     *--bufp = (negative_number ? -d : d) + ('0');
00810                 }
00811                 while (t != 0);
00812 
00813                 digits = 1;
00814                 always_output_a_sign = FALSE;
00815                 goto do_number_sign_and_padding;
00816             }
00817 
00818         case ('X'):
00819             if (modifier == ('O'))
00820                 goto bad_format;
00821             goto underlying_strftime;
00822         case ('T'):
00823             subfmt = ("%H:%M:%S");
00824             goto subformat;
00825 
00826         case ('t'):
00827             add1 (('\t'));
00828             break;
00829 
00830         case ('u'):
00831             DO_NUMBER (1, (qd->qd_wday - 1 + 7) % 7 + 1);
00832 
00833         case ('U'):
00834             if (modifier == ('E'))
00835                 goto bad_format;
00836 
00837             DO_NUMBER (2, (qd->qd_yday - qd->qd_wday + 7) / 7);
00838 
00839         case ('V'):
00840         case ('g'):
00841         case ('G'):
00842             if (modifier == ('E'))
00843                 goto bad_format;
00844             {
00845                 gint year_adjust = 0;
00846                 gint days = iso_week_days (qd->qd_yday, qd->qd_wday);
00847 
00848                 if (days < 0)
00849                 {
00850                     /* This ISO week belongs to the previous year.  */
00851                     year_adjust = -1;
00852                     days =
00853                         iso_week_days (qd->qd_yday +
00854                         (365 + qof_date_isleap (qd->qd_year - 1)), 
00855                         qd->qd_wday);
00856                 }
00857                 else
00858                 {
00859                     gint d =
00860                         iso_week_days (qd->qd_yday - (365 +
00861                             qof_date_isleap (qd->qd_year)),
00862                         qd->qd_wday);
00863                     if (0 <= d)
00864                     {
00865                         /* This ISO week belongs to the next year.  */
00866                         year_adjust = 1;
00867                         days = d;
00868                     }
00869                 }
00870 
00871                 switch (*f)
00872                 {
00873                 case ('g'):
00874                     {
00875                         /* use QofDate->qd_year */
00876                         gint yy = (qd->qd_year % 100 + year_adjust) % 100;
00877                         DO_NUMBER (2, (0 <= yy
00878                                 ? yy : qd->qd_year <
00879                                 -TM_YEAR_BASE -
00880                                 year_adjust ? -yy : yy + 100));
00881                     }
00882 
00883                 case ('G'):
00884                     /* use QofDate->qd_year */
00885                     DO_SIGNED_NUMBER (4,
00886                         qd->qd_year <
00887                         -TM_YEAR_BASE - year_adjust,
00888                         (qd->qd_year + (guint) TM_YEAR_BASE +
00889                             year_adjust));
00890 
00891                 default:
00892                     DO_NUMBER (2, days / 7 + 1);
00893                 }
00894             }
00895 
00896         case ('W'):
00897             if (modifier == ('E'))
00898                 goto bad_format;
00899 
00900             DO_NUMBER (2,
00901                 (qd->qd_yday - (qd->qd_wday - 1 + 7) % 7 + 7) / 7);
00902 
00903         case ('w'):
00904             if (modifier == ('E'))
00905                 goto bad_format;
00906 
00907             DO_NUMBER (1, qd->qd_wday);
00908 
00909         case ('Y'):
00910             if (modifier == 'E')
00911             {
00912                 goto underlying_strftime;
00913             }
00914             if (modifier == ('O'))
00915                 goto bad_format;
00916             else
00917                 /* use QofDate->qd_year */
00918                 DO_SIGNED_NUMBER (4, qd->qd_year < -TM_YEAR_BASE,
00919                     qd->qd_year + TM_YEAR_BASE);
00920 
00921         case ('y'):
00922             if (modifier == ('E'))
00923             {
00924                 goto underlying_strftime;
00925             }
00926 
00927             {
00928                 gint64 yy = qd->qd_year % 100;
00929                 if (yy < 0)
00930                     yy = qd->qd_year < -TM_YEAR_BASE ? -yy : yy + 100;
00931                 DO_NUMBER (2, yy);
00932             }
00933 
00934         case ('Z'):
00935             if (change_case)
00936             {
00937                 to_uppcase = FALSE;
00938                 to_lowcase = TRUE;
00939             }
00940 
00941             /* The tzset() call might have changed the value.  */
00942             if (!(zone && *zone) && qd->qd_is_dst >= 0)
00943                 zone = tzname[qd->qd_is_dst != 0];
00944             if (!zone)
00945                 zone = "";
00946 
00947             cpy (strlen (zone), zone);
00948             break;
00949 
00950         case (':'):
00951             /* :, ::, and ::: are valid only just before 'z'.
00952                :::: etc. are rejected later.  */
00953             for (colons = 1; f[colons] == (':'); colons++)
00954                 continue;
00955             if (f[colons] != ('z'))
00956                 goto bad_format;
00957             f += colons;
00958             goto do_z_conversion;
00959 
00960         case ('z'):
00961             colons = 0;
00962 
00963           do_z_conversion:
00964             if (qd->qd_is_dst < 0)
00965                 break;
00966 
00967             {
00968                 gint diff;
00969                 gint hour_diff;
00970                 gint min_diff;
00971                 gint sec_diff;
00972                 diff = qd->qd_gmt_off;
00973                 hour_diff = diff / 60 / 60;
00974                 min_diff = diff / 60 % 60;
00975                 sec_diff = diff % 60;
00976 
00977                 switch (colons)
00978                 {
00979                 case 0:     /* +hhmm */
00980                     DO_TZ_OFFSET (5, diff < 0, 0,
00981                         hour_diff * 100 + min_diff);
00982 
00983                 case 1:
00984                   tz_hh_mm: /* +hh:mm */
00985                     DO_TZ_OFFSET (6, diff < 0, 04,
00986                         hour_diff * 100 + min_diff);
00987 
00988                 case 2:
00989                   tz_hh_mm_ss:  /* +hh:mm:ss */
00990                     DO_TZ_OFFSET (9, diff < 0, 024,
00991                         hour_diff * 10000 + min_diff * 100 + sec_diff);
00992 
00993                 case 3:     /* +hh if possible, else +hh:mm, else +hh:mm:ss */
00994                     if (sec_diff != 0)
00995                         goto tz_hh_mm_ss;
00996                     if (min_diff != 0)
00997                         goto tz_hh_mm;
00998                     DO_TZ_OFFSET (3, diff < 0, 0, hour_diff);
00999 
01000                 default:
01001                     goto bad_format;
01002                 }
01003             }
01004 
01005         case ('\0'):            /* GNU extension: % at end of format.  */
01006             --f;
01007             /* Fall through.  */
01008         default:
01009             /* Unknown format; output the format, including the '%',
01010                since this is most likely the right thing to do if a
01011                multibyte string has been misparsed.  */
01012           bad_format:
01013             {
01014                 gint flen;
01015                 for (flen = 1; f[1 - flen] != ('%'); flen++)
01016                     continue;
01017                 cpy (flen, &f[1 - flen]);
01018             }
01019             break;
01020         }
01021     }
01022     return i;
01023 }

Generated on Thu Jan 31 22:50:26 2008 for QOF by  doxygen 1.5.4