Mercurial > hg > xemacs-beta
annotate src/strftime.c @ 5058:eb17f0c176ac
clean up a bit the object-memory-usage-stats after gc
-------------------- ChangeLog entries follow: --------------------
src/ChangeLog addition:
2010-02-21 Ben Wing <ben@xemacs.org>
* alloc.c:
* alloc.c (pluralize_word):
New function to pluralize a word.
* alloc.c (pluralize_and_append): New function.
* alloc.c (object_memory_usage_stats):
Clean up duplication.
author | Ben Wing <ben@xemacs.org> |
---|---|
date | Sun, 21 Feb 2010 05:19:08 -0600 |
parents | 2ade80e8c640 |
children | 1537701f08a1 |
rev | line source |
---|---|
428 | 1 /* strftime - custom formatting of date and/or time |
2 Copyright (C) 1989, 1991, 1992 Free Software Foundation, Inc. | |
3 | |
4 This program is free software; you can redistribute it and/or modify | |
5 it under the terms of the GNU General Public License as published by | |
6 the Free Software Foundation; either version 2, or (at your option) | |
7 any later version. | |
8 | |
9 This program is distributed in the hope that it will be useful, | |
10 but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 GNU General Public License for more details. | |
13 | |
14 You should have received a copy of the GNU General Public License | |
15 along with this program; see the file COPYING. If not, write to | |
16 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, | |
17 Boston, MA 02111-1307, USA. */ | |
18 | |
19 /* Synched up with: FSF 19.30. */ | |
20 | |
771 | 21 /* This file has been ... uhhhhh ... Mule-ized. Yeah. |
22 | |
23 (Everything here is external format. This is DANGEROUS and | |
24 data-lossy, but fixing it is too much of a bother now.) --ben */ | |
25 | |
428 | 26 /* Note: this version of strftime lacks locale support, |
27 but it is standalone. | |
28 | |
29 Performs `%' substitutions similar to those in printf. Except | |
30 where noted, substituted fields have a fixed size; numeric fields are | |
31 padded if necessary. Padding is with zeros by default; for fields | |
32 that display a single number, padding can be changed or inhibited by | |
33 following the `%' with one of the modifiers described below. Unknown | |
34 field specifiers are copied as normal characters. All other | |
35 characters are copied to the output without change. | |
36 | |
37 Supports a superset of the ANSI C field specifiers. | |
38 | |
39 Literal character fields: | |
40 % % | |
41 n newline | |
42 t tab | |
43 | |
44 Numeric modifiers (a nonstandard extension): | |
45 - do not pad the field | |
46 _ pad the field with spaces | |
47 | |
48 Time fields: | |
49 %H hour (00..23) | |
50 %I hour (01..12) | |
51 %k hour ( 0..23) | |
52 %l hour ( 1..12) | |
53 %M minute (00..59) | |
54 %p locale's AM or PM | |
55 %r time, 12-hour (hh:mm:ss [AP]M) | |
56 %R time, 24-hour (hh:mm) | |
57 %s time in seconds since 00:00:00, Jan 1, 1970 (a nonstandard extension) | |
58 %S second (00..61) | |
59 %T time, 24-hour (hh:mm:ss) | |
60 %X locale's time representation (%H:%M:%S) | |
4203 | 61 %z time zone offset (e.g. +0530, -0800 etc) |
428 | 62 %Z time zone (EDT), or nothing if no time zone is determinable |
63 | |
64 Date fields: | |
65 %a locale's abbreviated weekday name (Sun..Sat) | |
66 %A locale's full weekday name, variable length (Sunday..Saturday) | |
67 %b locale's abbreviated month name (Jan..Dec) | |
68 %B locale's full month name, variable length (January..December) | |
69 %c locale's date and time (Sat Nov 04 12:02:33 EST 1989) | |
70 %C century (00..99) | |
71 %d day of month (01..31) | |
72 %e day of month ( 1..31) | |
73 %D date (mm/dd/yy) | |
4203 | 74 %G year corresponding to the ISO 8601 week |
75 %g Year of the ISO 8601 week within century (00 - 99) | |
428 | 76 %h same as %b |
77 %j day of year (001..366) | |
78 %m month (01..12) | |
79 %U week number of year with Sunday as first day of week (00..53) | |
4203 | 80 %V ISO 8601 week number (first week is the earliest one with Thu) |
428 | 81 %w day of week (0..6) |
82 %W week number of year with Monday as first day of week (00..53) | |
83 %x locale's date representation (mm/dd/yy) | |
84 %y last two digits of year (00..99) | |
85 %Y year (1970...) | |
86 | |
87 David MacKenzie <djm@gnu.ai.mit.edu> */ | |
88 | |
89 #ifdef HAVE_CONFIG_H | |
90 #include <config.h> | |
91 #include "lisp.h" | |
92 #endif | |
93 | |
94 #include <stdio.h> | |
95 #include <sys/types.h> | |
96 #if defined(TM_IN_SYS_TIME) || (!defined(HAVE_TM_ZONE) && !defined(HAVE_TZNAME)) | |
97 #include <sys/time.h> | |
98 #else | |
99 #include <time.h> | |
100 #endif | |
101 | |
102 #ifndef STDC_HEADERS | |
103 time_t mktime (); | |
104 #endif | |
105 | |
442 | 106 #if defined(WIN32_NATIVE) || defined(CYGWIN) |
428 | 107 #include <time.h> |
108 #else | |
109 #if defined(HAVE_TZNAME) | |
110 extern char *tzname[2]; | |
111 #endif | |
442 | 112 #endif /* WIN32_NATIVE */ |
428 | 113 |
114 #ifdef emacs | |
115 #define strftime emacs_strftime | |
116 #endif | |
117 | |
118 /* Types of padding for numbers in date and time. */ | |
119 enum padding | |
120 { | |
121 none, blank, zero | |
122 }; | |
123 | |
442 | 124 static char const* const days[] = |
428 | 125 { |
126 "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" | |
127 }; | |
128 | |
442 | 129 static char const * const months[] = |
428 | 130 { |
131 "January", "February", "March", "April", "May", "June", | |
132 "July", "August", "September", "October", "November", "December" | |
133 }; | |
134 | |
135 /* Add character C to STRING and increment LENGTH, | |
136 unless LENGTH would exceed MAX. */ | |
137 | |
138 #define add_char(c) do \ | |
139 { \ | |
140 if (length + 1 <= max) \ | |
141 string[length++] = (c); \ | |
142 } while (0) | |
143 | |
144 /* Add a 2 digit number to STRING, padding if specified. | |
145 Return the number of characters added, up to MAX. */ | |
146 | |
147 static int | |
148 add_num2 (char *string, int num, int max, enum padding pad) | |
149 { | |
150 int top = num / 10; | |
151 int length = 0; | |
152 | |
153 if (top == 0 && pad == blank) | |
154 add_char (' '); | |
155 else if (top != 0 || pad == zero) | |
156 add_char (top + '0'); | |
157 add_char (num % 10 + '0'); | |
158 return length; | |
159 } | |
160 | |
161 /* Add a 3 digit number to STRING, padding if specified. | |
162 Return the number of characters added, up to MAX. */ | |
163 | |
164 static int | |
165 add_num3 (char *string, int num, int max, enum padding pad) | |
166 { | |
167 int top = num / 100; | |
168 int mid = (num - top * 100) / 10; | |
169 int length = 0; | |
170 | |
171 if (top == 0 && pad == blank) | |
172 add_char (' '); | |
173 else if (top != 0 || pad == zero) | |
174 add_char (top + '0'); | |
175 if (mid == 0 && top == 0 && pad == blank) | |
176 add_char (' '); | |
177 else if (mid != 0 || top != 0 || pad == zero) | |
178 add_char (mid + '0'); | |
179 add_char (num % 10 + '0'); | |
180 return length; | |
181 } | |
182 | |
183 /* Like strncpy except return the number of characters copied. */ | |
184 | |
185 static int | |
442 | 186 add_str (char *to, const char *from, int max) |
428 | 187 { |
188 int i; | |
189 | |
190 for (i = 0; from[i] && i <= max; ++i) | |
191 to[i] = from[i]; | |
192 return i; | |
193 } | |
194 | |
195 static int | |
196 add_num_time_t (char *string, int max, time_t num) | |
197 { | |
198 /* This buffer is large enough to hold the character representation | |
199 (including the trailing NUL) of any unsigned decimal quantity | |
200 whose binary representation fits in 128 bits. */ | |
201 char buf[40]; | |
202 | |
203 if (sizeof (num) > 16) | |
2500 | 204 ABORT (); |
428 | 205 sprintf (buf, "%lu", (unsigned long) num); |
793 | 206 return add_str (string, buf, max); |
428 | 207 } |
208 | |
209 /* Return the week in the year of the time in TM, with the weeks | |
210 starting on Sundays. */ | |
211 | |
212 static int | |
442 | 213 sun_week (const struct tm *tm) |
428 | 214 { |
215 int dl; | |
216 | |
217 /* Set `dl' to the day in the year of the last day of the week previous | |
218 to the one containing the day specified in TM. If the day specified | |
219 in TM is in the first week of the year, `dl' will be negative or 0. | |
220 Otherwise, calculate the number of complete weeks before our week | |
221 (dl / 7) and add any partial week at the start of the year (dl % 7). */ | |
222 dl = tm->tm_yday - tm->tm_wday; | |
223 return dl <= 0 ? 0 : dl / 7 + (dl % 7 != 0); | |
224 } | |
225 | |
226 /* Return the week in the year of the time in TM, with the weeks | |
227 starting on Mondays. */ | |
228 | |
229 static int | |
442 | 230 mon_week (const struct tm *tm) |
428 | 231 { |
232 int dl, wday; | |
233 | |
234 if (tm->tm_wday == 0) | |
235 wday = 6; | |
236 else | |
237 wday = tm->tm_wday - 1; | |
238 dl = tm->tm_yday - wday; | |
239 return dl <= 0 ? 0 : dl / 7 + (dl % 7 != 0); | |
240 } | |
241 | |
4203 | 242 #ifndef __isleap |
243 /* Nonzero if YEAR is a leap year (every 4 years, | |
244 except every 100th isn't, and every 400th is). */ | |
245 # define __isleap(year) \ | |
246 ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0)) | |
247 #endif | |
248 | |
249 /* The number of days from the first day of the first ISO week of this | |
250 year to the year day YDAY with week day WDAY. ISO weeks start on | |
251 Monday; the first ISO week has the year's first Thursday. YDAY may | |
252 be as small as YDAY_MINIMUM. */ | |
253 #define ISO_WEEK_START_WDAY 1 /* Monday */ | |
254 #define ISO_WEEK1_WDAY 4 /* Thursday */ | |
255 #define YDAY_MINIMUM (-366) | |
256 static int | |
257 iso_week_days (int yday, int wday) | |
258 { | |
259 /* Add enough to the first operand of % to make it nonnegative. */ | |
260 int big_enough_multiple_of_7 = (-YDAY_MINIMUM / 7 + 2) * 7; | |
261 return (yday | |
262 - (yday - wday + ISO_WEEK1_WDAY + big_enough_multiple_of_7) % 7 | |
263 + ISO_WEEK1_WDAY - ISO_WEEK_START_WDAY); | |
264 } | |
265 | |
428 | 266 #if !defined(HAVE_TM_ZONE) && !defined(HAVE_TZNAME) |
442 | 267 char *zone_name (const struct tm *tp); |
428 | 268 char * |
442 | 269 zone_name (const struct tm *tp) |
428 | 270 { |
271 char *timezone (); | |
272 struct timeval tv; | |
273 struct timezone tz; | |
274 | |
275 gettimeofday (&tv, &tz); | |
276 return timezone (tz.tz_minuteswest, tp->tm_isdst); | |
277 } | |
278 #endif | |
279 | |
280 /* Format the time given in TM according to FORMAT, and put the | |
281 results in STRING. | |
282 Return the number of characters (not including terminating null) | |
283 that were put into STRING, or 0 if the length would have | |
284 exceeded MAX. */ | |
285 | |
442 | 286 size_t strftime (char *string, size_t max, const char *format, |
287 const struct tm *tm); | |
428 | 288 |
289 size_t | |
442 | 290 strftime (char *string, size_t max, const char *format, const struct tm *tm) |
428 | 291 { |
292 enum padding pad; /* Type of padding to apply. */ | |
293 size_t length = 0; /* Characters put in STRING so far. */ | |
294 | |
295 for (; *format && length < max; ++format) | |
296 { | |
297 if (*format != '%') | |
298 add_char (*format); | |
299 else | |
300 { | |
301 ++format; | |
302 /* Modifiers: */ | |
303 if (*format == '-') | |
304 { | |
305 pad = none; | |
306 ++format; | |
307 } | |
308 else if (*format == '_') | |
309 { | |
310 pad = blank; | |
311 ++format; | |
312 } | |
313 else | |
314 pad = zero; | |
315 | |
316 switch (*format) | |
317 { | |
318 /* Literal character fields: */ | |
319 case 0: | |
320 case '%': | |
321 add_char ('%'); | |
322 break; | |
323 case 'n': | |
324 add_char ('\n'); | |
325 break; | |
326 case 't': | |
327 add_char ('\t'); | |
328 break; | |
329 default: | |
330 add_char (*format); | |
331 break; | |
332 | |
333 /* Time fields: */ | |
334 case 'H': | |
335 case 'k': | |
336 length += | |
337 add_num2 (&string[length], tm->tm_hour, max - length, | |
338 *format == 'H' ? pad : blank); | |
339 break; | |
340 case 'I': | |
341 case 'l': | |
342 { | |
343 int hour12; | |
344 | |
345 if (tm->tm_hour == 0) | |
346 hour12 = 12; | |
347 else if (tm->tm_hour > 12) | |
348 hour12 = tm->tm_hour - 12; | |
349 else | |
350 hour12 = tm->tm_hour; | |
351 length += | |
352 add_num2 (&string[length], hour12, max - length, | |
353 *format == 'I' ? pad : blank); | |
354 } | |
355 break; | |
356 case 'M': | |
357 length += | |
358 add_num2 (&string[length], tm->tm_min, max - length, pad); | |
359 break; | |
360 case 'p': | |
361 if (tm->tm_hour < 12) | |
362 add_char ('A'); | |
363 else | |
364 add_char ('P'); | |
365 add_char ('M'); | |
366 break; | |
367 case 'r': | |
368 length += | |
369 strftime (&string[length], max - length, "%I:%M:%S %p", tm); | |
370 break; | |
371 case 'R': | |
372 length += | |
373 strftime (&string[length], max - length, "%H:%M", tm); | |
374 break; | |
375 | |
376 case 's': | |
377 { | |
378 struct tm writable_tm; | |
379 writable_tm = *tm; | |
380 length += add_num_time_t (&string[length], max - length, | |
381 mktime (&writable_tm)); | |
382 } | |
383 break; | |
384 | |
385 case 'S': | |
386 length += | |
387 add_num2 (&string[length], tm->tm_sec, max - length, pad); | |
388 break; | |
389 case 'T': | |
390 length += | |
391 strftime (&string[length], max - length, "%H:%M:%S", tm); | |
392 break; | |
4203 | 393 |
394 case 'V': | |
395 case 'g': | |
396 case 'G': | |
397 { | |
398 int year = tm->tm_year + 1900; | |
5016
2ade80e8c640
enable more warnings and fix them
Ben Wing <ben@xemacs.org>
parents:
4203
diff
changeset
|
399 int ndays = iso_week_days (tm->tm_yday, tm->tm_wday); |
4203 | 400 |
5016
2ade80e8c640
enable more warnings and fix them
Ben Wing <ben@xemacs.org>
parents:
4203
diff
changeset
|
401 if (ndays < 0) |
4203 | 402 { |
403 /* This ISO week belongs to the previous year. */ | |
404 year--; | |
5016
2ade80e8c640
enable more warnings and fix them
Ben Wing <ben@xemacs.org>
parents:
4203
diff
changeset
|
405 ndays = |
4203 | 406 iso_week_days (tm->tm_yday + (365 + __isleap (year)), |
407 tm->tm_wday); | |
408 } | |
409 else | |
410 { | |
411 int d = | |
412 iso_week_days (tm->tm_yday - (365 + __isleap (year)), | |
413 tm->tm_wday); | |
414 if (0 <= d) | |
415 { | |
416 /* This ISO week belongs to the next year. */ | |
417 year++; | |
5016
2ade80e8c640
enable more warnings and fix them
Ben Wing <ben@xemacs.org>
parents:
4203
diff
changeset
|
418 ndays = d; |
4203 | 419 } |
420 } | |
421 | |
422 switch (*format) | |
423 { | |
424 /* | |
425 #### FIXME | |
426 We really can't assume 1000 <= year <= 9999 | |
427 once time_t gets beyond 32 bits, but it's true | |
428 of the rest of the code here so get with the | |
429 program | |
430 */ | |
431 case 'g': | |
432 length += | |
433 add_num2 (&string[length], year % 100, | |
434 max - length, pad); | |
435 break; | |
436 | |
437 case 'G': | |
438 add_char (year / 1000 + '0'); | |
439 length += add_num3 (&string[length], year % 1000, | |
440 max - length, zero); | |
441 break; | |
442 | |
443 default: | |
444 length += | |
5016
2ade80e8c640
enable more warnings and fix them
Ben Wing <ben@xemacs.org>
parents:
4203
diff
changeset
|
445 add_num2 (&string[length], ndays / 7 + 1, |
4203 | 446 max - length, pad); |
447 break; | |
448 } | |
449 } | |
450 break; | |
428 | 451 case 'X': |
452 length += | |
453 strftime (&string[length], max - length, "%H:%M:%S", tm); | |
454 break; | |
4203 | 455 case 'z': |
456 { | |
457 /* | |
458 #### FIXME: could use tm->tm_gmtoff if present. Since | |
459 the other code in xemacs does not do so we follow the | |
460 leaders (and don't add a autoconf macro to detect | |
461 its presence). | |
462 */ | |
463 long int offset; | |
464 long int minutes; | |
465 struct tm lt, *ut; | |
466 time_t utc; | |
467 | |
468 lt = *tm; | |
469 utc = mktime(<); | |
470 ut = gmtime(&utc); | |
471 /* assume that tm is valid so the others will be too! */ | |
472 assert( utc != (time_t) -1 && ut != NULL ); | |
473 | |
474 /* tm diff code below is based on mktime.c, glibc 2.3.2 */ | |
475 { | |
476 int lt4, ut4, lt100, ut100, lt400, ut400; | |
5016
2ade80e8c640
enable more warnings and fix them
Ben Wing <ben@xemacs.org>
parents:
4203
diff
changeset
|
477 int intervening_leap_days, years, ndays; |
4203 | 478 |
479 lt4 = (lt.tm_year >> 2) + (1900 >> 2) - | |
480 ! (lt.tm_year & 3); | |
481 ut4 = (ut->tm_year >> 2) + (1900 >> 2) - | |
482 ! (ut->tm_year & 3); | |
483 lt100 = lt4 / 25 - (lt4 % 25 < 0); | |
484 ut100 = ut4 / 25 - (ut4 % 25 < 0); | |
485 lt400 = lt100 >> 2; | |
486 ut400 = ut100 >> 2; | |
487 intervening_leap_days = | |
488 (lt4 - ut4) - (lt100 - ut100) + (lt400 - ut400); | |
489 years = lt.tm_year - ut->tm_year; | |
5016
2ade80e8c640
enable more warnings and fix them
Ben Wing <ben@xemacs.org>
parents:
4203
diff
changeset
|
490 ndays = (365 * years + intervening_leap_days |
4203 | 491 + (lt.tm_yday - ut->tm_yday)); |
5016
2ade80e8c640
enable more warnings and fix them
Ben Wing <ben@xemacs.org>
parents:
4203
diff
changeset
|
492 offset = (60 * (60 * (24 * ndays |
2ade80e8c640
enable more warnings and fix them
Ben Wing <ben@xemacs.org>
parents:
4203
diff
changeset
|
493 + (lt.tm_hour - ut->tm_hour)) |
4203 | 494 + (lt.tm_min - ut->tm_min)) |
495 + (lt.tm_sec - ut->tm_sec)); | |
496 } | |
497 | |
498 minutes = offset / ( offset < 0 ? -60 : 60 ); | |
499 | |
500 add_char ((offset < 0 ? '-' : '+')); | |
501 | |
502 if ( minutes / 600 != 0 ) | |
503 add_char (minutes / 600 + '0'); | |
504 else if ( pad != none ) | |
505 add_char ((pad == zero ? '0' : ' ')); | |
506 | |
507 length += | |
508 add_num3 (&string[length], | |
509 ((minutes / 60 ) % 10) * 100 + (minutes % 60), | |
510 max - length, pad); | |
511 break; | |
512 } | |
428 | 513 case 'Z': |
514 #ifdef HAVE_TM_ZONE | |
515 length += add_str (&string[length], tm->tm_zone, max - length); | |
516 #else | |
517 #ifdef HAVE_TZNAME | |
518 if (tm->tm_isdst && tzname[1] && *tzname[1]) | |
519 length += add_str (&string[length], tzname[1], max - length); | |
520 else | |
521 length += add_str (&string[length], tzname[0], max - length); | |
522 #else | |
523 length += add_str (&string[length], zone_name (tm), max - length); | |
524 #endif | |
525 #endif | |
526 break; | |
527 | |
528 /* Date fields: */ | |
529 case 'a': | |
530 add_char (days[tm->tm_wday][0]); | |
531 add_char (days[tm->tm_wday][1]); | |
532 add_char (days[tm->tm_wday][2]); | |
533 break; | |
534 case 'A': | |
535 length += | |
536 add_str (&string[length], days[tm->tm_wday], max - length); | |
537 break; | |
538 case 'b': | |
539 case 'h': | |
540 add_char (months[tm->tm_mon][0]); | |
541 add_char (months[tm->tm_mon][1]); | |
542 add_char (months[tm->tm_mon][2]); | |
543 break; | |
544 case 'B': | |
545 length += | |
546 add_str (&string[length], months[tm->tm_mon], max - length); | |
547 break; | |
548 case 'c': | |
549 length += | |
550 strftime (&string[length], max - length, | |
551 "%a %b %d %H:%M:%S %Z %Y", tm); | |
552 break; | |
553 case 'C': | |
554 length += | |
555 add_num2 (&string[length], (tm->tm_year + 1900) / 100, | |
556 max - length, pad); | |
557 break; | |
558 case 'd': | |
559 length += | |
560 add_num2 (&string[length], tm->tm_mday, max - length, pad); | |
561 break; | |
562 case 'e': | |
563 length += | |
564 add_num2 (&string[length], tm->tm_mday, max - length, blank); | |
565 break; | |
566 case 'D': | |
567 length += | |
568 strftime (&string[length], max - length, "%m/%d/%y", tm); | |
569 break; | |
570 case 'j': | |
571 length += | |
572 add_num3 (&string[length], tm->tm_yday + 1, max - length, pad); | |
573 break; | |
574 case 'm': | |
575 length += | |
576 add_num2 (&string[length], tm->tm_mon + 1, max - length, pad); | |
577 break; | |
578 case 'U': | |
579 length += | |
580 add_num2 (&string[length], sun_week (tm), max - length, pad); | |
581 break; | |
582 case 'w': | |
583 add_char (tm->tm_wday + '0'); | |
584 break; | |
585 case 'W': | |
586 length += | |
587 add_num2 (&string[length], mon_week (tm), max - length, pad); | |
588 break; | |
589 case 'x': | |
590 length += | |
591 strftime (&string[length], max - length, "%m/%d/%y", tm); | |
592 break; | |
593 case 'y': | |
594 length += | |
595 add_num2 (&string[length], tm->tm_year % 100, | |
596 max - length, pad); | |
597 break; | |
598 case 'Y': | |
599 add_char ((tm->tm_year + 1900) / 1000 + '0'); | |
600 length += | |
601 add_num3 (&string[length], | |
602 (1900 + tm->tm_year) % 1000, max - length, zero); | |
603 break; | |
604 } | |
605 } | |
606 } | |
607 add_char (0); | |
608 return length - 1; | |
609 } |