QOF  0.8.6
qofstrftime.c
1 /***************************************************************************
2  * qofstrftime.c
3  *
4  * Sun May 21 15:59:32 2006
5  * Copyright (C) 1991-1999, 2000, 2001, 2003, 2004, 2005, 2006
6  * Free Software Foundation, Inc.
7  *
8  ****************************************************************************/
9 /*
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin Street, Fifth Floor Boston, MA 02110-1301, USA
23  */
24 
25 /*
26 Modified version of strftime from Debian coreutils package.
27 
28 (note that the GNU date command includes only strftime,
29 the QOF strptime code comes direct from the GNU glibc.)
30 
31 1. Removed preprocessor directives that are always true or always false within QOF
32 2. Extended variables to full 64bit ranges, even on 32bit platforms.
33 3. Replaced time_t with qt_time to prevent overflow in 2038.
34 4. Replaced struct tm with QofDate to prevent overflow.
35 Neil Williams <linux@codehelp.co.uk>
36 */
37 
38 #include "config.h"
39 #include <stdio.h>
40 #include <ctype.h>
41 #include <sys/time.h>
42 #include <time.h>
43 #include <wchar.h>
44 #include <limits.h>
45 #include <stdlib.h>
46 #include <string.h>
47 #include <glib.h>
48 #include "qof.h"
49 #include "qofdate-p.h"
50 
51 static QofLogModule log_module = QOF_MOD_DATE;
52 
53 #define memset_space(P, Len) (memset (P, ' ', Len), (P) += (Len))
54 #define memset_zero(P, Len) (memset (P, '0', Len), (P) += (Len))
55 
56 #define FPRINTFTIME 0
57 
58 /* Shift A right by B bits portably, by dividing A by 2**B and
59 truncating towards minus infinity. A and B should be free of side
60 effects, and B should be in the range 0 <= B <= INT_BITS - 2, where
61 INT_BITS is the number of useful bits in an int. GNU code can
62 assume that INT_BITS is at least 32.
63 
64 ISO C99 says that A >> B is implementation-defined if A < 0. Some
65 implementations (e.g., UNICOS 9.0 on a Cray Y-MP EL) don't shift
66 right in the usual way when A < 0, so SHR falls back on division if
67 ordinary A >> B doesn't seem to be the usual signed shift. */
68 #define SHR(a, b) \
69  (-1 >> 1 == -1 \
70  ? (a) >> (b) \
71  : (a) / (1 << (b)) - ((a) % (1 << (b)) < 0))
72 
73 /* Bound on length of the string representing an integer
74 type or expression T. Subtract 1 for the sign bit if t is signed;
75 log10 (2.0) < 146/485; add 1 for integer division truncation;
76 add 1 more for a minus sign if needed. */
77 #define INT_strlen _BOUND(t) \
78  ((sizeof (t) * CHAR_BIT - 1) * 146 / 485 + 2)
79 
80 /* IMPORTANT: QofDate does not use 1900 or 1970 as a base, all
81  years in QofDate are true values. */
82 #define TM_YEAR_BASE 0
83 
84 #define add(n, f) \
85  do \
86  { \
87  gint _n = (n); \
88  gint _delta = width - _n; \
89  gint _incr = _n + (_delta > 0 ? _delta : 0); \
90  if ((size_t) _incr >= maxsize - i) \
91  return 0; \
92  if (p) \
93  { \
94  if (digits == 0 && _delta > 0) \
95  { \
96  if (pad == ('0')) \
97  memset_zero (p, _delta); \
98  else \
99  memset_space (p, _delta); \
100  } \
101  f; \
102  p += FPRINTFTIME ? 0 : _n; \
103  } \
104  i += _incr; \
105  } while (0)
106 
107 # define add1(C) add (1, *p = C)
108 
109 # define cpy(n, s) \
110  add ((n), \
111  if (to_lowcase) \
112  memcpy_lowcase (p, (s), _n); \
113  else if (to_uppcase) \
114  memcpy_uppcase (p, (s), _n); \
115  else \
116  memcpy ((void *) p, (void const *) (s), _n))
117 
118 #define TOUPPER(Ch, L) (islower (Ch) ? toupper (Ch) : (Ch))
119 #define TOLOWER(Ch, L) (isupper (Ch) ? tolower (Ch) : (Ch))
120 
121 /* We don't use `isdigit' here since the locale dependent
122 interpretation is not what we want here. We only need to accept
123 the arabic digits in the ASCII range. One day there is perhaps a
124 more reliable way to accept other sets of digits. */
125 #define ISDIGIT(Ch) ((guint) (Ch) - ('0') <= 9)
126 /* The number of days from the first day of the first ISO week of this
127 year to the year day YDAY with week day WDAY. ISO weeks start on
128 Monday; the first ISO week has the year's first Thursday. YDAY may
129 be as small as YDAY_MINIMUM. */
130 #define ISO_WEEK_START_WDAY 1 /* Monday */
131 #define ISO_WEEK1_WDAY 4 /* Thursday */
132 #define YDAY_MINIMUM (-366)
133 
134 static const mbstate_t mbstate_zero;
135 const gchar *format_end = NULL;
136 
137 static gchar *
138 memcpy_lowcase (gchar * dest, const gchar * src, size_t len)
139 {
140  while (len-- > 0)
141  dest[len] = TOLOWER ((guchar) src[len], loc);
142  return dest;
143 }
144 
145 static gchar *
146 memcpy_uppcase (gchar * dest, const gchar * src, size_t len)
147 {
148  while (len-- > 0)
149  dest[len] = TOUPPER ((guchar) src[len], loc);
150  return dest;
151 }
152 
153 static gint
154 iso_week_days (gint yday, gint wday)
155 {
156  /* Add enough to the first operand of % to make it nonnegative. */
157  gint big_enough_multiple_of_7 = (-YDAY_MINIMUM / 7 + 2) * 7;
158  return (yday
159  - (yday - wday + ISO_WEEK1_WDAY + big_enough_multiple_of_7) % 7
160  + ISO_WEEK1_WDAY - ISO_WEEK_START_WDAY);
161 }
162 
163 size_t
164 strftime_case (gboolean upcase, gchar * s,
165  size_t maxsize, const gchar *format, const QofDate *qd,
166  gint ut, glong ns)
167 {
168  const gchar *zone;
169  gint hour12 = qd->qd_hour;
170  size_t i = 0;
171  gchar *p = s;
172  const gchar *f;
173  QofDate copy = *qd;
174  qd = &copy;
175  zone = (const gchar *) qd->qd_zone;
176  if (ut)
177  {
178  if (!(zone && *zone))
179  {
180  setenv ("TZ", "GMT", 1);
181  zone = "GMT";
182  }
183  }
184  else
185  {
186  /* POSIX.1 requires that local time zone information be
187  used as though strftime called tzset. */
188  tzset ();
189  }
190 
191  if (hour12 > 12)
192  hour12 -= 12;
193  else if (hour12 == 0)
194  hour12 = 12;
195 
196  for (f = format; *f != '\0'; ++f)
197  {
198  gint pad = 0; /* Padding for number ('-', '_', or 0). */
199  gint modifier; /* Field modifier ('E', 'O', or 0). */
200  gint digits = 0; /* Max digits for numeric format. */
201  glong number_value; /* Numeric value to be printed. */
202  guint u_number_value; /* (unsigned int) number_value. */
203  gboolean negative_number; /* The number is negative. */
204  gboolean always_output_a_sign; /* +/- should always be output. */
205  gint tz_colon_mask; /* Bitmask of where ':' should appear. */
206  const gchar *subfmt;
207  gchar sign_char;
208  gchar *bufp;
209  gchar buf[MAX_DATE_BUFFER];
210  gint width = -1;
211  gboolean to_lowcase = FALSE;
212  gboolean to_uppcase = upcase;
213  size_t colons;
214  gboolean change_case = FALSE;
215  gint format_char;
216 
217  switch (*f)
218  {
219  case ('%'):
220  break;
221 
222  case ('\b'):
223  case ('\t'):
224  case ('\n'):
225  case ('\v'):
226  case ('\f'):
227  case ('\r'):
228  case (' '):
229  case ('!'):
230  case ('"'):
231  case ('#'):
232  case ('&'):
233  case ('\''):
234  case ('('):
235  case (')'):
236  case ('*'):
237  case ('+'):
238  case (','):
239  case ('-'):
240  case ('.'):
241  case ('/'):
242  case ('0'):
243  case ('1'):
244  case ('2'):
245  case ('3'):
246  case ('4'):
247  case ('5'):
248  case ('6'):
249  case ('7'):
250  case ('8'):
251  case ('9'):
252  case (':'):
253  case (';'):
254  case ('<'):
255  case ('='):
256  case ('>'):
257  case ('?'):
258  case ('A'):
259  case ('B'):
260  case ('C'):
261  case ('D'):
262  case ('E'):
263  case ('F'):
264  case ('G'):
265  case ('H'):
266  case ('I'):
267  case ('J'):
268  case ('K'):
269  case ('L'):
270  case ('M'):
271  case ('N'):
272  case ('O'):
273  case ('P'):
274  case ('Q'):
275  case ('R'):
276  case ('S'):
277  case ('T'):
278  case ('U'):
279  case ('V'):
280  case ('W'):
281  case ('X'):
282  case ('Y'):
283  case ('Z'):
284  case ('['):
285  case ('\\'):
286  case (']'):
287  case ('^'):
288  case ('_'):
289  case ('a'):
290  case ('b'):
291  case ('c'):
292  case ('d'):
293  case ('e'):
294  case ('f'):
295  case ('g'):
296  case ('h'):
297  case ('i'):
298  case ('j'):
299  case ('k'):
300  case ('l'):
301  case ('m'):
302  case ('n'):
303  case ('o'):
304  case ('p'):
305  case ('q'):
306  case ('r'):
307  case ('s'):
308  case ('t'):
309  case ('u'):
310  case ('v'):
311  case ('w'):
312  case ('x'):
313  case ('y'):
314  case ('z'):
315  case ('{'):
316  case ('|'):
317  case ('}'):
318  case ('~'):
319  /* The C Standard requires these 98 characters (plus '%')
320  to be in the basic execution character set. None of
321  these characters can start a multibyte sequence, so they
322  need not be analyzed further. */
323  add1 (*f);
324  continue;
325 
326  default:
327  /* Copy this multibyte sequence until we reach its end,
328  find an error, or come back to the initial shift state.
329  */
330  {
331  mbstate_t mbstate = mbstate_zero;
332  size_t len = 0;
333  size_t fsize;
334 
335  if (!format_end)
336  format_end = f + strlen (f) + 1;
337  fsize = format_end - f;
338 
339  do
340  {
341  size_t bytes = mbrlen (f + len, fsize - len, &mbstate);
342 
343  if (bytes == 0)
344  break;
345 
346  if (bytes == (size_t) - 2)
347  {
348  len += strlen (f + len);
349  break;
350  }
351 
352  if (bytes == (size_t) - 1)
353  {
354  len++;
355  break;
356  }
357 
358  len += bytes;
359  }
360  while (!mbsinit (&mbstate));
361 
362  cpy (len, f);
363  f += len - 1;
364  continue;
365  }
366  }
367 
368  /* Check for flags that can modify a format. */
369  while (1)
370  {
371  switch (*++f)
372  {
373  /* This influences the number formats. */
374  case ('_'):
375  case ('-'):
376  case ('0'):
377  pad = *f;
378  continue;
379 
380  /* This changes textual output. */
381  case ('^'):
382  to_uppcase = TRUE;
383  continue;
384  case ('#'):
385  change_case = TRUE;
386  continue;
387 
388  default:
389  break;
390  }
391  break;
392  }
393 
394  /* As a GNU extension we allow to specify the field width. */
395  if (ISDIGIT (*f))
396  {
397  width = 0;
398  do
399  {
400  if (width > INT_MAX / 10
401  || (width == INT_MAX / 10
402  && *f - ('0') > INT_MAX % 10))
403  /* Avoid overflow. */
404  width = INT_MAX;
405  else
406  {
407  width *= 10;
408  width += *f - ('0');
409  }
410  ++f;
411  }
412  while (ISDIGIT (*f));
413  }
414 
415  /* Check for modifiers. */
416  switch (*f)
417  {
418  case ('E'):
419  case ('O'):
420  modifier = *f++;
421  break;
422 
423  default:
424  modifier = 0;
425  break;
426  }
427 
428  /* Now do the specified format. */
429  format_char = *f;
430  switch (format_char)
431  {
432 #define DO_NUMBER(d, v) \
433  digits = d; \
434  number_value = v; goto do_number
435 #define DO_SIGNED_NUMBER(d, negative, v) \
436  digits = d; \
437  negative_number = negative; \
438  u_number_value = v; goto do_signed_number
439 
440 /* The mask is not what you might think.
441 When the ordinal i'th bit is set, insert a colon
442 before the i'th digit of the time zone representation. */
443 #define DO_TZ_OFFSET(d, negative, mask, v) \
444  digits = d; \
445  negative_number = negative; \
446  tz_colon_mask = mask; \
447  u_number_value = v; goto do_tz_offset
448 #define DO_NUMBER_SPACEPAD(d, v) \
449 digits = d; \
450  number_value = v; goto do_number_spacepad
451 
452  case ('%'):
453  if (modifier != 0)
454  goto bad_format;
455  add1 (*f);
456  break;
457 
451 
452  case ('%'):
453  if (modifier != 0)
454  goto bad_format;
455  add1 (*f);
456  break;
457 
451 
452  case ('%'):
453  if (modifier != 0)
454  goto bad_format;
455  add1 (*f);
456  break;
457 
451 
452  case ('%'):
453  if (modifier != 0)
454  goto bad_format;
455  add1 (*f);
456  break;
457 
451 
452  case ('%'):
453  if (modifier != 0)
454  goto bad_format;
455  add1 (*f);
456  break;
457 
451 
452  case ('%'):
453  if (modifier != 0)
454  goto bad_format;
455  add1 (*f);
456  break;
457 
451 
452  case ('%'):
453  if (modifier != 0)
454  goto bad_format;
455  add1 (*f);
456  break;
457 
451 
452  case ('%'):
453  if (modifier != 0)
454  goto bad_format;
455  add1 (*f);
456  break;
457 
451 
452  case ('%'):
453  if (modifier != 0)
454  goto bad_format;
455  add1 (*f);
456  break;
457 
451 
452  case ('%'):
453  if (modifier != 0)
454  goto bad_format;
455  add1 (*f);
456  break;
457 
451 
452  case ('%'):
453  if (modifier != 0)
454  goto bad_format;
455  add1 (*f);
456  break;
457 
451 
452  case ('%'):
453  if (modifier != 0)
454  goto bad_format;
455  add1 (*f);
456  break;
457 
451 
452  case ('%'):
453  if (modifier != 0)
454  goto bad_format;
455  add1 (*f);
456  break;
457 
451 
452  case ('%'):
453  if (modifier != 0)
454  goto bad_format;
455  add1 (*f);
456  break;
457 
451 
452  case ('%'):
453  if (modifier != 0)
454  goto bad_format;
455  add1 (*f);
456  break;
457 
451 
452  case ('%'):
453  if (modifier != 0)
454  goto bad_format;
455  add1 (*f);
456  break;
457 
451 
452  case ('%'):
453  if (modifier != 0)
454  goto bad_format;
455  add1 (*f);
456  break;
457 
451 
452  case ('%'):
453  if (modifier != 0)
454  goto bad_format;
455  add1 (*f);
456  break;
457 
451 
452  case ('%'):
453  if (modifier != 0)
454  goto bad_format;
455  add1 (*f);
456  break;
457 
451 
452  case ('%'):
453  if (modifier != 0)
454  goto bad_format;
455  add1 (*f);
456  break;
457 
451 
452  case ('%'):
453  if (modifier != 0)
454  goto bad_format;
455  add1 (*f);
456  break;
457 
451 
452  case ('%'):
453  if (modifier != 0)
454  goto bad_format;
455  add1 (*f);
456  break;
457 
451 
452  case ('%'):
453  if (modifier != 0)
454  goto bad_format;
455  add1 (*f);
456  break;
457 
451 
452  case ('%'):
453  if (modifier != 0)
454  goto bad_format;
455  add1 (*f);
456  break;
457 
451 
452  case ('%'):
453  if (modifier != 0)
454  goto bad_format;
455  add1 (*f);
456  break;
457 
451 
452  case ('%'):
453  if (modifier != 0)
454  goto bad_format;
455  add1 (*f);
456  break;
457 
451 
452  case ('%'):
453  if (modifier != 0)
454  goto bad_format;
455  add1 (*f);
456  break;
457 
451 
452  case ('%'):
453  if (modifier != 0)
454  goto bad_format;
455  add1 (*f);
456  break;
457 
451 
452  case ('%'):
453  if (modifier != 0)
454  goto bad_format;
455  add1 (*f);
456  break;
457 
451 
452  case ('%'):
453  if (modifier != 0)
454  goto bad_format;
455  add1 (*f);
456  break;
457 
451 
452  case ('%'):
453  if (modifier != 0)
454  goto bad_format;
455  add1 (*f);
456  break;
457 
451 
452  case ('%'):
453  if (modifier != 0)
454  goto bad_format;
455  add1 (*f);
456  break;
457 
451 
452  case ('%'):
453  if (modifier != 0)
454  goto bad_format;
455  add1 (*f);
456  break;
457 
451 
452  case ('%'):
453  if (modifier != 0)
454  goto bad_format;
455  add1 (*f);
456  break;
457 
451 
452  case ('%'):
453  if (modifier != 0)
454  goto bad_format;
455  add1 (*f);
456  break;
457 
451 
452  case ('%'):
453  if (modifier != 0)
454  goto bad_format;
455  add1 (*f);
456  break;
457 
451 
452  case ('%'):
453  if (modifier != 0)
454  goto bad_format;
455  add1 (*f);
456  break;
457 
451 
452  case ('%'):
453  if (modifier != 0)
454  goto bad_format;
455  add1 (*f);
456  break;
457 
451 
452  case ('%'):
453  if (modifier != 0)
454  goto bad_format;
455  add1 (*f);
456  break;
457 
451 
452  case ('%'):
453  if (modifier != 0)
454  goto bad_format;
455  add1 (*f);
456  break;
457 
451 
452  case ('%'):
453  if (modifier != 0)
454  goto bad_format;
455  add1 (*f);
456  break;
457 
451 
452  case ('%'):
453  if (modifier != 0)
454  goto bad_format;
455  add1 (*f);
456  break;
457 
451 
452  case ('%'):
453  if (modifier != 0)
454  goto bad_format;
455  add1 (*f);
456  break;
457 
451 
452  case ('%'):
453  if (modifier != 0)
454  goto bad_format;
455  add1 (*f);
456  break;
457 
451 
452  case ('%'):
453  if (modifier != 0)
454  goto bad_format;
455  add1 (*f);
456  break;
457 
451 
452  case ('%'):
453  if (modifier != 0)
454  goto bad_format;
455  add1 (*f);
456  break;
457 
451 
452  case ('%'):
453  if (modifier != 0)
454  goto bad_format;
455  add1 (*f);
456  break;
457 
451 
452  case ('%'):
453  if (modifier != 0)
454  goto bad_format;
455  add1 (*f);
456  break;
457 
451 
452  case ('%'):
453  if (modifier != 0)
454  goto bad_format;
455  add1 (*f);
456  break;
457 
451 
452  case ('%'):
453  if (modifier != 0)
454  goto bad_format;
455  add1 (*f);
456  break;
457 
451 
452  case ('%'):
453  if (modifier != 0)
454  goto bad_format;
455  add1 (*f);
456  break;
457 
451 
452  case ('%'):
453  if (modifier != 0)
454  goto bad_format;
455  add1 (*f);
456  break;
457 
451 
452  case ('%'):
453  if (modifier != 0)
454  goto bad_format;
455  add1 (*f);
456  break;
457 
451 
452  case ('%'):
453  if (modifier != 0)
454  goto bad_format;
455  add1 (*f);
456  break;
457 
451 
452  case ('%'):
453  if (modifier != 0)
454  goto bad_format;
455  add1 (*f);
456  break;
457 
451 
452  case ('%'):
453  if (modifier != 0)
454  goto bad_format;
455  add1 (*f);
456  break;
457 
451 
452  case ('%'):
453  if (modifier != 0)
454  goto bad_format;
455  add1 (*f);
456  break;
457 
451 
452  case ('%'):
453  if (modifier != 0)
454  goto bad_format;
455  add1 (*f);
456  break;
457 
451 
452  case ('%'):
453  if (modifier != 0)
454  goto bad_format;
455  add1 (*f);
456  break;
457 
451 
452  case ('%'):
453  if (modifier != 0)
454  goto bad_format;
455  add1 (*f);
456  break;
457 
451 
452  case ('%'):
453  if (modifier != 0)
454  goto bad_format;
455  add1 (*f);
456  break;
457 
451 
452  case ('%'):
453  if (modifier != 0)
454  goto bad_format;
455  add1 (*f);
456  break;
457 
451 
452  case ('%'):
453  if (modifier != 0)
454  goto bad_format;
455  add1 (*f);
456  break;
457 
451 
452  case ('%'):
453  if (modifier != 0)
454  goto bad_format;
455  add1 (*f);
456  break;
457 
451 
452  case ('%'):
453  if (modifier != 0)
454  goto bad_format;
455  add1 (*f);
456  break;
457 
451 
452  case ('%'):
453  if (modifier != 0)
454  goto bad_format;
455  add1 (*f);
456  break;
457 
451 
452  case ('%'):
453  if (modifier != 0)
454  goto bad_format;
455  add1 (*f);
456  break;
457 
451 
452  case ('%'):
453  if (modifier != 0)
454  goto bad_format;
455  add1 (*f);
456  break;
457 
451 
452  case ('%'):
453  if (modifier != 0)
454  goto bad_format;
455  add1 (*f);
456  break;
457 
451 
452  case ('%'):
453  if (modifier != 0)
454  goto bad_format;
455  add1 (*f);
456  break;
457 
451 
452  case ('%'):
453  if (modifier != 0)
454  goto bad_format;
455  add1 (*f);
456  break;
457 
451 
452  case ('%'):
453  if (modifier != 0)
454  goto bad_format;
455  add1 (*f);
456  break;
457 
451 
452  case ('%'):
453  if (modifier != 0)
454  goto bad_format;
455  add1 (*f);
456  break;
457 
451 
452  case ('%'):
453  if (modifier != 0)
454  goto bad_format;
455  add1 (*f);
456  break;
457 
451 
452  case ('%'):
453  if (modifier != 0)
454  goto bad_format;
455  add1 (*f);
456  break;
457 
451 
452  case ('%'):
453  if (modifier != 0)
454  goto bad_format;
455  add1 (*f);
456  break;
457 
451 
452  case ('%'):
453  if (modifier != 0)
454  goto bad_format;
455  add1 (*f);
456  break;
457 
451 
452  case ('%'):
453  if (modifier != 0)
454  goto bad_format;
455  add1 (*f);
456  break;
457 
451 
452  case ('%'):
453  if (modifier != 0)
454  goto bad_format;
455  add1 (*f);
456  break;
457 
451 
452  case ('%'):
453  if (modifier != 0)
454  goto bad_format;
455  add1 (*f);
456  break;
457 
451 
452  case ('%'):
453  if (modifier != 0)
454  goto bad_format;
455  add1 (*f);
456  break;
457 
451 
452