QOF
0.8.0
|
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 = © 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 }